From 1084be52b873e682b732cfb4c52b7c855a480f11 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Mon, 15 Jul 2019 07:35:09 +0300 Subject: [PATCH] WIP mode0 rendering Former-commit-id: 6bce375f9373bbddf4522da5ecc2ea3584373847 --- Cargo.lock | 12 ++ Cargo.toml | 4 +- src/debugger/command.rs | 25 ++- src/debugger/mod.rs | 4 +- src/debugger/render_view.rs | 50 ++++++ src/debugger/tile_view.rs | 105 ++++++++++++ src/gba.rs | 62 ++++++-- src/lcd.rs | 308 ++++++++++++++++++++++++++---------- src/palette.rs | 8 +- 9 files changed, 476 insertions(+), 102 deletions(-) create mode 100644 src/debugger/render_view.rs create mode 100644 src/debugger/tile_view.rs diff --git a/Cargo.lock b/Cargo.lock index 35686c2..98f1f87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -566,6 +566,7 @@ dependencies = [ "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -719,6 +720,16 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-segmentation" version = "1.3.0" @@ -887,6 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" diff --git a/Cargo.toml b/Cargo.toml index c3982e6..9f677f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ colored = "1.8" ansi_term = "0.11.0" hexdump = "0.1.0" sdl2 = "0.32.2" +time = "0.1.42" [profile.dev] -opt-level = 0 \ No newline at end of file +opt-level = 1 +debug = true \ No newline at end of file diff --git a/src/debugger/command.rs b/src/debugger/command.rs index 1c1a82a..c078c57 100644 --- a/src/debugger/command.rs +++ b/src/debugger/command.rs @@ -6,6 +6,8 @@ use crate::lcd::*; use crate::GBAError; use super::palette_view::create_palette_view; +use super::render_view::create_render_view; +use super::tile_view::create_tile_view; use super::{parser::Value, Debugger, DebuggerError, DebuggerResult}; use ansi_term::Colour; @@ -26,11 +28,13 @@ pub enum Command { Step(usize), Continue, Frame(usize), + Render, HexDump(Addr, usize), Disass(DisassMode, Addr, usize), AddBreakpoint(Addr), DelBreakpoint(Addr), PaletteView, + TileView(u32), ClearBreakpoints, ListBreakpoints, Reset, @@ -43,7 +47,6 @@ impl Command { match *self { Info => println!("{}", debugger.gba.cpu), DisplayInfo => { - println!("{:?}", debugger.gba.lcd); println!( "DISPCNT: {:#?}", DisplayControl::from(debugger.gba.sysbus.ioregs.read_reg(REG_DISPCNT)) @@ -56,6 +59,11 @@ impl Command { "VCOUNT: {:?}", debugger.gba.sysbus.ioregs.read_reg(REG_VCOUNT) ); + for bg in 0..4 { + let bgcnt = + BgControl::from(debugger.gba.sysbus.ioregs.read_reg(REG_BG0CNT + 2 * bg)); + println!("BG{}CNT: {:#?}", bg, bgcnt); + } } Step(count) => { for _ in 0..count { @@ -115,11 +123,15 @@ impl Command { }; }, Frame(count) => { + use super::time::PreciseTime; + let start = PreciseTime::now(); for _ in 0..count { debugger.gba.frame(); - print!(".") } + let end = PreciseTime::now(); + println!("that took {} seconds", start.to(end)); } + Render => create_render_view(&debugger.gba), HexDump(addr, nbytes) => { let bytes = debugger.gba.sysbus.get_bytes(addr); hexdump::hexdump(&bytes[0..nbytes]); @@ -166,6 +178,7 @@ impl Command { } } PaletteView => create_palette_view(debugger.gba.sysbus.get_bytes(0x0500_0000)), + TileView(bg) => create_tile_view(bg, &debugger.gba), Reset => { println!("resetting cpu..."); debugger.gba.cpu.reset(); @@ -308,9 +321,17 @@ impl Debugger { ))), }, "palette-view" => Ok(Command::PaletteView), + "tiles" => { + if args.len() != 1 { + return Err(DebuggerError::InvalidCommandFormat("tile ".to_string())); + } + let bg = self.val_number(&args[0])?; + Ok(Command::TileView(bg)) + } "bl" => Ok(Command::ListBreakpoints), "q" | "quit" => Ok(Command::Quit), "r" | "reset" => Ok(Command::Reset), + "rd" | "render" => Ok(Command::Render), _ => Err(DebuggerError::InvalidCommand(command)), } } diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index e1e1f6f..3f81feb 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -13,6 +13,9 @@ mod command; use command::Command; mod palette_view; +mod render_view; +mod tile_view; +extern crate time; #[derive(Debug, PartialEq)] pub enum DebuggerError { @@ -31,7 +34,6 @@ impl From for DebuggerError { type DebuggerResult = Result; -#[derive(Debug)] pub struct Debugger { pub gba: GameBoyAdvance, running: bool, diff --git a/src/debugger/render_view.rs b/src/debugger/render_view.rs new file mode 100644 index 0000000..0c18936 --- /dev/null +++ b/src/debugger/render_view.rs @@ -0,0 +1,50 @@ +use sdl2::event::Event; +use sdl2::pixels::Color; +use sdl2::rect::Point; + +use crate::gba::GameBoyAdvance; +use crate::lcd::Lcd; + +const SCREEN_WIDTH: u32 = Lcd::DISPLAY_WIDTH as u32; +const SCREEN_HEIGHT: u32 = Lcd::DISPLAY_HEIGHT as u32; + +pub fn create_render_view(gba: &GameBoyAdvance) { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let window = video_subsystem + .window("RenderView", SCREEN_WIDTH, SCREEN_HEIGHT) + .position_centered() + .build() + .unwrap(); + + let mut canvas = window.into_canvas().build().unwrap(); + + canvas.set_draw_color(Color::RGB(0xfa, 0xfa, 0xfa)); + canvas.clear(); + + for y in 0..Lcd::DISPLAY_HEIGHT { + for x in 0..Lcd::DISPLAY_WIDTH { + let index = (x as usize) + (y as usize) * (256 as usize); + let color = gba.lcd.pixeldata[index]; + let rgb24: Color = color.into(); + canvas.set_draw_color(rgb24); + canvas.draw_point(Point::from((x as i32, y as i32))); + } + } + + canvas.present(); + + let mut event_pump = sdl_context.event_pump().unwrap(); + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } => break 'running, + Event::MouseButtonDown { x, y, .. } => { + println!("({},{}) {:x}", x, y, x + y * (Lcd::DISPLAY_WIDTH as i32)); + } + _ => {} + } + } + } +} diff --git a/src/debugger/tile_view.rs b/src/debugger/tile_view.rs new file mode 100644 index 0000000..ace8533 --- /dev/null +++ b/src/debugger/tile_view.rs @@ -0,0 +1,105 @@ +use sdl2::event::Event; +use sdl2::pixels::Color; +use sdl2::rect::{Point, Rect}; +use sdl2::render::Canvas; + +use crate::arm7tdmi::bus::Bus; +use crate::gba::GameBoyAdvance; +use crate::ioregs::consts::*; +use crate::lcd::*; +use crate::palette::*; +use crate::sysbus::SysBus; + +impl Into for Rgb15 { + fn into(self) -> Color { + let (r, g, b) = self.get_rgb24(); + Color::RGB(r, g, b) + } +} + +fn draw_tile( + gba: &GameBoyAdvance, + tile_addr: u32, + pixel_format: PixelFormat, + p: Point, + canvas: &mut Canvas, +) { + for y in 0..8 { + for x in 0..8 { + let color = match pixel_format { + PixelFormat::BPP4 => { + let index = + gba.lcd + .read_pixel_index(&gba.sysbus, tile_addr, x, y, 4, pixel_format); + gba.lcd.get_palette_color(&gba.sysbus, index as u32, 0) + } + PixelFormat::BPP8 => { + let index = + gba.lcd + .read_pixel_index(&gba.sysbus, tile_addr, x, y, 8, pixel_format); + gba.lcd.get_palette_color(&gba.sysbus, index as u32, 0) + } + }; + let (r, g, b) = color.get_rgb24(); + + canvas.set_draw_color(Color::RGB(r, g, b)); + canvas.draw_point(p.offset(x as i32, y as i32)); + } + } +} + +pub fn create_tile_view(bg: u32, gba: &GameBoyAdvance) { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let window = video_subsystem + .window("PaletteView", 512, 512) + .position_centered() + .build() + .unwrap(); + + let mut canvas = window.into_canvas().build().unwrap(); + + canvas.set_draw_color(Color::RGB(00, 00, 00)); + canvas.clear(); + + let bgcnt = BgControl::from(gba.sysbus.ioregs.read_reg(REG_BG0CNT + 2 * bg)); + + let palette = Palette::from(gba.sysbus.get_bytes(0x0500_0000)); + let (tile_size, pixel_format) = bgcnt.tile_format(); + let tileset_addr = bgcnt.char_block(); + let tilemap_addr = bgcnt.screen_block(); + let tiles_per_row = 32; + let num_tiles = 0x4000 / tile_size; + println!("tileset: {:#x}, tilemap: {:#x}", tileset_addr, tilemap_addr); + + let mut tile_x = 0x20; + let mut tile_y = 0x20; + for t in 0..num_tiles { + let tile_addr = tileset_addr + t * tile_size; + if t != 0 && t % tiles_per_row == 0 { + tile_y += 10; + tile_x = 0x20; + } + tile_x += 10; + draw_tile( + gba, + tile_addr, + pixel_format, + Point::from((tile_x, tile_y)), + &mut canvas, + ); + } + + canvas.present(); + + let mut event_pump = sdl_context.event_pump().unwrap(); + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } => break 'running, + _ => {} + } + } + } +} diff --git a/src/gba.rs b/src/gba.rs index 9fafbc4..9d154c8 100644 --- a/src/gba.rs +++ b/src/gba.rs @@ -1,15 +1,15 @@ /// Struct containing everything /// -use super::arm7tdmi::{Core, DecodedInstruction}; +use super::arm7tdmi::{exception::*, Core, DecodedInstruction}; use super::cartridge::Cartridge; use super::dma::DmaChannel; +use super::interrupt::*; use super::ioregs::consts::*; -use super::lcd::Lcd; +use super::lcd::*; use super::sysbus::SysBus; -use super::{EmuIoDev, GBAResult}; +use super::{EmuIoDev, GBAError, GBAResult}; -#[derive(Debug)] pub struct GameBoyAdvance { pub cpu: Core, pub sysbus: SysBus, @@ -42,26 +42,52 @@ impl GameBoyAdvance { } } - fn run_cpu_for_n_cycles(&mut self, n: usize) { - let previous_cycles = self.cpu.cycles; + fn emulate_n_cycles(&mut self, mut n: usize) { + let mut cycles = 0; loop { + let previous_cycles = self.cpu.cycles; self.cpu.step_one(&mut self.sysbus).unwrap(); - if n > self.cpu.cycles - previous_cycles { + let new_cycles = self.cpu.cycles - previous_cycles; + + self.lcd.step(new_cycles, &mut self.sysbus); + cycles += new_cycles; + + if n <= cycles { break; } } } pub fn frame(&mut self) { - for _ in 0..Lcd::DISPLAY_HEIGHT { - self.run_cpu_for_n_cycles(Lcd::CYCLES_HDRAW); - let _irq = self.lcd.set_hblank(&mut self.sysbus); - self.run_cpu_for_n_cycles(Lcd::CYCLES_HBLANK); + while self.lcd.state == LcdState::VBlank { + self.emulate(); } - let _irq = self.lcd.set_vblank(&mut self.sysbus); - self.run_cpu_for_n_cycles(Lcd::CYCLES_VBLANK); - self.lcd.render(&mut self.sysbus); // Currently not implemented - self.lcd.set_hdraw(); + while self.lcd.state != LcdState::VBlank { + self.emulate(); + } + } + + pub fn emulate(&mut self) { + let previous_cycles = self.cpu.cycles; + self.cpu.step(&mut self.sysbus).unwrap(); + let cycles = self.cpu.cycles - previous_cycles; + self.lcd.step(cycles, &mut self.sysbus); + } + + fn interrupts_disabled(&self) -> bool { + self.sysbus.ioregs.read_reg(REG_IME) & 1 == 0 + } + + fn request_irq(&mut self, irq: Interrupt) { + // if self.interrupts_disabled() { + // return; + // } + // let irq_bit = irq as usize; + // let reg_ie = self.sysbus.ioregs.read_reg(REG_IE); + // if reg_ie & (1 << irq_bit) != 0 { + // println!("entering {:?}", irq); + // self.cpu.exception(Exception::Irq); + // } } pub fn step(&mut self) -> GBAResult { @@ -84,7 +110,11 @@ impl GameBoyAdvance { // let (dma_cycles, _) = self.dma3.step(cycles, &mut self.sysbus); // cycles += dma_cycles; - // let (lcd_cycles, _) = self.lcd.step(cycles, &mut self.sysbus); + /* let (_, irq) = */ + self.lcd.step(cycles, &mut self.sysbus); + // if let Some(irq) = irq { + // self.request_irq(irq); + // } // cycles += lcd_cycles; Ok(executed_insn) diff --git a/src/lcd.rs b/src/lcd.rs index bc178a8..fb48421 100644 --- a/src/lcd.rs +++ b/src/lcd.rs @@ -1,11 +1,17 @@ -use super::arm7tdmi::Bus; +use std::io::Cursor; +use std::io::{Seek, SeekFrom}; + +use super::arm7tdmi::{Addr, Bus}; use super::ioregs::consts::*; -use super::palette::{Palette, Rgb15}; +use super::palette::{Palette, PixelFormat, Rgb15}; use super::*; use crate::bit::BitIndex; +use crate::byteorder::{LittleEndian, ReadBytesExt}; use crate::num::FromPrimitive; +const VRAM_ADDR: Addr = 0x0600_0000; + #[derive(Debug, Primitive)] enum BGMode { BGMode0 = 0, @@ -81,7 +87,7 @@ pub struct BgControl { bg_priority: u8, character_base_block: u8, moasic: bool, - colors_palettes: bool, // 0=16/16, 1=256/1) + palette256: bool, // 0=16/16, 1=256/1) screen_base_block: u8, wraparound: bool, screen_width: usize, @@ -99,9 +105,9 @@ impl From for BgControl { }; BgControl { bg_priority: v.bit_range(0..2) as u8, - character_base_block: v.bit_range(2..3) as u8, + character_base_block: v.bit_range(2..4) as u8, moasic: v.bit(6), - colors_palettes: v.bit(7), // 0=16/16, 1=256/1) + palette256: v.bit(7), screen_base_block: v.bit_range(8..13) as u8, wraparound: v.bit(13), screen_width: width, @@ -110,6 +116,24 @@ impl From for BgControl { } } +impl BgControl { + pub fn char_block(&self) -> Addr { + VRAM_ADDR + (self.character_base_block as u32) * 0x4000 + } + + pub fn screen_block(&self) -> Addr { + VRAM_ADDR + (self.screen_base_block as u32) * 0x800 + } + + pub fn tile_format(&self) -> (u32, PixelFormat) { + if self.palette256 { + (2 * Lcd::TILE_SIZE, PixelFormat::BPP8) + } else { + (Lcd::TILE_SIZE, PixelFormat::BPP4) + } + } +} + #[derive(Debug, PartialEq, Copy, Clone)] pub enum LcdState { HDraw = 0, @@ -123,11 +147,11 @@ impl Default for LcdState { } use LcdState::*; -#[derive(Debug, Default)] pub struct Lcd { cycles: usize, - state: LcdState, - current_scanline: usize, // VCOUNT + pub pixeldata: [Rgb15; 256 * 256], + pub state: LcdState, + pub current_scanline: usize, // VCOUNT } impl Lcd { @@ -141,10 +165,14 @@ impl Lcd { pub const CYCLES_VDRAW: usize = 197120; pub const CYCLES_VBLANK: usize = 83776; + pub const TILE_SIZE: u32 = 0x20; + pub fn new() -> Lcd { Lcd { state: HDraw, - ..Default::default() + current_scanline: 0, + cycles: 0, + pixeldata: [Rgb15::from(0); 256 * 256], } } @@ -179,7 +207,7 @@ impl Lcd { let mut v = dispstat.raw_value; v.set_bit(1, false); v.set_bit(0, true); - self.state = HBlank; + self.state = VBlank; sysbus.ioregs.write_reg(REG_DISPSTAT, v); if dispstat.vblank_irq_enable { @@ -193,88 +221,206 @@ impl Lcd { self.state = HDraw; } - fn bgcnt(&self, sysbus: &SysBus, bg: u32) -> BgControl { + fn bgcnt(&self, bg: u32, sysbus: &SysBus) -> BgControl { BgControl::from(sysbus.ioregs.read_reg(REG_BG0CNT + 2 * bg)) } - pub fn render(&mut self, sysbus: &SysBus) { - // let dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT)); - // let dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT)); + fn bgofs(&self, bg: u32, sysbus: &SysBus) -> (u32, u32) { + let hofs = (sysbus.ioregs.read_reg(REG_BG0HOFS + 4 * bg) & 0x1ff) as u32; + let vofs = (sysbus.ioregs.read_reg(REG_BG0VOFS + 4 * bg) & 0x1ff) as u32; + (hofs, vofs) + } - // TODO - redner + /// helper method that reads the palette index from a base address and x + y + pub fn read_pixel_index( + &self, + sysbus: &SysBus, + addr: Addr, + x: u32, + y: u32, + width: u32, + format: PixelFormat, + ) -> usize { + match format { + PixelFormat::BPP4 => { + let byte = sysbus.read_8(addr + width * y + x / 2); + if x & 1 != 0 { + (byte >> 4) as usize + } else { + (byte & 0xf) as usize + } + } + PixelFormat::BPP8 => sysbus.read_8(addr + width * y + x) as usize, + } + } + + pub fn get_palette_color(&self, sysbus: &SysBus, index: u32, palette_index: u32) -> Rgb15 { + sysbus + .read_16(0x0500_0000 + 2 * index + 0x20 * palette_index) + .into() + } + + fn scanline_mode0(&mut self, bg: u32, sysbus: &mut SysBus) { + let bgcnt = self.bgcnt(bg, sysbus); + let tileset_base = bgcnt.char_block(); + let tilemap_base = bgcnt.screen_block(); + let (tile_size, pixel_format) = bgcnt.tile_format(); + + let tiles_per_row = bgcnt.screen_width / 8; + + let mut px = 0; + let py = self.current_scanline; + let tile_y = py % 8; + + for tile in 0..tiles_per_row { + let tile_y = py % 8; + + let map_addr = tilemap_base + (tile as u32) * 2; + let entry = TileMapEntry::from(sysbus.read_16(map_addr)); + let tile_addr = tileset_base + entry.tile_index * tile_size; + + for tile_x in 0..=7 { + let color = match pixel_format { + PixelFormat::BPP4 => { + let index = self.read_pixel_index( + sysbus, + tile_addr, + tile_x, + tile_y as u32, + 4, + pixel_format, + ); + self.get_palette_color(sysbus, index as u32, entry.palette_bank as u32) + } + PixelFormat::BPP8 => { + let index = self.read_pixel_index( + sysbus, + tile_addr, + tile_x, + tile_y as u32, + 8, + pixel_format, + ); + self.get_palette_color(sysbus, index as u32, 0) + } + }; + self.pixeldata[((px + tile_x) as usize) + py * 256] = color; + } + px += 8; + if px == bgcnt.screen_width as u32 { + return; + } + } + } + + pub fn scanline(&mut self, sysbus: &mut SysBus) { + let dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT)); + let dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT)); + + match dispcnt.bg_mode { + BGMode::BGMode0 | BGMode::BGMode2 => { + for bg in 0..3 { + if dispcnt.disp_bg[bg] { + self.scanline_mode0(bg as u32, sysbus); + } + } + } + _ => panic!("{:?} not supported", dispcnt.bg_mode), + } } } // *TODO* Running the Lcd step by step causes a massive performance impact, so for now not treat it as an emulated IO device. -// -// impl EmuIoDev for Lcd { -// fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> (usize, Option) { -// self.cycles += cycles; -// sysbus -// .ioregs -// .write_reg(REG_VCOUNT, self.current_scanline as u16); -// let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT)); +impl EmuIoDev for Lcd { + fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> (usize, Option) { + self.cycles += cycles; -// dispstat.vcount_flag = dispstat.vcount_setting as usize == self.current_scanline; -// if dispstat.vcount_irq_enable { -// panic!("VCOUNT IRQ NOT IMPL"); -// } + sysbus + .ioregs + .write_reg(REG_VCOUNT, self.current_scanline as u16); + let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT)); -// match self.state { -// HDraw => { -// if self.cycles > Lcd::CYCLES_HDRAW { -// self.current_scanline += 1; -// self.cycles -= Lcd::CYCLES_HDRAW; + dispstat.vcount_flag = dispstat.vcount_setting as usize == self.current_scanline; + if dispstat.vcount_irq_enable { + panic!("VCOUNT IRQ NOT IMPL"); + } -// let (new_state, irq) = if self.current_scanline < Lcd::DISPLAY_HEIGHT { -// // HBlank -// dispstat.hblank_flag = true; -// let irq = if dispstat.hblank_irq_enable { -// Some(Interrupt::LCD_HBlank) -// } else { -// None -// }; -// (HBlank, irq) -// } else { -// dispstat.vblank_flag = true; -// let irq = if dispstat.vblank_irq_enable { -// Some(Interrupt::LCD_HBlank) -// } else { -// None -// }; -// (VBlank, irq) -// }; -// self.state = new_state; -// self.update_regs(dispstat, sysbus); -// return (0, irq); -// } -// } -// HBlank => { -// if self.cycles > Lcd::CYCLES_HBLANK { -// self.cycles -= Lcd::CYCLES_HBLANK; -// self.state = HDraw; -// dispstat.hblank_flag = false; -// self.update_regs(dispstat, sysbus); -// return (0, None); -// } -// } -// VBlank => { -// if self.cycles > Lcd::CYCLES_VBLANK { -// self.cycles -= Lcd::CYCLES_VBLANK; -// self.state = HDraw; -// dispstat.vblank_flag = false; -// self.current_scanline = 0; -// self.update_regs(dispstat, sysbus); -// return (0, None); -// } -// } -// } + match self.state { + HDraw => { + if self.cycles > Lcd::CYCLES_HDRAW { + self.current_scanline += 1; + self.cycles -= Lcd::CYCLES_HDRAW; -// // let mut dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT)); -// // let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT)); + let (new_state, irq) = if self.current_scanline < Lcd::DISPLAY_HEIGHT { + self.scanline(sysbus); + // HBlank + dispstat.hblank_flag = true; + let irq = if dispstat.hblank_irq_enable { + Some(Interrupt::LCD_HBlank) + } else { + None + }; + (HBlank, irq) + } else { + dispstat.vblank_flag = true; + let irq = if dispstat.vblank_irq_enable { + Some(Interrupt::LCD_VBlank) + } else { + None + }; + (VBlank, irq) + }; + self.state = new_state; + self.update_regs(dispstat, sysbus); + return (0, irq); + } + } + HBlank => { + if self.cycles > Lcd::CYCLES_HBLANK { + self.cycles -= Lcd::CYCLES_HBLANK; + self.state = HDraw; + dispstat.hblank_flag = false; + self.update_regs(dispstat, sysbus); + return (0, None); + } + } + VBlank => { + if self.cycles > Lcd::CYCLES_VBLANK { + self.cycles -= Lcd::CYCLES_VBLANK; + self.state = HDraw; + dispstat.vblank_flag = false; + self.current_scanline = 0; + self.scanline(sysbus); + self.update_regs(dispstat, sysbus); + return (0, None); + } + } + } -// // TODO -// (0, None) -// } -// } + // let mut dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT)); + // let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT)); + + // TODO + (0, None) + } +} + +#[derive(Debug)] +struct TileMapEntry { + tile_index: u32, + x_flip: bool, + y_flip: bool, + palette_bank: usize, +} + +impl From for TileMapEntry { + fn from(t: u16) -> TileMapEntry { + TileMapEntry { + tile_index: t.bit_range(0..10) as u32, + x_flip: t.bit(10), + y_flip: t.bit(11), + palette_bank: t.bit_range(12..16) as usize, + } + } +} diff --git a/src/palette.rs b/src/palette.rs index 351a11e..59f13cc 100644 --- a/src/palette.rs +++ b/src/palette.rs @@ -3,7 +3,7 @@ use std::fmt; use byteorder::{LittleEndian, ReadBytesExt}; use std::io::Cursor; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub struct Rgb15 { pub r: u8, pub g: u8, @@ -34,6 +34,12 @@ impl Rgb15 { } } +#[derive(Debug, Primitive, Copy, Clone)] +pub enum PixelFormat { + BPP4 = 0, + BPP8 = 1, +} + pub struct Palette { pub bg_colors: [Rgb15; 256], pub fg_colors: [Rgb15; 256],