initial commit

This commit is contained in:
Christopher Shepherd 2016-05-25 23:34:19 -04:00
commit 0891665c81
9 changed files with 3831 additions and 0 deletions

View File

@ -0,0 +1,603 @@
<?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
}
}

233
Input/Input_2MG.php Normal file
View File

@ -0,0 +1,233 @@
<?php
/**
* Input_2MG - YAGSDisasm Input block device for .2MG image files
* from http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt
*
*/
class Input_2MG
{
// have we been initialized yet?
private $is_initialized = false;
// source filename
private $source_filename;
// creator_id
private $creator_id;
// 2mg comment
private $comment = '';
// number of 512-byte blocks in image
private $total_blocks;
// header length
private $header_length;
// version number
private $version_number;
// image format
private $image_format;
// disk is locked?
private $is_locked=false;
// offset to image data
private $image_offset;
// DOS 3.3 Volume Number
private $volume_number;
// volume length in bytes
private $total_bytes;
// offset to comment
private $comment_offset;
// length of comment
private $comment_length;
// 2MG Creator data offset
private $creator_data_offset;
// 2MG Creator data length
private $creator_data_length;
// our file descriptor
private $fp;
public function __construct( $filename )
{
$fp = fopen( $filename, 'r' );
if( !$fp )
{
throw new Exception( 'Input_2MG: Unable to open source file '.$filename );
}
$this->source_filename = $filename;
// check for 2MG magic
$magic = fread( $fp, 4 );
if( $magic != '2IMG' )
{
throw new Exception( 'Input_2MG: source file not a 2MG file' );
}
// save creator id
$this->creator_id = fread( $fp, 4 );
// get header length (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$this->header_length = (ord( $byte2 )*256) + ord( $byte1 );
// get version number (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$this->version_number = (ord( $byte2 )*256) + ord( $byte1 );
// get image format (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$image_format_id = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
$known_format_ids = array(
0 => 'DOS 3.3',
1 => 'ProDOS',
2 => 'NIB',
);
$this->image_format = $known_format_ids[$image_format_id];
// flags / volume number (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
if( ord( $byte2 ) && 0x01 )
{
$this->volume_number = ord( $byte1 );
} else {
if( $image_format_id == 1 )
{
$this->volume_number = 254;
}
}
if( ord( $byte4 ) && 0x80 )
{
$this->is_locked = true;
} else {
$this->is_locked = false;
}
// block count (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->total_blocks = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// offset to image data (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->image_offset = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// bytes of disk data (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->total_bytes = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// offset to comment (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->comment_offset = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// comment length (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->comment_length = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// creator data offset (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->creator_data_offset = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// creator data length (little-endian)
$byte1 = fread( $fp, 1 );
$byte2 = fread( $fp, 1 );
$byte3 = fread( $fp, 1 );
$byte4 = fread( $fp, 1 );
$this->creator_data_length = (ord( $byte4 )*16777216) + (ord( $byte3 )*65536) + (ord( $byte2 )*256) + ord( $byte1 );
// reserved bytes
$reserved = fread( $fp, 8 );
// get Comment string if present
if( $this->comment_length > 0 )
{
fseek( $fp, $this->comment_offset, SEEK_SET );
$this->comment = fread( $fp, $this->comment_length );
}
$this->is_initialized = true;
$this->fp = $fp;
}
/**
* getBlocks( $starting_block, $num_blocks ) - Retrieve $num_blocks 512-byte blocks from the file, starting at $starting_block
* @param $starting_block int, $num_blocks int
* @return $data str
*/
public function getBlocks( $starting_block, $num_blocks )
{
fseek( $this->fp, $this->image_offset, SEEK_SET );
fseek( $this->fp, $starting_block*512, SEEK_CUR );
$data = '';
for( $blk=0;$blk<$num_blocks;$blk++ )
{
$data .= fread( $this->fp, 512 );
}
return $data;
}
/**
* printInfo() - Print some information about this 2MG image
*/
public function printInfo()
{
echo "Source Filename: ".$this->source_filename."\n";
echo " Creator ID: ".$this->creator_id."\n";
echo " Header Length: ".$this->header_length."\n";
echo " Version Number: ".$this->version_number."\n";
echo " Image Format: ".$this->image_format."\n";
if( $this->image_format == 'DOS 3.3' )
{
echo " Volume Number: ".$this->volume_number."\n";
}
echo " Is Locked? ";
if( $this->is_locked )
{
echo "Yes\n";
} else {
echo "No\n";
}
echo " Block Count: ".$this->total_blocks."\n";
echo " Total Bytes: ".$this->total_bytes."\n";
if( $this->comment != '' )
{
echo " Comment: ".$this->comment."\n";
}
}
}

66
Input/Input_BIN.php Normal file
View File

@ -0,0 +1,66 @@
<?php
/**
* Input_BIN - YAGSDisasm Input byte device for binary blobs
*
*/
class Input_BIN
{
// have we been initialized yet?
private $is_initialized = false;
// source filename
private $source_filename;
// volume length in bytes
private $total_bytes;
// our file descriptor
private $fp;
public function __construct( $filename )
{
$fp = fopen( $filename, 'r' );
if( !$fp )
{
throw new Exception( 'Input_BIN: Unable to open source file '.$filename );
}
// source filename
$this->source_filename = $filename;
// bytes of disk data
$this->total_bytes = filesize( $filename );
$this->is_initialized = true;
$this->fp = $fp;
}
/**
* getBytes( $starting_byte, $num_bytes ) - Retrieve $num_bytes bytes from the file, starting at $starting_byte
* @param $starting_byte int, $num_bytes int
* @return $data str
*/
public function getBytes( $starting_byte, $num_bytes )
{
if( $num_bytes == 'all' )
{
return file_get_contents( $this->source_filename );
}
fseek( $fp, $starting_byte, SEEK_SET );
$data = '';
$data .= fread( $fp, $num_bytes );
return $data;
}
/**
* printInfo() - Print some information about this binary image
*/
public function printInfo()
{
echo "Source Filename: ".$this->source_filename."\n";
echo " Image Format: Binary\n";
echo " Total Bytes: ".$this->total_bytes."\n";
}
}

72
Input/Input_PO.php Normal file
View File

@ -0,0 +1,72 @@
<?php
/**
* Input_PO - YAGSDisasm Input block device for .PO image files
*
*/
class Input_PO
{
// have we been initialized yet?
private $is_initialized = false;
// source filename
private $source_filename;
// number of 512-byte blocks in image
private $total_blocks;
// volume length in bytes
private $total_bytes;
// our file descriptor
private $fp;
public function __construct( $filename )
{
$fp = fopen( $filename, 'r' );
if( !$fp )
{
throw new Exception( 'Input_PO: Unable to open source file '.$filename );
}
// source filename
$this->source_filename = $filename;
// bytes of disk data
$this->total_bytes = filesize( $filename );
// block count (little-endian)
$this->total_blocks = $this->total_bytes / 512;
$this->is_initialized = true;
$this->fp = $fp;
}
/**
* getBlocks( $starting_block, $num_blocks ) - Retrieve $num_blocks 512-byte blocks from the file, starting at $starting_block
* @param $starting_block int, $num_blocks int
* @return $data str
*/
public function getBlocks( $starting_block, $num_blocks )
{
fseek( $fp, $this->image_offset, SEEK_SET );
fseek( $fp, $starting_block*512 );
$data = '';
for( $blk=0;$blk<$num_blocks;$blk++ )
{
$data .= fread( $fp, 512 );
}
return $data;
}
/**
* printInfo() - Print some information about this PO image
*/
public function printInfo()
{
echo "Source Filename: ".$this->source_filename."\n";
echo " Image Format: ProDOS\n";
echo " Block Count: ".$this->total_blocks."\n";
echo " Total Bytes: ".$this->total_bytes."\n";
}
}

25
LICENSE.txt Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2009, CiderPress project authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of FaddenSoft, LLC nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY FaddenSoft, LLC ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL FaddenSoft, LLC BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2516
NList.Data.TXT Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
<?php
/**
* Personality_AppleIIGS - Handle things specific to the Apple IIGS computer (for auto-documentation)
*/
class Personality_AppleIIGS
{
/**
* getlines( $fp, $numlines ) - Parse some lines out of the NiftyList file
*
* @param $fp res, $numlines int
* @return array
*/
private function getlines( $fp, $numlines )
{
$retarr = array();
for($x=0;$x<$numlines;$x++)
{
$line = trim( fgets( $fp ));
$addr = substr( $line, 0, 4 );
$data = substr( $line, 5 );
$retarr[$addr] = $data;
}
return $retarr;
}
public function __construct()
{
$fp = fopen( 'NList.Data.TXT', 'r' );
if( !$fp )
{
throw new Exception( "Personality_AppleIIGS: Couldn't open NiftyList data file" );
}
// parse some stuff out of the NiftyList data file
$throwaway = $this->getlines( $fp, 4 );
$this->nlist_prodos8 = $this->getlines( $fp, 33-5+1 );
$throwaway = $this->getlines( $fp, 1 );
$this->nlist_prodos16 = $this->getlines( $fp, 151-35+1 );
$throwaway = $this->getlines( $fp, 1 );
$this->nlist_toolbox = $this->getlines( $fp, 1544-153+1 );
$throwaway = $this->getlines( $fp, 1 );
$this->nlist_e1xxxx = $this->getlines( $fp, 1620-1546+1 );
$throwaway = $this->getlines( $fp, 1 );
$this->nlist_e0xxxx = $this->getlines( $fp, 1637-1622+1 );
$throwaway = $this->getlines( $fp, 1 );
$this->nlist_ssf8rom = $this->getlines( $fp, 1911-1639+1 );
}
/**
* check( $last_instruction, $this_instruction ) - Check if something comment-worthy has occurred
*
* @param $last_instruction str, $this_isntruction str
* @return mixed
*/
public function check( $last_instruction, $this_instruction )
{
$comment = false;
// softswitches and 8-bit firmware
$addr_part = substr( $this_instruction, -5 );
if( $addr_part[0] == ' ' )
{
if( $this->nlist_ssf8rom[substr($addr_part,1)] )
{
$comment = $this->nlist_ssf8rom[substr($addr_part,1)];
}
}
// toolbox entry
if( $this_instruction == 'JSL E10000' )
{
if( substr( $last_instruction, 0, 3 ) == 'LDX' )
{
$toolcall_number = substr( $last_instruction, -4 );
if( $this->nlist_toolbox[$toolcall_number] )
{
$comment = 'Toolbox: '.$this->nlist_toolbox[$toolcall_number];
}
}
}
return $comment;
}
}

48
README.md Normal file
View File

@ -0,0 +1,48 @@
yagsdisasm - Yet Another GS Disassembler
========================================
yagsdisasm is written in PHP, which may be an unfortunate choice of languages to many, but is nearly-universally available and makes for a product that's easy to modify, when written properly.
the disassembly engine is largely inspired by Andy McFadden's Ciderpress disassembly engine, which is why we copy his license terms in LICENSE.txt
as of 5/25/2016, the project is only a couple hours old and is a proof of concept. future revisions to come later
### Example Run:
$ php yagsdisasm.php -x 0 -m 0 -e 0 -f /Applications/Emulation/Apple\ IIGS/images/WhatIs/Alien\ Mind/Alien\ Mind\ Dk1.2mg -s 1 -n 1 -k 1 -o 801| more
ORG 000801
MX %00
000801- 0A ASL
000802- A9 10 00 LDA #0010
000805- 8D 9F 0A STA 0A9F
000808- A9 00 01 LDA #0100
00080B- 8D A1 0A STA 0AA1
00080E- 60 RTS
00080F- 20 62 09 JSR 0962
000812- F4 00 00 PEA 0000
000815- F4 07 00 PEA 0007
000818- F4 00 20 PEA 2000
00081B- F4 00 40 PEA 4000
00081E- F4 00 00 PEA 0000
000821- F4 36 0A PEA 0A36
000824- F4 00 00 PEA 0000
000827- F4 3A 0A PEA 0A3A
00082A- A2 03 27 LDX #2703
00082D- 22 00 00 E1 JSL E10000 ; Toolbox: UnPackBytes(@Buff,BfSz,@StartPtr,@Sz):Size
000831- 68 PLA
000832- 8D 00 03 STA 0300
000835- 60 RTS
000836- 00 20 BRK 20
000838- E1 00 SBC (00,X)
00083A- 00 80 BRK 80
00083C- 20 67 09 JSR 0967
00083F- 2C 10 C0 BIT C010 ; r:KBDSTRB
000842- AD 00 C0 LDA C000 ; r:KBD w:CLR80COL
000845- 30 13 BMI 085A {+13}
000847- AD 61 C0 LDA C061 ; r:BUTN0
00084A- 30 0E BMI 085A {+0E}
00084C- AD 62 C0 LDA C062 ; r:BUTN1
00084F- 30 09 BMI 085A {+09}
000851- AD 27 C0 LDA C027 ; rw:KMSTATUS
000854- 89 80 D0 BIT #D080

181
yagsdisasm.php Normal file
View File

@ -0,0 +1,181 @@
<?php
/*
* YAGSDisasm - A 65C816 disassembler for Apple IIGS code and disk images
*
*/
require_once( 'Input/Input_2MG.php' );
require_once( 'Input/Input_PO.php' );
require_once( 'Input/Input_BIN.php' );
require_once( 'Disassembler/Disassembler_65816.php' );
require_once( 'Personality/Personality_AppleIIGS.php' );
// version string
$version_str = '1.0a';
/*
* begin options parsing here
*/
$options = getopt( 'p:e:x:m:f:s:n:o:Vvhk:' );
if( isset( $options['V'] ))
{
echo "Version: {$version_str}\n";
exit;
}
if( isset( $options['h'] ))
{
echo <<<EOF
YAGSDisasm - A 65C816 disassembler for Apple IIGS code and disk images
usage: php yagsdisasm.php [options]
-p specify machine personality (for auto commenting and formatting)
iigs - Apple IIGS (this is presently the only target)
-e value of E cpu flag (1 is emulation mode)
-x value of X cpu flag (1 is short X/Y register)
-m value of M cpu flag (1 is short accumulator)
-f specifies input file (required)
-i specifies format of input
2mg - .2mg disk (see -s, -n options)
po - .po disk image (see -s, -n options)
bin - binary blob (see -s, -n options which will refer to bytes instead of blocks, default is to disassemble the whole thing)
default behavior is to guess by file extension
-s starting disk block to disassemble (if input is binary blob, this is starting offset into file)
-n number of disk blocks to disassemble (if input is binary blob, this is length to disassemble from file)
-k skip this number of bytes from beginning when disassembling
-o 24-bit origin address, in hex (eg 002000)
addresses shorter than 24 bits will be zero-filled
addresses longer than 24 bits will be truncated
-v verbose/debugging info
-V print version information and exit
EOF;
exit;
}
// handle defaults and specification for filename and format
if( !isset( $options['f'] ))
{
echo "Filename must be specified with -f\n";
exit;
}
$filename = $options['f'];
$format = 'po';
$parts = explode( '.', $filename );
if( strtolower( $parts[count($parts)-1] ) == '2mg' )
{
$format = '2mg';
}
if( isset( $options['i'] ))
{
$known_type = false;
if( $options['i'] == '2mg' )
{
$format = '2mg';
$known_type = true;
}
if( $options['i'] == 'po' )
{
$format = 'po';
$known_type = true;
}
if( $options['bin'] == 'bin' )
{
$format = 'bin';
$known_type = true;
}
if( !$known_type )
{
echo "Unknown input image type: ".$options['i']."\n";
exit;
}
}
// handle defaults and specification for starting block and ending block
if( $format == 'bin' )
{
$starting_block = 0;
$num_blocks = 'all';
} else {
$starting_block = 0;
$num_blocks = 1;
}
if( isset( $options['s'] ))
{
$starting_block = intval( $options['s'] );
}
if( isset( $options['n'] ))
{
$num_blocks = intval( $options['n'] );
}
// handle defaults and specifications for M, X, E cpu flags
$m_flag = 1;
$x_flag = 1;
$e_flag = 1;
if( isset( $options['m'] ))
{
$m_flag = intval( $options['m'] );
}
if( isset( $options['x'] ))
{
$x_flag = intval( $options['x'] );
}
if( isset( $options['e'] ))
{
$e_flag = intval( $options['e'] );
}
// handle verbosity
$verbose = false;
if( isset( $options['v'] ))
{
$verbose = true;
}
// handle origin address
$origin = 0x1000;
if( isset( $options['o'] ))
{
$origin = hexdec( $options['o'] );
}
// handle skip amount
$skip = 0;
if( isset( $options['k'] ))
{
$skip = $options['k'];
}
/*
* begin main operations here
*/
switch( $format )
{
case 'bin': $fh = new Input_BIN( $filename );
$data = $fh->getBlocks( $starting_block, $num_blocks );
break;
case '2mg': $fh = new Input_2MG( $filename );
$data = $fh->getBlocks( $starting_block, $num_blocks );
break;
case 'po': $fh = new Input_PO( $filename );
$data = $fh->getBytes( $starting_block, $num_blocks );
break;
}
if( $verbose )
{
echo "Processed {$format} file {$filename}\n";
$fh->printInfo();
}
if( $skip > 0 )
{
$data = substr( $data, $skip );
}
$pers = new Personality_AppleIIGS;
$dh = new Disassembler_65816;
$dh->disassemble( $m_flag, $x_flag, $e_flag, $origin, $data, $pers );