diff --git a/.cargo/config.toml b/.cargo/config.toml
index 9bb778c..65b241f 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -3,3 +3,7 @@ coverage = "tarpaulin --out Html --skip-clean --output-dir coverage"
[build]
rustc-wrapper = "sccache"
+
+[target.x86_64-unknown-linux-gnu]
+linker = "clang"
+rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/mold"]
diff --git a/Cargo.lock b/Cargo.lock
index c08abba..c5c43ec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "ab_glyph"
@@ -1842,7 +1842,9 @@ dependencies = [
name = "gemmatelnet"
version = "0.1.0"
dependencies = [
+ "clap",
"gemma",
+ "log",
]
[[package]]
diff --git a/coverage/tarpaulin-report.html b/coverage/tarpaulin-report.html
new file mode 100644
index 0000000..8d28d31
--- /dev/null
+++ b/coverage/tarpaulin-report.html
@@ -0,0 +1,671 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs
index c34db82..ddee91d 100644
--- a/gemma/src/chip8/computer.rs
+++ b/gemma/src/chip8/computer.rs
@@ -92,6 +92,11 @@ impl Chip8Computer {
self.registers.set_pc(offset);
}
+ pub fn execute_instruction(&mut self, instruction_to_execute: Chip8CpuInstructions) {
+ //
+ }
+
+
pub fn step_system(&mut self) -> &mut Chip8Computer {
println!("Stepping System 1 Step");
// read the next instruction
diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs
index 9cea84f..8174890 100644
--- a/gemma/src/chip8/computer_manager.rs
+++ b/gemma/src/chip8/computer_manager.rs
@@ -13,7 +13,7 @@ pub enum ManagerDumpables {
}
pub struct Chip8ComputerManager {
- core_should_run: bool,
+ pub core_should_run: bool,
one_step: bool,
core_last_cycle_start: Instant,
computer: Chip8Computer,
@@ -31,6 +31,23 @@ impl Default for Chip8ComputerManager {
}
impl Chip8ComputerManager {
+
+ /// Builds a string that can be displayed at a console giving the user
+ /// an idea of the internal state of the Chip-8 Computer
+ pub fn status_as_string(&self) -> String {
+ // build a string
+ format!("Should Run: {}\nLast Cycle Start: {:?}\nNum Cycles: {}\nRegisters\n{}\nTimers: {}/{}\nKeypad: \n{}\nVideo:\n{}",
+ self.core_should_run,
+ self.core_last_cycle_start,
+ self.computer.num_cycles,
+ self.computer.registers.format_as_string(),
+ self.computer.delay_timer.current(),
+ self.computer.sound_timer.current(),
+ self.computer.keypad.format_as_string(),
+ self.computer.video_memory.format_as_string()
+ )
+ }
+
pub fn quirks_mode(&self) -> QuirkMode {
self.computer.quirk_mode.clone()
}
diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs
index 08fd579..6dba5fd 100644
--- a/gemma/src/chip8/instructions.rs
+++ b/gemma/src/chip8/instructions.rs
@@ -7,7 +7,6 @@ use crate::constants::*;
use log::debug;
use rand::Rng;
use serde::{Deserialize, Serialize};
-use std::ascii::AsciiExt;
use std::fmt::{Debug, Display, Formatter};
use std::ops::BitAnd;
use std::time::Instant;
@@ -760,7 +759,7 @@ impl Chip8CpuInstructions {
debug!("OrVxVy [0x{x:1x}] [0x{y:1x}]")
}
// 0x8xy2 Set Vx = Vx AND Vy
- Chip8CpuInstructions::AND(x, y) => {
+ AND(x, y) => {
let lhs_16 = input.registers.peek(*x) as u16;
let rhs_16 = input.registers.peek(*y) as u16;
// reset of VF quirk
@@ -768,7 +767,7 @@ impl Chip8CpuInstructions {
input.registers.poke(*x, (lhs_16 & rhs_16) as u8);
}
// 0x8xy3 Set Vx = Vx XOR Vy
- Chip8CpuInstructions::ORY(x, y) => {
+ ORY(x, y) => {
let lhs_16 = input.registers.peek(*x) as u16;
let rhs_16 = input.registers.peek(*y) as u16;
// reset of VF quirk
@@ -776,7 +775,7 @@ impl Chip8CpuInstructions {
input.registers.poke(*x, (lhs_16 ^ rhs_16) as u8);
}
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
- Chip8CpuInstructions::ADDR(x, y) => {
+ ADDR(x, y) => {
let lhs = input.registers.peek(*x) as i16;
let rhs = input.registers.peek(*y) as i16;
let working = lhs + rhs;
@@ -788,7 +787,7 @@ impl Chip8CpuInstructions {
input.registers.poke(0x0f, 0x00);
}
}
- Chip8CpuInstructions::SUB(x, y) => {
+ SUB(x, y) => {
// 8xy5 - SUB Vx, Vy
// Set Vx = Vx - Vy, set VF = NOT borrow.
//
diff --git a/gemma/src/chip8/system_memory.rs b/gemma/src/chip8/system_memory.rs
index 5a68a44..97c197e 100644
--- a/gemma/src/chip8/system_memory.rs
+++ b/gemma/src/chip8/system_memory.rs
@@ -4,7 +4,6 @@ use crate::constants::*;
use serde::Deserialize;
use serde::Serialize;
-use super::quirk_modes;
use super::quirk_modes::QuirkMode;
#[derive(Clone, Serialize, Deserialize, Debug)]
diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs
index 146d735..80d295b 100644
--- a/gemma/src/chip8/video.rs
+++ b/gemma/src/chip8/video.rs
@@ -49,7 +49,7 @@ impl Chip8Video {
LowRes => CHIP8_VIDEO_MEMORY,
HighRes => SCHIP_VIDE_MEMORY,
};
- for i in 0..num_loops {
+ for _ in 0..num_loops {
self.memory.push(false);
}
}
@@ -115,7 +115,7 @@ impl Chip8Video {
pub fn format_as_string(&self) -> String {
let (width, height) = self.get_resolution();
- println!("FORMATTING {width}x{height}");
+ // println!("FORMATTING {width}x{height}");
let mut output = String::new();
for row in 0..height {
for column in 0..width {
@@ -177,7 +177,7 @@ impl Chip8Video {
for current_row in 0..height {
let row_offset = current_row * width;
- for current_column in (0..width - 4) {
+ for current_column in 0..width - 4 {
let target: usize = (row_offset + current_column) as usize;
let source: usize = target + 4;
self.memory[target] = self.memory[source];
diff --git a/gemma/src/chip8_2/instructions/add.rs b/gemma/src/chip8_2/instructions/add.rs
new file mode 100644
index 0000000..ac86548
--- /dev/null
+++ b/gemma/src/chip8_2/instructions/add.rs
@@ -0,0 +1,23 @@
+use crate::chip8::instructions::Chip8CpuInstructions;
+use crate::chip8::registers::Chip8Registers;
+use crate::chip8_2::instructions::instruction_trait::InstructionTrait;
+
+struct Add {
+ pub relative: bool,
+ pub source: Option,
+ pub destination: Chip8Registers,
+ pub literal: Option
+}
+
+impl InstructionTrait for Add {
+ fn length(&self) -> u8 {
+ 1
+ }
+
+ fn encoded(&self) -> Chip8CpuInstructions {
+ }
+
+ fn decode(from: Vec) -> &dyn InstructionTrait {
+ todo!()
+ }
+}
\ No newline at end of file
diff --git a/gemma/src/chip8_2/instructions/call.rs b/gemma/src/chip8_2/instructions/call.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/cls.rs b/gemma/src/chip8_2/instructions/cls.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/drw.rs b/gemma/src/chip8_2/instructions/drw.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/exit.rs b/gemma/src/chip8_2/instructions/exit.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/high.rs b/gemma/src/chip8_2/instructions/high.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/jp.rs b/gemma/src/chip8_2/instructions/jp.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/ld.rs b/gemma/src/chip8_2/instructions/ld.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/low.rs b/gemma/src/chip8_2/instructions/low.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/ret.rs b/gemma/src/chip8_2/instructions/ret.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/rnd.rs b/gemma/src/chip8_2/instructions/rnd.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/scd.rs b/gemma/src/chip8_2/instructions/scd.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/scl.rs b/gemma/src/chip8_2/instructions/scl.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/scr.rs b/gemma/src/chip8_2/instructions/scr.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/se.rs b/gemma/src/chip8_2/instructions/se.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/sknp.rs b/gemma/src/chip8_2/instructions/sknp.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/skp.rs b/gemma/src/chip8_2/instructions/skp.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/sne.rs b/gemma/src/chip8_2/instructions/sne.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemma/src/chip8_2/instructions/sys.rs b/gemma/src/chip8_2/instructions/sys.rs
new file mode 100644
index 0000000..e69de29
diff --git a/gemmatelnet/Cargo.toml b/gemmatelnet/Cargo.toml
index 41dd034..9533577 100644
--- a/gemmatelnet/Cargo.toml
+++ b/gemmatelnet/Cargo.toml
@@ -1,7 +1,9 @@
[package]
name = "gemmatelnet"
version = "0.1.0"
-edition = "2021"
+edition = "2024"
[dependencies]
gemma = { path = "../gemma" }
+log = "0.4.22"
+clap = { version = "4.5.20", features = ["derive"] }
diff --git a/gemmatelnet/src/bin/gemmatelnetd.rs b/gemmatelnet/src/bin/gemmatelnetd.rs
index 58e3f33..986466f 100644
--- a/gemmatelnet/src/bin/gemmatelnetd.rs
+++ b/gemmatelnet/src/bin/gemmatelnetd.rs
@@ -1,3 +1,50 @@
-fn main() {
- println!("Taxation is Theft");
+use std::io::{BufRead, BufReader, Write};
+use std::net::{TcpListener, TcpStream};
+use std::thread;
+use gemma::chip8::computer_manager::Chip8ComputerManager;
+use gemma::chip8::quirk_modes::QuirkMode;
+use gemmatelnet::client_handler::handle_client;
+use log::info;
+use clap::Parser;
+
+const LISTEN_HOST: &str = "0.0.0.0";
+const LISTEN_PORT: u32 = 1420;
+
+#[derive(Parser, Debug)]
+#[command(name = "myapp")]
+#[command(about = "Does awesome things", long_about = None)]
+struct Cli {
+ #[arg(long, default_value = "0.0.0.0")]
+ host: Option,
+ #[arg(long, default_value = "1420")]
+ port: Option,
+}
+
+impl Cli {
+ pub fn host(&self) -> String {
+ self.host.clone().unwrap_or(LISTEN_HOST.into())
+ }
+ pub fn port(&self) -> u32 {
+ self.port.unwrap_or(LISTEN_PORT.into())
+ }
+}
+
+fn main() -> std::io::Result<()> {
+ let config = Cli::parse();
+
+ let listener = TcpListener::bind(format!("{}:{}", config.host(), config.port()))?;
+ println!("Listening to {}:{}", LISTEN_HOST, LISTEN_PORT);
+
+ for stream in listener.incoming() {
+ match stream {
+ Ok(stream) => {
+ thread::spawn(|| handle_client(stream));
+ }
+ Err(e) => {
+ eprintln!("Connection Failed -> {}", e);
+ }
+ }
+ }
+
+ Ok(())
}
\ No newline at end of file
diff --git a/gemmatelnet/src/client_handler.rs b/gemmatelnet/src/client_handler.rs
new file mode 100644
index 0000000..f1b53fa
--- /dev/null
+++ b/gemmatelnet/src/client_handler.rs
@@ -0,0 +1,127 @@
+use std::io::{BufRead, BufReader};
+use std::net::TcpStream;
+use gemma::chip8::computer_manager::Chip8ComputerManager;
+use gemma::chip8::quirk_modes::QuirkMode;
+use crate::telnet_utils::list_of_files_in_directory;
+use std::io::Write;
+use std::path::Path;
+
+const CANT_NOTIFY_ERROR: &str = "Unable to notify client";
+const MENU_TEXT: &str = r#"
+---------------------------
+status -> Current status of the Instance
+new -> Start New Instance
+* list -> list available files to load into Chip-8 Instance
+* load -> Load Chip-8 Program from server
+menu -> display the menu
+step -> step chip-8 machine 1 step
+* steps -> Step a fixed number of steps
+* memory -> dump current memory from chip-8 instance
+* video -> dump current video from chip-8 instance
+* window -> display a window of memory
+disconnect -> close connection
+---------------------------
+"#;
+
+pub fn handle_client(mut stream: TcpStream) {
+ // each client gets their own instance of a Chip-8 system
+ let mut chip8 = Chip8ComputerManager::default();
+
+ let peer_addr = stream.peer_addr().unwrap();
+ println!("New CX From {}", peer_addr);
+
+ let mut reader = BufReader::new(stream.try_clone().unwrap());
+ let mut line = String::new();
+
+ write!(stream, "Welcome to Gemma Telnet Server\n").unwrap();
+ write!(stream, "{}", MENU_TEXT).unwrap();
+
+ let mut time_to_die: bool = false;
+
+ while !time_to_die {
+
+ // clear the buffer
+ line.clear();
+
+ // read from the client to the buffer
+ let bytes = reader.read_line(&mut line).unwrap();
+ if bytes == 0 {
+ println!("Connection closed by {}", peer_addr);
+ break;
+ }
+
+ // display the message received
+ eprint!("RX {}: {}", peer_addr, line);
+
+ // split the line into (if params exist)
+
+ let mut result = line.split(" ");
+ let command = result.next().unwrap();
+ let p1 = result.next();
+ let p2 = result.next();
+
+ let mut message_to_send: String = "Unrecognized Command".to_string();
+
+ match command.trim() {
+ "status" => {
+ message_to_send = chip8.status_as_string();
+ }
+ "disconnect" => {
+ message_to_send = "Disconnecting User".to_string();
+ time_to_die = true;
+ break;
+ }
+ "new" => {
+ message_to_send = "Starting new Chip-8 Instance".to_string();
+ chip8.reset(QuirkMode::Chip8);
+ }
+ "list" => {
+ message_to_send =
+ format!("Listing files in path: \n{}",
+ list_of_files_in_directory(
+ &mut Path::new("/home/tmerritt/Projects/trevors_chip8_toy/resources/roms".into())
+ )
+ );
+ }
+ "load" => {
+
+ // chip8 = Chip8ComputerManager::from(chip8).load_new_program_to_system_memory()
+ message_to_send = format!("Preparing to load {} from disk.",
+ p1.unwrap_or("default.ch8").trim());
+ chip8.start();
+ }
+ "menu" => {
+ message_to_send = MENU_TEXT.to_string();
+ }
+ "steps" => {
+ if chip8.core_should_run {
+ let num_steps = p1.unwrap_or("0").trim();
+ message_to_send = format!("Advancing {} steps.", num_steps);
+ for _ in 0..num_steps.parse().unwrap_or(0) {
+ chip8.tick();
+ }
+ } else {
+ message_to_send = "Not Ready to Step".to_string();
+ }
+ }
+ "step" => {
+ if chip8.core_should_run {
+ message_to_send = "Advancing 1 step.".to_string();
+ chip8.tick();
+ } else {
+ message_to_send = "Not Ready To Step".to_string();
+ }
+ }
+ "window" => {
+ message_to_send = format!("Memory window from {} to {}",
+ p1.unwrap_or("0x0000").trim(),
+ p2.unwrap_or("0xFFFF").trim());
+ }
+ _ => {}
+ }
+
+ write!(stream, "{}\n", message_to_send).expect(CANT_NOTIFY_ERROR);
+
+ stream.flush().unwrap();
+ }
+}
diff --git a/gemmatelnet/src/lib.rs b/gemmatelnet/src/lib.rs
new file mode 100644
index 0000000..5b3a199
--- /dev/null
+++ b/gemmatelnet/src/lib.rs
@@ -0,0 +1,3 @@
+
+pub mod telnet_utils;
+pub mod client_handler;
\ No newline at end of file
diff --git a/gemmatelnet/src/telnet_utils.rs b/gemmatelnet/src/telnet_utils.rs
new file mode 100644
index 0000000..89ef167
--- /dev/null
+++ b/gemmatelnet/src/telnet_utils.rs
@@ -0,0 +1,18 @@
+use std::{fs, io};
+use std::path::Path;
+
+pub fn list_of_files_in_directory(root_directory: &Path) -> String {
+
+ let mut return_value = String::new();
+
+ if root_directory.is_dir() {
+ for entry in fs::read_dir(root_directory).unwrap() {
+ let entry = entry.unwrap();
+ let path = entry.path();
+ if path.is_file() {
+ return_value = format!("{}\n{}", return_value, path.file_name().unwrap().to_string_lossy());
+ }
+ }
+ }
+ return_value
+}
\ No newline at end of file
diff --git a/gemmautil/src/bin/ch8asm.rs b/gemmautil/src/bin/ch8asm.rs
index 914ecd4..e94e816 100644
--- a/gemmautil/src/bin/ch8asm.rs
+++ b/gemmautil/src/bin/ch8asm.rs
@@ -11,7 +11,6 @@ use std::io::Write;
// <0x|0X>[, ][<0x|OX>[, ][<0x|OX>]][; comment]
use pest::Parser;
use pest_derive::Parser;
-
#[derive(Parser)]
#[grammar = "chip8_asm.pest"]
pub struct Chip8AsmParser;
diff --git a/gemmautil/src/bin/ch8disasm.rs b/gemmautil/src/bin/ch8disasm.rs
index 3c90408..d52be59 100644
--- a/gemmautil/src/bin/ch8disasm.rs
+++ b/gemmautil/src/bin/ch8disasm.rs
@@ -68,7 +68,6 @@ fn main() {
std::fs::read(source_file).unwrap()
);
-
match result.output_file {
None => {
println!("Output to console.");