1use log::debug;
2
3use crate::{
4 context::{Bus, Interrupt, Timing},
5 cpu::Cpu,
6 util::trait_alias,
7};
8
9trait_alias!(pub trait Context = Bus + Timing + Interrupt);
10
11struct BiosFunctionInfo {
12 id: u8,
13 name: &'static str,
14 args: &'static [BiosFunctionArg],
15 }
17
18struct BiosFunctionArg {
19 name: &'static str,
20 ty: &'static str,
21}
22
23macro_rules! bios_functions {
24 ($id:literal $name:ident ($($arg_name:ident : $arg_type:ty),*) $($rest:tt)*) => {
25 bios_functions!($($rest)* (BiosFunctionInfo {
26 id: $id,
27 name: stringify!($name),
28 args: &[$(BiosFunctionArg {
29 name: stringify!($arg_name),
30 ty: stringify!($arg_type),
31 }),*],
32 }))
33 };
34
35 ($($entry:tt)*) => {
36 &[ $($entry),* ]
37 };
38}
39
40const BIOS_FUNCTIONS: &[BiosFunctionInfo] = bios_functions! {
41 0x00 SoftReset()
42 0x01 RegisterRamReset(reset_flags: u8)
43 0x02 Halt()
44 0x03 Stop()
45 0x04 IntrWait(force_wait_next: bool, irq_to_wait: u32)
46 0x05 VBlankIntrWait()
47 0x06 Div(num: i32, denom: i32)
48 0x07 DivArm(num: i32, denom: i32)
49 0x08 Sqrt(num: u32)
50 0x09 ArcTan(tan: f_1_14)
51 0x0A ArcTan2(x: f_1_14, y: f_1_14)
52 0x0B CpuSet(src: addr, dest: addr, __: length_mode)
53 0x0C CpuFastSet(src: addr, dest: addr, __: length_mode)
54 0x0D GetBiosChecksum()
55 0x0E BgAffineSet(src_info: addr, dest_info: addr)
56 0x0F ObjAffineSet(src_info: addr, dest_info: addr)
57 0x10 BitUnPack(src: addr, dest: addr, unpack_info: addr)
58 0x11 LZ77UnCompReadNormalWrite8bit(src_info: addr, dest: addr)
59 0x12 LZ77UnCompReadNormalWrite16bit(src_info: addr, dest: addr)
60 0x13 HuffUnCompReadNormal(src_info: addr, dest: addr)
61 0x14 RLUnCompReadNormalWrite8bit(src_info: addr, dest: addr)
62 0x15 RLUnCompReadNormalWrite16bit(src_info: addr, dest: addr)
63 0x16 Diff8bitUnFilterWrite8bit(src_info: addr, dest: addr)
64 0x17 Diff8bitUnFilterWrite16bit(src_info: addr, dest: addr)
65 0x18 Diff16bitUnFilter(src_info: addr, dest: addr)
66 0x19 SoundBias(bias_level: u16)
67 0x1A SoundDriverInit(work: addr)
68 0x1B SoundDriverMode(mode: u32)
69 0x1C SoundDriverMain()
70 0x1D SoundDriverVSync()
71 0x1E SoundChannelClear()
72 0x1F MidiKey2Freq(wave_data: addr, mk: u8, fp: u8)
73 0x20 SoundWhatever0()
74 0x21 SoundWhatever1()
75 0x22 SoundWhatever2()
76 0x23 SoundWhatever3()
77 0x24 SoundWhatever4()
78 0x25 MultiBoot(param: addr, trans_mode: u8)
79 0x26 HardReset()
80 0x27 CustomHalt(arg1: u32, arg2: u32, halt_or_stop: u8)
81 0x28 SoundDriverVSyncOff()
82 0x29 SoundDriverVSyncOn()
83 0x2A SoundGetJumpList(dest: addr)
84};
85
86const UNKNOWN_BIOS_FUNCTION: BiosFunctionInfo = BiosFunctionInfo {
87 id: 0xFF,
88 name: "Unknown",
89 args: &[],
90};
91
92pub fn trace_swi<C: Context>(cpu: &mut Cpu<C>, id: u8, pc: u32) {
93 if !log::log_enabled!(log::Level::Debug) {
94 return;
95 }
96
97 let info = BIOS_FUNCTIONS
98 .iter()
99 .find(|i| i.id == id)
100 .unwrap_or(&UNKNOWN_BIOS_FUNCTION);
101
102 let extract_arg = |regno: usize, arg: &BiosFunctionArg| {
103 let name = arg.name;
104 let val = cpu.regs().r(regno);
105 match arg.ty {
106 "bool" => {
107 if val == 0 || val == 1 {
108 format!("{name}: {}", val != 0)
109 } else {
110 format!("{name}: {} (={val:08X})", val != 0)
111 }
112 }
113 "u8" => format!("{name}: 0x{val:02X}"),
114 "u16" => format!("{name}: 0x{val:04X}"),
115 "u32" => format!("{name}: 0x{val:8X}"),
116 "i32" => format!("{name}: {}", val as i32),
117 "addr" => format!("{name}: 0x{val:08X}"),
118
119 "f_1_14" => format!("{name}: {}", val as u16 as i16 as f64 / 16384.0),
120
121 "length_mode" => {
122 let count = val & 0x1FFFFF;
123 let mode = if (val >> 24) & 0x1 == 0 {
124 "copy"
125 } else {
126 "fill"
127 };
128 let size = if (val >> 26) & 0x1 == 0 { 16 } else { 32 };
129 format!("param: ({count}x{size}bit, {mode})")
130 }
131 _ => unreachable!("{}", info.name),
132 }
133 };
134
135 let args = info
136 .args
137 .iter()
138 .enumerate()
139 .map(|(i, arg)| extract_arg(i, arg))
140 .collect::<Vec<_>>()
141 .join(", ");
142
143 debug!("SWI {id:02X}h from 0x{pc:08X}: {}({args})", info.name);
144}