Adds more instructions

This commit is contained in:
Felipe Lima 2015-06-14 23:37:41 -07:00
parent b793402c90
commit 908f42587b
8 changed files with 225 additions and 11 deletions

View File

@ -21,6 +21,9 @@ android {
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
testOptions {
unitTests.returnDefaultValues = true
}
} }
dependencies { dependencies {

View File

@ -35,6 +35,8 @@ class CPU(private val memory: Memory) {
Pair(Instruction.LDY, LDY(this)), Pair(Instruction.LDY, LDY(this)),
Pair(Instruction.STA, STA(memory, this)), Pair(Instruction.STA, STA(memory, this)),
Pair(Instruction.STX, STX(this)), Pair(Instruction.STX, STX(this)),
Pair(Instruction.TAX, TAX(this)),
Pair(Instruction.INX, INX(this)),
Pair(Instruction.ORA, ORA(this)) Pair(Instruction.ORA, ORA(this))
// Pair(Instruction.BPL, BPL(this)), // Pair(Instruction.BPL, BPL(this)),
// Pair(Instruction.BMI, BMI(this)), // Pair(Instruction.BMI, BMI(this)),
@ -62,10 +64,8 @@ class CPU(private val memory: Memory) {
// Pair(Instruction.JSR, JSR(this)), // Pair(Instruction.JSR, JSR(this)),
// Pair(Instruction.LSR, LSR(this)), // Pair(Instruction.LSR, LSR(this)),
// Pair(Instruction.NOP, NOP(this)), // Pair(Instruction.NOP, NOP(this)),
// Pair(Instruction.TAX, TAX(this)),
// Pair(Instruction.TXA, TXA(this)), // Pair(Instruction.TXA, TXA(this)),
// Pair(Instruction.DEX, DEX(this)), // Pair(Instruction.DEX, DEX(this)),
// Pair(Instruction.INX, INX(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)),
@ -94,7 +94,7 @@ class CPU(private val memory: Memory) {
break break
} }
} }
isRunning = false stop()
Log.i(TAG, "Program end at PC=$" + (PC - 1).toHexString() + ", A=$" + A.toHexString() + Log.i(TAG, "Program end at PC=$" + (PC - 1).toHexString() + ", A=$" + A.toHexString() +
", X=$" + X.toHexString() + ", Y=$" + Y.toHexString()) ", X=$" + X.toHexString() + ", Y=$" + Y.toHexString())
} }
@ -107,12 +107,16 @@ class CPU(private val memory: Memory) {
target.operation.function() target.operation.function()
} else { } else {
Log.e(TAG, "Address $" + PC.toHexString() + " - unknown opcode " + instruction.toHexString()) Log.e(TAG, "Address $" + PC.toHexString() + " - unknown opcode " + instruction.toHexString())
isRunning = false stop()
} }
} }
fun stop() {
isRunning = false
}
fun popByte(): Int { fun popByte(): Int {
return memory.get(PC++).and(0xff); return memory.get(PC++).and(0xff)
} }
private fun setRandomByte() { private fun setRandomByte() {
@ -123,20 +127,107 @@ class CPU(private val memory: Memory) {
setSZFlagsForValue(A) setSZFlagsForValue(A)
} }
fun setSZflagsForRegX() {
setSZFlagsForValue(X)
}
private fun setSZFlagsForValue(value: Int) { private fun setSZFlagsForValue(value: Int) {
if (value != 0) { if (value != 0) {
P = P.and(0xfd); P = P.and(0xfd)
} else { } else {
P = P.or(0x02); P = P.or(0x02)
} }
if (value.and(0x80) != 0) { if (value.and(0x80) != 0) {
P = P.or(0x80); P = P.or(0x80)
} else { } else {
P = P.and(0x7f); P = P.and(0x7f)
} }
} }
fun popWord(): Int { fun popWord(): Int {
return popByte() + popByte().shl(8) 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)
}
} }

View File

@ -2,4 +2,8 @@ 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
} }

View File

@ -5,6 +5,8 @@ import android.emu6502.instructions.BaseInstruction
import android.emu6502.instructions.Instruction import android.emu6502.instructions.Instruction
/** ADd with Carry */ /** ADd with Carry */
class ADC(cpu: CPU) class ADC(private val cpu: CPU) : BaseInstruction(Instruction.ADC, cpu.instructionList) {
: BaseInstruction(Instruction.ADC, cpu.instructionList) { override fun immediate() {
cpu.testADC(cpu.popByte())
}
} }

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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<String> 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<String> 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<String> 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));
}
}