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
M
build.sh
M
build.sh
+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+}