Source code for src.chip8.cpu

import time
import random
from src.chip8.conf import SCREEN_WIDTH, SCREEN_HEIGHT
from src.chip8.memory import Memory, START_ADDR

from typing import List

_KEY_NUM = 16


[docs]class Instruction: def __init__(self, val: int): self.val = val self.opcode = 0 self.x = 0 self.y = 0 self.n = 0 self.kk = 0 self.nnn = 0
[docs] def decode_opcode(self) -> int: return self.val & 0xF000
[docs] def decode_x(self) -> int: return (self.val & 0x0F00) >> 8
[docs] def decode_y(self) -> int: return (self.val & 0x00F0) >> 4
[docs] def decode_n(self) -> int: return self.val & 0x000F
[docs] def decode_kk(self) -> int: return self.val & 0x00FF
[docs] def decode_nnn(self) -> int: return self.val & 0x0FFF
[docs] def decode(self): self.opcode = self.decode_opcode() self.x = self.decode_x() self.y = self.decode_y() self.n = self.decode_n() self.kk = self.decode_kk() self.nnn = self.decode_nnn()
[docs]class CPU: def __init__(self): # 8 bits register self._reg_V = [0] * 16 # 16 bits register self._reg_PC = START_ADDR self._reg_I = 0 self._memory = Memory() self.draw_flag = False self._screen_buf = self.reset_screen() self._keys_pressed_buf = [0] * _KEY_NUM self._delay_timer = 0 self._sound_timer = 0 self._IR = None
[docs] def reset_screen(self): """ clear screen buffer """ return [[0 for _ in range(SCREEN_WIDTH)] for _ in range(SCREEN_HEIGHT)]
@property def screen_buf(self): return self._screen_buf @property def keys_pressed_buf(self): return self._keys_pressed_buf
[docs] def load_rom(self, data: List[int]): self._memory.write(data)
@property def opcode(self) -> int: return self._IR.opcode @property def x(self) -> int: return self._IR.x @property def y(self) -> int: return self._IR.y @property def n(self) -> int: return self._IR.n @property def kk(self) -> int: return self._IR.kk @property def nnn(self) -> int: return self._IR.nnn @property def flag(self) -> int: return self._IR.n
[docs] def cycle(self): self.fetch() self.decode() self.execute()
[docs] def ticker(self): if self._delay_timer > 0: self._delay_timer -= 1 if self._sound_timer > 0: self._sound_timer -= 1
# pygame.mixer.music.play()
[docs] def fetch(self): high_byte = self._memory.ram[self._reg_PC] low_byte = self._memory.ram[self._reg_PC + 1] instruction = (high_byte << 8) | low_byte self._reg_PC += 2 self._IR = Instruction(instruction) return
[docs] def decode(self): self._IR.decode()
[docs] def execute(self): if self.opcode == 0x0000: # 00E0 # CLS # Display # clear the display if self._IR.kk == 0x00E0: self._screen_buf = self.reset_screen() self.draw_flag = True # 00EE # RET # Flow # return from a subroutine elif self._IR.kk == 0x00EE: self._reg_PC = self._memory.stack_pop() # 1NNN # JP addr # Flow # jump to nnn elif self.opcode == 0x1000: addr = self.nnn self._reg_PC = addr # 2NNN # CALL addr # Flow # call subroutine at nnn elif self.opcode == 0x2000: addr = self.nnn self._memory.stack_push(self._reg_PC) self._reg_PC = addr # 3XKK # SE Vx, byte # Cond # skip if vx equals kk elif self.opcode == 0x3000: x = self.x kk = self.kk if self._reg_V[x] == kk: self._reg_PC += 2 # 4XKK # SEN Vx, byte # Cond # skip if vx not equal to kk elif self.opcode == 0x4000: x = self.x kk = self.kk if self._reg_V[x] != kk: self._reg_PC += 2 # 5XY0 # SE Vx, Vy # Cond # skip if vx equals vy elif self.opcode == 0x5000: x = self.x y = self.y if self._reg_V[x] == self._reg_V[y]: self._reg_PC += 2 # 6XKK # LD Vx, byte # Const # set vx to kk elif self.opcode == 0x6000: x = self.x kk = self.kk self._reg_V[x] = kk # 7XKK # ADD Vx, byte # Vx = Vx + kk # Const # set vx to vx plus kk elif self.opcode == 0x7000: x = self.x kk = self.kk self._reg_V[x] += kk self._reg_V[x] &= 0xff # 8XYN # Logical and arithmetic instructions elif self.opcode == 0x8000: # 8XY0 # LD Vx, Vy # Assig # set vx to vy if self.flag == 0x0000: x = self.x y = self.y self._reg_V[x] = self._reg_V[y] # 8XY1 # OR Vx, Vy # BitOp # set vx to vx or vy elif self.flag == 0x0001: x = self.x y = self.y self._reg_V[x] |= self._reg_V[y] # 8XY2 # AND Vx, Vy # BitOp # set vx to vx and vy elif self.flag == 0x0002: x = self.x y = self.y self._reg_V[x] &= self._reg_V[y] # 8XY3 # XOR Vx, Vy # BitOp # set vx to vx xor vy elif self.flag == 0x0003: x = self.x y = self.y self._reg_V[x] ^= self._reg_V[y] # 8XY4 # ADD Vx, Vy # Math # set vx to vx add vy and set vf to carry elif self.flag == 0x0004: x = self.x y = self.y self._reg_V[x] += self._reg_V[y] self._reg_V[0x0F] = 0x01 if self._reg_V[x] > 0xFF else 0x00 self._reg_V[x] &= 0xFF # 8XY5 # SUB Vx, Vy # Math # set vx to vx sub vy elif self.flag == 0x0005: x = self.x y = self.y self._reg_V[0x0F] = 0x00 if self._reg_V[x] < self._reg_V[y] else 0x01 self._reg_V[x] -= self._reg_V[y] self._reg_V[x] &= 0xFF # 8XY6 # SHR Vx, {, Vy} # BitOp # set vx to vy shr 1 elif self.flag == 0x0006: x = self.x self._reg_V[0x0F] = self._reg_V[x] & 0x01 self._reg_V[x] >>= 1 # 8XY7 # SUBN Vx, Vy # Math # set vx to vy - vx , set VF = NOT borrow elif self.flag == 0x0007: x = self.x y = self.y self._reg_V[0x0F] = 0x01 if self._reg_V[x] < self._reg_V[y] else 0x00 self._reg_V[x] = self._reg_V[y] - self._reg_V[x] self._reg_V[x] &= 0xFF # 8XYE # SHL VX, {, Vy} # BitOp # set vx to vx shl 1 elif self.flag == 0x000E: x = self.x self._reg_V[0x0F] = (self._reg_V[x] >> 7) & 0x01 self._reg_V[x] = self._reg_V[x] << 1 self._reg_V[x] &= 0xFF # 9XY0 # SNE Vx, Vy # Cond # skip if vx not equal vy elif self.opcode == 0x9000: x = self.x y = self.y if self._reg_V[x] != self._reg_V[y]: self._reg_PC += 2 # ANNN # LD I, addr # MEM # set i to nnn elif self.opcode == 0xA000: addr = self.nnn self._reg_I = addr # BNNN # JP V0, addr # Flow # jump to nnn plus v0 elif self.opcode == 0xB000: addr = self.nnn self._reg_PC = self._reg_V[0] + addr # CXKK # RND Vx, byte # Rand # set vx to random byte and kk elif self.opcode == 0xC000: x = self.x kk = self.kk self._reg_V[x] = random.randrange(0, 255) & kk # DXYN # DRW Vx, Vy, n # Display # draw to display elif self.opcode == 0xD000: n = self.n x = self.x y = self.y vx = self._reg_V[x] vy = self._reg_V[y] self._reg_V[0xF] = 0 for yy in range(n): sys_byte = self._memory.ram[self._reg_I + yy] for xx in range(8): x_cord = vx + xx y_cord = vy + yy if x_cord < SCREEN_WIDTH and y_cord < SCREEN_HEIGHT: sys_bit = (sys_byte >> (7 - xx)) & 0x01 if (self._screen_buf[y_cord][x_cord] & sys_bit) == 1: self._reg_V[0xF] = 1 self._screen_buf[y_cord][x_cord] ^= sys_bit self.draw_flag = True elif self.opcode == 0xE000: # EX9E # SKP Vx # KeyOp # skip if key equals vx if self.kk == 0x009E: x = self.x if self._keys_pressed_buf[self._reg_V[x]] == 1: self._reg_PC += 2 # EXA1 # SKNP Vx # KeyOp # skip if key not equal to vx elif self.kk == 0x00A1: x = self.x if self._keys_pressed_buf[self._reg_V[x]] == 0: self._reg_PC += 2 # FX00 # elif self.opcode == 0xF000: # FX07 # LD Vx, DT # Timer # set vx to delay timer if self.kk == 0x0007: x = self.x self._reg_V[x] = self._delay_timer # FX0A # LD Vx, K # KeyOp # store keypress in vx elif self.kk == 0x000A: x = self.x pressed = False for i in range(16): if self._keys_pressed_buf[i] == 1: self._reg_V[x] = i pressed = True break if not pressed: self._reg_PC -= 2 # FX15 # LD DT, Vx # Timer # set delay timer to vx elif self.kk == 0x0015: x = self.x self._delay_timer = self._reg_V[x] # FX18 # LD ST, Vx # Sound # set sound timer to vx elif self.kk == 0x0018: x = self.x self._sound_timer = self._reg_V[x] # FX1E # ADD I, Vx # MEM # set i to i plus vx elif self.kk == 0x001E: x = self.x self._reg_I += self._reg_V[x] # FX29 # LD F, Vx # MEM # set i to sprite location for vx elif self.kk == 0x0029: x = self.x self._reg_I = self._reg_V[x] * 5 # FX33 # LD B, Vx # BCD # store bcd in i elif self.kk == 0x0033: x = self.x self._memory.ram[self._reg_I] = self._reg_V[x] // 100 self._memory.ram[self._reg_I + 1] = (self._reg_V[x] % 100) // 10 self._memory.ram[self._reg_I + 2] = (self._reg_V[x] % 100) % 10 # FX55 # LD [I], Vx # MEM # store v0 to vx in memory from location i elif self.kk == 0x0055: x = self.x for i in range(x + 1): self._memory.ram[self._reg_I + i] = self._reg_V[i] # FX65 # LD Vx, [I] # MEM # fill v0 to vx from memory location i elif self.kk == 0x0065: x = self.x for i in range(x + 1): self._reg_V[i] = self._memory.ram[self._reg_I + i]