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
 | 
						|
 |