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"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "*"
|
||||
byteorder = "1"
|
||||
num = "0.2.0"
|
||||
num-traits = "0.2"
|
||||
enum-primitive-derive = "^0.1"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::bit::BitIndex;
|
||||
|
||||
use super::super::alu::*;
|
||||
use crate::core::arm7tdmi::bus::Bus;
|
||||
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||
use crate::core::arm7tdmi::psr::RegPSR;
|
||||
use crate::core::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, REG_LR, REG_PC};
|
||||
use crate::core::sysbus::SysBus;
|
||||
use crate::core::Bus;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@ use ansi_term::{Colour, Style};
|
|||
|
||||
pub use super::exception::Exception;
|
||||
use super::{
|
||||
arm::*, bus::Bus, psr::RegPSR, reg_string, thumb::ThumbInstruction, Addr, CpuMode, CpuResult,
|
||||
CpuState, DecodedInstruction, InstructionDecoder,
|
||||
arm::*, psr::RegPSR, reg_string, thumb::ThumbInstruction, Addr, CpuMode, CpuResult, CpuState,
|
||||
DecodedInstruction, InstructionDecoder,
|
||||
};
|
||||
|
||||
use super::super::sysbus::{
|
||||
use crate::core::bus::Bus;
|
||||
use crate::core::sysbus::{
|
||||
MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus,
|
||||
};
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@ pub mod cpu;
|
|||
pub use cpu::*;
|
||||
pub mod alu;
|
||||
pub use alu::*;
|
||||
pub mod bus;
|
||||
pub use bus::*;
|
||||
pub mod exception;
|
||||
pub mod psr;
|
||||
|
||||
|
@ -21,7 +19,7 @@ pub const REG_PC: usize = 15;
|
|||
pub const REG_LR: usize = 14;
|
||||
pub const REG_SP: usize = 13;
|
||||
|
||||
pub type Addr = u32;
|
||||
pub(self) use crate::core::{Addr, Bus};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum DecodedInstruction {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::core::arm7tdmi::bus::Bus;
|
||||
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||
use crate::core::arm7tdmi::*;
|
||||
use crate::core::sysbus::SysBus;
|
||||
use crate::core::Bus;
|
||||
|
||||
use crate::bit::BitIndex;
|
||||
|
||||
|
|
|
@ -343,9 +343,10 @@ impl ThumbInstruction {
|
|||
#[cfg(test)]
|
||||
/// All instructions constants were generated using an ARM assembler.
|
||||
mod tests {
|
||||
use super::super::Core;
|
||||
use super::*;
|
||||
use crate::core::arm7tdmi::{Bus, Core};
|
||||
use crate::core::sysbus::BoxedMemory;
|
||||
use crate::core::Bus;
|
||||
|
||||
#[test]
|
||||
fn mov_low_reg() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::Addr;
|
||||
pub type Addr = u32;
|
||||
|
||||
pub trait Bus {
|
||||
fn read_32(&self, addr: Addr) -> u32;
|
|
@ -8,8 +8,7 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|||
use zip::ZipArchive;
|
||||
|
||||
use super::super::util::read_bin_file;
|
||||
use super::arm7tdmi::{bus::Bus, Addr};
|
||||
use super::{GBAError, GBAResult};
|
||||
use super::{Addr, Bus, GBAResult};
|
||||
|
||||
/// From GBATEK
|
||||
///
|
||||
|
|
|
@ -2,10 +2,9 @@ extern crate bit_set;
|
|||
|
||||
use bit_set::BitSet;
|
||||
|
||||
use super::arm7tdmi::{Addr, Bus};
|
||||
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
|
||||
use super::sysbus::SysBus;
|
||||
use super::{Interrupt, IrqBitmask};
|
||||
use super::{Addr, Bus, Interrupt, IrqBitmask};
|
||||
|
||||
use num::FromPrimitive;
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ impl GameBoyAdvance {
|
|||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||
input_device: Rc<RefCell<dyn InputInterface>>,
|
||||
) -> GameBoyAdvance {
|
||||
let gpu = Gpu::new(video_device.clone());
|
||||
let sound_controller = SoundController::new(audio_device.clone());
|
||||
let gpu = Box::new(Gpu::new(video_device.clone()));
|
||||
let sound_controller = Box::new(SoundController::new(audio_device.clone()));
|
||||
let io = IoDevices::new(gpu, sound_controller);
|
||||
GameBoyAdvance {
|
||||
cpu: cpu,
|
||||
|
@ -48,6 +48,7 @@ impl GameBoyAdvance {
|
|||
|
||||
pub fn frame(&mut self) {
|
||||
self.key_poll();
|
||||
self.sysbus.io.gpu.clear();
|
||||
while self.sysbus.io.gpu.vcount != DISPLAY_HEIGHT {
|
||||
self.step();
|
||||
}
|
||||
|
|
|
@ -1,38 +1,48 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::arm7tdmi::{Addr, Bus};
|
||||
use super::*;
|
||||
use crate::VideoInterface;
|
||||
use super::super::VideoInterface;
|
||||
use super::interrupt::IrqBitmask;
|
||||
use super::sysbus::{BoxedMemory, SysBus};
|
||||
use super::Bus;
|
||||
|
||||
use crate::bitfield::Bit;
|
||||
use crate::num::FromPrimitive;
|
||||
|
||||
mod render;
|
||||
|
||||
mod mosaic;
|
||||
mod obj;
|
||||
use obj::ObjInfo;
|
||||
mod sfx;
|
||||
mod rgb15;
|
||||
mod sfx;
|
||||
pub use rgb15::Rgb15;
|
||||
|
||||
pub mod regs;
|
||||
pub use regs::*;
|
||||
|
||||
pub const VRAM_ADDR: Addr = 0x0600_0000;
|
||||
pub const DISPLAY_WIDTH: usize = 240;
|
||||
pub const DISPLAY_HEIGHT: usize = 160;
|
||||
pub const VBLANK_LINES: usize = 68;
|
||||
#[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;
|
||||
|
||||
const CYCLES_PIXEL: usize = 4;
|
||||
const CYCLES_HDRAW: usize = 960;
|
||||
const CYCLES_HBLANK: usize = 272;
|
||||
const CYCLES_SCANLINE: usize = 1232;
|
||||
const CYCLES_VDRAW: usize = 197120;
|
||||
const CYCLES_VBLANK: usize = 83776;
|
||||
pub const VRAM_ADDR: u32 = 0x0600_0000;
|
||||
pub const DISPLAY_WIDTH: usize = 240;
|
||||
pub const DISPLAY_HEIGHT: usize = 160;
|
||||
pub const VBLANK_LINES: usize = 68;
|
||||
|
||||
pub const TILE_SIZE: u32 = 0x20;
|
||||
pub(super) const CYCLES_PIXEL: usize = 4;
|
||||
pub(super) const CYCLES_HDRAW: usize = 960;
|
||||
pub(super) const CYCLES_HBLANK: usize = 272;
|
||||
pub(super) const CYCLES_SCANLINE: usize = 1232;
|
||||
pub(super) const CYCLES_VDRAW: usize = 197120;
|
||||
pub(super) const CYCLES_VBLANK: usize = 83776;
|
||||
|
||||
pub const TILE_SIZE: u32 = 0x20;
|
||||
}
|
||||
pub use self::consts::*;
|
||||
|
||||
pub type FrameBuffer<T> = [T; DISPLAY_WIDTH * DISPLAY_HEIGHT];
|
||||
|
||||
#[derive(Debug, Primitive, Copy, Clone)]
|
||||
pub enum PixelFormat {
|
||||
|
@ -146,6 +156,23 @@ pub struct BgAffine {
|
|||
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>>;
|
||||
|
||||
#[derive(DebugStub)]
|
||||
|
@ -173,11 +200,15 @@ pub struct Gpu {
|
|||
pub bldalpha: BlendAlpha,
|
||||
pub bldy: u16,
|
||||
|
||||
pub palette_ram: BoxedMemory,
|
||||
pub vram: BoxedMemory,
|
||||
pub oam: BoxedMemory,
|
||||
|
||||
#[debug_stub = "Sprite Buffer"]
|
||||
pub obj_buffer: [ObjInfo; DISPLAY_WIDTH],
|
||||
pub obj_buffer: FrameBuffer<ObjBufferEntry>,
|
||||
|
||||
#[debug_stub = "Frame Buffer"]
|
||||
pub frame_buffer: [u32; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
||||
pub(super) frame_buffer: FrameBuffer<u32>,
|
||||
}
|
||||
|
||||
impl Gpu {
|
||||
|
@ -201,210 +232,95 @@ impl Gpu {
|
|||
state: HDraw,
|
||||
vcount: 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],
|
||||
}
|
||||
}
|
||||
|
||||
/// helper method that reads the palette index from a base address and x + y
|
||||
pub fn read_pixel_index(
|
||||
&self,
|
||||
sb: &SysBus,
|
||||
addr: Addr,
|
||||
x: u32,
|
||||
y: u32,
|
||||
format: PixelFormat,
|
||||
) -> usize {
|
||||
pub fn read_pixel_index(&self, addr: u32, x: u32, y: u32, format: PixelFormat) -> usize {
|
||||
let ofs = addr - VRAM_ADDR;
|
||||
match format {
|
||||
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 {
|
||||
(byte >> 4) as usize
|
||||
} else {
|
||||
(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(
|
||||
&self,
|
||||
sb: &SysBus,
|
||||
index: u32,
|
||||
palette_index: u32,
|
||||
offset: u32,
|
||||
) -> Rgb15 {
|
||||
pub fn get_palette_color(&self, index: u32, palette_index: u32, offset: u32) -> Rgb15 {
|
||||
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
||||
return Rgb15::TRANSPARENT;
|
||||
}
|
||||
Rgb15(
|
||||
sb.palette_ram
|
||||
self.palette_ram
|
||||
.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();
|
||||
}
|
||||
|
||||
fn scanline_reg_bg(&mut self, bg: usize, sb: &mut SysBus) {
|
||||
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
|
||||
pub fn render_scanline(&mut self) {
|
||||
match self.dispcnt.mode() {
|
||||
0 => {
|
||||
for bg in 0..4 {
|
||||
if self.dispcnt.disp_bg(bg) {
|
||||
self.scanline_reg_bg(bg, sb);
|
||||
self.render_reg_bg(bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
if self.dispcnt.disp_bg(2) {
|
||||
self.scanline_aff_bg(2, sb);
|
||||
self.render_aff_bg(2);
|
||||
}
|
||||
if self.dispcnt.disp_bg(1) {
|
||||
self.scanline_reg_bg(1, sb);
|
||||
self.render_reg_bg(1);
|
||||
}
|
||||
if self.dispcnt.disp_bg(0) {
|
||||
self.scanline_reg_bg(0, sb);
|
||||
self.render_reg_bg(0);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if self.dispcnt.disp_bg(3) {
|
||||
self.scanline_aff_bg(3, sb);
|
||||
self.render_aff_bg(3);
|
||||
}
|
||||
if self.dispcnt.disp_bg(2) {
|
||||
self.scanline_aff_bg(2, sb);
|
||||
self.render_aff_bg(2);
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
self.scanline_mode3(2, sb);
|
||||
self.render_mode3(2);
|
||||
}
|
||||
4 => {
|
||||
self.scanline_mode4(2, sb);
|
||||
self.render_mode4(2);
|
||||
}
|
||||
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
||||
}
|
||||
if self.dispcnt.disp_obj() {
|
||||
self.render_objs(sb);
|
||||
self.render_objs();
|
||||
}
|
||||
self.mosaic_sfx();
|
||||
let post_sfx_line = self.composite_sfx(sb);
|
||||
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
|
||||
self.composite_sfx_to_framebuffer();
|
||||
}
|
||||
|
||||
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
|
||||
pub fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
||||
self.cycles += cycles;
|
||||
|
@ -443,7 +366,7 @@ impl Gpu {
|
|||
self.update_vcount(self.vcount + 1, irqs);
|
||||
|
||||
if self.vcount < DISPLAY_HEIGHT {
|
||||
self.render_scanline(sb);
|
||||
self.render_scanline();
|
||||
self.state = HDraw;
|
||||
} else {
|
||||
self.state = VBlank;
|
||||
|
@ -465,7 +388,7 @@ impl Gpu {
|
|||
} else {
|
||||
self.update_vcount(0, irqs);
|
||||
self.dispstat.set_vblank_flag(false);
|
||||
self.render_scanline(sb);
|
||||
self.render_scanline();
|
||||
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::regs::*;
|
||||
use super::*;
|
||||
use super::super::regs::*;
|
||||
use super::super::*;
|
||||
|
||||
const OVRAM: u32 = 0x0601_0000;
|
||||
const PALRAM_OFS_FG: u32 = 0x200;
|
||||
|
@ -10,21 +9,6 @@ struct ObjAttrs(Attribute0, Attribute1, Attribute2);
|
|||
|
||||
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 {
|
||||
fn size(&self) -> (i32, i32) {
|
||||
match (self.1.size(), self.0.shape()) {
|
||||
|
@ -71,22 +55,11 @@ impl ObjAttrs {
|
|||
let attr1 = (self.1).0;
|
||||
((attr1 >> 9) & 0x1f) as u32
|
||||
}
|
||||
fn affine_matrix(&self, sb: &SysBus) -> AffineMatrix {
|
||||
AffineMatrix::from_index(sb, self.affine_index())
|
||||
}
|
||||
fn is_hidden(&self) -> bool {
|
||||
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 {
|
||||
fn obj_tile_base(&self) -> u32 {
|
||||
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 (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_height = bbox_h / 2;
|
||||
|
@ -145,7 +139,11 @@ impl Gpu {
|
|||
if screen_x >= screen_width {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -159,25 +157,16 @@ impl Gpu {
|
|||
let tile_addr = tile_base
|
||||
+ index2d!(u32, texture_x / 8, texture_y / 8, tile_array_width)
|
||||
* (tile_size as u32);
|
||||
let pixel_index = self.read_pixel_index(
|
||||
sb,
|
||||
tile_addr,
|
||||
tile_x as u32,
|
||||
tile_y as u32,
|
||||
pixel_format,
|
||||
);
|
||||
let pixel_index =
|
||||
self.read_pixel_index(tile_addr, tile_x as u32, tile_y as u32, pixel_format);
|
||||
let pixel_color =
|
||||
self.get_palette_color(sb, pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
||||
if pixel_color != Rgb15::TRANSPARENT {
|
||||
let mut current_obj = &mut self.obj_buffer[screen_x as usize];
|
||||
current_obj.color = pixel_color;
|
||||
current_obj.priority = obj.2.priority();
|
||||
}
|
||||
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 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 (ref_x, ref_y) = obj.coords();
|
||||
|
@ -217,7 +206,11 @@ impl Gpu {
|
|||
if screen_x >= screen_width {
|
||||
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;
|
||||
}
|
||||
let mut sprite_y = screen_y - ref_y;
|
||||
|
@ -237,46 +230,39 @@ impl Gpu {
|
|||
let tile_addr = tile_base
|
||||
+ index2d!(u32, sprite_x / 8, sprite_y / 8, tile_array_width) * (tile_size as u32);
|
||||
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 =
|
||||
self.get_palette_color(sb, pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
||||
if pixel_color != Rgb15::TRANSPARENT {
|
||||
let mut current_obj = &mut self.obj_buffer[screen_x as usize];
|
||||
current_obj.color = pixel_color;
|
||||
current_obj.priority = obj.2.priority();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_objs(&mut self, sb: &SysBus) {
|
||||
// reset the scanline
|
||||
self.obj_buffer = [Default::default(); DISPLAY_WIDTH];
|
||||
for obj_num in 0..128 {
|
||||
let obj = read_obj_attrs(sb, obj_num);
|
||||
match obj.0.objtype() {
|
||||
ObjType::Hidden => continue,
|
||||
ObjType::Normal => self.render_normal_obj(sb, obj, obj_num),
|
||||
ObjType::Affine | ObjType::AffineDoubleSize => {
|
||||
self.render_affine_obj(sb, obj, obj_num)
|
||||
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 {
|
||||
current_obj.color = pixel_color;
|
||||
current_obj.priority = attrs.2.priority();
|
||||
}
|
||||
}
|
||||
ObjMode::Window => {
|
||||
current_obj.window = true;
|
||||
}
|
||||
ObjMode::Forbidden => {
|
||||
panic!("Forbidden sprite mode!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
pub(in super::super) fn render_objs(&mut self) {
|
||||
for obj_num in 0..128 {
|
||||
let obj = self.read_obj_attrs(obj_num);
|
||||
match obj.0.objtype() {
|
||||
ObjType::Hidden => continue,
|
||||
ObjType::Normal => self.render_normal_obj(obj, obj_num),
|
||||
ObjType::Affine | ObjType::AffineDoubleSize => self.render_affine_obj(obj, obj_num),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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,37 +1,37 @@
|
|||
//! Helper type to deal with the GBA's 15bit color
|
||||
|
||||
bitfield! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Rgb15(u16);
|
||||
impl Debug;
|
||||
pub r, set_r: 4, 0;
|
||||
pub g, set_g: 9, 5;
|
||||
pub b, set_b: 14, 10;
|
||||
}
|
||||
|
||||
impl Rgb15 {
|
||||
pub const BLACK: Rgb15 = Rgb15(0);
|
||||
pub const WHITE: Rgb15 = Rgb15(0x7fff);
|
||||
pub const TRANSPARENT: Rgb15 = Rgb15(0x8000);
|
||||
|
||||
pub fn to_rgb24(&self) -> u32 {
|
||||
((self.r() as u32) << 19) | ((self.g() as u32) << 11) | ((self.b() as u32) << 3)
|
||||
}
|
||||
|
||||
pub fn from_rgb(r: u16, g: u16, b: u16) -> Rgb15 {
|
||||
let mut c = Rgb15(0);
|
||||
c.set_r(r);
|
||||
c.set_g(g);
|
||||
c.set_b(b);
|
||||
c
|
||||
}
|
||||
|
||||
pub fn get_rgb(&self) -> (u16, u16, u16) {
|
||||
(self.r(), self.g(), self.b())
|
||||
}
|
||||
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
self.0 == 0x8000
|
||||
}
|
||||
}
|
||||
//! Helper type to deal with the GBA's 15bit color
|
||||
|
||||
bitfield! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Rgb15(u16);
|
||||
impl Debug;
|
||||
pub r, set_r: 4, 0;
|
||||
pub g, set_g: 9, 5;
|
||||
pub b, set_b: 14, 10;
|
||||
}
|
||||
|
||||
impl Rgb15 {
|
||||
pub const BLACK: Rgb15 = Rgb15(0);
|
||||
pub const WHITE: Rgb15 = Rgb15(0x7fff);
|
||||
pub const TRANSPARENT: Rgb15 = Rgb15(0x8000);
|
||||
|
||||
pub fn to_rgb24(&self) -> u32 {
|
||||
((self.r() as u32) << 19) | ((self.g() as u32) << 11) | ((self.b() as u32) << 3)
|
||||
}
|
||||
|
||||
pub fn from_rgb(r: u16, g: u16, b: u16) -> Rgb15 {
|
||||
let mut c = Rgb15(0);
|
||||
c.set_r(r);
|
||||
c.set_g(g);
|
||||
c.set_b(b);
|
||||
c
|
||||
}
|
||||
|
||||
pub fn get_rgb(&self) -> (u16, u16, u16) {
|
||||
(self.r(), self.g(), self.b())
|
||||
}
|
||||
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
self.0 == 0x8000
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::cmp;
|
||||
use std::ops::Sub;
|
||||
|
||||
use super::regs::*;
|
||||
use super::*;
|
||||
|
@ -42,20 +41,20 @@ struct Layer {
|
|||
impl Gpu {
|
||||
fn get_top_layer(
|
||||
&self,
|
||||
sb: &SysBus,
|
||||
screen_x: usize,
|
||||
bflags: BlendFlags,
|
||||
wflags: WindowFlags,
|
||||
) -> Option<Layer> {
|
||||
// priorities are 0-4 when 0 is the highest
|
||||
'outer: for priority in 0..4 {
|
||||
let obj = self.obj_buffer_get(screen_x, self.vcount);
|
||||
if bflags.contains(BlendFlags::OBJ)
|
||||
&& wflags.contains(WindowFlags::OBJ)
|
||||
&& !self.obj_buffer[screen_x].color.is_transparent()
|
||||
&& self.obj_buffer[screen_x].priority == priority
|
||||
&& !obj.color.is_transparent()
|
||||
&& obj.priority == priority
|
||||
{
|
||||
return Some(Layer {
|
||||
color: self.obj_buffer[screen_x].color,
|
||||
color: obj.color,
|
||||
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) {
|
||||
return Some(Layer {
|
||||
color: Rgb15(backdrop),
|
||||
|
@ -110,17 +109,11 @@ impl Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
fn sfx_blend_alpha(
|
||||
&self,
|
||||
sb: &SysBus,
|
||||
x: usize,
|
||||
_y: usize,
|
||||
wflags: WindowFlags,
|
||||
) -> Option<Rgb15> {
|
||||
fn sfx_blend_alpha(&self, x: usize, _y: usize, wflags: WindowFlags) -> Option<Rgb15> {
|
||||
let top_layers = self.bldcnt.top();
|
||||
let bottom_layers = self.bldcnt.bottom();
|
||||
if let Some(top_layer) = self.get_top_layer(sb, x, top_layers, wflags) {
|
||||
if let Some(bot_layer) = self.get_top_layer(sb, x, bottom_layers, wflags) {
|
||||
if let Some(top_layer) = self.get_top_layer(x, top_layers, wflags) {
|
||||
if let Some(bot_layer) = self.get_top_layer(x, bottom_layers, wflags) {
|
||||
let eva = self.bldalpha.eva();
|
||||
let evb = self.bldalpha.evb();
|
||||
return Some(top_layer.color.blend_with(bot_layer.color, eva, evb));
|
||||
|
@ -133,7 +126,6 @@ impl Gpu {
|
|||
|
||||
fn sfx_blend_bw(
|
||||
&self,
|
||||
sb: &SysBus,
|
||||
fadeto: Rgb15,
|
||||
x: usize,
|
||||
_y: usize,
|
||||
|
@ -142,21 +134,19 @@ impl Gpu {
|
|||
let top_layers = self.bldcnt.top();
|
||||
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));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn composite_sfx(&self, sb: &SysBus) -> Scanline<Rgb15> {
|
||||
let mut line: Scanline<Rgb15> = Scanline::default();
|
||||
pub fn composite_sfx_to_framebuffer(&mut self) {
|
||||
let y = self.vcount;
|
||||
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let window = self.get_active_window_type(x, y);
|
||||
let wflags = self.get_window_flags(window);
|
||||
let toplayer = self
|
||||
.get_top_layer(sb, x, BlendFlags::all(), wflags)
|
||||
.unwrap();
|
||||
let toplayer = self.get_top_layer(x, BlendFlags::all(), wflags).unwrap();
|
||||
|
||||
let bldmode = if wflags.sfx_enabled() {
|
||||
self.bldcnt.mode()
|
||||
|
@ -164,41 +154,37 @@ impl Gpu {
|
|||
BldMode::BldNone
|
||||
};
|
||||
|
||||
match bldmode {
|
||||
let pixel = match bldmode {
|
||||
BldMode::BldAlpha => {
|
||||
if self.bldcnt.top().contains(toplayer.blend_flag)
|
||||
|| self.bldcnt.bottom().contains(toplayer.blend_flag)
|
||||
{
|
||||
line[x] = self
|
||||
.sfx_blend_alpha(sb, x, y, wflags)
|
||||
.unwrap_or(toplayer.color);
|
||||
self.sfx_blend_alpha(x, y, wflags).unwrap_or(toplayer.color)
|
||||
} else {
|
||||
line[x] = toplayer.color;
|
||||
toplayer.color
|
||||
}
|
||||
}
|
||||
BldMode::BldWhite => {
|
||||
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)
|
||||
} else {
|
||||
toplayer.color
|
||||
};
|
||||
line[x] = result;
|
||||
result
|
||||
}
|
||||
BldMode::BldBlack => {
|
||||
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)
|
||||
} else {
|
||||
toplayer.color
|
||||
};
|
||||
line[x] = result;
|
||||
result
|
||||
}
|
||||
BldMode::BldNone => {
|
||||
line[x] = toplayer.color;
|
||||
}
|
||||
}
|
||||
BldMode::BldNone => 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::gpu::regs::WindowFlags;
|
||||
use super::gpu::*;
|
||||
|
@ -7,8 +6,9 @@ use super::keypad;
|
|||
use super::sound::SoundController;
|
||||
use super::sysbus::BoxedMemory;
|
||||
use super::timer::Timers;
|
||||
use super::{Addr, Bus};
|
||||
|
||||
use consts::*;
|
||||
use self::consts::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum HaltState {
|
||||
|
@ -19,8 +19,8 @@ pub enum HaltState {
|
|||
|
||||
pub struct IoDevices {
|
||||
pub intc: InterruptController,
|
||||
pub gpu: Gpu,
|
||||
pub sound: SoundController,
|
||||
pub gpu: Box<Gpu>,
|
||||
pub sound: Box<SoundController>,
|
||||
pub timers: Timers,
|
||||
pub dmac: DmaController,
|
||||
pub keyinput: u16,
|
||||
|
@ -32,7 +32,7 @@ pub struct IoDevices {
|
|||
}
|
||||
|
||||
impl IoDevices {
|
||||
pub fn new(gpu: Gpu, sound_controller: SoundController) -> IoDevices {
|
||||
pub fn new(gpu: Box<Gpu>, sound_controller: Box<SoundController>) -> IoDevices {
|
||||
IoDevices {
|
||||
gpu: gpu,
|
||||
sound: sound_controller,
|
||||
|
|
|
@ -10,18 +10,18 @@ pub use interrupt::Interrupt;
|
|||
pub use interrupt::IrqBitmask;
|
||||
pub mod gba;
|
||||
pub use gba::GameBoyAdvance;
|
||||
pub mod bus;
|
||||
pub mod dma;
|
||||
pub mod keypad;
|
||||
pub mod timer;
|
||||
pub use bus::*;
|
||||
|
||||
pub use super::{AudioInterface, InputInterface, VideoInterface};
|
||||
|
||||
use crate::debugger;
|
||||
|
||||
use zip;
|
||||
|
||||
pub trait SyncedIoDevice {
|
||||
fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GBAError {
|
||||
IO(::std::io::Error),
|
||||
|
|
|
@ -3,17 +3,13 @@ use std::ops::Add;
|
|||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use super::arm7tdmi::bus::Bus;
|
||||
use super::arm7tdmi::Addr;
|
||||
use super::cartridge::Cartridge;
|
||||
use super::gpu::GpuState;
|
||||
use super::gpu::{GpuState, VIDEO_RAM_SIZE};
|
||||
use super::iodev::IoDevices;
|
||||
use super::{Addr, Bus};
|
||||
|
||||
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||
const WORK_RAM_SIZE: usize = 256 * 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 EWRAM_ADDR: u32 = 0x0200_0000;
|
||||
|
@ -144,9 +140,6 @@ pub struct SysBus {
|
|||
bios: BoxedMemory,
|
||||
onboard_work_ram: BoxedMemory,
|
||||
internal_work_ram: BoxedMemory,
|
||||
pub palette_ram: BoxedMemory,
|
||||
pub vram: BoxedMemory,
|
||||
pub oam: BoxedMemory,
|
||||
gamepak: Cartridge,
|
||||
dummy: DummyBus,
|
||||
|
||||
|
@ -161,9 +154,6 @@ impl SysBus {
|
|||
bios: BoxedMemory::new(bios_rom.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()),
|
||||
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,
|
||||
dummy: DummyBus([0; 4]),
|
||||
|
||||
|
@ -190,15 +180,15 @@ impl SysBus {
|
|||
ofs & 0x7ff
|
||||
}
|
||||
}),
|
||||
PALRAM_ADDR => (&self.palette_ram, ofs & 0x3ff),
|
||||
VRAM_ADDR => (&self.vram, {
|
||||
PALRAM_ADDR => (&self.io.gpu.palette_ram, ofs & 0x3ff),
|
||||
VRAM_ADDR => (&self.io.gpu.vram, {
|
||||
let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1);
|
||||
if ofs > 0x18000 {
|
||||
ofs -= 0x8000;
|
||||
}
|
||||
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 => {
|
||||
(&self.gamepak, addr & 0x01ff_ffff)
|
||||
}
|
||||
|
@ -220,15 +210,15 @@ impl SysBus {
|
|||
ofs & 0x7ff
|
||||
}
|
||||
}),
|
||||
PALRAM_ADDR => (&mut self.palette_ram, ofs & 0x3ff),
|
||||
VRAM_ADDR => (&mut self.vram, {
|
||||
PALRAM_ADDR => (&mut self.io.gpu.palette_ram, ofs & 0x3ff),
|
||||
VRAM_ADDR => (&mut self.io.gpu.vram, {
|
||||
let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1);
|
||||
if ofs > 0x18000 {
|
||||
ofs -= 0x8000;
|
||||
}
|
||||
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 => {
|
||||
(&mut self.gamepak, addr & 0x01ff_ffff)
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ use std::sync::Arc;
|
|||
use std::time;
|
||||
|
||||
use crate::core::arm7tdmi::arm::ArmInstruction;
|
||||
use crate::core::arm7tdmi::bus::Bus;
|
||||
use crate::core::arm7tdmi::thumb::ThumbInstruction;
|
||||
use crate::core::arm7tdmi::{Addr, CpuState};
|
||||
use crate::core::arm7tdmi::CpuState;
|
||||
use crate::core::GBAError;
|
||||
use crate::core::{Addr, Bus};
|
||||
use crate::disass::Disassembler;
|
||||
use crate::{AudioInterface, InputInterface, VideoInterface};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
extern crate ctrlc;
|
||||
use std::fs::File;
|
||||
use std::io::{self, prelude::*, BufReader};
|
||||
use std::io::{prelude::*, BufReader};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -9,10 +9,9 @@ use rustyline::Editor;
|
|||
|
||||
use colored::*;
|
||||
|
||||
use super::core::arm7tdmi::{Addr, Bus, CpuError};
|
||||
use super::core::arm7tdmi::CpuError;
|
||||
use super::core::GameBoyAdvance;
|
||||
|
||||
use super::{AudioInterface, InputInterface, VideoInterface};
|
||||
use super::core::{Addr, Bus};
|
||||
|
||||
mod parser;
|
||||
use parser::{parse_expr, DerefType, Expr, Value};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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;
|
||||
|
||||
pub struct Disassembler<'a, D>
|
||||
|
|
Reference in a new issue