6502Android/app/src/main/kotlin/android/emu6502/Assembler.kt

260 lines
7.6 KiB
Kotlin

package android.emu6502
import android.emu6502.instructions.Instruction
import android.emu6502.instructions.Opcodes
import java.util.regex.Pattern
class Assembler(private var labels: Labels,
private var memory: Memory,
private var symbols: Symbols) {
private var defaultCodePC = 0
private var codeLen = 0
private var codeAssembledOK = false
private var BOOTSTRAP_ADDRESS = 0x600
fun assembleCode(lines: Array<String>): Boolean {
lines.forEach { line ->
if (!assembleLine(line)) {
return false
}
}
return true
}
private fun assembleLine(line: String): Boolean {
var input = line
var command: String
var param: String
var addr: Int
// Find command or label
if (input.matches("^\\w+:".toRegex())) {
if (line.matches("^\\w+:[\\s]*\\w+.*$".toRegex())) {
input = input.replace("^\\w+:[\\s]*(.*)$".toRegex(), "$1")
command = input.replace("^(\\w+).*$".toRegex(), "$1")
} else {
command = ""
}
} else {
command = input.replace("^(\\w+).*$".toRegex(), "$1")
}
// Nothing to do for blank lines
if (command.equals("")) {
return true
}
command = command.toUpperCase()
if (input.matches("^\\*\\s*=\\s*\$?[0-9a-f]*$".toRegex())) {
// equ spotted
param = input.replace("^\\s*\\*\\s*=\\s*".toRegex(), "")
if (param[0].equals("$")) {
param = param.replace("^\$".toRegex(), "")
addr = Integer.parseInt(param, 16)
} else {
addr = Integer.parseInt(param, 10)
}
if ((addr < 0) || (addr > 0xffff)) {
throw IllegalStateException("Unable to relocate code outside 64k memory")
}
defaultCodePC = addr
return true
}
if (input.matches("^\\w+\\s+.*?$".toRegex())) {
param = input.replace("^\\w+\\s+(.*?)".toRegex(), "$1")
} else if (input.matches("^\\w+$".toRegex())) {
param = ""
} else {
return false
}
param = param.replace("[ ]".toRegex(), "")
if (command === "DCB") {
return DCB(param)
}
val opcode = Opcodes.MAP.get(Instruction.valueOf(command))
if (opcode != null) {
if (checkImmediate(param, opcode[0])) {
return true
}
if (checkZeroPage(param, opcode[1])) {
return true
}
if (checkZeroPageX(param, opcode[2])) {
return true
}
if (checkZeroPageY(param, opcode[3])) {
return true
}
if (checkAbsolute(param, opcode[4])) {
return true
}
if (checkAbsoluteX(param, opcode[5])) {
return true
}
if (checkAbsoluteY(param, opcode[6])) {
return true
}
if (checkIndirect(param, opcode[7])) {
return true
}
if (checkIndirectX(param, opcode[8])) {
return true
}
if (checkIndirectY(param, opcode[9])) {
return true
}
if (checkSingle(param, opcode[10])) {
return true
}
if (checkBranch(param, opcode[11])) {
return true
}
}
return false
}
private fun DCB(param: String): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkBranch(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkAbsolute(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkIndirectY(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkIndirectX(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkIndirect(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkAbsoluteY(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkAbsoluteX(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkZeroPageY(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkZeroPageX(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
private fun checkZeroPage(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
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
}
}
// Label lo/hi
if (param.matches("^#[<>]\\w+$".toRegex())) {
var label = param.replace("^#[<>](\\w+)$".toRegex(), "$1")
var hilo = param.replace("^#([<>]).*$".toRegex(), "$1")
pushByte(opcode)
if (labels.find(label)) {
var addr = labels.getPC(label)
when (hilo) {
">" -> {
pushByte(addr.shr(8).and(0xFF))
}
"<" -> {
pushByte(addr.and(0xFF))
}
else -> return false
}
} else {
pushByte(0x00)
return true
}
}
return false
}
// Try to parse the given parameter as a byte operand.
// Returns the (positive) value if successful, otherwise -1
private fun tryParseByteOperand(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]{1,2})$")
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,3})$")
matcher = pattern.matcher(parameter)
if (matcher.find()) {
value = Integer.parseInt(matcher.group(1), 10)
}
}
// Validate range
if (value >= 0 && value <= 0xff) {
return value
}
return -1
}
private fun pushByte(value: Int) {
memory.set(defaultCodePC, value.and(0xFF))
defaultCodePC++
codeLen++
}
private fun checkSingle(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
}