WIP mode0 rendering

Former-commit-id: 6bce375f9373bbddf4522da5ecc2ea3584373847
This commit is contained in:
Michel Heily 2019-07-15 07:35:09 +03:00
parent 9d8272b895
commit 1084be52b8
9 changed files with 476 additions and 102 deletions

12
Cargo.lock generated
View file

@ -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"

View file

@ -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
opt-level = 1
debug = true

View file

@ -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 <bg>".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)),
}
}

View file

@ -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<CpuError> for DebuggerError {
type DebuggerResult<T> = Result<T, DebuggerError>;
#[derive(Debug)]
pub struct Debugger {
pub gba: GameBoyAdvance,
running: bool,

View file

@ -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));
}
_ => {}
}
}
}
}

105
src/debugger/tile_view.rs Normal file
View file

@ -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<Color> 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<sdl2::video::Window>,
) {
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,
_ => {}
}
}
}
}

View file

@ -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<DecodedInstruction> {
@ -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)

View file

@ -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<u16> 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<u16> 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<Interrupt>) {
// 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<Interrupt>) {
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<u16> 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,
}
}
}

View file

@ -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],