CPU
CPU of the Emulator.
Source code in core/cpu.py
class CPU:
"""
CPU of the Emulator.
"""
__slots__ = (
"DT",
"I",
"PC",
"ST",
"V",
"display",
"halt",
"keypad",
"memory",
"op",
"sound",
"stack",
"sync",
)
def __init__(self, display: Display, memory: Memory, keypad: Keypad) -> None:
"""
CPU construtor.
Args:
memory: Primary Memory of size 4096 bytes.
display: Display Handler for rendering sprites.
keypad: 16-key hexadecimal keypad for taking input.
Attributes:
sound (pygame.mixer.Sound): A [pygame.mixer.Sound](https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Sound) object.
op (Opcode): Opcode for identifying operation.
V (List[int]): 16 General Purpose 8-bit registers.
I (int): 16-bit Index register to store memory locations.
DT (int): 8-bit Delay Timer.
ST (int): 8-bit Sound Timer.
PC (int): 16-bit register to store the currently executing address.
stack (List[int]): Array of 16 16-bit values for nesting subroutines.
halt (bool): Flag to check if the CPU is halted.
sync (bool): Flag to sync the display with the timer.
"""
# devices
self.display = display
self.memory = memory
self.keypad = keypad
self.sound: pygame.mixer.Sound = pygame.mixer.Sound(
files("rechip8.static").joinpath("beep.mp3").open("rb")
)
self.op: Opcode = Opcode(inst=0x0000)
# registers
self.V: t.List[int] = [0] * 16
self.I: int = 0
self.DT: int = 0
self.ST: int = 0
self.PC: int = INIT_LOC_CONSTANT
self.stack: t.List[int] = [0] * 16
# flags
self.halt: bool = False
self.sync: bool = False
def SYS_addr(self) -> None:
"""
$0nnn - Jump to a machine code routine at nnn. (Edit: use kk instead)
"""
optab = {0xE0: self.CLS, 0xEE: self.RET}
optab[self.op.kk]()
def CLS(self) -> None:
"""
$00E0 - Clear the display.
"""
self.display.clear()
def RET(self) -> None:
"""
$00EE - Return from a subroutine.
"""
self.PC = self.stack.pop()
def JP_addr(self) -> None:
"""
$1nnn - Jump to location nnn. (PC = nnn)
"""
self.PC = self.op.nnn
def CALL_addr(self) -> None:
"""
$2nnn - Call subroutine at nnn.
"""
self.stack.append(self.PC)
self.PC = self.op.nnn
def SE_Vx_byte(self) -> None:
"""
$3xkk - Skip next instruction if Vx = kk.
"""
if self.V[self.op.x] == self.op.kk:
self.PC += 2
def SNE_Vx_byte(self) -> None:
"""
$4xkk - Skip next instruction if Vx != kk.
"""
if self.V[self.op.x] != self.op.kk:
self.PC += 2
def SE_Vx_Vy(self) -> None:
"""
$5xy0 - Skip next instruction if Vx = Vy.
"""
if self.V[self.op.x] == self.V[self.op.y]:
self.PC += 2
def LD_Vx_byte(self) -> None:
"""
$6xkk - Set Vx = kk.
"""
self.V[self.op.x] = self.op.kk
def ADD_Vx_byte(self) -> None:
"""
$7xkk - Set Vx = Vx + kk.
"""
self.V[self.op.x] = (self.V[self.op.x] + self.op.kk) & 0xFF
def JP_addr_8(self) -> None:
"""
$8xyn - Jump to a machine code routine at n.
"""
optab = {
0x0: self.LD_Vx_Vy,
0x1: self.OR_Vx_Vy,
0x2: self.AND_Vx_Vy,
0x3: self.XOR_Vx_Vy,
0x4: self.ADD_Vx_Vy,
0x5: self.SUB_Vx_Vy,
0x6: self.SHR_Vx_Vy,
0x7: self.SUBN_Vx_Vy,
0xE: self.SHL_Vx_Vy,
}
optab[self.op.n]()
def LD_Vx_Vy(self) -> None:
"""
$8xy0 - Set Vx = Vy.
"""
self.V[self.op.x] = (self.V[self.op.y]) & 0xFF
def OR_Vx_Vy(self) -> None:
"""
$8xy1 - Set Vx = Vx OR Vy.
"""
self.V[self.op.x] = (self.V[self.op.x] | self.V[self.op.y]) & 0xFF
self.V[0xF] = 0
def AND_Vx_Vy(self) -> None:
"""
$8xy2 - Set Vx = Vx AND Vy.
"""
self.V[self.op.x] = (self.V[self.op.x] & self.V[self.op.y]) & 0xFF
self.V[0xF] = 0
def XOR_Vx_Vy(self) -> None:
"""
$8xy3 - Set Vx = Vx XOR Vy.
"""
self.V[self.op.x] = (self.V[self.op.x] ^ self.V[self.op.y]) & 0xFF
self.V[0xF] = 0
def ADD_Vx_Vy(self) -> None:
"""
$8xy4 - Set Vx = Vx + Vy, set VF = carry.
"""
val = self.V[self.op.x] + self.V[self.op.y]
self.V[self.op.x] = val & 0xFF
if val > 0xFF:
self.V[0xF] = 1
else:
self.V[0xF] = 0
def SUB_Vx_Vy(self) -> None:
"""
$8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow.
"""
x = self.V[self.op.x]
y = self.V[self.op.y]
self.V[self.op.x] = (x - y) & 0xFF
if x < y:
self.V[0xF] = 0
else:
self.V[0xF] = 1
def SHR_Vx_Vy(self) -> None:
"""
$8xy6 - Set Vx = Vx SHR 1.
"""
prev = self.V[self.op.x]
self.V[self.op.x] = self.V[self.op.y]
self.V[self.op.x] = (self.V[self.op.x] >> 1) & 0xFF
self.V[0xF] = prev & 1
def SUBN_Vx_Vy(self) -> None:
"""
$8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow.
"""
self.V[self.op.x] = (self.V[self.op.y] - self.V[self.op.x]) & 0xFF
if self.V[self.op.x] > self.V[self.op.y]:
self.V[0xF] = 0
else:
self.V[0xF] = 1
def SHL_Vx_Vy(self) -> None:
"""
$8xyE - Set Vx = Vx SHL 1.
"""
y = self.V[self.op.y]
self.V[self.op.x] = (y << 1) & 0xFF
self.V[0xF] = (y & 0xFF) >> 7
def SNE_Vx_Vy(self) -> None:
"""
$9xy0 - Skip next instruction if Vx != Vy.
"""
if self.V[self.op.x] != self.V[self.op.y]:
self.PC += 2
def LD_I_addr(self) -> None:
"""
$Annn - Set I = nnn.
"""
self.I = self.op.nnn
def JP_V0_addr(self) -> None:
"""
$Bnnn - Jump to location nnn + V0.
"""
self.PC = self.op.nnn + self.V[0]
def RND_Vx_byte(self) -> None:
"""
$Cxkk - Set Vx = random byte AND kk.
"""
self.V[self.op.x] = randint(0, 0xFF) & self.op.kk
def DRW_Vx_Vy_nibble(self) -> None:
"""
$Dxyn - Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision. :)
"""
if not self.sync:
self.PC -= 2
return
x = self.V[self.op.x] % COLUMNS
y = self.V[self.op.y] % ROWS
self.V[0xF] = 0
for i in range(self.op.n):
sprite = self.memory.space[self.I + i]
if (y + i) <= ROWS:
for j in range(8):
if (x + j) <= COLUMNS:
px = (sprite >> (7 - j)) & 1
index = self.display.wrap(x + j, y + i)
if px == 1 and self.display.buffer[index] == 1:
self.V[0xF] = 1
self.display.buffer[index] ^= px
self.sync = False
def Jp_addr_E(self) -> None:
"""
$Exkk - Jump to a machine code routine at kk.
"""
optab = {0x9E: self.SKP_Vx, 0xA1: self.SKNP_Vx}
optab[self.op.kk]()
def SKP_Vx(self) -> None:
"""
$Ex9E - Skip next instruction if key with the value of Vx is pressed.
"""
if self.keypad.state[self.V[self.op.x] & 0xF]:
self.PC += 2
def SKNP_Vx(self) -> None:
"""
$ExA1 - Skip next instruction if key with the value of Vx is not pressed.
"""
if not self.keypad.state[self.V[self.op.x] & 0xF]:
self.PC += 2
def Jp_addr_F(self) -> None:
"""
$Fxkk - Jump to a machine code routine at kk.
"""
optab = {
0x07: self.LD_Vx_DT,
0x0A: self.LD_Vx_K,
0x15: self.LD_DT_Vx,
0x18: self.LD_ST_Vx,
0x1E: self.ADD_I_Vx,
0x29: self.LD_F_Vx,
0x33: self.LD_B_Vx,
0x55: self.LD_I_Vx,
0x65: self.LD_Vx_I,
}
optab[self.op.kk]()
def LD_Vx_DT(self) -> None:
"""
$Fx07 - Set Vx = delay timer value.
"""
self.V[self.op.x] = self.DT
def LD_Vx_K(self) -> None:
"""
$Fx0A - Wait for a key press, store the value of the key in Vx.
"""
self.halt = True
def LD_DT_Vx(self) -> None:
"""
$Fx15 - Set delay timer = Vx.
"""
self.DT = self.V[self.op.x]
def LD_ST_Vx(self) -> None:
"""
$Fx18 - Set sound timer = Vx.
"""
self.ST = self.V[self.op.x]
def ADD_I_Vx(self) -> None:
"""
$Fx1E - Set I = I + Vx.
"""
self.I += self.V[self.op.x]
def LD_F_Vx(self) -> None:
"""
$Fx29 - Set I = location of sprite for digit Vx.
"""
self.I = (self.V[self.op.x] * 5) & 0x0FFF
def LD_B_Vx(self) -> None:
"""
$Fx33 - Store BCD representation of Vx in memory locations I, I+1, and I+2.
"""
self.memory.space[self.I] = self.V[self.op.x] // 100
self.memory.space[self.I + 1] = (self.V[self.op.x] % 100) // 10
self.memory.space[self.I + 2] = self.V[self.op.x] % 10
def LD_I_Vx(self) -> None:
"""
$Fx55 - Store registers V0 through Vx in memory starting at location I.
"""
for i in range(self.op.x + 1):
self.memory.space[self.I + i] = self.V[i]
self.I += self.op.x + 1
def LD_Vx_I(self) -> None:
"""
$Fx65 - Read registers V0 through Vx from memory starting at location I.
"""
for i in range(self.op.x + 1):
self.V[i] = self.memory.space[self.I + i]
self.I += self.op.x + 1
@property
def optable(self) -> t.Mapping[int, t.Callable[..., None]]:
"""
Opcode function lookup table.
Returns:
dict: opcode.type:subroutine mapping.
"""
return {
0x0: self.SYS_addr,
0x1: self.JP_addr,
0x2: self.CALL_addr,
0x3: self.SE_Vx_byte,
0x4: self.SNE_Vx_byte,
0x5: self.SE_Vx_Vy,
0x6: self.LD_Vx_byte,
0x7: self.ADD_Vx_byte,
0x8: self.JP_addr_8,
0x9: self.SNE_Vx_Vy,
0xA: self.LD_I_addr,
0xB: self.JP_V0_addr,
0xC: self.RND_Vx_byte,
0xD: self.DRW_Vx_Vy_nibble,
0xE: self.Jp_addr_E,
0xF: self.Jp_addr_F,
}
def beep(self) -> None:
"""
Make a beep sound.
"""
self.sound.play()
def cycle(self) -> None:
"""
Fetch, Decode and Execute instructions from the memory.
"""
fetch = (self.memory.space[self.PC] << 8) | self.memory.space[self.PC + 1]
self.op = Opcode(fetch)
self.PC += 2
try:
self.optable[(self.op.type & 0xF000) >> 12]()
except KeyError:
logging.error(f"{CROSS} Opcode not found: {hex(self.op.inst)}")
self.display.destroy()
def handle_timers(self) -> None:
"""
Decrement ST and DT.
"""
if self.DT > 0:
self.DT -= 1
if self.ST > 0:
self.ST = 0
self.beep()
optable: Mapping[int, Callable[..., NoneType]]
property
readonly
+
Opcode function lookup table.
Returns:
Type | Description |
---|---|
dict |
opcode.type:subroutine mapping. |
__init__(self, display, memory, keypad)
special
+
CPU construtor.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
memory |
Memory |
Primary Memory of size 4096 bytes. |
required |
display |
Display |
Display Handler for rendering sprites. |
required |
keypad |
Keypad |
16-key hexadecimal keypad for taking input. |
required |
Attributes:
Name | Type | Description |
---|---|---|
sound |
pygame.mixer.Sound |
A pygame.mixer.Sound object. |
op |
Opcode |
Opcode for identifying operation. |
V |
List[int] |
16 General Purpose 8-bit registers. |
I |
int |
16-bit Index register to store memory locations. |
DT |
int |
8-bit Delay Timer. |
ST |
int |
8-bit Sound Timer. |
PC |
int |
16-bit register to store the currently executing address. |
stack |
List[int] |
Array of 16 16-bit values for nesting subroutines. |
halt |
bool |
Flag to check if the CPU is halted. |
sync |
bool |
Flag to sync the display with the timer. |
Source code in core/cpu.py
def __init__(self, display: Display, memory: Memory, keypad: Keypad) -> None:
"""
CPU construtor.
Args:
memory: Primary Memory of size 4096 bytes.
display: Display Handler for rendering sprites.
keypad: 16-key hexadecimal keypad for taking input.
Attributes:
sound (pygame.mixer.Sound): A [pygame.mixer.Sound](https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Sound) object.
op (Opcode): Opcode for identifying operation.
V (List[int]): 16 General Purpose 8-bit registers.
I (int): 16-bit Index register to store memory locations.
DT (int): 8-bit Delay Timer.
ST (int): 8-bit Sound Timer.
PC (int): 16-bit register to store the currently executing address.
stack (List[int]): Array of 16 16-bit values for nesting subroutines.
halt (bool): Flag to check if the CPU is halted.
sync (bool): Flag to sync the display with the timer.
"""
# devices
self.display = display
self.memory = memory
self.keypad = keypad
self.sound: pygame.mixer.Sound = pygame.mixer.Sound(
files("rechip8.static").joinpath("beep.mp3").open("rb")
)
self.op: Opcode = Opcode(inst=0x0000)
# registers
self.V: t.List[int] = [0] * 16
self.I: int = 0
self.DT: int = 0
self.ST: int = 0
self.PC: int = INIT_LOC_CONSTANT
self.stack: t.List[int] = [0] * 16
# flags
self.halt: bool = False
self.sync: bool = False
SYS_addr(self)
+
$0nnn - Jump to a machine code routine at nnn. (Edit: use kk instead)
Source code in core/cpu.py
def SYS_addr(self) -> None:
"""
$0nnn - Jump to a machine code routine at nnn. (Edit: use kk instead)
"""
optab = {0xE0: self.CLS, 0xEE: self.RET}
optab[self.op.kk]()
CLS(self)
+
$00E0 - Clear the display.
Source code in core/cpu.py
def CLS(self) -> None:
"""
$00E0 - Clear the display.
"""
self.display.clear()
RET(self)
+
$00EE - Return from a subroutine.
Source code in core/cpu.py
def RET(self) -> None:
"""
$00EE - Return from a subroutine.
"""
self.PC = self.stack.pop()
JP_addr(self)
+
$1nnn - Jump to location nnn. (PC = nnn)
Source code in core/cpu.py
def JP_addr(self) -> None:
"""
$1nnn - Jump to location nnn. (PC = nnn)
"""
self.PC = self.op.nnn
CALL_addr(self)
+
$2nnn - Call subroutine at nnn.
Source code in core/cpu.py
def CALL_addr(self) -> None:
"""
$2nnn - Call subroutine at nnn.
"""
self.stack.append(self.PC)
self.PC = self.op.nnn
SE_Vx_byte(self)
+
$3xkk - Skip next instruction if Vx = kk.
Source code in core/cpu.py
def SE_Vx_byte(self) -> None:
"""
$3xkk - Skip next instruction if Vx = kk.
"""
if self.V[self.op.x] == self.op.kk:
self.PC += 2
SNE_Vx_byte(self)
+
$4xkk - Skip next instruction if Vx != kk.
Source code in core/cpu.py
def SNE_Vx_byte(self) -> None:
"""
$4xkk - Skip next instruction if Vx != kk.
"""
if self.V[self.op.x] != self.op.kk:
self.PC += 2
SE_Vx_Vy(self)
+
$5xy0 - Skip next instruction if Vx = Vy.
Source code in core/cpu.py
def SE_Vx_Vy(self) -> None:
"""
$5xy0 - Skip next instruction if Vx = Vy.
"""
if self.V[self.op.x] == self.V[self.op.y]:
self.PC += 2
LD_Vx_byte(self)
+
$6xkk - Set Vx = kk.
Source code in core/cpu.py
def LD_Vx_byte(self) -> None:
"""
$6xkk - Set Vx = kk.
"""
self.V[self.op.x] = self.op.kk
ADD_Vx_byte(self)
+
$7xkk - Set Vx = Vx + kk.
Source code in core/cpu.py
def ADD_Vx_byte(self) -> None:
"""
$7xkk - Set Vx = Vx + kk.
"""
self.V[self.op.x] = (self.V[self.op.x] + self.op.kk) & 0xFF
JP_addr_8(self)
+
$8xyn - Jump to a machine code routine at n.
Source code in core/cpu.py
def JP_addr_8(self) -> None:
"""
$8xyn - Jump to a machine code routine at n.
"""
optab = {
0x0: self.LD_Vx_Vy,
0x1: self.OR_Vx_Vy,
0x2: self.AND_Vx_Vy,
0x3: self.XOR_Vx_Vy,
0x4: self.ADD_Vx_Vy,
0x5: self.SUB_Vx_Vy,
0x6: self.SHR_Vx_Vy,
0x7: self.SUBN_Vx_Vy,
0xE: self.SHL_Vx_Vy,
}
optab[self.op.n]()
LD_Vx_Vy(self)
+
$8xy0 - Set Vx = Vy.
Source code in core/cpu.py
def LD_Vx_Vy(self) -> None:
"""
$8xy0 - Set Vx = Vy.
"""
self.V[self.op.x] = (self.V[self.op.y]) & 0xFF
OR_Vx_Vy(self)
+
$8xy1 - Set Vx = Vx OR Vy.
Source code in core/cpu.py
def OR_Vx_Vy(self) -> None:
"""
$8xy1 - Set Vx = Vx OR Vy.
"""
self.V[self.op.x] = (self.V[self.op.x] | self.V[self.op.y]) & 0xFF
self.V[0xF] = 0
AND_Vx_Vy(self)
+
$8xy2 - Set Vx = Vx AND Vy.
Source code in core/cpu.py
def AND_Vx_Vy(self) -> None:
"""
$8xy2 - Set Vx = Vx AND Vy.
"""
self.V[self.op.x] = (self.V[self.op.x] & self.V[self.op.y]) & 0xFF
self.V[0xF] = 0
XOR_Vx_Vy(self)
+
$8xy3 - Set Vx = Vx XOR Vy.
Source code in core/cpu.py
def XOR_Vx_Vy(self) -> None:
"""
$8xy3 - Set Vx = Vx XOR Vy.
"""
self.V[self.op.x] = (self.V[self.op.x] ^ self.V[self.op.y]) & 0xFF
self.V[0xF] = 0
ADD_Vx_Vy(self)
+
$8xy4 - Set Vx = Vx + Vy, set VF = carry.
Source code in core/cpu.py
def ADD_Vx_Vy(self) -> None:
"""
$8xy4 - Set Vx = Vx + Vy, set VF = carry.
"""
val = self.V[self.op.x] + self.V[self.op.y]
self.V[self.op.x] = val & 0xFF
if val > 0xFF:
self.V[0xF] = 1
else:
self.V[0xF] = 0
SUB_Vx_Vy(self)
+
$8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow.
Source code in core/cpu.py
def SUB_Vx_Vy(self) -> None:
"""
$8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow.
"""
x = self.V[self.op.x]
y = self.V[self.op.y]
self.V[self.op.x] = (x - y) & 0xFF
if x < y:
self.V[0xF] = 0
else:
self.V[0xF] = 1
SHR_Vx_Vy(self)
+
$8xy6 - Set Vx = Vx SHR 1.
Source code in core/cpu.py
def SHR_Vx_Vy(self) -> None:
"""
$8xy6 - Set Vx = Vx SHR 1.
"""
prev = self.V[self.op.x]
self.V[self.op.x] = self.V[self.op.y]
self.V[self.op.x] = (self.V[self.op.x] >> 1) & 0xFF
self.V[0xF] = prev & 1
SUBN_Vx_Vy(self)
+
$8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow.
Source code in core/cpu.py
def SUBN_Vx_Vy(self) -> None:
"""
$8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow.
"""
self.V[self.op.x] = (self.V[self.op.y] - self.V[self.op.x]) & 0xFF
if self.V[self.op.x] > self.V[self.op.y]:
self.V[0xF] = 0
else:
self.V[0xF] = 1
SHL_Vx_Vy(self)
+
$8xyE - Set Vx = Vx SHL 1.
Source code in core/cpu.py
def SHL_Vx_Vy(self) -> None:
"""
$8xyE - Set Vx = Vx SHL 1.
"""
y = self.V[self.op.y]
self.V[self.op.x] = (y << 1) & 0xFF
self.V[0xF] = (y & 0xFF) >> 7
SNE_Vx_Vy(self)
+
$9xy0 - Skip next instruction if Vx != Vy.
Source code in core/cpu.py
def SNE_Vx_Vy(self) -> None:
"""
$9xy0 - Skip next instruction if Vx != Vy.
"""
if self.V[self.op.x] != self.V[self.op.y]:
self.PC += 2
LD_I_addr(self)
+
$Annn - Set I = nnn.
Source code in core/cpu.py
def LD_I_addr(self) -> None:
"""
$Annn - Set I = nnn.
"""
self.I = self.op.nnn
JP_V0_addr(self)
+
$Bnnn - Jump to location nnn + V0.
Source code in core/cpu.py
def JP_V0_addr(self) -> None:
"""
$Bnnn - Jump to location nnn + V0.
"""
self.PC = self.op.nnn + self.V[0]
RND_Vx_byte(self)
+
$Cxkk - Set Vx = random byte AND kk.
Source code in core/cpu.py
def RND_Vx_byte(self) -> None:
"""
$Cxkk - Set Vx = random byte AND kk.
"""
self.V[self.op.x] = randint(0, 0xFF) & self.op.kk
DRW_Vx_Vy_nibble(self)
+
$Dxyn - Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision. :)
Source code in core/cpu.py
def DRW_Vx_Vy_nibble(self) -> None:
"""
$Dxyn - Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision. :)
"""
if not self.sync:
self.PC -= 2
return
x = self.V[self.op.x] % COLUMNS
y = self.V[self.op.y] % ROWS
self.V[0xF] = 0
for i in range(self.op.n):
sprite = self.memory.space[self.I + i]
if (y + i) <= ROWS:
for j in range(8):
if (x + j) <= COLUMNS:
px = (sprite >> (7 - j)) & 1
index = self.display.wrap(x + j, y + i)
if px == 1 and self.display.buffer[index] == 1:
self.V[0xF] = 1
self.display.buffer[index] ^= px
self.sync = False
Jp_addr_E(self)
+
$Exkk - Jump to a machine code routine at kk.
Source code in core/cpu.py
def Jp_addr_E(self) -> None:
"""
$Exkk - Jump to a machine code routine at kk.
"""
optab = {0x9E: self.SKP_Vx, 0xA1: self.SKNP_Vx}
optab[self.op.kk]()
SKP_Vx(self)
+
$Ex9E - Skip next instruction if key with the value of Vx is pressed.
Source code in core/cpu.py
def SKP_Vx(self) -> None:
"""
$Ex9E - Skip next instruction if key with the value of Vx is pressed.
"""
if self.keypad.state[self.V[self.op.x] & 0xF]:
self.PC += 2
SKNP_Vx(self)
+
$ExA1 - Skip next instruction if key with the value of Vx is not pressed.
Source code in core/cpu.py
def SKNP_Vx(self) -> None:
"""
$ExA1 - Skip next instruction if key with the value of Vx is not pressed.
"""
if not self.keypad.state[self.V[self.op.x] & 0xF]:
self.PC += 2
Jp_addr_F(self)
+
$Fxkk - Jump to a machine code routine at kk.
Source code in core/cpu.py
def Jp_addr_F(self) -> None:
"""
$Fxkk - Jump to a machine code routine at kk.
"""
optab = {
0x07: self.LD_Vx_DT,
0x0A: self.LD_Vx_K,
0x15: self.LD_DT_Vx,
0x18: self.LD_ST_Vx,
0x1E: self.ADD_I_Vx,
0x29: self.LD_F_Vx,
0x33: self.LD_B_Vx,
0x55: self.LD_I_Vx,
0x65: self.LD_Vx_I,
}
optab[self.op.kk]()
LD_Vx_DT(self)
+
$Fx07 - Set Vx = delay timer value.
Source code in core/cpu.py
def LD_Vx_DT(self) -> None:
"""
$Fx07 - Set Vx = delay timer value.
"""
self.V[self.op.x] = self.DT
LD_Vx_K(self)
+
$Fx0A - Wait for a key press, store the value of the key in Vx.
Source code in core/cpu.py
def LD_Vx_K(self) -> None:
"""
$Fx0A - Wait for a key press, store the value of the key in Vx.
"""
self.halt = True
LD_DT_Vx(self)
+
$Fx15 - Set delay timer = Vx.
Source code in core/cpu.py
def LD_DT_Vx(self) -> None:
"""
$Fx15 - Set delay timer = Vx.
"""
self.DT = self.V[self.op.x]
LD_ST_Vx(self)
+
$Fx18 - Set sound timer = Vx.
Source code in core/cpu.py
def LD_ST_Vx(self) -> None:
"""
$Fx18 - Set sound timer = Vx.
"""
self.ST = self.V[self.op.x]
ADD_I_Vx(self)
+
$Fx1E - Set I = I + Vx.
Source code in core/cpu.py
def ADD_I_Vx(self) -> None:
"""
$Fx1E - Set I = I + Vx.
"""
self.I += self.V[self.op.x]
LD_F_Vx(self)
+
$Fx29 - Set I = location of sprite for digit Vx.
Source code in core/cpu.py
def LD_F_Vx(self) -> None:
"""
$Fx29 - Set I = location of sprite for digit Vx.
"""
self.I = (self.V[self.op.x] * 5) & 0x0FFF
LD_B_Vx(self)
+
$Fx33 - Store BCD representation of Vx in memory locations I, I+1, and I+2.
Source code in core/cpu.py
def LD_B_Vx(self) -> None:
"""
$Fx33 - Store BCD representation of Vx in memory locations I, I+1, and I+2.
"""
self.memory.space[self.I] = self.V[self.op.x] // 100
self.memory.space[self.I + 1] = (self.V[self.op.x] % 100) // 10
self.memory.space[self.I + 2] = self.V[self.op.x] % 10
LD_I_Vx(self)
+
$Fx55 - Store registers V0 through Vx in memory starting at location I.
Source code in core/cpu.py
def LD_I_Vx(self) -> None:
"""
$Fx55 - Store registers V0 through Vx in memory starting at location I.
"""
for i in range(self.op.x + 1):
self.memory.space[self.I + i] = self.V[i]
self.I += self.op.x + 1
LD_Vx_I(self)
+
$Fx65 - Read registers V0 through Vx from memory starting at location I.
Source code in core/cpu.py
def LD_Vx_I(self) -> None:
"""
$Fx65 - Read registers V0 through Vx from memory starting at location I.
"""
for i in range(self.op.x + 1):
self.V[i] = self.memory.space[self.I + i]
self.I += self.op.x + 1
beep(self)
+
Make a beep sound.
Source code in core/cpu.py
def beep(self) -> None:
"""
Make a beep sound.
"""
self.sound.play()
cycle(self)
+
Fetch, Decode and Execute instructions from the memory.
Source code in core/cpu.py
def cycle(self) -> None:
"""
Fetch, Decode and Execute instructions from the memory.
"""
fetch = (self.memory.space[self.PC] << 8) | self.memory.space[self.PC + 1]
self.op = Opcode(fetch)
self.PC += 2
try:
self.optable[(self.op.type & 0xF000) >> 12]()
except KeyError:
logging.error(f"{CROSS} Opcode not found: {hex(self.op.inst)}")
self.display.destroy()
handle_timers(self)
+
Decrement ST and DT.
Source code in core/cpu.py
def handle_timers(self) -> None:
"""
Decrement ST and DT.
"""
if self.DT > 0:
self.DT -= 1
if self.ST > 0:
self.ST = 0
self.beep()