fix: squash several bugs in MBC1 implementation
This commit is contained in:
		
							
								
								
									
										141
									
								
								src/cartridge.rs
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								src/cartridge.rs
									
									
									
									
									
								
							@@ -90,8 +90,8 @@ impl Cartridge {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Default)]
 | 
					#[derive(Debug, Clone, Default)]
 | 
				
			||||||
struct MBC1 {
 | 
					struct MBC1 {
 | 
				
			||||||
    current_rom_bank: u8, // 5-bit Number
 | 
					    rom_bank: u8, // 5-bit Number
 | 
				
			||||||
    current_ram_bank: u8, // 2-bit number
 | 
					    ram_bank: u8, // 2-bit number
 | 
				
			||||||
    mode: bool,
 | 
					    mode: bool,
 | 
				
			||||||
    ram_size: RamSize,
 | 
					    ram_size: RamSize,
 | 
				
			||||||
    ram: Vec<u8>,
 | 
					    ram: Vec<u8>,
 | 
				
			||||||
@@ -101,30 +101,32 @@ struct MBC1 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl MBC1 {
 | 
					impl MBC1 {
 | 
				
			||||||
    fn calc_zero_bank_number(&self) -> u8 {
 | 
					    fn calc_zero_bank_number(&self) -> u8 {
 | 
				
			||||||
 | 
					        use BankCount::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match self.bank_count {
 | 
					        match self.bank_count {
 | 
				
			||||||
            BankCount::ThirtyTwo | BankCount::Sixteen | BankCount::Eight | BankCount::Four => 0,
 | 
					            ThirtyTwo | Sixteen | Eight | Four => 0,
 | 
				
			||||||
            BankCount::SixtyFour => (self.current_ram_bank & 0x01) << 5,
 | 
					            SixtyFour => (self.ram_bank & 0x01) << 5,
 | 
				
			||||||
            BankCount::OneHundredTwentyEight => self.current_ram_bank << 5,
 | 
					            OneHundredTwentyEight => self.ram_bank << 5,
 | 
				
			||||||
            _ => unreachable!("{#:?} is not a valid ROM Bank Number for MBC1"),
 | 
					            _ => unreachable!("{:#?} is not a valid MBC1 BankCount", self.bank_count),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn calc_high_bank_number(&self) -> u8 {
 | 
					    fn calc_high_bank_number(&self) -> u8 {
 | 
				
			||||||
 | 
					        use BankCount::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match self.bank_count {
 | 
					        match self.bank_count {
 | 
				
			||||||
            BankCount::ThirtyTwo | BankCount::Sixteen | BankCount::Eight | BankCount::Four => {
 | 
					            None | Four | Eight | Sixteen | ThirtyTwo => self.apply_rom_size_bitmask(self.rom_bank),
 | 
				
			||||||
                self.apply_rom_size_bitmask(self.current_rom_bank)
 | 
					            SixtyFour => {
 | 
				
			||||||
            }
 | 
					                let mut num = self.apply_rom_size_bitmask(self.rom_bank);
 | 
				
			||||||
            BankCount::SixtyFour => {
 | 
					 | 
				
			||||||
                let mut num = self.apply_rom_size_bitmask(self.current_rom_bank);
 | 
					 | 
				
			||||||
                num &= !(0x01 << 5);
 | 
					                num &= !(0x01 << 5);
 | 
				
			||||||
                num | ((self.current_ram_bank & 0x01) << 5)
 | 
					                num | ((self.ram_bank & 0x01) << 5)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            BankCount::OneHundredTwentyEight => {
 | 
					            OneHundredTwentyEight => {
 | 
				
			||||||
                let mut num = self.apply_rom_size_bitmask(self.current_rom_bank);
 | 
					                let mut num = self.apply_rom_size_bitmask(self.rom_bank);
 | 
				
			||||||
                num &= !(0x03 << 5);
 | 
					                num &= !(0x03 << 5);
 | 
				
			||||||
                num | ((self.current_ram_bank) << 5)
 | 
					                num | ((self.ram_bank) << 5)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            _ => unreachable!("{#:?} is not a valid ROM Bank Number for MBC1"),
 | 
					            _ => unreachable!("{:#?} is not a valid MBC1 BankCount", self.bank_count),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,7 +140,24 @@ impl MBC1 {
 | 
				
			|||||||
            ThirtyTwo => byte & 0b00011111,
 | 
					            ThirtyTwo => byte & 0b00011111,
 | 
				
			||||||
            SixtyFour => byte & 0b00011111,
 | 
					            SixtyFour => byte & 0b00011111,
 | 
				
			||||||
            OneHundredTwentyEight => byte & 0b00011111,
 | 
					            OneHundredTwentyEight => byte & 0b00011111,
 | 
				
			||||||
            _ => unreachable!("{#:?} is not a valid ROM Bank Number for MBC1"),
 | 
					            _ => unreachable!("{:#?} does not have a bitmask in MBC1", self.bank_count),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn calc_ram_address(&self, addr: u16) -> u16 {
 | 
				
			||||||
 | 
					        match self.ram_size {
 | 
				
			||||||
 | 
					            RamSize::_2KB | RamSize::_8KB => {
 | 
				
			||||||
 | 
					                let ram_size = self.ram_size.to_byte_count() as u16;
 | 
				
			||||||
 | 
					                (addr - 0xA000) % ram_size
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            RamSize::_32KB => {
 | 
				
			||||||
 | 
					                if self.mode {
 | 
				
			||||||
 | 
					                    0x2000 * self.ram_bank as u16 + (addr - 0xA000)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    addr - 0xA000
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => unreachable!("RAM size can not be greater than 32KB on MBC1"),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -150,35 +169,22 @@ impl MemoryBankController for MBC1 {
 | 
				
			|||||||
        match addr {
 | 
					        match addr {
 | 
				
			||||||
            0x0000..=0x3FFF => {
 | 
					            0x0000..=0x3FFF => {
 | 
				
			||||||
                if self.mode {
 | 
					                if self.mode {
 | 
				
			||||||
                    let zero_bank_number = self.calc_zero_bank_number() as u16;
 | 
					                    let zero_bank = self.calc_zero_bank_number() as u16;
 | 
				
			||||||
                    Address(0x4000 * zero_bank_number + addr)
 | 
					                    Address(0x4000 * zero_bank + addr)
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Address(addr)
 | 
					                    Address(addr)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0x4000..=0x7FFF => {
 | 
					            0x4000..=0x7FFF => {
 | 
				
			||||||
                let high_bank_number = self.calc_high_bank_number() as u16;
 | 
					                let high_bank = self.calc_high_bank_number() as u16;
 | 
				
			||||||
                Address(0x4000 * high_bank_number * (addr - 0x4000))
 | 
					                Address(0x4000 * high_bank + (addr - 0x4000))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0xA000..=0xBFFF => {
 | 
					            0xA000..=0xBFFF => {
 | 
				
			||||||
                if self.ram_enabled {
 | 
					                if self.ram_enabled {
 | 
				
			||||||
                    let ram_addr = match self.ram_size {
 | 
					                    let ram_addr = self.calc_ram_address(addr);
 | 
				
			||||||
                        RamSize::_2KB | RamSize::_8KB => {
 | 
					 | 
				
			||||||
                            (addr as u32 - 0xA000) % self.ram_size.to_byte_count()
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        RamSize::_32KB => {
 | 
					 | 
				
			||||||
                            if self.mode {
 | 
					 | 
				
			||||||
                                0x2000 * self.current_ram_bank as u32 + (addr as u32 - 0xA000)
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                addr as u32 - 0xA000
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        _ => unreachable!(""),
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Value(self.ram[ram_addr as usize])
 | 
					                    Value(self.ram[ram_addr as usize])
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Address(0x00FF)
 | 
					                    Value(0xFF)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            _ => unimplemented!(),
 | 
					            _ => unimplemented!(),
 | 
				
			||||||
@@ -189,30 +195,17 @@ impl MemoryBankController for MBC1 {
 | 
				
			|||||||
        match addr {
 | 
					        match addr {
 | 
				
			||||||
            0x0000..=0x1FFF => self.ram_enabled = (byte & 0x0F) == 0x0A,
 | 
					            0x0000..=0x1FFF => self.ram_enabled = (byte & 0x0F) == 0x0A,
 | 
				
			||||||
            0x2000..=0x3FFF => {
 | 
					            0x2000..=0x3FFF => {
 | 
				
			||||||
                self.current_rom_bank = self.apply_rom_size_bitmask(byte);
 | 
					                self.rom_bank = self.apply_rom_size_bitmask(byte);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if self.current_rom_bank == 0 {
 | 
					                if self.rom_bank == 0 {
 | 
				
			||||||
                    self.current_rom_bank = 1;
 | 
					                    self.rom_bank = 1;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0x4000..=0x5FFF => self.current_ram_bank = byte & 0x03,
 | 
					            0x4000..=0x5FFF => self.ram_bank = byte & 0b11,
 | 
				
			||||||
            0x6000..=0x7FFF => self.mode = byte >> 7 == 0x01,
 | 
					            0x6000..=0x7FFF => self.mode = (byte & 0x01) == 0x01,
 | 
				
			||||||
            0xA000..=0xBFFF => {
 | 
					            0xA000..=0xBFFF => {
 | 
				
			||||||
                if self.ram_enabled {
 | 
					                if self.ram_enabled {
 | 
				
			||||||
                    let ram_addr = match self.ram_size {
 | 
					                    let ram_addr = self.calc_ram_address(addr);
 | 
				
			||||||
                        RamSize::_2KB | RamSize::_8KB => {
 | 
					 | 
				
			||||||
                            (addr as u32 - 0xA000) % self.ram_size.to_byte_count()
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        RamSize::_32KB => {
 | 
					 | 
				
			||||||
                            if self.mode {
 | 
					 | 
				
			||||||
                                0x2000 * (self.current_ram_bank as u32) + (addr as u32 - 0xA000)
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                addr as u32 - 0xA000
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        _ => unreachable!("RAM size can not be greater than 32KB on MBC1"),
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    self.ram[ram_addr as usize] = byte;
 | 
					                    self.ram[ram_addr as usize] = byte;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -259,12 +252,12 @@ impl Default for MBCKind {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy)]
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
enum RamSize {
 | 
					enum RamSize {
 | 
				
			||||||
    None = 0,
 | 
					    None = 0x00,
 | 
				
			||||||
    _2KB = 1,
 | 
					    _2KB = 0x01,
 | 
				
			||||||
    _8KB = 2,
 | 
					    _8KB = 0x02,
 | 
				
			||||||
    _32KB = 3,  // Split into 4 RAM banks
 | 
					    _32KB = 0x03,  // Split into 4 RAM banks
 | 
				
			||||||
    _128KB = 4, // Split into 16 RAM banks
 | 
					    _128KB = 0x04, // Split into 16 RAM banks
 | 
				
			||||||
    _64KB = 5,  // Split into 8 RAm Banks
 | 
					    _64KB = 0x05,  // Split into 8 RAm Banks
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl RamSize {
 | 
					impl RamSize {
 | 
				
			||||||
@@ -297,8 +290,8 @@ impl From<u8> for RamSize {
 | 
				
			|||||||
            0x01 => _2KB,
 | 
					            0x01 => _2KB,
 | 
				
			||||||
            0x02 => _8KB,
 | 
					            0x02 => _8KB,
 | 
				
			||||||
            0x03 => _32KB,
 | 
					            0x03 => _32KB,
 | 
				
			||||||
            0x04 => _64KB,
 | 
					            0x04 => _128KB,
 | 
				
			||||||
            0x05 => _128KB,
 | 
					            0x05 => _64KB,
 | 
				
			||||||
            _ => unreachable!("{:#04X} is an invalid value for RAMSize"),
 | 
					            _ => unreachable!("{:#04X} is an invalid value for RAMSize"),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -306,15 +299,15 @@ impl From<u8> for RamSize {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy)]
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
enum BankCount {
 | 
					enum BankCount {
 | 
				
			||||||
    None = 0,                  // 32KB
 | 
					    None = 0x00,                  // 32KB
 | 
				
			||||||
    Four = 1,                  // 64KB
 | 
					    Four = 0x01,                  // 64KB
 | 
				
			||||||
    Eight = 2,                 // 128KB
 | 
					    Eight = 0x02,                 // 128KB
 | 
				
			||||||
    Sixteen = 3,               // 256KB
 | 
					    Sixteen = 0x03,               // 256KB
 | 
				
			||||||
    ThirtyTwo = 4,             // 512KB
 | 
					    ThirtyTwo = 0x04,             // 512KB
 | 
				
			||||||
    SixtyFour = 5,             // 1MB
 | 
					    SixtyFour = 0x05,             // 1MB
 | 
				
			||||||
    OneHundredTwentyEight = 6, // 2MB
 | 
					    OneHundredTwentyEight = 0x06, // 2MB
 | 
				
			||||||
    TwoHundredFiftySix = 7,    // 4MB
 | 
					    TwoHundredFiftySix = 0x07,    // 4MB
 | 
				
			||||||
    FiveHundredTwelve = 8,     // 8MB
 | 
					    FiveHundredTwelve = 0x08,     // 8MB
 | 
				
			||||||
    SeventyTwo = 0x52,            // 1.1MB
 | 
					    SeventyTwo = 0x52,            // 1.1MB
 | 
				
			||||||
    Eighty = 0x53,                // 1.2MB
 | 
					    Eighty = 0x53,                // 1.2MB
 | 
				
			||||||
    NinetySix = 0x54,             // 1.5MB
 | 
					    NinetySix = 0x54,             // 1.5MB
 | 
				
			||||||
@@ -328,7 +321,7 @@ impl Default for BankCount {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl BankCount {
 | 
					impl BankCount {
 | 
				
			||||||
    // https://hacktix.github.io/GBEDG/mbcs/#rom-size
 | 
					    // https://hacktix.github.io/GBEDG/mbcs/#rom-size
 | 
				
			||||||
    pub fn to_byte_count(&self) -> u32 {
 | 
					    pub fn to_rom_size(&self) -> u32 {
 | 
				
			||||||
        use BankCount::*;
 | 
					        use BankCount::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match *self {
 | 
					        match *self {
 | 
				
			||||||
@@ -365,7 +358,7 @@ impl From<u8> for BankCount {
 | 
				
			|||||||
            0x52 => SeventyTwo,
 | 
					            0x52 => SeventyTwo,
 | 
				
			||||||
            0x53 => Eighty,
 | 
					            0x53 => Eighty,
 | 
				
			||||||
            0x54 => NinetySix,
 | 
					            0x54 => NinetySix,
 | 
				
			||||||
            _ => unreachable!("{:#04X} is an invalid value for BankCount"),
 | 
					            _ => unreachable!("{:#04X} is an invalid value for BankCount", byte),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,9 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        None => LR35902::new(),
 | 
					        None => LR35902::new(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // game_boy.load_cartridge("bin/instr_timing.gb");
 | 
				
			||||||
    game_boy.load_cartridge("bin/cpu_instrs.gb");
 | 
					    game_boy.load_cartridge("bin/cpu_instrs.gb");
 | 
				
			||||||
 | 
					    // game_boy.load_cartridge("bin/tetris.gb");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut now = Instant::now();
 | 
					    let mut now = Instant::now();
 | 
				
			||||||
    let mut cycles_in_frame = Cycles::default();
 | 
					    let mut cycles_in_frame = Cycles::default();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,14 +123,16 @@ impl Ppu {
 | 
				
			|||||||
            TileMapAddress::X9C00 => 0x9C00,
 | 
					            TileMapAddress::X9C00 => 0x9C00,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let y_pos = self.pos.line_y as u16 + self.pos.scroll_y as u16;
 | 
					        // let y_pos = self.pos.line_y as u16 + self.pos.scroll_y as u16;
 | 
				
			||||||
 | 
					        let y_pos = self.pos.line_y as u16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // There are always 20 rows of tiles in the LCD Viewport
 | 
					        // There are always 20 rows of tiles in the LCD Viewport
 | 
				
			||||||
        // 160 / 20 = 8, so we can figure out the row of a tile with the following
 | 
					        // 160 / 20 = 8, so we can figure out the row of a tile with the following
 | 
				
			||||||
        let tile_row: u16 = y_pos as u16 / 8;
 | 
					        let tile_row: u16 = y_pos as u16 / 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (line_x, chunk) in scanline.chunks_mut(4).enumerate() {
 | 
					        for (line_x, chunk) in scanline.chunks_mut(4).enumerate() {
 | 
				
			||||||
            let x_pos = line_x as u16 + self.pos.scroll_x as u16;
 | 
					            // let x_pos = line_x as u16 + self.pos.scroll_x as u16;
 | 
				
			||||||
 | 
					            let x_pos = line_x as u16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // There are always 18 columns of tiles in the LCD Viewport
 | 
					            // There are always 18 columns of tiles in the LCD Viewport
 | 
				
			||||||
            // 144 / 18 = 8, so we can figure out the column of a tile with the following
 | 
					            // 144 / 18 = 8, so we can figure out the column of a tile with the following
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user