From 5bfde2bff0517a64ccfd7598ed0aace150fa8640 Mon Sep 17 00:00:00 2001 From: transistor Date: Sat, 6 Nov 2021 21:46:17 -0700 Subject: [PATCH] Added TRS-80 simulation to test Z80 impl --- binaries/trs80/level1.rom | Bin 0 -> 4096 bytes binaries/trs80/level2.rom | Bin 0 -> 12288 bytes frontends/moa-minifb/src/bin/moa-trs80.rs | 43 ++ src/host/keys.rs | 112 ++++ src/machines/mod.rs | 1 + src/machines/trs80.rs | 32 + src/peripherals/mod.rs | 1 + src/peripherals/trs80/charset.rs | 693 ++++++++++++++++++++++ src/peripherals/trs80/keymap.rs | 54 ++ src/peripherals/trs80/mod.rs | 5 + src/peripherals/trs80/model1.rs | 116 ++++ 11 files changed, 1057 insertions(+) create mode 100644 binaries/trs80/level1.rom create mode 100644 binaries/trs80/level2.rom create mode 100644 frontends/moa-minifb/src/bin/moa-trs80.rs create mode 100644 src/host/keys.rs create mode 100644 src/machines/trs80.rs create mode 100644 src/peripherals/trs80/charset.rs create mode 100644 src/peripherals/trs80/keymap.rs create mode 100644 src/peripherals/trs80/mod.rs create mode 100644 src/peripherals/trs80/model1.rs diff --git a/binaries/trs80/level1.rom b/binaries/trs80/level1.rom new file mode 100644 index 0000000000000000000000000000000000000000..1bffa75340cde1928b7f594d8d74aba6fa8396d6 GIT binary patch literal 4096 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 { + while frontend.lock().unwrap().finalized == false { + thread::sleep(Duration::from_millis(10)); + } +} + diff --git a/src/host/keys.rs b/src/host/keys.rs new file mode 100644 index 0000000..c7a98ad --- /dev/null +++ b/src/host/keys.rs @@ -0,0 +1,112 @@ + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Key { + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + Num0, + Enter, + Escape, + Backspace, + Tab, + Space, + Minus, + Equals, + LeftBracket, + RightBracket, + Backslash, + Semicolon, + Apostrophe, + Backquote, + Comma, + Period, + Slash, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + PrintScreen, + ScrollLock, + Pause, + Insert, + Home, + PageUp, + Delete, + End, + PageDown, + Right, + Left, + Down, + Up, + + NumLock, + CapsLock, + LeftShift, + RightShift, + LeftCtrl, + RightCtrl, + LeftAlt, + RightAlt, + LeftSuper, + RightSuper, + + NumPad0, + NumPad1, + NumPad2, + NumPad3, + NumPad4, + NumPad5, + NumPad6, + NumPad7, + NumPad8, + NumPad9, + NumPadDot, + NumPadSlash, + NumPadAsterisk, + NumPadMinus, + NumPadPlus, + NumPadEnter, + + Unknown, +} + diff --git a/src/machines/mod.rs b/src/machines/mod.rs index 2565f2a..05819db 100644 --- a/src/machines/mod.rs +++ b/src/machines/mod.rs @@ -1,4 +1,5 @@ pub mod computie; pub mod genesis; +pub mod trs80; diff --git a/src/machines/trs80.rs b/src/machines/trs80.rs new file mode 100644 index 0000000..b644c3d --- /dev/null +++ b/src/machines/trs80.rs @@ -0,0 +1,32 @@ + +use crate::error::Error; +use crate::system::System; +use crate::devices::{Debuggable, wrap_transmutable}; +use crate::memory::{MemoryBlock, BusPort}; + +use crate::cpus::z80::{Z80, Z80Type}; +use crate::peripherals::trs80; + +use crate::host::traits::Host; +use crate::host::tty::SimplePty; + + +pub fn build_trs80(host: &mut H) -> Result { + let mut system = System::new(); + + let mut rom = MemoryBlock::load("binaries/trs80/level1.rom")?; + rom.read_only(); + system.add_addressable_device(0x0000, wrap_transmutable(rom))?; + + let mut ram = MemoryBlock::new(vec![0; 0xC000]); + system.add_addressable_device(0x4000, wrap_transmutable(ram))?; + + let model1 = trs80::model1::Model1Peripherals::create(host)?; + system.add_addressable_device(0x37E0, wrap_transmutable(model1)).unwrap(); + + let mut cpu = Z80::new(Z80Type::Z80, 4_000_000, BusPort::new(0, 16, 8, system.bus.clone())); + system.add_interruptable_device("cpu", wrap_transmutable(cpu))?; + + Ok(system) +} + diff --git a/src/peripherals/mod.rs b/src/peripherals/mod.rs index 3d5cc3c..2414147 100644 --- a/src/peripherals/mod.rs +++ b/src/peripherals/mod.rs @@ -2,4 +2,5 @@ pub mod ata; pub mod mc68681; pub mod genesis; +pub mod trs80; diff --git a/src/peripherals/trs80/charset.rs b/src/peripherals/trs80/charset.rs new file mode 100644 index 0000000..81e6169 --- /dev/null +++ b/src/peripherals/trs80/charset.rs @@ -0,0 +1,693 @@ + +const CHARACTERS: [[u8; 8]; 64] = [ +// Blank +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Character ! +[ 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00000, + 0b00100, + 0b00000 ], + +// Character " +[ 0b01010, + 0b01010, + 0b01010, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Character # +[ 0b01010, + 0b01010, + 0b11111, + 0b01010, + 0b11111, + 0b01010, + 0b01010, + 0b00000 ], + +// Character $ +[ 0b01110, + 0b10101, + 0b10100, + 0b01110, + 0b00101, + 0b10101, + 0b01110, + 0b00100 ], + + +// Character % +[ 0b11001, + 0b11010, + 0b00010, + 0b00100, + 0b01000, + 0b01011, + 0b10011, + 0b00000 ], + +// Character & +[ 0b00100, + 0b01010, + 0b01010, + 0b00100, + 0b01100, + 0b10011, + 0b01110, + 0b00000 ], + +// Character ' +[ 0b01000, + 0b01000, + 0b01000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Character ( +[ 0b00100, + 0b01000, + 0b10000, + 0b10000, + 0b10000, + 0b01000, + 0b00100, + 0b00000 ], + +// Character ) +[ 0b00100, + 0b00010, + 0b00001, + 0b00001, + 0b00001, + 0b00010, + 0b00100, + 0b00000 ], + +// Character * +[ 0b00000, + 0b00000, + 0b01010, + 0b00100, + 0b01010, + 0b00000, + 0b00000, + 0b00000 ], + +// Character + +[ 0b00000, + 0b00000, + 0b00100, + 0b01110, + 0b00100, + 0b00000, + 0b00000, + 0b00000 ], + +// Character , +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00010, + 0b00010, + 0b00100, + 0b00000 ], + +// Character - +[ 0b00000, + 0b00000, + 0b00000, + 0b01110, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Character . +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00110, + 0b00110, + 0b00000, + 0b00000 ], + +// Character / +[ 0b00010, + 0b00010, + 0b00100, + 0b00100, + 0b01000, + 0b01000, + 0b10000, + 0b00000 ], + +// Character 0 +[ 0b01100, + 0b10010, + 0b11010, + 0b10110, + 0b10010, + 0b10010, + 0b01100, + 0b00000 ], + +// Character 1 +[ 0b00100, + 0b01100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b01110, + 0b00000 ], + +// Character 2 +[ 0b01100, + 0b10010, + 0b00010, + 0b00100, + 0b01000, + 0b10000, + 0b11110, + 0b00000 ], + +// Character 3 +[ 0b11100, + 0b00010, + 0b00010, + 0b01100, + 0b00010, + 0b00010, + 0b11100, + 0b00000 ], + +// Character 4 +[ 0b00010, + 0b00110, + 0b01010, + 0b10010, + 0b11110, + 0b00010, + 0b00010, + 0b00000 ], + +// Character 5 +[ 0b11110, + 0b10000, + 0b10000, + 0b11110, + 0b00010, + 0b00010, + 0b11100, + 0b00000 ], + +// Character 6 +[ 0b01100, + 0b10010, + 0b10000, + 0b11110, + 0b10010, + 0b10010, + 0b01100, + 0b00000 ], + +// Character 7 +[ 0b11110, + 0b00010, + 0b00100, + 0b01000, + 0b01000, + 0b01000, + 0b01000, + 0b00000 ], + +// Character 8 +[ 0b01100, + 0b10010, + 0b10010, + 0b01100, + 0b10010, + 0b10010, + 0b01100, + 0b00000 ], + +// Character 9 +[ 0b01100, + 0b10010, + 0b10010, + 0b01110, + 0b00010, + 0b00010, + 0b01100, + 0b00000 ], + +// Character : +[ 0b00000, + 0b00000, + 0b01100, + 0b01100, + 0b00000, + 0b01100, + 0b01100, + 0b00000 ], + +// Character ; +[ 0b00000, + 0b00000, + 0b01100, + 0b01100, + 0b00000, + 0b00100, + 0b00100, + 0b00000 ], + +// Character < +[ 0b00010, + 0b00100, + 0b01000, + 0b10000, + 0b01000, + 0b00100, + 0b00010, + 0b00000 ], + +// Character = +[ 0b00000, + 0b00000, + 0b11110, + 0b00000, + 0b11110, + 0b00000, + 0b00000, + 0b00000 ], + +// Character > +[ 0b10000, + 0b01000, + 0b00100, + 0b00010, + 0b00100, + 0b01000, + 0b10000, + 0b00000 ], + +// Character ? +[ 0b01100, + 0b10010, + 0b00010, + 0b00100, + 0b01000, + 0b00000, + 0b01000, + 0b00000 ], + +// Character @ +[ 0b01100, + 0b10010, + 0b10010, + 0b10110, + 0b11110, + 0b11110, + 0b01100, + 0b00000 ], + + +// Letter A +[ 0b01100, + 0b10010, + 0b10010, + 0b11110, + 0b10010, + 0b10010, + 0b10010, + 0b00000 ], + +// Letter B +[ 0b11100, + 0b10010, + 0b10010, + 0b11100, + 0b10010, + 0b10010, + 0b11100, + 0b00000 ], + +// Letter C +[ 0b01100, + 0b10010, + 0b10000, + 0b10000, + 0b10000, + 0b10010, + 0b01100, + 0b00000 ], + +// Letter D +[ 0b11100, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b11100, + 0b00000 ], + +// Letter E +[ 0b11110, + 0b10000, + 0b10000, + 0b11110, + 0b10000, + 0b10000, + 0b11110, + 0b00000 ], + +// Letter F +[ 0b11110, + 0b10000, + 0b10000, + 0b11110, + 0b10000, + 0b10000, + 0b10000, + 0b00000 ], + +// Letter G +[ 0b01100, + 0b10010, + 0b10000, + 0b10110, + 0b10010, + 0b10010, + 0b01110, + 0b00000 ], + +// Letter H +[ 0b10010, + 0b10010, + 0b10010, + 0b11110, + 0b10010, + 0b10010, + 0b10010, + 0b00000 ], + +// Letter I +[ 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00000 ], + +// Letter J +[ 0b00010, + 0b00010, + 0b00010, + 0b00010, + 0b00010, + 0b10010, + 0b01100, + 0b00000 ], + +// Letter K +[ 0b10010, + 0b10010, + 0b10100, + 0b11000, + 0b10100, + 0b10010, + 0b10010, + 0b00000 ], + +// Letter L +[ 0b10000, + 0b10000, + 0b10000, + 0b10000, + 0b10000, + 0b10000, + 0b11110, + 0b00000 ], + +// Letter M +[ 0b00000, + 0b10001, + 0b11011, + 0b10101, + 0b10001, + 0b10001, + 0b10001, + 0b00000 ], + +// Letter N +[ 0b00000, + 0b10010, + 0b11010, + 0b10110, + 0b10010, + 0b10010, + 0b10010, + 0b00000 ], + +// Letter O +[ 0b01100, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b01100, + 0b00000 ], + +// Letter P +[ 0b11100, + 0b10010, + 0b10010, + 0b11100, + 0b10000, + 0b10000, + 0b10000, + 0b00000 ], + +// Letter Q +[ 0b01100, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b10110, + 0b01110, + 0b00000 ], + +// Letter R +[ 0b11100, + 0b10010, + 0b10010, + 0b11100, + 0b10010, + 0b10010, + 0b10010, + 0b00000 ], + +// Letter S +[ 0b01110, + 0b10000, + 0b10000, + 0b01100, + 0b00010, + 0b00010, + 0b11100, + 0b00000 ], + +// Letter T +[ 0b11111, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00100, + 0b00000 ], + +// Letter U +[ 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b10010, + 0b01100, + 0b00000 ], + +// Letter V +[ 0b10001, + 0b10001, + 0b10001, + 0b10001, + 0b10001, + 0b01010, + 0b00100, + 0b00000 ], + +// Letter W +[ 0b10001, + 0b10001, + 0b10001, + 0b10001, + 0b10101, + 0b10101, + 0b01010, + 0b00000 ], + +// Letter X +[ 0b00000, + 0b10001, + 0b01010, + 0b00100, + 0b00100, + 0b01010, + 0b10001, + 0b00000 ], + +// Letter Y +[ 0b10001, + 0b10001, + 0b10001, + 0b01010, + 0b00100, + 0b00100, + 0b00100, + 0b00000 ], + +// Letter Z +[ 0b11110, + 0b00010, + 0b00100, + 0b01000, + 0b10000, + 0b10000, + 0b11110, + 0b00000 ], + + +//////// LEFT TO DO /////// + +// Blank +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Blank +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Blank +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Blank +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + +// Blank +[ 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000, + 0b00000 ], + + + +]; + + + +pub struct CharacterGenerator { + pub row: i8, + pub col: i8, + pub data: &'static [u8; 8], +} + +impl CharacterGenerator { + pub fn new(ch: u8) -> Self { + Self { + row: 0, + col: 4, + data: &CHARACTERS[ch as usize], + } + } +} + +impl Iterator for CharacterGenerator { + type Item = u32; + + fn next(&mut self) -> Option { + if self.row >= 8 { + None + } else { + let bit = (self.data[self.row as usize] & (1 << self.col)) != 0; + + self.col -= 1; + if self.col < 0 { + self.col = 4; + self.row += 1; + } + + if bit { + Some(0xC0C0C0) + } else { + Some(0) + } + } + } +} + diff --git a/src/peripherals/trs80/keymap.rs b/src/peripherals/trs80/keymap.rs new file mode 100644 index 0000000..92ff491 --- /dev/null +++ b/src/peripherals/trs80/keymap.rs @@ -0,0 +1,54 @@ + +use crate::host::keys::Key; + +#[inline(always)] +pub fn set_bit(data: &mut [u8; 8], index: usize, bit: u8, state: bool) { + let mask = 1 << bit; + data[index] = (data[index] & !mask) | (if state { mask } else { 0 }); +} + +pub fn record_key_press(data: &mut [u8; 8], key: Key, state: bool) { + match key { + Key::A => set_bit(data, 0, 1, state), + Key::B => set_bit(data, 0, 2, state), + Key::C => set_bit(data, 0, 3, state), + Key::D => set_bit(data, 0, 4, state), + Key::E => set_bit(data, 0, 5, state), + Key::F => set_bit(data, 0, 6, state), + Key::G => set_bit(data, 0, 7, state), + Key::H => set_bit(data, 1, 0, state), + Key::I => set_bit(data, 1, 1, state), + Key::J => set_bit(data, 1, 2, state), + Key::K => set_bit(data, 1, 3, state), + Key::L => set_bit(data, 1, 4, state), + Key::M => set_bit(data, 1, 5, state), + Key::N => set_bit(data, 1, 6, state), + Key::O => set_bit(data, 1, 7, state), + Key::P => set_bit(data, 2, 0, state), + Key::Q => set_bit(data, 2, 1, state), + Key::R => set_bit(data, 2, 2, state), + Key::S => set_bit(data, 2, 3, state), + Key::T => set_bit(data, 2, 4, state), + Key::U => set_bit(data, 2, 5, state), + Key::V => set_bit(data, 2, 6, state), + Key::W => set_bit(data, 2, 7, state), + Key::X => set_bit(data, 3, 0, state), + Key::Y => set_bit(data, 3, 1, state), + Key::Z => set_bit(data, 3, 2, state), + Key::Num0 => set_bit(data, 4, 0, state), + Key::Num1 => set_bit(data, 4, 1, state), + Key::Num2 => set_bit(data, 4, 2, state), + Key::Num3 => set_bit(data, 4, 3, state), + Key::Num4 => set_bit(data, 4, 4, state), + Key::Num5 => set_bit(data, 4, 5, state), + Key::Num6 => set_bit(data, 4, 6, state), + Key::Num7 => set_bit(data, 4, 7, state), + Key::Num8 => set_bit(data, 5, 0, state), + Key::Num9 => set_bit(data, 5, 1, state), + Key::Enter => set_bit(data, 6, 0, state), + Key::Space => set_bit(data, 6, 7, state), + Key::LeftShift | Key::RightShift => set_bit(data, 7, 0, state), + _ => { }, + } +} + diff --git a/src/peripherals/trs80/mod.rs b/src/peripherals/trs80/mod.rs new file mode 100644 index 0000000..6eba46e --- /dev/null +++ b/src/peripherals/trs80/mod.rs @@ -0,0 +1,5 @@ + +pub mod model1; +pub mod keymap; +pub mod charset; + diff --git a/src/peripherals/trs80/model1.rs b/src/peripherals/trs80/model1.rs new file mode 100644 index 0000000..adbe674 --- /dev/null +++ b/src/peripherals/trs80/model1.rs @@ -0,0 +1,116 @@ + +use std::slice::Iter; +use std::sync::{Arc, Mutex}; + +use crate::error::Error; +use crate::system::System; +use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable}; + +use crate::host::keys::Key; +use crate::host::gfx::{Frame, FrameSwapper}; +use crate::host::traits::{Host, BlitableSurface, KeyboardUpdater}; + +use super::keymap; +use super::charset::CharacterGenerator; + + +const DEV_NAME: &'static str = "model1"; + +pub struct Model1Peripherals { + pub swapper: Arc>, + pub keyboard_mem: Arc>, + pub video_mem: [u8; 1024], +} + +impl Model1Peripherals { + pub fn create(host: &mut H) -> Result { + let swapper = FrameSwapper::new_shared(320, 128); + let keyboard_mem = Arc::new(Mutex::new([0; 8])); + + host.add_window(FrameSwapper::to_boxed(swapper.clone()))?; + host.register_keyboard(Box::new(Model1KeyboardUpdater(keyboard_mem.clone())))?; + + Ok(Self { + swapper, + keyboard_mem, + video_mem: [0; 1024], + }) + } +} + +pub struct Model1KeyboardUpdater(Arc>); + +impl KeyboardUpdater for Model1KeyboardUpdater { + fn update_keyboard(&mut self, key: Key, state: bool) { + println!(">>> {:?}", key); + keymap::record_key_press(&mut self.0.lock().unwrap(), key, state); + } +} + +impl Steppable for Model1Peripherals { + fn step(&mut self, system: &System) -> Result { + let mut swapper = self.swapper.lock().unwrap(); + swapper.current.clear(0); + for y in 0..16 { + for x in 0..64 { + //let iter = self.get_char_generator(self.video_mem[x + (y * 64)]); + + let ch = self.video_mem[x + (y * 64)]; + // TODO this is totally a hack for now!!!!! + let iter = CharacterGenerator::new((ch - 0x20) % 64); + swapper.current.blit((x * 5) as u32, (y * 8) as u32, iter, 5, 8); + } + } + + Ok(16_630_000) + } +} + +impl Addressable for Model1Peripherals { + fn len(&self) -> usize { + 0x820 + } + + fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> { + if addr >= 0x20 && addr <= 0xA0 { + let offset = addr - 0x20; + data[0] = 0; + if (offset & 0x01) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[0]; } + if (offset & 0x02) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[1]; } + if (offset & 0x04) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[2]; } + if (offset & 0x08) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[3]; } + if (offset & 0x10) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[4]; } + if (offset & 0x20) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[5]; } + if (offset & 0x40) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[6]; } + if (offset & 0x80) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[7]; } + //info!("{}: read from keyboard {:x} of {:?}", DEV_NAME, addr, data); + } else if addr >= 0x420 && addr <= 0x820 { + data[0] = self.video_mem[addr as usize - 0x420]; + } else { + warning!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); + } + debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + Ok(()) + } + + fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> { + info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); + if addr > 0x420 && addr < 0x820 { + self.video_mem[addr as usize - 0x420] = data[0]; + } else { + warning!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + } + Ok(()) + } +} + +impl Transmutable for Model1Peripherals { + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + Some(self) + } + + fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { + Some(self) + } +} +