Skip to content

Commit 7295049

Browse files
committed
feat: double-speed
1 parent b5c416d commit 7295049

1 file changed

Lines changed: 134 additions & 7 deletions

File tree

src/domain/bus.rs

Lines changed: 134 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -367,21 +367,35 @@ impl Bus {
367367
}
368368

369369
pub fn step(&mut self, cycles: u32) {
370-
self.step_div(cycles);
371-
self.step_timer(cycles);
372-
let _ = self.apu.step(cycles);
373-
self.step_ppu(cycles);
370+
// In double-speed mode, CPU cycles are twice as fast, but subsystems
371+
// (PPU, APU, timers) run at normal speed. Scale cycles accordingly.
372+
let subsystem_cycles = if self.double_speed {
373+
cycles / 2
374+
} else {
375+
cycles
376+
};
377+
378+
self.step_div(subsystem_cycles);
379+
self.step_timer(subsystem_cycles);
380+
let _ = self.apu.step(subsystem_cycles);
381+
self.step_ppu(subsystem_cycles);
374382
self.step_hdma();
375-
self.step_dma(cycles);
376-
self.mbc.tick(cycles);
383+
self.step_dma(subsystem_cycles);
384+
self.mbc.tick(subsystem_cycles);
377385
}
378386

379387
pub fn set_rtc_mode(&mut self, mode: RtcMode) {
380388
self.mbc.set_rtc_mode(mode);
381389
}
382390

383391
pub fn apu_step(&mut self, cycles: u32) {
384-
let _ = self.apu.step(cycles);
392+
// In double-speed mode, scale APU cycles appropriately
393+
let subsystem_cycles = if self.double_speed {
394+
cycles / 2
395+
} else {
396+
cycles
397+
};
398+
let _ = self.apu.step(subsystem_cycles);
385399
}
386400

387401
pub fn apu_sample_rate_hz(&self) -> f64 {
@@ -1508,6 +1522,119 @@ mod tests {
15081522
assert!(!bus.speed_switch_pending());
15091523
}
15101524

1525+
#[test]
1526+
fn bus_double_speed_scales_timer_correctly() {
1527+
let mut rom = vec![0; ROM_BANK_SIZE];
1528+
rom[0x0147] = 0x00;
1529+
rom[0x0143] = 0x80; // CGB supported
1530+
let cartridge = Cartridge::from_bytes(rom).expect("cartridge");
1531+
let mut bus = Bus::new(cartridge).expect("bus");
1532+
1533+
// Enable timer with fastest rate (16 cycles per increment in normal speed)
1534+
bus.write8(REG_TAC, 0x05); // Enable, 16-cycle period
1535+
bus.write8(REG_TIMA, 0x00);
1536+
1537+
// In normal speed, 16 cycles should increment TIMA once
1538+
bus.step(16);
1539+
assert_eq!(
1540+
bus.read8(REG_TIMA),
1541+
1,
1542+
"Normal speed: 16 cycles = 1 increment"
1543+
);
1544+
1545+
// Switch to double speed
1546+
bus.write8(REG_KEY1, 0x01);
1547+
bus.perform_speed_switch();
1548+
assert!(bus.is_double_speed());
1549+
1550+
bus.write8(REG_TIMA, 0x00);
1551+
1552+
// In double speed, CPU cycles are twice as fast, so we need 32 CPU cycles
1553+
// for the timer to see 16 timer cycles
1554+
bus.step(32);
1555+
assert_eq!(
1556+
bus.read8(REG_TIMA),
1557+
1,
1558+
"Double speed: 32 CPU cycles = 16 timer cycles = 1 increment"
1559+
);
1560+
}
1561+
1562+
#[test]
1563+
fn bus_double_speed_scales_div_correctly() {
1564+
let mut rom = vec![0; ROM_BANK_SIZE];
1565+
rom[0x0147] = 0x00;
1566+
rom[0x0143] = 0x80; // CGB supported
1567+
let cartridge = Cartridge::from_bytes(rom).expect("cartridge");
1568+
let mut bus = Bus::new(cartridge).expect("bus");
1569+
1570+
// DIV increments every 256 cycles
1571+
bus.write8(REG_DIV, 0x00);
1572+
let initial_div = bus.read8(REG_DIV);
1573+
1574+
// In normal speed, 256 cycles should increment DIV once
1575+
bus.step(256);
1576+
let after_normal = bus.read8(REG_DIV);
1577+
assert_eq!(
1578+
after_normal,
1579+
initial_div.wrapping_add(1),
1580+
"Normal speed: 256 cycles = 1 DIV increment"
1581+
);
1582+
1583+
// Switch to double speed
1584+
bus.write8(REG_KEY1, 0x01);
1585+
bus.perform_speed_switch();
1586+
assert!(bus.is_double_speed());
1587+
1588+
bus.write8(REG_DIV, 0x00);
1589+
let initial_div_double = bus.read8(REG_DIV);
1590+
1591+
// In double speed, need 512 CPU cycles for DIV to see 256 timer cycles
1592+
bus.step(512);
1593+
let after_double = bus.read8(REG_DIV);
1594+
assert_eq!(
1595+
after_double,
1596+
initial_div_double.wrapping_add(1),
1597+
"Double speed: 512 CPU cycles = 256 timer cycles = 1 DIV increment"
1598+
);
1599+
}
1600+
1601+
#[test]
1602+
fn bus_double_speed_ppu_timing() {
1603+
let mut rom = vec![0; ROM_BANK_SIZE];
1604+
rom[0x0147] = 0x00;
1605+
rom[0x0143] = 0x80; // CGB supported
1606+
let cartridge = Cartridge::from_bytes(rom).expect("cartridge");
1607+
let mut bus = Bus::new(cartridge).expect("bus");
1608+
1609+
// PPU line takes 456 cycles in normal mode
1610+
bus.write8(REG_LY, 0x00);
1611+
let initial_ly = bus.read8(REG_LY);
1612+
1613+
// Step one full line worth of cycles
1614+
bus.step(456);
1615+
let after_normal = bus.read8(REG_LY);
1616+
assert!(
1617+
after_normal > initial_ly || after_normal == 0,
1618+
"Normal speed: LY should advance or wrap"
1619+
);
1620+
1621+
// Switch to double speed
1622+
bus.write8(REG_KEY1, 0x01);
1623+
bus.perform_speed_switch();
1624+
assert!(bus.is_double_speed());
1625+
1626+
bus.write8(REG_LY, 0x00);
1627+
let initial_ly_double = bus.read8(REG_LY);
1628+
1629+
// In double speed, need 912 CPU cycles for PPU to see 456 PPU cycles
1630+
bus.step(912);
1631+
let after_double = bus.read8(REG_LY);
1632+
assert!(
1633+
after_double > initial_ly_double || after_double == 0,
1634+
"Double speed: LY should advance with scaled cycles"
1635+
);
1636+
}
1637+
15111638
#[test]
15121639
fn bus_hdma_gdma_minimal() {
15131640
let mut rom = vec![0; ROM_BANK_SIZE];

0 commit comments

Comments
 (0)