codecin 5.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. codecin/__init__.py +27 -0
  2. codecin/aot.py +229 -0
  3. codecin/assembler.py +548 -0
  4. codecin/cache.py +71 -0
  5. codecin/cin.py +2558 -0
  6. codecin/cli.py +282 -0
  7. codecin/config.py +58 -0
  8. codecin/console.py +183 -0
  9. codecin/cpu.py +1654 -0
  10. codecin/crom.py +222 -0
  11. codecin/debugger.py +659 -0
  12. codecin/disasm.py +86 -0
  13. codecin/errors.py +58 -0
  14. codecin/isa.py +457 -0
  15. codecin/jit.py +270 -0
  16. codecin/libcodecin_native.so +0 -0
  17. codecin/logger.py +145 -0
  18. codecin/memory.py +348 -0
  19. codecin/native/aot/aot.go +70 -0
  20. codecin/native/aot/build.go +219 -0
  21. codecin/native/aot/stub_main.go.txt +22 -0
  22. codecin/native/build.ps1 +33 -0
  23. codecin/native/build.sh +48 -0
  24. codecin/native/cmd/codecin/main.go +287 -0
  25. codecin/native/compiler/codegen.go +1938 -0
  26. codecin/native/compiler/compile_test.go +164 -0
  27. codecin/native/compiler/parser.go +1071 -0
  28. codecin/native/compiler/syscalls.go +86 -0
  29. codecin/native/compiler/tokenizer.go +336 -0
  30. codecin/native/compiler/types.go +213 -0
  31. codecin/native/engine/audio.go +152 -0
  32. codecin/native/engine/audio_other.go +46 -0
  33. codecin/native/engine/audio_windows.go +43 -0
  34. codecin/native/engine/canvas.go +271 -0
  35. codecin/native/engine/crom.go +86 -0
  36. codecin/native/engine/encode.go +93 -0
  37. codecin/native/engine/engine_test.go +198 -0
  38. codecin/native/engine/isa_gen.go +445 -0
  39. codecin/native/engine/system.go +190 -0
  40. codecin/native/engine/termux.go +113 -0
  41. codecin/native/engine/version_gen.go +7 -0
  42. codecin/native/engine/vm.go +1157 -0
  43. codecin/native/go.mod +3 -0
  44. codecin/native/ir/ir.go +48 -0
  45. codecin/native/main.go +225 -0
  46. codecin/native.py +336 -0
  47. codecin/registers.py +130 -0
  48. codecin/stats.py +235 -0
  49. codecin-5.4.2.dist-info/METADATA +985 -0
  50. codecin-5.4.2.dist-info/RECORD +54 -0
  51. codecin-5.4.2.dist-info/WHEEL +5 -0
  52. codecin-5.4.2.dist-info/entry_points.txt +2 -0
  53. codecin-5.4.2.dist-info/licenses/LICENSE +7 -0
  54. codecin-5.4.2.dist-info/top_level.txt +1 -0
codecin/debugger.py ADDED
@@ -0,0 +1,659 @@
1
+ """调试器: 条件断点、执行历史(前进/后退)、TCP 远程调试服务与交互式调试会话。
2
+
3
+ 职责 (自 codecin/cpu.py 拆分):
4
+ - DebugSession 交互式调试会话 (断点命中 / --step 统一命令集)
5
+ - display_state 终端状态渲染 (寄存器/内存/缓存)
6
+ - DebugServer 条件断点 + 执行历史 + TCP 远程调试服务
7
+
8
+ 设计约束: 本模块不 import codecin.cpu (仅 TYPE_CHECKING), 通过属性访问 CPU。
9
+ """
10
+
11
+ import contextlib
12
+ import socket
13
+ import time
14
+ from dataclasses import dataclass
15
+ from typing import TYPE_CHECKING, Dict, List, Optional
16
+
17
+ from .console import Colors, Panel
18
+
19
+ if TYPE_CHECKING:
20
+ from .cpu import CPU
21
+
22
+
23
+ # ==================== 状态渲染 ====================
24
+
25
+
26
+ def fmt_operand(op: tuple) -> str:
27
+ """格式化操作数为汇编文本 (自 cpu.CPU._fmt_operand 迁移)。"""
28
+ kind = op[0]
29
+ if kind == 'reg':
30
+ return f"X{op[1]}"
31
+ if kind == 'vec':
32
+ return f"V{op[1]}"
33
+ if kind == 'veclane':
34
+ return f"V{op[1]}.{op[2]}"
35
+ if kind == 'imm':
36
+ return f"#{op[1]}"
37
+ if kind == 'mem':
38
+ base, off = op[1], op[2]
39
+ if base >= 0:
40
+ return f"[X{base}, #{off}]" if off else f"[X{base}]"
41
+ return f"[#{off}]"
42
+ if kind == 'cond':
43
+ return op[1]
44
+ if kind == 'str':
45
+ return f"\"@{op[1]}\""
46
+ return str(op)
47
+
48
+
49
+ def display_state(cpu: 'CPU', title: str = "CPU State",
50
+ opcode: Optional[str] = None,
51
+ args: Optional[List[tuple]] = None) -> None:
52
+ """渲染 CPU 完整状态 (寄存器/标志/内存/缓存)。"""
53
+ console = cpu.console
54
+ console.clear()
55
+ console.rule(f"{Colors.colorize(title, Colors.MAGENTA, True)}")
56
+
57
+ if opcode:
58
+ instr_text = f"{opcode} " + ", ".join(fmt_operand(a) for a in (args or []))
59
+ console.print(str(Panel(instr_text.strip(), title="Current Instruction")))
60
+
61
+ reg_info = {
62
+ 'PSTATE': ' '.join(f"{k}={int(v)}" for k, v in cpu.pstate.items()),
63
+ 'SP': cpu.sp,
64
+ 'PC': cpu.pc,
65
+ }
66
+ if cpu.breakpoints:
67
+ reg_info['BREAKPOINTS'] = ', '.join(f"{b:#x}" for b in cpu.breakpoints)
68
+ cpu.regs.display_registers("General Registers (X0-X31)", reg_info, console)
69
+
70
+ if cpu.config.show_vector_regs:
71
+ cpu.vec_regs.display_vector_registers("Vector Registers (V0-V31)", console)
72
+
73
+ cpu.memory.display_memory("Memory (first 64 bytes)", 0, 64, console)
74
+
75
+ cache_stats = cpu.cache.get_stats()
76
+ console.print(
77
+ f"Cache: {cache_stats['hits']} hits, {cache_stats['misses']} misses "
78
+ f"({cache_stats['hit_rate'] * 100:.1f}% hit rate)")
79
+ console.rule()
80
+
81
+
82
+ DEBUG_HELP = f"""
83
+ {Colors.colorize('Debug Commands:', Colors.CYAN, True)}
84
+ {Colors.colorize('continue / c', Colors.GREEN)} - Continue execution
85
+ {Colors.colorize('step / s (空行)', Colors.GREEN)} - Single step
86
+ {Colors.colorize('run / r', Colors.GREEN)} - Run freely (退出单步模式)
87
+ {Colors.colorize('reverse', Colors.GREEN)} - Reverse one step
88
+ {Colors.colorize('forward', Colors.GREEN)} - Forward one step
89
+ {Colors.colorize('break <addr> [cond]', Colors.GREEN)} - Set breakpoint
90
+ {Colors.colorize('delete <addr>', Colors.GREEN)} - Remove breakpoint
91
+ {Colors.colorize('watch <addr> [r|w|rw]', Colors.GREEN)} - Set watchpoint
92
+ {Colors.colorize('list / info', Colors.GREEN)} - List breakpoints
93
+ {Colors.colorize('print / p <target>', Colors.GREEN)} - X0-X31, regs, mem [addr], cache, pc, sp
94
+ {Colors.colorize('quit / q', Colors.GREEN)} - Exit simulator
95
+ {Colors.colorize('help / h', Colors.GREEN)} - Show this help
96
+ """
97
+
98
+
99
+ @dataclass
100
+ class ConditionalBreakpoint:
101
+ address: int
102
+ condition: str
103
+ count: int = 0
104
+ hit_count: int = 0
105
+ enabled: bool = True
106
+
107
+
108
+ # ==================== 远程调试服务 ====================
109
+
110
+
111
+ class DebugServer:
112
+ """TCP 远程调试服务 + 条件断点求值 + 执行历史 (前进/后退)。
113
+
114
+ 注意: 交互式会话在本地以 DebugSession 驱动; 本服务为未来 IDE/远程
115
+ 集成预留 (start() 为阻塞式服务循环)。
116
+ """
117
+
118
+ def __init__(self, cpu: 'CPU', port: int = 1234):
119
+ self.cpu = cpu
120
+ self.port = port
121
+ self.socket = None
122
+ self.connected = False
123
+ self.running = False
124
+ self.execution_history: List[Dict] = []
125
+ self.history_limit = 1000
126
+ self.history_index = 0
127
+ self.conditional_breakpoints: List[ConditionalBreakpoint] = []
128
+
129
+ # ---------------- 历史记录 ----------------
130
+
131
+ def record_state(self) -> None:
132
+ snapshot = {
133
+ 'pc': self.cpu.pc,
134
+ 'regs': self.cpu.regs.get_all(),
135
+ 'sp': self.cpu.sp,
136
+ 'pstate': self.cpu.pstate.copy(),
137
+ 'timestamp': time.time(),
138
+ }
139
+ self.execution_history.append(snapshot)
140
+ if len(self.execution_history) > self.history_limit:
141
+ self.execution_history.pop(0)
142
+ self.history_index = len(self.execution_history) - 1
143
+
144
+ def _restore(self, snapshot: Dict) -> None:
145
+ self.cpu.pc = snapshot['pc']
146
+ self.cpu.regs.set_all(snapshot['regs'])
147
+ self.cpu.sp = snapshot['sp']
148
+ self.cpu.pstate = snapshot['pstate'].copy()
149
+
150
+ def reverse_step(self) -> bool:
151
+ if self.history_index > 0:
152
+ self.history_index -= 1
153
+ self._restore(self.execution_history[self.history_index])
154
+ return True
155
+ return False
156
+
157
+ def forward_step(self) -> bool:
158
+ if self.history_index < len(self.execution_history) - 1:
159
+ self.history_index += 1
160
+ self._restore(self.execution_history[self.history_index])
161
+ return True
162
+ return False
163
+
164
+ # ---------------- 条件断点 ----------------
165
+
166
+ def add_conditional_breakpoint(self, address: int, condition: str) -> None:
167
+ bp = ConditionalBreakpoint(address=address, condition=condition)
168
+ self.conditional_breakpoints.append(bp)
169
+ self.cpu.breakpoints.add(address)
170
+ self.cpu.logger.info(f"Conditional breakpoint set at PC={address:#x}: {condition}")
171
+
172
+ def check_conditional_breakpoints(self) -> bool:
173
+ for bp in self.conditional_breakpoints:
174
+ if bp.address == self.cpu.pc and bp.enabled:
175
+ try:
176
+ namespace = {
177
+ 'regs': self.cpu.regs.get_all(),
178
+ 'pc': self.cpu.pc,
179
+ 'sp': self.cpu.sp,
180
+ 'pstate': self.cpu.pstate.copy(),
181
+ }
182
+ if eval(bp.condition, {"__builtins__": {}}, namespace):
183
+ bp.hit_count += 1
184
+ self.cpu.console.print(
185
+ f"{Colors.colorize('Conditional breakpoint hit', Colors.YELLOW)} "
186
+ f"at PC={self.cpu.pc:#x}: {bp.condition}")
187
+ return True
188
+ except Exception as e:
189
+ self.cpu.logger.warning(f"Condition evaluation failed: {e}")
190
+ return False
191
+
192
+ # ---------------- TCP 服务 ----------------
193
+
194
+ def start(self) -> None:
195
+ """阻塞式远程调试服务 (仅绑定 localhost; 供未来 IDE/调试客户端接入)。"""
196
+ try:
197
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
198
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
199
+ self.socket.bind(('localhost', self.port))
200
+ self.socket.listen(1)
201
+ self.cpu.logger.info(f"Debug server listening on port {self.port}")
202
+ self.running = True
203
+ while self.running:
204
+ conn, addr = self.socket.accept()
205
+ self.connected = True
206
+ self.cpu.logger.info(f"Debug client connected from {addr}")
207
+ self._handle_connection(conn)
208
+ except Exception as e:
209
+ self.cpu.logger.error(f"Debug server error: {e}")
210
+
211
+ def _handle_connection(self, conn) -> None:
212
+ try:
213
+ while self.running:
214
+ data = conn.recv(1024)
215
+ if not data:
216
+ break
217
+ response = self._process_command(data)
218
+ if response:
219
+ conn.send(response.encode())
220
+ except Exception as e:
221
+ self.cpu.logger.debug(f"Debug connection error: {e}")
222
+ finally:
223
+ conn.close()
224
+ self.connected = False
225
+
226
+ def _process_command(self, data: bytes) -> str:
227
+ cmd = data.decode().strip()
228
+ parts = cmd.split()
229
+ if not parts:
230
+ return "ERROR: Empty command"
231
+ command = parts[0].lower()
232
+
233
+ if command == 'step':
234
+ self.record_state()
235
+ self.cpu.step()
236
+ return "OK: Stepped"
237
+ elif command == 'continue':
238
+ self.cpu.running = True
239
+ return "OK: Continuing"
240
+ elif command == 'reverse':
241
+ return "OK: Reversed" if self.reverse_step() else "ERROR: No history"
242
+ elif command == 'forward':
243
+ return "OK: Forwarded" if self.forward_step() else "ERROR: No history"
244
+ elif command == 'break':
245
+ if len(parts) > 1:
246
+ try:
247
+ addr = int(parts[1], 0)
248
+ if len(parts) > 2:
249
+ cond = ' '.join(parts[2:])
250
+ self.add_conditional_breakpoint(addr, cond)
251
+ return f"OK: Conditional breakpoint at {addr:#x}: {cond}"
252
+ self.cpu.add_breakpoint(addr)
253
+ return f"OK: Breakpoint at {addr:#x}"
254
+ except ValueError:
255
+ return "ERROR: Invalid address"
256
+ return "ERROR: Missing address"
257
+ elif command == 'delete':
258
+ if len(parts) > 1:
259
+ try:
260
+ addr = int(parts[1], 0)
261
+ self.cpu.remove_breakpoint(addr)
262
+ self.conditional_breakpoints = [
263
+ bp for bp in self.conditional_breakpoints if bp.address != addr]
264
+ return f"OK: Removed breakpoint at {addr:#x}"
265
+ except ValueError:
266
+ return "ERROR: Invalid address"
267
+ return "ERROR: Missing address"
268
+ elif command == 'watch':
269
+ if len(parts) > 1:
270
+ try:
271
+ addr = int(parts[1], 0)
272
+ access = parts[2] if len(parts) > 2 else 'rw'
273
+ self.cpu.memory.set_protection(addr, access)
274
+ return f"OK: Watchpoint at {addr:#x} for {access}"
275
+ except ValueError:
276
+ return "ERROR: Invalid address"
277
+ return "ERROR: Missing address"
278
+ elif command == 'regs':
279
+ return str(self.cpu.regs.get_all())
280
+ elif command == 'pc':
281
+ return f"PC: {self.cpu.pc:#x}"
282
+ elif command == 'mem':
283
+ if len(parts) > 1:
284
+ try:
285
+ addr = int(parts[1], 0)
286
+ if len(parts) > 2:
287
+ value = int(parts[2], 0)
288
+ self.cpu.memory.write_byte(addr, value)
289
+ return f"OK: mem[{addr:#x}] = {value:#x}"
290
+ value = self.cpu.memory.read_byte(addr)
291
+ return f"mem[{addr:#x}] = {value:#x}"
292
+ except ValueError:
293
+ return "ERROR: Invalid address or value"
294
+ return "ERROR: Missing address"
295
+ elif command == 'history':
296
+ if len(parts) > 1 and parts[1] == 'clear':
297
+ self.execution_history.clear()
298
+ self.history_index = 0
299
+ return "OK: History cleared"
300
+ return f"OK: History size: {len(self.execution_history)}, index: {self.history_index}"
301
+ elif command == 'info':
302
+ if len(parts) > 1:
303
+ if parts[1] == 'break':
304
+ lines = ["Breakpoints:"]
305
+ for bp in self.cpu.breakpoints:
306
+ lines.append(f" {bp:#x}")
307
+ for bp in self.conditional_breakpoints:
308
+ lines.append(f" {bp.address:#x} (cond: {bp.condition})")
309
+ return "\n".join(lines)
310
+ elif parts[1] == 'regs':
311
+ return str(self.cpu.regs.get_all())
312
+ elif parts[1] == 'pc':
313
+ return f"PC: {self.cpu.pc:#x}"
314
+ return "ERROR: Missing info target"
315
+ elif command == 'quit':
316
+ self.running = False
317
+ return "OK: Quitting"
318
+ return f"ERROR: Unknown command: {command}"
319
+
320
+ # ---------------- A4: 驱动式远程调试 ----------------
321
+
322
+ @staticmethod
323
+ def _readline(conn) -> str:
324
+ buf = []
325
+ while True:
326
+ ch = conn.recv(1)
327
+ if not ch:
328
+ return ''
329
+ if ch == b'\n':
330
+ return ''.join(buf)
331
+ if ch != b'\r':
332
+ buf.append(ch.decode('utf-8', 'replace'))
333
+ return ''.join(buf)
334
+
335
+ @staticmethod
336
+ def _send(conn, line: str) -> None:
337
+ conn.sendall((line + '\n').encode('utf-8'))
338
+
339
+ def _burst(self) -> str:
340
+ """continue: 执行到断点/停机/指令上限。返回 'halt' | 'limit' | 'paused'。"""
341
+ cpu = self.cpu
342
+ cpu.running = True
343
+ while cpu.running:
344
+ if cpu.pc < 0 or cpu.pc >= len(cpu.instructions):
345
+ return 'halt'
346
+ if cpu.stats.instruction_count >= cpu.config.max_instructions:
347
+ return 'limit'
348
+ # continue 后的首次断点豁免 (与本地会话一致)
349
+ resume_bp = cpu._resume_bp_pc is not None
350
+ if resume_bp:
351
+ cpu._resume_bp_pc = None
352
+ if not resume_bp:
353
+ if self.check_conditional_breakpoints():
354
+ return 'paused'
355
+ if cpu.pc in cpu.breakpoints:
356
+ cpu.console.print(
357
+ f"{Colors.colorize(f'Breakpoint hit at PC={cpu.pc:#x}', Colors.YELLOW)}")
358
+ return 'paused'
359
+ opcode, args = cpu.instructions[cpu.pc]
360
+ try:
361
+ if not cpu.execute(opcode, args):
362
+ return 'halt'
363
+ except Exception as e:
364
+ cpu.running = False
365
+ cpu.logger.error(f"Execution error: {e}")
366
+ raise
367
+ return 'halt'
368
+
369
+ def drive(self) -> None:
370
+ """单客户端驱动式调试会话 (阻塞; 由 cpu.run 在 --debug-server 时调用)。"""
371
+ cpu = self.cpu
372
+ try:
373
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
374
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
375
+ self.socket.bind(('localhost', self.port))
376
+ self.socket.listen(1)
377
+ cpu.logger.info(f"Debug server listening on port {self.port}")
378
+ conn, addr = self.socket.accept()
379
+ except Exception as e:
380
+ cpu.logger.error(f"Debug server error: {e}")
381
+ return
382
+ self.connected = True
383
+ cpu.logger.info(f"Debug client connected from {addr}")
384
+ try:
385
+ self._send(conn, 'Code CIN remote debug ready '
386
+ '(step/continue/break/delete/watch/regs/mem/pc/'
387
+ 'history/info/quit)')
388
+ halted = False
389
+ while True:
390
+ line = self._readline(conn)
391
+ if line == '':
392
+ break
393
+ cmd = line.strip()
394
+ if not cmd:
395
+ continue
396
+ parts = cmd.split()
397
+ verb = parts[0].lower()
398
+ if verb in ('quit', 'q', 'exit'):
399
+ self._send(conn, 'BYE')
400
+ break
401
+ if halted and verb in ('step', 's', 'continue', 'c', 'run', 'r'):
402
+ self._send(conn, 'ERROR: program halted')
403
+ continue
404
+ if verb in ('step', 's'):
405
+ self.record_state()
406
+ if cpu.step():
407
+ self._send(conn, f"OK pc=0x{cpu.pc:x}")
408
+ else:
409
+ self._send(conn, 'HALTED')
410
+ halted = True
411
+ continue
412
+ if verb in ('continue', 'c', 'run', 'r'):
413
+ self._send(conn, 'OK continuing')
414
+ try:
415
+ res = self._burst()
416
+ except Exception as e:
417
+ self._send(conn, f'ERROR: {e}')
418
+ halted = True
419
+ continue
420
+ if res == 'halt':
421
+ self._send(conn, 'HALTED')
422
+ halted = True
423
+ elif res == 'limit':
424
+ self._send(conn, 'LIMIT')
425
+ halted = True
426
+ else:
427
+ self._send(conn, f"PAUSED pc=0x{cpu.pc:x}")
428
+ continue
429
+ # 其余命令: 断点/查询等 (驱动空闲时执行)
430
+ try:
431
+ resp = self._process_command(cmd.encode('utf-8'))
432
+ except Exception as e:
433
+ resp = f'ERROR: {e}'
434
+ self._send(conn, resp or 'OK')
435
+ finally:
436
+ with contextlib.suppress(Exception):
437
+ conn.close()
438
+ self.connected = False
439
+ with contextlib.suppress(Exception):
440
+ self.socket.close()
441
+ self.running = False
442
+
443
+
444
+ # ==================== 交互式调试会话 ====================
445
+
446
+
447
+ class DebugSession:
448
+ """交互式调试会话 (断点命中 / --step 单步共用同一命令集)。
449
+
450
+ input_fn 可注入 (默认 input), 便于测试与 GUI 接入。
451
+ """
452
+
453
+ def __init__(self, cpu: 'CPU', input_fn=None):
454
+ self.cpu = cpu
455
+ self.input_fn = input_fn or input
456
+ self.console = cpu.console
457
+ self.server: Optional[DebugServer] = None
458
+
459
+ def _ensure_server(self) -> DebugServer:
460
+ """会话共用 CPU 上持久化的 DebugServer (保留条件断点/历史)。"""
461
+ if self.cpu.debug_server is None:
462
+ self.cpu.debug_server = DebugServer(self.cpu)
463
+ self.server = self.cpu.debug_server
464
+ return self.server
465
+
466
+ # ---------------- 打印目标 ----------------
467
+
468
+ def _print_value(self, parts: List[str]) -> None:
469
+ if len(parts) < 2:
470
+ self.console.print(f"{Colors.colorize('Missing argument', Colors.RED)}")
471
+ return
472
+ cpu = self.cpu
473
+ target = parts[1]
474
+ if target.lower().startswith('x') and target[1:].isdigit():
475
+ idx = int(target[1:])
476
+ v = cpu._reg(idx)
477
+ self.console.print(f"{target.upper()} = {v} (0x{v:x})")
478
+ elif target == 'regs':
479
+ cpu.regs.display_registers(console=self.console)
480
+ elif target == 'mem':
481
+ if len(parts) > 2:
482
+ try:
483
+ addr = int(parts[2], 0)
484
+ value = cpu.memory.read_qword(addr)
485
+ self.console.print(f"mem[{addr:#x}] = {value} (0x{value:x})")
486
+ except ValueError:
487
+ self.console.print(f"{Colors.colorize('Invalid address', Colors.RED)}")
488
+ else:
489
+ cpu.memory.display_memory(console=self.console)
490
+ elif target == 'cache':
491
+ self.console.print(f"Cache stats: {cpu.cache.get_stats()}")
492
+ elif target == 'pc':
493
+ self.console.print(f"PC: {cpu.pc:#x}")
494
+ elif target == 'sp':
495
+ self.console.print(f"SP: {cpu.sp:#x}")
496
+ else:
497
+ self.console.print(f"{Colors.colorize('Unknown target', Colors.RED)}")
498
+
499
+ def _set_breakpoint(self, parts: List[str]) -> None:
500
+ cpu = self.cpu
501
+ if len(parts) < 2:
502
+ self.console.print(f"{Colors.colorize('Missing address', Colors.RED)}")
503
+ return
504
+ try:
505
+ addr = int(parts[1], 0)
506
+ except ValueError:
507
+ self.console.print(f"{Colors.colorize('Invalid address', Colors.RED)}")
508
+ return
509
+ if len(parts) > 2:
510
+ condition = ' '.join(parts[2:])
511
+ self._ensure_server().add_conditional_breakpoint(addr, condition)
512
+ self.console.print(
513
+ f"{Colors.colorize(f'Conditional breakpoint set at {addr:#x}: {condition}', Colors.GREEN)}")
514
+ else:
515
+ cpu.add_breakpoint(addr)
516
+ self.console.print(
517
+ f"{Colors.colorize(f'Breakpoint set at {addr:#x}', Colors.GREEN)}")
518
+
519
+ def _set_watchpoint(self, parts: List[str]) -> None:
520
+ cpu = self.cpu
521
+ if len(parts) < 2:
522
+ self.console.print(f"{Colors.colorize('Missing address', Colors.RED)}")
523
+ return
524
+ try:
525
+ addr = int(parts[1], 0)
526
+ access = parts[2] if len(parts) > 2 else 'rw'
527
+ cpu.memory.set_protection(addr, access)
528
+ self.console.print(
529
+ f"{Colors.colorize(f'Watchpoint set at {addr:#x} for {access}', Colors.GREEN)}")
530
+ except ValueError:
531
+ self.console.print(f"{Colors.colorize('Invalid address', Colors.RED)}")
532
+
533
+ def _list_breakpoints(self) -> None:
534
+ cpu = self.cpu
535
+ self.console.print(f"{Colors.colorize('Breakpoints:', Colors.BOLD)}")
536
+ for addr in sorted(cpu.breakpoints):
537
+ self.console.print(f" {addr:#x}")
538
+ for bp in self._ensure_server().conditional_breakpoints:
539
+ self.console.print(f" {bp.address:#x} (cond: {bp.condition}, "
540
+ f"hits: {bp.hit_count})")
541
+
542
+ # ---------------- 命令分发 ----------------
543
+
544
+ def run(self) -> str:
545
+ """断点命中时进入的会话主循环。
546
+
547
+ 返回 'continue' | 'quit' | 'halted'。
548
+ """
549
+ cpu = self.cpu
550
+ cpu.is_debugging = True
551
+ server = self._ensure_server()
552
+ self.console.print(
553
+ f"{Colors.colorize('Debug mode (type help for commands)', Colors.BLUE, True)}")
554
+
555
+ while cpu.is_debugging:
556
+ try:
557
+ raw = self.input_fn(f"{Colors.colorize('dbg>', Colors.YELLOW)} ").strip()
558
+ except (EOFError, KeyboardInterrupt):
559
+ cpu.is_debugging = False
560
+ return 'continue'
561
+ if not raw:
562
+ continue
563
+ parts = raw.split()
564
+ command = parts[0].lower()
565
+
566
+ if command in ('help', 'h'):
567
+ self.console.print(DEBUG_HELP)
568
+ elif command in ('continue', 'c'):
569
+ cpu.is_debugging = False
570
+ # 建议 5: 继续时豁免当前 PC 一次, 避免立即再次命中同一断点
571
+ cpu._resume_bp_pc = cpu.pc
572
+ return 'continue'
573
+ elif command in ('step', 's'):
574
+ server.record_state()
575
+ if not cpu.step():
576
+ cpu.is_debugging = False
577
+ return 'halted'
578
+ display_state(cpu, "Step Execution")
579
+ elif command in ('reverse',):
580
+ if server.reverse_step():
581
+ self.console.print(f"{Colors.colorize('Reversed one step', Colors.GREEN)}")
582
+ display_state(cpu, "Reverse Step")
583
+ else:
584
+ self.console.print(f"{Colors.colorize('No history available', Colors.RED)}")
585
+ elif command == 'forward':
586
+ if server.forward_step():
587
+ self.console.print(f"{Colors.colorize('Forward one step', Colors.GREEN)}")
588
+ display_state(cpu, "Forward Step")
589
+ else:
590
+ self.console.print(f"{Colors.colorize('No forward history', Colors.RED)}")
591
+ elif command in ('print', 'p'):
592
+ self._print_value(parts)
593
+ elif command == 'break':
594
+ self._set_breakpoint(parts)
595
+ elif command == 'delete':
596
+ if len(parts) > 1:
597
+ try:
598
+ cpu.remove_breakpoint(int(parts[1]))
599
+ except ValueError:
600
+ self.console.print(f"{Colors.colorize('Invalid address', Colors.RED)}")
601
+ elif command == 'watch':
602
+ self._set_watchpoint(parts)
603
+ elif command in ('list', 'info'):
604
+ self._list_breakpoints()
605
+ elif command in ('quit', 'q'):
606
+ cpu.is_debugging = False
607
+ cpu.running = False # 优雅退出运行 (原 sys.exit(0) 的替代)
608
+ return 'quit'
609
+ else:
610
+ self.console.print(f"{Colors.colorize('Unknown command', Colors.RED)}")
611
+ return 'continue'
612
+
613
+ def run_step_mode(self) -> str:
614
+ """--step 单步模式: 每次执行前渲染状态并等待命令。
615
+
616
+ 返回 'next' | 'run' | 'quit' | 'halted'。
617
+ - 'next': 已执行一条指令, 由外层继续 (通常不再使用, 见下)
618
+ - 'run' : 用户选择 continue, 退出单步、自由运行
619
+ """
620
+ cpu = self.cpu
621
+ server = self._ensure_server()
622
+ while cpu.running:
623
+ if cpu.pc < 0 or cpu.pc >= len(cpu.instructions):
624
+ return 'halted'
625
+ opcode, args = cpu.instructions[cpu.pc]
626
+ display_state(cpu, "Step Execution", opcode, args)
627
+ try:
628
+ raw = self.input_fn(f"{Colors.colorize('step>', Colors.YELLOW)} ").strip()
629
+ except (EOFError, KeyboardInterrupt):
630
+ return 'quit'
631
+ if not raw or raw.lower() in ('s', 'step'):
632
+ server.record_state()
633
+ if not cpu.step():
634
+ return 'halted'
635
+ continue
636
+ parts = raw.split()
637
+ command = parts[0].lower()
638
+ if command in ('c', 'continue', 'r', 'run'):
639
+ return 'run'
640
+ if command in ('q', 'quit'):
641
+ cpu.running = False
642
+ return 'quit'
643
+ if command in ('p', 'print'):
644
+ self._print_value(parts)
645
+ elif command == 'b':
646
+ self._set_breakpoint(parts)
647
+ elif command == 'd':
648
+ if len(parts) > 1:
649
+ try:
650
+ cpu.remove_breakpoint(int(parts[1]))
651
+ except ValueError:
652
+ self.console.print(f"{Colors.colorize('Invalid address', Colors.RED)}")
653
+ elif command in ('list', 'info', 'l'):
654
+ self._list_breakpoints()
655
+ elif command in ('help', 'h', '?'):
656
+ self.console.print(DEBUG_HELP)
657
+ else:
658
+ self.console.print(f"{Colors.colorize('Unknown command', Colors.RED)}")
659
+ return 'quit'