| """ |
| Reference CPU cycle (software) for the threshold computer. |
| Implements fetch/decode/execute over the state layout. |
| """ |
|
|
| from __future__ import annotations |
|
|
| from typing import Tuple |
|
|
| from .state import CPUState |
|
|
|
|
| def decode_ir(ir: int) -> Tuple[int, int, int, int]: |
| opcode = (ir >> 12) & 0xF |
| rd = (ir >> 10) & 0x3 |
| rs = (ir >> 8) & 0x3 |
| imm8 = ir & 0xFF |
| return opcode, rd, rs, imm8 |
|
|
|
|
| def _flags_from_result(result: int, carry: int, overflow: int) -> Tuple[int, int, int, int]: |
| z = 1 if result == 0 else 0 |
| n = 1 if (result & 0x80) else 0 |
| c = 1 if carry else 0 |
| v = 1 if overflow else 0 |
| return z, n, c, v |
|
|
|
|
| def _alu_add(a: int, b: int) -> Tuple[int, int, int]: |
| full = a + b |
| result = full & 0xFF |
| carry = 1 if full > 0xFF else 0 |
| overflow = 1 if (((a ^ result) & (b ^ result)) & 0x80) else 0 |
| return result, carry, overflow |
|
|
|
|
| def _alu_sub(a: int, b: int) -> Tuple[int, int, int]: |
| full = (a - b) & 0x1FF |
| result = full & 0xFF |
| carry = 1 if a >= b else 0 |
| overflow = 1 if (((a ^ b) & (a ^ result)) & 0x80) else 0 |
| return result, carry, overflow |
|
|
|
|
| def step(state: CPUState) -> CPUState: |
| if state.ctrl[0] == 1: |
| return state.copy() |
|
|
| s = state.copy() |
|
|
| |
| hi = s.mem[s.pc] |
| lo = s.mem[(s.pc + 1) & 0xFFFF] |
| s.ir = ((hi & 0xFF) << 8) | (lo & 0xFF) |
| next_pc = (s.pc + 2) & 0xFFFF |
|
|
| opcode, rd, rs, imm8 = decode_ir(s.ir) |
| a = s.regs[rd] |
| b = s.regs[rs] |
|
|
| addr16 = None |
| next_pc_ext = next_pc |
| if opcode in (0xA, 0xB, 0xC, 0xD, 0xE): |
| addr_hi = s.mem[next_pc] |
| addr_lo = s.mem[(next_pc + 1) & 0xFFFF] |
| addr16 = ((addr_hi & 0xFF) << 8) | (addr_lo & 0xFF) |
| next_pc_ext = (next_pc + 2) & 0xFFFF |
|
|
| write_result = True |
| result = a |
| carry = 0 |
| overflow = 0 |
|
|
| if opcode == 0x0: |
| result, carry, overflow = _alu_add(a, b) |
| elif opcode == 0x1: |
| result, carry, overflow = _alu_sub(a, b) |
| elif opcode == 0x2: |
| result = a & b |
| elif opcode == 0x3: |
| result = a | b |
| elif opcode == 0x4: |
| result = a ^ b |
| elif opcode == 0x5: |
| carry = 1 if (a & 0x80) else 0 |
| result = (a << 1) & 0xFF |
| elif opcode == 0x6: |
| carry = 1 if (a & 0x01) else 0 |
| result = (a >> 1) & 0xFF |
| elif opcode == 0x7: |
| full = a * b |
| result = full & 0xFF |
| carry = 1 if full > 0xFF else 0 |
| elif opcode == 0x8: |
| if b == 0: |
| result = 0 |
| carry = 1 |
| overflow = 1 |
| else: |
| result = (a // b) & 0xFF |
| elif opcode == 0x9: |
| result, carry, overflow = _alu_sub(a, b) |
| write_result = False |
| elif opcode == 0xA: |
| result = s.mem[addr16] |
| elif opcode == 0xB: |
| s.mem[addr16] = b & 0xFF |
| write_result = False |
| elif opcode == 0xC: |
| s.pc = addr16 & 0xFFFF |
| write_result = False |
| elif opcode == 0xD: |
| if s.flags[0] == 1: |
| s.pc = addr16 & 0xFFFF |
| else: |
| s.pc = next_pc_ext |
| write_result = False |
| elif opcode == 0xE: |
| ret_addr = next_pc_ext & 0xFFFF |
| s.sp = (s.sp - 1) & 0xFFFF |
| s.mem[s.sp] = (ret_addr >> 8) & 0xFF |
| s.sp = (s.sp - 1) & 0xFFFF |
| s.mem[s.sp] = ret_addr & 0xFF |
| s.pc = addr16 & 0xFFFF |
| write_result = False |
| elif opcode == 0xF: |
| s.ctrl[0] = 1 |
| write_result = False |
|
|
| if opcode <= 0x9 or opcode in (0xA, 0x7, 0x8): |
| s.flags = list(_flags_from_result(result, carry, overflow)) |
|
|
| if write_result: |
| s.regs[rd] = result & 0xFF |
|
|
| if opcode not in (0xC, 0xD, 0xE): |
| s.pc = next_pc_ext |
|
|
| return s |
|
|
|
|
| def run_until_halt(state: CPUState, max_cycles: int = 256) -> Tuple[CPUState, int]: |
| s = state.copy() |
| for i in range(max_cycles): |
| if s.ctrl[0] == 1: |
| return s, i |
| s = step(s) |
| return s, max_cycles |
|
|