From 908f42587b9483dbed837f785ab512c95bea050a Mon Sep 17 00:00:00 2001 From: Felipe Lima Date: Sun, 14 Jun 2015 23:37:41 -0700 Subject: [PATCH] Adds more instructions --- app/build.gradle | 3 + app/src/main/kotlin/android/emu6502/CPU.kt | 109 ++++++++++++++++-- app/src/main/kotlin/android/emu6502/Utils.kt | 4 + .../android/emu6502/instructions/impl/ADC.kt | 6 +- .../android/emu6502/instructions/impl/BRK.kt | 12 ++ .../android/emu6502/instructions/impl/INX.kt | 13 +++ .../android/emu6502/instructions/impl/TAX.kt | 13 +++ .../test/java/android/emu6502/CPUTest.java | 76 ++++++++++++ 8 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/BRK.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/INX.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/TAX.kt create mode 100644 app/src/test/java/android/emu6502/CPUTest.java diff --git a/app/build.gradle b/app/build.gradle index c0b30e0..c82139c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,9 @@ android { sourceSets { main.java.srcDirs += 'src/main/kotlin' } + testOptions { + unitTests.returnDefaultValues = true + } } dependencies { diff --git a/app/src/main/kotlin/android/emu6502/CPU.kt b/app/src/main/kotlin/android/emu6502/CPU.kt index dd45650..58ffd95 100644 --- a/app/src/main/kotlin/android/emu6502/CPU.kt +++ b/app/src/main/kotlin/android/emu6502/CPU.kt @@ -35,6 +35,8 @@ class CPU(private val memory: Memory) { Pair(Instruction.LDY, LDY(this)), Pair(Instruction.STA, STA(memory, this)), Pair(Instruction.STX, STX(this)), + Pair(Instruction.TAX, TAX(this)), + Pair(Instruction.INX, INX(this)), Pair(Instruction.ORA, ORA(this)) // Pair(Instruction.BPL, BPL(this)), // Pair(Instruction.BMI, BMI(this)), @@ -62,10 +64,8 @@ class CPU(private val memory: Memory) { // Pair(Instruction.JSR, JSR(this)), // Pair(Instruction.LSR, LSR(this)), // Pair(Instruction.NOP, NOP(this)), -// Pair(Instruction.TAX, TAX(this)), // Pair(Instruction.TXA, TXA(this)), // Pair(Instruction.DEX, DEX(this)), -// Pair(Instruction.INX, INX(this)), // Pair(Instruction.TAY, TAY(this)), // Pair(Instruction.TYA, TYA(this)), // Pair(Instruction.DEY, DEY(this)), @@ -94,7 +94,7 @@ class CPU(private val memory: Memory) { break } } - isRunning = false + stop() Log.i(TAG, "Program end at PC=$" + (PC - 1).toHexString() + ", A=$" + A.toHexString() + ", X=$" + X.toHexString() + ", Y=$" + Y.toHexString()) } @@ -107,12 +107,16 @@ class CPU(private val memory: Memory) { target.operation.function() } else { Log.e(TAG, "Address $" + PC.toHexString() + " - unknown opcode " + instruction.toHexString()) - isRunning = false + stop() } } + fun stop() { + isRunning = false + } + fun popByte(): Int { - return memory.get(PC++).and(0xff); + return memory.get(PC++).and(0xff) } private fun setRandomByte() { @@ -123,20 +127,107 @@ class CPU(private val memory: Memory) { setSZFlagsForValue(A) } + fun setSZflagsForRegX() { + setSZFlagsForValue(X) + } + private fun setSZFlagsForValue(value: Int) { if (value != 0) { - P = P.and(0xfd); + P = P.and(0xfd) } else { - P = P.or(0x02); + P = P.or(0x02) } if (value.and(0x80) != 0) { - P = P.or(0x80); + P = P.or(0x80) } else { - P = P.and(0x7f); + P = P.and(0x7f) } } fun popWord(): Int { return popByte() + popByte().shl(8) } + + fun testADC(value: Int) { + var tmp: Int + if (A.xor(value).and(0x80) != 0) { + CLV() + } else { + setOverflow() + } + + if (decimalMode().isSet()) { + tmp = A.and(0xf) + value.and(0xf) + carry() + if (tmp >= 10) { + tmp = 0x10.or((tmp + 6).and(0xf)) + } + tmp += A.and(0xf0) + value.and(0xf0) + if (tmp >= 160) { + SEC() + if (overflow().isSet() && tmp >= 0x180) { + CLV() + } + tmp += 0x60 + } else { + CLC() + if (overflow().isSet() && tmp < 0x80) { + CLV() + } + } + } else { + tmp = A + value + carry() + if (tmp >= 0x100) { + SEC() + if (overflow().isSet() && tmp >= 0x180) { + CLV() + } + } else { + CLC() + if (overflow().isSet() && tmp < 0x80) { + CLV() + } + } + } + A = tmp.and(0xff) + setSZFlagsForRegA() + } + + fun overflow(): Int { + return P.and(0x40) + } + + fun decimalMode(): Int { + return P.and(8); + } + + fun carry(): Int { + return P.and(1); + } + + fun negative(): Int { + return P.and(0x80); + } + + fun zero(): Int { + return P.and(0x02); + } + + /** CLear Carry */ + fun CLC() { + P = P.and(0xfe) + } + + /** SEt Carry */ + fun SEC() { + P = P.or(1) + } + + /** CLear oVerflow */ + fun CLV() { + P = P.and(0xbf) + } + + fun setOverflow() { + P = P.or(0x40) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/Utils.kt b/app/src/main/kotlin/android/emu6502/Utils.kt index 0fdccb0..3311e51 100644 --- a/app/src/main/kotlin/android/emu6502/Utils.kt +++ b/app/src/main/kotlin/android/emu6502/Utils.kt @@ -2,4 +2,8 @@ package android.emu6502 fun Int.toHexString(): String { return java.lang.String.format("%02X", this); +} + +fun Int.isSet(): Boolean { + return this != 0 } \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/ADC.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/ADC.kt index f5c3759..041ed8f 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/ADC.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/ADC.kt @@ -5,6 +5,8 @@ import android.emu6502.instructions.BaseInstruction import android.emu6502.instructions.Instruction /** ADd with Carry */ -class ADC(cpu: CPU) - : BaseInstruction(Instruction.ADC, cpu.instructionList) { +class ADC(private val cpu: CPU) : BaseInstruction(Instruction.ADC, cpu.instructionList) { + override fun immediate() { + cpu.testADC(cpu.popByte()) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/BRK.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/BRK.kt new file mode 100644 index 0000000..5dcedca --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/BRK.kt @@ -0,0 +1,12 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** BRreaK */ +final class BRK(private val cpu: CPU) : BaseInstruction(Instruction.INX, cpu.instructionList) { + override fun single() { + cpu.stop() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/INX.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/INX.kt new file mode 100644 index 0000000..abd1487 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/INX.kt @@ -0,0 +1,13 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** INcrement X */ +class INX(private val cpu: CPU) : BaseInstruction(Instruction.INX, cpu.instructionList) { + override fun single() { + cpu.X = (cpu.X + 1).and(0xff) + cpu.setSZflagsForRegX() + } +} diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/TAX.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/TAX.kt new file mode 100644 index 0000000..bf51181 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/TAX.kt @@ -0,0 +1,13 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** Transfer A to X */ +class TAX(private val cpu: CPU) : BaseInstruction(Instruction.TAX, cpu.instructionList) { + override fun single() { + cpu.X = cpu.A.and(0xFF) + cpu.setSZflagsForRegX() + } +} diff --git a/app/src/test/java/android/emu6502/CPUTest.java b/app/src/test/java/android/emu6502/CPUTest.java new file mode 100644 index 0000000..cc02166 --- /dev/null +++ b/app/src/test/java/android/emu6502/CPUTest.java @@ -0,0 +1,76 @@ +package android.emu6502; + +import com.google.common.collect.ImmutableList; + +import android.emu6502.instructions.Symbols; + +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +public class CPUTest { + + private CPU cpu; + private Assembler assembler; + + @Before public void setUp() { + Memory memory = new Memory(new Display()); + assembler = new Assembler(memory, new Symbols()); + cpu = new CPU(memory); + } + + @Test public void testSimple() { + List lines = ImmutableList.of( + "LDA #$01", + "STA $0200", + "LDA #$05", + "STA $0201", + "LDA #$08", + "STA $0202"); + assembler.assembleCode(lines); + cpu.execute(); + assertThat(cpu.getA(), equalTo(0x08)); + assertThat(cpu.getX(), equalTo(0x00)); + assertThat(cpu.getY(), equalTo(0x00)); + assertThat(cpu.getSP(), equalTo(0xFF)); + assertThat(cpu.getPC(), equalTo(0x0610)); + } + + @Test public void testWithComments() { + List lines = ImmutableList.of( + "LDA #$c0 ;Load the hex value $c0 into the A register", + "TAX ;Transfer the value in the A register to X", + "INX ;Increment the value in the X register", + "ADC #$c4 ;Add the hex value $c4 to the A register", + "BRK ;Break - we're done"); + assembler.assembleCode(lines); + cpu.execute(); + assertThat(cpu.getA(), equalTo(0x84)); + assertThat(cpu.getX(), equalTo(0xC1)); + assertThat(cpu.getY(), equalTo(0x00)); + assertThat(cpu.getSP(), equalTo(0xFF)); + assertThat(cpu.getPC(), equalTo(0x0607)); + } + + @Test public void testBranchAndLabel() { + List lines = ImmutableList.of( + "LDX #$08", + "decrement:", + "DEX", + "STX $0200", + "CPX #$03", + "BNE decrement", + "STX $0201", + "BRK"); + assembler.assembleCode(lines); + assertThat(cpu.getA(), equalTo(0x00)); + assertThat(cpu.getX(), equalTo(0x03)); + assertThat(cpu.getY(), equalTo(0x00)); + assertThat(cpu.getSP(), equalTo(0xFF)); + assertThat(cpu.getPC(), equalTo(0x060e)); + } +}