0
0
Fork 0
mirror of https://git.sr.ht/~rabbits/uxn synced 2024-11-22 13:55:11 +00:00

Updated devices

This commit is contained in:
Devine Lu Linvega 2024-07-15 12:12:10 -07:00
parent 1c74aa1731
commit 215dc9db52
21 changed files with 569 additions and 590 deletions

View file

@ -96,10 +96,10 @@ uxnemu orca.rom | shim
## GUI Emulator Controls ## GUI Emulator Controls
- `F1` toggle zoom - `F1` toggle zoom
- `F2` toggle debug - `F2` toggle debugger
- `F3` capture screen - `F3` quit
- `F4` reboot - `F4` reboot
- `F5` soft reboot - `F5` reboot(soft)
- `F11` toggle fullscreen - `F11` toggle fullscreen
- `F12` toggle decorations - `F12` toggle decorations

View file

@ -250,10 +250,9 @@ audio_handler(void *ctx, Uint8 *out_stream, int len)
Uxn *u = (Uxn *)ctx; Uxn *u = (Uxn *)ctx;
Uint8 *addr = &u->dev[device]; Uint8 *addr = &u->dev[device];
if(channel[n].duration <= 0 && PEEK2(addr)) { if(channel[n].duration <= 0 && PEEK2(addr)) {
uxn_eval(u, PEEK2(addr)); uxn_eval(PEEK2(addr));
} }
channel[n].duration -= SOUND_TIMER; channel[n].duration -= SOUND_TIMER;
int x = 0; int x = 0;
if(channel[n].xfade) { if(channel[n].xfade) {
float delta = 1.0f / (XFADE_SAMPLES * 2); float delta = 1.0f / (XFADE_SAMPLES * 2);

View file

@ -11,8 +11,6 @@ WITH REGARD TO THIS SOFTWARE.
typedef signed int Sint32; typedef signed int Sint32;
#define AUDIO_VERSION 1
#define AUDIO_BUFSIZE 256.0f #define AUDIO_BUFSIZE 256.0f
#define SAMPLE_FREQUENCY 44100.0f #define SAMPLE_FREQUENCY 44100.0f
#define POLYPHONY 4 #define POLYPHONY 4

View file

@ -5,7 +5,7 @@
#include "console.h" #include "console.h"
/* /*
Copyright (c) 2022-2023 Devine Lu Linvega, Andrew Alderwick Copyright (c) 2022-2024 Devine Lu Linvega, 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,35 +16,29 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
int int
console_input(Uxn *u, char c, int type) console_input(Uint8 c, int type)
{ {
Uint8 *d = &u->dev[0x10]; uxn.dev[0x12] = c;
d[0x2] = c; uxn.dev[0x17] = type;
d[0x7] = type; return uxn_eval(PEEK2(&uxn.dev[0x10]));
return uxn_eval(u, PEEK2(d));
} }
void void
console_listen(Uxn *u, int i, int argc, char **argv) console_listen(int i, int argc, char **argv)
{ {
for(; i < argc; i++) { for(; i < argc; i++) {
char *p = argv[i]; char *p = argv[i];
while(*p) console_input(u, *p++, CONSOLE_ARG); while(*p) console_input(*p++, CONSOLE_ARG);
console_input(u, '\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA); console_input('\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA);
} }
} }
void void
console_deo(Uint8 *d, Uint8 port) console_deo(Uint8 addr)
{ {
switch(port) { FILE *fd;
case 0x8: switch(addr) {
fputc(d[port], stdout); case 0x18: fd = stdout, fputc(uxn.dev[0x18], fd), fflush(fd); break;
fflush(stdout); case 0x19: fd = stderr, fputc(uxn.dev[0x19], fd), fflush(fd); break;
return;
case 0x9:
fputc(d[port], stderr);
fflush(stderr);
return;
} }
} }

View file

@ -9,13 +9,12 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define CONSOLE_VERSION 1
#define CONSOLE_STD 0x1 #define CONSOLE_STD 0x1
#define CONSOLE_ARG 0x2 #define CONSOLE_ARG 0x2
#define CONSOLE_EOA 0x3 #define CONSOLE_EOA 0x3
#define CONSOLE_END 0x4 #define CONSOLE_END 0x4
int console_input(Uxn *u, char c, int type); int console_input(Uint8 c, int type);
void console_listen(Uxn *u, int i, int argc, char **argv); void console_listen(int i, int argc, char **argv);
void console_deo(Uint8 *d, Uint8 port); Uint8 console_dei(Uint8 addr);
void console_deo(Uint8 addr);

View file

@ -13,29 +13,29 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
void void
controller_down(Uxn *u, Uint8 *d, Uint8 mask) controller_down(Uint8 mask)
{ {
if(mask) { if(mask) {
d[2] |= mask; uxn.dev[0x82] |= mask;
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x80]));
} }
} }
void void
controller_up(Uxn *u, Uint8 *d, Uint8 mask) controller_up(Uint8 mask)
{ {
if(mask) { if(mask) {
d[2] &= (~mask); uxn.dev[0x82] &= (~mask);
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x80]));
} }
} }
void void
controller_key(Uxn *u, Uint8 *d, Uint8 key) controller_key(Uint8 key)
{ {
if(key) { if(key) {
d[3] = key; uxn.dev[0x83] = key;
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x80]));
d[3] = 0x00; uxn.dev[0x83] = 0;
} }
} }

View file

@ -9,8 +9,6 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define CONTROL_VERSION 1 void controller_down(Uint8 mask);
void controller_up(Uint8 mask);
void controller_down(Uxn *u, Uint8 *d, Uint8 mask); void controller_key(Uint8 key);
void controller_up(Uxn *u, Uint8 *d, Uint8 mask);
void controller_key(Uxn *u, Uint8 *d, Uint8 key);

View file

@ -15,11 +15,13 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
Uint8 Uint8
datetime_dei(Uxn *u, Uint8 addr) datetime_dei(Uint8 addr)
{ {
time_t seconds = time(NULL); time_t seconds = time(NULL);
struct tm zt = {0}, *t = localtime(&seconds); struct tm zt = {0};
if(t == NULL) t = &zt; struct tm *t = localtime(&seconds);
if(t == NULL)
t = &zt;
switch(addr) { switch(addr) {
case 0xc0: return (t->tm_year + 1900) >> 8; case 0xc0: return (t->tm_year + 1900) >> 8;
case 0xc1: return (t->tm_year + 1900); case 0xc1: return (t->tm_year + 1900);
@ -32,6 +34,6 @@ datetime_dei(Uxn *u, Uint8 addr)
case 0xc8: return t->tm_yday >> 8; case 0xc8: return t->tm_yday >> 8;
case 0xc9: return t->tm_yday; case 0xc9: return t->tm_yday;
case 0xca: return t->tm_isdst; case 0xca: return t->tm_isdst;
default: return u->dev[addr]; default: return uxn.dev[addr];
} }
} }

View file

@ -9,6 +9,4 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define DATETIME_VERSION 1 Uint8 datetime_dei(Uint8 addr);
Uint8 datetime_dei(Uxn *u, Uint8 addr);

View file

@ -54,27 +54,24 @@ typedef struct {
static UxnFile uxn_file[POLYFILEY]; static UxnFile uxn_file[POLYFILEY];
static char
inthex(int n)
{
n &= 0xf;
return n < 10 ? '0' + n : 'a' + (n - 10);
}
static void static void
reset(UxnFile *c) reset(UxnFile *c)
{ {
if(c->f != NULL) if(c->f != NULL) {
fclose(c->f), c->f = NULL; fclose(c->f);
if(c->dir != NULL) c->f = NULL;
closedir(c->dir), c->dir = NULL; }
if(c->dir != NULL) {
closedir(c->dir);
c->dir = NULL;
}
c->de = NULL; c->de = NULL;
c->state = IDLE; c->state = IDLE;
c->outside_sandbox = 0; c->outside_sandbox = 0;
} }
static Uint16 static Uint16
put_line(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero) get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
{ {
struct stat st; struct stat st;
if(len < strlen(basename) + 8) if(len < strlen(basename) + 8)
@ -117,7 +114,7 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name); snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name);
else else
pathname[0] = '\0'; pathname[0] = '\0';
n = put_line(p, len, pathname, c->de->d_name, 1); n = get_entry(p, len, pathname, c->de->d_name, 1);
if(!n) break; if(!n) break;
p += n; p += n;
len -= n; len -= n;
@ -228,25 +225,15 @@ file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
} }
static Uint16 static Uint16
file_stat(UxnFile *c, char *p, Uint16 len) file_stat(UxnFile *c, void *dest, Uint16 len)
{ {
unsigned int i, size; char *basename = strrchr(c->current_filename, DIR_SEP_CHAR);
struct stat st; if(c->outside_sandbox) return 0;
if(c->outside_sandbox || !len) if(basename != NULL)
return 0; basename++;
if(stat(c->current_filename, &st))
for(i = 0; i < len; i++)
p[i] = '!';
else if(S_ISDIR(st.st_mode))
for(i = 0; i < len; i++)
p[i] = '-';
else if(st.st_size >= 1 << (len << 2))
for(i = 0; i < len; i++)
p[i] = '?';
else else
for(i = 0, size = st.st_size; i < len; i++) basename = c->current_filename;
p[i] = inthex(size >> ((len - i - 1) << 2)); return get_entry(dest, len, c->current_filename, basename, 0);
return len;
} }
static Uint16 static Uint16
@ -255,48 +242,79 @@ file_delete(UxnFile *c)
return c->outside_sandbox ? 0 : unlink(c->current_filename); return c->outside_sandbox ? 0 : unlink(c->current_filename);
} }
/* file registers */
static Uint16 rL;
/* IO */ /* IO */
void void
file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port) file_deo(Uint8 port)
{ {
UxnFile *c = &uxn_file[id]; Uint16 addr, len, res;
Uint16 addr, res;
switch(port) { switch(port) {
case 0x5: case 0xa5:
addr = (d[0x4] << 8) | d[0x5]; addr = PEEK2(&uxn.dev[0xa4]);
if(rL > 0x10000 - addr) rL = 0x10000 - addr; len = PEEK2(&uxn.dev[0xaa]);
res = file_stat(c, (char *)&ram[addr], rL > 0x10 ? 0x10 : rL); if(len > 0x10000 - addr)
d[0x2] = res >> 8, d[0x3] = res; len = 0x10000 - addr;
return; res = file_stat(&uxn_file[0], &uxn.ram[addr], len);
case 0x6: POKE2(&uxn.dev[0xa2], res);
res = file_delete(c); break;
d[0x2] = res >> 8, d[0x3] = res; case 0xa6:
return; res = file_delete(&uxn_file[0]);
case 0x9: POKE2(&uxn.dev[0xa2], res);
addr = (d[0x8] << 8) | d[0x9]; break;
res = file_init(c, (char *)&ram[addr], 0x10000 - addr, 0); case 0xa9:
d[0x2] = res >> 8, d[0x3] = res; addr = PEEK2(&uxn.dev[0xa8]);
return; res = file_init(&uxn_file[0], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
case 0xa: POKE2(&uxn.dev[0xa2], res);
case 0xb: break;
rL = (d[0xa] << 8) | d[0xb]; case 0xad:
return; addr = PEEK2(&uxn.dev[0xac]);
case 0xd: len = PEEK2(&uxn.dev[0xaa]);
addr = (d[0xc] << 8) | d[0xd]; if(len > 0x10000 - addr)
if(rL > 0x10000 - addr) rL = 0x10000 - addr; len = 0x10000 - addr;
res = file_read(c, &ram[addr], rL); res = file_read(&uxn_file[0], &uxn.ram[addr], len);
d[0x2] = res >> 8, d[0x3] = res; POKE2(&uxn.dev[0xa2], res);
return; break;
case 0xf: case 0xaf:
addr = (d[0xe] << 8) | d[0xf]; addr = PEEK2(&uxn.dev[0xae]);
if(rL > 0x10000 - addr) rL = 0x10000 - addr; len = PEEK2(&uxn.dev[0xaa]);
res = file_write(c, &ram[addr], rL, d[0x7]); if(len > 0x10000 - addr)
d[0x2] = res >> 8, d[0x3] = res; len = 0x10000 - addr;
return; res = file_write(&uxn_file[0], &uxn.ram[addr], len, uxn.dev[0xa7]);
POKE2(&uxn.dev[0xa2], res);
break;
/* File 2 */
case 0xb5:
addr = PEEK2(&uxn.dev[0xb4]);
len = PEEK2(&uxn.dev[0xba]);
if(len > 0x10000 - addr)
len = 0x10000 - addr;
res = file_stat(&uxn_file[1], &uxn.ram[addr], len);
POKE2(&uxn.dev[0xb2], res);
break;
case 0xb6:
res = file_delete(&uxn_file[1]);
POKE2(&uxn.dev[0xb2], res);
break;
case 0xb9:
addr = PEEK2(&uxn.dev[0xb8]);
res = file_init(&uxn_file[1], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
POKE2(&uxn.dev[0xb2], res);
break;
case 0xbd:
addr = PEEK2(&uxn.dev[0xbc]);
len = PEEK2(&uxn.dev[0xba]);
if(len > 0x10000 - addr)
len = 0x10000 - addr;
res = file_read(&uxn_file[1], &uxn.ram[addr], len);
POKE2(&uxn.dev[0xb2], res);
break;
case 0xbf:
addr = PEEK2(&uxn.dev[0xbe]);
len = PEEK2(&uxn.dev[0xba]);
if(len > 0x10000 - addr)
len = 0x10000 - addr;
res = file_write(&uxn_file[1], &uxn.ram[addr], len, uxn.dev[0xb7]);
POKE2(&uxn.dev[0xb2], res);
break;
} }
} }

View file

@ -9,9 +9,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define FILE_VERSION 1
#define POLYFILEY 2 #define POLYFILEY 2
#define DEV_FILE0 0xa #define DEV_FILE0 0xa
void file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port); void file_deo(Uint8 port);

View file

@ -13,32 +13,33 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
void void
mouse_down(Uxn *u, Uint8 *d, Uint8 mask) mouse_down(Uint8 mask)
{ {
d[6] |= mask; uxn.dev[0x96] |= mask;
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x90]));
} }
void void
mouse_up(Uxn *u, Uint8 *d, Uint8 mask) mouse_up(Uint8 mask)
{ {
d[6] &= (~mask); uxn.dev[0x96] &= (~mask);
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x90]));
} }
void void
mouse_pos(Uxn *u, Uint8 *d, Uint16 x, Uint16 y) mouse_pos(Uint16 x, Uint16 y)
{ {
*(d + 2) = x >> 8, *(d + 3) = x; uxn.dev[0x92] = x >> 8, uxn.dev[0x93] = x;
*(d + 4) = y >> 8, *(d + 5) = y; uxn.dev[0x94] = y >> 8, uxn.dev[0x95] = y;
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x90]));
} }
void void
mouse_scroll(Uxn *u, Uint8 *d, Uint16 x, Uint16 y) mouse_scroll(Uint16 x, Uint16 y)
{ {
*(d + 0xa) = x >> 8, *(d + 0xb) = x; uxn.dev[0x9a] = x >> 8, uxn.dev[0x9b] = x;
*(d + 0xc) = -y >> 8, *(d + 0xd) = -y; uxn.dev[0x9c] = -y >> 8, uxn.dev[0x9d] = -y;
uxn_eval(u, PEEK2(d)); uxn_eval(PEEK2(&uxn.dev[0x90]));
*(d + 0xa) = *(d + 0xb) = *(d + 0xc) = *(d + 0xd) = 0; uxn.dev[0x9a] = 0, uxn.dev[0x9b] = 0;
uxn.dev[0x9c] = 0, uxn.dev[0x9d] = 0;
} }

View file

@ -9,9 +9,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define MOUSE_VERSION 1 void mouse_down(Uint8 mask);
void mouse_up(Uint8 mask);
void mouse_down(Uxn *u, Uint8 *d, Uint8 mask); void mouse_pos(Uint16 x, Uint16 y);
void mouse_up(Uxn *u, Uint8 *d, Uint8 mask); void mouse_scroll(Uint16 x, Uint16 y);
void mouse_pos(Uxn *u, Uint8 *d, Uint16 x, Uint16 y);
void mouse_scroll(Uxn *u, Uint8 *d, Uint16 x, Uint16 y);

View file

@ -19,55 +19,24 @@ UxnScreen uxn_screen;
/* c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1; */ /* c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1; */
static Uint8 blending[][16] = { static Uint8 blending[4][16] = {
{0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0},
{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
{1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1},
{2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}, {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}};
{0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}};
void
screen_change(Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2)
{
if(x1 > uxn_screen.width && x2 > x1) return;
if(y1 > uxn_screen.height && y2 > y1) return;
if(x1 > x2) x1 = 0;
if(y1 > y2) y1 = 0;
if(x1 < uxn_screen.x1) uxn_screen.x1 = x1;
if(y1 < uxn_screen.y1) uxn_screen.y1 = y1;
if(x2 > uxn_screen.x2) uxn_screen.x2 = x2;
if(y2 > uxn_screen.y2) uxn_screen.y2 = y2;
}
void
screen_fill(Uint8 *layer, int color)
{
int i, length = uxn_screen.width * uxn_screen.height;
for(i = 0; i < length; i++)
layer[i] = color;
}
void
screen_rect(Uint8 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, int color)
{
int row, x, y, w = uxn_screen.width, h = uxn_screen.height;
for(y = y1; y < y2 && y < h; y++)
for(x = x1, row = y * w; x < x2 && x < w; x++)
layer[x + row] = color;
}
static void static void
screen_2bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int fx, int fy) screen_2bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int fx, int fy)
{ {
int w = uxn_screen.width, h = uxn_screen.height, opaque = blending[4][color]; int row, w = uxn_screen.width, h = uxn_screen.height, opaque = (color % 5);
Uint16 y, ymod = (fy < 0 ? 7 : 0), ymax = y1 + ymod + fy * 8; Uint16 y, ymod = (fy < 0 ? 7 : 0), ymax = y1 + ymod + fy * 8;
Uint16 x, xmod = (fx > 0 ? 7 : 0), xmax = x1 + xmod - fx * 8; Uint16 x, xmod = (fx > 0 ? 7 : 0), xmax = x1 + xmod - fx * 8;
for(y = y1 + ymod; y != ymax; y += fy, addr++) { for(y = y1 + ymod; y != ymax; y += fy, addr++) {
int c = addr[0] | (addr[8] << 8), row = y * w; int c = (addr[8] << 8) | addr[0];
if(y < h) if(y < h)
for(x = x1 + xmod; x != xmax; x -= fx, c >>= 1) { for(x = x1 + xmod, row = y * w; x != xmax; x -= fx, c >>= 1) {
Uint8 ch = (c & 1) | ((c >> 7) & 2); Uint8 ch = (c & 1) | ((c >> 7) & 2);
if(x < w && (opaque || ch)) if((opaque || ch) && x < w)
layer[x + row] = blending[ch][color]; layer[x + row] = blending[ch][color];
} }
} }
@ -76,20 +45,51 @@ screen_2bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int f
static void static void
screen_1bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int fx, int fy) screen_1bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int fx, int fy)
{ {
int w = uxn_screen.width, h = uxn_screen.height, opaque = blending[4][color]; int row, w = uxn_screen.width, h = uxn_screen.height, opaque = (color % 5);
Uint16 y, ymod = (fy < 0 ? 7 : 0), ymax = y1 + ymod + fy * 8; Uint16 y, ymod = (fy < 0 ? 7 : 0), ymax = y1 + ymod + fy * 8;
Uint16 x, xmod = (fx > 0 ? 7 : 0), xmax = x1 + xmod - fx * 8; Uint16 x, xmod = (fx > 0 ? 7 : 0), xmax = x1 + xmod - fx * 8;
for(y = y1 + ymod; y != ymax; y += fy) { for(y = y1 + ymod; y != ymax; y += fy) {
int c = *addr++, row = y * w; int c = *addr++;
if(y < h) if(y < h)
for(x = x1 + xmod; x != xmax; x -= fx, c >>= 1) { for(x = x1 + xmod, row = y * w; x != xmax; x -= fx, c >>= 1) {
Uint8 ch = c & 1; Uint8 ch = c & 1;
if(x < w && (opaque || ch)) if((opaque || ch) && x < w)
layer[x + row] = blending[ch][color]; layer[x + row] = blending[ch][color];
} }
} }
} }
int
screen_changed(void)
{
if(uxn_screen.x1 < 0)
uxn_screen.x1 = 0;
else if(uxn_screen.x1 >= uxn_screen.width)
uxn_screen.x1 = uxn_screen.width;
if(uxn_screen.y1 < 0)
uxn_screen.y1 = 0;
else if(uxn_screen.y1 >= uxn_screen.height)
uxn_screen.y1 = uxn_screen.height;
if(uxn_screen.x2 < 0)
uxn_screen.x2 = 0;
else if(uxn_screen.x2 >= uxn_screen.width)
uxn_screen.x2 = uxn_screen.width;
if(uxn_screen.y2 < 0)
uxn_screen.y2 = 0;
else if(uxn_screen.y2 >= uxn_screen.height)
uxn_screen.y2 = uxn_screen.height;
return uxn_screen.x2 > uxn_screen.x1 && uxn_screen.y2 > uxn_screen.y1;
}
void
screen_change(int x1, int y1, int x2, int y2)
{
if(x1 < uxn_screen.x1) uxn_screen.x1 = x1;
if(y1 < uxn_screen.y1) uxn_screen.y1 = y1;
if(x2 > uxn_screen.x2) uxn_screen.x2 = x2;
if(y2 > uxn_screen.y2) uxn_screen.y2 = y2;
}
/* clang-format off */ /* clang-format off */
static Uint8 icons[] = { static Uint8 icons[] = {
@ -116,37 +116,54 @@ draw_byte(Uint8 b, Uint16 x, Uint16 y, Uint8 color)
} }
static void static void
screen_debugger(Uxn *u) screen_debugger(void)
{ {
int i; int i;
for(i = 0; i < 0x08; i++) { for(i = 0; i < 0x08; i++) {
Uint8 pos = u->wst.ptr - 4 + i; Uint8 pos = uxn.wst.ptr - 4 + i;
Uint8 color = i > 4 ? 0x01 : !pos ? 0xc Uint8 color = i > 4 ? 0x01 : !pos ? 0xc
: i == 4 ? 0x8 : i == 4 ? 0x8
: 0x2; : 0x2;
draw_byte(u->wst.dat[pos], i * 0x18 + 0x8, uxn_screen.height - 0x18, color); draw_byte(uxn.wst.dat[pos], i * 0x18 + 0x8, uxn_screen.height - 0x18, color);
} }
for(i = 0; i < 0x08; i++) { for(i = 0; i < 0x08; i++) {
Uint8 pos = u->rst.ptr - 4 + i; Uint8 pos = uxn.rst.ptr - 4 + i;
Uint8 color = i > 4 ? 0x01 : !pos ? 0xc Uint8 color = i > 4 ? 0x01 : !pos ? 0xc
: i == 4 ? 0x8 : i == 4 ? 0x8
: 0x2; : 0x2;
draw_byte(u->rst.dat[pos], i * 0x18 + 0x8, uxn_screen.height - 0x10, color); draw_byte(uxn.rst.dat[pos], i * 0x18 + 0x8, uxn_screen.height - 0x10, color);
} }
screen_1bpp(uxn_screen.fg, &arrow[0], 0x68, uxn_screen.height - 0x20, 3, 1, 1); screen_1bpp(uxn_screen.fg, &arrow[0], 0x68, uxn_screen.height - 0x20, 3, 1, 1);
for(i = 0; i < 0x20; i++) for(i = 0; i < 0x20; i++)
draw_byte(u->ram[i], (i & 0x7) * 0x18 + 0x8, ((i >> 3) << 3) + 0x8, 1 + !!u->ram[i]); draw_byte(uxn.ram[i], (i & 0x7) * 0x18 + 0x8, ((i >> 3) << 3) + 0x8, 1 + !!uxn.ram[i]);
} }
void void
screen_palette(Uint8 *addr) screen_fill(Uint8 *layer, int color)
{
int i, length = uxn_screen.width * uxn_screen.height;
for(i = 0; i < length; i++)
layer[i] = color;
}
void
screen_rect(Uint8 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, int color)
{
int row, x, y, w = uxn_screen.width, h = uxn_screen.height;
for(y = y1; y < y2 && y < h; y++)
for(x = x1, row = y * w; x < x2 && x < w; x++)
layer[x + row] = color;
}
void
screen_palette(void)
{ {
int i, shift; int i, shift;
for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) { for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
Uint8 Uint8
r = (addr[0 + i / 2] >> shift) & 0xf, r = (uxn.dev[0x8 + i / 2] >> shift) & 0xf,
g = (addr[2 + i / 2] >> shift) & 0xf, g = (uxn.dev[0xa + i / 2] >> shift) & 0xf,
b = (addr[4 + i / 2] >> shift) & 0xf; b = (uxn.dev[0xc + i / 2] >> shift) & 0xf;
uxn_screen.palette[i] = 0x0f000000 | r << 16 | g << 8 | b; uxn_screen.palette[i] = 0x0f000000 | r << 16 | g << 8 | b;
uxn_screen.palette[i] |= uxn_screen.palette[i] << 4; uxn_screen.palette[i] |= uxn_screen.palette[i] << 4;
} }
@ -154,56 +171,74 @@ screen_palette(Uint8 *addr)
} }
void void
screen_resize(Uint16 width, Uint16 height) screen_resize(Uint16 width, Uint16 height, int scale)
{ {
Uint8 *bg, *fg; Uint8 *bg, *fg;
Uint32 *pixels = NULL; Uint32 *pixels = NULL;
if(width < 0x8 || height < 0x8 || width >= 0x800 || height >= 0x800) int dim_change = uxn_screen.width != width || uxn_screen.height != height;
if(width < 0x8 || height < 0x8 || width >= 0x800 || height >= 0x800 || scale < 1 || scale >= 4)
return; return;
if(uxn_screen.width == width && uxn_screen.height == height) if(uxn_screen.width == width && uxn_screen.height == height && uxn_screen.scale == scale)
return; return;
if(dim_change) {
bg = malloc(width * height), fg = malloc(width * height); bg = malloc(width * height), fg = malloc(width * height);
if(bg && fg) if(bg && fg)
pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32)); pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32) * scale * scale);
if(!bg || !fg || !pixels) { if(!bg || !fg || !pixels) {
free(bg), free(fg); free(bg), free(fg);
return; return;
} }
free(uxn_screen.bg), free(uxn_screen.fg); free(uxn_screen.bg), free(uxn_screen.fg);
} else {
bg = uxn_screen.bg, fg = uxn_screen.fg;
pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32) * scale * scale);
if(!pixels)
return;
}
uxn_screen.bg = bg, uxn_screen.fg = fg; uxn_screen.bg = bg, uxn_screen.fg = fg;
uxn_screen.pixels = pixels; uxn_screen.pixels = pixels;
uxn_screen.width = width, uxn_screen.height = height; uxn_screen.width = width, uxn_screen.height = height, uxn_screen.scale = scale;
if(dim_change)
screen_fill(uxn_screen.bg, 0), screen_fill(uxn_screen.fg, 0); screen_fill(uxn_screen.bg, 0), screen_fill(uxn_screen.fg, 0);
emu_resize(width, height); emu_resize(width, height);
screen_change(0, 0, width, height); screen_change(0, 0, width, height);
} }
void void
screen_redraw(Uxn *u) screen_redraw(void)
{ {
int i, j, o, y; int i, x, y, k, l, s = uxn_screen.scale;
Uint8 *fg = uxn_screen.fg, *bg = uxn_screen.bg; Uint8 *fg = uxn_screen.fg, *bg = uxn_screen.bg;
Uint16 w = uxn_screen.width, h = uxn_screen.height; Uint16 w = uxn_screen.width, h = uxn_screen.height;
Uint16 x1 = uxn_screen.x1, y1 = uxn_screen.y1; Uint16 x1 = uxn_screen.x1, y1 = uxn_screen.y1;
Uint16 x2 = uxn_screen.x2 > w ? w : uxn_screen.x2, y2 = uxn_screen.y2 > h ? h : uxn_screen.y2; Uint16 x2 = uxn_screen.x2 > w ? w : uxn_screen.x2, y2 = uxn_screen.y2 > h ? h : uxn_screen.y2;
Uint32 palette[16], *pixels = uxn_screen.pixels; Uint32 palette[16], *pixels = uxn_screen.pixels;
uxn_screen.x1 = uxn_screen.y1 = 0xffff; uxn_screen.x1 = uxn_screen.y1 = 9000;
uxn_screen.x2 = uxn_screen.y2 = 0; uxn_screen.x2 = uxn_screen.y2 = 0;
if(u->dev[0x0e]) if(uxn.dev[0x0e])
screen_debugger(u); screen_debugger();
for(i = 0; i < 16; i++) for(i = 0; i < 16; i++)
palette[i] = uxn_screen.palette[(i >> 2) ? (i >> 2) : (i & 3)]; palette[i] = uxn_screen.palette[(i >> 2) ? (i >> 2) : (i & 3)];
for(y = y1; y < y2; y++) for(y = y1; y < y2; y++) {
for(o = y * w, i = x1 + o, j = x2 + o; i < j; i++) int ys = y * s;
pixels[i] = palette[fg[i] << 2 | bg[i]]; int o = y * w;
for(x = x1, i = x1 + o; x < x2; x++, i++) {
int c = palette[fg[i] << 2 | bg[i]];
for(k = 0; k < s; k++) {
int oo = ((ys + k) * w + x) * s;
for(l = 0; l < s; l++)
pixels[oo + l] = c;
}
}
}
} }
/* screen registers */ /* screen registers */
static Uint16 rX, rY, rA, rMX, rMY, rMA, rML, rDX, rDY; static int rX, rY, rA, rMX, rMY, rMA, rML, rDX, rDY;
Uint8 Uint8
screen_dei(Uxn *u, Uint8 addr) screen_dei(Uint8 addr)
{ {
switch(addr) { switch(addr) {
case 0x22: return uxn_screen.width >> 8; case 0x22: return uxn_screen.width >> 8;
@ -216,25 +251,25 @@ screen_dei(Uxn *u, Uint8 addr)
case 0x2b: return rY; case 0x2b: return rY;
case 0x2c: return rA >> 8; case 0x2c: return rA >> 8;
case 0x2d: return rA; case 0x2d: return rA;
default: return u->dev[addr]; default: return uxn.dev[addr];
} }
} }
void void
screen_deo(Uint8 *ram, Uint8 *d, Uint8 port) screen_deo(Uint8 addr)
{ {
switch(port) { switch(addr) {
case 0x3: screen_resize(PEEK2(d + 2), uxn_screen.height); return; case 0x23: screen_resize(PEEK2(&uxn.dev[0x22]), uxn_screen.height, uxn_screen.scale); return;
case 0x5: screen_resize(uxn_screen.width, PEEK2(d + 4)); return; case 0x25: screen_resize(uxn_screen.width, PEEK2(&uxn.dev[0x24]), uxn_screen.scale); return;
case 0x6: rMX = d[0x6] & 0x1, rMY = d[0x6] & 0x2, rMA = d[0x6] & 0x4, rML = d[0x6] >> 4, rDX = rMX << 3, rDY = rMY << 2; return; case 0x26: rMX = uxn.dev[0x26] & 0x1, rMY = uxn.dev[0x26] & 0x2, rMA = uxn.dev[0x26] & 0x4, rML = uxn.dev[0x26] >> 4, rDX = rMX << 3, rDY = rMY << 2; return;
case 0x8: case 0x28:
case 0x9: rX = (d[0x8] << 8) | d[0x9]; return; case 0x29: rX = (uxn.dev[0x28] << 8) | uxn.dev[0x29], rX = twos(rX); return;
case 0xa: case 0x2a:
case 0xb: rY = (d[0xa] << 8) | d[0xb]; return; case 0x2b: rY = (uxn.dev[0x2a] << 8) | uxn.dev[0x2b], rY = twos(rY); return;
case 0xc: case 0x2c:
case 0xd: rA = (d[0xc] << 8) | d[0xd]; return; case 0x2d: rA = (uxn.dev[0x2c] << 8) | uxn.dev[0x2d]; return;
case 0xe: { case 0x2e: {
Uint8 ctrl = d[0xe]; Uint8 ctrl = uxn.dev[0x2e];
Uint8 color = ctrl & 0x3; Uint8 color = ctrl & 0x3;
Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg; Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg;
/* fill mode */ /* fill mode */
@ -254,7 +289,7 @@ screen_deo(Uint8 *ram, Uint8 *d, Uint8 port)
/* pixel mode */ /* pixel mode */
else { else {
Uint16 w = uxn_screen.width; Uint16 w = uxn_screen.width;
if(rX < w && rY < uxn_screen.height) if(rX >= 0 && rY >= 0 && rX < w && rY < uxn_screen.height)
layer[rX + rY * w] = color; layer[rX + rY * w] = color;
screen_change(rX, rY, rX + 1, rY + 1); screen_change(rX, rY, rX + 1, rY + 1);
if(rMX) rX++; if(rMX) rX++;
@ -262,22 +297,30 @@ screen_deo(Uint8 *ram, Uint8 *d, Uint8 port)
} }
return; return;
} }
case 0xf: { case 0x2f: {
Uint8 i; Uint8 i;
Uint8 ctrl = d[0xf]; Uint8 ctrl = uxn.dev[0x2f];
Uint8 twobpp = !!(ctrl & 0x80); Uint8 twobpp = !!(ctrl & 0x80);
Uint8 color = ctrl & 0xf; Uint8 color = ctrl & 0xf;
Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg; Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg;
int fx = ctrl & 0x10 ? -1 : 1; int fx = ctrl & 0x10 ? -1 : 1, fy = ctrl & 0x20 ? -1 : 1;
int fy = ctrl & 0x20 ? -1 : 1; int x1, x2, y1, y2, x = rX, y = rY;
Uint16 dxy = rDX * fy, dyx = rDY * fx, addr_incr = rMA << (1 + twobpp); int dxy = rDX * fy, dyx = rDY * fx, addr_incr = rMA << (1 + twobpp);
if(twobpp) if(twobpp)
for(i = 0; i <= rML; i++, rA += addr_incr) for(i = 0; i <= rML; i++, x += dyx, y += dxy, rA += addr_incr)
screen_2bpp(layer, &ram[rA], rX + dyx * i, rY + dxy * i, color, fx, fy); screen_2bpp(layer, &uxn.ram[rA], x, y, color, fx, fy);
else else
for(i = 0; i <= rML; i++, rA += addr_incr) for(i = 0; i <= rML; i++, x += dyx, y += dxy, rA += addr_incr)
screen_1bpp(layer, &ram[rA], rX + dyx * i, rY + dxy * i, color, fx, fy); screen_1bpp(layer, &uxn.ram[rA], x, y, color, fx, fy);
screen_change(rX, rY, rX + dyx * rML + 8, rY + dxy * rML + 8); if(fx < 0)
x1 = x, x2 = rX;
else
x1 = rX, x2 = x;
if(fy < 0)
y1 = y, y2 = rY;
else
y1 = rY, y2 = y;
screen_change(x1, y1, x2 + 8, y2 + 8);
if(rMX) rX += rDX * fx; if(rMX) rX += rDX * fx;
if(rMY) rY += rDY * fy; if(rMY) rY += rDY * fy;
return; return;

View file

@ -9,23 +9,23 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define SCREEN_VERSION 1
typedef struct UxnScreen { typedef struct UxnScreen {
int width, height, x1, y1, x2, y2; int width, height, x1, y1, x2, y2, scale;
Uint32 palette[4], *pixels; Uint32 palette[4], *pixels;
Uint8 *fg, *bg; Uint8 *fg, *bg;
} UxnScreen; } UxnScreen;
extern UxnScreen uxn_screen; extern UxnScreen uxn_screen;
extern int emu_resize(int width, int height); extern int emu_resize(int width, int height);
int screen_changed(void);
void screen_change(int x1, int y1, int x2, int y2);
void screen_fill(Uint8 *layer, int color); void screen_fill(Uint8 *layer, int color);
void screen_rect(Uint8 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, int color); void screen_rect(Uint8 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, int color);
void screen_palette(Uint8 *addr); void screen_palette(void);
void screen_resize(Uint16 width, Uint16 height); void screen_resize(Uint16 width, Uint16 height, int scale);
void screen_change(Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2); void screen_redraw();
void screen_redraw(Uxn *u);
Uint8 screen_dei(Uxn *u, Uint8 addr); Uint8 screen_dei(Uint8 addr);
void screen_deo(Uint8 *ram, Uint8 *d, Uint8 port); void screen_deo(Uint8 addr);
#define twos(v) (v & 0x8000 ? (int)v - 0x10000 : (int)v)

View file

@ -5,7 +5,7 @@
#include "system.h" #include "system.h"
/* /*
Copyright (c) 2022-2023 Devine Lu Linvega, Andrew Alderwick Copyright (c) 2022-2024 Devine Lu Linvega, 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,20 +16,29 @@ WITH REGARD TO THIS SOFTWARE.
*/ */
char *boot_rom; char *boot_rom;
Uint16 dev_vers[0x10];
static void
system_zero(int soft)
{
int i;
for(i = soft ? 0x100 : 0; i < 0x10000; i++)
uxn.ram[i] = 0;
for(i = 0x0; i < 0x100; i++)
uxn.dev[i] = 0;
uxn.wst.ptr = uxn.rst.ptr = 0;
}
static int static int
system_load(Uxn *u, char *filename) system_load(char *filename)
{ {
int l, i = 0;
FILE *f = fopen(filename, "rb"); FILE *f = fopen(filename, "rb");
if(!f) if(f) {
return 0; int i = 0, l = fread(&uxn.ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM, 1, f);
l = fread(&u->ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM, 1, f);
while(l && ++i < RAM_PAGES) while(l && ++i < RAM_PAGES)
l = fread(u->ram + 0x10000 * i, 0x10000, 1, f); l = fread(uxn.ram + 0x10000 * i, 0x10000, 1, f);
fclose(f); fclose(f);
return 1; }
return !!f;
} }
static void static void
@ -41,49 +50,38 @@ system_print(Stack *s)
fprintf(stderr, "< \n"); fprintf(stderr, "< \n");
} }
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;
}
void void
system_inspect(Uxn *u) system_inspect(void)
{ {
fprintf(stderr, "WST "), system_print(&u->wst); fprintf(stderr, "WST "), system_print(&uxn.wst);
fprintf(stderr, "RST "), system_print(&u->rst); fprintf(stderr, "RST "), system_print(&uxn.rst);
} }
int int
system_error(char *msg, const char *err) system_error(char *msg, const char *err)
{ {
fprintf(stderr, "%s %s\n", msg, err); fprintf(stderr, "%s: %s\n", msg, err);
fflush(stderr); fflush(stderr);
return 0; return 0;
} }
void void
system_reboot(Uxn *u, char *rom, int soft) system_reboot(char *rom, int soft)
{ {
system_zero(u, soft); system_zero(soft);
if(system_load(u, boot_rom)) if(system_load(boot_rom))
if(uxn_eval(u, PAGE_PROGRAM)) if(uxn_eval(PAGE_PROGRAM))
boot_rom = rom; boot_rom = rom;
} }
int int
system_init(Uxn *u, Uint8 *ram, char *rom) system_boot(Uint8 *ram, char *rom)
{ {
u->ram = ram; uxn.ram = ram;
system_zero(u, 0); system_zero(0);
if(!system_load(u, rom)) if(!system_load(rom))
if(!system_load(u, "boot.rom")) if(!system_load("boot.rom"))
return system_error("Init", "Failed to load rom."); return system_error("Could not load rom", rom);
boot_rom = rom; boot_rom = rom;
return 1; return 1;
} }
@ -91,56 +89,54 @@ system_init(Uxn *u, Uint8 *ram, char *rom)
/* IO */ /* IO */
Uint8 Uint8
system_dei(Uxn *u, Uint8 addr) system_dei(Uint8 addr)
{ {
switch(addr) { switch(addr) {
case 0x4: return u->wst.ptr; case 0x4: return uxn.wst.ptr;
case 0x5: return u->rst.ptr; case 0x5: return uxn.rst.ptr;
default: return u->dev[addr]; default: return uxn.dev[addr];
} }
} }
void void
system_deo(Uxn *u, Uint8 *d, Uint8 port) system_deo(Uint8 port)
{ {
Uint8 *ram;
Uint16 addr;
switch(port) { switch(port) {
case 0x3: case 0x3: {
ram = u->ram; Uint16 addr = PEEK2(uxn.dev + 2);
addr = PEEK2(d + 2); if(uxn.ram[addr] == 0x0) {
if(ram[addr] == 0x0) { Uint8 value = uxn.ram[addr + 7];
Uint8 value = ram[addr + 7]; Uint16 i, length = PEEK2(uxn.ram + addr + 1);
Uint16 i, length = PEEK2(ram + addr + 1); Uint16 dst_page = PEEK2(uxn.ram + addr + 3), dst_addr = PEEK2(uxn.ram + addr + 5);
Uint16 dst_page = PEEK2(ram + addr + 3), dst_addr = PEEK2(ram + addr + 5);
int dst = (dst_page % RAM_PAGES) * 0x10000; int dst = (dst_page % RAM_PAGES) * 0x10000;
for(i = 0; i < length; i++) for(i = 0; i < length; i++)
ram[dst + (Uint16)(dst_addr + i)] = value; uxn.ram[dst + (Uint16)(dst_addr + i)] = value;
} else if(ram[addr] == 0x1) { } else if(uxn.ram[addr] == 0x1) {
Uint16 i, length = PEEK2(ram + addr + 1); Uint16 i, length = PEEK2(uxn.ram + addr + 1);
Uint16 a_page = PEEK2(ram + addr + 3), a_addr = PEEK2(ram + addr + 5); Uint16 a_page = PEEK2(uxn.ram + addr + 3), a_addr = PEEK2(uxn.ram + addr + 5);
Uint16 b_page = PEEK2(ram + addr + 7), b_addr = PEEK2(ram + addr + 9); Uint16 b_page = PEEK2(uxn.ram + addr + 7), b_addr = PEEK2(uxn.ram + addr + 9);
int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000; int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
for(i = 0; i < length; i++) for(i = 0; i < length; i++)
ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)]; uxn.ram[dst + (Uint16)(b_addr + i)] = uxn.ram[src + (Uint16)(a_addr + i)];
} else if(ram[addr] == 0x2) { } else if(uxn.ram[addr] == 0x2) {
Uint16 i, length = PEEK2(ram + addr + 1); Uint16 i, length = PEEK2(uxn.ram + addr + 1);
Uint16 a_page = PEEK2(ram + addr + 3), a_addr = PEEK2(ram + addr + 5); Uint16 a_page = PEEK2(uxn.ram + addr + 3), a_addr = PEEK2(uxn.ram + addr + 5);
Uint16 b_page = PEEK2(ram + addr + 7), b_addr = PEEK2(ram + addr + 9); Uint16 b_page = PEEK2(uxn.ram + addr + 7), b_addr = PEEK2(uxn.ram + addr + 9);
int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000; int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
for(i = length - 1; i != 0xffff; i--) for(i = length - 1; i != 0xffff; i--)
ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)]; uxn.ram[dst + (Uint16)(b_addr + i)] = uxn.ram[src + (Uint16)(a_addr + i)];
} else } else
fprintf(stderr, "Unknown Expansion Command 0x%02x\n", ram[addr]); fprintf(stderr, "Unknown Expansion Command 0x%02x\n", uxn.ram[addr]);
break; break;
}
case 0x4: case 0x4:
u->wst.ptr = d[4]; uxn.wst.ptr = uxn.dev[4];
break; break;
case 0x5: case 0x5:
u->rst.ptr = d[5]; uxn.rst.ptr = uxn.dev[5];
break; break;
case 0xe: case 0xe:
system_inspect(u); system_inspect();
break; break;
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2022 Devine Lu Linvega, Andrew Alderwick Copyright (c) 2024 Devine Lu Linvega, 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
@ -9,16 +9,14 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
#define SYSTEM_VERSION 2
#define RAM_PAGES 0x10 #define RAM_PAGES 0x10
extern char *boot_rom; void system_reboot(char *rom, int soft);
void system_inspect(void);
int system_error(char *msg, const char *err); int system_error(char *msg, const char *err);
void system_reboot(Uxn *u, char *rom, int soft); int system_boot(Uint8 *ram, char *rom);
void system_inspect(Uxn *u);
int system_init(Uxn *u, Uint8 *ram, char *rom);
Uint8 system_dei(Uxn *u, Uint8 addr); Uint8 system_dei(Uint8 addr);
void system_deo(Uxn *u, Uint8 *d, Uint8 port); void system_deo(Uint8 addr);
extern char *boot_rom;

172
src/uxn.c
View file

@ -1,7 +1,7 @@
#include "uxn.h" #include "uxn.h"
/* /*
Copyright (u) 2022-2023 Devine Lu Linvega, Andrew Alderwick, Andrew Richards Copyright (u) 2022-2024 Devine Lu Linvega, Andrew Alderwick, Andrew Richards
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
@ -11,113 +11,79 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
/* Registers #define OPC(opc, body) {\
[ Z ][ Y ][ X ][ L ][ N ][ T ] < case 0x00|opc: {int _2=0,_r=0,a,b,c; Stack *s = &uxn.wst; Uint8 *sp = &uxn.wst.ptr; body break;}\
[ . ][ . ][ . ][ H2 ][ . ] < case 0x20|opc: {int _2=1,_r=0,a,b,c; Stack *s = &uxn.wst; Uint8 *sp = &uxn.wst.ptr; body break;}\
[ L2 ][ N2 ][ T2 ] < case 0x40|opc: {int _2=0,_r=1,a,b,c; Stack *s = &uxn.rst; Uint8 *sp = &uxn.rst.ptr; body break;}\
*/ case 0x60|opc: {int _2=1,_r=1,a,b,c; Stack *s = &uxn.rst; Uint8 *sp = &uxn.rst.ptr; body break;}\
case 0x80|opc: {int _2=0,_r=0,a,b,c; Stack *s = &uxn.wst; Uint8 kp = uxn.wst.ptr, *sp = &kp; body break;}\
case 0xa0|opc: {int _2=1,_r=0,a,b,c; Stack *s = &uxn.wst; Uint8 kp = uxn.wst.ptr, *sp = &kp; body break;}\
case 0xc0|opc: {int _2=0,_r=1,a,b,c; Stack *s = &uxn.rst; Uint8 kp = uxn.rst.ptr, *sp = &kp; body break;}\
case 0xe0|opc: {int _2=1,_r=1,a,b,c; Stack *s = &uxn.rst; Uint8 kp = uxn.rst.ptr, *sp = &kp; body break;}\
}
#define T *(s->dat + s->ptr) #define FLP { s = _r ? &uxn.wst : &uxn.rst; }
#define N *(s->dat + (Uint8)(s->ptr - 1)) #define JMI { pc += uxn.ram[pc++] << 8 | uxn.ram[pc++]; }
#define L *(s->dat + (Uint8)(s->ptr - 2)) #define JMP(x) { if(_2) pc = (x); else pc += (Sint8)(x); }
#define X *(s->dat + (Uint8)(s->ptr - 3)) #define POx(o) { if(_2) { PO2(o) } else PO1(o) }
#define Y *(s->dat + (Uint8)(s->ptr - 4)) #define PO1(o) { o = s->dat[--*sp]; }
#define Z *(s->dat + (Uint8)(s->ptr - 5)) #define PO2(o) { o = s->dat[--*sp] | (s->dat[--*sp] << 8); }
#define T2 (N << 8 | T) #define PUx(y) { if(_2) { PU2(y) } else PU1(y) }
#define H2 (L << 8 | N) #define PU1(y) { s->dat[s->ptr++] = (y); }
#define N2 (X << 8 | L) #define PU2(y) { tt = (y); s->dat[s->ptr++] = tt >> 0x8; s->dat[s->ptr++] = tt; }
#define L2 (Z << 8 | Y) #define IMM(x, y) { uxn.x.dat[uxn.x.ptr++] = (y); }
#define T2_(v) { r = (v); T = r; N = r >> 8; } #define DEI(o, p) { if(_2) { o = (emu_dei(p) << 8) | emu_dei(p + 1); } else o = emu_dei(p); }
#define N2_(v) { r = (v); L = r; X = r >> 8; } #define DEO(p, y) { if(_2) { emu_deo(p, y >> 8); emu_deo(p + 1, y); } else emu_deo(p, y); }
#define L2_(v) { r = (v); Y = r; Z = r >> 8; } #define PEK(o, x, r) { if(_2) { r = (x); o = uxn.ram[r++] << 8 | uxn.ram[r]; } else o = uxn.ram[(x)]; }
#define FLIP { s = ins & 0x40 ? &u->wst : &u->rst; } #define POK(x, y, r) { if(_2) { r = (x); uxn.ram[r++] = y >> 8; uxn.ram[r] = y; } else uxn.ram[(x)] = (y); }
#define SHIFT(y) { s->ptr += (y); }
#define SET(x, y) { SHIFT((ins & 0x80) ? x + y : y) }
int int
uxn_eval(Uxn *u, Uint16 pc) uxn_eval(Uint16 pc)
{ {
Uint16 t, n, l, r; if(!pc || uxn.dev[0x0f]) return 0;
Uint8 *ram = u->ram, *rr;
if(!pc || u->dev[0x0f]) return 0;
for(;;) { for(;;) {
Uint8 ins = ram[pc++]; Uint8 t;
Stack *s = ins & 0x40 ? &u->rst : &u->wst; Uint16 tt;
switch(ins & 0x3f) { switch(uxn.ram[pc++]) {
/* IMM */ /* BRK */ case 0x00: return 1;
case 0x00: case 0x20: /* JCI */ case 0x20: if(uxn.wst.dat[--uxn.wst.ptr]) { JMI break; } pc += 2; break;
switch(ins) { /* JMI */ case 0x40: JMI break;
case 0x00: /* BRK */ return 1; /* JSI */ case 0x60: tt = pc + 2; IMM(rst, tt >> 8) IMM(rst, tt) JMI break;
case 0x20: /* JCI */ t=T; SHIFT(-1) if(!t) { pc += 2; break; } /* fall-through */ /* LI2 */ case 0xa0: IMM(wst, uxn.ram[pc++])
case 0x40: /* JMI */ rr = ram + pc; pc += 2 + PEEK2(rr); break; /* LIT */ case 0x80: IMM(wst, uxn.ram[pc++]) break;
case 0x60: /* JSI */ SHIFT( 2) rr = ram + pc; pc += 2; T2_(pc); pc += PEEK2(rr); break; /* L2r */ case 0xe0: IMM(rst, uxn.ram[pc++])
case 0x80: /* LIT */ case 0xc0: SHIFT( 1) T = ram[pc++]; break; /* LIr */ case 0xc0: IMM(rst, uxn.ram[pc++]) break;
case 0xa0: /* LIT2 */ case 0xe0: SHIFT( 2) N = ram[pc++]; T = ram[pc++]; break; /* INC */ OPC(0x01, POx(a) PUx(a + 1))
} break; /* POP */ OPC(0x02, POx(a))
/* ALU */ /* NIP */ OPC(0x03, POx(a) POx(b) PUx(a))
case 0x01: /* INC */ t=T; SET(1, 0) T = t + 1; break; /* SWP */ OPC(0x04, POx(a) POx(b) PUx(a) PUx(b))
case 0x21: /* INC2 */ t=T2; SET(2, 0) T2_(t + 1) break; /* ROT */ OPC(0x05, POx(a) POx(b) POx(c) PUx(b) PUx(a) PUx(c))
case 0x02: /* POP */ SET(1,-1) break; /* DUP */ OPC(0x06, POx(a) PUx(a) PUx(a))
case 0x22: /* POP2 */ SET(2,-2) break; /* OVR */ OPC(0x07, POx(a) POx(b) PUx(b) PUx(a) PUx(b))
case 0x03: /* NIP */ t=T; SET(2,-1) T = t; break; /* EQU */ OPC(0x08, POx(a) POx(b) PU1(b == a))
case 0x23: /* NIP2 */ t=T2; SET(4,-2) T2_(t) break; /* NEQ */ OPC(0x09, POx(a) POx(b) PU1(b != a))
case 0x04: /* SWP */ t=T;n=N; SET(2, 0) T = n; N = t; break; /* GTH */ OPC(0x0a, POx(a) POx(b) PU1(b > a))
case 0x24: /* SWP2 */ t=T2;n=N2; SET(4, 0) T2_(n) N2_(t) break; /* LTH */ OPC(0x0b, POx(a) POx(b) PU1(b < a))
case 0x05: /* ROT */ t=T;n=N;l=L; SET(3, 0) T = l; N = t; L = n; break; /* JMP */ OPC(0x0c, POx(a) JMP(a))
case 0x25: /* ROT2 */ t=T2;n=N2;l=L2; SET(6, 0) T2_(l) N2_(t) L2_(n) break; /* JCN */ OPC(0x0d, POx(a) PO1(b) if(b) JMP(a))
case 0x06: /* DUP */ t=T; SET(1, 1) T = t; N = t; break; /* JSR */ OPC(0x0e, POx(a) FLP PU2(pc) JMP(a))
case 0x26: /* DUP2 */ t=T2; SET(2, 2) T2_(t) N2_(t) break; /* STH */ OPC(0x0f, POx(a) FLP PUx(a))
case 0x07: /* OVR */ t=T;n=N; SET(2, 1) T = n; N = t; L = n; break; /* LDZ */ OPC(0x10, PO1(a) PEK(b, a, t) PUx(b))
case 0x27: /* OVR2 */ t=T2;n=N2; SET(4, 2) T2_(n) N2_(t) L2_(n) break; /* STZ */ OPC(0x11, PO1(a) POx(b) POK(a, b, t))
case 0x08: /* EQU */ t=T;n=N; SET(2,-1) T = n == t; break; /* LDR */ OPC(0x12, PO1(a) PEK(b, pc + (Sint8)a, tt) PUx(b))
case 0x28: /* EQU2 */ t=T2;n=N2; SET(4,-3) T = n == t; break; /* STR */ OPC(0x13, PO1(a) POx(b) POK(pc + (Sint8)a, b, tt))
case 0x09: /* NEQ */ t=T;n=N; SET(2,-1) T = n != t; break; /* LDA */ OPC(0x14, PO2(a) PEK(b, a, tt) PUx(b))
case 0x29: /* NEQ2 */ t=T2;n=N2; SET(4,-3) T = n != t; break; /* STA */ OPC(0x15, PO2(a) POx(b) POK(a, b, tt))
case 0x0a: /* GTH */ t=T;n=N; SET(2,-1) T = n > t; break; /* DEI */ OPC(0x16, PO1(a) DEI(b, a) PUx(b))
case 0x2a: /* GTH2 */ t=T2;n=N2; SET(4,-3) T = n > t; break; /* DEO */ OPC(0x17, PO1(a) POx(b) DEO(a, b))
case 0x0b: /* LTH */ t=T;n=N; SET(2,-1) T = n < t; break; /* ADD */ OPC(0x18, POx(a) POx(b) PUx(b + a))
case 0x2b: /* LTH2 */ t=T2;n=N2; SET(4,-3) T = n < t; break; /* SUB */ OPC(0x19, POx(a) POx(b) PUx(b - a))
case 0x0c: /* JMP */ t=T; SET(1,-1) pc += (Sint8)t; break; /* MUL */ OPC(0x1a, POx(a) POx(b) PUx(b * a))
case 0x2c: /* JMP2 */ t=T2; SET(2,-2) pc = t; break; /* DIV */ OPC(0x1b, POx(a) POx(b) PUx(a ? b / a : 0))
case 0x0d: /* JCN */ t=T;n=N; SET(2,-2) if(n) pc += (Sint8)t; break; /* AND */ OPC(0x1c, POx(a) POx(b) PUx(b & a))
case 0x2d: /* JCN2 */ t=T2;n=L; SET(3,-3) if(n) pc = t; break; /* ORA */ OPC(0x1d, POx(a) POx(b) PUx(b | a))
case 0x0e: /* JSR */ t=T; SET(1,-1) FLIP SHIFT(2) T2_(pc) pc += (Sint8)t; break; /* EOR */ OPC(0x1e, POx(a) POx(b) PUx(b ^ a))
case 0x2e: /* JSR2 */ t=T2; SET(2,-2) FLIP SHIFT(2) T2_(pc) pc = t; break; /* SFT */ OPC(0x1f, PO1(a) POx(b) PUx(b >> (a & 0xf) << (a >> 4)))
case 0x0f: /* STH */ t=T; SET(1,-1) FLIP SHIFT(1) T = t; break;
case 0x2f: /* STH2 */ t=T2; SET(2,-2) FLIP SHIFT(2) T2_(t) break;
case 0x10: /* LDZ */ t=T; SET(1, 0) T = ram[t]; break;
case 0x30: /* LDZ2 */ t=T; SET(1, 1) N = ram[t++]; T = ram[(Uint8)t]; break;
case 0x11: /* STZ */ t=T;n=N; SET(2,-2) ram[t] = n; break;
case 0x31: /* STZ2 */ t=T;n=H2; SET(3,-3) ram[t++] = n >> 8; ram[(Uint8)t] = n; break;
case 0x12: /* LDR */ t=T; SET(1, 0) r = pc + (Sint8)t; T = ram[r]; break;
case 0x32: /* LDR2 */ t=T; SET(1, 1) r = pc + (Sint8)t; N = ram[r++]; T = ram[r]; break;
case 0x13: /* STR */ t=T;n=N; SET(2,-2) r = pc + (Sint8)t; ram[r] = n; break;
case 0x33: /* STR2 */ t=T;n=H2; SET(3,-3) r = pc + (Sint8)t; ram[r++] = n >> 8; ram[r] = n; break;
case 0x14: /* LDA */ t=T2; SET(2,-1) T = ram[t]; break;
case 0x34: /* LDA2 */ t=T2; SET(2, 0) N = ram[t++]; T = ram[t]; break;
case 0x15: /* STA */ t=T2;n=L; SET(3,-3) ram[t] = n; break;
case 0x35: /* STA2 */ t=T2;n=N2; SET(4,-4) ram[t++] = n >> 8; ram[t] = n; break;
case 0x16: /* DEI */ t=T; SET(1, 0) T = emu_dei(u, t); break;
case 0x36: /* DEI2 */ t=T; SET(1, 1) N = emu_dei(u, t++); T = emu_dei(u, t); break;
case 0x17: /* DEO */ t=T;n=N; SET(2,-2) emu_deo(u, t, n); break;
case 0x37: /* DEO2 */ t=T;n=N;l=L; SET(3,-3) emu_deo(u, t++, l); emu_deo(u, t, n); break;
case 0x18: /* ADD */ t=T;n=N; SET(2,-1) T = n + t; break;
case 0x38: /* ADD2 */ t=T2;n=N2; SET(4,-2) T2_(n + t) break;
case 0x19: /* SUB */ t=T;n=N; SET(2,-1) T = n - t; break;
case 0x39: /* SUB2 */ t=T2;n=N2; SET(4,-2) T2_(n - t) break;
case 0x1a: /* MUL */ t=T;n=N; SET(2,-1) T = n * t; break;
case 0x3a: /* MUL2 */ t=T2;n=N2; SET(4,-2) T2_(n * t) break;
case 0x1b: /* DIV */ t=T;n=N; SET(2,-1) T = t ? n / t : 0; break;
case 0x3b: /* DIV2 */ t=T2;n=N2; SET(4,-2) T2_(t ? n / t : 0) break;
case 0x1c: /* AND */ t=T;n=N; SET(2,-1) T = n & t; break;
case 0x3c: /* AND2 */ t=T2;n=N2; SET(4,-2) T2_(n & t) break;
case 0x1d: /* ORA */ t=T;n=N; SET(2,-1) T = n | t; break;
case 0x3d: /* ORA2 */ t=T2;n=N2; SET(4,-2) T2_(n | t) break;
case 0x1e: /* EOR */ t=T;n=N; SET(2,-1) T = n ^ t; break;
case 0x3e: /* EOR2 */ t=T2;n=N2; SET(4,-2) T2_(n ^ t) break;
case 0x1f: /* SFT */ t=T;n=N; SET(2,-1) T = n >> (t & 0xf) << (t >> 4); break;
case 0x3f: /* SFT2 */ t=T;n=H2; SET(3,-1) T2_(n >> (t & 0xf) << (t >> 4)) break;
} }
} }
} }

View file

@ -29,15 +29,16 @@ typedef struct {
} Stack; } Stack;
typedef struct Uxn { typedef struct Uxn {
Uint8 *ram, *dev; Uint8 *ram, dev[0x100];
Stack wst, rst; Stack wst, rst;
} Uxn; } Uxn;
/* required functions */ /* required functions */
extern Uint8 emu_dei(Uxn *u, Uint8 addr); extern Uint8 emu_dei(Uint8 addr);
extern void emu_deo(Uxn *u, Uint8 addr, Uint8 value); extern void emu_deo(Uint8 addr, Uint8 value);
extern Uxn uxn;
/* built-ins */ /* built-ins */
int uxn_eval(Uxn *u, Uint16 pc); int uxn_eval(Uint16 pc);

82
src/uxncli.c Executable file → Normal file
View file

@ -8,7 +8,7 @@
#include "devices/datetime.h" #include "devices/datetime.h"
/* /*
Copyright (c) 2021-2024 Devine Lu Linvega, Andrew Alderwick Copyright (c) 2021-2023 Devine Lu Linvega, 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
@ -18,55 +18,65 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE. WITH REGARD TO THIS SOFTWARE.
*/ */
Uxn uxn;
Uint8 Uint8
emu_dei(Uxn *u, Uint8 addr) emu_dei(Uint8 addr)
{ {
switch(addr & 0xf0) { switch(addr & 0xf0) {
case 0x00: return system_dei(u, addr); case 0x00: return system_dei(addr);
case 0xc0: return datetime_dei(u, addr); case 0xc0: return datetime_dei(addr);
} }
return u->dev[addr]; return uxn.dev[addr];
} }
void void
emu_deo(Uxn *u, Uint8 addr, Uint8 value) emu_deo(Uint8 addr, Uint8 value)
{ {
Uint8 p = addr & 0x0f, d = addr & 0xf0; uxn.dev[addr] = value;
u->dev[addr] = value; switch(addr & 0xf0) {
switch(d) { case 0x00: system_deo(addr); break;
case 0x00: system_deo(u, &u->dev[d], p); break; case 0x10: console_deo(addr); break;
case 0x10: console_deo(&u->dev[d], p); break; case 0xa0: file_deo(addr); break;
case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break; case 0xb0: file_deo(addr); break;
case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break;
} }
} }
static void
emu_run(void)
{
while(!uxn.dev[0x0f]) {
int c = fgetc(stdin);
if(c == EOF) {
console_input(0x00, CONSOLE_END);
break;
}
console_input(c, CONSOLE_STD);
}
}
static int
emu_end(void)
{
free(uxn.ram);
return uxn.dev[0x0f] & 0x7f;
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int i = 1; int i = 1;
Uxn u = {0}; char *rom;
Uint8 dev[0x100] = {0}; if(i != argc && argv[i][0] == '-' && argv[i][1] == 'v') {
u.dev = dev; fprintf(stdout, "Uxncli - Console Varvara Emulator, 15 Jul 2024.\n");
if(i == argc) i++;
return system_error("usage:", "uxncli [-v] file.rom [args..]");
if(argv[i][0] == '-' && argv[i][1] == 'v')
return system_error("Uxncli - Varvara Emulator(CLI)", "18 Mar 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, PAGE_PROGRAM) && PEEK2(u.dev + 0x10)) {
console_listen(&u, i, argc, argv);
while(!u.dev[0x0f]) {
int c = fgetc(stdin);
if(c == EOF) {
console_input(&u, 0x00, CONSOLE_END);
break;
} }
console_input(&u, (Uint8)c, CONSOLE_STD); rom = i == argc ? "boot.rom" : argv[i++];
} if(!system_boot((Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), rom))
} return !fprintf(stdout, "usage: %s [-v] file.rom [args..]\n", argv[0]);
free(u.ram); /* Event Loop */
return u.dev[0x0f] & 0x7f; uxn.dev[0x17] = argc - i;
if(uxn_eval(PAGE_PROGRAM) && PEEK2(uxn.dev + 0x10))
console_listen(i, argc, argv), emu_run();
return emu_end();
} }

View file

@ -46,6 +46,8 @@ WITH REGARD TO THIS SOFTWARE.
#define HEIGHT 40 * 8 #define HEIGHT 40 * 8
#define TIMEOUT_MS 334 #define TIMEOUT_MS 334
Uxn uxn, uxn_audio;
static SDL_Window *emu_window; static SDL_Window *emu_window;
static SDL_Texture *emu_texture; static SDL_Texture *emu_texture;
static SDL_Renderer *emu_renderer; static SDL_Renderer *emu_renderer;
@ -60,10 +62,9 @@ static Uint32 stdin_event, audio0_event, zoom = 1;
static Uint64 exec_deadline, deadline_interval, ms_interval; static Uint64 exec_deadline, deadline_interval, ms_interval;
static int static int
clamp(int v, int min, int max) clamp(int val, int min, int max)
{ {
return v < min ? min : v > max ? max return (val >= min) ? (val <= max) ? val : max : min;
: v;
} }
static void static void
@ -79,39 +80,39 @@ audio_deo(int instance, Uint8 *d, Uint8 port, Uxn *u)
} }
Uint8 Uint8
emu_dei(Uxn *u, Uint8 addr) emu_dei(Uint8 addr)
{ {
Uint8 p = addr & 0x0f, d = addr & 0xf0; Uint8 p = addr & 0x0f, d = addr & 0xf0;
switch(d) { switch(d) {
case 0x00: return system_dei(u, addr); case 0x00: return system_dei(addr);
case 0x20: return screen_dei(u, addr); case 0x20: return screen_dei(addr);
case 0x30: return audio_dei(0, &u->dev[d], p); case 0x30: return audio_dei(0, &uxn.dev[d], p);
case 0x40: return audio_dei(1, &u->dev[d], p); case 0x40: return audio_dei(1, &uxn.dev[d], p);
case 0x50: return audio_dei(2, &u->dev[d], p); case 0x50: return audio_dei(2, &uxn.dev[d], p);
case 0x60: return audio_dei(3, &u->dev[d], p); case 0x60: return audio_dei(3, &uxn.dev[d], p);
case 0xc0: return datetime_dei(u, addr); case 0xc0: return datetime_dei(addr);
} }
return u->dev[addr]; return uxn.dev[addr];
} }
void void
emu_deo(Uxn *u, Uint8 addr, Uint8 value) emu_deo(Uint8 addr, Uint8 value)
{ {
Uint8 p = addr & 0x0f, d = addr & 0xf0; Uint8 p = addr & 0x0f, d = addr & 0xf0;
u->dev[addr] = value; uxn.dev[addr] = value;
switch(d) { switch(d) {
case 0x00: case 0x00:
system_deo(u, &u->dev[d], p); system_deo(addr);
if(p > 0x7 && p < 0xe) screen_palette(&u->dev[0x8]); if(p > 0x7 && p < 0xe) screen_palette();
break; break;
case 0x10: console_deo(&u->dev[d], p); break; case 0x10: console_deo(addr); break;
case 0x20: screen_deo(u->ram, &u->dev[0x20], p); break; case 0x20: screen_deo(addr); break;
case 0x30: audio_deo(0, &u->dev[d], p, u); break; case 0x30: audio_deo(0, &uxn.dev[d], p, &uxn); break;
case 0x40: audio_deo(1, &u->dev[d], p, u); break; case 0x40: audio_deo(1, &uxn.dev[d], p, &uxn); break;
case 0x50: audio_deo(2, &u->dev[d], p, u); break; case 0x50: audio_deo(2, &uxn.dev[d], p, &uxn); break;
case 0x60: audio_deo(3, &u->dev[d], p, u); break; case 0x60: audio_deo(3, &uxn.dev[d], p, &uxn); break;
case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break; case 0xa0: file_deo(addr); break;
case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break; case 0xb0: file_deo(addr); break;
} }
} }
@ -177,11 +178,11 @@ set_borderless(int value)
} }
static void static void
set_debugger(Uxn *u, int value) set_debugger(int value)
{ {
u->dev[0x0e] = value; uxn.dev[0x0e] = value;
screen_fill(uxn_screen.fg, 0); screen_fill(uxn_screen.fg, 0);
screen_redraw(u); screen_redraw();
} }
/* emulator primitives */ /* emulator primitives */
@ -208,9 +209,9 @@ emu_resize(int width, int height)
} }
static void static void
emu_redraw(Uxn *u) emu_redraw(void)
{ {
screen_redraw(u); screen_redraw();
if(SDL_UpdateTexture(emu_texture, NULL, uxn_screen.pixels, uxn_screen.width * sizeof(Uint32)) != 0) if(SDL_UpdateTexture(emu_texture, NULL, uxn_screen.pixels, uxn_screen.width * sizeof(Uint32)) != 0)
system_error("SDL_UpdateTexture", SDL_GetError()); system_error("SDL_UpdateTexture", SDL_GetError());
SDL_RenderClear(emu_renderer); SDL_RenderClear(emu_renderer);
@ -219,7 +220,7 @@ emu_redraw(Uxn *u)
} }
static int static int
emu_init(Uxn *u) emu_init(void)
{ {
SDL_AudioSpec as; SDL_AudioSpec as;
SDL_zero(as); SDL_zero(as);
@ -228,7 +229,7 @@ emu_init(Uxn *u)
as.channels = 2; as.channels = 2;
as.callback = audio_handler; as.callback = audio_handler;
as.samples = AUDIO_BUFSIZE; as.samples = AUDIO_BUFSIZE;
as.userdata = u; as.userdata = &uxn_audio;
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
return system_error("sdl", SDL_GetError()); return system_error("sdl", SDL_GetError());
audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0); audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
@ -246,52 +247,21 @@ emu_init(Uxn *u)
ms_interval = SDL_GetPerformanceFrequency() / 1000; ms_interval = SDL_GetPerformanceFrequency() / 1000;
deadline_interval = ms_interval * TIMEOUT_MS; deadline_interval = ms_interval * TIMEOUT_MS;
exec_deadline = SDL_GetPerformanceCounter() + deadline_interval; exec_deadline = SDL_GetPerformanceCounter() + deadline_interval;
screen_resize(WIDTH, HEIGHT); screen_resize(WIDTH, HEIGHT, 1);
SDL_PauseAudioDevice(audio_id, 1); SDL_PauseAudioDevice(audio_id, 1);
return 1; return 1;
} }
static void static void
emu_restart(Uxn *u, char *rom, int soft) emu_restart(char *rom, int soft)
{ {
screen_resize(WIDTH, HEIGHT); screen_resize(WIDTH, HEIGHT, 1);
screen_fill(uxn_screen.bg, 0); screen_fill(uxn_screen.bg, 0);
screen_fill(uxn_screen.fg, 0); screen_fill(uxn_screen.fg, 0);
system_reboot(u, rom, soft); system_reboot(rom, soft);
SDL_SetWindowTitle(emu_window, boot_rom); SDL_SetWindowTitle(emu_window, boot_rom);
} }
static void
capture_screen(void)
{
const Uint32 format = SDL_PIXELFORMAT_RGB24;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
/* SDL_PIXELFORMAT_RGB24 */
Uint32 Rmask = 0x000000FF;
Uint32 Gmask = 0x0000FF00;
Uint32 Bmask = 0x00FF0000;
#else
/* SDL_PIXELFORMAT_BGR24 */
Uint32 Rmask = 0x00FF0000;
Uint32 Gmask = 0x0000FF00;
Uint32 Bmask = 0x000000FF;
#endif
time_t t = time(NULL);
char fname[64];
int w, h;
SDL_Surface *surface;
SDL_GetRendererOutputSize(emu_renderer, &w, &h);
if((surface = SDL_CreateRGBSurface(0, w, h, 24, Rmask, Gmask, Bmask, 0)) == NULL)
return;
SDL_RenderReadPixels(emu_renderer, NULL, format, surface->pixels, surface->pitch);
strftime(fname, sizeof(fname), "screenshot-%Y%m%d-%H%M%S.bmp", localtime(&t));
if(SDL_SaveBMP(surface, fname) == 0) {
fprintf(stderr, "Saved %s\n", fname);
fflush(stderr);
}
SDL_FreeSurface(surface);
}
static Uint8 static Uint8
get_button(SDL_Event *event) get_button(SDL_Event *event)
{ {
@ -341,7 +311,7 @@ get_key(SDL_Event *event)
} }
static int static int
handle_events(Uxn *u) handle_events(void)
{ {
SDL_Event event; SDL_Event event;
while(SDL_PollEvent(&event)) { while(SDL_PollEvent(&event)) {
@ -349,41 +319,41 @@ handle_events(Uxn *u)
if(event.type == SDL_QUIT) if(event.type == SDL_QUIT)
return 0; return 0;
else if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED) else if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED)
emu_redraw(u); emu_redraw();
else if(event.type == SDL_DROPFILE) { else if(event.type == SDL_DROPFILE) {
emu_restart(u, event.drop.file, 0); emu_restart(event.drop.file, 0);
SDL_free(event.drop.file); SDL_free(event.drop.file);
} }
/* Mouse */ /* Mouse */
else if(event.type == SDL_MOUSEMOTION) else if(event.type == SDL_MOUSEMOTION)
mouse_pos(u, &u->dev[0x90], clamp(event.motion.x - PAD, 0, uxn_screen.width - 1), clamp(event.motion.y - PAD, 0, uxn_screen.height - 1)); mouse_pos(clamp(event.motion.x - PAD, 0, uxn_screen.width - 1), clamp(event.motion.y - PAD, 0, uxn_screen.height - 1));
else if(event.type == SDL_MOUSEBUTTONUP) else if(event.type == SDL_MOUSEBUTTONUP)
mouse_up(u, &u->dev[0x90], SDL_BUTTON(event.button.button)); mouse_up(SDL_BUTTON(event.button.button));
else if(event.type == SDL_MOUSEBUTTONDOWN) else if(event.type == SDL_MOUSEBUTTONDOWN)
mouse_down(u, &u->dev[0x90], SDL_BUTTON(event.button.button)); mouse_down(SDL_BUTTON(event.button.button));
else if(event.type == SDL_MOUSEWHEEL) else if(event.type == SDL_MOUSEWHEEL)
mouse_scroll(u, &u->dev[0x90], event.wheel.x, event.wheel.y); mouse_scroll(event.wheel.x, event.wheel.y);
/* Controller */ /* Controller */
else if(event.type == SDL_TEXTINPUT) { else if(event.type == SDL_TEXTINPUT) {
char *c; char *c;
for(c = event.text.text; *c; c++) for(c = event.text.text; *c; c++)
controller_key(u, &u->dev[0x80], *c); controller_key(*c);
} else if(event.type == SDL_KEYDOWN) { } else if(event.type == SDL_KEYDOWN) {
int ksym; int ksym;
if(get_key(&event)) if(get_key(&event))
controller_key(u, &u->dev[0x80], get_key(&event)); controller_key(get_key(&event));
else if(get_button(&event)) else if(get_button(&event))
controller_down(u, &u->dev[0x80], get_button(&event)); controller_down(get_button(&event));
else if(event.key.keysym.sym == SDLK_F1) else if(event.key.keysym.sym == SDLK_F1)
set_zoom(zoom == 3 ? 1 : zoom + 1, 1); set_zoom(zoom == 3 ? 1 : zoom + 1, 1);
else if(event.key.keysym.sym == SDLK_F2) else if(event.key.keysym.sym == SDLK_F2)
set_debugger(u, !u->dev[0x0e]); set_debugger(!uxn.dev[0x0e]);
else if(event.key.keysym.sym == SDLK_F3) else if(event.key.keysym.sym == SDLK_F3)
capture_screen(); uxn.dev[0x0f] = 0xff;
else if(event.key.keysym.sym == SDLK_F4) else if(event.key.keysym.sym == SDLK_F4)
emu_restart(u, boot_rom, 0); emu_restart(boot_rom, 0);
else if(event.key.keysym.sym == SDLK_F5) else if(event.key.keysym.sym == SDLK_F5)
emu_restart(u, boot_rom, 1); emu_restart(boot_rom, 1);
else if(event.key.keysym.sym == SDLK_F11) else if(event.key.keysym.sym == SDLK_F11)
set_fullscreen(!fullscreen, 1); set_fullscreen(!fullscreen, 1);
else if(event.key.keysym.sym == SDLK_F12) else if(event.key.keysym.sym == SDLK_F12)
@ -392,44 +362,43 @@ handle_events(Uxn *u)
if(SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYUP, SDL_KEYUP) == 1 && ksym == event.key.keysym.sym) if(SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYUP, SDL_KEYUP) == 1 && ksym == event.key.keysym.sym)
return 1; return 1;
} else if(event.type == SDL_KEYUP) } else if(event.type == SDL_KEYUP)
controller_up(u, &u->dev[0x80], get_button(&event)); controller_up(get_button(&event));
else if(event.type == SDL_JOYAXISMOTION) { else if(event.type == SDL_JOYAXISMOTION) {
Uint8 vec = get_vector_joystick(&event); Uint8 vec = get_vector_joystick(&event);
if(!vec) if(!vec)
controller_up(u, &u->dev[0x80], (3 << (!event.jaxis.axis * 2)) << 4); controller_up((3 << (!event.jaxis.axis * 2)) << 4);
else else
controller_down(u, &u->dev[0x80], (1 << ((vec + !event.jaxis.axis * 2) - 1)) << 4); controller_down((1 << ((vec + !event.jaxis.axis * 2) - 1)) << 4);
} else if(event.type == SDL_JOYBUTTONDOWN) } else if(event.type == SDL_JOYBUTTONDOWN)
controller_down(u, &u->dev[0x80], get_button_joystick(&event)); controller_down(get_button_joystick(&event));
else if(event.type == SDL_JOYBUTTONUP) else if(event.type == SDL_JOYBUTTONUP)
controller_up(u, &u->dev[0x80], get_button_joystick(&event)); controller_up(get_button_joystick(&event));
else if(event.type == SDL_JOYHATMOTION) { else if(event.type == SDL_JOYHATMOTION) {
/* NOTE: Assuming there is only one joyhat in the controller */ /* NOTE: Assuming there is only one joyhat in the controller */
switch(event.jhat.value) { switch(event.jhat.value) {
case SDL_HAT_UP: controller_down(u, &u->dev[0x80], 0x10); break; case SDL_HAT_UP: controller_down(0x10); break;
case SDL_HAT_DOWN: controller_down(u, &u->dev[0x80], 0x20); break; case SDL_HAT_DOWN: controller_down(0x20); break;
case SDL_HAT_LEFT: controller_down(u, &u->dev[0x80], 0x40); break; case SDL_HAT_LEFT: controller_down(0x40); break;
case SDL_HAT_RIGHT: controller_down(u, &u->dev[0x80], 0x80); break; case SDL_HAT_RIGHT: controller_down(0x80); break;
case SDL_HAT_LEFTDOWN: controller_down(u, &u->dev[0x80], 0x40 | 0x20); break; case SDL_HAT_LEFTDOWN: controller_down(0x40 | 0x20); break;
case SDL_HAT_LEFTUP: controller_down(u, &u->dev[0x80], 0x40 | 0x10); break; case SDL_HAT_LEFTUP: controller_down(0x40 | 0x10); break;
case SDL_HAT_RIGHTDOWN: controller_down(u, &u->dev[0x80], 0x80 | 0x20); break; case SDL_HAT_RIGHTDOWN: controller_down(0x80 | 0x20); break;
case SDL_HAT_RIGHTUP: controller_down(u, &u->dev[0x80], 0x80 | 0x10); break; case SDL_HAT_RIGHTUP: controller_down(0x80 | 0x10); break;
case SDL_HAT_CENTERED: controller_up(u, &u->dev[0x80], 0x10 | 0x20 | 0x40 | 0x80); break; case SDL_HAT_CENTERED: controller_up(0x10 | 0x20 | 0x40 | 0x80); break;
} }
} }
/* Console */ /* Console */
else if(event.type == stdin_event) else if(event.type == stdin_event)
console_input(u, event.cbutton.button, CONSOLE_STD); console_input(event.cbutton.button, CONSOLE_STD);
} }
return 1; return 1;
} }
static int static int
emu_run(Uxn *u, char *rom) emu_run(char *rom)
{ {
Uint64 next_refresh = 0; Uint64 next_refresh = 0;
Uint64 frame_interval = SDL_GetPerformanceFrequency() / 60; Uint64 frame_interval = SDL_GetPerformanceFrequency() / 60;
Uint8 *vector_addr = &u->dev[0x20];
Uint32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI; Uint32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI;
window_created = 1; window_created = 1;
if(fullscreen) if(fullscreen)
@ -451,18 +420,18 @@ emu_run(Uxn *u, char *rom)
Uint16 screen_vector; Uint16 screen_vector;
Uint64 now = SDL_GetPerformanceCounter(); Uint64 now = SDL_GetPerformanceCounter();
/* .System/halt */ /* .System/halt */
if(u->dev[0x0f]) if(uxn.dev[0x0f])
return system_error("Run", "Ended."); return system_error("Run", "Ended.");
exec_deadline = now + deadline_interval; exec_deadline = now + deadline_interval;
if(!handle_events(u)) if(!handle_events())
return 0; return 0;
screen_vector = PEEK2(vector_addr); screen_vector = uxn.dev[0x20] << 8 | uxn.dev[0x21];
if(now >= next_refresh) { if(now >= next_refresh) {
now = SDL_GetPerformanceCounter(); now = SDL_GetPerformanceCounter();
next_refresh = now + frame_interval; next_refresh = now + frame_interval;
uxn_eval(u, screen_vector); uxn_eval(screen_vector);
if(uxn_screen.x2) if(uxn_screen.x2)
emu_redraw(u); emu_redraw();
} }
if(screen_vector || uxn_screen.x2) { if(screen_vector || uxn_screen.x2) {
Uint64 delay_ms = (next_refresh - now) / ms_interval; Uint64 delay_ms = (next_refresh - now) / ms_interval;
@ -473,7 +442,7 @@ emu_run(Uxn *u, char *rom)
} }
static int static int
emu_end(Uxn *u) emu_end(void)
{ {
SDL_CloseAudioDevice(audio_id); SDL_CloseAudioDevice(audio_id);
#ifdef _WIN32 #ifdef _WIN32
@ -483,21 +452,15 @@ emu_end(Uxn *u)
close(0); /* make stdin thread exit */ close(0); /* make stdin thread exit */
#endif #endif
SDL_Quit(); SDL_Quit();
free(u->ram); free(uxn.ram);
return u->dev[0x0f] & 0x7f; return uxn.dev[0x0f] & 0x7f;
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int i = 1; int i = 1;
Uint8 *ram;
char *rom; char *rom;
Uxn u = {0};
Uint8 dev[0x100] = {0};
Uxn u_audio = {0};
u.dev = dev;
u_audio.dev = dev;
/* flags */ /* flags */
if(argc > 1 && argv[i][0] == '-') { if(argc > 1 && argv[i][0] == '-') {
if(!strcmp(argv[i], "-v")) if(!strcmp(argv[i], "-v"))
@ -512,16 +475,15 @@ main(int argc, char **argv)
} }
/* start */ /* start */
rom = i == argc ? "boot.rom" : argv[i++]; rom = i == argc ? "boot.rom" : argv[i++];
ram = (Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)); if(!system_boot((Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), rom))
if(!system_init(&u, ram, rom) || !system_init(&u_audio, ram, rom))
return system_error("usage:", "uxnemu [-v | -f | -2x | -3x] file.rom [args...]"); return system_error("usage:", "uxnemu [-v | -f | -2x | -3x] file.rom [args...]");
if(!emu_init(&u_audio)) if(!emu_init())
return system_error("Init", "Failed to initialize varvara."); return system_error("Init", "Failed to initialize varvara.");
/* loop */ /* loop */
u.dev[0x17] = argc - i; uxn.dev[0x17] = argc - i;
if(uxn_eval(&u, PAGE_PROGRAM)) { if(uxn_eval(PAGE_PROGRAM)) {
console_listen(&u, i, argc, argv); console_listen(i, argc, argv);
emu_run(&u, boot_rom); emu_run(boot_rom);
} }
return emu_end(&u); return emu_end();
} }