Repository URL to install this package:
|
Version:
9.0~240925-3.fc42 ▾
|
#include "m65816.hpp"
#include "bt.hpp"
// ---------------------------------------------------------------------------
//lint -estring(823,BTWALK_PREAMBLE) definition of macro ends in semi-colon
#define BTWALK_PREAMBLE(walker_ea, opcode_var, itype_var) \
if ( (walker_ea) < 4 ) \
break; \
(walker_ea) = prev_head((walker_ea), (walker_ea) - 4); \
if ( (walker_ea) == BADADDR ) \
break; \
flags64_t F = get_flags(walker_ea); \
if ( is_func(F) || !is_code(F) ) \
break; \
opcode_var = get_byte(walker_ea); \
itype_var = get_opcode_info(opcode_var).itype;
// ---------------------------------------------------------------------------
// FIXME: The following are lacks in implementation:
// * If the value we asked for is 16bits, and
// at some point we are reduced to an 8-bits one, we should
// fail.
int32 backtrack_value(ea_t from_ea, uint8 size, btsource_t source)
{
// Note: At some point, we were using:
// ---
// const func_t * const func = get_fchunk(from_ea);
// if (func == nullptr)
// return -1;
// ea_t chunk_start_ea = func->start_ea;
// ---
// in order to determine where we had to stop backtracking
// values.
// Unfortunately, that doesn't work because, during the initial
// analysis, where functions & chunks aren't properly formed yet,
// the 'func' ptr would always be nullptr. Therefore, we wouldn't
// be capable of backtracking a value properly; which also
// means that wrong values were propagated to other
// functions.
//
// A particularily interesting example is this: In a certain
// rom, we had the following seq. of instructions:
//
// --------------------------------------------------------------------------
// .C0:0019 SEI
// .C0:001A CLC
// .C0:001B XCE
// .C0:001C SEP #$20 ; ' ' ; .a8, .i16
// .C0:001E REP #$10 ; .a8, .i16
// .C0:0020 LDX #$15FF
// .C0:0023 TXS
// .C0:0024 LDX #0
// .C0:0027 PHX
// .C0:0028 PLD
// .C0:0029 TDC
// .C0:002A PHA
// .C0:002B PLB
// .C0:002C LDA #1
// .C0:002E STA CYCLE_SPEED_DESIGNATION ; 0000000a a: 0 = 2.68 MHz, 1 = 3.58 MHz
// .C0:0031 STZ REGULAR_DMA_CHANNEL_ENABLE ; abcdefgh a = Channel 7...h = Channel 0: 1 = Enable 0 = Disable
// .C0:0034 STZ H_DMA_CHANNEL_ENABLE ; abcdefgh a = Channel 7 .. h = Channel 0: 1 = Enable 0 = Disable
// .C0:0037 LDA #$8F ; ''
// .C0:0039 STA SCREEN_DISPLAY_REGISTER ; a000bbbb a: 0=screen on 1=screen off, b = brightness
// .C0:003C STZ NMI_V_H_COUNT_AND_JOYPAD_ENABLE ; a0bc000d a = NMI b = V-Count c = H-Count d = Joypad
// .C0:003F JSR sub_C00525
// --------------------------------------------------------------------------
//
// And, at 0xc00525:
//
// --------------------------------------------------------------------------
// .C0:0525 sub_C00525: ; CODE XREF: sub_C00019+26p
// .C0:0525 TDC
// .C0:0526 TAX
// .C0:0527 STX WRAM_ADDRESS_LOW_BYTE
// .C0:052A STA WRAM_ADDRESS_HIGH_BYTE
// .C0:052D LDX #$120
// .C0:0530
// .C0:0530 loc_C00530: ; CODE XREF: sub_C00525+3Cj
// .C0:0530 STA WRAM_DATA_READ_WRITE
// .C0:0533 STA WRAM_DATA_READ_WRITE
// .C0:0536 STA WRAM_DATA_READ_WRITE
// .C0:0539 STA WRAM_DATA_READ_WRITE
// .C0:053C STA WRAM_DATA_READ_WRITE
// .C0:053F STA WRAM_DATA_READ_WRITE
// .C0:0542 STA WRAM_DATA_READ_WRITE
// .C0:0545 STA WRAM_DATA_READ_WRITE
// .C0:0548 STA WRAM_DATA_READ_WRITE
// .C0:054B STA WRAM_DATA_READ_WRITE
// .C0:054E STA WRAM_DATA_READ_WRITE
// .C0:0551 STA WRAM_DATA_READ_WRITE
// .C0:0554 STA WRAM_DATA_READ_WRITE
// .C0:0557 STA WRAM_DATA_READ_WRITE
// .C0:055A STA WRAM_DATA_READ_WRITE
// .C0:055D STA WRAM_DATA_READ_WRITE
// .C0:0560 DEX
// .C0:0561 BNE loc_C00530
// .C0:0563 RTS
// --------------------------------------------------------------------------
//
// What would happen is that the 'STA's starting at
// C0:0530 would not reference the proper register:
// The B was 0xffffffff (from a previous
// propagation), and when a later propagation tried to set
// the B value to 0x00, starting at C0:0525, that
// propagation stopped at the next segment start.
// That is, C0:0530,
// which was established because of the BNE that
// appears below.
// Which also makes me think.. should we propagate from a BNE?
uint8 opcode;
ea_t cur_ea = from_ea;
uint8 itype;
switch ( source )
{
case BT_STACK:
while ( true )
{
BTWALK_PREAMBLE(cur_ea, opcode, itype);
if ( M65_ITYPE_PUSH(itype) )
{
switch ( itype )
{
case M65816_pea: // Push effective absolute address
{
uint16 val = get_word(cur_ea + 1);
if ( size == 1 )
val &= 0xff;
return val;
}
case M65816_pei: // Push effective indirect address
return -1;
case M65816_per: // Push effective PC-relative indirect address
{
uint16 val = cur_ea + 3;
val += get_word(cur_ea + 1);
val &= (size == 1 ? 0xff : 0xffff);
return val;
}
case M65816_pha: // Push A
return backtrack_value(cur_ea, size, BT_A);
case M65816_phb: // Push B (data bank register)
return get_sreg(cur_ea, rB);
case M65816_phd: // Push D (direct page register)
return get_sreg(cur_ea, rD);
case M65816_phk: // Push K (program bank register)
return get_sreg(cur_ea, rPB);
case M65816_php: // Push processor status
return -1;
case M65816_phx: // Push X
return backtrack_value(cur_ea, size, BT_X);
case M65816_phy: // Push Y
return backtrack_value(cur_ea, size, BT_Y);
default:
return -1;
}
}
else if ( M65_ITYPE_PULL(itype) )
{
// TODO: keep track of additional displacements in the stack
return -1;
}
}
break;
case BT_A:
while ( true )
{
BTWALK_PREAMBLE(cur_ea, opcode, itype);
uint8 opsize = from_ea - cur_ea;
uint8 cur_ea_acc_is_16 = is_acc_16_bits(cur_ea);
uint8 new_size = cur_ea_acc_is_16 ? 2 : 1;
switch ( itype )
{
// All these modify A in a way we cannot
// easily determine its value anymore.
// We'll thus stop.
case M65816_adc: // Add with carry
case M65816_and: // AND A with memory
case M65816_asl: // Shift memory or A left
case M65816_dec: // Decrement
case M65816_eor: // XOR A with M
case M65816_inc: // Increment
case M65816_mvn: // Block move next
case M65816_mvp: // Block move prev
case M65816_ora: // Or A with memory
case M65816_sbc: // Subtract with borrow from A
case M65816_xba: // Exchange bytes in A
return -1;
// For these next ones, there's hope.
case M65816_lsr: // Logical shift memory or A right
if ( opcode == 0x4a ) // LSR A
return -1;
break;
case M65816_rol: // Rotate memory or A left
case M65816_ror: // Rotate memory or A right
if ( opcode == 0x30 || opcode == 0x70 )
return -1;
break;
case M65816_lda: // Load A from memory
if ( opcode == 0xa9 ) // LDA imm
return opsize == 3 ? get_word(cur_ea + 1) : get_byte(cur_ea + 1);
else
return -1;
case M65816_pla: // Pull A
return backtrack_value(cur_ea, new_size, BT_STACK);
case M65816_tdc: // Transfer 16-bit D to A
return get_sreg(cur_ea, rD);
case M65816_tsc: // Transfer S to A
return get_sreg(cur_ea, rS);
case M65816_txa: // Transfer X to A
return backtrack_value(cur_ea, new_size, BT_X);
case M65816_tya: // Transfer Y to A
return backtrack_value(cur_ea, new_size, BT_Y);
}
}
break;
case BT_X:
while ( true )
{
BTWALK_PREAMBLE(cur_ea, opcode, itype);
uint8 opsize = from_ea - cur_ea;
uint8 cur_ea_xy_is_16 = is_xy_16_bits(cur_ea);
uint8 new_size = cur_ea_xy_is_16 ? 2 : 1;
switch ( itype )
{
// All these modify X in a way we cannot
// easily determine its value anymore.
// We'll thus stop.
case M65816_dex: // Decrement X
case M65816_inx: // Increment X
case M65816_mvn: // Block move next
case M65816_mvp: // Block move prev
return -1;
case M65816_ldx: // Load X from memory
if ( opcode == 0xa2 ) // LDX imm
return opsize == 3 ? get_word(cur_ea + 1) : get_byte(cur_ea + 1);
else
return -1;
case M65816_plx: // Pull X
return backtrack_value(cur_ea, new_size, BT_STACK);
case M65816_tax: // Transfer A to X
return backtrack_value(cur_ea, new_size, BT_A);
case M65816_tsx: // Transfer S to X
return get_sreg(cur_ea, rS);
case M65816_tyx: // Transfer Y to X
return backtrack_value(cur_ea, new_size, BT_Y);
}
}
break;
case BT_Y:
while ( true )
{
BTWALK_PREAMBLE(cur_ea, opcode, itype);
uint8 opsize = from_ea - cur_ea;
uint8 cur_ea_xy_is_16 = is_xy_16_bits(cur_ea);
uint8 new_size = cur_ea_xy_is_16 ? 2 : 1;
switch ( itype )
{
// All these modify X in a way we cannot
// easily determine its value anymore.
// We'll thus stop.
case M65816_dey: // Decrement Y
case M65816_iny: // Increment Y
case M65816_mvn: // Block move next
case M65816_mvp: // Block move prev
return -1;
case M65816_ldy: // Load Y from memory
if ( opcode == 0xa0 ) // LDY imm
return opsize == 3 ? get_word(cur_ea + 1) : get_byte(cur_ea + 1);
else
return -1;
case M65816_ply: // Pull Y
return backtrack_value(cur_ea, new_size, BT_STACK);
case M65816_tay: // Transfer A to Y
return backtrack_value(cur_ea, new_size, BT_A);
case M65816_txy: // Transfer X to Y
return backtrack_value(cur_ea, new_size, BT_X);
}
}
break;
case BT_DP:
while ( true )
{
BTWALK_PREAMBLE(cur_ea, opcode, itype);
switch ( itype )
{
// All these modify D in a way we cannot
// easily determine its value anymore.
// We'll thus stop.
case M65816_pld: // Pull D
return backtrack_value(cur_ea, size, BT_STACK);
case M65816_tcd: // Transfer 16-bit Accumulator to Direct Page Register
return backtrack_value(cur_ea, size, BT_A);
}
}
break;
default:
msg("WARNING: backtrack_value() of unsupported BT-type: %d\n", source);
break;
}
return -1;
}
// ---------------------------------------------------------------------------
ea_t backtrack_prev_ins(ea_t from_ea, m65_itype_t itype)
{
uint8 opcode;
ea_t cur_ea = from_ea;
uint8 candidate_itype;
while ( true )
{
BTWALK_PREAMBLE(cur_ea, opcode, candidate_itype);
if ( candidate_itype == itype )
return cur_ea;
}
return BADADDR;
}
#undef BTWALK_LOOP
#undef BTWALK_PREAMBLE