main pita/uxn12 / src / uxn12.c.orig
   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, &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}