From 657e1b2319f7d1ab886f869386e4dda71d8b6d86 Mon Sep 17 00:00:00 2001 From: Felipe Lima Date: Sun, 28 Jun 2015 23:26:56 -0700 Subject: [PATCH] Snake game partially functional with moving buttons --- app/src/main/kotlin/android/emu6502/CPU.kt | 87 ++++--- .../main/kotlin/android/emu6502/Display.kt | 2 +- app/src/main/kotlin/android/emu6502/Memory.kt | 8 + .../android/emu6502/app/MainActivity.kt | 35 ++- .../android/emu6502/instructions/impl/BCC.kt | 16 ++ .../android/emu6502/instructions/impl/BCS.kt | 15 ++ .../android/emu6502/instructions/impl/BEQ.kt | 16 ++ .../android/emu6502/instructions/impl/BIT.kt | 25 +- .../android/emu6502/instructions/impl/BPL.kt | 15 ++ .../android/emu6502/instructions/impl/CMP.kt | 19 ++ .../android/emu6502/instructions/impl/CPX.kt | 5 + .../android/emu6502/instructions/impl/DEC.kt | 19 ++ .../android/emu6502/instructions/impl/INC.kt | 20 ++ .../android/emu6502/instructions/impl/LDA.kt | 5 + .../android/emu6502/instructions/impl/LDX.kt | 5 + .../android/emu6502/instructions/impl/LDY.kt | 6 +- .../android/emu6502/instructions/impl/LSR.kt | 15 ++ .../android/emu6502/instructions/impl/NOP.kt | 12 + .../android/emu6502/instructions/impl/SBC.kt | 62 +++++ .../android/emu6502/instructions/impl/SEC.kt | 14 ++ .../android/emu6502/instructions/impl/STA.kt | 15 ++ .../android/emu6502/instructions/impl/TXA.kt | 15 ++ app/src/main/res/layout/activity_main.xml | 232 ++++++++++-------- .../test/java/android/emu6502/CPUTest.java | 10 +- 24 files changed, 526 insertions(+), 147 deletions(-) create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/BCC.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/BCS.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/BEQ.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/BPL.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/CMP.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/DEC.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/INC.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/LSR.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/NOP.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/SBC.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/SEC.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/impl/TXA.kt diff --git a/app/src/main/kotlin/android/emu6502/CPU.kt b/app/src/main/kotlin/android/emu6502/CPU.kt index fad8292..13df882 100644 --- a/app/src/main/kotlin/android/emu6502/CPU.kt +++ b/app/src/main/kotlin/android/emu6502/CPU.kt @@ -5,10 +5,20 @@ import android.emu6502.instructions.Instruction import android.emu6502.instructions.InstructionTarget import android.emu6502.instructions.Opcodes import android.emu6502.instructions.impl.* +import android.os.Handler +import android.os.HandlerThread import android.util.Log import java.util.HashMap class CPU(val memory: Memory) { + private val handlerThread = HandlerThread("Screencast Thread") + private val handler: Handler + + init { + handlerThread.start() + handler = Handler(handlerThread.getLooper()) + } + // Accumulator var A: Int = 0 // Registers @@ -47,34 +57,34 @@ class CPU(val memory: Memory) { Pair(Instruction.RTS, RTS(this)), Pair(Instruction.SEI, SEI(this)), Pair(Instruction.DEY, DEY(this)), - Pair(Instruction.CLC, CLC(this)) -// Pair(Instruction.BPL, BPL(this)), + Pair(Instruction.CLC, CLC(this)), + Pair(Instruction.CMP, CMP(this)), + Pair(Instruction.BEQ, BEQ(this)), + Pair(Instruction.TXA, TXA(this)), + Pair(Instruction.BPL, BPL(this)), + Pair(Instruction.LSR, LSR(this)), + Pair(Instruction.BCS, BCS(this)), + Pair(Instruction.INC, INC(this)), + Pair(Instruction.NOP, NOP(this)), + Pair(Instruction.SEC, SEC(this)), + Pair(Instruction.SBC, SBC(this)), + Pair(Instruction.BCC, BCC(this)), + Pair(Instruction.DEC, DEC(this)) // Pair(Instruction.BMI, BMI(this)), // Pair(Instruction.BVC, BVC(this)), // Pair(Instruction.BVS, BVS(this)), -// Pair(Instruction.BCC, BCC(this)), -// Pair(Instruction.BCS, BCS(this)), -// Pair(Instruction.BEQ, BEQ(this)), -// Pair(Instruction.CMP, CMP(this)), // Pair(Instruction.CPY, CPY(this)), -// Pair(Instruction.DEC, DEC(this)), // Pair(Instruction.EOR, EOR(this)), -// Pair(Instruction.SEC, SEC(this)), // Pair(Instruction.CLI, CLI(this)), // Pair(Instruction.CLV, CLV(this)), // Pair(Instruction.CLD, CLD(this)), // Pair(Instruction.SED, SED(this)), -// Pair(Instruction.INC, INC(this)), -// Pair(Instruction.LSR, LSR(this)), -// Pair(Instruction.NOP, NOP(this)), -// Pair(Instruction.TXA, TXA(this)), // Pair(Instruction.TAY, TAY(this)), // Pair(Instruction.TYA, TYA(this)), // Pair(Instruction.INY, INY(this)), // Pair(Instruction.ROR, ROR(this)), // Pair(Instruction.ROL, ROL(this)), // Pair(Instruction.RTI, RTI(this)), -// Pair(Instruction.SBC, SBC(this)), // Pair(Instruction.TXS, TXS(this)), // Pair(Instruction.TSX, TSX(this)), // Pair(Instruction.PHA, PHA(this)), @@ -84,19 +94,29 @@ class CPU(val memory: Memory) { // Pair(Instruction.STY, STY(this)) ) - fun execute() { + fun run() { isRunning = true - while (true) { - setRandomByte() - executeNextInstruction() + innerRun() + } - if (PC == 0 || !isRunning) { - break - } + private fun innerRun() { + (0..98).forEach { execute() } + handler.postDelayed({ innerRun() }, 15) + } + + private fun execute() { + if (!isRunning) { + return + } + + setRandomByte() + executeNextInstruction() + + if (PC == 0 || !isRunning) { + stop() + Log.i(TAG, "Program end at PC=$" + (PC - 1).toHexString() + ", A=$" + A.toHexString() + + ", X=$" + X.toHexString() + ", Y=$" + Y.toHexString()) } - stop() - Log.i(TAG, "Program end at PC=$" + (PC - 1).toHexString() + ", A=$" + A.toHexString() + - ", X=$" + X.toHexString() + ", Y=$" + Y.toHexString()) } private fun executeNextInstruction() { @@ -117,6 +137,7 @@ class CPU(val memory: Memory) { fun stop() { isRunning = false + handler.removeCallbacks(null) } fun popByte(): Int { @@ -139,7 +160,7 @@ class CPU(val memory: Memory) { setSVFlagsForValue(Y) } - private fun setSVFlagsForValue(value: Int) { + fun setSVFlagsForValue(value: Int) { if (value != 0) { P = P.and(0xfd) } else { @@ -200,19 +221,19 @@ class CPU(val memory: Memory) { setSZFlagsForRegA() } - private fun overflow(): Boolean { + fun overflow(): Boolean { return P.and(0x40) != 0 } - private fun decimalMode(): Boolean { + fun decimalMode(): Boolean { return P.and(8) != 0 } - private fun carry(): Boolean { + fun carry(): Boolean { return P.and(1) != 0 } - private fun negative(): Boolean { + fun negative(): Boolean { return P.and(0x80) != 0 } @@ -220,6 +241,10 @@ class CPU(val memory: Memory) { return P.and(0x02) != 0 } + fun setCarryFlagFromBit0(value: Int) { + P = P.and(0xfe).or(value.and(1)) + } + fun jumpBranch(offset: Int) { if (offset > 0x7f) { PC -= (0x100 - offset) @@ -243,16 +268,16 @@ class CPU(val memory: Memory) { } /** SEt Carry */ - private fun SEC() { + fun SEC() { P = P.or(1) } /** CLear oVerflow */ - private fun CLV() { + fun CLV() { P = P.and(0xbf) } - private fun setOverflow() { + fun setOverflow() { P = P.or(0x40) } diff --git a/app/src/main/kotlin/android/emu6502/Display.kt b/app/src/main/kotlin/android/emu6502/Display.kt index 3820d5f..f0a9db6 100644 --- a/app/src/main/kotlin/android/emu6502/Display.kt +++ b/app/src/main/kotlin/android/emu6502/Display.kt @@ -35,7 +35,7 @@ open class Display : View { val x = (addr - 0x200) % 32 val y = Math.floor(((addr - 0x200) / 32).toDouble()) drawingCache.add(Pixel(Point(x.toInt(), y.toInt()), Color.parseColor(color))) - invalidate() + postInvalidate() } override fun onDraw(canvas: Canvas) { diff --git a/app/src/main/kotlin/android/emu6502/Memory.kt b/app/src/main/kotlin/android/emu6502/Memory.kt index 1a92837..d5954f8 100644 --- a/app/src/main/kotlin/android/emu6502/Memory.kt +++ b/app/src/main/kotlin/android/emu6502/Memory.kt @@ -9,6 +9,10 @@ class Memory(private val display: Display) { return mem[addr] } + fun getWord(addr: Int): Int { + return get(addr) + get(addr + 1).shl(8) + } + fun set(addr: Int, value: Int) { mem[addr] = value } @@ -19,6 +23,10 @@ class Memory(private val display: Display) { display.updatePixel(addr, mem[addr].and(0x0f)) } } + // Store keycode in ZP $ff + fun storeKeypress(keyCode: Int) { + storeByte(0xff, keyCode) + } fun format(start: Int, length: Int): String { var i = 0 diff --git a/app/src/main/kotlin/android/emu6502/app/MainActivity.kt b/app/src/main/kotlin/android/emu6502/app/MainActivity.kt index 3aeb892..4901af2 100644 --- a/app/src/main/kotlin/android/emu6502/app/MainActivity.kt +++ b/app/src/main/kotlin/android/emu6502/app/MainActivity.kt @@ -5,14 +5,16 @@ import android.emu6502.Emulator import android.emu6502.R import android.os.Bundle import android.support.design.widget.FloatingActionButton +import android.support.design.widget.Snackbar import android.support.v7.app.ActionBar import android.support.v7.app.AppCompatActivity import android.support.v7.widget.Toolbar import android.view.Menu import android.view.MenuItem import android.view.View +import android.widget.Button +import android.widget.LinearLayout import android.widget.TextView -import android.widget.Toast import butterknife.bindView public class MainActivity : AppCompatActivity() { @@ -27,6 +29,13 @@ public class MainActivity : AppCompatActivity() { val display: Display by bindView(R.id.display) val txtInstructions: TextView by bindView(R.id.txtInstructions) val fabRun: FloatingActionButton by bindView(R.id.fabRun) + val layoutContent: LinearLayout by bindView(R.id.layout_content) + val btnLeft: Button by bindView(R.id.arrowLeft) + val btnRight: Button by bindView(R.id.arrowRight) + val btnUp: Button by bindView(R.id.arrowUp) + val btnDown: Button by bindView(R.id.arrowDown) + + private var emulator: Emulator? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -39,13 +48,25 @@ public class MainActivity : AppCompatActivity() { fabRun.setOnClickListener { display.setVisibility(View.VISIBLE) - val emulator = Emulator(display) - emulator.assembler.assembleCode(txtInstructions.getText().toString().splitBy("\n")) - Toast.makeText(fabRun.getContext(), - "Code assembled successfully, " + emulator.assembler.codeLen + " bytes.", - Toast.LENGTH_SHORT).show() - emulator.cpu.execute() + emulator = Emulator(display) + val emu: Emulator = emulator as Emulator + emu.assembler.assembleCode(txtInstructions.getText().toString().splitBy("\n")) + Snackbar.make(layoutContent, + "Code assembled successfully, ${emu.assembler.codeLen} bytes.", + Snackbar.LENGTH_SHORT).show() + emu.cpu.run() } + + val onClickButton = { code: Int -> + if (emulator != null) { + val emu = emulator as Emulator + emu.cpu.memory.storeKeypress(code) + } + } + btnLeft.setOnClickListener { onClickButton(0x61) } + btnRight.setOnClickListener { onClickButton(0x64) } + btnUp.setOnClickListener { onClickButton(0x77) } + btnDown.setOnClickListener { onClickButton(0x73) } } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/BCC.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/BCC.kt new file mode 100644 index 0000000..737a274 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/BCC.kt @@ -0,0 +1,16 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** Branch on Carry Clear */ +final class BCC(private val cpu: CPU) : BaseInstruction(Instruction.BCC, cpu.instructionList) { + override fun branch() { + val offset = cpu.popByte() + if (!cpu.carry()) { + cpu.jumpBranch(offset) + } + } +} + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/BCS.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/BCS.kt new file mode 100644 index 0000000..b2d385f --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/BCS.kt @@ -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 Carry Set */ +final class BCS(private val cpu: CPU) : BaseInstruction(Instruction.BCS, cpu.instructionList) { + override fun branch() { + val offset = cpu.popByte() + if (cpu.carry()) { + cpu.jumpBranch(offset) + } + } +} diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/BEQ.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/BEQ.kt new file mode 100644 index 0000000..f730173 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/BEQ.kt @@ -0,0 +1,16 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** Branch on EQual */ +class BEQ(private val cpu: CPU) : BaseInstruction(Instruction.BEQ, cpu.instructionList) { + override fun branch() { + val offset = cpu.popByte() + if (cpu.zero()) { + cpu.jumpBranch(offset) + } + } +} + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/BIT.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/BIT.kt index 3ab8e39..4a412cc 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/BIT.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/BIT.kt @@ -5,6 +5,27 @@ import android.emu6502.instructions.BaseInstruction import android.emu6502.instructions.Instruction /** test BITs */ -class BIT(cpu: CPU) - : BaseInstruction(Instruction.BIT, cpu.instructionList) { +class BIT(private val cpu: CPU) : BaseInstruction(Instruction.BIT, cpu.instructionList) { + override fun zeroPage() { + val value = cpu.memory.get(cpu.popByte()) + BIT(value) + } + + private fun BIT(value: Int) { + if (value.and(0x80) != 0) { + cpu.P = cpu.P.or(0x80) + } else { + cpu.P = cpu.P.and(0x7f) + } + if (value.and(0x40) != 0) { + cpu.P = cpu.P.or(0x40) + } else { + cpu.P = cpu.P.and(0x40.inv()) + } + if (cpu.A.and(value) != 0) { + cpu.P = cpu.P.and(0xfd) + } else { + cpu.P = cpu.P.or(0x02) + } + } } diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/BPL.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/BPL.kt new file mode 100644 index 0000000..e24341d --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/BPL.kt @@ -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 PLus */ +final class BPL(private val cpu: CPU) : BaseInstruction(Instruction.BPL, cpu.instructionList) { + override fun branch() { + val offset = cpu.popByte() + if (!cpu.negative()) { + cpu.jumpBranch(offset) + } + } +} diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/CMP.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/CMP.kt new file mode 100644 index 0000000..f864405 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/CMP.kt @@ -0,0 +1,19 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** CoMPare accumulator */ +final class CMP(private val cpu: CPU) : BaseInstruction(Instruction.CMP, cpu.instructionList) { + override fun immediate() { + cpu.doCompare(cpu.A, cpu.popByte()) + } + + override fun zeroPage() { + val value = cpu.memory.get(cpu.popByte()) + cpu.doCompare(cpu.A, value) + } +} + + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/CPX.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/CPX.kt index 745c120..b080287 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/CPX.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/CPX.kt @@ -10,4 +10,9 @@ class CPX(private val cpu: CPU) : BaseInstruction(Instruction.CPX, cpu.instructi val value = cpu.popByte() cpu.doCompare(cpu.X, value) } + + override fun zeroPage() { + val value = cpu.memory.get(cpu.popByte()) + cpu.doCompare(cpu.X, value) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/DEC.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/DEC.kt new file mode 100644 index 0000000..00fa011 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/DEC.kt @@ -0,0 +1,19 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** DECrement memory */ +class DEC(private val cpu: CPU) : BaseInstruction(Instruction.DEC, cpu.instructionList) { + override fun zeroPage() { + DEC(cpu.popByte()) + } + + fun DEC(addr: Int) { + var value = cpu.memory.get(addr) + value = (value - 1).and(0xff) + cpu.memory.storeByte(addr, value) + cpu.setSVFlagsForValue(value) + } +} diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/INC.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/INC.kt new file mode 100644 index 0000000..c1a9b0a --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/INC.kt @@ -0,0 +1,20 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** INCrement memory */ +final class INC(private val cpu: CPU) : BaseInstruction(Instruction.INC, cpu.instructionList) { + override fun zeroPage() { + inc(cpu.popByte()) + } + + private fun inc(addr: Int) { + var value = cpu.memory.get(addr) + value = (value + 1).and(0xff) + cpu.memory.storeByte(addr, value) + cpu.setSVFlagsForValue(value) + } +} + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/LDA.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/LDA.kt index cccd7c6..a2575ad 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/LDA.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/LDA.kt @@ -15,4 +15,9 @@ class LDA(private val cpu: CPU) : BaseInstruction(Instruction.LDA, cpu.instructi cpu.A = cpu.memory.get(cpu.popByte()) cpu.setSZFlagsForRegA() } + + override fun zeroPageX() { + cpu.A = cpu.memory.get(cpu.popByte() + cpu.X).and(0xff) + cpu.setSZFlagsForRegA() + } } diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/LDX.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/LDX.kt index 8675773..3bf8fcb 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/LDX.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/LDX.kt @@ -10,4 +10,9 @@ class LDX(private val cpu: CPU) : BaseInstruction(Instruction.LDX, cpu.instructi cpu.X = cpu.popByte() cpu.setSZflagsForRegX() } + + override fun zeroPage() { + cpu.X = cpu.memory.get(cpu.popByte()) + cpu.setSZflagsForRegX() + } } diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/LDY.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/LDY.kt index b60b1ed..0b73bb9 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/LDY.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/LDY.kt @@ -5,5 +5,9 @@ import android.emu6502.instructions.BaseInstruction import android.emu6502.instructions.Instruction /** LoaD Y register */ -class LDY(cpu: CPU) : BaseInstruction(Instruction.LDY, cpu.instructionList) { +class LDY(private val cpu: CPU) : BaseInstruction(Instruction.LDY, cpu.instructionList) { + override fun immediate() { + cpu.Y = cpu.popByte() + cpu.setSZflagsForRegY() + } } diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/LSR.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/LSR.kt new file mode 100644 index 0000000..7bbf9a6 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/LSR.kt @@ -0,0 +1,15 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** Logical Shift Right */ +final class LSR(private val cpu: CPU) : BaseInstruction(Instruction.LSR, cpu.instructionList) { + override fun single() { + cpu.setCarryFlagFromBit0(cpu.A) + cpu.A = cpu.A.shr(1) + cpu.setSZFlagsForRegA() + } +} + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/NOP.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/NOP.kt new file mode 100644 index 0000000..49e017a --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/NOP.kt @@ -0,0 +1,12 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** No OPeration */ +class NOP(private val cpu: CPU) : BaseInstruction(Instruction.NOP, cpu.instructionList) { + override fun single() { + } +} + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/SBC.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/SBC.kt new file mode 100644 index 0000000..f130c39 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/SBC.kt @@ -0,0 +1,62 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** SuBtract with Carry */ +class SBC(private val cpu: CPU) : BaseInstruction(Instruction.SBC, cpu.instructionList) { + override fun immediate() { + testSBC(cpu.popByte()) + } + + private fun testSBC(value: Int) { + if (cpu.A.xor(value).and(0x80) != 0) { + cpu.setOverflow() + } else { + cpu.CLV() + } + + var w: Int + if (cpu.decimalMode()) { + var tmp = 0xf + cpu.A.and(0xf) - value.and(0xf) + cpu.P.and(1) + if (tmp < 0x10) { + w = 0 + tmp -= 6 + } else { + w = 0x10 + tmp -= 0x10 + } + w += 0xf0 + cpu.A.and(0xf0) - value.and(0xf0) + if (w < 0x100) { + cpu.CLC() + if (cpu.overflow() && w < 0x80) { + cpu.CLV() + } + w -= 0x60 + } else { + cpu.SEC() + if (cpu.overflow() && w >= 0x180) { + cpu.CLV() + } + } + w += tmp + } else { + w = 0xff + cpu.A - value + cpu.P.and(1) + if (w < 0x100) { + cpu.CLC() + if (cpu.overflow() && w < 0x80) { + cpu.CLV() + } + } else { + cpu.SEC() + if (cpu.overflow() && w >= 0x180) { + cpu.CLV() + } + } + } + cpu.A = w.and(0xff) + cpu.setSZFlagsForRegA() + } +} + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/SEC.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/SEC.kt new file mode 100644 index 0000000..adddf14 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/SEC.kt @@ -0,0 +1,14 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** SEt Carry */ +class SEC(private val cpu: CPU) : BaseInstruction(Instruction.SEC, cpu.instructionList) { + override fun single() { + cpu.carry() + } +} + + diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/STA.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/STA.kt index 185651c..4b609b4 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/impl/STA.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/STA.kt @@ -16,5 +16,20 @@ class STA(private val memory: Memory, private val cpu: CPU) override fun zeroPage() { memory.storeByte(cpu.popByte(), cpu.A) } + + override fun zeroPageX() { + cpu.memory.storeByte((cpu.popByte() + cpu.X).and(0xff), cpu.A) + } + + override fun indirectY() { + val addr = memory.getWord(cpu.popByte()) + cpu.Y + memory.storeByte(addr, cpu.A) + } + + override fun indirectX() { + var zp = (cpu.popByte() + cpu.X).and(0xff) + var addr = memory.getWord(zp) + memory.storeByte(addr, cpu.A) + } } diff --git a/app/src/main/kotlin/android/emu6502/instructions/impl/TXA.kt b/app/src/main/kotlin/android/emu6502/instructions/impl/TXA.kt new file mode 100644 index 0000000..b2be3e7 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/impl/TXA.kt @@ -0,0 +1,15 @@ +package android.emu6502.instructions.impl + +import android.emu6502.CPU +import android.emu6502.instructions.BaseInstruction +import android.emu6502.instructions.Instruction + +/** Transfer X to A */ +class TXA(private val cpu: CPU) : BaseInstruction(Instruction.TXA, cpu.instructionList) { + override fun single() { + cpu.A = cpu.X.and(0xff) + cpu.setSZFlagsForRegA() + } +} + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ec1ddca..a0ebe42 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,9 +1,9 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="emu6502.app.MainActivity"> - + - + android:layout_height="300dp" + android:layout_margin="@dimen/card_margin"> - + android:layout_height="match_parent" + android:fontFamily="monospace" + android:gravity="top" + android:text="; Change direction: W A S D\n\ndefine appleL $00 ; screen location of apple, low byte\ndefine appleH $01 ; screen location of apple, high byte\ndefine snakeHeadL $10 ; screen location of snake head, low byte\ndefine snakeHeadH $11 ; screen location of snake head, high byte\ndefine snakeBodyStart $12 ; start of snake body byte pairs\ndefine snakeDirection $02 ; direction (possible values are below)\ndefine snakeLength $03 ; snake length, in bytes\n\n; Directions (each using a separate bit)\ndefine movingUp 1\ndefine movingRight 2\ndefine movingDown 4\ndefine movingLeft 8\n\n; ASCII values of keys controlling the snake\ndefine ASCII_w $77\ndefine ASCII_a $61\ndefine ASCII_s $73\ndefine ASCII_d $64\n\n; System variables\ndefine sysRandom $fe\ndefine sysLastKey $ff\n\n\n jsr init\n jsr loop\n\ninit:\n jsr initSnake\n jsr generateApplePosition\n rts\n\n\ninitSnake:\n lda #movingRight ;start direction\n sta snakeDirection\n\n lda #4 ;start length (2 segments)\n sta snakeLength\n \n lda #$11\n sta snakeHeadL\n \n lda #$10\n sta snakeBodyStart\n \n lda #$0f\n sta $14 ; body segment 1\n \n lda #$04\n sta snakeHeadH\n sta $13 ; body segment 1\n sta $15 ; body segment 2\n rts\n\n\ngenerateApplePosition:\n ;load a new random byte into $00\n lda sysRandom\n sta appleL\n\n ;load a new random number from 2 to 5 into $01\n lda sysRandom\n and #$03 ;mask out lowest 2 bits\n clc\n adc #2\n sta appleH\n\n rts\n\n\nloop:\n jsr readKeys\n jsr checkCollision\n jsr updateSnake\n jsr drawApple\n jsr drawSnake\n jsr spinWheels\n jmp loop\n\n\nreadKeys:\n lda sysLastKey\n cmp #ASCII_w\n beq upKey\n cmp #ASCII_d\n beq rightKey\n cmp #ASCII_s\n beq downKey\n cmp #ASCII_a\n beq leftKey\n rts\nupKey:\n lda #movingDown\n bit snakeDirection\n bne illegalMove\n\n lda #movingUp\n sta snakeDirection\n rts\nrightKey:\n lda #movingLeft\n bit snakeDirection\n bne illegalMove\n\n lda #movingRight\n sta snakeDirection\n rts\ndownKey:\n lda #movingUp\n bit snakeDirection\n bne illegalMove\n\n lda #movingDown\n sta snakeDirection\n rts\nleftKey:\n lda #movingRight\n bit snakeDirection\n bne illegalMove\n\n lda #movingLeft\n sta snakeDirection\n rts\nillegalMove:\n rts\n\n\ncheckCollision:\n jsr checkAppleCollision\n jsr checkSnakeCollision\n rts\n\n\ncheckAppleCollision:\n lda appleL\n cmp snakeHeadL\n bne doneCheckingAppleCollision\n lda appleH\n cmp snakeHeadH\n bne doneCheckingAppleCollision\n\n ;eat apple\n inc snakeLength\n inc snakeLength ;increase length\n jsr generateApplePosition\ndoneCheckingAppleCollision:\n rts\n\n\ncheckSnakeCollision:\n ldx #2 ;start with second segment\nsnakeCollisionLoop:\n lda snakeHeadL,x\n cmp snakeHeadL\n bne continueCollisionLoop\n\nmaybeCollided:\n lda snakeHeadH,x\n cmp snakeHeadH\n beq didCollide\n\ncontinueCollisionLoop:\n inx\n inx\n cpx snakeLength ;got to last section with no collision\n beq didntCollide\n jmp snakeCollisionLoop\n\ndidCollide:\n jmp gameOver\ndidntCollide:\n rts\n\n\nupdateSnake:\n ldx snakeLength\n dex\n txa\nupdateloop:\n lda snakeHeadL,x\n sta snakeBodyStart,x\n dex\n bpl updateloop\n\n lda snakeDirection\n lsr\n bcs up\n lsr\n bcs right\n lsr\n bcs down\n lsr\n bcs left\nup:\n lda snakeHeadL\n sec\n sbc #$20\n sta snakeHeadL\n bcc upup\n rts\nupup:\n dec snakeHeadH\n lda #$1\n cmp snakeHeadH\n beq collision\n rts\nright:\n inc snakeHeadL\n lda #$1f\n bit snakeHeadL\n beq collision\n rts\ndown:\n lda snakeHeadL\n clc\n adc #$20\n sta snakeHeadL\n bcs downdown\n rts\ndowndown:\n inc snakeHeadH\n lda #$6\n cmp snakeHeadH\n beq collision\n rts\nleft:\n dec snakeHeadL\n lda snakeHeadL\n and #$1f\n cmp #$1f\n beq collision\n rts\ncollision:\n jmp gameOver\n\n\ndrawApple:\n ldy #0\n lda sysRandom\n sta (appleL),y\n rts\n\n\ndrawSnake:\n ldx #0\n lda #1\n sta (snakeHeadL,x) ; paint head\n \n ldx snakeLength\n lda #0\n sta (snakeHeadL,x) ; erase end of tail\n rts\n\n\nspinWheels:\n ldx #0\nspinloop:\n nop\n nop\n dex\n bne spinloop\n rts\n\n\ngameOver:" + android:textSize="14sp"/> - - - - - - - + android:layout_height="match_parent" + android:visibility="gone"/> -