diff lwasm/output.c @ 543:e10618b48e68

Implement support for dragon format binaries Implement support for dragon format binaries. As an added bonus, also implement a variation on raw binaries which guarantees the whole binary fits in the file. These are the "dragon" and "abs" output formats. Based on code submitted by Mike Miller.
author William Astle <lost@l-w.ca>
date Thu, 29 Sep 2022 13:59:42 -0600
parents 33a59e232a5b
children e49d24f4a9a5
line wrap: on
line diff
--- a/lwasm/output.c	Wed Aug 17 17:06:30 2022 -0600
+++ b/lwasm/output.c	Thu Sep 29 13:59:42 2022 -0600
@@ -44,6 +44,8 @@
 void write_code_srec(asmstate_t *as, FILE *of);
 void write_code_ihex(asmstate_t *as, FILE *of);
 void write_code_lwmod(asmstate_t *as, FILE *of);
+void write_code_dragon(asmstate_t *as, FILE *of);
+void write_code_abs(asmstate_t *as, FILE *of);
 
 // this prevents warnings about not using the return value of fwrite()
 // r++ prevents the "set but not used" warnings; should be optimized out
@@ -109,6 +111,14 @@
 		write_code_lwmod(as, of);
 		break;
 
+	case OUTPUT_DRAGON:
+		write_code_dragon(as, of);
+		break;
+
+	case OUTPUT_ABS:
+		write_code_abs(as, of);
+		break;
+
 	default:
 		fprintf(stderr, "BUG: unrecognized output format when generating output file\n");
 		fclose(of);
@@ -1259,3 +1269,94 @@
 	if (initsize)
 		writebytes(initcode, initsize, 1, of);
 }
+
+void write_code_abs_calc(asmstate_t *as, unsigned int *start, unsigned int *length)
+{
+	line_t *cl;
+	unsigned int lowaddr = 65535;
+	unsigned int highaddr = 0;
+	char outbyte;
+	int outaddr, rc;
+
+	// if not specified, calculate
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		do
+		{
+			rc = fetch_output_byte(cl, &outbyte, &outaddr);
+			if (rc)
+			{
+				if (outaddr < lowaddr)
+					lowaddr = outaddr;
+				if (outaddr > highaddr)
+					highaddr = outaddr;
+			}
+		}
+		while (rc);
+	
+		*length = (lowaddr > highaddr) ? 0 : 1 + highaddr - lowaddr;
+		*start = (lowaddr > highaddr ) ? 0 : lowaddr;
+	}
+}
+
+void write_code_abs_aux(asmstate_t *as, FILE *of, unsigned int start, unsigned int header_size)
+{
+	line_t *cl;
+
+	char outbyte;
+	int outaddr, rc;
+
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		do
+		{
+			rc = fetch_output_byte(cl, &outbyte, &outaddr);
+			
+			// if first byte to write or output stream jumps address, seek
+			if (rc == -1)
+			{
+				fseek(of,(long int) header_size + outaddr - start, SEEK_SET);
+			}
+			if (rc) fputc(outbyte,of);
+		}
+		while (rc);
+	}
+
+}
+
+/* Write a DragonDOS binary file */
+
+void write_code_dragon(asmstate_t *as, FILE *of)
+{
+	unsigned char headerbuf[9];
+	unsigned int start, length;
+
+	write_code_abs_calc(as, &start, &length);
+
+	headerbuf[0] = 0x55; // magic $55
+	headerbuf[1] = 0x02; // binary file
+	headerbuf[2] = (start >> 8) & 0xFF;
+	headerbuf[3] = (start) & 0xFF;
+	headerbuf[4] = (length >> 8) & 0xFF;
+	headerbuf[5] = (length) & 0xFF;
+	headerbuf[6] = (as -> execaddr >> 8) & 0xFF;
+	headerbuf[7] = (as -> execaddr) & 0xFF;
+	headerbuf[8] = 0xAA; // magic $AA
+
+	writebytes(headerbuf, 9, 1, of);
+
+	write_code_abs_aux(as, of, start, 9);
+
+}
+
+/* Write a monolithic binary block, respecting absolute address segments from ORG directives */
+/* Uses fseek, requires lowest code address and header offset size */
+/* Out of order ORG addresses are handled */
+
+void write_code_abs(asmstate_t *as, FILE *of)
+{
+	unsigned int start, length;
+	
+	write_code_abs_calc(as, &start, &length);
+	write_code_abs_aux(as, of, start, 0);
+}