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