This commit is contained in:
2025-07-15 08:19:32 -04:00
parent b7e161ef0b
commit ff43a99e0c
48 changed files with 1368 additions and 197 deletions
+28
View File
@@ -0,0 +1,28 @@
pub mod new;
pub mod tick;
pub mod reset;
use std::fs;
use std::path::Path;
use crate::constants::constants_system::SIZE_1KB;
use crate::mos6502cpu::cpu::Mos6502Cpu;
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
use crate::periph::kim1_keypad::Kim1Keypad;
use crate::periph::mos6522::mos6522::Mos6522;
use crate::periph::mos6530::mos6530::Mos6530;
/// Represents a KIM-1
///
///
pub struct Kim1 {
pub running: bool,
pub cpu: Mos6502Cpu,
rriot1: Mos6530,
rriot2: Mos6530,
ram: Hm62256,
pub(crate) keypad: Kim1Keypad,
address_bus: u16,
data_bus: u8,
cpu_read: bool
}
+31
View File
@@ -0,0 +1,31 @@
use crate::computers::kim1::Kim1;
use crate::periph::hm62256::Hm62256;
use crate::periph::kim1_keypad::Kim1Keypad;
use crate::periph::mos6530::mos6530::Mos6530;
impl Kim1 {
pub fn dump(&self) {
println!("DUMPING KIM-1 PC STATE");
self.cpu.dump();
self.rriot1.dump();
self.rriot2.dump();
self.keypad.dump();
}
pub fn new() -> Self {
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-002_fillerbyte00-0x1c00.bin");
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-003_fillerbyte00-0x1800.bin");
Self {
cpu: Default::default(),
rriot1: Mos6530::new(0x1700, 0x1780, 0x1800, &rriot1_rom),
rriot2: Mos6530::new(0x1740, 0x17C0, 0x1C00, &rriot2_rom),
ram: Hm62256::new(0x0000),
keypad: Kim1Keypad::new(),
address_bus: 0,
data_bus: 0,
cpu_read: false,
running: false
}
}
}
+18
View File
@@ -0,0 +1,18 @@
use crate::computers::kim1::Kim1;
use crate::periph::hm62256::Hm62256;
use crate::periph::mos6530::mos6530::Mos6530;
impl Kim1 {
pub fn reset(&mut self) {
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-002_fillerbyte00-0x1c00.bin");
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-003_fillerbyte00-0x1800.bin");
self.cpu = Default::default();
self.rriot1 = Mos6530::new(0x1700, 0x1780, 0x1800, rriot1_rom.as_array().unwrap());
self.rriot2 = Mos6530::new(0x1740, 0x17c0, 0x1c00, rriot2_rom.as_array().unwrap());
self.ram = Hm62256::new(0x0000);
self.address_bus = 0x0000;
self.data_bus = 0x0000;
self.cpu_read = true;
self.cpu.pc = 0x0000;
}
}
+26
View File
@@ -0,0 +1,26 @@
use crate::computers::kim1::Kim1;
impl Kim1 {
pub fn tick(&mut self) {
println!("<- START KIM-1 Backplane Tick");
let (address_bus, data, rw) = self.cpu.tick2(self.address_bus, self.data_bus);
self.address_bus = address_bus;
self.data_bus = data;
self.cpu_read = rw;
// now tick the various items connected
self.rriot1.tick(self.address_bus, self.data_bus, false, self.cpu_read);
self.rriot2.tick(self.address_bus, self.data_bus, false, self.cpu_read);
self.ram.tick(self.address_bus, self.data_bus, self.cpu_read, true);
let (rr1_io, rr1_ram, rr1_rom) = self.rriot1.dump_data();
let (rr2_io, rr2_ram, rr2_rom) = self.rriot2.dump_data();
println!(" 0x0000 -> RAM / {}", self.ram.dump_data());
println!(" 0x1700 -> RRIOT 1 / 0x{rr1_io:04x}/0x{rr1_ram:04x}/0x{rr1_rom:04x}");
println!(" 0x1740 -> RRIOT 2 / 0x{rr2_io:04x}/0x{rr2_ram:04x}/0x{rr2_rom:04x}");
// display the memory map and device states
println!("-> FINISH KIM-1 Backplane Tick");
}
}
+1
View File
@@ -1,2 +1,3 @@
pub mod beneater;
pub mod rom_only;
pub mod kim1;
+13 -8
View File
@@ -1,21 +1,22 @@
use crate::constants::constants_system::{SIZE_32KB, SIZE_64KB};
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
use crate::periph::rom_chip::RomChip;
pub struct Backplane {
rom: Hm62256,
pub struct RomOnlyComputer {
rom: At28C256,
data_bus: u8,
address_bus: u16
}
impl Backplane {
pub fn new() -> Backplane {
Backplane::program(&[0x00; SIZE_32KB])
impl RomOnlyComputer {
pub fn new() -> RomOnlyComputer {
RomOnlyComputer::program(&[0x00; SIZE_32KB])
}
pub fn program(rom: &[u8; SIZE_32KB]) -> Backplane {
Backplane {
rom: *Hm62256::program(rom),
pub fn program(rom: &[u8; SIZE_32KB]) -> RomOnlyComputer {
RomOnlyComputer {
rom: *At28C256::new(0x000, rom),
address_bus: 0x0000,
data_bus: 0x00
}
@@ -23,6 +24,10 @@ impl Backplane {
pub fn tick(&mut self) {
println!("Preparing to tick.");
// do are we being addressed?
println!("Done ticking.");
}
+23
View File
@@ -0,0 +1,23 @@
pub const MOS6530_DRA: u8 = 0x00;
pub const MOS6530_DDRA: u8 = 0x01;
pub const MOS6530_DRB: u8 = 0x02;
pub const MOS6530_DDRB: u8 = 0x03;
/*
0 X Data Register A
1 X Data Direction Register A
2 X Data Register B
3 X Data Direction Register B
4 0 Count down from value, divide by 1, disable IRQ 1 ???
5 0 Count down from value, divide by 8, disable IRQ 1 ???
6 0 Count down from value, divide by 64, disable IRQ 1 Read current counter value, disable IRQ
7 0 Count down from value, divide by 1024, disable IRQ 1 Read counter status, bit7 = 1 means counter past zero
8 X Data Register A (mirror ?)
9 X Data Direction Register A (mirror ?)
A X Data Register B (mirror ?)
B X Data Direction Register B (mirror ?)
C 0 Count down from value, divide by 1, enable IRQ 1 ???
D 0 Count down from value, divide by 8, enable IRQ 1 ???
E 0 Count down from value, divide by 64, enable IRQ 1 Read current counter value, enable IRQ
F 0 Count down from value, divide by 1024, enable IRQ 1 Read counter status, bit7 = 1 means counter past zero
*/
+5 -1
View File
@@ -2,6 +2,10 @@ pub const SIZE_1KB: usize = 1024;
pub const SIZE_32KB: usize = SIZE_1KB * 32;
pub const SIZE_64KB: usize = SIZE_1KB * 64;
// S Suffixed constants are for indexing slices
pub const OFFSET_NMI_VECTOR: u16 = 0xfffa;
pub const OFFSET_NMI_VECTORS: usize = 0xfffa;
pub const OFFSET_RESET_VECTOR: u16 = 0xfffc;
pub const OFFSET_RESET_VECTORS: usize = 0xffff;
pub const OFFSET_INT_VECTOR: u16 = 0xfffe;
pub const OFFSET_INT_VECTORS: usize = 0xfffe;
+1
View File
@@ -2,3 +2,4 @@ pub mod constants_isa_op;
pub mod constants_isa_stub;
pub mod constants_system;
pub mod constants_via6522;
pub mod constants_mos6530;
+7 -9
View File
@@ -215,10 +215,10 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
table[ISA_OP_BCC as usize] = Some(OpInfo {
operation: BCC,
mode: AddressMode::Immediate,
mode: AddressMode::Implied,
length: 2,
cycles: 2,
format_prefix: "BCC #$",
format_prefix: "BCC $",
format_postfix: "",
});
@@ -229,7 +229,6 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
cycles: 2,
format_prefix: "BCS $",
format_postfix: "",
});
table[ISA_OP_BEQ as usize] = Some(OpInfo {
operation: BEQ,
@@ -267,7 +266,6 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
cycles: 2,
format_prefix: "BMI #$",
format_postfix: "",
});
table[ISA_OP_BNE as usize] = Some(OpInfo {
@@ -850,8 +848,8 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
mode: AddressMode::AbsoluteX,
length: 3,
cycles: 7,
format_prefix: "LSR $(",
format_postfix: ",X)",
format_prefix: "LSR $",
format_postfix: ",X",
});
table[ISA_OP_NOP as usize] = Some(OpInfo {
@@ -1221,15 +1219,15 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
mode: AddressMode::ZeroPageY,
length: 2,
cycles: 4,
format_prefix: "STX",
format_postfix: "",
format_prefix: "STX $",
format_postfix: ",Y",
});
table[ISA_OP_STX_ABS as usize] = Some(OpInfo {
operation: Operation::STX,
mode: AddressMode::Absolute,
length: 3,
cycles: 4,
format_prefix: "STX",
format_prefix: "STX $",
format_postfix: "",
});
+2
View File
@@ -1,3 +1,5 @@
#![feature(slice_as_array)]
pub mod computers;
pub mod address_mode;
pub mod constants;
+50 -2
View File
@@ -9,6 +9,8 @@ use crate::op_info::OpInfo;
use crate::operand::Operand;
use crate::operation::Operation;
use log::trace;
use crate::mos6502cpu::tick_stages::Mos6502TickStates;
use crate::mos6502cpu::tick_stages::Mos6502TickStates::*;
pub struct Mos6502Cpu {
pub(crate) memory: [u8; SIZE_64KB],
@@ -36,7 +38,9 @@ pub struct Mos6502Cpu {
/// CPU Read signal
pub read_signal: bool,
pub(crate) reset_vector: u16,
pub(crate) int_vector: u16
pub(crate) int_vector: u16,
pub(crate) nmi_vector: u16,
pub tick_stage: Mos6502TickStates
}
impl Mos6502Cpu {
@@ -74,7 +78,9 @@ impl Default for Mos6502Cpu {
ir_bytes: [0x00; 4],
read_signal: true,
reset_vector: 0x0000,
int_vector: 0x0000
int_vector: 0x0000,
nmi_vector: 0x0000,
tick_stage: LoadingInstruction
};
working.reset_cpu();
working
@@ -160,6 +166,48 @@ impl Mos6502Cpu {
/// Ticks the CPU
pub fn tick(&mut self) {
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
match self.tick_stage {
LoadingInstruction => {
println!("Loading instruction from data bus -> {}", self.data_bus);
let instruction = INSTRUCTION_TABLE[self.data_bus as usize].clone();
if let Some(inst) = instruction {
println!("DECODED INSTRUCTION [{:?}]/[{:?}]", inst.operation, inst.mode);
match inst.mode {
AddressMode::Absolute | AddressMode::AbsoluteX | AddressMode::AbsoluteY => {
println!("NEED TO LOAD a 16bit VALUE FOR INSTRUCTION");
self.tick_stage = Loading16BitParameter1;
}
AddressMode::Immediate => {
println!("LOADING A 8BIT VALUE FOR INSTRUCTION");
self.tick_stage = Loading8BitParameter;
}
_ => {}
}
} else {
println!("INVALID DECODE OF [${:02x}", self.data_bus);
}
}
Loading8BitParameter => {
println!("Loading parameter for 8bit ");
},
Loading16BitParameter1 => {
println!("Loading high bits of parameter");
},
Loading16BitParameter2 => {
println!("Loading low bits of parameter");
},
Stall(length) => {
println!("PREPARING TO STALL FOR {} CYCLES", length);
},
Waiting => {
println!("CPU IS WAITING.");
}
}
if self.microcode_step == 0 {
println!("OUT OF MICROSTEPS. Decoding the next instruction");
let offset = self.pc as usize;
+8 -4
View File
@@ -5,7 +5,7 @@ impl Mos6502Cpu {
///
/// returns
/// PC, A, X, Y, Address_Bus, Data_Bus, Microcode_Step
pub fn dump_data(&self) -> (u16, u8, u8, u8, u16, u8, u8, u16, u16) {
pub fn dump_data(&self) -> (u16, u8, u8, u8, u16, u8, u8, u16, u16, u16) {
(
self.pc,
self.a,
@@ -15,13 +15,14 @@ impl Mos6502Cpu {
self.data_bus,
self.microcode_step,
self.reset_vector,
self.int_vector
self.int_vector,
self.nmi_vector
)
}
pub fn dump(&self) {
println!(
"CPU State: PC: {:04x} / A: {:02x} / X: {:02x} / Y: {:02x} / ADDRESS: {:04x} / DATA: {:02x} / MICROSTEPS: {:02x} / S: {}",
"CPU State: PC: ${:04x} / A: ${:02x} / X: ${:02x} / Y: ${:02x} / ADDRESS: ${:04x} / DATA: ${:02x} / MICROSTEPS: {:02} / S: {} / NMI: ${:04x} / RST: ${:04x} / INT: ${:04x}",
self.pc,
self.a,
self.x,
@@ -29,7 +30,10 @@ impl Mos6502Cpu {
self.address_bus,
self.data_bus,
self.microcode_step,
self.flags.dump()
self.flags.dump(),
self.nmi_vector,
self.reset_vector,
self.int_vector
);
}
}
+2 -2
View File
@@ -2,5 +2,5 @@ pub mod cpu;
pub mod new;
pub mod tick2;
mod dbg;
mod tick_stages;
pub mod dbg;
pub mod tick_stages;
+19 -1
View File
@@ -1,6 +1,8 @@
use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR};
use crate::mos6502cpu::cpu::Mos6502Cpu;
impl Mos6502Cpu {
/// AccurateTick
///
@@ -19,6 +21,8 @@ impl Mos6502Cpu {
if self.read_signal {
// we should see new data in the data_bus for us
let read_data = data_bus;
println!("READ 0x{read_data:02x} from data bus.");
self.data_bus = read_data;
} else {
// we are writing to the bus.
}
@@ -28,6 +32,12 @@ impl Mos6502Cpu {
// reduce the number of remaining microsteps
self.read_signal = true;
match self.microcode_step {
6 => {
// NMI High byte
}
5 => {
// NMI low byte
}
4 => {
// read first byte of reset vector
self.address_bus = OFFSET_RESET_VECTOR;
@@ -35,6 +45,7 @@ impl Mos6502Cpu {
3 => {
// at this point data holds the upper byte of our reset vector
self.reset_vector = (data_bus as u16) << 8;
println!("Loaded reset vector of 0x{:04x}", self.reset_vector);
// read secondd byte of reset vector
self.address_bus = OFFSET_RESET_VECTOR + 1;
}
@@ -53,11 +64,18 @@ impl Mos6502Cpu {
println!("Loaded interrupt vector of 0x{:04x}", self.int_vector);
self.pc = self.reset_vector;
println!("Set PC to Reset Vector. Giddy-up!");
println!("START HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK");
// the KIM-1 uses 0x0000 for its initial PC
self.pc = 0x0000;
println!("END HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK");
self.has_reset = true;
}
_ => {
}
}
self.microcode_step -= 1;
if self.microcode_step > 0 {
self.microcode_step -= 1;
}
}
(self.address_bus, self.data_bus, self.read_signal)
}
+3 -3
View File
@@ -3,7 +3,7 @@
///
/// The set of what a tick can be doing
///
enum Mos6502TickStates {
pub enum Mos6502TickStates {
/// Loading the first byte into the IR
LoadingInstruction,
/// Loading an 8 bit parameter
@@ -14,6 +14,6 @@ enum Mos6502TickStates {
Loading16BitParameter2,
/// Stalling for accurate emulation
Stall(u8),
/// Completed the instruction
Complete
/// Waiting for the next instruction
Waiting
}
+5 -1
View File
@@ -9,7 +9,11 @@ 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 }
At28C256 { data: boxed_array,
address_bus: 0x0000,
data_bus: 0x00,
offset: 0x0000
}
}
}
+13
View File
@@ -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
}
}
}
+5
View File
@@ -3,6 +3,7 @@ pub mod rom_chip;
pub mod tick;
mod new;
mod program;
mod dump;
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::rom_chip::RomChip;
@@ -15,7 +16,11 @@ use std::io::Read;
/// 256kbit storage
/// 32kbyte storage
pub struct At28C256 {
data_bus: u8,
address_bus: u16,
data: Box<[u8; SIZE_32KB]>,
// where in the computer memory map do we live?
offset: u16
}
#[cfg(test)]
+5 -2
View File
@@ -2,9 +2,12 @@ use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
impl At28C256 {
pub fn new(data: &[u8; SIZE_32KB]) -> Self {
pub fn new(offset: u16, data: &[u8; SIZE_32KB]) -> Self {
At28C256 {
data: (*data).into()
data: (*data).into(),
address_bus: 0x0000,
data_bus: 0x00,
offset
}
}
}
+15 -6
View File
@@ -1,14 +1,23 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::at28c256::At28C256;
use crate::periph::hm62256::Hm62256;
impl At28C256 {
fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool) -> (u16, u8) {
if read_mode {
panic!("UNABLE TO WRITE TO ROM");
} else {
// has to be read mode. its a rom.
return (address_bus, data_bus)
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) -> (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)
}
}
// not for us.
(address_bus, self.data[address_bus as usize])
}
}
+3
View File
@@ -0,0 +1,3 @@
pub trait BusDevice {
fn talking_to_me(&self, address: u16) -> bool;
}
+17
View File
@@ -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
}
}
}
+17
View File
@@ -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
}
}
+11 -15
View File
@@ -3,27 +3,23 @@
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;
pub struct Hm62256 {
pub(crate) base_offset: u16,
pub(crate) data: Box<[u8]>,
}
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 {
base_offset: 0x0000,
data: boxed_array,
}
}
/// 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)]
+13
View File
@@ -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
}
}
}
+33 -8
View File
@@ -1,16 +1,41 @@
use crate::constants::constants_system::SIZE_32KB;
use crate::periph::hm62256::Hm62256;
impl Hm62256 {
fn tick(&mut self, address_bus: u16, data_bus: u8, read_mode: bool) -> (u16, u8) {
let new_data_bus = if read_mode {
// reading from ram
self.data[address_bus as usize]
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) {
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;
// 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[address_bus as usize] = data_bus.into();
self.data[addr as usize] = data_bus.into();
data_bus
};
(address_bus, new_data_bus)
(self.address_bus, self.data_bus)
}
}
@@ -26,9 +51,9 @@ mod test {
let mut ram = Hm62256::default();
// load the data to ram
ram.tick(0x0000, 0xab, false);
ram.tick(0x0000, 0xab, false, true);
// read the data back
let (_, new_data) = ram.tick(0x0000, 0x00, true);
let (_, new_data) = ram.tick(0x0000, 0x00, true, true);
assert_eq!(new_data, 0xab);
}
+105
View File
@@ -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));
}
}
+3 -1
View File
@@ -1,6 +1,8 @@
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;
+3 -1
View File
@@ -1,2 +1,4 @@
pub mod mos6522;
mod registers;
mod registers;
mod new;
mod tick;
+39 -99
View File
@@ -5,106 +5,47 @@ use crate::constants::constants_via6522::*;
#[derive(Default)]
pub struct Mos6522 {
/// data direction
dda: u8,
ddb: u8,
pub(crate) dda: u8,
pub(crate) ddb: u8,
/// bottom 4 address bits
rs0: u8,
rs1: u8,
rs2: u8,
rs3: u8,
pub(crate) rs0: u8,
pub(crate) rs1: u8,
pub(crate) rs2: u8,
pub(crate) rs3: u8,
/// external data bus
data_bus: u8,
pub(crate) data_bus: u8,
cs1: bool,
cs2: bool,
rw: bool,
pub(crate) cs1: bool,
pub(crate) cs2: bool,
// when true CPU is reading
pub(crate) rw: bool,
/// reset circuit - true when reset inited
reset: bool,
pub(crate) reset: bool,
/// IRQ - true when interrupt waiting
irq: bool,
pub(crate) irq: bool,
ira: u8,
ora: u8,
porta: u8,
irb: u8,
orb: u8,
portb: u8,
pub(crate) ira: u8,
pub(crate) ora: u8,
pub(crate) porta: u8,
pub(crate) irb: u8,
pub(crate) orb: u8,
pub(crate) portb: u8,
ca1: bool,
ca2: bool,
cb1: bool,
cb2: bool,
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 new() -> Self {
Mos6522::default()
}
/// tick
///
/// data_bus -> 8 bits from the data bus
/// control -> 4 bits to identify which register to control
pub fn tick(&mut self, data_bus: u8, control: u8, rw: bool) -> (u8) {
println!("Mos6522 Tick Start -> 0x{data_bus:02x} / 0x{control:02x} / {rw}");
if rw {
// RW true = CPU is writing
self.data_bus = data_bus;
match control {
VIA6522_DDRA => {
debug!("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}");
self.portb = masked_data;
},
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}");
self.porta = masked_data;
},
_ => {}
}
} else {
// RW false = CPU is reading
self.data_bus = match control {
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}", control, control);
// do nothing. bad address for VIA
self.data_bus
}
}
}
(self.data_bus)
}
pub fn start_clocks(&mut self) {
loop {
let cycle_start = Instant::now();
@@ -115,7 +56,6 @@ impl Mos6522 {
}
}
#[cfg(test)]
mod test {
use super::*;
@@ -126,40 +66,40 @@ mod test {
#[test]
fn registers() {
let mut x = Mos6522::new();
x.tick(0b0000_0000, VIA6522_DDRA, true);
x.tick(0b0000_0000, VIA6522_DDRA, false, true);
assert_eq!(x.dda, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_DDRA, true);
x.tick(0b1111_1111, VIA6522_DDRA, false, true);
assert_eq!(x.dda, 0b1111_1111);
x.tick(0b0000_0000, VIA6522_DDRB, true);
x.tick(0b0000_0000, VIA6522_DDRB, false, true);
assert_eq!(x.ddb, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_DDRB, true);
x.tick(0b1111_1111, VIA6522_DDRB, false, true);
assert_eq!(x.ddb, 0b1111_1111);
x.tick(0b0000_0000, VIA6522_ORA, true);
x.tick(0b0000_0000, VIA6522_ORA, false, true);
assert_eq!(x.porta, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_ORA, true);
x.tick(0b1111_1111, VIA6522_ORA, false, true);
assert_eq!(x.porta, 0b1111_1111);
x.tick(0b0000_0000, VIA6522_ORB, true);
x.tick(0b0000_0000, VIA6522_ORB, false, true);
assert_eq!(x.portb, 0b0000_0000);
x.tick(0b1111_1111, VIA6522_ORB, true);
x.tick(0b1111_1111, VIA6522_ORB, false, true);
assert_eq!(x.portb, 0b1111_1111);
}
#[test]
fn partial_output_porta() {
let mut x = Mos6522::new();
x.tick(0b1010_1010, VIA6522_DDRA, true);
x.tick(0b1111_1111, VIA6522_ORA, true);
x.tick(0b1010_1010, VIA6522_DDRA, false, true);
x.tick(0b1111_1111, VIA6522_ORA, false, true);
assert_eq!(x.porta, 0b1010_1010);
}
#[test]
fn partial_output_portb() {
let mut x = Mos6522::new();
x.tick(0b0101_0101, VIA6522_DDRB, true);
x.tick(0b1111_1111, VIA6522_ORB, true);
x.tick(0b0101_0101, VIA6522_DDRB, false, true);
x.tick(0b1111_1111, VIA6522_ORB, false, true);
assert_eq!(x.portb, 0b0101_0101);
}
}
+7
View File
@@ -0,0 +1,7 @@
use crate::periph::mos6522::mos6522::Mos6522;
impl Mos6522 {
pub fn new() -> Self {
Mos6522::default()
}
}
+86
View File
@@ -0,0 +1,86 @@
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.gt( &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}");
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 => {
debug!("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}");
self.portb = masked_data;
},
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}");
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)
}
}
+11
View File
@@ -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)
}
}
+4
View File
@@ -0,0 +1,4 @@
pub mod mos6530;
pub mod tick;
mod new;
mod dump;
+32
View File
@@ -0,0 +1,32 @@
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
///
/// SEE ALSO Mos6532
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
}
+25
View File
@@ -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
}
}
}
+26
View File
@@ -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}");
}
}
}