185 lines
5.3 KiB
Python
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
|
|
|