commit b88531d
neauoire
·
2022-03-27 00:23:52 +0000 UTC
parent b88531d
Init
+21,
-0
1@@ -0,0 +1,21 @@
2+AlignAfterOpenBracket: DontAlign
3+AlignEscapedNewlines: DontAlign
4+AlignOperands: DontAlign
5+AllowShortBlocksOnASingleLine: Always
6+AllowShortCaseLabelsOnASingleLine: true
7+AllowShortEnumsOnASingleLine: true
8+AllowShortIfStatementsOnASingleLine: true
9+AllowShortLoopsOnASingleLine: true
10+AlwaysBreakAfterDefinitionReturnType: TopLevel
11+BinPackArguments: false
12+BinPackParameters: false
13+BreakBeforeBraces: WebKit
14+IndentCaseLabels: false
15+TabWidth: 4
16+IndentWidth: 4
17+ContinuationIndentWidth: 4
18+UseTab: ForContinuationAndIndentation
19+ColumnLimit: 0
20+ReflowComments: false
21+SortIncludes: false
22+SpaceBeforeParens: false
+15,
-0
1@@ -0,0 +1,15 @@
2+.DS*
3+*jpg~
4+*png~
5+*gif~
6+*bmp~
7+/bin
8+*io.bit
9+*.bak
10+/*-test
11+/screenshot-*.bmp
12+
13+*snarf
14+*theme
15+
16+*.[o0125678vqki]
A
LICENSE
+21,
-0
1@@ -0,0 +1,21 @@
2+MIT License
3+
4+Copyright (c) Devine Lu Linvega
5+
6+Permission is hereby granted, free of charge, to any person obtaining a copy
7+of this software and associated documentation files (the "Software"), to deal
8+in the Software without restriction, including without limitation the rights
9+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the Software is
11+furnished to do so, subject to the following conditions:
12+
13+The above copyright notice and this permission notice shall be included in all
14+copies or substantial portions of the Software.
15+
16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+SOFTWARE.
+9,
-0
1@@ -0,0 +1,9 @@
2+# Uxn
3+
4+An assembler and emulator for the [Uxn stack-machine](https://wiki.xxiivv.com/site/uxn.html), written in ANSI C.
5+
6+More docs coming soon.
7+
8+## Contributing
9+
10+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).
A
build.sh
+13,
-0
1@@ -0,0 +1,13 @@
2+#!/bin/sh -e
3+
4+clang-format -i uxn11.c
5+
6+echo "Cleaning.."
7+rm -f ./bin/*
8+
9+echo "Building.."
10+mkdir -p bin
11+gcc uxn.c devices/ppu.c uxn11.c -o bin/uxn11 -lX11
12+
13+echo "Running.."
14+bin/uxn11 etc/screen.rom
+97,
-0
1@@ -0,0 +1,97 @@
2+#include "../uxn.h"
3+#include "apu.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 NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025)
18+#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
19+
20+/* clang-format off */
21+
22+static Uint32 advances[12] = {
23+ 0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1,
24+ 0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c
25+};
26+
27+/* clang-format on */
28+
29+static Sint32
30+envelope(Apu *c, Uint32 age)
31+{
32+ if(!c->r) return 0x0888;
33+ if(age < c->a) return 0x0888 * age / c->a;
34+ if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a);
35+ if(age < c->s) return 0x0444;
36+ if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s);
37+ c->advance = 0;
38+ return 0x0000;
39+}
40+
41+int
42+apu_render(Apu *c, Sint16 *sample, Sint16 *end)
43+{
44+ Sint32 s;
45+ if(!c->advance || !c->period) return 0;
46+ while(sample < end) {
47+ c->count += c->advance;
48+ c->i += c->count / c->period;
49+ c->count %= c->period;
50+ if(c->i >= c->len) {
51+ if(!c->repeat) {
52+ c->advance = 0;
53+ break;
54+ }
55+ c->i %= c->len;
56+ }
57+ s = (Sint8)(c->addr[c->i] + 0x80) * envelope(c, c->age++);
58+ *sample++ += s * c->volume[0] / 0x180;
59+ *sample++ += s * c->volume[1] / 0x180;
60+ }
61+ if(!c->advance) apu_finished_handler(c);
62+ return 1;
63+}
64+
65+void
66+apu_start(Apu *c, Uint16 adsr, Uint8 pitch)
67+{
68+ if(pitch < 108 && c->len)
69+ c->advance = advances[pitch % 12] >> (8 - pitch / 12);
70+ else {
71+ c->advance = 0;
72+ return;
73+ }
74+ c->a = ADSR_STEP * (adsr >> 12);
75+ c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a;
76+ c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d;
77+ c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s;
78+ c->age = 0;
79+ c->i = 0;
80+ if(c->len <= 0x100) /* single cycle mode */
81+ c->period = NOTE_PERIOD * 337 / 2 / c->len;
82+ else /* sample repeat mode */
83+ c->period = NOTE_PERIOD;
84+}
85+
86+Uint8
87+apu_get_vu(Apu *c)
88+{
89+ int i;
90+ Sint32 sum[2] = {0, 0};
91+ if(!c->advance || !c->period) return 0;
92+ for(i = 0; i < 2; ++i) {
93+ if(!c->volume[i]) continue;
94+ sum[i] = 1 + envelope(c, c->age) * c->volume[i] / 0x800;
95+ if(sum[i] > 0xf) sum[i] = 0xf;
96+ }
97+ return (sum[0] << 4) | sum[1];
98+}
+28,
-0
1@@ -0,0 +1,28 @@
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+typedef signed int Sint32;
15+
16+#define SAMPLE_FREQUENCY 44100
17+
18+typedef struct {
19+ Uint8 *addr;
20+ Uint32 count, advance, period, age, a, d, s, r;
21+ Uint16 i, len;
22+ Sint8 volume[2];
23+ Uint8 pitch, repeat;
24+} Apu;
25+
26+int apu_render(Apu *c, Sint16 *sample, Sint16 *end);
27+void apu_start(Apu *c, Uint16 adsr, Uint8 pitch);
28+Uint8 apu_get_vu(Apu *c);
29+void apu_finished_handler(Apu *c);
+139,
-0
1@@ -0,0 +1,139 @@
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+}
+17,
-0
1@@ -0,0 +1,17 @@
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+Uint16 file_init(void *filename);
15+Uint16 file_read(void *dest, Uint16 len);
16+Uint16 file_write(void *src, Uint16 len, Uint8 flags);
17+Uint16 file_stat(void *dest, Uint16 len);
18+Uint16 file_delete(void);
+150,
-0
1@@ -0,0 +1,150 @@
2+#include "ppu.h"
3+
4+/*
5+Copyright (c) 2021 Devine Lu Linvega
6+Copyright (c) 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+static Uint8 blending[5][16] = {
17+ {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0},
18+ {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
19+ {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1},
20+ {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2},
21+ {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}};
22+
23+static Uint8 font[][8] = {
24+ {0x00, 0x7c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7c},
25+ {0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10},
26+ {0x00, 0x7c, 0x82, 0x02, 0x7c, 0x80, 0x80, 0xfe},
27+ {0x00, 0x7c, 0x82, 0x02, 0x1c, 0x02, 0x82, 0x7c},
28+ {0x00, 0x0c, 0x14, 0x24, 0x44, 0x84, 0xfe, 0x04},
29+ {0x00, 0xfe, 0x80, 0x80, 0x7c, 0x02, 0x82, 0x7c},
30+ {0x00, 0x7c, 0x82, 0x80, 0xfc, 0x82, 0x82, 0x7c},
31+ {0x00, 0x7c, 0x82, 0x02, 0x1e, 0x02, 0x02, 0x02},
32+ {0x00, 0x7c, 0x82, 0x82, 0x7c, 0x82, 0x82, 0x7c},
33+ {0x00, 0x7c, 0x82, 0x82, 0x7e, 0x02, 0x82, 0x7c},
34+ {0x00, 0x7c, 0x82, 0x02, 0x7e, 0x82, 0x82, 0x7e},
35+ {0x00, 0xfc, 0x82, 0x82, 0xfc, 0x82, 0x82, 0xfc},
36+ {0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7c},
37+ {0x00, 0xfc, 0x82, 0x82, 0x82, 0x82, 0x82, 0xfc},
38+ {0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x82, 0x7c},
39+ {0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x80, 0x80}};
40+
41+void
42+ppu_palette(Ppu *p, Uint8 *addr)
43+{
44+ int i, shift;
45+ for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
46+ Uint8
47+ r = (addr[0 + i / 2] >> shift) & 0x0f,
48+ g = (addr[2 + i / 2] >> shift) & 0x0f,
49+ b = (addr[4 + i / 2] >> shift) & 0x0f;
50+ p->palette[i] = 0x0f000000 | r << 16 | g << 8 | b;
51+ p->palette[i] |= p->palette[i] << 4;
52+ }
53+ p->fg.changed = p->bg.changed = 1;
54+}
55+
56+void
57+ppu_resize(Ppu *p, Uint16 width, Uint16 height)
58+{
59+ Uint8
60+ *bg = realloc(p->bg.pixels, width * height),
61+ *fg = realloc(p->fg.pixels, width * height);
62+ if(bg) p->bg.pixels = bg;
63+ if(fg) p->fg.pixels = fg;
64+ if(bg && fg) {
65+ p->width = width;
66+ p->height = height;
67+ ppu_clear(p, &p->bg);
68+ ppu_clear(p, &p->fg);
69+ }
70+}
71+
72+void
73+ppu_clear(Ppu *p, Layer *layer)
74+{
75+ Uint32 i, size = p->width * p->height;
76+ for(i = 0; i < size; ++i)
77+ layer->pixels[i] = 0x00;
78+ layer->changed = 1;
79+}
80+
81+#pragma weak ppu_redraw
82+void
83+ppu_redraw(Ppu *p, Uint32 *screen)
84+{
85+ Uint32 i, size = p->width * p->height, palette[16];
86+ for(i = 0; i < 16; ++i)
87+ palette[i] = p->palette[(i >> 2) ? (i >> 2) : (i & 3)];
88+ for(i = 0; i < size; ++i)
89+ screen[i] = palette[p->fg.pixels[i] << 2 | p->bg.pixels[i]];
90+ p->fg.changed = p->bg.changed = 0;
91+}
92+
93+void
94+ppu_write(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 color)
95+{
96+ if(x < p->width && y < p->height) {
97+ Uint32 i = x + y * p->width;
98+ Uint8 prev = layer->pixels[i];
99+ if(color != prev) {
100+ layer->pixels[i] = color;
101+ layer->changed = 1;
102+ }
103+ }
104+}
105+
106+void
107+ppu_blit(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy, Uint8 twobpp)
108+{
109+ int v, h, opaque = blending[4][color];
110+ for(v = 0; v < 8; ++v) {
111+ Uint16 c = sprite[v] | (twobpp ? sprite[v + 8] : 0) << 8;
112+ for(h = 7; h >= 0; --h, c >>= 1) {
113+ Uint8 ch = (c & 1) | ((c >> 7) & 2);
114+ if(opaque || ch)
115+ ppu_write(p,
116+ layer,
117+ x + (flipx ? 7 - h : h),
118+ y + (flipy ? 7 - v : v),
119+ blending[ch][color]);
120+ }
121+ }
122+}
123+
124+void
125+ppu_debug(Ppu *p, Uint8 *stack, Uint8 wptr, Uint8 rptr, Uint8 *memory)
126+{
127+ Uint8 i, x, y, b;
128+ for(i = 0; i < 0x20; ++i) {
129+ x = ((i % 8) * 3 + 1) * 8, y = (i / 8 + 1) * 8, b = stack[i];
130+ /* working stack */
131+ ppu_blit(p, &p->fg, x, y, font[(b >> 4) & 0xf], 1 + (wptr == i) * 0x7, 0, 0, 0);
132+ ppu_blit(p, &p->fg, x + 8, y, font[b & 0xf], 1 + (wptr == i) * 0x7, 0, 0, 0);
133+ y = 0x28 + (i / 8 + 1) * 8;
134+ b = memory[i];
135+ /* return stack */
136+ ppu_blit(p, &p->fg, x, y, font[(b >> 4) & 0xf], 3, 0, 0, 0);
137+ ppu_blit(p, &p->fg, x + 8, y, font[b & 0xf], 3, 0, 0, 0);
138+ }
139+ /* return pointer */
140+ ppu_blit(p, &p->fg, 0x8, y + 0x10, font[(rptr >> 4) & 0xf], 0x2, 0, 0, 0);
141+ ppu_blit(p, &p->fg, 0x10, y + 0x10, font[rptr & 0xf], 0x2, 0, 0, 0);
142+ /* guides */
143+ for(x = 0; x < 0x10; ++x) {
144+ ppu_write(p, &p->fg, x, p->height / 2, 2);
145+ ppu_write(p, &p->fg, p->width - x, p->height / 2, 2);
146+ ppu_write(p, &p->fg, p->width / 2, p->height - x, 2);
147+ ppu_write(p, &p->fg, p->width / 2, x, 2);
148+ ppu_write(p, &p->fg, p->width / 2 - 0x10 / 2 + x, p->height / 2, 2);
149+ ppu_write(p, &p->fg, p->width / 2, p->height / 2 - 0x10 / 2 + x, 2);
150+ }
151+}
+37,
-0
1@@ -0,0 +1,37 @@
2+#include <stdlib.h>
3+
4+/*
5+Copyright (c) 2021 Devine Lu Linvega
6+Copyright (c) 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+typedef unsigned char Uint8;
17+typedef unsigned short Uint16;
18+typedef unsigned int Uint32;
19+
20+typedef struct Layer {
21+ Uint8 *pixels;
22+ Uint8 changed;
23+} Layer;
24+
25+typedef struct Ppu {
26+ Uint32 palette[4];
27+ Uint16 width, height;
28+ Layer fg, bg;
29+} Ppu;
30+
31+void ppu_palette(Ppu *p, Uint8 *addr);
32+void ppu_resize(Ppu *p, Uint16 width, Uint16 height);
33+void ppu_clear(Ppu *p, Layer *layer);
34+void ppu_redraw(Ppu *p, Uint32 *screen);
35+
36+void ppu_write(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 color);
37+void ppu_blit(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy, Uint8 twobpp);
38+void ppu_debug(Ppu *p, Uint8 *stack, Uint8 wptr, Uint8 rptr, Uint8 *memory);
+37,
-0
1@@ -0,0 +1,37 @@
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
A
uxn.c
+158,
-0
1@@ -0,0 +1,158 @@
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+}
A
uxn.h
+50,
-0
1@@ -0,0 +1,50 @@
2+/*
3+Copyright (c) 2021 Devine Lu Linvega
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 unsigned char Uint8;
14+typedef signed char Sint8;
15+typedef unsigned short Uint16;
16+typedef signed short Sint16;
17+typedef unsigned int Uint32;
18+
19+#define PAGE_PROGRAM 0x0100
20+
21+typedef struct {
22+ Uint8 ptr, kptr, error;
23+ Uint8 dat[256];
24+} Stack;
25+
26+typedef struct {
27+ Uint16 ptr;
28+ Uint8 dat[65536];
29+} Memory;
30+
31+typedef struct Device {
32+ struct Uxn *u;
33+ Uint8 addr, dat[16], *mem;
34+ Uint16 vector;
35+ Uint8 (*dei)(struct Device *d, Uint8);
36+ void (*deo)(struct Device *d, Uint8);
37+} Device;
38+
39+typedef struct Uxn {
40+ Stack wst, rst, *src, *dst;
41+ Memory ram;
42+ Device dev[16];
43+} Uxn;
44+
45+void poke16(Uint8 *m, Uint16 a, Uint16 b);
46+Uint16 peek16(Uint8 *m, Uint16 a);
47+
48+int uxn_boot(Uxn *c);
49+int uxn_eval(Uxn *u, Uint16 vec);
50+int uxn_halt(Uxn *u, Uint8 error, char *name, int id);
51+Device *uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *, Uint8), void (*deofn)(Device *, Uint8));
A
uxn11.c
+266,
-0
1@@ -0,0 +1,266 @@
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+}
A
uxncli.c
+214,
-0
1@@ -0,0 +1,214 @@
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+}
A
uxnemu.c
+577,
-0
1@@ -0,0 +1,577 @@
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+}