diff --git a/build.sh b/build.sh index 803f3bc..b23bf24 100755 --- a/build.sh +++ b/build.sh @@ -117,11 +117,9 @@ echo "Assembling(asma).." if [ $norun = 1 ]; then exit; fi echo "Assembling(piano).." -bin/uxncli bin/asma.rom projects/software/piano.tal bin/piano.rom 2> bin/piano.log +./bin/uxnasm projects/software/piano.tal bin/piano.rom 2> bin/piano.log echo "Running.." -cd bin -./uxnemu piano.rom +./bin/uxnemu bin/piano.rom echo "Done." -cd .. diff --git a/src/devices/datetime.c b/src/devices/datetime.c index b933377..e7a9f86 100644 --- a/src/devices/datetime.c +++ b/src/devices/datetime.c @@ -4,8 +4,7 @@ #include "datetime.h" /* -Copyright (c) 2021 Devine Lu Linvega -Copyright (c) 2021 Andrew Alderwick +Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -16,7 +15,7 @@ WITH REGARD TO THIS SOFTWARE. */ Uint8 -datetime_dei(Device *d, Uint8 port) +datetime_dei(Uint8 *d, Uint8 port) { time_t seconds = time(NULL); struct tm zt = {0}; @@ -35,6 +34,6 @@ datetime_dei(Device *d, Uint8 port) case 0x8: return t->tm_yday >> 8; case 0x9: return t->tm_yday; case 0xa: return t->tm_isdst; - default: return d->dat[port]; + default: return d[port]; } } diff --git a/src/devices/datetime.h b/src/devices/datetime.h index 95a3739..55ff56b 100644 --- a/src/devices/datetime.h +++ b/src/devices/datetime.h @@ -1,6 +1,5 @@ /* -Copyright (c) 2021 Devine Lu Linvega -Copyright (c) 2021 Andrew Alderwick +Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -10,4 +9,4 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ -Uint8 datetime_dei(Device *d, Uint8 port); +Uint8 datetime_dei(Uint8 *d, Uint8 port); diff --git a/src/devices/file.c b/src/devices/file.c index ecd1054..18a518a 100644 --- a/src/devices/file.c +++ b/src/devices/file.c @@ -1,15 +1,22 @@ +#define _XOPEN_SOURCE 500 #include #include +#include +#include #include +#include #include #include +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + #include "../uxn.h" #include "file.h" /* -Copyright (c) 2021 Devine Lu Linvega -Copyright (c) 2021 Andrew Alderwick +Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -28,6 +35,7 @@ typedef struct { FILE_READ, FILE_WRITE, DIR_READ } state; + int outside_sandbox; } UxnFile; static UxnFile uxn_file[POLYFILEY]; @@ -45,6 +53,7 @@ reset(UxnFile *c) } c->de = NULL; c->state = IDLE; + c->outside_sandbox = 0; } static Uint16 @@ -66,13 +75,24 @@ get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int f static Uint16 file_read_dir(UxnFile *c, char *dest, Uint16 len) { - static char pathname[4356]; + static char pathname[4352]; char *p = dest; if(c->de == NULL) c->de = readdir(c->dir); for(; c->de != NULL; c->de = readdir(c->dir)) { Uint16 n; if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0') continue; + if(strcmp(c->de->d_name, "..") == 0) { + /* hide "sandbox/.." */ + char cwd[PATH_MAX] = {'\0'}, t[PATH_MAX] = {'\0'}; + /* Note there's [currently] no way of chdir()ing from uxn, so $PWD + * is always the sandbox top level. */ + getcwd(cwd, sizeof(cwd)); + /* We already checked that c->current_filename exists so don't need a wrapper. */ + realpath(c->current_filename, t); + if(strcmp(cwd, t) == 0) + continue; + } if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname)) sprintf(pathname, "%s/%s", c->current_filename, c->de->d_name); else @@ -85,16 +105,62 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len) return p - dest; } +static char * +retry_realpath(const char *file_name) +{ + char r[PATH_MAX] = {'\0'}, p[PATH_MAX] = {'\0'}, *x; + if(file_name == NULL) { + errno = EINVAL; + return NULL; + } else if(strlen(file_name) >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + if(file_name[0] != '/') { + /* TODO: use a macro instead of '/' for absolute path first character so that other systems can work */ + /* if a relative path, prepend cwd */ + getcwd(p, sizeof(p)); + strcat(p, "/"); /* TODO: use a macro instead of '/' for the path delimiter */ + } + strcat(p, file_name); + while(realpath(p, r) == NULL) { + if(errno != ENOENT) + return NULL; + x = strrchr(p, '/'); /* TODO: path delimiter macro */ + if(x) + *x = '\0'; + else + return NULL; + } + return strdup(r); +} + +static void +file_check_sandbox(UxnFile *c) +{ + char *x, *rp, cwd[PATH_MAX] = {'\0'}; + x = getcwd(cwd, sizeof(cwd)); + rp = retry_realpath(c->current_filename); + if(rp == NULL || (x && strncmp(cwd, rp, strlen(cwd)) != 0)) { + c->outside_sandbox = 1; + fprintf(stderr, "file warning: blocked attempt to access %s outside of sandbox\n", c->current_filename); + } + free(rp); +} + static Uint16 -file_init(UxnFile *c, char *filename, size_t max_len) +file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox) { char *p = c->current_filename; size_t len = sizeof(c->current_filename); reset(c); if(len > max_len) len = max_len; while(len) { - if((*p++ = *filename++) == '\0') + if((*p++ = *filename++) == '\0') { + if(!override_sandbox) /* override sandbox for loading roms */ + file_check_sandbox(c); return 0; + } len--; } c->current_filename[0] = '\0'; @@ -104,6 +170,7 @@ file_init(UxnFile *c, char *filename, size_t max_len) static Uint16 file_read(UxnFile *c, void *dest, Uint16 len) { + if(c->outside_sandbox) return 0; if(c->state != FILE_READ && c->state != DIR_READ) { reset(c); if((c->dir = opendir(c->current_filename)) != NULL) @@ -122,6 +189,7 @@ static Uint16 file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags) { Uint16 ret = 0; + if(c->outside_sandbox) return 0; if(c->state != FILE_WRITE) { reset(c); if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL) @@ -138,6 +206,7 @@ static Uint16 file_stat(UxnFile *c, void *dest, Uint16 len) { char *basename = strrchr(c->current_filename, '/'); + if(c->outside_sandbox) return 0; if(basename != NULL) basename++; else @@ -148,72 +217,66 @@ file_stat(UxnFile *c, void *dest, Uint16 len) static Uint16 file_delete(UxnFile *c) { - return unlink(c->current_filename); -} - -static UxnFile * -file_instance(Device *d) -{ - return &uxn_file[d - &d->u->devold[DEV_FILE0]]; + return c->outside_sandbox ? 0 : unlink(c->current_filename); } /* IO */ void -file_deo(Device *d, Uint8 port) +file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port) { - UxnFile *c = file_instance(d); + UxnFile *c = &uxn_file[id]; Uint16 addr, len, res; switch(port) { case 0x5: - DEVPEEK16(addr, 0x4); - DEVPEEK16(len, 0xa); + PEKDEV(addr, 0x4); + PEKDEV(len, 0xa); if(len > 0x10000 - addr) len = 0x10000 - addr; - res = file_stat(c, &d->u->ram[addr], len); - DEVPOKE16(0x2, res); + res = file_stat(c, &ram[addr], len); + POKDEV(0x2, res); break; case 0x6: res = file_delete(c); - DEVPOKE16(0x2, res); + POKDEV(0x2, res); break; case 0x9: - DEVPEEK16(addr, 0x8); - res = file_init(c, (char *)&d->u->ram[addr], 0x10000 - addr); - DEVPOKE16(0x2, res); + PEKDEV(addr, 0x8); + res = file_init(c, (char *)&ram[addr], 0x10000 - addr, 0); + POKDEV(0x2, res); break; case 0xd: - DEVPEEK16(addr, 0xc); - DEVPEEK16(len, 0xa); + PEKDEV(addr, 0xc); + PEKDEV(len, 0xa); if(len > 0x10000 - addr) len = 0x10000 - addr; - res = file_read(c, &d->u->ram[addr], len); - DEVPOKE16(0x2, res); + res = file_read(c, &ram[addr], len); + POKDEV(0x2, res); break; case 0xf: - DEVPEEK16(addr, 0xe); - DEVPEEK16(len, 0xa); + PEKDEV(addr, 0xe); + PEKDEV(len, 0xa); if(len > 0x10000 - addr) len = 0x10000 - addr; - res = file_write(c, &d->u->ram[addr], len, d->dat[0x7]); - DEVPOKE16(0x2, res); + res = file_write(c, &ram[addr], len, d[0x7]); + POKDEV(0x2, res); break; } } Uint8 -file_dei(Device *d, Uint8 port) +file_dei(Uint8 id, Uint8 *d, Uint8 port) { - UxnFile *c = file_instance(d); + UxnFile *c = &uxn_file[id]; Uint16 res; switch(port) { case 0xc: case 0xd: - res = file_read(c, &d->dat[port], 1); - DEVPOKE16(0x2, res); + res = file_read(c, &d[port], 1); + POKDEV(0x2, res); break; } - return d->dat[port]; + return d[port]; } /* Boot */ @@ -222,7 +285,7 @@ int load_rom(Uxn *u, char *filename) { int ret; - file_init(uxn_file, filename, strlen(filename) + 1); + file_init(uxn_file, filename, strlen(filename) + 1, 1); ret = file_read(uxn_file, &u->ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM); reset(uxn_file); return ret; diff --git a/src/devices/file.h b/src/devices/file.h index f6db406..5edb9f5 100644 --- a/src/devices/file.h +++ b/src/devices/file.h @@ -1,6 +1,5 @@ /* -Copyright (c) 2021 Devine Lu Linvega -Copyright (c) 2021 Andrew Alderwick +Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -13,6 +12,6 @@ WITH REGARD TO THIS SOFTWARE. #define POLYFILEY 2 #define DEV_FILE0 0xa -void file_deo(Device *d, Uint8 port); -Uint8 file_dei(Device *d, Uint8 port); +void file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port); +Uint8 file_dei(Uint8 id, Uint8 *d, Uint8 port); int load_rom(Uxn *u, char *filename); diff --git a/src/devices/screen.c b/src/devices/screen.c index ddb5faf..4ea6b68 100644 --- a/src/devices/screen.c +++ b/src/devices/screen.c @@ -132,14 +132,14 @@ screen_mono(UxnScreen *p, Uint32 *pixels) /* IO */ Uint8 -screen_dei(Device *d, Uint8 port) +screen_dei(Uint8 *d, Uint8 port) { switch(port) { case 0x2: return uxn_screen.width >> 8; case 0x3: return uxn_screen.width; case 0x4: return uxn_screen.height >> 8; case 0x5: return uxn_screen.height; - default: return d->dat[port]; + default: return d[port]; } } diff --git a/src/devices/screen.h b/src/devices/screen.h index cd211be..9a6ecab 100644 --- a/src/devices/screen.h +++ b/src/devices/screen.h @@ -31,6 +31,6 @@ void screen_clear(UxnScreen *p, Layer *layer); void screen_redraw(UxnScreen *p, Uint32 *pixels); void screen_mono(UxnScreen *p, Uint32 *pixels); -Uint8 screen_dei(Device *d, Uint8 port); +Uint8 screen_dei(Uint8 *d, Uint8 port); void screen_deo(Device *d, Uint8 port); int clamp(int val, int min, int max); diff --git a/src/devices/system.c b/src/devices/system.c index a70fdd7..f1607cd 100644 --- a/src/devices/system.c +++ b/src/devices/system.c @@ -15,13 +15,9 @@ WITH REGARD TO THIS SOFTWARE. */ static const char *errors[] = { - "Working-stack underflow", - "Return-stack underflow", - "Working-stack overflow", - "Return-stack overflow", - "Working-stack division by zero", - "Return-stack division by zero", - "Execution timeout"}; + "underflow", + "overflow", + "division by zero"}; static void system_print(Stack *s, char *name) @@ -38,37 +34,37 @@ system_print(Stack *s, char *name) void system_inspect(Uxn *u) { - system_print(&u->wst, "wst"); - system_print(&u->rst, "rst"); + system_print(u->wst, "wst"); + system_print(u->rst, "rst"); } int -uxn_halt(Uxn *u, Uint8 error, Uint16 addr) +uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr) { - system_inspect(u); - fprintf(stderr, "Halted: %s#%04x, at 0x%04x\n", errors[error], u->ram[addr], addr); + Uint8 *d = &u->dev[0x00]; + if(instr & 0x40) + u->rst->err = err; + else + u->wst->err = err; + if(GETVEC(d)) + uxn_eval(u, GETVEC(d)); + else { + system_inspect(u); + fprintf(stderr, "%s %s, by %02x at 0x%04x.\n", (instr & 0x40) ? "Return-stack" : "Working-stack", errors[err - 1], instr, addr); + } return 0; } /* IO */ -Uint8 -system_dei(Device *d, Uint8 port) -{ - switch(port) { - case 0x2: return d->u->wst.ptr; - case 0x3: return d->u->rst.ptr; - default: return d->dat[port]; - } -} - void -system_deo(Device *d, Uint8 port) +system_deo(Uxn *u, Uint8 *d, Uint8 port) { switch(port) { - case 0x2: d->u->wst.ptr = d->dat[port]; break; - case 0x3: d->u->rst.ptr = d->dat[port]; break; - case 0xe: system_inspect(d->u); break; - default: system_deo_special(d, port); + case 0x2: u->wst = (Stack *)(u->ram + (d[port] ? (d[port] * 0x100) : 0x10000)); break; + case 0x3: u->rst = (Stack *)(u->ram + (d[port] ? (d[port] * 0x100) : 0x10100)); break; + case 0xe: + if(u->wst->ptr || u->rst->ptr) system_inspect(u); + break; } } diff --git a/src/devices/system.h b/src/devices/system.h index 6bdc847..9d8117d 100644 --- a/src/devices/system.h +++ b/src/devices/system.h @@ -9,13 +9,5 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ -typedef struct SystemDevice { - Device device; - struct UxnScreen *screen; -} SystemDevice; - void system_inspect(Uxn *u); - -Uint8 system_dei(Device *d, Uint8 port); -void system_deo(Device *d, Uint8 port); -void system_deo_special(Device *d, Uint8 port); +void system_deo(Uxn *u, Uint8 *d, Uint8 port); diff --git a/src/uxn.c b/src/uxn.c index 78d0fbf..7920226 100644 --- a/src/uxn.c +++ b/src/uxn.c @@ -31,6 +31,9 @@ WITH REGARD TO THIS SOFTWARE. #define DEVW8OLD(x, y) { dev->dat[(x) & 0xf] = y; dev->deo(dev, (x) & 0x0f); } #define DEVWOLD(d, x, y) { dev = (d); if(bs) { DEVW8OLD((x), (y) >> 8); DEVW8OLD((x) + 1, (y)); } else { DEVW8OLD((x), (y)) } } +#define DEVR(o, x) { o = u->dei(u, x); if (bs) o = (o << 8) + u->dei(u, ((x) + 1) & 0xFF); } +#define DEVW(x, y) { if (bs) { u->deo(u, (x), (y) >> 8); u->deo(u, ((x) + 1) & 0xFF, (y)); } else { u->deo(u, x, (y)); } } + #define WARP(x) { if(bs) pc = (x); else pc += (Sint8)(x); } #define LIMIT 0x40000 /* around 3 ms */ @@ -41,21 +44,16 @@ uxn_eval(Uxn *u, Uint16 pc) unsigned int limit = LIMIT; Uint8 kptr, *sp; Stack *src, *dst; - Device *dev; if(!pc || u->devold[0].dat[0xf]) return 0; while((instr = u->ram[pc++])) { if(!limit--) { - if(!uxn_interrupt()) { - errcode = 6; - goto timeout; - } limit = LIMIT; } /* Return Mode */ if(instr & 0x40) { - src = &u->rst; dst = &u->wst; + src = u->rst; dst = u->wst; } else { - src = &u->wst; dst = &u->rst; + src = u->wst; dst = u->rst; } /* Keep Mode */ if(instr & 0x80) { @@ -92,8 +90,8 @@ uxn_eval(Uxn *u, Uint16 pc) case 0x13: /* STR */ POP8(a) POP(b) c = pc + (Sint8)a; POKE(c, b) break; case 0x14: /* LDA */ POP16(a) PEEK(b, a) PUSH(src, b) break; case 0x15: /* STA */ POP16(a) POP(b) POKE(a, b) break; - case 0x16: /* DEI */ POP8(a) DEVROLD(b, &u->devold[a >> 4], a) PUSH(src, b) break; - case 0x17: /* DEO */ POP8(a) POP(b) DEVWOLD(&u->devold[a >> 4], a, b) break; + case 0x16: /* DEI */ POP8(a) DEVR(b, a) PUSH(src, b) break; + case 0x17: /* DEO */ POP8(a) POP(b) DEVW(a, b) break; /* Arithmetic */ case 0x18: /* ADD */ POP(a) POP(b) PUSH(src, b + a) break; case 0x19: /* SUB */ POP(a) POP(b) PUSH(src, b - a) break; @@ -106,13 +104,8 @@ uxn_eval(Uxn *u, Uint16 pc) } } return 1; - err: - /* set 1 in errcode if it involved the return stack instead of the working stack */ - /* (stack overflow & ( opcode was STH / JSR )) ^ Return Mode */ - errcode |= ((errcode >> 1 & ((instr & 0x1e) == 0x0e)) ^ instr >> 6) & 1; -timeout: - return uxn_halt(u, errcode, pc - 1); + return uxn_halt(u, instr, errcode, pc - 1); } /* clang-format on */ @@ -125,6 +118,8 @@ uxn_boot(Uxn *u, Uint8 *ram, Dei *dei, Deo *deo) for(i = 0; i < sizeof(*u); i++) cptr[i] = 0x00; u->ram = ram; + u->wst = (Stack *)(ram + 0x10000); + u->rst = (Stack *)(ram + 0x10100); u->dev = (Uint8 *)(ram + 0x10200); u->dei = dei; u->deo = deo; diff --git a/src/uxn.h b/src/uxn.h index d12ca54..f83de47 100644 --- a/src/uxn.h +++ b/src/uxn.h @@ -20,24 +20,17 @@ typedef unsigned int Uint32; #define PAGE_PROGRAM 0x0100 -/* clang-format off */ - -#define DEVPEEK16(o, x) { (o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; } -#define DEVPOKE16(x, y) { d->dat[(x)] = (y) >> 8; d->dat[(x) + 1] = (y); } +#define DEVPEEK16(o, x) \ + { \ + (o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; \ + } +#define DEVPOKE16(x, y) \ + { \ + d->dat[(x)] = (y) >> 8; \ + d->dat[(x) + 1] = (y); \ + } #define GETVECTOR(d) ((d)->dat[0] << 8 | (d)->dat[1]) -/* new macros */ - -#define GETVEC(d) ((d)[0] << 8 | (d)[1]) -#define POKDEV(x, y) { d[(x)] = (y) >> 8; d[(x) + 1] = (y); } -#define PEKDEV(o, x) { (o) = (d[(x)] << 8) + d[(x) + 1]; } - -/* clang-format on */ - -typedef struct { - Uint8 ptr, dat[255]; -} Stack; - typedef struct Device { struct Uxn *u; Uint8 dat[16]; @@ -45,9 +38,21 @@ typedef struct Device { void (*deo)(struct Device *d, Uint8); } Device; +/* clang-format off */ + +#define GETVEC(d) ((d)[0] << 8 | (d)[1]) +#define POKDEV(x, y) { d[(x)] = (y) >> 8; d[(x) + 1] = (y); } +#define PEKDEV(o, x) { (o) = (d[(x)] << 8) + d[(x) + 1]; } + +/* clang-format on */ + +typedef struct { + Uint8 dat[254], err, ptr; +} Stack; + typedef struct Uxn { Uint8 *ram, *dev; - Stack wst, rst; + Stack *wst, *rst; Device devold[16]; Uint8 (*dei)(struct Uxn *u, Uint8 addr); void (*deo)(struct Uxn *u, Uint8 addr, Uint8 value); @@ -58,7 +63,9 @@ typedef void Deo(Uxn *u, Uint8 addr, Uint8 value); int uxn_boot(Uxn *u, Uint8 *ram, Dei *dei, Deo *deo); int uxn_eval(Uxn *u, Uint16 pc); -int uxn_interrupt(void); -int uxn_halt(Uxn *u, Uint8 error, Uint16 addr); +int uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr); + +/* TODO: remove */ + Device *uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *, Uint8), void (*deofn)(Device *, Uint8)); #endif /* UXN_UXN_H */ diff --git a/src/uxncli.c b/src/uxncli.c index ab65530..07274f0 100644 --- a/src/uxncli.c +++ b/src/uxncli.c @@ -17,102 +17,60 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ +#define SUPPORT 0x1c03 /* devices mask */ + static int -error(char *msg, const char *err) +emu_error(char *msg, const char *err) { fprintf(stderr, "Error %s: %s\n", msg, err); return 0; } -static Uint8 -emu_dei(Uxn *u, Uint8 addr) +static int +console_input(Uxn *u, char c) { - return 0; + Uint8 *d = &u->dev[0x10]; + d[0x02] = c; + return uxn_eval(u, GETVEC(d)); } static void -emu_deo(Uxn *u, Uint8 addr, Uint8 v) +console_deo(Uint8 *d, Uint8 port) { -} - -void -system_deo_special(Device *d, Uint8 port) -{ - (void)d; - (void)port; -} - -static void -console_deo(Device *d, Uint8 port) -{ - FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr - : 0; + FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr : + 0; if(fd) { - fputc(d->dat[port], fd); + fputc(d[port], fd); fflush(fd); } } static Uint8 -nil_dei(Device *d, Uint8 port) +emu_dei(Uxn *u, Uint8 addr) { - return d->dat[port]; -} - -static void -nil_deo(Device *d, Uint8 port) -{ - (void)d; - (void)port; -} - -static int -console_input(Uxn *u, char c) -{ - Device *d = &u->devold[1]; - d->dat[0x2] = c; - return uxn_eval(u, GETVECTOR(d)); -} - -static void -run(Uxn *u) -{ - Device *d = &u->devold[0]; - while(!d->dat[0xf]) { - int c = fgetc(stdin); - if(c != EOF) - console_input(u, (Uint8)c); + Uint8 p = addr & 0x0f, d = addr & 0xf0; + switch(d) { + case 0xa0: return file_dei(0, &u->dev[d], p); + case 0xb0: return file_dei(1, &u->dev[d], p); + case 0xc0: return datetime_dei(&u->dev[d], p); } + return u->dev[addr]; } -int -uxn_interrupt(void) +static void +emu_deo(Uxn *u, Uint8 addr, Uint8 v) { - return 1; -} - -static int -start(Uxn *u) -{ - if(!uxn_boot(u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo)) - return error("Boot", "Failed"); - /* system */ uxn_port(u, 0x0, system_dei, system_deo); - /* console */ uxn_port(u, 0x1, nil_dei, console_deo); - /* empty */ uxn_port(u, 0x2, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x3, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x4, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x5, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x6, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x7, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x8, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x9, nil_dei, nil_deo); - /* file0 */ uxn_port(u, 0xa, file_dei, file_deo); - /* file1 */ uxn_port(u, 0xb, file_dei, file_deo); - /* datetime */ uxn_port(u, 0xc, datetime_dei, nil_deo); - /* empty */ uxn_port(u, 0xd, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0xe, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0xf, nil_dei, nil_deo); - return 1; + Uint8 p = addr & 0x0f, d = addr & 0xf0; + Uint16 mask = 0x1 << (d >> 4); + u->dev[addr] = v; + switch(d) { + case 0x00: system_deo(u, &u->dev[d], p); break; + case 0x10: console_deo(&u->dev[d], p); break; + case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break; + case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break; + } + if(p == 0x01 && !(SUPPORT & mask)) + fprintf(stderr, "Warning: Incompatible emulation, device: %02x.\n", d); } int @@ -121,19 +79,22 @@ main(int argc, char **argv) Uxn u; int i; if(argc < 2) - return error("Usage", "uxncli game.rom args"); - if(!start(&u)) - return error("Start", "Failed"); + return emu_error("Usage", "uxncli game.rom args"); + if(!uxn_boot(&u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo)) + return emu_error("Boot", "Failed"); if(!load_rom(&u, argv[1])) - return error("Load", "Failed"); - fprintf(stderr, "Loaded %s\n", argv[1]); + return emu_error("Load", "Failed"); if(!uxn_eval(&u, PAGE_PROGRAM)) - return error("Init", "Failed"); + return emu_error("Init", "Failed"); for(i = 2; i < argc; i++) { char *p = argv[i]; while(*p) console_input(&u, *p++); console_input(&u, '\n'); } - run(&u); + while(!u.dev[0x0f]) { + int c = fgetc(stdin); + if(c != EOF) + console_input(&u, (Uint8)c); + } return 0; } diff --git a/src/uxnemu.c b/src/uxnemu.c index a94dd64..11525a8 100644 --- a/src/uxnemu.c +++ b/src/uxnemu.c @@ -63,6 +63,25 @@ error(char *msg, const char *err) return 0; } +static int +console_input(Uxn *u, char c) +{ + Uint8 *d = &u->dev[0x10]; + d[0x02] = c; + return uxn_eval(u, GETVEC(d)); +} + +static void +console_deo(Uint8 *d, Uint8 port) +{ + FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr + : 0; + if(fd) { + fputc(d[port], fd); + fflush(fd); + } +} + #pragma mark - Generics static void @@ -179,12 +198,34 @@ init(void) static Uint8 emu_dei(Uxn *u, Uint8 addr) { + Uint8 p = addr & 0x0f, d = addr & 0xf0; + switch(d) { + case 0x20: return screen_dei(&u->dev[d], p); + case 0xa0: return file_dei(0, &u->dev[d], p); + case 0xb0: return file_dei(1, &u->dev[d], p); + case 0xc0: return datetime_dei(&u->dev[d], p); + } + return u->dev[addr]; return 0; } static void emu_deo(Uxn *u, Uint8 addr, Uint8 v) { +Uint8 p = addr & 0x0f, d = addr & 0xf0; + Uint16 mask = 0x1 << (d >> 4); + u->dev[addr] = v; + switch(d) { + case 0x00: + system_deo(u, &u->dev[d], p); + if(p > 0x7 && p < 0xe) + screen_palette(&uxn_screen, &u->dev[0x8]); + break; + case 0x10: console_deo(&u->dev[d], p); break; + /* case 0x20: screen_deo(u->ram, &u->dev[d], p); break; */ + case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break; + case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break; + } } void @@ -194,17 +235,6 @@ system_deo_special(Device *d, Uint8 port) screen_palette(&uxn_screen, &d->dat[0x8]); } -static void -console_deo(Device *d, Uint8 port) -{ - FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr - : 0; - if(fd) { - fputc(d->dat[port], fd); - fflush(fd); - } -} - static Uint8 audio_dei(Device *d, Uint8 port) { @@ -230,19 +260,6 @@ audio_deo(Device *d, Uint8 port) } } -static Uint8 -nil_dei(Device *d, Uint8 port) -{ - return d->dat[port]; -} - -static void -nil_deo(Device *d, Uint8 port) -{ - (void)d; - (void)port; -} - /* Boot */ static int @@ -264,26 +281,10 @@ static int start(Uxn *u, char *rom) { free(u->ram); - if(!uxn_boot(u, calloc(0x10300, 1), emu_dei, emu_deo)) + if(!uxn_boot(u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo)) return error("Boot", "Failed to start uxn."); if(!load(u, rom)) return error("Boot", "Failed to load rom."); - /* system */ uxn_port(u, 0x0, system_dei, system_deo); - /* console */ uxn_port(u, 0x1, nil_dei, console_deo); - /* screen */ devscreen = uxn_port(u, 0x2, screen_dei, screen_deo); - /* audio0 */ devaudio0 = uxn_port(u, 0x3, audio_dei, audio_deo); - /* audio1 */ uxn_port(u, 0x4, audio_dei, audio_deo); - /* audio2 */ uxn_port(u, 0x5, audio_dei, audio_deo); - /* audio3 */ uxn_port(u, 0x6, audio_dei, audio_deo); - /* unused */ uxn_port(u, 0x7, nil_dei, nil_deo); - /* control */ uxn_port(u, 0x8, nil_dei, nil_deo); - /* mouse */ uxn_port(u, 0x9, nil_dei, nil_deo); - /* file0 */ uxn_port(u, 0xa, file_dei, file_deo); - /* file1 */ uxn_port(u, 0xb, file_dei, file_deo); - /* datetime */ uxn_port(u, 0xc, datetime_dei, nil_deo); - /* unused */ uxn_port(u, 0xd, nil_dei, nil_deo); - /* unused */ uxn_port(u, 0xe, nil_dei, nil_deo); - /* unused */ uxn_port(u, 0xf, nil_dei, nil_deo); exec_deadline = SDL_GetPerformanceCounter() + deadline_interval; if(!uxn_eval(u, PAGE_PROGRAM)) return error("Boot", "Failed to start rom."); @@ -383,14 +384,6 @@ do_shortcut(Uxn *u, SDL_Event *event) } } -static int -console_input(Uxn *u, char c) -{ - Device *d = &u->devold[1]; - d->dat[0x2] = c; - return uxn_eval(u, GETVECTOR(d)); -} - static int handle_events(Uxn *u) { @@ -408,8 +401,8 @@ handle_events(Uxn *u) } /* Audio */ else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY) { - Device *d = devaudio0 + (event.type - audio0_event); - uxn_eval(u, GETVECTOR(d)); + /* Device *d = devaudio0 + (event.type - audio0_event); + uxn_eval(u, GETVECTOR(d)); */ } /* Mouse */ else if(event.type == SDL_MOUSEMOTION) @@ -467,7 +460,7 @@ run(Uxn *u) exec_deadline = now + deadline_interval; if(!handle_events(u)) return 0; - uxn_eval(u, GETVECTOR(devscreen)); + uxn_eval(u, GETVEC(&u->dev[0x20])); if(uxn_screen.fg.changed || uxn_screen.bg.changed) redraw(); now = SDL_GetPerformanceCounter(); @@ -479,12 +472,6 @@ run(Uxn *u) return error("SDL_WaitEvent", SDL_GetError()); } -int -uxn_interrupt(void) -{ - return !SDL_QuitRequested(); -} - int main(int argc, char **argv) {