From 7ac8bd86ba46993fdac597be65bca43fe8f9e73a Mon Sep 17 00:00:00 2001 From: Trevor Merritt Date: Tue, 29 Jul 2025 13:12:33 -0400 Subject: [PATCH] MOS6520 looking mostly there. --- Cargo.lock | 8 - Cargo.toml | 2 +- beneater/src/parts/backplane.rs | 2 +- cli/src/bin/kim1.rs | 2 +- cli/src/bin/ram_rom.rs | 2 +- cli/src/bin/ticker.rs | 5 - core/src/computers/beneater/backplane.rs | 52 ++++++ core/src/computers/beneater/beneater.rs | 0 core/src/computers/beneater/mod.rs | 13 +- core/src/computers/kim1/mod.rs | 5 - core/src/computers/mod.rs | 1 + core/src/computers/ram_rom/backplane.rs | 19 ++- core/src/computers/ram_rom/tick2.rs | 2 +- core/src/computers/rom_only/backplane.rs | 28 ++-- core/src/computers/rom_only/mod.rs | 2 +- core/src/computers/tim1/mod.rs | 8 + core/src/constants/constants_via6522.rs | 1 - core/src/mos6502cpu/bus_device.rs | 9 +- core/src/periph/at28c256/blocks.rs | 29 ---- core/src/periph/at28c256/bus_device.rs | 28 ++++ core/src/periph/at28c256/checksum.rs | 63 ------- core/src/periph/at28c256/default.rs | 10 -- core/src/periph/at28c256/memory_chip.rs | 16 ++ core/src/periph/at28c256/mod.rs | 154 +++++++++++++++++- core/src/periph/at28c256/program.rs | 21 --- core/src/periph/at28c256/rom_chip.rs | 35 +--- core/src/periph/at28c256/signal_tick.rs | 18 -- core/src/periph/at28c256/tick.rs | 71 -------- core/src/periph/hm62256/default.rs | 1 + core/src/periph/hm62256/mod.rs | 1 + core/src/periph/hm62256/new.rs | 1 + core/src/periph/hm62256/ramchip.rs | 28 ++++ core/src/periph/mos6520/bus_device.rs | 26 +++ core/src/periph/mos6520/mod.rs | 82 ++++------ core/src/periph/mos6520/new.rs | 21 +++ core/src/periph/mos6520/reset.rs | 12 -- core/src/periph/mos6520/tick.rs | 83 ++++++++++ core/src/periph/mos6520/tod.rs | 26 --- core/src/periph/mos6520/via_chip.rs | 126 ++++++++++++++ core/src/periph/mos6530/mod.rs | 6 +- core/src/periph/mos6530/mos6530.rs | 26 +-- core/src/periph/mos6530/viachip.rs | 29 +++- core/src/traits/backplane.rs | 9 +- core/src/traits/bus_device.rs | 27 ++- core/src/traits/memory_chip.rs | 4 +- core/src/traits/mod.rs | 1 + core/src/traits/ram_chip.rs | 3 +- core/src/traits/rom_chip.rs | 3 +- core/src/traits/timer_chip.rs | 16 ++ core/src/traits/via_chip.rs | 12 +- core/tests/at28c256.rs | 6 - core/tests/base.rs | 4 + core/tests/execution_tests.rs | 1 - core/tests/instruction_encode_decode.rs | 1 - resources/docs/6820_hardware_manual.pdf | Bin 0 -> 75834 bytes .../6530-002_fillerbyte00-0x1c00.bin | Bin .../6530-003_fillerbyte00-0x1800.bin | Bin src/main.rs | 3 - 58 files changed, 742 insertions(+), 422 deletions(-) delete mode 100644 cli/src/bin/ticker.rs create mode 100644 core/src/computers/beneater/backplane.rs delete mode 100644 core/src/computers/beneater/beneater.rs create mode 100644 core/src/computers/tim1/mod.rs create mode 100644 core/src/periph/at28c256/bus_device.rs create mode 100644 core/src/periph/at28c256/memory_chip.rs delete mode 100644 core/src/periph/at28c256/tick.rs create mode 100644 core/src/periph/mos6520/bus_device.rs create mode 100644 core/src/periph/mos6520/new.rs create mode 100644 core/src/periph/mos6520/tick.rs delete mode 100644 core/src/periph/mos6520/tod.rs create mode 100644 core/src/periph/mos6520/via_chip.rs create mode 100644 core/src/traits/timer_chip.rs delete mode 100644 core/tests/at28c256.rs create mode 100644 core/tests/base.rs delete mode 100644 core/tests/execution_tests.rs delete mode 100644 core/tests/instruction_encode_decode.rs create mode 100644 resources/docs/6820_hardware_manual.pdf rename resources/{kim1 => pia}/6530-002_fillerbyte00-0x1c00.bin (100%) rename resources/{kim1 => pia}/6530-003_fillerbyte00-0x1800.bin (100%) delete mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 263a8b5..1470c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -497,14 +497,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "beneater" -version = "0.1.0" -dependencies = [ - "core", - "macroquad 0.4.14", -] - [[package]] name = "bit-set" version = "0.5.3" diff --git a/Cargo.toml b/Cargo.toml index d7dfc39..ff22a3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "core", "cli", "macroquad", - "beneater" +# "beneater" ] resolver="2" diff --git a/beneater/src/parts/backplane.rs b/beneater/src/parts/backplane.rs index d11f0cf..ecb091e 100644 --- a/beneater/src/parts/backplane.rs +++ b/beneater/src/parts/backplane.rs @@ -1,7 +1,7 @@ use crate::parts::mos6522_peripheral::Mos6522Peripheral; use crate::parts::via6522::VIA6522; use core::constants::constants_system::*; -use core::mos6502cpu::cpu::Mos6502Cpu; +use core::mos6502cpu::Mos6502Cpu; use core::periph::at28c256::At28C256; use core::traits::rom_chip::RomChip; use core::constants::constants_via6522::*; diff --git a/cli/src/bin/kim1.rs b/cli/src/bin/kim1.rs index eec3ada..481a05a 100644 --- a/cli/src/bin/kim1.rs +++ b/cli/src/bin/kim1.rs @@ -1,5 +1,5 @@ use core::computers::kim1::Kim1; - +use core::constants::constants_system::*; fn main() { println!("Taxation is theft."); diff --git a/cli/src/bin/ram_rom.rs b/cli/src/bin/ram_rom.rs index 89fdd1d..d25cfef 100644 --- a/cli/src/bin/ram_rom.rs +++ b/cli/src/bin/ram_rom.rs @@ -1,4 +1,4 @@ -use core::computers::ram_rom::backplane::RamRomComputer; +use core::computers::ram_rom::RamRomComputer; use core::traits::backplane::Backplane; use std::fs; diff --git a/cli/src/bin/ticker.rs b/cli/src/bin/ticker.rs deleted file mode 100644 index 160d067..0000000 --- a/cli/src/bin/ticker.rs +++ /dev/null @@ -1,5 +0,0 @@ -use core::mos6502cpu::cpu::Mos6502Cpu; - -fn main() { - let x = Mos6502Cpu::default(); -} diff --git a/core/src/computers/beneater/backplane.rs b/core/src/computers/beneater/backplane.rs new file mode 100644 index 0000000..5942fc9 --- /dev/null +++ b/core/src/computers/beneater/backplane.rs @@ -0,0 +1,52 @@ +use crate::computers::beneater::BenEater; +use crate::traits::backplane::Backplane; + +impl Backplane for BenEater { + 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!("Tick the system."); + + self.tick_ram(self.address_bus, self.data_bus, true, true, true); + self.tick_rom(self.address_bus, true, true); + self.tick_via(self.address_bus, self.data_bus, true, true, self.read_mode, true, true); + } + + fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 { + println!("Ticking RAM"); + 0x00 + } + + fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 { + println!("Ticking ROM"); + 0x00 + } + + fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) { + println!("Ticking VIA 6522"); + let (new_address, new_data) = self.via.tick(self.address_bus, self.data_bus, false, self.read_mode); + (new_data, false, false) + } +} \ No newline at end of file diff --git a/core/src/computers/beneater/beneater.rs b/core/src/computers/beneater/beneater.rs deleted file mode 100644 index e69de29..0000000 diff --git a/core/src/computers/beneater/mod.rs b/core/src/computers/beneater/mod.rs index 0188360..880b0d6 100644 --- a/core/src/computers/beneater/mod.rs +++ b/core/src/computers/beneater/mod.rs @@ -1 +1,12 @@ -pub mod beneater; +mod backplane; + +use crate::mos6502cpu::Mos6502Cpu; +use crate::periph::mos6522::mos6522::Mos6522; + +pub struct BenEater { + cpu: Mos6502Cpu, + via: Mos6522, + data_bus: u8, + address_bus: u16, + read_mode: bool +} diff --git a/core/src/computers/kim1/mod.rs b/core/src/computers/kim1/mod.rs index 2b4a0c5..2c43c07 100644 --- a/core/src/computers/kim1/mod.rs +++ b/core/src/computers/kim1/mod.rs @@ -2,14 +2,9 @@ pub mod new; pub mod tick; pub mod reset; -use std::fs; -use std::path::Path; -use crate::constants::constants_system::SIZE_1KB; use crate::mos6502cpu::Mos6502Cpu; -use crate::periph::at28c256::At28C256; use crate::periph::hm62256::Hm62256; use crate::periph::kim1_keypad::Kim1Keypad; -use crate::periph::mos6522::mos6522::Mos6522; use crate::periph::mos6530::mos6530::Mos6530; /// Represents a KIM-1 diff --git a/core/src/computers/mod.rs b/core/src/computers/mod.rs index 80916a1..f5d33eb 100644 --- a/core/src/computers/mod.rs +++ b/core/src/computers/mod.rs @@ -2,3 +2,4 @@ pub mod beneater; pub mod rom_only; pub mod kim1; pub mod ram_rom; +pub mod tim1; diff --git a/core/src/computers/ram_rom/backplane.rs b/core/src/computers/ram_rom/backplane.rs index 2017e73..75d31e6 100644 --- a/core/src/computers/ram_rom/backplane.rs +++ b/core/src/computers/ram_rom/backplane.rs @@ -1,9 +1,9 @@ +use log::debug; use crate::computers::ram_rom::RamRomComputer; use crate::periph::at28c256::At28C256; use crate::traits::backplane::Backplane; use crate::periph::hm62256::Hm62256; - impl Backplane for RamRomComputer { fn data_bus(&self) -> u8 { self.data_bus @@ -45,7 +45,7 @@ impl Backplane for RamRomComputer { 0x4000..=0x7fff => { // ROM println!("ADDRESSING ROM"); - let (rom_address_bus, rom_data_bus) = self.rom.tick(self.address_bus, self.data_bus, self.read_mode); + let rom_data_bus = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true); self.data_bus = rom_data_bus; } _ => { @@ -54,16 +54,19 @@ impl Backplane for RamRomComputer { } } - fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) { - todo!() + fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 { + debug!("Ticking ram with A${address:04x} D${data:02x} CS:{cs} OE:{oe} WE:{we}"); + 0 } - fn tick_rom(&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 { + debug!("Ticking rom with A${address:04x} CS:{cs} OE:{oe}"); + 0 } - fn tick_via(&mut self, address: u16, data: u8, cs: bool, rw: bool, ce: bool) -> (u8, u8, bool) { - todo!() + fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) { + debug!("Ticking Via with A${address:04x} D${data:02x} cs0:{cs0} cs1:{cs1} rw:{rw} rs0:{rs0} rs1:{rs1}"); + (0, false, false) } } diff --git a/core/src/computers/ram_rom/tick2.rs b/core/src/computers/ram_rom/tick2.rs index e314cdf..21aec55 100644 --- a/core/src/computers/ram_rom/tick2.rs +++ b/core/src/computers/ram_rom/tick2.rs @@ -44,7 +44,7 @@ impl RamRomComputer { } _ => {} }; - let (_, rom_data_bus) = self.rom.tick(address, data, control == 1); + let rom_data_bus = self.rom.signal_tick(self.address_bus ,self.data_bus, true ,true , true); let (_, ram_data_bus) = self.ram.tick(address, data, control == 1, true); 0 } diff --git a/core/src/computers/rom_only/backplane.rs b/core/src/computers/rom_only/backplane.rs index 92f8b12..b7a7d2d 100644 --- a/core/src/computers/rom_only/backplane.rs +++ b/core/src/computers/rom_only/backplane.rs @@ -17,27 +17,29 @@ impl Backplane for RomOnlyComputer { } fn tick(&mut self) { - println!("COMPUTER: Preparing to tick."); +// println!("COMPUTER: Preparing to tick."); // do are we being addressed? - println!("COMPUTER: BUSSES PRE: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode); - let (new_addr, new_data) = self.rom.tick(self.address_bus, self.data_bus, self.read_mode); - self.set_address_bus(new_addr); +// println!("COMPUTER: BUSSES PRE: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode); + let new_data = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true); + // tick(self.address_bus, self.data_bus, self.read_mode); + self.set_address_bus(self.address_bus); self.set_data_bus(new_data); - println!("COMPUTER: BUSSES POST: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode); - println!("COMPUTER: Done ticking."); +// println!("COMPUTER: BUSSES POST: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode); +// println!("COMPUTER: Done ticking."); } - fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) { + fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 { debug!("This system has no ram. ROM only."); + 0x00 } - fn tick_rom(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> (u8) { - let (_, data) = self.rom.tick(address, data, true); - data + fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 { + let data = self.rom.signal_tick(address, self.data_bus, cs, oe, true); + data } - fn tick_via(&mut self, address: u16, data: u8, cs: bool, rw: bool, ce: bool) -> (u8, u8, bool) { - debug!("This system has no VIA controllers. ROM only"); - (0,0,true) + fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) { + debug!("This system has no VIA controllers. ROM only"); + (0,false,false) } } diff --git a/core/src/computers/rom_only/mod.rs b/core/src/computers/rom_only/mod.rs index d548820..db2c083 100644 --- a/core/src/computers/rom_only/mod.rs +++ b/core/src/computers/rom_only/mod.rs @@ -19,7 +19,7 @@ impl RomOnlyComputer { // tick the parts... println!("WIDETICK: A:${address:04x} D:${data:02x} C:b{control:08b}"); - let (_, new_data) = self.rom.tick(address, data, control == 0x01); + let new_data = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true); println!("\nNew Data : {new_data:02x}"); self.set_data_bus(new_data); new_data diff --git a/core/src/computers/tim1/mod.rs b/core/src/computers/tim1/mod.rs new file mode 100644 index 0000000..d6710aa --- /dev/null +++ b/core/src/computers/tim1/mod.rs @@ -0,0 +1,8 @@ +use crate::mos6502cpu::Mos6502Cpu; +use crate::periph::mos6530::mos6530::Mos6530; + +pub struct Tim1 { + cpu: Mos6502Cpu, + pia: Mos6530 +} + diff --git a/core/src/constants/constants_via6522.rs b/core/src/constants/constants_via6522.rs index 1286151..73df2d5 100644 --- a/core/src/constants/constants_via6522.rs +++ b/core/src/constants/constants_via6522.rs @@ -17,4 +17,3 @@ pub const VIA6522_ACR: u8 = 0b1100; pub const VIA6522_PCR: u8 = 0b1101; pub const VIA6522_IFR: u8 = 0b1110; pub const VIA6522_IER: u8 = 0b1111; - diff --git a/core/src/mos6502cpu/bus_device.rs b/core/src/mos6502cpu/bus_device.rs index f001eb1..ae089be 100644 --- a/core/src/mos6502cpu/bus_device.rs +++ b/core/src/mos6502cpu/bus_device.rs @@ -2,6 +2,12 @@ use crate::mos6502cpu::Mos6502Cpu; use crate::traits::bus_device::BusDevice; impl BusDevice for Mos6502Cpu { + fn min_offset(&self) -> u16 { 0 } + + fn max_offset(&self) -> u16 { + 0x7fff + } + fn address_bus(&self) -> u16 { self.address_bus } @@ -18,7 +24,4 @@ impl BusDevice for Mos6502Cpu { self.data_bus = new_value; } - fn talking_to_me(&self, address: u16) -> bool { - todo!() - } } \ No newline at end of file diff --git a/core/src/periph/at28c256/blocks.rs b/core/src/periph/at28c256/blocks.rs index 9d754d1..4e744cb 100644 --- a/core/src/periph/at28c256/blocks.rs +++ b/core/src/periph/at28c256/blocks.rs @@ -6,32 +6,3 @@ impl At28C256 { self.data.chunks(size) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn smoke() { assert!(true); } - - #[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); - } - } -} diff --git a/core/src/periph/at28c256/bus_device.rs b/core/src/periph/at28c256/bus_device.rs new file mode 100644 index 0000000..c6d91a2 --- /dev/null +++ b/core/src/periph/at28c256/bus_device.rs @@ -0,0 +1,28 @@ +use crate::periph::at28c256::At28C256; +use crate::traits::bus_device::BusDevice; + +impl BusDevice for At28C256 { + 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/at28c256/checksum.rs b/core/src/periph/at28c256/checksum.rs index e3298dc..55a3aa8 100644 --- a/core/src/periph/at28c256/checksum.rs +++ b/core/src/periph/at28c256/checksum.rs @@ -16,66 +16,3 @@ impl At28C256 { } } -#[cfg(test)] -mod test { - use std::fs; - use std::path::Path; - use crate::constants::constants_system::SIZE_1KB; - use crate::traits::memory_chip::MemoryChip; - use crate::traits::rom_chip::RomChip; - use super::*; - - #[test] - fn smoke() { assert!(true); } - - #[test] - fn programmed_data_reads_back_same() { - let mut data = At28C256::default(); - for i in 0..SIZE_32KB { - data.data[i] = 0xeau8; - } - for offset in 0..SIZE_32KB { - if offset.is_multiple_of(SIZE_1KB) {}; - assert_eq!(0xea, data.read(&(offset as u16))); - } - } - - #[test] - fn checksums_calculate_correctly_for_zero() { - let data1 = [0x00u8; SIZE_32KB]; - assert_eq!(0x00, At28C256::checksum_static(&data1)); - } - - #[test] - fn checksums_calculate_for_1_byte() { - let data = [0xff; 1]; - assert_eq!(0xff, At28C256::checksum_static(&data)); - } - - #[test] - fn checksums_calculate_for_2_bytes() { - let data = [0xff; 2]; - // 0xff + 0xff = 0x1fe - assert_eq!(0xfe, At28C256::checksum_static(&data)); - } - - #[test] - fn checksums_calculate_for_first_80_bytes() { - println!("STARTING TEST"); - let mut checksum = 0x00; - - let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin"); - println!("READING [{path}]"); - let data = fs::read(path); - match data { - Ok(bytes) => { - println!("Read {} bytes", bytes.len()); - checksum = At28C256::checksum_static(&bytes); - println!("Checksum: 0x{:02x}", checksum); - } - Err(e) => eprintln!("Failed to read file: {}", e), - } - assert_eq!(0x58, checksum); - println!("TEST COMPLETE"); - } -} diff --git a/core/src/periph/at28c256/default.rs b/core/src/periph/at28c256/default.rs index 018161e..f84bd92 100644 --- a/core/src/periph/at28c256/default.rs +++ b/core/src/periph/at28c256/default.rs @@ -21,13 +21,3 @@ impl Default for At28C256 { } } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn smoke() { - assert!(true); - } -} diff --git a/core/src/periph/at28c256/memory_chip.rs b/core/src/periph/at28c256/memory_chip.rs new file mode 100644 index 0000000..c536c6e --- /dev/null +++ b/core/src/periph/at28c256/memory_chip.rs @@ -0,0 +1,16 @@ +use log::debug; +use crate::periph::at28c256::At28C256; +use crate::traits::bus_device::BusDevice; +use crate::traits::memory_chip::MemoryChip; + +impl MemoryChip for At28C256 { + /// read + /// + /// Reads a byte from memory. + /// Returns a 0x00 if there is no data at that location but is still in ROM address range + fn read(&self, offset: &u16) -> u8 { + let effective = self.internal_address(*offset); + debug!("STARTING READ FROM At28C256 ${:04x} | ${:04x} / ${effective:04x} | ${:04x}", self.offset, offset, self.max_offset); + if effective >= self.data.len() as u16 { 0x00 } else { self.data[effective as usize] } + } +} \ No newline at end of file diff --git a/core/src/periph/at28c256/mod.rs b/core/src/periph/at28c256/mod.rs index 745ad39..588c999 100644 --- a/core/src/periph/at28c256/mod.rs +++ b/core/src/periph/at28c256/mod.rs @@ -1,6 +1,5 @@ pub mod default; pub mod rom_chip; -pub mod tick; pub mod new; pub mod program; pub mod dump; @@ -8,9 +7,9 @@ pub mod checksum; pub mod blocks; pub mod control; pub mod signal_tick; +pub mod memory_chip; +pub mod bus_device; -use crate::traits::rom_chip::RomChip; -use std::io::Read; /// At28C256 /// @@ -31,3 +30,152 @@ 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/at28c256/program.rs b/core/src/periph/at28c256/program.rs index cd835c6..3daf627 100644 --- a/core/src/periph/at28c256/program.rs +++ b/core/src/periph/at28c256/program.rs @@ -9,24 +9,3 @@ impl At28C256 { } } -#[cfg(test)] -mod test { - use crate::traits::memory_chip::MemoryChip; - use crate::traits::rom_chip::RomChip; - use super::*; - - #[test] - fn smoke() { assert!(true) } - - #[test] - fn programming_chip_changes_contents() { - let mut chip = At28C256::new(0x0000, 0x3fff, vec![]); - - assert_eq!(0x00, chip.read(&0x0000)); - - let new_data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - chip.program(new_data.into()); - assert_eq!(0xff, chip.read(&0x0000)); - assert_eq!(0x00, chip.read(&0x05)); - } -} diff --git a/core/src/periph/at28c256/rom_chip.rs b/core/src/periph/at28c256/rom_chip.rs index d50002a..c50c137 100644 --- a/core/src/periph/at28c256/rom_chip.rs +++ b/core/src/periph/at28c256/rom_chip.rs @@ -1,29 +1,10 @@ +use log::debug; use crate::constants::constants_system::SIZE_32KB; use crate::periph::at28c256::At28C256; +use crate::traits::bus_device::BusDevice; use crate::traits::memory_chip::MemoryChip; use crate::traits::rom_chip::RomChip; -impl MemoryChip for At28C256 { - fn read(&self, offset: &u16) -> u8 { - /// read - /// - /// Reads a byte from memory. - /// Returns a 0x00 if there is no data at that location but is still in ROM address range - println!("STARTING READ FROM At28C256 ${:04x} | ${:04x} | ${:04x}", self.offset, offset, self.max_offset); - if offset < &self.offset || offset > &self.max_offset { - println!("Unable to read from ${offset:04x} as it it out of range."); - return 0x00; - } else { - println!("OK READ FROM GOOD AREA total len = {}", self.data.len()); - } - - if *offset >= self.data.len() as u16 { - 0x00 - } else { - self.data[*offset as usize] - } } -} - impl RomChip for At28C256 { /// program /// @@ -34,14 +15,4 @@ impl RomChip for At28C256 { working.data = new_data.to_vec().into_boxed_slice(); Box::new(working) } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn smoke() { - assert!(true); - } -} +} \ No newline at end of file diff --git a/core/src/periph/at28c256/signal_tick.rs b/core/src/periph/at28c256/signal_tick.rs index 685b6a4..650112d 100644 --- a/core/src/periph/at28c256/signal_tick.rs +++ b/core/src/periph/at28c256/signal_tick.rs @@ -31,21 +31,3 @@ impl At28C256 { result } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn smoke() { assert!(true) } - - #[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/at28c256/tick.rs b/core/src/periph/at28c256/tick.rs deleted file mode 100644 index 5e978ee..0000000 --- a/core/src/periph/at28c256/tick.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::constants::constants_system::SIZE_32KB; -use crate::periph::at28c256::At28C256; -use crate::periph::hm62256::Hm62256; - -impl At28C256 { - fn talking_to_me(&self, address: u16) -> bool { - //println!("Checking on {address:04x} in range of {:04x} {:04x}", self.offset, self.max_offset); - address >= self.offset && address < self.max_offset - } - - - - pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool) -> (u16, u8) { - println!("At28C256: Tick starting for A${address_bus:04x} D${data_bus:02x} R{read_mode}"); - - // we aren't being addressed - // OR - // we arent reading from the ROM... - if !self.talking_to_me(address_bus) || - !read_mode { - // ...go away. - return (address_bus, data_bus) - } - - // print!("At28C256 tick for me."); - let effective = address_bus - self.offset; - if effective < self.max_offset { - if effective < self.data.len() as u16 { - self.data_bus = self.data[effective as usize]; - } else { - self.data_bus = 0x00; - } - } else { - println!("At28C256: OUTSIDE RANGE. :("); - return (address_bus, data_bus) - } - - // print!("At28C256: Read... {:02x}", self.data_bus); - (address_bus, self.data_bus) - } -} - -#[cfg(test)] -mod test { - use std::fs; - use crate::traits::rom_chip::RomChip; - use super::*; - - #[test] - fn smoke() { assert!(true); } - - #[test] - fn checksum_binary_loads() { - let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin"; - let bytes = match fs::read(path) { - Ok(bytes) => { - println!("Read {} bytes.", bytes.len()); - bytes - }, - Err(e) => { - eprintln!("FAIL to read rom."); - panic!("No rom no run."); - vec![] - } - }; - - let mut rom = At28C256::new(0x0000, 0x3fff, bytes); - - assert_eq!(rom.checksum(), 0x58); - } -} \ No newline at end of file diff --git a/core/src/periph/hm62256/default.rs b/core/src/periph/hm62256/default.rs index 46bf649..a95f7df 100644 --- a/core/src/periph/hm62256/default.rs +++ b/core/src/periph/hm62256/default.rs @@ -9,6 +9,7 @@ impl Default for Hm62256 { boxed_slice.try_into().expect("Unable to box the ram"); Hm62256 { offset: 0x0000, + max_offset: 0x2000, data: boxed_array, address_bus: 0x0000, data_bus: 0x00, diff --git a/core/src/periph/hm62256/mod.rs b/core/src/periph/hm62256/mod.rs index 775af1b..2661f6e 100644 --- a/core/src/periph/hm62256/mod.rs +++ b/core/src/periph/hm62256/mod.rs @@ -18,6 +18,7 @@ use log::debug; /// 32KByte pub struct Hm62256 { pub(crate) offset: u16, + pub(crate) max_offset: u16, pub(crate) data: Box<[u8]>, pub(crate) address_bus: u16, pub(crate) data_bus: u8, diff --git a/core/src/periph/hm62256/new.rs b/core/src/periph/hm62256/new.rs index 2b0af21..89fb56c 100644 --- a/core/src/periph/hm62256/new.rs +++ b/core/src/periph/hm62256/new.rs @@ -5,6 +5,7 @@ impl Hm62256 { pub fn new(base_offset: u16) -> Self { Self { offset: base_offset, + max_offset: base_offset + SIZE_32KB as u16, data: vec![0; SIZE_32KB].into_boxed_slice(), address_bus: 0x0000, data_bus: 0x00, diff --git a/core/src/periph/hm62256/ramchip.rs b/core/src/periph/hm62256/ramchip.rs index 9c29eca..602dbb4 100644 --- a/core/src/periph/hm62256/ramchip.rs +++ b/core/src/periph/hm62256/ramchip.rs @@ -1,7 +1,35 @@ use crate::constants::constants_system::SIZE_32KB; 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/mos6520/bus_device.rs b/core/src/periph/mos6520/bus_device.rs new file mode 100644 index 0000000..00bf416 --- /dev/null +++ b/core/src/periph/mos6520/bus_device.rs @@ -0,0 +1,26 @@ +use crate::periph::mos6520::Mos6520; +use crate::traits::bus_device::BusDevice; + +impl BusDevice for Mos6520 { + 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/mos6520/mod.rs b/core/src/periph/mos6520/mod.rs index 5694c24..04a4333 100644 --- a/core/src/periph/mos6520/mod.rs +++ b/core/src/periph/mos6520/mod.rs @@ -1,65 +1,51 @@ -mod tod; -mod reset; +pub mod reset; +pub mod bus_device; +pub mod via_chip; +pub mod new; +pub mod tick; +use crate::traits::bus_device::BusDevice; 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 +control problems in the implementation of microcomputer systems. This device allows +a very effective trade-off between software and hardware by providing significant +capability and flexibility in a low cost chip. When coupled with the power and +speed of the MCS6500 family of microprocessors, the MCS6S20 allows implementation +of very complex systems at a minimum overall cost. +Control of peripheral devices is handled primarily through two 8-bit bi-directional ports. + +Each of these lines can be programmed to act as either an input or +an output. In addition, four peripheral control/interrupt input lines are provided. +These lines can be used to interrupt the processor or for "hand-shaking" data +be tween the processor and a peripheral device. + */ pub struct Mos6520 { // Parallel ports A & B data registers port_a: u8, - port_b: u8, + out_a: u8, + in_a: u8, ddra: u8, + port_b: u8, + out_b: u8, + in_b: u8, ddrb: u8, - // Timer registers and control - tmr_a: u16, - latch_a: u16, - ctrl_a: u8, - tmr_b: u16, - latch_b: u16, - ctrl_b: u8, // Interrupt control register icr: u8, - // TOD clock raw BCD registers - tod_hours: u8, - tod_minutes: u8, - tod_seconds: u8, - tod_tenths: u8, - tod_frozen: bool, + // External Address and Data Bus + address_bus: u16, + data_bus: u8, + // Offsets + offset: u16, + max_offset: u16 } -impl RamChip for Mos6520 { - fn write(&mut self, offset: &u16, value: &u8) { - match offset & 0x0F { - 0x0 => { self.port_a = *value; } - 0x1 => { self.ddra = *value; } - 0x4 => { self.latch_a = (self.latch_a & 0xFF00) | *value as u16; } - 0x5 => { self.latch_a = (self.latch_a & 0x00FF) | ((*value as u16) << 8); } - 0x8..=0xB => { self.write_tod((offset & 0x03) as u8, *value); } - _ => {} - } +impl Mos6520 { + pub fn debug_dump(&self) { + println!("DUMPING STATE: \nPORT_A:\t{:08b}\nIN_A:\t{:08b}\nOUT_A:\t{:08b}\nDDRA:\t{:08b}", self.port_a, self.in_a, self.out_a, self.ddra); } } - -impl MemoryChip for Mos6520 { - fn read(&self, offset: &u16) -> u8 { - todo!() - } -} - -impl RomChip for Mos6520 { - fn program(new_data: &[u8]) -> Box { - todo!() - } -} - -impl ViaChip for Mos6520 { - fn set_port_ddr(&mut self, port_index: u8, value: u8) { - todo!() - } - - fn set_port_data(&mut self, port_index: u8, value: u8) { - todo!() - } -} \ No newline at end of file diff --git a/core/src/periph/mos6520/new.rs b/core/src/periph/mos6520/new.rs new file mode 100644 index 0000000..2c7e196 --- /dev/null +++ b/core/src/periph/mos6520/new.rs @@ -0,0 +1,21 @@ +use crate::periph::mos6520::Mos6520; + +impl Mos6520 { + pub fn new(start_offset: u16) -> Self { + Self { + port_a: 0, + out_a: 0, + port_b: 0, + out_b: 0, + ddra: 0, + ddrb: 0, + icr: 0, + address_bus: 0, + data_bus: 0, + offset: start_offset, + max_offset: start_offset + 4, + in_a: 0, + in_b: 0, + } + } +} diff --git a/core/src/periph/mos6520/reset.rs b/core/src/periph/mos6520/reset.rs index b2161b8..e8379a3 100644 --- a/core/src/periph/mos6520/reset.rs +++ b/core/src/periph/mos6520/reset.rs @@ -9,18 +9,6 @@ impl Mos6520 { self.port_b = 0x00; self.ddra = 0x00; self.ddrb = 0x00; - self.tmr_a = 0x00; - self.latch_a = 0x00; - self.ctrl_a = 0x00; - self.tmr_b = 0x00; - self.latch_b = 0x00; - self.ctrl_b = 0x00; self.icr = 0x00; - self.tod_hours = 0x01; - self.tod_minutes = 0x00; - self.tod_seconds = 0x00; - self.tod_tenths = 0x00; - self.tod_frozen = false; } } - diff --git a/core/src/periph/mos6520/tick.rs b/core/src/periph/mos6520/tick.rs new file mode 100644 index 0000000..466038d --- /dev/null +++ b/core/src/periph/mos6520/tick.rs @@ -0,0 +1,83 @@ +use log::debug; +use crate::periph::mos6520::Mos6520; +use crate::traits::bus_device::BusDevice; + +impl Mos6520 { + pub fn tick(&mut self, address_bus: u16, data_bus: u8) -> u8 { + self.address_bus = address_bus; + self.data_bus = data_bus; + + println!("Preparing to tick MOS6520"); + + // are we changing our state? + let effective = self.internal_address(self.address_bus); + + match effective { + 0x00 => { + // ddra + // println!("DDRA -> {:02x} -> {:02x}", self.ddra, self.data_bus); + self.ddra = self.data_bus; + } + 0x01 => { + // port a + // println!("PORT A -> {:02x} -> {:02x}", self.port_a, self.data_bus); + self.port_a = self.data_bus; + } + 0x02 => { + // ddrb + // println!("DDRB -> {:02x}", self.ddrb); + self.ddrb = self.data_bus; + } + 0x03 => { + // port b + // println!("PORT B -> {:02x}", self.port_b); + self.port_b = self.data_bus; + } + _ => { + panic!("Unable to access via 6520 at offset ${effective}") + } + } + + self.tick_port_a(); + self.tick_port_b(); + + self.data_bus + } + + fn tick_port_a(&mut self) { + self.out_a = 0x00; + self.in_a = 0x00; + + for current_bit in 0..8 { + let bit_mask = 1 << current_bit; + let is_output = (self.ddra & bit_mask) != 0; + let is_set = (self.port_a & bit_mask) != 0; + + match (is_output, is_set) { + (true, true) => self.out_a |= bit_mask, + (false, true) => self.in_a |= bit_mask, + _ => {} + } + } + + println!("Time to tick the device on port a"); + } + + fn tick_port_b(&mut self) { + self.out_b = 0x00; + self.in_b = 0x00; + + for current_bit in 0..8 { + let bit_mask = 1 << current_bit; + let is_output = (self.ddrb & bit_mask) != 0; + let is_set = (self.port_b & bit_mask) != 0; + + match (is_output, is_set) { + (true, true) => self.out_b |= bit_mask, + (false, true) => self.in_b |= bit_mask, + _ => {} + } + } + println!("Time to tick the device on port b"); + } +} diff --git a/core/src/periph/mos6520/tod.rs b/core/src/periph/mos6520/tod.rs deleted file mode 100644 index c162425..0000000 --- a/core/src/periph/mos6520/tod.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::periph::mos6520::Mos6520; - -impl Mos6520 { - fn read_tod(&mut self, sub: u8) -> u8 { - match sub { - 0 => { self.tod_frozen = true; self.tod_hours }, - 1 => self.tod_minutes, - 2 => self.tod_seconds, - 3 => { - self.tod_frozen = false; - self.tod_tenths - } - _ => 0 - } - } - - pub(crate) fn write_tod(&mut self, sub: u8, value: u8) { - match sub { - 0 => { self.tod_hours = value; self.tod_frozen = true; } - 1 => self.tod_minutes = value, - 2 => self.tod_seconds = value, - 3 => { self.tod_tenths = value; self.tod_frozen = false; } - _ => {} - } - } -} \ No newline at end of file diff --git a/core/src/periph/mos6520/via_chip.rs b/core/src/periph/mos6520/via_chip.rs new file mode 100644 index 0000000..f7462bd --- /dev/null +++ b/core/src/periph/mos6520/via_chip.rs @@ -0,0 +1,126 @@ +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/mos6530/mod.rs b/core/src/periph/mos6530/mod.rs index c8f6431..d14e5bb 100644 --- a/core/src/periph/mos6530/mod.rs +++ b/core/src/periph/mos6530/mod.rs @@ -1,5 +1,5 @@ pub mod mos6530; pub mod tick; -mod new; -mod dump; -mod viachip; \ No newline at end of file +pub mod new; +pub mod dump; +pub mod viachip; \ No newline at end of file diff --git a/core/src/periph/mos6530/mos6530.rs b/core/src/periph/mos6530/mos6530.rs index 7a72b0d..349552a 100644 --- a/core/src/periph/mos6530/mos6530.rs +++ b/core/src/periph/mos6530/mos6530.rs @@ -13,19 +13,19 @@ use crate::periph::mos6522::mos6522::Mos6522; /// IO Ports (A, B) /// Timer pub struct Mos6530 { - pub(crate) data: [u8; SIZE_1KB], - pub(crate) ram: [u8; 64], - pub(crate) porta: u8, - pub(crate) portb: u8, - pub(crate) data_bus: u8, - pub(crate) address_bus: u16, - pub(crate) cs1: bool, - pub(crate) cs2: bool, + 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(crate) rw: bool, - pub(crate) reset: bool, - pub(crate) io_offset: u16, - pub(crate) ram_offset: u16, - pub(crate) rom_offset: u16 + 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/viachip.rs b/core/src/periph/mos6530/viachip.rs index cb45ecc..b316838 100644 --- a/core/src/periph/mos6530/viachip.rs +++ b/core/src/periph/mos6530/viachip.rs @@ -1,11 +1,39 @@ use log::debug; use crate::constants::constants_system::{SIZE_1KB, SIZE_32KB}; use crate::periph::mos6530::mos6530::Mos6530; +use crate::traits::bus_device::BusDevice; 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 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 + } + +} + impl RamChip for Mos6530 { fn write(&mut self, offset: &u16, value: &u8) { debug!("🐙 Writing ${value:02x} to ${offset:04x}"); @@ -49,4 +77,3 @@ impl ViaChip for Mos6530 { debug!("🐙Setting PORT{port_index} to {value:02x}"); } } - diff --git a/core/src/traits/backplane.rs b/core/src/traits/backplane.rs index ce0b354..b8c6978 100644 --- a/core/src/traits/backplane.rs +++ b/core/src/traits/backplane.rs @@ -16,19 +16,20 @@ pub trait Backplane { /// Tick a ROM chip /// /// returns (data_for_databus) - fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool); + fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8; /// tick_rom /// /// Tick a ROM chip /// /// returns (data_for_databus) - fn tick_rom(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> (u8); + fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8; /// tick_via /// /// Tick a VIA chip /// - /// returns (portA, portB, interrupt) - fn tick_via(&mut self, address: u16, data: u8, cs: bool, rw: bool, ce: bool) -> (u8, u8, bool); + /// 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); } + diff --git a/core/src/traits/bus_device.rs b/core/src/traits/bus_device.rs index 848a307..557b6c6 100644 --- a/core/src/traits/bus_device.rs +++ b/core/src/traits/bus_device.rs @@ -1,8 +1,33 @@ + 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; + fn talking_to_me(&self, address: u16) -> bool { + address >= self.min_offset() && address < self.max_offset() + } + + /// internal_address + /// + /// Converts from a system-wide address to a device specific address + /// + /// ex: 0x3000 to a ROM chip with min_offset = 0x2000 + /// -> internal_address = 0x1000 <-- read this offset from data array + /// -> external_address = 0x3000 + fn internal_address(&self, address: u16) -> u16 { + // println!("MINOFFSET ${:04x}", self.min_offset()); + let mo = self.max_offset(); + let result= if address > mo { + address - self.max_offset() + } else { + address + }; + // println!("RESULT = {:04x}", result - self.min_offset()); + + result - self.min_offset() + } } diff --git a/core/src/traits/memory_chip.rs b/core/src/traits/memory_chip.rs index 835871a..d451da0 100644 --- a/core/src/traits/memory_chip.rs +++ b/core/src/traits/memory_chip.rs @@ -1,4 +1,6 @@ -pub trait MemoryChip { +use crate::traits::bus_device::BusDevice; + +pub trait MemoryChip : BusDevice { /// Read /// /// Reads a single byte from the specified address diff --git a/core/src/traits/mod.rs b/core/src/traits/mod.rs index c26e9cf..29b2941 100644 --- a/core/src/traits/mod.rs +++ b/core/src/traits/mod.rs @@ -5,3 +5,4 @@ pub mod rom_chip; pub mod ram_chip; pub mod via_chip; pub mod backplane; +mod timer_chip; diff --git a/core/src/traits/ram_chip.rs b/core/src/traits/ram_chip.rs index 16a5d8a..431504d 100644 --- a/core/src/traits/ram_chip.rs +++ b/core/src/traits/ram_chip.rs @@ -1,7 +1,8 @@ use crate::periph::hm62256::Hm62256; +use crate::traits::bus_device::BusDevice; use crate::traits::memory_chip::MemoryChip; use crate::traits::rom_chip::RomChip; -pub trait RamChip: MemoryChip { +pub trait RamChip: MemoryChip + BusDevice { fn write(&mut self, offset: &u16, value: &u8); } diff --git a/core/src/traits/rom_chip.rs b/core/src/traits/rom_chip.rs index 0041c3e..f1413fe 100644 --- a/core/src/traits/rom_chip.rs +++ b/core/src/traits/rom_chip.rs @@ -1,7 +1,8 @@ use crate::constants::constants_system::SIZE_32KB; +use crate::traits::bus_device::BusDevice; use crate::traits::memory_chip::MemoryChip; -pub trait RomChip: MemoryChip { +pub trait RomChip: MemoryChip + BusDevice { /// Program /// /// Replaces all data in the chip diff --git a/core/src/traits/timer_chip.rs b/core/src/traits/timer_chip.rs new file mode 100644 index 0000000..48f3e21 --- /dev/null +++ b/core/src/traits/timer_chip.rs @@ -0,0 +1,16 @@ +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); + + /// get_timer + /// + /// Read current value of timer + fn get_timer(&mut self, timer_index: u8) -> u16; + + /// Advance Timer circutry 1 cycle + fn tick(&mut self); +} \ No newline at end of file diff --git a/core/src/traits/via_chip.rs b/core/src/traits/via_chip.rs index 8606870..4788605 100644 --- a/core/src/traits/via_chip.rs +++ b/core/src/traits/via_chip.rs @@ -1,15 +1,21 @@ +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 { +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); + 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); + 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/at28c256.rs b/core/tests/at28c256.rs deleted file mode 100644 index 1295628..0000000 --- a/core/tests/at28c256.rs +++ /dev/null @@ -1,6 +0,0 @@ -use super::*; - -#[test] -fn test123() { - -} \ No newline at end of file diff --git a/core/tests/base.rs b/core/tests/base.rs new file mode 100644 index 0000000..fd1cd1f --- /dev/null +++ b/core/tests/base.rs @@ -0,0 +1,4 @@ +#[test] +fn test() { + assert!(true); +} diff --git a/core/tests/execution_tests.rs b/core/tests/execution_tests.rs deleted file mode 100644 index 8b13789..0000000 --- a/core/tests/execution_tests.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/tests/instruction_encode_decode.rs b/core/tests/instruction_encode_decode.rs deleted file mode 100644 index 8b13789..0000000 --- a/core/tests/instruction_encode_decode.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/resources/docs/6820_hardware_manual.pdf b/resources/docs/6820_hardware_manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4fb96a14bc0e6f6c039e3ff3c3312251672e47c2 GIT binary patch literal 75834 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

^g@na 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 iQ$q!%=L{Yqq31*0LPGyqqBKw{Dw