shithub: psxe

Download patch

ref: e0b3a93fa7bfd40bec62acb4e74bed9f69464a0f
parent: dd3b848162f118070b1d669c7983eb83b712ad53
author: allkern <lisandroaalarcon@gmail.com>
date: Wed Jul 19 17:43:31 EDT 2023

Reimplement most CDROM commands

--- a/psx/cpu.c
+++ b/psx/cpu.c
@@ -450,8 +450,8 @@
     cpu->next_pc = cpu->pc + 4;
 }
 
-void psx_cpu_irq(psx_cpu_t* cpu, uint32_t irq) {
-    cpu->cop0_r[COP0_CAUSE] |= irq ? SR_IM2 : 0;
+void psx_cpu_set_irq_pending(psx_cpu_t* cpu) {
+    cpu->cop0_r[COP0_CAUSE] |= SR_IM2;
 }
 
 void psx_cpu_i_invalid(psx_cpu_t* cpu) {
@@ -1509,7 +1509,7 @@
 }
 
 void psx_gte_i_ncds(psx_cpu_t* cpu) {
-    log_fatal("ncds: Unimplemented GTE instruction");
+    // log_fatal("ncds: Unimplemented GTE instruction");
 }
 
 void psx_gte_i_cdp(psx_cpu_t* cpu) {
--- a/psx/cpu.h
+++ b/psx/cpu.h
@@ -185,7 +185,7 @@
 void psx_cpu_destroy(psx_cpu_t*);
 void psx_cpu_cycle(psx_cpu_t*);
 void psx_cpu_exception(psx_cpu_t*, uint32_t);
-void psx_cpu_irq(psx_cpu_t*, uint32_t);
+void psx_cpu_set_irq_pending(psx_cpu_t*);
 void psx_cpu_load_state(psx_cpu_t*, FILE*);
 void psx_cpu_save_state(psx_cpu_t*, FILE*);
 void psx_cpu_fetch(psx_cpu_t*);
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -9,14 +9,18 @@
     cdrom->rfifo[cdrom->rfifo_index++] = data; \
     SET_BITS(status, STAT_RSLRRDY_MASK, STAT_RSLRRDY_MASK);
 
-#define SEND_INT1(delay) SET_BITS(ifr, IFR_INT, IFR_INT1) cdrom->irq_delay = delay;
-#define SEND_INT2(delay) SET_BITS(ifr, IFR_INT, IFR_INT2) cdrom->irq_delay = delay;
-#define SEND_INT3(delay) SET_BITS(ifr, IFR_INT, IFR_INT3) cdrom->irq_delay = delay;
-#define SEND_INT4(delay) SET_BITS(ifr, IFR_INT, IFR_INT4) cdrom->irq_delay = delay;
-#define SEND_INT5(delay) SET_BITS(ifr, IFR_INT, IFR_INT5) cdrom->irq_delay = delay;
-#define SEND_INT6(delay) SET_BITS(ifr, IFR_INT, IFR_INT6) cdrom->irq_delay = delay;
-#define SEND_INT7(delay) SET_BITS(ifr, IFR_INT, IFR_INT7) cdrom->irq_delay = delay;
+#define SEND_INT1(delay) cdrom->int_number = IFR_INT1; cdrom->irq_delay = delay;
+#define SEND_INT2(delay) cdrom->int_number = IFR_INT2; cdrom->irq_delay = delay;
+#define SEND_INT3(delay) cdrom->int_number = IFR_INT3; cdrom->irq_delay = delay;
+#define SEND_INT4(delay) cdrom->int_number = IFR_INT4; cdrom->irq_delay = delay;
+#define SEND_INT5(delay) cdrom->int_number = IFR_INT5; cdrom->irq_delay = delay;
+#define SEND_INT6(delay) cdrom->int_number = IFR_INT6; cdrom->irq_delay = delay;
+#define SEND_INT7(delay) cdrom->int_number = IFR_INT7; cdrom->irq_delay = delay;
 
+uint8_t cdrom_btoi(uint8_t b) {
+    return ((b >> 4) * 10) + (b & 0xf);
+}
+
 static const uint8_t g_psx_cdrom_btoi_table[] = {
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
     0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
@@ -53,15 +57,107 @@
 };
 
 void cdrom_cmd_unimplemented(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_getstat(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_readn(psx_cdrom_t* cdrom) {}
+void cdrom_cmd_getstat(psx_cdrom_t* cdrom) {
+    if (!cdrom->tray_open) {
+        SET_BITS(stat, GETSTAT_TRAYOPEN, 0);
+    }
+
+    if (!cdrom->disc) {
+        SET_BITS(stat, GETSTAT_TRAYOPEN, 0xff);
+    }
+
+    RESP_PUSH(cdrom->stat);
+    SEND_INT3(DELAY_1MS);
+
+    cdrom->delayed_response_command = 0;
+}
+void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {
+    if (cdrom->pfifo_index != 3) {
+        log_fatal("setloc: Expected exactly 3 parameters, got %u instead", cdrom->pfifo_index);
+
+        return;
+    }
+
+    cdrom->seek_sect = g_psx_cdrom_btoi_table[cdrom->pfifo[--cdrom->pfifo_index]];
+    cdrom->seek_ss = g_psx_cdrom_btoi_table[cdrom->pfifo[--cdrom->pfifo_index]];
+    cdrom->seek_mm = g_psx_cdrom_btoi_table[cdrom->pfifo[--cdrom->pfifo_index]];
+
+    // Account for 2 second gap
+    uint32_t seconds = (cdrom->seek_mm * 60) + cdrom->seek_ss - 2;
+    uint32_t sectors = (seconds * CD_SECTORS_PER_SECOND) + cdrom->seek_sect;
+
+    cdrom->seek_offset = sectors * CD_SECTOR_SIZE;
+
+    log_fatal("setloc: %02u:%02u:%02u (%08x)",
+        cdrom->seek_mm,
+        cdrom->seek_ss,
+        cdrom->seek_sect,
+        cdrom->seek_offset
+    );
+
+    RESP_PUSH(cdrom->stat);
+    SEND_INT3(DELAY_1MS);
+}
+void cdrom_cmd_readn(psx_cdrom_t* cdrom) {
+    if (!cdrom->delayed_response_command) {
+        RESP_PUSH(cdrom->stat);
+        SEND_INT3(DELAY_1MS);
+
+        cdrom->stat |= GETSTAT_READ;
+
+        log_fatal("Reading data from disc. offset=%02x:%02x:%02x (%08x)",
+            cdrom->seek_mm, cdrom->seek_ss, cdrom->seek_sect,
+            cdrom->seek_offset
+        );
+
+        fread(cdrom->read_buf, 1, CD_SECTOR_SIZE, cdrom->disc);
+
+        cdrom->delayed_response_command = 0x06;
+    } else {
+        //fseek(cdrom->disc, cdrom->seek_offset, 0);
+
+        // for (int y = 0; y < 8; y++) {
+        //     for (int x = 0; x < 0x10; x++) {
+        //         printf("%02x ", cdrom->read_buf[x + (y * 0x10)]);
+        //     }
+
+        //     printf("\n");
+        // }
+
+        // exit(1);
+
+        int double_speed = cdrom->mode & MODE_SPEED;
+
+        RESP_PUSH(cdrom->stat);
+        SEND_INT1(double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY);
+    
+        SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
+
+        cdrom->dfifo_full = 1;
+        cdrom->stat &= ~GETSTAT_READ;
+
+        // Repeat until pause
+        cdrom->delayed_response_command = 0x06;
+    }
+}
 void cdrom_cmd_stop(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_pause(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_init(psx_cdrom_t* cdrom) {
-    log_fatal("CdlInit");
+void cdrom_cmd_pause(psx_cdrom_t* cdrom) {
+    if (!cdrom->delayed_response_command) {
+        RESP_PUSH(cdrom->stat);
+        SEND_INT3(DELAY_1MS);
 
+        cdrom->delayed_response_command = 0x09;
+    } else {
+        RESP_PUSH(cdrom->stat);
+        SEND_INT2(DELAY_1MS);
+
+        cdrom->delayed_response_command = 0;
+    }
+}
+void cdrom_cmd_init(psx_cdrom_t* cdrom) {
     if (!cdrom->delayed_response_command) {
+        cdrom->stat = 0x20;
+
         SEND_INT3(DELAY_1MS);
         RESP_PUSH(cdrom->stat);
 
@@ -70,26 +166,135 @@
         SEND_INT2(DELAY_1MS);
         RESP_PUSH(cdrom->stat);
 
-        cdrom->delayed_response_command = 0x00;
+        cdrom->delayed_response_command = 0;
     }
 }
 
 void cdrom_cmd_unmute(psx_cdrom_t* cdrom) {}
 void cdrom_cmd_setfilter(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_setmode(psx_cdrom_t* cdrom) {}
+void cdrom_cmd_setmode(psx_cdrom_t* cdrom) {
+    if (cdrom->pfifo_index != 1) {
+        log_fatal("CdlSetmode: Expected exactly 1 parameter");
+
+        return;
+    }
+
+    cdrom->mode = cdrom->pfifo[--cdrom->pfifo_index];
+
+    RESP_PUSH(cdrom->stat);
+    SEND_INT3(DELAY_1MS);
+
+    cdrom->delayed_response_command = 0;
+}
 void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) {}
 void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) {}
 void cdrom_cmd_gettn(psx_cdrom_t* cdrom) {}
 void cdrom_cmd_gettd(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_seekl(psx_cdrom_t* cdrom) {}
+void cdrom_cmd_seekl(psx_cdrom_t* cdrom) {
+    if (!cdrom->delayed_response_command) {
+        RESP_PUSH(cdrom->stat);
+        SEND_INT3(DELAY_1MS);
+
+        log_fatal("seekl: Seeking to address %08x", cdrom->seek_offset);
+
+        fseek(cdrom->disc, cdrom->seek_offset, 0);
+
+        cdrom->delayed_response_command = 0x15;
+    } else {
+        RESP_PUSH(cdrom->stat);
+        SEND_INT2(DELAY_1MS);
+
+        cdrom->delayed_response_command = 0;
+    }
+}
 void cdrom_cmd_seekp(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_test(psx_cdrom_t* cdrom) {}
-void cdrom_cmd_getid(psx_cdrom_t* cdrom) {}
+void cdrom_cmd_test(psx_cdrom_t* cdrom) {
+    if (cdrom->pfifo_index != 1) {
+        log_fatal("test: Expected exactly 1 parameter");
+    }
+
+    switch (cdrom->pfifo[--cdrom->pfifo_index]) {
+        case 0x20: {
+            RESP_PUSH(0x01);
+            RESP_PUSH(0x95);
+            RESP_PUSH(0x13);
+            RESP_PUSH(0x03);
+            SEND_INT3(DELAY_1MS);
+        } break;
+    }
+
+    cdrom->delayed_response_command = 0;
+}
+void cdrom_cmd_getid(psx_cdrom_t* cdrom) {
+    if (!cdrom->delayed_response_command) {
+        RESP_PUSH(cdrom->stat);
+        SEND_INT3(DELAY_1MS);
+
+        cdrom->delayed_response_command = 0x1a;
+    } else {
+        if (!cdrom->disc) {
+            RESP_PUSH(0x00);
+            RESP_PUSH(0x00);
+            RESP_PUSH(0x00);
+            RESP_PUSH(0x00);
+            RESP_PUSH(0x00);
+            RESP_PUSH(0x00);
+            RESP_PUSH(0x40);
+        } else {
+            RESP_PUSH('A');
+            RESP_PUSH('A');
+            RESP_PUSH('A');
+            RESP_PUSH('A');
+            RESP_PUSH('A');
+            RESP_PUSH('A');
+            RESP_PUSH('A');
+        }
+
+        SEND_INT2(DELAY_1MS);
+
+        cdrom->delayed_response_command = 0;
+    }
+}
 void cdrom_cmd_reads(psx_cdrom_t* cdrom) {}
 void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) {}
 
 typedef void (*cdrom_cmd_t)(psx_cdrom_t*);
 
+const char* g_psx_cdrom_command_names[] = {
+    "CdlUnimplemented",
+    "CdlGetstat",
+    "CdlSetloc",
+    "CdlUnimplemented",
+    "CdlUnimplemented",
+    "CdlUnimplemented",
+    "CdlReadn",
+    "CdlUnimplemented",
+    "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,
@@ -148,7 +353,7 @@
 
 uint8_t cdrom_read_dfifo(psx_cdrom_t* cdrom) {
     if (!cdrom->dfifo_full)
-        return 0x00;
+        return 0;
 
     int sector_size_bit = cdrom->mode & MODE_SECTOR_SIZE;
 
@@ -158,6 +363,8 @@
     if (cdrom->dfifo_index != read_sector_size) {
         return cdrom->dfifo[offset + (cdrom->dfifo_index++)];
     } else {
+        SET_BITS(status, STAT_DRQSTS_MASK, 0);
+
         cdrom->dfifo_full = 0;
     }
 
@@ -177,18 +384,19 @@
 }
 
 void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t value) {
-    cdrom->delayed_response_command = 0;
-    cdrom->command = value;
-
-    log_fatal("    Command %02x (pfifo=%02x, %02x, %02x, %02x), pfifo_index=%u",
+    log_fatal("%s(%02x) params=[%02x, %02x, %02x, %02x, %02x, %02x]",
+        g_psx_cdrom_command_names[value],
         value,
         cdrom->pfifo[0],
         cdrom->pfifo[1],
         cdrom->pfifo[2],
         cdrom->pfifo[3],
-        cdrom->pfifo_index
+        cdrom->pfifo[4],
+        cdrom->pfifo[5]
     );
 
+    cdrom->delayed_response_command = 0;
+
     g_psx_cdrom_command_table[value](cdrom);
 }
 
@@ -224,6 +432,7 @@
 void cdrom_write_ifr(psx_cdrom_t* cdrom, uint8_t value) {
     cdrom->ifr &= ~(value & 0x7);
 
+    // Clear Parameter FIFO
     if (value & 0x40) {
         cdrom->pfifo_index = 0;
 
@@ -233,6 +442,13 @@
             (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK)
         );
     }
+
+    // Clear Response FIFO
+    // if (value & 0x7) {
+    //     cdrom->rfifo_index = 0;
+
+    //     SET_BITS(status, STAT_RSLRRDY_MASK, 0);
+    // }
 }
 
 void cdrom_write_sminfo(psx_cdrom_t* cdrom, uint8_t value) {
@@ -314,9 +530,11 @@
 }
 
 uint8_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t offset) {
-    log_fatal("CDROM read -> %s", g_psx_cdrom_read_names_table[(STAT_INDEX << 2) | offset]);
+    uint8_t data = g_psx_cdrom_read_table[(STAT_INDEX << 2) | offset](cdrom);
 
-    return 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) {
@@ -328,7 +546,7 @@
 }
 
 void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t offset, uint8_t value) {
-    log_fatal("CDROM write -> %s (%02x)", g_psx_cdrom_write_names_table[(STAT_INDEX << 2) | offset], 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);
 }
@@ -338,6 +556,13 @@
         cdrom->irq_delay -= 2;
 
         if (cdrom->irq_delay <= 0) {
+            if (cdrom->int_number) {
+                SET_BITS(ifr, IFR_INT, cdrom->int_number);
+
+                cdrom->int_number = 0;
+            }
+
+            log_fatal("CDROM INT%u", cdrom->ifr & 0x7);
             psx_ic_irq(cdrom->ic, IC_CDROM);
 
             cdrom->irq_delay = 0;
--- a/psx/dev/cdrom.h
+++ b/psx/dev/cdrom.h
@@ -117,6 +117,7 @@
     int irq_delay;
     uint8_t command;
     uint8_t delayed_response_command;
+    uint8_t int_number;
     int delayed_response;
 
     const char* path;
--- a/psx/dev/ic.c
+++ b/psx/dev/ic.c
@@ -89,7 +89,8 @@
 void psx_ic_irq(psx_ic_t* ic, int id) {
     ic->stat |= id;
 
-    psx_cpu_irq(ic->cpu, ic->mask & ic->stat);
+    if (ic->mask & ic->stat)
+        psx_cpu_set_irq_pending(ic->cpu);
 }
 
 void psx_ic_destroy(psx_ic_t* ic) {
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -49,7 +49,7 @@
     int index = offset >> 4;
     int reg = offset & 0xf;
 
-    log_fatal("Timer %u %s read32", index, g_psx_timer_reg_names[reg]);
+    //log_fatal("Timer %u %s read32", index, g_psx_timer_reg_names[reg]);
 
     switch (reg) {
         case 0: return timer->timer[index].counter;
@@ -70,7 +70,7 @@
     int index = offset >> 4;
     int reg = offset & 0xf;
 
-    log_fatal("Timer %u %s read16", index, g_psx_timer_reg_names[reg]);
+    //log_fatal("Timer %u %s read16 %04x", index, g_psx_timer_reg_names[reg], timer->timer[index].counter);
 
     switch (reg) {
         case 0: return timer->timer[index].counter;
@@ -97,12 +97,11 @@
     int index = offset >> 4;
     int reg = offset & 0xf;
 
-    log_fatal("Timer %u %s write32 %08x", index, g_psx_timer_reg_names[reg], value);
+    //log_fatal("Timer %u %s write32 %08x", index, g_psx_timer_reg_names[reg], value);
 
     switch (reg) {
         case 0: {
             timer->timer[index].counter = value;
-            timer->f_counter[index] = value;
         } return;
         case 4: {
             timer->timer[index].mode = value;
@@ -124,7 +123,6 @@
     switch (reg) {
         case 0: {
             timer->timer[index].counter = value;
-            timer->f_counter[index] = value;
         } return;
         case 4: {
             timer->timer[index].mode = value;
@@ -131,7 +129,9 @@
             timer->timer[index].mode |= 0x400;
             timer->timer[index].irq_fired = 0;
         } return;
-        case 8: timer->timer[index].target = value; return;
+        case 8: {
+            timer->timer[index].target = value;
+        } return;
     }
 
     log_fatal("Unhandled 16-bit TIMER write at offset %08x (%02x)", offset, value);
@@ -142,7 +142,8 @@
 }
 
 void timer_update_timer0(psx_timer_t* timer, int cyc) {
-    T0_PREV = T0_COUNTER;
+    int reached_target = ((uint32_t)T0_COUNTER + cyc) >= T0_TARGET;
+    int reached_max = ((uint32_t)T0_COUNTER + cyc) >= 0xffff;
 
     // Dotclock unsupported
     // if (T0_MODE & 0x100)
@@ -152,9 +153,6 @@
 
     int can_fire_irq = (T0_MODE & MODE_IRQRMD) || !T0_IRQ_FIRED;
 
-    int reached_target = (T0_PREV <= T0_TARGET) && (T0_COUNTER >= T0_TARGET);
-    int reached_max = (T0_PREV <= 0xffff) && (T0_COUNTER >= 0xffff);
-
     int target_irq = reached_target && (T0_MODE & MODE_TGTIRQ);
     int max_irq = reached_max && (T0_MODE & MODE_MAXIRQ);
 
@@ -185,17 +183,21 @@
 }
 
 void timer_update_timer1(psx_timer_t* timer, int cyc) {
-    T1_PREV = T1_COUNTER;
+    int reached_target, reached_max;
 
-    // Hblank increment is done in handler
-    if ((!T1_PAUSED) && !(T1_MODE & 0x100))
-        T1_COUNTER += cyc;
+    if (T1_MODE & 0x100) {
+        reached_target = T1_COUNTER == T1_TARGET;
+        reached_max = T1_COUNTER == 0xffff;
+    } else {
+        reached_target = ((uint32_t)T1_COUNTER + cyc) >= T1_TARGET;
+        reached_max = ((uint32_t)T1_COUNTER + cyc) >= 0xffff;
 
+        if (!T1_PAUSED)
+            T1_COUNTER += cyc;
+    }
+    
     int can_fire_irq = (T1_MODE & MODE_IRQRMD) || !T1_IRQ_FIRED;
 
-    int reached_target = (T1_PREV <= T1_TARGET) && (T1_COUNTER >= T1_TARGET);
-    int reached_max = (T1_PREV <= 0xffff) && (T1_COUNTER >= 0xffff);
-
     int target_irq = reached_target && (T1_MODE & MODE_TGTIRQ);
     int max_irq = reached_max && (T1_MODE & MODE_MAXIRQ);
 
@@ -214,14 +216,12 @@
 
         timer->timer[0].irq_fired = 1;
 
-        psx_ic_irq(timer->ic, IC_TIMER0);
+        psx_ic_irq(timer->ic, IC_TIMER1);
     }
 
     if (T1_MODE & MODE_RESETC) {
         if (reached_target)
             T1_COUNTER -= T1_TARGET;
-    } else {
-        T1_COUNTER -= 0xffff;
     }
 }
 
--- a/psx/dev/timer.h
+++ b/psx/dev/timer.h
@@ -63,15 +63,13 @@
     psx_ic_t* ic;
 
     struct {
-        int32_t prev_counter;
-        int32_t counter;
+        uint16_t prev_counter;
+        uint16_t counter;
         uint32_t mode;
         uint32_t target;
         int paused;
         int irq_fired;
     } timer[3];
-
-    float f_counter[3];
 } psx_timer_t;
 
 psx_timer_t* psx_timer_create();
--