2015-06-09 17:21:49 +00:00
|
|
|
package android.emu6502
|
|
|
|
|
2015-06-12 06:26:47 +00:00
|
|
|
import android.emu6502.instructions.BaseInstruction
|
|
|
|
import android.emu6502.instructions.Instruction
|
|
|
|
import android.emu6502.instructions.InstructionTarget
|
2015-06-28 15:24:34 +00:00
|
|
|
import android.emu6502.instructions.Opcodes
|
2015-06-12 06:26:47 +00:00
|
|
|
import android.emu6502.instructions.impl.*
|
2015-06-29 06:26:56 +00:00
|
|
|
import android.os.Handler
|
|
|
|
import android.os.HandlerThread
|
2015-06-10 07:02:55 +00:00
|
|
|
import android.util.Log
|
2015-06-09 17:21:49 +00:00
|
|
|
import java.util.HashMap
|
|
|
|
|
2015-06-17 06:20:23 +00:00
|
|
|
class CPU(val memory: Memory) {
|
2015-06-29 06:26:56 +00:00
|
|
|
private val handlerThread = HandlerThread("Screencast Thread")
|
|
|
|
private val handler: Handler
|
|
|
|
|
|
|
|
init {
|
|
|
|
handlerThread.start()
|
|
|
|
handler = Handler(handlerThread.getLooper())
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:21:49 +00:00
|
|
|
// Accumulator
|
2015-06-12 06:26:47 +00:00
|
|
|
var A: Int = 0
|
2015-06-09 17:21:49 +00:00
|
|
|
// Registers
|
2015-06-12 06:26:47 +00:00
|
|
|
var X: Int = 0
|
|
|
|
var Y: Int = 0
|
2015-06-09 17:21:49 +00:00
|
|
|
// Program counter
|
2015-06-12 06:26:47 +00:00
|
|
|
var PC: Int = 0x600
|
2015-06-09 17:21:49 +00:00
|
|
|
// Stack pointer
|
2015-06-12 06:26:47 +00:00
|
|
|
var SP: Int = 0xFF
|
|
|
|
// Processor flags
|
2015-06-17 06:20:23 +00:00
|
|
|
var P: Int = 0x30
|
2015-06-09 17:21:49 +00:00
|
|
|
private var isRunning = false
|
|
|
|
private var debug = false
|
|
|
|
private var monitoring = false
|
2015-06-10 07:02:55 +00:00
|
|
|
private var TAG = "CPU"
|
2015-06-12 06:26:47 +00:00
|
|
|
val instructionList: HashMap<Int, InstructionTarget> = HashMap()
|
2015-06-10 07:02:55 +00:00
|
|
|
private val operationList: HashMap<Instruction, BaseInstruction> = hashMapOf(
|
2015-06-12 06:26:47 +00:00
|
|
|
Pair(Instruction.ADC, ADC(this)),
|
|
|
|
Pair(Instruction.AND, AND(this)),
|
|
|
|
Pair(Instruction.ASL, ASL(this)),
|
|
|
|
Pair(Instruction.BIT, BIT(this)),
|
|
|
|
Pair(Instruction.LDA, LDA(this)),
|
|
|
|
Pair(Instruction.LDX, LDX(this)),
|
|
|
|
Pair(Instruction.LDY, LDY(this)),
|
|
|
|
Pair(Instruction.STA, STA(memory, this)),
|
|
|
|
Pair(Instruction.STX, STX(this)),
|
2015-06-15 06:37:41 +00:00
|
|
|
Pair(Instruction.TAX, TAX(this)),
|
|
|
|
Pair(Instruction.INX, INX(this)),
|
2015-06-17 06:20:23 +00:00
|
|
|
Pair(Instruction.DEX, DEX(this)),
|
|
|
|
Pair(Instruction.ORA, ORA(this)),
|
|
|
|
Pair(Instruction.CPX, CPX(this)),
|
|
|
|
Pair(Instruction.BRK, BRK(this)),
|
2015-06-22 01:06:44 +00:00
|
|
|
Pair(Instruction.BNE, BNE(this)),
|
|
|
|
Pair(Instruction.JMP, JMP(this)),
|
|
|
|
Pair(Instruction.JSR, JSR(this)),
|
2015-06-28 00:47:02 +00:00
|
|
|
Pair(Instruction.RTS, RTS(this)),
|
|
|
|
Pair(Instruction.SEI, SEI(this)),
|
2015-06-28 15:24:34 +00:00
|
|
|
Pair(Instruction.DEY, DEY(this)),
|
2015-06-29 06:26:56 +00:00
|
|
|
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))
|
2015-06-12 06:26:47 +00:00
|
|
|
// Pair(Instruction.BMI, BMI(this)),
|
|
|
|
// Pair(Instruction.BVC, BVC(this)),
|
|
|
|
// Pair(Instruction.BVS, BVS(this)),
|
|
|
|
// Pair(Instruction.CPY, CPY(this)),
|
|
|
|
// Pair(Instruction.EOR, EOR(this)),
|
|
|
|
// Pair(Instruction.CLI, CLI(this)),
|
|
|
|
// Pair(Instruction.CLV, CLV(this)),
|
|
|
|
// Pair(Instruction.CLD, CLD(this)),
|
|
|
|
// Pair(Instruction.SED, SED(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.TXS, TXS(this)),
|
|
|
|
// Pair(Instruction.TSX, TSX(this)),
|
|
|
|
// Pair(Instruction.PHA, PHA(this)),
|
|
|
|
// Pair(Instruction.PLA, PLA(this)),
|
|
|
|
// Pair(Instruction.PHP, PHP(this)),
|
|
|
|
// Pair(Instruction.PLP, PLP(this)),
|
|
|
|
// Pair(Instruction.STY, STY(this))
|
2015-06-10 07:02:55 +00:00
|
|
|
)
|
2015-06-09 17:21:49 +00:00
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun run() {
|
2015-06-13 10:02:44 +00:00
|
|
|
isRunning = true
|
2015-06-29 06:26:56 +00:00
|
|
|
innerRun()
|
|
|
|
}
|
2015-06-09 17:21:49 +00:00
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
private fun innerRun() {
|
2015-06-29 17:26:34 +00:00
|
|
|
(0..97).forEach { execute() }
|
|
|
|
handler.postDelayed({ innerRun() }, 10)
|
2015-06-29 06:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
2015-06-09 17:21:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun executeNextInstruction() {
|
2015-06-13 10:02:44 +00:00
|
|
|
val instruction = popByte()
|
2015-06-12 06:26:47 +00:00
|
|
|
val target = instructionList.get(instruction)
|
|
|
|
if (target != null) {
|
|
|
|
val function = target.method
|
|
|
|
target.operation.function()
|
2015-06-10 07:02:55 +00:00
|
|
|
} else {
|
2015-06-28 15:24:34 +00:00
|
|
|
val candidate = Opcodes.MAP.entrySet()
|
|
|
|
.first { it.value.any { opcode -> opcode == instruction } }
|
|
|
|
|
2015-06-28 00:47:02 +00:00
|
|
|
throw Exception(
|
2015-06-28 15:24:34 +00:00
|
|
|
"Address $${PC.toHexString()} - unknown opcode 0x${instruction.toHexString()} " +
|
|
|
|
"(instruction ${candidate.getKey().name()})")
|
2015-06-10 07:02:55 +00:00
|
|
|
}
|
2015-06-09 17:21:49 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 06:37:41 +00:00
|
|
|
fun stop() {
|
|
|
|
isRunning = false
|
2015-06-29 06:26:56 +00:00
|
|
|
handler.removeCallbacks(null)
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 06:26:47 +00:00
|
|
|
fun popByte(): Int {
|
2015-06-15 06:37:41 +00:00
|
|
|
return memory.get(PC++).and(0xff)
|
2015-06-09 17:21:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setRandomByte() {
|
|
|
|
memory.set(0xfe, Math.floor(Math.random() * 256).toInt())
|
|
|
|
}
|
2015-06-12 06:26:47 +00:00
|
|
|
|
|
|
|
fun setSZFlagsForRegA() {
|
2015-06-17 06:20:23 +00:00
|
|
|
setSVFlagsForValue(A)
|
2015-06-12 06:26:47 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 06:37:41 +00:00
|
|
|
fun setSZflagsForRegX() {
|
2015-06-17 06:20:23 +00:00
|
|
|
setSVFlagsForValue(X)
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-28 00:47:02 +00:00
|
|
|
fun setSZflagsForRegY() {
|
|
|
|
setSVFlagsForValue(Y)
|
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun setSVFlagsForValue(value: Int) {
|
2015-06-12 06:26:47 +00:00
|
|
|
if (value != 0) {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.and(0xfd)
|
2015-06-12 06:26:47 +00:00
|
|
|
} else {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.or(0x02)
|
2015-06-12 06:26:47 +00:00
|
|
|
}
|
|
|
|
if (value.and(0x80) != 0) {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.or(0x80)
|
2015-06-12 06:26:47 +00:00
|
|
|
} else {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.and(0x7f)
|
2015-06-12 06:26:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun popWord(): Int {
|
|
|
|
return popByte() + popByte().shl(8)
|
|
|
|
}
|
2015-06-15 06:37:41 +00:00
|
|
|
|
|
|
|
fun testADC(value: Int) {
|
|
|
|
var tmp: Int
|
|
|
|
if (A.xor(value).and(0x80) != 0) {
|
|
|
|
CLV()
|
|
|
|
} else {
|
|
|
|
setOverflow()
|
|
|
|
}
|
|
|
|
|
2015-06-17 06:20:23 +00:00
|
|
|
if (decimalMode()) {
|
|
|
|
tmp = A.and(0xf) + value.and(0xf) + P.and(1)
|
2015-06-15 06:37:41 +00:00
|
|
|
if (tmp >= 10) {
|
|
|
|
tmp = 0x10.or((tmp + 6).and(0xf))
|
|
|
|
}
|
|
|
|
tmp += A.and(0xf0) + value.and(0xf0)
|
|
|
|
if (tmp >= 160) {
|
|
|
|
SEC()
|
2015-06-17 06:20:23 +00:00
|
|
|
if (overflow() && tmp >= 0x180) {
|
2015-06-15 06:37:41 +00:00
|
|
|
CLV()
|
|
|
|
}
|
|
|
|
tmp += 0x60
|
|
|
|
} else {
|
|
|
|
CLC()
|
2015-06-17 06:20:23 +00:00
|
|
|
if (overflow() && tmp < 0x80) {
|
2015-06-15 06:37:41 +00:00
|
|
|
CLV()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2015-06-17 06:20:23 +00:00
|
|
|
tmp = A + value + P.and(1)
|
2015-06-15 06:37:41 +00:00
|
|
|
if (tmp >= 0x100) {
|
|
|
|
SEC()
|
2015-06-17 06:20:23 +00:00
|
|
|
if (overflow() && tmp >= 0x180) {
|
2015-06-15 06:37:41 +00:00
|
|
|
CLV()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CLC()
|
2015-06-17 06:20:23 +00:00
|
|
|
if (overflow() && tmp < 0x80) {
|
2015-06-15 06:37:41 +00:00
|
|
|
CLV()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
A = tmp.and(0xff)
|
|
|
|
setSZFlagsForRegA()
|
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun overflow(): Boolean {
|
2015-06-17 06:20:23 +00:00
|
|
|
return P.and(0x40) != 0
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun decimalMode(): Boolean {
|
2015-06-17 06:20:23 +00:00
|
|
|
return P.and(8) != 0
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun carry(): Boolean {
|
2015-06-17 06:20:23 +00:00
|
|
|
return P.and(1) != 0
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun negative(): Boolean {
|
2015-06-17 06:20:23 +00:00
|
|
|
return P.and(0x80) != 0
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-17 06:20:23 +00:00
|
|
|
fun zero(): Boolean {
|
|
|
|
return P.and(0x02) != 0
|
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun setCarryFlagFromBit0(value: Int) {
|
|
|
|
P = P.and(0xfe).or(value.and(1))
|
|
|
|
}
|
|
|
|
|
2015-06-17 06:20:23 +00:00
|
|
|
fun jumpBranch(offset: Int) {
|
|
|
|
if (offset > 0x7f) {
|
|
|
|
PC -= (0x100 - offset)
|
|
|
|
} else {
|
|
|
|
PC += offset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun doCompare(reg: Int, value: Int) {
|
|
|
|
if (reg >= value) {
|
|
|
|
SEC()
|
|
|
|
} else {
|
|
|
|
CLC()
|
|
|
|
}
|
|
|
|
setSVFlagsForValue(reg - value)
|
2015-06-15 06:37:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** CLear Carry */
|
2015-06-28 15:24:34 +00:00
|
|
|
fun CLC() {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.and(0xfe)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** SEt Carry */
|
2015-06-29 06:26:56 +00:00
|
|
|
fun SEC() {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.or(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** CLear oVerflow */
|
2015-06-29 06:26:56 +00:00
|
|
|
fun CLV() {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.and(0xbf)
|
|
|
|
}
|
|
|
|
|
2015-06-29 06:26:56 +00:00
|
|
|
fun setOverflow() {
|
2015-06-15 06:37:41 +00:00
|
|
|
P = P.or(0x40)
|
|
|
|
}
|
2015-06-17 06:20:23 +00:00
|
|
|
|
2015-06-22 01:06:44 +00:00
|
|
|
fun stackPush(value: Int) {
|
|
|
|
memory.set(SP.and(0xff) + 0x100, value.and(0xff))
|
|
|
|
SP--
|
|
|
|
if (SP < 0) {
|
|
|
|
SP = SP.and(0xff)
|
|
|
|
Log.i(TAG, "6502 Stack filled! Wrapping...")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun stackPop(): Int {
|
|
|
|
SP++;
|
|
|
|
if (SP >= 0x100) {
|
|
|
|
SP = SP.and(0xff)
|
|
|
|
Log.i(TAG, "6502 Stack emptied! Wrapping...")
|
|
|
|
}
|
|
|
|
return memory.get(SP + 0x100)
|
|
|
|
}
|
|
|
|
|
2015-06-17 06:20:23 +00:00
|
|
|
/**
|
|
|
|
* http://nesdev.com/6502.txt
|
|
|
|
* Returns the processor flags in the format SV-BDIZC
|
|
|
|
* Sign - this is set if the result of an operation is negative, cleared if positive.
|
|
|
|
* Overflow - when an arithmetic operation produces a result too large to be represented in a byte
|
|
|
|
* Unused - Supposed to be logical 1 at all times.
|
|
|
|
* Break - this is set when a software interrupt (BRK instruction) is executed.
|
|
|
|
* Decimal Mode - When set, and an Add with Carry or Subtract with Carry instruction is executed,
|
|
|
|
* the source values are treated as valid BCD (Binary Coded Decimal, eg. 0x00-0x99 = 0-99) numbers.
|
|
|
|
* The result generated is also a BCD number.
|
|
|
|
* Interrupt - If it is set, interrupts are disabled. If it is cleared, interrupts are enabled.
|
|
|
|
* Zero - this is set to 1 when any arithmetic or logical operation produces a zero result, and is
|
|
|
|
* set to 0 if the result is non-zero.
|
|
|
|
* Carry - this holds the carry out of the most significant bit in any arithmetic operation.
|
|
|
|
* In subtraction operations however, this flag is cleared - set to 0 - if a borrow is required,
|
|
|
|
* set to 1 - if no borrow is required. The carry flag is also used in shift and rotate logical
|
|
|
|
* operations.
|
|
|
|
* */
|
|
|
|
fun flags(): String {
|
|
|
|
val flags = StringBuilder()
|
|
|
|
for (i in 7 downTo 0) {
|
|
|
|
flags.append(P.shr(i).and(1))
|
|
|
|
}
|
|
|
|
return flags.toString()
|
|
|
|
}
|
2015-06-10 07:02:55 +00:00
|
|
|
}
|