Compare commits
2 Commits
55da5a29d8
...
f2c49b398c
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | f2c49b398c | |
Rekai Nyangadzayi Musuka | 6f76571d6c |
|
@ -4,10 +4,10 @@
|
||||||
### Status
|
### Status
|
||||||
* From [Blargg Test ROMs](https://github.com/L-P/blargg-test-roms/)
|
* From [Blargg Test ROMs](https://github.com/L-P/blargg-test-roms/)
|
||||||
* [x] cpu_instrs
|
* [x] cpu_instrs
|
||||||
* [x] instr_timing
|
* [ ] instr_timing (kind of)
|
||||||
* [x] mem_timing
|
* [x] mem_timing
|
||||||
* [x] mem_timing-2
|
* [x] mem_timing-2
|
||||||
* [ ] Partially dmg_sound
|
* [ ] dmg_sound (partial)
|
||||||
* [x] [dmg-acid2](https://github.com/mattcurrie/dmg-acid2)
|
* [x] [dmg-acid2](https://github.com/mattcurrie/dmg-acid2)
|
||||||
* From [mooneye-gb](https://github.com/Gekkio/mooneye-gb):
|
* From [mooneye-gb](https://github.com/Gekkio/mooneye-gb):
|
||||||
* Cartridges:
|
* Cartridges:
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
* [x] MBC2
|
* [x] MBC2
|
||||||
* [x] MBC5
|
* [x] MBC5
|
||||||
* Implements a cycle-accurate PPU FIFO
|
* Implements a cycle-accurate PPU FIFO
|
||||||
* Doesn't \*exactly\* work right just yet
|
* Doesn't \*exactly\* work just yet
|
||||||
|
|
||||||
Supports: ROM-only, MBC1, MBC2, MBC3 and MBC5 games.
|
Supports: ROM-only, MBC1, MBC2, MBC3 and MBC5 games.
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ impl Bus {
|
||||||
fn tick(&mut self, limit: u8) {
|
fn tick(&mut self, limit: u8) {
|
||||||
for _ in 0..limit {
|
for _ in 0..limit {
|
||||||
self.timer.tick();
|
self.timer.tick();
|
||||||
|
self.cart.as_mut().map(|cart| cart.tick());
|
||||||
self.ppu.tick();
|
self.ppu.tick();
|
||||||
self.apu.tick(self.timer.divider);
|
self.apu.tick(self.timer.divider);
|
||||||
self.dma_tick()
|
self.dma_tick()
|
||||||
|
|
185
src/cartridge.rs
185
src/cartridge.rs
|
@ -1,4 +1,8 @@
|
||||||
|
use bitfield::bitfield;
|
||||||
|
|
||||||
use crate::bus::BusIo;
|
use crate::bus::BusIo;
|
||||||
|
use crate::emu::SM83_CLOCK_SPEED;
|
||||||
|
use crate::Cycle;
|
||||||
|
|
||||||
const RAM_SIZE_ADDRESS: usize = 0x0149;
|
const RAM_SIZE_ADDRESS: usize = 0x0149;
|
||||||
const ROM_SIZE_ADDRESS: usize = 0x0148;
|
const ROM_SIZE_ADDRESS: usize = 0x0148;
|
||||||
|
@ -33,6 +37,10 @@ impl Cartridge {
|
||||||
self.mbc.write_ext_ram(memory)
|
self.mbc.write_ext_ram(memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn tick(&mut self) {
|
||||||
|
self.mbc.tick()
|
||||||
|
}
|
||||||
|
|
||||||
fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
|
fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
|
||||||
let ram_size = Self::detect_ram_info(memory);
|
let ram_size = Self::detect_ram_info(memory);
|
||||||
let rom_size = Self::detect_rom_info(memory);
|
let rom_size = Self::detect_rom_info(memory);
|
||||||
|
@ -281,10 +289,120 @@ impl MBCIo for MBC1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RtClockTick for MBC1 {
|
||||||
|
fn tick(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
struct RtClock {
|
||||||
|
/// 6-bit unsigned integer
|
||||||
|
sec: u8,
|
||||||
|
/// 6-bit unsigned integer
|
||||||
|
min: u8,
|
||||||
|
/// 6-bit unsigned integer
|
||||||
|
hr: u8,
|
||||||
|
day_low: u8,
|
||||||
|
day_high: DayHigh,
|
||||||
|
|
||||||
|
cycles: Cycle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RtClock {
|
||||||
|
fn inc_day(&mut self) {
|
||||||
|
// TODO: Figure out order of operations, the brackets are a bit too defenseive here
|
||||||
|
let days: u16 = (((self.day_high.ninth() as u16) << 8) | self.day_low as u16) + 1;
|
||||||
|
|
||||||
|
if days > 0x1FF {
|
||||||
|
self.day_high.set_carry(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.day_high.set_ninth(((days >> 8) & 0x01) == 0x01);
|
||||||
|
self.day_low = days as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RtClockTick for RtClock {
|
||||||
|
fn tick(&mut self) {
|
||||||
|
// This is the sort of situation where you'd want to use a scheduler.
|
||||||
|
if self.day_high.halt() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cycles += 1;
|
||||||
|
|
||||||
|
if self.cycles >= SM83_CLOCK_SPEED {
|
||||||
|
self.cycles %= SM83_CLOCK_SPEED;
|
||||||
|
|
||||||
|
self.sec += 1;
|
||||||
|
|
||||||
|
if self.sec == 60 {
|
||||||
|
self.sec = 0;
|
||||||
|
self.min += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.min == 60 {
|
||||||
|
self.min = 0;
|
||||||
|
self.hr += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.hr == 24 {
|
||||||
|
self.hr = 0;
|
||||||
|
self.inc_day();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait RtClockTick {
|
||||||
|
fn tick(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
struct DayHigh(u8);
|
||||||
|
impl Debug;
|
||||||
|
_, set_carry: 7;
|
||||||
|
halt, _: 6;
|
||||||
|
ninth, set_ninth: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Copy for DayHigh {}
|
||||||
|
impl Clone for DayHigh {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DayHigh {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for DayHigh {
|
||||||
|
fn from(byte: u8) -> Self {
|
||||||
|
Self(byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DayHigh> for u8 {
|
||||||
|
fn from(dh: DayHigh) -> Self {
|
||||||
|
dh.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum MBC3Device {
|
enum MBC3Device {
|
||||||
ExternalRam,
|
ExternalRam,
|
||||||
RealTimeClock,
|
Clock(RtcRegister),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum RtcRegister {
|
||||||
|
Second,
|
||||||
|
Minute,
|
||||||
|
Hour,
|
||||||
|
DayLow,
|
||||||
|
DayHigh,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -302,6 +420,8 @@ struct MBC3 {
|
||||||
prev_latch_write: Option<u8>,
|
prev_latch_write: Option<u8>,
|
||||||
|
|
||||||
has_battery: bool,
|
has_battery: bool,
|
||||||
|
rtc: RtClock,
|
||||||
|
rtc_latch: Option<RtClock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MBC3 {
|
impl MBC3 {
|
||||||
|
@ -314,6 +434,8 @@ impl MBC3 {
|
||||||
mapped: Default::default(),
|
mapped: Default::default(),
|
||||||
prev_latch_write: Default::default(),
|
prev_latch_write: Default::default(),
|
||||||
has_battery: Default::default(),
|
has_battery: Default::default(),
|
||||||
|
rtc: Default::default(),
|
||||||
|
rtc_latch: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,6 +447,8 @@ impl MBC3 {
|
||||||
devs_enabled: Default::default(),
|
devs_enabled: Default::default(),
|
||||||
mapped: Default::default(),
|
mapped: Default::default(),
|
||||||
prev_latch_write: Default::default(),
|
prev_latch_write: Default::default(),
|
||||||
|
rtc: Default::default(),
|
||||||
|
rtc_latch: Default::default(),
|
||||||
has_battery: true,
|
has_battery: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,6 +472,7 @@ impl Savable for MBC3 {
|
||||||
impl MBCIo for MBC3 {
|
impl MBCIo for MBC3 {
|
||||||
fn handle_read(&self, addr: u16) -> MBCResult {
|
fn handle_read(&self, addr: u16) -> MBCResult {
|
||||||
use MBCResult::*;
|
use MBCResult::*;
|
||||||
|
use RtcRegister::*;
|
||||||
|
|
||||||
let res = match addr {
|
let res = match addr {
|
||||||
0x0000..=0x3FFF => Address(addr as usize),
|
0x0000..=0x3FFF => Address(addr as usize),
|
||||||
|
@ -356,9 +481,18 @@ impl MBCIo for MBC3 {
|
||||||
Some(MBC3Device::ExternalRam) if self.devs_enabled => {
|
Some(MBC3Device::ExternalRam) if self.devs_enabled => {
|
||||||
Value(self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)])
|
Value(self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)])
|
||||||
}
|
}
|
||||||
Some(MBC3Device::RealTimeClock) if self.devs_enabled => {
|
Some(MBC3Device::Clock(reg)) if self.devs_enabled => Value(
|
||||||
todo!("Return Latched value of register")
|
self.rtc_latch
|
||||||
}
|
.as_ref()
|
||||||
|
.map(|rtc| match reg {
|
||||||
|
Second => rtc.sec,
|
||||||
|
Minute => rtc.min,
|
||||||
|
Hour => rtc.hr,
|
||||||
|
DayLow => rtc.day_low,
|
||||||
|
DayHigh => rtc.day_high.into(),
|
||||||
|
})
|
||||||
|
.unwrap_or(0xFF),
|
||||||
|
),
|
||||||
_ => Value(0xFF),
|
_ => Value(0xFF),
|
||||||
},
|
},
|
||||||
_ => unreachable!("A read from {:#06X} should not be handled by MBC3", addr),
|
_ => unreachable!("A read from {:#06X} should not be handled by MBC3", addr),
|
||||||
|
@ -368,6 +502,8 @@ impl MBCIo for MBC3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_write(&mut self, addr: u16, byte: u8) {
|
fn handle_write(&mut self, addr: u16, byte: u8) {
|
||||||
|
use RtcRegister::*;
|
||||||
|
|
||||||
match addr {
|
match addr {
|
||||||
0x000..=0x1FFF => self.devs_enabled = (byte & 0x0F) == 0x0A, // Enable External RAM and Access to RTC if there is one
|
0x000..=0x1FFF => self.devs_enabled = (byte & 0x0F) == 0x0A, // Enable External RAM and Access to RTC if there is one
|
||||||
0x2000..=0x3FFF => {
|
0x2000..=0x3FFF => {
|
||||||
|
@ -381,15 +517,18 @@ impl MBCIo for MBC3 {
|
||||||
self.ram_bank = byte & 0x03;
|
self.ram_bank = byte & 0x03;
|
||||||
self.mapped = Some(MBC3Device::ExternalRam);
|
self.mapped = Some(MBC3Device::ExternalRam);
|
||||||
}
|
}
|
||||||
0x08 | 0x09 | 0x0A | 0x0B | 0x0C => {
|
0x08 => self.mapped = Some(MBC3Device::Clock(Second)),
|
||||||
self.mapped = Some(MBC3Device::RealTimeClock);
|
0x09 => self.mapped = Some(MBC3Device::Clock(Minute)),
|
||||||
}
|
0x0A => self.mapped = Some(MBC3Device::Clock(Hour)),
|
||||||
|
0x0B => self.mapped = Some(MBC3Device::Clock(DayLow)),
|
||||||
|
0x0C => self.mapped = Some(MBC3Device::Clock(DayHigh)),
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
0x6000..=0x7FFF => {
|
0x6000..=0x7FFF => {
|
||||||
if let Some(0x00) = self.prev_latch_write {
|
if let Some(0x00) = self.prev_latch_write {
|
||||||
if byte == 0x01 {
|
if byte == 0x01 {
|
||||||
todo!("Perform Data Latch")
|
self.rtc_latch = Some(self.rtc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.prev_latch_write = Some(byte);
|
self.prev_latch_write = Some(byte);
|
||||||
|
@ -398,9 +537,13 @@ impl MBCIo for MBC3 {
|
||||||
Some(MBC3Device::ExternalRam) if self.devs_enabled => {
|
Some(MBC3Device::ExternalRam) if self.devs_enabled => {
|
||||||
self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)] = byte
|
self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)] = byte
|
||||||
}
|
}
|
||||||
Some(MBC3Device::RealTimeClock) if self.devs_enabled => {
|
Some(MBC3Device::Clock(rtc_reg)) if self.devs_enabled => match rtc_reg {
|
||||||
todo!("Write to RTC")
|
Second => self.rtc.sec = byte & 0x3F,
|
||||||
}
|
Minute => self.rtc.min = byte & 0x3F,
|
||||||
|
Hour => self.rtc.hr = byte & 0x1F,
|
||||||
|
DayLow => self.rtc.day_low = byte & 0xFF,
|
||||||
|
DayHigh => self.rtc.day_high = (byte & 0xC1).into(),
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => unreachable!("A write to {:#06X} should not be handled by MBC3", addr),
|
_ => unreachable!("A write to {:#06X} should not be handled by MBC3", addr),
|
||||||
|
@ -408,6 +551,12 @@ impl MBCIo for MBC3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RtClockTick for MBC3 {
|
||||||
|
fn tick(&mut self) {
|
||||||
|
self.rtc.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MBC5 {
|
struct MBC5 {
|
||||||
/// 9-bit number
|
/// 9-bit number
|
||||||
|
@ -495,6 +644,10 @@ impl MBCIo for MBC5 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RtClockTick for MBC5 {
|
||||||
|
fn tick(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MBC2 {
|
struct MBC2 {
|
||||||
/// 4-bit number
|
/// 4-bit number
|
||||||
|
@ -583,6 +736,10 @@ impl MBCIo for MBC2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RtClockTick for MBC2 {
|
||||||
|
fn tick(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct NoMBC;
|
struct NoMBC;
|
||||||
|
|
||||||
|
@ -606,7 +763,11 @@ impl MBCIo for NoMBC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait MBCIo: Savable {
|
impl RtClockTick for NoMBC {
|
||||||
|
fn tick(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MBCIo: Savable + RtClockTick {
|
||||||
fn handle_read(&self, addr: u16) -> MBCResult;
|
fn handle_read(&self, addr: u16) -> MBCResult;
|
||||||
fn handle_write(&mut self, addr: u16, byte: u8);
|
fn handle_write(&mut self, addr: u16, byte: u8);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue