diff --git a/build-win64.ps1 b/build-win64.ps1 index c5ee3de..4b6057f 100644 --- a/build-win64.ps1 +++ b/build-win64.ps1 @@ -29,4 +29,4 @@ gcc -I"$($PSX_DIR)" ` -Wno-address-of-packed-member ` -ffast-math -Ofast -g -flto - Copy-Item -Path "$($SDL2_DIR)\bin\SDL2.dll" -Destination "bin" \ No newline at end of file +Copy-Item -Path "$($SDL2_DIR)\bin\SDL2.dll" -Destination "bin" \ No newline at end of file diff --git a/frontend/screen.c b/frontend/screen.c index 5cda8d0..2b82d2f 100644 --- a/frontend/screen.c +++ b/frontend/screen.c @@ -121,8 +121,6 @@ void psxe_screen_reload(psxe_screen_t* screen) { SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC ); - SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "linear"), - screen->texture = SDL_CreateTexture( screen->renderer, screen->format, @@ -146,8 +144,6 @@ void psxe_screen_reload(psxe_screen_t* screen) { SDL_RenderSetScale(screen->renderer, width_scale, height_scale); } - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0); - screen->open = 1; } @@ -486,4 +482,6 @@ void psxe_gpu_vblank_event_cb(psx_gpu_t* gpu) { psxe_screen_t* screen = gpu->udata[0]; psxe_screen_update(screen); + + psxe_gpu_vblank_timer_event_cb(gpu); } \ No newline at end of file diff --git a/psx/dev/cdrom.c b/psx/dev/cdrom.c index fc74e09..2d43dd7 100644 --- a/psx/dev/cdrom.c +++ b/psx/dev/cdrom.c @@ -262,14 +262,13 @@ void cdrom_cmd_getstat(psx_cdrom_t* cdrom) { SET_BITS(ifr, IFR_INT, IFR_INT3); RESP_PUSH( GETSTAT_MOTOR | - (cdrom->cdda_playing ? GETSTAT_PLAY : 0) | + ((cdrom->cdda_playing || cdrom->xa_playing) ? GETSTAT_PLAY : 0) | (cdrom->ongoing_read_command ? GETSTAT_READ : 0) | (cdrom->disc ? 0 : GETSTAT_TRAYOPEN) ); if (cdrom->ongoing_read_command) { - cdrom->status |= STAT_BUSYSTS_MASK; - // printf("command=%02x\n", cdrom->ongoing_read_command); + // 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; @@ -398,6 +397,8 @@ void cdrom_cmd_play(psx_cdrom_t* cdrom) { cdrom->state = CD_STATE_SEND_RESP1; cdrom->delayed_command = CDL_PLAY; + printf("play track %u\n", track); + if (track) { psx_disc_get_track_addr(cdrom->disc, &cdrom->cdda_msf, track); @@ -964,7 +965,7 @@ void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) { RESP_PUSH(0x01); if (cdrom->ongoing_read_command) { - //// printf("command=%02x\n", cdrom->ongoing_read_command); + 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; @@ -1034,8 +1035,15 @@ void cdrom_cmd_gettn(psx_cdrom_t* cdrom) { RESP_PUSH(0x01); RESP_PUSH(GETSTAT_MOTOR); - 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; } } @@ -1415,17 +1423,16 @@ 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->irq_delay = DELAY_1MS; cdrom->state = CD_STATE_SEND_RESP1; cdrom->delayed_command = CDL_READTOC; } break; case CD_STATE_SEND_RESP1: { - cdrom->status &= ~STAT_BUSYSTS_MASK; SET_BITS(ifr, IFR_INT, 3); RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ); - cdrom->irq_delay = DELAY_1MS * 1000; + cdrom->irq_delay = DELAY_1MS; cdrom->state = CD_STATE_SEND_RESP2; cdrom->delayed_command = CDL_READTOC; } break; @@ -1634,6 +1641,17 @@ void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t value) { cdrom->command = value; cdrom->state = CD_STATE_RECV_CMD; + // Required for Spyro - The Year of the Dragon + if (!cdrom->command) { + cdrom->irq_delay = DELAY_1MS * 600; + cdrom->delayed_command = CDL_ERROR; + cdrom->state = CD_STATE_ERROR; + cdrom->error = ERR_INVSUBF; + cdrom->error_flags = GETSTAT_SEEKERROR; + + return; + } + g_psx_cdrom_command_table[value](cdrom); } @@ -2114,6 +2132,10 @@ void cdrom_fetch_xa_sector(psx_cdrom_t* cdrom) { msf_add_f(&cdrom->xa_msf, 1); + // Check for EOR, EOF bits + if (cdrom->xa_sector_buf[0x12] & 0x80) + return; + // Check RT and Audio bit if ((cdrom->xa_sector_buf[0x12] & 4) != 4) continue; diff --git a/psx/dev/gpu.c b/psx/dev/gpu.c index d88ee29..f954400 100644 --- a/psx/dev/gpu.c +++ b/psx/dev/gpu.c @@ -1772,7 +1772,8 @@ void gpu_hblank_event(psx_gpu_t* gpu) { // enough. Sending T2 IRQs every line fixes DoA // but breaks a bunch of games, so I'll keep this // like this until I actually fix the timers - // Games that cared about T2: + // Games that seem to care about T2 timing: + // - Street Fighter Alpha 2 // - Dead or Alive // - NBA Jam // - Doom @@ -1782,7 +1783,14 @@ void gpu_hblank_event(psx_gpu_t* gpu) { // - Mortal Kombat // - PaRappa the Rapper // - In The Hunt + // - Crash Bandicoot + // - Jackie Chan Stuntmaster // - etc. + // Masking with 7 breaks Street Fighter Alpha 2. The game + // just stops sending commands to the CDROM while on + // 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); } else { @@ -1815,7 +1823,7 @@ void psx_gpu_update(psx_gpu_t* gpu, int cyc) { int curr_hblank = (gpu->cycles >= GPU_CYCLES_PER_HDRAW_NTSC) && (gpu->cycles <= GPU_CYCLES_PER_SCANL_NTSC); - + if (curr_hblank && !prev_hblank) { if (gpu->event_cb_table[GPU_EVENT_HBLANK]) gpu->event_cb_table[GPU_EVENT_HBLANK](gpu); diff --git a/psx/dev/ic.c b/psx/dev/ic.c index 68bfdda..01a1a03 100644 --- a/psx/dev/ic.c +++ b/psx/dev/ic.c @@ -70,9 +70,8 @@ void psx_ic_write32(psx_ic_t* ic, uint32_t offset, uint32_t value) { } // Emulate acknowledge - if (!(ic->stat & ic->mask)) { + if (!(ic->stat & ic->mask)) ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2; - } } void psx_ic_write16(psx_ic_t* ic, uint32_t offset, uint16_t value) { @@ -84,9 +83,8 @@ void psx_ic_write16(psx_ic_t* ic, uint32_t offset, uint16_t value) { } // Emulate acknowledge - if (!(ic->stat & ic->mask)) { + if (!(ic->stat & ic->mask)) ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2; - } } void psx_ic_write8(psx_ic_t* ic, uint32_t offset, uint8_t value) { @@ -102,14 +100,16 @@ void psx_ic_write8(psx_ic_t* ic, uint32_t offset, uint8_t value) { } // Emulate acknowledge - if (!(ic->stat & ic->mask)) { + if (!(ic->stat & ic->mask)) ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2; - } } void psx_ic_irq(psx_ic_t* ic, int id) { ic->stat |= id; + // if (ic->mask & (1 << id)) + // printf("%u IRQ gone through\n"); + if (ic->mask & ic->stat) psx_cpu_set_irq_pending(ic->cpu); } diff --git a/psx/dev/spu.c b/psx/dev/spu.c index 6a1d8cb..b0e8455 100644 --- a/psx/dev/spu.c +++ b/psx/dev/spu.c @@ -568,6 +568,9 @@ uint32_t psx_spu_get_sample(psx_spu_t* spu) { spu->data[v].counter &= 0xfff; spu->data[v].counter |= sample_index << 12; + if (spu->data[v].block_flags & 4) + spu->data[v].repeat_addr = spu->data[v].current_addr; + switch (spu->data[v].block_flags & 3) { case 0: case 2: { spu->data[v].current_addr += 0x10; @@ -587,9 +590,6 @@ uint32_t psx_spu_get_sample(psx_spu_t* spu) { } break; } - if (spu->data[v].block_flags & 4) - spu->data[v].repeat_addr = spu->data[v].current_addr; - spu_read_block(spu, v); } diff --git a/psx/dev/timer.c b/psx/dev/timer.c index 4904295..0478738 100644 --- a/psx/dev/timer.c +++ b/psx/dev/timer.c @@ -5,52 +5,118 @@ #include "timer.h" #include "../log.h" -#define T0_COUNTER timer->timer[0].counter -#define T0_PREV timer->timer[0].prev_counter -#define T0_MODE timer->timer[0].mode -#define T0_TARGET timer->timer[0].target -#define T0_PAUSED timer->timer[0].paused -#define T0_IRQ_FIRED timer->timer[0].irq_fired - -#define T1_COUNTER timer->timer[1].counter -#define T1_PREV timer->timer[1].prev_counter -#define T1_MODE timer->timer[1].mode -#define T1_TARGET timer->timer[1].target -#define T1_PAUSED timer->timer[1].paused -#define T1_IRQ_FIRED timer->timer[1].irq_fired - -#define T2_COUNTER timer->timer[2].counter -#define T2_PREV timer->timer[2].prev_counter -#define T2_MODE timer->timer[2].mode -#define T2_TARGET timer->timer[2].target -#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; -// } +#define T0_COUNTER timer->timer[0].counter +#define T0_SYNC_EN timer->timer[0].sync_enable +#define T0_SYNC_MODE timer->timer[0].sync_mode +#define T0_RESET_TGT timer->timer[0].reset_target +#define T0_IRQ_TGT timer->timer[0].irq_target +#define T0_IRQ_MAX timer->timer[0].irq_max +#define T0_IRQ_REPEAT timer->timer[0].irq_repeat +#define T0_IRQ_TOGGLE timer->timer[0].irq_toggle +#define T0_CLKSRC timer->timer[0].clk_source +#define T0_IRQ timer->timer[0].irq +#define T0_TGT_REACHED timer->timer[0].target_reached +#define T0_MAX_REACHED timer->timer[0].max_reached +#define T0_IRQ_FIRED timer->timer[0].irq_fired +#define T0_PAUSED timer->timer[0].paused +#define T0_BLANK_ONCE timer->timer[0].blank_once +#define T1_COUNTER timer->timer[1].counter +#define T1_SYNC_EN timer->timer[1].sync_enable +#define T1_SYNC_MODE timer->timer[1].sync_mode +#define T1_RESET_TGT timer->timer[1].reset_target +#define T1_IRQ_TGT timer->timer[1].irq_target +#define T1_IRQ_MAX timer->timer[1].irq_max +#define T1_IRQ_REPEAT timer->timer[1].irq_repeat +#define T1_IRQ_TOGGLE timer->timer[1].irq_toggle +#define T1_CLKSRC timer->timer[1].clk_source +#define T1_IRQ timer->timer[1].irq +#define T1_TGT_REACHED timer->timer[1].target_reached +#define T1_MAX_REACHED timer->timer[1].max_reached +#define T1_IRQ_FIRED timer->timer[1].irq_fired +#define T1_PAUSED timer->timer[1].paused +#define T1_BLANK_ONCE timer->timer[1].blank_once +#define T2_COUNTER timer->timer[2].counter +#define T2_SYNC_EN timer->timer[2].sync_enable +#define T2_SYNC_MODE timer->timer[2].sync_mode +#define T2_RESET_TGT timer->timer[2].reset_target +#define T2_IRQ_TGT timer->timer[2].irq_target +#define T2_IRQ_MAX timer->timer[2].irq_max +#define T2_IRQ_REPEAT timer->timer[2].irq_repeat +#define T2_IRQ_TOGGLE timer->timer[2].irq_toggle +#define T2_CLKSRC timer->timer[2].clk_source +#define T2_IRQ timer->timer[2].irq +#define T2_TGT_REACHED timer->timer[2].target_reached +#define T2_MAX_REACHED timer->timer[2].max_reached +#define T2_IRQ_FIRED timer->timer[2].irq_fired +#define T2_PAUSED timer->timer[2].paused +#define T2_BLANK_ONCE timer->timer[2].blank_once +#define T2_DIV_COUNTER timer->timer[2].div_counter + +uint16_t timer_get_mode(psx_timer_t* timer, int index) { + uint16_t value = (timer->timer[index].sync_enable << 0) | + (timer->timer[index].sync_mode << 1) | + (timer->timer[index].reset_target << 3) | + (timer->timer[index].irq_target << 4) | + (timer->timer[index].irq_max << 5) | + (timer->timer[index].irq_repeat << 6) | + (timer->timer[index].irq_toggle << 7) | + (timer->timer[index].clk_source << 8) | + (timer->timer[index].irq << 10) | + (timer->timer[index].target_reached << 11) | + (timer->timer[index].max_reached << 12); + + timer->timer[index].target_reached = 0; + timer->timer[index].max_reached = 0; + + return value; +} + +void timer_set_mode(psx_timer_t* timer, int index, uint16_t value) { + timer->timer[index].sync_enable = (value >> 0) & 1; + timer->timer[index].sync_mode = (value >> 1) & 3; + timer->timer[index].reset_target = (value >> 3) & 1; + timer->timer[index].irq_target = (value >> 4) & 1; + timer->timer[index].irq_max = (value >> 5) & 1; + timer->timer[index].irq_repeat = (value >> 6) & 1; + timer->timer[index].irq_toggle = (value >> 7) & 1; + timer->timer[index].clk_source = (value >> 8) & 3; + timer->timer[index].target_reached = 0; + timer->timer[index].max_reached = 0; + + // IRQ and counter are set to 0 on mode writes + timer->timer[index].irq = 1; + timer->timer[index].irq_fired = 0; + timer->timer[index].counter = 0; + timer->timer[index].div_counter = 0; + timer->timer[index].blank_once = 0; + timer->timer[index].paused = 0; + + if (index == 2) + printf("timer_set_mode %u %04x\n", index, value); + + switch (index) { + case 0: { + if ((T0_SYNC_MODE == 1) || (T0_SYNC_MODE == 2) || !T0_SYNC_EN) + return; + + T0_PAUSED = timer->hblank | (T0_SYNC_MODE == 3); + } break; + + case 1: { + if ((T1_SYNC_MODE == 1) || (T1_SYNC_MODE == 2) || !T1_SYNC_EN) + return; + + T1_PAUSED = timer->vblank | (T1_SYNC_MODE == 3); + } break; + + case 2: { + if (!T2_SYNC_EN) + return; + + T2_PAUSED = (T2_SYNC_MODE == 0) || (T2_SYNC_MODE == 3); + } break; + } +} const char* g_psx_timer_reg_names[] = { "counter", 0, 0, 0, @@ -62,36 +128,27 @@ psx_timer_t* psx_timer_create(void) { return (psx_timer_t*)malloc(sizeof(psx_timer_t)); } -void psx_timer_init(psx_timer_t* timer, psx_ic_t* ic) { +void psx_timer_init(psx_timer_t* timer, psx_ic_t* ic, psx_gpu_t* gpu) { memset(timer, 0, sizeof(psx_timer_t)); timer->io_base = PSX_TIMER_BEGIN; timer->io_size = PSX_TIMER_SIZE; timer->ic = ic; + timer->gpu = gpu; } -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; - } break; - case 4: { - timer->timer[index].mode &= 0xffffe7ff; - - return timer->timer[index].mode; - } break; + case 0: return timer->timer[index].counter; + case 4: return timer_get_mode(timer, index); case 8: return timer->timer[index].target; } - log_fatal("Unhandled 32-bit TIMER read at offset %08x", offset); - - // exit(1); + printf("Unhandled 32-bit TIMER read at offset %08x\n", offset); return 0x0; } @@ -102,11 +159,7 @@ uint16_t psx_timer_read16(psx_timer_t* timer, uint32_t offset) { switch (reg) { case 0: return timer->timer[index].counter; - case 4: { - timer->timer[index].mode &= 0xffffe7ff; - - return timer->timer[index].mode; - } break; + case 4: return timer_get_mode(timer, index); case 8: return timer->timer[index].target; } @@ -121,26 +174,19 @@ uint8_t psx_timer_read8(psx_timer_t* timer, uint32_t offset) { return 0x0; } +void timer_handle_irq(psx_timer_t* timer, int i); + void psx_timer_write32(psx_timer_t* timer, uint32_t offset, uint32_t value) { int index = offset >> 4; int reg = offset & 0xf; switch (reg) { - case 0: { - timer->timer[index].counter = value; - } return; - case 4: { - timer->timer[index].mode = value; - timer->timer[index].mode |= 0x400; - timer->timer[index].irq_fired = 0; - timer->timer[index].counter = 0; - } return; - case 8: timer->timer[index].target = value; return; + case 0: timer->timer[index].counter = value & 0xffff; break; + case 4: timer_set_mode(timer, index, value); break; + case 8: timer->timer[index].target = value & 0xffff; break; } - log_fatal("Unhandled 32-bit TIMER write at offset %08x (%02x)", offset, value); - - // exit(1); + timer_handle_irq(timer, index); } void psx_timer_write16(psx_timer_t* timer, uint32_t offset, uint16_t value) { @@ -148,221 +194,210 @@ void psx_timer_write16(psx_timer_t* timer, uint32_t offset, uint16_t value) { int reg = offset & 0xf; switch (reg) { - case 0: { - timer->timer[index].counter = value; - } return; - case 4: { - timer->timer[index].mode = value; - timer->timer[index].mode |= 0x400; - timer->timer[index].irq_fired = 0; - timer->timer[index].counter = 0; - } return; - case 8: { - timer->timer[index].target = value; - } return; + case 0: timer->timer[index].counter = value & 0xffff; break; + case 4: timer_set_mode(timer, index, value); break; + case 8: timer->timer[index].target = value & 0xffff; break; } - printf("Unhandled 16-bit TIMER write at offset %08x (%02x)\n", offset, value); - - // exit(1); + timer_handle_irq(timer, index); } void psx_timer_write8(psx_timer_t* timer, uint32_t offset, uint8_t value) { printf("Unhandled 8-bit TIMER write at offset %08x (%02x)\n", offset, value); } -void timer_update_timer0(psx_timer_t* timer, int cyc) { - int reached_target = ((uint32_t)T0_COUNTER + cyc) >= T0_TARGET; - int reached_max = ((uint32_t)T0_COUNTER + cyc) >= 0xffff; +void timer_handle_irq(psx_timer_t* timer, int i) { + int irq = 0; - // Dotclock unsupported - // if (T0_MODE & 0x100) + 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; - if (!T0_PAUSED) - T0_COUNTER += cyc; + if (target_reached) { + timer->timer[i].target_reached = 1; - int can_fire_irq = (T0_MODE & MODE_IRQRMD) || !T0_IRQ_FIRED; + if (timer->timer[i].reset_target) { + timer->timer[i].counter = 0; + timer->timer[i].div_counter = 0; + } - int target_irq = reached_target && (T0_MODE & MODE_TGTIRQ); - int max_irq = reached_max && (T0_MODE & MODE_MAXIRQ); + if (timer->timer[i].irq_target && !prev_target_reached) + irq = 1; + } - T0_MODE &= ~0x0800; - T0_MODE |= reached_target << 11; + if (max_reached) { + timer->timer[i].counter -= 65536.0f; + timer->timer[i].max_reached = 1; - T0_MODE &= ~0x1000; - T0_MODE |= reached_max << 12; + if (timer->timer[i].irq_max && !prev_max_reached) + irq = 1; + } - if ((target_irq || max_irq) && can_fire_irq) { - if (T0_MODE & MODE_IRQPMD) { - T0_MODE ^= 0x400; - } else { - T0_MODE |= 0x400; - } + timer->timer[i].div_counter &= 0xffff; - timer->timer[0].irq_fired = 1; + if (!irq) + return; - psx_ic_irq(timer->ic, IC_TIMER0); + if (!timer->timer[i].irq_toggle) { + timer->timer[i].irq = 0; + } else { + timer->timer[i].irq ^= 1; } - if (T0_MODE & MODE_RESETC) { - if (reached_target) - T0_COUNTER -= T0_TARGET; + int trigger = !timer->timer[i].irq; + + if (!timer->timer[i].irq_repeat) { + if (trigger && !timer->timer[i].irq_fired) { + timer->timer[i].irq_fired = 1; + } else { + return; + } + } + + timer->timer[i].irq = 1; + + if (trigger) { + // printf("timer %u irq fire\n", i); + + psx_ic_irq(timer->ic, 16 << i); } } -void timer_update_timer1(psx_timer_t* timer, int cyc) { - int reached_target, reached_max; +float timer_get_dotclock_div(psx_timer_t* timer) { + static const float dmode_dotclk_div_table[] = { + 10.0f, 8.0f, 5.0f, 4.0f + }; - if (T1_MODE & 0x100) { - reached_target = T1_COUNTER == T1_TARGET; - reached_max = T1_COUNTER == 0xffff; + if (timer->gpu->display_mode & 0x40) { + return 11.0f / 7.0f / 7.0f; } else { - reached_target = ((uint32_t)T1_COUNTER + cyc) >= T1_TARGET; - reached_max = ((uint32_t)T1_COUNTER + cyc) >= 0xffff; - - if (!T1_PAUSED) - T1_COUNTER += cyc; + return 11.0f / 7.0f / dmode_dotclk_div_table[timer->gpu->display_mode & 0x3]; } - - int can_fire_irq = (T1_MODE & MODE_IRQRMD) || !T1_IRQ_FIRED; - - int target_irq = reached_target && (T1_MODE & MODE_TGTIRQ); - int max_irq = reached_max && (T1_MODE & MODE_MAXIRQ); +} - T1_MODE &= ~0x0800; - T1_MODE |= reached_target << 11; +void timer_update_timer0(psx_timer_t* timer, int cyc) { + if (T0_PAUSED) + return; - T1_MODE &= ~0x1000; - T1_MODE |= reached_max << 12; + if (T0_CLKSRC & 1) { + // Dotclock test + T0_COUNTER += (float)cyc * timer_get_dotclock_div(timer); + } else { + T0_COUNTER += (float)cyc; + } - if ((target_irq || max_irq) && can_fire_irq) { - if (T1_MODE & MODE_IRQPMD) { - T1_MODE ^= 0x400; - } else { - T1_MODE |= 0x400; - } + timer_handle_irq(timer, 0); +} - T1_IRQ_FIRED = 1; +void timer_update_timer1(psx_timer_t* timer, int cyc) { + if (T1_PAUSED) + return; - psx_ic_irq(timer->ic, IC_TIMER1); + if (T1_CLKSRC & 1) { + // Counter is incremented in our hblank callback + } else { + T1_COUNTER += (float)cyc; } - if (T1_MODE & MODE_RESETC) { - if (reached_target) - T1_COUNTER -= T1_TARGET; - } + timer_handle_irq(timer, 1); } void timer_update_timer2(psx_timer_t* timer, int cyc) { - T2_PREV = T2_COUNTER; + if (T2_PAUSED) + return; - if (!T2_PAUSED) + if (T2_CLKSRC <= 1) { T2_COUNTER += cyc; - - uint16_t reset = (T2_MODE & MODE_RESETC) ? T2_TARGET : 0xffff; - - if ((T2_COUNTER >= reset) && (T2_PREV <= reset)) { - T2_COUNTER -= reset; - T2_MODE |= 0x800; - - if (reset == 0xffff) - T2_MODE |= 0x1000; + } else { + T2_COUNTER += ((float)cyc) * (1.0f / 8.0f); + // T2_COUNTER = T2_DIV_COUNTER >> 3; } - if ((T2_COUNTER >= 0xffff) && (T2_PREV <= 0xffff)) { - T2_COUNTER &= 0xffff; - T2_MODE |= 0x1000; - } + timer_handle_irq(timer, 2); } void psx_timer_update(psx_timer_t* timer, int cyc) { - timer_update_timer0(timer, cyc); - timer_update_timer1(timer, cyc); - timer_update_timer2(timer, cyc); + timer->prev_hblank = timer->hblank; + timer->prev_vblank = timer->vblank; + + timer_update_timer0(timer, 2); + timer_update_timer1(timer, 2); + timer_update_timer2(timer, 2); } void psxe_gpu_hblank_event_cb(psx_gpu_t* gpu) { psx_timer_t* timer = gpu->udata[1]; - if (T1_MODE & 0x100 && !T1_PAUSED) - T1_COUNTER++; + timer->hblank = 1; - if (T0_MODE & MODE_SYNCEN) { - switch (T0_MODE & 6) { - case 0: { - T0_PAUSED = 1; - } break; + if ((T1_CLKSRC & 1) && !T1_PAUSED) + ++T1_COUNTER; - case 2: { - T0_COUNTER = 0; - } break; + if (!T0_SYNC_EN) + return; - case 4: { - T0_COUNTER = 0; + switch (T0_SYNC_MODE) { + case 0: T0_PAUSED = 1; break; + case 1: T0_COUNTER = 0; break; + case 2: T0_COUNTER = 0; T0_PAUSED = 0; break; + case 3: { + if (!T0_BLANK_ONCE) { + T0_BLANK_ONCE = 1; + T0_SYNC_EN = 0; T0_PAUSED = 0; - } break; - - case 6: { - T0_MODE &= ~MODE_SYNCEN; - } break; - } + } + } break; } } void psxe_gpu_hblank_end_event_cb(psx_gpu_t* gpu) { psx_timer_t* timer = gpu->udata[1]; - if (T0_MODE & MODE_SYNCEN) { - switch (T0_MODE & 6) { - case 0: { - T0_PAUSED = 0; - } break; + timer->hblank = 0; - case 4: { - T0_PAUSED = 1; - } break; - } + if (!T0_SYNC_EN) + return; + + switch (T0_SYNC_MODE) { + case 0: T0_PAUSED = 0; break; + case 2: T0_PAUSED = 1; break; } } void psxe_gpu_vblank_timer_event_cb(psx_gpu_t* gpu) { psx_timer_t* timer = gpu->udata[1]; - if (T1_MODE & MODE_SYNCEN) { - switch (T1_MODE & 6) { - case 0: { - T1_PAUSED = 1; - } break; + timer->vblank = 1; - case 2: { - T1_COUNTER = 0; - } break; + if (!T1_SYNC_EN) + return; - case 4: { - T1_COUNTER = 0; + switch (T1_SYNC_MODE) { + case 0: T1_PAUSED = 1; break; + case 1: T1_COUNTER = 0; break; + case 2: T1_COUNTER = 0; T1_PAUSED = 0; break; + case 3: { + if (!T1_BLANK_ONCE) { + T1_BLANK_ONCE = 1; + T1_SYNC_EN = 0; T1_PAUSED = 0; - } break; - - case 6: { - T1_MODE &= ~MODE_SYNCEN; - } break; - } + } + } break; } } void psxe_gpu_vblank_end_event_cb(psx_gpu_t* gpu) { psx_timer_t* timer = gpu->udata[1]; - if (T1_MODE & MODE_SYNCEN) { - switch (T1_MODE & 6) { - case 0: { - T1_PAUSED = 0; - } break; + timer->vblank = 0; - case 4: { - T1_PAUSED = 1; - } break; - } + if (!T1_SYNC_EN) + return; + + switch (T1_SYNC_MODE) { + case 0: T1_PAUSED = 0; break; + case 2: T1_PAUSED = 1; break; } } diff --git a/psx/dev/timer.h b/psx/dev/timer.h index e753f45..88a20b3 100644 --- a/psx/dev/timer.h +++ b/psx/dev/timer.h @@ -57,24 +57,69 @@ #define MODE_IRQPMD 0x0080 #define MODE_CLK 0x0080 +/* + 0 Synchronization Enable (0=Free Run, 1=Synchronize via Bit1-2) + 1-2 Synchronization Mode (0-3, see lists below) + Synchronization Modes for Counter 0: + 0 = Pause counter during Hblank(s) + 1 = Reset counter to 0000h at Hblank(s) + 2 = Reset counter to 0000h at Hblank(s) and pause outside of Hblank + 3 = Pause until Hblank occurs once, then switch to Free Run + Synchronization Modes for Counter 1: + Same as above, but using Vblank instead of Hblank + Synchronization Modes for Counter 2: + 0 or 3 = Stop counter at current value (forever, no h/v-blank start) + 1 or 2 = Free Run (same as when Synchronization Disabled) + 3 Reset counter to 0000h (0=After Counter=FFFFh, 1=After Counter=Target) + 4 IRQ when Counter=Target (0=Disable, 1=Enable) + 5 IRQ when Counter=FFFFh (0=Disable, 1=Enable) + 6 IRQ Once/Repeat Mode (0=One-shot, 1=Repeatedly) + 7 IRQ Pulse/Toggle Mode (0=Short Bit10=0 Pulse, 1=Toggle Bit10 on/off) + 8-9 Clock Source (0-3, see list below) + Counter 0: 0 or 2 = System Clock, 1 or 3 = Dotclock + Counter 1: 0 or 2 = System Clock, 1 or 3 = Hblank + Counter 2: 0 or 1 = System Clock, 2 or 3 = System Clock/8 + 10 Interrupt Request (0=Yes, 1=No) (Set after Writing) (W=1) (R) + 11 Reached Target Value (0=No, 1=Yes) (Reset after Reading) (R) + 12 Reached FFFFh Value (0=No, 1=Yes) (Reset after Reading) (R) + 13-15 Unknown (seems to be always zero) + 16-31 Garbage (next opcode) +*/ + typedef struct { uint32_t bus_delay; uint32_t io_base, io_size; psx_ic_t* ic; + psx_gpu_t* gpu; + + int hblank, prev_hblank; + int vblank, prev_vblank; struct { - uint16_t prev_counter; - uint16_t counter; - uint32_t mode; + float counter; uint32_t target; - int paused; + int sync_enable; + int sync_mode; + int reset_target; + int irq_target; + int irq_max; + int irq_repeat; + int irq_toggle; + int clk_source; + int irq; + int target_reached; + int max_reached; int irq_fired; + uint32_t div_counter; + + int paused; + int blank_once; } timer[3]; } psx_timer_t; psx_timer_t* psx_timer_create(void); -void psx_timer_init(psx_timer_t*, psx_ic_t*); +void psx_timer_init(psx_timer_t*, psx_ic_t*, psx_gpu_t*); uint32_t psx_timer_read32(psx_timer_t*, uint32_t); uint16_t psx_timer_read16(psx_timer_t*, uint32_t); uint8_t psx_timer_read8(psx_timer_t*, uint32_t); diff --git a/psx/psx.c b/psx/psx.c index d2798b4..d394675 100644 --- a/psx/psx.c +++ b/psx/psx.c @@ -175,7 +175,7 @@ void psx_init(psx_t* psx, const char* bios_path, const char* exp_path) { psx_scratchpad_init(psx->scratchpad); psx_gpu_init(psx->gpu, psx->ic); psx_spu_init(psx->spu); - psx_timer_init(psx->timer, 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); psx_mdec_init(psx->mdec);