[perf] core: gpu: Refactor to use unpacked mmio configuration during rendering
* Convert gpu bitfield!() registers to unpacked form, and defer pack/unpack to bus read/write operations Former-commit-id: 26e7d7d62d6418ce7bcdb8e414cabe5ddb56333d Former-commit-id: 716ddd9fe2b7b95b7613fc549a7bee406272478b
This commit is contained in:
parent
a413ebe891
commit
7e2c9d040a
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -1231,6 +1231,7 @@ dependencies = [
|
|||
"rustyline 6.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smart-default 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zip 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1481,6 +1482,16 @@ dependencies = [
|
|||
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smart-default"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin_sleep"
|
||||
version = "0.3.7"
|
||||
|
@ -2134,6 +2145,7 @@ dependencies = [
|
|||
"checksum serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
||||
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||
"checksum sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
"checksum smart-default 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
|
||||
"checksum spin_sleep 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "891836ef5f8a5b9678938d34d75391a3794267806482105ffcd363271980c10c"
|
||||
"checksum standback 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "33a71ea1ea5f8747d1af1979bfb7e65c3a025a70609f04ceb78425bc5adad8e6"
|
||||
"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
|
|
@ -41,6 +41,7 @@ fuzzy-matcher = { version = "0.3.4", optional = true }
|
|||
bit_reverse = "0.1.8"
|
||||
yaml-rust = "0.4"
|
||||
lazy_static = "1.4.0"
|
||||
smart-default = "0.6.0"
|
||||
|
||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||
instant = { version = "0.1.2", features = ["wasm-bindgen"] }
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#[cfg(not(feature = "no_video_interface"))]
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -14,7 +13,6 @@ pub use super::sysbus::consts::*;
|
|||
#[cfg(not(feature = "no_video_interface"))]
|
||||
use super::VideoInterface;
|
||||
|
||||
use crate::bitfield::Bit;
|
||||
use crate::num::FromPrimitive;
|
||||
|
||||
mod render;
|
||||
|
@ -66,88 +64,6 @@ pub enum PixelFormat {
|
|||
BPP8 = 1,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum GpuState {
|
||||
HDraw = 0,
|
||||
HBlank,
|
||||
VBlankHDraw,
|
||||
VBlankHBlank,
|
||||
}
|
||||
|
||||
impl Default for GpuState {
|
||||
fn default() -> GpuState {
|
||||
GpuState::HDraw
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuState {
|
||||
pub fn is_vblank(&self) -> bool {
|
||||
match self {
|
||||
VBlankHBlank | VBlankHDraw => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use GpuState::*;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Scanline {
|
||||
inner: Vec<Rgb15>,
|
||||
}
|
||||
|
||||
impl Default for Scanline {
|
||||
fn default() -> Scanline {
|
||||
Scanline {
|
||||
inner: vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Scanline {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "...")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<usize> for Scanline {
|
||||
type Output = Rgb15;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.inner[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::IndexMut<usize> for Scanline {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.inner[index]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||
pub struct Background {
|
||||
pub bgcnt: BgControl,
|
||||
pub bgvofs: u16,
|
||||
pub bghofs: u16,
|
||||
|
||||
line: Scanline,
|
||||
|
||||
// for mosaic
|
||||
mosaic_first_row: Scanline,
|
||||
}
|
||||
|
||||
impl Background {
|
||||
#[inline]
|
||||
pub fn get_priority(&self) -> u16 {
|
||||
self.bgcnt.priority()
|
||||
}
|
||||
#[inline]
|
||||
pub fn pixel_at(&self, x: usize) -> Rgb15 {
|
||||
self.line[x]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct AffineMatrix {
|
||||
pub pa: i32,
|
||||
|
@ -192,7 +108,6 @@ type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
|
|||
|
||||
#[derive(Serialize, Deserialize, Clone, DebugStub)]
|
||||
pub struct Gpu {
|
||||
pub state: GpuState,
|
||||
interrupt_flags: SharedInterruptFlags,
|
||||
|
||||
/// When deserializing this struct using serde, make sure to call connect_scheduler
|
||||
|
@ -208,30 +123,25 @@ pub struct Gpu {
|
|||
pub dispcnt: DisplayControl,
|
||||
pub dispstat: DisplayStatus,
|
||||
|
||||
pub backgrounds: [Background; 4],
|
||||
pub bgcnt: [BgControl; 4],
|
||||
pub bg_vofs: [u16; 4],
|
||||
pub bg_hofs: [u16; 4],
|
||||
pub bg_aff: [BgAffine; 2],
|
||||
|
||||
pub win0: Window,
|
||||
pub win1: Window,
|
||||
pub winout_flags: WindowFlags,
|
||||
pub winobj_flags: WindowFlags,
|
||||
|
||||
pub mosaic: RegMosaic,
|
||||
pub bldcnt: BlendControl,
|
||||
pub bldalpha: BlendAlpha,
|
||||
pub bldy: u16,
|
||||
|
||||
pub palette_ram: Box<[u8]>,
|
||||
pub vram: Box<[u8]>,
|
||||
pub oam: Box<[u8]>,
|
||||
|
||||
pub(super) vram_obj_tiles_start: u32,
|
||||
|
||||
#[debug_stub = "Sprite Buffer"]
|
||||
pub obj_buffer: Vec<ObjBufferEntry>,
|
||||
|
||||
#[debug_stub = "Frame Buffer"]
|
||||
pub(super) frame_buffer: Vec<u32>,
|
||||
pub(super) obj_buffer: Box<[ObjBufferEntry]>,
|
||||
pub(super) frame_buffer: Box<[u32]>,
|
||||
pub(super) bg_line: [Box<[Rgb15]>; 4],
|
||||
}
|
||||
|
||||
impl InterruptConnect for Gpu {
|
||||
|
@ -249,56 +159,59 @@ impl SchedulerConnect for Gpu {
|
|||
impl Gpu {
|
||||
pub fn new(mut scheduler: SharedScheduler, interrupt_flags: SharedInterruptFlags) -> Gpu {
|
||||
scheduler.push_gpu_event(GpuEvent::HDraw, CYCLES_HDRAW);
|
||||
|
||||
fn alloc_scanline_buffer() -> Box<[Rgb15]> {
|
||||
vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH].into_boxed_slice()
|
||||
}
|
||||
|
||||
Gpu {
|
||||
interrupt_flags,
|
||||
scheduler,
|
||||
dispcnt: DisplayControl(0x80),
|
||||
dispstat: DisplayStatus(0),
|
||||
backgrounds: [
|
||||
Background::default(),
|
||||
Background::default(),
|
||||
Background::default(),
|
||||
Background::default(),
|
||||
],
|
||||
dispcnt: DisplayControl::from(0x80),
|
||||
dispstat: Default::default(),
|
||||
bgcnt: Default::default(),
|
||||
bg_vofs: [0; 4],
|
||||
bg_hofs: [0; 4],
|
||||
bg_aff: [BgAffine::default(); 2],
|
||||
win0: Window::default(),
|
||||
win1: Window::default(),
|
||||
winout_flags: WindowFlags::from(0),
|
||||
winobj_flags: WindowFlags::from(0),
|
||||
mosaic: RegMosaic(0),
|
||||
bldcnt: BlendControl(0),
|
||||
bldalpha: BlendAlpha(0),
|
||||
bldcnt: BlendControl::default(),
|
||||
bldalpha: BlendAlpha::default(),
|
||||
bldy: 0,
|
||||
|
||||
vram_obj_tiles_start: VRAM_OBJ_TILES_START_TEXT,
|
||||
|
||||
state: HDraw,
|
||||
vcount: 0,
|
||||
cycles_left_for_current_state: CYCLES_HDRAW,
|
||||
|
||||
palette_ram: vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
||||
vram: vec![0; VIDEO_RAM_SIZE].into_boxed_slice(),
|
||||
oam: vec![0; OAM_SIZE].into_boxed_slice(),
|
||||
|
||||
obj_buffer: vec![Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
||||
|
||||
frame_buffer: vec![0; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
||||
obj_buffer: vec![Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT].into_boxed_slice(),
|
||||
frame_buffer: vec![0; DISPLAY_WIDTH * DISPLAY_HEIGHT].into_boxed_slice(),
|
||||
bg_line: [
|
||||
alloc_scanline_buffer(),
|
||||
alloc_scanline_buffer(),
|
||||
alloc_scanline_buffer(),
|
||||
alloc_scanline_buffer(),
|
||||
],
|
||||
vram_obj_tiles_start: VRAM_OBJ_TILES_START_TEXT,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_dispcnt(&mut self, value: u16) {
|
||||
let new_dispcnt = DisplayControl(value);
|
||||
let old_mode = self.dispcnt.mode();
|
||||
let new_mode = new_dispcnt.mode();
|
||||
let old_mode = self.dispcnt.mode;
|
||||
self.dispcnt.write(value);
|
||||
let new_mode = self.dispcnt.mode;
|
||||
if old_mode != new_mode {
|
||||
debug!("[GPU] Display mode changed! {} -> {}", old_mode, new_mode);
|
||||
self.vram_obj_tiles_start = if new_dispcnt.mode() >= 3 {
|
||||
self.vram_obj_tiles_start = if new_mode >= 3 {
|
||||
VRAM_OBJ_TILES_START_BITMAP
|
||||
} else {
|
||||
VRAM_OBJ_TILES_START_TEXT
|
||||
};
|
||||
}
|
||||
self.dispcnt = new_dispcnt;
|
||||
}
|
||||
|
||||
pub fn skip_bios(&mut self) {
|
||||
|
@ -368,7 +281,7 @@ impl Gpu {
|
|||
}
|
||||
|
||||
pub fn render_scanline(&mut self) {
|
||||
if self.dispcnt.force_blank() {
|
||||
if self.dispcnt.force_blank {
|
||||
for x in self.frame_buffer[self.vcount * DISPLAY_WIDTH..]
|
||||
.iter_mut()
|
||||
.take(DISPLAY_WIDTH)
|
||||
|
@ -378,35 +291,35 @@ impl Gpu {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.dispcnt.enable_obj() {
|
||||
if self.dispcnt.enable_obj {
|
||||
self.render_objs();
|
||||
}
|
||||
match self.dispcnt.mode() {
|
||||
match self.dispcnt.mode {
|
||||
0 => {
|
||||
for bg in 0..=3 {
|
||||
if self.dispcnt.enable_bg(bg) {
|
||||
if self.dispcnt.enable_bg[bg] {
|
||||
self.render_reg_bg(bg);
|
||||
}
|
||||
}
|
||||
self.finalize_scanline(0, 3);
|
||||
}
|
||||
1 => {
|
||||
if self.dispcnt.enable_bg(2) {
|
||||
if self.dispcnt.enable_bg[2] {
|
||||
self.render_aff_bg(2);
|
||||
}
|
||||
if self.dispcnt.enable_bg(1) {
|
||||
if self.dispcnt.enable_bg[1] {
|
||||
self.render_reg_bg(1);
|
||||
}
|
||||
if self.dispcnt.enable_bg(0) {
|
||||
if self.dispcnt.enable_bg[0] {
|
||||
self.render_reg_bg(0);
|
||||
}
|
||||
self.finalize_scanline(0, 2);
|
||||
}
|
||||
2 => {
|
||||
if self.dispcnt.enable_bg(3) {
|
||||
if self.dispcnt.enable_bg[3] {
|
||||
self.render_aff_bg(3);
|
||||
}
|
||||
if self.dispcnt.enable_bg(2) {
|
||||
if self.dispcnt.enable_bg[2] {
|
||||
self.render_aff_bg(2);
|
||||
}
|
||||
self.finalize_scanline(2, 3);
|
||||
|
@ -423,7 +336,7 @@ impl Gpu {
|
|||
self.render_mode5(2);
|
||||
self.finalize_scanline(2, 2);
|
||||
}
|
||||
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
||||
_ => panic!("{:?} not supported", self.dispcnt.mode),
|
||||
}
|
||||
// self.mosaic_sfx();
|
||||
}
|
||||
|
@ -442,19 +355,18 @@ impl Gpu {
|
|||
#[inline]
|
||||
fn update_vcount(&mut self, value: usize) {
|
||||
self.vcount = value;
|
||||
let vcount_setting = self.dispstat.vcount_setting();
|
||||
self.dispstat
|
||||
.set_vcount_flag(vcount_setting == self.vcount as u16);
|
||||
let vcount_setting = self.dispstat.vcount_setting;
|
||||
self.dispstat.vcount_flag = vcount_setting == self.vcount;
|
||||
|
||||
if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount_flag() {
|
||||
if self.dispstat.vcount_irq_enable && self.dispstat.vcount_flag {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VCounterMatch);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle_hdraw<D: DmaNotifer>(&mut self, dma_notifier: &mut D) -> (GpuEvent, usize) {
|
||||
self.dispstat.set_hblank_flag(true);
|
||||
if self.dispstat.hblank_irq_enable() {
|
||||
fn handle_hdraw_end<D: DmaNotifer>(&mut self, dma_notifier: &mut D) -> (GpuEvent, usize) {
|
||||
self.dispstat.hblank_flag = true;
|
||||
if self.dispstat.hblank_irq_enable {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank);
|
||||
};
|
||||
dma_notifier.notify(TIMING_HBLANK);
|
||||
|
@ -463,7 +375,7 @@ impl Gpu {
|
|||
(GpuEvent::HBlank, CYCLES_HBLANK)
|
||||
}
|
||||
|
||||
fn handle_hblank<D: DmaNotifer>(
|
||||
fn handle_hblank_end<D: DmaNotifer>(
|
||||
&mut self,
|
||||
dma_notifier: &mut D,
|
||||
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
|
||||
|
@ -471,7 +383,7 @@ impl Gpu {
|
|||
self.update_vcount(self.vcount + 1);
|
||||
|
||||
if self.vcount < DISPLAY_HEIGHT {
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.dispstat.hblank_flag = false;
|
||||
self.render_scanline();
|
||||
// update BG2/3 reference points on the end of a scanline
|
||||
for i in 0..2 {
|
||||
|
@ -487,9 +399,9 @@ impl Gpu {
|
|||
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() {
|
||||
self.dispstat.vblank_flag = true;
|
||||
self.dispstat.hblank_flag = false;
|
||||
if self.dispstat.vblank_irq_enable {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VBlank);
|
||||
};
|
||||
|
||||
|
@ -504,149 +416,28 @@ impl Gpu {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_vblank_hdraw(&mut self) -> (GpuEvent, usize) {
|
||||
self.dispstat.set_hblank_flag(true);
|
||||
if self.dispstat.hblank_irq_enable() {
|
||||
fn handle_vblank_hdraw_end(&mut self) -> (GpuEvent, usize) {
|
||||
self.dispstat.hblank_flag = true;
|
||||
if self.dispstat.hblank_irq_enable {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank);
|
||||
};
|
||||
(GpuEvent::VBlankHBlank, CYCLES_HBLANK)
|
||||
}
|
||||
|
||||
fn handle_vblank_hblank(&mut self) -> (GpuEvent, usize) {
|
||||
fn handle_vblank_hblank_end(&mut self) -> (GpuEvent, usize) {
|
||||
if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
|
||||
self.update_vcount(self.vcount + 1);
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.dispstat.hblank_flag = false;
|
||||
(GpuEvent::VBlankHDraw, CYCLES_HDRAW)
|
||||
} else {
|
||||
self.update_vcount(0);
|
||||
self.dispstat.set_vblank_flag(false);
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.dispstat.vblank_flag = false;
|
||||
self.dispstat.hblank_flag = false;
|
||||
self.render_scanline();
|
||||
(GpuEvent::HDraw, CYCLES_HDRAW)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_state_completed<D>(
|
||||
&mut self,
|
||||
completed: GpuState,
|
||||
dma_notifier: &mut D,
|
||||
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
|
||||
) where
|
||||
D: DmaNotifer,
|
||||
{
|
||||
macro_rules! update_vcount {
|
||||
($value:expr) => {
|
||||
self.vcount = $value;
|
||||
let vcount_setting = self.dispstat.vcount_setting();
|
||||
self.dispstat
|
||||
.set_vcount_flag(vcount_setting == self.vcount as u16);
|
||||
|
||||
if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount_flag() {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VCounterMatch);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match completed {
|
||||
HDraw => {
|
||||
// Transition to HBlank
|
||||
self.state = HBlank;
|
||||
self.cycles_left_for_current_state = CYCLES_HBLANK;
|
||||
|
||||
self.handle_hdraw(dma_notifier);
|
||||
}
|
||||
HBlank => {
|
||||
update_vcount!(self.vcount + 1);
|
||||
|
||||
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 {
|
||||
// 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() {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VBlank);
|
||||
};
|
||||
|
||||
dma_notifier.notify(TIMING_VBLANK);
|
||||
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
video_device.borrow_mut().render(&self.frame_buffer);
|
||||
|
||||
self.obj_buffer_reset();
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
self.state = VBlankHDraw;
|
||||
}
|
||||
}
|
||||
VBlankHDraw => {
|
||||
self.cycles_left_for_current_state = CYCLES_HBLANK;
|
||||
self.state = VBlankHBlank;
|
||||
|
||||
self.dispstat.set_hblank_flag(true);
|
||||
if self.dispstat.hblank_irq_enable() {
|
||||
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank);
|
||||
};
|
||||
}
|
||||
VBlankHBlank => {
|
||||
if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
|
||||
update_vcount!(self.vcount + 1);
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
self.state = VBlankHDraw;
|
||||
} else {
|
||||
update_vcount!(0);
|
||||
self.dispstat.set_vblank_flag(false);
|
||||
self.dispstat.set_hblank_flag(false);
|
||||
self.render_scanline();
|
||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||
self.state = HDraw;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update<D>(
|
||||
&mut self,
|
||||
mut cycles: usize,
|
||||
cycles_to_next_event: &mut usize,
|
||||
dma_notifier: &mut D,
|
||||
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
|
||||
) where
|
||||
D: DmaNotifer,
|
||||
{
|
||||
loop {
|
||||
if self.cycles_left_for_current_state <= cycles {
|
||||
cycles -= self.cycles_left_for_current_state;
|
||||
self.on_state_completed(
|
||||
self.state,
|
||||
dma_notifier,
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
video_device,
|
||||
);
|
||||
} else {
|
||||
self.cycles_left_for_current_state -= cycles;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if self.cycles_left_for_current_state < *cycles_to_next_event {
|
||||
*cycles_to_next_event = self.cycles_left_for_current_state;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_event<D>(
|
||||
&mut self,
|
||||
event: GpuEvent,
|
||||
|
@ -657,14 +448,14 @@ impl Gpu {
|
|||
D: DmaNotifer,
|
||||
{
|
||||
let (next_event, cycles) = match event {
|
||||
GpuEvent::HDraw => self.handle_hdraw(dma_notifier),
|
||||
GpuEvent::HBlank => self.handle_hblank(
|
||||
GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier),
|
||||
GpuEvent::HBlank => self.handle_hblank_end(
|
||||
dma_notifier,
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
video_device,
|
||||
),
|
||||
GpuEvent::VBlankHDraw => self.handle_vblank_hdraw(),
|
||||
GpuEvent::VBlankHBlank => self.handle_vblank_hblank(),
|
||||
GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(),
|
||||
GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(),
|
||||
};
|
||||
self.scheduler
|
||||
.push(EventType::Gpu(next_event), cycles - extra_cycles);
|
||||
|
@ -765,32 +556,32 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_gpu_state_machine() {
|
||||
let mut gpu = Gpu::new(
|
||||
Scheduler::new_shared(),
|
||||
Rc::new(Cell::new(Default::default())),
|
||||
);
|
||||
let mut sched = Scheduler::new_shared();
|
||||
let mut gpu = Gpu::new(sched.clone(), Rc::new(Cell::new(Default::default())));
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
let video = Rc::new(RefCell::new(TestVideoInterface::default()));
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
let video_clone: VideoDeviceRcRefCell = video.clone();
|
||||
let mut dma_notifier = NopDmaNotifer;
|
||||
let mut cycles_to_next_event = CYCLES_FULL_REFRESH;
|
||||
|
||||
gpu.dispstat.set_vcount_setting(0);
|
||||
gpu.dispstat.set_vcount_irq_enable(true);
|
||||
|
||||
let mut total_cycles = 0;
|
||||
gpu.dispstat.vcount_setting = 0;
|
||||
gpu.dispstat.vcount_irq_enable = true;
|
||||
|
||||
macro_rules! update {
|
||||
($cycles:expr) => {
|
||||
gpu.update(
|
||||
$cycles,
|
||||
&mut cycles_to_next_event,
|
||||
&mut dma_notifier,
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
&video_clone,
|
||||
);
|
||||
total_cycles += $cycles;
|
||||
sched.update($cycles);
|
||||
let (event, cycles_late) = sched.pop_pending_event().unwrap();
|
||||
assert_eq!(cycles_late, 0);
|
||||
match event {
|
||||
EventType::Gpu(event) => gpu.on_event(
|
||||
event,
|
||||
cycles_late,
|
||||
&mut dma_notifier,
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
&video_clone,
|
||||
),
|
||||
_ => panic!("Found unexpected event in queue!"),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -799,15 +590,16 @@ mod tests {
|
|||
#[cfg(not(feature = "no_video_interface"))]
|
||||
assert_eq!(video.borrow().frame_counter, 0);
|
||||
assert_eq!(gpu.vcount, line);
|
||||
assert_eq!(gpu.state, GpuState::HDraw);
|
||||
assert_eq!(gpu.dispstat.get_hblank_flag(), false);
|
||||
assert_eq!(gpu.dispstat.get_vblank_flag(), false);
|
||||
assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HDraw)));
|
||||
assert_eq!(gpu.dispstat.hblank_flag, false);
|
||||
assert_eq!(gpu.dispstat.vblank_flag, false);
|
||||
|
||||
update!(CYCLES_HDRAW);
|
||||
|
||||
assert_eq!(gpu.state, GpuState::HBlank);
|
||||
assert_eq!(gpu.dispstat.get_hblank_flag(), true);
|
||||
assert_eq!(gpu.dispstat.get_vblank_flag(), false);
|
||||
println!("{:?}", sched.num_pending_events());
|
||||
assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HBlank)));
|
||||
assert_eq!(gpu.dispstat.hblank_flag, true);
|
||||
assert_eq!(gpu.dispstat.vblank_flag, false);
|
||||
|
||||
update!(CYCLES_HBLANK);
|
||||
|
||||
|
@ -819,15 +611,21 @@ mod tests {
|
|||
|
||||
for line in 0..68 {
|
||||
println!("line = {}", 160 + line);
|
||||
assert_eq!(gpu.dispstat.get_hblank_flag(), false);
|
||||
assert_eq!(gpu.dispstat.get_vblank_flag(), true);
|
||||
assert_eq!(gpu.state, GpuState::VBlankHDraw);
|
||||
assert_eq!(gpu.dispstat.hblank_flag, false);
|
||||
assert_eq!(gpu.dispstat.vblank_flag, true);
|
||||
assert_eq!(
|
||||
sched.peek_next(),
|
||||
Some(EventType::Gpu(GpuEvent::VBlankHDraw))
|
||||
);
|
||||
|
||||
update!(CYCLES_HDRAW);
|
||||
|
||||
assert_eq!(gpu.dispstat.get_hblank_flag(), true);
|
||||
assert_eq!(gpu.dispstat.get_vblank_flag(), true);
|
||||
assert_eq!(gpu.state, GpuState::VBlankHBlank);
|
||||
assert_eq!(gpu.dispstat.hblank_flag, true);
|
||||
assert_eq!(gpu.dispstat.vblank_flag, true);
|
||||
assert_eq!(
|
||||
sched.peek_next(),
|
||||
Some(EventType::Gpu(GpuEvent::VBlankHBlank))
|
||||
);
|
||||
assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), false);
|
||||
|
||||
update!(CYCLES_HBLANK);
|
||||
|
@ -835,13 +633,13 @@ mod tests {
|
|||
|
||||
#[cfg(not(feature = "no_video_interface"))]
|
||||
assert_eq!(video.borrow().frame_counter, 1);
|
||||
assert_eq!(total_cycles, CYCLES_FULL_REFRESH);
|
||||
assert_eq!(sched.timestamp(), CYCLES_FULL_REFRESH);
|
||||
|
||||
assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), true);
|
||||
assert_eq!(gpu.cycles_left_for_current_state, CYCLES_HDRAW);
|
||||
assert_eq!(gpu.state, GpuState::HDraw);
|
||||
assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HDraw)));
|
||||
assert_eq!(gpu.vcount, 0);
|
||||
assert_eq!(gpu.dispstat.get_vcount_flag(), true);
|
||||
assert_eq!(gpu.dispstat.get_hblank_flag(), false);
|
||||
assert_eq!(gpu.dispstat.vcount_flag, true);
|
||||
assert_eq!(gpu.dispstat.hblank_flag, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,21 +9,21 @@ impl RegMosaic {
|
|||
|
||||
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;
|
||||
// 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.enable_bg(bg) && self.backgrounds[bg].bgcnt.mosaic() {
|
||||
let y = self.vcount as usize;
|
||||
if y % vsize == 0 {
|
||||
self.backgrounds[bg].mosaic_first_row = self.backgrounds[bg].line.clone();
|
||||
}
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let color = self.backgrounds[bg].mosaic_first_row[(x / hsize) * hsize];
|
||||
self.backgrounds[bg].line[x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for bg in 0..4 {
|
||||
// if self.dispcnt.enable_bg[bg] && self.backgrounds[bg].mosaic {
|
||||
// let y = self.vcount as usize;
|
||||
// if y % vsize == 0 {
|
||||
// self.backgrounds[bg].mosaic_first_row = self.backgrounds[bg].line.clone();
|
||||
// }
|
||||
// for x in 0..DISPLAY_WIDTH {
|
||||
// let color = self.backgrounds[bg].mosaic_first_row[(x / hsize) * hsize];
|
||||
// self.backgrounds[bg].line[x] = color;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn mosaic_sfx(&mut self) {
|
||||
|
|
|
@ -1,27 +1,51 @@
|
|||
use super::layer::RenderLayer;
|
||||
use super::sfx::BldMode;
|
||||
use super::*;
|
||||
|
||||
use num::ToPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const SCREEN_BLOCK_SIZE: u32 = 0x800;
|
||||
|
||||
pub trait GpuMemoryMappedIO {
|
||||
fn read(&self) -> u16;
|
||||
fn write(&mut self, value: u16);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ObjMapping {
|
||||
TwoDimension,
|
||||
OneDimension,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct DisplayControl {
|
||||
pub mode: u16,
|
||||
pub display_frame_select: u16,
|
||||
pub hblank_interval_free: bool,
|
||||
pub obj_character_vram_mapping: bool,
|
||||
pub force_blank: bool,
|
||||
pub enable_bg: [bool; 4],
|
||||
pub enable_obj: bool,
|
||||
pub enable_window0: bool,
|
||||
pub enable_window1: bool,
|
||||
pub enable_obj_window: bool,
|
||||
}
|
||||
|
||||
impl From<u16> for DisplayControl {
|
||||
fn from(value: u16) -> DisplayControl {
|
||||
let mut dispcnt = DisplayControl::default();
|
||||
dispcnt.write(value);
|
||||
dispcnt
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayControl {
|
||||
pub fn enable_bg(&self, bg: usize) -> bool {
|
||||
self.0.bit(8 + bg)
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_using_windows(&self) -> bool {
|
||||
self.enable_window0() || self.enable_window1() || self.enable_obj_window()
|
||||
self.enable_window0 || self.enable_window1 || self.enable_obj_window
|
||||
}
|
||||
#[inline]
|
||||
pub fn obj_mapping(&self) -> ObjMapping {
|
||||
if self.obj_character_vram_mapping() {
|
||||
if self.obj_character_vram_mapping {
|
||||
ObjMapping::OneDimension
|
||||
} else {
|
||||
ObjMapping::TwoDimension
|
||||
|
@ -29,17 +53,122 @@ impl DisplayControl {
|
|||
}
|
||||
}
|
||||
|
||||
impl GpuMemoryMappedIO for DisplayControl {
|
||||
#[inline]
|
||||
fn write(&mut self, value: u16) {
|
||||
self.mode = value & 0b111;
|
||||
self.display_frame_select = (value >> 4) & 1;
|
||||
self.hblank_interval_free = (value >> 5) & 1 != 0;
|
||||
self.obj_character_vram_mapping = (value >> 6) & 1 != 0;
|
||||
self.force_blank = (value >> 7) & 1 != 0;
|
||||
self.enable_bg[0] = (value >> 8) & 1 != 0;
|
||||
self.enable_bg[1] = (value >> 9) & 1 != 0;
|
||||
self.enable_bg[2] = (value >> 10) & 1 != 0;
|
||||
self.enable_bg[3] = (value >> 11) & 1 != 0;
|
||||
self.enable_obj = (value >> 12) & 1 != 0;
|
||||
self.enable_window0 = (value >> 13) & 1 != 0;
|
||||
self.enable_window1 = (value >> 14) & 1 != 0;
|
||||
self.enable_obj_window = (value >> 15) & 1 != 0;
|
||||
}
|
||||
#[inline]
|
||||
fn read(&self) -> u16 {
|
||||
self.mode
|
||||
| self.display_frame_select << 4
|
||||
| u16::from(self.hblank_interval_free) << 5
|
||||
| u16::from(self.obj_character_vram_mapping) << 6
|
||||
| u16::from(self.force_blank) << 7
|
||||
| u16::from(self.enable_bg[0]) << 8
|
||||
| u16::from(self.enable_bg[1]) << 9
|
||||
| u16::from(self.enable_bg[2]) << 10
|
||||
| u16::from(self.enable_bg[3]) << 11
|
||||
| u16::from(self.enable_obj) << 12
|
||||
| u16::from(self.enable_window0) << 13
|
||||
| u16::from(self.enable_window1) << 14
|
||||
| u16::from(self.enable_obj_window) << 15
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||
pub struct DisplayStatus {
|
||||
pub vblank_flag: bool,
|
||||
pub hblank_flag: bool,
|
||||
pub vcount_flag: bool,
|
||||
pub vblank_irq_enable: bool,
|
||||
pub hblank_irq_enable: bool,
|
||||
pub vcount_irq_enable: bool,
|
||||
pub vcount_setting: usize,
|
||||
}
|
||||
|
||||
impl GpuMemoryMappedIO for DisplayStatus {
|
||||
#[inline]
|
||||
fn write(&mut self, value: u16) {
|
||||
// self.vblank_flag = (value >> 0) & 1 != 0;
|
||||
// self.hblank_flag = (value >> 1) & 1 != 0;
|
||||
// self.vcount_flag = (value >> 2) & 1 != 0;
|
||||
self.vblank_irq_enable = (value >> 3) & 1 != 0;
|
||||
self.hblank_irq_enable = (value >> 4) & 1 != 0;
|
||||
self.vcount_irq_enable = (value >> 5) & 1 != 0;
|
||||
self.vcount_setting = usize::from((value >> 8) & 0xff);
|
||||
}
|
||||
#[inline]
|
||||
fn read(&self) -> u16 {
|
||||
u16::from(self.vblank_flag) << 0
|
||||
| u16::from(self.hblank_flag) << 1
|
||||
| u16::from(self.vcount_flag) << 2
|
||||
| u16::from(self.vblank_irq_enable) << 3
|
||||
| u16::from(self.hblank_irq_enable) << 4
|
||||
| u16::from(self.vcount_irq_enable) << 5
|
||||
| (self.vcount_setting as u16) << 8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||
pub struct BgControl {
|
||||
pub priority: u16,
|
||||
pub character_base_block: u16,
|
||||
pub screen_base_block: u16,
|
||||
pub mosaic: bool,
|
||||
pub palette256: bool,
|
||||
pub affine_wraparound: bool,
|
||||
pub size: u8,
|
||||
}
|
||||
|
||||
impl GpuMemoryMappedIO for BgControl {
|
||||
#[inline]
|
||||
fn write(&mut self, value: u16) {
|
||||
self.priority = (value >> 0) & 0b11;
|
||||
self.character_base_block = (value >> 2) & 0b11;
|
||||
self.mosaic = (value >> 6) & 1 != 0;
|
||||
self.palette256 = (value >> 7) & 1 != 0;
|
||||
self.screen_base_block = (value >> 8) & 0b11111;
|
||||
self.affine_wraparound = (value >> 13) & 1 != 0;
|
||||
self.size = ((value >> 14) & 0b11) as u8;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read(&self) -> u16 {
|
||||
self.priority
|
||||
| self.character_base_block << 2
|
||||
| u16::from(self.mosaic) << 6
|
||||
| u16::from(self.palette256) << 7
|
||||
| self.screen_base_block << 8
|
||||
| u16::from(self.affine_wraparound) << 13
|
||||
| u16::from(self.size) << 14
|
||||
}
|
||||
}
|
||||
|
||||
impl BgControl {
|
||||
#[inline]
|
||||
pub fn char_block(&self) -> u32 {
|
||||
(self.character_base_block() as u32) * 0x4000
|
||||
(self.character_base_block as u32) * 0x4000
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn screen_block(&self) -> u32 {
|
||||
(self.screen_base_block() as u32) * SCREEN_BLOCK_SIZE
|
||||
(self.screen_base_block as u32) * SCREEN_BLOCK_SIZE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size_regular(&self) -> (u32, u32) {
|
||||
match self.bg_size() {
|
||||
match self.size {
|
||||
0b00 => (256, 256),
|
||||
0b01 => (512, 256),
|
||||
0b10 => (256, 512),
|
||||
|
@ -47,14 +176,9 @@ impl BgControl {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size_affine(&self) -> (i32, i32) {
|
||||
let x = 128 << self.bg_size();
|
||||
(x, x)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tile_format(&self) -> (u32, PixelFormat) {
|
||||
if self.palette256() {
|
||||
if self.palette256 {
|
||||
(2 * TILE_SIZE, PixelFormat::BPP8)
|
||||
} else {
|
||||
(TILE_SIZE, PixelFormat::BPP4)
|
||||
|
@ -62,55 +186,6 @@ impl BgControl {
|
|||
}
|
||||
}
|
||||
|
||||
// struct definitions below because the bitfield! macro messes up syntax highlighting in vscode.
|
||||
bitfield! {
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct DisplayControl(u16);
|
||||
impl Debug;
|
||||
u16;
|
||||
pub 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 force_blank, _: 7;
|
||||
pub enable_bg0, _ : 8;
|
||||
pub enable_bg1, _ : 9;
|
||||
pub enable_bg2, _ : 10;
|
||||
pub enable_bg3, _ : 11;
|
||||
pub enable_obj, _ : 12;
|
||||
pub enable_window0, _ : 13;
|
||||
pub enable_window1, _ : 14;
|
||||
pub enable_obj_window, _ : 15;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct DisplayStatus(u16);
|
||||
impl Debug;
|
||||
u16;
|
||||
pub get_vblank_flag, set_vblank_flag: 0;
|
||||
pub get_hblank_flag, set_hblank_flag: 1;
|
||||
pub get_vcount_flag, set_vcount_flag: 2;
|
||||
pub vblank_irq_enable, set_vblank_irq_enable : 3;
|
||||
pub hblank_irq_enable, set_hblank_irq_enable : 4;
|
||||
pub vcount_irq_enable, set_vcount_irq_enable : 5;
|
||||
pub vcount_setting, set_vcount_setting : 15, 8;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
#[derive(Serialize, Deserialize, 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(Serialize, Deserialize, Default, Copy, Clone)]
|
||||
pub struct RegMosaic(u16);
|
||||
|
@ -124,7 +199,7 @@ bitfield! {
|
|||
|
||||
bitflags! {
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
pub struct BlendFlags: u32 {
|
||||
pub struct BlendFlags: u16 {
|
||||
const BG0 = 0b00000001;
|
||||
const BG1 = 0b00000010;
|
||||
const BG2 = 0b00000100;
|
||||
|
@ -134,12 +209,6 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<u16> for BlendFlags {
|
||||
fn from(v: u16) -> BlendFlags {
|
||||
BlendFlags::from_bits(v as u32).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl BlendFlags {
|
||||
const BG_LAYER_FLAG: [BlendFlags; 4] = [
|
||||
BlendFlags::BG0,
|
||||
|
@ -147,44 +216,73 @@ impl BlendFlags {
|
|||
BlendFlags::BG2,
|
||||
BlendFlags::BG3,
|
||||
];
|
||||
|
||||
#[inline]
|
||||
pub fn from_bg(bg: usize) -> BlendFlags {
|
||||
Self::BG_LAYER_FLAG[bg]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn obj_enabled(&self) -> bool {
|
||||
self.contains(BlendFlags::OBJ)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains_render_layer(&self, layer: &RenderLayer) -> bool {
|
||||
let layer_flags = BlendFlags::from_bits(layer.kind.to_u32().unwrap()).unwrap();
|
||||
let layer_flags = BlendFlags::from_bits_truncate(layer.kind as u16);
|
||||
self.contains(layer_flags)
|
||||
}
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
#[derive(Serialize, Deserialize, 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;
|
||||
#[derive(SmartDefault, Debug, Serialize, Deserialize, Primitive, PartialEq, Clone, Copy)]
|
||||
pub enum BlendMode {
|
||||
#[default]
|
||||
BldNone = 0b00,
|
||||
BldAlpha = 0b01,
|
||||
BldWhite = 0b10,
|
||||
BldBlack = 0b11,
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
#[derive(Serialize, Deserialize, Default, Copy, Clone)]
|
||||
pub struct BlendAlpha(u16);
|
||||
impl Debug;
|
||||
u16;
|
||||
pub eva, _: 5, 0;
|
||||
pub evb, _: 12, 8;
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Copy, Clone)]
|
||||
pub struct BlendControl {
|
||||
pub target1: BlendFlags,
|
||||
pub target2: BlendFlags,
|
||||
pub mode: BlendMode,
|
||||
}
|
||||
|
||||
impl GpuMemoryMappedIO for BlendControl {
|
||||
#[inline]
|
||||
fn write(&mut self, value: u16) {
|
||||
self.target1 = BlendFlags::from_bits_truncate((value >> 0) & 0x3f);
|
||||
self.target2 = BlendFlags::from_bits_truncate((value >> 8) & 0x3f);
|
||||
self.mode = BlendMode::from_u16((value >> 6) & 0b11).unwrap_or_else(|| unreachable!());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read(&self) -> u16 {
|
||||
(self.target1.bits() << 0) | (self.mode as u16) << 6 | (self.target2.bits() << 8)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Copy, Clone)]
|
||||
pub struct BlendAlpha {
|
||||
pub eva: u16,
|
||||
pub evb: u16,
|
||||
}
|
||||
|
||||
impl GpuMemoryMappedIO for BlendAlpha {
|
||||
#[inline]
|
||||
fn write(&mut self, value: u16) {
|
||||
self.eva = value & 0x1f;
|
||||
self.evb = (value >> 8) & 0x1f;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read(&self) -> u16 {
|
||||
self.eva | self.evb << 8
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
pub struct WindowFlags: u32 {
|
||||
pub struct WindowFlags: u16 {
|
||||
const BG0 = 0b00000001;
|
||||
const BG1 = 0b00000010;
|
||||
const BG2 = 0b00000100;
|
||||
|
@ -196,7 +294,7 @@ bitflags! {
|
|||
|
||||
impl From<u16> for WindowFlags {
|
||||
fn from(v: u16) -> WindowFlags {
|
||||
WindowFlags::from_bits(v as u32).unwrap()
|
||||
WindowFlags::from_bits_truncate(v)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,12 +316,3 @@ const BG_WIN_FLAG: [WindowFlags; 4] = [
|
|||
WindowFlags::BG2,
|
||||
WindowFlags::BG3,
|
||||
];
|
||||
|
||||
bitfield! {
|
||||
#[derive(Serialize, Deserialize, Default, Copy, Clone)]
|
||||
pub struct WindowReg(u16);
|
||||
impl Debug;
|
||||
u16;
|
||||
pub into WindowFlags, lower, _: 5, 0;
|
||||
pub into WindowFlags, upper, _: 13, 8;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ impl Gpu {
|
|||
let pc = self.bg_aff[bg - 2].pc as i32;
|
||||
let ref_point = self.get_ref_point(bg);
|
||||
|
||||
let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound();
|
||||
let wraparound = self.bgcnt[bg].affine_wraparound;
|
||||
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let mut t = utils::transform_bg_point(ref_point, x as i32, pa, pc);
|
||||
|
@ -25,19 +25,19 @@ impl Gpu {
|
|||
t.0 = t.0.rem_euclid(SCREEN_VIEWPORT.w);
|
||||
t.1 = t.1.rem_euclid(SCREEN_VIEWPORT.h);
|
||||
} else {
|
||||
self.backgrounds[bg].line[x] = Rgb15::TRANSPARENT;
|
||||
self.bg_line[bg][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.backgrounds[bg].line[x] = color;
|
||||
self.bg_line[bg][x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn render_mode4(&mut self, bg: usize) {
|
||||
let page_ofs: u32 = match self.dispcnt.display_frame() {
|
||||
let page_ofs: u32 = match self.dispcnt.display_frame_select {
|
||||
0 => 0x0600_0000 - VRAM_ADDR,
|
||||
1 => 0x0600_a000 - VRAM_ADDR,
|
||||
_ => unreachable!(),
|
||||
|
@ -49,7 +49,7 @@ impl Gpu {
|
|||
let pc = self.bg_aff[bg - 2].pc as i32;
|
||||
let ref_point = self.get_ref_point(bg);
|
||||
|
||||
let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound();
|
||||
let wraparound = self.bgcnt[bg].affine_wraparound;
|
||||
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let mut t = utils::transform_bg_point(ref_point, x as i32, pa, pc);
|
||||
|
@ -58,7 +58,7 @@ impl Gpu {
|
|||
t.0 = t.0.rem_euclid(SCREEN_VIEWPORT.w);
|
||||
t.1 = t.1.rem_euclid(SCREEN_VIEWPORT.h);
|
||||
} else {
|
||||
self.backgrounds[bg].line[x] = Rgb15::TRANSPARENT;
|
||||
self.bg_line[bg][x] = Rgb15::TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -66,12 +66,12 @@ impl Gpu {
|
|||
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.backgrounds[bg].line[x] = color;
|
||||
self.bg_line[bg][x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
pub(in super::super) fn render_mode5(&mut self, bg: usize) {
|
||||
let page_ofs: u32 = match self.dispcnt.display_frame() {
|
||||
let page_ofs: u32 = match self.dispcnt.display_frame_select {
|
||||
0 => 0x0600_0000 - VRAM_ADDR,
|
||||
1 => 0x0600_a000 - VRAM_ADDR,
|
||||
_ => unreachable!(),
|
||||
|
@ -83,7 +83,7 @@ impl Gpu {
|
|||
let pc = self.bg_aff[bg - 2].pc as i32;
|
||||
let ref_point = self.get_ref_point(bg);
|
||||
|
||||
let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound();
|
||||
let wraparound = self.bgcnt[bg].affine_wraparound;
|
||||
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
let mut t = utils::transform_bg_point(ref_point, x as i32, pa, pc);
|
||||
|
@ -92,13 +92,13 @@ impl Gpu {
|
|||
t.0 = t.0.rem_euclid(MODE5_VIEWPORT.w);
|
||||
t.1 = t.1.rem_euclid(MODE5_VIEWPORT.h);
|
||||
} else {
|
||||
self.backgrounds[bg].line[x] = Rgb15::TRANSPARENT;
|
||||
self.bg_line[bg][x] = Rgb15::TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let pixel_ofs = page_ofs + 2 * index2d!(u32, t.0, t.1, MODE5_VIEWPORT.w);
|
||||
let color = Rgb15(self.vram.read_16(pixel_ofs));
|
||||
self.backgrounds[bg].line[x] = color;
|
||||
self.bg_line[bg][x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,12 @@ use crate::Bus;
|
|||
|
||||
impl Gpu {
|
||||
pub(in super::super) fn render_reg_bg(&mut self, bg: usize) {
|
||||
let (h_ofs, v_ofs) = (
|
||||
self.backgrounds[bg].bghofs as u32,
|
||||
self.backgrounds[bg].bgvofs as u32,
|
||||
);
|
||||
let tileset_base = self.backgrounds[bg].bgcnt.char_block();
|
||||
let tilemap_base = self.backgrounds[bg].bgcnt.screen_block();
|
||||
let (tile_size, pixel_format) = self.backgrounds[bg].bgcnt.tile_format();
|
||||
let (h_ofs, v_ofs) = (self.bg_hofs[bg] as u32, self.bg_vofs[bg] as u32);
|
||||
let tileset_base = self.bgcnt[bg].char_block();
|
||||
let tilemap_base = self.bgcnt[bg].screen_block();
|
||||
let (tile_size, pixel_format) = self.bgcnt[bg].tile_format();
|
||||
|
||||
let (bg_width, bg_height) = self.backgrounds[bg].bgcnt.size_regular();
|
||||
let (bg_width, bg_height) = self.bgcnt[bg].size_regular();
|
||||
|
||||
let screen_y = self.vcount as u32;
|
||||
let mut screen_x = 0;
|
||||
|
@ -70,7 +67,7 @@ impl Gpu {
|
|||
PixelFormat::BPP8 => 0u32,
|
||||
};
|
||||
let color = self.get_palette_color(index as u32, palette_bank, 0);
|
||||
self.backgrounds[bg].line[screen_x as usize] = color;
|
||||
self.bg_line[bg][screen_x as usize] = color;
|
||||
screen_x += 1;
|
||||
if (DISPLAY_WIDTH as u32) == screen_x {
|
||||
return;
|
||||
|
@ -96,17 +93,17 @@ impl Gpu {
|
|||
pub(in super::super) fn render_aff_bg(&mut self, bg: usize) {
|
||||
assert!(bg == 2 || bg == 3);
|
||||
|
||||
let texture_size = 128 << self.backgrounds[bg].bgcnt.bg_size();
|
||||
let texture_size = 128 << self.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.backgrounds[bg].bgcnt.screen_block();
|
||||
let char_block = self.backgrounds[bg].bgcnt.char_block();
|
||||
let screen_block = self.bgcnt[bg].screen_block();
|
||||
let char_block = self.bgcnt[bg].char_block();
|
||||
|
||||
let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound();
|
||||
let wraparound = self.bgcnt[bg].affine_wraparound;
|
||||
|
||||
for screen_x in 0..(DISPLAY_WIDTH as i32) {
|
||||
let mut t = utils::transform_bg_point(ref_point, screen_x, pa, pc);
|
||||
|
@ -116,7 +113,7 @@ impl Gpu {
|
|||
t.0 = t.0.rem_euclid(texture_size);
|
||||
t.1 = t.1.rem_euclid(texture_size);
|
||||
} else {
|
||||
self.backgrounds[bg].line[screen_x as usize] = Rgb15::TRANSPARENT;
|
||||
self.bg_line[bg][screen_x as usize] = Rgb15::TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +128,7 @@ impl Gpu {
|
|||
PixelFormat::BPP8,
|
||||
) as u32;
|
||||
let color = self.get_palette_color(pixel_index, 0, 0);
|
||||
self.backgrounds[bg].line[screen_x as usize] = color;
|
||||
self.bg_line[bg][screen_x as usize] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,12 @@
|
|||
use std::cmp;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use num::FromPrimitive;
|
||||
|
||||
use super::regs::*;
|
||||
|
||||
use super::layer::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Primitive, PartialEq, 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);
|
||||
|
@ -31,33 +16,26 @@ impl Rgb15 {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<WindowFlags> for BlendFlags {
|
||||
fn from(wf: WindowFlags) -> BlendFlags {
|
||||
BlendFlags::from_bits(wf.bits()).unwrap()
|
||||
}
|
||||
/// Filters a background indexes array by whether they're active
|
||||
fn filter_window_backgrounds(
|
||||
backgrounds: &[usize],
|
||||
window_flags: WindowFlags,
|
||||
) -> ArrayVec<[usize; 4]> {
|
||||
backgrounds
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|bg| window_flags.bg_enabled(*bg))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Gpu {
|
||||
/// Filters a background indexes array by whether they're active
|
||||
fn active_backgrounds_for_window(
|
||||
&self,
|
||||
backgrounds: &[usize],
|
||||
window_flags: WindowFlags,
|
||||
) -> ArrayVec<[usize; 4]> {
|
||||
backgrounds
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|bg| window_flags.bg_enabled(*bg))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn layer_to_pixel(&mut self, x: usize, y: usize, layer: &RenderLayer) -> Rgb15 {
|
||||
match layer.kind {
|
||||
RenderLayerKind::Background0 => self.backgrounds[0].line[x],
|
||||
RenderLayerKind::Background1 => self.backgrounds[1].line[x],
|
||||
RenderLayerKind::Background2 => self.backgrounds[2].line[x],
|
||||
RenderLayerKind::Background3 => self.backgrounds[3].line[x],
|
||||
RenderLayerKind::Background0 => self.bg_line[0][x],
|
||||
RenderLayerKind::Background1 => self.bg_line[1][x],
|
||||
RenderLayerKind::Background2 => self.bg_line[2][x],
|
||||
RenderLayerKind::Background3 => self.bg_line[3][x],
|
||||
RenderLayerKind::Objects => self.obj_buffer_get(x, y).color,
|
||||
RenderLayerKind::Backdrop => Rgb15(self.palette_ram.read_16(0)),
|
||||
}
|
||||
|
@ -70,9 +48,9 @@ impl Gpu {
|
|||
// filter out disabled backgrounds and sort by priority
|
||||
// the backgrounds are sorted once for the entire scanline
|
||||
let mut sorted_backgrounds: ArrayVec<[usize; 4]> = (bg_start..=bg_end)
|
||||
.filter(|bg| self.dispcnt.enable_bg(*bg))
|
||||
.filter(|bg| self.dispcnt.enable_bg[*bg])
|
||||
.collect();
|
||||
sorted_backgrounds.sort_by_key(|bg| (self.backgrounds[*bg].bgcnt.priority(), *bg));
|
||||
sorted_backgrounds.sort_by_key(|bg| (self.bgcnt[*bg].priority, *bg));
|
||||
|
||||
let y = self.vcount;
|
||||
|
||||
|
@ -84,10 +62,9 @@ impl Gpu {
|
|||
} else {
|
||||
let mut occupied = [false; DISPLAY_WIDTH];
|
||||
let mut occupied_count = 0;
|
||||
if self.dispcnt.enable_window0() && self.win0.contains_y(y) {
|
||||
if self.dispcnt.enable_window0 && self.win0.contains_y(y) {
|
||||
let win = WindowInfo::new(WindowType::Win0, self.win0.flags);
|
||||
let backgrounds =
|
||||
self.active_backgrounds_for_window(&sorted_backgrounds, win.flags);
|
||||
let backgrounds = filter_window_backgrounds(&sorted_backgrounds, win.flags);
|
||||
for x in self.win0.left()..self.win0.right() {
|
||||
self.finalize_pixel(x, y, &win, &backgrounds, backdrop_color);
|
||||
occupied[x] = true;
|
||||
|
@ -97,10 +74,9 @@ impl Gpu {
|
|||
if occupied_count == DISPLAY_WIDTH {
|
||||
return;
|
||||
}
|
||||
if self.dispcnt.enable_window1() && self.win1.contains_y(y) {
|
||||
if self.dispcnt.enable_window1 && self.win1.contains_y(y) {
|
||||
let win = WindowInfo::new(WindowType::Win1, self.win1.flags);
|
||||
let backgrounds =
|
||||
self.active_backgrounds_for_window(&sorted_backgrounds, win.flags);
|
||||
let backgrounds = filter_window_backgrounds(&sorted_backgrounds, win.flags);
|
||||
for x in self.win1.left()..self.win1.right() {
|
||||
if occupied[x] {
|
||||
continue;
|
||||
|
@ -114,12 +90,11 @@ impl Gpu {
|
|||
return;
|
||||
}
|
||||
let win_out = WindowInfo::new(WindowType::WinOut, self.winout_flags);
|
||||
let win_out_backgrounds =
|
||||
self.active_backgrounds_for_window(&sorted_backgrounds, win_out.flags);
|
||||
if self.dispcnt.enable_obj_window() {
|
||||
let win_out_backgrounds = filter_window_backgrounds(&sorted_backgrounds, win_out.flags);
|
||||
if self.dispcnt.enable_obj_window {
|
||||
let win_obj = WindowInfo::new(WindowType::WinObj, self.winobj_flags);
|
||||
let win_obj_backgrounds =
|
||||
self.active_backgrounds_for_window(&sorted_backgrounds, win_obj.flags);
|
||||
filter_window_backgrounds(&sorted_backgrounds, win_obj.flags);
|
||||
for x in 0..DISPLAY_WIDTH {
|
||||
if occupied[x] {
|
||||
continue;
|
||||
|
@ -128,13 +103,9 @@ impl Gpu {
|
|||
if obj_entry.window {
|
||||
// WinObj
|
||||
self.finalize_pixel(x, y, &win_obj, &win_obj_backgrounds, backdrop_color);
|
||||
// occupied[x] = true;
|
||||
// occupied_count += 1;
|
||||
} else {
|
||||
// WinOut
|
||||
self.finalize_pixel(x, y, &win_out, &win_out_backgrounds, backdrop_color);
|
||||
// occupied[x] = true;
|
||||
// occupied_count += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -143,8 +114,6 @@ impl Gpu {
|
|||
continue;
|
||||
}
|
||||
self.finalize_pixel(x, y, &win_out, &win_out_backgrounds, backdrop_color);
|
||||
// occupied[x] = true;
|
||||
// occupied_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,25 +139,22 @@ impl Gpu {
|
|||
// lets start by taking the first 2 backgrounds that have an opaque pixel at x
|
||||
let mut it = backgrounds
|
||||
.iter()
|
||||
.filter(|i| !self.backgrounds[**i].line[x].is_transparent())
|
||||
.filter(|i| !self.bg_line[**i][x].is_transparent())
|
||||
.take(2);
|
||||
|
||||
let mut top_layer = it.next().map_or(backdrop_layer, |bg| {
|
||||
let background = &self.backgrounds[*bg];
|
||||
RenderLayer::background(*bg, background.pixel_at(x), background.get_priority())
|
||||
RenderLayer::background(*bg, self.bg_line[*bg][x], self.bgcnt[*bg].priority)
|
||||
});
|
||||
|
||||
let mut bot_layer = it.next().map_or(backdrop_layer, |bg| {
|
||||
let background = &self.backgrounds[*bg];
|
||||
RenderLayer::background(*bg, background.pixel_at(x), background.get_priority())
|
||||
RenderLayer::background(*bg, self.bg_line[*bg][x], self.bgcnt[*bg].priority)
|
||||
});
|
||||
|
||||
drop(it);
|
||||
|
||||
// Now that backgrounds are taken care of, we need to check if there is an object pixel that takes priority of one of the layers
|
||||
let obj_entry = self.obj_buffer_get(x, y);
|
||||
if win.flags.obj_enabled() && self.dispcnt.enable_obj() && !obj_entry.color.is_transparent()
|
||||
{
|
||||
if win.flags.obj_enabled() && self.dispcnt.enable_obj && !obj_entry.color.is_transparent() {
|
||||
let obj_layer = RenderLayer::objects(obj_entry.color, obj_entry.priority);
|
||||
if obj_layer.priority <= top_layer.priority {
|
||||
bot_layer = top_layer;
|
||||
|
@ -201,10 +167,10 @@ impl Gpu {
|
|||
let obj_entry = self.obj_buffer_get(x, y);
|
||||
let obj_alpha_blend = top_layer.is_object() && obj_entry.alpha;
|
||||
|
||||
let top_flags = self.bldcnt.top();
|
||||
let bot_flags = self.bldcnt.bottom();
|
||||
let top_flags = self.bldcnt.target1;
|
||||
let bot_flags = self.bldcnt.target2;
|
||||
|
||||
let sfx_enabled = (self.bldcnt.mode() != BldMode::BldNone || obj_alpha_blend)
|
||||
let sfx_enabled = (self.bldcnt.mode != BlendMode::BldNone || obj_alpha_blend)
|
||||
&& top_flags.contains_render_layer(&top_layer); // sfx must at least have a first target configured
|
||||
|
||||
if win.flags.sfx_enabled() && sfx_enabled {
|
||||
|
@ -216,8 +182,8 @@ impl Gpu {
|
|||
} else {
|
||||
let (top_layer, bot_layer) = (top_layer, bot_layer);
|
||||
|
||||
match self.bldcnt.mode() {
|
||||
BldMode::BldAlpha => {
|
||||
match self.bldcnt.mode {
|
||||
BlendMode::BldAlpha => {
|
||||
output[x] = if bot_flags.contains_render_layer(&bot_layer) {
|
||||
self.do_alpha(top_layer.pixel, bot_layer.pixel).to_rgb24()
|
||||
} else {
|
||||
|
@ -225,11 +191,11 @@ impl Gpu {
|
|||
top_layer.pixel.to_rgb24()
|
||||
}
|
||||
}
|
||||
BldMode::BldWhite => output[x] = self.do_brighten(top_layer.pixel).to_rgb24(),
|
||||
BlendMode::BldWhite => output[x] = self.do_brighten(top_layer.pixel).to_rgb24(),
|
||||
|
||||
BldMode::BldBlack => output[x] = self.do_darken(top_layer.pixel).to_rgb24(),
|
||||
BlendMode::BldBlack => output[x] = self.do_darken(top_layer.pixel).to_rgb24(),
|
||||
|
||||
BldMode::BldNone => output[x] = top_layer.pixel.to_rgb24(),
|
||||
BlendMode::BldNone => output[x] = top_layer.pixel.to_rgb24(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -240,8 +206,8 @@ impl Gpu {
|
|||
|
||||
#[inline]
|
||||
fn do_alpha(&self, upper: Rgb15, lower: Rgb15) -> Rgb15 {
|
||||
let eva = self.bldalpha.eva();
|
||||
let evb = self.bldalpha.evb();
|
||||
let eva = self.bldalpha.eva;
|
||||
let evb = self.bldalpha.evb;
|
||||
upper.blend_with(lower, eva, evb)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::cmp;
|
|||
|
||||
use super::bus::*;
|
||||
use super::dma::DmaController;
|
||||
use super::gpu::regs::GpuMemoryMappedIO;
|
||||
use super::gpu::regs::WindowFlags;
|
||||
use super::gpu::*;
|
||||
use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags};
|
||||
|
@ -99,13 +100,13 @@ impl Bus for IoDevices {
|
|||
// }
|
||||
|
||||
match io_addr {
|
||||
REG_DISPCNT => io.gpu.dispcnt.0,
|
||||
REG_DISPSTAT => io.gpu.dispstat.0,
|
||||
REG_DISPCNT => io.gpu.dispcnt.read(),
|
||||
REG_DISPSTAT => io.gpu.dispstat.read(),
|
||||
REG_VCOUNT => io.gpu.vcount as u16,
|
||||
REG_BG0CNT => io.gpu.backgrounds[0].bgcnt.0,
|
||||
REG_BG1CNT => io.gpu.backgrounds[1].bgcnt.0,
|
||||
REG_BG2CNT => io.gpu.backgrounds[2].bgcnt.0,
|
||||
REG_BG3CNT => io.gpu.backgrounds[3].bgcnt.0,
|
||||
REG_BG0CNT => io.gpu.bgcnt[0].read(),
|
||||
REG_BG1CNT => io.gpu.bgcnt[1].read(),
|
||||
REG_BG2CNT => io.gpu.bgcnt[2].read(),
|
||||
REG_BG3CNT => io.gpu.bgcnt[3].read(),
|
||||
REG_WIN0H => ((io.gpu.win0.left as u16) << 8 | (io.gpu.win0.right as u16)),
|
||||
REG_WIN1H => ((io.gpu.win1.left as u16) << 8 | (io.gpu.win1.right as u16)),
|
||||
REG_WIN0V => ((io.gpu.win0.top as u16) << 8 | (io.gpu.win0.bottom as u16)),
|
||||
|
@ -116,8 +117,8 @@ impl Bus for IoDevices {
|
|||
REG_WINOUT => {
|
||||
((io.gpu.winobj_flags.bits() as u16) << 8) | (io.gpu.winout_flags.bits() as u16)
|
||||
}
|
||||
REG_BLDCNT => io.gpu.bldcnt.0,
|
||||
REG_BLDALPHA => io.gpu.bldalpha.0,
|
||||
REG_BLDCNT => io.gpu.bldcnt.read(),
|
||||
REG_BLDALPHA => io.gpu.bldalpha.read(),
|
||||
|
||||
REG_IME => io.intc.interrupt_master_enable as u16,
|
||||
REG_IE => io.intc.interrupt_enable.0 as u16,
|
||||
|
@ -187,19 +188,19 @@ impl Bus for IoDevices {
|
|||
|
||||
match io_addr {
|
||||
REG_DISPCNT => io.gpu.write_dispcnt(value),
|
||||
REG_DISPSTAT => io.gpu.dispstat.0 = value | (io.gpu.dispstat.0 & 7),
|
||||
REG_BG0CNT => io.gpu.backgrounds[0].bgcnt.0 = value,
|
||||
REG_BG1CNT => io.gpu.backgrounds[1].bgcnt.0 = value,
|
||||
REG_BG2CNT => io.gpu.backgrounds[2].bgcnt.0 = value,
|
||||
REG_BG3CNT => io.gpu.backgrounds[3].bgcnt.0 = value,
|
||||
REG_BG0HOFS => io.gpu.backgrounds[0].bghofs = value & 0x1ff,
|
||||
REG_BG0VOFS => io.gpu.backgrounds[0].bgvofs = value & 0x1ff,
|
||||
REG_BG1HOFS => io.gpu.backgrounds[1].bghofs = value & 0x1ff,
|
||||
REG_BG1VOFS => io.gpu.backgrounds[1].bgvofs = value & 0x1ff,
|
||||
REG_BG2HOFS => io.gpu.backgrounds[2].bghofs = value & 0x1ff,
|
||||
REG_BG2VOFS => io.gpu.backgrounds[2].bgvofs = value & 0x1ff,
|
||||
REG_BG3HOFS => io.gpu.backgrounds[3].bghofs = value & 0x1ff,
|
||||
REG_BG3VOFS => io.gpu.backgrounds[3].bgvofs = value & 0x1ff,
|
||||
REG_DISPSTAT => io.gpu.dispstat.write(value),
|
||||
REG_BG0CNT => io.gpu.bgcnt[0].write(value),
|
||||
REG_BG1CNT => io.gpu.bgcnt[1].write(value),
|
||||
REG_BG2CNT => io.gpu.bgcnt[2].write(value),
|
||||
REG_BG3CNT => io.gpu.bgcnt[3].write(value),
|
||||
REG_BG0HOFS => io.gpu.bg_hofs[0] = value & 0x1ff,
|
||||
REG_BG0VOFS => io.gpu.bg_vofs[0] = value & 0x1ff,
|
||||
REG_BG1HOFS => io.gpu.bg_hofs[1] = value & 0x1ff,
|
||||
REG_BG1VOFS => io.gpu.bg_vofs[1] = value & 0x1ff,
|
||||
REG_BG2HOFS => io.gpu.bg_hofs[2] = value & 0x1ff,
|
||||
REG_BG2VOFS => io.gpu.bg_vofs[2] = value & 0x1ff,
|
||||
REG_BG3HOFS => io.gpu.bg_hofs[3] = value & 0x1ff,
|
||||
REG_BG3VOFS => io.gpu.bg_vofs[3] = value & 0x1ff,
|
||||
REG_BG2X_L | REG_BG3X_L => write_reference_point!(low bg x internal_x),
|
||||
REG_BG2Y_L | REG_BG3Y_L => write_reference_point!(low bg y internal_y),
|
||||
REG_BG2X_H | REG_BG3X_H => write_reference_point!(high bg x internal_x),
|
||||
|
@ -247,8 +248,8 @@ impl Bus for IoDevices {
|
|||
io.gpu.winobj_flags = WindowFlags::from(value >> 8);
|
||||
}
|
||||
REG_MOSAIC => io.gpu.mosaic.0 = value,
|
||||
REG_BLDCNT => io.gpu.bldcnt.0 = value,
|
||||
REG_BLDALPHA => io.gpu.bldalpha.0 = value,
|
||||
REG_BLDCNT => io.gpu.bldcnt.write(value),
|
||||
REG_BLDALPHA => io.gpu.bldalpha.write(value),
|
||||
REG_BLDY => io.gpu.bldy = cmp::min(value & 0b11111, 16),
|
||||
|
||||
REG_IME => io.intc.interrupt_master_enable = value != 0,
|
||||
|
|
|
@ -26,6 +26,9 @@ extern crate log;
|
|||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
|
||||
#[macro_use]
|
||||
extern crate smart_default;
|
||||
|
||||
extern crate cfg_if;
|
||||
|
||||
use zip;
|
||||
|
|
|
@ -128,6 +128,18 @@ impl Scheduler {
|
|||
SharedScheduler::new(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unused)]
|
||||
pub fn num_pending_events(&self) -> usize {
|
||||
self.events.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unused)]
|
||||
pub fn peek_next(&self) -> Option<EventType> {
|
||||
self.events.peek().map(|e| e.typ)
|
||||
}
|
||||
|
||||
/// Schedule an event to be executed in `cycles` cycles from now
|
||||
pub fn push(&mut self, typ: EventType, cycles: usize) {
|
||||
let event = Event::new(typ, self.timestamp + cycles);
|
||||
|
|
Reference in a new issue