diff --git a/src/__pycache__/optable.cpython-311.pyc b/src/__pycache__/optable.cpython-311.pyc new file mode 100644 index 0000000..5d000cd Binary files /dev/null and b/src/__pycache__/optable.cpython-311.pyc differ diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..89cd420 --- /dev/null +++ b/src/main.py @@ -0,0 +1,6 @@ +def main(): + print("Hello from dp32-proto!") + + +if __name__ == "__main__": + main() diff --git a/src/optable.py b/src/optable.py new file mode 100644 index 0000000..8daa5dd --- /dev/null +++ b/src/optable.py @@ -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) diff --git a/src/vm.py b/src/vm.py new file mode 100644 index 0000000..d603b2d --- /dev/null +++ b/src/vm.py @@ -0,0 +1,172 @@ + +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) + if not VMFlags.AFTER_BRANCH: + self.pc = c_uint32(self.cc.value + 1) + self._vm_flags + + def continue_(self) -> None: + """ + Continue from current breakpoint + """ + pass + + def run(self) -> None: + """ + Run from very beginning + """ + + def _fetch_opcode_desc(self, opcode: int): + return OPCODES[opcode] + + 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)