Screen device is half ported to new device handlers
This commit is contained in:
parent
679aec047a
commit
7afe1f39c7
6
build.sh
6
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 ..
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
#define _XOPEN_SOURCE 500
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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, "Halted: %s#%04x, at 0x%04x\n", errors[error], u->ram[addr], addr);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
25
src/uxn.c
25
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;
|
||||
|
|
45
src/uxn.h
45
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 */
|
||||
|
|
123
src/uxncli.c
123
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;
|
||||
}
|
||||
|
|
103
src/uxnemu.c
103
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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue