more chips
more docs
This commit is contained in:
Generated
+8
@@ -1102,6 +1102,14 @@ dependencies = [
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
"eframe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.27.2"
|
||||
|
||||
@@ -4,6 +4,7 @@ members = [
|
||||
"cli",
|
||||
"macroquad",
|
||||
# "beneater"
|
||||
"egui"
|
||||
]
|
||||
resolver="2"
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
use std::fs;
|
||||
use core::computers::ram_rom::RamRomComputer;
|
||||
use core::traits::backplane::Backplane;
|
||||
|
||||
|
||||
fn main() {
|
||||
let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin");
|
||||
println!("Taxation is theft");
|
||||
|
||||
let mut ramrom_computer = RamRomComputer::program_rom((&bytes[..]).to_vec());
|
||||
|
||||
ramrom_computer.tick2(0x05, 0b0000_0000, 0x05);
|
||||
println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus",
|
||||
ramrom_computer.data_bus(),
|
||||
ramrom_computer.address_bus()
|
||||
);
|
||||
println!("--TICK--");
|
||||
ramrom_computer.tick2(0x4005, 0b0000_0001, ramrom_computer.data_bus());
|
||||
println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus",
|
||||
ramrom_computer.data_bus(),
|
||||
ramrom_computer.address_bus()
|
||||
);
|
||||
println!("--TICK--");
|
||||
ramrom_computer.tick2(0x4005, 0b0000_0000, ramrom_computer.data_bus());
|
||||
println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus",
|
||||
ramrom_computer.data_bus(),
|
||||
ramrom_computer.address_bus()
|
||||
);
|
||||
println!("--TICK--");
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
use std::fs;
|
||||
use core::computers::rom_only::RomOnlyComputer;
|
||||
use core::traits::backplane::Backplane;
|
||||
|
||||
fn main() {
|
||||
println!("Taxation is theft");
|
||||
let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin";
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes.", bytes.len());
|
||||
bytes
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("FAIL to read rom.");
|
||||
panic!("No rom no run.");
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec());
|
||||
|
||||
rom_only.set_read_mode(true);
|
||||
rom_only.set_address_bus(0x05);
|
||||
rom_only.tick();
|
||||
|
||||
println!("COMPUTER: Read {:02x} from ROM", rom_only.data_bus()) ;
|
||||
println!("COMPUTER: Read {:04x} from Address Bus", rom_only.address_bus());
|
||||
|
||||
println!("----");
|
||||
rom_only.set_read_mode(true);
|
||||
rom_only.set_address_bus(0x07);
|
||||
rom_only.tick();
|
||||
|
||||
println!("COMPUTER: Read {:02x} from ROM", rom_only.data_bus()) ;
|
||||
println!("COMPUTER: Read {:04x} from Address Bus", rom_only.address_bus());
|
||||
println!("----");
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use std::fs;
|
||||
use core::computers::rom_only::RomOnlyComputer;
|
||||
use core::traits::backplane::Backplane;
|
||||
|
||||
fn main() {
|
||||
println!("Taxation is theft");
|
||||
let path = "/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin";
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
println!("Loaded {} bytes", bytes.len());
|
||||
bytes
|
||||
},
|
||||
Err(e) => vec![]
|
||||
};
|
||||
|
||||
let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec());
|
||||
|
||||
rom_only.tick2(0x05, 0b0000_0001, 0x05);
|
||||
println!("COMPUTER: Read {:02x} from ROM / {:04x} from Address bus", rom_only.data_bus(), rom_only.address_bus()) ;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use core::computers::single_breadboard::SingleBreadboard;
|
||||
|
||||
fn main() {
|
||||
let sb = SingleBreadboard::new();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod ram_rom_tests;
|
||||
mod rom_only_test;
|
||||
@@ -0,0 +1,16 @@
|
||||
use core::computers::ram_rom::RamRomComputer;
|
||||
use core::traits::backplane::Backplane;
|
||||
const RAM_START: u16 = 0x4000;
|
||||
const ROM_START: u16 = 0x0000;
|
||||
|
||||
#[test]
|
||||
fn ram_rom_thintick() {
|
||||
let mut ramrom = RamRomComputer::new();
|
||||
|
||||
ramrom.tick2(0x0001, 1, 0x00);
|
||||
println!("DataBus: {}", ramrom.data_bus());
|
||||
ramrom.tick2(0x4001, 1,0xab);
|
||||
println!("DataBus: {}", ramrom.data_bus());
|
||||
ramrom.tick2(0x4001, 1,0x00);
|
||||
println!("DataBus: {}", ramrom.data_bus());
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use core::computers::rom_only::RomOnlyComputer;
|
||||
use core::traits::backplane::Backplane;
|
||||
|
||||
#[test]
|
||||
fn rom_only_widetick_test() {
|
||||
let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin");
|
||||
|
||||
let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec());
|
||||
|
||||
rom_only.tick2(0x05, 0x00, 0x00);
|
||||
assert_eq!(rom_only.data_bus(), 0x05);
|
||||
assert_eq!(rom_only.address_bus(), 0x05);
|
||||
|
||||
rom_only.tick2(0x07, 0x00, 0x00);
|
||||
assert_eq!(rom_only.data_bus(), 0x07);
|
||||
assert_eq!(rom_only.address_bus(), 0x07);
|
||||
|
||||
rom_only.tick2(0x09, 0x00, 0x00);
|
||||
assert_eq!(rom_only.data_bus(), 0x09);
|
||||
assert_eq!(rom_only.address_bus(), 0x09);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rom_only_thintick_test() {
|
||||
let bytes = include_bytes!("/home/tmerritt/Projects/mos6502/resources/test/periph/at28c256/checksum.bin");
|
||||
|
||||
let mut rom_only = RomOnlyComputer::program((&bytes[..]).to_vec());
|
||||
|
||||
rom_only.set_read_mode(true);
|
||||
rom_only.set_address_bus(0x05);
|
||||
rom_only.tick();
|
||||
|
||||
assert_eq!(rom_only.data_bus(), 0x05);
|
||||
assert_eq!(rom_only.address_bus(), 0x05);
|
||||
|
||||
rom_only.set_read_mode(true);
|
||||
rom_only.set_address_bus(0x07);
|
||||
rom_only.tick();
|
||||
|
||||
assert_eq!(rom_only.data_bus(), 0x07);
|
||||
assert_eq!(rom_only.address_bus(), 0x07);
|
||||
}
|
||||
@@ -1,7 +1,17 @@
|
||||
mod backplane;
|
||||
|
||||
use crate::mos6502cpu::Mos6502Cpu;
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
use crate::periph::mos6522::Mos6522;
|
||||
|
||||
/*
|
||||
SBC Designed by Ben Eater
|
||||
|
||||
0x0000 -> 0x3fff -> RAM (16KB)
|
||||
0x4000 -> 0x5fff -> UNUSED
|
||||
0x6000 -> 0x600f -> VIA
|
||||
0x6010 -> 0x7fff -> UNUSED
|
||||
0x8000 -> 0xffff -> ROM (32KB)
|
||||
*/
|
||||
|
||||
pub struct BenEater {
|
||||
cpu: Mos6502Cpu,
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod reset;
|
||||
use crate::mos6502cpu::Mos6502Cpu;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::kim1_keypad::Kim1Keypad;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
/// Represents a KIM-1
|
||||
///
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::computers::kim1::Kim1;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::kim1_keypad::Kim1Keypad;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
impl Kim1 {
|
||||
pub fn dump(&self) {
|
||||
@@ -13,8 +13,8 @@ impl Kim1 {
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-002_fillerbyte00-0x1c00.bin");
|
||||
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-003_fillerbyte00-0x1800.bin");
|
||||
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-002_fillerbyte00-0x1c00.bin");
|
||||
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-003_fillerbyte00-0x1800.bin");
|
||||
|
||||
Self {
|
||||
cpu: Default::default(),
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::computers::kim1::Kim1;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
impl Kim1 {
|
||||
pub fn reset(&mut self) {
|
||||
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-002_fillerbyte00-0x1c00.bin");
|
||||
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/kim1/6530-003_fillerbyte00-0x1800.bin");
|
||||
let rriot1_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-002_fillerbyte00-0x1c00.bin");
|
||||
let rriot2_rom = include_bytes!("/home/tmerritt/Projects/mos6502/resources/pia/6530-003_fillerbyte00-0x1800.bin");
|
||||
self.cpu = Default::default();
|
||||
self.rriot1 = Mos6530::new(0x1700, 0x1780, 0x1800, rriot1_rom.as_array().unwrap());
|
||||
self.rriot2 = Mos6530::new(0x1740, 0x17c0, 0x1c00, rriot2_rom.as_array().unwrap());
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod rom_only;
|
||||
pub mod kim1;
|
||||
pub mod ram_rom;
|
||||
pub mod tim1;
|
||||
pub mod single_breadboard;
|
||||
|
||||
@@ -69,4 +69,3 @@ impl Backplane for RamRomComputer {
|
||||
(0, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::periph::hm62256::Hm62256;
|
||||
pub mod backplane;
|
||||
pub mod program_rom;
|
||||
pub mod new;
|
||||
mod tick2;
|
||||
pub mod tick2;
|
||||
|
||||
pub struct RamRomComputer {
|
||||
pub(crate) rom: At28C256,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use crate::computers::ram_rom::RamRomComputer;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::backplane::Backplane;
|
||||
|
||||
struct ChipSignals {
|
||||
cs: bool,
|
||||
@@ -14,21 +13,8 @@ enum RomRamChips {
|
||||
}
|
||||
|
||||
impl RamRomComputer {
|
||||
pub fn signal_tick(&mut self, address: u16, data: u8) {
|
||||
println!("⏲️RAM ROM COMPUTER SIGNAL TICK");
|
||||
|
||||
// no CPU to tick.
|
||||
|
||||
let mut ram_state = ChipSignals { oe: false, we: false, cs: false};
|
||||
let mut rom_state = ChipSignals { oe: false, we: false, cs: false};
|
||||
|
||||
|
||||
// Tick the RAM
|
||||
// Tick the ROM
|
||||
}
|
||||
|
||||
pub fn tick2(&mut self, address: u16, control: u8, data: u8) -> (u8) {
|
||||
println!("RAM ROM Computer tick starting / {address:04x} {control:08b} {data:02x}");
|
||||
println!("⏲RAM ROM Computer tick starting / {address:04x} {control:08b} {data:02x}");
|
||||
|
||||
// tick the parts
|
||||
|
||||
@@ -37,10 +23,16 @@ impl RamRomComputer {
|
||||
// 0x4000 -> 0x7fff -> ROM (At28C256)
|
||||
match address {
|
||||
0x0000..=0x3fff => {
|
||||
println!("__DATA TARGETTING ROM BEING STORED ON DATA BUS");
|
||||
if self.read_mode {
|
||||
println!("⏲__ROM BEING READ FROM");
|
||||
let new_data = self.rom.signal_tick(address, data, true, true, true);
|
||||
self.set_data_bus(new_data);
|
||||
} else {
|
||||
panic!("UNABLE TO WRITE TO ROM");
|
||||
}
|
||||
}
|
||||
0x4000 ..=0x7fff => {
|
||||
println!("__DATA TARGETTING RRAAMM GETTING STORED ON DATA BUS");
|
||||
println!("⏲__DATA TARGETTING RRAAMM GETTING STORED ON DATA BUS");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ impl RomOnlyComputer {
|
||||
for index in 0..size {
|
||||
println!("Index {index} for {}", index + start_offset);
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,18 @@ pub mod debug_memory;
|
||||
mod rom_chunks;
|
||||
|
||||
pub struct RomOnlyComputer {
|
||||
pub(crate) rom: At28C256,
|
||||
pub(crate) data_bus: u8,
|
||||
pub(crate) address_bus: u16,
|
||||
pub(crate) read_mode: bool,
|
||||
pub rom: At28C256,
|
||||
pub data_bus: u8,
|
||||
pub address_bus: u16,
|
||||
pub read_mode: bool,
|
||||
}
|
||||
|
||||
impl RomOnlyComputer {
|
||||
pub fn tick2(&mut self, address: u16, control: u8, data: u8) -> (u8) {
|
||||
// tick the parts...
|
||||
println!("WIDETICK: A:${address:04x} D:${data:02x} C:b{control:08b}");
|
||||
self.address_bus = address;
|
||||
self.data_bus = data;
|
||||
|
||||
let new_data = self.rom.signal_tick(self.address_bus, self.data_bus, true, true, true);
|
||||
println!("\nNew Data : {new_data:02x}");
|
||||
|
||||
@@ -9,5 +9,4 @@ impl RomOnlyComputer {
|
||||
}
|
||||
RomOnlyComputer::program(working)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Computer at
|
||||
https://www.youtube.com/watch?v=s3t2QMukBRs
|
||||
https://github.com/AndersBNielsen/6507SBC
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::mos6502cpu::Mos6502Cpu;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::periph::mos6532::Mos6532;
|
||||
|
||||
pub struct SingleBreadboard {
|
||||
pub cpu: Mos6502Cpu,
|
||||
pub ram: At28C256,
|
||||
pub via: Mos6532
|
||||
}
|
||||
|
||||
impl SingleBreadboard {
|
||||
pub fn new() -> Self {
|
||||
SingleBreadboard {
|
||||
cpu: Mos6502Cpu::default(),
|
||||
ram: At28C256::new(0xf000, 0xffff, vec![0x00; SIZE_32KB]),
|
||||
via: Mos6532::new(0x0000, 0x0080)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::net::IpAddr::V4;
|
||||
use log::debug;
|
||||
use crate::computers::tim1::Tim1;
|
||||
use crate::traits::backplane::Backplane;
|
||||
|
||||
impl Backplane for Tim1 {
|
||||
fn data_bus(&self) -> u8 { self.data_bus }
|
||||
fn address_bus(&self) -> u16 { self.address_bus }
|
||||
fn read_mode(&self) -> bool { self.read_mode }
|
||||
fn set_read_mode(&mut self, new_mode: bool) {
|
||||
self.read_mode = new_mode
|
||||
}
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
}
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
println!("Starting tick for TIM-1");
|
||||
}
|
||||
|
||||
fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 {
|
||||
debug!("No dedicated RAM. Its in the VIA");
|
||||
0x00
|
||||
}
|
||||
|
||||
fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 {
|
||||
debug!("No dedicated ROM. Its in the VIA");
|
||||
0x00
|
||||
}
|
||||
|
||||
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) {
|
||||
debug!("Starting VIA Tick");
|
||||
self.pia.tick(address, data, false, self.read_mode);
|
||||
// is this for the ROM?
|
||||
|
||||
// is this for the RAM?
|
||||
|
||||
// is this for the Interrupts?
|
||||
|
||||
// is this for the Timers?
|
||||
(0x00, false, false)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
mod backplane;
|
||||
|
||||
use crate::mos6502cpu::Mos6502Cpu;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
pub struct Tim1 {
|
||||
cpu: Mos6502Cpu,
|
||||
pia: Mos6530
|
||||
pia: Mos6530,
|
||||
read_mode: bool,
|
||||
data_bus: u8,
|
||||
address_bus: u16
|
||||
}
|
||||
|
||||
|
||||
+2
-1263
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,5 @@
|
||||
use crate::address_mode::AddressMode;
|
||||
use crate::constants::constants_isa_op::{
|
||||
ISA_OP_ADC_ABS, ISA_OP_ADC_ABSX, ISA_OP_ADC_ABSY, ISA_OP_ADC_I, ISA_OP_ADC_INDX,
|
||||
ISA_OP_ADC_INDY, ISA_OP_ADC_Z, ISA_OP_ADC_ZX, ISA_OP_AND_ABS, ISA_OP_AND_ABSX, ISA_OP_AND_ABSY,
|
||||
ISA_OP_AND_I, ISA_OP_AND_INDX, ISA_OP_AND_INDY, ISA_OP_AND_Z, ISA_OP_AND_ZX, ISA_OP_ASL_A,
|
||||
ISA_OP_ASL_ABS, ISA_OP_ASL_ABSX, ISA_OP_ASL_Z, ISA_OP_ASL_ZX, ISA_OP_BCC, ISA_OP_BCS,
|
||||
ISA_OP_BEQ, ISA_OP_BIT_ABS, ISA_OP_BIT_ZP, ISA_OP_BMI, ISA_OP_BNE, ISA_OP_BPL, ISA_OP_BRK,
|
||||
ISA_OP_BVC, ISA_OP_BVS, ISA_OP_CLC, ISA_OP_CLD, ISA_OP_CLI, ISA_OP_CLV, ISA_OP_CMP_ABS,
|
||||
ISA_OP_CMP_ABSX, ISA_OP_CMP_ABSY, ISA_OP_CMP_I, ISA_OP_CMP_INDX, ISA_OP_CMP_INDY,
|
||||
ISA_OP_CMP_ZP, ISA_OP_CMP_ZPX, ISA_OP_CPX_ABS, ISA_OP_CPX_I, ISA_OP_CPX_ZP, ISA_OP_CPY_ABS,
|
||||
ISA_OP_CPY_I, ISA_OP_CPY_ZP, ISA_OP_DEC_ABS, ISA_OP_DEC_ABSX, ISA_OP_DEC_ZP, ISA_OP_DEC_ZPX,
|
||||
ISA_OP_DEX, ISA_OP_DEY, ISA_OP_EOR_ABS, ISA_OP_EOR_ABSX, ISA_OP_EOR_ABSY, ISA_OP_EOR_I,
|
||||
ISA_OP_EOR_INDX, ISA_OP_EOR_INDY, ISA_OP_EOR_ZP, ISA_OP_EOR_ZPX, ISA_OP_INC_ABS,
|
||||
ISA_OP_INC_ABSX, ISA_OP_INC_ZP, ISA_OP_INC_ZPX, ISA_OP_INX, ISA_OP_INY, ISA_OP_JMP_ABS,
|
||||
ISA_OP_JMP_IND, ISA_OP_JSR, ISA_OP_LDA_ABS, ISA_OP_LDA_ABSX, ISA_OP_LDA_ABSY, ISA_OP_LDA_I,
|
||||
ISA_OP_LDA_INDX, ISA_OP_LDA_INDY, ISA_OP_LDA_Z, ISA_OP_LDA_ZX, ISA_OP_LDX_ABS, ISA_OP_LDX_ABSY,
|
||||
ISA_OP_LDX_I, ISA_OP_LDX_ZP, ISA_OP_LDX_ZPY, ISA_OP_LDY_ABS, ISA_OP_LDY_ABSX, ISA_OP_LDY_I,
|
||||
ISA_OP_LDY_ZP, ISA_OP_LDY_ZPX, ISA_OP_LSR_A, ISA_OP_LSR_ABS, ISA_OP_LSR_ABSX, ISA_OP_LSR_ZP,
|
||||
ISA_OP_LSR_ZPX, ISA_OP_NOP, ISA_OP_ORA_ABS, ISA_OP_ORA_ABSX, ISA_OP_ORA_ABSY, ISA_OP_ORA_I,
|
||||
ISA_OP_ORA_INDX, ISA_OP_ORA_INDY, ISA_OP_ORA_ZP, ISA_OP_ORA_ZPX, ISA_OP_PHA, ISA_OP_PHP,
|
||||
ISA_OP_PLA, ISA_OP_PLP, ISA_OP_ROL_A, ISA_OP_ROL_ABS, ISA_OP_ROL_ABSX, ISA_OP_ROL_ZP,
|
||||
ISA_OP_ROL_ZPX, ISA_OP_ROR_A, ISA_OP_ROR_ABS, ISA_OP_ROR_ABSX, ISA_OP_ROR_ZP, ISA_OP_ROR_ZPX,
|
||||
ISA_OP_RTI, ISA_OP_RTS, ISA_OP_SBC_ABS, ISA_OP_SBC_ABSX, ISA_OP_SBC_ABSY, ISA_OP_SBC_I,
|
||||
ISA_OP_SBC_INDX, ISA_OP_SBC_INDY, ISA_OP_SBC_ZP, ISA_OP_SBC_ZPX, ISA_OP_SEC, ISA_OP_SED,
|
||||
ISA_OP_SEI, ISA_OP_STA_ABS, ISA_OP_STA_ABSX, ISA_OP_STA_ABSY, ISA_OP_STA_INDX, ISA_OP_STA_INDY,
|
||||
ISA_OP_STA_ZP, ISA_OP_STA_ZPX, ISA_OP_STX_ABS, ISA_OP_STX_ZP, ISA_OP_STX_ZPY, ISA_OP_STY_ABS,
|
||||
ISA_OP_STY_ZP, ISA_OP_STY_ZPX, ISA_OP_TAX, ISA_OP_TAY, ISA_OP_TSX, ISA_OP_TXA, ISA_OP_TXS,
|
||||
ISA_OP_TYA,
|
||||
};
|
||||
use crate::constants::constants_isa_op::*;
|
||||
use crate::op_info::OpInfo;
|
||||
use crate::operation::Operation;
|
||||
use crate::operation::Operation::*;
|
||||
@@ -39,7 +12,6 @@ pub fn instruction_length(instruction: u8) -> u8 {
|
||||
INSTRUCTION_TABLE[instruction as usize].clone().unwrap().length
|
||||
}
|
||||
|
||||
|
||||
pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
|
||||
let mut table: [Option<OpInfo>; 256] = [const { None }; 256];
|
||||
table[ISA_OP_ADC_I as usize] = Some(OpInfo {
|
||||
@@ -1307,49 +1279,3 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
|
||||
|
||||
table
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_instruction_table_completeness() {
|
||||
use INSTRUCTION_TABLE; // Adjust to your actual path
|
||||
|
||||
let mut defined_count = 0;
|
||||
let mut defined_opcodes = vec![];
|
||||
|
||||
for (i, entry) in INSTRUCTION_TABLE.iter().enumerate() {
|
||||
if let Some(info) = entry {
|
||||
defined_count += 1;
|
||||
defined_opcodes.push(i);
|
||||
// Optional: sanity check
|
||||
assert!(
|
||||
info.length > 0 && info.cycles > 0,
|
||||
"Invalid OpInfo at opcode {:#04x?}",
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("Defined opcodes: {}", defined_count);
|
||||
for i in 0..256 {
|
||||
if defined_opcodes.contains(&i) {
|
||||
print!("{:02x} ", i);
|
||||
}
|
||||
}
|
||||
println!("\nMissing opcodes:");
|
||||
for i in 0..256 {
|
||||
if !defined_opcodes.contains(&i) {
|
||||
print!("{:02X} ", i);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
// The standard 6502 has 151 documented opcodes
|
||||
assert_eq!(
|
||||
defined_count, 151,
|
||||
"Expected 151 opcodes, found {}",
|
||||
defined_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::mos6502flags::Mos6502Flag::{
|
||||
Break, Carry, Decimal, Interrupt, Negative, Overflow, Zero,
|
||||
};
|
||||
use crate::mos6502flags::Mos6502Flag::*;
|
||||
|
||||
pub const BIT_NEGATIVE: u8 = 7;
|
||||
pub const BIT_OVERFLOW: u8 = 6;
|
||||
@@ -68,13 +66,14 @@ impl Mos6502Flag {
|
||||
|
||||
#[derive(Default, PartialEq, Debug)]
|
||||
pub struct Mos6502Flags {
|
||||
carry: bool,
|
||||
zero: bool,
|
||||
interrupt: bool,
|
||||
decimal: bool,
|
||||
break_flag: bool,
|
||||
overflow: bool,
|
||||
negative: bool,
|
||||
// TODO: This is pub for tests. shouldn't be that way
|
||||
pub carry: bool,
|
||||
pub zero: bool,
|
||||
pub interrupt: bool,
|
||||
pub decimal: bool,
|
||||
pub break_flag: bool,
|
||||
pub overflow: bool,
|
||||
pub negative: bool,
|
||||
}
|
||||
|
||||
impl Mos6502Flags {
|
||||
@@ -197,31 +196,3 @@ impl Mos6502Flags {
|
||||
(src >> pos) & 1 != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity() {
|
||||
let f = Mos6502Flags::default();
|
||||
let magic_byte = 0b1110_1101;
|
||||
let magic_flags = Mos6502Flags {
|
||||
carry: true,
|
||||
zero: false,
|
||||
interrupt: true,
|
||||
decimal: true,
|
||||
break_flag: false,
|
||||
overflow: true,
|
||||
negative: true,
|
||||
};
|
||||
|
||||
assert_eq!(magic_flags, Mos6502Flags::from_byte(magic_byte));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,3 @@ impl At28C256 {
|
||||
data.iter().fold(0u8, |acc, &b| acc.wrapping_add(b))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ pub mod signal_tick;
|
||||
pub mod memory_chip;
|
||||
pub mod bus_device;
|
||||
|
||||
|
||||
/// At28C256
|
||||
///
|
||||
/// Represents a single At28C256 EEPROM Chip
|
||||
@@ -21,7 +20,7 @@ pub struct At28C256 {
|
||||
// Logical parts
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
data: Box<[u8]>,
|
||||
pub data: Box<[u8]>,
|
||||
// where in the computer memory map do we live?
|
||||
offset: u16,
|
||||
max_offset: u16,
|
||||
@@ -30,152 +29,3 @@ pub struct At28C256 {
|
||||
we: bool,
|
||||
oe: bool
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use crate::constants::constants_system::*;
|
||||
use crate::constants::constants_test::*;
|
||||
use crate::periph::at28c256::At28C256;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn checksum_binary_loads() {
|
||||
let path = format!("{}/{}", TEST_PERIPH_AT28C256_ROOT, "checksum.bin");
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes.", bytes.len());
|
||||
bytes
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("FAIL to read rom.");
|
||||
panic!("No rom no run.");
|
||||
}
|
||||
};
|
||||
|
||||
let mut rom = At28C256::new(0x0000, 0x3fff, bytes);
|
||||
|
||||
assert_eq!(rom.checksum(), 0x58);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_chunks_come_back_ok() {
|
||||
let test_data = (0..255).collect();
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, test_data);
|
||||
|
||||
let chunks = chip.chunks(16);
|
||||
assert_eq!(chunks.len(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_blocks_come_back_ok() {
|
||||
let test_data = (0..=3).collect();
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, test_data);
|
||||
|
||||
let chunks = chip.chunks(16);
|
||||
assert_eq!(chunks.len(), 1);
|
||||
for chunk in chunks {
|
||||
assert_eq!(chunk.len(), 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn programmed_data_reads_back_same() {
|
||||
let mut data = At28C256::default();
|
||||
for i in 0..SIZE_32KB {
|
||||
data.data[i] = 0xeau8;
|
||||
}
|
||||
for offset in 0..(SIZE_32KB - 1) {
|
||||
assert_eq!(0xea, data.read(&(offset as u16)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_correctly_for_zero() {
|
||||
let data1 = [0x00u8; SIZE_32KB];
|
||||
assert_eq!(0x00, At28C256::checksum_static(&data1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_1_byte() {
|
||||
let data = [0xff; 1];
|
||||
assert_eq!(0xff, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_2_bytes() {
|
||||
let data = [0xff; 2];
|
||||
// 0xff + 0xff = 0x1fe
|
||||
assert_eq!(0xfe, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_first_80_bytes() {
|
||||
println!("STARTING TEST");
|
||||
let mut checksum = 0x00;
|
||||
|
||||
let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin");
|
||||
println!("READING [{path}]");
|
||||
let data = fs::read(path);
|
||||
match data {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes", bytes.len());
|
||||
checksum = At28C256::checksum_static(&bytes);
|
||||
println!("Checksum: 0x{:02x}", checksum);
|
||||
}
|
||||
Err(e) => eprintln!("Failed to read file: {}", e),
|
||||
}
|
||||
assert_eq!(0x58, checksum);
|
||||
println!("TEST COMPLETE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_data_set_reads() {
|
||||
let mut x = At28C256::new(0x0000, 0x3fff, vec![0xea; SIZE_32KB]);
|
||||
|
||||
// set both...
|
||||
x.set_address_bus(0x3000);
|
||||
x.set_data_bus(0xab);
|
||||
assert_eq!(0xab, x.data_bus());
|
||||
assert_eq!(0x3000, x.address_bus());
|
||||
|
||||
// ...set address...
|
||||
x.set_address_bus(0x2000);
|
||||
assert_eq!(0x2000, x.address_bus());
|
||||
assert_eq!(0xab, x.data_bus());
|
||||
|
||||
// ...set data.
|
||||
x.set_data_bus(0xba);
|
||||
assert_eq!(0xba, x.data_bus());
|
||||
assert_eq!(0x2000, x.address_bus());
|
||||
}
|
||||
|
||||
fn programming_chip_changes_contents() {
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, vec![]);
|
||||
|
||||
assert_eq!(0x00, chip.read(&0x0000));
|
||||
|
||||
let new_data: Vec<u8> = 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
|
||||
impl BusDevice for Hm62256 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 {
|
||||
self.max_offset
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::periph::hm62256::Hm62256;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
impl MemoryChip for Hm62256 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
// loops memory around past 32k
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
self.data[effective as usize]
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ pub mod default;
|
||||
pub mod new;
|
||||
pub mod dump;
|
||||
mod control;
|
||||
mod bus_device;
|
||||
mod memory_chip;
|
||||
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
@@ -29,51 +31,3 @@ pub struct Hm62256 {
|
||||
// Output Enable
|
||||
pub(crate) oe: bool
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rand::random;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn written_data_comes_back() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// 100,000 random read/writes to ram that all read back right
|
||||
for _ in 0..100_000 {
|
||||
let mut offset: u16 = random();
|
||||
println!("Size = {SIZE_32KB}");
|
||||
let value: u8 = random();
|
||||
println!("Wrote [{value:02x}] to [{offset:04x}]");
|
||||
ram.write(&offset, &value);
|
||||
|
||||
assert_eq!(ram.read(&offset), value)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_space_is_round() {
|
||||
// addresses written past the last address 'loop' back to 0+(offset - MAX_SIZE)
|
||||
let max_offset = SIZE_32KB;
|
||||
let test_offset = max_offset;
|
||||
|
||||
// all zero
|
||||
let mut ram = Hm62256::default();
|
||||
// write FF to the addresss after the last
|
||||
ram.write(&(test_offset as u16), &0xff);
|
||||
|
||||
// check all the ram for anything that isn't 0x00
|
||||
|
||||
assert_eq!(ram.read(&(0x0000)), 0xff);
|
||||
for offset in 1..SIZE_32KB {
|
||||
println!("Testing offset {offset:04x} for 0x00");
|
||||
assert_eq!(ram.read(&(offset as u16)), 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,33 +3,6 @@ use crate::periph::hm62256::Hm62256;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
|
||||
impl BusDevice for Hm62256 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 {
|
||||
self.max_offset
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl RamChip for Hm62256 {
|
||||
fn write(&mut self, offset: &u16, value: &u8) {
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
|
||||
@@ -4,13 +4,6 @@ use crate::periph::hm62256::Hm62256;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
|
||||
impl MemoryChip for Hm62256 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
// loops memory around past 32k
|
||||
let effective = *offset as i32 % SIZE_32KB as i32;
|
||||
self.data[effective as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl RomChip for Hm62256 {
|
||||
fn program(_: &[u8]) -> Box<Self> {
|
||||
|
||||
@@ -39,23 +39,3 @@ impl Hm62256 {
|
||||
(self.address_bus, self.data_bus)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn write_to_memory_read_back_works_at_0() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// load the data to ram
|
||||
ram.tick(0x0000, 0xab, false, true);
|
||||
// read the data back
|
||||
let (_, new_data) = ram.tick(0x0000, 0x00, true, true);
|
||||
|
||||
assert_eq!(new_data, 0xab);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
*/
|
||||
|
||||
pub struct Kim1Keypad {
|
||||
keys: [bool; 23],
|
||||
stepping: bool
|
||||
pub keys: [bool; 23],
|
||||
pub stepping: bool
|
||||
}
|
||||
|
||||
impl Kim1Keypad {
|
||||
@@ -52,54 +52,3 @@ impl Kim1Keypad {
|
||||
self.keys[Self::keyid(key)]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn keys_are_pressed() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
for index in 0..23 {
|
||||
assert!(!kb.is_pressed(index));
|
||||
kb.press_key(index);
|
||||
assert!(kb.is_pressed(index));
|
||||
|
||||
kb.release_key(index);
|
||||
assert!(!kb.is_pressed(index));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stepping_changes() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.set_stepping(false);
|
||||
|
||||
assert!(!kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(!kb.stepping);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn out_of_range() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.press_key(24);
|
||||
assert!(kb.is_pressed(1));
|
||||
}
|
||||
}
|
||||
|
||||
+12
-5
@@ -1,6 +1,13 @@
|
||||
pub mod at28c256;
|
||||
pub mod hm62256;
|
||||
pub mod mos6520;
|
||||
pub mod mos6522;
|
||||
pub mod mos6530;
|
||||
pub mod kim1_keypad;
|
||||
|
||||
pub mod at28c256; // EEPROM with 32KB/256Kbit
|
||||
pub mod hm62256; // RAM with 32KBit/256Kbit
|
||||
|
||||
pub mod mos6520; // PIA
|
||||
pub mod mos6522; // PIA
|
||||
pub mod mos6530; // RRIOT ROM, RAM, IO, Timer
|
||||
pub mod mos6532; // PIA RAM, IO, Timer
|
||||
pub mod mos6526;
|
||||
mod mos6540;
|
||||
mod mos6111;
|
||||
// CIA Parallel, Serial, Interval Timers, Time Of Day
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
// MOS6111
|
||||
// AKA MCS6111
|
||||
// 128B/1024b RAM
|
||||
pub struct Mos6111 {
|
||||
data: Box<[u8]>,
|
||||
offset: u16,
|
||||
max_offset: u16
|
||||
}
|
||||
|
||||
impl Mos6111 {
|
||||
pub fn new(min_offset: u16, max_offset: u16) -> Self {
|
||||
Mos6111 {
|
||||
data: Box::new([0x00u8; 64]),
|
||||
offset: min_offset,
|
||||
max_offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BusDevice for Mos6111 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 {
|
||||
self.max_offset
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryChip for Mos6111 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
println!("IA = {}", self.internal_address(*offset));
|
||||
0x00
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
impl MemoryChip for Mos6520 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
debug!("This has no ROM ${offset:04x}");
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
pub mod reset;
|
||||
pub mod bus_device;
|
||||
pub mod via_chip;
|
||||
pub mod new;
|
||||
pub mod tick;
|
||||
pub mod rom_chip;
|
||||
pub mod ram_chip;
|
||||
pub mod memory_chip;
|
||||
pub mod pia_chip;
|
||||
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::gpio_chip::GpioChip;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
use crate::traits::via_chip::ViaChip;
|
||||
|
||||
/*
|
||||
The MCS6520 Peripheral Adapter is designed to solve a broad range of peripheral
|
||||
@@ -26,22 +29,22 @@ be tween the processor and a peripheral device.
|
||||
*/
|
||||
pub struct Mos6520 {
|
||||
// Parallel ports A & B data registers
|
||||
port_a: u8,
|
||||
out_a: u8,
|
||||
in_a: u8,
|
||||
ddra: u8,
|
||||
port_b: u8,
|
||||
out_b: u8,
|
||||
in_b: u8,
|
||||
ddrb: u8,
|
||||
pub port_a: u8,
|
||||
pub out_a: u8,
|
||||
pub in_a: u8,
|
||||
pub ddra: u8,
|
||||
pub port_b: u8,
|
||||
pub out_b: u8,
|
||||
pub in_b: u8,
|
||||
pub ddrb: u8,
|
||||
// Interrupt control register
|
||||
icr: u8,
|
||||
pub icr: u8,
|
||||
// External Address and Data Bus
|
||||
address_bus: u16,
|
||||
data_bus: u8,
|
||||
pub address_bus: u16,
|
||||
pub data_bus: u8,
|
||||
// Offsets
|
||||
offset: u16,
|
||||
max_offset: u16
|
||||
pub offset: u16,
|
||||
pub max_offset: u16
|
||||
}
|
||||
|
||||
impl Mos6520 {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::traits::gpio_chip::{GpioChip, GpioChipPorts};
|
||||
use crate::traits::pia_chip::PiaChip;
|
||||
|
||||
impl GpioChip for Mos6520 {
|
||||
fn set_port(&mut self, new_value: u8, port: GpioChipPorts) {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.port_a = new_value
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.port_b = new_value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_ddr(&mut self, new_ddr_value: u8, port: GpioChipPorts) {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.ddra = new_ddr_value
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.ddrb = new_ddr_value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn get_port(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.port_a
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.port_b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ddr(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.ddra
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.ddrb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_in(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.in_a
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.in_b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_out(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.port_a
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.port_b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ports(&mut self) {
|
||||
let (in_a, out_a) = Mos6520::split_port(self.port_a, self.ddra);
|
||||
self.in_a = in_a;
|
||||
self.out_a = out_a;
|
||||
|
||||
// Port B
|
||||
let (in_b, out_b) = Mos6520::split_port(self.port_b, self.ddrb);
|
||||
self.in_b = in_b;
|
||||
self.out_b = out_b;
|
||||
}
|
||||
}
|
||||
|
||||
impl PiaChip for Mos6520 { }
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn check_gpio_ports() {
|
||||
let mut x = Mos6520::new(0x1000);
|
||||
|
||||
// offsets from 0x1000 -> 0x1004
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
|
||||
impl RamChip for Mos6520 {
|
||||
fn write(&mut self, offset: &u16, value: &u8) {
|
||||
debug!("This has no ROM / ${offset:04x} ${value:02x}");
|
||||
}
|
||||
}
|
||||
@@ -10,5 +10,7 @@ impl Mos6520 {
|
||||
self.ddra = 0x00;
|
||||
self.ddrb = 0x00;
|
||||
self.icr = 0x00;
|
||||
self.address_bus = 0x0000;
|
||||
self.data_bus = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
|
||||
impl RomChip for Mos6520 {
|
||||
fn program(new_data: &[u8]) -> Box<Self> {
|
||||
debug!("This has no rom. Cant program.");
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,11 @@ use log::debug;
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
|
||||
const MOS6520_DDRA_OFFSET: u8 = 0x00;
|
||||
const MOS6520_DDRB_OFFSET: u8 = 0x02;
|
||||
const MOS6520_PRTA_OFFSET: u8 = 0x01;
|
||||
const MOS6520_PRTB_OFFSET: u8 = 0x03;
|
||||
|
||||
impl Mos6520 {
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8) -> u8 {
|
||||
self.address_bus = address_bus;
|
||||
@@ -10,25 +15,25 @@ impl Mos6520 {
|
||||
println!("Preparing to tick MOS6520");
|
||||
|
||||
// are we changing our state?
|
||||
let effective = self.internal_address(self.address_bus);
|
||||
let effective = self.internal_address(self.address_bus) as u8;
|
||||
|
||||
match effective {
|
||||
0x00 => {
|
||||
MOS6520_DDRA_OFFSET => {
|
||||
// ddra
|
||||
// println!("DDRA -> {:02x} -> {:02x}", self.ddra, self.data_bus);
|
||||
self.ddra = self.data_bus;
|
||||
}
|
||||
0x01 => {
|
||||
MOS6520_PRTA_OFFSET => {
|
||||
// port a
|
||||
// println!("PORT A -> {:02x} -> {:02x}", self.port_a, self.data_bus);
|
||||
self.port_a = self.data_bus;
|
||||
}
|
||||
0x02 => {
|
||||
MOS6520_DDRB_OFFSET => {
|
||||
// ddrb
|
||||
// println!("DDRB -> {:02x}", self.ddrb);
|
||||
self.ddrb = self.data_bus;
|
||||
}
|
||||
0x03 => {
|
||||
MOS6520_PRTB_OFFSET => {
|
||||
// port b
|
||||
// println!("PORT B -> {:02x}", self.port_b);
|
||||
self.port_b = self.data_bus;
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
use crate::traits::via_chip::ViaChip;
|
||||
|
||||
impl RomChip for Mos6520 {
|
||||
fn program(new_data: &[u8]) -> Box<Self> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,47 @@
|
||||
pub mod mos6522;
|
||||
mod registers;
|
||||
mod new;
|
||||
mod tick;
|
||||
pub mod registers;
|
||||
pub mod new;
|
||||
pub mod tick;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mos6522 {
|
||||
/// data direction
|
||||
pub dda: u8,
|
||||
pub ddb: u8,
|
||||
|
||||
/// bottom 4 address bits
|
||||
pub rs0: u8,
|
||||
pub rs1: u8,
|
||||
pub rs2: u8,
|
||||
pub rs3: u8,
|
||||
|
||||
/// external data bus
|
||||
pub data_bus: u8,
|
||||
|
||||
pub cs1: bool,
|
||||
pub cs2: bool,
|
||||
// when true CPU is reading
|
||||
pub rw: bool,
|
||||
|
||||
/// reset circuit - true when reset inited
|
||||
pub reset: bool,
|
||||
|
||||
/// IRQ - true when interrupt waiting
|
||||
pub irq: bool,
|
||||
|
||||
pub ira: u8,
|
||||
pub ora: u8,
|
||||
pub porta: u8,
|
||||
pub irb: u8,
|
||||
pub orb: u8,
|
||||
pub portb: u8,
|
||||
|
||||
pub ca1: bool,
|
||||
pub ca2: bool,
|
||||
pub cb1: bool,
|
||||
pub cb2: bool,
|
||||
|
||||
// memory offset for where in the computers memory map this fits
|
||||
pub offset: u16,
|
||||
pub address_bus: u16,
|
||||
}
|
||||
|
||||
@@ -1,49 +1,6 @@
|
||||
use std::time::Instant;
|
||||
use log::debug;
|
||||
use crate::constants::constants_via6522::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mos6522 {
|
||||
/// data direction
|
||||
pub(crate) dda: u8,
|
||||
pub(crate) ddb: u8,
|
||||
|
||||
/// bottom 4 address bits
|
||||
pub(crate) rs0: u8,
|
||||
pub(crate) rs1: u8,
|
||||
pub(crate) rs2: u8,
|
||||
pub(crate) rs3: u8,
|
||||
|
||||
/// external data bus
|
||||
pub(crate) data_bus: u8,
|
||||
|
||||
pub(crate) cs1: bool,
|
||||
pub(crate) cs2: bool,
|
||||
// when true CPU is reading
|
||||
pub(crate) rw: bool,
|
||||
|
||||
/// reset circuit - true when reset inited
|
||||
pub(crate) reset: bool,
|
||||
|
||||
/// IRQ - true when interrupt waiting
|
||||
pub(crate) irq: bool,
|
||||
|
||||
pub(crate) ira: u8,
|
||||
pub(crate) ora: u8,
|
||||
pub(crate) porta: u8,
|
||||
pub(crate) irb: u8,
|
||||
pub(crate) orb: u8,
|
||||
pub(crate) portb: u8,
|
||||
|
||||
pub(crate) ca1: bool,
|
||||
pub(crate) ca2: bool,
|
||||
pub(crate) cb1: bool,
|
||||
pub(crate) cb2: bool,
|
||||
|
||||
// memory offset for where in the computers memory map this fits
|
||||
pub(crate) offset: u16,
|
||||
pub(crate) address_bus: u16,
|
||||
}
|
||||
use crate::periph::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
pub fn max_offset(&self) -> u16 {
|
||||
@@ -59,51 +16,3 @@ impl Mos6522 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true); }
|
||||
|
||||
#[test]
|
||||
fn registers() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.dda, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.dda, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_DDRB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ddb, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ddb, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ora, 0b0000_0000);
|
||||
x.tick(VIA6522_ORA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ora, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.orb, 0b0000_0000);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.orb, 0b1111_1111);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_porta() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRA as u16, 0b1010_1010, false, true);
|
||||
x.tick(VIA6522_ORA as u16,0b1111_1111, false, true);
|
||||
assert_eq!(x.porta, 0b1010_1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_portb() {
|
||||
let mut x = Mos6522::new();
|
||||
x.tick(VIA6522_DDRB as u16, 0b0101_0101, false, true);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.portb, 0b0101_0101);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,32 @@
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
use crate::periph::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
pub fn new() -> Self {
|
||||
Mos6522::default()
|
||||
pub fn new(offset: u16) -> Self {
|
||||
Mos6522 {
|
||||
dda: 0,
|
||||
ddb: 0,
|
||||
rs0: 0,
|
||||
rs1: 0,
|
||||
rs2: 0,
|
||||
rs3: 0,
|
||||
data_bus: 0,
|
||||
cs1: false,
|
||||
cs2: false,
|
||||
rw: false,
|
||||
reset: false,
|
||||
irq: false,
|
||||
ira: 0,
|
||||
ora: 0,
|
||||
porta: 0,
|
||||
irb: 0,
|
||||
orb: 0,
|
||||
portb: 0,
|
||||
ca1: false,
|
||||
ca2: false,
|
||||
cb1: false,
|
||||
cb2: false,
|
||||
offset,
|
||||
address_bus: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
use log::debug;
|
||||
use crate::constants::constants_system::SIZE_32KB;
|
||||
use crate::constants::constants_via6522::{VIA6522_DDRA, VIA6522_DDRB, VIA6522_ORA, VIA6522_ORB};
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
use crate::periph::mos6522::Mos6522;
|
||||
|
||||
impl Mos6522 {
|
||||
fn max_address(&self) -> u16 {
|
||||
self.offset + SIZE_32KB as u16
|
||||
}
|
||||
|
||||
/// tick
|
||||
///
|
||||
/// data_bus -> 8 bits from the data bus
|
||||
/// control -> 4 bits to identify which register to control
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8,reset: bool, rw: bool) -> (u16, u8) {
|
||||
if !(address_bus >= self.offset && address_bus.le(&self.max_address())) {
|
||||
if !(address_bus >= self.offset && address_bus.le(&self.max_offset())) {
|
||||
return (address_bus, data_bus);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use crate::periph::mos6526::Mos6526;
|
||||
use crate::traits::backplane::Backplane;
|
||||
|
||||
impl Backplane for Mos6526 {
|
||||
fn data_bus(&self) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read_mode(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_read_mode(&mut self, new_mode: bool) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn tick_ram(&mut self, address: u16, data: u8, cs: bool, oe: bool, we: bool) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn tick_rom(&mut self, address: u16, cs: bool, oe: bool) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
mod backplane;
|
||||
|
||||
|
||||
pub struct Mos6526 {}
|
||||
@@ -0,0 +1,30 @@
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
|
||||
impl BusDevice for Mos6530 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
self.ram_offset
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 {
|
||||
self.min_offset() + SIZE_1KB as u16
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn dump(&self) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
impl MemoryChip for Mos6530 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
debug!("🐙 Reading from ${offset:04x}");
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,45 @@
|
||||
pub mod mos6530;
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
|
||||
pub mod tick;
|
||||
pub mod new;
|
||||
pub mod dump;
|
||||
pub mod viachip;
|
||||
pub mod bus_device;
|
||||
pub mod ram_chip;
|
||||
pub mod rom_chip;
|
||||
pub mod memory_chip;
|
||||
|
||||
/// Mos6530 RRIOT
|
||||
/// Ram/Rom/IO/Timer
|
||||
///
|
||||
/// Represents a single Mos6530 RRIOT Chip
|
||||
///
|
||||
/// Used in the TIM-1, KIM-1
|
||||
///
|
||||
/// 1kb Rom
|
||||
/// 64 bytes RAM
|
||||
/// IO Ports (A, B)
|
||||
/// Timer
|
||||
pub struct Mos6530 {
|
||||
pub data: [u8; SIZE_1KB],
|
||||
pub ram: [u8; 64],
|
||||
pub port_a: u8,
|
||||
pub ddra: u8,
|
||||
pub in_a: u8,
|
||||
pub out_a: u8,
|
||||
pub port_b: u8,
|
||||
pub ddrb: u8,
|
||||
pub in_b: u8,
|
||||
pub out_b: u8,
|
||||
pub data_bus: u8,
|
||||
pub address_bus: u16,
|
||||
pub cs1: bool,
|
||||
pub cs2: bool,
|
||||
// when true, CPU is reading
|
||||
pub rw: bool,
|
||||
pub reset: bool,
|
||||
pub io_offset: u16,
|
||||
pub ram_offset: u16,
|
||||
pub rom_offset: u16
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
use crate::constants::constants_system::*;
|
||||
use crate::periph::mos6522::mos6522::Mos6522;
|
||||
|
||||
/// Mos6530 RRIOT
|
||||
/// Ram/Rom/IO/Timer
|
||||
///
|
||||
/// Represents a single Mos6530 RRIOT Chip
|
||||
///
|
||||
/// Used in the TIM-1, KIM-1
|
||||
///
|
||||
/// 1kb Rom
|
||||
/// 64 bytes RAM
|
||||
/// IO Ports (A, B)
|
||||
/// Timer
|
||||
pub struct Mos6530 {
|
||||
pub data: [u8; SIZE_1KB],
|
||||
pub ram: [u8; 64],
|
||||
pub porta: u8,
|
||||
pub portb: u8,
|
||||
pub data_bus: u8,
|
||||
pub address_bus: u16,
|
||||
pub cs1: bool,
|
||||
pub cs2: bool,
|
||||
// when true, CPU is reading
|
||||
pub rw: bool,
|
||||
pub reset: bool,
|
||||
pub io_offset: u16,
|
||||
pub ram_offset: u16,
|
||||
pub rom_offset: u16
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::constants::constants_system::SIZE_1KB;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn new(io_offset: u16,
|
||||
@@ -9,8 +9,14 @@ impl Mos6530 {
|
||||
Mos6530 {
|
||||
data: *data,
|
||||
ram: [0x00; 64],
|
||||
porta: 0,
|
||||
portb: 0,
|
||||
port_a: 0,
|
||||
ddra: 0,
|
||||
in_a: 0,
|
||||
out_a: 0,
|
||||
port_b: 0,
|
||||
ddrb: 0,
|
||||
in_b: 0,
|
||||
out_b: 0,
|
||||
data_bus: 0,
|
||||
address_bus: 0,
|
||||
cs1: false,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
|
||||
impl RamChip for Mos6530 {
|
||||
fn write(&mut self, offset: &u16, value: &u8) {
|
||||
debug!("🐙 Writing ${value:02x} to ${offset:04x}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
|
||||
impl RomChip for Mos6530 {
|
||||
fn program(new_data: &[u8]) -> Box<Self> {
|
||||
debug!("🐙 programming {}b to ROM", new_data.len());
|
||||
Box::new(Mos6530 {
|
||||
data: new_data.to_vec().try_into().unwrap(),
|
||||
ram: [0x00; 64],
|
||||
port_a: 0,
|
||||
ddra: 0,
|
||||
in_a: 0,
|
||||
out_a: 0,
|
||||
port_b: 0,
|
||||
ddrb: 0,
|
||||
in_b: 0,
|
||||
out_b: 0,
|
||||
data_bus: 0,
|
||||
address_bus: 0,
|
||||
cs1: false,
|
||||
cs2: false,
|
||||
rw: false,
|
||||
reset: false,
|
||||
io_offset: 0,
|
||||
ram_offset: 0,
|
||||
rom_offset: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use log::debug;
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
|
||||
impl Mos6530 {
|
||||
pub fn tick(&mut self, address_bus: u16, data_bus: u8, reset: bool, rw: bool) {
|
||||
|
||||
@@ -1,79 +1,93 @@
|
||||
use log::debug;
|
||||
use crate::constants::constants_system::{SIZE_1KB, SIZE_32KB};
|
||||
use crate::periph::mos6530::mos6530::Mos6530;
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use crate::periph::mos6530::Mos6530;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::gpio_chip::{GpioChip, GpioChipPorts};
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
use crate::traits::pia_chip::PiaChip;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
use crate::traits::via_chip::ViaChip;
|
||||
|
||||
impl BusDevice for Mos6530 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
self.ram_offset
|
||||
impl GpioChip for Mos6530 {
|
||||
fn set_port(&mut self, new_value: u8, port: GpioChipPorts) {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.port_a = new_value
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.port_b = new_value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 {
|
||||
self.min_offset() + SIZE_1KB as u16
|
||||
fn set_ddr(&mut self, new_ddr_value: u8, port: GpioChipPorts) {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.ddra = new_ddr_value
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.ddrb = new_ddr_value
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
fn get_port(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.port_a
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.port_b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
fn get_ddr(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.ddra
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.ddrb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
fn get_in(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.in_a
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.in_b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
fn get_out(&self, port: GpioChipPorts) -> u8 {
|
||||
match port {
|
||||
GpioChipPorts::PORT_A => {
|
||||
self.port_a
|
||||
}
|
||||
GpioChipPorts::PORT_B => {
|
||||
self.port_b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
fn update_ports(&mut self) {
|
||||
let (in_a, out_a) = Mos6520::split_port(self.port_a, self.ddra);
|
||||
self.in_a = in_a;
|
||||
self.out_a = out_a;
|
||||
|
||||
impl RamChip for Mos6530 {
|
||||
fn write(&mut self, offset: &u16, value: &u8) {
|
||||
debug!("🐙 Writing ${value:02x} to ${offset:04x}");
|
||||
// Port B
|
||||
let (in_b, out_b) = Mos6520::split_port(self.port_b, self.ddrb);
|
||||
self.in_b = in_b;
|
||||
self.out_b = out_b;
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryChip for Mos6530 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
debug!("🐙 Reading from ${offset:04x}");
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl RomChip for Mos6530 {
|
||||
fn program(new_data: &[u8]) -> Box<Self> {
|
||||
debug!("🐙 programming {}b to ROM", new_data.len());
|
||||
Box::new(Mos6530 {
|
||||
data: new_data.to_vec().try_into().unwrap(),
|
||||
ram: [0x00; 64],
|
||||
porta: 0,
|
||||
portb: 0,
|
||||
data_bus: 0,
|
||||
address_bus: 0,
|
||||
cs1: false,
|
||||
cs2: false,
|
||||
rw: false,
|
||||
reset: false,
|
||||
io_offset: 0,
|
||||
ram_offset: 0,
|
||||
rom_offset: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ViaChip for Mos6530 {
|
||||
fn set_port_ddr(&mut self, port_index: u8, value: u8) {
|
||||
debug!("🐙Setting DDR{port_index} to {value:02x}");
|
||||
}
|
||||
|
||||
fn set_port_data(&mut self, port_index: u8, value: u8) {
|
||||
debug!("🐙Setting PORT{port_index} to {value:02x}");
|
||||
}
|
||||
impl PiaChip for Mos6530 {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use crate::periph::mos6532::Mos6532;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
|
||||
impl BusDevice for Mos6532 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
self.ram_offset.min(self.io_offset)
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 { self.min_offset() + 0x40 }
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
self.address_bus
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
self.data_bus
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
self.address_bus = new_value
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
self.data_bus = new_value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
mod bus_device;
|
||||
mod new;
|
||||
|
||||
use log::debug;
|
||||
use crate::traits::backplane::Backplane;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
|
||||
pub struct Mos6532 {
|
||||
// R
|
||||
ram: Box<[u8; 64]>,
|
||||
// IO
|
||||
ddra: u8,
|
||||
porta: u8,
|
||||
in_a: u8,
|
||||
out_a: u8,
|
||||
ddrb: u8,
|
||||
portb: u8,
|
||||
in_b: u8,
|
||||
out_b: u8,
|
||||
// T
|
||||
|
||||
// Generic 6502 stuff
|
||||
read_mode: bool,
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
|
||||
// Part Specific
|
||||
ram_offset: u16,
|
||||
io_offset: u16,
|
||||
}
|
||||
|
||||
const MOS6532_DDRA_OFFSET: u8 = 0x00;
|
||||
const MOS6532_DDRB_OFFSET: u8 = 0x02;
|
||||
const MOS6532_PRTA_OFFSET: u8 = 0x01;
|
||||
const MOS6532_PRTB_OFFSET: u8 = 0x03;
|
||||
|
||||
impl Mos6532 {
|
||||
fn targets_ram(&self, offset: u16) -> bool {
|
||||
// we are at or above the offset AND we are under the end of ram
|
||||
offset >= self.ram_offset && offset <= self.ram_offset + 64
|
||||
}
|
||||
|
||||
fn targets_io(&self, offset: u16) -> bool {
|
||||
offset >= self.io_offset && offset <= self.io_offset + 4
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn targetting_parts() {
|
||||
// RAM from 0x0000 -> 0x0040
|
||||
// IO from 0x1000 -> 0x1004
|
||||
let via = Mos6532::new(0x0000, 0x1000);
|
||||
|
||||
assert!(via.targets_io(0x1000));
|
||||
assert!(!via.targets_ram(0x1000));
|
||||
|
||||
assert!(!via.targets_io(0x0000));
|
||||
assert!(via.targets_ram(0x0000));
|
||||
|
||||
assert!(!via.targets_io(0x4000));
|
||||
assert!(!via.targets_ram(0x4000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
use crate::periph::mos6532::Mos6532;
|
||||
|
||||
impl Mos6532 {
|
||||
pub fn new(ram_offset: u16, io_offset: u16) -> Self {
|
||||
Mos6532 {
|
||||
ram: Box::new([0x00; 64]),
|
||||
ddra: 0,
|
||||
porta: 0,
|
||||
in_a: 0,
|
||||
out_a: 0,
|
||||
ddrb: 0,
|
||||
portb: 0,
|
||||
in_b: 0,
|
||||
out_b: 0,
|
||||
read_mode: true,
|
||||
data_bus: 0x00,
|
||||
address_bus: 0x0000,
|
||||
ram_offset,
|
||||
io_offset
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::periph::mos6540::Mos6540;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::memory_chip::MemoryChip;
|
||||
|
||||
impl BusDevice for Mos6540 {
|
||||
fn min_offset(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn max_offset(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn address_bus(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn data_bus(&self) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_data_bus(&mut self, new_value: u8) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryChip for Mos6540 {
|
||||
fn read(&self, offset: &u16) -> u8 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// MOS6540
|
||||
// 2KByte 16kbit ROM
|
||||
|
||||
mod memory_chip;
|
||||
|
||||
struct Mos6540 {
|
||||
data_bus: u8,
|
||||
address_bus: u16,
|
||||
pub data: Box<u8>,
|
||||
offset: u16,
|
||||
max_offset: u16
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use log::debug;
|
||||
|
||||
pub trait Backplane {
|
||||
fn data_bus(&self) -> u8;
|
||||
fn address_bus(&self) -> u16;
|
||||
@@ -31,5 +33,11 @@ pub trait Backplane {
|
||||
///
|
||||
/// returns (data, irqa, irqb)
|
||||
fn tick_via(&mut self, address: u16, data: u8, cs0: bool, cs1: bool, rw: bool, rs0: bool, rs1: bool) -> (u8, bool, bool);
|
||||
}
|
||||
|
||||
/// tick_tod
|
||||
///
|
||||
/// Tick the TOD circutry
|
||||
///
|
||||
/// returns (time_irq)
|
||||
fn tick_tod(&mut self) -> bool;
|
||||
}
|
||||
|
||||
@@ -7,4 +7,3 @@ use crate::traits::bus_device::BusDevice;
|
||||
pub trait BusControlByte<T: BusDevice> {
|
||||
fn from_byte(source: u8) -> T;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
pub trait BusDevice {
|
||||
fn min_offset(&self) -> u16;
|
||||
fn max_offset(&self) -> u16;
|
||||
fn address_bus(&self) -> u16;
|
||||
fn data_bus(&self) -> u8;
|
||||
|
||||
fn set_address_bus(&mut self, new_value: u16);
|
||||
fn set_data_bus(&mut self, new_value: u8);
|
||||
fn talking_to_me(&self, address: u16) -> bool {
|
||||
address >= self.min_offset() && address < self.max_offset()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::gpio_chip::GpioChip;
|
||||
use crate::traits::timer_chip::TimerChip;
|
||||
|
||||
pub trait CiaChip: BusDevice + GpioChip + TimerChip {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
pub enum GpioChipPorts {
|
||||
PORT_A,
|
||||
PORT_B
|
||||
}
|
||||
|
||||
pub trait GpioChip {
|
||||
fn set_port(&mut self, new_a_value: u8, port: GpioChipPorts);
|
||||
fn set_ddr(&mut self, new_ddra_value: u8, port: GpioChipPorts);
|
||||
fn get_port(&self, port: GpioChipPorts) -> u8;
|
||||
fn get_ddr(&self, port: GpioChipPorts) -> u8;
|
||||
fn get_in(&self, port: GpioChipPorts) -> u8;
|
||||
fn get_out(&self, port: GpioChipPorts) -> u8;
|
||||
fn update_ports(&mut self);
|
||||
|
||||
fn split_port(value: u8, ddr: u8) -> (u8, u8) {
|
||||
let mut in_val = 0x00;
|
||||
let mut out_val = 0x00;
|
||||
|
||||
for bit in 0..8 {
|
||||
let mask = 1 << bit;
|
||||
let is_output = (ddr & mask) != 0;
|
||||
let is_set = (value & mask) != 0;
|
||||
|
||||
match (is_output, is_set) {
|
||||
(true, true) => out_val |= mask,
|
||||
(false, true) => in_val |= mask,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(in_val, out_val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::periph::mos6520::Mos6520;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_split() {
|
||||
let params = vec![
|
||||
(0xf0, 0xff, 0xf0, 0x0f),
|
||||
(0x0f, 0xff, 0x0f, 0xf0),
|
||||
(0xff, 0xff, 0xff, 0x00),
|
||||
(0x00, 0xff, 0x00, 0xff),
|
||||
(0x55, 0xff, 0x55, 0xaa)
|
||||
];
|
||||
|
||||
for (ddr, port, output, input) in params {
|
||||
|
||||
let (calc_ina, calc_oua) = Mos6520::split_port(port, ddr);
|
||||
|
||||
assert_eq!(calc_oua, output);
|
||||
assert_eq!(calc_ina, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,11 @@ pub mod bus_control_byte;
|
||||
pub mod memory_chip;
|
||||
pub mod rom_chip;
|
||||
pub mod ram_chip;
|
||||
pub mod via_chip;
|
||||
pub mod pia_chip;
|
||||
pub mod backplane;
|
||||
mod timer_chip;
|
||||
pub mod timer_chip;
|
||||
pub mod serial_interface_chip;
|
||||
pub mod parallel_interface_chip;
|
||||
pub mod time_of_day_chip;
|
||||
pub mod gpio_chip;
|
||||
mod cia_chip;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub trait ParallelInterface {}
|
||||
@@ -0,0 +1,7 @@
|
||||
use log::debug;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::gpio_chip::{GpioChip, GpioChipPorts};
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
|
||||
pub trait PiaChip: BusDevice + GpioChip {}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub trait SerialInterface {}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub trait TimeOfDay {}
|
||||
@@ -1,10 +1,11 @@
|
||||
use log::debug;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
|
||||
pub trait TimerChip: BusDevice {
|
||||
/// set_timer
|
||||
///
|
||||
/// Set current value of timer
|
||||
fn set_timer(&mut self, timer_index: u8, new_value: u16);
|
||||
fn set_timer(&mut self, timer_index: u8, new_value: u16, divisor: u16);
|
||||
|
||||
/// get_timer
|
||||
///
|
||||
@@ -13,4 +14,10 @@ pub trait TimerChip: BusDevice {
|
||||
|
||||
/// Advance Timer circutry 1 cycle
|
||||
fn tick(&mut self);
|
||||
|
||||
/// Edge Detect Control
|
||||
fn set_edge_detect(&mut self, edge: bool);
|
||||
|
||||
/// Interrupt Control
|
||||
fn set_interrupt(&mut self, enabled: bool);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
use log::debug;
|
||||
use crate::traits::bus_device::BusDevice;
|
||||
use crate::traits::ram_chip::RamChip;
|
||||
use crate::traits::rom_chip::RomChip;
|
||||
|
||||
pub trait ViaChip: RamChip + RomChip + BusDevice {
|
||||
/// set_port_ddr
|
||||
///
|
||||
/// Sets the Data Direction Register in the VIA chip for the specified
|
||||
/// port.
|
||||
fn set_port_ddr(&mut self, port_index: u8, value: u8) {
|
||||
debug!("Please implement this.");
|
||||
}
|
||||
|
||||
/// set_port_data
|
||||
///
|
||||
/// Sets the
|
||||
fn set_port_data(&mut self, port_index: u8, value: u8) {
|
||||
debug!("Please implement this.");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#[test]
|
||||
fn test() {
|
||||
assert!(true);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod periph;
|
||||
mod mos6502;
|
||||
|
||||
#[test]
|
||||
fn smoke() { assert!(true) }
|
||||
@@ -0,0 +1,18 @@
|
||||
use core::mos6502flags::Mos6502Flags;
|
||||
|
||||
#[test]
|
||||
fn sanity() {
|
||||
let f = Mos6502Flags::default();
|
||||
let magic_byte = 0b1110_1101;
|
||||
let magic_flags = Mos6502Flags {
|
||||
carry: true,
|
||||
zero: false,
|
||||
interrupt: true,
|
||||
decimal: true,
|
||||
break_flag: false,
|
||||
overflow: true,
|
||||
negative: true,
|
||||
};
|
||||
|
||||
assert_eq!(magic_flags, Mos6502Flags::from_byte(magic_byte));
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
#[test]
|
||||
fn test_instruction_table_completeness() {
|
||||
use core::instruction_table::INSTRUCTION_TABLE; // Adjust to your actual path
|
||||
|
||||
let mut defined_count = 0;
|
||||
let mut defined_opcodes = vec![];
|
||||
|
||||
for (i, entry) in INSTRUCTION_TABLE.iter().enumerate() {
|
||||
if let Some(info) = entry {
|
||||
defined_count += 1;
|
||||
defined_opcodes.push(i);
|
||||
// Optional: sanity check
|
||||
assert!(
|
||||
info.length > 0 && info.cycles > 0,
|
||||
"Invalid OpInfo at opcode {:#04x?}",
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("Defined opcodes: {}", defined_count);
|
||||
for i in 0..256 {
|
||||
if defined_opcodes.contains(&i) {
|
||||
print!("{:02x} ", i);
|
||||
}
|
||||
}
|
||||
println!("\nMissing opcodes:");
|
||||
for i in 0..256 {
|
||||
if !defined_opcodes.contains(&i) {
|
||||
print!("{:02x} ", i);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
// The standard 6502 has 151 documented opcodes
|
||||
assert_eq!(
|
||||
defined_count, 151,
|
||||
"Expected 151 opcodes, found {}",
|
||||
defined_count
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
mod flags;
|
||||
mod instructions;
|
||||
mod instruction_table;
|
||||
@@ -0,0 +1,153 @@
|
||||
use core::constants::constants_system::*;
|
||||
use core::constants::constants_test::*;
|
||||
use core::periph::at28c256::At28C256;
|
||||
use core::traits::bus_device::BusDevice;
|
||||
use core::traits::memory_chip::MemoryChip;
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksum_binary_loads() {
|
||||
let path = format!("{}/{}", TEST_PERIPH_AT28C256_ROOT, "checksum.bin");
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes.", bytes.len());
|
||||
bytes
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("FAIL to read rom.");
|
||||
panic!("No rom no run.");
|
||||
}
|
||||
};
|
||||
|
||||
let mut rom = At28C256::new(0x0000, 0x3fff, bytes);
|
||||
|
||||
assert_eq!(rom.checksum(), 0x58);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_chunks_come_back_ok() {
|
||||
let test_data = (0..255).collect();
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, test_data);
|
||||
|
||||
let chunks = chip.chunks(16);
|
||||
assert_eq!(chunks.len(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_blocks_come_back_ok() {
|
||||
let test_data = (0..=3).collect();
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, test_data);
|
||||
|
||||
let chunks = chip.chunks(16);
|
||||
assert_eq!(chunks.len(), 1);
|
||||
for chunk in chunks {
|
||||
assert_eq!(chunk.len(), 4);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn programmed_data_reads_back_same() {
|
||||
let mut data = At28C256::default();
|
||||
for i in 0..SIZE_32KB {
|
||||
data.data[i] = 0xeau8;
|
||||
}
|
||||
for offset in 0..(SIZE_32KB - 1) {
|
||||
assert_eq!(0xea, data.read(&(offset as u16)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_correctly_for_zero() {
|
||||
let data1 = [0x00u8; SIZE_32KB];
|
||||
assert_eq!(0x00, At28C256::checksum_static(&data1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_1_byte() {
|
||||
let data = [0xff; 1];
|
||||
assert_eq!(0xff, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_2_bytes() {
|
||||
let data = [0xff; 2];
|
||||
// 0xff + 0xff = 0x1fe
|
||||
assert_eq!(0xfe, At28C256::checksum_static(&data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checksums_calculate_for_first_80_bytes() {
|
||||
println!("STARTING TEST");
|
||||
let mut checksum = 0x00;
|
||||
|
||||
let path = format!("{}{}", TEST_PERIPH_AT28C256_ROOT, "/checksum.bin");
|
||||
println!("READING [{path}]");
|
||||
let data = fs::read(path);
|
||||
match data {
|
||||
Ok(bytes) => {
|
||||
println!("Read {} bytes", bytes.len());
|
||||
checksum = At28C256::checksum_static(&bytes);
|
||||
println!("Checksum: 0x{:02x}", checksum);
|
||||
}
|
||||
Err(e) => eprintln!("Failed to read file: {}", e),
|
||||
}
|
||||
assert_eq!(0x58, checksum);
|
||||
println!("TEST COMPLETE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_data_set_reads() {
|
||||
let mut x = At28C256::new(0x0000, 0x3fff, vec![0xea; SIZE_32KB]);
|
||||
|
||||
// set both...
|
||||
x.set_address_bus(0x3000);
|
||||
x.set_data_bus(0xab);
|
||||
assert_eq!(0xab, x.data_bus());
|
||||
assert_eq!(0x3000, x.address_bus());
|
||||
|
||||
// ...set address...
|
||||
x.set_address_bus(0x2000);
|
||||
assert_eq!(0x2000, x.address_bus());
|
||||
assert_eq!(0xab, x.data_bus());
|
||||
|
||||
// ...set data.
|
||||
x.set_data_bus(0xba);
|
||||
assert_eq!(0xba, x.data_bus());
|
||||
assert_eq!(0x2000, x.address_bus());
|
||||
}
|
||||
|
||||
fn programming_chip_changes_contents() {
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, vec![]);
|
||||
|
||||
assert_eq!(0x00, chip.read(&0x0000));
|
||||
|
||||
let new_data: Vec<u8> = vec![0xff, 0xff, 0xff, 0xff];
|
||||
chip.program(new_data.into());
|
||||
assert_eq!(0xff, chip.read(&0x0000));
|
||||
assert_eq!(0x00, chip.read(&0x05));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correct_flags_required() {
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, vec![0xff]);
|
||||
assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, true));
|
||||
assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, true, false));
|
||||
assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, false, true, false));
|
||||
assert_eq!(0xff, chip.signal_tick(0x0000, 0xab, true, true, true));
|
||||
assert_eq!(0xab, chip.signal_tick(0x0000, 0xab, true, false, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loaded_rom_is_readable() {
|
||||
let mut chip = At28C256::new(0x0000, 0x3fff, [0xff; SIZE_32KB].to_vec());
|
||||
|
||||
for index in 0..SIZE_32KB {
|
||||
assert_eq!(chip.data[index as usize], 0xff);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use core::constants::constants_system::*;
|
||||
use rand::random;
|
||||
use core::traits::memory_chip::MemoryChip;
|
||||
use core::periph::hm62256::Hm62256;
|
||||
use core::traits::ram_chip::RamChip;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn written_data_comes_back() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// 100,000 random read/writes to ram that all read back right
|
||||
for _ in 0..100_000 {
|
||||
let mut offset: u16 = random();
|
||||
println!("Size = {SIZE_32KB}");
|
||||
let value: u8 = random();
|
||||
println!("Wrote [{value:02x}] to [{offset:04x}]");
|
||||
ram.write(&offset, &value);
|
||||
|
||||
assert_eq!(ram.read(&offset), value)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to_memory_read_back_works_at_0() {
|
||||
let mut ram = Hm62256::default();
|
||||
|
||||
// load the data to ram
|
||||
ram.tick(0x0000, 0xab, false, true);
|
||||
// read the data back
|
||||
let (_, new_data) = ram.tick(0x0000, 0x00, true, true);
|
||||
|
||||
assert_eq!(new_data, 0xab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_space_is_round() {
|
||||
// addresses written past the last address 'loop' back to 0+(offset - MAX_SIZE)
|
||||
let max_offset = SIZE_32KB;
|
||||
let test_offset = max_offset;
|
||||
|
||||
// all zero
|
||||
let mut ram = Hm62256::default();
|
||||
// write FF to the addresss after the last
|
||||
ram.write(&(test_offset as u16), &0xff);
|
||||
|
||||
// check all the ram for anything that isn't 0x00
|
||||
|
||||
assert_eq!(ram.read(&(0x0000)), 0xff);
|
||||
for offset in 1..SIZE_32KB {
|
||||
println!("Testing offset {offset:04x} for 0x00");
|
||||
assert_eq!(ram.read(&(offset as u16)), 0x00);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use core::periph::kim1_keypad::Kim1Keypad;
|
||||
|
||||
#[test]
|
||||
fn keys_are_pressed() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
for index in 0..23 {
|
||||
assert!(!kb.is_pressed(index));
|
||||
kb.press_key(index);
|
||||
assert!(kb.is_pressed(index));
|
||||
|
||||
kb.release_key(index);
|
||||
assert!(!kb.is_pressed(index));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stepping_changes() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.set_stepping(false);
|
||||
|
||||
assert!(!kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(kb.stepping);
|
||||
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
kb.toggle_stepping();
|
||||
|
||||
assert!(!kb.stepping);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn out_of_range() {
|
||||
let mut kb = Kim1Keypad::new();
|
||||
|
||||
kb.press_key(24);
|
||||
assert!(kb.is_pressed(1));
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod at28c256;
|
||||
mod mos6522;
|
||||
mod mos6520;
|
||||
mod kim1_keypad;
|
||||
mod hm62256;
|
||||
@@ -0,0 +1,56 @@
|
||||
use core::periph::mos6520::Mos6520;
|
||||
|
||||
const DDRA_OFFSET: u8 = 0x00;
|
||||
const PORTA_OFFSET: u8 = 0x01;
|
||||
const DDRB_OFFSET: u8 = 0x02;
|
||||
const PORTB_OFFSET: u8 = 0x03;
|
||||
|
||||
fn actual(base: u16, offset: u8) -> u16 {
|
||||
base + offset as u16
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ddrb_tests() {
|
||||
let mut x = Mos6520::new(0x1000);
|
||||
let params = vec![
|
||||
// Offset data outa ina ddra porta
|
||||
(DDRB_OFFSET, 0xff, 0x00, 0x00, 0xff, 0x00),
|
||||
(PORTB_OFFSET, 0xff, 0xff, 0x00, 0xff, 0xff),
|
||||
(DDRB_OFFSET, 0xaa, 0xaa, 0x55, 0xaa, 0xff),
|
||||
(DDRB_OFFSET, 0x55, 0x55, 0xaa, 0x55, 0xff),
|
||||
(PORTB_OFFSET, 0xf0, 0x50, 0xa0, 0x55, 0xf0),
|
||||
(PORTB_OFFSET, 0x0f, 0x05, 0x0a, 0x55, 0x0f),
|
||||
(DDRB_OFFSET, 0xff, 0x0f, 0x00, 0xff, 0x0f)
|
||||
];
|
||||
|
||||
for (offset, data, outb, inb, ddrb, portb) in params {
|
||||
x.tick(actual(x.offset, offset), data);
|
||||
assert_eq!(outb, x.out_b);
|
||||
assert_eq!(inb, x.in_b);
|
||||
assert_eq!(ddrb, x.ddrb);
|
||||
assert_eq!(portb, x.port_b);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ddra_tests() {
|
||||
let mut x = Mos6520::new(0x1000);
|
||||
let params = vec![
|
||||
// Offset data outa ina ddra porta
|
||||
(DDRA_OFFSET, 0xff, 0x00, 0x00, 0xff, 0x00),
|
||||
(PORTA_OFFSET, 0xff, 0xff, 0x00, 0xff, 0xff),
|
||||
(DDRA_OFFSET, 0xaa, 0xaa, 0x55, 0xaa, 0xff),
|
||||
(DDRA_OFFSET, 0x55, 0x55, 0xaa, 0x55, 0xff),
|
||||
(PORTA_OFFSET, 0xf0, 0x50, 0xa0, 0x55, 0xf0),
|
||||
(PORTA_OFFSET, 0x0f, 0x05, 0x0a, 0x55, 0x0f),
|
||||
(DDRA_OFFSET, 0xff, 0x0f, 0x00, 0xff, 0x0f)
|
||||
];
|
||||
|
||||
for (offset, data, outa, ina, ddra, porta) in params {
|
||||
x.tick(actual(x.offset, offset), data);
|
||||
assert_eq!(outa, x.out_a);
|
||||
assert_eq!(ina, x.in_a);
|
||||
assert_eq!(ddra, x.ddra);
|
||||
assert_eq!(porta, x.port_a);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use core::periph::mos6522::Mos6522;
|
||||
use core::constants::constants_via6522::*;
|
||||
|
||||
#[test]
|
||||
fn registers() {
|
||||
let mut x = Mos6522::new(0x0000);
|
||||
x.tick(VIA6522_DDRA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.dda, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.dda, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_DDRB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ddb, 0b0000_0000);
|
||||
x.tick(VIA6522_DDRB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ddb, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORA as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.ora, 0b0000_0000);
|
||||
x.tick(VIA6522_ORA as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.ora, 0b1111_1111);
|
||||
|
||||
x.tick(VIA6522_ORB as u16, 0b0000_0000, false, true);
|
||||
assert_eq!(x.orb, 0b0000_0000);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.orb, 0b1111_1111);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_porta() {
|
||||
let mut x = Mos6522::new(0x0000);
|
||||
x.tick(VIA6522_DDRA as u16, 0b1010_1010, false, true);
|
||||
x.tick(VIA6522_ORA as u16,0b1111_1111, false, true);
|
||||
assert_eq!(x.porta, 0b1010_1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_output_portb() {
|
||||
let mut x = Mos6522::new(0x0000);
|
||||
x.tick(VIA6522_DDRB as u16, 0b0101_0101, false, true);
|
||||
x.tick(VIA6522_ORB as u16, 0b1111_1111, false, true);
|
||||
assert_eq!(x.portb, 0b0101_0101);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "egui"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
eframe = "0.27"
|
||||
core = { path = "../core" }
|
||||
@@ -0,0 +1,77 @@
|
||||
use eframe::egui;
|
||||
use core::computers::rom_only::RomOnlyComputer;
|
||||
use core::periph::at28c256::At28C256;
|
||||
|
||||
struct RomGui {
|
||||
computer: RomOnlyComputer,
|
||||
address_input: String,
|
||||
data_input: String,
|
||||
last_result: Option<u8>,
|
||||
}
|
||||
|
||||
impl Default for RomGui {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
computer: RomOnlyComputer {
|
||||
rom: At28C256::new(0x0000, 0x3fff, [0xff; 0x4000].to_vec()),
|
||||
data_bus: 0x00,
|
||||
address_bus: 0x0000,
|
||||
read_mode: true,
|
||||
},
|
||||
address_input: "0000".to_string(),
|
||||
data_input: "00".to_string(),
|
||||
last_result: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for RomGui {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("RomOnlyComputer");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Address (hex):");
|
||||
ui.text_edit_singleline(&mut self.address_input);
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Data (hex):");
|
||||
ui.text_edit_singleline(&mut self.data_input);
|
||||
});
|
||||
|
||||
ui.checkbox(&mut self.computer.read_mode, "Read Mode");
|
||||
|
||||
if ui.button("Tick").clicked() {
|
||||
if let (Ok(addr), Ok(data)) = (
|
||||
u16::from_str_radix(&self.address_input.trim(), 16),
|
||||
u8::from_str_radix(&self.data_input.trim(), 16),
|
||||
) {
|
||||
self.computer.address_bus = addr;
|
||||
self.computer.data_bus = data;
|
||||
|
||||
let result = self.computer.rom.signal_tick(addr, data, true, true, true);
|
||||
self.last_result = Some(result);
|
||||
|
||||
println!(
|
||||
"Tick: A=${:04X} D=${:02X} R={} -> ${:02X}",
|
||||
addr, data, self.computer.read_mode, result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(result) = self.last_result {
|
||||
ui.label(format!("Last result: ${:02X}", result));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
let options = eframe::NativeOptions::default();
|
||||
eframe::run_native(
|
||||
"RomOnlyComputer GUI",
|
||||
options,
|
||||
Box::new(|_cc| Box::new(RomGui::default())),
|
||||
)
|
||||
}
|
||||
@@ -1,88 +1,91 @@
|
||||
use eframe::egui;
|
||||
use core::periph::at28c256::At28C256;
|
||||
use core::computers::rom_only::RomOnlyComputer;
|
||||
struct MyApp {
|
||||
address: String,
|
||||
data: String,
|
||||
cpu_read: bool,
|
||||
computer: RomOnlyComputer
|
||||
use macroquad::prelude::*;
|
||||
use core::traits::backplane::Backplane;
|
||||
|
||||
#[macroquad::main("RomOnlyComputer GUI")]
|
||||
async fn main() {
|
||||
let mut computer = RomOnlyComputer {
|
||||
rom: At28C256::new(0x0000, 0x3fff, [0xea; 0x4000].to_vec()),
|
||||
data_bus: 0x00,
|
||||
address_bus: 0x0000,
|
||||
read_mode: true,
|
||||
};
|
||||
|
||||
let mut input_addr = String::new();
|
||||
let mut input_data = String::new();
|
||||
|
||||
loop {
|
||||
clear_background(GRAY);
|
||||
let mut y = 20.0;
|
||||
|
||||
draw_text("RomOnlyComputer", 20.0, y, 30.0, WHITE);
|
||||
y += 40.0;
|
||||
|
||||
// Address input
|
||||
draw_text("Address Bus (hex):", 20.0, y, 24.0, WHITE);
|
||||
y += 30.0;
|
||||
draw_input_box(&mut input_addr, 20.0, y, 100.0, 30.0);
|
||||
y += 40.0;
|
||||
|
||||
// Data input
|
||||
draw_text("Data Bus (hex):", 20.0, y, 24.0, WHITE);
|
||||
y += 30.0;
|
||||
draw_input_box(&mut input_data, 20.0, y, 100.0, 30.0);
|
||||
y += 40.0;
|
||||
|
||||
// Read mode checkbox
|
||||
let read_mode_rect = Rect::new(20.0, y, 20.0, 20.0);
|
||||
draw_rectangle_lines(read_mode_rect.x, read_mode_rect.y, read_mode_rect.w, read_mode_rect.h, 2.0, WHITE);
|
||||
if computer.read_mode() {
|
||||
draw_text("✔", read_mode_rect.x + 2.0, read_mode_rect.y + 18.0, 20.0, GREEN);
|
||||
}
|
||||
draw_text("Read Mode", read_mode_rect.x + 30.0, read_mode_rect.y + 18.0, 20.0, WHITE);
|
||||
if is_mouse_button_pressed(MouseButton::Left) && read_mode_rect.contains(mouse_position().into()) {
|
||||
computer.set_read_mode(!computer.read_mode());
|
||||
}
|
||||
y += 40.0;
|
||||
|
||||
// Tick button
|
||||
let button_rect = Rect::new(20.0, y, 100.0, 40.0);
|
||||
draw_rectangle(button_rect.x, button_rect.y, button_rect.w, button_rect.h, DARKGRAY);
|
||||
draw_text("Tick", button_rect.x + 25.0, button_rect.y + 28.0, 24.0, WHITE);
|
||||
if is_mouse_button_pressed(MouseButton::Left) && button_rect.contains(mouse_position().into()) {
|
||||
if let (Ok(addr), Ok(data)) = (
|
||||
u16::from_str_radix(&input_addr.trim_start_matches("0x"), 16),
|
||||
u8::from_str_radix(&input_data.trim_start_matches("0x"), 16),
|
||||
) {
|
||||
computer.set_address_bus(addr);
|
||||
computer.set_data_bus(data);
|
||||
// let result = computer.rom.tick(addr, data, computer.read_mode);
|
||||
println!(
|
||||
"Tick: A=${:04X} D=${:02X} R={} -> ", // ${:02X}",
|
||||
addr, data, computer.read_mode, // result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
next_frame().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MyApp {
|
||||
fn default() -> Self {
|
||||
let rom_data = vec![0x01, 0x02, 0x03, 0x04];
|
||||
Self {
|
||||
address: String::new(),
|
||||
data: String::new(),
|
||||
cpu_read: false,
|
||||
computer: RomOnlyComputer::program(rom_data), // Example memory: 0x00 to 0xFF
|
||||
fn draw_input_box(value: &mut String, x: f32, y: f32, w: f32, h: f32) {
|
||||
draw_rectangle(x, y, w, h, DARKGRAY);
|
||||
let mut display = value.clone();
|
||||
if get_time() as u64 % 2 == 0 {
|
||||
display.push('_');
|
||||
}
|
||||
draw_text(&display, x + 5.0, y + 22.0, 20.0, WHITE);
|
||||
|
||||
let rect = Rect::new(x, y, w, h);
|
||||
if is_mouse_button_pressed(MouseButton::Left) && rect.contains(mouse_position().into()) {
|
||||
if let Some(chr) = get_char_pressed() {
|
||||
if chr.is_ascii_hexdigit() && value.len() < 4 {
|
||||
value.push(chr.to_ascii_uppercase());
|
||||
}
|
||||
}
|
||||
if is_key_pressed(KeyCode::Backspace) {
|
||||
value.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Memory Inspector");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Address:");
|
||||
ui.text_edit_singleline(&mut self.address);
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Data:");
|
||||
ui.text_edit_singleline(&mut self.data);
|
||||
});
|
||||
|
||||
ui.checkbox(&mut self.cpu_read, "CPU Read");
|
||||
|
||||
if ui.button("Tick").clicked() {
|
||||
println!(
|
||||
"Ticked with Address: {}, Data: {}, CPU Read: {}",
|
||||
self.address, self.data, self.cpu_read
|
||||
);
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("Address Bus ${:?}", self.address).as_str());
|
||||
ui.label(format!("Data Bus ${:?}", self.data).as_str());
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
ui.label("Memory View (Hex Dump):");
|
||||
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
let bytes_per_row = 16;
|
||||
for (i, chunk) in self.computer.rom_chunks(bytes_per_row).enumerate() {
|
||||
let address = i * bytes_per_row;
|
||||
let hex_values: String = chunk
|
||||
.iter()
|
||||
.map(|b| format!("{:02X} ", b))
|
||||
.collect();
|
||||
|
||||
let ascii_values: String = chunk
|
||||
.iter()
|
||||
.map(|b| {
|
||||
if b.is_ascii_graphic() {
|
||||
*b as char
|
||||
} else {
|
||||
'.'
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
ui.monospace(format!("{:08X}: {:<48} {}", address, hex_values, ascii_values));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
let options = eframe::NativeOptions::default();
|
||||
eframe::run_native(
|
||||
"Memory Inspector",
|
||||
options,
|
||||
Box::new(|_cc| Box::new(MyApp::default())),
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user