refractoring of NES
This commit is contained in:
25
src/main.rs
25
src/main.rs
@@ -9,13 +9,10 @@ extern crate rand;
|
|||||||
|
|
||||||
mod nes;
|
mod nes;
|
||||||
|
|
||||||
|
use crate::nes::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use piston_window::*;
|
use piston_window::*;
|
||||||
use nes::NES;
|
|
||||||
use nes::types::*;
|
|
||||||
use nes::cpu::*;
|
use nes::cpu::*;
|
||||||
use nes::bus::*;
|
|
||||||
use nes::cartridge::*;
|
|
||||||
use nes::disasm::*;
|
use nes::disasm::*;
|
||||||
use opengl_graphics::OpenGL;
|
use opengl_graphics::OpenGL;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
@@ -42,12 +39,12 @@ fn main() -> Result<(), Error> {
|
|||||||
let mut nes = NES::new();
|
let mut nes = NES::new();
|
||||||
let cartridge = Path::new("test_roms/nestest.nes");
|
let cartridge = Path::new("test_roms/nestest.nes");
|
||||||
let cartridge = Cartridge::new(cartridge)?;
|
let cartridge = Cartridge::new(cartridge)?;
|
||||||
nes.insert_cartrige(cartridge);
|
nes.insert_cartridge(cartridge);
|
||||||
nes.cpu.find_pc_addr(&nes.bus);
|
nes.start();
|
||||||
// nes.cpu.regs.pc = 0xC000;
|
// nes.cpu.regs.pc = 0xC000;
|
||||||
|
|
||||||
// disassemble instructions
|
// disassemble instructions
|
||||||
let disasm = Disasm::disassemble(&nes.bus, 0xC000, 0xFFFF).unwrap();
|
let disasm = Disasm::disassemble(&nes.memory, 0xC000, 0xFFFF).unwrap();
|
||||||
|
|
||||||
// Prepare window and drawing resources
|
// Prepare window and drawing resources
|
||||||
let mut window: PistonWindow = WindowSettings::new("xXx NESemu xXx", [256*3, 240*2+50])
|
let mut window: PistonWindow = WindowSettings::new("xXx NESemu xXx", [256*3, 240*2+50])
|
||||||
@@ -92,7 +89,7 @@ fn main() -> Result<(), Error> {
|
|||||||
let transform = c.transform.trans(256.0, 50.0).scale(2.0, 2.0);
|
let transform = c.transform.trans(256.0, 50.0).scale(2.0, 2.0);
|
||||||
image(&texture, transform, g);
|
image(&texture, transform, g);
|
||||||
});
|
});
|
||||||
render_debug(&mut window, &event, &mut glyph_brush, &nes.cpu, &nes.bus, &disasm);
|
render_debug(&mut window, &event, &mut glyph_brush, &nes.cpu, &nes, &disasm);
|
||||||
}
|
}
|
||||||
if let Some(Button::Keyboard(key)) = event.press_args() {
|
if let Some(Button::Keyboard(key)) = event.press_args() {
|
||||||
match key {
|
match key {
|
||||||
@@ -103,7 +100,7 @@ fn main() -> Result<(), Error> {
|
|||||||
nes.clock();
|
nes.clock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Key::R => nes.cpu.reset(&mut nes.bus),
|
Key::R => nes.reset(),
|
||||||
Key::Space => run = !run,
|
Key::Space => run = !run,
|
||||||
Key::Up => {
|
Key::Up => {
|
||||||
event_settings.ups = (event_settings.ups as f64 * 1.1) as u64 + 1;
|
event_settings.ups = (event_settings.ups as f64 * 1.1) as u64 + 1;
|
||||||
@@ -124,7 +121,7 @@ fn main() -> Result<(), Error> {
|
|||||||
|
|
||||||
fn render_debug(window: &mut PistonWindow, event: &Event,
|
fn render_debug(window: &mut PistonWindow, event: &Event,
|
||||||
glyphs: &mut GlyphBrush<Resources, Factory>,
|
glyphs: &mut GlyphBrush<Resources, Factory>,
|
||||||
cpu: &CPU, _bus: &Bus, disasm: &Disasm) {
|
cpu: &CPU, _nes: &NES, disasm: &Disasm) {
|
||||||
|
|
||||||
|
|
||||||
window.draw_2d(event, |_c, _g, _d| {
|
window.draw_2d(event, |_c, _g, _d| {
|
||||||
@@ -132,7 +129,7 @@ fn render_debug(window: &mut PistonWindow, event: &Event,
|
|||||||
render_cpu(glyphs, cpu, debug_offset);
|
render_cpu(glyphs, cpu, debug_offset);
|
||||||
render_disasm(glyphs, disasm, cpu.regs.pc,
|
render_disasm(glyphs, disasm, cpu.regs.pc,
|
||||||
[debug_offset[0], debug_offset[1] + (7.0 * (FT_LINE_DISTANCE+FT_SIZE_PX))]);
|
[debug_offset[0], debug_offset[1] + (7.0 * (FT_LINE_DISTANCE+FT_SIZE_PX))]);
|
||||||
// render_memory(glyphs, bus,
|
// render_memory(glyphs, nes,
|
||||||
// [debug_offset[0] + 400.0, debug_offset[1]]);
|
// [debug_offset[0] + 400.0, debug_offset[1]]);
|
||||||
});
|
});
|
||||||
glyphs.use_queue().draw(&mut window.encoder, &window.output_color).unwrap();
|
glyphs.use_queue().draw(&mut window.encoder, &window.output_color).unwrap();
|
||||||
@@ -289,13 +286,13 @@ fn render_disasm(glyphs: &mut GlyphBrush<Resources, Factory>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_memory(glyphs: &mut GlyphBrush<Resources, Factory>, bus: &Bus, offset: [f32; 2]) {
|
fn render_memory(glyphs: &mut GlyphBrush<Resources, Factory>, nes: &NES, offset: [f32; 2]) {
|
||||||
let mut position_y = (offset[0], offset[1]);
|
let mut position_y = (offset[0], offset[1]);
|
||||||
for page in (0x0000..0x00FF).step_by(16) {
|
for page in (0x0000..0x00FF).step_by(16) {
|
||||||
position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX;
|
position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX;
|
||||||
let mut line = format!("{:#06x}:", page);
|
let mut line = format!("{:#06x}:", page);
|
||||||
(0u16..16u16).map(|offset| offset + page)
|
(0u16..16u16).map(|offset| offset + page)
|
||||||
.map(|addr| bus.readb(addr))
|
.map(|addr| nes.memory.readb(addr))
|
||||||
.map(|val| format!(" {:02x}", val))
|
.map(|val| format!(" {:02x}", val))
|
||||||
.for_each(|s| line.push_str(&s));
|
.for_each(|s| line.push_str(&s));
|
||||||
glyphs.queue(Section {
|
glyphs.queue(Section {
|
||||||
@@ -312,7 +309,7 @@ fn render_memory(glyphs: &mut GlyphBrush<Resources, Factory>, bus: &Bus, offset:
|
|||||||
position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX;
|
position_y.1 += FT_LINE_DISTANCE + FT_SIZE_PX;
|
||||||
let mut line = format!("{:#06x}:", page);
|
let mut line = format!("{:#06x}:", page);
|
||||||
(0u16..16u16).map(|offset| offset + page)
|
(0u16..16u16).map(|offset| offset + page)
|
||||||
.map(|addr| bus.readb(addr))
|
.map(|addr| nes.memory.readb(addr))
|
||||||
.map(|val| format!(" {:02x}", val))
|
.map(|val| format!(" {:02x}", val))
|
||||||
.for_each(|s| line.push_str(&s));
|
.for_each(|s| line.push_str(&s));
|
||||||
glyphs.queue(Section {
|
glyphs.queue(Section {
|
||||||
|
|||||||
67
src/nes.rs
67
src/nes.rs
@@ -1,46 +1,87 @@
|
|||||||
use crate::nes::bus::*;
|
pub use crate::nes::cartridge::Cartridge;
|
||||||
use crate::nes::cartridge::Cartridge;
|
pub use crate::nes::ppu::PPU;
|
||||||
use crate::nes::ppu::*;
|
pub use crate::nes::cpu::CPU;
|
||||||
use crate::nes::cpu::*;
|
pub use crate::nes::types::*;
|
||||||
|
pub use crate::nes::memory::*;
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod bus;
|
pub mod memory;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod disasm;
|
pub mod disasm;
|
||||||
pub mod cartridge;
|
pub mod cartridge;
|
||||||
pub mod mappers;
|
pub mod mappers;
|
||||||
pub mod ppu;
|
pub mod ppu;
|
||||||
|
|
||||||
|
// The NES class connects all elements of the NES together. It acts
|
||||||
|
// as the mediator between the different components and hold the RAM
|
||||||
pub struct NES {
|
pub struct NES {
|
||||||
pub bus: Bus,
|
|
||||||
pub cpu: CPU,
|
pub cpu: CPU,
|
||||||
pub ppu: PPU,
|
pub ppu: PPU,
|
||||||
|
pub memory: NESMemory,
|
||||||
pub clock_count: u64,
|
pub clock_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NES {
|
impl NES {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
NES {
|
NES {
|
||||||
bus: Bus::new(),
|
|
||||||
cpu: CPU::new(),
|
cpu: CPU::new(),
|
||||||
ppu: PPU::new(),
|
ppu: PPU::new(),
|
||||||
|
memory: NESMemory::new(),
|
||||||
clock_count: 0,
|
clock_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_cartrige(&mut self, cartrige: Cartridge) {
|
pub fn insert_cartridge(&mut self, cartridge: Cartridge) {
|
||||||
self.bus.insert_cartrige(cartrige);
|
self.memory.insert_cartridge(cartridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
self.cpu.find_pc_addr(&self.memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.clock_count = 0;
|
||||||
|
self.cpu.reset(&self.memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clock(&mut self) {
|
pub fn clock(&mut self) {
|
||||||
self.clock_count += 1;
|
self.clock_count += 1;
|
||||||
if self.clock_count % 3 == 0 {
|
if self.clock_count % 3 == 0 {
|
||||||
self.cpu.clock(&mut self.bus);
|
self.cpu.clock(&mut self.memory);
|
||||||
}
|
}
|
||||||
self.ppu.clock(&mut self.bus);
|
self.ppu.clock(&mut self.memory);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// impl PPUMemory for NES {
|
||||||
|
// fn readb_ppu(&self, addr: Addr) -> Byte {
|
||||||
|
// if let Some(cartridge) = &self.cartridge {
|
||||||
|
// if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
||||||
|
// return cartridge.readb(addr)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
||||||
|
// // Ram is 3x mirrored after 07ff
|
||||||
|
// return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
||||||
|
// }
|
||||||
|
// 0x0000 // generic response
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn writeb_ppu(&mut self, addr: Addr, data: Byte) {
|
||||||
|
// if let Some(cartridge) = &mut self.cartridge {
|
||||||
|
// if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
||||||
|
// cartridge.writeb(addr, data)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
||||||
|
// // Ram is 3x mirrored after 07ff
|
||||||
|
// self.ram[(addr & RAM_PHYS_RANGE[1]) as usize] = data
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
204
src/nes/bus.rs
204
src/nes/bus.rs
@@ -1,204 +0,0 @@
|
|||||||
use crate::nes::types::*;
|
|
||||||
use crate::nes::cartridge::Cartridge;
|
|
||||||
|
|
||||||
const RAM_ADDR_RANGE: [Addr; 2] = [0x0000, 0x1fff];
|
|
||||||
const RAM_PHYS_RANGE: [Addr; 2] = [0x0000, 0x07ff];
|
|
||||||
const PPU_ADDR_RANGE: [Addr; 2] = [0x2000, 0x3fff];
|
|
||||||
const PPU_PHYS_RANGE: [Addr; 2] = [0x2000, 0x2007];
|
|
||||||
const CART_ADDR_RANGE: [Addr; 2] = [0x4020, 0xffff];
|
|
||||||
|
|
||||||
// Generic interface for a device allowing to read/write memory
|
|
||||||
pub trait Memory {
|
|
||||||
fn readb(&self, addr: Addr) -> Byte;
|
|
||||||
fn writeb(&mut self, addr: Addr, data: Byte);
|
|
||||||
fn readw(&self, addr: Addr) -> Word {
|
|
||||||
let lo = self.readb(addr);
|
|
||||||
let hi = self.readb(addr+1);
|
|
||||||
(hi as Word) << 8 | lo as Word
|
|
||||||
}
|
|
||||||
fn writew(&mut self, addr: Addr, data: Word) {
|
|
||||||
self.writeb(addr, data as Byte);
|
|
||||||
self.writeb(addr + 1, (data >> 8) as Byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PPU interface to allow read/write of memory
|
|
||||||
pub trait PPUMemory {
|
|
||||||
fn readb_ppu(&self, addr: Addr) -> Byte;
|
|
||||||
fn writeb_ppu(&mut self, addr: Addr, data: Byte);
|
|
||||||
fn readw_ppu(&self, addr: Addr) -> Word {
|
|
||||||
let lo = self.readb_ppu(addr);
|
|
||||||
let hi = self.readb_ppu(addr+1);
|
|
||||||
(hi as Word) << 8 | lo as Word
|
|
||||||
}
|
|
||||||
fn writew_ppu(&mut self, addr: Addr, data: Word) {
|
|
||||||
self.writeb_ppu(addr, data as Byte);
|
|
||||||
self.writeb_ppu(addr + 1, (data >> 8) as Byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Impl by devices to access the Bus
|
|
||||||
pub trait BusDevice {
|
|
||||||
fn readb<T: Memory>(&self, bus: &T, addr: Addr) -> Byte {
|
|
||||||
bus.readb(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readw<T: Memory>(&self, bus: &T, addr: Addr) -> Word {
|
|
||||||
bus.readw(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeb<T: Memory>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
|
|
||||||
bus.writeb(addr, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Impl by devices to access the Bus
|
|
||||||
pub trait PPUBusDevice {
|
|
||||||
fn readb<T: PPUMemory>(&self, bus: &T, addr: Addr) -> Byte {
|
|
||||||
bus.readb_ppu(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readw<T: PPUMemory>(&self, bus: &T, addr: Addr) -> Word {
|
|
||||||
bus.readw_ppu(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeb<T: PPUMemory>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
|
|
||||||
bus.writeb_ppu(addr, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Impl by devices to do stuff on bus clock
|
|
||||||
pub trait Clockable {
|
|
||||||
fn clock<T: Memory>(&mut self, bus: &mut T);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// A simple bus giving access to a chunk of memory
|
|
||||||
// and the cartrige
|
|
||||||
pub struct Bus {
|
|
||||||
ram: [Byte; 0x0800], // 2kb
|
|
||||||
cartrige: Option<Cartridge>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Bus {
|
|
||||||
pub fn new() -> Bus {
|
|
||||||
Bus {
|
|
||||||
ram: [0; 0x0800],
|
|
||||||
cartrige: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_cartrige(&mut self, cartrige: Cartridge) {
|
|
||||||
self.cartrige = Some(cartrige);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Memory for Bus {
|
|
||||||
|
|
||||||
fn readb(&self, addr: Addr) -> Byte {
|
|
||||||
if let Some(cartrige) = &self.cartrige {
|
|
||||||
if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
|
||||||
return cartrige.readb(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
|
||||||
// Ram is 3x mirrored after 0x07ff
|
|
||||||
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
|
||||||
}
|
|
||||||
// if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
|
||||||
// // PPU memory is mirrored after 0x2007 to 0x3fff
|
|
||||||
// return self.ram[(addr & PPU_PHYS_RANGE[1]) as usize]
|
|
||||||
// }
|
|
||||||
0x0000 // generic response
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeb(&mut self, addr: Addr, data: Byte) {
|
|
||||||
if let Some(cartrige) = &mut self.cartrige {
|
|
||||||
if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
|
||||||
cartrige.writeb(addr, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
|
||||||
// Ram is 3x mirrored after 07ff
|
|
||||||
self.ram[(addr & RAM_PHYS_RANGE[1]) as usize] = data
|
|
||||||
}
|
|
||||||
// if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
|
||||||
// // PPU memory is mirrored after 0x2007 to 0x3fff
|
|
||||||
// self.ram[(addr & PPU_PHYS_RANGE[1]) as usize] = data
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PPUMemory for Bus {
|
|
||||||
fn readb_ppu(&self, addr: Addr) -> Byte {
|
|
||||||
if let Some(cartrige) = &self.cartrige {
|
|
||||||
if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
|
||||||
return cartrige.readb(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
|
||||||
// Ram is 3x mirrored after 07ff
|
|
||||||
return self.ram[(addr & 0x07ff) as usize]
|
|
||||||
}
|
|
||||||
0x0000 // generic response
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeb_ppu(&mut self, addr: Addr, data: Byte) {
|
|
||||||
if let Some(cartrige) = &mut self.cartrige {
|
|
||||||
if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
|
||||||
cartrige.writeb(addr, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
|
||||||
// Ram is 3x mirrored after 07ff
|
|
||||||
self.ram[(addr & 0x07ff) as usize] = data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_consts() {
|
|
||||||
assert_eq!(RAM_ADDR_RANGE[1] - RAM_ADDR_RANGE[0] + 1, 2048*4);
|
|
||||||
assert_eq!(RAM_PHYS_RANGE[1] - RAM_PHYS_RANGE[0] + 1, 2048);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_write_ram() {
|
|
||||||
let mut bus = Bus::new();
|
|
||||||
|
|
||||||
// read/write to ram addr
|
|
||||||
bus.writeb(0x0000, 1);
|
|
||||||
assert_eq!(1, bus.readb(0x0000));
|
|
||||||
assert_eq!(1, bus.readb(0x0800));
|
|
||||||
|
|
||||||
// read/write to mirrored ram
|
|
||||||
bus.writeb(0x0801, 2);
|
|
||||||
assert_eq!(2, bus.readb(0x0801));
|
|
||||||
assert_eq!(2, bus.readb(0x0001));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_write_cartrige() {
|
|
||||||
let mut bus = Bus::new();
|
|
||||||
|
|
||||||
// read/write non-existant to cartrige
|
|
||||||
bus.writeb(0x4030, 3);
|
|
||||||
assert_eq!(0, bus.readb(0x4030));
|
|
||||||
|
|
||||||
let cartrige = Cartridge::dummy();
|
|
||||||
bus.insert_cartrige(cartrige);
|
|
||||||
bus.writeb(0x4030, 3);
|
|
||||||
assert_eq!(3, bus.readb(0x4030));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::nes::mappers::*;
|
use crate::nes::mappers::*;
|
||||||
use crate::nes::bus::Memory;
|
use crate::nes::Memory;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|||||||
448
src/nes/cpu.rs
448
src/nes/cpu.rs
@@ -1,11 +1,11 @@
|
|||||||
|
use crate::nes::{Memory,MemoryReader};
|
||||||
|
use crate::nes::types::*;
|
||||||
|
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
|
|
||||||
use instructions::{Instruction,Operation,AddrMode};
|
use instructions::{Instruction,Operation,AddrMode};
|
||||||
use core::fmt::{Debug,Formatter,Result};
|
use core::fmt::{Debug,Formatter,Result};
|
||||||
use crate::nes::bus::*;
|
|
||||||
use crate::nes::types::*;
|
|
||||||
use log::{debug};
|
use log::{debug};
|
||||||
use failure::err_msg;
|
|
||||||
|
|
||||||
pub struct Registers {
|
pub struct Registers {
|
||||||
pub a: Byte,
|
pub a: Byte,
|
||||||
@@ -58,31 +58,8 @@ pub struct CPU {
|
|||||||
stopped: bool,
|
stopped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default implementation to read/write from bus
|
// Default implementation to read/write from mem
|
||||||
impl BusDevice for CPU { }
|
impl MemoryReader for CPU { }
|
||||||
|
|
||||||
// A cpu is clockable
|
|
||||||
impl Clockable for CPU {
|
|
||||||
fn clock<T: Memory>(&mut self, bus: &mut T) {
|
|
||||||
// if processor is halted, we do nothing anymore
|
|
||||||
if self.stopped {
|
|
||||||
panic!("Stopped processor clock'ed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.cycles_ahead == 0 {
|
|
||||||
let opcode = self.readb_pc(bus);
|
|
||||||
self.curr_op = opcode;
|
|
||||||
let instruction = Instruction::decode_op(opcode);
|
|
||||||
|
|
||||||
info!("{:#06X} {:02X} {} A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{}",
|
|
||||||
self.regs.pc-1, opcode, instruction.operation,
|
|
||||||
self.regs.a, self.regs.x, self.regs.y, self.regs.flags, self.regs.sp, self.cycles);
|
|
||||||
self.cycles_ahead += self.run_instruction(bus, instruction);
|
|
||||||
}
|
|
||||||
self.cycles_ahead -= 1;
|
|
||||||
self.cycles += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -95,25 +72,45 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clock<T: Memory>(&mut self, mem: &mut T) {
|
||||||
|
// if processor is halted, we do nothing anymore
|
||||||
|
if self.stopped {
|
||||||
|
panic!("Stopped processor clock'ed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.cycles_ahead == 0 {
|
||||||
|
let opcode = self.readb_pc(mem);
|
||||||
|
self.curr_op = opcode;
|
||||||
|
let instruction = Instruction::decode_op(opcode);
|
||||||
|
|
||||||
|
info!("{:#06X} {:02X} {} A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{}",
|
||||||
|
self.regs.pc-1, opcode, instruction.operation,
|
||||||
|
self.regs.a, self.regs.x, self.regs.y, self.regs.flags, self.regs.sp, self.cycles);
|
||||||
|
self.cycles_ahead += self.run_instruction(mem, instruction);
|
||||||
|
}
|
||||||
|
self.cycles_ahead -= 1;
|
||||||
|
self.cycles += 1
|
||||||
|
}
|
||||||
|
|
||||||
// sets PC
|
// sets PC
|
||||||
pub fn find_pc_addr<T: Memory>(&mut self, bus: &T) {
|
pub fn find_pc_addr<T: Memory>(&mut self, mem: &T) {
|
||||||
// 0xfffc and 0xfffc+1 stores the location of the first op code (where
|
// 0xfffc and 0xfffc+1 stores the location of the first op code (where
|
||||||
// the program starts). Read it and set pc accordingly.
|
// the program starts). Read it and set pc accordingly.
|
||||||
let addr: u16 = 0xfffc;
|
let addr: u16 = 0xfffc;
|
||||||
let lo = bus.readb(addr);
|
let lo = mem.readb(addr);
|
||||||
let hi = bus.readb(addr + 1);
|
let hi = mem.readb(addr + 1);
|
||||||
self.regs.pc = (hi as u16) << 8 | lo as u16;
|
self.regs.pc = (hi as u16) << 8 | lo as u16;
|
||||||
debug!("PC set to: {:#06x}", self.regs.pc);
|
debug!("PC set to: {:#06x}", self.regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Brings the CPU to a known state. Resets all registers and flags
|
// Brings the CPU to a known state. Resets all registers and flags
|
||||||
// Read location of pc from 0xfffc
|
// Read location of pc from 0xfffc
|
||||||
pub fn reset<T: Memory>(&mut self, bus: &T) {
|
pub fn reset<T: Memory>(&mut self, mem: &T) {
|
||||||
// reset registers
|
// reset registers
|
||||||
self.regs.sp = self.regs.sp - 3;
|
self.regs.sp = self.regs.sp - 3;
|
||||||
self.set_flag(IRQ, true);
|
self.set_flag(IRQ, true);
|
||||||
|
|
||||||
self.find_pc_addr(bus);
|
self.find_pc_addr(mem);
|
||||||
|
|
||||||
// A reset takes 8 CPU clocks
|
// A reset takes 8 CPU clocks
|
||||||
self.cycles = 8;
|
self.cycles = 8;
|
||||||
@@ -128,46 +125,29 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read the next opcode and increment pc
|
// read the next opcode and increment pc
|
||||||
fn readb_pc<T: Memory>(&mut self, bus: &T) -> Byte {
|
fn readb_pc<T: Memory>(&mut self, mem: &T) -> Byte {
|
||||||
let val = self.readb(bus, self.regs.pc);
|
let val = self.readb(mem, self.regs.pc);
|
||||||
self.regs.pc += 1;
|
self.regs.pc += 1;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
// read whole Word from pc
|
// read whole Word from pc
|
||||||
fn readw_pc<T: Memory>(&mut self, bus: &T) -> Word {
|
fn readw_pc<T: Memory>(&mut self, mem: &T) -> Word {
|
||||||
let val = self.readw(bus, self.regs.pc);
|
let val = self.readw(mem, self.regs.pc);
|
||||||
self.regs.pc += 2;
|
self.regs.pc += 2;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop a byte from the SP
|
// Pop a byte from the SP
|
||||||
fn popb_sp<T: Memory>(&mut self, bus: &T) -> Byte {
|
fn popb_sp<T: Memory>(&mut self, mem: &T) -> Byte {
|
||||||
self.regs.sp += 1;
|
self.regs.sp += 1;
|
||||||
let val = self.readb(bus, STACK_BASE_ADDR + self.regs.sp as Word);
|
let val = self.readb(mem, STACK_BASE_ADDR + self.regs.sp as Word);
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pop a word from the stack
|
|
||||||
fn popw_sp<T: Memory>(&mut self, bus: &T) -> Word {
|
|
||||||
let hi = self.popb_sp(bus);
|
|
||||||
let lo = self.popb_sp(bus);
|
|
||||||
(hi << 8) as Word & lo as Word
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push a byte to the SP.
|
// Push a byte to the SP.
|
||||||
fn pushb_sp<T: Memory>(&mut self, bus: &mut T, val: Byte) {
|
fn pushb_sp<T: Memory>(&mut self, mem: &mut T, val: Byte) {
|
||||||
self.writeb(bus, STACK_BASE_ADDR + self.regs.sp as Word, val);
|
self.writeb(mem, STACK_BASE_ADDR + self.regs.sp as Word, val);
|
||||||
self.regs.sp -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push a word to the stack, lo first, hi second
|
|
||||||
fn pushw_sp<T: Memory>(&mut self, bus: &mut T, val: Word) {
|
|
||||||
let lo = ((val >> 8) & LO) as Byte;
|
|
||||||
let hi = (val & LO) as Byte;
|
|
||||||
self.writeb(bus, STACK_BASE_ADDR + self.regs.sp as Word, lo);
|
|
||||||
self.regs.sp -= 1;
|
|
||||||
self.writeb(bus, STACK_BASE_ADDR + self.regs.sp as Word, hi);
|
|
||||||
self.regs.sp -= 1;
|
self.regs.sp -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,82 +178,82 @@ impl CPU {
|
|||||||
self.regs.pc = addr;
|
self.regs.pc = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_instruction<T: Memory>(&mut self, bus: &mut T, i: &Instruction) -> u8 {
|
fn run_instruction<T: Memory>(&mut self, mem: &mut T, i: &Instruction) -> u8 {
|
||||||
let (value, page_cross) = match &i.addr_mode {
|
let (value, page_cross) = match &i.addr_mode {
|
||||||
AddrMode::IMP => self.am_IMP(),
|
AddrMode::IMP => self.am_IMP(),
|
||||||
AddrMode::IMM => self.am_IMM(),
|
AddrMode::IMM => self.am_IMM(),
|
||||||
AddrMode::ZP0 => self.am_ZP0(bus),
|
AddrMode::ZP0 => self.am_ZP0(mem),
|
||||||
AddrMode::ZPX => self.am_ZPX(bus),
|
AddrMode::ZPX => self.am_ZPX(mem),
|
||||||
AddrMode::ZPY => self.am_ZPY(bus),
|
AddrMode::ZPY => self.am_ZPY(mem),
|
||||||
AddrMode::REL => self.am_REL(bus),
|
AddrMode::REL => self.am_REL(mem),
|
||||||
AddrMode::ABS => self.am_ABS(bus),
|
AddrMode::ABS => self.am_ABS(mem),
|
||||||
AddrMode::ABX => self.am_ABX(bus),
|
AddrMode::ABX => self.am_ABX(mem),
|
||||||
AddrMode::ABY => self.am_ABY(bus),
|
AddrMode::ABY => self.am_ABY(mem),
|
||||||
AddrMode::IND => self.am_IND(bus),
|
AddrMode::IND => self.am_IND(mem),
|
||||||
AddrMode::IZX => self.am_IZX(bus),
|
AddrMode::IZX => self.am_IZX(mem),
|
||||||
AddrMode::IZY => self.am_IZY(bus),
|
AddrMode::IZY => self.am_IZY(mem),
|
||||||
};
|
};
|
||||||
|
|
||||||
let extra_cycle_on_page_cross = match i.operation {
|
let extra_cycle_on_page_cross = match i.operation {
|
||||||
Operation::ADC => self.op_ADC(bus, value),
|
Operation::ADC => self.op_ADC(mem, value),
|
||||||
Operation::AND => self.op_AND(bus, value),
|
Operation::AND => self.op_AND(mem, value),
|
||||||
Operation::ASL => self.op_ASL(bus, value),
|
Operation::ASL => self.op_ASL(mem, value),
|
||||||
Operation::BCC => self.op_BCC(bus, value),
|
Operation::BCC => self.op_BCC(value),
|
||||||
Operation::BCS => self.op_BCS(value),
|
Operation::BCS => self.op_BCS(value),
|
||||||
Operation::BEQ => self.op_BEQ(bus, value),
|
Operation::BEQ => self.op_BEQ(value),
|
||||||
Operation::BIT => self.op_BIT(bus, value),
|
Operation::BIT => self.op_BIT(mem, value),
|
||||||
Operation::BMI => self.op_BMI(bus, value),
|
Operation::BMI => self.op_BMI(value),
|
||||||
Operation::BNE => self.op_BNE(value),
|
Operation::BNE => self.op_BNE(value),
|
||||||
Operation::BPL => self.op_BPL(bus, value),
|
Operation::BPL => self.op_BPL(value),
|
||||||
Operation::BRK => self.op_BRK(bus),
|
Operation::BRK => self.op_BRK(mem),
|
||||||
Operation::BVC => self.op_BVC(value),
|
Operation::BVC => self.op_BVC(value),
|
||||||
Operation::BVS => self.op_BVS(value),
|
Operation::BVS => self.op_BVS(value),
|
||||||
Operation::CLC => self.op_CLC(),
|
Operation::CLC => self.op_CLC(),
|
||||||
Operation::CLD => self.op_CLD(),
|
Operation::CLD => self.op_CLD(),
|
||||||
Operation::CLI => self.op_CLI(),
|
Operation::CLI => self.op_CLI(),
|
||||||
Operation::CLV => self.op_CLV(),
|
Operation::CLV => self.op_CLV(),
|
||||||
Operation::CMP => self.op_CMP(bus, value),
|
Operation::CMP => self.op_CMP(mem, value),
|
||||||
Operation::CPX => self.op_CPX(bus, value),
|
Operation::CPX => self.op_CPX(mem, value),
|
||||||
Operation::CPY => self.op_CPY(bus, value),
|
Operation::CPY => self.op_CPY(mem, value),
|
||||||
Operation::DCP => self.op_DCP(bus, value),
|
Operation::DCP => self.op_DCP(mem, value),
|
||||||
Operation::DEC => self.op_DEC(bus, value),
|
Operation::DEC => self.op_DEC(mem, value),
|
||||||
Operation::DEX => self.op_DEX(),
|
Operation::DEX => self.op_DEX(),
|
||||||
Operation::DEY => self.op_DEY(),
|
Operation::DEY => self.op_DEY(),
|
||||||
Operation::EOR => self.op_EOR(bus, value),
|
Operation::EOR => self.op_EOR(mem, value),
|
||||||
Operation::INC => self.op_INC(bus, value),
|
Operation::INC => self.op_INC(mem, value),
|
||||||
Operation::INX => self.op_INX(),
|
Operation::INX => self.op_INX(),
|
||||||
Operation::INY => self.op_INY(),
|
Operation::INY => self.op_INY(),
|
||||||
Operation::ISB => self.op_ISB(bus, value),
|
Operation::ISB => self.op_ISB(mem, value),
|
||||||
Operation::JMP => self.op_JMP(value),
|
Operation::JMP => self.op_JMP(value),
|
||||||
Operation::JSR => self.op_JSR(bus, value),
|
Operation::JSR => self.op_JSR(mem, value),
|
||||||
Operation::KIL => self.op_KIL(),
|
Operation::KIL => self.op_KIL(),
|
||||||
Operation::LAX => self.op_LAX(bus, value),
|
Operation::LAX => self.op_LAX(mem, value),
|
||||||
Operation::LDA => self.op_LDA(bus, value),
|
Operation::LDA => self.op_LDA(mem, value),
|
||||||
Operation::LDX => self.op_LDX(bus, value),
|
Operation::LDX => self.op_LDX(mem, value),
|
||||||
Operation::LDY => self.op_LDY(bus, value),
|
Operation::LDY => self.op_LDY(mem, value),
|
||||||
Operation::LSR => self.op_LSR(bus, value),
|
Operation::LSR => self.op_LSR(mem, value),
|
||||||
Operation::NOP => self.op_NOP(),
|
Operation::NOP => self.op_NOP(),
|
||||||
Operation::ORA => self.op_ORA(bus, value),
|
Operation::ORA => self.op_ORA(mem, value),
|
||||||
Operation::PHA => self.op_PHA(bus),
|
Operation::PHA => self.op_PHA(mem),
|
||||||
Operation::PHP => self.op_PHP(bus),
|
Operation::PHP => self.op_PHP(mem),
|
||||||
Operation::PLA => self.op_PLA(bus),
|
Operation::PLA => self.op_PLA(mem),
|
||||||
Operation::ROL => self.op_ROL(bus, value),
|
Operation::ROL => self.op_ROL(mem, value),
|
||||||
Operation::PLP => self.op_PLP(bus),
|
Operation::PLP => self.op_PLP(mem),
|
||||||
Operation::RLA => self.op_RLA(bus, value),
|
Operation::RLA => self.op_RLA(mem, value),
|
||||||
Operation::ROR => self.op_ROR(bus, value),
|
Operation::ROR => self.op_ROR(mem, value),
|
||||||
Operation::RRA => self.op_RRA(bus, value),
|
Operation::RRA => self.op_RRA(mem, value),
|
||||||
Operation::RTI => self.op_RTI(bus),
|
Operation::RTI => self.op_RTI(mem),
|
||||||
Operation::RTS => self.op_RTS(bus),
|
Operation::RTS => self.op_RTS(mem),
|
||||||
Operation::SAX => self.op_SAX(bus, value),
|
Operation::SAX => self.op_SAX(mem, value),
|
||||||
Operation::SBC => self.op_SBC(bus, value),
|
Operation::SBC => self.op_SBC(mem, value),
|
||||||
Operation::SEC => self.op_SEC(),
|
Operation::SEC => self.op_SEC(),
|
||||||
Operation::SED => self.op_SED(),
|
Operation::SED => self.op_SED(),
|
||||||
Operation::SEI => self.op_SEI(),
|
Operation::SEI => self.op_SEI(),
|
||||||
Operation::SLO => self.op_SLO(bus, value),
|
Operation::SLO => self.op_SLO(mem, value),
|
||||||
Operation::SRE => self.op_SRE(bus, value),
|
Operation::SRE => self.op_SRE(mem, value),
|
||||||
Operation::STA => self.op_STA(bus, value),
|
Operation::STA => self.op_STA(mem, value),
|
||||||
Operation::STX => self.op_STX(bus, value),
|
Operation::STX => self.op_STX(mem, value),
|
||||||
Operation::STY => self.op_STY(bus, value),
|
Operation::STY => self.op_STY(mem, value),
|
||||||
Operation::TAX => self.op_TAX(),
|
Operation::TAX => self.op_TAX(),
|
||||||
Operation::TAY => self.op_TAY(),
|
Operation::TAY => self.op_TAY(),
|
||||||
Operation::TSX => self.op_TSX(),
|
Operation::TSX => self.op_TSX(),
|
||||||
@@ -308,33 +288,33 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Absolute address on zero page
|
// Absolute address on zero page
|
||||||
fn am_ZP0<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ZP0<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let addr = self.readb_pc(bus);
|
let addr = self.readb_pc(mem);
|
||||||
(LO & addr as Word, false)
|
(LO & addr as Word, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Absolute address on zero page with x offset
|
// Absolute address on zero page with x offset
|
||||||
fn am_ZPX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ZPX<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let addr = self.readb_pc(bus).wrapping_add(self.regs.x);
|
let addr = self.readb_pc(mem).wrapping_add(self.regs.x);
|
||||||
(LO & addr as Word , false)
|
(LO & addr as Word , false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Absolute address on zero page with y offset
|
// Absolute address on zero page with y offset
|
||||||
fn am_ZPY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ZPY<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let addr = self.readb_pc(bus).wrapping_add(self.regs.y);
|
let addr = self.readb_pc(mem).wrapping_add(self.regs.y);
|
||||||
(LO & addr as Word, false)
|
(LO & addr as Word, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Absolute address. Next 2 bytes of pc are the address
|
// Absolute address. Next 2 bytes of pc are the address
|
||||||
fn am_ABS<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ABS<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let addr = self.readw_pc(bus);
|
let addr = self.readw_pc(mem);
|
||||||
(addr, false)
|
(addr, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Absolute address with offset. Next 2 bytes of pc are the address
|
// Absolute address with offset. Next 2 bytes of pc are the address
|
||||||
// additional cycle on page wrap
|
// additional cycle on page wrap
|
||||||
fn am_ABX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ABX<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let tmp_addr = self.readw_pc(bus);
|
let tmp_addr = self.readw_pc(mem);
|
||||||
let addr = tmp_addr.wrapping_add(self.regs.x as Word);
|
let addr = tmp_addr.wrapping_add(self.regs.x as Word);
|
||||||
|
|
||||||
let page_cross = addr & HI != tmp_addr & HI;
|
let page_cross = addr & HI != tmp_addr & HI;
|
||||||
@@ -343,8 +323,8 @@ impl CPU {
|
|||||||
|
|
||||||
// Absolute address with offset. Next 2 bytes of pc are the address
|
// Absolute address with offset. Next 2 bytes of pc are the address
|
||||||
// additional cycle on page wrap
|
// additional cycle on page wrap
|
||||||
fn am_ABY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_ABY<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let tmp_addr = self.readw_pc(bus);
|
let tmp_addr = self.readw_pc(mem);
|
||||||
let addr = tmp_addr.wrapping_add(self.regs.y as Word);
|
let addr = tmp_addr.wrapping_add(self.regs.y as Word);
|
||||||
|
|
||||||
let page_cross = addr & HI != tmp_addr & HI;
|
let page_cross = addr & HI != tmp_addr & HI;
|
||||||
@@ -353,8 +333,8 @@ impl CPU {
|
|||||||
|
|
||||||
// Relative addressing. Only used for branching. The next byte on the
|
// Relative addressing. Only used for branching. The next byte on the
|
||||||
// pc is a signed offset from the current pc location
|
// pc is a signed offset from the current pc location
|
||||||
fn am_REL<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_REL<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let rel_addr = self.readb_pc(bus) as Word;
|
let rel_addr = self.readb_pc(mem) as Word;
|
||||||
let base_addr = self.regs.pc;
|
let base_addr = self.regs.pc;
|
||||||
|
|
||||||
// If rel_addr > 0x8000, we substract 256 to make a negative jump
|
// If rel_addr > 0x8000, we substract 256 to make a negative jump
|
||||||
@@ -372,19 +352,19 @@ impl CPU {
|
|||||||
// Hardware bug: Normally, if lo of the supplied address is 0xFF, high byte
|
// Hardware bug: Normally, if lo of the supplied address is 0xFF, high byte
|
||||||
// must be read from the next page. Instead it wraps around and reads from
|
// must be read from the next page. Instead it wraps around and reads from
|
||||||
// the same page!
|
// the same page!
|
||||||
fn am_IND<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IND<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let ind_addr = self.readw_pc(bus);
|
let ind_addr = self.readw_pc(mem);
|
||||||
|
|
||||||
// page boundary bug: If LO is 0x00FF, we are at the page border
|
// page boundary bug: If LO is 0x00FF, we are at the page border
|
||||||
// and need to wrap around. So hi is fetched from 0x0000 instead of
|
// and need to wrap around. So hi is fetched from 0x0000 instead of
|
||||||
// 0x0100
|
// 0x0100
|
||||||
let addr = if ind_addr & LO == 0x00FF {
|
let addr = if ind_addr & LO == 0x00FF {
|
||||||
let lo = self.readb(bus, ind_addr);
|
let lo = self.readb(mem, ind_addr);
|
||||||
let hi_addr = ind_addr - 0x00FF;
|
let hi_addr = ind_addr - 0x00FF;
|
||||||
let hi = self.readb(bus, hi_addr);
|
let hi = self.readb(mem, hi_addr);
|
||||||
(((hi as Word) << 8) | lo as Word)
|
(((hi as Word) << 8) | lo as Word)
|
||||||
} else { // normal behaviour
|
} else { // normal behaviour
|
||||||
self.readw(bus, ind_addr)
|
self.readw(mem, ind_addr)
|
||||||
};
|
};
|
||||||
|
|
||||||
(addr, false)
|
(addr, false)
|
||||||
@@ -392,21 +372,21 @@ impl CPU {
|
|||||||
|
|
||||||
// the next 8 bits + x are an address on the zero page. This address stores the real address
|
// the next 8 bits + x are an address on the zero page. This address stores the real address
|
||||||
// that is used for the operation.
|
// that is used for the operation.
|
||||||
fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IZX<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let ind_addr = self.readb_pc(bus);
|
let ind_addr = self.readb_pc(mem);
|
||||||
|
|
||||||
let lo_addr = ind_addr.wrapping_add(self.regs.x);
|
let lo_addr = ind_addr.wrapping_add(self.regs.x);
|
||||||
let hi_addr = ind_addr.wrapping_add(self.regs.x).wrapping_add(1);
|
let hi_addr = ind_addr.wrapping_add(self.regs.x).wrapping_add(1);
|
||||||
let lo = self.readb(bus, lo_addr as Word);
|
let lo = self.readb(mem, lo_addr as Word);
|
||||||
let hi = self.readb(bus, hi_addr as Word);
|
let hi = self.readb(mem, hi_addr as Word);
|
||||||
((hi as Word) << 8 | lo as Word, false)
|
((hi as Word) << 8 | lo as Word, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn am_IZY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
fn am_IZY<T: Memory>(&mut self, mem: &T) -> (Word, bool) {
|
||||||
let ind_addr = self.readb_pc(bus);
|
let ind_addr = self.readb_pc(mem);
|
||||||
|
|
||||||
let lo = self.readb(bus, ind_addr as Word);
|
let lo = self.readb(mem, ind_addr as Word);
|
||||||
let hi = self.readb(bus, ind_addr.wrapping_add(1) as Word);
|
let hi = self.readb(mem, ind_addr.wrapping_add(1) as Word);
|
||||||
|
|
||||||
let addr = (hi as Word) << 8 | lo as Word;
|
let addr = (hi as Word) << 8 | lo as Word;
|
||||||
let addr = addr.wrapping_add(self.regs.y as Word);
|
let addr = addr.wrapping_add(self.regs.y as Word);
|
||||||
@@ -427,8 +407,8 @@ impl CPU {
|
|||||||
// carry bit is set, this enables multiple byte addition to be performed.
|
// carry bit is set, this enables multiple byte addition to be performed.
|
||||||
// If the result is 0, Zero bit is set. If the result if negative,
|
// If the result is 0, Zero bit is set. If the result if negative,
|
||||||
// Negative bit is set
|
// Negative bit is set
|
||||||
fn op_ADC<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
|
fn op_ADC<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr) as Word;
|
let val = self.readb(mem, addr) as Word;
|
||||||
let tmp = self.regs.a as Word + val + self.get_flag(CARRY) as Word;
|
let tmp = self.regs.a as Word + val + self.get_flag(CARRY) as Word;
|
||||||
|
|
||||||
self.set_flag(CARRY, tmp > 255);
|
self.set_flag(CARRY, tmp > 255);
|
||||||
@@ -451,8 +431,8 @@ impl CPU {
|
|||||||
// using the contents of a byte of memory.
|
// using the contents of a byte of memory.
|
||||||
// If the result is 0, Zero bit is set. If the result if negative,
|
// If the result is 0, Zero bit is set. If the result if negative,
|
||||||
// Negative bit is set
|
// Negative bit is set
|
||||||
fn op_AND<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_AND<T: Memory>(&mut self, mem: &T, addr: Addr) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.regs.a &= val;
|
self.regs.a &= val;
|
||||||
self.set_flag_nz(self.regs.a);
|
self.set_flag_nz(self.regs.a);
|
||||||
true
|
true
|
||||||
@@ -467,13 +447,13 @@ impl CPU {
|
|||||||
// carry if the result will not fit in 8 bits.
|
// carry if the result will not fit in 8 bits.
|
||||||
// If the result is 0, Zero bit is set. If the result if negative,
|
// If the result is 0, Zero bit is set. If the result if negative,
|
||||||
// Negative bit is set
|
// Negative bit is set
|
||||||
fn op_ASL<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_ASL<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
// LSR works on memory or A. We can differenciate by the addr mode
|
// LSR works on memory or A. We can differenciate by the addr mode
|
||||||
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
||||||
let val = if *addr_mode == AddrMode::IMP {
|
let val = if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a
|
self.regs.a
|
||||||
} else {
|
} else {
|
||||||
self.readb(bus, addr)
|
self.readb(mem, addr)
|
||||||
};
|
};
|
||||||
let shifted = (val << 1) as Byte;
|
let shifted = (val << 1) as Byte;
|
||||||
self.set_flag(CARRY, (val & 0b10000000) > 0);
|
self.set_flag(CARRY, (val & 0b10000000) > 0);
|
||||||
@@ -481,7 +461,7 @@ impl CPU {
|
|||||||
if *addr_mode == AddrMode::IMP {
|
if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a = shifted;
|
self.regs.a = shifted;
|
||||||
} else {
|
} else {
|
||||||
self.writeb(bus, addr, shifted);
|
self.writeb(mem, addr, shifted);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_flag_nz(shifted);
|
self.set_flag_nz(shifted);
|
||||||
@@ -492,7 +472,7 @@ impl CPU {
|
|||||||
// BCC - Branch if Carry Clear
|
// BCC - Branch if Carry Clear
|
||||||
// If the carry flag is clear then add the relative displacement to
|
// If the carry flag is clear then add the relative displacement to
|
||||||
// the program counter to cause a branch to a new location.
|
// the program counter to cause a branch to a new location.
|
||||||
fn op_BCC<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_BCC(&mut self, addr: Addr) -> bool {
|
||||||
if self.get_flag(CARRY) == 0 {
|
if self.get_flag(CARRY) == 0 {
|
||||||
self.jump(addr);
|
self.jump(addr);
|
||||||
self.cycles_ahead += 1;
|
self.cycles_ahead += 1;
|
||||||
@@ -516,7 +496,7 @@ impl CPU {
|
|||||||
// BEQ - Branch if Equal
|
// BEQ - Branch if Equal
|
||||||
// If the zero flag is set then add the relative displacement to
|
// If the zero flag is set then add the relative displacement to
|
||||||
// the program counter to cause a branch to a new location.
|
// the program counter to cause a branch to a new location.
|
||||||
fn op_BEQ<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_BEQ(&mut self, addr: Addr) -> bool {
|
||||||
if self.get_flag(ZERO) == 1 {
|
if self.get_flag(ZERO) == 1 {
|
||||||
self.jump(addr);
|
self.jump(addr);
|
||||||
self.cycles_ahead += 1;
|
self.cycles_ahead += 1;
|
||||||
@@ -529,8 +509,8 @@ impl CPU {
|
|||||||
// A & M, N = M7, V = M6
|
// A & M, N = M7, V = M6
|
||||||
// bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);
|
// bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);
|
||||||
// the zeroflag is set to the result of operand AND accumulator.
|
// the zeroflag is set to the result of operand AND accumulator.
|
||||||
fn op_BIT<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
|
fn op_BIT<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.set_flag(OVERFLOW, (val & OVERFLOW) > 1);
|
self.set_flag(OVERFLOW, (val & OVERFLOW) > 1);
|
||||||
self.set_flag(NEGATIVE, (val & NEGATIVE) > 1);
|
self.set_flag(NEGATIVE, (val & NEGATIVE) > 1);
|
||||||
self.set_flag(ZERO, (val & self.regs.a) == 0);
|
self.set_flag(ZERO, (val & self.regs.a) == 0);
|
||||||
@@ -540,7 +520,7 @@ impl CPU {
|
|||||||
// BMI - Branch if Minus
|
// BMI - Branch if Minus
|
||||||
// If the negative flag is set then add the relative displacement to the
|
// If the negative flag is set then add the relative displacement to the
|
||||||
// program counter to cause a branch to a new location.
|
// program counter to cause a branch to a new location.
|
||||||
fn op_BMI<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_BMI(&mut self, addr: Addr) -> bool {
|
||||||
if self.get_flag(NEGATIVE) == 1 {
|
if self.get_flag(NEGATIVE) == 1 {
|
||||||
self.jump(addr);
|
self.jump(addr);
|
||||||
self.cycles_ahead += 1;
|
self.cycles_ahead += 1;
|
||||||
@@ -564,7 +544,7 @@ impl CPU {
|
|||||||
// BPL - Branch if Positive
|
// BPL - Branch if Positive
|
||||||
// If the negative flag is clear then add the relative displacement to
|
// If the negative flag is clear then add the relative displacement to
|
||||||
// the program counter to cause a branch to a new location.
|
// the program counter to cause a branch to a new location.
|
||||||
fn op_BPL<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_BPL(&mut self, addr: Addr) -> bool {
|
||||||
if self.get_flag(NEGATIVE) == 0 {
|
if self.get_flag(NEGATIVE) == 0 {
|
||||||
self.jump(addr);
|
self.jump(addr);
|
||||||
self.cycles_ahead += 1;
|
self.cycles_ahead += 1;
|
||||||
@@ -578,21 +558,21 @@ impl CPU {
|
|||||||
// The program counter and processor status are pushed on the stack
|
// The program counter and processor status are pushed on the stack
|
||||||
// then the IRQ interrupt vector at $FFFE/F is loaded into the PC and
|
// then the IRQ interrupt vector at $FFFE/F is loaded into the PC and
|
||||||
// the break flag in the status set to one.
|
// the break flag in the status set to one.
|
||||||
fn op_BRK<T: Memory>(&mut self, bus: &mut T) -> bool {
|
fn op_BRK<T: Memory>(&mut self, mem: &mut T) -> bool {
|
||||||
self.regs.pc += 1;
|
self.regs.pc += 1;
|
||||||
self.set_flag(IRQ, true);
|
self.set_flag(IRQ, true);
|
||||||
|
|
||||||
// Push pc to stack
|
// Push pc to stack
|
||||||
self.pushb_sp(bus, (self.regs.pc >> 8) as Byte);
|
self.pushb_sp(mem, (self.regs.pc >> 8) as Byte);
|
||||||
self.pushb_sp(bus, self.regs.pc as Byte);
|
self.pushb_sp(mem, self.regs.pc as Byte);
|
||||||
|
|
||||||
// Push flags to stack
|
// Push flags to stack
|
||||||
self.set_flag(BREAK, true);
|
self.set_flag(BREAK, true);
|
||||||
self.pushb_sp(bus, self.regs.flags);
|
self.pushb_sp(mem, self.regs.flags);
|
||||||
self.set_flag(BREAK, false);
|
self.set_flag(BREAK, false);
|
||||||
|
|
||||||
// set PC to IRQ vector
|
// set PC to IRQ vector
|
||||||
self.regs.pc = self.readw(bus, 0xFFFE);
|
self.regs.pc = self.readw(mem, 0xFFFE);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,8 +629,8 @@ impl CPU {
|
|||||||
// Z,C,N = A-M
|
// Z,C,N = A-M
|
||||||
// This instruction compares the contents of the accumulator with another
|
// This instruction compares the contents of the accumulator with another
|
||||||
// memory held value and sets the zero and carry flags as appropriate.
|
// memory held value and sets the zero and carry flags as appropriate.
|
||||||
fn op_CMP<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_CMP<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
let tmp = (self.regs.a as Word).wrapping_sub(val as Word);
|
let tmp = (self.regs.a as Word).wrapping_sub(val as Word);
|
||||||
|
|
||||||
self.set_flag(CARRY, self.regs.a >= val);
|
self.set_flag(CARRY, self.regs.a >= val);
|
||||||
@@ -659,8 +639,8 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare X
|
// Compare X
|
||||||
fn op_CPX<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_CPX<T: Memory>(&mut self, mem: &T, addr: Addr) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
let tmp = (self.regs.x as Word).wrapping_sub(val as Word);
|
let tmp = (self.regs.x as Word).wrapping_sub(val as Word);
|
||||||
|
|
||||||
self.set_flag(CARRY, self.regs.x >= val);
|
self.set_flag(CARRY, self.regs.x >= val);
|
||||||
@@ -669,8 +649,8 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare Y
|
// Compare Y
|
||||||
fn op_CPY<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_CPY<T: Memory>(&mut self, mem: &T, addr: Addr) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
let tmp = (self.regs.y as Word).wrapping_sub(val as Word);
|
let tmp = (self.regs.y as Word).wrapping_sub(val as Word);
|
||||||
|
|
||||||
self.set_flag(CARRY, self.regs.y >= val);
|
self.set_flag(CARRY, self.regs.y >= val);
|
||||||
@@ -679,9 +659,9 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial: DEC value, then CMP
|
// Unofficial: DEC value, then CMP
|
||||||
fn op_DCP<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_DCP<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.op_DEC(bus, addr);
|
self.op_DEC(mem, addr);
|
||||||
self.op_CMP(bus, addr);
|
self.op_CMP(mem, addr);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,10 +669,10 @@ impl CPU {
|
|||||||
// M,Z,N = M-1
|
// M,Z,N = M-1
|
||||||
// Subtracts one from the value held at a specified memory location
|
// Subtracts one from the value held at a specified memory location
|
||||||
// setting the zero and negative flags as appropriate.
|
// setting the zero and negative flags as appropriate.
|
||||||
fn op_DEC<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_DEC<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
let val = val.wrapping_sub(1);
|
let val = val.wrapping_sub(1);
|
||||||
self.writeb(bus, addr, val);
|
self.writeb(mem, addr, val);
|
||||||
|
|
||||||
self.set_flag_nz(val);
|
self.set_flag_nz(val);
|
||||||
false
|
false
|
||||||
@@ -724,8 +704,8 @@ impl CPU {
|
|||||||
// A,Z,N = A^M
|
// A,Z,N = A^M
|
||||||
// An exclusive OR is performed, bit by bit, on the accumulator contents
|
// An exclusive OR is performed, bit by bit, on the accumulator contents
|
||||||
// using the contents of a byte of memory.
|
// using the contents of a byte of memory.
|
||||||
fn op_EOR<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_EOR<T: Memory>(&mut self, mem: &T, addr: Addr) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.regs.a = self.regs.a ^ val;
|
self.regs.a = self.regs.a ^ val;
|
||||||
|
|
||||||
self.set_flag_nz(self.regs.a);
|
self.set_flag_nz(self.regs.a);
|
||||||
@@ -736,10 +716,10 @@ impl CPU {
|
|||||||
// M,Z,N = M+1
|
// M,Z,N = M+1
|
||||||
// Adds one to the value held at a specified memory location setting the
|
// Adds one to the value held at a specified memory location setting the
|
||||||
// zero and negative flags as appropriate.
|
// zero and negative flags as appropriate.
|
||||||
fn op_INC<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_INC<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
let val = val.wrapping_add(1);
|
let val = val.wrapping_add(1);
|
||||||
self.writeb(bus, addr, val);
|
self.writeb(mem, addr, val);
|
||||||
self.set_flag_nz(val);
|
self.set_flag_nz(val);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -764,9 +744,9 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial opcode: INC, then SBC
|
// Unofficial opcode: INC, then SBC
|
||||||
fn op_ISB<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_ISB<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
self.op_INC(bus, addr);
|
self.op_INC(mem, addr);
|
||||||
self.op_SBC(bus, addr);
|
self.op_SBC(mem, addr);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,11 +757,11 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Jump to subroutine (leaves trace on the stack)
|
// Jump to subroutine (leaves trace on the stack)
|
||||||
fn op_JSR<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_JSR<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.regs.pc -= 1;
|
self.regs.pc -= 1;
|
||||||
self.writeb(bus, STACK_BASE_ADDR + self.regs.sp as Word, ((self.regs.pc >> 8) & 0x00ff) as Byte);
|
self.writeb(mem, STACK_BASE_ADDR + self.regs.sp as Word, ((self.regs.pc >> 8) & 0x00ff) as Byte);
|
||||||
self.regs.sp -= 1;
|
self.regs.sp -= 1;
|
||||||
self.writeb(bus, STACK_BASE_ADDR + self.regs.sp as Word, (self.regs.pc & 0x00ff) as Byte);
|
self.writeb(mem, STACK_BASE_ADDR + self.regs.sp as Word, (self.regs.pc & 0x00ff) as Byte);
|
||||||
self.regs.sp -= 1;
|
self.regs.sp -= 1;
|
||||||
self.jump(addr);
|
self.jump(addr);
|
||||||
false
|
false
|
||||||
@@ -796,8 +776,8 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial op code! Shortcut for LDA, TAX
|
// Unofficial op code! Shortcut for LDA, TAX
|
||||||
fn op_LAX<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
|
fn op_LAX<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
self.op_LDA(bus, addr);
|
self.op_LDA(mem, addr);
|
||||||
self.op_TAX();
|
self.op_TAX();
|
||||||
|
|
||||||
match Instruction::decode_op(self.curr_op).addr_mode {
|
match Instruction::decode_op(self.curr_op).addr_mode {
|
||||||
@@ -807,24 +787,24 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read value from addr into A
|
// Read value from addr into A
|
||||||
fn op_LDA<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
|
fn op_LDA<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.regs.a = val;
|
self.regs.a = val;
|
||||||
self.set_flag_nz(val);
|
self.set_flag_nz(val);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read value from addr into X
|
// Read value from addr into X
|
||||||
fn op_LDX<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
|
fn op_LDX<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.regs.x = val;
|
self.regs.x = val;
|
||||||
self.set_flag_nz(val);
|
self.set_flag_nz(val);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read value from addr into Y
|
// Read value from addr into Y
|
||||||
fn op_LDY<T: Memory>(&mut self, bus: &T, addr: Word) -> bool {
|
fn op_LDY<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
let val = self.readb(bus, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.regs.y = val;
|
self.regs.y = val;
|
||||||
self.set_flag_nz(val);
|
self.set_flag_nz(val);
|
||||||
true
|
true
|
||||||
@@ -834,13 +814,13 @@ impl CPU {
|
|||||||
// A,C,Z,N = A/2 or M,C,Z,N = M/2
|
// A,C,Z,N = A/2 or M,C,Z,N = M/2
|
||||||
// Each of the bits in A or M is shift one place to the right. The bit
|
// Each of the bits in A or M is shift one place to the right. The bit
|
||||||
// that was in bit 0 is shifted into the carry flag. Bit 7 is set to zero.
|
// that was in bit 0 is shifted into the carry flag. Bit 7 is set to zero.
|
||||||
fn op_LSR<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_LSR<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
// LSR works on memory or A. We can differenciate by the addr mode
|
// LSR works on memory or A. We can differenciate by the addr mode
|
||||||
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
||||||
let val = if *addr_mode == AddrMode::IMP {
|
let val = if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a as Word
|
self.regs.a as Word
|
||||||
} else {
|
} else {
|
||||||
self.readb(bus, addr) as Word
|
self.readb(mem, addr) as Word
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_flag(CARRY, (val & 0b00000001) != 0);
|
self.set_flag(CARRY, (val & 0b00000001) != 0);
|
||||||
@@ -850,7 +830,7 @@ impl CPU {
|
|||||||
if *addr_mode == AddrMode::IMP {
|
if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a = shifted;
|
self.regs.a = shifted;
|
||||||
} else {
|
} else {
|
||||||
self.writeb(bus, addr, shifted);
|
self.writeb(mem, addr, shifted);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -867,31 +847,31 @@ impl CPU {
|
|||||||
// A,Z,N = A|M
|
// A,Z,N = A|M
|
||||||
// An inclusive OR is performed, bit by bit, on the accumulator contents
|
// An inclusive OR is performed, bit by bit, on the accumulator contents
|
||||||
// using the contents of a byte of memory.
|
// using the contents of a byte of memory.
|
||||||
fn op_ORA<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_ORA<T: Memory>(&mut self, mem: &T, addr: Addr) -> bool {
|
||||||
self.regs.a = self.regs.a | self.readb(bus, addr);
|
self.regs.a = self.regs.a | self.readb(mem, addr);
|
||||||
self.set_flag_nz(self.regs.a);
|
self.set_flag_nz(self.regs.a);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHA - Push Accumulator
|
// PHA - Push Accumulator
|
||||||
// Pushes a copy of the accumulator on to the stack.
|
// Pushes a copy of the accumulator on to the stack.
|
||||||
fn op_PHA<T: Memory>(&mut self, bus: &mut T) -> bool {
|
fn op_PHA<T: Memory>(&mut self, mem: &mut T) -> bool {
|
||||||
self.pushb_sp(bus, self.regs.a);
|
self.pushb_sp(mem, self.regs.a);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHP - Push Processor Status
|
// PHP - Push Processor Status
|
||||||
// Pushes a copy of the status flags on to the stack.
|
// Pushes a copy of the status flags on to the stack.
|
||||||
fn op_PHP<T: Memory>(&mut self, bus: &mut T) -> bool {
|
fn op_PHP<T: Memory>(&mut self, mem: &mut T) -> bool {
|
||||||
let tmp = self.regs.flags | BREAK;
|
let tmp = self.regs.flags | BREAK;
|
||||||
self.pushb_sp(bus, tmp);
|
self.pushb_sp(mem, tmp);
|
||||||
self.set_flag(BREAK, false);
|
self.set_flag(BREAK, false);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from stack into A
|
// Read from stack into A
|
||||||
fn op_PLA<T: Memory>(&mut self, bus: &T) -> bool {
|
fn op_PLA<T: Memory>(&mut self, mem: &T) -> bool {
|
||||||
self.regs.a = self.popb_sp(bus);
|
self.regs.a = self.popb_sp(mem);
|
||||||
self.set_flag_nz(self.regs.a);
|
self.set_flag_nz(self.regs.a);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -899,8 +879,8 @@ impl CPU {
|
|||||||
// PLP - Pull Processor Status
|
// PLP - Pull Processor Status
|
||||||
// Pulls an 8 bit value from the stack and into the processor flags. The
|
// Pulls an 8 bit value from the stack and into the processor flags. The
|
||||||
// flags will take on new states as determined by the value pulled.
|
// flags will take on new states as determined by the value pulled.
|
||||||
fn op_PLP<T: Memory>(&mut self, bus: &T) -> bool {
|
fn op_PLP<T: Memory>(&mut self, mem: &T) -> bool {
|
||||||
self.regs.flags = self.popb_sp(bus);
|
self.regs.flags = self.popb_sp(mem);
|
||||||
|
|
||||||
// Im not sure why this is set to false and stack value is not used
|
// Im not sure why this is set to false and stack value is not used
|
||||||
// but that's how the nestest.log shows it..
|
// but that's how the nestest.log shows it..
|
||||||
@@ -912,12 +892,12 @@ impl CPU {
|
|||||||
// Move each of the bits in either A or M one place to the left. Bit 0 is
|
// Move each of the bits in either A or M one place to the left. Bit 0 is
|
||||||
// filled with the current value of the carry flag whilst the old bit 7
|
// filled with the current value of the carry flag whilst the old bit 7
|
||||||
// becomes the new carry flag value.
|
// becomes the new carry flag value.
|
||||||
fn op_ROL<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_ROL<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
||||||
let val = if *addr_mode == AddrMode::IMP {
|
let val = if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a as Word
|
self.regs.a as Word
|
||||||
} else {
|
} else {
|
||||||
self.readb(bus, addr) as Word
|
self.readb(mem, addr) as Word
|
||||||
};
|
};
|
||||||
let shifted = (val << 1) as Byte | self.get_flag(CARRY);
|
let shifted = (val << 1) as Byte | self.get_flag(CARRY);
|
||||||
|
|
||||||
@@ -927,23 +907,23 @@ impl CPU {
|
|||||||
if *addr_mode == AddrMode::IMP {
|
if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a = shifted as Byte;
|
self.regs.a = shifted as Byte;
|
||||||
} else {
|
} else {
|
||||||
self.writeb(bus, addr, shifted);
|
self.writeb(mem, addr, shifted);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial: ROL and then AND
|
// Unofficial: ROL and then AND
|
||||||
fn op_RLA<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_RLA<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
self.op_ROL(bus, addr);
|
self.op_ROL(mem, addr);
|
||||||
self.op_AND(bus, addr);
|
self.op_AND(mem, addr);
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial: Performs ROR + ADC
|
// Unofficial: Performs ROR + ADC
|
||||||
fn op_RRA<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_RRA<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
self.op_ROR(bus, addr);
|
self.op_ROR(mem, addr);
|
||||||
self.op_ADC(bus, addr);
|
self.op_ADC(mem, addr);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,12 +931,12 @@ impl CPU {
|
|||||||
// Move each of the bits in either A or M one place to the right. Bit 7 is
|
// Move each of the bits in either A or M one place to the right. Bit 7 is
|
||||||
// filled with the current value of the carry flag whilst the old bit 0
|
// filled with the current value of the carry flag whilst the old bit 0
|
||||||
// becomes the new carry flag value.
|
// becomes the new carry flag value.
|
||||||
fn op_ROR<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_ROR<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
let addr_mode = &Instruction::decode_op(self.curr_op).addr_mode;
|
||||||
let val = if *addr_mode == AddrMode::IMP {
|
let val = if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a as Word
|
self.regs.a as Word
|
||||||
} else {
|
} else {
|
||||||
self.readb(bus, addr) as Word
|
self.readb(mem, addr) as Word
|
||||||
};
|
};
|
||||||
|
|
||||||
let shifted = (val >> 1) as Byte | (self.get_flag(CARRY) << 7);
|
let shifted = (val >> 1) as Byte | (self.get_flag(CARRY) << 7);
|
||||||
@@ -967,7 +947,7 @@ impl CPU {
|
|||||||
if *addr_mode == AddrMode::IMP {
|
if *addr_mode == AddrMode::IMP {
|
||||||
self.regs.a = shifted;
|
self.regs.a = shifted;
|
||||||
} else {
|
} else {
|
||||||
self.writeb(bus, addr, shifted);
|
self.writeb(mem, addr, shifted);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -976,12 +956,12 @@ impl CPU {
|
|||||||
// The RTI instruction is used at the end of an interrupt processing
|
// The RTI instruction is used at the end of an interrupt processing
|
||||||
// routine. It pulls the processor flags from the stack followed by the
|
// routine. It pulls the processor flags from the stack followed by the
|
||||||
// program counter.
|
// program counter.
|
||||||
fn op_RTI<T: Memory>(&mut self, bus: &T) -> bool {
|
fn op_RTI<T: Memory>(&mut self, mem: &T) -> bool {
|
||||||
self.regs.flags = self.popb_sp(bus);
|
self.regs.flags = self.popb_sp(mem);
|
||||||
self.regs.flags &= !BREAK;
|
self.regs.flags &= !BREAK;
|
||||||
|
|
||||||
let pc_lo = self.popb_sp(bus) as Word;
|
let pc_lo = self.popb_sp(mem) as Word;
|
||||||
let pc_hi = self.popb_sp(bus) as Word;
|
let pc_hi = self.popb_sp(mem) as Word;
|
||||||
self.regs.pc = pc_hi << 8 | pc_lo;
|
self.regs.pc = pc_hi << 8 | pc_lo;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -989,20 +969,20 @@ impl CPU {
|
|||||||
// RTS - Return from Subroutine
|
// RTS - Return from Subroutine
|
||||||
// The RTS instruction is used at the end of a subroutine to return to the
|
// The RTS instruction is used at the end of a subroutine to return to the
|
||||||
// calling routine. It pulls the program counter (minus one) from the stack.
|
// calling routine. It pulls the program counter (minus one) from the stack.
|
||||||
fn op_RTS<T: Memory>(&mut self, bus: &T) -> bool {
|
fn op_RTS<T: Memory>(&mut self, mem: &T) -> bool {
|
||||||
self.regs.sp += 1;
|
self.regs.sp += 1;
|
||||||
let lo = self.readb(bus, 0x0100 + self.regs.sp as Addr);
|
let lo = self.readb(mem, 0x0100 + self.regs.sp as Addr);
|
||||||
self.regs.sp += 1;
|
self.regs.sp += 1;
|
||||||
let hi = self.readb(bus, 0x0100 + self.regs.sp as Addr);
|
let hi = self.readb(mem, 0x0100 + self.regs.sp as Addr);
|
||||||
let addr = (hi as Addr) << 8 | lo as Addr;
|
let addr = (hi as Addr) << 8 | lo as Addr;
|
||||||
self.regs.pc = addr + 1;
|
self.regs.pc = addr + 1;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial: Stores bitwise AND of A and X
|
// Unofficial: Stores bitwise AND of A and X
|
||||||
fn op_SAX<T: Memory>(&mut self, bus: &mut T, addr: Addr) -> bool {
|
fn op_SAX<T: Memory>(&mut self, mem: &mut T, addr: Addr) -> bool {
|
||||||
let val = self.regs.a & self.regs.x;
|
let val = self.regs.a & self.regs.x;
|
||||||
self.writeb(bus, addr, val);
|
self.writeb(mem, addr, val);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1013,8 +993,8 @@ impl CPU {
|
|||||||
// accumulator together with the not of the carry bit. If overflow occurs
|
// accumulator together with the not of the carry bit. If overflow occurs
|
||||||
// the carry bit is clear, this enables multiple byte subtraction to be
|
// the carry bit is clear, this enables multiple byte subtraction to be
|
||||||
// performed.
|
// performed.
|
||||||
fn op_SBC<T: Memory>(&mut self, bus: &T, addr: Addr) -> bool {
|
fn op_SBC<T: Memory>(&mut self, mem: &T, addr: Addr) -> bool {
|
||||||
let val = self.readb(bus, addr) as Word;
|
let val = self.readb(mem, addr) as Word;
|
||||||
|
|
||||||
// invert buttom 8 bits
|
// invert buttom 8 bits
|
||||||
let val = val ^ LO;
|
let val = val ^ LO;
|
||||||
@@ -1057,34 +1037,34 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial: ASL + ORA
|
// Unofficial: ASL + ORA
|
||||||
fn op_SLO<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_SLO<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.op_ASL(bus, addr);
|
self.op_ASL(mem, addr);
|
||||||
self.op_ORA(bus, addr);
|
self.op_ORA(mem, addr);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unofficial: LSR + EOR
|
// Unofficial: LSR + EOR
|
||||||
fn op_SRE<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_SRE<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.op_LSR(bus, addr);
|
self.op_LSR(mem, addr);
|
||||||
self.op_EOR(bus, addr);
|
self.op_EOR(mem, addr);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push A reg to memory
|
// Push A reg to memory
|
||||||
fn op_STA<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_STA<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.writeb(bus, addr, self.regs.a);
|
self.writeb(mem, addr, self.regs.a);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push X reg to memory
|
// Push X reg to memory
|
||||||
fn op_STX<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_STX<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.writeb(bus, addr, self.regs.x);
|
self.writeb(mem, addr, self.regs.x);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push Y reg to memory
|
// Push Y reg to memory
|
||||||
fn op_STY<T: Memory>(&mut self, bus: &mut T, addr: Word) -> bool {
|
fn op_STY<T: Memory>(&mut self, mem: &mut T, addr: Word) -> bool {
|
||||||
self.writeb(bus, addr, self.regs.y);
|
self.writeb(mem, addr, self.regs.y);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use crate::nes::*;
|
||||||
use crate::nes::cpu::instructions::*;
|
use crate::nes::cpu::instructions::*;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use crate::nes::bus::*;
|
|
||||||
use crate::nes::types::*;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
// Disassemble code around the pc of the cpu
|
// Disassemble code around the pc of the cpu
|
||||||
@@ -13,11 +13,11 @@ pub struct Disasm {
|
|||||||
pub addresses: Vec<Addr>
|
pub addresses: Vec<Addr>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BusDevice for Disasm {}
|
impl MemoryReader for Disasm {}
|
||||||
|
|
||||||
impl Disasm {
|
impl Disasm {
|
||||||
// Disassemble given code region
|
// Disassemble given code region
|
||||||
pub fn disassemble(mem: &Bus, start: Addr, stop: Addr) -> Result<Self, Error> {
|
pub fn disassemble<T: Memory>(mem: &T, start: Addr, stop: Addr) -> Result<Self, Error> {
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let mut addresses = Vec::new();
|
let mut addresses = Vec::new();
|
||||||
let mut mem_iter = ((start as usize) .. (stop as usize)+1).map({|a| a as Addr});
|
let mut mem_iter = ((start as usize) .. (stop as usize)+1).map({|a| a as Addr});
|
||||||
|
|||||||
107
src/nes/memory.rs
Normal file
107
src/nes/memory.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// Interface for devices that contain memory that can be accessed by CPU
|
||||||
|
use crate::nes::cartridge::Cartridge;
|
||||||
|
use crate::nes::types::*;
|
||||||
|
|
||||||
|
const RAM_SIZE: usize = 0x0800;
|
||||||
|
const RAM_ADDR_RANGE: [Addr; 2] = [0x0000, 0x1fff];
|
||||||
|
const RAM_PHYS_RANGE: [Addr; 2] = [0x0000, 0x07ff];
|
||||||
|
const PPU_ADDR_RANGE: [Addr; 2] = [0x2000, 0x3fff];
|
||||||
|
const PPU_PHYS_RANGE: [Addr; 2] = [0x2000, 0x2007];
|
||||||
|
const CART_ADDR_RANGE: [Addr; 2] = [0x4020, 0xffff];
|
||||||
|
|
||||||
|
pub struct NESMemory {
|
||||||
|
ram: [Byte; RAM_SIZE], // 2kb
|
||||||
|
cartridge: Option<Cartridge>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NESMemory {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
NESMemory {
|
||||||
|
ram: [0; RAM_SIZE],
|
||||||
|
cartridge: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_cartridge(&mut self, c: Cartridge) {
|
||||||
|
self.cartridge = Some(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Memory {
|
||||||
|
fn readb(&self, addr: Addr) -> Byte;
|
||||||
|
fn writeb(&mut self, addr: Addr, data: Byte);
|
||||||
|
fn readw(&self, addr: Addr) -> Word {
|
||||||
|
let lo = self.readb(addr);
|
||||||
|
let hi = self.readb(addr+1);
|
||||||
|
(hi as Word) << 8 | lo as Word
|
||||||
|
}
|
||||||
|
fn writew(&mut self, addr: Addr, data: Word) {
|
||||||
|
self.writeb(addr, data as Byte);
|
||||||
|
self.writeb(addr + 1, (data >> 8) as Byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory for NESMemory {
|
||||||
|
fn readb(&self, addr: Addr) -> Byte {
|
||||||
|
if let Some(cartridge) = &self.cartridge {
|
||||||
|
if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
||||||
|
return cartridge.readb(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
||||||
|
// Ram is 3x mirrored after 0x07ff
|
||||||
|
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
||||||
|
}
|
||||||
|
// if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
||||||
|
// // PPU memory is mirrored after 0x2007 to 0x3fff
|
||||||
|
// return self.ram[(addr & PPU_PHYS_RANGE[1]) as usize]
|
||||||
|
// }
|
||||||
|
0x0000 // generic response
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeb(&mut self, addr: Addr, data: Byte) {
|
||||||
|
if let Some(cartridge) = &mut self.cartridge {
|
||||||
|
if CART_ADDR_RANGE[0] <= addr && addr <= CART_ADDR_RANGE[1] {
|
||||||
|
cartridge.writeb(addr, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if RAM_ADDR_RANGE[0] <= addr && addr <= RAM_ADDR_RANGE[1] {
|
||||||
|
// Ram is 3x mirrored after 07ff
|
||||||
|
self.ram[(addr & RAM_PHYS_RANGE[1]) as usize] = data
|
||||||
|
}
|
||||||
|
// if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
||||||
|
// // PPU memory is mirrored after 0x2007 to 0x3fff
|
||||||
|
// self.ram[(addr & PPU_PHYS_RANGE[1]) as usize] = data
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MemoryReader {
|
||||||
|
fn readb<T: Memory>(&self, mem: &T, addr: Addr) -> Byte {
|
||||||
|
mem.readb(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readw<T: Memory>(&self, mem: &T, addr: Addr) -> Word {
|
||||||
|
mem.readw(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeb<T: Memory>(&mut self, mem: &mut T, addr: Addr, data: Byte) {
|
||||||
|
mem.writeb(addr, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PPU interface to allow read/write of memory
|
||||||
|
pub trait PPUMemory {
|
||||||
|
fn readb_ppu(&self, addr: Addr) -> Byte;
|
||||||
|
fn writeb_ppu(&mut self, addr: Addr, data: Byte);
|
||||||
|
fn readw_ppu(&self, addr: Addr) -> Word {
|
||||||
|
let lo = self.readb_ppu(addr);
|
||||||
|
let hi = self.readb_ppu(addr+1);
|
||||||
|
(hi as Word) << 8 | lo as Word
|
||||||
|
}
|
||||||
|
fn writew_ppu(&mut self, addr: Addr, data: Word) {
|
||||||
|
self.writeb_ppu(addr, data as Byte);
|
||||||
|
self.writeb_ppu(addr + 1, (data >> 8) as Byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::nes::bus::Memory;
|
use crate::nes::memory::Memory;
|
||||||
use crate::nes::bus::Clockable;
|
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
@@ -18,10 +17,8 @@ impl PPU {
|
|||||||
canvas_main: ImageBuffer::new(256, 240),
|
canvas_main: ImageBuffer::new(256, 240),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Clockable for PPU {
|
pub fn clock<T: Memory>(&mut self, _mem: &mut T) {
|
||||||
fn clock<T: Memory>(&mut self, _bus: &mut T) {
|
|
||||||
// random noise
|
// random noise
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let x = rng.gen_range(0, 256);
|
let x = rng.gen_range(0, 256);
|
||||||
@@ -37,6 +34,3 @@ impl Clockable for PPU {
|
|||||||
self.canvas_main.put_pixel(x as u32, y as u32, px);
|
self.canvas_main.put_pixel(x as u32, y as u32, px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user