1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
5#include <sys/mman.h>
6#include <poll.h>
7#include <time.h>
8#include <wayland-client.h>
9#include <xkbcommon/xkbcommon.h>
10#include <linux/input-event-codes.h>
11#include "xdg-shell-client-protocol.h"
12#include "wlr-layer-shell-client-protocol.h"
13
14/*
15Copyright (c) 2021-2026 Devine Lu Linvega, Andrew Alderwick,
16Andrew Richards, Eiríkr Åsheim, Sigrid Solveig Haflínudóttir
17
18Permission to use, copy, modify, and distribute this software for any
19purpose with or without fee is hereby granted, provided that the above
20copyright notice and this permission notice appear in all copies.
21
22THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
23WITH REGARD TO THIS SOFTWARE.
24
25cc -DNDEBUG -O2 -g0 -s uxn11.c -lX11 -lutil -o uxn11
26*/
27
28extern int mkstemp(char *template);
29
30typedef signed char Sint8;
31typedef unsigned char Uint8;
32typedef signed short Sint16;
33typedef unsigned short Uint16;
34typedef signed int Sint32;
35typedef unsigned int Uint32;
36typedef void (*deo_handler)(void);
37typedef Uint8 (*dei_handler)(void);
38
39static unsigned int uxn_eval(Uint16 pc);
40static Uint8 *ram, dev[0x100], stk[2][0x100], ptr[2];
41static int console_vector, screen_vector, controller_vector, mouse_vector;
42static int rX, rY, rA, rMX, rMY, rMA, rML, rDX, rDY, rL1, rL2;
43
44#define SAMPLE_FREQUENCY 44100
45#define POLYPHONY 4
46#define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025)
47#define ADSR_STEP (SAMPLE_FREQUENCY / 0xf)
48
49typedef struct {
50 Uint8 *addr;
51 Uint32 count, advance, period, age, a, d, s, r;
52 Uint16 i, len;
53 Sint8 volume[2];
54 Uint8 pitch, repeat;
55} UxnAudio;
56
57static UxnAudio uxn_audio[POLYPHONY];
58
59static Uint32 advances[12] = {
60 0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1,
61 0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c
62};
63
64/* clang-format off */
65
66#define BANKS 0x10
67#define BANKS_CAP BANKS * 0x10000
68#define WIDTH (64 * 8)
69#define HEIGHT (40 * 8)
70
71static inline Uint16 peek2(const Uint8 *d) { return ((Uint16)d[0] << 8) | d[1]; }
72static inline void poke2(Uint8 *d, Uint16 v) { d[0] = v >> 8, d[1] = v; }
73
74/* clang-format on */
75
76/*
77@|System ------------------------------------------------------------ */
78
79static char *system_boot_path;
80static void emu_screenshot(void);
81
82static void
83system_print(char *name, int r)
84{
85 Uint8 i;
86 fprintf(stderr, "%s%c", name, ptr[r] - 8 ? ' ' : '|');
87 for(i = ptr[r] - 8; i != ptr[r]; i++)
88 fprintf(stderr, "%02x%c", stk[r][i], i == 0xff ? '|' : ' ');
89 fprintf(stderr, "<%02x\n", ptr[r]);
90}
91
92static unsigned int
93system_load(const char *rom_path)
94{
95 FILE *f = fopen(rom_path, "rb");
96 if(f) {
97 unsigned int i = 0, l = fread(ram + 0x100, 0x10000 - 0x100, 1, f);
98 while(l && ++i < BANKS)
99 l = fread(ram + i * 0x10000, 0x10000, 1, f);
100 fclose(f);
101 }
102 return !!f;
103}
104
105static unsigned int
106system_boot(char *rom_path, const unsigned int has_args)
107{
108 ram = (Uint8 *)calloc(BANKS_CAP, sizeof(Uint8));
109 system_boot_path = rom_path;
110 dev[0x17] = has_args;
111 return ram && system_load(rom_path);
112}
113
114static unsigned int
115system_reboot(const unsigned int soft)
116{
117 memset(dev, 0, 0x100);
118 memset(stk[0], 0, 0x100);
119 memset(stk[1], 0, 0x100);
120 if(soft)
121 memset(ram + 0x100, 0, 0xff00);
122 else
123 memset(ram, 0, 0x10000);
124 ptr[0] = ptr[1] = 0;
125 console_vector = screen_vector = controller_vector = mouse_vector = 0;
126 rX = rY = rA = rMX = rMY = rMA = rML = rDX = rDY = rL1 = rL2 = 0;
127 return system_load(system_boot_path);
128}
129
130static void
131system_deo_expansion(void)
132{
133 const Uint16 exp = peek2(dev + 2);
134 Uint8 *aptr = ram + exp;
135 Uint16 length = peek2(aptr + 1);
136 unsigned int bank = peek2(aptr + 3) * 0x10000;
137 unsigned int addr = peek2(aptr + 5);
138 if(ram[exp] == 0x0) {
139 if(bank < BANKS_CAP)
140 memset(ram + bank + addr, ram[exp + 7], length);
141 } else if(ram[exp] == 1 || ram[exp] == 2) {
142 unsigned int dst_bank = peek2(aptr + 7) * 0x10000;
143 unsigned int dst_addr = peek2(aptr + 9);
144 if(bank < BANKS_CAP && dst_bank < BANKS_CAP)
145 memmove(ram + dst_bank + dst_addr, ram + bank + addr, length);
146 } else
147 fprintf(stderr, "Unknown command: %02x\n", ram[exp]);
148}
149
150static void
151system_deo_print(void)
152{
153 if(dev[0xe] & 0x1)
154 system_print("WST", 0), system_print("RST", 1);
155 if(dev[0xe] & 0x2)
156 emu_screenshot();
157}
158
159/* clang-format off */
160
161static Uint8 system_dei_wst(void) { return ptr[0]; }
162static Uint8 system_dei_rst(void) { return ptr[1]; }
163static void system_deo_wst(void) { ptr[0] = dev[4]; }
164static void system_deo_rst(void) { ptr[1] = dev[5]; }
165
166/* clang-format on */
167
168/*
169@|Console ----------------------------------------------------------- */
170
171#define CONSOLE_STD 0x1
172#define CONSOLE_ARG 0x2
173#define CONSOLE_EOA 0x3
174#define CONSOLE_END 0x4
175
176static void
177console_input(int c, unsigned int type)
178{
179 if(c == EOF) c = 0, type = CONSOLE_END;
180 dev[0x12] = c, dev[0x17] = type;
181 if(console_vector)
182 uxn_eval(console_vector);
183}
184
185#include <signal.h>
186#include <sys/wait.h>
187
188#ifdef __linux
189#include <pty.h>
190#include <sys/prctl.h>
191#endif
192
193#ifdef __NetBSD__
194#include <sys/ioctl.h>
195#include <util.h>
196#endif
197
198/* subprocess support */
199static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
200static int child_mode;
201static int to_child_fd[2];
202static int from_child_fd[2];
203static int saved_in;
204static int saved_out;
205static pid_t child_pid;
206
207/* child_mode:
208* 0x01: writes to child's stdin
209* 0x02: reads from child's stdout
210* 0x04: reads from child's stderr
211* 0x08: kill previous process (if any) but do not start
212* (other bits ignored for now )
213*/
214
215#define CMD_LIVE 0x15 /* 0x00 not started, 0x01 running, 0xff dead */
216#define CMD_EXIT 0x16 /* if dead, exit code of process */
217#define CMD_ADDR 0x1c /* address to read command args from */
218#define CMD_MODE 0x1e /* mode to execute, 0x00 to 0x07 */
219
220/* call after we're sure the process has exited */
221
222static void
223clean_after_child(void)
224{
225 child_pid = 0;
226 if(child_mode & 0x01) {
227 close(to_child_fd[1]);
228 dup2(saved_out, 1);
229 }
230 if(child_mode & (0x04 | 0x02)) {
231 close(from_child_fd[0]);
232 dup2(saved_in, 0);
233 }
234 child_mode = 0;
235 saved_in = -1;
236 saved_out = -1;
237}
238
239static void
240start_fork_pipe(void)
241{
242 pid_t pid;
243 pid_t parent_pid = getpid();
244 int addr = peek2(&dev[CMD_ADDR]);
245 fflush(stdout);
246 if(child_mode & 0x08) {
247 dev[CMD_EXIT] = dev[CMD_LIVE] = 0x00;
248 return;
249 }
250 if(child_mode & 0x01) {
251 /* parent writes to child's stdin */
252 if(pipe(to_child_fd) == -1) {
253 dev[CMD_EXIT] = dev[CMD_LIVE] = 0xff;
254 fprintf(stderr, "Pipe error to child.\n");
255 return;
256 }
257 }
258 if(child_mode & (0x04 | 0x02)) {
259 /* parent reads from child's stdout and/or stderr */
260 if(pipe(from_child_fd) == -1) {
261 dev[CMD_EXIT] = dev[CMD_LIVE] = 0xff;
262 fprintf(stderr, "Pipe error from child.\n");
263 return;
264 }
265 }
266
267 fork_args[2] = (char *)&ram[addr];
268 pid = fork();
269 if(pid < 0) { /* failure */
270 dev[CMD_EXIT] = dev[CMD_LIVE] = 0xff;
271 fprintf(stderr, "Fork failure.\n");
272 } else if(pid == 0) { /* child */
273
274#ifdef __linux__
275 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
276 if(r == -1) {
277 perror(0);
278 exit(6);
279 }
280 if(getppid() != parent_pid) exit(13);
281#endif
282
283 if(child_mode & 0x01) {
284 dup2(to_child_fd[0], 0);
285 close(to_child_fd[1]);
286 }
287 if(child_mode & (0x04 | 0x02)) {
288 if(child_mode & 0x02) dup2(from_child_fd[1], 1);
289 if(child_mode & 0x04) dup2(from_child_fd[1], 2);
290 close(from_child_fd[0]);
291 }
292 fflush(stdout);
293 execvp(fork_args[0], fork_args);
294 exit(1);
295 } else { /*parent*/
296 child_pid = pid;
297 dev[CMD_LIVE] = 0x01;
298 dev[CMD_EXIT] = 0x00;
299 if(child_mode & 0x01) {
300 saved_out = dup(1);
301 dup2(to_child_fd[1], 1);
302 close(to_child_fd[0]);
303 }
304 if(child_mode & (0x04 | 0x02)) {
305 saved_in = dup(0);
306 dup2(from_child_fd[0], 0);
307 close(from_child_fd[1]);
308 }
309 }
310}
311
312static void
313check_child(void)
314{
315 int wstatus;
316 if(child_pid) {
317 if(waitpid(child_pid, &wstatus, WNOHANG)) {
318 dev[CMD_LIVE] = 0xff;
319 dev[CMD_EXIT] = WEXITSTATUS(wstatus);
320 clean_after_child();
321 } else {
322 dev[CMD_LIVE] = 0x01;
323 dev[CMD_EXIT] = 0x00;
324 }
325 }
326}
327
328static void
329kill_child(void)
330{
331 int wstatus;
332 if(child_pid) {
333 kill(child_pid, 9);
334 if(waitpid(child_pid, &wstatus, WNOHANG)) {
335 dev[CMD_LIVE] = 0xff;
336 dev[CMD_EXIT] = WEXITSTATUS(wstatus);
337 clean_after_child();
338 }
339 }
340}
341
342static void
343console_deo_fork(void)
344{
345 fflush(stderr);
346 kill_child();
347 child_mode = dev[CMD_MODE];
348 start_fork_pipe();
349}
350
351static void
352console_close(void)
353{
354 kill_child();
355}
356
357/* clang-format off */
358
359static Uint8 console_dei_childlive(void) { check_child(); return dev[CMD_LIVE]; }
360static Uint8 console_dei_childexit(void) { check_child(); return dev[CMD_EXIT]; }
361static void console_deo_vector(void) { console_vector = peek2(&dev[0x10]); }
362static void console_deo_stdout(void) { fputc(dev[0x18], stdout), fflush(stdout); }
363static void console_deo_stderr(void) { fputc(dev[0x19], stderr), fflush(stderr); }
364static void console_deo_debug1(void) { fprintf(stderr, "%02x", dev[0x1a]); }
365static void console_deo_debug2(void) { fprintf(stderr, "%02x%02x", dev[0x1a], dev[0x1b]); }
366
367/* clang-format on */
368
369/*
370@|Screen ------------------------------------------------------------ */
371
372static int screen_zoom = 1, screen_reqsize, screen_reqdraw;
373static int screen_width, screen_height, screen_wmar2, screen_hmar2;
374static int *screen_pixels, screen_palette[16];
375static Uint8 *screen_layers;
376
377void emu_redraw(void), emu_resize(void);
378
379static const Uint8 blend_lut[16][2][4] = {
380 {{0, 0, 1, 2}, {0, 0, 4, 8}},
381 {{0, 1, 2, 3}, {0, 4, 8, 12}},
382 {{0, 2, 3, 1}, {0, 8, 12, 4}},
383 {{0, 3, 1, 2}, {0, 12, 4, 8}},
384 {{1, 0, 1, 2}, {4, 0, 4, 8}},
385 {{1, 1, 2, 3}, {4, 4, 8, 12}},
386 {{1, 2, 3, 1}, {4, 8, 12, 4}},
387 {{1, 3, 1, 2}, {4, 12, 4, 8}},
388 {{2, 0, 1, 2}, {8, 0, 4, 8}},
389 {{2, 1, 2, 3}, {8, 4, 8, 12}},
390 {{2, 2, 3, 1}, {8, 8, 12, 4}},
391 {{2, 3, 1, 2}, {8, 12, 4, 8}},
392 {{3, 0, 1, 2}, {12, 0, 4, 8}},
393 {{3, 1, 2, 3}, {12, 4, 8, 12}},
394 {{3, 2, 3, 1}, {12, 8, 12, 4}},
395 {{3, 3, 1, 2}, {12, 12, 4, 8}}};
396
397
398static void
399system_deo_colorize(void)
400{
401 unsigned int i, shift, colors[4];
402 for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
403 Uint8
404 r = dev[0x8 + i / 2] >> shift & 0xf,
405 g = dev[0xa + i / 2] >> shift & 0xf,
406 b = dev[0xc + i / 2] >> shift & 0xf;
407 colors[i] = 0x0f000000 | r << 16 | g << 8 | b;
408 colors[i] |= colors[i] << 4;
409 }
410 for(i = 0; i < 16; i++)
411 screen_palette[i] = colors[i < 4 ? i : i >> 2];
412 screen_reqdraw = 1;
413}
414
415static void
416screen_resize(int width, int height)
417{
418 if(width != screen_width || height != screen_height) {
419 int length;
420 screen_width = width, screen_wmar2 = width + 0x10;
421 poke2(&dev[0x22], screen_width);
422 screen_height = height, screen_hmar2 = height + 0x10;
423 poke2(&dev[0x24], screen_height);
424 length = screen_wmar2 * screen_hmar2;
425 screen_layers = realloc(screen_layers, length);
426 memset(screen_layers, 0, length);
427 screen_reqsize = screen_reqdraw = 1;
428 }
429}
430
431static void
432screen_redraw(void)
433{
434 if(screen_zoom == 1) {
435 int y, *dst_ptr = screen_pixels;
436 Uint8 *src_ptr = &screen_layers[8 * screen_wmar2 + 8];
437 for(y = 0; y < screen_height; y++) {
438 int *dst_end = dst_ptr + screen_width;
439 while(dst_ptr < dst_end)
440 *dst_ptr++ = screen_palette[*src_ptr++];
441 src_ptr += 0x10;
442 }
443 } else {
444 const int stride = screen_width * screen_zoom;
445 const int zoom_stride = screen_zoom * stride;
446 int x, y, k, l, row_base = 0, i_base = 8 * screen_wmar2 + 8;
447 for(y = 0; y < screen_height; y++, row_base += zoom_stride, i_base += screen_wmar2) {
448 int i = i_base, xs = 0;
449 for(x = 0; x < screen_width; x++, i++, xs += screen_zoom) {
450 const int c = screen_palette[screen_layers[i]];
451 int oo = row_base + xs;
452 for(k = 0; k < screen_zoom; k++, oo += stride)
453 for(l = 0; l < screen_zoom; l++)
454 screen_pixels[oo + l] = c;
455 }
456 }
457 }
458 screen_reqdraw = 0;
459}
460
461static void
462screen_update(void)
463{
464 if(screen_vector)
465 uxn_eval(screen_vector);
466 if(screen_reqsize) {
467 screen_reqsize = 0;
468 emu_resize();
469 }
470 if(screen_reqdraw)
471 screen_redraw();
472}
473
474static void
475screen_deo_pixel(void)
476{
477 const int ctrl = dev[0x2e];
478 const int hi = ctrl & 0x40;
479 const Uint8 mask = hi ? 0x3 : 0xc;
480 const Uint8 color = hi ? ((ctrl & 0x3) << 2) : (ctrl & 0x3);
481 const Uint16 x = rX, y = rY;
482 if(x < screen_width && y < screen_height) {
483 /* fill mode */
484 if(ctrl & 0x80) {
485 const int x1 = (ctrl & 0x10) ? 8 : x + 8;
486 const int x2 = (ctrl & 0x10) ? x : screen_width;
487 const int y1 = (ctrl & 0x20) ? 8 : y + 8;
488 const int y2 = (ctrl & 0x20) ? y : screen_height;
489 const int hor = (x2 + 8) - x1;
490 const int ver = (y2 + 8) - y1;
491 Uint8 *row = &screen_layers[y1 * screen_wmar2 + x1];
492 Uint8 *end = row + ver * screen_wmar2;
493 for(; row < end; row += screen_wmar2) {
494 int px;
495 Uint8 *dst = row;
496 for(px = 0; px < hor; px++)
497 dst[px] = (dst[px] & mask) | color;
498 }
499 }
500 /* pixel mode */
501 else {
502 Uint8 *dst = &screen_layers[(y + 8) * screen_wmar2 + (x + 8)];
503 *dst = (*dst & mask) | color;
504 if(rMX) rX += ctrl & 0x10 ? -1 : 1;
505 if(rMY) rY += ctrl & 0x20 ? -1 : 1;
506 }
507 screen_reqdraw = 1;
508 }
509}
510
511static void
512screen_deo_sprite(void)
513{
514 const int ctrl = dev[0x2f];
515 const int flipx = ctrl & 0x10, flipy = ctrl & 0x20;
516 const int dx = flipx ? -rDY : rDY, dy = flipy ? -rDX : rDX;
517 const int row_start = flipx ? 0 : 7, row_delta = flipx ? 1 : -1;
518 const int col_start = flipy ? 7 : 0, col_delta = flipy ? -1 : 1;
519 const int layer = ctrl & 0x40, layer_mask = layer ? 0x3 : 0xc;
520 const int is_2bpp = ctrl >> 7;
521 const int addr_incr = rMA << is_2bpp;
522 const int stride = screen_wmar2;
523 const int blend = ctrl & 0xf;
524 const int row_step = dy * stride;
525 const Uint8 opaque_mask = blend % 5;
526 const Uint8 *table = blend_lut[blend][layer >> 6];
527 int i, j, x = rX, y = rY;
528 int row_base = (y + 8) * stride;
529 for(i = 0; i <= rML; i++, x += dx, y += dy, rA += addr_incr, row_base += row_step) {
530 const Uint16 x0 = x + 8, y0 = y + 8;
531 if(x0 + 8 >= stride || y0 + 8 >= screen_hmar2) continue;
532 Uint8 *dst = screen_layers + row_base + x0;
533 const Uint8 *col = &ram[rA + col_start];
534 for(j = 0; j < 8; j++, dst += stride, col += col_delta) {
535 Uint8 *d = dst;
536 const int ch1 = *col;
537 const int ch2 = is_2bpp ? (col[8] << 1) : 0;
538 int row = row_start;
539 for(int k = 0; k < 8; k++, d++, row += row_delta) {
540 const int color = ((ch1 >> row) & 1) | ((ch2 >> row) & 2);
541 if(opaque_mask || color)
542 *d = (*d & layer_mask) | table[color];
543 }
544 }
545 }
546 screen_reqdraw = 1;
547 if(rMX) rX += flipx ? -rDX : rDX;
548 if(rMY) rY += flipy ? -rDY : rDY;
549}
550
551/* clang-format off */
552
553static Uint8 screen_dei_rx(void) { dev[0x29] = rX; return rX >> 8; }
554static Uint8 screen_dei_ry(void) { dev[0x2b] = rY; return rY >> 8; }
555static Uint8 screen_dei_ra(void) { dev[0x2d] = rA; return rA >> 8; }
556static void screen_deo_vector(void) { screen_vector = peek2(&dev[0x20]); }
557static void screen_deo_width(void) { screen_resize(peek2(&dev[0x22]) & 0xfff, screen_height & 0xfff); }
558static void screen_deo_height(void) { screen_resize(screen_width & 0xfff, peek2(&dev[0x24]) & 0xfff); }
559static void screen_deo_auto(void) { rMX = dev[0x26] & 0x1, rMY = dev[0x26] & 0x2, rMA = (dev[0x26] & 0x4) << 1, rML = dev[0x26] >> 4, rDX = rMX << 3, rDY = rMY << 2; }
560static void screen_deo_x(void) { rX = (Sint16)peek2(&dev[0x28]); }
561static void screen_deo_y(void) { rY = (Sint16)peek2(&dev[0x2a]); }
562static void screen_deo_addr(void) { rA = peek2(&dev[0x2c]); }
563
564/* clang-format on */
565
566/*
567@|Audio ------------------------------------------------------------- */
568#define MINIAUDIO_IMPLEMENTATION
569#include "miniaudio.h"
570
571static unsigned audio_rate;
572
573static Sint32
574envelope(UxnAudio *c, Uint32 age)
575{
576 if(!c->r)
577 return 0x0888;
578 if(age < c->a)
579 return 0x0888 * age / c->a;
580 if(age < c->d)
581 return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a);
582 if(age < c->s)
583 return 0x0444;
584 if(age < c->r)
585 return 0x0444 * (c->r - age) / (c->r - c->s);
586 c->advance = 0;
587 return 0x0000;
588}
589
590static Uint8
591audio_get_vu(int instance)
592{
593 int i;
594 UxnAudio *c = &uxn_audio[instance];
595 Sint32 sum[2] = {0, 0};
596 if(!c->advance || !c->period)
597 return 0;
598 for(i = 0; i < 2; i++) {
599 if(!c->volume[i])
600 continue;
601 sum[i] = 1 + envelope(c, c->age) * c->volume[i] / 0x800;
602 if(sum[i] > 0xf)
603 sum[i] = 0xf;
604 }
605 return (sum[0] << 4) | sum[1];
606}
607
608static Uint16
609audio_get_position(int instance)
610{
611 return uxn_audio[instance].i;
612}
613
614static void
615audio_start(int instance, Uint8 *d)
616{
617 UxnAudio *c = &uxn_audio[instance];
618 Uint8 pitch = d[0xf] & 0x7f;
619 Uint16 addr = peek2(d + 0xc);
620 Uint16 adsr = peek2(d + 0x8);
621 c->len = peek2(d + 0xa);
622 if(c->len > 0x10000 - addr)
623 c->len = 0x10000 - addr;
624 c->addr = &ram[addr];
625 c->volume[0] = d[0xe] >> 4;
626 c->volume[1] = d[0xe] & 0xf;
627 c->repeat = !(d[0xf] & 0x80);
628 if(pitch < 108 && c->len)
629 c->advance = advances[pitch % 12] >> (8 - pitch / 12);
630 else {
631 c->advance = 0;
632 return;
633 }
634 c->a = ADSR_STEP * (adsr >> 12);
635 c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a;
636 c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d;
637 c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s;
638 c->age = 0;
639 c->i = 0;
640 if(c->len <= 0x100)
641 c->period = NOTE_PERIOD * 337 / 2 / c->len;
642 else
643 c->period = NOTE_PERIOD;
644
645}
646
647#define AUDIO_CHAN(n, base) \
648static void audio##n##_deo_vector (void) {} \
649static void audio##n##_deo_volume (void) {} \
650static void audio##n##_deo_pitch (void) { \
651 audio_start(n, &dev[base]); \
652} \
653static Uint8 audio##n##_dei_position (void) { \
654 return audio_get_position(n) >> 8; \
655} \
656static Uint8 audio##n##_dei_position_lo(void) { \
657 return (Uint8)audio_get_position(n); \
658} \
659static Uint8 audio##n##_dei_output (void) { \
660 return audio_get_vu(n); \
661}
662
663AUDIO_CHAN(0, 0x30)
664AUDIO_CHAN(1, 0x40)
665AUDIO_CHAN(2, 0x50)
666AUDIO_CHAN(3, 0x60)
667
668
669static ma_device audio_device;
670#define RING_SIZE 8192
671static Sint16 audio_ring[RING_SIZE];
672static volatile int audio_ring_w = 0;
673static volatile int audio_ring_r = 0;
674
675static void
676data_callback(ma_device *device, void *output, const void *input, ma_uint32 framecount)
677{
678 Sint16 *out = (Sint16*)output;
679 ma_uint32 i;
680 int w = audio_ring_w;
681 int r = audio_ring_r;
682 (void)device; (void)input;
683 for(i = 0; i < framecount * 2; i++) {
684 if(r != w) {
685 out[i] = audio_ring[r];
686 r = (r + 1) % RING_SIZE;
687 } else
688 out[i] = 0;
689 }
690 audio_ring_r = r;
691}
692
693static int audio_initialized = 0;
694
695void
696audio_init(void)
697{
698 ma_device_config config = ma_device_config_init(ma_device_type_playback);
699 config.playback.format = ma_format_s16;
700 config.playback.channels = 2;
701 config.sampleRate = SAMPLE_FREQUENCY;
702 config.dataCallback = data_callback;
703 if(ma_device_init(NULL, &config, &audio_device) == MA_SUCCESS) {
704 ma_device_start(&audio_device);
705 audio_initialized = 1;
706 }
707 audio_rate = SAMPLE_FREQUENCY;
708}
709
710void
711audio_write(Sint16 *buf, unsigned count)
712{
713 (void)buf;
714 (void)count;
715 // that white cat thing presenting his hand in a confused matter
716 unsigned i;
717 int w = audio_ring_w;
718 for(i = 0; i < count * 2; i++) {
719 audio_ring[w] = buf[i];
720 w = (w + 1) % RING_SIZE;
721 }
722 audio_ring_w = w;
723}
724
725static void
726audio_finished_handler(int instance)
727{
728 Uint8 *port_value = &dev[0x30 + 0x10 * instance];
729 Uint16 vector = port_value[0] << 8 | port_value[1];
730 if(vector)
731 uxn_eval(vector);
732}
733
734static int
735audio_render(int instance, Sint16 *sample, Sint16 *end)
736{
737 UxnAudio *c = &uxn_audio[instance];
738 Sint32 s;
739 if(!c->advance || !c->period)
740 return 0;
741 while(sample < end) {
742 c->count += c->advance;
743 c->i += c->count / c->period;
744 c->count %= c->period;
745 if(c->i >= c->len) {
746 if(!c->repeat) {
747 c->advance = 0;
748 break;
749 }
750 c->i %= c->len;
751 }
752 s = (Sint8)(*(c->addr + c->i) + 0x80) * envelope(c, c->age++);
753 *sample++ += s * c->volume[0] / 0x180;
754 *sample++ += s * c->volume[1] / 0x180;
755 }
756 if(!c->advance)
757 audio_finished_handler(instance);
758 return 1;
759}
760
761static void
762audio_output(void)
763{
764 int c;
765 Sint16 mix[2048];
766 int r = audio_ring_r;
767 int w = audio_ring_w;
768 int fill = (w - r + RING_SIZE) % RING_SIZE;
769 if(fill >= 4096)
770 return;
771 unsigned count = (4096 - fill) / 2;
772 if(count > 1024)
773 count = 1024;
774 memset(mix, 0, count * 2 * sizeof(Sint16));
775 for(c = 0; c < POLYPHONY; c++) {
776 audio_render(c, mix, mix + count * 2);
777 }
778 audio_write(mix, count);
779}
780
781/*
782@|Controller -------------------------------------------------------- */
783
784static void
785controller_down(Uint8 mask)
786{
787 if(mask) {
788 dev[0x82] |= mask;
789 if(controller_vector)
790 uxn_eval(controller_vector);
791 }
792}
793
794static void
795controller_up(Uint8 mask)
796{
797 if(mask) {
798 dev[0x82] &= (~mask);
799 if(controller_vector)
800 uxn_eval(controller_vector);
801 }
802}
803
804static void
805controller_key(Uint8 key)
806{
807 if(key) {
808 dev[0x83] = key;
809 if(controller_vector)
810 uxn_eval(controller_vector);
811 dev[0x83] = 0;
812 }
813}
814
815void
816controller_deo_vector(void)
817{
818 controller_vector = peek2(&dev[0x80]);
819}
820
821/*
822@|Mouse ------------------------------------------------------------- */
823
824static void
825mouse_down(Uint8 mask)
826{
827 dev[0x96] |= mask;
828 if(mouse_vector)
829 uxn_eval(mouse_vector);
830}
831
832static void
833mouse_up(Uint8 mask)
834{
835 dev[0x96] &= (~mask);
836 if(mouse_vector)
837 uxn_eval(mouse_vector);
838}
839
840static void
841mouse_pos(Uint16 x, Uint16 y)
842{
843 poke2(&dev[0x92], x);
844 poke2(&dev[0x94], y);
845 if(mouse_vector)
846 uxn_eval(mouse_vector);
847}
848
849static void
850mouse_scroll(Uint16 x, Uint16 y)
851{
852 poke2(&dev[0x9a], x);
853 poke2(&dev[0x9c], -y);
854 if(mouse_vector)
855 uxn_eval(mouse_vector);
856 poke2(&dev[0x9a], 0);
857 poke2(&dev[0x9c], 0);
858}
859
860void
861mouse_deo_vector(void)
862{
863 mouse_vector = peek2(&dev[0x90]);
864}
865
866/*
867@|File -------------------------------------------------------------- */
868
869#include <dirent.h>
870#include <sys/stat.h>
871#include <string.h>
872
873static char *theme_file = NULL;
874
875typedef struct {
876 union {
877 FILE *f;
878 DIR *dir;
879 } h;
880 char *filepath;
881 enum { IDLE,
882 FILE_READ,
883 FILE_WRITE,
884 DIR_READ,
885 DIR_WRITE } state;
886} UxnFile;
887
888static UxnFile ufs[2];
889
890static inline unsigned int
891put_text(Uint8 *dest, unsigned int *length, const char *text)
892{
893 Uint8 *anchor = dest;
894 while(*text && *length) {
895 *dest = *text++, dest++;
896 *length = *length - 1;
897 }
898 *dest = 0;
899 return dest - anchor;
900}
901
902static inline unsigned int
903put_size(Uint8 *dest, unsigned int *length, unsigned int len, unsigned int size)
904{
905 unsigned int i;
906 for(i = 0, dest += len; i < len && *length; i++) {
907 *(--dest) = "0123456789abcdef"[(Uint8)(size & 0xf)];
908 size >>= 4;
909 *length = *length - 1;
910 }
911 return len;
912}
913
914static unsigned int
915put_fill(Uint8 *dest, unsigned int *length, unsigned int len, char c)
916{
917 unsigned int i;
918 for(i = 0; i < len && *length; i++) {
919 *dest++ = c;
920 *length = *length - 1;
921 }
922 return len;
923}
924
925static unsigned int
926put_stat(Uint8 *dest, unsigned int *length, unsigned int len, unsigned int size, unsigned int err, unsigned int dir)
927{
928 unsigned int max = 1 << (len << 2);
929 if(dir)
930 return put_fill(dest, length, len, '-');
931 else if(err)
932 return put_fill(dest, length, len, '!');
933 else if(size < max)
934 return put_size(dest, length, len, size);
935 return put_fill(dest, length, len, '?');
936}
937
938static unsigned int
939put_statfile(Uint8 *dest, unsigned int *length, char *pathbuf, unsigned int prefix_len, unsigned int cap, const char *basename)
940{
941 unsigned int err, dir;
942 struct stat st;
943 Uint8 *anchor = dest;
944 char *p = pathbuf + prefix_len, *end = pathbuf + cap - 1;
945 const char *b = basename;
946 while(*b && p < end)
947 *p++ = *b++;
948 *p = 0;
949 err = stat(pathbuf, &st);
950 dir = !err && S_ISDIR(st.st_mode);
951 dest += put_stat(dest, length, 4, st.st_size, err, dir);
952 dest += put_text(dest, length, "\t");
953 dest += put_text(dest, length, basename);
954 dest += put_text(dest, length, dir ? "/\n" : "\n");
955 return dest - anchor;
956}
957
958static unsigned int
959put_fdir(Uint8 *dest, unsigned int *length, const char *filepath, DIR *dir)
960{
961 Uint8 *p = dest, *end = dest + *length - 1;
962 struct dirent *de;
963 char pathbuf[0x200];
964 unsigned int prefix_len = 0;
965 char c = '/';
966 const char *fp = filepath;
967 while(*fp && prefix_len < sizeof pathbuf - 1)
968 c = *fp++, pathbuf[prefix_len++] = c;
969 if(c != '/' && prefix_len < sizeof pathbuf - 1)
970 pathbuf[prefix_len++] = '/';
971 while((de = readdir(dir)) && p < end) {
972 const char *name = de->d_name;
973 if(name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])))
974 continue;
975 p += put_statfile(p, length, pathbuf, prefix_len, sizeof pathbuf, name);
976 }
977 *p = 0;
978 return p - dest;
979}
980
981static unsigned int
982is_dir_path(const char *p)
983{
984 const char *end = p;
985 while(*end) end++;
986 return end > p && end[-1] == '/';
987}
988
989static unsigned int
990is_dir_real(char *p)
991{
992 struct stat st;
993 return stat(p, &st) == 0 && S_ISDIR(st.st_mode);
994}
995
996static unsigned int
997file_write_dir(const char *p)
998{
999 char buf[0x200];
1000 char *s = buf, *end = buf + sizeof buf - 1;
1001 unsigned int ok = 1;
1002 while(*p && ok && s < end) {
1003 *s++ = *p;
1004 if(*p++ == '/') {
1005 s[-1] = '\0';
1006 ok = is_dir_real(buf) || mkdir(buf, 0755) == 0;
1007 s[-1] = '/';
1008 }
1009 }
1010 return ok && !*p;
1011}
1012
1013static void
1014file_reset(unsigned int id)
1015{
1016 switch(ufs[id].state) {
1017 case FILE_READ:
1018 case FILE_WRITE: fclose(ufs[id].h.f); break;
1019 case DIR_READ: closedir(ufs[id].h.dir); break;
1020 default: break;
1021 }
1022 ufs[id].state = IDLE;
1023}
1024
1025static unsigned int
1026file_open(unsigned int id, unsigned int want_write, Uint8 flags)
1027{
1028 UxnFile *u = &ufs[id];
1029 struct stat st;
1030 if(want_write) {
1031 if(u->state == FILE_WRITE || u->state == DIR_WRITE)
1032 return 1;
1033 file_reset(id);
1034 file_write_dir(u->filepath);
1035 if(is_dir_path(u->filepath)) {
1036 u->state = is_dir_real(u->filepath) ? DIR_WRITE : IDLE;
1037 } else if((u->h.f = fopen(u->filepath, (flags & 0x01) ? "ab" : "wb"))) {
1038 u->state = FILE_WRITE;
1039 }
1040 } else {
1041 if(u->state == FILE_READ || u->state == DIR_READ)
1042 return 1;
1043 file_reset(id);
1044 if(stat(u->filepath, &st) == 0 && S_ISDIR(st.st_mode)) {
1045 if((u->h.dir = opendir(u->filepath)))
1046 u->state = DIR_READ;
1047 } else if((u->h.f = fopen(u->filepath, "rb"))) {
1048 u->state = FILE_READ;
1049 }
1050 }
1051 return u->state != IDLE;
1052}
1053
1054static unsigned int
1055file_init(unsigned int id, Uint16 addr)
1056{
1057 file_reset(id);
1058 char *path = (char *)&ram[addr];
1059 if(theme_file && strcmp(path, ".theme") == 0)
1060 ufs[id].filepath = theme_file;
1061 else
1062 ufs[id].filepath = path;
1063 return 0;
1064}
1065
1066static unsigned int
1067file_read(unsigned int id, Uint16 addr, unsigned int len)
1068{
1069 void *dest = &ram[addr];
1070 unsigned int n, length = len;
1071 if(ufs[id].filepath == 0)
1072 return 0;
1073 if(addr + len > 0x10000)
1074 length = 0x10000 - addr;
1075 if(!file_open(id, 0, 0))
1076 return 0;
1077 if(ufs[id].state == FILE_READ)
1078 n = fread(dest, 1, length, ufs[id].h.f);
1079 else
1080 n = put_fdir(dest, &length, ufs[id].filepath, ufs[id].h.dir);
1081 if(len > 0 && n == 0) file_reset(id);
1082 return n;
1083}
1084
1085static unsigned int
1086file_write(unsigned int id, Uint16 addr, unsigned int len, Uint8 flags)
1087{
1088 if(ufs[id].filepath == 0)
1089 return 0;
1090 if(addr + len > 0x10000)
1091 len = 0x10000 - addr;
1092 if(!file_open(id, 1, flags))
1093 return 0;
1094 if(ufs[id].state == FILE_WRITE) {
1095 unsigned int ret = fwrite(&ram[addr], 1, len, ufs[id].h.f);
1096 return (ret > 0 && fflush(ufs[id].h.f) != 0) ? 0 : ret;
1097 }
1098 return is_dir_real(ufs[id].filepath);
1099}
1100
1101static unsigned int
1102file_stat(unsigned int id, Uint16 addr, unsigned int len)
1103{
1104 unsigned int err, dir;
1105 unsigned int length = len;
1106 struct stat st;
1107 if(ufs[id].filepath == 0)
1108 return 0;
1109 if(addr + len > 0x10000)
1110 len = 0x10000 - addr;
1111 err = stat(ufs[id].filepath, &st);
1112 dir = !err && S_ISDIR(st.st_mode);
1113 return put_stat(&ram[addr], &length, len, st.st_size, err, dir);
1114}
1115
1116static unsigned int
1117file_delete(unsigned int id)
1118{
1119 if(ufs[id].filepath == 0)
1120 return 0;
1121 return !unlink(ufs[id].filepath);
1122}
1123
1124/* clang-format off */
1125
1126static void filea_deo_stat(void) { poke2(&dev[0xa2], file_stat(0, peek2(&dev[0xa4]), rL1)); }
1127static void filea_deo_delete(void) { poke2(&dev[0xa2], file_delete(0)); }
1128static void filea_deo_name(void) { poke2(&dev[0xa2], file_init(0, peek2(&dev[0xa8]))); }
1129static void filea_deo_length(void) { rL1 = peek2(&dev[0xaa]); }
1130static void filea_deo_read(void) { poke2(&dev[0xa2], file_read(0, peek2(&dev[0xac]), rL1)); }
1131static void filea_deo_write(void) { poke2(&dev[0xa2], file_write(0, peek2(&dev[0xae]), rL1, dev[0xa7])); }
1132static void fileb_deo_stat(void) { poke2(&dev[0xb2], file_stat(1, peek2(&dev[0xb4]), rL2)); }
1133static void fileb_deo_delete(void) { poke2(&dev[0xb2], file_delete(1)); }
1134static void fileb_deo_name(void) { poke2(&dev[0xb2], file_init(1, peek2(&dev[0xb8]))); }
1135static void fileb_deo_length(void) { rL2 = peek2(&dev[0xba]); }
1136static void fileb_deo_read(void) { poke2(&dev[0xb2], file_read(1, peek2(&dev[0xbc]), rL2)); }
1137static void fileb_deo_write(void) { poke2(&dev[0xb2], file_write(1, peek2(&dev[0xbe]), rL2, dev[0xb7])); }
1138
1139/* clang-format on */
1140
1141/*
1142@|Datetime ---------------------------------------------------------- */
1143
1144int datetime_busy;
1145time_t datetime_seconds;
1146struct tm *datetime_t, datetime_zt = {0};
1147
1148void
1149datetime_update(void)
1150{
1151 if(!datetime_busy) {
1152 datetime_seconds = time(NULL);
1153 datetime_t = localtime(&datetime_seconds);
1154 if(datetime_t == NULL)
1155 datetime_t = &datetime_zt;
1156 datetime_busy = 1;
1157 }
1158}
1159
1160/* clang-format off */
1161
1162static Uint8 datetime_dei_y(void) {
1163 datetime_update();
1164 const int v = (datetime_t->tm_year + 1900);
1165 dev[0xc1] = v;
1166 return v >> 8; }
1167static Uint8 datetime_dei_mon(void) { datetime_update(); return datetime_t->tm_mon; }
1168static Uint8 datetime_dei_day(void) { datetime_update(); return datetime_t->tm_mday; }
1169static Uint8 datetime_dei_hou(void) { datetime_update(); return datetime_t->tm_hour; }
1170static Uint8 datetime_dei_min(void) { datetime_update(); return datetime_t->tm_min; }
1171static Uint8 datetime_dei_sec(void) { datetime_update(); return datetime_t->tm_sec; }
1172static Uint8 datetime_dei_wday(void) { datetime_update(); return datetime_t->tm_wday; }
1173static Uint8 datetime_dei_yday(void) {
1174 datetime_update();
1175 const int v = datetime_t->tm_yday;
1176 dev[0xc9] = v;
1177 return v >> 8; }
1178static Uint8 datetime_dei_dst(void) { datetime_update(); return datetime_t->tm_isdst; }
1179
1180/* clang-format on */
1181
1182/*
1183@|Core -------------------------------------------------------------- */
1184
1185static const dei_handler dei_handlers[256] = {
1186 [0x04] = system_dei_wst,
1187 [0x05] = system_dei_rst,
1188 [0x15] = console_dei_childlive,
1189 [0x16] = console_dei_childexit,
1190 [0x28] = screen_dei_rx,
1191 [0x2a] = screen_dei_ry,
1192 [0x2c] = screen_dei_ra,
1193 [0x32] = audio0_dei_position,
1194 [0x33] = audio0_dei_position_lo,
1195 [0x34] = audio0_dei_output,
1196 [0x42] = audio1_dei_position,
1197 [0x43] = audio1_dei_position_lo,
1198 [0x44] = audio1_dei_output,
1199 [0x52] = audio2_dei_position,
1200 [0x53] = audio2_dei_position_lo,
1201 [0x54] = audio2_dei_output,
1202 [0x62] = audio3_dei_position,
1203 [0x63] = audio3_dei_position_lo,
1204 [0x64] = audio3_dei_output,
1205 [0xc0] = datetime_dei_y,
1206 [0xc2] = datetime_dei_mon,
1207 [0xc3] = datetime_dei_day,
1208 [0xc4] = datetime_dei_hou,
1209 [0xc5] = datetime_dei_min,
1210 [0xc6] = datetime_dei_sec,
1211 [0xc7] = datetime_dei_wday,
1212 [0xc8] = datetime_dei_yday,
1213 [0xca] = datetime_dei_dst};
1214
1215static const deo_handler deo_handlers[256] = {
1216 [0x03] = system_deo_expansion,
1217 [0x04] = system_deo_wst,
1218 [0x05] = system_deo_rst,
1219 [0x09] = system_deo_colorize,
1220 [0x0b] = system_deo_colorize,
1221 [0x0d] = system_deo_colorize,
1222 [0x0e] = system_deo_print,
1223 [0x11] = console_deo_vector,
1224 [0x18] = console_deo_stdout,
1225 [0x19] = console_deo_stderr,
1226 [0x1a] = console_deo_debug1,
1227 [0x1b] = console_deo_debug2,
1228 [0x1f] = console_deo_fork,
1229 [0x21] = screen_deo_vector,
1230 [0x23] = screen_deo_width,
1231 [0x25] = screen_deo_height,
1232 [0x26] = screen_deo_auto,
1233 [0x29] = screen_deo_x,
1234 [0x2b] = screen_deo_y,
1235 [0x2d] = screen_deo_addr,
1236 [0x2e] = screen_deo_pixel,
1237 [0x2f] = screen_deo_sprite,
1238 [0x30] = audio0_deo_vector,
1239 [0x3e] = audio0_deo_volume,
1240 [0x3f] = audio0_deo_pitch,
1241 [0x40] = audio1_deo_vector,
1242 [0x4e] = audio1_deo_volume,
1243 [0x4f] = audio1_deo_pitch,
1244 [0x50] = audio2_deo_vector,
1245 [0x5e] = audio2_deo_volume,
1246 [0x5f] = audio2_deo_pitch,
1247 [0x60] = audio3_deo_vector,
1248 [0x6e] = audio3_deo_volume,
1249 [0x6f] = audio3_deo_pitch,
1250 [0x81] = controller_deo_vector,
1251 [0x91] = mouse_deo_vector,
1252 [0xa5] = filea_deo_stat,
1253 [0xa6] = filea_deo_delete,
1254 [0xa9] = filea_deo_name,
1255 [0xab] = filea_deo_length,
1256 [0xad] = filea_deo_read,
1257 [0xaf] = filea_deo_write,
1258 [0xb5] = fileb_deo_stat,
1259 [0xb6] = fileb_deo_delete,
1260 [0xb9] = fileb_deo_name,
1261 [0xbb] = fileb_deo_length,
1262 [0xbd] = fileb_deo_read,
1263 [0xbf] = fileb_deo_write};
1264
1265static inline Uint8
1266emu_dei(const Uint8 port)
1267{
1268 dei_handler h = dei_handlers[port];
1269 return h ? h() : dev[port];
1270}
1271
1272static inline void
1273emu_deo(const Uint8 port, const Uint8 value)
1274{
1275 deo_handler h = deo_handlers[port];
1276 dev[port] = value;
1277 if(h) h();
1278}
1279
1280/* clang-format off */
1281
1282#define Ld1(o) stk[r][(Uint8)(ptr[r]-(o))]
1283#define Ld2(o) (Ld1(o) << 8 | stk[r][(Uint8)(ptr[r]-(o)+1)])
1284#define Ldx(o8,o16) d ? Ld2(o16) : Ld1(o8)
1285#define Re1(m) ptr[r] -= m;
1286#define Rex(m1,m2) ptr[r] -= d ? m2 : m1;
1287#define Pu1(s,v) stk[s][ptr[s]++] = v;
1288#define Pu2(s,v) Pu1(s,v>>8) Pu1(s,v)
1289#define Pux(s,v) if(d) Pu1(s,(v)>>8) Pu1(s,v)
1290#define Lda(s,o) Pu1(r,ram[o]) if(d) Pu1(r,ram[(s)(o+1)])
1291#define Sta(s,o,u) if(d) ram[o]=u>>8, ram[(s)(o+1)]=u; else ram[o]=u;
1292#define Dei Pu1(r,emu_dei(x)) if(d) Pu1(r,dev[(x+1)&0xff])
1293#define Deo if(d) dev[x]=y>>8, emu_deo(x+1,y); else emu_deo(x,y);
1294
1295#define MUTE (void)x;(void)y;(void)z;(void)r;(void)d;
1296
1297#define OP(opc,X,Y,Z,K,P) \
1298 case (opc)|0x00: { const int d=0,r=0,x=X,y=Y,z=Z; K P MUTE } break; \
1299 case (opc)|0x20: { const int d=1,r=0,x=X,y=Y,z=Z; K P MUTE } break; \
1300 case (opc)|0x40: { const int d=0,r=1,x=X,y=Y,z=Z; K P MUTE } break; \
1301 case (opc)|0x60: { const int d=1,r=1,x=X,y=Y,z=Z; K P MUTE } break; \
1302 case (opc)|0x80: { const int d=0,r=0,x=X,y=Y,z=Z; P MUTE } break; \
1303 case (opc)|0xa0: { const int d=1,r=0,x=X,y=Y,z=Z; P MUTE } break; \
1304 case (opc)|0xc0: { const int d=0,r=1,x=X,y=Y,z=Z; P MUTE } break; \
1305 case (opc)|0xe0: { const int d=1,r=1,x=X,y=Y,z=Z; P MUTE } break;
1306
1307static unsigned int
1308uxn_eval(Uint16 start_pc)
1309{
1310 Uint16 pc = start_pc;
1311 for(;;)
1312 switch(ram[pc++]) {
1313 /* BRK */ case 0x00: return 1;
1314 /* JCI */ case 0x20: { Uint16 a=ram[pc]<<8|ram[pc+1]; pc+=2; if(stk[0][--ptr[0]]) pc+=a; } break;
1315 /* JMI */ case 0x40: { Uint16 a=ram[pc]<<8|ram[pc+1]; pc+=2+a; } break;
1316 /* JSI */ case 0x60: { Uint16 a=ram[pc]<<8|ram[pc+1]; const Uint8 d=1; pc+=2; Pux(1,pc); pc+=a; } break;
1317 /* LIT */ case 0x80: { const Uint8 r=0,d=0; Lda(Uint16,pc); pc+=1; } break;
1318 /* LI2 */ case 0xa0: { const Uint8 r=0,d=1; Lda(Uint16,pc); pc+=2; } break;
1319 /* LIr */ case 0xc0: { const Uint8 r=1,d=0; Lda(Uint16,pc); pc+=1; } break;
1320 /* L2r */ case 0xe0: { const Uint8 r=1,d=1; Lda(Uint16,pc); pc+=2; } break;
1321 /* INC */ OP(0x01, Ldx(1,2),0, 0, Rex(1,2),Pux(r,x+1))
1322 /* POP */ OP(0x02, 0, 0, 0, Rex(1,2),{})
1323 /* NIP */ OP(0x03, Ldx(1,2),0, 0, Rex(2,4),Pux(r,x))
1324 /* SWP */ OP(0x04, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,x) Pux(r,y))
1325 /* ROT */ OP(0x05, Ldx(1,2),Ldx(2,4),Ldx(3,6),Rex(3,6),Pux(r,y) Pux(r,x) Pux(r,z))
1326 /* DUP */ OP(0x06, Ldx(1,2),0, 0, Rex(1,2),Pux(r,x) Pux(r,x))
1327 /* OVR */ OP(0x07, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y) Pux(r,x) Pux(r,y))
1328 /* EQU */ OP(0x08, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pu1(r,x==y))
1329 /* NEQ */ OP(0x09, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pu1(r,y!=x))
1330 /* GTH */ OP(0x0a, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pu1(r,y>x))
1331 /* LTH */ OP(0x0b, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pu1(r,y<x))
1332 /* JMP */ OP(0x0c, Ldx(1,2),0, 0, Rex(1,2),pc=d?x:(pc+(Sint8)x);)
1333 /* JCN */ OP(0x0d, Ldx(1,2),Ld1(d+2),0, Rex(2,3),if(y) pc=d?x:(pc+(Sint8)x);)
1334 /* JSR */ OP(0x0e, Ldx(1,2),0, 0, Rex(1,2),Pu2(!r,pc) pc=d?x:(pc+(Sint8)x);)
1335 /* STH */ OP(0x0f, Ldx(1,2),0, 0, Rex(1,2),Pux(!r,x))
1336 /* LDZ */ OP(0x10, Ld1(1), 0, 0, Re1(1), Lda(Uint8,x))
1337 /* STZ */ OP(0x11, Ld1(1), Ldx(2,3),0, Rex(2,3),Sta(Uint8,x,y))
1338 /* LDR */ OP(0x12, Ld1(1), 0, 0, Re1(1), Uint16 t=pc+(Sint8)x; Lda(Uint16,t))
1339 /* STR */ OP(0x13, Ld1(1), Ldx(2,3),0, Rex(2,3),Uint16 t=pc+(Sint8)x; Sta(Uint16,t,y))
1340 /* LDA */ OP(0x14, Ld2(2), 0, 0, Re1(2), Lda(Uint16,x))
1341 /* STA */ OP(0x15, Ld2(2), Ldx(3,4),0, Rex(3,4),Sta(Uint16,x,y))
1342 /* DEI */ OP(0x16, Ld1(1), 0, 0, Re1(1), Dei)
1343 /* DEO */ OP(0x17, Ld1(1), Ldx(2,3),0, Rex(2,3),Deo)
1344 /* ADD */ OP(0x18, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y+x))
1345 /* SUB */ OP(0x19, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y-x))
1346 /* MUL */ OP(0x1a, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y*x))
1347 /* DIV */ OP(0x1b, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,x?y/x:0))
1348 /* AND */ OP(0x1c, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y&x))
1349 /* ORA */ OP(0x1d, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y|x))
1350 /* EOR */ OP(0x1e, Ldx(1,2),Ldx(2,4),0, Rex(2,4),Pux(r,y^x))
1351 /* SFT */ OP(0x1f, Ld1(1) ,Ldx(2,3),0, Rex(2,3),Pux(r,(y>>(x&0xf))<<(x>>4)))
1352 }
1353 return 0;
1354}
1355
1356/* clang-format on */
1357
1358static struct wl_display *emu_dpy;
1359static struct wl_registry *emu_reg;
1360static struct wl_compositor *emu_comp;
1361static struct wl_shm *emu_shm;
1362static struct wl_seat *emu_seat;
1363static struct wl_pointer *emu_ptr;
1364static struct wl_keyboard *emu_kbd;
1365static struct xdg_wm_base *emu_wm;
1366static struct wl_surface *emu_surf;
1367static struct xdg_surface *emu_xdgsurf;
1368static struct xdg_toplevel *emu_top;
1369static struct wl_buffer *emu_bufs[2];
1370static struct zwlr_layer_shell_v1 *emu_layer_shell;
1371static struct zwlr_layer_surface_v1 *emu_layer_surf;
1372static int use_layer_shell = 0;
1373static int layer_shell_x = 0;
1374static int layer_shell_y = 0;
1375static int emu_configured;
1376static int active_buf;
1377
1378static void *shm_data;
1379static int shm_size, shm_bufsize, shm_width, shm_height;
1380
1381static struct xkb_context *xkb_ctx;
1382static struct xkb_keymap *xkb_map;
1383static struct xkb_state *xkb_state;
1384
1385#define CONINBUFSIZE 256
1386
1387void
1388emu_resize(void)
1389{
1390 const int width = screen_width * screen_zoom;
1391 const int height = screen_height * screen_zoom;
1392 if(width != shm_width || height != shm_height) {
1393 const int stride = width * 4;
1394 const int size = stride * height;
1395 struct wl_shm_pool *pool;
1396 int fd;
1397 if(shm_data)
1398 munmap(shm_data, shm_size);
1399 if(emu_bufs[0])
1400 wl_buffer_destroy(emu_bufs[0]);
1401 if(emu_bufs[1])
1402 wl_buffer_destroy(emu_bufs[1]);
1403 char tmpl[] = "/tmp/uxn-wl-XXXXXX";
1404 fd = mkstemp(tmpl);
1405 unlink(tmpl);
1406 ftruncate(fd, size * 2);
1407 shm_data = mmap(NULL, size * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1408 pool = wl_shm_create_pool(emu_shm, fd, size * 2);
1409 emu_bufs[0] = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888);
1410 emu_bufs[1] = wl_shm_pool_create_buffer(pool, size, width, height, stride, WL_SHM_FORMAT_XRGB8888);
1411 wl_shm_pool_destroy(pool);
1412 close(fd);
1413 shm_size = size * 2, shm_bufsize = size, shm_width = width, shm_height = height;
1414 }
1415 screen_pixels = (int *)((Uint8 *)shm_data + active_buf * shm_bufsize);
1416 active_buf = 0;
1417}
1418
1419void
1420emu_redraw(void)
1421{
1422 if(!emu_configured)
1423 return;
1424 wl_surface_attach(emu_surf, emu_bufs[active_buf], 0, 0);
1425 wl_surface_damage_buffer(emu_surf, 0, 0, screen_width * screen_zoom, screen_height * screen_zoom);
1426 wl_surface_commit(emu_surf);
1427}
1428
1429static void
1430emu_restart(unsigned int soft)
1431{
1432 system_reboot(soft);
1433 screen_width = screen_height = 0;
1434 screen_resize(WIDTH, HEIGHT);
1435 uxn_eval(0x100);
1436}
1437
1438static void
1439emu_screenshot(void)
1440{
1441 FILE *f;
1442 char filename[] = "_00x00.chr";
1443 unsigned int length = 4;
1444 int tx, ty, row, col, tw = screen_width / 8, th = screen_height / 8;
1445 put_size((Uint8 *)filename + 1, &length, 2, tw);
1446 put_size((Uint8 *)filename + 4, &length, 2, th);
1447 f = fopen(filename, "wb");
1448 if(f) {
1449 for(ty = 0; ty < th; ty++) {
1450 for(tx = 0; tx < tw; tx++) {
1451 Uint8 tile[16] = {0};
1452 for(row = 0; row < 8; row++) {
1453 const Uint8 *src = &screen_layers[(ty * 8 + row + 8) * screen_wmar2 + (tx * 8 + 8)];
1454 Uint8 b1 = 0, b2 = 0;
1455 for(col = 0; col < 8; col++) {
1456 Uint8 px = src[col];
1457 b1 |= ((px & 0x1) << (7 - col)), b2 |= (((px >> 2) & 0x1) << (7 - col));
1458 }
1459 tile[row] = b1, tile[row + 8] = b2;
1460 }
1461 fwrite(tile, 1, 16, f);
1462 }
1463 }
1464 fclose(f);
1465 }
1466}
1467
1468static Uint8
1469get_button(xkb_keysym_t sym)
1470{
1471 switch(sym) {
1472 case XKB_KEY_Up: return 0x10;
1473 case XKB_KEY_Down: return 0x20;
1474 case XKB_KEY_Left: return 0x40;
1475 case XKB_KEY_Right: return 0x80;
1476 case XKB_KEY_Control_L: return 0x01;
1477 case XKB_KEY_Alt_R:
1478 case XKB_KEY_Alt_L: return 0x02;
1479 case XKB_KEY_Shift_L: return 0x04;
1480 case XKB_KEY_Home: return 0x08;
1481 case XKB_KEY_Super_L: return 0x02;
1482 }
1483 return 0x00;
1484}
1485
1486static void
1487kbd_keymap(void *d, struct wl_keyboard *k, uint32_t fmt, int32_t fd, uint32_t size)
1488{
1489 char *map;
1490 if(fmt != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
1491 close(fd);
1492 return;
1493 }
1494 map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1495 if(map != MAP_FAILED) {
1496 xkb_map = xkb_keymap_new_from_string(xkb_ctx, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
1497 munmap(map, size);
1498 if(xkb_state)
1499 xkb_state_unref(xkb_state);
1500 xkb_state = xkb_state_new(xkb_map);
1501 }
1502 close(fd);
1503}
1504
1505static void kbd_enter(void *d, struct wl_keyboard *k, uint32_t serial, struct wl_surface *surf, struct wl_array *keys) { }
1506static void kbd_leave(void *d, struct wl_keyboard *k, uint32_t serial, struct wl_surface *surf) { }
1507
1508static void
1509kbd_key(void *d, struct wl_keyboard *k, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
1510{
1511 xkb_keysym_t sym;
1512 if(!xkb_state)
1513 return;
1514 sym = xkb_state_key_get_one_sym(xkb_state, key + 8);
1515 if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
1516 char buf[8] = {0};
1517 switch(sym) {
1518 case XKB_KEY_F1: screen_zoom = (screen_zoom % 3) + 1, screen_reqsize = screen_reqdraw = 1; break;
1519 case XKB_KEY_F2: emu_deo(0xe, 0x1); break;
1520 case XKB_KEY_F4: console_close(), emu_restart(0); break;
1521 case XKB_KEY_F5: console_close(), emu_restart(1); break;
1522 case XKB_KEY_F6: emu_deo(0xe, 0x2); break;
1523 }
1524 controller_down(get_button(sym));
1525 xkb_state_key_get_utf8(xkb_state, key + 8, buf, sizeof buf);
1526 if(sym == XKB_KEY_Tab)
1527 buf[0] = 0x9;
1528 controller_key(sym < 0x80 ? (Uint8)sym : (Uint8)buf[0]);
1529 } else
1530 controller_up(get_button(sym));
1531}
1532
1533static void
1534kbd_modifiers(void *d, struct wl_keyboard *k, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
1535{
1536 if(xkb_state)
1537 xkb_state_update_mask(xkb_state, depressed, latched, locked, 0, 0, group);
1538}
1539
1540static const struct wl_keyboard_listener kbd_listener = {
1541 .keymap = kbd_keymap,
1542 .enter = kbd_enter,
1543 .leave = kbd_leave,
1544 .key = kbd_key,
1545 .modifiers = kbd_modifiers,
1546};
1547
1548static void
1549ptr_enter(void *d, struct wl_pointer *p, uint32_t serial, struct wl_surface *surf, wl_fixed_t sx, wl_fixed_t sy)
1550{
1551 wl_pointer_set_cursor(p, serial, NULL, 0, 0);
1552 mouse_pos(wl_fixed_to_int(sx) / screen_zoom, wl_fixed_to_int(sy) / screen_zoom);
1553}
1554
1555static void ptr_leave(void *d, struct wl_pointer *p, uint32_t serial, struct wl_surface *surf) { }
1556
1557static void
1558ptr_motion(void *d, struct wl_pointer *p, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
1559{
1560 mouse_pos(wl_fixed_to_int(sx) / screen_zoom, wl_fixed_to_int(sy) / screen_zoom);
1561}
1562
1563static void
1564ptr_button(void *d, struct wl_pointer *p, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
1565{
1566 Uint8 mask = 0;
1567 switch(button) {
1568 case BTN_LEFT: mask = 0x01; break;
1569 case BTN_MIDDLE: mask = 0x02; break;
1570 case BTN_RIGHT: mask = 0x04; break;
1571 }
1572 if(state == WL_POINTER_BUTTON_STATE_PRESSED)
1573 mouse_down(mask);
1574 else
1575 mouse_up(mask);
1576}
1577
1578static void
1579ptr_axis(void *d, struct wl_pointer *p, uint32_t time, uint32_t axis, wl_fixed_t value)
1580{
1581 const int v = wl_fixed_to_int(value) / 10;
1582 if(axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
1583 mouse_scroll(0, v);
1584 else if(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
1585 mouse_scroll(v, 0);
1586}
1587
1588static const struct wl_pointer_listener ptr_listener = {
1589 .enter = ptr_enter,
1590 .leave = ptr_leave,
1591 .motion = ptr_motion,
1592 .button = ptr_button,
1593 .axis = ptr_axis,
1594};
1595
1596static void
1597seat_caps(void *d, struct wl_seat *seat, uint32_t caps)
1598{
1599 if((caps & WL_SEAT_CAPABILITY_POINTER) && !emu_ptr) {
1600 emu_ptr = wl_seat_get_pointer(seat);
1601 wl_pointer_add_listener(emu_ptr, &ptr_listener, NULL);
1602 }
1603 if((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !emu_kbd) {
1604 emu_kbd = wl_seat_get_keyboard(seat);
1605 wl_keyboard_add_listener(emu_kbd, &kbd_listener, NULL);
1606 }
1607}
1608
1609static const struct wl_seat_listener seat_listener = {
1610 .capabilities = seat_caps,
1611};
1612
1613static void wm_ping(void *d, struct xdg_wm_base *wm, uint32_t serial) { xdg_wm_base_pong(wm, serial); }
1614static const struct xdg_wm_base_listener wm_listener = { .ping = wm_ping };
1615
1616static void
1617xs_configure(void *d, struct xdg_surface *xs, uint32_t serial)
1618{
1619 xdg_surface_ack_configure(xs, serial);
1620 emu_configured = 1;
1621 screen_reqdraw = 1;
1622}
1623
1624static const struct xdg_surface_listener xs_listener = {
1625 .configure = xs_configure,
1626};
1627
1628static void xt_configure(void *d, struct xdg_toplevel *xt, int32_t w, int32_t h, struct wl_array *st) { }
1629/* xt_close takes over X11's ClientMessage/WM_DELETE_WINDOW case. */
1630static void xt_close(void *d, struct xdg_toplevel *xt) { dev[0x0f] = 0x80; }
1631
1632static const struct xdg_toplevel_listener xt_listener = {
1633 .configure = xt_configure,
1634 .close = xt_close,
1635};
1636
1637static void
1638ls_configure(void *d, struct zwlr_layer_surface_v1 *ls, uint32_t serial, uint32_t w, uint32_t h)
1639{
1640 zwlr_layer_surface_v1_ack_configure(ls, serial);
1641 emu_configured = 1;
1642 screen_reqdraw = 1;
1643}
1644
1645static void
1646ls_closed(void *d, struct zwlr_layer_surface_v1 *ls) { dev[0x0f] = 0x80; }
1647
1648static const struct zwlr_layer_surface_v1_listener ls_listener = {
1649 .configure = ls_configure,
1650 .closed = ls_closed,
1651};
1652
1653static void
1654reg_global(void *d, struct wl_registry *reg, uint32_t name, const char *iface, uint32_t ver)
1655{
1656 if(!strcmp(iface, "wl_compositor"))
1657 emu_comp = wl_registry_bind(reg, name, &wl_compositor_interface, 4);
1658 else if(!strcmp(iface, "wl_shm"))
1659 emu_shm = wl_registry_bind(reg, name, &wl_shm_interface, 1);
1660 else if(!strcmp(iface, "xdg_wm_base"))
1661 emu_wm = wl_registry_bind(reg, name, &xdg_wm_base_interface, 1);
1662 else if(!strcmp(iface, "zwlr_layer_shell_v1"))
1663 emu_layer_shell = wl_registry_bind(reg, name, &zwlr_layer_shell_v1_interface, 4);
1664 else if(!strcmp(iface, "wl_seat")) {
1665 emu_seat = wl_registry_bind(reg, name, &wl_seat_interface, 1);
1666 wl_seat_add_listener(emu_seat, &seat_listener, NULL);
1667 }
1668}
1669
1670static void
1671reg_global_remove(void *d, struct wl_registry *reg, uint32_t name)
1672{
1673 (void)d;
1674 (void)reg;
1675 (void)name;
1676}
1677
1678static const struct wl_registry_listener reg_listener = {
1679 .global = reg_global,
1680 .global_remove = reg_global_remove,
1681};
1682
1683
1684static int
1685emu_init(void)
1686{
1687 xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1688 emu_dpy = wl_display_connect(NULL);
1689 if(!emu_dpy)
1690 return !fprintf(stderr, "Display: failed\n");
1691 emu_reg = wl_display_get_registry(emu_dpy);
1692 wl_registry_add_listener(emu_reg, ®_listener, NULL);
1693 wl_display_roundtrip(emu_dpy);
1694 if(!emu_comp || !emu_shm || (!emu_wm && !use_layer_shell ))
1695 return !fprintf(stderr, "Wayland: missing globals\n");
1696
1697 emu_surf = wl_compositor_create_surface(emu_comp);
1698 if(use_layer_shell && emu_layer_shell) {
1699 emu_layer_surf = zwlr_layer_shell_v1_get_layer_surface(
1700 emu_layer_shell, emu_surf, NULL,
1701 ZWLR_LAYER_SHELL_V1_LAYER_TOP, "uxn-wl");
1702 zwlr_layer_surface_v1_add_listener(emu_layer_surf, &ls_listener, NULL);
1703 zwlr_layer_surface_v1_set_size(emu_layer_surf, screen_width, screen_height);
1704 zwlr_layer_surface_v1_set_anchor(emu_layer_surf,
1705 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
1706 zwlr_layer_surface_v1_set_margin(emu_layer_surf, layer_shell_y, 0, 0, layer_shell_x);
1707 zwlr_layer_surface_v1_set_keyboard_interactivity(emu_layer_surf, 2);
1708 wl_surface_commit(emu_surf);
1709 } else if(emu_wm) {
1710 xdg_wm_base_add_listener(emu_wm, &wm_listener, NULL);
1711 emu_xdgsurf = xdg_wm_base_get_xdg_surface(emu_wm, emu_surf);
1712 xdg_surface_add_listener(emu_xdgsurf, &xs_listener, NULL);
1713 emu_top = xdg_surface_get_toplevel(emu_xdgsurf);
1714 xdg_toplevel_add_listener(emu_top, &xt_listener, NULL);
1715 xdg_toplevel_set_title(emu_top, "uxn-wl");
1716 xdg_toplevel_set_app_id(emu_top, "uxn-wl");
1717 wl_surface_commit(emu_surf);
1718 }
1719 wl_display_roundtrip(emu_dpy);
1720 return 1;
1721}
1722
1723static void
1724emu_run(void)
1725{
1726 int has_input, stdin_active = 1;
1727 char coninp[CONINBUFSIZE];
1728 struct pollfd fds[2];
1729 struct timespec next_refresh;
1730 fds[0].fd = wl_display_get_fd(emu_dpy), fds[0].events = POLLIN;
1731 fds[1].fd = STDIN_FILENO, fds[1].events = POLLIN | POLLHUP;
1732 clock_gettime(CLOCK_MONOTONIC, &next_refresh);
1733 while(!dev[0x0f]) {
1734 wl_display_dispatch_pending(emu_dpy);
1735 wl_display_flush(emu_dpy);
1736 if(wl_display_dispatch_pending(emu_dpy) < 0 || wl_display_get_error(emu_dpy)) {
1737 dev[0x0f] = 0x80;
1738 break;
1739 }
1740 if(wl_display_flush(emu_dpy) < 0 && errno != EAGAIN) {
1741 dev[0x0f] = 0x80;
1742 break;
1743 }
1744 struct timespec now;
1745 clock_gettime(CLOCK_MONOTONIC, &now);
1746 long long now_ms = (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
1747 long long next_ms = (long long)next_refresh.tv_sec * 1000 + next_refresh.tv_nsec / 1000000;
1748 if(now_ms >= next_ms) {
1749 audio_output();
1750 screen_update();
1751 emu_redraw();
1752 next_ms += 16;
1753 if(now_ms > next_ms + 100)
1754 next_ms = now_ms +16;
1755 next_refresh.tv_sec = next_ms / 1000;
1756 next_refresh.tv_nsec = (next_ms % 1000) * 1000000;
1757 }
1758 clock_gettime(CLOCK_MONOTONIC, &now);
1759 now_ms = (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
1760 int timeout = next_ms - now_ms;
1761 if(timeout < 0)
1762 timeout = 0;
1763 /* only pull stdin while it's still active */
1764 if(poll(fds, stdin_active ? 2 : 1, timeout) <= 0) {
1765 if(wl_display_get_error(emu_dpy)) {
1766 dev[0x0f] = 0x80;
1767 break;
1768 }
1769 continue;
1770 }
1771 if(fds[0].revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL)) {
1772 if(wl_display_dispatch(emu_dpy) < 0 || wl_display_get_error(emu_dpy)) {
1773 dev[0x0f] = 0x80;
1774 break;
1775 }
1776 }
1777 has_input = stdin_active && (fds[1].revents & POLLIN);
1778 if(has_input || (stdin_active && (fds[1].revents & POLLHUP))) {
1779 int i, n = read(STDIN_FILENO, coninp, CONINBUFSIZE - 1);
1780 if(n > 0) {
1781 for(i = 0; i < n; i++)
1782 console_input(coninp[i], CONSOLE_STD);
1783 } else {
1784 console_input('\n', CONSOLE_END);
1785 stdin_active = 0;
1786 }
1787 }
1788 datetime_busy = 0;
1789 }
1790 if(emu_bufs[0])
1791 wl_buffer_destroy(emu_bufs[0]);
1792 if(emu_bufs[1])
1793 wl_buffer_destroy(emu_bufs[1]);
1794 if(emu_top)
1795 xdg_toplevel_destroy(emu_top);
1796 if(emu_xdgsurf)
1797 xdg_surface_destroy(emu_xdgsurf);
1798 if(emu_layer_surf)
1799 zwlr_layer_surface_v1_destroy(emu_layer_surf);
1800 if(emu_surf)
1801 wl_surface_destroy(emu_surf);
1802 if(audio_initialized)
1803 ma_device_uninit(&audio_device);
1804 if(emu_dpy)
1805 wl_display_disconnect(emu_dpy);
1806 console_close();
1807}
1808
1809int
1810main(int argc, char **argv)
1811{
1812 int i = 1;
1813 if(argc == 1)
1814 return !fprintf(stdout, "usage: %s [-v] [-t path/to/.theme] [-l x y] file.rom [args..]\n", argv[0]);
1815 while(i < argc && argv[i][0] == '-') {
1816 if(!strcmp(argv[i], "-t")) {
1817 if(i + 1 >= argc)
1818 return !fprintf(stdout, "usage: %s [-v] [-t theme] [-l x y] file.rom [args..]\n", argv[0]);
1819 theme_file = argv[++i];
1820 i++;
1821 continue;
1822 }
1823 if(!strcmp(argv[i], "-v"))
1824 return !fprintf(stdout, "%s - Varvara Emulator(79K), 10 Jul 2026.\n", argv[0]);
1825 if(!strcmp(argv[i], "--")){ i++; break; }
1826 if(!strcmp(argv[i], "-l")) {
1827 if(i + 3 >= argc)
1828 return !fprintf(stdout, "usage: %s [-l x y] file.rom [args..]\n", argv[0]);
1829 use_layer_shell = 1;
1830 layer_shell_x = atoi(argv[++i]);
1831 layer_shell_y = atoi(argv[++i]);
1832 i++;
1833 continue;
1834 }
1835 return !fprintf(stdout, "unknown flag: %s\n", argv[i]);
1836 }
1837 if(i >= argc)
1838 return !fprintf(stdout, "usage: %s [-v] file.rom [args..]\n", argv[0]);
1839 if(!system_boot(argv[i], argc > i + 1))
1840 return !fprintf(stdout, "Could not load %s.\n", argv[i]);
1841 i++;
1842 screen_resize(WIDTH, HEIGHT);
1843 if(uxn_eval(0x100) && console_vector) {
1844 for(; i < argc; i++) {
1845 char *p = argv[i];
1846 while(*p)
1847 console_input(*p++, CONSOLE_ARG);
1848 console_input('\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA);
1849 }
1850 }
1851 if(!dev[0x0f]) {
1852 if(!emu_init())
1853 return !fprintf(stdout, "Could not initialize %s.\n", argv[0]);
1854 screen_update();
1855 emu_redraw();
1856 audio_init();
1857 emu_run();
1858 }
1859 return dev[0x0f] & 0x7f;
1860}