]> git.the-white-hart.net Git - vhdl/commitdiff
Add emulator with mul_uu test
authorrs <>
Fri, 10 Oct 2025 05:41:21 +0000 (00:41 -0500)
committerrs <>
Fri, 10 Oct 2025 05:41:21 +0000 (00:41 -0500)
projects/cpu_0/asm/emu.py [new file with mode: 0755]

diff --git a/projects/cpu_0/asm/emu.py b/projects/cpu_0/asm/emu.py
new file mode 100755 (executable)
index 0000000..8cc6832
--- /dev/null
@@ -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())