aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/kotlin/BitManipulation.kt11
-rw-r--r--src/main/kotlin/cpu/Registers.kt32
-rw-r--r--src/main/kotlin/cpu/opcodes/Loads16Bit.kt23
-rw-r--r--src/test/kotlin/RegisterTest.kt54
4 files changed, 119 insertions, 1 deletions
diff --git a/src/main/kotlin/BitManipulation.kt b/src/main/kotlin/BitManipulation.kt
index b4215e8..b655718 100644
--- a/src/main/kotlin/BitManipulation.kt
+++ b/src/main/kotlin/BitManipulation.kt
@@ -28,4 +28,15 @@ object BitManipulation {
}
}
+ fun isSignedBitNegative(value: Int): Boolean {
+ return value and (1 shl 7) != 0
+ }
+
+ fun getAbsoluteValue(value: Int ): Int{
+ return if(isSignedBitNegative(value)) {
+ 0x0100 - value // 1 00000000 - 1xxxxxx = value, due to 2s compliment
+ } else {
+ value
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/kotlin/cpu/Registers.kt b/src/main/kotlin/cpu/Registers.kt
index 70c7f06..54539a8 100644
--- a/src/main/kotlin/cpu/Registers.kt
+++ b/src/main/kotlin/cpu/Registers.kt
@@ -131,4 +131,36 @@ class Registers {
return currentSP
}
+ fun getFlag(flag: Flag): Int {
+ return (F shr flag.bitPosition) and 0x01
+ }
+
+ fun setFlag(flag: Flag) {
+ F = F or (1 shl flag.bitPosition)
+ }
+
+ fun clearFlag(flag: Flag) {
+ F = F and (1 shl flag.bitPosition).inv()
+ }
+
+ fun setFlagFromBool(flag: Flag, isSet: Boolean) {
+ if(isSet) {
+ setFlag(flag)
+ }
+ else {
+ clearFlag(flag)
+ }
+ }
+
+ fun clearFlags() {
+ F = 0x00
+ }
+
+ enum class Flag(val bitPosition: Int) {
+ ZERO(7),
+ SUBTRACT(6),
+ HALF_CARRY(5),
+ CARRY(4),
+ }
+
} \ No newline at end of file
diff --git a/src/main/kotlin/cpu/opcodes/Loads16Bit.kt b/src/main/kotlin/cpu/opcodes/Loads16Bit.kt
index 671a02d..830f6ab 100644
--- a/src/main/kotlin/cpu/opcodes/Loads16Bit.kt
+++ b/src/main/kotlin/cpu/opcodes/Loads16Bit.kt
@@ -2,6 +2,7 @@ package cpu.opcodes
import cpu.Operation
import cpu.Registers
+import cpu.Registers.Flag
import ram.Ram
import BitManipulation as bm
@@ -15,7 +16,27 @@ var loads16Bit = mapOf(
0xF9 to Operation("LD SP,HL", 0, 8, {r, _, _ -> r.SP = r.HL}),
- 0xF8 to Operation("LDHL SP,n", 1, 12, {r, _, a -> r.HL = (r.SP + a[0])}), // TODO - not sure what the flags should be here
+ 0xF8 to Operation("LDHL SP,n", 1, 12, {r, _, a ->
+
+ // https://stackoverflow.com/questions/5159603/gbz80-how-does-ld-hl-spe-affect-h-and-c-flags
+ // the carry flag is set if there's an overflow from the 7th to 8th bit.
+ // the half carry flag is set if there's an overflow from the 3rd into the 4th bit.
+ r.clearFlags()
+
+ val absoluteValue = bm.getAbsoluteValue(a[0])
+
+ if (bm.isSignedBitNegative(a[0])) {
+ r.setFlagFromBool(Flag.HALF_CARRY, r.SP and 0x0F < absoluteValue and 0x0F)
+ r.setFlagFromBool(Flag.CARRY,r.SP and 0xFF < absoluteValue)
+ r.HL = r.SP - absoluteValue
+
+ } else {
+ r.setFlagFromBool(Flag.HALF_CARRY,(r.SP and 0x0F) + (absoluteValue and 0x0F) > 0x0F)
+ r.setFlagFromBool(Flag.CARRY,(r.SP and 0xFF) + absoluteValue > 0xFF)
+ r.HL = r.SP + absoluteValue
+ }
+
+ }),
0x08 to Operation("LD (nn),SP", 2, 20, {r, m, a ->
m.writeByte(bm.argsToWord(a), bm.getLsb(r.SP))
m.writeByte(bm.argsToWord(a) + 1, bm.getMsb(r.SP))
diff --git a/src/test/kotlin/RegisterTest.kt b/src/test/kotlin/RegisterTest.kt
index 7b9188c..617d6f7 100644
--- a/src/test/kotlin/RegisterTest.kt
+++ b/src/test/kotlin/RegisterTest.kt
@@ -1,4 +1,6 @@
+
import cpu.Registers
+import cpu.Registers.Flag
import io.kotlintest.shouldBe
import io.kotlintest.shouldThrow
import io.kotlintest.specs.StringSpec
@@ -124,4 +126,56 @@ class RegisterTest : StringSpec({
reg.PC shouldBe 0xAAFE
}
+ "Test flags" {
+ val reg = Registers()
+
+ reg.getFlag(Flag.ZERO) shouldBe 0
+ reg.getFlag(Flag.SUBTRACT) shouldBe 0
+ reg.getFlag(Flag.HALF_CARRY) shouldBe 0
+ reg.getFlag(Flag.CARRY) shouldBe 0
+
+ reg.setFlag(Flag.ZERO)
+ reg.setFlag(Flag.SUBTRACT)
+ reg.setFlag(Flag.HALF_CARRY)
+ reg.setFlag(Flag.CARRY)
+
+ reg.getFlag(Flag.ZERO) shouldBe 1
+ reg.getFlag(Flag.SUBTRACT) shouldBe 1
+ reg.getFlag(Flag.HALF_CARRY) shouldBe 1
+ reg.getFlag(Flag.CARRY) shouldBe 1
+ reg.F shouldBe 0xF0
+
+ reg.clearFlag(Flag.ZERO)
+ reg.clearFlag(Flag.SUBTRACT)
+ reg.clearFlag(Flag.HALF_CARRY)
+ reg.clearFlag(Flag.CARRY)
+
+ reg.getFlag(Flag.ZERO) shouldBe 0
+ reg.getFlag(Flag.SUBTRACT) shouldBe 0
+ reg.getFlag(Flag.HALF_CARRY) shouldBe 0
+ reg.getFlag(Flag.CARRY) shouldBe 0
+ reg.F shouldBe 0x00
+
+ reg.setFlag(Flag.ZERO)
+ reg.setFlag(Flag.HALF_CARRY)
+
+ reg.getFlag(Flag.ZERO) shouldBe 1
+ reg.getFlag(Flag.SUBTRACT) shouldBe 0
+ reg.getFlag(Flag.HALF_CARRY) shouldBe 1
+ reg.getFlag(Flag.CARRY) shouldBe 0
+ reg.F shouldBe 0xA0 // 10100000
+
+ reg.setFlag(Flag.SUBTRACT)
+ reg.setFlag(Flag.CARRY)
+ reg.clearFlag(Flag.ZERO)
+ reg.clearFlag(Flag.HALF_CARRY)
+
+ reg.getFlag(Flag.ZERO) shouldBe 0
+ reg.getFlag(Flag.SUBTRACT) shouldBe 1
+ reg.getFlag(Flag.HALF_CARRY) shouldBe 0
+ reg.getFlag(Flag.CARRY) shouldBe 1
+ reg.F shouldBe 0x50 // 01010000
+
+ }
+
}) \ No newline at end of file