Major work
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 10s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 10s
This commit is contained in:
483
src/ppu.rs
483
src/ppu.rs
@@ -1,3 +1,5 @@
|
||||
use std::fmt;
|
||||
|
||||
use iced::{
|
||||
Point, Size,
|
||||
advanced::graphics::geometry::Renderer,
|
||||
@@ -6,10 +8,10 @@ use iced::{
|
||||
|
||||
use crate::{
|
||||
hex_view::Memory,
|
||||
mem::{MemoryMap, Segment},
|
||||
mem::{Mapper, MemoryMap, Segment},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Color {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
@@ -22,6 +24,13 @@ impl Into<Fill> for Color {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Color {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RenderBuffer<const W: usize, const H: usize> {
|
||||
buffer: Box<[Color]>,
|
||||
}
|
||||
@@ -45,6 +54,22 @@ impl<const W: usize, const H: usize> RenderBuffer<W, H> {
|
||||
pub fn clone_from(&mut self, other: &Self) {
|
||||
self.buffer.copy_from_slice(&other.buffer);
|
||||
}
|
||||
|
||||
pub fn assert_eq(&self, other: &Self) {
|
||||
// if self.buffer != other.buffer {
|
||||
for y in 0..H {
|
||||
for x in 0..W {
|
||||
if self.read(y, x) != other.read(y, x) {
|
||||
panic!(
|
||||
"Rendered Buffers do not match\n Mismatch at ({x}, {y})\n Left: {}\n Right: {}",
|
||||
self.read(y, x),
|
||||
other.read(y, x)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum PPUMMRegisters {
|
||||
@@ -69,37 +94,40 @@ enum BackgroundState {
|
||||
|
||||
pub struct Background {
|
||||
/// Current vram address, 15 bits
|
||||
v: u16,
|
||||
pub v: u16,
|
||||
/// Temp vram address, 15 bits
|
||||
t: u16,
|
||||
pub t: u16,
|
||||
/// Fine X control, 3 bits
|
||||
x: u8,
|
||||
pub x: u8,
|
||||
/// Whether this is the first or second write to PPUSCROLL
|
||||
/// When false, writes to x
|
||||
w: bool,
|
||||
pub w: bool,
|
||||
|
||||
/// When true, v is incremented by 32 after each read
|
||||
vram_column: bool,
|
||||
pub vram_column: bool,
|
||||
pub second_pattern: bool,
|
||||
|
||||
state: BackgroundState,
|
||||
|
||||
cur_nametable: u8,
|
||||
cur_attr: u8,
|
||||
cur_high: u8,
|
||||
cur_low: u8,
|
||||
cur_shift_high: u8,
|
||||
cur_shift_low: u8,
|
||||
pub cur_nametable: u8,
|
||||
pub cur_attr: u8,
|
||||
pub next_attr: u8,
|
||||
pub next_attr_2: u8,
|
||||
pub cur_high: u8,
|
||||
pub cur_low: u8,
|
||||
pub cur_shift_high: u32,
|
||||
pub cur_shift_low: u32,
|
||||
}
|
||||
|
||||
pub struct Mask {
|
||||
grayscale: bool,
|
||||
background_on_left_edge: bool,
|
||||
sprites_on_left_edge: bool,
|
||||
enable_background: bool,
|
||||
enable_sprites: bool,
|
||||
em_red: bool,
|
||||
em_green: bool,
|
||||
em_blue: bool,
|
||||
pub grayscale: bool,
|
||||
pub background_on_left_edge: bool,
|
||||
pub sprites_on_left_edge: bool,
|
||||
pub enable_background: bool,
|
||||
pub enable_sprites: bool,
|
||||
pub em_red: bool,
|
||||
pub em_green: bool,
|
||||
pub em_blue: bool,
|
||||
}
|
||||
|
||||
const COLORS: &'static [Color; 0b100_0000] = &[
|
||||
@@ -431,9 +459,9 @@ pub struct Palette {
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
pub fn color(&self, idx: u8) -> Color {
|
||||
pub fn color(&self, idx: u8, palette: u8) -> Color {
|
||||
debug_assert!(idx < 0x20, "Palette index out of range");
|
||||
self.colors[(self.ram[idx as usize] & 0x3F) as usize]
|
||||
self.colors[(self.ram[idx as usize + palette as usize * 4] & 0x3F) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +469,7 @@ pub struct PPU {
|
||||
// registers: PPURegisters,
|
||||
frame_count: usize,
|
||||
nmi_enabled: bool,
|
||||
nmi_waiting: bool,
|
||||
// nmi_waiting: bool,
|
||||
pub even: bool,
|
||||
pub scanline: usize,
|
||||
pub pixel: usize,
|
||||
@@ -451,7 +479,7 @@ pub struct PPU {
|
||||
|
||||
pub(crate) memory: MemoryMap<PPUMMRegisters>,
|
||||
palette: Palette,
|
||||
background: Background,
|
||||
pub background: Background,
|
||||
oam: OAM,
|
||||
pub render_buffer: RenderBuffer<256, 240>,
|
||||
pub dbg_int: bool,
|
||||
@@ -477,7 +505,7 @@ impl std::fmt::Debug for PPU {
|
||||
}
|
||||
|
||||
impl PPU {
|
||||
pub fn with_chr_rom(rom: &[u8]) -> Self {
|
||||
pub fn with_chr_rom(rom: &[u8], mapper: Mapper) -> Self {
|
||||
Self {
|
||||
cycle: 25,
|
||||
dbg_int: false,
|
||||
@@ -503,24 +531,19 @@ impl PPU {
|
||||
vblank: false,
|
||||
frame_count: 0,
|
||||
nmi_enabled: false,
|
||||
nmi_waiting: false,
|
||||
// nmi_waiting: false,
|
||||
even: false,
|
||||
scanline: 0,
|
||||
pixel: 25,
|
||||
render_buffer: RenderBuffer::empty(),
|
||||
memory: MemoryMap::new(vec![
|
||||
Segment::rom("CHR ROM", 0x0000, rom),
|
||||
Segment::ram("Internal VRAM", 0x2000, 0x1000),
|
||||
Segment::mirror("Mirror of VRAM", 0x3000, 0x0F00, 1),
|
||||
Segment::reg("Palette Control", 0x3F00, 0x0020, PPUMMRegisters::Palette),
|
||||
Segment::mirror("Mirror of Palette", 0x3F20, 0x00E0, 3),
|
||||
]),
|
||||
memory: mapper.ppu_map(rom),
|
||||
background: Background {
|
||||
v: 0,
|
||||
t: 0,
|
||||
x: 0,
|
||||
w: false,
|
||||
vram_column: false,
|
||||
second_pattern: false,
|
||||
state: BackgroundState::NameTableBytePre,
|
||||
cur_high: 0,
|
||||
cur_low: 0,
|
||||
@@ -528,6 +551,8 @@ impl PPU {
|
||||
cur_shift_low: 0,
|
||||
cur_nametable: 0,
|
||||
cur_attr: 0,
|
||||
next_attr: 0,
|
||||
next_attr_2: 0,
|
||||
},
|
||||
oam: OAM {
|
||||
mem: vec![0u8; 256],
|
||||
@@ -538,7 +563,7 @@ impl PPU {
|
||||
pub fn reset(&mut self) {
|
||||
*self = Self {
|
||||
memory: std::mem::replace(&mut self.memory, MemoryMap::new(vec![])),
|
||||
..Self::with_chr_rom(&[])
|
||||
..Self::with_chr_rom(&[], Mapper::from_flags(0))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -561,6 +586,7 @@ impl PPU {
|
||||
5 => panic!("ppuscroll is write-only"),
|
||||
6 => panic!("ppuaddr is write-only"),
|
||||
7 => {
|
||||
// println!("Updating v for ppudata read");
|
||||
let val = self
|
||||
.memory
|
||||
.read(self.background.v)
|
||||
@@ -568,10 +594,7 @@ impl PPU {
|
||||
PPUMMRegisters::Palette => self.palette.ram[off as usize],
|
||||
});
|
||||
// if self.background
|
||||
self.background.v = self
|
||||
.background
|
||||
.v
|
||||
.wrapping_add(if self.background.vram_column { 32 } else { 1 });
|
||||
self.increment_v();
|
||||
val
|
||||
}
|
||||
// 7 => self.registers.data,
|
||||
@@ -584,7 +607,9 @@ impl PPU {
|
||||
self.nmi_enabled = val & 0b1000_0000 != 0;
|
||||
self.background.t =
|
||||
(self.background.t & 0b0001_1000_0000_0000) | (((val & 0b11) as u16) << 10);
|
||||
self.background.vram_column = val & 0b0000_0010 != 0;
|
||||
// self.background.vram_column = val & 0b0000_0010 != 0;
|
||||
self.background.vram_column = val & 0b0000_0100 != 0;
|
||||
self.background.second_pattern = val & 0b0001_0000 != 0;
|
||||
// TODO: other control fields
|
||||
}
|
||||
0x01 => {
|
||||
@@ -628,20 +653,43 @@ impl PPU {
|
||||
if self.background.w {
|
||||
self.background.v =
|
||||
u16::from_le_bytes([val, self.background.v.to_le_bytes()[1]]);
|
||||
self.background.w = true;
|
||||
self.background.w = false;
|
||||
} else {
|
||||
self.background.v =
|
||||
u16::from_le_bytes([self.background.v.to_le_bytes()[0], val & 0b0011_1111]);
|
||||
self.background.w = true;
|
||||
}
|
||||
// println!("Updating v for ppuaddr write: to {:04X}", self.background.v);
|
||||
// self.dbg_int = true;
|
||||
// todo!("PPUADDR write")
|
||||
}
|
||||
0x07 => {
|
||||
// TODO: ppu data
|
||||
// println!("Writing: {:02X}, @{:04X}", val, self.background.v);
|
||||
self.memory
|
||||
.write(self.background.v, val, |r, o, v| match r {
|
||||
PPUMMRegisters::Palette => {
|
||||
// println!("Writing {:02X} to {:02X}", v & 0x3F, o);
|
||||
self.palette.ram[o as usize] = v & 0x3F;
|
||||
}
|
||||
});
|
||||
self.increment_v();
|
||||
// self.background.v += 1; // TODO: implement inc behavior
|
||||
}
|
||||
_ => panic!("No register at {:02X}", offset),
|
||||
}
|
||||
// TODO: use data in PPU
|
||||
}
|
||||
/// Apply either row wise or column wise increment
|
||||
fn increment_v(&mut self) {
|
||||
if self.background.vram_column {
|
||||
// if self.background.v
|
||||
self.background.v += 32;
|
||||
} else {
|
||||
self.background.v += 1;
|
||||
}
|
||||
// self.background.v = self
|
||||
// .background
|
||||
// .v
|
||||
// .wrapping_add(if self.background.vram_column { 32 } else { 1 });
|
||||
}
|
||||
// pub fn write_oamdma(&mut self, val: u8) {
|
||||
// // TODO: OAM high addr
|
||||
@@ -662,90 +710,140 @@ impl PPU {
|
||||
self.scanline += 1;
|
||||
}
|
||||
if self.mask.enable_background || self.mask.enable_sprites {
|
||||
if self.pixel == 257 {
|
||||
self.background.v = (self.background.v & 0b0111_1011_1110_0000)
|
||||
| (self.background.t & 0b0000_0100_0001_1111);
|
||||
}
|
||||
if self.pixel == 280 && self.scanline == 260 {
|
||||
self.background.v = (self.background.v & 0b0000_0100_0001_1111)
|
||||
| (self.background.t & 0b0111_1011_1110_0000);
|
||||
}
|
||||
if self.scanline < 240 || self.scanline == 261 {
|
||||
if self.pixel == 257 {
|
||||
self.background.v = (self.background.v & 0b0111_1011_1110_0000)
|
||||
| (self.background.t & 0b0000_0100_0001_1111);
|
||||
}
|
||||
if self.pixel >= 280 && self.pixel <= 304 && self.scanline == 261 {
|
||||
self.background.v = (self.background.v & 0b0000_0100_0001_1111)
|
||||
| (self.background.t & 0b0111_1011_1110_0000);
|
||||
}
|
||||
if self.pixel > 280 && self.pixel < 320 {
|
||||
self.background.cur_shift_high <<= 1;
|
||||
self.background.cur_shift_low <<= 1;
|
||||
}
|
||||
// TODO
|
||||
if self.pixel == 0 {
|
||||
// self.dbg_int = true;
|
||||
// idle cycle
|
||||
} else if self.pixel < 257 {
|
||||
} else if self.pixel < 257 || self.pixel > 320 {
|
||||
// self.dbg_int = true;
|
||||
if self.scanline < 240 {
|
||||
// Determine background color
|
||||
let a = self.background.cur_shift_high & 0x80;
|
||||
let b = self.background.cur_shift_low & 0x80;
|
||||
let val = (a >> 6) | (b >> 7);
|
||||
debug_assert!(val < 4);
|
||||
const POS: u32 = 1 << 15;
|
||||
// Determine background color
|
||||
let a = self.background.cur_shift_high & POS;
|
||||
let b = self.background.cur_shift_low & POS;
|
||||
let val = (a >> 14) | (b >> 15);
|
||||
debug_assert!(val < 4);
|
||||
|
||||
let h_off = ((self.pixel - 1) / 16) % 2;
|
||||
let v_off = (self.scanline / 16) % 2;
|
||||
let off = v_off * 4 + h_off * 2;
|
||||
let palette = (self.background.cur_attr >> off) & 0x3;
|
||||
|
||||
if self.scanline < 240 && self.pixel < 257 {
|
||||
// Write to screen
|
||||
self.render_buffer.write(
|
||||
self.scanline,
|
||||
self.pixel - 1,
|
||||
self.palette.color(val)
|
||||
// self.palette.colors[val as usize],
|
||||
self.palette
|
||||
.color(val as u8, if val != 0 { palette } else { 0 }), // self.palette.colors[val as usize],
|
||||
); // TODO: this should come from shift registers
|
||||
}
|
||||
if self.pixel < 337 {
|
||||
self.background.cur_shift_high <<= 1;
|
||||
self.background.cur_shift_low <<= 1;
|
||||
}
|
||||
self.background.state = match self.background.state {
|
||||
BackgroundState::NameTableByte => {
|
||||
// TODO: Fetch name table byte
|
||||
let addr = 0x2000 + self.pixel / 8 + self.scanline / 8;
|
||||
let val = self.memory.read(addr as u16).reg_map(|_, _| todo!());
|
||||
self.background.cur_nametable = val;
|
||||
// self.background.v;
|
||||
BackgroundState::AttrTableBytePre
|
||||
|
||||
if self.scanline < 240 || self.scanline == 261 {
|
||||
if self.pixel <= 260 || self.pixel >= 321 {
|
||||
if self.pixel % 8 == 2 {
|
||||
// Name table fetch
|
||||
// let addr = 0x2000 + self.pixel / 8 + self.scanline / 8;
|
||||
let addr = 0x2000 | (self.background.v & 0x0FFF);
|
||||
// println!("Cur: {:04X}, comp: {:04X}", addr, addr_2);
|
||||
let val = self.memory.read(addr).reg_map(|_, _| 0);
|
||||
self.background.cur_nametable = val;
|
||||
} else if self.pixel % 8 == 4 {
|
||||
// Attr table fetch
|
||||
// let addr = 0x23C0 + self.pixel / 16 + self.scanline / 16;
|
||||
let addr = 0x23C0
|
||||
| (self.background.v & 0x0C00)
|
||||
| ((self.background.v >> 4) & 0x38)
|
||||
| ((self.background.v >> 2) & 0x07);
|
||||
// println!("Cur: {:04X}, comp: {:04X}", addr, addr_2);
|
||||
// assert_eq!(addr, addr_2);
|
||||
let val = self.memory.read(addr).reg_map(|_, _| 0); // TODO: handle reg reads
|
||||
self.background.next_attr_2 = val;
|
||||
} else if self.pixel % 8 == 6 {
|
||||
// BG pattern low
|
||||
let addr = self.background.cur_nametable as u16 * 16
|
||||
+ ((self.background.v & 0x7000) >> 12)
|
||||
// + (self.scanline % 8) as u16
|
||||
+ if self.background.second_pattern {
|
||||
0x1000
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let val = self.memory.read(addr).reg_map(|_, _| 0);
|
||||
self.background.cur_low = val;
|
||||
} else if self.pixel % 8 == 0 && self.pixel > 0 {
|
||||
let addr = self.background.cur_nametable as u16 * 16
|
||||
+ 8
|
||||
+ ((self.background.v & 0x7000) >> 12)
|
||||
// (self.scanline % 8) as u16
|
||||
+ if self.background.second_pattern {
|
||||
0x1000
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let val = self.memory.read(addr).reg_map(|_, _| todo!());
|
||||
self.background.cur_high = val;
|
||||
self.background.cur_shift_low |= self.background.cur_low as u32;
|
||||
self.background.cur_shift_high |= self.background.cur_high as u32;
|
||||
self.background.cur_attr = self.background.next_attr;
|
||||
self.background.next_attr = self.background.next_attr_2;
|
||||
// Inc horizontal
|
||||
if self.background.v & 0x1F == 31 {
|
||||
self.background.v = (self.background.v & !0x1F) ^ 0x400;
|
||||
} else {
|
||||
self.background.v += 1;
|
||||
}
|
||||
// Inc vertical
|
||||
if self.pixel == 256 {
|
||||
// && self.scanline % 4 == 0
|
||||
if self.background.v & 0x7000 != 0x7000 {
|
||||
self.background.v += 0x1000;
|
||||
} else {
|
||||
self.background.v &= !0x7000;
|
||||
let mut y = (self.background.v & 0x03E0) >> 5;
|
||||
if y == 29 {
|
||||
y = 0;
|
||||
self.background.v ^= 0x0800
|
||||
} else if y == 31 {
|
||||
y = 0;
|
||||
} else {
|
||||
y += 1;
|
||||
}
|
||||
self.background.v =
|
||||
(self.background.v & !0x03E0) | (y << 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BackgroundState::AttrTableByte => {
|
||||
// TODO: Fetch attr table byte
|
||||
// let addr = 0x2000 + self.pixel / 8 + self.scanline / 8;
|
||||
// let val = self.memory.read(addr as u16).reg_map(|_, _| todo!());
|
||||
// self.background.cur_attr = val;
|
||||
BackgroundState::PatternTableTileLowPre
|
||||
}
|
||||
BackgroundState::PatternTableTileLow => {
|
||||
// TODO: Fetch
|
||||
let addr = self.background.cur_nametable as u16 * 16
|
||||
+ (self.scanline % 8) as u16;
|
||||
let val = self.memory.read(addr).reg_map(|_, _| todo!());
|
||||
self.background.cur_low = val;
|
||||
BackgroundState::PatternTableTileHighPre
|
||||
}
|
||||
BackgroundState::PatternTableTileHigh => {
|
||||
// TODO: Fetch
|
||||
let addr = self.background.cur_nametable as u16 * 16
|
||||
+ 8
|
||||
+ (self.scanline % 8) as u16;
|
||||
let val = self.memory.read(addr).reg_map(|_, _| todo!());
|
||||
self.background.cur_high = val;
|
||||
self.background.cur_shift_low = self.background.cur_low;
|
||||
self.background.cur_shift_high = self.background.cur_high;
|
||||
BackgroundState::NameTableBytePre
|
||||
}
|
||||
BackgroundState::NameTableBytePre => BackgroundState::NameTableByte,
|
||||
BackgroundState::AttrTableBytePre => BackgroundState::AttrTableByte,
|
||||
BackgroundState::PatternTableTileLowPre => {
|
||||
BackgroundState::PatternTableTileLow
|
||||
}
|
||||
BackgroundState::PatternTableTileHighPre => {
|
||||
BackgroundState::PatternTableTileHigh
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// TODO: Sprite fetches
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.scanline == 261 && self.pixel == 1 {
|
||||
self.vblank = false;
|
||||
// TODO: clear sprite 0 & sprite overflow
|
||||
}
|
||||
if self.scanline == 241 && self.pixel == 1 {
|
||||
self.vblank = true;
|
||||
self.nmi_waiting = self.nmi_enabled;
|
||||
// self.nmi_waiting = self.nmi_enabled;
|
||||
self.frame_count += 1;
|
||||
self.background.state = BackgroundState::NameTableBytePre;
|
||||
return true;
|
||||
@@ -753,52 +851,80 @@ impl PPU {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn nmi_on_vblank(&self) -> bool {
|
||||
self.nmi_enabled
|
||||
}
|
||||
pub fn peek_nmi(&self) -> bool {
|
||||
self.nmi_waiting
|
||||
self.vblank && self.nmi_enabled
|
||||
}
|
||||
pub fn nmi_waiting(&mut self) -> bool {
|
||||
if self.nmi_waiting {
|
||||
self.nmi_waiting = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
self.vblank && self.nmi_enabled
|
||||
// if self.nmi_waiting {
|
||||
// self.nmi_waiting = false;
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
pub fn peek_irq(&self) -> bool {
|
||||
self.nmi_waiting
|
||||
false
|
||||
}
|
||||
pub fn irq_waiting(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn render_name_table<R: Renderer>(&self, frame: &mut Frame<R>) {
|
||||
for y in 0..280 / 8 {
|
||||
for x in 0..512 / 8 {
|
||||
let name = self.memory.peek(x + y * 512 / 8 + 0x2000).unwrap() as u16 * 16;
|
||||
for y in 0..60 {
|
||||
for x in 0..64 {
|
||||
let row = y % 30;
|
||||
let col = x % 32;
|
||||
let off = 0x2000 + 0x400 * (y / 30 * 2 + x / 32);
|
||||
let name = self.memory.peek((off + col + row * 32) as u16).unwrap() as u16 * 16
|
||||
+ if self.background.second_pattern {
|
||||
0x1000
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let attr = self
|
||||
.memory
|
||||
.peek((col / 4 + row / 4 * 8 + 0x3C0 + off) as u16)
|
||||
.unwrap();
|
||||
// attr << (((col & 1) << 1) | ((row & 1) << 0)) * 2
|
||||
// let h_off = ((self.pixel - 1) / 16) % 2;
|
||||
// let v_off = (self.scanline / 16) % 2;
|
||||
// let off = v_off * 4 + h_off * 2;
|
||||
let palette = (attr >> (((col & 1) << 1) | ((row & 1) << 2))) & 0x3;
|
||||
for y_off in 0..8 {
|
||||
let low = self.memory.peek(name + y_off).unwrap();
|
||||
let high = self.memory.peek(name + y_off + 8).unwrap();
|
||||
for bit in (0..8).rev() {
|
||||
for bit in 0..8 {
|
||||
frame.fill_rectangle(
|
||||
Point::new(x as f32 * 8. + bit as f32, y as f32 * 8. + y_off as f32),
|
||||
Point::new(
|
||||
x as f32 * 8. + 8. - bit as f32,
|
||||
y as f32 * 8. + y_off as f32,
|
||||
),
|
||||
Size::new(1., 1.),
|
||||
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
||||
(false, false) => Color { r: 0, g: 0, b: 0 },
|
||||
(true, false) => Color {
|
||||
r: 64,
|
||||
g: 64,
|
||||
b: 64,
|
||||
},
|
||||
(false, true) => Color {
|
||||
r: 128,
|
||||
g: 128,
|
||||
b: 128,
|
||||
},
|
||||
(true, true) => Color {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
},
|
||||
(false, false) => self.palette.color(0, 0),
|
||||
(true, false) => self.palette.color(1, palette),
|
||||
(false, true) => self.palette.color(2, palette),
|
||||
(true, true) => self.palette.color(3, palette),
|
||||
// (false, false) => Color { r: 0, g: 0, b: 0 },
|
||||
// (true, false) => Color {
|
||||
// r: 64,
|
||||
// g: 64,
|
||||
// b: 64,
|
||||
// },
|
||||
// (false, true) => Color {
|
||||
// r: 128,
|
||||
// g: 128,
|
||||
// b: 128,
|
||||
// },
|
||||
// (true, true) => Color {
|
||||
// r: 255,
|
||||
// g: 255,
|
||||
// b: 255,
|
||||
// },
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -808,6 +934,61 @@ impl PPU {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn name_cursor_info(&self, cursor: Point) -> Option<String> {
|
||||
let x = (cursor.x / 8.) as usize;
|
||||
let y = (cursor.y / 8.) as usize;
|
||||
if x < 64 && y < 60 {
|
||||
let row = y % 30;
|
||||
let col = x % 32;
|
||||
let off = 0x2000 + 0x400 * (y / 30 * 2 + x / 32);
|
||||
let name = self.memory.peek((off + col + row * 32) as u16).unwrap() as usize;
|
||||
let attr = self
|
||||
.memory
|
||||
.peek((col / 4 + row / 4 * 8 + 0x3C0 + off) as u16)
|
||||
.unwrap();
|
||||
Some(format!(
|
||||
"Row, Column: {}, {}
|
||||
X, Y: {}, {}
|
||||
Tilemap address: ${:04X}
|
||||
|
||||
Tile Index: ${:02X}
|
||||
Tile Address (PPU): ${:04X}
|
||||
Tile Address (CHR): ${:04X}
|
||||
|
||||
Palette Index {}
|
||||
Palette Address ${:04X}
|
||||
|
||||
Attribute Address ${:04X}
|
||||
Attribute data: ${:02X}
|
||||
",
|
||||
row,
|
||||
col,
|
||||
col * 8,
|
||||
row * 8,
|
||||
off + col + row * 32,
|
||||
name,
|
||||
name * 16
|
||||
+ if self.background.second_pattern {
|
||||
0x1000
|
||||
} else {
|
||||
0
|
||||
},
|
||||
name * 16
|
||||
+ if self.background.second_pattern {
|
||||
0x1000
|
||||
} else {
|
||||
0
|
||||
},
|
||||
(attr >> (((col & 1) << 1) | ((row & 1) << 2))) & 0x3,
|
||||
((attr >> (((col & 1) << 1) | ((row & 1) << 2))) & 0x3) as usize * 4 + 0x3F00,
|
||||
col / 4 + row / 4 * 8 + 0x3C0 + off,
|
||||
attr,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_pattern_tables<R: Renderer>(&self, frame: &mut Frame<R>) {
|
||||
for y in 0..16 {
|
||||
for x in 0..16 {
|
||||
@@ -817,7 +998,7 @@ impl PPU {
|
||||
let high = self.memory.peek(name + y_off + 8).unwrap();
|
||||
for bit in 0..8 {
|
||||
frame.fill_rectangle(
|
||||
Point::new(
|
||||
Point::new(
|
||||
x as f32 * 8. + 8. - bit as f32,
|
||||
y as f32 * 8. + y_off as f32,
|
||||
),
|
||||
@@ -885,6 +1066,20 @@ impl PPU {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn pattern_cursor_info(&self, cursor: Point) -> Option<String> {
|
||||
let x = (cursor.x / 8.) as usize;
|
||||
let y = (cursor.y / 8.) as usize;
|
||||
if x < 16 && y < 32 {
|
||||
Some(format!(
|
||||
"Tile address (PPU): {:04X}\nTile address (CHR): {:04X}\nIndex: {:02X}",
|
||||
(y * 16 + x) * 16,
|
||||
(y * 16 + x) * 16,
|
||||
((y % 16) * 16 + x),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_palette<R: Renderer>(&self, frame: &mut Frame<R>) {
|
||||
for y in 0..8 {
|
||||
@@ -897,6 +1092,24 @@ impl PPU {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn palette_cursor_info(&self, cursor: Point) -> Option<String> {
|
||||
let x = (cursor.x / 10.) as usize;
|
||||
let y = (cursor.y / 10.) as usize;
|
||||
if x < 4 && y < 8 {
|
||||
Some(format!(
|
||||
"Index: {:02X}\nValue: {:02X}\nColor code: {}",
|
||||
x + y * 4,
|
||||
self.palette.ram[x + y * 4] & 0x3F,
|
||||
self.palette.colors[(self.palette.ram[x + y * 4] & 0x3F) as usize],
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mem(&self) -> &impl Memory {
|
||||
&self.memory
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -905,7 +1118,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn ppu_registers() {
|
||||
let mut ppu = PPU::with_chr_rom(&[0u8; 8192]);
|
||||
let mut ppu = PPU::with_chr_rom(&[0u8; 8192], Mapper::from_flags(0));
|
||||
assert_eq!(ppu.background.v, 0);
|
||||
assert_eq!(ppu.background.t, 0);
|
||||
assert_eq!(ppu.background.x, 0);
|
||||
|
||||
Reference in New Issue
Block a user