yagsdisasm/Disassembler/Disassembler_65816.php
Christopher Shepherd 0891665c81 initial commit
2016-05-25 23:34:19 -04:00

604 lines
18 KiB
PHP

<?php
/*
* Disassembler_65816 - A 65816 disassembler, of course
* loosely references Andy McFadden's Ciderpress disassembler work at
* https://github.com/fadden/ciderpress/blob/master/reformat/DisasmTable.cpp
*/
class Disassembler_65816
{
/*
* initialization here
*/
public function __construct()
{
$this->opcodes = array(
0x00 => array( 'BRK', 'StackInt' ),
0x01 => array( 'ORA', 'DPIndexXInd' ),
0x02 => array( 'COP', 'StackInt' ),
0x03 => array( 'ORA', 'StackRel' ),
0x04 => array( 'TSB', 'DP' ),
0x05 => array( 'ORA', 'DP' ),
0x06 => array( 'ASL', 'DP' ),
0x07 => array( 'ORA', 'DPIndLong' ),
0x08 => array( 'PHP', 'StackPush' ),
0x09 => array( 'ORA', 'Imm' ),
0x0A => array( 'ASL', 'Acc' ),
0x0B => array( 'PHD', 'StackPush' ),
0x0C => array( 'TSB', 'Abs' ),
0x0D => array( 'ORA', 'Abs' ),
0x0E => array( 'ASL', 'Abs' ),
0x0F => array( 'ORA', 'AbsLong' ),
0x10 => array( 'BPL', 'PCRel' ),
0x11 => array( 'ORA', 'DPIndIndexY' ),
0x12 => array( 'ORA', 'DPInd' ),
0x13 => array( 'ORA', 'StackRelIndexY' ),
0x14 => array( 'TRB', 'DP' ),
0x15 => array( 'ORA', 'DPIndexX' ),
0x16 => array( 'ASL', 'DPIndexX' ),
0x17 => array( 'ORA', 'DPIndexYLong' ),
0x18 => array( 'CLC', 'Implied' ),
0x19 => array( 'ORA', 'AbsIndexY' ),
0x1A => array( 'INC', 'Acc' ),
0x1B => array( 'TCS', 'Implied' ),
0x1C => array( 'TRB', 'Abs' ),
0x1D => array( 'ORA', 'AbsIndexX' ),
0x1E => array( 'ASL', 'AbsIndexX' ),
0x1F => array( 'ORA', 'AbsIndexXLong' ),
0x20 => array( 'JSR', 'Abs' ),
0x21 => array( 'AND', 'DPIndexXind' ),
0x22 => array( 'JSL', 'AbsLong' ),
0x23 => array( 'AND', 'StackRel' ),
0x24 => array( 'BIT', 'DP' ),
0x25 => array( 'AND', 'DP' ),
0x26 => array( 'ROL', 'DP' ),
0x27 => array( 'AND', 'DPIndLong' ),
0x28 => array( 'PLP', 'StackPull' ),
0x29 => array( 'AND', 'Imm' ),
0x2A => array( 'ROL', 'Acc' ),
0x2B => array( 'PLD', 'StackPull' ),
0x2C => array( 'BIT', 'Abs' ),
0x2D => array( 'AND', 'Abs' ),
0x2E => array( 'ROL', 'Abs' ),
0x2F => array( 'AND', 'AbsLong' ),
0x30 => array( 'BMI', 'PCRel' ),
0x31 => array( 'AND', 'DPIndIndexY' ),
0x32 => array( 'AND', 'DPInd' ),
0x33 => array( 'AND', 'StackRelIndexY' ),
0x34 => array( 'BIT', 'DPIndexX' ),
0x35 => array( 'AND', 'DPIndexX' ),
0x36 => array( 'ROL', 'DPIndexX' ),
0x37 => array( 'AND', 'DPIndIndexYLong' ),
0x38 => array( 'SEC', 'Implied' ),
0x39 => array( 'AND', 'AbsIndexY' ),
0x3A => array( 'DEC', 'Acc' ),
0x3B => array( 'TSC', 'Implied' ),
0x3C => array( 'BIT', 'AbsIndexX' ),
0x3D => array( 'AND', 'AbsIndexX' ),
0x3E => array( 'ROL', 'AbsIndexX' ),
0x3F => array( 'AND', 'AbsIndexXLong' ),
0x40 => array( 'RTI', 'StackRTI' ),
0x41 => array( 'EOR', 'DPIndexXind' ),
0x42 => array( 'WDM', 'WDM' ),
0x43 => array( 'EOR', 'StackRel' ),
0x44 => array( 'MVP', 'BlockMove' ),
0x45 => array( 'EOR', 'DP' ),
0x46 => array( 'LSR', 'DP' ),
0x47 => array( 'EOR', 'DPIndLong' ),
0x48 => array( 'PHA', 'StackPush' ),
0x49 => array( 'EOR', 'Imm' ),
0x4A => array( 'LSR', 'Acc' ),
0x4B => array( 'PHK', 'StackPush' ),
0x4C => array( 'JMP', 'Abs' ),
0x4D => array( 'EOR', 'Abs' ),
0x4E => array( 'LSR', 'Abs' ),
0x4F => array( 'EOR', 'AbsLong' ),
0x50 => array( 'BVC', 'PCRel' ),
0x51 => array( 'EOR', 'DPIndIndexY' ),
0x52 => array( 'EOR', 'DPInd' ),
0x53 => array( 'EOR', 'StackRelIndexY' ),
0x54 => array( 'MVN', 'BlockMove' ),
0x55 => array( 'EOR', 'DPIndexX' ),
0x56 => array( 'LSR', 'DPIndexX' ),
0x57 => array( 'EOR', 'DPIndIndexYLong' ),
0x58 => array( 'CLI', 'Implied' ),
0x59 => array( 'EOR', 'AbsIndexY' ),
0x5A => array( 'PHY', 'StackPush' ),
0x5B => array( 'TCD', 'Implied' ),
0x5C => array( 'JMP', 'AbsLong' ),
0x5D => array( 'EOR', 'AbsIndexX' ),
0x5E => array( 'LSR', 'AbsIndexX' ),
0x5F => array( 'EOR', 'AbsIndexXLong' ),
0x60 => array( 'RTS', 'StackRTS' ),
0x61 => array( 'ADC', 'DPIndexXInd' ),
0x62 => array( 'PER', 'StackPCRel' ),
0x63 => array( 'ADC', 'StackRel' ),
0x64 => array( 'STZ', 'DP' ),
0x65 => array( 'ADC', 'DP' ),
0x66 => array( 'ROR', 'DP' ),
0x67 => array( 'ADC', 'DPIndLong' ),
0x68 => array( 'PLA', 'StackPull' ),
0x69 => array( 'ADC', 'Imm' ),
0x6A => array( 'ROR', 'Acc' ),
0x6B => array( 'RTL', 'StackRTL' ),
0x6C => array( 'JMP', 'AbsInd' ),
0x6D => array( 'ADC', 'Abs' ),
0x6E => array( 'ROR', 'Abs' ),
0x6F => array( 'ADC', 'AbsLong' ),
0x70 => array( 'BVS', 'PCRel' ),
0x71 => array( 'ADC', 'DPIndIndexY' ),
0x72 => array( 'ADC', 'DPInd' ),
0x73 => array( 'ADC', 'StackRelIndexY' ),
0x74 => array( 'STZ', 'DPIndexX' ),
0x75 => array( 'ADC', 'DPIndexX' ),
0x76 => array( 'ROR', 'DPIndexX' ),
0x77 => array( 'ADC', 'DPIndIndexYLong' ),
0x78 => array( 'SEI', 'Implied' ),
0x79 => array( 'ADC', 'AbsIndexY' ),
0x7A => array( 'PLY', 'StackPull' ),
0x7B => array( 'TDC', 'Implied' ),
0x7C => array( 'JMP', 'AbsIndexXInd' ),
0x7D => array( 'ADC', 'AbsIndexX' ),
0x7E => array( 'ROR', 'AbsIndexX' ),
0x7F => array( 'ADC', 'AbsIndexXLong' ),
0x80 => array( 'BRA', 'PCRel' ),
0x81 => array( 'STA', 'DPIndexXind' ),
0x82 => array( 'BRL', 'PCRelLong' ),
0x83 => array( 'STA', 'StackRel' ),
0x84 => array( 'STY', 'DP' ),
0x85 => array( 'STA', 'DP' ),
0x86 => array( 'STX', 'DP' ),
0x87 => array( 'STA', 'DPIndLong' ),
0x88 => array( 'DEY', 'Implied' ),
0x89 => array( 'BIT', 'Imm' ),
0x8A => array( 'TXA', 'Implied' ),
0x8B => array( 'PHB', 'StackPush' ),
0x8C => array( 'STY', 'Abs' ),
0x8D => array( 'STA', 'Abs' ),
0x8E => array( 'STX', 'Abs' ),
0x8F => array( 'STA', 'AbsLong' ),
0x90 => array( 'BCC', 'PCRel' ),
0x91 => array( 'STA', 'DPIndIndexY' ),
0x92 => array( 'STA', 'DPInd' ),
0x93 => array( 'STA', 'StackRelIndexY' ),
0x94 => array( 'STY', 'DPIndexX' ),
0x95 => array( 'STA', 'DPIndexX' ),
0x96 => array( 'STX', 'DPIndexY' ),
0x97 => array( 'STA', 'DPIndIndexYLong' ),
0x98 => array( 'TYA', 'Implied' ),
0x99 => array( 'STA', 'AbsIndexY' ),
0x9A => array( 'TXS', 'Implied' ),
0x9B => array( 'TXY', 'Implied' ),
0x9C => array( 'STZ', 'Abs' ),
0x9D => array( 'STA', 'AbsIndexX' ),
0x9E => array( 'STZ', 'AbsIndexX' ),
0x9F => array( 'STA', 'AbsIndexXLong' ),
0xA0 => array( 'LDY', 'Imm' ),
0xA1 => array( 'LDA', 'DPIndexXInd' ),
0xA2 => array( 'LDX', 'Imm' ),
0xA3 => array( 'LDA', 'StackRel' ),
0xA4 => array( 'LDY', 'DP' ),
0xA5 => array( 'LDA', 'DP' ),
0xA6 => array( 'LDX', 'DP' ),
0xA7 => array( 'LDA', 'DPIndLong' ),
0xA8 => array( 'TAY', 'Implied' ),
0xA9 => array( 'LDA', 'Imm' ),
0xAA => array( 'TAX', 'Implied' ),
0xAB => array( 'PLB', 'StackPull' ),
0xAC => array( 'LDY', 'Abs' ),
0xAD => array( 'LDA', 'Abs' ),
0xAE => array( 'LDX', 'Abs' ),
0xAF => array( 'LDA', 'AbsLong' ),
0xB0 => array( 'BCS', 'PCRel' ),
0xB1 => array( 'LDA', 'DPIndIndexY' ),
0xB2 => array( 'LDA', 'DPInd' ),
0xB3 => array( 'LDA', 'StackRelIndexY' ),
0xB4 => array( 'LDY', 'DPIndexX' ),
0xB5 => array( 'LDA', 'DPIndexX' ),
0xB6 => array( 'LDX', 'DPIndexY' ),
0xB7 => array( 'LDA', 'DPIndIndexYLong' ),
0xB8 => array( 'CLV', 'Implied' ),
0xB9 => array( 'LDA', 'AbsIndexY' ),
0xBA => array( 'TSX', 'Implied' ),
0xBB => array( 'TYX', 'Implied' ),
0xBC => array( 'LDY', 'AbsIndexX' ),
0xBD => array( 'LDA', 'AbsIndexX' ),
0xBE => array( 'LDX', 'AbsIndexY' ),
0xBF => array( 'LDA', 'AbsIndexXLong' ),
0xC0 => array( 'CPY', 'Imm' ),
0xC1 => array( 'CMP', 'DPIndexXInd' ),
0xC2 => array( 'REP', 'Imm' ),
0xC3 => array( 'CMP', 'StackRel' ),
0xC4 => array( 'CPY', 'DP' ),
0xC5 => array( 'CMP', 'DP' ),
0xC6 => array( 'DEC', 'DP' ),
0xC7 => array( 'CMP', 'DPIndLong' ),
0xC8 => array( 'INY', 'Implied' ),
0xC9 => array( 'CMP', 'Imm' ),
0xCA => array( 'DEX', 'Implied' ),
0xCB => array( 'WAI', 'Implied' ),
0xCC => array( 'CPY', 'Abs' ),
0xCD => array( 'CMP', 'Abs' ),
0xCE => array( 'DEC', 'Abs' ),
0xCF => array( 'CMP', 'AbsLong' ),
0xD0 => array( 'BNE', 'PCRel' ),
0xD1 => array( 'CMP', 'DPIndIndexY' ),
0xD2 => array( 'CMP', 'DPInd' ),
0xD3 => array( 'CMP', 'StackRelIndexY' ),
0xD4 => array( 'PEI', 'StackDPInd' ),
0xD5 => array( 'CMP', 'DPIndexX' ),
0xD6 => array( 'DEC', 'DPIndexX' ),
0xD7 => array( 'CMP', 'DPIndIndexYLong' ),
0xD8 => array( 'CLD', 'Implied' ),
0xD9 => array( 'CMP', 'AbsIndexY' ),
0xDA => array( 'PHX', 'StackPush' ),
0xDB => array( 'STP', 'Implied' ),
0xDC => array( 'JML', 'AbsIndLong' ),
0xDD => array( 'CMP', 'AbsIndexX' ),
0xDE => array( 'DEC', 'AbsIndexX' ),
0xDF => array( 'CMP', 'AbsIndexXLong' ),
0xE0 => array( 'CPX', 'Imm' ),
0xE1 => array( 'SBC', 'DPIndexXInd' ),
0xE2 => array( 'SEP', 'Imm' ),
0xE3 => array( 'SBC', 'StackRel' ),
0xE4 => array( 'CPX', 'DP' ),
0xE5 => array( 'SBC', 'DP' ),
0xE6 => array( 'INC', 'DP' ),
0xE7 => array( 'SBC', 'DPIndLong' ),
0xE8 => array( 'INX', 'Implied' ),
0xE9 => array( 'SBC', 'Imm' ),
0xEA => array( 'NOP', 'Implied' ),
0xEB => array( 'XBA', 'Implied' ),
0xEC => array( 'CPX', 'Abs' ),
0xED => array( 'SBC', 'Abs' ),
0xEE => array( 'INC', 'Abs' ),
0xEF => array( 'SBC', 'AbsLong' ),
0xF0 => array( 'BEQ', 'PCRel' ),
0xF1 => array( 'SBC', 'DPIndIndexY' ),
0xF2 => array( 'SBC', 'DPInd' ),
0xF3 => array( 'SBC', 'StackRelIndexY' ),
0xF4 => array( 'PEA', 'StackAbs' ),
0xF5 => array( 'SBC', 'DPIndexX' ),
0xF6 => array( 'INC', 'DPIndexX' ),
0xF7 => array( 'SBC', 'DPIndIndexYLong' ),
0xF8 => array( 'SED', 'Implied' ),
0xF9 => array( 'SBC', 'AbsIndexY' ),
0xFA => array( 'PLX', 'StackPull' ),
0xFB => array( 'XCE', 'Implied' ),
0xFC => array( 'JSR', 'AbsIndexXInd' ),
0xFD => array( 'SBC', 'AbsIndexX' ),
0xFE => array( 'INC', 'AbsIndexX' ),
0xFF => array( 'SBC', 'AbsIndexXLong' ),
);
}
/**
* rel_offset( $pc, $offset ) - compute absolute offset vs program counter for a branch offset
*
* @param $pc int, $offset int
*/
private function rel_offset( $pc, $offset, $offset2 )
{
if( $offset > 127 )
{
$result = $pc - (256-$offset);
} else {
$result = $pc + $offset;
}
$result += $offset2+1;
return $result;
}
/**
* rel_long_offset( $pc, $offset ) - compute absolute offset vs program counter for a branch offset
*
* @param $pc int, $offset int
*/
private function rel_long_offset( $pc, $offset, $offset2 )
{
if( $offset > 32767 )
{
$result = $pc - (65536-$offset);
} else {
$result = $pc + $offset;
}
$result += $offset2+1;
return $result;
}
/**
* print_instruction( $opcode_txt, $opcode, $mode, $arg_bytes, $pc ) - Output text-friendly disassembled output for one instruction
*
* @param $opcode_txt str, $opcode int, $mode str, $arg_bytes str, $pc int
*/
public function print_instruction( $opcode_txt, $opcode, $mode, $arg_bytes, $pc )
{
// first print the program counter and hex bytes
switch( strlen( $arg_bytes ))
{
case 0:
$output = sprintf( '%06X- %02X ', $pc, $opcode );
break;
case 1:
$output = sprintf( '%06X- %02X %02X ', $pc, $opcode, ord($arg_bytes[0]) );
break;
case 2:
$output = sprintf( '%06X- %02X %02X %02X ', $pc, $opcode, ord($arg_bytes[0]), ord($arg_bytes[1]) );
break;
case 3:
$output = sprintf( '%06X- %02X %02X %02X %02X ', $pc, $opcode, ord($arg_bytes[0]), ord($arg_bytes[1]), ord($arg_bytes[2]) );
break;
}
// now the opcode
$output .= $opcode_txt;
// now the args depending on mode
switch( $mode )
{
case 'Implied':
case 'StackPull':
case 'StackPush':
case 'StackRTI':
case 'StackRTS':
case 'Acc':
break;
case 'DP':
$output .= sprintf( ' %02X', ord($arg_bytes[0] ));
break;
case 'DPIndexX':
$output .= sprintf( ' %02X,X', ord($arg_bytes[0] ));
break;
case 'DPIndexY':
$output .= sprintf( ' %02X,Y', ord($arg_bytes[0] ));
break;
case 'DPIndexXInd':
$output .= sprintf( ' (%02X,X)', ord($arg_bytes[0] ));
break;
case 'DPInd':
$output .= sprintf( ' (%02X)', ord($arg_bytes[0] ));
break;
case 'DPIndIndexY':
$output .= sprintf( ' (%02X),Y', ord($arg_bytes[0] ));
break;
case 'Imm':
if( strlen( $arg_bytes ) == 2 )
{
$output .= sprintf( ' #%02X%02X', ord($arg_bytes[1]), ord($arg_bytes[0]));
} else {
$output .= sprintf( ' #%02X', ord($arg_bytes[0] ));
}
break;
case 'PCRel':
if( ord($arg_bytes[0]) > 127)
{
$output .= sprintf( ' %04X {-%02X}', $this->rel_offset( $pc, ord($arg_bytes[0]),strlen($arg_bytes)), 256-ord($arg_bytes[0]));
} else {
$output .= sprintf( ' %04X {+%02X}', $this->rel_offset( $pc, ord($arg_bytes[0]),strlen($arg_bytes)), ord($arg_bytes[0]));
}
break;
case 'Abs':
$output .= sprintf( ' %02X%02X', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'AbsIndexX':
$output .= sprintf( ' %02X%02X,X', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'AbsIndexY':
$output .= sprintf( ' %02X%02X,Y', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'AbsIndexXInd':
$output .= sprintf( ' (%02X%02X,X)', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'AbsInd':
$output .= sprintf( ' (%02X%02X)', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'WDM':
case 'StackInt':
if( strlen( $arg_bytes ) > 0 )
{
$output .= sprintf( ' %02X', ord($arg_bytes[0] ));
}
break;
case 'AbsIndLong':
$output .= sprintf( ' (%02X%02X)', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'AbsLong':
$output .= sprintf( ' %02X%02X%02X', ord($arg_bytes[2]), ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'AbsIndexXLong':
$output .= sprintf( ' %02X%02X%02X,X', ord($arg_bytes[2]), ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'BlockMove':
$output .= sprintf( ' %02X,%02X', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'DPIndLong':
$output .= sprintf( ' [%02X]', ord($arg_bytes[0] ));
break;
case 'DPIndIndexYLong':
$output .= sprintf( ' [%02X],Y', ord($arg_bytes[0] ));
break;
case 'StackPCRel':
case 'PCRelLong':
$offset = (ord($arg_bytes[1])*256)+ord($arg_bytes[0]);
if( $offset > 32767 )
{
$output .= sprintf( ' %04X {-%02X}', $this->rel_long_offset( $pc, $offset, strlen($arg_bytes)), 65536-$offset);
} else {
$output .= sprintf( ' %04X {+%02X}', $this->rel_long_offset( $pc, $offset, strlen($arg_bytes)), $offset);
}
break;
case 'StackAbs':
$output .= sprintf( ' %02X%02X', ord($arg_bytes[1]), ord($arg_bytes[0]));
break;
case 'StackDPInd':
$output .= sprintf( ' %02X', ord($arg_bytes[0]));
break;
case 'StackRel':
$output .= sprintf( ' %02X,S', ord($arg_bytes[0]));
break;
case 'StackRelIndexY':
$output .= sprintf( ' (%02X,S),Y', ord($arg_bytes[0]));
break;
}
return $output;
}
/**
* disassemble( $m_flag, $x_flag, $e_flag, $origin, $data, $personality ) - Disassemble some data
*
* @param $m_flag bool, $x_flag bool, $e_flag bool, $origin int, $data str, $personality obj
* @return mixed
*/
public function disassemble( $m_flag, $x_flag, $e_flag, $origin, $data, $personality )
{
$pc = $origin;
$data_index = 0;
$last_output = '';
echo " ORG ".sprintf('%06X',$origin)."\n";
if( $m_flag > 0)
{
$print_m = '1';
} else {
$print_m = '0';
}
if( $x_flag > 0 )
{
$print_x = '1';
} else {
$print_x = '0';
}
echo " MX %{$print_m}{$print_x}\n\n";
while( $data_index < strlen( $data ))
{
$opcode = $data[$data_index];
$opcode_txt = $this->opcodes[ord($opcode)][0];
$mode = $this->opcodes[ord($opcode)][1];
// get opcode width
$width = 0;
switch( $mode )
{
case 'Acc':
case 'Implied':
case 'StackPull':
case 'StackPush':
case 'StackRTI':
case 'StackRTL':
case 'StackRTS':
$width = 1;
break;
case 'DP':
case 'DPIndexX':
case 'DPIndexY':
case 'DPIndexXInd':
case 'DPInd':
case 'DPIndLong':
case 'DPIndIndexY':
case 'DPIndIndexYLong':
case 'PCRel':
case 'StackRel':
case 'StackRelIndexY':
case 'StackDPInd':
$width = 2;
break;
case 'Abs':
case 'AbsIndexX':
case 'AbsIndexY':
case 'AbsIndexXInd':
case 'AbsInd':
case 'AbsIndLong':
case 'BlockMove':
case 'PCRelLong':
case 'StackAbs':
case 'StackPCRel':
$width = 3;
break;
case 'AbsLong':
case 'AbsIndexXLong':
$width = 4;
break;
case 'StackInt':
if( $e_flag )
{
$width = 1;
} else {
$width = 2;
}
break;
case 'Imm':
$width = 2;
if( $e_flag == 0 )
{
if( in_array( $opcode_txt, array( 'CPX', 'CPY', 'LDX', 'LDY' )))
{
if( $x_flag == 0 )
{
$width = 3;
}
} elseif( in_array( $opcode_txt, array( 'ADC', 'AND', 'BIT', 'CMP', 'EOR', 'LDA', 'ORA', 'SBC' )))
{
if( $m_flag == 0 )
{
$width = 3;
}
}
}
break;
case 'WDM':
$width = 2;
break;
default:
$width = 1;
break;
} // switch
// fetch arguments if any
$arg_bytes = '';
for( $ind=1;$ind<$width;$ind++ )
{
$arg_bytes .= $data[$data_index + $ind];
}
$output = $this->print_instruction( $opcode_txt, ord($opcode), $mode, $arg_bytes, $pc );
// Personality support
$this_output = substr( $output, 22 );
$comment = $personality->check( $last_output, $this_output );
if( $comment )
{
for($pad=strlen($output);$pad<42;$pad++)
{
$output .= ' ';
}
$output .= '; '.$comment;
}
$last_output = $this_output;
echo "{$output}\n";
$pc += $width;
$data_index += $width;
} // for
}
}