commit 70329bd

zzo38  ·  2022-10-28 02:33:43 +0000 UTC
parent 7e345a5
Add main.c and uxnasm.c
2 files changed,  +1179, -0
A main.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+
+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+}