fixed bug with mmc1, working on mmc3
This commit is contained in:
parent
10370a0f16
commit
3e598ec9e6
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue