Compare commits

..

2 Commits

4 changed files with 279 additions and 0 deletions

Binary file not shown.

6
src/main.py Normal file
View File

@ -0,0 +1,6 @@
def main():
print("Hello from dp32-proto!")
if __name__ == "__main__":
main()

71
src/optable.py Normal file
View File

@ -0,0 +1,71 @@
from dataclasses import dataclass
from enum import Flag, Enum, auto
class OpcodeFlags(Flag):
QUICK = auto()
# Форсирует чтение не более 1 слова
# в тех случаях, когда это не требуется
# по характеру действия
UNEXPANDED = auto()
OpF = OpcodeFlags
class OpcodeLayout(Enum):
MATH = auto()
BRANCH = auto()
MEM = auto()
OpL = OpcodeLayout
class OpcodeActions(Enum):
ADD = auto()
SUB = auto()
MUL = auto()
DIV = auto()
AND = auto()
OR = auto()
XOR = auto()
MASK = auto()
LOAD = auto()
STORE = auto()
BRANCH = auto()
MEM_BRANCH = auto()
OpA = OpcodeActions
@dataclass(frozen=True)
class OpcodeDescription:
flags: OpcodeFlags
layout: OpcodeLayout
action: OpcodeActions
OpD = OpcodeDescription
OPCODES = {
# block 1
0x00: OpD(OpF(0), OpL.MATH, OpA.ADD),
0x10: OpD(OpF.QUICK, OpL.MATH, OpA.ADD),
0x01: OpD(OpF(0), OpL.MATH, OpA.SUB),
0x11: OpD(OpF.QUICK, OpL.MATH, OpA.SUB),
0x02: OpD(OpF(0), OpL.MATH, OpA.MUL),
0x12: OpD(OpF.QUICK, OpL.MATH, OpA.MUL),
0x03: OpD(OpF(0), OpL.MATH, OpA.DIV),
0x13: OpD(OpF.QUICK, OpL.MATH, OpA.DIV),
0x04: OpD(OpF.UNEXPANDED, OpL.MATH, OpA.AND),
0x05: OpD(OpF.UNEXPANDED, OpL.MATH, OpA.OR),
0x06: OpD(OpF.UNEXPANDED, OpL.MATH, OpA.XOR),
0x07: OpD(OpF.UNEXPANDED, OpL.MATH, OpA.MASK),
# block 2
0x20: OpD(OpF(0), OpL.MEM, OpA.LOAD),
0x30: OpD(OpF.QUICK, OpL.MEM, OpA.LOAD),
0x21: OpD(OpF(0), OpL.MEM, OpA.STORE),
0x31: OpD(OpF.QUICK, OpL.MEM, OpA.STORE),
# block 3
0x40: OpD(OpF(0), OpL.BRANCH, OpA.BRANCH),
0x50: OpD(OpF.QUICK, OpL.BRANCH, OpA.BRANCH),
0x51: OpD(OpF(0), OpL.BRANCH, OpA.MEM_BRANCH),
}
if __name__ == "__main__":
print(hex(12))
print(~12)

202
src/vm.py Normal file
View File

@ -0,0 +1,202 @@
from dataclasses import dataclass, field
from typing import ClassVar, Callable, Any
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))
@dataclass
class VM:
instr_callbacks: ClassVar[dict[OpcodeDescription, Callable]]
def __init__(self, mem):
self.mem: bytearray = mem
self.cc: c_uint8 = c_uint8(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.MEM_BRANCH):
self._branch_indexed_callback
}
def step(self) -> None:
"""
Make one step (only step into)
"""
opcode = self.mem[self.pc.value]
opdesc = self._fetch_opcode_desc(opcode)
args = self._parse_arguments(opdesc)
if not VMFlags.AFTER_BRANCH:
self.pc = c_uint32(self.cc.value + 1)
self._vm_flags &= ~(VMFlags.AFTER_BRANCH)
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
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:
lhs = self.registers[r1].value
self.registers[r3] = c_int32(operation(lhs, i8))
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)