ref: 594aa466b7057e4a3b7ff95836169106161a4787
parent: 7f9fc68a99d1056963e74a37e921d449a3c99cca
author: allkern <lisandroaalarcon@gmail.com>
date: Sun Jun 2 20:35:56 EDT 2024
Many fixes and improvements Fixed CD-XA popping issue Improved SPU IRQ emulation Fixed GPU signed draw offsets Improved CDROM timings (better compatibility)
--- a/build-deps.ps1
+++ b/build-deps.ps1
@@ -5,4 +5,6 @@
$SDL2_URL = "https://github.com/libsdl-org/SDL/releases/download/release-2.30.3/SDL2-devel-2.30.3-mingw.zip"
Invoke-WebRequest -URI $SDL2_URL -OutFile "sdl2.zip"
-Expand-Archive "sdl2.zip" -DestinationPath "." -Force
\ No newline at end of file
+Expand-Archive "sdl2.zip" -DestinationPath "." -Force
+
+Remove-Item "sdl2.zip"
\ No newline at end of file
--- a/psx/cpu.c
+++ b/psx/cpu.c
@@ -1477,9 +1477,9 @@
if (i == 3)
cpu->s_mac3 = value;
- if (value < -0x80000000000) {
+ if (value < -0x80000000000ll) {
R_FLAG |= 0x8000000 >> (i - 1);
- } else if (value > 0x7ffffffffff) {
+ } else if (value > 0x7ffffffffffll) {
R_FLAG |= 0x40000000 >> (i - 1);
}
@@ -1486,6 +1486,14 @@
return (int32_t)(((value << 20) >> 20) >> cpu->gte_sf);
}
+void gte_check_mac(psx_cpu_t* cpu, int i, int64_t value) {
+ if (value < -0x80000000000ll) {
+ R_FLAG |= 0x8000000 >> (i - 1);
+ } else if (value > 0x7ffffffffffll) {
+ R_FLAG |= 0x40000000 >> (i - 1);
+ }
+}
+
int32_t gte_clamp_ir0(psx_cpu_t* cpu, int32_t value) {
if (value < 0) {
R_FLAG |= 0x1000;
@@ -1841,9 +1849,21 @@
R_IR1 = gte_clamp_ir(cpu, 1, R_MAC1, cpu->gte_lm); \
R_IR2 = gte_clamp_ir(cpu, 2, R_MAC2, cpu->gte_lm); \
R_IR3 = gte_clamp_ir(cpu, 3, R_MAC3, cpu->gte_lm); \
- R_MAC1 = gte_clamp_mac(cpu, 1, (I64(R_RBK) << 12) + (I64(R_LR1) * I64(R_IR1)) + (I64(R_LR2) * I64(R_IR2)) + (I64(R_LR3) * I64(R_IR3))); \
- R_MAC2 = gte_clamp_mac(cpu, 2, (I64(R_GBK) << 12) + (I64(R_LG1) * I64(R_IR1)) + (I64(R_LG2) * I64(R_IR2)) + (I64(R_LG3) * I64(R_IR3))); \
- R_MAC3 = gte_clamp_mac(cpu, 3, (I64(R_BBK) << 12) + (I64(R_LB1) * I64(R_IR1)) + (I64(R_LB2) * I64(R_IR2)) + (I64(R_LB3) * I64(R_IR3))); \
+ gte_check_mac(cpu, 1, ((I64(R_RBK) << 12) + (I64(R_LR1) * I64(R_IR1)))); \
+ gte_check_mac(cpu, 2, ((I64(R_GBK) << 12) + (I64(R_LG1) * I64(R_IR1)))); \
+ gte_check_mac(cpu, 3, ((I64(R_BBK) << 12) + (I64(R_LB1) * I64(R_IR1)))); \
+ gte_check_mac(cpu, 1, I64(R_LR2) * I64(R_IR2)); \
+ gte_check_mac(cpu, 2, I64(R_LG2) * I64(R_IR2)); \
+ gte_check_mac(cpu, 3, I64(R_LB2) * I64(R_IR2)); \
+ gte_check_mac(cpu, 1, I64(R_LR3) * I64(R_IR3)); \
+ gte_check_mac(cpu, 2, I64(R_LG3) * I64(R_IR3)); \
+ gte_check_mac(cpu, 3, I64(R_LB3) * I64(R_IR3)); \
+ R_MAC1 = gte_clamp_mac(cpu, 1, ((I64(R_RBK) << 12) + (I64(R_LR1) * I64(R_IR1))) + (I64(R_LR2) * I64(R_IR2)) + (I64(R_LR3) * I64(R_IR3))); \
+ R_MAC2 = gte_clamp_mac(cpu, 2, ((I64(R_GBK) << 12) + (I64(R_LG1) * I64(R_IR1))) + (I64(R_LG2) * I64(R_IR2)) + (I64(R_LG3) * I64(R_IR3))); \
+ R_MAC3 = gte_clamp_mac(cpu, 3, ((I64(R_BBK) << 12) + (I64(R_LB1) * I64(R_IR1))) + (I64(R_LB2) * I64(R_IR2)) + (I64(R_LB3) * I64(R_IR3))); \
+ /* R_MAC1 = gte_clamp_mac(cpu, 1, () + () + ()); */ \
+ /* R_MAC2 = gte_clamp_mac(cpu, 2, () + () + ()); */ \
+ /* R_MAC3 = gte_clamp_mac(cpu, 3, () + () + ()); */ \
R_IR1 = gte_clamp_ir(cpu, 1, R_MAC1, cpu->gte_lm); \
R_IR2 = gte_clamp_ir(cpu, 2, R_MAC2, cpu->gte_lm); \
R_IR3 = gte_clamp_ir(cpu, 3, R_MAC3, cpu->gte_lm); \
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -253,7 +253,7 @@
return;
}
- cdrom->irq_delay = DELAY_1MS;
+ cdrom->irq_delay = DELAY_1MS * 2;
cdrom->state = CD_STATE_SEND_RESP1;
cdrom->delayed_command = CDL_GETSTAT;
} break;
@@ -262,7 +262,7 @@
SET_BITS(ifr, IFR_INT, IFR_INT3);
RESP_PUSH(
GETSTAT_MOTOR |
- ((cdrom->cdda_playing || cdrom->xa_playing) ? GETSTAT_PLAY : 0) |
+ (cdrom->cdda_playing ? GETSTAT_PLAY : 0) |
(cdrom->ongoing_read_command ? GETSTAT_READ : 0) |
(cdrom->disc ? 0 : GETSTAT_TRAYOPEN)
);
@@ -271,7 +271,7 @@
// printf("getstat 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;
+ cdrom->irq_delay = DELAY_1MS * 2;
} else {
cdrom->delayed_command = CDL_NONE;
cdrom->state = CD_STATE_RECV_CMD;
@@ -379,15 +379,15 @@
case CD_STATE_RECV_CMD: {
int track = 0;
- if (cdrom->cdda_playing) {
- cdrom->pfifo_index = 0;
+ // if (cdrom->cdda_playing) {
+ // cdrom->pfifo_index = 0;
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_PLAY;
+ // cdrom->irq_delay = DELAY_1MS;
+ // cdrom->state = CD_STATE_SEND_RESP1;
+ // cdrom->delayed_command = CDL_PLAY;
- return;
- }
+ // return;
+ // }
// Optional track number parameter
if (cdrom->pfifo_index)
@@ -461,6 +461,7 @@
if (cdrom->mode & MODE_XA_ADPCM) {
cdrom->xa_msf = cdrom->seek_msf;
+ cdrom->xa_current_msf = cdrom->xa_msf;
cdrom->xa_playing = 1;
cdrom->xa_remaining_samples = 0;
@@ -883,7 +884,7 @@
void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) {
switch (cdrom->state) {
case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS;
+ cdrom->irq_delay = DELAY_1MS * 4;
cdrom->delayed_command = CDL_GETLOCL;
cdrom->state = CD_STATE_SEND_RESP1;
} break;
@@ -903,7 +904,7 @@
// 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;
+ cdrom->irq_delay = DELAY_1MS * 4;
} else {
cdrom->delayed_command = CDL_NONE;
cdrom->state = CD_STATE_RECV_CMD;
@@ -914,7 +915,7 @@
void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) {
switch (cdrom->state) {
case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS;
+ cdrom->irq_delay = DELAY_1MS * 8;
cdrom->delayed_command = CDL_GETLOCP;
cdrom->state = CD_STATE_SEND_RESP1;
} break;
@@ -968,7 +969,7 @@
printf("getlocp 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;
+ cdrom->irq_delay = DELAY_1MS * 8;
} else {
cdrom->delayed_command = CDL_NONE;
cdrom->state = CD_STATE_RECV_CMD;
@@ -1234,10 +1235,10 @@
// 95h,05h,16h,C1h
SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(0xc1);
- RESP_PUSH(0x16);
- RESP_PUSH(0x05);
- RESP_PUSH(0x95);
+ RESP_PUSH(0xc0);
+ RESP_PUSH(0x19);
+ RESP_PUSH(0x09);
+ RESP_PUSH(0x94);
cdrom->state = CD_STATE_RECV_CMD;
} break;
@@ -1327,6 +1328,7 @@
if (cdrom->mode & MODE_XA_ADPCM) {
cdrom->xa_msf = cdrom->seek_msf;
+ cdrom->xa_current_msf = cdrom->xa_msf;
cdrom->xa_playing = 1;
cdrom->xa_remaining_samples = 0;
@@ -2086,8 +2088,6 @@
int16_t left[28];
int16_t right[28];
- int16_t left_h[2] = { 0, 0 };
- int16_t right_h[2] = { 0, 0 };
int16_t* left_ptr = cdrom->xa_left_buf;
int16_t* right_ptr = cdrom->xa_right_buf;
@@ -2096,8 +2096,8 @@
for (int i = 0; i < 18; i++) {
for (int blk = 0; blk < 4; blk++) {
if (cdrom->xa_sector_buf[0x13] & 1) {
- cdrom_decode_xa_block(cdrom, src, blk, 0, left, left_h);
- cdrom_decode_xa_block(cdrom, src, blk, 1, right, right_h);
+ cdrom_decode_xa_block(cdrom, src, blk, 0, left, cdrom->xa_left_h);
+ cdrom_decode_xa_block(cdrom, src, blk, 1, right, cdrom->xa_right_h);
for (int i = 0; i < 28; i++) {
*left_ptr++ = left[i];
@@ -2104,12 +2104,12 @@
*right_ptr++ = right[i];
}
} else {
- cdrom_decode_xa_block(cdrom, src, blk, 0, left, left_h);
+ cdrom_decode_xa_block(cdrom, src, blk, 0, left, cdrom->xa_left_h);
for (int i = 0; i < 28; i++)
*mono_ptr++ = left[i];
- cdrom_decode_xa_block(cdrom, src, blk, 1, left, left_h);
+ cdrom_decode_xa_block(cdrom, src, blk, 1, left, cdrom->xa_left_h);
for (int i = 0; i < 28; i++)
*mono_ptr++ = left[i];
@@ -2124,6 +2124,7 @@
while (true) {
if (psx_disc_seek(cdrom->disc, cdrom->xa_msf)) {
cdrom->xa_playing = 0;
+ cdrom->xa_remaining_samples = 0;
return;
}
@@ -2132,12 +2133,12 @@
msf_add_f(&cdrom->xa_msf, 1);
- // Check for EOR, EOF bits
- if (cdrom->xa_sector_buf[0x12] & 0x80)
+ // Check for EOR bit
+ if (cdrom->xa_sector_buf[0x12] & 1)
return;
- // Check RT and Audio bit
- if ((cdrom->xa_sector_buf[0x12] & 4) != 4)
+ // Check Audio bit
+ if (!(cdrom->xa_sector_buf[0x12] & 4))
continue;
// If we get here it means this is a real-time audio sector.
@@ -2185,14 +2186,18 @@
if (!cdrom->xa_remaining_samples) {
cdrom_fetch_xa_sector(cdrom);
- if (cdrom->xa_sector_buf[0x12] & 0x80) {
+ if (cdrom->xa_sector_buf[0x12] & 0x01) {
SET_BITS(status, STAT_ADPBUSY_MASK, 0);
+ printf("Pausing XA-ADPCM playback\n");
+
cdrom->xa_playing = 0;
cdrom->xa_remaining_samples = 0;
return;
}
+
+ msf_add_f(&cdrom->xa_current_msf, 1);
stereo = (cdrom->xa_sector_buf[0x13] & 1) == 1;
--- a/psx/dev/cdrom.h
+++ b/psx/dev/cdrom.h
@@ -11,10 +11,10 @@
#include "../msf.h"
#include "spu.h"
-// #define DELAY_1MS (0xc4e1)
+#define DELAY_1MS (0xc4e1)
// #define READ_SINGLE_DELAY (0x6e1cd)
// #define READ_DOUBLE_DELAY (0x36cd2)
-#define DELAY_1MS (PSX_CPU_CPS / 1000)
+// #define DELAY_1MS (PSX_CPU_CPS / 1000)
#define READ_SINGLE_DELAY (PSX_CPU_CPS / 75)
#define READ_DOUBLE_DELAY (PSX_CPU_CPS / (2 * 75))
@@ -210,11 +210,14 @@
// XA-ADPCM
uint8_t* xa_sector_buf;
msf_t xa_msf;
+ msf_t xa_current_msf;
int xa_playing;
int xa_mute;
uint8_t xa_file;
uint8_t xa_channel;
uint8_t xa_coding;
+ int16_t xa_left_h[2];
+ int16_t xa_right_h[2];
int16_t* xa_left_buf;
int16_t* xa_right_buf;
int16_t* xa_mono_buf;
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -345,14 +345,10 @@
uint32_t size = BCR_SIZE(cdrom);
- if (!size) {
+ if (!size)
printf("0 sized CDROM DMA\n");
- return;
- exit(1);
- }
-
- dma->cdrom_irq_delay = size * 24;
+ dma->cdrom_irq_delay = 1;
if (!CHCR_TDIR(cdrom)) {
for (int i = 0; i < size; i++) {
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -825,6 +825,8 @@
}
}
+#define I32(v, b) (((int32_t)((v) << (31-b))) >> (31-b))
+
void gpu_rect(psx_gpu_t* gpu) {
switch (gpu->state) {
case GPU_STATE_RECV_CMD: {
@@ -1516,8 +1518,8 @@
// gpu->ysiz
// );
- for (uint32_t y = gpu->v0.y; y < (gpu->v0.y + gpu->ysiz); y++) {
- for (uint32_t x = gpu->v0.x; x < (gpu->v0.x + gpu->xsiz); x++) {
+ for (int y = gpu->v0.y; y < (gpu->v0.y + gpu->ysiz); y++) {
+ for (int x = gpu->v0.x; x < (gpu->v0.x + gpu->xsiz); x++) {
// This shouldn't be needed
int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
(y >= gpu->draw_y1) && (y <= gpu->draw_y2);
@@ -1645,8 +1647,8 @@
gpu->draw_y2 = (gpu->buf[0] >> 10) & 0x1ff;
} break;
case 0xe5: {
- gpu->off_x = (gpu->buf[0] >> 0 ) & 0x7ff;
- gpu->off_y = (gpu->buf[0] >> 11) & 0x7ff;
+ gpu->off_x = ((int32_t)((gpu->buf[0] >> 0 ) & 0x7ff) << 21) >> 21;
+ gpu->off_y = ((int32_t)((gpu->buf[0] >> 11) & 0x7ff) << 21) >> 21;
} break;
case 0xe6: {
/* To-do: Implement mask bit thing */
@@ -1791,8 +1793,9 @@
// Player Select. It probably uses T2 IRQs to time
// GetlocP commands, if the timer is too slow it will
// break.
- if (!(gpu->line & 7))
- psx_ic_irq(gpu->ic, IC_TIMER2);
+ // if (!(gpu->line & 7))
+ // psx_ic_irq(gpu->ic, IC_TIMER2);
+ // psx_ic_irq(gpu->ic, IC_SPU);
} else {
gpu->gpustat &= ~(1 << 31);
}
@@ -1807,8 +1810,6 @@
} else if (gpu->line == GPU_SCANS_PER_FRAME_NTSC) {
if (gpu->event_cb_table[GPU_EVENT_VBLANK_END])
gpu->event_cb_table[GPU_EVENT_VBLANK_END](gpu);
-
- psx_ic_irq(gpu->ic, IC_SPU);
gpu->line = 0;
}
--- a/psx/dev/gpu.h
+++ b/psx/dev/gpu.h
@@ -130,7 +130,7 @@
uint32_t draw_x2, draw_y2;
// Drawing offset
- uint32_t off_x, off_y;
+ int32_t off_x, off_y;
// Texture Window
uint32_t texw_mx, texw_my;
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -89,12 +89,13 @@
return (psx_spu_t*)malloc(sizeof(psx_spu_t));
}
-void psx_spu_init(psx_spu_t* spu) {
+void psx_spu_init(psx_spu_t* spu, psx_ic_t* ic) {
memset(spu, 0, sizeof(psx_spu_t));
spu->io_base = PSX_SPU_BEGIN;
spu->io_size = PSX_SPU_SIZE;
+ spu->ic = ic;
spu->ram = (uint8_t*)malloc(SPU_RAM_SIZE);
memset(spu->ram, 0, SPU_RAM_SIZE);
@@ -141,6 +142,9 @@
int32_t f0 = g_spu_pos_adpcm_table[filter];
int32_t f1 = g_spu_neg_adpcm_table[filter];
+ if ((spu->irq9addr << 3) == addr)
+ psx_ic_irq(spu->ic, IC_SPU);
+
for (int j = 0; j < 28; j++) {
uint16_t n = (spu->ram[addr + 2 + (j >> 1)] >> ((j & 1) * 4)) & 0xf;
@@ -356,6 +360,10 @@
spu_kon(spu, value << (16 * high));
} return 1;
+
+ // case SPUR_SPUIRQA: {
+ // spu->irq9addr = value << 3;
+ // } return 1;
case SPUR_KOFFL: case SPUR_KOFFH: {
int high = (offset & 2) != 0;
--- a/psx/dev/spu.h
+++ b/psx/dev/spu.h
@@ -3,6 +3,8 @@
#include <stdint.h>
+#include "ic.h"
+
#define PSX_SPU_BEGIN 0x1f801c00
#define PSX_SPU_SIZE 0x400
#define PSX_SPU_END 0x1f801fff
@@ -32,11 +34,13 @@
#define SPUR_TCTRL 0x1ac
#define SPUR_SPUSTAT 0x1ae
#define SPUR_MBASE 0x1a2
+#define SPUR_SPUIRQA 0x1a4
typedef struct __attribute__((__packed__)) {
uint32_t bus_delay;
uint32_t io_base, io_size;
+ psx_ic_t* ic;
uint8_t* ram;
struct __attribute__((__packed__)) {
@@ -166,7 +170,7 @@
} psx_spu_t;
psx_spu_t* psx_spu_create(void);
-void psx_spu_init(psx_spu_t*);
+void psx_spu_init(psx_spu_t*, psx_ic_t*);
uint32_t psx_spu_read32(psx_spu_t*, uint32_t);
uint16_t psx_spu_read16(psx_spu_t*, uint32_t);
uint8_t psx_spu_read8(psx_spu_t*, uint32_t);
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -91,8 +91,32 @@
timer->timer[index].blank_once = 0;
timer->timer[index].paused = 0;
- if (index == 2)
- printf("timer_set_mode %u %04x\n", index, value);
+ // printf(
+ // "timer_set_mode %u %04x\n"
+ // "sync_enable %u\n"
+ // "sync_mode %u\n"
+ // "reset_target %u\n"
+ // "irq_target %u\n"
+ // "irq_max %u\n"
+ // "irq_repeat %u\n"
+ // "irq_toggle %u\n"
+ // "clk_source %u\n"
+ // "target_reached %u\n"
+ // "max_reached %u\n"
+ // "target %04x\n",
+ // index, value,
+ // timer->timer[index].sync_enable,
+ // timer->timer[index].sync_mode,
+ // timer->timer[index].reset_target,
+ // timer->timer[index].irq_target,
+ // timer->timer[index].irq_max,
+ // timer->timer[index].irq_repeat,
+ // timer->timer[index].irq_toggle,
+ // timer->timer[index].clk_source,
+ // timer->timer[index].target_reached,
+ // timer->timer[index].max_reached,
+ // timer->timer[index].target
+ // );
switch (index) {
case 0: {
@@ -209,8 +233,6 @@
void timer_handle_irq(psx_timer_t* timer, int i) {
int irq = 0;
- int prev_target_reached = timer->timer[i].target_reached;
- int prev_max_reached = timer->timer[i].max_reached;
int target_reached = timer->timer[i].counter > timer->timer[i].target;
int max_reached = timer->timer[i].counter > 65535.0f;
@@ -217,12 +239,13 @@
if (target_reached) {
timer->timer[i].target_reached = 1;
- if (timer->timer[i].reset_target) {
+ // if ((i == 2) && (T2_CLKSRC == 2))
+ // printf("target %04x (%f) reached\n", timer->timer[i].target, timer->timer[i].counter);
+
+ if (timer->timer[i].reset_target)
timer->timer[i].counter = 0;
- timer->timer[i].div_counter = 0;
- }
- if (timer->timer[i].irq_target && !prev_target_reached)
+ if (timer->timer[i].irq_target)
irq = 1;
}
@@ -230,12 +253,10 @@
timer->timer[i].counter -= 65536.0f;
timer->timer[i].max_reached = 1;
- if (timer->timer[i].irq_max && !prev_max_reached)
+ if (timer->timer[i].irq_max)
irq = 1;
}
- timer->timer[i].div_counter &= 0xffff;
-
if (!irq)
return;
@@ -258,8 +279,9 @@
timer->timer[i].irq = 1;
if (trigger) {
- // printf("timer %u irq fire\n", i);
-
+ // if ((i == 1))
+ // printf("timer 1 irq fire\n");
+
psx_ic_irq(timer->ic, 16 << i);
}
}
@@ -308,10 +330,9 @@
return;
if (T2_CLKSRC <= 1) {
- T2_COUNTER += cyc;
+ T2_COUNTER += (float)cyc;
} else {
- T2_COUNTER += ((float)cyc) * (1.0f / 8.0f);
- // T2_COUNTER = T2_DIV_COUNTER >> 3;
+ T2_COUNTER += ((float)cyc) / 8.0f;
}
timer_handle_irq(timer, 2);
@@ -331,8 +352,11 @@
timer->hblank = 1;
- if ((T1_CLKSRC & 1) && !T1_PAUSED)
+ if ((T1_CLKSRC & 1) && !T1_PAUSED) {
++T1_COUNTER;
+
+ timer_handle_irq(timer, 1);
+ }
if (!T0_SYNC_EN)
return;
--- a/psx/msf.c
+++ b/psx/msf.c
@@ -23,7 +23,7 @@
msf->f -= CD_SECTORS_PS * s;
}
- if (msf->s > 60) {
+ if (msf->s >= 60) {
int m = msf->s / 60;
msf->m += m;
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -174,7 +174,7 @@
psx_ic_init(psx->ic, psx->cpu);
psx_scratchpad_init(psx->scratchpad);
psx_gpu_init(psx->gpu, psx->ic);
- psx_spu_init(psx->spu);
+ psx_spu_init(psx->spu, psx->ic);
psx_timer_init(psx->timer, psx->ic, psx->gpu);
psx_cdrom_init(psx->cdrom, psx->ic);
psx_pad_init(psx->pad, psx->ic);
--
⑨