windows-nt/Source/XPSP1/NT/sdktools/debuggers/ntsd64/alpha_dis.cpp
2020-09-26 16:20:57 +08:00

745 lines
19 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//----------------------------------------------------------------------------
//
// Disassembly portions of Alpha machine implementation.
//
// Copyright (C) Microsoft Corporation, 2000-2001.
//
//----------------------------------------------------------------------------
// TODO:
// (3) Should registers be treated as 64 or 32bit? Note that Fregs are
// 64bits only. -- All registers should be treated as 64bit values
// since LDQ/EXTB is done for byte fetches (stores); the intermediate
// values could be hidden.
#include "ntsdp.hpp"
#include "alpha_dis.h"
#include "alpha_optable.h"
// See Get/SetRegVal comments in machine.hpp.
#define RegValError Do_not_use_GetSetRegVal_in_machine_implementations
#define GetRegVal(index, val) RegValError
#define GetRegVal32(index) RegValError
#define GetRegVal64(index) RegValError
#define SetRegVal(index, val) RegValError
#define SetRegVal32(index, val) RegValError
#define SetRegVal64(index, val) RegValError
ADDR g_AlphaEffAddr;
ALPHA_INSTRUCTION g_AlphaDisinstr;
// these are defined in alphaops.h
#define NUMBER_OF_BREAK_INSTRUCTIONS 3
#define ALPHA_BP_LEN 4
ULONG g_AlphaTrapInstr = CALLPAL_OP | BPT_FUNC;
ULONG g_AlphaBreakInstrs[NUMBER_OF_BREAK_INSTRUCTIONS] =
{
CALLPAL_OP | BPT_FUNC,
CALLPAL_OP | KBPT_FUNC,
CALLPAL_OP | CALLKD_FUNC
};
#define OPRNDCOL 27 // Column for first operand
#define EACOL 40 // column for effective address
#define FPTYPECOL 40 // .. for the type of FP instruction
BOOL
AlphaMachineInfo::Disassemble (
PADDR poffset,
PSTR bufptr,
BOOL fEAout
)
{
ULONG opcode;
ULONG64 Ea; // Effective Address
POPTBLENTRY pEntry;
m_BufStart = m_Buf = bufptr;
sprintAddr(&m_Buf, poffset);
*m_Buf++ = ':';
*m_Buf++ = ' ';
if (!GetMemDword(poffset, &g_AlphaDisinstr.Long)) {
BufferString("???????? ????\n");
*m_Buf = '\0';
return(FALSE);
}
BufferHex(g_AlphaDisinstr.Long, 8, FALSE); // Output instruction in Hex
*m_Buf++ = ' ';
opcode = g_AlphaDisinstr.Memory.Opcode; // Select disassembly procedure from
pEntry = findOpCodeEntry(opcode); // Get non-func entry for this code
switch (pEntry->iType) {
case ALPHA_UNKNOWN:
BufferString(pEntry->pszAlphaName);
break;
case ALPHA_MEMORY:
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferReg(g_AlphaDisinstr.Memory.Ra);
*m_Buf++ = ',';
BufferHex(g_AlphaDisinstr.Memory.MemDisp, (WIDTH_MEM_DISP + 3)/4, TRUE );
*m_Buf++ = '(';
BufferReg(g_AlphaDisinstr.Memory.Rb);
*m_Buf++ = ')';
break;
case ALPHA_FP_MEMORY:
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferFReg(g_AlphaDisinstr.Memory.Ra);
*m_Buf++ = ',';
BufferHex(g_AlphaDisinstr.Memory.MemDisp, (WIDTH_MEM_DISP + 3)/4, TRUE );
*m_Buf++ = '(';
BufferReg(g_AlphaDisinstr.Memory.Rb);
*m_Buf++ = ')';
break;
case ALPHA_MEMSPC:
BufferString(findFuncName(pEntry, g_AlphaDisinstr.Memory.MemDisp & BITS_MEM_DISP));
//
// Some memory special instructions have an operand
//
switch (g_AlphaDisinstr.Memory.MemDisp & BITS_MEM_DISP) {
case FETCH_FUNC:
case FETCH_M_FUNC:
// one operand, in Rb
BufferBlanks(OPRNDCOL);
*m_Buf++ = '0';
*m_Buf++ = '(';
BufferReg(g_AlphaDisinstr.Memory.Rb);
*m_Buf++ = ')';
break;
case RS_FUNC:
case RC_FUNC:
case RPCC_FUNC:
// one operand, in Ra
BufferBlanks(OPRNDCOL);
BufferReg(g_AlphaDisinstr.Memory.Ra);
break;
case MB_FUNC:
case WMB_FUNC:
case MB2_FUNC:
case MB3_FUNC:
case TRAPB_FUNC:
case EXCB_FUNC:
// no operands
break;
default:
printf("we shouldn't get here \n");
break;
}
break;
case ALPHA_JUMP:
BufferString(findFuncName(pEntry, g_AlphaDisinstr.Jump.Function));
BufferBlanks(OPRNDCOL);
BufferReg(g_AlphaDisinstr.Jump.Ra);
*m_Buf++ = ',';
*m_Buf++ = '(';
BufferReg(g_AlphaDisinstr.Jump.Rb);
*m_Buf++ = ')';
*m_Buf++ = ',';
BufferHex(g_AlphaDisinstr.Jump.Hint, (WIDTH_HINT + 3)/4, TRUE);
Ea = GetReg64(GetIntRegNumber(g_AlphaDisinstr.Jump.Rb)) & (~3);
BufferEffectiveAddress(Ea);
break;
case ALPHA_BRANCH:
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferReg(g_AlphaDisinstr.Branch.Ra);
*m_Buf++ = ',';
//
// The next line might be a call to GetNextOffset, but
// GetNextOffset assumes that it should work from FIR.
//
Ea = Flat(*poffset) +
sizeof(ULONG) +
(g_AlphaDisinstr.Branch.BranchDisp * 4);
BufferHex(Ea, 16, FALSE);
BufferEffectiveAddress(Ea);
break;
case ALPHA_FP_BRANCH:
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferFReg(g_AlphaDisinstr.Branch.Ra);
*m_Buf++ = ',';
//
// The next line might be a call to GetNextOffset, but
// GetNextOffset assumes that it should work from FIR.
//
Ea = Flat(*poffset) +
sizeof(ULONG) +
(g_AlphaDisinstr.Branch.BranchDisp * 4);
BufferHex(Ea, 16, FALSE);
BufferEffectiveAddress(Ea);
break;
case ALPHA_OPERATE:
BufferString(findFuncName(pEntry, g_AlphaDisinstr.OpReg.Function));
BufferBlanks(OPRNDCOL);
if (g_AlphaDisinstr.OpReg.Opcode != SEXT_OP) {
BufferReg(g_AlphaDisinstr.OpReg.Ra);
*m_Buf++ = ',';
}
if (g_AlphaDisinstr.OpReg.RbvType) {
*m_Buf++ = '#';
BufferHex(g_AlphaDisinstr.OpLit.Literal, (WIDTH_LIT + 3)/4, TRUE);
} else {
BufferReg(g_AlphaDisinstr.OpReg.Rb);
}
*m_Buf++ = ',';
BufferReg(g_AlphaDisinstr.OpReg.Rc);
break;
case ALPHA_FP_OPERATE:
{
ULONG Function;
ULONG Flags;
Flags = g_AlphaDisinstr.FpOp.Function & MSK_FP_FLAGS;
Function = g_AlphaDisinstr.FpOp.Function & MSK_FP_OP;
#if 0
if (fVerboseBuffer) {
dprintf("In FP_OPERATE: Flags %08x Function %08x\n",
Flags, Function);
dprintf("opcode %d \n", opcode);
}
#endif
//
// CVTST and CVTST/S are different: they look like
// CVTTS with some flags set
//
if (Function == CVTTS_FUNC) {
if (g_AlphaDisinstr.FpOp.Function == CVTST_S_FUNC) {
Function = CVTST_S_FUNC;
Flags = NONE_FLAGS;
}
if (g_AlphaDisinstr.FpOp.Function == CVTST_FUNC) {
Function = CVTST_FUNC;
Flags = NONE_FLAGS;
}
}
BufferString(findFuncName(pEntry, Function));
//
// Append the opcode qualifier, if any, to the opcode name.
//
if ( (opcode == IEEEFP_OP) || (opcode == VAXFP_OP)
|| (Function == CVTQL_FUNC) ) {
BufferString(findFlagName(Flags, Function));
}
BufferBlanks(OPRNDCOL);
//
// If this is a convert instruction, only Rb and Rc are used
//
if (strncmp("cvt", findFuncName(pEntry, Function), 3) != 0) {
BufferFReg(g_AlphaDisinstr.FpOp.Fa);
*m_Buf++ = ',';
}
BufferFReg(g_AlphaDisinstr.FpOp.Fb);
*m_Buf++ = ',';
BufferFReg(g_AlphaDisinstr.FpOp.Fc);
break;
}
case ALPHA_FP_CONVERT:
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferFReg(g_AlphaDisinstr.FpOp.Fa);
*m_Buf++ = ',';
BufferFReg(g_AlphaDisinstr.FpOp.Fb);
break;
case ALPHA_CALLPAL:
BufferString(findFuncName(pEntry, g_AlphaDisinstr.Pal.Function));
break;
case ALPHA_EV4_PR:
if ((g_AlphaDisinstr.Long & MSK_EV4_PR) == 0)
{
BufferString("NOP");
}
else
{
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferReg(g_AlphaDisinstr.EV4_PR.Ra);
*m_Buf++ = ',';
if(g_AlphaDisinstr.EV4_PR.Ra != g_AlphaDisinstr.EV4_PR.Rb)
{
BufferReg(g_AlphaDisinstr.EV4_PR.Rb);
*m_Buf++ = ',';
}
BufferString(findFuncName(pEntry, (g_AlphaDisinstr.Long & MSK_EV4_PR)));
}
break;
case ALPHA_EV4_MEM:
BufferString(pEntry->pszAlphaName);
BufferBlanks(OPRNDCOL);
BufferReg(g_AlphaDisinstr.EV4_MEM.Ra);
*m_Buf++ = ',';
BufferReg(g_AlphaDisinstr.EV4_MEM.Rb);
break;
case ALPHA_EV4_REI:
BufferString(pEntry->pszAlphaName);
break;
default:
BufferString("Invalid type");
break;
}
Off(*poffset) += sizeof(ULONG);
NotFlat(*poffset);
ComputeFlatAddress(poffset, NULL);
*m_Buf++ = '\n';
*m_Buf = '\0';
return(TRUE);
}
void
AlphaMachineInfo::BufferReg (ULONG regnum)
{
BufferString(RegNameFromIndex(GetIntRegNumber(regnum)));
}
void
AlphaMachineInfo::BufferFReg (ULONG regnum)
{
*m_Buf++ = 'f';
if (regnum > 9)
*m_Buf++ = (UCHAR)('0' + regnum / 10);
*m_Buf++ = (UCHAR)('0' + regnum % 10);
}
/*** BufferEffectiveAddress - Print EA symbolically
*
* Purpose:
* Given the effective address (for a branch, jump or
* memory instruction, print it symbolically, if
* symbols are available.
*
* Input:
* offset - computed by the caller as
* for jumps, the value in Rb
* for branches, func(PC, displacement)
* for memory, func(PC, displacement)
*
* Returns:
* None
*
*************************************************************************/
void
AlphaMachineInfo::BufferEffectiveAddress(
ULONG64 offset
)
{
CHAR chAddrBuffer[MAX_SYMBOL_LEN];
ULONG64 displacement;
PCHAR pszTemp;
UCHAR ch;
//
// MBH - i386 compiler bug with fast calling standard.
// If "chAddrBuffer is used as a calling argument to
// GetSymbol, it believes (here, but not in the other
// uses of GetSymbol that the size is 60+8=68.
//
PCHAR pch = chAddrBuffer;
BufferBlanks(EACOL);
GetSymbolStdCall(offset, pch, sizeof(chAddrBuffer), &displacement, NULL);
if (chAddrBuffer[0])
{
pszTemp = chAddrBuffer;
while (ch = *pszTemp++)
{
*m_Buf++ = ch;
}
if (displacement)
{
*m_Buf++ = '+';
BufferHex(displacement, 8, TRUE);
}
}
else
{
BufferHex(offset, 16, FALSE);
}
// Save EA.
ADDRFLAT(&g_AlphaEffAddr, offset);
}
BOOL
AlphaMachineInfo::IsBreakpointInstruction(PADDR Addr)
{
UCHAR Instr[ALPHA_BP_LEN];
if (GetMemString(Addr, Instr, ALPHA_BP_LEN) != ALPHA_BP_LEN)
{
return FALSE;
}
LONG index;
//
// ALPHA has several breakpoint instructions - see
// if we have hit any of them.
//
index = 0;
do
{
if (!memcmp(Instr, (PUCHAR)&g_AlphaBreakInstrs[index], ALPHA_BP_LEN))
{
return TRUE;
}
}
while (++index < NUMBER_OF_BREAK_INSTRUCTIONS);
return FALSE;
}
HRESULT
AlphaMachineInfo::InsertBreakpointInstruction(PUSER_DEBUG_SERVICES Services,
ULONG64 Process,
ULONG64 Offset,
PUCHAR SaveInstr,
PULONG64 ChangeStart,
PULONG ChangeLen)
{
*ChangeStart = Offset;
*ChangeLen = ALPHA_BP_LEN;
ULONG Done;
HRESULT Status;
Status = Services->ReadVirtual(Process, Offset, SaveInstr,
ALPHA_BP_LEN, &Done);
if (Status == S_OK && Done != ALPHA_BP_LEN)
{
Status = HRESULT_FROM_WIN32(ERROR_READ_FAULT);
}
if (Status == S_OK)
{
Status = Services->WriteVirtual(Process, Offset, &g_AlphaTrapInstr,
ALPHA_BP_LEN, &Done);
if (Status == S_OK && Done != ALPHA_BP_LEN)
{
Status = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
}
}
return Status;
}
HRESULT
AlphaMachineInfo::RemoveBreakpointInstruction(PUSER_DEBUG_SERVICES Services,
ULONG64 Process,
ULONG64 Offset,
PUCHAR SaveInstr,
PULONG64 ChangeStart,
PULONG ChangeLen)
{
*ChangeStart = Offset;
*ChangeLen = ALPHA_BP_LEN;
ULONG Done;
HRESULT Status;
Status = Services->WriteVirtual(Process, Offset, SaveInstr,
ALPHA_BP_LEN, &Done);
if (Status == S_OK && Done != ALPHA_BP_LEN)
{
Status = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
}
return Status;
}
void
AlphaMachineInfo::AdjustPCPastBreakpointInstruction(PADDR Addr,
ULONG BreakType)
{
DBG_ASSERT(BreakType == DEBUG_BREAKPOINT_CODE);
SetPC(AddrAdd(Addr, ALPHA_BP_LEN));
}
BOOL
AlphaMachineInfo::IsCallDisasm(PCSTR Disasm)
{
return strstr(Disasm, " jsr") != NULL;
}
BOOL
AlphaMachineInfo::IsReturnDisasm(PCSTR Disasm)
{
return strstr(Disasm, " ret ") != NULL && strstr(Disasm, " ra") != NULL;
}
BOOL
AlphaMachineInfo::IsSystemCallDisasm(PCSTR Disasm)
{
return strstr(Disasm, " CallSys") != NULL;
}
BOOL
AlphaMachineInfo::IsDelayInstruction(PADDR Addr)
{
return(FALSE);
}
void
AlphaMachineInfo::GetEffectiveAddr(PADDR Addr)
{
*Addr = g_AlphaEffAddr;
}
void
AlphaMachineInfo::GetNextOffset(BOOL StepOver,
PADDR NextAddr, PULONG NextMachine)
{
ULONG64 rv;
ULONG opcode;
ULONG64 firaddr;
ULONG64 updatedpc;
ULONG64 branchTarget;
ADDR fir;
// Canonical form to shorten tests; Abs is absolute value
LONG Can, Abs;
LARGE_INTEGER Rav;
LARGE_INTEGER Rbv;
LARGE_INTEGER Fav;
// NextMachine is always the same.
*NextMachine = m_ExecTypes[0];
//
// Get current address
//
firaddr = GetReg64(ALPHA_FIR);
//
// relative addressing updates PC first
// Assume next sequential instruction is next offset
//
updatedpc = firaddr + sizeof(ULONG);
rv = updatedpc;
ADDRFLAT( &fir, firaddr);
GetMemDword(&fir, &(g_AlphaDisinstr.Long)); // Get current instruction
opcode = g_AlphaDisinstr.Memory.Opcode;
switch(findOpCodeEntry(opcode)->iType)
{
case ALPHA_JUMP:
switch(g_AlphaDisinstr.Jump.Function)
{
case JSR_FUNC:
case JSR_CO_FUNC:
if (StepOver)
{
//
// Step over the subroutine call;
//
break;
}
//
// fall through
//
case JMP_FUNC:
case RET_FUNC:
Rbv.QuadPart = GetReg64(GetIntRegNumber(g_AlphaDisinstr.Jump.Rb));
if (m_Ptr64)
{
rv = (Rbv.QuadPart & (~3));
}
else
{
rv = EXTEND64(Rbv.LowPart & (~3));
}
break;
}
break;
case ALPHA_BRANCH:
branchTarget = (updatedpc + (g_AlphaDisinstr.Branch.BranchDisp * 4));
Rav.QuadPart = GetReg64(GetIntRegNumber(g_AlphaDisinstr.Branch.Ra));
//
// set up a canonical value for computing the branch test
// - works with ALPHA, MIPS and 386 hosts
//
Can = Rav.LowPart & 1;
if ((LONG)Rav.HighPart < 0)
{
Can |= 0x80000000;
}
if ((Rav.LowPart & 0xfffffffe) || (Rav.HighPart & 0x7fffffff))
{
Can |= 2;
}
#if 0
VerbOut("Rav High %08lx Low %08lx Canonical %08lx\n",
Rav.HighPart, Rav.LowPart, Can);
VerbOut("returnvalue %08lx branchTarget %08lx\n",
rv, branchTarget);
#endif
switch(opcode)
{
case BR_OP: rv = branchTarget; break;
case BSR_OP: if (!StepOver) rv = branchTarget; break;
case BEQ_OP: if (Can == 0) rv = branchTarget; break;
case BLT_OP: if (Can < 0) rv = branchTarget; break;
case BLE_OP: if (Can <= 0) rv = branchTarget; break;
case BNE_OP: if (Can != 0) rv = branchTarget; break;
case BGE_OP: if (Can >= 0) rv = branchTarget; break;
case BGT_OP: if (Can > 0) rv = branchTarget; break;
case BLBC_OP: if ((Can & 0x1) == 0) rv = branchTarget; break;
case BLBS_OP: if ((Can & 0x1) == 1) rv = branchTarget; break;
}
break;
case ALPHA_FP_BRANCH:
branchTarget = (updatedpc + (g_AlphaDisinstr.Branch.BranchDisp * 4));
Fav.QuadPart = GetReg64(g_AlphaDisinstr.Branch.Ra);
//
// Set up a canonical value for computing the branch test
//
Can = Fav.HighPart & 0x80000000;
//
// The absolute value is needed -0 and non-zero computation
//
Abs = Fav.LowPart || (Fav.HighPart & 0x7fffffff);
if (Can && (Abs == 0x0))
{
//
// negative 0 should be considered as zero
//
Can = 0x0;
}
else
{
Can |= Abs;
}
#if 0
VerbOut("Fav High %08lx Low %08lx Canonical %08lx Absolute %08lx\n",
Fav.HighPart, Fav.LowPart, Can, Abs);
VerbOut("returnvalue %08lx branchTarget %08lx\n",
rv, branchTarget);
#endif
switch(opcode)
{
case FBEQ_OP: if (Can == 0) rv = branchTarget; break;
case FBLT_OP: if (Can < 0) rv = branchTarget; break;
case FBNE_OP: if (Can != 0) rv = branchTarget; break;
case FBLE_OP: if (Can <= 0) rv = branchTarget; break;
case FBGE_OP: if (Can >= 0) rv = branchTarget; break;
case FBGT_OP: if (Can > 0) rv = branchTarget; break;
}
break;
}
#if 0
VerbOut("GetNextOffset returning %08lx\n", rv);
#endif
ADDRFLAT( NextAddr, rv );
}
void
AlphaMachineInfo::IncrementBySmallestInstruction(PADDR Addr)
{
AddrAdd(Addr, 4);
}
void
AlphaMachineInfo::DecrementBySmallestInstruction(PADDR Addr)
{
AddrSub(Addr, 4);
}
void
AlphaMachineInfo::PrintStackFrameAddresses(ULONG Flags,
PDEBUG_STACK_FRAME StackFrame)
{
//
// this is pure hack...
// Alpha's "return address" is really the address of the
// instruction where control left the frame. Show the address of
// the next instruction, to make it easy to set a BP on the
// return site. It will still be wrong sometimes, but it will
// be right more often this way.
//
DEBUG_STACK_FRAME AlphaStackFrame = *StackFrame;
if (AlphaStackFrame.ReturnOffset)
{
AlphaStackFrame.ReturnOffset += 4;
}
MachineInfo::PrintStackFrameAddresses(Flags, &AlphaStackFrame);
}