kopia lustrzana https://github.com/maccasoft/z80-tools
266 wiersze
8.2 KiB
Java
266 wiersze
8.2 KiB
Java
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();
|
|
}
|
|
|
|
}
|