Add files via upload
This commit is contained in:
parent
3508c7d0ed
commit
74e3673a4f
10 changed files with 416 additions and 0 deletions
328
CPU.py
Normal file
328
CPU.py
Normal file
|
@ -0,0 +1,328 @@
|
|||
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
|
BIN
demo1.ch8
Normal file
BIN
demo1.ch8
Normal file
Binary file not shown.
BIN
demo2.ch8
Normal file
BIN
demo2.ch8
Normal file
Binary file not shown.
BIN
demo3.ch8
Normal file
BIN
demo3.ch8
Normal file
Binary file not shown.
BIN
demo4.ch8
Normal file
BIN
demo4.ch8
Normal file
Binary file not shown.
BIN
demo5.ch8
Normal file
BIN
demo5.ch8
Normal file
Binary file not shown.
BIN
demo6.ch8
Normal file
BIN
demo6.ch8
Normal file
Binary file not shown.
9
instruction.py
Normal file
9
instruction.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
class Opcode:
|
||||
def __init__(self, opc: int):
|
||||
self.op = opc
|
||||
self.i = (opc & 0xF000) >> 12
|
||||
self.nnn = opc & 0x0FFF
|
||||
self.n = (opc & 0x000F)
|
||||
self.x = (opc & 0x0F00) >> 8
|
||||
self.y = (opc & 0x00F0) >> 4
|
||||
self.kk = (opc & 0x00FF)
|
79
main.py
Normal file
79
main.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
import discord
|
||||
from instruction import Opcode
|
||||
from CPU import CPU
|
||||
import time
|
||||
|
||||
client = discord.Client()
|
||||
cpu = CPU
|
||||
allowed_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
|
||||
demos = 6
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
await client.change_presence(game=discord.Game(name="Emulator"), status=discord.Status("idle"))
|
||||
print("Ready to emulate!")
|
||||
# while not cpu.interrupt:
|
||||
# cpu.tick(cpu)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_message(message):
|
||||
|
||||
if message.content.lower().startswith("?break"):
|
||||
cpu.interrupt = True
|
||||
|
||||
if message.content.lower().startswith("?start"):
|
||||
if cpu.interrupt is False:
|
||||
await client.send_message(discord.Object(id='485511121407574016'), "CPU already in use")
|
||||
return
|
||||
|
||||
if len(message.content) < 8:
|
||||
await client.send_message(discord.Object(id='485511121407574016'), "Please choose a valid demo file")
|
||||
return
|
||||
|
||||
if not message.content[7:].isdigit():
|
||||
await client.send_message(discord.Object(id='485511121407574016'), "Please choose a valid demo file")
|
||||
return
|
||||
|
||||
demo = message.content[7:]
|
||||
if int(demo) > 6 or int(demo) < 1:
|
||||
await client.send_message(discord.Object(id='485511121407574016'), "Please choose a valid demo file")
|
||||
return
|
||||
|
||||
timer = time.localtime(time.time())[5]
|
||||
cpu.load_rom(cpu, "demo" + demo + ".ch8")
|
||||
cpu.interrupt = False
|
||||
msg = await client.send_message(discord.Object(id='485511121407574016'), "Awaiting display instructions")
|
||||
while not cpu.interrupt:
|
||||
cpu.tick(cpu)
|
||||
|
||||
if cpu.drawFlag is True and (time.localtime(time.time())[5] - timer) >= 1:
|
||||
screen = ""
|
||||
for y in range(0, 31):
|
||||
for x in range(0, 63):
|
||||
screen += cpu.display[y * 64 + x]
|
||||
# print(str(x) + ", " + str(y) + ": " + str(cpu.display[y * 64 + x]))
|
||||
screen += "\n"
|
||||
|
||||
await client.edit_message(msg, "```" + screen + "```")
|
||||
cpu.drawFlag = False
|
||||
timer = time.localtime(time.time())[5]
|
||||
|
||||
await client.send_message(discord.Object(id='485511121407574016'), "Done!")
|
||||
cpu.reset(cpu)
|
||||
|
||||
if message.content.lower().startswith("0x"):
|
||||
opc = message.content.upper()[2:6]
|
||||
|
||||
for char in opc:
|
||||
if char not in allowed_chars:
|
||||
await client.send_message(message.channel, "The character '" + char
|
||||
+ "' is not a valid hexadecimal number!")
|
||||
return
|
||||
|
||||
instruction = Opcode(int(opc, 16))
|
||||
await client.send_message(message.channel, cpu.execute(cpu, instruction))
|
||||
|
||||
|
||||
|
||||
client.run(token)
|
BIN
pong2.c8
Normal file
BIN
pong2.c8
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue