ref: 7f9fc68a99d1056963e74a37e921d449a3c99cca
parent: a6366b678095acd5d5388576ce1e8cb5574e7c88
author: allkern <lisandroaalarcon@gmail.com>
date: Tue May 21 10:04:03 EDT 2024
A lot of fixes and improvements Fixed huge SPU bug Completely rewrote timers
--- a/build-win64.ps1
+++ b/build-win64.ps1
@@ -29,4 +29,4 @@
-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
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -121,8 +121,6 @@
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 @@
SDL_RenderSetScale(screen->renderer, width_scale, height_scale);
}
- SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0);
-
screen->open = 1;
}
@@ -486,4 +482,6 @@
psxe_screen_t* screen = gpu->udata[0];
psxe_screen_update(screen);
+
+ psxe_gpu_vblank_timer_event_cb(gpu);
}
\ No newline at end of file
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -262,14 +262,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
}
@@ -2113,6 +2131,10 @@
psx_disc_read_sector(cdrom->disc, cdrom->xa_sector_buf);
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)
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -1772,7 +1772,8 @@
// 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 @@
// - 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 @@
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);
--- a/psx/dev/ic.c
+++ b/psx/dev/ic.c
@@ -70,9 +70,8 @@
}
// 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 @@
}
// 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,13 +100,15 @@
}
// 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);
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -568,6 +568,9 @@
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;
@@ -586,9 +589,6 @@
spu->data[v].current_addr = spu->data[v].repeat_addr;
} break;
}
-
- if (spu->data[v].block_flags & 4)
- spu->data[v].repeat_addr = spu->data[v].current_addr;
spu_read_block(spu, v);
}
--- a/psx/dev/timer.c
+++ b/psx/dev/timer.c
@@ -5,53 +5,119 @@
#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 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
-#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
+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);
-#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
+ timer->timer[index].target_reached = 0;
+ timer->timer[index].max_reached = 0;
-// 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();
-// }
-// }
+ return value;
+}
-// bool did_timer_reach_target(Timer timer) {
-// if ((timer.mode & 8) == 1) return timer.value >= timer.target;
-// return timer.value >= 0xffff;
-// }
+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;
-// 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;
-// }
+ // 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,
"mode", 0, 0, 0,
@@ -62,7 +128,7 @@
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;
@@ -69,30 +135,21 @@
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);
+ printf("Unhandled 32-bit TIMER read at offset %08x\n", offset);
- // exit(1);
-
return 0x0;
}
@@ -102,11 +159,7 @@
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 @@
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,23 +194,12 @@
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) {
@@ -171,141 +206,148 @@
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;
+ timer->timer[i].div_counter &= 0xffff;
+
+ if (!irq)
+ return;
+
+ if (!timer->timer[i].irq_toggle) {
+ timer->timer[i].irq = 0;
+ } else {
+ timer->timer[i].irq ^= 1;
+ }
+
+ 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 {
- T0_MODE |= 0x400;
+ return;
}
+ }
- timer->timer[0].irq_fired = 1;
+ timer->timer[i].irq = 1;
- psx_ic_irq(timer->ic, IC_TIMER0);
+ if (trigger) {
+ // printf("timer %u irq fire\n", i);
+
+ psx_ic_irq(timer->ic, 16 << i);
}
+}
- if (T0_MODE & MODE_RESETC) {
- if (reached_target)
- T0_COUNTER -= T0_TARGET;
+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 (timer->gpu->display_mode & 0x40) {
+ return 11.0f / 7.0f / 7.0f;
+ } else {
+ return 11.0f / 7.0f / dmode_dotclk_div_table[timer->gpu->display_mode & 0x3];
}
}
-void timer_update_timer1(psx_timer_t* timer, int cyc) {
- int reached_target, reached_max;
+void timer_update_timer0(psx_timer_t* timer, int cyc) {
+ if (T0_PAUSED)
+ return;
- if (T1_MODE & 0x100) {
- reached_target = T1_COUNTER == T1_TARGET;
- reached_max = T1_COUNTER == 0xffff;
+ if (T0_CLKSRC & 1) {
+ // Dotclock test
+ T0_COUNTER += (float)cyc * timer_get_dotclock_div(timer);
} else {
- reached_target = ((uint32_t)T1_COUNTER + cyc) >= T1_TARGET;
- reached_max = ((uint32_t)T1_COUNTER + cyc) >= 0xffff;
-
- if (!T1_PAUSED)
- T1_COUNTER += cyc;
+ T0_COUNTER += (float)cyc;
}
-
- 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);
+ timer_handle_irq(timer, 0);
+}
- T1_MODE &= ~0x0800;
- T1_MODE |= reached_target << 11;
+void timer_update_timer1(psx_timer_t* timer, int cyc) {
+ if (T1_PAUSED)
+ return;
- T1_MODE &= ~0x1000;
- T1_MODE |= reached_max << 12;
-
- if ((target_irq || max_irq) && can_fire_irq) {
- if (T1_MODE & MODE_IRQPMD) {
- T1_MODE ^= 0x400;
- } else {
- T1_MODE |= 0x400;
- }
-
- T1_IRQ_FIRED = 1;
-
- 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;
}
}
@@ -312,16 +354,14 @@
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;
}
}
@@ -328,25 +368,22 @@
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;
}
}
@@ -353,16 +390,14 @@
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;
}
}
--- 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);
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -175,7 +175,7 @@
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);
--
⑨