Screen device is half ported to new device handlers

This commit is contained in:
Devine Lu Linvega 2023-01-01 13:19:40 -08:00
parent 679aec047a
commit 7afe1f39c7
13 changed files with 259 additions and 263 deletions

View File

@ -117,11 +117,9 @@ echo "Assembling(asma).."
if [ $norun = 1 ]; then exit; fi if [ $norun = 1 ]; then exit; fi
echo "Assembling(piano).." 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.." echo "Running.."
cd bin ./bin/uxnemu bin/piano.rom
./uxnemu piano.rom
echo "Done." echo "Done."
cd ..

View File

@ -4,8 +4,7 @@
#include "datetime.h" #include "datetime.h"
/* /*
Copyright (c) 2021 Devine Lu Linvega Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
Copyright (c) 2021 Andrew Alderwick
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -16,7 +15,7 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
Uint8 Uint8
datetime_dei(Device *d, Uint8 port) datetime_dei(Uint8 *d, Uint8 port)
{ {
time_t seconds = time(NULL); time_t seconds = time(NULL);
struct tm zt = {0}; struct tm zt = {0};
@ -35,6 +34,6 @@ datetime_dei(Device *d, Uint8 port)
case 0x8: return t->tm_yday >> 8; case 0x8: return t->tm_yday >> 8;
case 0x9: return t->tm_yday; case 0x9: return t->tm_yday;
case 0xa: return t->tm_isdst; case 0xa: return t->tm_isdst;
default: return d->dat[port]; default: return d[port];
} }
} }

View File

@ -1,6 +1,5 @@
/* /*
Copyright (c) 2021 Devine Lu Linvega Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
Copyright (c) 2021 Andrew Alderwick
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above 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. WITH REGARD TO THIS SOFTWARE.
*/ */
Uint8 datetime_dei(Device *d, Uint8 port); Uint8 datetime_dei(Uint8 *d, Uint8 port);

View File

@ -1,15 +1,22 @@
#define _XOPEN_SOURCE 500
#include <stdio.h> #include <stdio.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#include "../uxn.h" #include "../uxn.h"
#include "file.h" #include "file.h"
/* /*
Copyright (c) 2021 Devine Lu Linvega Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
Copyright (c) 2021 Andrew Alderwick
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -28,6 +35,7 @@ typedef struct {
FILE_READ, FILE_READ,
FILE_WRITE, FILE_WRITE,
DIR_READ } state; DIR_READ } state;
int outside_sandbox;
} UxnFile; } UxnFile;
static UxnFile uxn_file[POLYFILEY]; static UxnFile uxn_file[POLYFILEY];
@ -45,6 +53,7 @@ reset(UxnFile *c)
} }
c->de = NULL; c->de = NULL;
c->state = IDLE; c->state = IDLE;
c->outside_sandbox = 0;
} }
static Uint16 static Uint16
@ -66,13 +75,24 @@ get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int f
static Uint16 static Uint16
file_read_dir(UxnFile *c, char *dest, Uint16 len) file_read_dir(UxnFile *c, char *dest, Uint16 len)
{ {
static char pathname[4356]; static char pathname[4352];
char *p = dest; char *p = dest;
if(c->de == NULL) c->de = readdir(c->dir); if(c->de == NULL) c->de = readdir(c->dir);
for(; c->de != NULL; c->de = readdir(c->dir)) { for(; c->de != NULL; c->de = readdir(c->dir)) {
Uint16 n; Uint16 n;
if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0') if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0')
continue; 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)) if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname))
sprintf(pathname, "%s/%s", c->current_filename, c->de->d_name); sprintf(pathname, "%s/%s", c->current_filename, c->de->d_name);
else else
@ -85,16 +105,62 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
return p - dest; 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 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; char *p = c->current_filename;
size_t len = sizeof(c->current_filename); size_t len = sizeof(c->current_filename);
reset(c); reset(c);
if(len > max_len) len = max_len; if(len > max_len) len = max_len;
while(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; return 0;
}
len--; len--;
} }
c->current_filename[0] = '\0'; c->current_filename[0] = '\0';
@ -104,6 +170,7 @@ file_init(UxnFile *c, char *filename, size_t max_len)
static Uint16 static Uint16
file_read(UxnFile *c, void *dest, Uint16 len) file_read(UxnFile *c, void *dest, Uint16 len)
{ {
if(c->outside_sandbox) return 0;
if(c->state != FILE_READ && c->state != DIR_READ) { if(c->state != FILE_READ && c->state != DIR_READ) {
reset(c); reset(c);
if((c->dir = opendir(c->current_filename)) != NULL) if((c->dir = opendir(c->current_filename)) != NULL)
@ -122,6 +189,7 @@ static Uint16
file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags) file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
{ {
Uint16 ret = 0; Uint16 ret = 0;
if(c->outside_sandbox) return 0;
if(c->state != FILE_WRITE) { if(c->state != FILE_WRITE) {
reset(c); reset(c);
if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL) 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) file_stat(UxnFile *c, void *dest, Uint16 len)
{ {
char *basename = strrchr(c->current_filename, '/'); char *basename = strrchr(c->current_filename, '/');
if(c->outside_sandbox) return 0;
if(basename != NULL) if(basename != NULL)
basename++; basename++;
else else
@ -148,72 +217,66 @@ file_stat(UxnFile *c, void *dest, Uint16 len)
static Uint16 static Uint16
file_delete(UxnFile *c) file_delete(UxnFile *c)
{ {
return unlink(c->current_filename); return c->outside_sandbox ? 0 : unlink(c->current_filename);
}
static UxnFile *
file_instance(Device *d)
{
return &uxn_file[d - &d->u->devold[DEV_FILE0]];
} }
/* IO */ /* IO */
void 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; Uint16 addr, len, res;
switch(port) { switch(port) {
case 0x5: case 0x5:
DEVPEEK16(addr, 0x4); PEKDEV(addr, 0x4);
DEVPEEK16(len, 0xa); PEKDEV(len, 0xa);
if(len > 0x10000 - addr) if(len > 0x10000 - addr)
len = 0x10000 - addr; len = 0x10000 - addr;
res = file_stat(c, &d->u->ram[addr], len); res = file_stat(c, &ram[addr], len);
DEVPOKE16(0x2, res); POKDEV(0x2, res);
break; break;
case 0x6: case 0x6:
res = file_delete(c); res = file_delete(c);
DEVPOKE16(0x2, res); POKDEV(0x2, res);
break; break;
case 0x9: case 0x9:
DEVPEEK16(addr, 0x8); PEKDEV(addr, 0x8);
res = file_init(c, (char *)&d->u->ram[addr], 0x10000 - addr); res = file_init(c, (char *)&ram[addr], 0x10000 - addr, 0);
DEVPOKE16(0x2, res); POKDEV(0x2, res);
break; break;
case 0xd: case 0xd:
DEVPEEK16(addr, 0xc); PEKDEV(addr, 0xc);
DEVPEEK16(len, 0xa); PEKDEV(len, 0xa);
if(len > 0x10000 - addr) if(len > 0x10000 - addr)
len = 0x10000 - addr; len = 0x10000 - addr;
res = file_read(c, &d->u->ram[addr], len); res = file_read(c, &ram[addr], len);
DEVPOKE16(0x2, res); POKDEV(0x2, res);
break; break;
case 0xf: case 0xf:
DEVPEEK16(addr, 0xe); PEKDEV(addr, 0xe);
DEVPEEK16(len, 0xa); PEKDEV(len, 0xa);
if(len > 0x10000 - addr) if(len > 0x10000 - addr)
len = 0x10000 - addr; len = 0x10000 - addr;
res = file_write(c, &d->u->ram[addr], len, d->dat[0x7]); res = file_write(c, &ram[addr], len, d[0x7]);
DEVPOKE16(0x2, res); POKDEV(0x2, res);
break; break;
} }
} }
Uint8 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; Uint16 res;
switch(port) { switch(port) {
case 0xc: case 0xc:
case 0xd: case 0xd:
res = file_read(c, &d->dat[port], 1); res = file_read(c, &d[port], 1);
DEVPOKE16(0x2, res); POKDEV(0x2, res);
break; break;
} }
return d->dat[port]; return d[port];
} }
/* Boot */ /* Boot */
@ -222,7 +285,7 @@ int
load_rom(Uxn *u, char *filename) load_rom(Uxn *u, char *filename)
{ {
int ret; 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); ret = file_read(uxn_file, &u->ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM);
reset(uxn_file); reset(uxn_file);
return ret; return ret;

View File

@ -1,6 +1,5 @@
/* /*
Copyright (c) 2021 Devine Lu Linvega Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
Copyright (c) 2021 Andrew Alderwick
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above 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 POLYFILEY 2
#define DEV_FILE0 0xa #define DEV_FILE0 0xa
void file_deo(Device *d, Uint8 port); void file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port);
Uint8 file_dei(Device *d, Uint8 port); Uint8 file_dei(Uint8 id, Uint8 *d, Uint8 port);
int load_rom(Uxn *u, char *filename); int load_rom(Uxn *u, char *filename);

View File

@ -132,14 +132,14 @@ screen_mono(UxnScreen *p, Uint32 *pixels)
/* IO */ /* IO */
Uint8 Uint8
screen_dei(Device *d, Uint8 port) screen_dei(Uint8 *d, Uint8 port)
{ {
switch(port) { switch(port) {
case 0x2: return uxn_screen.width >> 8; case 0x2: return uxn_screen.width >> 8;
case 0x3: return uxn_screen.width; case 0x3: return uxn_screen.width;
case 0x4: return uxn_screen.height >> 8; case 0x4: return uxn_screen.height >> 8;
case 0x5: return uxn_screen.height; case 0x5: return uxn_screen.height;
default: return d->dat[port]; default: return d[port];
} }
} }

View File

@ -31,6 +31,6 @@ void screen_clear(UxnScreen *p, Layer *layer);
void screen_redraw(UxnScreen *p, Uint32 *pixels); void screen_redraw(UxnScreen *p, Uint32 *pixels);
void screen_mono(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); void screen_deo(Device *d, Uint8 port);
int clamp(int val, int min, int max); int clamp(int val, int min, int max);

View File

@ -15,13 +15,9 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
static const char *errors[] = { static const char *errors[] = {
"Working-stack underflow", "underflow",
"Return-stack underflow", "overflow",
"Working-stack overflow", "division by zero"};
"Return-stack overflow",
"Working-stack division by zero",
"Return-stack division by zero",
"Execution timeout"};
static void static void
system_print(Stack *s, char *name) system_print(Stack *s, char *name)
@ -38,37 +34,37 @@ system_print(Stack *s, char *name)
void void
system_inspect(Uxn *u) system_inspect(Uxn *u)
{ {
system_print(&u->wst, "wst"); system_print(u->wst, "wst");
system_print(&u->rst, "rst"); system_print(u->rst, "rst");
} }
int int
uxn_halt(Uxn *u, Uint8 error, Uint16 addr) uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr)
{ {
system_inspect(u); Uint8 *d = &u->dev[0x00];
fprintf(stderr, "Halted: %s#%04x, at 0x%04x\n", errors[error], u->ram[addr], addr); 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; return 0;
} }
/* IO */ /* 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 void
system_deo(Device *d, Uint8 port) system_deo(Uxn *u, Uint8 *d, Uint8 port)
{ {
switch(port) { switch(port) {
case 0x2: d->u->wst.ptr = d->dat[port]; break; case 0x2: u->wst = (Stack *)(u->ram + (d[port] ? (d[port] * 0x100) : 0x10000)); break;
case 0x3: d->u->rst.ptr = d->dat[port]; break; case 0x3: u->rst = (Stack *)(u->ram + (d[port] ? (d[port] * 0x100) : 0x10100)); break;
case 0xe: system_inspect(d->u); break; case 0xe:
default: system_deo_special(d, port); if(u->wst->ptr || u->rst->ptr) system_inspect(u);
break;
} }
} }

View File

@ -9,13 +9,5 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
typedef struct SystemDevice {
Device device;
struct UxnScreen *screen;
} SystemDevice;
void system_inspect(Uxn *u); void system_inspect(Uxn *u);
void system_deo(Uxn *u, Uint8 *d, Uint8 port);
Uint8 system_dei(Device *d, Uint8 port);
void system_deo(Device *d, Uint8 port);
void system_deo_special(Device *d, Uint8 port);

View File

@ -31,6 +31,9 @@ WITH REGARD TO THIS SOFTWARE.
#define DEVW8OLD(x, y) { dev->dat[(x) & 0xf] = y; dev->deo(dev, (x) & 0x0f); } #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 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 WARP(x) { if(bs) pc = (x); else pc += (Sint8)(x); }
#define LIMIT 0x40000 /* around 3 ms */ #define LIMIT 0x40000 /* around 3 ms */
@ -41,21 +44,16 @@ uxn_eval(Uxn *u, Uint16 pc)
unsigned int limit = LIMIT; unsigned int limit = LIMIT;
Uint8 kptr, *sp; Uint8 kptr, *sp;
Stack *src, *dst; Stack *src, *dst;
Device *dev;
if(!pc || u->devold[0].dat[0xf]) return 0; if(!pc || u->devold[0].dat[0xf]) return 0;
while((instr = u->ram[pc++])) { while((instr = u->ram[pc++])) {
if(!limit--) { if(!limit--) {
if(!uxn_interrupt()) {
errcode = 6;
goto timeout;
}
limit = LIMIT; limit = LIMIT;
} }
/* Return Mode */ /* Return Mode */
if(instr & 0x40) { if(instr & 0x40) {
src = &u->rst; dst = &u->wst; src = u->rst; dst = u->wst;
} else { } else {
src = &u->wst; dst = &u->rst; src = u->wst; dst = u->rst;
} }
/* Keep Mode */ /* Keep Mode */
if(instr & 0x80) { 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 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 0x14: /* LDA */ POP16(a) PEEK(b, a) PUSH(src, b) break;
case 0x15: /* STA */ POP16(a) POP(b) POKE(a, 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 0x16: /* DEI */ POP8(a) DEVR(b, a) PUSH(src, b) break;
case 0x17: /* DEO */ POP8(a) POP(b) DEVWOLD(&u->devold[a >> 4], a, b) break; case 0x17: /* DEO */ POP8(a) POP(b) DEVW(a, b) break;
/* Arithmetic */ /* Arithmetic */
case 0x18: /* ADD */ POP(a) POP(b) PUSH(src, b + a) break; case 0x18: /* ADD */ POP(a) POP(b) PUSH(src, b + a) break;
case 0x19: /* SUB */ 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; return 1;
err: err:
/* set 1 in errcode if it involved the return stack instead of the working stack */ return uxn_halt(u, instr, errcode, pc - 1);
/* (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);
} }
/* clang-format on */ /* clang-format on */
@ -125,6 +118,8 @@ uxn_boot(Uxn *u, Uint8 *ram, Dei *dei, Deo *deo)
for(i = 0; i < sizeof(*u); i++) for(i = 0; i < sizeof(*u); i++)
cptr[i] = 0x00; cptr[i] = 0x00;
u->ram = ram; u->ram = ram;
u->wst = (Stack *)(ram + 0x10000);
u->rst = (Stack *)(ram + 0x10100);
u->dev = (Uint8 *)(ram + 0x10200); u->dev = (Uint8 *)(ram + 0x10200);
u->dei = dei; u->dei = dei;
u->deo = deo; u->deo = deo;

View File

@ -20,24 +20,17 @@ typedef unsigned int Uint32;
#define PAGE_PROGRAM 0x0100 #define PAGE_PROGRAM 0x0100
/* clang-format off */ #define DEVPEEK16(o, x) \
{ \
#define DEVPEEK16(o, x) { (o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; } (o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; \
#define DEVPOKE16(x, y) { d->dat[(x)] = (y) >> 8; d->dat[(x) + 1] = (y); } }
#define DEVPOKE16(x, y) \
{ \
d->dat[(x)] = (y) >> 8; \
d->dat[(x) + 1] = (y); \
}
#define GETVECTOR(d) ((d)->dat[0] << 8 | (d)->dat[1]) #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 { typedef struct Device {
struct Uxn *u; struct Uxn *u;
Uint8 dat[16]; Uint8 dat[16];
@ -45,9 +38,21 @@ typedef struct Device {
void (*deo)(struct Device *d, Uint8); void (*deo)(struct Device *d, Uint8);
} Device; } 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 { typedef struct Uxn {
Uint8 *ram, *dev; Uint8 *ram, *dev;
Stack wst, rst; Stack *wst, *rst;
Device devold[16]; Device devold[16];
Uint8 (*dei)(struct Uxn *u, Uint8 addr); Uint8 (*dei)(struct Uxn *u, Uint8 addr);
void (*deo)(struct Uxn *u, Uint8 addr, Uint8 value); 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_boot(Uxn *u, Uint8 *ram, Dei *dei, Deo *deo);
int uxn_eval(Uxn *u, Uint16 pc); int uxn_eval(Uxn *u, Uint16 pc);
int uxn_interrupt(void); int uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr);
int uxn_halt(Uxn *u, Uint8 error, Uint16 addr);
/* TODO: remove */
Device *uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *, Uint8), void (*deofn)(Device *, Uint8)); Device *uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *, Uint8), void (*deofn)(Device *, Uint8));
#endif /* UXN_UXN_H */ #endif /* UXN_UXN_H */

View File

@ -17,102 +17,60 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define SUPPORT 0x1c03 /* devices mask */
static int static int
error(char *msg, const char *err) emu_error(char *msg, const char *err)
{ {
fprintf(stderr, "Error %s: %s\n", msg, err); fprintf(stderr, "Error %s: %s\n", msg, err);
return 0; return 0;
} }
static Uint8 static int
emu_dei(Uxn *u, Uint8 addr) console_input(Uxn *u, char c)
{ {
return 0; Uint8 *d = &u->dev[0x10];
d[0x02] = c;
return uxn_eval(u, GETVEC(d));
} }
static void static void
emu_deo(Uxn *u, Uint8 addr, Uint8 v) console_deo(Uint8 *d, Uint8 port)
{ {
} FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr :
0;
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;
if(fd) { if(fd) {
fputc(d->dat[port], fd); fputc(d[port], fd);
fflush(fd); fflush(fd);
} }
} }
static Uint8 static Uint8
nil_dei(Device *d, Uint8 port) emu_dei(Uxn *u, Uint8 addr)
{ {
return d->dat[port]; Uint8 p = addr & 0x0f, d = addr & 0xf0;
} switch(d) {
case 0xa0: return file_dei(0, &u->dev[d], p);
static void case 0xb0: return file_dei(1, &u->dev[d], p);
nil_deo(Device *d, Uint8 port) case 0xc0: return datetime_dei(&u->dev[d], p);
{
(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);
} }
return u->dev[addr];
} }
int static void
uxn_interrupt(void) emu_deo(Uxn *u, Uint8 addr, Uint8 v)
{ {
return 1; Uint8 p = addr & 0x0f, d = addr & 0xf0;
} Uint16 mask = 0x1 << (d >> 4);
u->dev[addr] = v;
static int switch(d) {
start(Uxn *u) case 0x00: system_deo(u, &u->dev[d], p); break;
{ case 0x10: console_deo(&u->dev[d], p); break;
if(!uxn_boot(u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo)) case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break;
return error("Boot", "Failed"); case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break;
/* system */ uxn_port(u, 0x0, system_dei, system_deo); }
/* console */ uxn_port(u, 0x1, nil_dei, console_deo); if(p == 0x01 && !(SUPPORT & mask))
/* empty */ uxn_port(u, 0x2, nil_dei, nil_deo); fprintf(stderr, "Warning: Incompatible emulation, device: %02x.\n", d);
/* 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;
} }
int int
@ -121,19 +79,22 @@ main(int argc, char **argv)
Uxn u; Uxn u;
int i; int i;
if(argc < 2) if(argc < 2)
return error("Usage", "uxncli game.rom args"); return emu_error("Usage", "uxncli game.rom args");
if(!start(&u)) if(!uxn_boot(&u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo))
return error("Start", "Failed"); return emu_error("Boot", "Failed");
if(!load_rom(&u, argv[1])) if(!load_rom(&u, argv[1]))
return error("Load", "Failed"); return emu_error("Load", "Failed");
fprintf(stderr, "Loaded %s\n", argv[1]);
if(!uxn_eval(&u, PAGE_PROGRAM)) if(!uxn_eval(&u, PAGE_PROGRAM))
return error("Init", "Failed"); return emu_error("Init", "Failed");
for(i = 2; i < argc; i++) { for(i = 2; i < argc; i++) {
char *p = argv[i]; char *p = argv[i];
while(*p) console_input(&u, *p++); while(*p) console_input(&u, *p++);
console_input(&u, '\n'); console_input(&u, '\n');
} }
run(&u); while(!u.dev[0x0f]) {
int c = fgetc(stdin);
if(c != EOF)
console_input(&u, (Uint8)c);
}
return 0; return 0;
} }

View File

@ -63,6 +63,25 @@ error(char *msg, const char *err)
return 0; 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 #pragma mark - Generics
static void static void
@ -179,12 +198,34 @@ init(void)
static Uint8 static Uint8
emu_dei(Uxn *u, Uint8 addr) 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; return 0;
} }
static void static void
emu_deo(Uxn *u, Uint8 addr, Uint8 v) 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 void
@ -194,17 +235,6 @@ system_deo_special(Device *d, Uint8 port)
screen_palette(&uxn_screen, &d->dat[0x8]); 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 static Uint8
audio_dei(Device *d, Uint8 port) 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 */ /* Boot */
static int static int
@ -264,26 +281,10 @@ static int
start(Uxn *u, char *rom) start(Uxn *u, char *rom)
{ {
free(u->ram); 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."); return error("Boot", "Failed to start uxn.");
if(!load(u, rom)) if(!load(u, rom))
return error("Boot", "Failed to load 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; exec_deadline = SDL_GetPerformanceCounter() + deadline_interval;
if(!uxn_eval(u, PAGE_PROGRAM)) if(!uxn_eval(u, PAGE_PROGRAM))
return error("Boot", "Failed to start rom."); 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 static int
handle_events(Uxn *u) handle_events(Uxn *u)
{ {
@ -408,8 +401,8 @@ handle_events(Uxn *u)
} }
/* Audio */ /* Audio */
else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY) { else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY) {
Device *d = devaudio0 + (event.type - audio0_event); /* Device *d = devaudio0 + (event.type - audio0_event);
uxn_eval(u, GETVECTOR(d)); uxn_eval(u, GETVECTOR(d)); */
} }
/* Mouse */ /* Mouse */
else if(event.type == SDL_MOUSEMOTION) else if(event.type == SDL_MOUSEMOTION)
@ -467,7 +460,7 @@ run(Uxn *u)
exec_deadline = now + deadline_interval; exec_deadline = now + deadline_interval;
if(!handle_events(u)) if(!handle_events(u))
return 0; return 0;
uxn_eval(u, GETVECTOR(devscreen)); uxn_eval(u, GETVEC(&u->dev[0x20]));
if(uxn_screen.fg.changed || uxn_screen.bg.changed) if(uxn_screen.fg.changed || uxn_screen.bg.changed)
redraw(); redraw();
now = SDL_GetPerformanceCounter(); now = SDL_GetPerformanceCounter();
@ -479,12 +472,6 @@ run(Uxn *u)
return error("SDL_WaitEvent", SDL_GetError()); return error("SDL_WaitEvent", SDL_GetError());
} }
int
uxn_interrupt(void)
{
return !SDL_QuitRequested();
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {