Complete initial tests for startup
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 8s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 8s
This commit is contained in:
@@ -107,6 +107,17 @@ impl DebuggerState {
|
|||||||
labelled_box("BG at $1000", false),
|
labelled_box("BG at $1000", false),
|
||||||
labelled_box("Sprites at $1000", false),
|
labelled_box("Sprites at $1000", false),
|
||||||
],
|
],
|
||||||
|
column![
|
||||||
|
labelled_box("Even frame", nes.ppu.even),
|
||||||
|
labelled_box("BG Enabled", false),
|
||||||
|
labelled_box("Sprites Enabled", false),
|
||||||
|
labelled_box("BG Mask", false),
|
||||||
|
labelled_box("Sprites Mask", false),
|
||||||
|
labelled_box("Grayscale", false),
|
||||||
|
labelled_box("Intensify Red", false),
|
||||||
|
labelled_box("Intensify Green", false),
|
||||||
|
labelled_box("Intensify Blue", false),
|
||||||
|
],
|
||||||
column![
|
column![
|
||||||
run_type(
|
run_type(
|
||||||
"PPU Cycles:",
|
"PPU Cycles:",
|
||||||
@@ -190,7 +201,7 @@ impl DebuggerState {
|
|||||||
DebuggerMessage::RunToScanLine => {
|
DebuggerMessage::RunToScanLine => {
|
||||||
Self::run_until(nes, |_, n| n.ppu.scanline == self.to_scan_line)
|
Self::run_until(nes, |_, n| n.ppu.scanline == self.to_scan_line)
|
||||||
}
|
}
|
||||||
DebuggerMessage::RunFrames => Self::run_n_clock_cycles(nes, self.frames * 341 * 261),
|
DebuggerMessage::RunFrames => Self::run_n_clock_cycles(nes, self.frames * 341 * 262),
|
||||||
DebuggerMessage::Run => todo!(),
|
DebuggerMessage::Run => todo!(),
|
||||||
DebuggerMessage::Pause => todo!(),
|
DebuggerMessage::Pause => todo!(),
|
||||||
}
|
}
|
||||||
@@ -205,7 +216,7 @@ fn run_type<'a, Message: Clone + 'a>(
|
|||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
row![
|
row![
|
||||||
widget::container(text(label)).padding(2.),
|
widget::container(text(label)).padding(2.),
|
||||||
widget::container(number_input(val).on_input(update)).padding(2.),
|
widget::container(number_input(val).on_input(update).on_submit(run.clone())).padding(2.),
|
||||||
widget::container(button(image("./images/ic_fluent_play_24_filled.png")).on_press(run))
|
widget::container(button(image("./images/ic_fluent_play_24_filled.png")).on_press(run))
|
||||||
.padding(2.),
|
.padding(2.),
|
||||||
]
|
]
|
||||||
|
|||||||
57
src/main.rs
57
src/main.rs
@@ -1,7 +1,4 @@
|
|||||||
use std::{
|
use std::{collections::HashMap, fmt};
|
||||||
collections::HashMap,
|
|
||||||
fmt,
|
|
||||||
};
|
|
||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
Color, Element, Font,
|
Color, Element, Font,
|
||||||
@@ -15,11 +12,14 @@ use iced::{
|
|||||||
window::{self, Id, Settings},
|
window::{self, Id, Settings},
|
||||||
};
|
};
|
||||||
use nes_emu::{
|
use nes_emu::{
|
||||||
debugger::{DebuggerMessage, DebuggerState}, header_menu::header_menu, hex_view::{HexEvent, HexView}, NES, PPU
|
NES, PPU,
|
||||||
|
debugger::{DebuggerMessage, DebuggerState},
|
||||||
|
header_menu::header_menu,
|
||||||
|
hex_view::{HexEvent, HexView},
|
||||||
};
|
};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "basic_init_1.nes");
|
const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "even_odd.nes");
|
||||||
// const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
// const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||||
|
|
||||||
extern crate nes_emu;
|
extern crate nes_emu;
|
||||||
@@ -45,6 +45,7 @@ enum WindowType {
|
|||||||
Memory(MemoryTy, HexView),
|
Memory(MemoryTy, HexView),
|
||||||
TileMap,
|
TileMap,
|
||||||
TileViewer,
|
TileViewer,
|
||||||
|
Palette,
|
||||||
Debugger,
|
Debugger,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +94,7 @@ enum Message {
|
|||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
fn new() -> (Self, Task<Message>) {
|
fn new() -> (Self, Task<Message>) {
|
||||||
let mut nes = nes_emu::NES::load_nes_file(ROM_FILE)
|
let mut nes = nes_emu::NES::load_nes_file(ROM_FILE).expect("Failed to load nes file");
|
||||||
.expect("Failed to load nes file");
|
|
||||||
nes.reset();
|
nes.reset();
|
||||||
let (win, task) = iced::window::open(Settings::default());
|
let (win, task) = iced::window::open(Settings::default());
|
||||||
(
|
(
|
||||||
@@ -112,6 +112,7 @@ impl Emulator {
|
|||||||
Some(WindowType::Memory(_, _)) => "NES MemoryView".into(),
|
Some(WindowType::Memory(_, _)) => "NES MemoryView".into(),
|
||||||
Some(WindowType::TileMap) => "NES TileMap".into(),
|
Some(WindowType::TileMap) => "NES TileMap".into(),
|
||||||
Some(WindowType::TileViewer) => "NES Tile Viewer".into(),
|
Some(WindowType::TileViewer) => "NES Tile Viewer".into(),
|
||||||
|
Some(WindowType::Palette) => "NES Palette Viewer".into(),
|
||||||
Some(WindowType::Debugger) => "NES Debugger".into(),
|
Some(WindowType::Debugger) => "NES Debugger".into(),
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
}
|
}
|
||||||
@@ -159,9 +160,15 @@ impl Emulator {
|
|||||||
return view.update(val).map(move |e| Message::Hex(id, e));
|
return view.update(val).map(move |e| Message::Hex(id, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Header(HeaderButton::Reset) => { self.nes.reset(); }
|
Message::Header(HeaderButton::Reset) => {
|
||||||
Message::Header(HeaderButton::PowerCycle) => { self.nes.power_cycle(); }
|
self.nes.reset();
|
||||||
Message::Debugger(debugger_message) => self.debugger.update(debugger_message, &mut self.nes),
|
}
|
||||||
|
Message::Header(HeaderButton::PowerCycle) => {
|
||||||
|
self.nes.power_cycle();
|
||||||
|
}
|
||||||
|
Message::Debugger(debugger_message) => {
|
||||||
|
self.debugger.update(debugger_message, &mut self.nes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// self.image.0.clone_from(self.nes.image());
|
// self.image.0.clone_from(self.nes.image());
|
||||||
Task::none()
|
Task::none()
|
||||||
@@ -193,13 +200,28 @@ impl Emulator {
|
|||||||
container(content).width(Fill).height(Fill).into()
|
container(content).width(Fill).height(Fill).into()
|
||||||
}
|
}
|
||||||
Some(WindowType::TileMap) => {
|
Some(WindowType::TileMap) => {
|
||||||
container(Canvas::new(DbgImage::PatternTable(self.nes.ppu()))).width(Fill).height(Fill).into()
|
container(Canvas::new(DbgImage::NameTable(self.nes.ppu())))
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
Some(WindowType::TileViewer) => {
|
Some(WindowType::TileViewer) => {
|
||||||
container(Canvas::new(DbgImage::NameTable(self.nes.ppu()))).width(Fill).height(Fill).into()
|
container(Canvas::new(DbgImage::PatternTable(self.nes.ppu())))
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Some(WindowType::Palette) => {
|
||||||
|
container(Canvas::new(DbgImage::Palette(self.nes.ppu())))
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
Some(WindowType::Debugger) => {
|
Some(WindowType::Debugger) => {
|
||||||
container(self.debugger.view(&self.nes).map(Message::Debugger)).width(Fill).height(Fill).into()
|
container(self.debugger.view(&self.nes).map(Message::Debugger))
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
None => panic!("Window not found"),
|
None => panic!("Window not found"),
|
||||||
// _ => todo!(),
|
// _ => todo!(),
|
||||||
@@ -231,10 +253,7 @@ impl Emulator {
|
|||||||
row![
|
row![
|
||||||
header_menu(
|
header_menu(
|
||||||
"Console",
|
"Console",
|
||||||
[
|
[HeaderButton::Reset, HeaderButton::PowerCycle,],
|
||||||
HeaderButton::Reset,
|
|
||||||
HeaderButton::PowerCycle,
|
|
||||||
],
|
|
||||||
Message::Header
|
Message::Header
|
||||||
),
|
),
|
||||||
header_menu(
|
header_menu(
|
||||||
@@ -290,6 +309,7 @@ impl Program<Message> for Emulator {
|
|||||||
enum DbgImage<'a> {
|
enum DbgImage<'a> {
|
||||||
NameTable(&'a PPU),
|
NameTable(&'a PPU),
|
||||||
PatternTable(&'a PPU),
|
PatternTable(&'a PPU),
|
||||||
|
Palette(&'a PPU),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program<Message> for DbgImage<'_> {
|
impl Program<Message> for DbgImage<'_> {
|
||||||
@@ -310,6 +330,7 @@ impl Program<Message> for DbgImage<'_> {
|
|||||||
match self {
|
match self {
|
||||||
DbgImage::NameTable(ppu) => ppu.render_name_table(&mut name_table_frame),
|
DbgImage::NameTable(ppu) => ppu.render_name_table(&mut name_table_frame),
|
||||||
DbgImage::PatternTable(ppu) => ppu.render_pattern_tables(&mut name_table_frame),
|
DbgImage::PatternTable(ppu) => ppu.render_pattern_tables(&mut name_table_frame),
|
||||||
|
DbgImage::Palette(ppu) => ppu.render_palette(&mut name_table_frame),
|
||||||
}
|
}
|
||||||
vec![name_table_frame.into_geometry()]
|
vec![name_table_frame.into_geometry()]
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/ppu.rs
119
src/ppu.rs
@@ -1,6 +1,13 @@
|
|||||||
use iced::{advanced::graphics::geometry::Renderer, widget::canvas::{Fill, Frame}, Point, Size};
|
use iced::{
|
||||||
|
Point, Size,
|
||||||
|
advanced::graphics::geometry::Renderer,
|
||||||
|
widget::canvas::{Fill, Frame},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{hex_view::Memory, mem::{MemoryMap, Segment}};
|
use crate::{
|
||||||
|
hex_view::Memory,
|
||||||
|
mem::{MemoryMap, Segment},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
@@ -91,6 +98,15 @@ pub struct Mask {
|
|||||||
em_blue: bool,
|
em_blue: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COLORS: &'static [Color; 0b11_1111] = &[
|
||||||
|
Color { r: 0, g: 0, b: 0}; 0b11_1111
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct Palette {
|
||||||
|
colors: &'static [Color; 0b11_1111],
|
||||||
|
ram: [u8; 0x20]
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PPU {
|
pub struct PPU {
|
||||||
// registers: PPURegisters,
|
// registers: PPURegisters,
|
||||||
frame_count: usize,
|
frame_count: usize,
|
||||||
@@ -104,6 +120,7 @@ pub struct PPU {
|
|||||||
pub vblank: bool,
|
pub vblank: bool,
|
||||||
|
|
||||||
pub(crate) memory: MemoryMap<PPUMMRegisters>,
|
pub(crate) memory: MemoryMap<PPUMMRegisters>,
|
||||||
|
palette: Palette,
|
||||||
background: Background,
|
background: Background,
|
||||||
oam: OAM,
|
oam: OAM,
|
||||||
pub render_buffer: RenderBuffer<256, 240>,
|
pub render_buffer: RenderBuffer<256, 240>,
|
||||||
@@ -145,11 +162,12 @@ impl PPU {
|
|||||||
em_green: false,
|
em_green: false,
|
||||||
em_blue: false,
|
em_blue: false,
|
||||||
},
|
},
|
||||||
|
palette: Palette { colors: COLORS, ram: [0; _] },
|
||||||
vblank: false,
|
vblank: false,
|
||||||
frame_count: 0,
|
frame_count: 0,
|
||||||
nmi_enabled: false,
|
nmi_enabled: false,
|
||||||
nmi_waiting: false,
|
nmi_waiting: false,
|
||||||
even: true, // ??
|
even: false,
|
||||||
scanline: 0,
|
scanline: 0,
|
||||||
pixel: 25,
|
pixel: 25,
|
||||||
render_buffer: RenderBuffer::empty(),
|
render_buffer: RenderBuffer::empty(),
|
||||||
@@ -157,7 +175,8 @@ impl PPU {
|
|||||||
Segment::rom("CHR ROM", 0x0000, rom),
|
Segment::rom("CHR ROM", 0x0000, rom),
|
||||||
Segment::ram("Internal VRAM", 0x2000, 0x1000),
|
Segment::ram("Internal VRAM", 0x2000, 0x1000),
|
||||||
Segment::mirror("Mirror of VRAM", 0x3000, 0x0F00, 1),
|
Segment::mirror("Mirror of VRAM", 0x3000, 0x0F00, 1),
|
||||||
Segment::reg("Palette Control", 0x3F00, 0x0100, PPUMMRegisters::Palette),
|
Segment::reg("Palette Control", 0x3F00, 0x0020, PPUMMRegisters::Palette),
|
||||||
|
Segment::mirror("Mirror of Palette", 0x3F20, 0x00E0, 3),
|
||||||
]),
|
]),
|
||||||
background: Background {
|
background: Background {
|
||||||
v: 0,
|
v: 0,
|
||||||
@@ -182,6 +201,10 @@ impl PPU {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rendering_enabled(&self) -> bool {
|
||||||
|
self.mask.enable_background || self.mask.enable_sprites
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_reg(&mut self, offset: u16) -> u8 {
|
pub fn read_reg(&mut self, offset: u16) -> u8 {
|
||||||
match offset {
|
match offset {
|
||||||
0 => panic!("ppuctrl is write-only"),
|
0 => panic!("ppuctrl is write-only"),
|
||||||
@@ -190,7 +213,6 @@ impl PPU {
|
|||||||
let tmp = if self.vblank { 0b1000_0000 } else { 0 };
|
let tmp = if self.vblank { 0b1000_0000 } else { 0 };
|
||||||
self.vblank = false;
|
self.vblank = false;
|
||||||
self.background.w = false;
|
self.background.w = false;
|
||||||
println!("Reading status: {:02X}", tmp);
|
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
3 => panic!("oamaddr is write-only"),
|
3 => panic!("oamaddr is write-only"),
|
||||||
@@ -227,7 +249,7 @@ impl PPU {
|
|||||||
self.mask.background_on_left_edge = val & 0b0000_0010 != 0;
|
self.mask.background_on_left_edge = val & 0b0000_0010 != 0;
|
||||||
self.mask.sprites_on_left_edge = val & 0b0000_0100 != 0;
|
self.mask.sprites_on_left_edge = val & 0b0000_0100 != 0;
|
||||||
self.mask.enable_background = val & 0b0000_1000 != 0;
|
self.mask.enable_background = val & 0b0000_1000 != 0;
|
||||||
self.mask.enable_background = val & 0b0001_0000 != 0;
|
self.mask.enable_sprites = val & 0b0001_0000 != 0;
|
||||||
self.mask.em_red = val & 0b0010_0000 != 0;
|
self.mask.em_red = val & 0b0010_0000 != 0;
|
||||||
self.mask.em_green = val & 0b0100_0000 != 0;
|
self.mask.em_green = val & 0b0100_0000 != 0;
|
||||||
self.mask.em_blue = val & 0b1000_0000 != 0;
|
self.mask.em_blue = val & 0b1000_0000 != 0;
|
||||||
@@ -284,7 +306,9 @@ impl PPU {
|
|||||||
pub fn run_one_clock_cycle(&mut self) -> bool {
|
pub fn run_one_clock_cycle(&mut self) -> bool {
|
||||||
self.cycle += 1;
|
self.cycle += 1;
|
||||||
self.pixel += 1;
|
self.pixel += 1;
|
||||||
if self.scanline == 261 && (self.pixel == 341 || (self.pixel == 340 && self.even)) {
|
if self.scanline == 261
|
||||||
|
&& (self.pixel == 341 || (self.pixel == 340 && self.even && self.rendering_enabled()))
|
||||||
|
{
|
||||||
self.scanline = 0;
|
self.scanline = 0;
|
||||||
self.pixel = 0;
|
self.pixel = 0;
|
||||||
self.even = !self.even;
|
self.even = !self.even;
|
||||||
@@ -388,10 +412,22 @@ impl PPU {
|
|||||||
Size::new(1., 1.),
|
Size::new(1., 1.),
|
||||||
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
||||||
(false, false) => Color { r: 0, g: 0, b: 0 },
|
(false, false) => Color { r: 0, g: 0, b: 0 },
|
||||||
(true, false) => Color { r: 64, g: 64, b: 64 },
|
(true, false) => Color {
|
||||||
(false, true) => Color { r: 128, g: 128, b: 128 },
|
r: 64,
|
||||||
(true, true) => Color { r: 255, g: 255, b: 255 },
|
g: 64,
|
||||||
}
|
b: 64,
|
||||||
|
},
|
||||||
|
(false, true) => Color {
|
||||||
|
r: 128,
|
||||||
|
g: 128,
|
||||||
|
b: 128,
|
||||||
|
},
|
||||||
|
(true, true) => Color {
|
||||||
|
r: 255,
|
||||||
|
g: 255,
|
||||||
|
b: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,14 +445,29 @@ impl PPU {
|
|||||||
let high = self.memory.peek(name + y_off + 8).unwrap();
|
let high = self.memory.peek(name + y_off + 8).unwrap();
|
||||||
for bit in 0..8 {
|
for bit in 0..8 {
|
||||||
frame.fill_rectangle(
|
frame.fill_rectangle(
|
||||||
Point::new(x as f32 * 8. + 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.),
|
Size::new(1., 1.),
|
||||||
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
||||||
(false, false) => Color { r: 0, g: 0, b: 0 },
|
(false, false) => Color { r: 0, g: 0, b: 0 },
|
||||||
(true, false) => Color { r: 64, g: 64, b: 64 },
|
(true, false) => Color {
|
||||||
(false, true) => Color { r: 128, g: 128, b: 128 },
|
r: 64,
|
||||||
(true, true) => Color { r: 255, g: 255, b: 255 },
|
g: 64,
|
||||||
}
|
b: 64,
|
||||||
|
},
|
||||||
|
(false, true) => Color {
|
||||||
|
r: 128,
|
||||||
|
g: 128,
|
||||||
|
b: 128,
|
||||||
|
},
|
||||||
|
(true, true) => Color {
|
||||||
|
r: 255,
|
||||||
|
g: 255,
|
||||||
|
b: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,14 +482,29 @@ impl PPU {
|
|||||||
let high = self.memory.peek(name + y_off + 8 + 0x1000).unwrap();
|
let high = self.memory.peek(name + y_off + 8 + 0x1000).unwrap();
|
||||||
for bit in 0..8 {
|
for bit in 0..8 {
|
||||||
frame.fill_rectangle(
|
frame.fill_rectangle(
|
||||||
Point::new(x as f32 * 8. + 8. - bit as f32, y as f32 * 8. + y_off as f32 + 130.),
|
Point::new(
|
||||||
|
x as f32 * 8. + 8. - bit as f32,
|
||||||
|
y as f32 * 8. + y_off as f32 + 130.,
|
||||||
|
),
|
||||||
Size::new(1., 1.),
|
Size::new(1., 1.),
|
||||||
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
match (low & (1 << bit) != 0, high & (1 << bit) != 0) {
|
||||||
(false, false) => Color { r: 0, g: 0, b: 0 },
|
(false, false) => Color { r: 0, g: 0, b: 0 },
|
||||||
(true, false) => Color { r: 64, g: 64, b: 64 },
|
(true, false) => Color {
|
||||||
(false, true) => Color { r: 128, g: 128, b: 128 },
|
r: 64,
|
||||||
(true, true) => Color { r: 255, g: 255, b: 255 },
|
g: 64,
|
||||||
}
|
b: 64,
|
||||||
|
},
|
||||||
|
(false, true) => Color {
|
||||||
|
r: 128,
|
||||||
|
g: 128,
|
||||||
|
b: 128,
|
||||||
|
},
|
||||||
|
(true, true) => Color {
|
||||||
|
r: 255,
|
||||||
|
g: 255,
|
||||||
|
b: 255,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,6 +513,17 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn render_palette<R: Renderer>(&self, frame: &mut Frame<R>) {
|
||||||
|
frame.fill_rectangle(
|
||||||
|
Point::new(0., 0.),
|
||||||
|
Size::new(10., 10.),
|
||||||
|
Color {
|
||||||
|
r: todo!(),
|
||||||
|
g: todo!(),
|
||||||
|
b: todo!(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
43
src/test_roms/basic_init_2.asm
Normal file
43
src/test_roms/basic_init_2.asm
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
.inesprg 2 ; 2 banks
|
||||||
|
.ineschr 1 ;
|
||||||
|
.inesmap 0 ; mapper 0 = NROM
|
||||||
|
.inesmir 0 ; background mirroring, horizontal
|
||||||
|
|
||||||
|
.org $8000
|
||||||
|
RESET:
|
||||||
|
sei ; Ignore IRQs while starting up
|
||||||
|
cld ; disabled decimal mode (iirc it doesn't work properly on NES anyway)
|
||||||
|
ldx #$40
|
||||||
|
stx $4017 ; Disable APU frame IRQ
|
||||||
|
ldx #$ff
|
||||||
|
txs ; Set stack pointer to 0x1ff
|
||||||
|
inx ; Set x to zero
|
||||||
|
stx $2000 ; Disable NMI (by writing zero)
|
||||||
|
stx $2001 ; Disable rendering
|
||||||
|
stx $4010 ; Disable DMC IRQs
|
||||||
|
|
||||||
|
bit $2002 ; Clear vblank flag by reading ppu status
|
||||||
|
VBLANKWAIT1:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT1
|
||||||
|
VBLANKWAIT2:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT2
|
||||||
|
hlt
|
||||||
|
hlt
|
||||||
|
|
||||||
|
ERROR_:
|
||||||
|
hlt
|
||||||
|
|
||||||
|
IGNORE:
|
||||||
|
rti
|
||||||
|
|
||||||
|
.org $FFFA ; Interrupt vectors go here:
|
||||||
|
.word IGNORE ; NMI
|
||||||
|
.word RESET ; Reset
|
||||||
|
.word IGNORE; IRQ
|
||||||
|
|
||||||
|
;;;; NESASM COMPILER STUFF, ADDING THE PATTERN DATA ;;;;
|
||||||
|
|
||||||
|
.incbin "Sprites.pcx"
|
||||||
|
.incbin "Tiles.pcx"
|
||||||
46
src/test_roms/basic_init_3.asm
Normal file
46
src/test_roms/basic_init_3.asm
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
.inesprg 2 ; 2 banks
|
||||||
|
.ineschr 1 ;
|
||||||
|
.inesmap 0 ; mapper 0 = NROM
|
||||||
|
.inesmir 0 ; background mirroring, horizontal
|
||||||
|
|
||||||
|
.org $8000
|
||||||
|
RESET:
|
||||||
|
sei ; Ignore IRQs while starting up
|
||||||
|
cld ; disabled decimal mode (iirc it doesn't work properly on NES anyway)
|
||||||
|
ldx #$40
|
||||||
|
stx $4017 ; Disable APU frame IRQ
|
||||||
|
ldx #$ff
|
||||||
|
txs ; Set stack pointer to 0x1ff
|
||||||
|
inx ; Set x to zero
|
||||||
|
stx $2000 ; Disable NMI (by writing zero)
|
||||||
|
stx $2001 ; Disable rendering
|
||||||
|
stx $4010 ; Disable DMC IRQs
|
||||||
|
|
||||||
|
bit $2002 ; Clear vblank flag by reading ppu status
|
||||||
|
VBLANKWAIT1:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT1
|
||||||
|
VBLANKWAIT2:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT2
|
||||||
|
VBLANKWAIT3:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT3
|
||||||
|
hlt
|
||||||
|
hlt
|
||||||
|
|
||||||
|
ERROR_:
|
||||||
|
hlt
|
||||||
|
|
||||||
|
IGNORE:
|
||||||
|
rti
|
||||||
|
|
||||||
|
.org $FFFA ; Interrupt vectors go here:
|
||||||
|
.word IGNORE ; NMI
|
||||||
|
.word RESET ; Reset
|
||||||
|
.word IGNORE; IRQ
|
||||||
|
|
||||||
|
;;;; NESASM COMPILER STUFF, ADDING THE PATTERN DATA ;;;;
|
||||||
|
|
||||||
|
.incbin "Sprites.pcx"
|
||||||
|
.incbin "Tiles.pcx"
|
||||||
44
src/test_roms/even_odd.asm
Normal file
44
src/test_roms/even_odd.asm
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
.inesprg 2 ; 2 banks
|
||||||
|
.ineschr 1 ;
|
||||||
|
.inesmap 0 ; mapper 0 = NROM
|
||||||
|
.inesmir 0 ; background mirroring, horizontal
|
||||||
|
|
||||||
|
.org $8000
|
||||||
|
RESET:
|
||||||
|
sei ; Ignore IRQs while starting up
|
||||||
|
cld ; disabled decimal mode (iirc it doesn't work properly on NES anyway)
|
||||||
|
ldx #$40
|
||||||
|
stx $4017 ; Disable APU frame IRQ
|
||||||
|
ldx #$ff
|
||||||
|
txs ; Set stack pointer to 0x1ff
|
||||||
|
inx ; Set x to zero
|
||||||
|
stx $2000 ; Disable NMI (by writing zero)
|
||||||
|
stx $4010 ; Disable DMC IRQs
|
||||||
|
ldx #$08
|
||||||
|
stx $2001 ; Disable rendering
|
||||||
|
|
||||||
|
bit $2002 ; Clear vblank flag by reading ppu status
|
||||||
|
VBLANKWAIT1:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT1
|
||||||
|
VBLANKWAIT2:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT2
|
||||||
|
hlt
|
||||||
|
hlt
|
||||||
|
|
||||||
|
ERROR_:
|
||||||
|
hlt
|
||||||
|
|
||||||
|
IGNORE:
|
||||||
|
rti
|
||||||
|
|
||||||
|
.org $FFFA ; Interrupt vectors go here:
|
||||||
|
.word IGNORE ; NMI
|
||||||
|
.word RESET ; Reset
|
||||||
|
.word IGNORE; IRQ
|
||||||
|
|
||||||
|
;;;; NESASM COMPILER STUFF, ADDING THE PATTERN DATA ;;;;
|
||||||
|
|
||||||
|
.incbin "Sprites.pcx"
|
||||||
|
.incbin "Tiles.pcx"
|
||||||
@@ -15,6 +15,20 @@ macro_rules! rom_test {
|
|||||||
$eval
|
$eval
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
($name:ident, . $rom:literal, |$nes:ident| $eval:expr) => {
|
||||||
|
rom_test!($name, . $rom, timeout = 10000000, |$nes| $eval);
|
||||||
|
};
|
||||||
|
($name:ident, . $rom:literal, timeout = $timeout:expr, |$nes:ident| $eval:expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
let rom_file = $rom;
|
||||||
|
println!("{}: {}", stringify!($name), rom_file);
|
||||||
|
let mut $nes = NES::load_nes_file(rom_file).expect("Failed to create nes object");
|
||||||
|
$nes.reset_and_run_with_timeout($timeout);
|
||||||
|
println!("Final: {:?}", $nes);
|
||||||
|
$eval
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
rom_test!(basic_cpu, "basic-cpu.nes", |nes| {
|
rom_test!(basic_cpu, "basic-cpu.nes", |nes| {
|
||||||
@@ -23,6 +37,8 @@ rom_test!(basic_cpu, "basic-cpu.nes", |nes| {
|
|||||||
assert_eq!(nes.cycle, 11);
|
assert_eq!(nes.cycle, 11);
|
||||||
// This is off by one from Mesen, because Mesen is left pointing at the 'invalid' opcode
|
// This is off by one from Mesen, because Mesen is left pointing at the 'invalid' opcode
|
||||||
assert_eq!(nes.cpu.pc, 0x8002);
|
assert_eq!(nes.cpu.pc, 0x8002);
|
||||||
|
// Off by one from Mesen, since Mesen doesn't count the clock cycle attempting to execute the 'invalid' opcode
|
||||||
|
assert_eq!(nes.ppu.pixel, 35);
|
||||||
|
|
||||||
assert_eq!(nes.cpu.sp, 0xFD);
|
assert_eq!(nes.cpu.sp, 0xFD);
|
||||||
// assert_eq!(nes.cpu.a, 0x00);
|
// assert_eq!(nes.cpu.a, 0x00);
|
||||||
@@ -35,6 +51,7 @@ rom_test!(read_write, "read_write.nes", |nes| {
|
|||||||
assert_eq!(nes.cycle, 31);
|
assert_eq!(nes.cycle, 31);
|
||||||
assert_eq!(nes.cpu.pc, 0x8012);
|
assert_eq!(nes.cpu.pc, 0x8012);
|
||||||
assert_eq!(nes.cpu.sp, 0xFD);
|
assert_eq!(nes.cpu.sp, 0xFD);
|
||||||
|
|
||||||
assert_eq!(nes.cpu.a, 0xAA);
|
assert_eq!(nes.cpu.a, 0xAA);
|
||||||
assert_eq!(nes.cpu.x, 0xAA);
|
assert_eq!(nes.cpu.x, 0xAA);
|
||||||
assert_eq!(nes.cpu.y, 0xAA);
|
assert_eq!(nes.cpu.y, 0xAA);
|
||||||
@@ -48,6 +65,7 @@ rom_test!(basic_init_0, "basic_init_0.nes", |nes| {
|
|||||||
assert_eq!(nes.cycle, 41);
|
assert_eq!(nes.cycle, 41);
|
||||||
assert_eq!(nes.cpu.pc, 0x8018);
|
assert_eq!(nes.cpu.pc, 0x8018);
|
||||||
assert_eq!(nes.cpu.sp, 0xFF);
|
assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
|
|
||||||
assert_eq!(nes.cpu.a, 0x00);
|
assert_eq!(nes.cpu.a, 0x00);
|
||||||
assert_eq!(nes.cpu.x, 0x00);
|
assert_eq!(nes.cpu.x, 0x00);
|
||||||
assert_eq!(nes.cpu.y, 0x00);
|
assert_eq!(nes.cpu.y, 0x00);
|
||||||
@@ -56,10 +74,59 @@ rom_test!(basic_init_0, "basic_init_0.nes", |nes| {
|
|||||||
rom_test!(basic_init_1, "basic_init_1.nes", |nes| {
|
rom_test!(basic_init_1, "basic_init_1.nes", |nes| {
|
||||||
assert_eq!(nes.last_instruction, "0x801C HLT :2 []");
|
assert_eq!(nes.last_instruction, "0x801C HLT :2 []");
|
||||||
assert_eq!(nes.cycle, 27403);
|
assert_eq!(nes.cycle, 27403);
|
||||||
assert_eq!(nes.ppu.pixel, 30);
|
|
||||||
assert_eq!(nes.cpu.pc, 0x801D);
|
assert_eq!(nes.cpu.pc, 0x801D);
|
||||||
|
assert_eq!(nes.ppu.pixel, 30);
|
||||||
|
|
||||||
assert_eq!(nes.cpu.sp, 0xFF);
|
assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
assert_eq!(nes.cpu.a, 0x00);
|
assert_eq!(nes.cpu.a, 0x00);
|
||||||
assert_eq!(nes.cpu.x, 0x00);
|
assert_eq!(nes.cpu.x, 0x00);
|
||||||
assert_eq!(nes.cpu.y, 0x00);
|
assert_eq!(nes.cpu.y, 0x00);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
rom_test!(basic_init_2, "basic_init_2.nes", |nes| {
|
||||||
|
assert_eq!(nes.last_instruction, "0x8021 HLT :2 []");
|
||||||
|
assert_eq!(nes.cycle, 57180);
|
||||||
|
assert_eq!(nes.cpu.pc, 0x8022);
|
||||||
|
assert_eq!(nes.ppu.pixel, 19);
|
||||||
|
|
||||||
|
assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
|
assert_eq!(nes.cpu.a, 0x00);
|
||||||
|
assert_eq!(nes.cpu.x, 0x00);
|
||||||
|
assert_eq!(nes.cpu.y, 0x00);
|
||||||
|
});
|
||||||
|
|
||||||
|
rom_test!(basic_init_3, "basic_init_3.nes", |nes| {
|
||||||
|
assert_eq!(nes.last_instruction, "0x8026 HLT :2 []");
|
||||||
|
assert_eq!(nes.cycle, 86964);
|
||||||
|
assert_eq!(nes.cpu.pc, 0x8027);
|
||||||
|
assert_eq!(nes.ppu.pixel, 29);
|
||||||
|
|
||||||
|
assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
|
assert_eq!(nes.cpu.a, 0x00);
|
||||||
|
assert_eq!(nes.cpu.x, 0x00);
|
||||||
|
assert_eq!(nes.cpu.y, 0x00);
|
||||||
|
});
|
||||||
|
|
||||||
|
rom_test!(even_odd, "even_odd.nes", |nes| {
|
||||||
|
assert_eq!(nes.last_instruction, "0x8023 HLT :2 []");
|
||||||
|
assert_eq!(nes.cycle, 57182);
|
||||||
|
assert_eq!(nes.cpu.pc, 0x8024);
|
||||||
|
assert_eq!(nes.ppu.pixel, 25);
|
||||||
|
|
||||||
|
assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
|
assert_eq!(nes.cpu.a, 0x00);
|
||||||
|
assert_eq!(nes.cpu.x, 0x08);
|
||||||
|
assert_eq!(nes.cpu.y, 0x00);
|
||||||
|
});
|
||||||
|
|
||||||
|
// rom_test!(even_odd, "even_odd.nes", |nes| {
|
||||||
|
// assert_eq!(nes.last_instruction, "0x8023 HLT :2 []");
|
||||||
|
// assert_eq!(nes.cycle, 57182);
|
||||||
|
// assert_eq!(nes.cpu.pc, 0x8024);
|
||||||
|
// assert_eq!(nes.ppu.pixel, 25);
|
||||||
|
|
||||||
|
// assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
|
// assert_eq!(nes.cpu.a, 0x00);
|
||||||
|
// assert_eq!(nes.cpu.x, 0x08);
|
||||||
|
// assert_eq!(nes.cpu.y, 0x00);
|
||||||
|
// });
|
||||||
|
|||||||
56
src/test_roms/palette_test.asm.old
Normal file
56
src/test_roms/palette_test.asm.old
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
.inesprg 2 ; 2 banks
|
||||||
|
.ineschr 1 ;
|
||||||
|
.inesmap 0 ; mapper 0 = NROM
|
||||||
|
.inesmir 0 ; background mirroring, horizontal
|
||||||
|
|
||||||
|
; .org $0000 ; "ZEROPAGE"
|
||||||
|
ppu_emphasis = $0000
|
||||||
|
color = $0001
|
||||||
|
temp = $0002
|
||||||
|
gamepad = $0003
|
||||||
|
gamepad_last = $0004
|
||||||
|
|
||||||
|
; .org $0200 ; "OAM"
|
||||||
|
; .assert ((* & $FF) = 0),error,"oam not aligned to page"
|
||||||
|
oam = $0200
|
||||||
|
|
||||||
|
.org $8000
|
||||||
|
RESET:
|
||||||
|
sei ; Ignore IRQs while starting up
|
||||||
|
cld ; disabled decimal mode (iirc it doesn't work properly on NES anyway)
|
||||||
|
ldx #$40
|
||||||
|
stx $4017 ; Disable APU frame IRQ
|
||||||
|
ldx #$ff
|
||||||
|
txs ; Set stack pointer to 0x1ff
|
||||||
|
inx ; Set x to zero
|
||||||
|
stx $2000 ; Disable NMI (by writing zero)
|
||||||
|
stx $4010 ; Disable DMC IRQs
|
||||||
|
ldx #$08
|
||||||
|
stx $2001 ; Disable rendering
|
||||||
|
|
||||||
|
bit $2002 ; Clear vblank flag by reading ppu status
|
||||||
|
VBLANKWAIT1:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT1
|
||||||
|
VBLANKWAIT2:
|
||||||
|
bit $2002
|
||||||
|
bpl VBLANKWAIT2
|
||||||
|
hlt
|
||||||
|
hlt
|
||||||
|
|
||||||
|
ERROR_:
|
||||||
|
hlt
|
||||||
|
|
||||||
|
IGNORE:
|
||||||
|
rti
|
||||||
|
|
||||||
|
|
||||||
|
.org $FFFA ; Interrupt vectors go here:
|
||||||
|
.word nmi; NMI
|
||||||
|
.word reset ; Reset
|
||||||
|
.word irq; IRQ
|
||||||
|
|
||||||
|
;;;; NESASM COMPILER STUFF, ADDING THE PATTERN DATA ;;;;
|
||||||
|
|
||||||
|
.incbin "Sprites.pcx"
|
||||||
|
.incbin "Tiles.pcx"
|
||||||
Reference in New Issue
Block a user