fix: полностью переработана система изменения счетчика
Выяснилось, что в DP32 счетчик команд ведет себя совершенно не так, как у меня в виртуальной машине, поэтому логика его работы была полностью переписана
This commit is contained in:
79
src/vm.py
79
src/vm.py
@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user