yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
apu_debugger.cc
Go to the documentation of this file.
1// apu_debugger.cc - APU Handshake Tracker Implementation
2
4
5#include "absl/strings/str_format.h"
6#include "util/log.h"
7
8namespace yaze {
9namespace emu {
10namespace debug {
11
15
18 handshake_complete_ = false;
19 ipl_rom_enabled_ = true;
22
23 memset(cpu_ports_, 0, sizeof(cpu_ports_));
24 memset(spc_ports_, 0, sizeof(spc_ports_));
25
26 blocks_.clear();
27 port_history_.clear();
28
29 LOG_DEBUG("APU_DEBUG", "Handshake tracker reset");
30}
31
32void ApuHandshakeTracker::OnCpuPortWrite(uint8_t port, uint8_t value,
33 uint32_t pc) {
34 if (port > 3)
35 return;
36
37 cpu_ports_[port] = value;
38
39 // Check for handshake acknowledge
40 if (phase_ == Phase::WAITING_BBAA && port == 0 && value == 0xCC) {
43 LogPortWrite(true, port, value, pc, "HANDSHAKE ACKNOWLEDGE");
44 LOG_INFO("APU_DEBUG", "✓ CPU sent handshake $CC at PC=$%06X", pc);
45 return;
46 }
47
48 // Track transfer counter writes
50 if (port == 0) {
51 transfer_counter_ = value;
53 LogPortWrite(true, port, value, pc,
54 absl::StrFormat("Counter=%d", transfer_counter_));
55 } else if (port == 1) {
56 // F5 = continuation flag (0=more blocks, 1=final block)
57 bool is_final = (value & 0x01) != 0;
58 LogPortWrite(true, port, value, pc,
59 is_final ? "FINAL BLOCK" : "More blocks");
60 } else if (port == 2 || port == 3) {
61 // F6:F7 = destination address
62 LogPortWrite(true, port, value, pc, "Dest addr");
63 }
64 } else {
65 LogPortWrite(true, port, value, pc, "");
66 }
67}
68
69void ApuHandshakeTracker::OnSpcPortWrite(uint8_t port, uint8_t value,
70 uint16_t pc) {
71 if (port > 3)
72 return;
73
74 spc_ports_[port] = value;
75
76 // Check for ready signal ($BBAA in F4:F5)
77 if (phase_ == Phase::IPL_BOOT && port == 0 && value == 0xAA) {
78 if (spc_ports_[1] == 0xBB || port == 1) { // Check if both ready
80 LogPortWrite(false, port, value, pc, "READY SIGNAL $BBAA");
81 LOG_INFO("APU_DEBUG", "✓ SPC ready signal: F4=$AA F5=$BB at PC=$%04X",
82 pc);
83 return;
84 }
85 }
86
87 if (phase_ == Phase::IPL_BOOT && port == 1 && value == 0xBB) {
88 if (spc_ports_[0] == 0xAA) {
90 LogPortWrite(false, port, value, pc, "READY SIGNAL $BBAA");
91 LOG_INFO("APU_DEBUG", "✓ SPC ready signal: F4=$AA F5=$BB at PC=$%04X",
92 pc);
93 return;
94 }
95 }
96
97 // Track counter echo during transfer
98 if (phase_ == Phase::TRANSFER_ACTIVE && port == 0) {
99 int echoed_counter = value;
100 if (echoed_counter == transfer_counter_) {
102 LogPortWrite(false, port, value, pc,
103 absl::StrFormat("Echo counter=%d (byte %d)", echoed_counter,
105 } else {
106 LogPortWrite(false, port, value, pc,
107 absl::StrFormat("Counter mismatch! Expected=%d Got=%d",
108 transfer_counter_, echoed_counter));
109 LOG_WARN("APU_DEBUG", "Counter mismatch at PC=$%04X: expected %d, got %d",
110 pc, transfer_counter_, echoed_counter);
111 }
112 } else {
113 LogPortWrite(false, port, value, pc, "");
114 }
115}
116
117void ApuHandshakeTracker::OnSpcPCChange(uint16_t old_pc, uint16_t new_pc) {
118 // Detect IPL ROM boot sequence
119 if (phase_ == Phase::RESET && new_pc >= 0xFFC0 && new_pc <= 0xFFFF) {
121 LOG_INFO("APU_DEBUG", "✓ SPC entered IPL ROM at PC=$%04X", new_pc);
122 }
123
124 // Detect IPL ROM disable (jump to uploaded driver)
125 if (ipl_rom_enabled_ && new_pc < 0xFFC0) {
126 ipl_rom_enabled_ = false;
129 LOG_INFO("APU_DEBUG",
130 "✓ Transfer complete! SPC jumped to $%04X (audio driver entry)",
131 new_pc);
132 }
134 }
135}
136
138 if (phase_ != new_phase) {
139 LOG_DEBUG("APU_DEBUG", "Phase change: %s → %s", GetPhaseString().c_str(),
140 [new_phase]() {
141 switch (new_phase) {
142 case Phase::RESET:
143 return "RESET";
144 case Phase::IPL_BOOT:
145 return "IPL_BOOT";
147 return "WAITING_BBAA";
149 return "HANDSHAKE_CC";
151 return "TRANSFER_ACTIVE";
153 return "TRANSFER_DONE";
154 case Phase::RUNNING:
155 return "RUNNING";
156 default:
157 return "UNKNOWN";
158 }
159 }());
160 phase_ = new_phase;
161 }
162}
163
164void ApuHandshakeTracker::LogPortWrite(bool is_cpu, uint8_t port, uint8_t value,
165 uint32_t pc, const std::string& desc) {
166 PortWrite entry;
167 entry.timestamp = port_history_.size();
168 entry.pc = static_cast<uint16_t>(pc & 0xFFFF);
169 entry.port = port;
170 entry.value = value;
171 entry.is_cpu = is_cpu;
172 entry.description = desc;
173
174 port_history_.push_back(entry);
175
176 // Keep history bounded
177 if (port_history_.size() > kMaxHistorySize) {
178 port_history_.pop_front();
179 }
180}
181
183 switch (phase_) {
184 case Phase::RESET:
185 return "RESET";
186 case Phase::IPL_BOOT:
187 return "IPL_BOOT";
189 return "WAITING_BBAA";
191 return "HANDSHAKE_CC";
193 return "TRANSFER_ACTIVE";
195 return "TRANSFER_DONE";
196 case Phase::RUNNING:
197 return "RUNNING";
198 default:
199 return "UNKNOWN";
200 }
201}
202
204 return absl::StrFormat("Phase: %s | Handshake: %s | Bytes: %d | Blocks: %d",
205 GetPhaseString(), handshake_complete_ ? "✓" : "✗",
207}
208
211 return "";
212 }
213
214 // Estimate progress (typical ALTTP upload is ~8KB)
215 int estimated_total = 8192;
216 int percent = (total_bytes_transferred_ * 100) / estimated_total;
217 percent = std::min(percent, 100);
218
219 int bar_width = 20;
220 int filled = (percent * bar_width) / 100;
221
222 std::string bar = "[";
223 for (int i = 0; i < bar_width; ++i) {
224 bar += (i < filled) ? "█" : "░";
225 }
226 bar += absl::StrFormat("] %d%%", percent);
227
228 return bar;
229}
230
231} // namespace debug
232} // namespace emu
233} // namespace yaze
void OnCpuPortWrite(uint8_t port, uint8_t value, uint32_t pc)
void OnSpcPCChange(uint16_t old_pc, uint16_t new_pc)
void LogPortWrite(bool is_cpu, uint8_t port, uint8_t value, uint32_t pc, const std::string &desc)
std::vector< TransferBlock > blocks_
static constexpr size_t kMaxHistorySize
void OnSpcPortWrite(uint8_t port, uint8_t value, uint16_t pc)
std::deque< PortWrite > port_history_
#define LOG_DEBUG(category, format,...)
Definition log.h:103
#define LOG_WARN(category, format,...)
Definition log.h:107
#define LOG_INFO(category, format,...)
Definition log.h:105