Files
dp32-proto/src/vm.py

257 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from dataclasses import dataclass, field
from typing import ClassVar, Callable
from ctypes import c_uint32, c_int32, c_uint8
import struct
from optable import OPCODES, OpcodeDescription, OpL, OpA, OpF, OpD
from enum import IntFlag, auto
class VMFlags(IntFlag):
AFTER_BRANCH = auto()
class Condition:
def __init__(self, cond: int):
self.i: bool = bool(cond & (1 << 3))
self.v: bool = bool(cond & (1 << 2))
self.n: bool = bool(cond & (1 << 1))
self.z: bool = bool(cond & (1 << 0))
class VMCC(IntFlag):
OVERFLOW = 1 << 2
NEGATIVE = 1 << 1
ZERO = 1 << 0
@dataclass
class VM:
instr_callbacks: ClassVar[dict[OpcodeDescription, Callable]]
def __init__(self, mem):
self.mem: bytearray = mem
self.cc: VMCC = VMCC(0)
self.pc: c_uint32 = c_uint32(0)
self.registers: list[c_int32] = [c_int32(0) for _ in range(256)]
self.breakpoints: set[int] = field(default_factory=set)
self._vm_flags: VMFlags = VMFlags(0)
self.__init_callbacks__()
def __init_callbacks__(self):
VM.instr_callbacks = {
# ariphmetic
OpD(OpF(0), OpL.MATH, OpA.ADD):
self._math_callback_gen(lambda lhs, rhs: lhs + rhs),
OpD(OpF.QUICK, OpL.MATH, OpA.ADD):
self._math_quick_callback_gen(lambda lhs, rhs: lhs + rhs),
OpD(OpF(0), OpL.MATH, OpA.SUB):
self._math_callback_gen(lambda lhs, rhs: lhs - rhs),
OpD(OpF.QUICK, OpL.MATH, OpA.SUB):
self._math_quick_callback_gen(lambda lhs, rhs: lhs - rhs),
OpD(OpF(0), OpL.MATH, OpA.MUL):
self._math_callback_gen(lambda lhs, rhs: lhs * rhs),
OpD(OpF.QUICK, OpL.MATH, OpA.MUL):
self._math_quick_callback_gen(lambda lhs, rhs: lhs * rhs),
OpD(OpF(0), OpL.MATH, OpA.DIV):
self._math_callback_gen(lambda lhs, rhs: lhs // rhs),
OpD(OpF.QUICK, OpL.MATH, OpA.DIV):
self._math_quick_callback_gen(lambda lhs, rhs: lhs // rhs),
# logical ops
OpD(OpF.UNEXPANDED, OpL.MATH, OpA.AND):
self._math_callback_gen(lambda lhs, rhs: lhs & rhs),
OpD(OpF.UNEXPANDED, OpL.MATH, OpA.OR):
self._math_callback_gen(lambda lhs, rhs: lhs | rhs),
OpD(OpF.UNEXPANDED, OpL.MATH, OpA.XOR):
self._math_callback_gen(lambda lhs, rhs: lhs ^ rhs),
OpD(OpF.UNEXPANDED, OpL.MATH, OpA.MASK):
self._math_callback_gen(
lambda lhs, rhs: lhs & ((~rhs + (1 << 32)) % (1 << 32))
),
# block 2
OpD(OpF(0), OpL.MEM, OpA.LOAD):
self._load_callback,
OpD(OpF.QUICK, OpL.MEM, OpA.LOAD):
self._load_callback,
OpD(OpF(0), OpL.MEM, OpA.STORE):
self._store_callback,
OpD(OpF.QUICK, OpL.MEM, OpA.STORE):
self._store_callback,
# block 3
OpD(OpF(0), OpL.BRANCH, OpA.BRANCH):
self._branch_callback,
OpD(OpF.QUICK, OpL.BRANCH, OpA.BRANCH):
self._branch_callback,
OpD(OpF(0), OpL.BRANCH, OpA.IND_BRANCH):
self._branch_indexed_callback
}
def step(self) -> None:
"""
Make one step (only step into)
"""
# По какой-то причине адрессация работает
# так, будто мы на 1 слово впереди опкода
if not VMFlags.AFTER_BRANCH:
self.pc = c_uint32(self.cc.value + 1)
# сбрасываем флаг AFTER_BRANCH
self._vm_flags &= ~(VMFlags.AFTER_BRANCH)
opcode = self.mem[self.pc.value]
opdesc = self._fetch_opcode_desc(opcode)
args = self._parse_arguments(opdesc)
self._run_callback(opdesc, args)
def continue_(self) -> None:
"""
Continue from current breakpoint
"""
while ((self.pc.value < len(self.mem) // 4)
and not self.pc.value in self.breakpoints):
self.step()
print("breakpoint")
input()
def run(self) -> None:
"""
Run from very beginning
"""
self.pc = c_uint32(0)
while (self.pc.value < len(self.mem) // 4):
self.continue_()
def _fetch_opcode_desc(self, opcode: int):
return OPCODES[opcode]
def _parse_arguments(self, opdesc: OpcodeDescription) -> tuple[int, ...]:
addr = self.pc.value
main_part = struct.unpack(">BBBb", self.mem[addr:addr+4])
if not OpF.UNEXPANDED in opdesc.flags or OpF.QUICK in opdesc.flags:
upper_part = struct.unpack(">i", self.mem[addr+4:addr+8])
return (*main_part, *upper_part)
return main_part
def _run_callback(
self,
opdesc: OpcodeDescription,
args: tuple[int, ...]
) -> None:
if opdesc.layout == OpL.MATH:
assert len(args) == 4
_, r3, r1, r2_or_i8 = args
self.instr_callbacks[opdesc](
r3, r1, r2_or_i8
)
if opdesc.layout == OpL.MEM:
if OpF.QUICK in opdesc.flags:
assert len(args) == 4
_, r3, r1, i8 = args
self.instr_callbacks[opdesc](
r3, r1, i8
)
else:
assert len(args) == 5
_, r3, r1, _, disp = args
self.instr_callbacks[opdesc](
r3, r1, disp
)
if opdesc.layout == OpL.BRANCH:
if OpF.QUICK in opdesc.flags:
assert len(args) == 4
_, cond, _, i8 = args
self.instr_callbacks[opdesc](
cond, i8
)
elif opdesc.action == OpA.IND_BRANCH:
assert len(args) == 5
_, cond, r1, _, disp = args
self.instr_callbacks[opdesc](
cond, r1, disp
)
else:
assert len(args) == 5
_, cond, _, _, disp = args
self.instr_callbacks[opdesc](
cond, disp
)
def _math_callback_gen(
self,
operation: Callable[[int, int], int]
) -> Callable[["VM", int, int ,int], None]:
"""
Поскольку математические операции конструируются
по одному шаблону, я завел функцию высшего порядка,
которая будет их генерировать
"""
def callback(self, r3: int, r1: int, r2: int):
lhs = self.registers[r1].value
rhs = self.registers[r2].value
self.registers[r3] = c_int32(operation(lhs, rhs))
return callback
def _math_quick_callback_gen(
self,
operation: Callable[[int, int], int]
) -> Callable[["VM", int, int ,int], None]:
"""
Поскольку математические операции конструируются
по одному шаблону, я завел функцию высшего порядка,
которая будет их генерировать.
В отлиие от _math_callback_gen, эта уже предназначена для
операций с пометкой QUICK
"""
def callback(self, r3: int, r1: int, i8: int) -> None:
self.cc = VMCC(0)
lhs = self.registers[r1].value
result = operation(lhs, i8)
if result < 0:
self.cc |= VMCC.NEGATIVE
elif result == 0:
self.cc |= VMCC.ZERO
# самая дорогая проверка на переполнение)
try:
struct.pack('i', result)
except struct.error:
self.cc |= VMCC.OVERFLOW
self.registers[r3] = c_int32(result)
return callback
def _load_callback(self, r3: int, r1: int, disp: int) -> None:
addr = (self.registers[r1].value + disp) * 4
self.registers[r3] = c_int32(
struct.unpack(">i", self.mem[addr:addr+4])[0]
)
def _store_callback(self, r3: int, r1: int, disp: int) -> None:
addr = (self.registers[r1].value + disp) * 4
self.mem[addr:addr+4] = struct.pack(">i", self.registers[r3].value)
def _branch_callback(self, cond: int, disp: int) -> None:
c = Condition(cond)
vm_c = Condition(self.cc.value)
if (c.v & vm_c.v) & (c.n & vm_c.n) & (c.z & vm_c.z) == c.i:
self._vm_flags |= VMFlags.AFTER_BRANCH
self.pc = c_uint32(self.pc.value + disp)
def _branch_indexed_callback(self, cond: int, r1: int, disp: int) -> None:
c = Condition(cond)
vm_c = Condition(self.cc.value)
if (c.v & vm_c.v) & (c.n & vm_c.n) & (c.z & vm_c.z) == c.i:
self._vm_flags |= VMFlags.AFTER_BRANCH
addr = self.registers[r1].value + disp
self.pc = c_uint32(addr)