commit ac45af6

neauoire  ·  2022-03-27 01:32:46 +0000 UTC
parent b88531d
Updated screen device to latest specs
25 files changed,  +1027, -1415
D uxn.c
+6, -0
 1@@ -4,6 +4,12 @@ An assembler and emulator for the [Uxn stack-machine](https://wiki.xxiivv.com/si
 2 
 3 More docs coming soon.
 4 
 5+If you wish to build the emulator without graphics mode:
 6+
 7+```sh
 8+cc src/devices/datetime.c src/devices/system.c src/devices/file.c src/uxn.c -DNDEBUG -Os -g0 -s src/uxncli.c -o bin/uxncli
 9+```
10+
11 ## Contributing
12 
13 Submit patches using [`git send-email`](https://git-send-email.io/) to the [~rabbits/public-inbox mailing list](https://lists.sr.ht/~rabbits/public-inbox).
+2, -2
 1@@ -1,13 +1,13 @@
 2 #!/bin/sh -e
 3 
 4-clang-format -i uxn11.c
 5+clang-format -i src/uxn11.c
 6 
 7 echo "Cleaning.."
 8 rm -f ./bin/*
 9 
10 echo "Building.."
11 mkdir -p bin
12-gcc uxn.c devices/ppu.c uxn11.c -o bin/uxn11 -lX11
13+gcc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/devices/system.c src/devices/screen.c src/uxn11.c -o bin/uxn11 -lX11
14 
15 echo "Running.."
16 bin/uxn11 etc/screen.rom
+0, -139
  1@@ -1,139 +0,0 @@
  2-#include "../uxn.h"
  3-#include "file.h"
  4-
  5-/*
  6-Copyright (c) 2021 Devine Lu Linvega
  7-Copyright (c) 2021 Andrew Alderwick
  8-
  9-Permission to use, copy, modify, and distribute this software for any
 10-purpose with or without fee is hereby granted, provided that the above
 11-copyright notice and this permission notice appear in all copies.
 12-
 13-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 14-WITH REGARD TO THIS SOFTWARE.
 15-*/
 16-
 17-#define _POSIX_C_SOURCE 200809L
 18-
 19-#include <stdio.h>
 20-#include <dirent.h>
 21-#include <string.h>
 22-#include <sys/stat.h>
 23-#include <unistd.h>
 24-
 25-static FILE *f;
 26-static DIR *d;
 27-static char *current_filename = "";
 28-static enum { IDLE,
 29-	FILE_READ,
 30-	FILE_WRITE,
 31-	DIR_READ } state;
 32-static struct dirent *de;
 33-
 34-static void
 35-reset(void)
 36-{
 37-	if(f != NULL) {
 38-		fclose(f);
 39-		f = NULL;
 40-	}
 41-	if(d != NULL) {
 42-		closedir(d);
 43-		d = NULL;
 44-	}
 45-	de = NULL;
 46-	state = IDLE;
 47-}
 48-
 49-static Uint16
 50-get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
 51-{
 52-	struct stat st;
 53-	if(len < strlen(basename) + 7)
 54-		return 0;
 55-	if(stat(pathname, &st))
 56-		return fail_nonzero ? snprintf(p, len, "!!!! %s\n", basename) : 0;
 57-	else if(S_ISDIR(st.st_mode))
 58-		return snprintf(p, len, "---- %s\n", basename);
 59-	else if(st.st_size < 0x10000)
 60-		return snprintf(p, len, "%04x %s\n", (Uint16)st.st_size, basename);
 61-	else
 62-		return snprintf(p, len, "???? %s\n", basename);
 63-}
 64-
 65-static Uint16
 66-file_read_dir(char *dest, Uint16 len)
 67-{
 68-	static char pathname[4096];
 69-	char *p = dest;
 70-	if(de == NULL) de = readdir(d);
 71-	for(; de != NULL; de = readdir(d)) {
 72-		Uint16 n;
 73-		if(de->d_name[0] == '.' && de->d_name[1] == '\0')
 74-			continue;
 75-		snprintf(pathname, sizeof(pathname), "%s/%s", current_filename, de->d_name);
 76-		n = get_entry(p, len, pathname, de->d_name, 1);
 77-		if(!n) break;
 78-		p += n;
 79-		len -= n;
 80-	}
 81-	return p - dest;
 82-}
 83-
 84-Uint16
 85-file_init(void *filename)
 86-{
 87-	reset();
 88-	current_filename = filename;
 89-	return 0;
 90-}
 91-
 92-Uint16
 93-file_read(void *dest, Uint16 len)
 94-{
 95-	if(state != FILE_READ && state != DIR_READ) {
 96-		reset();
 97-		if((d = opendir(current_filename)) != NULL)
 98-			state = DIR_READ;
 99-		else if((f = fopen(current_filename, "rb")) != NULL)
100-			state = FILE_READ;
101-	}
102-	if(state == FILE_READ)
103-		return fread(dest, 1, len, f);
104-	if(state == DIR_READ)
105-		return file_read_dir(dest, len);
106-	return 0;
107-}
108-
109-Uint16
110-file_write(void *src, Uint16 len, Uint8 flags)
111-{
112-	Uint16 ret = 0;
113-	if(state != FILE_WRITE) {
114-		reset();
115-		if((f = fopen(current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
116-			state = FILE_WRITE;
117-	}
118-	if(state == FILE_WRITE) {
119-		if((ret = fwrite(src, 1, len, f)) > 0 && fflush(f) != 0)
120-			ret = 0;
121-	}
122-	return ret;
123-}
124-
125-Uint16
126-file_stat(void *dest, Uint16 len)
127-{
128-	char *basename = strrchr(current_filename, '/');
129-	if(basename != NULL)
130-		++basename;
131-	else
132-		basename = current_filename;
133-	return get_entry(dest, len, current_filename, basename, 0);
134-}
135-
136-Uint16
137-file_delete(void)
138-{
139-	return unlink(current_filename);
140-}
+0, -37
 1@@ -1,37 +0,0 @@
 2-#ifdef __aarch64__
 3-#include <arm_neon.h>
 4-#include "ppu.h"
 5-
 6-void
 7-ppu_redraw(Ppu *p, Uint32 *screen)
 8-{
 9-	uint8x16x4_t pal = vld4q_u8((Uint8*)p->palette);
10-	Uint8 *fg = p->fg.pixels;
11-	Uint8 *bg = p->bg.pixels;
12-	int i;
13-
14-	p->fg.changed = p->bg.changed = 0;
15-
16-#ifdef __has_builtin
17-#if __has_builtin(__builtin_assume)
18-	__builtin_assume(p->width > 0 && p->height > 0);
19-#endif
20-#endif
21-
22-	for(i = 0; i < (p->width * p->height & ~15); i += 16, fg += 16, bg += 16, screen += 16) {
23-		uint8x16_t fg8 = vld1q_u8(fg);
24-		uint8x16_t bg8 = vld1q_u8(bg);
25-		uint8x16_t px8 = vbslq_u8(vceqzq_u8(fg8), bg8, fg8);
26-		uint8x16x4_t px = {
27-			vqtbl1q_u8(pal.val[0], px8),
28-			vqtbl1q_u8(pal.val[1], px8),
29-			vqtbl1q_u8(pal.val[2], px8),
30-			vdupq_n_u8(0xff),
31-		};
32-		vst4q_u8((uint8_t*)screen, px);
33-	}
34-
35-	for(; i < p->width * p->height; i++)
36-		screen[i] = p->palette[*fg ? *fg : *bg];
37-}
38-#endif
+0, -0
R devices/apu.c => src/devices/apu.c
+0, -0
R devices/apu.h => src/devices/apu.h
+0, -0
+40, -0
 1@@ -0,0 +1,40 @@
 2+#include <time.h>
 3+
 4+#include "../uxn.h"
 5+#include "datetime.h"
 6+
 7+/*
 8+Copyright (c) 2021 Devine Lu Linvega
 9+Copyright (c) 2021 Andrew Alderwick
10+
11+Permission to use, copy, modify, and distribute this software for any
12+purpose with or without fee is hereby granted, provided that the above
13+copyright notice and this permission notice appear in all copies.
14+
15+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16+WITH REGARD TO THIS SOFTWARE.
17+*/
18+
19+Uint8
20+datetime_dei(Device *d, Uint8 port)
21+{
22+	time_t seconds = time(NULL);
23+	struct tm zt = {0};
24+	struct tm *t = localtime(&seconds);
25+	if(t == NULL)
26+		t = &zt;
27+	switch(port) {
28+	case 0x0: return (t->tm_year + 1900) >> 8;
29+	case 0x1: return (t->tm_year + 1900);
30+	case 0x2: return t->tm_mon;
31+	case 0x3: return t->tm_mday;
32+	case 0x4: return t->tm_hour;
33+	case 0x5: return t->tm_min;
34+	case 0x6: return t->tm_sec;
35+	case 0x7: return t->tm_wday;
36+	case 0x8: return t->tm_yday >> 8;
37+	case 0x9: return t->tm_yday;
38+	case 0xa: return t->tm_isdst;
39+	default: return d->dat[port];
40+	}
41+}
+13, -0
 1@@ -0,0 +1,13 @@
 2+/*
 3+Copyright (c) 2021 Devine Lu Linvega
 4+Copyright (c) 2021 Andrew Alderwick
 5+
 6+Permission to use, copy, modify, and distribute this software for any
 7+purpose with or without fee is hereby granted, provided that the above
 8+copyright notice and this permission notice appear in all copies.
 9+
10+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+WITH REGARD TO THIS SOFTWARE.
12+*/
13+
14+Uint8 datetime_dei(Device *d, Uint8 port);
+211, -0
  1@@ -0,0 +1,211 @@
  2+#include "../uxn.h"
  3+#include "file.h"
  4+
  5+/*
  6+Copyright (c) 2021 Devine Lu Linvega
  7+Copyright (c) 2021 Andrew Alderwick
  8+
  9+Permission to use, copy, modify, and distribute this software for any
 10+purpose with or without fee is hereby granted, provided that the above
 11+copyright notice and this permission notice appear in all copies.
 12+
 13+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 14+WITH REGARD TO THIS SOFTWARE.
 15+*/
 16+
 17+#include <stdio.h>
 18+#include <dirent.h>
 19+#include <string.h>
 20+#include <sys/stat.h>
 21+#include <unistd.h>
 22+
 23+typedef struct {
 24+	FILE *f;
 25+	DIR *dir;
 26+	char current_filename[4096];
 27+	struct dirent *de;
 28+	enum { IDLE,
 29+		FILE_READ,
 30+		FILE_WRITE,
 31+		DIR_READ } state;
 32+} UxnFile;
 33+
 34+static UxnFile uxn_file[POLYFILEY];
 35+
 36+static void
 37+reset(UxnFile *c)
 38+{
 39+	if(c->f != NULL) {
 40+		fclose(c->f);
 41+		c->f = NULL;
 42+	}
 43+	if(c->dir != NULL) {
 44+		closedir(c->dir);
 45+		c->dir = NULL;
 46+	}
 47+	c->de = NULL;
 48+	c->state = IDLE;
 49+}
 50+
 51+static Uint16
 52+get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
 53+{
 54+	struct stat st;
 55+	if(len < strlen(basename) + 7)
 56+		return 0;
 57+	if(stat(pathname, &st))
 58+		return fail_nonzero ? sprintf(p, "!!!! %s\n", basename) : 0;
 59+	else if(S_ISDIR(st.st_mode))
 60+		return sprintf(p, "---- %s\n", basename);
 61+	else if(st.st_size < 0x10000)
 62+		return sprintf(p, "%04x %s\n", (unsigned int)st.st_size, basename);
 63+	else
 64+		return sprintf(p, "???? %s\n", basename);
 65+}
 66+
 67+static Uint16
 68+file_read_dir(UxnFile *c, char *dest, Uint16 len)
 69+{
 70+	static char pathname[4352];
 71+	char *p = dest;
 72+	if(c->de == NULL) c->de = readdir(c->dir);
 73+	for(; c->de != NULL; c->de = readdir(c->dir)) {
 74+		Uint16 n;
 75+		if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0')
 76+			continue;
 77+		if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname))
 78+			sprintf(pathname, "%s/%s", c->current_filename, c->de->d_name);
 79+		else
 80+			pathname[0] = '\0';
 81+		n = get_entry(p, len, pathname, c->de->d_name, 1);
 82+		if(!n) break;
 83+		p += n;
 84+		len -= n;
 85+	}
 86+	return p - dest;
 87+}
 88+
 89+static Uint16
 90+file_init(UxnFile *c, char *filename, size_t max_len)
 91+{
 92+	char *p = c->current_filename;
 93+	size_t len = sizeof(c->current_filename);
 94+	reset(c);
 95+	if(len > max_len) len = max_len;
 96+	while(len) {
 97+		if((*p++ = *filename++) == '\0')
 98+			return 0;
 99+		len--;
100+	}
101+	c->current_filename[0] = '\0';
102+	return 0;
103+}
104+
105+static Uint16
106+file_read(UxnFile *c, void *dest, Uint16 len)
107+{
108+	if(c->state != FILE_READ && c->state != DIR_READ) {
109+		reset(c);
110+		if((c->dir = opendir(c->current_filename)) != NULL)
111+			c->state = DIR_READ;
112+		else if((c->f = fopen(c->current_filename, "rb")) != NULL)
113+			c->state = FILE_READ;
114+	}
115+	if(c->state == FILE_READ)
116+		return fread(dest, 1, len, c->f);
117+	if(c->state == DIR_READ)
118+		return file_read_dir(c, dest, len);
119+	return 0;
120+}
121+
122+static Uint16
123+file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
124+{
125+	Uint16 ret = 0;
126+	if(c->state != FILE_WRITE) {
127+		reset(c);
128+		if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
129+			c->state = FILE_WRITE;
130+	}
131+	if(c->state == FILE_WRITE) {
132+		if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
133+			ret = 0;
134+	}
135+	return ret;
136+}
137+
138+static Uint16
139+file_stat(UxnFile *c, void *dest, Uint16 len)
140+{
141+	char *basename = strrchr(c->current_filename, '/');
142+	if(basename != NULL)
143+		basename++;
144+	else
145+		basename = c->current_filename;
146+	return get_entry(dest, len, c->current_filename, basename, 0);
147+}
148+
149+static Uint16
150+file_delete(UxnFile *c)
151+{
152+	return unlink(c->current_filename);
153+}
154+
155+/* IO */
156+
157+void
158+file_i_deo(int instance, Device *d, Uint8 port)
159+{
160+	UxnFile *c = &uxn_file[instance];
161+	Uint16 addr, len, res;
162+	switch(port) {
163+	case 0x5:
164+		DEVPEEK16(addr, 0x4);
165+		DEVPEEK16(len, 0xa);
166+		if(len > 0x10000 - addr)
167+			len = 0x10000 - addr;
168+		res = file_stat(c, &d->u->ram[addr], len);
169+		DEVPOKE16(0x2, res);
170+		break;
171+	case 0x6:
172+		res = file_delete(c);
173+		DEVPOKE16(0x2, res);
174+		break;
175+	case 0x9:
176+		DEVPEEK16(addr, 0x8);
177+		res = file_init(c, (char *)&d->u->ram[addr], 0x10000 - addr);
178+		DEVPOKE16(0x2, res);
179+		break;
180+	case 0xd:
181+		DEVPEEK16(addr, 0xc);
182+		DEVPEEK16(len, 0xa);
183+		if(len > 0x10000 - addr)
184+			len = 0x10000 - addr;
185+		res = file_read(c, &d->u->ram[addr], len);
186+		DEVPOKE16(0x2, res);
187+		break;
188+	case 0xf:
189+		DEVPEEK16(addr, 0xe);
190+		DEVPEEK16(len, 0xa);
191+		if(len > 0x10000 - addr)
192+			len = 0x10000 - addr;
193+		res = file_write(c, &d->u->ram[addr], len, d->dat[0x7]);
194+		DEVPOKE16(0x2, res);
195+		break;
196+	}
197+}
198+
199+Uint8
200+file_i_dei(int instance, Device *d, Uint8 port)
201+{
202+	UxnFile *c = &uxn_file[instance];
203+	Uint16 res;
204+	switch(port) {
205+	case 0xc:
206+	case 0xd:
207+		res = file_read(c, &d->dat[port], 1);
208+		DEVPOKE16(0x2, res);
209+		break;
210+	}
211+	return d->dat[port];
212+}
R devices/file.h => src/devices/file.h
+4, -5
 1@@ -10,8 +10,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 2 WITH REGARD TO THIS SOFTWARE.
 3 */
 4 
 5-Uint16 file_init(void *filename);
 6-Uint16 file_read(void *dest, Uint16 len);
 7-Uint16 file_write(void *src, Uint16 len, Uint8 flags);
 8-Uint16 file_stat(void *dest, Uint16 len);
 9-Uint16 file_delete(void);
10+#define POLYFILEY 2
11+
12+void file_i_deo(int instance, Device *d, Uint8 port);
13+Uint8 file_i_dei(int instance, Device *d, Uint8 port);
R devices/ppu.c => src/devices/ppu.c
+0, -0
R devices/ppu.h => src/devices/ppu.h
+0, -0
+181, -0
  1@@ -0,0 +1,181 @@
  2+#include <stdlib.h>
  3+
  4+#include "../uxn.h"
  5+#include "screen.h"
  6+
  7+/*
  8+Copyright (c) 2021 Devine Lu Linvega
  9+Copyright (c) 2021 Andrew Alderwick
 10+
 11+Permission to use, copy, modify, and distribute this software for any
 12+purpose with or without fee is hereby granted, provided that the above
 13+copyright notice and this permission notice appear in all copies.
 14+
 15+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 16+WITH REGARD TO THIS SOFTWARE.
 17+*/
 18+
 19+UxnScreen uxn_screen;
 20+
 21+static Uint8 blending[5][16] = {
 22+	{0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0},
 23+	{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
 24+	{1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1},
 25+	{2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2},
 26+	{1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}};
 27+
 28+static void
 29+screen_write(UxnScreen *p, Layer *layer, Uint16 x, Uint16 y, Uint8 color)
 30+{
 31+	if(x < p->width && y < p->height) {
 32+		Uint32 i = x + y * p->width;
 33+		if(color != layer->pixels[i]) {
 34+			layer->pixels[i] = color;
 35+			layer->changed = 1;
 36+		}
 37+	}
 38+}
 39+
 40+static void
 41+screen_blit(UxnScreen *p, Layer *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy, Uint8 twobpp)
 42+{
 43+	int v, h, opaque = blending[4][color];
 44+	for(v = 0; v < 8; v++) {
 45+		Uint16 c = sprite[v] | (twobpp ? sprite[v + 8] : 0) << 8;
 46+		for(h = 7; h >= 0; --h, c >>= 1) {
 47+			Uint8 ch = (c & 1) | ((c >> 7) & 2);
 48+			if(opaque || ch)
 49+				screen_write(p,
 50+					layer,
 51+					x + (flipx ? 7 - h : h),
 52+					y + (flipy ? 7 - v : v),
 53+					blending[ch][color]);
 54+		}
 55+	}
 56+}
 57+
 58+void
 59+screen_palette(UxnScreen *p, Uint8 *addr)
 60+{
 61+	int i, shift;
 62+	for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
 63+		Uint8
 64+			r = (addr[0 + i / 2] >> shift) & 0x0f,
 65+			g = (addr[2 + i / 2] >> shift) & 0x0f,
 66+			b = (addr[4 + i / 2] >> shift) & 0x0f;
 67+		p->palette[i] = 0x0f000000 | r << 16 | g << 8 | b;
 68+		p->palette[i] |= p->palette[i] << 4;
 69+	}
 70+	p->fg.changed = p->bg.changed = 1;
 71+}
 72+
 73+void
 74+screen_resize(UxnScreen *p, Uint16 width, Uint16 height)
 75+{
 76+	Uint8
 77+		*bg = realloc(p->bg.pixels, width * height),
 78+		*fg = realloc(p->fg.pixels, width * height);
 79+	Uint32
 80+		*pixels = realloc(p->pixels, width * height * sizeof(Uint32));
 81+	if(bg) p->bg.pixels = bg;
 82+	if(fg) p->fg.pixels = fg;
 83+	if(pixels) p->pixels = pixels;
 84+	if(bg && fg && pixels) {
 85+		p->width = width;
 86+		p->height = height;
 87+		screen_clear(p, &p->bg);
 88+		screen_clear(p, &p->fg);
 89+	}
 90+}
 91+
 92+void
 93+screen_clear(UxnScreen *p, Layer *layer)
 94+{
 95+	Uint32 i, size = p->width * p->height;
 96+	for(i = 0; i < size; i++)
 97+		layer->pixels[i] = 0x00;
 98+	layer->changed = 1;
 99+}
100+
101+void
102+screen_redraw(UxnScreen *p, Uint32 *pixels)
103+{
104+	Uint32 i, size = p->width * p->height, palette[16];
105+	for(i = 0; i < 16; i++)
106+		palette[i] = p->palette[(i >> 2) ? (i >> 2) : (i & 3)];
107+	for(i = 0; i < size; i++)
108+		pixels[i] = palette[p->fg.pixels[i] << 2 | p->bg.pixels[i]];
109+	p->fg.changed = p->bg.changed = 0;
110+}
111+
112+int
113+clamp(int val, int min, int max)
114+{
115+	return (val >= min) ? (val <= max) ? val : max : min;
116+}
117+
118+/* IO */
119+
120+Uint8
121+screen_dei(Device *d, Uint8 port)
122+{
123+	switch(port) {
124+	case 0x2: return uxn_screen.width >> 8;
125+	case 0x3: return uxn_screen.width;
126+	case 0x4: return uxn_screen.height >> 8;
127+	case 0x5: return uxn_screen.height;
128+	default: return d->dat[port];
129+	}
130+}
131+
132+void
133+screen_deo(Device *d, Uint8 port)
134+{
135+	switch(port) {
136+	case 0x3:
137+		if(!FIXED_SIZE) {
138+			Uint16 w;
139+			DEVPEEK16(w, 0x2);
140+			screen_resize(&uxn_screen, clamp(w, 1, 1024), uxn_screen.height);
141+		}
142+		break;
143+	case 0x5:
144+		if(!FIXED_SIZE) {
145+			Uint16 h;
146+			DEVPEEK16(h, 0x4);
147+			screen_resize(&uxn_screen, uxn_screen.width, clamp(h, 1, 1024));
148+		}
149+		break;
150+	case 0xe: {
151+		Uint16 x, y;
152+		Uint8 layer = d->dat[0xe] & 0x40;
153+		DEVPEEK16(x, 0x8);
154+		DEVPEEK16(y, 0xa);
155+		screen_write(&uxn_screen, layer ? &uxn_screen.fg : &uxn_screen.bg, x, y, d->dat[0xe] & 0x3);
156+		if(d->dat[0x6] & 0x01) DEVPOKE16(0x8, x + 1); /* auto x+1 */
157+		if(d->dat[0x6] & 0x02) DEVPOKE16(0xa, y + 1); /* auto y+1 */
158+		break;
159+	}
160+	case 0xf: {
161+		Uint16 x, y, dx, dy, addr;
162+		Uint8 i, n, twobpp = !!(d->dat[0xf] & 0x80);
163+		Layer *layer = (d->dat[0xf] & 0x40) ? &uxn_screen.fg : &uxn_screen.bg;
164+		DEVPEEK16(x, 0x8);
165+		DEVPEEK16(y, 0xa);
166+		DEVPEEK16(addr, 0xc);
167+		n = d->dat[0x6] >> 4;
168+		dx = (d->dat[0x6] & 0x01) << 3;
169+		dy = (d->dat[0x6] & 0x02) << 2;
170+		if(addr > 0x10000 - ((n + 1) << (3 + twobpp)))
171+			return;
172+		for(i = 0; i <= n; i++) {
173+			screen_blit(&uxn_screen, layer, x + dy * i, y + dx * i, &d->u->ram[addr], d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20, twobpp);
174+			addr += (d->dat[0x6] & 0x04) << (1 + twobpp);
175+		}
176+		DEVPOKE16(0xc, addr);   /* auto addr+length */
177+		DEVPOKE16(0x8, x + dx); /* auto x+8 */
178+		DEVPOKE16(0xa, y + dy); /* auto y+8 */
179+		break;
180+	}
181+	}
182+}
+34, -0
 1@@ -0,0 +1,34 @@
 2+/*
 3+Copyright (c) 2021 Devine Lu Linvega
 4+Copyright (c) 2021 Andrew Alderwick
 5+
 6+Permission to use, copy, modify, and distribute this software for any
 7+purpose with or without fee is hereby granted, provided that the above
 8+copyright notice and this permission notice appear in all copies.
 9+
10+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+WITH REGARD TO THIS SOFTWARE.
12+*/
13+
14+#define FIXED_SIZE 0
15+
16+typedef struct Layer {
17+	Uint8 *pixels, changed;
18+} Layer;
19+
20+typedef struct UxnScreen {
21+	Uint32 palette[4], *pixels;
22+	Uint16 width, height;
23+	Layer fg, bg;
24+} UxnScreen;
25+
26+extern UxnScreen uxn_screen;
27+
28+void screen_palette(UxnScreen *p, Uint8 *addr);
29+void screen_resize(UxnScreen *p, Uint16 width, Uint16 height);
30+void screen_clear(UxnScreen *p, Layer *layer);
31+void screen_redraw(UxnScreen *p, Uint32 *pixels);
32+
33+Uint8 screen_dei(Device *d, Uint8 port);
34+void screen_deo(Device *d, Uint8 port);
35+int clamp(int val, int min, int max);
+73, -0
 1@@ -0,0 +1,73 @@
 2+#include "../uxn.h"
 3+#include "system.h"
 4+
 5+#include <stdio.h>
 6+
 7+/*
 8+Copyright (c) 2022 Devine Lu Linvega, Andrew Alderwick
 9+
10+Permission to use, copy, modify, and distribute this software for any
11+purpose with or without fee is hereby granted, provided that the above
12+copyright notice and this permission notice appear in all copies.
13+
14+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15+WITH REGARD TO THIS SOFTWARE.
16+*/
17+
18+static const char *errors[] = {
19+	"Working-stack underflow",
20+	"Return-stack underflow",
21+	"Working-stack overflow",
22+	"Return-stack overflow",
23+	"Working-stack division by zero",
24+	"Return-stack division by zero"};
25+
26+static void
27+system_print(Stack *s, char *name)
28+{
29+	Uint8 i;
30+	fprintf(stderr, "<%s>", name);
31+	for(i = 0; i < s->ptr; i++)
32+		fprintf(stderr, " %02x", s->dat[i]);
33+	if(!i)
34+		fprintf(stderr, " empty");
35+	fprintf(stderr, "\n");
36+}
37+
38+void
39+system_inspect(Uxn *u)
40+{
41+	system_print(&u->wst, "wst");
42+	system_print(&u->rst, "rst");
43+}
44+
45+int
46+uxn_halt(Uxn *u, Uint8 error, Uint16 addr)
47+{
48+	system_inspect(u);
49+	fprintf(stderr, "Halted: %s#%04x, at 0x%04x\n", errors[error], u->ram[addr], addr);
50+	return 0;
51+}
52+
53+/* IO */
54+
55+Uint8
56+system_dei(Device *d, Uint8 port)
57+{
58+	switch(port) {
59+	case 0x2: return d->u->wst.ptr;
60+	case 0x3: return d->u->rst.ptr;
61+	default: return d->dat[port];
62+	}
63+}
64+
65+void
66+system_deo(Device *d, Uint8 port)
67+{
68+	switch(port) {
69+	case 0x2: d->u->wst.ptr = d->dat[port]; break;
70+	case 0x3: d->u->rst.ptr = d->dat[port]; break;
71+	case 0xe: system_inspect(d->u); break;
72+	default: system_deo_special(d, port);
73+	}
74+}
+21, -0
 1@@ -0,0 +1,21 @@
 2+/*
 3+Copyright (c) 2022 Devine Lu Linvega, Andrew Alderwick
 4+
 5+Permission to use, copy, modify, and distribute this software for any
 6+purpose with or without fee is hereby granted, provided that the above
 7+copyright notice and this permission notice appear in all copies.
 8+
 9+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+WITH REGARD TO THIS SOFTWARE.
11+*/
12+
13+typedef struct SystemDevice {
14+	Device device;
15+	struct UxnScreen *screen;
16+} SystemDevice;
17+
18+void system_inspect(Uxn *u);
19+
20+Uint8 system_dei(Device *d, Uint8 port);
21+void system_deo(Device *d, Uint8 port);
22+void system_deo_special(Device *d, Uint8 port);
+124, -0
  1@@ -0,0 +1,124 @@
  2+#include "uxn.h"
  3+
  4+/*
  5+Copyright (u) 2022 Devine Lu Linvega, Andrew Alderwick, Andrew Richards
  6+
  7+Permission to use, copy, modify, and distribute this software for any
  8+purpose with or without fee is hereby granted, provided that the above
  9+copyright notice and this permission notice appear in all copies.
 10+
 11+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 12+WITH REGARD TO THIS SOFTWARE.
 13+*/
 14+
 15+/* clang-format off */
 16+
 17+#define PUSH8(s, x) { if(s->ptr == 0xff) { errcode = 2; goto err; } s->dat[s->ptr++] = (x); }
 18+#define PUSH16(s, x) { if((j = s->ptr) >= 0xfe) { errcode = 2; goto err; } k = (x); s->dat[j] = k >> 8; s->dat[j + 1] = k; s->ptr = j + 2; }
 19+#define PUSH(s, x) { if(bs) { PUSH16(s, (x)) } else { PUSH8(s, (x)) } }
 20+#define POP8(o) { if(!(j = *sp)) { errcode = 0; goto err; } o = (Uint16)src->dat[--j]; *sp = j; }
 21+#define POP16(o) { if((j = *sp) <= 1) { errcode = 0; goto err; } o = src->dat[j - 1]; o += src->dat[j - 2] << 8; *sp = j - 2; }
 22+#define POP(o) { if(bs) { POP16(o) } else { POP8(o) } }
 23+#define POKE(x, y) { if(bs) { u->ram[(x)] = (y) >> 8; u->ram[(x) + 1] = (y); } else { u->ram[(x)] = y; } }
 24+#define PEEK16(o, x) { o = (u->ram[(x)] << 8) + u->ram[(x) + 1]; }
 25+#define PEEK(o, x) { if(bs) { PEEK16(o, x) } else { o = u->ram[(x)]; } }
 26+#define DEVR(o, d, x) { dev = (d); o = dev->dei(dev, (x) & 0x0f); if(bs) { o = (o << 8) + dev->dei(dev, ((x) + 1) & 0x0f); } }
 27+#define DEVW8(x, y) { dev->dat[(x) & 0xf] = y; dev->deo(dev, (x) & 0x0f); }
 28+#define DEVW(d, x, y) { dev = (d); if(bs) { DEVW8((x), (y) >> 8); DEVW8((x) + 1, (y)); } else { DEVW8((x), (y)) } }
 29+#define WARP(x) { if(bs) pc = (x); else pc += (Sint8)(x); }
 30+
 31+int
 32+uxn_eval(Uxn *u, Uint16 pc)
 33+{
 34+	unsigned int a, b, c, j, k, bs, instr, errcode;
 35+	Uint8 kptr, *sp;
 36+	Stack *src, *dst;
 37+	Device *dev;
 38+	if(!pc || u->dev[0].dat[0xf]) return 0;
 39+	while((instr = u->ram[pc++])) {
 40+		/* Return Mode */
 41+		if(instr & 0x40) {
 42+			src = &u->rst; dst = &u->wst;
 43+		} else {
 44+			src = &u->wst; dst = &u->rst;
 45+		}
 46+		/* Keep Mode */
 47+		if(instr & 0x80) {
 48+			kptr = src->ptr;
 49+			sp = &kptr;
 50+		} else {
 51+			sp = &src->ptr;
 52+		}
 53+		/* Short Mode */
 54+		bs = instr & 0x20 ? 1 : 0;
 55+		switch(instr & 0x1f) {
 56+		/* Stack */
 57+		case 0x00: /* LIT */ if(bs) { PEEK16(a, pc) PUSH16(src, a) pc += 2; }
 58+		                     else   { a = u->ram[pc]; PUSH8(src, a) pc++; } break;
 59+		case 0x01: /* INC */ POP(a) PUSH(src, a + 1) break;
 60+		case 0x02: /* POP */ POP(a) break;
 61+		case 0x03: /* DUP */ POP(a) PUSH(src, a) PUSH(src, a) break;
 62+		case 0x04: /* NIP */ POP(a) POP(b) PUSH(src, a) break;
 63+		case 0x05: /* SWP */ POP(a) POP(b) PUSH(src, a) PUSH(src, b) break;
 64+		case 0x06: /* OVR */ POP(a) POP(b) PUSH(src, b) PUSH(src, a) PUSH(src, b) break;
 65+		case 0x07: /* ROT */ POP(a) POP(b) POP(c) PUSH(src, b) PUSH(src, a) PUSH(src, c) break;
 66+		/* Logic */
 67+		case 0x08: /* EQU */ POP(a) POP(b) PUSH8(src, b == a) break;
 68+		case 0x09: /* NEQ */ POP(a) POP(b) PUSH8(src, b != a) break;
 69+		case 0x0a: /* GTH */ POP(a) POP(b) PUSH8(src, b > a) break;
 70+		case 0x0b: /* LTH */ POP(a) POP(b) PUSH8(src, b < a) break;
 71+		case 0x0c: /* JMP */ POP(a) WARP(a) break;
 72+		case 0x0d: /* JCN */ POP(a) POP8(b) if(b) WARP(a) break;
 73+		case 0x0e: /* JSR */ POP(a) PUSH16(dst, pc) WARP(a) break;
 74+		case 0x0f: /* STH */ POP(a) PUSH(dst, a) break;
 75+		/* Memory */
 76+		case 0x10: /* LDZ */ POP8(a) PEEK(b, a) PUSH(src, b) break;
 77+		case 0x11: /* STZ */ POP8(a) POP(b) POKE(a, b) break;
 78+		case 0x12: /* LDR */ POP8(a) PEEK(b, pc + (Sint8)a) PUSH(src, b) break;
 79+		case 0x13: /* STR */ POP8(a) POP(b) c = pc + (Sint8)a; POKE(c, b) break;
 80+		case 0x14: /* LDA */ POP16(a) PEEK(b, a) PUSH(src, b) break;
 81+		case 0x15: /* STA */ POP16(a) POP(b) POKE(a, b) break;
 82+		case 0x16: /* DEI */ POP8(a) DEVR(b, &u->dev[a >> 4], a) PUSH(src, b) break;
 83+		case 0x17: /* DEO */ POP8(a) POP(b) DEVW(&u->dev[a >> 4], a, b) break;
 84+		/* Arithmetic */
 85+		case 0x18: /* ADD */ POP(a) POP(b) PUSH(src, b + a) break;
 86+		case 0x19: /* SUB */ POP(a) POP(b) PUSH(src, b - a) break;
 87+		case 0x1a: /* MUL */ POP(a) POP(b) PUSH(src, (Uint32)b * a) break;
 88+		case 0x1b: /* DIV */ POP(a) POP(b) if(a == 0) { errcode = 4; goto err; } PUSH(src, b / a) break;
 89+		case 0x1c: /* AND */ POP(a) POP(b) PUSH(src, b & a) break;
 90+		case 0x1d: /* ORA */ POP(a) POP(b) PUSH(src, b | a) break;
 91+		case 0x1e: /* EOR */ POP(a) POP(b) PUSH(src, b ^ a) break;
 92+		case 0x1f: /* SFT */ POP8(a) POP(b) c = b >> (a & 0x0f) << ((a & 0xf0) >> 4); PUSH(src, c) break;
 93+		}
 94+	}
 95+	return 1;
 96+
 97+err:
 98+	/* set 1 in errcode if it involved the return stack instead of the working stack */
 99+	/*        (stack overflow & ( opcode was STH / JSR )) ^ Return Mode */
100+	errcode |= ((errcode >> 1 & ((instr & 0x1e) == 0x0e)) ^ instr >> 6) & 1;
101+	return uxn_halt(u, errcode, pc - 1);
102+}
103+
104+/* clang-format on */
105+
106+int
107+uxn_boot(Uxn *u, Uint8 *ram)
108+{
109+	Uint32 i;
110+	char *cptr = (char *)u;
111+	for(i = 0; i < sizeof(*u); i++)
112+		cptr[i] = 0x00;
113+	u->ram = ram;
114+	return 1;
115+}
116+
117+Device *
118+uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *d, Uint8 port), void (*deofn)(Device *d, Uint8 port))
119+{
120+	Device *d = &u->dev[id];
121+	d->u = u;
122+	d->dei = deifn;
123+	d->deo = deofn;
124+	return d;
125+}
R uxn.h => src/uxn.h
+19, -17
 1@@ -1,3 +1,6 @@
 2+#ifndef UXN_UXN_H
 3+#define UXN_UXN_H
 4+
 5 /*
 6 Copyright (c) 2021 Devine Lu Linvega
 7 
 8@@ -17,34 +20,33 @@ typedef unsigned int Uint32;
 9 
10 #define PAGE_PROGRAM 0x0100
11 
12-typedef struct {
13-	Uint8 ptr, kptr, error;
14-	Uint8 dat[256];
15-} Stack;
16+/* clang-format off */
17+
18+#define DEVPEEK16(o, x) { (o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; }
19+#define DEVPOKE16(x, y) { d->dat[(x)] = (y) >> 8; d->dat[(x) + 1] = (y); }
20+#define GETVECTOR(d) ((d)->dat[0] << 8 | (d)->dat[1])
21+
22+/* clang-format on */
23 
24 typedef struct {
25-	Uint16 ptr;
26-	Uint8 dat[65536];
27-} Memory;
28+	Uint8 ptr, dat[255];
29+} Stack;
30 
31 typedef struct Device {
32 	struct Uxn *u;
33-	Uint8 addr, dat[16], *mem;
34-	Uint16 vector;
35+	Uint8 dat[16];
36 	Uint8 (*dei)(struct Device *d, Uint8);
37 	void (*deo)(struct Device *d, Uint8);
38 } Device;
39 
40 typedef struct Uxn {
41-	Stack wst, rst, *src, *dst;
42-	Memory ram;
43+	Uint8 *ram;
44+	Stack wst, rst;
45 	Device dev[16];
46 } Uxn;
47 
48-void poke16(Uint8 *m, Uint16 a, Uint16 b);
49-Uint16 peek16(Uint8 *m, Uint16 a);
50-
51-int uxn_boot(Uxn *c);
52-int uxn_eval(Uxn *u, Uint16 vec);
53-int uxn_halt(Uxn *u, Uint8 error, char *name, int id);
54+int uxn_boot(Uxn *u, Uint8 *ram);
55+int uxn_eval(Uxn *u, Uint16 pc);
56+int uxn_halt(Uxn *u, Uint8 error, Uint16 addr);
57 Device *uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *, Uint8), void (*deofn)(Device *, Uint8));
58+#endif /* UXN_UXN_H */
+151, -0
  1@@ -0,0 +1,151 @@
  2+#include <stdio.h>
  3+#include <time.h>
  4+#include <unistd.h>
  5+
  6+#include <X11/Xlib.h>
  7+#include <stdlib.h>
  8+#include <string.h>
  9+
 10+#define WIDTH 64 * 8
 11+#define HEIGHT 40 * 8
 12+
 13+#include "uxn.h"
 14+#include "devices/system.h"
 15+#include "devices/screen.h"
 16+
 17+static Device *devsystem, *devconsole, *devscreen;
 18+
 19+static int
 20+error(char *msg, const char *err)
 21+{
 22+	fprintf(stderr, "Error %s: %s\n", msg, err);
 23+	return 0;
 24+}
 25+
 26+static int
 27+set_size(Uint16 width, Uint16 height, int is_resize)
 28+{
 29+	screen_resize(&uxn_screen, width, height);
 30+	return 1;
 31+}
 32+
 33+void
 34+system_deo_special(Device *d, Uint8 port)
 35+{
 36+	if(port > 0x7 && port < 0xe)
 37+		screen_palette(&uxn_screen, &d->dat[0x8]);
 38+}
 39+
 40+static void
 41+console_deo(Device *d, Uint8 port)
 42+{
 43+	FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr
 44+												  : 0;
 45+	if(fd) {
 46+		fputc(d->dat[port], fd);
 47+		fflush(fd);
 48+	}
 49+}
 50+
 51+static Uint8
 52+nil_dei(Device *d, Uint8 port)
 53+{
 54+	return d->dat[port];
 55+}
 56+
 57+static void
 58+nil_deo(Device *d, Uint8 port)
 59+{
 60+	(void)d;
 61+	(void)port;
 62+}
 63+
 64+static int
 65+load(Uxn *u, char *filepath)
 66+{
 67+	FILE *f;
 68+	int r;
 69+	if(!(f = fopen(filepath, "rb"))) return 0;
 70+	r = fread(u->ram + PAGE_PROGRAM, 1, 0x10000 - PAGE_PROGRAM, f);
 71+	fclose(f);
 72+	if(r < 1) return 0;
 73+	fprintf(stderr, "Loaded %s\n", filepath);
 74+	return 1;
 75+}
 76+
 77+void
 78+processEvent(Display *display, Window window, XImage *ximage, int width, int height)
 79+{
 80+	XEvent ev;
 81+	XNextEvent(display, &ev);
 82+	switch(ev.type) {
 83+	case Expose:
 84+		XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, 0, 0, width, height);
 85+		break;
 86+	case ButtonPress:
 87+		exit(0);
 88+	}
 89+}
 90+
 91+static int
 92+start(Uxn *u)
 93+{
 94+	if(!uxn_boot(u, (Uint8 *)calloc(0x10000, sizeof(Uint8))))
 95+		return error("Boot", "Failed");
 96+	/* system   */ devsystem = uxn_port(u, 0x0, system_dei, system_deo);
 97+	/* console  */ devconsole = uxn_port(u, 0x1, nil_dei, console_deo);
 98+	/* screen   */ devscreen = uxn_port(u, 0x2, screen_dei, screen_deo);
 99+	/* empty    */ uxn_port(u, 0x3, nil_dei, nil_deo);
100+	/* empty    */ uxn_port(u, 0x4, nil_dei, nil_deo);
101+	/* empty    */ uxn_port(u, 0x5, nil_dei, nil_deo);
102+	/* empty    */ uxn_port(u, 0x6, nil_dei, nil_deo);
103+	/* empty    */ uxn_port(u, 0x7, nil_dei, nil_deo);
104+	/* empty    */ uxn_port(u, 0x8, nil_dei, nil_deo);
105+	/* empty    */ uxn_port(u, 0x9, nil_dei, nil_deo);
106+	/* file     */ uxn_port(u, 0xa, nil_dei, nil_deo);
107+	/* datetime */ uxn_port(u, 0xb, nil_dei, nil_deo);
108+	/* empty    */ uxn_port(u, 0xc, nil_dei, nil_deo);
109+	/* empty    */ uxn_port(u, 0xd, nil_dei, nil_deo);
110+	/* empty    */ uxn_port(u, 0xe, nil_dei, nil_deo);
111+	/* empty    */ uxn_port(u, 0xf, nil_dei, nil_deo);
112+	return 1;
113+}
114+
115+int
116+main(int argc, char **argv)
117+{
118+	Uxn u;
119+
120+	if(argc < 2)
121+		return error("Usage", "uxncli game.rom args");
122+	if(!start(&u))
123+		return error("Start", "Failed");
124+	if(!load(&u, argv[1]))
125+		return error("Load", "Failed");
126+
127+	if(!set_size(WIDTH, HEIGHT, 0))
128+		return error("Window", "Failed to set window size.");
129+
130+	if(!uxn_eval(&u, PAGE_PROGRAM))
131+		return error("Boot", "Failed to start rom.");
132+
133+	screen_redraw(&uxn_screen, uxn_screen.pixels);
134+
135+	XImage *ximage;
136+	Display *display = XOpenDisplay(NULL);
137+	Visual *visual = DefaultVisual(display, 0);
138+	Window window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0, uxn_screen.width, uxn_screen.height, 1, 0, 0);
139+	if(visual->class != TrueColor) {
140+		fprintf(stderr, "Cannot handle non true color visual ...\n");
141+		exit(1);
142+	}
143+
144+	ximage = XCreateImage(display, visual, DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (char *)uxn_screen.pixels, uxn_screen.width, uxn_screen.height, 32, 0);
145+
146+	XSelectInput(display, window, ButtonPressMask | ExposureMask);
147+	XMapWindow(display, window);
148+	while(1) {
149+		processEvent(display, window, ximage, uxn_screen.width, uxn_screen.height);
150+	}
151+	return 0;
152+}
+148, -0
  1@@ -0,0 +1,148 @@
  2+#include <stdio.h>
  3+#include <stdlib.h>
  4+
  5+#include "uxn.h"
  6+#include "devices/system.h"
  7+#include "devices/file.h"
  8+#include "devices/datetime.h"
  9+
 10+/*
 11+Copyright (c) 2021 Devine Lu Linvega
 12+
 13+Permission to use, copy, modify, and distribute this software for any
 14+purpose with or without fee is hereby granted, provided that the above
 15+copyright notice and this permission notice appear in all copies.
 16+
 17+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 18+WITH REGARD TO THIS SOFTWARE.
 19+*/
 20+
 21+static Device *devfile0;
 22+
 23+static int
 24+error(char *msg, const char *err)
 25+{
 26+	fprintf(stderr, "Error %s: %s\n", msg, err);
 27+	return 0;
 28+}
 29+
 30+void
 31+system_deo_special(Device *d, Uint8 port)
 32+{
 33+	(void)d;
 34+	(void)port;
 35+}
 36+
 37+static void
 38+console_deo(Device *d, Uint8 port)
 39+{
 40+	FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr
 41+												  : 0;
 42+	if(fd) {
 43+		fputc(d->dat[port], fd);
 44+		fflush(fd);
 45+	}
 46+}
 47+
 48+static void
 49+file_deo(Device *d, Uint8 port)
 50+{
 51+	file_i_deo(d - devfile0, d, port);
 52+}
 53+
 54+static Uint8
 55+file_dei(Device *d, Uint8 port)
 56+{
 57+	return file_i_dei(d - devfile0, d, port);
 58+}
 59+
 60+static Uint8
 61+nil_dei(Device *d, Uint8 port)
 62+{
 63+	return d->dat[port];
 64+}
 65+
 66+static void
 67+nil_deo(Device *d, Uint8 port)
 68+{
 69+	(void)d;
 70+	(void)port;
 71+}
 72+
 73+static int
 74+console_input(Uxn *u, char c)
 75+{
 76+	Device *d = &u->dev[1];
 77+	d->dat[0x2] = c;
 78+	return uxn_eval(u, GETVECTOR(d));
 79+}
 80+
 81+static void
 82+run(Uxn *u)
 83+{
 84+	Device *d = &u->dev[0];
 85+	while(!d->dat[0xf]) {
 86+		int c = fgetc(stdin);
 87+		if(c != EOF)
 88+			console_input(u, (Uint8)c);
 89+	}
 90+}
 91+
 92+static int
 93+load(Uxn *u, char *filepath)
 94+{
 95+	FILE *f;
 96+	int r;
 97+	if(!(f = fopen(filepath, "rb"))) return 0;
 98+	r = fread(u->ram + PAGE_PROGRAM, 1, 0x10000 - PAGE_PROGRAM, f);
 99+	fclose(f);
100+	if(r < 1) return 0;
101+	fprintf(stderr, "Loaded %s\n", filepath);
102+	return 1;
103+}
104+
105+static int
106+start(Uxn *u)
107+{
108+	if(!uxn_boot(u, (Uint8 *)calloc(0x10000, sizeof(Uint8))))
109+		return error("Boot", "Failed");
110+	/* system   */ uxn_port(u, 0x0, system_dei, system_deo);
111+	/* console  */ uxn_port(u, 0x1, nil_dei, console_deo);
112+	/* empty    */ uxn_port(u, 0x2, nil_dei, nil_deo);
113+	/* empty    */ uxn_port(u, 0x3, nil_dei, nil_deo);
114+	/* empty    */ uxn_port(u, 0x4, nil_dei, nil_deo);
115+	/* empty    */ uxn_port(u, 0x5, nil_dei, nil_deo);
116+	/* empty    */ uxn_port(u, 0x6, nil_dei, nil_deo);
117+	/* empty    */ uxn_port(u, 0x7, nil_dei, nil_deo);
118+	/* empty    */ uxn_port(u, 0x8, nil_dei, nil_deo);
119+	/* empty    */ uxn_port(u, 0x9, nil_dei, nil_deo);
120+	/* file0    */ devfile0 = uxn_port(u, 0xa, file_dei, file_deo);
121+	/* file1    */ uxn_port(u, 0xb, file_dei, file_deo);
122+	/* datetime */ uxn_port(u, 0xc, datetime_dei, nil_deo);
123+	/* empty    */ uxn_port(u, 0xd, nil_dei, nil_deo);
124+	/* empty    */ uxn_port(u, 0xe, nil_dei, nil_deo);
125+	/* empty    */ uxn_port(u, 0xf, nil_dei, nil_deo);
126+	return 1;
127+}
128+
129+int
130+main(int argc, char **argv)
131+{
132+	Uxn u;
133+	int i;
134+	if(argc < 2)
135+		return error("Usage", "uxncli game.rom args");
136+	if(!start(&u))
137+		return error("Start", "Failed");
138+	if(!load(&u, argv[1]))
139+		return error("Load", "Failed");
140+	if(!uxn_eval(&u, PAGE_PROGRAM))
141+		return error("Init", "Failed");
142+	for(i = 2; i < argc; i++) {
143+		char *p = argv[i];
144+		while(*p) console_input(&u, *p++);
145+		console_input(&u, '\n');
146+	}
147+	run(&u);
148+	return 0;
149+}
D uxn.c
+0, -158
  1@@ -1,158 +0,0 @@
  2-#include "uxn.h"
  3-
  4-/*
  5-Copyright (u) 2021 Devine Lu Linvega
  6-Copyright (u) 2021 Andrew Alderwick
  7-
  8-Permission to use, copy, modify, and distribute this software for any
  9-purpose with or without fee is hereby granted, provided that the above
 10-copyright notice and this permission notice appear in all copies.
 11-
 12-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 13-WITH REGARD TO THIS SOFTWARE.
 14-*/
 15-
 16-#define MODE_SHORT 0x20
 17-#define MODE_RETURN 0x40
 18-#define MODE_KEEP 0x80
 19-
 20-#pragma mark - Operations
 21-
 22-/* clang-format off */
 23-
 24-/* Utilities */
 25-static void   (*push)(Stack *s, Uint16 a);
 26-static Uint16 (*pop8)(Stack *s);
 27-static Uint16 (*pop)(Stack *s);
 28-static void   (*poke)(Uint8 *m, Uint16 a, Uint16 b);
 29-static Uint16 (*peek)(Uint8 *m, Uint16 a);
 30-static void   (*devw)(Device *d, Uint8 a, Uint16 b);
 31-static Uint16 (*devr)(Device *d, Uint8 a);
 32-static void   (*warp)(Uxn *u, Uint16 a);
 33-static void   (*pull)(Uxn *u);
 34-/* byte mode */
 35-static void   push8(Stack *s, Uint16 a) { if(s->ptr == 0xff) { s->error = 2; return; } s->dat[s->ptr++] = a; }
 36-static Uint16 pop8k(Stack *s) { if(s->kptr == 0) { s->error = 1; return 0; } return s->dat[--s->kptr]; }
 37-static Uint16 pop8d(Stack *s) { if(s->ptr == 0) { s->error = 1; return 0; } return s->dat[--s->ptr]; }
 38-static void   poke8(Uint8 *m, Uint16 a, Uint16 b) { m[a] = b; }
 39-static Uint16 peek8(Uint8 *m, Uint16 a) { return m[a]; }
 40-static void   devw8(Device *d, Uint8 a, Uint16 b) { d->dat[a & 0xf] = b; d->deo(d, a & 0x0f); }
 41-static Uint16 devr8(Device *d, Uint8 a) { return d->dei(d, a & 0x0f); }
 42-static void   warp8(Uxn *u, Uint16 a){ u->ram.ptr += (Sint8)a; }
 43-static void   pull8(Uxn *u){ push8(u->src, peek8(u->ram.dat, u->ram.ptr++)); }
 44-/* short mode */
 45-static void   push16(Stack *s, Uint16 a) { push8(s, a >> 8); push8(s, a); }
 46-static Uint16 pop16(Stack *s) { Uint8 a = pop8(s), b = pop8(s); return a + (b << 8); }
 47-	   void   poke16(Uint8 *m, Uint16 a, Uint16 b) { poke8(m, a, b >> 8); poke8(m, a + 1, b); }
 48-	   Uint16 peek16(Uint8 *m, Uint16 a) { return (peek8(m, a) << 8) + peek8(m, a + 1); }
 49-static void   devw16(Device *d, Uint8 a, Uint16 b) { devw8(d, a, b >> 8); devw8(d, a + 1, b); }
 50-static Uint16 devr16(Device *d, Uint8 a) { return (devr8(d, a) << 8) + devr8(d, a + 1); }
 51-static void   warp16(Uxn *u, Uint16 a){ u->ram.ptr = a; }
 52-static void   pull16(Uxn *u){ push16(u->src, peek16(u->ram.dat, u->ram.ptr++)); u->ram.ptr++; }
 53-
 54-#pragma mark - Core
 55-
 56-int
 57-uxn_eval(Uxn *u, Uint16 vec)
 58-{
 59-	Uint8 instr;
 60-	Uint16 a,b,c;
 61-	if(!vec || u->dev[0].dat[0xf])
 62-		return 0;
 63-	u->ram.ptr = vec;
 64-	if(u->wst.ptr > 0xf8) u->wst.ptr = 0xf8;
 65-	while((instr = u->ram.dat[u->ram.ptr++])) {
 66-		/* Return Mode */
 67-		if(instr & MODE_RETURN) {
 68-			u->src = &u->rst; 
 69-			u->dst = &u->wst;
 70-		} else {
 71-			u->src = &u->wst; 
 72-			u->dst = &u->rst;
 73-		}
 74-		/* Keep Mode */
 75-		if(instr & MODE_KEEP) {
 76-			pop8 = pop8k;
 77-			u->src->kptr = u->src->ptr;
 78-		} else {
 79-			pop8 = pop8d;
 80-		}
 81-		/* Short Mode */
 82-		if(instr & MODE_SHORT) {
 83-			push = push16; pop = pop16;
 84-			poke = poke16; peek = peek16;
 85-			devw = devw16; devr = devr16;
 86-			warp = warp16; pull = pull16;
 87-		} else {
 88-			push = push8; pop = pop8;
 89-			poke = poke8; peek = peek8;
 90-			devw = devw8; devr = devr8;
 91-			warp = warp8; pull = pull8;
 92-		}
 93-		switch(instr & 0x1f){
 94-			/* Stack */
 95-			case 0x00: /* LIT */ pull(u); break;
 96-			case 0x01: /* INC */ a = pop(u->src); push(u->src, a + 1); break;
 97-			case 0x02: /* POP */ pop(u->src); break;
 98-			case 0x03: /* DUP */ a = pop(u->src); push(u->src, a); push(u->src, a); break;
 99-			case 0x04: /* NIP */ a = pop(u->src); pop(u->src); push(u->src, a); break;
100-			case 0x05: /* SWP */ a = pop(u->src), b = pop(u->src); push(u->src, a); push(u->src, b); break;
101-			case 0x06: /* OVR */ a = pop(u->src), b = pop(u->src); push(u->src, b); push(u->src, a); push(u->src, b); break;
102-			case 0x07: /* ROT */ a = pop(u->src), b = pop(u->src), c = pop(u->src); push(u->src, b); push(u->src, a); push(u->src, c); break;
103-			/* Logic */
104-			case 0x08: /* EQU */ a = pop(u->src), b = pop(u->src); push8(u->src, b == a); break;
105-			case 0x09: /* NEQ */ a = pop(u->src), b = pop(u->src); push8(u->src, b != a); break;
106-			case 0x0a: /* GTH */ a = pop(u->src), b = pop(u->src); push8(u->src, b > a); break;
107-			case 0x0b: /* LTH */ a = pop(u->src), b = pop(u->src); push8(u->src, b < a); break;
108-			case 0x0c: /* JMP */ a = pop(u->src); warp(u, a); break;
109-			case 0x0d: /* JCN */ a = pop(u->src); if(pop8(u->src)) warp(u, a); break;
110-			case 0x0e: /* JSR */ a = pop(u->src); push16(u->dst, u->ram.ptr); warp(u, a); break;
111-			case 0x0f: /* STH */ a = pop(u->src); push(u->dst, a); break;
112-			/* Memory */
113-			case 0x10: /* LDZ */ a = pop8(u->src); push(u->src, peek(u->ram.dat, a)); break;
114-			case 0x11: /* STZ */ a = pop8(u->src); b = pop(u->src); poke(u->ram.dat, a, b); break;
115-			case 0x12: /* LDR */ a = pop8(u->src); push(u->src, peek(u->ram.dat, u->ram.ptr + (Sint8)a)); break;
116-			case 0x13: /* STR */ a = pop8(u->src); b = pop(u->src); poke(u->ram.dat, u->ram.ptr + (Sint8)a, b); break;
117-			case 0x14: /* LDA */ a = pop16(u->src); push(u->src, peek(u->ram.dat, a)); break;
118-			case 0x15: /* STA */ a = pop16(u->src); b = pop(u->src); poke(u->ram.dat, a, b); break;
119-			case 0x16: /* DEI */ a = pop8(u->src); push(u->src, devr(&u->dev[a >> 4], a)); break;
120-			case 0x17: /* DEO */ a = pop8(u->src); b = pop(u->src); devw(&u->dev[a >> 4], a, b); break;
121-			/* Arithmetic */
122-			case 0x18: /* ADD */ a = pop(u->src), b = pop(u->src); push(u->src, b + a); break;
123-			case 0x19: /* SUB */ a = pop(u->src), b = pop(u->src); push(u->src, b - a); break;
124-			case 0x1a: /* MUL */ a = pop(u->src), b = pop(u->src); push(u->src, (Uint32)b * a); break;
125-			case 0x1b: /* DIV */ a = pop(u->src), b = pop(u->src); if(a == 0) { u->src->error = 3; a = 1; } push(u->src, b / a); break;
126-			case 0x1c: /* AND */ a = pop(u->src), b = pop(u->src); push(u->src, b & a); break;
127-			case 0x1d: /* ORA */ a = pop(u->src), b = pop(u->src); push(u->src, b | a); break;
128-			case 0x1e: /* EOR */ a = pop(u->src), b = pop(u->src); push(u->src, b ^ a); break;
129-			case 0x1f: /* SFT */ a = pop8(u->src), b = pop(u->src); push(u->src, b >> (a & 0x0f) << ((a & 0xf0) >> 4)); break;
130-		}
131-		if(u->wst.error) return uxn_halt(u, u->wst.error, "Working-stack", instr);
132-		if(u->rst.error) return uxn_halt(u, u->rst.error, "Return-stack", instr);
133-	}
134-	return 1;
135-}
136-
137-/* clang-format on */
138-
139-int
140-uxn_boot(Uxn *u)
141-{
142-	Uint32 i;
143-	char *cptr = (char *)u;
144-	for(i = 0; i < sizeof(*u); ++i)
145-		cptr[i] = 0x00;
146-	return 1;
147-}
148-
149-Device *
150-uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *d, Uint8 port), void (*deofn)(Device *d, Uint8 port))
151-{
152-	Device *d = &u->dev[id];
153-	d->addr = id * 0x10;
154-	d->u = u;
155-	d->mem = u->ram.dat;
156-	d->dei = deifn;
157-	d->deo = deofn;
158-	return d;
159-}
+0, -266
  1@@ -1,266 +0,0 @@
  2-#include <stdio.h>
  3-#include <time.h>
  4-#include <unistd.h>
  5-
  6-#include <X11/Xlib.h>
  7-#include <stdlib.h>
  8-#include <string.h>
  9-
 10-#define WIDTH 64 * 8
 11-#define HEIGHT 40 * 8
 12-#define FIXED_SIZE 0
 13-
 14-#include "uxn.h"
 15-
 16-#include "devices/ppu.h"
 17-static Ppu ppu;
 18-
 19-static Device *devsystem, *devconsole, *devscreen;
 20-static Uint32 *ppu_screen;
 21-
 22-static int
 23-error(char *msg, const char *err)
 24-{
 25-	fprintf(stderr, "Error %s: %s\n", msg, err);
 26-	return 0;
 27-}
 28-
 29-static int
 30-set_size(Uint16 width, Uint16 height, int is_resize)
 31-{
 32-	ppu_resize(&ppu, width, height);
 33-	if(!(ppu_screen =
 34-			   realloc(ppu_screen, ppu.width * ppu.height * sizeof(Uint32))))
 35-		return error("ppu_screen", "Memory failure");
 36-	memset(ppu_screen, 0, ppu.width * ppu.height * sizeof(Uint32));
 37-	printf("%d x %d(%d)\n", width, height, is_resize);
 38-	return 1;
 39-}
 40-
 41-static Uint8
 42-system_dei(Device *d, Uint8 port)
 43-{
 44-	switch(port) {
 45-	case 0x2:
 46-		return d->u->wst.ptr;
 47-	case 0x3:
 48-		return d->u->rst.ptr;
 49-	default:
 50-		return d->dat[port];
 51-	}
 52-}
 53-
 54-static void
 55-system_deo(Device *d, Uint8 port)
 56-{
 57-	switch(port) {
 58-	case 0x2:
 59-		d->u->wst.ptr = d->dat[port];
 60-		break;
 61-	case 0x3:
 62-		d->u->rst.ptr = d->dat[port];
 63-		break;
 64-	}
 65-	if(port > 0x7 && port < 0xe)
 66-		ppu_palette(&ppu, &d->dat[0x8]);
 67-}
 68-
 69-static void
 70-console_deo(Device *d, Uint8 port)
 71-{
 72-	if(port == 0x1)
 73-		d->vector = peek16(d->dat, 0x0);
 74-	if(port > 0x7)
 75-		write(port - 0x7, (char *)&d->dat[port], 1);
 76-}
 77-
 78-static Uint8
 79-screen_dei(Device *d, Uint8 port)
 80-{
 81-	switch(port) {
 82-	case 0x2:
 83-		return ppu.width >> 8;
 84-	case 0x3:
 85-		return ppu.width;
 86-	case 0x4:
 87-		return ppu.height >> 8;
 88-	case 0x5:
 89-		return ppu.height;
 90-	default:
 91-		return d->dat[port];
 92-	}
 93-}
 94-
 95-static void
 96-screen_deo(Device *d, Uint8 port)
 97-{
 98-	switch(port) {
 99-	case 0x1:
100-		d->vector = peek16(d->dat, 0x0);
101-		break;
102-	case 0x5:
103-		if(!FIXED_SIZE)
104-			set_size(peek16(d->dat, 0x2), peek16(d->dat, 0x4), 1);
105-		break;
106-	case 0xe: {
107-		Uint16 x = peek16(d->dat, 0x8);
108-		Uint16 y = peek16(d->dat, 0xa);
109-		Uint8 layer = d->dat[0xe] & 0x40;
110-		ppu_write(&ppu, layer ? &ppu.fg : &ppu.bg, x, y, d->dat[0xe] & 0x3);
111-		if(d->dat[0x6] & 0x01)
112-			poke16(d->dat, 0x8, x + 1); /* auto x+1 */
113-		if(d->dat[0x6] & 0x02)
114-			poke16(d->dat, 0xa, y + 1); /* auto y+1 */
115-		break;
116-	}
117-	case 0xf: {
118-		Uint16 x = peek16(d->dat, 0x8);
119-		Uint16 y = peek16(d->dat, 0xa);
120-		Layer *layer = (d->dat[0xf] & 0x40) ? &ppu.fg : &ppu.bg;
121-		Uint8 *addr = &d->mem[peek16(d->dat, 0xc)];
122-		Uint8 twobpp = !!(d->dat[0xf] & 0x80);
123-		ppu_blit(&ppu, layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20, twobpp);
124-		if(d->dat[0x6] & 0x04)
125-			poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 8 + twobpp * 8); /* auto addr+8 / auto addr+16 */
126-		if(d->dat[0x6] & 0x01)
127-			poke16(d->dat, 0x8, x + 8); /* auto x+8 */
128-		if(d->dat[0x6] & 0x02)
129-			poke16(d->dat, 0xa, y + 8); /* auto y+8 */
130-		break;
131-	}
132-	}
133-}
134-
135-static Uint8
136-nil_dei(Device *d, Uint8 port)
137-{
138-	return d->dat[port];
139-}
140-
141-static void
142-nil_deo(Device *d, Uint8 port)
143-{
144-	if(port == 0x1)
145-		d->vector = peek16(d->dat, 0x0);
146-}
147-
148-static const char *errors[] = {"underflow", "overflow", "division by zero"};
149-
150-int
151-uxn_halt(Uxn *u, Uint8 error, char *name, int id)
152-{
153-	fprintf(stderr, "Halted: %s %s#%04x, at 0x%04x\n", name, errors[error - 1], id, u->ram.ptr);
154-	return 0;
155-}
156-
157-static int
158-load(Uxn *u, char *filepath)
159-{
160-	FILE *f;
161-	int r;
162-	if(!(f = fopen(filepath, "rb")))
163-		return 0;
164-	r = fread(u->ram.dat + PAGE_PROGRAM, 1, sizeof(u->ram.dat) - PAGE_PROGRAM, f);
165-	fclose(f);
166-	if(r < 1)
167-		return 0;
168-	fprintf(stderr, "Loaded %s\n", filepath);
169-	return 1;
170-}
171-
172-// ---------------------------
173-
174-void
175-processEvent(Display *display, Window window, XImage *ximage, int width, int height)
176-{
177-	static char *tir = "This is red";
178-	static char *tig = "This is green";
179-	static char *tib = "This is blue";
180-	XEvent ev;
181-	XNextEvent(display, &ev);
182-	switch(ev.type) {
183-	case Expose:
184-		XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, 0, 0, width, height);
185-		XSetForeground(display, DefaultGC(display, 0), 0x00ff0000); // red
186-		XDrawString(display, window, DefaultGC(display, 0), 32, 32, tir, strlen(tir));
187-		XDrawString(display, window, DefaultGC(display, 0), 32 + 256, 32, tir, strlen(tir));
188-		XDrawString(display, window, DefaultGC(display, 0), 32 + 256, 32 + 256, tir, strlen(tir));
189-		XDrawString(display, window, DefaultGC(display, 0), 32, 32 + 256, tir, strlen(tir));
190-		XSetForeground(display, DefaultGC(display, 0), 0x0000ff00); // green
191-		XDrawString(display, window, DefaultGC(display, 0), 32, 52, tig, strlen(tig));
192-		XDrawString(display, window, DefaultGC(display, 0), 32 + 256, 52, tig, strlen(tig));
193-		XDrawString(display, window, DefaultGC(display, 0), 32 + 256, 52 + 256, tig, strlen(tig));
194-		XDrawString(display, window, DefaultGC(display, 0), 32, 52 + 256, tig, strlen(tig));
195-		XSetForeground(display, DefaultGC(display, 0), 0x000000ff); // blue
196-		XDrawString(display, window, DefaultGC(display, 0), 32, 72, tib, strlen(tib));
197-		XDrawString(display, window, DefaultGC(display, 0), 32 + 256, 72, tib, strlen(tib));
198-		XDrawString(display, window, DefaultGC(display, 0), 32 + 256, 72 + 256, tib, strlen(tib));
199-		XDrawString(display, window, DefaultGC(display, 0), 32, 72 + 256, tib, strlen(tib));
200-		break;
201-	case ButtonPress:
202-		exit(0);
203-	}
204-}
205-
206-int
207-main(int argc, char **argv)
208-{
209-
210-	Uxn u;
211-	int i, loaded = 0;
212-
213-	if(!uxn_boot(&u))
214-		return error("Boot", "Failed");
215-
216-	/* system   */ devsystem = uxn_port(&u, 0x0, system_dei, system_deo);
217-	/* console  */ devconsole = uxn_port(&u, 0x1, nil_dei, console_deo);
218-	/* screen   */ devscreen = uxn_port(&u, 0x2, screen_dei, screen_deo);
219-	/* empty    */ uxn_port(&u, 0x3, nil_dei, nil_deo);
220-	/* empty    */ uxn_port(&u, 0x4, nil_dei, nil_deo);
221-	/* empty    */ uxn_port(&u, 0x5, nil_dei, nil_deo);
222-	/* empty    */ uxn_port(&u, 0x6, nil_dei, nil_deo);
223-	/* empty    */ uxn_port(&u, 0x7, nil_dei, nil_deo);
224-	/* empty    */ uxn_port(&u, 0x8, nil_dei, nil_deo);
225-	/* empty    */ uxn_port(&u, 0x9, nil_dei, nil_deo);
226-	/* file     */ uxn_port(&u, 0xa, nil_dei, nil_deo);
227-	/* datetime */ uxn_port(&u, 0xb, nil_dei, nil_deo);
228-	/* empty    */ uxn_port(&u, 0xc, nil_dei, nil_deo);
229-	/* empty    */ uxn_port(&u, 0xd, nil_dei, nil_deo);
230-	/* empty    */ uxn_port(&u, 0xe, nil_dei, nil_deo);
231-	/* empty    */ uxn_port(&u, 0xf, nil_dei, nil_deo);
232-
233-	for(i = 1; i < argc; ++i) {
234-		if(!loaded++) {
235-			if(!load(&u, argv[i]))
236-				return error("Load", "Failed");
237-			if(!uxn_eval(&u, PAGE_PROGRAM))
238-				return error("Init", "Failed");
239-		}
240-	}
241-	if(!loaded)
242-		return error("Input", "Missing");
243-
244-	if(!set_size(WIDTH, HEIGHT, 0))
245-		return error("Window", "Failed to set window size.");
246-
247-	if(!uxn_eval(&u, PAGE_PROGRAM))
248-		return error("Boot", "Failed to start rom.");
249-	ppu_redraw(&ppu, ppu_screen);
250-
251-	XImage *ximage;
252-	Display *display = XOpenDisplay(NULL);
253-	Visual *visual = DefaultVisual(display, 0);
254-	Window window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0, ppu.width, ppu.height, 1, 0, 0);
255-	if(visual->class != TrueColor) {
256-		fprintf(stderr, "Cannot handle non true color visual ...\n");
257-		exit(1);
258-	}
259-
260-	ximage = XCreateImage(display, visual, DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (Uint8 *)ppu_screen, ppu.width, ppu.height, 32, 0);
261-
262-	XSelectInput(display, window, ButtonPressMask | ExposureMask);
263-	XMapWindow(display, window);
264-	while(1) {
265-		processEvent(display, window, ximage, ppu.width, ppu.height);
266-	}
267-}
+0, -214
  1@@ -1,214 +0,0 @@
  2-#include <stdio.h>
  3-#include <unistd.h>
  4-#include <time.h>
  5-#include "uxn.h"
  6-#include "devices/file.h"
  7-
  8-/*
  9-Copyright (c) 2021 Devine Lu Linvega
 10-
 11-Permission to use, copy, modify, and distribute this software for any
 12-purpose with or without fee is hereby granted, provided that the above
 13-copyright notice and this permission notice appear in all copies.
 14-
 15-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 16-WITH REGARD TO THIS SOFTWARE.
 17-*/
 18-
 19-#pragma mark - Core
 20-
 21-static Device *devsystem, *devconsole;
 22-
 23-static int
 24-error(char *msg, const char *err)
 25-{
 26-	fprintf(stderr, "Error %s: %s\n", msg, err);
 27-	return 0;
 28-}
 29-
 30-static void
 31-inspect(Stack *s, char *name)
 32-{
 33-	Uint8 x, y;
 34-	fprintf(stderr, "\n%s\n", name);
 35-	for(y = 0; y < 0x04; ++y) {
 36-		for(x = 0; x < 0x08; ++x) {
 37-			Uint8 p = y * 0x08 + x;
 38-			fprintf(stderr,
 39-				p == s->ptr ? "[%02x]" : " %02x ",
 40-				s->dat[p]);
 41-		}
 42-		fprintf(stderr, "\n");
 43-	}
 44-}
 45-
 46-#pragma mark - Devices
 47-
 48-static Uint8
 49-system_dei(Device *d, Uint8 port)
 50-{
 51-	switch(port) {
 52-	case 0x2: return d->u->wst.ptr;
 53-	case 0x3: return d->u->rst.ptr;
 54-	default: return d->dat[port];
 55-	}
 56-}
 57-
 58-static void
 59-system_deo(Device *d, Uint8 port)
 60-{
 61-	switch(port) {
 62-	case 0x2: d->u->wst.ptr = d->dat[port]; break;
 63-	case 0x3: d->u->rst.ptr = d->dat[port]; break;
 64-	case 0xe:
 65-		inspect(&d->u->wst, "Working-stack");
 66-		inspect(&d->u->rst, "Return-stack");
 67-		break;
 68-	}
 69-}
 70-
 71-static void
 72-console_deo(Device *d, Uint8 port)
 73-{
 74-	if(port == 0x1)
 75-		d->vector = peek16(d->dat, 0x0);
 76-	if(port > 0x7)
 77-		write(port - 0x7, (char *)&d->dat[port], 1);
 78-}
 79-
 80-static void
 81-file_deo(Device *d, Uint8 port)
 82-{
 83-	switch(port) {
 84-	case 0x1: d->vector = peek16(d->dat, 0x0); break;
 85-	case 0x9: poke16(d->dat, 0x2, file_init(&d->mem[peek16(d->dat, 0x8)])); break;
 86-	case 0xd: poke16(d->dat, 0x2, file_read(&d->mem[peek16(d->dat, 0xc)], peek16(d->dat, 0xa))); break;
 87-	case 0xf: poke16(d->dat, 0x2, file_write(&d->mem[peek16(d->dat, 0xe)], peek16(d->dat, 0xa), d->dat[0x7])); break;
 88-	case 0x5: poke16(d->dat, 0x2, file_stat(&d->mem[peek16(d->dat, 0x4)], peek16(d->dat, 0xa))); break;
 89-	case 0x6: poke16(d->dat, 0x2, file_delete()); break;
 90-	}
 91-}
 92-
 93-static Uint8
 94-datetime_dei(Device *d, Uint8 port)
 95-{
 96-	time_t seconds = time(NULL);
 97-	struct tm zt = {0};
 98-	struct tm *t = localtime(&seconds);
 99-	if(t == NULL)
100-		t = &zt;
101-	switch(port) {
102-	case 0x0: return (t->tm_year + 1900) >> 8;
103-	case 0x1: return (t->tm_year + 1900);
104-	case 0x2: return t->tm_mon;
105-	case 0x3: return t->tm_mday;
106-	case 0x4: return t->tm_hour;
107-	case 0x5: return t->tm_min;
108-	case 0x6: return t->tm_sec;
109-	case 0x7: return t->tm_wday;
110-	case 0x8: return t->tm_yday >> 8;
111-	case 0x9: return t->tm_yday;
112-	case 0xa: return t->tm_isdst;
113-	default: return d->dat[port];
114-	}
115-}
116-
117-static Uint8
118-nil_dei(Device *d, Uint8 port)
119-{
120-	return d->dat[port];
121-}
122-
123-static void
124-nil_deo(Device *d, Uint8 port)
125-{
126-	if(port == 0x1) d->vector = peek16(d->dat, 0x0);
127-}
128-
129-#pragma mark - Generics
130-
131-static const char *errors[] = {"underflow", "overflow", "division by zero"};
132-
133-int
134-uxn_halt(Uxn *u, Uint8 error, char *name, int id)
135-{
136-	fprintf(stderr, "Halted: %s %s#%04x, at 0x%04x\n", name, errors[error - 1], id, u->ram.ptr);
137-	return 0;
138-}
139-
140-static int
141-console_input(Uxn *u, char c)
142-{
143-	devconsole->dat[0x2] = c;
144-	return uxn_eval(u, devconsole->vector);
145-}
146-
147-static void
148-run(Uxn *u)
149-{
150-	Uint16 vec;
151-	while((!u->dev[0].dat[0xf]) && (read(0, &devconsole->dat[0x2], 1) > 0)) {
152-		vec = peek16(devconsole->dat, 0);
153-		if(!vec) vec = u->ram.ptr; /* continue after last BRK */
154-		uxn_eval(u, vec);
155-	}
156-}
157-
158-static int
159-load(Uxn *u, char *filepath)
160-{
161-	FILE *f;
162-	int r;
163-	if(!(f = fopen(filepath, "rb"))) return 0;
164-	r = fread(u->ram.dat + PAGE_PROGRAM, 1, sizeof(u->ram.dat) - PAGE_PROGRAM, f);
165-	fclose(f);
166-	if(r < 1) return 0;
167-	fprintf(stderr, "Loaded %s\n", filepath);
168-	return 1;
169-}
170-
171-int
172-main(int argc, char **argv)
173-{
174-	Uxn u;
175-	int i, loaded = 0;
176-
177-	if(!uxn_boot(&u))
178-		return error("Boot", "Failed");
179-
180-	/* system   */ devsystem = uxn_port(&u, 0x0, system_dei, system_deo);
181-	/* console  */ devconsole = uxn_port(&u, 0x1, nil_dei, console_deo);
182-	/* empty    */ uxn_port(&u, 0x2, nil_dei, nil_deo);
183-	/* empty    */ uxn_port(&u, 0x3, nil_dei, nil_deo);
184-	/* empty    */ uxn_port(&u, 0x4, nil_dei, nil_deo);
185-	/* empty    */ uxn_port(&u, 0x5, nil_dei, nil_deo);
186-	/* empty    */ uxn_port(&u, 0x6, nil_dei, nil_deo);
187-	/* empty    */ uxn_port(&u, 0x7, nil_dei, nil_deo);
188-	/* empty    */ uxn_port(&u, 0x8, nil_dei, nil_deo);
189-	/* empty    */ uxn_port(&u, 0x9, nil_dei, nil_deo);
190-	/* file     */ uxn_port(&u, 0xa, nil_dei, file_deo);
191-	/* datetime */ uxn_port(&u, 0xb, datetime_dei, nil_deo);
192-	/* empty    */ uxn_port(&u, 0xc, nil_dei, nil_deo);
193-	/* empty    */ uxn_port(&u, 0xd, nil_dei, nil_deo);
194-	/* empty    */ uxn_port(&u, 0xe, nil_dei, nil_deo);
195-	/* empty    */ uxn_port(&u, 0xf, nil_dei, nil_deo);
196-
197-	for(i = 1; i < argc; ++i) {
198-		if(!loaded++) {
199-			if(!load(&u, argv[i]))
200-				return error("Load", "Failed");
201-			if(!uxn_eval(&u, PAGE_PROGRAM))
202-				return error("Init", "Failed");
203-		} else {
204-			char *p = argv[i];
205-			while(*p) console_input(&u, *p++);
206-			console_input(&u, '\n');
207-		}
208-	}
209-	if(!loaded)
210-		return error("Input", "Missing");
211-
212-	run(&u);
213-
214-	return 0;
215-}
+0, -577
  1@@ -1,577 +0,0 @@
  2-#include <stdio.h>
  3-#include <unistd.h>
  4-#include <time.h>
  5-#include "uxn.h"
  6-
  7-#pragma GCC diagnostic push
  8-#pragma clang diagnostic push
  9-#pragma GCC diagnostic ignored "-Wpedantic"
 10-#pragma clang diagnostic ignored "-Wtypedef-redefinition"
 11-#include <SDL.h>
 12-#include "devices/ppu.h"
 13-#include "devices/apu.h"
 14-#include "devices/file.h"
 15-#pragma GCC diagnostic pop
 16-#pragma clang diagnostic pop
 17-
 18-/*
 19-Copyright (c) 2021 Devine Lu Linvega
 20-
 21-Permission to use, copy, modify, and distribute this software for any
 22-purpose with or without fee is hereby granted, provided that the above
 23-copyright notice and this permission notice appear in all copies.
 24-
 25-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 26-WITH REGARD TO THIS SOFTWARE.
 27-*/
 28-
 29-#define WIDTH 64 * 8
 30-#define HEIGHT 40 * 8
 31-#define PAD 4
 32-#define FIXED_SIZE 0
 33-#define POLYPHONY 4
 34-#define BENCH 0
 35-
 36-static SDL_Window *gWindow;
 37-static SDL_Texture *gTexture;
 38-static SDL_Renderer *gRenderer;
 39-static SDL_AudioDeviceID audio_id;
 40-static SDL_Rect gRect;
 41-/* devices */
 42-static Ppu ppu;
 43-static Apu apu[POLYPHONY];
 44-static Device *devsystem, *devscreen, *devmouse, *devctrl, *devaudio0, *devconsole;
 45-static Uint8 zoom = 1;
 46-static Uint32 *ppu_screen, stdin_event, audio0_event;
 47-
 48-static int
 49-error(char *msg, const char *err)
 50-{
 51-	fprintf(stderr, "%s: %s\n", msg, err);
 52-	return 0;
 53-}
 54-
 55-#pragma mark - Generics
 56-
 57-static void
 58-audio_callback(void *u, Uint8 *stream, int len)
 59-{
 60-	int i, running = 0;
 61-	Sint16 *samples = (Sint16 *)stream;
 62-	SDL_memset(stream, 0, len);
 63-	for(i = 0; i < POLYPHONY; ++i)
 64-		running += apu_render(&apu[i], samples, samples + len / 2);
 65-	if(!running)
 66-		SDL_PauseAudioDevice(audio_id, 1);
 67-	(void)u;
 68-}
 69-
 70-void
 71-apu_finished_handler(Apu *c)
 72-{
 73-	SDL_Event event;
 74-	event.type = audio0_event + (c - apu);
 75-	SDL_PushEvent(&event);
 76-}
 77-
 78-static int
 79-stdin_handler(void *p)
 80-{
 81-	SDL_Event event;
 82-	event.type = stdin_event;
 83-	while(read(0, &event.cbutton.button, 1) > 0)
 84-		SDL_PushEvent(&event);
 85-	return 0;
 86-	(void)p;
 87-}
 88-
 89-static void
 90-set_window_size(SDL_Window *window, int w, int h)
 91-{
 92-	SDL_Point win, win_old;
 93-	SDL_GetWindowPosition(window, &win.x, &win.y);
 94-	SDL_GetWindowSize(window, &win_old.x, &win_old.y);
 95-	SDL_SetWindowPosition(window, (win.x + win_old.x / 2) - w / 2, (win.y + win_old.y / 2) - h / 2);
 96-	SDL_SetWindowSize(window, w, h);
 97-}
 98-
 99-static void
100-set_zoom(Uint8 scale)
101-{
102-	zoom = SDL_clamp(scale, 1, 3);
103-	set_window_size(gWindow, (ppu.width + PAD * 2) * zoom, (ppu.height + PAD * 2) * zoom);
104-}
105-
106-static int
107-set_size(Uint16 width, Uint16 height, int is_resize)
108-{
109-	ppu_resize(&ppu, width, height);
110-	gRect.x = PAD;
111-	gRect.y = PAD;
112-	gRect.w = ppu.width;
113-	gRect.h = ppu.height;
114-	if(!(ppu_screen = realloc(ppu_screen, ppu.width * ppu.height * sizeof(Uint32))))
115-		return error("ppu_screen", "Memory failure");
116-	memset(ppu_screen, 0, ppu.width * ppu.height * sizeof(Uint32));
117-	if(gTexture != NULL) SDL_DestroyTexture(gTexture);
118-	SDL_RenderSetLogicalSize(gRenderer, ppu.width + PAD * 2, ppu.height + PAD * 2);
119-	gTexture = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, ppu.width + PAD * 2, ppu.height + PAD * 2);
120-	if(gTexture == NULL || SDL_SetTextureBlendMode(gTexture, SDL_BLENDMODE_NONE))
121-		return error("gTexture", SDL_GetError());
122-	if(SDL_UpdateTexture(gTexture, NULL, ppu_screen, sizeof(Uint32)) != 0)
123-		return error("SDL_UpdateTexture", SDL_GetError());
124-	if(is_resize)
125-		set_window_size(gWindow, (ppu.width + PAD * 2) * zoom, (ppu.height + PAD * 2) * zoom);
126-	return 1;
127-}
128-
129-static void
130-capture_screen(void)
131-{
132-	const Uint32 format = SDL_PIXELFORMAT_RGB24;
133-	time_t t = time(NULL);
134-	char fname[64];
135-	int w, h;
136-	SDL_Surface *surface;
137-	SDL_GetRendererOutputSize(gRenderer, &w, &h);
138-	surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 24, format);
139-	SDL_RenderReadPixels(gRenderer, NULL, format, surface->pixels, surface->pitch);
140-	strftime(fname, sizeof(fname), "screenshot-%Y%m%d-%H%M%S.bmp", localtime(&t));
141-	SDL_SaveBMP(surface, fname);
142-	SDL_FreeSurface(surface);
143-	fprintf(stderr, "Saved %s\n", fname);
144-}
145-
146-static void
147-redraw(Uxn *u)
148-{
149-	if(devsystem->dat[0xe])
150-		ppu_debug(&ppu, u->wst.dat, u->wst.ptr, u->rst.ptr, u->ram.dat);
151-	ppu_redraw(&ppu, ppu_screen);
152-	if(SDL_UpdateTexture(gTexture, &gRect, ppu_screen, ppu.width * sizeof(Uint32)) != 0)
153-		error("SDL_UpdateTexture", SDL_GetError());
154-	SDL_RenderClear(gRenderer);
155-	SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
156-	SDL_RenderPresent(gRenderer);
157-}
158-
159-static int
160-init(void)
161-{
162-	SDL_AudioSpec as;
163-	SDL_zero(as);
164-	as.freq = SAMPLE_FREQUENCY;
165-	as.format = AUDIO_S16;
166-	as.channels = 2;
167-	as.callback = audio_callback;
168-	as.samples = 512;
169-	as.userdata = NULL;
170-	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
171-		error("sdl", SDL_GetError());
172-		if(SDL_Init(SDL_INIT_VIDEO) < 0)
173-			return error("sdl", SDL_GetError());
174-	} else {
175-		audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
176-		if(!audio_id)
177-			error("sdl_audio", SDL_GetError());
178-	}
179-	gWindow = SDL_CreateWindow("Uxn", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, (WIDTH + PAD * 2) * zoom, (HEIGHT + PAD * 2) * zoom, SDL_WINDOW_SHOWN);
180-	if(gWindow == NULL)
181-		return error("sdl_window", SDL_GetError());
182-	gRenderer = SDL_CreateRenderer(gWindow, -1, 0);
183-	if(gRenderer == NULL)
184-		return error("sdl_renderer", SDL_GetError());
185-	stdin_event = SDL_RegisterEvents(1);
186-	audio0_event = SDL_RegisterEvents(POLYPHONY);
187-	SDL_CreateThread(stdin_handler, "stdin", NULL);
188-	SDL_StartTextInput();
189-	SDL_ShowCursor(SDL_DISABLE);
190-	SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
191-	return 1;
192-}
193-
194-static void
195-domouse(SDL_Event *event)
196-{
197-	Uint8 flag = 0x00;
198-	Uint16 x = SDL_clamp(event->motion.x - PAD, 0, ppu.width - 1);
199-	Uint16 y = SDL_clamp(event->motion.y - PAD, 0, ppu.height - 1);
200-	if(event->type == SDL_MOUSEWHEEL) {
201-		devmouse->dat[7] = event->wheel.y;
202-		return;
203-	}
204-	poke16(devmouse->dat, 0x2, x);
205-	poke16(devmouse->dat, 0x4, y);
206-	devmouse->dat[7] = 0x00;
207-	switch(event->button.button) {
208-	case SDL_BUTTON_LEFT: flag = 0x01; break;
209-	case SDL_BUTTON_RIGHT: flag = 0x10; break;
210-	}
211-	switch(event->type) {
212-	case SDL_MOUSEBUTTONDOWN:
213-		devmouse->dat[6] |= flag;
214-		break;
215-	case SDL_MOUSEBUTTONUP:
216-		devmouse->dat[6] &= (~flag);
217-		break;
218-	}
219-}
220-
221-#pragma mark - Devices
222-
223-static Uint8
224-system_dei(Device *d, Uint8 port)
225-{
226-	switch(port) {
227-	case 0x2: return d->u->wst.ptr;
228-	case 0x3: return d->u->rst.ptr;
229-	default: return d->dat[port];
230-	}
231-}
232-
233-static void
234-system_deo(Device *d, Uint8 port)
235-{
236-	switch(port) {
237-	case 0x2: d->u->wst.ptr = d->dat[port]; break;
238-	case 0x3: d->u->rst.ptr = d->dat[port]; break;
239-	}
240-	if(port > 0x7 && port < 0xe)
241-		ppu_palette(&ppu, &d->dat[0x8]);
242-}
243-
244-static void
245-console_deo(Device *d, Uint8 port)
246-{
247-	if(port == 0x1)
248-		d->vector = peek16(d->dat, 0x0);
249-	if(port > 0x7)
250-		write(port - 0x7, (char *)&d->dat[port], 1);
251-}
252-
253-static Uint8
254-screen_dei(Device *d, Uint8 port)
255-{
256-	switch(port) {
257-	case 0x2: return ppu.width >> 8;
258-	case 0x3: return ppu.width;
259-	case 0x4: return ppu.height >> 8;
260-	case 0x5: return ppu.height;
261-	default: return d->dat[port];
262-	}
263-}
264-
265-static void
266-screen_deo(Device *d, Uint8 port)
267-{
268-	switch(port) {
269-	case 0x1: d->vector = peek16(d->dat, 0x0); break;
270-	case 0x5:
271-		if(!FIXED_SIZE) set_size(peek16(d->dat, 0x2), peek16(d->dat, 0x4), 1);
272-		break;
273-	case 0xe: {
274-		Uint16 x = peek16(d->dat, 0x8);
275-		Uint16 y = peek16(d->dat, 0xa);
276-		Uint8 layer = d->dat[0xe] & 0x40;
277-		ppu_write(&ppu, layer ? &ppu.fg : &ppu.bg, x, y, d->dat[0xe] & 0x3);
278-		if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 1); /* auto x+1 */
279-		if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 1); /* auto y+1 */
280-		break;
281-	}
282-	case 0xf: {
283-		Uint16 x = peek16(d->dat, 0x8);
284-		Uint16 y = peek16(d->dat, 0xa);
285-		Layer *layer = (d->dat[0xf] & 0x40) ? &ppu.fg : &ppu.bg;
286-		Uint8 *addr = &d->mem[peek16(d->dat, 0xc)];
287-		Uint8 twobpp = !!(d->dat[0xf] & 0x80);
288-		ppu_blit(&ppu, layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20, twobpp);
289-		if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 8 + twobpp*8); /* auto addr+8 / auto addr+16 */
290-		if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 8); /* auto x+8 */
291-		if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 8); /* auto y+8 */
292-		break;
293-	}
294-	}
295-}
296-
297-static void
298-file_deo(Device *d, Uint8 port)
299-{
300-	switch(port) {
301-	case 0x1: d->vector = peek16(d->dat, 0x0); break;
302-	case 0x9: poke16(d->dat, 0x2, file_init(&d->mem[peek16(d->dat, 0x8)])); break;
303-	case 0xd: poke16(d->dat, 0x2, file_read(&d->mem[peek16(d->dat, 0xc)], peek16(d->dat, 0xa))); break;
304-	case 0xf: poke16(d->dat, 0x2, file_write(&d->mem[peek16(d->dat, 0xe)], peek16(d->dat, 0xa), d->dat[0x7])); break;
305-	case 0x5: poke16(d->dat, 0x2, file_stat(&d->mem[peek16(d->dat, 0x4)], peek16(d->dat, 0xa))); break;
306-	case 0x6: poke16(d->dat, 0x2, file_delete()); break;
307-	}
308-}
309-
310-static Uint8
311-audio_dei(Device *d, Uint8 port)
312-{
313-	Apu *c = &apu[d - devaudio0];
314-	if(!audio_id) return d->dat[port];
315-	switch(port) {
316-	case 0x4: return apu_get_vu(c);
317-	case 0x2: poke16(d->dat, 0x2, c->i); /* fall through */
318-	default: return d->dat[port];
319-	}
320-}
321-
322-static void
323-audio_deo(Device *d, Uint8 port)
324-{
325-	Apu *c = &apu[d - devaudio0];
326-	if(!audio_id) return;
327-	if(port == 0xf) {
328-		SDL_LockAudioDevice(audio_id);
329-		c->len = peek16(d->dat, 0xa);
330-		c->addr = &d->mem[peek16(d->dat, 0xc)];
331-		c->volume[0] = d->dat[0xe] >> 4;
332-		c->volume[1] = d->dat[0xe] & 0xf;
333-		c->repeat = !(d->dat[0xf] & 0x80);
334-		apu_start(c, peek16(d->dat, 0x8), d->dat[0xf] & 0x7f);
335-		SDL_UnlockAudioDevice(audio_id);
336-		SDL_PauseAudioDevice(audio_id, 0);
337-	}
338-}
339-
340-static Uint8
341-datetime_dei(Device *d, Uint8 port)
342-{
343-	time_t seconds = time(NULL);
344-	struct tm zt = {0};
345-	struct tm *t = localtime(&seconds);
346-	if(t == NULL)
347-		t = &zt;
348-	switch(port) {
349-	case 0x0: return (t->tm_year + 1900) >> 8;
350-	case 0x1: return (t->tm_year + 1900);
351-	case 0x2: return t->tm_mon;
352-	case 0x3: return t->tm_mday;
353-	case 0x4: return t->tm_hour;
354-	case 0x5: return t->tm_min;
355-	case 0x6: return t->tm_sec;
356-	case 0x7: return t->tm_wday;
357-	case 0x8: return t->tm_yday >> 8;
358-	case 0x9: return t->tm_yday;
359-	case 0xa: return t->tm_isdst;
360-	default: return d->dat[port];
361-	}
362-}
363-
364-static Uint8
365-nil_dei(Device *d, Uint8 port)
366-{
367-	return d->dat[port];
368-}
369-
370-static void
371-nil_deo(Device *d, Uint8 port)
372-{
373-	if(port == 0x1) d->vector = peek16(d->dat, 0x0);
374-}
375-
376-/* Boot */
377-
378-static int
379-load(Uxn *u, char *rom)
380-{
381-	SDL_RWops *f;
382-	int r;
383-	if(!(f = SDL_RWFromFile(rom, "rb"))) return 0;
384-	r = f->read(f, u->ram.dat + PAGE_PROGRAM, 1, sizeof(u->ram.dat) - PAGE_PROGRAM);
385-	f->close(f);
386-	if(r < 1) return 0;
387-	fprintf(stderr, "Loaded %s\n", rom);
388-	SDL_SetWindowTitle(gWindow, rom);
389-	return 1;
390-}
391-
392-static int
393-start(Uxn *u, char *rom)
394-{
395-	if(!uxn_boot(u))
396-		return error("Boot", "Failed to start uxn.");
397-	if(!load(u, rom))
398-		return error("Boot", "Failed to load rom.");
399-
400-	/* system   */ devsystem = uxn_port(u, 0x0, system_dei, system_deo);
401-	/* console  */ devconsole = uxn_port(u, 0x1, nil_dei, console_deo);
402-	/* screen   */ devscreen = uxn_port(u, 0x2, screen_dei, screen_deo);
403-	/* audio0   */ devaudio0 = uxn_port(u, 0x3, audio_dei, audio_deo);
404-	/* audio1   */ uxn_port(u, 0x4, audio_dei, audio_deo);
405-	/* audio2   */ uxn_port(u, 0x5, audio_dei, audio_deo);
406-	/* audio3   */ uxn_port(u, 0x6, audio_dei, audio_deo);
407-	/* unused   */ uxn_port(u, 0x7, nil_dei, nil_deo);
408-	/* control  */ devctrl = uxn_port(u, 0x8, nil_dei, nil_deo);
409-	/* mouse    */ devmouse = uxn_port(u, 0x9, nil_dei, nil_deo);
410-	/* file     */ uxn_port(u, 0xa, nil_dei, file_deo);
411-	/* datetime */ uxn_port(u, 0xb, datetime_dei, nil_deo);
412-	/* unused   */ uxn_port(u, 0xc, nil_dei, nil_deo);
413-	/* unused   */ uxn_port(u, 0xd, nil_dei, nil_deo);
414-	/* unused   */ uxn_port(u, 0xe, nil_dei, nil_deo);
415-	/* unused   */ uxn_port(u, 0xf, nil_dei, nil_deo);
416-
417-	if(!uxn_eval(u, PAGE_PROGRAM))
418-		return error("Boot", "Failed to start rom.");
419-
420-	return 1;
421-}
422-
423-static void
424-restart(Uxn *u)
425-{
426-	set_size(WIDTH, HEIGHT, 1);
427-	start(u, "boot.rom");
428-}
429-
430-static void
431-doctrl(Uxn *u, SDL_Event *event, int z)
432-{
433-	Uint8 flag = 0x00;
434-	SDL_Keymod mods = SDL_GetModState();
435-	devctrl->dat[2] &= 0xf8;
436-	if(mods & KMOD_CTRL) devctrl->dat[2] |= 0x01;
437-	if(mods & KMOD_ALT) devctrl->dat[2] |= 0x02;
438-	if(mods & KMOD_SHIFT) devctrl->dat[2] |= 0x04;
439-	/* clang-format off */
440-	switch(event->key.keysym.sym) {
441-	case SDLK_ESCAPE: flag = 0x08; break;
442-	case SDLK_UP: flag = 0x10; break;
443-	case SDLK_DOWN: flag = 0x20; break;
444-	case SDLK_LEFT: flag = 0x40; break;
445-	case SDLK_RIGHT: flag = 0x80; break;
446-	case SDLK_F1: if(z) set_zoom(zoom > 2 ? 1 : zoom + 1); break;
447-	case SDLK_F2: if(z) devsystem->dat[0xe] = !devsystem->dat[0xe]; ppu_clear(&ppu, &ppu.fg); break;
448-	case SDLK_F3: if(z) capture_screen(); break;
449-	case SDLK_AC_BACK:
450-	case SDLK_F4: if(z) restart(u); break;
451-	}
452-	/* clang-format on */
453-	if(z) {
454-		devctrl->dat[2] |= flag;
455-		if(event->key.keysym.sym < 0x20 || event->key.keysym.sym == SDLK_DELETE)
456-			devctrl->dat[3] = event->key.keysym.sym;
457-		else if((mods & KMOD_CTRL) && event->key.keysym.sym >= SDLK_a && event->key.keysym.sym <= SDLK_z)
458-			devctrl->dat[3] = event->key.keysym.sym - (mods & KMOD_SHIFT) * 0x20;
459-	} else
460-		devctrl->dat[2] &= ~flag;
461-}
462-
463-static const char *errors[] = {"underflow", "overflow", "division by zero"};
464-
465-int
466-uxn_halt(Uxn *u, Uint8 error, char *name, int id)
467-{
468-	fprintf(stderr, "Halted: %s %s#%04x, at 0x%04x\n", name, errors[error - 1], id, u->ram.ptr);
469-	return 0;
470-}
471-
472-static int
473-console_input(Uxn *u, char c)
474-{
475-	devconsole->dat[0x2] = c;
476-	return uxn_eval(u, devconsole->vector);
477-}
478-
479-static int
480-run(Uxn *u)
481-{
482-	redraw(u);
483-	while(!devsystem->dat[0xf]) {
484-		SDL_Event event;
485-		double elapsed, begin = 0;
486-		int ksym;
487-		if(!BENCH)
488-			begin = SDL_GetPerformanceCounter();
489-		while(SDL_PollEvent(&event) != 0) {
490-			switch(event.type) {
491-			case SDL_DROPFILE:
492-				set_size(WIDTH, HEIGHT, 0);
493-				start(u, event.drop.file);
494-				SDL_free(event.drop.file);
495-				break;
496-			case SDL_QUIT:
497-				return error("Run", "Quit.");
498-			case SDL_TEXTINPUT:
499-				devctrl->dat[3] = event.text.text[0]; /* fall-thru */
500-			case SDL_KEYDOWN:
501-			case SDL_KEYUP:
502-				doctrl(u, &event, event.type == SDL_KEYDOWN);
503-				uxn_eval(u, devctrl->vector);
504-				devctrl->dat[3] = 0;
505-
506-				if(event.type == SDL_KEYDOWN) {
507-					ksym = event.key.keysym.sym;
508-					if(SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYUP, SDL_KEYUP) == 1 && ksym == event.key.keysym.sym)
509-						goto breakout;
510-				}
511-				break;
512-			case SDL_MOUSEWHEEL:
513-			case SDL_MOUSEBUTTONUP:
514-			case SDL_MOUSEBUTTONDOWN:
515-			case SDL_MOUSEMOTION:
516-				domouse(&event);
517-				uxn_eval(u, devmouse->vector);
518-				break;
519-			case SDL_WINDOWEVENT:
520-				if(event.window.event == SDL_WINDOWEVENT_EXPOSED)
521-					redraw(u);
522-				break;
523-			default:
524-				if(event.type == stdin_event) {
525-					console_input(u, event.cbutton.button);
526-				} else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY)
527-					uxn_eval(u, peek16((devaudio0 + (event.type - audio0_event))->dat, 0));
528-			}
529-		}
530-	breakout:
531-		uxn_eval(u, devscreen->vector);
532-		if(ppu.fg.changed || ppu.bg.changed || devsystem->dat[0xe])
533-			redraw(u);
534-		if(!BENCH) {
535-			elapsed = (SDL_GetPerformanceCounter() - begin) / (double)SDL_GetPerformanceFrequency() * 1000.0f;
536-			SDL_Delay(SDL_clamp(16.666f - elapsed, 0, 1000));
537-		}
538-	}
539-	return error("Run", "Ended.");
540-}
541-
542-int
543-main(int argc, char **argv)
544-{
545-	SDL_DisplayMode DM;
546-	Uxn u;
547-	int i, loaded = 0;
548-
549-	if(!init())
550-		return error("Init", "Failed to initialize emulator.");
551-	if(!set_size(WIDTH, HEIGHT, 0))
552-		return error("Window", "Failed to set window size.");
553-
554-	/* set default zoom */
555-	if(SDL_GetCurrentDisplayMode(0, &DM) == 0)
556-		set_zoom(DM.w / 1280);
557-	for(i = 1; i < argc; ++i) {
558-		/* get default zoom from flags */
559-		if(strcmp(argv[i], "-s") == 0) {
560-			if(i < argc - 1)
561-				set_zoom(atoi(argv[++i]));
562-			else
563-				return error("Opt", "-s No scale provided.");
564-		} else if(!loaded++) {
565-			if(!start(&u, argv[i]))
566-				return error("Boot", "Failed to boot.");
567-		} else {
568-			char *p = argv[i];
569-			while(*p) console_input(&u, *p++);
570-			console_input(&u, '\n');
571-		}
572-	}
573-	if(!loaded && !start(&u, "boot.rom"))
574-		return error("usage", "uxnemu [-s scale] file.rom");
575-	run(&u);
576-	SDL_Quit();
577-	return 0;
578-}