telnetd implementing more stuff.

This commit is contained in:
Trevor Merritt 2025-05-22 13:14:33 -04:00
parent 67ca71ccb7
commit 8c22cf9308
34 changed files with 931 additions and 16 deletions

View File

@ -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"]

4
Cargo.lock generated
View File

@ -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]]

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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()
}

View File

@ -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.
//

View File

@ -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)]

View File

@ -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];

View File

@ -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<Chip8Registers>,
pub destination: Chip8Registers,
pub literal: Option<u8>
}
impl InstructionTrait for Add {
fn length(&self) -> u8 {
1
}
fn encoded(&self) -> Chip8CpuInstructions {
}
fn decode(from: Vec<u8>) -> &dyn InstructionTrait {
todo!()
}
}

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

@ -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"] }

View File

@ -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<String>,
#[arg(long, default_value = "1420")]
port: Option<u32>,
}
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(())
}

View File

@ -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 <filename> -> Load Chip-8 Program from server
menu -> display the menu
step -> step chip-8 machine 1 step
* steps <number> -> Step a fixed number of steps
* memory -> dump current memory from chip-8 instance
* video -> dump current video from chip-8 instance
* window <start> <length> -> 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 <command> <param1> <param2> (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();
}
}

3
gemmatelnet/src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod telnet_utils;
pub mod client_handler;

View File

@ -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
}

View File

@ -11,7 +11,6 @@ use std::io::Write;
// <INSTRUCTION> <0x|0X><parameter>[, ][<0x|OX><parameter>[, ][<0x|OX><parameter>]][; comment]
use pest::Parser;
use pest_derive::Parser;
#[derive(Parser)]
#[grammar = "chip8_asm.pest"]
pub struct Chip8AsmParser;

View File

@ -68,7 +68,6 @@ fn main() {
std::fs::read(source_file).unwrap()
);
match result.output_file {
None => {
println!("Output to console.");