gigatron/rom/Contrib/tbraun-de/pytron/cpu.py
2025-01-28 19:17:01 +03:00

185 lines
5.3 KiB
Python

from random import randint
import sys
class CpuState:
def __init__(self):
self.pc = randint(0, 255)
self.ir = randint(0, 255)
self.d = randint(0, 255)
self.ac = randint(0, 255)
self.x = randint(0, 255)
self.y = randint(0, 255)
self.out = randint(0, 255)
self.undef = randint(0, 255)
def copyTo(self, destination):
destination.pc = self.pc
destination.ir = self.ir
destination.d = self.d
destination.ac = self.ac
destination.x = self.x
destination.y = self.y
destination.out = self.out
destination.undef = self.undef
class Cpu():
def __init__(self):
self.rom = [[randint(0, 255) for x in range(2)] for y in range(1 << 16)]
self.ram = [randint(0, 255) for x in range(1 << 15)]
self.IN = 0xff
self.tempState = CpuState()
def cpuCycle(self, state: CpuState):
state.copyTo(self.tempState)
self.tempState.ir = self.rom[state.pc][0]
self.tempState.d = self.rom[state.pc][1]
instruction = state.ir >> 5
mod = (state.ir >> 2) & 7
bus = state.ir & 3
w = (instruction == 6)
j = (instruction == 7)
lo = state.d
hi = 0
to = None
inc_x = False
if not j:
if mod == 0:
to = "AC" if w is False else None
elif mod == 1:
to = "AC" if w is False else None
lo = state.x
elif mod == 2:
to = "AC" if w is False else None
hi = state.y
elif mod == 3:
to = "AC" if w is False else None
lo = state.x
hi = state.y
elif mod == 4:
to = "X"
elif mod == 5:
to = "Y"
elif mod == 6:
to = "OUT" if w is False else None
elif mod == 7:
to = "OUT" if w is False else None
lo = state.x
hi = state.y
inc_x = True
addr = (hi << 8) | lo
b = state.undef
if bus == 0:
b = state.d
elif bus == 1:
if not w:
b = self.ram[addr & 0x7fff]
elif bus == 2:
b = state.ac
elif bus == 3:
b = self.IN
if w:
self.ram[addr & 0x7fff] = b
if instruction == 0: # LD
ALU = b
elif instruction == 1: # ANDA
ALU = state.ac & b
elif instruction == 2: # ORA
ALU = state.ac | b
elif instruction == 3: # XORA
ALU = state.ac ^ b
elif instruction == 4: # ADDA
ALU = (state.ac + b) & 0xFF
elif instruction == 5: # SUBA
ALU = (state.ac - b) & 0xFF
elif instruction == 6: # ST
ALU = state.ac
elif instruction == 7: # Bcc/JMP
ALU = (-state.ac) & 0xFF
# Load value into register
if to == "AC":
self.tempState.ac = ALU
elif to == "X":
self.tempState.x = ALU
elif to == "Y":
self.tempState.y = ALU
elif to == "OUT":
self.tempState.out = ALU
if inc_x:
self.tempState.x = (state.x + 1) & 0xFF
self.tempState.pc = (state.pc + 1) & 0xFFFF
if j:
if mod != 0:
cond = (state.ac >> 7) + 2 * (state.ac == 0)
if (mod & (1 << cond)) != 0: # 74153
self.tempState.pc = (state.pc & 0xff00) | b
else:
self.tempState.pc = (state.y << 8) | b # Unconditional far jump
return self.tempState
state = CpuState()
new_state = CpuState()
cpu = Cpu()
with open("../../../ROMv3.rom", "rb") as f:
byte = f.read(1)
rom_address = 0
while byte != b"":
cpu.rom[rom_address][0] = ord(byte)
byte = f.read(1)
cpu.rom[rom_address][1] = ord(byte)
byte = f.read(1)
rom_address = rom_address + 1
vgaX = 0
vgaY = 0
t = -2
while True:
if t < 0:
state.pc = 0 # MCP100 Power-On Reset
#sys.stdout.write("PC %04x: %02x%02x IR:%02x D:%02x AC:%02x X:%02x Y:%02x OUT:%02x UNDEF:%02x\n" %
# (state.pc, cpu.rom[state.pc][0], cpu.rom[state.pc][1], state.ir, state.d, state.ac, state.x,
# state.y, state.out, state.undef))
cpu.cpuCycle(state).copyTo(new_state) # Update CPU
hSync = (new_state.out & 0x40) - (state.out & 0x40) # "VGA monitor" (use simple stdout)
vSync = (new_state.out & 0x80) - (state.out & 0x80)
if vSync < 0:
vgaY = -36
vgaX = vgaX + 1
if vgaX <= 200:
if hSync != 0:
sys.stdout.write('|') # Visual indicator of hSync
elif vgaX == 200:
sys.stdout.write('>') # too many pixels
elif (~state.out) & 0x80 > 0:
sys.stdout.write('^') # visualize vBlank pulse
else:
sys.stdout.write(chr(32 + (state.out & 63))) # plot pixel
if hSync > 0:
if vgaX != 200:
sys.stdout.write('~') # mark horizontal cycle errors
sys.stdout.write("line %-3d xout %02x t %0.3f\n" % (vgaY, new_state.ac, t / 6250000))
vgaX = 0
vgaY = vgaY + 1
new_state.undef = randint(0, 255)
new_state.copyTo(state)
t = t + 1