Fixes display update routine
This commit is contained in:
parent
75b72abe81
commit
fef8112fb3
|
@ -9,14 +9,16 @@ import android.os.Handler
|
|||
import android.os.HandlerThread
|
||||
import android.util.Log
|
||||
import java.util.HashMap
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
class CPU(val memory: Memory) {
|
||||
private val handlerThread = HandlerThread("Screencast Thread")
|
||||
private val handler: Handler
|
||||
class CPU(val memory: Memory) : Display.Callbacks {
|
||||
private val bgHandlerThread = HandlerThread("Screencast Thread")
|
||||
private val bgHandler: Handler
|
||||
private var lock: CountDownLatch? = null
|
||||
|
||||
init {
|
||||
handlerThread.start()
|
||||
handler = Handler(handlerThread.getLooper())
|
||||
bgHandlerThread.start()
|
||||
bgHandler = Handler(bgHandlerThread.getLooper())
|
||||
}
|
||||
|
||||
// Accumulator
|
||||
|
@ -96,12 +98,12 @@ class CPU(val memory: Memory) {
|
|||
|
||||
fun run() {
|
||||
isRunning = true
|
||||
innerRun()
|
||||
bgHandler.post { innerRun() }
|
||||
}
|
||||
|
||||
private fun innerRun() {
|
||||
(0..97).forEach { execute() }
|
||||
handler.postDelayed({ innerRun() }, 10)
|
||||
bgHandler.postDelayed({ innerRun() }, 10)
|
||||
}
|
||||
|
||||
private fun execute() {
|
||||
|
@ -137,7 +139,20 @@ class CPU(val memory: Memory) {
|
|||
|
||||
fun stop() {
|
||||
isRunning = false
|
||||
handler.removeCallbacks(null)
|
||||
bgHandler.removeCallbacks(null)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
var i = 0
|
||||
while (i < 0x600) {
|
||||
memory.set(i++, 0x00)
|
||||
}
|
||||
A = 0
|
||||
Y = 0
|
||||
X = 0
|
||||
PC = 0x600
|
||||
SP = 0xff
|
||||
P = 0x30
|
||||
}
|
||||
|
||||
fun popByte(): Int {
|
||||
|
@ -324,4 +339,13 @@ class CPU(val memory: Memory) {
|
|||
}
|
||||
return flags.toString()
|
||||
}
|
||||
|
||||
override fun onUpdate() {
|
||||
lock = CountDownLatch(1)
|
||||
lock?.await()
|
||||
}
|
||||
|
||||
override fun onDraw() {
|
||||
lock?.countDown()
|
||||
}
|
||||
}
|
|
@ -8,21 +8,25 @@ import android.graphics.Point
|
|||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import java.util.ArrayList
|
||||
import java.util
|
||||
import java.util.*
|
||||
|
||||
open class Display : View {
|
||||
private val numX = 32
|
||||
private val numY = 32
|
||||
|
||||
private val matrix = Array(32, { IntArray(32) })
|
||||
|
||||
private val palette = arrayOf(
|
||||
"#000000", "#ffffff", "#880000", "#aaffee",
|
||||
"#cc44cc", "#00cc55", "#0000aa", "#eeee77",
|
||||
"#dd8855", "#664400", "#ff7777", "#333333",
|
||||
"#777777", "#aaff66", "#0088ff", "#bbbbbb")
|
||||
|
||||
private val drawingCache = ArrayList<Pixel>()
|
||||
private val paint: Paint
|
||||
private val bgPaint: Paint
|
||||
private val TAG = "Display"
|
||||
private var listener: Callbacks? = null
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||
paint = Paint()
|
||||
|
@ -30,29 +34,50 @@ open class Display : View {
|
|||
bgPaint.setColor(Color.BLACK);
|
||||
}
|
||||
|
||||
fun setOnDisplayCallback(callback: Callbacks) {
|
||||
listener = callback;
|
||||
}
|
||||
|
||||
open fun updatePixel(addr: Int, value: Int) {
|
||||
val offsetAddr = addr - 0x200
|
||||
val x = offsetAddr % 32
|
||||
val y = Math.floor((offsetAddr / 32).toDouble()).toInt()
|
||||
val color = palette[value]
|
||||
val x = (addr - 0x200) % 32
|
||||
val y = Math.floor(((addr - 0x200) / 32).toDouble())
|
||||
drawingCache.add(Pixel(Point(x.toInt(), y.toInt()), Color.parseColor(color)))
|
||||
matrix[x][y] = Color.parseColor(color)
|
||||
postInvalidate()
|
||||
listener?.onUpdate()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val pixelSize = getWidth() / numX
|
||||
|
||||
canvas.drawRect(0f, 0f, getWidth().toFloat(), getHeight().toFloat(), bgPaint)
|
||||
|
||||
for (pixel in drawingCache) {
|
||||
val right = (pixel.point.x * pixelSize).toFloat()
|
||||
val top = (pixel.point.y * pixelSize).toFloat()
|
||||
paint.setColor(pixel.color)
|
||||
canvas.drawRect(right, top, right + pixelSize, top + pixelSize, paint)
|
||||
matrix.forEachIndexed { i, _ ->
|
||||
matrix[i].forEachIndexed { j, _ ->
|
||||
val color = matrix[i][j]
|
||||
val right = (i * pixelSize).toFloat()
|
||||
val top = (j * pixelSize).toFloat()
|
||||
if (color != 0) {
|
||||
paint.setColor(color)
|
||||
canvas.drawRect(right, top, right + pixelSize, right + pixelSize, paint)
|
||||
} else {
|
||||
canvas.drawRect(right, top, right + pixelSize, right + pixelSize, bgPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawingCache.clear()
|
||||
listener?.onDraw()
|
||||
}
|
||||
|
||||
class Pixel(val point: Point, val color: Int) {
|
||||
interface Callbacks {
|
||||
fun onUpdate()
|
||||
fun onDraw()
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
matrix.forEachIndexed { i, _ ->
|
||||
matrix[i].forEachIndexed { j, _ ->
|
||||
matrix[i][j] = 0
|
||||
}
|
||||
}
|
||||
postInvalidate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,17 @@ package android.emu6502
|
|||
|
||||
import android.emu6502.instructions.Symbols
|
||||
|
||||
final class Emulator(display: Display) {
|
||||
final class Emulator(val display: Display) {
|
||||
val memory = Memory(display)
|
||||
val cpu = CPU(memory)
|
||||
val assembler = Assembler(memory, Symbols())
|
||||
|
||||
init {
|
||||
display.setOnDisplayCallback(cpu)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
display.reset()
|
||||
cpu.reset()
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package android.emu6502
|
||||
|
||||
import java.util.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
class Memory(private val display: Display) {
|
||||
private val mem = IntArray(65536)
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.emu6502.Display
|
|||
import android.emu6502.Emulator
|
||||
import android.emu6502.R
|
||||
import android.os.Bundle
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v7.app.ActionBar
|
||||
|
@ -13,7 +14,7 @@ import android.view.Menu
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import butterknife.bindView
|
||||
|
||||
|
@ -26,14 +27,16 @@ public class MainActivity : AppCompatActivity() {
|
|||
val txtSP: TextView by bindView(R.id.SP)
|
||||
val txtPC: TextView by bindView(R.id.PC)
|
||||
val txtFlags: TextView by bindView(R.id.PC)
|
||||
val displayWrapper: FrameLayout by bindView(R.id.display_wrapper)
|
||||
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 layoutContent: CoordinatorLayout 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)
|
||||
val btnReset: Button by bindView(R.id.btnReset)
|
||||
|
||||
private var emulator: Emulator? = null
|
||||
|
||||
|
@ -47,7 +50,7 @@ public class MainActivity : AppCompatActivity() {
|
|||
ab.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
fabRun.setOnClickListener {
|
||||
display.setVisibility(View.VISIBLE)
|
||||
displayWrapper.setVisibility(View.VISIBLE)
|
||||
emulator = Emulator(display)
|
||||
val emu: Emulator = emulator as Emulator
|
||||
emu.assembler.assembleCode(txtInstructions.getText().toString().splitBy("\n"))
|
||||
|
@ -57,6 +60,11 @@ public class MainActivity : AppCompatActivity() {
|
|||
emu.cpu.run()
|
||||
}
|
||||
|
||||
btnReset.setOnClickListener {
|
||||
val emu: Emulator = emulator as Emulator
|
||||
emu.reset()
|
||||
}
|
||||
|
||||
val onClickButton = { code: Int ->
|
||||
if (emulator != null) {
|
||||
val emu = emulator as Emulator
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<android.support.design.widget.CoordinatorLayout
|
||||
android:id="@+id/layout_content"
|
||||
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"
|
||||
|
@ -22,7 +23,6 @@
|
|||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
@ -41,11 +41,18 @@
|
|||
android:text="define 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="10sp"/>
|
||||
|
||||
<android.emu6502.Display
|
||||
android:id="@+id/display"
|
||||
<FrameLayout
|
||||
android:id="@+id/display_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
android:background="@android:color/black"
|
||||
android:visibility="gone">
|
||||
|
||||
<android.emu6502.Display
|
||||
android:id="@+id/display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</FrameLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ public class CPUTest {
|
|||
"LDA #$08",
|
||||
"STA $0202");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x08));
|
||||
assertThat(cpu.getX(), equalTo(0x00));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
@ -51,7 +51,7 @@ public class CPUTest {
|
|||
"ADC #$c4 ;Add the hex value $c4 to the A register",
|
||||
"BRK ;Break - we're done");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x84));
|
||||
assertThat(cpu.getX(), equalTo(0xC1));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
@ -72,7 +72,7 @@ public class CPUTest {
|
|||
"STX $0201",
|
||||
"BRK");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x00));
|
||||
assertThat(cpu.getX(), equalTo(0x03));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
@ -92,7 +92,7 @@ public class CPUTest {
|
|||
"there:",
|
||||
"STA $0200");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x03));
|
||||
assertThat(cpu.getX(), equalTo(0x00));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
@ -120,7 +120,7 @@ public class CPUTest {
|
|||
"end:",
|
||||
"BRK");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x00));
|
||||
assertThat(cpu.getX(), equalTo(0x05));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
@ -134,7 +134,7 @@ public class CPUTest {
|
|||
"define a_dozen $0c ; a constant",
|
||||
"LDX #a_dozen ; equivalent to \"LDX #$0c\"");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x00));
|
||||
assertThat(cpu.getX(), equalTo(0x0C));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
@ -372,7 +372,7 @@ public class CPUTest {
|
|||
" rts",
|
||||
"gameOver:", "\n");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.execute();
|
||||
cpu.run();
|
||||
assertThat(cpu.getA(), equalTo(0x1f));
|
||||
assertThat(cpu.getX(), equalTo(0xff));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
|
|
Loading…
Reference in New Issue