// #![allow(dead_code)] mod cpu; mod error; mod instructions; mod keyboard; mod memory; mod types; mod video; use crate::cpu::Cpu; use crate::keyboard::Keyboard; use crate::memory::Mem; use crate::video::{Screen, TerminalRenderer}; use crossterm::execute; use crossterm::terminal::{size, Clear, ClearType, SetSize}; // use cpu::CpuController; use memory::MemHandle; // use minifb::{Scale, ScaleMode, Window, WindowOptions}; use serde::{Deserialize, Serialize}; use std::env; use std::io::ErrorKind; use std::process::exit; use std::thread::{self, sleep}; use std::time::Duration; use std::{ fs::File, io::{stdout, Read, Result}, path::PathBuf, str::FromStr, }; use crossterm::{ cursor, event::{self, KeyCode, KeyEventKind}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; #[derive(Serialize, Deserialize, Debug)] struct Config { char_rom: Option, rom: String, } fn main() -> Result<()> { let args: Vec = env::args().collect(); let mut memory = Mem::new(); match args.len() { 0 => { println!("george-emu must be run in the terminal, don't know what went wrong here!"); exit(1) } 1 => { let config: Config = match File::open("./george.toml") { Ok(mut file) => { let mut string = String::new(); file.read_to_string(&mut string).unwrap(); toml::from_str(string.as_str()).unwrap() } Err(_) => { println!("couldn't find a `george.toml` in the current directory!"); exit(1); } }; let rom = match std::fs::File::open(config.rom) { Ok(file) => file, Err(error) => panic!("Couldn't open main rom! {:?}", error), }; if let Err(error) = memory.load_rom(rom) { println!("{:?}", error); }; } 2 => match &args[1] as &str { "help" => { println!("ʕ·ᴥ·ʔ- george-emu is an emulator for george:"); println!("https://git.augustkline.com/august/george\n"); println!("commands:"); println!(" help: print this help screen"); println!(" help : print help info for any command"); println!(" rom : load a rom/binary from path\n"); println!("configuration:"); println!(" george-emu searches for a `george.toml` in the current directory. in `george.toml` you can specify a path for the character rom using the key `char_rom` and the main rom/binary with the key `rom`"); exit(0); } _ => { println!( "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands", &args[1], &args[0] ); exit(1); } }, 3 => match &args[1] as &str { "help" => match &args[2] as &str { "rom" => { println!("{:?} rom \nload a rom/binary from path", &args[0]); } _ => { println!( "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands", &args[2], &args[0] ); exit(1); } }, "rom" => { let rom = match std::fs::File::open(&args[2]) { Ok(file) => file, Err(error) => { match error.kind() { ErrorKind::NotFound => { println!("couldn't find the rom at {:?}", &args[2]); } ErrorKind::PermissionDenied => { println!( "didn't have sufficient permissions to open the rom at {:?}", &args[2] ); } _ => { println!("something went wrong! try again in a moment? really not sure why you're getting this error"); } } exit(1); } }; if let Err(error) = memory.load_rom(rom) { println!("oh no! this rom couldn't be loaded: {:?}", error); exit(1); }; } _ => { println!( "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands", &args[1], &args[0] ); exit(1); } }, _ => { println!( "too many arguments were provided!\n\nuse `{} help` to see all valid commands", &args[0] ); exit(1); } } let mut stdout = stdout(); let (cols, rows) = size()?; execute!(stdout, SetSize(64, 29), cursor::Hide, EnterAlternateScreen)?; enable_raw_mode()?; memory .dump(PathBuf::from_str("./coredump.bin").unwrap()) .unwrap(); let shared_memory = MemHandle::new(memory); let screen_memory = shared_memory.clone(); let cpu_memory = shared_memory.clone(); let keyboard_memory = shared_memory.clone(); // For when we want to leave the terminal again :sigh: // // thread::spawn(move || { // let mut screen = Crtc::new( // screen_memory, // config.char_rom.as_ref(), // CpuController::new(screen_cpu_tx), // window_tx, // ); // screen.run(); // }); // let mut window = Window::new( // "ʕ·ᴥ·ʔ-☆", // 512, // 380, // WindowOptions { // resize: true, // borderless: true, // title: true, // transparency: false, // scale: Scale::X2, // scale_mode: ScaleMode::AspectRatioStretch, // topmost: false, // none: true, // }, // ) // .unwrap(); let keyboard = Keyboard::new(keyboard_memory); let (mut cpu, cpu_controller) = Cpu::new_with_control(cpu_memory); let _ = cpu.reset(); thread::spawn(move || loop { cpu.cycle(); }); let stdout_lock = stdout.lock(); let renderer = TerminalRenderer::new(screen_memory, stdout_lock); let mut screen = Screen::new(renderer, cpu_controller); loop { sleep(Duration::from_millis(16)); screen.draw(); if event::poll(std::time::Duration::from_millis(16))? { if let event::Event::Key(key) = event::read()? { keyboard.read_keys(key); if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { break; } } } } execute!( stdout, LeaveAlternateScreen, SetSize(cols, rows), Clear(ClearType::Purge) )?; disable_raw_mode()?; Ok(()) }