Working Sprite implementation
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 9s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 9s
This commit is contained in:
@@ -35,7 +35,10 @@ use tracing_subscriber::EnvFilter;
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "int_nmi_exit_timing.nes");
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "render-updating.nes");
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_palette_shared.nes");
|
||||
const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "input_test.nes");
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "scrolling.nes");
|
||||
const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "sprites.nes");
|
||||
// const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||
// const ROM_FILE: &str = "./cpu_timing_test.nes";
|
||||
// const ROM_FILE: &str = "../nes-test-roms/instr_test-v5/official_only.nes";
|
||||
|
||||
@@ -178,7 +181,7 @@ impl Emulator {
|
||||
nes,
|
||||
windows: HashMap::from_iter([
|
||||
(win, WindowType::Main),
|
||||
(win_2, WindowType::Memory(MemoryTy::OAM, HexView {}))
|
||||
(win_2, WindowType::Debugger)
|
||||
]),
|
||||
debugger: DebuggerState::new(),
|
||||
main_win_size: Size::new(0., 0.),
|
||||
|
||||
268
src/ppu.rs
268
src/ppu.rs
@@ -88,11 +88,205 @@ pub enum PPUMMRegisters {
|
||||
Palette,
|
||||
}
|
||||
|
||||
bitfield::bitfield! {
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct SpriteAttrs(u8);
|
||||
impl Debug;
|
||||
vflip, set_vflip: 7;
|
||||
hflip, set_hflip: 6;
|
||||
priority, set_priority: 5;
|
||||
palette, set_palette: 1, 0;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct SpriteOutputUnit {
|
||||
high: u8,
|
||||
low: u8,
|
||||
x_pos: u8,
|
||||
y_pos: u8,
|
||||
tile: u8,
|
||||
attrs: SpriteAttrs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum OamState {
|
||||
ReadY,
|
||||
ReadTile,
|
||||
ReadAttrs,
|
||||
ReadX,
|
||||
OverflowScan,
|
||||
Wait,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OAM {
|
||||
mem: Vec<u8>,
|
||||
secondary: Vec<u8>,
|
||||
addr: u8,
|
||||
count: u8,
|
||||
state: OamState,
|
||||
oam_read_buffer: u8,
|
||||
edit_ver: usize,
|
||||
sprite_output_units: Vec<SpriteOutputUnit>,
|
||||
overflow: bool,
|
||||
sprite_offset_0x1000: bool,
|
||||
}
|
||||
|
||||
impl OAM {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
mem: vec![0u8; 256],
|
||||
addr: 0,
|
||||
count: 0,
|
||||
edit_ver: 0,
|
||||
secondary: vec![0xFF; 32],
|
||||
sprite_output_units: vec![SpriteOutputUnit::default(); 8],
|
||||
state: OamState::ReadY,
|
||||
oam_read_buffer: 0,
|
||||
overflow: false,
|
||||
sprite_offset_0x1000: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn ppu_cycle(&mut self, pixel: usize, line: usize, mem: &mut PpuMem<'_>) {
|
||||
if pixel < 64 {
|
||||
} else if pixel == 64 {
|
||||
self.secondary.fill(0xFF);
|
||||
self.state = OamState::ReadY;
|
||||
self.addr = 0;
|
||||
self.count = 0;
|
||||
} else if pixel <= 256 {
|
||||
// Fetch/evalute sprites for next line
|
||||
if pixel % 2 == 1 && !matches!(self.state, OamState::Wait) {
|
||||
self.oam_read_buffer = self.mem[self.addr as usize];
|
||||
if self.addr == 255 {
|
||||
self.addr = 0;
|
||||
} else {
|
||||
self.addr += 1;
|
||||
}
|
||||
} else {
|
||||
match self.state {
|
||||
OamState::ReadY => {
|
||||
let l = self.oam_read_buffer as usize;
|
||||
if self.count < 8 {
|
||||
if l <= line && line < l + 8 {
|
||||
self.secondary[self.count as usize * 4] = self.oam_read_buffer;
|
||||
self.state = OamState::ReadTile;
|
||||
} else {
|
||||
if self.addr < 0xFD {
|
||||
self.addr += 3;
|
||||
} else {
|
||||
self.state = OamState::Wait; // Should be Overflow scan...
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.state = OamState::Wait; // Should be Overflow scan...
|
||||
}
|
||||
}
|
||||
OamState::ReadTile => {
|
||||
self.secondary[self.count as usize * 4 + 1] = self.oam_read_buffer;
|
||||
self.state = OamState::ReadAttrs;
|
||||
}
|
||||
OamState::ReadAttrs => {
|
||||
self.secondary[self.count as usize * 4 + 2] = self.oam_read_buffer;
|
||||
self.state = OamState::ReadX;
|
||||
}
|
||||
OamState::ReadX => {
|
||||
self.secondary[self.count as usize * 4 + 3] = self.oam_read_buffer;
|
||||
self.state = OamState::ReadY;
|
||||
self.count += 1;
|
||||
if self.count == 8 {
|
||||
self.state = OamState::Wait; // Should be Overflow scan...
|
||||
}
|
||||
}
|
||||
OamState::OverflowScan => todo!(),
|
||||
OamState::Wait => (),
|
||||
}
|
||||
}
|
||||
} else if pixel <= 320 {
|
||||
if pixel == 257 {
|
||||
// Reset output units
|
||||
self.sprite_output_units.fill(SpriteOutputUnit::default());
|
||||
}
|
||||
let run = pixel - 257;
|
||||
if run / 8 < self.count as usize {
|
||||
if run % 8 == 0 {
|
||||
self.sprite_output_units[run / 8].y_pos = self.secondary[(run / 8) * 4];
|
||||
} else if run % 8 == 1 {
|
||||
self.sprite_output_units[run / 8].tile = self.secondary[(run / 8) * 4 + 1];
|
||||
} else if run % 8 == 2 {
|
||||
self.sprite_output_units[run / 8].attrs.0 = self.secondary[(run / 8) * 4 + 2];
|
||||
} else if run % 8 == 3 {
|
||||
self.sprite_output_units[run / 8].x_pos = self.secondary[(run / 8) * 4 + 3];
|
||||
} else if run % 8 == 4 {
|
||||
} else if run % 8 == 5 {
|
||||
let off = line - self.sprite_output_units[run / 8].y_pos as usize;
|
||||
let off = if self.sprite_output_units[run / 8].attrs.vflip() {
|
||||
7 - off
|
||||
} else {
|
||||
off
|
||||
};
|
||||
let addr = if self.sprite_offset_0x1000 {
|
||||
0x1000
|
||||
} else {
|
||||
0x0000
|
||||
} + 16 * self.sprite_output_units[run / 8].tile as u16
|
||||
+ off as u16;
|
||||
self.sprite_output_units[run / 8].low = mem.read(addr).reg_map(|_, _| todo!());
|
||||
} else if run % 8 == 6 {
|
||||
} else if run % 8 == 7 {
|
||||
let off = line - self.sprite_output_units[run / 8].y_pos as usize;
|
||||
let off = if self.sprite_output_units[run / 8].attrs.vflip() {
|
||||
7 - off
|
||||
} else {
|
||||
off
|
||||
};
|
||||
let addr = if self.sprite_offset_0x1000 {
|
||||
0x1000
|
||||
} else {
|
||||
0x0000
|
||||
} + 16 * self.sprite_output_units[run / 8].tile as u16
|
||||
+ off as u16
|
||||
+ 8;
|
||||
self.sprite_output_units[run / 8].high = mem.read(addr).reg_map(|_, _| todo!());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Some((palette index, above background))
|
||||
fn pixel(&mut self, pixel: usize, _line: usize) -> Option<(u8, bool, bool)> {
|
||||
self.sprite_output_units
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, s)| {
|
||||
if pixel >= s.x_pos as usize && pixel < s.x_pos as usize + 8 && s.x_pos < 0xFF {
|
||||
let bit_pos = pixel - s.x_pos as usize;
|
||||
let bit_pos = if s.attrs.hflip() {
|
||||
bit_pos
|
||||
} else {
|
||||
7 - bit_pos
|
||||
};
|
||||
// println!("Shifting: 0b{:08b} by {}", s.high, 8 - bit_pos);
|
||||
let hi = (s.high >> bit_pos) & 1;
|
||||
// println!("Shifting: 0b{:08b} by {}", s.low, bit_pos);
|
||||
let lo = (s.low >> bit_pos) & 1;
|
||||
let idx = (hi << 1) | lo;
|
||||
Some((
|
||||
if idx != 0 {
|
||||
idx + s.attrs.palette() * 4 + 0x10
|
||||
} else {
|
||||
0
|
||||
},
|
||||
!s.attrs.priority(),
|
||||
i == 0,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -463,9 +657,9 @@ pub struct Palette {
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
pub fn color(&self, idx: u8, palette: u8) -> Color {
|
||||
pub fn color(&self, idx: u8) -> Color {
|
||||
debug_assert!(idx < 0x20, "Palette index out of range");
|
||||
self.colors[(self.ram[idx as usize + palette as usize * 4] & 0x3F) as usize]
|
||||
self.colors[(self.ram[idx as usize] & 0x3F) as usize]
|
||||
}
|
||||
|
||||
pub fn ram(&self, offset: u8) -> u8 {
|
||||
@@ -487,6 +681,7 @@ pub struct PPU {
|
||||
|
||||
pub mask: Mask,
|
||||
pub vblank: bool,
|
||||
sprite_zero_hit: bool,
|
||||
|
||||
pub palette: Palette,
|
||||
pub background: Background,
|
||||
@@ -542,6 +737,7 @@ impl PPU {
|
||||
],
|
||||
},
|
||||
vblank: false,
|
||||
sprite_zero_hit: false,
|
||||
frame_count: 0,
|
||||
nmi_enabled: false,
|
||||
// nmi_waiting: false,
|
||||
@@ -566,11 +762,7 @@ impl PPU {
|
||||
next_attr: 0,
|
||||
next_attr_2: 0,
|
||||
},
|
||||
oam: OAM {
|
||||
mem: vec![0u8; 256],
|
||||
addr: 0,
|
||||
edit_ver: 0,
|
||||
},
|
||||
oam: OAM::new(),
|
||||
vram_buffer: 0,
|
||||
}
|
||||
}
|
||||
@@ -587,7 +779,9 @@ impl PPU {
|
||||
0 => panic!("ppuctrl is write-only"),
|
||||
1 => panic!("ppumask is write-only"),
|
||||
2 => {
|
||||
let tmp = if self.vblank { 0b1000_0000 } else { 0 };
|
||||
let tmp = if self.vblank { 0b1000_0000 } else { 0 }
|
||||
| if self.sprite_zero_hit { 0b0100_0000 } else { 0 }
|
||||
| if self.oam.overflow { 0b0010_0000 } else { 0 };
|
||||
self.vblank = false;
|
||||
self.background.w = false;
|
||||
tmp
|
||||
@@ -605,10 +799,11 @@ impl PPU {
|
||||
let val = self.vram_buffer;
|
||||
self.vram_buffer = v;
|
||||
val
|
||||
},
|
||||
Value::Register { reg: PPUMMRegisters::Palette, offset } => {
|
||||
self.palette.ram[offset as usize]
|
||||
},
|
||||
}
|
||||
Value::Register {
|
||||
reg: PPUMMRegisters::Palette,
|
||||
offset,
|
||||
} => self.palette.ram[offset as usize],
|
||||
};
|
||||
// .reg_map(|a, off| match a {
|
||||
// PPUMMRegisters::Palette => self.palette.ram[off as usize],
|
||||
@@ -626,9 +821,10 @@ impl PPU {
|
||||
0x00 => {
|
||||
self.nmi_enabled = val & 0b1000_0000 != 0;
|
||||
self.background.t =
|
||||
(self.background.t & 0b0001_1000_0000_0000) | (((val & 0b11) as u16) << 10);
|
||||
(self.background.t & !0b0001_1000_0000_0000) | (((val & 0b11) as u16) << 10);
|
||||
self.background.vram_column = val & 0b0000_0100 != 0;
|
||||
self.background.second_pattern = val & 0b0001_0000 != 0;
|
||||
self.oam.sprite_offset_0x1000 = val & 0b0000_1000 != 0;
|
||||
// TODO: other control fields
|
||||
}
|
||||
0x01 => {
|
||||
@@ -752,31 +948,54 @@ impl PPU {
|
||||
self.background.cur_shift_high <<= 1;
|
||||
self.background.cur_shift_low <<= 1;
|
||||
}
|
||||
if self.scanline != 261 {
|
||||
self.oam.ppu_cycle(self.pixel, self.scanline, mem);
|
||||
}
|
||||
// TODO
|
||||
if self.pixel == 0 {
|
||||
if self.scanline < 9 {
|
||||
// dbg!((self.scanline, &self.oam.sprite_output_units[0]));
|
||||
// dbg!(&self.oam.secondary);
|
||||
}
|
||||
// self.dbg_int = true;
|
||||
// idle cycle
|
||||
} else if self.pixel < 257 || self.pixel > 320 {
|
||||
// self.dbg_int = true;
|
||||
const POS: u32 = 1 << 15;
|
||||
// const POS: u32 = 1 << 15;
|
||||
let bit_pos = 15 - self.background.x;
|
||||
// let pos: u32 = 1 << (15 + self.background.x*0); // TODO: handle this correctly
|
||||
// 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);
|
||||
let a = (self.background.cur_shift_high >> bit_pos) & 1;
|
||||
let b = (self.background.cur_shift_low >> bit_pos) & 1;
|
||||
let val = (a << 1) | b;
|
||||
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;
|
||||
let color = val as u8 + if val != 0 { palette * 4 } else { 0 };
|
||||
|
||||
if self.scanline < 240 && self.pixel < 257 {
|
||||
let color = if let Some((sp_color, above, is_sprite_zero)) =
|
||||
self.oam.pixel(self.pixel, self.scanline)
|
||||
{
|
||||
if is_sprite_zero && sp_color != 0 && color != 0 {
|
||||
self.sprite_zero_hit = true;
|
||||
}
|
||||
if sp_color == 0 || (color != 0 && !above) {
|
||||
color
|
||||
} else {
|
||||
sp_color
|
||||
}
|
||||
} else {
|
||||
color
|
||||
};
|
||||
// Write to screen
|
||||
self.render_buffer.write(
|
||||
self.scanline,
|
||||
self.pixel - 1,
|
||||
self.palette
|
||||
.color(val as u8, if val != 0 { palette } else { 0 }), // self.palette.colors[val as usize],
|
||||
self.palette.color(color), // self.palette.colors[val as usize],
|
||||
); // TODO: this should come from shift registers
|
||||
}
|
||||
if self.pixel < 337 {
|
||||
@@ -868,7 +1087,8 @@ impl PPU {
|
||||
}
|
||||
if self.scanline == 261 && self.pixel == 1 {
|
||||
self.vblank = false;
|
||||
// TODO: clear sprite 0 & sprite overflow
|
||||
self.sprite_zero_hit = false;
|
||||
self.oam.overflow = false;
|
||||
}
|
||||
if self.scanline == 241 && self.pixel == 1 {
|
||||
self.vblank = true;
|
||||
@@ -930,10 +1150,10 @@ impl PPU {
|
||||
),
|
||||
Size::new(1., 1.),
|
||||
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
||||
(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) => self.palette.color(0),
|
||||
(true, false) => self.palette.color(1 + 4 * palette),
|
||||
(false, true) => self.palette.color(2 + 4 * palette),
|
||||
(true, true) => self.palette.color(3 + 4 * palette),
|
||||
// (false, false) => Color { r: 0, g: 0, b: 0 },
|
||||
// (true, false) => Color {
|
||||
// r: 64,
|
||||
|
||||
335
src/test_roms/input_test.s
Normal file
335
src/test_roms/input_test.s
Normal file
@@ -0,0 +1,335 @@
|
||||
|
||||
; Verifies that reset doesn't alter any RAM.
|
||||
|
||||
.include "testing.s"
|
||||
; patterns_bin "pat.bin"
|
||||
.macro chr b,s
|
||||
.repeat s
|
||||
.byte b
|
||||
.endrepeat
|
||||
.endmacro
|
||||
.pushseg
|
||||
.segment "CHARS"
|
||||
T_EMPTY = 0
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.endrepeat
|
||||
|
||||
T_TOP_LEFT = 1
|
||||
.repeat 2 ; Top Left 1
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_TOP_RIGHT = 2
|
||||
.repeat 2 ; Top Right 2
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
T_BOTTOM_RIGHT = 3
|
||||
.repeat 2 ; Bottom Right 3
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_BOTTOM_LEFT = 4
|
||||
.repeat 2 ; Bottom Left 4
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_FORWARD = 5
|
||||
.repeat 2 ; FW 5
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %00001110
|
||||
.byte %00011100
|
||||
.byte %00111000
|
||||
.byte %01110000
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_BACKWARD = 6
|
||||
.repeat 2 ; BW 6
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01110000
|
||||
.byte %00111000
|
||||
.byte %00011100
|
||||
.byte %00001110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
|
||||
T_PILL_L = 7
|
||||
.repeat 2 ; Pill L 7
|
||||
.byte %00111111
|
||||
.byte %01111111
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01111111
|
||||
.byte %00111111
|
||||
.endrepeat
|
||||
T_PILL_R = 8
|
||||
.repeat 2 ; Pill R 8
|
||||
.byte %11111100
|
||||
.byte %11111110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %11111110
|
||||
.byte %11111100
|
||||
.endrepeat
|
||||
|
||||
.popseg
|
||||
|
||||
zp_res TEMP
|
||||
|
||||
.macro load_ppu_addr addr
|
||||
lda #.hibyte(addr)
|
||||
sta PPUADDR
|
||||
lda #.lobyte(addr)
|
||||
sta PPUADDR ; Load $2000
|
||||
.endmacro
|
||||
|
||||
reset:
|
||||
sei
|
||||
cld
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
; Init PPU
|
||||
bit PPUSTATUS
|
||||
vwait1:
|
||||
bit PPUSTATUS
|
||||
bpl vwait1
|
||||
vwait2:
|
||||
bit PPUSTATUS
|
||||
bpl vwait2
|
||||
|
||||
lda #$00
|
||||
sta PPUCTRL ; NMI off, PPU slave, Small sprites, 0 addresses
|
||||
|
||||
; Fill NT0
|
||||
load_ppu_addr $2000
|
||||
ldx #$00
|
||||
ldy #$04
|
||||
lda #$00
|
||||
fill_loop:
|
||||
sta PPUDATA
|
||||
dex
|
||||
bne fill_loop
|
||||
dey
|
||||
bne fill_loop
|
||||
; Set specific tiles
|
||||
load_ppu_addr $2184
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21A4
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E2
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C6
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E6
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2204
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $2224
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21EA
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
lda #$00
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D2
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $3F00
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$20
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$09
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
lda #$00
|
||||
sta PPUSCROLL
|
||||
sta PPUSCROLL
|
||||
lda #%00001110
|
||||
sta PPUMASK
|
||||
lda #$80
|
||||
sta PPUCTRL ; NMI on, PPU slave, Small sprites, 0 addresses
|
||||
loop:
|
||||
jmp loop
|
||||
|
||||
.macro shl_a count
|
||||
.scope
|
||||
ldx #count
|
||||
a_l:asl A
|
||||
dex
|
||||
bne a_l
|
||||
.endscope
|
||||
.endmacro
|
||||
|
||||
nmi:
|
||||
lda PPUSTATUS
|
||||
lda #%00000110
|
||||
sta PPUMASK ; Force blank
|
||||
lda #$01
|
||||
sta JOY1
|
||||
lda #$00
|
||||
sta JOY1
|
||||
|
||||
load_ppu_addr $23DC ; A
|
||||
lda JOY1
|
||||
and #$01
|
||||
shl_a 6
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $23DD ; B
|
||||
lda JOY1
|
||||
and #$01
|
||||
shl_a 6
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $23DA ; Select
|
||||
lda JOY1
|
||||
and #$01
|
||||
shl_a 6
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $23DB ; Start
|
||||
lda JOY1
|
||||
and #$01
|
||||
shl_a 6
|
||||
sta PPUDATA
|
||||
|
||||
lda JOY1 ; Up
|
||||
and #$01
|
||||
sta TEMP
|
||||
|
||||
load_ppu_addr $23E1 ; Down
|
||||
lda JOY1
|
||||
and #$01
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $23D8 ; Left
|
||||
lda JOY1
|
||||
and #$01
|
||||
shl_a 6
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $23D9 ; Right
|
||||
lda JOY1
|
||||
and #$01
|
||||
shl_a 6
|
||||
ora TEMP
|
||||
sta PPUDATA
|
||||
|
||||
lda #$00
|
||||
sta PPUSCROLL
|
||||
sta PPUSCROLL
|
||||
|
||||
lda #%00001110
|
||||
sta PPUMASK ; Enable rendering
|
||||
rti
|
||||
exit:
|
||||
stp
|
||||
|
||||
irq:
|
||||
stp
|
||||
@@ -36,6 +36,8 @@ macro_rules! rom_test {
|
||||
|
||||
pub(crate) use rom_test;
|
||||
|
||||
use crate::{Break, NES};
|
||||
|
||||
rom_test!(basic_cpu, "basic-cpu.nes", |nes| {
|
||||
assert_eq!(nes.last_instruction(), "0x8001 HLT :2 []");
|
||||
assert_eq!(nes.cpu_cycle(), 10);
|
||||
@@ -136,6 +138,67 @@ rom_test!(even_odd, "even_odd.nes", |nes| {
|
||||
assert_eq!(nes.cpu.y, 0x00);
|
||||
});
|
||||
|
||||
fn run_frame(nes: &mut NES) {
|
||||
nes.run_one_clock_cycle(&Break::default());
|
||||
while nes.ppu().scanline != 241 || nes.ppu().pixel != 0 {
|
||||
nes.run_one_clock_cycle(&Break::default());
|
||||
}
|
||||
}
|
||||
|
||||
rom_test!(input_test, "input_test.nes", timeout = 86964*3, |nes| {
|
||||
const A: u16 = 0x23DC; // 0 || 0b01000000
|
||||
const B: u16 = 0x23DD; // 0 || 0b01000000
|
||||
const SELECT: u16 = 0x23DA; // 0 || 0b01000000
|
||||
const START: u16 = 0x23DB; // 0 || 0b01000000
|
||||
const DOWN: u16 = 0x23E1; // 0 || 1
|
||||
const LEFT: u16 = 0x23D8; // 0 || 0b01000000
|
||||
const UP_RIGHT: u16 = 0x23D9; // 0 || 0b01000000 | 1
|
||||
run_frame(&mut nes);
|
||||
assert_eq!(nes.mapped.peek_ppu(A).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(B).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(SELECT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(START).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(DOWN).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(LEFT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(UP_RIGHT).unwrap(), 0);
|
||||
nes.controller_1().set_a(true);
|
||||
run_frame(&mut nes);
|
||||
assert_eq!(nes.mapped.peek_ppu(A).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(B).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(SELECT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(START).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(DOWN).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(LEFT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(UP_RIGHT).unwrap(), 0);
|
||||
nes.controller_1().set_b(true);
|
||||
run_frame(&mut nes);
|
||||
assert_eq!(nes.mapped.peek_ppu(A).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(B).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(SELECT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(START).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(DOWN).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(LEFT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(UP_RIGHT).unwrap(), 0);
|
||||
nes.controller_1().0 = 0xFF;
|
||||
run_frame(&mut nes);
|
||||
assert_eq!(nes.mapped.peek_ppu(A).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(B).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(SELECT).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(START).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(DOWN).unwrap(), 0x01);
|
||||
assert_eq!(nes.mapped.peek_ppu(LEFT).unwrap(), 0x40);
|
||||
assert_eq!(nes.mapped.peek_ppu(UP_RIGHT).unwrap(), 0x41);
|
||||
nes.controller_1().0 = 0;
|
||||
run_frame(&mut nes);
|
||||
assert_eq!(nes.mapped.peek_ppu(A).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(B).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(SELECT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(START).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(DOWN).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(LEFT).unwrap(), 0);
|
||||
assert_eq!(nes.mapped.peek_ppu(UP_RIGHT).unwrap(), 0);
|
||||
});
|
||||
|
||||
// rom_test!(even_odd, "even_odd.nes", |nes| {
|
||||
// assert_eq!(nes.last_instruction(), "0x8023 HLT :2 []");
|
||||
// assert_eq!(nes.cpu_cycle(), 57182);
|
||||
|
||||
@@ -94,3 +94,8 @@ rom_test!(ppu_palette_shared, "ppu_palette_shared.nes", |nes| {
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Sets up an image, and scrolls a specific number of pixels over
|
||||
rom_test!(ppu_scrolling, "ppu_fine_x_scrolling.nes", timeout = 86964*4, |nes| {
|
||||
assert_eq!(nes.image().read(0, 0), Color { r: 0xFF, g: 0xFE, b: 0xFF });
|
||||
});
|
||||
|
||||
306
src/test_roms/ppu_fine_x_scrolling.s
Normal file
306
src/test_roms/ppu_fine_x_scrolling.s
Normal file
@@ -0,0 +1,306 @@
|
||||
.include "testing.s"
|
||||
; patterns_bin "pat.bin"
|
||||
.macro chr b,s
|
||||
.repeat s
|
||||
.byte b
|
||||
.endrepeat
|
||||
.endmacro
|
||||
.pushseg
|
||||
.segment "CHARS"
|
||||
T_EMPTY = 0
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.endrepeat
|
||||
|
||||
T_TOP_LEFT = 1
|
||||
.repeat 2 ; Top Left 1
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_TOP_RIGHT = 2
|
||||
.repeat 2 ; Top Right 2
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
T_BOTTOM_RIGHT = 3
|
||||
.repeat 2 ; Bottom Right 3
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_BOTTOM_LEFT = 4
|
||||
.repeat 2 ; Bottom Left 4
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_FORWARD = 5
|
||||
.repeat 2 ; FW 5
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %00001110
|
||||
.byte %00011100
|
||||
.byte %00111000
|
||||
.byte %01110000
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_BACKWARD = 6
|
||||
.repeat 2 ; BW 6
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01110000
|
||||
.byte %00111000
|
||||
.byte %00011100
|
||||
.byte %00001110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
|
||||
T_PILL_L = 7
|
||||
.repeat 2 ; Pill L 7
|
||||
.byte %00111111
|
||||
.byte %01111111
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01111111
|
||||
.byte %00111111
|
||||
.endrepeat
|
||||
T_PILL_R = 8
|
||||
.repeat 2 ; Pill R 8
|
||||
.byte %11111100
|
||||
.byte %11111110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %11111110
|
||||
.byte %11111100
|
||||
.endrepeat
|
||||
|
||||
T_LINE = 9
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.endrepeat
|
||||
|
||||
.popseg
|
||||
|
||||
zp_res Y_SCROLL
|
||||
zp_res X_SCROLL
|
||||
zp_res CTRL_BYTE
|
||||
|
||||
.macro load_ppu_addr addr
|
||||
lda #.hibyte(addr)
|
||||
sta PPUADDR
|
||||
lda #.lobyte(addr)
|
||||
sta PPUADDR ; Load $2000
|
||||
.endmacro
|
||||
|
||||
reset:
|
||||
sei
|
||||
cld
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
; Init PPU
|
||||
bit PPUSTATUS
|
||||
vwait1:
|
||||
bit PPUSTATUS
|
||||
bpl vwait1
|
||||
vwait2:
|
||||
bit PPUSTATUS
|
||||
bpl vwait2
|
||||
|
||||
lda #$00
|
||||
sta PPUCTRL ; NMI off, PPU slave, Small sprites, 0 addresses
|
||||
|
||||
; Fill NT0
|
||||
load_ppu_addr $2000
|
||||
ldx #$00
|
||||
ldy #$04
|
||||
lda #$00
|
||||
fill_loop:
|
||||
sta PPUDATA
|
||||
dex
|
||||
bne fill_loop
|
||||
dey
|
||||
bne fill_loop
|
||||
; Set specific tiles
|
||||
load_ppu_addr $2184
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21A4
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E2
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C6
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E6
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2204
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $2224
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21EA
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
lda #$00
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D2
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $3F00
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$20
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$09
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2001
|
||||
lda #T_LINE
|
||||
sta PPUDATA
|
||||
|
||||
lda #$00
|
||||
sta PPUSCROLL
|
||||
sta PPUSCROLL
|
||||
lda #%00001110
|
||||
sta PPUMASK
|
||||
lda #$80
|
||||
sta PPUCTRL ; NMI on, PPU slave, Small sprites, 0 addresses
|
||||
sta CTRL_BYTE
|
||||
loop:
|
||||
jmp loop
|
||||
|
||||
nmi:
|
||||
lda PPUSTATUS
|
||||
lda #%00000110
|
||||
sta PPUMASK ; Force blank
|
||||
|
||||
lda #$0A
|
||||
; adc X_SCROLL
|
||||
; sta X_SCROLL
|
||||
sta PPUSCROLL
|
||||
; bcc y_scroll
|
||||
; lda #$01
|
||||
; eor CTRL_BYTE
|
||||
; sta CTRL_BYTE
|
||||
; sta PPUCTRL
|
||||
; y_scroll:
|
||||
; clc
|
||||
lda #$00
|
||||
; adc Y_SCROLL
|
||||
; sta Y_SCROLL
|
||||
sta PPUSCROLL
|
||||
|
||||
lda CTRL_BYTE
|
||||
sta PPUCTRL
|
||||
lda #%00001110
|
||||
sta PPUMASK ; Enable rendering
|
||||
rti
|
||||
exit:
|
||||
stp
|
||||
|
||||
irq:
|
||||
stp
|
||||
306
src/test_roms/scrolling.s
Normal file
306
src/test_roms/scrolling.s
Normal file
@@ -0,0 +1,306 @@
|
||||
.include "testing.s"
|
||||
; patterns_bin "pat.bin"
|
||||
.macro chr b,s
|
||||
.repeat s
|
||||
.byte b
|
||||
.endrepeat
|
||||
.endmacro
|
||||
.pushseg
|
||||
.segment "CHARS"
|
||||
T_EMPTY = 0
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.endrepeat
|
||||
|
||||
T_TOP_LEFT = 1
|
||||
.repeat 2 ; Top Left 1
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_TOP_RIGHT = 2
|
||||
.repeat 2 ; Top Right 2
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
T_BOTTOM_RIGHT = 3
|
||||
.repeat 2 ; Bottom Right 3
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_BOTTOM_LEFT = 4
|
||||
.repeat 2 ; Bottom Left 4
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_FORWARD = 5
|
||||
.repeat 2 ; FW 5
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %00001110
|
||||
.byte %00011100
|
||||
.byte %00111000
|
||||
.byte %01110000
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_BACKWARD = 6
|
||||
.repeat 2 ; BW 6
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01110000
|
||||
.byte %00111000
|
||||
.byte %00011100
|
||||
.byte %00001110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
|
||||
T_PILL_L = 7
|
||||
.repeat 2 ; Pill L 7
|
||||
.byte %00111111
|
||||
.byte %01111111
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01111111
|
||||
.byte %00111111
|
||||
.endrepeat
|
||||
T_PILL_R = 8
|
||||
.repeat 2 ; Pill R 8
|
||||
.byte %11111100
|
||||
.byte %11111110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %11111110
|
||||
.byte %11111100
|
||||
.endrepeat
|
||||
|
||||
T_LINE = 9
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.endrepeat
|
||||
|
||||
.popseg
|
||||
|
||||
zp_res Y_SCROLL
|
||||
zp_res X_SCROLL
|
||||
zp_res CTRL_BYTE
|
||||
|
||||
.macro load_ppu_addr addr
|
||||
lda #.hibyte(addr)
|
||||
sta PPUADDR
|
||||
lda #.lobyte(addr)
|
||||
sta PPUADDR ; Load $2000
|
||||
.endmacro
|
||||
|
||||
reset:
|
||||
sei
|
||||
cld
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
; Init PPU
|
||||
bit PPUSTATUS
|
||||
vwait1:
|
||||
bit PPUSTATUS
|
||||
bpl vwait1
|
||||
vwait2:
|
||||
bit PPUSTATUS
|
||||
bpl vwait2
|
||||
|
||||
lda #$00
|
||||
sta PPUCTRL ; NMI off, PPU slave, Small sprites, 0 addresses
|
||||
|
||||
; Fill NT0
|
||||
load_ppu_addr $2000
|
||||
ldx #$00
|
||||
ldy #$04
|
||||
lda #$00
|
||||
fill_loop:
|
||||
sta PPUDATA
|
||||
dex
|
||||
bne fill_loop
|
||||
dey
|
||||
bne fill_loop
|
||||
; Set specific tiles
|
||||
load_ppu_addr $2184
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21A4
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E2
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C6
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E6
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2204
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $2224
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21EA
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
lda #$00
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D2
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $3F00
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$20
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$09
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2001
|
||||
lda #T_LINE
|
||||
sta PPUDATA
|
||||
|
||||
lda #$00
|
||||
sta PPUSCROLL
|
||||
sta PPUSCROLL
|
||||
lda #%00001110
|
||||
sta PPUMASK
|
||||
lda #$80
|
||||
sta PPUCTRL ; NMI on, PPU slave, Small sprites, 0 addresses
|
||||
sta CTRL_BYTE
|
||||
loop:
|
||||
jmp loop
|
||||
|
||||
nmi:
|
||||
lda PPUSTATUS
|
||||
lda #%00000110
|
||||
sta PPUMASK ; Force blank
|
||||
|
||||
lda #$0A
|
||||
adc X_SCROLL
|
||||
sta X_SCROLL
|
||||
sta PPUSCROLL
|
||||
bcc y_scroll
|
||||
lda #$01
|
||||
eor CTRL_BYTE
|
||||
sta CTRL_BYTE
|
||||
sta PPUCTRL
|
||||
y_scroll:
|
||||
clc
|
||||
lda #$00
|
||||
adc Y_SCROLL
|
||||
sta Y_SCROLL
|
||||
sta PPUSCROLL
|
||||
|
||||
lda CTRL_BYTE
|
||||
sta PPUCTRL
|
||||
lda #%00001110
|
||||
sta PPUMASK ; Enable rendering
|
||||
rti
|
||||
exit:
|
||||
stp
|
||||
|
||||
irq:
|
||||
stp
|
||||
321
src/test_roms/sprites.s
Normal file
321
src/test_roms/sprites.s
Normal file
@@ -0,0 +1,321 @@
|
||||
.include "testing.s"
|
||||
|
||||
.pushseg
|
||||
.segment "CHARS"
|
||||
T_EMPTY = 0
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.byte %00000000
|
||||
.endrepeat
|
||||
|
||||
T_TOP_LEFT = 1
|
||||
.repeat 2 ; Top Left 1
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_TOP_RIGHT = 2
|
||||
.repeat 2 ; Top Right 2
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
T_BOTTOM_RIGHT = 3
|
||||
.repeat 2 ; Bottom Right 3
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_BOTTOM_LEFT = 4
|
||||
.repeat 2 ; Bottom Left 4
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11111111
|
||||
.byte %11111111
|
||||
.endrepeat
|
||||
T_FORWARD = 5
|
||||
.repeat 2 ; FW 5
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %00001110
|
||||
.byte %00011100
|
||||
.byte %00111000
|
||||
.byte %01110000
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.endrepeat
|
||||
T_BACKWARD = 6
|
||||
.repeat 2 ; BW 6
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01110000
|
||||
.byte %00111000
|
||||
.byte %00011100
|
||||
.byte %00001110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.endrepeat
|
||||
|
||||
T_PILL_L = 7
|
||||
.repeat 2 ; Pill L 7
|
||||
.byte %00111111
|
||||
.byte %01111111
|
||||
.byte %11100000
|
||||
.byte %11000000
|
||||
.byte %11000000
|
||||
.byte %11100000
|
||||
.byte %01111111
|
||||
.byte %00111111
|
||||
.endrepeat
|
||||
T_PILL_R = 8
|
||||
.repeat 2 ; Pill R 8
|
||||
.byte %11111100
|
||||
.byte %11111110
|
||||
.byte %00000111
|
||||
.byte %00000011
|
||||
.byte %00000011
|
||||
.byte %00000111
|
||||
.byte %11111110
|
||||
.byte %11111100
|
||||
.endrepeat
|
||||
|
||||
T_LINE = 9
|
||||
.repeat 2 ; Empty 0
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.byte %00100000
|
||||
.endrepeat
|
||||
|
||||
.popseg
|
||||
|
||||
zp_res Y_SCROLL
|
||||
zp_res X_SCROLL
|
||||
zp_res CTRL_BYTE
|
||||
|
||||
.macro load_ppu_addr addr
|
||||
lda #.hibyte(addr)
|
||||
sta PPUADDR
|
||||
lda #.lobyte(addr)
|
||||
sta PPUADDR ; Load $2000
|
||||
.endmacro
|
||||
|
||||
reset:
|
||||
sei
|
||||
cld
|
||||
ldx #$FF
|
||||
txs
|
||||
|
||||
; Init PPU
|
||||
bit PPUSTATUS
|
||||
vwait1:
|
||||
bit PPUSTATUS
|
||||
bpl vwait1
|
||||
vwait2:
|
||||
bit PPUSTATUS
|
||||
bpl vwait2
|
||||
|
||||
lda #$00
|
||||
sta PPUCTRL ; NMI off, PPU slave, Small sprites, 0 addresses
|
||||
|
||||
; Fill NT0
|
||||
load_ppu_addr $2000
|
||||
ldx #$00
|
||||
ldy #$04
|
||||
lda #$00
|
||||
fill_loop:
|
||||
sta PPUDATA
|
||||
dex
|
||||
bne fill_loop
|
||||
dey
|
||||
bne fill_loop
|
||||
; Set specific tiles
|
||||
load_ppu_addr $2184
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21A4
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E2
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21C6
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21E6
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2204
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $2224
|
||||
lda #T_BOTTOM_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_BOTTOM_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21EA
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
lda #$00
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
lda #T_PILL_L
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D2
|
||||
lda #T_FORWARD
|
||||
sta PPUDATA
|
||||
lda #T_BACKWARD
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F2
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_TOP_RIGHT
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $21D6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
load_ppu_addr $21F6
|
||||
lda #T_TOP_LEFT
|
||||
sta PPUDATA
|
||||
lda #T_PILL_R
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $3F00
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$20
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
lda #$0f
|
||||
sta PPUDATA
|
||||
lda #$09
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
sta PPUDATA
|
||||
|
||||
load_ppu_addr $2001
|
||||
lda #T_LINE
|
||||
sta PPUDATA
|
||||
|
||||
ldx #$00
|
||||
sprite_loop:
|
||||
lda sprite_data,x
|
||||
sta $300,x
|
||||
inx
|
||||
cpx #(sprite_data_end - sprite_data)
|
||||
bne sprite_loop
|
||||
|
||||
lda #$00
|
||||
sta PPUSCROLL
|
||||
sta PPUSCROLL
|
||||
lda #%00001110
|
||||
sta PPUMASK
|
||||
lda #$80
|
||||
sta PPUCTRL ; NMI on, PPU slave, Small sprites, 0 addresses
|
||||
sta CTRL_BYTE
|
||||
loop:
|
||||
jmp loop
|
||||
|
||||
nmi:
|
||||
lda PPUSTATUS
|
||||
lda #%00000110
|
||||
sta PPUMASK ; Force blank
|
||||
|
||||
lda #$01
|
||||
adc X_SCROLL
|
||||
sta X_SCROLL
|
||||
sta PPUSCROLL
|
||||
bcc y_scroll
|
||||
lda #$01
|
||||
eor CTRL_BYTE
|
||||
sta CTRL_BYTE
|
||||
sta PPUCTRL
|
||||
y_scroll:
|
||||
clc
|
||||
lda #$00
|
||||
adc Y_SCROLL
|
||||
sta Y_SCROLL
|
||||
sta PPUSCROLL
|
||||
|
||||
lda #$00
|
||||
sta SPRADDR
|
||||
lda #$03
|
||||
sta SPRDMA
|
||||
|
||||
lda CTRL_BYTE
|
||||
sta PPUCTRL
|
||||
lda #%00011110
|
||||
sta PPUMASK ; Enable rendering
|
||||
rti
|
||||
exit:
|
||||
stp
|
||||
|
||||
irq:
|
||||
stp
|
||||
|
||||
sprite_data:
|
||||
.byte $00
|
||||
.byte T_PILL_R
|
||||
.byte $00
|
||||
.byte $00
|
||||
sprite_data_end:
|
||||
Reference in New Issue
Block a user