commit 70329bd
zzo38
·
2022-10-28 02:33:43 +0000 UTC
parent 7e345a5
Add main.c and uxnasm.c
A
main.c
+735,
-0
1@@ -0,0 +1,735 @@
2+#if 0
3+gcc -s -O2 -Wno-unused-result main.c `sdl-config --cflags --libs`
4+exit
5+#endif
6+
7+// This file is public domain.
8+
9+#include "SDL.h"
10+#include <dirent.h>
11+#include <err.h>
12+#include <stdio.h>
13+#include <stdlib.h>
14+#include <string.h>
15+#include <sys/stat.h>
16+#include <time.h>
17+#include <unistd.h>
18+
19+#define GET16(p) (((p)[0]<<8)|(p)[1])
20+#define PUT16(p,q) ((p)[0]=(q)>>8,(p)[1]=(q))
21+
22+typedef struct {
23+ Uint8 p;
24+ Uint8 d[255];
25+} Stack;
26+
27+typedef struct Device {
28+ Uint8 d[16];
29+ Uint8(*in)(struct Device*,Uint8);
30+ void(*out)(struct Device*,Uint8);
31+ void*aux;
32+} Device;
33+
34+typedef struct {
35+ FILE*file;
36+ DIR*dir;
37+ char name[4096];
38+ struct dirent*de;
39+ Uint8 mode;
40+} UxnFile;
41+
42+static SDL_Color colors[64]={
43+ // 0
44+ {0xFF,0xFF,0xFF,0},
45+ {0x00,0x00,0x00,0},
46+ {0x00,0xFF,0xFF,0},
47+ {0xFF,0x00,0x00,0},
48+ {0xAA,0xAA,0xAA,0},
49+ {0x43,0x43,0x33,0},
50+ {0x43,0xAA,0xAA,0},
51+ {0xAA,0x43,0x33,0},
52+ // 1
53+ {0x00,0x00,0x00,0},
54+ {0xFF,0xFF,0xFF,0},
55+ {0x00,0xFF,0xFF,0},
56+ {0xFF,0x00,0x00,0},
57+ {0x43,0x43,0x33,0},
58+ {0xAA,0xAA,0xAA,0},
59+ {0x43,0xAA,0xAA,0},
60+ {0xAA,0x43,0x33,0},
61+ // 2
62+ {0x10,0x30,0x10,0},
63+ {0x60,0xE0,0x60,0},
64+ {0x60,0x60,0xE0,0},
65+ {0xE0,0x60,0x60,0},
66+ {0x10,0x80,0x10,0},
67+ {0x18,0xFF,0x18,0},
68+ {0x18,0x18,0xFF,0},
69+ {0xFF,0x18,0x18,0},
70+ // 3
71+ {0x55,0x55,0x55,0},
72+ {0xFF,0x55,0x00,0},
73+ {0x00,0x00,0x00,0},
74+ {0xFF,0xFF,0xFF,0},
75+ {0x55,0x55,0x55,0},
76+ {0xFF,0x55,0x00,0},
77+ {0x00,0x00,0x00,0},
78+ {0xFF,0xFF,0xFF,0},
79+ // 4
80+ {0x55,0x55,0x55,0},
81+ {0xFF,0x55,0x00,0},
82+ {0xFF,0xFF,0xFF,0},
83+ {0x00,0x00,0x00,0},
84+ {0x55,0x55,0x55,0},
85+ {0xFF,0x55,0x00,0},
86+ {0xFF,0xFF,0xFF,0},
87+ {0x00,0x00,0x00,0},
88+ // 5
89+ {0xFF,0xFF,0xFF,0},
90+ {0xAA,0xAA,0xAA,0},
91+ {0x55,0x55,0x55,0},
92+ {0x00,0x00,0x00,0},
93+ {0xFF,0x80,0x00,0},
94+ {0xC6,0x60,0x00,0},
95+ {0x94,0x40,0x00,0},
96+ {0x72,0x20,0x00,0},
97+ // 6
98+ {0xAA,0xAA,0xAA,0},
99+ {0x00,0x00,0x00,0},
100+ {0xAA,0xAA,0xAA,0},
101+ {0x00,0x00,0x00,0},
102+ {0x91,0xAA,0x9A,0},
103+ {0x00,0x00,0x00,0},
104+ {0x91,0xAA,0x9A,0},
105+ {0x19,0x15,0x00,0},
106+ // 7
107+ {0xFF,0xFF,0xFF,0},
108+ {0x00,0x00,0x00,0},
109+ {0x77,0xCC,0xAA,0},
110+ {0x99,0x22,0x44,0},
111+ {0xFF,0xFF,0xFF,0},
112+ {0x00,0x00,0x00,0},
113+ {0x77,0xCC,0xAA,0},
114+ {0x99,0x22,0x44,0},
115+};
116+
117+static const Uint8 blend[4][16]={
118+ {0,0,0,0,1,4,1,1,2,2,4,2,3,3,3,4},
119+ {0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3},
120+ {1,2,3,1,1,2,3,1,1,2,3,1,1,2,3,1},
121+ {2,3,1,2,2,3,1,2,2,3,1,2,2,3,1,2},
122+};
123+
124+static Uint8 mem[0x30000];
125+static Stack ds,rs;
126+static Device device[16];
127+static Uint16 scr_w,scr_h;
128+static Uint8*bglayer;
129+static Uint8*fglayer;
130+static UxnFile uxnfile0;
131+static UxnFile uxnfile1;
132+
133+static const char*rom_name;
134+static Uint8 use_console=1;
135+static Uint8 use_screen=1;
136+static Uint8 use_mouse=1;
137+static Uint8 use_debug=0;
138+static Uint8 use_utc=0;
139+static Uint8 hide_cursor=0;
140+static Uint8 allow_write=0;
141+static Uint8 zoom=1;
142+static Sint16 default_width=512;
143+static Sint16 default_height=320;
144+static Uint32 scrflags=SDL_SWSURFACE;
145+static Uint16 timer_rate=16;
146+static Uint8 paused=0;
147+static Uint8 picture_changed=0;
148+static Uint8 size_changed=0;
149+static volatile Uint8 timer_expired=0;
150+static SDL_Surface*screen;
151+static Uint8 layers;
152+static Uint8 palet=7;
153+
154+static void debug(int i) {
155+ if(i) fprintf(stderr,"Debug(%02X)\n",i);
156+ fprintf(stderr,"ds:");
157+ for(i=0;i<ds.p;i++) fprintf(stderr," %02X",ds.d[i]);
158+ fputc('\n',stderr);
159+ fprintf(stderr,"rs:");
160+ for(i=0;i<rs.p;i++) fprintf(stderr," %02X",rs.d[i]);
161+ fputc('\n',stderr);
162+}
163+
164+static void uxnerr(int n,int pc) {
165+ if(use_debug) debug(-1);
166+ fflush(stdout);
167+ fflush(stderr);
168+ errx(2,"Uxn error %d at 0x%04X",n,pc);
169+}
170+
171+#define Push8(z) ({ if(s->p>254) uxnerr(2,pc); s->d[s->p++]=(z); })
172+#define Push16(z) ({ if(s->p>253) uxnerr(2,pc); s->d[s->p++]=(z)>>8; s->d[s->p++]=(z); })
173+#define Push(z) ({ if(s->p>(v&0x20?253:254)) uxnerr(2,pc); if(v&0x20) s->d[s->p++]=(z)>>8; s->d[s->p++]=(z); })
174+#define Pop8(z) ({ if(!k) uxnerr(1,pc); z=s->d[--k]; if(v<0x80) s->p=k; })
175+#define Pop16(z) ({ if(k<2) uxnerr(1,pc); z=s->d[--k]; z|=s->d[--k]<<8; if(v<0x80) s->p=k; })
176+#define Pop(z) ({ if(v&0x20) Pop16(z); else Pop8(z); })
177+
178+static void run(Uint16 pc) {
179+ Stack*s;
180+ Uint8 k,v;
181+ Uint16 x,y,z;
182+ if(!pc) return;
183+ while(v=mem[pc++]) {
184+ // if(use_debug) printf("! pc=%04X v=%02X ds=%02X rs=%02X\n",pc-1,v,ds.p,rs.p);
185+ s=(v&0x40?&rs:&ds);
186+ k=s->p;
187+ switch(v&0x1F) {
188+ case 0x00: Push8(mem[pc++]); if(v&0x20) Push8(mem[pc++]); break;
189+ case 0x01: Pop(x); Push(x+1); break;
190+ case 0x02: Pop(x); break;
191+ case 0x03: Pop(x); Pop(y); Push(x); break;
192+ case 0x04: Pop(x); Pop(y); Push(x); Push(y); break;
193+ case 0x05: Pop(z); Pop(y); Pop(x); Push(y); Push(z); Push(x); break;
194+ case 0x06: Pop(x); Push(x); Push(x); break;
195+ case 0x07: Pop(x); Pop(y); Push(y); Push(x); Push(y); break;
196+ case 0x08: Pop(x); Pop(y); Push8(y==x?1:0); break;
197+ case 0x09: Pop(x); Pop(y); Push8(y!=x?1:0); break;
198+ case 0x0A: Pop(x); Pop(y); Push8(y>x?1:0); break;
199+ case 0x0B: Pop(x); Pop(y); Push8(y<x?1:0); break;
200+ case 0x0C: Pop(x); jump: if(v&0x20) pc=x; else pc+=(Sint8)x; break;
201+ case 0x0D: Pop(x); Pop8(y); if(y) goto jump; break;
202+ case 0x0E: Pop(x); y=pc; goto stash;
203+ case 0x0F: Pop(x); goto stash;
204+ case 0x10: Pop8(x); peek: if(v&0x20) x=GET16(mem+x); else x=mem[x]; Push(x); break;
205+ case 0x11: Pop8(x); Pop(y); poke: if(v&0x20) PUT16(mem+x,y); else mem[x]=y; break;
206+ case 0x12: Pop8(x); x=pc+(Sint8)x; goto peek; break;
207+ case 0x13: Pop8(x); Pop(y); x=pc+(Sint8)x; goto poke; break;
208+ case 0x14: Pop16(x); goto peek; break;
209+ case 0x15: Pop16(x); Pop(y); goto poke; break;
210+ case 0x16:
211+ Pop8(x);
212+ z=(x>>4)&15; x&=15;
213+ y=device[z].in(device+z,x);
214+ if(v&0x20) y=(y<<8)|device[z].in(device+z,x+1);
215+ Push(y);
216+ break;
217+ case 0x17:
218+ Pop8(x); Pop(y);
219+ z=(x>>4)&15; x&=15;
220+ if(v&0x20) {
221+ device[z].d[x]=y>>8;
222+ device[z].d[(x+1)&15]=y;
223+ device[z].out(device+z,x);
224+ device[z].out(device+z,x+1);
225+ } else {
226+ device[z].d[x]=y;
227+ device[z].out(device+z,x);
228+ }
229+ break;
230+ case 0x18: Pop(x); Pop(y); Push(y+x); break;
231+ case 0x19: Pop(x); Pop(y); Push(y-x); break;
232+ case 0x1A: Pop(x); Pop(y); Push(y*x); break;
233+ case 0x1B: Pop(x); Pop(y); if(!x) uxnerr(3,pc); Push(y/x); break;
234+ case 0x1C: Pop(x); Pop(y); Push(y&x); break;
235+ case 0x1D: Pop(x); Pop(y); Push(y|x); break;
236+ case 0x1E: Pop(x); Pop(y); Push(y^x); break;
237+ case 0x1F: Pop8(x); Pop(y); z=x>>4; x&=15; Push((y>>x)<<z); break;
238+ }
239+ continue;
240+ stash:
241+ s=(v&0x40?&ds:&rs);
242+ if(v&1) {
243+ Push(x);
244+ } else {
245+ Push16(y);
246+ if(v&0x20) pc=x; else pc+=(Sint8)x;
247+ }
248+ }
249+ fflush(stdout);
250+}
251+
252+static void load_rom(void) {
253+ FILE*fp=fopen(rom_name,"r");
254+ if(!fp) err(1,"Cannot load program file");
255+ memset(mem,0,0x30000);
256+ fread(mem+0x0100,1,0x30000-0x0100,fp);
257+ if(ferror(fp)) err(1,"Cannot load program file");
258+ fclose(fp);
259+}
260+
261+static Uint8 default_in(Device*misc1,Uint8 misc2) { return misc1->d[misc2&15]; }
262+static void default_out(Device*misc1,Uint8 misc2) {}
263+
264+static Uint8 system_in(Device*dev,Uint8 id) {
265+ switch(id) {
266+ case 2: return rs.p;
267+ case 3: return ds.p;
268+ default: return dev->d[id];
269+ }
270+}
271+
272+static void system_out(Device*dev,Uint8 id) {
273+ switch(id) {
274+ case 2: rs.p=dev->d[2]; break;
275+ case 3: ds.p=dev->d[3]; break;
276+ case 8 ... 13:
277+ if(!screen) break;
278+ colors[070].r=colors[074].r=(dev->d[8]>>4)*0x11;
279+ colors[071].r=colors[075].r=(dev->d[8]&15)*0x11;
280+ colors[072].r=colors[076].r=(dev->d[9]>>4)*0x11;
281+ colors[073].r=colors[077].r=(dev->d[9]&15)*0x11;
282+ colors[070].g=colors[074].g=(dev->d[10]>>4)*0x11;
283+ colors[071].g=colors[075].g=(dev->d[10]&15)*0x11;
284+ colors[072].g=colors[076].g=(dev->d[11]>>4)*0x11;
285+ colors[073].g=colors[077].g=(dev->d[11]&15)*0x11;
286+ colors[070].b=colors[074].b=(dev->d[12]>>4)*0x11;
287+ colors[071].b=colors[075].b=(dev->d[12]&15)*0x11;
288+ colors[072].b=colors[076].b=(dev->d[13]>>4)*0x11;
289+ colors[073].b=colors[077].b=(dev->d[13]&15)*0x11;
290+ if(palet==7) {
291+ SDL_SetColors(screen,colors+palet*8,0,8);
292+ picture_changed=1;
293+ }
294+ break;
295+ case 14: if(dev->d[14] && use_debug) debug(dev->d[14]); break;
296+ case 15: if(dev->d[15]) exit(0); break;
297+ }
298+}
299+
300+static void console_out(Device*dev,Uint8 id) {
301+ switch(id) {
302+ case 8: putchar(dev->d[8]); break;
303+ case 9: fputc(dev->d[9],stderr); break;
304+ }
305+}
306+
307+static Uint16 files_stat(char*name,Uint16 len,Uint8*out) {
308+ struct stat st;
309+ if(strlen(name)+7>len || stat(name,&st)) return 0;
310+ if(st.st_size<0x10000) return snprintf(out,len,"%04x %s\n",(unsigned int)st.st_size,name);
311+ else return snprintf(out,len,"???? %s\n",name);
312+ return 0;
313+}
314+
315+static void files_out(Device*dev,Uint8 id) {
316+ UxnFile*uf=dev->aux;
317+ Uint16 len=GET16(dev->d+10);
318+ Uint16 addr,x,y;
319+ int i;
320+ switch(id) {
321+ case 5:
322+ addr=GET16(dev->d+4);
323+ dev->d[2]=dev->d[3]=0;
324+ if(!uf->name[0]) break;
325+ y=files_stat(uf->name,len,mem+addr);
326+ if(y<=len) PUT16(dev->d+2,y);
327+ break;
328+ case 9:
329+ addr=GET16(dev->d+8);
330+ if(uf->file) fclose(uf->file);
331+ if(uf->dir) closedir(uf->dir);
332+ uf->file=0;
333+ uf->dir=0;
334+ uf->de=0;
335+ uf->mode=0;
336+ dev->d[2]=dev->d[3]=0;
337+ x=GET16(dev->d+8);
338+ for(i=0;i<4095;i++) {
339+ if(!(uf->name[i]=mem[x+i])) break;
340+ // if(mem[x+i]=='/') uf->name[i]=0;
341+ }
342+ if(use_debug) fprintf(stderr,"! File: %s\n",uf->name);
343+ break;
344+ case 13:
345+ addr=GET16(dev->d+12);
346+ dev->d[2]=dev->d[3]=0;
347+ read:
348+ if(uf->mode==1) {
349+ if(!len) break;
350+ x=fread(mem+addr,1,len,uf->file);
351+ PUT16(dev->d+2,x);
352+ if(ferror(uf->file)) {
353+ warn("Error reading file");
354+ clearerr(uf->file);
355+ }
356+ } else if(uf->mode==3) {
357+ //TODO
358+ } else {
359+ if(uf->file) fclose(uf->file);
360+ if(uf->dir) closedir(uf->dir);
361+ uf->file=0;
362+ uf->dir=0;
363+ uf->de=0;
364+ if(!uf->name[0]) break;
365+ if(uf->name[0]=='.' && !uf->name[1] && (uf->dir=opendir(uf->name))) {
366+ uf->mode=3;
367+ goto read;
368+ } else if(uf->file=fopen(uf->name,"r")) {
369+ uf->mode=1;
370+ goto read;
371+ } else {
372+ warn("Cannot open file for reading");
373+ }
374+ }
375+ break;
376+ case 15:
377+ addr=GET16(dev->d+14);
378+ dev->d[2]=dev->d[3]=0;
379+ if(!allow_write) break;
380+ write:
381+ //TODO
382+ break;
383+ }
384+}
385+
386+static Uint8 files_in(Device*dev,Uint8 id) {
387+ UxnFile*uf=dev->aux;
388+ int c;
389+ switch(id) {
390+ case 12: case 13:
391+ dev->d[2]=dev->d[3]=0;
392+ read:
393+ if(uf->mode==1) {
394+ c=fgetc(uf->file);
395+ if(c==EOF) {
396+ if(ferror(uf->file)) {
397+ warn("Error reading file");
398+ clearerr(uf->file);
399+ }
400+ c=0;
401+ } else {
402+ dev->d[3]=1;
403+ }
404+ return c;
405+ } else if(uf->mode==3) {
406+ return 0;
407+ } else {
408+ if(uf->file) fclose(uf->file);
409+ if(uf->dir) closedir(uf->dir);
410+ uf->file=0;
411+ uf->dir=0;
412+ uf->de=0;
413+ if(!uf->name[0]) break;
414+ if(uf->name[0]=='.' && !uf->name[1] && (uf->dir=opendir(uf->name))) {
415+ uf->mode=3;
416+ goto read;
417+ } else if(uf->file=fopen(uf->name,"r")) {
418+ uf->mode=1;
419+ goto read;
420+ } else {
421+ warn("Cannot open file for reading");
422+ }
423+ }
424+ return 0;
425+ default: return dev->d[id];
426+ }
427+}
428+
429+static Uint8 datetime_in(Device*dev,Uint8 id) {
430+ time_t t=time(0);
431+ struct tm r;
432+ if(use_utc) gmtime_r(&t,&r); else localtime_r(&t,&r);
433+ switch(id) {
434+ case 0: return (r.tm_year+1900)>>8;
435+ case 1: return r.tm_year+1900;
436+ case 2: return r.tm_mon;
437+ case 3: return r.tm_mday;
438+ case 4: return r.tm_hour;
439+ case 5: return r.tm_min;
440+ case 6: return r.tm_sec;
441+ case 7: return r.tm_wday;
442+ case 8: return r.tm_yday>>8;
443+ case 9: return r.tm_yday;
444+ case 10: return r.tm_isdst;
445+ default: return dev->d[id];
446+ }
447+}
448+
449+static void set_screen_mode(int w,int h) {
450+ picture_changed=size_changed=0;
451+ free(bglayer);
452+ if(screen) SDL_FreeSurface(screen);
453+ if(w<1) w=1;
454+ if(h<1) h=1;
455+ scr_w=w;
456+ scr_h=h;
457+ screen=SDL_SetVideoMode(w*zoom,h*zoom,8,scrflags);
458+ if(!screen) errx(1,"Cannot set screen mode: %s",SDL_GetError());
459+ SDL_SetColors(screen,colors+palet*8,0,8);
460+ bglayer=calloc(w*h,2);
461+ if(!bglayer) err(1,"Memory allocation error");
462+ fglayer=bglayer+w*h;
463+}
464+
465+static void draw_sprite(Uint16 x,Uint16 y,Uint16 addr,Uint8 v) {
466+ Uint8 b=v&0x80;
467+ Uint8 u;
468+ Uint16 c;
469+ Uint8*p=v&0x40?fglayer:bglayer;
470+ Uint8*m;
471+ if(x+8>scr_w || y+8>scr_h || addr>0x10000-(b?16:8)) return;
472+ p+=y*scr_w+x;
473+ m=mem+addr;
474+ for(y=0;y<8;y++) {
475+ c=m[y]|(b?m[y+8]<<8:0);
476+ for(x=0;x<8;x++) {
477+ u=(c&1)|((c>>7)&2);
478+ c>>=1;
479+ if((u=blend[u][v&0x0F])!=4) p[(y^(v&0x20?7:0))*scr_w+(x^(v&0x10?0:7))]=u;
480+ }
481+ }
482+}
483+
484+static void screen_out(Device*dev,Uint8 id) {
485+ int i;
486+ Uint16 w;
487+ Uint16 x=GET16(dev->d+8);
488+ Uint16 y=GET16(dev->d+10);
489+ Uint8 z=dev->d[id];
490+ switch(id) {
491+ case 2: case 3:
492+ x=GET16(dev->d+2);
493+ if(x!=scr_w) scr_w=x,size_changed=1;
494+ break;
495+ case 4: case 5:
496+ x=GET16(dev->d+4);
497+ if(x!=scr_h) scr_h=x,size_changed=1;
498+ break;
499+ case 14:
500+ if(size_changed) set_screen_mode(scr_w,scr_h);
501+ picture_changed=1;
502+ if(x<scr_w && y<scr_h) (z&0x40?fglayer:bglayer)[x+y*scr_w]=z&3;
503+ if(dev->d[6]&0x01) PUT16(dev->d+8,x+1);
504+ if(dev->d[6]&0x02) PUT16(dev->d+10,y+1);
505+ break;
506+ case 15:
507+ if(size_changed) set_screen_mode(scr_w,scr_h);
508+ picture_changed=1;
509+ w=GET16(dev->d+12);
510+ for(i=0;i<=(dev->d[6]>>4);i++) {
511+ draw_sprite(x+i*(dev->d[6]&0x02?8:0),y+i*(dev->d[6]&0x01?8:0),w,z);
512+ if(dev->d[6]&0x04) w+=z&0x80?16:8;
513+ }
514+ if(dev->d[6]&0x01) PUT16(dev->d+8,x+8);
515+ if(dev->d[6]&0x02) PUT16(dev->d+10,y+8);
516+ if(dev->d[6]&0x04) PUT16(dev->d+12,w);
517+ break;
518+ }
519+}
520+
521+static Uint32 timer_callback(Uint32 interval) {
522+ if(!timer_expired) {
523+ SDL_Event e;
524+ timer_expired=1;
525+ e.type=SDL_USEREVENT;
526+ SDL_PushEvent(&e);
527+ timer_expired=1;
528+ }
529+ return interval;
530+}
531+
532+static void redraw(void) {
533+ int a,r,u,v,x,y;
534+ Uint8*p;
535+ picture_changed=0;
536+ SDL_LockSurface(screen);
537+ a=0;
538+ p=screen->pixels;
539+ r=screen->pitch;
540+ if(layers) {
541+ for(y=0;y<scr_h;y++) {
542+ for(x=0;x<scr_w;x++) {
543+ u=(layers==1?fglayer:bglayer)[a];
544+ for(v=0;v<zoom;v++) memset(p+v*r+x*zoom,u,zoom);
545+ a++;
546+ }
547+ p+=r*zoom;
548+ }
549+ } else {
550+ for(y=0;y<scr_h;y++) {
551+ for(x=0;x<scr_w;x++) {
552+ u=fglayer[a]?fglayer[a]+4:bglayer[a];
553+ for(v=0;v<zoom;v++) memset(p+v*r+x*zoom,u,zoom);
554+ a++;
555+ }
556+ p+=r*zoom;
557+ }
558+ }
559+ SDL_UnlockSurface(screen);
560+ SDL_Flip(screen);
561+}
562+
563+static void set_cursor(void) {
564+ if(hide_cursor) SDL_ShowCursor(paused || !use_mouse);
565+}
566+
567+static int run_screen(void) {
568+ SDL_Event e;
569+ int i,j,k;
570+ while(SDL_WaitEvent(&e)) {
571+ switch(e.type) {
572+ case SDL_QUIT: return 0;
573+ case SDL_VIDEOEXPOSE: redraw(); break;
574+ case SDL_USEREVENT: timer_expired=0; if(!paused) run(GET16(device[2].d)); break;
575+ case SDL_KEYDOWN:
576+ i=0;
577+ switch(e.key.keysym.sym) {
578+ case SDLK_LCTRL: case SDLK_END: i=0x01; break;
579+ case SDLK_LALT: i=0x02; break;
580+ case SDLK_LSHIFT: i=0x04; break;
581+ case SDLK_HOME: i=0x08; break;
582+ case SDLK_UP: i=0x10; break;
583+ case SDLK_DOWN: i=0x20; break;
584+ case SDLK_LEFT: i=0x40; break;
585+ case SDLK_RIGHT: i=0x80; break;
586+ case SDLK_F1: run(GET16(device[2].d)); timer_expired=0; break;
587+ case SDLK_F3: use_mouse^=1; set_cursor(); break;
588+ case SDLK_F4: layers=(layers+1)%3; redraw(); break;
589+ case SDLK_F5: redraw(); if(SDL_GetModState()&KMOD_RSHIFT) debug(-1); break;
590+ case SDLK_F6: palet=(palet+1)&7; SDL_SetColors(screen,colors+palet*8,0,8); redraw(); break;
591+ case SDLK_F7: palet=(palet-1)&7; SDL_SetColors(screen,colors+palet*8,0,8); redraw(); break;
592+ case SDLK_F9: return 1;
593+ case SDLK_F10: return 0;
594+ case SDLK_PAUSE: paused^=1; set_cursor(); break;
595+ }
596+ if(paused) break;
597+ if(i) {
598+ if(!(device[8].d[2]&i)) {
599+ device[8].d[2]|=i;
600+ run(GET16(device[8].d));
601+ }
602+ } else if(i=e.key.keysym.unicode) {
603+ device[8].d[3]=i;
604+ run(GET16(device[8].d));
605+ device[8].d[3]=0;
606+ }
607+ break;
608+ case SDL_KEYUP:
609+ i=0;
610+ switch(e.key.keysym.sym) {
611+ case SDLK_LCTRL: case SDLK_END: i=0x01; break;
612+ case SDLK_LALT: i=0x02; break;
613+ case SDLK_LSHIFT: i=0x04; break;
614+ case SDLK_HOME: i=0x08; break;
615+ case SDLK_UP: i=0x10; break;
616+ case SDLK_DOWN: i=0x20; break;
617+ case SDLK_LEFT: i=0x40; break;
618+ case SDLK_RIGHT: i=0x80; break;
619+ }
620+ if(paused) break;
621+ if(device[8].d[2]&i) {
622+ device[8].d[2]&=~i;
623+ run(GET16(device[8].d));
624+ }
625+ break;
626+ case SDL_MOUSEMOTION:
627+ case SDL_MOUSEBUTTONDOWN:
628+ case SDL_MOUSEBUTTONUP:
629+ if(paused || !use_mouse) break;
630+ k=SDL_GetMouseState(&i,&j);
631+ i/=zoom; j/=zoom;
632+ if(i<0 || i>=screen->w || j<0 || j>=screen->h) break;
633+ PUT16(device[9].d+2,i);
634+ PUT16(device[9].d+4,j);
635+ device[9].d[6]=k&7;
636+ run(GET16(device[9].d));
637+ break;
638+ }
639+ if(picture_changed) redraw();
640+ }
641+ return 0;
642+}
643+
644+static void set_datetime(char*s) {
645+ int i=2;
646+ i=strtol(s,&s,10);
647+ PUT16(device[12].d,i);
648+ if(*s) s++;
649+ i=2;
650+ while(*s && i<15) {
651+ device[12].d[i++]=strtol(s,&s,10);
652+ if(*s) s++;
653+ }
654+}
655+
656+int main(int argc,char**argv) {
657+ int i,j;
658+ for(i=0;i<16;i++) {
659+ device[i].in=default_in;
660+ device[i].out=default_out;
661+ }
662+ device[0].in=system_in;
663+ device[0].out=system_out;
664+ device[10].aux=&uxnfile0;
665+ device[11].aux=&uxnfile1;
666+ device[12].in=datetime_in;
667+ while((i=getopt(argc,argv,"+ADFNQT:YZdh:inp:qt:w:yz:"))>0) switch(i) {
668+ case 'A': scrflags|=SDL_ASYNCBLIT; break;
669+ case 'D': scrflags|=SDL_DOUBLEBUF; break;
670+ case 'F': scrflags|=SDL_FULLSCREEN; break;
671+ case 'N': scrflags|=SDL_NOFRAME; break;
672+ case 'Q': use_mouse=0; break;
673+ case 'T': device[12].in=default_in; set_datetime(optarg); break;
674+ case 'Y': allow_write=1; device[10].out=device[11].out=files_out; device[10].in=device[11].in=files_in; break;
675+ case 'Z': use_utc=1; break;
676+ case 'd': use_debug=1; break;
677+ case 'h': default_height=strtol(optarg,0,10); break;
678+ case 'i': hide_cursor=1; break;
679+ case 'n': use_screen=0; break;
680+ case 'p': palet=strtol(optarg,0,10)&7; break;
681+ case 'q': use_console=0; break;
682+ case 't': timer_rate=strtol(optarg,0,10); break;
683+ case 'w': default_width=strtol(optarg,0,10); break;
684+ case 'y': allow_write=0; device[10].out=device[11].out=files_out; device[10].in=device[11].in=files_in; break;
685+ case 'z': zoom=strtol(optarg,0,10); break;
686+ default: return 1;
687+ }
688+ if(optind==argc) errx(1,"Required argument missing");
689+ rom_name=argv[optind];
690+ load_rom();
691+ if(use_screen) {
692+ if(zoom<1) errx(1,"Zoom out of range");
693+ if(default_height<1 || default_width<1) errx(1,"Screen size out of range");
694+ if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) errx(1,"Cannot initialize SDL: %s",SDL_GetError());
695+ atexit(SDL_Quit);
696+ set_screen_mode(default_width,default_height);
697+ SDL_EnableUNICODE(1);
698+ device[2].out=screen_out;
699+ PUT16(device[2].d+2,default_width);
700+ PUT16(device[2].d+4,default_height);
701+ if(timer_rate) SDL_SetTimer(timer_rate,timer_callback);
702+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
703+ SDL_WM_SetCaption(rom_name,rom_name);
704+ set_cursor();
705+ }
706+ if(use_console) device[1].out=console_out;
707+ if(device[12].d[15]) device[12].d[15]=0; else device[12].in=datetime_in;
708+ if(optind+1<argc) strncpy(uxnfile0.name,argv[optind+1],4095);
709+ if(optind+2<argc) strncpy(uxnfile1.name,argv[optind+2],4095);
710+ run(0x0100);
711+ if(device[1].d[0] || device[1].d[1]) for(i=optind+1;i<argc;i++) {
712+ for(j=0;argv[i][j];j++) {
713+ device[1].d[2]=argv[i][j];
714+ run(GET16(device[1].d));
715+ }
716+ device[1].d[2]='\n';
717+ run(GET16(device[1].d));
718+ }
719+ if(use_screen) {
720+ while(run_screen()) {
721+ for(i=0;i<15;i++) for(j=0;j<16;j++) device[i].d[j]=0;
722+ load_rom();
723+ PUT16(device[2].d+2,default_width);
724+ PUT16(device[2].d+4,default_height);
725+ set_screen_mode(default_width,default_height);
726+ run(0x0100);
727+ }
728+ } else {
729+ while((device[1].d[0] || device[1].d[1]) && ((i=getchar())>=0)) {
730+ device[1].d[2]=i;
731+ run(GET16(device[1].d));
732+ }
733+ }
734+ return 0;
735+}
736+
A
uxnasm.c
+444,
-0
1@@ -0,0 +1,444 @@
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+
49+/* clang-format off */
50+
51+static char ops[][4] = {
52+ "LIT", "INC", "POP", "NIP", "SWP", "ROT", "DUP", "OVR",
53+ "EQU", "NEQ", "GTH", "LTH", "JMP", "JCN", "JSR", "STH",
54+ "LDZ", "STZ", "LDR", "STR", "LDA", "STA", "DEI", "DEO",
55+ "ADD", "SUB", "MUL", "DIV", "AND", "ORA", "EOR", "SFT"
56+};
57+
58+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 */
59+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 */
60+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 */
61+static int slen(char *s) { int i = 0; while(s[i]) i++; return i; } /* string length */
62+static int spos(char *s, char c) { Uint8 i = 0, j; while((j = s[i++])) if(j == c) return i; return -1; } /* character position */
63+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 */
64+static char *scat(char *dst, const char *src) { char *ptr = dst + slen(dst); while(*src) *ptr++ = *src++; *ptr = '\0'; return dst; } /* string cat */
65+
66+/* clang-format on */
67+
68+static int parse(char *w, FILE *f);
69+
70+static int
71+error(const char *name, const char *msg)
72+{
73+ fprintf(stderr, "%s: %s\n", name, msg);
74+ return 0;
75+}
76+
77+static char *
78+sublabel(char *src, char *scope, char *name)
79+{
80+ return scat(scat(scpy(scope, src, 0x40), "/"), name);
81+}
82+
83+static Macro *
84+findmacro(char *name)
85+{
86+ int i;
87+ for(i = 0; i < p.mlen; i++)
88+ if(scmp(p.macros[i].name, name, 0x40))
89+ return &p.macros[i];
90+ return NULL;
91+}
92+
93+static Label *
94+findlabel(char *name)
95+{
96+ int i;
97+ for(i = 0; i < p.llen; i++)
98+ if(scmp(p.labels[i].name, name, 0x40))
99+ return &p.labels[i];
100+ return NULL;
101+}
102+
103+static Uint8
104+findopcode(char *s)
105+{
106+ int i;
107+ for(i = 0; i < 0x20; i++) {
108+ int m = 0;
109+ if(!scmp(ops[i], s, 3))
110+ continue;
111+ if(!i) i |= (1 << 7); /* force keep for LIT */
112+ while(s[3 + m]) {
113+ if(s[3 + m] == '2')
114+ i |= (1 << 5); /* mode: short */
115+ else if(s[3 + m] == 'r')
116+ i |= (1 << 6); /* mode: return */
117+ else if(s[3 + m] == 'k')
118+ i |= (1 << 7); /* mode: keep */
119+ else
120+ return 0; /* failed to match */
121+ m++;
122+ }
123+ return i;
124+ }
125+ return 0;
126+}
127+
128+static int
129+makemacro(char *name, FILE *f)
130+{
131+ Macro *m;
132+ char word[0x40];
133+ if(findmacro(name))
134+ return error("Macro duplicate", name);
135+ if(sihx(name) && slen(name) % 2 == 0)
136+ return error("Macro name is hex number", name);
137+ if(findopcode(name) || scmp(name, "BRK", 4) || !slen(name))
138+ return error("Macro name is invalid", name);
139+ if(p.mlen == 0x100)
140+ return error("Macros limit exceeded", name);
141+ m = &p.macros[p.mlen++];
142+ scpy(name, m->name, 0x40);
143+ while(fscanf(f, "%63s", word) == 1) {
144+ if(word[0] == '{') continue;
145+ if(word[0] == '}') break;
146+ if(word[0] == '%')
147+ return error("Macro error", name);
148+ if(m->len >= 0x40)
149+ return error("Macro size exceeded", name);
150+ scpy(word, m->items[m->len++], 0x40);
151+ }
152+ return 1;
153+}
154+
155+static int
156+makelabel(char *name)
157+{
158+ Label *l;
159+ if(findlabel(name))
160+ return error("Label duplicate", name);
161+ if(sihx(name) && (slen(name) == 2 || slen(name) == 4))
162+ return error("Label name is hex number", name);
163+ if(findopcode(name) || scmp(name, "BRK", 4) || !slen(name))
164+ return error("Label name is invalid", name);
165+ if(p.llen == 0x400)
166+ return error("Labels limit exceeded", name);
167+ l = &p.labels[p.llen++];
168+ l->addr = p.ptr;
169+ l->refs = 0;
170+ scpy(name, l->name, 0x40);
171+ return 1;
172+}
173+
174+static int
175+makereference(char *scope, char *label, Uint16 addr)
176+{
177+ char subw[0x40], parent[0x40];
178+ Reference *r;
179+ if(p.rlen == 0x800)
180+ return error("References limit exceeded", label);
181+ r = &p.refs[p.rlen++];
182+ if(label[1] == '&')
183+ scpy(sublabel(subw, scope, label + 2), r->name, 0x40);
184+ else {
185+ int pos = spos(label + 1, '/');
186+ if(pos > 0) {
187+ Label *l;
188+ if((l = findlabel(scpy(label + 1, parent, pos))))
189+ l->refs++;
190+ }
191+ scpy(label + 1, r->name, 0x40);
192+ }
193+ r->rune = label[0];
194+ r->addr = addr;
195+ return 1;
196+}
197+
198+static int
199+writebyte(Uint8 b)
200+{
201+ if(p.ptr < TRIM)
202+ return error("Writing in zero-page", "");
203+ else if(p.ptr > 0xffff)
204+ return error("Writing after the end of RAM", "");
205+ else if(p.ptr < p.length)
206+ return error("Memory overwrite", "");
207+ p.data[p.ptr++] = b;
208+ p.length = p.ptr;
209+ return 1;
210+}
211+
212+static int
213+writeopcode(char *w)
214+{
215+ Uint8 res;
216+ res = writebyte(findopcode(w));
217+ return res;
218+}
219+
220+static int
221+writeshort(Uint16 s, int lit)
222+{
223+ if(lit)
224+ if(!writebyte(findopcode("LIT2"))) return 0;
225+ return writebyte(s >> 8) && writebyte(s & 0xff);
226+}
227+
228+static int
229+writelitbyte(Uint8 b)
230+{
231+ if(!writebyte(findopcode("LIT"))) return 0;
232+ if(!writebyte(b)) return 0;
233+ return 1;
234+}
235+
236+static int
237+doinclude(const char *filename)
238+{
239+ FILE *f;
240+ char w[0x40];
241+ if(!(f = fopen(filename, "r")))
242+ return error("Include missing", filename);
243+ while(fscanf(f, "%63s", w) == 1)
244+ if(!parse(w, f))
245+ return error("Unknown token", w);
246+ fclose(f);
247+ return 1;
248+}
249+
250+static int
251+parse(char *w, FILE *f)
252+{
253+ int i;
254+ char word[0x40], subw[0x40], c;
255+ Macro *m;
256+ if(slen(w) >= 63)
257+ return error("Invalid token", w);
258+ switch(w[0]) {
259+ case '(': /* comment */
260+ if(slen(w) != 1) fprintf(stderr, "-- Malformed comment: %s\n", w);
261+ i = 1; /* track nested comment depth */
262+ while(fscanf(f, "%63s", word) == 1) {
263+ if(slen(word) != 1)
264+ continue;
265+ else if(word[0] == '(')
266+ i++;
267+ else if(word[0] == ')' && --i < 1)
268+ break;
269+ }
270+ break;
271+ case '~': /* include */
272+ if(!doinclude(w + 1))
273+ return error("Invalid include", w);
274+ break;
275+ case '%': /* macro */
276+ if(!makemacro(w + 1, f))
277+ return error("Invalid macro", w);
278+ break;
279+ case '|': /* pad-absolute */
280+ if(!sihx(w + 1))
281+ return error("Invalid padding", w);
282+ p.ptr = shex(w + 1);
283+ break;
284+ case '$': /* pad-relative */
285+ if(!sihx(w + 1))
286+ return error("Invalid padding", w);
287+ p.ptr += shex(w + 1);
288+ break;
289+ case '@': /* label */
290+ if(!makelabel(w + 1))
291+ return error("Invalid label", w);
292+ scpy(w + 1, p.scope, 0x40);
293+ break;
294+ case '&': /* sublabel */
295+ if(!makelabel(sublabel(subw, p.scope, w + 1)))
296+ return error("Invalid sublabel", w);
297+ break;
298+ case '#': /* literals hex */
299+ if(!sihx(w + 1) || (slen(w) != 3 && slen(w) != 5))
300+ return error("Invalid hex literal", w);
301+ if(slen(w) == 3) {
302+ if(!writelitbyte(shex(w + 1))) return 0;
303+ } else if(slen(w) == 5) {
304+ if(!writeshort(shex(w + 1), 1)) return 0;
305+ }
306+ break;
307+ case '.': /* literal byte zero-page */
308+ makereference(p.scope, w, p.ptr);
309+ if(!writelitbyte(0xff)) return 0;
310+ break;
311+ case ',': /* literal byte relative */
312+ makereference(p.scope, w, p.ptr);
313+ if(!writelitbyte(0xff)) return 0;
314+ break;
315+ case ';': /* literal short absolute */
316+ makereference(p.scope, w, p.ptr);
317+ if(!writeshort(0xffff, 1)) return 0;
318+ break;
319+ case ':': /* raw short absolute */
320+ makereference(p.scope, w, p.ptr);
321+ if(!writeshort(0xffff, 0)) return 0;
322+ break;
323+ case '"': /* raw string */
324+ i = 0;
325+ while((c = w[++i]))
326+ if(!writebyte(c)) return 0;
327+ break;
328+ case '[':
329+ case ']':
330+ if(slen(w) == 1) break; /* else fallthrough */
331+ default:
332+ /* opcode */
333+ if(findopcode(w) || scmp(w, "BRK", 4)) {
334+ if(!writeopcode(w)) return 0;
335+ }
336+ /* raw byte */
337+ else if(sihx(w) && slen(w) == 2) {
338+ if(!writebyte(shex(w))) return 0;
339+ }
340+ /* raw short */
341+ else if(sihx(w) && slen(w) == 4) {
342+ if(!writeshort(shex(w), 0)) return 0;
343+ }
344+ /* macro */
345+ else if((m = findmacro(w))) {
346+ for(i = 0; i < m->len; i++)
347+ if(!parse(m->items[i], f))
348+ return 0;
349+ return 1;
350+ } else
351+ return error("Unknown token", w);
352+ }
353+ return 1;
354+}
355+
356+static int
357+resolve(void)
358+{
359+ Label *l;
360+ int i;
361+ for(i = 0; i < p.rlen; i++) {
362+ Reference *r = &p.refs[i];
363+ switch(r->rune) {
364+ case '.':
365+ if(!(l = findlabel(r->name)))
366+ return error("Unknown zero-page reference", r->name);
367+ p.data[r->addr + 1] = l->addr & 0xff;
368+ l->refs++;
369+ break;
370+ case ',':
371+ if(!(l = findlabel(r->name)))
372+ return error("Unknown relative reference", r->name);
373+ p.data[r->addr + 1] = (Sint8)(l->addr - r->addr - 3);
374+ if((Sint8)p.data[r->addr + 1] != (l->addr - r->addr - 3))
375+ return error("Relative reference is too far", r->name);
376+ l->refs++;
377+ break;
378+ case ';':
379+ if(!(l = findlabel(r->name)))
380+ return error("Unknown absolute reference", r->name);
381+ p.data[r->addr + 1] = l->addr >> 0x8;
382+ p.data[r->addr + 2] = l->addr & 0xff;
383+ l->refs++;
384+ break;
385+ case ':':
386+ if(!(l = findlabel(r->name)))
387+ return error("Unknown absolute reference", r->name);
388+ p.data[r->addr + 0] = l->addr >> 0x8;
389+ p.data[r->addr + 1] = l->addr & 0xff;
390+ l->refs++;
391+ break;
392+ default:
393+ return error("Unknown reference", r->name);
394+ }
395+ }
396+ return 1;
397+}
398+
399+static int
400+assemble(FILE *f)
401+{
402+ char w[0x40];
403+ scpy("on-reset", p.scope, 0x40);
404+ while(fscanf(f, "%63s", w) == 1)
405+ if(!parse(w, f))
406+ return error("Unknown token", w);
407+ return resolve();
408+}
409+
410+static void
411+review(char *filename)
412+{
413+ int i;
414+ for(i = 0; i < p.llen; i++)
415+ if(p.labels[i].name[0] >= 'A' && p.labels[i].name[0] <= 'Z')
416+ continue; /* Ignore capitalized labels(devices) */
417+ else if(!p.labels[i].refs)
418+ fprintf(stderr, "-- Unused label: %s\n", p.labels[i].name);
419+ fprintf(stderr,
420+ "Assembled %s in %d bytes(%.2f%% used), %d labels, %d macros.\n",
421+ filename,
422+ p.length - TRIM,
423+ (p.length - TRIM) / 652.80,
424+ p.llen,
425+ p.mlen);
426+}
427+
428+int
429+main(int argc, char *argv[])
430+{
431+ FILE *src, *dst;
432+ if(argc < 3)
433+ return !error("usage", "input.tal output.rom");
434+ if(!(src = fopen(argv[1], "r")))
435+ return !error("Invalid input", argv[1]);
436+ if(!assemble(src))
437+ return !error("Assembly", "Failed to assemble rom.");
438+ if(!(dst = fopen(argv[2], "wb")))
439+ return !error("Invalid Output", argv[2]);
440+ if(p.length <= TRIM)
441+ return !error("Assembly", "Output rom is empty.");
442+ fwrite(p.data + TRIM, p.length - TRIM, 1, dst);
443+ review(argv[2]);
444+ return 0;
445+}