commit 245ba17

Devine Lu Linvega  ·  2022-07-13 04:27:52 +0000 UTC
parent d9b1a3e
Added a copy of uxnasm
2 files changed,  +475, -1
+4, -1
 1@@ -19,6 +19,7 @@ then
 2 	clang-format -i src/uxn.c
 3 	clang-format -i src/uxn.h
 4 	clang-format -i src/uxn11.c
 5+	clang-format -i src/uxnasm.c
 6 	clang-format -i src/uxncli.c
 7 	clang-format -i src/devices/*
 8 fi
 9@@ -28,13 +29,15 @@ rm -f bin/*
10 mkdir -p bin
11 
12 echo "Building.."
13+cc -DNDEBUG -Os -g0 -s src/uxnasm.c -o bin/uxnasm
14 if [ "${1}" = '--install' ];
15 then
16 	echo "Installing.."
17 	gcc ${RELEASE_FLAGS} ${EMU_INC}
18 	gcc ${RELEASE_FLAGS} ${CLI_INC}
19-	cp bin/uxn11 ~/bin
20+	cp bin/uxnasm ~/bin
21 	cp bin/uxncli ~/bin
22+	cp bin/uxn11 ~/bin
23 else
24 	gcc ${DEBUG_FLAGS} ${EMU_INC}
25 	gcc ${DEBUG_FLAGS} ${CLI_INC} 
+471, -0
  1@@ -0,0 +1,471 @@
  2+#include <stdio.h>
  3+
  4+/*
  5+Copyright (c) 2021 Devine Lu Linvega
  6+
  7+Permission to use, copy, modify, and distribute this software for any
  8+purpose with or without fee is hereby granted, provided that the above
  9+copyright notice and this permission notice appear in all copies.
 10+
 11+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 12+WITH REGARD TO THIS SOFTWARE.
 13+*/
 14+
 15+#define TRIM 0x0100
 16+#define LENGTH 0x10000
 17+
 18+typedef unsigned char Uint8;
 19+typedef signed char Sint8;
 20+typedef unsigned short Uint16;
 21+
 22+typedef struct {
 23+	char name[0x40], items[0x40][0x40];
 24+	Uint8 len;
 25+} Macro;
 26+
 27+typedef struct {
 28+	char name[0x40];
 29+	Uint16 addr, refs;
 30+} Label;
 31+
 32+typedef struct {
 33+	char name[0x40], rune;
 34+	Uint16 addr;
 35+} Reference;
 36+
 37+typedef struct {
 38+	Uint8 data[LENGTH];
 39+	unsigned int ptr, length;
 40+	Uint16 llen, mlen, rlen;
 41+	Label labels[0x400];
 42+	Macro macros[0x100];
 43+	Reference refs[0x800];
 44+	char scope[0x40];
 45+} Program;
 46+
 47+Program p;
 48+static int litlast = 0;
 49+static int jsrlast = 0;
 50+
 51+/* clang-format off */
 52+
 53+static char ops[][4] = {
 54+	"LIT", "INC", "POP", "NIP", "SWP", "ROT", "DUP", "OVR",
 55+	"EQU", "NEQ", "GTH", "LTH", "JMP", "JCN", "JSR", "STH",
 56+	"LDZ", "STZ", "LDR", "STR", "LDA", "STA", "DEI", "DEO",
 57+	"ADD", "SUB", "MUL", "DIV", "AND", "ORA", "EOR", "SFT"
 58+};
 59+
 60+static int   scmp(char *a, char *b, int len) { int i = 0; while(a[i] == b[i]) if(!a[i] || ++i >= len) return 1; return 0; } /* string compare */
 61+static int   sihx(char *s) { int i = 0; char c; while((c = s[i++])) if(!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f')) return 0; return i > 1; } /* string is hexadecimal */
 62+static int   shex(char *s) { int n = 0, i = 0; char c; while((c = s[i++])) if(c >= '0' && c <= '9') n = n * 16 + (c - '0'); else if(c >= 'a' && c <= 'f') n = n * 16 + 10 + (c - 'a'); return n; } /* string to num */
 63+static int   slen(char *s) { int i = 0; while(s[i]) i++; return i; } /* string length */
 64+static int   spos(char *s, char c) { Uint8 i = 0, j; while((j = s[i++])) if(j == c) return i; return -1; } /* character position */
 65+static char *scpy(char *src, char *dst, int len) { int i = 0; while((dst[i] = src[i]) && i < len - 2) i++; dst[i + 1] = '\0'; return dst; } /* string copy */
 66+static char *scat(char *dst, const char *src) { char *ptr = dst + slen(dst); while(*src) *ptr++ = *src++; *ptr = '\0'; return dst; } /* string cat */
 67+
 68+/* clang-format on */
 69+
 70+static int parse(char *w, FILE *f);
 71+
 72+static int
 73+error(const char *name, const char *msg)
 74+{
 75+	fprintf(stderr, "%s: %s\n", name, msg);
 76+	return 0;
 77+}
 78+
 79+static char *
 80+sublabel(char *src, char *scope, char *name)
 81+{
 82+	return scat(scat(scpy(scope, src, 0x40), "/"), name);
 83+}
 84+
 85+static Macro *
 86+findmacro(char *name)
 87+{
 88+	int i;
 89+	for(i = 0; i < p.mlen; i++)
 90+		if(scmp(p.macros[i].name, name, 0x40))
 91+			return &p.macros[i];
 92+	return NULL;
 93+}
 94+
 95+static Label *
 96+findlabel(char *name)
 97+{
 98+	int i;
 99+	for(i = 0; i < p.llen; i++)
100+		if(scmp(p.labels[i].name, name, 0x40))
101+			return &p.labels[i];
102+	return NULL;
103+}
104+
105+static Uint8
106+findopcode(char *s)
107+{
108+	int i;
109+	for(i = 0; i < 0x20; i++) {
110+		int m = 0;
111+		if(!scmp(ops[i], s, 3))
112+			continue;
113+		if(!i) i |= (1 << 7); /* force keep for LIT */
114+		while(s[3 + m]) {
115+			if(s[3 + m] == '2')
116+				i |= (1 << 5); /* mode: short */
117+			else if(s[3 + m] == 'r')
118+				i |= (1 << 6); /* mode: return */
119+			else if(s[3 + m] == 'k')
120+				i |= (1 << 7); /* mode: keep */
121+			else
122+				return 0; /* failed to match */
123+			m++;
124+		}
125+		return i;
126+	}
127+	return 0;
128+}
129+
130+static int
131+makemacro(char *name, FILE *f)
132+{
133+	Macro *m;
134+	char word[0x40];
135+	if(findmacro(name))
136+		return error("Macro duplicate", name);
137+	if(sihx(name) && slen(name) % 2 == 0)
138+		return error("Macro name is hex number", name);
139+	if(findopcode(name) || scmp(name, "BRK", 4) || !slen(name))
140+		return error("Macro name is invalid", name);
141+	if(p.mlen == 0x100)
142+		return error("Macros limit exceeded", name);
143+	m = &p.macros[p.mlen++];
144+	scpy(name, m->name, 0x40);
145+	while(fscanf(f, "%63s", word) == 1) {
146+		if(word[0] == '{') continue;
147+		if(word[0] == '}') break;
148+		if(word[0] == '%')
149+			return error("Macro error", name);
150+		if(m->len >= 0x40)
151+			return error("Macro size exceeded", name);
152+		scpy(word, m->items[m->len++], 0x40);
153+	}
154+	return 1;
155+}
156+
157+static int
158+makelabel(char *name)
159+{
160+	Label *l;
161+	if(findlabel(name))
162+		return error("Label duplicate", name);
163+	if(sihx(name) && (slen(name) == 2 || slen(name) == 4))
164+		return error("Label name is hex number", name);
165+	if(findopcode(name) || scmp(name, "BRK", 4) || !slen(name))
166+		return error("Label name is invalid", name);
167+	if(p.llen == 0x400)
168+		return error("Labels limit exceeded", name);
169+	l = &p.labels[p.llen++];
170+	l->addr = p.ptr;
171+	l->refs = 0;
172+	scpy(name, l->name, 0x40);
173+	return 1;
174+}
175+
176+static int
177+makereference(char *scope, char *label, Uint16 addr)
178+{
179+	char subw[0x40], parent[0x40];
180+	Reference *r;
181+	if(p.rlen == 0x800)
182+		return error("References limit exceeded", label);
183+	r = &p.refs[p.rlen++];
184+	if(label[1] == '&')
185+		scpy(sublabel(subw, scope, label + 2), r->name, 0x40);
186+	else {
187+		int pos = spos(label + 1, '/');
188+		if(pos > 0) {
189+			Label *l;
190+			if((l = findlabel(scpy(label + 1, parent, pos))))
191+				l->refs++;
192+		}
193+		scpy(label + 1, r->name, 0x40);
194+	}
195+	r->rune = label[0];
196+	r->addr = addr;
197+	return 1;
198+}
199+
200+static int
201+writebyte(Uint8 b)
202+{
203+	if(p.ptr < TRIM)
204+		return error("Writing in zero-page", "");
205+	else if(p.ptr > 0xffff)
206+		return error("Writing after the end of RAM", "");
207+	else if(p.ptr < p.length)
208+		return error("Memory overwrite", "");
209+	p.data[p.ptr++] = b;
210+	p.length = p.ptr;
211+	litlast = 0;
212+	jsrlast = 0;
213+	return 1;
214+}
215+
216+static int
217+writeopcode(char *w)
218+{
219+	Uint8 res;
220+	if(jsrlast && scmp(w, "JMP2r", 5)) { /* tail-call optimization */
221+		p.data[p.ptr - 1] = jsrlast == 2 ? findopcode("JMP2") : findopcode("JMP");
222+		jsrlast = 0;
223+		return 1;
224+	}
225+	res = writebyte(findopcode(w));
226+	if(scmp(w, "JSR2", 4))
227+		jsrlast = 2;
228+	else if(scmp(w, "JSR", 3))
229+		jsrlast = 1;
230+	return res;
231+}
232+
233+static int
234+writeshort(Uint16 s, int lit)
235+{
236+	if(lit)
237+		if(!writebyte(findopcode("LIT2"))) return 0;
238+	return writebyte(s >> 8) && writebyte(s & 0xff);
239+}
240+
241+static int
242+writelitbyte(Uint8 b)
243+{
244+	if(litlast) { /* literals optimization */
245+		Uint8 hb = p.data[p.ptr - 1];
246+		p.ptr -= 2;
247+		p.length = p.ptr;
248+		return writeshort((hb << 8) + b, 1);
249+	}
250+	if(!writebyte(findopcode("LIT"))) return 0;
251+	if(!writebyte(b)) return 0;
252+	litlast = 1;
253+	return 1;
254+}
255+
256+static int
257+doinclude(const char *filename)
258+{
259+	FILE *f;
260+	char w[0x40];
261+	if(!(f = fopen(filename, "r")))
262+		return error("Include missing", filename);
263+	while(fscanf(f, "%63s", w) == 1)
264+		if(!parse(w, f))
265+			return error("Unknown token", w);
266+	fclose(f);
267+	return 1;
268+}
269+
270+static int
271+parse(char *w, FILE *f)
272+{
273+	int i;
274+	char word[0x40], subw[0x40], c;
275+	Macro *m;
276+	if(slen(w) >= 63)
277+		return error("Invalid token", w);
278+	switch(w[0]) {
279+	case '(': /* comment */
280+		if(slen(w) != 1) fprintf(stderr, "-- Malformed comment: %s\n", w);
281+		i = 1; /* track nested comment depth */
282+		while(fscanf(f, "%63s", word) == 1) {
283+			if(slen(word) != 1)
284+				continue;
285+			else if(word[0] == '(')
286+				i++;
287+			else if(word[0] == ')' && --i < 1)
288+				break;
289+		}
290+		break;
291+	case '~': /* include */
292+		if(!doinclude(w + 1))
293+			return error("Invalid include", w);
294+		break;
295+	case '%': /* macro */
296+		if(!makemacro(w + 1, f))
297+			return error("Invalid macro", w);
298+		break;
299+	case '|': /* pad-absolute */
300+		if(!sihx(w + 1))
301+			return error("Invalid padding", w);
302+		p.ptr = shex(w + 1);
303+		litlast = jsrlast = 0;
304+		break;
305+	case '$': /* pad-relative */
306+		if(!sihx(w + 1))
307+			return error("Invalid padding", w);
308+		p.ptr += shex(w + 1);
309+		litlast = jsrlast = 0;
310+		break;
311+	case '@': /* label */
312+		if(!makelabel(w + 1))
313+			return error("Invalid label", w);
314+		scpy(w + 1, p.scope, 0x40);
315+		litlast = jsrlast = 0;
316+		break;
317+	case '&': /* sublabel */
318+		if(!makelabel(sublabel(subw, p.scope, w + 1)))
319+			return error("Invalid sublabel", w);
320+		litlast = jsrlast = 0;
321+		break;
322+	case '#': /* literals hex */
323+		if(!sihx(w + 1) || (slen(w) != 3 && slen(w) != 5))
324+			return error("Invalid hex literal", w);
325+		if(slen(w) == 3) {
326+			if(!writelitbyte(shex(w + 1))) return 0;
327+		} else if(slen(w) == 5) {
328+			if(!writeshort(shex(w + 1), 1)) return 0;
329+		}
330+		break;
331+	case '.': /* literal byte zero-page */
332+		makereference(p.scope, w, p.ptr - litlast);
333+		if(!writelitbyte(0xff)) return 0;
334+		break;
335+	case ',': /* literal byte relative */
336+		makereference(p.scope, w, p.ptr - litlast);
337+		if(!writelitbyte(0xff)) return 0;
338+		break;
339+	case ';': /* literal short absolute */
340+		makereference(p.scope, w, p.ptr);
341+		if(!writeshort(0xffff, 1)) return 0;
342+		break;
343+	case ':': /* raw short absolute */
344+		makereference(p.scope, w, p.ptr);
345+		if(!writeshort(0xffff, 0)) return 0;
346+		break;
347+	case '\'': /* raw char */
348+		if(!writebyte((Uint8)w[1])) return 0;
349+		break;
350+	case '"': /* raw string */
351+		i = 0;
352+		while((c = w[++i]))
353+			if(!writebyte(c)) return 0;
354+		break;
355+	case '[':
356+	case ']':
357+		if(slen(w) == 1) break; /* else fallthrough */
358+	default:
359+		/* opcode */
360+		if(findopcode(w) || scmp(w, "BRK", 4)) {
361+			if(!writeopcode(w)) return 0;
362+		}
363+		/* raw byte */
364+		else if(sihx(w) && slen(w) == 2) {
365+			if(!writebyte(shex(w))) return 0;
366+		}
367+		/* raw short */
368+		else if(sihx(w) && slen(w) == 4) {
369+			if(!writeshort(shex(w), 0)) return 0;
370+		}
371+		/* macro */
372+		else if((m = findmacro(w))) {
373+			for(i = 0; i < m->len; i++)
374+				if(!parse(m->items[i], f))
375+					return 0;
376+			return 1;
377+		} else
378+			return error("Unknown token", w);
379+	}
380+	return 1;
381+}
382+
383+static int
384+resolve(void)
385+{
386+	Label *l;
387+	int i;
388+	for(i = 0; i < p.rlen; i++) {
389+		Reference *r = &p.refs[i];
390+		switch(r->rune) {
391+		case '.':
392+			if(!(l = findlabel(r->name)))
393+				return error("Unknown zero-page reference", r->name);
394+			p.data[r->addr + 1] = l->addr & 0xff;
395+			l->refs++;
396+			break;
397+		case ',':
398+			if(!(l = findlabel(r->name)))
399+				return error("Unknown relative reference", r->name);
400+			p.data[r->addr + 1] = (Sint8)(l->addr - r->addr - 3);
401+			if((Sint8)p.data[r->addr + 1] != (l->addr - r->addr - 3))
402+				return error("Relative reference is too far", r->name);
403+			l->refs++;
404+			break;
405+		case ';':
406+			if(!(l = findlabel(r->name)))
407+				return error("Unknown absolute reference", r->name);
408+			p.data[r->addr + 1] = l->addr >> 0x8;
409+			p.data[r->addr + 2] = l->addr & 0xff;
410+			l->refs++;
411+			break;
412+		case ':':
413+			if(!(l = findlabel(r->name)))
414+				return error("Unknown absolute reference", r->name);
415+			p.data[r->addr + 0] = l->addr >> 0x8;
416+			p.data[r->addr + 1] = l->addr & 0xff;
417+			l->refs++;
418+			break;
419+		default:
420+			return error("Unknown reference", r->name);
421+		}
422+	}
423+	return 1;
424+}
425+
426+static int
427+assemble(FILE *f)
428+{
429+	char w[0x40];
430+	scpy("on-reset", p.scope, 0x40);
431+	while(fscanf(f, "%63s", w) == 1)
432+		if(!parse(w, f))
433+			return error("Unknown token", w);
434+	return resolve();
435+}
436+
437+static void
438+review(char *filename)
439+{
440+	int i;
441+	for(i = 0; i < p.llen; i++)
442+		if(p.labels[i].name[0] >= 'A' && p.labels[i].name[0] <= 'Z')
443+			continue; /* Ignore capitalized labels(devices) */
444+		else if(!p.labels[i].refs)
445+			fprintf(stderr, "-- Unused label: %s\n", p.labels[i].name);
446+	fprintf(stderr,
447+		"Assembled %s in %d bytes(%.2f%% used), %d labels, %d macros.\n",
448+		filename,
449+		p.length - TRIM,
450+		(p.length - TRIM) / 652.80,
451+		p.llen,
452+		p.mlen);
453+}
454+
455+int
456+main(int argc, char *argv[])
457+{
458+	FILE *src, *dst;
459+	if(argc < 3)
460+		return !error("usage", "input.tal output.rom");
461+	if(!(src = fopen(argv[1], "r")))
462+		return !error("Invalid input", argv[1]);
463+	if(!assemble(src))
464+		return !error("Assembly", "Failed to assemble rom.");
465+	if(!(dst = fopen(argv[2], "wb")))
466+		return !error("Invalid Output", argv[2]);
467+	if(p.length <= TRIM)
468+		return !error("Assembly", "Output rom is empty.");
469+	fwrite(p.data + TRIM, p.length - TRIM, 1, dst);
470+	review(argv[2]);
471+	return 0;
472+}