2021-01-19 04:54:38 +00:00
|
|
|
use crate::instruction::Cycles;
|
2021-03-16 05:27:27 +00:00
|
|
|
use bitfield::bitfield;
|
2021-01-19 04:54:38 +00:00
|
|
|
|
2021-01-19 06:30:10 +00:00
|
|
|
const GB_WIDTH: usize = 160;
|
|
|
|
const GB_HEIGHT: usize = 144;
|
2020-12-24 06:27:06 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2021-03-16 06:05:13 +00:00
|
|
|
pub struct Ppu {
|
2021-01-18 01:25:53 +00:00
|
|
|
pub lcd_control: LCDControl,
|
|
|
|
pub monochrome: Monochrome,
|
2021-01-18 08:22:45 +00:00
|
|
|
pub pos: ScreenPosition,
|
2021-01-03 05:58:49 +00:00
|
|
|
pub vram: Box<[u8]>,
|
2021-01-19 04:54:38 +00:00
|
|
|
pub oam: Box<[u8]>,
|
2021-01-19 06:30:10 +00:00
|
|
|
frame_buf: [u8; GB_WIDTH * GB_HEIGHT * 4],
|
2021-01-18 08:22:45 +00:00
|
|
|
pub stat: LCDStatus,
|
2021-01-19 04:54:38 +00:00
|
|
|
cycles: Cycles,
|
2021-01-19 06:30:10 +00:00
|
|
|
mode: Mode,
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
impl Ppu {
|
2021-01-19 04:54:38 +00:00
|
|
|
pub fn step(&mut self, cycles: Cycles) {
|
|
|
|
self.cycles += cycles;
|
|
|
|
|
2021-03-16 00:19:40 +00:00
|
|
|
// let tmp: u32 = self.cycles.into();
|
|
|
|
// println!("Mode: {:?} | Cycles: {}", self.mode, tmp);
|
2021-01-19 06:30:10 +00:00
|
|
|
|
2021-01-19 04:54:38 +00:00
|
|
|
match self.mode {
|
2021-01-19 06:30:10 +00:00
|
|
|
Mode::OAMScan => {
|
2021-01-19 04:54:38 +00:00
|
|
|
if self.cycles >= 80.into() {
|
2021-01-19 06:30:10 +00:00
|
|
|
self.cycles %= 80;
|
|
|
|
self.mode = Mode::Draw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Mode::Draw => {
|
|
|
|
if self.cycles >= 172.into() {
|
|
|
|
// 172 -> 129 Cycles
|
|
|
|
// self.cycles %= 172;
|
|
|
|
self.mode = Mode::HBlank;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Mode::HBlank => {
|
|
|
|
// The 80 comes from the 80 cycles we made disappear in OAMScan above.
|
|
|
|
if self.cycles >= (456 - 80).into() {
|
|
|
|
self.cycles %= 456 - 80;
|
|
|
|
self.pos.line_y += 1;
|
|
|
|
|
|
|
|
if self.pos.line_y >= 144 {
|
|
|
|
self.mode = Mode::VBlank;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Mode::VBlank => {
|
|
|
|
if self.cycles >= 456.into() {
|
|
|
|
self.cycles %= 456;
|
|
|
|
self.pos.line_y += 1;
|
|
|
|
|
|
|
|
if self.pos.line_y == 154 {
|
|
|
|
self.mode = Mode::OAMScan;
|
|
|
|
self.pos.line_y = 0;
|
|
|
|
}
|
2021-01-19 04:54:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-19 02:47:09 +00:00
|
|
|
|
|
|
|
pub fn draw(&self, frame: &mut [u8]) {
|
2021-01-19 06:30:10 +00:00
|
|
|
frame.copy_from_slice(&self.frame_buf);
|
2021-01-19 02:47:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-16 06:05:13 +00:00
|
|
|
impl Default for Ppu {
|
2020-12-24 06:27:06 +00:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2021-01-18 01:25:53 +00:00
|
|
|
lcd_control: Default::default(),
|
|
|
|
monochrome: Default::default(),
|
2021-01-18 08:22:45 +00:00
|
|
|
pos: Default::default(),
|
|
|
|
stat: Default::default(),
|
2021-01-03 05:58:49 +00:00
|
|
|
vram: vec![0; 8192].into_boxed_slice(),
|
2021-01-19 04:54:38 +00:00
|
|
|
oam: vec![0; 160].into_boxed_slice(),
|
|
|
|
cycles: 0.into(),
|
2021-01-19 06:30:10 +00:00
|
|
|
frame_buf: [0; GB_WIDTH * GB_HEIGHT * 4],
|
|
|
|
mode: Mode::OAMScan,
|
2020-12-24 06:27:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-19 04:54:38 +00:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2021-01-19 06:30:10 +00:00
|
|
|
enum Mode {
|
2021-01-19 04:54:38 +00:00
|
|
|
OAMScan,
|
|
|
|
Draw,
|
|
|
|
HBlank,
|
|
|
|
VBlank,
|
|
|
|
}
|
|
|
|
|
2021-03-16 05:27:27 +00:00
|
|
|
bitfield! {
|
|
|
|
pub struct LCDStatus(u8);
|
|
|
|
impl Debug;
|
|
|
|
pub lyc_ly_intr, set_lyc_ly_intr: 6;
|
|
|
|
pub oam_intr, set_oam_intr: 5;
|
|
|
|
pub vblank_intr, set_vblank_intr: 4;
|
|
|
|
pub hblank_intr, set_hblank_intr: 3;
|
|
|
|
pub lyc_ly_flag, _: 2;
|
|
|
|
pub from into LCDMode, mode, _: 1, 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Copy for LCDStatus {}
|
|
|
|
impl Clone for LCDStatus {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
*self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for LCDStatus {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(0)
|
|
|
|
}
|
2021-01-18 08:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for LCDStatus {
|
|
|
|
fn from(byte: u8) -> Self {
|
2021-03-16 05:27:27 +00:00
|
|
|
Self(byte)
|
2021-01-18 08:22:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<LCDStatus> for u8 {
|
|
|
|
fn from(status: LCDStatus) -> Self {
|
2021-03-16 05:27:27 +00:00
|
|
|
status.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum LCDMode {
|
|
|
|
HBlank = 0,
|
|
|
|
VBlank = 1,
|
2021-03-16 06:05:13 +00:00
|
|
|
Oam = 2,
|
2021-03-16 05:27:27 +00:00
|
|
|
Transfer = 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for LCDMode {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
|
|
|
0b00 => Self::HBlank,
|
|
|
|
0b01 => Self::VBlank,
|
2021-03-16 06:05:13 +00:00
|
|
|
0b10 => Self::Oam,
|
2021-03-16 05:27:27 +00:00
|
|
|
0b11 => Self::Transfer,
|
|
|
|
_ => unreachable!("{:#04X} is not a valid value for LCDMode", byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for LCDMode {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::HBlank
|
2021-01-18 08:22:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
|
|
pub struct ScreenPosition {
|
|
|
|
pub scroll_y: u8,
|
|
|
|
pub scroll_x: u8,
|
|
|
|
pub line_y: u8,
|
|
|
|
}
|
|
|
|
|
2021-01-18 01:25:53 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
|
|
pub struct Monochrome {
|
|
|
|
pub bg_palette: BackgroundPalette,
|
|
|
|
}
|
|
|
|
|
2021-03-16 05:27:27 +00:00
|
|
|
bitfield! {
|
|
|
|
pub struct LCDControl(u8);
|
|
|
|
impl Debug;
|
|
|
|
lcd_enabled, set_lcd_enabled: 7;
|
|
|
|
from into TileMapRegister, win_tile_map_area, set_win_tile_map_area: 6;
|
|
|
|
window_enabled, set_window_enabled: 5;
|
|
|
|
from into TileDataRegister, tile_data_area, set_tile_data_area: 4;
|
|
|
|
from into TileMapRegister, gb_tile_map_area, set_gb_tile_map_area: 3;
|
|
|
|
from into OBJSize, obg_size, set_obj_size: 2;
|
|
|
|
obj_enabled, set_obj_enabled: 1;
|
|
|
|
bg_win_enabled, set_bg_win_enabled: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Copy for LCDControl {}
|
|
|
|
impl Clone for LCDControl {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
*self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for LCDControl {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(0)
|
|
|
|
}
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for LCDControl {
|
|
|
|
fn from(byte: u8) -> Self {
|
2021-03-16 05:27:27 +00:00
|
|
|
Self(byte)
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<LCDControl> for u8 {
|
2021-03-16 00:19:40 +00:00
|
|
|
fn from(ctrl: LCDControl) -> Self {
|
2021-03-16 05:27:27 +00:00
|
|
|
ctrl.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum TileMapRegister {
|
|
|
|
X9800 = 0,
|
|
|
|
X9C00 = 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for TileMapRegister {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
|
|
|
0b00 => Self::X9800,
|
|
|
|
0b01 => Self::X9C00,
|
|
|
|
_ => unreachable!("{:#04X} is not a valid value for TileMapRegister", byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TileMapRegister {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::X9800
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum TileDataRegister {
|
|
|
|
X8800 = 0,
|
|
|
|
X8000 = 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for TileDataRegister {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
|
|
|
0b00 => Self::X8800,
|
|
|
|
0b01 => Self::X8000,
|
|
|
|
_ => unreachable!("{:#04X} is not a valid value for TileDataRegister", byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TileDataRegister {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::X8800
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum ObjSize {
|
|
|
|
EightByEight = 0,
|
|
|
|
EightBySixteen = 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for ObjSize {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
|
|
|
0b00 => Self::EightByEight,
|
|
|
|
0b01 => Self::EightBySixteen,
|
|
|
|
_ => unreachable!("{:#04X} is not a valid value for ObjSize", byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ObjSize {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::EightByEight
|
2020-12-24 03:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-18 01:25:53 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum GrayShade {
|
2021-01-19 07:36:44 +00:00
|
|
|
White = 0,
|
|
|
|
LightGray = 1,
|
|
|
|
DarkGray = 2,
|
|
|
|
Black = 3,
|
2021-01-18 01:25:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for GrayShade {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::White
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for GrayShade {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
|
|
|
0b00 => GrayShade::White,
|
|
|
|
0b01 => GrayShade::LightGray,
|
|
|
|
0b10 => GrayShade::DarkGray,
|
|
|
|
0b11 => GrayShade::Black,
|
2021-03-16 04:51:40 +00:00
|
|
|
_ => unreachable!("{:#04X} is not a valid value for GrayShade", byte),
|
2021-01-18 01:25:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
|
|
pub struct BackgroundPalette {
|
2021-03-16 00:19:40 +00:00
|
|
|
colour: GrayShade,
|
|
|
|
colour2: GrayShade,
|
|
|
|
colour3: GrayShade,
|
|
|
|
colour0: GrayShade, // FIXME: Is this supposed to be colour0?
|
2021-01-18 01:25:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for BackgroundPalette {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
Self {
|
2021-03-16 00:19:40 +00:00
|
|
|
colour: (byte >> 6).into(),
|
|
|
|
colour2: ((byte >> 4) & 0x03).into(),
|
|
|
|
colour3: ((byte >> 2) & 0x03).into(),
|
|
|
|
colour0: (byte & 0x03).into(),
|
2021-01-18 01:25:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<BackgroundPalette> for u8 {
|
|
|
|
fn from(palette: BackgroundPalette) -> Self {
|
2021-03-16 00:19:40 +00:00
|
|
|
// FIXME: There is a bug here, see the above FIXME
|
|
|
|
let colour0: u8 = palette.colour0 as u8;
|
|
|
|
let colour1: u8 = palette.colour3 as u8;
|
|
|
|
let colour2: u8 = palette.colour2 as u8;
|
|
|
|
let colour3: u8 = palette.colour0 as u8;
|
2021-01-18 01:25:53 +00:00
|
|
|
|
2021-03-16 00:19:40 +00:00
|
|
|
colour3 << 6 | colour2 << 4 | colour1 << 2 | colour0
|
2021-01-18 01:25:53 +00:00
|
|
|
}
|
|
|
|
}
|