Compare commits

...

6 Commits

4 changed files with 225 additions and 62 deletions

View File

@ -1,7 +1,7 @@
[package] [package]
name = "rmg-001" name = "rmg-001"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,40 +1,38 @@
use crate::utils::{join_bytes}; use crate::utils::{join_bytes};
use crate::rom::ROM; use crate::rom::ROM;
pub enum MemoryMap { pub struct AddressRange {
BankZero, begin: u16,
BankSwitchable, end: u16,
VideoRAM,
ExternalRAM,
WorkRAM1,
WorkRAM2,
EchoRAM,
SpriteAttributeTable,
NotUsable,
IORegisters,
HighRAM,
InterruptEnableRegister,
} }
impl MemoryMap { impl AddressRange {
pub fn get_map(address: u16) -> Self { pub fn begin(&self) -> u16 {
match address { self.begin
0x0000..=0x3FFF => Self::BankZero, }
0x4000..=0x7FFF => Self::BankSwitchable,
0x8000..=0x9FFF => Self::VideoRAM, pub fn end(&self) -> u16 {
0xA000..=0xBFFF => Self::ExternalRAM, self.end
0xC000..=0xCFFF => Self::WorkRAM1, }
0xD000..=0xDFFF => Self::WorkRAM2,
0xE000..=0xFDFF => Self::EchoRAM, // Mirror of C000~DDFF pub fn in_range(&self, address: u16) -> bool {
0xFE00..=0xFE9F => Self::SpriteAttributeTable, address >= self.begin && address <= self.end
0xFEA0..=0xFEFF => Self::NotUsable,
0xFF00..=0xFF7F => Self::IORegisters,
0xFF80..=0xFFFE => Self::HighRAM,
0xFFFF => Self::InterruptEnableRegister,
}
} }
} }
pub const BANK_ZERO: AddressRange = AddressRange{begin: 0x0000, end: 0x3FFF};
pub const BANK_SWITCHABLE: AddressRange = AddressRange{begin: 0x4000, end: 0x7FFF};
pub const VIDEO_RAM: AddressRange = AddressRange{begin: 0x8000, end: 0x9FFF};
pub const EXTERNAL_RAM: AddressRange = AddressRange{begin: 0xA000, end: 0xBFFF};
pub const WORK_RAM_1: AddressRange = AddressRange{begin: 0xC000, end: 0xCFFF};
pub const WORK_RAM_2: AddressRange = AddressRange{begin: 0xD000, end: 0xDFFF};
pub const ECHO_RAM: AddressRange = AddressRange{begin: 0xE000, end: 0xFDFF};
pub const SPRITE_ATTRIBUTE_TABLE: AddressRange = AddressRange{begin: 0xFE00, end: 0xFE9F};
pub const NOT_USABLE: AddressRange = AddressRange{begin: 0xFEA0, end: 0xFEFF};
pub const IO_REGISTERS: AddressRange = AddressRange{begin: 0xFF00, end: 0xFF7F};
pub const HIGH_RAM: AddressRange = AddressRange{begin: 0xFF80, end: 0xFFFE};
pub const INTERRUPT_ENABLE_REGISTER: AddressRange = AddressRange{begin: 0xFFFF, end: 0xFFFF};
pub struct Bus { pub struct Bus {
game_rom: ROM, game_rom: ROM,
data: [u8; 0x10000], data: [u8; 0x10000],
@ -62,49 +60,45 @@ impl Bus {
} }
pub fn read(&self, address: u16) -> u8 { pub fn read(&self, address: u16) -> u8 {
match MemoryMap::get_map(address) { if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
MemoryMap::BankZero => self.game_rom.read(address), return self.game_rom.read(address);
MemoryMap::BankSwitchable => self.game_rom.read(address), } else if IO_REGISTERS.in_range(address) {
MemoryMap::WorkRAM1 | MemoryMap::WorkRAM2 | MemoryMap::InterruptEnableRegister => self.data[address as usize], return match address {
MemoryMap::IORegisters => match address {
0xFF44 => 0x90, 0xFF44 => 0x90,
0xFF4D => 0xFF, 0xFF4D => 0xFF,
_ => self.data[address as usize], _ => self.data[address as usize],
} }
_ => self.data[address as usize],
} }
self.data[address as usize]
} }
pub fn read_16bit(&self, address: u16) -> u16 { pub fn read_16bit(&self, address: u16) -> u16 {
join_bytes(self.read(address + 1), self.read(address)) join_bytes(self.read(address.wrapping_add(1)), self.read(address))
} }
pub fn write(&mut self, address: u16, data: u8) { pub fn write(&mut self, address: u16, data: u8) {
if address == 0xFF01 { if address == 0xFF01 {
print!("{}", data as char); print!("{}", data as char);
} }
match MemoryMap::get_map(address) {
MemoryMap::BankZero | MemoryMap::BankSwitchable => { if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
// println!("WRITING TO ROM"); println!("WRITING TO ROM");
}, } else if WORK_RAM_1.in_range(address) || WORK_RAM_2.in_range(address) {
MemoryMap::WorkRAM1 | MemoryMap::WorkRAM2 => { self.data[address as usize] = data;
self.data[address as usize] = data; // Copy to the ECHO RAM
// Copy to the ECHO RAM if address <= 0xDDFF {
if address <= 0xDDFF { self.data[(ECHO_RAM.begin() + (address - WORK_RAM_1.begin())) as usize] = data;
// self.data[(0xE000 + (address - 0xC000)) as usize] = data; }
} } else if ECHO_RAM.in_range(address) {
}, self.data[address as usize] = data;
MemoryMap::EchoRAM => { self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM
self.data[address as usize] = data; }
// self.data[(0xC000 + (address - 0xE000)) as usize] = data; // Copy to the working RAM self.data[address as usize] = data;
},
_ => self.data[address as usize] = data,
};
} }
pub fn write_16bit(&mut self, address: u16, data: u16) { pub fn write_16bit(&mut self, address: u16, data: u16) {
let bytes = data.to_le_bytes(); let bytes = data.to_le_bytes();
self.write(address, bytes[0]); self.write(address, bytes[0]);
self.write(address + 1, bytes[1]); self.write(address.wrapping_add(1), bytes[1]);
} }
} }

View File

@ -227,9 +227,9 @@ impl OpcodeParameterBytes {
pub fn from_address(address: u16, bus: &Bus)-> OpcodeParameterBytes { pub fn from_address(address: u16, bus: &Bus)-> OpcodeParameterBytes {
OpcodeParameterBytes( OpcodeParameterBytes(
bus.read(address), bus.read(address),
bus.read(address + 1), bus.read(address.wrapping_add(1)),
bus.read(address + 2), bus.read(address.wrapping_add(2)),
bus.read(address + 3), bus.read(address.wrapping_add(3)),
) )
} }
@ -872,7 +872,7 @@ impl CPU {
// self.log(parameter_bytes); // self.log(parameter_bytes);
self.increment_cycles(cycles); self.increment_cycles(cycles);
self.exec(opcode, bus); self.exec(opcode, bus);
self.increment_exec_calls_count(); // self.increment_exec_calls_count();
} }
pub fn exec(&mut self, opcode: Opcode, bus: &mut Bus) { pub fn exec(&mut self, opcode: Opcode, bus: &mut Bus) {
@ -1716,7 +1716,6 @@ impl CPU {
self.registers.increment(Register::PC, 2); self.registers.increment(Register::PC, 2);
}, },
Opcode::NOP => self.registers.increment(Register::PC, 1), Opcode::NOP => self.registers.increment(Register::PC, 1),
// _ => println!("Illegal instruction"),
Opcode::IllegalInstruction => {panic!("Illegal instruction");}, Opcode::IllegalInstruction => {panic!("Illegal instruction");},
_ => {panic!("Illegal instruction");}, _ => {panic!("Illegal instruction");},
}; };

View File

@ -1,7 +1,177 @@
pub struct PPU; use crate::utils::{
BitIndex,
get_bit,
set_bit,
};
use crate::bus::{Bus, BANK_ZERO};
struct ColorPalette(u8, u8, u8, u8);
struct Tile {
}
struct Sprite {
}
pub enum LCDControl {
DisplayEnable,
WindowTileMapAddress,
WindowEnable,
BackgroundWindowTileAddress,
BackgroundTileMapAddress,
ObjectSize,
ObjectEnable,
BackgroundPriority,
}
pub enum LCDStatusModeFlag {
HBlank,
VBlank,
SearchingOAM,
TransferringToLCD,
}
pub enum LCDStatus {
LYCInterrupt,
Mode2OAMInterrupt,
Mode1VBlankInterrupt,
Mode0HBlankInterrupt,
LYCFlag,
ModeFlag(LCDStatusModeFlag),
}
const LCD_CONTROL_ADDRESS: u16 = 0xFF40;
const LCD_STATUS_ADDRESS: u16 = 0xFF41;
const SCROLL_X_ADDRESS: u16 = 0xFF42;
const SCROLL_Y_ADDRESS: u16 = 0xFF43;
const LCD_Y_ADDRESS: u16 = 0xFF44;
const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45;
const DMA_ADDRESS: u16 = 0xFF46;
const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
const WINDOW_X_ADDRESS: u16 = 0xFF4A;
const WINDOW_Y_ADDRESS: u16 = 0xFF4B;
pub struct Window {}
impl Window {
pub fn new() -> Self {
Self {}
}
fn get_x(bus: &Bus) -> u8 {
bus.read(WINDOW_X_ADDRESS)
}
fn set_x(bus: &mut Bus, val: u8) {
bus.write(WINDOW_X_ADDRESS, val);
}
fn get_y(bus: &Bus) -> u8 {
bus.read(WINDOW_Y_ADDRESS)
}
fn set_y(bus: &mut Bus, val: u8) {
bus.write(WINDOW_Y_ADDRESS, val);
}
}
pub struct PPU {
window: Window,
}
impl PPU { impl PPU {
pub fn new() -> Self { pub fn new() -> Self {
Self{} Self {
window: Window::new(),
}
}
fn get_sprite(address: u16) {
}
fn get_scroll_x(bus: &Bus) -> u8 {
bus.read(SCROLL_X_ADDRESS)
}
fn set_scroll_x(bus: &mut Bus, val: u8) {
bus.write(SCROLL_X_ADDRESS, val);
}
fn get_scroll_y(bus: &Bus) -> u8 {
bus.read(SCROLL_Y_ADDRESS)
}
fn set_scroll_y(bus: &mut Bus, val: u8) {
bus.write(SCROLL_Y_ADDRESS, val);
}
fn get_lcd_control(bus: &Bus, control: LCDControl) -> bool {
let byte = bus.read(LCD_CONTROL_ADDRESS);
match control {
LCDControl::DisplayEnable => get_bit(byte, BitIndex::I7),
LCDControl::WindowTileMapAddress => get_bit(byte, BitIndex::I6),
LCDControl::WindowEnable => get_bit(byte, BitIndex::I5),
LCDControl::BackgroundWindowTileAddress => get_bit(byte, BitIndex::I4),
LCDControl::BackgroundTileMapAddress => get_bit(byte, BitIndex::I3),
LCDControl::ObjectSize => get_bit(byte, BitIndex::I2),
LCDControl::ObjectEnable => get_bit(byte, BitIndex::I1),
LCDControl::BackgroundPriority => get_bit(byte, BitIndex::I0),
}
}
fn set_lcd_control(bus: &mut Bus, control: LCDControl, val: bool) {
let mut byte = bus.read(LCD_CONTROL_ADDRESS);
byte = match control {
LCDControl::DisplayEnable => set_bit(byte, val, BitIndex::I7),
LCDControl::WindowTileMapAddress => set_bit(byte, val, BitIndex::I6),
LCDControl::WindowEnable => set_bit(byte, val, BitIndex::I5),
LCDControl::BackgroundWindowTileAddress => set_bit(byte, val, BitIndex::I4),
LCDControl::BackgroundTileMapAddress => set_bit(byte, val, BitIndex::I3),
LCDControl::ObjectSize => set_bit(byte, val, BitIndex::I2),
LCDControl::ObjectEnable => set_bit(byte, val, BitIndex::I1),
LCDControl::BackgroundPriority => set_bit(byte, val, BitIndex::I0),
};
bus.write(LCD_CONTROL_ADDRESS, byte);
}
fn get_lcd_status(bus: &Bus, status: LCDStatus) -> bool {
let byte = bus.read(LCD_STATUS_ADDRESS);
match status {
LCDStatus::LYCInterrupt => get_bit(byte, BitIndex::I6),
LCDStatus::Mode2OAMInterrupt => get_bit(byte, BitIndex::I5),
LCDStatus::Mode1VBlankInterrupt => get_bit(byte, BitIndex::I4),
LCDStatus::Mode0HBlankInterrupt => get_bit(byte, BitIndex::I3),
LCDStatus::LYCFlag => get_bit(byte, BitIndex::I2),
LCDStatus::ModeFlag(mode) => match mode {
LCDStatusModeFlag::HBlank => (byte & 0b00000011) == 0,
LCDStatusModeFlag::VBlank => (byte & 0b00000011) == 1,
LCDStatusModeFlag::SearchingOAM => (byte & 0b00000011) == 2,
LCDStatusModeFlag::TransferringToLCD => (byte & 0b00000011) == 3,
},
}
}
fn set_lcd_status(bus: &mut Bus, status: LCDStatus, val: bool) {
let mut byte = bus.read(LCD_STATUS_ADDRESS);
byte = match status {
LCDStatus::LYCInterrupt => set_bit(byte, val, BitIndex::I6),
LCDStatus::Mode2OAMInterrupt => set_bit(byte, val, BitIndex::I5),
LCDStatus::Mode1VBlankInterrupt => set_bit(byte, val, BitIndex::I4),
LCDStatus::Mode0HBlankInterrupt => set_bit(byte, val, BitIndex::I3),
LCDStatus::LYCFlag => set_bit(byte, val, BitIndex::I2),
LCDStatus::ModeFlag(mode) => match mode {
LCDStatusModeFlag::HBlank => (byte & 0b11111100) | 0,
LCDStatusModeFlag::VBlank => (byte & 0b11111100) | 1,
LCDStatusModeFlag::SearchingOAM => (byte & 0b11111100) | 2,
LCDStatusModeFlag::TransferringToLCD => (byte & 0b11111100) | 3,
},
};
bus.write(LCD_STATUS_ADDRESS, byte);
} }
} }