diff --git a/Cargo.lock b/Cargo.lock index a0a0ef6..a5de6d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "anstream" version = "0.6.13" @@ -117,6 +135,21 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.0.83" @@ -178,6 +211,44 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "cty" version = "0.2.2" @@ -199,6 +270,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + [[package]] name = "equivalent" version = "1.0.1" @@ -324,7 +401,9 @@ dependencies = [ "bdf", "bitvec", "clap", + "crossterm", "minifb", + "ratatui", "serde", "toml", ] @@ -334,6 +413,10 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -363,6 +446,21 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.67" @@ -402,7 +500,7 @@ checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ "bitflags 2.4.2", "libc", - "redox_syscall", + "redox_syscall 0.4.1", ] [[package]] @@ -411,12 +509,31 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + [[package]] name = "memchr" version = "2.7.1" @@ -458,6 +575,18 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "nix" version = "0.24.3" @@ -488,6 +617,35 @@ dependencies = [ "sdl2-sys", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.2", + "smallvec", + "windows-targets 0.52.0", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -530,6 +688,26 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "ratatui" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" +dependencies = [ + "bitflags 2.4.2", + "cassowary", + "compact_str", + "crossterm", + "itertools", + "lru", + "paste", + "stability", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + [[package]] name = "raw-window-handle" version = "0.4.3" @@ -548,6 +726,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.4.2", +] + [[package]] name = "rustix" version = "0.38.31" @@ -561,12 +748,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sdl2" version = "0.35.2" @@ -619,6 +824,36 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -634,12 +869,50 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "stability" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" version = "2.0.48" @@ -665,7 +938,7 @@ checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.4.1", "rustix", "windows-sys 0.52.0", ] @@ -730,6 +1003,28 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-truncate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" +dependencies = [ + "itertools", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + [[package]] name = "utf8parse" version = "0.2.1" @@ -742,6 +1037,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.90" @@ -1085,3 +1392,23 @@ name = "xml-rs" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 816c740..fba75a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ anyhow = "1.0.81" bdf = "0.6.0" bitvec = "1.0.1" clap = { version = "4.5.4", features = ["derive"] } +crossterm = "0.27.0" minifb = "0.25.0" +ratatui = "0.26.3" serde = { version = "1.0.197", features = ["serde_derive", "derive"] } toml = "0.8.12" diff --git a/config.toml b/config.toml index 95aaea3..5295807 100644 --- a/config.toml +++ b/config.toml @@ -1,2 +1,3 @@ char_rom = "./src/roms/cozette.rom" -rom = "./src/roms/george.rom" +rom = "./src/roms/test.rom" + diff --git a/run.sh b/run.sh index 0696295..55bd644 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -vasm6502_oldstyle ./src/george.asm -dotdir -wdc02 -ldots -Fbin -o ./src/george.rom; +vasm6502_oldstyle ./src/roms/george.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/george.rom; cargo run; # hexdump -C ./cpu_dump.bin; diff --git a/src/cpu.rs b/src/cpu.rs index d1ac36d..c58164e 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,8 +1,9 @@ -// use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError}; use crate::instructions::{get_instruction, Instruction}; use crate::memory::Mem; use crate::types::{Byte, Word}; +use std::cell::RefCell; use std::path::PathBuf; +use std::rc::Rc; use std::str::FromStr; use std::sync::mpsc::Receiver; use std::sync::Arc; @@ -157,9 +158,9 @@ impl Cpu { sleep(Duration::from_nanos(100)); self.pending_cycles -= 1; } - if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() { - self.interrupt(); - } + // if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() { + // self.interrupt(); + // } let opcode = match self.read(self.pc) { Ok(byte) => byte, Err(_) => { @@ -170,12 +171,12 @@ impl Cpu { let instruction = get_instruction(opcode); match instruction { Instruction::Valid(valid_instruction) => { - //println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi); - //println!( - // "Instruction: {:?}, {:#04x}", - // valid_instruction.opcode, opcode - //); - //println!(""); + // println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi); + // println!( + // "Instruction: {:?}, {:#04x}", + // valid_instruction.opcode, opcode + // ); + // println!(""); self.pc += 1; match valid_instruction.opcode.call(self) { Ok(_) => { @@ -196,7 +197,7 @@ impl Cpu { } }; - println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq.try_recv().unwrap_or_default(), nmi = self.nmi); + // println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq.try_recv().unwrap_or_default(), nmi = self.nmi); memory .dump(PathBuf::from_str("./cpu_dump.bin").unwrap()) .unwrap(); diff --git a/src/keyboard.rs b/src/keyboard.rs index f7839c6..0a3f94c 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,10 +1,5 @@ use minifb::{InputCallback, Key}; -use std::{ - path::PathBuf, - process::exit, - str::FromStr, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use crate::memory::Mem; diff --git a/src/main.rs b/src/main.rs index 1ef59c2..a7a1cf7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod error; mod instructions; mod keyboard; mod memory; +mod tui; mod types; mod video; @@ -13,18 +14,28 @@ use crate::keyboard::Keyboard; use crate::memory::Mem; use crate::video::Crtc; -use clap::Parser; +// use clap::Parser; use minifb::{Scale, ScaleMode, Window, WindowOptions}; use serde::{Deserialize, Serialize}; -use std::fs::File; -use std::io::Read; -use std::process::exit; -use std::str::FromStr; -use std::sync::{mpsc, Mutex}; -use std::{path::PathBuf, sync::Arc, thread}; +use std::{ + fs::File, + io::{stdout, Read, Result}, + path::PathBuf, + str::FromStr, + sync::{mpsc, Arc, Mutex}, + thread, +}; -use toml::Table; +use crossterm::{ + event::{self, KeyCode, KeyEventKind}, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; + +use ratatui::prelude::{CrosstermBackend, Terminal}; + +// use toml::Table; //#[derive(Parser)] //struct Cli { @@ -39,16 +50,21 @@ struct Config { rom: String, } -fn main() { +fn main() -> Result<()> { + stdout().execute(EnterAlternateScreen)?; + enable_raw_mode()?; + let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; + terminal.clear()?; + let config: Config = match File::open("./config.toml") { Ok(mut file) => { let mut string = String::new(); file.read_to_string(&mut string).unwrap(); toml::from_str(string.as_str()).unwrap() } - Err(_) => return, + Err(_) => return Ok(()), }; - println!("{config:#?}"); + let mut memory = Mem::new(); let rom = match std::fs::File::open(config.rom) { Ok(file) => file, @@ -58,23 +74,18 @@ fn main() { println!("{:?}", error); }; - let (interrupt_tx, interrupt_rx) = mpsc::channel(); - let (window_tx, window_rx) = mpsc::channel(); - memory .dump(PathBuf::from_str("./coredump.bin").unwrap()) .unwrap(); let shared_memory = Arc::new(Mutex::new(memory)); + let cpu_memory = shared_memory.clone(); let display_memory = shared_memory.clone(); let keyboard_memory = shared_memory.clone(); - thread::spawn(move || { - let mut cpu = Cpu::new(cpu_memory, interrupt_rx); - cpu.reset().unwrap(); - cpu.execute(); - }); + let (interrupt_tx, interrupt_rx) = mpsc::channel(); + let (window_tx, window_rx) = mpsc::channel(); thread::spawn(move || { let mut screen = Crtc::new( @@ -105,8 +116,23 @@ fn main() { window.set_input_callback(Box::new(Keyboard::new(keyboard_memory))); - while window.is_open() { + let cpu = Cpu::new(cpu_memory, interrupt_rx); + let mut tui = tui::App::new(cpu); + tui.init(); + + loop { + if event::poll(std::time::Duration::from_millis(16))? { + if let event::Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { + break; + } + } + } let buffer = window_rx.recv().unwrap(); window.update_with_buffer(&buffer, 512, 380).unwrap(); + tui.update(&mut terminal)?; } + stdout().execute(LeaveAlternateScreen)?; + disable_raw_mode()?; + Ok(()) } diff --git a/src/roms/cozette.rom b/src/roms/cozette.rom index 9ce58aa..20d5eb5 100644 Binary files a/src/roms/cozette.rom and b/src/roms/cozette.rom differ diff --git a/src/roms/george.asm b/src/roms/george.asm index c24a7a7..154811f 100644 --- a/src/roms/george.asm +++ b/src/roms/george.asm @@ -10,7 +10,7 @@ cursor = $202 char_buffer = $300 ; 256 byte character buffer -kb_row = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here +kb_row = $4400 ; keyboard hardware register kb_row_cache = $203 ; cache .org $8000 @@ -34,32 +34,188 @@ cleardisplay: sta $6700,y ; this goes slightly over but it's fine iny bne cleardisplay - cli + ; cli main: - jsr draw + jsr keyboard + ; key_zero: + ; stz keyboard_cache, x + ; dex + ; bpl key_zero + ; fim: + ; cli + ; bra fim + ; jsr kitty_keys jmp main +; ; copying @smal rn: https://github.com/smaldragon/KittyEMU/blob/main/roms/foxmon.asm +; char_timer = $10 +; line_buffer = $0200 +; char_cur = $11 +; line_buffer_i = $12 +; line_buffer_l = $13 + +; keyboard_cache = $14 +; line_cur = $20 + +; irq: + +; lda line_buffer_i +; lda $e0 +; lda $6f +; sei +; stz char_cur +; lda line_buffer_i +; sta line_buffer_l +; ldx #4 + +; kitty_keys: ; reads pressed key and writes keymap value to char_cur +; phx +; txa +; asl +; asl +; asl +; asl +; asl ; i think this is supposed to be once for every keyboard row +; tax +; lda kb_row, x +; plx + +; pha +; cmp keyboard_cache, x +; bne change +; jmp nochange +; change: +; bit7: +; asl keyboard_cache, x +; bcs bit6 +; bit #0b10000000 +; beq bit6 +; pha +; lda keymap_7, x +; sta char_cur +; pla +; bit6: +; asl keyboard_cache, x +; bcs bit5 +; bit #0b01000000 +; beq bit5 +; pha +; lda keymap_6, x +; sta char_cur +; pla +; bit5: +; asl keyboard_cache, x +; bcs bit4 +; bit #0b00100000 +; beq bit4 +; pha +; lda keymap_5, x +; sta char_cur +; pla +; bit4: +; asl keyboard_cache, x +; bcs bit3 +; bit #0b00010000 +; beq bit3 +; pha +; lda keymap_4, x +; sta char_cur +; pla +; bit3: +; asl keyboard_cache, x +; bcs bit2 +; bit #0b00001000 +; beq bit2 +; pha +; lda keymap_3, x +; sta char_cur +; pla +; bit2: +; asl keyboard_cache, x +; bcs bit1 +; bit #0b00000100 +; beq bit1 +; pha +; lda keymap_2, x +; sta char_cur +; pla +; bit1: +; asl keyboard_cache, x +; bcs bit0 +; bit #0b00000010 +; beq bit0 +; pha +; lda keymap_1, x +; sta char_cur +; pla +; bit0: +; asl keyboard_cache, x +; bcs bitend +; bit #0b00000001 +; beq bitend +; pha +; lda keymap_0, x +; sta char_cur +; pla +; bitend: +; nochange: +; pla +; sta keyboard_cache, x +; dex +; bmi keyend +; jmp kitty_keys +; keyend: + +; write: +; lda char_cur +; ldy cursor +; sta $6000, y +; inc cursor +; rts + + +; ; col = keyboard row, row = keyboard bit placement inverted +; keymap_0: +; .byte "??????" +; keymap_1: +; .byte "wqdzx " +; keymap_2: +; .byte "esvfc " +; keymap_3: +; .byte "rghbn?" +; keymap_4: +; .byte "tykjm?" +; keymap_5: +; .byte "ui?l??" +; keymap_6: +; .byte "op?21?" +; keymap_7: +; .byte "??a43?" +; +; keyboard: + ldy #0 + x + +not_keyboard: ldy #0 - .loop: ; loop through each row + .check_row: ; loop through each row lda kb_row, y - beq .skip ; if row has no key pressed, skip checking which key + beq .skip_row ; if row has no key pressed, skip checking which key sta kb_row_cache, y ; if key pressed, cache it lda kb_row, y cmp kb_row_cache, y ; has key changed? beq key_down - .skip: + .skip_row: iny cpy #5 - bne .loop + bne .check_row rts key_down: ; a is loaded with the row byte - sty key_row ; store character row - inc cursor - pha phy + sty key_row ; store character row ldy #0 .find_col: ; test each row bit, store column if key pressed lsr ; test bit 7 @@ -90,12 +246,12 @@ keymap_index: lda 0, x tay -do_something_w_key: ; we've stored the character position, now let's +print: ; we've stored the character position, now let's lda keymap, y ldy cursor sta $6000, y + inc cursor ply - pla rts keymap: @@ -107,9 +263,9 @@ keymap: .byte "????? m" draw: - push_coords #0, #0 - push_char #$00 - jsr draw_char + ; push_coords #0, #0 + ; push_char #$00 + ; jsr draw_char rts draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- ) @@ -151,7 +307,7 @@ isr: ; interrupt service routine pha phx phy - jsr keyboard + ; jsr irq ply plx pla diff --git a/src/roms/george.rom b/src/roms/george.rom index a72c0ec..53b0e93 100644 Binary files a/src/roms/george.rom and b/src/roms/george.rom differ diff --git a/src/roms/macro.inc b/src/roms/macro.inc index 4690248..ec1d950 100644 --- a/src/roms/macro.inc +++ b/src/roms/macro.inc @@ -26,7 +26,7 @@ dex .endm - .macro push_char, char ; pushes an ascii character code onto the stack + .macro push_char, char; pushes an ascii character code onto the stack lda \char push sta 0, x ; char low byte diff --git a/src/tui/mod.rs b/src/tui/mod.rs new file mode 100644 index 0000000..31a0110 --- /dev/null +++ b/src/tui/mod.rs @@ -0,0 +1,150 @@ +// use std::time::Duration; + +mod tabs; +mod term; +use std::{io::Result, time::Duration}; + +use crossterm::event::{self, poll, Event, KeyCode, KeyEvent, KeyEventKind}; +use ratatui::{ + prelude::*, + widgets::{Block, Cell, Paragraph, Row, Table, TableState}, +}; + +use crate::cpu::Cpu; + +pub struct App { + cpu: Cpu, + running: bool, + table_state: TableState, +} + +impl App { + pub fn new(cpu: Cpu) -> Self { + Self { + cpu, + running: true, + table_state: TableState::default(), + } + } + pub fn init(&mut self) { + let _ = self.cpu.reset(); + } + pub fn update(&mut self, terminal: &mut Terminal) -> Result<()> { + self.draw(terminal)?; + self.handle_events()?; + self.handle_cpu()?; + Ok(()) + } + + fn handle_cpu(&mut self) -> Result<()> { + if self.running { + self.cpu.cycle(); + } + Ok(()) + } + + /// Draw a single frame of the app. + fn draw(&self, terminal: &mut Terminal) -> Result<()> { + terminal + .draw(|frame| { + frame.render_widget(self, frame.size()); + }) + .unwrap(); + Ok(()) + } + + fn handle_events(&mut self) -> Result<()> { + if poll(Duration::from_secs_f32(1.0 / 25.0))? { + match event::read()? { + Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key), + _ => {} + } + } + Ok(()) + } + + fn handle_key_press(&mut self, key: KeyEvent) { + match key.code { + KeyCode::Enter => { + if !self.running { + self.cpu.cycle() + } + } + KeyCode::Char(' ') => { + self.running = !self.running; + } + _ => {} + }; + } +} + +/// Implement Widget for &App rather than for App as we would otherwise have to clone or copy the +/// entire app state on every frame. For this example, the app state is small enough that it doesn't +/// matter, but for larger apps this can be a significant performance improvement. +impl Widget for &App { + fn render(self, area: Rect, buf: &mut Buffer) { + let [cpu_area, memory_area] = + Layout::horizontal([Constraint::Percentage(50), Constraint::Fill(1)]).areas(area); + + let cpu_info = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.cpu.a, x = self.cpu.x, y = self.cpu.y, pc = self.cpu.pc, s = self.cpu.s, p = self.cpu.p, irq = self.cpu.irq, nmi = self.cpu.nmi); + + Paragraph::new(cpu_info) + .block(Block::bordered().title("cpu info!")) + .render(cpu_area, buf); + + let memory = self.cpu.memory.lock().unwrap(); + let table_height = memory_area.rows().count() - 2; + let rows: Vec = memory.data[0..table_height * 16] + .chunks(16) + .map(|chunk| { + chunk + .iter() + .map(|content| Cell::from(Text::from(format!("{content:#02}")))) + .collect::() + }) + .collect(); + let widths = vec![Constraint::Length(2); 16]; + Widget::render( + Table::new(rows, widths) + .header( + Row::new(vec![ + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", + "f", + ]) + .style(Style::new().bold()) + // To add space between the header and the rest of the rows, specify the margin + .bottom_margin(1), + ) + .block(Block::bordered().title("memory!")), + memory_area, + buf, + ); + } +} + +// impl Widget for App { +// fn render_title_bar(&self, area: Rect, buf: &mut Buffer) { +// // let layout = Layout::horizontal([Constraint::Min(0), Constraint::Length(43)]); +// // let [title] = layout.areas(area); + +// Span::styled("Ratatui", Style::default()).render(area, buf); +// // let titles = Tab::iter().map(Tab::title); +// // Tabs::new(titles) +// // .style(THEME.tabs) +// // .highlight_style(THEME.tabs_selected) +// // .select(self.tab as usize) +// // .divider("") +// // .padding("", "") +// // .render(tabs, buf); +// } + +// // fn render_selected_tab(&self, area: Rect, buf: &mut Buffer) { +// // match self.tab { +// // Tab::About => self.about_tab.render(area, buf), +// // Tab::Recipe => self.recipe_tab.render(area, buf), +// // Tab::Email => self.email_tab.render(area, buf), +// // Tab::Traceroute => self.traceroute_tab.render(area, buf), +// // Tab::Weather => self.weather_tab.render(area, buf), +// // }; +// // } +// } diff --git a/src/video.rs b/src/video.rs index 9f62d74..c86ac7b 100644 --- a/src/video.rs +++ b/src/video.rs @@ -3,12 +3,9 @@ use std::{ fs::File, io::Read, path::Path, - sync::{ - mpsc::{Sender, SyncSender}, - Arc, Mutex, - }, + sync::{mpsc::Sender, Arc, Mutex}, thread::sleep, - time::{Duration, Instant}, + time::Duration, }; const FG_COLOR: u32 = 0xFFCC00; @@ -31,7 +28,7 @@ where let mut file = File::open(path).unwrap(); let mut bin = vec![0; 0x8000]; file.read_exact(&mut bin).unwrap(); - println!("reading char rom"); + // println!("reading char rom"); bin } None => include_bytes!("./roms/cozette.rom").to_vec(), @@ -65,7 +62,9 @@ impl Crtc { return; } }; - // the rest of this function is arcane wizardry based on the specifics of george's weird + + // the rest of this function is arcane wizardry + // based on the specifics of george's weird // display and characters... don't fuck around w it let mut i = 0; for char_row in 0..29 { @@ -88,15 +87,15 @@ impl Crtc { let buffer = self.buffer.to_owned(); - self.window.send(buffer).unwrap(); + let _ = self.window.send(buffer); } pub fn run(&mut self) { loop { - self.interrupt.send(false).unwrap(); + let _ = self.interrupt.send(false); sleep(Duration::from_millis(16)); self.draw(); - self.interrupt.send(true).unwrap(); + let _ = self.interrupt.send(true); } } }