/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.codesourcery.jasm16.disassembler;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.Size;
import de.codesourcery.jasm16.ast.OperandNode.OperandPosition;
import de.codesourcery.jasm16.compiler.io.FileResource;
import de.codesourcery.jasm16.compiler.io.IResource.ResourceType;
import de.codesourcery.jasm16.emulator.ICPU;
import de.codesourcery.jasm16.emulator.memory.IReadOnlyMemory;
import de.codesourcery.jasm16.emulator.memory.MemUtils;
import de.codesourcery.jasm16.emulator.memory.MemoryIterator;
import de.codesourcery.jasm16.utils.IMemoryIterator;
import de.codesourcery.jasm16.utils.Misc;
/**
* Crude DCPU-16 disassembler.
*
* @author tobias.gierke@code-sourcery.de
*/
public class Disassembler
{
public static final int DISASSEMBLE_EVERYTHING = -1;
public static void main(String[] args) throws IOException {
if ( ArrayUtils.isEmpty( args ) ) {
System.err.println("ERROR: No input file");
System.exit(1);
}
if ( args.length != 1 ) {
System.err.println("ERROR: Bad command-line, expected exactly one input file.");
System.exit(1);
}
final byte[] data = Misc.readBytes( new FileResource( new File( args[0] ) , ResourceType.OBJECT_FILE ) );
System.out.println("Input file has "+data.length+" ("+Misc.toHexString( data.length)+") bytes.");
for ( DisassembledLine line : new Disassembler().disassemble( data , true ) ) {
System.out.println( Misc.toHexString( line.getAddress() )+": "+line.getContents() );
}
}
public List<DisassembledLine> disassemble(final Address startingAddress, final byte[] data, final int instructionCountToDisassemble,boolean printHexDump)
{
final IReadOnlyMemory mem = new ByteArrayMemoryAdapter( data );
return disassemble( mem , startingAddress , instructionCountToDisassemble , printHexDump );
}
public List<DisassembledLine> disassemble(final byte[] data,boolean printHexDump)
{
final IReadOnlyMemory mem = new ByteArrayMemoryAdapter( data );
return disassemble( mem , Address.ZERO , DISASSEMBLE_EVERYTHING, printHexDump );
}
public List<DisassembledLine> disassemble(final IReadOnlyMemory memory,
final Address startingAddress,
final int instructionCountToDisassemble,boolean printHexDump)
{
final int[] instructionsLeft = { instructionCountToDisassemble };
final IMemoryIterator iterator = new MemoryIterator(memory,startingAddress,true);
final List<DisassembledLine> lines = new ArrayList<DisassembledLine>();
while( ( instructionCountToDisassemble == DISASSEMBLE_EVERYTHING || instructionsLeft[0] > 0 ) && iterator.hasNext() )
{
final Address current = iterator.currentAddress();
String contents = dissassembleInstruction( iterator );
final Size instructionLength =
Address.calcDistanceInBytes( current , iterator.currentAddress() );
if ( printHexDump )
{
byte[] data = MemUtils.getBytes( memory , current , instructionLength , true );
final String hex = " ; "+Misc.toHexDumpWithoutAddresses( Address.byteAddress(0) , data , data.length , 8 ).replaceAll("\n","");
contents = contents + hex;
}
lines.add( new DisassembledLine( current , contents , instructionLength ) );
instructionsLeft[0]--;
}
return lines;
}
private String dissassembleInstruction(IMemoryIterator iterator)
{
final int instructionWord = iterator.nextWord();
final int basicOpCode = (instructionWord & 0x1f); // 1+2+4+8+16
switch( basicOpCode ) {
case 0x00:
return handleSpecialOpCode( iterator , instructionWord );
case 0x01:
return handleSET( iterator , instructionWord );
case 0x02:
return handleADD( iterator , instructionWord );
case 0x03:
return handleSUB( iterator , instructionWord );
case 0x04:
return handleMUL( iterator , instructionWord );
case 0x05:
return handleMLI( iterator , instructionWord );
case 0x06:
return handleDIV( iterator , instructionWord );
case 0x07:
return handleDVI( iterator , instructionWord );
case 0x08:
return handleMOD( iterator , instructionWord );
case 0x09:
return handleMDI( iterator , instructionWord );
case 0x0a:
return handleAND( iterator , instructionWord );
case 0x0b:
return handleBOR( iterator , instructionWord );
case 0x0c:
return handleXOR( iterator , instructionWord );
case 0x0d:
return handleSHR( iterator , instructionWord );
case 0x0e:
return handleASR( iterator , instructionWord );
case 0x0f:
return handleSHL( iterator , instructionWord );
case 0x10:
return handleIFB( iterator , instructionWord );
case 0x11:
return handleIFC( iterator , instructionWord );
case 0x12:
return handleIFE( iterator , instructionWord );
case 0x13:
return handleIFN( iterator , instructionWord );
case 0x14:
return handleIFG( iterator , instructionWord );
case 0x15:
return handleIFA( iterator , instructionWord );
case 0x16:
return handleIFL( iterator , instructionWord );
case 0x17:
return handleIFU( iterator , instructionWord );
case 0x18:
case 0x19:
return handleUnknownOpCode( iterator , instructionWord );
case 0x1a:
return handleADX( iterator , instructionWord );
case 0x1b:
return handleSBX( iterator , instructionWord );
case 0x1c:
case 0x1d:
return handleUnknownOpCode( iterator , instructionWord );
case 0x1e:
return handleSTI( iterator , instructionWord );
case 0x1f:
return handleSTD( iterator , instructionWord );
default:
return handleUnknownOpCode( iterator , instructionWord );
}
}
private String handleSTD(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "STD" , it , instructionWord );
}
private String disassembleBasicInstruction(String opCode,IMemoryIterator it , int instructionWord)
{
final String src = disassembleOperand( it , OperandPosition.SOURCE_OPERAND , instructionWord );
final String target = disassembleOperand( it , OperandPosition.TARGET_OPERAND , instructionWord );
return opCode+" "+target+" , "+src;
}
private String disassembleSpecialInstruction(String opCode,IMemoryIterator it , int instructionWord) {
return opCode+" "+disassembleOperand( it , OperandPosition.SOURCE_OPERAND , instructionWord );
}
private String handleSTI(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "STI" , it , instructionWord );
}
private String handleSBX(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "SBX" , it , instructionWord );
}
private String handleADX(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "ADX" , it , instructionWord );
}
private String handleUnknownOpCode(IMemoryIterator it , int instructionWord)
{
return "< unknown opcode: "+Misc.toBinaryString( instructionWord, 16 )+" >";
}
private String handleIFU(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFU" , it , instructionWord );
}
private String handleIFL(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFL" , it , instructionWord );
}
private String handleIFA(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFA" , it , instructionWord );
}
private String handleIFG(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFG" , it , instructionWord );
}
private String handleIFN(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFN" , it , instructionWord );
}
private String handleIFE(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFE" , it , instructionWord );
}
private String handleIFC(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFC" , it , instructionWord );
}
private String handleIFB(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "IFB" , it , instructionWord );
}
private String handleSHL(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "SHL" , it , instructionWord );
}
private String handleASR(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "ASR" , it , instructionWord );
}
private String handleSHR(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "SHR" , it , instructionWord );
}
private String handleXOR(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "XOR" , it , instructionWord );
}
private String handleBOR(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "BOR" , it , instructionWord );
}
private String handleAND(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "AND" , it , instructionWord );
}
private String handleMDI(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "MDI" , it , instructionWord );
}
private String handleMOD(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "MOD" , it , instructionWord );
}
private String handleDVI(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "DVI" , it , instructionWord );
}
private String handleDIV(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "DIV" , it , instructionWord );
}
private String handleMLI(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "MLI" , it , instructionWord );
}
private String handleMUL(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "MUL" , it , instructionWord );
}
private String handleSUB(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "SUB" , it , instructionWord );
}
private String handleADD(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "ADD" , it , instructionWord );
}
private String handleSET(IMemoryIterator it , int instructionWord)
{
return disassembleBasicInstruction( "SET" , it , instructionWord );
}
private String handleSpecialOpCode(IMemoryIterator it, int instructionWord)
{
final int opCode = ( instructionWord >> 5 ) &0x1f;
switch( opCode ) {
case 0x00:
return handleUnknownOpCode( it , instructionWord );
case 0x01:
return handleJSR( it , instructionWord );
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
return handleUnknownOpCode( it, instructionWord );
case 0x07:
return handleHCF( it , instructionWord );
case 0x08:
return handleINT( it , instructionWord );
case 0x09:
return handleIAG( it , instructionWord );
case 0x0a:
return handleIAS( it , instructionWord );
case 0x0b:
return handleRFI( it , instructionWord );
case 0x0c:
return handleIAQ( it , instructionWord );
case 0x0d:
case 0x0e:
case 0x0f:
return handleUnknownOpCode( it , instructionWord );
case 0x10:
return handleHWN( it , instructionWord );
case 0x11:
return handleHWQ( it , instructionWord );
case 0x12:
return handleHWI( it , instructionWord );
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
default:
return handleUnknownOpCode( it , instructionWord );
}
}
private String handleHWI(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("HWI",it,instructionWord);
}
private String handleHWQ(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("HWQ",it,instructionWord);
}
private String handleHWN(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("HWN",it,instructionWord);
}
private String handleIAQ(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("IAQ",it,instructionWord);
}
private String handleRFI(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("RFI",it,instructionWord);
}
private String handleIAS(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("IAS",it,instructionWord);
}
private String handleIAG(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("IAG",it,instructionWord);
}
private String handleINT(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("INT",it,instructionWord);
}
private String handleHCF(IMemoryIterator it, int instructionWord)
{
return disassembleSpecialInstruction("HCF",it,instructionWord);
}
private String handleJSR(IMemoryIterator it , int instructionWord)
{
return disassembleSpecialInstruction("JSR",it,instructionWord);
}
protected String disassembleOperand(IMemoryIterator it , OperandPosition position , int instructionWord) {
final boolean specialInstruction = (instructionWord & 0x1f) == 0;
/*
* SET b,a
*
* b = TARGET operand
* a = SOURCE operand
*
* SOURCE is always handled by the processor BEFORE TARGET, and is the lower five bits.
* In bits (in LSB-0 format), a basic instruction has the format:
*
* aaaaaabbbbbooooo
*
* SPECIAL opcodes always have their lower five bits unset, have one value and a
* five bit opcode. In binary, they have the format:
*
* aaaaaaooooo00000
*
* The value (a) is in the same six bit format as defined earlier.
*
* --- Values: (5/6 bits) ---------------------------------------------------------
*
* | C | VALUE | DESCRIPTION
* +---+-----------+----------------------------------------------------------------
* | 0 | 0x00-0x07 | register (A, B, C, X, Y, Z, I or J, in that order)
* | 0 | 0x08-0x0f | [register]
* | 1 | 0x10-0x17 | [register + next word]
* | 0 | 0x18 | (PUSH / [--SP]) if in TARGET, or (POP / [SP++]) if in SOURCE
* | 0 | 0x19 | [SP] / PEEK
* | 1 | 0x1a | [SP + next word] / PICK n
* | 0 | 0x1b | SP
* | 0 | 0x1c | PC
* | 0 | 0x1d | EX
* | 1 | 0x1e | [next word]
* | 1 | 0x1f | next word (literal)
* | 0 | 0x20-0x3f | literal value 0xffff-0x1e (-1..30) (literal) (only for SOURCE)
* +---+-----------+----------------------------------------------------------------
*/
final int operandBits;
if ( specialInstruction || position == OperandPosition.SOURCE_OPERAND ) {
// aaaaaa bbbbb ooooo
// System.out.println("Instruction: "+Misc.toHexString( instructionWord )+" => "+Misc.toBinaryString( instructionWord , 16 , 4, 9 )+" ( special: "+specialInstruction+")");
operandBits = (instructionWord >> 10) & ( 1+2+4+8+16+32 ); // 6 operand bit
} else {
// aaaaaa bbbbb ooooo
// System.out.println("Instruction: "+Misc.toHexString( instructionWord )+" => "+Misc.toBinaryString( instructionWord , 16 , 4, 9 )+" ( special: "+specialInstruction+")");
operandBits = (instructionWord >> 5) & ( 1+2+4+8+16); // 5 operand bits
}
if ( operandBits < 0 ) {
throw new RuntimeException("Internal error");
}
if ( operandBits <= 0x07 ) {
return ICPU.COMMON_REGISTER_NAMES[ operandBits ];
}
if ( operandBits <= 0x0f ) {
return "["+ICPU.COMMON_REGISTER_NAMES[ ( operandBits - 0x08 ) ]+"]";
}
if ( operandBits <= 0x17 )
{
if ( ! it.hasNext() ) {
return "operand word missing";
}
final int nextWord = it.nextWord();
return "["+ICPU.COMMON_REGISTER_NAMES[ operandBits - 0x10 ]+" + 0x"+Misc.toHexString( nextWord)+"]";
}
switch( operandBits ) {
case 0x18:
return "[SP++]";
case 0x19:
return "[SP]";
case 0x1a:
if ( ! it.hasNext() ) {
return "operand word missing";
}
int nextWord = it.nextWord();
return "[SP + 0x"+Misc.toHexString( nextWord)+"]";
case 0x1b:
return "SP";
case 0x1c:
return "PC";
case 0x1d:
return "EX";
case 0x1e:
if ( ! it.hasNext() ) {
return "operand word missing";
}
nextWord = it.nextWord();
return "[ 0x"+Misc.toHexString( nextWord )+" ]";
case 0x1f:
if ( ! it.hasNext() ) {
return "operand word missing";
}
nextWord = it.nextWord();
return "0x"+Misc.toHexString( nextWord );
}
if ( position == OperandPosition.SOURCE_OPERAND || specialInstruction ) {
final int literalValue = operandBits - 0x21;
return "0x"+Misc.toHexString( literalValue );
}
return "<illegal operand bits: "+Misc.toBinaryString( operandBits , 5 )+">";
}
}