781 lines
23 KiB
Python
781 lines
23 KiB
Python
# SYNOPSIS: from vcpu import *
|
|
#
|
|
# This is the a small vCPU emulator in Python.
|
|
# It can be very useful for debugging and testing GT1 programs
|
|
# in Python, specially the ones written using vasm.
|
|
#
|
|
|
|
# VirtualCpu to emulate the vCPU inside the Gigatron ROM.
|
|
|
|
class VirtualCpu(object):
|
|
vPC_ADDR = 0x16
|
|
vAC_ADDR = 0x18
|
|
vLR_ADDR = 0x1A
|
|
vSP_ADDR = 0x1C
|
|
|
|
def __init__(self):
|
|
"""Constructor of the VirtualCpu."""
|
|
self._memory = None
|
|
self._mem_size = None
|
|
self._read_watchpoints = None
|
|
self._write_watchpoints = None
|
|
self._lup_callback = None
|
|
self._sys_callback = None
|
|
self._enable_experimental = None
|
|
self._halt = None
|
|
|
|
# Instruction table
|
|
insn_table = [
|
|
( 0x5E, "ST", 2, self.execute_st ),
|
|
( 0x2B, "STW", 2, self.execute_stw ),
|
|
( 0xEC, "STLW", 2, self.execute_stlw ),
|
|
( 0x1A, "LD", 2, self.execute_ld ),
|
|
( 0x59, "LDI", 2, self.execute_ldi ),
|
|
( 0x11, "LDWI", 3, self.execute_ldwi ),
|
|
( 0x21, "LDW", 2, self.execute_ldw ),
|
|
( 0xEE, "LDLW", 2, self.execute_ldlw ),
|
|
( 0x99, "ADDW", 2, self.execute_addw ),
|
|
( 0xB8, "SUBW", 2, self.execute_subw ),
|
|
( 0xE3, "ADDI", 2, self.execute_addi ),
|
|
( 0xE6, "SUBI", 2, self.execute_subi ),
|
|
( 0xE9, "LSLW", 1, self.execute_lslw ),
|
|
( 0x93, "INC", 2, self.execute_inc ),
|
|
( 0x82, "ANDI", 2, self.execute_andi ),
|
|
( 0xF8, "ANDW", 2, self.execute_andw ),
|
|
( 0x88, "ORI", 2, self.execute_ori ),
|
|
( 0xFA, "ORW", 2, self.execute_orw ),
|
|
( 0x8C, "XORI", 2, self.execute_xori ),
|
|
( 0xFC, "XORW", 2, self.execute_xorw ),
|
|
( 0xAD, "PEEK", 1, self.execute_peek ),
|
|
( 0xF6, "DEEK", 1, self.execute_deek ),
|
|
( 0xF0, "POKE", 2, self.execute_poke ),
|
|
( 0xF3, "DOKE", 2, self.execute_doke ),
|
|
( 0x7F, "LUP", 2, self.execute_lup ),
|
|
( 0x90, "BRA", 2, self.execute_bra ),
|
|
( 0x35, "BCC", 3, self.execute_bcc ),
|
|
( 0xCF, "CALL", 2, self.execute_call ),
|
|
( 0xFF, "RET", 1, self.execute_ret ),
|
|
( 0x75, "PUSH", 1, self.execute_push ),
|
|
( 0x63, "POP", 1, self.execute_pop ),
|
|
( 0xDF, "ALLOC", 2, self.execute_alloc ),
|
|
( 0xB4, "SYS", 2, self.execute_sys ),
|
|
( 0xCD, "DEF", 2, self.execute_def ),
|
|
|
|
# Experimental instructions
|
|
( 0x85, "CALLI", 3, self.execute_calli ),
|
|
( 0x1F, "CMPHS", 2, self.execute_cmphs ),
|
|
( 0x97, "CMPHU", 2, self.execute_cmphu ),
|
|
]
|
|
|
|
# Construct a dispatch table for the instructions
|
|
self._insn_table = {
|
|
entry[0] : entry for entry in insn_table
|
|
}
|
|
|
|
# Branch table
|
|
branch_table = [
|
|
( 0x3F, "BEQ" ),
|
|
( 0x72, "BNE" ),
|
|
( 0x50, "BLT" ),
|
|
( 0x4D, "BGT" ),
|
|
( 0x56, "BLE" ),
|
|
( 0x53, "BGE" ),
|
|
]
|
|
self._branch_table = {
|
|
entry[0] : entry for entry in branch_table
|
|
}
|
|
|
|
def read_byte(self, addr):
|
|
"""Reads one byte on a given address.
|
|
|
|
Parameters
|
|
----------
|
|
addr: int
|
|
The address to read from.
|
|
|
|
Returns
|
|
-------
|
|
The byte at the given address.
|
|
"""
|
|
cb = self._read_watchpoints.get(addr, None)
|
|
if cb is not None:
|
|
cb(self, addr)
|
|
|
|
addr = addr % self._mem_size
|
|
return self._memory[addr]
|
|
|
|
def write_byte(self, addr, b):
|
|
"""Writes one byte to a given address.
|
|
|
|
Parameters
|
|
----------
|
|
addr: int
|
|
The address to write to.
|
|
b: int
|
|
The byte to write.
|
|
"""
|
|
cb = self._write_watchpoints.get(addr, None)
|
|
if cb is not None:
|
|
cb(self, addr)
|
|
|
|
addr = addr % self._mem_size
|
|
self._memory[addr] = (b & 0xFF)
|
|
|
|
def read_word(self, addr):
|
|
"""Reads one word (2 bytes) on a given address.
|
|
|
|
Parameters
|
|
----------
|
|
addr: int
|
|
The address to read from.
|
|
|
|
Returns
|
|
-------
|
|
The word at the given address.
|
|
"""
|
|
lo = self.read_byte(addr)
|
|
hi = self.read_byte(self.relative(addr, 1))
|
|
return (hi << 8) | lo
|
|
|
|
def write_word(self, addr, w):
|
|
"""Writes one word to a given address.
|
|
|
|
Parameters
|
|
----------
|
|
addr: int
|
|
The address to write to.
|
|
w: int
|
|
The word to write.
|
|
"""
|
|
self.write_byte(addr, w & 0xFF)
|
|
self.write_byte(self.relative(addr, 1), (w >> 8))
|
|
|
|
def absolute(self, base, page_offset):
|
|
"""Computes the address at same page as in the base address.
|
|
|
|
Parameters
|
|
----------
|
|
base: int
|
|
The base address.
|
|
page_offset: int
|
|
The offset relative to the page of the base address.
|
|
|
|
Returns
|
|
-------
|
|
The address.
|
|
"""
|
|
return (base & ~0xFF) | (page_offset & 0xFF)
|
|
|
|
def relative(self, base, offset):
|
|
"""Computes the address at given offset of a base address.
|
|
|
|
Because addresses stay on the same page (they wrap around, and
|
|
when adding, the carry to the high byte is not included), this
|
|
function is necessary.
|
|
|
|
Parameters
|
|
----------
|
|
base: int
|
|
The base address.
|
|
offset: int
|
|
The offset relative to the base address.
|
|
|
|
Returns
|
|
-------
|
|
The address.
|
|
"""
|
|
return self.absolute(base, base + offset)
|
|
|
|
def get_vPC(self):
|
|
"""Gets the value of the vPC register.
|
|
|
|
Returns
|
|
-------
|
|
The vPC register.
|
|
"""
|
|
return self.read_word(VirtualCpu.vPC_ADDR)
|
|
|
|
def set_vPC(self, w):
|
|
"""Sets the value of the vPC register.
|
|
|
|
Parameters
|
|
----------
|
|
w: int
|
|
The new value of the vPC register.
|
|
"""
|
|
self.write_word(VirtualCpu.vPC_ADDR, w)
|
|
|
|
def get_vAC(self):
|
|
"""Gets the value of the vAC register.
|
|
|
|
Returns
|
|
-------
|
|
The vAC register.
|
|
"""
|
|
return self.read_word(VirtualCpu.vAC_ADDR)
|
|
|
|
def set_vAC(self, w):
|
|
"""Sets the value of the vAC register.
|
|
|
|
Parameters
|
|
----------
|
|
w: int
|
|
The new value of the vAC register.
|
|
"""
|
|
self.write_word(VirtualCpu.vAC_ADDR, w)
|
|
|
|
def get_vLR(self):
|
|
"""Gets the value of the vLR register.
|
|
|
|
Returns
|
|
-------
|
|
The vLR register.
|
|
"""
|
|
return self.read_word(VirtualCpu.vLR_ADDR)
|
|
|
|
def set_vLR(self, w):
|
|
"""Sets the value of the vLR register.
|
|
|
|
Parameters
|
|
----------
|
|
w: int
|
|
The new value of the vLR register.
|
|
"""
|
|
self.write_word(VirtualCpu.vLR_ADDR, w)
|
|
|
|
def get_vSP(self):
|
|
"""Gets the value of the vSP register.
|
|
|
|
Returns
|
|
-------
|
|
The SP register.
|
|
"""
|
|
return self.read_byte(VirtualCpu.vSP_ADDR)
|
|
|
|
def set_vSP(self, b):
|
|
"""Sets the value of the vSP register.
|
|
|
|
Parameters
|
|
----------
|
|
b: int
|
|
The new value of the vSP register.
|
|
"""
|
|
self.write_byte(VirtualCpu.vSP_ADDR, b)
|
|
|
|
def halt(self, v=True):
|
|
"""Halts the emulation of the vCPU.
|
|
|
|
Parameters
|
|
----------
|
|
v: bool
|
|
Set to True to halt.
|
|
"""
|
|
self._halt = v
|
|
|
|
def load_gt1(self, gt1_bytes, mem_size=32768,
|
|
lup_callback=None, sys_callback=None,
|
|
enable_experimental=False):
|
|
"""Loads the GT1 file to memory and resets the vCPU.
|
|
|
|
Parameters
|
|
----------
|
|
gt1_bytes: bytes
|
|
A byte array / buffer that holds the contents of the GT1
|
|
file to load.
|
|
mem_size: int
|
|
The memory size of this VirtualCpu instance.
|
|
lup_callback: callable
|
|
The callback used to implement LUP instructions.
|
|
sys_callback: callable
|
|
The callback used to implement SYS instructions.
|
|
enable_experimental: bool
|
|
To enable experimental instructions (such as CALLI).
|
|
"""
|
|
self._mem_size = mem_size
|
|
self._memory = bytearray(mem_size)
|
|
self._read_watchpoints = dict()
|
|
self._write_watchpoints = dict()
|
|
self._lup_callback = lup_callback
|
|
self._sys_callback = sys_callback
|
|
self._enable_experimental = enable_experimental
|
|
|
|
idx = 0
|
|
|
|
hi_addr = gt1_bytes[idx]
|
|
idx += 1
|
|
|
|
while hi_addr != 0:
|
|
lo_addr = gt1_bytes[idx]
|
|
idx += 1
|
|
|
|
num_bytes = gt1_bytes[idx]
|
|
idx += 1
|
|
|
|
addr = (hi_addr << 8) | lo_addr
|
|
for j in range(num_bytes):
|
|
self.write_byte(self.relative(addr, j),
|
|
gt1_bytes[idx + j])
|
|
idx += num_bytes
|
|
|
|
hi_addr = gt1_bytes[idx]
|
|
idx += 1
|
|
|
|
hi_addr = gt1_bytes[idx]
|
|
idx += 1
|
|
|
|
lo_addr = gt1_bytes[idx]
|
|
idx += 1
|
|
|
|
vPC = (hi_addr << 8) | lo_addr
|
|
self.set_vPC(vPC)
|
|
self.set_vSP(0xFE)
|
|
|
|
def disassemble(self):
|
|
vPC = self.get_vPC()
|
|
insn = self.read_byte(vPC)
|
|
|
|
if insn in self._insn_table:
|
|
name, nb = self._insn_table[insn][1:3]
|
|
t = "%-5s" % name
|
|
if name == "BCC":
|
|
b1 = self.read_byte(self.relative(vPC, 1))
|
|
b2 = self.read_byte(self.relative(vPC, 2))
|
|
if b1 in self._branch_table:
|
|
t = self._branch_table[b1][-1]
|
|
t = "%-5s $%02X" % (t, b2)
|
|
else:
|
|
t = "<err>"
|
|
else:
|
|
if nb == 2:
|
|
b = self.read_byte(self.relative(vPC, 1))
|
|
t = "%s $%02X" % (t, b)
|
|
elif nb == 3:
|
|
w = self.read_word(self.relative(vPC, 1))
|
|
t = "%s $%04X" % (t, w)
|
|
else:
|
|
t = "<err>"
|
|
|
|
t = "%-15s" % t
|
|
vAC = self.get_vAC()
|
|
vLR = self.get_vLR()
|
|
vSP = self.get_vSP()
|
|
print("PC: $%04X, AC: $%04X, LR: $%04X, SP: $%02X %s" %\
|
|
(vPC, vAC, vLR, vSP, t))
|
|
|
|
def run(self,
|
|
max_instructions=None,
|
|
breakpoints=None,
|
|
read_watchpoints=None,
|
|
write_watchpoints=None):
|
|
"""Runs the vCPU for a specified amount.
|
|
|
|
Parameters
|
|
----------
|
|
max_instructions: int
|
|
The maximum number of instructions to run.
|
|
If not specified, it runs indefinitely.
|
|
breakpoints: dict
|
|
The dictionary of breakpoints. The keys are the addresses
|
|
of the breakpoints and the associated values are callbacks
|
|
to be called when the vCPU hits the corresponding breakpoint.
|
|
The callbacks should accept two parameters: a reference to
|
|
`VirtualCpu` itself, and the address of the breakpoint.
|
|
read_watchpoints: dict
|
|
The dictionary of memory addresses to watch for reads.
|
|
It works similarly as the `breakpoints` parameter.
|
|
write_watchpoints: dict
|
|
The dictionary of memory addresses to watch for writes.
|
|
It works similarly as the `breakpoints` parameter.
|
|
"""
|
|
if breakpoints is None:
|
|
breakpoints = dict()
|
|
else:
|
|
breakpoints = dict(breakpoints)
|
|
|
|
if read_watchpoints is None:
|
|
read_watchpoints = dict()
|
|
else:
|
|
read_watchpoints = dict(read_watchpoints)
|
|
|
|
if write_watchpoints is None:
|
|
write_watchpoints = dict()
|
|
else:
|
|
write_watchpoints = ditc(write_watchpoints)
|
|
|
|
self._read_watchpoints = read_watchpoints
|
|
self._write_watchpoints = write_watchpoints
|
|
self._halt = False
|
|
|
|
num_instructions = 0
|
|
while not self._halt:
|
|
vPC = self.get_vPC()
|
|
|
|
cb = breakpoints.get(vPC, None)
|
|
if cb is not None:
|
|
cb(self, vPC)
|
|
|
|
if self._halt:
|
|
break
|
|
|
|
self.execute_instruction()
|
|
num_instructions += 1
|
|
|
|
if max_instructions is not None and\
|
|
num_instructions >= max_instructions:
|
|
break
|
|
|
|
def execute_instruction(self):
|
|
"""Executes one vCPU instruction."""
|
|
vPC = self.get_vPC()
|
|
insn = self.read_byte(vPC)
|
|
if insn in self._insn_table:
|
|
self._insn_table[insn][-1]()
|
|
else:
|
|
raise RuntimeError("invalid instruction 0x%02X" % insn)
|
|
|
|
def execute_st(self):
|
|
"""Executes the ST instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
self.write_byte(dd, vAC & 0xFF)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_stw(self):
|
|
"""Executes the STW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
self.write_word(dd, vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_stlw(self):
|
|
"""Executes the STLW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
dd += self.get_vSP()
|
|
vAC = self.get_vAC()
|
|
self.write_word(dd & 0xFF, vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_ld(self):
|
|
"""Executes the LD instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.read_byte(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_ldi(self):
|
|
"""Executes the LDI instruction."""
|
|
vPC = self.get_vPC()
|
|
vAC = self.read_byte(self.relative(vPC, 1))
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_ldwi(self):
|
|
"""Executes the LDWI instruction."""
|
|
vPC = self.get_vPC()
|
|
vAC = self.read_word(self.relative(vPC, 1))
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 3))
|
|
|
|
def execute_ldw(self):
|
|
"""Executes the LDW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.read_word(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_ldlw(self):
|
|
"""Executes the LDLW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
dd += self.get_vSP()
|
|
vAC = self.read_word(dd & 0xFF)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_addw(self):
|
|
"""Executes the ADDW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC += self.read_word(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_subw(self):
|
|
"""Executes the SUBW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC -= self.read_word(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_addi(self):
|
|
"""Executes the ADDI instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC += dd
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_subi(self):
|
|
"""Executes the SUBI instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC -= dd
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_lslw(self):
|
|
"""Executes the LSLW instruction."""
|
|
vPC = self.get_vPC()
|
|
vAC = self.get_vAC()
|
|
vAC = vAC << 1
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 1))
|
|
|
|
def execute_inc(self):
|
|
"""Executes the INC instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
v = self.read_byte(dd)
|
|
self.write_byte(dd, v + 1)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_andi(self):
|
|
"""Executes the ANDI instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC = vAC & dd
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_andw(self):
|
|
"""Executes the ANDW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC = vAC & self.read_word(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_ori(self):
|
|
"""Executes the ORI instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC = vAC | dd
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_orw(self):
|
|
"""Executes the ORW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC = vAC | self.read_word(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_xori(self):
|
|
"""Executes the XORI instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC = vAC ^ dd
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_xorw(self):
|
|
"""Executes the XORW instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
vAC = vAC ^ self.read_word(dd)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_peek(self):
|
|
"""Executes the PEEK instruction."""
|
|
vPC = self.get_vPC()
|
|
vAC = self.get_vAC()
|
|
vAC = self.read_byte(vAC)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 1))
|
|
|
|
def execute_deek(self):
|
|
"""Executes the DEEK instruction."""
|
|
vPC = self.get_vPC()
|
|
vAC = self.get_vAC()
|
|
vAC = self.read_word(vAC)
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 1))
|
|
|
|
def execute_poke(self):
|
|
"""Executes the POKE instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
addr = self.read_word(dd)
|
|
vAC = self.get_vAC()
|
|
self.write_byte(addr, vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_doke(self):
|
|
"""Executes the DOKE instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
addr = self.read_word(dd)
|
|
vAC = self.get_vAC()
|
|
self.write_word(addr, vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_lup(self):
|
|
"""Executes the LUP instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.get_vAC()
|
|
if self._lup_callback is not None:
|
|
vAC = self._lup_callback(self, self.relative(vAC, dd))
|
|
self.set_vAC(vAC)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_bra(self):
|
|
"""Executes the BRA instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vPC = self.absolute(vPC, dd + 2)
|
|
self.set_vPC(vPC)
|
|
|
|
def execute_bcc(self):
|
|
"""Executes the several BCC instructions (conditional branches)."""
|
|
vPC = self.get_vPC()
|
|
cc = self.read_byte(self.relative(vPC, 1))
|
|
dd = self.read_byte(self.relative(vPC, 2))
|
|
vAC = self.get_vAC()
|
|
jump = ((cc == 0x3F) and (vAC == 0)) or\
|
|
((cc == 0x72) and (vAC != 0)) or\
|
|
((cc == 0x50) and (vAC >= 0x8000)) or\
|
|
((cc == 0x4D) and (vAC > 0) and (vAC < 0x8000)) or\
|
|
((cc == 0x56) and ((vAC >= 0x8000) or (vAC == 0))) or\
|
|
((cc == 0x53) and (vAC < 0x8000))
|
|
|
|
if jump:
|
|
vPC = self.absolute(vPC, dd + 2)
|
|
else:
|
|
vPC = self.relative(vPC, 3)
|
|
self.set_vPC(vPC)
|
|
|
|
def execute_call(self):
|
|
"""Executes the CALL instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
addr = self.read_word(dd)
|
|
self.set_vLR(self.relative(vPC, 2))
|
|
self.set_vPC(addr)
|
|
|
|
def execute_ret(self):
|
|
"""Executes the RET instruction."""
|
|
vLR = self.get_vLR()
|
|
self.set_vPC(vLR)
|
|
|
|
def execute_push(self):
|
|
"""Executes the PUSH instruction."""
|
|
vPC = self.get_vPC()
|
|
vSP = self.get_vSP()
|
|
vSP = (vSP - 2) & 0xFF
|
|
vLR = self.get_vLR()
|
|
self.write_word(vSP, vLR)
|
|
self.set_vSP(vSP)
|
|
self.set_vPC(self.relative(vPC, 1))
|
|
|
|
def execute_pop(self):
|
|
"""Executes the POP instruction."""
|
|
vPC = self.get_vPC()
|
|
vSP = self.get_vSP()
|
|
vLR = self.read_word(vSP)
|
|
vSP = (vSP + 2) & 0xFF
|
|
self.set_vSP(vSP)
|
|
self.set_vLR(vLR)
|
|
self.set_vPC(self.relative(vPC, 1))
|
|
|
|
def execute_alloc(self):
|
|
"""Executes the ALLOC instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vSP = self.get_vSP()
|
|
vSP += dd
|
|
self.set_vSP(vSP)
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_sys(self):
|
|
"""Executes the SYS instruction."""
|
|
if self._sys_callback is not None:
|
|
self._sys_callback(self)
|
|
vPC = self.get_vPC()
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_def(self):
|
|
"""Executes the DEF instruction."""
|
|
vPC = self.get_vPC()
|
|
dd = self.read_byte(self.relative(vPC, 1))
|
|
vAC = self.relative(vPC, 2)
|
|
self.set_vAC(vAC)
|
|
|
|
vPC = self.absolute(vPC, dd)
|
|
self.set_vPC(vPC)
|
|
|
|
def execute_calli(self):
|
|
"""Executes the CALLI instruction."""
|
|
if not self._enable_experimental:
|
|
raise RuntimeError("experimental instruction!")
|
|
|
|
vPC = self.get_vPC()
|
|
self.set_vLR(self.relative(vPC, 2))
|
|
self.set_vPC(self.read_word(self.relative(vPC, 1)))
|
|
|
|
def execute_cmphs(self):
|
|
"""Executes the CMPHS instruction."""
|
|
if not self._enable_experimental:
|
|
raise RuntimeError("experimental instruction!")
|
|
|
|
vPC = self.get_vPC()
|
|
vAC = self.get_vAC()
|
|
vACH = vAC >> 8
|
|
addr = self.read_byte(self.relative(vPC, 1))
|
|
b = self.read_byte(addr)
|
|
|
|
if (vACH ^ b) & 0x80 != 0:
|
|
if b & 0x80 != 0:
|
|
vAC = ((b + 1) & 0xFF) << 8 | (vAC & 0xFF)
|
|
else:
|
|
vAC = ((b - 1) & 0xFF) << 8 | (vAC & 0xFF)
|
|
|
|
self.set_vPC(self.relative(vPC, 2))
|
|
|
|
def execute_cmphu(self):
|
|
"""Executes the CMPHU instruction."""
|
|
if not self._enable_experimental:
|
|
raise RuntimeError("experimental instruction!")
|
|
|
|
vPC = self.get_vPC()
|
|
vAC = self.get_vAC()
|
|
vACH = vAC >> 8
|
|
addr = self.read_byte(self.relative(vPC, 1))
|
|
b = self.read_byte(addr)
|
|
|
|
if (vACH ^ b) & 0x80 != 0:
|
|
if b & 0x80 != 0:
|
|
vAC = ((b - 1) & 0xFF) << 8 | (vAC & 0xFF)
|
|
else:
|
|
vAC = ((b + 1) & 0xFF) << 8 | (vAC & 0xFF)
|
|
|
|
self.set_vPC(self.relative(vPC, 2))
|