more fixed stuff

This commit is contained in:
2025-07-16 15:01:16 -04:00
parent ff43a99e0c
commit 6fcf1547d1
29 changed files with 625 additions and 139 deletions
+1
View File
@@ -5,3 +5,4 @@ edition = "2024"
[dependencies]
log = "0.4"
rand = "0.9.0"
once_cell = "0.1"
+25
View File
@@ -0,0 +1,25 @@
use crate::mos6502cpu::cpu::Mos6502Cpu;
use crate::periph::ram_chip::RamChip;
/// BackplaneBuilder
///
/// Builds a Backplane for a 6502 Emulated PC
struct BackplaneBuilder {
cpu: Mos6502Cpu,
// ram_modules: Vec<dyn RamChip>
}
impl BackplaneBuilder {
pub fn add_cpu(mut self, new_cpu: Mos6502Cpu) -> Self {
self.cpu = new_cpu;
self
}
pub fn add_ram(mut self, new_ram: impl RamChip) -> Self {
// self.ram_modules.push(new_ram);
self
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod beneater;
pub mod rom_only;
pub mod kim1;
pub mod ram_rom;
+53
View File
@@ -0,0 +1,53 @@
use crate::periph::at28c256::At28C256;
use crate::periph::backplane::Backplane;
use crate::periph::hm62256::Hm62256;
pub struct RamRomComputer {
rom: At28C256,
ram: Hm62256,
data_bus: u8,
address_bus: u16,
read_mode: bool,
}
impl Backplane for RamRomComputer {
fn data_bus(&self) -> u8 {
self.data_bus
}
fn address_bus(&self) -> u16 {
self.address_bus
}
fn read_mode(&self) -> bool {
self.read_mode
}
fn tick(&mut self) {
todo!()
}
fn set_read_mode(&mut self, new_mode: bool) {
self.read_mode = new_mode;
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value;
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value;
}
}
impl RamRomComputer {
pub fn new() -> RamRomComputer {
RamRomComputer {
rom: At28C256::default(),
ram: Hm62256::default(),
data_bus: 0x00,
address_bus: 0x0000,
/// is the CPU reading from the 'other' device?
read_mode: true
}
}
}
+1
View File
@@ -0,0 +1 @@
pub mod backplane;
+43 -18
View File
@@ -1,34 +1,59 @@
use crate::constants::constants_system::{SIZE_32KB, SIZE_64KB};
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
use crate::periph::backplane::Backplane;
use crate::periph::rom_chip::RomChip;
pub struct RomOnlyComputer {
rom: At28C256,
data_bus: u8,
address_bus: u16
address_bus: u16,
read_mode: bool,
}
impl Backplane for RomOnlyComputer {
fn data_bus(&self) -> u8 { self.data_bus }
fn address_bus(&self) -> u16 { self.address_bus }
fn read_mode(&self) -> bool { self.read_mode }
fn set_read_mode(&mut self, new_mode: bool) {
self.read_mode = new_mode
}
fn set_data_bus(&mut self, new_value: u8) {
self.data_bus = new_value
}
fn set_address_bus(&mut self, new_value: u16) {
self.address_bus = new_value
}
fn tick(&mut self) {
println!("COMPUTER: Preparing to tick.");
// do are we being addressed?
println!("COMPUTER: BUSSES PRE: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode);
let (new_addr, new_data) = self.rom.tick(self.address_bus, self.data_bus, self.read_mode);
self.set_address_bus(new_addr);
self.set_data_bus(new_data);
println!("COMPUTER: BUSSES POST: 0x{:04x} 0x{:02x} {}", self.address_bus, self.data_bus, self.read_mode);
println!("COMPUTER: Done ticking.");
}
}
impl RomOnlyComputer {
pub fn new() -> RomOnlyComputer {
RomOnlyComputer::program(&[0x00; SIZE_32KB])
let mut working = vec![0x00u8; SIZE_32KB];
for index in 0..SIZE_32KB {
working[index] = index as u8;
}
RomOnlyComputer::program(working)
}
pub fn program(rom: &[u8; SIZE_32KB]) -> RomOnlyComputer {
pub fn program(rom: Vec<u8>) -> RomOnlyComputer {
RomOnlyComputer {
rom: *At28C256::new(0x000, rom),
rom: At28C256::new(0x000, 0x3fff, rom),
address_bus: 0x0000,
data_bus: 0x00
data_bus: 0x00,
read_mode: true,
}
}
pub fn tick(&mut self) {
println!("Preparing to tick.");
// do are we being addressed?
println!("Done ticking.");
}
}
+7
View File
@@ -0,0 +1,7 @@
use std::borrow::ToOwned;
use once_cell::unsync::Lazy;
pub const TEST_RESOURCES_ROOT: &str = "/home/tmerritt/Projects/resources/test";
pub const TEST_PERIPH_ROOT: &str = "/home/tmerritt/Projects/resources/test/periph";
pub const TEST_PERIPH_AT28C256_ROOT: &str = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256";
+1
View File
@@ -3,3 +3,4 @@ pub mod constants_isa_stub;
pub mod constants_system;
pub mod constants_via6522;
pub mod constants_mos6530;
pub mod constants_test;
+1
View File
@@ -11,3 +11,4 @@ pub mod op_info;
pub mod operand;
pub mod operation;
pub mod periph;
mod backplane;
+8 -1
View File
@@ -526,7 +526,7 @@ impl Mos6502Cpu {
}
}
}
/*
#[cfg(test)]
mod test {
use super::*;
@@ -537,6 +537,12 @@ mod test {
fn clc() {
// setup the CPU for our test
let mut cpu = Mos6502Cpu::default();
// tick through the reset cycle
while !cpu.has_reset {
cpu.tick();
}
println!("DONE RESET TICKS");
cpu.flags.set_flag(Carry);
// Load our 'test program'
cpu.memory[0x6000] = ISA_OP_CLC;
@@ -763,3 +769,4 @@ mod test {
assert_eq!(cpu.memory[0xab], 0b1010_1010);;
}
}
*/
+3 -5
View File
@@ -13,14 +13,12 @@ impl Mos6502Cpu {
working
}
pub(crate) fn reset_cpu(&mut self) {
self.microcode_step = 7 + 4;
self.microcode_step = 7 + 6;
// self = &mut Mos6502Cpu::default();
println!("Should tick 7 times, then 4 cycles to read the reset and int vectors.");
println!("Should tick 7 times, then 6 cycles to read the reset and int vectors.");
// read the value at 0xfffa 0xfffb for our NMI vector.
// read the value at 0xfffc 0xfffd for our reset vector.
// read the value at 0xfffe 0xffff for our int vector
}
}
+80
View File
@@ -0,0 +1,80 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
use crate::constants::constants_test::*;
impl At28C256 {
/// checksum
///
/// calculates and returns the checksum for the loaded binary.
/// files with all zero will calculate to zero
pub fn checksum(&self) -> u8 {
At28C256::checksum_static(&self.data[..])
}
pub fn checksum_static(data: &[u8]) -> u8 {
data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
}
}
#[cfg(test)]
mod test {
use std::fs;
use std::path::Path;
use crate::constants::constants_system::SIZE_1KB;
use crate::periph::rom_chip::RomChip;
use super::*;
#[test]
fn smoke() { assert!(true); }
#[test]
fn programmed_data_reads_back_same() {
let mut data = At28C256::default();
for i in 0..SIZE_32KB {
data.data[i] = 0xeau8;
}
for offset in 0..SIZE_32KB {
if offset.is_multiple_of(SIZE_1KB) {};
assert_eq!(0xea, data.read(&(offset as u16)));
}
}
#[test]
fn checksums_calculate_correctly_for_zero() {
let data1 = [0x00u8; SIZE_32KB];
assert_eq!(0x00, At28C256::checksum_static(&data1));
}
#[test]
fn checksums_calculate_for_1_byte() {
let data = [0xff; 1];
assert_eq!(0xff, At28C256::checksum_static(&data));
}
#[test]
fn checksums_calculate_for_2_bytes() {
let data = [0xff; 2];
// 0xff + 0xff = 0x1fe
assert_eq!(0xfe, At28C256::checksum_static(&data));
}
#[test]
fn checksums_calculate_for_first_80_bytes() {
println!("STARTING TEST");
let mut checksum = 0x00;
let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin");
println!("READING [{path}]");
let data = fs::read(path);
match data {
Ok(bytes) => {
println!("Read {} bytes", bytes.len());
checksum = At28C256::checksum_static(&bytes);
println!("Checksum: 0x{:02x}", checksum);
}
Err(e) => eprintln!("Failed to read file: {}", e),
}
assert_eq!(0x58, checksum);
println!("TEST COMPLETE");
}
}
+6 -4
View File
@@ -9,10 +9,12 @@ impl Default for At28C256 {
let boxed_array: Box<[u8; SIZE_32KB]> = boxed_slice
.try_into()
.expect("Failed to convert Vec to boxed array");
At28C256 { data: boxed_array,
address_bus: 0x0000,
data_bus: 0x00,
offset: 0x0000
At28C256 {
data: boxed_array,
address_bus: 0x0000,
data_bus: 0x00,
offset: 0x0000,
max_offset: 0x3fff,
}
}
}
+5 -26
View File
@@ -4,6 +4,7 @@ pub mod tick;
mod new;
mod program;
mod dump;
mod checksum;
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::rom_chip::RomChip;
@@ -11,37 +12,15 @@ use std::io::Read;
/// At28C256
///
/// Represents a single At28C256 Chip
/// Represents a single At28C256 EEPROM Chip
///
/// 256kbit storage
/// 32kbyte storage
pub struct At28C256 {
data_bus: u8,
address_bus: u16,
data: Box<[u8; SIZE_32KB]>,
data: Box<[u8]>,
// where in the computer memory map do we live?
offset: u16
}
#[cfg(test)]
mod test {
use super::*;
use crate::constants::constants_system::SIZE_1KB;
#[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] = 0xea;
}
for offset in 0..SIZE_32KB {
if offset.is_multiple_of(SIZE_1KB) {};
assert_eq!(0xea, data.read(&(offset as u16)));
}
}
offset: u16,
max_offset: u16
}
+6 -3
View File
@@ -2,12 +2,15 @@ use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
impl At28C256 {
pub fn new(offset: u16, data: &[u8; SIZE_32KB]) -> Self {
pub fn new(offset: u16, max_offset: u16, data: Vec<u8>) -> Self {
println!("NEW At28C256 with checksum ${:02x}", At28C256::checksum_static(&data[..]));
At28C256 {
data: (*data).into(),
data: data.into_boxed_slice(),
address_bus: 0x0000,
data_bus: 0x00,
offset
offset,
max_offset
}
}
}
+44 -18
View File
@@ -3,40 +3,66 @@ use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
impl At28C256 {
fn max_address(&self) -> u16 {
self.offset + SIZE_32KB as u16
fn talking_to_me(&self, address: u16) -> bool {
address >= self.offset && address < self.max_offset
}
pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool) -> (u16, u8) {
if address_bus.gt(&self.offset) & address_bus.lt(&self.max_address()) {
if read_mode {
panic!("UNABLE TO WRITE TO ROM");
} else {
// has to be read mode. its a rom.
return (address_bus, data_bus)
}
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)
}
// not for us.
(address_bus, self.data[address_bus as usize])
let effective = address_bus - self.offset;
if effective < self.max_offset {
if effective < self.data.len() as u16 {
self.data_bus = self.data[effective as usize];
} else {
self.data_bus = 0x00;
}
} else {
println!("At28C256: OUTSIDE RANGE. :(");
return (address_bus, data_bus)
}
println!("At28C256: Read... {:02x}", self.data_bus);
println!("At28C256: Done with ticking the AtC256");
(address_bus, self.data_bus)
}
}
#[cfg(test)]
mod test {
use std::fs;
use crate::periph::rom_chip::RomChip;
use super::*;
#[test]
fn smoke() { assert!(true); }
#[test]
fn write_to_memory_read_back_works_at_0() {
let mut rom = At28C256::default();
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![]
}
};
rom.tick(0x0000, 0xab, false);
let (_, new_data) = rom.tick(0x0000, 0x00, true);
let mut rom = At28C256::new(0x0000, 0x3fff, bytes);
assert_eq!(new_data, 0xab);
assert_eq!(rom.checksum(), 0x58);
}
}
+9
View File
@@ -0,0 +1,9 @@
pub trait Backplane {
fn data_bus(&self) -> u8;
fn address_bus(&self) -> u16;
fn read_mode(&self) -> bool;
fn set_read_mode(&mut self, new_mode: bool);
fn set_data_bus(&mut self, new_value: u8);
fn set_address_bus(&mut self, new_value: u16);
fn tick(&mut self);
}
+1 -2
View File
@@ -7,12 +7,11 @@ impl Hm62256 {
}
pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool, cs: bool) -> (u16, u8) {
println!("HM62256RAM TICK START -> 0x{address_bus:04x} 0x{data_bus:02x} {read_mode} {cs}");
if !(address_bus.gt( &self.offset) && address_bus.le(&self.max_address())) {
return (address_bus, data_bus);
}
println!("HM62256RAM TICK START -> 0x{address_bus:04x} 0x{data_bus:02x} {read_mode} {cs}");
self.address_bus = address_bus;
self.data_bus = data_bus;
let addr = address_bus.wrapping_sub(self.offset) + self.offset;
+1
View File
@@ -6,3 +6,4 @@ pub mod mos6522;
pub mod mos6530;
pub mod kim1_keypad;
mod bus_device;
pub mod backplane;
+20 -16
View File
@@ -46,6 +46,10 @@ pub struct Mos6522 {
}
impl Mos6522 {
pub fn max_offset(&self) -> u16 {
self.offset + 0x10
}
pub fn start_clocks(&mut self) {
loop {
let cycle_start = Instant::now();
@@ -66,40 +70,40 @@ mod test {
#[test]
fn registers() {
let mut x = Mos6522::new();
x.tick(0b0000_0000, VIA6522_DDRA, false, true);
x.tick(VIA6522_DDRA as u16, 0b0000_0000, false, true);
assert_eq!(x.dda, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_DDRA, false, true);
x.tick(VIA6522_DDRA as u16, 0b1111_1111, false, true);
assert_eq!(x.dda, 0b1111_1111);
x.tick(0b0000_0000, VIA6522_DDRB, false, true);
x.tick(VIA6522_DDRB as u16, 0b0000_0000, false, true);
assert_eq!(x.ddb, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_DDRB, false, true);
x.tick(VIA6522_DDRB as u16, 0b1111_1111, false, true);
assert_eq!(x.ddb, 0b1111_1111);
x.tick(0b0000_0000, VIA6522_ORA, false, true);
assert_eq!(x.porta, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_ORA, false, true);
assert_eq!(x.porta, 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(0b0000_0000, VIA6522_ORB, false, true);
assert_eq!(x.portb, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_ORB, false, true);
assert_eq!(x.portb, 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(0b1010_1010, VIA6522_DDRA, false, true);
x.tick(0b1111_1111, VIA6522_ORA, false, true);
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(0b0101_0101, VIA6522_DDRB, false, true);
x.tick(0b1111_1111, VIA6522_ORB, false, true);
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);
}
}
+13 -10
View File
@@ -12,13 +12,13 @@ impl Mos6522 {
/// 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.gt( &self.offset) && address_bus.le(&self.max_address())) {
if !(address_bus >= self.offset && address_bus.le(&self.max_address())) {
return (address_bus, data_bus);
}
let local_address = address_bus - self.offset;
println!("Mos6522 Tick Start -> D:0x{data_bus:02x} / A:0x{address_bus:02x} / {rw} (Actual 0x{local_address:02x}");
println!("Mos6522 Tick Start -> D:0x{data_bus:02x} / A:0x{address_bus:02x} / {rw} (Actual 0x{local_address:02x} / 0b{local_address:08b})");
if reset {
// reset process
println!("Resetting Mos6522");
@@ -35,25 +35,28 @@ impl Mos6522 {
self.data_bus = data_bus;
match local_address as u8 {
VIA6522_DDRA => {
debug!("Setting DDA to 0x{data_bus:02x}");
println!("Setting DDA to 0x{data_bus:02x}");
// setting the Data Direction for Port A
self.dda = data_bus;
},
VIA6522_DDRB => {
debug!("Setting DDB to 0x{data_bus:02x}");
// setting the data direction for port b
self.ddb = data_bus;
},
VIA6522_ORB => {
// writing data to ORB
let masked_data = data_bus & self.ddb;
debug!("Setting ORB to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
println!("Setting ORB to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
self.orb = data_bus;
self.portb = masked_data;
},
VIA6522_DDRB => {
println!("Setting DDB to 0x{data_bus:02x}");
// setting the data direction for port b
self.ddb = data_bus;
},
VIA6522_ORA => {
// writing data to ORA
let masked_data = data_bus & self.dda;
debug!("Setting ORA to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
println!("Setting ORA to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
self.ora = data_bus;
self.porta = masked_data;
},
_ => {}
+1 -2
View File
@@ -12,8 +12,6 @@ use crate::periph::mos6522::mos6522::Mos6522;
/// 64 bytes RAM
/// IO Ports (A, B)
/// Timer
///
/// SEE ALSO Mos6532
pub struct Mos6530 {
pub(crate) data: [u8; SIZE_1KB],
pub(crate) ram: [u8; 64],
@@ -30,3 +28,4 @@ pub struct Mos6530 {
pub(crate) ram_offset: u16,
pub(crate) rom_offset: u16
}
View File