1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4
5/*
6Copyright (c) 2021-2025 Devine Lu Linvega, Andrew Alderwick,
7Andrew Richards, Eiríkr Åsheim, Sigrid Solveig Haflínudóttir
8
9Permission to use, copy, modify, and distribute this software for any
10purpose with or without fee is hereby granted, provided that the above
11copyright notice and this permission notice appear in all copies.
12
13THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14WITH REGARD TO THIS SOFTWARE.
15
16cc -DNDEBUG -O2 -g0 -s src/uxncli.c -lutil -o bin/uxncli
17*/
18
19typedef unsigned char Uint8;
20typedef unsigned short Uint16;
21
22/* clang-format off */
23
24#define BANKS 0x10
25#define BANKS_CAP BANKS * 0x10000
26
27#define PEEK2(d) (*(d) << 8 | (d)[1])
28#define NEXT if(--cycles) goto step; else return 0;
29
30#define OPC(opc, A, B) {\
31 case 0x00|opc: {const int _2=0,_r=0;A B;} NEXT\
32 case 0x20|opc: {const int _2=1,_r=0;A B;} NEXT\
33 case 0x40|opc: {const int _2=0,_r=1;A B;} NEXT\
34 case 0x60|opc: {const int _2=1,_r=1;A B;} NEXT\
35 case 0x80|opc: {const int _2=0,_r=0;int k=ptr[0];A ptr[0]=k;B;} NEXT\
36 case 0xa0|opc: {const int _2=1,_r=0;int k=ptr[0];A ptr[0]=k;B;} NEXT\
37 case 0xc0|opc: {const int _2=0,_r=1;int k=ptr[1];A ptr[1]=k;B;} NEXT\
38 case 0xe0|opc: {const int _2=1,_r=1;int k=ptr[1];A ptr[1]=k;B;} NEXT\
39}
40
41#define REM ptr[_r] -= 1 + _2;
42#define DEC(m) stk[m][--ptr[m]]
43#define INC(m) stk[m][ptr[m]++]
44#define IMM(r) { r = ram[pc++] << 8, r |= ram[pc++]; }
45#define MOV(x) { if(_2) pc = x; else pc += (signed char)x; }
46#define PO1(o) o = DEC(_r);
47#define PO2(o) { o = DEC(_r), o |= DEC(_r) << 8; }
48#define POx(o) if(_2) PO2(o) else PO1(o)
49#define GOT(o) if(_2) PO1(o[1]) PO1(o[0])
50#define DEO(o,r) emu_deo(o, r[0]); if(_2) emu_deo(o + 1, r[1]);
51#define POK(o,r,m) ram[o] = r[0]; if(_2) ram[(o + 1) & m] = r[1];
52#define RP1(i) INC(!_r) = i;
53#define PU1(i) INC(_r) = i;
54#define PUx(i) if(_2) { c = (i); PU1(c >> 8) PU1(c) } else PU1(i)
55#define PUT(i) PU1(i[0]) if(_2) PU1(i[1])
56#define DEI(i,r) r[0] = emu_dei(i); if(_2) r[1] = emu_dei(i + 1); PUT(r)
57#define PEK(i,r,m) r[0] = ram[i]; if(_2) r[1] = ram[(i + 1) & m]; PUT(r)
58
59Uint8 *ram, dev[0x100], ptr[2], stk[2][0x100], emu_dei(const Uint8 port);
60void emu_deo(const Uint8 port, const Uint8 value);
61
62unsigned int
63uxn_eval(unsigned short pc)
64{
65 unsigned int a, b, c, x[2], y[2], z[2], cycles = 0x80000000;
66step:
67 switch(ram[pc++]) {
68 /* BRK */ case 0x00: return 1;
69 /* JCI */ case 0x20: if(DEC(0)) { IMM(c) pc += c; } else pc += 2; NEXT
70 /* JMI */ case 0x40: IMM(c) pc += c; NEXT
71 /* JSI */ case 0x60: IMM(c) INC(1) = pc >> 8, INC(1) = pc, pc += c; NEXT
72 /* LI2 */ case 0xa0: INC(0) = ram[pc++]; /* fall-through */
73 /* LIT */ case 0x80: INC(0) = ram[pc++]; NEXT
74 /* L2r */ case 0xe0: INC(1) = ram[pc++]; /* fall-through */
75 /* LIr */ case 0xc0: INC(1) = ram[pc++]; NEXT
76 /* INC */ OPC(0x01,POx(a),PUx(a + 1))
77 /* POP */ OPC(0x02,REM,{})
78 /* NIP */ OPC(0x03,GOT(x) REM,PUT(x))
79 /* SWP */ OPC(0x04,GOT(x) GOT(y),PUT(x) PUT(y))
80 /* ROT */ OPC(0x05,GOT(x) GOT(y) GOT(z),PUT(y) PUT(x) PUT(z))
81 /* DUP */ OPC(0x06,GOT(x),PUT(x) PUT(x))
82 /* OVR */ OPC(0x07,GOT(x) GOT(y),PUT(y) PUT(x) PUT(y))
83 /* EQU */ OPC(0x08,POx(a) POx(b),PU1(b == a))
84 /* NEQ */ OPC(0x09,POx(a) POx(b),PU1(b != a))
85 /* GTH */ OPC(0x0a,POx(a) POx(b),PU1(b > a))
86 /* LTH */ OPC(0x0b,POx(a) POx(b),PU1(b < a))
87 /* JMP */ OPC(0x0c,POx(a),MOV(a))
88 /* JCN */ OPC(0x0d,POx(a) PO1(b),if(b) MOV(a))
89 /* JSR */ OPC(0x0e,POx(a),RP1(pc >> 8) RP1(pc) MOV(a))
90 /* STH */ OPC(0x0f,GOT(x),RP1(x[0]) if(_2) RP1(x[1]))
91 /* LDZ */ OPC(0x10,PO1(a),PEK(a, x, 0xff))
92 /* STZ */ OPC(0x11,PO1(a) GOT(y),POK(a, y, 0xff))
93 /* LDR */ OPC(0x12,PO1(a),PEK(pc + (signed char)a, x, 0xffff))
94 /* STR */ OPC(0x13,PO1(a) GOT(y),POK(pc + (signed char)a, y, 0xffff))
95 /* LDA */ OPC(0x14,PO2(a),PEK(a, x, 0xffff))
96 /* STA */ OPC(0x15,PO2(a) GOT(y),POK(a, y, 0xffff))
97 /* DEI */ OPC(0x16,PO1(a),DEI(a, x))
98 /* DEO */ OPC(0x17,PO1(a) GOT(y),DEO(a, y))
99 /* ADD */ OPC(0x18,POx(a) POx(b),PUx(b + a))
100 /* SUB */ OPC(0x19,POx(a) POx(b),PUx(b - a))
101 /* MUL */ OPC(0x1a,POx(a) POx(b),PUx(b * a))
102 /* DIV */ OPC(0x1b,POx(a) POx(b),PUx(a ? b / a : 0))
103 /* AND */ OPC(0x1c,POx(a) POx(b),PUx(b & a))
104 /* ORA */ OPC(0x1d,POx(a) POx(b),PUx(b | a))
105 /* EOR */ OPC(0x1e,POx(a) POx(b),PUx(b ^ a))
106 /* SFT */ OPC(0x1f,PO1(a) POx(b),PUx(b >> (a & 0xf) << (a >> 4)))
107 }
108 return 0;
109}
110
111/* clang-format on */
112
113/*
114@|System ------------------------------------------------------------ */
115
116static char *system_boot_path;
117
118static void
119system_print(char *name, int r)
120{
121 Uint8 i;
122 fprintf(stderr, "%s ", name);
123 for(i = ptr[r] - 8; i != ptr[r]; i++)
124 fprintf(stderr, "%02x%c", stk[r][i], i == 0xff ? '|' : ' ');
125 fprintf(stderr, "<%02x\n", ptr[r]);
126}
127
128static unsigned int
129system_load(const char *rom_path)
130{
131 FILE *f = fopen(rom_path, "rb");
132 if(f) {
133 unsigned int i = 0, l = fread(ram + 0x100, 0x10000 - 0x100, 1, f);
134 while(l && ++i < BANKS)
135 l = fread(ram + i * 0x10000, 0x10000, 1, f);
136 fclose(f);
137 }
138 return !!f;
139}
140
141static unsigned int
142system_boot(char *rom_path, const unsigned int has_args)
143{
144 ram = (Uint8 *)calloc(BANKS_CAP, sizeof(Uint8));
145 system_boot_path = rom_path;
146 dev[0x17] = has_args;
147 return ram && system_load(rom_path);
148}
149
150static void
151system_expansion(const Uint16 exp)
152{
153 Uint8 *aptr = ram + exp;
154 unsigned short length = PEEK2(aptr + 1), limit;
155 unsigned int bank = PEEK2(aptr + 3) * 0x10000;
156 unsigned int addr = PEEK2(aptr + 5);
157 if(ram[exp] == 0x0) {
158 unsigned int dst_value = ram[exp + 7];
159 unsigned short a = addr;
160 if(bank < BANKS_CAP)
161 for(limit = a + length; a != limit; a++)
162 ram[bank + a] = dst_value;
163 } else if(ram[exp] == 0x1) {
164 unsigned int dst_bank = PEEK2(aptr + 7) * 0x10000;
165 unsigned int dst_addr = PEEK2(aptr + 9);
166 unsigned short a = addr, c = dst_addr;
167 if(bank < BANKS_CAP && dst_bank < BANKS_CAP)
168 for(limit = a + length; a != limit; c++, a++)
169 ram[dst_bank + c] = ram[bank + a];
170 } else if(ram[exp] == 0x2) {
171 unsigned int dst_bank = PEEK2(aptr + 7) * 0x10000;
172 unsigned int dst_addr = PEEK2(aptr + 9);
173 unsigned short a = addr + length - 1, c = dst_addr + length - 1;
174 if(bank < BANKS_CAP && dst_bank < BANKS_CAP)
175 for(limit = addr - 1; a != limit; a--, c--)
176 ram[dst_bank + c] = ram[bank + a];
177 } else
178 fprintf(stderr, "Unknown command: %s\n", &ram[exp]);
179}
180
181/*
182@|Console ----------------------------------------------------------- */
183
184#define CONSOLE_STD 0x1
185#define CONSOLE_ARG 0x2
186#define CONSOLE_EOA 0x3
187#define CONSOLE_END 0x4
188
189#undef _POSIX_C_SOURCE
190#define _POSIX_C_SOURCE 200112L
191
192#include <signal.h>
193#include <sys/select.h>
194#include <sys/wait.h>
195
196#ifdef __linux
197#include <pty.h>
198#include <sys/prctl.h>
199#endif
200
201#ifdef __NetBSD__
202#include <sys/ioctl.h>
203#include <util.h>
204#endif
205
206static unsigned int console_vector;
207
208/* subprocess support */
209static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
210static int child_mode;
211static int to_child_fd[2];
212static int from_child_fd[2];
213static int saved_in;
214static int saved_out;
215static pid_t child_pid;
216
217/* child_mode:
218* 0x01: writes to child's stdin
219* 0x02: reads from child's stdout
220* 0x04: reads from child's stderr
221* 0x08: kill previous process (if any) but do not start
222* (other bits ignored for now )
223*/
224
225#define CMD_LIVE 0x15 /* 0x00 not started, 0x01 running, 0xff dead */
226#define CMD_EXIT 0x16 /* if dead, exit code of process */
227#define CMD_ADDR 0x1c /* address to read command args from */
228#define CMD_MODE 0x1e /* mode to execute, 0x00 to 0x07 */
229
230/* call after we're sure the process has exited */
231
232static void
233clean_after_child(void)
234{
235 child_pid = 0;
236 if(child_mode & 0x01) {
237 close(to_child_fd[1]);
238 dup2(saved_out, 1);
239 }
240 if(child_mode & (0x04 | 0x02)) {
241 close(from_child_fd[0]);
242 dup2(saved_in, 0);
243 }
244 child_mode = 0;
245 saved_in = -1;
246 saved_out = -1;
247}
248
249static void
250start_fork_pipe(void)
251{
252 pid_t pid;
253 pid_t parent_pid = getpid();
254 int addr = PEEK2(&dev[CMD_ADDR]);
255 fflush(stdout);
256 if(child_mode & 0x08) {
257 dev[CMD_EXIT] = dev[CMD_LIVE] = 0x00;
258 return;
259 }
260 if(child_mode & 0x01) {
261 /* parent writes to child's stdin */
262 if(pipe(to_child_fd) == -1) {
263 dev[CMD_EXIT] = dev[CMD_LIVE] = 0xff;
264 fprintf(stderr, "Pipe error to child.\n");
265 return;
266 }
267 }
268 if(child_mode & (0x04 | 0x02)) {
269 /* parent reads from child's stdout and/or stderr */
270 if(pipe(from_child_fd) == -1) {
271 dev[CMD_EXIT] = dev[CMD_LIVE] = 0xff;
272 fprintf(stderr, "Pipe error from child.\n");
273 return;
274 }
275 }
276
277 fork_args[2] = (char *)&ram[addr];
278 pid = fork();
279 if(pid < 0) { /* failure */
280 dev[CMD_EXIT] = dev[CMD_LIVE] = 0xff;
281 fprintf(stderr, "Fork failure.\n");
282 } else if(pid == 0) { /* child */
283
284#ifdef __linux__
285 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
286 if(r == -1) {
287 perror(0);
288 exit(6);
289 }
290 if(getppid() != parent_pid) exit(13);
291#endif
292
293 if(child_mode & 0x01) {
294 dup2(to_child_fd[0], 0);
295 close(to_child_fd[1]);
296 }
297 if(child_mode & (0x04 | 0x02)) {
298 if(child_mode & 0x02) dup2(from_child_fd[1], 1);
299 if(child_mode & 0x04) dup2(from_child_fd[1], 2);
300 close(from_child_fd[0]);
301 }
302 fflush(stdout);
303 execvp(fork_args[0], fork_args);
304 exit(1);
305 } else { /*parent*/
306 child_pid = pid;
307 dev[CMD_LIVE] = 0x01;
308 dev[CMD_EXIT] = 0x00;
309 if(child_mode & 0x01) {
310 saved_out = dup(1);
311 dup2(to_child_fd[1], 1);
312 close(to_child_fd[0]);
313 }
314 if(child_mode & (0x04 | 0x02)) {
315 saved_in = dup(0);
316 dup2(from_child_fd[0], 0);
317 close(from_child_fd[1]);
318 }
319 }
320}
321
322static void
323check_child(void)
324{
325 int wstatus;
326 if(child_pid) {
327 if(waitpid(child_pid, &wstatus, WNOHANG)) {
328 dev[CMD_LIVE] = 0xff;
329 dev[CMD_EXIT] = WEXITSTATUS(wstatus);
330 clean_after_child();
331 } else {
332 dev[CMD_LIVE] = 0x01;
333 dev[CMD_EXIT] = 0x00;
334 }
335 }
336}
337
338static void
339kill_child(void)
340{
341 int wstatus;
342 if(child_pid) {
343 kill(child_pid, 9);
344 if(waitpid(child_pid, &wstatus, WNOHANG)) {
345 dev[CMD_LIVE] = 0xff;
346 dev[CMD_EXIT] = WEXITSTATUS(wstatus);
347 clean_after_child();
348 }
349 }
350}
351
352static void
353start_fork(void)
354{
355 fflush(stderr);
356 kill_child();
357 child_mode = dev[CMD_MODE];
358 start_fork_pipe();
359}
360
361static void
362console_input(int c, unsigned int type)
363{
364 dev[0x12] = c, dev[0x17] = type;
365 if(console_vector && !dev[0x0f])
366 uxn_eval(console_vector);
367}
368
369/*
370@|File -------------------------------------------------------------- */
371
372#include <dirent.h>
373#include <sys/stat.h>
374#include <string.h>
375
376typedef struct {
377 FILE *f;
378 DIR *dir;
379 char *filepath;
380 enum { IDLE,
381 FILE_READ,
382 FILE_WRITE,
383 DIR_READ,
384 DIR_WRITE
385 } state;
386} UxnFile;
387
388static UxnFile ufs[2];
389static Uint8 dirbuf[0x10000], *_dirbuf = dirbuf;
390static unsigned int rL1, rL2;
391
392static void
393make_pathfile(char *pathbuf, const char *filepath, const char *basename)
394{
395 char c = '/';
396 while(*filepath)
397 c = *filepath++, *pathbuf = c, pathbuf++;
398 if(c != '/')
399 *pathbuf = '/', pathbuf++;
400 while(*basename)
401 *pathbuf = *basename++, pathbuf++;
402 *pathbuf = 0;
403}
404
405static unsigned int
406put_fill(Uint8 *dest, unsigned int len, char c)
407{
408 unsigned int i;
409 for(i = 0; i < len; i++)
410 *dest = c, dest++;
411 return len;
412}
413
414static unsigned int
415put_size(Uint8 *dest, unsigned int len, unsigned int size)
416{
417 unsigned int i;
418 for(i = 0, dest += len; i < len; i++, size >>= 4)
419 *(--dest) = "0123456789abcdef"[(Uint8)(size & 0xf)];
420 return len;
421}
422
423static unsigned int
424put_text(Uint8 *dest, const char *text)
425{
426 Uint8 *anchor = dest;
427 while(*text)
428 *dest = *text++, dest++;
429 *dest = 0;
430 return dest - anchor;
431}
432
433static unsigned int
434put_stat(Uint8 *dest, unsigned int len, unsigned int size, unsigned int err, unsigned int dir, unsigned int capsize)
435{
436 if(err) return put_fill(dest, len, '!');
437 if(dir) return put_fill(dest, len, '-');
438 if(capsize && size >= 0x10000) return put_fill(dest, len, '?');
439 return put_size(dest, len, size);
440}
441
442static unsigned int
443put_statfile(Uint8 *dest, const char *filepath, const char *basename)
444{
445 unsigned int err, dir;
446 struct stat st;
447 Uint8 *anchor = dest;
448 char pathbuf[0x2000];
449 make_pathfile(pathbuf, filepath, basename);
450 err = stat(pathbuf, &st);
451 dir = S_ISDIR(st.st_mode);
452 dest += put_stat(dest, 4, st.st_size, err, dir, 1);
453 dest += put_text(dest, " ");
454 dest += put_text(dest, basename);
455 dest += put_text(dest, dir ? "/\n" : "\n");
456 return dest - anchor;
457}
458
459static unsigned int
460put_fdir(Uint8 *dest, unsigned int len, const char *filepath, DIR *dir)
461{
462 unsigned int i;
463 struct dirent *de = readdir(dir);
464 for(_dirbuf = dirbuf; de != NULL; de = readdir(dir)) {
465 const char *name = de->d_name;
466 if(name[0] == '.' && (name[1] == '.' || name[1] == '\0'))
467 continue;
468 else
469 _dirbuf += put_statfile(_dirbuf, filepath, name);
470 }
471 for(i = 0; i < len && dirbuf[i]; i++)
472 dest[i] = dirbuf[i];
473 dest[i] = 0;
474 return i;
475}
476
477static unsigned int
478is_dir_path(char *p)
479{
480 char c;
481 unsigned int saw_slash = 0;
482 while((c = *p++)) saw_slash = c == '/';
483 return saw_slash;
484}
485
486static unsigned int
487is_dir_real(char *p)
488{
489 struct stat st;
490 return stat(p, &st) == 0 && S_ISDIR(st.st_mode);
491}
492
493static unsigned int
494file_write_dir(char *p)
495{
496 unsigned int ok = 1;
497 char c, *s = p;
498 for(; ok && (c = *p); p++) {
499 if(c == '/') {
500 *p = '\0';
501 ok = is_dir_real(s) || (mkdir(s, 0755) == 0);
502 *p = c;
503 }
504 }
505 return ok;
506}
507
508static void
509file_reset(unsigned int id)
510{
511 if(ufs[id].f != NULL) fclose(ufs[id].f), ufs[id].f = NULL;
512 if(ufs[id].dir != NULL) closedir(ufs[id].dir), ufs[id].dir = NULL;
513 ufs[id].state = IDLE;
514}
515
516static unsigned int
517file_init(unsigned int id, Uint16 addr)
518{
519 file_reset(id);
520 ufs[id].filepath = (char *)&ram[addr];
521 return 0;
522}
523
524static unsigned int
525file_not_ready(unsigned int id)
526{
527 if(ufs[id].filepath == 0) {
528 fprintf(stderr, "File %d is uninitialized\n", id);
529 return 1;
530 } else
531 return 0;
532}
533
534static unsigned int
535file_read(unsigned int id, Uint16 addr, unsigned int len)
536{
537 void *dest = &ram[addr];
538 if(file_not_ready(id))
539 return 0;
540 if(addr + len > 0x10000)
541 len = 0x10000 - addr;
542 if(ufs[id].state != FILE_READ && ufs[id].state != DIR_READ) {
543 file_reset(id);
544 if((ufs[id].dir = opendir(ufs[id].filepath)) != NULL)
545 ufs[id].state = DIR_READ;
546 else if((ufs[id].f = fopen(ufs[id].filepath, "rb")) != NULL)
547 ufs[id].state = FILE_READ;
548 }
549 if(ufs[id].state == FILE_READ)
550 return fread(dest, 1, len, ufs[id].f);
551 if(ufs[id].state == DIR_READ)
552 return put_fdir(dest, len, ufs[id].filepath, ufs[id].dir);
553 return 0;
554}
555
556static unsigned int
557file_write(unsigned int id, Uint16 addr, unsigned int len, Uint8 flags)
558{
559 unsigned int ret = 0;
560 if(file_not_ready(id))
561 return 0;
562 if(addr + len > 0x10000)
563 len = 0x10000 - addr;
564 file_write_dir(ufs[id].filepath);
565 if(ufs[id].state != FILE_WRITE && ufs[id].state != DIR_WRITE) {
566 file_reset(id);
567 if(is_dir_path(ufs[id].filepath))
568 ufs[id].state = DIR_WRITE;
569 else if((ufs[id].f = fopen(ufs[id].filepath, (flags & 0x01) ? "ab" : "wb")) != NULL)
570 ufs[id].state = FILE_WRITE;
571 }
572 if(ufs[id].state == FILE_WRITE)
573 if((ret = fwrite(&ram[addr], 1, len, ufs[id].f)) > 0 && fflush(ufs[id].f) != 0)
574 ret = 0;
575 if(ufs[id].state == DIR_WRITE)
576 ret = is_dir_real(ufs[id].filepath);
577 return ret;
578}
579
580static unsigned int
581file_stat(unsigned int id, Uint16 addr, unsigned int len)
582{
583 unsigned int err, dir;
584 struct stat st;
585 if(file_not_ready(id))
586 return 0;
587 if(addr + len > 0x10000)
588 len = 0x10000 - addr;
589 err = stat(ufs[id].filepath, &st);
590 dir = S_ISDIR(st.st_mode);
591 return put_stat(&ram[addr], len, st.st_size, err, dir, 0);
592}
593
594static unsigned int
595file_delete(unsigned int id)
596{
597 if(file_not_ready(id))
598 return 0;
599 return !unlink(ufs[id].filepath);
600}
601
602static void
603file_success(unsigned int port, unsigned int value)
604{
605 dev[port] = value >> 8, dev[port + 1] = value;
606}
607
608/*
609@|Datetime ---------------------------------------------------------- */
610
611#include <time.h>
612
613time_t datetime_seconds;
614struct tm *datetime_t, datetime_zt = {0};
615
616void
617datetime_update(void)
618{
619 datetime_seconds = time(NULL);
620 datetime_t = localtime(&datetime_seconds);
621 if(datetime_t == NULL)
622 datetime_t = &datetime_zt;
623}
624
625/*
626@|Core -------------------------------------------------------------- */
627
628Uint8
629emu_dei(const Uint8 port)
630{
631 switch(port) {
632 /* System */
633 case 0x04: return ptr[0];
634 case 0x05: return ptr[1];
635 /* Console */
636 case CMD_LIVE:
637 case CMD_EXIT: check_child(); break;
638 /* DateTime */
639 case 0xc0: datetime_update(); return (datetime_t->tm_year + 1900) >> 8;
640 case 0xc1: datetime_update(); return (datetime_t->tm_year + 1900);
641 case 0xc2: datetime_update(); return datetime_t->tm_mon;
642 case 0xc3: datetime_update(); return datetime_t->tm_mday;
643 case 0xc4: datetime_update(); return datetime_t->tm_hour;
644 case 0xc5: datetime_update(); return datetime_t->tm_min;
645 case 0xc6: datetime_update(); return datetime_t->tm_sec;
646 case 0xc7: datetime_update(); return datetime_t->tm_wday;
647 case 0xc8: datetime_update(); return datetime_t->tm_yday >> 8;
648 case 0xc9: datetime_update(); return datetime_t->tm_yday;
649 case 0xca: datetime_update(); return datetime_t->tm_isdst;
650 }
651 return dev[port];
652}
653
654void
655emu_deo(const Uint8 port, const Uint8 value)
656{
657 dev[port] = value;
658 switch(port) {
659 /* System */
660 case 0x03: system_expansion(PEEK2(dev + 2)); break;
661 case 0x04: ptr[0] = dev[4]; break;
662 case 0x05: ptr[1] = dev[5]; break;
663 case 0x0e: system_print("WST", 0), system_print("RST", 1); break;
664 /* Console */
665 case 0x11: console_vector = PEEK2(&dev[0x10]); return;
666 case 0x18: fputc(dev[0x18], stdout), fflush(stdout); break;
667 case 0x19: fputc(dev[0x19], stderr), fflush(stderr); break;
668 case 0x1a: fprintf(stderr, "%02x", dev[0x1a]); break;
669 case 0x1b: fprintf(stderr, "%02x", dev[0x1b]); break;
670 case 0x1f: start_fork(); break;
671 /* File 1 */
672 case 0xab: rL1 = PEEK2(&dev[0xaa]); break;
673 case 0xa5: file_success(0xa2, file_stat(0, PEEK2(&dev[0xa4]), rL1)); break;
674 case 0xa6: file_success(0xa2, file_delete(0)); break;
675 case 0xa9: file_success(0xa2, file_init(0, PEEK2(&dev[0xa8]))); break;
676 case 0xad: file_success(0xa2, file_read(0, PEEK2(&dev[0xac]), rL1)); break;
677 case 0xaf: file_success(0xa2, file_write(0, PEEK2(&dev[0xae]), rL1, dev[0xa7])); break;
678 /* File 2 */
679 case 0xbb: rL2 = PEEK2(&dev[0xba]); break;
680 case 0xb5: file_success(0xb2, file_stat(1, PEEK2(&dev[0xb4]), rL2)); break;
681 case 0xb6: file_success(0xb2, file_delete(1)); break;
682 case 0xb9: file_success(0xb2, file_init(1, PEEK2(&dev[0xb8]))); break;
683 case 0xbd: file_success(0xb2, file_read(1, PEEK2(&dev[0xbc]), rL2)); break;
684 case 0xbf: file_success(0xb2, file_write(1, PEEK2(&dev[0xbe]), rL2, dev[0xb7])); break;
685 }
686}
687
688int
689main(int argc, char **argv)
690{
691 int i = 1;
692 if(argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v')
693 return !fprintf(stdout, "Uxn11 - Varvara Emulator(cli), 23 Dec 2025.\n");
694 else if(argc == 1)
695 return !fprintf(stdout, "usage: %s [-v] file.rom [args..]\n", argv[0]);
696 else if(!system_boot(argv[i++], argc > 2))
697 return !fprintf(stdout, "Could not load %s.\n", argv[i - 1]);
698 if(uxn_eval(0x100) && console_vector) {
699 for(; i < argc; i++) {
700 char c, *p = argv[i];
701 while(!dev[0x0f] && (c = *p++))
702 console_input(c, CONSOLE_ARG);
703 console_input('\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA);
704 }
705 while(!dev[0x0f]) {
706 char c = fgetc(stdin);
707 if(feof(stdin)) break;
708 console_input(c, CONSOLE_STD);
709 }
710 console_input(0, CONSOLE_END);
711 }
712 return dev[0x0f] & 0x7f;
713}