fixed bug with mmc1, working on mmc3

This commit is contained in:
Theron Spiegl 2020-01-16 20:57:17 -06:00
parent 10370a0f16
commit 3e598ec9e6
8 changed files with 215 additions and 75 deletions

View File

@ -36,13 +36,10 @@ impl Mapper for Cnrom {
fn get_mirroring(&mut self) -> Mirror {
self.cart.mirroring
// if self.cart.four_screen_vram {
// Mirror::FourScreen
// } else {
// self.cart.mirroring
// }
}
fn load_battery_backed_ram(&mut self) {}
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
}

View File

@ -113,18 +113,22 @@ impl Mmc1 {
impl Mapper for Mmc1 {
fn read(&mut self, address: usize) -> u8 {
match address {
0x0000..=0x0FFF => {
if self.cart.chr_rom_size > self.chr_low_bank {
self.cart.chr_rom[self.chr_low_bank][address % 0x1000]
0x0000..=0x1FFF => {
let offset = address % 0x1000;
if self.chr_bank_mode {
// if 4K bank mode, $0000-$0FFF will be a 4K-indexed section of some CHR-ROM chunk referred to by chr_low_bank
// and $1000-$1FFF will be the one referred to by chr_high_bank
let bank = match address {
0x0000..=0x0FFF => self.chr_low_bank,
0x1000..=0x1FFF => self.chr_high_bank,
_ => panic!("bad address read from MMC1: 0x{:X}", address),
};
let chunk_num = bank / 2;
let chunk_half = if bank % 2 == 0 {0x0} else {0x1000};
self.cart.chr_rom[chunk_num][chunk_half + offset]
} else {
self.chr_ram_bank[address]
}
},
0x1000..=0x1FFF => {
if self.cart.chr_rom_size > self.chr_high_bank {
self.cart.chr_rom[self.chr_high_bank][address % 0x1000]
} else {
self.chr_ram_bank[address]
// if we're in 8K bank mode, the whole $0000-$1FFF region will be the 8K range referred to by chr_low_bank
self.cart.chr_rom[self.chr_low_bank][address]
}
},
0x6000..=0x7FFF => self.prg_ram_bank[address % 0x2000],
@ -197,4 +201,7 @@ impl Mapper for Mmc1 {
.expect("could not create output file for battery-backed RAM");
f.write_all(&self.prg_ram_bank).expect("could not write battery-backed RAM to file");
}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
}

View File

@ -2,95 +2,199 @@ use super::{Cartridge, Mapper, Mirror};
pub struct Mmc3 {
cart: Cartridge,
mirroring: Mirror,
reg0: u8,
reg1: u8,
reg2: u8,
reg3: u8,
reg4: u8,
reg5: u8,
reg6: u8,
reg7: u8,
bank_registers: Vec<usize>,
next_bank: u8,
irq_counter: u8,
irq_latch: u8,
irq_counter: u8,
irq_enable: bool,
trigger_irq: bool, // signal to send to CPU
prg_ram_bank: Vec<u8>, // CPU $6000-$7FFF
// 0: $8000-$9FFF swappable, $C000-$DFFF fixed to second-last bank
// 1: $C000-$DFFF swappable, $8000-$9FFF fixed to second-last bank
prg_rom_bank_mode: bool,
// 0: two 2 KB banks at $0000-$0FFF, four 1 KB banks at $1000-$1FFF
// 1: two 2 KB banks at $1000-$1FFF, four 1 KB banks at $0000-$0FFF
chr_rom_bank_mode: bool,
}
impl Mmc3 {
pub fn new(cart: Cartridge) -> Self {
let m = cart.mirroring;
// let num_banks = cart.prg_rom_size
Mmc3{
cart: cart,
reg0: 0,
reg1: 0,
reg2: 0,
reg3: 0,
reg4: 0,
reg5: 0,
reg6: 0,
reg7: 0,
mirroring: m,
bank_registers: vec![0, 0, 0, 0, 0, 0, 0, 0],
next_bank: 0,
irq_latch: 0,
irq_counter: 0,
irq_enable: false,
trigger_irq: false,
prg_ram_bank: vec![0; 0x2000],
prg_rom_bank_mode: false,
chr_rom_bank_mode: false,
}
}
}
impl Mmc3 {
fn bank_select(&mut self, value: u8) {
self.next_bank = value & 0b111;
// ?? = value & (1<<5); // Nothing on the MMC3, see MMC6
self.prg_rom_bank_mode = value & (1<<6) != 0;
self.chr_rom_bank_mode = value & (1<<7) != 0;
}
fn bank_data(&mut self, value: u8) {
// R6 and R7 will ignore the top two bits, as the MMC3 has only 6 PRG ROM address lines.
// R0 and R1 ignore the bottom bit, as the value written still counts banks in 1KB units but odd numbered banks can't be selected.
self.bank_registers[self.next_bank as usize] = match self.next_bank {
0 | 1 => value & 0b0011_1111,
6 | 7 => value & 0b1111_1110,
_ => value,
} as usize;
}
fn prg_ram_protect(&mut self) {}
}
impl Mapper for Mmc3 {
fn read(&mut self, address: usize) -> u8 {
match address {
0x0000..=0x07FF => ,
0x0800..=0x0FFF => ,
0x1000..=0x13FF => ,
0x1400..=0x17FF => ,
0x1800..=0x1BFF => ,
0x1C00..=0x1FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
}
let val = match address {
0x0000..=0x1FFF => { // reading from CHR-ROM
let offset_1k = address % 0x400;
let offset_2k = address % 0x800;
let bank_reg_num = match self.chr_rom_bank_mode {
// look at the value in the bank reg, then depending whether that's a
true => {
match address {
0x0000..=0x03FF => 2,
0x0400..=0x07FF => 3,
0x0800..=0x0BFF => 4,
0x0C00..=0x0FFF => 5,
0x1000..=0x17FF => 0,
0x1800..=0x1FFF => 1,
_ => panic!("oh no"),
}
},
false => {
match address {
0x0000..=0x07FF => 0,
0x0800..=0x0FFF => 1,
0x1000..=0x13FF => 2,
0x1400..=0x17FF => 3,
0x1800..=0x1BFF => 4,
0x1C00..=0x1FFF => 5,
_ => panic!("oh no"),
}
},
};
let bank_num = self.bank_registers[bank_reg_num];
if bank_reg_num == 0 || bank_reg_num == 1 { // dealing with 2K banks of 8K chunks
let chunk_num = bank_num / 4;
let chunk_quarter = (bank_num % 4) * 0x800;
self.cart.chr_rom[chunk_num][chunk_quarter + offset_2k]
} else { // dealing with 1K banks of 8K chunks
let chunk_num = bank_num / 8;
let chunk_eighth = (bank_num % 8) * 0x400;
self.cart.chr_rom[chunk_num][chunk_eighth + offset_1k]
}
},
0x6000..=0x7FFF => self.prg_ram_bank[address % 0x2000], // PRG-RAM
0x8000..=0xFFFF => { // reading from PRG ROM, dealing with 8K banks of 16K chunks
let offset_8k = address % 0x2000;
let num_banks = self.cart.prg_rom_size * 2;
let bank_num = match self.chr_rom_bank_mode {
true => {
match address {
0x8000..=0x9FFF => num_banks - 2,
0xA000..=0xBFFF => self.bank_registers[7],
0xC000..=0xDFFF => self.bank_registers[6],
0xE000..=0xFFFF => num_banks - 1,
_ => panic!("oh no"),
}
},
false => {
match address {
0x8000..=0x9FFF => self.bank_registers[6],
0xA000..=0xBFFF => self.bank_registers[7],
0xC000..=0xDFFF => num_banks - 2,
0xE000..=0xFFFF => num_banks - 1,
_ => panic!("oh no"),
}
},
};
let chunk_num = bank_num / 2;
let chunk_half = (bank_num % 2) * 0x1000;
self.cart.prg_rom[chunk_num][chunk_half + offset_8k]
},
_ => {
println!("bad address read from MMC3: 0x{:X}", address);
0
},
};
println!("reading 0x{:X} from 0x{:X}", val, address);
val
}
fn write(&mut self, address: usize, value: u8) {
match address % 2 == 0 {
true => { // even
match address {
0x8000..=0x9FFF => self.bank_select(address),
0x6000..=0x7FFF => self.prg_ram_bank[address % 0x2000] = value, // PRG-RAM
0x8000..=0x9FFF => self.bank_select(value),
0xA000..=0xBFFF => self.mirroring = if value & 1 == 0 {Mirror::Vertical} else {Mirror::Horizontal},
0xC000..=0xDFFF => ,
0x1400..=0x17FF => ,
0x1800..=0x1BFF => ,
0x1C00..=0x1FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0xC000..=0xDFFF => self.irq_latch = value,
0xE000..=0xFFFF => self.irq_enable = false,
_ => panic!("oh no"),
}
},
false => { // odd
match address {
0x6000..=0x7FFF => self.prg_ram_bank[address % 0x2000] = value, // PRG-RAM
0x8000..=0x9FFF => self.bank_data(value),
0xA000..=0xBFFF => self.prg_ram_protect(value),
0xC000..=0xDFFF => ,
0x1400..=0x17FF => ,
0x1800..=0x1BFF => ,
0x1C00..=0x1FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0x6000..=0x7FFF => ,
0xA000..=0xBFFF => self.prg_ram_protect(),
0xC000..=0xDFFF => self.irq_counter = 0, // Writing any value to this register reloads the MMC3 IRQ counter at the NEXT rising edge of the PPU address, presumably at PPU cycle 260 of the current scanline.
0xE000..=0xFFFF => self.irq_enable = true,
_ => panic!("oh no"),
}
},
}
}
fn get_mirroring(&mut self) -> Mirror {}
fn get_mirroring(&mut self) -> Mirror {
if self.cart.four_screen_vram {
Mirror::FourScreen
} else {
self.mirroring
}
}
fn load_battery_backed_ram(&mut self) {}
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {
if self.irq_counter == 0 {
self.irq_counter = self.irq_latch;
} else {
self.irq_counter -= 1;
}
if self.irq_counter == 0 && self.irq_enable {
self.trigger_irq = true;
}
}
fn check_irq(&mut self) -> bool {
if self.trigger_irq {
self.trigger_irq = false;
true
} else {
false
}
}
}

View File

@ -2,13 +2,13 @@ mod nrom;
mod mmc1;
mod uxrom;
mod cnrom;
// mod mmc3;
mod mmc3;
use nrom::Nrom;
use mmc1::Mmc1;
use uxrom::Uxrom;
use cnrom::Cnrom;
// use mmc3::Mmc3;
use mmc3::Mmc3;
use std::cell::RefCell;
use std::rc::Rc;
@ -20,6 +20,8 @@ pub trait Mapper {
fn get_mirroring(&mut self) -> Mirror;
fn load_battery_backed_ram(&mut self);
fn save_battery_backed_ram(&self);
fn clock(&mut self);
fn check_irq(&mut self) -> bool;
}
#[derive(Copy, Clone, Debug, PartialEq)]
@ -39,7 +41,7 @@ pub fn get_mapper() -> Rc<RefCell<dyn Mapper>> {
1 => Rc::new(RefCell::new(Mmc1::new(cart))),
2 => Rc::new(RefCell::new(Uxrom::new(cart))),
3 => Rc::new(RefCell::new(Cnrom::new(cart))),
// 4 => Rc::new(RefCell::new(Mmc3::new(cart))),
4 => Rc::new(RefCell::new(Mmc3::new(cart))),
_ => panic!("unimplemented mapper: {}", num),
}
}

View File

@ -57,4 +57,6 @@ impl Mapper for Nrom {
fn load_battery_backed_ram(&mut self) {}
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
}

View File

@ -50,4 +50,6 @@ impl Mapper for Uxrom {
fn load_battery_backed_ram(&mut self) {}
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
}

View File

@ -144,15 +144,20 @@ impl Cpu {
return 1;
}
// handle interrupts
// handle interrupts from ppu
if self.ppu.trigger_nmi {
self.nmi();
}
self.ppu.trigger_nmi = false;
// and apu
if self.apu.trigger_irq && (self.P & INTERRUPT_DISABLE_FLAG == 0) {
self.irq();
}
self.apu.trigger_irq = false;
// and mapper MMC3
if self.mapper.borrow_mut().check_irq() {
self.irq();
}
// back up clock so we know how many cycles we complete
let clock = self.clock;

View File

@ -241,6 +241,27 @@ impl Ppu {
} else {
self.line_cycle += 1;
}
// deal with mapper MMC3
if self.rendering() && (
self.line_cycle == 260
&& self.sprite_size == 8
&& self.background_pattern_table_base == 0x0000
&& self.sprite_pattern_table_base == 0x1000
) || (
self.line_cycle == 324
&& self.sprite_size == 8
&& self.background_pattern_table_base == 0x1000
&& self.sprite_pattern_table_base == 0x0000
) || (
self.line_cycle == 260
&& self.sprite_size == 16
// TODO: figure out exact conditions here
)
{
self.mapper.borrow_mut().clock()
}
(pixel, end_of_frame)
}
}