shithub: psxe

Download patch

ref: 45886e067d78fddee5a84a28f4ee44e8b4a3af6a
parent: 3a9d344655a77e3eb6453fb7e0905afafd9bf969
author: allkern <lisandroaalarcon@gmail.com>
date: Sat Jul 1 19:42:46 EDT 2023

Tidy up, Improve CDROM IRQ timings

--- a/frontend/main.c
+++ b/frontend/main.c
@@ -3,28 +3,8 @@
 #include "screen.h"
 #include "config.h"
 
-#include <signal.h>
-
 #undef main
 
-psx_cpu_t* g_cpu;
-
-void sigint_handler(int sig) {
-    log_set_quiet(0);
-
-    log_fatal("r0=%08x at=%08x v0=%08x v1=%08x", g_cpu->r[0] , g_cpu->r[1] , g_cpu->r[2] , g_cpu->r[3] );
-    log_fatal("a0=%08x a1=%08x a2=%08x a3=%08x", g_cpu->r[4] , g_cpu->r[5] , g_cpu->r[6] , g_cpu->r[7] );
-    log_fatal("t0=%08x t1=%08x t2=%08x t3=%08x", g_cpu->r[8] , g_cpu->r[9] , g_cpu->r[10], g_cpu->r[11]);
-    log_fatal("t4=%08x t5=%08x t6=%08x t7=%08x", g_cpu->r[12], g_cpu->r[13], g_cpu->r[14], g_cpu->r[15]);
-    log_fatal("s0=%08x s1=%08x s2=%08x s3=%08x", g_cpu->r[16], g_cpu->r[17], g_cpu->r[18], g_cpu->r[19]);
-    log_fatal("s4=%08x s5=%08x s6=%08x s7=%08x", g_cpu->r[20], g_cpu->r[21], g_cpu->r[22], g_cpu->r[23]);
-    log_fatal("t8=%08x t9=%08x k0=%08x k1=%08x", g_cpu->r[24], g_cpu->r[25], g_cpu->r[26], g_cpu->r[27]);
-    log_fatal("gp=%08x sp=%08x fp=%08x ra=%08x", g_cpu->r[28], g_cpu->r[29], g_cpu->r[30], g_cpu->r[31]);
-    log_fatal("pc=%08x hi=%08x lo=%08x epc=%08x", g_cpu->pc, g_cpu->hi, g_cpu->lo, g_cpu->cop0_epc);
-
-    exit(1);
-}
-
 int main(int argc, const char* argv[]) {
     psxe_config_t* cfg = psxe_cfg_create();
 
@@ -34,15 +14,11 @@
 
     log_set_level(cfg->log_level);
 
-    signal(SIGINT, sigint_handler);
-
     psx_t* psx = psx_create();
     psx_init(psx, cfg->bios);
 
     psx_gpu_t* gpu = psx_get_gpu(psx);
 
-    g_cpu = psx_get_cpu(psx);
-
     psxe_screen_t* screen = psxe_screen_create();
     psxe_screen_init(screen, psx);
     psxe_screen_set_scale(screen, 2);
@@ -52,7 +28,7 @@
     psx_gpu_set_udata(gpu, 0, screen);
 
     if (cfg->exe) {
-        while (psx->cpu->pc != 0xBFC06FF0) {
+        while (psx->cpu->pc != 0x80030000) {
             psx_update(psx);
         }
 
@@ -77,7 +53,7 @@
     log_fatal("s4=%08x s5=%08x s6=%08x s7=%08x", cpu->r[20], cpu->r[21], cpu->r[22], cpu->r[23]);
     log_fatal("t8=%08x t9=%08x k0=%08x k1=%08x", cpu->r[24], cpu->r[25], cpu->r[26], cpu->r[27]);
     log_fatal("gp=%08x sp=%08x fp=%08x ra=%08x", cpu->r[28], cpu->r[29], cpu->r[30], cpu->r[31]);
-    log_fatal("pc=%08x hi=%08x lo=%08x ep=%08x", cpu->pc, cpu->hi, cpu->lo, cpu->cop0_epc);
+    log_fatal("pc=%08x hi=%08x lo=%08x ep=%08x", cpu->pc, cpu->hi, cpu->lo, cpu->cop0_r[COP0_EPC]);
 
     psx_destroy(psx);
     psxe_screen_destroy(screen);
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -17,6 +17,7 @@
 
     screen->scale = 1;
     screen->open = 1;
+    screen->format = SDL_PIXELFORMAT_BGR555;
     screen->psx = psx;
 
     SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
@@ -47,7 +48,7 @@
 
     screen->texture = SDL_CreateTexture(
         screen->renderer,
-        SDL_PIXELFORMAT_BGR555,
+        screen->format,
         SDL_TEXTUREACCESS_STREAMING,
         PSX_GPU_FB_WIDTH, PSX_GPU_FB_HEIGHT
     );
@@ -74,7 +75,7 @@
 void psxe_screen_toggle_debug_mode(psxe_screen_t* screen) {
     screen->debug_mode = !screen->debug_mode;
 
-    psxe_screen_reload(screen);
+    psxe_gpu_dmode_event_cb(screen->psx->gpu);
 }
 
 void psxe_screen_update(psxe_screen_t* screen) {
@@ -91,6 +92,14 @@
             case SDL_QUIT: {
                 screen->open = 0;
             } break;
+
+            case SDL_KEYDOWN: {
+                switch (event.key.keysym.sym) {
+                    case SDLK_F1: {
+                        psxe_screen_toggle_debug_mode(screen);
+                    } break;
+                }
+            } break;
         }
     }
 }
@@ -98,7 +107,6 @@
 void psxe_screen_set_scale(psxe_screen_t* screen, unsigned int scale) {
     if (screen->debug_mode) {
         screen->scale = 1;
-        screen->saved_scale = 1;
     } else {
         screen->scale = scale;
         screen->saved_scale = scale;
@@ -117,6 +125,9 @@
 
 void psxe_gpu_dmode_event_cb(psx_gpu_t* gpu) {
     psxe_screen_t* screen = gpu->udata[0];
+
+    screen->format = psx_get_display_format(screen->psx) ?
+        SDL_PIXELFORMAT_BGR555 : SDL_PIXELFORMAT_RGB888;
 
     if (screen->debug_mode) {
         screen->width = PSX_GPU_FB_WIDTH;
--- a/frontend/screen.h
+++ b/frontend/screen.h
@@ -17,6 +17,7 @@
 
     unsigned int saved_scale;
     unsigned int width, height, scale;
+    unsigned int format;
 
     int debug_mode;
     int open;
--- a/psx/cpu.c
+++ b/psx/cpu.c
@@ -5,73 +5,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define R_R0 (cpu->r[0])
-#define R_AT (cpu->r[1])
-#define R_V0 (cpu->r[2])
-#define R_V1 (cpu->r[3])
-#define R_A0 (cpu->r[4])
-#define R_A1 (cpu->r[5])
-#define R_A2 (cpu->r[6])
-#define R_A3 (cpu->r[7])
-#define R_T0 (cpu->r[8])
-#define R_T1 (cpu->r[9])
-#define R_T2 (cpu->r[10])
-#define R_T3 (cpu->r[11])
-#define R_T4 (cpu->r[12])
-#define R_T5 (cpu->r[13])
-#define R_T6 (cpu->r[14])
-#define R_T7 (cpu->r[15])
-#define R_S0 (cpu->r[16])
-#define R_S1 (cpu->r[17])
-#define R_S2 (cpu->r[18])
-#define R_S3 (cpu->r[19])
-#define R_S4 (cpu->r[20])
-#define R_S5 (cpu->r[21])
-#define R_S6 (cpu->r[22])
-#define R_S7 (cpu->r[23])
-#define R_T8 (cpu->r[24])
-#define R_T9 (cpu->r[25])
-#define R_K0 (cpu->r[26])
-#define R_K1 (cpu->r[27])
-#define R_GP (cpu->r[28])
-#define R_SP (cpu->r[29])
-#define R_FP (cpu->r[30])
-#define R_RA (cpu->r[31])
-
-psx_cpu_t* psx_cpu_create() {
-    return (psx_cpu_t*)malloc(sizeof(psx_cpu_t));
-}
-
-void cpu_a_kcall_hook(psx_cpu_t*);
-void cpu_b_kcall_hook(psx_cpu_t*);
-
-void psx_cpu_fetch(psx_cpu_t* cpu) {
-    cpu->buf[0] = psx_bus_read32(cpu->bus, cpu->pc);
-    cpu->pc += 4;
-
-    // Discard fetch cycles
-    psx_bus_get_access_cycles(cpu->bus);
-}
-
-void psx_cpu_init(psx_cpu_t* cpu, psx_bus_t* bus) {
-    memset(cpu, 0, sizeof(psx_cpu_t));
-
-    psx_cpu_set_a_kcall_hook(cpu, cpu_a_kcall_hook);
-    psx_cpu_set_b_kcall_hook(cpu, cpu_b_kcall_hook);
-
-    cpu->bus = bus;
-    cpu->pc = 0xbfc00000;
-
-    cpu->cop0_sr = 0x10900000;
-    cpu->cop0_prid = 0x00000002;
-
-    psx_cpu_fetch(cpu);
-}
-
-void psx_cpu_destroy(psx_cpu_t* cpu) {
-    free(cpu);
-}
-
 psx_cpu_instruction_t g_psx_cpu_secondary_table[] = {
     psx_cpu_i_sll    , psx_cpu_i_invalid, psx_cpu_i_srl    , psx_cpu_i_sra    ,
     psx_cpu_i_sllv   , psx_cpu_i_invalid, psx_cpu_i_srlv   , psx_cpu_i_srav   ,
@@ -143,6 +76,39 @@
 #define IMM16 (cpu->buf[1] & 0xffff)
 #define IMM16S ((int32_t)((int16_t)IMM16))
 
+#define R_R0 (cpu->r[0])
+#define R_AT (cpu->r[1])
+#define R_V0 (cpu->r[2])
+#define R_V1 (cpu->r[3])
+#define R_A0 (cpu->r[4])
+#define R_A1 (cpu->r[5])
+#define R_A2 (cpu->r[6])
+#define R_A3 (cpu->r[7])
+#define R_T0 (cpu->r[8])
+#define R_T1 (cpu->r[9])
+#define R_T2 (cpu->r[10])
+#define R_T3 (cpu->r[11])
+#define R_T4 (cpu->r[12])
+#define R_T5 (cpu->r[13])
+#define R_T6 (cpu->r[14])
+#define R_T7 (cpu->r[15])
+#define R_S0 (cpu->r[16])
+#define R_S1 (cpu->r[17])
+#define R_S2 (cpu->r[18])
+#define R_S3 (cpu->r[19])
+#define R_S4 (cpu->r[20])
+#define R_S5 (cpu->r[21])
+#define R_S6 (cpu->r[22])
+#define R_S7 (cpu->r[23])
+#define R_T8 (cpu->r[24])
+#define R_T9 (cpu->r[25])
+#define R_K0 (cpu->r[26])
+#define R_K1 (cpu->r[27])
+#define R_GP (cpu->r[28])
+#define R_SP (cpu->r[29])
+#define R_FP (cpu->r[30])
+#define R_RA (cpu->r[31])
+
 // #define CPU_TRACE
 
 #ifdef CPU_TRACE
@@ -295,6 +261,40 @@
     }
 }
 
+psx_cpu_t* psx_cpu_create() {
+    return (psx_cpu_t*)malloc(sizeof(psx_cpu_t));
+}
+
+void cpu_a_kcall_hook(psx_cpu_t*);
+void cpu_b_kcall_hook(psx_cpu_t*);
+
+void psx_cpu_fetch(psx_cpu_t* cpu) {
+    cpu->buf[0] = psx_bus_read32(cpu->bus, cpu->pc);
+    cpu->pc += 4;
+
+    // Discard fetch cycles
+    psx_bus_get_access_cycles(cpu->bus);
+}
+
+void psx_cpu_init(psx_cpu_t* cpu, psx_bus_t* bus) {
+    memset(cpu, 0, sizeof(psx_cpu_t));
+
+    psx_cpu_set_a_kcall_hook(cpu, cpu_a_kcall_hook);
+    psx_cpu_set_b_kcall_hook(cpu, cpu_b_kcall_hook);
+
+    cpu->bus = bus;
+    cpu->pc = 0xbfc00000;
+
+    cpu->cop0_r[COP0_SR] = 0x10900000;
+    cpu->cop0_r[COP0_PRID] = 0x00000002;
+
+    psx_cpu_fetch(cpu);
+}
+
+void psx_cpu_destroy(psx_cpu_t* cpu) {
+    free(cpu);
+}
+
 void psx_cpu_set_a_kcall_hook(psx_cpu_t* cpu, psx_cpu_kcall_hook_t hook) {
     cpu->a_function_hook = hook;
 }
@@ -333,7 +333,7 @@
 
     g_psx_cpu_primary_table[OP](cpu);
 
-    if ((cpu->cop0_sr & SR_IEC) && (cpu->cop0_cause & cpu->cop0_sr & SR_IM2)) {
+    if ((cpu->cop0_r[COP0_SR] & SR_IEC) && (cpu->cop0_r[COP0_CAUSE] & cpu->cop0_r[COP0_SR] & SR_IM2)) {
         psx_cpu_exception(cpu, CAUSE_INT);
     }
 
@@ -342,36 +342,59 @@
     cpu->r[0] = 0;
 }
 
+void psx_cpu_check_irq(psx_cpu_t* cpu) {
+    if ((cpu->cop0_r[COP0_SR] & SR_IEC) && (cpu->cop0_r[COP0_CAUSE] & cpu->cop0_r[COP0_SR] & SR_IM2)) {
+        // log_fatal("(before) IRQ pc=%08x, epc=%08x, cause=%08x, sr=%08x, tc=%u (%08x), istat=%08x, imask=%08x, [0]=%08x, [1]=%08x",
+        //     cpu->pc,
+        //     cpu->cop0_r[COP0_EPC],
+        //     cpu->cop0_r[COP0_CAUSE],
+        //     cpu->cop0_r[COP0_SR],
+        //     cpu->total_cycles,
+        //     cpu->total_cycles,
+        //     psx_bus_read32(cpu->bus, 0x1F801070),
+        //     psx_bus_read32(cpu->bus, 0x1F801074),
+        //     cpu->buf[0],
+        //     cpu->buf[1]
+        // );
+
+        psx_cpu_exception(cpu, CAUSE_INT);
+    }
+}
+
 void psx_cpu_exception(psx_cpu_t* cpu, uint32_t cause) {
-    cpu->cop0_cause = cause << 2;
+    // Set excode and clear 3 LSBs
+    cpu->cop0_r[COP0_CAUSE] &= 0xffffff80;
+    cpu->cop0_r[COP0_CAUSE] |= cause;
 
     // If we're in a delay slot, set delay slot bit
     // on CAUSE
     if (cpu->delay_slot) {
-        cpu->cop0_epc = cpu->pc - 12;
-        cpu->cop0_cause |= 0x80000000;
+        cpu->cop0_r[COP0_EPC] = cpu->pc - 12;
+        cpu->cop0_r[COP0_CAUSE] |= 0x80000000;
     } else {
-        cpu->cop0_epc = cpu->pc - 8;
-        cpu->cop0_cause &= 0x7fffffff;
+        cpu->cop0_r[COP0_EPC] = cpu->pc - 8;
+        cpu->cop0_r[COP0_CAUSE] &= 0x7fffffff;
     }
 
     // Do exception stack push
-    uint32_t mode = cpu->cop0_sr & 0x3f;
+    uint32_t mode = cpu->cop0_r[COP0_SR] & 0x3f;
 
-    cpu->cop0_sr &= 0xffffffc0;
-    cpu->cop0_sr |= (mode << 2) & 0x3f;
+    cpu->cop0_r[COP0_SR] &= 0xffffffc0;
+    cpu->cop0_r[COP0_SR] |= (mode << 2) & 0x3f;
 
     // Set PC to the vector selected on BEV
-    cpu->pc = (cpu->cop0_sr & SR_BEV) ? 0xbfc00180 : 0x80000080;
+    cpu->pc = (cpu->cop0_r[COP0_SR] & SR_BEV) ? 0xbfc00180 : 0x80000080;
 
     // Simulate pipeline flush
     psx_cpu_fetch(cpu);
+
+    cpu->buf[1] = cpu->buf[0];
 }
 
 void psx_cpu_irq(psx_cpu_t* cpu, uint32_t irq) {
     // Set interrupt pending field
-    cpu->cop0_cause &= ~SR_IM2;
-    cpu->cop0_cause |= irq ? SR_IM2 : 0;
+    cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2;
+    cpu->cop0_r[COP0_CAUSE] |= irq ? SR_IM2 : 0;
 }
 
 void psx_cpu_i_invalid(psx_cpu_t* cpu) {
@@ -758,7 +781,7 @@
     DO_PENDING_LOAD;
 
     // Cache isolated
-    if (cpu->cop0_sr & SR_ISC) {
+    if (cpu->cop0_r[COP0_SR] & SR_ISC) {
         log_debug("Ignoring write while cache is isolated");
 
         return;
@@ -777,7 +800,7 @@
     DO_PENDING_LOAD;
 
     // Cache isolated
-    if (cpu->cop0_sr & SR_ISC) {
+    if (cpu->cop0_r[COP0_SR] & SR_ISC) {
         log_debug("Ignoring write while cache is isolated");
 
         return;
@@ -821,7 +844,7 @@
     DO_PENDING_LOAD;
 
     // Cache isolated
-    if (cpu->cop0_sr & SR_ISC) {
+    if (cpu->cop0_r[COP0_SR] & SR_ISC) {
         log_debug("Ignoring write while cache is isolated");
 
         return;
@@ -1226,20 +1249,7 @@
 
     DO_PENDING_LOAD;
 
-    switch (D) {
-        case 3: cpu->load_v = cpu->cop0_bpc; break;
-        case 5: cpu->load_v = cpu->cop0_bda; break;
-        case 6: cpu->load_v = cpu->cop0_jumpdest; break;
-        case 7: cpu->load_v = cpu->cop0_dcic; break;
-        case 8: cpu->load_v = cpu->cop0_badvaddr; break;
-        case 9: cpu->load_v = cpu->cop0_bdam; break;
-        case 11: cpu->load_v = cpu->cop0_bpcm; break;
-        case 12: cpu->load_v = cpu->cop0_sr; break;
-        case 13: cpu->load_v = cpu->cop0_cause; break;
-        case 14: cpu->load_v = cpu->cop0_epc; break;
-        case 15: cpu->load_v = cpu->cop0_prid; break;
-    }
-
+    cpu->load_v = cpu->cop0_r[D];
     cpu->load_d = T;
 }
 
@@ -1256,18 +1266,7 @@
 
     DO_PENDING_LOAD;
 
-    switch (D) {
-        case 3: cpu->cop0_bpc = t; break;
-        case 5: cpu->cop0_bda = t; break;
-        case 7: cpu->cop0_dcic = t; break;
-        case 9: cpu->cop0_bdam = t; break;
-        case 11: cpu->cop0_bpcm = t; break;
-        case 12: cpu->cop0_sr = t; break;
-    }
-
-    if ((cpu->cop0_sr & SR_IEC) && (cpu->cop0_cause & cpu->cop0_sr & SR_IM2)) {
-        psx_cpu_exception(cpu, CAUSE_INT);
-    }
+    cpu->cop0_r[D] = t;
 }
 
 void psx_cpu_i_ctc0(psx_cpu_t* cpu) {
@@ -1288,10 +1287,10 @@
 
     DO_PENDING_LOAD;
 
-    uint32_t mode = cpu->cop0_sr & 0x3f;
+    uint32_t mode = cpu->cop0_r[COP0_SR] & 0x3f;
 
-    cpu->cop0_sr &= 0xfffffff0;
-    cpu->cop0_sr |= mode >> 2;
+    cpu->cop0_r[COP0_SR] &= 0xfffffff0;
+    cpu->cop0_r[COP0_SR] |= mode >> 2;
 }
 
 #undef R_R0
--- a/psx/cpu.h
+++ b/psx/cpu.h
@@ -52,6 +52,18 @@
     cop0r15     - PRID - Processor ID (R)
 */
 
+#define COP0_BPC      3
+#define COP0_BDA      5
+#define COP0_JUMPDEST 6
+#define COP0_DCIC     7
+#define COP0_BADVADDR 8
+#define COP0_BDAM     9
+#define COP0_BPCM     11
+#define COP0_SR       12
+#define COP0_CAUSE    13
+#define COP0_EPC      14
+#define COP0_PRID     15
+
 struct psx_cpu_t {
     uint32_t r[32];
     uint32_t buf[2];
@@ -59,19 +71,10 @@
     uint32_t hi, lo;
     uint32_t load_d, load_v;
     uint32_t last_cycles;
+    uint32_t total_cycles;
     int branch, delay_slot;
 
-    uint32_t cop0_bpc;
-    uint32_t cop0_bda;
-    uint32_t cop0_jumpdest;
-    uint32_t cop0_dcic;
-    uint32_t cop0_badvaddr;
-    uint32_t cop0_bdam;
-    uint32_t cop0_bpcm;
-    uint32_t cop0_sr;
-    uint32_t cop0_cause;
-    uint32_t cop0_epc;
-    uint32_t cop0_prid;
+    uint32_t cop0_r[16];
 
     psx_bus_t* bus;
 
@@ -158,6 +161,7 @@
 void psx_cpu_fetch(psx_cpu_t*);
 void psx_cpu_set_a_kcall_hook(psx_cpu_t*, psx_cpu_kcall_hook_t);
 void psx_cpu_set_b_kcall_hook(psx_cpu_t*, psx_cpu_kcall_hook_t);
+void psx_cpu_check_irq(psx_cpu_t*);
 
 /*
     00h INT     Interrupt
@@ -178,19 +182,19 @@
     0Ch Ov      Arithmetic overflow
 */
 
-#define CAUSE_INT       0x00
-#define CAUSE_MOD       0x01
-#define CAUSE_TLBL      0x02
-#define CAUSE_TLBS      0x03
-#define CAUSE_ADEL      0x04
-#define CAUSE_ADES      0x05
-#define CAUSE_IBE       0x06
-#define CAUSE_DBE       0x07
-#define CAUSE_SYSCALL   0x08
-#define CAUSE_BP        0x09
-#define CAUSE_RI        0x0a
-#define CAUSE_CPU       0x0b
-#define CAUSE_OV        0x0c
+#define CAUSE_INT       (0x00 << 2)
+#define CAUSE_MOD       (0x01 << 2)
+#define CAUSE_TLBL      (0x02 << 2)
+#define CAUSE_TLBS      (0x03 << 2)
+#define CAUSE_ADEL      (0x04 << 2)
+#define CAUSE_ADES      (0x05 << 2)
+#define CAUSE_IBE       (0x06 << 2)
+#define CAUSE_DBE       (0x07 << 2)
+#define CAUSE_SYSCALL   (0x08 << 2)
+#define CAUSE_BP        (0x09 << 2)
+#define CAUSE_RI        (0x0a << 2)
+#define CAUSE_CPU       (0x0b << 2)
+#define CAUSE_OV        (0x0c << 2)
 
 void psx_cpu_i_invalid(psx_cpu_t*);
 
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -239,8 +239,11 @@
     if (cdrom->irq_delay) {
         cdrom->irq_delay -= 2;
 
-        if (!cdrom->irq_delay)
+        if (cdrom->irq_delay <= 0) {
             psx_ic_irq(cdrom->ic, IC_CDROM);
+
+            cdrom->irq_delay = 0;
+        }
     }
 }
 
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -596,8 +596,14 @@
                 gpu->v0.x  = gpu->buf[1] & 0xffff;
                 gpu->v0.y  = gpu->buf[1] >> 16;
 
-                gpu_render_flat_rectangle(gpu, gpu->v0, 1, 1, gpu->color);
+                gpu->v0.x += gpu->off_x;
+                gpu->v0.y += gpu->off_y;
 
+                gpu->vram[gpu->v0.x + (gpu->v0.y * 1024)] = gpu_to_bgr555(gpu->color);
+
+                // Optimize pixel plotting
+                // gpu_render_flat_rectangle(gpu, gpu->v0, 1, 1, gpu->color);
+
                 gpu->state = GPU_STATE_RECV_CMD;
             }
         } break;
@@ -752,7 +758,8 @@
 
     if (gpu->line == GPU_SCANS_PER_VDRAW_NTSC) {
         // Disable Vblank for now
-        // psx_ic_irq(gpu->ic, IC_VBLANK);
+        // log_fatal("Vblank");
+        psx_ic_irq(gpu->ic, IC_VBLANK);
     } else if (gpu->line == GPU_SCANS_PER_FRAME_NTSC) {
         gpu->line = 0;
     }
@@ -760,13 +767,20 @@
 
 void psx_gpu_update(psx_gpu_t* gpu, int cyc) {
     // Convert CPU (~33.8 MHz) cycles to GPU (~53.7 MHz) cycles
-    gpu->cycles += ((float)cyc) * (PSX_GPU_CLOCK_FREQ_PAL / PSX_CPU_CLOCK_FREQ);
+    gpu->cycles += (float)cyc * (PSX_GPU_CLOCK_FREQ_NTSC / PSX_CPU_CLOCK_FREQ);
+    //gpu->cycles += (float)cyc;
 
-    //if (gpu->cycles >= (float)GPU_CYCLES_PER_HDRAW_NTSC) {
-        // Tick Hblank timer
+    // if (gpu->cycles >= ((float)PSX_CPU_CLOCK / 60.0f)) {
+    //     psx_ic_irq(gpu->ic, IC_VBLANK);
 
+    //     gpu->cycles -= (float)PSX_CPU_CLOCK / 60.0f;
+    // }
+
+    // if (gpu->cycles >= (float)GPU_CYCLES_PER_HDRAW_NTSC) {
+    //     Tick Hblank timer
+
     if (gpu->cycles >= (float)GPU_CYCLES_PER_SCANL_NTSC) {
-        gpu->cycles -= (float)GPU_CYCLES_PER_SCANL_NTSC;
+        gpu->cycles = 0;
 
         gpu_scanline_event(gpu);
     }
--- a/psx/dev/ic.c
+++ b/psx/dev/ic.c
@@ -59,16 +59,26 @@
             log_warn("Unhandled 32-bit IC write at offset %08x (%08x)", offset, value);
         } break;
     }
+
+    // Emulate acknowledge
+    if (!(ic->stat & ic->mask)) {
+        ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2;
+    }
 }
 
 void psx_ic_write16(psx_ic_t* ic, uint32_t offset, uint16_t value) {
     switch (offset) {
-        case 0x00: ic->stat = value; break;
+        case 0x00: ic->stat &= value; break;
         case 0x04: ic->mask = value; break;
 
         default: {
             log_warn("Unhandled 16-bit IC write at offset %08x (%08x)", offset, value);
         } break;
+    }
+
+    // Emulate acknowledge
+    if (!(ic->stat & ic->mask)) {
+        ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2;
     }
 }
 
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -38,16 +38,16 @@
         return 0x000016b0;
     }
 
-    int t = (offset >> 4) & 0x3;
-    int r = offset & 0xf;
+    // int t = (offset >> 4) & 0x3;
+    // int r = offset & 0xf;
 
-    if (r == 0) {
-        switch (t) {
-            case 0: return timer->t0_stub++;
-            case 1: return timer->t1_stub++;
-            case 2: return timer->t2_stub++;
-        }
-    }
+    // if (r == 0) {
+    //     switch (t) {
+    //         case 0: return timer->t0_stub++;
+    //         case 1: return timer->t1_stub++;
+    //         case 2: return timer->t2_stub++;
+    //     }
+    // }
 
     log_fatal("Unhandled 16-bit TIMER read at offset %08x", offset);
 
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -28,6 +28,7 @@
     psx_cpu_cycle(psx->cpu);
     psx_cdrom_update(psx->cdrom);
     psx_gpu_update(psx->gpu, psx->cpu->last_cycles);
+    psx_cpu_check_irq(psx->cpu);
 }
 
 void psx_run_frame(psx_t* psx) {
@@ -55,6 +56,10 @@
 
 uint32_t psx_get_display_height(psx_t* psx) {
     return (psx->gpu->display_mode & 0x4) ? 480 : 240;
+}
+
+uint32_t psx_get_display_format(psx_t* psx) {
+    return (psx->gpu->display_mode >> 4) & 1;
 }
 
 void psx_init(psx_t* psx, const char* bios_path) {
--- a/psx/psx.h
+++ b/psx/psx.h
@@ -39,6 +39,7 @@
 void* psx_get_display_buffer(psx_t*);
 uint32_t psx_get_display_width(psx_t*);
 uint32_t psx_get_display_height(psx_t*);
+uint32_t psx_get_display_format(psx_t*);
 uint32_t* psx_take_screenshot(psx_t*);
 psx_bios_t* psx_get_bios(psx_t*);
 psx_ram_t* psx_get_ram(psx_t*);
--