Browse code

Screen device is half ported to new device handlers

Devine Lu Linvega authored on 01/01/2023 21:19:40
Showing 13 changed files
... ...
@@ -117,11 +117,9 @@ echo "Assembling(asma).."
117 117
 if [ $norun = 1 ]; then exit; fi
118 118
 
119 119
 echo "Assembling(piano).."
120
-bin/uxncli bin/asma.rom projects/software/piano.tal bin/piano.rom 2> bin/piano.log
120
+./bin/uxnasm projects/software/piano.tal bin/piano.rom 2> bin/piano.log
121 121
 
122 122
 echo "Running.."
123
-cd bin
124
-./uxnemu piano.rom
123
+./bin/uxnemu bin/piano.rom
125 124
 
126 125
 echo "Done."
127
-cd ..
... ...
@@ -4,8 +4,7 @@
4 4
 #include "datetime.h"
5 5
 
6 6
 /*
7
-Copyright (c) 2021 Devine Lu Linvega
8
-Copyright (c) 2021 Andrew Alderwick
7
+Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
9 8
 
10 9
 Permission to use, copy, modify, and distribute this software for any
11 10
 purpose with or without fee is hereby granted, provided that the above
... ...
@@ -16,7 +15,7 @@ WITH REGARD TO THIS SOFTWARE.
16 15
 */
17 16
 
18 17
 Uint8
19
-datetime_dei(Device *d, Uint8 port)
18
+datetime_dei(Uint8 *d, Uint8 port)
20 19
 {
21 20
 	time_t seconds = time(NULL);
22 21
 	struct tm zt = {0};
... ...
@@ -35,6 +34,6 @@ datetime_dei(Device *d, Uint8 port)
35 34
 	case 0x8: return t->tm_yday >> 8;
36 35
 	case 0x9: return t->tm_yday;
37 36
 	case 0xa: return t->tm_isdst;
38
-	default: return d->dat[port];
37
+	default: return d[port];
39 38
 	}
40 39
 }
... ...
@@ -1,6 +1,5 @@
1 1
 /*
2
-Copyright (c) 2021 Devine Lu Linvega
3
-Copyright (c) 2021 Andrew Alderwick
2
+Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
4 3
 
5 4
 Permission to use, copy, modify, and distribute this software for any
6 5
 purpose with or without fee is hereby granted, provided that the above
... ...
@@ -10,4 +9,4 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 9
 WITH REGARD TO THIS SOFTWARE.
11 10
 */
12 11
 
13
-Uint8 datetime_dei(Device *d, Uint8 port);
12
+Uint8 datetime_dei(Uint8 *d, Uint8 port);
... ...
@@ -1,15 +1,22 @@
1
+#define _XOPEN_SOURCE 500
1 2
 #include <stdio.h>
2 3
 #include <dirent.h>
4
+#include <errno.h>
5
+#include <limits.h>
3 6
 #include <string.h>
7
+#include <stdlib.h>
4 8
 #include <sys/stat.h>
5 9
 #include <unistd.h>
6 10
 
11
+#ifndef PATH_MAX
12
+#define PATH_MAX 4096
13
+#endif
14
+
7 15
 #include "../uxn.h"
8 16
 #include "file.h"
9 17
 
10 18
 /*
11
-Copyright (c) 2021 Devine Lu Linvega
12
-Copyright (c) 2021 Andrew Alderwick
19
+Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
13 20
 
14 21
 Permission to use, copy, modify, and distribute this software for any
15 22
 purpose with or without fee is hereby granted, provided that the above
... ...
@@ -28,6 +35,7 @@ typedef struct {
28 35
 		FILE_READ,
29 36
 		FILE_WRITE,
30 37
 		DIR_READ } state;
38
+	int outside_sandbox;
31 39
 } UxnFile;
32 40
 
33 41
 static UxnFile uxn_file[POLYFILEY];
... ...
@@ -45,6 +53,7 @@ reset(UxnFile *c)
45 53
 	}
46 54
 	c->de = NULL;
47 55
 	c->state = IDLE;
56
+	c->outside_sandbox = 0;
48 57
 }
49 58
 
50 59
 static Uint16
... ...
@@ -66,13 +75,24 @@ get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int f
66 75
 static Uint16
67 76
 file_read_dir(UxnFile *c, char *dest, Uint16 len)
68 77
 {
69
-	static char pathname[4356];
78
+	static char pathname[4352];
70 79
 	char *p = dest;
71 80
 	if(c->de == NULL) c->de = readdir(c->dir);
72 81
 	for(; c->de != NULL; c->de = readdir(c->dir)) {
73 82
 		Uint16 n;
74 83
 		if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0')
75 84
 			continue;
85
+		if(strcmp(c->de->d_name, "..") == 0) {
86
+			/* hide "sandbox/.." */
87
+			char cwd[PATH_MAX] = {'\0'}, t[PATH_MAX] = {'\0'};
88
+			/* Note there's [currently] no way of chdir()ing from uxn, so $PWD
89
+			 * is always the sandbox top level. */
90
+			getcwd(cwd, sizeof(cwd));
91
+			/* We already checked that c->current_filename exists so don't need a wrapper. */
92
+			realpath(c->current_filename, t);
93
+			if(strcmp(cwd, t) == 0)
94
+				continue;
95
+		}
76 96
 		if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname))
77 97
 			sprintf(pathname, "%s/%s", c->current_filename, c->de->d_name);
78 98
 		else
... ...
@@ -85,16 +105,62 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
85 105
 	return p - dest;
86 106
 }
87 107
 
108
+static char *
109
+retry_realpath(const char *file_name)
110
+{
111
+	char r[PATH_MAX] = {'\0'}, p[PATH_MAX] = {'\0'}, *x;
112
+	if(file_name == NULL) {
113
+		errno = EINVAL;
114
+		return NULL;
115
+	} else if(strlen(file_name) >= PATH_MAX) {
116
+		errno = ENAMETOOLONG;
117
+		return NULL;
118
+	}
119
+	if(file_name[0] != '/') {
120
+		/* TODO: use a macro instead of '/' for absolute path first character so that other systems can work */
121
+		/* if a relative path, prepend cwd */
122
+		getcwd(p, sizeof(p));
123
+		strcat(p, "/"); /* TODO: use a macro instead of '/' for the path delimiter */
124
+	}
125
+	strcat(p, file_name);
126
+	while(realpath(p, r) == NULL) {
127
+		if(errno != ENOENT)
128
+			return NULL;
129
+		x = strrchr(p, '/'); /* TODO: path delimiter macro */
130
+		if(x)
131
+			*x = '\0';
132
+		else
133
+			return NULL;
134
+	}
135
+	return strdup(r);
136
+}
137
+
138
+static void
139
+file_check_sandbox(UxnFile *c)
140
+{
141
+	char *x, *rp, cwd[PATH_MAX] = {'\0'};
142
+	x = getcwd(cwd, sizeof(cwd));
143
+	rp = retry_realpath(c->current_filename);
144
+	if(rp == NULL || (x && strncmp(cwd, rp, strlen(cwd)) != 0)) {
145
+		c->outside_sandbox = 1;
146
+		fprintf(stderr, "file warning: blocked attempt to access %s outside of sandbox\n", c->current_filename);
147
+	}
148
+	free(rp);
149
+}
150
+
88 151
 static Uint16
89
-file_init(UxnFile *c, char *filename, size_t max_len)
152
+file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox)
90 153
 {
91 154
 	char *p = c->current_filename;
92 155
 	size_t len = sizeof(c->current_filename);
93 156
 	reset(c);
94 157
 	if(len > max_len) len = max_len;
95 158
 	while(len) {
96
-		if((*p++ = *filename++) == '\0')
159
+		if((*p++ = *filename++) == '\0') {
160
+			if(!override_sandbox) /* override sandbox for loading roms */
161
+				file_check_sandbox(c);
97 162
 			return 0;
163
+		}
98 164
 		len--;
99 165
 	}
100 166
 	c->current_filename[0] = '\0';
... ...
@@ -104,6 +170,7 @@ file_init(UxnFile *c, char *filename, size_t max_len)
104 170
 static Uint16
105 171
 file_read(UxnFile *c, void *dest, Uint16 len)
106 172
 {
173
+	if(c->outside_sandbox) return 0;
107 174
 	if(c->state != FILE_READ && c->state != DIR_READ) {
108 175
 		reset(c);
109 176
 		if((c->dir = opendir(c->current_filename)) != NULL)
... ...
@@ -122,6 +189,7 @@ static Uint16
122 189
 file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
123 190
 {
124 191
 	Uint16 ret = 0;
192
+	if(c->outside_sandbox) return 0;
125 193
 	if(c->state != FILE_WRITE) {
126 194
 		reset(c);
127 195
 		if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
... ...
@@ -138,6 +206,7 @@ static Uint16
138 206
 file_stat(UxnFile *c, void *dest, Uint16 len)
139 207
 {
140 208
 	char *basename = strrchr(c->current_filename, '/');
209
+	if(c->outside_sandbox) return 0;
141 210
 	if(basename != NULL)
142 211
 		basename++;
143 212
 	else
... ...
@@ -148,72 +217,66 @@ file_stat(UxnFile *c, void *dest, Uint16 len)
148 217
 static Uint16
149 218
 file_delete(UxnFile *c)
150 219
 {
151
-	return unlink(c->current_filename);
152
-}
153
-
154
-static UxnFile *
155
-file_instance(Device *d)
156
-{
157
-	return &uxn_file[d - &d->u->devold[DEV_FILE0]];
220
+	return c->outside_sandbox ? 0 : unlink(c->current_filename);
158 221
 }
159 222
 
160 223
 /* IO */
161 224
 
162 225
 void
163
-file_deo(Device *d, Uint8 port)
226
+file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port)
164 227
 {
165
-	UxnFile *c = file_instance(d);
228
+	UxnFile *c = &uxn_file[id];
166 229
 	Uint16 addr, len, res;
167 230
 	switch(port) {
168 231
 	case 0x5:
169
-		DEVPEEK16(addr, 0x4);
170
-		DEVPEEK16(len, 0xa);
232
+		PEKDEV(addr, 0x4);
233
+		PEKDEV(len, 0xa);
171 234
 		if(len > 0x10000 - addr)
172 235
 			len = 0x10000 - addr;
173
-		res = file_stat(c, &d->u->ram[addr], len);
174
-		DEVPOKE16(0x2, res);
236
+		res = file_stat(c, &ram[addr], len);
237
+		POKDEV(0x2, res);
175 238
 		break;
176 239
 	case 0x6:
177 240
 		res = file_delete(c);
178
-		DEVPOKE16(0x2, res);
241
+		POKDEV(0x2, res);
179 242
 		break;
180 243
 	case 0x9:
181
-		DEVPEEK16(addr, 0x8);
182
-		res = file_init(c, (char *)&d->u->ram[addr], 0x10000 - addr);
183
-		DEVPOKE16(0x2, res);
244
+		PEKDEV(addr, 0x8);
245
+		res = file_init(c, (char *)&ram[addr], 0x10000 - addr, 0);
246
+		POKDEV(0x2, res);
184 247
 		break;
185 248
 	case 0xd:
186
-		DEVPEEK16(addr, 0xc);
187
-		DEVPEEK16(len, 0xa);
249
+		PEKDEV(addr, 0xc);
250
+		PEKDEV(len, 0xa);
188 251
 		if(len > 0x10000 - addr)
189 252
 			len = 0x10000 - addr;
190
-		res = file_read(c, &d->u->ram[addr], len);
191
-		DEVPOKE16(0x2, res);
253
+		res = file_read(c, &ram[addr], len);
254
+		POKDEV(0x2, res);
192 255
 		break;
193 256
 	case 0xf:
194
-		DEVPEEK16(addr, 0xe);
195
-		DEVPEEK16(len, 0xa);
257
+		PEKDEV(addr, 0xe);
258
+		PEKDEV(len, 0xa);
196 259
 		if(len > 0x10000 - addr)
197 260
 			len = 0x10000 - addr;
198
-		res = file_write(c, &d->u->ram[addr], len, d->dat[0x7]);
199
-		DEVPOKE16(0x2, res);
261
+		res = file_write(c, &ram[addr], len, d[0x7]);
262
+		POKDEV(0x2, res);
200 263
 		break;
201 264
 	}
202 265
 }
203 266
 
204 267
 Uint8
205
-file_dei(Device *d, Uint8 port)
268
+file_dei(Uint8 id, Uint8 *d, Uint8 port)
206 269
 {
207
-	UxnFile *c = file_instance(d);
270
+	UxnFile *c = &uxn_file[id];
208 271
 	Uint16 res;
209 272
 	switch(port) {
210 273
 	case 0xc:
211 274
 	case 0xd:
212
-		res = file_read(c, &d->dat[port], 1);
213
-		DEVPOKE16(0x2, res);
275
+		res = file_read(c, &d[port], 1);
276
+		POKDEV(0x2, res);
214 277
 		break;
215 278
 	}
216
-	return d->dat[port];
279
+	return d[port];
217 280
 }
218 281
 
219 282
 /* Boot */
... ...
@@ -222,7 +285,7 @@ int
222 285
 load_rom(Uxn *u, char *filename)
223 286
 {
224 287
 	int ret;
225
-	file_init(uxn_file, filename, strlen(filename) + 1);
288
+	file_init(uxn_file, filename, strlen(filename) + 1, 1);
226 289
 	ret = file_read(uxn_file, &u->ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM);
227 290
 	reset(uxn_file);
228 291
 	return ret;
... ...
@@ -1,6 +1,5 @@
1 1
 /*
2
-Copyright (c) 2021 Devine Lu Linvega
3
-Copyright (c) 2021 Andrew Alderwick
2
+Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick
4 3
 
5 4
 Permission to use, copy, modify, and distribute this software for any
6 5
 purpose with or without fee is hereby granted, provided that the above
... ...
@@ -13,6 +12,6 @@ WITH REGARD TO THIS SOFTWARE.
13 12
 #define POLYFILEY 2
14 13
 #define DEV_FILE0 0xa
15 14
 
16
-void file_deo(Device *d, Uint8 port);
17
-Uint8 file_dei(Device *d, Uint8 port);
15
+void file_deo(Uint8 id, Uint8 *ram, Uint8 *d, Uint8 port);
16
+Uint8 file_dei(Uint8 id, Uint8 *d, Uint8 port);
18 17
 int load_rom(Uxn *u, char *filename);
... ...
@@ -132,14 +132,14 @@ screen_mono(UxnScreen *p, Uint32 *pixels)
132 132
 /* IO */
133 133
 
134 134
 Uint8
135
-screen_dei(Device *d, Uint8 port)
135
+screen_dei(Uint8 *d, Uint8 port)
136 136
 {
137 137
 	switch(port) {
138 138
 	case 0x2: return uxn_screen.width >> 8;
139 139
 	case 0x3: return uxn_screen.width;
140 140
 	case 0x4: return uxn_screen.height >> 8;
141 141
 	case 0x5: return uxn_screen.height;
142
-	default: return d->dat[port];
142
+	default: return d[port];
143 143
 	}
144 144
 }
145 145
 
... ...
@@ -31,6 +31,6 @@ void screen_clear(UxnScreen *p, Layer *layer);
31 31
 void screen_redraw(UxnScreen *p, Uint32 *pixels);
32 32
 void screen_mono(UxnScreen *p, Uint32 *pixels);
33 33
 
34
-Uint8 screen_dei(Device *d, Uint8 port);
34
+Uint8 screen_dei(Uint8 *d, Uint8 port);
35 35
 void screen_deo(Device *d, Uint8 port);
36 36
 int clamp(int val, int min, int max);
... ...
@@ -15,13 +15,9 @@ WITH REGARD TO THIS SOFTWARE.
15 15
 */
16 16
 
17 17
 static const char *errors[] = {
18
-	"Working-stack underflow",
19
-	"Return-stack underflow",
20
-	"Working-stack overflow",
21
-	"Return-stack overflow",
22
-	"Working-stack division by zero",
23
-	"Return-stack division by zero",
24
-	"Execution timeout"};
18
+	"underflow",
19
+	"overflow",
20
+	"division by zero"};
25 21
 
26 22
 static void
27 23
 system_print(Stack *s, char *name)
... ...
@@ -38,37 +34,37 @@ system_print(Stack *s, char *name)
38 34
 void
39 35
 system_inspect(Uxn *u)
40 36
 {
41
-	system_print(&u->wst, "wst");
42
-	system_print(&u->rst, "rst");
37
+	system_print(u->wst, "wst");
38
+	system_print(u->rst, "rst");
43 39
 }
44 40
 
45 41
 int
46
-uxn_halt(Uxn *u, Uint8 error, Uint16 addr)
42
+uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr)
47 43
 {
48
-	system_inspect(u);
49
-	fprintf(stderr, "Halted: %s#%04x, at 0x%04x\n", errors[error], u->ram[addr], addr);
44
+	Uint8 *d = &u->dev[0x00];
45
+	if(instr & 0x40)
46
+		u->rst->err = err;
47
+	else
48
+		u->wst->err = err;
49
+	if(GETVEC(d))
50
+		uxn_eval(u, GETVEC(d));
51
+	else {
52
+		system_inspect(u);
53
+		fprintf(stderr, "%s %s, by %02x at 0x%04x.\n", (instr & 0x40) ? "Return-stack" : "Working-stack", errors[err - 1], instr, addr);
54
+	}
50 55
 	return 0;
51 56
 }
52 57
 
53 58
 /* IO */
54 59
 
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 60
 void
66
-system_deo(Device *d, Uint8 port)
61
+system_deo(Uxn *u, Uint8 *d, Uint8 port)
67 62
 {
68 63
 	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);
64
+	case 0x2: u->wst = (Stack *)(u->ram + (d[port] ? (d[port] * 0x100) : 0x10000)); break;
65
+	case 0x3: u->rst = (Stack *)(u->ram + (d[port] ? (d[port] * 0x100) : 0x10100)); break;
66
+	case 0xe:
67
+		if(u->wst->ptr || u->rst->ptr) system_inspect(u);
68
+		break;
73 69
 	}
74 70
 }
... ...
@@ -9,13 +9,5 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 9
 WITH REGARD TO THIS SOFTWARE.
10 10
 */
11 11
 
12
-typedef struct SystemDevice {
13
-	Device device;
14
-	struct UxnScreen *screen;
15
-} SystemDevice;
16
-
17 12
 void system_inspect(Uxn *u);
18
-
19
-Uint8 system_dei(Device *d, Uint8 port);
20
-void system_deo(Device *d, Uint8 port);
21
-void system_deo_special(Device *d, Uint8 port);
13
+void system_deo(Uxn *u, Uint8 *d, Uint8 port);
... ...
@@ -31,6 +31,9 @@ WITH REGARD TO THIS SOFTWARE.
31 31
 #define DEVW8OLD(x, y) { dev->dat[(x) & 0xf] = y; dev->deo(dev, (x) & 0x0f); }
32 32
 #define DEVWOLD(d, x, y) { dev = (d); if(bs) { DEVW8OLD((x), (y) >> 8); DEVW8OLD((x) + 1, (y)); } else { DEVW8OLD((x), (y)) } }
33 33
 
34
+#define DEVR(o, x) { o = u->dei(u, x); if (bs) o = (o << 8) + u->dei(u, ((x) + 1) & 0xFF); }
35
+#define DEVW(x, y) { if (bs) { u->deo(u, (x), (y) >> 8); u->deo(u, ((x) + 1) & 0xFF, (y)); } else { u->deo(u, x, (y)); } }
36
+
34 37
 #define WARP(x) { if(bs) pc = (x); else pc += (Sint8)(x); }
35 38
 #define LIMIT 0x40000 /* around 3 ms */
36 39
 
... ...
@@ -41,21 +44,16 @@ uxn_eval(Uxn *u, Uint16 pc)
41 44
 	unsigned int limit = LIMIT;
42 45
 	Uint8 kptr, *sp;
43 46
 	Stack *src, *dst;
44
-	Device *dev;
45 47
 	if(!pc || u->devold[0].dat[0xf]) return 0;
46 48
 	while((instr = u->ram[pc++])) {
47 49
 		if(!limit--) {
48
-			if(!uxn_interrupt()) {
49
-				errcode = 6;
50
-				goto timeout;
51
-			}
52 50
 			limit = LIMIT;
53 51
 		}
54 52
 		/* Return Mode */
55 53
 		if(instr & 0x40) {
56
-			src = &u->rst; dst = &u->wst;
54
+			src = u->rst; dst = u->wst;
57 55
 		} else {
58
-			src = &u->wst; dst = &u->rst;
56
+			src = u->wst; dst = u->rst;
59 57
 		}
60 58
 		/* Keep Mode */
61 59
 		if(instr & 0x80) {
... ...
@@ -92,8 +90,8 @@ uxn_eval(Uxn *u, Uint16 pc)
92 90
 		case 0x13: /* STR */ POP8(a) POP(b) c = pc + (Sint8)a; POKE(c, b) break;
93 91
 		case 0x14: /* LDA */ POP16(a) PEEK(b, a) PUSH(src, b) break;
94 92
 		case 0x15: /* STA */ POP16(a) POP(b) POKE(a, b) break;
95
-		case 0x16: /* DEI */ POP8(a) DEVROLD(b, &u->devold[a >> 4], a) PUSH(src, b) break;
96
-		case 0x17: /* DEO */ POP8(a) POP(b) DEVWOLD(&u->devold[a >> 4], a, b) break;
93
+		case 0x16: /* DEI */ POP8(a) DEVR(b, a) PUSH(src, b) break;
94
+		case 0x17: /* DEO */ POP8(a) POP(b) DEVW(a, b) break;
97 95
 		/* Arithmetic */
98 96
 		case 0x18: /* ADD */ POP(a) POP(b) PUSH(src, b + a) break;
99 97
 		case 0x19: /* SUB */ POP(a) POP(b) PUSH(src, b - a) break;
... ...
@@ -106,13 +104,8 @@ uxn_eval(Uxn *u, Uint16 pc)
106 104
 		}
107 105
 	}
108 106
 	return 1;
109
-
110 107
 err:
111
-	/* set 1 in errcode if it involved the return stack instead of the working stack */
112
-	/*        (stack overflow & ( opcode was STH / JSR )) ^ Return Mode */
113
-	errcode |= ((errcode >> 1 & ((instr & 0x1e) == 0x0e)) ^ instr >> 6) & 1;
114
-timeout:
115
-	return uxn_halt(u, errcode, pc - 1);
108
+	return uxn_halt(u, instr, errcode, pc - 1);
116 109
 }
117 110
 
118 111
 /* clang-format on */
... ...
@@ -125,6 +118,8 @@ uxn_boot(Uxn *u, Uint8 *ram, Dei *dei, Deo *deo)
125 118
 	for(i = 0; i < sizeof(*u); i++)
126 119
 		cptr[i] = 0x00;
127 120
 	u->ram = ram;
121
+	u->wst = (Stack *)(ram + 0x10000);
122
+	u->rst = (Stack *)(ram + 0x10100);
128 123
 	u->dev = (Uint8 *)(ram + 0x10200);
129 124
 	u->dei = dei;
130 125
 	u->deo = deo;
... ...
@@ -20,13 +20,25 @@ typedef unsigned int Uint32;
20 20
 
21 21
 #define PAGE_PROGRAM 0x0100
22 22
 
23
-/* clang-format off */
24
-
25
-#define DEVPEEK16(o, x) { (o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; }
26
-#define DEVPOKE16(x, y) { d->dat[(x)] = (y) >> 8; d->dat[(x) + 1] = (y); }
23
+#define DEVPEEK16(o, x) \
24
+	{ \
25
+		(o) = (d->dat[(x)] << 8) + d->dat[(x) + 1]; \
26
+	}
27
+#define DEVPOKE16(x, y) \
28
+	{ \
29
+		d->dat[(x)] = (y) >> 8; \
30
+		d->dat[(x) + 1] = (y); \
31
+	}
27 32
 #define GETVECTOR(d) ((d)->dat[0] << 8 | (d)->dat[1])
28 33
 
29
-/* new macros */
34
+typedef struct Device {
35
+	struct Uxn *u;
36
+	Uint8 dat[16];
37
+	Uint8 (*dei)(struct Device *d, Uint8);
38
+	void (*deo)(struct Device *d, Uint8);
39
+} Device;
40
+
41
+/* clang-format off */
30 42
 
31 43
 #define GETVEC(d) ((d)[0] << 8 | (d)[1])
32 44
 #define POKDEV(x, y) { d[(x)] = (y) >> 8; d[(x) + 1] = (y); }
... ...
@@ -35,19 +47,12 @@ typedef unsigned int Uint32;
35 47
 /* clang-format on */
36 48
 
37 49
 typedef struct {
38
-	Uint8 ptr, dat[255];
50
+	Uint8 dat[254], err, ptr;
39 51
 } Stack;
40 52
 
41
-typedef struct Device {
42
-	struct Uxn *u;
43
-	Uint8 dat[16];
44
-	Uint8 (*dei)(struct Device *d, Uint8);
45
-	void (*deo)(struct Device *d, Uint8);
46
-} Device;
47
-
48 53
 typedef struct Uxn {
49 54
 	Uint8 *ram, *dev;
50
-	Stack wst, rst;
55
+	Stack *wst, *rst;
51 56
 	Device devold[16];
52 57
 	Uint8 (*dei)(struct Uxn *u, Uint8 addr);
53 58
 	void (*deo)(struct Uxn *u, Uint8 addr, Uint8 value);
... ...
@@ -58,7 +63,9 @@ typedef void Deo(Uxn *u, Uint8 addr, Uint8 value);
58 63
 
59 64
 int uxn_boot(Uxn *u, Uint8 *ram, Dei *dei, Deo *deo);
60 65
 int uxn_eval(Uxn *u, Uint16 pc);
61
-int uxn_interrupt(void);
62
-int uxn_halt(Uxn *u, Uint8 error, Uint16 addr);
66
+int uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr);
67
+
68
+/* TODO: remove */
69
+
63 70
 Device *uxn_port(Uxn *u, Uint8 id, Uint8 (*deifn)(Device *, Uint8), void (*deofn)(Device *, Uint8));
64 71
 #endif /* UXN_UXN_H */
... ...
@@ -17,102 +17,60 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17 17
 WITH REGARD TO THIS SOFTWARE.
18 18
 */
19 19
 
20
+#define SUPPORT 0x1c03 /* devices mask */
21
+
20 22
 static int
21
-error(char *msg, const char *err)
23
+emu_error(char *msg, const char *err)
22 24
 {
23 25
 	fprintf(stderr, "Error %s: %s\n", msg, err);
24 26
 	return 0;
25 27
 }
26 28
 
27
-static Uint8
28
-emu_dei(Uxn *u, Uint8 addr)
29
-{
30
-	return 0;
31
-}
32
-
33
-static void
34
-emu_deo(Uxn *u, Uint8 addr, Uint8 v)
35
-{
36
-}
37
-
38
-void
39
-system_deo_special(Device *d, Uint8 port)
29
+static int
30
+console_input(Uxn *u, char c)
40 31
 {
41
-	(void)d;
42
-	(void)port;
32
+	Uint8 *d = &u->dev[0x10];
33
+	d[0x02] = c;
34
+	return uxn_eval(u, GETVEC(d));
43 35
 }
44 36
 
45 37
 static void
46
-console_deo(Device *d, Uint8 port)
38
+console_deo(Uint8 *d, Uint8 port)
47 39
 {
48
-	FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr
49
-												  : 0;
40
+	FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr :
41
+                                                    0;
50 42
 	if(fd) {
51
-		fputc(d->dat[port], fd);
43
+		fputc(d[port], fd);
52 44
 		fflush(fd);
53 45
 	}
54 46
 }
55 47
 
56 48
 static Uint8
57
-nil_dei(Device *d, Uint8 port)
58
-{
59
-	return d->dat[port];
60
-}
61
-
62
-static void
63
-nil_deo(Device *d, Uint8 port)
64
-{
65
-	(void)d;
66
-	(void)port;
67
-}
68
-
69
-static int
70
-console_input(Uxn *u, char c)
49
+emu_dei(Uxn *u, Uint8 addr)
71 50
 {
72
-	Device *d = &u->devold[1];
73
-	d->dat[0x2] = c;
74
-	return uxn_eval(u, GETVECTOR(d));
51
+	Uint8 p = addr & 0x0f, d = addr & 0xf0;
52
+	switch(d) {
53
+	case 0xa0: return file_dei(0, &u->dev[d], p);
54
+	case 0xb0: return file_dei(1, &u->dev[d], p);
55
+	case 0xc0: return datetime_dei(&u->dev[d], p);
56
+	}
57
+	return u->dev[addr];
75 58
 }
76 59
 
77 60
 static void
78
-run(Uxn *u)
61
+emu_deo(Uxn *u, Uint8 addr, Uint8 v)
79 62
 {
80
-	Device *d = &u->devold[0];
81
-	while(!d->dat[0xf]) {
82
-		int c = fgetc(stdin);
83
-		if(c != EOF)
84
-			console_input(u, (Uint8)c);
63
+	Uint8 p = addr & 0x0f, d = addr & 0xf0;
64
+	Uint16 mask = 0x1 << (d >> 4);
65
+	u->dev[addr] = v;
66
+	switch(d) {
67
+	case 0x00: system_deo(u, &u->dev[d], p); break;
68
+	case 0x10: console_deo(&u->dev[d], p); break;
69
+	case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break;
70
+	case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break;
85 71
 	}
86
-}
87
-
88
-int
89
-uxn_interrupt(void)
90
-{
91
-	return 1;
92
-}
93
-
94
-static int
95
-start(Uxn *u)
96
-{
97
-	if(!uxn_boot(u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo))
98
-		return error("Boot", "Failed");
99
-	/* system   */ uxn_port(u, 0x0, system_dei, system_deo);
100
-	/* console  */ uxn_port(u, 0x1, nil_dei, console_deo);
101
-	/* empty    */ uxn_port(u, 0x2, nil_dei, nil_deo);
102
-	/* empty    */ uxn_port(u, 0x3, nil_dei, nil_deo);
103
-	/* empty    */ uxn_port(u, 0x4, nil_dei, nil_deo);
104
-	/* empty    */ uxn_port(u, 0x5, nil_dei, nil_deo);
105
-	/* empty    */ uxn_port(u, 0x6, nil_dei, nil_deo);
106
-	/* empty    */ uxn_port(u, 0x7, nil_dei, nil_deo);
107
-	/* empty    */ uxn_port(u, 0x8, nil_dei, nil_deo);
108
-	/* empty    */ uxn_port(u, 0x9, nil_dei, nil_deo);
109
-	/* file0    */ uxn_port(u, 0xa, file_dei, file_deo);
110
-	/* file1    */ uxn_port(u, 0xb, file_dei, file_deo);
111
-	/* datetime */ uxn_port(u, 0xc, datetime_dei, nil_deo);
112
-	/* empty    */ uxn_port(u, 0xd, nil_dei, nil_deo);
113
-	/* empty    */ uxn_port(u, 0xe, nil_dei, nil_deo);
114
-	/* empty    */ uxn_port(u, 0xf, nil_dei, nil_deo);
115
-	return 1;
72
+	if(p == 0x01 && !(SUPPORT & mask))
73
+		fprintf(stderr, "Warning: Incompatible emulation, device: %02x.\n", d);
116 74
 }
117 75
 
118 76
 int
... ...
@@ -121,19 +79,22 @@ main(int argc, char **argv)
121 79
 	Uxn u;
122 80
 	int i;
123 81
 	if(argc < 2)
124
-		return error("Usage", "uxncli game.rom args");
125
-	if(!start(&u))
126
-		return error("Start", "Failed");
82
+		return emu_error("Usage", "uxncli game.rom args");
83
+	if(!uxn_boot(&u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo))
84
+		return emu_error("Boot", "Failed");
127 85
 	if(!load_rom(&u, argv[1]))
128
-		return error("Load", "Failed");
129
-	fprintf(stderr, "Loaded %s\n", argv[1]);
86
+		return emu_error("Load", "Failed");
130 87
 	if(!uxn_eval(&u, PAGE_PROGRAM))
131
-		return error("Init", "Failed");
88
+		return emu_error("Init", "Failed");
132 89
 	for(i = 2; i < argc; i++) {
133 90
 		char *p = argv[i];
134 91
 		while(*p) console_input(&u, *p++);
135 92
 		console_input(&u, '\n');
136 93
 	}
137
-	run(&u);
94
+	while(!u.dev[0x0f]) {
95
+		int c = fgetc(stdin);
96
+		if(c != EOF)
97
+			console_input(&u, (Uint8)c);
98
+	}
138 99
 	return 0;
139 100
 }
... ...
@@ -63,6 +63,25 @@ error(char *msg, const char *err)
63 63
 	return 0;
64 64
 }
65 65
 
66
+static int
67
+console_input(Uxn *u, char c)
68
+{
69
+	Uint8 *d = &u->dev[0x10];
70
+	d[0x02] = c;
71
+	return uxn_eval(u, GETVEC(d));
72
+}
73
+
74
+static void
75
+console_deo(Uint8 *d, Uint8 port)
76
+{
77
+	FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr
78
+												  : 0;
79
+	if(fd) {
80
+		fputc(d[port], fd);
81
+		fflush(fd);
82
+	}
83
+}
84
+
66 85
 #pragma mark - Generics
67 86
 
68 87
 static void
... ...
@@ -179,12 +198,34 @@ init(void)
179 198
 static Uint8
180 199
 emu_dei(Uxn *u, Uint8 addr)
181 200
 {
201
+	Uint8 p = addr & 0x0f, d = addr & 0xf0;
202
+	switch(d) {
203
+	case 0x20: return screen_dei(&u->dev[d], p); 
204
+	case 0xa0: return file_dei(0, &u->dev[d], p);
205
+	case 0xb0: return file_dei(1, &u->dev[d], p);
206
+	case 0xc0: return datetime_dei(&u->dev[d], p);
207
+	}
208
+	return u->dev[addr];
182 209
 	return 0;
183 210
 }
184 211
 
185 212
 static void
186 213
 emu_deo(Uxn *u, Uint8 addr, Uint8 v)
187 214
 {
215
+Uint8 p = addr & 0x0f, d = addr & 0xf0;
216
+	Uint16 mask = 0x1 << (d >> 4);
217
+	u->dev[addr] = v;
218
+	switch(d) {
219
+	case 0x00:
220
+		system_deo(u, &u->dev[d], p);
221
+		if(p > 0x7 && p < 0xe)
222
+			screen_palette(&uxn_screen, &u->dev[0x8]);
223
+		break;
224
+	case 0x10: console_deo(&u->dev[d], p); break;
225
+	/* case 0x20: screen_deo(u->ram, &u->dev[d], p); break; */
226
+	case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break;
227
+	case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break;
228
+	}
188 229
 }
189 230
 
190 231
 void
... ...
@@ -194,17 +235,6 @@ system_deo_special(Device *d, Uint8 port)
194 235
 		screen_palette(&uxn_screen, &d->dat[0x8]);
195 236
 }
196 237
 
197
-static void
198
-console_deo(Device *d, Uint8 port)
199
-{
200
-	FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr
201
-												  : 0;
202
-	if(fd) {
203
-		fputc(d->dat[port], fd);
204
-		fflush(fd);
205
-	}
206
-}
207
-
208 238
 static Uint8
209 239
 audio_dei(Device *d, Uint8 port)
210 240
 {
... ...
@@ -230,19 +260,6 @@ audio_deo(Device *d, Uint8 port)
230 260
 	}
231 261
 }
232 262
 
233
-static Uint8
234
-nil_dei(Device *d, Uint8 port)
235
-{
236
-	return d->dat[port];
237
-}
238
-
239
-static void
240
-nil_deo(Device *d, Uint8 port)
241
-{
242
-	(void)d;
243
-	(void)port;
244
-}
245
-
246 263
 /* Boot */
247 264
 
248 265
 static int
... ...
@@ -264,26 +281,10 @@ static int
264 281
 start(Uxn *u, char *rom)
265 282
 {
266 283
 	free(u->ram);
267
-	if(!uxn_boot(u, calloc(0x10300, 1), emu_dei, emu_deo))
284
+	if(!uxn_boot(u, (Uint8 *)calloc(0x10300, sizeof(Uint8)), emu_dei, emu_deo))
268 285
 		return error("Boot", "Failed to start uxn.");
269 286
 	if(!load(u, rom))
270 287
 		return error("Boot", "Failed to load rom.");
271
-	/* system   */ uxn_port(u, 0x0, system_dei, system_deo);
272
-	/* console  */ uxn_port(u, 0x1, nil_dei, console_deo);
273
-	/* screen   */ devscreen = uxn_port(u, 0x2, screen_dei, screen_deo);
274
-	/* audio0   */ devaudio0 = uxn_port(u, 0x3, audio_dei, audio_deo);
275
-	/* audio1   */ uxn_port(u, 0x4, audio_dei, audio_deo);
276
-	/* audio2   */ uxn_port(u, 0x5, audio_dei, audio_deo);
277
-	/* audio3   */ uxn_port(u, 0x6, audio_dei, audio_deo);
278
-	/* unused   */ uxn_port(u, 0x7, nil_dei, nil_deo);
279
-	/* control  */ uxn_port(u, 0x8, nil_dei, nil_deo);
280
-	/* mouse    */ uxn_port(u, 0x9, nil_dei, nil_deo);
281
-	/* file0    */ uxn_port(u, 0xa, file_dei, file_deo);
282
-	/* file1    */ uxn_port(u, 0xb, file_dei, file_deo);
283
-	/* datetime */ uxn_port(u, 0xc, datetime_dei, nil_deo);
284
-	/* unused   */ uxn_port(u, 0xd, nil_dei, nil_deo);
285
-	/* unused   */ uxn_port(u, 0xe, nil_dei, nil_deo);
286
-	/* unused   */ uxn_port(u, 0xf, nil_dei, nil_deo);
287 288
 	exec_deadline = SDL_GetPerformanceCounter() + deadline_interval;
288 289
 	if(!uxn_eval(u, PAGE_PROGRAM))
289 290
 		return error("Boot", "Failed to start rom.");
... ...
@@ -383,14 +384,6 @@ do_shortcut(Uxn *u, SDL_Event *event)
383 384
 	}
384 385
 }
385 386
 
386
-static int
387
-console_input(Uxn *u, char c)
388
-{
389
-	Device *d = &u->devold[1];
390
-	d->dat[0x2] = c;
391
-	return uxn_eval(u, GETVECTOR(d));
392
-}
393
-
394 387
 static int
395 388
 handle_events(Uxn *u)
396 389
 {
... ...
@@ -408,8 +401,8 @@ handle_events(Uxn *u)
408 401
 		}
409 402
 		/* Audio */
410 403
 		else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY) {
411
-			Device *d = devaudio0 + (event.type - audio0_event);
412
-			uxn_eval(u, GETVECTOR(d));
404
+			/* Device *d = devaudio0 + (event.type - audio0_event);
405
+			uxn_eval(u, GETVECTOR(d)); */
413 406
 		}
414 407
 		/* Mouse */
415 408
 		else if(event.type == SDL_MOUSEMOTION)
... ...
@@ -467,7 +460,7 @@ run(Uxn *u)
467 460
 		exec_deadline = now + deadline_interval;
468 461
 		if(!handle_events(u))
469 462
 			return 0;
470
-		uxn_eval(u, GETVECTOR(devscreen));
463
+		uxn_eval(u, GETVEC(&u->dev[0x20]));
471 464
 		if(uxn_screen.fg.changed || uxn_screen.bg.changed)
472 465
 			redraw();
473 466
 		now = SDL_GetPerformanceCounter();
... ...
@@ -479,12 +472,6 @@ run(Uxn *u)
479 472
 	return error("SDL_WaitEvent", SDL_GetError());
480 473
 }
481 474
 
482
-int
483
-uxn_interrupt(void)
484
-{
485
-	return !SDL_QuitRequested();
486
-}
487
-
488 475
 int
489 476
 main(int argc, char **argv)
490 477
 {