From ccd387d4fb5802882544c0538f739d16c0bb9e51 Mon Sep 17 00:00:00 2001 From: rs <> Date: Fri, 10 Oct 2025 00:41:21 -0500 Subject: [PATCH] Add emulator with mul_uu test --- projects/cpu_0/asm/emu.py | 250 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100755 projects/cpu_0/asm/emu.py diff --git a/projects/cpu_0/asm/emu.py b/projects/cpu_0/asm/emu.py new file mode 100755 index 0000000..8cc6832 --- /dev/null +++ b/projects/cpu_0/asm/emu.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 + +from __future__ import annotations +from typing import * + +# ------------------------------------------------------------------------------ + + +class Cpu0(object): + INSTRS = { + 0x00: 'NOP', 0x01: '#8', 0x02: '#32', 0x03: '@8', + 0x04: '@32', 0x05: '!8', 0x06: '!32', 0x07: 'r@', + 0x08: '>r', 0x09: 'r>', 0x0a: 'CALL', 0x0b: ';', + 0x0c: 'JMP', 0x0d: 'JZ', 0x0e: 'JN', 0x0f: 'JP', + 0x10: '+', 0x11: '-', 0x12: '&', 0x13: '|', + 0x14: '^', 0x15: '~', 0x16: 'DUP', 0x17: 'DROP', + 0x18: 'SWAP', 0x19: 'IEN', 0x1a: 'IDIS', 0x1b: 'LSR', + 0x1c: 'ASR', 0x1d: 'SHL', + } + + def __init__(self): + self._pc = 0 + self._param_stack = list() + self._return_stack = list() + self._regions = dict() + self._syms = list() + + def load_bin(self, data: bytes, offset: int = 0): + for i, b in enumerate(data): + self.st8(offset + i, b) + + def load_map(self, filename: str): + with open(filename, 'r') as f: + for line in f: + saddr, symbol = line.split(' ', maxsplit=1) + addr = int(saddr, 0) + self._syms.append((addr, symbol.strip())) + + def addr_to_symbol(self, address: int) -> str: + # Assumes symbols are sorted + found_symbol = '' + found_offset = 0 + for sym_addr, symbol in self._syms: + if address >= sym_addr: + found_symbol = symbol + found_offset = address - sym_addr + return f'{found_symbol}+0x{found_offset:x}' + + def symbol_to_addr(self, symbol: str) -> int: + for sym_addr, found_symbol in self._syms: + if found_symbol == symbol: + return sym_addr + assert False + + def p_push(self, value: int) -> None: + self._param_stack.append(value & 0xffffffff) + + def p_pop(self) -> int: + return self._param_stack.pop() + + def p_peek(self) -> int: + return self._param_stack[-1] + + def r_push(self, value: int) -> None: + self._return_stack.append(value & 0xffffffff) + + def r_pop(self) -> int: + return self._return_stack.pop() + + def r_peek(self) -> int: + return self._return_stack[-1] + + def ld8(self, address: int) -> int: + aligned = address & ~0xfff + if aligned not in self._regions: + return 0 + return self._regions[aligned][address & 0xfff] + + def st8(self, address: int, value: int) -> None: + aligned = address & ~0xfff + if aligned not in self._regions: + self._regions[aligned] = bytearray(0x1000) + self._regions[aligned][address & 0xfff] = value + + def ld32(self, address: int) -> int: + a = self.ld8(address + 0) + b = self.ld8(address + 1) + c = self.ld8(address + 2) + d = self.ld8(address + 3) + return (d << 24) | (c << 16) | (b << 8) | (a << 0) + + def st32(self, address: int, value: int) -> None: + a = (value >> 0) & 0xff + b = (value >> 8) & 0xff + c = (value >> 16) & 0xff + d = (value >> 24) & 0xff + self.st8(address + 0, a) + self.st8(address + 1, b) + self.st8(address + 2, c) + self.st8(address + 3, d) + + @property + def pc(self) -> int: + return self._pc + + @pc.setter + def pc(self, value: int | str) -> None: + if isinstance(value, str): + self._pc = self.symbol_to_addr(value) + else: + self._pc = value & 0xffffffff + + def run_until(self, addr: int | str): + if isinstance(addr, str): + addr = self.symbol_to_addr(addr) + while self.pc != addr: + self.step() + + def step(self) -> None: + instr = self.ld8(self.pc) + self.pc += 1 + if instr == 0x01: # #8 + self.p_push(self.ld8(self.pc)) + self.pc += 1 + elif instr == 0x02: # #32 + self.p_push(self.ld32(self.pc)) + self.pc += 4 + elif instr == 0x03: # @8 + addr = self.p_pop() + self.p_push(self.ld8(addr)) + elif instr == 0x04: # @32 + addr = self.p_pop() & ~0x3 + self.p_push(self.ld32(addr)) + elif instr == 0x05: # !8 + addr = self.p_pop() + val = self.p_peek() & 0xff + self.st8(addr, val) + elif instr == 0x06: # !32 + addr = self.p_pop() & ~0x3 + val = self.p_peek() + self.st32(addr, val) + elif instr == 0x07: # r@ + self.p_push(self.r_peek()) + elif instr == 0x08: # >r + self.r_push(self.p_pop()) + elif instr == 0x09: # r> + self.p_push(self.r_pop()) + elif instr == 0x0a: # CALL + addr = self.ld32(self.pc) + self.pc += 4 + self.r_push(self.pc) + self.pc = addr + elif instr == 0x0b: # ; + self.pc = self.r_pop() + elif instr == 0x0c: # JMP + addr = self.ld32(self.pc) + self.pc = addr + elif instr == 0x0d: # JZ + addr = self.ld32(self.pc) + self.pc += 4 + if self.p_pop() == 0: + self.pc = addr + elif instr == 0x0e: # JN + addr = self.ld32(self.pc) + self.pc += 4 + if self.p_pop() & 0x80000000 != 0: + self.pc = addr + elif instr == 0x0f: # JP + addr = self.ld32(self.pc) + self.pc += 4 + if self.p_pop() > 0: + self.pc = addr + elif instr == 0x10: # + + self.p_push(self.p_pop() + self.p_pop()) + elif instr == 0x11: # - + self.p_push(self.p_pop() - self.p_pop()) + elif instr == 0x12: # & + self.p_push(self.p_pop() & self.p_pop()) + elif instr == 0x13: # | + self.p_push(self.p_pop() | self.p_pop()) + elif instr == 0x14: # ^ + self.p_push(self.p_pop() ^ self.p_pop()) + elif instr == 0x15: # ~ + self.p_push(~self.p_pop()) + elif instr == 0x16: # DUP + self.p_push(self.p_peek()) + elif instr == 0x17: # DROP + self.p_pop() + elif instr == 0x18: # SWAP + a = self.p_pop() + b = self.p_pop() + self.p_push(a) + self.p_push(b) + elif instr == 0x1b: # LSR + val = self.p_pop() + self.p_push(val >> 1) + elif instr == 0x1c: # ASR + val = self.p_pop() + self.p_push((val >> 1) | (val & 0x80000000)) + elif instr == 0x1d: # SHL + val = self.p_pop() + self.p_push(val << 1) + else: # NOP + pass + + def dump(self) -> None: + print('-'*80) + print(f'0x{self.pc:08x} {self.addr_to_symbol(self.pc)}') + instr = self.ld8(self.pc) + sinstr = self.INSTRS[instr] if instr in self.INSTRS else '???' + print(f' (0x{instr:02x} {sinstr})') + print() + print('Data stack:') + for x in reversed(self._param_stack): + print(f' 0x{x:08x} {self.addr_to_symbol(x)}') + print() + print('Return stack:') + for x in reversed(self._return_stack): + print(f' 0x{x:08x} {self.addr_to_symbol(x)}') + print() + + +# ------------------------------------------------------------------------------ + + +def main() -> int: + binary = './writer.bin' + mapfile = './writer.map' + + emu = Cpu0() + with open(binary, 'rb') as f: + data = f.read() + emu.load_bin(data) + emu.load_map(mapfile) + + emu.dump() + emu.r_push(0xdeadbeef) + emu.p_push(0x1234) + emu.p_push(0x567) + emu.pc = 'mul_uu' + emu.dump() + + emu.run_until(0xdeadbeef) + emu.dump() + + return 0 + + +if __name__ == '__main__': + exit(main()) -- 2.43.0