mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-27 11:31:33 +00:00
Compare commits
No commits in common. "1444e8b63d34599f52d5c4e02659ae4d8ec3a801" and "d2bf668bdc51212b9280695bc0ac65a0a378e84b" have entirely different histories.
1444e8b63d
...
d2bf668bdc
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rmg-001"
|
name = "rmg-001"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
|
90
src/bus.rs
90
src/bus.rs
@ -1,38 +1,40 @@
|
|||||||
use crate::utils::{join_bytes};
|
use crate::utils::{join_bytes};
|
||||||
use crate::rom::ROM;
|
use crate::rom::ROM;
|
||||||
|
|
||||||
pub struct AddressRange {
|
pub enum MemoryMap {
|
||||||
begin: u16,
|
BankZero,
|
||||||
end: u16,
|
BankSwitchable,
|
||||||
|
VideoRAM,
|
||||||
|
ExternalRAM,
|
||||||
|
WorkRAM1,
|
||||||
|
WorkRAM2,
|
||||||
|
EchoRAM,
|
||||||
|
SpriteAttributeTable,
|
||||||
|
NotUsable,
|
||||||
|
IORegisters,
|
||||||
|
HighRAM,
|
||||||
|
InterruptEnableRegister,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddressRange {
|
impl MemoryMap {
|
||||||
pub fn begin(&self) -> u16 {
|
pub fn get_map(address: u16) -> Self {
|
||||||
self.begin
|
match address {
|
||||||
|
0x0000..=0x3FFF => Self::BankZero,
|
||||||
|
0x4000..=0x7FFF => Self::BankSwitchable,
|
||||||
|
0x8000..=0x9FFF => Self::VideoRAM,
|
||||||
|
0xA000..=0xBFFF => Self::ExternalRAM,
|
||||||
|
0xC000..=0xCFFF => Self::WorkRAM1,
|
||||||
|
0xD000..=0xDFFF => Self::WorkRAM2,
|
||||||
|
0xE000..=0xFDFF => Self::EchoRAM, // Mirror of C000~DDFF
|
||||||
|
0xFE00..=0xFE9F => Self::SpriteAttributeTable,
|
||||||
|
0xFEA0..=0xFEFF => Self::NotUsable,
|
||||||
|
0xFF00..=0xFF7F => Self::IORegisters,
|
||||||
|
0xFF80..=0xFFFE => Self::HighRAM,
|
||||||
|
0xFFFF => Self::InterruptEnableRegister,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end(&self) -> u16 {
|
|
||||||
self.end
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn in_range(&self, address: u16) -> bool {
|
|
||||||
address >= self.begin && address <= self.end
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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],
|
||||||
@ -60,45 +62,49 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, address: u16) -> u8 {
|
pub fn read(&self, address: u16) -> u8 {
|
||||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
|
match MemoryMap::get_map(address) {
|
||||||
return self.game_rom.read(address);
|
MemoryMap::BankZero => self.game_rom.read(address),
|
||||||
} else if IO_REGISTERS.in_range(address) {
|
MemoryMap::BankSwitchable => self.game_rom.read(address),
|
||||||
return match address {
|
MemoryMap::WorkRAM1 | MemoryMap::WorkRAM2 | MemoryMap::InterruptEnableRegister => self.data[address as usize],
|
||||||
|
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.wrapping_add(1)), self.read(address))
|
join_bytes(self.read(address + 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) {
|
||||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
|
MemoryMap::BankZero | MemoryMap::BankSwitchable => {
|
||||||
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;
|
|
||||||
self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
MemoryMap::EchoRAM => {
|
||||||
self.data[address as usize] = data;
|
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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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.wrapping_add(1), bytes[1]);
|
self.write(address + 1, bytes[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.wrapping_add(1)),
|
bus.read(address + 1),
|
||||||
bus.read(address.wrapping_add(2)),
|
bus.read(address + 2),
|
||||||
bus.read(address.wrapping_add(3)),
|
bus.read(address + 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,6 +1716,7 @@ 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");},
|
||||||
};
|
};
|
||||||
|
174
src/ppu.rs
174
src/ppu.rs
@ -1,177 +1,7 @@
|
|||||||
use crate::utils::{
|
pub struct PPU;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user