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
|
|
|
|
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;
|
|
|
|
|
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;
|
2019-12-29 19:27:51 +00:00
|
|
|
pub use rgb15::Rgb15;
|
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
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
pub type FrameBuffer<T> = [T; DISPLAY_WIDTH * DISPLAY_HEIGHT];
|
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
|
|
|
|
2019-07-11 16:17:28 +01:00
|
|
|
#[derive(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
|
|
|
|
2019-08-13 19:57:45 +01:00
|
|
|
#[derive(Copy, Clone)]
|
2019-08-27 21:15:22 +01:00
|
|
|
pub struct Scanline<T>([T; DISPLAY_WIDTH]);
|
2019-08-13 19:57:45 +01:00
|
|
|
|
2019-08-27 21:15:22 +01:00
|
|
|
impl Default for Scanline<Rgb15> {
|
|
|
|
fn default() -> Scanline<Rgb15> {
|
|
|
|
Scanline([Rgb15::TRANSPARENT; DISPLAY_WIDTH])
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 21:15:22 +01:00
|
|
|
impl<T> fmt::Debug for Scanline<T> {
|
2019-08-13 19:57:45 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "...")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 21:15:22 +01:00
|
|
|
impl<T> std::ops::Index<usize> for Scanline<T> {
|
|
|
|
type Output = T;
|
2019-08-13 19:57:45 +01:00
|
|
|
fn index(&self, index: usize) -> &Self::Output {
|
|
|
|
&self.0[index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 21:15:22 +01:00
|
|
|
impl<T> std::ops::IndexMut<usize> for Scanline<T> {
|
2019-08-13 19:57:45 +01:00
|
|
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
|
|
|
&mut self.0[index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Copy, 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,
|
2019-08-27 21:15:22 +01:00
|
|
|
line: Scanline<Rgb15>,
|
2019-08-13 19:57:45 +01:00
|
|
|
|
|
|
|
// for mosaic
|
2019-08-27 21:15:22 +01:00
|
|
|
mosaic_first_row: Scanline<Rgb15>,
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
|
2019-08-23 22:36:48 +01:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub struct Window {
|
|
|
|
pub left: u8,
|
|
|
|
pub right: u8,
|
|
|
|
pub top: u8,
|
|
|
|
pub bottom: u8,
|
|
|
|
pub flags: WindowFlags,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Window {
|
|
|
|
pub fn inside(&self, x: usize, y: usize) -> bool {
|
|
|
|
let left = self.left as usize;
|
|
|
|
let mut right = self.right as usize;
|
|
|
|
let top = self.top as usize;
|
|
|
|
let mut bottom = self.bottom as usize;
|
|
|
|
|
|
|
|
if right > DISPLAY_WIDTH || right < left {
|
|
|
|
right = DISPLAY_WIDTH;
|
|
|
|
}
|
|
|
|
if bottom > DISPLAY_HEIGHT || bottom < top {
|
|
|
|
bottom = DISPLAY_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
(x >= left && x < right) && (y >= top && y < bottom)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum WindowType {
|
|
|
|
Win0,
|
|
|
|
Win1,
|
|
|
|
WinObj,
|
|
|
|
WinOut,
|
|
|
|
WinNone,
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2019-08-13 19:57:45 +01:00
|
|
|
#[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,
|
|
|
|
}
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub struct ObjBufferEntry {
|
|
|
|
pub(super) color: Rgb15,
|
|
|
|
pub(super) priority: u16,
|
|
|
|
pub(super) window: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ObjBufferEntry {
|
|
|
|
fn default() -> ObjBufferEntry {
|
|
|
|
ObjBufferEntry {
|
|
|
|
window: false,
|
|
|
|
color: Rgb15::TRANSPARENT,
|
|
|
|
priority: 4,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-29 19:30:33 +00:00
|
|
|
type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
|
|
|
|
|
2019-12-28 19:13:44 +00:00
|
|
|
#[derive(DebugStub)]
|
2019-07-15 23:21:11 +01:00
|
|
|
pub struct Gpu {
|
2019-12-29 19:30:33 +00:00
|
|
|
#[debug_stub = "video handle"]
|
|
|
|
video_device: VideoDeviceRcRefCell,
|
2019-08-23 22:36:48 +01:00
|
|
|
pub state: GpuState,
|
|
|
|
cycles: usize,
|
|
|
|
|
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"]
|
2019-12-29 21:03:57 +00:00
|
|
|
pub obj_buffer: FrameBuffer<ObjBufferEntry>,
|
2019-12-28 19:13:44 +00:00
|
|
|
|
|
|
|
#[debug_stub = "Frame Buffer"]
|
2019-12-29 21:03:57 +00:00
|
|
|
pub(super) frame_buffer: FrameBuffer<u32>,
|
2019-07-06 13:53:36 +01:00
|
|
|
}
|
|
|
|
|
2019-07-15 23:21:11 +01:00
|
|
|
impl Gpu {
|
2019-12-29 19:30:33 +00:00
|
|
|
pub fn new(video_device: VideoDeviceRcRefCell) -> Gpu {
|
2019-07-15 23:21:11 +01:00
|
|
|
Gpu {
|
2019-12-29 19:30:33 +00:00
|
|
|
video_device: video_device,
|
|
|
|
|
2019-08-02 22:18:59 +01:00
|
|
|
dispcnt: DisplayControl(0x80),
|
|
|
|
dispstat: DisplayStatus(0),
|
2019-11-21 15:46:51 +00:00
|
|
|
bg: [Background::default(); 4],
|
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,
|
2019-07-15 05:35:09 +01:00
|
|
|
cycles: 0,
|
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()),
|
|
|
|
|
|
|
|
obj_buffer: [Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
2019-12-28 19:13:44 +00:00
|
|
|
frame_buffer: [0; DISPLAY_WIDTH * DISPLAY_HEIGHT],
|
2019-07-06 13:53:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
pub(super) fn render_pixel(&mut self, x: i32, y: i32, p: Rgb15) {
|
|
|
|
self.frame_buffer[index2d!(usize, x, y, DISPLAY_WIDTH)] = p.to_rgb24();
|
2019-07-15 18:49:47 +01:00
|
|
|
}
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
pub fn render_scanline(&mut self) {
|
2019-08-02 22:18:59 +01:00
|
|
|
match self.dispcnt.mode() {
|
2019-08-27 21:15:22 +01:00
|
|
|
0 => {
|
2019-11-16 16:10:49 +00:00
|
|
|
for bg in 0..4 {
|
2019-08-02 22:18:59 +01:00
|
|
|
if self.dispcnt.disp_bg(bg) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_reg_bg(bg);
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
1 => {
|
2019-08-13 19:57:45 +01:00
|
|
|
if self.dispcnt.disp_bg(2) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_aff_bg(2);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
if self.dispcnt.disp_bg(1) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_reg_bg(1);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
if self.dispcnt.disp_bg(0) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_reg_bg(0);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
2 => {
|
2019-08-13 19:57:45 +01:00
|
|
|
if self.dispcnt.disp_bg(3) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_aff_bg(3);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
|
|
|
if self.dispcnt.disp_bg(2) {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_aff_bg(2);
|
2019-08-13 19:57:45 +01:00
|
|
|
}
|
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);
|
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);
|
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
|
|
|
}
|
2019-08-27 21:15:22 +01:00
|
|
|
if self.dispcnt.disp_obj() {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_objs();
|
2019-08-27 21:15:22 +01:00
|
|
|
}
|
2019-08-13 19:57:45 +01:00
|
|
|
self.mosaic_sfx();
|
2019-12-29 21:03:57 +00:00
|
|
|
self.composite_sfx_to_framebuffer();
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-29 21:03:57 +00:00
|
|
|
// Clears the gpu internal buffer
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
for x in self.obj_buffer.iter_mut() {
|
|
|
|
*x = Default::default();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-08 23:43:43 +00:00
|
|
|
// Returns the new gpu state
|
2019-12-28 13:59:35 +00:00
|
|
|
pub fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
2019-07-15 05:35:09 +01:00
|
|
|
self.cycles += cycles;
|
|
|
|
|
|
|
|
match self.state {
|
|
|
|
HDraw => {
|
2019-08-13 19:57:45 +01:00
|
|
|
if self.cycles > CYCLES_HDRAW {
|
|
|
|
self.cycles -= CYCLES_HDRAW;
|
2019-11-12 16:11:45 +00:00
|
|
|
// HBlank
|
|
|
|
self.dispstat.set_hblank_flag(true);
|
|
|
|
if self.dispstat.hblank_irq_enable() {
|
|
|
|
irqs.set_LCD_HBlank(true);
|
|
|
|
};
|
|
|
|
self.state = HBlank;
|
2019-12-27 14:31:18 +00:00
|
|
|
sb.io.dmac.notify_hblank();
|
2019-11-12 16:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
HBlank => {
|
|
|
|
if self.cycles > CYCLES_HBLANK {
|
|
|
|
self.cycles -= CYCLES_HBLANK;
|
|
|
|
|
|
|
|
self.dispstat.set_hblank_flag(false);
|
2019-12-28 18:25:27 +00:00
|
|
|
self.update_vcount(self.vcount + 1, irqs);
|
2019-07-15 05:35:09 +01:00
|
|
|
|
2019-12-28 18:25:27 +00:00
|
|
|
if self.vcount < DISPLAY_HEIGHT {
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_scanline();
|
2019-11-12 16:11:45 +00:00
|
|
|
self.state = HDraw;
|
2019-07-15 05:35:09 +01:00
|
|
|
} else {
|
2019-11-12 16:11:45 +00:00
|
|
|
self.state = VBlank;
|
|
|
|
self.dispstat.set_vblank_flag(true);
|
2019-08-05 07:44:27 +01:00
|
|
|
if self.dispstat.vblank_irq_enable() {
|
|
|
|
irqs.set_LCD_VBlank(true);
|
2019-07-15 05:35:09 +01:00
|
|
|
};
|
2019-12-27 14:31:18 +00:00
|
|
|
sb.io.dmac.notify_vblank();
|
2019-12-29 19:30:33 +00:00
|
|
|
self.video_device.borrow_mut().render(&self.frame_buffer);
|
2019-11-12 16:11:45 +00:00
|
|
|
}
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
VBlank => {
|
2019-11-12 16:11:45 +00:00
|
|
|
if self.cycles > CYCLES_SCANLINE {
|
|
|
|
self.cycles -= CYCLES_SCANLINE;
|
|
|
|
|
2019-12-28 18:25:27 +00:00
|
|
|
if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
|
|
|
|
self.update_vcount(self.vcount + 1, irqs);
|
2019-11-12 16:11:45 +00:00
|
|
|
} else {
|
|
|
|
self.update_vcount(0, irqs);
|
|
|
|
self.dispstat.set_vblank_flag(false);
|
2019-12-29 21:03:57 +00:00
|
|
|
self.render_scanline();
|
2019-11-12 16:11:45 +00:00
|
|
|
self.state = HDraw;
|
|
|
|
}
|
2019-07-15 05:35:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|