mirror of
https://github.com/felipecsl/6502Android.git
synced 2025-01-04 14:33:00 +00:00
Fixes labels, adds more tests
This commit is contained in:
parent
60c9b254a6
commit
a7769ba7f0
@ -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)
|
||||
|
@ -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())
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package android.emu6502.instructions
|
||||
|
||||
import java.util.*
|
||||
|
||||
class Symbols : HashMap<String, String>() {
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
28
app/src/test/java/android/emu6502/LabelsTest.java
Normal file
28
app/src/test/java/android/emu6502/LabelsTest.java
Normal 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()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user