From c6a953d722b1fdc27a9db6f78f00c52fe43a7222 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Sat, 21 Jul 2018 19:46:20 +0100 Subject: Add ability to step through CPU execution of boot rom --- src/main/kotlin/cpu/Cpu.kt | 61 +++++++++++++++++++++++++++++++ src/main/kotlin/gui/CpuRegisterWindow.kt | 2 +- src/main/kotlin/gui/RunControlWindow.kt | 46 +++++++++++++++++++++++ src/main/kotlin/gui/WindowContainer.kt | 6 +++ src/main/kotlin/ram/Ram.kt | 7 ++++ src/main/resources/roms/boot-rom.gb | Bin 0 -> 256 bytes 6 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/gui/RunControlWindow.kt create mode 100644 src/main/resources/roms/boot-rom.gb diff --git a/src/main/kotlin/cpu/Cpu.kt b/src/main/kotlin/cpu/Cpu.kt index 91150c4..f474dbb 100644 --- a/src/main/kotlin/cpu/Cpu.kt +++ b/src/main/kotlin/cpu/Cpu.kt @@ -2,12 +2,16 @@ package cpu import cpu.opcodes.* import ram.Ram +import kotlin.concurrent.thread class Cpu { val registers = Registers() val ram = Ram() + var currentOp: Operation? = null + var nextOp: Operation? = null + var standardOpcodes: Map var extendedOpcodes: Map init { @@ -33,4 +37,61 @@ class Cpu { } + fun loadRom(rom: ByteArray) { + ram.load(rom) + } + + fun executeNextInstruction() { + + val (op, opArgs) = getNextOp() + op.command.invoke(registers, ram, opArgs) + + currentOp = op + nextOp = peekNextOp() + } + + fun run() { + thread { + while(true) { + executeNextInstruction() + } + } + + } + + private fun getNextOp(): Pair { + + var addr = registers.PC + val startByte = ram.readByte(addr) + val op: Operation + + op = if(startByte == 0xCB) { + extendedOpcodes[ram.readByte(++addr)]!! + } else { + standardOpcodes[startByte]!! + } + + val opLength = op.length + val opArgs = IntArray(opLength) + + IntRange(0, opLength -1).forEach {i -> + opArgs[i] = ram.readByte(++addr) + } + + registers.PC = ++addr + + return Pair(op, opArgs) + } + + + private fun peekNextOp(): Operation { + + val startByte = ram.readByte(registers.PC) + return if(startByte == 0xCB) { + extendedOpcodes[ram.readByte(registers.PC + 1)]!! + } else { + standardOpcodes[startByte]!! + } + } + } \ No newline at end of file diff --git a/src/main/kotlin/gui/CpuRegisterWindow.kt b/src/main/kotlin/gui/CpuRegisterWindow.kt index 0de7ad6..618d298 100644 --- a/src/main/kotlin/gui/CpuRegisterWindow.kt +++ b/src/main/kotlin/gui/CpuRegisterWindow.kt @@ -7,7 +7,7 @@ import imgui.ImGui fun paintCpuRegisterWindow(registers: Registers) { with(ImGui) { - setNextWindowSize(Vec2(200, 300), Cond.FirstUseEver) + setNextWindowSize(Vec2(200, 250), Cond.FirstUseEver) setNextWindowPos(Vec2(20, 150), Cond.FirstUseEver) begin("CPU state") diff --git a/src/main/kotlin/gui/RunControlWindow.kt b/src/main/kotlin/gui/RunControlWindow.kt new file mode 100644 index 0000000..8ffc764 --- /dev/null +++ b/src/main/kotlin/gui/RunControlWindow.kt @@ -0,0 +1,46 @@ +package gui + +import cpu.Cpu +import glm_.vec2.Vec2 +import imgui.Cond +import imgui.ImGui + +fun paintRunControlWindow(cpu: Cpu) { + with(ImGui) { + + setNextWindowPos(Vec2(20, 410), Cond.FirstUseEver) + begin("Run control") + + text("Current op:") + separator() + if(cpu.currentOp != null) { + text(cpu.currentOp!!.name) + } + else { + text("None") + } + + newLine() + + text("Next op:") + separator() + if(cpu.nextOp != null) { + text(cpu.nextOp!!.name) + } + else { + text("None") + } + + newLine() + + text("Control:") + separator() + + + if(button("Step")) { + cpu.executeNextInstruction() + } + + end() + } +} \ No newline at end of file diff --git a/src/main/kotlin/gui/WindowContainer.kt b/src/main/kotlin/gui/WindowContainer.kt index 5e101b7..4d58c18 100644 --- a/src/main/kotlin/gui/WindowContainer.kt +++ b/src/main/kotlin/gui/WindowContainer.kt @@ -10,6 +10,7 @@ import imgui.impl.LwjglGL3 import org.lwjgl.opengl.GL11 import uno.glfw.GlfwWindow import uno.glfw.glfw +import java.io.File class WindowContainer { @@ -18,6 +19,10 @@ class WindowContainer { init { glfw.init("3.2") cpu = Cpu() + val file = File("src/main/resources/roms/boot-rom.gb") + val rom = file.readBytes() + cpu.loadRom(rom) + //cpu.run() } val window = GlfwWindow(1280, 720, "KGB - KotlinGameBoy").apply { @@ -52,6 +57,7 @@ class WindowContainer { paintDebugWindow() paintEmulationOutputWindow() paintCpuRegisterWindow(cpu.registers) + paintRunControlWindow(cpu) } gln.glViewport(window.framebufferSize) diff --git a/src/main/kotlin/ram/Ram.kt b/src/main/kotlin/ram/Ram.kt index 93336a7..7614040 100644 --- a/src/main/kotlin/ram/Ram.kt +++ b/src/main/kotlin/ram/Ram.kt @@ -13,4 +13,11 @@ class Ram { ram[address] = data } + fun load(rom: ByteArray) { + + rom.forEachIndexed{i, b -> + ram[i] = b.toInt() and 0xFF + } + } + } \ No newline at end of file diff --git a/src/main/resources/roms/boot-rom.gb b/src/main/resources/roms/boot-rom.gb new file mode 100644 index 0000000..afa0ee4 Binary files /dev/null and b/src/main/resources/roms/boot-rom.gb differ -- cgit v1.2.3