/*
Phase.java
(c) 2008-2015 Edward Swartz
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 v9t9.machine.ti99.asm;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import ejs.base.utils.Check;
import ejs.base.utils.HexUtils;
import v9t9.common.asm.Block;
import v9t9.common.asm.IDecompileInfo;
import v9t9.common.asm.IDecompilePhase;
import v9t9.common.asm.IHighLevelInstruction;
import v9t9.common.asm.IMachineOperand;
import v9t9.common.asm.InstTableCommon;
import v9t9.common.asm.Label;
import v9t9.common.asm.MemoryRange;
import v9t9.common.asm.RawInstruction;
import v9t9.common.asm.Routine;
import v9t9.common.cpu.ICpuState;
import v9t9.common.memory.IMemoryDomain;
import v9t9.machine.ti99.cpu.ChangeBlock9900;
import v9t9.machine.ti99.cpu.Inst9900;
import v9t9.machine.ti99.cpu.MachineOperand9900;
public abstract class Phase implements IDecompilePhase {
protected Map<Integer, Block> blocks;
protected IMemoryDomain mainMemory;
public IDecompileInfo decompileInfo;
protected Map<Block, Label> labels;
protected Map<Label, Routine> routines;
private ICpuState state;
public Phase(ICpuState state, IDecompileInfo info) {
this.decompileInfo = info;
this.state = state;
this.mainMemory = state.getConsole();
//this.setBlocks(new TreeSet<Block>());
blocks = info.getBlockMap();
labels = info.getLabelMap();
routines = info.getRoutineMap();
//blocks = new TreeMap<Integer, Block>();
//labels = new TreeMap<Block, Label>();
//this.labels = codeProvider.getLabelMap();
//labels = info.getLabelMap();
//this.routines = codeProvider.getRoutineMap();
//this.routines = info.getRoutines();
//routines = new TreeMap<Label, Routine>();
}
/* (non-Javadoc)
* @see v9t9.common.asm.IDecompilePhase#getDecompileInfo()
*/
@Override
public IDecompileInfo getDecompileInfo() {
return decompileInfo;
}
/* (non-Javadoc)
* @see v9t9.common.asm.IDecompilePhase#reset()
*/
@Override
public void reset() {
decompileInfo.reset();
blocks.clear();
labels.clear();
routines.clear();
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#getLabels()
*/
@Override
public Map<Block, Label> getLabels() {
return labels;
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#disassemble()
*/
@Override
public Collection<MemoryRange> disassemble() {
List<MemoryRange> ranges = new ArrayList<MemoryRange>();
MemoryRange prev = null;
MemoryRange range = null;
for (Iterator<MemoryRange> iter = decompileInfo.getMemoryRanges().rangeIterator(); iter
.hasNext();) {
range = iter.next();
if (prev != null && prev.isCode()) {
IHighLevelInstruction first = decompileInfo.disassemble(prev.from, range.from - prev.from);
prev.setCode(first);
ranges.add(prev);
}
prev = range;
}
return ranges;
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpInstructions(java.io.PrintStream)
*/
@Override
public void dumpInstructions(PrintStream os) {
for (Iterator<MemoryRange> iter = decompileInfo.getMemoryRanges().rangeIterator(); iter
.hasNext();) {
MemoryRange range = iter.next();
for (IHighLevelInstruction inst = (IHighLevelInstruction) range.getCode(); inst != null;
inst = inst.getLogicalNext()) {
dumpInstruction(os, inst);
}
}
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpInstructions(java.io.PrintStream, java.util.Collection)
*/
@Override
public void dumpInstructions(PrintStream os, Collection<MemoryRange> ranges) {
for (MemoryRange range : ranges) {
for (IHighLevelInstruction inst = range.getCode(); inst != null; inst = inst.getLogicalNext()) {
dumpInstruction(os, inst);
}
}
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpInstruction(java.io.PrintStream, v9t9.common.asm.IHighLevelInstruction)
*/
@Override
public void dumpInstruction(PrintStream os, IHighLevelInstruction inst) {
if (inst.getBlock() != null && inst.getBlock().getFirst() == inst) {
os.println(inst.getBlock().format());
}
Label label = getLabel(inst.getInst().pc);
if (label != null) {
os.println(label);
}
os.print('\t');
// os.println("WP="+ Utils.toHex4(inst.wp) +" " + inst.format(true, true));
os.println(inst.format(true, true));
}
protected Block getLabelKey(int addr) {
IHighLevelInstruction inst = decompileInfo.getLLInstructions().get(addr);
if (inst == null)
return null;
if (inst.getBlock() == null)
return null;
if (inst.getBlock().getFirst().getInst().pc == addr)
return inst.getBlock();
return null;
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#getLabel(int)
*/
@Override
public Label getLabel(int addr) {
Block block = getLabelKey(addr);
if (block == null)
return null;
Label label = labels.get(block);
return label;
}
private void addProgramList(int list) {
int addr, link;
char[] nameChars = new char[256];
int len;
System.out.printf("Scanning program list at >%04X\n", list);
while (list != 0) {
link = mainMemory.readWord(list);
addr = mainMemory.readWord(list + 2);
if (validCodeAddress(addr)) {
len = mainMemory.readByte(list + 4);
String name = null;
if (len > 0) {
for (int i = 0; i < len; i++) {
nameChars[i] = (char) mainMemory.readByte(list + 5 + i);
}
name = new String(nameChars, 0, len);
}
System.out.printf("Adding routine %s at >%04X\n",
name != null ? name : "<unnamed>", addr);
addRoutine(addr, name, new LinkedRoutine());
}
list = link;
}
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#getRoutine(int)
*/
@Override
public Routine getRoutine(int addr) {
Label label = getLabel(addr);
if (label == null) {
return null;
}
return routines.get(label);
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#addRoutine(int, java.lang.String, v9t9.common.asm.Routine)
*/
@Override
public Routine addRoutine(int addr, String name, Routine routine) {
Check.checkState(validCodeAddress(addr));
Label label = decompileInfo.findOrCreateLabel(addr);
if (label.getAddr() != addr) {
// instr could be in the middle of valid instructions
return null;
}
if (name != null && label.getName() == null) {
label.setName(name);
}
routine.addEntry(label);
routines.put(label, routine);
return routine;
}
/**
* Add REF/DEF tables, where each entry points to the END of the table
*
*/
public void addRefDefTables(List<Integer> refDefTables) {
// Get explicit symbol tables
for (Object element : refDefTables) {
int addr = ((Integer) element).intValue();
MemoryRange range = decompileInfo.getMemoryRanges().getRangeContaining(addr - 1);
if (range == null) {
System.err.println("!!! Can't find range containing >"
+ HexUtils.toHex4((addr - 1)));
continue;
}
int ptr = addr;
char[] nameChars = new char[6];
while (true) {
ptr -= 2;
addr = mainMemory.readWord(ptr);
if (addr == 0) {
break;
}
int length = 6;
for (int i = 0; i < 6; i++) {
int pos = 5 - i;
nameChars[pos] = (char) mainMemory.readByte(--ptr);
if (nameChars[pos] == ' ') {
length = pos;
}
}
// now, these are almost always vectors, so take the PC
String name = new String(nameChars, 0, length);
short wp = mainMemory.readWord(addr);
addr = mainMemory.readWord(addr + 2);
if (validCodeAddress(addr)) {
System.out.println("Adding label " + name + " at >"
+ HexUtils.toHex4(addr));
addRoutine(addr, name, new ContextSwitchRoutine(wp));
}
}
}
}
public void addStandardROMRoutines() {
// Get standard entries
for (int addr = 0; addr < 0x10000; addr += 0x2000) {
if (mainMemory.readByte(addr) == (byte) 0xaa) {
System.out.println("Scanning standard header at >"
+ HexUtils.toHex4(addr));
int paddr = mainMemory.readWord(addr + 4);
addProgramList(paddr);
paddr = mainMemory.readWord(addr + 6);
addProgramList(paddr);
paddr = mainMemory.readWord(addr + 8);
addProgramList(paddr);
paddr = mainMemory.readWord(addr + 10);
addProgramList(paddr);
}
if (addr == 0) {
addPossibleContextSwitch(0, "RESET");
// int1
addPossibleContextSwitch(4, "INT1");
// int2
addPossibleContextSwitch(8, "INT2");
for (int xop = 0; xop < 2; xop++) {
// XOP
addPossibleContextSwitch(0x40 + xop * 4, "XOP" + xop);
}
}
}
}
protected Routine addPossibleContextSwitch(int ctx, String name) {
short wp = mainMemory.readWord(ctx);
int addr = mainMemory.readWord(ctx + 2);
if (wp == (short) addr || wp == ctx) {
return null;
}
if (/*mainMemory.hasRamAccess(wp) && mainMemory.hasRamAccess(wp + 31)
&&*/ (addr & 1) == 0
&& validCodeAddress(addr)) {
System.out.println("Adding " + name + " vector at >"
+ HexUtils.toHex4(addr));
Routine routine = addRoutine(addr, name, new ContextSwitchRoutine(
wp));
return routine;
}
return null;
}
public short operandEffectiveAddress(IHighLevelInstruction inst, IMachineOperand mop) {
ChangeBlock9900 changes = new ChangeBlock9900(state, inst.getInst().pc);
changes.appendOperandFetch();
return changes.getEA(mop);
}
public boolean operandIsLabel(IHighLevelInstruction inst, IMachineOperand mop) {
return mop.isLabel()
&& decompileInfo.getMemoryRanges().getRangeContaining(operandEffectiveAddress(
inst, mop)) != null;
}
// operand is relocatable if it's in our memory
// and is a direct address, a jump target, or
// a nontrivial register indirect (a likely lookup table)
public boolean operandIsRelocatable(IHighLevelInstruction inst, MachineOperand9900 mop) {
if (inst.getInst().getInst() == Inst9900.Ilwpi) {
return true;
}
if (!(mop instanceof IMachineOperand)) {
return false;
}
return (mop.type == MachineOperand9900.OP_ADDR
&& (mop.val == 0 || mop.immed >= 0x20) || mop.type == MachineOperand9900.OP_JUMP)
&& decompileInfo.getMemoryRanges().getRangeContaining(operandEffectiveAddress(
inst, mop)) != null;
}
protected boolean validCodeAddress(int addr) {
MemoryRange range = decompileInfo.getMemoryRanges().getRangeContaining(addr);
if (range == null) {
return false;
}
if (!range.isCode()) {
return false;
}
RawInstruction inst = decompileInfo.getInstruction(new Integer(addr));
if (inst == null) {
return false;
}
if (inst.getInst() == InstTableCommon.Idata) {
return false;
}
return true;
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpLabels(java.io.PrintStream)
*/
@Override
public void dumpLabels(PrintStream os) {
for (Object element : labels.values()) {
Label label = (Label) element;
os.println(label);
}
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpBlocks(java.io.PrintStream)
*/
@Override
public void dumpBlocks(PrintStream os) {
for (Object element : getBlocks()) {
Block block = (Block) element;
dumpBlock(os, block);
}
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpRoutines(java.io.PrintStream)
*/
@Override
public void dumpRoutines(PrintStream os) {
for (Object element : getRoutines()) {
Routine routine = (Routine) element;
os.print("routine: " + routine);
if ((routine.flags & Routine.fSubroutine) != 0)
os.print(" [subroutine]");
if ((routine.flags & Routine.fUnknownExit) != 0)
os.print(" [unknownExit]");
os.println();
Collection<Block> blocks = routine.getSpannedBlocks();
os.print("blocks = [");
for (Block block : blocks) {
os.print(block.getId() + " ");
}
os.println("]");
for (Block block : blocks) {
dumpBlock(os, block);
}
os.println("-------------------");
}
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#dumpBlock(java.io.PrintStream, v9t9.common.asm.Block)
*/
@Override
public void dumpBlock(PrintStream os, Block block) {
for (Iterator<IHighLevelInstruction> iter = block.iterator(); iter.hasNext();) {
IHighLevelInstruction inst = iter.next();
dumpInstruction(os, inst);
}
System.out.println();
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#addBlock(v9t9.common.asm.Block)
*/
@Override
public void addBlock(Block block) {
blocks.put(block.getFirst().getInst().pc, block);
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#getBlocks()
*/
@Override
public Set<Block> getBlocks() {
return new TreeSet<Block>(blocks.values());
}
/* (non-Javadoc)
* @see v9t9.machine.ti99.asm.IDecompilePhase#getRoutines()
*/
@Override
public Collection<Routine> getRoutines() {
return routines.values();
}
}