diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d923eb7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +AlignAfterOpenBracket: DontAlign +AlignEscapedNewlines: DontAlign +AlignOperands: DontAlign +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: TopLevel +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: WebKit +IndentCaseLabels: false +TabWidth: 4 +IndentWidth: 4 +ContinuationIndentWidth: 4 +UseTab: ForContinuationAndIndentation +ColumnLimit: 0 +ReflowComments: false +SortIncludes: false +SpaceBeforeParens: false \ No newline at end of file diff --git a/.gitignore b/.gitignore index d06cbd8..9dc8305 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *jpg *png *bmp -/bin +*/bin *snarf *notepad *theme @@ -10,3 +10,4 @@ *rom *sym + diff --git a/ref/makefile b/ref/makefile new file mode 100644 index 0000000..59d55c8 --- /dev/null +++ b/ref/makefile @@ -0,0 +1,16 @@ +.PHONY: all run format clean + +all: bin/uxn + +run: all + @ bin/uxn test.bin + +format: + @ clang-format -i uxn.c + +clean: + @ rm -r -f bin + +bin/uxn: uxn.c + @ mkdir -p bin/ + @ cc uxn.c -o bin/uxn diff --git a/ref/test.bin b/ref/test.bin new file mode 100644 index 0000000..4f5f45f Binary files /dev/null and b/ref/test.bin differ diff --git a/ref/uxn b/ref/uxn new file mode 100755 index 0000000..6bdafe8 Binary files /dev/null and b/ref/uxn differ diff --git a/ref/uxn.c b/ref/uxn.c new file mode 100644 index 0000000..242d1f9 --- /dev/null +++ b/ref/uxn.c @@ -0,0 +1,226 @@ +#include +#include + +/* clang-format off */ + +#define PEEK2(d) (*(d) << 8 | (d)[1]) +#define POKE2(d, v) { *(d) = (v) >> 8; (d)[1] = (v); } +#define CONSOLE_STD 0x1 +#define CONSOLE_ARG 0x2 +#define CONSOLE_EOA 0x3 +#define CONSOLE_END 0x4 +#define PAGE_PROGRAM 0x0100 +#define RAM_PAGES 0x10 + +/* clang-format on */ + +typedef unsigned char Uint8; +typedef signed char Sint8; +typedef unsigned short Uint16; +typedef signed short Sint16; +typedef unsigned int Uint32; + +typedef struct { + Uint8 dat[0x100], ptr; +} Stack; + +typedef struct Uxn { + Uint8 *ram, dev[0x100]; + Stack wst, rst; +} Uxn; + +int uxn_eval(Uxn *u, Uint16 pc); + +int +system_error(char *msg, const char *err) +{ + fprintf(stderr, "%s %s\n", msg, err); + fflush(stderr); + return 0; +} + +static void +system_zero(Uxn *u, int soft) +{ + int i; + for(i = PAGE_PROGRAM * soft; i < 0x10000; i++) + u->ram[i] = 0; + for(i = 0x0; i < 0x100; i++) + u->dev[i] = 0; + u->wst.ptr = u->rst.ptr = 0; +} + +char *boot_rom; + +static int +system_load(Uxn *u, char *filename) +{ + int l, i = 0; + FILE *f = fopen(filename, "rb"); + if(!f) + return 0; + l = fread(&u->ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM, 1, f); + while(l && ++i < RAM_PAGES) + l = fread(u->ram + 0x10000 * i, 0x10000, 1, f); + fclose(f); + return 1; +} + +int +system_init(Uxn *u, Uint8 *ram, char *rom) +{ + u->ram = ram; + system_zero(u, 0); + if(!system_load(u, rom)) + if(!system_load(u, "boot.rom")) + return system_error("Init", "Failed to load rom."); + boot_rom = rom; + return 1; +} + +int +console_input(Uxn *u, char c, int type) +{ + Uint8 *d = &u->dev[0x10]; + d[0x2] = c; + d[0x7] = type; + return uxn_eval(u, PEEK2(d)); +} + +void +console_listen(Uxn *u, int i, int argc, char **argv) +{ + for(; i < argc; i++) { + char *p = argv[i]; + while(*p) console_input(u, *p++, CONSOLE_ARG); + console_input(u, '\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA); + } +} + +void +console_deo(Uint8 *d, Uint8 port) +{ + switch(port) { + case 0x8: + fputc(d[port], stdout); + fflush(stdout); + return; + case 0x9: + fputc(d[port], stderr); + fflush(stderr); + return; + } +} + +Uint8 +emu_dei(Uxn *u, Uint8 addr) +{ + return u->dev[addr]; +} + +void +emu_deo(Uxn *u, Uint8 addr, Uint8 value) +{ + Uint8 p = addr & 0x0f, d = addr & 0xf0; + u->dev[addr] = value; + switch(d) { + case 0x10: console_deo(&u->dev[d], p); break; + } +} + +#define FLIP { s = ins & 0x40 ? &u->wst : &u->rst; } +#define JUMP(x) { if(m2) pc = (x); else pc += (Sint8)(x); } +#define POP1(o) { o = s->dat[--*sp]; } +#define POP2(o) { o = s->dat[--*sp] | (s->dat[--*sp] << 0x8); } +#define POPx(o) { if(m2) { POP2(o) } else POP1(o) } +#define PUSH1(y) { s->dat[s->ptr++] = (y); } +#define PUSH2(y) { tt = (y); s->dat[s->ptr++] = tt >> 0x8; s->dat[s->ptr++] = tt; } +#define PUSHx(y) { if(m2) { PUSH2(y) } else PUSH1(y) } +#define PEEK(o, x, r) { if(m2) { r = (x); o = ram[r++] << 8 | ram[r]; } else o = ram[(x)]; } +#define POKE(x, y, r) { if(m2) { r = (x); ram[r++] = y >> 8; ram[r] = y; } else ram[(x)] = (y); } +#define DEVR(o, p) { if(m2) { o = (emu_dei(u, p) << 8) | emu_dei(u, p + 1); } else o = emu_dei(u, p); } +#define DEVW(p, y) { if(m2) { emu_deo(u, p, y >> 8); emu_deo(u, p + 1, y); } else emu_deo(u, p, y); } + +int +uxn_eval(Uxn *u, Uint16 pc) +{ + Uint8 t, kp, *sp, *ram = u->ram; + Uint16 tt, a, b, c; + if(!pc || u->dev[0x0f]) return 0; + for(;;) { + Uint8 ins = ram[pc++], m2 = ins & 0x20; + Stack *s = ins & 0x40 ? &u->rst : &u->wst; + if(ins & 0x80) kp = s->ptr, sp = &kp; + else sp = &s->ptr; + switch(ins & 0x1f) { + case 0x00: case 0x20: + switch(ins) { + case 0x00: /* BRK */ return 1; + case 0x20: /* JCI */ POP1(b) if(!b) { pc += 2; break; } + case 0x40: /* JMI */ pc += PEEK2(ram + pc) + 2; break; + case 0x60: /* JSI */ PUSH2(pc + 2) pc += PEEK2(ram + pc) + 2; break; + case 0x80: case 0xc0: /* LIT */ PUSH1(ram[pc++]) break; + case 0xa0: case 0xe0: /* LIT2 */ PUSH1(ram[pc++]) PUSH1(ram[pc++]) break; + } break; + case 0x01: /* INC */ POPx(a) PUSHx(a + 1) break; + case 0x02: /* POP */ POPx(a) break; + case 0x03: /* NIP */ POPx(a) POPx(b) PUSHx(a) break; + case 0x04: /* SWP */ POPx(a) POPx(b) PUSHx(a) PUSHx(b) break; + case 0x05: /* ROT */ POPx(a) POPx(b) POPx(c) PUSHx(b) PUSHx(a) PUSHx(c) break; + case 0x06: /* DUP */ POPx(a) PUSHx(a) PUSHx(a) break; + case 0x07: /* OVR */ POPx(a) POPx(b) PUSHx(b) PUSHx(a) PUSHx(b) break; + case 0x08: /* EQU */ POPx(a) POPx(b) PUSH1(b == a) break; + case 0x09: /* NEQ */ POPx(a) POPx(b) PUSH1(b != a) break; + case 0x0a: /* GTH */ POPx(a) POPx(b) PUSH1(b > a) break; + case 0x0b: /* LTH */ POPx(a) POPx(b) PUSH1(b < a) break; + case 0x0c: /* JMP */ POPx(a) JUMP(a) break; + case 0x0d: /* JCN */ POPx(a) POP1(b) if(b) JUMP(a) break; + case 0x0e: /* JSR */ POPx(a) FLIP PUSH2(pc) JUMP(a) break; + case 0x0f: /* STH */ POPx(a) FLIP PUSHx(a) break; + case 0x10: /* LDZ */ POP1(a) PEEK(b, a, t) PUSHx(b) break; + case 0x11: /* STZ */ POP1(a) POPx(b) POKE(a, b, t) break; + case 0x12: /* LDR */ POP1(a) PEEK(b, pc + (Sint8)a, tt) PUSHx(b) break; + case 0x13: /* STR */ POP1(a) POPx(b) POKE(pc + (Sint8)a, b, tt) break; + case 0x14: /* LDA */ POP2(a) PEEK(b, a, tt) PUSHx(b) break; + case 0x15: /* STA */ POP2(a) POPx(b) POKE(a, b, tt) break; + case 0x16: /* DEI */ POP1(a) DEVR(b, a) PUSHx(b) break; + case 0x17: /* DEO */ POP1(a) POPx(b) DEVW(a, b) break; + case 0x18: /* ADD */ POPx(a) POPx(b) PUSHx(b + a) break; + case 0x19: /* SUB */ POPx(a) POPx(b) PUSHx(b - a) break; + case 0x1a: /* MUL */ POPx(a) POPx(b) PUSHx((Uint32)b * a) break; + case 0x1b: /* DIV */ POPx(a) POPx(b) PUSHx(a ? b / a : 0) break; + case 0x1c: /* AND */ POPx(a) POPx(b) PUSHx(b & a) break; + case 0x1d: /* ORA */ POPx(a) POPx(b) PUSHx(b | a) break; + case 0x1e: /* EOR */ POPx(a) POPx(b) PUSHx(b ^ a) break; + case 0x1f: /* SFT */ POP1(a) POPx(b) PUSHx(b >> (a & 0xf) << (a >> 4)) break; + } + } +} + +int +main(int argc, char **argv) +{ + int i = 1; + Uxn u = {0}; + if(i == argc) + return system_error("usage:", "uxncli [-v] file.rom [args..]"); + if(argv[i][0] == '-' && argv[i][1] == 'v') + return system_error("Uxncli - Varvara Emulator(CLI)", "4 Jan 2024."); + if(!system_init(&u, (Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), argv[i++])) + return system_error("Init", "Failed to initialize uxn."); + /* eval */ + u.dev[0x17] = argc - i; + if(uxn_eval(&u, 0x0100)) { + console_listen(&u, i, argc, argv); + while(!u.dev[0x0f]) { + char c = fgetc(stdin); + if(c == EOF) { + console_input(&u, 0x00, CONSOLE_END); + break; + } + console_input(&u, (Uint8)c, CONSOLE_STD); + } + } + free(u.ram); + return u.dev[0x0f] & 0x7f; +}