1use arg::Args;
2use log::{Level, LevelFilter, Metadata, Record};
3use std::{cell::RefCell, ops::ControlFlow, rc::Rc};
4
5use librsmsx::{
6 dev::{api::ret_no_hook, util::peek_stack},
7 prelude::*,
8};
9
10const SYSTEM_ROM_FILE: &str = "cbios_main_msx1.rom";
11
12#[derive(Args, Debug)]
13struct MyArgs {
16 #[arg(long)]
17 cart: String,
19
20 #[arg(long = "sys")]
22 system_rom: String,
23
24 #[arg(long, default_value = "true")]
25 quality: bool,
27
28 #[arg(long = "fint", default_value = "16")]
29 frame_interval: u32,
35
36 #[arg(long)]
37 mtype: String,
39}
40
41static MY_LOGGER: MyLogger = MyLogger;
42
43struct MyLogger;
44
45impl log::Log for MyLogger {
46 fn enabled(&self, metadata: &Metadata) -> bool {
47 metadata.level() <= Level::Debug
48 }
49
50 fn log(&self, record: &Record) {
51 if self.enabled(record.metadata()) {
52 println!("{} - {}", record.level(), record.args());
53 }
54 if record.metadata().level() == Level::Error {
56 panic!();
57 }
58 }
59 fn flush(&self) {}
60}
61
62pub struct SampleHook {
63 calls: u64,
64}
65impl Default for SampleHook {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl SampleHook {
72 pub(crate) fn new() -> Self {
73 Self { calls: 0 }
74 }
75}
76
77impl FuncHook for SampleHook {
78 fn handle_generic_hook(
79 &mut self,
80 data: &mut Z80Data,
81 memory: &mut Memory,
82 _ports: &mut Ports,
83 ) -> ControlFlow<()> {
84 if data.pc() == 0x89c7 {
85 println!("HGH: pc:0x{:04x} hl:0x{:04x}", data.pc(), data.hl());
86 let mut addr = data.hl();
87 loop {
88 let ch = memory.read_byte(addr);
89 if ch == 0 {
90 break;
91 }
92 print!("{}", ch as char);
93 addr += 1;
94 }
95 println!();
96 peek_stack(data, memory, 10);
97 ret_no_hook(data, memory);
98 ControlFlow::Continue(()) } else {
101 ControlFlow::Break(()) }
103 }
104 fn handle_call_hook(
105 &mut self,
106 _data: &mut Z80Data,
107 _memory: &mut Memory,
108 _ports: &mut Ports,
109 new_pc: u16,
110 _old_pc: u16,
111 ) -> ControlFlow<()> {
112 if new_pc == 0xb181 {
113 println!("regs before: {:?}", _data);
114 }
115 self.calls += 1;
116 if new_pc == 0xb181 {
117 println!("new call hook #:{} 0x{:04x}", self.calls, new_pc);
118 println!("regs after: {:?}", _data);
119 panic!();
120 }
121 ControlFlow::Continue(())
123 }
124 fn handle_ret_hook(&mut self, _data: &mut Z80Data, _memory: &mut Memory, _next_pc: u16) {}
125 fn handle_command_hook(
126 &mut self,
127 _data: &mut Z80Data,
128 _memory: &mut Memory,
129 _ports: &mut Ports,
130 mode: u64,
131 ) {
132 println!("debug mode #:{}", mode);
133 }
134}
135
136fn main() -> Result<(), Box<dyn std::error::Error>> {
137 log::set_logger(&MY_LOGGER).unwrap();
138 log::set_max_level(LevelFilter::Debug);
139
140 let args: std::vec::Vec<_> = std::env::args().skip(1).collect();
141 match MyArgs::from_args(args.iter().map(|x| x.as_str())) {
142 Ok(mut args) => {
143 if args.system_rom.is_empty() {
144 args.system_rom = SYSTEM_ROM_FILE.to_string();
145 }
146 let base_scale = 2.0;
147 let scale_magnifier = 2.0;
148 let mut sys = BaseSystem::new(base_scale * scale_magnifier);
149 let ppi = Rc::new(RefCell::new(PPI::new()));
150 let mut memory = Memory::new(ppi.clone());
151 memory.load_bios_basic(&args.system_rom);
152 if !args.cart.is_empty() {
153 memory.load_rom(&args.cart, 1, &args.mtype);
154 }
155 let psg = PSG::new(SoundType::Normal, Some(&mut sys));
156 let vdp = Rc::new(RefCell::new(Vdp::new(GraphicsType::Normal, args.quality)));
157 let ports = Ports::new(vdp.clone(), ppi.clone(), psg);
158 let mut cpu_z80 = Z80::new(memory, ports);
159 cpu_z80.set_hook(Rc::new(RefCell::new(SampleHook::new())));
160 cpu_z80.reboot();
161 let mut msx = MSX::new(
162 cpu_z80,
163 vdp.clone(),
164 );
168
169 let avg_fps = msx.main_loop(args.frame_interval as isize, &mut sys);
170 log::info!("Avg FPS: {:.2}", avg_fps);
172 }
173 Err(err) => println!("err={:?}", err),
174 }
175 Ok(())
176}