2019-12-29 19:30:33 +00:00
|
|
|
use std::cell::RefCell;
|
2019-08-02 15:58:56 +01:00
|
|
|
use std::fmt;
|
2019-12-29 21:03:57 +00:00
|
|
|
use std::rc::Rc;
|
2019-08-02 15:58:56 +01:00
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
use super::super::VideoInterface;
|
|
|
|
use super::interrupt::IrqBitmask;
|
|
|
|
use super::sysbus::{BoxedMemory, SysBus};
|
|
|
|
use super::Bus;
|
2019-07-06 13:53:36 +01:00
|
|
|
|
2019-08-02 22:18:59 +01:00
|
|
|
use crate::bitfield::Bit;
|
2019-07-06 13:53:36 +01:00
|
|
|
use crate::num::FromPrimitive;
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
mod render;
|
|
|
|
|
2020-01-16 17:46:36 +00:00
|
|
|
use render::Point;
|
|
|
|
|
2020-02-07 12:39:35 +00:00
|
|
|
mod layer;
|
2019-08-13 19:57:45 +01:00
|
|
|
mod mosaic;
|
2019-12-29 19:27:51 +00:00
|
|
|
mod rgb15;
|
2019-12-29 21:03:57 +00:00
|
|
|
mod sfx;
|
2020-02-07 12:39:35 +00:00
|
|
|
mod window;
|
|
|
|
|
2019-12-29 19:27:51 +00:00
|
|
|
pub use rgb15::Rgb15;
|
2020-02-07 12:39:35 +00:00
|
|
|
pub use window::*;
|
2019-08-13 19:57:45 +01:00
|
|
|
|
2019-08-23 22:36:48 +01:00
|
|
|
pub mod regs;
|
2019-08-11 20:35:32 +01:00
|
|
|
pub use regs::*;
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
#[allow(unused)]
|
|
|
|
pub mod consts {
|
|
|
|
pub const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
|
|
|
pub const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
|
|
|
pub const OAM_SIZE: usize = 1 * 1024;
|
|
|
|
|
|
|
|
pub const VRAM_ADDR: u32 = 0x0600_0000;
|
|
|
|
pub const DISPLAY_WIDTH: usize = 240;
|
|
|
|
pub const DISPLAY_HEIGHT: usize = 160;
|
|
|
|
pub const VBLANK_LINES: usize = 68;
|
|
|
|
|
|
|
|
pub(super) const CYCLES_PIXEL: usize = 4;
|
|
|
|
pub(super) const CYCLES_HDRAW: usize = 960;
|
|
|
|
pub(super) const CYCLES_HBLANK: usize = 272;
|
|
|
|
pub(super) const CYCLES_SCANLINE: usize = 1232;
|
|
|
|
pub(super) const CYCLES_VDRAW: usize = 197120;
|
|
|
|
pub(super) const CYCLES_VBLANK: usize = 83776;
|
|
|
|
|
|
|
|
pub const TILE_SIZE: u32 = 0x20;
|
|
|
|
}
|
|
|
|
pub use self::consts::*;
|
2019-08-13 19:57:45 +01:00
|
|
|
|
|
|
|
#[derive(Debug, Primitive, Copy, Clone)]
|
|
|
|
pub enum PixelFormat {
|
|
|
|
BPP4 = 0,
|
|
|
|
BPP8 = 1,
|
|
|
|
}
|
2019-07-15 05:35:09 +01:00
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
2019-07-15 23:21:11 +01:00
|
|
|
pub enum GpuState {
|
2019-07-11 16:17:28 +01:00
|
|
|
HDraw = 0,
|
|
|
|
HBlank,
|
|
|
|
VBlank,
|
|
|
|
}
|
2019-07-15 23:21:11 +01:00
|
|
|
impl Default for GpuState {
|
|
|
|
fn default() -> GpuState {
|
|
|
|
GpuState::HDraw
|
2019-07-11 16:17:28 +01:00
|
|
|
}
|
|
|
|
}
|
2019-07-15 23:21:11 +01:00
|
|
|
use GpuState::*;
|
2019-07-11 16:17:28 +01:00
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
|
|
pub struct Scanline {
|
|
|
|
inner: Vec<Rgb15>,
|
|
|
|
}
|
2019-08-13 19:57:45 +01:00
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
impl Default for Scanline {
|
|
|
|
fn default() -> Scanline {
|
|
|
|
Scanline {
|
|
|
|
inner: vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH],
|
|
|
|
}
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
impl fmt::Debug for Scanline {
|
2020-02-14 12:01:48 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2019-08-13 19:57:45 +01:00
|
|
|
write!(f, "...")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
impl std::ops::Index<usize> for Scanline {
|
|
|
|
type Output = Rgb15;
|
2019-08-13 19:57:45 +01:00
|
|
|
fn index(&self, index: usize) -> &Self::Output {
|
2020-01-16 17:53:09 +00:00
|
|
|
&self.inner[index]
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
impl std::ops::IndexMut<usize> for Scanline {
|
2019-08-13 19:57:45 +01:00
|
|
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
2020-01-16 17:53:09 +00:00
|
|
|
&mut self.inner[index]
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
2019-11-21 15:46:51 +00:00
|
|
|
pub struct Background {
|
2019-08-13 19:57:45 +01:00
|
|
|
pub bgcnt: BgControl,
|
|
|
|
pub bgvofs: u16,
|
|
|
|
pub bghofs: u16,
|
2020-01-16 17:53:09 +00:00
|
|
|
|
|
|
|
line: Scanline,
|
2019-08-13 19:57:45 +01:00
|
|
|
|
|
|
|
// for mosaic
|
2020-01-16 17:53:09 +00:00
|
|
|
mosaic_first_row: Scanline,
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 03:27:57 +00:00
|
|
|
#[derive(Debug, Default, Copy, Clone)]
|
|
|
|
pub struct AffineMatrix {
|
|
|
|
pub pa: i32,
|
|
|
|
pub pb: i32,
|
|
|
|
pub pc: i32,
|
|
|
|
pub pd: i32,
|
|
|
|
}
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Copy, Clone)]
|
2019-08-13 19:57:45 +01:00
|
|
|
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,
|
2020-01-16 17:46:36 +00:00
|
|
|
pub internal_x: i32,
|
|
|
|
pub internal_y: i32,
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
2019-12-29 21:03:57 +00:00
|
|
|
pub struct ObjBufferEntry {
|
2020-02-07 12:39:35 +00:00
|
|
|
pub(super) window: bool,
|
|
|
|
pub(super) alpha: bool,
|
2019-12-29 21:03:57 +00:00
|
|
|
pub(super) color: Rgb15,
|
|
|
|
pub(super) priority: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ObjBufferEntry {
|
|
|
|
fn default() -> ObjBufferEntry {
|
|
|
|
ObjBufferEntry {
|
|
|
|
window: false,
|
2020-02-07 12:39:35 +00:00
|
|
|
alpha: false,
|
2019-12-29 21:03:57 +00:00
|
|
|
color: Rgb15::TRANSPARENT,
|
|
|
|
priority: 4,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-29 19:30:33 +00:00
|
|
|
type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Clone, DebugStub)]
|
2019-07-15 23:21:11 +01:00
|
|
|
pub struct Gpu {
|
2019-08-23 22:36:48 +01:00
|
|
|
pub state: GpuState,
|
2020-01-11 13:58:32 +00:00
|
|
|
|
|
|
|
/// how many cycles left until next gpu state ?
|
|
|
|
cycles_left_for_current_state: usize,
|
2019-08-23 22:36:48 +01:00
|
|
|
|
2019-08-02 22:18:59 +01:00
|
|
|
// registers
|
2019-12-28 18:25:27 +00:00
|
|
|
pub vcount: usize, // VCOUNT
|
2019-08-02 22:18:59 +01:00
|
|
|
pub dispcnt: DisplayControl,
|
|
|
|
pub dispstat: DisplayStatus,
|
2019-08-13 19:57:45 +01:00
|
|
|
|
2019-11-21 15:46:51 +00:00
|
|
|
pub bg: [Background; 4],
|
2019-08-13 19:57:45 +01:00
|
|
|
pub bg_aff: [BgAffine; 2],
|
|
|
|
|
2019-08-23 22:36:48 +01:00
|
|
|
pub win0: Window,
|
|
|
|
pub win1: Window,
|
|
|
|
pub winout_flags: WindowFlags,
|
|
|
|
pub winobj_flags: WindowFlags,
|
|
|
|
|
2019-08-13 19:57:45 +01:00
|
|
|
pub mosaic: RegMosaic,
|
|
|
|
pub bldcnt: BlendControl,
|
|
|
|
pub bldalpha: BlendAlpha,
|
2019-08-02 22:18:59 +01:00
|
|
|
pub bldy: u16,
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
pub palette_ram: BoxedMemory,
|
|
|
|
pub vram: BoxedMemory,
|
|
|
|
pub oam: BoxedMemory,
|
|
|
|
|
2019-12-28 19:13:44 +00:00
|
|
|
#[debug_stub = "Sprite Buffer"]
|
2020-01-16 17:53:09 +00:00
|
|
|
pub obj_buffer: Vec<ObjBufferEntry>,
|
2019-12-28 19:13:44 +00:00
|
|
|
|
|
|
|
#[debug_stub = "Frame Buffer"]
|
2020-01-16 17:53:09 +00:00
|
|
|
pub(super) frame_buffer: Vec<u32>,
|
2019-07-06 13:53:36 +01:00
|
|
|
}
|
|
|
|
|
2019-07-15 23:21:11 +01:00
|
|
|
impl Gpu {
|
2020-01-16 17:56:05 +00:00
|
|
|
pub fn new() -> Gpu {
|
2019-07-15 23:21:11 +01:00
|
|
|
Gpu {
|
2019-08-02 22:18:59 +01:00
|
|
|
dispcnt: DisplayControl(0x80),
|
|
|
|
dispstat: DisplayStatus(0),
|
2020-01-16 17:53:09 +00:00
|
|
|
bg: [
|
|
|
|
Background::default(),
|
|
|
|
Background::default(),
|
|
|
|
Background::default(),
|
|
|
|
Background::default(),
|
|
|
|
],
|
2019-08-13 19:57:45 +01:00
|
|
|
bg_aff: [BgAffine::default(); 2],
|
2019-08-23 22:36:48 +01:00
|
|
|
win0: Window::default(),
|
|
|
|
win1: Window::default(),
|
|
|
|
winout_flags: WindowFlags::from(0),
|
|
|
|
winobj_flags: WindowFlags::from(0),
|
2019-08-13 19:57:45 +01:00
|
|
|
mosaic: RegMosaic(0),
|
|
|
|
bldcnt: BlendControl(0),
|
|
|
|
bldalpha: BlendAlpha(0),
|
2019-08-02 22:18:59 +01:00
|
|
|
bldy: 0,
|
|
|
|
|
2019-07-11 16:17:28 +01:00
|
|
|
state: HDraw,
|
2019-12-28 18:25:27 +00:00
|
|
|
vcount: 0,
|
2020-01-11 13:58:32 +00:00
|
|
|
cycles_left_for_current_state: CYCLES_HDRAW,
|
2019-12-29 21:03:57 +00:00
|
|
|
|
|
|
|
palette_ram: BoxedMemory::new(vec![0; PALETTE_RAM_SIZE].into_boxed_slice()),
|
|
|
|
vram: BoxedMemory::new(vec![0; VIDEO_RAM_SIZE].into_boxed_slice()),
|
|
|
|
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice()),
|
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
obj_buffer: vec![Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
2020-02-07 12:39:35 +00:00
|
|
|
|
2020-01-16 17:53:09 +00:00
|
|
|
frame_buffer: vec![0; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
2019-07-06 13:53:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 17:47:05 +00:00
|
|
|
pub fn skip_bios(&mut self) {
|
|
|
|
for i in 0..2 {
|
|
|
|
self.bg_aff[i].pa = 0x100;
|
|
|
|
self.bg_aff[i].pb = 0;
|
|
|
|
self.bg_aff[i].pc = 0;
|
|
|
|
self.bg_aff[i].pd = 0x100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-15 05:35:09 +01:00
|
|
|
/// helper method that reads the palette index from a base address and x + y
|
2019-12-29 21:03:57 +00:00
|
|
|
pub fn read_pixel_index(&self, addr: u32, x: u32, y: u32, format: PixelFormat) -> usize {
|
2019-08-02 15:58:56 +01:00
|
|
|
let ofs = addr - VRAM_ADDR;
|
2019-07-15 05:35:09 +01:00
|
|
|
match format {
|
|
|
|
PixelFormat::BPP4 => {
|
2019-12-29 21:03:57 +00:00
|
|
|
let byte = self.vram.read_8(ofs + index2d!(u32, x / 2, y, 4));
|
2019-07-15 05:35:09 +01:00
|
|
|
if x & 1 != 0 {
|
|
|
|
(byte >> 4) as usize
|
|
|
|
} else {
|
|
|
|
(byte & 0xf) as usize
|
|
|
|
}
|
|
|
|
}
|
2019-12-29 21:03:57 +00:00
|
|
|
PixelFormat::BPP8 => self.vram.read_8(ofs + index2d!(u32, x, y, 8)) as usize,
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
pub fn get_palette_color(&self, index: u32, palette_index: u32, offset: u32) -> Rgb15 {
|
2019-08-13 19:57:45 +01:00
|
|
|
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
|
|
|
return Rgb15::TRANSPARENT;
|
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
Rgb15(
|
2019-12-29 21:03:57 +00:00
|
|
|
self.palette_ram
|
2019-08-27 21:15:22 +01:00
|
|
|
.read_16(offset + 2 * index + 0x20 * palette_index),
|
|
|
|
)
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
|
2020-02-07 12:39:35 +00:00
|
|
|
#[inline]
|
2019-12-29 21:03:57 +00:00
|
|
|
pub(super) fn obj_buffer_get(&self, x: usize, y: usize) -> &ObjBufferEntry {
|
|
|
|
&self.obj_buffer[index2d!(x, y, DISPLAY_WIDTH)]
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
|
2020-02-07 12:39:35 +00:00
|
|
|
#[inline]
|
2019-12-29 21:03:57 +00:00
|
|
|
pub(super) fn obj_buffer_get_mut(&mut self, x: usize, y: usize) -> &mut ObjBufferEntry {
|
|
|
|
&mut self.obj_buffer[index2d!(x, y, DISPLAY_WIDTH)]
|
2019-07-21 22:09:44 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 17:46:36 +00:00
|
|
|
pub fn get_ref_point(&self, bg: usize) -> Point {
|
|
|
|
assert!(bg == 2 || bg == 3);
|
|
|
|
(
|
|
|
|
self.bg_aff[bg - 2].internal_x,
|
|
|
|
self.bg_aff[bg - 2].internal_y,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
pub fn render_scanline(&mut self) {
|
2020-02-07 12:39:35 +00:00
|
|
|
if self.dispcnt.enable_obj() {
|
|
|
|
self.render_objs();
|
|
|
|
}
|
2019-08-02 22:18:59 +01:00
|
|
|
match self.dispcnt.mode() {
|
2019-08-27 21:15:22 +01:00
|
|
|
0 => {
|
2020-02-07 12:39:35 +00:00
|
|
|
for bg in 0..=3 {
|
|
|
|
if self.dispcnt.enable_bg(bg) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_reg_bg(bg);
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
self.finalize_scanline(0, 3);
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
1 => {
|
2020-02-07 12:39:35 +00:00
|
|
|
if self.dispcnt.enable_bg(2) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_aff_bg(2);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
if self.dispcnt.enable_bg(1) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_reg_bg(1);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
if self.dispcnt.enable_bg(0) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_reg_bg(0);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
self.finalize_scanline(0, 2);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
2 => {
|
2020-02-07 12:39:35 +00:00
|
|
|
if self.dispcnt.enable_bg(3) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_aff_bg(3);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
if self.dispcnt.enable_bg(2) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_aff_bg(2);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
self.finalize_scanline(2, 3);
|
2019-07-29 07:46:12 +01:00
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
3 => {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_mode3(2);
|
2020-02-07 12:39:35 +00:00
|
|
|
self.finalize_scanline(2, 2);
|
2019-07-21 22:09:44 +01:00
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
4 => {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_mode4(2);
|
2020-02-07 12:39:35 +00:00
|
|
|
self.finalize_scanline(2, 2);
|
2019-07-15 18:49:47 +01:00
|
|
|
}
|
2019-08-02 22:18:59 +01:00
|
|
|
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
2020-02-07 12:39:35 +00:00
|
|
|
// self.mosaic_sfx();
|
2019-07-20 21:02:18 +01:00
|
|
|
}
|
2019-07-11 16:17:28 +01:00
|
|
|
|
2019-11-12 16:11:45 +00:00
|
|
|
fn update_vcount(&mut self, value: usize, irqs: &mut IrqBitmask) {
|
2019-12-28 18:25:27 +00:00
|
|
|
self.vcount = value;
|
2019-11-12 16:11:45 +00:00
|
|
|
let vcount_setting = self.dispstat.vcount_setting();
|
|
|
|
self.dispstat
|
2019-12-28 18:25:27 +00:00
|
|
|
.set_vcount_flag(vcount_setting == self.vcount as u16);
|
2019-11-12 16:11:45 +00:00
|
|
|
|
|
|
|
if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount_flag() {
|
|
|
|
irqs.set_LCD_VCounterMatch(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-07 12:39:35 +00:00
|
|
|
/// Clears the gpu obj buffer
|
|
|
|
pub fn obj_buffer_reset(&mut self) {
|
2019-12-29 21:03:57 +00:00
|
|
|
for x in self.obj_buffer.iter_mut() {
|
|
|
|
*x = Default::default();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 17:46:36 +00:00
|
|
|
pub fn on_state_completed(
|
|
|
|
&mut self,
|
|
|
|
completed: GpuState,
|
|
|
|
sb: &mut SysBus,
|
|
|
|
irqs: &mut IrqBitmask,
|
2020-01-16 17:56:05 +00:00
|
|
|
video_device: &VideoDeviceRcRefCell,
|
2020-01-16 17:46:36 +00:00
|
|
|
) {
|
2020-02-07 12:39:35 +00:00
|
|
|
match completed {
|
2020-01-16 17:46:36 +00:00
|
|
|
HDraw => {
|
|
|
|
// Transition to HBlank
|
|
|
|
self.state = HBlank;
|
|
|
|
self.cycles_left_for_current_state = CYCLES_HBLANK;
|
|
|
|
self.dispstat.set_hblank_flag(true);
|
|
|
|
|
|
|
|
if self.dispstat.hblank_irq_enable() {
|
|
|
|
irqs.set_LCD_HBlank(true);
|
|
|
|
};
|
|
|
|
sb.io.dmac.notify_hblank();
|
|
|
|
}
|
|
|
|
HBlank => {
|
|
|
|
self.update_vcount(self.vcount + 1, irqs);
|
|
|
|
|
|
|
|
if self.vcount < DISPLAY_HEIGHT {
|
|
|
|
self.state = HDraw;
|
|
|
|
self.dispstat.set_hblank_flag(false);
|
|
|
|
self.render_scanline();
|
|
|
|
// update BG2/3 reference points on the end of a scanline
|
|
|
|
for i in 0..2 {
|
|
|
|
self.bg_aff[i].internal_x += self.bg_aff[i].pb as i16 as i32;
|
|
|
|
self.bg_aff[i].internal_y += self.bg_aff[i].pd as i16 as i32;
|
|
|
|
}
|
|
|
|
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
|
|
|
} else {
|
|
|
|
self.state = VBlank;
|
|
|
|
|
|
|
|
// latch BG2/3 reference points on vblank
|
|
|
|
for i in 0..2 {
|
|
|
|
self.bg_aff[i].internal_x = self.bg_aff[i].x;
|
|
|
|
self.bg_aff[i].internal_y = self.bg_aff[i].y;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.dispstat.set_vblank_flag(true);
|
|
|
|
self.dispstat.set_hblank_flag(false);
|
|
|
|
if self.dispstat.vblank_irq_enable() {
|
|
|
|
irqs.set_LCD_VBlank(true);
|
|
|
|
};
|
|
|
|
|
|
|
|
sb.io.dmac.notify_vblank();
|
2020-01-16 17:56:05 +00:00
|
|
|
video_device.borrow_mut().render(&self.frame_buffer);
|
2020-02-07 12:39:35 +00:00
|
|
|
self.obj_buffer_reset();
|
2020-01-16 17:46:36 +00:00
|
|
|
self.cycles_left_for_current_state = CYCLES_SCANLINE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VBlank => {
|
|
|
|
if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
|
|
|
|
self.update_vcount(self.vcount + 1, irqs);
|
|
|
|
self.cycles_left_for_current_state = CYCLES_SCANLINE;
|
|
|
|
} else {
|
|
|
|
self.update_vcount(0, irqs);
|
|
|
|
self.dispstat.set_vblank_flag(false);
|
|
|
|
self.render_scanline();
|
|
|
|
self.state = HDraw;
|
|
|
|
|
|
|
|
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-11-08 23:43:43 +00:00
|
|
|
// Returns the new gpu state
|
2020-01-11 13:58:32 +00:00
|
|
|
pub fn step(
|
|
|
|
&mut self,
|
|
|
|
cycles: usize,
|
|
|
|
sb: &mut SysBus,
|
|
|
|
irqs: &mut IrqBitmask,
|
|
|
|
cycles_to_next_event: &mut usize,
|
2020-01-16 17:56:05 +00:00
|
|
|
video_device: &VideoDeviceRcRefCell,
|
2020-01-11 13:58:32 +00:00
|
|
|
) {
|
|
|
|
if self.cycles_left_for_current_state <= cycles {
|
|
|
|
let overshoot = cycles - self.cycles_left_for_current_state;
|
|
|
|
|
2020-01-16 17:56:05 +00:00
|
|
|
self.on_state_completed(self.state, sb, irqs, video_device);
|
2020-01-11 13:58:32 +00:00
|
|
|
|
|
|
|
// handle the overshoot
|
|
|
|
if overshoot < self.cycles_left_for_current_state {
|
|
|
|
self.cycles_left_for_current_state -= overshoot;
|
|
|
|
} else {
|
|
|
|
panic!("OH SHIT");
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
2020-01-11 13:58:32 +00:00
|
|
|
} else {
|
|
|
|
self.cycles_left_for_current_state -= cycles;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.cycles_left_for_current_state < *cycles_to_next_event {
|
|
|
|
*cycles_to_next_event = self.cycles_left_for_current_state;
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|