diff --git a/src/bin/cli/mod.rs b/src/bin/cli/mod.rs index 8a50f65..243d657 100644 --- a/src/bin/cli/mod.rs +++ b/src/bin/cli/mod.rs @@ -86,7 +86,11 @@ fn help(command: Option) { println!(" rom, -r, --rom : load a rom/binary from path"); println!(" screen, -s, --screen : use the \"terminal\" or \"window\" display type"); println!("\nconfiguration:"); - println!(" george-emu searches for a `george.toml` in the current directory.\n 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`"); + println!(" george-emu searches for a `george.toml` in the current directory.\n in `george.toml` you can specify a path for the character rom\n using the key `char_rom` and the main rom/binary with the key `rom`"); + println!("\ndebugging:"); + println!(" you can pipe in a rom for george to evaluate."); + println!(" she'll read it until she reaches a breakpoint (`0x02`) or `stp` instruction,"); + println!(" then will dump her memory to stdout <3"); exit(0); } } diff --git a/src/bin/main.rs b/src/bin/main.rs index b59c455..898f8c0 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,48 +1,75 @@ +use std::io::{self, stdin, IsTerminal}; + #[cfg(not(target_arch = "wasm32"))] mod cli; #[cfg(not(target_arch = "wasm32"))] use cli::get_input; +use georgeemu::memory::{Mem, MemHandle}; #[cfg(not(target_arch = "wasm32"))] use georgeemu::GeorgeEmu; #[cfg(not(target_arch = "wasm32"))] use minifb::{Scale, ScaleMode, Window, WindowOptions}; +use std::{fs::File, io::Read}; + #[cfg(not(target_arch = "wasm32"))] -fn main() { - use std::{fs::File, io::Read}; +fn main() -> io::Result<()> { + use std::io::{stdout, Write}; - let window = Window::new( - "ʕ·ᴥ·ʔ-☆", - 512, - 380, - WindowOptions { - resize: true, - borderless: true, - title: true, - transparency: false, - scale: Scale::FitScreen, - scale_mode: ScaleMode::AspectRatioStretch, - topmost: false, - none: true, - }, - ) - .unwrap(); + use georgeemu::cpu::Cpu; - let config = get_input(); - let rom = match config.rom { - Some(path) => { - let mut file = File::open(path).unwrap(); - let mut bin = vec![0; 0x8000]; - file.read_exact(&mut bin).unwrap(); - // println!("reading char rom"); - bin.try_into().unwrap() - } - None => *include_bytes!("../roms/george.rom"), - }; - let mut emu = GeorgeEmu::builder().rom(rom).window(window).build(); + let mut input = stdin(); - emu.run(); + if !input.is_terminal() { + let mut bytes = [0u8; 0x8000]; + input.read_exact(&mut bytes).unwrap(); + let memory = make_memory(&bytes); + let mut cpu = Cpu::new(memory); + let final_output = cpu.run_to_end(); + + let _ = stdout().write_all(&final_output); + Ok(()) + } else { + let window = Window::new( + "ʕ·ᴥ·ʔ-☆", + 512, + 380, + WindowOptions { + resize: true, + borderless: true, + title: true, + transparency: false, + scale: Scale::FitScreen, + scale_mode: ScaleMode::AspectRatioStretch, + topmost: false, + none: true, + }, + ) + .unwrap(); + + let config = get_input(); + let rom = match config.rom { + Some(path) => { + let mut file = File::open(path).unwrap(); + let mut bin = vec![0; 0x8000]; + file.read_exact(&mut bin).unwrap(); + bin.try_into().unwrap() + } + None => *include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/roms/george.rom")), + }; + let mut emu = GeorgeEmu::builder().rom(rom).window(window).build(); + + emu.run(); + Ok(()) + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn make_memory(bytes: &[u8]) -> MemHandle { + let mut memory = Mem::new(); + memory.load_bytes(bytes); + MemHandle::new(memory) } #[cfg(target_arch = "wasm32")] diff --git a/src/cpu.rs b/src/cpu.rs index 1276691..3f74292 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -97,11 +97,18 @@ impl MemoryWriter for Cpu { impl Cpu { pub fn new(memory: MemHandle) -> Self { + // reset the cpu on initialization so we don't + // scream and cry for two days trying to figure + // out why george isn't working <3 + let low_byte = memory.read(0xFFFC); + let high_byte = memory.read(0xFFFD); + let pc = (high_byte as u16) << 8 | (low_byte as u16); + Cpu { a: 0x00, x: 0x00, y: 0x00, - pc: 0x0000, + pc, s: 0xFF, p: 0b0010_0100, irq: false, @@ -111,8 +118,7 @@ impl Cpu { debug: false, stopped: false, pending_cycles: 0, - cycle: false, // cycle_count: 0, - // state_tx, + cycle: false, } } pub fn with_receiver(mut self, receiver: CpuReceiver) -> Self { @@ -265,4 +271,26 @@ impl Cpu { self.stop(); } } + + fn quick_cycle(&mut self) { + if !self.get_flag(StatusFlag::IrqDisable) && self.irq { + self.interrupt(); + } + let opcode = self.read(self.pc); + let instruction = get_instruction(opcode); + instruction.call(self); + } + + pub fn run_to_end(&mut self) -> [u8; 0x10000] { + self.quick_cycle(); + while self.pending_cycles != 0 { + let opcode = self.read(self.pc); + let instruction = get_instruction(opcode); + match instruction.name { + "stp" | "breakpoint" => return self.memory.dump(), + _ => self.quick_cycle(), + } + } + unreachable!() + } } diff --git a/src/instructions.rs b/src/instructions.rs index 0250fea..8903f46 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -1028,14 +1028,14 @@ pub const OPCODES: [Instruction; 256] = [ instr_fn: Some(breakpoint), address_fn: None, cycles: 0, - name: "none", + name: "breakpoint", addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, - name: "none", + name: "end", addr_mode: "implied", }, Instruction {