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:
Michel Heily 2019-12-29 23:03:57 +02:00
parent 885bce2aa4
commit b00fbfb38c
23 changed files with 389 additions and 379 deletions

View file

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

View file

@ -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::*;

View file

@ -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,
};

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
use super::Addr;
pub type Addr = u32;
pub trait Bus {
fn read_32(&self, addr: Addr) -> u32;

View file

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

View file

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

View file

@ -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();
}

View file

@ -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
View file

@ -0,0 +1,3 @@
pub(super) mod bitmap;
pub(super) mod obj;
pub(super) mod text;

View 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;
}
}
}

View file

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

View 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;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
}

View file

@ -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};

View file

@ -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};

View file

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