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