diff --git a/src/debugger.rs b/src/debugger.rs index 41e4782..9bb8b33 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -107,6 +107,17 @@ impl DebuggerState { labelled_box("BG 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![ run_type( "PPU Cycles:", @@ -190,7 +201,7 @@ impl DebuggerState { DebuggerMessage::RunToScanLine => { 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::Pause => todo!(), } @@ -205,7 +216,7 @@ fn run_type<'a, Message: Clone + 'a>( ) -> Element<'a, Message> { row![ 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)) .padding(2.), ] diff --git a/src/main.rs b/src/main.rs index 570f73c..1a7f538 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashMap, - fmt, -}; +use std::{collections::HashMap, fmt}; use iced::{ Color, Element, Font, @@ -15,11 +12,14 @@ use iced::{ window::{self, Id, Settings}, }; 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; -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"; extern crate nes_emu; @@ -45,6 +45,7 @@ enum WindowType { Memory(MemoryTy, HexView), TileMap, TileViewer, + Palette, Debugger, } @@ -93,8 +94,7 @@ enum Message { impl Emulator { fn new() -> (Self, Task) { - let mut nes = nes_emu::NES::load_nes_file(ROM_FILE) - .expect("Failed to load nes file"); + let mut nes = nes_emu::NES::load_nes_file(ROM_FILE).expect("Failed to load nes file"); nes.reset(); let (win, task) = iced::window::open(Settings::default()); ( @@ -112,6 +112,7 @@ impl Emulator { Some(WindowType::Memory(_, _)) => "NES MemoryView".into(), Some(WindowType::TileMap) => "NES TileMap".into(), Some(WindowType::TileViewer) => "NES Tile Viewer".into(), + Some(WindowType::Palette) => "NES Palette Viewer".into(), Some(WindowType::Debugger) => "NES Debugger".into(), None => todo!(), } @@ -126,42 +127,48 @@ impl Emulator { fn update(&mut self, message: Message) -> Task { match message { Message::Tick(count) => { - for _ in 0..count { - self.nes.run_one_clock_cycle(); - } - } + for _ in 0..count { + self.nes.run_one_clock_cycle(); + } + } Message::Frame => while !self.nes.run_one_clock_cycle().ppu_frame {}, Message::DMA => while !self.nes.run_one_clock_cycle().dma {}, Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {}, Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {}, Message::WindowOpened(_id) => { - // Window - } + // Window + } Message::WindowClosed(id) => { - if let Some(WindowType::Main) = self.windows.remove(&id) { - return iced::exit(); - } - } + if let Some(WindowType::Main) = self.windows.remove(&id) { + return iced::exit(); + } + } Message::Header(HeaderButton::OpenMemory) => { - return self.open(WindowType::Memory(MemoryTy::Cpu, HexView::new())); - } + return self.open(WindowType::Memory(MemoryTy::Cpu, HexView::new())); + } Message::Header(HeaderButton::OpenTileMap) => { - return self.open(WindowType::TileMap); - } + return self.open(WindowType::TileMap); + } Message::Header(HeaderButton::OpenTileViewer) => { - return self.open(WindowType::TileViewer); - } + return self.open(WindowType::TileViewer); + } Message::Header(HeaderButton::OpenDebugger) => { - return self.open(WindowType::Debugger); - } + return self.open(WindowType::Debugger); + } Message::Hex(id, val) => { - if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) { - return view.update(val).map(move |e| Message::Hex(id, e)); - } - } - Message::Header(HeaderButton::Reset) => { self.nes.reset(); } - Message::Header(HeaderButton::PowerCycle) => { self.nes.power_cycle(); } - Message::Debugger(debugger_message) => self.debugger.update(debugger_message, &mut self.nes), + if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) { + return view.update(val).map(move |e| Message::Hex(id, e)); + } + } + Message::Header(HeaderButton::Reset) => { + self.nes.reset(); + } + 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()); Task::none() @@ -193,13 +200,28 @@ impl Emulator { container(content).width(Fill).height(Fill).into() } 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) => { - 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) => { - 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"), // _ => todo!(), @@ -231,10 +253,7 @@ impl Emulator { row![ header_menu( "Console", - [ - HeaderButton::Reset, - HeaderButton::PowerCycle, - ], + [HeaderButton::Reset, HeaderButton::PowerCycle,], Message::Header ), header_menu( @@ -290,6 +309,7 @@ impl Program for Emulator { enum DbgImage<'a> { NameTable(&'a PPU), PatternTable(&'a PPU), + Palette(&'a PPU), } impl Program for DbgImage<'_> { @@ -310,6 +330,7 @@ impl Program for DbgImage<'_> { match self { DbgImage::NameTable(ppu) => ppu.render_name_table(&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()] } diff --git a/src/ppu.rs b/src/ppu.rs index 8eb0c24..defb357 100644 --- a/src/ppu.rs +++ b/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)] pub struct Color { @@ -91,6 +98,15 @@ pub struct Mask { 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 { // registers: PPURegisters, frame_count: usize, @@ -104,6 +120,7 @@ pub struct PPU { pub vblank: bool, pub(crate) memory: MemoryMap, + palette: Palette, background: Background, oam: OAM, pub render_buffer: RenderBuffer<256, 240>, @@ -145,11 +162,12 @@ impl PPU { em_green: false, em_blue: false, }, + palette: Palette { colors: COLORS, ram: [0; _] }, vblank: false, frame_count: 0, nmi_enabled: false, nmi_waiting: false, - even: true, // ?? + even: false, scanline: 0, pixel: 25, render_buffer: RenderBuffer::empty(), @@ -157,7 +175,8 @@ impl PPU { 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, 0x0100, PPUMMRegisters::Palette), + Segment::reg("Palette Control", 0x3F00, 0x0020, PPUMMRegisters::Palette), + Segment::mirror("Mirror of Palette", 0x3F20, 0x00E0, 3), ]), background: Background { 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 { match offset { 0 => panic!("ppuctrl is write-only"), @@ -190,7 +213,6 @@ impl PPU { let tmp = if self.vblank { 0b1000_0000 } else { 0 }; self.vblank = false; self.background.w = false; - println!("Reading status: {:02X}", tmp); tmp } 3 => panic!("oamaddr is write-only"), @@ -227,7 +249,7 @@ impl PPU { self.mask.background_on_left_edge = val & 0b0000_0010 != 0; self.mask.sprites_on_left_edge = val & 0b0000_0100 != 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_green = val & 0b0100_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 { self.cycle += 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.pixel = 0; self.even = !self.even; @@ -388,10 +412,22 @@ impl PPU { 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 }, - } + (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, + }, + }, ); } } @@ -409,14 +445,29 @@ impl PPU { let high = self.memory.peek(name + y_off + 8).unwrap(); for bit in 0..8 { 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.), 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 }, - } + (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, + }, + }, ); } } @@ -431,14 +482,29 @@ impl PPU { let high = self.memory.peek(name + y_off + 8 + 0x1000).unwrap(); for bit in 0..8 { 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.), 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 }, - } + (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, + }, + }, ); } } @@ -447,6 +513,17 @@ impl PPU { } } } + pub fn render_palette(&self, frame: &mut Frame) { + frame.fill_rectangle( + Point::new(0., 0.), + Size::new(10., 10.), + Color { + r: todo!(), + g: todo!(), + b: todo!(), + }, + ); + } } #[cfg(test)] diff --git a/src/test_roms/basic_init_2.asm b/src/test_roms/basic_init_2.asm new file mode 100644 index 0000000..2545655 --- /dev/null +++ b/src/test_roms/basic_init_2.asm @@ -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" diff --git a/src/test_roms/basic_init_3.asm b/src/test_roms/basic_init_3.asm new file mode 100644 index 0000000..610edae --- /dev/null +++ b/src/test_roms/basic_init_3.asm @@ -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" diff --git a/src/test_roms/even_odd.asm b/src/test_roms/even_odd.asm new file mode 100644 index 0000000..cae4467 --- /dev/null +++ b/src/test_roms/even_odd.asm @@ -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" diff --git a/src/test_roms/mod.rs b/src/test_roms/mod.rs index 1f302e3..905d739 100644 --- a/src/test_roms/mod.rs +++ b/src/test_roms/mod.rs @@ -15,6 +15,20 @@ macro_rules! rom_test { $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| { @@ -23,6 +37,8 @@ rom_test!(basic_cpu, "basic-cpu.nes", |nes| { assert_eq!(nes.cycle, 11); // This is off by one from Mesen, because Mesen is left pointing at the 'invalid' opcode 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.a, 0x00); @@ -35,6 +51,7 @@ rom_test!(read_write, "read_write.nes", |nes| { assert_eq!(nes.cycle, 31); assert_eq!(nes.cpu.pc, 0x8012); assert_eq!(nes.cpu.sp, 0xFD); + assert_eq!(nes.cpu.a, 0xAA); assert_eq!(nes.cpu.x, 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.cpu.pc, 0x8018); assert_eq!(nes.cpu.sp, 0xFF); + assert_eq!(nes.cpu.a, 0x00); assert_eq!(nes.cpu.x, 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| { assert_eq!(nes.last_instruction, "0x801C HLT :2 []"); assert_eq!(nes.cycle, 27403); - assert_eq!(nes.ppu.pixel, 30); assert_eq!(nes.cpu.pc, 0x801D); + assert_eq!(nes.ppu.pixel, 30); + 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_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); +// }); diff --git a/src/test_roms/palette_test.asm.old b/src/test_roms/palette_test.asm.old new file mode 100644 index 0000000..e1a3b27 --- /dev/null +++ b/src/test_roms/palette_test.asm.old @@ -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"