aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCan2025-01-26 18:11:07 +0100
committerCan2025-01-26 18:11:07 +0100
commitba86b9db29b1cbf3bebea70d98655967ba9da770 (patch)
tree0d77f6687e069e290ce97ccafa77c3e914b28b53 /src
parent1eaabb587a10bad0799360f056d8fa74aa02aa6a (diff)
added keyboard support
Diffstat (limited to 'src')
-rw-r--r--src/cpu.effekt60
-rw-r--r--src/renderer.effekt1
-rw-r--r--src/renderers/js.effekt21
3 files changed, 69 insertions, 13 deletions
diff --git a/src/cpu.effekt b/src/cpu.effekt
index 4a18d22..f347ddd 100644
--- a/src/cpu.effekt
+++ b/src/cpu.effekt
@@ -21,7 +21,27 @@ extern io def getNow(): Int = jsWeb "Date.now()"
interface CPU {
def initCPU(rom: ByteArray): Unit
def cycleCPU(): Unit
+
}
+def convertKey(key: String): Int = key match {
+ case "1" => 1
+ case "2" => 2
+ case "3" => 3
+ case "4" => 12
+ case "q" => 4
+ case "w" => 5
+ case "e" => 6
+ case "r" => 13
+ case "a" => 7
+ case "s" => 8
+ case "d" => 9
+ case "f" => 14
+ case "z" => 10
+ case "x" => 0
+ case "c" => 11
+ case "v" => 15
+ case _ => -1
+}
def makeCPU() {r: Renderer} = {
// Initialize the RAM
@@ -33,7 +53,6 @@ def makeCPU() {r: Renderer} = {
var delay: Byte in global = 0.toByte() // Delay timer
var sound: Byte in global = 0.toByte() // Sound timer
-
var pc: Int in global = 512 // Program counter
var sp: Byte in global = 0.toByte() // Stack pointer
// After checking the implementation of queue in Effekt, I realized that it also works as a stack, so I will use it as a stack
@@ -47,16 +66,27 @@ def makeCPU() {r: Renderer} = {
new CPU {
def initCPU(rom: ByteArray) = {
ram.init(rom)
- r.fill("black")
+ r.clear()
}
def cycleCPU() = {
val currentTime = getNow()
+ var key: Int = -1 // Key pressed
+ if (currentTime - last_key_update_time >= 16) { // 60Hz key updates
+ // Update keys
+ val key_ = r.getKeyPressed().getOrElse { "P" }
+ if (key_ != "P") {
+ last_key_update_time = currentTime
+ key = convertKey(key_)
+ r.log("Key pressed: " ++ show(key))
+ }
+ }
+
if (currentTime - last_instruction_run_time >= 2) { // ~500Hz
val h = ram.getAddr(pc)
val l = ram.getAddr(pc + 1)
val inst = bitwiseOr(bitwiseShl(h.toInt(), 8), l.toInt())
- // r.log("PC: " ++ show(pc))
- // r.log("Instruction: " ++ show(inst))
+ r.log("PC: " ++ show(pc))
+ r.log("Instruction: " ++ show(inst))
// Decode
val opcode = bitwiseShr(bitwiseAnd(inst, 61440), 12)
@@ -225,16 +255,21 @@ def makeCPU() {r: Renderer} = {
pc = pc + 2
}
case 14 => {
+ r.log("nn: " ++ show(nn))
nn match {
- // Skip next instruction if key with the value of VX is pressed
+ // Skip next instruction if key with the value of VX is not pressed
case 161 => {
- // TODO: missing keyboard input
- ()
+ r.log("Key: " ++ show(key) ++ " VX: " ++ show(v.unsafeGet(x)))
+ if (key != -1 && key != v.unsafeGet(x).toInt()) {
+ pc = pc + 2
+ }
}
- // Skip next instruction if key with the value of VX is not pressed
+ // Skip next instruction if key with the value of VX is pressed
case 158 => {
- // TODO: missing keyboard input
- ()
+ r.log("Key: " ++ show(key) ++ " VX: " ++ show(v.unsafeGet(x)))
+ if (key != -1 && key == v.unsafeGet(x).toInt()) {
+ pc = pc + 2
+ }
}
} else {
r.log("Unknown Instruction: " ++ show(inst))
@@ -248,8 +283,9 @@ def makeCPU() {r: Renderer} = {
}
// Wait for a key press, store the value of the key in VX
case 10 => {
- // TODO: missing keyboard input
- ()
+ if (key != -1) {
+ v.set(x, key.toByte())
+ }
}
// SET delay timer = VX
case 21 => {
diff --git a/src/renderer.effekt b/src/renderer.effekt
index 358272b..b4d5343 100644
--- a/src/renderer.effekt
+++ b/src/renderer.effekt
@@ -16,4 +16,5 @@ interface Renderer {
def get(x: Int, y: Int): Bool
def update(f: () => Unit at {io, global}): Unit
def log(msg: String): Unit
+ def getKeyPressed(): Option[String]
}
diff --git a/src/renderers/js.effekt b/src/renderers/js.effekt
index ab7cc53..4aea494 100644
--- a/src/renderers/js.effekt
+++ b/src/renderers/js.effekt
@@ -14,6 +14,7 @@ extern io def loadRom(): ByteArray = jsWeb "rom ? rom : new Uint8Array(0)"
extern io def requestAnimationFrame {callback: () => Unit}: Unit = jsWeb "requestAnimationFrame(() => $effekt.runToplevel((ks) => ${box callback}(ks)))"
extern jsWeb """
let rom = undefined;
+ let key = undefined;
// Global Log Function
function log(msg) {
const message = new Date().toLocaleTimeString() + ' - ' + msg + ' \n'
@@ -28,6 +29,12 @@ extern jsWeb """
document.getElementById('start').removeAttribute('disabled');
});
}
+
+ function keyRegister() {
+ document.addEventListener('keydown', (event) => {
+ window.key = event.key;
+ });
+ }
"""
val pageContent = """
@@ -70,7 +77,7 @@ val width = 640
val height = 320
val scale = 10
-extern io def renderPage(content: String): Unit = jsWeb "document.write(${content}); romCheck();"
+extern io def renderPage(content: String): Unit = jsWeb "document.write(${content}); romCheck(); keyRegister();"
// Initialize the screen
def init(run: (ByteArray) => Unit at {io, global}) = {
renderPage(pageContent)
@@ -126,6 +133,14 @@ extern io def update(f: () => Unit at {io, global}): Unit = jsWeb """
}, 1000 / 120);
"""
+extern io def getKeyPressed(): String = jsWeb """
+ (() => {
+ const key = window.key;
+ window.key = undefined;
+ return key;
+ })();
+"""
+
namespace JSRenderer {
// make JS Renderer
def makeRenderer: Renderer = new Renderer {
@@ -136,5 +151,9 @@ namespace JSRenderer {
def log(msg: String) = log(msg)
def fill(color: String) = fill(color)
def get(x: Int, y: Int) = get(x, y)
+ def getKeyPressed() = {
+ val key = getKeyPressed()
+ undefinedToOption(key)
+ }
}
}