diff --git a/tools/z80/java/.classpath b/tools/z80/java/.classpath
new file mode 100644
index 000000000..fb5011632
--- /dev/null
+++ b/tools/z80/java/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/tools/z80/java/.cvsignore b/tools/z80/java/.cvsignore
new file mode 100644
index 000000000..ba077a403
--- /dev/null
+++ b/tools/z80/java/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/tools/z80/java/.project b/tools/z80/java/.project
new file mode 100644
index 000000000..eb92522af
--- /dev/null
+++ b/tools/z80/java/.project
@@ -0,0 +1,17 @@
+
+
+ org.markn.contiki.z80
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/tools/z80/java/src/org/markn/contiki/z80/linker/Area.java b/tools/z80/java/src/org/markn/contiki/z80/linker/Area.java
new file mode 100644
index 000000000..c43a276da
--- /dev/null
+++ b/tools/z80/java/src/org/markn/contiki/z80/linker/Area.java
@@ -0,0 +1,53 @@
+/**
+ *
+ */
+package org.markn.contiki.z80.linker;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Area {
+ private short _offset;
+ private int _index;
+ private String _name;
+ private int _size;
+ /**
+ * Line data.
+ */
+ private List _lines;
+
+ public Area(int index, String name, int size) {
+ _index = index;
+ _name = name;
+ _size = size;
+ _lines = new ArrayList();
+ }
+ public short getOffset() {
+ return _offset;
+ }
+ public int getIndex() {
+ return _index;
+ }
+ public String getName() {
+ return _name;
+ }
+ public int getSize() {
+ return _size;
+ }
+ public void setOffset(short offset) {
+ _offset = offset;
+ }
+ public void addLine(Line line) {
+ _lines.add(line);
+ }
+ public void relocate(Objfile object, byte[] image) {
+ for (Line line: _lines) {
+ line.fill(object, image);
+ }
+ }
+ public String toString() {
+ StringBuffer buf = new StringBuffer(120);
+ buf.append(_name);
+ return buf.toString();
+ }
+}
\ No newline at end of file
diff --git a/tools/z80/java/src/org/markn/contiki/z80/linker/Line.java b/tools/z80/java/src/org/markn/contiki/z80/linker/Line.java
new file mode 100644
index 000000000..9f43c9ce2
--- /dev/null
+++ b/tools/z80/java/src/org/markn/contiki/z80/linker/Line.java
@@ -0,0 +1,132 @@
+package org.markn.contiki.z80.linker;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Line {
+ private static final Pattern WORD = Pattern.compile("([\\dA-F]{2})\\s([\\dA-F]{2})");
+ private static final Pattern BYTE = Pattern.compile("([\\dA-F]{2})");
+
+ private class Relocation {
+ private int _mode;
+ private int _offset;
+ // area index(!S) or symbol index(S)
+ private int _symbol;
+ }
+
+ private Area _area;
+ private int _address;
+ private List _bytes;
+ private List _relocs;
+
+ public Line(Objfile object, String tline, String rline) {
+ _relocs = new ArrayList(16);
+ _bytes = new ArrayList(16);
+ rline = rline.substring(8);
+ int areaindex = getWord(rline);
+ _area = object.getArea(areaindex);
+ if (_area == null) {
+ throw new IllegalArgumentException("no such area:" + areaindex);
+ }
+ _area.addLine(this);
+ tline = tline.substring(2);
+ _address = getWord(tline);
+ tline = tline.substring(3);
+ while (true) {
+ if (tline.length() < 3) {
+ break;
+ }
+ tline = tline.substring(3);
+ _bytes.add(getByte(tline));
+ }
+ // relocation line
+ while (true) {
+ if (rline.length() < 6) {
+ break;
+ }
+ Relocation reloc = new Relocation();
+ _relocs.add(reloc);
+ rline = rline.substring(6);
+ reloc._mode = getByte(rline);
+ rline = rline.substring(3);
+ reloc._offset = getByte(rline) - 2;
+ rline = rline.substring(3);
+ reloc._symbol = getWord(rline);
+ }
+ }
+ private int getWord(String line) {
+ Matcher m = WORD.matcher(line);
+ if (!m.find()) {
+ return -1;
+ }
+ String hexstr = m.group(2) + m.group(1);
+ return Integer.parseInt(hexstr, 16);
+ }
+
+ private short getByte(String line) {
+ Matcher m = BYTE.matcher(line);
+ if (!m.find()) {
+ return -1;
+ }
+ String hexstr = m.group(1);
+ return Short.parseShort(hexstr, 16);
+ }
+ public void fill(Objfile object, byte[] image) {
+ int address = _address + _area.getOffset();
+ for (Relocation reloc : _relocs) {
+ int target = 0;
+ byte mode = 0; // Ext/Int MSB/LSB Byte/Word
+ RelocationInformation info = new RelocationInformation();
+ if ((reloc._mode & 2) > 0) {
+ // external
+ Symbol symbol = object.getSymbol(reloc._symbol);
+ target = symbol.calcOffset();
+ System.out.printf("%s %04X=>%04X\n", symbol, symbol.getOffset(), target);
+ if (symbol.isAbsolute()) {
+ mode |= 0x80;
+ }
+ } else {
+ // internal
+ Area area = object.getArea(reloc._symbol);
+ int offset = area.getOffset();
+ short source = (short) ((_bytes.get(reloc._offset + 1) << 8) + _bytes.get(reloc._offset));
+ target = (short) (source + offset);
+ // TODO: save relocation information
+ System.out.printf("%s:%04X=>%04X\n", area, source, target);
+ }
+ info.setAddress(address);
+ if ((reloc._mode & 1) > 0) {
+ // byte mode
+ if ((reloc._mode & 128) > 0) {
+ // MSB
+ mode |= 0x60;
+ _bytes.set(reloc._offset, (short) (target >> 8));
+ _bytes.set(reloc._offset + 1, (short) -1);
+ } else {
+ // LSB
+ mode |= 0x20;
+ _bytes.set(reloc._offset, (short) -1);
+ _bytes.set(reloc._offset, (short) (target & 0xff));
+ }
+ address++;
+ } else {
+ // word mode
+ _bytes.set(reloc._offset, (short) (target & 0xff));
+ _bytes.set(reloc._offset + 1, (short) (target >> 8));
+ address += 2;
+ }
+ info.setMode(mode);
+ info.setData(target);
+ object.getLinker().addRelocation(info);
+ }
+ address = _address + _area.getOffset();
+ for (int data : _bytes) {
+ if (data >= 0) {
+ image[address++] = (byte) data;
+ }
+ }
+ }
+
+}
diff --git a/tools/z80/java/src/org/markn/contiki/z80/linker/Linker.java b/tools/z80/java/src/org/markn/contiki/z80/linker/Linker.java
new file mode 100644
index 000000000..031ef531e
--- /dev/null
+++ b/tools/z80/java/src/org/markn/contiki/z80/linker/Linker.java
@@ -0,0 +1,192 @@
+package org.markn.contiki.z80.linker;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Linker {
+ private static final String _DATA = "_DATA";
+
+ private static final String _GSINIT = "_GSINIT";
+
+ private static final String _CODE = "_CODE";
+
+ private static final Pattern SYMLINE = Pattern.compile("^00:([\\dA-F]{4})\\s(\\w+)");
+
+ public static void main(String[] arg) throws IOException {
+ File target = new File(arg[0]);
+ if (!target.exists()) {
+ System.out.println(arg[0] + " is no exist.");
+ return;
+ }
+ Linker linker = new Linker();
+ linker.prepare();
+ linker.make(target);
+ }
+
+ private Map _symbols;
+
+ private Map _objfiles;
+
+ private List _relocations;
+
+ public Linker() {
+ _symbols = new HashMap();
+ _objfiles = new HashMap();
+ _relocations = new ArrayList();
+ }
+
+ private void prepare() throws IOException {
+ loadSymfile("contiki.sym");
+ loadLibfile("contiki-pc-6001.lib");
+ loadLibfile("c:/dev/sdcc/lib/z80/z80.lib");
+ }
+
+ private void make(File file) throws IOException {
+ List required = new ArrayList();
+ Objfile object = _objfiles.get(file);
+ make(required, object);
+ short codeSize = 0;
+ for (Objfile obj : required) {
+ obj.setAreaOffset(_CODE, codeSize);
+ codeSize += obj.getAreaSize(_CODE);
+ }
+ short gsinitSize = 0;
+ short gsinitOffset = codeSize;
+ for (Objfile obj : required) {
+ obj.setAreaOffset(_GSINIT, gsinitOffset);
+ gsinitSize += obj.getAreaSize(_GSINIT);
+ gsinitOffset += obj.getAreaSize(_GSINIT);
+ }
+ // add space for C9 (ret)
+ gsinitSize++;
+ gsinitOffset++;
+ short dataSize = 0;
+ short dataOffset = gsinitOffset;
+ for (Objfile obj : required) {
+ obj.setAreaOffset(_DATA, dataOffset);
+ dataSize += obj.getAreaSize(_DATA);
+ dataOffset += obj.getAreaSize(_DATA);
+ }
+ byte[] image = new byte[gsinitOffset];
+ for (Objfile obj : required) {
+ System.out.printf("Relocating: %s %s=%04X %s=%04X %s=%04X\n", obj.getFile(),
+ _CODE, obj.getArea(_CODE).getOffset(),
+ _GSINIT, obj.getArea(_GSINIT).getOffset(),
+ _DATA, obj.getArea(_DATA).getOffset());
+ obj.relocate(_CODE, image);
+ obj.relocate(_GSINIT, image);
+ }
+ // the end of GSINIT
+ image[image.length - 1] = (byte) 0xc9;
+
+ System.out.println("_CODE:" + Integer.toHexString(codeSize));
+ System.out.println("_GSINIT:" + Integer.toHexString(gsinitSize));
+ System.out.println("_DATA:" + Integer.toHexString(dataSize));
+ dump(image, dataOffset);
+ out(new File("tmp.out"), image, dataOffset);
+ }
+
+ private void dump(byte[] image, int size) {
+ int address = 0;
+ System.out.printf("size:%04X", size);
+ while (address < image.length) {
+ if (address % 16 == 0) {
+ System.out.printf("\n%04X:", address);
+ }
+ System.out.printf("%02x ", image[address++]);
+ }
+ System.out.println();
+ System.out.println("Relocations:" + _relocations.size());
+ for (RelocationInformation reloc : _relocations) {
+ System.out.println(reloc);
+ }
+ }
+
+ private void out(File file, byte[] image, int size) throws IOException {
+ FileOutputStream stream = new FileOutputStream(file);
+ stream.write(size & 0xff);
+ stream.write(size >> 8);
+ stream.write(image);
+ stream.write(_relocations.size() & 0xff);
+ stream.write(_relocations.size() >> 8);
+ for (RelocationInformation reloc : _relocations) {
+ reloc.write(stream);
+ }
+ stream.close();
+ }
+
+ private void make(List objects, Objfile obj) {
+ if (objects.contains(obj)) {
+ return;
+ }
+ objects.add(obj);
+ Set required = obj.getRequiredFiles();
+ for (File require : required) {
+ make(objects, _objfiles.get(require));
+ }
+ }
+
+ private void loadLibfile(String filename) throws IOException {
+ File file = new File(filename);
+ File dir = file.getParentFile();
+ BufferedReader isr = new BufferedReader(new FileReader(file));
+ while (true) {
+ String line = isr.readLine();
+ if (line == null) {
+ break;
+ }
+ File objfile = new File(dir, line);
+ Objfile object = new Objfile(this, objfile);
+ _objfiles.put(objfile, object);
+ object.analyze();
+ }
+ }
+
+ public void addSymbol(String name, Symbol symbol) {
+ if (!_symbols.containsKey(name)) {
+ _symbols.put(name, symbol);
+ } else if (_symbols.get(name).getArea() != null) {
+ System.out.println("Warning: duplicate symbol:" + name);
+ }
+ }
+
+ public void addRelocation(RelocationInformation info) {
+ _relocations.add(info);
+ }
+
+ public Symbol getSymbol(String name) {
+ return _symbols.get(name);
+ }
+
+ private void loadSymfile(String filename) throws IOException {
+ File file = new File(filename);
+ BufferedReader isr = new BufferedReader(new FileReader(file));
+ while (true) {
+ String line = isr.readLine();
+ if (line == null) {
+ break;
+ }
+ if (line.startsWith(";")) {
+ // comment
+ continue;
+ }
+ Matcher m = SYMLINE.matcher(line);
+ if (!m.find()) {
+ continue;
+ }
+ Symbol symbol = new Symbol(file, Integer.parseInt(m.group(1), 16));
+ _symbols.put(m.group(2), symbol);
+ }
+ isr.close();
+ }
+}
diff --git a/tools/z80/java/src/org/markn/contiki/z80/linker/Objfile.java b/tools/z80/java/src/org/markn/contiki/z80/linker/Objfile.java
new file mode 100644
index 000000000..8698a49fb
--- /dev/null
+++ b/tools/z80/java/src/org/markn/contiki/z80/linker/Objfile.java
@@ -0,0 +1,175 @@
+package org.markn.contiki.z80.linker;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author markn
+ *
+ */
+public class Objfile {
+ private static final Pattern REFLINE = Pattern.compile("^S\\s(\\w+)\\sRef([\\dA-F]{4})");
+ private static final Pattern DEFLINE = Pattern.compile("^S\\s(\\w+)\\sDef([\\dA-F]{4})");
+ private static final Pattern AREALINE = Pattern.compile("^A\\s(\\w+)\\ssize\\s([\\dA-F]+)");
+
+ /**
+ * Parent object.
+ */
+ private Linker _linker;
+
+ /**
+ * A file that this object indicates.
+ */
+ private File _file;
+
+ /**
+ * References to be imported.
+ */
+ private List _refs;
+
+ /**
+ * Area name and its size.
+ */
+ private Map _areas;
+
+ /**
+ * @param linker
+ * @param file
+ */
+ public Objfile(Linker linker, File file) {
+ _linker = linker;
+ _file = file;
+ _refs = new ArrayList();
+ _areas = new HashMap();
+ }
+
+ public File getFile() {
+ return _file;
+ }
+
+ public Set getRequiredFiles() {
+ Set files = new HashSet();
+ for (String ref : _refs) {
+ if (_linker.getSymbol(ref).isAbsolute()) {
+ // no need to link
+ continue;
+ }
+ Symbol symbol = _linker.getSymbol(ref);
+ if (symbol != null) {
+ files.add(symbol.getFile());
+ } else {
+ System.out.println("undefined symbol:" + ref);
+ }
+ }
+ return files;
+ }
+
+ public Linker getLinker() {
+ return _linker;
+ }
+
+ public Area getArea(String name) {
+ return _areas.get(name);
+ }
+
+ public int getAreaSize(String name) {
+ Area area = _areas.get(name);
+ if (area != null) {
+ return area.getSize();
+ } else {
+ return 0;
+ }
+ }
+
+ public void setAreaOffset(String name, short offset) {
+ Area area = _areas.get(name);
+ if (area != null) {
+ area.setOffset(offset);
+ }
+ }
+
+ public void analyze() throws IOException {
+ System.out.println("analyzing:" + _file);
+ int areaindex = 0;
+ BufferedReader isr = new BufferedReader(new FileReader(_file));
+ String tline = null;
+ Area area = null;
+ while (true) {
+ String line = isr.readLine();
+ if (line == null) {
+ break;
+ }
+ Matcher m = AREALINE.matcher(line);
+ if (m.find()) {
+ String areaname = m.group(1);
+ area = new Area(areaindex, areaname, Integer.parseInt(m.group(2), 16));
+ _areas.put(areaname, area);
+ areaindex++;
+ continue;
+ }
+ m = REFLINE.matcher(line);
+ if (m.find()) {
+ _refs.add(m.group(1));
+ continue;
+ }
+ m = DEFLINE.matcher(line);
+ if (m.find()) {
+ String symbolname = m.group(1);
+ int address = Integer.parseInt(m.group(2), 16);
+ Symbol symbol = new Symbol(_file, area, (short) address);
+ _linker.addSymbol(symbolname, symbol);
+ continue;
+ }
+ if (line.startsWith("T")) {
+ // process T line
+ tline = line;
+ }
+ if (line.startsWith("R")) {
+ // process R line
+ if (tline == null) {
+ System.out.println("wrong format as object file:" + _file);
+ continue;
+ }
+ new Line(this, tline, line);
+ tline = null;
+ }
+ }
+ isr.close();
+ }
+
+ public Area getArea(int index) {
+ for (Area area : _areas.values()) {
+ if (area.getIndex() == index) {
+ return area;
+ }
+ }
+ return null;
+ }
+
+ public Symbol getSymbol(int index) {
+ String name = _refs.get(index);
+ return _linker.getSymbol(name);
+ }
+
+ public void relocate(String areaname, byte[] image) {
+ Area area = _areas.get(areaname);
+ if (area != null) {
+ area.relocate(this, image);
+ return;
+ }
+ System.out.println("no such area:" + areaname + " on " + _file);
+ }
+
+
+
+}
diff --git a/tools/z80/java/src/org/markn/contiki/z80/linker/RelocationInformation.java b/tools/z80/java/src/org/markn/contiki/z80/linker/RelocationInformation.java
new file mode 100644
index 000000000..56d18a1af
--- /dev/null
+++ b/tools/z80/java/src/org/markn/contiki/z80/linker/RelocationInformation.java
@@ -0,0 +1,51 @@
+package org.markn.contiki.z80.linker;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class RelocationInformation {
+ private byte _mode;
+ private int _address;
+ private int _data;
+ public byte getMode() {
+ return _mode;
+ }
+ public void setMode(byte mode) {
+ this._mode = mode;
+ }
+ public int getAddress() {
+ return _address;
+ }
+ public void setAddress(int address) {
+ _address = address;
+ }
+ public int getData() {
+ return _data;
+ }
+ public void setData(int data) {
+ _data = data;
+ }
+ public void write(OutputStream stream) throws IOException {
+ stream.write(_mode);
+ stream.write(_address & 0xff);
+ stream.write(_address >> 8);
+ stream.write(_data & 0xff);
+ stream.write(_data >> 8);
+ }
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append((_mode & 0x80) > 0 ? 'E' : 'I');
+ if ((_mode & 0x60) == 0x60) {
+ buf.append("MB");
+ } else if ((_mode & 0x20) > 0) {
+ buf.append("LB");
+ } else {
+ buf.append("_W");
+ }
+// buf.append((_mode & 0x60) > 0 ? 'M' : 'L');
+// buf.append((_mode & 0x20) > 0 ? 'B' : 'W');
+ buf.append(String.format(":%04X:%04X", _address, _data));
+ return buf.toString();
+ }
+
+}
diff --git a/tools/z80/java/src/org/markn/contiki/z80/linker/Symbol.java b/tools/z80/java/src/org/markn/contiki/z80/linker/Symbol.java
new file mode 100644
index 000000000..a72bd9507
--- /dev/null
+++ b/tools/z80/java/src/org/markn/contiki/z80/linker/Symbol.java
@@ -0,0 +1,52 @@
+package org.markn.contiki.z80.linker;
+
+import java.io.File;
+
+public class Symbol {
+ private File _file;
+ private Area _area;
+ private int _offset;
+ public Symbol(File file, int offset) {
+ this(file, null, offset);
+ }
+ public Symbol(File file, Area area, int offset) {
+ this._file = file;
+ this._area = area;
+ this._offset = offset;
+ }
+ public File getFile() {
+ return _file;
+ }
+ public Area getArea() {
+ return _area;
+ }
+ public boolean isAbsolute() {
+ return _area == null;
+ }
+ public int getOffset() {
+ return _offset;
+ }
+ public int calcOffset() {
+ if (isAbsolute()) {
+ return _offset;
+ } else {
+ return _offset + _area.getOffset();
+ }
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer(120);
+ buf.append(_file.toString());
+ buf.append(':');
+ if (_area != null) {
+ buf.append(_area);
+ } else {
+ buf.append("Absolute");
+ }
+ buf.append(':');
+ buf.append(Integer.toHexString(_offset));
+ return buf.toString();
+
+ }
+
+}