use std::sync::mpsc;
use i8051::peripheral::{Serial, Timer, TimerTick};
use i8051::sfr::{SFR_P0, SFR_P1, SFR_P2, SFR_P3};
use i8051::{Cpu, CpuContext, CpuView, PortMapper, ReadOnlyMemoryMapper};
use crate::Key;
static LK401_ROM: &[u8] = include_bytes!("23-LK401.BIN");
pub struct LK401Hardware {
cpu: Cpu,
system: LK401System,
serial_in: mpsc::Sender<u8>,
serial_out: mpsc::Receiver<u8>,
}
impl LK401Hardware {
pub fn new() -> Self {
let (serial, serial_in, serial_out) = Serial::new(60);
let mut ports = LK401Ports::default();
ports.ports[0].0 = 0x00;
ports.ports[1].0 = 0xFF;
ports.ports[2].0 = 0x00;
ports.ports[3].0 = 0xFF;
let mut this = Self {
cpu: Cpu::new(),
system: LK401System {
ports,
serial,
timer: Timer::default(),
},
serial_in,
serial_out,
};
while this.cpu.pc != 0x0092 {
this.tick();
}
this
}
pub fn tick(&mut self) {
self.cpu.step(&mut self.system);
self.system.serial.tick(&mut self.cpu);
let tick = self.system.timer.prepare_tick(&mut self.cpu, &self.system);
self.system.timer.tick(&mut self.cpu, tick);
self.system.ports.port2_prev = self.system.ports.ports[2].1;
}
}
struct LK401Ports {
ports: [(u8, u8); 4],
key_matrix: [bool; 0x70],
port2_prev: u8,
key_index: u8,
key_pressed: bool,
}
impl Default for LK401Ports {
fn default() -> Self {
Self {
ports: [(0, 0); 4],
key_matrix: [false; 0x70],
port2_prev: 0,
key_index: 0,
key_pressed: false,
}
}
}
impl PortMapper for LK401Ports {
type WriteValue = (u8, u8);
fn interest<C: CpuView>(&self, cpu: &C, addr: u8) -> bool {
addr == SFR_P0 || addr == SFR_P1 || addr == SFR_P2 || addr == SFR_P3 || addr == 0x8E
}
fn read<C: CpuView>(&self, cpu: &C, addr: u8) -> u8 {
if addr != SFR_P2 {
eprintln!("{:02X} read @ {:04X}", addr, cpu.pc_ext());
}
match addr {
SFR_P0 => self.ports[0].0,
SFR_P1 => self.ports[1].0,
SFR_P2 => (self.key_pressed as u8) << 2,
SFR_P3 => self.ports[3].0,
0x8E => 0,
_ => unreachable!(),
}
}
fn prepare_write<C: CpuView>(&self, cpu: &C, addr: u8, value: u8) -> Self::WriteValue {
if addr != SFR_P2 {
} else {
let delta = value ^ self.ports[2].1;
if delta & (1 << 0) != 0 {
}
if delta & (1 << 1) != 0 {
}
if delta & (1 << 2) != 0 {
}
}
(addr, value)
}
fn write(&mut self, (addr, value): Self::WriteValue) {
match addr {
SFR_P0 => self.ports[0].1 = value,
SFR_P1 => {}
SFR_P2 => {
let delta = value ^ self.ports[2].1;
if delta & (1 << 0) != 0 && (value & (1 << 0) == 0) {
self.key_index = (self.key_index + 1) % (self.key_matrix.len() as u8);
if self.key_index == 0 {
self.key_pressed = false;
}
let old_key_pressed = self.key_pressed;
self.key_pressed = self.key_matrix[self.key_index as usize];
if old_key_pressed != self.key_pressed {
}
}
if delta & (1 << 1) != 0 && (value & (1 << 1) == 0) {
self.key_index = self.key_matrix.len() as u8 - 1;
}
self.ports[2].1 = value;
}
SFR_P3 => self.ports[3].1 = value,
0x8E => {}
_ => unreachable!(),
}
}
fn read_latch<C: CpuView>(&self, cpu: &C, addr: u8) -> u8 {
match addr {
SFR_P0 => self.ports[0].1,
SFR_P1 => self.ports[1].1,
SFR_P2 => self.ports[2].1,
SFR_P3 => self.ports[3].1,
_ => unreachable!(),
}
}
}
struct LK401System {
ports: LK401Ports,
serial: Serial,
timer: Timer,
}
impl PortMapper for LK401System {
type WriteValue = <(LK401Ports, (Serial, Timer)) as PortMapper>::WriteValue;
fn interest<C: CpuView>(&self, cpu: &C, addr: u8) -> bool {
self.ports.interest(cpu, addr)
|| self.serial.interest(cpu, addr)
|| self.timer.interest(cpu, addr)
}
fn read<C: CpuView>(&self, cpu: &C, addr: u8) -> u8 {
(&self.ports, (&self.serial, &self.timer)).read(cpu, addr)
}
fn prepare_write<C: CpuView>(&self, cpu: &C, addr: u8, value: u8) -> Self::WriteValue {
(&self.ports, (&self.serial, &self.timer)).prepare_write(cpu, addr, value)
}
fn write(&mut self, write: Self::WriteValue) {
(&mut self.ports, (&mut self.serial, &mut self.timer)).write(write)
}
fn read_latch<C: CpuView>(&self, cpu: &C, addr: u8) -> u8 {
(&self.ports, (&self.serial, &self.timer)).read_latch(cpu, addr)
}
}
impl ReadOnlyMemoryMapper for LK401System {
fn len(&self) -> u32 {
LK401_ROM.len() as u32
}
fn read<C: CpuView>(&self, cpu: &C, addr: u32) -> u8 {
if addr >= LK401_ROM.len() as u32 {
return 0xff;
}
LK401_ROM[addr as usize]
}
}
impl CpuContext for LK401System {
type Ports = LK401System;
type Xdata = ();
type Code = LK401System;
fn ports(&self) -> &Self::Ports {
self
}
fn ports_mut(&mut self) -> &mut Self::Ports {
self
}
fn xdata(&self) -> &Self::Xdata {
unreachable!()
}
fn xdata_mut(&mut self) -> &mut Self::Xdata {
unreachable!()
}
fn code(&self) -> &Self::Code {
&self
}
fn code_mut(&mut self) -> &mut Self::Code {
self
}
}
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use crate::LK201Command;
use crate::{ALL_KEYS, AutoRepeatRegister, Division, Key, KeyMode, LK201, Led, Volume};
use tracing::Level;
use super::*;
#[test]
fn test_hardware_lk401() {
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
let mut hardware = LK401Hardware::new();
eprintln!("ready");
for _ in 0..0x2000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
eprintln!("serial out: {:02X}", byte);
}
}
hardware.serial_in.send(0xE9).unwrap();
for _ in 0..0x2000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
eprintln!("serial out: {:02X}", byte);
}
}
eprintln!("pressing keys");
let mut matrix = [0_u8; 0x6F];
for i in 0..hardware.system.ports.key_matrix.len() {
eprintln!("pressing key {:02X}", i);
hardware.system.ports.key_matrix[i] = true;
for _ in 0..0x2000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
eprintln!(" - serial out: {:02X}", byte);
matrix[i] = byte;
}
}
hardware.system.ports.key_matrix[i] = false;
for _ in 0..0x2000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
eprintln!(" + serial out: {:02X}", byte);
}
}
}
eprintln!("matrix: {:02X?}", matrix);
}
#[test]
fn test_hardware_commands() {
for i in 0x80..=0xFF {
let mut hardware = LK401Hardware::new();
for _ in 0..0x1000 {
hardware.tick();
_ = hardware.serial_out.try_recv();
}
hardware.serial_in.send(i).unwrap();
let mut response = None;
for _ in 0..0x1000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
response = Some(byte);
}
}
match response {
None => {
eprintln!("{i:02X}: Waiting for more data");
hardware.serial_in.send(0x80).unwrap();
for _ in 0..0x1000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
eprintln!("{byte:02X}");
}
}
}
Some(0xB6) => {}
Some(0xB7) => {
let cmd = LK201Command::try_from(&VecDeque::from_iter([i]));
eprintln!("{i:02X}: Keyboard locked (cmd = {cmd:?}");
}
Some(0xBA) => {
let cmd = LK201Command::try_from(&VecDeque::from_iter([i]));
eprintln!("{i:02X}: Mode change ack (cmd = {cmd:?}");
}
Some(b) => {
eprintln!("{i:02X}: Unknown response ({b:02X})");
}
}
}
}
#[test]
fn test_response() {
use LK201Command::*;
for cmd in [
KeyClickEnable(Volume::new(2).unwrap()),
LedEnable(Led::new(0x1)),
EnableRepeat,
DisableRepeat,
RepeatToDown,
TempNoRepeat,
SoundClick,
SetAutoRepeat {
register: AutoRepeatRegister::new(0).unwrap(),
timeout: 100,
rate: 30,
},
SetMode {
mode: KeyMode::AutoDown,
division: Division::new(1).unwrap(),
},
SetModeWithAutoRepeat {
mode: KeyMode::AutoDown,
division: Division::new(1).unwrap(),
register: AutoRepeatRegister::new(0).unwrap(),
},
SetDefaults,
] {
let mut hardware = LK401Hardware::new();
for _ in 0..0x1000 {
hardware.tick();
_ = hardware.serial_out.try_recv();
}
let bytes: Vec<u8> = cmd.into();
for b in &bytes {
hardware.serial_in.send(*b).unwrap();
}
let mut hw_response = vec![];
for _ in 0..0x1000 {
hardware.tick();
if let Ok(byte) = hardware.serial_out.try_recv() {
hw_response.push(byte);
}
}
eprintln!("{cmd:?} ({bytes:02X?}) -> {hw_response:02X?}");
if let Some(response) = cmd.response() {
assert_eq!(response.to_bytes(), hw_response);
} else {
assert_eq!(hw_response, vec![]);
}
}
}
}