use clap::Parser;
use std::{
error::Error,
fs,
time::{Duration, Instant},
};
mod bus;
mod cpu;
mod memory;
use crate::{cpu::Cpu, memory::Memory};
fn parse_hex(src: &str) -> Result<u16, std::num::ParseIntError> {
u16::from_str_radix(src.trim_start_matches("0x"), 16)
}
#[derive(Parser, Debug)]
#[command(name = "atebitemu")]
#[command(version, long_about = None)]
struct Args {
program_path: String,
#[arg(short, long)]
unlimited: bool,
#[arg(short, long, default_value_t = 1)]
mhz: usize,
#[arg(short, long)]
trace: bool,
#[arg(short, long, value_parser = parse_hex)]
start_pc: Option<u16>,
#[arg(short, long, value_parser = parse_hex, default_value = "0x8000")]
load_addr: u16,
#[arg(short, long, value_parser = parse_hex)]
reset_vector: Option<u16>,
}
fn run(mut cpu: Cpu<Memory>, args: &Args) -> Result<(), Box<dyn Error>> {
let clock_speed_hz = args.mhz * 1_000_000;
let batch_duration = Duration::from_millis(16);
let cycles_per_batch = (clock_speed_hz * 16) / 1000;
let mut next_batch_time = Instant::now() + batch_duration;
let mut total_cycles = 0;
loop {
let mut cycles_this_batch = 0;
while cycles_this_batch < cycles_per_batch {
let old_pc = cpu.pc;
if args.trace {
println!("{cpu}");
}
let step_cycles = usize::from(cpu.step()?);
if cpu.pc == old_pc {
println!("\nInfinite loop trap");
println!("{cpu}");
println!("Total cycles: {}", total_cycles + step_cycles);
return Ok(());
}
cycles_this_batch += step_cycles;
total_cycles += step_cycles;
}
if !args.unlimited {
let now = Instant::now();
if next_batch_time > now {
std::thread::sleep(next_batch_time - now);
} else {
next_batch_time = now;
}
next_batch_time += batch_duration;
}
}
}
fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
let program = fs::read(&args.program_path)?;
let mut mem = memory::Memory::new();
mem.load_at(args.load_addr, &program);
if program.len() < 65536 {
let boot_addr = args.reset_vector.unwrap_or(args.load_addr);
mem.write_u16(0xFFFC, boot_addr);
}
let mut cpu = cpu::Cpu::new(mem);
if let Some(pc) = args.start_pc {
cpu.pc = pc;
}
run(cpu, &args)?;
Ok(())
}