mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
Compare commits
4 Commits
2fc82faef6
...
c68f24ea05
Author | SHA1 | Date | |
---|---|---|---|
c68f24ea05 | |||
66182da336 | |||
a67c7d5bb6 | |||
0ce0c11488 |
15
src/bus.rs
15
src/bus.rs
@ -57,7 +57,10 @@ pub struct Bus {
|
|||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let game_rom = match ROM::load_file("ignore/metroid-2.gb".to_string()) {
|
let game_rom = match ROM::load_file("/home/fran/Development/Personal/Rust/rmg-001/ignore/mario-land.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("ignore/dmg-acid2.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("ignore/mario-land.gb".to_string()) {
|
||||||
|
// let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) {
|
||||||
// let game_rom = match ROM::load_file("ignore/mooneye/emulator-only/mbc1/bits_bank1.gb".to_string()) {
|
// let game_rom = match ROM::load_file("ignore/mooneye/emulator-only/mbc1/bits_bank1.gb".to_string()) {
|
||||||
// let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) {
|
// let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) {
|
||||||
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) {
|
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) {
|
||||||
@ -181,18 +184,8 @@ impl Bus {
|
|||||||
self.write(address.wrapping_add(1), bytes[1]);
|
self.write(address.wrapping_add(1), bytes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_interrupt_enable(&mut self, interrupt: Interrupt, val: bool) {
|
|
||||||
let byte = self.read(INTERRUPT_ENABLE_ADDRESS);
|
|
||||||
self.write(INTERRUPT_ENABLE_ADDRESS, interrupt.set(byte, val));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) {
|
pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) {
|
||||||
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
||||||
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
|
self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_interrupt(&mut self, interrupt: Interrupt) -> bool {
|
|
||||||
let byte = self.read(INTERRUPT_ENABLE_ADDRESS) & self.read(INTERRUPT_FLAG_ADDRESS);
|
|
||||||
interrupt.get(byte)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
17
src/cpu.rs
17
src/cpu.rs
@ -844,6 +844,7 @@ pub struct CPU {
|
|||||||
is_halted: bool,
|
is_halted: bool,
|
||||||
ime: bool, // Interrupt Master Enable
|
ime: bool, // Interrupt Master Enable
|
||||||
ei_delay: bool,
|
ei_delay: bool,
|
||||||
|
enable_logs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
@ -856,6 +857,7 @@ impl CPU {
|
|||||||
is_halted: false,
|
is_halted: false,
|
||||||
ei_delay: false,
|
ei_delay: false,
|
||||||
ime: true,
|
ime: true,
|
||||||
|
enable_logs: !env::var("CPU_LOG").is_err(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -915,22 +917,23 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option<Interrupt> {
|
pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option<Interrupt> {
|
||||||
if (bus.read(INTERRUPT_ENABLE_ADDRESS) & 0b00011111) & (bus.read(INTERRUPT_FLAG_ADDRESS) & 0b00011111) != 0 {
|
let interrupts = (bus.read(INTERRUPT_ENABLE_ADDRESS) & 0b00011111) & (bus.read(INTERRUPT_FLAG_ADDRESS) & 0b00011111);
|
||||||
|
if interrupts != 0 {
|
||||||
self.is_halted = false;
|
self.is_halted = false;
|
||||||
}
|
}
|
||||||
if !self.ime {
|
if !self.ime {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if bus.get_interrupt(Interrupt::VBlank) {
|
if Interrupt::VBlank.get(interrupts) {
|
||||||
return Some(Interrupt::VBlank);
|
return Some(Interrupt::VBlank);
|
||||||
} else if bus.get_interrupt(Interrupt::LCDSTAT) {
|
} else if Interrupt::LCDSTAT.get(interrupts) {
|
||||||
return Some(Interrupt::LCDSTAT);
|
return Some(Interrupt::LCDSTAT);
|
||||||
} else if bus.get_interrupt(Interrupt::Timer) {
|
} else if Interrupt::Timer.get(interrupts) {
|
||||||
return Some(Interrupt::Timer);
|
return Some(Interrupt::Timer);
|
||||||
} else if bus.get_interrupt(Interrupt::Serial) {
|
} else if Interrupt::Serial.get(interrupts) {
|
||||||
return Some(Interrupt::Serial);
|
return Some(Interrupt::Serial);
|
||||||
} else if bus.get_interrupt(Interrupt::Joypad) {
|
} else if Interrupt::Joypad.get(interrupts) {
|
||||||
return Some(Interrupt::Joypad);
|
return Some(Interrupt::Joypad);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -953,7 +956,7 @@ impl CPU {
|
|||||||
let program_counter = self.registers.get(Register::PC);
|
let program_counter = self.registers.get(Register::PC);
|
||||||
let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus);
|
let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus);
|
||||||
let (opcode, cycles) = parameter_bytes.parse_opcode();
|
let (opcode, cycles) = parameter_bytes.parse_opcode();
|
||||||
if !env::var("CPU_LOG").is_err() {
|
if self.enable_logs {
|
||||||
self.log(parameter_bytes);
|
self.log(parameter_bytes);
|
||||||
self.increment_exec_calls_count();
|
self.increment_exec_calls_count();
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ impl Emulator {
|
|||||||
self.timer.reset(&mut self.bus);
|
self.timer.reset(&mut self.bus);
|
||||||
}
|
}
|
||||||
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), frame_buffer);
|
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), frame_buffer);
|
||||||
self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
|
self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t());
|
||||||
|
|
||||||
// 1 CPU cycle = 238.42ns
|
// 1 CPU cycle = 238.42ns
|
||||||
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
// thread::sleep(time::Duration::from_nanos((self.cpu.get_last_op_cycles().0 * 238).try_into().unwrap()));
|
||||||
@ -116,7 +116,7 @@ impl Emulator {
|
|||||||
|
|
||||||
pub fn cpu_loop(&mut self) {
|
pub fn cpu_loop(&mut self) {
|
||||||
let mut exit = false;
|
let mut exit = false;
|
||||||
let mut frame: [u8; 144 * 160] = [0; 144 * 160];
|
let mut frame: [u8; 144 * 160 * 4] = [0; 144 * 160 * 4];
|
||||||
while !exit {
|
while !exit {
|
||||||
self.cpu.run(&mut self.bus);
|
self.cpu.run(&mut self.bus);
|
||||||
if self.bus.reset_timer {
|
if self.bus.reset_timer {
|
||||||
@ -124,7 +124,7 @@ impl Emulator {
|
|||||||
self.timer.reset(&mut self.bus);
|
self.timer.reset(&mut self.bus);
|
||||||
}
|
}
|
||||||
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), &mut frame);
|
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t(), &mut frame);
|
||||||
self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
|
self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles().to_t());
|
||||||
|
|
||||||
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
|
// exit = self.cpu.get_exec_calls_count() >= 1258895; // log 1
|
||||||
exit = self.cpu.get_exec_calls_count() >= 161502; // log 2
|
exit = self.cpu.get_exec_calls_count() >= 161502; // log 2
|
||||||
|
233
src/ppu.rs
233
src/ppu.rs
@ -93,11 +93,12 @@ struct Sprite {
|
|||||||
x: u8,
|
x: u8,
|
||||||
y: u8,
|
y: u8,
|
||||||
tile_number: u8,
|
tile_number: u8,
|
||||||
|
palette: u8,
|
||||||
x_flip: bool,
|
x_flip: bool,
|
||||||
y_flip: bool,
|
y_flip: bool,
|
||||||
over_bg: bool,
|
over_bg: bool,
|
||||||
palette_one: bool,
|
|
||||||
is_long: bool,
|
is_long: bool,
|
||||||
|
bit_pixels: Option<[u8; 8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sprite {
|
impl Sprite {
|
||||||
@ -105,7 +106,7 @@ impl Sprite {
|
|||||||
self.x
|
self.x
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pixel(&self, lcd_x: u8, lcd_y: u8, bus: &Bus, last_bg_index: u8) -> Option<Pixel> {
|
pub fn get_pixel(&mut self, lcd_x: u8, lcd_y: u8, bus: &Bus, last_bg_index: u8) -> Option<Pixel> {
|
||||||
if lcd_x < self.x.saturating_sub(8) || lcd_x >= self.x {
|
if lcd_x < self.x.saturating_sub(8) || lcd_x >= self.x {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -118,10 +119,8 @@ impl Sprite {
|
|||||||
true => 16,
|
true => 16,
|
||||||
false => 8,
|
false => 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
let x = lcd_x.saturating_sub(self.x.saturating_sub(8));
|
let x = lcd_x.saturating_sub(self.x.saturating_sub(8));
|
||||||
let y = lcd_y.saturating_sub(self.y .saturating_sub(16));
|
let y = lcd_y.saturating_sub(self.y .saturating_sub(16));
|
||||||
|
|
||||||
let x = match self.x_flip {
|
let x = match self.x_flip {
|
||||||
true => 7 - x,
|
true => 7 - x,
|
||||||
false => x,
|
false => x,
|
||||||
@ -131,33 +130,38 @@ impl Sprite {
|
|||||||
false => y,
|
false => y,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tile_number = self.tile_number;
|
let bit_pixel_index = (x as usize).rem_euclid(8);
|
||||||
|
|
||||||
if self.is_long && x <= 7 {
|
let bit_pixel = match self.bit_pixels {
|
||||||
tile_number = tile_number & 0xFE;
|
Some(bit_pixels_array) => {
|
||||||
} else if self.is_long && x > 7 {
|
bit_pixels_array[bit_pixel_index]
|
||||||
tile_number = tile_number | 0x01;
|
},
|
||||||
}
|
None => {
|
||||||
|
let mut tile_number = self.tile_number;
|
||||||
|
|
||||||
let tile_line = y.rem_euclid(height) * 2;
|
if self.is_long && x <= 7 {
|
||||||
let addr = 0x8000 + (tile_number as u16 * 16) + tile_line as u16;
|
tile_number = tile_number & 0xFE;
|
||||||
|
} else if self.is_long && x > 7 {
|
||||||
|
tile_number = tile_number | 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
let tile_byte_1 = bus.read(addr);
|
let tile_line = y.rem_euclid(height) * 2;
|
||||||
let tile_byte_2 = bus.read(addr + 1);
|
let addr = 0x8000 + (tile_number as u16 * 16) + tile_line as u16;
|
||||||
|
|
||||||
let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
let tile_byte_1 = bus.read(addr);
|
||||||
|
let tile_byte_2 = bus.read(addr + 1);
|
||||||
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
|
self.bit_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
let index = (x as usize).rem_euclid(8);
|
bit_pixels_array[bit_pixel_index]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if pixels[index] == 0 {
|
if bit_pixel == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let palette = match self.palette_one {
|
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.palette)))
|
||||||
true => bus.read(OBJECT_PALETTE_1_ADDRESS),
|
|
||||||
false => bus.read(OBJECT_PALETTE_0_ADDRESS),
|
|
||||||
};
|
|
||||||
Some(PPU::get_pixel(PPU::get_palette(pixels[index], palette)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +171,15 @@ pub struct PPU {
|
|||||||
sprite_buffer: Vec<Sprite>,
|
sprite_buffer: Vec<Sprite>,
|
||||||
window_y_counter: u8,
|
window_y_counter: u8,
|
||||||
last_bg_index: u8,
|
last_bg_index: u8,
|
||||||
|
bg_palette: u8,
|
||||||
|
lcd_control_cache: Option<u8>,
|
||||||
|
current_background_pixels: Option<[u8; 8]>,
|
||||||
|
current_window_pixels: Option<[u8; 8]>,
|
||||||
|
lcd_y: u8,
|
||||||
|
scroll_x: u8,
|
||||||
|
scroll_y: u8,
|
||||||
|
window_x: u8,
|
||||||
|
window_y: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PPU {
|
impl PPU {
|
||||||
@ -177,6 +190,15 @@ impl PPU {
|
|||||||
sprite_buffer: Vec::new(),
|
sprite_buffer: Vec::new(),
|
||||||
window_y_counter: 0,
|
window_y_counter: 0,
|
||||||
last_bg_index: 0,
|
last_bg_index: 0,
|
||||||
|
bg_palette: 0,
|
||||||
|
lcd_control_cache: None,
|
||||||
|
current_background_pixels: None,
|
||||||
|
current_window_pixels: None,
|
||||||
|
lcd_y: 0,
|
||||||
|
scroll_x: 0,
|
||||||
|
scroll_y: 0,
|
||||||
|
window_x: 0,
|
||||||
|
window_y: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,12 +211,18 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles, frame_buffer: &mut [u8]) {
|
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles, frame_buffer: &mut [u8]) {
|
||||||
if !PPU::get_lcd_control(bus, LCDControl::LCDEnable) {
|
self.lcd_control_cache = None;
|
||||||
|
if !self.get_lcd_control(bus, LCDControl::LCDEnable) {
|
||||||
self.increment_cycles(cycles);
|
self.increment_cycles(cycles);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.lcd_y = bus.read(LCD_Y_ADDRESS);
|
||||||
|
self.scroll_x = bus.read(SCROLL_X_ADDRESS);
|
||||||
|
self.scroll_y = bus.read(SCROLL_Y_ADDRESS);
|
||||||
|
self.window_x = bus.read(WINDOW_X_ADDRESS);
|
||||||
|
self.window_y = bus.read(WINDOW_Y_ADDRESS);
|
||||||
|
|
||||||
if PPU::get_lcd_y(bus) < 144 {
|
if self.lcd_y < 144 {
|
||||||
if self.cycles.0 <= 80 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) {
|
if self.cycles.0 <= 80 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM)) {
|
||||||
// Mode 2 OAM scan
|
// Mode 2 OAM scan
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
|
||||||
@ -209,7 +237,7 @@ impl PPU {
|
|||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
||||||
self.stat_interrupt(bus);
|
self.stat_interrupt(bus);
|
||||||
}
|
}
|
||||||
} else if PPU::get_lcd_y(bus) >= 144 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
|
} else if self.lcd_y >= 144 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
|
||||||
// Mode 1 Vertical blank
|
// Mode 1 Vertical blank
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
||||||
bus.set_interrupt_flag(Interrupt::VBlank, true);
|
bus.set_interrupt_flag(Interrupt::VBlank, true);
|
||||||
@ -222,13 +250,14 @@ impl PPU {
|
|||||||
if self.cycles.0 > 456 {
|
if self.cycles.0 > 456 {
|
||||||
self.reset_cycles();
|
self.reset_cycles();
|
||||||
|
|
||||||
PPU::set_lcd_y(bus, PPU::get_lcd_y(bus).wrapping_add(1));
|
self.lcd_y = self.lcd_y.wrapping_add(1);
|
||||||
|
|
||||||
// Frame completed
|
// Frame completed
|
||||||
if PPU::get_lcd_y(bus) > 153 {
|
if self.lcd_y > 153 {
|
||||||
PPU::set_lcd_y(bus, 0);
|
self.lcd_y = 0;
|
||||||
self.window_y_counter = 0;
|
self.window_y_counter = 0;
|
||||||
}
|
}
|
||||||
|
bus.force_write(LCD_Y_ADDRESS, self.lcd_y);
|
||||||
// self.check_lyc(bus);
|
// self.check_lyc(bus);
|
||||||
self.stat_interrupt(bus);
|
self.stat_interrupt(bus);
|
||||||
}
|
}
|
||||||
@ -236,7 +265,7 @@ impl PPU {
|
|||||||
|
|
||||||
fn stat_interrupt(&mut self, bus: &mut Bus) {
|
fn stat_interrupt(&mut self, bus: &mut Bus) {
|
||||||
let prev_state = self.state;
|
let prev_state = self.state;
|
||||||
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
|
let lyc_compare = self.lcd_y == bus.read(LCD_Y_COMPARE_ADDRESS);
|
||||||
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
|
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
|
||||||
self.state =
|
self.state =
|
||||||
(
|
(
|
||||||
@ -259,7 +288,7 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_lyc(&mut self, bus: &mut Bus) {
|
fn check_lyc(&mut self, bus: &mut Bus) {
|
||||||
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
|
let lyc_compare = self.lcd_y == bus.read(LCD_Y_COMPARE_ADDRESS);
|
||||||
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
|
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
|
||||||
if !self.state && lyc_compare && PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) {
|
if !self.state && lyc_compare && PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt) {
|
||||||
bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
|
bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
|
||||||
@ -269,10 +298,12 @@ impl PPU {
|
|||||||
|
|
||||||
fn oam_search(&mut self, bus: &Bus) {
|
fn oam_search(&mut self, bus: &Bus) {
|
||||||
self.sprite_buffer = Vec::new();
|
self.sprite_buffer = Vec::new();
|
||||||
if !PPU::get_lcd_control(bus, LCDControl::ObjectEnable) {
|
if !self.get_lcd_control(bus, LCDControl::ObjectEnable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let long_sprites = PPU::get_lcd_control(bus, LCDControl::ObjectSize);
|
let palette_0 = bus.read(OBJECT_PALETTE_0_ADDRESS);
|
||||||
|
let palette_1 = bus.read(OBJECT_PALETTE_1_ADDRESS);
|
||||||
|
let long_sprites = self.get_lcd_control(bus, LCDControl::ObjectSize);
|
||||||
let mut addr = SPRITE_ATTRIBUTE_TABLE.begin();
|
let mut addr = SPRITE_ATTRIBUTE_TABLE.begin();
|
||||||
while addr <= SPRITE_ATTRIBUTE_TABLE.end() {
|
while addr <= SPRITE_ATTRIBUTE_TABLE.end() {
|
||||||
// The gameboy only supports 10 sprites per line,
|
// The gameboy only supports 10 sprites per line,
|
||||||
@ -294,7 +325,7 @@ impl PPU {
|
|||||||
false => 8,
|
false => 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
let lcd_y = PPU::get_lcd_y(bus).saturating_add(16);
|
let lcd_y = self.lcd_y.saturating_add(16);
|
||||||
|
|
||||||
if lcd_y < y || lcd_y > (y + sprite_height - 1) {
|
if lcd_y < y || lcd_y > (y + sprite_height - 1) {
|
||||||
addr += 4;
|
addr += 4;
|
||||||
@ -310,10 +341,14 @@ impl PPU {
|
|||||||
y,
|
y,
|
||||||
tile_number,
|
tile_number,
|
||||||
is_long: long_sprites,
|
is_long: long_sprites,
|
||||||
palette_one: get_bit(attributes, BitIndex::I4),
|
palette: match get_bit(attributes, BitIndex::I4) {
|
||||||
|
true => palette_1,
|
||||||
|
false => palette_0,
|
||||||
|
},
|
||||||
x_flip: get_bit(attributes, BitIndex::I5),
|
x_flip: get_bit(attributes, BitIndex::I5),
|
||||||
y_flip: get_bit(attributes, BitIndex::I6),
|
y_flip: get_bit(attributes, BitIndex::I6),
|
||||||
over_bg: get_bit(attributes, BitIndex::I7),
|
over_bg: get_bit(attributes, BitIndex::I7),
|
||||||
|
bit_pixels: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
addr += 4;
|
addr += 4;
|
||||||
@ -321,9 +356,9 @@ impl PPU {
|
|||||||
self.sprite_buffer.sort_by(|a, b| a.x().cmp(&b.x()));
|
self.sprite_buffer.sort_by(|a, b| a.x().cmp(&b.x()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_sprite_pixel(&self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
fn find_sprite_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
||||||
let lcd_y = PPU::get_lcd_y(bus);
|
let lcd_y = self.lcd_y;
|
||||||
for sprite in &self.sprite_buffer {
|
for sprite in &mut self.sprite_buffer {
|
||||||
if let Some(pixel) = sprite.get_pixel(lcd_x, lcd_y, bus, self.last_bg_index) {
|
if let Some(pixel) = sprite.get_pixel(lcd_x, lcd_y, bus, self.last_bg_index) {
|
||||||
return Some(pixel);
|
return Some(pixel);
|
||||||
}
|
}
|
||||||
@ -332,32 +367,15 @@ impl PPU {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lcd_y(bus: &Bus) -> u8 {
|
pub fn get_lcd_control(&mut self, bus: &Bus, control: LCDControl) -> bool {
|
||||||
bus.read(LCD_Y_ADDRESS)
|
let byte = match self.lcd_control_cache {
|
||||||
}
|
Some(byte) => byte,
|
||||||
|
None => {
|
||||||
fn set_lcd_y(bus: &mut Bus, val: u8) {
|
let byte = bus.read(LCD_CONTROL_ADDRESS);
|
||||||
bus.force_write(LCD_Y_ADDRESS, val);
|
self.lcd_control_cache = Some(byte);
|
||||||
}
|
byte
|
||||||
|
},
|
||||||
fn get_scroll_x(bus: &Bus) -> u8 {
|
};
|
||||||
bus.read(SCROLL_X_ADDRESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_scroll_y(bus: &Bus) -> u8 {
|
|
||||||
bus.read(SCROLL_Y_ADDRESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_window_x(bus: &Bus) -> u8 {
|
|
||||||
bus.read(WINDOW_X_ADDRESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_window_y(bus: &Bus) -> u8 {
|
|
||||||
bus.read(WINDOW_Y_ADDRESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_lcd_control(bus: &Bus, control: LCDControl) -> bool {
|
|
||||||
let byte = bus.read(LCD_CONTROL_ADDRESS);
|
|
||||||
control.get(byte)
|
control.get(byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,12 +433,12 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
fn get_window_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
||||||
let lcd_y = PPU::get_lcd_y(bus);
|
let lcd_y = self.lcd_y;
|
||||||
let window_x = PPU::get_window_x(bus);
|
let window_x = self.window_x;
|
||||||
let window_y = PPU::get_window_y(bus);
|
let window_y = self.window_y;
|
||||||
|
|
||||||
if
|
if
|
||||||
!PPU::get_lcd_control(bus, LCDControl::WindowEnable) ||
|
!self.get_lcd_control(bus, LCDControl::WindowEnable) ||
|
||||||
lcd_x < window_x.saturating_sub(7) ||
|
lcd_x < window_x.saturating_sub(7) ||
|
||||||
lcd_y < window_y ||
|
lcd_y < window_y ||
|
||||||
window_y >= 144 ||
|
window_y >= 144 ||
|
||||||
@ -432,49 +450,78 @@ impl PPU {
|
|||||||
let x = lcd_x.wrapping_sub(window_x.saturating_sub(7));
|
let x = lcd_x.wrapping_sub(window_x.saturating_sub(7));
|
||||||
let y = self.window_y_counter;
|
let y = self.window_y_counter;
|
||||||
|
|
||||||
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
|
let bit_pixel_index = (x as usize).rem_euclid(8);
|
||||||
let tilemap_area = match PPU::get_lcd_control(bus, LCDControl::WindowTileMapAddress) {
|
if bit_pixel_index == 0 {
|
||||||
true => 0x9C00,
|
self.current_window_pixels = None;
|
||||||
false => 0x9800,
|
}
|
||||||
|
|
||||||
|
let bit_pixel = match self.current_window_pixels {
|
||||||
|
Some(bit_pixels_array) => {
|
||||||
|
bit_pixels_array[bit_pixel_index]
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let default_mode = self.get_lcd_control(bus, LCDControl::TileAddressMode);
|
||||||
|
let tilemap_area = match self.get_lcd_control(bus, LCDControl::WindowTileMapAddress) {
|
||||||
|
true => 0x9C00,
|
||||||
|
false => 0x9800,
|
||||||
|
};
|
||||||
|
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
||||||
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
|
self.current_window_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
|
bit_pixels_array[bit_pixel_index]
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
|
||||||
|
|
||||||
let palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
|
self.last_bg_index = bit_pixel & 0b11;
|
||||||
let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
|
||||||
let index = pixels[(x as usize).rem_euclid(8)];
|
|
||||||
self.last_bg_index = index & 0b11;
|
|
||||||
|
|
||||||
Some(PPU::get_pixel(PPU::get_palette(index, palette)))
|
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_background_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
fn get_background_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
||||||
if !PPU::get_lcd_control(bus, LCDControl::BackgroundPriority) {
|
if !self.get_lcd_control(bus, LCDControl::BackgroundPriority) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let lcd_y = PPU::get_lcd_y(bus);
|
let lcd_y = self.lcd_y;
|
||||||
let palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
|
let y = lcd_y.wrapping_add(self.scroll_y);
|
||||||
let y = lcd_y.wrapping_add(PPU::get_scroll_y(bus));
|
let x = lcd_x.wrapping_add(self.scroll_x);
|
||||||
let x = lcd_x.wrapping_add(PPU::get_scroll_x(bus));
|
let bit_pixel_index = x.rem_euclid(8) as usize;
|
||||||
|
|
||||||
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
|
if bit_pixel_index == 0 {
|
||||||
let tilemap_area = match PPU::get_lcd_control(bus, LCDControl::BackgroundTileMapAddress) {
|
self.current_background_pixels = None;
|
||||||
true => 0x9C00,
|
}
|
||||||
false => 0x9800,
|
|
||||||
|
let bit_pixel = match self.current_background_pixels {
|
||||||
|
Some(bit_pixels_array) => {
|
||||||
|
bit_pixels_array[bit_pixel_index]
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let default_mode = self.get_lcd_control(bus, LCDControl::TileAddressMode);
|
||||||
|
let tilemap_area = match self.get_lcd_control(bus, LCDControl::BackgroundTileMapAddress) {
|
||||||
|
true => 0x9C00,
|
||||||
|
false => 0x9800,
|
||||||
|
};
|
||||||
|
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
||||||
|
let bit_pixels_array = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
|
self.current_background_pixels = Some(bit_pixels_array);
|
||||||
|
|
||||||
|
bit_pixels_array[bit_pixel_index]
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
self.last_bg_index = bit_pixel & 0b11;
|
||||||
|
|
||||||
let bg_pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
Some(PPU::get_pixel(PPU::get_palette(bit_pixel, self.bg_palette)))
|
||||||
let index = bg_pixels[x.rem_euclid(8) as usize];
|
|
||||||
self.last_bg_index = index & 0b11;
|
|
||||||
|
|
||||||
Some(PPU::get_pixel(PPU::get_palette(index, palette)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_line(&mut self, bus: &Bus, frame_buffer: &mut [u8]) {
|
fn draw_line(&mut self, bus: &Bus, frame_buffer: &mut [u8]) {
|
||||||
let lcd_y = PPU::get_lcd_y(bus);
|
let lcd_y = self.lcd_y;
|
||||||
if lcd_y as u32 >= LCD_HEIGHT {
|
if lcd_y as u32 >= LCD_HEIGHT {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.current_background_pixels = None;
|
||||||
|
self.current_window_pixels = None;
|
||||||
|
|
||||||
|
self.bg_palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
|
||||||
let mut lcd_x: u8 = 0;
|
let mut lcd_x: u8 = 0;
|
||||||
let mut window_drawn = false;
|
let mut window_drawn = false;
|
||||||
while (lcd_x as u32) < LCD_WIDTH {
|
while (lcd_x as u32) < LCD_WIDTH {
|
||||||
|
@ -66,7 +66,7 @@ pub fn start_eventloop() {
|
|||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
emulator.run(Cycles(70224), pixels.get_frame());
|
emulator.run(Cycles(70224), pixels.get_frame());
|
||||||
|
|
||||||
thread::sleep(time::Duration::from_millis(1));
|
// thread::sleep(time::Duration::from_millis(1));
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
},
|
},
|
||||||
Event::RedrawRequested(_) => {
|
Event::RedrawRequested(_) => {
|
||||||
|
@ -31,15 +31,15 @@ impl Timer {
|
|||||||
|
|
||||||
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) {
|
pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
while count < cycles.to_t().0 {
|
while count < cycles.0 {
|
||||||
self.cycle(bus);
|
self.cycle(bus);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
bus.force_write(TIMER_DIVIDER_REGISTER_ADDRESS, self.divider.to_be_bytes()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cycle(&mut self, bus: &mut Bus) {
|
fn cycle(&mut self, bus: &mut Bus) {
|
||||||
self.divider = self.divider.wrapping_add(1);
|
self.divider = self.divider.wrapping_add(1);
|
||||||
bus.force_write(TIMER_DIVIDER_REGISTER_ADDRESS, self.divider.to_be_bytes()[0]);
|
|
||||||
|
|
||||||
let result = Timer::is_timer_enabled(bus) && self.get_tima_rate(bus);
|
let result = Timer::is_timer_enabled(bus) && self.get_tima_rate(bus);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user