mirror of
https://github.com/felipecsl/6502Android.git
synced 2024-06-15 20:29:39 +00:00
Snake game partially functional with moving buttons
This commit is contained in:
parent
6816980fda
commit
657e1b2319
|
@ -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,20 +94,30 @@ class CPU(val memory: Memory) {
|
|||
// Pair(Instruction.STY, STY(this))
|
||||
)
|
||||
|
||||
fun execute() {
|
||||
fun run() {
|
||||
isRunning = true
|
||||
while (true) {
|
||||
innerRun()
|
||||
}
|
||||
|
||||
private fun innerRun() {
|
||||
(0..98).forEach { execute() }
|
||||
handler.postDelayed({ innerRun() }, 15)
|
||||
}
|
||||
|
||||
private fun execute() {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
|
||||
setRandomByte()
|
||||
executeNextInstruction()
|
||||
|
||||
if (PC == 0 || !isRunning) {
|
||||
break
|
||||
}
|
||||
}
|
||||
stop()
|
||||
Log.i(TAG, "Program end at PC=$" + (PC - 1).toHexString() + ", A=$" + A.toHexString() +
|
||||
", X=$" + X.toHexString() + ", Y=$" + Y.toHexString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeNextInstruction() {
|
||||
val instruction = popByte()
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
16
app/src/main/kotlin/android/emu6502/instructions/impl/BCC.kt
Normal file
16
app/src/main/kotlin/android/emu6502/instructions/impl/BCC.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
app/src/main/kotlin/android/emu6502/instructions/impl/BCS.kt
Normal file
15
app/src/main/kotlin/android/emu6502/instructions/impl/BCS.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
16
app/src/main/kotlin/android/emu6502/instructions/impl/BEQ.kt
Normal file
16
app/src/main/kotlin/android/emu6502/instructions/impl/BEQ.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
app/src/main/kotlin/android/emu6502/instructions/impl/BPL.kt
Normal file
15
app/src/main/kotlin/android/emu6502/instructions/impl/BPL.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
19
app/src/main/kotlin/android/emu6502/instructions/impl/CMP.kt
Normal file
19
app/src/main/kotlin/android/emu6502/instructions/impl/CMP.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
19
app/src/main/kotlin/android/emu6502/instructions/impl/DEC.kt
Normal file
19
app/src/main/kotlin/android/emu6502/instructions/impl/DEC.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
20
app/src/main/kotlin/android/emu6502/instructions/impl/INC.kt
Normal file
20
app/src/main/kotlin/android/emu6502/instructions/impl/INC.kt
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
15
app/src/main/kotlin/android/emu6502/instructions/impl/LSR.kt
Normal file
15
app/src/main/kotlin/android/emu6502/instructions/impl/LSR.kt
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
12
app/src/main/kotlin/android/emu6502/instructions/impl/NOP.kt
Normal file
12
app/src/main/kotlin/android/emu6502/instructions/impl/NOP.kt
Normal file
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
62
app/src/main/kotlin/android/emu6502/instructions/impl/SBC.kt
Normal file
62
app/src/main/kotlin/android/emu6502/instructions/impl/SBC.kt
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
14
app/src/main/kotlin/android/emu6502/instructions/impl/SEC.kt
Normal file
14
app/src/main/kotlin/android/emu6502/instructions/impl/SEC.kt
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
15
app/src/main/kotlin/android/emu6502/instructions/impl/TXA.kt
Normal file
15
app/src/main/kotlin/android/emu6502/instructions/impl/TXA.kt
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context="emu6502.app.MainActivity">
|
||||
|
||||
<LinearLayout
|
||||
|
@ -21,28 +21,24 @@
|
|||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:layout_height="300dp"
|
||||
android:layout_margin="@dimen/card_margin">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/txtInstructions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="LDA #$01\nSTA $0200\nLDA #$05\nSTA $0201\nLDA #$08\nSTA $0202"
|
||||
android:fontFamily="monospace"
|
||||
android:textSize="14sp"
|
||||
android:gravity="top" />
|
||||
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.emu6502.Display
|
||||
android:id="@+id/display"
|
||||
|
@ -53,7 +49,6 @@
|
|||
</android.support.v7.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
@ -65,21 +60,16 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="Reset"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="20dp">
|
||||
android:layout_marginBottom="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/A"
|
||||
|
@ -109,8 +99,8 @@
|
|||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="20dp">
|
||||
android:layout_marginBottom="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/SP"
|
||||
|
@ -133,8 +123,7 @@
|
|||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="20dp">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/flags"
|
||||
|
@ -148,6 +137,43 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_gravity="center_horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/arrowLeft"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="left|center_vertical"
|
||||
android:text="L"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/arrowUp"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="top|center_horizontal"
|
||||
android:text="U"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/arrowRight"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="right|center_vertical"
|
||||
android:text="R"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/arrowDown"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:text="D"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
|
@ -157,6 +183,6 @@
|
|||
android:layout_gravity="end|bottom"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
android:src="@drawable/ic_play_arrow_white_48dp"
|
||||
app:elevation="3dp" />
|
||||
app:borderWidth="0dp"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
|
|
@ -373,5 +373,11 @@ public class CPUTest {
|
|||
"gameOver:", "\n");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
assertThat(cpu.getA(), equalTo(0x1f));
|
||||
assertThat(cpu.getX(), equalTo(0xff));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xfb));
|
||||
assertThat(cpu.getPC(), equalTo(0x0736));
|
||||
assertThat(cpu.flags(), equalTo("00110011"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user