WIP mode0 rendering
Former-commit-id: 6bce375f9373bbddf4522da5ecc2ea3584373847
This commit is contained in:
parent
9d8272b895
commit
1084be52b8
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -566,6 +566,7 @@ dependencies = [
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
|
@ -719,6 +720,16 @@ dependencies = [
|
||||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.3.0"
|
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 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 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 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-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-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"
|
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||||
|
|
|
@ -17,6 +17,8 @@ colored = "1.8"
|
||||||
ansi_term = "0.11.0"
|
ansi_term = "0.11.0"
|
||||||
hexdump = "0.1.0"
|
hexdump = "0.1.0"
|
||||||
sdl2 = "0.32.2"
|
sdl2 = "0.32.2"
|
||||||
|
time = "0.1.42"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 1
|
||||||
|
debug = true
|
|
@ -6,6 +6,8 @@ use crate::lcd::*;
|
||||||
use crate::GBAError;
|
use crate::GBAError;
|
||||||
|
|
||||||
use super::palette_view::create_palette_view;
|
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 super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
|
||||||
|
|
||||||
use ansi_term::Colour;
|
use ansi_term::Colour;
|
||||||
|
@ -26,11 +28,13 @@ pub enum Command {
|
||||||
Step(usize),
|
Step(usize),
|
||||||
Continue,
|
Continue,
|
||||||
Frame(usize),
|
Frame(usize),
|
||||||
|
Render,
|
||||||
HexDump(Addr, usize),
|
HexDump(Addr, usize),
|
||||||
Disass(DisassMode, Addr, usize),
|
Disass(DisassMode, Addr, usize),
|
||||||
AddBreakpoint(Addr),
|
AddBreakpoint(Addr),
|
||||||
DelBreakpoint(Addr),
|
DelBreakpoint(Addr),
|
||||||
PaletteView,
|
PaletteView,
|
||||||
|
TileView(u32),
|
||||||
ClearBreakpoints,
|
ClearBreakpoints,
|
||||||
ListBreakpoints,
|
ListBreakpoints,
|
||||||
Reset,
|
Reset,
|
||||||
|
@ -43,7 +47,6 @@ impl Command {
|
||||||
match *self {
|
match *self {
|
||||||
Info => println!("{}", debugger.gba.cpu),
|
Info => println!("{}", debugger.gba.cpu),
|
||||||
DisplayInfo => {
|
DisplayInfo => {
|
||||||
println!("{:?}", debugger.gba.lcd);
|
|
||||||
println!(
|
println!(
|
||||||
"DISPCNT: {:#?}",
|
"DISPCNT: {:#?}",
|
||||||
DisplayControl::from(debugger.gba.sysbus.ioregs.read_reg(REG_DISPCNT))
|
DisplayControl::from(debugger.gba.sysbus.ioregs.read_reg(REG_DISPCNT))
|
||||||
|
@ -56,6 +59,11 @@ impl Command {
|
||||||
"VCOUNT: {:?}",
|
"VCOUNT: {:?}",
|
||||||
debugger.gba.sysbus.ioregs.read_reg(REG_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) => {
|
Step(count) => {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
|
@ -115,11 +123,15 @@ impl Command {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
Frame(count) => {
|
Frame(count) => {
|
||||||
|
use super::time::PreciseTime;
|
||||||
|
let start = PreciseTime::now();
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
debugger.gba.frame();
|
debugger.gba.frame();
|
||||||
print!(".")
|
|
||||||
}
|
}
|
||||||
|
let end = PreciseTime::now();
|
||||||
|
println!("that took {} seconds", start.to(end));
|
||||||
}
|
}
|
||||||
|
Render => create_render_view(&debugger.gba),
|
||||||
HexDump(addr, nbytes) => {
|
HexDump(addr, nbytes) => {
|
||||||
let bytes = debugger.gba.sysbus.get_bytes(addr);
|
let bytes = debugger.gba.sysbus.get_bytes(addr);
|
||||||
hexdump::hexdump(&bytes[0..nbytes]);
|
hexdump::hexdump(&bytes[0..nbytes]);
|
||||||
|
@ -166,6 +178,7 @@ impl Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaletteView => create_palette_view(debugger.gba.sysbus.get_bytes(0x0500_0000)),
|
PaletteView => create_palette_view(debugger.gba.sysbus.get_bytes(0x0500_0000)),
|
||||||
|
TileView(bg) => create_tile_view(bg, &debugger.gba),
|
||||||
Reset => {
|
Reset => {
|
||||||
println!("resetting cpu...");
|
println!("resetting cpu...");
|
||||||
debugger.gba.cpu.reset();
|
debugger.gba.cpu.reset();
|
||||||
|
@ -308,9 +321,17 @@ impl Debugger {
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
"palette-view" => Ok(Command::PaletteView),
|
"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),
|
"bl" => Ok(Command::ListBreakpoints),
|
||||||
"q" | "quit" => Ok(Command::Quit),
|
"q" | "quit" => Ok(Command::Quit),
|
||||||
"r" | "reset" => Ok(Command::Reset),
|
"r" | "reset" => Ok(Command::Reset),
|
||||||
|
"rd" | "render" => Ok(Command::Render),
|
||||||
_ => Err(DebuggerError::InvalidCommand(command)),
|
_ => Err(DebuggerError::InvalidCommand(command)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ mod command;
|
||||||
use command::Command;
|
use command::Command;
|
||||||
|
|
||||||
mod palette_view;
|
mod palette_view;
|
||||||
|
mod render_view;
|
||||||
|
mod tile_view;
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DebuggerError {
|
pub enum DebuggerError {
|
||||||
|
@ -31,7 +34,6 @@ impl From<CpuError> for DebuggerError {
|
||||||
|
|
||||||
type DebuggerResult<T> = Result<T, DebuggerError>;
|
type DebuggerResult<T> = Result<T, DebuggerError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Debugger {
|
pub struct Debugger {
|
||||||
pub gba: GameBoyAdvance,
|
pub gba: GameBoyAdvance,
|
||||||
running: bool,
|
running: bool,
|
||||||
|
|
50
src/debugger/render_view.rs
Normal file
50
src/debugger/render_view.rs
Normal 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
105
src/debugger/tile_view.rs
Normal 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,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
src/gba.rs
62
src/gba.rs
|
@ -1,15 +1,15 @@
|
||||||
/// Struct containing everything
|
/// Struct containing everything
|
||||||
///
|
///
|
||||||
use super::arm7tdmi::{Core, DecodedInstruction};
|
use super::arm7tdmi::{exception::*, Core, DecodedInstruction};
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::dma::DmaChannel;
|
use super::dma::DmaChannel;
|
||||||
|
use super::interrupt::*;
|
||||||
use super::ioregs::consts::*;
|
use super::ioregs::consts::*;
|
||||||
use super::lcd::Lcd;
|
use super::lcd::*;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
use super::{EmuIoDev, GBAResult};
|
use super::{EmuIoDev, GBAError, GBAResult};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GameBoyAdvance {
|
pub struct GameBoyAdvance {
|
||||||
pub cpu: Core,
|
pub cpu: Core,
|
||||||
pub sysbus: SysBus,
|
pub sysbus: SysBus,
|
||||||
|
@ -42,26 +42,52 @@ impl GameBoyAdvance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_cpu_for_n_cycles(&mut self, n: usize) {
|
fn emulate_n_cycles(&mut self, mut n: usize) {
|
||||||
let previous_cycles = self.cpu.cycles;
|
let mut cycles = 0;
|
||||||
loop {
|
loop {
|
||||||
|
let previous_cycles = self.cpu.cycles;
|
||||||
self.cpu.step_one(&mut self.sysbus).unwrap();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame(&mut self) {
|
pub fn frame(&mut self) {
|
||||||
for _ in 0..Lcd::DISPLAY_HEIGHT {
|
while self.lcd.state == LcdState::VBlank {
|
||||||
self.run_cpu_for_n_cycles(Lcd::CYCLES_HDRAW);
|
self.emulate();
|
||||||
let _irq = self.lcd.set_hblank(&mut self.sysbus);
|
|
||||||
self.run_cpu_for_n_cycles(Lcd::CYCLES_HBLANK);
|
|
||||||
}
|
}
|
||||||
let _irq = self.lcd.set_vblank(&mut self.sysbus);
|
while self.lcd.state != LcdState::VBlank {
|
||||||
self.run_cpu_for_n_cycles(Lcd::CYCLES_VBLANK);
|
self.emulate();
|
||||||
self.lcd.render(&mut self.sysbus); // Currently not implemented
|
}
|
||||||
self.lcd.set_hdraw();
|
}
|
||||||
|
|
||||||
|
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> {
|
pub fn step(&mut self) -> GBAResult<DecodedInstruction> {
|
||||||
|
@ -84,7 +110,11 @@ impl GameBoyAdvance {
|
||||||
// let (dma_cycles, _) = self.dma3.step(cycles, &mut self.sysbus);
|
// let (dma_cycles, _) = self.dma3.step(cycles, &mut self.sysbus);
|
||||||
// cycles += dma_cycles;
|
// 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;
|
// cycles += lcd_cycles;
|
||||||
|
|
||||||
Ok(executed_insn)
|
Ok(executed_insn)
|
||||||
|
|
308
src/lcd.rs
308
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::ioregs::consts::*;
|
||||||
use super::palette::{Palette, Rgb15};
|
use super::palette::{Palette, PixelFormat, Rgb15};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::bit::BitIndex;
|
use crate::bit::BitIndex;
|
||||||
|
use crate::byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use crate::num::FromPrimitive;
|
use crate::num::FromPrimitive;
|
||||||
|
|
||||||
|
const VRAM_ADDR: Addr = 0x0600_0000;
|
||||||
|
|
||||||
#[derive(Debug, Primitive)]
|
#[derive(Debug, Primitive)]
|
||||||
enum BGMode {
|
enum BGMode {
|
||||||
BGMode0 = 0,
|
BGMode0 = 0,
|
||||||
|
@ -81,7 +87,7 @@ pub struct BgControl {
|
||||||
bg_priority: u8,
|
bg_priority: u8,
|
||||||
character_base_block: u8,
|
character_base_block: u8,
|
||||||
moasic: bool,
|
moasic: bool,
|
||||||
colors_palettes: bool, // 0=16/16, 1=256/1)
|
palette256: bool, // 0=16/16, 1=256/1)
|
||||||
screen_base_block: u8,
|
screen_base_block: u8,
|
||||||
wraparound: bool,
|
wraparound: bool,
|
||||||
screen_width: usize,
|
screen_width: usize,
|
||||||
|
@ -99,9 +105,9 @@ impl From<u16> for BgControl {
|
||||||
};
|
};
|
||||||
BgControl {
|
BgControl {
|
||||||
bg_priority: v.bit_range(0..2) as u8,
|
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),
|
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,
|
screen_base_block: v.bit_range(8..13) as u8,
|
||||||
wraparound: v.bit(13),
|
wraparound: v.bit(13),
|
||||||
screen_width: width,
|
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)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum LcdState {
|
pub enum LcdState {
|
||||||
HDraw = 0,
|
HDraw = 0,
|
||||||
|
@ -123,11 +147,11 @@ impl Default for LcdState {
|
||||||
}
|
}
|
||||||
use LcdState::*;
|
use LcdState::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Lcd {
|
pub struct Lcd {
|
||||||
cycles: usize,
|
cycles: usize,
|
||||||
state: LcdState,
|
pub pixeldata: [Rgb15; 256 * 256],
|
||||||
current_scanline: usize, // VCOUNT
|
pub state: LcdState,
|
||||||
|
pub current_scanline: usize, // VCOUNT
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lcd {
|
impl Lcd {
|
||||||
|
@ -141,10 +165,14 @@ impl Lcd {
|
||||||
pub const CYCLES_VDRAW: usize = 197120;
|
pub const CYCLES_VDRAW: usize = 197120;
|
||||||
pub const CYCLES_VBLANK: usize = 83776;
|
pub const CYCLES_VBLANK: usize = 83776;
|
||||||
|
|
||||||
|
pub const TILE_SIZE: u32 = 0x20;
|
||||||
|
|
||||||
pub fn new() -> Lcd {
|
pub fn new() -> Lcd {
|
||||||
Lcd {
|
Lcd {
|
||||||
state: HDraw,
|
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;
|
let mut v = dispstat.raw_value;
|
||||||
v.set_bit(1, false);
|
v.set_bit(1, false);
|
||||||
v.set_bit(0, true);
|
v.set_bit(0, true);
|
||||||
self.state = HBlank;
|
self.state = VBlank;
|
||||||
sysbus.ioregs.write_reg(REG_DISPSTAT, v);
|
sysbus.ioregs.write_reg(REG_DISPSTAT, v);
|
||||||
|
|
||||||
if dispstat.vblank_irq_enable {
|
if dispstat.vblank_irq_enable {
|
||||||
|
@ -193,88 +221,206 @@ impl Lcd {
|
||||||
self.state = HDraw;
|
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))
|
BgControl::from(sysbus.ioregs.read_reg(REG_BG0CNT + 2 * bg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, sysbus: &SysBus) {
|
fn bgofs(&self, bg: u32, sysbus: &SysBus) -> (u32, u32) {
|
||||||
// let dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT));
|
let hofs = (sysbus.ioregs.read_reg(REG_BG0HOFS + 4 * bg) & 0x1ff) as u32;
|
||||||
// let dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT));
|
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.
|
// *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
|
impl EmuIoDev for Lcd {
|
||||||
// .ioregs
|
fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> (usize, Option<Interrupt>) {
|
||||||
// .write_reg(REG_VCOUNT, self.current_scanline as u16);
|
self.cycles += cycles;
|
||||||
// let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT));
|
|
||||||
|
|
||||||
// dispstat.vcount_flag = dispstat.vcount_setting as usize == self.current_scanline;
|
sysbus
|
||||||
// if dispstat.vcount_irq_enable {
|
.ioregs
|
||||||
// panic!("VCOUNT IRQ NOT IMPL");
|
.write_reg(REG_VCOUNT, self.current_scanline as u16);
|
||||||
// }
|
let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT));
|
||||||
|
|
||||||
// match self.state {
|
dispstat.vcount_flag = dispstat.vcount_setting as usize == self.current_scanline;
|
||||||
// HDraw => {
|
if dispstat.vcount_irq_enable {
|
||||||
// if self.cycles > Lcd::CYCLES_HDRAW {
|
panic!("VCOUNT IRQ NOT IMPL");
|
||||||
// self.current_scanline += 1;
|
}
|
||||||
// self.cycles -= Lcd::CYCLES_HDRAW;
|
|
||||||
|
|
||||||
// let (new_state, irq) = if self.current_scanline < Lcd::DISPLAY_HEIGHT {
|
match self.state {
|
||||||
// // HBlank
|
HDraw => {
|
||||||
// dispstat.hblank_flag = true;
|
if self.cycles > Lcd::CYCLES_HDRAW {
|
||||||
// let irq = if dispstat.hblank_irq_enable {
|
self.current_scanline += 1;
|
||||||
// Some(Interrupt::LCD_HBlank)
|
self.cycles -= Lcd::CYCLES_HDRAW;
|
||||||
// } 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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // let mut dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT));
|
let (new_state, irq) = if self.current_scanline < Lcd::DISPLAY_HEIGHT {
|
||||||
// // let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT));
|
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
|
// let mut dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT));
|
||||||
// (0, None)
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
pub struct Rgb15 {
|
pub struct Rgb15 {
|
||||||
pub r: u8,
|
pub r: u8,
|
||||||
pub g: 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 struct Palette {
|
||||||
pub bg_colors: [Rgb15; 256],
|
pub bg_colors: [Rgb15; 256],
|
||||||
pub fg_colors: [Rgb15; 256],
|
pub fg_colors: [Rgb15; 256],
|
||||||
|
|
Reference in a new issue