shithub: psxe

Download patch

ref: ce32c2fd274db2a5d5302a3744758f62c6d8fd6c
parent: 8a5aaf02459fea9f1cc287dc6406e3557547407f
author: allkern <lisandroaalarcon@gmail.com>
date: Tue Mar 19 18:30:01 EDT 2024

Fix stuff

--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,5 @@
 *.zip
 *.cue
 *.iso
-*.mcd
\ No newline at end of file
+*.mcd
+*.rom
--- a/frontend/config.c
+++ b/frontend/config.c
@@ -77,7 +77,7 @@
 }
 
 void psxe_cfg_load_defaults(psxe_config_t* cfg) {
-    cfg->bios = NULL;
+    cfg->bios = "bios.bin";
     cfg->bios_search = "bios";
     cfg->exe = NULL;
     cfg->help_model = 0;
@@ -91,6 +91,7 @@
     cfg->log_level = LOG_FATAL;
     cfg->quiet = 0;
     cfg->cd_path = NULL;
+    cfg->exp_path = NULL;
 }
 
 void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) {
@@ -111,6 +112,7 @@
     const char* region = NULL;
     const char* psxe_version = NULL;
     const char* cd_path = NULL;
+    const char* exp_path = NULL;
 
     static const char *const usages[] = {
         "psxe [options] path-to-cdrom",
@@ -124,15 +126,16 @@
         OPT_BOOLEAN ('v', "version"       , &version       , "Display version and build information", NULL, 0, 0),
         OPT_GROUP("Basic options"),
         OPT_BOOLEAN ('a', "use-args"      , &use_args      , "Ignore settings file, use CLI args instead", NULL, 0, 0),
-        OPT_STRING  ('b', "bios"          , &bios          , "Use this BIOS file (ignores -B, -M)", NULL, 0, 0),
+        OPT_STRING  ('b', "bios"          , &bios          , "Specify a BIOS file (ignores -B, -M)", NULL, 0, 0),
         OPT_BOOLEAN ('B', "bios-folder"   , &bios_search   , "Specify a BIOS search folder", NULL, 0, 0),
         OPT_STRING  ('c', "console-source", &console_source, "Select console source (auto, null, kernel, atcons)"),
+        OPT_STRING  ('e', "exp-rom"       , &exp_path      , "Specify an expansion ROM file"),
         OPT_INTEGER ('L', "log-level"     , &log_level     , "Set log level"),
         OPT_STRING  ('M', "model"         , &model         , "Specify console model (SPCH-XXXX)", NULL, 0, 0),
         OPT_STRING  ('r', "region"        , &region        , "Specify console region"),
         OPT_STRING  ('S', "settings-file" , &settings_path , "Specify settings file path", NULL, 0, 0),
         OPT_BOOLEAN ('q', "quiet"         , &quiet         , "Silence all logs (ignores -L)"),
-        OPT_STRING  ('x', "exe"           , &exe           , "Boot this PS-X EXE file"),
+        OPT_STRING  ('x', "exe"           , &exe           , "Launch a PS-X EXE file"),
         OPT_STRING  (0  , "cdrom"         , &cd_path       , "Specify a CDROM image"),
         OPT_END()
     };
@@ -274,6 +277,9 @@
 
     if (psxe_version)
         cfg->psxe_version = psxe_version;
+
+    if (exp_path)
+        cfg->exp_path = exp_path;
 }
 
 // To-do: Implement BIOS searching
--- a/frontend/config.h
+++ b/frontend/config.h
@@ -24,6 +24,7 @@
     const char* region;
     const char* psxe_version;
     const char* cd_path;
+    const char* exp_path;
 } psxe_config_t;
 
 psxe_config_t* psxe_cfg_create();
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -34,7 +34,7 @@
     log_set_level(cfg->log_level);
 
     psx_t* psx = psx_create();
-    psx_init(psx, cfg->bios);
+    psx_init(psx, cfg->bios, cfg->exp_path);
 
     psx_cdrom_t* cdrom = psx_get_cdrom(psx);
 
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -52,13 +52,10 @@
     screen->texture_height = PSX_GPU_FB_HEIGHT;
 
     SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
-    SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "0");
     SDL_SetRenderDrawColor(screen->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
 }
 
 void psxe_screen_reload(psxe_screen_t* screen) {
-    SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "0");
-
     if (screen->texture) SDL_DestroyTexture(screen->texture);
     if (screen->renderer) SDL_DestroyRenderer(screen->renderer);
     if (screen->window) SDL_DestroyWindow(screen->window);
@@ -81,7 +78,7 @@
         SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
         screen->width * screen->scale,
         screen->height * screen->scale,
-        SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI
+        0
     );
 
     screen->renderer = SDL_CreateRenderer(
@@ -90,6 +87,8 @@
         SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
     );
 
+    SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "linear"),
+
     screen->texture = SDL_CreateTexture(
         screen->renderer,
         screen->format,
@@ -97,6 +96,8 @@
         screen->texture_width, screen->texture_height
     );
 
+    SDL_SetTextureScaleMode(screen->texture, screen->bilinear);
+
     // Check for retina displays
     int width = 0, height = 0;
 
@@ -130,20 +131,24 @@
 }
 
 void psxe_screen_update(psxe_screen_t* screen) {
-    void* display_buf = screen->debug_mode ?
-        psx_get_vram(screen->psx) : psx_get_display_buffer(screen->psx);
+    // void* vram = psx_get_vram(screen->psx);
 
-    // // Update texture with Lock/UnlockTexture
-    // int pitch;
-    // uint8_t* ptr = NULL;
+    // if (screen->field & 2) {
+    //     for (int y = screen->field & 1; y < 512; y += 2) {
+    //         memcpy(
+    //             ((uint8_t*)screen->buf) + (y * PSX_GPU_FB_STRIDE),
+    //             ((uint8_t*)vram) + (y * PSX_GPU_FB_STRIDE),
+    //             PSX_GPU_FB_STRIDE
+    //         );
+    //     }
+    // }
 
-    // SDL_LockTexture(screen->texture, NULL, &ptr, &pitch);
+    // screen->field += 1;
+    // screen->field &= 3;
 
-    // for (int y = 0; y < screen->texture_height; y++)
-    //     memcpy(ptr + (y * pitch), display_buf + (y * PSX_GPU_FB_STRIDE), pitch);
+    void* display_buf = screen->debug_mode ?
+        psx_get_vram(screen->psx) : psx_get_display_buffer(screen->psx);
 
-    // SDL_UnlockTexture(screen->texture);
-
     SDL_UpdateTexture(screen->texture, NULL, display_buf, PSX_GPU_FB_STRIDE);
     SDL_RenderClear(screen->renderer);
 
@@ -212,6 +217,12 @@
                     } break;
 
                     case SDLK_F4: {
+                        screen->bilinear = !screen->bilinear;
+
+                        psxe_gpu_dmode_event_cb(screen->psx->gpu);
+                    } break;
+
+                    case SDLK_F11: {
                         screen->fullscreen = !screen->fullscreen;
 
                         SDL_SetWindowFullscreen(
@@ -237,8 +248,16 @@
                 uint16_t mask = screen_get_button(event.key.keysym.sym);
 
                 psx_pad_button_press(screen->pad, 0, mask);
+
+                if (event.key.keysym.sym == SDLK_RETURN) {
+                    psx_exp2_atcons_put(screen->psx->exp2, 13);
+                }
             } break;
 
+            case SDL_TEXTINPUT: {
+                psx_exp2_atcons_put(screen->psx->exp2, event.text.text[0]);
+            } break;
+
             case SDL_KEYUP: {
                 uint16_t mask = screen_get_button(event.key.keysym.sym);
 
@@ -280,9 +299,12 @@
         screen->texture_height = PSX_GPU_FB_HEIGHT;
     } else {
         if (screen->fullscreen) {
-            screen->width = 1920;
-            screen->height = 1080;
+            SDL_DisplayMode dm;
+            SDL_GetCurrentDisplayMode(0, &dm);
 
+            screen->width = dm.w;
+            screen->height = dm.h;
+
             if (screen->vertical_mode) {
                 screen->image_width = screen->height;
                 screen->image_height = (double)screen->height / psx_get_display_aspect(screen->psx);
@@ -330,6 +352,8 @@
         SDL_TEXTUREACCESS_STREAMING,
         screen->texture_width, screen->texture_height
     );
+
+    SDL_SetTextureScaleMode(screen->texture, screen->bilinear);
 
     SDL_SetWindowSize(screen->window, screen->width, screen->height);
 }
--- a/frontend/screen.h
+++ b/frontend/screen.h
@@ -23,6 +23,7 @@
     unsigned int format;
     unsigned int texture_width, texture_height;
 
+    int bilinear;
     int fullscreen;
     int vertical_mode;
     int debug_mode;
--- a/psx/bus.c
+++ b/psx/bus.c
@@ -48,6 +48,7 @@
     HANDLE_READ(ram, 32);
     HANDLE_READ(dma, 32);
     HANDLE_READ(exp1, 32);
+    HANDLE_READ(exp2, 32);
     HANDLE_READ(mc1, 32);
     HANDLE_READ(mc2, 32);
     HANDLE_READ(mc3, 32);
@@ -82,6 +83,7 @@
     HANDLE_READ(ram, 16);
     HANDLE_READ(dma, 16);
     HANDLE_READ(exp1, 16);
+    HANDLE_READ(exp2, 16);
     HANDLE_READ(mc1, 16);
     HANDLE_READ(mc2, 16);
     HANDLE_READ(mc3, 16);
@@ -112,6 +114,7 @@
     HANDLE_READ(ram, 8);
     HANDLE_READ(dma, 8);
     HANDLE_READ(exp1, 8);
+    HANDLE_READ(exp2, 8);
     HANDLE_READ(mc1, 8);
     HANDLE_READ(mc2, 8);
     HANDLE_READ(mc3, 8);
@@ -146,6 +149,7 @@
     HANDLE_WRITE(ram, 32);
     HANDLE_WRITE(dma, 32);
     HANDLE_WRITE(exp1, 32);
+    HANDLE_WRITE(exp2, 32);
     HANDLE_WRITE(mc1, 32);
     HANDLE_WRITE(mc2, 32);
     HANDLE_WRITE(mc3, 32);
@@ -178,6 +182,7 @@
     HANDLE_WRITE(ram, 16);
     HANDLE_WRITE(dma, 16);
     HANDLE_WRITE(exp1, 16);
+    HANDLE_WRITE(exp2, 16);
     HANDLE_WRITE(mc1, 16);
     HANDLE_WRITE(mc2, 16);
     HANDLE_WRITE(mc3, 16);
@@ -206,6 +211,7 @@
     HANDLE_WRITE(ram, 8);
     HANDLE_WRITE(dma, 8);
     HANDLE_WRITE(exp1, 8);
+    HANDLE_WRITE(exp2, 8);
     HANDLE_WRITE(mc1, 8);
     HANDLE_WRITE(mc2, 8);
     HANDLE_WRITE(mc3, 8);
@@ -237,6 +243,10 @@
 
 void psx_bus_init_exp1(psx_bus_t* bus, psx_exp1_t* exp1) {
     bus->exp1 = exp1;
+}
+
+void psx_bus_init_exp2(psx_bus_t* bus, psx_exp2_t* exp2) {
+    bus->exp2 = exp2;
 }
 
 void psx_bus_init_mc1(psx_bus_t* bus, psx_mc1_t* mc1) {
--- a/psx/bus_init.h
+++ b/psx/bus_init.h
@@ -5,6 +5,7 @@
 #include "dev/ram.h"
 #include "dev/dma.h"
 #include "dev/exp1.h"
+#include "dev/exp2.h"
 #include "dev/mc1.h"
 #include "dev/mc2.h"
 #include "dev/mc3.h"
@@ -22,6 +23,7 @@
     psx_ram_t* ram;
     psx_dma_t* dma;
     psx_exp1_t* exp1;
+    psx_exp2_t* exp2;
     psx_mc1_t* mc1;
     psx_mc2_t* mc2;
     psx_mc3_t* mc3;
@@ -41,6 +43,7 @@
 void psx_bus_init_ram(psx_bus_t*, psx_ram_t*);
 void psx_bus_init_dma(psx_bus_t*, psx_dma_t*);
 void psx_bus_init_exp1(psx_bus_t*, psx_exp1_t*);
+void psx_bus_init_exp2(psx_bus_t*, psx_exp2_t*);
 void psx_bus_init_mc1(psx_bus_t*, psx_mc1_t*);
 void psx_bus_init_mc2(psx_bus_t*, psx_mc2_t*);
 void psx_bus_init_mc3(psx_bus_t*, psx_mc3_t*);
--- a/psx/dev/bios.c
+++ b/psx/dev/bios.c
@@ -15,8 +15,6 @@
     bios->io_base = PSX_BIOS_BEGIN;
     bios->io_size = PSX_BIOS_SIZE;
     bios->bus_delay = 18;
-
-    bios->buf = (uint8_t*)malloc(PSX_BIOS_SIZE);
 }
 
 void psx_bios_load(psx_bios_t* bios, const char* path) {
@@ -28,7 +26,21 @@
         exit(1);
     }
 
-    if (!fread(bios->buf, 1, PSX_BIOS_SIZE, file)) {
+    // Almost all PS1 BIOS ROMs are 512 KiB in size.
+    // There's (at least) one exception, and that is SCPH-5903.
+    // This is a special asian model PS1 that had built-in support
+    // for Video CD (VCD) playback. Its BIOS is double the normal
+    // size
+    fseek(file, 0, SEEK_END);
+
+    size_t size = ftell(file);
+
+    fseek(file, 0, SEEK_SET);
+
+    bios->buf = malloc(size);
+    bios->io_size = size;
+
+    if (!fread(bios->buf, 1, size, file)) {
         perror("Error reading BIOS file");
 
         exit(1);
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -1,6 +1,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "cdrom.h"
 #include "../log.h"
@@ -21,6 +22,106 @@
     Modchip:Audio/Mode1    INT3(stat)     INT2(02h,00h, 00h,00h, 53h,43h,45h,4xh)
 */
 
+msf_t cdrom_get_track_addr(psx_cdrom_t* cdrom, msf_t msf) {
+    uint32_t lba = msf_to_address(msf);
+
+    int num_tracks, track;
+
+    psx_disc_get_track_count(cdrom->disc, &num_tracks);
+
+    for (track = 1; track < num_tracks - 1; track++) {
+        msf_t curr, next;
+
+        psx_disc_get_track_addr(cdrom->disc, &curr, track);
+        psx_disc_get_track_addr(cdrom->disc, &next, track + 1);
+
+        uint32_t curr_lba = msf_to_address(curr);
+        uint32_t next_lba = msf_to_address(next);
+
+        printf("lba=%02u:%02u:%02u (%08x) curr=%02u:%02u:%02u (%08x) next=%02u:%02u:%02u (%08x)\n",
+            msf.m,
+            msf.s,
+            msf.f,
+            lba,
+            curr.m,
+            curr.s,
+            curr.f,
+            curr_lba,
+            next.m,
+            next.s,
+            next.f,
+            next_lba
+        );
+
+        if ((lba >= curr_lba) && (lba < next_lba))
+            break;
+    }
+
+    msf_t track_msf;
+
+    psx_disc_get_track_addr(cdrom->disc, &track_msf, track);
+
+    return track_msf;
+}
+
+void cdrom_fetch_video_sector(psx_cdrom_t* cdrom) {
+    while (true) {
+        if (psx_disc_seek(cdrom->disc, cdrom->seek_msf))
+            return;
+
+        psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
+
+        // printf("%02u:%02u:%02u - file=%02x channel=%02x sm=%02x (%u%u%u%u%u%u%u%u) ci=%02x... ",
+        //     cdrom->seek_msf.m,
+        //     cdrom->seek_msf.s,
+        //     cdrom->seek_msf.f,
+        //     cdrom->dfifo[0x10],
+        //     cdrom->dfifo[0x11],
+        //     cdrom->dfifo[0x12],
+        //     (cdrom->dfifo[0x12] & 0x80) != 0,
+        //     (cdrom->dfifo[0x12] & 0x40) != 0,
+        //     (cdrom->dfifo[0x12] & 0x20) != 0,
+        //     (cdrom->dfifo[0x12] & 0x10) != 0,
+        //     (cdrom->dfifo[0x12] & 0x08) != 0,
+        //     (cdrom->dfifo[0x12] & 0x04) != 0,
+        //     (cdrom->dfifo[0x12] & 0x02) != 0,
+        //     (cdrom->dfifo[0x12] & 0x01) != 0,
+        //     cdrom->dfifo[0x13]
+        // );
+
+        msf_add_f(&cdrom->seek_msf, 1);
+
+        // Check RT and Video/Data bit
+        uint8_t sm = cdrom->dfifo[0x12] & 4;
+
+        if (sm) {
+            // printf("Not sent (Unfiltered)\n");
+
+            continue;
+        }
+
+        // If we get here it means this is a real-time video sector.
+        // If the XA filter is disabled, we're done
+        if (!(cdrom->mode & MODE_XA_FILTER)) {
+            // printf("Sent (Unfiltered)\n");
+            return;
+        }
+
+        // Else check XA file/channel
+        int file_eq = cdrom->dfifo[0x10] == cdrom->xa_file;
+        int channel_eq = cdrom->dfifo[0x11] == cdrom->xa_channel;
+
+        // If they are equal to our filter values, we're done
+        // else keep searching
+        if (file_eq && channel_eq) {
+            // printf("Sent (Filtered)\n");
+            return;
+        }
+
+        // printf("Not sent (Filtered)\n");
+    }
+}
+
 #define GETID_RESPONSE_SIZE 8
 #define GETID_RESPONSE_END (GETID_RESPONSE_SIZE - 1)
 
@@ -157,10 +258,18 @@
     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->ongoing_read_command) {
+                cdrom->status |= STAT_BUSYSTS_MASK;
+                // printf("command=%02x\n", cdrom->ongoing_read_command);
+                cdrom->state = CD_STATE_SEND_RESP2;
+                cdrom->delayed_command = cdrom->ongoing_read_command;
+                cdrom->irq_delay = DELAY_1MS;
+
+                return;
+            }
+
             if (cdrom->pfifo_index) {
                 log_fatal("CdlGetStat: Expected exactly 0 parameters");
 
@@ -183,12 +292,15 @@
             RESP_PUSH(
                 GETSTAT_MOTOR |
                 (cdrom->cdda_playing ? GETSTAT_PLAY : 0) |
+                // (cdrom->ongoing_read_command ? GETSTAT_READ : 0) |
                 (cdrom->disc ? 0 : GETSTAT_TRAYOPEN)
             );
 
-            if (cdrom->read_ongoing) {
+            if (cdrom->ongoing_read_command) {
+                cdrom->status |= STAT_BUSYSTS_MASK;
+                // printf("command=%02x\n", cdrom->ongoing_read_command);
                 cdrom->state = CD_STATE_SEND_RESP2;
-                cdrom->delayed_command = CDL_READN;
+                cdrom->delayed_command = cdrom->ongoing_read_command;
                 cdrom->irq_delay = DELAY_1MS;
             } else {
                 cdrom->delayed_command = CDL_NONE;
@@ -216,16 +328,6 @@
                 return;
             }
 
-            //if (!cdrom->read_ongoing) {
-                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 = CDL_READN;
-            //     cdrom->state = CD_STATE_SEND_RESP2;
-            // }
-
             int f = PFIFO_POP;
             int s = PFIFO_POP;
             int m = PFIFO_POP;
@@ -240,26 +342,40 @@
                 return;
             }
 
-            cdrom->seek_ff = f;
-            cdrom->seek_ss = s;
-            cdrom->seek_mm = m;
+            cdrom->seek_msf.m = m;
+            cdrom->seek_msf.s = s;
+            cdrom->seek_msf.f = f;
 
+            msf_from_bcd(&cdrom->seek_msf);
+
+            cdrom->cdda_msf = cdrom->seek_msf;
+
             cdrom->seek_pending = 1;
 
             log_fatal("setloc: %02x:%02x:%02x",
-                cdrom->seek_mm,
-                cdrom->seek_ss,
-                cdrom->seek_ff
+                cdrom->seek_msf.m,
+                cdrom->seek_msf.s,
+                cdrom->seek_msf.f
             );
+
+            cdrom->irq_delay = DELAY_1MS;
+            cdrom->delayed_command = CDL_SETLOC;
+            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;
+            if (cdrom->ongoing_read_command) {
+                // printf("command=%02x\n", 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;
 
         // Read ongoing
@@ -269,7 +385,7 @@
             int m = PFIFO_POP;
 
             if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {
-                cdrom->read_ongoing = false;
+                cdrom->ongoing_read_command = false;
                 cdrom->irq_delay = DELAY_1MS;
                 cdrom->delayed_command = CDL_ERROR;
                 cdrom->state = CD_STATE_ERROR;
@@ -279,9 +395,9 @@
                 return;
             }
 
-            cdrom->seek_ff = f;
-            cdrom->seek_ss = s;
-            cdrom->seek_mm = m;
+            cdrom->seek_msf.m = m;
+            cdrom->seek_msf.s = s;
+            cdrom->seek_msf.f = f;
         } break;
     }
 }
@@ -316,9 +432,9 @@
                 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_msf.m = cdrom->cdda_msf.m;
+                cdrom->seek_msf.s = cdrom->cdda_msf.s;
+                cdrom->seek_msf.f = cdrom->cdda_msf.f;
     
                 cdrom->seek_pending = 1;
             }
@@ -326,24 +442,17 @@
             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;
+                printf("Seeked to location\n");
 
-                // Convert seek to I
-                msf_t msf = cdrom->cdda_msf;
-                
-                msf_from_bcd(&msf);
-
+                cdrom->cdda_msf = cdrom->seek_msf;
+
                 // Seek to that address and read sector
-                psx_disc_seek(cdrom->disc, msf);
+                psx_disc_seek(cdrom->disc, cdrom->cdda_msf);
                 psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
 
                 // Increment sector
-                msf_add_f(&msf, 1);
-                msf_to_bcd(&msf);
+                msf_add_f(&cdrom->cdda_msf, 1);
 
-                cdrom->cdda_msf = msf;
                 cdrom->cdda_sector_offset = 0;
             }
 
@@ -361,7 +470,7 @@
 }
 void cdrom_cmd_readn(psx_cdrom_t* cdrom) {
     cdrom->delayed_command = CDL_NONE;
-    cdrom->read_ongoing = 1;
+    cdrom->ongoing_read_command = CDL_READN;
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
@@ -377,17 +486,8 @@
             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);
-
-            cdrom->xa_msf = msf;
-
             if (cdrom->mode & MODE_XA_ADPCM) {
+                cdrom->xa_msf = cdrom->seek_msf;
                 cdrom->xa_playing = 1;
 
                 SET_BITS(status, STAT_ADPBUSY_MASK, STAT_ADPBUSY_MASK);
@@ -402,7 +502,7 @@
                 );
             }
 
-            int err = psx_disc_seek(cdrom->disc, msf);
+            int err = psx_disc_seek(cdrom->disc, cdrom->seek_msf);
 
             if (err) {
                 log_set_quiet(0);
@@ -420,34 +520,6 @@
 
             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;
@@ -461,44 +533,31 @@
         } break;
 
         case CD_STATE_SEND_RESP2: {
-            log_fatal("CdlReadN: CD_STATE_SEND_RESP2");
+            log_fatal("CdlReadS: CD_STATE_SEND_RESP2");
 
-            msf_t msf;
+            // Returning only non-ADPCM sectors causes desync for some
+            // reason. I'll keep returning all sectors for now
 
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
+            if (cdrom->mode & MODE_XA_ADPCM) {
+                // printf("ReadS fetching non ADPCM sector...\n");
+                cdrom_fetch_video_sector(cdrom);
 
-            msf_from_bcd(&msf);
+                printf("%02u:%02u:%02u - file=%02x channel=%02x sm=%02x ci=%02x\n",
+                    cdrom->seek_msf.m,
+                    cdrom->seek_msf.s,
+                    cdrom->seek_msf.f,
+                    cdrom->dfifo[0x10],
+                    cdrom->dfifo[0x11],
+                    cdrom->dfifo[0x12],
+                    cdrom->dfifo[0x13]
+                );
+            } else {
+                psx_disc_seek(cdrom->disc, cdrom->seek_msf);
+                psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
 
-            psx_disc_seek(cdrom->disc, msf);
-            psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
-            // 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);
+                msf_add_f(&cdrom->seek_msf, 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;
@@ -516,7 +575,7 @@
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
-            cdrom->read_ongoing = 0;
+            cdrom->ongoing_read_command = CDL_NONE;
             cdrom->cdda_playing = 0;
 
             cdrom->irq_delay = DELAY_1MS;
@@ -549,18 +608,17 @@
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
-            cdrom->read_ongoing = 0;
+            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;
+            cdrom->seek_msf.m = 0;
+            cdrom->seek_msf.s = 0;
+            cdrom->seek_msf.f = 0;
+
+            cdrom->cdda_msf = cdrom->seek_msf;
         } break;
 
         case CD_STATE_SEND_RESP1: {
@@ -588,7 +646,7 @@
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
-            cdrom->read_ongoing = 0;
+            cdrom->ongoing_read_command = CDL_NONE;
             cdrom->cdda_playing = 0;
             cdrom->xa_playing = 0;
 
@@ -627,20 +685,20 @@
             cdrom->irq_delay = DELAY_1MS;
             cdrom->state = CD_STATE_SEND_RESP1;
             cdrom->delayed_command = CDL_INIT;
-            cdrom->read_ongoing = 0;
+            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;
+            cdrom->seek_msf.m = 0;
+            cdrom->seek_msf.s = 2;
+            cdrom->seek_msf.f = 0;
         } break;
 
         case CD_STATE_SEND_RESP1: {
             SET_BITS(ifr, IFR_INT, 3);
-            RESP_PUSH(cdrom->stat);
+            RESP_PUSH(GETSTAT_MOTOR);
 
             cdrom->irq_delay = DELAY_1MS;
             cdrom->state = CD_STATE_SEND_RESP2;
@@ -649,7 +707,7 @@
 
         case CD_STATE_SEND_RESP2: {
             SET_BITS(ifr, IFR_INT, 2);
-            RESP_PUSH(cdrom->stat);
+            RESP_PUSH(GETSTAT_MOTOR);
 
             cdrom->state = CD_STATE_RECV_CMD;
             cdrom->delayed_command = CDL_NONE;
@@ -682,8 +740,15 @@
             SET_BITS(ifr, IFR_INT, IFR_INT3);
             RESP_PUSH(cdrom->stat);
 
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
+            if (cdrom->ongoing_read_command) {
+                // printf("command=%02x\n", 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;
     }
 }
@@ -718,6 +783,12 @@
             SET_BITS(ifr, IFR_INT, IFR_INT3);
             RESP_PUSH(cdrom->stat);
 
+            if (cdrom->ongoing_read_command) {
+                cdrom->irq_delay = DELAY_1MS;
+                cdrom->delayed_command = cdrom->ongoing_read_command;
+                cdrom->state = CD_STATE_SEND_RESP2;
+            }
+
             cdrom->state = CD_STATE_RECV_CMD;
         } break;
     }
@@ -755,8 +826,14 @@
             cdrom->delayed_command = CDL_NONE;
     
             SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_PLAY);
+            RESP_PUSH(GETSTAT_MOTOR);
 
+            if (cdrom->ongoing_read_command) {
+                cdrom->irq_delay = DELAY_1MS;
+                cdrom->delayed_command = cdrom->ongoing_read_command;
+                cdrom->state = CD_STATE_SEND_RESP2;
+            }
+
             cdrom->state = CD_STATE_RECV_CMD;
         } break;
     }
@@ -795,17 +872,24 @@
 
         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);
+            RESP_PUSH(cdrom->dfifo[0x13]);
+            RESP_PUSH(cdrom->dfifo[0x12]);
+            RESP_PUSH(cdrom->dfifo[0x11]);
+            RESP_PUSH(cdrom->dfifo[0x10]);
+            RESP_PUSH(cdrom->dfifo[0x0f]);
+            RESP_PUSH(cdrom->dfifo[0x0e]);
+            RESP_PUSH(cdrom->dfifo[0x0d]);
+            RESP_PUSH(cdrom->dfifo[0x0c]);
 
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
+            // if (cdrom->ongoing_read_command) {
+            //     // printf("command=%02x\n", 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;
     }
 }
@@ -818,18 +902,59 @@
         } break;
 
         case CD_STATE_SEND_RESP1: {
+            msf_t absolute = cdrom->xa_playing ? cdrom->xa_msf : cdrom->seek_msf;
+            msf_t relative = absolute;
+            msf_t track_msf = cdrom_get_track_addr(cdrom, absolute);
+
+            relative.m -= track_msf.m;
+            relative.s -= track_msf.s;
+            relative.f -= track_msf.f;
+
+            msf_adjust_sub(&relative);
+
+            // printf("abs=%02u:%02u:%02u tra=%02u:%02u:%02u rel=%02u:%02u:%02u\n",
+            //     absolute.m,
+            //     absolute.s,
+            //     absolute.f,
+            //     track_msf.m,
+            //     track_msf.s,
+            //     track_msf.f,
+            //     relative.m,
+            //     relative.s,
+            //     relative.f
+            // );
+
+            msf_to_bcd(&absolute);
+            msf_to_bcd(&relative);
+
+            // printf("getlocp 01 01 %02x:%02x:%02x %02x:%02x:%02x\n",
+            //     relative.m,
+            //     relative.s,
+            //     relative.f,
+            //     absolute.m,
+            //     absolute.s,
+            //     absolute.f
+            // );
+
             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(absolute.f);
+            RESP_PUSH(absolute.s);
+            RESP_PUSH(absolute.m);
+            RESP_PUSH(relative.f);
+            RESP_PUSH(relative.s);
+            RESP_PUSH(relative.m);
             RESP_PUSH(0x01);
-            RESP_PUSH(0xff);
+            RESP_PUSH(0x01);
 
-            cdrom->delayed_command = CDL_NONE;
-            cdrom->state = CD_STATE_RECV_CMD;
+            if (cdrom->ongoing_read_command) {
+                //// printf("command=%02x\n", 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;
     }
 }
@@ -877,7 +1002,7 @@
                 return;
             }
 
-            cdrom->irq_delay = DELAY_1MS;
+            cdrom->irq_delay = DELAY_1MS * 2;
             cdrom->delayed_command = CDL_GETTN;
             cdrom->state = CD_STATE_SEND_RESP1;
         } break;
@@ -903,7 +1028,7 @@
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
             if (cdrom->pfifo_index != 1) {
-                log_fatal("CdlGetTD: Expected exactly 0 parameters");
+                log_fatal("CdlGetTD: Expected exactly 1 parameter");
 
                 cdrom->irq_delay = DELAY_1MS;
                 cdrom->delayed_command = CDL_ERROR;
@@ -916,6 +1041,16 @@
 
             cdrom->gettd_track = PFIFO_POP;
 
+            if (!VALID_BCD(cdrom->gettd_track)) {
+                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;
+            }
+
             int err = psx_disc_get_track_addr(cdrom->disc, NULL, cdrom->gettd_track);
 
             if (err) {
@@ -922,7 +1057,7 @@
                 cdrom->irq_delay = DELAY_1MS;
                 cdrom->delayed_command = CDL_ERROR;
                 cdrom->state = CD_STATE_ERROR;
-                cdrom->error = ERR_PCOUNT;
+                cdrom->error = ERR_INVSUBF;
                 cdrom->error_flags = GETSTAT_ERROR;
 
                 return;
@@ -978,16 +1113,8 @@
             cdrom->state = CD_STATE_SEND_RESP2;
             cdrom->delayed_command = CDL_SEEKL;
 
-            msf_t msf;
+            psx_disc_seek(cdrom->disc, cdrom->seek_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;
 
@@ -1030,16 +1157,8 @@
             cdrom->state = CD_STATE_SEND_RESP2;
             cdrom->delayed_command = CDL_SEEKP;
 
-            msf_t msf;
+            psx_disc_seek(cdrom->disc, cdrom->seek_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;
 
@@ -1087,12 +1206,13 @@
 
         case CD_STATE_SEND_RESP1: {
             cdrom->delayed_command = CDL_NONE;
-    
+
+            // 95h,05h,16h,C1h
             SET_BITS(ifr, IFR_INT, IFR_INT3);
-            RESP_PUSH(0xc0);
-            RESP_PUSH(0x19);
-            RESP_PUSH(0x09);
-            RESP_PUSH(0x94);
+            RESP_PUSH(0xc1);
+            RESP_PUSH(0x16);
+            RESP_PUSH(0x05);
+            RESP_PUSH(0x95);
 
             cdrom->state = CD_STATE_RECV_CMD;
         } break;
@@ -1163,7 +1283,7 @@
 }
 void cdrom_cmd_reads(psx_cdrom_t* cdrom) {
     cdrom->delayed_command = CDL_NONE;
-    cdrom->read_ongoing = 1;
+    cdrom->ongoing_read_command = CDL_READS;
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
@@ -1174,27 +1294,19 @@
         } break;
 
         case CD_STATE_SEND_RESP1: {
+            printf("CdlReadS: CD_STATE_SEND_RESP1\n");
             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);
-
-            cdrom->xa_msf = msf;
-
             if (cdrom->mode & MODE_XA_ADPCM) {
+                cdrom->xa_msf = cdrom->seek_msf;
                 cdrom->xa_playing = 1;
 
                 SET_BITS(status, STAT_ADPBUSY_MASK, STAT_ADPBUSY_MASK);
 
-                printf("Play XA-ADPCM encoded song at %02u:%02u:%02u, filter=%u, file=%02x, channel=%02x\n",
+                printf("Play XA-ADPCM encoded song at %02u:%02u:%02u, filter=%u, file=%02x, channel=%02x (ReadS)\n",
                     cdrom->xa_msf.m,
                     cdrom->xa_msf.s,
                     cdrom->xa_msf.f,
@@ -1205,17 +1317,19 @@
 
                 int double_speed = cdrom->mode & MODE_SPEED;
 
-                cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
+                cdrom->irq_delay = DELAY_1MS * 2;
                 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;
-                }
+                // if (cdrom->spin_delay) {
+                //     cdrom->irq_delay += cdrom->spin_delay;
+                //     cdrom->spin_delay = 0;
+                // }
+
+                return;
             }
 
-            int err = psx_disc_seek(cdrom->disc, msf);
+            int err = psx_disc_seek(cdrom->disc, cdrom->seek_msf);
 
             if (err) {
                 log_fatal("CdlReadS: Out of bounds seek");
@@ -1244,52 +1358,117 @@
         case CD_STATE_SEND_RESP2: {
             log_fatal("CdlReadS: CD_STATE_SEND_RESP2");
 
-            msf_t msf;
+            // Returning only non-ADPCM sectors causes desync for some
+            // reason. I'll keep returning all sectors for now
 
-            msf.m = cdrom->seek_mm;
-            msf.s = cdrom->seek_ss;
-            msf.f = cdrom->seek_ff;
+            if (cdrom->mode & MODE_XA_ADPCM) {
+                // printf("ReadS fetching non ADPCM sector...\n");
+                cdrom_fetch_video_sector(cdrom);
 
-            msf_from_bcd(&msf);
+                // printf("%02u:%02u:%02u - file=%02x channel=%02x sm=%02x ci=%02x\n",
+                //     cdrom->seek_msf.m,
+                //     cdrom->seek_msf.s,
+                //     cdrom->seek_msf.f,
+                //     cdrom->dfifo[0x10],
+                //     cdrom->dfifo[0x11],
+                //     cdrom->dfifo[0x12],
+                //     cdrom->dfifo[0x13]
+                // );
+            } else {
+                psx_disc_seek(cdrom->disc, cdrom->seek_msf);
+                psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
 
-            psx_disc_seek(cdrom->disc, msf);
-            psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
+                msf_add_f(&cdrom->seek_msf, 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->irq_delay = DELAY_1MS * 2;
             cdrom->state = CD_STATE_SEND_RESP2;
             cdrom->delayed_command = CDL_READS;
             cdrom->dfifo_index = 0;
 
-            if (cdrom->mode & MODE_XA_ADPCM) {
-                int double_speed = cdrom->mode & MODE_SPEED;
+            SET_BITS(ifr, IFR_INT, IFR_INT1);
+            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+        } break;
+    }
+}
+void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) {
+    switch (cdrom->state) {
+        case CD_STATE_RECV_CMD: {
+            cdrom->status |= STAT_BUSYSTS_MASK;
+            cdrom->irq_delay = DELAY_1MS * 1000;
+            cdrom->state = CD_STATE_SEND_RESP1;
+            cdrom->delayed_command = CDL_READTOC;
+        } break;
 
-                if (cdrom->dfifo[0x12] & 4) {
-                    cdrom->irq_delay = 10;
-                    cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
-                    cdrom->state = CD_STATE_SEND_RESP2;
-                    cdrom->delayed_command = CDL_READS;
-                    // cdrom->irq_disable = 1;
+        case CD_STATE_SEND_RESP1: {
+            cdrom->status &= ~STAT_BUSYSTS_MASK;
+            SET_BITS(ifr, IFR_INT, 3);
+            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
 
-                    return;
-                }
+            cdrom->irq_delay = DELAY_1MS * 1000;
+            cdrom->state = CD_STATE_SEND_RESP2;
+            cdrom->delayed_command = CDL_READTOC;
+        } 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_videocd(psx_cdrom_t* cdrom) {
+    switch (cdrom->state) {
+        case CD_STATE_RECV_CMD: {
+            cdrom->irq_delay = DELAY_1MS;
+            cdrom->state = CD_STATE_SEND_RESP1;
+            cdrom->delayed_command = CDL_VIDEOCD;
+            cdrom->pfifo_index = 0;
+        } break;
+
+        case CD_STATE_SEND_RESP1: {
+            printf("VideoCD task %02x\n", cdrom->pfifo[4]);
+            SET_BITS(ifr, IFR_INT, 3);
+
+            switch (cdrom->pfifo[4]) {
+                case 0: {
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(GETSTAT_MOTOR);
+                } break;
+
+                case 1: {
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x81);
+                    RESP_PUSH(GETSTAT_MOTOR);
+                } break;
+
+                case 2: {
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x00);
+                    RESP_PUSH(0x05);
+                    RESP_PUSH(GETSTAT_MOTOR);
+                } break;
             }
 
-            SET_BITS(ifr, IFR_INT, IFR_INT1);
-            RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
+            cdrom->irq_delay = DELAY_1MS;
+            cdrom->state = CD_STATE_RECV_CMD;
+            cdrom->delayed_command = CDL_NONE;
         } break;
     }
 }
-void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) { log_fatal("readtoc: Unimplemented"); exit(1); }
 
 typedef void (*cdrom_cmd_t)(psx_cdrom_t*);
 
@@ -1325,7 +1504,7 @@
     "CdlUnimplemented",
     "CdlUnimplemented",
     "CdlReadtoc",
-    "CdlUnimplemented"
+    "CdlVideoCD"
 };
 
 cdrom_cmd_t g_psx_cdrom_command_table[] = {
@@ -1360,6 +1539,7 @@
     cdrom_cmd_unimplemented,
     cdrom_cmd_unimplemented,
     cdrom_cmd_readtoc,
+    cdrom_cmd_videocd,
 
     // Actually an unimplemented command, we use this
     // index for CD error handling
@@ -1418,8 +1598,7 @@
 }
 
 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]",
+    // printf("%s(%02x) %u params=[%02x, %02x, %02x, %02x, %02x, %02x]\n",
     //     g_psx_cdrom_command_names[value],
     //     value,
     //     cdrom->pfifo_index,
@@ -1430,7 +1609,6 @@
     //     cdrom->pfifo[4],
     //     cdrom->pfifo[5]
     // );
-    // log_set_quiet(1);
 
     cdrom->command = value;
     cdrom->state = CD_STATE_RECV_CMD;
@@ -1577,6 +1755,10 @@
     memset(cdrom->xa_left_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
     memset(cdrom->xa_right_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
     memset(cdrom->xa_mono_resample_buf, 0, (XA_MONO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
+
+    cdrom->seek_msf.m = 0;
+    cdrom->seek_msf.s = 2;
+    cdrom->seek_msf.f = 0;
 }
 
 uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t offset) {
@@ -1730,7 +1912,16 @@
 void psx_cdrom_open(psx_cdrom_t* cdrom, const char* path) {
     cdrom->disc = psx_disc_create();
 
-    int ext = cdrom_get_extension(path);
+    int len = strlen(path);
+
+    char* lower = malloc(len + 1);
+
+    for (int i = 0; i < len; i++)
+        lower[i] = tolower(path[i]);
+
+    lower[len] = '\0';
+
+    int ext = cdrom_get_extension(lower);
     int error = 0;
 
     switch (ext) {
@@ -1739,6 +1930,7 @@
 
             psxd_cue_init_disc(cue, cdrom->disc);
             psxd_cue_init(cue);
+
             error = psxd_cue_load(cue, path);
 
             if (error)
@@ -1768,6 +1960,8 @@
         } break;
     }
 
+    free(lower);
+
     if (error) {
         log_fatal("Error loading file \'%s\'", path);
 
@@ -1963,13 +2157,8 @@
         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))
+    if (psx_disc_seek(cdrom->disc, cdrom->cdda_msf))
         cdrom->cdda_playing = 0;
 
     psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
@@ -1977,11 +2166,7 @@
     ++cdrom->cdda_sectors_played;
 
     // Increment sector
-    msf_add_f(&msf, 1);
-    msf_to_bcd(&msf);
-    
-    // Assign to CDDA MSF
-    cdrom->cdda_msf = msf;
+    msf_add_f(&cdrom->cdda_msf, 1);
 
     memcpy(buf, cdrom->cdda_buf, size);
 
--- a/psx/dev/cdrom.h
+++ b/psx/dev/cdrom.h
@@ -58,7 +58,8 @@
 #define CDL_RESET       0x1c
 #define CDL_GETQ        0x1d
 #define CDL_READTOC     0x1e
-#define CDL_ERROR       0x1f
+#define CDL_VIDEOCD     0x1f
+#define CDL_ERROR       0x20
 
 #define STAT_INDEX_MASK   0x3
 #define STAT_ADPBUSY_MASK 0x4
@@ -178,7 +179,7 @@
     int tray_open;
 
     // Setloc
-    uint8_t seek_ss, seek_mm, seek_ff;
+    msf_t seek_msf;
     uint32_t seek_offset;
     int seek_pending;
 
@@ -195,7 +196,7 @@
     int spin_delay;
     uint8_t error;
     uint8_t error_flags;
-    int read_ongoing;
+    int ongoing_read_command;
     int gettd_track;
 
     // CDDA
@@ -318,5 +319,6 @@
 void cdrom_cmd_getid(psx_cdrom_t*);
 void cdrom_cmd_reads(psx_cdrom_t*);
 void cdrom_cmd_readtoc(psx_cdrom_t*);
+void cdrom_cmd_videocd(psx_cdrom_t*);
 
 #endif
\ No newline at end of file
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -165,19 +165,28 @@
     if (!CHCR_BUSY(mdec_in))
         return;
 
+    // printf("dma mdec in size=%04x bcnt=%04x step=%u madr=%08x\n",
+    //     BCR_SIZE(mdec_in),
+    //     BCR_BCNT(mdec_in),
+    //     CHCR_STEP(mdec_in),
+    //     dma->mdec_in.madr
+    // );
+
     size_t size = BCR_SIZE(mdec_in) * BCR_BCNT(mdec_in);
 
+    int step = CHCR_STEP(mdec_in) ? -4 : 4;
+
     for (int i = 0; i < size; i++) {
         uint32_t data = psx_bus_read32(dma->bus, dma->mdec_in.madr);
 
         psx_bus_write32(dma->bus, 0x1f801820, data);
 
-        dma->mdec_in.madr += CHCR_STEP(mdec_in) ? -4 : 4;
+        dma->mdec_in.madr += step;
     }
 
     dma->mdec_in_irq_delay = size;
 
-    dma->mdec_in.chcr &= ~(CHCR_BUSY_MASK | CHCR_TRIG_MASK);
+    dma->mdec_in.chcr = 0;
     dma->mdec_in.bcr = 0;
 }
 
@@ -185,6 +194,15 @@
     if (!CHCR_BUSY(mdec_out))
         return;
 
+    // printf("dma mdec out size=%04x bcnt=%04x step=%u madr=%08x\n",
+    //     BCR_SIZE(mdec_out),
+    //     BCR_BCNT(mdec_out),
+    //     CHCR_STEP(mdec_out),
+    //     dma->mdec_out.madr
+    // );
+
+    // printf("mdec out transfer\n");
+
     size_t size = BCR_SIZE(mdec_out) * BCR_BCNT(mdec_out);
 
     for (int i = 0; i < size; i++) {
@@ -293,8 +311,7 @@
     if (!CHCR_BUSY(cdrom))
         return;
 
-    // log_set_quiet(0);
-    // log_fatal("CDROM DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x",
+    // printf("CDROM DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x\n",
     //     dma->cdrom.madr,
     //     CHCR_TDIR(cdrom) ? "to device" : "to RAM",
     //     g_psx_dma_sync_type_name_table[CHCR_SYNC(cdrom)], CHCR_SYNC(cdrom),
@@ -302,13 +319,12 @@
     //     BCR_SIZE(cdrom)
     // );
 
-    // log_fatal("DICR: force=%u, en=%02x, irqen=%u, flags=%02x",
+    // printf("DICR: force=%u, en=%02x, irqen=%u, flags=%02x\n",
     //     (dma->dicr >> 15) & 1,
     //     (dma->dicr >> 16) & 0x7f,
     //     (dma->dicr >> 23) & 1,
     //     (dma->dicr >> 24) & 0x7f
     // );
-    // log_set_quiet(1);    
 
     uint32_t size = BCR_SIZE(cdrom);
 
--- a/psx/dev/exp1.c
+++ b/psx/dev/exp1.c
@@ -9,7 +9,7 @@
     return (psx_exp1_t*)malloc(sizeof(psx_exp1_t));
 }
 
-void psx_exp1_init(psx_exp1_t* exp1, psx_mc1_t* mc1) {
+void psx_exp1_init(psx_exp1_t* exp1, psx_mc1_t* mc1, const char* path) {
     memset(exp1, 0, sizeof(psx_exp1_t));
 
     exp1->io_base = PSX_EXP1_BEGIN;
@@ -19,6 +19,23 @@
     exp1->rom = (uint8_t*)malloc(PSX_EXP1_SIZE);
 
     memset(exp1->rom, 0xff, PSX_EXP1_SIZE);
+
+    if (path)
+        psx_exp1_load(exp1, path);
+}
+
+void psx_exp1_load(psx_exp1_t* exp1, const char* path) {
+    FILE* file = fopen(path, "rb");
+
+    if (!file) {
+        perror("Error opening expansion ROM file \'%s\'");
+
+        exit(1);
+    }
+
+    fread(exp1->rom, 1, PSX_EXP1_SIZE, file);
+
+    fclose(file);
 }
 
 uint32_t psx_exp1_read32(psx_exp1_t* exp1, uint32_t offset) {
--- a/psx/dev/exp1.h
+++ b/psx/dev/exp1.h
@@ -18,7 +18,8 @@
 } psx_exp1_t;
 
 psx_exp1_t* psx_exp1_create();
-void psx_exp1_init(psx_exp1_t*, psx_mc1_t*);
+void psx_exp1_init(psx_exp1_t*, psx_mc1_t*, const char*);
+void psx_exp1_load(psx_exp1_t*, const char*);
 uint32_t psx_exp1_read32(psx_exp1_t*, uint32_t);
 uint16_t psx_exp1_read16(psx_exp1_t*, uint32_t);
 uint8_t psx_exp1_read8(psx_exp1_t*, uint32_t);
--- /dev/null
+++ b/psx/dev/exp2.c
@@ -1,0 +1,80 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../log.h"
+#include "exp2.h"
+
+psx_exp2_t* psx_exp2_create() {
+    return (psx_exp2_t*)malloc(sizeof(psx_exp2_t));
+}
+
+void psx_exp2_init(psx_exp2_t* exp2, exp2_tty_tx atcons_tx, exp2_tty_tx duart_tx) {
+    memset(exp2, 0, sizeof(psx_exp2_t));
+
+    exp2->io_base = PSX_EXP2_BEGIN;
+    exp2->io_size = PSX_EXP2_SIZE;
+    exp2->atcons_tx = atcons_tx;
+    exp2->duart_tx = duart_tx;
+}
+
+void psx_exp2_atcons_put(psx_exp2_t* exp2, char c) {
+    exp2->atc_stat |= 0x10;
+    exp2->atc_rx = c;
+}
+
+void psx_exp2_duart_put(psx_exp2_t* exp2, char c) {
+    /* To-do */
+}
+
+uint32_t psx_exp2_read32(psx_exp2_t* exp2, uint32_t offset) {
+    return 0;
+}
+
+uint16_t psx_exp2_read16(psx_exp2_t* exp2, uint32_t offset) {
+    return 0;
+}
+
+uint8_t psx_exp2_read8(psx_exp2_t* exp2, uint32_t offset) {
+    switch (offset) {
+        case EXP2_DTL_ATC_STAT:
+            return exp2->atc_stat | 8;
+
+        case EXP2_DTL_ATC_DATA:
+            exp2->atc_stat &= 0xef;
+            return exp2->atc_rx;
+    }
+
+    return 0;
+}
+
+void psx_exp2_write32(psx_exp2_t* exp2, uint32_t offset, uint32_t value) {
+    log_warn("Unhandled 32-bit EXP2 write at offset %08x (%08x)", offset, value);
+}
+
+void psx_exp2_write16(psx_exp2_t* exp2, uint32_t offset, uint16_t value) {
+    log_warn("Unhandled 16-bit EXP2 write at offset %08x (%04x)", offset, value);
+}
+
+void psx_exp2_write8(psx_exp2_t* exp2, uint32_t offset, uint8_t value) {
+    switch (offset) {
+        case EXP2_DTL_ATC_DATA:
+            if (exp2->atcons_tx)
+                exp2->atcons_tx(exp2->atcons_udata, value);
+            return;
+        break;
+
+        case EXP2_LED:
+        case EXP2_POST:
+        case EXP2_POST2:
+            // To-do: Do something with this data
+            return;
+        break;
+    }
+
+    log_warn("Unhandled 8-bit EXP2 write at offset %08x (%02x)", offset, value);
+}
+
+void psx_exp2_destroy(psx_exp2_t* exp2) {
+    free(exp2);
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/exp2.h
@@ -1,0 +1,48 @@
+#ifndef EXP2_H
+#define EXP2_H
+
+#include <stdint.h>
+
+#define PSX_EXP2_BEGIN 0x1f802000
+#define PSX_EXP2_SIZE  0x1fe000
+#define PSX_EXP2_END   0x1f9fffff
+
+#define EXP2_DTL_ATC_STAT  0x00 // 1f802000
+#define EXP2_DTL_ATC_DATA  0x02 // 1f802002
+#define EXP2_DTL_HDATA     0x04 // 1f802004
+#define EXP2_DTL_SEC_IRQ10 0x30 // 1f802030
+#define EXP2_DTL_IRQ_CTRL  0x32 // 1f802032
+#define EXP2_DTL_BOOT_DIP  0x40 // 1f802040
+#define EXP2_POST          0x41 // 1f802041
+#define EXP2_LED           0x42 // 1f802042
+#define EXP2_POST2         0x70 // 1f802070
+
+typedef void (*exp2_tty_tx)(void*, uint8_t);
+
+typedef struct {
+    uint32_t bus_delay;
+    uint32_t io_base, io_size;
+
+    void* duart_udata;
+    void* atcons_udata;
+
+    exp2_tty_tx duart_tx;
+    exp2_tty_tx atcons_tx;
+
+    uint8_t atc_stat;
+    uint8_t atc_rx;
+} psx_exp2_t;
+
+psx_exp2_t* psx_exp2_create();
+void psx_exp2_init(psx_exp2_t*, exp2_tty_tx atcons_tx, exp2_tty_tx duart_tx);
+void psx_exp2_atcons_put(psx_exp2_t*, char);
+void psx_exp2_duart_put(psx_exp2_t*, char);
+uint32_t psx_exp2_read32(psx_exp2_t*, uint32_t);
+uint16_t psx_exp2_read16(psx_exp2_t*, uint32_t);
+uint8_t psx_exp2_read8(psx_exp2_t*, uint32_t);
+void psx_exp2_write32(psx_exp2_t*, uint32_t, uint32_t);
+void psx_exp2_write16(psx_exp2_t*, uint32_t, uint16_t);
+void psx_exp2_write8(psx_exp2_t*, uint32_t, uint8_t);
+void psx_exp2_destroy(psx_exp2_t*);
+
+#endif
\ No newline at end of file
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -1683,6 +1683,8 @@
 void gpu_hblank_event(psx_gpu_t* gpu) {
     gpu->line++;
 
+    psx_ic_irq(gpu->ic, IC_TIMER2);
+
     if (gpu->line < GPU_SCANS_PER_VDRAW_NTSC) {
         if (gpu->line & 1) {
             gpu->gpustat |= 1 << 31;
@@ -1701,9 +1703,9 @@
     } 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);
 
+        psx_ic_irq(gpu->ic, IC_SPU);
+
         gpu->line = 0;
     }
 }
@@ -1726,6 +1728,9 @@
     } else if (prev_hblank && !curr_hblank) {
         if (gpu->event_cb_table[GPU_EVENT_HBLANK_END])
             gpu->event_cb_table[GPU_EVENT_HBLANK_END](gpu);
+
+        //psx_ic_irq(gpu->ic, IC_SPU);
+        // psx_ic_irq(gpu->ic, IC_SPU);
         
         gpu->cycles -= (float)GPU_CYCLES_PER_SCANL_NTSC;
     }
--- a/psx/dev/mdec.c
+++ b/psx/dev/mdec.c
@@ -282,6 +282,13 @@
 uint32_t psx_mdec_read32(psx_mdec_t* mdec, uint32_t offset) {
     switch (offset) {
         case 0: {
+            // printf("mdec data read\n");
+            // mdec->output_empty = 1;
+            // mdec->output_index = 0;
+            // mdec->output_request = 0;
+
+            // return 0xaaaaaaaa;
+
             if (mdec->output_words_remaining) {
                 --mdec->output_words_remaining;
 
@@ -291,6 +298,7 @@
 
                 return ((uint32_t*)mdec->output)[mdec->output_index++];
             } else {
+                // printf("no read words remaining\n");
                 mdec->output_empty = 0;
                 mdec->output_index = 0;
                 mdec->output_request = 0;
@@ -299,6 +307,7 @@
             }
         } break;
         case 4: {
+            //printf("mdec status read\n");
             uint32_t status = 0;
 
             status |= mdec->words_remaining;
@@ -334,6 +343,7 @@
 void psx_mdec_write32(psx_mdec_t* mdec, uint32_t offset, uint32_t value) {
     switch (offset) {
         case 0: {
+            //printf("mdec data write\n");
             if (mdec->words_remaining) {
                 mdec->input[mdec->input_index++] = value;
 
@@ -340,6 +350,7 @@
                 --mdec->words_remaining;
 
                 if (!mdec->words_remaining) {
+                    //printf("no words remaining\n");
                     mdec->output_empty = 0;
                     mdec->input_full = 1;
                     mdec->input_request = 0;
@@ -367,6 +378,7 @@
             //log_set_quiet(0);
             switch (mdec->cmd >> 29) {
                 case MDEC_CMD_NOP: {
+                    //printf("mdec nop\n");
                     mdec->busy = 0;
                     mdec->words_remaining = 0;
 
@@ -374,15 +386,17 @@
                 } break;
 
                 case MDEC_CMD_DECODE: {
+                    //printf("mdec decode\n");
                     mdec->words_remaining = mdec->cmd & 0xffff;
 
-                    printf("MDEC %08x: decode macroblock %04x\n",
-                        mdec->cmd,
-                        mdec->words_remaining
-                    );
+                    // printf("MDEC %08x: decode macroblock %04x\n",
+                    //     mdec->cmd,
+                    //     mdec->words_remaining
+                    // );
                 } break;
 
                 case MDEC_CMD_SET_QT: {
+                    //printf("mdec setqt\n");
                     mdec->recv_color = mdec->cmd & 1;
                     mdec->words_remaining = mdec->recv_color ? 32 : 16;
 
@@ -393,6 +407,7 @@
                 } break;
 
                 case MDEC_CMD_SET_ST: {
+                    //printf("mdec setst\n");
                     mdec->words_remaining = 32;
 
                     log_fatal("MDEC %08x: set scale table %04x",
@@ -404,7 +419,7 @@
             // log_set_quiet(1);
 
             if (mdec->words_remaining) {
-                mdec->input_request = mdec->enable_dma0;
+                mdec->input_request = 1;
                 mdec->input_size = mdec->words_remaining * sizeof(uint32_t);
                 mdec->input_full = 0;
                 mdec->input_index = 0;
@@ -413,6 +428,7 @@
         } break;
 
         case 4: {
+            //printf("mdec status write\n");
             mdec->enable_dma0 = (value & 0x40000000) != 0;
             mdec->enable_dma1 = (value & 0x20000000) != 0;
 
--- a/psx/dev/old/cdrom.c
+++ b/psx/dev/old/cdrom.c
@@ -93,9 +93,9 @@
             SET_BITS(ifr, IFR_INT, IFR_INT3);
             RESP_PUSH(GETSTAT_MOTOR | (cdrom->disc ? 0 : GETSTAT_TRAYOPEN));
 
-            if (cdrom->read_ongoing) {
+            if (cdrom->ongoing_read_command) {
                 cdrom->state = CD_STATE_SEND_RESP2;
-                cdrom->delayed_command = CDL_READN;
+                cdrom->delayed_command = cdrom->ongoing_read_command;
                 cdrom->irq_delay = DELAY_1MS;
             } else {
                 cdrom->delayed_command = CDL_NONE;
@@ -121,13 +121,13 @@
                 return;
             }
 
-            if (!cdrom->read_ongoing) {
+            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 = CDL_READN;
+                cdrom->delayed_command = cdrom->ongoing_read_command;
                 cdrom->state = CD_STATE_SEND_RESP2;
             }
 
@@ -174,7 +174,7 @@
             int m = PFIFO_POP;
 
             if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {
-                cdrom->read_ongoing = false;
+                cdrom->ongoing_read_command = CDL_NONE;
                 cdrom->irq_delay = DELAY_1MS;
                 cdrom->delayed_command = CDL_ERROR;
                 cdrom->state = CD_STATE_ERROR;
@@ -257,7 +257,7 @@
 }
 void cdrom_cmd_readn(psx_cdrom_t* cdrom) {
     cdrom->delayed_command = CDL_NONE;
-    cdrom->read_ongoing = 1;
+    cdrom->ongoing_read_command = 1;
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
@@ -396,7 +396,7 @@
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
-            cdrom->read_ongoing = 0;
+            cdrom->ongoing_read_command = CDL_NONE;
             cdrom->cdda_playing = 0;
 
             cdrom->irq_delay = DELAY_1MS;
@@ -429,7 +429,7 @@
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
-            cdrom->read_ongoing = 0;
+            cdrom->ongoing_read_command = CDL_NONE;
             cdrom->cdda_playing = 0;
 
             cdrom->irq_delay = DELAY_1MS;
@@ -468,7 +468,7 @@
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
-            cdrom->read_ongoing = 0;
+            cdrom->ongoing_read_command = CDL_NONE;
             cdrom->cdda_playing = 0;
 
             cdrom->irq_delay = DELAY_1MS;
@@ -504,7 +504,7 @@
             cdrom->irq_delay = DELAY_1MS;
             cdrom->state = CD_STATE_SEND_RESP1;
             cdrom->delayed_command = CDL_INIT;
-            cdrom->read_ongoing = 0;
+            cdrom->ongoing_read_command = CDL_NONE;
             cdrom->mode = 0;
             cdrom->dfifo_index = 0;
             cdrom->dfifo_full = 0;
@@ -965,7 +965,7 @@
 }
 void cdrom_cmd_reads(psx_cdrom_t* cdrom) {
     cdrom->delayed_command = CDL_NONE;
-    cdrom->read_ongoing = 1;
+    cdrom->ongoing_read_command = 1;
 
     switch (cdrom->state) {
         case CD_STATE_RECV_CMD: {
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -26,6 +26,32 @@
 #define T2_PAUSED timer->timer[2].paused
 #define T2_IRQ_FIRED timer->timer[2].irq_fired
 
+// bool should_I_pause_the_timer(psx_timer_t* timer) {
+//   if ((timer->mode & 1) == 0) return false;
+//   switch ((timer->mode >> 1) & 3) {
+//     case 0: return gpu.isXblank();
+//     case 1: return false;
+//     case 2: return !gpu.isXblank();
+//     case 3: return gpu.gotXblankOnce();
+//   }
+// }
+
+// bool did_timer_reach_target(Timer timer) {
+//   if ((timer.mode & 8) == 1) return timer.value >= timer.target;
+//   return timer.value >= 0xffff;
+// }
+
+// bool should_I_reset_the_timer(Timer timer) {
+//   if (did_timer_reach_target(timer)) return true;
+//   if ((timer.mode & 1) == 0) return false;
+//   switch ((timer.mode >> 1) & 3) {
+//     case 1:
+//     case 2:
+//       return gpu.isXBlank();
+//   }
+//   return false;
+// }
+
 const char* g_psx_timer_reg_names[] = {
     "counter", 0, 0, 0,
     "mode", 0, 0, 0,
@@ -45,12 +71,16 @@
     timer->ic = ic;
 }
 
+int t1_counter = 0;
+
 uint32_t psx_timer_read32(psx_timer_t* timer, uint32_t offset) {
     int index = offset >> 4;
     int reg = offset & 0xf;
 
     switch (reg) {
-        case 0: return timer->timer[index].counter;
+        case 0: {
+            return timer->timer[index].counter;
+        } break;
         case 4: {
             timer->timer[index].mode &= 0xffffe7ff;
 
--- a/psx/disc/cue.c
+++ b/psx/disc/cue.c
@@ -444,6 +444,9 @@
 
     track = BTOI(track);
 
+    if (track > cue->num_tracks)
+        return DISC_ERR_TRACK_OUT_OF_BOUNDS;
+
     if (!msf)
         return 0;
 
@@ -450,15 +453,14 @@
     if (!track) {
         msf->m = cue->end.m;
         msf->s = cue->end.s;
+        msf->f = 0;
 
         return 0;
     }
 
-    if (track > cue->num_tracks)
-        return DISC_ERR_TRACK_OUT_OF_BOUNDS;
-    
     msf->m = cue->track[track - 1]->disc_offset.m;
     msf->s = cue->track[track - 1]->disc_offset.s;
+    msf->f = 0;
 
     return 0;
 }
--- a/psx/msf.c
+++ b/psx/msf.c
@@ -31,6 +31,24 @@
     }
 }
 
+void msf_adjust_sub(msf_t* msf) {
+    if ((int)msf->f < 0) {
+        int f = ((int)msf->f) * -1;
+        int s = (f / 60) + 1;
+
+        msf->s -= s;
+        msf->f += CD_SECTORS_PS * f;
+    }
+
+    if ((int)msf->s < 0) {
+        int s = ((int)msf->s) * -1;
+        int m = (s / 60) + 1;
+
+        msf->m -= m;
+        msf->s += 60 * m;
+    }
+}
+
 void msf_to_bcd(msf_t* msf) {
     msf->m = ITOB(msf->m);
     msf->s = ITOB(msf->s);
--- a/psx/msf.h
+++ b/psx/msf.h
@@ -89,6 +89,7 @@
 
 void msf_copy(msf_t*, msf_t);
 void msf_adjust(msf_t*);
+void msf_adjust_sub(msf_t*);
 void msf_to_bcd(msf_t*);
 void msf_from_bcd(msf_t*);
 uint32_t msf_to_address(msf_t);
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -108,7 +108,11 @@
     return aspect;
 }
 
-void psx_init(psx_t* psx, const char* bios_path) {
+void atcons_tx(void* udata, char c) {
+    putchar(c);
+}
+
+void psx_init(psx_t* psx, const char* bios_path, const char* exp_path) {
     memset(psx, 0, sizeof(psx_t));
 
     psx->bios = psx_bios_create();
@@ -115,6 +119,7 @@
     psx->ram = psx_ram_create();
     psx->dma = psx_dma_create();
     psx->exp1 = psx_exp1_create();
+    psx->exp2 = psx_exp2_create();
     psx->mc1 = psx_mc1_create();
     psx->mc2 = psx_mc2_create();
     psx->mc3 = psx_mc3_create();
@@ -135,6 +140,7 @@
     psx_bus_init_ram(psx->bus, psx->ram);
     psx_bus_init_dma(psx->bus, psx->dma);
     psx_bus_init_exp1(psx->bus, psx->exp1);
+    psx_bus_init_exp2(psx->bus, psx->exp2);
     psx_bus_init_mc1(psx->bus, psx->mc1);
     psx_bus_init_mc2(psx->bus, psx->mc2);
     psx_bus_init_mc3(psx->bus, psx->mc3);
@@ -155,7 +161,8 @@
     psx_mc3_init(psx->mc3);
     psx_ram_init(psx->ram, psx->mc2);
     psx_dma_init(psx->dma, psx->bus, psx->ic);
-    psx_exp1_init(psx->exp1, psx->mc1);
+    psx_exp1_init(psx->exp1, psx->mc1, exp_path);
+    psx_exp2_init(psx->exp2, atcons_tx, NULL);
     psx_ic_init(psx->ic, psx->cpu);
     psx_scratchpad_init(psx->scratchpad);
     psx_gpu_init(psx->gpu, psx->ic);
@@ -167,6 +174,10 @@
     psx_cpu_init(psx->cpu, psx->bus);
 }
 
+void psx_load_expansion(psx_t* psx, const char* path) {
+    psx_exp1_init(psx->exp1, psx->mc1, path);
+}
+
 void psx_hard_reset(psx_t* psx) {
     log_fatal("Hard reset not yet implemented");
 
@@ -229,6 +240,10 @@
 
 psx_exp1_t* psx_get_exp1(psx_t* psx) {
     return psx->exp1;
+}
+
+psx_exp2_t* psx_get_exp2(psx_t* psx) {
+    return psx->exp2;
 }
 
 psx_mc1_t* psx_get_mc1(psx_t* psx) {
--- a/psx/psx.h
+++ b/psx/psx.h
@@ -12,6 +12,7 @@
     psx_ram_t* ram;
     psx_dma_t* dma;
     psx_exp1_t* exp1;
+    psx_exp2_t* exp2;
     psx_mc1_t* mc1;
     psx_mc2_t* mc2;
     psx_mc3_t* mc3;
@@ -28,7 +29,8 @@
 } psx_t;
 
 psx_t* psx_create();
-void psx_init(psx_t*, const char*);
+void psx_init(psx_t*, const char*, const char*);
+void psx_load_expansion(psx_t*, const char*);
 void psx_load_bios(psx_t*, const char*);
 void psx_hard_reset(psx_t*);
 void psx_soft_reset(psx_t*);
@@ -51,6 +53,7 @@
 psx_ram_t* psx_get_ram(psx_t*);
 psx_dma_t* psx_get_dma(psx_t*);
 psx_exp1_t* psx_get_exp1(psx_t*);
+psx_exp2_t* psx_get_exp2(psx_t*);
 psx_mc1_t* psx_get_mc1(psx_t*);
 psx_mc2_t* psx_get_mc2(psx_t*);
 psx_mc3_t* psx_get_mc3(psx_t*);
--- /dev/null
+++ b/working.txt
@@ -1,0 +1,11 @@
+Atari Anniversary Edition Redux (USA)
+Harmful Park (Japan)
+Kyuiin (Japan)
+NASCAR 98 Collector''s Edition (USA)
+Panzer Bandit (Japan)
+Puzzle Bobble 4 (Japan)
+World Soccer Jikkyou Winning Eleven 3 - World Cup France '98 (Japan) (Taikenban)
+Worms (USA)
+San Francisco Rush - Extreme Racing (USA)
+Samurai Shodown III - Blades of Blood (USA)
+Time Crisis - Project Titan (USA)
\ No newline at end of file
--