Added command line assembler

master
Marco Maccaferri 2020-05-16 10:33:24 +02:00
rodzic 53131d8dd0
commit f6cd577b61
2 zmienionych plików z 447 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,182 @@
/*
* Copyright (c) 2019 Marco Maccaferri and others.
* All rights reserved.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this
* distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.maccasoft.tools;
import static org.junit.Assert.assertArrayEquals;
import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import junit.framework.TestCase;
import nl.grauw.glass.Source;
import nl.grauw.glass.SourceBuilder;
public class AssemblerTest extends TestCase {
public void testGetHex() throws Exception {
Assembler builder = new Assembler();
builder.ram.put(0x0000, (byte) 0x01);
builder.ram.put(0x0001, (byte) 0x02);
builder.ram.put(0x0002, (byte) 0x03);
StringBuilder sb = builder.getHex();
assertEquals(":03000000010203F7\r\n", sb.toString());
}
public void testGetHexWithGap() throws Exception {
Assembler builder = new Assembler();
builder.ram.put(0x0000, (byte) 0x01);
builder.ram.put(0x0001, (byte) 0x02);
builder.ram.put(0x0003, (byte) 0x03);
StringBuilder sb = builder.getHex();
assertEquals(":020000000102FB\r\n:0100030003F9\r\n", sb.toString());
}
public void testGetHexWithOrigin() throws Exception {
Assembler builder = new Assembler();
builder.ram.put(0x0100, (byte) 0x01);
builder.ram.put(0x0101, (byte) 0x02);
builder.ram.put(0x0102, (byte) 0x03);
StringBuilder sb = builder.getHex();
assertEquals(":03010000010203F6\r\n", sb.toString());
}
public void testBuildHex() throws Exception {
Source source = assemble(
" xor a",
" ret");
Assembler builder = new Assembler();
builder.build(source);
StringBuilder sb = builder.getHex();
assertEquals(":02000000AFC986\r\n", sb.toString());
}
public void testBuildHexWithOrigin() throws Exception {
Source source = assemble(
" .org 100H",
" xor a",
" ret");
Assembler builder = new Assembler();
builder.build(source);
StringBuilder sb = builder.getHex();
assertEquals(":02010000AFC985\r\n", sb.toString());
}
public void testBuildHexWithDsGap() throws Exception {
Source source = assemble(
" xor a",
" .ds 1",
" ret");
Assembler builder = new Assembler();
builder.build(source);
StringBuilder sb = builder.getHex();
assertEquals(":01000000AF50\r\n:01000200C934\r\n", sb.toString());
}
public void testGetBinary() throws Exception {
Assembler builder = new Assembler();
builder.ram.put(0x0000, (byte) 0x01);
builder.ram.put(0x0001, (byte) 0x02);
builder.ram.put(0x0002, (byte) 0x03);
assertArrayEquals(b(0x01, 0x02, 0x03), builder.getBinary());
}
public void testGetBinaryWithGap() throws Exception {
Assembler builder = new Assembler();
builder.ram.put(0x0000, (byte) 0x01);
builder.ram.put(0x0001, (byte) 0x02);
builder.ram.put(0x0003, (byte) 0x03);
assertArrayEquals(b(0x01, 0x02, 0x00, 0x03), builder.getBinary());
}
public void testGetBinaryWithOrigin() throws Exception {
Assembler builder = new Assembler();
builder.ram.put(0x0100, (byte) 0x01);
builder.ram.put(0x0101, (byte) 0x02);
builder.ram.put(0x0102, (byte) 0x03);
assertArrayEquals(b(0x01, 0x02, 0x03), builder.getBinary());
}
public void testBuildBinary() throws Exception {
Source source = assemble(
" xor a",
" ret");
Assembler builder = new Assembler();
builder.build(source);
assertArrayEquals(b(0xAF, 0xC9), builder.getBinary());
}
public void testBuildBinaryWithOrigin() throws Exception {
Source source = assemble(
" .org 100H",
" xor a",
" ret");
Assembler builder = new Assembler();
builder.build(source);
assertArrayEquals(b(0xAF, 0xC9), builder.getBinary());
}
public void testBuildBinaryWithDsGap() throws Exception {
Source source = assemble(
" xor a",
" .ds 1",
" ret");
Assembler builder = new Assembler();
builder.build(source);
assertArrayEquals(b(0xAF, 0x00, 0xC9), builder.getBinary());
}
private Source assemble(String... sourceLines) {
StringBuilder builder = new StringBuilder();
for (String lineText : sourceLines) {
builder.append(lineText).append("\n");
}
SourceBuilder sourceBuilder = new SourceBuilder(new ArrayList<File>());
Source source = sourceBuilder.parse(new StringReader(builder.toString()), null);
source.register();
source.expand();
source.resolve();
return source;
}
private byte[] b(int... values) {
byte[] bytes = new byte[values.length];
for (int i = 0; i < values.length; i++) {
bytes[i] = (byte) values[i];
}
return bytes;
}
}

Wyświetl plik

@ -0,0 +1,265 @@
package com.maccasoft.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import nl.grauw.glass.AssemblyException;
import nl.grauw.glass.Line;
import nl.grauw.glass.Scope;
import nl.grauw.glass.Source;
import nl.grauw.glass.SourceBuilder;
import nl.grauw.glass.directives.If;
import nl.grauw.glass.directives.Section;
public class Assembler {
public static final int RAM_SIZE = 65536;
private static Assembler instance;
Source source;
Map<Integer, Byte> ram;
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java -jar glass.jar [OPTION] SOURCE [OBJECT]");
System.exit(1);
}
File sourcePath = null;
File objectPath = null;
List<File> includePaths = new ArrayList<File>();
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-I") && i + 1 < args.length) {
includePaths.add(new File(args[++i]));
}
else if (sourcePath == null) {
sourcePath = new File(args[i]);
}
else if (objectPath == null) {
objectPath = new File(args[i]);
}
else {
throw new AssemblyException("Too many arguments.");
}
}
try {
instance = new Assembler();
instance.assemble(sourcePath, includePaths, objectPath);
} catch (AssemblyException ex) {
StringBuilder sb = new StringBuilder();
Iterator<AssemblyException.Context> iter = ex.contexts.iterator();
if (iter.hasNext()) {
AssemblyException.Context context = iter.next();
sb.append(context.file.getName());
sb.append(":");
sb.append(context.line + 1);
if (context.column != -1) {
sb.append(":");
sb.append(context.column);
}
sb.append(": error: ");
sb.append(ex.getPlainMessage());
}
System.out.println();
System.err.println(sb.toString());
System.exit(1);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
}
}
public Assembler() {
ram = new HashMap<Integer, Byte>(RAM_SIZE);
}
void assemble(File sourcePath, List<File> includePaths, File objectPath) {
System.out.print("Compiling " + sourcePath.getName() + "...");
System.out.flush();
source = new SourceBuilder(includePaths) {
@Override
public Source parse(File sourceFile) {
if (!sourceFile.equals(sourcePath)) {
System.out.print("\r\nCompiling " + sourceFile.getName() + "...");
System.out.flush();
}
return super.parse(sourceFile);
}
}.parse(sourcePath);
source.register();
source.expand();
source.resolve();
build(source);
writeObject(objectPath);
int lower = Integer.MAX_VALUE;
int higher = Integer.MIN_VALUE;
for (Line line : source.getLines()) {
try {
if (line.getSize() != 0) {
lower = Math.min(lower, line.getScope().getAddress());
higher = Math.max(higher, line.getScope().getAddress() + line.getSize() - 1);
}
} catch (Exception e) {
// Ignore, not important
}
}
System.out.println();
System.out.println(String.format("Compiled %d lines from %04XH to %04XH (%d bytes)", source.getLines().size(), lower, higher, higher - lower + 1));
}
void build(Source source) {
for (Line line : source.getLines()) {
try {
if (line.getDirective() instanceof If) {
if (line.getInstructionObject() != null) {
nl.grauw.glass.instructions.If ins = (nl.grauw.glass.instructions.If) line.getInstruction();
build(ins.getThenSource());
if (ins.getElseSource() != null) {
build(ins.getElseSource());
}
}
else {
If ins = (If) line.getDirective();
build(ins.getThenSource());
if (ins.getElseSource() != null) {
build(ins.getElseSource());
}
}
}
else if (line.getDirective() instanceof Section) {
if (line.getInstructionObject() != null) {
nl.grauw.glass.instructions.Section ins = (nl.grauw.glass.instructions.Section) line.getInstruction();
build(ins.getSource());
}
else {
Section ins = (Section) line.getDirective();
build(ins.getSource());
}
}
else {
Scope scope = line.getScope();
if (scope.isAddressSet()) {
byte[] code = line.getBytes();
for (int i = 0; i < code.length; i++) {
ram.put(scope.getAddress() + i, code[i]);
}
}
}
} catch (AssemblyException e) {
e.addContext(line);
throw e;
} catch (Exception e) {
e.printStackTrace();
}
}
}
void writeObject(File objectPath) {
try {
if (objectPath.getName().toLowerCase().endsWith(".hex")) {
OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(objectPath));
os.write(getHex().toString());
os.write(":00000001FF\r\n");
os.close();
}
else {
OutputStream output = new FileOutputStream(objectPath);
output.write(getBinary());
output.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
byte[] getBinary() {
int from, to;
from = 0;
while (from < RAM_SIZE && !ram.containsKey(from)) {
from++;
}
to = RAM_SIZE - 1;
while (to > from && !ram.containsKey(to)) {
to--;
}
to++;
byte[] data = new byte[to - from];
for (int i = 0; i < data.length; i++) {
if (ram.containsKey(from + i)) {
data[i] = ram.get(from + i);
}
}
return data;
}
StringBuilder getHex() {
int from, to;
StringBuilder sb = new StringBuilder();
from = 0;
while (from < RAM_SIZE) {
if (ram.containsKey(from)) {
to = from + 1;
while (to < RAM_SIZE && ram.containsKey(to)) {
to++;
}
byte[] data = new byte[to - from];
for (int i = 0; i < data.length; i++) {
data[i] = ram.get(from + i);
}
sb.append(toHexString(from, data));
from = to - 1;
}
from++;
}
return sb;
}
String toHexString(int addr, byte[] data) {
StringBuilder sb = new StringBuilder();
int i = 0;
while ((data.length - i) > 0) {
int l = data.length - i;
if (l > 24) {
l = 24;
}
sb.append(String.format(":%02X%04X%02X", l, addr, 0));
int checksum = l + (addr & 0xFF) + ((addr >> 8) & 0xFF) + 0;
for (int n = 0; n < l; n++, i++, addr++) {
sb.append(String.format("%02X", data[i]));
checksum += data[i];
}
sb.append(String.format("%02X\r\n", (-checksum) & 0xFF));
}
return sb.toString();
}
}