Lots of stuff.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::constants::constants_test::*;
|
||||
|
||||
impl At28C256 {
|
||||
/// checksum
|
||||
///
|
||||
/// calculates and returns the checksum for the loaded binary.
|
||||
/// files with all zero will calculate to zero
|
||||
pub fn checksum(&self) -> u8 {
|
||||
At28C256::checksum_static(&self.data[..])
|
||||
}
|
||||
|
||||
pub fn checksum_static(data: &[u8]) -> u8 {
|
||||
data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn programmed_data_reads_back_same() {
|
||||
let mut data = At28C256::default();
|
||||
for i in 0..SIZE_32KB {
|
||||
data.data[i] = 0xeau8;
|
||||
}
|
||||
for offset in 0..SIZE_32KB {
|
||||
if offset.is_multiple_of(SIZE_1KB) {};
|
||||
assert_eq!(0xea, data.read(&(offset as u16)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_correctly_for_zero() {
|
||||
let data1 = [0x00u8; SIZE_32KB];
|
||||
assert_eq!(0x00, At28C256::checksum_static(&data1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_1_byte() {
|
||||
let data = [0xff; 1];
|
||||
assert_eq!(0xff, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_2_bytes() {
|
||||
let data = [0xff; 2];
|
||||
// 0xff + 0xff = 0x1fe
|
||||
assert_eq!(0xfe, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_first_80_bytes() {
|
||||
println!("STARTING TEST");
|
||||
let mut checksum = 0x00;
|
||||
|
||||
let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin");
|
||||
println!("READING [{path}]");
|
||||
let data = fs::read(path);
|
||||
match data {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes", bytes.len());
|
||||
checksum = At28C256::checksum_static(&bytes);
|
||||
println!("Checksum: 0x{:02x}", checksum);
|
||||
}
|
||||
Err(e) => eprintln!("Failed to read file: {}", e),
|
||||
}
|
||||
assert_eq!(0x58, checksum);
|
||||
println!("TEST COMPLETE");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Default for At28C256 {
|
||||
fn default() -> Self {
|
||||
let vec = vec![0xea; SIZE_32KB];
|
||||
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
|
||||
let boxed_array: Box<[u8; SIZE_32KB]> = boxed_slice
|
||||
.try_into()
|
||||
.expect("Failed to convert Vec to boxed array");
|
||||
At28C256 {
|
||||
data: boxed_array,
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00,
|
||||
offset: 0x0000,
|
||||
max_offset: 0x3fff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::periph::at28c256::At28C256;
|
||||
|
||||
pub struct At28C256State {
|
||||
offset: u16
|
||||
}
|
||||
|
||||
impl At28C256 {
|
||||
pub fn dump(&self) -> At28C256State {
|
||||
At28C256State {
|
||||
offset: self.offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
pub mod default;
|
||||
pub mod rom_chip;
|
||||
pub mod tick;
|
||||
mod new;
|
||||
mod program;
|
||||
mod dump;
|
||||
mod checksum;
|
||||
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use std::io::Read;
|
||||
|
||||
/// At28C256
|
||||
///
|
||||
/// Represents a single At28C256 EEPROM Chip
|
||||
///
|
||||
/// 256kbit storage
|
||||
/// 32kbyte storage
|
||||
pub struct At28C256 {
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
data: Box<[u8]>,
|
||||
// where in the computer memory map do we live?
|
||||
offset: u16,
|
||||
max_offset: u16
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
|
||||
impl At28C256 {
|
||||
pub fn new(offset: u16, max_offset: u16, data: Vec<u8>) -> Self {
|
||||
println!("NEW At28C256 with checksum ${:02x}", At28C256::checksum_static(&data[..]));
|
||||
|
||||
At28C256 {
|
||||
data: data.into_boxed_slice(),
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00,
|
||||
offset,
|
||||
max_offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
|
||||
impl At28C256 {
|
||||
pub fn program(&mut self, new_program: Box<[u8]>) {
|
||||
// panic!("FAIL. Cant program the chip.");
|
||||
// println!("PROGRAMMING {:?}", new_program);
|
||||
self.data = new_program;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true) }
|
||||
|
||||
#[test]
|
||||
fn programming_chip_changes_contents() {
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, vec![]);
|
||||
|
||||
assert_eq!(0x00, chip.read(&0x0000));
|
||||
|
||||
let new_data: Vec<u8> = vec![0xff, 0xff, 0xff, 0xff];
|
||||
chip.program(new_data.into());
|
||||
assert_eq!(0xff, chip.read(&0x0000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
impl RomChip for At28C256 {
|
||||
/// read
|
||||
///
|
||||
/// Reads a byte from memory.
|
||||
/// Returns a 0x00 if there is no data at that location but is still in ROM address range
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
println!("STARTING READ FROM At28C256 ${:04x} | ${:04x} | ${:04x}", self.offset, offset, self.max_offset);
|
||||
if offset < &self.offset || offset > &self.max_offset {
|
||||
println!("Unable to read from ${offset:04x} as it it out of range.");
|
||||
return 0x00;
|
||||
} else {
|
||||
println!("OK READ FROM GOOD AREA total len = {}", self.data.len());
|
||||
}
|
||||
|
||||
if *offset >= self.data.len() as u16 {
|
||||
0x00
|
||||
} else {
|
||||
self.data[*offset as usize]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// program
|
||||
///
|
||||
/// Writes new data to the memory chip
|
||||
fn program(new_data: &[u8; SIZE_32KB]) -> Box<At28C256> {
|
||||
println!("Writing new chip.");
|
||||
let mut working = At28C256::default();
|
||||
working.data = Box::new(*new_data);
|
||||
working.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl At28C256 {
|
||||
fn talking_to_me(&self, address: u16) -> bool {
|
||||
address >= self.offset && address < self.max_offset
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool) -> (u16, u8) {
|
||||
println!("At28C256: Tick starting for A${address_bus:04x} D${data_bus:02x} R{read_mode}");
|
||||
|
||||
// we aren't being addressed
|
||||
// OR
|
||||
// we arent reading from the ROM...
|
||||
if !self.talking_to_me(address_bus) ||
|
||||
!read_mode {
|
||||
// ...go away.
|
||||
return (address_bus, data_bus)
|
||||
}
|
||||
|
||||
let effective = address_bus - self.offset;
|
||||
if effective < self.max_offset {
|
||||
if effective < self.data.len() as u16 {
|
||||
self.data_bus = self.data[effective as usize];
|
||||
} else {
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
} else {
|
||||
println!("At28C256: OUTSIDE RANGE. :(");
|
||||
return (address_bus, data_bus)
|
||||
}
|
||||
|
||||
println!("At28C256: Read... {:02x}", self.data_bus);
|
||||
println!("At28C256: Done with ticking the AtC256");
|
||||
(address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn checksum_binary_loads() {
|
||||
let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin";
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes.", bytes.len());
|
||||
bytes
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("FAIL to read rom.");
|
||||
panic!("No rom no run.");
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let mut rom = At28C256::new(0x0000, 0x3fff, bytes);
|
||||
|
||||
assert_eq!(rom.checksum(), 0x58);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pub trait Backplane {
|
||||
fn data_bus(&self) -> u8;
|
||||
fn address_bus(&self) -> u16;
|
||||
fn read_mode(&self) -> bool;
|
||||
fn set_read_mode(&mut self, new_mode: bool);
|
||||
fn set_data_bus(&mut self, new_value: u8);
|
||||
fn set_address_bus(&mut self, new_value: u16);
|
||||
fn tick(&mut self);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub trait BusDevice {
|
||||
fn talking_to_me(&self, address: u16) -> bool;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Default for Hm62256 {
|
||||
fn default() -> Self {
|
||||
let vec = vec![0x00; SIZE_32KB];
|
||||
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
|
||||
let boxed_array: Box<[u8; SIZE_32KB]> =
|
||||
boxed_slice.try_into().expect("Unable to box the ram");
|
||||
Hm62256 {
|
||||
offset: 0x0000,
|
||||
data: boxed_array,
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
pub struct Hm62256State {
|
||||
pub offset: u16
|
||||
}
|
||||
|
||||
impl Hm62256 {
|
||||
pub fn dump(&self) -> Hm62256State {
|
||||
Hm62256State {
|
||||
offset: self.offset
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump_data(&self) -> (u16) {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// HM62256 Static Ram
|
||||
|
||||
pub mod ramchip;
|
||||
pub mod romchip;
|
||||
pub mod tick;
|
||||
pub mod default;
|
||||
pub mod new;
|
||||
pub mod dump;
|
||||
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::ram_chip::RamChip;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
use log::debug;
|
||||
|
||||
/// Hitachi Semiconductor
|
||||
/// 8 Bit High Speed Static Ram
|
||||
/// 32KByte
|
||||
pub struct Hm62256 {
|
||||
pub(crate) offset: u16,
|
||||
pub(crate) data: Box<[u8]>,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) data_bus: u8
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rand::random;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn written_data_comes_back() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// 100,000 random read/writes to ram that all read back right
|
||||
for _ in 0..100_000 {
|
||||
let mut offset: u16 = random();
|
||||
println!("Size = {SIZE_32KB}");
|
||||
let value: u8 = random();
|
||||
println!("Wrote [{value:02x}] to [{offset:04x}]");
|
||||
ram.write(&offset, &value);
|
||||
|
||||
assert_eq!(ram.read(&offset), value)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_space_is_round() {
|
||||
// addresses written past the last address 'loop' back to 0+(offset - MAX_SIZE)
|
||||
let max_offset = SIZE_32KB;
|
||||
let test_offset = max_offset;
|
||||
|
||||
// all zero
|
||||
let mut ram = Hm62256::default();
|
||||
// write FF to the addresss after the last
|
||||
ram.write(&(test_offset as u16), &0xff);
|
||||
|
||||
// check all the ram for anything that isn't 0x00
|
||||
|
||||
assert_eq!(ram.read(&(0x0000)), 0xff);
|
||||
for offset in 1..SIZE_32KB {
|
||||
println!("Testing offset {offset:04x} for 0x00");
|
||||
assert_eq!(ram.read(&(offset as u16)), 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Hm62256 {
|
||||
pub fn new(base_offset: u16) -> Self {
|
||||
Self {
|
||||
offset: base_offset,
|
||||
data: vec![0; SIZE_32KB].into_boxed_slice(),
|
||||
address_bus: 0x0000,
|
||||
data_bus: 0x00
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::ram_chip::RamChip;
|
||||
|
||||
impl RamChip for Hm62256 {
|
||||
fn write(&mut self, offset: &u16, value: &u8) {
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
println!("Writing at E[{effective:04x}] / O[{offset:04x}]");
|
||||
self.data[effective as usize] = *value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
use log::debug;
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
impl RomChip for Hm62256 {
|
||||
|
||||
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
// loops memory around past 32k
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
self.data[effective as usize]
|
||||
}
|
||||
|
||||
fn program(_: &[u8; SIZE_32KB]) -> Box<Self> {
|
||||
debug!("Dont program ram.");
|
||||
Hm62256::default().into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
|
||||
impl Hm62256 {
|
||||
fn max_address(&self) -> u16 {
|
||||
self.offset + SIZE_32KB as u16
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool, cs: bool) -> (u16, u8) {
|
||||
println!("HM62256RAM TICK START -> 0x{address_bus:04x} 0x{data_bus:02x} {read_mode} {cs}");
|
||||
if !(address_bus >= self.offset && address_bus < self.max_address()) {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
self.address_bus = address_bus;
|
||||
self.data_bus = data_bus;
|
||||
let addr = address_bus.wrapping_sub(self.offset) + self.offset;
|
||||
|
||||
// did we want to talk to the chip...
|
||||
if !cs {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
// ...or are we outside the range?
|
||||
if (addr - self.offset) > SIZE_32KB as u16 {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
|
||||
// ok. lets see what we are dealing with
|
||||
self.data_bus = if read_mode {
|
||||
self.data[addr as usize]
|
||||
} else {
|
||||
// writing to ram
|
||||
self.data[addr as usize] = data_bus.into();
|
||||
data_bus
|
||||
};
|
||||
(self.address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn write_to_memory_read_back_works_at_0() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// load the data to ram
|
||||
ram.tick(0x0000, 0xab, false, true);
|
||||
// read the data back
|
||||
let (_, new_data) = ram.tick(0x0000, 0x00, true, true);
|
||||
|
||||
assert_eq!(new_data, 0xab);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
+---+---+---+---+---+---+
|
||||
| 0 | 1 | 2 | 3 | 4 | 5 |
|
||||
+---+---+---+---+---+---+
|
||||
| 6 | 7 | 8 | 9 | A | B |
|
||||
+---+---+---+---+---+---+
|
||||
| C | D | E | F | AD| DA|
|
||||
+---+---+---+---+---+---+
|
||||
| + | PC| ST| RS| | |
|
||||
+---+---+---+---+---+---+
|
||||
*/
|
||||
|
||||
pub struct Kim1Keypad {
|
||||
keys: [bool; 23],
|
||||
stepping: bool
|
||||
}
|
||||
|
||||
impl Kim1Keypad {
|
||||
pub fn dump(&self) {
|
||||
println!("Dumping state of keypad");
|
||||
}
|
||||
}
|
||||
|
||||
impl Kim1Keypad {
|
||||
fn keyid(from: u8) -> usize{
|
||||
(from % 23) as usize
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Kim1Keypad {
|
||||
keys: [false; 23],
|
||||
stepping: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_stepping(&mut self) {
|
||||
self.stepping = !self.stepping;;
|
||||
}
|
||||
|
||||
pub fn set_stepping(&mut self, new_state: bool) {
|
||||
self.stepping = new_state
|
||||
}
|
||||
|
||||
pub fn press_key(&mut self, key_to_press: u8) {
|
||||
self.keys[Self::keyid(key_to_press)] = true;
|
||||
}
|
||||
|
||||
pub fn release_key(&mut self, key_to_release: u8) {
|
||||
self.keys[Self::keyid(key_to_release)] = false;
|
||||
}
|
||||
|
||||
pub fn is_pressed(&self, key: u8) -> bool {
|
||||
self.keys[Self::keyid(key)]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn keys_are_pressed() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
for index in 0..23 {
|
||||
assert!(!kb.is_pressed(index));
|
||||
kb.press_key(index);
|
||||
assert!(kb.is_pressed(index));
|
||||
|
||||
kb.release_key(index);
|
||||
assert!(!kb.is_pressed(index));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stepping_changes() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.set_stepping(false);
|
||||
|
||||
assert!(!kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(!kb.stepping);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn out_of_range() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.press_key(24);
|
||||
assert!(kb.is_pressed(1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pub mod rom_chip;
|
||||
pub mod at28c256;
|
||||
pub mod hm62256;
|
||||
pub mod ram_chip;
|
||||
pub mod mos6522;
|
||||
pub mod mos6530;
|
||||
pub mod kim1_keypad;
|
||||
mod bus_device;
|
||||
pub mod backplane;
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod mos6522;
|
||||
mod registers;
|
||||
mod new;
|
||||
mod tick;
|
||||
@@ -0,0 +1,109 @@
|
||||
use std::time::Instant;
|
||||
use log::debug;
|
||||
use crate::constants::constants_via6522::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mos6522 {
|
||||
/// data direction
|
||||
pub(crate) dda: u8,
|
||||
pub(crate) ddb: u8,
|
||||
|
||||
/// bottom 4 address bits
|
||||
pub(crate) rs0: u8,
|
||||
pub(crate) rs1: u8,
|
||||
pub(crate) rs2: u8,
|
||||
pub(crate) rs3: u8,
|
||||
|
||||
/// external data bus
|
||||
pub(crate) data_bus: u8,
|
||||
|
||||
pub(crate) cs1: bool,
|
||||
pub(crate) cs2: bool,
|
||||
// when true CPU is reading
|
||||
pub(crate) rw: bool,
|
||||
|
||||
/// reset circuit - true when reset inited
|
||||
pub(crate) reset: bool,
|
||||
|
||||
/// IRQ - true when interrupt waiting
|
||||
pub(crate) irq: bool,
|
||||
|
||||
pub(crate) ira: u8,
|
||||
pub(crate) ora: u8,
|
||||
pub(crate) porta: u8,
|
||||
pub(crate) irb: u8,
|
||||
pub(crate) orb: u8,
|
||||
pub(crate) portb: u8,
|
||||
|
||||
pub(crate) ca1: bool,
|
||||
pub(crate) ca2: bool,
|
||||
pub(crate) cb1: bool,
|
||||
pub(crate) cb2: bool,
|
||||
|
||||
// memory offset for where in the computers memory map this fits
|
||||
pub(crate) offset: u16,
|
||||
pub(crate) address_bus: u16,
|
||||
}
|
||||
|
||||
impl Mos6522 {
|
||||
pub fn max_offset(&self) -> u16 {
|
||||
self.offset + 0x10
|
||||
}
|
||||
|
||||
pub fn start_clocks(&mut self) {
|
||||
loop {
|
||||
let cycle_start = Instant::now();
|
||||
// let duration = cycle_start.duration_since(self.clock);
|
||||
// set the time to the new time.
|
||||
// self.clock = cycle_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn registers() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.dda, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.dda, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_DDRB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ddb, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ddb, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ora, 0b0000_0000);
|
||||
x.tick(VIA6522_ORA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ora, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.orb, 0b0000_0000);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.orb, 0b1111_1111);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_porta() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRA as u16, 0b1010_1010, false, true);
|
||||
x.tick(VIA6522_ORA as u16,0b1111_1111, false, true);
|
||||
assert_eq!(x.porta, 0b1010_1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_portb() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRB as u16, 0b0101_0101, false, true);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.portb, 0b0101_0101);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
pub fn new() -> Self {
|
||||
Mos6522::default()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
pub enum Via6522Registers {
|
||||
ORA,
|
||||
ORB,
|
||||
DDRA,
|
||||
DDRB,
|
||||
T1WL,
|
||||
T1CL,
|
||||
T1CH,
|
||||
T1LL,
|
||||
T2LL,
|
||||
T2CH,
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use log::debug;
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::constants::constants_via6522::{VIA6522_DDRA, VIA6522_DDRB, VIA6522_ORA, VIA6522_ORB};
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
fn max_address(&self) -> u16 {
|
||||
self.offset + SIZE_32KB as u16
|
||||
}
|
||||
/// tick
|
||||
///
|
||||
/// data_bus -> 8 bits from the data bus
|
||||
/// control -> 4 bits to identify which register to control
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8,reset: bool, rw: bool) -> (u16, u8) {
|
||||
if !(address_bus >= self.offset && address_bus.le(&self.max_address())) {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
let local_address = address_bus - self.offset;
|
||||
|
||||
println!("Mos6522 Tick Start -> D:0x{data_bus:02x} / A:0x{address_bus:02x} / {rw} (Actual 0x{local_address:02x} / 0b{local_address:08b})");
|
||||
if reset {
|
||||
// reset process
|
||||
println!("Resetting Mos6522");
|
||||
self.data_bus = data_bus;
|
||||
self.dda = 0x00;
|
||||
self.ddb = 0x00;
|
||||
self.porta = 0x00;
|
||||
self.portb = 0x00;
|
||||
return (self.address_bus, self.data_bus)
|
||||
}
|
||||
|
||||
if rw {
|
||||
// RW true = CPU is writing
|
||||
self.data_bus = data_bus;
|
||||
match local_address as u8 {
|
||||
VIA6522_DDRA => {
|
||||
println!("Setting DDA to 0x{data_bus:02x}");
|
||||
// setting the Data Direction for Port A
|
||||
self.dda = data_bus;
|
||||
},
|
||||
VIA6522_ORB => {
|
||||
// writing data to ORB
|
||||
let masked_data = data_bus & self.ddb;
|
||||
println!("Setting ORB to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
|
||||
self.orb = data_bus;
|
||||
self.portb = masked_data;
|
||||
},
|
||||
VIA6522_DDRB => {
|
||||
println!("Setting DDB to 0x{data_bus:02x}");
|
||||
// setting the data direction for port b
|
||||
self.ddb = data_bus;
|
||||
},
|
||||
|
||||
VIA6522_ORA => {
|
||||
// writing data to ORA
|
||||
let masked_data = data_bus & self.dda;
|
||||
println!("Setting ORA to 0x{data_bus:02x} / masked at 0x{masked_data:02x}");
|
||||
self.ora = data_bus;
|
||||
self.porta = masked_data;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
// RW false = CPU is reading
|
||||
self.data_bus = match local_address as u8 {
|
||||
VIA6522_DDRA => {
|
||||
self.dda
|
||||
}
|
||||
VIA6522_DDRB => {
|
||||
self.ddb
|
||||
}
|
||||
VIA6522_ORA => {
|
||||
self.porta & self.dda
|
||||
}
|
||||
VIA6522_ORB => {
|
||||
self.portb & self.ddb
|
||||
}
|
||||
_ => {
|
||||
debug!("VIA got request for b{:08b} / 0x{:02x}", address_bus, address_bus);
|
||||
// do nothing. bad address for VIA
|
||||
self.data_bus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(self.address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn dump(&self) {
|
||||
println!("Dumping state of Mos6530 RRIOT");
|
||||
}
|
||||
|
||||
pub fn dump_data(&self) -> (u16, u16, u16) {
|
||||
(self.io_offset, self.ram_offset, self.rom_offset)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod mos6530;
|
||||
pub mod tick;
|
||||
mod new;
|
||||
mod dump;
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::constants::constants_system::*;
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
/// Mos6530 RRIOT
|
||||
/// Ram/Rom/IO/Timer
|
||||
///
|
||||
/// Represents a single Mos6530 RRIOT Chip
|
||||
///
|
||||
/// Used in the TIM-1, KIM-1
|
||||
///
|
||||
/// 1kb Rom
|
||||
/// 64 bytes RAM
|
||||
/// IO Ports (A, B)
|
||||
/// Timer
|
||||
pub struct Mos6530 {
|
||||
pub(crate) data: [u8; SIZE_1KB],
|
||||
pub(crate) ram: [u8; 64],
|
||||
pub(crate) porta: u8,
|
||||
pub(crate) portb: u8,
|
||||
pub(crate) data_bus: u8,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) cs1: bool,
|
||||
pub(crate) cs2: bool,
|
||||
// when true, CPU is reading
|
||||
pub(crate) rw: bool,
|
||||
pub(crate) reset: bool,
|
||||
pub(crate) io_offset: u16,
|
||||
pub(crate) ram_offset: u16,
|
||||
pub(crate) rom_offset: u16
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn new(io_offset: u16,
|
||||
ram_offset: u16,
|
||||
rom_offset: u16,
|
||||
data: &[u8; SIZE_1KB]) -> Self {
|
||||
Mos6530 {
|
||||
data: *data,
|
||||
ram: [0x00; 64],
|
||||
porta: 0,
|
||||
portb: 0,
|
||||
data_bus: 0,
|
||||
address_bus: 0,
|
||||
cs1: false,
|
||||
cs2: false,
|
||||
rw: false,
|
||||
reset: false,
|
||||
io_offset,
|
||||
ram_offset,
|
||||
rom_offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, reset: bool, rw: bool) {
|
||||
debug!("Starting tick of MOS6530 RRIOT with 0x{address_bus:04x} / 0b{data_bus:08b} / R:{reset} / RW:{rw} (OFFSETS: I{:04x}, RA{:04x}, RO{:04x})", self.io_offset, self.ram_offset, self.rom_offset);
|
||||
let io_max = self.io_offset + 0x3f;
|
||||
let ram_max = self.ram_offset + 0x3f;
|
||||
let rom_max = self.rom_offset + 0x400;
|
||||
|
||||
if address_bus.ge(&self.io_offset) && address_bus.le(&io_max) {
|
||||
let effective = address_bus - self.io_offset;
|
||||
println!("IO Activity at effective 0x{effective:02x}");
|
||||
}
|
||||
|
||||
if address_bus.ge(&self.ram_offset) && address_bus.le(&ram_max) {
|
||||
let effective = address_bus - self.ram_offset;
|
||||
println!("RAM Activity at effective 0x{effective:02x}");
|
||||
}
|
||||
|
||||
if address_bus.ge(&self.rom_offset) && address_bus.le(&rom_max) {
|
||||
let effective = address_bus - self.rom_offset;
|
||||
println!("Rom Activity at effective 0x{effective:02x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
pub trait RamChip: RomChip {
|
||||
fn write(&mut self, offset: &u16, value: &u8);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
|
||||
pub trait RomChip {
|
||||
/// Read
|
||||
///
|
||||
/// Reads a single byte from the specified address
|
||||
fn read(&self, offset: &u16) -> u8;
|
||||
/// Program
|
||||
///
|
||||
/// Replaces all data in the ROM chip
|
||||
fn program(new_data: &[u8; SIZE_32KB]) -> Box<Self>;
|
||||
}
|
||||
Reference in New Issue
Block a user