PortfolioAI
Add packed memory routing and 16-bit addressing
ea46629
raw
history blame
4.04 kB
"""
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: # HALT
return state.copy()
s = state.copy()
# Fetch: two bytes, big-endian
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: # ADD
result, carry, overflow = _alu_add(a, b)
elif opcode == 0x1: # SUB
result, carry, overflow = _alu_sub(a, b)
elif opcode == 0x2: # AND
result = a & b
elif opcode == 0x3: # OR
result = a | b
elif opcode == 0x4: # XOR
result = a ^ b
elif opcode == 0x5: # SHL
carry = 1 if (a & 0x80) else 0
result = (a << 1) & 0xFF
elif opcode == 0x6: # SHR
carry = 1 if (a & 0x01) else 0
result = (a >> 1) & 0xFF
elif opcode == 0x7: # MUL
full = a * b
result = full & 0xFF
carry = 1 if full > 0xFF else 0
elif opcode == 0x8: # DIV
if b == 0:
result = 0
carry = 1
overflow = 1
else:
result = (a // b) & 0xFF
elif opcode == 0x9: # CMP
result, carry, overflow = _alu_sub(a, b)
write_result = False
elif opcode == 0xA: # LOAD
result = s.mem[addr16]
elif opcode == 0xB: # STORE
s.mem[addr16] = b & 0xFF
write_result = False
elif opcode == 0xC: # JMP
s.pc = addr16 & 0xFFFF
write_result = False
elif opcode == 0xD: # JZ
if s.flags[0] == 1:
s.pc = addr16 & 0xFFFF
else:
s.pc = next_pc_ext
write_result = False
elif opcode == 0xE: # CALL
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: # HALT
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