diff --git a/binaries/trs80/level1.rom b/binaries/trs80/level1.rom new file mode 100644 index 0000000..1bffa75 Binary files /dev/null and b/binaries/trs80/level1.rom differ diff --git a/binaries/trs80/level2.rom b/binaries/trs80/level2.rom new file mode 100644 index 0000000..e32d03b Binary files /dev/null and b/binaries/trs80/level2.rom differ diff --git a/frontends/moa-minifb/src/bin/moa-trs80.rs b/frontends/moa-minifb/src/bin/moa-trs80.rs new file mode 100644 index 0000000..f371871 --- /dev/null +++ b/frontends/moa-minifb/src/bin/moa-trs80.rs @@ -0,0 +1,43 @@ + +use std::thread; +use std::time::Duration; +use std::sync::{Arc, Mutex}; + +use moa::machines::trs80::build_trs80; +use moa_minifb::MiniFrontendBuilder; + +fn main() { + /* + let mut frontend = Arc::new(Mutex::new(MiniFrontendBuilder::new())); + + { + let frontend = frontend.clone(); + thread::spawn(move || { + let mut system = build_trs80(&mut *(frontend.lock().unwrap())).unwrap(); + frontend.lock().unwrap().finalize(); + system.run_loop(); + }); + } + + wait_until_initialized(frontend.clone()); + + frontend + .lock().unwrap() + .build() + .start(None); + */ + + let mut frontend = MiniFrontendBuilder::new(); + let mut system = build_trs80(&mut frontend).unwrap(); + + frontend + .build() + .start(Some(system)); +} + +fn wait_until_initialized(frontend: Arc>) { + 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) + } +} +