more work on cpu flags and vectors

This commit is contained in:
Christopher Shepherd 2016-05-27 17:42:24 -04:00
parent aa5c0170cc
commit cdf66ed8d5
2 changed files with 296 additions and 34 deletions

View File

@ -1,12 +1,99 @@
<?php <?php
/* /**
* Disassembler_65816 - A 65816 disassembler, of course * Disassembler_65816 - A 65816 disassembler, of course
* loosely references Andy McFadden's Ciderpress disassembler work at * loosely references Andy McFadden's Ciderpress disassembler work at
* https://github.com/fadden/ciderpress/blob/master/reformat/DisasmTable.cpp * https://github.com/fadden/ciderpress/blob/master/reformat/DisasmTable.cpp
*/ */
class Disassembler_65816 class Disassembler_65816
{ {
/* /**
* 65816 X status register
*/
private $x_flag;
/**
* 65816 M status register
*/
private $m_flag;
/**
* 65816 emulation mode flag
*/
private $e_flag;
/**
* program counter
*/
private $pc;
/**
* array of byte groups to not disassemble
*/
private $bytes_queue = array();
/**
* set_x_flag() - set 65816 'X' processor status flag
*
*/
public function set_x_flag()
{
$this->x_flag = 1;
}
/**
* clear_x_flag() - clear 65816 'X' processor status flag
*
*/
public function clear_x_flag()
{
$this->x_flag = 0;
}
/**
* set_m_flag() - set 65816 'M' processor status flag
*
*/
public function set_m_flag()
{
$this->m_flag = 1;
}
/**
* clear_m_flag() - clear 65816 'M' processor status flag
*
*/
public function clear_m_flag()
{
$this->m_flag = 0;
}
/**
* set_e_flag() - set 65816 'E' processor status flag
*
*/
public function set_e_flag()
{
$this->e_flag = 1;
}
/**
* clear_e_flag() - clear 65816 'E' processor status flag
*
*/
public function clear_e_flag()
{
$this->e_flag = 0;
}
/**
* raw_bytes() - personality tells us not to disassemble the next $num_bytes bytes
*/
public function do_raw_bytes( $bytes_queue )
{
$this->bytes_queue = $bytes_queue;
}
/**
* initialization here * initialization here
*/ */
public function __construct() public function __construct()
@ -287,17 +374,17 @@ class Disassembler_65816
} }
/** /**
* rel_offset( $pc, $offset ) - compute absolute offset vs program counter for a branch offset * rel_offset( $offset ) - compute absolute offset vs program counter for a branch offset
* *
* @param $pc int, $offset int * @param $offset int
*/ */
private function rel_offset( $pc, $offset, $offset2 ) private function rel_offset( $offset, $offset2 )
{ {
if( $offset > 127 ) if( $offset > 127 )
{ {
$result = $pc - (256-$offset); $result = $this->pc - (256-$offset);
} else { } else {
$result = $pc + $offset; $result = $this->pc + $offset;
} }
$result += $offset2+1; $result += $offset2+1;
@ -306,17 +393,17 @@ class Disassembler_65816
} }
/** /**
* rel_long_offset( $pc, $offset ) - compute absolute offset vs program counter for a branch offset * rel_long_offset( $offset, $offset2 ) - compute absolute offset vs program counter for a branch offset
* *
* @param $pc int, $offset int * @param $offset int, $offset2 int
*/ */
private function rel_long_offset( $pc, $offset, $offset2 ) private function rel_long_offset( $offset, $offset2 )
{ {
if( $offset > 32767 ) if( $offset > 32767 )
{ {
$result = $pc - (65536-$offset); $result = $this->pc - (65536-$offset);
} else { } else {
$result = $pc + $offset; $result = $this->pc + $offset;
} }
$result += $offset2+1; $result += $offset2+1;
@ -325,26 +412,26 @@ class Disassembler_65816
} }
/** /**
* print_instruction( $opcode_txt, $opcode, $mode, $arg_bytes, $pc ) - Output text-friendly disassembled output for one instruction * print_instruction( $opcode_txt, $opcode, $mode, $arg_bytes ) - Output text-friendly disassembled output for one instruction
* *
* @param $opcode_txt str, $opcode int, $mode str, $arg_bytes str, $pc int * @param $opcode_txt str, $opcode int, $mode str, $arg_bytes str
*/ */
public function print_instruction( $opcode_txt, $opcode, $mode, $arg_bytes, $pc ) public function print_instruction( $opcode_txt, $opcode, $mode, $arg_bytes )
{ {
// first print the program counter and hex bytes // first print the program counter and hex bytes
switch( strlen( $arg_bytes )) switch( strlen( $arg_bytes ))
{ {
case 0: case 0:
$output = sprintf( '%06X- %02X ', $pc, $opcode ); $output = sprintf( '%06X- %02X ', $this->pc, $opcode );
break; break;
case 1: case 1:
$output = sprintf( '%06X- %02X %02X ', $pc, $opcode, ord($arg_bytes[0]) ); $output = sprintf( '%06X- %02X %02X ', $this->pc, $opcode, ord($arg_bytes[0]) );
break; break;
case 2: case 2:
$output = sprintf( '%06X- %02X %02X %02X ', $pc, $opcode, ord($arg_bytes[0]), ord($arg_bytes[1]) ); $output = sprintf( '%06X- %02X %02X %02X ', $this->pc, $opcode, ord($arg_bytes[0]), ord($arg_bytes[1]) );
break; break;
case 3: case 3:
$output = sprintf( '%06X- %02X %02X %02X %02X ', $pc, $opcode, ord($arg_bytes[0]), ord($arg_bytes[1]), ord($arg_bytes[2]) ); $output = sprintf( '%06X- %02X %02X %02X %02X ', $this->pc, $opcode, ord($arg_bytes[0]), ord($arg_bytes[1]), ord($arg_bytes[2]) );
break; break;
} }
@ -390,9 +477,9 @@ class Disassembler_65816
case 'PCRel': case 'PCRel':
if( ord($arg_bytes[0]) > 127) 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])); $output .= sprintf( ' %04X {-%02X}', $this->rel_offset( $this->pc, ord($arg_bytes[0]),strlen($arg_bytes)), 256-ord($arg_bytes[0]));
} else { } else {
$output .= sprintf( ' %04X {+%02X}', $this->rel_offset( $pc, ord($arg_bytes[0]),strlen($arg_bytes)), ord($arg_bytes[0])); $output .= sprintf( ' %04X {+%02X}', $this->rel_offset( $this->pc, ord($arg_bytes[0]),strlen($arg_bytes)), ord($arg_bytes[0]));
} }
break; break;
case 'Abs': case 'Abs':
@ -440,9 +527,9 @@ class Disassembler_65816
$offset = (ord($arg_bytes[1])*256)+ord($arg_bytes[0]); $offset = (ord($arg_bytes[1])*256)+ord($arg_bytes[0]);
if( $offset > 32767 ) if( $offset > 32767 )
{ {
$output .= sprintf( ' %04X {-%02X}', $this->rel_long_offset( $pc, $offset, strlen($arg_bytes)), 65536-$offset); $output .= sprintf( ' %04X {-%02X}', $this->rel_long_offset( $this->pc, $offset, strlen($arg_bytes)), 65536-$offset);
} else { } else {
$output .= sprintf( ' %04X {+%02X}', $this->rel_long_offset( $pc, $offset, strlen($arg_bytes)), $offset); $output .= sprintf( ' %04X {+%02X}', $this->rel_long_offset( $this->pc, $offset, strlen($arg_bytes)), $offset);
} }
break; break;
case 'StackAbs': case 'StackAbs':
@ -470,18 +557,22 @@ class Disassembler_65816
*/ */
public function disassemble( $m_flag, $x_flag, $e_flag, $origin, $data, $personality ) public function disassemble( $m_flag, $x_flag, $e_flag, $origin, $data, $personality )
{ {
$pc = $origin; $this->x_flag = $x_flag;
$this->e_flag = $e_flag;
$this->m_flag = $m_flag;
$this->pc = $origin;
$data_index = 0; $data_index = 0;
$last_output = ''; $last_output = '';
echo " ORG ".sprintf('%06X',$origin)."\n"; echo " ORG ".sprintf('%06X',$origin)."\n";
if( $m_flag > 0) if( $this->m_flag > 0)
{ {
$print_m = '1'; $print_m = '1';
} else { } else {
$print_m = '0'; $print_m = '0';
} }
if( $x_flag > 0 ) if( $this->x_flag > 0 )
{ {
$print_x = '1'; $print_x = '1';
} else { } else {
@ -539,7 +630,7 @@ class Disassembler_65816
$width = 4; $width = 4;
break; break;
case 'StackInt': case 'StackInt':
if( $e_flag ) if( $this->e_flag )
{ {
$width = 1; $width = 1;
} else { } else {
@ -548,17 +639,17 @@ class Disassembler_65816
break; break;
case 'Imm': case 'Imm':
$width = 2; $width = 2;
if( $e_flag == 0 ) if( $this->e_flag == 0 )
{ {
if( in_array( $opcode_txt, array( 'CPX', 'CPY', 'LDX', 'LDY' ))) if( in_array( $opcode_txt, array( 'CPX', 'CPY', 'LDX', 'LDY' )))
{ {
if( $x_flag == 0 ) if( $this->x_flag == 0 )
{ {
$width = 3; $width = 3;
} }
} elseif( in_array( $opcode_txt, array( 'ADC', 'AND', 'BIT', 'CMP', 'EOR', 'LDA', 'ORA', 'SBC' ))) } elseif( in_array( $opcode_txt, array( 'ADC', 'AND', 'BIT', 'CMP', 'EOR', 'LDA', 'ORA', 'SBC' )))
{ {
if( $m_flag == 0 ) if( $this->m_flag == 0 )
{ {
$width = 3; $width = 3;
} }
@ -580,11 +671,11 @@ class Disassembler_65816
$arg_bytes .= $data[$data_index + $ind]; $arg_bytes .= $data[$data_index + $ind];
} }
$output = $this->print_instruction( $opcode_txt, ord($opcode), $mode, $arg_bytes, $pc ); $output = $this->print_instruction( $opcode_txt, ord($opcode), $mode, $arg_bytes );
// Personality support // Personality support
$this_output = substr( $output, 22 ); $this_output = substr( $output, 22 );
$comment = $personality->check( $last_output, $this_output ); $comment = $personality->check( $last_output, $this_output, $this );
if( $comment ) if( $comment )
{ {
for($pad=strlen($output);$pad<42;$pad++) for($pad=strlen($output);$pad<42;$pad++)
@ -596,8 +687,52 @@ class Disassembler_65816
$last_output = $this_output; $last_output = $this_output;
echo "{$output}\n"; echo "{$output}\n";
$pc += $width; $this->pc += $width;
$data_index += $width; $data_index += $width;
// raw bytes support, if the Personality module signalled them. hacky.
foreach( $this->bytes_queue as $raw_bytes)
{
// if personality told us the next few bytes will be raw, then handle them that way
while( $raw_bytes > 0 )
{
if( $raw_bytes == 1 )
{
$output = sprintf( '%06X- %02X DB ', $this->pc, ord( $data[$data_index] ));
$output .= sprintf( ' %02X', ord( $data[$data_index] ));
$width = 1;
}
if( $raw_bytes == 4 )
{
$output = sprintf( '%06X- %02X %02X %02X %02X ', $this->pc, ord($data[$data_index]), ord($data[$data_index+1]), ord($data[$data_index+2]), ord($data[$data_index+3]) );
$output .= sprintf( ' %02X%02X%02X%02X', ord($data[$data_index+3]), ord($data[$data_index+2]), ord($data[$data_index+1]), ord($data[$data_index]));
$width = 4;
}
if( $raw_bytes > 1 )
{
$output = sprintf( '%06X- %02X %02X DW ', $this->pc, ord($data[$data_index]), ord($data[$data_index+1]) );
$output .= sprintf( ' %02X%02X', ord($data[$data_index+1]), ord($data[$data_index]));
$width = 2;
}
$raw_bytes -= $width;
$this_output = substr( $output, 22 );
$comment = $personality->check( $last_output, $this_output, $this );
if( $comment )
{
for($pad=strlen($output);$pad<42;$pad++)
{
$output .= ' ';
}
$output .= '; '.$comment;
}
$last_output = $this_output;
echo "{$output}\n";
$this->pc += $width;
$data_index += $width;
}
}
$this->bytes_queue = array();
} // for } // for
} }
} }

View File

@ -4,6 +4,10 @@
*/ */
class Personality_AppleIIGS class Personality_AppleIIGS
{ {
private $inside_prodos = 0;
private $inside_prodos16 = 0;
private $inside_smartport = 0;
/** /**
* getlines( $fp, $numlines ) - Parse some lines out of the NiftyList file * getlines( $fp, $numlines ) - Parse some lines out of the NiftyList file
* *
@ -50,14 +54,122 @@ class Personality_AppleIIGS
/** /**
* check( $last_instruction, $this_instruction ) - Check if something comment-worthy has occurred * check( $last_instruction, $this_instruction ) - Check if something comment-worthy has occurred
* (after instruction decoding)
* *
* @param $last_instruction str, $this_isntruction str * @param $last_instruction str, $this_isntruction str
* @return mixed * @return mixed
*/ */
public function check( $last_instruction, $this_instruction ) public function check( $last_instruction, $this_instruction, $disasm )
{ {
$comment = false; $comment = false;
// m/x/e flags
if( $this_instruction == 'XCE' )
{
if( $last_instruction == 'CLC' )
{
$disasm->clear_e_flag();
}
if( $last_instruction == 'SEC' )
{
$disasm->set_e_flag();
}
}
$opcode = substr( $this_instruction, 0, 3 );
if( in_array( $opcode, array( 'REP', 'SEP' )))
{
$cpu_flags = substr( $this_instruction, -2 );
// 0x10 - index flags (X status register)
if( $cpu_flags && 0x10 )
{
if( $opcode == 'REP' )
{
$disasm->clear_x_flag();
} else {
$disasm->set_x_flag();
}
}
// 0x40 - accumulator (M status register)
if( $cpu_flags && 0x40 )
{
if( $opcode == 'REP' )
{
$disasm->clear_m_flag();
} else {
$disasm->set_m_flag();
}
}
}
// SmartPort
if( $this->inside_smartport > 0 )
{
if( $this->inside_smartport == 1 )
{
$comment = 'Pointer to parameters';
$this->inside_smartport -= 1;
} else {
$call_number = substr( $this_instruction, -2 );
$comment = 'Call Number';
// TODO extended smartport call support is broke
if( substr( $call_number, 0, 1 ) == '4' )
{
// extended smartport call: address param is long
$disasm->do_raw_bytes( array(4) );
} else {
// non-extended smartport call: address param is 16 bits
$disasm->do_raw_bytes( array(2) );
}
$this->inside_smartport -= 1;
}
}
if( in_array( $this_instruction, array( 'JSR C50D', 'JSR C50A', 'JSR C70D', 'JSR C70A' )))
{
$this->inside_smartport = 2;
$disasm->do_raw_bytes( array(1,2) );
$comment = 'SmartPort Entry';
}
// ProDOS 8
if( $this->inside_prodos > 0 )
{
if( $this->inside_prodos == 1 )
{
$comment = 'Pointer to parameters';
$this->inside_prodos -= 1;
} else {
$call_number = substr( $this_instruction, -2 );
$comment = $this->nlist_prodos8[$call_number];
$this->inside_prodos -= 1;
}
}
if( $this_instruction == 'JSR BF00' )
{
$this->inside_prodos = 2;
$disasm->do_raw_bytes( array(1,2) );
$comment = 'ProDOS 8 MLI';
}
// ProDOS 16 / GS/OS inline
if( $this->inside_gsos > 0 )
{
if( $this->inside_gsos == 1 )
{
$comment = 'Pointer to parameters';
$this->inside_gsos -= 1;
} else {
$call_number = substr( $this_instruction, -4 );
$comment = $this->nlist_prodos16[$call_number];
$this->inside_gsos -= 1;
}
}
if( $this_instruction == 'JSL E100A8' )
{
$this->inside_gsos = 2;
$disasm->do_raw_bytes( array(2,4) );
$comment = 'GS/OS In-Line MLI';
}
// softswitches and 8-bit firmware // softswitches and 8-bit firmware
$addr_part = substr( $this_instruction, -5 ); $addr_part = substr( $this_instruction, -5 );
if( $addr_part[0] == ' ' ) if( $addr_part[0] == ' ' )
@ -68,6 +180,21 @@ class Personality_AppleIIGS
} }
} }
// E1xxxx vectors
$parts = explode( ' ', $this_instruction );
if(( $parts[1] ) && ( strlen( $parts[1] ) == 6 ) && ( substr( $parts[1], 0, 2 ) == 'E1' ))
{
$vector = substr( $parts[1], -4 );
$comment = $this->nlist_e1xxxx[$vector];
}
// E0xxxx vectors
if(( $parts[1] ) && ( strlen( $parts[1] ) == 6 ) && ( substr( $parts[1], 0, 2 ) == 'E0' ))
{
$vector = substr( $parts[1], -4 );
$comment = $this->nlist_e0xxxx[$vector];
}
// toolbox entry // toolbox entry
if( $this_instruction == 'JSL E10000' ) if( $this_instruction == 'JSL E10000' )
{ {