Add blending and mosaic SFX, and cleanup code.
Former-commit-id: b9f0ccaf1820da61f49ebeb2af5beff5cccd722f
This commit is contained in:
parent
c4b24fde02
commit
639993edd7
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -599,6 +599,7 @@ dependencies = [
|
||||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -20,6 +20,7 @@ sdl2 = "0.32.2"
|
||||||
minifb = "0.11.2"
|
minifb = "0.11.2"
|
||||||
time = "0.1.42"
|
time = "0.1.42"
|
||||||
bitfield = "0.13.1"
|
bitfield = "0.13.1"
|
||||||
|
bitflags = "1.1.0"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|
|
@ -7,7 +7,7 @@ extern crate minifb;
|
||||||
use minifb::{Key, Window, WindowOptions};
|
use minifb::{Key, Window, WindowOptions};
|
||||||
|
|
||||||
use super::EmulatorBackend;
|
use super::EmulatorBackend;
|
||||||
use crate::core::gpu::Gpu;
|
use crate::core::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
|
||||||
use crate::core::keypad;
|
use crate::core::keypad;
|
||||||
|
|
||||||
pub struct MinifbBackend {
|
pub struct MinifbBackend {
|
||||||
|
@ -20,8 +20,8 @@ impl MinifbBackend {
|
||||||
pub fn new() -> MinifbBackend {
|
pub fn new() -> MinifbBackend {
|
||||||
let window = Window::new(
|
let window = Window::new(
|
||||||
"rustboyadvance-ng",
|
"rustboyadvance-ng",
|
||||||
Gpu::DISPLAY_WIDTH,
|
DISPLAY_WIDTH,
|
||||||
Gpu::DISPLAY_HEIGHT,
|
DISPLAY_HEIGHT,
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
borderless: true,
|
borderless: true,
|
||||||
scale: minifb::Scale::X4,
|
scale: minifb::Scale::X4,
|
||||||
|
@ -39,7 +39,7 @@ impl MinifbBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmulatorBackend for MinifbBackend {
|
impl EmulatorBackend for MinifbBackend {
|
||||||
fn render(&mut self, buffer: Vec<u32>) {
|
fn render(&mut self, buffer: &[u32]) {
|
||||||
self.frames_rendered += 1;
|
self.frames_rendered += 1;
|
||||||
if self.first_frame_start.elapsed() >= time::Duration::from_secs(1) {
|
if self.first_frame_start.elapsed() >= time::Duration::from_secs(1) {
|
||||||
let title = format!("rustboyadvance-ng ({} fps)", self.frames_rendered);
|
let title = format!("rustboyadvance-ng ({} fps)", self.frames_rendered);
|
||||||
|
@ -47,7 +47,7 @@ impl EmulatorBackend for MinifbBackend {
|
||||||
self.first_frame_start = time::Instant::now();
|
self.first_frame_start = time::Instant::now();
|
||||||
self.frames_rendered = 0;
|
self.frames_rendered = 0;
|
||||||
}
|
}
|
||||||
self.window.update_with_buffer(&buffer).unwrap();
|
self.window.update_with_buffer(buffer).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_key_state(&mut self) -> u16 {
|
fn get_key_state(&mut self) -> u16 {
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod sdl2_backend;
|
||||||
pub use sdl2_backend::Sdl2Backend;
|
pub use sdl2_backend::Sdl2Backend;
|
||||||
|
|
||||||
pub trait EmulatorBackend {
|
pub trait EmulatorBackend {
|
||||||
fn render(&mut self, buffer: Vec<u32>);
|
fn render(&mut self, buffer: &[u32]);
|
||||||
|
|
||||||
fn get_key_state(&mut self) -> u16;
|
fn get_key_state(&mut self) -> u16;
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,5 @@ impl EmulatorBackend for DummyBackend {
|
||||||
fn get_key_state(&mut self) -> u16 {
|
fn get_key_state(&mut self) -> u16 {
|
||||||
keypad::KEYINPUT_ALL_RELEASED
|
keypad::KEYINPUT_ALL_RELEASED
|
||||||
}
|
}
|
||||||
fn render(&mut self, _buffer: Vec<u32>) {}
|
fn render(&mut self, _buffer: &[u32]) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use crate::bit::BitIndex;
|
use crate::bit::BitIndex;
|
||||||
|
@ -6,13 +5,13 @@ use crate::bit::BitIndex;
|
||||||
extern crate sdl2;
|
extern crate sdl2;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::pixels::{Color, PixelFormat, PixelFormatEnum};
|
use sdl2::pixels::{Color, PixelFormatEnum};
|
||||||
use sdl2::rect::{Point, Rect};
|
use sdl2::rect::{Point, Rect};
|
||||||
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
|
use sdl2::render::{TextureCreator, WindowCanvas};
|
||||||
use sdl2::video::{Window, WindowContext};
|
use sdl2::video::WindowContext;
|
||||||
|
|
||||||
use super::EmulatorBackend;
|
use super::EmulatorBackend;
|
||||||
use crate::core::gpu::Gpu;
|
use crate::core::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
|
||||||
use crate::core::keypad;
|
use crate::core::keypad;
|
||||||
|
|
||||||
pub struct Sdl2Backend {
|
pub struct Sdl2Backend {
|
||||||
|
@ -24,8 +23,8 @@ pub struct Sdl2Backend {
|
||||||
keyinput: u16,
|
keyinput: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SCREEN_WIDTH: u32 = Gpu::DISPLAY_WIDTH as u32;
|
const SCREEN_WIDTH: u32 = DISPLAY_WIDTH as u32;
|
||||||
const SCREEN_HEIGHT: u32 = Gpu::DISPLAY_HEIGHT as u32;
|
const SCREEN_HEIGHT: u32 = DISPLAY_HEIGHT as u32;
|
||||||
|
|
||||||
impl Sdl2Backend {
|
impl Sdl2Backend {
|
||||||
pub fn new() -> Sdl2Backend {
|
pub fn new() -> Sdl2Backend {
|
||||||
|
@ -57,7 +56,7 @@ impl Sdl2Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmulatorBackend for Sdl2Backend {
|
impl EmulatorBackend for Sdl2Backend {
|
||||||
fn render(&mut self, buffer: Vec<u32>) {
|
fn render(&mut self, buffer: &[u32]) {
|
||||||
let mut texture = self
|
let mut texture = self
|
||||||
.tc
|
.tc
|
||||||
.create_texture_target(PixelFormatEnum::RGB24, SCREEN_WIDTH, SCREEN_HEIGHT)
|
.create_texture_target(PixelFormatEnum::RGB24, SCREEN_WIDTH, SCREEN_HEIGHT)
|
||||||
|
|
|
@ -65,10 +65,9 @@ fn run_emulator(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
loop {
|
loop {
|
||||||
let start_time = time::Instant::now();
|
let start_time = time::Instant::now();
|
||||||
gba.frame();
|
gba.frame();
|
||||||
let time_passed = time::Instant::now() - start_time;
|
let time_passed = start_time.elapsed();
|
||||||
if time_passed <= frame_time {
|
if time_passed <= frame_time {
|
||||||
let duration = frame_time - time_passed;
|
::std::thread::sleep(frame_time - time_passed);
|
||||||
::std::thread::sleep(duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl GameBoyAdvance {
|
||||||
let cycles = self.emulate_cpu();
|
let cycles = self.emulate_cpu();
|
||||||
self.emulate_peripherals(cycles);
|
self.emulate_peripherals(cycles);
|
||||||
}
|
}
|
||||||
self.backend.render(self.io.borrow().gpu.render());
|
self.backend.render(self.io.borrow().gpu.get_framebuffer());
|
||||||
while self.io.borrow().gpu.state == GpuState::VBlank {
|
while self.io.borrow().gpu.state == GpuState::VBlank {
|
||||||
let cycles = self.emulate_cpu();
|
let cycles = self.emulate_cpu();
|
||||||
self.emulate_peripherals(cycles);
|
self.emulate_peripherals(cycles);
|
||||||
|
@ -106,7 +106,7 @@ impl GameBoyAdvance {
|
||||||
self.emulate_peripherals(cycles);
|
self.emulate_peripherals(cycles);
|
||||||
|
|
||||||
if self.io.borrow().gpu.state == GpuState::VBlank {
|
if self.io.borrow().gpu.state == GpuState::VBlank {
|
||||||
self.backend.render(self.io.borrow().gpu.render());
|
self.backend.render(self.io.borrow().gpu.get_framebuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(executed_insn)
|
Ok(executed_insn)
|
||||||
|
|
117
src/core/gpu/blend.rs
Normal file
117
src/core/gpu/blend.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
use std::cmp;
|
||||||
|
use std::ops::Sub;
|
||||||
|
|
||||||
|
use super::regs::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive, Clone, Copy)]
|
||||||
|
pub enum BldMode {
|
||||||
|
BldNone = 0b00,
|
||||||
|
BldAlpha = 0b01,
|
||||||
|
BldWhite = 0b10,
|
||||||
|
BldBlack = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for BldMode {
|
||||||
|
fn from(v: u16) -> BldMode {
|
||||||
|
BldMode::from_u16(v).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rgb15 {
|
||||||
|
fn blend_with(self, other: Rgb15, my_weight: u16, other_weight: u16) -> Rgb15 {
|
||||||
|
let r = cmp::min(31, (self.r() * my_weight + other.r() * other_weight) >> 4);
|
||||||
|
let g = cmp::min(31, (self.g() * my_weight + other.g() * other_weight) >> 4);
|
||||||
|
let b = cmp::min(31, (self.b() * my_weight + other.b() * other_weight) >> 4);
|
||||||
|
Rgb15::from_rgb(r, g, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
fn get_topmost_color(
|
||||||
|
&self,
|
||||||
|
sb: &SysBus,
|
||||||
|
screen_x: usize,
|
||||||
|
layers: &BlendFlags,
|
||||||
|
) -> Option<Rgb15> {
|
||||||
|
// TODO - only BGs are supported, don't forget OBJs
|
||||||
|
|
||||||
|
let mut color: Option<Rgb15> = None;
|
||||||
|
|
||||||
|
// priorities are 0-4 when 0 is the highest
|
||||||
|
'outer: for priority in 0..4 {
|
||||||
|
for bg in 0..4 {
|
||||||
|
let c = self.bg[bg].line[screen_x];
|
||||||
|
if self.dispcnt.disp_bg(bg)
|
||||||
|
&& !c.is_transparent()
|
||||||
|
&& (layers.is_empty() || layers.contains(BG_LAYER_FLAG[bg]))
|
||||||
|
&& self.bg[bg].bgcnt.priority() == priority
|
||||||
|
{
|
||||||
|
color = Some(c);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if color.is_none() && layers.contains(BlendFlags::BACKDROP) {
|
||||||
|
color = Some(Rgb15(sb.palette_ram.read_16(0)))
|
||||||
|
}
|
||||||
|
color
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blend_line(&mut self, sb: &mut SysBus) -> Scanline {
|
||||||
|
let mut bld_line = Scanline::default();
|
||||||
|
let bldmode = self.bldcnt.mode();
|
||||||
|
match bldmode {
|
||||||
|
BldMode::BldAlpha => {
|
||||||
|
let top_layers = self.bldcnt.top();
|
||||||
|
let bottom_layers = self.bldcnt.bottom();
|
||||||
|
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
if let Some(top_color) = self.get_topmost_color(sb, x, &top_layers) {
|
||||||
|
if let Some(bot_color) = self.get_topmost_color(sb, x, &bottom_layers) {
|
||||||
|
let eva = self.bldalpha.eva();
|
||||||
|
let evb = self.bldalpha.evb();
|
||||||
|
bld_line[x] = top_color.blend_with(bot_color, eva, evb);
|
||||||
|
} else {
|
||||||
|
bld_line[x] = top_color;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bld_line[x] = self.get_topmost_color(sb, x, &BlendFlags::all()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BldMode::BldWhite => {
|
||||||
|
let top_layers = self.bldcnt.top();
|
||||||
|
let evy = self.bldy;
|
||||||
|
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
bld_line[x] =
|
||||||
|
if let Some(top_color) = self.get_topmost_color(sb, x, &top_layers) {
|
||||||
|
top_color.blend_with(Rgb15::WHITE, 16 - evy, evy)
|
||||||
|
} else {
|
||||||
|
self.get_topmost_color(sb, x, &BlendFlags::all()).unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BldMode::BldBlack => {
|
||||||
|
let top_layers = self.bldcnt.top();
|
||||||
|
let evy = self.bldy;
|
||||||
|
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
bld_line[x] =
|
||||||
|
if let Some(top_color) = self.get_topmost_color(sb, x, &top_layers) {
|
||||||
|
top_color.blend_with(Rgb15::BLACK, 16 - evy, evy)
|
||||||
|
} else {
|
||||||
|
self.get_topmost_color(sb, x, &BlendFlags::all()).unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BldMode::BldNone => {
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
bld_line[x] = self.get_topmost_color(sb, x, &BlendFlags::all()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bld_line
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,68 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use super::arm7tdmi::{Addr, Bus};
|
use super::arm7tdmi::{Addr, Bus};
|
||||||
pub use super::palette::{PixelFormat, Rgb15};
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::bitfield::Bit;
|
use crate::bitfield::Bit;
|
||||||
use crate::num::FromPrimitive;
|
use crate::num::FromPrimitive;
|
||||||
|
|
||||||
|
mod blend;
|
||||||
|
mod mosaic;
|
||||||
|
|
||||||
mod regs;
|
mod regs;
|
||||||
pub use regs::*;
|
pub use regs::*;
|
||||||
|
|
||||||
pub const VRAM_ADDR: Addr = 0x0600_0000;
|
pub const VRAM_ADDR: Addr = 0x0600_0000;
|
||||||
|
pub const DISPLAY_WIDTH: usize = 240;
|
||||||
|
pub const DISPLAY_HEIGHT: usize = 160;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
const TILE_SIZE: u32 = 0x20;
|
||||||
|
|
||||||
|
// TODO - remove the one in palette.rs
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
pub struct Rgb15(u16);
|
||||||
|
impl Debug;
|
||||||
|
pub r, set_r: 4, 0;
|
||||||
|
pub g, set_g: 9, 5;
|
||||||
|
pub b, set_b: 14, 10;
|
||||||
|
pub is_transparent, _ : 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive, Copy, Clone)]
|
||||||
|
pub enum PixelFormat {
|
||||||
|
BPP4 = 0,
|
||||||
|
BPP8 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Primitive, Clone, Copy)]
|
#[derive(Debug, Primitive, Clone, Copy)]
|
||||||
pub enum BGMode {
|
pub enum BGMode {
|
||||||
|
@ -41,21 +93,20 @@ impl Default for GpuState {
|
||||||
}
|
}
|
||||||
use GpuState::*;
|
use GpuState::*;
|
||||||
|
|
||||||
pub struct FrameBuffer([Rgb15; 512 * 512]);
|
pub struct FrameBuffer([u32; DISPLAY_WIDTH * DISPLAY_HEIGHT]);
|
||||||
|
|
||||||
impl fmt::Debug for FrameBuffer {
|
impl fmt::Debug for FrameBuffer {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "FrameBuffer: ")?;
|
write!(f, "FrameBuffer: ")?;
|
||||||
for i in 0..6 {
|
for i in 0..6 {
|
||||||
let (r, g, b) = self.0[i].get_rgb24();
|
write!(f, "#{:06x}, ", self[i])?;
|
||||||
write!(f, "#{:x}{:x}{:x}, ", r, g, b)?;
|
|
||||||
}
|
}
|
||||||
write!(f, "...")
|
write!(f, "...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Index<usize> for FrameBuffer {
|
impl std::ops::Index<usize> for FrameBuffer {
|
||||||
type Output = Rgb15;
|
type Output = u32;
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
&self.0[index]
|
&self.0[index]
|
||||||
}
|
}
|
||||||
|
@ -67,67 +118,108 @@ impl std::ops::IndexMut<usize> for FrameBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Scanline([Rgb15; DISPLAY_WIDTH]);
|
||||||
|
|
||||||
|
impl Default for Scanline {
|
||||||
|
fn default() -> Scanline {
|
||||||
|
Scanline([Rgb15(0); DISPLAY_WIDTH])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Scanline {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Scanline: ")?;
|
||||||
|
for i in 0..6 {
|
||||||
|
write!(f, "#{:06x}, ", self[i].0)?;
|
||||||
|
}
|
||||||
|
write!(f, "...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<usize> for Scanline {
|
||||||
|
type Output = Rgb15;
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<usize> for Scanline {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
|
pub struct Bg {
|
||||||
|
pub bgcnt: BgControl,
|
||||||
|
pub bgvofs: u16,
|
||||||
|
pub bghofs: u16,
|
||||||
|
line: Scanline,
|
||||||
|
|
||||||
|
// for mosaic
|
||||||
|
mosaic_first_row: Scanline,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
|
pub struct BgAffine {
|
||||||
|
pub pa: i16, // dx
|
||||||
|
pub pb: i16, // dmx
|
||||||
|
pub pc: i16, // dy
|
||||||
|
pub pd: i16, // dmy
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Gpu {
|
pub struct Gpu {
|
||||||
// registers
|
// registers
|
||||||
pub dispcnt: DisplayControl,
|
pub dispcnt: DisplayControl,
|
||||||
pub dispstat: DisplayStatus,
|
pub dispstat: DisplayStatus,
|
||||||
pub bgcnt: [BgControl; 4],
|
|
||||||
pub bgvofs: [u16; 4],
|
pub bg: [Bg; 4],
|
||||||
pub bghofs: [u16; 4],
|
pub bg_aff: [BgAffine; 2],
|
||||||
|
|
||||||
pub win0h: u16,
|
pub win0h: u16,
|
||||||
pub win1h: u16,
|
pub win1h: u16,
|
||||||
pub win0v: u16,
|
pub win0v: u16,
|
||||||
pub win1v: u16,
|
pub win1v: u16,
|
||||||
pub winin: u16,
|
pub winin: u16,
|
||||||
pub winout: u16,
|
pub winout: u16,
|
||||||
pub mosaic: u16,
|
pub mosaic: RegMosaic,
|
||||||
pub bldcnt: u16,
|
pub bldcnt: BlendControl,
|
||||||
pub bldalpha: u16,
|
pub bldalpha: BlendAlpha,
|
||||||
pub bldy: u16,
|
pub bldy: u16,
|
||||||
|
|
||||||
cycles: usize,
|
cycles: usize,
|
||||||
|
|
||||||
pub pixeldata: FrameBuffer,
|
pub frame_buffer: FrameBuffer,
|
||||||
pub state: GpuState,
|
pub state: GpuState,
|
||||||
pub current_scanline: usize, // VCOUNT
|
pub current_scanline: usize, // VCOUNT
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gpu {
|
impl Gpu {
|
||||||
pub const DISPLAY_WIDTH: usize = 240;
|
|
||||||
pub const DISPLAY_HEIGHT: usize = 160;
|
|
||||||
|
|
||||||
pub const CYCLES_PIXEL: usize = 4;
|
|
||||||
pub const CYCLES_HDRAW: usize = 960;
|
|
||||||
pub const CYCLES_HBLANK: usize = 272;
|
|
||||||
pub const CYCLES_SCANLINE: usize = 1232;
|
|
||||||
pub const CYCLES_VDRAW: usize = 197120;
|
|
||||||
pub const CYCLES_VBLANK: usize = 83776;
|
|
||||||
|
|
||||||
pub const TILE_SIZE: u32 = 0x20;
|
|
||||||
|
|
||||||
pub fn new() -> Gpu {
|
pub fn new() -> Gpu {
|
||||||
Gpu {
|
Gpu {
|
||||||
dispcnt: DisplayControl(0x80),
|
dispcnt: DisplayControl(0x80),
|
||||||
dispstat: DisplayStatus(0),
|
dispstat: DisplayStatus(0),
|
||||||
bgcnt: [BgControl(0), BgControl(0), BgControl(0), BgControl(0)],
|
bg: [Bg::default(); 4],
|
||||||
bgvofs: [0; 4],
|
bg_aff: [BgAffine::default(); 2],
|
||||||
bghofs: [0; 4],
|
|
||||||
win0h: 0,
|
win0h: 0,
|
||||||
win1h: 0,
|
win1h: 0,
|
||||||
win0v: 0,
|
win0v: 0,
|
||||||
win1v: 0,
|
win1v: 0,
|
||||||
winin: 0,
|
winin: 0,
|
||||||
winout: 0,
|
winout: 0,
|
||||||
mosaic: 0,
|
mosaic: RegMosaic(0),
|
||||||
bldcnt: 0,
|
bldcnt: BlendControl(0),
|
||||||
bldalpha: 0,
|
bldalpha: BlendAlpha(0),
|
||||||
bldy: 0,
|
bldy: 0,
|
||||||
|
|
||||||
state: HDraw,
|
state: HDraw,
|
||||||
current_scanline: 0,
|
current_scanline: 0,
|
||||||
cycles: 0,
|
cycles: 0,
|
||||||
pixeldata: FrameBuffer([Rgb15::from(0); 512 * 512]),
|
frame_buffer: FrameBuffer([0; DISPLAY_WIDTH * DISPLAY_HEIGHT]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,30 +235,35 @@ impl Gpu {
|
||||||
let ofs = addr - VRAM_ADDR;
|
let ofs = addr - VRAM_ADDR;
|
||||||
match format {
|
match format {
|
||||||
PixelFormat::BPP4 => {
|
PixelFormat::BPP4 => {
|
||||||
let byte = sb.vram.read_8(ofs + index2d!(x / 2, y, 4));
|
let byte = sb.vram.read_8(ofs + index2d!(Addr, x / 2, y, 4));
|
||||||
if x & 1 != 0 {
|
if x & 1 != 0 {
|
||||||
(byte >> 4) as usize
|
(byte >> 4) as usize
|
||||||
} else {
|
} else {
|
||||||
(byte & 0xf) as usize
|
(byte & 0xf) as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelFormat::BPP8 => sb.vram.read_8(ofs + index2d!(x, y, 8)) as usize,
|
PixelFormat::BPP8 => sb.vram.read_8(ofs + index2d!(Addr, x, y, 8)) as usize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_palette_color(&self, sb: &SysBus, index: u32, palette_index: u32) -> Rgb15 {
|
pub fn get_palette_color(&self, sb: &SysBus, index: u32, palette_index: u32) -> Rgb15 {
|
||||||
sb.palette_ram
|
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
||||||
.read_16(2 * index + 0x20 * palette_index)
|
return Rgb15::TRANSPARENT;
|
||||||
.into()
|
}
|
||||||
|
Rgb15(sb.palette_ram.read_16(2 * index + 0x20 * palette_index))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scanline_mode0(&mut self, bg: usize, sb: &mut SysBus) {
|
fn render_pixel(&mut self, x: i32, y: i32, p: Rgb15) {
|
||||||
let (h_ofs, v_ofs) = (self.bghofs[bg] as u32, self.bgvofs[bg] as u32);
|
self.frame_buffer.0[index2d!(usize, x, y, DISPLAY_WIDTH)] = p.to_rgb24();
|
||||||
let tileset_base = self.bgcnt[bg].char_block() - VRAM_ADDR;
|
}
|
||||||
let tilemap_base = self.bgcnt[bg].screen_block() - VRAM_ADDR;
|
|
||||||
let (tile_size, pixel_format) = self.bgcnt[bg].tile_format();
|
|
||||||
|
|
||||||
let (bg_width, bg_height) = self.bgcnt[bg].size_regular();
|
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.current_scanline as u32;
|
let screen_y = self.current_scanline as u32;
|
||||||
let mut screen_x = 0;
|
let mut screen_x = 0;
|
||||||
|
@ -186,65 +283,70 @@ impl Gpu {
|
||||||
(256, 256) => 0,
|
(256, 256) => 0,
|
||||||
(512, 256) => bg_x / 256,
|
(512, 256) => bg_x / 256,
|
||||||
(256, 512) => bg_y / 256,
|
(256, 512) => bg_y / 256,
|
||||||
(512, 512) => index2d!(bg_x / 256, bg_y / 256, 2),
|
(512, 512) => index2d!(u32, bg_x / 256, bg_y / 256, 2),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
} as u32;
|
} as u32;
|
||||||
|
|
||||||
let se_row = (bg_x / 8) % 32;
|
let mut se_row = (bg_x / 8) % 32;
|
||||||
let se_column = (bg_y / 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
|
// 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 mut start_tile_x = bg_x % 8;
|
||||||
|
let tile_py = (bg_y % 8) as u32;
|
||||||
|
|
||||||
for t in 0..32 {
|
loop {
|
||||||
let map_addr = tilemap_base
|
let mut map_addr = tilemap_base
|
||||||
+ SCREEN_BLOCK_SIZE * screen_block
|
+ SCREEN_BLOCK_SIZE * screen_block
|
||||||
+ 2 * (index2d!((se_row + t) % 32, se_column, 32) as u32);
|
+ 2 * index2d!(u32, se_row, se_column, 32);
|
||||||
let entry = TileMapEntry(sb.vram.read_16(map_addr - VRAM_ADDR));
|
for _ in se_row..32 {
|
||||||
let tile_addr = tileset_base + entry.tile_index() * tile_size;
|
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..=7 {
|
for tile_px in start_tile_x..=7 {
|
||||||
let tile_py = (bg_y % 8) as u32;
|
let index = self.read_pixel_index(
|
||||||
let index = self.read_pixel_index(
|
sb,
|
||||||
sb,
|
tile_addr,
|
||||||
tile_addr,
|
if entry.x_flip() { 7 - tile_px } else { tile_px },
|
||||||
if entry.x_flip() { 7 - tile_px } else { tile_px },
|
if entry.y_flip() { 7 - tile_py } else { tile_py },
|
||||||
if entry.y_flip() { 7 - tile_py } else { tile_py },
|
pixel_format,
|
||||||
pixel_format,
|
);
|
||||||
);
|
let palette_bank = match pixel_format {
|
||||||
let palette_bank = match pixel_format {
|
PixelFormat::BPP4 => entry.palette_bank() as u32,
|
||||||
PixelFormat::BPP4 => entry.palette_bank() as u32,
|
PixelFormat::BPP8 => 0u32,
|
||||||
PixelFormat::BPP8 => 0u32,
|
};
|
||||||
};
|
let color = self.get_palette_color(sb, index as u32, palette_bank);
|
||||||
let color = self.get_palette_color(sb, index as u32, palette_bank);
|
self.bg[bg].line[screen_x as usize] = color;
|
||||||
if color.get_rgb24() != (0, 0, 0) {
|
screen_x += 1;
|
||||||
self.pixeldata[index2d!(screen_x as usize, screen_y as usize, 512)] = color;
|
if (DISPLAY_WIDTH as u32) == screen_x {
|
||||||
}
|
return;
|
||||||
screen_x += 1;
|
}
|
||||||
if (Gpu::DISPLAY_WIDTH as u32) == screen_x {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
start_tile_x = 0;
|
||||||
|
map_addr += 2;
|
||||||
}
|
}
|
||||||
start_tile_x = 0;
|
se_row = 0;
|
||||||
if se_row + t == 31 {
|
if bg_width == 512 {
|
||||||
if bg_width == 512 {
|
screen_block = screen_block ^ 1;
|
||||||
screen_block = screen_block ^ 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scanline_mode3(&mut self, _bg: u32, sb: &mut SysBus) {
|
fn scanline_aff_bg(&mut self, bg: usize, sb: &mut SysBus) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scanline_mode3(&mut self, bg: usize, sb: &mut SysBus) {
|
||||||
let y = self.current_scanline;
|
let y = self.current_scanline;
|
||||||
|
|
||||||
for x in 0..Self::DISPLAY_WIDTH {
|
for x in 0..DISPLAY_WIDTH {
|
||||||
let pixel_index = index2d!(x, y, Self::DISPLAY_WIDTH);
|
let pixel_index = index2d!(u32, x, y, DISPLAY_WIDTH);
|
||||||
let pixel_ofs = 2 * (pixel_index as u32);
|
let pixel_ofs = 2 * pixel_index;
|
||||||
self.pixeldata[index2d!(x, y, 512)] = sb.vram.read_16(pixel_ofs).into();
|
let color = Rgb15(sb.vram.read_16(pixel_ofs));
|
||||||
|
self.bg[bg].line[x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scanline_mode4(&mut self, _bg: u32, sb: &mut SysBus) {
|
fn scanline_mode4(&mut self, bg: usize, sb: &mut SysBus) {
|
||||||
let page_ofs: u32 = match self.dispcnt.display_frame() {
|
let page_ofs: u32 = match self.dispcnt.display_frame() {
|
||||||
0 => 0x0600_0000 - VRAM_ADDR,
|
0 => 0x0600_0000 - VRAM_ADDR,
|
||||||
1 => 0x0600_a000 - VRAM_ADDR,
|
1 => 0x0600_a000 - VRAM_ADDR,
|
||||||
|
@ -253,26 +355,43 @@ impl Gpu {
|
||||||
|
|
||||||
let y = self.current_scanline;
|
let y = self.current_scanline;
|
||||||
|
|
||||||
for x in 0..Self::DISPLAY_WIDTH {
|
for x in 0..DISPLAY_WIDTH {
|
||||||
let bitmap_index = index2d!(x, y, Self::DISPLAY_WIDTH);
|
let bitmap_index = index2d!(x, y, DISPLAY_WIDTH);
|
||||||
let bitmap_ofs = page_ofs + (bitmap_index as u32);
|
let bitmap_ofs = page_ofs + (bitmap_index as u32);
|
||||||
let index = sb.vram.read_8(bitmap_ofs as Addr) as u32;
|
let index = sb.vram.read_8(bitmap_ofs as Addr) as u32;
|
||||||
self.pixeldata[index2d!(x, y, 512)] = self.get_palette_color(sb, index, 0);
|
let color = self.get_palette_color(sb, index, 0);
|
||||||
|
self.bg[bg].line[x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scanline(&mut self, sb: &mut SysBus) {
|
pub fn render_scanline(&mut self, sb: &mut SysBus) {
|
||||||
|
// TODO - also render objs
|
||||||
match self.dispcnt.mode() {
|
match self.dispcnt.mode() {
|
||||||
BGMode::BGMode0 => {
|
BGMode::BGMode0 => {
|
||||||
for bg in (0..3).rev() {
|
for bg in 0..3 {
|
||||||
if self.dispcnt.disp_bg(bg) {
|
if self.dispcnt.disp_bg(bg) {
|
||||||
self.scanline_mode0(bg, sb);
|
self.scanline_reg_bg(bg, sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BGMode::BGMode1 => {
|
||||||
|
if self.dispcnt.disp_bg(2) {
|
||||||
|
self.scanline_aff_bg(2, sb);
|
||||||
|
}
|
||||||
|
if self.dispcnt.disp_bg(1) {
|
||||||
|
self.scanline_reg_bg(1, sb);
|
||||||
|
}
|
||||||
|
if self.dispcnt.disp_bg(0) {
|
||||||
|
self.scanline_reg_bg(0, sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
BGMode::BGMode2 => {
|
BGMode::BGMode2 => {
|
||||||
self.scanline_mode0(3, sb);
|
if self.dispcnt.disp_bg(3) {
|
||||||
self.scanline_mode0(2, sb);
|
self.scanline_aff_bg(3, sb);
|
||||||
|
}
|
||||||
|
if self.dispcnt.disp_bg(2) {
|
||||||
|
self.scanline_aff_bg(2, sb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BGMode::BGMode3 => {
|
BGMode::BGMode3 => {
|
||||||
self.scanline_mode3(2, sb);
|
self.scanline_mode3(2, sb);
|
||||||
|
@ -282,18 +401,16 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
||||||
}
|
}
|
||||||
|
self.mosaic_sfx();
|
||||||
|
let post_blend_line = self.blend_line(sb);
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
self.frame_buffer.0[x + self.current_scanline * DISPLAY_WIDTH] =
|
||||||
|
post_blend_line[x].to_rgb24();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self) -> Vec<u32> {
|
pub fn get_framebuffer(&self) -> &[u32] {
|
||||||
let mut buffer = vec![0u32; Gpu::DISPLAY_WIDTH * Gpu::DISPLAY_WIDTH];
|
&self.frame_buffer.0
|
||||||
for y in 0..Gpu::DISPLAY_HEIGHT {
|
|
||||||
for x in 0..Gpu::DISPLAY_WIDTH {
|
|
||||||
let (r, g, b) = self.pixeldata[index2d!(x as usize, y as usize, 512)].get_rgb24();
|
|
||||||
buffer[index2d!(x, y, Gpu::DISPLAY_WIDTH)] =
|
|
||||||
((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,12 +428,12 @@ impl SyncedIoDevice for Gpu {
|
||||||
|
|
||||||
match self.state {
|
match self.state {
|
||||||
HDraw => {
|
HDraw => {
|
||||||
if self.cycles > Gpu::CYCLES_HDRAW {
|
if self.cycles > CYCLES_HDRAW {
|
||||||
self.current_scanline += 1;
|
self.current_scanline += 1;
|
||||||
self.cycles -= Gpu::CYCLES_HDRAW;
|
self.cycles -= CYCLES_HDRAW;
|
||||||
|
|
||||||
if self.current_scanline < Gpu::DISPLAY_HEIGHT {
|
if self.current_scanline < DISPLAY_HEIGHT {
|
||||||
self.scanline(sb);
|
self.render_scanline(sb);
|
||||||
// HBlank
|
// HBlank
|
||||||
self.dispstat.set_hblank(true);
|
self.dispstat.set_hblank(true);
|
||||||
if self.dispstat.hblank_irq_enable() {
|
if self.dispstat.hblank_irq_enable() {
|
||||||
|
@ -324,7 +441,6 @@ impl SyncedIoDevice for Gpu {
|
||||||
};
|
};
|
||||||
self.state = HBlank;
|
self.state = HBlank;
|
||||||
} else {
|
} else {
|
||||||
self.scanline(sb);
|
|
||||||
self.dispstat.set_vblank(true);
|
self.dispstat.set_vblank(true);
|
||||||
if self.dispstat.vblank_irq_enable() {
|
if self.dispstat.vblank_irq_enable() {
|
||||||
irqs.set_LCD_VBlank(true);
|
irqs.set_LCD_VBlank(true);
|
||||||
|
@ -334,21 +450,21 @@ impl SyncedIoDevice for Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HBlank => {
|
HBlank => {
|
||||||
if self.cycles > Gpu::CYCLES_HBLANK {
|
if self.cycles > CYCLES_HBLANK {
|
||||||
self.cycles -= Gpu::CYCLES_HBLANK;
|
self.cycles -= CYCLES_HBLANK;
|
||||||
self.state = HDraw;
|
self.state = HDraw;
|
||||||
self.dispstat.set_hblank(false);
|
self.dispstat.set_hblank(false);
|
||||||
self.dispstat.set_vblank(false);
|
self.dispstat.set_vblank(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VBlank => {
|
VBlank => {
|
||||||
if self.cycles > Gpu::CYCLES_VBLANK {
|
if self.cycles > CYCLES_VBLANK {
|
||||||
self.cycles -= Gpu::CYCLES_VBLANK;
|
self.cycles -= CYCLES_VBLANK;
|
||||||
self.state = HDraw;
|
self.state = HDraw;
|
||||||
self.dispstat.set_hblank(false);
|
self.dispstat.set_hblank(false);
|
||||||
self.dispstat.set_vblank(false);
|
self.dispstat.set_vblank(false);
|
||||||
self.current_scanline = 0;
|
self.current_scanline = 0;
|
||||||
self.scanline(sb);
|
self.render_scanline(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
src/core/gpu/mosaic.rs
Normal file
35
src/core/gpu/mosaic.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use super::*;
|
||||||
|
use regs::RegMosaic;
|
||||||
|
|
||||||
|
impl RegMosaic {
|
||||||
|
fn is_enabled_for_bg(&self) -> bool {
|
||||||
|
(self.bg_hsize() != 0) || (self.bg_vsize() != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
fn mosaic_bg(&mut self) {
|
||||||
|
let hsize = (self.mosaic.bg_hsize() + 1) as usize;
|
||||||
|
let vsize = (self.mosaic.bg_vsize() + 1) as usize;
|
||||||
|
|
||||||
|
for bg in 0..4 {
|
||||||
|
if self.dispcnt.disp_bg(bg) && self.bg[bg].bgcnt.mosaic() {
|
||||||
|
let y = self.current_scanline as usize;
|
||||||
|
if y % vsize == 0 {
|
||||||
|
self.bg[bg].mosaic_first_row = self.bg[bg].line.clone();
|
||||||
|
}
|
||||||
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
let color = self.bg[bg].mosaic_first_row[(x / hsize) * hsize];
|
||||||
|
self.bg[bg].line[x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mosaic_sfx(&mut self) {
|
||||||
|
if self.mosaic.is_enabled_for_bg() {
|
||||||
|
self.mosaic_bg();
|
||||||
|
}
|
||||||
|
// TODO obj mosaic
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,7 @@
|
||||||
|
use super::blend::BldMode;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
bitfield! {
|
pub const SCREEN_BLOCK_SIZE: u32 = 0x800;
|
||||||
pub struct DisplayControl(u16);
|
|
||||||
impl Debug;
|
|
||||||
u16;
|
|
||||||
pub into BGMode, mode, set_mode: 2, 0;
|
|
||||||
pub display_frame, set_display_frame: 4, 4;
|
|
||||||
pub hblank_interval_free, _: 5;
|
|
||||||
pub obj_character_vram_mapping, _: 6;
|
|
||||||
pub forst_vblank, _: 7;
|
|
||||||
pub disp_bg0, _ : 8;
|
|
||||||
pub disp_bg1, _ : 9;
|
|
||||||
pub disp_bg2, _ : 10;
|
|
||||||
pub disp_bg3, _ : 11;
|
|
||||||
pub disp_obj, _ : 12;
|
|
||||||
pub disp_window0, _ : 13;
|
|
||||||
pub disp_window1, _ : 14;
|
|
||||||
pub disp_obj_window, _ : 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisplayControl {
|
impl DisplayControl {
|
||||||
pub fn disp_bg(&self, bg: usize) -> bool {
|
pub fn disp_bg(&self, bg: usize) -> bool {
|
||||||
|
@ -25,35 +9,6 @@ impl DisplayControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
pub struct DisplayStatus(u16);
|
|
||||||
impl Debug;
|
|
||||||
u16;
|
|
||||||
pub get_vblank, set_vblank: 0;
|
|
||||||
pub get_hblank, set_hblank: 1;
|
|
||||||
pub get_vcount, set_vcount: 2;
|
|
||||||
pub vblank_irq_enable, _ : 3;
|
|
||||||
pub hblank_irq_enable, _ : 4;
|
|
||||||
pub vcount_irq_enable, _ : 5;
|
|
||||||
pub vcount_setting, _ : 15, 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct BgControl(u16);
|
|
||||||
impl Debug;
|
|
||||||
u16;
|
|
||||||
pub bg_priority, _: 1, 0;
|
|
||||||
pub character_base_block, _: 3, 2;
|
|
||||||
pub moasic, _ : 6;
|
|
||||||
pub palette256, _ : 7;
|
|
||||||
pub screen_base_block, _: 12, 8;
|
|
||||||
pub affine_wraparound, _: 13;
|
|
||||||
pub bg_size, _ : 15, 14;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SCREEN_BLOCK_SIZE: u32 = 0x800;
|
|
||||||
|
|
||||||
impl BgControl {
|
impl BgControl {
|
||||||
pub fn char_block(&self) -> u32 {
|
pub fn char_block(&self) -> u32 {
|
||||||
VRAM_ADDR + (self.character_base_block() as u32) * 0x4000
|
VRAM_ADDR + (self.character_base_block() as u32) * 0x4000
|
||||||
|
@ -73,11 +28,121 @@ 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 tile_format(&self) -> (u32, PixelFormat) {
|
pub fn tile_format(&self) -> (u32, PixelFormat) {
|
||||||
if self.palette256() {
|
if self.palette256() {
|
||||||
(2 * Gpu::TILE_SIZE, PixelFormat::BPP8)
|
(2 * TILE_SIZE, PixelFormat::BPP8)
|
||||||
} else {
|
} else {
|
||||||
(Gpu::TILE_SIZE, PixelFormat::BPP4)
|
(TILE_SIZE, PixelFormat::BPP4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// struct definitions below because the bitfield! macro messes up syntax highlighting in vscode.
|
||||||
|
bitfield! {
|
||||||
|
pub struct DisplayControl(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
pub into BGMode, mode, set_mode: 2, 0;
|
||||||
|
pub display_frame, set_display_frame: 4, 4;
|
||||||
|
pub hblank_interval_free, _: 5;
|
||||||
|
pub obj_character_vram_mapping, _: 6;
|
||||||
|
pub forst_vblank, _: 7;
|
||||||
|
pub disp_bg0, _ : 8;
|
||||||
|
pub disp_bg1, _ : 9;
|
||||||
|
pub disp_bg2, _ : 10;
|
||||||
|
pub disp_bg3, _ : 11;
|
||||||
|
pub disp_obj, _ : 12;
|
||||||
|
pub disp_window0, _ : 13;
|
||||||
|
pub disp_window1, _ : 14;
|
||||||
|
pub disp_obj_window, _ : 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct DisplayStatus(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
pub get_vblank, set_vblank: 0;
|
||||||
|
pub get_hblank, set_hblank: 1;
|
||||||
|
pub get_vcount, set_vcount: 2;
|
||||||
|
pub vblank_irq_enable, _ : 3;
|
||||||
|
pub hblank_irq_enable, _ : 4;
|
||||||
|
pub vcount_irq_enable, _ : 5;
|
||||||
|
pub vcount_setting, _ : 15, 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct BgControl(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
pub priority, _: 1, 0;
|
||||||
|
pub character_base_block, _: 3, 2;
|
||||||
|
pub mosaic, _ : 6;
|
||||||
|
pub palette256, _ : 7;
|
||||||
|
pub screen_base_block, _: 12, 8;
|
||||||
|
pub affine_wraparound, _: 13;
|
||||||
|
pub bg_size, _ : 15, 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct RegMosaic(u16);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
pub bg_hsize, _: 3, 0;
|
||||||
|
pub bg_vsize, _: 7, 4;
|
||||||
|
pub obj_hsize, _ : 11, 8;
|
||||||
|
pub obj_vsize, _ : 15, 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct BlendFlags: u32 {
|
||||||
|
const BG0 = 0b00000001;
|
||||||
|
const BG1 = 0b00000010;
|
||||||
|
const BG2 = 0b00000100;
|
||||||
|
const BG3 = 0b00001000;
|
||||||
|
const OBJ = 0b00010000;
|
||||||
|
const BACKDROP = 0b00100000; // BACKDROP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for BlendFlags {
|
||||||
|
fn from(v: u16) -> BlendFlags {
|
||||||
|
BlendFlags::from_bits(v as u32).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const BG_LAYER_FLAG: [BlendFlags; 4] = [
|
||||||
|
BlendFlags::BG0,
|
||||||
|
BlendFlags::BG1,
|
||||||
|
BlendFlags::BG2,
|
||||||
|
BlendFlags::BG3,
|
||||||
|
];
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct BlendControl(u16);
|
||||||
|
impl Debug;
|
||||||
|
pub into BlendFlags, top, _: 5, 0;
|
||||||
|
pub into BldMode, mode, set_mode: 7, 6;
|
||||||
|
pub into BlendFlags, bottom, _: 13, 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct BlendAlpha(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
pub eva, _: 5, 0;
|
||||||
|
pub evb, _: 12, 8;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use super::arm7tdmi::Core;
|
|
||||||
|
|
||||||
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum Interrupt {
|
pub enum Interrupt {
|
||||||
|
@ -54,22 +52,35 @@ impl IrqBitmask {
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
#[derive(Default, Copy, Clone, PartialEq)]
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct IrqBitmask(u16);
|
pub struct IrqBitmask(u16);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
u16;
|
u16;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub LCD_VBlank, set_LCD_VBlank: 0;
|
pub LCD_VBlank, set_LCD_VBlank: 0;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub LCD_HBlank, set_LCD_HBlank: 1;
|
pub LCD_HBlank, set_LCD_HBlank: 1;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub LCD_VCounterMatch, set_LCD_VCounterMatch: 2;
|
pub LCD_VCounterMatch, set_LCD_VCounterMatch: 2;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub Timer0_Overflow, set_Timer0_Overflow: 3;
|
pub Timer0_Overflow, set_Timer0_Overflow: 3;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub Timer1_Overflow, set_Timer1_Overflow: 4;
|
pub Timer1_Overflow, set_Timer1_Overflow: 4;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub Timer2_Overflow, set_Timer2_Overflow: 5;
|
pub Timer2_Overflow, set_Timer2_Overflow: 5;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub Timer3_Overflow, set_Timer3_Overflow: 6;
|
pub Timer3_Overflow, set_Timer3_Overflow: 6;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub SerialCommunication, set_SerialCommunication: 7;
|
pub SerialCommunication, set_SerialCommunication: 7;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub DMA0, set_DMA0: 8;
|
pub DMA0, set_DMA0: 8;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub DMA1, set_DMA1: 9;
|
pub DMA1, set_DMA1: 9;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub DMA2, set_DMA2: 10;
|
pub DMA2, set_DMA2: 10;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub DMA3, set_DMA3: 11;
|
pub DMA3, set_DMA3: 11;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub Keypad, set_Keypad: 12;
|
pub Keypad, set_Keypad: 12;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub GamePak, set_GamePak: 13;
|
pub GamePak, set_GamePak: 13;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,18 @@ pub mod consts {
|
||||||
pub const REG_BG2PB: Addr = IO_BASE + 0x_0022; // 2 W BG2 Rotation/Scaling Parameter B (dmx)
|
pub const REG_BG2PB: Addr = IO_BASE + 0x_0022; // 2 W BG2 Rotation/Scaling Parameter B (dmx)
|
||||||
pub const REG_BG2PC: Addr = IO_BASE + 0x_0024; // 2 W BG2 Rotation/Scaling Parameter C (dy)
|
pub const REG_BG2PC: Addr = IO_BASE + 0x_0024; // 2 W BG2 Rotation/Scaling Parameter C (dy)
|
||||||
pub const REG_BG2PD: Addr = IO_BASE + 0x_0026; // 2 W BG2 Rotation/Scaling Parameter D (dmy)
|
pub const REG_BG2PD: Addr = IO_BASE + 0x_0026; // 2 W BG2 Rotation/Scaling Parameter D (dmy)
|
||||||
pub const REG_BG2X: Addr = IO_BASE + 0x_0028; // 4 W BG2 Reference Point X-Coordinate
|
pub const REG_BG2X_L: Addr = IO_BASE + 0x_0028; // 4 W BG2 Reference Point X-Coordinate, lower 16 bit
|
||||||
pub const REG_BG2Y: Addr = IO_BASE + 0x_002C; // 4 W BG2 Reference Point Y-Coordinate
|
pub const REG_BG2X_H: Addr = IO_BASE + 0x_002A; // 4 W BG2 Reference Point X-Coordinate, upper 16 bit
|
||||||
|
pub const REG_BG2Y_L: Addr = IO_BASE + 0x_002C; // 4 W BG2 Reference Point Y-Coordinate, lower 16 bit
|
||||||
|
pub const REG_BG2Y_H: Addr = IO_BASE + 0x_002E; // 4 W BG2 Reference Point Y-Coordinate, upper 16 bit
|
||||||
pub const REG_BG3PA: Addr = IO_BASE + 0x_0030; // 2 W BG3 Rotation/Scaling Parameter A (dx)
|
pub const REG_BG3PA: Addr = IO_BASE + 0x_0030; // 2 W BG3 Rotation/Scaling Parameter A (dx)
|
||||||
pub const REG_BG3PB: Addr = IO_BASE + 0x_0032; // 2 W BG3 Rotation/Scaling Parameter B (dmx)
|
pub const REG_BG3PB: Addr = IO_BASE + 0x_0032; // 2 W BG3 Rotation/Scaling Parameter B (dmx)
|
||||||
pub const REG_BG3PC: Addr = IO_BASE + 0x_0034; // 2 W BG3 Rotation/Scaling Parameter C (dy)
|
pub const REG_BG3PC: Addr = IO_BASE + 0x_0034; // 2 W BG3 Rotation/Scaling Parameter C (dy)
|
||||||
pub const REG_BG3PD: Addr = IO_BASE + 0x_0036; // 2 W BG3 Rotation/Scaling Parameter D (dmy)
|
pub const REG_BG3PD: Addr = IO_BASE + 0x_0036; // 2 W BG3 Rotation/Scaling Parameter D (dmy)
|
||||||
pub const REG_BG3X: Addr = IO_BASE + 0x_0038; // 4 W BG3 Reference Point X-Coordinate
|
pub const REG_BG3X_L: Addr = IO_BASE + 0x_0038; // 4 W BG3 Reference Point X-Coordinate, lower 16 bit
|
||||||
pub const REG_BG3Y: Addr = IO_BASE + 0x_003C; // 4 W BG3 Reference Point Y-Coordinate
|
pub const REG_BG3X_H: Addr = IO_BASE + 0x_003A; // 4 W BG3 Reference Point X-Coordinate, upper 16 bit
|
||||||
|
pub const REG_BG3Y_L: Addr = IO_BASE + 0x_003C; // 4 W BG3 Reference Point Y-Coordinate, lower 16 bit
|
||||||
|
pub const REG_BG3Y_H: Addr = IO_BASE + 0x_003E; // 4 W BG3 Reference Point Y-Coordinate, upper 16 bit
|
||||||
pub const REG_WIN0H: Addr = IO_BASE + 0x_0040; // 2 W Window 0 Horizontal Dimensions
|
pub const REG_WIN0H: Addr = IO_BASE + 0x_0040; // 2 W Window 0 Horizontal Dimensions
|
||||||
pub const REG_WIN1H: Addr = IO_BASE + 0x_0042; // 2 W Window 1 Horizontal Dimensions
|
pub const REG_WIN1H: Addr = IO_BASE + 0x_0042; // 2 W Window 1 Horizontal Dimensions
|
||||||
pub const REG_WIN0V: Addr = IO_BASE + 0x_0044; // 2 W Window 0 Vertical Dimensions
|
pub const REG_WIN0V: Addr = IO_BASE + 0x_0044; // 2 W Window 0 Vertical Dimensions
|
||||||
|
@ -155,20 +159,18 @@ impl Bus for IoRegs {
|
||||||
REG_DISPCNT => io.gpu.dispcnt.0,
|
REG_DISPCNT => io.gpu.dispcnt.0,
|
||||||
REG_DISPSTAT => io.gpu.dispstat.0,
|
REG_DISPSTAT => io.gpu.dispstat.0,
|
||||||
REG_VCOUNT => io.gpu.current_scanline as u16,
|
REG_VCOUNT => io.gpu.current_scanline as u16,
|
||||||
REG_BG0CNT => io.gpu.bgcnt[0].0,
|
REG_BG0CNT => io.gpu.bg[0].bgcnt.0,
|
||||||
REG_BG1CNT => io.gpu.bgcnt[1].0,
|
REG_BG1CNT => io.gpu.bg[1].bgcnt.0,
|
||||||
REG_BG2CNT => io.gpu.bgcnt[2].0,
|
REG_BG2CNT => io.gpu.bg[2].bgcnt.0,
|
||||||
REG_BG3CNT => io.gpu.bgcnt[3].0,
|
REG_BG3CNT => io.gpu.bg[3].bgcnt.0,
|
||||||
REG_WIN0H => io.gpu.win0h,
|
REG_WIN0H => io.gpu.win0h,
|
||||||
REG_WIN1H => io.gpu.win1h,
|
REG_WIN1H => io.gpu.win1h,
|
||||||
REG_WIN0V => io.gpu.win0v,
|
REG_WIN0V => io.gpu.win0v,
|
||||||
REG_WIN1V => io.gpu.win1v,
|
REG_WIN1V => io.gpu.win1v,
|
||||||
REG_WININ => io.gpu.winin,
|
REG_WININ => io.gpu.winin,
|
||||||
REG_WINOUT => io.gpu.winout,
|
REG_WINOUT => io.gpu.winout,
|
||||||
REG_MOSAIC => io.gpu.mosaic,
|
REG_BLDCNT => io.gpu.bldcnt.0,
|
||||||
REG_BLDCNT => io.gpu.bldcnt,
|
REG_BLDALPHA => io.gpu.bldalpha.0,
|
||||||
REG_BLDALPHA => io.gpu.bldalpha,
|
|
||||||
REG_BLDY => io.gpu.bldy,
|
|
||||||
|
|
||||||
REG_IME => io.intc.interrupt_master_enable as u16,
|
REG_IME => io.intc.interrupt_master_enable as u16,
|
||||||
REG_IE => io.intc.interrupt_enable.0 as u16,
|
REG_IE => io.intc.interrupt_enable.0 as u16,
|
||||||
|
@ -206,28 +208,44 @@ impl Bus for IoRegs {
|
||||||
match addr + IO_BASE {
|
match addr + IO_BASE {
|
||||||
REG_DISPCNT => io.gpu.dispcnt.0 |= value,
|
REG_DISPCNT => io.gpu.dispcnt.0 |= value,
|
||||||
REG_DISPSTAT => io.gpu.dispstat.0 |= value,
|
REG_DISPSTAT => io.gpu.dispstat.0 |= value,
|
||||||
REG_BG0CNT => io.gpu.bgcnt[0].0 |= value,
|
REG_BG0CNT => io.gpu.bg[0].bgcnt.0 |= value,
|
||||||
REG_BG1CNT => io.gpu.bgcnt[1].0 |= value,
|
REG_BG1CNT => io.gpu.bg[1].bgcnt.0 |= value,
|
||||||
REG_BG2CNT => io.gpu.bgcnt[2].0 |= value,
|
REG_BG2CNT => io.gpu.bg[2].bgcnt.0 |= value,
|
||||||
REG_BG3CNT => io.gpu.bgcnt[3].0 |= value,
|
REG_BG3CNT => io.gpu.bg[3].bgcnt.0 |= value,
|
||||||
REG_BG0HOFS => io.gpu.bghofs[0] = value,
|
REG_BG0HOFS => io.gpu.bg[0].bghofs = value & 0x1ff,
|
||||||
REG_BG0VOFS => io.gpu.bgvofs[0] = value,
|
REG_BG0VOFS => io.gpu.bg[0].bgvofs = value & 0x1ff,
|
||||||
REG_BG1HOFS => io.gpu.bghofs[1] = value,
|
REG_BG1HOFS => io.gpu.bg[1].bghofs = value & 0x1ff,
|
||||||
REG_BG1VOFS => io.gpu.bgvofs[1] = value,
|
REG_BG1VOFS => io.gpu.bg[1].bgvofs = value & 0x1ff,
|
||||||
REG_BG2HOFS => io.gpu.bghofs[2] = value,
|
REG_BG2HOFS => io.gpu.bg[2].bghofs = value & 0x1ff,
|
||||||
REG_BG2VOFS => io.gpu.bgvofs[2] = value,
|
REG_BG2VOFS => io.gpu.bg[2].bgvofs = value & 0x1ff,
|
||||||
REG_BG3HOFS => io.gpu.bghofs[3] = value,
|
REG_BG3HOFS => io.gpu.bg[3].bghofs = value & 0x1ff,
|
||||||
REG_BG3VOFS => io.gpu.bgvofs[3] = value,
|
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_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,
|
||||||
|
REG_BG2PD => io.gpu.bg_aff[0].pd = value as i16,
|
||||||
|
REG_BG3PA => io.gpu.bg_aff[1].pa = value as i16,
|
||||||
|
REG_BG3PB => io.gpu.bg_aff[1].pb = value as i16,
|
||||||
|
REG_BG3PC => io.gpu.bg_aff[1].pc = value as i16,
|
||||||
|
REG_BG3PD => io.gpu.bg_aff[1].pd = value as i16,
|
||||||
REG_WIN0H => io.gpu.win0h = value,
|
REG_WIN0H => io.gpu.win0h = value,
|
||||||
REG_WIN1H => io.gpu.win1h = value,
|
REG_WIN1H => io.gpu.win1h = value,
|
||||||
REG_WIN0V => io.gpu.win0v = value,
|
REG_WIN0V => io.gpu.win0v = value,
|
||||||
REG_WIN1V => io.gpu.win1v = value,
|
REG_WIN1V => io.gpu.win1v = value,
|
||||||
REG_WININ => io.gpu.winin = value,
|
REG_WININ => io.gpu.winin = value,
|
||||||
REG_WINOUT => io.gpu.winout = value,
|
REG_WINOUT => io.gpu.winout = value,
|
||||||
REG_MOSAIC => io.gpu.mosaic = value,
|
REG_MOSAIC => io.gpu.mosaic.0 = value,
|
||||||
REG_BLDCNT => io.gpu.bldcnt = value,
|
REG_BLDCNT => io.gpu.bldcnt.0 = value,
|
||||||
REG_BLDALPHA => io.gpu.bldalpha = value,
|
REG_BLDALPHA => io.gpu.bldalpha.0 = value,
|
||||||
REG_BLDY => io.gpu.bldy = value,
|
REG_BLDY => io.gpu.bldy = value & 0b11111,
|
||||||
|
|
||||||
REG_IME => io.intc.interrupt_master_enable = value != 0,
|
REG_IME => io.intc.interrupt_master_enable = value != 0,
|
||||||
REG_IE => io.intc.interrupt_enable.0 = value,
|
REG_IE => io.intc.interrupt_enable.0 = value,
|
||||||
|
|
|
@ -114,7 +114,6 @@ impl SyncedIoDevice for Timers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TimerAction::Increment => {}
|
TimerAction::Increment => {}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,7 @@ use sdl2::rect::{Point, Rect};
|
||||||
use sdl2::render::Canvas;
|
use sdl2::render::Canvas;
|
||||||
|
|
||||||
use crate::core::gba::GameBoyAdvance;
|
use crate::core::gba::GameBoyAdvance;
|
||||||
use crate::core::palette::*;
|
use crate::core::gpu::PixelFormat;
|
||||||
|
|
||||||
impl Into<Color> for Rgb15 {
|
|
||||||
fn into(self) -> Color {
|
|
||||||
let (r, g, b) = self.get_rgb24();
|
|
||||||
Color::RGB(r, g, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_tile(
|
fn draw_tile(
|
||||||
gba: &GameBoyAdvance,
|
gba: &GameBoyAdvance,
|
||||||
|
@ -29,9 +22,11 @@ fn draw_tile(
|
||||||
.gpu
|
.gpu
|
||||||
.read_pixel_index(&gba.sysbus, tile_addr, x, y, pixel_format);
|
.read_pixel_index(&gba.sysbus, tile_addr, x, y, pixel_format);
|
||||||
let color = io.gpu.get_palette_color(&gba.sysbus, index as u32, 0);
|
let color = io.gpu.get_palette_color(&gba.sysbus, index as u32, 0);
|
||||||
let (r, g, b) = color.get_rgb24();
|
canvas.set_draw_color(Color::RGB(
|
||||||
|
(color.r() as u8) << 3,
|
||||||
canvas.set_draw_color(Color::RGB(r, g, b));
|
(color.g() as u8) << 3,
|
||||||
|
(color.b() as u8) << 3,
|
||||||
|
));
|
||||||
canvas.draw_point(p.offset(x as i32, y as i32)).unwrap();
|
canvas.draw_point(p.offset(x as i32, y as i32)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +47,7 @@ pub fn create_tile_view(bg: u32, gba: &GameBoyAdvance) {
|
||||||
|
|
||||||
let mut canvas = window.into_canvas().build().unwrap();
|
let mut canvas = window.into_canvas().build().unwrap();
|
||||||
|
|
||||||
let bgcnt = gba.io.borrow().gpu.bgcnt[bg as usize].clone();
|
let bgcnt = gba.io.borrow().gpu.bg[bg as usize].bgcnt.clone();
|
||||||
|
|
||||||
let (tile_size, pixel_format) = bgcnt.tile_format();
|
let (tile_size, pixel_format) = bgcnt.tile_format();
|
||||||
let tileset_addr = bgcnt.char_block();
|
let tileset_addr = bgcnt.char_block();
|
||||||
|
|
|
@ -6,6 +6,8 @@ extern crate num_traits;
|
||||||
extern crate bit;
|
extern crate bit;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitfield;
|
extern crate bitfield;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate bitflags;
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
||||||
|
|
|
@ -14,4 +14,7 @@ macro_rules! index2d {
|
||||||
($x:expr, $y:expr, $w:expr) => {
|
($x:expr, $y:expr, $w:expr) => {
|
||||||
$w * $y + $x
|
$w * $y + $x
|
||||||
};
|
};
|
||||||
|
($t:ty, $x:expr, $y:expr, $w:expr) => {
|
||||||
|
(($w as $t) * ($y as $t) + ($x as $t)) as $t
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue