diff --git a/Cargo.lock b/Cargo.lock index 1470c39..63dd0a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1102,6 +1102,14 @@ dependencies = [ "winit", ] +[[package]] +name = "egui" +version = "0.1.0" +dependencies = [ + "core", + "eframe", +] + [[package]] name = "egui" version = "0.27.2" diff --git a/Cargo.toml b/Cargo.toml index ff22a3b..a321a60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "cli", "macroquad", # "beneater" + "egui" ] resolver="2" diff --git a/cli/src/bin/ram_rom_widetick.rs b/cli/src/bin/ram_rom_widetick.rs deleted file mode 100644 index f4a477a..0000000 --- a/cli/src/bin/ram_rom_widetick.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::fs; -use core::computers::ram_rom::RamRomComputer; -use core::traits::backplane::Backplane; - - -fn main() { - let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin"); - println!("Taxation is theft"); - - let mut ramrom_computer = RamRomComputer::program_rom((&bytes[..]).to_vec()); - - ramrom_computer.tick2(0x05, 0b0000_0000, 0x05); - println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus", - ramrom_computer.data_bus(), - ramrom_computer.address_bus() - ); - println!("--TICK--"); - ramrom_computer.tick2(0x4005, 0b0000_0001, ramrom_computer.data_bus()); - println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus", - ramrom_computer.data_bus(), - ramrom_computer.address_bus() - ); - println!("--TICK--"); - ramrom_computer.tick2(0x4005, 0b0000_0000, ramrom_computer.data_bus()); - println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus", - ramrom_computer.data_bus(), - ramrom_computer.address_bus() - ); - println!("--TICK--"); -} \ No newline at end of file diff --git a/cli/src/bin/rom_only.rs b/cli/src/bin/rom_only.rs deleted file mode 100644 index 5cca49b..0000000 --- a/cli/src/bin/rom_only.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::fs; -use core::computers::rom_only::RomOnlyComputer; -use core::traits::backplane::Backplane; - -fn main() { - println!("Taxation is theft"); - 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_only = RomOnlyComputer::program((&bytes[..]).to_vec()); - - rom_only.set_read_mode(true); - rom_only.set_address_bus(0x05); - rom_only.tick(); - - println!("COMPUTER: Read {:02x} from ROM", rom_only.data_bus()) ; - println!("COMPUTER: Read {:04x} from Address Bus", rom_only.address_bus()); - - println!("----"); - rom_only.set_read_mode(true); - rom_only.set_address_bus(0x07); - rom_only.tick(); - - println!("COMPUTER: Read {:02x} from ROM", rom_only.data_bus()) ; - println!("COMPUTER: Read {:04x} from Address Bus", rom_only.address_bus()); - println!("----"); -} diff --git a/cli/src/bin/rom_only_widetick.rs b/cli/src/bin/rom_only_widetick.rs deleted file mode 100644 index b3ca53d..0000000 --- a/cli/src/bin/rom_only_widetick.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::fs; -use core::computers::rom_only::RomOnlyComputer; -use core::traits::backplane::Backplane; - -fn main() { - println!("Taxation is theft"); - let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin"; - let bytes = match fs::read(path) { - Ok(bytes) => { - println!("Loaded {} bytes", bytes.len()); - bytes - }, - Err(e) => vec![] - }; - - let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec()); - - rom_only.tick2(0x05, 0b0000_0001, 0x05); - println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus", rom_only.data_bus(), rom_only.address_bus()) ; -} diff --git a/cli/src/bin/single_breadboard.rs b/cli/src/bin/single_breadboard.rs new file mode 100644 index 0000000..ee20739 --- /dev/null +++ b/cli/src/bin/single_breadboard.rs @@ -0,0 +1,6 @@ +use core::computers::single_breadboard::SingleBreadboard; + +fn main() { + let sb = SingleBreadboard::new(); + +} \ No newline at end of file diff --git a/cli/tests/mod.rs b/cli/tests/mod.rs new file mode 100644 index 0000000..bbcc7be --- /dev/null +++ b/cli/tests/mod.rs @@ -0,0 +1,2 @@ +mod ram_rom_tests; +mod rom_only_test; \ No newline at end of file diff --git a/cli/tests/ram_rom_tests.rs b/cli/tests/ram_rom_tests.rs new file mode 100644 index 0000000..6a6fab0 --- /dev/null +++ b/cli/tests/ram_rom_tests.rs @@ -0,0 +1,16 @@ +use core::computers::ram_rom::RamRomComputer; +use core::traits::backplane::Backplane; +const RAM_START: u16 = 0x4000; +const ROM_START: u16 = 0x0000; + +#[test] +fn ram_rom_thintick() { + let mut ramrom = RamRomComputer::new(); + + ramrom.tick2(0x0001, 1, 0x00); + println!("DataBus: {}", ramrom.data_bus()); + ramrom.tick2(0x4001, 1,0xab); + println!("DataBus: {}", ramrom.data_bus()); + ramrom.tick2(0x4001, 1,0x00); + println!("DataBus: {}", ramrom.data_bus()); +} diff --git a/cli/tests/rom_only_test.rs b/cli/tests/rom_only_test.rs new file mode 100644 index 0000000..f6f1286 --- /dev/null +++ b/cli/tests/rom_only_test.rs @@ -0,0 +1,42 @@ +use core::computers::rom_only::RomOnlyComputer; +use core::traits::backplane::Backplane; + +#[test] +fn rom_only_widetick_test() { + let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin"); + + let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec()); + + rom_only.tick2(0x05, 0x00, 0x00); + assert_eq!(rom_only.data_bus(), 0x05); + assert_eq!(rom_only.address_bus(), 0x05); + + rom_only.tick2(0x07, 0x00, 0x00); + assert_eq!(rom_only.data_bus(), 0x07); + assert_eq!(rom_only.address_bus(), 0x07); + + rom_only.tick2(0x09, 0x00, 0x00); + assert_eq!(rom_only.data_bus(), 0x09); + assert_eq!(rom_only.address_bus(), 0x09); +} + +#[test] +fn rom_only_thintick_test() { + let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin"); + + let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec()); + + rom_only.set_read_mode(true); + rom_only.set_address_bus(0x05); + rom_only.tick(); + + assert_eq!(rom_only.data_bus(), 0x05); + assert_eq!(rom_only.address_bus(), 0x05); + + rom_only.set_read_mode(true); + rom_only.set_address_bus(0x07); + rom_only.tick(); + + assert_eq!(rom_only.data_bus(), 0x07); + assert_eq!(rom_only.address_bus(), 0x07); +} diff --git a/core/src/computers/beneater/mod.rs b/core/src/computers/beneater/mod.rs index 880b0d6..142de71 100644 --- a/core/src/computers/beneater/mod.rs +++ b/core/src/computers/beneater/mod.rs @@ -1,7 +1,17 @@ mod backplane; use crate::mos6502cpu::Mos6502Cpu; -use crate::periph::mos6522::mos6522::Mos6522; +use crate::periph::mos6522::Mos6522; + +/* +SBC Designed by Ben Eater + +0x0000 -> 0x3fff -> RAM (16KB) +0x4000 -> 0x5fff -> UNUSED +0x6000 -> 0x600f -> VIA +0x6010 -> 0x7fff -> UNUSED +0x8000 -> 0xffff -> ROM (32KB) + */ pub struct BenEater { cpu: Mos6502Cpu, diff --git a/core/src/computers/kim1/mod.rs b/core/src/computers/kim1/mod.rs index 2c43c07..627e96c 100644 --- a/core/src/computers/kim1/mod.rs +++ b/core/src/computers/kim1/mod.rs @@ -5,7 +5,7 @@ pub mod reset; use crate::mos6502cpu::Mos6502Cpu; use crate::periph::hm62256::Hm62256; use crate::periph::kim1_keypad::Kim1Keypad; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6530::Mos6530; /// Represents a KIM-1 /// diff --git a/core/src/computers/kim1/new.rs b/core/src/computers/kim1/new.rs index 773f378..9a5d627 100644 --- a/core/src/computers/kim1/new.rs +++ b/core/src/computers/kim1/new.rs @@ -1,7 +1,7 @@ use crate::computers::kim1::Kim1; use crate::periph::hm62256::Hm62256; use crate::periph::kim1_keypad::Kim1Keypad; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6530::Mos6530; impl Kim1 { pub fn dump(&self) { @@ -13,8 +13,8 @@ impl Kim1 { } 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"); + let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-002_fillerbyte00-0x1c00.bin"); + let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-003_fillerbyte00-0x1800.bin"); Self { cpu: Default::default(), diff --git a/core/src/computers/kim1/reset.rs b/core/src/computers/kim1/reset.rs index 1d13e8f..0745f43 100644 --- a/core/src/computers/kim1/reset.rs +++ b/core/src/computers/kim1/reset.rs @@ -1,11 +1,11 @@ use crate::computers::kim1::Kim1; use crate::periph::hm62256::Hm62256; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::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"); + let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-002_fillerbyte00-0x1c00.bin"); + let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/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()); diff --git a/core/src/computers/mod.rs b/core/src/computers/mod.rs index f5d33eb..25a1028 100644 --- a/core/src/computers/mod.rs +++ b/core/src/computers/mod.rs @@ -3,3 +3,4 @@ pub mod rom_only; pub mod kim1; pub mod ram_rom; pub mod tim1; +pub mod single_breadboard; diff --git a/core/src/computers/ram_rom/backplane.rs b/core/src/computers/ram_rom/backplane.rs index 75d31e6..63d095a 100644 --- a/core/src/computers/ram_rom/backplane.rs +++ b/core/src/computers/ram_rom/backplane.rs @@ -69,4 +69,3 @@ impl Backplane for RamRomComputer { (0, false, false) } } - diff --git a/core/src/computers/ram_rom/mod.rs b/core/src/computers/ram_rom/mod.rs index 0a7aada..5fe828f 100644 --- a/core/src/computers/ram_rom/mod.rs +++ b/core/src/computers/ram_rom/mod.rs @@ -4,7 +4,7 @@ use crate::periph::hm62256::Hm62256; pub mod backplane; pub mod program_rom; pub mod new; -mod tick2; +pub mod tick2; pub struct RamRomComputer { pub(crate) rom: At28C256, diff --git a/core/src/computers/ram_rom/tick2.rs b/core/src/computers/ram_rom/tick2.rs index 21aec55..40fa288 100644 --- a/core/src/computers/ram_rom/tick2.rs +++ b/core/src/computers/ram_rom/tick2.rs @@ -1,6 +1,5 @@ -use std::collections::BTreeMap; use crate::computers::ram_rom::RamRomComputer; -use crate::traits::bus_device::BusDevice; +use crate::traits::backplane::Backplane; struct ChipSignals { cs: bool, @@ -14,21 +13,8 @@ enum RomRamChips { } impl RamRomComputer { - pub fn signal_tick(&mut self, address: u16, data: u8) { - println!("⏲️RAM ROM COMPUTER SIGNAL TICK"); - - // no CPU to tick. - - let mut ram_state = ChipSignals { oe: false, we: false, cs: false}; - let mut rom_state = ChipSignals { oe: false, we: false, cs: false}; - - - // Tick the RAM - // Tick the ROM - } - pub fn tick2(&mut self, address: u16, control: u8, data: u8) -> (u8) { - println!("RAM ROM Computer tick starting / {address:04x} {control:08b} {data:02x}"); + println!("⏲RAM ROM Computer tick starting / {address:04x} {control:08b} {data:02x}"); // tick the parts @@ -37,10 +23,16 @@ impl RamRomComputer { // 0x4000 -> 0x7fff -> ROM (At28C256) match address { 0x0000..=0x3fff => { - println!("__DATA TARGETTING ROM BEING STORED ON DATA BUS"); + if self.read_mode { + println!("⏲__ROM BEING READ FROM"); + let new_data = self.rom.signal_tick(address, data, true, true, true); + self.set_data_bus(new_data); + } else { + panic!("UNABLE TO WRITE TO ROM"); + } } 0x4000 ..=0x7fff => { - println!("__DATA TARGETTING RRAAMM GETTING STORED ON DATA BUS"); + println!("⏲__DATA TARGETTING RRAAMM GETTING STORED ON DATA BUS"); } _ => {} }; @@ -48,4 +40,4 @@ impl RamRomComputer { let (_, ram_data_bus) = self.ram.tick(address, data, control == 1, true); 0 } -} \ No newline at end of file +} diff --git a/core/src/computers/rom_only/debug_memory.rs b/core/src/computers/rom_only/debug_memory.rs index 342d5af..c288dd0 100644 --- a/core/src/computers/rom_only/debug_memory.rs +++ b/core/src/computers/rom_only/debug_memory.rs @@ -7,7 +7,6 @@ impl RomOnlyComputer { for index in 0..size { println!("Index {index} for {}", index + start_offset); } - data } -} \ No newline at end of file +} diff --git a/core/src/computers/rom_only/mod.rs b/core/src/computers/rom_only/mod.rs index db2c083..9a1c84f 100644 --- a/core/src/computers/rom_only/mod.rs +++ b/core/src/computers/rom_only/mod.rs @@ -8,16 +8,18 @@ pub mod debug_memory; mod rom_chunks; pub struct RomOnlyComputer { - pub(crate) rom: At28C256, - pub(crate) data_bus: u8, - pub(crate) address_bus: u16, - pub(crate) read_mode: bool, + pub rom: At28C256, + pub data_bus: u8, + pub address_bus: u16, + pub read_mode: bool, } impl RomOnlyComputer { pub fn tick2(&mut self, address: u16, control: u8, data: u8) -> (u8) { // tick the parts... println!("WIDETICK: A:${address:04x} D:${data:02x} C:b{control:08b}"); + self.address_bus = address; + self.data_bus = data; let new_data = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true); println!("\nNew Data : {new_data:02x}"); diff --git a/core/src/computers/rom_only/new.rs b/core/src/computers/rom_only/new.rs index f022b63..889f708 100644 --- a/core/src/computers/rom_only/new.rs +++ b/core/src/computers/rom_only/new.rs @@ -9,5 +9,4 @@ impl RomOnlyComputer { } RomOnlyComputer::program(working) } - } \ No newline at end of file diff --git a/core/src/computers/single_breadboard/README b/core/src/computers/single_breadboard/README new file mode 100644 index 0000000..4377941 --- /dev/null +++ b/core/src/computers/single_breadboard/README @@ -0,0 +1,3 @@ +Computer at +https://www.youtube.com/watch?v=s3t2QMukBRs +https://github.com/AndersBNielsen/6507SBC diff --git a/core/src/computers/single_breadboard/mod.rs b/core/src/computers/single_breadboard/mod.rs new file mode 100644 index 0000000..bcc9292 --- /dev/null +++ b/core/src/computers/single_breadboard/mod.rs @@ -0,0 +1,20 @@ +use crate::constants::constants_system::SIZE_32KB; +use crate::mos6502cpu::Mos6502Cpu; +use crate::periph::at28c256::At28C256; +use crate::periph::mos6532::Mos6532; + +pub struct SingleBreadboard { + pub cpu: Mos6502Cpu, + pub ram: At28C256, + pub via: Mos6532 +} + +impl SingleBreadboard { + pub fn new() -> Self { + SingleBreadboard { + cpu: Mos6502Cpu::default(), + ram: At28C256::new(0xf000, 0xffff, vec![0x00; SIZE_32KB]), + via: Mos6532::new(0x0000, 0x0080) + } + } +} diff --git a/core/src/computers/tim1/backplane.rs b/core/src/computers/tim1/backplane.rs new file mode 100644 index 0000000..bb5808d --- /dev/null +++ b/core/src/computers/tim1/backplane.rs @@ -0,0 +1,46 @@ +use std::net::IpAddr::V4; +use log::debug; +use crate::computers::tim1::Tim1; +use crate::traits::backplane::Backplane; + +impl Backplane for Tim1 { + 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!("Starting tick for TIM-1"); + } + + fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 { + debug!("No dedicated RAM. Its in the VIA"); + 0x00 + } + + fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 { + debug!("No dedicated ROM. Its in the VIA"); + 0x00 + } + + fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) { + debug!("Starting VIA Tick"); + self.pia.tick(address, data, false, self.read_mode); + // is this for the ROM? + + // is this for the RAM? + + // is this for the Interrupts? + + // is this for the Timers? + (0x00, false, false) + } +} diff --git a/core/src/computers/tim1/mod.rs b/core/src/computers/tim1/mod.rs index d6710aa..bb235a0 100644 --- a/core/src/computers/tim1/mod.rs +++ b/core/src/computers/tim1/mod.rs @@ -1,8 +1,13 @@ +mod backplane; + use crate::mos6502cpu::Mos6502Cpu; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6530::Mos6530; pub struct Tim1 { cpu: Mos6502Cpu, - pia: Mos6530 + pia: Mos6530, + read_mode: bool, + data_bus: u8, + address_bus: u16 } diff --git a/core/src/instruction.rs b/core/src/instruction.rs index 54e2f24..3bf0698 100644 --- a/core/src/instruction.rs +++ b/core/src/instruction.rs @@ -6,7 +6,7 @@ use crate::op_info::OpInfo; use crate::operand::Operand; use crate::operation::Operation; use crate::operation::Operation::*; -use log::trace; +use log::{debug, trace}; #[derive(Debug, PartialEq)] pub struct Instruction { @@ -17,7 +17,7 @@ pub struct Instruction { impl Instruction { pub fn opinfo(bytes: &[u8]) -> Option { - trace!("DECODING : {bytes:?}"); + debug!("DECODING : {bytes:?}"); let opcode = bytes.get(0).copied()?; Some(INSTRUCTION_TABLE[opcode as usize].clone())? } @@ -37,1267 +37,6 @@ impl Instruction { operand, }; - trace!("RETURNING: {:?}", instruction); Some(instruction) } } - -#[cfg(test)] -mod test { - use super::*; - use crate::address_mode::AddressMode::*; - use crate::instruction::Instruction; - use crate::operation::Operation::*; - - #[test] - fn decode_instruction() { - let params = vec![ - // ADC - ( - vec![0x69, 0xab], - Instruction { - op: ADC, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x65, 0xab], - Instruction { - op: ADC, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x75, 0xab], - Instruction { - op: ADC, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x6d, 0xcd, 0xab], - Instruction { - op: ADC, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x7d, 0xcd, 0xab], - Instruction { - op: ADC, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x79, 0xcd, 0xab], - Instruction { - op: ADC, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x61, 0xab], - Instruction { - op: ADC, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x71, 0xab], - Instruction { - op: ADC, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // AND - ( - vec![0x29, 0xab], - Instruction { - op: AND, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x25, 0xab], - Instruction { - op: AND, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x35, 0xab], - Instruction { - op: AND, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x2d, 0xcd, 0xab], - Instruction { - op: AND, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x3d, 0xcd, 0xab], - Instruction { - op: AND, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x39, 0xcd, 0xab], - Instruction { - op: AND, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x21, 0xab], - Instruction { - op: AND, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x31, 0xab], - Instruction { - op: AND, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // ASL - ( - vec![0x0a], - Instruction { - op: ASL, - mode: Accumulator, - operand: Operand::None, - }, - ), - ( - vec![0x06, 0xab], - Instruction { - op: ASL, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x16, 0xab], - Instruction { - op: ASL, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x0e, 0xcd, 0xab], - Instruction { - op: ASL, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x1e, 0xcd, 0xab], - Instruction { - op: ASL, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - // BIT - ( - vec![0x24, 0xab], - Instruction { - op: BIT, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x2c, 0xcd, 0xab], - Instruction { - op: BIT, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - // BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS - ( - vec![0x10, 0xab], - Instruction { - op: BPL, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x30, 0xab], - Instruction { - op: BMI, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x50, 0xab], - Instruction { - op: BVC, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x70, 0xab], - Instruction { - op: BVS, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x90, 0xab], - Instruction { - op: BCC, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xb0, 0xab], - Instruction { - op: BCS, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xd0, 0xab], - Instruction { - op: BNE, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xf0, 0xab], - Instruction { - op: BEQ, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - // BRK - ( - vec![0x00], - Instruction { - op: BRK, - mode: Implied, - operand: Operand::None, - }, - ), - // CLC, CLD, CLI, CLV - ( - vec![0x18], - Instruction { - op: CLC, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0xd8], - Instruction { - op: CLD, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x58], - Instruction { - op: CLI, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0xb8], - Instruction { - op: CLV, - mode: Implied, - operand: Operand::None, - }, - ), - // CMP - ( - vec![0xc9, 0xab], - Instruction { - op: CMP, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xc5, 0xab], - Instruction { - op: CMP, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xd5, 0xab], - Instruction { - op: CMP, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xcd, 0xcd, 0xab], - Instruction { - op: CMP, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xdd, 0xcd, 0xab], - Instruction { - op: CMP, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xd9, 0xcd, 0xab], - Instruction { - op: CMP, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xc1, 0xab], - Instruction { - op: CMP, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xd1, 0xab], - Instruction { - op: CMP, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // CPX - ( - vec![0xe0, 0xab], - Instruction { - op: CPX, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xe4, 0xab], - Instruction { - op: CPX, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xec, 0xcd, 0xab], - Instruction { - op: CPX, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - // CPY - ( - vec![0xc0, 0xab], - Instruction { - op: CPY, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xc4, 0xab], - Instruction { - op: CPY, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xcc, 0xcd, 0xab], - Instruction { - op: CPY, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - // DEC, DEX, DEY - ( - vec![0xc6, 0xab], - Instruction { - op: DEC, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xd6, 0xab], - Instruction { - op: DEC, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xce, 0xcd, 0xab], - Instruction { - op: DEC, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xde, 0xcd, 0xab], - Instruction { - op: DEC, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xca], - Instruction { - op: DEX, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x88], - Instruction { - op: DEY, - mode: Implied, - operand: Operand::None, - }, - ), - // EOR - ( - vec![0x49, 0xab], - Instruction { - op: EOR, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x45, 0xab], - Instruction { - op: EOR, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x55, 0xab], - Instruction { - op: EOR, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x4d, 0xcd, 0xab], - Instruction { - op: EOR, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x5d, 0xcd, 0xab], - Instruction { - op: EOR, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x59, 0xcd, 0xab], - Instruction { - op: EOR, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x41, 0xab], - Instruction { - op: EOR, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x51, 0xab], - Instruction { - op: EOR, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // INC, INX, INY - ( - vec![0xe6, 0xab], - Instruction { - op: INC, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xf6, 0xab], - Instruction { - op: INC, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xee, 0xcd, 0xab], - Instruction { - op: INC, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xfe, 0xcd, 0xab], - Instruction { - op: INC, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xe8], - Instruction { - op: INX, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0xc8], - Instruction { - op: INY, - mode: Implied, - operand: Operand::None, - }, - ), - // JMP, JSR - ( - vec![0x4c, 0xcd, 0xab], - Instruction { - op: JMP, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x6c, 0xcd, 0xab], - Instruction { - op: JMP, - mode: Indirect, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x20], - Instruction { - op: JSR, - mode: Implied, - operand: Operand::None, - }, - ), - // LDA - ( - vec![0xa9, 0xab], - Instruction { - op: LDA, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xa5, 0xab], - Instruction { - op: LDA, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xb5, 0xab], - Instruction { - op: LDA, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xad, 0xcd, 0xab], - Instruction { - op: LDA, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xbd, 0xcd, 0xab], - Instruction { - op: LDA, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xb9, 0xcd, 0xab], - Instruction { - op: LDA, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xa1, 0xab], - Instruction { - op: LDA, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xb1, 0xab], - Instruction { - op: LDA, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // LDX - ( - vec![0xa2, 0xab], - Instruction { - op: LDX, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xa6, 0xab], - Instruction { - op: LDX, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xb6, 0xab], - Instruction { - op: LDX, - mode: ZeroPageY, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xae, 0xcd, 0xab], - Instruction { - op: LDX, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xbe, 0xcd, 0xab], - Instruction { - op: LDX, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - // LDY - ( - vec![0xa0, 0xab], - Instruction { - op: LDY, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xa4, 0xab], - Instruction { - op: LDY, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xb4, 0xab], - Instruction { - op: LDY, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xac, 0xcd, 0xab], - Instruction { - op: LDY, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xbc, 0xcd, 0xab], - Instruction { - op: LDY, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - // LSR - ( - vec![0x4a], - Instruction { - op: LSR, - mode: Accumulator, - operand: Operand::None, - }, - ), - ( - vec![0x46, 0xab], - Instruction { - op: LSR, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x56, 0xab], - Instruction { - op: LSR, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x4e, 0xcd, 0xab], - Instruction { - op: LSR, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x5e, 0xcd, 0xab], - Instruction { - op: LSR, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - // NOP - ( - vec![0xea], - Instruction { - op: NOP, - mode: Implied, - operand: Operand::None, - }, - ), - // ORA - ( - vec![0x09, 0xab], - Instruction { - op: ORA, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x05, 0xab], - Instruction { - op: ORA, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x15, 0xab], - Instruction { - op: ORA, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x0d, 0xcd, 0xab], - Instruction { - op: ORA, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x1d, 0xcd, 0xab], - Instruction { - op: ORA, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x19, 0xcd, 0xab], - Instruction { - op: ORA, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x01, 0xab], - Instruction { - op: ORA, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x11, 0xab], - Instruction { - op: ORA, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // PHA, PHP, PLA, PLP - ( - vec![0x48], - Instruction { - op: PHA, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x08], - Instruction { - op: PHP, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x68], - Instruction { - op: PLA, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x28], - Instruction { - op: PLP, - mode: Implied, - operand: Operand::None, - }, - ), - // ROL - ( - vec![0x2a], - Instruction { - op: ROL, - mode: Accumulator, - operand: Operand::None, - }, - ), - ( - vec![0x26, 0xab], - Instruction { - op: ROL, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x36, 0xab], - Instruction { - op: ROL, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x2e, 0xcd, 0xab], - Instruction { - op: ROL, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x3e, 0xcd, 0xab], - Instruction { - op: ROL, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - // ROR - ( - vec![0x6a], - Instruction { - op: ROR, - mode: Accumulator, - operand: Operand::None, - }, - ), - ( - vec![0x66, 0xab], - Instruction { - op: ROR, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x76, 0xab], - Instruction { - op: ROR, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x6e, 0xcd, 0xab], - Instruction { - op: ROR, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x7e, 0xcd, 0xab], - Instruction { - op: ROR, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - // RTI, RTS - ( - vec![0x40], - Instruction { - op: RTI, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x60], - Instruction { - op: RTS, - mode: Implied, - operand: Operand::None, - }, - ), - // SBC - ( - vec![0xe9, 0xab], - Instruction { - op: SBC, - mode: Immediate, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xe5, 0xab], - Instruction { - op: SBC, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xf5, 0xab], - Instruction { - op: SBC, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xed, 0xcd, 0xab], - Instruction { - op: SBC, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xfd, 0xcd, 0xab], - Instruction { - op: SBC, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xf9, 0xcd, 0xab], - Instruction { - op: SBC, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0xe1, 0xab], - Instruction { - op: SBC, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0xf1, 0xab], - Instruction { - op: SBC, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // SEC, SED, SEI - ( - vec![0x38], - Instruction { - op: SEC, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0xf8], - Instruction { - op: SED, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x78], - Instruction { - op: SEI, - mode: Implied, - operand: Operand::None, - }, - ), - // STA - ( - vec![0x85, 0xab], - Instruction { - op: STA, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x95, 0xab], - Instruction { - op: STA, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x8d, 0xcd, 0xab], - Instruction { - op: STA, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x9d, 0xcd, 0xab], - Instruction { - op: STA, - mode: AbsoluteX, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x99, 0xcd, 0xab], - Instruction { - op: STA, - mode: AbsoluteY, - operand: Operand::Word(0xabcd), - }, - ), - ( - vec![0x81, 0xab], - Instruction { - op: STA, - mode: IndirectX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x91, 0xab], - Instruction { - op: STA, - mode: IndirectY, - operand: Operand::Byte(0xab), - }, - ), - // STX - ( - vec![0x86, 0xab], - Instruction { - op: STX, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x96, 0xab], - Instruction { - op: STX, - mode: ZeroPageY, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x8e, 0xcd, 0xab], - Instruction { - op: STX, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - // STY - ( - vec![0x84, 0xab], - Instruction { - op: STY, - mode: ZeroPage, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x94, 0xab], - Instruction { - op: STY, - mode: ZeroPageX, - operand: Operand::Byte(0xab), - }, - ), - ( - vec![0x8c, 0xcd, 0xab], - Instruction { - op: STY, - mode: Absolute, - operand: Operand::Word(0xabcd), - }, - ), - // TAX, TAY, TSX, TXA, TXS, TYA - ( - vec![0xaa], - Instruction { - op: TAX, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0xa8], - Instruction { - op: TAY, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0xba], - Instruction { - op: TSX, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x8a], - Instruction { - op: TXA, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x9a], - Instruction { - op: TXS, - mode: Implied, - operand: Operand::None, - }, - ), - ( - vec![0x98], - Instruction { - op: TYA, - mode: Implied, - operand: Operand::None, - }, - ), - ]; - for (bytes, instruction) in params { - let result1 = Instruction::decode(&bytes); - if let Some(instruction1) = result1 { - assert_eq!(instruction, instruction1) - } else { - println!("Failed to decode {:?}", bytes); - } - } - } -} diff --git a/core/src/instruction_table.rs b/core/src/instruction_table.rs index f1dff83..698b97d 100644 --- a/core/src/instruction_table.rs +++ b/core/src/instruction_table.rs @@ -1,32 +1,5 @@ use crate::address_mode::AddressMode; -use crate::constants::constants_isa_op::{ - ISA_OP_ADC_ABS, ISA_OP_ADC_ABSX, ISA_OP_ADC_ABSY, ISA_OP_ADC_I, ISA_OP_ADC_INDX, - ISA_OP_ADC_INDY, ISA_OP_ADC_Z, ISA_OP_ADC_ZX, ISA_OP_AND_ABS, ISA_OP_AND_ABSX, ISA_OP_AND_ABSY, - ISA_OP_AND_I, ISA_OP_AND_INDX, ISA_OP_AND_INDY, ISA_OP_AND_Z, ISA_OP_AND_ZX, ISA_OP_ASL_A, - ISA_OP_ASL_ABS, ISA_OP_ASL_ABSX, ISA_OP_ASL_Z, ISA_OP_ASL_ZX, ISA_OP_BCC, ISA_OP_BCS, - ISA_OP_BEQ, ISA_OP_BIT_ABS, ISA_OP_BIT_ZP, ISA_OP_BMI, ISA_OP_BNE, ISA_OP_BPL, ISA_OP_BRK, - ISA_OP_BVC, ISA_OP_BVS, ISA_OP_CLC, ISA_OP_CLD, ISA_OP_CLI, ISA_OP_CLV, ISA_OP_CMP_ABS, - ISA_OP_CMP_ABSX, ISA_OP_CMP_ABSY, ISA_OP_CMP_I, ISA_OP_CMP_INDX, ISA_OP_CMP_INDY, - ISA_OP_CMP_ZP, ISA_OP_CMP_ZPX, ISA_OP_CPX_ABS, ISA_OP_CPX_I, ISA_OP_CPX_ZP, ISA_OP_CPY_ABS, - ISA_OP_CPY_I, ISA_OP_CPY_ZP, ISA_OP_DEC_ABS, ISA_OP_DEC_ABSX, ISA_OP_DEC_ZP, ISA_OP_DEC_ZPX, - ISA_OP_DEX, ISA_OP_DEY, ISA_OP_EOR_ABS, ISA_OP_EOR_ABSX, ISA_OP_EOR_ABSY, ISA_OP_EOR_I, - ISA_OP_EOR_INDX, ISA_OP_EOR_INDY, ISA_OP_EOR_ZP, ISA_OP_EOR_ZPX, ISA_OP_INC_ABS, - ISA_OP_INC_ABSX, ISA_OP_INC_ZP, ISA_OP_INC_ZPX, ISA_OP_INX, ISA_OP_INY, ISA_OP_JMP_ABS, - ISA_OP_JMP_IND, ISA_OP_JSR, ISA_OP_LDA_ABS, ISA_OP_LDA_ABSX, ISA_OP_LDA_ABSY, ISA_OP_LDA_I, - ISA_OP_LDA_INDX, ISA_OP_LDA_INDY, ISA_OP_LDA_Z, ISA_OP_LDA_ZX, ISA_OP_LDX_ABS, ISA_OP_LDX_ABSY, - ISA_OP_LDX_I, ISA_OP_LDX_ZP, ISA_OP_LDX_ZPY, ISA_OP_LDY_ABS, ISA_OP_LDY_ABSX, ISA_OP_LDY_I, - ISA_OP_LDY_ZP, ISA_OP_LDY_ZPX, ISA_OP_LSR_A, ISA_OP_LSR_ABS, ISA_OP_LSR_ABSX, ISA_OP_LSR_ZP, - ISA_OP_LSR_ZPX, ISA_OP_NOP, ISA_OP_ORA_ABS, ISA_OP_ORA_ABSX, ISA_OP_ORA_ABSY, ISA_OP_ORA_I, - ISA_OP_ORA_INDX, ISA_OP_ORA_INDY, ISA_OP_ORA_ZP, ISA_OP_ORA_ZPX, ISA_OP_PHA, ISA_OP_PHP, - ISA_OP_PLA, ISA_OP_PLP, ISA_OP_ROL_A, ISA_OP_ROL_ABS, ISA_OP_ROL_ABSX, ISA_OP_ROL_ZP, - ISA_OP_ROL_ZPX, ISA_OP_ROR_A, ISA_OP_ROR_ABS, ISA_OP_ROR_ABSX, ISA_OP_ROR_ZP, ISA_OP_ROR_ZPX, - ISA_OP_RTI, ISA_OP_RTS, ISA_OP_SBC_ABS, ISA_OP_SBC_ABSX, ISA_OP_SBC_ABSY, ISA_OP_SBC_I, - ISA_OP_SBC_INDX, ISA_OP_SBC_INDY, ISA_OP_SBC_ZP, ISA_OP_SBC_ZPX, ISA_OP_SEC, ISA_OP_SED, - ISA_OP_SEI, ISA_OP_STA_ABS, ISA_OP_STA_ABSX, ISA_OP_STA_ABSY, ISA_OP_STA_INDX, ISA_OP_STA_INDY, - ISA_OP_STA_ZP, ISA_OP_STA_ZPX, ISA_OP_STX_ABS, ISA_OP_STX_ZP, ISA_OP_STX_ZPY, ISA_OP_STY_ABS, - ISA_OP_STY_ZP, ISA_OP_STY_ZPX, ISA_OP_TAX, ISA_OP_TAY, ISA_OP_TSX, ISA_OP_TXA, ISA_OP_TXS, - ISA_OP_TYA, -}; +use crate::constants::constants_isa_op::*; use crate::op_info::OpInfo; use crate::operation::Operation; use crate::operation::Operation::*; @@ -39,7 +12,6 @@ pub fn instruction_length(instruction: u8) -> u8 { INSTRUCTION_TABLE[instruction as usize].clone().unwrap().length } - pub const INSTRUCTION_TABLE: [Option; 256] = { let mut table: [Option; 256] = [const { None }; 256]; table[ISA_OP_ADC_I as usize] = Some(OpInfo { @@ -1307,49 +1279,3 @@ pub const INSTRUCTION_TABLE: [Option; 256] = { table }; - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn test_instruction_table_completeness() { - use INSTRUCTION_TABLE; // Adjust to your actual path - - let mut defined_count = 0; - let mut defined_opcodes = vec![]; - - for (i, entry) in INSTRUCTION_TABLE.iter().enumerate() { - if let Some(info) = entry { - defined_count += 1; - defined_opcodes.push(i); - // Optional: sanity check - assert!( - info.length > 0 && info.cycles > 0, - "Invalid OpInfo at opcode {:#04x?}", - i - ); - } - } - - println!("Defined opcodes: {}", defined_count); - for i in 0..256 { - if defined_opcodes.contains(&i) { - print!("{:02x} ", i); - } - } - println!("\nMissing opcodes:"); - for i in 0..256 { - if !defined_opcodes.contains(&i) { - print!("{:02X} ", i); - } - } - println!(); - - // The standard 6502 has 151 documented opcodes - assert_eq!( - defined_count, 151, - "Expected 151 opcodes, found {}", - defined_count - ); - } -} diff --git a/core/src/mos6502flags.rs b/core/src/mos6502flags.rs index a52286d..c101664 100644 --- a/core/src/mos6502flags.rs +++ b/core/src/mos6502flags.rs @@ -1,6 +1,4 @@ -use crate::mos6502flags::Mos6502Flag::{ - Break, Carry, Decimal, Interrupt, Negative, Overflow, Zero, -}; +use crate::mos6502flags::Mos6502Flag::*; pub const BIT_NEGATIVE: u8 = 7; pub const BIT_OVERFLOW: u8 = 6; @@ -68,13 +66,14 @@ impl Mos6502Flag { #[derive(Default, PartialEq, Debug)] pub struct Mos6502Flags { - carry: bool, - zero: bool, - interrupt: bool, - decimal: bool, - break_flag: bool, - overflow: bool, - negative: bool, + // TODO: This is pub for tests. shouldn't be that way + pub carry: bool, + pub zero: bool, + pub interrupt: bool, + pub decimal: bool, + pub break_flag: bool, + pub overflow: bool, + pub negative: bool, } impl Mos6502Flags { @@ -197,31 +196,3 @@ impl Mos6502Flags { (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)); - } -} diff --git a/core/src/periph/at28c256/checksum.rs b/core/src/periph/at28c256/checksum.rs index 55a3aa8..cf03095 100644 --- a/core/src/periph/at28c256/checksum.rs +++ b/core/src/periph/at28c256/checksum.rs @@ -15,4 +15,3 @@ impl At28C256 { data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b)) } } - diff --git a/core/src/periph/at28c256/mod.rs b/core/src/periph/at28c256/mod.rs index 588c999..af446b2 100644 --- a/core/src/periph/at28c256/mod.rs +++ b/core/src/periph/at28c256/mod.rs @@ -10,7 +10,6 @@ pub mod signal_tick; pub mod memory_chip; pub mod bus_device; - /// At28C256 /// /// Represents a single At28C256 EEPROM Chip @@ -21,7 +20,7 @@ pub struct At28C256 { // Logical parts data_bus: u8, address_bus: u16, - data: Box<[u8]>, + pub data: Box<[u8]>, // where in the computer memory map do we live? offset: u16, max_offset: u16, @@ -30,152 +29,3 @@ pub struct At28C256 { we: bool, oe: bool } - -#[cfg(test)] -mod test { - use crate::traits::rom_chip::RomChip; - use std::io::Read; - use std::fs; - use crate::constants::constants_system::*; - use crate::constants::constants_test::*; - use crate::periph::at28c256::At28C256; - use crate::traits::bus_device::BusDevice; - use crate::traits::memory_chip::MemoryChip; - - #[test] - fn smoke() { assert!(true); } - - #[test] - fn checksum_binary_loads() { - let path = format!("{}/{}", TEST_PERIPH_AT28C256_ROOT, "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."); - } - }; - - let mut rom = At28C256::new(0x0000, 0x3fff, bytes); - - assert_eq!(rom.checksum(), 0x58); - } - - #[test] - fn full_chunks_come_back_ok() { - let test_data = (0..255).collect(); - let mut chip = At28C256::new(0x0000, 0x3fff, test_data); - - let chunks = chip.chunks(16); - assert_eq!(chunks.len(), 16); - } - - #[test] - fn partial_blocks_come_back_ok() { - let test_data = (0..=3).collect(); - let mut chip = At28C256::new(0x0000, 0x3fff, test_data); - - let chunks = chip.chunks(16); - assert_eq!(chunks.len(), 1); - for chunk in chunks { - assert_eq!(chunk.len(), 4); - } - } - - - #[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 - 1) { - 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"); - } - - #[test] - fn address_data_set_reads() { - let mut x = At28C256::new(0x0000, 0x3fff, vec![0xea; SIZE_32KB]); - - // set both... - x.set_address_bus(0x3000); - x.set_data_bus(0xab); - assert_eq!(0xab, x.data_bus()); - assert_eq!(0x3000, x.address_bus()); - - // ...set address... - x.set_address_bus(0x2000); - assert_eq!(0x2000, x.address_bus()); - assert_eq!(0xab, x.data_bus()); - - // ...set data. - x.set_data_bus(0xba); - assert_eq!(0xba, x.data_bus()); - assert_eq!(0x2000, x.address_bus()); - } - - fn programming_chip_changes_contents() { - let mut chip = At28C256::new(0x0000, 0x3fff, vec![]); - - assert_eq!(0x00, chip.read(&0x0000)); - - let new_data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - chip.program(new_data.into()); - assert_eq!(0xff, chip.read(&0x0000)); - assert_eq!(0x00, chip.read(&0x05)); - } - - #[test] - fn correct_flags_required() { - let mut chip = At28C256::new(0x0000, 0x3fff, vec![0xff]); - assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, true)); - assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, true, false)); - assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, false)); - assert_eq!(0xff, chip.signal_tick(0x0000, 0xab, true, true, true)); - assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, false, true)); - } - -} diff --git a/core/src/periph/hm62256/bus_device.rs b/core/src/periph/hm62256/bus_device.rs new file mode 100644 index 0000000..2e732de --- /dev/null +++ b/core/src/periph/hm62256/bus_device.rs @@ -0,0 +1,29 @@ +use crate::periph::hm62256::Hm62256; +use crate::traits::bus_device::BusDevice; + +impl BusDevice for Hm62256 { + fn min_offset(&self) -> u16 { + self.offset + } + + fn max_offset(&self) -> u16 { + self.max_offset + } + + fn address_bus(&self) -> u16 { + self.address_bus + } + + fn data_bus(&self) -> u8 { + self.data_bus + } + + 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 + } + +} diff --git a/core/src/periph/hm62256/memory_chip.rs b/core/src/periph/hm62256/memory_chip.rs new file mode 100644 index 0000000..75c738b --- /dev/null +++ b/core/src/periph/hm62256/memory_chip.rs @@ -0,0 +1,11 @@ +use crate::constants::constants_system::SIZE_32KB; +use crate::periph::hm62256::Hm62256; +use crate::traits::memory_chip::MemoryChip; + +impl MemoryChip 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] + } +} diff --git a/core/src/periph/hm62256/mod.rs b/core/src/periph/hm62256/mod.rs index 2661f6e..0dcac58 100644 --- a/core/src/periph/hm62256/mod.rs +++ b/core/src/periph/hm62256/mod.rs @@ -7,6 +7,8 @@ pub mod default; pub mod new; pub mod dump; mod control; +mod bus_device; +mod memory_chip; use crate::constants::constants_system::SIZE_32KB; use crate::traits::ram_chip::RamChip; @@ -29,51 +31,3 @@ pub struct Hm62256 { // Output Enable pub(crate) oe: bool } - -#[cfg(test)] -mod test { - use super::*; - use rand::random; - use crate::traits::memory_chip::MemoryChip; - - #[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); - } - } -} diff --git a/core/src/periph/hm62256/ramchip.rs b/core/src/periph/hm62256/ramchip.rs index 602dbb4..b6bda83 100644 --- a/core/src/periph/hm62256/ramchip.rs +++ b/core/src/periph/hm62256/ramchip.rs @@ -3,33 +3,6 @@ use crate::periph::hm62256::Hm62256; use crate::traits::bus_device::BusDevice; use crate::traits::ram_chip::RamChip; -impl BusDevice for Hm62256 { - fn min_offset(&self) -> u16 { - self.offset - } - - fn max_offset(&self) -> u16 { - self.max_offset - } - - fn address_bus(&self) -> u16 { - self.address_bus - } - - fn data_bus(&self) -> u8 { - self.data_bus - } - - 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 RamChip for Hm62256 { fn write(&mut self, offset: &u16, value: &u8) { let effective = *offset as i32 % SIZE_32KB as i32; diff --git a/core/src/periph/hm62256/romchip.rs b/core/src/periph/hm62256/romchip.rs index 0abc6ea..7254b04 100644 --- a/core/src/periph/hm62256/romchip.rs +++ b/core/src/periph/hm62256/romchip.rs @@ -4,13 +4,6 @@ use crate::periph::hm62256::Hm62256; use crate::traits::memory_chip::MemoryChip; use crate::traits::rom_chip::RomChip; -impl MemoryChip 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] - } -} impl RomChip for Hm62256 { fn program(_: &[u8]) -> Box { diff --git a/core/src/periph/hm62256/tick.rs b/core/src/periph/hm62256/tick.rs index 5c3d1a0..a61e7f7 100644 --- a/core/src/periph/hm62256/tick.rs +++ b/core/src/periph/hm62256/tick.rs @@ -39,23 +39,3 @@ impl Hm62256 { (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); - } -} diff --git a/core/src/periph/kim1_keypad/mod.rs b/core/src/periph/kim1_keypad/mod.rs index d942e3c..50c0e36 100644 --- a/core/src/periph/kim1_keypad/mod.rs +++ b/core/src/periph/kim1_keypad/mod.rs @@ -11,8 +11,8 @@ */ pub struct Kim1Keypad { - keys: [bool; 23], - stepping: bool + pub keys: [bool; 23], + pub stepping: bool } impl Kim1Keypad { @@ -52,54 +52,3 @@ impl Kim1Keypad { 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)); - } -} diff --git a/core/src/periph/mod.rs b/core/src/periph/mod.rs index f83a541..bb62fa2 100644 --- a/core/src/periph/mod.rs +++ b/core/src/periph/mod.rs @@ -1,6 +1,13 @@ -pub mod at28c256; -pub mod hm62256; -pub mod mos6520; -pub mod mos6522; -pub mod mos6530; pub mod kim1_keypad; + +pub mod at28c256; // EEPROM with 32KB/256Kbit +pub mod hm62256; // RAM with 32KBit/256Kbit + +pub mod mos6520; // PIA +pub mod mos6522; // PIA +pub mod mos6530; // RRIOT ROM, RAM, IO, Timer +pub mod mos6532; // PIA RAM, IO, Timer +pub mod mos6526; +mod mos6540; +mod mos6111; +// CIA Parallel, Serial, Interval Timers, Time Of Day diff --git a/core/src/periph/mos6111/mod.rs b/core/src/periph/mos6111/mod.rs new file mode 100644 index 0000000..17913a6 --- /dev/null +++ b/core/src/periph/mos6111/mod.rs @@ -0,0 +1,50 @@ +use crate::traits::bus_device::BusDevice; +use crate::traits::memory_chip::MemoryChip; + +// MOS6111 +// AKA MCS6111 +// 128B/1024b RAM +pub struct Mos6111 { + data: Box<[u8]>, + offset: u16, + max_offset: u16 +} + +impl Mos6111 { + pub fn new(min_offset: u16, max_offset: u16) -> Self { + Mos6111 { + data: Box::new([0x00u8; 64]), + offset: min_offset, + max_offset + } + } +} + +impl BusDevice for Mos6111 { + fn min_offset(&self) -> u16 { + self.offset + } + + fn max_offset(&self) -> u16 { + self.max_offset + } + + fn data_bus(&self) -> u8 { + todo!() + } + + fn set_address_bus(&mut self, new_value: u16) { + todo!() + } + + fn set_data_bus(&mut self, new_value: u8) { + todo!() + } +} + +impl MemoryChip for Mos6111 { + fn read(&self, offset: &u16) -> u8 { + println!("IA = {}", self.internal_address(*offset)); + 0x00 + } +} \ No newline at end of file diff --git a/core/src/periph/mos6520/memory_chip.rs b/core/src/periph/mos6520/memory_chip.rs new file mode 100644 index 0000000..44072a3 --- /dev/null +++ b/core/src/periph/mos6520/memory_chip.rs @@ -0,0 +1,10 @@ +use log::debug; +use crate::periph::mos6520::Mos6520; +use crate::traits::memory_chip::MemoryChip; + +impl MemoryChip for Mos6520 { + fn read(&self, offset: &u16) -> u8 { + debug!("This has no ROM ${offset:04x}"); + 0 + } +} diff --git a/core/src/periph/mos6520/mod.rs b/core/src/periph/mos6520/mod.rs index 04a4333..12c9811 100644 --- a/core/src/periph/mos6520/mod.rs +++ b/core/src/periph/mos6520/mod.rs @@ -1,14 +1,17 @@ pub mod reset; pub mod bus_device; -pub mod via_chip; pub mod new; pub mod tick; +pub mod rom_chip; +pub mod ram_chip; +pub mod memory_chip; +pub mod pia_chip; use crate::traits::bus_device::BusDevice; +use crate::traits::gpio_chip::GpioChip; use crate::traits::memory_chip::MemoryChip; use crate::traits::ram_chip::RamChip; use crate::traits::rom_chip::RomChip; -use crate::traits::via_chip::ViaChip; /* The MCS6520 Peripheral Adapter is designed to solve a broad range of peripheral @@ -26,22 +29,22 @@ be tween the processor and a peripheral device. */ pub struct Mos6520 { // Parallel ports A & B data registers - port_a: u8, - out_a: u8, - in_a: u8, - ddra: u8, - port_b: u8, - out_b: u8, - in_b: u8, - ddrb: u8, + pub port_a: u8, + pub out_a: u8, + pub in_a: u8, + pub ddra: u8, + pub port_b: u8, + pub out_b: u8, + pub in_b: u8, + pub ddrb: u8, // Interrupt control register - icr: u8, + pub icr: u8, // External Address and Data Bus - address_bus: u16, - data_bus: u8, + pub address_bus: u16, + pub data_bus: u8, // Offsets - offset: u16, - max_offset: u16 + pub offset: u16, + pub max_offset: u16 } impl Mos6520 { diff --git a/core/src/periph/mos6520/pia_chip.rs b/core/src/periph/mos6520/pia_chip.rs new file mode 100644 index 0000000..0421325 --- /dev/null +++ b/core/src/periph/mos6520/pia_chip.rs @@ -0,0 +1,101 @@ +use crate::periph::mos6520::Mos6520; +use crate::traits::gpio_chip::{GpioChip, GpioChipPorts}; +use crate::traits::pia_chip::PiaChip; + +impl GpioChip for Mos6520 { + fn set_port(&mut self, new_value: u8, port: GpioChipPorts) { + match port { + GpioChipPorts::PORT_A => { + self.port_a = new_value + } + GpioChipPorts::PORT_B => { + self.port_b = new_value + } + } + } + + fn set_ddr(&mut self, new_ddr_value: u8, port: GpioChipPorts) { + match port { + GpioChipPorts::PORT_A => { + self.ddra = new_ddr_value + } + GpioChipPorts::PORT_B => { + self.ddrb = new_ddr_value + } + } + + } + + fn get_port(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.port_a + } + GpioChipPorts::PORT_B => { + self.port_b + } + } + } + + fn get_ddr(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.ddra + } + GpioChipPorts::PORT_B => { + self.ddrb + } + } + } + + fn get_in(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.in_a + } + GpioChipPorts::PORT_B => { + self.in_b + } + } + } + + fn get_out(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.port_a + } + GpioChipPorts::PORT_B => { + self.port_b + } + } + } + + fn update_ports(&mut self) { + let (in_a, out_a) = Mos6520::split_port(self.port_a, self.ddra); + self.in_a = in_a; + self.out_a = out_a; + + // Port B + let (in_b, out_b) = Mos6520::split_port(self.port_b, self.ddrb); + self.in_b = in_b; + self.out_b = out_b; + } +} + +impl PiaChip for Mos6520 { } + +#[cfg(test)] +mod test { + use super::*; + + + + #[test] + fn check_gpio_ports() { + let mut x = Mos6520::new(0x1000); + + // offsets from 0x1000 -> 0x1004 + + + } +} \ No newline at end of file diff --git a/core/src/periph/mos6520/ram_chip.rs b/core/src/periph/mos6520/ram_chip.rs new file mode 100644 index 0000000..1bc2926 --- /dev/null +++ b/core/src/periph/mos6520/ram_chip.rs @@ -0,0 +1,9 @@ +use log::debug; +use crate::periph::mos6520::Mos6520; +use crate::traits::ram_chip::RamChip; + +impl RamChip for Mos6520 { + fn write(&mut self, offset: &u16, value: &u8) { + debug!("This has no ROM / ${offset:04x} ${value:02x}"); + } +} diff --git a/core/src/periph/mos6520/reset.rs b/core/src/periph/mos6520/reset.rs index e8379a3..d720caa 100644 --- a/core/src/periph/mos6520/reset.rs +++ b/core/src/periph/mos6520/reset.rs @@ -10,5 +10,7 @@ impl Mos6520 { self.ddra = 0x00; self.ddrb = 0x00; self.icr = 0x00; + self.address_bus = 0x0000; + self.data_bus = 0x00; } } diff --git a/core/src/periph/mos6520/rom_chip.rs b/core/src/periph/mos6520/rom_chip.rs new file mode 100644 index 0000000..9b06661 --- /dev/null +++ b/core/src/periph/mos6520/rom_chip.rs @@ -0,0 +1,10 @@ +use log::debug; +use crate::periph::mos6520::Mos6520; +use crate::traits::rom_chip::RomChip; + +impl RomChip for Mos6520 { + fn program(new_data: &[u8]) -> Box { + debug!("This has no rom. Cant program."); + todo!() + } +} diff --git a/core/src/periph/mos6520/tick.rs b/core/src/periph/mos6520/tick.rs index 466038d..d521e1b 100644 --- a/core/src/periph/mos6520/tick.rs +++ b/core/src/periph/mos6520/tick.rs @@ -2,6 +2,11 @@ use log::debug; use crate::periph::mos6520::Mos6520; use crate::traits::bus_device::BusDevice; +const MOS6520_DDRA_OFFSET: u8 = 0x00; +const MOS6520_DDRB_OFFSET: u8 = 0x02; +const MOS6520_PRTA_OFFSET: u8 = 0x01; +const MOS6520_PRTB_OFFSET: u8 = 0x03; + impl Mos6520 { pub fn tick(&mut self, address_bus: u16, data_bus: u8) -> u8 { self.address_bus = address_bus; @@ -10,25 +15,25 @@ impl Mos6520 { println!("Preparing to tick MOS6520"); // are we changing our state? - let effective = self.internal_address(self.address_bus); + let effective = self.internal_address(self.address_bus) as u8; match effective { - 0x00 => { + MOS6520_DDRA_OFFSET => { // ddra // println!("DDRA -> {:02x} -> {:02x}", self.ddra, self.data_bus); self.ddra = self.data_bus; } - 0x01 => { + MOS6520_PRTA_OFFSET => { // port a // println!("PORT A -> {:02x} -> {:02x}", self.port_a, self.data_bus); self.port_a = self.data_bus; } - 0x02 => { + MOS6520_DDRB_OFFSET => { // ddrb // println!("DDRB -> {:02x}", self.ddrb); self.ddrb = self.data_bus; } - 0x03 => { + MOS6520_PRTB_OFFSET => { // port b // println!("PORT B -> {:02x}", self.port_b); self.port_b = self.data_bus; diff --git a/core/src/periph/mos6520/via_chip.rs b/core/src/periph/mos6520/via_chip.rs deleted file mode 100644 index f7462bd..0000000 --- a/core/src/periph/mos6520/via_chip.rs +++ /dev/null @@ -1,126 +0,0 @@ -use log::debug; -use crate::periph::mos6520::Mos6520; -use crate::traits::memory_chip::MemoryChip; -use crate::traits::ram_chip::RamChip; -use crate::traits::rom_chip::RomChip; -use crate::traits::via_chip::ViaChip; - -impl RomChip for Mos6520 { - fn program(new_data: &[u8]) -> Box { - debug!("This has no rom. Cant program."); - todo!() - } -} - -impl RamChip for Mos6520 { - fn write(&mut self, offset: &u16, value: &u8) { - debug!("This has no ROM / ${offset:04x} ${value:02x}"); - } -} - -impl MemoryChip for Mos6520 { - fn read(&self, offset: &u16) -> u8 { - debug!("This has no ROM ${offset:04x}"); - 0 - } -} - -impl ViaChip for Mos6520 { - fn set_port_ddr(&mut self, port_index: u8, value: u8) { - match port_index { - 1 => { - self.ddra = value; - }, - 2 => { - self.ddrb = value; - } - _ => { - debug!("Invalid Port"); - } - } - - self.update_ports(); - } - - fn set_port_data(&mut self, port_index: u8, value: u8) { - match port_index { - 1 => { - self.port_a = value; - }, - 2 => { - self.port_b = value; - } - _ => { - debug!("Invalid Port"); - } - } - self.update_ports(); - } -} - -impl Mos6520 { - fn update_ports(&mut self) { - debug!("PortA: DDR {:08b}", self.port_a); - debug!("PortB: DDR {:08b}", self.port_b); - } -} - -#[cfg(test)] -mod test { - use crate::periph::mos6520::Mos6520; - - const DDRA_OFFSET: u8 = 0x00; - const PORTA_OFFSET: u8 = 0x01; - const DDRB_OFFSET: u8 = 0x02; - const PORTB_OFFSET: u8 = 0x03; - - fn actual(base: u16, offset: u8) -> u16 { - base + offset as u16 - } - - #[test] - fn ddrb_tests() { - let mut x = Mos6520::new(0x1000); - let params = vec![ - // Offset data outa ina ddra porta - (DDRB_OFFSET, 0xff, 0x00, 0x00, 0xff, 0x00), - (PORTB_OFFSET, 0xff, 0xff, 0x00, 0xff, 0xff), - (DDRB_OFFSET, 0xaa, 0xaa, 0x55, 0xaa, 0xff), - (DDRB_OFFSET, 0x55, 0x55, 0xaa, 0x55, 0xff), - (PORTB_OFFSET, 0xf0, 0x50, 0xa0, 0x55, 0xf0), - (PORTB_OFFSET, 0x0f, 0x05, 0x0a, 0x55, 0x0f), - (DDRB_OFFSET, 0xff, 0x0f, 0x00, 0xff, 0x0f) - ]; - - for (offset, data, outb, inb, ddrb, portb) in params { - x.tick(actual(x.offset, offset), data); - assert_eq!(outb, x.out_b); - assert_eq!(inb, x.in_b); - assert_eq!(ddrb, x.ddrb); - assert_eq!(portb, x.port_b); - } - } - - #[test] - fn ddra_tests() { - let mut x = Mos6520::new(0x1000); - let params = vec![ - // Offset data outa ina ddra porta - (DDRA_OFFSET, 0xff, 0x00, 0x00, 0xff, 0x00), - (PORTA_OFFSET, 0xff, 0xff, 0x00, 0xff, 0xff), - (DDRA_OFFSET, 0xaa, 0xaa, 0x55, 0xaa, 0xff), - (DDRA_OFFSET, 0x55, 0x55, 0xaa, 0x55, 0xff), - (PORTA_OFFSET, 0xf0, 0x50, 0xa0, 0x55, 0xf0), - (PORTA_OFFSET, 0x0f, 0x05, 0x0a, 0x55, 0x0f), - (DDRA_OFFSET, 0xff, 0x0f, 0x00, 0xff, 0x0f) - ]; - - for (offset, data, outa, ina, ddra, porta) in params { - x.tick(actual(x.offset, offset), data); - assert_eq!(outa, x.out_a); - assert_eq!(ina, x.in_a); - assert_eq!(ddra, x.ddra); - assert_eq!(porta, x.port_a); - } - } -} diff --git a/core/src/periph/mos6522/mod.rs b/core/src/periph/mos6522/mod.rs index 205335f..a6d1f68 100644 --- a/core/src/periph/mos6522/mod.rs +++ b/core/src/periph/mos6522/mod.rs @@ -1,4 +1,47 @@ pub mod mos6522; -mod registers; -mod new; -mod tick; \ No newline at end of file +pub mod registers; +pub mod new; +pub mod tick; + +#[derive(Default)] +pub struct Mos6522 { + /// data direction + pub dda: u8, + pub ddb: u8, + + /// bottom 4 address bits + pub rs0: u8, + pub rs1: u8, + pub rs2: u8, + pub rs3: u8, + + /// external data bus + pub data_bus: u8, + + pub cs1: bool, + pub cs2: bool, + // when true CPU is reading + pub rw: bool, + + /// reset circuit - true when reset inited + pub reset: bool, + + /// IRQ - true when interrupt waiting + pub irq: bool, + + pub ira: u8, + pub ora: u8, + pub porta: u8, + pub irb: u8, + pub orb: u8, + pub portb: u8, + + pub ca1: bool, + pub ca2: bool, + pub cb1: bool, + pub cb2: bool, + + // memory offset for where in the computers memory map this fits + pub offset: u16, + pub address_bus: u16, +} diff --git a/core/src/periph/mos6522/mos6522.rs b/core/src/periph/mos6522/mos6522.rs index 1c9c8b6..0f51bcd 100644 --- a/core/src/periph/mos6522/mos6522.rs +++ b/core/src/periph/mos6522/mos6522.rs @@ -1,49 +1,6 @@ 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, -} +use crate::periph::mos6522::Mos6522; impl Mos6522 { pub fn max_offset(&self) -> u16 { @@ -59,51 +16,3 @@ impl Mos6522 { } } } - -#[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); - } -} diff --git a/core/src/periph/mos6522/new.rs b/core/src/periph/mos6522/new.rs index 73a9bf8..04b09b1 100644 --- a/core/src/periph/mos6522/new.rs +++ b/core/src/periph/mos6522/new.rs @@ -1,7 +1,32 @@ -use crate::periph::mos6522::mos6522::Mos6522; +use crate::periph::mos6522::Mos6522; impl Mos6522 { - pub fn new() -> Self { - Mos6522::default() + pub fn new(offset: u16) -> Self { + Mos6522 { + dda: 0, + ddb: 0, + rs0: 0, + rs1: 0, + rs2: 0, + rs3: 0, + data_bus: 0, + cs1: false, + cs2: false, + rw: false, + reset: false, + irq: false, + ira: 0, + ora: 0, + porta: 0, + irb: 0, + orb: 0, + portb: 0, + ca1: false, + ca2: false, + cb1: false, + cb2: false, + offset, + address_bus: 0, + } } } \ No newline at end of file diff --git a/core/src/periph/mos6522/tick.rs b/core/src/periph/mos6522/tick.rs index 9198372..ea4e33f 100644 --- a/core/src/periph/mos6522/tick.rs +++ b/core/src/periph/mos6522/tick.rs @@ -1,18 +1,16 @@ 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; +use crate::periph::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())) { + if !(address_bus >= self.offset && address_bus.le(&self.max_offset())) { return (address_bus, data_bus); } diff --git a/core/src/periph/mos6526/backplane.rs b/core/src/periph/mos6526/backplane.rs new file mode 100644 index 0000000..b2271ef --- /dev/null +++ b/core/src/periph/mos6526/backplane.rs @@ -0,0 +1,44 @@ +use crate::periph::mos6526::Mos6526; +use crate::traits::backplane::Backplane; + +impl Backplane for Mos6526 { + fn data_bus(&self) -> u8 { + todo!() + } + + fn address_bus(&self) -> u16 { + todo!() + } + + fn read_mode(&self) -> bool { + todo!() + } + + fn set_read_mode(&mut self, new_mode: bool) { + todo!() + } + + fn set_data_bus(&mut self, new_value: u8) { + todo!() + } + + fn set_address_bus(&mut self, new_value: u16) { + todo!() + } + + fn tick(&mut self) { + todo!() + } + + fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 { + todo!() + } + + fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 { + todo!() + } + + fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) { + todo!() + } +} \ No newline at end of file diff --git a/core/src/periph/mos6526/mod.rs b/core/src/periph/mos6526/mod.rs new file mode 100644 index 0000000..49725ad --- /dev/null +++ b/core/src/periph/mos6526/mod.rs @@ -0,0 +1,4 @@ +mod backplane; + + +pub struct Mos6526 {} \ No newline at end of file diff --git a/core/src/periph/mos6530/bus_device.rs b/core/src/periph/mos6530/bus_device.rs new file mode 100644 index 0000000..f7ddc6d --- /dev/null +++ b/core/src/periph/mos6530/bus_device.rs @@ -0,0 +1,30 @@ +use crate::constants::constants_system::SIZE_1KB; +use crate::periph::mos6530::Mos6530; +use crate::traits::bus_device::BusDevice; + +impl BusDevice for Mos6530 { + fn min_offset(&self) -> u16 { + self.ram_offset + } + + fn max_offset(&self) -> u16 { + self.min_offset() + SIZE_1KB as u16 + } + + fn address_bus(&self) -> u16 { + self.address_bus + } + + fn data_bus(&self) -> u8 { + self.data_bus + } + + 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 + } + +} diff --git a/core/src/periph/mos6530/dump.rs b/core/src/periph/mos6530/dump.rs index f68f8e4..2996ba2 100644 --- a/core/src/periph/mos6530/dump.rs +++ b/core/src/periph/mos6530/dump.rs @@ -1,4 +1,4 @@ -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6530::Mos6530; impl Mos6530 { pub fn dump(&self) { diff --git a/core/src/periph/mos6530/memory_chip.rs b/core/src/periph/mos6530/memory_chip.rs new file mode 100644 index 0000000..e92d63b --- /dev/null +++ b/core/src/periph/mos6530/memory_chip.rs @@ -0,0 +1,10 @@ +use log::debug; +use crate::periph::mos6530::Mos6530; +use crate::traits::memory_chip::MemoryChip; + +impl MemoryChip for Mos6530 { + fn read(&self, offset: &u16) -> u8 { + debug!("🐙 Reading from ${offset:04x}"); + 0 + } +} diff --git a/core/src/periph/mos6530/mod.rs b/core/src/periph/mos6530/mod.rs index d14e5bb..626811f 100644 --- a/core/src/periph/mos6530/mod.rs +++ b/core/src/periph/mos6530/mod.rs @@ -1,5 +1,45 @@ -pub mod mos6530; +use crate::constants::constants_system::SIZE_1KB; + pub mod tick; pub mod new; pub mod dump; -pub mod viachip; \ No newline at end of file +pub mod viachip; +pub mod bus_device; +pub mod ram_chip; +pub mod rom_chip; +pub mod memory_chip; + +/// 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 data: [u8; SIZE_1KB], + pub ram: [u8; 64], + pub port_a: u8, + pub ddra: u8, + pub in_a: u8, + pub out_a: u8, + pub port_b: u8, + pub ddrb: u8, + pub in_b: u8, + pub out_b: u8, + pub data_bus: u8, + pub address_bus: u16, + pub cs1: bool, + pub cs2: bool, + // when true, CPU is reading + pub rw: bool, + pub reset: bool, + pub io_offset: u16, + pub ram_offset: u16, + pub rom_offset: u16 +} + diff --git a/core/src/periph/mos6530/mos6530.rs b/core/src/periph/mos6530/mos6530.rs deleted file mode 100644 index 349552a..0000000 --- a/core/src/periph/mos6530/mos6530.rs +++ /dev/null @@ -1,31 +0,0 @@ -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 data: [u8; SIZE_1KB], - pub ram: [u8; 64], - pub porta: u8, - pub portb: u8, - pub data_bus: u8, - pub address_bus: u16, - pub cs1: bool, - pub cs2: bool, - // when true, CPU is reading - pub rw: bool, - pub reset: bool, - pub io_offset: u16, - pub ram_offset: u16, - pub rom_offset: u16 -} - diff --git a/core/src/periph/mos6530/new.rs b/core/src/periph/mos6530/new.rs index 3123cc6..f8ef4a0 100644 --- a/core/src/periph/mos6530/new.rs +++ b/core/src/periph/mos6530/new.rs @@ -1,5 +1,5 @@ use crate::constants::constants_system::SIZE_1KB; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6530::Mos6530; impl Mos6530 { pub fn new(io_offset: u16, @@ -9,8 +9,14 @@ impl Mos6530 { Mos6530 { data: *data, ram: [0x00; 64], - porta: 0, - portb: 0, + port_a: 0, + ddra: 0, + in_a: 0, + out_a: 0, + port_b: 0, + ddrb: 0, + in_b: 0, + out_b: 0, data_bus: 0, address_bus: 0, cs1: false, diff --git a/core/src/periph/mos6530/ram_chip.rs b/core/src/periph/mos6530/ram_chip.rs new file mode 100644 index 0000000..b313d05 --- /dev/null +++ b/core/src/periph/mos6530/ram_chip.rs @@ -0,0 +1,9 @@ +use log::debug; +use crate::periph::mos6530::Mos6530; +use crate::traits::ram_chip::RamChip; + +impl RamChip for Mos6530 { + fn write(&mut self, offset: &u16, value: &u8) { + debug!("🐙 Writing ${value:02x} to ${offset:04x}"); + } +} diff --git a/core/src/periph/mos6530/rom_chip.rs b/core/src/periph/mos6530/rom_chip.rs new file mode 100644 index 0000000..aebc867 --- /dev/null +++ b/core/src/periph/mos6530/rom_chip.rs @@ -0,0 +1,30 @@ +use log::debug; +use crate::periph::mos6530::Mos6530; +use crate::traits::rom_chip::RomChip; + +impl RomChip for Mos6530 { + fn program(new_data: &[u8]) -> Box { + debug!("🐙 programming {}b to ROM", new_data.len()); + Box::new(Mos6530 { + data: new_data.to_vec().try_into().unwrap(), + ram: [0x00; 64], + port_a: 0, + ddra: 0, + in_a: 0, + out_a: 0, + port_b: 0, + ddrb: 0, + in_b: 0, + out_b: 0, + data_bus: 0, + address_bus: 0, + cs1: false, + cs2: false, + rw: false, + reset: false, + io_offset: 0, + ram_offset: 0, + rom_offset: 0, + }) + } +} \ No newline at end of file diff --git a/core/src/periph/mos6530/tick.rs b/core/src/periph/mos6530/tick.rs index c601bd7..de36cf4 100644 --- a/core/src/periph/mos6530/tick.rs +++ b/core/src/periph/mos6530/tick.rs @@ -1,5 +1,5 @@ use log::debug; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6530::Mos6530; impl Mos6530 { pub fn tick(&mut self, address_bus: u16, data_bus: u8, reset: bool, rw: bool) { diff --git a/core/src/periph/mos6530/viachip.rs b/core/src/periph/mos6530/viachip.rs index b316838..b4a621a 100644 --- a/core/src/periph/mos6530/viachip.rs +++ b/core/src/periph/mos6530/viachip.rs @@ -1,79 +1,93 @@ use log::debug; use crate::constants::constants_system::{SIZE_1KB, SIZE_32KB}; -use crate::periph::mos6530::mos6530::Mos6530; +use crate::periph::mos6520::Mos6520; +use crate::periph::mos6530::Mos6530; use crate::traits::bus_device::BusDevice; +use crate::traits::gpio_chip::{GpioChip, GpioChipPorts}; use crate::traits::memory_chip::MemoryChip; +use crate::traits::pia_chip::PiaChip; use crate::traits::ram_chip::RamChip; use crate::traits::rom_chip::RomChip; -use crate::traits::via_chip::ViaChip; -impl BusDevice for Mos6530 { - fn min_offset(&self) -> u16 { - self.ram_offset +impl GpioChip for Mos6530 { + fn set_port(&mut self, new_value: u8, port: GpioChipPorts) { + match port { + GpioChipPorts::PORT_A => { + self.port_a = new_value + } + GpioChipPorts::PORT_B => { + self.port_b = new_value + } + } } - fn max_offset(&self) -> u16 { - self.min_offset() + SIZE_1KB as u16 + fn set_ddr(&mut self, new_ddr_value: u8, port: GpioChipPorts) { + match port { + GpioChipPorts::PORT_A => { + self.ddra = new_ddr_value + } + GpioChipPorts::PORT_B => { + self.ddrb = new_ddr_value + } + } + } - fn address_bus(&self) -> u16 { - self.address_bus + fn get_port(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.port_a + } + GpioChipPorts::PORT_B => { + self.port_b + } + } } - fn data_bus(&self) -> u8 { - self.data_bus + fn get_ddr(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.ddra + } + GpioChipPorts::PORT_B => { + self.ddrb + } + } } - fn set_address_bus(&mut self, new_value: u16) { - self.address_bus = new_value + fn get_in(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.in_a + } + GpioChipPorts::PORT_B => { + self.in_b + } + } } - fn set_data_bus(&mut self, new_value: u8) { - self.data_bus = new_value + fn get_out(&self, port: GpioChipPorts) -> u8 { + match port { + GpioChipPorts::PORT_A => { + self.port_a + } + GpioChipPorts::PORT_B => { + self.port_b + } + } } -} + fn update_ports(&mut self) { + let (in_a, out_a) = Mos6520::split_port(self.port_a, self.ddra); + self.in_a = in_a; + self.out_a = out_a; -impl RamChip for Mos6530 { - fn write(&mut self, offset: &u16, value: &u8) { - debug!("🐙 Writing ${value:02x} to ${offset:04x}"); + // Port B + let (in_b, out_b) = Mos6520::split_port(self.port_b, self.ddrb); + self.in_b = in_b; + self.out_b = out_b; } } -impl MemoryChip for Mos6530 { - fn read(&self, offset: &u16) -> u8 { - debug!("🐙 Reading from ${offset:04x}"); - 0 - } -} - -impl RomChip for Mos6530 { - fn program(new_data: &[u8]) -> Box { - debug!("🐙 programming {}b to ROM", new_data.len()); - Box::new(Mos6530 { - data: new_data.to_vec().try_into().unwrap(), - ram: [0x00; 64], - porta: 0, - portb: 0, - data_bus: 0, - address_bus: 0, - cs1: false, - cs2: false, - rw: false, - reset: false, - io_offset: 0, - ram_offset: 0, - rom_offset: 0, - }) - } -} - -impl ViaChip for Mos6530 { - fn set_port_ddr(&mut self, port_index: u8, value: u8) { - debug!("🐙Setting DDR{port_index} to {value:02x}"); - } - - fn set_port_data(&mut self, port_index: u8, value: u8) { - debug!("🐙Setting PORT{port_index} to {value:02x}"); - } +impl PiaChip for Mos6530 { } diff --git a/core/src/periph/mos6532/bus_device.rs b/core/src/periph/mos6532/bus_device.rs new file mode 100644 index 0000000..b0fbf96 --- /dev/null +++ b/core/src/periph/mos6532/bus_device.rs @@ -0,0 +1,26 @@ +use crate::periph::mos6532::Mos6532; +use crate::traits::bus_device::BusDevice; + +impl BusDevice for Mos6532 { + fn min_offset(&self) -> u16 { + self.ram_offset.min(self.io_offset) + } + + fn max_offset(&self) -> u16 { self.min_offset() + 0x40 } + + fn address_bus(&self) -> u16 { + self.address_bus + } + + fn data_bus(&self) -> u8 { + self.data_bus + } + + 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 + } +} \ No newline at end of file diff --git a/core/src/periph/mos6532/mod.rs b/core/src/periph/mos6532/mod.rs new file mode 100644 index 0000000..5ff8238 --- /dev/null +++ b/core/src/periph/mos6532/mod.rs @@ -0,0 +1,67 @@ +mod bus_device; +mod new; + +use log::debug; +use crate::traits::backplane::Backplane; +use crate::traits::bus_device::BusDevice; + +pub struct Mos6532 { + // R + ram: Box<[u8; 64]>, + // IO + ddra: u8, + porta: u8, + in_a: u8, + out_a: u8, + ddrb: u8, + portb: u8, + in_b: u8, + out_b: u8, + // T + + // Generic 6502 stuff + read_mode: bool, + data_bus: u8, + address_bus: u16, + + // Part Specific + ram_offset: u16, + io_offset: u16, +} + +const MOS6532_DDRA_OFFSET: u8 = 0x00; +const MOS6532_DDRB_OFFSET: u8 = 0x02; +const MOS6532_PRTA_OFFSET: u8 = 0x01; +const MOS6532_PRTB_OFFSET: u8 = 0x03; + +impl Mos6532 { + fn targets_ram(&self, offset: u16) -> bool { + // we are at or above the offset AND we are under the end of ram + offset >= self.ram_offset && offset <= self.ram_offset + 64 + } + + fn targets_io(&self, offset: u16) -> bool { + offset >= self.io_offset && offset <= self.io_offset + 4 + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn targetting_parts() { + // RAM from 0x0000 -> 0x0040 + // IO from 0x1000 -> 0x1004 + let via = Mos6532::new(0x0000, 0x1000); + + assert!(via.targets_io(0x1000)); + assert!(!via.targets_ram(0x1000)); + + assert!(!via.targets_io(0x0000)); + assert!(via.targets_ram(0x0000)); + + assert!(!via.targets_io(0x4000)); + assert!(!via.targets_ram(0x4000)); + } +} diff --git a/core/src/periph/mos6532/new.rs b/core/src/periph/mos6532/new.rs new file mode 100644 index 0000000..6950baf --- /dev/null +++ b/core/src/periph/mos6532/new.rs @@ -0,0 +1,22 @@ +use crate::periph::mos6532::Mos6532; + +impl Mos6532 { + pub fn new(ram_offset: u16, io_offset: u16) -> Self { + Mos6532 { + ram: Box::new([0x00; 64]), + ddra: 0, + porta: 0, + in_a: 0, + out_a: 0, + ddrb: 0, + portb: 0, + in_b: 0, + out_b: 0, + read_mode: true, + data_bus: 0x00, + address_bus: 0x0000, + ram_offset, + io_offset + } + } +} \ No newline at end of file diff --git a/core/src/periph/mos6540/memory_chip.rs b/core/src/periph/mos6540/memory_chip.rs new file mode 100644 index 0000000..350c4e2 --- /dev/null +++ b/core/src/periph/mos6540/memory_chip.rs @@ -0,0 +1,35 @@ +use crate::periph::mos6540::Mos6540; +use crate::traits::bus_device::BusDevice; +use crate::traits::memory_chip::MemoryChip; + +impl BusDevice for Mos6540 { + fn min_offset(&self) -> u16 { + todo!() + } + + fn max_offset(&self) -> u16 { + todo!() + } + + fn address_bus(&self) -> u16 { + todo!() + } + + fn data_bus(&self) -> u8 { + todo!() + } + + fn set_address_bus(&mut self, new_value: u16) { + todo!() + } + + fn set_data_bus(&mut self, new_value: u8) { + todo!() + } +} + +impl MemoryChip for Mos6540 { + fn read(&self, offset: &u16) -> u8 { + todo!() + } +} \ No newline at end of file diff --git a/core/src/periph/mos6540/mod.rs b/core/src/periph/mos6540/mod.rs new file mode 100644 index 0000000..4447de4 --- /dev/null +++ b/core/src/periph/mos6540/mod.rs @@ -0,0 +1,13 @@ +// MOS6540 +// 2KByte 16kbit ROM + +mod memory_chip; + +struct Mos6540 { + data_bus: u8, + address_bus: u16, + pub data: Box, + offset: u16, + max_offset: u16 +} + diff --git a/core/src/traits/backplane.rs b/core/src/traits/backplane.rs index b8c6978..f0e9176 100644 --- a/core/src/traits/backplane.rs +++ b/core/src/traits/backplane.rs @@ -1,3 +1,5 @@ +use log::debug; + pub trait Backplane { fn data_bus(&self) -> u8; fn address_bus(&self) -> u16; @@ -31,5 +33,11 @@ pub trait Backplane { /// /// returns (data, irqa, irqb) fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool); -} + /// tick_tod + /// + /// Tick the TOD circutry + /// + /// returns (time_irq) + fn tick_tod(&mut self) -> bool; +} diff --git a/core/src/traits/bus_control_byte.rs b/core/src/traits/bus_control_byte.rs index d16952b..47e0533 100644 --- a/core/src/traits/bus_control_byte.rs +++ b/core/src/traits/bus_control_byte.rs @@ -7,4 +7,3 @@ use crate::traits::bus_device::BusDevice; pub trait BusControlByte { fn from_byte(source: u8) -> T; } - diff --git a/core/src/traits/bus_device.rs b/core/src/traits/bus_device.rs index 557b6c6..b73274d 100644 --- a/core/src/traits/bus_device.rs +++ b/core/src/traits/bus_device.rs @@ -2,10 +2,8 @@ pub trait BusDevice { fn min_offset(&self) -> u16; fn max_offset(&self) -> u16; - fn address_bus(&self) -> u16; fn data_bus(&self) -> u8; - fn set_address_bus(&mut self, new_value: u16); fn set_data_bus(&mut self, new_value: u8); fn talking_to_me(&self, address: u16) -> bool { address >= self.min_offset() && address < self.max_offset() diff --git a/core/src/traits/cia_chip.rs b/core/src/traits/cia_chip.rs new file mode 100644 index 0000000..38995a7 --- /dev/null +++ b/core/src/traits/cia_chip.rs @@ -0,0 +1,7 @@ +use crate::traits::bus_device::BusDevice; +use crate::traits::gpio_chip::GpioChip; +use crate::traits::timer_chip::TimerChip; + +pub trait CiaChip: BusDevice + GpioChip + TimerChip { + +} \ No newline at end of file diff --git a/core/src/traits/gpio_chip.rs b/core/src/traits/gpio_chip.rs new file mode 100644 index 0000000..b6688a0 --- /dev/null +++ b/core/src/traits/gpio_chip.rs @@ -0,0 +1,58 @@ +pub enum GpioChipPorts { + PORT_A, + PORT_B +} + +pub trait GpioChip { + fn set_port(&mut self, new_a_value: u8, port: GpioChipPorts); + fn set_ddr(&mut self, new_ddra_value: u8, port: GpioChipPorts); + fn get_port(&self, port: GpioChipPorts) -> u8; + fn get_ddr(&self, port: GpioChipPorts) -> u8; + fn get_in(&self, port: GpioChipPorts) -> u8; + fn get_out(&self, port: GpioChipPorts) -> u8; + fn update_ports(&mut self); + + fn split_port(value: u8, ddr: u8) -> (u8, u8) { + let mut in_val = 0x00; + let mut out_val = 0x00; + + for bit in 0..8 { + let mask = 1 << bit; + let is_output = (ddr & mask) != 0; + let is_set = (value & mask) != 0; + + match (is_output, is_set) { + (true, true) => out_val |= mask, + (false, true) => in_val |= mask, + _ => {} + } + } + (in_val, out_val) + } +} + + +#[cfg(test)] +mod test { + use crate::periph::mos6520::Mos6520; + use super::*; + + #[test] + fn test_split() { + let params = vec![ + (0xf0, 0xff, 0xf0, 0x0f), + (0x0f, 0xff, 0x0f, 0xf0), + (0xff, 0xff, 0xff, 0x00), + (0x00, 0xff, 0x00, 0xff), + (0x55, 0xff, 0x55, 0xaa) + ]; + + for (ddr, port, output, input) in params { + + let (calc_ina, calc_oua) = Mos6520::split_port(port, ddr); + + assert_eq!(calc_oua, output); + assert_eq!(calc_ina, input); + } + } +} \ No newline at end of file diff --git a/core/src/traits/mod.rs b/core/src/traits/mod.rs index 29b2941..c347627 100644 --- a/core/src/traits/mod.rs +++ b/core/src/traits/mod.rs @@ -3,6 +3,11 @@ pub mod bus_control_byte; pub mod memory_chip; pub mod rom_chip; pub mod ram_chip; -pub mod via_chip; +pub mod pia_chip; pub mod backplane; -mod timer_chip; +pub mod timer_chip; +pub mod serial_interface_chip; +pub mod parallel_interface_chip; +pub mod time_of_day_chip; +pub mod gpio_chip; +mod cia_chip; diff --git a/core/src/traits/parallel_interface_chip.rs b/core/src/traits/parallel_interface_chip.rs new file mode 100644 index 0000000..6a5db24 --- /dev/null +++ b/core/src/traits/parallel_interface_chip.rs @@ -0,0 +1 @@ +pub trait ParallelInterface {} \ No newline at end of file diff --git a/core/src/traits/pia_chip.rs b/core/src/traits/pia_chip.rs new file mode 100644 index 0000000..e2a32a2 --- /dev/null +++ b/core/src/traits/pia_chip.rs @@ -0,0 +1,7 @@ +use log::debug; +use crate::traits::bus_device::BusDevice; +use crate::traits::gpio_chip::{GpioChip, GpioChipPorts}; +use crate::traits::ram_chip::RamChip; +use crate::traits::rom_chip::RomChip; + +pub trait PiaChip: BusDevice + GpioChip {} diff --git a/core/src/traits/serial_interface_chip.rs b/core/src/traits/serial_interface_chip.rs new file mode 100644 index 0000000..b080337 --- /dev/null +++ b/core/src/traits/serial_interface_chip.rs @@ -0,0 +1,2 @@ +pub trait SerialInterface {} + diff --git a/core/src/traits/time_of_day_chip.rs b/core/src/traits/time_of_day_chip.rs new file mode 100644 index 0000000..93292e5 --- /dev/null +++ b/core/src/traits/time_of_day_chip.rs @@ -0,0 +1 @@ +pub trait TimeOfDay {} \ No newline at end of file diff --git a/core/src/traits/timer_chip.rs b/core/src/traits/timer_chip.rs index 48f3e21..944426a 100644 --- a/core/src/traits/timer_chip.rs +++ b/core/src/traits/timer_chip.rs @@ -1,10 +1,11 @@ +use log::debug; use crate::traits::bus_device::BusDevice; pub trait TimerChip: BusDevice { /// set_timer /// /// Set current value of timer - fn set_timer(&mut self, timer_index: u8, new_value: u16); + fn set_timer(&mut self, timer_index: u8, new_value: u16, divisor: u16); /// get_timer /// @@ -13,4 +14,10 @@ pub trait TimerChip: BusDevice { /// Advance Timer circutry 1 cycle fn tick(&mut self); -} \ No newline at end of file + + /// Edge Detect Control + fn set_edge_detect(&mut self, edge: bool); + + /// Interrupt Control + fn set_interrupt(&mut self, enabled: bool); +} diff --git a/core/src/traits/via_chip.rs b/core/src/traits/via_chip.rs deleted file mode 100644 index 4788605..0000000 --- a/core/src/traits/via_chip.rs +++ /dev/null @@ -1,21 +0,0 @@ -use log::debug; -use crate::traits::bus_device::BusDevice; -use crate::traits::ram_chip::RamChip; -use crate::traits::rom_chip::RomChip; - -pub trait ViaChip: RamChip + RomChip + BusDevice { - /// set_port_ddr - /// - /// Sets the Data Direction Register in the VIA chip for the specified - /// port. - fn set_port_ddr(&mut self, port_index: u8, value: u8) { - debug!("Please implement this."); - } - - /// set_port_data - /// - /// Sets the - fn set_port_data(&mut self, port_index: u8, value: u8) { - debug!("Please implement this."); - } -} \ No newline at end of file diff --git a/core/tests/base.rs b/core/tests/base.rs deleted file mode 100644 index fd1cd1f..0000000 --- a/core/tests/base.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn test() { - assert!(true); -} diff --git a/core/tests/mod.rs b/core/tests/mod.rs new file mode 100644 index 0000000..7e34e7f --- /dev/null +++ b/core/tests/mod.rs @@ -0,0 +1,5 @@ +mod periph; +mod mos6502; + +#[test] +fn smoke() { assert!(true) } diff --git a/core/tests/mos6502/flags.rs b/core/tests/mos6502/flags.rs new file mode 100644 index 0000000..926c349 --- /dev/null +++ b/core/tests/mos6502/flags.rs @@ -0,0 +1,18 @@ +use core::mos6502flags::Mos6502Flags; + +#[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)); +} \ No newline at end of file diff --git a/core/tests/mos6502/instruction_table.rs b/core/tests/mos6502/instruction_table.rs new file mode 100644 index 0000000..af77c63 --- /dev/null +++ b/core/tests/mos6502/instruction_table.rs @@ -0,0 +1,42 @@ + +#[test] +fn test_instruction_table_completeness() { + use core::instruction_table::INSTRUCTION_TABLE; // Adjust to your actual path + + let mut defined_count = 0; + let mut defined_opcodes = vec![]; + + for (i, entry) in INSTRUCTION_TABLE.iter().enumerate() { + if let Some(info) = entry { + defined_count += 1; + defined_opcodes.push(i); + // Optional: sanity check + assert!( + info.length > 0 && info.cycles > 0, + "Invalid OpInfo at opcode {:#04x?}", + i + ); + } + } + + println!("Defined opcodes: {}", defined_count); + for i in 0..256 { + if defined_opcodes.contains(&i) { + print!("{:02x} ", i); + } + } + println!("\nMissing opcodes:"); + for i in 0..256 { + if !defined_opcodes.contains(&i) { + print!("{:02x} ", i); + } + } + println!(); + + // The standard 6502 has 151 documented opcodes + assert_eq!( + defined_count, 151, + "Expected 151 opcodes, found {}", + defined_count + ); +} \ No newline at end of file diff --git a/core/tests/mos6502/instructions.rs b/core/tests/mos6502/instructions.rs new file mode 100644 index 0000000..7b4fdd0 --- /dev/null +++ b/core/tests/mos6502/instructions.rs @@ -0,0 +1,1257 @@ +use core::operand::Operand; +use super::*; +use core::address_mode::AddressMode::*; +use core::instruction::Instruction; +use core::operation::Operation::*; + +#[test] +fn decode_instruction() { + let params = vec![ + // ADC + ( + vec![0x69, 0xab], + Instruction { + op: ADC, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x65, 0xab], + Instruction { + op: ADC, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x75, 0xab], + Instruction { + op: ADC, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x6d, 0xcd, 0xab], + Instruction { + op: ADC, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x7d, 0xcd, 0xab], + Instruction { + op: ADC, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x79, 0xcd, 0xab], + Instruction { + op: ADC, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x61, 0xab], + Instruction { + op: ADC, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x71, 0xab], + Instruction { + op: ADC, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // AND + ( + vec![0x29, 0xab], + Instruction { + op: AND, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x25, 0xab], + Instruction { + op: AND, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x35, 0xab], + Instruction { + op: AND, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x2d, 0xcd, 0xab], + Instruction { + op: AND, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x3d, 0xcd, 0xab], + Instruction { + op: AND, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x39, 0xcd, 0xab], + Instruction { + op: AND, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x21, 0xab], + Instruction { + op: AND, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x31, 0xab], + Instruction { + op: AND, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // ASL + ( + vec![0x0a], + Instruction { + op: ASL, + mode: Accumulator, + operand: Operand::None, + }, + ), + ( + vec![0x06, 0xab], + Instruction { + op: ASL, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x16, 0xab], + Instruction { + op: ASL, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x0e, 0xcd, 0xab], + Instruction { + op: ASL, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x1e, 0xcd, 0xab], + Instruction { + op: ASL, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + // BIT + ( + vec![0x24, 0xab], + Instruction { + op: BIT, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x2c, 0xcd, 0xab], + Instruction { + op: BIT, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + // BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS + ( + vec![0x10, 0xab], + Instruction { + op: BPL, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x30, 0xab], + Instruction { + op: BMI, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x50, 0xab], + Instruction { + op: BVC, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x70, 0xab], + Instruction { + op: BVS, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x90, 0xab], + Instruction { + op: BCC, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xb0, 0xab], + Instruction { + op: BCS, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xd0, 0xab], + Instruction { + op: BNE, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xf0, 0xab], + Instruction { + op: BEQ, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + // BRK + ( + vec![0x00], + Instruction { + op: BRK, + mode: Implied, + operand: Operand::None, + }, + ), + // CLC, CLD, CLI, CLV + ( + vec![0x18], + Instruction { + op: CLC, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0xd8], + Instruction { + op: CLD, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x58], + Instruction { + op: CLI, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0xb8], + Instruction { + op: CLV, + mode: Implied, + operand: Operand::None, + }, + ), + // CMP + ( + vec![0xc9, 0xab], + Instruction { + op: CMP, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xc5, 0xab], + Instruction { + op: CMP, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xd5, 0xab], + Instruction { + op: CMP, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xcd, 0xcd, 0xab], + Instruction { + op: CMP, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xdd, 0xcd, 0xab], + Instruction { + op: CMP, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xd9, 0xcd, 0xab], + Instruction { + op: CMP, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xc1, 0xab], + Instruction { + op: CMP, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xd1, 0xab], + Instruction { + op: CMP, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // CPX + ( + vec![0xe0, 0xab], + Instruction { + op: CPX, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xe4, 0xab], + Instruction { + op: CPX, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xec, 0xcd, 0xab], + Instruction { + op: CPX, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + // CPY + ( + vec![0xc0, 0xab], + Instruction { + op: CPY, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xc4, 0xab], + Instruction { + op: CPY, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xcc, 0xcd, 0xab], + Instruction { + op: CPY, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + // DEC, DEX, DEY + ( + vec![0xc6, 0xab], + Instruction { + op: DEC, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xd6, 0xab], + Instruction { + op: DEC, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xce, 0xcd, 0xab], + Instruction { + op: DEC, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xde, 0xcd, 0xab], + Instruction { + op: DEC, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xca], + Instruction { + op: DEX, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x88], + Instruction { + op: DEY, + mode: Implied, + operand: Operand::None, + }, + ), + // EOR + ( + vec![0x49, 0xab], + Instruction { + op: EOR, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x45, 0xab], + Instruction { + op: EOR, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x55, 0xab], + Instruction { + op: EOR, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x4d, 0xcd, 0xab], + Instruction { + op: EOR, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x5d, 0xcd, 0xab], + Instruction { + op: EOR, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x59, 0xcd, 0xab], + Instruction { + op: EOR, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x41, 0xab], + Instruction { + op: EOR, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x51, 0xab], + Instruction { + op: EOR, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // INC, INX, INY + ( + vec![0xe6, 0xab], + Instruction { + op: INC, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xf6, 0xab], + Instruction { + op: INC, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xee, 0xcd, 0xab], + Instruction { + op: INC, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xfe, 0xcd, 0xab], + Instruction { + op: INC, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xe8], + Instruction { + op: INX, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0xc8], + Instruction { + op: INY, + mode: Implied, + operand: Operand::None, + }, + ), + // JMP, JSR + ( + vec![0x4c, 0xcd, 0xab], + Instruction { + op: JMP, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x6c, 0xcd, 0xab], + Instruction { + op: JMP, + mode: Indirect, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x20], + Instruction { + op: JSR, + mode: Implied, + operand: Operand::None, + }, + ), + // LDA + ( + vec![0xa9, 0xab], + Instruction { + op: LDA, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xa5, 0xab], + Instruction { + op: LDA, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xb5, 0xab], + Instruction { + op: LDA, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xad, 0xcd, 0xab], + Instruction { + op: LDA, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xbd, 0xcd, 0xab], + Instruction { + op: LDA, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xb9, 0xcd, 0xab], + Instruction { + op: LDA, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xa1, 0xab], + Instruction { + op: LDA, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xb1, 0xab], + Instruction { + op: LDA, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // LDX + ( + vec![0xa2, 0xab], + Instruction { + op: LDX, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xa6, 0xab], + Instruction { + op: LDX, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xb6, 0xab], + Instruction { + op: LDX, + mode: ZeroPageY, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xae, 0xcd, 0xab], + Instruction { + op: LDX, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xbe, 0xcd, 0xab], + Instruction { + op: LDX, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + // LDY + ( + vec![0xa0, 0xab], + Instruction { + op: LDY, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xa4, 0xab], + Instruction { + op: LDY, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xb4, 0xab], + Instruction { + op: LDY, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xac, 0xcd, 0xab], + Instruction { + op: LDY, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xbc, 0xcd, 0xab], + Instruction { + op: LDY, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + // LSR + ( + vec![0x4a], + Instruction { + op: LSR, + mode: Accumulator, + operand: Operand::None, + }, + ), + ( + vec![0x46, 0xab], + Instruction { + op: LSR, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x56, 0xab], + Instruction { + op: LSR, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x4e, 0xcd, 0xab], + Instruction { + op: LSR, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x5e, 0xcd, 0xab], + Instruction { + op: LSR, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + // NOP + ( + vec![0xea], + Instruction { + op: NOP, + mode: Implied, + operand: Operand::None, + }, + ), + // ORA + ( + vec![0x09, 0xab], + Instruction { + op: ORA, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x05, 0xab], + Instruction { + op: ORA, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x15, 0xab], + Instruction { + op: ORA, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x0d, 0xcd, 0xab], + Instruction { + op: ORA, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x1d, 0xcd, 0xab], + Instruction { + op: ORA, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x19, 0xcd, 0xab], + Instruction { + op: ORA, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x01, 0xab], + Instruction { + op: ORA, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x11, 0xab], + Instruction { + op: ORA, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // PHA, PHP, PLA, PLP + ( + vec![0x48], + Instruction { + op: PHA, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x08], + Instruction { + op: PHP, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x68], + Instruction { + op: PLA, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x28], + Instruction { + op: PLP, + mode: Implied, + operand: Operand::None, + }, + ), + // ROL + ( + vec![0x2a], + Instruction { + op: ROL, + mode: Accumulator, + operand: Operand::None, + }, + ), + ( + vec![0x26, 0xab], + Instruction { + op: ROL, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x36, 0xab], + Instruction { + op: ROL, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x2e, 0xcd, 0xab], + Instruction { + op: ROL, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x3e, 0xcd, 0xab], + Instruction { + op: ROL, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + // ROR + ( + vec![0x6a], + Instruction { + op: ROR, + mode: Accumulator, + operand: Operand::None, + }, + ), + ( + vec![0x66, 0xab], + Instruction { + op: ROR, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x76, 0xab], + Instruction { + op: ROR, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x6e, 0xcd, 0xab], + Instruction { + op: ROR, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x7e, 0xcd, 0xab], + Instruction { + op: ROR, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + // RTI, RTS + ( + vec![0x40], + Instruction { + op: RTI, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x60], + Instruction { + op: RTS, + mode: Implied, + operand: Operand::None, + }, + ), + // SBC + ( + vec![0xe9, 0xab], + Instruction { + op: SBC, + mode: Immediate, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xe5, 0xab], + Instruction { + op: SBC, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xf5, 0xab], + Instruction { + op: SBC, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xed, 0xcd, 0xab], + Instruction { + op: SBC, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xfd, 0xcd, 0xab], + Instruction { + op: SBC, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xf9, 0xcd, 0xab], + Instruction { + op: SBC, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0xe1, 0xab], + Instruction { + op: SBC, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0xf1, 0xab], + Instruction { + op: SBC, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // SEC, SED, SEI + ( + vec![0x38], + Instruction { + op: SEC, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0xf8], + Instruction { + op: SED, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x78], + Instruction { + op: SEI, + mode: Implied, + operand: Operand::None, + }, + ), + // STA + ( + vec![0x85, 0xab], + Instruction { + op: STA, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x95, 0xab], + Instruction { + op: STA, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x8d, 0xcd, 0xab], + Instruction { + op: STA, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x9d, 0xcd, 0xab], + Instruction { + op: STA, + mode: AbsoluteX, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x99, 0xcd, 0xab], + Instruction { + op: STA, + mode: AbsoluteY, + operand: Operand::Word(0xabcd), + }, + ), + ( + vec![0x81, 0xab], + Instruction { + op: STA, + mode: IndirectX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x91, 0xab], + Instruction { + op: STA, + mode: IndirectY, + operand: Operand::Byte(0xab), + }, + ), + // STX + ( + vec![0x86, 0xab], + Instruction { + op: STX, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x96, 0xab], + Instruction { + op: STX, + mode: ZeroPageY, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x8e, 0xcd, 0xab], + Instruction { + op: STX, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + // STY + ( + vec![0x84, 0xab], + Instruction { + op: STY, + mode: ZeroPage, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x94, 0xab], + Instruction { + op: STY, + mode: ZeroPageX, + operand: Operand::Byte(0xab), + }, + ), + ( + vec![0x8c, 0xcd, 0xab], + Instruction { + op: STY, + mode: Absolute, + operand: Operand::Word(0xabcd), + }, + ), + // TAX, TAY, TSX, TXA, TXS, TYA + ( + vec![0xaa], + Instruction { + op: TAX, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0xa8], + Instruction { + op: TAY, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0xba], + Instruction { + op: TSX, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x8a], + Instruction { + op: TXA, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x9a], + Instruction { + op: TXS, + mode: Implied, + operand: Operand::None, + }, + ), + ( + vec![0x98], + Instruction { + op: TYA, + mode: Implied, + operand: Operand::None, + }, + ), + ]; + for (bytes, instruction) in params { + let result1 = Instruction::decode(&bytes); + if let Some(instruction1) = result1 { + assert_eq!(instruction, instruction1) + } else { + println!("Failed to decode {:?}", bytes); + } + } +} \ No newline at end of file diff --git a/core/tests/mos6502/mod.rs b/core/tests/mos6502/mod.rs new file mode 100644 index 0000000..54035e8 --- /dev/null +++ b/core/tests/mos6502/mod.rs @@ -0,0 +1,3 @@ +mod flags; +mod instructions; +mod instruction_table; \ No newline at end of file diff --git a/core/tests/periph/at28c256.rs b/core/tests/periph/at28c256.rs new file mode 100644 index 0000000..d127b94 --- /dev/null +++ b/core/tests/periph/at28c256.rs @@ -0,0 +1,153 @@ +use core::constants::constants_system::*; +use core::constants::constants_test::*; +use core::periph::at28c256::At28C256; +use core::traits::bus_device::BusDevice; +use core::traits::memory_chip::MemoryChip; +use std::fs; +use std::io::Read; + +#[test] +fn smoke() { + assert!(true); +} + +#[test] +fn checksum_binary_loads() { + let path = format!("{}/{}", TEST_PERIPH_AT28C256_ROOT, "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."); + } + }; + + let mut rom = At28C256::new(0x0000, 0x3fff, bytes); + + assert_eq!(rom.checksum(), 0x58); +} + +#[test] +fn full_chunks_come_back_ok() { + let test_data = (0..255).collect(); + let mut chip = At28C256::new(0x0000, 0x3fff, test_data); + + let chunks = chip.chunks(16); + assert_eq!(chunks.len(), 16); +} + +#[test] +fn partial_blocks_come_back_ok() { + let test_data = (0..=3).collect(); + let mut chip = At28C256::new(0x0000, 0x3fff, test_data); + + let chunks = chip.chunks(16); + assert_eq!(chunks.len(), 1); + for chunk in chunks { + assert_eq!(chunk.len(), 4); + } +} + +#[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 - 1) { + 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"); +} + +#[test] +fn address_data_set_reads() { + let mut x = At28C256::new(0x0000, 0x3fff, vec![0xea; SIZE_32KB]); + + // set both... + x.set_address_bus(0x3000); + x.set_data_bus(0xab); + assert_eq!(0xab, x.data_bus()); + assert_eq!(0x3000, x.address_bus()); + + // ...set address... + x.set_address_bus(0x2000); + assert_eq!(0x2000, x.address_bus()); + assert_eq!(0xab, x.data_bus()); + + // ...set data. + x.set_data_bus(0xba); + assert_eq!(0xba, x.data_bus()); + assert_eq!(0x2000, x.address_bus()); +} + +fn programming_chip_changes_contents() { + let mut chip = At28C256::new(0x0000, 0x3fff, vec![]); + + assert_eq!(0x00, chip.read(&0x0000)); + + let new_data: Vec = vec![0xff, 0xff, 0xff, 0xff]; + chip.program(new_data.into()); + assert_eq!(0xff, chip.read(&0x0000)); + assert_eq!(0x00, chip.read(&0x05)); +} + +#[test] +fn correct_flags_required() { + let mut chip = At28C256::new(0x0000, 0x3fff, vec![0xff]); + assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, true)); + assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, true, false)); + assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, false)); + assert_eq!(0xff, chip.signal_tick(0x0000, 0xab, true, true, true)); + assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, false, true)); +} + +#[test] +fn loaded_rom_is_readable() { + let mut chip = At28C256::new(0x0000, 0x3fff, [0xff; SIZE_32KB].to_vec()); + + for index in 0..SIZE_32KB { + assert_eq!(chip.data[index as usize], 0xff); + } +} diff --git a/core/tests/periph/hm62256.rs b/core/tests/periph/hm62256.rs new file mode 100644 index 0000000..8d9e46c --- /dev/null +++ b/core/tests/periph/hm62256.rs @@ -0,0 +1,54 @@ +use core::constants::constants_system::*; +use rand::random; +use core::traits::memory_chip::MemoryChip; +use core::periph::hm62256::Hm62256; +use core::traits::ram_chip::RamChip; + +#[test] +#[ignore] +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 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); +} + +#[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); + } +} \ No newline at end of file diff --git a/core/tests/periph/kim1_keypad.rs b/core/tests/periph/kim1_keypad.rs new file mode 100644 index 0000000..5c29ebc --- /dev/null +++ b/core/tests/periph/kim1_keypad.rs @@ -0,0 +1,44 @@ +use core::periph::kim1_keypad::Kim1Keypad; + +#[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)); +} \ No newline at end of file diff --git a/core/tests/periph/mod.rs b/core/tests/periph/mod.rs new file mode 100644 index 0000000..cc4a20e --- /dev/null +++ b/core/tests/periph/mod.rs @@ -0,0 +1,5 @@ +mod at28c256; +mod mos6522; +mod mos6520; +mod kim1_keypad; +mod hm62256; diff --git a/core/tests/periph/mos6520.rs b/core/tests/periph/mos6520.rs new file mode 100644 index 0000000..49bc606 --- /dev/null +++ b/core/tests/periph/mos6520.rs @@ -0,0 +1,56 @@ +use core::periph::mos6520::Mos6520; + +const DDRA_OFFSET: u8 = 0x00; +const PORTA_OFFSET: u8 = 0x01; +const DDRB_OFFSET: u8 = 0x02; +const PORTB_OFFSET: u8 = 0x03; + +fn actual(base: u16, offset: u8) -> u16 { + base + offset as u16 +} + +#[test] +fn ddrb_tests() { + let mut x = Mos6520::new(0x1000); + let params = vec![ + // Offset data outa ina ddra porta + (DDRB_OFFSET, 0xff, 0x00, 0x00, 0xff, 0x00), + (PORTB_OFFSET, 0xff, 0xff, 0x00, 0xff, 0xff), + (DDRB_OFFSET, 0xaa, 0xaa, 0x55, 0xaa, 0xff), + (DDRB_OFFSET, 0x55, 0x55, 0xaa, 0x55, 0xff), + (PORTB_OFFSET, 0xf0, 0x50, 0xa0, 0x55, 0xf0), + (PORTB_OFFSET, 0x0f, 0x05, 0x0a, 0x55, 0x0f), + (DDRB_OFFSET, 0xff, 0x0f, 0x00, 0xff, 0x0f) + ]; + + for (offset, data, outb, inb, ddrb, portb) in params { + x.tick(actual(x.offset, offset), data); + assert_eq!(outb, x.out_b); + assert_eq!(inb, x.in_b); + assert_eq!(ddrb, x.ddrb); + assert_eq!(portb, x.port_b); + } +} + +#[test] +fn ddra_tests() { + let mut x = Mos6520::new(0x1000); + let params = vec![ + // Offset data outa ina ddra porta + (DDRA_OFFSET, 0xff, 0x00, 0x00, 0xff, 0x00), + (PORTA_OFFSET, 0xff, 0xff, 0x00, 0xff, 0xff), + (DDRA_OFFSET, 0xaa, 0xaa, 0x55, 0xaa, 0xff), + (DDRA_OFFSET, 0x55, 0x55, 0xaa, 0x55, 0xff), + (PORTA_OFFSET, 0xf0, 0x50, 0xa0, 0x55, 0xf0), + (PORTA_OFFSET, 0x0f, 0x05, 0x0a, 0x55, 0x0f), + (DDRA_OFFSET, 0xff, 0x0f, 0x00, 0xff, 0x0f) + ]; + + for (offset, data, outa, ina, ddra, porta) in params { + x.tick(actual(x.offset, offset), data); + assert_eq!(outa, x.out_a); + assert_eq!(ina, x.in_a); + assert_eq!(ddra, x.ddra); + assert_eq!(porta, x.port_a); + } +} \ No newline at end of file diff --git a/core/tests/periph/mos6522.rs b/core/tests/periph/mos6522.rs new file mode 100644 index 0000000..b75cde4 --- /dev/null +++ b/core/tests/periph/mos6522.rs @@ -0,0 +1,42 @@ +use core::periph::mos6522::Mos6522; +use core::constants::constants_via6522::*; + +#[test] +fn registers() { + let mut x = Mos6522::new(0x0000); + 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(0x0000); + 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(0x0000); + 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); +} \ No newline at end of file diff --git a/egui/Cargo.toml b/egui/Cargo.toml new file mode 100644 index 0000000..a69d709 --- /dev/null +++ b/egui/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "egui" +version = "0.1.0" +edition = "2024" + +[dependencies] +eframe = "0.27" +core = { path = "../core" } \ No newline at end of file diff --git a/egui/src/bin/rom_only_gui.rs b/egui/src/bin/rom_only_gui.rs new file mode 100644 index 0000000..e3bf7f2 --- /dev/null +++ b/egui/src/bin/rom_only_gui.rs @@ -0,0 +1,77 @@ +use eframe::egui; +use core::computers::rom_only::RomOnlyComputer; +use core::periph::at28c256::At28C256; + +struct RomGui { + computer: RomOnlyComputer, + address_input: String, + data_input: String, + last_result: Option, +} + +impl Default for RomGui { + fn default() -> Self { + Self { + computer: RomOnlyComputer { + rom: At28C256::new(0x0000, 0x3fff, [0xff; 0x4000].to_vec()), + data_bus: 0x00, + address_bus: 0x0000, + read_mode: true, + }, + address_input: "0000".to_string(), + data_input: "00".to_string(), + last_result: None, + } + } +} + +impl eframe::App for RomGui { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::CentralPanel::default().show(ctx, |ui| { + ui.heading("RomOnlyComputer"); + + ui.horizontal(|ui| { + ui.label("Address (hex):"); + ui.text_edit_singleline(&mut self.address_input); + }); + + ui.horizontal(|ui| { + ui.label("Data (hex):"); + ui.text_edit_singleline(&mut self.data_input); + }); + + ui.checkbox(&mut self.computer.read_mode, "Read Mode"); + + if ui.button("Tick").clicked() { + if let (Ok(addr), Ok(data)) = ( + u16::from_str_radix(&self.address_input.trim(), 16), + u8::from_str_radix(&self.data_input.trim(), 16), + ) { + self.computer.address_bus = addr; + self.computer.data_bus = data; + + let result = self.computer.rom.signal_tick(addr, data, true, true, true); + self.last_result = Some(result); + + println!( + "Tick: A=${:04X} D=${:02X} R={} -> ${:02X}", + addr, data, self.computer.read_mode, result + ); + } + } + + if let Some(result) = self.last_result { + ui.label(format!("Last result: ${:02X}", result)); + } + }); + } +} + +fn main() -> eframe::Result<()> { + let options = eframe::NativeOptions::default(); + eframe::run_native( + "RomOnlyComputer GUI", + options, + Box::new(|_cc| Box::new(RomGui::default())), + ) +} diff --git a/macroquad/src/bin/rom_only_gui.rs b/macroquad/src/bin/rom_only_gui.rs index 48be1ce..2b7404b 100644 --- a/macroquad/src/bin/rom_only_gui.rs +++ b/macroquad/src/bin/rom_only_gui.rs @@ -1,88 +1,91 @@ -use eframe::egui; +use core::periph::at28c256::At28C256; use core::computers::rom_only::RomOnlyComputer; -struct MyApp { - address: String, - data: String, - cpu_read: bool, - computer: RomOnlyComputer +use macroquad::prelude::*; +use core::traits::backplane::Backplane; + +#[macroquad::main("RomOnlyComputer GUI")] +async fn main() { + let mut computer = RomOnlyComputer { + rom: At28C256::new(0x0000, 0x3fff, [0xea; 0x4000].to_vec()), + data_bus: 0x00, + address_bus: 0x0000, + read_mode: true, + }; + + let mut input_addr = String::new(); + let mut input_data = String::new(); + + loop { + clear_background(GRAY); + let mut y = 20.0; + + draw_text("RomOnlyComputer", 20.0, y, 30.0, WHITE); + y += 40.0; + + // Address input + draw_text("Address Bus (hex):", 20.0, y, 24.0, WHITE); + y += 30.0; + draw_input_box(&mut input_addr, 20.0, y, 100.0, 30.0); + y += 40.0; + + // Data input + draw_text("Data Bus (hex):", 20.0, y, 24.0, WHITE); + y += 30.0; + draw_input_box(&mut input_data, 20.0, y, 100.0, 30.0); + y += 40.0; + + // Read mode checkbox + let read_mode_rect = Rect::new(20.0, y, 20.0, 20.0); + draw_rectangle_lines(read_mode_rect.x, read_mode_rect.y, read_mode_rect.w, read_mode_rect.h, 2.0, WHITE); + if computer.read_mode() { + draw_text("✔", read_mode_rect.x + 2.0, read_mode_rect.y + 18.0, 20.0, GREEN); + } + draw_text("Read Mode", read_mode_rect.x + 30.0, read_mode_rect.y + 18.0, 20.0, WHITE); + if is_mouse_button_pressed(MouseButton::Left) && read_mode_rect.contains(mouse_position().into()) { + computer.set_read_mode(!computer.read_mode()); + } + y += 40.0; + + // Tick button + let button_rect = Rect::new(20.0, y, 100.0, 40.0); + draw_rectangle(button_rect.x, button_rect.y, button_rect.w, button_rect.h, DARKGRAY); + draw_text("Tick", button_rect.x + 25.0, button_rect.y + 28.0, 24.0, WHITE); + if is_mouse_button_pressed(MouseButton::Left) && button_rect.contains(mouse_position().into()) { + if let (Ok(addr), Ok(data)) = ( + u16::from_str_radix(&input_addr.trim_start_matches("0x"), 16), + u8::from_str_radix(&input_data.trim_start_matches("0x"), 16), + ) { + computer.set_address_bus(addr); + computer.set_data_bus(data); + // let result = computer.rom.tick(addr, data, computer.read_mode); + println!( + "Tick: A=${:04X} D=${:02X} R={} -> ", // ${:02X}", + addr, data, computer.read_mode, // result + ); + } + } + + next_frame().await; + } } -impl Default for MyApp { - fn default() -> Self { - let rom_data = vec![0x01, 0x02, 0x03, 0x04]; - Self { - address: String::new(), - data: String::new(), - cpu_read: false, - computer: RomOnlyComputer::program(rom_data), // Example memory: 0x00 to 0xFF +fn draw_input_box(value: &mut String, x: f32, y: f32, w: f32, h: f32) { + draw_rectangle(x, y, w, h, DARKGRAY); + let mut display = value.clone(); + if get_time() as u64 % 2 == 0 { + display.push('_'); + } + draw_text(&display, x + 5.0, y + 22.0, 20.0, WHITE); + + let rect = Rect::new(x, y, w, h); + if is_mouse_button_pressed(MouseButton::Left) && rect.contains(mouse_position().into()) { + if let Some(chr) = get_char_pressed() { + if chr.is_ascii_hexdigit() && value.len() < 4 { + value.push(chr.to_ascii_uppercase()); + } + } + if is_key_pressed(KeyCode::Backspace) { + value.pop(); } } } - -impl eframe::App for MyApp { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - egui::CentralPanel::default().show(ctx, |ui| { - ui.heading("Memory Inspector"); - - ui.horizontal(|ui| { - ui.label("Address:"); - ui.text_edit_singleline(&mut self.address); - }); - - ui.horizontal(|ui| { - ui.label("Data:"); - ui.text_edit_singleline(&mut self.data); - }); - - ui.checkbox(&mut self.cpu_read, "CPU Read"); - - if ui.button("Tick").clicked() { - println!( - "Ticked with Address: {}, Data: {}, CPU Read: {}", - self.address, self.data, self.cpu_read - ); - } - - ui.horizontal(|ui| { - ui.label(format!("Address Bus ${:?}", self.address).as_str()); - ui.label(format!("Data Bus ${:?}", self.data).as_str()); - }); - - ui.separator(); - ui.label("Memory View (Hex Dump):"); - - egui::ScrollArea::vertical().show(ui, |ui| { - let bytes_per_row = 16; - for (i, chunk) in self.computer.rom_chunks(bytes_per_row).enumerate() { - let address = i * bytes_per_row; - let hex_values: String = chunk - .iter() - .map(|b| format!("{:02X} ", b)) - .collect(); - - let ascii_values: String = chunk - .iter() - .map(|b| { - if b.is_ascii_graphic() { - *b as char - } else { - '.' - } - }) - .collect(); - - ui.monospace(format!("{:08X}: {:<48} {}", address, hex_values, ascii_values)); - } - }); - }); - } -} - -fn main() -> eframe::Result<()> { - let options = eframe::NativeOptions::default(); - eframe::run_native( - "Memory Inspector", - options, - Box::new(|_cc| Box::new(MyApp::default())), - ) -} diff --git a/resources/docs/mcs6500_family_hardware_manual.pdf b/resources/docs/mcs6500_family_hardware_manual.pdf new file mode 100644 index 0000000..ab0121c Binary files /dev/null and b/resources/docs/mcs6500_family_hardware_manual.pdf differ diff --git a/resources/docs/mos_microcomputers_programming_manual.pdf b/resources/docs/mos_microcomputers_programming_manual.pdf new file mode 100644 index 0000000..3ed6b30 Binary files /dev/null and b/resources/docs/mos_microcomputers_programming_manual.pdf differ diff --git a/resources/docs/synertek6502.pdf b/resources/docs/synertek6502.pdf new file mode 100644 index 0000000..9f9cd4c Binary files /dev/null and b/resources/docs/synertek6502.pdf differ diff --git a/resources/docs/the_mos_catalogue_pinned_to_april_1976_letter_to_customers.pdf b/resources/docs/the_mos_catalogue_pinned_to_april_1976_letter_to_customers.pdf new file mode 100644 index 0000000..da75e39 Binary files /dev/null and b/resources/docs/the_mos_catalogue_pinned_to_april_1976_letter_to_customers.pdf differ