from instruction import Opcode import random import time class CPU: display = ["."] * 64 * 32 drawFlag = False memory = [0x00] * 4096 stack = [0x00] * 12 V = [0x00] * 0x10 PC = 0x200 SP = 0x00 I = 0x0000 delay_timer = 0x00 sound_timer = 0x00 tick_timer = int(round(time.time() * 1000)) interrupt = True jumps = 0 instruction = Opcode def load_rom(self, rom: str): with open(rom, "rb") as f: byte = f.read(1) offset = 0 while byte != b"": self.memory[0x200 + offset] = int.from_bytes(byte, byteorder='little') offset += 1 byte = f.read(1) def reset(self): self.display = ["."] * 64 * 32 self.drawFlag = False self.memory = [0x00] * 4096 self.stack = [0x00] * 12 self.V = [0x00] * 0x10 self.PC = 0x200 self.SP = 0x00 self.I = 0x0000 self.delay_timer = 0x00 self.sound_timer = 0x00 self.interrupt = True self.jumps = 0 self.instruction = Opcode self.tick_timer = int(round(time.time() * 1000)) def tick(self): if self.jumps >= 5 and self.drawFlag is False: self.interrupt = True self.execute(self, Opcode((self.memory[self.PC] << 8) | self.memory[self.PC + 1])) # print(time.localtime(time.time())[6]) if (int(round(time.time() * 1000)) - self.tick_timer) >= 1000 / 60: if self.delay_timer is not 0: self.delay_timer -= 1 if self.sound_timer is not 0: self.sound_timer -= 1 self.tick_timer = int(round(time.time() * 1000)) def execute(self, opc: Opcode): self.instruction = opc print(hex(self.instruction.op)) if opc.i == 0x0: # multi case if opc.kk == 0xE0: self.display = ["."] * 64 * 32 self.PC += 2 print("Screen cleared") self.jumps = 0 return if opc.kk == 0xEE: # return from subroutine self.SP -= 1 self.PC = self.stack[self.SP] self.PC += 2 print("Returned from subroutine") self.jumps = 0 return if opc.i == 0x1: # 1NNN: Jump to address NNN if hex(opc.nnn) is 0x349: print("klklklkl") self.PC = opc.nnn print("=>Jumped to address " + hex(opc.nnn)) self.jumps += 1 return if opc.i == 0x2: # 2NNN Call subroutnie at NNN self.stack[self.SP] = self.PC self.SP += 1 self.PC = self.instruction.nnn print("=>Called subroutine at " + str(hex(self.instruction.nnn))) self.jumps = 0 return if opc.i == 0x3: # 3XKK: Skip next instruction if VX == KK if self.V[self.instruction.x] == self.instruction.kk: self.PC += 4 print("V" + str(self.instruction.x) + " == KK" + " [" + str(self.V[self.instruction.x]) + " == " + str(self.instruction.kk) + "] || Skipped instruction") else: self.PC += 2 print("V" + str(self.instruction.x) + " != KK" + " [" + str(self.V[self.instruction.x]) + " != " + str(self.instruction.kk) + " || Didn't skip instruction") self.jumps = 0 return if opc.i == 0x4: # 4XKK: Skip next instruction if VX != KK if self.V[self.instruction.x] != self.instruction.kk: self.PC += 4 print("V" + str(self.instruction.x) + " != KK" + " [" + str(self.V[self.instruction.x]) + " != " + str(self.instruction.kk) + "] || Skipped instruction") else: self.PC += 2 print("V" + str(self.instruction.x) + " == KK" + " [" + str(self.V[self.instruction.x]) + " == " + str(self.instruction.kk) + " || Didn't skip instruction") self.jumps = 0 return if opc.i == 0x5: # 5XY0: Skip next instruction if VX == VY if self.V[self.instruction.x] == self.V[self.instruction.y]: self.PC += 4 print("V" + str(self.instruction.x) + " == V" + str(self.instruction.y) + " [" + str(self.V[self.instruction.x]) + " == " + str(self.V[self.instruction.y]) + "] || Skipped instruction") else: self.PC += 2 print("V" + str(self.instruction.x) + " != V" + str(self.instruction.y) + " [" + str(self.V[self.instruction.x]) + " != " + str(self.V[self.instruction.y]) + " || Didn't skip instruction") self.jumps = 0 return if opc.i == 0x6: # 6XKK: Set VX to KK self.V[self.instruction.x] = opc.kk self.PC += 2 print("=>Set V" + str(self.instruction.x) + " to " + str(opc.kk)) self.jumps = 0 return if opc.i == 0x7: # 7XKK: Set VX = VX + KK self.V[self.instruction.x] = (self.V[self.instruction.x] + self.instruction.kk) & 0xFF self.PC += 2 print("=>Added V" + str(self.instruction.x) + " and KK (" + str(self.instruction.kk) + ") = " + str(self.V[self.instruction.x])) self.jumps = 0 return if opc.i == 0x8: # Multi Case if opc.n == 0x0: # 8XY0: VX = VY self.V[self.instruction.x] = self.V[self.instruction.y] self.PC += 2 print("=>Set V" + str(self.instruction.x) + " = V" + str(self.instruction.y) + " (" + str(self.V[self.instruction.x]) + ")") self.jumps = 0 return if opc.n == 0x2: # 8XY2: VX = VX AND VY self.V[self.instruction.x] = (self.V[self.instruction.x] & self.instruction.y) self.PC += 2 print("=>ANDed V" + str(self.instruction.x) + " and V" + str(self.instruction.y) + "(" + str(self.V[self.instruction.y]) + ") = " + str(self.V[self.instruction.x])) self.jumps = 0 return if opc.n == 0x3: # 8XY3: Set VX = VX XOR VY self.V[self.instruction.x] = self.V[self.instruction.x] ^ self.V[self.instruction.y] self.PC += 2 print("=>XORed V" + str(self.instruction.x) + " and V" + str(self.instruction.y) + "(" + str(self.V[self.instruction.y]) + ") = " + str(self.V[self.instruction.x])) self.jumps = 0 return if opc.n == 0x4: # 8XY4: Set VX = VX + VY. Set Carry if overflow self.V[0xF] = 0 if self.V[self.instruction.y] > (255 - self.V[self.instruction.x]): self.V[0xF] = 1 self.V[self.instruction.x] = (self.V[self.instruction.x] + self.V[self.instruction.y]) & 0xFF self.PC += 2 print("=>Added V" + str(self.instruction.x) + " and V" + str(self.instruction.y) + "(" + str(self.V[self.instruction.y]) + ") = " + str(self.V[self.instruction.x]) + " ; Carry is now " + str(self.V[0xF])) self.jumps = 0 return if opc.n == 0x5: # 8XY5: Set VX = VX - VY. Set Flag if NOT borrow self.V[0xF] = 0 if self.V[self.instruction.x] > self.V[self.instruction.y]: self.V[0xF] = 1 self.V[self.instruction.x] = (self.V[self.instruction.x] - self.V[self.instruction.y]) & 0xFF self.PC += 2 print("=>Subtracted V" + str(self.instruction.x) + " and V" + str(self.instruction.y) + "(" + str(self.V[self.instruction.y]) + ") = " + str(self.V[self.instruction.x]) + " ; Flag is now " + str(self.V[0xF])) self.jumps = 0 return if opc.n == 0x6: self.V[0xF] = self.V[self.instruction.x] & 0x1 self.V[self.instruction.x] >>= 0x1 self.PC += 2 print("=>Shifted V" + str(self.instruction.x) + " right. Is now: " + str(self.V[self.instruction.x]) + " | VF: " + str(self.V[0xF])) self.jumps = 0 return if opc.n == 0xE: self.V[0xF] = self.V[self.instruction.x] & 0x1 self.V[self.instruction.x] <<= 0x1 self.PC += 2 print("=>Shifted V" + str(self.instruction.x) + " left. Is now: " + str(self.V[self.instruction.x]) + " | VF: " + str(self.V[0xF])) self.jumps = 0 return if opc.i == 0x9: # 9XY0: Skip next instruction if VX != VY if self.V[self.instruction.x] != self.V[self.instruction.y]: self.PC += 4 print("V" + str(self.instruction.x) + " != V" + str(self.instruction.y) + " [" + str(self.V[self.instruction.x]) + " != " + str(self.V[self.instruction.y]) + "] || Skipped instruction") else: self.PC += 2 print("V" + str(self.instruction.x) + " == V" + str(self.instruction.y) + " [" + str(self.V[self.instruction.x]) + " == " + str(self.V[self.instruction.y]) + " || Didn't skip instruction") self.jumps = 0 return if opc.i == 0xA: # ANNN: Set I = NNN self.I = opc.nnn self.PC += 2 print("=>Set I to " + str(hex(opc.nnn))) self.jumps = 0 return if opc.i == 0xC: # CXNN: Set VX = random(255) & KK self.V[self.instruction.x] = random.randint(0, 255) & self.instruction.kk self.PC += 2 print("Set V" + str(self.instruction.x) + " to " + str(self.V[self.instruction.x])) self.jumps = 0 return if opc.i == 0xD: # DXYN: Draw Sprite located at I with height N at X, Y self.V[0xF] = 0x00 for y in range(0, opc.n): line = self.memory[self.I + y] print("----" + str(line) + "----") for x in range(0, 8): pixel = line & (0x80 >> x) if pixel is not 0: total_x = self.V[self.instruction.x] + x total_y = self.V[self.instruction.y] + y index = total_y * 64 + total_x if self.display[index] is "#": self.V[0xF] = 1 if self.display[index] is "#": self.display[index] = "." else: self.display[index] = "#" self.PC += 2 self.drawFlag = True print("=>Drew sprite located at " + str(hex(self.I)) + " that is " + str(opc.n) + " bytes long at position (" + str(self.V[self.instruction.x]) + ", " + str(self.V[self.instruction.y]) + ")") self.jumps = 0 return if opc.i == 0xF: # Multi Case if opc.kk == 0x07: self.V[self.instruction.x] = self.delay_timer self.PC += 2 print("Set V" + str(self.instruction.x) + " to delay timer (" + str(self.delay_timer) + ")") return if opc.kk == 0x15: self.delay_timer = self.V[self.instruction.x] self.PC += 2 print("Set delay timer to V" + str(self.instruction.x) + " (" + str(self.delay_timer) + ")") return if opc.kk == 0x1E: # FX1E: Set I = I + VX self.I = (self.I + self.V[self.instruction.x]) & 0xFFFF self.PC += 2 print("=>Added V" + str(self.instruction.x) + "(" + str(self.V[self.instruction.x]) + ") to I. Is now: " + str(hex(self.I))) self.jumps = 0 return if opc.kk == 0x55: # FX55: Copy values of register V0-VX into memory, starting at register I for offset in range(0, self.instruction.x + 1): self.memory[self.I + offset] = self.V[offset] self.PC += 2 print("=>Stored " + str(self.V[0:self.instruction.x + 1]) + " at " + str(hex(self.I))) self.jumps = 0 return if opc.kk == 0x65: # FX55: Copy values of register V0-VX into memory, starting at register I for offset in range(0, self.instruction.x + 1): self.V[offset] = self.memory[self.I + offset] self.PC += 2 print("=>Stored " + str(self.V[0:self.instruction.x + 1]) + " from " + str(hex(self.I))) self.jumps = 0 return self.interrupt = True return "Unknown Opcode: " + str(hex(self.instruction.op)) # TODO: Redo opcodes. Weird stuff happening. Probably typo. Do it carefully this time