From 6deb08adcde78d83684acf40e1295282c5fce668 Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Wed, 10 Jan 2024 12:33:12 -0800 Subject: [PATCH] (ref) Basic implementation --- .clang-format | 21 +++++ .gitignore | 3 +- ref/makefile | 16 ++++ ref/test.bin | Bin 0 -> 352 bytes ref/uxn | Bin 0 -> 28680 bytes ref/uxn.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 .clang-format create mode 100644 ref/makefile create mode 100644 ref/test.bin create mode 100755 ref/uxn create mode 100644 ref/uxn.c 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 0000000000000000000000000000000000000000..4f5f45f92014e9550e8450859efc163f302a218b GIT binary patch literal 352 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 +#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; +}