Fixes labels, adds more tests

This commit is contained in:
Felipe Lima 2015-06-14 22:51:31 -07:00
parent 60c9b254a6
commit a7769ba7f0
7 changed files with 211 additions and 38 deletions

View File

@ -2,18 +2,28 @@ package android.emu6502
import android.emu6502.instructions.Instruction
import android.emu6502.instructions.Opcodes
import android.emu6502.instructions.Symbols
import java.util.HashMap
import java.util.regex.Pattern
import kotlin.text.Regex
class Assembler(private var labels: Labels, private var memory: Memory,
private var symbols: Symbols) {
class Assembler(private var memory: Memory, private val symbols: Symbols) {
var codeLen = 0
val BOOTSTRAP_ADDRESS = 0x600
var defaultCodePC = BOOTSTRAP_ADDRESS
private var labels = Labels(this, symbols)
fun assembleCode(lines: List<String>) {
lines.forEachIndexed { i, line ->
val preprocessedLines = preprocess(lines)
labels.indexLines(preprocessedLines)
// Reset PC and code length since labels screwed it
defaultCodePC = BOOTSTRAP_ADDRESS
codeLen = 0
preprocessedLines.forEachIndexed { i, line ->
if (!assembleLine(line)) {
throw RuntimeException("**Syntax error line " + (i + 1) + ": " + line + "**")
}
@ -22,7 +32,24 @@ class Assembler(private var labels: Labels, private var memory: Memory,
memory.set(defaultCodePC, 0x00)
}
private fun assembleLine(line: String): Boolean {
private fun preprocess(lines: List<String>): List<String> {
val pattern = Pattern.compile("^define\\s+(\\w+)\\s+(\\S+)", Pattern.CASE_INSENSITIVE)
var sanitizedLines = lines.map { sanitize(it) }
sanitizedLines
.map { pattern.matcher(it) }
.filter { it.find() }
.forEach { symbols.put(it.group(1), sanitize(it.group(2))) }
return sanitizedLines.filterNot { pattern.matcher(it).find() }
}
private fun sanitize(line: String): String {
return line.replace("^(.*?);.*".toRegex(), "$1").trim()
}
fun assembleLine(line: String): Boolean {
var input = line
var command: String
var param: String
@ -56,7 +83,7 @@ class Assembler(private var labels: Labels, private var memory: Memory,
} else {
addr = Integer.parseInt(param, 10)
}
if ((addr < 0) || (addr > 0xffff)) {
if (addr < 0 || addr > 0xffff) {
throw IllegalStateException("Unable to relocate code outside 64k memory")
}
defaultCodePC = addr
@ -73,7 +100,7 @@ class Assembler(private var labels: Labels, private var memory: Memory,
param = param.replace("[ ]".toRegex(), "")
if (command === "DCB") {
if (command == "DCB") {
return DCB(param)
}
@ -129,12 +156,35 @@ class Assembler(private var labels: Labels, private var memory: Memory,
"not implemented") //To change body of created functions use File | Settings | File Templates.
}
/** Common branch function for all branches (BCC, BCS, BEQ, BNE..) */
private fun checkBranch(param: String, opcode: Int): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
if (opcode == 0xff) {
return false
}
var addr = -1
if (param.matches("\\w+".toRegex())) {
addr = labels.get(param)
}
if (addr == -1) {
pushWord(0x00)
return false
}
pushByte(opcode)
if (addr < (defaultCodePC - 0x600)) {
// Backwards?
pushByte((0xff - ((defaultCodePC - 0x600) - addr)).and(0xff))
return true
}
pushByte((addr - (defaultCodePC - 0x600) - 1).and(0xff))
return true
}
private fun checkAbsolute(param: String, opcode: Int): Boolean {
if (opcode == 0xff) {
return false
}
if (checkWordOperand(param, opcode, "^([\\w\\$]+)$")) {
return true
}
@ -183,8 +233,8 @@ class Assembler(private var labels: Labels, private var memory: Memory,
if (param.matches(regex)) {
val finalParam = param.replace(",Y", "", true).replace(",X", "", true)
pushByte(opcode)
if (labels.find(finalParam)) {
val addr = (labels.getPC(finalParam))
val addr = labels.get(finalParam)
if (addr != null) {
if (addr < 0 || addr > 0xffff) {
return false
}
@ -269,8 +319,8 @@ class Assembler(private var labels: Labels, private var memory: Memory,
var label = param.replace("^#[<>](\\w+)$".toRegex(), "$1")
var hilo = param.replace("^#([<>]).*$".toRegex(), "$1")
pushByte(opcode)
if (labels.find(label)) {
var addr = labels.getPC(label)
val addr = labels.get(label)
if (addr != null) {
when (hilo) {
">" -> {
pushByte(addr.shr(8).and(0xFF))
@ -296,7 +346,7 @@ class Assembler(private var labels: Labels, private var memory: Memory,
var parameter = param
if (parameter.matches("^\\w+$".toRegex())) {
var lookupVal = symbols.lookup(parameter) // Substitute symbol by actual value, then proceed
var lookupVal = symbols.get(parameter) // Substitute symbol by actual value, then proceed
if (lookupVal != null) {
parameter = lookupVal
}
@ -328,7 +378,7 @@ class Assembler(private var labels: Labels, private var memory: Memory,
var parameter = param
if (parameter.matches("^\\w+$".toRegex())) {
var lookupVal = symbols.lookup(parameter) // Substitute symbol by actual value, then proceed
var lookupVal = symbols.get(parameter) // Substitute symbol by actual value, then proceed
if (lookupVal != null) {
parameter = lookupVal
}
@ -368,7 +418,7 @@ class Assembler(private var labels: Labels, private var memory: Memory,
private fun checkSingle(param: String, opcode: Int): Boolean {
// Accumulator instructions are counted as single-byte opcodes
if (!param.equals("") && !param.equals("A")) {
if (!param.equals("") && !param.equals("A")) {
return false
}
pushByte(opcode)

View File

@ -1,8 +1,10 @@
package android.emu6502
import android.emu6502.instructions.Symbols
final class Emulator {
val display = Display()
val memory = Memory(display)
val cpu = CPU(memory)
val assembler = Assembler(Labels(), memory, Symbols())
val assembler = Assembler(memory, Symbols())
}

View File

@ -1,13 +1,39 @@
package android.emu6502
class Labels {
fun getPC(label: String): Int {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
import android.emu6502.instructions.Symbols
import java.util.*
class Labels(private val assembler: Assembler, private val symbols: Symbols) : HashMap<String, Int>() {
private val labelIndex: ArrayList<String> = ArrayList()
fun indexLines(lines: List<String>) {
lines.forEach { line ->
indexLine(line)
}
}
fun find(label: String): Boolean {
throw UnsupportedOperationException(
"not implemented") //To change body of created functions use File | Settings | File Templates.
fun get(label: String): Int {
return super.get(label) ?: -1
}
// Extract label if line contains one and calculate position in memory.
// Return false if label already exists.
private fun indexLine(input: String) {
// Figure out how many bytes this instruction takes
val currentPC = assembler.defaultCodePC;
// TODO: find a better way for Labels to have access to assembler
assembler.assembleLine(input);
// Find command or label
if (input.matches("^\\w+:".toRegex())) {
val label = input.replace("(^\\w+):.*$".toRegex(), "$1");
if (symbols.get(label) != null) {
throw RuntimeException(
"**Label " + label + "is already used as a symbol; please rename one of them**");
}
put(label, currentPC);
}
}
}

View File

@ -1,13 +0,0 @@
package android.emu6502
class Symbols {
private val table: Map<String, String> = emptyMap()
fun lookup(key: String): String? {
return null
}
fun add(key: String, value: String) {
}
}

View File

@ -0,0 +1,6 @@
package android.emu6502.instructions
import java.util.*
class Symbols : HashMap<String, String>() {
}

View File

@ -2,6 +2,9 @@ package android.emu6502;
import com.google.common.collect.ImmutableList;
import android.emu6502.instructions.Symbols;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
@ -11,7 +14,13 @@ import static org.junit.Assert.assertThat;
public class AssemblerTest {
@Test public void testAssembler() {
private Assembler assembler;
@Before public void setUp() {
assembler = new Assembler(new Memory(new Display()), new Symbols());
}
@Test public void testSimple() {
List<String> lines = ImmutableList.of(
"LDA #$01",
"STA $0200",
@ -19,8 +28,73 @@ public class AssemblerTest {
"STA $0201",
"LDA #$08",
"STA $0202");
Assembler assembler = new Assembler(new Labels(), new Memory(new Display()), new Symbols());
assembler.assembleCode(lines);
assertThat(assembler.hexdump(), equalTo("0600: A9 01 8D 00 02 A9 05 8D 01 02 A9 08 8D 02 02"));
}
@Test public void testWithComments() {
List<String> lines = ImmutableList.of(
"LDA #$c0 ;Load the hex value $c0 into the A register",
"TAX ;Transfer the value in the A register to X",
"INX ;Increment the value in the X register",
"ADC #$c4 ;Add the hex value $c4 to the A register",
"BRK ;Break - we're done");
assembler.assembleCode(lines);
assertThat(assembler.hexdump(), equalTo("0600: A9 C0 AA E8 69 C4 00"));
}
@Test public void testBranchAndLabel() {
List<String> lines = ImmutableList.of(
"LDX #$08",
"decrement:",
"DEX",
"STX $0200",
"CPX #$03",
"BNE decrement",
"STX $0201",
"BRK");
assembler.assembleCode(lines);
assertThat(assembler.hexdump(), equalTo("0600: A2 08 CA 8E 00 02 E0 03 D0 F8 8E 01 02 00"));
}
@Test public void testRelative() {
List<String> lines = ImmutableList.of(
"LDA #$01",
"CMP #$02",
"BNE notequal",
"STA $22",
"notequal:",
"BRK");
assembler.assembleCode(lines);
assertThat(assembler.hexdump(), equalTo("0600: A9 01 C9 02 D0 02 85 22 00"));
}
@Test public void testIndirect() {
List<String> lines = ImmutableList.of(
"LDA #$01",
"STA $f0",
"LDA #$cc",
"STA $f1",
"JMP ($00f0) ;dereferences to $cc01");
assembler.assembleCode(lines);
assertThat(assembler.hexdump(), equalTo("0600: A9 01 85 F0 A9 CC 85 F1 6C F0 00"));
}
@Test public void testIndexedIndirect() {
List<String> lines = ImmutableList.of(
"LDX #$01",
"LDA #$05",
"STA $01",
"LDA #$06",
"STA $02",
"LDY #$0a",
"STY $0605",
"LDA ($00,X)");
assembler.assembleCode(lines);
assertThat(assembler.hexdump(),
equalTo("0600: A2 01 A9 05 85 01 A9 06 85 02 A0 0A 8C 05 06 A1 \n0610: 00"));
}
}

View File

@ -0,0 +1,28 @@
package android.emu6502;
import android.emu6502.instructions.Symbols;
import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
public class LabelsTest {
private Labels labels;
private Assembler assembler;
@Before public void setUp() {
Symbols symbols = new Symbols();
assembler = new Assembler(new Memory(new Display()), symbols);
labels = new Labels(assembler, symbols);
}
@Test public void testAddLabel() {
labels.indexLines(Collections.singletonList("test:"));
assertThat(labels.get("test"), equalTo(assembler.getDefaultCodePC()));
}
}