Lots of stuff.
This commit is contained in:
+72
-12
@@ -1,16 +1,76 @@
|
||||
use std::ops::Add;
|
||||
use crate::mos6502cpu::Mos6502Cpu;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
/// Represents the various addressing modes of the 6502 CPU.
|
||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||
pub enum AddressMode {
|
||||
/// Implied
|
||||
///
|
||||
/// No operand is needed; the instruction implicitly operates on a register or flag.
|
||||
/// Example: `CLC` (Clear Carry Flag)
|
||||
Implied,
|
||||
|
||||
/// Accumulator
|
||||
///
|
||||
/// Operates directly on the accumulator register.
|
||||
/// Example: `ASL A` (Arithmetic Shift Left on Accumulator)
|
||||
Accumulator,
|
||||
Immediate(u8),
|
||||
ZeroPage(u8),
|
||||
ZeroPageX(u8),
|
||||
Absolute(u16),
|
||||
AbsoluteX(u16),
|
||||
AbsoluteY(u16),
|
||||
IndirectX(u8),
|
||||
IndirectY(u8),
|
||||
|
||||
/// Immediate
|
||||
///
|
||||
/// Operand is a constant 8-bit value.
|
||||
/// Example: `LDA #$01` loads the value 0x01 into the accumulator.
|
||||
Immediate,
|
||||
|
||||
/// Zero Page
|
||||
///
|
||||
/// Operand is an address in the first 256 bytes of memory (0x0000–0x00FF).
|
||||
/// Example: `LDA $10` reads from address 0x0010.
|
||||
ZeroPage,
|
||||
|
||||
/// Zero Page X
|
||||
///
|
||||
/// Zero page address offset by the X register.
|
||||
/// Example: If X = 0x10, `LDA $23,X` reads from 0x33.
|
||||
ZeroPageX,
|
||||
|
||||
/// Zero Page Y
|
||||
///
|
||||
/// Zero page address offset by the Y register.
|
||||
/// Used only by a few instructions like `LDX` and `STX`.
|
||||
/// Example: If Y = 0x10, `LDX $23,Y` reads from 0x33.
|
||||
ZeroPageY,
|
||||
|
||||
/// Absolute
|
||||
///
|
||||
/// Full 16-bit address is provided as the operand.
|
||||
/// Example: `LDA $1234` reads from address 0x1234.
|
||||
Absolute,
|
||||
|
||||
/// Absolute X
|
||||
///
|
||||
/// Absolute address offset by the X register.
|
||||
/// Example: If X = 0x10, `LDA $1234,X` reads from 0x1244.
|
||||
AbsoluteX,
|
||||
|
||||
/// Absolute Y
|
||||
///
|
||||
/// Absolute address offset by the Y register.
|
||||
/// Example: If Y = 0x10, `LDA $1234,Y` reads from 0x1244.
|
||||
AbsoluteY,
|
||||
|
||||
/// Indirect
|
||||
///
|
||||
/// Only used by `JMP`. Operand is a 16-bit address pointing to another 16-bit address.
|
||||
/// Example: `JMP ($1234)` jumps to the address stored at 0x1234/0x1235.
|
||||
Indirect,
|
||||
|
||||
/// Indirect X (Indexed Indirect)
|
||||
///
|
||||
/// Operand is a zero-page address. Add X to it, then fetch the 16-bit address from that location.
|
||||
/// Example: If X = 0x04 and operand = $20, `LDA ($20,X)` reads from the address at $24/$25.
|
||||
IndirectX,
|
||||
|
||||
/// Indirect Y (Indirect Indexed)
|
||||
///
|
||||
/// Operand is a zero-page address. Fetch the 16-bit address from that location, then add Y.
|
||||
/// Example: If Y = 0x10 and ($20) = $3000, `LDA ($20),Y` reads from $3010.
|
||||
IndirectY,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
use crate::periph::ram_chip::RamChip;
|
||||
|
||||
/// BackplaneBuilder
|
||||
///
|
||||
/// Builds a Backplane for a 6502 Emulated PC
|
||||
struct BackplaneBuilder {
|
||||
cpu: Mos6502Cpu,
|
||||
// ram_modules: Vec<dyn RamChip>
|
||||
}
|
||||
|
||||
impl BackplaneBuilder {
|
||||
pub fn add_cpu(mut self, new_cpu: Mos6502Cpu) -> Self {
|
||||
self.cpu = new_cpu;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_ram(mut self, new_ram: impl RamChip) -> Self {
|
||||
// self.ram_modules.push(new_ram);
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod beneater;
|
||||
@@ -0,0 +1,28 @@
|
||||
pub mod new;
|
||||
pub mod tick;
|
||||
pub mod reset;
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::kim1_keypad::Kim1Keypad;
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
/// Represents a KIM-1
|
||||
///
|
||||
///
|
||||
pub struct Kim1 {
|
||||
pub running: bool,
|
||||
pub cpu: Mos6502Cpu,
|
||||
rriot1: Mos6530,
|
||||
rriot2: Mos6530,
|
||||
ram: Hm62256,
|
||||
pub(crate) keypad: Kim1Keypad,
|
||||
address_bus: u16,
|
||||
data_bus: u8,
|
||||
cpu_read: bool
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::computers::kim1::Kim1;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::kim1_keypad::Kim1Keypad;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Kim1 {
|
||||
pub fn dump(&self) {
|
||||
println!("DUMPING KIM-1 PC STATE");
|
||||
self.cpu.dump();
|
||||
self.rriot1.dump();
|
||||
self.rriot2.dump();
|
||||
self.keypad.dump();
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-002_fillerbyte00-0x1c00.bin");
|
||||
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-003_fillerbyte00-0x1800.bin");
|
||||
|
||||
Self {
|
||||
cpu: Default::default(),
|
||||
rriot1: Mos6530::new(0x1700, 0x1780, 0x1800, &rriot1_rom),
|
||||
rriot2: Mos6530::new(0x1740, 0x17C0, 0x1C00, &rriot2_rom),
|
||||
ram: Hm62256::new(0x0000),
|
||||
keypad: Kim1Keypad::new(),
|
||||
address_bus: 0,
|
||||
data_bus: 0,
|
||||
cpu_read: false,
|
||||
running: false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use crate::computers::kim1::Kim1;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Kim1 {
|
||||
pub fn reset(&mut self) {
|
||||
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-002_fillerbyte00-0x1c00.bin");
|
||||
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-003_fillerbyte00-0x1800.bin");
|
||||
self.cpu = Default::default();
|
||||
self.rriot1 = Mos6530::new(0x1700, 0x1780, 0x1800, rriot1_rom.as_array().unwrap());
|
||||
self.rriot2 = Mos6530::new(0x1740, 0x17c0, 0x1c00, rriot2_rom.as_array().unwrap());
|
||||
self.ram = Hm62256::new(0x0000);
|
||||
self.address_bus = 0x0000;
|
||||
self.data_bus = 0x0000;
|
||||
self.cpu_read = true;
|
||||
self.cpu.pc = 0x0000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
use crate::computers::kim1::Kim1;
|
||||
|
||||
impl Kim1 {
|
||||
pub fn tick(&mut self) {
|
||||
println!("<- START KIM-1 Backplane Tick");
|
||||
let (address_bus, data, rw) = self.cpu.tick2(self.address_bus, self.data_bus);
|
||||
self.address_bus = address_bus;
|
||||
self.data_bus = data;
|
||||
self.cpu_read = rw;
|
||||
// now tick the various items connected
|
||||
|
||||
self.rriot1.tick(self.address_bus, self.data_bus, false, self.cpu_read);
|
||||
self.rriot2.tick(self.address_bus, self.data_bus, false, self.cpu_read);
|
||||
self.ram.tick(self.address_bus, self.data_bus, self.cpu_read, true);
|
||||
|
||||
|
||||
let (rr1_io, rr1_ram, rr1_rom) = self.rriot1.dump_data();
|
||||
let (rr2_io, rr2_ram, rr2_rom) = self.rriot2.dump_data();
|
||||
|
||||
println!(" 0x0000 -> RAM / {}", self.ram.dump_data());
|
||||
println!(" 0x1700 -> RRIOT 1 / 0x{rr1_io:04x}/0x{rr1_ram:04x}/0x{rr1_rom:04x}");
|
||||
println!(" 0x1740 -> RRIOT 2 / 0x{rr2_io:04x}/0x{rr2_ram:04x}/0x{rr2_rom:04x}");
|
||||
// display the memory map and device states
|
||||
println!("-> FINISH KIM-1 Backplane Tick");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod beneater;
|
||||
pub mod rom_only;
|
||||
pub mod kim1;
|
||||
pub mod ram_rom;
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::backplane::Backplane;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
pub struct RamRomComputer {
|
||||
rom: At28C256,
|
||||
ram: Hm62256,
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
read_mode: bool,
|
||||
}
|
||||
|
||||
impl Backplane for RamRomComputer {
|
||||
fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
fn read_mode(&self) -> bool {
|
||||
self.read_mode
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
fn set_read_mode(&mut self, new_mode: bool) {
|
||||
self.read_mode = new_mode;
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value;
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
impl RamRomComputer {
|
||||
pub fn new() -> RamRomComputer {
|
||||
RamRomComputer {
|
||||
rom: At28C256::default(),
|
||||
ram: Hm62256::default(),
|
||||
data_bus: 0x00,
|
||||
address_bus: 0x0000,
|
||||
/// is the CPU reading from the 'other' device?
|
||||
read_mode: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod backplane;
|
||||
@@ -0,0 +1,59 @@
|
||||
use crate::constants::constants_system::{SIZE_32KB, SIZE_64KB};
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::backplane::Backplane;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
pub struct RomOnlyComputer {
|
||||
rom: At28C256,
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
read_mode: bool,
|
||||
}
|
||||
|
||||
impl Backplane for RomOnlyComputer {
|
||||
fn data_bus(&self) -> u8 { self.data_bus }
|
||||
fn address_bus(&self) -> u16 { self.address_bus }
|
||||
fn read_mode(&self) -> bool { self.read_mode }
|
||||
|
||||
fn set_read_mode(&mut self, new_mode: bool) {
|
||||
self.read_mode = new_mode
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
println!("COMPUTER: Preparing to tick.");
|
||||
|
||||
// do are we being addressed?
|
||||
println!("COMPUTER: BUSSES PRE: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode);
|
||||
let (new_addr, new_data) = self.rom.tick(self.address_bus, self.data_bus, self.read_mode);
|
||||
self.set_address_bus(new_addr);
|
||||
self.set_data_bus(new_data);
|
||||
println!("COMPUTER: BUSSES POST: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode);
|
||||
println!("COMPUTER: Done ticking.");
|
||||
}
|
||||
}
|
||||
|
||||
impl RomOnlyComputer {
|
||||
pub fn new() -> RomOnlyComputer {
|
||||
let mut working = vec![0x00u8; SIZE_32KB];
|
||||
for index in 0..SIZE_32KB {
|
||||
working[index] = index as u8;
|
||||
}
|
||||
RomOnlyComputer::program(working)
|
||||
}
|
||||
|
||||
pub fn program(rom: Vec<u8>) -> RomOnlyComputer {
|
||||
RomOnlyComputer {
|
||||
rom: At28C256::new(0x000, 0x3fff, rom),
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00,
|
||||
read_mode: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod backplane;
|
||||
@@ -1,5 +1,6 @@
|
||||
// Instruction OP Codes
|
||||
|
||||
/// Instruction OP Codes
|
||||
/// Verified against
|
||||
/// https://www.nesdev.org/obelisk-6502-guide/reference.html
|
||||
/// ADC
|
||||
pub const ISA_OP_ADC_I: u8 = 0x69;
|
||||
pub const ISA_OP_ADC_Z: u8 = 0x65;
|
||||
@@ -18,78 +19,59 @@ pub const ISA_OP_AND_ABSX: u8 = 0x3d;
|
||||
pub const ISA_OP_AND_ABSY: u8 = 0x39;
|
||||
pub const ISA_OP_AND_INDX: u8 = 0x21;
|
||||
pub const ISA_OP_AND_INDY: u8 = 0x31;
|
||||
|
||||
/// ASL
|
||||
pub const ISA_OP_ASL_A: u8 = 0x0a;
|
||||
pub const ISA_OP_ASL_Z: u8 = 0x06;
|
||||
pub const ISA_OP_ASL_ZX: u8 = 0x16;
|
||||
pub const ISA_OP_ASL_ABS: u8 = 0x0e;
|
||||
pub const ISA_OP_ASL_ABSX: u8 = 0x1e;
|
||||
|
||||
/// BCC
|
||||
pub const ISA_OP_BCC: u8 = 0x90;
|
||||
|
||||
/// BCS
|
||||
pub const ISA_OP_BCS: u8 = 0xb0;
|
||||
|
||||
/// BEQ
|
||||
pub const ISA_OP_BEQ: u8 = 0xf0;
|
||||
/// BIT
|
||||
|
||||
pub const ISA_OP_BIT_ZP: u8 = 0x24;
|
||||
pub const ISA_OP_BIT_ABS: u8 = 0x2c;
|
||||
|
||||
/// BMI
|
||||
pub const ISA_OP_BMI: u8 = 0x30;
|
||||
|
||||
/// BNE
|
||||
pub const ISA_OP_BNE: u8 = 0xd0;
|
||||
|
||||
/// BPL
|
||||
pub const ISA_OP_BPL: u8 = 0x10;
|
||||
|
||||
/// BRK
|
||||
pub const ISA_OP_BRK: u8 = 0x00;
|
||||
/// BVC
|
||||
|
||||
pub const ISA_OP_BVC: u8 = 0x50;
|
||||
|
||||
/// BVS
|
||||
pub const ISA_OP_BVS: u8 = 0x70;
|
||||
|
||||
pub const ISA_OP_CLC: u8 = 0x18;
|
||||
|
||||
pub const ISA_OP_CLD: u8 = 0xd8;
|
||||
|
||||
pub const ISA_OP_CLI: u8 = 0x58;
|
||||
|
||||
pub const ISA_OP_CLV: u8 = 0xb8;
|
||||
|
||||
pub const ISA_OP_CMP_I: u8 = 0xc9;
|
||||
pub const ISA_OP_CMP_ZP: u8 = 0xc5;
|
||||
pub const ISA_OP_CMP_ZPX: u8 = 0xd5;
|
||||
pub const ISA_OP_CMP_ABS: u8 = 0xcd;
|
||||
pub const ISA_OP_CMP_ABSX: u8 = 0xdd;
|
||||
pub const ISA_OP_CMP_ABSY: u8 = 0xd9;
|
||||
pub const ISA_OP_CMP_INDX: u8 = 0xc1;
|
||||
pub const ISA_OP_CMP_INDY: u8 = 0xd1;
|
||||
|
||||
pub const ISA_OP_CPX_I: u8 = 0xe0;
|
||||
pub const ISA_OP_CPX_ZP: u8 = 0xe4;
|
||||
pub const ISA_OP_CPX_ABS: u8 = 0xec;
|
||||
|
||||
pub const ISA_OP_CPY_I: u8 = 0xc0;
|
||||
pub const ISA_OP_CPY_ZP: u8 = 0xc4;
|
||||
pub const ISA_OP_CPY_ABS: u8 = 0xcc;
|
||||
|
||||
pub const ISA_OP_DEC_ZP: u8 = 0xc6;
|
||||
pub const ISA_OP_DEC_ZPX: u8 = 0xd6;
|
||||
pub const ISA_OP_DEC_ABS: u8 = 0xce;
|
||||
pub const ISA_OP_DEC_ABSX: u8 = 0xde;
|
||||
|
||||
pub const ISA_OP_DEX: u8 = 0xca;
|
||||
|
||||
pub const ISA_OP_DEY: u8 = 0x88;
|
||||
|
||||
pub const ISA_OP_EOR_I: u8 = 0x49;
|
||||
pub const ISA_OP_EOR_ZP: u8 = 0x45;
|
||||
pub const ISA_OP_EOR_ZPX: u8 = 0x55;
|
||||
@@ -98,50 +80,39 @@ pub const ISA_OP_EOR_ABSX: u8 = 0x5d;
|
||||
pub const ISA_OP_EOR_ABSY: u8 = 0x59;
|
||||
pub const ISA_OP_EOR_INDX: u8 = 0x41;
|
||||
pub const ISA_OP_EOR_INDY: u8 = 0x51;
|
||||
|
||||
pub const ISA_OP_INC_ZP: u8 = 0xe6;
|
||||
pub const ISA_OP_INC_ZPX: u8 = 0xf6;
|
||||
pub const ISA_OP_INC_ABS: u8 = 0xee;
|
||||
pub const ISA_OP_INC_ABSX: u8 = 0xfe;
|
||||
|
||||
|
||||
pub const ISA_OP_INX: u8 = 0xe8;
|
||||
|
||||
pub const ISA_OP_INY: u8 = 0xc8;
|
||||
|
||||
pub const ISA_OP_JMP_ABS: u8 = 0x4c;
|
||||
pub const ISA_OP_JMP_IND: u8 = 0x6c;
|
||||
|
||||
pub const ISA_OP_JSR: u8 = 0x20;
|
||||
|
||||
pub const ISA_OP_LDA_I: u8 = 0xA9;
|
||||
pub const ISA_OP_LDA_Z: u8 = 0xA5;
|
||||
pub const ISA_OP_LDA_ZX: u8 = 0xB5;
|
||||
pub const ISA_OP_LDA_ABS: u8 = 0xAD;
|
||||
pub const IAS_OP_LDA_ABSX: u8 = 0xBD;
|
||||
pub const ISA_OP_LDA_ABSX: u8 = 0xBD;
|
||||
pub const ISA_OP_LDA_ABSY: u8 = 0xB9;
|
||||
pub const ISA_OP_LDA_INDX: u8 = 0xA1;
|
||||
pub const ISA_OP_LDA_INDY: u8 = 0xB1;
|
||||
|
||||
pub const ISA_OP_LDX_I: u8 = 0xa2;
|
||||
pub const ISA_OP_LDX_ZP: u8 = 0xa6;
|
||||
pub const ISA_OP_LDX_ZPY: u8 = 0x86;
|
||||
pub const ISA_OP_LDX_ZPY: u8 = 0xb6;
|
||||
pub const ISA_OP_LDX_ABS: u8 = 0xae;
|
||||
pub const ISA_OP_LDX_ABSY: u8 = 0xbe;
|
||||
|
||||
pub const ISA_OP_LDY_I: u8 = 0xa0;
|
||||
pub const ISA_OP_LDY_ZP: u8 = 0xa4;
|
||||
pub const ISA_OP_LDY_ZPX: u8 = 0xb4;
|
||||
pub const ISA_OP_LDY_ABS: u8 = 0xac;
|
||||
pub const ISA_OP_LDY_ABSX: u8 = 0xac;
|
||||
pub const ISA_OP_LDY_ABSX: u8 = 0xbc;
|
||||
pub const ISA_OP_LSR_A: u8 = 0x4a;
|
||||
pub const ISA_OP_LSR_ZP: u8 = 0x46;
|
||||
pub const ISA_OP_LSR_ZPX: u8 = 0x56;
|
||||
pub const ISA_OP_LSR_ABS: u8 = 0x4e;
|
||||
pub const ISA_OP_LSR_ABSX: u8 = 0x5e;
|
||||
|
||||
pub const ISA_OP_NOP: u8 = 0xEA;
|
||||
|
||||
pub const ISA_OP_ORA_I: u8 = 0x09;
|
||||
pub const ISA_OP_ORA_ZP: u8 = 0x05;
|
||||
pub const ISA_OP_ORA_ZPX: u8 = 0x15;
|
||||
@@ -150,24 +121,15 @@ pub const ISA_OP_ORA_ABSX: u8 = 0x1d;
|
||||
pub const ISA_OP_ORA_ABSY: u8 = 0x19;
|
||||
pub const ISA_OP_ORA_INDX: u8 = 0x01;
|
||||
pub const ISA_OP_ORA_INDY: u8 = 0x11;
|
||||
|
||||
pub const ISA_OP_PHA: u8 = 0x48;
|
||||
|
||||
pub const ISA_OP_PHP: u8 = 0x08;
|
||||
///
|
||||
pub const ISA_OP_PLA: u8 = 0x68;
|
||||
///
|
||||
pub const ISA_OP_PLP: u8 = 0x28;
|
||||
|
||||
///
|
||||
///
|
||||
pub const ISA_OP_ROL_A: u8 = 0x2a;
|
||||
pub const ISA_OP_ROL_ZP: u8 = 0x26;
|
||||
pub const ISA_OP_ROL_ZPX: u8 = 0x36;
|
||||
pub const ISA_OP_ROL_ABS: u8 = 0x2e;
|
||||
pub const ISA_OP_ROL_ABSX: u8 = 0x3e;
|
||||
|
||||
///
|
||||
pub const ISA_OP_ROR_A: u8 = 0x6a;
|
||||
pub const ISA_OP_ROR_ZP: u8 = 0x66;
|
||||
pub const ISA_OP_ROR_ZPX: u8 = 0x76;
|
||||
@@ -194,7 +156,7 @@ pub const ISA_OP_STA_ABSY: u8 = 0x99;
|
||||
pub const ISA_OP_STA_INDX: u8 = 0x81;
|
||||
pub const ISA_OP_STA_INDY: u8 = 0x91;
|
||||
pub const ISA_OP_STX_ZP: u8 = 0x86;
|
||||
pub const ISA_OP_STX_ZPX: u8 = 0x96;
|
||||
pub const ISA_OP_STX_ZPY: u8 = 0x96;
|
||||
pub const ISA_OP_STX_ABS: u8 = 0x8e;
|
||||
pub const ISA_OP_STY_ZP: u8 = 0x84;
|
||||
pub const ISA_OP_STY_ZPX: u8 = 0x94;
|
||||
@@ -0,0 +1,57 @@
|
||||
/// STUB Parts
|
||||
pub const ISA_STUB_ADC: &str = "ADC";
|
||||
pub const ISA_STUB_AND: &str = "AND";
|
||||
pub const ISA_STUB_ASL: &str = "ASL";
|
||||
pub const ISA_STUB_BCC: &str = "BCC";
|
||||
pub const ISA_STUB_BCS: &str = "BCS";
|
||||
pub const ISA_STUB_BEQ: &str = "BEQ";
|
||||
pub const ISA_STUB_BIT: &str = "BIT";
|
||||
pub const ISA_STUB_BMI: &str = "BMI";
|
||||
pub const ISA_STUB_BNE: &str = "BNE";
|
||||
pub const ISA_STUB_BPL: &str = "BPL";
|
||||
pub const ISA_STUB_BRK: &str = "BRK";
|
||||
pub const ISA_STUB_BVC: &str = "BVC";
|
||||
pub const ISA_STUB_BVS: &str = "BVS";
|
||||
pub const ISA_STUB_CLC: &str = "CLC";
|
||||
pub const ISA_STUB_CLD: &str = "CLD";
|
||||
pub const ISA_STUB_CLI: &str = "CLI";
|
||||
pub const ISA_STUB_CLV: &str = "CLV";
|
||||
pub const ISA_STUB_CMP: &str = "CMP";
|
||||
pub const ISA_STUB_CPX: &str = "CPX";
|
||||
pub const ISA_STUB_CPY: &str = "CPY";
|
||||
pub const ISA_STUB_DEC: &str = "DEC";
|
||||
pub const ISA_STUB_DEX: &str = "DEX";
|
||||
pub const ISA_STUB_DEY: &str = "DEY";
|
||||
pub const ISA_STUB_EOR: &str = "EOR";
|
||||
pub const ISA_STUB_INC: &str = "INC";
|
||||
pub const ISA_STUB_INX: &str = "INX";
|
||||
pub const ISA_STUB_INY: &str = "INY";
|
||||
pub const ISA_STUB_JMP: &str = "JMP";
|
||||
pub const ISA_STUB_JSR: &str = "JSR";
|
||||
pub const ISA_STUB_LDA: &str = "LDA";
|
||||
pub const ISA_STUB_LDX: &str = "LDX";
|
||||
pub const ISA_STUB_LDY: &str = "LDY";
|
||||
pub const ISA_STUB_LSR: &str = "LSR";
|
||||
pub const ISA_STUB_NOP: &str = "NOP";
|
||||
pub const ISA_STUB_ORA: &str = "ORA";
|
||||
pub const ISA_STUB_PHA: &str = "PHA";
|
||||
pub const ISA_STUB_PHP: &str = "PHP";
|
||||
pub const ISA_STUB_PLA: &str = "PLA";
|
||||
pub const ISA_STUB_PLP: &str = "PLP";
|
||||
pub const ISA_STUB_ROL: &str = "ROL";
|
||||
pub const ISA_STUB_ROR: &str = "ROR";
|
||||
pub const ISA_STUB_RTI: &str = "RTI";
|
||||
pub const ISA_STUB_RTS: &str = "RTS";
|
||||
pub const ISA_STUB_SBC: &str = "SBC";
|
||||
pub const ISA_STUB_SEC: &str = "SEC";
|
||||
pub const ISA_STUB_SED: &str = "SED";
|
||||
pub const ISA_STUB_SEI: &str = "SEI";
|
||||
pub const ISA_STUB_STA: &str = "STA";
|
||||
pub const ISA_STUB_STX: &str = "STX";
|
||||
pub const ISA_STUB_STY: &str = "STY";
|
||||
pub const ISA_STUB_TAX: &str = "TAX";
|
||||
pub const ISA_STUB_TAY: &str = "TAY";
|
||||
pub const ISA_STUB_TSX: &str = "TSX";
|
||||
pub const ISA_STUB_TXA: &str = "TXA";
|
||||
pub const ISA_STUB_TXS: &str = "TXS";
|
||||
pub const ISA_STUB_TYA: &str = "TYA";
|
||||
@@ -0,0 +1,23 @@
|
||||
pub const MOS6530_DRA: u8 = 0x00;
|
||||
pub const MOS6530_DDRA: u8 = 0x01;
|
||||
pub const MOS6530_DRB: u8 = 0x02;
|
||||
pub const MOS6530_DDRB: u8 = 0x03;
|
||||
|
||||
/*
|
||||
0 X Data Register A
|
||||
1 X Data Direction Register A
|
||||
2 X Data Register B
|
||||
3 X Data Direction Register B
|
||||
4 0 Count down from value, divide by 1, disable IRQ 1 ???
|
||||
5 0 Count down from value, divide by 8, disable IRQ 1 ???
|
||||
6 0 Count down from value, divide by 64, disable IRQ 1 Read current counter value, disable IRQ
|
||||
7 0 Count down from value, divide by 1024, disable IRQ 1 Read counter status, bit7 = 1 means counter past zero
|
||||
8 X Data Register A (mirror ?)
|
||||
9 X Data Direction Register A (mirror ?)
|
||||
A X Data Register B (mirror ?)
|
||||
B X Data Direction Register B (mirror ?)
|
||||
C 0 Count down from value, divide by 1, enable IRQ 1 ???
|
||||
D 0 Count down from value, divide by 8, enable IRQ 1 ???
|
||||
E 0 Count down from value, divide by 64, enable IRQ 1 Read current counter value, enable IRQ
|
||||
F 0 Count down from value, divide by 1024, enable IRQ 1 Read counter status, bit7 = 1 means counter past zero
|
||||
*/
|
||||
@@ -0,0 +1,11 @@
|
||||
pub const SIZE_1KB: usize = 1024;
|
||||
pub const SIZE_32KB: usize = SIZE_1KB * 32;
|
||||
pub const SIZE_64KB: usize = SIZE_1KB * 64;
|
||||
|
||||
// S Suffixed constants are for indexing slices
|
||||
pub const OFFSET_NMI_VECTOR: u16 = 0xfffa;
|
||||
pub const OFFSET_NMI_VECTORS: usize = 0xfffa;
|
||||
pub const OFFSET_RESET_VECTOR: u16 = 0xfffc;
|
||||
pub const OFFSET_RESET_VECTORS: usize = 0xffff;
|
||||
pub const OFFSET_INT_VECTOR: u16 = 0xfffe;
|
||||
pub const OFFSET_INT_VECTORS: usize = 0xfffe;
|
||||
@@ -0,0 +1,7 @@
|
||||
use std::borrow::ToOwned;
|
||||
use once_cell::unsync::Lazy;
|
||||
|
||||
pub const TEST_RESOURCES_ROOT: &str = "/home/tmerritt/Projects/resources/test";
|
||||
pub const TEST_PERIPH_ROOT: &str = "/home/tmerritt/Projects/resources/test/periph";
|
||||
pub const TEST_PERIPH_AT28C256_ROOT: &str = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256";
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
pub const VIA6522_ORB: u8 = 0b0000;
|
||||
pub const VIA6522_ORA: u8 = 0b0001;
|
||||
pub const VIA6522_DDRB: u8 = 0b0010;
|
||||
pub const VIA6522_DDRA: u8 = 0b0011;
|
||||
|
||||
/// Timer 1 Write Latch
|
||||
pub const VIA6522_T1WL: u8 = 0b0100;
|
||||
/// Timer 1 Read Counter High
|
||||
pub const VIA6522_T1CL: u8 = 0b0101;
|
||||
pub const VIA6522_T1CH: u8 = 0b0110;
|
||||
pub const VIA6522_T1LL: u8 = 0b0111;
|
||||
pub const VIA6522_T1LH: u8 = 0b1000;
|
||||
pub const VIA6522_T2LL: u8 = 0b1001;
|
||||
pub const VIA6522_T2CH: u8 = 0b1010;
|
||||
pub const VIA6522_SR: u8 = 0b1011;
|
||||
pub const VIA6522_ACR: u8 = 0b1100;
|
||||
pub const VIA6522_PCR: u8 = 0b1101;
|
||||
pub const VIA6522_IFR: u8 = 0b1110;
|
||||
pub const VIA6522_IER: u8 = 0b1111;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod constants_isa_op;
|
||||
pub mod constants_isa_stub;
|
||||
pub mod constants_system;
|
||||
pub mod constants_via6522;
|
||||
pub mod constants_mos6530;
|
||||
pub mod constants_test;
|
||||
+1282
-1717
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
use crate::address_mode::AddressMode;
|
||||
|
||||
pub struct InstructionStringify {}
|
||||
impl InstructionStringify {
|
||||
pub fn format(mode: AddressMode, prefix: &str) -> String {
|
||||
let suffix = match mode {
|
||||
AddressMode::Implied => "",
|
||||
AddressMode::Accumulator => "A",
|
||||
AddressMode::Immediate(value) => &*format!("#${value:02x}"),
|
||||
AddressMode::ZeroPage(value) => &*format!("${value:02x}"),
|
||||
AddressMode::ZeroPageX(value) => &*format!("${value:02x},X"),
|
||||
AddressMode::Absolute(offset) => &*format!("${offset:04x}"),
|
||||
AddressMode::AbsoluteX(offset) => &*format!("${offset:04x},X"),
|
||||
AddressMode::AbsoluteY(offset) => &*format!("${offset:04x},Y"),
|
||||
AddressMode::IndirectX(value) => &*format!("(${value:02x},X)"),
|
||||
AddressMode::IndirectY(value) => &*format!("(${value:02x}),Y")
|
||||
};
|
||||
|
||||
format!("{} {}", prefix, suffix)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,74 +0,0 @@
|
||||
use crate::address_mode::AddressMode::*;
|
||||
use crate::constants::*;
|
||||
use crate::instruction::Instruction;
|
||||
use crate::instruction::Instruction::*;
|
||||
|
||||
pub struct Decoder {}
|
||||
|
||||
|
||||
impl Decoder {
|
||||
/// decode
|
||||
///
|
||||
/// Returns the decoded instruction or a NOP.
|
||||
/// NOP will be returned when an instruction without a valid parameter
|
||||
/// and more data is required to decode.
|
||||
pub fn decode(decode_from: Vec<u8>) -> Instruction {
|
||||
NOP
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn valid_decodes() {
|
||||
let params = vec![
|
||||
(vec![ISA_OP_ADC_I, 0xab], ADC(Immediate(0xab))),
|
||||
(vec![ISA_OP_ADC_Z, 0xab], ADC(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_ADC_ZX, 0xab], ADC(ZeroPageX(0xab))),
|
||||
(vec![ISA_OP_ADC_ABS, 0xab, 0xcd], ADC(Absolute(0xcdab))),
|
||||
(vec![ISA_OP_ADC_ABSX, 0xcd, 0xab], ADC(AbsoluteX(0xabcd))),
|
||||
(vec![ISA_OP_ADC_ABSY, 0xcd, 0xab], ADC(AbsoluteY(0xabcd))),
|
||||
(vec![ISA_OP_ADC_INDX, 0xab], ADC(IndirectX(0xab))),
|
||||
(vec![ISA_OP_ADC_INDY, 0xcd], ADC(IndirectY(0xcd))),
|
||||
|
||||
(vec![ISA_OP_AND_I, 0xab], AND(Immediate(0xab))),
|
||||
(vec![ISA_OP_AND_Z, 0xab], AND(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_AND_ZX, 0xab], AND(ZeroPageX(0xab))),
|
||||
(vec![ISA_OP_AND_ABS, 0xcd, 0xab], AND(Absolute(0xabcd))),
|
||||
|
||||
(vec![ISA_OP_ASL_A], ASL(Accumulator)),
|
||||
(vec![ISA_OP_ASL_Z, 0xab], ASL(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_ASL_ZX, 0xab], ASL(ZeroPageX(0xab))),
|
||||
(vec![ISA_OP_ASL_ABS, 0xab, 0xcd], ASL(Absolute(0xcdab))),
|
||||
(vec![ISA_OP_ASL_ABSX, 0xab, 0xcd], ASL(AbsoluteX(0xcdab))),
|
||||
|
||||
(vec![ISA_OP_BCC, 0xab], BCC(Immediate(0xab))),
|
||||
|
||||
(vec![ISA_OP_BEQ, 0xab], BEQ(Immediate(0xab))),
|
||||
|
||||
(vec![ISA_OP_BIT_ZP, 0xab], BIT(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_BIT_ABS, 0xab, 0xcd], BIT(Absolute(0xcdab))),
|
||||
|
||||
(vec![ISA_OP_BMI, 0xab], BMI(Immediate(0xab))),
|
||||
(vec![ISA_OP_BNE, 0xab], BNE(Immediate(0xab))),
|
||||
(vec![ISA_OP_BPL, 0xab], BPL(Immediate(0xab))),
|
||||
(vec![ISA_OP_BVC, 0xab], BVC(Immediate(0xab))),
|
||||
(vec![ISA_OP_BVS, 0xab], BVS(Immediate(0xab))),
|
||||
|
||||
(vec![ISA_OP_BRK], BRK),
|
||||
|
||||
];
|
||||
|
||||
for (bytes, instruction) in params {
|
||||
println!("Expecting {:?} to be {:?}", bytes, instruction);
|
||||
|
||||
assert_eq!(
|
||||
Decoder::decode(bytes),
|
||||
instruction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
use crate::address_mode::AddressMode::*;
|
||||
use crate::constants::*;
|
||||
use crate::instruction::Instruction;
|
||||
use crate::instruction::Instruction::*;
|
||||
|
||||
pub struct Encoder {}
|
||||
|
||||
impl Encoder {
|
||||
pub fn encode(to_encode: Instruction) -> Vec<u8> {
|
||||
match to_encode {
|
||||
// ADC(_) => {}
|
||||
// AND(_) => {}
|
||||
// ASL(_) => {}
|
||||
BCC(mode) => {
|
||||
match mode {
|
||||
Immediate(address)=> {
|
||||
vec![ISA_OP_BCC, address]
|
||||
}
|
||||
_ => NOP.to_bytes()
|
||||
}
|
||||
}
|
||||
// BCS(_) => {}
|
||||
// BEQ(_) => {}
|
||||
// BIT(_) => {}
|
||||
// BMI(_) => {}
|
||||
// BNE(_) => {}
|
||||
// BPL(_) => {}
|
||||
// BRK => {}
|
||||
// BVC(_) => {}
|
||||
// BVS(_) => {}
|
||||
// CLC => {}
|
||||
// CLD => {}
|
||||
// CLI => {}
|
||||
// CLV => {}
|
||||
// CMP(_) => {}
|
||||
// CPX(_) => {}
|
||||
// CPY(_) => {}
|
||||
// DEC(_) => {}
|
||||
// DEX => {}
|
||||
// DEY => {}
|
||||
// EOR(_) => {}
|
||||
// INC(_) => {}
|
||||
// INX => {}
|
||||
// INY => {}
|
||||
// JMP(_) => {}
|
||||
// JSR(_) => {}
|
||||
// LDA(_) => {}
|
||||
// LDX(_) => {}
|
||||
// LDY(_) => {}
|
||||
// LSR(_) => {}
|
||||
// NOP => {}
|
||||
// ORA(_) => {}
|
||||
// PHA => {}
|
||||
// PHP => {}
|
||||
// PLA => {}
|
||||
// PLP => {}
|
||||
// ROL(_) => {}
|
||||
// ROR(_) => {}
|
||||
// RTI => {}
|
||||
// RTS => {}
|
||||
// SBC(_) => {}
|
||||
// SEC => {}
|
||||
// SED => {}
|
||||
// SEI => {}
|
||||
// STA(_) => {}
|
||||
// STX(_) => {}
|
||||
// STY(_) => {}
|
||||
// TAX => {}
|
||||
// TAY => {}
|
||||
// TSX => {}
|
||||
// TXA => {}
|
||||
// TXS => {}
|
||||
// TYA => {}
|
||||
_ => NOP.to_bytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::constants::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn adc_decode() {
|
||||
let params = vec![
|
||||
(vec![ISA_OP_ADC_I, 0xab], ADC(Immediate(0xab))),
|
||||
(vec![ISA_OP_ADC_Z, 0xab], ADC(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_ADC_ZX, 0xab], ADC(ZeroPageX(0xab))),
|
||||
(vec![ISA_OP_ADC_ABS, 0xab, 0xcd], ADC(Absolute(0xcdab))),
|
||||
(vec![ISA_OP_ADC_ABSX, 0xcd, 0xab], ADC(AbsoluteX(0xabcd))),
|
||||
(vec![ISA_OP_ADC_ABSY, 0xcd, 0xab], ADC(AbsoluteY(0xabcd))),
|
||||
(vec![ISA_OP_ADC_INDX, 0xab], ADC(IndirectX(0xab))),
|
||||
(vec![ISA_OP_ADC_INDY, 0xcd], ADC(IndirectY(0xcd))),
|
||||
|
||||
(vec![ISA_OP_AND_I, 0xab], AND(Immediate(0xab))),
|
||||
(vec![ISA_OP_AND_Z, 0xab], AND(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_AND_ZX, 0xab], AND(ZeroPageX(0xab))),
|
||||
(vec![ISA_OP_AND_ABS, 0xcd, 0xab], AND(Absolute(0xabcd))),
|
||||
|
||||
(vec![ISA_OP_ASL_A], ASL(Accumulator)),
|
||||
(vec![ISA_OP_ASL_Z, 0xab], ASL(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_ASL_ZX, 0xab], ASL(ZeroPageX(0xab))),
|
||||
(vec![ISA_OP_ASL_ABS, 0xab, 0xcd], ASL(Absolute(0xcdab))),
|
||||
(vec![ISA_OP_ASL_ABSX, 0xab, 0xcd], ASL(AbsoluteX(0xcdab))),
|
||||
|
||||
(vec![ISA_OP_BCC, 0xab], BCC(Immediate(0xab))),
|
||||
|
||||
(vec![ISA_OP_BEQ, 0xab], BEQ(Immediate(0xab))),
|
||||
|
||||
(vec![ISA_OP_BIT_ZP, 0xab], BIT(ZeroPage(0xab))),
|
||||
(vec![ISA_OP_BIT_ABS, 0xab, 0xcd], BIT(Absolute(0xcdab))),
|
||||
|
||||
(vec![ISA_OP_BMI, 0xab], BMI(Immediate(0xab))),
|
||||
(vec![ISA_OP_BNE, 0xab], BNE(Immediate(0xab))),
|
||||
(vec![ISA_OP_BPL, 0xab], BPL(Immediate(0xab))),
|
||||
(vec![ISA_OP_BVC, 0xab], BVC(Immediate(0xab))),
|
||||
(vec![ISA_OP_BVS, 0xab], BVS(Immediate(0xab))),
|
||||
|
||||
(vec![ISA_OP_BRK], BRK),
|
||||
|
||||
];
|
||||
|
||||
// for (bytes, instruction) in params {
|
||||
// let encoded = Encoder::encode(bytes);
|
||||
// assert_eq!(encoded, instruction.into());
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use crate::mos6502flags::Mos6502Flag;
|
||||
|
||||
pub enum MicrocodeStep {
|
||||
ReadRegisterA,
|
||||
ReadRegisterX,
|
||||
ReadRegisterY,
|
||||
ReadFlag(Mos6502Flag),
|
||||
WriteRegisterA,
|
||||
WriteRegisterX,
|
||||
WriteRegisterY,
|
||||
WriteFlag(Mos6502Flag, bool),
|
||||
ReadMemory(u16),
|
||||
WriteMemory(u16, u8),
|
||||
ALUAdd(u8, u8),
|
||||
ALUSub(u8, u8),
|
||||
ALUAddC(u8, u8, bool),
|
||||
ALUSubC(u8, u8, bool),
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
pub mod encode;
|
||||
pub mod decoder;
|
||||
pub mod microcode_steps;
|
||||
+12
-5
@@ -1,7 +1,14 @@
|
||||
#![feature(slice_as_array)]
|
||||
|
||||
pub mod computers;
|
||||
pub mod address_mode;
|
||||
pub mod mos6502cpu;
|
||||
pub mod instruction;
|
||||
pub mod mos6502flags;
|
||||
pub mod isa;
|
||||
pub mod constants;
|
||||
mod instruction_stringify;
|
||||
pub mod instruction;
|
||||
pub mod instruction_table;
|
||||
pub mod mos6502cpu;
|
||||
pub mod mos6502flags;
|
||||
pub mod op_info;
|
||||
pub mod operand;
|
||||
pub mod operation;
|
||||
pub mod periph;
|
||||
mod backplane;
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
||||
|
||||
pub const SIZE_1KB: usize = 1024 * 1024;
|
||||
pub const SIZE_64KB: usize = SIZE_1KB * 64;
|
||||
|
||||
pub struct Mos6502Cpu {
|
||||
memory: [u8; SIZE_64KB],
|
||||
a: u8,
|
||||
x: u8,
|
||||
y: u8,
|
||||
flags: Mos6502Flags,
|
||||
pc: u16,
|
||||
s: u8,
|
||||
microcode_step: u8
|
||||
}
|
||||
|
||||
impl Mos6502Cpu {
|
||||
pub fn new() -> Mos6502Cpu {
|
||||
Mos6502Cpu {
|
||||
memory: [0; SIZE_64KB],
|
||||
a: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
flags: Mos6502Flags::default(),
|
||||
pc: 0,
|
||||
s: 0xfd,
|
||||
microcode_step: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
||||
self.flags.flag(flag_to_read)
|
||||
}
|
||||
pub fn poke_flag(&mut self, flag_to_set: Mos6502Flag, new_value: bool) {
|
||||
if new_value { self.flags.set_flag(flag_to_set) } else { self.flags.clear_flag(flag_to_set) }
|
||||
}
|
||||
|
||||
pub fn peek(&self, offset: u16) -> u8 {
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
|
||||
pub fn poke(&mut self, offset: u16, value: u8) {
|
||||
self.memory[offset as usize] = value
|
||||
}
|
||||
|
||||
pub fn peek_a(&self) -> u8 {
|
||||
self.a
|
||||
}
|
||||
pub fn poke_a(&mut self, new_a: u8) {
|
||||
self.a = new_a;
|
||||
}
|
||||
|
||||
pub fn peek_x(&self) -> u8 {
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn poke_x(&mut self, new_x: u8) {
|
||||
self.x = new_x
|
||||
}
|
||||
|
||||
pub fn peek_y(&self) -> u8 {
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn poke_y(&mut self, new_y: u8) {
|
||||
self.y = new_y
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,772 @@
|
||||
use crate::address_mode::AddressMode;
|
||||
use crate::constants::constants_isa_op::ISA_OP_NOP;
|
||||
use crate::constants::constants_system::*;
|
||||
use crate::instruction::Instruction;
|
||||
use crate::instruction_table::INSTRUCTION_TABLE;
|
||||
use crate::mos6502flags::Mos6502Flag::*;
|
||||
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
||||
use crate::op_info::OpInfo;
|
||||
use crate::operand::Operand;
|
||||
use crate::operation::Operation;
|
||||
use log::trace;
|
||||
use crate::mos6502cpu::tick_stages::Mos6502TickStates;
|
||||
use crate::mos6502cpu::tick_stages::Mos6502TickStates::*;
|
||||
|
||||
pub struct Mos6502Cpu {
|
||||
pub(crate) memory: [u8; SIZE_64KB],
|
||||
/// accumulator
|
||||
pub(crate) a: u8,
|
||||
/// x register
|
||||
pub(crate) x: u8,
|
||||
/// y register
|
||||
pub(crate) y: u8,
|
||||
/// cpu flags
|
||||
pub(crate) flags: Mos6502Flags,
|
||||
/// program counter
|
||||
pub pc: u16,
|
||||
/// stack offset
|
||||
pub(crate) s: u8,
|
||||
pub microcode_step: u8,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) data_bus: u8,
|
||||
pub(crate) ir: Instruction, // Instruction Register
|
||||
pub(crate) oi: OpInfo,
|
||||
pub(crate) has_reset: bool,
|
||||
pub(crate) iv: u16, // Interrupt Vector
|
||||
pub(crate) cycle_carry: u16, // Value to hold between microsteps
|
||||
pub(crate) ir_bytes: [u8; 4],
|
||||
/// CPU Read signal
|
||||
pub read_signal: bool,
|
||||
pub(crate) reset_vector: u16,
|
||||
pub(crate) int_vector: u16,
|
||||
pub(crate) nmi_vector: u16,
|
||||
pub tick_stage: Mos6502TickStates
|
||||
}
|
||||
|
||||
impl Mos6502Cpu {
|
||||
/// set_data_bus
|
||||
///
|
||||
/// Sets data on the data bus.
|
||||
/// Used when CPU is in "R" mode
|
||||
pub fn set_data_bus(&mut self, to_set: u8) {
|
||||
self.data_bus = to_set;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mos6502Cpu {
|
||||
fn default() -> Self {
|
||||
let mut working = Mos6502Cpu {
|
||||
memory: [0x00; SIZE_64KB],
|
||||
a: 0x00,
|
||||
x: 0x00,
|
||||
y: 0x00,
|
||||
flags: Default::default(),
|
||||
pc: 0xfffd,
|
||||
s: 0x00,
|
||||
microcode_step: 0x00,
|
||||
address_bus: 0x00,
|
||||
data_bus: 0x00,
|
||||
ir: Instruction {
|
||||
op: Operation::NOP,
|
||||
mode: AddressMode::Implied,
|
||||
operand: Operand::None,
|
||||
},
|
||||
oi: INSTRUCTION_TABLE[ISA_OP_NOP as usize].clone().unwrap(),
|
||||
has_reset: false,
|
||||
iv: 0xfffe,
|
||||
cycle_carry: 0x0000,
|
||||
ir_bytes: [0x00; 4],
|
||||
read_signal: true,
|
||||
reset_vector: 0x0000,
|
||||
int_vector: 0x0000,
|
||||
nmi_vector: 0x0000,
|
||||
tick_stage: LoadingInstruction
|
||||
};
|
||||
working.reset_cpu();
|
||||
working
|
||||
}
|
||||
}
|
||||
|
||||
impl Mos6502Cpu {
|
||||
pub fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
pub fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
//
|
||||
// fn read_word(&self, offset: &u16) -> u16 {
|
||||
// println!("READING OFFSET 0x{offset:04x} and 0x{:04x}", offset + 1);
|
||||
// let low = self.memory[*offset as usize];
|
||||
// let high = self.memory[*offset as usize + 1];
|
||||
// println!("LOW = 0x{low:02x} HIGH = 0x{high:02x}");
|
||||
// let result = (high as u16) << 8 | low as u16;
|
||||
// // println!("MEMORY: {:?}", self.memory);
|
||||
// println!("READ {result:04x}");
|
||||
// result
|
||||
// }
|
||||
|
||||
pub fn peek_flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
||||
self.flags.flag(flag_to_read)
|
||||
}
|
||||
pub fn poke_flag(&mut self, flag_to_set: Mos6502Flag, new_value: bool) {
|
||||
if new_value {
|
||||
self.flags.set_flag(flag_to_set)
|
||||
} else {
|
||||
self.flags.clear_flag(flag_to_set)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&self, offset: u16) -> u8 {
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
|
||||
pub fn poke(&mut self, offset: u16, value: u8) {
|
||||
println!("Setting memory at {offset:04x} to {value:02x}");
|
||||
self.memory[offset as usize] = value
|
||||
}
|
||||
|
||||
pub fn peek_a(&self) -> u8 {
|
||||
println!("Readding register A => 0x{:02x}", self.a);
|
||||
self.a
|
||||
}
|
||||
pub fn poke_a(&mut self, new_a: u8) {
|
||||
println!("Updating register A from [{}] to [{}]", self.a, new_a);
|
||||
self.a = new_a;
|
||||
}
|
||||
|
||||
pub fn peek_x(&self) -> u8 {
|
||||
println!("Readding register X => 0x{}", self.x);
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn poke_x(&mut self, new_x: u8) {
|
||||
println!("Updating register X from [{}] to [{}]", self.x, new_x);
|
||||
self.x = new_x
|
||||
}
|
||||
|
||||
pub fn peek_y(&self) -> u8 {
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn poke_y(&mut self, new_y: u8) {
|
||||
self.y = new_y
|
||||
}
|
||||
|
||||
fn advance_pc(&mut self, how_far: u16) {
|
||||
self.pc += how_far;
|
||||
}
|
||||
|
||||
fn set_pc_to(&mut self, new_pc: u16) {
|
||||
self.pc = new_pc;
|
||||
}
|
||||
|
||||
/// Ticks the CPU
|
||||
pub fn tick(&mut self) {
|
||||
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
|
||||
match self.tick_stage {
|
||||
LoadingInstruction => {
|
||||
println!("Loading instruction from data bus -> {}", self.data_bus);
|
||||
|
||||
let instruction = INSTRUCTION_TABLE[self.data_bus as usize].clone();
|
||||
|
||||
if let Some(inst) = instruction {
|
||||
println!("DECODED INSTRUCTION [{:?}]/[{:?}]", inst.operation, inst.mode);
|
||||
match inst.mode {
|
||||
AddressMode::Absolute | AddressMode::AbsoluteX | AddressMode::AbsoluteY => {
|
||||
println!("NEED TO LOAD a 16bit VALUE FOR INSTRUCTION");
|
||||
self.tick_stage = Loading16BitParameter1;
|
||||
}
|
||||
AddressMode::Immediate => {
|
||||
println!("LOADING A 8BIT VALUE FOR INSTRUCTION");
|
||||
self.tick_stage = Loading8BitParameter;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
println!("INVALID DECODE OF [${:02x}", self.data_bus);
|
||||
}
|
||||
|
||||
}
|
||||
Loading8BitParameter => {
|
||||
println!("Loading parameter for 8bit ");
|
||||
},
|
||||
Loading16BitParameter1 => {
|
||||
println!("Loading high bits of parameter");
|
||||
},
|
||||
Loading16BitParameter2 => {
|
||||
println!("Loading low bits of parameter");
|
||||
},
|
||||
Stall(length) => {
|
||||
println!("PREPARING TO STALL FOR {} CYCLES", length);
|
||||
},
|
||||
Waiting => {
|
||||
println!("CPU IS WAITING.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if self.microcode_step == 0 {
|
||||
println!("OUT OF MICROSTEPS. Decoding the next instruction");
|
||||
let offset = self.pc as usize;
|
||||
// TODO: this calls opinfo 2x
|
||||
self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap();
|
||||
self.ir = Instruction::decode(&self.memory[offset..offset + 4]).unwrap();
|
||||
self.microcode_step = self.oi.cycles;
|
||||
println!("Decoded [[{:?}]]", self.ir);
|
||||
self.advance_pc(self.oi.length as u16);
|
||||
// load the microstep buffer with what steps to run
|
||||
// set the counter to the number of steps left
|
||||
} else {
|
||||
// run 1 microcode step
|
||||
println!(
|
||||
"Microstep {}/{} for {:?}",
|
||||
self.microcode_step, self.oi.cycles, self.ir.op
|
||||
);
|
||||
match self.ir.op {
|
||||
Operation::ADC => match self.microcode_step {
|
||||
1 => match self.ir.mode {
|
||||
AddressMode::Immediate => {}
|
||||
AddressMode::ZeroPage => {}
|
||||
AddressMode::ZeroPageX => {}
|
||||
AddressMode::Absolute => {}
|
||||
AddressMode::AbsoluteX => {}
|
||||
AddressMode::AbsoluteY => {}
|
||||
AddressMode::Indirect => {}
|
||||
AddressMode::IndirectX => {}
|
||||
AddressMode::IndirectY => {}
|
||||
_ => {}
|
||||
},
|
||||
2 => {}
|
||||
_ => {}
|
||||
},
|
||||
Operation::AND => {}
|
||||
Operation::ASL => {}
|
||||
Operation::BCC => {}
|
||||
Operation::BCS => {}
|
||||
Operation::BEQ => {}
|
||||
Operation::BIT => {}
|
||||
Operation::BMI => {}
|
||||
Operation::BNE => {}
|
||||
Operation::BPL => {}
|
||||
Operation::BRK => {}
|
||||
Operation::BVC => {}
|
||||
Operation::BVS => {}
|
||||
Operation::CLC => {
|
||||
self.flags.clear_flag(Carry);
|
||||
}
|
||||
Operation::CLD => {
|
||||
self.flags.clear_flag(Decimal);
|
||||
}
|
||||
Operation::CLI => {
|
||||
self.flags.clear_flag(Interrupt);
|
||||
}
|
||||
Operation::CLV => {
|
||||
self.flags.clear_flag(Overflow);
|
||||
}
|
||||
Operation::CMP => {}
|
||||
Operation::CPX => {}
|
||||
Operation::CPY => {}
|
||||
Operation::DEC => {
|
||||
match self.microcode_step {
|
||||
// DEC Step 1
|
||||
1 => {
|
||||
let working_value = match self.oi.mode {
|
||||
AddressMode::ZeroPage => {
|
||||
// read from
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Byte(z) => z,
|
||||
_ => 0x00,
|
||||
};
|
||||
trace!("READING FROM MEMORY AT 0x{offset:04x}");
|
||||
self.memory[offset as usize]
|
||||
// self.peek(offset);
|
||||
}
|
||||
AddressMode::ZeroPageX => {
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Byte(z) => z,
|
||||
_ => 0x00,
|
||||
};
|
||||
// self.memory.peek(offset + self.x);
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
AddressMode::Absolute => {
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Word(offset) => offset,
|
||||
_ => 0x00,
|
||||
};
|
||||
// self.memory.peek(offset)
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
AddressMode::AbsoluteX => {
|
||||
let offset = match self.ir.operand {
|
||||
Operand::Word(offset) => offset,
|
||||
_ => 0x00,
|
||||
};
|
||||
// self.memory.peek(offset + self.x);
|
||||
self.memory[offset as usize]
|
||||
}
|
||||
_ => 0x00,
|
||||
};
|
||||
}
|
||||
// DEC write memory
|
||||
2 => {
|
||||
self.a = self.cycle_carry as u8;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Operation::DEX => {
|
||||
if self.microcode_step == 1 {
|
||||
let (new_x, new_carry) = self.x.overflowing_sub(1);
|
||||
self.poke_x(new_x);
|
||||
self.poke_flag(Carry, new_carry);
|
||||
}
|
||||
}
|
||||
Operation::DEY => {
|
||||
if self.microcode_step == 1 {
|
||||
(self.y, _) = self.y.overflowing_sub(1);
|
||||
}
|
||||
}
|
||||
Operation::EOR => {}
|
||||
Operation::INC => {}
|
||||
Operation::INX => {
|
||||
if self.microcode_step == 1 {
|
||||
let (new_x, new_carry) = self.x.overflowing_add(1);
|
||||
self.poke_x(new_x);
|
||||
self.poke_flag(Carry, new_carry);
|
||||
self.address_bus = self.pc;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
}
|
||||
Operation::INY => {
|
||||
if self.microcode_step == 1 {
|
||||
let (new_y, new_carry) = self.y.overflowing_add(1);
|
||||
self.poke_y(new_y);
|
||||
self.poke_flag(Carry, new_carry);
|
||||
self.address_bus = self.pc;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
}
|
||||
Operation::JMP => match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.pc = offset;
|
||||
self.address_bus = self.pc;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Operation::JSR => {
|
||||
// push pc to stack.
|
||||
// jump to the subroutine.
|
||||
}
|
||||
Operation::LDA => match self.oi.mode {
|
||||
AddressMode::Immediate => match self.ir.operand {
|
||||
Operand::Byte(value) => {
|
||||
println!("Loading 0x{value:02x} ({value}) into A");
|
||||
self.a = value;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AddressMode::ZeroPage => match self.ir.operand {
|
||||
Operand::Byte(value) => {
|
||||
println!("Loading from zero page at 0x{value:02x} ({value})");
|
||||
self.a = self.memory[value as usize];
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AddressMode::ZeroPageX => match self.ir.operand {
|
||||
Operand::Byte(value) => {
|
||||
let x_offset = self.x;
|
||||
self.a = self.memory[(value + x_offset) as usize];
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AddressMode::Absolute => {
|
||||
if let Operand::Word(offset) = self.ir.operand {
|
||||
println!("Loading from absolute address 0x{offset:04x}");
|
||||
self.a = self.memory[offset as usize];
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteX => {
|
||||
if let Operand::Word(offset) = self.ir.operand {
|
||||
self.a = self.memory[(offset + self.x as u16) as usize];
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteY => {
|
||||
if let Operand::Word(offset) = self.ir.operand {
|
||||
let real_offset = offset + self.y as u16;
|
||||
println!("offset: {offset:04x} + {:02x}", self.y);
|
||||
self.a = self.memory[(offset + self.y as u16) as usize];
|
||||
}
|
||||
}
|
||||
AddressMode::Indirect => {}
|
||||
AddressMode::IndirectX => {}
|
||||
AddressMode::IndirectY => {}
|
||||
_ => {
|
||||
println!("INVALID ADDRESS MODE FOR LDA");
|
||||
}
|
||||
},
|
||||
Operation::LDX => {}
|
||||
Operation::LDY => {}
|
||||
Operation::LSR => {}
|
||||
Operation::NOP => {
|
||||
// do nothing.
|
||||
}
|
||||
Operation::ORA => {}
|
||||
Operation::PHA => {}
|
||||
Operation::PHP => {}
|
||||
Operation::PLA => {}
|
||||
Operation::PLP => {}
|
||||
Operation::ROL => {
|
||||
if self.microcode_step == 1 {
|
||||
self.a = self.a.rotate_left(1);
|
||||
}
|
||||
}
|
||||
Operation::ROR => {
|
||||
// rotate A
|
||||
if self.microcode_step == 1 {
|
||||
self.a = self.a.rotate_right(1);
|
||||
}
|
||||
}
|
||||
Operation::RTI => {}
|
||||
Operation::RTS => {}
|
||||
Operation::SBC => {}
|
||||
Operation::SEC => {
|
||||
self.flags.set_flag(Carry);
|
||||
}
|
||||
Operation::SED => {
|
||||
self.flags.set_flag(Decimal);
|
||||
}
|
||||
Operation::SEI => {
|
||||
self.flags.set_flag(Interrupt);
|
||||
}
|
||||
Operation::STA => {
|
||||
match self.oi.mode {
|
||||
AddressMode::ZeroPage => {
|
||||
// write to the zero page.
|
||||
match self.ir.operand {
|
||||
Operand::Byte(target) => {
|
||||
self.memory[target as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::ZeroPageX => {
|
||||
match self.ir.operand {
|
||||
Operand::Byte(target) => {
|
||||
let x = self.x;
|
||||
self.memory[(x + target) as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::Absolute => {
|
||||
// write from A to the specified memory location
|
||||
match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.memory[offset as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteX => {
|
||||
match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.memory[(offset + self.x as u16) as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::AbsoluteY => {
|
||||
match self.ir.operand {
|
||||
Operand::Word(offset) => {
|
||||
self.memory[(offset + self.y as u16) as usize] = self.a;
|
||||
}
|
||||
_ => {
|
||||
// Invalid Parameter
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressMode::IndirectX => {}
|
||||
AddressMode::IndirectY => {}
|
||||
_ => {
|
||||
// invalid memory mode
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::STX => {}
|
||||
Operation::STY => {}
|
||||
Operation::TAX => {
|
||||
self.x = self.a;
|
||||
}
|
||||
Operation::TAY => {
|
||||
self.y = self.a;
|
||||
}
|
||||
Operation::TSX => {}
|
||||
Operation::TXA => {
|
||||
self.a = self.x;
|
||||
}
|
||||
Operation::TXS => {}
|
||||
Operation::TYA => {
|
||||
self.y = self.a;
|
||||
}
|
||||
}
|
||||
self.microcode_step -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::constants::constants_isa_op::*;
|
||||
use crate::instruction_table::{instruction_cycles, INSTRUCTION_TABLE};
|
||||
|
||||
#[test]
|
||||
fn clc() {
|
||||
// setup the CPU for our test
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
|
||||
// tick through the reset cycle
|
||||
while !cpu.has_reset {
|
||||
cpu.tick();
|
||||
}
|
||||
println!("DONE RESET TICKS");
|
||||
cpu.flags.set_flag(Carry);
|
||||
// Load our 'test program'
|
||||
cpu.memory[0x6000] = ISA_OP_CLC;
|
||||
// Start the PC at our program
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
// Tick the CPU through the instruction
|
||||
for _ in 0..instruction_cycles(ISA_OP_CLC) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Carry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cld() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Decimal);
|
||||
cpu.memory[0x6000] = ISA_OP_CLD;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..instruction_cycles(ISA_OP_CLD) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Decimal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Interrupt);
|
||||
cpu.memory[0x6000] = ISA_OP_CLI;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_CLI) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Interrupt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clv() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.flags.set_flag(Overflow);
|
||||
cpu.memory[0x6000] = ISA_OP_CLV;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_CLV) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert!(!cpu.peek_flag(Overflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_immediate() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_I;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_LDA_I) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_zx() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.poke_x(1);
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ZX;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
cpu.memory[0x00ac] = 0xbe;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_LDA_ZX) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
// println!("MEMORY AT 0x00aa, ab, ac, ad, ae -> {:02x} {:02x} {:02x} {:02x} {:02x}", cpu.memory[0x00aa], cpu.memory[0x00ab], cpu.memory[0x00ac], cpu.memory[0x00ad], cpu.memory[0x00ae]);
|
||||
// cpu.dump();
|
||||
assert_eq!(cpu.peek_a(), 0xbe);
|
||||
assert!(!cpu.peek_flag(Zero));
|
||||
assert!(!cpu.peek_flag(Carry));
|
||||
assert!(!cpu.peek_flag(Negative));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_zeropage() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_Z;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
// Load ZeroPage
|
||||
cpu.memory[0x00ab] = 0xbe;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..instruction_cycles(ISA_OP_LDA_Z) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xbe);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_absolute() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ABS;
|
||||
cpu.memory[0x6001] = 0xef;
|
||||
cpu.memory[0x6002] = 0x0e;
|
||||
cpu.memory[0x0eef] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_LDA_ABS) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_absolutex() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ABSX;
|
||||
cpu.memory[0x6001] = 0xef;
|
||||
cpu.memory[0x6002] = 0x0e;
|
||||
cpu.poke_x(0x01);
|
||||
cpu.memory[0x0ef0] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_LDA_ABSX) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lda_absolutey() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x6000] = ISA_OP_LDA_ABSY;
|
||||
cpu.memory[0x6001] = 0xef;
|
||||
cpu.memory[0x6002] = 0x0e;
|
||||
cpu.poke_y(0x01);
|
||||
cpu.memory[0x0ef0] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_LDA_ABSY) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.a, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dex_inx() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.x = 0xab;
|
||||
cpu.memory[0x6000] = ISA_OP_DEX;
|
||||
cpu.memory[0x6001] = ISA_OP_INX;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_DEX) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xaa, cpu.x);
|
||||
for _ in 0..=instruction_cycles(ISA_OP_INX) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xab, cpu.x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dey_iny() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.poke_y(0xab);
|
||||
cpu.memory[0x6000] = ISA_OP_DEY;
|
||||
cpu.memory[0x6001] = ISA_OP_INY;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_DEY) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xaa, cpu.peek_y());
|
||||
for _ in 0..=instruction_cycles(ISA_OP_INY) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(0xab, cpu.peek_y());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rol_a_ror_a() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.poke_a(0b1010_1010); // 0xaa
|
||||
cpu.memory[0x6000] = ISA_OP_ROL_A;
|
||||
cpu.memory[0x6001] = ISA_OP_ROR_A;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_ROL_A) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(cpu.peek_a(), 0b0101_0101);
|
||||
for _ in 0..=instruction_cycles(ISA_OP_ROR_A) {
|
||||
cpu.tick();
|
||||
}
|
||||
assert_eq!(cpu.peek_a(), 0b1010_1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rol_zp_ror_zp() {
|
||||
let mut cpu = Mos6502Cpu::default();
|
||||
cpu.memory[0x00ab] = 0b0101_0101;
|
||||
cpu.memory[0x6000] = ISA_OP_ROL_ZP;
|
||||
cpu.memory[0x6001] = 0xab;
|
||||
cpu.pc = 0x6000;
|
||||
|
||||
for _ in 0..=instruction_cycles(ISA_OP_ROL_ZP) {
|
||||
cpu.tick();
|
||||
}
|
||||
|
||||
assert_eq!(cpu.memory[0xab], 0b1010_1010);;
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,39 @@
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
|
||||
impl Mos6502Cpu {
|
||||
/// dump_data
|
||||
///
|
||||
/// returns
|
||||
/// PC, A, X, Y, Address_Bus, Data_Bus, Microcode_Step
|
||||
pub fn dump_data(&self) -> (u16, u8, u8, u8, u16, u8, u8, u16, u16, u16) {
|
||||
(
|
||||
self.pc,
|
||||
self.a,
|
||||
self.x,
|
||||
self.y,
|
||||
self.address_bus,
|
||||
self.data_bus,
|
||||
self.microcode_step,
|
||||
self.reset_vector,
|
||||
self.int_vector,
|
||||
self.nmi_vector
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dump(&self) {
|
||||
println!(
|
||||
"CPU State: PC: ${:04x} / A: ${:02x} / X: ${:02x} / Y: ${:02x} / ADDRESS: ${:04x} / DATA: ${:02x} / MICROSTEPS: {:02} / S: {} / NMI: ${:04x} / RST: ${:04x} / INT: ${:04x}",
|
||||
self.pc,
|
||||
self.a,
|
||||
self.x,
|
||||
self.y,
|
||||
self.address_bus,
|
||||
self.data_bus,
|
||||
self.microcode_step,
|
||||
self.flags.dump(),
|
||||
self.nmi_vector,
|
||||
self.reset_vector,
|
||||
self.int_vector
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod cpu;
|
||||
pub mod new;
|
||||
|
||||
pub mod tick2;
|
||||
pub mod dbg;
|
||||
pub mod tick_stages;
|
||||
@@ -0,0 +1,24 @@
|
||||
use crate::constants::constants_system::{OFFSET_RESET_VECTOR, SIZE_64KB};
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
|
||||
impl Mos6502Cpu {
|
||||
pub fn new() -> Mos6502Cpu {
|
||||
let array = [0x00u8; SIZE_64KB];
|
||||
let mut working = Mos6502Cpu {
|
||||
memory: array,
|
||||
ir_bytes: [0x00; 4],
|
||||
..Default::default()
|
||||
};
|
||||
working.reset_cpu();
|
||||
working
|
||||
}
|
||||
|
||||
pub(crate) fn reset_cpu(&mut self) {
|
||||
self.microcode_step = 7 + 6;
|
||||
// self = &mut Mos6502Cpu::default();
|
||||
println!("Should tick 7 times, then 6 cycles to read the reset and int vectors.");
|
||||
// read the value at 0xfffa 0xfffb for our NMI vector.
|
||||
// read the value at 0xfffc 0xfffd for our reset vector.
|
||||
// read the value at 0xfffe 0xffff for our int vector
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR};
|
||||
use crate::mos6502cpu::cpu::Mos6502Cpu;
|
||||
|
||||
|
||||
|
||||
impl Mos6502Cpu {
|
||||
/// AccurateTick
|
||||
///
|
||||
/// In: address_bus > Address of data operationm
|
||||
/// data_bus > Data read or written
|
||||
/// State:
|
||||
/// read_bus > Flag for if cpu is reading or writing the data bus
|
||||
/// cycle_step > Index for what step of the Decode->Load->Execute cycle we are in
|
||||
/// Out: address_bus > address for operation
|
||||
/// data_bus > data for the operation
|
||||
/// read_bus > lets rest of the computer know if the CPU is reading from the address
|
||||
/// provided or if we are writing to the address
|
||||
pub fn tick2(&mut self, address_bus: u16, data_bus: u8) -> (u16, u8, bool) {
|
||||
if self.has_reset {
|
||||
// we have completed the reset cycle
|
||||
if self.read_signal {
|
||||
// we should see new data in the data_bus for us
|
||||
let read_data = data_bus;
|
||||
println!("READ 0x{read_data:02x} from data bus.");
|
||||
self.data_bus = read_data;
|
||||
} else {
|
||||
// we are writing to the bus.
|
||||
}
|
||||
} else {
|
||||
println!("Reset microstep {}", self.microcode_step);
|
||||
// we need to do the reset steps
|
||||
// reduce the number of remaining microsteps
|
||||
self.read_signal = true;
|
||||
match self.microcode_step {
|
||||
6 => {
|
||||
// NMI High byte
|
||||
}
|
||||
5 => {
|
||||
// NMI low byte
|
||||
}
|
||||
4 => {
|
||||
// read first byte of reset vector
|
||||
self.address_bus = OFFSET_RESET_VECTOR;
|
||||
}
|
||||
3 => {
|
||||
// at this point data holds the upper byte of our reset vector
|
||||
self.reset_vector = (data_bus as u16) << 8;
|
||||
println!("Loaded reset vector of 0x{:04x}", self.reset_vector);
|
||||
// read secondd byte of reset vector
|
||||
self.address_bus = OFFSET_RESET_VECTOR + 1;
|
||||
}
|
||||
2 => {
|
||||
self.reset_vector |= data_bus as u16;
|
||||
println!("Loaded reset vector of 0x{:04x}", self.reset_vector);
|
||||
// read first byte of interrupt vector
|
||||
self.address_bus = OFFSET_INT_VECTOR;
|
||||
}
|
||||
1 => {
|
||||
// read second byte of interrupt vector
|
||||
self.address_bus = OFFSET_INT_VECTOR + 1;
|
||||
}
|
||||
0 => {
|
||||
self.int_vector |= data_bus as u16;
|
||||
println!("Loaded interrupt vector of 0x{:04x}", self.int_vector);
|
||||
self.pc = self.reset_vector;
|
||||
println!("Set PC to Reset Vector. Giddy-up!");
|
||||
println!("START HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK");
|
||||
// the KIM-1 uses 0x0000 for its initial PC
|
||||
self.pc = 0x0000;
|
||||
println!("END HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK");
|
||||
self.has_reset = true;
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
}
|
||||
if self.microcode_step > 0 {
|
||||
self.microcode_step -= 1;
|
||||
}
|
||||
}
|
||||
(self.address_bus, self.data_bus, self.read_signal)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
/// Mos6502TickStates
|
||||
///
|
||||
/// The set of what a tick can be doing
|
||||
///
|
||||
pub enum Mos6502TickStates {
|
||||
/// Loading the first byte into the IR
|
||||
LoadingInstruction,
|
||||
/// Loading an 8 bit parameter
|
||||
Loading8BitParameter,
|
||||
/// Loading the MSB 8 bits
|
||||
Loading16BitParameter1,
|
||||
/// Loading the LSB 8 bits
|
||||
Loading16BitParameter2,
|
||||
/// Stalling for accurate emulation
|
||||
Stall(u8),
|
||||
/// Waiting for the next instruction
|
||||
Waiting
|
||||
}
|
||||
+188
-47
@@ -1,15 +1,72 @@
|
||||
use crate::mos6502flags::Mos6502Flag::{
|
||||
Break, Carry, Decimal, Interrupt, Negative, Overflow, Zero,
|
||||
};
|
||||
|
||||
pub const BIT_NEGATIVE: u8 = 7;
|
||||
pub const BIT_OVERFLOW: u8 = 6;
|
||||
pub const BIT_BREAK: u8 = 4;
|
||||
pub const BIT_DECIMAL: u8 = 3;
|
||||
pub const BIT_INTERRUPT: u8 = 2;
|
||||
pub const BIT_ZERO: u8 = 1;
|
||||
pub const BIT_CARRY: u8 = 0;
|
||||
/// Represents the status flags in the 6502 processor's status register (P).
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum Mos6502Flag {
|
||||
/// Carry Flag (C)
|
||||
///
|
||||
/// Set if an arithmetic operation results in a carry out of the most significant bit (for addition),
|
||||
/// or a borrow (for subtraction). Also used for bit shifts and rotates.
|
||||
Carry,
|
||||
|
||||
/// Zero Flag (Z)
|
||||
///
|
||||
/// Set if the result of an operation is zero.
|
||||
Zero,
|
||||
|
||||
/// Interrupt Disable Flag (I)
|
||||
///
|
||||
/// When set, disables maskable interrupts (IRQ).
|
||||
Interrupt,
|
||||
|
||||
/// Decimal Mode Flag (D)
|
||||
///
|
||||
/// When set, arithmetic operations use Binary-Coded Decimal (BCD) mode.
|
||||
/// Note: Not supported on all 6502 variants (e.g., not on the NES CPU).
|
||||
Decimal,
|
||||
|
||||
/// Break Command Flag (B)
|
||||
///
|
||||
/// Set when a BRK (break) instruction is executed.
|
||||
/// Used to distinguish software interrupts from hardware ones.
|
||||
Break,
|
||||
|
||||
/// Overflow Flag (V)
|
||||
///
|
||||
/// Set when an arithmetic operation results in a signed overflow.
|
||||
/// For example, adding two positive numbers results in a negative.
|
||||
Overflow,
|
||||
Negative
|
||||
|
||||
/// Negative Flag (N)
|
||||
///
|
||||
/// Set if the result of an operation has bit 7 set (i.e., the result is negative in two's complement).
|
||||
Negative,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
impl Mos6502Flag {
|
||||
pub fn index(&self) -> u8 {
|
||||
match self {
|
||||
Carry => BIT_CARRY,
|
||||
Zero => BIT_ZERO,
|
||||
Interrupt => BIT_INTERRUPT,
|
||||
Decimal => BIT_DECIMAL,
|
||||
Break => BIT_BREAK,
|
||||
Overflow => BIT_OVERFLOW,
|
||||
Negative => BIT_NEGATIVE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug)]
|
||||
pub struct Mos6502Flags {
|
||||
carry: bool,
|
||||
zero: bool,
|
||||
@@ -21,66 +78,150 @@ pub struct Mos6502Flags {
|
||||
}
|
||||
|
||||
impl Mos6502Flags {
|
||||
pub fn dump(&self) -> String {
|
||||
format!(
|
||||
"{}{}{}{}{}{}{}",
|
||||
if self.carry { 'C' } else { 'c' },
|
||||
if self.zero { 'Z' } else { 'z' },
|
||||
if self.interrupt { 'I' } else { 'i' },
|
||||
if self.decimal { 'D' } else { 'd' },
|
||||
if self.break_flag { 'B' } else { 'b' },
|
||||
if self.overflow { 'O' } else { 'o' },
|
||||
if self.negative { 'N' } else { 'n' }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mos6502Flags {
|
||||
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {
|
||||
self.change_flag(flag_to_set, true);
|
||||
println!("Setting {flag_to_set:?} flag");
|
||||
match flag_to_set {
|
||||
Carry => self.carry = true,
|
||||
Zero => self.zero = true,
|
||||
Interrupt => self.interrupt = true,
|
||||
Decimal => self.decimal = true,
|
||||
Break => self.break_flag = true,
|
||||
Overflow => self.overflow = true,
|
||||
Negative => self.negative = true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_flag(&mut self, flag_to_clear: Mos6502Flag) {
|
||||
self.change_flag(flag_to_clear, false);
|
||||
println!("Clearing {flag_to_clear:?} flag");
|
||||
match flag_to_clear {
|
||||
Carry => self.carry = false,
|
||||
Zero => self.zero = false,
|
||||
Interrupt => self.interrupt = false,
|
||||
Decimal => self.decimal = false,
|
||||
Break => self.break_flag = false,
|
||||
Overflow => self.overflow = false,
|
||||
Negative => self.negative = false,
|
||||
}
|
||||
}
|
||||
|
||||
fn change_flag(&mut self, flag_to_change: Mos6502Flag, new_value: bool) {
|
||||
match flag_to_change {
|
||||
Mos6502Flag::Carry => {
|
||||
self.carry = new_value
|
||||
}
|
||||
Mos6502Flag::Zero => {
|
||||
self.zero = new_value
|
||||
}
|
||||
Mos6502Flag::Interrupt => {
|
||||
self.interrupt = new_value
|
||||
}
|
||||
Mos6502Flag::Decimal => {
|
||||
self.decimal = new_value
|
||||
}
|
||||
Mos6502Flag::Break => {
|
||||
self.break_flag = new_value
|
||||
}
|
||||
Mos6502Flag::Overflow => {
|
||||
self.overflow = new_value
|
||||
}
|
||||
Mos6502Flag::Negative => {
|
||||
self.negative = new_value
|
||||
}
|
||||
if new_value {
|
||||
self.set_flag(flag_to_change);
|
||||
} else {
|
||||
self.clear_flag(flag_to_change);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
||||
match flag_to_read {
|
||||
Mos6502Flag::Carry => {
|
||||
self.carry
|
||||
}
|
||||
Mos6502Flag::Zero => {
|
||||
self.zero
|
||||
}
|
||||
Mos6502Flag::Interrupt => {
|
||||
self.interrupt
|
||||
}
|
||||
Mos6502Flag::Decimal => {
|
||||
self.decimal
|
||||
}
|
||||
Mos6502Flag::Break => {
|
||||
self.break_flag
|
||||
}
|
||||
Mos6502Flag::Overflow => {
|
||||
self.overflow
|
||||
}
|
||||
Mos6502Flag::Negative => {
|
||||
self.negative
|
||||
}
|
||||
Mos6502Flag::Negative => self.negative,
|
||||
Mos6502Flag::Overflow => self.overflow,
|
||||
// 5
|
||||
Mos6502Flag::Break => self.break_flag,
|
||||
Mos6502Flag::Decimal => self.decimal,
|
||||
Mos6502Flag::Interrupt => self.interrupt,
|
||||
Mos6502Flag::Zero => self.zero,
|
||||
Mos6502Flag::Carry => self.carry,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_byte(&self) -> u8 {
|
||||
let mut working = 0x00;
|
||||
|
||||
if self.flag(Negative) {
|
||||
working += 1 << Negative.index();
|
||||
}
|
||||
if self.flag(Overflow) {
|
||||
working += 1 << Overflow.index();
|
||||
}
|
||||
working += 1 << 5; // Always Set
|
||||
if self.flag(Break) {
|
||||
working += 1 << Break.index();
|
||||
}
|
||||
if self.flag(Decimal) {
|
||||
working += 1 << Decimal.index();
|
||||
}
|
||||
if self.flag(Interrupt) {
|
||||
working += 1 << Interrupt.index();
|
||||
}
|
||||
if self.flag(Zero) {
|
||||
working += 1 << Zero.index();
|
||||
}
|
||||
if self.flag(Carry) {
|
||||
working += 1 << Carry.index();
|
||||
}
|
||||
|
||||
working
|
||||
}
|
||||
|
||||
pub fn from_byte(src: u8) -> Self {
|
||||
let mut working = Self::default();
|
||||
|
||||
working.change_flag(Negative, Self::bit(src, Negative.index()));
|
||||
working.change_flag(Overflow, Self::bit(src, Overflow.index()));
|
||||
working.change_flag(Break, Self::bit(src, Break.index()));
|
||||
working.change_flag(Decimal, Self::bit(src, Decimal.index()));
|
||||
working.change_flag(Interrupt, Self::bit(src, Interrupt.index()));
|
||||
working.change_flag(Zero, Self::bit(src, Zero.index()));
|
||||
working.change_flag(Carry, Self::bit(src, Carry.index()));
|
||||
|
||||
working
|
||||
}
|
||||
|
||||
/// bit
|
||||
///
|
||||
/// src -> Source byte to check in
|
||||
/// pos -> Which bit to check
|
||||
///
|
||||
/// returns bool
|
||||
///
|
||||
/// True if the bit is set.
|
||||
/// False if the bit is not set
|
||||
#[inline]
|
||||
fn bit(src: u8, pos: u8) -> bool {
|
||||
(src >> pos) & 1 != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity() {
|
||||
let f = Mos6502Flags::default();
|
||||
let magic_byte = 0b1110_1101;
|
||||
let magic_flags = Mos6502Flags {
|
||||
carry: true,
|
||||
zero: false,
|
||||
interrupt: true,
|
||||
decimal: true,
|
||||
break_flag: false,
|
||||
overflow: true,
|
||||
negative: true,
|
||||
};
|
||||
|
||||
assert_eq!(magic_flags, Mos6502Flags::from_byte(magic_byte));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use crate::address_mode::AddressMode;
|
||||
use crate::operation::Operation;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpInfo {
|
||||
/// What is the operation
|
||||
pub operation: Operation,
|
||||
/// How does this operation access memory
|
||||
pub mode: AddressMode,
|
||||
/// Bytes to represent the instruction and parameters
|
||||
pub length: u8,
|
||||
/// CPU Cycles to complete the instruction
|
||||
pub cycles: u8,
|
||||
/// Format string for disassembly
|
||||
pub format_prefix: &'static str,
|
||||
pub format_postfix: &'static str
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operand {
|
||||
None,
|
||||
Byte(u8),
|
||||
Word(u16),
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/// Represents all official 6502 CPU instructions.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Operation {
|
||||
/// Add with Carry
|
||||
///
|
||||
/// Affects flags: N, V, Z, C
|
||||
///
|
||||
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
|
||||
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
|
||||
ADC,
|
||||
|
||||
/// Logical AND with Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
|
||||
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
|
||||
AND,
|
||||
|
||||
/// Arithmetic Shift Left
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Accumulator (1/2), ZeroPage (2/5), ZeroPageX (2/6), Absolute (3/6),
|
||||
/// AbsoluteX (3/7)
|
||||
ASL,
|
||||
|
||||
/// Branch if Carry Clear
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BCC,
|
||||
|
||||
/// Branch if Carry Set
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BCS,
|
||||
|
||||
/// Branch if Equal (Zero Set)
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BEQ,
|
||||
|
||||
/// Bit Test
|
||||
///
|
||||
/// Affects flags: N, V, Z
|
||||
///
|
||||
/// Addressing Modes: ZeroPage (2/3), Absolute (3/4)
|
||||
BIT,
|
||||
|
||||
/// Branch if Minus (Negative Set)
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BMI,
|
||||
|
||||
/// Branch if Not Equal (Zero Clear)
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BNE,
|
||||
|
||||
/// Branch if Positive (Negative Clear)
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BPL,
|
||||
|
||||
/// Force Interrupt
|
||||
///
|
||||
/// Affects flags: B
|
||||
///
|
||||
/// Addressing Modes: Implied (1/7)
|
||||
BRK,
|
||||
|
||||
/// Branch if Overflow Clear
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BVC,
|
||||
|
||||
/// Branch if Overflow Set
|
||||
///
|
||||
/// Addressing Modes: Relative (2/2)
|
||||
BVS,
|
||||
|
||||
/// Clear Carry Flag
|
||||
///
|
||||
/// Affects flags: C
|
||||
///
|
||||
/// Addressing Modes: Implied (1/2)
|
||||
CLC,
|
||||
|
||||
/// Clear Decimal Mode
|
||||
///
|
||||
/// Affects flags: D
|
||||
///
|
||||
/// Addressing Modes: Implied (1/2)
|
||||
CLD,
|
||||
|
||||
/// Clear Interrupt Disable
|
||||
///
|
||||
/// Affects flags: I
|
||||
///
|
||||
/// Addressing Modes: Implied (1/2)
|
||||
CLI,
|
||||
|
||||
/// Clear Overflow Flag
|
||||
///
|
||||
/// Affects flags: V
|
||||
///
|
||||
/// Addressing Modes: Implied (2/2)
|
||||
CLV,
|
||||
|
||||
/// Compare Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
|
||||
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
|
||||
CMP,
|
||||
|
||||
/// Compare X Register
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), Absolute (3/4)
|
||||
CPX,
|
||||
|
||||
/// Compare Y Register
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, Absolute
|
||||
CPY,
|
||||
|
||||
/// Decrement Memory
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
||||
DEC,
|
||||
|
||||
/// Decrement X Register
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
DEX,
|
||||
|
||||
/// Decrement Y Register
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
DEY,
|
||||
|
||||
/// Exclusive OR with Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
||||
EOR,
|
||||
|
||||
/// Increment Memory
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
||||
INC,
|
||||
|
||||
/// Increment X Register
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
INX,
|
||||
|
||||
/// Increment Y Register
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
INY,
|
||||
|
||||
/// Jump to Address
|
||||
///
|
||||
/// Addressing Modes: Absolute, Indirect
|
||||
JMP,
|
||||
|
||||
/// Jump to Subroutine
|
||||
///
|
||||
/// Addressing Modes: Absolute
|
||||
JSR,
|
||||
|
||||
/// Load Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
||||
LDA,
|
||||
|
||||
/// Load X Register
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageY, Absolute, AbsoluteY
|
||||
LDX,
|
||||
|
||||
/// Load Y Register
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
||||
LDY,
|
||||
|
||||
/// Logical Shift Right
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
||||
LSR,
|
||||
|
||||
/// No Operation
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
NOP,
|
||||
|
||||
/// Logical Inclusive OR with Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
||||
ORA,
|
||||
|
||||
/// Push Accumulator on Stack
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
PHA,
|
||||
|
||||
/// Push Processor Status on Stack
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
PHP,
|
||||
|
||||
/// Pull Accumulator from Stack
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
PLA,
|
||||
|
||||
/// Pull Processor Status from Stack
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
PLP,
|
||||
|
||||
/// Rotate Left
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
||||
ROL,
|
||||
|
||||
/// Rotate Right
|
||||
///
|
||||
/// Affects flags: N, Z, C
|
||||
///
|
||||
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
||||
ROR,
|
||||
|
||||
/// Return from Interrupt
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
RTI,
|
||||
|
||||
/// Return from Subroutine
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
RTS,
|
||||
|
||||
/// Subtract with Carry
|
||||
///
|
||||
/// Affects flags: N, V, Z, C
|
||||
///
|
||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
||||
SBC,
|
||||
|
||||
/// Set Carry Flag
|
||||
///
|
||||
/// Affects flags: C
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
SEC,
|
||||
|
||||
/// Set Decimal Flag
|
||||
///
|
||||
/// Affects flags: D
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
SED,
|
||||
|
||||
/// Set Interrupt Disable
|
||||
///
|
||||
/// Affects flags: I
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
SEI,
|
||||
|
||||
/// Store Accumulator
|
||||
///
|
||||
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
||||
STA,
|
||||
|
||||
/// Store X Register
|
||||
///
|
||||
/// Addressing Modes: ZeroPage, ZeroPageY, Absolute
|
||||
STX,
|
||||
|
||||
/// Store Y Register
|
||||
///
|
||||
/// Addressing Modes: ZeroPage, ZeroPageX, Absolute
|
||||
STY,
|
||||
|
||||
/// Transfer Accumulator to X
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
TAX,
|
||||
|
||||
/// Transfer Accumulator to Y
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
TAY,
|
||||
|
||||
/// Transfer Stack Pointer to X
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
TSX,
|
||||
|
||||
/// Transfer X to Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
TXA,
|
||||
|
||||
/// Transfer X to Stack Pointer
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
TXS,
|
||||
|
||||
/// Transfer Y to Accumulator
|
||||
///
|
||||
/// Affects flags: N, Z
|
||||
///
|
||||
/// Addressing Modes: Implied
|
||||
TYA,
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::constants::constants_test::*;
|
||||
|
||||
impl At28C256 {
|
||||
/// checksum
|
||||
///
|
||||
/// calculates and returns the checksum for the loaded binary.
|
||||
/// files with all zero will calculate to zero
|
||||
pub fn checksum(&self) -> u8 {
|
||||
At28C256::checksum_static(&self.data[..])
|
||||
}
|
||||
|
||||
pub fn checksum_static(data: &[u8]) -> u8 {
|
||||
data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn programmed_data_reads_back_same() {
|
||||
let mut data = At28C256::default();
|
||||
for i in 0..SIZE_32KB {
|
||||
data.data[i] = 0xeau8;
|
||||
}
|
||||
for offset in 0..SIZE_32KB {
|
||||
if offset.is_multiple_of(SIZE_1KB) {};
|
||||
assert_eq!(0xea, data.read(&(offset as u16)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_correctly_for_zero() {
|
||||
let data1 = [0x00u8; SIZE_32KB];
|
||||
assert_eq!(0x00, At28C256::checksum_static(&data1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_1_byte() {
|
||||
let data = [0xff; 1];
|
||||
assert_eq!(0xff, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_2_bytes() {
|
||||
let data = [0xff; 2];
|
||||
// 0xff + 0xff = 0x1fe
|
||||
assert_eq!(0xfe, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_first_80_bytes() {
|
||||
println!("STARTING TEST");
|
||||
let mut checksum = 0x00;
|
||||
|
||||
let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin");
|
||||
println!("READING [{path}]");
|
||||
let data = fs::read(path);
|
||||
match data {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes", bytes.len());
|
||||
checksum = At28C256::checksum_static(&bytes);
|
||||
println!("Checksum: 0x{:02x}", checksum);
|
||||
}
|
||||
Err(e) => eprintln!("Failed to read file: {}", e),
|
||||
}
|
||||
assert_eq!(0x58, checksum);
|
||||
println!("TEST COMPLETE");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Default for At28C256 {
|
||||
fn default() -> Self {
|
||||
let vec = vec![0xea; SIZE_32KB];
|
||||
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
|
||||
let boxed_array: Box<[u8; SIZE_32KB]> = boxed_slice
|
||||
.try_into()
|
||||
.expect("Failed to convert Vec to boxed array");
|
||||
At28C256 {
|
||||
data: boxed_array,
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00,
|
||||
offset: 0x0000,
|
||||
max_offset: 0x3fff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::periph::at28c256::At28C256;
|
||||
|
||||
pub struct At28C256State {
|
||||
offset: u16
|
||||
}
|
||||
|
||||
impl At28C256 {
|
||||
pub fn dump(&self) -> At28C256State {
|
||||
At28C256State {
|
||||
offset: self.offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
pub mod default;
|
||||
pub mod rom_chip;
|
||||
pub mod tick;
|
||||
mod new;
|
||||
mod program;
|
||||
mod dump;
|
||||
mod checksum;
|
||||
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use std::io::Read;
|
||||
|
||||
/// At28C256
|
||||
///
|
||||
/// Represents a single At28C256 EEPROM Chip
|
||||
///
|
||||
/// 256kbit storage
|
||||
/// 32kbyte storage
|
||||
pub struct At28C256 {
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
data: Box<[u8]>,
|
||||
// where in the computer memory map do we live?
|
||||
offset: u16,
|
||||
max_offset: u16
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
|
||||
impl At28C256 {
|
||||
pub fn new(offset: u16, max_offset: u16, data: Vec<u8>) -> Self {
|
||||
println!("NEW At28C256 with checksum ${:02x}", At28C256::checksum_static(&data[..]));
|
||||
|
||||
At28C256 {
|
||||
data: data.into_boxed_slice(),
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00,
|
||||
offset,
|
||||
max_offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
|
||||
impl At28C256 {
|
||||
pub fn program(&mut self, new_program: Box<[u8]>) {
|
||||
// panic!("FAIL. Cant program the chip.");
|
||||
// println!("PROGRAMMING {:?}", new_program);
|
||||
self.data = new_program;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true) }
|
||||
|
||||
#[test]
|
||||
fn programming_chip_changes_contents() {
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, vec![]);
|
||||
|
||||
assert_eq!(0x00, chip.read(&0x0000));
|
||||
|
||||
let new_data: Vec<u8> = vec![0xff, 0xff, 0xff, 0xff];
|
||||
chip.program(new_data.into());
|
||||
assert_eq!(0xff, chip.read(&0x0000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
impl RomChip for At28C256 {
|
||||
/// read
|
||||
///
|
||||
/// Reads a byte from memory.
|
||||
/// Returns a 0x00 if there is no data at that location but is still in ROM address range
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
println!("STARTING READ FROM At28C256 ${:04x} | ${:04x} | ${:04x}", self.offset, offset, self.max_offset);
|
||||
if offset < &self.offset || offset > &self.max_offset {
|
||||
println!("Unable to read from ${offset:04x} as it it out of range.");
|
||||
return 0x00;
|
||||
} else {
|
||||
println!("OK READ FROM GOOD AREA total len = {}", self.data.len());
|
||||
}
|
||||
|
||||
if *offset >= self.data.len() as u16 {
|
||||
0x00
|
||||
} else {
|
||||
self.data[*offset as usize]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// program
|
||||
///
|
||||
/// Writes new data to the memory chip
|
||||
fn program(new_data: &[u8; SIZE_32KB]) -> Box<At28C256> {
|
||||
println!("Writing new chip.");
|
||||
let mut working = At28C256::default();
|
||||
working.data = Box::new(*new_data);
|
||||
working.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl At28C256 {
|
||||
fn talking_to_me(&self, address: u16) -> bool {
|
||||
address >= self.offset && address < self.max_offset
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool) -> (u16, u8) {
|
||||
println!("At28C256: Tick starting for A${address_bus:04x} D${data_bus:02x} R{read_mode}");
|
||||
|
||||
// we aren't being addressed
|
||||
// OR
|
||||
// we arent reading from the ROM...
|
||||
if !self.talking_to_me(address_bus) ||
|
||||
!read_mode {
|
||||
// ...go away.
|
||||
return (address_bus, data_bus)
|
||||
}
|
||||
|
||||
let effective = address_bus - self.offset;
|
||||
if effective < self.max_offset {
|
||||
if effective < self.data.len() as u16 {
|
||||
self.data_bus = self.data[effective as usize];
|
||||
} else {
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
} else {
|
||||
println!("At28C256: OUTSIDE RANGE. :(");
|
||||
return (address_bus, data_bus)
|
||||
}
|
||||
|
||||
println!("At28C256: Read... {:02x}", self.data_bus);
|
||||
println!("At28C256: Done with ticking the AtC256");
|
||||
(address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn checksum_binary_loads() {
|
||||
let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin";
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes.", bytes.len());
|
||||
bytes
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("FAIL to read rom.");
|
||||
panic!("No rom no run.");
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let mut rom = At28C256::new(0x0000, 0x3fff, bytes);
|
||||
|
||||
assert_eq!(rom.checksum(), 0x58);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pub trait Backplane {
|
||||
fn data_bus(&self) -> u8;
|
||||
fn address_bus(&self) -> u16;
|
||||
fn read_mode(&self) -> bool;
|
||||
fn set_read_mode(&mut self, new_mode: bool);
|
||||
fn set_data_bus(&mut self, new_value: u8);
|
||||
fn set_address_bus(&mut self, new_value: u16);
|
||||
fn tick(&mut self);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub trait BusDevice {
|
||||
fn talking_to_me(&self, address: u16) -> bool;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Default for Hm62256 {
|
||||
fn default() -> Self {
|
||||
let vec = vec![0x00; SIZE_32KB];
|
||||
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
|
||||
let boxed_array: Box<[u8; SIZE_32KB]> =
|
||||
boxed_slice.try_into().expect("Unable to box the ram");
|
||||
Hm62256 {
|
||||
offset: 0x0000,
|
||||
data: boxed_array,
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
pub struct Hm62256State {
|
||||
pub offset: u16
|
||||
}
|
||||
|
||||
impl Hm62256 {
|
||||
pub fn dump(&self) -> Hm62256State {
|
||||
Hm62256State {
|
||||
offset: self.offset
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump_data(&self) -> (u16) {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// HM62256 Static Ram
|
||||
|
||||
pub mod ramchip;
|
||||
pub mod romchip;
|
||||
pub mod tick;
|
||||
pub mod default;
|
||||
pub mod new;
|
||||
pub mod dump;
|
||||
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::ram_chip::RamChip;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use log::debug;
|
||||
|
||||
/// Hitachi Semiconductor
|
||||
/// 8 Bit High Speed Static Ram
|
||||
/// 32KByte
|
||||
pub struct Hm62256 {
|
||||
pub(crate) offset: u16,
|
||||
pub(crate) data: Box<[u8]>,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) data_bus: u8
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rand::random;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn written_data_comes_back() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// 100,000 random read/writes to ram that all read back right
|
||||
for _ in 0..100_000 {
|
||||
let mut offset: u16 = random();
|
||||
println!("Size = {SIZE_32KB}");
|
||||
let value: u8 = random();
|
||||
println!("Wrote [{value:02x}] to [{offset:04x}]");
|
||||
ram.write(&offset, &value);
|
||||
|
||||
assert_eq!(ram.read(&offset), value)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_space_is_round() {
|
||||
// addresses written past the last address 'loop' back to 0+(offset - MAX_SIZE)
|
||||
let max_offset = SIZE_32KB;
|
||||
let test_offset = max_offset;
|
||||
|
||||
// all zero
|
||||
let mut ram = Hm62256::default();
|
||||
// write FF to the addresss after the last
|
||||
ram.write(&(test_offset as u16), &0xff);
|
||||
|
||||
// check all the ram for anything that isn't 0x00
|
||||
|
||||
assert_eq!(ram.read(&(0x0000)), 0xff);
|
||||
for offset in 1..SIZE_32KB {
|
||||
println!("Testing offset {offset:04x} for 0x00");
|
||||
assert_eq!(ram.read(&(offset as u16)), 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Hm62256 {
|
||||
pub fn new(base_offset: u16) -> Self {
|
||||
Self {
|
||||
offset: base_offset,
|
||||
data: vec![0; SIZE_32KB].into_boxed_slice(),
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::ram_chip::RamChip;
|
||||
|
||||
impl RamChip for Hm62256 {
|
||||
fn write(&mut self, offset: &u16, value: &u8) {
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
println!("Writing at E[{effective:04x}] / O[{offset:04x}]");
|
||||
self.data[effective as usize] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
use log::debug;
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
impl RomChip for Hm62256 {
|
||||
|
||||
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
// loops memory around past 32k
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
self.data[effective as usize]
|
||||
}
|
||||
|
||||
fn program(_: &[u8; SIZE_32KB]) -> Box<Self> {
|
||||
debug!("Dont program ram.");
|
||||
Hm62256::default().into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Hm62256 {
|
||||
fn max_address(&self) -> u16 {
|
||||
self.offset + SIZE_32KB as u16
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool, cs: bool) -> (u16, u8) {
|
||||
println!("HM62256RAM TICK START -> 0x{address_bus:04x} 0x{data_bus:02x} {read_mode} {cs}");
|
||||
if !(address_bus >= self.offset && address_bus < self.max_address()) {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
self.address_bus = address_bus;
|
||||
self.data_bus = data_bus;
|
||||
let addr = address_bus.wrapping_sub(self.offset) + self.offset;
|
||||
|
||||
// did we want to talk to the chip...
|
||||
if !cs {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
// ...or are we outside the range?
|
||||
if (addr - self.offset) > SIZE_32KB as u16 {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
|
||||
// ok. lets see what we are dealing with
|
||||
self.data_bus = if read_mode {
|
||||
self.data[addr as usize]
|
||||
} else {
|
||||
// writing to ram
|
||||
self.data[addr as usize] = data_bus.into();
|
||||
data_bus
|
||||
};
|
||||
(self.address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn write_to_memory_read_back_works_at_0() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// load the data to ram
|
||||
ram.tick(0x0000, 0xab, false, true);
|
||||
// read the data back
|
||||
let (_, new_data) = ram.tick(0x0000, 0x00, true, true);
|
||||
|
||||
assert_eq!(new_data, 0xab);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
+---+---+---+---+---+---+
|
||||
| 0 | 1 | 2 | 3 | 4 | 5 |
|
||||
+---+---+---+---+---+---+
|
||||
| 6 | 7 | 8 | 9 | A | B |
|
||||
+---+---+---+---+---+---+
|
||||
| C | D | E | F | AD| DA|
|
||||
+---+---+---+---+---+---+
|
||||
| + | PC| ST| RS| | |
|
||||
+---+---+---+---+---+---+
|
||||
*/
|
||||
|
||||
pub struct Kim1Keypad {
|
||||
keys: [bool; 23],
|
||||
stepping: bool
|
||||
}
|
||||
|
||||
impl Kim1Keypad {
|
||||
pub fn dump(&self) {
|
||||
println!("Dumping state of keypad");
|
||||
}
|
||||
}
|
||||
|
||||
impl Kim1Keypad {
|
||||
fn keyid(from: u8) -> usize{
|
||||
(from % 23) as usize
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Kim1Keypad {
|
||||
keys: [false; 23],
|
||||
stepping: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_stepping(&mut self) {
|
||||
self.stepping = !self.stepping;;
|
||||
}
|
||||
|
||||
pub fn set_stepping(&mut self, new_state: bool) {
|
||||
self.stepping = new_state
|
||||
}
|
||||
|
||||
pub fn press_key(&mut self, key_to_press: u8) {
|
||||
self.keys[Self::keyid(key_to_press)] = true;
|
||||
}
|
||||
|
||||
pub fn release_key(&mut self, key_to_release: u8) {
|
||||
self.keys[Self::keyid(key_to_release)] = false;
|
||||
}
|
||||
|
||||
pub fn is_pressed(&self, key: u8) -> bool {
|
||||
self.keys[Self::keyid(key)]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn keys_are_pressed() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
for index in 0..23 {
|
||||
assert!(!kb.is_pressed(index));
|
||||
kb.press_key(index);
|
||||
assert!(kb.is_pressed(index));
|
||||
|
||||
kb.release_key(index);
|
||||
assert!(!kb.is_pressed(index));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stepping_changes() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.set_stepping(false);
|
||||
|
||||
assert!(!kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(!kb.stepping);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn out_of_range() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.press_key(24);
|
||||
assert!(kb.is_pressed(1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pub mod rom_chip;
|
||||
pub mod at28c256;
|
||||
pub mod hm62256;
|
||||
pub mod ram_chip;
|
||||
pub mod mos6522;
|
||||
pub mod mos6530;
|
||||
pub mod kim1_keypad;
|
||||
mod bus_device;
|
||||
pub mod backplane;
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod mos6522;
|
||||
mod registers;
|
||||
mod new;
|
||||
mod tick;
|
||||
@@ -0,0 +1,109 @@
|
||||
use std::time::Instant;
|
||||
use log::debug;
|
||||
use crate::constants::constants_via6522::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mos6522 {
|
||||
/// data direction
|
||||
pub(crate) dda: u8,
|
||||
pub(crate) ddb: u8,
|
||||
|
||||
/// bottom 4 address bits
|
||||
pub(crate) rs0: u8,
|
||||
pub(crate) rs1: u8,
|
||||
pub(crate) rs2: u8,
|
||||
pub(crate) rs3: u8,
|
||||
|
||||
/// external data bus
|
||||
pub(crate) data_bus: u8,
|
||||
|
||||
pub(crate) cs1: bool,
|
||||
pub(crate) cs2: bool,
|
||||
// when true CPU is reading
|
||||
pub(crate) rw: bool,
|
||||
|
||||
/// reset circuit - true when reset inited
|
||||
pub(crate) reset: bool,
|
||||
|
||||
/// IRQ - true when interrupt waiting
|
||||
pub(crate) irq: bool,
|
||||
|
||||
pub(crate) ira: u8,
|
||||
pub(crate) ora: u8,
|
||||
pub(crate) porta: u8,
|
||||
pub(crate) irb: u8,
|
||||
pub(crate) orb: u8,
|
||||
pub(crate) portb: u8,
|
||||
|
||||
pub(crate) ca1: bool,
|
||||
pub(crate) ca2: bool,
|
||||
pub(crate) cb1: bool,
|
||||
pub(crate) cb2: bool,
|
||||
|
||||
// memory offset for where in the computers memory map this fits
|
||||
pub(crate) offset: u16,
|
||||
pub(crate) address_bus: u16,
|
||||
}
|
||||
|
||||
impl Mos6522 {
|
||||
pub fn max_offset(&self) -> u16 {
|
||||
self.offset + 0x10
|
||||
}
|
||||
|
||||
pub fn start_clocks(&mut self) {
|
||||
loop {
|
||||
let cycle_start = Instant::now();
|
||||
// let duration = cycle_start.duration_since(self.clock);
|
||||
// set the time to the new time.
|
||||
// self.clock = cycle_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn registers() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.dda, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.dda, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_DDRB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ddb, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ddb, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ora, 0b0000_0000);
|
||||
x.tick(VIA6522_ORA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ora, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.orb, 0b0000_0000);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.orb, 0b1111_1111);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_porta() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRA as u16, 0b1010_1010, false, true);
|
||||
x.tick(VIA6522_ORA as u16,0b1111_1111, false, true);
|
||||
assert_eq!(x.porta, 0b1010_1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_portb() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRB as u16, 0b0101_0101, false, true);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.portb, 0b0101_0101);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
pub fn new() -> Self {
|
||||
Mos6522::default()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
pub enum Via6522Registers {
|
||||
ORA,
|
||||
ORB,
|
||||
DDRA,
|
||||
DDRB,
|
||||
T1WL,
|
||||
T1CL,
|
||||
T1CH,
|
||||
T1LL,
|
||||
T2LL,
|
||||
T2CH,
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use log::debug;
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::constants::constants_via6522::{VIA6522_DDRA, VIA6522_DDRB, VIA6522_ORA, VIA6522_ORB};
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
fn max_address(&self) -> u16 {
|
||||
self.offset + SIZE_32KB as u16
|
||||
}
|
||||
/// tick
|
||||
///
|
||||
/// data_bus -> 8 bits from the data bus
|
||||
/// control -> 4 bits to identify which register to control
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8,reset: bool, rw: bool) -> (u16, u8) {
|
||||
if !(address_bus >= self.offset && address_bus.le(&self.max_address())) {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
let local_address = address_bus - self.offset;
|
||||
|
||||
println!("Mos6522 Tick Start -> D:0x{data_bus:02x} / A:0x{address_bus:02x} / {rw} (Actual 0x{local_address:02x} / 0b{local_address:08b})");
|
||||
if reset {
|
||||
// reset process
|
||||
println!("Resetting Mos6522");
|
||||
self.data_bus = data_bus;
|
||||
self.dda = 0x00;
|
||||
self.ddb = 0x00;
|
||||
self.porta = 0x00;
|
||||
self.portb = 0x00;
|
||||
return (self.address_bus, self.data_bus)
|
||||
}
|
||||
|
||||
if rw {
|
||||
// RW true = CPU is writing
|
||||
self.data_bus = data_bus;
|
||||
match local_address as u8 {
|
||||
VIA6522_DDRA => {
|
||||
println!("Setting DDA to 0x{data_bus:02x}");
|
||||
// setting the Data Direction for Port A
|
||||
self.dda = data_bus;
|
||||
},
|
||||
VIA6522_ORB => {
|
||||
// writing data to ORB
|
||||
let masked_data = data_bus & self.ddb;
|
||||
println!("Setting ORB to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
|
||||
self.orb = data_bus;
|
||||
self.portb = masked_data;
|
||||
},
|
||||
VIA6522_DDRB => {
|
||||
println!("Setting DDB to 0x{data_bus:02x}");
|
||||
// setting the data direction for port b
|
||||
self.ddb = data_bus;
|
||||
},
|
||||
|
||||
VIA6522_ORA => {
|
||||
// writing data to ORA
|
||||
let masked_data = data_bus & self.dda;
|
||||
println!("Setting ORA to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
|
||||
self.ora = data_bus;
|
||||
self.porta = masked_data;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
// RW false = CPU is reading
|
||||
self.data_bus = match local_address as u8 {
|
||||
VIA6522_DDRA => {
|
||||
self.dda
|
||||
}
|
||||
VIA6522_DDRB => {
|
||||
self.ddb
|
||||
}
|
||||
VIA6522_ORA => {
|
||||
self.porta & self.dda
|
||||
}
|
||||
VIA6522_ORB => {
|
||||
self.portb & self.ddb
|
||||
}
|
||||
_ => {
|
||||
debug!("VIA got request for b{:08b} / 0x{:02x}", address_bus, address_bus);
|
||||
// do nothing. bad address for VIA
|
||||
self.data_bus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(self.address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn dump(&self) {
|
||||
println!("Dumping state of Mos6530 RRIOT");
|
||||
}
|
||||
|
||||
pub fn dump_data(&self) -> (u16, u16, u16) {
|
||||
(self.io_offset, self.ram_offset, self.rom_offset)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod mos6530;
|
||||
pub mod tick;
|
||||
mod new;
|
||||
mod dump;
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::constants::constants_system::*;
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
/// Mos6530 RRIOT
|
||||
/// Ram/Rom/IO/Timer
|
||||
///
|
||||
/// Represents a single Mos6530 RRIOT Chip
|
||||
///
|
||||
/// Used in the TIM-1, KIM-1
|
||||
///
|
||||
/// 1kb Rom
|
||||
/// 64 bytes RAM
|
||||
/// IO Ports (A, B)
|
||||
/// Timer
|
||||
pub struct Mos6530 {
|
||||
pub(crate) data: [u8; SIZE_1KB],
|
||||
pub(crate) ram: [u8; 64],
|
||||
pub(crate) porta: u8,
|
||||
pub(crate) portb: u8,
|
||||
pub(crate) data_bus: u8,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) cs1: bool,
|
||||
pub(crate) cs2: bool,
|
||||
// when true, CPU is reading
|
||||
pub(crate) rw: bool,
|
||||
pub(crate) reset: bool,
|
||||
pub(crate) io_offset: u16,
|
||||
pub(crate) ram_offset: u16,
|
||||
pub(crate) rom_offset: u16
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn new(io_offset: u16,
|
||||
ram_offset: u16,
|
||||
rom_offset: u16,
|
||||
data: &[u8; SIZE_1KB]) -> Self {
|
||||
Mos6530 {
|
||||
data: *data,
|
||||
ram: [0x00; 64],
|
||||
porta: 0,
|
||||
portb: 0,
|
||||
data_bus: 0,
|
||||
address_bus: 0,
|
||||
cs1: false,
|
||||
cs2: false,
|
||||
rw: false,
|
||||
reset: false,
|
||||
io_offset,
|
||||
ram_offset,
|
||||
rom_offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, reset: bool, rw: bool) {
|
||||
debug!("Starting tick of MOS6530 RRIOT with 0x{address_bus:04x} / 0b{data_bus:08b} / R:{reset} / RW:{rw} (OFFSETS: I{:04x}, RA{:04x}, RO{:04x})", self.io_offset, self.ram_offset, self.rom_offset);
|
||||
let io_max = self.io_offset + 0x3f;
|
||||
let ram_max = self.ram_offset + 0x3f;
|
||||
let rom_max = self.rom_offset + 0x400;
|
||||
|
||||
if address_bus.ge(&self.io_offset) && address_bus.le(&io_max) {
|
||||
let effective = address_bus - self.io_offset;
|
||||
println!("IO Activity at effective 0x{effective:02x}");
|
||||
}
|
||||
|
||||
if address_bus.ge(&self.ram_offset) && address_bus.le(&ram_max) {
|
||||
let effective = address_bus - self.ram_offset;
|
||||
println!("RAM Activity at effective 0x{effective:02x}");
|
||||
}
|
||||
|
||||
if address_bus.ge(&self.rom_offset) && address_bus.le(&rom_max) {
|
||||
let effective = address_bus - self.rom_offset;
|
||||
println!("Rom Activity at effective 0x{effective:02x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
pub trait RamChip: RomChip {
|
||||
fn write(&mut self, offset: &u16, value: &u8);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
|
||||
pub trait RomChip {
|
||||
/// Read
|
||||
///
|
||||
/// Reads a single byte from the specified address
|
||||
fn read(&self, offset: &u16) -> u8;
|
||||
/// Program
|
||||
///
|
||||
/// Replaces all data in the ROM chip
|
||||
fn program(new_data: &[u8; SIZE_32KB]) -> Box<Self>;
|
||||
}
|
||||
Reference in New Issue
Block a user