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
1419static void frame_callback(void *, struct wl_callback *, uint32_t);
1420
1421static const struct wl_callback_listener frame_listener = {
1422 .done = frame_callback,
1423};
1424
1425void
1426emu_redraw(void)
1427{
1428 if(!emu_configured)
1429 return;
1430 wl_callback_add_listener(wl_surface_frame(emu_surf), &frame_listener, NULL);
1431 wl_surface_attach(emu_surf, emu_bufs[active_buf], 0, 0);
1432 wl_surface_damage_buffer(emu_surf, 0, 0, screen_width * screen_zoom, screen_height * screen_zoom);
1433 wl_surface_commit(emu_surf);
1434}
1435
1436static void
1437frame_callback(void *data, struct wl_callback *cb, uint32_t time)
1438{
1439 wl_callback_destroy(cb);
1440 int old = active_buf;
1441 active_buf ^= 1;
1442 memcpy((Uint8 *)shm_data + active_buf * shm_bufsize,
1443 (Uint8 *)shm_data + old * shm_bufsize, shm_bufsize);
1444 screen_pixels = (int *)((Uint8 *)shm_data + active_buf * shm_bufsize);
1445}
1446
1447static void
1448emu_restart(unsigned int soft)
1449{
1450 system_reboot(soft);
1451 screen_width = screen_height = 0;
1452 screen_resize(WIDTH, HEIGHT);
1453 uxn_eval(0x100);
1454}
1455
1456static void
1457emu_screenshot(void)
1458{
1459 FILE *f;
1460 char filename[] = "_00x00.chr";
1461 unsigned int length = 4;
1462 int tx, ty, row, col, tw = screen_width / 8, th = screen_height / 8;
1463 put_size((Uint8 *)filename + 1, &length, 2, tw);
1464 put_size((Uint8 *)filename + 4, &length, 2, th);
1465 f = fopen(filename, "wb");
1466 if(f) {
1467 for(ty = 0; ty < th; ty++) {
1468 for(tx = 0; tx < tw; tx++) {
1469 Uint8 tile[16] = {0};
1470 for(row = 0; row < 8; row++) {
1471 const Uint8 *src = &screen_layers[(ty * 8 + row + 8) * screen_wmar2 + (tx * 8 + 8)];
1472 Uint8 b1 = 0, b2 = 0;
1473 for(col = 0; col < 8; col++) {
1474 Uint8 px = src[col];
1475 b1 |= ((px & 0x1) << (7 - col)), b2 |= (((px >> 2) & 0x1) << (7 - col));
1476 }
1477 tile[row] = b1, tile[row + 8] = b2;
1478 }
1479 fwrite(tile, 1, 16, f);
1480 }
1481 }
1482 fclose(f);
1483 }
1484}
1485
1486static Uint8
1487get_button(xkb_keysym_t sym)
1488{
1489 switch(sym) {
1490 case XKB_KEY_Up: return 0x10;
1491 case XKB_KEY_Down: return 0x20;
1492 case XKB_KEY_Left: return 0x40;
1493 case XKB_KEY_Right: return 0x80;
1494 case XKB_KEY_Control_L: return 0x01;
1495 case XKB_KEY_Alt_R:
1496 case XKB_KEY_Alt_L: return 0x02;
1497 case XKB_KEY_Shift_L: return 0x04;
1498 case XKB_KEY_Home: return 0x08;
1499 case XKB_KEY_Super_L: return 0x02;
1500 }
1501 return 0x00;
1502}
1503
1504static void
1505kbd_keymap(void *d, struct wl_keyboard *k, uint32_t fmt, int32_t fd, uint32_t size)
1506{
1507 char *map;
1508 if(fmt != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
1509 close(fd);
1510 return;
1511 }
1512 map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1513 if(map != MAP_FAILED) {
1514 xkb_map = xkb_keymap_new_from_string(xkb_ctx, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
1515 munmap(map, size);
1516 if(xkb_state)
1517 xkb_state_unref(xkb_state);
1518 xkb_state = xkb_state_new(xkb_map);
1519 }
1520 close(fd);
1521}
1522
1523static void kbd_enter(void *d, struct wl_keyboard *k, uint32_t serial, struct wl_surface *surf, struct wl_array *keys) { }
1524static void kbd_leave(void *d, struct wl_keyboard *k, uint32_t serial, struct wl_surface *surf) { }
1525
1526static void
1527kbd_key(void *d, struct wl_keyboard *k, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
1528{
1529 xkb_keysym_t sym;
1530 if(!xkb_state)
1531 return;
1532 sym = xkb_state_key_get_one_sym(xkb_state, key + 8);
1533 if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
1534 char buf[8] = {0};
1535 switch(sym) {
1536 case XKB_KEY_F1: screen_zoom = (screen_zoom % 3) + 1, screen_reqsize = screen_reqdraw = 1; break;
1537 case XKB_KEY_F2: emu_deo(0xe, 0x1); break;
1538 case XKB_KEY_F4: console_close(), emu_restart(0); break;
1539 case XKB_KEY_F5: console_close(), emu_restart(1); break;
1540 case XKB_KEY_F6: emu_deo(0xe, 0x2); break;
1541 }
1542 controller_down(get_button(sym));
1543 xkb_state_key_get_utf8(xkb_state, key + 8, buf, sizeof buf);
1544 if(sym == XKB_KEY_Tab)
1545 buf[0] = 0x9;
1546 controller_key(sym < 0x80 ? (Uint8)sym : (Uint8)buf[0]);
1547 } else
1548 controller_up(get_button(sym));
1549}
1550
1551static void
1552kbd_modifiers(void *d, struct wl_keyboard *k, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
1553{
1554 if(xkb_state)
1555 xkb_state_update_mask(xkb_state, depressed, latched, locked, 0, 0, group);
1556}
1557
1558static const struct wl_keyboard_listener kbd_listener = {
1559 .keymap = kbd_keymap,
1560 .enter = kbd_enter,
1561 .leave = kbd_leave,
1562 .key = kbd_key,
1563 .modifiers = kbd_modifiers,
1564};
1565
1566static void
1567ptr_enter(void *d, struct wl_pointer *p, uint32_t serial, struct wl_surface *surf, wl_fixed_t sx, wl_fixed_t sy)
1568{
1569 wl_pointer_set_cursor(p, serial, NULL, 0, 0);
1570 mouse_pos(wl_fixed_to_int(sx) / screen_zoom, wl_fixed_to_int(sy) / screen_zoom);
1571}
1572
1573static void ptr_leave(void *d, struct wl_pointer *p, uint32_t serial, struct wl_surface *surf) { }
1574
1575static void
1576ptr_motion(void *d, struct wl_pointer *p, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
1577{
1578 mouse_pos(wl_fixed_to_int(sx) / screen_zoom, wl_fixed_to_int(sy) / screen_zoom);
1579}
1580
1581static void
1582ptr_button(void *d, struct wl_pointer *p, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
1583{
1584 Uint8 mask = 0;
1585 switch(button) {
1586 case BTN_LEFT: mask = 0x01; break;
1587 case BTN_MIDDLE: mask = 0x02; break;
1588 case BTN_RIGHT: mask = 0x04; break;
1589 }
1590 if(state == WL_POINTER_BUTTON_STATE_PRESSED)
1591 mouse_down(mask);
1592 else
1593 mouse_up(mask);
1594}
1595
1596static void
1597ptr_axis(void *d, struct wl_pointer *p, uint32_t time, uint32_t axis, wl_fixed_t value)
1598{
1599 const int v = wl_fixed_to_int(value) / 10;
1600 if(axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
1601 mouse_scroll(0, v);
1602 else if(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
1603 mouse_scroll(v, 0);
1604}
1605
1606static const struct wl_pointer_listener ptr_listener = {
1607 .enter = ptr_enter,
1608 .leave = ptr_leave,
1609 .motion = ptr_motion,
1610 .button = ptr_button,
1611 .axis = ptr_axis,
1612};
1613
1614static void
1615seat_caps(void *d, struct wl_seat *seat, uint32_t caps)
1616{
1617 if((caps & WL_SEAT_CAPABILITY_POINTER) && !emu_ptr) {
1618 emu_ptr = wl_seat_get_pointer(seat);
1619 wl_pointer_add_listener(emu_ptr, &ptr_listener, NULL);
1620 }
1621 if((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !emu_kbd) {
1622 emu_kbd = wl_seat_get_keyboard(seat);
1623 wl_keyboard_add_listener(emu_kbd, &kbd_listener, NULL);
1624 }
1625}
1626
1627static const struct wl_seat_listener seat_listener = {
1628 .capabilities = seat_caps,
1629};
1630
1631static void wm_ping(void *d, struct xdg_wm_base *wm, uint32_t serial) { xdg_wm_base_pong(wm, serial); }
1632static const struct xdg_wm_base_listener wm_listener = { .ping = wm_ping };
1633
1634static void
1635xs_configure(void *d, struct xdg_surface *xs, uint32_t serial)
1636{
1637 xdg_surface_ack_configure(xs, serial);
1638 emu_configured = 1;
1639 screen_reqdraw = 1;
1640}
1641
1642static const struct xdg_surface_listener xs_listener = {
1643 .configure = xs_configure,
1644};
1645
1646static void xt_configure(void *d, struct xdg_toplevel *xt, int32_t w, int32_t h, struct wl_array *st) { }
1647/* xt_close takes over X11's ClientMessage/WM_DELETE_WINDOW case. */
1648static void xt_close(void *d, struct xdg_toplevel *xt) { dev[0x0f] = 0x80; }
1649
1650static const struct xdg_toplevel_listener xt_listener = {
1651 .configure = xt_configure,
1652 .close = xt_close,
1653};
1654
1655static void
1656ls_configure(void *d, struct zwlr_layer_surface_v1 *ls, uint32_t serial, uint32_t w, uint32_t h)
1657{
1658 zwlr_layer_surface_v1_ack_configure(ls, serial);
1659 emu_configured = 1;
1660 screen_reqdraw = 1;
1661}
1662
1663static void
1664ls_closed(void *d, struct zwlr_layer_surface_v1 *ls) { dev[0x0f] = 0x80; }
1665
1666static const struct zwlr_layer_surface_v1_listener ls_listener = {
1667 .configure = ls_configure,
1668 .closed = ls_closed,
1669};
1670
1671static void
1672reg_global(void *d, struct wl_registry *reg, uint32_t name, const char *iface, uint32_t ver)
1673{
1674 if(!strcmp(iface, "wl_compositor"))
1675 emu_comp = wl_registry_bind(reg, name, &wl_compositor_interface, 4);
1676 else if(!strcmp(iface, "wl_shm"))
1677 emu_shm = wl_registry_bind(reg, name, &wl_shm_interface, 1);
1678 else if(!strcmp(iface, "xdg_wm_base"))
1679 emu_wm = wl_registry_bind(reg, name, &xdg_wm_base_interface, 1);
1680 else if(!strcmp(iface, "zwlr_layer_shell_v1"))
1681 emu_layer_shell = wl_registry_bind(reg, name, &zwlr_layer_shell_v1_interface, 4);
1682 else if(!strcmp(iface, "wl_seat")) {
1683 emu_seat = wl_registry_bind(reg, name, &wl_seat_interface, 1);
1684 wl_seat_add_listener(emu_seat, &seat_listener, NULL);
1685 }
1686}
1687
1688static const struct wl_registry_listener reg_listener = {
1689 .global = reg_global,
1690};
1691
1692
1693static int
1694emu_init(void)
1695{
1696 xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1697 emu_dpy = wl_display_connect(NULL);
1698 if(!emu_dpy)
1699 return !fprintf(stderr, "Display: failed\n");
1700 emu_reg = wl_display_get_registry(emu_dpy);
1701 wl_registry_add_listener(emu_reg, ®_listener, NULL);
1702 wl_display_roundtrip(emu_dpy);
1703 if(!emu_comp || !emu_shm || (!emu_wm && !use_layer_shell ))
1704 return !fprintf(stderr, "Wayland: missing globals\n");
1705
1706 emu_surf = wl_compositor_create_surface(emu_comp);
1707 if(use_layer_shell && emu_layer_shell) {
1708 emu_layer_surf = zwlr_layer_shell_v1_get_layer_surface(
1709 emu_layer_shell, emu_surf, NULL,
1710 ZWLR_LAYER_SHELL_V1_LAYER_TOP, "uxn-wl");
1711 zwlr_layer_surface_v1_add_listener(emu_layer_surf, &ls_listener, NULL);
1712 zwlr_layer_surface_v1_set_size(emu_layer_surf, screen_width, screen_height);
1713 zwlr_layer_surface_v1_set_anchor(emu_layer_surf,
1714 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
1715 zwlr_layer_surface_v1_set_margin(emu_layer_surf, layer_shell_y, 0, 0, layer_shell_x);
1716 zwlr_layer_surface_v1_set_keyboard_interactivity(emu_layer_surf, 2);
1717 wl_surface_commit(emu_surf);
1718 } else if(emu_wm) {
1719 xdg_wm_base_add_listener(emu_wm, &wm_listener, NULL);
1720 emu_xdgsurf = xdg_wm_base_get_xdg_surface(emu_wm, emu_surf);
1721 xdg_surface_add_listener(emu_xdgsurf, &xs_listener, NULL);
1722 emu_top = xdg_surface_get_toplevel(emu_xdgsurf);
1723 xdg_toplevel_add_listener(emu_top, &xt_listener, NULL);
1724 xdg_toplevel_set_title(emu_top, "uxn-wl");
1725 xdg_toplevel_set_app_id(emu_top, "uxn-wl");
1726 wl_surface_commit(emu_surf);
1727 }
1728 wl_display_roundtrip(emu_dpy);
1729 return 1;
1730}
1731
1732static void
1733emu_run(void)
1734{
1735 int has_input, stdin_active = 1;
1736 char coninp[CONINBUFSIZE];
1737 struct pollfd fds[2];
1738 struct timespec next_refresh;
1739 fds[0].fd = wl_display_get_fd(emu_dpy), fds[0].events = POLLIN;
1740 fds[1].fd = STDIN_FILENO, fds[1].events = POLLIN | POLLHUP;
1741 clock_gettime(CLOCK_MONOTONIC, &next_refresh);
1742 while(!dev[0x0f]) {
1743 wl_display_dispatch_pending(emu_dpy);
1744 wl_display_flush(emu_dpy);
1745 struct timespec now;
1746 clock_gettime(CLOCK_MONOTONIC, &now);
1747 long long now_ms = (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
1748 long long next_ms = (long long)next_refresh.tv_sec * 1000 + next_refresh.tv_nsec / 1000000;
1749 if(now_ms >= next_ms) {
1750 audio_output();
1751 screen_update();
1752 emu_redraw();
1753 next_ms += 16;
1754 if(now_ms > next_ms + 100)
1755 next_ms = now_ms +16;
1756 next_refresh.tv_sec = next_ms / 1000;
1757 next_refresh.tv_nsec = (next_ms % 1000) * 1000000;
1758 }
1759 clock_gettime(CLOCK_MONOTONIC, &now);
1760 now_ms = (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
1761 int timeout = next_ms - now_ms;
1762 if(timeout < 0)
1763 timeout = 0;
1764 /* only pull stdin while it's still active */
1765 if(poll(fds, stdin_active ? 2 : 1, timeout) <= 0)
1766 continue;
1767 if(fds[0].revents & POLLIN)
1768 wl_display_dispatch(emu_dpy);
1769 has_input = stdin_active && (fds[1].revents & POLLIN);
1770 if(has_input || (stdin_active && (fds[1].revents & POLLHUP))) {
1771 int i, n = read(STDIN_FILENO, coninp, CONINBUFSIZE - 1);
1772 if(n > 0) {
1773 for(i = 0; i < n; i++)
1774 console_input(coninp[i], CONSOLE_STD);
1775 } else {
1776 console_input('\n', CONSOLE_END);
1777 stdin_active = 0;
1778 }
1779 }
1780 datetime_busy = 0;
1781 }
1782 if(emu_bufs[0])
1783 wl_buffer_destroy(emu_bufs[0]);
1784 if(emu_bufs[1])
1785 wl_buffer_destroy(emu_bufs[1]);
1786 if(emu_top)
1787 xdg_toplevel_destroy(emu_top);
1788 if(emu_xdgsurf)
1789 xdg_surface_destroy(emu_xdgsurf);
1790 if(emu_layer_surf)
1791 zwlr_layer_surface_v1_destroy(emu_layer_surf);
1792 if(emu_surf)
1793 wl_surface_destroy(emu_surf);
1794 if(audio_initialized)
1795 ma_device_uninit(&audio_device);
1796 if(emu_dpy)
1797 wl_display_disconnect(emu_dpy);
1798 console_close();
1799}
1800
1801int
1802main(int argc, char **argv)
1803{
1804 int i = 1;
1805 if(argc == 1)
1806 return !fprintf(stdout, "usage: %s [-v] [-t path/to/.theme] [-l x y] file.rom [args..]\n", argv[0]);
1807 while(i < argc && argv[i][0] == '-') {
1808 if(!strcmp(argv[i], "-t")) {
1809 if(i + 1 >= argc)
1810 return !fprintf(stdout, "usage: %s [-v] [-t theme] [-l x y] file.rom [args..]\n", argv[0]);
1811 theme_file = argv[++i];
1812 i++;
1813 continue;
1814 }
1815 if(!strcmp(argv[i], "-v"))
1816 return !fprintf(stdout, "%s - Varvara Emulator(79K), 10 Jul 2026.\n", argv[0]);
1817 if(!strcmp(argv[i], "--")){ i++; break; }
1818 if(!strcmp(argv[i], "-l")) {
1819 if(i + 3 >= argc)
1820 return !fprintf(stdout, "usage: %s [-l x y] file.rom [args..]\n", argv[0]);
1821 use_layer_shell = 1;
1822 layer_shell_x = atoi(argv[++i]);
1823 layer_shell_y = atoi(argv[++i]);
1824 i++;
1825 continue;
1826 }
1827 return !fprintf(stdout, "unknown flag: %s\n", argv[i]);
1828 }
1829 if(i >= argc)
1830 return !fprintf(stdout, "usage: %s [-v] file.rom [args..]\n", argv[0]);
1831 if(!system_boot(argv[i], argc > i + 1))
1832 return !fprintf(stdout, "Could not load %s.\n", argv[i]);
1833 i++;
1834 screen_resize(WIDTH, HEIGHT);
1835 if(uxn_eval(0x100) && console_vector) {
1836 for(; i < argc; i++) {
1837 char *p = argv[i];
1838 while(*p)
1839 console_input(*p++, CONSOLE_ARG);
1840 console_input('\n', i == argc - 1 ? CONSOLE_END : CONSOLE_EOA);
1841 }
1842 }
1843 if(!dev[0x0f]) {
1844 if(!emu_init())
1845 return !fprintf(stdout, "Could not initialize %s.\n", argv[0]);
1846 screen_update();
1847 emu_redraw();
1848 audio_init();
1849 emu_run();
1850 }
1851 return dev[0x0f] & 0x7f;
1852}