shithub: psxe

Download patch

ref: b958a4826fe639704e81351a844bb947c799839e
parent: c4e01fe1def3f7f124b478cdbabad3805c1da6d0
author: Jean-André Santoni <jean.andre.santoni@gmail.com>
date: Sat Mar 7 18:53:19 EST 2026

Quick and dirty port

--- a/frontend/argparse.h
+++ b/frontend/argparse.h
@@ -13,7 +13,7 @@
 extern "C" {
 #endif
 
-#include <stdint.h>
+#include "p9.h"
 
 struct argparse;
 struct argparse_option;
--- a/frontend/toml.c
+++ b/frontend/toml.c
@@ -29,8 +29,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
-#include <stdbool.h>
-#include <stdint.h>
+#include "p9.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
--- a/frontend/toml.h
+++ b/frontend/toml.h
@@ -29,7 +29,7 @@
 #pragma warning(disable: 4996)
 #endif
 
-#include <stdint.h>
+#include "p9.h"
 #include <stdio.h>
 
 #ifdef __cplusplus
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,89 @@
+</$objtype/mkfile
+
+# Core-only build for 9front/native ports.
+# Excludes frontend/ (SDL UI/audio loop) and builds reusable emulator core.
+LIB=libpsxe_core.a
+CFLAGS=$CFLAGS -I. -Ipsx
+
+HFILES=\
+	psx/bus.h\
+	psx/bus_init.h\
+	psx/config.h\
+	psx/cpu.h\
+	psx/cpu_debug.h\
+	psx/exe.h\
+	psx/log.h\
+	psx/psx.h\
+	psx/input/guncon.h\
+	psx/input/sda.h\
+	psx/dev/bios.h\
+	psx/dev/dma.h\
+	psx/dev/exp1.h\
+	psx/dev/exp2.h\
+	psx/dev/gpu.h\
+	psx/dev/ic.h\
+	psx/dev/input.h\
+	psx/dev/mc1.h\
+	psx/dev/mc2.h\
+	psx/dev/mc3.h\
+	psx/dev/mcd.h\
+	psx/dev/mdec.h\
+	psx/dev/pad.h\
+	psx/dev/ram.h\
+	psx/dev/scratchpad.h\
+	psx/dev/spu.h\
+	psx/dev/timer.h\
+	psx/dev/xa.h\
+	psx/dev/cdrom/cdrom.h\
+	psx/dev/cdrom/cue.h\
+	psx/dev/cdrom/disc.h\
+	psx/dev/cdrom/list.h\
+	psx/dev/cdrom/queue.h\
+
+OFILES=\
+	bus.$O\
+	config.$O\
+	cpu.$O\
+	exe.$O\
+	log.$O\
+	psx.$O\
+	guncon.$O\
+	sda.$O\
+	bios.$O\
+	dma.$O\
+	exp1.$O\
+	exp2.$O\
+	gpu.$O\
+	ic.$O\
+	input.$O\
+	mc1.$O\
+	mc2.$O\
+	mc3.$O\
+	mcd.$O\
+	mdec.$O\
+	pad.$O\
+	ram.$O\
+	scratchpad.$O\
+	spu.$O\
+	timer.$O\
+	xa.$O\
+	audio.$O\
+	cdrom.$O\
+	cue.$O\
+	disc.$O\
+	impl.$O\
+	list.$O\
+	queue.$O\
+
+default:V: $LIB
+
+%.$O: psx/%.c
+	$CC $CFLAGS -o $target $prereq
+%.$O: psx/input/%.c
+	$CC $CFLAGS -o $target $prereq
+%.$O: psx/dev/%.c
+	$CC $CFLAGS -o $target $prereq
+%.$O: psx/dev/cdrom/%.c
+	$CC $CFLAGS -o $target $prereq
+
+</sys/src/cmd/mksyslib
--- a/psx/bus.c
+++ b/psx/bus.c
@@ -1,10 +1,69 @@
-#include <stdint.h>
-#include <stdlib.h>
+#include "p9.h"
+
+#include "bus.h"
+#include "bus_init.h"
+#include "dev/cdrom/cdrom.h"
+#include "dev/bios.h"
+#include "dev/ram.h"
+#include "dev/dma.h"
+#include "dev/exp1.h"
+#include "dev/exp2.h"
+#include "dev/mc1.h"
+#include "dev/mc2.h"
+#include "dev/mc3.h"
+#include "dev/ic.h"
+#include "dev/scratchpad.h"
+#include "dev/gpu.h"
+#include "log.h"
+
+/*
+ * 6c struggles with the full SPU header in this TU; bus only needs
+ * the MMIO prefix fields and read/write entry points.
+ */
+struct psx_spu_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+};
+uint32_t psx_spu_read32(struct psx_spu_t*, uint32_t);
+uint16_t psx_spu_read16(struct psx_spu_t*, uint32_t);
+uint8_t psx_spu_read8(struct psx_spu_t*, uint32_t);
+void psx_spu_write32(struct psx_spu_t*, uint32_t, uint32_t);
+void psx_spu_write16(struct psx_spu_t*, uint32_t, uint16_t);
+void psx_spu_write8(struct psx_spu_t*, uint32_t, uint8_t);
+
+struct psx_timer_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+};
+uint32_t psx_timer_read32(struct psx_timer_t*, uint32_t);
+uint16_t psx_timer_read16(struct psx_timer_t*, uint32_t);
+uint8_t psx_timer_read8(struct psx_timer_t*, uint32_t);
+void psx_timer_write32(struct psx_timer_t*, uint32_t, uint32_t);
+void psx_timer_write16(struct psx_timer_t*, uint32_t, uint16_t);
+void psx_timer_write8(struct psx_timer_t*, uint32_t, uint8_t);
+
+struct psx_pad_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+};
+uint32_t psx_pad_read32(struct psx_pad_t*, uint32_t);
+uint16_t psx_pad_read16(struct psx_pad_t*, uint32_t);
+uint8_t psx_pad_read8(struct psx_pad_t*, uint32_t);
+void psx_pad_write32(struct psx_pad_t*, uint32_t, uint32_t);
+void psx_pad_write16(struct psx_pad_t*, uint32_t, uint16_t);
+void psx_pad_write8(struct psx_pad_t*, uint32_t, uint8_t);
+
+struct psx_mdec_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+};
+uint32_t psx_mdec_read32(struct psx_mdec_t*, uint32_t);
+uint16_t psx_mdec_read16(struct psx_mdec_t*, uint32_t);
+uint8_t psx_mdec_read8(struct psx_mdec_t*, uint32_t);
+void psx_mdec_write32(struct psx_mdec_t*, uint32_t, uint32_t);
+void psx_mdec_write16(struct psx_mdec_t*, uint32_t, uint16_t);
+void psx_mdec_write8(struct psx_mdec_t*, uint32_t, uint8_t);
 
-#include "bus.h"
-#include "bus_init.h"
-#include "log.h"
-
 #define RANGE(v, s, e) ((v >= s) && (v < e))
 
 const uint32_t g_psx_bus_region_mask_table[] = {
@@ -16,24 +75,26 @@
     return (psx_bus_t*)malloc(sizeof(psx_bus_t));
 }
 
-// Does nothing for now
-void psx_bus_init(psx_bus_t* bus) {}
+// Does nothing for now
+void psx_bus_init(psx_bus_t* bus) {
+    USED(bus);
+}
 
 void psx_bus_destroy(psx_bus_t* bus) {
     free(bus);
 }
 
-#define HANDLE_READ(dev, bits) \
-    if (RANGE(addr, bus->dev->io_base, (bus->dev->io_base + bus->dev->io_size))) { \
-        bus->access_cycles = bus->dev->bus_delay; \
-        return psx_ ## dev ## _read ## bits (bus->dev, addr - bus->dev->io_base); \
-    }
-#define HANDLE_WRITE(dev, bits) \
-    if (RANGE(addr, bus->dev->io_base, (bus->dev->io_base + bus->dev->io_size))) { \
-        bus->access_cycles = bus->dev->bus_delay; \
-        psx_ ## dev ## _write ## bits (bus->dev, addr - bus->dev->io_base, value); \
-        return; \
-    }
+#define HANDLE_READ_OP(dev, fn) \
+    if (RANGE(addr, bus->dev->io_base, (bus->dev->io_base + bus->dev->io_size))) { \
+        bus->access_cycles = bus->dev->bus_delay; \
+        return fn(bus->dev, addr - bus->dev->io_base); \
+    }
+#define HANDLE_WRITE_OP(dev, fn) \
+    if (RANGE(addr, bus->dev->io_base, (bus->dev->io_base + bus->dev->io_size))) { \
+        bus->access_cycles = bus->dev->bus_delay; \
+        fn(bus->dev, addr - bus->dev->io_base, value); \
+        return; \
+    }
 
 uint32_t psx_bus_read32(psx_bus_t* bus, uint32_t addr) {
     uint32_t vaddr = addr;
@@ -44,22 +105,22 @@
         log_fatal("Unaligned 32-bit read from %08x:%08x", vaddr, addr);
     }
 
-    HANDLE_READ(bios, 32);
-    HANDLE_READ(ram, 32);
-    HANDLE_READ(dma, 32);
-    HANDLE_READ(exp1, 32);
-    HANDLE_READ(exp2, 32);
-    HANDLE_READ(mc1, 32);
-    HANDLE_READ(mc2, 32);
-    HANDLE_READ(mc3, 32);
-    HANDLE_READ(ic, 32);
-    HANDLE_READ(scratchpad, 32);
-    HANDLE_READ(gpu, 32);
-    HANDLE_READ(spu, 32);
-    HANDLE_READ(timer, 32);
-    HANDLE_READ(cdrom, 32);
-    HANDLE_READ(pad, 32);
-    HANDLE_READ(mdec, 32);
+    HANDLE_READ_OP(bios, psx_bios_read32);
+    HANDLE_READ_OP(ram, psx_ram_read32);
+    HANDLE_READ_OP(dma, psx_dma_read32);
+    HANDLE_READ_OP(exp1, psx_exp1_read32);
+    HANDLE_READ_OP(exp2, psx_exp2_read32);
+    HANDLE_READ_OP(mc1, psx_mc1_read32);
+    HANDLE_READ_OP(mc2, psx_mc2_read32);
+    HANDLE_READ_OP(mc3, psx_mc3_read32);
+    HANDLE_READ_OP(ic, psx_ic_read32);
+    HANDLE_READ_OP(scratchpad, psx_scratchpad_read32);
+    HANDLE_READ_OP(gpu, psx_gpu_read32);
+    HANDLE_READ_OP(spu, psx_spu_read32);
+    HANDLE_READ_OP(timer, psx_timer_read32);
+    HANDLE_READ_OP(cdrom, psx_cdrom_read32);
+    HANDLE_READ_OP(pad, psx_pad_read32);
+    HANDLE_READ_OP(mdec, psx_mdec_read32);
 
     log_fatal("Unhandled 32-bit read from %08x:%08x", vaddr, addr);
 
@@ -81,22 +142,22 @@
         log_fatal("Unaligned 16-bit read from %08x:%08x", vaddr, addr);
     }
 
-    HANDLE_READ(bios, 16);
-    HANDLE_READ(ram, 16);
-    HANDLE_READ(dma, 16);
-    HANDLE_READ(exp1, 16);
-    HANDLE_READ(exp2, 16);
-    HANDLE_READ(mc1, 16);
-    HANDLE_READ(mc2, 16);
-    HANDLE_READ(mc3, 16);
-    HANDLE_READ(ic, 16);
-    HANDLE_READ(scratchpad, 16);
-    HANDLE_READ(gpu, 16);
-    HANDLE_READ(spu, 16);
-    HANDLE_READ(timer, 16);
-    HANDLE_READ(cdrom, 16);
-    HANDLE_READ(pad, 16);
-    HANDLE_READ(mdec, 16);
+    HANDLE_READ_OP(bios, psx_bios_read16);
+    HANDLE_READ_OP(ram, psx_ram_read16);
+    HANDLE_READ_OP(dma, psx_dma_read16);
+    HANDLE_READ_OP(exp1, psx_exp1_read16);
+    HANDLE_READ_OP(exp2, psx_exp2_read16);
+    HANDLE_READ_OP(mc1, psx_mc1_read16);
+    HANDLE_READ_OP(mc2, psx_mc2_read16);
+    HANDLE_READ_OP(mc3, psx_mc3_read16);
+    HANDLE_READ_OP(ic, psx_ic_read16);
+    HANDLE_READ_OP(scratchpad, psx_scratchpad_read16);
+    HANDLE_READ_OP(gpu, psx_gpu_read16);
+    HANDLE_READ_OP(spu, psx_spu_read16);
+    HANDLE_READ_OP(timer, psx_timer_read16);
+    HANDLE_READ_OP(cdrom, psx_cdrom_read16);
+    HANDLE_READ_OP(pad, psx_pad_read16);
+    HANDLE_READ_OP(mdec, psx_mdec_read16);
 
     if (addr == 0x1f80105a)
         return sio_ctrl;
@@ -110,7 +171,7 @@
     if (addr == 0x1f400006)
         return 0x1fe0;
 
-    printf("Unhandled 16-bit read from %08x:%08x\n", vaddr, addr);
+    print("Unhandled 16-bit read from %08x:%08x\n", vaddr, addr);
 
     // exit(1);
 
@@ -124,22 +185,22 @@
 
     addr &= g_psx_bus_region_mask_table[addr >> 29];
 
-    HANDLE_READ(bios, 8);
-    HANDLE_READ(ram, 8);
-    HANDLE_READ(dma, 8);
-    HANDLE_READ(exp1, 8);
-    HANDLE_READ(exp2, 8);
-    HANDLE_READ(mc1, 8);
-    HANDLE_READ(mc2, 8);
-    HANDLE_READ(mc3, 8);
-    HANDLE_READ(ic, 8);
-    HANDLE_READ(scratchpad, 8);
-    HANDLE_READ(gpu, 8);
-    HANDLE_READ(spu, 8);
-    HANDLE_READ(timer, 8);
-    HANDLE_READ(cdrom, 8);
-    HANDLE_READ(pad, 8);
-    HANDLE_READ(mdec, 8);
+    HANDLE_READ_OP(bios, psx_bios_read8);
+    HANDLE_READ_OP(ram, psx_ram_read8);
+    HANDLE_READ_OP(dma, psx_dma_read8);
+    HANDLE_READ_OP(exp1, psx_exp1_read8);
+    HANDLE_READ_OP(exp2, psx_exp2_read8);
+    HANDLE_READ_OP(mc1, psx_mc1_read8);
+    HANDLE_READ_OP(mc2, psx_mc2_read8);
+    HANDLE_READ_OP(mc3, psx_mc3_read8);
+    HANDLE_READ_OP(ic, psx_ic_read8);
+    HANDLE_READ_OP(scratchpad, psx_scratchpad_read8);
+    HANDLE_READ_OP(gpu, psx_gpu_read8);
+    HANDLE_READ_OP(spu, psx_spu_read8);
+    HANDLE_READ_OP(timer, psx_timer_read8);
+    HANDLE_READ_OP(cdrom, psx_cdrom_read8);
+    HANDLE_READ_OP(pad, psx_pad_read8);
+    HANDLE_READ_OP(mdec, psx_mdec_read8);
 
     // printf("Unhandled 8-bit read from %08x:%08x\n", vaddr, addr);
 
@@ -159,24 +220,24 @@
         log_fatal("Unaligned 32-bit write to %08x:%08x (%08x)", vaddr, addr, value);
     }
 
-    HANDLE_WRITE(bios, 32);
-    HANDLE_WRITE(ram, 32);
-    HANDLE_WRITE(dma, 32);
-    HANDLE_WRITE(exp1, 32);
-    HANDLE_WRITE(exp2, 32);
-    HANDLE_WRITE(mc1, 32);
-    HANDLE_WRITE(mc2, 32);
-    HANDLE_WRITE(mc3, 32);
-    HANDLE_WRITE(ic, 32);
-    HANDLE_WRITE(scratchpad, 32);
-    HANDLE_WRITE(gpu, 32);
-    HANDLE_WRITE(spu, 32);
-    HANDLE_WRITE(timer, 32);
-    HANDLE_WRITE(cdrom, 32);
-    HANDLE_WRITE(pad, 32);
-    HANDLE_WRITE(mdec, 32);
+    HANDLE_WRITE_OP(bios, psx_bios_write32);
+    HANDLE_WRITE_OP(ram, psx_ram_write32);
+    HANDLE_WRITE_OP(dma, psx_dma_write32);
+    HANDLE_WRITE_OP(exp1, psx_exp1_write32);
+    HANDLE_WRITE_OP(exp2, psx_exp2_write32);
+    HANDLE_WRITE_OP(mc1, psx_mc1_write32);
+    HANDLE_WRITE_OP(mc2, psx_mc2_write32);
+    HANDLE_WRITE_OP(mc3, psx_mc3_write32);
+    HANDLE_WRITE_OP(ic, psx_ic_write32);
+    HANDLE_WRITE_OP(scratchpad, psx_scratchpad_write32);
+    HANDLE_WRITE_OP(gpu, psx_gpu_write32);
+    HANDLE_WRITE_OP(spu, psx_spu_write32);
+    HANDLE_WRITE_OP(timer, psx_timer_write32);
+    HANDLE_WRITE_OP(cdrom, psx_cdrom_write32);
+    HANDLE_WRITE_OP(pad, psx_pad_write32);
+    HANDLE_WRITE_OP(mdec, psx_mdec_write32);
 
-    printf("Unhandled 32-bit write to %08x:%08x (%08x)\n", vaddr, addr, value);
+    print("Unhandled 32-bit write to %08x:%08x (%08x)\n", vaddr, addr, value);
 
     //exit(1);
 }
@@ -193,26 +254,26 @@
         log_fatal("Unaligned 16-bit write to %08x:%08x (%04x)", vaddr, addr, value);
     }
 
-    HANDLE_WRITE(bios, 16);
-    HANDLE_WRITE(ram, 16);
-    HANDLE_WRITE(dma, 16);
-    HANDLE_WRITE(exp1, 16);
-    HANDLE_WRITE(exp2, 16);
-    HANDLE_WRITE(mc1, 16);
-    HANDLE_WRITE(mc2, 16);
-    HANDLE_WRITE(mc3, 16);
-    HANDLE_WRITE(ic, 16);
-    HANDLE_WRITE(scratchpad, 16);
-    HANDLE_WRITE(gpu, 16);
-    HANDLE_WRITE(spu, 16);
-    HANDLE_WRITE(timer, 16);
-    HANDLE_WRITE(cdrom, 16);
-    HANDLE_WRITE(pad, 16);
-    HANDLE_WRITE(mdec, 16);
+    HANDLE_WRITE_OP(bios, psx_bios_write16);
+    HANDLE_WRITE_OP(ram, psx_ram_write16);
+    HANDLE_WRITE_OP(dma, psx_dma_write16);
+    HANDLE_WRITE_OP(exp1, psx_exp1_write16);
+    HANDLE_WRITE_OP(exp2, psx_exp2_write16);
+    HANDLE_WRITE_OP(mc1, psx_mc1_write16);
+    HANDLE_WRITE_OP(mc2, psx_mc2_write16);
+    HANDLE_WRITE_OP(mc3, psx_mc3_write16);
+    HANDLE_WRITE_OP(ic, psx_ic_write16);
+    HANDLE_WRITE_OP(scratchpad, psx_scratchpad_write16);
+    HANDLE_WRITE_OP(gpu, psx_gpu_write16);
+    HANDLE_WRITE_OP(spu, psx_spu_write16);
+    HANDLE_WRITE_OP(timer, psx_timer_write16);
+    HANDLE_WRITE_OP(cdrom, psx_cdrom_write16);
+    HANDLE_WRITE_OP(pad, psx_pad_write16);
+    HANDLE_WRITE_OP(mdec, psx_mdec_write16);
 
     // if (addr == 0x1f80105a) { sio_ctrl = value; return; }
 
-    printf("Unhandled 16-bit write to %08x:%08x (%04x)\n", vaddr, addr, value);
+    print("Unhandled 16-bit write to %08x:%08x (%04x)\n", vaddr, addr, value);
 
     //exit(1);
 }
@@ -224,24 +285,24 @@
 
     addr &= g_psx_bus_region_mask_table[addr >> 29];
 
-    HANDLE_WRITE(bios, 8);
-    HANDLE_WRITE(ram, 8);
-    HANDLE_WRITE(dma, 8);
-    HANDLE_WRITE(exp1, 8);
-    HANDLE_WRITE(exp2, 8);
-    HANDLE_WRITE(mc1, 8);
-    HANDLE_WRITE(mc2, 8);
-    HANDLE_WRITE(mc3, 8);
-    HANDLE_WRITE(ic, 8);
-    HANDLE_WRITE(scratchpad, 8);
-    HANDLE_WRITE(gpu, 8);
-    HANDLE_WRITE(spu, 8);
-    HANDLE_WRITE(timer, 8);
-    HANDLE_WRITE(cdrom, 8);
-    HANDLE_WRITE(pad, 8);
-    HANDLE_WRITE(mdec, 8);
+    HANDLE_WRITE_OP(bios, psx_bios_write8);
+    HANDLE_WRITE_OP(ram, psx_ram_write8);
+    HANDLE_WRITE_OP(dma, psx_dma_write8);
+    HANDLE_WRITE_OP(exp1, psx_exp1_write8);
+    HANDLE_WRITE_OP(exp2, psx_exp2_write8);
+    HANDLE_WRITE_OP(mc1, psx_mc1_write8);
+    HANDLE_WRITE_OP(mc2, psx_mc2_write8);
+    HANDLE_WRITE_OP(mc3, psx_mc3_write8);
+    HANDLE_WRITE_OP(ic, psx_ic_write8);
+    HANDLE_WRITE_OP(scratchpad, psx_scratchpad_write8);
+    HANDLE_WRITE_OP(gpu, psx_gpu_write8);
+    HANDLE_WRITE_OP(spu, psx_spu_write8);
+    HANDLE_WRITE_OP(timer, psx_timer_write8);
+    HANDLE_WRITE_OP(cdrom, psx_cdrom_write8);
+    HANDLE_WRITE_OP(pad, psx_pad_write8);
+    HANDLE_WRITE_OP(mdec, psx_mdec_write8);
 
-    printf("Unhandled 8-bit write to %08x:%08x (%02x)\n", vaddr, addr, value);
+    print("Unhandled 8-bit write to %08x:%08x (%02x)\n", vaddr, addr, value);
 
     //exit(1);
 }
@@ -290,25 +351,25 @@
     bus->gpu = gpu;
 }
 
-void psx_bus_init_spu(psx_bus_t* bus, psx_spu_t* spu) {
-    bus->spu = spu;
-}
+void psx_bus_init_spu(psx_bus_t* bus, struct psx_spu_t* spu) {
+    bus->spu = spu;
+}
 
-void psx_bus_init_timer(psx_bus_t* bus, psx_timer_t* timer) {
-    bus->timer = timer;
-}
+void psx_bus_init_timer(psx_bus_t* bus, struct psx_timer_t* timer) {
+    bus->timer = timer;
+}
 
 void psx_bus_init_cdrom(psx_bus_t* bus, psx_cdrom_t* cdrom) {
     bus->cdrom = cdrom;
 }
 
-void psx_bus_init_pad(psx_bus_t* bus, psx_pad_t* pad) {
-    bus->pad = pad;
-}
+void psx_bus_init_pad(psx_bus_t* bus, struct psx_pad_t* pad) {
+    bus->pad = pad;
+}
 
-void psx_bus_init_mdec(psx_bus_t* bus, psx_mdec_t* mdec) {
-    bus->mdec = mdec;
-}
+void psx_bus_init_mdec(psx_bus_t* bus, struct psx_mdec_t* mdec) {
+    bus->mdec = mdec;
+}
 
 uint32_t psx_bus_get_access_cycles(psx_bus_t* bus) {
     uint32_t cycles = bus->access_cycles;
@@ -318,5 +379,5 @@
     return cycles;
 }
 
-#undef HANDLE_READ
-#undef HANDLE_WRITE
\ No newline at end of file
+#undef HANDLE_READ_OP
+#undef HANDLE_WRITE_OP
--- a/psx/bus.h
+++ b/psx/bus.h
@@ -1,12 +1,12 @@
 #ifndef BUS_H
 #define BUS_H
 
-#include <stdint.h>
+#include "p9.h"
 
-struct psx_bus_t;
+struct psx_bus_t;
+
+typedef struct psx_bus_t psx_bus_t;
 
-typedef struct psx_bus_t psx_bus_t;
-
 psx_bus_t* psx_bus_create(void);
 void psx_bus_init(psx_bus_t*);
 uint32_t psx_bus_read32(psx_bus_t*, uint32_t);
@@ -18,4 +18,4 @@
 uint32_t psx_bus_get_access_cycles(psx_bus_t*);
 void psx_bus_destroy(psx_bus_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/bus_init.h
+++ b/psx/bus_init.h
@@ -1,59 +1,78 @@
-#ifndef BUS_INIT_H
-#define BUS_INIT_H
-
-#include "dev/cdrom/cdrom.h"
-#include "dev/bios.h"
-#include "dev/ram.h"
-#include "dev/dma.h"
-#include "dev/exp1.h"
-#include "dev/exp2.h"
-#include "dev/mc1.h"
-#include "dev/mc2.h"
-#include "dev/mc3.h"
-#include "dev/ic.h"
-#include "dev/scratchpad.h"
-#include "dev/gpu.h"
-#include "dev/spu.h"
-#include "dev/timer.h"
-#include "dev/pad.h"
-#include "dev/mdec.h"
-
-struct psx_bus_t {
-    psx_bios_t* bios;
-    psx_ram_t* ram;
-    psx_dma_t* dma;
-    psx_exp1_t* exp1;
-    psx_exp2_t* exp2;
-    psx_mc1_t* mc1;
-    psx_mc2_t* mc2;
-    psx_mc3_t* mc3;
-    psx_ic_t* ic;
-    psx_scratchpad_t* scratchpad;
-    psx_gpu_t* gpu;
-    psx_spu_t* spu;
-    psx_timer_t* timer;
-    psx_cdrom_t* cdrom;
-    psx_pad_t* pad;
-    psx_mdec_t* mdec;
+#ifndef BUS_INIT_H
+#define BUS_INIT_H
+
+#include "p9.h"
+
+struct psx_bios_t;
+struct psx_ram_t;
+struct psx_dma_t;
+struct psx_exp1_t;
+struct psx_exp2_t;
+struct psx_mc1_t;
+struct psx_mc2_t;
+struct psx_mc3_t;
+struct psx_ic_t;
+struct psx_scratchpad_t;
+struct psx_gpu_t;
+struct psx_spu_t;
+struct psx_timer_t;
+struct psx_cdrom_t;
+struct psx_pad_t;
+struct psx_mdec_t;
+
+typedef struct psx_bios_t psx_bios_t;
+typedef struct psx_ram_t psx_ram_t;
+typedef struct psx_dma_t psx_dma_t;
+typedef struct psx_exp1_t psx_exp1_t;
+typedef struct psx_exp2_t psx_exp2_t;
+typedef struct psx_mc1_t psx_mc1_t;
+typedef struct psx_mc2_t psx_mc2_t;
+typedef struct psx_mc3_t psx_mc3_t;
+typedef struct psx_ic_t psx_ic_t;
+typedef struct psx_scratchpad_t psx_scratchpad_t;
+typedef struct psx_gpu_t psx_gpu_t;
+typedef struct psx_spu_t psx_spu_t;
+typedef struct psx_timer_t psx_timer_t;
+typedef struct psx_cdrom_t psx_cdrom_t;
+typedef struct psx_pad_t psx_pad_t;
+typedef struct psx_mdec_t psx_mdec_t;
 
-    uint32_t access_cycles;
-};
+struct psx_bus_t {
+    struct psx_bios_t* bios;
+    struct psx_ram_t* ram;
+    struct psx_dma_t* dma;
+    struct psx_exp1_t* exp1;
+    struct psx_exp2_t* exp2;
+    struct psx_mc1_t* mc1;
+    struct psx_mc2_t* mc2;
+    struct psx_mc3_t* mc3;
+    struct psx_ic_t* ic;
+    struct psx_scratchpad_t* scratchpad;
+    struct psx_gpu_t* gpu;
+    struct psx_spu_t* spu;
+    struct psx_timer_t* timer;
+    struct psx_cdrom_t* cdrom;
+    struct psx_pad_t* pad;
+    struct psx_mdec_t* mdec;
+
+    uint32_t access_cycles;
+};
+
+void psx_bus_init_bios(psx_bus_t*, struct psx_bios_t*);
+void psx_bus_init_ram(psx_bus_t*, struct psx_ram_t*);
+void psx_bus_init_dma(psx_bus_t*, struct psx_dma_t*);
+void psx_bus_init_exp1(psx_bus_t*, struct psx_exp1_t*);
+void psx_bus_init_exp2(psx_bus_t*, struct psx_exp2_t*);
+void psx_bus_init_mc1(psx_bus_t*, struct psx_mc1_t*);
+void psx_bus_init_mc2(psx_bus_t*, struct psx_mc2_t*);
+void psx_bus_init_mc3(psx_bus_t*, struct psx_mc3_t*);
+void psx_bus_init_ic(psx_bus_t*, struct psx_ic_t*);
+void psx_bus_init_scratchpad(psx_bus_t*, struct psx_scratchpad_t*);
+void psx_bus_init_gpu(psx_bus_t*, struct psx_gpu_t*);
+void psx_bus_init_spu(psx_bus_t*, struct psx_spu_t*);
+void psx_bus_init_timer(psx_bus_t*, struct psx_timer_t*);
+void psx_bus_init_cdrom(psx_bus_t*, struct psx_cdrom_t*);
+void psx_bus_init_pad(psx_bus_t*, struct psx_pad_t*);
+void psx_bus_init_mdec(psx_bus_t*, struct psx_mdec_t*);
 
-void psx_bus_init_bios(psx_bus_t*, psx_bios_t*);
-void psx_bus_init_ram(psx_bus_t*, psx_ram_t*);
-void psx_bus_init_dma(psx_bus_t*, psx_dma_t*);
-void psx_bus_init_exp1(psx_bus_t*, psx_exp1_t*);
-void psx_bus_init_exp2(psx_bus_t*, psx_exp2_t*);
-void psx_bus_init_mc1(psx_bus_t*, psx_mc1_t*);
-void psx_bus_init_mc2(psx_bus_t*, psx_mc2_t*);
-void psx_bus_init_mc3(psx_bus_t*, psx_mc3_t*);
-void psx_bus_init_ic(psx_bus_t*, psx_ic_t*);
-void psx_bus_init_scratchpad(psx_bus_t*, psx_scratchpad_t*);
-void psx_bus_init_gpu(psx_bus_t*, psx_gpu_t*);
-void psx_bus_init_spu(psx_bus_t*, psx_spu_t*);
-void psx_bus_init_timer(psx_bus_t*, psx_timer_t*);
-void psx_bus_init_cdrom(psx_bus_t*, psx_cdrom_t*);
-void psx_bus_init_pad(psx_bus_t*, psx_pad_t*);
-void psx_bus_init_mdec(psx_bus_t*, psx_mdec_t*);
-
-#endif
\ No newline at end of file
+#endif
--- a/psx/cpu.c
+++ b/psx/cpu.c
@@ -2,8 +2,7 @@
 #include "bus.h"
 #include "log.h"
 
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
 #include "cpu_debug.h"
 
@@ -118,42 +117,42 @@
     cpu->branch = 1; \
     cpu->branch_taken = 1; }
 
-void cpu_a_kcall_hook(psx_cpu_t* cpu) {
-    switch (cpu->r[9]) {
-        case 0x09: putc(R_A0, stdout); break;
-        case 0x3c: putchar(R_A0); break;
-        case 0x3e: {
-            uint32_t src = R_A0;
-
-            char c = psx_bus_read8(cpu->bus, src++);
-
-            while (c) {
-                putchar(c);
-
-                c = psx_bus_read8(cpu->bus, src++);
-            }
-        } break;
-    }
+void cpu_a_kcall_hook(psx_cpu_t* cpu) {
+    switch (cpu->r[9]) {
+        case 0x09: print("%c", R_A0); break;
+        case 0x3c: print("%c", R_A0); break;
+        case 0x3e: {
+            uint32_t src = R_A0;
+
+            char c = psx_bus_read8(cpu->bus, src++);
+
+            while (c) {
+                print("%c", c);
+
+                c = psx_bus_read8(cpu->bus, src++);
+            }
+        } break;
+    }
+}
+
+void cpu_b_kcall_hook(psx_cpu_t* cpu) {
+    switch (cpu->r[9]) {
+        case 0x3b: print("%c", R_A0); break;
+        case 0x3d: print("%c", R_A0); break;
+        case 0x3f: {
+            uint32_t src = R_A0;
+
+            char c = psx_bus_read8(cpu->bus, src++);
+
+            while (c) {
+                print("%c", c);
+
+                c = psx_bus_read8(cpu->bus, src++);
+            }
+        } break;
+    }
 }
 
-void cpu_b_kcall_hook(psx_cpu_t* cpu) {
-    switch (cpu->r[9]) {
-        case 0x3b: putc(R_A0, stdout); break;
-        case 0x3d: putchar(R_A0); break;
-        case 0x3f: {
-            uint32_t src = R_A0;
-
-            char c = psx_bus_read8(cpu->bus, src++);
-
-            while (c) {
-                putchar(c);
-
-                c = psx_bus_read8(cpu->bus, src++);
-            }
-        } break;
-    }
-}
-
 psx_cpu_t* psx_cpu_create(void) {
     return (psx_cpu_t*)malloc(sizeof(psx_cpu_t));
 }
@@ -173,18 +172,16 @@
     cpu->b_function_hook = hook;
 }
 
-void psx_cpu_save_state(psx_cpu_t* cpu, FILE* file) {
-    fwrite((char*)cpu, sizeof(*cpu) - sizeof(psx_bus_t*), 1, file);
-}
+void psx_cpu_save_state(psx_cpu_t* cpu, void* file) {
+    USED(cpu);
+    USED(file);
+}
+
+void psx_cpu_load_state(psx_cpu_t* cpu, void* file) {
+    USED(cpu);
+    USED(file);
+}
 
-void psx_cpu_load_state(psx_cpu_t* cpu, FILE* file) {
-    if (!fread((char*)cpu, sizeof(*cpu) - sizeof(psx_bus_t*), 1, file)) {
-        perror("Error reading CPU state");
-
-        exit(1);
-    }
-}
-
 void psx_cpu_init(psx_cpu_t* cpu, psx_bus_t* bus) {
     memset(cpu, 0, sizeof(psx_cpu_t));
 
@@ -298,7 +295,7 @@
     int cyc = psx_cpu_execute(cpu);
 
     if (!cyc) {
-        printf("psxe: Illegal instruction %08x at %08x (next=%08x, saved=%08x)\n", cpu->opcode, cpu->pc, cpu->next_pc, cpu->saved_pc);
+        print("psxe: Illegal instruction %08x at %08x (next=%08x, saved=%08x)\n", cpu->opcode, cpu->pc, cpu->next_pc, cpu->saved_pc);
 
         psx_cpu_exception(cpu, CAUSE_RI);
     }
@@ -1562,8 +1559,7 @@
 #define R_TRX cpu->cop2_cr.tr.x
 #define R_TRY cpu->cop2_cr.tr.y
 #define R_TRZ cpu->cop2_cr.tr.z
-#define R_RT11 cpu->cop2_cr.rt.m[0].c[0]
-#define R_RT11 cpu->cop2_cr.rt.m[0].c[0]
+#define R_RT11 cpu->cop2_cr.rt.m[0].c[0]
 #define R_RT12 cpu->cop2_cr.rt.m[0].c[1]
 #define R_RT13 cpu->cop2_cr.rt.m[1].c[0]
 #define R_RT21 cpu->cop2_cr.rt.m[1].c[1]
@@ -1881,8 +1877,7 @@
 #define R_VX v.p[0]
 #define R_VY v.p[1]
 #define R_VZ v.z
-#define R_MX11 mx.m[0].c[0]
-#define R_MX11 mx.m[0].c[0]
+#define R_MX11 mx.m[0].c[0]
 #define R_MX12 mx.m[0].c[1]
 #define R_MX13 mx.m[1].c[0]
 #define R_MX21 mx.m[1].c[1]
@@ -1969,8 +1964,7 @@
 #undef R_VX
 #undef R_VY
 #undef R_VZ
-#undef R_MX11
-#undef R_MX11
+#undef R_MX11
 #undef R_MX12
 #undef R_MX13
 #undef R_MX21
@@ -2348,4 +2342,4 @@
 #undef DEBUG_ALL
 
 #undef SE8
-#undef SE16
\ No newline at end of file
+#undef SE16
--- a/psx/cpu.h
+++ b/psx/cpu.h
@@ -1,8 +1,7 @@
 #ifndef CPU_H
 #define CPU_H
 
-#include <stdint.h>
-#include <stdio.h>
+#include "p9.h"
 
 #include "bus.h"
 
@@ -228,8 +227,8 @@
 void psx_cpu_destroy(psx_cpu_t*);
 void psx_cpu_cycle(psx_cpu_t*);
 void psx_cpu_set_irq_pending(psx_cpu_t*);
-void psx_cpu_load_state(psx_cpu_t*, FILE*);
-void psx_cpu_save_state(psx_cpu_t*, FILE*);
+void psx_cpu_load_state(psx_cpu_t*, void*);
+void psx_cpu_save_state(psx_cpu_t*, void*);
 void psx_cpu_fetch(psx_cpu_t*);
 void psx_cpu_set_a_kcall_hook(psx_cpu_t*, psx_cpu_kcall_hook_t);
 void psx_cpu_set_b_kcall_hook(psx_cpu_t*, psx_cpu_kcall_hook_t);
@@ -310,4 +309,4 @@
 #define GTEF_M1POVF 0x40000000
 #define GTEF_ERRORF 0x80000000
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/bios.c
+++ b/psx/dev/bios.c
@@ -1,14 +1,31 @@
-#include "bios.h"
-#include "../log.h"
+#include "dev/bios.h"
+#include "log.h"
+
+#include "p9.h"
+
+static int
+psx_readn_fd(int fd, void* buf, uint32_t size)
+{
+    uint8_t* p = (uint8_t*)buf;
+    uint32_t n = size;
+
+    while (n) {
+        long r = read(fd, p, n);
+
+        if (r <= 0)
+            return -1;
+
+        p += r;
+        n -= r;
+    }
+
+    return 0;
+}
+
+psx_bios_t* psx_bios_create(void) {
+    return (psx_bios_t*)malloc(sizeof(psx_bios_t));
+}
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-psx_bios_t* psx_bios_create(void) {
-    return (psx_bios_t*)malloc(sizeof(psx_bios_t));
-}
-
 void psx_bios_init(psx_bios_t* bios) {
     memset(bios, 0, sizeof(psx_bios_t));
 
@@ -17,37 +34,49 @@
     bios->bus_delay = 18;
 }
 
-int psx_bios_load(psx_bios_t* bios, const char* path) {
-    if (!path)
-        return 0;
+int psx_bios_load(psx_bios_t* bios, const char* path) {
+    if (!path)
+        return 0;
+
+    int fd = open(path, OREAD);
+    vlong size;
+
+    if (fd < 0)
+        return 1;
 
-    FILE* file = fopen(path, "rb");
-
-    if (!file)
-        return 1;
-
     // Almost all PS1 BIOS ROMs are 512 KiB in size.
     // There's (at least) one exception, and that is SCPH-5903.
     // This is a special asian model PS1 that had built-in support
     // for Video CD (VCD) playback. Its BIOS is double the normal
     // size
-    fseek(file, 0, SEEK_END);
+    size = seek(fd, 0, 2);
+    if (size <= 0) {
+        close(fd);
+        return 2;
+    }
+
+    if (seek(fd, 0, 0) < 0) {
+        close(fd);
+        return 2;
+    }
+
+    bios->buf = malloc(size);
+    if (!bios->buf) {
+        close(fd);
+        return 2;
+    }
+    bios->io_size = size;
+
+    if (psx_readn_fd(fd, bios->buf, size) < 0) {
+        close(fd);
+        return 2;
+    }
+
+    close(fd);
+
+    return 0;
+}
 
-    size_t size = ftell(file);
-
-    fseek(file, 0, SEEK_SET);
-
-    bios->buf = malloc(size);
-    bios->io_size = size;
-
-    if (!fread(bios->buf, 1, size, file))
-        return 2;
-
-    fclose(file);
-
-    return 0;
-}
-
 uint32_t psx_bios_read32(psx_bios_t* bios, uint32_t offset) {
     return *((uint32_t*)(bios->buf + offset));
 }
@@ -60,19 +89,22 @@
     return bios->buf[offset];
 }
 
-void psx_bios_write32(psx_bios_t* bios, uint32_t offset, uint32_t value) {
-    log_warn("Unhandled 32-bit BIOS write at offset %08x (%08x)", offset, value);
-}
+void psx_bios_write32(psx_bios_t* bios, uint32_t offset, uint32_t value) {
+    USED(bios);
+    log_warn("Unhandled 32-bit BIOS write at offset %08x (%08x)", offset, value);
+}
+
+void psx_bios_write16(psx_bios_t* bios, uint32_t offset, uint16_t value) {
+    USED(bios);
+    log_warn("Unhandled 16-bit BIOS write at offset %08x (%04x)", offset, value);
+}
+
+void psx_bios_write8(psx_bios_t* bios, uint32_t offset, uint8_t value) {
+    USED(bios);
+    log_warn("Unhandled 8-bit BIOS write at offset %08x (%02x)", offset, value);
+}
 
-void psx_bios_write16(psx_bios_t* bios, uint32_t offset, uint16_t value) {
-    log_warn("Unhandled 16-bit BIOS write at offset %08x (%04x)", offset, value);
-}
-
-void psx_bios_write8(psx_bios_t* bios, uint32_t offset, uint8_t value) {
-    log_warn("Unhandled 8-bit BIOS write at offset %08x (%02x)", offset, value);
-}
-
 void psx_bios_destroy(psx_bios_t* bios) {
     free(bios->buf);
     free(bios);
-}
\ No newline at end of file
+}
--- a/psx/dev/bios.h
+++ b/psx/dev/bios.h
@@ -1,21 +1,23 @@
-#ifndef BIOS_H
-#define BIOS_H
+#ifndef PSX_DEV_BIOS_H
+#define PSX_DEV_BIOS_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "../log.h"
+#include "log.h"
 
 #define PSX_BIOS_SIZE   0x80000
 #define PSX_BIOS_BEGIN  0x1fc00000
 #define PSX_BIOS_END    0x1fc7ffff
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_bios_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    uint8_t* buf;
+};
+
+typedef struct psx_bios_t psx_bios_t;
 
-    uint8_t* buf;
-} psx_bios_t;
-
 psx_bios_t* psx_bios_create(void);
 void psx_bios_init(psx_bios_t*);
 int psx_bios_load(psx_bios_t*, const char*);
@@ -27,4 +29,4 @@
 void psx_bios_write8(psx_bios_t*, uint32_t, uint8_t);
 void psx_bios_destroy(psx_bios_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/cdrom/audio.c
+++ b/psx/dev/cdrom/audio.c
@@ -1,8 +1,8 @@
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "cdrom.h"
-#include "../spu.h"
+#include "dev/cdrom/cdrom.h"
+#include "dev/ic.h"
+#include "dev/spu.h"
 
 #define ITOB(b) itob_table[b]
 
@@ -95,19 +95,20 @@
     for (int j = 0; j < 28; j++) {
         uint16_t n = (cdrom->xa_buf[idx + 16 + blk + j * 4] >> (nib * 4)) & 0x0f;
 
-        int16_t t = (int16_t)(n << 12) >> 12; 
-        int16_t s = (t << shift) + (((h[0] * f0) + (h[1] * f1) + 32) / 64);
+        int16_t t = (int16_t)(n << 12) >> 12;
+        int32_t s = (t << shift) + (((h[0] * f0) + (h[1] * f1) + 32) / 64);
 
         s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
 
         h[1] = h[0];
-        h[0] = s;
+        h[0] = (int16_t)s;
 
-        buf[j] = s;
+        buf[j] = (int16_t)s;
     }
 }
 
 void cdrom_decode_xa_sector(psx_cdrom_t* cdrom, void* buf) {
+    USED(buf);
     int src = 24;
 
     int16_t left[28];
@@ -353,7 +354,7 @@
     int ss = (diff % (60 * 75)) / 75;
     int ff = (diff % (60 * 75)) % 75;
 
-    printf("report: track %u %02u:%02u:%02u relative=%d\n",
+    printf("report: track %d %02d:%02d:%02d relative=%d\n",
         track,
         mm, ss, ff,
         relative
@@ -425,4 +426,4 @@
 
         cdrom->cdda_remaining_samples -= 2;
     }
-}
\ No newline at end of file
+}
--- a/psx/dev/cdrom/cdrom.c
+++ b/psx/dev/cdrom/cdrom.c
@@ -1,9 +1,7 @@
-#include <string.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include "p9.h"
 
-#include "cdrom.h"
+#include "dev/cdrom/cdrom.h"
+#include "dev/ic.h"
 
 typedef void (*cdrom_cmd_func)(psx_cdrom_t* cdrom);
 
@@ -671,7 +669,7 @@
     printf("cdrom: %-16s (%02x) params: ", cdrom_cmd_names[data], data);
 
     if (queue_is_empty(cdrom->parameters)) {
-        puts("(none)");
+        print("(none)\n");
 
         return;
     }
@@ -685,6 +683,8 @@
 }
 
 void cdrom_write_null(psx_cdrom_t* cdrom, uint8_t data) {
+    USED(cdrom);
+    USED(data);
     /* Ignore writes */
 }
 
@@ -739,14 +739,14 @@
 }
 
 uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t addr) {
-    assert("32-bit CDROM reads are not supported" && 0);
-
-    return 0;
+    uint32_t v0 = psx_cdrom_read8(cdrom, addr);
+    uint32_t v1 = psx_cdrom_read8(cdrom, addr + 1);
+    uint32_t v2 = psx_cdrom_read8(cdrom, addr + 2);
+    uint32_t v3 = psx_cdrom_read8(cdrom, addr + 3);
+    return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24);
 }
 
 uint32_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t addr) {
-    assert("16-bit CDROM reads are not supported" && 0);
-
     // The CDROM controller is connected to the SUB-BUS which is a 16-bit
     // bus, but the output from the controller itself is 8-bit. I think
     // 16-bit accesses are handled as a pair of 8-bit accesses
@@ -754,9 +754,13 @@
 }
 
 void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) {
-    assert("32-bit CDROM writes are not supported" && 0);
+    psx_cdrom_write8(cdrom, addr + 0, value & 0xff);
+    psx_cdrom_write8(cdrom, addr + 1, (value >> 8) & 0xff);
+    psx_cdrom_write8(cdrom, addr + 2, (value >> 16) & 0xff);
+    psx_cdrom_write8(cdrom, addr + 3, (value >> 24) & 0xff);
 }
 
 void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) {
-    assert("16-bit CDROM writes are not supported" && 0);
-}
\ No newline at end of file
+    psx_cdrom_write8(cdrom, addr + 0, value & 0xff);
+    psx_cdrom_write8(cdrom, addr + 1, (value >> 8) & 0xff);
+}
--- a/psx/dev/cdrom/cdrom.h
+++ b/psx/dev/cdrom/cdrom.h
@@ -1,12 +1,13 @@
-#ifndef CDROM_H
-#define CDROM_H
+#ifndef PSX_DEV_CDROM_H
+#define PSX_DEV_CDROM_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "queue.h"
-#include "disc.h"
-#include "../ic.h"
+#include "dev/cdrom/queue.h"
+#include "dev/cdrom/disc.h"
 
+typedef struct psx_ic_t psx_ic_t;
+
 #define PSX_CDROM_BEGIN 0x1f801800
 #define PSX_CDROM_END   0x1f801803
 #define PSX_CDROM_SIZE  0x4
@@ -200,7 +201,7 @@
     QUERY_TRACK_TYPE
 };
 
-typedef struct {
+struct psx_cdrom_t {
     int mute;
     uint32_t bus_delay;
     uint32_t io_base, io_size;
@@ -257,8 +258,10 @@
     int16_t xa_left_resample_buf[XA_STEREO_RESAMPLE_MAX_SIZE];
     int16_t xa_right_resample_buf[XA_STEREO_RESAMPLE_MAX_SIZE];
     int16_t xa_mono_resample_buf[XA_MONO_RESAMPLE_MAX_SIZE];
-} psx_cdrom_t;
+};
 
+typedef struct psx_cdrom_t psx_cdrom_t;
+
 enum {
     CDR_VERSION_01,  // DTL-H2000                 (??-???-????)
     CDR_VERSION_C0A, // PSX (PU-7)                (19-Sep-1994)
@@ -307,4 +310,4 @@
 void psx_cdrom_get_audio_samples(psx_cdrom_t* cdrom, void* buf, size_t size);
 void psx_cdrom_destroy(psx_cdrom_t* cdrom);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/cdrom/cue.c
+++ b/psx/dev/cdrom/cue.c
@@ -1,10 +1,6 @@
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdio.h>
-#include <ctype.h>
+#include "p9.h"
 
-#include "cue.h"
+#include "dev/cdrom/cue.h"
 
 static const char* cue_keywords[] = {
     "4CH",
@@ -143,14 +139,12 @@
 }
 
 uint32_t cue_parse_msf(cue_t* cue) {
-    int m = 0;
-    int s = 0;
-    int f = 0;
+    uint32_t frames = 0;
 
     if (!isdigit(cue->c))
         return 0;
 
-    m = cue_parse_number(cue);
+    frames = cue_parse_number(cue) * 4500;
 
     if (cue->c != ':')
         return 0;
@@ -157,7 +151,7 @@
 
     cue->c = fgetc(cue->file);
 
-    s = cue_parse_number(cue);
+    frames += cue_parse_number(cue) * 75;
 
     if (cue->c != ':')
         return 0;
@@ -164,11 +158,11 @@
 
     cue->c = fgetc(cue->file);
 
-    f = cue_parse_number(cue);
+    frames += cue_parse_number(cue);
 
     // 1 second = 75 frames (sectors)
     // 1 minute = 60 seconds = 4500 frames
-    return f + (s * 75) + (m * 4500);
+    return frames;
 }
 
 void cue_parse_index(cue_t* cue) {
@@ -278,7 +272,7 @@
     while (isspace(cue->c))
         cue->c = fgetc(cue->file);
 
-    while (!feof(cue->file)) {
+    while (cue->c != EOF) {
         int kw = cue_parse_keyword(cue);
 
         switch (kw) {
@@ -303,12 +297,13 @@
                 while ((cue->c != '\n') && (cue->c != '\r'))
                     cue->c = fgetc(cue->file);
 
-                while ((cue->c == '\n') && (cue->c == '\r'))
+                while ((cue->c == '\n') || (cue->c == '\r'))
                     cue->c = fgetc(cue->file);
             } break;
 
             default: {
-                printf("Unknown keyword: %s (%u)\n", cue_keywords[kw], kw);
+                const char* k = (kw >= 0 && cue_keywords[kw]) ? cue_keywords[kw] : "<unknown>";
+                printf("Unknown keyword: %s (%d)\n", k, kw);
 
                 return 1;
             } break;
@@ -593,4 +588,4 @@
     disc->get_track_count = (get_track_count_func)cue_get_track_count;
     disc->get_track_lba = (get_track_lba_func)cue_get_track_lba;
     disc->destroy = (destroy_func)cue_destroy;
-}
\ No newline at end of file
+}
--- a/psx/dev/cdrom/cue.h
+++ b/psx/dev/cdrom/cue.h
@@ -1,12 +1,10 @@
-#ifndef CUE_H
-#define CUE_H
+#ifndef PSX_DEV_CUE_H
+#define PSX_DEV_CUE_H
 
-#include "list.h"
-#include "disc.h"
+#include "dev/cdrom/list.h"
+#include "dev/cdrom/disc.h"
 
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
+#include "p9.h"
 
 enum {
     CUE_OK = 0,
@@ -79,7 +77,7 @@
     list_t* files;
     list_t* tracks;
 
-    char c;
+    int c;
     FILE* file;
 } cue_t;
 
@@ -97,4 +95,4 @@
 void cue_init_disc(cue_t* cue, psx_disc_t* disc);
 void cue_destroy(cue_t* cue);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/cdrom/disc.c
+++ b/psx/dev/cdrom/disc.c
@@ -1,9 +1,19 @@
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "disc.h"
-#include "cue.h"
+#include "dev/cdrom/disc.h"
 
+/*
+ * Keep cue API usage opaque in this TU; this avoids typedef visibility
+ * issues with 6c while preserving link-time behavior.
+ */
+void* cue_create(void);
+void cue_init(void*);
+void cue_init_disc(void*, psx_disc_t*);
+int cue_parse(void*, const char*);
+int cue_load(void*, int);
+
+#define CUE_LD_FILE 1
+
 #define MSF_TO_LBA(m, s, f) ((m * 4500) + (s * 75) + f)
 
 const char* disc_cd_extensions[] = {
@@ -68,9 +78,11 @@
 }
 
 int psx_disc_open_as(psx_disc_t* disc, const char* path, int type) {
+    void* cue;
+
     switch (type) {
         case CD_EXT_CUE: {
-            cue_t* cue = cue_create();
+            cue = cue_create();
 
             cue_init(cue);
             cue_init_disc(cue, disc);
@@ -78,7 +90,7 @@
             if (cue_parse(cue, path))
                 return CDT_ERROR;
 
-            if (cue_load(cue, LD_FILE))
+            if (cue_load(cue, CUE_LD_FILE))
                 return CDT_ERROR;
         } break;
     }
@@ -110,4 +122,4 @@
     disc->destroy(disc->udata);
 
     free(disc);
-}
\ No newline at end of file
+}
--- a/psx/dev/cdrom/disc.h
+++ b/psx/dev/cdrom/disc.h
@@ -1,7 +1,7 @@
-#ifndef DISC_H
-#define DISC_H
+#ifndef PSX_DEV_DISC_H
+#define PSX_DEV_DISC_H
 
-#include <stdint.h>
+#include "p9.h"
 
 /*
     PSX disc reader API version 2 specification:
--- a/psx/dev/cdrom/impl.c
+++ b/psx/dev/cdrom/impl.c
@@ -1,4 +1,4 @@
-#include "cdrom.h"
+#include "dev/cdrom/cdrom.h"
 
 #define BTOI(b) btoi_table[b]
 #define ITOB(b) itob_table[b]
@@ -732,4 +732,4 @@
 
 void cdrom_cmd_videocd(psx_cdrom_t* cdrom) {
 
-}
\ No newline at end of file
+}
--- a/psx/dev/cdrom/list.c
+++ b/psx/dev/cdrom/list.c
@@ -1,7 +1,6 @@
-#include "list.h"
+#include "dev/cdrom/list.h"
 
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
 list_t* list_create(void) {
     list_t* list = malloc(sizeof(list_t));
@@ -119,4 +118,4 @@
     }
 
     free(list);
-}
\ No newline at end of file
+}
--- a/psx/dev/cdrom/list.h
+++ b/psx/dev/cdrom/list.h
@@ -1,7 +1,7 @@
-#ifndef LIST_H
-#define LIST_H
+#ifndef PSX_DEV_LIST_H
+#define PSX_DEV_LIST_H
 
-#include <stddef.h>
+#include "p9.h"
 
 typedef struct node_t node_t;
 
@@ -28,4 +28,4 @@
 void list_iterate(list_t* list, void (*func)(void*));
 void list_destroy(list_t* list);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/cdrom/queue.c
+++ b/psx/dev/cdrom/queue.c
@@ -1,8 +1,6 @@
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
+#include "p9.h"
 
-#include "queue.h"
+#include "dev/cdrom/queue.h"
 
 queue_t* queue_create(void) {
     return malloc(sizeof(queue_t));
@@ -72,4 +70,4 @@
 void queue_destroy(queue_t* queue) {
     free(queue->buf);
     free(queue);
-}
\ No newline at end of file
+}
--- a/psx/dev/cdrom/queue.h
+++ b/psx/dev/cdrom/queue.h
@@ -1,8 +1,7 @@
-#ifndef QUEUE_H
-#define QUEUE_H
+#ifndef PSX_DEV_QUEUE_H
+#define PSX_DEV_QUEUE_H
 
-#include <stdint.h>
-#include <stddef.h>
+#include "p9.h"
 
 typedef struct {
     uint8_t* buf;
@@ -24,4 +23,4 @@
 int queue_max_size(queue_t* queue);
 void queue_destroy(queue_t* queue);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -1,11 +1,7 @@
-#include "dma.h"
-#include "../log.h"
+#include "dev/dma.h"
+#include "log.h"
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
+#include "p9.h"
 
 psx_dma_t* psx_dma_create(void) {
     return (psx_dma_t*)malloc(sizeof(psx_dma_t));
@@ -291,12 +287,13 @@
     dma->gpu_irq_delay = size;
 }
 
-void psx_dma_do_gpu_burst(psx_dma_t* dma) {
-    printf("GPU DMA burst sync mode unimplemented\n");
+void psx_dma_do_gpu_burst(psx_dma_t* dma) {
+    USED(dma);
+    printf("GPU DMA burst sync mode unimplemented\n");
+
+    exit(1);
+}
 
-    exit(1);
-}
-
 psx_dma_do_fn_t g_psx_dma_gpu_table[] = {
     psx_dma_do_gpu_burst,
     psx_dma_do_gpu_request,
@@ -436,9 +433,10 @@
     dma->spu.bcr = 0;
 }
 
-void psx_dma_do_pio(psx_dma_t* dma) {
-    log_fatal("PIO DMA channel unimplemented");
-}
+void psx_dma_do_pio(psx_dma_t* dma) {
+    USED(dma);
+    log_fatal("PIO DMA channel unimplemented");
+}
 
 void psx_dma_do_otc(psx_dma_t* dma) {
     if ((!(dma->dpcr & DPCR_DMA6EN)) || (!CHCR_TRIG(otc)) || (!CHCR_BUSY(otc)))
@@ -473,8 +471,9 @@
     dma->otc.bcr = 0;
 }
 
-void psx_dma_update(psx_dma_t* dma, int cyc) {
-    if (dma->cdrom_irq_delay) {
+void psx_dma_update(psx_dma_t* dma, int cyc) {
+    USED(cyc);
+    if (dma->cdrom_irq_delay) {
         dma->cdrom_irq_delay = 0;
 
         if ((dma->dicr & DICR_DMA3EN) && !dma->cdrom_irq_delay)
@@ -539,4 +538,4 @@
     free(dma);
 }
 
-#undef CR
\ No newline at end of file
+#undef CR
--- a/psx/dev/dma.h
+++ b/psx/dev/dma.h
@@ -1,14 +1,14 @@
-#ifndef DMA_H
-#define DMA_H
+#ifndef PSX_DEV_DMA_H
+#define PSX_DEV_DMA_H
 
-#include <stdint.h>
+#include "p9.h"
 
 #define PSX_DMAR_BEGIN 0x1f801080
 #define PSX_DMAR_SIZE  0x80
 #define PSX_DMAR_END   0x1f8010ff
 
-#include "../bus.h"
-#include "ic.h"
+#include "bus.h"
+#include "dev/ic.h"
 
 typedef struct {
     uint32_t madr;
@@ -16,9 +16,9 @@
     uint32_t chcr;
 } dma_channel_t;
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_dma_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
     psx_bus_t* bus;
     psx_ic_t* ic;
@@ -38,9 +38,11 @@
     int gpu_irq_delay;
     int otc_irq_delay;
 
-    uint32_t dpcr;
-    uint32_t dicr;
-} psx_dma_t;
+    uint32_t dpcr;
+    uint32_t dicr;
+};
+
+typedef struct psx_dma_t psx_dma_t;
 
 psx_dma_t* psx_dma_create(void);
 void psx_dma_init(psx_dma_t*, psx_bus_t*, psx_ic_t*);
@@ -168,4 +170,4 @@
 #define DICR_DMA5FL 0x20000000
 #define DICR_DMA6FL 0x40000000
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/exp1.c
+++ b/psx/dev/exp1.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "../log.h"
-#include "exp1.h"
+#include "log.h"
+#include "dev/exp1.h"
 
 psx_exp1_t* psx_exp1_create(void) {
     return (psx_exp1_t*)malloc(sizeof(psx_exp1_t));
@@ -55,19 +53,22 @@
     return exp1->rom[offset];
 }
 
-void psx_exp1_write32(psx_exp1_t* exp1, uint32_t offset, uint32_t value) {
-    log_warn("Unhandled 32-bit EXP1 write at offset %08x (%08x)", offset, value);
-}
+void psx_exp1_write32(psx_exp1_t* exp1, uint32_t offset, uint32_t value) {
+    USED(exp1);
+    log_warn("Unhandled 32-bit EXP1 write at offset %08x (%08x)", offset, value);
+}
+
+void psx_exp1_write16(psx_exp1_t* exp1, uint32_t offset, uint16_t value) {
+    USED(exp1);
+    log_warn("Unhandled 16-bit EXP1 write at offset %08x (%04x)", offset, value);
+}
+
+void psx_exp1_write8(psx_exp1_t* exp1, uint32_t offset, uint8_t value) {
+    USED(exp1);
+    log_warn("Unhandled 8-bit EXP1 write at offset %08x (%02x)", offset, value);
+}
 
-void psx_exp1_write16(psx_exp1_t* exp1, uint32_t offset, uint16_t value) {
-    log_warn("Unhandled 16-bit EXP1 write at offset %08x (%04x)", offset, value);
-}
-
-void psx_exp1_write8(psx_exp1_t* exp1, uint32_t offset, uint8_t value) {
-    log_warn("Unhandled 8-bit EXP1 write at offset %08x (%02x)", offset, value);
-}
-
 void psx_exp1_destroy(psx_exp1_t* exp1) {
     free(exp1->rom);
     free(exp1);
-}
\ No newline at end of file
+}
--- a/psx/dev/exp1.h
+++ b/psx/dev/exp1.h
@@ -1,22 +1,24 @@
-#ifndef EXP1_H
-#define EXP1_H
+#ifndef PSX_DEV_EXP1_H
+#define PSX_DEV_EXP1_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "mc1.h"
+#include "dev/mc1.h"
 
 #define PSX_EXP1_BEGIN 0x1f000000
 #define PSX_EXP1_SIZE  0x80000
 #define PSX_EXP1_END   0x1f080000
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_exp1_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    psx_mc1_t* mc1;
+    uint8_t* rom;
+};
+
+typedef struct psx_exp1_t psx_exp1_t;
 
-    psx_mc1_t* mc1;
-    uint8_t* rom;
-} psx_exp1_t;
-
 psx_exp1_t* psx_exp1_create(void);
 int psx_exp1_init(psx_exp1_t*, psx_mc1_t*, const char*);
 int psx_exp1_load(psx_exp1_t*, const char*);
@@ -28,4 +30,4 @@
 void psx_exp1_write8(psx_exp1_t*, uint32_t, uint8_t);
 void psx_exp1_destroy(psx_exp1_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/exp2.c
+++ b/psx/dev/exp2.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "../log.h"
-#include "exp2.h"
+#include "log.h"
+#include "dev/exp2.h"
 
 psx_exp2_t* psx_exp2_create(void) {
     return (psx_exp2_t*)malloc(sizeof(psx_exp2_t));
@@ -23,18 +21,24 @@
     exp2->atc_rx = c;
 }
 
-void psx_exp2_duart_put(psx_exp2_t* exp2, char c) {
-    /* To-do */
-}
+void psx_exp2_duart_put(psx_exp2_t* exp2, char c) {
+    USED(exp2);
+    USED(c);
+    /* To-do */
+}
+
+uint32_t psx_exp2_read32(psx_exp2_t* exp2, uint32_t offset) {
+    USED(exp2);
+    USED(offset);
+    return 0;
+}
+
+uint16_t psx_exp2_read16(psx_exp2_t* exp2, uint32_t offset) {
+    USED(exp2);
+    USED(offset);
+    return 0;
+}
 
-uint32_t psx_exp2_read32(psx_exp2_t* exp2, uint32_t offset) {
-    return 0;
-}
-
-uint16_t psx_exp2_read16(psx_exp2_t* exp2, uint32_t offset) {
-    return 0;
-}
-
 uint8_t psx_exp2_read8(psx_exp2_t* exp2, uint32_t offset) {
     switch (offset) {
         case EXP2_DTL_ATC_STAT:
@@ -48,14 +52,16 @@
     return 0;
 }
 
-void psx_exp2_write32(psx_exp2_t* exp2, uint32_t offset, uint32_t value) {
-    log_warn("Unhandled 32-bit EXP2 write at offset %08x (%08x)", offset, value);
-}
+void psx_exp2_write32(psx_exp2_t* exp2, uint32_t offset, uint32_t value) {
+    USED(exp2);
+    log_warn("Unhandled 32-bit EXP2 write at offset %08x (%08x)", offset, value);
+}
+
+void psx_exp2_write16(psx_exp2_t* exp2, uint32_t offset, uint16_t value) {
+    USED(exp2);
+    log_warn("Unhandled 16-bit EXP2 write at offset %08x (%04x)", offset, value);
+}
 
-void psx_exp2_write16(psx_exp2_t* exp2, uint32_t offset, uint16_t value) {
-    log_warn("Unhandled 16-bit EXP2 write at offset %08x (%04x)", offset, value);
-}
-
 void psx_exp2_write8(psx_exp2_t* exp2, uint32_t offset, uint8_t value) {
     switch (offset) {
         case EXP2_DTL_ATC_DATA:
@@ -77,4 +83,4 @@
 
 void psx_exp2_destroy(psx_exp2_t* exp2) {
     free(exp2);
-}
\ No newline at end of file
+}
--- a/psx/dev/exp2.h
+++ b/psx/dev/exp2.h
@@ -1,7 +1,7 @@
-#ifndef EXP2_H
-#define EXP2_H
+#ifndef PSX_DEV_EXP2_H
+#define PSX_DEV_EXP2_H
 
-#include <stdint.h>
+#include "p9.h"
 
 #define PSX_EXP2_BEGIN 0x1f802000
 #define PSX_EXP2_SIZE  0x1fe000
@@ -19,20 +19,22 @@
 
 typedef void (*exp2_tty_tx)(void*, uint8_t);
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_exp2_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
     void* duart_udata;
     void* atcons_udata;
 
     exp2_tty_tx duart_tx;
-    exp2_tty_tx atcons_tx;
+    exp2_tty_tx atcons_tx;
+
+    uint8_t atc_stat;
+    uint8_t atc_rx;
+};
+
+typedef struct psx_exp2_t psx_exp2_t;
 
-    uint8_t atc_stat;
-    uint8_t atc_rx;
-} psx_exp2_t;
-
 psx_exp2_t* psx_exp2_create(void);
 void psx_exp2_init(psx_exp2_t*, exp2_tty_tx atcons_tx, exp2_tty_tx duart_tx);
 void psx_exp2_atcons_put(psx_exp2_t*, char);
@@ -45,4 +47,4 @@
 void psx_exp2_write8(psx_exp2_t*, uint32_t, uint8_t);
 void psx_exp2_destroy(psx_exp2_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -1,10 +1,6 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
+#include "p9.h"
 
-#include "gpu.h"
-#include "../log.h"
+#include "dev/gpu.h"
 
 #define SE10(v) ((int16_t)((v) << 5) >> 5)
 
@@ -115,13 +111,14 @@
         case 0x04: return gpu->gpustat | 0x1c000000;
     }
 
-    log_warn("Unhandled 32-bit GPU read at offset %08x", offset);
+    print("Unhandled 32-bit GPU read at offset %08x\n", offset);
 
     return 0x0;
 }
 
-uint16_t psx_gpu_read16(psx_gpu_t* gpu, uint32_t offset) {
-    printf("Unhandled 16-bit GPU read at offset %08x\n", offset);
+uint16_t psx_gpu_read16(psx_gpu_t* gpu, uint32_t offset) {
+    USED(gpu);
+    printf("Unhandled 16-bit GPU read at offset %08x\n", offset);
 
     return 0;
 
@@ -128,8 +125,9 @@
     // exit(1);
 }
 
-uint8_t psx_gpu_read8(psx_gpu_t* gpu, uint32_t offset) {
-    printf("Unhandled 8-bit GPU read at offset %08x\n", offset);
+uint8_t psx_gpu_read8(psx_gpu_t* gpu, uint32_t offset) {
+    USED(gpu);
+    printf("Unhandled 8-bit GPU read at offset %08x\n", offset);
 
     return 0;
 
@@ -222,8 +220,9 @@
 #define TL(z, a, b) \
     ((z < 0) || ((z == 0) && ((b.y > a.y) || ((b.y == a.y) && (b.x < a.x)))))
 
-void gpu_render_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, poly_data_t data, int edge) {
-    vertex_t a, b, c, p;
+void gpu_render_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, poly_data_t data, int edge) {
+    USED(edge);
+    vertex_t a, b, c, p;
 
     int tpx = (data.texp & 0xf) << 6;
     int tpy = (data.texp & 0x10) << 4;
@@ -293,8 +292,8 @@
             if (TL(z2, a, b))
                 continue;
 
-            uint16_t color = 0;
-            uint32_t mod   = 0;
+            uint16_t color;
+            uint32_t mod;
 
             if (data.attrib & PA_SHADED) {
                 float cr = (z0 * ((a.c >>  0) & 0xff) + z1 * ((b.c >>  0) & 0xff) + z2 * ((c.c >>  0) & 0xff)) / area;
@@ -424,25 +423,17 @@
 
 #define CLAMP(v, d, u) ((v) <= (d)) ? (d) : (((v) >= (u)) ? (u) : (v))
 
-void gpu_render_rect(psx_gpu_t* gpu, rect_data_t data) {
+void gpu_render_rect(psx_gpu_t* gpu, rect_data_t data) {
+    uint16_t width, height;
+
+    switch ((data.attrib >> 3) & 3) {
+        case RS_VARIABLE: { width = data.width; height = data.height; } break;
+        case RS_1X1     : { width = 1         ; height = 1          ; } break;
+        case RS_8X8     : { width = 8         ; height = 8          ; } break;
+        case RS_16X16   : { width = 16        ; height = 16         ; } break;
+        default         : { width = 0         ; height = 0          ; } break;
+    }
 
-#if 0 //PATCH2:Fix clipping bug.
-    if ((data.v0.x >= 1024) || (data.v0.y >= 512))
-        return;
-
-    if ((data.v0.x <= -1024) || (data.v0.y <= -512))
-        return;
-#endif
-
-    uint16_t width, height;
-
-    switch ((data.attrib >> 3) & 3) {
-        case RS_VARIABLE: { width = data.width; height = data.height; } break;
-        case RS_1X1     : { width = 1         ; height = 1          ; } break;
-        case RS_8X8     : { width = 8         ; height = 8          ; } break;
-        case RS_16X16   : { width = 16        ; height = 16         ; } break;
-    }
-
     int textured = (data.attrib & RA_TEXTURED) != 0;
     int transp = (data.attrib & RA_TRANSP) != 0;
     int transp_mode = (gpu->gpustat >> 5) & 3;
@@ -450,14 +441,12 @@
     int clutx = (data.clut & 0x3f) << 4;
     int cluty = (data.clut >> 6) & 0x1ff;
 
-    /* Offset coordinates */
-    data.v0.x += gpu->off_x;
-    data.v0.y += gpu->off_y;
-#if 1 //PATCH2:Fix clipping bug.
-    data.v0.x = SE10(data.v0.x);
-    data.v0.y = SE10(data.v0.y);
-#endif
-    /* Calculate bounding box */
+    /* Offset coordinates */
+    data.v0.x += gpu->off_x;
+    data.v0.y += gpu->off_y;
+    data.v0.x = SE10(data.v0.x);
+    data.v0.y = SE10(data.v0.y);
+    /* Calculate bounding box */
     int xmax = data.v0.x + width;
     int ymax = data.v0.y + height;
 
@@ -683,8 +672,9 @@
     }
 }
 
-void gpu_render_textured_rectangle(psx_gpu_t* gpu, vertex_t v, uint32_t w, uint32_t h, uint16_t clutx, uint16_t cluty, uint32_t color) {
-    vertex_t a = v;
+void gpu_render_textured_rectangle(psx_gpu_t* gpu, vertex_t v, uint32_t w, uint32_t h, uint16_t clutx, uint16_t cluty, uint32_t color) {
+    USED(color);
+    vertex_t a = v;
 
     a.x += gpu->off_x;
     a.y += gpu->off_y;
@@ -1138,14 +1128,11 @@
 
             ++gpu->xcnt;
             
-            if (gpu->xcnt == gpu->xsiz) {
-                ++gpu->ycnt;
-                gpu->xcnt = 0;
+            if (gpu->xcnt == gpu->xsiz) {
+                ++gpu->ycnt;
+                gpu->xcnt = 0;
+            }
 
-                xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
-                ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
-            }
-
             gpu->tsiz -= 2;
 
             if (!gpu->tsiz) {
@@ -1873,22 +1860,24 @@
                 } break;
             }
 
-            log_error("GP1(%02Xh) args=%06x", value >> 24, value & 0xffffff);
+            print("GP1(%02Xh) args=%06x\n", value >> 24, value & 0xffffff);
 
             return;
         } break;
     }
 
-    log_warn("Unhandled 32-bit GPU write at offset %08x (%08x)", offset, value);
-}
-
-void psx_gpu_write16(psx_gpu_t* gpu, uint32_t offset, uint16_t value) {
-    printf("Unhandled 16-bit GPU write at offset %08x (%04x)\n", offset, value);
-}
-
-void psx_gpu_write8(psx_gpu_t* gpu, uint32_t offset, uint8_t value) {
-    printf("Unhandled 8-bit GPU write at offset %08x (%02x)\n", offset, value);
-}
+    print("Unhandled 32-bit GPU write at offset %08x (%08x)\n", offset, value);
+}
+
+void psx_gpu_write16(psx_gpu_t* gpu, uint32_t offset, uint16_t value) {
+    USED(gpu);
+    printf("Unhandled 16-bit GPU write at offset %08x (%04x)\n", offset, value);
+}
+
+void psx_gpu_write8(psx_gpu_t* gpu, uint32_t offset, uint8_t value) {
+    USED(gpu);
+    printf("Unhandled 8-bit GPU write at offset %08x (%02x)\n", offset, value);
+}
 
 void psx_gpu_set_event_callback(psx_gpu_t* gpu, int event, psx_gpu_event_callback_t cb) {
     gpu->event_cb_table[event] = cb;
--- a/psx/dev/gpu.h
+++ b/psx/dev/gpu.h
@@ -1,11 +1,9 @@
-#ifndef GPU_H
-#define GPU_H
+#ifndef PSX_DEV_GPU_H
+#define PSX_DEV_GPU_H
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "ic.h"
+#include "dev/ic.h"
 
 #define PSX_GPU_BEGIN 0x1f801810
 #define PSX_GPU_SIZE  0x8
--- a/psx/dev/ic.c
+++ b/psx/dev/ic.c
@@ -1,10 +1,8 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "ic.h"
+#include "dev/ic.h"
 
-#include "../log.h"
+#include "log.h"
 
 psx_ic_t* psx_ic_create(void) {
     return (psx_ic_t*)malloc(sizeof(psx_ic_t));
--- a/psx/dev/ic.h
+++ b/psx/dev/ic.h
@@ -1,9 +1,9 @@
-#ifndef IC_H
-#define IC_H
+#ifndef PSX_DEV_IC_H
+#define PSX_DEV_IC_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "../cpu.h"
+#include "cpu.h"
 
 #define PSX_IC_BEGIN 0x1f801070
 #define PSX_IC_SIZE  0x8
@@ -43,16 +43,18 @@
     1F801074h 2    I_MASK - Interrupt mask register
 */
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_ic_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    uint16_t stat;
+    uint16_t mask;
+
+    psx_cpu_t* cpu;
+};
+
+typedef struct psx_ic_t psx_ic_t;
 
-    uint16_t stat;
-    uint16_t mask;
-
-    psx_cpu_t* cpu;
-} psx_ic_t;
-
 psx_ic_t* psx_ic_create(void);
 void psx_ic_init(psx_ic_t*, psx_cpu_t*);
 uint32_t psx_ic_read32(psx_ic_t*, uint32_t);
@@ -64,4 +66,4 @@
 void psx_ic_irq(psx_ic_t*, int);
 void psx_ic_destroy(psx_ic_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/input.c
+++ b/psx/dev/input.c
@@ -1,11 +1,7 @@
-#ifndef PAD_H
-#define PAD_H
+#include "dev/input.h"
+
+#include "p9.h"
 
-#include "input.h"
-
-#include <string.h>
-#include <stdlib.h>
-
 psx_input_t* psx_input_create(void) {
     return (psx_input_t*)malloc(sizeof(psx_input_t));
 }
@@ -30,13 +26,15 @@
     input->on_button_release_func = on_button_release_func;
 }
 
-void psx_input_set_on_analog_change_func(psx_input_t* input, psx_input_on_analog_change_t on_analog_change_func) {
-    input->on_analog_change_func = on_analog_change_func;
-}
-
-void psx_input_destroy(psx_input_t* input) {
-    free(input->udata);
-    free(input);
-}
-
-#endif
\ No newline at end of file
+void psx_input_set_on_analog_change_func(psx_input_t* input, psx_input_on_analog_change_t on_analog_change_func) {
+    input->on_analog_change_func = on_analog_change_func;
+}
+
+void psx_input_set_query_fifo_func(psx_input_t* input, psx_input_query_fifo_t query_fifo_func) {
+    input->query_fifo_func = query_fifo_func;
+}
+
+void psx_input_destroy(psx_input_t* input) {
+    free(input->udata);
+    free(input);
+}
--- a/psx/dev/input.h
+++ b/psx/dev/input.h
@@ -1,7 +1,7 @@
-#ifndef INPUT_H
-#define INPUT_H
+#ifndef PSX_DEV_INPUT_H
+#define PSX_DEV_INPUT_H
 
-#include <stdint.h>
+#include "p9.h"
 
 struct psx_input_t;
 
--- a/psx/dev/mc1.c
+++ b/psx/dev/mc1.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "mc1.h"
-#include "../log.h"
+#include "dev/mc1.h"
+#include "log.h"
 
 /*
   0-3   Write Delay        (00h..0Fh=01h..10h Cycles)
@@ -24,7 +22,14 @@
   31    Wait               (1=wait on external device before being ready)
 */
 
-#define DEFAULT_DLY 2
+static uint32_t
+mc1_default_delay(psx_mc1_t* mc1)
+{
+    USED(mc1);
+    return 2;
+}
+
+#define DEFAULT_DLY mc1_default_delay(mc1)
 
 // #define WRITE_DLY(dev) ((mc1-> ## dev ## _delay & 0xf) + 1)
 // #define READ_DLY(dev) (((mc1-> ## dev ## _delay >> 4) & 0xf) + 1)
@@ -91,14 +96,16 @@
     return 0x0;
 }
 
-uint16_t psx_mc1_read16(psx_mc1_t* mc1, uint32_t offset) {
-    log_warn("Unhandled 16-bit MC1 read at offset %08x", offset);
+uint16_t psx_mc1_read16(psx_mc1_t* mc1, uint32_t offset) {
+    USED(mc1);
+    log_warn("Unhandled 16-bit MC1 read at offset %08x", offset);
 
     return 0x0;
 }
 
-uint8_t psx_mc1_read8(psx_mc1_t* mc1, uint32_t offset) {
-    log_warn("Unhandled 8-bit MC1 read at offset %08x", offset);
+uint8_t psx_mc1_read8(psx_mc1_t* mc1, uint32_t offset) {
+    USED(mc1);
+    log_warn("Unhandled 8-bit MC1 read at offset %08x", offset);
 
     return 0x0;
 }
@@ -121,14 +128,16 @@
     }
 }
 
-void psx_mc1_write16(psx_mc1_t* mc1, uint32_t offset, uint16_t value) {
-    log_warn("Unhandled 16-bit MC1 write at offset %08x (%04x)", offset, value);
-}
+void psx_mc1_write16(psx_mc1_t* mc1, uint32_t offset, uint16_t value) {
+    USED(mc1);
+    log_warn("Unhandled 16-bit MC1 write at offset %08x (%04x)", offset, value);
+}
+
+void psx_mc1_write8(psx_mc1_t* mc1, uint32_t offset, uint8_t value) {
+    USED(mc1);
+    log_warn("Unhandled 8-bit MC1 write at offset %08x (%02x)", offset, value);
+}
 
-void psx_mc1_write8(psx_mc1_t* mc1, uint32_t offset, uint8_t value) {
-    log_warn("Unhandled 8-bit MC1 write at offset %08x (%02x)", offset, value);
-}
-
 uint32_t psx_mc1_get_bios_read_delay(psx_mc1_t* mc1) {
     return DEFAULT_DLY;
 }
@@ -161,9 +170,10 @@
     return DEFAULT_DLY;
 }
 
-uint32_t psx_mc1_get_scratchpad_read_delay(psx_mc1_t* mc1) {
-    return 1;
-}
+uint32_t psx_mc1_get_scratchpad_read_delay(psx_mc1_t* mc1) {
+    USED(mc1);
+    return 1;
+}
 
 uint32_t psx_mc1_get_gpu_read_delay(psx_mc1_t* mc1) {
     return DEFAULT_DLY;
@@ -217,9 +227,10 @@
     return DEFAULT_DLY;
 }
 
-uint32_t psx_mc1_get_scratchpad_write_delay(psx_mc1_t* mc1) {
-    return 1;
-}
+uint32_t psx_mc1_get_scratchpad_write_delay(psx_mc1_t* mc1) {
+    USED(mc1);
+    return 1;
+}
 
 uint32_t psx_mc1_get_gpu_write_delay(psx_mc1_t* mc1) {
     return DEFAULT_DLY;
@@ -243,4 +254,4 @@
 
 void psx_mc1_destroy(psx_mc1_t* mc1) {
     free(mc1);
-}
\ No newline at end of file
+}
--- a/psx/dev/mc1.h
+++ b/psx/dev/mc1.h
@@ -1,15 +1,15 @@
-#ifndef MC1_H
-#define MC1_H
+#ifndef PSX_DEV_MC1_H
+#define PSX_DEV_MC1_H
 
-#include <stdint.h>
+#include "p9.h"
 
 #define PSX_MC1_BEGIN 0x1f801000
 #define PSX_MC1_SIZE  0x24
 #define PSX_MC1_END   0x1f801023
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_mc1_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
     uint32_t exp1_base;
     uint32_t exp2_base;
@@ -16,11 +16,13 @@
     uint32_t exp1_delay;
     uint32_t exp3_delay;
     uint32_t bios_delay;
-    uint32_t spu_delay;
-    uint32_t cdrom_delay;
-    uint32_t exp2_delay;
-    uint32_t com_delay;
-} psx_mc1_t;
+    uint32_t spu_delay;
+    uint32_t cdrom_delay;
+    uint32_t exp2_delay;
+    uint32_t com_delay;
+};
+
+typedef struct psx_mc1_t psx_mc1_t;
 
 typedef struct {
     int fst;
@@ -66,4 +68,4 @@
 uint32_t psx_mc1_get_cdrom_write_delay(psx_mc1_t*);
 uint32_t psx_mc1_get_pad_write_delay(psx_mc1_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/mc2.c
+++ b/psx/dev/mc2.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "mc2.h"
-#include "../log.h"
+#include "dev/mc2.h"
+#include "log.h"
 
 psx_mc2_t* psx_mc2_create(void) {
     return (psx_mc2_t*)malloc(sizeof(psx_mc2_t));
@@ -31,14 +29,16 @@
     return 0x0;
 }
 
-uint16_t psx_mc2_read16(psx_mc2_t* mc2, uint32_t offset) {
-    log_warn("Unhandled 16-bit MC2 read at offset %08x", offset);
+uint16_t psx_mc2_read16(psx_mc2_t* mc2, uint32_t offset) {
+    USED(mc2);
+    log_warn("Unhandled 16-bit MC2 read at offset %08x", offset);
 
     return 0x0;
 }
 
-uint8_t psx_mc2_read8(psx_mc2_t* mc2, uint32_t offset) {
-    log_warn("Unhandled 8-bit MC2 read at offset %08x", offset);
+uint8_t psx_mc2_read8(psx_mc2_t* mc2, uint32_t offset) {
+    USED(mc2);
+    log_warn("Unhandled 8-bit MC2 read at offset %08x", offset);
 
     return 0x0;
 }
@@ -53,14 +53,16 @@
     }
 }
 
-void psx_mc2_write16(psx_mc2_t* mc2, uint32_t offset, uint16_t value) {
-    log_warn("Unhandled 16-bit MC2 write at offset %08x (%04x)", offset, value);
-}
+void psx_mc2_write16(psx_mc2_t* mc2, uint32_t offset, uint16_t value) {
+    USED(mc2);
+    log_warn("Unhandled 16-bit MC2 write at offset %08x (%04x)", offset, value);
+}
+
+void psx_mc2_write8(psx_mc2_t* mc2, uint32_t offset, uint8_t value) {
+    USED(mc2);
+    log_warn("Unhandled 8-bit MC2 write at offset %08x (%02x)", offset, value);
+}
 
-void psx_mc2_write8(psx_mc2_t* mc2, uint32_t offset, uint8_t value) {
-    log_warn("Unhandled 8-bit MC2 write at offset %08x (%02x)", offset, value);
-}
-
 void psx_mc2_destroy(psx_mc2_t* mc2) {
     free(mc2);
-}
\ No newline at end of file
+}
--- a/psx/dev/mc2.h
+++ b/psx/dev/mc2.h
@@ -1,19 +1,21 @@
-#ifndef MC2_H
-#define MC2_H
+#ifndef PSX_DEV_MC2_H
+#define PSX_DEV_MC2_H
 
-#include <stdint.h>
+#include "p9.h"
 
 #define PSX_MC2_BEGIN 0x1f801060
 #define PSX_MC2_SIZE  0x4
 #define PSX_MC2_END   0x1F801063
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_mc2_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    uint32_t ram_size;
+};
+
+typedef struct psx_mc2_t psx_mc2_t;
 
-    uint32_t ram_size;
-} psx_mc2_t;
-
 psx_mc2_t* psx_mc2_create(void);
 void psx_mc2_init(psx_mc2_t*);
 uint32_t psx_mc2_read32(psx_mc2_t*, uint32_t);
@@ -24,4 +26,4 @@
 void psx_mc2_write8(psx_mc2_t*, uint32_t, uint8_t);
 void psx_mc2_destroy(psx_mc2_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/mc3.c
+++ b/psx/dev/mc3.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "mc3.h"
-#include "../log.h"
+#include "dev/mc3.h"
+#include "log.h"
 
 psx_mc3_t* psx_mc3_create(void) {
     return (psx_mc3_t*)malloc(sizeof(psx_mc3_t));
@@ -29,14 +27,16 @@
     return 0x0;
 }
 
-uint16_t psx_mc3_read16(psx_mc3_t* mc3, uint32_t offset) {
-    log_warn("Unhandled 16-bit MC3 read at offset %08x", offset);
+uint16_t psx_mc3_read16(psx_mc3_t* mc3, uint32_t offset) {
+    USED(mc3);
+    log_warn("Unhandled 16-bit MC3 read at offset %08x", offset);
 
     return 0x0;
 }
 
-uint8_t psx_mc3_read8(psx_mc3_t* mc3, uint32_t offset) {
-    log_warn("Unhandled 8-bit MC3 read at offset %08x", offset);
+uint8_t psx_mc3_read8(psx_mc3_t* mc3, uint32_t offset) {
+    USED(mc3);
+    log_warn("Unhandled 8-bit MC3 read at offset %08x", offset);
 
     return 0x0;
 }
@@ -51,14 +51,16 @@
     }
 }
 
-void psx_mc3_write16(psx_mc3_t* mc3, uint32_t offset, uint16_t value) {
-    log_warn("Unhandled 16-bit MC3 write at offset %08x (%04x)", offset, value);
-}
+void psx_mc3_write16(psx_mc3_t* mc3, uint32_t offset, uint16_t value) {
+    USED(mc3);
+    log_warn("Unhandled 16-bit MC3 write at offset %08x (%04x)", offset, value);
+}
+
+void psx_mc3_write8(psx_mc3_t* mc3, uint32_t offset, uint8_t value) {
+    USED(mc3);
+    log_warn("Unhandled 8-bit MC3 write at offset %08x (%02x)", offset, value);
+}
 
-void psx_mc3_write8(psx_mc3_t* mc3, uint32_t offset, uint8_t value) {
-    log_warn("Unhandled 8-bit MC3 write at offset %08x (%02x)", offset, value);
-}
-
 void psx_mc3_destroy(psx_mc3_t* mc3) {
     free(mc3);
-}
\ No newline at end of file
+}
--- a/psx/dev/mc3.h
+++ b/psx/dev/mc3.h
@@ -1,19 +1,21 @@
-#ifndef MC3_H
-#define MC3_H
+#ifndef PSX_DEV_MC3_H
+#define PSX_DEV_MC3_H
 
-#include <stdint.h>
+#include "p9.h"
 
 #define PSX_MC3_BEGIN 0xfffe0130
 #define PSX_MC3_SIZE  0x4
 #define PSX_MC3_END   0xfffe0133
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_mc3_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    uint32_t cache_control;
+};
+
+typedef struct psx_mc3_t psx_mc3_t;
 
-    uint32_t cache_control;
-} psx_mc3_t;
-
 psx_mc3_t* psx_mc3_create(void);
 void psx_mc3_init(psx_mc3_t*);
 uint32_t psx_mc3_read32(psx_mc3_t*, uint32_t);
@@ -24,4 +26,4 @@
 void psx_mc3_write8(psx_mc3_t*, uint32_t, uint8_t);
 void psx_mc3_destroy(psx_mc3_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/mcd.c
+++ b/psx/dev/mcd.c
@@ -1,5 +1,5 @@
-#include "mcd.h"
-#include "../log.h"
+#include "dev/mcd.h"
+#include "log.h"
 
 #define PATCH1   1  // 1=Fix memory card access.
 
@@ -36,35 +36,31 @@
 }
 
 uint8_t psx_mcd_read(psx_mcd_t* mcd) {
-    switch (mcd->state) {
-        case MCD_STATE_TX_HIZ: mcd->tx_data = 0xff; break;
-        case MCD_STATE_TX_FLG: 
-#if PATCH1
-			if((mcd->rx_data == 0x81) ||(mcd->rx_data == 0x01) ) {
-	            mcd->tx_data_ready = 1;
-	            mcd->tx_data = 0xff;
-	            mcd->state = MCD_STATE_TX_HIZ;
-	            break;
-			}
-#endif
-        	mcd->tx_data = mcd->flag; mcd->flag = 0x00; break;
-        case MCD_STATE_TX_ID1: mcd->tx_data = 0x5a; break;
-        case MCD_STATE_TX_ID2: {
+    switch (mcd->state) {
+        case MCD_STATE_TX_HIZ: mcd->tx_data = 0xff; break;
+        case MCD_STATE_TX_FLG: 
+            if ((mcd->rx_data == 0x81) || (mcd->rx_data == 0x01)) {
+                mcd->tx_data_ready = 1;
+                mcd->tx_data = 0xff;
+                mcd->state = MCD_STATE_TX_HIZ;
+                break;
+            }
+        	mcd->tx_data = mcd->flag; mcd->flag = 0x00; break;
+        case MCD_STATE_TX_ID1: mcd->tx_data = 0x5a; break;
+        case MCD_STATE_TX_ID2: {
             mcd->tx_data_ready = 1;
             mcd->tx_data = 0x5d;
 
-            switch (mcd->mode) {
-                case 'R': mcd->state = MCD_R_STATE_RX_MSB; break;
-                case 'W': mcd->state = MCD_W_STATE_RX_MSB; break;
-                case 'S': mcd->state = MCD_S_STATE_TX_ACK1; break;
-#if PATCH1
-				default:
-		            mcd->tx_data_ready = 0;
-		            mcd->tx_data = 0xff;
-		            mcd->state = MCD_STATE_TX_HIZ;
-				    return mcd->tx_data;
-#endif
-            }
+            switch (mcd->mode) {
+                case 'R': mcd->state = MCD_R_STATE_RX_MSB; break;
+                case 'W': mcd->state = MCD_W_STATE_RX_MSB; break;
+                case 'S': mcd->state = MCD_S_STATE_TX_ACK1; break;
+                default:
+                    mcd->tx_data_ready = 0;
+                    mcd->tx_data = 0xff;
+                    mcd->state = MCD_STATE_TX_HIZ;
+                    return mcd->tx_data;
+            }
 
             // printf("mcd read %02x\n", mcd->tx_data);
 
@@ -118,16 +114,12 @@
             return 'G';
         } break;
 
-        /* Write states */
-        case MCD_W_STATE_RX_MSB: mcd->tx_data = 0x00; break;
-        case MCD_W_STATE_RX_LSB: mcd->tx_data = mcd->msb;
-#if PATCH1
-                                 mcd->pending_bytes = 128; break;
-#else
-                                 mcd->pending_bytes = 127; break;
-#endif
-        case MCD_W_STATE_RX_DATA: {
-             --mcd->pending_bytes;
+        /* Write states */
+        case MCD_W_STATE_RX_MSB: mcd->tx_data = 0x00; break;
+        case MCD_W_STATE_RX_LSB: mcd->tx_data = mcd->msb;
+                                 mcd->pending_bytes = 128; break;
+        case MCD_W_STATE_RX_DATA: {
+             --mcd->pending_bytes;
 
             mcd->buf[mcd->addr++] = mcd->rx_data;
 
@@ -143,14 +135,12 @@
             // log_fatal("mcd read %02x", mcd->rx_data);
             // log_set_quiet(1);
 
-            return mcd->rx_data;
-        } break;
-        case MCD_W_STATE_RX_CHK: mcd->tx_data = mcd->rx_data; break;
-#if PATCH1
-        case MCD_W_STATE_RX_CHK2: mcd->tx_data = mcd->rx_data; break;
-#endif
-        case MCD_W_STATE_TX_ACK1: mcd->tx_data = 0x5c; break;
-        case MCD_W_STATE_TX_ACK2: mcd->tx_data = 0x5d; break;
+            return mcd->rx_data;
+        } break;
+        case MCD_W_STATE_RX_CHK: mcd->tx_data = mcd->rx_data; break;
+        case MCD_W_STATE_RX_CHK2: mcd->tx_data = mcd->rx_data; break;
+        case MCD_W_STATE_TX_ACK1: mcd->tx_data = 0x5c; break;
+        case MCD_W_STATE_TX_ACK2: mcd->tx_data = 0x5d; break;
         case MCD_W_STATE_TX_MEB: {
             mcd->tx_data_ready = 0;
             mcd->state = MCD_STATE_TX_HIZ;
@@ -177,14 +167,12 @@
     return mcd->tx_data;
 }
 
-void psx_mcd_write(psx_mcd_t* mcd, uint8_t data) {
+void psx_mcd_write(psx_mcd_t* mcd, uint8_t data) {
     // log_set_quiet(0);
     // log_fatal("mcd write %02x", data);
     // log_set_quiet(1);
 
-#if PATCH1
-	mcd->rx_data = data;
-#endif
+    mcd->rx_data = data;
 //  printf("mcd write %02x\n", data);
 
     switch (mcd->state) {
--- a/psx/dev/mcd.h
+++ b/psx/dev/mcd.h
@@ -1,10 +1,7 @@
-#ifndef MCD_H
-#define MCD_H
+#ifndef PSX_DEV_MCD_H
+#define PSX_DEV_MCD_H
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
+#include "p9.h"
 
 #define MCD_MEMORY_SIZE 0x20000 // 128 KB
 
@@ -22,14 +19,12 @@
     MCD_R_STATE_TX_DATA,
     MCD_R_STATE_TX_CHK,
     MCD_R_STATE_TX_MEB,
-    MCD_W_STATE_RX_MSB,
-    MCD_W_STATE_RX_LSB,
-    MCD_W_STATE_RX_DATA,
-    MCD_W_STATE_RX_CHK,
-#if 1  // add.
-    MCD_W_STATE_RX_CHK2,
-#endif
-    MCD_W_STATE_TX_ACK1,
+    MCD_W_STATE_RX_MSB,
+    MCD_W_STATE_RX_LSB,
+    MCD_W_STATE_RX_DATA,
+    MCD_W_STATE_RX_CHK,
+    MCD_W_STATE_RX_CHK2,
+    MCD_W_STATE_TX_ACK1,
     MCD_W_STATE_TX_ACK2,
     MCD_W_STATE_TX_MEB,
     MCD_S_STATE_TX_ACK1,
@@ -40,10 +35,10 @@
     MCD_S_STATE_TX_DAT3
 };
 
-typedef struct {
-    const char* path;
-    uint8_t* buf;
-    uint8_t flag;
+struct psx_mcd_t {
+    const char* path;
+    uint8_t* buf;
+    uint8_t flag;
     uint16_t msb;
     uint16_t lsb;
     uint16_t addr;
@@ -50,11 +45,13 @@
     uint8_t rx_data;
     int pending_bytes;
     char mode;
-    int state;
-    uint8_t tx_data;
-    int tx_data_ready;
-    uint8_t checksum;
-} psx_mcd_t;
+    int state;
+    uint8_t tx_data;
+    int tx_data_ready;
+    uint8_t checksum;
+};
+
+typedef struct psx_mcd_t psx_mcd_t;
 
 psx_mcd_t* psx_mcd_create(void);
 int psx_mcd_init(psx_mcd_t*, const char*);
@@ -64,4 +61,4 @@
 void psx_mcd_reset(psx_mcd_t*);
 void psx_mcd_destroy(psx_mcd_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/mdec.c
+++ b/psx/dev/mdec.c
@@ -1,9 +1,7 @@
-#include "mdec.h"
-#include "../log.h"
+#include "dev/mdec.h"
+#include "log.h"
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
+#include "p9.h"
 
 int zigzag[] = {
     0 , 1 , 5 , 6 , 14, 15, 27, 28,
@@ -175,7 +173,9 @@
     }
 }
 
-void mdec_nop(psx_mdec_t* mdec) { /* Do nothing */ }
+void mdec_nop(psx_mdec_t* mdec) {
+    USED(mdec);
+}
 
 void mdec_decode_macroblock(psx_mdec_t* mdec) {
     if (mdec->output_depth < 2) {
@@ -333,18 +333,22 @@
     return 0x0;
 }
 
-uint16_t psx_mdec_read16(psx_mdec_t* mdec, uint32_t offset) {
-    printf("Unhandled 16-bit MDEC read offset=%u\n", offset);
+uint16_t psx_mdec_read16(psx_mdec_t* mdec, uint32_t offset) {
+    USED(mdec);
+    printf("Unhandled 16-bit MDEC read offset=%08x\n", offset);
+
+    exit(1);
+    return 0;
+}
+
+uint8_t psx_mdec_read8(psx_mdec_t* mdec, uint32_t offset) {
+    USED(mdec);
+    printf("Unhandled 8-bit MDEC read offset=%08x\n", offset);
+
+    exit(1);
+    return 0;
+}
 
-    exit(1);
-}
-
-uint8_t psx_mdec_read8(psx_mdec_t* mdec, uint32_t offset) {
-    printf("Unhandled 8-bit MDEC read offset=%u\n", offset);
-
-    exit(1);
-}
-
 void psx_mdec_write32(psx_mdec_t* mdec, uint32_t offset, uint32_t value) {
     switch (offset) {
         case 0: {
@@ -457,17 +461,19 @@
     // log_fatal("32-bit MDEC write offset=%u, value=%08x", offset, value);
 }
 
-void psx_mdec_write16(psx_mdec_t* mdec, uint32_t offset, uint16_t value) {
-    printf("Unhandled 16-bit MDEC write offset=%u, value=%04x\n", offset, value);
-}
+void psx_mdec_write16(psx_mdec_t* mdec, uint32_t offset, uint16_t value) {
+    USED(mdec);
+    printf("Unhandled 16-bit MDEC write offset=%08x, value=%04x\n", offset, value);
+}
+
+void psx_mdec_write8(psx_mdec_t* mdec, uint32_t offset, uint8_t value) {
+    USED(mdec);
+    printf("Unhandled 8-bit MDEC write offset=%08x, value=%02x\n", offset, value);
+}
 
-void psx_mdec_write8(psx_mdec_t* mdec, uint32_t offset, uint8_t value) {
-    printf("Unhandled 8-bit MDEC write offset=%u, value=%02x\n", offset, value);
-}
-
 void psx_mdec_destroy(psx_mdec_t* mdec) {
     free(mdec);
 }
 
 #undef CLAMP
-#undef MAX
\ No newline at end of file
+#undef MAX
--- a/psx/dev/mdec.h
+++ b/psx/dev/mdec.h
@@ -1,9 +1,9 @@
-#ifndef MDEC_H
-#define MDEC_H
+#ifndef PSX_DEV_MDEC_H
+#define PSX_DEV_MDEC_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "../log.h"
+#include "log.h"
 
 #define PSX_MDEC_SIZE    0x8
 #define PSX_MDEC_BEGIN   0x1f801820
@@ -41,9 +41,9 @@
     MDEC_CMD_SET_ST
 };
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_mdec_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
     uint32_t cmd;
 
@@ -80,11 +80,13 @@
 
     int16_t yblk[64];
     int16_t crblk[64];
-    int16_t cbblk[64];
+    int16_t cbblk[64];
+
+    uint32_t status;
+};
+
+typedef struct psx_mdec_t psx_mdec_t;
 
-    uint32_t status;
-} psx_mdec_t;
-
 psx_mdec_t* psx_mdec_create(void);
 void psx_mdec_init(psx_mdec_t*);
 uint32_t psx_mdec_read32(psx_mdec_t*, uint32_t);
@@ -97,4 +99,4 @@
 
 typedef void (*mdec_fn_t)(psx_mdec_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/pad.c
+++ b/psx/dev/pad.c
@@ -1,10 +1,9 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
+
+#include "dev/pad.h"
+#include "dev/mcd.h"
+#include "log.h"
 
-#include "pad.h"
-#include "../log.h"
-
 #define JOY_IRQ_DELAY 512
 
 uint32_t pad_read_rx(psx_pad_t* pad) {
@@ -174,7 +173,7 @@
     return v;
 }
 
-uint16_t psx_pad_read16(psx_pad_t* pad, uint32_t offset) {
+uint16_t psx_pad_read16(psx_pad_t* pad, uint32_t offset) {
     uint32_t v = 0;
 
     switch (offset) {
@@ -183,15 +182,11 @@
         case 8: v = pad->mode; break;
         case 10: v = pad->ctrl & 0xffff; break;
         case 14: v = pad->baud; break;
-    }
+    }
+
+    return v;
+}
 
-    return v;
-
-    printf("Unhandled 16-bit PAD read at offset %08x", offset);
-
-    return 0x0;
-}
-
 uint8_t psx_pad_read8(psx_pad_t* pad, uint32_t offset) {
     uint32_t v = 0;
 
@@ -292,16 +287,10 @@
     pad->joy_slot[slot] = NULL;
 }
 
-int psx_pad_attach_mcd(psx_pad_t* pad, int slot, const char* path) {
+int psx_pad_attach_mcd(psx_pad_t* pad, int slot, const char* path) {
+    if (pad->mcd_slot[slot])
+        psx_pad_detach_mcd(pad, slot);
 
-#if 0
-    printf("Memory Card support is disabled\n");
-
-    return 0;
-#endif
-    if (pad->mcd_slot[slot])
-        psx_pad_detach_mcd(pad, slot);
-
     psx_mcd_t* mcd = psx_mcd_create();
 
     int r = psx_mcd_init(mcd, path);
@@ -350,4 +339,4 @@
     psx_pad_detach_mcd(pad, 1);
 
     free(pad);
-}
\ No newline at end of file
+}
--- a/psx/dev/pad.h
+++ b/psx/dev/pad.h
@@ -1,14 +1,15 @@
-#ifndef PAD_H
-#define PAD_H
+#ifndef PSX_DEV_PAD_H
+#define PSX_DEV_PAD_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "ic.h"
-#include "input.h"
-#include "mcd.h"
+#include "dev/ic.h"
+#include "dev/input.h"
+
+#include "input/sda.h"
+
+typedef struct psx_mcd_t psx_mcd_t;
 
-#include "../input/sda.h"
-
 #define PSX_PAD_BEGIN 0x1f801040
 #define PSX_PAD_SIZE  0x10
 #define PSX_PAD_END   0x1f80104f
@@ -103,9 +104,9 @@
     - _on_analog_change(id)
 */
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_pad_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
     psx_ic_t* ic;
     psx_input_t* joy_slot[2];
@@ -115,11 +116,13 @@
     int cycles_until_irq;
     int cycle_counter;
     int dest[2];
-    int irq_bit;
+    int irq_bit;
+
+    uint16_t mode, ctrl, baud, stat;
+};
+
+typedef struct psx_pad_t psx_pad_t;
 
-    uint16_t mode, ctrl, baud, stat;
-} psx_pad_t;
-
 psx_pad_t* psx_pad_create(void);
 void psx_pad_init(psx_pad_t*, psx_ic_t*);
 uint32_t psx_pad_read32(psx_pad_t*, uint32_t);
@@ -138,4 +141,4 @@
 void psx_pad_detach_mcd(psx_pad_t*, int);
 void psx_pad_update(psx_pad_t*, int);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/ram.c
+++ b/psx/dev/ram.c
@@ -1,10 +1,9 @@
-#include "ram.h"
-#include "../log.h"
+#include "dev/ram.h"
+#include "log.h"
+#include "dev/mc2.h"
+
+#include "p9.h"
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
 psx_ram_t* psx_ram_create(void) {
     return (psx_ram_t*)malloc(sizeof(psx_ram_t));
 }
@@ -77,4 +76,4 @@
 void psx_ram_destroy(psx_ram_t* ram) {
     free(ram->buf);
     free(ram);
-}
\ No newline at end of file
+}
--- a/psx/dev/ram.h
+++ b/psx/dev/ram.h
@@ -1,11 +1,12 @@
-#ifndef RAM_H
-#define RAM_H
+#ifndef PSX_DEV_RAM_H
+#define PSX_DEV_RAM_H
 
-#include <stdint.h>
+#include "p9.h"
+
+#include "log.h"
+
+typedef struct psx_mc2_t psx_mc2_t;
 
-#include "../log.h"
-#include "mc2.h"
-
 #define PSX_RAM_SIZE    0x800000 // 8MB window
 #define PSX_RAM_BEGIN   0x00000000
 //#define PSX_RAM_END     0x001fffff
@@ -16,17 +17,19 @@
 #define RAM_SIZE_4MB 0x400000
 #define RAM_SIZE_8MB 0x800000
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_ram_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
-    size_t size;
+    uintptr size;
 
-    psx_mc2_t* mc2;
+    psx_mc2_t* mc2;
+
+    uint8_t* buf;
+};
+
+typedef struct psx_ram_t psx_ram_t;
 
-    uint8_t* buf;
-} psx_ram_t;
-
 psx_ram_t* psx_ram_create(void);
 void psx_ram_init(psx_ram_t*, psx_mc2_t*, int size);
 uint32_t psx_ram_read32(psx_ram_t*, uint32_t);
@@ -37,4 +40,4 @@
 void psx_ram_write8(psx_ram_t*, uint32_t, uint8_t);
 void psx_ram_destroy(psx_ram_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/scratchpad.c
+++ b/psx/dev/scratchpad.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "../log.h"
-#include "scratchpad.h"
+#include "log.h"
+#include "dev/scratchpad.h"
 
 psx_scratchpad_t* psx_scratchpad_create(void) {
     return (psx_scratchpad_t*)malloc(sizeof(psx_scratchpad_t));
--- a/psx/dev/scratchpad.h
+++ b/psx/dev/scratchpad.h
@@ -1,21 +1,23 @@
-#ifndef SCRATCHPAD_H
-#define SCRATCHPAD_H
+#ifndef PSX_DEV_SCRATCHPAD_H
+#define PSX_DEV_SCRATCHPAD_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "mc1.h"
+#include "dev/mc1.h"
 
 #define PSX_SCRATCHPAD_BEGIN 0x1f800000
 #define PSX_SCRATCHPAD_SIZE  0x400
 #define PSX_SCRATCHPAD_END   0x1f8003ff
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_scratchpad_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    uint8_t* buf;
+};
+
+typedef struct psx_scratchpad_t psx_scratchpad_t;
 
-    uint8_t* buf;
-} psx_scratchpad_t;
-
 psx_scratchpad_t* psx_scratchpad_create(void);
 void psx_scratchpad_init(psx_scratchpad_t*);
 uint32_t psx_scratchpad_read32(psx_scratchpad_t*, uint32_t);
@@ -26,4 +28,4 @@
 void psx_scratchpad_write8(psx_scratchpad_t*, uint32_t, uint8_t);
 void psx_scratchpad_destroy(psx_scratchpad_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "spu.h"
-#include "../log.h"
+#include "dev/spu.h"
+#include "log.h"
 
 #define CLAMP(v, l, h) (((v) <= (l)) ? (l) : (((v) >= (h)) ? (h) : (v)))
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -134,8 +132,9 @@
     return *((uint16_t*)(ptr + offset));
 }
 
-uint8_t psx_spu_read8(psx_spu_t* spu, uint32_t offset) {
-    log_fatal("Unhandled 8-bit SPU read at offset %08x", offset);
+uint8_t psx_spu_read8(psx_spu_t* spu, uint32_t offset) {
+    USED(spu);
+    log_fatal("Unhandled 8-bit SPU read at offset %08x", offset);
 
     return 0x0;
 }
@@ -161,14 +160,14 @@
         uint16_t n = (spu->ram[addr + 2 + (j >> 1)] >> ((j & 1) * 4)) & 0xf;
 
         // Sign extend t
-        int16_t t = (int16_t)(n << 12) >> 12; 
-        int16_t s = (t << shift) + (((spu->data[v].h[0] * f0) + (spu->data[v].h[1] * f1) + 32) / 64);
+        int16_t t = (int16_t)(n << 12) >> 12;
+        int32_t s = (t << shift) + (((spu->data[v].h[0] * f0) + (spu->data[v].h[1] * f1) + 32) / 64);
         
         s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
 
-        spu->data[v].h[1] = spu->data[v].h[0];
-        spu->data[v].h[0] = s;
-        spu->data[v].buf[j] = s;
+        spu->data[v].h[1] = spu->data[v].h[0];
+        spu->data[v].h[0] = (int16_t)s;
+        spu->data[v].buf[j] = (int16_t)s;
     }
 }
 
@@ -451,9 +450,10 @@
         *((uint16_t*)(ptr + offset)) = value;
 }
 
-void psx_spu_write8(psx_spu_t* spu, uint32_t offset, uint8_t value) {
-    printf("Unhandled 8-bit SPU write at offset %08x (%02x)\n", offset, value);
-}
+void psx_spu_write8(psx_spu_t* spu, uint32_t offset, uint8_t value) {
+    USED(spu);
+    printf("Unhandled 8-bit SPU write at offset %08x (%02x)\n", offset, value);
+}
 
 void psx_spu_destroy(psx_spu_t* spu) {
     free(spu->ram);
@@ -645,7 +645,7 @@
         int16_t g1 = g_spu_gauss_table[0x1ff - gauss_index];
         int16_t g2 = g_spu_gauss_table[0x100 + gauss_index];
         int16_t g3 = g_spu_gauss_table[0x000 + gauss_index];
-        int16_t out = spu->data[v].s[0];
+        int16_t out;
 
         // out = interpolate_hermite(
         //     spu->data[v].s[3],
@@ -744,4 +744,4 @@
 }
 
 #undef CLAMP
-#undef MAX
\ No newline at end of file
+#undef MAX
--- a/psx/dev/spu.h
+++ b/psx/dev/spu.h
@@ -1,9 +1,9 @@
-#ifndef SPU_H
-#define SPU_H
+#ifndef PSX_DEV_SPU_H
+#define PSX_DEV_SPU_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "ic.h"
+#include "dev/ic.h"
 
 #define PSX_SPU_BEGIN 0x1f801c00
 #define PSX_SPU_SIZE  0x400
@@ -36,7 +36,7 @@
 #define SPUR_MBASE   0x1a2
 #define SPUR_SPUIRQA 0x1a4
 
-typedef struct __attribute__((__packed__)) {
+struct __attribute__((__packed__)) psx_spu_t {
     uint32_t bus_delay;
     uint32_t io_base, io_size;
 
@@ -170,7 +170,9 @@
         int adsr_sustain_level;
         uint32_t envctl;
     } data[24];
-} psx_spu_t;
+};
+
+typedef struct psx_spu_t psx_spu_t;
 
 psx_spu_t* psx_spu_create(void);
 void psx_spu_init(psx_spu_t*, psx_ic_t*);
@@ -184,4 +186,4 @@
 void psx_spu_update_cdda_buffer(psx_spu_t*, void*);
 uint32_t psx_spu_get_sample(psx_spu_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -1,9 +1,7 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
-#include "timer.h"
-#include "../log.h"
+#include "dev/timer.h"
+#include "log.h"
 
 #define T0_COUNTER     timer->timer[0].counter
 #define T0_SYNC_EN     timer->timer[0].sync_enable
@@ -192,12 +190,13 @@
     return 0x0;
 }
 
-uint8_t psx_timer_read8(psx_timer_t* timer, uint32_t offset) {
-    printf("Unhandled 8-bit TIMER read at offset %08x\n", offset);
+uint8_t psx_timer_read8(psx_timer_t* timer, uint32_t offset) {
+    USED(timer);
+    printf("Unhandled 8-bit TIMER read at offset %08x\n", offset);
+
+    return 0x0;
+}
 
-    return 0x0;
-}
-
 void timer_handle_irq(psx_timer_t* timer, int i);
 
 void psx_timer_write32(psx_timer_t* timer, uint32_t offset, uint32_t value) {
@@ -226,9 +225,10 @@
     timer_handle_irq(timer, index);
 }
 
-void psx_timer_write8(psx_timer_t* timer, uint32_t offset, uint8_t value) {
-    printf("Unhandled 8-bit TIMER write at offset %08x (%02x)\n", offset, value);
-}
+void psx_timer_write8(psx_timer_t* timer, uint32_t offset, uint8_t value) {
+    USED(timer);
+    printf("Unhandled 8-bit TIMER write at offset %08x (%02x)\n", offset, value);
+}
 
 void timer_handle_irq(psx_timer_t* timer, int i) {
     int irq = 0;
@@ -335,9 +335,10 @@
     timer_handle_irq(timer, 2);
 }
 
-void psx_timer_update(psx_timer_t* timer, int cyc) {
-    timer->prev_hblank = timer->hblank;
-    timer->prev_vblank = timer->vblank;
+void psx_timer_update(psx_timer_t* timer, int cyc) {
+    USED(cyc);
+    timer->prev_hblank = timer->hblank;
+    timer->prev_vblank = timer->vblank;
 
     timer_update_timer0(timer, 2);
     timer_update_timer1(timer, 2);
@@ -424,4 +425,4 @@
 
 void psx_timer_destroy(psx_timer_t* timer) {
     free(timer);
-}
\ No newline at end of file
+}
--- a/psx/dev/timer.h
+++ b/psx/dev/timer.h
@@ -1,10 +1,10 @@
-#ifndef TIMER_H
-#define TIMER_H
+#ifndef PSX_DEV_TIMER_H
+#define PSX_DEV_TIMER_H
 
-#include <stdint.h>
+#include "p9.h"
 
-#include "ic.h"
-#include "gpu.h"
+#include "dev/ic.h"
+#include "dev/gpu.h"
 
 #define PSX_TIMER_BEGIN 0x1f801100
 #define PSX_TIMER_SIZE  0x30
@@ -86,9 +86,9 @@
   16-31 Garbage (next opcode)
 */
 
-typedef struct {
-    uint32_t bus_delay;
-    uint32_t io_base, io_size;
+struct psx_timer_t {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
 
     psx_ic_t* ic;
     psx_gpu_t* gpu;
@@ -113,10 +113,12 @@
         int irq_fired;
         uint32_t div_counter;
 
-        int paused;
-        int blank_once;
-    } timer[3];
-} psx_timer_t;
+        int paused;
+        int blank_once;
+    } timer[3];
+};
+
+typedef struct psx_timer_t psx_timer_t;
 
 psx_timer_t* psx_timer_create(void);
 void psx_timer_init(psx_timer_t*, psx_ic_t*, psx_gpu_t*);
@@ -135,4 +137,4 @@
 void psxe_gpu_vblank_timer_event_cb(psx_gpu_t*);
 void psxe_gpu_vblank_end_event_cb(psx_gpu_t*);
 
-#endif
\ No newline at end of file
+#endif
--- a/psx/dev/xa.c
+++ b/psx/dev/xa.c
@@ -1,11 +1,12 @@
-#include "xa.h"
+#include "dev/xa.h"
 
-#include <stdint.h>
+#include "p9.h"
 
-void xa_decode_audio(uint8_t* src, uint16_t* dst) {
-    // Not a XA sector
-    if (src[XA_HDR_MODE] != 0x02)
-        return;
+void xa_decode_audio(uint8_t* src, uint16_t* dst) {
+    USED(dst);
+    // Not a XA sector
+    if (src[XA_HDR_MODE] != 0x02)
+        return;
     
     // uint8_t ci = src[XA_SHDR_CODINGINFO];
-}
\ No newline at end of file
+}
--- a/psx/dev/xa.h
+++ b/psx/dev/xa.h
@@ -1,5 +1,5 @@
-#ifndef XA_H
-#define XA_H
+#ifndef PSX_DEV_XA_H
+#define PSX_DEV_XA_H
 
 /*
   000h 0Ch  Sync
--- a/psx/exe.c
+++ b/psx/exe.c
@@ -1,34 +1,58 @@
-#include <stdio.h>
-
-#include "exe.h"
-#include "log.h"
-
-int psx_exe_load(psx_cpu_t* cpu, const char* path) {
-    if (!path)
-        return 0;
-
-    FILE* file = fopen(path, "rb");
-
-    if (!file)
-        return 1;
-
-    // Read header
-    psx_exe_hdr_t hdr;
-    
-    if (!fread((char*)&hdr, 1, sizeof(psx_exe_hdr_t), file))
-        return 2;
-
-    // Seek to program start 
-    fseek(file, 0x800, SEEK_SET);
-
-    // Read to RAM directly
-    uint32_t offset = hdr.ramdest & 0x7fffffff;
-
-    if (!fread(cpu->bus->ram->buf + offset, 1, hdr.filesz, file))
-        return 3;
-
-    // Load initial register values
-    cpu->pc = hdr.ipc;
+
+#include "exe.h"
+#include "log.h"
+#include "dev/ram.h"
+
+static int psx_readn_fd(int fd, void* buf, uint32_t size) {
+    uint8_t* p = (uint8_t*)buf;
+    uint32_t n = size;
+
+    while (n) {
+        long r = read(fd, p, n);
+
+        if (r <= 0)
+            return -1;
+
+        p += r;
+        n -= r;
+    }
+
+    return 0;
+}
+
+int psx_exe_load(psx_cpu_t* cpu, const char* path) {
+    if (!path)
+        return 0;
+
+    int fd = open(path, OREAD);
+
+    if (fd < 0)
+        return 1;
+
+    // Read header
+    psx_exe_hdr_t hdr;
+    
+    if (psx_readn_fd(fd, &hdr, sizeof(psx_exe_hdr_t)) < 0) {
+        close(fd);
+        return 2;
+    }
+
+    // Seek to program start 
+    if (seek(fd, 0x800, 0) < 0) {
+        close(fd);
+        return 2;
+    }
+
+    // Read to RAM directly
+    uint32_t offset = hdr.ramdest & 0x7fffffff;
+
+    if (psx_readn_fd(fd, cpu->bus->ram->buf + offset, hdr.filesz) < 0) {
+        close(fd);
+        return 3;
+    }
+
+    // Load initial register values
+    cpu->pc = hdr.ipc;
     cpu->next_pc = cpu->pc + 4;
     cpu->r[28] = hdr.igp;
 
@@ -37,10 +61,10 @@
         cpu->r[30] = cpu->r[29];
     }
 
-    log_info("Loaded PS-X EXE file \"%s\"", path);
-    log_fatal("PC=%08x SP=%08x (%08x) GP=%08x", cpu->pc, cpu->r[29], hdr.ispb, cpu->r[28]);
-
-    fclose(file);
-
-    return 0;
-}
\ No newline at end of file
+    log_info("Loaded PS-X EXE file \"%s\"", path);
+    log_fatal("PC=%08x SP=%08x (%08x) GP=%08x", cpu->pc, cpu->r[29], hdr.ispb, cpu->r[28]);
+
+    close(fd);
+
+    return 0;
+}
--- a/psx/exe.h
+++ b/psx/exe.h
@@ -1,9 +1,7 @@
 #ifndef EXE_H
 #define EXE_H
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include "p9.h"
 
 #include "cpu.h"
 #include "bus_init.h"
--- a/psx/input/guncon.c
+++ b/psx/input/guncon.c
@@ -4,11 +4,10 @@
     Sony PlayStation Standard Digital/Analog Controller Emulator
 */
 
-#include "guncon.h"
-#include "../log.h"
+#include "input/guncon.h"
+#include "log.h"
 
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
 const char* states[] = {
     "HIZ",
@@ -70,11 +69,10 @@
 }
 
 void psxi_guncon_write(void* udata, uint16_t data) {
-    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+    USED(udata);
+    USED(data);
 
     // printf("guncon: write %02x\n", data);
-
-    (void)guncon;
 }
 
 void psxi_guncon_on_button_press(void* udata, uint32_t data) {
@@ -135,4 +133,4 @@
 
 void psxi_guncon_destroy(psxi_guncon_t* guncon) {
     free(guncon);
-}
\ No newline at end of file
+}
--- a/psx/input/guncon.h
+++ b/psx/input/guncon.h
@@ -4,10 +4,10 @@
     Namco GunCon emulation
 */
 
-#ifndef GUNCON_H
-#define GUNCON_H
+#ifndef PSX_INPUT_GUNCON_H
+#define PSX_INPUT_GUNCON_H
 
-#include "../dev/input.h"
+#include "dev/input.h"
 
 /*
   __Halfword 0 (Controller Info)___________________
--- a/psx/input/sda.c
+++ b/psx/input/sda.c
@@ -4,11 +4,10 @@
     Sony PlayStation Standard Digital/Analog Controller Emulator
 */
 
-#include "sda.h"
-#include "../log.h"
+#include "input/sda.h"
+#include "log.h"
 
-#include <stdlib.h>
-#include <string.h>
+#include "p9.h"
 
 psxi_sda_t* psxi_sda_create(void) {
     return (psxi_sda_t*)malloc(sizeof(psxi_sda_t));
--- a/psx/input/sda.h
+++ b/psx/input/sda.h
@@ -7,7 +7,7 @@
 #ifndef SDA_H
 #define SDA_H
 
-#include "../dev/input.h"
+#include "dev/input.h"
 
 // Controller/Input IDs
 #define SDA_MODEL_DIGITAL       0x41
--- a/psx/log.c
+++ b/psx/log.c
@@ -43,48 +43,31 @@
   "trace", "debug", "info", "warn", "error", "fatal"
 };
 
-#ifdef LOG_USE_COLOR
-static const char *level_colors[] = {
-  "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
-};
-#endif
-
-
 static void stdout_callback(log_Event *ev) {
-#ifdef LOG_USE_COLOR
-  fprintf(
-    ev->udata, "psx: %s%-5s\x1b[0m \x1b[90m%s:\x1b[0m ",
-    level_colors[ev->level], level_strings[ev->level],
-    ev->file);
-#else
-  fprintf(
-    ev->udata, "psx: %-5s %s: ",
-    level_strings[ev->level], ev->file);
-#endif
-  vfprintf(ev->udata, ev->fmt, ev->ap);
-  fprintf(ev->udata, "\n");
-  fflush(ev->udata);
-}
+  char *msg = vsmprint(ev->fmt, ev->ap);
 
+  if (!msg) {
+    msg = smprint("%s", ev->fmt);
+  }
 
-static void file_callback(log_Event *ev) {
-  char buf[64];
-  buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
-  fprintf(
-    ev->udata, "%s %-5s %s:%d: ",
-    buf, level_strings[ev->level], ev->file, ev->line);
-  vfprintf(ev->udata, ev->fmt, ev->ap);
-  fprintf(ev->udata, "\n");
-  fflush(ev->udata);
+  if (msg) {
+    print("psx: %-5s %s:%d: %s\n",
+      level_strings[ev->level], ev->file, ev->line, msg);
+    free(msg);
+    return;
+  }
+
+  print("psx: %-5s %s:%d\n",
+    level_strings[ev->level], ev->file, ev->line);
 }
 
 
-static void lock(void)   {
+static void log_lock(void)   {
   if (L.lock) { L.lock(true, L.udata); }
 }
 
 
-static void unlock(void) {
+static void log_unlock(void) {
   if (L.lock) { L.lock(false, L.udata); }
 }
 
@@ -121,15 +104,17 @@
 }
 
 
-int log_add_fp(FILE *fp, int level) {
-  return log_add_callback(file_callback, fp, level);
+int log_add_fp(void *fp, int level) {
+  USED(fp);
+  USED(level);
+  return -1;
 }
 
 
 static void init_event(log_Event *ev, void *udata) {
   if (!ev->time) {
-    time_t t = time(NULL);
-    ev->time = localtime(&t);
+    long t = time(0);
+    ev->time = localtime(t);
   }
   ev->udata = udata;
 }
@@ -143,10 +128,10 @@
     .level = level,
   };
 
-  lock();
+  log_lock();
 
   if (!L.quiet && level >= L.level) {
-    init_event(&ev, stderr);
+    init_event(&ev, nil);
     va_start(ev.ap, fmt);
     stdout_callback(&ev);
     va_end(ev.ap);
@@ -162,5 +147,5 @@
     }
   }
 
-  unlock();
+  log_unlock();
 }
--- a/psx/log.h
+++ b/psx/log.h
@@ -5,13 +5,10 @@
  * under the terms of the MIT license. See `log.c` for details.
  */
 
-#ifndef LOG_H
-#define LOG_H
+#ifndef PSX_LOG_H
+#define PSX_LOG_H
 
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <time.h>
+#include "p9.h"
 
 #define LOG_VERSION "0.1.0"
 
@@ -19,7 +16,7 @@
   va_list ap;
   const char *fmt;
   const char *file;
-  struct tm *time;
+  Tm *time;
   void *udata;
   int line;
   int level;
@@ -42,8 +39,8 @@
 void log_set_level(int level);
 void log_set_quiet(bool enable);
 int log_add_callback(log_LogFn fn, void *udata, int level);
-int log_add_fp(FILE *fp, int level);
+int log_add_fp(void *fp, int level);
 
 void log_log(int level, const char *file, int line, const char *fmt, ...);
 
-#endif
\ No newline at end of file
+#endif
--- /dev/null
+++ b/psx/p9.h
@@ -1,0 +1,364 @@
+#ifndef PSX_P9_H
+#define PSX_P9_H
+
+#include <u.h>
+#include <libc.h>
+
+/* C99 integer aliases mapped to native 9front integer types. */
+typedef u8int uint8_t;
+typedef u16int uint16_t;
+typedef u32int uint32_t;
+typedef u64int uint64_t;
+typedef s8int int8_t;
+typedef s16int int16_t;
+typedef s32int int32_t;
+typedef s64int int64_t;
+typedef intptr intptr_t;
+typedef uintptr uintptr_t;
+
+/* stddef-like aliases used across the core. */
+#ifndef _SIZE_T
+typedef uintptr size_t;
+#define _SIZE_T
+#endif
+#ifndef _SSIZE_T
+typedef intptr ssize_t;
+#define _SSIZE_T
+#endif
+
+/* stdbool compatibility for native toolchains without <stdbool.h>. */
+#ifndef __bool_true_false_are_defined
+typedef int bool;
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined 1
+#endif
+
+/* Plan 9 uses `nil`; keep NULL-compatible call sites building. */
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Minimal libc naming shims for native Plan 9 builds. */
+#ifndef printf
+#define printf print
+#endif
+#ifndef putchar
+static inline int
+p9_putchar(int c)
+{
+	return print("%c", c);
+}
+#define putchar(c) p9_putchar((c))
+#endif
+#ifndef exit
+static inline void
+p9_exit(int status)
+{
+	exits(status ? "error" : nil);
+}
+#define exit(status) p9_exit((status))
+#endif
+
+/* Minimal stdio compatibility for code still using FILE*. */
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+#ifndef _P9_FILE_COMPAT
+#define _P9_FILE_COMPAT
+typedef struct p9_file {
+	int fd;
+} FILE;
+
+static inline FILE*
+fopen(const char *path, const char *mode)
+{
+	int fd;
+	int omode;
+	FILE *fp;
+
+	if (!path || !mode)
+		return nil;
+
+	omode = -1;
+
+	switch (mode[0]) {
+	case 'r':
+		omode = OREAD;
+		break;
+	case 'w':
+		omode = OWRITE | OTRUNC;
+		break;
+	case 'a':
+		omode = OWRITE;
+		break;
+	}
+
+	if (omode < 0)
+		return nil;
+
+	if (mode[0] == 'r') {
+		fd = open(path, omode);
+	} else {
+		fd = create(path, omode, 0666);
+		if (fd < 0)
+			fd = open(path, omode);
+	}
+
+	if (fd < 0)
+		return nil;
+
+	fp = malloc(sizeof(*fp));
+	if (!fp) {
+		close(fd);
+		return nil;
+	}
+
+	fp->fd = fd;
+
+	if (mode[0] == 'a')
+		(void)seek(fp->fd, 0, SEEK_END);
+
+	return fp;
+}
+
+static inline int
+fclose(FILE *fp)
+{
+	int r;
+
+	if (!fp)
+		return -1;
+
+	r = close(fp->fd);
+	free(fp);
+	return r;
+}
+
+static inline int
+fseek(FILE *fp, long offset, int whence)
+{
+	if (!fp)
+		return -1;
+
+	return seek(fp->fd, offset, whence) < 0 ? -1 : 0;
+}
+
+static inline long
+ftell(FILE *fp)
+{
+	vlong pos;
+
+	if (!fp)
+		return -1;
+
+	pos = seek(fp->fd, 0, SEEK_CUR);
+	if (pos < 0)
+		return -1;
+
+	return (long)pos;
+}
+
+static inline size_t
+fread(void *ptr, size_t size, size_t nmemb, FILE *fp)
+{
+	size_t total;
+	size_t done;
+	uchar *p;
+
+	if (!fp || !ptr || size == 0 || nmemb == 0)
+		return 0;
+
+	total = size * nmemb;
+	done = 0;
+	p = (uchar*)ptr;
+
+	while (done < total) {
+		long r = read(fp->fd, p + done, total - done);
+
+		if (r <= 0)
+			break;
+
+		done += r;
+	}
+
+	return done / size;
+}
+
+static inline size_t
+fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp)
+{
+	size_t total;
+	size_t done;
+	const uchar *p;
+
+	if (!fp || !ptr || size == 0 || nmemb == 0)
+		return 0;
+
+	total = size * nmemb;
+	done = 0;
+	p = (const uchar*)ptr;
+
+	while (done < total) {
+		long w = write(fp->fd, (void*)(p + done), total - done);
+
+		if (w <= 0)
+			break;
+
+		done += w;
+	}
+
+	return done / size;
+}
+
+static inline int
+fgetc(FILE *fp)
+{
+	uchar c;
+	long r;
+
+	if (!fp)
+		return EOF;
+
+	r = read(fp->fd, &c, 1);
+	if (r != 1)
+		return EOF;
+
+	return (int)c;
+}
+#endif
+
+#ifndef isdigit
+static inline int
+p9_isdigit(int c)
+{
+	return c >= '0' && c <= '9';
+}
+#define isdigit(c) p9_isdigit((c))
+#endif
+
+#ifndef isalpha
+static inline int
+p9_isalpha(int c)
+{
+	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+#define isalpha(c) p9_isalpha((c))
+#endif
+
+#ifndef isspace
+static inline int
+p9_isspace(int c)
+{
+	return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+#define isspace(c) p9_isspace((c))
+#endif
+
+/* GCC attributes are ignored by native 9front compilers. */
+#ifndef __attribute__
+#define __attribute__(x)
+#endif
+
+/* 6c may provide __FILE/__LINE (without trailing underscores). */
+#ifndef __FILE__
+#ifdef __FILE
+#define __FILE__ __FILE
+#else
+#define __FILE__ "unknown"
+#endif
+#endif
+#ifndef __LINE__
+#ifdef __LINE
+#define __LINE__ __LINE
+#else
+#define __LINE__ 0
+#endif
+#endif
+
+/* Some toolchains do not accept the C99 inline keyword. */
+#ifndef __GNUC__
+#ifndef inline
+#define inline
+#endif
+#endif
+
+/* Minimal assert replacement for native headers. */
+#ifndef assert
+#define assert(x) do { \
+  if (!(x)) sysfatal("assert failed: %s:%d: %s", __FILE__, __LINE__, #x); \
+} while (0)
+#endif
+
+/* Helpers for codepaths that expect C99 float math helpers. */
+#ifndef floorf
+#define floorf(x) ((float)floor((double)(x)))
+#endif
+#ifndef ceilf
+#define ceilf(x) ((float)ceil((double)(x)))
+#endif
+#ifndef roundf
+#define roundf(x) ((float)(((x) >= 0) ? floor((double)(x) + 0.5) : ceil((double)(x) - 0.5)))
+#endif
+
+/* GCC builtin compatibility shims used by the CPU core. */
+#ifndef __GNUC__
+static inline int
+p9_builtin_ssub_overflow(int a, int b, int *r)
+{
+  vlong v = (vlong)a - (vlong)b;
+  *r = (int)v;
+  return (v > 2147483647LL) || (v < -2147483648LL);
+}
+
+static inline int
+p9_builtin_clz(unsigned int x)
+{
+  int n = 0;
+
+  if (x == 0)
+    return 32;
+
+  while ((x & 0x80000000U) == 0) {
+    n++;
+    x <<= 1;
+  }
+
+  return n;
+}
+
+#define __builtin_ssub_overflow(a, b, r) p9_builtin_ssub_overflow((a), (b), (r))
+#define __builtin_clz(x) p9_builtin_clz((unsigned int)(x))
+#endif
+
+/* stdint limit macros used in the codebase. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX 127
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32768)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX 32767
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647 - 1)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX 2147483647
+#endif
+
+#endif
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -1,4 +1,29 @@
-#include "psx.h"
+#include "psx.h"
+#include "dev/bios.h"
+#include "dev/ram.h"
+#include "dev/dma.h"
+#include "dev/exp1.h"
+#include "dev/exp2.h"
+#include "dev/mc1.h"
+#include "dev/mc2.h"
+#include "dev/mc3.h"
+#include "dev/ic.h"
+#include "dev/scratchpad.h"
+#include "dev/gpu.h"
+#include "dev/spu.h"
+#include "dev/timer.h"
+#include "dev/cdrom/cdrom.h"
+#include "dev/pad.h"
+#include "dev/mdec.h"
+
+/* Keep explicit prototypes for 6c in this TU. */
+psx_bios_t* psx_bios_create(void);
+void psx_bios_init(psx_bios_t*);
+int psx_bios_load(psx_bios_t*, const char*);
+void psx_bios_destroy(psx_bios_t*);
+psx_spu_t* psx_spu_create(void);
+void psx_spu_init(psx_spu_t*, psx_ic_t*);
+void psx_spu_destroy(psx_spu_t*);
 
 psx_t* psx_create(void) {
     return (psx_t*)malloc(sizeof(psx_t));
@@ -8,14 +33,18 @@
     return psx_bios_load(psx->bios, path);
 }
 
-void psx_load_state(psx_t* psx, const char* path) {
-    log_fatal("State saving/loading is not yet supported");
+void psx_load_state(psx_t* psx, const char* path) {
+    USED(psx);
+    USED(path);
+    log_fatal("State saving/loading is not yet supported");
 
     exit(1);
 }
 
-void psx_save_state(psx_t* psx, const char* path) {
-    log_fatal("State saving/loading is not yet supported");
+void psx_save_state(psx_t* psx, const char* path) {
+    USED(psx);
+    USED(path);
+    log_fatal("State saving/loading is not yet supported");
 
     exit(1);
 }
@@ -109,9 +138,10 @@
     return aspect;
 }
 
-void atcons_tx(void* udata, unsigned char c) {
-    putchar(c);
-}
+void atcons_tx(void* udata, unsigned char c) {
+    USED(udata);
+    putchar(c);
+}
 
 int psx_init(psx_t* psx, const char* bios_path, const char* exp_path) {
     memset(psx, 0, sizeof(psx_t));
@@ -187,8 +217,9 @@
     return psx_exp1_init(psx->exp1, psx->mc1, path);
 }
 
-void psx_hard_reset(psx_t* psx) {
-    log_fatal("Hard reset not yet implemented");
+void psx_hard_reset(psx_t* psx) {
+    USED(psx);
+    log_fatal("Hard reset not yet implemented");
 
     exit(1);
 }
@@ -197,12 +228,14 @@
     psx_cpu_init(psx->cpu, psx->bus);
 }
 
-uint32_t* psx_take_screenshot(psx_t* psx) {
-    log_fatal("Screenshots not yet supported");
+uint32_t* psx_take_screenshot(psx_t* psx) {
+    USED(psx);
+    log_fatal("Screenshots not yet supported");
+
+    exit(1);
+    return nil;
+}
 
-    exit(1);
-}
-
 int psx_swap_disc(psx_t* psx, const char* path) {
     psx_cdrom_destroy(psx->cdrom);
 
@@ -306,4 +339,4 @@
 
 psx_cpu_t* psx_get_cpu(psx_t* psx) {
     return psx->cpu;
-}
\ No newline at end of file
+}
--- a/psx/psx.h
+++ b/psx/psx.h
@@ -5,7 +5,7 @@
 #include "log.h"
 #include "exe.h"
 
-#include <stdint.h>
+#include "p9.h"
 
 #define STR1(m) #m
 #define STR(m) STR1(m)
--