Fixes failing tests
This commit is contained in:
parent
908f42587b
commit
4f9f9c4b9b
|
@ -8,7 +8,7 @@ import android.util.Log
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
import kotlin.reflect.KMemberFunction0
|
import kotlin.reflect.KMemberFunction0
|
||||||
|
|
||||||
class CPU(private val memory: Memory) {
|
class CPU(val memory: Memory) {
|
||||||
// Accumulator
|
// Accumulator
|
||||||
var A: Int = 0
|
var A: Int = 0
|
||||||
// Registers
|
// Registers
|
||||||
|
@ -19,7 +19,7 @@ class CPU(private val memory: Memory) {
|
||||||
// Stack pointer
|
// Stack pointer
|
||||||
var SP: Int = 0xFF
|
var SP: Int = 0xFF
|
||||||
// Processor flags
|
// Processor flags
|
||||||
var P: Int = 0
|
var P: Int = 0x30
|
||||||
private var isRunning = false
|
private var isRunning = false
|
||||||
private var debug = false
|
private var debug = false
|
||||||
private var monitoring = false
|
private var monitoring = false
|
||||||
|
@ -37,18 +37,19 @@ class CPU(private val memory: Memory) {
|
||||||
Pair(Instruction.STX, STX(this)),
|
Pair(Instruction.STX, STX(this)),
|
||||||
Pair(Instruction.TAX, TAX(this)),
|
Pair(Instruction.TAX, TAX(this)),
|
||||||
Pair(Instruction.INX, INX(this)),
|
Pair(Instruction.INX, INX(this)),
|
||||||
Pair(Instruction.ORA, ORA(this))
|
Pair(Instruction.DEX, DEX(this)),
|
||||||
|
Pair(Instruction.ORA, ORA(this)),
|
||||||
|
Pair(Instruction.CPX, CPX(this)),
|
||||||
|
Pair(Instruction.BRK, BRK(this)),
|
||||||
|
Pair(Instruction.BNE, BNE(this))
|
||||||
// Pair(Instruction.BPL, BPL(this)),
|
// Pair(Instruction.BPL, BPL(this)),
|
||||||
// Pair(Instruction.BMI, BMI(this)),
|
// Pair(Instruction.BMI, BMI(this)),
|
||||||
// Pair(Instruction.BVC, BVC(this)),
|
// Pair(Instruction.BVC, BVC(this)),
|
||||||
// Pair(Instruction.BVS, BVS(this)),
|
// Pair(Instruction.BVS, BVS(this)),
|
||||||
// Pair(Instruction.BCC, BCC(this)),
|
// Pair(Instruction.BCC, BCC(this)),
|
||||||
// Pair(Instruction.BCS, BCS(this)),
|
// Pair(Instruction.BCS, BCS(this)),
|
||||||
// Pair(Instruction.BNE, BNE(this)),
|
|
||||||
// Pair(Instruction.BEQ, BEQ(this)),
|
// Pair(Instruction.BEQ, BEQ(this)),
|
||||||
// Pair(Instruction.BRK, BRK(this)),
|
|
||||||
// Pair(Instruction.CMP, CMP(this)),
|
// Pair(Instruction.CMP, CMP(this)),
|
||||||
// Pair(Instruction.CPX, CPX(this)),
|
|
||||||
// Pair(Instruction.CPY, CPY(this)),
|
// Pair(Instruction.CPY, CPY(this)),
|
||||||
// Pair(Instruction.DEC, DEC(this)),
|
// Pair(Instruction.DEC, DEC(this)),
|
||||||
// Pair(Instruction.EOR, EOR(this)),
|
// Pair(Instruction.EOR, EOR(this)),
|
||||||
|
@ -65,7 +66,6 @@ class CPU(private val memory: Memory) {
|
||||||
// Pair(Instruction.LSR, LSR(this)),
|
// Pair(Instruction.LSR, LSR(this)),
|
||||||
// Pair(Instruction.NOP, NOP(this)),
|
// Pair(Instruction.NOP, NOP(this)),
|
||||||
// Pair(Instruction.TXA, TXA(this)),
|
// Pair(Instruction.TXA, TXA(this)),
|
||||||
// Pair(Instruction.DEX, DEX(this)),
|
|
||||||
// Pair(Instruction.TAY, TAY(this)),
|
// Pair(Instruction.TAY, TAY(this)),
|
||||||
// Pair(Instruction.TYA, TYA(this)),
|
// Pair(Instruction.TYA, TYA(this)),
|
||||||
// Pair(Instruction.DEY, DEY(this)),
|
// Pair(Instruction.DEY, DEY(this)),
|
||||||
|
@ -124,14 +124,14 @@ class CPU(private val memory: Memory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSZFlagsForRegA() {
|
fun setSZFlagsForRegA() {
|
||||||
setSZFlagsForValue(A)
|
setSVFlagsForValue(A)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSZflagsForRegX() {
|
fun setSZflagsForRegX() {
|
||||||
setSZFlagsForValue(X)
|
setSVFlagsForValue(X)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSZFlagsForValue(value: Int) {
|
private fun setSVFlagsForValue(value: Int) {
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
P = P.and(0xfd)
|
P = P.and(0xfd)
|
||||||
} else {
|
} else {
|
||||||
|
@ -156,34 +156,34 @@ class CPU(private val memory: Memory) {
|
||||||
setOverflow()
|
setOverflow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decimalMode().isSet()) {
|
if (decimalMode()) {
|
||||||
tmp = A.and(0xf) + value.and(0xf) + carry()
|
tmp = A.and(0xf) + value.and(0xf) + P.and(1)
|
||||||
if (tmp >= 10) {
|
if (tmp >= 10) {
|
||||||
tmp = 0x10.or((tmp + 6).and(0xf))
|
tmp = 0x10.or((tmp + 6).and(0xf))
|
||||||
}
|
}
|
||||||
tmp += A.and(0xf0) + value.and(0xf0)
|
tmp += A.and(0xf0) + value.and(0xf0)
|
||||||
if (tmp >= 160) {
|
if (tmp >= 160) {
|
||||||
SEC()
|
SEC()
|
||||||
if (overflow().isSet() && tmp >= 0x180) {
|
if (overflow() && tmp >= 0x180) {
|
||||||
CLV()
|
CLV()
|
||||||
}
|
}
|
||||||
tmp += 0x60
|
tmp += 0x60
|
||||||
} else {
|
} else {
|
||||||
CLC()
|
CLC()
|
||||||
if (overflow().isSet() && tmp < 0x80) {
|
if (overflow() && tmp < 0x80) {
|
||||||
CLV()
|
CLV()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tmp = A + value + carry()
|
tmp = A + value + P.and(1)
|
||||||
if (tmp >= 0x100) {
|
if (tmp >= 0x100) {
|
||||||
SEC()
|
SEC()
|
||||||
if (overflow().isSet() && tmp >= 0x180) {
|
if (overflow() && tmp >= 0x180) {
|
||||||
CLV()
|
CLV()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CLC()
|
CLC()
|
||||||
if (overflow().isSet() && tmp < 0x80) {
|
if (overflow() && tmp < 0x80) {
|
||||||
CLV()
|
CLV()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,42 +192,85 @@ class CPU(private val memory: Memory) {
|
||||||
setSZFlagsForRegA()
|
setSZFlagsForRegA()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun overflow(): Int {
|
private fun overflow(): Boolean {
|
||||||
return P.and(0x40)
|
return P.and(0x40) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decimalMode(): Int {
|
private fun decimalMode(): Boolean {
|
||||||
return P.and(8);
|
return P.and(8) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun carry(): Int {
|
private fun carry(): Boolean {
|
||||||
return P.and(1);
|
return P.and(1) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun negative(): Int {
|
private fun negative(): Boolean {
|
||||||
return P.and(0x80);
|
return P.and(0x80) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zero(): Int {
|
fun zero(): Boolean {
|
||||||
return P.and(0x02);
|
return P.and(0x02) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jumpBranch(offset: Int) {
|
||||||
|
if (offset > 0x7f) {
|
||||||
|
PC -= (0x100 - offset)
|
||||||
|
} else {
|
||||||
|
PC += offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doCompare(reg: Int, value: Int) {
|
||||||
|
if (reg >= value) {
|
||||||
|
SEC()
|
||||||
|
} else {
|
||||||
|
CLC()
|
||||||
|
}
|
||||||
|
setSVFlagsForValue(reg - value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** CLear Carry */
|
/** CLear Carry */
|
||||||
fun CLC() {
|
private fun CLC() {
|
||||||
P = P.and(0xfe)
|
P = P.and(0xfe)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** SEt Carry */
|
/** SEt Carry */
|
||||||
fun SEC() {
|
private fun SEC() {
|
||||||
P = P.or(1)
|
P = P.or(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** CLear oVerflow */
|
/** CLear oVerflow */
|
||||||
fun CLV() {
|
private fun CLV() {
|
||||||
P = P.and(0xbf)
|
P = P.and(0xbf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOverflow() {
|
private fun setOverflow() {
|
||||||
P = P.or(0x40)
|
P = P.or(0x40)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://nesdev.com/6502.txt
|
||||||
|
* Returns the processor flags in the format SV-BDIZC
|
||||||
|
* Sign - this is set if the result of an operation is negative, cleared if positive.
|
||||||
|
* Overflow - when an arithmetic operation produces a result too large to be represented in a byte
|
||||||
|
* Unused - Supposed to be logical 1 at all times.
|
||||||
|
* Break - this is set when a software interrupt (BRK instruction) is executed.
|
||||||
|
* Decimal Mode - When set, and an Add with Carry or Subtract with Carry instruction is executed,
|
||||||
|
* the source values are treated as valid BCD (Binary Coded Decimal, eg. 0x00-0x99 = 0-99) numbers.
|
||||||
|
* The result generated is also a BCD number.
|
||||||
|
* Interrupt - If it is set, interrupts are disabled. If it is cleared, interrupts are enabled.
|
||||||
|
* Zero - this is set to 1 when any arithmetic or logical operation produces a zero result, and is
|
||||||
|
* set to 0 if the result is non-zero.
|
||||||
|
* Carry - this holds the carry out of the most significant bit in any arithmetic operation.
|
||||||
|
* In subtraction operations however, this flag is cleared - set to 0 - if a borrow is required,
|
||||||
|
* set to 1 - if no borrow is required. The carry flag is also used in shift and rotate logical
|
||||||
|
* operations.
|
||||||
|
* */
|
||||||
|
fun flags(): String {
|
||||||
|
val flags = StringBuilder()
|
||||||
|
for (i in 7 downTo 0) {
|
||||||
|
flags.append(P.shr(i).and(1))
|
||||||
|
}
|
||||||
|
return flags.toString()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,8 +2,4 @@ package android.emu6502
|
||||||
|
|
||||||
fun Int.toHexString(): String {
|
fun Int.toHexString(): String {
|
||||||
return java.lang.String.format("%02X", this);
|
return java.lang.String.format("%02X", this);
|
||||||
}
|
|
||||||
|
|
||||||
fun Int.isSet(): Boolean {
|
|
||||||
return this != 0
|
|
||||||
}
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package android.emu6502.instructions.impl
|
||||||
|
|
||||||
|
import android.emu6502.CPU
|
||||||
|
import android.emu6502.instructions.BaseInstruction
|
||||||
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
|
/** Branch on Not Equal */
|
||||||
|
final class BNE(private val cpu: CPU) : BaseInstruction(Instruction.BNE, cpu.instructionList) {
|
||||||
|
override fun branch() {
|
||||||
|
val offset = cpu.popByte()
|
||||||
|
if (!cpu.zero()) {
|
||||||
|
cpu.jumpBranch(offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import android.emu6502.instructions.BaseInstruction
|
||||||
import android.emu6502.instructions.Instruction
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
/** BRreaK */
|
/** BRreaK */
|
||||||
final class BRK(private val cpu: CPU) : BaseInstruction(Instruction.INX, cpu.instructionList) {
|
final class BRK(private val cpu: CPU) : BaseInstruction(Instruction.BRK, cpu.instructionList) {
|
||||||
override fun single() {
|
override fun single() {
|
||||||
cpu.stop()
|
cpu.stop()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package android.emu6502.instructions.impl
|
||||||
|
|
||||||
|
import android.emu6502.CPU
|
||||||
|
import android.emu6502.instructions.BaseInstruction
|
||||||
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
|
/** ComPare X register */
|
||||||
|
class CPX(private val cpu: CPU) : BaseInstruction(Instruction.CPX, cpu.instructionList) {
|
||||||
|
override fun immediate() {
|
||||||
|
val value = cpu.popByte()
|
||||||
|
cpu.doCompare(cpu.X, value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package android.emu6502.instructions.impl
|
||||||
|
|
||||||
|
import android.emu6502.CPU
|
||||||
|
import android.emu6502.instructions.BaseInstruction
|
||||||
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
|
/**DEcrement X */
|
||||||
|
class DEX(private val cpu: CPU) : BaseInstruction(Instruction.DEX, cpu.instructionList) {
|
||||||
|
override fun single() {
|
||||||
|
cpu.X = (cpu.X - 1).and(0xff)
|
||||||
|
cpu.setSZflagsForRegX()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,9 +5,7 @@ import android.emu6502.instructions.BaseInstruction
|
||||||
import android.emu6502.instructions.Instruction
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
/** LoaD Accumulator */
|
/** LoaD Accumulator */
|
||||||
class LDA(private val cpu: CPU)
|
class LDA(private val cpu: CPU) : BaseInstruction(Instruction.LDA, cpu.instructionList) {
|
||||||
: BaseInstruction(Instruction.LDA, cpu.instructionList) {
|
|
||||||
|
|
||||||
override fun immediate() {
|
override fun immediate() {
|
||||||
cpu.A = cpu.popByte()
|
cpu.A = cpu.popByte()
|
||||||
cpu.setSZFlagsForRegA()
|
cpu.setSZFlagsForRegA()
|
||||||
|
|
|
@ -5,5 +5,9 @@ import android.emu6502.instructions.BaseInstruction
|
||||||
import android.emu6502.instructions.Instruction
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
/** LoaD X register */
|
/** LoaD X register */
|
||||||
class LDX(cpu: CPU) : BaseInstruction(Instruction.LDX, cpu.instructionList) {
|
class LDX(private val cpu: CPU) : BaseInstruction(Instruction.LDX, cpu.instructionList) {
|
||||||
|
override fun immediate() {
|
||||||
|
cpu.X = cpu.popByte()
|
||||||
|
cpu.setSZflagsForRegX()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,5 @@ import android.emu6502.instructions.BaseInstruction
|
||||||
import android.emu6502.instructions.Instruction
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
/** LoaD Y register */
|
/** LoaD Y register */
|
||||||
class LDY(cpu: CPU)
|
class LDY(cpu: CPU) : BaseInstruction(Instruction.LDY, cpu.instructionList) {
|
||||||
: BaseInstruction(Instruction.LDY, cpu.instructionList) {
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,5 @@ import android.emu6502.instructions.BaseInstruction
|
||||||
import android.emu6502.instructions.Instruction
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
/** bitwise OR with Accumulator */
|
/** bitwise OR with Accumulator */
|
||||||
class ORA(cpu: CPU)
|
class ORA(cpu: CPU) : BaseInstruction(Instruction.ORA, cpu.instructionList) {
|
||||||
: BaseInstruction(Instruction.ORA, cpu.instructionList) {
|
|
||||||
}
|
}
|
|
@ -5,6 +5,12 @@ import android.emu6502.instructions.BaseInstruction
|
||||||
import android.emu6502.instructions.Instruction
|
import android.emu6502.instructions.Instruction
|
||||||
|
|
||||||
/** STore X register */
|
/** STore X register */
|
||||||
class STX(cpu: CPU)
|
class STX(private val cpu: CPU) : BaseInstruction(Instruction.STX, cpu.instructionList) {
|
||||||
: BaseInstruction(Instruction.STX, cpu.instructionList) {
|
override fun zeroPage() {
|
||||||
|
cpu.memory.storeByte(cpu.popByte(), cpu.X)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun absolute() {
|
||||||
|
cpu.memory.storeByte(cpu.popWord(), cpu.X)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class CPUTest {
|
||||||
assertThat(cpu.getY(), equalTo(0x00));
|
assertThat(cpu.getY(), equalTo(0x00));
|
||||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||||
assertThat(cpu.getPC(), equalTo(0x0610));
|
assertThat(cpu.getPC(), equalTo(0x0610));
|
||||||
|
assertThat(cpu.flags(), equalTo("00110000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void testWithComments() {
|
@Test public void testWithComments() {
|
||||||
|
@ -54,6 +55,7 @@ public class CPUTest {
|
||||||
assertThat(cpu.getY(), equalTo(0x00));
|
assertThat(cpu.getY(), equalTo(0x00));
|
||||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||||
assertThat(cpu.getPC(), equalTo(0x0607));
|
assertThat(cpu.getPC(), equalTo(0x0607));
|
||||||
|
assertThat(cpu.flags(), equalTo("10110001"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void testBranchAndLabel() {
|
@Test public void testBranchAndLabel() {
|
||||||
|
@ -67,10 +69,12 @@ public class CPUTest {
|
||||||
"STX $0201",
|
"STX $0201",
|
||||||
"BRK");
|
"BRK");
|
||||||
assembler.assembleCode(lines);
|
assembler.assembleCode(lines);
|
||||||
|
cpu.execute();
|
||||||
assertThat(cpu.getA(), equalTo(0x00));
|
assertThat(cpu.getA(), equalTo(0x00));
|
||||||
assertThat(cpu.getX(), equalTo(0x03));
|
assertThat(cpu.getX(), equalTo(0x03));
|
||||||
assertThat(cpu.getY(), equalTo(0x00));
|
assertThat(cpu.getY(), equalTo(0x00));
|
||||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||||
assertThat(cpu.getPC(), equalTo(0x060e));
|
assertThat(cpu.getPC(), equalTo(0x060e));
|
||||||
|
assertThat(cpu.flags(), equalTo("00110011"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue