fix: полностью переработана система изменения счетчика

Выяснилось, что в DP32 счетчик команд ведет себя совершенно не так, как у меня в виртуальной машине, поэтому логика его работы была полностью переписана
This commit is contained in:
ElectronixTM
2025-03-30 11:54:50 +03:00
parent ca2e19ae0a
commit 9392326337

View File

@ -1,10 +1,12 @@
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import ClassVar, Callable from typing import ClassVar, Callable
from ctypes import c_uint32, c_int32 from ctypes import c_uint32, c_int32
import struct import struct
from optable import OPCODES, OpcodeDescription, OpL, OpA, OpF, OpD from optable import OPCODES, OpcodeDescription, OpL, OpA, OpF, OpD
from enum import IntFlag, Enum, auto from enum import IntFlag, Enum, auto
WORD_SIZE: int = 4
class VMFlags(IntFlag): class VMFlags(IntFlag):
# if last instruction is big, then you should # if last instruction is big, then you should
# skip more memory on step # skip more memory on step
@ -120,21 +122,19 @@ class VM:
""" """
Make one step (only step into) Make one step (only step into)
""" """
if self.pc.value * 4 > len(self.mem) - 4: if self._to_raw_bytes_offset(self.pc) > len(self.mem) - WORD_SIZE:
raise VMException( raise VMException(
VMExceptionType.END_OF_MEM, VMExceptionType.END_OF_MEM,
self.pc.value self.pc.value
) )
opcode = self.mem[self.pc.value * 4] opcode, *_ = instr = self._fetch_instr()
opdesc = self._fetch_opcode_desc(opcode) opdesc = self._get_opcode_desc(opcode)
args = self._parse_arguments(opdesc) args: tuple[int, ...] = self._parse_instr_fields(bytes(instr))
# По какой-то причине адрессация работает if not (OpF.UNEXPANDED in opdesc.flags or
# так, будто мы на 1 слово впереди опкода OpF.QUICK in opdesc.flags):
self.pc = c_uint32(self.pc.value + 1) extra = struct.unpack(">i", self._fetch_instr())[0]
args = (*args, extra)
self._run_callback(opdesc, args) self._run_callback(opdesc, args)
if (VMFlags.EXPANDED_INSTR in self._vm_flags
and not VMFlags.AFTER_BRANCH in self._vm_flags):
self.pc = c_uint32(self.pc.value + 1)
self._vm_flags &= ~(VMFlags.EXPANDED_INSTR) self._vm_flags &= ~(VMFlags.EXPANDED_INSTR)
self._vm_flags &= ~(VMFlags.AFTER_BRANCH) self._vm_flags &= ~(VMFlags.AFTER_BRANCH)
@ -142,7 +142,7 @@ class VM:
""" """
Continue from current breakpoint Continue from current breakpoint
""" """
while self.pc.value * 4 < len(self.mem): while self._to_raw_bytes_offset(self.pc.value) < len(self.mem):
if self.pc.value in self.breakpoints: if self.pc.value in self.breakpoints:
raise Breakpoint(self.pc.value) raise Breakpoint(self.pc.value)
self.step() self.step()
@ -152,10 +152,26 @@ class VM:
Run from very beginning Run from very beginning
""" """
self.pc = c_uint32(0) self.pc = c_uint32(0)
while (self.pc.value < len(self.mem) // 4): while (self._to_raw_bytes_offset(self.pc.value) < len(self.mem)):
self.continue_() self.continue_()
def _fetch_opcode_desc(self, opcode: int): def _fetch_instr(self) -> bytearray:
"""
Берет из памяти следущее слово, возвращает его
ВАЖНО: инкрементирует счетчик команд на каждом вызове
"""
addr = self._to_raw_bytes_offset(self.pc)
instr = self.mem[addr:addr+WORD_SIZE]
self._increment_pc()
return instr
def _get_opcode_desc(self, opcode: int):
"""
Обертка над обращением к таблице опкодов из файлика optable.py.
Нужна чтобы прокидывать исключения виртуальной машины, а не
KeyError
"""
if not opcode in OPCODES: if not opcode in OPCODES:
raise VMException( raise VMException(
VMExceptionType.INVALID_OPCODE, VMExceptionType.INVALID_OPCODE,
@ -163,14 +179,20 @@ class VM:
) )
return OPCODES[opcode] return OPCODES[opcode]
def _parse_arguments(self, opdesc: OpcodeDescription) -> tuple[int, ...]: def _to_raw_bytes_offset(self, pointer: int | c_uint32 | c_int32) -> int:
addr = self.pc.value * 4 """
main_part = struct.unpack(">BBBb", self.mem[addr:addr+4]) Переводит адресс из указателей в рамках процессора, которые
являются 32-битными, в формат смещения в сырых байтах в памяти
"""
if isinstance(pointer, int):
return pointer * WORD_SIZE
return pointer.value * WORD_SIZE
if OpF.UNEXPANDED in opdesc.flags or OpF.QUICK in opdesc.flags: def _parse_instr_fields(
return main_part self,
upper_part = struct.unpack(">i", self.mem[addr+4:addr+8]) instr: bytes
return (*main_part, *upper_part) ) -> tuple[int, int, int, int]:
return struct.unpack(">BBBb", instr)
def _run_callback( def _run_callback(
self, self,
@ -267,14 +289,18 @@ class VM:
return callback return callback
def _load_callback(self, r3: int, r1: int, disp: int) -> None: def _load_callback(self, r3: int, r1: int, disp: int) -> None:
addr = (self.registers[r1].value + disp) * 4 addr = self._to_raw_bytes_offset(self.registers[r1].value + disp)
self.registers[r3] = c_int32( self.registers[r3] = c_int32(
struct.unpack(">i", self.mem[addr:addr+4])[0] struct.unpack(
">i", self.mem[addr:addr+WORD_SIZE])[0]
) )
def _store_callback(self, r3: int, r1: int, disp: int) -> None: def _store_callback(self, r3: int, r1: int, disp: int) -> None:
addr = (self.registers[r1].value + disp) * 4 addr = self._to_raw_bytes_offset(self.registers[r1].value + disp)
self.mem[addr:addr+4] = struct.pack(">i", self.registers[r3].value) self.mem[addr:addr+WORD_SIZE] = struct.pack(
">i",
self.registers[r3].value
)
def _branch_callback(self, cond: int, disp: int) -> None: def _branch_callback(self, cond: int, disp: int) -> None:
c = Condition(cond) c = Condition(cond)
@ -291,3 +317,6 @@ class VM:
addr = self.registers[r1].value + disp addr = self.registers[r1].value + disp
self.pc = c_uint32(addr) self.pc = c_uint32(addr)
def _increment_pc(self):
self.pc = c_uint32(self.pc.value + 1)