shithub: psxe

Download patch

ref: 86e6d8311787c3dc56a2420966378960bccb0640
parent: 6e692eceb9bba9f26d33160b1319130580dfb516
author: allkern <lisandroaalarcon@gmail.com>
date: Sat May 11 08:04:09 EDT 2024

Fix warnings again

--- a/frontend/config.c
+++ b/frontend/config.c
@@ -67,7 +67,7 @@
 static const char* g_desc_text =
     "\nPlease report any bugs to <https://github.com/allkern/psxe/issues>\n";
 
-psxe_config_t* psxe_cfg_create() {
+psxe_config_t* psxe_cfg_create(void) {
     return (psxe_config_t*)malloc(sizeof(psxe_config_t));
 }
 
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -47,7 +47,7 @@
     return 0;
 } 
 
-SDL_GameController* screen_find_controller() {
+SDL_GameController* screen_find_controller(void) {
     for (int i = 0; i < SDL_NumJoysticks(); i++)
         if (SDL_IsGameController(i))
             return SDL_GameControllerOpen(i);
@@ -61,7 +61,7 @@
     return (width == 256) ? 256 : 320;
 }
 
-psxe_screen_t* psxe_screen_create() {
+psxe_screen_t* psxe_screen_create(void) {
     return (psxe_screen_t*)malloc(sizeof(psxe_screen_t));
 }
 
--- a/frontend/screen.h
+++ b/frontend/screen.h
@@ -7,8 +7,8 @@
 
 #include <string.h>
 
-#include "SDL2/SDL.h"
-#include "SDL2/SDL_gamecontroller.h"
+#include "SDL.h"
+#include "SDL_gamecontroller.h"
 
 typedef struct {
     SDL_Window* window;
--- a/psx/bus.c
+++ b/psx/bus.c
@@ -12,7 +12,7 @@
     0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff
 };
 
-psx_bus_t* psx_bus_create() {
+psx_bus_t* psx_bus_create(void) {
     return (psx_bus_t*)malloc(sizeof(psx_bus_t));
 }
 
--- a/psx/cpu.c
+++ b/psx/cpu.c
@@ -221,7 +221,7 @@
     }
 }
 
-psx_cpu_t* psx_cpu_create() {
+psx_cpu_t* psx_cpu_create(void) {
     return (psx_cpu_t*)malloc(sizeof(psx_cpu_t));
 }
 
--- a/psx/dev/bios.c
+++ b/psx/dev/bios.c
@@ -5,7 +5,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-psx_bios_t* psx_bios_create() {
+psx_bios_t* psx_bios_create(void) {
     return (psx_bios_t*)malloc(sizeof(psx_bios_t));
 }
 
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -1739,7 +1739,7 @@
     "cdrom_write_status", "cdrom_write_rcdrspuv", "cdrom_write_rcdlspuv", "cdrom_write_volume"
 };
 
-psx_cdrom_t* psx_cdrom_create() {
+psx_cdrom_t* psx_cdrom_create(void) {
     return (psx_cdrom_t*)malloc(sizeof(psx_cdrom_t));
 }
 
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -7,7 +7,7 @@
 #include <string.h>
 #include <ctype.h>
 
-psx_dma_t* psx_dma_create() {
+psx_dma_t* psx_dma_create(void) {
     return (psx_dma_t*)malloc(sizeof(psx_dma_t));
 }
 
--- a/psx/dev/exp1.c
+++ b/psx/dev/exp1.c
@@ -5,7 +5,7 @@
 #include "../log.h"
 #include "exp1.h"
 
-psx_exp1_t* psx_exp1_create() {
+psx_exp1_t* psx_exp1_create(void) {
     return (psx_exp1_t*)malloc(sizeof(psx_exp1_t));
 }
 
--- a/psx/dev/exp2.c
+++ b/psx/dev/exp2.c
@@ -5,7 +5,7 @@
 #include "../log.h"
 #include "exp2.h"
 
-psx_exp2_t* psx_exp2_create() {
+psx_exp2_t* psx_exp2_create(void) {
     return (psx_exp2_t*)malloc(sizeof(psx_exp2_t));
 }
 
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -38,7 +38,7 @@
     return (m > c) ? m : c;
 }
 
-psx_gpu_t* psx_gpu_create() {
+psx_gpu_t* psx_gpu_create(void) {
     return (psx_gpu_t*)malloc(sizeof(psx_gpu_t));
 }
 
--- a/psx/dev/ic.c
+++ b/psx/dev/ic.c
@@ -6,7 +6,7 @@
 
 #include "../log.h"
 
-psx_ic_t* psx_ic_create() {
+psx_ic_t* psx_ic_create(void) {
     return (psx_ic_t*)malloc(sizeof(psx_ic_t));
 }
 
--- a/psx/dev/input.c
+++ b/psx/dev/input.c
@@ -6,7 +6,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-psx_input_t* psx_input_create() {
+psx_input_t* psx_input_create(void) {
     return (psx_input_t*)malloc(sizeof(psx_input_t));
 }
 
--- a/psx/dev/mc1.c
+++ b/psx/dev/mc1.c
@@ -41,7 +41,7 @@
 
 // #define COM_DLY(dev)
 
-psx_mc1_t* psx_mc1_create() {
+psx_mc1_t* psx_mc1_create(void) {
     return (psx_mc1_t*)malloc(sizeof(psx_mc1_t));
 }
 
--- a/psx/dev/mc2.c
+++ b/psx/dev/mc2.c
@@ -5,7 +5,7 @@
 #include "mc2.h"
 #include "../log.h"
 
-psx_mc2_t* psx_mc2_create() {
+psx_mc2_t* psx_mc2_create(void) {
     return (psx_mc2_t*)malloc(sizeof(psx_mc2_t));
 }
 
--- a/psx/dev/mc3.c
+++ b/psx/dev/mc3.c
@@ -5,7 +5,7 @@
 #include "mc3.h"
 #include "../log.h"
 
-psx_mc3_t* psx_mc3_create() {
+psx_mc3_t* psx_mc3_create(void) {
     return (psx_mc3_t*)malloc(sizeof(psx_mc3_t));
 }
 
--- a/psx/dev/mcd.c
+++ b/psx/dev/mcd.c
@@ -1,7 +1,7 @@
 #include "mcd.h"
 #include "../log.h"
 
-psx_mcd_t* psx_mcd_create() {
+psx_mcd_t* psx_mcd_create(void) {
     return (psx_mcd_t*)malloc(sizeof(psx_mcd_t));
 }
 
--- a/psx/dev/mdec.c
+++ b/psx/dev/mdec.c
@@ -269,7 +269,7 @@
     mdec_nop
 };
 
-psx_mdec_t* psx_mdec_create() {
+psx_mdec_t* psx_mdec_create(void) {
     return (psx_mdec_t*)malloc(sizeof(psx_mdec_t));
 }
 
--- a/psx/dev/old/cdrom.c
+++ /dev/null
@@ -1,1593 +1,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "cdrom.h"
-#include "../log.h"
-#include "../msf.h"
-
-/*
-    Drive Status           1st Response   2nd Response
-    Door Open              INT5(11h,80h)  N/A
-    Spin-up                INT5(01h,80h)  N/A
-    Detect busy            INT5(03h,80h)  N/A
-    No Disk                INT3(stat)     INT5(08h,40h, 00h,00h, 00h,00h,00h,00h)
-    Audio Disk             INT3(stat)     INT5(0Ah,90h, 00h,00h, 00h,00h,00h,00h)
-    Unlicensed:Mode1       INT3(stat)     INT5(0Ah,80h, 00h,00h, 00h,00h,00h,00h)
-    Unlicensed:Mode2       INT3(stat)     INT5(0Ah,80h, 20h,00h, 00h,00h,00h,00h)
-    Unlicensed:Mode2+Audio INT3(stat)     INT5(0Ah,90h, 20h,00h, 00h,00h,00h,00h)
-    Debug/Yaroze:Mode2     INT3(stat)     INT2(02h,00h, 20h,00h, 20h,20h,20h,20h)
-    Licensed:Mode2         INT3(stat)     INT2(02h,00h, 20h,00h, 53h,43h,45h,4xh)
-    Modchip:Audio/Mode1    INT3(stat)     INT2(02h,00h, 00h,00h, 53h,43h,45h,4xh)
-*/
-
-#define GETID_RESPONSE_SIZE 8
-#define GETID_RESPONSE_END (GETID_RESPONSE_SIZE - 1)
-
-static const uint8_t g_getid_no_disc[] = {
-    0x08, 0x40, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t g_getid_audio[] = {
-    0x0a, 0x90, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t g_getid_unlicensed[] = {
-    0x0a, 0x80, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t g_getid_licensed[] = {
-    0x02, 0x00, 0x20, 0x00,
-    'S' , 'C' , 'E' , 'A'
-};
-
-#define RESP_PUSH(data) { \
-    cdrom->rfifo[cdrom->rfifo_index++] = data; \
-    cdrom->rfifo_index &= 15; \
-    SET_BITS(status, STAT_RSLRRDY_MASK, STAT_RSLRRDY_MASK); }
-
-#define PFIFO_POP (cdrom->pfifo[--cdrom->pfifo_index])
-
-#define VALID_BCD(v) (((v & 0xf) <= 9) && ((v & 0xf0) <= 0x90))
-
-void cdrom_cmd_error(psx_cdrom_t* cdrom) {
-    SET_BITS(ifr, IFR_INT, IFR_INT5);
-    RESP_PUSH(cdrom->error);
-    RESP_PUSH(GETSTAT_MOTOR | cdrom->error_flags);
-
-    cdrom->pfifo_index = 0;
-    cdrom->delayed_command = CDL_NONE;
-    cdrom->state = CD_STATE_RECV_CMD;
-}
-void cdrom_cmd_unimplemented(psx_cdrom_t* cdrom) {
-    printf("Unimplemented CDROM command (%u)\n", cdrom->command);
-
-    exit(1);
-}
-void cdrom_cmd_getstat(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index) {
-                log_fatal("CdlGetStat: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_GETSTAT;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR | (cdrom->disc ? 0 : GETSTAT_TRAYOPEN));
-
-            if (cdrom->ongoing_read_command) {
-                cdrom->state = CD_STATE_SEND_RESP2;
-                cdrom->delayed_command = cdrom->ongoing_read_command;
-                cdrom->irq_delay = DELAY_1MS;
-            } else {
-                cdrom->delayed_command = CDL_NONE;
-                cdrom->state = CD_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index != 3) {
-                log_fatal("CdlSetloc: Expected exactly 3 parameters, got %u instead",
-                    cdrom->pfifo_index
-                );
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            if (!cdrom->ongoing_read_command) {
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_SETLOC;
-                cdrom->state = CD_STATE_SEND_RESP1;
-            } else {
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = cdrom->ongoing_read_command;
-                cdrom->state = CD_STATE_SEND_RESP2;
-            }
-
-            int f = PFIFO_POP;
-            int s = PFIFO_POP;
-            int m = PFIFO_POP;
-
-            if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_INVSUBF;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->seek_ff = f;
-            cdrom->seek_ss = s;
-            cdrom->seek_mm = m;
-
-            cdrom->seek_pending = 1;
-
-            log_fatal("setloc: %02x:%02x:%02x",
-                cdrom->seek_mm,
-                cdrom->seek_ss,
-                cdrom->seek_ff
-            );
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            cdrom->delayed_command = CDL_NONE;
-
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-
-        // Read ongoing
-        case CD_STATE_SEND_RESP2: {
-            int f = PFIFO_POP;
-            int s = PFIFO_POP;
-            int m = PFIFO_POP;
-
-            if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {
-                cdrom->ongoing_read_command = CDL_NONE;
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_INVSUBF;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->seek_ff = f;
-            cdrom->seek_ss = s;
-            cdrom->seek_mm = m;
-        } break;
-    }
-}
-void cdrom_cmd_play(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            int track = 0;
-
-            // Optional track number parameter
-            if (cdrom->pfifo_index)
-                track = PFIFO_POP;
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_PLAY;
-
-            if (track) {
-
-                psx_disc_get_track_addr(cdrom->disc, &cdrom->cdda_msf, track);
-
-                msf_to_bcd(&cdrom->cdda_msf);
-
-                cdrom->cdda_track = track;
-                cdrom->seek_mm = cdrom->cdda_msf.m;
-                cdrom->seek_ss = cdrom->cdda_msf.s;
-                cdrom->seek_ff = cdrom->cdda_msf.f;
-    
-                cdrom->seek_pending = 1;
-            }
-
-            if (cdrom->seek_pending) {
-                cdrom->seek_pending = 0;
-
-                cdrom->cdda_msf.m = cdrom->seek_mm;
-                cdrom->cdda_msf.s = cdrom->seek_ss;
-                cdrom->cdda_msf.f = cdrom->seek_ff;
-
-                // Convert seek to I
-                msf_t msf = cdrom->cdda_msf;
-                
-                msf_from_bcd(&msf);
-
-                // Seek to that address and read sector
-                psx_disc_seek(cdrom->disc, msf);
-                psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
-
-                // Increment sector
-                msf_add_f(&msf, 1);
-                msf_to_bcd(&msf);
-
-                cdrom->cdda_msf = msf;
-                cdrom->cdda_sector_offset = 0;
-            }
-
-            cdrom->cdda_playing = 1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_PLAY);
-
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_readn(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-    cdrom->ongoing_read_command = 1;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            log_fatal("CdlReadN: CD_STATE_RECV_CMD");
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_READN;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            log_fatal("CdlReadN: CD_STATE_SEND_RESP1");
-
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            // msf_t msf;
-
-            // msf.m = cdrom->seek_mm;
-            // msf.s = cdrom->seek_ss;
-            // msf.f = cdrom->seek_ff;
-
-            // msf_from_bcd(&msf);
-
-            // int err = psx_disc_seek(cdrom->disc, msf);
-
-            // if (err) {
-            //     log_set_quiet(0);
-            //     log_fatal("CdlReadN: Out of bounds seek");
-            //     log_set_quiet(1);
-
-            //     cdrom->irq_delay = DELAY_1MS * 600;
-            //     cdrom->delayed_command = CDL_ERROR;
-            //     cdrom->state = CD_STATE_ERROR;
-            //     cdrom->error = ERR_INVSUBF;
-            //     cdrom->error_flags = GETSTAT_SEEKERROR;
-
-            //     return;
-            // }
-
-            // psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
-            // msf_t sector_msf;
-
-            // sector_msf.m = cdrom->dfifo[0x0c];
-            // sector_msf.s = cdrom->dfifo[0x0d];
-            // sector_msf.f = cdrom->dfifo[0x0e];
-
-            // int correct_msf = (cdrom->seek_mm == sector_msf.m) && 
-            //                   (cdrom->seek_ss == sector_msf.s) &&
-            //                   (cdrom->seek_ff == sector_msf.f);
-            
-            // Most probably audio sector:
-            // Purposefully constructed audio data could
-            // circumvent this detection code, but it will work
-            // for most intents and purposes 
-            // if (!correct_msf) {
-            //     log_set_quiet(0);
-            //     log_fatal("CdlReadN: Audio read");
-            //     log_set_quiet(1);
-
-            //     cdrom->irq_delay = DELAY_1MS * 600;
-            //     cdrom->delayed_command = CDL_ERROR;
-            //     cdrom->state = CD_STATE_ERROR;
-            //     cdrom->error = ERR_SEEK;
-            //     cdrom->error_flags = GETSTAT_SEEKERROR;
-
-            //     return;
-            // }
-            
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_READN;
-
-            // if (cdrom->spin_delay) {
-            //     cdrom->irq_delay += cdrom->spin_delay;
-            //     cdrom->spin_delay = 0;
-            // }
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            log_fatal("CdlReadN: CD_STATE_SEND_RESP2");
-
-            msf_t msf;
-
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
-
-            msf_from_bcd(&msf);
-
-            psx_disc_seek(cdrom->disc, msf);
-            psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-            cdrom->dfifo_index = 0;
-            cdrom->dfifo_full = 1;
-
-            // printf("Sector header: msf=%02x:%02x:%02x, mode=%02x, subheader=%02x,%02x,%02x,%02x\n",
-            //     cdrom->dfifo[0x0c],
-            //     cdrom->dfifo[0x0d],
-            //     cdrom->dfifo[0x0e],
-            //     cdrom->dfifo[0x0f],
-            //     cdrom->dfifo[0x10],
-            //     cdrom->dfifo[0x11],
-            //     cdrom->dfifo[0x12],
-            //     cdrom->dfifo[0x13]
-            // );
-
-            if (cdrom->dfifo[0x12] & 0x20) {
-                log_fatal("Unimplemented XA Form2 Sector");
-
-                // exit(1);
-            }
-
-            cdrom->seek_ff++;
-
-            if ((cdrom->seek_ff & 0xF) == 10) { cdrom->seek_ff += 0x10; cdrom->seek_ff &= 0xF0; }
-            if (cdrom->seek_ff == 0x75) { cdrom->seek_ss++; cdrom->seek_ff = 0; }
-            if ((cdrom->seek_ss & 0xF) == 10) { cdrom->seek_ss += 0x10; cdrom->seek_ss &= 0xF0; }
-            if (cdrom->seek_ss == 0x60) { cdrom->seek_mm++; cdrom->seek_ss = 0; }
-            if ((cdrom->seek_mm & 0xF) == 10) { cdrom->seek_mm += 0x10; cdrom->seek_mm &= 0xF0; }
-
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_READN;
-
-            SET_BITS(ifr, IFR_INT, IFR_INT1);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-        } break;
-    }
-}
-void cdrom_cmd_motoron(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            cdrom->ongoing_read_command = CDL_NONE;
-            cdrom->cdda_playing = 0;
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_MOTORON;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_MOTORON;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_stop(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            cdrom->ongoing_read_command = CDL_NONE;
-            cdrom->cdda_playing = 0;
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_STOP;
-            cdrom->seek_ff = 0;
-            cdrom->seek_ss = 0;
-            cdrom->seek_mm = 0;
-            cdrom->cdda_msf.m = 0;
-            cdrom->cdda_msf.s = 0;
-            cdrom->cdda_msf.f = 0;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_STOP;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_pause(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            cdrom->ongoing_read_command = CDL_NONE;
-            cdrom->cdda_playing = 0;
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_PAUSE;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_PAUSE;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_init(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_INIT;
-            cdrom->ongoing_read_command = CDL_NONE;
-            cdrom->mode = 0;
-            cdrom->dfifo_index = 0;
-            cdrom->dfifo_full = 0;
-            cdrom->pfifo_index = 0;
-            cdrom->rfifo_index = 0;
-            cdrom->seek_mm = 0;
-            cdrom->seek_ss = 0;
-            cdrom->seek_ff = 0;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(cdrom->stat);
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_INIT;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(cdrom->stat);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_unmute(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index) {
-                log_fatal("CdlUnmute: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_DEMUTE;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(cdrom->stat);
-
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_setfilter(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index != 2) {
-                log_fatal("CdlSetfilter: Expected exactly 2 parameter");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->pfifo_index = 0;
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_SETFILTER;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            cdrom->delayed_command = CDL_NONE;
-    
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(cdrom->stat);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_setmode(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index != 1) {
-                log_fatal("CdlSetmode: Expected exactly 1 parameter");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            int prev_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->mode = PFIFO_POP;
-
-            if ((cdrom->mode & MODE_SPEED) != prev_speed)
-                cdrom->spin_delay = DELAY_1MS * 650;
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_SETMODE;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            cdrom->delayed_command = CDL_NONE;
-    
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) { log_fatal("getlocl: Unimplemented"); exit(1); }
-void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) {
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_GETLOCP;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(0x00);
-            RESP_PUSH(0x00);
-            RESP_PUSH(0x00);
-            RESP_PUSH(0x00);
-            RESP_PUSH(0x00);
-            RESP_PUSH(0x00);
-            RESP_PUSH(0x01);
-            RESP_PUSH(0xff);
-
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_gettn(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index) {
-                log_fatal("CdlGetTN: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_GETTN;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            int tn;
-
-            psx_disc_get_track_count(cdrom->disc, &tn);
-
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(tn);
-            RESP_PUSH(0x01);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_gettd(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index != 1) {
-                log_fatal("CdlGetTD: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->gettd_track = PFIFO_POP;
-
-            int err = psx_disc_get_track_addr(cdrom->disc, NULL, cdrom->gettd_track);
-
-            if (err) {
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_GETTD;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            msf_t td;
-
-            psx_disc_get_track_addr(cdrom->disc, &td, cdrom->gettd_track);
-
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(ITOB(td.s));
-            RESP_PUSH(ITOB(td.m));
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_seekl(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index) {
-                log_fatal("CdlSeekL: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_SEEKL;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_SEEKL;
-
-            msf_t msf;
-
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
-
-            msf_from_bcd(&msf);
-
-            psx_disc_seek(cdrom->disc, msf);
-
-            cdrom->seek_pending = 0;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_seekp(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index) {
-                log_fatal("CdlSeekP: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_SEEKP;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_SEEKP;
-
-            msf_t msf;
-
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
-
-            msf_from_bcd(&msf);
-
-            psx_disc_seek(cdrom->disc, msf);
-
-            cdrom->seek_pending = 0;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_test(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index != 1) {
-                log_fatal("CdlTest: Expected exactly 1 parameter");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            if (PFIFO_POP != 0x20) {
-                log_fatal("CdlTest: Unhandled subcommand");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_INVSUBF;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->delayed_command = CDL_TEST;
-            cdrom->state = CD_STATE_SEND_RESP1;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            cdrom->delayed_command = CDL_NONE;
-    
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(0x01);
-            RESP_PUSH(0x95);
-            RESP_PUSH(0x13);
-            RESP_PUSH(0x03);
-
-            cdrom->state = CD_STATE_RECV_CMD;
-        } break;
-    }
-}
-void cdrom_cmd_getid(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            if (cdrom->pfifo_index) {
-                log_fatal("CdlGetID: Expected exactly 0 parameters");
-
-                cdrom->irq_delay = DELAY_1MS;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
-                cdrom->error_flags = GETSTAT_ERROR;
-
-                return;
-            }
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_GETID;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_GETID;
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            if (cdrom->disc) {
-                SET_BITS(ifr, IFR_INT, 2);
-
-                switch (cdrom->cd_type) {
-                    case CDT_LICENSED: {
-                        for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
-                            RESP_PUSH(g_getid_licensed[GETID_RESPONSE_END - i]);
-                    } break;
-
-                    case CDT_AUDIO: {
-                        for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
-                            RESP_PUSH(g_getid_audio[GETID_RESPONSE_END - i]);
-                    } break;
-
-                    case CDT_UNKNOWN: {
-                        for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
-                            RESP_PUSH(g_getid_unlicensed[GETID_RESPONSE_END - i]);
-                    } break;
-                }
-            } else {
-                SET_BITS(ifr, IFR_INT, 5);
-
-                for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
-                    RESP_PUSH(g_getid_no_disc[GETID_RESPONSE_END - i]);
-            }
-
-            cdrom->state = CD_STATE_RECV_CMD;
-            cdrom->delayed_command = CDL_NONE;
-        } break;
-    }
-}
-void cdrom_cmd_reads(psx_cdrom_t* cdrom) {
-    cdrom->delayed_command = CDL_NONE;
-    cdrom->ongoing_read_command = 1;
-
-    switch (cdrom->state) {
-        case CD_STATE_RECV_CMD: {
-            log_fatal("CdlReadS: CD_STATE_RECV_CMD");
-            cdrom->irq_delay = DELAY_1MS;
-            cdrom->state = CD_STATE_SEND_RESP1;
-            cdrom->delayed_command = CDL_READS;
-        } break;
-
-        case CD_STATE_SEND_RESP1: {
-            log_fatal("CdlReadS: CD_STATE_SEND_RESP1");
-
-            SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR);
-
-            msf_t msf;
-
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
-
-            msf_from_bcd(&msf);
-
-            int err = psx_disc_seek(cdrom->disc, msf);
-
-            if (err) {
-                log_fatal("CdlReadS: Out of bounds seek");
-
-                cdrom->irq_delay = DELAY_1MS * 600;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_INVSUBF;
-                cdrom->error_flags = GETSTAT_SEEKERROR;
-
-                return;
-            }
-
-            psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
-            msf_t sector_msf;
-
-            sector_msf.m = cdrom->dfifo[0x0c];
-            sector_msf.s = cdrom->dfifo[0x0d];
-            sector_msf.f = cdrom->dfifo[0x0e];
-
-            int correct_msf = (cdrom->seek_mm == sector_msf.m) && 
-                              (cdrom->seek_ss == sector_msf.s) &&
-                              (cdrom->seek_ff == sector_msf.f);
-            
-            // Most probably audio sector:
-            // Purposefully constructed audio data could
-            // circumvent this detection code, but it will work
-            // for most intents and purposes 
-            if (!correct_msf) {
-                log_fatal("CdlReadS: Audio read");
-
-                cdrom->irq_delay = DELAY_1MS * 600;
-                cdrom->delayed_command = CDL_ERROR;
-                cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_SEEK;
-                cdrom->error_flags = GETSTAT_SEEKERROR;
-
-                return;
-            }
-            
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_READS;
-
-            if (cdrom->spin_delay) {
-                cdrom->irq_delay += cdrom->spin_delay;
-                cdrom->spin_delay = 0;
-            }
-        } break;
-
-        case CD_STATE_SEND_RESP2: {
-            log_fatal("CdlReadS: CD_STATE_SEND_RESP2");
-
-            msf_t msf;
-
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
-
-            msf_from_bcd(&msf);
-
-            psx_disc_seek(cdrom->disc, msf);
-            psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
-            if (cdrom->mode & MODE_XA_ADPCM) {
-                cdrom->state = CD_STATE_RECV_CMD;
-                cdrom->delayed_command = CDL_NONE;
-
-                return;
-            }
-
-            if (cdrom->dfifo[0x12] & 0x20) {
-                log_fatal("Unimplemented XA Form2 Sector");
-
-                // exit(1);
-            }
-
-            cdrom->seek_ff++;
-
-            if ((cdrom->seek_ff & 0xF) == 10) { cdrom->seek_ff += 0x10; cdrom->seek_ff &= 0xF0; }
-            if (cdrom->seek_ff == 0x75) { cdrom->seek_ss++; cdrom->seek_ff = 0; }
-            if ((cdrom->seek_ss & 0xF) == 10) { cdrom->seek_ss += 0x10; cdrom->seek_ss &= 0xF0; }
-            if (cdrom->seek_ss == 0x60) { cdrom->seek_mm++; cdrom->seek_ss = 0; }
-            if ((cdrom->seek_mm & 0xF) == 10) { cdrom->seek_mm += 0x10; cdrom->seek_mm &= 0xF0; }
-
-            int double_speed = cdrom->mode & MODE_SPEED;
-
-            cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
-            cdrom->state = CD_STATE_SEND_RESP2;
-            cdrom->delayed_command = CDL_READS;
-            cdrom->dfifo_index = 0;
-
-            SET_BITS(ifr, IFR_INT, IFR_INT1);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-        } break;
-    }
-}
-void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) { log_fatal("readtoc: Unimplemented"); exit(1); }
-
-typedef void (*cdrom_cmd_t)(psx_cdrom_t*);
-
-const char* g_psx_cdrom_command_names[] = {
-    "CdlUnimplemented",
-    "CdlGetstat",
-    "CdlSetloc",
-    "CdlPlay",
-    "CdlUnimplemented",
-    "CdlUnimplemented",
-    "CdlReadn",
-    "CdlMotoron",
-    "CdlStop",
-    "CdlPause",
-    "CdlInit",
-    "CdlUnimplemented",
-    "CdlUnmute",
-    "CdlSetfilter",
-    "CdlSetmode",
-    "CdlUnimplemented",
-    "CdlGetlocl",
-    "CdlGetlocp",
-    "CdlUnimplemented",
-    "CdlGettn",
-    "CdlGettd",
-    "CdlSeekl",
-    "CdlSeekp",
-    "CdlUnimplemented",
-    "CdlUnimplemented",
-    "CdlTest",
-    "CdlGetid",
-    "CdlReads",
-    "CdlUnimplemented",
-    "CdlUnimplemented",
-    "CdlReadtoc",
-    "CdlUnimplemented"
-};
-
-cdrom_cmd_t g_psx_cdrom_command_table[] = {
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_getstat,
-    cdrom_cmd_setloc,
-    cdrom_cmd_play,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_readn,
-    cdrom_cmd_motoron,
-    cdrom_cmd_stop,
-    cdrom_cmd_pause,
-    cdrom_cmd_init,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_unmute,
-    cdrom_cmd_setfilter,
-    cdrom_cmd_setmode,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_getlocl,
-    cdrom_cmd_getlocp,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_gettn,
-    cdrom_cmd_gettd,
-    cdrom_cmd_seekl,
-    cdrom_cmd_seekp,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_test,
-    cdrom_cmd_getid,
-    cdrom_cmd_reads,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_unimplemented,
-    cdrom_cmd_readtoc,
-
-    // Actually an unimplemented command, we use this
-    // index for CD error handling
-    cdrom_cmd_error
-};
-
-typedef uint8_t (*psx_cdrom_read_function_t)(psx_cdrom_t*);
-typedef void (*psx_cdrom_write_function_t)(psx_cdrom_t*, uint8_t);
-
-uint8_t cdrom_read_status(psx_cdrom_t* cdrom) {
-    return cdrom->status;
-}
-
-uint8_t cdrom_read_rfifo(psx_cdrom_t* cdrom) {
-    uint8_t data = cdrom->rfifo[--cdrom->rfifo_index];
-
-    if (cdrom->rfifo_index == 0)
-        SET_BITS(status, STAT_RSLRRDY_MASK, 0);
-
-    return data;
-}
-
-uint8_t cdrom_read_dfifo(psx_cdrom_t* cdrom) {
-    if (!cdrom->dfifo_full)
-        return 0;
-
-    int sector_size_bit = cdrom->mode & MODE_SECTOR_SIZE;
-
-    uint32_t sector_size = sector_size_bit ? 0x924 : 0x800;
-    uint32_t offset = sector_size_bit ? 12 : 24;
-
-    if (cdrom->dfifo_index < sector_size) {
-        SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
-
-        return cdrom->dfifo[offset + (cdrom->dfifo_index++)];
-    } else {
-        SET_BITS(status, STAT_DRQSTS_MASK, 0);
-
-        cdrom->dfifo_full = 0;
-    }
-
-    return 0x00;
-}
-
-uint8_t cdrom_read_ier(psx_cdrom_t* cdrom) {
-    return cdrom->ier;
-}
-
-uint8_t cdrom_read_ifr(psx_cdrom_t* cdrom) {
-    return 0xe0 | cdrom->ifr;
-}
-
-void cdrom_write_status(psx_cdrom_t* cdrom, uint8_t value) {
-    SET_BITS(status, STAT_INDEX_MASK, value);
-}
-
-void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t value) {
-    // log_set_quiet(0);
-    // log_fatal("%s(%02x) %u params=[%02x, %02x, %02x, %02x, %02x, %02x]",
-    //     g_psx_cdrom_command_names[value],
-    //     value,
-    //     cdrom->pfifo_index,
-    //     cdrom->pfifo[0],
-    //     cdrom->pfifo[1],
-    //     cdrom->pfifo[2],
-    //     cdrom->pfifo[3],
-    //     cdrom->pfifo[4],
-    //     cdrom->pfifo[5]
-    // );
-    // log_set_quiet(1);
-
-    cdrom->command = value;
-    cdrom->state = CD_STATE_RECV_CMD;
-
-    g_psx_cdrom_command_table[value](cdrom);
-}
-
-void cdrom_write_pfifo(psx_cdrom_t* cdrom, uint8_t value) {
-    cdrom->pfifo[(cdrom->pfifo_index++) & 0xf] = value;
-
-    SET_BITS(status, STAT_PRMWRDY_MASK, (cdrom->pfifo_index & 0x10) ? 0x0 : 0xff);
-
-    cdrom->pfifo_index &= 0x1f;
-}
-
-void cdrom_write_req(psx_cdrom_t* cdrom, uint8_t value) {
-    if (value & REQ_BFRD) {
-        SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
-
-        cdrom->dfifo_full = 1;
-        cdrom->dfifo_index = 0;
-    } else {
-        SET_BITS(status, STAT_DRQSTS_MASK, 0);
-
-        cdrom->dfifo_full = 0;
-        cdrom->dfifo_index = 0;
-    }
-}
-
-void cdrom_write_smdout(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Sound map data out unimplemented");
-}
-
-void cdrom_write_ier(psx_cdrom_t* cdrom, uint8_t value) {
-    cdrom->ier = value;
-}
-
-void cdrom_write_ifr(psx_cdrom_t* cdrom, uint8_t value) {
-    cdrom->ifr &= ~(value & 0x1f);
-
-    // Clear Parameter FIFO
-    if (value & 0x40) {
-        cdrom->pfifo_index = 0;
-
-        SET_BITS(
-            status,
-            (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK),
-            (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK)
-        );
-    }
-}
-
-void cdrom_write_sminfo(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Sound map coding info unimplemented");
-}
-
-void cdrom_write_lcdlspuv(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Volume registers unimplemented");
-}
-
-void cdrom_write_rcdrspuv(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Volume registers unimplemented");
-}
-
-void cdrom_write_rcdlspuv(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Volume registers unimplemented");
-}
-
-void cdrom_write_lcdrspuv(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Volume registers unimplemented");
-}
-
-void cdrom_write_volume(psx_cdrom_t* cdrom, uint8_t value) {
-    log_fatal("Volume registers unimplemented");
-}
-
-psx_cdrom_read_function_t g_psx_cdrom_read_table[] = {
-    cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ier,
-    cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ifr,
-    cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ier,
-    cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ifr
-};
-
-psx_cdrom_write_function_t g_psx_cdrom_write_table[] = {
-    cdrom_write_status, cdrom_write_cmd     , cdrom_write_pfifo   , cdrom_write_req     ,
-    cdrom_write_status, cdrom_write_smdout  , cdrom_write_ier     , cdrom_write_ifr     ,
-    cdrom_write_status, cdrom_write_sminfo  , cdrom_write_lcdlspuv, cdrom_write_lcdrspuv,
-    cdrom_write_status, cdrom_write_rcdrspuv, cdrom_write_rcdlspuv, cdrom_write_volume
-};
-
-const char* g_psx_cdrom_read_names_table[] = {
-    "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ier",
-    "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ifr",
-    "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ier",
-    "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ifr"
-};
-
-const char* g_psx_cdrom_write_names_table[] = {
-    "cdrom_write_status", "cdrom_write_cmd"     , "cdrom_write_pfifo"   , "cdrom_write_req"     ,
-    "cdrom_write_status", "cdrom_write_smdout"  , "cdrom_write_ier"     , "cdrom_write_ifr"     ,
-    "cdrom_write_status", "cdrom_write_sminfo"  , "cdrom_write_lcdlspuv", "cdrom_write_lcdrspuv",
-    "cdrom_write_status", "cdrom_write_rcdrspuv", "cdrom_write_rcdlspuv", "cdrom_write_volume"
-};
-
-psx_cdrom_t* psx_cdrom_create() {
-    return (psx_cdrom_t*)malloc(sizeof(psx_cdrom_t));
-}
-
-void psx_cdrom_init(psx_cdrom_t* cdrom, psx_ic_t* ic) {
-    memset(cdrom, 0, sizeof(psx_cdrom_t));
-
-    cdrom->io_base = PSX_CDROM_BEGIN;
-    cdrom->io_size = PSX_CDROM_SIZE;
-
-    cdrom->ic = ic;
-    cdrom->status = STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK | STAT_RSLRRDY_MASK;
-    cdrom->dfifo = malloc(CD_SECTOR_SIZE);
-    cdrom->cdda_buf = malloc(CD_SECTOR_SIZE);
-}
-
-uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t offset) {
-    log_fatal("Unhandled 32-bit CDROM read at offset %08x", offset);
-
-    return 0x0;
-}
-
-uint16_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t offset) {
-    log_fatal("Unhandled 16-bit CDROM read at offset %08x", offset);
-
-    return 0x0;
-}
-
-uint8_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t offset) {
-    uint8_t data = g_psx_cdrom_read_table[(STAT_INDEX << 2) | offset](cdrom);
-
-    // log_fatal("%s (read %02x)", g_psx_cdrom_read_names_table[(STAT_INDEX << 2) | offset], data);
-
-    return data;
-}
-
-void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t offset, uint32_t value) {
-    log_fatal("Unhandled 32-bit CDROM write at offset %08x (%08x)", offset, value);
-}
-
-void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t offset, uint16_t value) {
-    log_fatal("Unhandled 16-bit CDROM write at offset %08x (%04x)", offset, value);
-}
-
-void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t offset, uint8_t value) {
-    // log_fatal("%s (write %02x)", g_psx_cdrom_write_names_table[(STAT_INDEX << 2) | offset], value);
-
-    g_psx_cdrom_write_table[(STAT_INDEX << 2) | offset](cdrom, value);
-}
-
-void psx_cdrom_update(psx_cdrom_t* cdrom, int cyc) {
-    if (cdrom->irq_delay) {
-        cdrom->irq_delay -= cyc;
-
-        if (cdrom->irq_delay <= 0) {
-            psx_ic_irq(cdrom->ic, IC_CDROM);
-
-            cdrom->irq_delay = 0;
-
-            if (cdrom->delayed_command)
-                g_psx_cdrom_command_table[cdrom->delayed_command](cdrom);
-        }
-    }
-}
-
-const char* g_psx_cdrom_extensions[] = {
-    "cue",
-    "bin",
-    0
-};
-
-enum {
-    CD_EXT_CUE,
-    CD_EXT_BIN,
-    CD_EXT_UNSUPPORTED
-};
-
-int cdrom_get_extension(const char* path) {
-    const char* ptr = &path[strlen(path) - 1];
-    int i = 0;
-
-    while ((*ptr != '.') && (ptr != path))
-        ptr--;
-    
-    if (ptr == path)
-        return CD_EXT_UNSUPPORTED;
-
-    while (g_psx_cdrom_extensions[i]) {
-        if (!strcmp(ptr + 1, g_psx_cdrom_extensions[i]))
-            return i;
-        
-        ++i;
-    }
-
-    return CD_EXT_UNSUPPORTED;
-}
-
-void cdrom_check_cd_type(psx_cdrom_t* cdrom) {
-    char buf[CD_SECTOR_SIZE];
-
-    // Seek to Primary Volume Descriptor
-    msf_t pvd = { 0, 2, 16 };
-
-    // If the disc is smaller than 16 sectors
-    // then it can't be a PlayStation game.
-    // Audio discs should also have ISO volume
-    // descriptors, so it's probably something else
-    // entirely.
-    if (psx_disc_seek(cdrom->disc, pvd)) {
-        cdrom->cd_type = CDT_UNKNOWN;
-
-        return;
-    }
-
-    psx_disc_read_sector(cdrom->disc, buf);
-
-    // Check for the "PLAYSTATION" string at PVD offset 20h
-
-    // Patch 20 byte so comparison is done correctly
-    buf[0x2b] = 0;
-
-    if (strncmp(&buf[0x20], "PLAYSTATION", 12)) {
-        cdrom->cd_type = CDT_AUDIO;
-
-        return;
-    }
-
-    cdrom->cd_type = CDT_LICENSED;
-}
-
-void psx_cdrom_open(psx_cdrom_t* cdrom, const char* path) {
-    cdrom->disc = psx_disc_create();
-
-    int ext = cdrom_get_extension(path);
-    int error = 0;
-
-    switch (ext) {
-        case CD_EXT_CUE: {
-            psxd_cue_t* cue = psxd_cue_create();
-
-            psxd_cue_init_disc(cue, cdrom->disc);
-            psxd_cue_init(cue);
-            error = psxd_cue_load(cue, path);
-
-            if (error)
-                break;
-
-            cdrom_check_cd_type(cdrom);
-        } break;
-
-        case CD_EXT_BIN: {
-            psxd_bin_t* bin = psxd_bin_create();
-
-            psxd_bin_init_disc(bin, cdrom->disc);
-            psxd_bin_init(bin);
-
-            error = psxd_bin_load(bin, path);
-
-            if (error)
-                break;
-
-            cdrom_check_cd_type(cdrom);
-        } break;
-
-        case CD_EXT_UNSUPPORTED: {
-            log_fatal("Unsupported disc format");
-
-            cdrom->cd_type = CDT_UNKNOWN;
-        } break;
-    }
-
-    if (error) {
-        log_fatal("Error loading file \'%s\'", path);
-
-        exit(1);
-    }
-}
-
-void psx_cdrom_get_cdda_samples(psx_cdrom_t* cdrom, void* buf, int size, psx_spu_t* spu) {
-    if (!cdrom->cdda_playing) {
-        memset(buf, 0, size);
-    
-        return;
-    }
-
-    if (!cdrom->disc)
-        return;
-
-    // Convert seek to I
-    msf_t msf = cdrom->cdda_msf;
-    
-    msf_from_bcd(&msf);
-
-    // Seek to that address and read sector
-    if (psx_disc_seek(cdrom->disc, msf)) {
-        cdrom->cdda_playing = 0;
-    }
-
-    psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
-
-    ++cdrom->cdda_sectors_played;
-
-    // Increment sector
-    msf_add_f(&msf, 1);
-    msf_to_bcd(&msf);
-    
-    // Assign to CDDA MSF
-    cdrom->cdda_msf = msf;
-
-    memcpy(buf, cdrom->cdda_buf, size);
-
-    psx_spu_update_cdda_buffer(spu, cdrom->cdda_buf);
-
-    // Handle report IRQ
-    if (cdrom->cdda_sectors_played == CD_SECTORS_PS) {
-        if (cdrom->mode & MODE_REPORT) {
-            SET_BITS(ifr, IFR_INT, 1);
-
-            msf_t track, current = cdrom->cdda_msf;
-
-            msf_from_bcd(&current);
-
-            psx_disc_get_track_addr(cdrom->disc, &track, cdrom->cdda_track);
-
-            unsigned int track_s = (track.m * 60) + track.s;
-            unsigned int current_s = (current.m * 60) + current.s;
-            unsigned int diff = current_s - track_s;
-
-            current.s = diff;
-            current.m = 0;
-
-            msf_adjust(&current);
-            msf_to_bcd(&current);
-
-            RESP_PUSH(0);
-            RESP_PUSH(0);
-            RESP_PUSH(cdrom->cdda_msf.f);
-            RESP_PUSH(current.s | 0x80);
-            RESP_PUSH(current.m);
-            RESP_PUSH(0);
-            RESP_PUSH(cdrom->cdda_track);
-            RESP_PUSH(GETSTAT_PLAY);
-
-            psx_ic_irq(cdrom->ic, IC_CDROM);
-        }
-
-        cdrom->cdda_sectors_played = 0;
-    }
-}
-
-void psx_cdrom_destroy(psx_cdrom_t* cdrom) {
-    if (cdrom->disc)
-        psx_disc_destroy(cdrom->disc);
-
-    free(cdrom);
-}
\ No newline at end of file
--- a/psx/dev/pad.c
+++ b/psx/dev/pad.c
@@ -135,7 +135,7 @@
     }
 }
 
-psx_pad_t* psx_pad_create() {
+psx_pad_t* psx_pad_create(void) {
     return (psx_pad_t*)malloc(sizeof(psx_pad_t));
 }
 
--- a/psx/dev/ram.c
+++ b/psx/dev/ram.c
@@ -5,7 +5,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-psx_ram_t* psx_ram_create() {
+psx_ram_t* psx_ram_create(void) {
     return (psx_ram_t*)malloc(sizeof(psx_ram_t));
 }
 
--- a/psx/dev/rework/gpu.c
+++ /dev/null
@@ -1,1773 +1,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "gpu.h"
-#include "../log.h"
-
-int g_psx_gpu_dither_kernel[] = {
-    -4, +0, -3, +1,
-    +2, -2, +3, -1,
-    -3, +1, -4, +0,
-    +3, -1, +2, -2,
-};
-
-uint16_t gpu_to_bgr555(uint32_t color) {
-    return ((color & 0x0000f8) >> 3) |
-           ((color & 0x00f800) >> 6) |
-           ((color & 0xf80000) >> 9);
-}
-
-#define BGR555(c) \
-    (((c & 0x0000f8) >> 3) | \
-     ((c & 0x00f800) >> 6) | \
-     ((c & 0xf80000) >> 9))
-
-// #define BGR555(c) gpu_to_bgr555(c)
-
-#define VRAM(x, y) gpu->vram[(x) + ((y) * 1024)]
-
-int min3(int a, int b, int c) {
-    int m = a < b ? a : b;
-
-    return m < c ? m : c;
-}
-
-int max3(int a, int b, int c) {
-    int m = a > b ? a : b;
-
-    return m > c ? m : c;
-}
-
-psx_gpu_t* psx_gpu_create() {
-    return (psx_gpu_t*)malloc(sizeof(psx_gpu_t));
-}
-
-void psx_gpu_init(psx_gpu_t* gpu, psx_ic_t* ic) {
-    memset(gpu, 0, sizeof(psx_gpu_t));
-
-    gpu->io_base = PSX_GPU_BEGIN;
-    gpu->io_size = PSX_GPU_SIZE;
-
-    gpu->vram = (uint16_t*)malloc(PSX_GPU_VRAM_SIZE);
-    gpu->state = GPU_STATE_RECV_CMD;
-
-    gpu->ic = ic;
-}
-
-uint32_t psx_gpu_read32(psx_gpu_t* gpu, uint32_t offset) {
-    switch (offset) {
-        case 0x00: {
-            uint32_t data = 0x0;
-
-            if (gpu->c0_tsiz) {
-                data |= gpu->vram[gpu->c0_addr + (gpu->c0_xcnt + (gpu->c0_ycnt * 1024))];
-
-                gpu->c0_xcnt += 1;
-
-                if (gpu->c0_xcnt == gpu->c0_xsiz) {
-                    gpu->c0_ycnt += 1;
-                    gpu->c0_xcnt = 0;
-                }
-
-                data |= gpu->vram[gpu->c0_addr + (gpu->c0_xcnt + (gpu->c0_ycnt * 1024))] << 16;
-
-                gpu->c0_xcnt += 1;
-
-                if (gpu->c0_xcnt == gpu->c0_xsiz) {
-                    gpu->c0_ycnt += 1;
-                    gpu->c0_xcnt = 0;
-                }
-
-                gpu->c0_tsiz -= 2;
-            }
-
-            if (gpu->gp1_10h_req) {
-                switch (gpu->gp1_10h_req & 7) {
-                    case 2: {
-                        data = ((gpu->texw_oy / 8) << 15) | ((gpu->texw_ox / 8) << 10) | ((gpu->texw_my / 8) << 5) | (gpu->texw_mx / 8);
-                    } break;
-                    case 3: {
-                        data = (gpu->draw_y1 << 10) | gpu->draw_x1;
-                    } break;
-                    case 4: {
-                        data = (gpu->draw_y2 << 10) | gpu->draw_x2;
-                    } break;
-                    case 5: {
-                        data = (gpu->off_y << 10) | gpu->off_x;
-                    } break;
-                }
-
-                gpu->gp1_10h_req = 0;
-            }
-
-            return data;
-        } break;
-        case 0x04: return gpu->gpustat | 0x1e000000;
-    }
-
-    log_warn("Unhandled 32-bit GPU read at offset %08x", offset);
-
-    return 0x0;
-}
-
-uint16_t psx_gpu_read16(psx_gpu_t* gpu, uint32_t offset) {
-    log_fatal("Unhandled 16-bit GPU read at offset %08x", offset);
-}
-
-uint8_t psx_gpu_read8(psx_gpu_t* gpu, uint32_t offset) {
-    log_fatal("Unhandled 8-bit GPU read at offset %08x", offset);
-}
-
-int min(int x0, int x1) {
-    return (x0 < x1) ? x0 : x1;
-}
-
-int max(int x0, int x1) {
-    return (x0 > x1) ? x0 : x1;
-}
-
-#define EDGE(a, b, c) ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x))
-
-uint16_t gpu_fetch_texel(psx_gpu_t* gpu, uint16_t tx, uint16_t ty, uint32_t tpx, uint32_t tpy, uint16_t clutx, uint16_t cluty, int depth) {
-    tx = (tx & ~gpu->texw_mx) | (gpu->texw_ox & gpu->texw_mx);
-    ty = (ty & ~gpu->texw_my) | (gpu->texw_oy & gpu->texw_my);
-    tx &= 0xff;
-    ty &= 0xff;
-
-    switch (depth) {
-        // 4-bit
-        case 0: {
-            uint16_t texel = VRAM(tpx + (tx >> 2), tpy + ty);
-            uint16_t index = (texel >> ((tx & 0x3) << 2)) & 0xf;
-
-            return VRAM(clutx + index, cluty);
-        } break;
-
-        // 8-bit
-        case 1: {
-            uint16_t texel = VRAM(tpx + (tx >> 1), tpy + ty);
-            uint16_t index = (texel >> ((tx & 0x1) << 3)) & 0xff;
-
-            return VRAM(clutx + index, cluty);
-        } break;
-
-        // 15-bit
-        default: {
-            return VRAM(tpx + tx, tpy + ty);
-        } break;
-    }
-}
-
-void gpu_render_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, poly_data_t data) {
-    vertex_t a, b, c, p;
-
-    int tpx = (data.texp & 0xf) << 6;
-    int tpy = (data.texp & 0x10) << 4;
-    int clutx = (data.clut & 0x3f) << 4;
-    int cluty = (data.clut >> 6) & 0x1ff;
-    int depth = (data.texp >> 7) & 3;
-    int transp = (data.attrib & PA_TRANSP) != 0;
-    int transp_mode;
-
-    if (data.attrib & PA_TEXTURED) {
-        transp_mode = (data.texp >> 5) & 3;
-    } else {
-        transp_mode = (gpu->gpustat >> 5) & 3;
-    }
-
-    a = v0;
-
-    /* Ensure the winding order is correct */
-    if (EDGE(v0, v1, v2) < 0) {
-        b = v2;
-        c = v1;
-    } else {
-        b = v1;
-        c = v2;
-    }
-
-    a.x += gpu->off_x;
-    a.y += gpu->off_y;
-    b.x += gpu->off_x;
-    b.y += gpu->off_y;
-    c.x += gpu->off_x;
-    c.y += gpu->off_y;
-
-    int xmin = min3(a.x, b.x, c.x);
-    int ymin = min3(a.y, b.y, c.y);
-    int xmax = max3(a.x, b.x, c.x); 
-    int ymax = max3(a.y, b.y, c.y);
-
-    float area = EDGE(a, b, c);
-
-    for (int y = ymin; y < ymax; y++) {
-        for (int x = xmin; x < xmax; x++) {
-            p.x = x;
-            p.y = y;
-
-            float z0 = EDGE(b, c, p);
-            float z1 = EDGE(c, a, p);
-            float z2 = EDGE(a, b, p);
-
-            if ((z0 < 0) || (z1 < 0) || (z2 < 0))
-                continue;
-            
-            int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
-                     (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
-            
-            if (!bc)
-                continue;
-
-            uint16_t color = 0;
-            uint32_t mod   = 0;
-
-            if (data.attrib & PA_SHADED) {
-                int cr = (z0 * ((a.c >>  0) & 0xff) + z1 * ((b.c >>  0) & 0xff) + z2 * ((c.c >>  0) & 0xff)) / area;
-                int cg = (z0 * ((a.c >>  8) & 0xff) + z1 * ((b.c >>  8) & 0xff) + z2 * ((c.c >>  8) & 0xff)) / area;
-                int cb = (z0 * ((a.c >> 16) & 0xff) + z1 * ((b.c >> 16) & 0xff) + z2 * ((c.c >> 16) & 0xff)) / area;
-
-                int dy = (y - ymin) & 3;
-                int dx = (x - xmin) & 3;
-
-                int dither = g_psx_gpu_dither_kernel[dx + (dy * 4)];
-
-                cr += dither;
-                cg += dither;
-                cb += dither;
-
-                // Saturate (clamp) to 00-ff
-                cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
-                cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
-                cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
-
-                uint32_t rgb = (cb << 16) | (cg << 8) | cr;
-
-                mod = rgb;
-            } else {
-                mod = data.v[0].c;
-            }
-
-            if (data.attrib & PA_TEXTURED) {
-                uint32_t tx = ((z0 * a.tx) + (z1 * b.tx) + (z2 * c.tx)) / area;
-                uint32_t ty = ((z0 * a.ty) + (z1 * b.ty) + (z2 * c.ty)) / area;
-
-                uint16_t texel = gpu_fetch_texel(gpu, tx, ty, tpx, tpy, clutx, cluty, depth);
-
-                if (!texel)
-                    continue;
-
-                if (transp) {
-                    transp = (texel & 0x8000) != 0;
-                }
-
-                if (data.attrib & PA_RAW) {
-                    color = texel;
-                } else {
-                    int tr = ((texel >> 0 ) & 0x1f) << 3;
-                    int tg = ((texel >> 5 ) & 0x1f) << 3;
-                    int tb = ((texel >> 10) & 0x1f) << 3;
-
-                    int mr = (mod >> 0 ) & 0xff;
-                    int mg = (mod >> 8 ) & 0xff;
-                    int mb = (mod >> 16) & 0xff;
-
-                    int cr = (tr * mr) / 0x80;
-                    int cg = (tg * mg) / 0x80;
-                    int cb = (tb * mb) / 0x80;
-
-                    cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
-                    cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
-                    cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
-
-                    uint32_t rgb = cr | (cg << 8) | (cb << 16);
-
-                    color = BGR555(rgb);
-                }
-            } else {
-                color = BGR555(mod);
-            }
-
-            int cr = ((color >> 0 ) & 0x1f) << 3;
-            int cg = ((color >> 5 ) & 0x1f) << 3;
-            int cb = ((color >> 10) & 0x1f) << 3;
-
-            if (transp) {
-                uint16_t back = VRAM(x, y);
-
-                int br = ((back >> 0 ) & 0x1f) << 3;
-                int bg = ((back >> 5 ) & 0x1f) << 3;
-                int bb = ((back >> 10) & 0x1f) << 3;
-
-                switch (transp_mode) {
-                    case 0: {
-                        cr = (0.5f * br) + (0.5f * cr);
-                        cg = (0.5f * bg) + (0.5f * cg);
-                        cb = (0.5f * bb) + (0.5f * cb);
-                    } break;
-                    case 1: {
-                        cr = br + cr;
-                        cg = bg + cg;
-                        cb = bb + cb;
-                    } break;
-                    case 2: {
-                        cr = br - cr;
-                        cg = bg - cg;
-                        cb = bb - cb;
-                    } break;
-                    case 3: {
-                        cr = br + (0.25 * cr);
-                        cg = bg + (0.25 * cg);
-                        cb = bb + (0.25 * cb);
-                    } break;
-                }
-
-                cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
-                cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
-                cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
-
-                uint32_t rgb = cr | (cg << 8) | (cb << 16);
-
-                color = BGR555(rgb);
-            }
-
-            VRAM(x, y) = color;
-        }
-    }
-}
-
-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;
-    }
-
-    int textured = (data.attrib & RA_TEXTURED) != 0;
-    int transp = (data.attrib & RA_TRANSP) != 0;
-    int transp_mode = (gpu->gpustat >> 5) & 3;
-
-    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;
-
-    /* Calculate bounding box */
-    int xmax = data.v0.x + width;
-    int ymax = data.v0.y + height;
-
-    int32_t xc = 0, yc = 0;
-
-    for (int16_t y = data.v0.y; y < ymax; y++) {
-        for (int16_t x = data.v0.x; x < xmax; x++) {
-            int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
-                     (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
-
-            if (!bc)
-                goto skip;
-
-            uint16_t color;
-
-            if (textured) {
-                uint16_t texel = gpu_fetch_texel(
-                    gpu,
-                    data.v0.tx + xc, data.v0.ty + yc,
-                    gpu->texp_x, gpu->texp_y,
-                    clutx, cluty,
-                    gpu->texp_d
-                );
-
-                if (!texel)
-                    goto skip;
-
-                if (transp) {
-                    transp = (texel & 0x8000) != 0;
-                }
-
-                int tr = ((texel >> 0 ) & 0x1f) << 3;
-                int tg = ((texel >> 5 ) & 0x1f) << 3;
-                int tb = ((texel >> 10) & 0x1f) << 3;
-
-                int mr = (data.v0.c >> 0 ) & 0xff;
-                int mg = (data.v0.c >> 8 ) & 0xff;
-                int mb = (data.v0.c >> 16) & 0xff;
-
-                int cr = (tr * mr) / 0x80;
-                int cg = (tg * mg) / 0x80;
-                int cb = (tb * mb) / 0x80;
-
-                uint32_t rgb = cr | (cg << 8) | (cb << 16);
-
-                color = BGR555(rgb);
-            } else {
-                color = BGR555(data.v0.c);
-            }
-
-            int cr = ((color >> 0 ) & 0x1f) << 3;
-            int cg = ((color >> 5 ) & 0x1f) << 3;
-            int cb = ((color >> 10) & 0x1f) << 3;
-
-            if (transp) {
-                uint16_t back = VRAM(x, y);
-
-                int br = ((back >> 0 ) & 0x1f) << 3;
-                int bg = ((back >> 5 ) & 0x1f) << 3;
-                int bb = ((back >> 10) & 0x1f) << 3;
-
-                switch (transp_mode) {
-                    case 0: {
-                        cr = (0.5f * br) + (0.5f * cr);
-                        cg = (0.5f * bg) + (0.5f * cg);
-                        cb = (0.5f * bb) + (0.5f * cb);
-                    } break;
-                    case 1: {
-                        cr = br + cr;
-                        cg = bg + cg;
-                        cb = bb + cb;
-                    } break;
-                    case 2: {
-                        cr = br - cr;
-                        cg = bg - cg;
-                        cb = bb - cb;
-                    } break;
-                    case 3: {
-                        cr = br + (0.25f * cr);
-                        cg = bg + (0.25f * cg);
-                        cb = bb + (0.25f * cb);
-                    } break;
-                }
-
-                cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
-                cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
-                cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
-
-                uint32_t rgb = cr | (cg << 8) | (cb << 16);
-
-                color = BGR555(rgb);
-            }
-
-            VRAM(x, y) = color;
-
-            skip:
-
-            ++xc;
-        }
-
-        xc = 0;
-
-        ++yc;
-    }
-}
-
-void plotLineLow(psx_gpu_t* gpu, int x0, int y0, int x1, int y1, uint16_t color) {
-    int dx = x1 - x0;
-    int dy = y1 - y0;
-    int yi = 1;
-    if (dy < 0) {
-        yi = -1;
-        dy = -dy;
-    }
-    int d = (2 * dy) - dx;
-    int y = y0;
-
-    for (int x = x0; x < x1; x++) {
-        if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
-            VRAM(x, y) = color;
-
-        if (d > 0) {
-            y += yi;
-            d += (2 * (dy - dx));
-        } else {
-            d += 2*dy;
-        }
-    }
-}
-
-void plotLineHigh(psx_gpu_t* gpu, int x0, int y0, int x1, int y1, uint16_t color) {
-    int dx = x1 - x0;
-    int dy = y1 - y0;
-    int xi = 1;
-    if (dx < 0) {
-        xi = -1;
-        dx = -dx;
-    }
-    int d = (2 * dx) - dy;
-    int x = x0;
-
-    for (int y = y0; y < y1; y++) {
-        if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
-            VRAM(x, y) = color;
-
-        if (d > 0) {
-            x = x + xi;
-            d += (2 * (dx - dy));
-        } else {
-            d += 2*dx;
-        }
-    }
-}
-
-void plotLine(psx_gpu_t* gpu, int x0, int y0, int x1, int y1, uint16_t color) {
-    if (abs(y1 - y0) < abs(x1 - x0)) {
-        if (x0 > x1) {
-            plotLineLow(gpu, x1, y1, x0, y0, color);
-        } else {
-            plotLineLow(gpu, x0, y0, x1, y1, color);
-        }
-    } else {
-        if (y0 > y1) {
-            plotLineHigh(gpu, x1, y1, x0, y0, color);
-        } else {
-            plotLineHigh(gpu, x0, y0, x1, y1, color);
-        }
-    }
-}
-
-void gpu_render_flat_line(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, uint32_t color) {
-    plotLine(gpu, v0.x, v0.y, v1.x, v1.y, color);
-}
-
-void gpu_render_flat_rectangle(psx_gpu_t* gpu, vertex_t v, uint32_t w, uint32_t h, uint32_t color) {
-    /* Offset coordinates */
-    v.x += gpu->off_x;
-    v.y += gpu->off_y;
-
-    /* Calculate bounding box */
-    int xmin = max(v.x, gpu->draw_x1);
-    int ymin = max(v.y, gpu->draw_y1);
-    int xmax = min(xmin + w, gpu->draw_x2);
-    int ymax = min(ymin + h, gpu->draw_y2);
-
-    for (uint32_t y = ymin; y < ymax; y++)
-        for (uint32_t x = xmin; x < xmax; x++)
-            VRAM(x, y) = color;
-}
-
-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;
-
-    a.x += gpu->off_x;
-    a.y += gpu->off_y;
-
-    int xmin = max(a.x, gpu->draw_x1);
-    int ymin = max(a.y, gpu->draw_y1);
-    int xmax = min(xmin + w, gpu->draw_x2);
-    int ymax = min(ymin + h, gpu->draw_y2);
-
-    uint32_t xc = 0, yc = 0;
-
-    for (int y = ymin; y < ymax; y++) {
-        for (int x = xmin; x < xmax; x++) {
-            uint16_t texel = gpu_fetch_texel(
-                gpu,
-                a.tx + xc, a.ty + yc,
-                gpu->texp_x, gpu->texp_y,
-                clutx, cluty,
-                gpu->texp_d
-            );
-
-            ++xc;
-
-            gpu->vram[x + (y * 1024)] = texel;
-        }
-
-        xc = 0;
-
-        ++yc;
-    }
-}
-
-void gpu_render_flat_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, uint32_t color) {
-    vertex_t a, b, c;
-
-    a = v0;
-
-    /* Ensure the winding order is correct */
-    if (EDGE(v0, v1, v2) < 0) {
-        b = v2;
-        c = v1;
-    } else {
-        b = v1;
-        c = v2;
-    }
-
-    a.x += gpu->off_x;
-    a.y += gpu->off_y;
-    b.x += gpu->off_x;
-    b.y += gpu->off_y;
-    c.x += gpu->off_x;
-    c.y += gpu->off_y;
-
-    int xmin = max(min(min(a.x, b.x), c.x), gpu->draw_x1);
-    int ymin = max(min(min(a.y, b.y), c.y), gpu->draw_y1);
-    int xmax = min(max(max(a.x, b.x), c.x), gpu->draw_x2); 
-    int ymax = min(max(max(a.y, b.y), c.y), gpu->draw_y2);
-
-    for (int y = ymin; y < ymax; y++) {
-        for (int x = xmin; x < xmax; x++) {
-            int z0 = ((b.x - a.x) * (y - a.y)) - ((b.y - a.y) * (x - a.x));
-            int z1 = ((c.x - b.x) * (y - b.y)) - ((c.y - b.y) * (x - b.x));
-            int z2 = ((a.x - c.x) * (y - c.y)) - ((a.y - c.y) * (x - c.x));
-
-            if ((z0 >= 0) && (z1 >= 0) && (z2 >= 0)) {
-                gpu->vram[x + (y * 1024)] = BGR555(color);
-            }
-        }
-    }
-}
-
-void gpu_render_shaded_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2) {
-    vertex_t a, b, c, p;
-
-    a = v0;
-
-    /* Ensure the winding order is correct */
-    if (EDGE(v0, v1, v2) < 0) {
-        b = v2;
-        c = v1;
-    } else {
-        b = v1;
-        c = v2;
-    }
-
-    a.x += gpu->off_x;
-    a.y += gpu->off_y;
-    b.x += gpu->off_x;
-    b.y += gpu->off_y;
-    c.x += gpu->off_x;
-    c.y += gpu->off_y;
-
-    int xmin = max(min(min(a.x, b.x), c.x), gpu->draw_x1);
-    int ymin = max(min(min(a.y, b.y), c.y), gpu->draw_y1);
-    int xmax = min(max(max(a.x, b.x), c.x), gpu->draw_x2); 
-    int ymax = min(max(max(a.y, b.y), c.y), gpu->draw_y2);
-
-    int area = EDGE(a, b, c);
-
-    for (int y = ymin; y < ymax; y++) {
-        for (int x = xmin; x < xmax; x++) {
-            p.x = x;
-            p.y = y;
-
-            float z0 = EDGE((float)b, (float)c, (float)p);
-            float z1 = EDGE((float)c, (float)a, (float)p);
-            float z2 = EDGE((float)a, (float)b, (float)p);
-
-            if ((z0 >= 0) && (z1 >= 0) && (z2 >= 0)) {
-                int cr = (z0 * ((a.c >>  0) & 0xff) + z1 * ((b.c >>  0) & 0xff) + z2 * ((c.c >>  0) & 0xff)) / area;
-                int cg = (z0 * ((a.c >>  8) & 0xff) + z1 * ((b.c >>  8) & 0xff) + z2 * ((c.c >>  8) & 0xff)) / area;
-                int cb = (z0 * ((a.c >> 16) & 0xff) + z1 * ((b.c >> 16) & 0xff) + z2 * ((c.c >> 16) & 0xff)) / area;
-
-                // Calculate positions within our 4x4 dither
-                // kernel
-                int dy = (y - ymin) % 4;
-                int dx = (x - xmin) % 4;
-
-                // Shift two pixels horizontally on the last
-                // two scanlines?
-                // if (dy > 1) {
-                //     dx = ((x + 2) - xmin) % 4;
-                // }
-
-                int dither = g_psx_gpu_dither_kernel[dx + (dy * 4)];
-
-                // Add to the original 8-bit color values
-                cr += dither;
-                cg += dither;
-                cb += dither;
-
-                // Saturate (clamp) to 00-ff
-                cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
-                cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
-                cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
-
-                uint32_t color = (cb << 16) | (cg << 8) | cr;
-
-                gpu->vram[x + (y * 1024)] = BGR555(color);
-            }
-        }
-    }
-}
-
-void gpu_render_textured_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, uint32_t tpx, uint32_t tpy, uint16_t clutx, uint16_t cluty, int depth) {
-    vertex_t a, b, c;
-
-    a = v0;
-
-    /* Ensure the winding order is correct */
-    if (EDGE(v0, v1, v2) < 0) {
-        b = v2;
-        c = v1;
-    } else {
-        b = v1;
-        c = v2;
-    }
-
-    a.x += gpu->off_x;
-    a.y += gpu->off_y;
-    b.x += gpu->off_x;
-    b.y += gpu->off_y;
-    c.x += gpu->off_x;
-    c.y += gpu->off_y;
-
-    int xmin = max(min(min(a.x, b.x), c.x), gpu->draw_x1);
-    int ymin = max(min(min(a.y, b.y), c.y), gpu->draw_y1);
-    int xmax = min(max(max(a.x, b.x), c.x), gpu->draw_x2); 
-    int ymax = min(max(max(a.y, b.y), c.y), gpu->draw_y2);
-
-    uint32_t area = EDGE(a, b, c);
-
-    for (int y = ymin; y < ymax; y++) {
-        for (int x = xmin; x < xmax; x++) {
-            vertex_t p;
-
-            p.x = x;
-            p.y = y;
-
-            float z0 = EDGE((float)b, (float)c, (float)p);
-            float z1 = EDGE((float)c, (float)a, (float)p);
-            float z2 = EDGE((float)a, (float)b, (float)p);
-
-            if ((z0 >= 0) && (z1 >= 0) && (z2 >= 0)) {
-                uint32_t tx = ((z0 * a.tx) + (z1 * b.tx) + (z2 * c.tx)) / area;
-                uint32_t ty = ((z0 * a.ty) + (z1 * b.ty) + (z2 * c.ty)) / area;
-
-                uint16_t color = gpu_fetch_texel(
-                    gpu,
-                    tx, ty,
-                    tpx, tpy,
-                    clutx, cluty,
-                    depth
-                );
-
-                if (!color) continue;
-
-                gpu->vram[x + (y * 1024)] = color;
-            }
-        }
-    }
-}
-
-void gpu_rect(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-
-            int size = (gpu->buf[0] >> 27) & 3;
-            int textured = (gpu->buf[0] & 0x04000000) != 0;
-
-            gpu->cmd_args_remaining = 1 + (size == RS_VARIABLE) + textured;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                rect_data_t rect;
-
-                rect.attrib = gpu->buf[0] >> 24;
-
-                int textured = (rect.attrib & RA_TEXTURED) != 0;
-                int raw      = (rect.attrib & RA_RAW) != 0;
-
-                // Add 1 if is textured
-                int size_offset = 2 + textured;
-
-                rect.v0.c   = gpu->buf[0] & 0xffffff;
-                rect.v0.x   = gpu->buf[1] & 0xffff;
-                rect.v0.y   = gpu->buf[1] >> 16;
-                rect.v0.tx  = (gpu->buf[2] >> 0) & 0xff;
-                rect.v0.ty  = (gpu->buf[2] >> 8) & 0xff;
-                rect.clut   = gpu->buf[2] >> 16;
-                rect.width  = gpu->buf[size_offset] & 0xffff;
-                rect.height = gpu->buf[size_offset] >> 16;
-
-                if (textured && raw)
-                    rect.v0.c = 0x808080;
-                
-                gpu_render_rect(gpu, rect);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_poly(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-
-            int shaded   = (gpu->buf[0] & 0x10000000) != 0;
-            int quad     = (gpu->buf[0] & 0x08000000) != 0;
-            int textured = (gpu->buf[0] & 0x04000000) != 0;
-
-            int fields_per_vertex = 1 + shaded + textured;
-            int vertices = 3 + quad;
- 
-            gpu->cmd_args_remaining = (fields_per_vertex * vertices) - shaded;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                poly_data_t poly;
-
-                poly.attrib = gpu->buf[0] >> 24;
-
-                int shaded   = (poly.attrib & PA_SHADED) != 0;
-                int textured = (poly.attrib & PA_TEXTURED) != 0;
-
-                int color_offset = shaded * (2 + textured);
-                int vert_offset = 1 + (textured | shaded) +
-                                      (textured & shaded);
-                int texc_offset = textured * (2 + shaded);
-                int texp_offset = textured * (4 + shaded);
-
-                poly.clut = gpu->buf[2] >> 16;
-                poly.texp = gpu->buf[texp_offset] >> 16;
-
-                poly.v[0].c = gpu->buf[0+0*color_offset] & 0xffffff;
-                poly.v[1].c = gpu->buf[0+1*color_offset] & 0xffffff;
-                poly.v[2].c = gpu->buf[0+2*color_offset] & 0xffffff;
-                poly.v[3].c = gpu->buf[0+3*color_offset] & 0xffffff;
-                poly.v[0].x = gpu->buf[1+0*vert_offset] & 0xffff;
-                poly.v[1].x = gpu->buf[1+1*vert_offset] & 0xffff;
-                poly.v[2].x = gpu->buf[1+2*vert_offset] & 0xffff;
-                poly.v[3].x = gpu->buf[1+3*vert_offset] & 0xffff;
-                poly.v[0].y = gpu->buf[1+0*vert_offset] >> 16;
-                poly.v[1].y = gpu->buf[1+1*vert_offset] >> 16;
-                poly.v[2].y = gpu->buf[1+2*vert_offset] >> 16;
-                poly.v[3].y = gpu->buf[1+3*vert_offset] >> 16;
-                poly.v[0].tx = gpu->buf[2+0*texc_offset] & 0xff;
-                poly.v[1].tx = gpu->buf[2+1*texc_offset] & 0xff;
-                poly.v[2].tx = gpu->buf[2+2*texc_offset] & 0xff;
-                poly.v[3].tx = gpu->buf[2+3*texc_offset] & 0xff;
-                poly.v[0].ty = (gpu->buf[2+0*texc_offset] >> 8) & 0xff;
-                poly.v[1].ty = (gpu->buf[2+1*texc_offset] >> 8) & 0xff;
-                poly.v[2].ty = (gpu->buf[2+2*texc_offset] >> 8) & 0xff;
-                poly.v[3].ty = (gpu->buf[2+3*texc_offset] >> 8) & 0xff;
-
-                if (poly.attrib & PA_QUAD) {
-                    gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly);
-                    gpu_render_triangle(gpu, poly.v[1], poly.v[2], poly.v[3], poly);
-                } else {
-                    gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly);
-                }
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_copy(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 3;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                uint32_t srcx = gpu->buf[1] & 0xffff;
-                uint32_t srcy = gpu->buf[1] >> 16;
-                uint32_t dstx = gpu->buf[2] & 0xffff;
-                uint32_t dsty = gpu->buf[2] >> 16;
-                uint32_t xsiz = gpu->buf[3] & 0xffff;
-                uint32_t ysiz = gpu->buf[3] >> 16;
-
-                for (int y = 0; y < ysiz; y++)
-                    for (int x = 0; x < xsiz; x++)
-                        VRAM(dstx + x, dsty + y) = VRAM(srcx + x, srcy + y);
-            }
-        } break;
-    }
-}
-
-void gpu_recv(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                // Save static data
-                gpu->xpos = gpu->buf[1] & 0x3ff;
-                gpu->ypos = (gpu->buf[1] >> 16) & 0x1ff;
-                gpu->xsiz = gpu->buf[2] & 0xffff;
-                gpu->ysiz = gpu->buf[2] >> 16;
-                gpu->xsiz = ((gpu->xsiz - 1) & 0x3ff) + 1;
-                gpu->ysiz = ((gpu->ysiz - 1) & 0x1ff) + 1;
-                gpu->tsiz = ((gpu->xsiz * gpu->ysiz) + 1) & 0xfffffffe;
-                gpu->addr = gpu->xpos + (gpu->ypos * 1024);
-                gpu->xcnt = 0;
-                gpu->ycnt = 0;
-            }
-        } break;
-
-        case GPU_STATE_RECV_DATA: {
-            unsigned int xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
-            unsigned int ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
-
-            // To-do: This is segfaulting for some reason
-            //        Fix GPU edge cases in general
-            VRAM(xpos, ypos) = gpu->recv_data & 0xffff;
-
-            ++gpu->xcnt;
-
-            xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
-            ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
-
-            if (gpu->xcnt == gpu->xsiz) {
-                ++gpu->ycnt;
-                gpu->xcnt = 0;
-
-                ypos = (gpu->ypos + gpu->ycnt) & 0x1ff;
-                xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
-            }
-
-            VRAM(xpos, ypos) = gpu->recv_data >> 16;
-
-            ++gpu->xcnt;
-            
-            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) {
-                gpu->xcnt = 0;
-                gpu->ycnt = 0;
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_send(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->c0_xcnt = 0;
-                gpu->c0_ycnt = 0;
-                uint32_t c0_xpos = gpu->buf[1] & 0xffff;
-                uint32_t c0_ypos = gpu->buf[1] >> 16;
-                gpu->c0_xsiz = gpu->buf[2] & 0xffff;
-                gpu->c0_ysiz = gpu->buf[2] >> 16;
-                c0_xpos = c0_xpos & 0x3ff;
-                c0_ypos = c0_ypos & 0x1ff;
-                gpu->c0_xsiz = ((gpu->c0_xsiz - 1) & 0x3ff) + 1;
-                gpu->c0_ysiz = ((gpu->c0_ysiz - 1) & 0x1ff) + 1;
-                gpu->c0_tsiz = ((gpu->c0_xsiz * gpu->c0_ysiz) + 1) & 0xfffffffe;
-                gpu->c0_addr = c0_xpos + (c0_ypos * 1024);
-
-                printf("c0addr=%08x c0xcnt=%u c0ycnt=%u\n",
-                    gpu->c0_addr,
-                    gpu->c0_xcnt,
-                    gpu->c0_ycnt
-                );
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_28(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 4;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.x = gpu->buf[2] & 0xffff;
-                gpu->v1.y = gpu->buf[2] >> 16;
-                gpu->v2.x = gpu->buf[3] & 0xffff;
-                gpu->v2.y = gpu->buf[3] >> 16;
-                gpu->v3.x = gpu->buf[4] & 0xffff;
-                gpu->v3.y = gpu->buf[4] >> 16;
-                gpu->color = gpu->buf[0] & 0xffffff;
-
-                gpu_render_flat_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, gpu->color);
-                gpu_render_flat_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, gpu->color);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_30(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 5;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->v0.c = gpu->buf[0] & 0xffffff;
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.c = gpu->buf[2] & 0xffffff;
-                gpu->v1.x = gpu->buf[3] & 0xffff;
-                gpu->v1.y = gpu->buf[3] >> 16;
-                gpu->v2.c = gpu->buf[4] & 0xffffff;
-                gpu->v2.x = gpu->buf[5] & 0xffff;
-                gpu->v2.y = gpu->buf[5] >> 16;
-
-                gpu_render_shaded_triangle(gpu, gpu->v0, gpu->v1, gpu->v2);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_38(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 7;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->v0.c = gpu->buf[0] & 0xffffff;
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.c = gpu->buf[2] & 0xffffff;
-                gpu->v1.x = gpu->buf[3] & 0xffff;
-                gpu->v1.y = gpu->buf[3] >> 16;
-                gpu->v2.c = gpu->buf[4] & 0xffffff;
-                gpu->v2.x = gpu->buf[5] & 0xffff;
-                gpu->v2.y = gpu->buf[5] >> 16;
-                gpu->v3.c = gpu->buf[6] & 0xffffff;
-                gpu->v3.x = gpu->buf[7] & 0xffff;
-                gpu->v3.y = gpu->buf[7] >> 16;
-
-                gpu_render_shaded_triangle(gpu, gpu->v0, gpu->v1, gpu->v2);
-                gpu_render_shaded_triangle(gpu, gpu->v1, gpu->v2, gpu->v3);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_3c(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 11;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                uint32_t texp = gpu->buf[5] >> 16;
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->pal   = gpu->buf[2] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->v1.tx = gpu->buf[5] & 0xff;
-                gpu->v1.ty = (gpu->buf[5] >> 8) & 0xff;
-                gpu->v2.tx = gpu->buf[8] & 0xff;
-                gpu->v2.ty = (gpu->buf[8] >> 8) & 0xff;
-                gpu->v3.tx = gpu->buf[11] & 0xff;
-                gpu->v3.ty = (gpu->buf[11] >> 8) & 0xff;
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.x = gpu->buf[4] & 0xffff;
-                gpu->v1.y = gpu->buf[4] >> 16;
-                gpu->v2.x = gpu->buf[7] & 0xffff;
-                gpu->v2.y = gpu->buf[7] >> 16;
-                gpu->v3.x = gpu->buf[10] & 0xffff;
-                gpu->v3.y = gpu->buf[10] >> 16;
-
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-                uint16_t tpx = (texp & 0xf) << 6;
-                uint16_t tpy = (texp & 0x10) << 4;
-                uint16_t depth = (texp >> 7) & 0x3;
-
-                gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
-                gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_2c(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 8;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                uint32_t texp = gpu->buf[4] >> 16;
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->pal   = gpu->buf[2] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->v1.tx = gpu->buf[4] & 0xff;
-                gpu->v1.ty = (gpu->buf[4] >> 8) & 0xff;
-                gpu->v2.tx = gpu->buf[6] & 0xff;
-                gpu->v2.ty = (gpu->buf[6] >> 8) & 0xff;
-                gpu->v3.tx = gpu->buf[8] & 0xff;
-                gpu->v3.ty = (gpu->buf[8] >> 8) & 0xff;
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.x = gpu->buf[3] & 0xffff;
-                gpu->v1.y = gpu->buf[3] >> 16;
-                gpu->v2.x = gpu->buf[5] & 0xffff;
-                gpu->v2.y = gpu->buf[5] >> 16;
-                gpu->v3.x = gpu->buf[7] & 0xffff;
-                gpu->v3.y = gpu->buf[7] >> 16;
-
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-                uint16_t tpx = (texp & 0xf) << 6;
-                uint16_t tpy = (texp & 0x10) << 4;
-                uint16_t depth = (texp >> 7) & 0x3;
-
-                gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
-                gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_24(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 6;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                uint32_t texp = gpu->buf[4] >> 16;
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->pal   = gpu->buf[2] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->v1.tx = gpu->buf[4] & 0xff;
-                gpu->v1.ty = (gpu->buf[4] >> 8) & 0xff;
-                gpu->v2.tx = gpu->buf[6] & 0xff;
-                gpu->v2.ty = (gpu->buf[6] >> 8) & 0xff;
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.x = gpu->buf[3] & 0xffff;
-                gpu->v1.y = gpu->buf[3] >> 16;
-                gpu->v2.x = gpu->buf[5] & 0xffff;
-                gpu->v2.y = gpu->buf[5] >> 16;
-
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-                uint16_t tpx = (texp & 0xf) << 6;
-                uint16_t tpy = (texp & 0x10) << 4;
-                uint16_t depth = (texp >> 7) & 0x3;
-
-                gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
-                gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-// Monochrome Opaque Quadrilateral
-void gpu_cmd_2d(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 8;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                uint32_t texp = gpu->buf[4] >> 16;
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->pal   = gpu->buf[2] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->v1.tx = gpu->buf[4] & 0xff;
-                gpu->v1.ty = (gpu->buf[4] >> 8) & 0xff;
-                gpu->v2.tx = gpu->buf[6] & 0xff;
-                gpu->v2.ty = (gpu->buf[6] >> 8) & 0xff;
-                gpu->v3.tx = gpu->buf[8] & 0xff;
-                gpu->v3.ty = (gpu->buf[8] >> 8) & 0xff;
-                gpu->v0.x = gpu->buf[1] & 0xffff;
-                gpu->v0.y = gpu->buf[1] >> 16;
-                gpu->v1.x = gpu->buf[3] & 0xffff;
-                gpu->v1.y = gpu->buf[3] >> 16;
-                gpu->v2.x = gpu->buf[5] & 0xffff;
-                gpu->v2.y = gpu->buf[5] >> 16;
-                gpu->v3.x = gpu->buf[7] & 0xffff;
-                gpu->v3.y = gpu->buf[7] >> 16;
-
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-                uint16_t tpx = (texp & 0xf) << 6;
-                uint16_t tpy = (texp & 0x10) << 4;
-                uint16_t depth = (texp >> 7) & 0x3;
-
-                gpu_render_textured_triangle(gpu, gpu->v0, gpu->v1, gpu->v2, tpx, tpy, clutx, cluty, depth);
-                gpu_render_textured_triangle(gpu, gpu->v1, gpu->v2, gpu->v3, tpx, tpy, clutx, cluty, depth);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_64(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 3;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->pal   = gpu->buf[2] >> 16;
-
-                uint32_t w = gpu->buf[3] & 0xffff;
-                uint32_t h = gpu->buf[3] >> 16;
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-
-                gpu_render_textured_rectangle(gpu, gpu->v0, w, h, clutx, cluty, gpu->color);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_7c(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->pal   = gpu->buf[2] >> 16;
-
-                uint32_t w = 16;
-                uint32_t h = 16;
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-
-                gpu_render_textured_rectangle(gpu, gpu->v0, w, h, clutx, cluty, gpu->color);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_74(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-                gpu->v0.tx = gpu->buf[2] & 0xff;
-                gpu->v0.ty = (gpu->buf[2] >> 8) & 0xff;
-                gpu->pal   = gpu->buf[2] >> 16;
-
-                uint32_t w = 8;
-                uint32_t h = 8;
-                uint16_t clutx = (gpu->pal & 0x3f) << 4;
-                uint16_t cluty = (gpu->pal >> 6) & 0x1ff;
-
-                gpu_render_textured_rectangle(gpu, gpu->v0, w, h, clutx, cluty, gpu->color);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_60(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-                gpu->xsiz  = gpu->buf[2] & 0xffff;
-                gpu->ysiz  = gpu->buf[2] >> 16;
-
-                gpu->v0.x += gpu->off_x;
-                gpu->v0.y += gpu->off_y;
-
-                gpu_render_flat_rectangle(gpu, gpu->v0, gpu->xsiz, gpu->ysiz, BGR555(gpu->color));
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_68(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 1;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-
-                gpu->v0.x += gpu->off_x;
-                gpu->v0.y += gpu->off_y;
-
-                gpu->vram[gpu->v0.x + (gpu->v0.y * 1024)] = BGR555(gpu->color);
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_40(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-                gpu->v1.x  = gpu->buf[2] & 0xffff;
-                gpu->v1.y  = gpu->buf[2] >> 16;
-
-                gpu_render_flat_line(gpu, gpu->v0, gpu->v1, BGR555(gpu->color));
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_02(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 2;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                gpu->color = gpu->buf[0] & 0xffffff;
-                gpu->v0.x  = gpu->buf[1] & 0xffff;
-                gpu->v0.y  = gpu->buf[1] >> 16;
-                gpu->xsiz  = gpu->buf[2] & 0xffff;
-                gpu->ysiz  = gpu->buf[2] >> 16;
-
-                gpu->v0.x = (gpu->v0.x & 0x3f0);
-                gpu->v0.y = gpu->v0.y & 0x1ff;
-                gpu->xsiz = (((gpu->xsiz & 0x3ff) + 0x0f) & 0xfffffff0);
-                gpu->ysiz = gpu->ysiz & 0x1ff;
-
-                uint16_t color = BGR555(gpu->color);
-
-                for (uint32_t y = gpu->v0.y; y < (gpu->v0.y + gpu->ysiz); y++) {
-                    for (uint32_t x = gpu->v0.x; x < (gpu->v0.x + gpu->xsiz); x++) {
-                        if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
-                            VRAM(x, y) = color;
-                    }
-                }
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void gpu_cmd_80(psx_gpu_t* gpu) {
-    switch (gpu->state) {
-        case GPU_STATE_RECV_CMD: {
-            gpu->state = GPU_STATE_RECV_ARGS;
-            gpu->cmd_args_remaining = 3;
-        } break;
-
-        case GPU_STATE_RECV_ARGS: {
-            if (!gpu->cmd_args_remaining) {
-                gpu->state = GPU_STATE_RECV_DATA;
-
-                uint32_t srcx = gpu->buf[1] & 0xffff;
-                uint32_t srcy = gpu->buf[1] >> 16;
-                uint32_t dstx = gpu->buf[2] & 0xffff;
-                uint32_t dsty = gpu->buf[2] >> 16;
-                uint32_t xsiz = gpu->buf[3] & 0xffff;
-                uint32_t ysiz = gpu->buf[3] >> 16;
-
-                for (int y = 0; y < ysiz; y++) {
-                    for (int x = 0; x < xsiz; x++) {
-                        if ((x >= gpu->draw_x1) && (x <= gpu->draw_x2) && (y >= gpu->draw_y1) && (y <= gpu->draw_y2))
-                            gpu->vram[(dstx + x) + (dsty + y) * 1024] = gpu->vram[(srcx + x) + (srcy + y) * 1024];
-                    }
-                }
-
-                gpu->state = GPU_STATE_RECV_CMD;
-            }
-        } break;
-    }
-}
-
-void psx_gpu_update_cmd(psx_gpu_t* gpu) {
-    int type = (gpu->buf[0] >> 29) & 7;
-
-    switch (type) {
-        case 1: gpu_poly(gpu); return;
-        case 3: gpu_rect(gpu); return;
-        case 4: gpu_copy(gpu); return;
-        case 5: gpu_recv(gpu); return;
-        case 6: gpu_send(gpu); return;
-        default: break;
-    }
-
-    switch (gpu->buf[0] >> 24) {
-        case 0x00: /* nop */ break;
-        case 0x01: /* Cache clear */ break;
-        case 0x02: gpu_cmd_02(gpu); break;
-        // case 0x24: gpu_cmd_24(gpu); break;
-        // case 0x25: gpu_cmd_24(gpu); break;
-        // case 0x26: gpu_cmd_24(gpu); break;
-        // case 0x27: gpu_cmd_24(gpu); break;
-        // case 0x28: gpu_cmd_28(gpu); break;
-        // case 0x2a: gpu_cmd_28(gpu); break;
-        // case 0x2c: gpu_cmd_2d(gpu); break;
-        // case 0x2d: gpu_cmd_2d(gpu); break;
-        // case 0x2e: gpu_cmd_2d(gpu); break;
-        // case 0x2f: gpu_cmd_2d(gpu); break;
-        // case 0x30: gpu_cmd_30(gpu); break;
-        // case 0x32: gpu_cmd_30(gpu); break;
-        // case 0x38: gpu_cmd_38(gpu); break;
-        // case 0x3c: gpu_cmd_3c(gpu); break;
-        // case 0x3e: gpu_cmd_3c(gpu); break;
-        case 0x40: gpu_cmd_40(gpu); break;
-        // case 0x60: gpu_cmd_60(gpu); break;
-        // case 0x62: gpu_cmd_60(gpu); break;
-        // case 0x64: gpu_cmd_64(gpu); break;
-        // case 0x65: gpu_cmd_64(gpu); break;
-        // case 0x66: gpu_cmd_64(gpu); break;
-        // case 0x67: gpu_cmd_64(gpu); break;
-        // case 0x68: gpu_cmd_68(gpu); break;
-        // case 0x74: gpu_cmd_74(gpu); break;
-        // case 0x75: gpu_cmd_74(gpu); break;
-        // case 0x76: gpu_cmd_74(gpu); break;
-        // case 0x77: gpu_cmd_74(gpu); break;
-        // case 0x7c: gpu_cmd_7c(gpu); break;
-        // case 0x7d: gpu_cmd_7c(gpu); break;
-        // case 0x7e: gpu_cmd_7c(gpu); break;
-        // case 0x7f: gpu_cmd_7c(gpu); break;
-        // case 0x80: gpu_cmd_80(gpu); break;
-        // case 0xa0: gpu_cmd_a0(gpu); break;
-        // case 0xc0: gpu_cmd_c0(gpu); break;
-        case 0xe1: {
-            gpu->gpustat &= 0xfffff800;
-            gpu->gpustat |= gpu->buf[0] & 0x7ff;
-            gpu->texp_x = (gpu->gpustat & 0xf) << 6;
-            gpu->texp_y = (gpu->gpustat & 0x10) << 4;
-            gpu->texp_d = (gpu->gpustat >> 7) & 0x3;
-        } break;
-        case 0xe2: {
-            gpu->texw_mx = (gpu->buf[0] >> 0 ) & 0x1f;
-            gpu->texw_my = (gpu->buf[0] >> 5 ) & 0x1f;
-            gpu->texw_ox = (gpu->buf[0] >> 10) & 0x1f;
-            gpu->texw_oy = (gpu->buf[0] >> 15) & 0x1f;
-        } break;
-        case 0xe3: {
-            gpu->draw_x1 = (gpu->buf[0] >> 0 ) & 0x3ff;
-            gpu->draw_y1 = (gpu->buf[0] >> 10) & 0x1ff;
-        } break;
-        case 0xe4: {
-            gpu->draw_x2 = (gpu->buf[0] >> 0 ) & 0x3ff;
-            gpu->draw_y2 = (gpu->buf[0] >> 10) & 0x1ff;
-        } break;
-        case 0xe5: {
-            gpu->off_x = (gpu->buf[0] >> 0 ) & 0x7ff;
-            gpu->off_y = (gpu->buf[0] >> 11) & 0x7ff;
-        } break;
-        case 0xe6: {
-            /* To-do: Implement mask bit thing */
-        } break;
-        default: {
-            // log_set_quiet(0);
-            // log_fatal("Unhandled GP0(%02Xh)", gpu->buf[0] >> 24);
-            // log_set_quiet(1);
-
-            // exit(1);
-        } break;
-    }
-}
-
-void psx_gpu_write32(psx_gpu_t* gpu, uint32_t offset, uint32_t value) {
-    switch (offset) {
-        // GP0
-        case 0x00: {
-            switch (gpu->state) {
-                case GPU_STATE_RECV_CMD: {
-                    gpu->buf_index = 0;
-                    gpu->buf[gpu->buf_index++] = value;
-
-                    psx_gpu_update_cmd(gpu);
-                } break;
-
-                case GPU_STATE_RECV_ARGS: {
-                    gpu->buf[gpu->buf_index++] = value;
-                    gpu->cmd_args_remaining--;
-
-                    psx_gpu_update_cmd(gpu);
-                } break;
-
-                case GPU_STATE_RECV_DATA: {
-                    gpu->recv_data = value;
-
-                    psx_gpu_update_cmd(gpu);
-                } break;
-            }
-
-            return;
-        } break;
-
-        // GP1
-        case 0x04: {
-            uint8_t cmd = value >> 24;
-
-            switch (cmd) {
-                case 0x00: {
-                    gpu->gpustat = 0x14802000;
-
-                    /*
-                        GP1(01h)      ;clear fifo
-                        GP1(02h)      ;ack irq (0)
-                        GP1(03h)      ;display off (1)
-                        GP1(04h)      ;dma off (0)
-                        GP1(05h)      ;display address (0)
-                        GP1(06h)      ;display x1,x2 (x1=200h, x2=200h+256*10)
-                        GP1(07h)      ;display y1,y2 (y1=010h, y2=010h+240)
-                        GP1(08h)      ;display mode 320x200 NTSC (0)
-                        GP0(E1h..E6h) ;rendering attributes (0)
-                    */
-
-                    gpu->disp_x1 = 0x200;
-                    gpu->disp_x2 = 0xc00;
-                    gpu->disp_y1 = 0x010;
-                    gpu->disp_y2 = 0x100;
-                    gpu->display_mode = 0;
-
-                    gpu->disp_x = 0;
-                    gpu->disp_y = 0;
-
-                    if (gpu->event_cb_table[GPU_EVENT_DMODE])
-                        gpu->event_cb_table[GPU_EVENT_DMODE](gpu);
-                } break;
-                case 0x04: {
-                } break;
-                case 0x05: {
-                    gpu->disp_x = value & 0x3ff;
-                    gpu->disp_y = (value >> 10) & 0x1ff;
-                } break;
-                case 0x06: {
-                    gpu->disp_x1 = value & 0xfff;
-                    gpu->disp_x2 = (value >> 12) & 0xfff;
-                } break;
-                case 0x08:
-                    gpu->display_mode = value & 0xffffff;
-
-                    if (gpu->event_cb_table[GPU_EVENT_DMODE])
-                        gpu->event_cb_table[GPU_EVENT_DMODE](gpu);
-                break;
-
-                case 0x10: {
-                    gpu->gp1_10h_req = value & 7;
-                } break;
-            }
-
-            log_error("GP1(%02Xh) args=%06x", 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) {
-    log_warn("Unhandled 16-bit GPU write at offset %08x (%04x)", offset, value);
-}
-
-void psx_gpu_write8(psx_gpu_t* gpu, uint32_t offset, uint8_t value) {
-    log_warn("Unhandled 8-bit GPU write at offset %08x (%02x)", 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;
-}
-
-void psx_gpu_set_udata(psx_gpu_t* gpu, int index, void* udata) {
-    gpu->udata[index] = udata;
-}
-
-#define GPU_CYCLES_PER_HDRAW_NTSC 2560.0f
-#define GPU_CYCLES_PER_SCANL_NTSC 3413.0f
-#define GPU_SCANS_PER_VDRAW_NTSC 240
-#define GPU_SCANS_PER_FRAME_NTSC 263
-#define GPU_CYCLES_PER_SCANL_PAL 3406.0f
-#define GPU_SCANS_PER_FRAME_PAL  314
-
-void gpu_hblank_event(psx_gpu_t* gpu) {
-    gpu->line++;
-
-    if (gpu->line < GPU_SCANS_PER_VDRAW_NTSC) {
-        if (gpu->line & 1) {
-            gpu->gpustat |= 1 << 31;
-        } else {
-            gpu->gpustat &= ~(1 << 31);
-        }
-    } else {
-        gpu->gpustat &= ~(1 << 31);
-    }
-
-    if (gpu->line == GPU_SCANS_PER_VDRAW_NTSC) {
-        if (gpu->event_cb_table[GPU_EVENT_VBLANK])
-            gpu->event_cb_table[GPU_EVENT_VBLANK](gpu);
-
-        psx_ic_irq(gpu->ic, IC_VBLANK);
-    } else if (gpu->line == GPU_SCANS_PER_FRAME_NTSC) {
-        if (gpu->event_cb_table[GPU_EVENT_VBLANK_END])
-            gpu->event_cb_table[GPU_EVENT_VBLANK_END](gpu);
-
-        // psx_ic_irq(gpu->ic, IC_SPU);
-
-        gpu->line = 0;
-    }
-}
-
-void psx_gpu_update(psx_gpu_t* gpu, int cyc) {
-    int prev_hblank = (gpu->cycles >= GPU_CYCLES_PER_HDRAW_NTSC) &&
-                      (gpu->cycles <= GPU_CYCLES_PER_SCANL_NTSC);
-
-    // Convert CPU (~33.8 MHz) cycles to GPU (~53.7 MHz) cycles
-    // gpu->cycles += (float)cyc * (PSX_GPU_CLOCK_FREQ_NTSC / PSX_CPU_FREQ);
-    gpu->cycles += (float)cyc * (11.0f / 7.0f);
-
-    int curr_hblank = (gpu->cycles >= GPU_CYCLES_PER_HDRAW_NTSC) &&
-                      (gpu->cycles <= GPU_CYCLES_PER_SCANL_NTSC);
-    
-    if (curr_hblank && !prev_hblank) {
-        if (gpu->event_cb_table[GPU_EVENT_HBLANK])
-            gpu->event_cb_table[GPU_EVENT_HBLANK](gpu);
-
-        gpu_hblank_event(gpu);
-    } else if (prev_hblank && !curr_hblank) {
-        if (gpu->event_cb_table[GPU_EVENT_HBLANK_END])
-            gpu->event_cb_table[GPU_EVENT_HBLANK_END](gpu);
-        
-        gpu->cycles -= (float)GPU_CYCLES_PER_SCANL_NTSC;
-    }
-}
-
-void* psx_gpu_get_display_buffer(psx_gpu_t* gpu) {
-    return gpu->vram + (gpu->disp_x + (gpu->disp_y * 1024));
-}
-
-void psx_gpu_destroy(psx_gpu_t* gpu) {
-    free(gpu->vram);
-    free(gpu);
-}
\ No newline at end of file
--- a/psx/dev/scratchpad.c
+++ b/psx/dev/scratchpad.c
@@ -5,7 +5,7 @@
 #include "../log.h"
 #include "scratchpad.h"
 
-psx_scratchpad_t* psx_scratchpad_create() {
+psx_scratchpad_t* psx_scratchpad_create(void) {
     return (psx_scratchpad_t*)malloc(sizeof(psx_scratchpad_t));
 }
 
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -85,7 +85,7 @@
     0x5997, 0x599E, 0x59A4, 0x59A9, 0x59AD, 0x59B0, 0x59B2, 0x59B3
 };
 
-psx_spu_t* psx_spu_create() {
+psx_spu_t* psx_spu_create(void) {
     return (psx_spu_t*)malloc(sizeof(psx_spu_t));
 }
 
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -58,7 +58,7 @@
     "target", 0, 0, 0
 };
 
-psx_timer_t* psx_timer_create() {
+psx_timer_t* psx_timer_create(void) {
     return (psx_timer_t*)malloc(sizeof(psx_timer_t));
 }
 
--- a/psx/disc.c
+++ b/psx/disc.c
@@ -19,7 +19,7 @@
     return sectors * CD_SECTOR_SIZE;
 }
 
-psx_disc_t* psx_disc_create() {
+psx_disc_t* psx_disc_create(void) {
     return (psx_disc_t*)malloc(sizeof(psx_disc_t));
 }
 
--- a/psx/disc/bin.c
+++ b/psx/disc/bin.c
@@ -11,7 +11,7 @@
 #include <stdio.h>
 #include <string.h>
 
-psxd_bin_t* psxd_bin_create() {
+psxd_bin_t* psxd_bin_create(void) {
     return (psxd_bin_t*)malloc(sizeof(psxd_bin_t));
 }
 
--- a/psx/disc/cue.c
+++ b/psx/disc/cue.c
@@ -282,7 +282,7 @@
     }
 }
 
-psxd_cue_t* psxd_cue_create() {
+psxd_cue_t* psxd_cue_create(void) {
     return (psxd_cue_t*)malloc(sizeof(psxd_cue_t));
 }
 
--- a/psx/input/sda.c
+++ b/psx/input/sda.c
@@ -10,7 +10,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-psxi_sda_t* psxi_sda_create() {
+psxi_sda_t* psxi_sda_create(void) {
     return (psxi_sda_t*)malloc(sizeof(psxi_sda_t));
 }
 
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -1,6 +1,6 @@
 #include "psx.h"
 
-psx_t* psx_create() {
+psx_t* psx_create(void) {
     return (psx_t*)malloc(sizeof(psx_t));
 }
 
--