Snake game partially functional with moving buttons

This commit is contained in:
Felipe Lima 2015-06-28 23:26:56 -07:00
parent 6816980fda
commit 657e1b2319
24 changed files with 526 additions and 147 deletions

View File

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

View File

@ -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) {

View File

@ -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

View File

@ -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 {

View 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)
}
}
}

View 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)
}
}
}

View 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)
}
}
}

View File

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

View 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)
}
}
}

View 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)
}
}

View File

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

View 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)
}
}

View 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)
}
}

View File

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

View File

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

View File

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

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

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
/** No OPeration */
class NOP(private val cpu: CPU) : BaseInstruction(Instruction.NOP, cpu.instructionList) {
override fun single() {
}
}

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

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

View File

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

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

View File

@ -1,9 +1,9 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
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">
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">
<LinearLayout
android:layout_width="match_parent"
@ -16,136 +16,162 @@
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<include layout="@layout/toolbar" />
<include layout="@layout/toolbar"/>
</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.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="300dp"
android:layout_margin="@dimen/card_margin">
<android.support.v7.widget.CardView
<EditText
android:id="@+id/txtInstructions"
android:layout_width="match_parent"
android:layout_height="400dp"
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"/>
<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.emu6502.Display
android:id="@+id/display"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</android.support.v7.widget.CardView>
<LinearLayout
style="?android:attr/buttonBarStyle"
<android.emu6502.Display
android:id="@+id/display"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_height="match_parent"
android:visibility="gone"/>
<Button
android:id="@+id/btnReset"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
android:orientation="horizontal">
<Button
android:id="@+id/btnReset"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="20dp">
android:orientation="vertical">
<TextView
android:id="@+id/A"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="A=$00" />
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/X"
android:layout_width="wrap_content"
<TextView
android:id="@+id/A"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="A=$00"/>
<TextView
android:id="@+id/X"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="X=$00"/>
<TextView
android:id="@+id/Y"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Y=$00"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="X=$00" />
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/Y"
android:layout_width="wrap_content"
<TextView
android:id="@+id/SP"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="SP=$ff"/>
<TextView
android:id="@+id/PC"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="PC=$0600"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="Y=$00" />
android:orientation="horizontal">
<TextView
android:id="@+id/flags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="00110000"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="20dp">
<TextView
android:id="@+id/SP"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="SP=$ff" />
<TextView
android:id="@+id/PC"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:fontFamily="monospace"
android:text="PC=$0600"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="20dp">
<TextView
android:id="@+id/flags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="00110000" />
</LinearLayout>
</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>
@ -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>

View File

@ -371,7 +371,13 @@ public class CPUTest {
" bne spinloop",
" rts",
"gameOver:", "\n");
assembler.assembleCode(lines);
cpu.execute();
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"));
}
}