From 1f1f2e12ed431d392f6e260117296f7541223bfd Mon Sep 17 00:00:00 2001 From: matsutsuka Date: Wed, 28 Nov 2007 06:16:20 +0000 Subject: [PATCH] The Micro-Executable Format support (experimental). --- tools/z80/java/.classpath | 6 + tools/z80/java/.cvsignore | 1 + tools/z80/java/.project | 17 ++ .../org/markn/contiki/z80/linker/Area.java | 53 +++++ .../org/markn/contiki/z80/linker/Line.java | 132 ++++++++++++ .../org/markn/contiki/z80/linker/Linker.java | 192 ++++++++++++++++++ .../org/markn/contiki/z80/linker/Objfile.java | 175 ++++++++++++++++ .../z80/linker/RelocationInformation.java | 51 +++++ .../org/markn/contiki/z80/linker/Symbol.java | 52 +++++ 9 files changed, 679 insertions(+) create mode 100644 tools/z80/java/.classpath create mode 100644 tools/z80/java/.cvsignore create mode 100644 tools/z80/java/.project create mode 100644 tools/z80/java/src/org/markn/contiki/z80/linker/Area.java create mode 100644 tools/z80/java/src/org/markn/contiki/z80/linker/Line.java create mode 100644 tools/z80/java/src/org/markn/contiki/z80/linker/Linker.java create mode 100644 tools/z80/java/src/org/markn/contiki/z80/linker/Objfile.java create mode 100644 tools/z80/java/src/org/markn/contiki/z80/linker/RelocationInformation.java create mode 100644 tools/z80/java/src/org/markn/contiki/z80/linker/Symbol.java 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(); + + } + +}