Compare commits

..

2 Commits

6 changed files with 94 additions and 78 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target /target
*.log *.log
/ignore

View File

@ -40,7 +40,8 @@ pub struct Bus {
impl Bus { impl Bus {
pub fn new() -> Self { pub fn new() -> Self {
let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) { let game_rom = match ROM::load_file("ignore/dmg-acid2.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/03-op sp,hl.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/03-op sp,hl.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) {
@ -51,7 +52,8 @@ impl Bus {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/10-bit ops.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/10-bit ops.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/11-op a,(hl).gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/11-op a,(hl).gb".to_string()) {
Ok(rom) => rom, Ok(rom) => rom,
_ => ROM::from_bytes(&[0; 0xFFFF]) // _ => ROM::from_bytes(&[0; 0xFFFF])
_ => panic!("Could not read ROM"),
}; };
Self { Self {
data: [0x00; 0x10000], data: [0x00; 0x10000],

View File

@ -23,7 +23,6 @@ impl Emulator {
self.ppu.draw_background(&self.bus); self.ppu.draw_background(&self.bus);
let ppu_frame = self.ppu.get_rgba_frame(&self.bus); let ppu_frame = self.ppu.get_rgba_frame(&self.bus);
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
let rgba = [0x5e, 0x48, 0xe8, 0xff];
pixel.copy_from_slice(&ppu_frame[i]); pixel.copy_from_slice(&ppu_frame[i]);
} }
} }

View File

@ -2,20 +2,23 @@ use crate::utils::{
BitIndex, BitIndex,
get_bit, get_bit,
set_bit, set_bit,
to_bit_index,
}; };
use crate::bus::{Bus, BANK_ZERO, VIDEO_RAM}; use crate::bus::{Bus, AddressRange, BANK_ZERO, VIDEO_RAM};
use crate::cpu::{Cycles};
use rand::Rng; use rand::Rng;
#[derive(Debug, Copy, Clone)]
enum Pixel {
White,
Light,
Dark,
Black,
}
#[derive(Debug, Copy, Clone)]
struct ColorPalette(u8, u8, u8, u8); struct ColorPalette(u8, u8, u8, u8);
struct Tile {
}
struct Sprite {
}
pub enum LCDControl { pub enum LCDControl {
DisplayEnable, DisplayEnable,
WindowTileMapAddress, WindowTileMapAddress,
@ -50,8 +53,8 @@ pub const FRAME_BUFFER_LENGTH: u32 = WIDTH * HEIGHT;
const LCD_CONTROL_ADDRESS: u16 = 0xFF40; const LCD_CONTROL_ADDRESS: u16 = 0xFF40;
const LCD_STATUS_ADDRESS: u16 = 0xFF41; const LCD_STATUS_ADDRESS: u16 = 0xFF41;
const SCROLL_X_ADDRESS: u16 = 0xFF42; const SCROLL_Y_ADDRESS: u16 = 0xFF42;
const SCROLL_Y_ADDRESS: u16 = 0xFF43; const SCROLL_X_ADDRESS: u16 = 0xFF43;
const LCD_Y_ADDRESS: u16 = 0xFF44; const LCD_Y_ADDRESS: u16 = 0xFF44;
const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45; const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45;
const DMA_ADDRESS: u16 = 0xFF46; const DMA_ADDRESS: u16 = 0xFF46;
@ -60,49 +63,21 @@ const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48;
const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49; const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49;
const WINDOW_X_ADDRESS: u16 = 0xFF4A; const WINDOW_X_ADDRESS: u16 = 0xFF4A;
const WINDOW_Y_ADDRESS: u16 = 0xFF4B; const WINDOW_Y_ADDRESS: u16 = 0xFF4B;
const TILE_MAP_ADDRESS: u16 = 0x9800;
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 { pub struct PPU {
window: Window, cycles: Cycles,
rgba_frame: [[u8; 4]; FRAME_BUFFER_LENGTH as usize], rgba_frame: [[u8; 4]; FRAME_BUFFER_LENGTH as usize],
} }
impl PPU { impl PPU {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
cycles: Cycles(0),
rgba_frame: [[0, 0, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize], rgba_frame: [[0, 0, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize],
window: Window::new(),
} }
} }
fn get_sprite(address: u16) {
}
fn get_scroll_x(bus: &Bus) -> u8 { fn get_scroll_x(bus: &Bus) -> u8 {
bus.read(SCROLL_X_ADDRESS) bus.read(SCROLL_X_ADDRESS)
} }
@ -183,46 +158,67 @@ impl PPU {
bus.write(LCD_STATUS_ADDRESS, byte); bus.write(LCD_STATUS_ADDRESS, byte);
} }
fn get_rgba_pixel(two_bit_pixel: u8) -> [u8; 4] { fn get_pixel(two_bit_pixel: u8) -> Pixel {
match two_bit_pixel { match two_bit_pixel {
0x00 => [255, 255, 255, 255], 0x00 => Pixel::White,
0x01 => [192, 192, 192, 0], 0x01 => Pixel::Light,
0x10 => [128, 128, 128, 0], 0x10 => Pixel::Dark,
0x11 => [0, 0, 0, 0], 0x11 => Pixel::Black,
_ => [0, 0, 0, 0], _ => Pixel::Black,
}
}
fn get_rgba(pixel: Pixel) -> [u8; 4] {
match pixel {
Pixel::White => [255, 255, 255, 0],
Pixel::Light => [192, 192, 192, 0],
Pixel::Dark => [81, 81, 81, 0],
Pixel::Black => [0, 0, 0, 0],
} }
} }
pub fn draw_background(&mut self, bus: &Bus) { pub fn draw_background(&mut self, bus: &Bus) {
let mut pointer = VIDEO_RAM.begin(); let mut idx = 0;
let mut pixels_drawn: usize = 0; // let mut tile_line: u16 = 0;
while pixels_drawn < FRAME_BUFFER_LENGTH as usize { let mut lcd_y: u8 = 0;
let byte1 = bus.read(pointer); while lcd_y < 144 {
let byte2 = bus.read(pointer + 1); let mut lcd_x: u8 = 0;
let pixels = PPU::get_byte_pixels(byte1, byte2); while lcd_x < 160 {
self.rgba_frame[pixels_drawn] = PPU::get_rgba_pixel(pixels[0]); let y = lcd_y.wrapping_add(PPU::get_scroll_y(bus));
self.rgba_frame[pixels_drawn + 1] = PPU::get_rgba_pixel(pixels[1]); let x = lcd_x.wrapping_add(PPU::get_scroll_x(bus));
self.rgba_frame[pixels_drawn + 2] = PPU::get_rgba_pixel(pixels[2]); let index_x = (x as u16 / 8);
self.rgba_frame[pixels_drawn + 3] = PPU::get_rgba_pixel(pixels[3]); let index_y = (y as u16 / 8) * 32;
self.rgba_frame[pixels_drawn + 4] = PPU::get_rgba_pixel(pixels[4]); let index = index_x + index_y;
self.rgba_frame[pixels_drawn + 5] = PPU::get_rgba_pixel(pixels[5]); let tile_line = (y).rem_euclid(8) * 2;
self.rgba_frame[pixels_drawn + 6] = PPU::get_rgba_pixel(pixels[6]); let index_byte = (bus.read(0x9800 + index as u16) as u16) * 16;
self.rgba_frame[pixels_drawn + 7] = PPU::get_rgba_pixel(pixels[7]);
pixels_drawn += 8; let tile_byte_1 = bus.read(0x8000 + tile_line as u16 + index_byte);
pointer += 2; let tile_byte_2 = bus.read(0x8000 + tile_line as u16 + index_byte + 1);
let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2);
for pixel in pixels {
self.rgba_frame[idx] = PPU::get_rgba(pixel);
idx += 1;
}
lcd_x += 8;
}
lcd_y += 1;
// tile_line += 2;
} }
} }
fn get_byte_pixels(byte1: u8, byte2: u8) -> [u8; 8] { fn get_byte_pixels(byte1: u8, byte2: u8) -> [Pixel; 8] {
let mut pixels: [u8; 8] = [0; 8]; let mut pixels: [Pixel; 8] = [Pixel::White; 8];
pixels[0] = ((get_bit(byte1, BitIndex::I7) as u8) << 1) | (get_bit(byte2, BitIndex::I7) as u8); pixels[0] = PPU::get_pixel(((get_bit(byte1, BitIndex::I7) as u8) << 1) | (get_bit(byte2, BitIndex::I7) as u8));
pixels[1] = ((get_bit(byte1, BitIndex::I6) as u8) << 1) | (get_bit(byte2, BitIndex::I6) as u8); pixels[1] = PPU::get_pixel(((get_bit(byte1, BitIndex::I6) as u8) << 1) | (get_bit(byte2, BitIndex::I6) as u8));
pixels[2] = ((get_bit(byte1, BitIndex::I5) as u8) << 1) | (get_bit(byte2, BitIndex::I5) as u8); pixels[2] = PPU::get_pixel(((get_bit(byte1, BitIndex::I5) as u8) << 1) | (get_bit(byte2, BitIndex::I5) as u8));
pixels[3] = ((get_bit(byte1, BitIndex::I4) as u8) << 1) | (get_bit(byte2, BitIndex::I4) as u8); pixels[3] = PPU::get_pixel(((get_bit(byte1, BitIndex::I4) as u8) << 1) | (get_bit(byte2, BitIndex::I4) as u8));
pixels[4] = ((get_bit(byte1, BitIndex::I3) as u8) << 1) | (get_bit(byte2, BitIndex::I3) as u8); pixels[4] = PPU::get_pixel(((get_bit(byte1, BitIndex::I3) as u8) << 1) | (get_bit(byte2, BitIndex::I3) as u8));
pixels[5] = ((get_bit(byte1, BitIndex::I2) as u8) << 1) | (get_bit(byte2, BitIndex::I2) as u8); pixels[5] = PPU::get_pixel(((get_bit(byte1, BitIndex::I2) as u8) << 1) | (get_bit(byte2, BitIndex::I2) as u8));
pixels[6] = ((get_bit(byte1, BitIndex::I1) as u8) << 1) | (get_bit(byte2, BitIndex::I1) as u8); pixels[6] = PPU::get_pixel(((get_bit(byte1, BitIndex::I1) as u8) << 1) | (get_bit(byte2, BitIndex::I1) as u8));
pixels[7] = ((get_bit(byte1, BitIndex::I0) as u8) << 1) | (get_bit(byte2, BitIndex::I0) as u8); pixels[7] = PPU::get_pixel(((get_bit(byte1, BitIndex::I0) as u8) << 1) | (get_bit(byte2, BitIndex::I0) as u8));
pixels pixels
} }

View File

@ -2,6 +2,8 @@ use crate::emulator::Emulator;
use crate::cpu::{CPU, Cycles}; use crate::cpu::{CPU, Cycles};
use crate::ppu::{WIDTH, HEIGHT}; use crate::ppu::{WIDTH, HEIGHT};
use std::{thread, time};
use log::error; use log::error;
use pixels::{Error, Pixels, SurfaceTexture}; use pixels::{Error, Pixels, SurfaceTexture};
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
@ -62,10 +64,12 @@ pub fn start_eventloop() {
}, },
Event::MainEventsCleared => { Event::MainEventsCleared => {
emulator.run(Cycles(70224)); emulator.run(Cycles(70224));
emulator.draw(pixels.get_frame());
thread::sleep(time::Duration::from_millis(14));
window.request_redraw(); window.request_redraw();
}, },
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
emulator.draw(pixels.get_frame());
if pixels if pixels
.render() .render()
.map_err(|e| error!("pixels.render() failed: {}", e)) .map_err(|e| error!("pixels.render() failed: {}", e))

View File

@ -10,6 +10,20 @@ pub enum BitIndex {
I7, I7,
} }
pub fn to_bit_index(index: u8) -> BitIndex {
match index {
0 => BitIndex::I0,
1 => BitIndex::I1,
2 => BitIndex::I2,
3 => BitIndex::I3,
4 => BitIndex::I4,
5 => BitIndex::I5,
6 => BitIndex::I6,
7 => BitIndex::I7,
_ => BitIndex::I7,
}
}
pub fn get_bit_index(index: BitIndex) -> u8 { pub fn get_bit_index(index: BitIndex) -> u8 {
match index { match index {
BitIndex::I0 => 0, BitIndex::I0 => 0,