mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
Compare commits
4 Commits
f7ca3fcc25
...
bb19a6a6db
Author | SHA1 | Date | |
---|---|---|---|
bb19a6a6db | |||
aa30b98679 | |||
2474cd72b7 | |||
b92160dee4 |
19
README.md
19
README.md
@ -7,12 +7,23 @@ Any help or suggestion is welcome!
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
- [x] CPU implementation
|
- [x] CPU implementation
|
||||||
- [x] Interrupts (interrupts test not passing yet)
|
- [x] Interrupts
|
||||||
- [x] Timer
|
- [x] Timer
|
||||||
- [x] Joypad (not configurable yet)
|
- [x] Joypad (not configurable yet)
|
||||||
- [ ] PPU implementations
|
- [X] PPU implementations
|
||||||
- [ ] Gameboy boot ROM
|
|
||||||
- [x] Render the pixels
|
- [x] Render the pixels
|
||||||
|
- [ ] MBC Implementations
|
||||||
|
- [x] NoMBC
|
||||||
|
- [ ] MBC1
|
||||||
|
- [ ] MBC2
|
||||||
|
- [ ] MBC3
|
||||||
|
- [ ] MBC4
|
||||||
|
- [ ] MBC5
|
||||||
|
- [ ] MBC6
|
||||||
|
- [ ] MBC7
|
||||||
|
- [ ] HuC1
|
||||||
|
- [ ] Web Assembly support (because this is a Rust project and it has to support Web Assembly)
|
||||||
|
- [ ] Gameboy boot ROM (Not important for now)
|
||||||
- [ ] Gameboy Color compatibility
|
- [ ] Gameboy Color compatibility
|
||||||
- [ ] Sound
|
- [ ] Sound
|
||||||
- [ ] Web Assembly support (because this is a Rust project and it has to support Web Assembly)
|
- [ ] Many code refactors and optimizations are needed
|
||||||
|
10
src/bus.rs
10
src/bus.rs
@ -57,7 +57,7 @@ pub struct Bus {
|
|||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) {
|
let game_rom = match ROM::load_file("ignore/dmg-acid2.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()) {
|
||||||
@ -129,7 +129,6 @@ impl Bus {
|
|||||||
|
|
||||||
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) || EXTERNAL_RAM.in_range(address) {
|
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) || EXTERNAL_RAM.in_range(address) {
|
||||||
self.game_rom.write(address, data);
|
self.game_rom.write(address, data);
|
||||||
// println!("WRITING TO ROM");
|
|
||||||
} else if WORK_RAM_1.in_range(address) || WORK_RAM_2.in_range(address) {
|
} else if WORK_RAM_1.in_range(address) || WORK_RAM_2.in_range(address) {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
// Copy to the ECHO RAM
|
// Copy to the ECHO RAM
|
||||||
@ -143,9 +142,12 @@ impl Bus {
|
|||||||
self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM
|
self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM
|
||||||
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
|
||||||
self.reset_timer = true;
|
self.reset_timer = true;
|
||||||
} else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) {
|
} else if address == LCD_CONTROL_ADDRESS {
|
||||||
self.data[address as usize] = data;
|
self.data[address as usize] = data;
|
||||||
self.data[LCD_Y_ADDRESS as usize] = 0x00;
|
// Check if LCD is being turned on
|
||||||
|
if get_bit(data, BitIndex::I7) && !get_bit(self.data[address as usize], BitIndex::I7) {
|
||||||
|
self.data[LCD_Y_ADDRESS as usize] = 0x00;
|
||||||
|
}
|
||||||
} else if address == LCD_Y_ADDRESS {
|
} else if address == LCD_Y_ADDRESS {
|
||||||
// println!("Write to LCD_Y not allowed");
|
// println!("Write to LCD_Y not allowed");
|
||||||
} else if address == LCD_STATUS_ADDRESS {
|
} else if address == LCD_STATUS_ADDRESS {
|
||||||
|
187
src/ppu.rs
187
src/ppu.rs
@ -23,8 +23,8 @@ pub const DMA_ADDRESS: u16 = 0xFF46;
|
|||||||
pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
|
pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47;
|
||||||
pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
|
||||||
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
|
||||||
pub const WINDOW_X_ADDRESS: u16 = 0xFF4A;
|
pub const WINDOW_X_ADDRESS: u16 = 0xFF4B;
|
||||||
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4B;
|
pub const WINDOW_Y_ADDRESS: u16 = 0xFF4A;
|
||||||
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
pub const TILE_MAP_ADDRESS: u16 = 0x9800;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -89,12 +89,6 @@ pub enum LCDStatus {
|
|||||||
ModeFlag(LCDStatusModeFlag),
|
ModeFlag(LCDStatusModeFlag),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PPU {
|
|
||||||
prev_state: bool,
|
|
||||||
cycles: Cycles,
|
|
||||||
sprite_buffer: Vec<Sprite>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
x: u8,
|
x: u8,
|
||||||
y: u8,
|
y: u8,
|
||||||
@ -111,13 +105,13 @@ impl Sprite {
|
|||||||
self.x
|
self.x
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pixel(&self, lcd_x: u8, lcd_y: u8, bus: &Bus) -> Option<Pixel> {
|
pub fn get_pixel(&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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.over_bg {
|
if self.over_bg && (last_bg_index & 0b11) != 0 {
|
||||||
// todo!("Implement over_bg sprite property");
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let height: u8 = match self.is_long {
|
let height: u8 = match self.is_long {
|
||||||
@ -151,9 +145,11 @@ impl Sprite {
|
|||||||
let tile_byte_1 = bus.read(addr);
|
let tile_byte_1 = bus.read(addr);
|
||||||
let tile_byte_2 = bus.read(addr + 1);
|
let tile_byte_2 = bus.read(addr + 1);
|
||||||
|
|
||||||
let pixel_index = (x as usize).rem_euclid(8);
|
let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
|
|
||||||
if PPU::get_two_bit_byte_pixels(tile_byte_1, tile_byte_2)[pixel_index] == 0 {
|
let index = (x as usize).rem_euclid(8);
|
||||||
|
|
||||||
|
if pixels[index] == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,19 +157,26 @@ impl Sprite {
|
|||||||
true => bus.read(OBJECT_PALETTE_1_ADDRESS),
|
true => bus.read(OBJECT_PALETTE_1_ADDRESS),
|
||||||
false => bus.read(OBJECT_PALETTE_0_ADDRESS),
|
false => bus.read(OBJECT_PALETTE_0_ADDRESS),
|
||||||
};
|
};
|
||||||
let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2, palette);
|
Some(PPU::get_pixel(PPU::get_palette(pixels[index], palette)))
|
||||||
|
|
||||||
Some(pixels[pixel_index])
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PPU {
|
||||||
|
state: bool,
|
||||||
|
cycles: Cycles,
|
||||||
|
sprite_buffer: Vec<Sprite>,
|
||||||
|
window_y_counter: u8,
|
||||||
|
last_bg_index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
impl PPU {
|
impl PPU {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
prev_state: false,
|
state: false,
|
||||||
cycles: Cycles(0),
|
cycles: Cycles(0),
|
||||||
sprite_buffer: Vec::new(),
|
sprite_buffer: Vec::new(),
|
||||||
|
window_y_counter: 0,
|
||||||
|
last_bg_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,8 +202,8 @@ impl PPU {
|
|||||||
self.oam_search(bus);
|
self.oam_search(bus);
|
||||||
} else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
} else if self.cycles.0 > 80 && self.cycles.0 <= 80 + 172 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
|
||||||
// Mode 3 drawing pixel line. This could also last 289 cycles
|
// Mode 3 drawing pixel line. This could also last 289 cycles
|
||||||
self.draw_line(bus, frame_buffer);
|
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
|
||||||
|
self.draw_line(bus, frame_buffer);
|
||||||
} else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) {
|
} else if self.cycles.0 > 80 + 172 && self.cycles.0 <= 80 + 172 + 204 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank)) {
|
||||||
// Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3
|
// Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3
|
||||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
|
||||||
@ -208,8 +211,8 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
} else if PPU::get_lcd_y(bus) >= 144 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
|
} else if PPU::get_lcd_y(bus) >= 144 && !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)) {
|
||||||
// Mode 1 Vertical blank
|
// Mode 1 Vertical blank
|
||||||
bus.set_interrupt_flag(Interrupt::VBlank, true);
|
|
||||||
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);
|
||||||
self.stat_interrupt(bus);
|
self.stat_interrupt(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,30 +223,47 @@ impl PPU {
|
|||||||
self.reset_cycles();
|
self.reset_cycles();
|
||||||
|
|
||||||
PPU::set_lcd_y(bus, PPU::get_lcd_y(bus).wrapping_add(1));
|
PPU::set_lcd_y(bus, PPU::get_lcd_y(bus).wrapping_add(1));
|
||||||
|
|
||||||
// Frame completed
|
// Frame completed
|
||||||
if PPU::get_lcd_y(bus) > 153 {
|
if PPU::get_lcd_y(bus) > 153 {
|
||||||
PPU::set_lcd_y(bus, 0);
|
PPU::set_lcd_y(bus, 0);
|
||||||
|
self.window_y_counter = 0;
|
||||||
}
|
}
|
||||||
self.check_lyc(bus);
|
// self.check_lyc(bus);
|
||||||
|
self.stat_interrupt(bus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stat_interrupt(&mut self, bus: &mut Bus) {
|
fn stat_interrupt(&mut self, bus: &mut Bus) {
|
||||||
let state = self.prev_state;
|
let prev_state = self.state;
|
||||||
self.prev_state = (PPU::get_lcd_status(bus, LCDStatus::Mode2OAMInterrupt) && PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM))) ||
|
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
|
||||||
(PPU::get_lcd_status(bus, LCDStatus::Mode0HBlankInterrupt) && PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank))) ||
|
PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare);
|
||||||
(PPU::get_lcd_status(bus, LCDStatus::Mode1VBlankInterrupt) && PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank)));
|
self.state =
|
||||||
if self.prev_state && !state {
|
(
|
||||||
bus.set_interrupt_flag(Interrupt::LCDSTAT, true);
|
lyc_compare &&
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::LYCInterrupt)
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::Mode2OAMInterrupt) &&
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM))
|
||||||
|
) || (
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::Mode0HBlankInterrupt) &&
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank))
|
||||||
|
) || (
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::Mode1VBlankInterrupt) &&
|
||||||
|
PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank))
|
||||||
|
);
|
||||||
|
if self.state && !prev_state {
|
||||||
|
bus.set_interrupt_flag(Interrupt::LCDSTAT, self.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = PPU::get_lcd_y(bus) == 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 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);
|
||||||
self.prev_state = true;
|
self.state = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +279,7 @@ impl PPU {
|
|||||||
// but since we are on an emulator we can avoud that limitation
|
// but since we are on an emulator we can avoud that limitation
|
||||||
if self.sprite_buffer.len() >= 10 {
|
if self.sprite_buffer.len() >= 10 {
|
||||||
// todo!("Make a setting for the 10 sprites per scanline");
|
// todo!("Make a setting for the 10 sprites per scanline");
|
||||||
// break;
|
break;
|
||||||
}
|
}
|
||||||
let y = bus.read(addr);
|
let y = bus.read(addr);
|
||||||
let x = bus.read(addr + 1);
|
let x = bus.read(addr + 1);
|
||||||
@ -304,7 +324,7 @@ impl PPU {
|
|||||||
fn find_sprite_pixel(&self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
fn find_sprite_pixel(&self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
||||||
let lcd_y = PPU::get_lcd_y(bus);
|
let lcd_y = PPU::get_lcd_y(bus);
|
||||||
for sprite in &self.sprite_buffer {
|
for sprite in &self.sprite_buffer {
|
||||||
if let Some(pixel) = sprite.get_pixel(lcd_x, lcd_y, bus) {
|
if let Some(pixel) = sprite.get_pixel(lcd_x, lcd_y, bus, self.last_bg_index) {
|
||||||
return Some(pixel);
|
return Some(pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,17 +414,23 @@ impl PPU {
|
|||||||
(bus.read(addr), bus.read(addr + 1))
|
(bus.read(addr), bus.read(addr + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_pixel(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 = PPU::get_lcd_y(bus);
|
||||||
let window_x = PPU::get_window_x(bus);
|
let window_x = PPU::get_window_x(bus);
|
||||||
let window_y = PPU::get_window_y(bus);
|
let window_y = PPU::get_window_y(bus);
|
||||||
|
|
||||||
if !PPU::get_lcd_control(bus, LCDControl::WindowEnable) || lcd_x < (window_x.saturating_sub(7)) || window_y != lcd_y {
|
if
|
||||||
|
!PPU::get_lcd_control(bus, LCDControl::WindowEnable) ||
|
||||||
|
lcd_x < window_x.saturating_sub(7) ||
|
||||||
|
lcd_y < window_y ||
|
||||||
|
window_y >= 144 ||
|
||||||
|
window_x.saturating_sub(7) >= 160
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = lcd_x.wrapping_sub(window_x.saturating_sub(7));
|
let x = lcd_x.wrapping_sub(window_x.saturating_sub(7));
|
||||||
let y = lcd_y.wrapping_sub(window_y);
|
let y = self.window_y_counter;
|
||||||
|
|
||||||
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
|
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
|
||||||
let tilemap_area = match PPU::get_lcd_control(bus, LCDControl::WindowTileMapAddress) {
|
let tilemap_area = match PPU::get_lcd_control(bus, LCDControl::WindowTileMapAddress) {
|
||||||
@ -414,44 +440,59 @@ impl PPU {
|
|||||||
let (tile_byte_1, tile_byte_2) = PPU::get_tile_bytes(x, y, tilemap_area, default_mode, bus);
|
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);
|
let palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
|
||||||
let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2, palette);
|
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(pixels[(x as usize).rem_euclid(8)])
|
Some(PPU::get_pixel(PPU::get_palette(index, palette)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_background_pixel(&mut self, lcd_x: u8, bus: &Bus) -> Option<Pixel> {
|
||||||
|
if !PPU::get_lcd_control(bus, LCDControl::BackgroundPriority) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let lcd_y = PPU::get_lcd_y(bus);
|
||||||
|
let palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
|
||||||
|
let y = lcd_y.wrapping_add(PPU::get_scroll_y(bus));
|
||||||
|
let x = lcd_x.wrapping_add(PPU::get_scroll_x(bus));
|
||||||
|
|
||||||
|
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
|
||||||
|
let tilemap_area = match PPU::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 bg_pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
|
||||||
|
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 palette = bus.read(BACKGROUND_PALETTE_ADDRESS);
|
|
||||||
let lcd_y = PPU::get_lcd_y(bus);
|
let lcd_y = PPU::get_lcd_y(bus);
|
||||||
if lcd_y as u32 >= LCD_HEIGHT {
|
if lcd_y as u32 >= LCD_HEIGHT {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut lcd_x: u8 = 0;
|
let mut lcd_x: u8 = 0;
|
||||||
|
let mut window_drawn = false;
|
||||||
while (lcd_x as u32) < LCD_WIDTH {
|
while (lcd_x as u32) < LCD_WIDTH {
|
||||||
let y = lcd_y.wrapping_add(PPU::get_scroll_y(bus));
|
|
||||||
let x = lcd_x.wrapping_add(PPU::get_scroll_x(bus));
|
|
||||||
|
|
||||||
let default_mode = PPU::get_lcd_control(bus, LCDControl::TileAddressMode);
|
|
||||||
let tilemap_area = match PPU::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 bg_pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2, palette);
|
|
||||||
|
|
||||||
let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4;
|
let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4;
|
||||||
let pixel = bg_pixels[x.rem_euclid(8) as usize];
|
|
||||||
let rgba = PPU::get_rgba(pixel);
|
if let Some(background_pixel) = self.get_background_pixel(lcd_x, bus) {
|
||||||
frame_buffer[idx] = rgba[0];
|
let rgba = PPU::get_rgba(background_pixel);
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
frame_buffer[idx] = rgba[0];
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
frame_buffer[idx + 1] = rgba[1];
|
||||||
if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) {
|
frame_buffer[idx + 2] = rgba[2];
|
||||||
|
}
|
||||||
|
if let Some(window_pixel) = self.get_window_pixel(lcd_x, bus) {
|
||||||
|
window_drawn = true;
|
||||||
let rgba = PPU::get_rgba(window_pixel);
|
let rgba = PPU::get_rgba(window_pixel);
|
||||||
frame_buffer[idx] = rgba[0];
|
frame_buffer[idx] = rgba[0];
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
frame_buffer[idx + 1] = rgba[1];
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
frame_buffer[idx + 2] = rgba[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sprite_pixel) = self.find_sprite_pixel(lcd_x, bus) {
|
if let Some(sprite_pixel) = self.find_sprite_pixel(lcd_x, bus) {
|
||||||
let rgba = PPU::get_rgba(sprite_pixel);
|
let rgba = PPU::get_rgba(sprite_pixel);
|
||||||
frame_buffer[idx] = rgba[0];
|
frame_buffer[idx] = rgba[0];
|
||||||
@ -460,22 +501,9 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lcd_x += 1;
|
lcd_x += 1;
|
||||||
|
}
|
||||||
/* for pixel in bg_pixels {
|
if window_drawn {
|
||||||
let idx = (lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize)) * 4;
|
self.window_y_counter += 1;
|
||||||
let rgba = PPU::get_rgba(pixel);
|
|
||||||
frame_buffer[idx] = rgba[0];
|
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
|
||||||
if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) {
|
|
||||||
let rgba = PPU::get_rgba(pixel);
|
|
||||||
frame_buffer[idx] = rgba[0];
|
|
||||||
frame_buffer[idx + 1] = rgba[1];
|
|
||||||
frame_buffer[idx + 2] = rgba[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
lcd_x += 1;
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +536,7 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_two_bit_byte_pixels(byte1: u8, byte2: u8) -> [u8; 8] {
|
fn get_byte_pixels(byte1: u8, byte2: u8) -> [u8; 8] {
|
||||||
[
|
[
|
||||||
((byte1 >> 7) & 0b01) | ((byte2 >> 6) & 0b10),
|
((byte1 >> 7) & 0b01) | ((byte2 >> 6) & 0b10),
|
||||||
((byte1 >> 6) & 0b01) | ((byte2 >> 5) & 0b10),
|
((byte1 >> 6) & 0b01) | ((byte2 >> 5) & 0b10),
|
||||||
@ -520,17 +548,4 @@ impl PPU {
|
|||||||
(byte1 & 0b01) | ((byte2 << 1) & 0b10),
|
(byte1 & 0b01) | ((byte2 << 1) & 0b10),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_byte_pixels(byte1: u8, byte2: u8, palette: u8) -> [Pixel; 8] {
|
|
||||||
[
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 7) & 0b01) | ((byte2 >> 6) & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 6) & 0b01) | ((byte2 >> 5) & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 5) & 0b01) | ((byte2 >> 4) & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 4) & 0b01) | ((byte2 >> 3) & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 3) & 0b01) | ((byte2 >> 2) & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 2) & 0b01) | ((byte2 >> 1) & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette(((byte1 >> 1) & 0b01) | (byte2 & 0b10), palette)),
|
|
||||||
PPU::get_pixel(PPU::get_palette((byte1 & 0b01) | ((byte2 << 1) & 0b10), palette)),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,6 @@ pub fn start_eventloop() {
|
|||||||
},
|
},
|
||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
emulator.run(Cycles(70224), pixels.get_frame());
|
emulator.run(Cycles(70224), pixels.get_frame());
|
||||||
// emulator.draw(pixels.get_frame());
|
|
||||||
|
|
||||||
thread::sleep(time::Duration::from_millis(1));
|
thread::sleep(time::Duration::from_millis(1));
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
|
Loading…
Reference in New Issue
Block a user