core: gpu: Implement rotation/scale background rendering
Displaying sbb_aff.gba and rsbin demos correctly. Problems in mode7: * Incorrect first scanline in m7_demo.gba * Mariokart scaling is off Former-commit-id: 27655a7a3cde0e9cdabd727a6ec9c99270a99b35
This commit is contained in:
parent
12ebcf44a2
commit
d97d07774f
|
@ -12,6 +12,8 @@ use crate::num::FromPrimitive;
|
|||
|
||||
mod render;
|
||||
|
||||
use render::Point;
|
||||
|
||||
mod mosaic;
|
||||
mod rgb15;
|
||||
mod sfx;
|
||||
|
@ -154,6 +156,8 @@ pub struct BgAffine {
|
|||
pub pd: i16, // dmy
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub internal_x: i32,
|
||||
pub internal_y: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
@ -282,6 +286,14 @@ impl Gpu {
|
|||
self.frame_buffer[index2d!(usize, x, y, DISPLAY_WIDTH)] = p.to_rgb24();
|
||||
}
|
||||
|
||||
pub fn get_ref_point(&self, bg: usize) -> Point {
|
||||
assert!(bg == 2 || bg == 3);
|
||||
(
|
||||
self.bg_aff[bg - 2].internal_x,
|
||||
self.bg_aff[bg - 2].internal_y,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_scanline(&mut self) {
|
||||
match self.dispcnt.mode() {
|
||||
0 => {
|
||||
|
@ -343,6 +355,73 @@ impl Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn on_state_completed(
|
||||
&mut self,
|
||||
completed: GpuState,
|
||||
sb: &mut SysBus,
|
||||
irqs: &mut IrqBitmask,
|
||||
) {
|
||||
match self.state {
|
||||
HDraw => {
|
||||
// Transition to HBlank
|
||||
self.state = HBlank;
|
||||
self.cycles_left_for_current_state = CYCLES_HBLANK;
|
||||
self.dispstat.set_hblank_flag(true);
|
||||
|
||||
if self.dispstat.hblank_irq_enable() {
|
||||
irqs.set_LCD_HBlank(true);
|
||||
};
|
||||
sb.io.dmac.notify_hblank();
|
||||
}
|
||||
HBlank => {
|
||||
self.update_vcount(self.vcount + 1, irqs);
|
||||
|
||||
if self.vcount < DISPLAY_HEIGHT {
|
||||
self.state = HDraw;
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.render_scanline();
|
||||
// update BG2/3 reference points on the end of a scanline
|
||||
for i in 0..2 {
|
||||
self.bg_aff[i].internal_x += self.bg_aff[i].pb as i16 as i32;
|
||||
self.bg_aff[i].internal_y += self.bg_aff[i].pd as i16 as i32;
|
||||
}
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
} else {
|
||||
self.state = VBlank;
|
||||
|
||||
// latch BG2/3 reference points on vblank
|
||||
for i in 0..2 {
|
||||
self.bg_aff[i].internal_x = self.bg_aff[i].x;
|
||||
self.bg_aff[i].internal_y = self.bg_aff[i].y;
|
||||
}
|
||||
|
||||
self.dispstat.set_vblank_flag(true);
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
if self.dispstat.vblank_irq_enable() {
|
||||
irqs.set_LCD_VBlank(true);
|
||||
};
|
||||
|
||||
sb.io.dmac.notify_vblank();
|
||||
self.video_device.borrow_mut().render(&self.frame_buffer);
|
||||
self.cycles_left_for_current_state = CYCLES_SCANLINE;
|
||||
}
|
||||
}
|
||||
VBlank => {
|
||||
if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
|
||||
self.update_vcount(self.vcount + 1, irqs);
|
||||
self.cycles_left_for_current_state = CYCLES_SCANLINE;
|
||||
} else {
|
||||
self.update_vcount(0, irqs);
|
||||
self.dispstat.set_vblank_flag(false);
|
||||
self.render_scanline();
|
||||
self.state = HDraw;
|
||||
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the new gpu state
|
||||
pub fn step(
|
||||
&mut self,
|
||||
|
@ -354,51 +433,7 @@ impl Gpu {
|
|||
if self.cycles_left_for_current_state <= cycles {
|
||||
let overshoot = cycles - self.cycles_left_for_current_state;
|
||||
|
||||
// handle the state change
|
||||
match self.state {
|
||||
HDraw => {
|
||||
// Transition to HBlank
|
||||
self.state = HBlank;
|
||||
self.cycles_left_for_current_state = CYCLES_HBLANK;
|
||||
self.dispstat.set_hblank_flag(true);
|
||||
if self.dispstat.hblank_irq_enable() {
|
||||
irqs.set_LCD_HBlank(true);
|
||||
};
|
||||
sb.io.dmac.notify_hblank();
|
||||
}
|
||||
HBlank => {
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.update_vcount(self.vcount + 1, irqs);
|
||||
|
||||
if self.vcount < DISPLAY_HEIGHT {
|
||||
self.render_scanline();
|
||||
self.state = HDraw;
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
} else {
|
||||
self.state = VBlank;
|
||||
self.cycles_left_for_current_state = CYCLES_SCANLINE;
|
||||
self.dispstat.set_vblank_flag(true);
|
||||
if self.dispstat.vblank_irq_enable() {
|
||||
irqs.set_LCD_VBlank(true);
|
||||
};
|
||||
sb.io.dmac.notify_vblank();
|
||||
self.video_device.borrow_mut().render(&self.frame_buffer);
|
||||
}
|
||||
}
|
||||
VBlank => {
|
||||
if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
|
||||
self.update_vcount(self.vcount + 1, irqs);
|
||||
self.cycles_left_for_current_state = CYCLES_SCANLINE;
|
||||
} else {
|
||||
self.update_vcount(0, irqs);
|
||||
self.dispstat.set_vblank_flag(false);
|
||||
self.render_scanline();
|
||||
self.state = HDraw;
|
||||
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
}
|
||||
}
|
||||
};
|
||||
self.on_state_completed(self.state, sb, irqs);
|
||||
|
||||
// handle the overshoot
|
||||
if overshoot < self.cycles_left_for_current_state {
|
||||
|
|
|
@ -44,14 +44,9 @@ impl BgControl {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn size_affine(&self) -> (u32, u32) {
|
||||
match self.bg_size() {
|
||||
0b00 => (128, 128),
|
||||
0b01 => (256, 256),
|
||||
0b10 => (512, 512),
|
||||
0b11 => (1024, 1024),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
pub fn size_affine(&self) -> (i32, i32) {
|
||||
let x = 128 << self.bg_size();
|
||||
(x, x)
|
||||
}
|
||||
|
||||
pub fn tile_format(&self) -> (u32, PixelFormat) {
|
||||
|
|
|
@ -1,3 +1,48 @@
|
|||
pub(super) mod bitmap;
|
||||
pub(super) mod obj;
|
||||
pub(super) mod text;
|
||||
|
||||
pub(super) type Point = (i32, i32);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ViewPort {
|
||||
pub origin: Point,
|
||||
pub w: i32,
|
||||
pub h: i32,
|
||||
}
|
||||
|
||||
impl ViewPort {
|
||||
pub fn new(w: i32, h: i32) -> ViewPort {
|
||||
ViewPort {
|
||||
origin: (0, 0),
|
||||
w: w,
|
||||
h: h,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, p: Point) -> bool {
|
||||
let (mut x, mut y) = p;
|
||||
|
||||
x -= self.origin.0;
|
||||
y -= self.origin.1;
|
||||
|
||||
x >= 0 && x < self.w && y >= 0 && y < self.h
|
||||
}
|
||||
}
|
||||
|
||||
use super::consts::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
|
||||
pub(super) static SCREEN_VIEWPORT: ViewPort = ViewPort {
|
||||
origin: (0, 0),
|
||||
w: DISPLAY_WIDTH as i32,
|
||||
h: DISPLAY_HEIGHT as i32,
|
||||
};
|
||||
|
||||
pub(super) mod utils {
|
||||
use super::Point;
|
||||
|
||||
#[inline]
|
||||
pub fn transform_bg_point(ref_point: Point, screen_x: i32, pa: i32, pc: i32) -> Point {
|
||||
let (ref_x, ref_y) = ref_point;
|
||||
((ref_x + screen_x * pa) >> 8, (ref_y + screen_x * pc) >> 8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,59 @@
|
|||
//! 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
//! Rendering for modes 4-5
|
||||
|
||||
use super::super::consts::*;
|
||||
use super::super::Gpu;
|
||||
use super::super::Rgb15;
|
||||
|
||||
use super::{utils, SCREEN_VIEWPORT};
|
||||
|
||||
use crate::core::Bus;
|
||||
|
||||
impl Gpu {
|
||||
pub(in super::super) fn render_mode3(&mut self, bg: usize) {
|
||||
let y = self.vcount;
|
||||
|
||||
let pa = self.bg_aff[bg - 2].pa as i32;
|
||||
let pc = self.bg_aff[bg - 2].pc as i32;
|
||||
let ref_point = self.get_ref_point(bg);
|
||||
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let t = utils::transform_bg_point(ref_point, x as i32, pa, pc);
|
||||
if !SCREEN_VIEWPORT.contains_point(t) {
|
||||
self.bg[bg].line[x] = Rgb15::TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
|
||||
let pixel_index = index2d!(u32, t.0, t.1, 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;
|
||||
|
||||
let pa = self.bg_aff[bg - 2].pa as i32;
|
||||
let pc = self.bg_aff[bg - 2].pc as i32;
|
||||
let ref_point = self.get_ref_point(bg);
|
||||
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let t = utils::transform_bg_point(ref_point, x as i32, pa, pc);
|
||||
if !SCREEN_VIEWPORT.contains_point(t) {
|
||||
self.bg[bg].line[x] = Rgb15::TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
let bitmap_index = index2d!(u32, t.0, t.1, 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,94 +1,136 @@
|
|||
//! 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;
|
||||
}
|
||||
//! Rendering for modes 0-3
|
||||
|
||||
use super::super::consts::*;
|
||||
use super::super::Rgb15;
|
||||
use super::super::{Gpu, PixelFormat, SCREEN_BLOCK_SIZE};
|
||||
use super::{utils, ViewPort};
|
||||
|
||||
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) {
|
||||
assert!(bg == 2 || bg == 3);
|
||||
|
||||
let texture_size = 128 << self.bg[bg].bgcnt.bg_size();
|
||||
let viewport = ViewPort::new(texture_size, texture_size);
|
||||
|
||||
let ref_point = self.get_ref_point(bg);
|
||||
let pa = self.bg_aff[bg - 2].pa as i16 as i32;
|
||||
let pc = self.bg_aff[bg - 2].pc as i16 as i32;
|
||||
|
||||
let screen_block = self.bg[bg].bgcnt.screen_block();
|
||||
let char_block = self.bg[bg].bgcnt.char_block();
|
||||
|
||||
let wraparound = self.bg[bg].bgcnt.affine_wraparound();
|
||||
|
||||
for screen_x in 0..(DISPLAY_WIDTH as i32) {
|
||||
let mut t = utils::transform_bg_point(ref_point, screen_x, pa, pc);
|
||||
|
||||
if !viewport.contains_point(t) {
|
||||
if wraparound {
|
||||
t.0 = t.0.rem_euclid(texture_size);
|
||||
t.1 = t.1.rem_euclid(texture_size);
|
||||
} else {
|
||||
self.bg[bg].line[screen_x as usize] = Rgb15::TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let map_addr = screen_block + index2d!(u32, t.0 / 8, t.1 / 8, texture_size / 8);
|
||||
let tile_index = self.vram.read_8(map_addr - VRAM_ADDR) as u32;
|
||||
let tile_addr = char_block + tile_index * 0x40;
|
||||
|
||||
let color = self.get_palette_color(
|
||||
self.read_pixel_index(
|
||||
tile_addr,
|
||||
(t.0 % 8) as u32,
|
||||
(t.1 % 8) as u32,
|
||||
PixelFormat::BPP8,
|
||||
) as u32,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
self.bg[bg].line[screen_x as usize] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
struct TileMapEntry(u16);
|
||||
u16;
|
||||
u32, tile_index, _: 9, 0;
|
||||
x_flip, _ : 10;
|
||||
y_flip, _ : 11;
|
||||
palette_bank, _ : 15, 12;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use super::gpu::*;
|
|||
use super::interrupt::InterruptController;
|
||||
use super::keypad;
|
||||
use super::sound::SoundController;
|
||||
use super::sysbus::BoxedMemory;
|
||||
use super::timer::Timers;
|
||||
use super::{Addr, Bus};
|
||||
|
||||
|
@ -27,8 +26,6 @@ pub struct IoDevices {
|
|||
pub post_boot_flag: bool,
|
||||
pub waitcnt: WaitControl, // TODO also implement 4000800
|
||||
pub haltcnt: HaltState,
|
||||
|
||||
mem: BoxedMemory,
|
||||
}
|
||||
|
||||
impl IoDevices {
|
||||
|
@ -39,7 +36,6 @@ impl IoDevices {
|
|||
timers: Timers::new(),
|
||||
dmac: DmaController::new(),
|
||||
intc: InterruptController::new(),
|
||||
mem: BoxedMemory::new(vec![0; 0x800].into_boxed_slice()),
|
||||
post_boot_flag: false,
|
||||
haltcnt: HaltState::Running,
|
||||
keyinput: keypad::KEYINPUT_ALL_RELEASED,
|
||||
|
@ -135,14 +131,40 @@ impl Bus for IoDevices {
|
|||
REG_BG2VOFS => io.gpu.bg[2].bgvofs = value & 0x1ff,
|
||||
REG_BG3HOFS => io.gpu.bg[3].bghofs = value & 0x1ff,
|
||||
REG_BG3VOFS => io.gpu.bg[3].bgvofs = value & 0x1ff,
|
||||
REG_BG2X_L => io.gpu.bg_aff[0].x |= (value as u32) as i32,
|
||||
REG_BG2X_H => io.gpu.bg_aff[0].x |= ((value as u32) << 16) as i32,
|
||||
REG_BG2Y_L => io.gpu.bg_aff[0].y |= (value as u32) as i32,
|
||||
REG_BG2Y_H => io.gpu.bg_aff[0].y |= ((value as u32) << 16) as i32,
|
||||
REG_BG3X_L => io.gpu.bg_aff[1].x |= (value as u32) as i32,
|
||||
REG_BG3X_H => io.gpu.bg_aff[1].x |= ((value as u32) << 16) as i32,
|
||||
REG_BG3Y_L => io.gpu.bg_aff[1].y |= (value as u32) as i32,
|
||||
REG_BG3Y_H => io.gpu.bg_aff[1].y |= ((value as u32) << 16) as i32,
|
||||
REG_BG2X_L | REG_BG3X_L => {
|
||||
let index = (io_addr - REG_BG2X_L) / 0x10;
|
||||
let t = io.gpu.bg_aff[index as usize].x as u32;
|
||||
io.gpu.bg_aff[index as usize].x = ((t & 0xffff0000) + (value as u32)) as i32;
|
||||
if io.gpu.state != GpuState::VBlank {
|
||||
io.gpu.bg_aff[index as usize].internal_x = io.gpu.bg_aff[index as usize].x;
|
||||
}
|
||||
}
|
||||
REG_BG2Y_L | REG_BG3Y_L => {
|
||||
let index = (io_addr - REG_BG2X_L) / 0x10;
|
||||
let t = io.gpu.bg_aff[index as usize].y as u32;
|
||||
io.gpu.bg_aff[index as usize].y = ((t & 0xffff0000) + (value as u32)) as i32;
|
||||
if io.gpu.state != GpuState::VBlank {
|
||||
io.gpu.bg_aff[index as usize].internal_y = io.gpu.bg_aff[index as usize].y;
|
||||
}
|
||||
}
|
||||
REG_BG2X_H | REG_BG3X_H => {
|
||||
let index = (io_addr - REG_BG2X_L) / 0x10;
|
||||
let t = io.gpu.bg_aff[index as usize].x;
|
||||
io.gpu.bg_aff[index as usize].x =
|
||||
(t & 0xffff) | ((sign_extend_i32((value & 0xfff) as i32, 12)) << 16);
|
||||
if io.gpu.state != GpuState::VBlank {
|
||||
io.gpu.bg_aff[index as usize].internal_x = io.gpu.bg_aff[index as usize].x;
|
||||
}
|
||||
}
|
||||
REG_BG2Y_H | REG_BG3Y_H => {
|
||||
let index = (io_addr - REG_BG2X_L) / 0x10;
|
||||
let t = io.gpu.bg_aff[index as usize].y;
|
||||
io.gpu.bg_aff[index as usize].y =
|
||||
(t & 0xffff) | ((sign_extend_i32((value & 0xfff) as i32, 12)) << 16);
|
||||
if io.gpu.state != GpuState::VBlank {
|
||||
io.gpu.bg_aff[index as usize].internal_y = io.gpu.bg_aff[index as usize].y;
|
||||
}
|
||||
}
|
||||
REG_BG2PA => io.gpu.bg_aff[0].pa = value as i16,
|
||||
REG_BG2PB => io.gpu.bg_aff[0].pb = value as i16,
|
||||
REG_BG2PC => io.gpu.bg_aff[0].pc = value as i16,
|
||||
|
@ -479,3 +501,8 @@ pub fn io_reg_string(addr: u32) -> &'static str {
|
|||
_ => "UNKNOWN",
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_extend_i32(value: i32, size: u32) -> i32 {
|
||||
let shift = 32 - size;
|
||||
((value << shift) as i32) >> shift
|
||||
}
|
||||
|
|
Reference in a new issue