gpu: refactor: Big refactor preparations
1) Decouple SysBus from Gpu 2) Split Gpu rendering function into separate modules 3) Cleanup Former-commit-id: 0435ad1c9c1de72ed50769fabfea7c5f33b670e0
This commit is contained in:
parent
885bce2aa4
commit
b00fbfb38c
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||||
default-run= "rba-sdl2"
|
default-run= "rba-sdl2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "*"
|
byteorder = "1"
|
||||||
num = "0.2.0"
|
num = "0.2.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
enum-primitive-derive = "^0.1"
|
enum-primitive-derive = "^0.1"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::bit::BitIndex;
|
use crate::bit::BitIndex;
|
||||||
|
|
||||||
use super::super::alu::*;
|
use super::super::alu::*;
|
||||||
use crate::core::arm7tdmi::bus::Bus;
|
|
||||||
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||||
use crate::core::arm7tdmi::psr::RegPSR;
|
use crate::core::arm7tdmi::psr::RegPSR;
|
||||||
use crate::core::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, REG_LR, REG_PC};
|
use crate::core::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, REG_LR, REG_PC};
|
||||||
use crate::core::sysbus::SysBus;
|
use crate::core::sysbus::SysBus;
|
||||||
|
use crate::core::Bus;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@ use ansi_term::{Colour, Style};
|
||||||
|
|
||||||
pub use super::exception::Exception;
|
pub use super::exception::Exception;
|
||||||
use super::{
|
use super::{
|
||||||
arm::*, bus::Bus, psr::RegPSR, reg_string, thumb::ThumbInstruction, Addr, CpuMode, CpuResult,
|
arm::*, psr::RegPSR, reg_string, thumb::ThumbInstruction, Addr, CpuMode, CpuResult, CpuState,
|
||||||
CpuState, DecodedInstruction, InstructionDecoder,
|
DecodedInstruction, InstructionDecoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::sysbus::{
|
use crate::core::bus::Bus;
|
||||||
|
use crate::core::sysbus::{
|
||||||
MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus,
|
MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,6 @@ pub mod cpu;
|
||||||
pub use cpu::*;
|
pub use cpu::*;
|
||||||
pub mod alu;
|
pub mod alu;
|
||||||
pub use alu::*;
|
pub use alu::*;
|
||||||
pub mod bus;
|
|
||||||
pub use bus::*;
|
|
||||||
pub mod exception;
|
pub mod exception;
|
||||||
pub mod psr;
|
pub mod psr;
|
||||||
|
|
||||||
|
@ -21,7 +19,7 @@ pub const REG_PC: usize = 15;
|
||||||
pub const REG_LR: usize = 14;
|
pub const REG_LR: usize = 14;
|
||||||
pub const REG_SP: usize = 13;
|
pub const REG_SP: usize = 13;
|
||||||
|
|
||||||
pub type Addr = u32;
|
pub(self) use crate::core::{Addr, Bus};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum DecodedInstruction {
|
pub enum DecodedInstruction {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::core::arm7tdmi::bus::Bus;
|
|
||||||
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||||
use crate::core::arm7tdmi::*;
|
use crate::core::arm7tdmi::*;
|
||||||
use crate::core::sysbus::SysBus;
|
use crate::core::sysbus::SysBus;
|
||||||
|
use crate::core::Bus;
|
||||||
|
|
||||||
use crate::bit::BitIndex;
|
use crate::bit::BitIndex;
|
||||||
|
|
||||||
|
|
|
@ -343,9 +343,10 @@ impl ThumbInstruction {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// All instructions constants were generated using an ARM assembler.
|
/// All instructions constants were generated using an ARM assembler.
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::super::Core;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::core::arm7tdmi::{Bus, Core};
|
|
||||||
use crate::core::sysbus::BoxedMemory;
|
use crate::core::sysbus::BoxedMemory;
|
||||||
|
use crate::core::Bus;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mov_low_reg() {
|
fn mov_low_reg() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::Addr;
|
pub type Addr = u32;
|
||||||
|
|
||||||
pub trait Bus {
|
pub trait Bus {
|
||||||
fn read_32(&self, addr: Addr) -> u32;
|
fn read_32(&self, addr: Addr) -> u32;
|
|
@ -8,8 +8,7 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
||||||
use super::super::util::read_bin_file;
|
use super::super::util::read_bin_file;
|
||||||
use super::arm7tdmi::{bus::Bus, Addr};
|
use super::{Addr, Bus, GBAResult};
|
||||||
use super::{GBAError, GBAResult};
|
|
||||||
|
|
||||||
/// From GBATEK
|
/// From GBATEK
|
||||||
///
|
///
|
||||||
|
|
|
@ -2,10 +2,9 @@ extern crate bit_set;
|
||||||
|
|
||||||
use bit_set::BitSet;
|
use bit_set::BitSet;
|
||||||
|
|
||||||
use super::arm7tdmi::{Addr, Bus};
|
|
||||||
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
|
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
use super::{Interrupt, IrqBitmask};
|
use super::{Addr, Bus, Interrupt, IrqBitmask};
|
||||||
|
|
||||||
use num::FromPrimitive;
|
use num::FromPrimitive;
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ impl GameBoyAdvance {
|
||||||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||||
input_device: Rc<RefCell<dyn InputInterface>>,
|
input_device: Rc<RefCell<dyn InputInterface>>,
|
||||||
) -> GameBoyAdvance {
|
) -> GameBoyAdvance {
|
||||||
let gpu = Gpu::new(video_device.clone());
|
let gpu = Box::new(Gpu::new(video_device.clone()));
|
||||||
let sound_controller = SoundController::new(audio_device.clone());
|
let sound_controller = Box::new(SoundController::new(audio_device.clone()));
|
||||||
let io = IoDevices::new(gpu, sound_controller);
|
let io = IoDevices::new(gpu, sound_controller);
|
||||||
GameBoyAdvance {
|
GameBoyAdvance {
|
||||||
cpu: cpu,
|
cpu: cpu,
|
||||||
|
@ -48,6 +48,7 @@ impl GameBoyAdvance {
|
||||||
|
|
||||||
pub fn frame(&mut self) {
|
pub fn frame(&mut self) {
|
||||||
self.key_poll();
|
self.key_poll();
|
||||||
|
self.sysbus.io.gpu.clear();
|
||||||
while self.sysbus.io.gpu.vcount != DISPLAY_HEIGHT {
|
while self.sysbus.io.gpu.vcount != DISPLAY_HEIGHT {
|
||||||
self.step();
|
self.step();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,48 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::arm7tdmi::{Addr, Bus};
|
use super::super::VideoInterface;
|
||||||
use super::*;
|
use super::interrupt::IrqBitmask;
|
||||||
use crate::VideoInterface;
|
use super::sysbus::{BoxedMemory, SysBus};
|
||||||
|
use super::Bus;
|
||||||
|
|
||||||
use crate::bitfield::Bit;
|
use crate::bitfield::Bit;
|
||||||
use crate::num::FromPrimitive;
|
use crate::num::FromPrimitive;
|
||||||
|
|
||||||
|
mod render;
|
||||||
|
|
||||||
mod mosaic;
|
mod mosaic;
|
||||||
mod obj;
|
|
||||||
use obj::ObjInfo;
|
|
||||||
mod sfx;
|
|
||||||
mod rgb15;
|
mod rgb15;
|
||||||
|
mod sfx;
|
||||||
pub use rgb15::Rgb15;
|
pub use rgb15::Rgb15;
|
||||||
|
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
pub use regs::*;
|
pub use regs::*;
|
||||||
|
|
||||||
pub const VRAM_ADDR: Addr = 0x0600_0000;
|
#[allow(unused)]
|
||||||
|
pub mod consts {
|
||||||
|
pub const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||||
|
pub const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
||||||
|
pub const OAM_SIZE: usize = 1 * 1024;
|
||||||
|
|
||||||
|
pub const VRAM_ADDR: u32 = 0x0600_0000;
|
||||||
pub const DISPLAY_WIDTH: usize = 240;
|
pub const DISPLAY_WIDTH: usize = 240;
|
||||||
pub const DISPLAY_HEIGHT: usize = 160;
|
pub const DISPLAY_HEIGHT: usize = 160;
|
||||||
pub const VBLANK_LINES: usize = 68;
|
pub const VBLANK_LINES: usize = 68;
|
||||||
|
|
||||||
const CYCLES_PIXEL: usize = 4;
|
pub(super) const CYCLES_PIXEL: usize = 4;
|
||||||
const CYCLES_HDRAW: usize = 960;
|
pub(super) const CYCLES_HDRAW: usize = 960;
|
||||||
const CYCLES_HBLANK: usize = 272;
|
pub(super) const CYCLES_HBLANK: usize = 272;
|
||||||
const CYCLES_SCANLINE: usize = 1232;
|
pub(super) const CYCLES_SCANLINE: usize = 1232;
|
||||||
const CYCLES_VDRAW: usize = 197120;
|
pub(super) const CYCLES_VDRAW: usize = 197120;
|
||||||
const CYCLES_VBLANK: usize = 83776;
|
pub(super) const CYCLES_VBLANK: usize = 83776;
|
||||||
|
|
||||||
pub const TILE_SIZE: u32 = 0x20;
|
pub const TILE_SIZE: u32 = 0x20;
|
||||||
|
}
|
||||||
|
pub use self::consts::*;
|
||||||
|
|
||||||
|
pub type FrameBuffer<T> = [T; DISPLAY_WIDTH * DISPLAY_HEIGHT];
|
||||||
|
|
||||||
#[derive(Debug, Primitive, Copy, Clone)]
|
#[derive(Debug, Primitive, Copy, Clone)]
|
||||||
pub enum PixelFormat {
|
pub enum PixelFormat {
|
||||||
|
@ -146,6 +156,23 @@ pub struct BgAffine {
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ObjBufferEntry {
|
||||||
|
pub(super) color: Rgb15,
|
||||||
|
pub(super) priority: u16,
|
||||||
|
pub(super) window: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ObjBufferEntry {
|
||||||
|
fn default() -> ObjBufferEntry {
|
||||||
|
ObjBufferEntry {
|
||||||
|
window: false,
|
||||||
|
color: Rgb15::TRANSPARENT,
|
||||||
|
priority: 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
|
type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
|
||||||
|
|
||||||
#[derive(DebugStub)]
|
#[derive(DebugStub)]
|
||||||
|
@ -173,11 +200,15 @@ pub struct Gpu {
|
||||||
pub bldalpha: BlendAlpha,
|
pub bldalpha: BlendAlpha,
|
||||||
pub bldy: u16,
|
pub bldy: u16,
|
||||||
|
|
||||||
|
pub palette_ram: BoxedMemory,
|
||||||
|
pub vram: BoxedMemory,
|
||||||
|
pub oam: BoxedMemory,
|
||||||
|
|
||||||
#[debug_stub = "Sprite Buffer"]
|
#[debug_stub = "Sprite Buffer"]
|
||||||
pub obj_buffer: [ObjInfo; DISPLAY_WIDTH],
|
pub obj_buffer: FrameBuffer<ObjBufferEntry>,
|
||||||
|
|
||||||
#[debug_stub = "Frame Buffer"]
|
#[debug_stub = "Frame Buffer"]
|
||||||
pub frame_buffer: [u32; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
pub(super) frame_buffer: FrameBuffer<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gpu {
|
impl Gpu {
|
||||||
|
@ -201,210 +232,95 @@ impl Gpu {
|
||||||
state: HDraw,
|
state: HDraw,
|
||||||
vcount: 0,
|
vcount: 0,
|
||||||
cycles: 0,
|
cycles: 0,
|
||||||
obj_buffer: [Default::default(); DISPLAY_WIDTH],
|
|
||||||
|
palette_ram: BoxedMemory::new(vec![0; PALETTE_RAM_SIZE].into_boxed_slice()),
|
||||||
|
vram: BoxedMemory::new(vec![0; VIDEO_RAM_SIZE].into_boxed_slice()),
|
||||||
|
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice()),
|
||||||
|
|
||||||
|
obj_buffer: [Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
||||||
frame_buffer: [0; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
frame_buffer: [0; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// helper method that reads the palette index from a base address and x + y
|
/// helper method that reads the palette index from a base address and x + y
|
||||||
pub fn read_pixel_index(
|
pub fn read_pixel_index(&self, addr: u32, x: u32, y: u32, format: PixelFormat) -> usize {
|
||||||
&self,
|
|
||||||
sb: &SysBus,
|
|
||||||
addr: Addr,
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
format: PixelFormat,
|
|
||||||
) -> usize {
|
|
||||||
let ofs = addr - VRAM_ADDR;
|
let ofs = addr - VRAM_ADDR;
|
||||||
match format {
|
match format {
|
||||||
PixelFormat::BPP4 => {
|
PixelFormat::BPP4 => {
|
||||||
let byte = sb.vram.read_8(ofs + index2d!(Addr, x / 2, y, 4));
|
let byte = self.vram.read_8(ofs + index2d!(u32, x / 2, y, 4));
|
||||||
if x & 1 != 0 {
|
if x & 1 != 0 {
|
||||||
(byte >> 4) as usize
|
(byte >> 4) as usize
|
||||||
} else {
|
} else {
|
||||||
(byte & 0xf) as usize
|
(byte & 0xf) as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelFormat::BPP8 => sb.vram.read_8(ofs + index2d!(Addr, x, y, 8)) as usize,
|
PixelFormat::BPP8 => self.vram.read_8(ofs + index2d!(u32, x, y, 8)) as usize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_palette_color(
|
pub fn get_palette_color(&self, index: u32, palette_index: u32, offset: u32) -> Rgb15 {
|
||||||
&self,
|
|
||||||
sb: &SysBus,
|
|
||||||
index: u32,
|
|
||||||
palette_index: u32,
|
|
||||||
offset: u32,
|
|
||||||
) -> Rgb15 {
|
|
||||||
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
||||||
return Rgb15::TRANSPARENT;
|
return Rgb15::TRANSPARENT;
|
||||||
}
|
}
|
||||||
Rgb15(
|
Rgb15(
|
||||||
sb.palette_ram
|
self.palette_ram
|
||||||
.read_16(offset + 2 * index + 0x20 * palette_index),
|
.read_16(offset + 2 * index + 0x20 * palette_index),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_pixel(&mut self, x: i32, y: i32, p: Rgb15) {
|
pub(super) fn obj_buffer_get(&self, x: usize, y: usize) -> &ObjBufferEntry {
|
||||||
|
&self.obj_buffer[index2d!(x, y, DISPLAY_WIDTH)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn obj_buffer_get_mut(&mut self, x: usize, y: usize) -> &mut ObjBufferEntry {
|
||||||
|
&mut self.obj_buffer[index2d!(x, y, DISPLAY_WIDTH)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn render_pixel(&mut self, x: i32, y: i32, p: Rgb15) {
|
||||||
self.frame_buffer[index2d!(usize, x, y, DISPLAY_WIDTH)] = p.to_rgb24();
|
self.frame_buffer[index2d!(usize, x, y, DISPLAY_WIDTH)] = p.to_rgb24();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scanline_reg_bg(&mut self, bg: usize, sb: &mut SysBus) {
|
pub fn render_scanline(&mut self) {
|
||||||
let (h_ofs, v_ofs) = (self.bg[bg].bghofs as u32, self.bg[bg].bgvofs as u32);
|
|
||||||
let tileset_base = self.bg[bg].bgcnt.char_block();
|
|
||||||
let tilemap_base = self.bg[bg].bgcnt.screen_block();
|
|
||||||
let (tile_size, pixel_format) = self.bg[bg].bgcnt.tile_format();
|
|
||||||
|
|
||||||
let (bg_width, bg_height) = self.bg[bg].bgcnt.size_regular();
|
|
||||||
|
|
||||||
let screen_y = self.vcount as u32;
|
|
||||||
let mut screen_x = 0;
|
|
||||||
|
|
||||||
// calculate the bg coords at the top-left corner, including wraparound
|
|
||||||
let bg_x = (screen_x + h_ofs) % bg_width;
|
|
||||||
let bg_y = (screen_y + v_ofs) % bg_height;
|
|
||||||
|
|
||||||
// calculate the initial screen entry index
|
|
||||||
// | (256,256) | (512,256) | (256,512) | (512,512) |
|
|
||||||
// |-----------|-----------|-------------|-----------|
|
|
||||||
// | | | [1] | [2][3] |
|
|
||||||
// | [0] | [0][1] | [0] | [0][1] |
|
|
||||||
// |___________|___________|_____________|___________|
|
|
||||||
//
|
|
||||||
let mut sbb = match (bg_width, bg_height) {
|
|
||||||
(256, 256) => 0,
|
|
||||||
(512, 256) => bg_x / 256,
|
|
||||||
(256, 512) => bg_y / 256,
|
|
||||||
(512, 512) => index2d!(u32, bg_x / 256, bg_y / 256, 2),
|
|
||||||
_ => unreachable!(),
|
|
||||||
} as u32;
|
|
||||||
|
|
||||||
let mut se_row = (bg_x / 8) % 32;
|
|
||||||
let se_column = (bg_y / 8) % 32;
|
|
||||||
|
|
||||||
// this will be non-zero if the h-scroll lands in a middle of a tile
|
|
||||||
let mut start_tile_x = bg_x % 8;
|
|
||||||
let tile_py = (bg_y % 8) as u32;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut map_addr =
|
|
||||||
tilemap_base + SCREEN_BLOCK_SIZE * sbb + 2 * index2d!(u32, se_row, se_column, 32);
|
|
||||||
for _ in se_row..32 {
|
|
||||||
let entry = TileMapEntry(sb.vram.read_16(map_addr - VRAM_ADDR));
|
|
||||||
let tile_addr = tileset_base + entry.tile_index() * tile_size;
|
|
||||||
|
|
||||||
for tile_px in start_tile_x..8 {
|
|
||||||
let index = self.read_pixel_index(
|
|
||||||
sb,
|
|
||||||
tile_addr,
|
|
||||||
if entry.x_flip() { 7 - tile_px } else { tile_px },
|
|
||||||
if entry.y_flip() { 7 - tile_py } else { tile_py },
|
|
||||||
pixel_format,
|
|
||||||
);
|
|
||||||
let palette_bank = match pixel_format {
|
|
||||||
PixelFormat::BPP4 => entry.palette_bank() as u32,
|
|
||||||
PixelFormat::BPP8 => 0u32,
|
|
||||||
};
|
|
||||||
let color = self.get_palette_color(sb, index as u32, palette_bank, 0);
|
|
||||||
self.bg[bg].line[screen_x as usize] = color;
|
|
||||||
screen_x += 1;
|
|
||||||
if (DISPLAY_WIDTH as u32) == screen_x {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
start_tile_x = 0;
|
|
||||||
map_addr += 2;
|
|
||||||
}
|
|
||||||
se_row = 0;
|
|
||||||
if bg_width == 512 {
|
|
||||||
sbb = sbb ^ 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scanline_aff_bg(&mut self, bg: usize, sb: &mut SysBus) {
|
|
||||||
// TODO
|
|
||||||
self.bg[bg].line = Scanline::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scanline_mode3(&mut self, bg: usize, sb: &mut SysBus) {
|
|
||||||
let y = self.vcount;
|
|
||||||
|
|
||||||
for x in 0..DISPLAY_WIDTH {
|
|
||||||
let pixel_index = index2d!(u32, x, y, DISPLAY_WIDTH);
|
|
||||||
let pixel_ofs = 2 * pixel_index;
|
|
||||||
let color = Rgb15(sb.vram.read_16(pixel_ofs));
|
|
||||||
self.bg[bg].line[x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scanline_mode4(&mut self, bg: usize, sb: &mut SysBus) {
|
|
||||||
let page_ofs: u32 = match self.dispcnt.display_frame() {
|
|
||||||
0 => 0x0600_0000 - VRAM_ADDR,
|
|
||||||
1 => 0x0600_a000 - VRAM_ADDR,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let y = self.vcount;
|
|
||||||
|
|
||||||
for x in 0..DISPLAY_WIDTH {
|
|
||||||
let bitmap_index = index2d!(x, y, DISPLAY_WIDTH);
|
|
||||||
let bitmap_ofs = page_ofs + (bitmap_index as u32);
|
|
||||||
let index = sb.vram.read_8(bitmap_ofs as Addr) as u32;
|
|
||||||
let color = self.get_palette_color(sb, index, 0, 0);
|
|
||||||
self.bg[bg].line[x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_scanline(&mut self, sb: &mut SysBus) {
|
|
||||||
// TODO - also render objs
|
|
||||||
match self.dispcnt.mode() {
|
match self.dispcnt.mode() {
|
||||||
0 => {
|
0 => {
|
||||||
for bg in 0..4 {
|
for bg in 0..4 {
|
||||||
if self.dispcnt.disp_bg(bg) {
|
if self.dispcnt.disp_bg(bg) {
|
||||||
self.scanline_reg_bg(bg, sb);
|
self.render_reg_bg(bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
if self.dispcnt.disp_bg(2) {
|
if self.dispcnt.disp_bg(2) {
|
||||||
self.scanline_aff_bg(2, sb);
|
self.render_aff_bg(2);
|
||||||
}
|
}
|
||||||
if self.dispcnt.disp_bg(1) {
|
if self.dispcnt.disp_bg(1) {
|
||||||
self.scanline_reg_bg(1, sb);
|
self.render_reg_bg(1);
|
||||||
}
|
}
|
||||||
if self.dispcnt.disp_bg(0) {
|
if self.dispcnt.disp_bg(0) {
|
||||||
self.scanline_reg_bg(0, sb);
|
self.render_reg_bg(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
if self.dispcnt.disp_bg(3) {
|
if self.dispcnt.disp_bg(3) {
|
||||||
self.scanline_aff_bg(3, sb);
|
self.render_aff_bg(3);
|
||||||
}
|
}
|
||||||
if self.dispcnt.disp_bg(2) {
|
if self.dispcnt.disp_bg(2) {
|
||||||
self.scanline_aff_bg(2, sb);
|
self.render_aff_bg(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
self.scanline_mode3(2, sb);
|
self.render_mode3(2);
|
||||||
}
|
}
|
||||||
4 => {
|
4 => {
|
||||||
self.scanline_mode4(2, sb);
|
self.render_mode4(2);
|
||||||
}
|
}
|
||||||
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
||||||
}
|
}
|
||||||
if self.dispcnt.disp_obj() {
|
if self.dispcnt.disp_obj() {
|
||||||
self.render_objs(sb);
|
self.render_objs();
|
||||||
}
|
}
|
||||||
self.mosaic_sfx();
|
self.mosaic_sfx();
|
||||||
let post_sfx_line = self.composite_sfx(sb);
|
self.composite_sfx_to_framebuffer();
|
||||||
for x in 0..DISPLAY_WIDTH {
|
|
||||||
self.frame_buffer[x + self.vcount * DISPLAY_WIDTH] = post_sfx_line[x].to_rgb24();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_framebuffer(&self) -> &[u32] {
|
|
||||||
&self.frame_buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_vcount(&mut self, value: usize, irqs: &mut IrqBitmask) {
|
fn update_vcount(&mut self, value: usize, irqs: &mut IrqBitmask) {
|
||||||
|
@ -418,6 +334,13 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clears the gpu internal buffer
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
for x in self.obj_buffer.iter_mut() {
|
||||||
|
*x = Default::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the new gpu state
|
// Returns the new gpu state
|
||||||
pub fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
pub fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
||||||
self.cycles += cycles;
|
self.cycles += cycles;
|
||||||
|
@ -443,7 +366,7 @@ impl Gpu {
|
||||||
self.update_vcount(self.vcount + 1, irqs);
|
self.update_vcount(self.vcount + 1, irqs);
|
||||||
|
|
||||||
if self.vcount < DISPLAY_HEIGHT {
|
if self.vcount < DISPLAY_HEIGHT {
|
||||||
self.render_scanline(sb);
|
self.render_scanline();
|
||||||
self.state = HDraw;
|
self.state = HDraw;
|
||||||
} else {
|
} else {
|
||||||
self.state = VBlank;
|
self.state = VBlank;
|
||||||
|
@ -465,7 +388,7 @@ impl Gpu {
|
||||||
} else {
|
} else {
|
||||||
self.update_vcount(0, irqs);
|
self.update_vcount(0, irqs);
|
||||||
self.dispstat.set_vblank_flag(false);
|
self.dispstat.set_vblank_flag(false);
|
||||||
self.render_scanline(sb);
|
self.render_scanline();
|
||||||
self.state = HDraw;
|
self.state = HDraw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,12 +396,3 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
struct TileMapEntry(u16);
|
|
||||||
u16;
|
|
||||||
u32, tile_index, _: 9, 0;
|
|
||||||
x_flip, _ : 10;
|
|
||||||
y_flip, _ : 11;
|
|
||||||
palette_bank, _ : 15, 12;
|
|
||||||
}
|
|
||||||
|
|
3
src/core/gpu/render.rs
Normal file
3
src/core/gpu/render.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub(super) mod bitmap;
|
||||||
|
pub(super) mod obj;
|
||||||
|
pub(super) mod text;
|
38
src/core/gpu/render/bitmap.rs
Normal file
38
src/core/gpu/render/bitmap.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//! Rendering for modes 4-5
|
||||||
|
|
||||||
|
use super::super::consts::*;
|
||||||
|
use super::super::Gpu;
|
||||||
|
use super::super::Rgb15;
|
||||||
|
|
||||||
|
use crate::core::Bus;
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
pub(in super::super) fn render_mode3(&mut self, bg: usize) {
|
||||||
|
let y = self.vcount;
|
||||||
|
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
let pixel_index = index2d!(u32, x, y, DISPLAY_WIDTH);
|
||||||
|
let pixel_ofs = 2 * pixel_index;
|
||||||
|
let color = Rgb15(self.vram.read_16(pixel_ofs));
|
||||||
|
self.bg[bg].line[x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in super::super) fn render_mode4(&mut self, bg: usize) {
|
||||||
|
let page_ofs: u32 = match self.dispcnt.display_frame() {
|
||||||
|
0 => 0x0600_0000 - VRAM_ADDR,
|
||||||
|
1 => 0x0600_a000 - VRAM_ADDR,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let y = self.vcount;
|
||||||
|
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
let bitmap_index = index2d!(x, y, DISPLAY_WIDTH);
|
||||||
|
let bitmap_ofs = page_ofs + (bitmap_index as u32);
|
||||||
|
let index = self.vram.read_8(bitmap_ofs) as u32;
|
||||||
|
let color = self.get_palette_color(index, 0, 0);
|
||||||
|
self.bg[bg].line[x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use super::super::SysBus;
|
use super::super::regs::*;
|
||||||
use super::regs::*;
|
use super::super::*;
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const OVRAM: u32 = 0x0601_0000;
|
const OVRAM: u32 = 0x0601_0000;
|
||||||
const PALRAM_OFS_FG: u32 = 0x200;
|
const PALRAM_OFS_FG: u32 = 0x200;
|
||||||
|
@ -10,21 +9,6 @@ struct ObjAttrs(Attribute0, Attribute1, Attribute2);
|
||||||
|
|
||||||
const AFFINE_FILL: u32 = 2 * 3;
|
const AFFINE_FILL: u32 = 2 * 3;
|
||||||
|
|
||||||
impl AffineMatrix {
|
|
||||||
fn from_index(sb: &SysBus, index: u32) -> AffineMatrix {
|
|
||||||
let mut offset = AFFINE_FILL + index * 16 * 2;
|
|
||||||
let pa = sb.oam.read_16(offset) as i16 as i32;
|
|
||||||
offset += 2 + AFFINE_FILL;
|
|
||||||
let pb = sb.oam.read_16(offset) as i16 as i32;
|
|
||||||
offset += 2 + AFFINE_FILL;
|
|
||||||
let pc = sb.oam.read_16(offset) as i16 as i32;
|
|
||||||
offset += 2 + AFFINE_FILL;
|
|
||||||
let pd = sb.oam.read_16(offset) as i16 as i32;
|
|
||||||
|
|
||||||
AffineMatrix { pa, pb, pc, pd }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjAttrs {
|
impl ObjAttrs {
|
||||||
fn size(&self) -> (i32, i32) {
|
fn size(&self) -> (i32, i32) {
|
||||||
match (self.1.size(), self.0.shape()) {
|
match (self.1.size(), self.0.shape()) {
|
||||||
|
@ -71,22 +55,11 @@ impl ObjAttrs {
|
||||||
let attr1 = (self.1).0;
|
let attr1 = (self.1).0;
|
||||||
((attr1 >> 9) & 0x1f) as u32
|
((attr1 >> 9) & 0x1f) as u32
|
||||||
}
|
}
|
||||||
fn affine_matrix(&self, sb: &SysBus) -> AffineMatrix {
|
|
||||||
AffineMatrix::from_index(sb, self.affine_index())
|
|
||||||
}
|
|
||||||
fn is_hidden(&self) -> bool {
|
fn is_hidden(&self) -> bool {
|
||||||
self.0.objtype() == ObjType::Hidden
|
self.0.objtype() == ObjType::Hidden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_obj_attrs(sb: &SysBus, obj: usize) -> ObjAttrs {
|
|
||||||
let addr = ATTRS_SIZE * (obj as u32);
|
|
||||||
let attr0 = Attribute0(sb.oam.read_16(addr + 0));
|
|
||||||
let attr1 = Attribute1(sb.oam.read_16(addr + 2));
|
|
||||||
let attr2 = Attribute2(sb.oam.read_16(addr + 4));
|
|
||||||
ObjAttrs(attr0, attr1, attr2)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpu {
|
impl Gpu {
|
||||||
fn obj_tile_base(&self) -> u32 {
|
fn obj_tile_base(&self) -> u32 {
|
||||||
match self.dispcnt.mode() {
|
match self.dispcnt.mode() {
|
||||||
|
@ -95,7 +68,28 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_affine_obj(&mut self, sb: &SysBus, obj: ObjAttrs, _obj_num: usize) {
|
fn get_affine_matrix(&self, affine_index: u32) -> AffineMatrix {
|
||||||
|
let mut offset = AFFINE_FILL + affine_index * 16 * 2;
|
||||||
|
let pa = self.oam.read_16(offset) as i16 as i32;
|
||||||
|
offset += 2 + AFFINE_FILL;
|
||||||
|
let pb = self.oam.read_16(offset) as i16 as i32;
|
||||||
|
offset += 2 + AFFINE_FILL;
|
||||||
|
let pc = self.oam.read_16(offset) as i16 as i32;
|
||||||
|
offset += 2 + AFFINE_FILL;
|
||||||
|
let pd = self.oam.read_16(offset) as i16 as i32;
|
||||||
|
|
||||||
|
AffineMatrix { pa, pb, pc, pd }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_obj_attrs(&self, obj: usize) -> ObjAttrs {
|
||||||
|
let addr = ATTRS_SIZE * (obj as u32);
|
||||||
|
let attr0 = Attribute0(self.oam.read_16(addr + 0));
|
||||||
|
let attr1 = Attribute1(self.oam.read_16(addr + 2));
|
||||||
|
let attr2 = Attribute2(self.oam.read_16(addr + 4));
|
||||||
|
ObjAttrs(attr0, attr1, attr2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_affine_obj(&mut self, obj: ObjAttrs, _obj_num: usize) {
|
||||||
let screen_y = self.vcount as i32;
|
let screen_y = self.vcount as i32;
|
||||||
|
|
||||||
let (ref_x, ref_y) = obj.coords();
|
let (ref_x, ref_y) = obj.coords();
|
||||||
|
@ -131,7 +125,7 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let affine_matrix = obj.affine_matrix(sb);
|
let affine_matrix = self.get_affine_matrix(obj.affine_index());
|
||||||
|
|
||||||
let half_width = bbox_w / 2;
|
let half_width = bbox_w / 2;
|
||||||
let half_height = bbox_h / 2;
|
let half_height = bbox_h / 2;
|
||||||
|
@ -145,7 +139,11 @@ impl Gpu {
|
||||||
if screen_x >= screen_width {
|
if screen_x >= screen_width {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if self.obj_buffer[screen_x as usize].priority <= obj.2.priority() {
|
if self
|
||||||
|
.obj_buffer_get(screen_x as usize, screen_y as usize)
|
||||||
|
.priority
|
||||||
|
<= obj.2.priority()
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,25 +157,16 @@ impl Gpu {
|
||||||
let tile_addr = tile_base
|
let tile_addr = tile_base
|
||||||
+ index2d!(u32, texture_x / 8, texture_y / 8, tile_array_width)
|
+ index2d!(u32, texture_x / 8, texture_y / 8, tile_array_width)
|
||||||
* (tile_size as u32);
|
* (tile_size as u32);
|
||||||
let pixel_index = self.read_pixel_index(
|
let pixel_index =
|
||||||
sb,
|
self.read_pixel_index(tile_addr, tile_x as u32, tile_y as u32, pixel_format);
|
||||||
tile_addr,
|
|
||||||
tile_x as u32,
|
|
||||||
tile_y as u32,
|
|
||||||
pixel_format,
|
|
||||||
);
|
|
||||||
let pixel_color =
|
let pixel_color =
|
||||||
self.get_palette_color(sb, pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
self.get_palette_color(pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
||||||
if pixel_color != Rgb15::TRANSPARENT {
|
self.write_obj_pixel(screen_x as usize, screen_y as usize, pixel_color, &obj);
|
||||||
let mut current_obj = &mut self.obj_buffer[screen_x as usize];
|
|
||||||
current_obj.color = pixel_color;
|
|
||||||
current_obj.priority = obj.2.priority();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_normal_obj(&mut self, sb: &SysBus, obj: ObjAttrs, _obj_num: usize) {
|
fn render_normal_obj(&mut self, obj: ObjAttrs, _obj_num: usize) {
|
||||||
let screen_y = self.vcount as i32;
|
let screen_y = self.vcount as i32;
|
||||||
|
|
||||||
let (ref_x, ref_y) = obj.coords();
|
let (ref_x, ref_y) = obj.coords();
|
||||||
|
@ -217,7 +206,11 @@ impl Gpu {
|
||||||
if screen_x >= screen_width {
|
if screen_x >= screen_width {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if self.obj_buffer[screen_x as usize].priority <= obj.2.priority() {
|
if self
|
||||||
|
.obj_buffer_get(screen_x as usize, screen_y as usize)
|
||||||
|
.priority
|
||||||
|
<= obj.2.priority()
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut sprite_y = screen_y - ref_y;
|
let mut sprite_y = screen_y - ref_y;
|
||||||
|
@ -237,49 +230,42 @@ impl Gpu {
|
||||||
let tile_addr = tile_base
|
let tile_addr = tile_base
|
||||||
+ index2d!(u32, sprite_x / 8, sprite_y / 8, tile_array_width) * (tile_size as u32);
|
+ index2d!(u32, sprite_x / 8, sprite_y / 8, tile_array_width) * (tile_size as u32);
|
||||||
let pixel_index =
|
let pixel_index =
|
||||||
self.read_pixel_index(sb, tile_addr, tile_x as u32, tile_y as u32, pixel_format);
|
self.read_pixel_index(tile_addr, tile_x as u32, tile_y as u32, pixel_format);
|
||||||
let pixel_color =
|
let pixel_color =
|
||||||
self.get_palette_color(sb, pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
self.get_palette_color(pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
||||||
|
self.write_obj_pixel(screen_x as usize, screen_y as usize, pixel_color, &obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_obj_pixel(&mut self, x: usize, y: usize, pixel_color: Rgb15, attrs: &ObjAttrs) {
|
||||||
|
let mut current_obj = self.obj_buffer_get_mut(x, y);
|
||||||
|
match attrs.0.objmode() {
|
||||||
|
ObjMode::Normal | ObjMode::Sfx => {
|
||||||
if pixel_color != Rgb15::TRANSPARENT {
|
if pixel_color != Rgb15::TRANSPARENT {
|
||||||
let mut current_obj = &mut self.obj_buffer[screen_x as usize];
|
|
||||||
current_obj.color = pixel_color;
|
current_obj.color = pixel_color;
|
||||||
current_obj.priority = obj.2.priority();
|
current_obj.priority = attrs.2.priority();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjMode::Window => {
|
||||||
|
current_obj.window = true;
|
||||||
|
}
|
||||||
|
ObjMode::Forbidden => {
|
||||||
|
panic!("Forbidden sprite mode!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_objs(&mut self, sb: &SysBus) {
|
pub(in super::super) fn render_objs(&mut self) {
|
||||||
// reset the scanline
|
|
||||||
self.obj_buffer = [Default::default(); DISPLAY_WIDTH];
|
|
||||||
for obj_num in 0..128 {
|
for obj_num in 0..128 {
|
||||||
let obj = read_obj_attrs(sb, obj_num);
|
let obj = self.read_obj_attrs(obj_num);
|
||||||
match obj.0.objtype() {
|
match obj.0.objtype() {
|
||||||
ObjType::Hidden => continue,
|
ObjType::Hidden => continue,
|
||||||
ObjType::Normal => self.render_normal_obj(sb, obj, obj_num),
|
ObjType::Normal => self.render_normal_obj(obj, obj_num),
|
||||||
ObjType::Affine | ObjType::AffineDoubleSize => {
|
ObjType::Affine | ObjType::AffineDoubleSize => self.render_affine_obj(obj, obj_num),
|
||||||
self.render_affine_obj(sb, obj, obj_num)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct ObjInfo {
|
|
||||||
pub(super) color: Rgb15,
|
|
||||||
pub(super) priority: u16,
|
|
||||||
pub(super) mode: ObjMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ObjInfo {
|
|
||||||
fn default() -> ObjInfo {
|
|
||||||
ObjInfo {
|
|
||||||
mode: ObjMode::Normal,
|
|
||||||
color: Rgb15::TRANSPARENT,
|
|
||||||
priority: 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
||||||
pub enum ObjMode {
|
pub enum ObjMode {
|
94
src/core/gpu/render/text.rs
Normal file
94
src/core/gpu/render/text.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
//! Rendering for modes 0-3
|
||||||
|
|
||||||
|
use super::super::consts::*;
|
||||||
|
use super::super::{Gpu, PixelFormat, Scanline, SCREEN_BLOCK_SIZE};
|
||||||
|
|
||||||
|
use crate::core::Bus;
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
pub(in super::super) fn render_reg_bg(&mut self, bg: usize) {
|
||||||
|
let (h_ofs, v_ofs) = (self.bg[bg].bghofs as u32, self.bg[bg].bgvofs as u32);
|
||||||
|
let tileset_base = self.bg[bg].bgcnt.char_block();
|
||||||
|
let tilemap_base = self.bg[bg].bgcnt.screen_block();
|
||||||
|
let (tile_size, pixel_format) = self.bg[bg].bgcnt.tile_format();
|
||||||
|
|
||||||
|
let (bg_width, bg_height) = self.bg[bg].bgcnt.size_regular();
|
||||||
|
|
||||||
|
let screen_y = self.vcount as u32;
|
||||||
|
let mut screen_x = 0;
|
||||||
|
|
||||||
|
// calculate the bg coords at the top-left corner, including wraparound
|
||||||
|
let bg_x = (screen_x + h_ofs) % bg_width;
|
||||||
|
let bg_y = (screen_y + v_ofs) % bg_height;
|
||||||
|
|
||||||
|
// calculate the initial screen entry index
|
||||||
|
// | (256,256) | (512,256) | (256,512) | (512,512) |
|
||||||
|
// |-----------|-----------|-------------|-----------|
|
||||||
|
// | | | [1] | [2][3] |
|
||||||
|
// | [0] | [0][1] | [0] | [0][1] |
|
||||||
|
// |___________|___________|_____________|___________|
|
||||||
|
//
|
||||||
|
let mut sbb = match (bg_width, bg_height) {
|
||||||
|
(256, 256) => 0,
|
||||||
|
(512, 256) => bg_x / 256,
|
||||||
|
(256, 512) => bg_y / 256,
|
||||||
|
(512, 512) => index2d!(u32, bg_x / 256, bg_y / 256, 2),
|
||||||
|
_ => unreachable!(),
|
||||||
|
} as u32;
|
||||||
|
|
||||||
|
let mut se_row = (bg_x / 8) % 32;
|
||||||
|
let se_column = (bg_y / 8) % 32;
|
||||||
|
|
||||||
|
// this will be non-zero if the h-scroll lands in a middle of a tile
|
||||||
|
let mut start_tile_x = bg_x % 8;
|
||||||
|
let tile_py = (bg_y % 8) as u32;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut map_addr =
|
||||||
|
tilemap_base + SCREEN_BLOCK_SIZE * sbb + 2 * index2d!(u32, se_row, se_column, 32);
|
||||||
|
for _ in se_row..32 {
|
||||||
|
let entry = TileMapEntry(self.vram.read_16(map_addr - VRAM_ADDR));
|
||||||
|
let tile_addr = tileset_base + entry.tile_index() * tile_size;
|
||||||
|
|
||||||
|
for tile_px in start_tile_x..8 {
|
||||||
|
let index = self.read_pixel_index(
|
||||||
|
tile_addr,
|
||||||
|
if entry.x_flip() { 7 - tile_px } else { tile_px },
|
||||||
|
if entry.y_flip() { 7 - tile_py } else { tile_py },
|
||||||
|
pixel_format,
|
||||||
|
);
|
||||||
|
let palette_bank = match pixel_format {
|
||||||
|
PixelFormat::BPP4 => entry.palette_bank() as u32,
|
||||||
|
PixelFormat::BPP8 => 0u32,
|
||||||
|
};
|
||||||
|
let color = self.get_palette_color(index as u32, palette_bank, 0);
|
||||||
|
self.bg[bg].line[screen_x as usize] = color;
|
||||||
|
screen_x += 1;
|
||||||
|
if (DISPLAY_WIDTH as u32) == screen_x {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start_tile_x = 0;
|
||||||
|
map_addr += 2;
|
||||||
|
}
|
||||||
|
se_row = 0;
|
||||||
|
if bg_width == 512 {
|
||||||
|
sbb = sbb ^ 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in super::super) fn render_aff_bg(&mut self, bg: usize) {
|
||||||
|
// TODO
|
||||||
|
self.bg[bg].line = Scanline::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
struct TileMapEntry(u16);
|
||||||
|
u16;
|
||||||
|
u32, tile_index, _: 9, 0;
|
||||||
|
x_flip, _ : 10;
|
||||||
|
y_flip, _ : 11;
|
||||||
|
palette_bank, _ : 15, 12;
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::ops::Sub;
|
|
||||||
|
|
||||||
use super::regs::*;
|
use super::regs::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -42,20 +41,20 @@ struct Layer {
|
||||||
impl Gpu {
|
impl Gpu {
|
||||||
fn get_top_layer(
|
fn get_top_layer(
|
||||||
&self,
|
&self,
|
||||||
sb: &SysBus,
|
|
||||||
screen_x: usize,
|
screen_x: usize,
|
||||||
bflags: BlendFlags,
|
bflags: BlendFlags,
|
||||||
wflags: WindowFlags,
|
wflags: WindowFlags,
|
||||||
) -> Option<Layer> {
|
) -> Option<Layer> {
|
||||||
// priorities are 0-4 when 0 is the highest
|
// priorities are 0-4 when 0 is the highest
|
||||||
'outer: for priority in 0..4 {
|
'outer: for priority in 0..4 {
|
||||||
|
let obj = self.obj_buffer_get(screen_x, self.vcount);
|
||||||
if bflags.contains(BlendFlags::OBJ)
|
if bflags.contains(BlendFlags::OBJ)
|
||||||
&& wflags.contains(WindowFlags::OBJ)
|
&& wflags.contains(WindowFlags::OBJ)
|
||||||
&& !self.obj_buffer[screen_x].color.is_transparent()
|
&& !obj.color.is_transparent()
|
||||||
&& self.obj_buffer[screen_x].priority == priority
|
&& obj.priority == priority
|
||||||
{
|
{
|
||||||
return Some(Layer {
|
return Some(Layer {
|
||||||
color: self.obj_buffer[screen_x].color,
|
color: obj.color,
|
||||||
blend_flag: BlendFlags::OBJ,
|
blend_flag: BlendFlags::OBJ,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -75,7 +74,7 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let backdrop = sb.palette_ram.read_16(0);
|
let backdrop = self.palette_ram.read_16(0);
|
||||||
if bflags.contains(BlendFlags::BACKDROP) {
|
if bflags.contains(BlendFlags::BACKDROP) {
|
||||||
return Some(Layer {
|
return Some(Layer {
|
||||||
color: Rgb15(backdrop),
|
color: Rgb15(backdrop),
|
||||||
|
@ -110,17 +109,11 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sfx_blend_alpha(
|
fn sfx_blend_alpha(&self, x: usize, _y: usize, wflags: WindowFlags) -> Option<Rgb15> {
|
||||||
&self,
|
|
||||||
sb: &SysBus,
|
|
||||||
x: usize,
|
|
||||||
_y: usize,
|
|
||||||
wflags: WindowFlags,
|
|
||||||
) -> Option<Rgb15> {
|
|
||||||
let top_layers = self.bldcnt.top();
|
let top_layers = self.bldcnt.top();
|
||||||
let bottom_layers = self.bldcnt.bottom();
|
let bottom_layers = self.bldcnt.bottom();
|
||||||
if let Some(top_layer) = self.get_top_layer(sb, x, top_layers, wflags) {
|
if let Some(top_layer) = self.get_top_layer(x, top_layers, wflags) {
|
||||||
if let Some(bot_layer) = self.get_top_layer(sb, x, bottom_layers, wflags) {
|
if let Some(bot_layer) = self.get_top_layer(x, bottom_layers, wflags) {
|
||||||
let eva = self.bldalpha.eva();
|
let eva = self.bldalpha.eva();
|
||||||
let evb = self.bldalpha.evb();
|
let evb = self.bldalpha.evb();
|
||||||
return Some(top_layer.color.blend_with(bot_layer.color, eva, evb));
|
return Some(top_layer.color.blend_with(bot_layer.color, eva, evb));
|
||||||
|
@ -133,7 +126,6 @@ impl Gpu {
|
||||||
|
|
||||||
fn sfx_blend_bw(
|
fn sfx_blend_bw(
|
||||||
&self,
|
&self,
|
||||||
sb: &SysBus,
|
|
||||||
fadeto: Rgb15,
|
fadeto: Rgb15,
|
||||||
x: usize,
|
x: usize,
|
||||||
_y: usize,
|
_y: usize,
|
||||||
|
@ -142,21 +134,19 @@ impl Gpu {
|
||||||
let top_layers = self.bldcnt.top();
|
let top_layers = self.bldcnt.top();
|
||||||
let evy = self.bldy;
|
let evy = self.bldy;
|
||||||
|
|
||||||
if let Some(layer) = self.get_top_layer(sb, x, top_layers, wflags) {
|
if let Some(layer) = self.get_top_layer(x, top_layers, wflags) {
|
||||||
return Some(layer.color.blend_with(fadeto, 16 - evy, evy));
|
return Some(layer.color.blend_with(fadeto, 16 - evy, evy));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn composite_sfx(&self, sb: &SysBus) -> Scanline<Rgb15> {
|
pub fn composite_sfx_to_framebuffer(&mut self) {
|
||||||
let mut line: Scanline<Rgb15> = Scanline::default();
|
|
||||||
let y = self.vcount;
|
let y = self.vcount;
|
||||||
|
|
||||||
for x in 0..DISPLAY_WIDTH {
|
for x in 0..DISPLAY_WIDTH {
|
||||||
let window = self.get_active_window_type(x, y);
|
let window = self.get_active_window_type(x, y);
|
||||||
let wflags = self.get_window_flags(window);
|
let wflags = self.get_window_flags(window);
|
||||||
let toplayer = self
|
let toplayer = self.get_top_layer(x, BlendFlags::all(), wflags).unwrap();
|
||||||
.get_top_layer(sb, x, BlendFlags::all(), wflags)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let bldmode = if wflags.sfx_enabled() {
|
let bldmode = if wflags.sfx_enabled() {
|
||||||
self.bldcnt.mode()
|
self.bldcnt.mode()
|
||||||
|
@ -164,41 +154,37 @@ impl Gpu {
|
||||||
BldMode::BldNone
|
BldMode::BldNone
|
||||||
};
|
};
|
||||||
|
|
||||||
match bldmode {
|
let pixel = match bldmode {
|
||||||
BldMode::BldAlpha => {
|
BldMode::BldAlpha => {
|
||||||
if self.bldcnt.top().contains(toplayer.blend_flag)
|
if self.bldcnt.top().contains(toplayer.blend_flag)
|
||||||
|| self.bldcnt.bottom().contains(toplayer.blend_flag)
|
|| self.bldcnt.bottom().contains(toplayer.blend_flag)
|
||||||
{
|
{
|
||||||
line[x] = self
|
self.sfx_blend_alpha(x, y, wflags).unwrap_or(toplayer.color)
|
||||||
.sfx_blend_alpha(sb, x, y, wflags)
|
|
||||||
.unwrap_or(toplayer.color);
|
|
||||||
} else {
|
} else {
|
||||||
line[x] = toplayer.color;
|
toplayer.color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BldMode::BldWhite => {
|
BldMode::BldWhite => {
|
||||||
let result = if self.bldcnt.top().contains(toplayer.blend_flag) {
|
let result = if self.bldcnt.top().contains(toplayer.blend_flag) {
|
||||||
self.sfx_blend_bw(sb, Rgb15::WHITE, x, y, wflags)
|
self.sfx_blend_bw(Rgb15::WHITE, x, y, wflags)
|
||||||
.unwrap_or(toplayer.color)
|
.unwrap_or(toplayer.color)
|
||||||
} else {
|
} else {
|
||||||
toplayer.color
|
toplayer.color
|
||||||
};
|
};
|
||||||
line[x] = result;
|
result
|
||||||
}
|
}
|
||||||
BldMode::BldBlack => {
|
BldMode::BldBlack => {
|
||||||
let result = if self.bldcnt.top().contains(toplayer.blend_flag) {
|
let result = if self.bldcnt.top().contains(toplayer.blend_flag) {
|
||||||
self.sfx_blend_bw(sb, Rgb15::BLACK, x, y, wflags)
|
self.sfx_blend_bw(Rgb15::BLACK, x, y, wflags)
|
||||||
.unwrap_or(toplayer.color)
|
.unwrap_or(toplayer.color)
|
||||||
} else {
|
} else {
|
||||||
toplayer.color
|
toplayer.color
|
||||||
};
|
};
|
||||||
line[x] = result;
|
result
|
||||||
}
|
}
|
||||||
BldMode::BldNone => {
|
BldMode::BldNone => toplayer.color,
|
||||||
line[x] = toplayer.color;
|
};
|
||||||
|
self.render_pixel(x as i32, y as i32, pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use super::arm7tdmi::{Addr, Bus};
|
|
||||||
use super::dma::DmaController;
|
use super::dma::DmaController;
|
||||||
use super::gpu::regs::WindowFlags;
|
use super::gpu::regs::WindowFlags;
|
||||||
use super::gpu::*;
|
use super::gpu::*;
|
||||||
|
@ -7,8 +6,9 @@ use super::keypad;
|
||||||
use super::sound::SoundController;
|
use super::sound::SoundController;
|
||||||
use super::sysbus::BoxedMemory;
|
use super::sysbus::BoxedMemory;
|
||||||
use super::timer::Timers;
|
use super::timer::Timers;
|
||||||
|
use super::{Addr, Bus};
|
||||||
|
|
||||||
use consts::*;
|
use self::consts::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum HaltState {
|
pub enum HaltState {
|
||||||
|
@ -19,8 +19,8 @@ pub enum HaltState {
|
||||||
|
|
||||||
pub struct IoDevices {
|
pub struct IoDevices {
|
||||||
pub intc: InterruptController,
|
pub intc: InterruptController,
|
||||||
pub gpu: Gpu,
|
pub gpu: Box<Gpu>,
|
||||||
pub sound: SoundController,
|
pub sound: Box<SoundController>,
|
||||||
pub timers: Timers,
|
pub timers: Timers,
|
||||||
pub dmac: DmaController,
|
pub dmac: DmaController,
|
||||||
pub keyinput: u16,
|
pub keyinput: u16,
|
||||||
|
@ -32,7 +32,7 @@ pub struct IoDevices {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoDevices {
|
impl IoDevices {
|
||||||
pub fn new(gpu: Gpu, sound_controller: SoundController) -> IoDevices {
|
pub fn new(gpu: Box<Gpu>, sound_controller: Box<SoundController>) -> IoDevices {
|
||||||
IoDevices {
|
IoDevices {
|
||||||
gpu: gpu,
|
gpu: gpu,
|
||||||
sound: sound_controller,
|
sound: sound_controller,
|
||||||
|
|
|
@ -10,18 +10,18 @@ pub use interrupt::Interrupt;
|
||||||
pub use interrupt::IrqBitmask;
|
pub use interrupt::IrqBitmask;
|
||||||
pub mod gba;
|
pub mod gba;
|
||||||
pub use gba::GameBoyAdvance;
|
pub use gba::GameBoyAdvance;
|
||||||
|
pub mod bus;
|
||||||
pub mod dma;
|
pub mod dma;
|
||||||
pub mod keypad;
|
pub mod keypad;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
pub use bus::*;
|
||||||
|
|
||||||
|
pub use super::{AudioInterface, InputInterface, VideoInterface};
|
||||||
|
|
||||||
use crate::debugger;
|
use crate::debugger;
|
||||||
|
|
||||||
use zip;
|
use zip;
|
||||||
|
|
||||||
pub trait SyncedIoDevice {
|
|
||||||
fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GBAError {
|
pub enum GBAError {
|
||||||
IO(::std::io::Error),
|
IO(::std::io::Error),
|
||||||
|
|
|
@ -3,17 +3,13 @@ use std::ops::Add;
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use super::arm7tdmi::bus::Bus;
|
|
||||||
use super::arm7tdmi::Addr;
|
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::gpu::GpuState;
|
use super::gpu::{GpuState, VIDEO_RAM_SIZE};
|
||||||
use super::iodev::IoDevices;
|
use super::iodev::IoDevices;
|
||||||
|
use super::{Addr, Bus};
|
||||||
|
|
||||||
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
|
||||||
const WORK_RAM_SIZE: usize = 256 * 1024;
|
const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||||
const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
||||||
const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
|
||||||
const OAM_SIZE: usize = 1 * 1024;
|
|
||||||
|
|
||||||
pub const BIOS_ADDR: u32 = 0x0000_0000;
|
pub const BIOS_ADDR: u32 = 0x0000_0000;
|
||||||
pub const EWRAM_ADDR: u32 = 0x0200_0000;
|
pub const EWRAM_ADDR: u32 = 0x0200_0000;
|
||||||
|
@ -144,9 +140,6 @@ pub struct SysBus {
|
||||||
bios: BoxedMemory,
|
bios: BoxedMemory,
|
||||||
onboard_work_ram: BoxedMemory,
|
onboard_work_ram: BoxedMemory,
|
||||||
internal_work_ram: BoxedMemory,
|
internal_work_ram: BoxedMemory,
|
||||||
pub palette_ram: BoxedMemory,
|
|
||||||
pub vram: BoxedMemory,
|
|
||||||
pub oam: BoxedMemory,
|
|
||||||
gamepak: Cartridge,
|
gamepak: Cartridge,
|
||||||
dummy: DummyBus,
|
dummy: DummyBus,
|
||||||
|
|
||||||
|
@ -161,9 +154,6 @@ impl SysBus {
|
||||||
bios: BoxedMemory::new(bios_rom.into_boxed_slice()),
|
bios: BoxedMemory::new(bios_rom.into_boxed_slice()),
|
||||||
onboard_work_ram: BoxedMemory::new(vec![0; WORK_RAM_SIZE].into_boxed_slice()),
|
onboard_work_ram: BoxedMemory::new(vec![0; WORK_RAM_SIZE].into_boxed_slice()),
|
||||||
internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM_SIZE].into_boxed_slice()),
|
internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM_SIZE].into_boxed_slice()),
|
||||||
palette_ram: BoxedMemory::new(vec![0; PALETTE_RAM_SIZE].into_boxed_slice()),
|
|
||||||
vram: BoxedMemory::new(vec![0; VIDEO_RAM_SIZE].into_boxed_slice()),
|
|
||||||
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice()),
|
|
||||||
gamepak: gamepak,
|
gamepak: gamepak,
|
||||||
dummy: DummyBus([0; 4]),
|
dummy: DummyBus([0; 4]),
|
||||||
|
|
||||||
|
@ -190,15 +180,15 @@ impl SysBus {
|
||||||
ofs & 0x7ff
|
ofs & 0x7ff
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
PALRAM_ADDR => (&self.palette_ram, ofs & 0x3ff),
|
PALRAM_ADDR => (&self.io.gpu.palette_ram, ofs & 0x3ff),
|
||||||
VRAM_ADDR => (&self.vram, {
|
VRAM_ADDR => (&self.io.gpu.vram, {
|
||||||
let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1);
|
let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1);
|
||||||
if ofs > 0x18000 {
|
if ofs > 0x18000 {
|
||||||
ofs -= 0x8000;
|
ofs -= 0x8000;
|
||||||
}
|
}
|
||||||
ofs
|
ofs
|
||||||
}),
|
}),
|
||||||
OAM_ADDR => (&self.oam, ofs & 0x3ff),
|
OAM_ADDR => (&self.io.gpu.oam, ofs & 0x3ff),
|
||||||
GAMEPAK_WS0_ADDR | GAMEPAK_MIRROR_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => {
|
GAMEPAK_WS0_ADDR | GAMEPAK_MIRROR_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => {
|
||||||
(&self.gamepak, addr & 0x01ff_ffff)
|
(&self.gamepak, addr & 0x01ff_ffff)
|
||||||
}
|
}
|
||||||
|
@ -220,15 +210,15 @@ impl SysBus {
|
||||||
ofs & 0x7ff
|
ofs & 0x7ff
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
PALRAM_ADDR => (&mut self.palette_ram, ofs & 0x3ff),
|
PALRAM_ADDR => (&mut self.io.gpu.palette_ram, ofs & 0x3ff),
|
||||||
VRAM_ADDR => (&mut self.vram, {
|
VRAM_ADDR => (&mut self.io.gpu.vram, {
|
||||||
let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1);
|
let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1);
|
||||||
if ofs > 0x18000 {
|
if ofs > 0x18000 {
|
||||||
ofs -= 0x8000;
|
ofs -= 0x8000;
|
||||||
}
|
}
|
||||||
ofs
|
ofs
|
||||||
}),
|
}),
|
||||||
OAM_ADDR => (&mut self.oam, ofs & 0x3ff),
|
OAM_ADDR => (&mut self.io.gpu.oam, ofs & 0x3ff),
|
||||||
GAMEPAK_WS0_ADDR | GAMEPAK_MIRROR_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => {
|
GAMEPAK_WS0_ADDR | GAMEPAK_MIRROR_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => {
|
||||||
(&mut self.gamepak, addr & 0x01ff_ffff)
|
(&mut self.gamepak, addr & 0x01ff_ffff)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ use std::sync::Arc;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use crate::core::arm7tdmi::arm::ArmInstruction;
|
use crate::core::arm7tdmi::arm::ArmInstruction;
|
||||||
use crate::core::arm7tdmi::bus::Bus;
|
|
||||||
use crate::core::arm7tdmi::thumb::ThumbInstruction;
|
use crate::core::arm7tdmi::thumb::ThumbInstruction;
|
||||||
use crate::core::arm7tdmi::{Addr, CpuState};
|
use crate::core::arm7tdmi::CpuState;
|
||||||
use crate::core::GBAError;
|
use crate::core::GBAError;
|
||||||
|
use crate::core::{Addr, Bus};
|
||||||
use crate::disass::Disassembler;
|
use crate::disass::Disassembler;
|
||||||
use crate::{AudioInterface, InputInterface, VideoInterface};
|
use crate::{AudioInterface, InputInterface, VideoInterface};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, prelude::*, BufReader};
|
use std::io::{prelude::*, BufReader};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -9,10 +9,9 @@ use rustyline::Editor;
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
use super::core::arm7tdmi::{Addr, Bus, CpuError};
|
use super::core::arm7tdmi::CpuError;
|
||||||
use super::core::GameBoyAdvance;
|
use super::core::GameBoyAdvance;
|
||||||
|
use super::core::{Addr, Bus};
|
||||||
use super::{AudioInterface, InputInterface, VideoInterface};
|
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
use parser::{parse_expr, DerefType, Expr, Value};
|
use parser::{parse_expr, DerefType, Expr, Value};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::core::arm7tdmi::{Addr, InstructionDecoder, InstructionDecoderError};
|
use super::core::arm7tdmi::{InstructionDecoder, InstructionDecoderError};
|
||||||
|
use super::core::Addr;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
|
|
||||||
pub struct Disassembler<'a, D>
|
pub struct Disassembler<'a, D>
|
||||||
|
|
Reference in a new issue