The Micro-Executable Format support (experimental).

This commit is contained in:
matsutsuka 2007-11-28 06:16:20 +00:00
parent 874217b5b7
commit 1f1f2e12ed
9 changed files with 679 additions and 0 deletions

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1 @@
bin

17
tools/z80/java/.project Normal file
View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.markn.contiki.z80</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -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<Line> _lines;
public Area(int index, String name, int size) {
_index = index;
_name = name;
_size = size;
_lines = new ArrayList<Line>();
}
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();
}
}

View file

@ -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<Short> _bytes;
private List<Relocation> _relocs;
public Line(Objfile object, String tline, String rline) {
_relocs = new ArrayList<Relocation>(16);
_bytes = new ArrayList<Short>(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;
}
}
}
}

View file

@ -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<String, Symbol> _symbols;
private Map<File, Objfile> _objfiles;
private List<RelocationInformation> _relocations;
public Linker() {
_symbols = new HashMap<String, Symbol>();
_objfiles = new HashMap<File, Objfile>();
_relocations = new ArrayList<RelocationInformation>();
}
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<Objfile> required = new ArrayList<Objfile>();
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<Objfile> objects, Objfile obj) {
if (objects.contains(obj)) {
return;
}
objects.add(obj);
Set<File> 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();
}
}

View file

@ -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<String> _refs;
/**
* Area name and its size.
*/
private Map<String, Area> _areas;
/**
* @param linker
* @param file
*/
public Objfile(Linker linker, File file) {
_linker = linker;
_file = file;
_refs = new ArrayList<String>();
_areas = new HashMap<String, Area>();
}
public File getFile() {
return _file;
}
public Set<File> getRequiredFiles() {
Set<File> files = new HashSet<File>();
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);
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}