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.
- codecin/__init__.py +27 -0
- codecin/aot.py +229 -0
- codecin/assembler.py +548 -0
- codecin/cache.py +71 -0
- codecin/cin.py +2558 -0
- codecin/cli.py +282 -0
- codecin/config.py +58 -0
- codecin/console.py +183 -0
- codecin/cpu.py +1654 -0
- codecin/crom.py +222 -0
- codecin/debugger.py +659 -0
- codecin/disasm.py +86 -0
- codecin/errors.py +58 -0
- codecin/isa.py +457 -0
- codecin/jit.py +270 -0
- codecin/libcodecin_native.so +0 -0
- codecin/logger.py +145 -0
- codecin/memory.py +348 -0
- codecin/native/aot/aot.go +70 -0
- codecin/native/aot/build.go +219 -0
- codecin/native/aot/stub_main.go.txt +22 -0
- codecin/native/build.ps1 +33 -0
- codecin/native/build.sh +48 -0
- codecin/native/cmd/codecin/main.go +287 -0
- codecin/native/compiler/codegen.go +1938 -0
- codecin/native/compiler/compile_test.go +164 -0
- codecin/native/compiler/parser.go +1071 -0
- codecin/native/compiler/syscalls.go +86 -0
- codecin/native/compiler/tokenizer.go +336 -0
- codecin/native/compiler/types.go +213 -0
- codecin/native/engine/audio.go +152 -0
- codecin/native/engine/audio_other.go +46 -0
- codecin/native/engine/audio_windows.go +43 -0
- codecin/native/engine/canvas.go +271 -0
- codecin/native/engine/crom.go +86 -0
- codecin/native/engine/encode.go +93 -0
- codecin/native/engine/engine_test.go +198 -0
- codecin/native/engine/isa_gen.go +445 -0
- codecin/native/engine/system.go +190 -0
- codecin/native/engine/termux.go +113 -0
- codecin/native/engine/version_gen.go +7 -0
- codecin/native/engine/vm.go +1157 -0
- codecin/native/go.mod +3 -0
- codecin/native/ir/ir.go +48 -0
- codecin/native/main.go +225 -0
- codecin/native.py +336 -0
- codecin/registers.py +130 -0
- codecin/stats.py +235 -0
- codecin-5.4.2.dist-info/METADATA +985 -0
- codecin-5.4.2.dist-info/RECORD +54 -0
- codecin-5.4.2.dist-info/WHEEL +5 -0
- codecin-5.4.2.dist-info/entry_points.txt +2 -0
- codecin-5.4.2.dist-info/licenses/LICENSE +7 -0
- codecin-5.4.2.dist-info/top_level.txt +1 -0
codecin/cpu.py
ADDED
|
@@ -0,0 +1,1654 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
import struct
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
8
|
+
|
|
9
|
+
from .assembler import Assembler
|
|
10
|
+
from .cache import Cache
|
|
11
|
+
from .config import Config
|
|
12
|
+
from .console import Colors, Console, Panel
|
|
13
|
+
from .errors import CPUSimulatorError, ExecutionError
|
|
14
|
+
from .isa import Constants, Syscall
|
|
15
|
+
from .logger import Logger
|
|
16
|
+
from .memory import FastMemory
|
|
17
|
+
from .registers import RegisterFile, VectorRegisterFile
|
|
18
|
+
from .stats import Statistics
|
|
19
|
+
|
|
20
|
+
Operand = Tuple[Any, ...]
|
|
21
|
+
Instruction = Tuple[str, List[Operand]]
|
|
22
|
+
|
|
23
|
+
MASK64 = 0xFFFFFFFFFFFFFFFF
|
|
24
|
+
MASK32 = 0xFFFFFFFF
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _f_to_bits(f: float) -> int:
|
|
28
|
+
return struct.unpack('<Q', struct.pack('<d', f))[0]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _bits_to_f(b: int) -> float:
|
|
32
|
+
return struct.unpack('<d', struct.pack('<Q', b & MASK64))[0]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _format_float(v: float) -> str:
|
|
36
|
+
if math.isnan(v):
|
|
37
|
+
return "NaN"
|
|
38
|
+
if math.isinf(v):
|
|
39
|
+
return "+Inf" if v > 0 else "-Inf"
|
|
40
|
+
s = repr(v)
|
|
41
|
+
if 'e' not in s and 'E' not in s and '.' in s:
|
|
42
|
+
s = s.rstrip('0').rstrip('.')
|
|
43
|
+
return s or "0"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CPU:
|
|
47
|
+
# 建议 7: 指令处理器自动注册 (方法名 _op_XXX -> 指令 XXX)。
|
|
48
|
+
# 别名: 无事件模型的 ARM64 等待/事件指令, 语义上等价 NOP (预留扩展点)。
|
|
49
|
+
_OP_HANDLER_PREFIX = '_op_'
|
|
50
|
+
_OP_ALIASES = {
|
|
51
|
+
'WFE': '_op_nop', # Wait For Event: 无中断模型, 视作 NOP
|
|
52
|
+
'WFI': '_op_nop', # Wait For Interrupt: 无中断模型, 视作 NOP
|
|
53
|
+
'SEV': '_op_nop', # Send Event: 无多核事件模型, 视作 NOP
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def __init__(self, config: Config, filename: Optional[str] = None,
|
|
57
|
+
crom_file: Optional[str] = None,
|
|
58
|
+
from_bin: bool = False,
|
|
59
|
+
console: Optional[Console] = None):
|
|
60
|
+
|
|
61
|
+
self.console = console or Console()
|
|
62
|
+
self.config = config
|
|
63
|
+
self.filename = filename
|
|
64
|
+
|
|
65
|
+
# debug 模式强制 DEBUG 级别 (超详细日志)
|
|
66
|
+
if config.debug_mode and config.log_level.upper() != 'DEBUG':
|
|
67
|
+
config.log_level = 'DEBUG'
|
|
68
|
+
self.logger = Logger(self.console, config.log_level)
|
|
69
|
+
if config.log_file:
|
|
70
|
+
self.logger.set_log_file(config.log_file)
|
|
71
|
+
# 逐指令超详细追踪开关 (DEBUG 级别启用)
|
|
72
|
+
self._trace = self.logger.is_debug
|
|
73
|
+
|
|
74
|
+
self.memory = FastMemory(config.mem_size)
|
|
75
|
+
if config.mmu:
|
|
76
|
+
from .memory import Mmu
|
|
77
|
+
self.memory.attach_mmu(Mmu(config.mem_size))
|
|
78
|
+
self.cache = Cache(config.cache_size, config.cache_assoc)
|
|
79
|
+
# 内存访问日志 (DEBUG 级别生效)
|
|
80
|
+
self.memory.attach_logger(self.logger)
|
|
81
|
+
|
|
82
|
+
self.regs = RegisterFile(self.console)
|
|
83
|
+
self.vec_regs = VectorRegisterFile()
|
|
84
|
+
|
|
85
|
+
self.pstate: Dict[str, bool] = {'N': False, 'Z': False, 'C': False, 'V': False}
|
|
86
|
+
self.pc = 0
|
|
87
|
+
self.sp = (config.mem_size - Constants.STACK_SLOT) & ~0x7
|
|
88
|
+
self.heap_ptr = config.mem_size // 2
|
|
89
|
+
|
|
90
|
+
self.logger.dump("CPU 初始化", {
|
|
91
|
+
'memory': f"0x{config.mem_size:x} bytes",
|
|
92
|
+
'cache': f"{config.cache_size} lines x {config.cache_assoc}-way",
|
|
93
|
+
'sp_init': f"0x{self.sp:x}",
|
|
94
|
+
'heap_base': f"0x{self.heap_ptr:x}",
|
|
95
|
+
'native': config.use_native,
|
|
96
|
+
'jit': config.enable_jit,
|
|
97
|
+
'log_level': config.log_level,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
self.instructions: List[Instruction] = []
|
|
101
|
+
self.labels: Dict[str, int] = {}
|
|
102
|
+
self.data_labels: Dict[str, int] = {}
|
|
103
|
+
self.entry_pc = 0
|
|
104
|
+
|
|
105
|
+
self.stats = Statistics()
|
|
106
|
+
self.jit = None
|
|
107
|
+
if config.enable_jit and not config.debug_mode and not config.step_mode:
|
|
108
|
+
try:
|
|
109
|
+
from .jit import JITCompiler
|
|
110
|
+
self.jit = JITCompiler(logger=self.logger)
|
|
111
|
+
self.logger.info("JIT compiler enabled")
|
|
112
|
+
except Exception as e:
|
|
113
|
+
self.logger.warning(f"JIT unavailable: {e}")
|
|
114
|
+
self.running = False
|
|
115
|
+
self.is_debugging = False
|
|
116
|
+
self.breakpoints: set = set()
|
|
117
|
+
self.debug_server = None
|
|
118
|
+
# 建议 5: 'continue' 后豁免一次当前 PC 的断点命中 (防止原地重入)
|
|
119
|
+
self._resume_bp_pc: Optional[int] = None
|
|
120
|
+
|
|
121
|
+
self.input_buffer: str = ""
|
|
122
|
+
self._input_pos = 0
|
|
123
|
+
self.output_buffer: List[str] = []
|
|
124
|
+
self._capture_output = False
|
|
125
|
+
self.native_engine = None
|
|
126
|
+
self.native_used = False
|
|
127
|
+
# 执行期是否发生过错误 (供 CLI 决定退出码; 旧实现任何运行时错误
|
|
128
|
+
# 都只记日志, 进程仍以 0 退出)
|
|
129
|
+
self.execution_failed = False
|
|
130
|
+
|
|
131
|
+
self._sys_buffers = [bytearray(64) for _ in range(8)]
|
|
132
|
+
self._sys_buf_idx = 0
|
|
133
|
+
self._rand = random.Random(
|
|
134
|
+
config.seed if config.seed is not None else None)
|
|
135
|
+
|
|
136
|
+
self._init_dispatch()
|
|
137
|
+
|
|
138
|
+
if filename:
|
|
139
|
+
self.load_program(filename, crom_file=crom_file, from_bin=from_bin)
|
|
140
|
+
|
|
141
|
+
def load_program(self, filename: str, crom_file: Optional[str] = None,
|
|
142
|
+
from_bin: bool = False) -> None:
|
|
143
|
+
if not os.path.exists(filename):
|
|
144
|
+
raise CPUSimulatorError(f"File '{filename}' not found")
|
|
145
|
+
|
|
146
|
+
ext = os.path.splitext(filename)[1].lower()
|
|
147
|
+
|
|
148
|
+
if ext == '.cin':
|
|
149
|
+
from .cin import CINCompiler
|
|
150
|
+
compiler = CINCompiler(self.console, logger=self.logger)
|
|
151
|
+
result = compiler.compile(filename,
|
|
152
|
+
bounds_check=self.config.bounds_check)
|
|
153
|
+
self.instructions = result.instructions
|
|
154
|
+
self.labels = result.labels
|
|
155
|
+
self.data_labels = result.data_labels
|
|
156
|
+
for addr, data in result.data_writes:
|
|
157
|
+
self.memory.write_block(addr, data)
|
|
158
|
+
# pc 0 为 bootstrap (CALL main; HALT), main 标签为函数体入口
|
|
159
|
+
self.entry_pc = 0
|
|
160
|
+
self.pc = 0
|
|
161
|
+
self.logger.info(f"CIN compiled: {len(self.instructions)} instructions")
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
if from_bin or ext == '.bin':
|
|
165
|
+
from . import crom as crom_mod
|
|
166
|
+
crom_mod.load_bin(self, filename)
|
|
167
|
+
self.logger.info(f"Binary loaded: {len(self.instructions)} instructions")
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# .pl / .asm
|
|
171
|
+
if crom_file is None:
|
|
172
|
+
base = os.path.splitext(filename)[0]
|
|
173
|
+
candidate = base + '.crom'
|
|
174
|
+
if os.path.exists(candidate):
|
|
175
|
+
crom_file = candidate
|
|
176
|
+
if crom_file and os.path.exists(crom_file):
|
|
177
|
+
from . import crom as crom_mod
|
|
178
|
+
crom_mod.load_crom(self.memory, crom_file, self.logger,
|
|
179
|
+
enable_mmu=self.config.mmu)
|
|
180
|
+
|
|
181
|
+
asm = Assembler(self.memory, self.console, strict=self.config.strict_mode,
|
|
182
|
+
logger=self.logger)
|
|
183
|
+
self.instructions, self.labels, self.data_labels = asm.assemble_file(filename)
|
|
184
|
+
self.entry_pc = self.labels.get('main', 0)
|
|
185
|
+
self.pc = self.entry_pc
|
|
186
|
+
self.cache.warmup(self.instructions)
|
|
187
|
+
self.logger.info(f"Assembly successful: {len(self.instructions)} instructions")
|
|
188
|
+
if self.logger.is_debug:
|
|
189
|
+
self.logger.dump("汇编标签表", {name: f"0x{pc:x}"
|
|
190
|
+
for name, pc in
|
|
191
|
+
sorted(self.labels.items())})
|
|
192
|
+
def _init_dispatch(self) -> None:
|
|
193
|
+
"""自动注册指令处理器 (建议 7)。
|
|
194
|
+
|
|
195
|
+
约定: CPU 上任意名为 ``_op_XXX`` 的可调用方法即指令 ``XXX`` 的处理器,
|
|
196
|
+
不再手工维护 112 行映射表; 别名 (WFE/WFI/SEV -> NOP 语义) 见 _OP_ALIASES。
|
|
197
|
+
dispatch 键集合由 test_dispatch_complete 与 Opcode 枚举逐一校验。
|
|
198
|
+
"""
|
|
199
|
+
t: Dict[str, Any] = {}
|
|
200
|
+
prefix = self._OP_HANDLER_PREFIX
|
|
201
|
+
for name in dir(self):
|
|
202
|
+
if not name.startswith(prefix):
|
|
203
|
+
continue
|
|
204
|
+
opcode_name = name[len(prefix):].upper()
|
|
205
|
+
handler = getattr(self, name)
|
|
206
|
+
if opcode_name and callable(handler):
|
|
207
|
+
t.setdefault(opcode_name, handler)
|
|
208
|
+
for opcode_name, handler_name in self._OP_ALIASES.items():
|
|
209
|
+
t[opcode_name] = getattr(self, handler_name)
|
|
210
|
+
self._dispatch = t
|
|
211
|
+
|
|
212
|
+
# ==================== 操作数辅助 ====================
|
|
213
|
+
|
|
214
|
+
def _reg(self, n: int) -> int:
|
|
215
|
+
if n == Constants.SP_REG:
|
|
216
|
+
return self.sp & MASK64
|
|
217
|
+
return self.regs.read(n)
|
|
218
|
+
|
|
219
|
+
def _set_reg(self, n: int, v: int) -> None:
|
|
220
|
+
v &= MASK64
|
|
221
|
+
if self._trace:
|
|
222
|
+
old = self._reg(n)
|
|
223
|
+
if old != v:
|
|
224
|
+
sv = v if v < (1 << 63) else v - (1 << 64)
|
|
225
|
+
self.logger.trace(f" R[{n}] {self._fmt_reg(n, old)} -> "
|
|
226
|
+
f"0x{v:016x} ({sv})")
|
|
227
|
+
if n == Constants.SP_REG:
|
|
228
|
+
self.sp = v
|
|
229
|
+
else:
|
|
230
|
+
self.regs.write(n, v)
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def _fmt_reg(n: int, v: int) -> str:
|
|
234
|
+
return f"0x{v:016x}"
|
|
235
|
+
|
|
236
|
+
def _fmt_op_trace(self, op: Operand) -> str:
|
|
237
|
+
"""格式化操作数并附带当前值 (超详细追踪)。"""
|
|
238
|
+
kind = op[0]
|
|
239
|
+
try:
|
|
240
|
+
if kind == 'reg':
|
|
241
|
+
n = op[1]
|
|
242
|
+
v = self._reg(n)
|
|
243
|
+
return f"X{n}=0x{v:x}({v if v < (1 << 63) else v - (1 << 64)})"
|
|
244
|
+
if kind == 'imm':
|
|
245
|
+
return f"#{op[1]}"
|
|
246
|
+
if kind == 'mem':
|
|
247
|
+
addr = self._mem_addr(op)
|
|
248
|
+
return (f"[{op[1] if op[1] >= 0 else 'abs'}"
|
|
249
|
+
f"{op[2]:+d}]@0x{addr:x}"
|
|
250
|
+
f"=0x{self.memory.read_qword(addr):x}")
|
|
251
|
+
if kind == 'cond':
|
|
252
|
+
return (f"{op[1]}="
|
|
253
|
+
f"{int(self._condition(op[1]))}"
|
|
254
|
+
f"{{N={int(self.pstate['N'])} Z={int(self.pstate['Z'])} "
|
|
255
|
+
f"C={int(self.pstate['C'])} V={int(self.pstate['V'])}}}")
|
|
256
|
+
if kind == 'label':
|
|
257
|
+
return f"{op[1]}->0x{self.labels.get(op[1], -1):x}"
|
|
258
|
+
if kind == 'str':
|
|
259
|
+
return f'"@{op[1]}"'
|
|
260
|
+
if kind == 'float':
|
|
261
|
+
return f"#{op[1]}f"
|
|
262
|
+
except Exception:
|
|
263
|
+
pass
|
|
264
|
+
return self._fmt_operand(op)
|
|
265
|
+
|
|
266
|
+
def _trace_flags(self) -> str:
|
|
267
|
+
p = self.pstate
|
|
268
|
+
return (f"N={int(p['N'])} Z={int(p['Z'])} "
|
|
269
|
+
f"C={int(p['C'])} V={int(p['V'])}")
|
|
270
|
+
|
|
271
|
+
def _trace_cache(self, addr: int, hit: bool, kind: str) -> None:
|
|
272
|
+
cs = self.cache.get_stats()
|
|
273
|
+
self.logger.trace(
|
|
274
|
+
f" CACHE {'HIT ' if hit else 'MISS'} {kind} @0x{addr:x} "
|
|
275
|
+
f"(hit_rate={cs['hit_rate'] * 100:.1f}%)")
|
|
276
|
+
|
|
277
|
+
def _val(self, op: Operand) -> Union[int, float]:
|
|
278
|
+
kind = op[0]
|
|
279
|
+
if kind == 'reg':
|
|
280
|
+
return self._reg(op[1])
|
|
281
|
+
if kind == 'imm':
|
|
282
|
+
return op[1] & MASK64
|
|
283
|
+
if kind == 'vec':
|
|
284
|
+
return int(self.vec_regs.read_scalar(op[1]))
|
|
285
|
+
if kind == 'veclane':
|
|
286
|
+
return int(self.vec_regs.read_scalar(op[1], op[2]))
|
|
287
|
+
if kind == 'float':
|
|
288
|
+
return op[1]
|
|
289
|
+
if kind == 'mem':
|
|
290
|
+
addr = self._mem_addr(op)
|
|
291
|
+
return self.memory.read_qword(addr)
|
|
292
|
+
if kind == 'cond':
|
|
293
|
+
return 1 if self._condition(op[1]) else 0
|
|
294
|
+
if kind == 'str':
|
|
295
|
+
return op[1]
|
|
296
|
+
if kind == 'label':
|
|
297
|
+
target = self.labels.get(op[1])
|
|
298
|
+
if target is None:
|
|
299
|
+
raise ExecutionError(f"Undefined label: {op[1]}")
|
|
300
|
+
return target
|
|
301
|
+
raise ExecutionError(f"Unsupported operand kind: {kind}")
|
|
302
|
+
|
|
303
|
+
def _mem_addr(self, op: Operand) -> int:
|
|
304
|
+
# ('mem', base, off): base == -1 表示绝对地址
|
|
305
|
+
base = op[1] if len(op) > 1 else -1
|
|
306
|
+
off = op[2] if len(op) > 2 else 0
|
|
307
|
+
if isinstance(off, tuple):
|
|
308
|
+
off = self._val(off)
|
|
309
|
+
if base >= 0:
|
|
310
|
+
return (self._reg(base) + off) & MASK64
|
|
311
|
+
return off & MASK64
|
|
312
|
+
|
|
313
|
+
def _condition(self, cond: str) -> bool:
|
|
314
|
+
n, z, c, v = (self.pstate['N'], self.pstate['Z'],
|
|
315
|
+
self.pstate['C'], self.pstate['V'])
|
|
316
|
+
table = {
|
|
317
|
+
'EQ': z, 'NE': not z,
|
|
318
|
+
'CS': c, 'CC': not c,
|
|
319
|
+
'MI': n, 'PL': not n,
|
|
320
|
+
'VS': v, 'VC': not v,
|
|
321
|
+
'HI': c and not z, 'LS': not c or z,
|
|
322
|
+
'GE': n == v, 'LT': n != v,
|
|
323
|
+
'GT': not z and (n == v), 'LE': z or (n != v),
|
|
324
|
+
'AL': True, 'NV': False,
|
|
325
|
+
}
|
|
326
|
+
return table.get(cond.upper(), False)
|
|
327
|
+
|
|
328
|
+
def _set_flags_sub(self, a: int, b: int, result: int) -> None:
|
|
329
|
+
a &= MASK64
|
|
330
|
+
b &= MASK64
|
|
331
|
+
result &= MASK64
|
|
332
|
+
sa = a if a < (1 << 63) else a - (1 << 64)
|
|
333
|
+
sb = b if b < (1 << 63) else b - (1 << 64)
|
|
334
|
+
sr = sa - sb
|
|
335
|
+
old = dict(self.pstate)
|
|
336
|
+
self.pstate['Z'] = (result == 0)
|
|
337
|
+
self.pstate['N'] = bool(result & (1 << 63))
|
|
338
|
+
self.pstate['C'] = a >= b # 无借位
|
|
339
|
+
self.pstate['V'] = (sr > (1 << 63) - 1) or (sr < -(1 << 63))
|
|
340
|
+
if self._trace and old != self.pstate:
|
|
341
|
+
self.logger.trace(f" FLAGS <- {self._trace_flags()} "
|
|
342
|
+
f"(a=0x{a:x} b=0x{b:x})")
|
|
343
|
+
|
|
344
|
+
def _set_flags_add(self, a: int, b: int, result: int) -> None:
|
|
345
|
+
a &= MASK64
|
|
346
|
+
b &= MASK64
|
|
347
|
+
result &= MASK64
|
|
348
|
+
sa = a if a < (1 << 63) else a - (1 << 64)
|
|
349
|
+
sb = b if b < (1 << 63) else b - (1 << 64)
|
|
350
|
+
ss = sa + sb
|
|
351
|
+
old = dict(self.pstate)
|
|
352
|
+
self.pstate['Z'] = (result == 0)
|
|
353
|
+
self.pstate['N'] = bool(result & (1 << 63))
|
|
354
|
+
self.pstate['C'] = (a + b) > MASK64
|
|
355
|
+
self.pstate['V'] = (ss > (1 << 63) - 1) or (ss < -(1 << 63))
|
|
356
|
+
if self._trace and old != self.pstate:
|
|
357
|
+
self.logger.trace(f" FLAGS <- {self._trace_flags()} "
|
|
358
|
+
f"(a=0x{a:x} b=0x{b:x})")
|
|
359
|
+
|
|
360
|
+
# ==================== 栈 ====================
|
|
361
|
+
|
|
362
|
+
def _push(self, value: int) -> None:
|
|
363
|
+
self.sp = (self.sp - Constants.STACK_SLOT) & MASK64
|
|
364
|
+
if self.sp < self.heap_ptr + 4096:
|
|
365
|
+
raise ExecutionError("Stack overflow (collides with heap)")
|
|
366
|
+
self.memory.write_qword(self.sp, value & MASK64)
|
|
367
|
+
if self._trace:
|
|
368
|
+
self.logger.trace(f" PUSH @0x{self.sp:x} <- "
|
|
369
|
+
f"0x{value & MASK64:016x}")
|
|
370
|
+
|
|
371
|
+
def _pop(self) -> int:
|
|
372
|
+
if self.sp >= len(self.memory) - Constants.STACK_SLOT:
|
|
373
|
+
raise ExecutionError("Stack underflow")
|
|
374
|
+
value = self.memory.read_qword(self.sp)
|
|
375
|
+
self.sp = (self.sp + Constants.STACK_SLOT) & MASK64
|
|
376
|
+
if self._trace:
|
|
377
|
+
self.logger.trace(f" POP @0x{self.sp - 8:x} -> "
|
|
378
|
+
f"0x{value:016x}")
|
|
379
|
+
return value
|
|
380
|
+
|
|
381
|
+
def _check_stack(self) -> None:
|
|
382
|
+
if self.sp < self.heap_ptr + 4096:
|
|
383
|
+
raise ExecutionError("Stack overflow (collides with heap)")
|
|
384
|
+
|
|
385
|
+
# ==================== I/O ====================
|
|
386
|
+
|
|
387
|
+
def _emit_text(self, text: str) -> None:
|
|
388
|
+
if self._capture_output:
|
|
389
|
+
self.output_buffer.append(text)
|
|
390
|
+
else:
|
|
391
|
+
sys.stdout.write(text)
|
|
392
|
+
sys.stdout.flush()
|
|
393
|
+
|
|
394
|
+
def _read_input_int(self) -> int:
|
|
395
|
+
if self._input_pos < len(self.input_buffer):
|
|
396
|
+
end = self.input_buffer.find('\n', self._input_pos)
|
|
397
|
+
if end < 0:
|
|
398
|
+
end = len(self.input_buffer)
|
|
399
|
+
line = self.input_buffer[self._input_pos:end].strip()
|
|
400
|
+
self._input_pos = end + 1
|
|
401
|
+
try:
|
|
402
|
+
return int(line)
|
|
403
|
+
except ValueError:
|
|
404
|
+
return 0
|
|
405
|
+
try:
|
|
406
|
+
return int(input())
|
|
407
|
+
except (ValueError, EOFError):
|
|
408
|
+
return 0
|
|
409
|
+
|
|
410
|
+
# ==================== 基础指令 ====================
|
|
411
|
+
|
|
412
|
+
def _op_mov(self, args):
|
|
413
|
+
self._set_reg(args[0][1], self._val(args[1]))
|
|
414
|
+
return True
|
|
415
|
+
|
|
416
|
+
def _op_load(self, args):
|
|
417
|
+
rd = args[0][1]
|
|
418
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
419
|
+
self.stats.record_memory_read()
|
|
420
|
+
hit = self.cache.read(addr)
|
|
421
|
+
self.stats.record_cache(hit)
|
|
422
|
+
if self._trace:
|
|
423
|
+
self._trace_cache(addr, hit, 'R')
|
|
424
|
+
self._set_reg(rd, self.memory.read_dword(addr))
|
|
425
|
+
return True
|
|
426
|
+
|
|
427
|
+
def _op_store(self, args):
|
|
428
|
+
rs = args[0][1]
|
|
429
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
430
|
+
self.stats.record_memory_write()
|
|
431
|
+
hit = self.cache.write(addr)
|
|
432
|
+
self.stats.record_cache(hit)
|
|
433
|
+
if self._trace:
|
|
434
|
+
self._trace_cache(addr, hit, 'W')
|
|
435
|
+
self.memory.write_dword(addr, self._reg(rs))
|
|
436
|
+
return True
|
|
437
|
+
|
|
438
|
+
def _op_add(self, args):
|
|
439
|
+
rd = args[0][1]
|
|
440
|
+
self._set_reg(rd, self._reg(rd) + self._val(args[1]))
|
|
441
|
+
return True
|
|
442
|
+
|
|
443
|
+
def _op_sub(self, args):
|
|
444
|
+
rd = args[0][1]
|
|
445
|
+
self._set_reg(rd, self._reg(rd) - self._val(args[1]))
|
|
446
|
+
return True
|
|
447
|
+
|
|
448
|
+
def _op_mul(self, args):
|
|
449
|
+
rd = args[0][1]
|
|
450
|
+
self._set_reg(rd, self._reg(rd) * self._val(args[1]))
|
|
451
|
+
return True
|
|
452
|
+
|
|
453
|
+
def _op_div(self, args):
|
|
454
|
+
rd = args[0][1]
|
|
455
|
+
d = self._val(args[1])
|
|
456
|
+
if d == 0:
|
|
457
|
+
raise ExecutionError("Division by zero")
|
|
458
|
+
a = self._reg(rd)
|
|
459
|
+
self._set_reg(rd, self._sdiv(a, d))
|
|
460
|
+
return True
|
|
461
|
+
|
|
462
|
+
@staticmethod
|
|
463
|
+
def _sdiv(a: int, b: int) -> int:
|
|
464
|
+
"""有符号 64 位除法。"""
|
|
465
|
+
sa = a if a < (1 << 63) else a - (1 << 64)
|
|
466
|
+
sb = b if b < (1 << 63) else b - (1 << 64)
|
|
467
|
+
q = abs(sa) // abs(sb)
|
|
468
|
+
if (sa < 0) != (sb < 0):
|
|
469
|
+
q = -q
|
|
470
|
+
return q & MASK64
|
|
471
|
+
|
|
472
|
+
def _op_and(self, args):
|
|
473
|
+
rd = args[0][1]
|
|
474
|
+
self._set_reg(rd, self._reg(rd) & self._val(args[1]))
|
|
475
|
+
return True
|
|
476
|
+
|
|
477
|
+
def _op_or(self, args):
|
|
478
|
+
rd = args[0][1]
|
|
479
|
+
self._set_reg(rd, self._reg(rd) | self._val(args[1]))
|
|
480
|
+
return True
|
|
481
|
+
|
|
482
|
+
def _op_xor(self, args):
|
|
483
|
+
rd = args[0][1]
|
|
484
|
+
self._set_reg(rd, self._reg(rd) ^ self._val(args[1]))
|
|
485
|
+
return True
|
|
486
|
+
|
|
487
|
+
def _op_shl(self, args):
|
|
488
|
+
rd = args[0][1]
|
|
489
|
+
self._set_reg(rd, self._reg(rd) << (self._val(args[1]) & 63))
|
|
490
|
+
return True
|
|
491
|
+
|
|
492
|
+
def _op_shr(self, args):
|
|
493
|
+
rd = args[0][1]
|
|
494
|
+
self._set_reg(rd, (self._reg(rd) & MASK64) >> (self._val(args[1]) & 63))
|
|
495
|
+
return True
|
|
496
|
+
|
|
497
|
+
def _op_inc(self, args):
|
|
498
|
+
rd = args[0][1]
|
|
499
|
+
self._set_reg(rd, self._reg(rd) + 1)
|
|
500
|
+
return True
|
|
501
|
+
|
|
502
|
+
def _op_dec(self, args):
|
|
503
|
+
rd = args[0][1]
|
|
504
|
+
self._set_reg(rd, self._reg(rd) - 1)
|
|
505
|
+
return True
|
|
506
|
+
|
|
507
|
+
def _op_cmp(self, args):
|
|
508
|
+
a = self._val(args[0])
|
|
509
|
+
b = self._val(args[1])
|
|
510
|
+
self._set_flags_sub(a, b, (a - b) & MASK64)
|
|
511
|
+
return True
|
|
512
|
+
|
|
513
|
+
def _op_jmp(self, args):
|
|
514
|
+
self.pc = self._val(args[0])
|
|
515
|
+
return True
|
|
516
|
+
|
|
517
|
+
def _op_jz(self, args):
|
|
518
|
+
if self.pstate['Z']:
|
|
519
|
+
self.pc = self._val(args[0])
|
|
520
|
+
return True
|
|
521
|
+
|
|
522
|
+
def _op_jnz(self, args):
|
|
523
|
+
if not self.pstate['Z']:
|
|
524
|
+
self.pc = self._val(args[0])
|
|
525
|
+
return True
|
|
526
|
+
|
|
527
|
+
def _op_je(self, args):
|
|
528
|
+
if self.pstate['Z']:
|
|
529
|
+
self.pc = self._val(args[0])
|
|
530
|
+
return True
|
|
531
|
+
|
|
532
|
+
def _op_jl(self, args):
|
|
533
|
+
if self.pstate['N'] != self.pstate['V']:
|
|
534
|
+
self.pc = self._val(args[0])
|
|
535
|
+
return True
|
|
536
|
+
|
|
537
|
+
def _op_jg(self, args):
|
|
538
|
+
if not self.pstate['Z'] and self.pstate['N'] == self.pstate['V']:
|
|
539
|
+
self.pc = self._val(args[0])
|
|
540
|
+
return True
|
|
541
|
+
|
|
542
|
+
def _op_push(self, args):
|
|
543
|
+
self._check_stack()
|
|
544
|
+
self._push(self._val(args[0]))
|
|
545
|
+
return True
|
|
546
|
+
|
|
547
|
+
def _op_pop(self, args):
|
|
548
|
+
self._set_reg(args[0][1], self._pop())
|
|
549
|
+
return True
|
|
550
|
+
|
|
551
|
+
def _op_call(self, args):
|
|
552
|
+
self._check_stack()
|
|
553
|
+
self._push(self.pc)
|
|
554
|
+
self.pc = self._val(args[0])
|
|
555
|
+
return True
|
|
556
|
+
|
|
557
|
+
def _op_ret(self, args):
|
|
558
|
+
self.pc = self._pop()
|
|
559
|
+
return True
|
|
560
|
+
|
|
561
|
+
def _op_in(self, args):
|
|
562
|
+
if self.config.allow_io:
|
|
563
|
+
self._set_reg(args[0][1], self._read_input_int())
|
|
564
|
+
return True
|
|
565
|
+
|
|
566
|
+
def _op_out(self, args):
|
|
567
|
+
if not self.config.allow_io:
|
|
568
|
+
return True
|
|
569
|
+
op = args[0]
|
|
570
|
+
if op[0] == 'str':
|
|
571
|
+
text = self.memory.read_string(op[1])
|
|
572
|
+
else:
|
|
573
|
+
val = self._val(op)
|
|
574
|
+
if op[0] == 'float' or (op[0] == 'vec') or (op[0] == 'veclane'):
|
|
575
|
+
text = _format_float(float(val))
|
|
576
|
+
else:
|
|
577
|
+
iv = int(val)
|
|
578
|
+
text = '\n' if iv == 10 else str(iv)
|
|
579
|
+
if self._trace:
|
|
580
|
+
shown = text.replace('\n', '\\n')
|
|
581
|
+
self.logger.trace(f" OUT -> {shown!r}")
|
|
582
|
+
self._emit_text(text)
|
|
583
|
+
return True
|
|
584
|
+
|
|
585
|
+
def _op_halt(self, args):
|
|
586
|
+
return False
|
|
587
|
+
|
|
588
|
+
# ==================== ARM64 扩展 ====================
|
|
589
|
+
|
|
590
|
+
def _op_adds(self, args):
|
|
591
|
+
rd, rn = args[0][1], args[1][1]
|
|
592
|
+
a, b = self._reg(rn), self._val(args[2])
|
|
593
|
+
r = (a + b) & MASK64
|
|
594
|
+
self._set_flags_add(a, b, r)
|
|
595
|
+
self._set_reg(rd, r)
|
|
596
|
+
return True
|
|
597
|
+
|
|
598
|
+
def _op_subs(self, args):
|
|
599
|
+
rd, rn = args[0][1], args[1][1]
|
|
600
|
+
a, b = self._reg(rn), self._val(args[2])
|
|
601
|
+
r = (a - b) & MASK64
|
|
602
|
+
self._set_flags_sub(a, b, r)
|
|
603
|
+
self._set_reg(rd, r)
|
|
604
|
+
return True
|
|
605
|
+
|
|
606
|
+
def _op_addc(self, args):
|
|
607
|
+
rd, rn = args[0][1], args[1][1]
|
|
608
|
+
a, b = self._reg(rn), self._val(args[2])
|
|
609
|
+
r = (a + b + (1 if self.pstate['C'] else 0)) & MASK64
|
|
610
|
+
self._set_flags_add(a, b, r)
|
|
611
|
+
self._set_reg(rd, r)
|
|
612
|
+
return True
|
|
613
|
+
|
|
614
|
+
def _op_subc(self, args):
|
|
615
|
+
rd, rn = args[0][1], args[1][1]
|
|
616
|
+
a, b = self._reg(rn), self._val(args[2])
|
|
617
|
+
r = (a - b - (0 if self.pstate['C'] else 1)) & MASK64
|
|
618
|
+
self._set_flags_sub(a, b, r)
|
|
619
|
+
self._set_reg(rd, r)
|
|
620
|
+
return True
|
|
621
|
+
|
|
622
|
+
def _op_lsl(self, args):
|
|
623
|
+
rd, rn = args[0][1], args[1][1]
|
|
624
|
+
self._set_reg(rd, (self._reg(rn) << (self._val(args[2]) & 63)) & MASK64)
|
|
625
|
+
return True
|
|
626
|
+
|
|
627
|
+
def _op_lsr(self, args):
|
|
628
|
+
rd, rn = args[0][1], args[1][1]
|
|
629
|
+
self._set_reg(rd, (self._reg(rn) & MASK64) >> (self._val(args[2]) & 63))
|
|
630
|
+
return True
|
|
631
|
+
|
|
632
|
+
def _op_asr(self, args):
|
|
633
|
+
rd, rn = args[0][1], args[1][1]
|
|
634
|
+
v = self._reg(rn)
|
|
635
|
+
sv = v if v < (1 << 63) else v - (1 << 64)
|
|
636
|
+
r = sv >> (self._val(args[2]) & 63)
|
|
637
|
+
self._set_reg(rd, r & MASK64)
|
|
638
|
+
return True
|
|
639
|
+
|
|
640
|
+
def _op_ror(self, args):
|
|
641
|
+
rd, rn = args[0][1], args[1][1]
|
|
642
|
+
v = self._reg(rn) & MASK64
|
|
643
|
+
amt = self._val(args[2]) & 63
|
|
644
|
+
r = ((v >> amt) | (v << (64 - amt))) & MASK64 if amt else v
|
|
645
|
+
self._set_reg(rd, r)
|
|
646
|
+
return True
|
|
647
|
+
|
|
648
|
+
def _op_mvn(self, args):
|
|
649
|
+
self._set_reg(args[0][1], (~self._val(args[1])) & MASK64)
|
|
650
|
+
return True
|
|
651
|
+
|
|
652
|
+
def _op_eor(self, args):
|
|
653
|
+
rd, rn = args[0][1], args[1][1]
|
|
654
|
+
self._set_reg(rd, self._reg(rn) ^ self._val(args[2]))
|
|
655
|
+
return True
|
|
656
|
+
|
|
657
|
+
def _op_bic(self, args):
|
|
658
|
+
rd, rn = args[0][1], args[1][1]
|
|
659
|
+
self._set_reg(rd, self._reg(rn) & (~self._val(args[2]) & MASK64))
|
|
660
|
+
return True
|
|
661
|
+
|
|
662
|
+
def _op_orn(self, args):
|
|
663
|
+
rd, rn = args[0][1], args[1][1]
|
|
664
|
+
self._set_reg(rd, self._reg(rn) | (~self._val(args[2]) & MASK64))
|
|
665
|
+
return True
|
|
666
|
+
|
|
667
|
+
def _op_ldr(self, args):
|
|
668
|
+
rd = args[0][1]
|
|
669
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
670
|
+
self.stats.record_memory_read()
|
|
671
|
+
hit = self.cache.read(addr)
|
|
672
|
+
self.stats.record_cache(hit)
|
|
673
|
+
if self._trace:
|
|
674
|
+
self._trace_cache(addr, hit, 'R')
|
|
675
|
+
self._set_reg(rd, self.memory.read_dword(addr))
|
|
676
|
+
return True
|
|
677
|
+
|
|
678
|
+
def _op_str(self, args):
|
|
679
|
+
rs = args[0][1]
|
|
680
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
681
|
+
self.stats.record_memory_write()
|
|
682
|
+
hit = self.cache.write(addr)
|
|
683
|
+
self.stats.record_cache(hit)
|
|
684
|
+
if self._trace:
|
|
685
|
+
self._trace_cache(addr, hit, 'W')
|
|
686
|
+
self.memory.write_dword(addr, self._reg(rs))
|
|
687
|
+
return True
|
|
688
|
+
|
|
689
|
+
def _op_ldp(self, args):
|
|
690
|
+
rt, rt2 = args[0][1], args[1][1]
|
|
691
|
+
addr = self._mem_addr(args[2]) if args[2][0] == 'mem' else self._val(args[2])
|
|
692
|
+
self.stats.record_memory_read()
|
|
693
|
+
self._set_reg(rt, self.memory.read_qword(addr))
|
|
694
|
+
self._set_reg(rt2, self.memory.read_qword(addr + 8))
|
|
695
|
+
return True
|
|
696
|
+
|
|
697
|
+
def _op_stp(self, args):
|
|
698
|
+
rt, rt2 = args[0][1], args[1][1]
|
|
699
|
+
addr = self._mem_addr(args[2]) if args[2][0] == 'mem' else self._val(args[2])
|
|
700
|
+
self.stats.record_memory_write()
|
|
701
|
+
self.memory.write_qword(addr, self._reg(rt))
|
|
702
|
+
self.memory.write_qword(addr + 8, self._reg(rt2))
|
|
703
|
+
return True
|
|
704
|
+
|
|
705
|
+
def _op_cbz(self, args):
|
|
706
|
+
if self._reg(args[0][1]) == 0:
|
|
707
|
+
self.pc = self._val(args[1])
|
|
708
|
+
return True
|
|
709
|
+
|
|
710
|
+
def _op_cbnz(self, args):
|
|
711
|
+
if self._reg(args[0][1]) != 0:
|
|
712
|
+
self.pc = self._val(args[1])
|
|
713
|
+
return True
|
|
714
|
+
|
|
715
|
+
def _op_tbz(self, args):
|
|
716
|
+
rn, bit = self._reg(args[0][1]), self._val(args[1]) & 63
|
|
717
|
+
if not (rn & (1 << bit)):
|
|
718
|
+
self.pc = self._val(args[2])
|
|
719
|
+
return True
|
|
720
|
+
|
|
721
|
+
def _op_tbnz(self, args):
|
|
722
|
+
rn, bit = self._reg(args[0][1]), self._val(args[1]) & 63
|
|
723
|
+
if rn & (1 << bit):
|
|
724
|
+
self.pc = self._val(args[2])
|
|
725
|
+
return True
|
|
726
|
+
|
|
727
|
+
def _op_b(self, args):
|
|
728
|
+
# 可选条件操作数 ('cond', 'EQ'); 条件不满足则不跳转
|
|
729
|
+
cond_op = next((a for a in args if a[0] == 'cond'), None)
|
|
730
|
+
if cond_op is not None and not self._condition(cond_op[1]):
|
|
731
|
+
return True
|
|
732
|
+
for op in args:
|
|
733
|
+
if op[0] != 'cond':
|
|
734
|
+
self.pc = self._val(op)
|
|
735
|
+
return True
|
|
736
|
+
|
|
737
|
+
def _op_bl(self, args):
|
|
738
|
+
self._check_stack()
|
|
739
|
+
self._push(self.pc)
|
|
740
|
+
self.pc = self._val(args[0])
|
|
741
|
+
return True
|
|
742
|
+
|
|
743
|
+
def _op_br(self, args):
|
|
744
|
+
self.pc = self._reg(args[0][1])
|
|
745
|
+
return True
|
|
746
|
+
|
|
747
|
+
def _op_nop(self, args):
|
|
748
|
+
return True
|
|
749
|
+
|
|
750
|
+
def _op_csel(self, args):
|
|
751
|
+
rd, rn, rm = args[0][1], args[1][1], args[2]
|
|
752
|
+
cond = args[3][1] if args[3][0] == 'cond' else 'AL'
|
|
753
|
+
self._set_reg(rd, self._reg(rn) if self._condition(cond) else self._val(rm))
|
|
754
|
+
return True
|
|
755
|
+
|
|
756
|
+
def _op_csinc(self, args):
|
|
757
|
+
rd, rn, rm = args[0][1], args[1][1], args[2]
|
|
758
|
+
cond = args[3][1] if args[3][0] == 'cond' else 'AL'
|
|
759
|
+
if self._condition(cond):
|
|
760
|
+
self._set_reg(rd, self._reg(rn))
|
|
761
|
+
else:
|
|
762
|
+
self._set_reg(rd, self._val(rm) + 1)
|
|
763
|
+
return True
|
|
764
|
+
|
|
765
|
+
def _op_csinv(self, args):
|
|
766
|
+
rd, rn, rm = args[0][1], args[1][1], args[2]
|
|
767
|
+
cond = args[3][1] if args[3][0] == 'cond' else 'AL'
|
|
768
|
+
if self._condition(cond):
|
|
769
|
+
self._set_reg(rd, self._reg(rn))
|
|
770
|
+
else:
|
|
771
|
+
self._set_reg(rd, (~self._val(rm)) & MASK64)
|
|
772
|
+
return True
|
|
773
|
+
|
|
774
|
+
def _op_csneg(self, args):
|
|
775
|
+
rd, rn, rm = args[0][1], args[1][1], args[2]
|
|
776
|
+
cond = args[3][1] if args[3][0] == 'cond' else 'AL'
|
|
777
|
+
if self._condition(cond):
|
|
778
|
+
self._set_reg(rd, self._reg(rn))
|
|
779
|
+
else:
|
|
780
|
+
self._set_reg(rd, (-self._val(rm)) & MASK64)
|
|
781
|
+
return True
|
|
782
|
+
|
|
783
|
+
@staticmethod
|
|
784
|
+
def _sign_extend(v: int, bits: int) -> int:
|
|
785
|
+
sign = 1 << (bits - 1)
|
|
786
|
+
v &= (1 << bits) - 1
|
|
787
|
+
return (v ^ sign) - sign
|
|
788
|
+
|
|
789
|
+
def _op_sxtb(self, args):
|
|
790
|
+
self._set_reg(args[0][1], self._sign_extend(self._val(args[1]), 8) & MASK64)
|
|
791
|
+
return True
|
|
792
|
+
|
|
793
|
+
def _op_sxth(self, args):
|
|
794
|
+
self._set_reg(args[0][1], self._sign_extend(self._val(args[1]), 16) & MASK64)
|
|
795
|
+
return True
|
|
796
|
+
|
|
797
|
+
def _op_sxtw(self, args):
|
|
798
|
+
self._set_reg(args[0][1], self._sign_extend(self._val(args[1]), 32) & MASK64)
|
|
799
|
+
return True
|
|
800
|
+
|
|
801
|
+
def _op_uxtb(self, args):
|
|
802
|
+
self._set_reg(args[0][1], self._val(args[1]) & 0xFF)
|
|
803
|
+
return True
|
|
804
|
+
|
|
805
|
+
def _op_uxth(self, args):
|
|
806
|
+
self._set_reg(args[0][1], self._val(args[1]) & 0xFFFF)
|
|
807
|
+
return True
|
|
808
|
+
|
|
809
|
+
def _op_clz(self, args):
|
|
810
|
+
v = self._val(args[1]) & MASK64
|
|
811
|
+
self._set_reg(args[0][1], 64 - v.bit_length() if v else 64)
|
|
812
|
+
return True
|
|
813
|
+
|
|
814
|
+
def _op_cls(self, args):
|
|
815
|
+
v = self._val(args[1]) & MASK64
|
|
816
|
+
sv = v if v < (1 << 63) else v - (1 << 64)
|
|
817
|
+
if sv >= 0:
|
|
818
|
+
self._set_reg(args[0][1], 63 - sv.bit_length() if sv else 63)
|
|
819
|
+
else:
|
|
820
|
+
self._set_reg(args[0][1], 63 - (~sv & MASK64).bit_length() + 1)
|
|
821
|
+
return True
|
|
822
|
+
|
|
823
|
+
def _op_rbit(self, args):
|
|
824
|
+
v = self._val(args[1]) & MASK64
|
|
825
|
+
r = int(f'{v:064b}'[::-1], 2)
|
|
826
|
+
self._set_reg(args[0][1], r)
|
|
827
|
+
return True
|
|
828
|
+
|
|
829
|
+
def _op_rev(self, args):
|
|
830
|
+
v = self._val(args[1]) & MASK64
|
|
831
|
+
b = v.to_bytes(8, 'little')
|
|
832
|
+
self._set_reg(args[0][1], int.from_bytes(b[::-1], 'little'))
|
|
833
|
+
return True
|
|
834
|
+
|
|
835
|
+
# ==================== 浮点 / 向量 ====================
|
|
836
|
+
|
|
837
|
+
def _op_fadd(self, args):
|
|
838
|
+
rd, rn, rm = args[0][1], args[1][1], args[2][1]
|
|
839
|
+
self.vec_regs.write_scalar(rd, self.vec_regs.read_scalar(rn) + self.vec_regs.read_scalar(rm))
|
|
840
|
+
return True
|
|
841
|
+
|
|
842
|
+
def _op_fsub(self, args):
|
|
843
|
+
rd, rn, rm = args[0][1], args[1][1], args[2][1]
|
|
844
|
+
self.vec_regs.write_scalar(rd, self.vec_regs.read_scalar(rn) - self.vec_regs.read_scalar(rm))
|
|
845
|
+
return True
|
|
846
|
+
|
|
847
|
+
def _op_fmul(self, args):
|
|
848
|
+
rd, rn, rm = args[0][1], args[1][1], args[2][1]
|
|
849
|
+
self.vec_regs.write_scalar(rd, self.vec_regs.read_scalar(rn) * self.vec_regs.read_scalar(rm))
|
|
850
|
+
return True
|
|
851
|
+
|
|
852
|
+
def _op_fdiv(self, args):
|
|
853
|
+
rd, rn, rm = args[0][1], args[1][1], args[2][1]
|
|
854
|
+
b = self.vec_regs.read_scalar(rm)
|
|
855
|
+
if b == 0:
|
|
856
|
+
raise ExecutionError("Float division by zero")
|
|
857
|
+
self.vec_regs.write_scalar(rd, self.vec_regs.read_scalar(rn) / b)
|
|
858
|
+
return True
|
|
859
|
+
|
|
860
|
+
def _op_fcmp(self, args):
|
|
861
|
+
rn, rm = args[0][1], args[1][1]
|
|
862
|
+
a, b = self.vec_regs.read_scalar(rn), self.vec_regs.read_scalar(rm)
|
|
863
|
+
self.pstate['Z'] = (a == b)
|
|
864
|
+
self.pstate['N'] = (a < b)
|
|
865
|
+
self.pstate['C'] = (a >= b)
|
|
866
|
+
self.pstate['V'] = False
|
|
867
|
+
return True
|
|
868
|
+
|
|
869
|
+
def _op_fcvt(self, args):
|
|
870
|
+
rd, rs = args[0][1], args[1][1]
|
|
871
|
+
if args[1][0] in ('vec', 'veclane'):
|
|
872
|
+
# float -> int
|
|
873
|
+
self._set_reg(rd, int(self.vec_regs.read_scalar(rs)))
|
|
874
|
+
else:
|
|
875
|
+
# int -> float
|
|
876
|
+
self.vec_regs.write_scalar(rd, float(self._reg(rs)))
|
|
877
|
+
return True
|
|
878
|
+
|
|
879
|
+
def _op_fabs(self, args):
|
|
880
|
+
self.vec_regs.write_scalar(args[0][1], abs(self.vec_regs.read_scalar(args[1][1])))
|
|
881
|
+
return True
|
|
882
|
+
|
|
883
|
+
def _op_fneg(self, args):
|
|
884
|
+
self.vec_regs.write_scalar(args[0][1], -self.vec_regs.read_scalar(args[1][1]))
|
|
885
|
+
return True
|
|
886
|
+
|
|
887
|
+
def _op_ldrs(self, args):
|
|
888
|
+
rd = args[0][1]
|
|
889
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
890
|
+
self.stats.record_memory_read()
|
|
891
|
+
self.vec_regs.write_scalar(rd, self.memory.read_float(addr))
|
|
892
|
+
return True
|
|
893
|
+
|
|
894
|
+
def _op_strs(self, args):
|
|
895
|
+
rs = args[0][1]
|
|
896
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
897
|
+
self.stats.record_memory_write()
|
|
898
|
+
self.memory.write_float(addr, self.vec_regs.read_scalar(rs))
|
|
899
|
+
return True
|
|
900
|
+
|
|
901
|
+
def _vec_bin(self, args, fn):
|
|
902
|
+
rd, rn, rm = args[0][1], args[1][1], args[2][1]
|
|
903
|
+
v1 = self.vec_regs.read_vector(rn)
|
|
904
|
+
v2 = self.vec_regs.read_vector(rm)
|
|
905
|
+
self.vec_regs.write_vector(rd, [fn(v1[i], v2[i]) for i in range(Constants.VECTOR_LANES)])
|
|
906
|
+
return True
|
|
907
|
+
|
|
908
|
+
def _op_vadd(self, args):
|
|
909
|
+
return self._vec_bin(args, lambda a, b: a + b)
|
|
910
|
+
|
|
911
|
+
def _op_vsub(self, args):
|
|
912
|
+
return self._vec_bin(args, lambda a, b: a - b)
|
|
913
|
+
|
|
914
|
+
def _op_vmul(self, args):
|
|
915
|
+
return self._vec_bin(args, lambda a, b: a * b)
|
|
916
|
+
|
|
917
|
+
def _op_vdiv(self, args):
|
|
918
|
+
def f(a, b):
|
|
919
|
+
if b == 0:
|
|
920
|
+
raise ExecutionError("Vector division by zero")
|
|
921
|
+
return a / b
|
|
922
|
+
return self._vec_bin(args, f)
|
|
923
|
+
|
|
924
|
+
def _op_vld1(self, args):
|
|
925
|
+
rd = args[0][1]
|
|
926
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
927
|
+
self.stats.record_memory_read()
|
|
928
|
+
data = self.memory.read_block(addr, 16)
|
|
929
|
+
self.vec_regs.write_vector(rd, [float(x) for x in struct.unpack('<4f', data)])
|
|
930
|
+
return True
|
|
931
|
+
|
|
932
|
+
def _op_vst1(self, args):
|
|
933
|
+
rs = args[0][1]
|
|
934
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
935
|
+
self.stats.record_memory_write()
|
|
936
|
+
vec = self.vec_regs.read_vector(rs)
|
|
937
|
+
self.memory.write_block(addr, struct.pack('<4f', *[float(x) for x in vec]))
|
|
938
|
+
return True
|
|
939
|
+
|
|
940
|
+
# ==================== RISC-V 扩展 ====================
|
|
941
|
+
|
|
942
|
+
def _load_width(self, args, bits: int, signed: bool):
|
|
943
|
+
rd = args[0][1]
|
|
944
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
945
|
+
self.stats.record_memory_read()
|
|
946
|
+
hit = self.cache.read(addr)
|
|
947
|
+
self.stats.record_cache(hit)
|
|
948
|
+
if self._trace:
|
|
949
|
+
self._trace_cache(addr, hit, 'R')
|
|
950
|
+
if bits == 8:
|
|
951
|
+
v = self.memory.read_byte(addr)
|
|
952
|
+
if signed:
|
|
953
|
+
v = self._sign_extend(v, 8)
|
|
954
|
+
elif bits == 16:
|
|
955
|
+
v = self.memory.read_word(addr)
|
|
956
|
+
if signed:
|
|
957
|
+
v = self._sign_extend(v, 16)
|
|
958
|
+
elif bits == 32:
|
|
959
|
+
v = self.memory.read_dword(addr)
|
|
960
|
+
if signed:
|
|
961
|
+
v = self._sign_extend(v, 32)
|
|
962
|
+
else:
|
|
963
|
+
v = self.memory.read_qword(addr)
|
|
964
|
+
self._set_reg(rd, v & MASK64)
|
|
965
|
+
return True
|
|
966
|
+
|
|
967
|
+
def _store_width(self, args, bits: int):
|
|
968
|
+
rs = args[0][1]
|
|
969
|
+
addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
|
|
970
|
+
self.stats.record_memory_write()
|
|
971
|
+
hit = self.cache.write(addr)
|
|
972
|
+
self.stats.record_cache(hit)
|
|
973
|
+
if self._trace:
|
|
974
|
+
self._trace_cache(addr, hit, 'W')
|
|
975
|
+
v = self._reg(rs)
|
|
976
|
+
if bits == 8:
|
|
977
|
+
self.memory.write_byte(addr, v & 0xFF)
|
|
978
|
+
elif bits == 16:
|
|
979
|
+
self.memory.write_word(addr, v & 0xFFFF)
|
|
980
|
+
elif bits == 32:
|
|
981
|
+
self.memory.write_dword(addr, v & MASK32)
|
|
982
|
+
else:
|
|
983
|
+
self.memory.write_qword(addr, v & MASK64)
|
|
984
|
+
return True
|
|
985
|
+
|
|
986
|
+
def _op_lb(self, args):
|
|
987
|
+
return self._load_width(args, 8, True)
|
|
988
|
+
|
|
989
|
+
def _op_lh(self, args):
|
|
990
|
+
return self._load_width(args, 16, True)
|
|
991
|
+
|
|
992
|
+
def _op_lw(self, args):
|
|
993
|
+
return self._load_width(args, 32, True)
|
|
994
|
+
|
|
995
|
+
def _op_ld(self, args):
|
|
996
|
+
return self._load_width(args, 64, False)
|
|
997
|
+
|
|
998
|
+
def _op_sb(self, args):
|
|
999
|
+
return self._store_width(args, 8)
|
|
1000
|
+
|
|
1001
|
+
def _op_sh(self, args):
|
|
1002
|
+
return self._store_width(args, 16)
|
|
1003
|
+
|
|
1004
|
+
def _op_sw(self, args):
|
|
1005
|
+
return self._store_width(args, 32)
|
|
1006
|
+
|
|
1007
|
+
def _op_sd(self, args):
|
|
1008
|
+
return self._store_width(args, 64)
|
|
1009
|
+
|
|
1010
|
+
def _op_addi(self, args):
|
|
1011
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1012
|
+
self._set_reg(rd, self._reg(rs1) + self._val(args[2]))
|
|
1013
|
+
return True
|
|
1014
|
+
|
|
1015
|
+
def _op_slti(self, args):
|
|
1016
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1017
|
+
a = self._reg(rs1)
|
|
1018
|
+
b = self._val(args[2])
|
|
1019
|
+
sa = a if a < (1 << 63) else a - (1 << 64)
|
|
1020
|
+
sb = b if b < (1 << 63) else b - (1 << 64)
|
|
1021
|
+
self._set_reg(rd, 1 if sa < sb else 0)
|
|
1022
|
+
return True
|
|
1023
|
+
|
|
1024
|
+
def _op_sltiu(self, args):
|
|
1025
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1026
|
+
self._set_reg(rd, 1 if (self._reg(rs1) & MASK64) < (self._val(args[2]) & MASK64) else 0)
|
|
1027
|
+
return True
|
|
1028
|
+
|
|
1029
|
+
def _op_xori(self, args):
|
|
1030
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1031
|
+
self._set_reg(rd, self._reg(rs1) ^ self._val(args[2]))
|
|
1032
|
+
return True
|
|
1033
|
+
|
|
1034
|
+
def _op_ori(self, args):
|
|
1035
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1036
|
+
self._set_reg(rd, self._reg(rs1) | self._val(args[2]))
|
|
1037
|
+
return True
|
|
1038
|
+
|
|
1039
|
+
def _op_andi(self, args):
|
|
1040
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1041
|
+
self._set_reg(rd, self._reg(rs1) & self._val(args[2]))
|
|
1042
|
+
return True
|
|
1043
|
+
|
|
1044
|
+
def _op_slli(self, args):
|
|
1045
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1046
|
+
self._set_reg(rd, (self._reg(rs1) << (self._val(args[2]) & 63)) & MASK64)
|
|
1047
|
+
return True
|
|
1048
|
+
|
|
1049
|
+
def _op_srli(self, args):
|
|
1050
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1051
|
+
self._set_reg(rd, (self._reg(rs1) & MASK64) >> (self._val(args[2]) & 63))
|
|
1052
|
+
return True
|
|
1053
|
+
|
|
1054
|
+
def _op_srai(self, args):
|
|
1055
|
+
rd, rs1 = args[0][1], args[1][1]
|
|
1056
|
+
v = self._reg(rs1)
|
|
1057
|
+
sv = v if v < (1 << 63) else v - (1 << 64)
|
|
1058
|
+
self._set_reg(rd, (sv >> (self._val(args[2]) & 63)) & MASK64)
|
|
1059
|
+
return True
|
|
1060
|
+
|
|
1061
|
+
def _branch_rs(self, args, pred_taken: bool, predicted: bool):
|
|
1062
|
+
self.stats.record_branch(self.pc, pred_taken, predicted)
|
|
1063
|
+
if pred_taken:
|
|
1064
|
+
self.pc = self._val(args[2])
|
|
1065
|
+
|
|
1066
|
+
def _op_beq(self, args):
|
|
1067
|
+
predicted = self.stats.performance_counters.branch_predictor.predict(self.pc)
|
|
1068
|
+
taken = self._reg(args[0][1]) == self._reg(args[1][1])
|
|
1069
|
+
self._branch_rs(args, taken, predicted)
|
|
1070
|
+
return True
|
|
1071
|
+
|
|
1072
|
+
def _op_bne(self, args):
|
|
1073
|
+
predicted = self.stats.performance_counters.branch_predictor.predict(self.pc)
|
|
1074
|
+
taken = self._reg(args[0][1]) != self._reg(args[1][1])
|
|
1075
|
+
self._branch_rs(args, taken, predicted)
|
|
1076
|
+
return True
|
|
1077
|
+
|
|
1078
|
+
def _op_blt(self, args):
|
|
1079
|
+
predicted = self.stats.performance_counters.branch_predictor.predict(self.pc)
|
|
1080
|
+
a, b = self._reg(args[0][1]), self._reg(args[1][1])
|
|
1081
|
+
sa = a if a < (1 << 63) else a - (1 << 64)
|
|
1082
|
+
sb = b if b < (1 << 63) else b - (1 << 64)
|
|
1083
|
+
self._branch_rs(args, sa < sb, predicted)
|
|
1084
|
+
return True
|
|
1085
|
+
|
|
1086
|
+
def _op_bge(self, args):
|
|
1087
|
+
predicted = self.stats.performance_counters.branch_predictor.predict(self.pc)
|
|
1088
|
+
a, b = self._reg(args[0][1]), self._reg(args[1][1])
|
|
1089
|
+
sa = a if a < (1 << 63) else a - (1 << 64)
|
|
1090
|
+
sb = b if b < (1 << 63) else b - (1 << 64)
|
|
1091
|
+
self._branch_rs(args, sa >= sb, predicted)
|
|
1092
|
+
return True
|
|
1093
|
+
|
|
1094
|
+
def _op_bltu(self, args):
|
|
1095
|
+
predicted = self.stats.performance_counters.branch_predictor.predict(self.pc)
|
|
1096
|
+
taken = (self._reg(args[0][1]) & MASK64) < (self._reg(args[1][1]) & MASK64)
|
|
1097
|
+
self._branch_rs(args, taken, predicted)
|
|
1098
|
+
return True
|
|
1099
|
+
|
|
1100
|
+
def _op_bgeu(self, args):
|
|
1101
|
+
predicted = self.stats.performance_counters.branch_predictor.predict(self.pc)
|
|
1102
|
+
taken = (self._reg(args[0][1]) & MASK64) >= (self._reg(args[1][1]) & MASK64)
|
|
1103
|
+
self._branch_rs(args, taken, predicted)
|
|
1104
|
+
return True
|
|
1105
|
+
|
|
1106
|
+
def _op_jalr(self, args):
|
|
1107
|
+
# JALR rd, rs1, imm / jalr rs1 (单寄存器形式视为 ret, rd 丢弃)
|
|
1108
|
+
regs = [a[1] for a in args if a[0] == 'reg']
|
|
1109
|
+
imm = 0
|
|
1110
|
+
for a in args:
|
|
1111
|
+
if a[0] == 'imm':
|
|
1112
|
+
imm = a[1]
|
|
1113
|
+
elif a[0] == 'mem':
|
|
1114
|
+
imm = a[2]
|
|
1115
|
+
if a[1] >= 0 and len(regs) < 2:
|
|
1116
|
+
regs.append(a[1])
|
|
1117
|
+
if len(regs) >= 2:
|
|
1118
|
+
rd, rs1 = regs[0], regs[1]
|
|
1119
|
+
elif len(regs) == 1:
|
|
1120
|
+
rd, rs1 = 31, regs[0]
|
|
1121
|
+
else:
|
|
1122
|
+
rd, rs1 = 31, 31
|
|
1123
|
+
if rd != 31:
|
|
1124
|
+
self._set_reg(rd, self.pc)
|
|
1125
|
+
self.pc = (self._reg(rs1) + imm) & MASK64
|
|
1126
|
+
return True
|
|
1127
|
+
|
|
1128
|
+
def _op_jal(self, args):
|
|
1129
|
+
rd = args[0][1] if args[0][0] == 'reg' else 31
|
|
1130
|
+
if rd != 31:
|
|
1131
|
+
self._set_reg(rd, self.pc)
|
|
1132
|
+
target = args[1] if args[0][0] == 'reg' else args[0]
|
|
1133
|
+
self.pc = self._val(target)
|
|
1134
|
+
return True
|
|
1135
|
+
|
|
1136
|
+
def _op_lui(self, args):
|
|
1137
|
+
rd = args[0][1]
|
|
1138
|
+
imm = self._val(args[1]) if args[1][0] != 'imm' else args[1][1]
|
|
1139
|
+
self._set_reg(rd, (imm & 0xFFFFF) << 12)
|
|
1140
|
+
return True
|
|
1141
|
+
|
|
1142
|
+
def _op_auipc(self, args):
|
|
1143
|
+
rd = args[0][1]
|
|
1144
|
+
imm = self._val(args[1]) if args[1][0] != 'imm' else args[1][1]
|
|
1145
|
+
self._set_reg(rd, self.pc + ((imm & 0xFFFFF) << 12))
|
|
1146
|
+
return True
|
|
1147
|
+
|
|
1148
|
+
# ==================== SYS 宿主调用 ====================
|
|
1149
|
+
|
|
1150
|
+
def _heap_dup_string(self, data: bytes) -> int:
|
|
1151
|
+
"""在堆上分配 NUL 结尾字符串副本, 返回指针。"""
|
|
1152
|
+
size = (len(data) + 15) & ~0xF
|
|
1153
|
+
ptr = self.heap_ptr
|
|
1154
|
+
self.heap_ptr += size
|
|
1155
|
+
if self.heap_ptr > self.sp:
|
|
1156
|
+
raise ExecutionError("Heap exhausted (string operation)")
|
|
1157
|
+
self.memory.write_block(ptr, data)
|
|
1158
|
+
return ptr
|
|
1159
|
+
|
|
1160
|
+
def _sys_buffer(self) -> int:
|
|
1161
|
+
idx = self._sys_buf_idx % len(self._sys_buffers)
|
|
1162
|
+
self._sys_buf_idx += 1
|
|
1163
|
+
# 缓冲放在内存高地址区下方 (heap 之后)
|
|
1164
|
+
addr = self.heap_ptr + 2048 + idx * 64
|
|
1165
|
+
return addr
|
|
1166
|
+
|
|
1167
|
+
def _op_sys(self, args):
|
|
1168
|
+
if not args or args[0][0] != 'imm':
|
|
1169
|
+
raise ExecutionError("SYS requires an immediate call id")
|
|
1170
|
+
call_id = args[0][1]
|
|
1171
|
+
x0 = self._reg(0)
|
|
1172
|
+
x1 = self._reg(1)
|
|
1173
|
+
x2 = self._reg(2)
|
|
1174
|
+
if self._trace:
|
|
1175
|
+
name = Syscall(call_id).name if call_id in Syscall._value2member_map_ \
|
|
1176
|
+
else f'UNKNOWN({call_id})'
|
|
1177
|
+
self.logger.trace(f" SYS #{call_id} ({name}) "
|
|
1178
|
+
f"x0=0x{x0:x} x1=0x{x1:x} x2=0x{x2:x}")
|
|
1179
|
+
|
|
1180
|
+
if call_id == Syscall.ABS:
|
|
1181
|
+
self._set_reg(0, abs(x0 if x0 < (1 << 63) else x0 - (1 << 64)))
|
|
1182
|
+
elif call_id in (Syscall.SQRT,):
|
|
1183
|
+
self._set_reg(0, _f_to_bits(math.sqrt(_bits_to_f(x0))))
|
|
1184
|
+
elif call_id == Syscall.POW:
|
|
1185
|
+
self._set_reg(0, _f_to_bits(math.pow(_bits_to_f(x0), _bits_to_f(x1))))
|
|
1186
|
+
elif call_id == Syscall.SIN:
|
|
1187
|
+
self._set_reg(0, _f_to_bits(math.sin(_bits_to_f(x0))))
|
|
1188
|
+
elif call_id == Syscall.COS:
|
|
1189
|
+
self._set_reg(0, _f_to_bits(math.cos(_bits_to_f(x0))))
|
|
1190
|
+
elif call_id == Syscall.TAN:
|
|
1191
|
+
self._set_reg(0, _f_to_bits(math.tan(_bits_to_f(x0))))
|
|
1192
|
+
elif call_id == Syscall.FADD:
|
|
1193
|
+
self._set_reg(0, _f_to_bits(_bits_to_f(x0) + _bits_to_f(x1)))
|
|
1194
|
+
elif call_id == Syscall.FSUB:
|
|
1195
|
+
self._set_reg(0, _f_to_bits(_bits_to_f(x0) - _bits_to_f(x1)))
|
|
1196
|
+
elif call_id == Syscall.FMUL:
|
|
1197
|
+
self._set_reg(0, _f_to_bits(_bits_to_f(x0) * _bits_to_f(x1)))
|
|
1198
|
+
elif call_id == Syscall.FDIV:
|
|
1199
|
+
b = _bits_to_f(x1)
|
|
1200
|
+
if b == 0:
|
|
1201
|
+
raise ExecutionError("Float division by zero (SYS)")
|
|
1202
|
+
self._set_reg(0, _f_to_bits(_bits_to_f(x0) / b))
|
|
1203
|
+
elif call_id == Syscall.FCMP:
|
|
1204
|
+
a, b = _bits_to_f(x0), _bits_to_f(x1)
|
|
1205
|
+
self._set_reg(0, -1 if a < b else (1 if a > b else 0))
|
|
1206
|
+
elif call_id == Syscall.FTOI:
|
|
1207
|
+
self._set_reg(0, int(_bits_to_f(x0)))
|
|
1208
|
+
elif call_id == Syscall.ITOF:
|
|
1209
|
+
self._set_reg(0, _f_to_bits(float(x0 if x0 < (1 << 63) else x0 - (1 << 64))))
|
|
1210
|
+
elif call_id == Syscall.RAND:
|
|
1211
|
+
self._set_reg(0, self._rand.randrange(1 << 31))
|
|
1212
|
+
elif call_id == Syscall.SRAND:
|
|
1213
|
+
self._rand.seed(x0)
|
|
1214
|
+
elif call_id == Syscall.TIME:
|
|
1215
|
+
self._set_reg(0, int(time.time()))
|
|
1216
|
+
elif call_id == Syscall.STRLEN:
|
|
1217
|
+
n = 0
|
|
1218
|
+
while self.memory.read_byte(x0 + n) != 0:
|
|
1219
|
+
n += 1
|
|
1220
|
+
self._set_reg(0, n)
|
|
1221
|
+
elif call_id == Syscall.STRCMP:
|
|
1222
|
+
i = 0
|
|
1223
|
+
while True:
|
|
1224
|
+
ca = self.memory.read_byte(x0 + i)
|
|
1225
|
+
cb = self.memory.read_byte(x1 + i)
|
|
1226
|
+
if ca != cb or ca == 0:
|
|
1227
|
+
self._set_reg(0, ca - cb)
|
|
1228
|
+
break
|
|
1229
|
+
i += 1
|
|
1230
|
+
elif call_id in (Syscall.STRCPY, Syscall.STRCAT):
|
|
1231
|
+
dst = x0
|
|
1232
|
+
if call_id == Syscall.STRCAT:
|
|
1233
|
+
while self.memory.read_byte(dst) != 0:
|
|
1234
|
+
dst += 1
|
|
1235
|
+
src = x1
|
|
1236
|
+
i = 0
|
|
1237
|
+
while True:
|
|
1238
|
+
ch = self.memory.read_byte(src + i)
|
|
1239
|
+
self.memory.write_byte(dst + i, ch)
|
|
1240
|
+
i += 1
|
|
1241
|
+
if ch == 0:
|
|
1242
|
+
break
|
|
1243
|
+
self._set_reg(0, x0)
|
|
1244
|
+
elif call_id == Syscall.MALLOC:
|
|
1245
|
+
size = (x0 + 15) & ~0xF
|
|
1246
|
+
ptr = self.heap_ptr
|
|
1247
|
+
self.heap_ptr += size
|
|
1248
|
+
if self.heap_ptr > self.sp:
|
|
1249
|
+
raise ExecutionError("Heap exhausted")
|
|
1250
|
+
self._set_reg(0, ptr)
|
|
1251
|
+
elif call_id == Syscall.PRINT_FLOAT:
|
|
1252
|
+
self._emit_text(_format_float(_bits_to_f(x0)))
|
|
1253
|
+
elif call_id == Syscall.ITOA:
|
|
1254
|
+
addr = self._sys_buffer()
|
|
1255
|
+
signed = x0 if x0 < (1 << 63) else x0 - (1 << 64)
|
|
1256
|
+
self.memory.write_string(addr, str(signed))
|
|
1257
|
+
self._set_reg(0, addr)
|
|
1258
|
+
elif call_id == Syscall.FTOA:
|
|
1259
|
+
addr = self._sys_buffer()
|
|
1260
|
+
self.memory.write_string(addr, _format_float(_bits_to_f(x0)))
|
|
1261
|
+
self._set_reg(0, addr)
|
|
1262
|
+
elif call_id == Syscall.PRINT_STR:
|
|
1263
|
+
self._emit_text(self.memory.read_string(x0))
|
|
1264
|
+
elif call_id == Syscall.STR_CONCAT:
|
|
1265
|
+
sa = self.memory.read_string(x0)
|
|
1266
|
+
sb = self.memory.read_string(x1)
|
|
1267
|
+
data = (sa + sb).encode('utf-8') + b'\x00'
|
|
1268
|
+
size = (len(data) + 15) & ~0xF
|
|
1269
|
+
ptr = self.heap_ptr
|
|
1270
|
+
self.heap_ptr += size
|
|
1271
|
+
if self.heap_ptr > self.sp:
|
|
1272
|
+
raise ExecutionError("Heap exhausted (string concat)")
|
|
1273
|
+
self.memory.write_block(ptr, data)
|
|
1274
|
+
self._set_reg(0, ptr)
|
|
1275
|
+
elif call_id == Syscall.BOOL_STR:
|
|
1276
|
+
addr = self._sys_buffer()
|
|
1277
|
+
self.memory.write_string(addr, "true" if x0 != 0 else "false")
|
|
1278
|
+
self._set_reg(0, addr)
|
|
1279
|
+
elif call_id == Syscall.ABORT:
|
|
1280
|
+
# 运行时中止 (assert / 边界检查): 消息指针位于 x0
|
|
1281
|
+
msg = self.memory.read_string(x0, 512)
|
|
1282
|
+
raise ExecutionError(f"Runtime abort: {msg}" if msg
|
|
1283
|
+
else "Runtime abort")
|
|
1284
|
+
elif call_id == Syscall.SUBSTR:
|
|
1285
|
+
# substr(s, start, len): 越界自动裁剪
|
|
1286
|
+
s = self.memory.read_string(x0)
|
|
1287
|
+
n = len(s)
|
|
1288
|
+
start = 0 if x1 < 0 else (n if x1 > n else x1)
|
|
1289
|
+
length = 0 if x2 < 0 else x2
|
|
1290
|
+
self._set_reg(0, self._heap_dup_string(
|
|
1291
|
+
s[start:start + length].encode('utf-8') + b'\x00'))
|
|
1292
|
+
elif call_id == Syscall.INDEXOF:
|
|
1293
|
+
hay = self.memory.read_string(x0)
|
|
1294
|
+
needle = self.memory.read_string(x1)
|
|
1295
|
+
idx = hay.find(needle)
|
|
1296
|
+
self._set_reg(0, idx)
|
|
1297
|
+
elif call_id == Syscall.TOUPPER:
|
|
1298
|
+
data = self.memory.read_string(x0)
|
|
1299
|
+
self._set_reg(0, self._heap_dup_string(
|
|
1300
|
+
data.upper().encode('utf-8') + b'\x00'))
|
|
1301
|
+
elif call_id == Syscall.TOLOWER:
|
|
1302
|
+
data = self.memory.read_string(x0)
|
|
1303
|
+
self._set_reg(0, self._heap_dup_string(
|
|
1304
|
+
data.lower().encode('utf-8') + b'\x00'))
|
|
1305
|
+
elif call_id == Syscall.FLOOR:
|
|
1306
|
+
self._set_reg(0, _f_to_bits(math.floor(_bits_to_f(x0))))
|
|
1307
|
+
elif call_id == Syscall.CEIL:
|
|
1308
|
+
self._set_reg(0, _f_to_bits(math.ceil(_bits_to_f(x0))))
|
|
1309
|
+
elif call_id == Syscall.ROUND:
|
|
1310
|
+
# 与 lib/math.cin f_round 一致: floor(x + 0.5) (半值向 +inf)
|
|
1311
|
+
self._set_reg(0, _f_to_bits(math.floor(_bits_to_f(x0) + 0.5)))
|
|
1312
|
+
elif call_id == Syscall.ATOI:
|
|
1313
|
+
s = self.memory.read_string(x0).strip()
|
|
1314
|
+
try:
|
|
1315
|
+
self._set_reg(0, int(s) & MASK64)
|
|
1316
|
+
except ValueError:
|
|
1317
|
+
self._set_reg(0, 0)
|
|
1318
|
+
elif call_id == Syscall.TRIM:
|
|
1319
|
+
self._set_reg(0, self._heap_dup_string(
|
|
1320
|
+
self.memory.read_string(x0).strip().encode('utf-8') + b'\x00'))
|
|
1321
|
+
elif call_id == Syscall.LTRIM:
|
|
1322
|
+
self._set_reg(0, self._heap_dup_string(
|
|
1323
|
+
self.memory.read_string(x0).lstrip().encode('utf-8') + b'\x00'))
|
|
1324
|
+
elif call_id == Syscall.RTRIM:
|
|
1325
|
+
self._set_reg(0, self._heap_dup_string(
|
|
1326
|
+
self.memory.read_string(x0).rstrip().encode('utf-8') + b'\x00'))
|
|
1327
|
+
elif Syscall.AUDIOPLAY <= call_id <= Syscall.TERMUXSMS:
|
|
1328
|
+
raise ExecutionError(
|
|
1329
|
+
"host builtins (GUI/audio/system/Termux) require the native "
|
|
1330
|
+
"Go runtime (run without --no-native)")
|
|
1331
|
+
else:
|
|
1332
|
+
raise ExecutionError(f"Unknown SYS call id: {call_id}")
|
|
1333
|
+
return True
|
|
1334
|
+
|
|
1335
|
+
# ==================== 执行引擎 ====================
|
|
1336
|
+
|
|
1337
|
+
def execute(self, opcode: str, args: List[Operand]) -> bool:
|
|
1338
|
+
if self._trace:
|
|
1339
|
+
ops = ' '.join(self._fmt_op_trace(a) for a in args)
|
|
1340
|
+
self.logger.trace(
|
|
1341
|
+
f"PC=0x{self.pc:04x} #{self.stats.instruction_count:08d} "
|
|
1342
|
+
f"{opcode} {ops} SP=0x{self.sp:x}")
|
|
1343
|
+
self.pc += 1
|
|
1344
|
+
handler = self._dispatch.get(opcode)
|
|
1345
|
+
if handler is None:
|
|
1346
|
+
raise ExecutionError(f"Unimplemented instruction: {opcode}")
|
|
1347
|
+
result = handler(args)
|
|
1348
|
+
self.stats.record_instruction(opcode)
|
|
1349
|
+
if self._trace:
|
|
1350
|
+
self.logger.trace(
|
|
1351
|
+
f" => pc=0x{self.pc:04x} {self._trace_flags()}")
|
|
1352
|
+
return result
|
|
1353
|
+
|
|
1354
|
+
def step(self) -> bool:
|
|
1355
|
+
if self.pc < 0 or self.pc >= len(self.instructions):
|
|
1356
|
+
return False
|
|
1357
|
+
opcode, args = self.instructions[self.pc]
|
|
1358
|
+
return self.execute(opcode, args)
|
|
1359
|
+
|
|
1360
|
+
def _try_native_run(self) -> Optional[bool]:
|
|
1361
|
+
"""尝试使用 Go 原生库执行整个程序; 不可用/不支持时返回 None。"""
|
|
1362
|
+
if not self.config.use_native or self.config.debug_mode or self.config.step_mode:
|
|
1363
|
+
return None
|
|
1364
|
+
if self.config.enable_jit:
|
|
1365
|
+
return None
|
|
1366
|
+
try:
|
|
1367
|
+
from . import native
|
|
1368
|
+
except Exception:
|
|
1369
|
+
return None
|
|
1370
|
+
engine = native.get_engine(self.logger)
|
|
1371
|
+
if engine is None:
|
|
1372
|
+
return None
|
|
1373
|
+
|
|
1374
|
+
from .native import encode_program
|
|
1375
|
+
all_labels = dict(self.labels)
|
|
1376
|
+
all_labels.update(self.data_labels)
|
|
1377
|
+
bytecode = encode_program(self.instructions, self.entry_pc, all_labels)
|
|
1378
|
+
mem_image = self.memory.get_snapshot()
|
|
1379
|
+
self.logger.debug(
|
|
1380
|
+
f"Native run: bytecode={len(bytecode)}B mem={len(mem_image)}B "
|
|
1381
|
+
f"entry={self.entry_pc} sp=0x{self.sp:x} "
|
|
1382
|
+
f"heap=0x{self.heap_ptr:x} max_steps={self.config.max_instructions}")
|
|
1383
|
+
import time as _time
|
|
1384
|
+
t0 = _time.perf_counter()
|
|
1385
|
+
result = engine.run(
|
|
1386
|
+
bytecode=bytecode,
|
|
1387
|
+
mem=mem_image,
|
|
1388
|
+
entry=self.entry_pc,
|
|
1389
|
+
sp=self.sp,
|
|
1390
|
+
heap_base=self.heap_ptr,
|
|
1391
|
+
input_data=self.input_buffer.encode('utf-8'),
|
|
1392
|
+
max_steps=self.config.max_instructions,
|
|
1393
|
+
)
|
|
1394
|
+
elapsed = _time.perf_counter() - t0
|
|
1395
|
+
if result is None:
|
|
1396
|
+
self.logger.debug("Native run failed (result None)")
|
|
1397
|
+
return None
|
|
1398
|
+
|
|
1399
|
+
self.logger.debug(
|
|
1400
|
+
f"Native result: status={result.get('status')} "
|
|
1401
|
+
f"halted={result.get('halted')} steps={result.get('steps')} "
|
|
1402
|
+
f"pc={result.get('pc')} elapsed={elapsed * 1000:.2f}ms "
|
|
1403
|
+
f"error={result.get('error')}")
|
|
1404
|
+
|
|
1405
|
+
if result.get('error') == 'unsupported':
|
|
1406
|
+
# 原生库遇到不支持的指令: 同步状态后回退解释执行
|
|
1407
|
+
self._apply_native_state(result)
|
|
1408
|
+
self.logger.debug(
|
|
1409
|
+
f"Native engine hit unsupported opcode, falling back to "
|
|
1410
|
+
f"interpreter at PC=0x{result.get('pc', 0):x}")
|
|
1411
|
+
return None
|
|
1412
|
+
|
|
1413
|
+
if result.get('error'):
|
|
1414
|
+
# 原生运行时错误 (status=3): 同步状态并按解释器行为抛出
|
|
1415
|
+
self._apply_native_state(result)
|
|
1416
|
+
self.native_used = True
|
|
1417
|
+
raise ExecutionError(result.get('error') or 'native runtime error')
|
|
1418
|
+
|
|
1419
|
+
# 应用原生执行结果
|
|
1420
|
+
self._apply_native_state(result)
|
|
1421
|
+
self.native_used = True
|
|
1422
|
+
if result.get('output'):
|
|
1423
|
+
self._emit_text(result['output'])
|
|
1424
|
+
return not result.get('halted', False) or result.get('error') is None
|
|
1425
|
+
|
|
1426
|
+
def _apply_native_state(self, result: dict) -> None:
|
|
1427
|
+
if 'regs' in result:
|
|
1428
|
+
self.regs.set_all(list(result['regs'])[:32])
|
|
1429
|
+
if 'sp' in result:
|
|
1430
|
+
self.sp = result['sp'] & MASK64
|
|
1431
|
+
if 'pc' in result:
|
|
1432
|
+
self.pc = result['pc']
|
|
1433
|
+
if 'vec_regs' in result:
|
|
1434
|
+
self.vec_regs.set_all(result['vec_regs'])
|
|
1435
|
+
if 'mem' in result:
|
|
1436
|
+
mem = result['mem']
|
|
1437
|
+
self.memory.write_block(0, bytes(mem[:len(self.memory)]))
|
|
1438
|
+
if 'steps' in result:
|
|
1439
|
+
# 原生 VM 已整程序执行完毕: 统计改为一次性批量写入, 不再按指令数在
|
|
1440
|
+
# Python 里逐条调用 record_instruction (实测可让原生路径快约 7 倍,
|
|
1441
|
+
# 且原写法算出的 opcode 直方图随后就被 clear 掉, 纯属浪费)。
|
|
1442
|
+
steps = int(result.get('steps') or 0)
|
|
1443
|
+
counted = min(steps, self.config.max_instructions)
|
|
1444
|
+
self.stats.instruction_count = steps
|
|
1445
|
+
self.stats.opcode_count.clear()
|
|
1446
|
+
self.stats.hot_instructions['?'] += counted
|
|
1447
|
+
self.stats.inst_profiler.cycles['?'] += counted
|
|
1448
|
+
self.stats.performance_counters.counters['instructions'] += counted
|
|
1449
|
+
if 'heap_ptr' in result:
|
|
1450
|
+
self.heap_ptr = result['heap_ptr']
|
|
1451
|
+
|
|
1452
|
+
def run(self) -> None:
|
|
1453
|
+
if self.config.debug_server_port is not None:
|
|
1454
|
+
return self._run_remote(self.config.debug_server_port)
|
|
1455
|
+
self.running = True
|
|
1456
|
+
self.stats.start()
|
|
1457
|
+
self.logger.info("Starting program execution")
|
|
1458
|
+
|
|
1459
|
+
native_outcome = None
|
|
1460
|
+
if (self.config.use_native and not self.config.debug_mode
|
|
1461
|
+
and not self.config.step_mode
|
|
1462
|
+
and not self.config.bounds_check
|
|
1463
|
+
and not self.config.mmu):
|
|
1464
|
+
native_outcome = self._try_native_run()
|
|
1465
|
+
|
|
1466
|
+
try:
|
|
1467
|
+
if native_outcome is not None:
|
|
1468
|
+
# 原生执行完成
|
|
1469
|
+
if not native_outcome:
|
|
1470
|
+
self.logger.info("HALT (native)")
|
|
1471
|
+
elif self._can_use_fast_path():
|
|
1472
|
+
self._run_fast()
|
|
1473
|
+
else:
|
|
1474
|
+
self._run_interpreted()
|
|
1475
|
+
except KeyboardInterrupt:
|
|
1476
|
+
self.logger.info("User interrupt")
|
|
1477
|
+
self.console.print(f"\n{Colors.colorize('User interrupt', Colors.YELLOW)}")
|
|
1478
|
+
except Exception as e:
|
|
1479
|
+
self.execution_failed = True
|
|
1480
|
+
if self.config.debug_mode:
|
|
1481
|
+
# 超详细: rich 彩色完整堆栈
|
|
1482
|
+
self.logger.exception(f"Execution error: {e}")
|
|
1483
|
+
else:
|
|
1484
|
+
self.logger.error(f"Execution error: {e}")
|
|
1485
|
+
self.console.print(Panel(
|
|
1486
|
+
f"{e}", title="Execution Error", border_style='red'))
|
|
1487
|
+
finally:
|
|
1488
|
+
self.running = False
|
|
1489
|
+
self.stats.stop()
|
|
1490
|
+
self.cache.flush()
|
|
1491
|
+
self.logger.info("Program execution finished")
|
|
1492
|
+
|
|
1493
|
+
if self.config.profile or self.config.debug_mode:
|
|
1494
|
+
self.display_state("Execution Complete")
|
|
1495
|
+
cache_stats = self.cache.get_stats()
|
|
1496
|
+
self.stats.display_summary(self.console, cache_stats,
|
|
1497
|
+
native_used=self.native_used)
|
|
1498
|
+
|
|
1499
|
+
if self.config.auto_save_crom:
|
|
1500
|
+
from . import crom as crom_mod
|
|
1501
|
+
crom_file = os.path.splitext(self.filename)[0] + '.crom' if self.filename else 'program.crom'
|
|
1502
|
+
crom_mod.save_crom(self.memory, crom_file,
|
|
1503
|
+
compress=self.config.compress_crom,
|
|
1504
|
+
logger=self.logger)
|
|
1505
|
+
|
|
1506
|
+
def _run_remote(self, port: int) -> None:
|
|
1507
|
+
"""A4: 远程驱动式调试 (单线程服务; 不启用原生/JIT/本地单步会话)。"""
|
|
1508
|
+
from .debugger import DebugServer
|
|
1509
|
+
self.running = False
|
|
1510
|
+
self.stats.start()
|
|
1511
|
+
self.logger.info(f"Remote debug server on port {port} "
|
|
1512
|
+
f"(waiting for client)")
|
|
1513
|
+
srv = DebugServer(self, port)
|
|
1514
|
+
self.debug_server = srv
|
|
1515
|
+
try:
|
|
1516
|
+
srv.drive()
|
|
1517
|
+
except KeyboardInterrupt:
|
|
1518
|
+
self.logger.info("User interrupt")
|
|
1519
|
+
finally:
|
|
1520
|
+
self.running = False
|
|
1521
|
+
self.stats.stop()
|
|
1522
|
+
self.cache.flush()
|
|
1523
|
+
self.logger.info("Remote debug session ended")
|
|
1524
|
+
|
|
1525
|
+
def _can_use_fast_path(self) -> bool:
|
|
1526
|
+
"""无调试/断点/单步/JIT/追踪/节流时, 可走紧凑解释循环。"""
|
|
1527
|
+
return (not self.config.debug_mode
|
|
1528
|
+
and not self.config.step_mode
|
|
1529
|
+
and not self.config.interactive_mode
|
|
1530
|
+
and not self.breakpoints
|
|
1531
|
+
and self.debug_server is None
|
|
1532
|
+
and self.jit is None
|
|
1533
|
+
and not self._trace
|
|
1534
|
+
and self.config.execution_interval <= 0)
|
|
1535
|
+
|
|
1536
|
+
def _run_fast(self) -> None:
|
|
1537
|
+
"""紧凑解释循环 (快路径)。
|
|
1538
|
+
|
|
1539
|
+
与 _run_interpreted 的语义差异仅在"没有"哪些检查: 断点命中、远程
|
|
1540
|
+
调试轮询、单步会话、JIT 尝试、逐指令追踪与执行节流。这些在快路径的
|
|
1541
|
+
启用条件里都已确认不生效, 因此逐指令语义 (pc 先自增、统计记账、HALT
|
|
1542
|
+
返回 False) 完全一致。
|
|
1543
|
+
"""
|
|
1544
|
+
instructions = self.instructions
|
|
1545
|
+
execute = self.execute
|
|
1546
|
+
logger = self.logger
|
|
1547
|
+
limit = self.config.max_instructions
|
|
1548
|
+
n = len(instructions)
|
|
1549
|
+
while True:
|
|
1550
|
+
pc = self.pc
|
|
1551
|
+
if pc < 0 or pc >= n:
|
|
1552
|
+
logger.info("Program ended normally")
|
|
1553
|
+
return
|
|
1554
|
+
if self.stats.instruction_count >= limit:
|
|
1555
|
+
raise ExecutionError(
|
|
1556
|
+
f"instruction limit reached ({limit} steps)")
|
|
1557
|
+
opcode, args = instructions[pc]
|
|
1558
|
+
if not execute(opcode, args):
|
|
1559
|
+
logger.info("HALT instruction executed")
|
|
1560
|
+
return
|
|
1561
|
+
|
|
1562
|
+
def _run_interpreted(self) -> None:
|
|
1563
|
+
while self.running:
|
|
1564
|
+
if self.pc < 0 or self.pc >= len(self.instructions):
|
|
1565
|
+
self.logger.info("Program ended normally")
|
|
1566
|
+
break
|
|
1567
|
+
|
|
1568
|
+
if self.stats.instruction_count >= self.config.max_instructions:
|
|
1569
|
+
# 与 Go 原生 VM 保持一致的语义: 步数用尽不是"正常结束",
|
|
1570
|
+
# 而是失败 (旧行为只是 warning + break, 会让被截断的程序
|
|
1571
|
+
# 以退出码 0 报告成功)。
|
|
1572
|
+
raise ExecutionError(
|
|
1573
|
+
f"instruction limit reached "
|
|
1574
|
+
f"({self.config.max_instructions} steps)")
|
|
1575
|
+
|
|
1576
|
+
# 建议 5: 'continue' 后豁免一次当前 PC 的断点命中,
|
|
1577
|
+
# 确保至少执行一条指令 (与 GDB 语义一致, 避免死循环重入)
|
|
1578
|
+
resume_bp = self._resume_bp_pc is not None
|
|
1579
|
+
if resume_bp:
|
|
1580
|
+
self._resume_bp_pc = None
|
|
1581
|
+
|
|
1582
|
+
if not resume_bp:
|
|
1583
|
+
if self.debug_server and self.debug_server.check_conditional_breakpoints():
|
|
1584
|
+
self.is_debugging = True
|
|
1585
|
+
self.debug_command_loop()
|
|
1586
|
+
continue
|
|
1587
|
+
|
|
1588
|
+
if self.pc in self.breakpoints:
|
|
1589
|
+
self.console.print(
|
|
1590
|
+
f"{Colors.colorize(f'Breakpoint hit at PC={self.pc:#x}', Colors.YELLOW)}")
|
|
1591
|
+
self.is_debugging = True
|
|
1592
|
+
self.debug_command_loop()
|
|
1593
|
+
continue
|
|
1594
|
+
|
|
1595
|
+
# 建议 6: --step 与断点命中共用 DebugSession 命令集
|
|
1596
|
+
if self.config.step_mode and self.config.interactive_mode:
|
|
1597
|
+
from .debugger import DebugSession
|
|
1598
|
+
action = DebugSession(self).run_step_mode()
|
|
1599
|
+
if action in ('quit', 'halted'):
|
|
1600
|
+
if action == 'quit':
|
|
1601
|
+
self.running = False
|
|
1602
|
+
break
|
|
1603
|
+
if action == 'run':
|
|
1604
|
+
self.config.step_mode = False
|
|
1605
|
+
continue
|
|
1606
|
+
|
|
1607
|
+
if self.jit is not None:
|
|
1608
|
+
jit_result = self.jit.try_step(self)
|
|
1609
|
+
if jit_result is True:
|
|
1610
|
+
continue
|
|
1611
|
+
if jit_result is False:
|
|
1612
|
+
self.logger.info("Program ended (JIT)")
|
|
1613
|
+
break
|
|
1614
|
+
# None: 该指令不适合 JIT, 回退解释执行
|
|
1615
|
+
|
|
1616
|
+
opcode, args = self.instructions[self.pc]
|
|
1617
|
+
continue_exec = self.execute(opcode, args)
|
|
1618
|
+
|
|
1619
|
+
if not continue_exec:
|
|
1620
|
+
self.logger.info("HALT instruction executed")
|
|
1621
|
+
break
|
|
1622
|
+
|
|
1623
|
+
if self.config.execution_interval > 0:
|
|
1624
|
+
time.sleep(self.config.execution_interval)
|
|
1625
|
+
|
|
1626
|
+
# ==================== 状态显示 / 调试 ====================
|
|
1627
|
+
|
|
1628
|
+
def display_state(self, title: str = "CPU State", opcode: Optional[str] = None,
|
|
1629
|
+
args: Optional[List[Operand]] = None) -> None:
|
|
1630
|
+
# 建议 6: 渲染逻辑迁移至 codecin/debugger.py (状态显示与执行解耦)
|
|
1631
|
+
from .debugger import display_state as _render_state
|
|
1632
|
+
_render_state(self, title, opcode, args)
|
|
1633
|
+
|
|
1634
|
+
@staticmethod
|
|
1635
|
+
def _fmt_operand(op: Operand) -> str:
|
|
1636
|
+
# 建议 6: 操作数格式化迁移至 codecin/debugger.py
|
|
1637
|
+
from .debugger import fmt_operand
|
|
1638
|
+
return fmt_operand(op)
|
|
1639
|
+
|
|
1640
|
+
def add_breakpoint(self, addr: int) -> None:
|
|
1641
|
+
self.breakpoints.add(addr)
|
|
1642
|
+
self.logger.info(f"Breakpoint set at PC={addr:#x}")
|
|
1643
|
+
|
|
1644
|
+
def remove_breakpoint(self, addr: int) -> None:
|
|
1645
|
+
self.breakpoints.discard(addr)
|
|
1646
|
+
self.logger.info(f"Breakpoint removed at PC={addr:#x}")
|
|
1647
|
+
|
|
1648
|
+
def debug_command_loop(self) -> None:
|
|
1649
|
+
"""断点命中后的交互调试入口 (建议 6: 逻辑已迁移至 codecin/debugger.DebugSession)。"""
|
|
1650
|
+
from .debugger import DebugSession
|
|
1651
|
+
action = DebugSession(self).run()
|
|
1652
|
+
if action == 'halted':
|
|
1653
|
+
self.running = False
|
|
1654
|
+
|