From 54fa43a8938a238930c95dcc9ed8181bf28ca4e9 Mon Sep 17 00:00:00 2001 From: Felipe Lima Date: Wed, 10 Jun 2015 00:02:55 -0700 Subject: [PATCH] Adds some instructions and assembler implementation --- .../main/kotlin/android/emu6502/Assembler.kt | 154 ++++++++++++++---- app/src/main/kotlin/android/emu6502/CPU.kt | 78 +++++++-- .../android/emu6502/instructions/ADC.kt | 9 + .../android/emu6502/instructions/AND.kt | 9 + .../android/emu6502/instructions/ASL.kt | 9 + .../android/emu6502/instructions/LDX.kt | 9 + .../android/emu6502/instructions/ORA.kt | 2 +- .../android/emu6502/instructions/STX.kt | 10 ++ 8 files changed, 236 insertions(+), 44 deletions(-) create mode 100644 app/src/main/kotlin/android/emu6502/instructions/ADC.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/AND.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/ASL.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/LDX.kt create mode 100644 app/src/main/kotlin/android/emu6502/instructions/STX.kt diff --git a/app/src/main/kotlin/android/emu6502/Assembler.kt b/app/src/main/kotlin/android/emu6502/Assembler.kt index 8fd573f..e7bd871 100644 --- a/app/src/main/kotlin/android/emu6502/Assembler.kt +++ b/app/src/main/kotlin/android/emu6502/Assembler.kt @@ -3,10 +3,11 @@ package android.emu6502 import android.emu6502.instructions.Instruction import android.emu6502.instructions.Opcodes import java.util.regex.Pattern +import kotlin.text.Regex class Assembler(private var labels: Labels, - private var memory: Memory, - private var symbols: Symbols) { + private var memory: Memory, + private var symbols: Symbols) { private var defaultCodePC = 0 private var codeLen = 0 @@ -131,60 +132,106 @@ class Assembler(private var labels: Labels, } private fun checkAbsolute(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + if (checkWordOperand("^([\\w\$]+)$")) { + return true + } + + // it could be a label too.. + return checkLabel(param, opcode, "^\\w+$".toRegex()) + } + + private fun checkWordOperand(param: String, opcode: Int, regex: String): Boolean { + val matcher = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(param) + if (!matcher.find()) { + return false + } + return innerCheckWordOperand(matcher.group(1), opcode) + } + + private fun innerCheckWordOperand(param: String, opcode: Int): Boolean { + var operand = tryParseWordOperand(param) + if (operand < 0) { + return false + } + pushByte(opcode) + pushWord(operand) + return true + } + + private fun checkByteOperand(param: String, opcode: Int, regex: String): Boolean { + val matcher = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(param) + if (!matcher.find()) { + return false + } + return innerCheckByteOperand(matcher.group(1), opcode) + } + + private fun innerCheckByteOperand(param: String, opcode: Int): Boolean { + var operand = tryParseByteOperand(param) + if (operand < 0) { + return false + } + pushByte(opcode) + pushByte(operand) + return true + } + + private fun checkLabel(param: String, opcode: Int, regex: Regex): Boolean { + if (param.matches(regex)) { + val finalParam = param.replace(",Y", "", true).replace(",X", "", true) + pushByte(opcode) + if (labels.find(finalParam)) { + val addr = (labels.getPC(finalParam)) + if (addr < 0 || addr > 0xffff) { + return false + } + pushWord(addr) + return true + } else { + pushWord(0xffff) // filler, only used while indexing labels + return true + } + } + return false } private fun checkIndirectY(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkByteOperand(param, opcode, "^\\(([\\w\$]+)\\),Y$") } private fun checkIndirectX(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkByteOperand(param, opcode, "^\\(([\\w\$]+)\\),X$") } private fun checkIndirect(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkWordOperand(param, opcode, "^\\(([\\w\$]+)\\)$") } private fun checkAbsoluteY(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkWordOperand(param, opcode, "^([\\w\$]+),Y$") || + checkLabel(param, opcode, "^\\w+,Y$".toRegex()) } private fun checkAbsoluteX(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkWordOperand(param, opcode, "^([\\w\$]+),X$") || + checkLabel(param, opcode, "^\\w+,X$".toRegex()) } private fun checkZeroPageY(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkByteOperand(param, opcode, "^([\\w\$]+),Y") } private fun checkZeroPageX(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return checkByteOperand(param, opcode, "^([\\w\$]+),X") } private fun checkZeroPage(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + return innerCheckByteOperand(param, opcode) } private fun checkImmediate(param: String, opcode: Int): Boolean { - val pattern = Pattern.compile("^#([\\w\$]+)$") - val matcher = pattern.matcher(param) - if (matcher.find()) { - var operand = tryParseByteOperand(matcher.group(1)) - if (operand >= 0) { - pushByte(opcode) - pushByte(operand) - return true - } + if (checkByteOperand(param, opcode, "^#([\\w\$]+)$")) { + return true } // Label lo/hi @@ -216,7 +263,7 @@ class Assembler(private var labels: Labels, // Returns the (positive) value if successful, otherwise -1 private fun tryParseByteOperand(param: String): Int { var value: Int = 0 - var parameter = param; + var parameter = param if (parameter.matches("^\\w+$".toRegex())) { var lookupVal = symbols.lookup(parameter) // Substitute symbol by actual value, then proceed @@ -246,14 +293,55 @@ class Assembler(private var labels: Labels, return -1 } + private fun tryParseWordOperand(param: String): Int { + var value: Int = 0 + var parameter = param + + if (parameter.matches("^\\w+$".toRegex())) { + var lookupVal = symbols.lookup(parameter) // Substitute symbol by actual value, then proceed + if (lookupVal != null) { + parameter = lookupVal + } + } + + // Is it a hexadecimal operand? + var pattern = Pattern.compile("^\$([0-9a-f]{3,4})$") + var matcher = pattern.matcher(parameter) + if (matcher.find()) { + value = Integer.parseInt(matcher.group(1), 16) + } else { + // Is it a decimal operand? + pattern = Pattern.compile("^([0-9]{1,5})$") + matcher = pattern.matcher(parameter) + if (matcher.find()) { + value = Integer.parseInt(matcher.group(1), 10) + } + } + + // Validate range + if (value >= 0 && value <= 0xffff) { + return value + } + return -1 + } + private fun pushByte(value: Int) { - memory.set(defaultCodePC, value.and(0xFF)) + memory.set(defaultCodePC, value.and(0xff)) defaultCodePC++ codeLen++ } + private fun pushWord(value: Int) { + pushByte(value.and(0xff)) + pushByte(value.shr(8).and(0xff)) + } + private fun checkSingle(param: String, opcode: Int): Boolean { - throw UnsupportedOperationException( - "not implemented") //To change body of created functions use File | Settings | File Templates. + // Accumulator instructions are counted as single-byte opcodes + if (!param.equals("") && !param.equals("A")) { + return false + } + pushByte(opcode) + return true } } diff --git a/app/src/main/kotlin/android/emu6502/CPU.kt b/app/src/main/kotlin/android/emu6502/CPU.kt index 8654597..b2de827 100644 --- a/app/src/main/kotlin/android/emu6502/CPU.kt +++ b/app/src/main/kotlin/android/emu6502/CPU.kt @@ -1,7 +1,7 @@ package android.emu6502 -import android.emu6502.instructions.BaseInstruction -import android.emu6502.instructions.ORA +import android.emu6502.instructions.* +import android.util.Log import java.util.HashMap import kotlin.reflect.KMemberFunction0 @@ -19,8 +19,66 @@ class CPU(private val memory: Memory) { private var isRunning = false private var debug = false private var monitoring = false - + private var TAG = "CPU" private val instructionList: HashMap> = HashMap() + private val operationList: HashMap = hashMapOf( + Pair(Instruction.ADC, ADC(instructionList)), + Pair(Instruction.AND, AND(instructionList)), + Pair(Instruction.ASL, ASL(instructionList)), + Pair(Instruction.BIT, BIT(instructionList)), + Pair(Instruction.BPL, BPL(instructionList)), + Pair(Instruction.BMI, BMI(instructionList)), + Pair(Instruction.BVC, BVC(instructionList)), + Pair(Instruction.BVS, BVS(instructionList)), + Pair(Instruction.BCC, BCC(instructionList)), + Pair(Instruction.BCS, BCS(instructionList)), + Pair(Instruction.BNE, BNE(instructionList)), + Pair(Instruction.BEQ, BEQ(instructionList)), + Pair(Instruction.BRK, BRK(instructionList)), + Pair(Instruction.CMP, CMP(instructionList)), + Pair(Instruction.CPX, CPX(instructionList)), + Pair(Instruction.CPY, CPY(instructionList)), + Pair(Instruction.DEC, DEC(instructionList)), + Pair(Instruction.EOR, EOR(instructionList)), + Pair(Instruction.CLC, CLC(instructionList)), + Pair(Instruction.SEC, SEC(instructionList)), + Pair(Instruction.CLI, CLI(instructionList)), + Pair(Instruction.SEI, SEI(instructionList)), + Pair(Instruction.CLV, CLV(instructionList)), + Pair(Instruction.CLD, CLD(instructionList)), + Pair(Instruction.SED, SED(instructionList)), + Pair(Instruction.INC, INC(instructionList)), + Pair(Instruction.JMP, JMP(instructionList)), + Pair(Instruction.JSR, JSR(instructionList)), + Pair(Instruction.LDA, LDA(instructionList)), + Pair(Instruction.LDX, LDX(instructionList)), + Pair(Instruction.LDY, LDY(instructionList)), + Pair(Instruction.LSR, LSR(instructionList)), + Pair(Instruction.NOP, NOP(instructionList)), + Pair(Instruction.ORA, ORA(instructionList)), + Pair(Instruction.TAX, TAX(instructionList)), + Pair(Instruction.TXA, TXA(instructionList)), + Pair(Instruction.DEX, DEX(instructionList)), + Pair(Instruction.INX, INX(instructionList)), + Pair(Instruction.TAY, TAY(instructionList)), + Pair(Instruction.TYA, TYA(instructionList)), + Pair(Instruction.DEY, DEY(instructionList)), + Pair(Instruction.INY, INY(instructionList)), + Pair(Instruction.ROR, ROR(instructionList)), + Pair(Instruction.ROL, ROL(instructionList)), + Pair(Instruction.RTI, RTI(instructionList)), + Pair(Instruction.RTS, RTS(instructionList)), + Pair(Instruction.SBC, SBC(instructionList)), + Pair(Instruction.STA, STA(instructionList)), + Pair(Instruction.TXS, TXS(instructionList)), + Pair(Instruction.TSX, TSX(instructionList)), + Pair(Instruction.PHA, PHA(instructionList)), + Pair(Instruction.PLA, PLA(instructionList)), + Pair(Instruction.PHP, PHP(instructionList)), + Pair(Instruction.PLP, PLP(instructionList)), + Pair(Instruction.STX, STX(instructionList)), + Pair(Instruction.STY, STY(instructionList)) + ) fun execute() { setRandomByte() @@ -28,8 +86,7 @@ class CPU(private val memory: Memory) { if (PC == 0 || !isRunning) { stop() -// message("Program end at PC=$" + addr2hex(regPC - 1)) -// ui.stop() + Log.i(TAG, "Program end at PC=$" + (PC - 1)) } } @@ -40,10 +97,11 @@ class CPU(private val memory: Memory) { private fun executeNextInstruction() { val instruction = Integer.valueOf(popByte().toInt().toString(), 16) val function = instructionList.get(instruction) - ORA(instructionList).function() -// else { -// instructions.ierr() -// } + if (function != null) { + ORA(instructionList).function() + } else { + Log.e(TAG, "Address $" + PC + " - unknown opcode") + } } private fun popByte(): Byte { @@ -53,4 +111,4 @@ class CPU(private val memory: Memory) { private fun setRandomByte() { memory.set(0xfe, Math.floor(Math.random() * 256).toInt()) } -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/ADC.kt b/app/src/main/kotlin/android/emu6502/instructions/ADC.kt new file mode 100644 index 0000000..18ef3d1 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/ADC.kt @@ -0,0 +1,9 @@ +package android.emu6502.instructions + +import java.util.* +import kotlin.reflect.KMemberFunction0 + +/** ADd with Carry */ +class ADC(instructionList: HashMap>) + : BaseInstruction(Instruction.ADC, instructionList) { +} \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/AND.kt b/app/src/main/kotlin/android/emu6502/instructions/AND.kt new file mode 100644 index 0000000..0cf8f59 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/AND.kt @@ -0,0 +1,9 @@ +package android.emu6502.instructions + +import java.util.* +import kotlin.reflect.KMemberFunction0 + +/** bitwise AND with accumulator */ +class AND(instructionList: HashMap>) + : BaseInstruction(Instruction.AND, instructionList) { +} \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/ASL.kt b/app/src/main/kotlin/android/emu6502/instructions/ASL.kt new file mode 100644 index 0000000..832ac25 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/ASL.kt @@ -0,0 +1,9 @@ +package android.emu6502.instructions + +import java.util.* +import kotlin.reflect.KMemberFunction0 + +/** Arithmetic Shift Left */ +class ASL(instructionList: HashMap>) + : BaseInstruction(Instruction.ASL, instructionList) { +} diff --git a/app/src/main/kotlin/android/emu6502/instructions/LDX.kt b/app/src/main/kotlin/android/emu6502/instructions/LDX.kt new file mode 100644 index 0000000..68e2657 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/LDX.kt @@ -0,0 +1,9 @@ +package android.emu6502.instructions + +import java.util.* +import kotlin.reflect.KMemberFunction0 + +/** LoaD X register */ +class LDX(instructionList: HashMap>) + : BaseInstruction(Instruction.LDX, instructionList) { +} diff --git a/app/src/main/kotlin/android/emu6502/instructions/ORA.kt b/app/src/main/kotlin/android/emu6502/instructions/ORA.kt index 9971058..8f18be5 100644 --- a/app/src/main/kotlin/android/emu6502/instructions/ORA.kt +++ b/app/src/main/kotlin/android/emu6502/instructions/ORA.kt @@ -5,4 +5,4 @@ import java.util.HashMap /** bitwise OR with Accumulator */ class ORA(instructionList: HashMap>) : BaseInstruction(Instruction.ORA, instructionList) { -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/android/emu6502/instructions/STX.kt b/app/src/main/kotlin/android/emu6502/instructions/STX.kt new file mode 100644 index 0000000..33e18e6 --- /dev/null +++ b/app/src/main/kotlin/android/emu6502/instructions/STX.kt @@ -0,0 +1,10 @@ +package android.emu6502.instructions + + +import java.util.* +import kotlin.reflect.KMemberFunction0 + +/** STore X register */ +class STX(instructionList: HashMap>) +: BaseInstruction(Instruction.STX, instructionList) { +}