commit 9cc0785
Devine Lu Linvega
·
2024-11-14 19:10:23 +0000 UTC
parent 1ebf0d8
Uxncli is now a single file implementation
2 files changed,
+639,
-31
M
makefile
M
makefile
+4,
-3
1@@ -38,6 +38,7 @@ grab:
2 @ cp ../uxn-utils/cli/opctest/opctest.tal etc/opctest.tal
3 archive:
4 @ cp src/uxnasm.c ../oscean/etc/uxnasm.c.txt
5+ @ cp src/uxncli.c ../oscean/etc/uxncli.c.txt
6 clean:
7 @ rm -fr bin
8
9@@ -54,9 +55,9 @@ src/bios.c: src/utils/bios.tal
10 bin/uxnasm: src/uxnasm.c
11 @ mkdir -p bin
12 @ cc ${RELEASE_flags} ${CFLAGS} src/uxnasm.c -o bin/uxnasm
13-bin/uxncli: ${CLI_src} src/uxncli.c
14+bin/uxncli: src/uxncli.c
15 @ mkdir -p bin
16- @ cc ${RELEASE_flags} ${CFLAGS} ${CLI_src} src/uxncli.c -lutil -o bin/uxncli
17+ @ cc ${RELEASE_flags} ${CFLAGS} src/uxncli.c -lutil -o bin/uxncli
18 bin/uxn11: ${EMU_src} src/uxn11.c src/bios.c
19 @ mkdir -p bin
20 @ cc ${RELEASE_flags} ${CFLAGS} ${EMU_src} src/uxn11.c -lX11 -lutil -o bin/uxn11
21@@ -66,7 +67,7 @@ bin/uxnasm-debug: src/uxnasm.c
22 @ cc ${DEBUG_flags} ${CFLAGS} src/uxnasm.c -o bin/uxnasm-debug
23 bin/uxncli-debug: ${CLI_src} src/uxncli.c
24 @ mkdir -p bin
25- @ cc ${DEBUG_flags} ${CFLAGS} ${CLI_src} src/uxncli.c -lutil -o bin/uxncli-debug
26+ @ cc ${DEBUG_flags} ${CFLAGS} src/uxncli.c -lutil -o bin/uxncli-debug
27 bin/uxn11-debug: ${EMU_src} src/uxn11.c
28 @ mkdir -p bin
29 @ cc ${DEBUG_flags} ${CFLAGS} ${EMU_src} src/uxn11.c -lX11 -lutil -o bin/uxn11-debug
+635,
-28
1@@ -1,11 +1,13 @@
2+#define _XOPEN_SOURCE 500
3 #include <stdio.h>
4 #include <stdlib.h>
5-
6-#include "uxn.h"
7-#include "devices/system.h"
8-#include "devices/console.h"
9-#include "devices/file.h"
10-#include "devices/datetime.h"
11+#include <string.h>
12+#include <dirent.h>
13+#include <errno.h>
14+#include <limits.h>
15+#include <sys/stat.h>
16+#include <unistd.h>
17+#include <time.h>
18
19 /*
20 Copyright (c) 2021-2024 Devine Lu Linvega, Andrew Alderwick
21@@ -18,15 +20,618 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
22 WITH REGARD TO THIS SOFTWARE.
23 */
24
25+#define RAM_PAGES 0x10
26+#define STEP_MAX 0x80000000
27+#define PAGE_PROGRAM 0x0100
28+
29+typedef unsigned char Uint8;
30+typedef signed char Sint8;
31+typedef unsigned short Uint16;
32+typedef signed short Sint16;
33+typedef unsigned int Uint32;
34+
35+typedef struct {
36+ Uint8 dat[0x100], ptr;
37+} Stack;
38+
39+typedef struct Uxn {
40+ Uint8 *ram, dev[0x100];
41+ Stack wst, rst;
42+} Uxn;
43+
44+/*
45+@|Uxn
46+*/
47+
48 Uxn uxn;
49-Uint8 ram[0x10000 * (RAM_PAGES + 1)];
50+Uint8 emu_dei(Uxn *u, Uint8 addr);
51+void emu_deo(Uxn *u, Uint8 addr, Uint8 value);
52+
53+/* clang-format off */
54+
55+#define PEEK2(d) (*(d) << 8 | (d)[1])
56+#define POKE2(d, v) { *(d) = (v) >> 8; (d)[1] = (v); }
57+
58+#define OPC(opc, init, body) {\
59+ case 0x00|opc: {const int _2=0,_r=0;init body;} break;\
60+ case 0x20|opc: {const int _2=1,_r=0;init body;} break;\
61+ case 0x40|opc: {const int _2=0,_r=1;init body;} break;\
62+ case 0x60|opc: {const int _2=1,_r=1;init body;} break;\
63+ case 0x80|opc: {const int _2=0,_r=0,k=u->wst.ptr;init u->wst.ptr=k;body;} break;\
64+ case 0xa0|opc: {const int _2=1,_r=0,k=u->wst.ptr;init u->wst.ptr=k;body;} break;\
65+ case 0xc0|opc: {const int _2=0,_r=1,k=u->rst.ptr;init u->rst.ptr=k;body;} break;\
66+ case 0xe0|opc: {const int _2=1,_r=1,k=u->rst.ptr;init u->rst.ptr=k;body;} break;\
67+}
68+
69+#define JMI a = u->ram[pc] << 8 | u->ram[pc + 1], pc += a + 2;
70+#define REM if(_r) u->rst.ptr -= 1 + _2; else u->wst.ptr -= 1 + _2;
71+#define INC(s) u->s.dat[u->s.ptr++]
72+#define DEC(s) u->s.dat[--u->s.ptr]
73+#define JMP(i) pc = _2 ? i : pc + (Sint8)i;
74+#define PO1(o) o = _r ? DEC(rst) : DEC(wst);
75+#define PO2(o) o = _r ? DEC(rst) | DEC(rst) << 8 : DEC(wst) | DEC(wst) << 8;
76+#define POx(o) { if(_2) PO2(o) else PO1(o) }
77+#define PU1(i) { if(_r) INC(rst) = i; else INC(wst) = i; }
78+#define RP1(i) { if(_r) INC(wst) = i; else INC(rst) = i; }
79+#define PUx(i) { if(_2) { c = (i); PU1(c >> 8) PU1(c) } else PU1(i) }
80+#define GET(o) { if(_2) PO1(o[1]) PO1(o[0]) }
81+#define PUT(i) { PU1(i[0]) if(_2) PU1(i[1]) }
82+#define DEI(i,o) o[0] = emu_dei(u, i); if(_2) o[1] = emu_dei(u, i + 1); PUT(o)
83+#define DEO(i,j) emu_deo(u, i, j[0]); if(_2) emu_deo(u, i + 1, j[1]);
84+#define PEK(i,o,m) o[0] = u->ram[i]; if(_2) o[1] = u->ram[(i + 1) & m]; PUT(o)
85+#define POK(i,j,m) u->ram[i] = j[0]; if(_2) u->ram[(i + 1) & m] = j[1];
86+
87+int
88+uxn_eval(Uxn *u, Uint16 pc)
89+{
90+ unsigned int a, b, c, x[2], y[2], z[2], step;
91+ if(!pc || u->dev[0x0f]) return 0;
92+ for(step = STEP_MAX; step; step--) {
93+ switch(u->ram[pc++]) {
94+ /* BRK */ case 0x00: return 1;
95+ /* JCI */ case 0x20: if(DEC(wst)) { JMI break; } pc += 2; break;
96+ /* JMI */ case 0x40: JMI break;
97+ /* JSI */ case 0x60: c = pc + 2; INC(rst) = c >> 8; INC(rst) = c; JMI break;
98+ /* LI2 */ case 0xa0: INC(wst) = u->ram[pc++]; /* fall-through */
99+ /* LIT */ case 0x80: INC(wst) = u->ram[pc++]; break;
100+ /* L2r */ case 0xe0: INC(rst) = u->ram[pc++]; /* fall-through */
101+ /* LIr */ case 0xc0: INC(rst) = u->ram[pc++]; break;
102+ /* INC */ OPC(0x01,POx(a),PUx(a + 1))
103+ /* POP */ OPC(0x02,REM ,{})
104+ /* NIP */ OPC(0x03,GET(x) REM ,PUT(x))
105+ /* SWP */ OPC(0x04,GET(x) GET(y),PUT(x) PUT(y))
106+ /* ROT */ OPC(0x05,GET(x) GET(y) GET(z),PUT(y) PUT(x) PUT(z))
107+ /* DUP */ OPC(0x06,GET(x),PUT(x) PUT(x))
108+ /* OVR */ OPC(0x07,GET(x) GET(y),PUT(y) PUT(x) PUT(y))
109+ /* EQU */ OPC(0x08,POx(a) POx(b),PU1(b == a))
110+ /* NEQ */ OPC(0x09,POx(a) POx(b),PU1(b != a))
111+ /* GTH */ OPC(0x0a,POx(a) POx(b),PU1(b > a))
112+ /* LTH */ OPC(0x0b,POx(a) POx(b),PU1(b < a))
113+ /* JMP */ OPC(0x0c,POx(a),JMP(a))
114+ /* JCN */ OPC(0x0d,POx(a) PO1(b),if(b) JMP(a))
115+ /* JSR */ OPC(0x0e,POx(a),RP1(pc >> 8) RP1(pc) JMP(a))
116+ /* STH */ OPC(0x0f,GET(x),RP1(x[0]) if(_2) RP1(x[1]))
117+ /* LDZ */ OPC(0x10,PO1(a),PEK(a, x, 0xff))
118+ /* STZ */ OPC(0x11,PO1(a) GET(y),POK(a, y, 0xff))
119+ /* LDR */ OPC(0x12,PO1(a),PEK(pc + (Sint8)a, x, 0xffff))
120+ /* STR */ OPC(0x13,PO1(a) GET(y),POK(pc + (Sint8)a, y, 0xffff))
121+ /* LDA */ OPC(0x14,PO2(a),PEK(a, x, 0xffff))
122+ /* STA */ OPC(0x15,PO2(a) GET(y),POK(a, y, 0xffff))
123+ /* DEI */ OPC(0x16,PO1(a),DEI(a, x))
124+ /* DEO */ OPC(0x17,PO1(a) GET(y),DEO(a, y))
125+ /* ADD */ OPC(0x18,POx(a) POx(b),PUx(b + a))
126+ /* SUB */ OPC(0x19,POx(a) POx(b),PUx(b - a))
127+ /* MUL */ OPC(0x1a,POx(a) POx(b),PUx(b * a))
128+ /* DIV */ OPC(0x1b,POx(a) POx(b),PUx(a ? b / a : 0))
129+ /* AND */ OPC(0x1c,POx(a) POx(b),PUx(b & a))
130+ /* ORA */ OPC(0x1d,POx(a) POx(b),PUx(b | a))
131+ /* EOR */ OPC(0x1e,POx(a) POx(b),PUx(b ^ a))
132+ /* SFT */ OPC(0x1f,PO1(a) POx(b),PUx(b >> (a & 0xf) << (a >> 4)))
133+ }
134+ }
135+ return 0;
136+}
137+
138+/* clang-format on */
139+
140+/*
141+@|00:System
142+*/
143+
144+static int
145+system_load(char *filename)
146+{
147+ FILE *f = fopen(filename, "rb");
148+ if(f) {
149+ int i = 0, l = fread(&uxn.ram[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM, 1, f);
150+ while(l && ++i < RAM_PAGES)
151+ l = fread(uxn.ram + 0x10000 * i, 0x10000, 1, f);
152+ fclose(f);
153+ }
154+ return !!f;
155+}
156+
157+static void
158+system_printstack(char *name, Stack *s)
159+{
160+ Uint8 i;
161+ fprintf(stderr, "%s ", name);
162+ for(i = s->ptr - 8; i != (Uint8)(s->ptr); i++)
163+ fprintf(stderr, "%02x%c", s->dat[i], i == 0xff ? '|' : ' ');
164+ fprintf(stderr, "< \n");
165+}
166+
167+static int
168+system_boot(Uint8 *ram, char *rom)
169+{
170+ uxn.ram = ram;
171+ if(!system_load(rom))
172+ return 0;
173+ return 1;
174+}
175+
176+static Uint8
177+system_dei(Uint8 addr)
178+{
179+ switch(addr) {
180+ case 0x4: return uxn.wst.ptr;
181+ case 0x5: return uxn.rst.ptr;
182+ default: return uxn.dev[addr];
183+ }
184+}
185+
186+static void
187+system_deo(Uint8 port)
188+{
189+ switch(port) {
190+ case 0x3: {
191+ Uint16 addr = PEEK2(uxn.dev + 2);
192+ if(uxn.ram[addr] == 0x0) {
193+ Uint8 value = uxn.ram[addr + 7];
194+ Uint16 i, length = PEEK2(uxn.ram + addr + 1);
195+ Uint16 dst_page = PEEK2(uxn.ram + addr + 3), dst_addr = PEEK2(uxn.ram + addr + 5);
196+ int dst = (dst_page % RAM_PAGES) * 0x10000;
197+ for(i = 0; i < length; i++)
198+ uxn.ram[dst + (Uint16)(dst_addr + i)] = value;
199+ } else if(uxn.ram[addr] == 0x1) {
200+ Uint16 i, length = PEEK2(uxn.ram + addr + 1);
201+ Uint16 a_page = PEEK2(uxn.ram + addr + 3), a_addr = PEEK2(uxn.ram + addr + 5);
202+ Uint16 b_page = PEEK2(uxn.ram + addr + 7), b_addr = PEEK2(uxn.ram + addr + 9);
203+ int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
204+ for(i = 0; i < length; i++)
205+ uxn.ram[dst + (Uint16)(b_addr + i)] = uxn.ram[src + (Uint16)(a_addr + i)];
206+ } else if(uxn.ram[addr] == 0x2) {
207+ Uint16 i, length = PEEK2(uxn.ram + addr + 1);
208+ Uint16 a_page = PEEK2(uxn.ram + addr + 3), a_addr = PEEK2(uxn.ram + addr + 5);
209+ Uint16 b_page = PEEK2(uxn.ram + addr + 7), b_addr = PEEK2(uxn.ram + addr + 9);
210+ int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000;
211+ for(i = length - 1; i != 0xffff; i--)
212+ uxn.ram[dst + (Uint16)(b_addr + i)] = uxn.ram[src + (Uint16)(a_addr + i)];
213+ } else
214+ fprintf(stderr, "Unknown Expansion Command 0x%02x\n", uxn.ram[addr]);
215+ break;
216+ }
217+ case 0x4:
218+ uxn.wst.ptr = uxn.dev[4];
219+ break;
220+ case 0x5:
221+ uxn.rst.ptr = uxn.dev[5];
222+ break;
223+ case 0xe:
224+ system_printstack("WST", &uxn.wst);
225+ system_printstack("RST", &uxn.rst);
226+ break;
227+ }
228+}
229+
230+/*
231+@|10:Console
232+*/
233+
234+#define CONSOLE_STD 0x1
235+#define CONSOLE_ARG 0x2
236+#define CONSOLE_EOA 0x3
237+#define CONSOLE_END 0x4
238+
239+static int
240+console_input(Uint8 c, int type)
241+{
242+ uxn.dev[0x12] = c;
243+ uxn.dev[0x17] = type;
244+ return uxn_eval(&uxn, PEEK2(&uxn.dev[0x10]));
245+}
246+
247+static void
248+console_deo(Uint8 addr)
249+{
250+ FILE *fd;
251+ switch(addr) {
252+ case 0x18: fd = stdout, fputc(uxn.dev[0x18], fd), fflush(fd); break;
253+ case 0x19: fd = stderr, fputc(uxn.dev[0x19], fd), fflush(fd); break;
254+ }
255+}
256+
257+/*
258+@|a0:File
259+@|b0:File
260+*/
261+
262+#define DIR_SEP_CHAR '/'
263+#define DIR_SEP_STR "/"
264+#define pathcmp(path1, path2, length) strncmp(path1, path2, length)
265+#define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR)
266+#define mkdir(file_name) (mkdir(file_name, 0755) == 0)
267+#define PATH_MAX 4096
268+
269+typedef struct {
270+ FILE *f;
271+ DIR *dir;
272+ char current_filename[PATH_MAX];
273+ struct dirent *de;
274+ enum { IDLE,
275+ FILE_READ,
276+ FILE_WRITE,
277+ DIR_READ,
278+ DIR_WRITE
279+ } state;
280+ int outside_sandbox;
281+} UxnFile;
282+
283+static UxnFile uxn_file[2];
284+
285+static void
286+file_reset(UxnFile *c)
287+{
288+ if(c->f != NULL)
289+ fclose(c->f), c->f = NULL;
290+ if(c->dir != NULL)
291+ closedir(c->dir), c->dir = NULL;
292+ c->de = NULL;
293+ c->state = IDLE;
294+ c->outside_sandbox = 0;
295+}
296+
297+static Uint16
298+file_get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero)
299+{
300+ struct stat st;
301+ if(len < strlen(basename) + 8)
302+ return 0;
303+ if(stat(pathname, &st))
304+ return fail_nonzero ? snprintf(p, len, "!!!! %s\n", basename) : 0;
305+ else if(S_ISDIR(st.st_mode))
306+ return snprintf(p, len, "---- %s/\n", basename);
307+ else if(st.st_size < 0x10000)
308+ return snprintf(p, len, "%04x %s\n", (unsigned int)st.st_size, basename);
309+ else
310+ return snprintf(p, len, "???? %s\n", basename);
311+}
312+
313+static Uint16
314+file_read_dir(UxnFile *c, char *dest, Uint16 len)
315+{
316+ static char pathname[4352];
317+ char *p = dest;
318+ if(c->de == NULL) c->de = readdir(c->dir);
319+ for(; c->de != NULL; c->de = readdir(c->dir)) {
320+ Uint16 n;
321+ if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0')
322+ continue;
323+ if(strcmp(c->de->d_name, "..") == 0) {
324+ /* hide "sandbox/.." */
325+ char cwd[PATH_MAX] = {'\0'}, *t;
326+ /* Note there's [currently] no way of chdir()ing from uxn, so $PWD
327+ * is always the sandbox top level. */
328+ getcwd(cwd, sizeof(cwd));
329+ /* We already checked that c->current_filename exists so don't need a wrapper. */
330+ t = realpath(c->current_filename, NULL);
331+ if(strcmp(cwd, t) == 0) {
332+ free(t);
333+ continue;
334+ }
335+ free(t);
336+ }
337+ if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname))
338+ snprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name);
339+ else
340+ pathname[0] = '\0';
341+ n = file_get_entry(p, len, pathname, c->de->d_name, 1);
342+ if(!n) break;
343+ p += n;
344+ len -= n;
345+ }
346+ return p - dest;
347+}
348+
349+static char *
350+file_retry_realpath(const char *file_name)
351+{
352+ char *r, p[PATH_MAX] = {'\0'}, *x;
353+ int fnlen;
354+ if(file_name == NULL) {
355+ errno = EINVAL;
356+ return NULL;
357+ } else if((fnlen = strlen(file_name)) >= PATH_MAX) {
358+ errno = ENAMETOOLONG;
359+ return NULL;
360+ }
361+ if(notdriveroot(file_name)) {
362+ getcwd(p, sizeof(p));
363+ if(strlen(p) + strlen(DIR_SEP_STR) + fnlen >= PATH_MAX) {
364+ errno = ENAMETOOLONG;
365+ return NULL;
366+ }
367+ strcat(p, DIR_SEP_STR);
368+ }
369+ strcat(p, file_name);
370+ while((r = realpath(p, NULL)) == NULL) {
371+ if(errno != ENOENT)
372+ return NULL;
373+ x = strrchr(p, DIR_SEP_CHAR);
374+ if(x)
375+ *x = '\0';
376+ else
377+ return NULL;
378+ }
379+ return r;
380+}
381+
382+static void
383+file_check_sandbox(UxnFile *c)
384+{
385+ char *x, *rp, cwd[PATH_MAX] = {'\0'};
386+ x = getcwd(cwd, sizeof(cwd));
387+ rp = file_retry_realpath(c->current_filename);
388+ if(rp == NULL || (x && pathcmp(cwd, rp, strlen(cwd)) != 0)) {
389+ c->outside_sandbox = 1;
390+ fprintf(stderr, "file warning: blocked attempt to access %s outside of sandbox\n", c->current_filename);
391+ }
392+ free(rp);
393+}
394+
395+static Uint16
396+file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox)
397+{
398+ char *p = c->current_filename;
399+ size_t len = sizeof(c->current_filename);
400+ file_reset(c);
401+ if(len > max_len) len = max_len;
402+ while(len) {
403+ if((*p++ = *filename++) == '\0') {
404+ if(!override_sandbox) /* override sandbox for loading roms */
405+ file_check_sandbox(c);
406+ return 0;
407+ }
408+ len--;
409+ }
410+ c->current_filename[0] = '\0';
411+ return 0;
412+}
413+
414+static Uint16
415+file_read(UxnFile *c, void *dest, int len)
416+{
417+ if(c->outside_sandbox) return 0;
418+ if(c->state != FILE_READ && c->state != DIR_READ) {
419+ file_reset(c);
420+ if((c->dir = opendir(c->current_filename)) != NULL)
421+ c->state = DIR_READ;
422+ else if((c->f = fopen(c->current_filename, "rb")) != NULL)
423+ c->state = FILE_READ;
424+ }
425+ if(c->state == FILE_READ)
426+ return fread(dest, 1, len, c->f);
427+ if(c->state == DIR_READ)
428+ return file_read_dir(c, dest, len);
429+ return 0;
430+}
431+
432+static int
433+file_is_dir_path(char *p)
434+{
435+ char c;
436+ int saw_slash = 0;
437+ while((c = *p++))
438+ saw_slash = c == DIR_SEP_CHAR;
439+ return saw_slash;
440+}
441+
442+static int
443+file_dir_exists(char *p)
444+{
445+ struct stat st;
446+ return stat(p, &st) == 0 && S_ISDIR(st.st_mode);
447+}
448+
449+static int
450+file_ensure_parent_dirs(char *p)
451+{
452+ int ok = 1;
453+ char c, *s = p;
454+ for(; ok && (c = *p); p++) {
455+ if(c == DIR_SEP_CHAR) {
456+ *p = '\0';
457+ ok = file_dir_exists(s) || mkdir(s);
458+ *p = c;
459+ }
460+ }
461+ return ok;
462+}
463+
464+static Uint16
465+file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
466+{
467+ Uint16 ret = 0;
468+ if(c->outside_sandbox) return 0;
469+ file_ensure_parent_dirs(c->current_filename);
470+ if(c->state != FILE_WRITE && c->state != DIR_WRITE) {
471+ file_reset(c);
472+ if(file_is_dir_path(c->current_filename))
473+ c->state = DIR_WRITE;
474+ else if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
475+ c->state = FILE_WRITE;
476+ }
477+ if(c->state == FILE_WRITE) {
478+ if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
479+ ret = 0;
480+ }
481+ if(c->state == DIR_WRITE) {
482+ ret = file_dir_exists(c->current_filename);
483+ }
484+ return ret;
485+}
486+
487+static Uint16
488+file_stat_fill(Uint8 *dest, Uint16 len, char c)
489+{
490+ Uint16 i;
491+ for(i = 0; i < len; i++) *(dest++) = c;
492+ return len;
493+}
494+
495+static Uint16
496+file_stat_size(Uint8 *dest, Uint16 len, off_t size)
497+{
498+ Uint16 i;
499+ dest += len - 1;
500+ for(i = 0; i < len; i++) {
501+ *(dest--) = "0123456789abcdef"[(Uint8)(size & 0xf)];
502+ size = size >> 4;
503+ }
504+ return size == 0 ? len : file_stat_fill(dest, len, '?');
505+}
506+
507+static Uint16
508+file_stat(UxnFile *c, void *dest, Uint16 len)
509+{
510+ struct stat st;
511+ if(c->outside_sandbox)
512+ return 0;
513+ else if(stat(c->current_filename, &st))
514+ return file_stat_fill(dest, len, '!');
515+ else if(S_ISDIR(st.st_mode))
516+ return file_stat_fill(dest, len, '-');
517+ else
518+ return file_stat_size(dest, len, st.st_size);
519+}
520+
521+static Uint16
522+file_delete(UxnFile *c)
523+{
524+ return c->outside_sandbox ? 0 : unlink(c->current_filename);
525+}
526+
527+static void
528+file_deo(Uint8 port)
529+{
530+ Uint16 addr, len, res;
531+ switch(port) {
532+ case 0xa5:
533+ addr = PEEK2(&uxn.dev[0xa4]);
534+ len = PEEK2(&uxn.dev[0xaa]);
535+ if(len > 0x10000 - addr)
536+ len = 0x10000 - addr;
537+ res = file_stat(&uxn_file[0], &uxn.ram[addr], len);
538+ POKE2(&uxn.dev[0xa2], res);
539+ break;
540+ case 0xa6:
541+ res = file_delete(&uxn_file[0]);
542+ POKE2(&uxn.dev[0xa2], res);
543+ break;
544+ case 0xa9:
545+ addr = PEEK2(&uxn.dev[0xa8]);
546+ res = file_init(&uxn_file[0], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
547+ POKE2(&uxn.dev[0xa2], res);
548+ break;
549+ case 0xad:
550+ addr = PEEK2(&uxn.dev[0xac]);
551+ len = PEEK2(&uxn.dev[0xaa]);
552+ if(len > 0x10000 - addr)
553+ len = 0x10000 - addr;
554+ res = file_read(&uxn_file[0], &uxn.ram[addr], len);
555+ POKE2(&uxn.dev[0xa2], res);
556+ break;
557+ case 0xaf:
558+ addr = PEEK2(&uxn.dev[0xae]);
559+ len = PEEK2(&uxn.dev[0xaa]);
560+ if(len > 0x10000 - addr)
561+ len = 0x10000 - addr;
562+ res = file_write(&uxn_file[0], &uxn.ram[addr], len, uxn.dev[0xa7]);
563+ POKE2(&uxn.dev[0xa2], res);
564+ break;
565+ /* File 2 */
566+ case 0xb5:
567+ addr = PEEK2(&uxn.dev[0xb4]);
568+ len = PEEK2(&uxn.dev[0xba]);
569+ if(len > 0x10000 - addr)
570+ len = 0x10000 - addr;
571+ res = file_stat(&uxn_file[1], &uxn.ram[addr], len);
572+ POKE2(&uxn.dev[0xb2], res);
573+ break;
574+ case 0xb6:
575+ res = file_delete(&uxn_file[1]);
576+ POKE2(&uxn.dev[0xb2], res);
577+ break;
578+ case 0xb9:
579+ addr = PEEK2(&uxn.dev[0xb8]);
580+ res = file_init(&uxn_file[1], (char *)&uxn.ram[addr], 0x10000 - addr, 0);
581+ POKE2(&uxn.dev[0xb2], res);
582+ break;
583+ case 0xbd:
584+ addr = PEEK2(&uxn.dev[0xbc]);
585+ len = PEEK2(&uxn.dev[0xba]);
586+ if(len > 0x10000 - addr)
587+ len = 0x10000 - addr;
588+ res = file_read(&uxn_file[1], &uxn.ram[addr], len);
589+ POKE2(&uxn.dev[0xb2], res);
590+ break;
591+ case 0xbf:
592+ addr = PEEK2(&uxn.dev[0xbe]);
593+ len = PEEK2(&uxn.dev[0xba]);
594+ if(len > 0x10000 - addr)
595+ len = 0x10000 - addr;
596+ res = file_write(&uxn_file[1], &uxn.ram[addr], len, uxn.dev[0xb7]);
597+ POKE2(&uxn.dev[0xb2], res);
598+ break;
599+ }
600+}
601+
602+/*
603+@|c0:Datetime
604+*/
605+
606+static Uint8
607+datetime_dei(Uint8 addr)
608+{
609+ time_t seconds = time(NULL);
610+ struct tm zt = {0}, *t = localtime(&seconds);
611+ if(t == NULL)
612+ t = &zt;
613+ switch(addr) {
614+ case 0xc0: return (t->tm_year + 1900) >> 8;
615+ case 0xc1: return (t->tm_year + 1900);
616+ case 0xc2: return t->tm_mon;
617+ case 0xc3: return t->tm_mday;
618+ case 0xc4: return t->tm_hour;
619+ case 0xc5: return t->tm_min;
620+ case 0xc6: return t->tm_sec;
621+ case 0xc7: return t->tm_wday;
622+ case 0xc8: return t->tm_yday >> 8;
623+ case 0xc9: return t->tm_yday;
624+ case 0xca: return t->tm_isdst;
625+ default: return uxn.dev[addr];
626+ }
627+}
628+
629+/*
630+@|Emu
631+*/
632
633 Uint8
634 emu_dei(Uxn *u, Uint8 addr)
635 {
636 switch(addr & 0xf0) {
637- case 0x00: return system_dei(u, addr);
638- case 0x10: return console_dei(u, addr);
639+ case 0x00: return system_dei(addr);
640 case 0xc0: return datetime_dei(addr);
641 }
642 return u->dev[addr];
643@@ -37,38 +642,40 @@ emu_deo(Uxn *u, Uint8 addr, Uint8 value)
644 {
645 u->dev[addr] = value;
646 switch(addr & 0xf0) {
647- case 0x00: system_deo(u, addr); break;
648- case 0x10: console_deo(u, addr); break;
649+ case 0x00: system_deo(addr); break;
650+ case 0x10: console_deo(addr); break;
651 case 0xa0: file_deo(addr); break;
652 case 0xb0: file_deo(addr); break;
653 }
654 }
655
656-static void
657-emu_run(void)
658-{
659- while(!uxn.dev[0x0f]) {
660- int c = fgetc(stdin);
661- if(c == EOF) {
662- console_input(0x00, CONSOLE_END);
663- break;
664- }
665- console_input(c, CONSOLE_STD);
666- }
667-}
668-
669 int
670 main(int argc, char **argv)
671 {
672 int i = 1;
673 if(argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v')
674- return !fprintf(stdout, "Uxn11(cli) - Varvara Emulator, 12 Nov 2024.\n");
675+ return !fprintf(stdout, "Uxn11(cli) - Varvara Emulator, 14 Nov 2024.\n");
676 else if(argc == 1)
677 return !fprintf(stdout, "usage: %s [-v] file.rom [args..]\n", argv[0]);
678- else if(!system_boot(ram, argv[i++]))
679+ else if(!system_boot((Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), argv[i++]))
680 return !fprintf(stdout, "Could not load %s.\n", argv[i - 1]);
681 uxn.dev[0x17] = argc - 2;
682- if(uxn_eval(&uxn, PAGE_PROGRAM) && uxn.dev[0x10])
683- console_listen(2, argc, argv), emu_run();
684+ if(uxn_eval(&uxn, PAGE_PROGRAM) && uxn.dev[0x10]) {
685+ /* arguments input */
686+ for(; i < argc; i++) {
687+ char *p = argv[i];
688+ while(*p) console_input(*p++, CONSOLE_ARG);
689+ console_input('\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA);
690+ }
691+ /* console input */
692+ while(!uxn.dev[0x0f]) {
693+ int c = fgetc(stdin);
694+ if(c == EOF) {
695+ console_input(0x00, CONSOLE_END);
696+ break;
697+ }
698+ console_input(c, CONSOLE_STD);
699+ }
700+ }
701 return uxn.dev[0x0f] & 0x7f;
702 }