1#[cfg(target_arch = "mips")]
19use psx_hw::memory::{expansion2::telemetry, to_kseg1};
20
21const EVENT_KIND_FRAME_BEGIN: u8 = 1;
22const EVENT_KIND_STAGE_BEGIN: u8 = 2;
23const EVENT_KIND_STAGE_END: u8 = 3;
24const EVENT_KIND_COUNTER: u8 = 4;
25const EVENT_KIND_TASK_BEGIN: u8 = 5;
26const EVENT_KIND_TASK_END: u8 = 6;
27
28#[cfg(target_arch = "mips")]
30const fn uncached(physical: u32) -> *mut u32 {
31 to_kseg1(physical) as *mut u32
32}
33
34#[cfg(all(target_arch = "mips", feature = "emit"))]
35const EVENT_ADDR: *mut u32 = uncached(telemetry::EVENT);
36#[cfg(all(target_arch = "mips", feature = "emit"))]
37const VALUE_ADDR: *mut u32 = uncached(telemetry::VALUE);
38#[cfg(all(target_arch = "mips", feature = "emit"))]
39const LOG_ADDR: *mut u32 = uncached(telemetry::LOG);
40
41#[inline(always)]
44pub fn cycles() -> u32 {
45 #[cfg(all(target_arch = "mips", feature = "emit"))]
46 {
47 unsafe { core::ptr::read_volatile(uncached(telemetry::CYCLES) as *const u32) }
51 }
52 #[cfg(not(all(target_arch = "mips", feature = "emit")))]
53 {
54 0
55 }
56}
57
58#[inline(always)]
60pub fn frame_begin(frame: u32) {
61 emit_value(frame);
62 emit_event(EVENT_KIND_FRAME_BEGIN, 0);
63}
64
65#[inline(always)]
67pub fn stage_begin(stage_id: u16) {
68 emit_event(EVENT_KIND_STAGE_BEGIN, stage_id);
69}
70
71#[inline(always)]
73pub fn stage_end(stage_id: u16) {
74 emit_event(EVENT_KIND_STAGE_END, stage_id);
75}
76
77#[inline(always)]
79pub fn counter(counter_id: u16, value: u32) {
80 emit_value(value);
81 emit_event(EVENT_KIND_COUNTER, counter_id);
82}
83
84#[inline(always)]
86pub fn task_begin(task_id: u16) {
87 emit_event(EVENT_KIND_TASK_BEGIN, task_id);
88}
89
90#[inline(always)]
92pub fn task_end(task_id: u16) {
93 emit_event(EVENT_KIND_TASK_END, task_id);
94}
95
96#[inline(always)]
98pub fn debug_log(message: &str) {
99 debug_bytes(message.as_bytes());
100 debug_byte(b'\n');
101}
102
103#[inline(always)]
107pub fn console(message: &str) {
108 #[cfg(target_arch = "mips")]
109 {
110 const PORT: *mut u32 = uncached(telemetry::LOG);
111 for &byte in message.as_bytes() {
112 unsafe { core::ptr::write_volatile(PORT, byte as u32) };
116 }
117 unsafe { core::ptr::write_volatile(PORT, b'\n' as u32) };
119 }
120 #[cfg(not(target_arch = "mips"))]
121 {
122 let _ = message;
123 }
124}
125
126#[inline(always)]
127fn debug_bytes(bytes: &[u8]) {
128 for &byte in bytes {
129 debug_byte(byte);
130 }
131}
132
133#[cfg(any(all(target_arch = "mips", feature = "emit"), test))]
134#[inline(always)]
135fn encode_event(kind: u8, id: u16) -> u32 {
136 ((kind as u32) << 24) | id as u32
137}
138
139#[cfg(all(target_arch = "mips", feature = "emit"))]
140#[inline(always)]
141fn emit_value(value: u32) {
142 unsafe {
145 core::ptr::write_volatile(VALUE_ADDR, value);
146 }
147}
148
149#[cfg(not(all(target_arch = "mips", feature = "emit")))]
150#[inline(always)]
151fn emit_value(_value: u32) {}
152
153#[cfg(all(target_arch = "mips", feature = "emit"))]
154#[inline(always)]
155fn debug_byte(byte: u8) {
156 unsafe {
159 core::ptr::write_volatile(LOG_ADDR, byte as u32);
160 }
161}
162
163#[cfg(not(all(target_arch = "mips", feature = "emit")))]
164#[inline(always)]
165fn debug_byte(_byte: u8) {}
166
167#[cfg(all(target_arch = "mips", feature = "emit"))]
168#[inline(always)]
169fn emit_event(kind: u8, id: u16) {
170 unsafe {
173 core::ptr::write_volatile(EVENT_ADDR, encode_event(kind, id));
174 }
175}
176
177#[cfg(not(all(target_arch = "mips", feature = "emit")))]
178#[inline(always)]
179fn emit_event(_kind: u8, _id: u16) {}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
190 fn event_words_decode_the_way_the_emulator_does() {
191 let kinds = [
192 (EVENT_KIND_FRAME_BEGIN, 1u8),
193 (EVENT_KIND_STAGE_BEGIN, 2),
194 (EVENT_KIND_STAGE_END, 3),
195 (EVENT_KIND_COUNTER, 4),
196 (EVENT_KIND_TASK_BEGIN, 5),
197 (EVENT_KIND_TASK_END, 6),
198 ];
199 for (kind, number) in kinds {
200 assert_eq!(kind, number);
201 for id in [0u16, 1, 51, 266, 0xFFFF] {
202 let word = encode_event(kind, id);
203 assert_eq!((word >> 24) & 0xFF, number as u32);
204 assert_eq!(word & 0xFFFF, id as u32);
205 }
206 }
207 }
208
209 #[test]
210 fn emitters_are_silent_no_ops_off_target() {
211 assert_eq!(cycles(), 0);
214 frame_begin(7);
215 stage_begin(crate::stage::UPDATE);
216 stage_end(crate::stage::UPDATE);
217 counter(crate::counter::TRI_PRIMITIVES, 9);
218 task_begin(crate::task::FIXED_UPDATE);
219 task_end(crate::task::FIXED_UPDATE);
220 debug_log("x");
221 console("x");
222 }
223}