/*++ Copyright (c) 1990 Microsoft Corporation Module Name: kdcpuapi.c Abstract: This module implements CPU specific remote debug APIs. Author: Mark Lucovsky (markl) 04-Sep-1990 Revision History: 24-sep-90 bryanwi Port to the x86. --*/ #include #include "kdp.h" #define END_OF_CONTROL_SPACE (sizeof(KPROCESSOR_STATE)) extern ULONG KdpCurrentSymbolStart, KdpCurrentSymbolEnd; extern ULONG KdSpecialCalls[]; extern ULONG KdNumberOfSpecialCalls; extern BOOLEAN KdpSendContext; LONG KdpLevelChange ( ULONG Pc, PCONTEXT ContextRecord ); LONG regValue( UCHAR reg, PCONTEXT ContextRecord ); BOOLEAN KdpIsSpecialCall ( ULONG Pc, PCONTEXT ContextRecord ); ULONG KdpGetReturnAddress ( PCONTEXT ContextRecord ); ULONG KdpGetCallNextOffset ( ULONG Pc ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGEKD, KdpLevelChange) #pragma alloc_text(PAGEKD, regValue) #pragma alloc_text(PAGEKD, KdpIsSpecialCall) #pragma alloc_text(PAGEKD, KdpGetReturnAddress) #pragma alloc_text(PAGEKD, KdpSetLoadState) #pragma alloc_text(PAGEKD, KdpSetStateChange) #pragma alloc_text(PAGEKD, KdpGetStateChange) #pragma alloc_text(PAGEKD, KdpReadControlSpace) #pragma alloc_text(PAGEKD, KdpWriteControlSpace) #pragma alloc_text(PAGEKD, KdpReadIoSpace) #pragma alloc_text(PAGEKD, KdpWriteIoSpace) #pragma alloc_text(PAGEKD, KdpReadMachineSpecificRegister) #pragma alloc_text(PAGEKD, KdpWriteMachineSpecificRegister) #pragma alloc_text(PAGEKD, KdpGetCallNextOffset) #endif /**** KdpLevelChange - say how the instruction affects the call level * * Input: * pc - program counter of instruction to check * * Output: * returns -1 for a level pop, 1 for a push and 0 if it is * unchanged. * NOTE: This function belongs in some other file. I should move it. ***************************************************************************/ LONG KdpLevelChange ( ULONG Pc, PCONTEXT ContextRecord ) { UCHAR membuf[2]; UCHAR opcode; KdpMoveMemory( (PCHAR)membuf, (PCHAR)Pc, 2 ); opcode = membuf[0]; if ( (opcode == 0x9a) || (opcode == 0xe8) || ((opcode == 0xff) && ((membuf[1] & 0x30) == 0x10)) ) { return 1; } // // The complier generates code for a try-finally that involves having // a ret instruction that does not match with a call instruction. // This ret never returns a value (ie, it's a c3 return and not a // c2). It always returns into the current symbol scope. It is never // preceeded by a leave, which (hopefully) should differentiate it // from recursive returns. Check for this, and if we find it count // it as *0* level change. // if ( opcode == 0xc3 ) { // // figure out the return address. It's the first 4 bytes on // the stack. // ULONG retaddr; KdpMoveMemory( (PCHAR)&retaddr, (PCHAR)ContextRecord->Esp, 4 ); if ( (KdpCurrentSymbolStart < retaddr) && (retaddr < KdpCurrentSymbolEnd) ) { // // returning to the current function. Either a finally // or a recursive return. Check for a leave. // KdpMoveMemory( (PCHAR)&opcode,(PCHAR)Pc-1, 1 ); if ( opcode != 0xc9 ) { // not a leave. Assume a try-finally. return 0; } } return -1; // not into current sym, or recursive return. } if ( (opcode == 0xc2) || // ret with arg opcode (opcode == 0xca) || (opcode == 0xcb) ) { // retf opcodes return -1; } return 0; } // KdpLevelChange LONG regValue( UCHAR reg, PCONTEXT ContextRecord ) { switch (reg) { case 0x0: return(ContextRecord->Eax); break; case 0x1: return(ContextRecord->Ecx); break; case 0x2: return(ContextRecord->Edx); break; case 0x3: return(ContextRecord->Ebx); break; case 0x4: return(ContextRecord->Esp); break; case 0x5: return(ContextRecord->Ebp); break; case 0x6: return(ContextRecord->Esi); break; case 0x7: return(ContextRecord->Edi); break; } } BOOLEAN KdpIsSpecialCall ( ULONG Pc, PCONTEXT ContextRecord ) /*++ Routine Description: Check to see if the instruction at pc is a call to one of the SpecialCall routines. Argument: Pc - program counter of instruction in question. --*/ { UCHAR opcode; UCHAR modRM; UCHAR sib; USHORT twoBytes; ULONG callAddr; ULONG addrAddr; LONG offset; ULONG i; char d8; KdpMoveMemory(( PCHAR)&opcode, (PCHAR)Pc, 1 ); if ( opcode == 0xe8 ) { // // Signed offset from pc // KdpMoveMemory( (PCHAR)&offset, (PCHAR)Pc+1, 4 ); callAddr = Pc + offset + 5; // +5 for instr len. } else if ( opcode == 0xff ) { KdpMoveMemory( (PCHAR)&modRM, (PCHAR)Pc+1, 1 ); if ( ((modRM & 0x30) == 0x00) || ((modRM & 0x30) == 0x30) ) { // not call or jump return FALSE; } if ( (modRM & 0x08) == 0x08 ) { // m16:16 or m16:32 -- we don't handle this return FALSE; } if ( (modRM & 0xc0) == 0xc0 ) { /* Direct register addressing */ callAddr = regValue( (UCHAR)(modRM&0x7), ContextRecord ); } else if ( (modRM & 0xc7) == 0x05 ) { // // ff15 or ff25 -- call or jump with disp32 // KdpMoveMemory( (PCHAR)&addrAddr, (PCHAR)Pc+2, 4 ); KdpMoveMemory( (PCHAR)&callAddr, (PCHAR)addrAddr, 4 ); } else if ( (modRM & 0x7) == 0x4 ) { LONG indexValue; /* sib byte present */ KdpMoveMemory( (PCHAR)&sib, (PCHAR)Pc+2, 1 ); indexValue = regValue( (UCHAR)((sib & 0x31) >> 3), ContextRecord ); switch ( sib&0xc0 ) { case 0x0: /* x1 */ break; case 0x40: indexValue *= 2; break; case 0x80: indexValue *= 4; break; case 0xc0: indexValue *= 8; break; } /* switch */ switch ( modRM & 0xc0 ) { case 0x0: /* no displacement */ if ( (sib & 0x7) == 0x5 ) { DPRINT(("funny call #1 at %x\n", Pc)); return FALSE; } callAddr = indexValue + regValue((UCHAR)(sib&0x7), ContextRecord ); break; case 0x40: if ( (sib & 0x6) == 0x4 ) { DPRINT(("Funny call #2\n")); /* calling into the stack */ return FALSE; } KdpMoveMemory( &d8, (PCHAR)Pc+3,1 ); callAddr = indexValue + d8 + regValue((UCHAR)(sib&0x7), ContextRecord ); break; case 0x80: if ( (sib & 0x6) == 0x4 ) { DPRINT(("Funny call #3\n")); /* calling into the stack */ return FALSE; } KdpMoveMemory( (PCHAR)&offset, (PCHAR)Pc+3, 4 ); callAddr = indexValue + offset + regValue((UCHAR)(sib&0x7), ContextRecord ); break; case 0xc0: ASSERT( FALSE ); break; } } else { //KdPrint(( "undecoded call at %x\n", // CONTEXT_TO_PROGRAM_COUNTER(ContextRecord) )); return FALSE; } } else if ( opcode == 0x9a ) { /* Absolute address call (best I can tell, cc doesn't generate this) */ KdpMoveMemory( (PCHAR)&callAddr, (PCHAR)Pc+1, 4 ); } else { return FALSE; } // // Calls across dll boundaries involve a call into a jump table, // wherein the jump address is set to the real called routine at DLL // load time. Check to see if we're calling such an instruction, // and if so, compute its target address and set callAddr there. // #if 0 KdpMoveMemory( (PCHAR)&twoBytes, (PCHAR)callAddr, 2 ); if ( twoBytes == 0x25ff ) { /* i386 is little-Endian; really 0xff25 */ // // This is a 'jmp dword ptr [mem]' instruction, which is the sort of // jump used for a dll-boundary crossing call. Fixup callAddr. // KdpMoveMemory( (PCHAR)&addrAddr, (PCHAR)callAddr+2, 4 ); KdpMoveMemory( (PCHAR)&callAddr, (PCHAR)addrAddr, 4 ); } #endif for ( i = 0; i < KdNumberOfSpecialCalls; i++ ) { if ( KdSpecialCalls[i] == callAddr ) { return TRUE; } } return FALSE; } /* * Find the return address of the current function. Only works when * locals haven't yet been pushed (ie, on the first instruction of the * function). */ ULONG KdpGetReturnAddress ( PCONTEXT ContextRecord ) { ULONG retaddr; KdpMoveMemory((PCHAR)(&retaddr), (PCHAR)(ContextRecord->Esp), 4 ); return retaddr; } // KdpGetReturnAddress VOID KdpSetLoadState( IN PDBGKD_WAIT_STATE_CHANGE WaitStateChange, IN PCONTEXT ContextRecord ) /*++ Routine Description: Fill in the Wait_State_Change message record for the load symbol case. Arguments: WaitStateChange - Supplies pointer to record to fill in ContextRecord - Supplies a pointer to a context record. Return Value: None. --*/ { WaitStateChange->ControlReport.Dr6 = KeGetCurrentPrcb()->ProcessorState.SpecialRegisters.KernelDr6; WaitStateChange->ControlReport.Dr7 = KeGetCurrentPrcb()->ProcessorState.SpecialRegisters.KernelDr7; WaitStateChange->ControlReport.ReportFlags = 0; } VOID KdpSetStateChange( IN PDBGKD_WAIT_STATE_CHANGE WaitStateChange, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN BOOLEAN SecondChance ) /*++ Routine Description: Fill in the Wait_State_Change message record. Arguments: WaitStateChange - Supplies pointer to record to fill in ExceptionRecord - Supplies a pointer to an exception record. ContextRecord - Supplies a pointer to a context record. SecondChance - Supplies a boolean value that determines whether this is the first or second chance for the exception. Return Value: None. --*/ { PKPRCB Prcb; BOOLEAN status; // // Set up description of event, including exception record // WaitStateChange->NewState = DbgKdExceptionStateChange; WaitStateChange->ProcessorLevel = KeProcessorLevel; WaitStateChange->Processor = (USHORT)KeGetCurrentPrcb()->Number; WaitStateChange->NumberProcessors = (ULONG)KeNumberProcessors; WaitStateChange->Thread = (PVOID)KeGetCurrentThread(); WaitStateChange->ProgramCounter = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ContextRecord); KdpQuickMoveMemory( (PCHAR)&WaitStateChange->u.Exception.ExceptionRecord, (PCHAR)ExceptionRecord, sizeof(EXCEPTION_RECORD) ); WaitStateChange->u.Exception.FirstChance = !SecondChance; // // Copy instruction stream immediately following location of event // WaitStateChange->ControlReport.InstructionCount = (USHORT)KdpMoveMemory( (PCHAR)(&(WaitStateChange->ControlReport.InstructionStream[0])), (PCHAR)(WaitStateChange->ProgramCounter), DBGKD_MAXSTREAM ); // // Copy context record immediately following instruction stream // if (KdpSendContext) { KdpMoveMemory( (PCHAR)(&WaitStateChange->Context), (PCHAR)ContextRecord, sizeof(*ContextRecord) ); } // // Clear breakpoints in copied area // status = KdpDeleteBreakpointRange( WaitStateChange->ProgramCounter, (PVOID)((PUCHAR)WaitStateChange->ProgramCounter + WaitStateChange->ControlReport.InstructionCount - 1) ); // // If there were any breakpoints cleared, recopy the area without them // if (status == TRUE) { KdpMoveMemory( &(WaitStateChange->ControlReport.InstructionStream[0]), WaitStateChange->ProgramCounter, WaitStateChange->ControlReport.InstructionCount ); } // // Special registers for the x86 // Prcb = KeGetCurrentPrcb(); WaitStateChange->ControlReport.Dr6 = Prcb->ProcessorState.SpecialRegisters.KernelDr6; WaitStateChange->ControlReport.Dr7 = Prcb->ProcessorState.SpecialRegisters.KernelDr7; WaitStateChange->ControlReport.SegCs = (USHORT)(ContextRecord->SegCs); WaitStateChange->ControlReport.SegDs = (USHORT)(ContextRecord->SegDs); WaitStateChange->ControlReport.SegEs = (USHORT)(ContextRecord->SegEs); WaitStateChange->ControlReport.SegFs = (USHORT)(ContextRecord->SegFs); WaitStateChange->ControlReport.EFlags = ContextRecord->EFlags; WaitStateChange->ControlReport.ReportFlags = REPORT_INCLUDES_SEGS; } VOID KdpGetStateChange( IN PDBGKD_MANIPULATE_STATE ManipulateState, IN PCONTEXT ContextRecord ) /*++ Routine Description: Extract continuation control data from Manipulate_State message Arguments: ManipulateState - supplies pointer to Manipulate_State packet ContextRecord - Supplies a pointer to a context record. Return Value: None. --*/ { PKPRCB Prcb; ULONG Processor; if (NT_SUCCESS(ManipulateState->u.Continue2.ContinueStatus) == TRUE) { // // If NT_SUCCESS returns TRUE, then the debugger is doing a // continue, and it makes sense to apply control changes. // Otherwise the debugger is saying that it doesn't know what // to do with this exception, so control values are ignored. // if (ManipulateState->u.Continue2.ControlSet.TraceFlag == TRUE) { ContextRecord->EFlags |= 0x100L; } else { ContextRecord->EFlags &= ~0x100L; } for (Processor = 0; Processor < (ULONG)KeNumberProcessors; Processor++) { Prcb = KiProcessorBlock[Processor]; Prcb->ProcessorState.SpecialRegisters.KernelDr7 = ManipulateState->u.Continue2.ControlSet.Dr7; Prcb->ProcessorState.SpecialRegisters.KernelDr6 = 0L; } if (ManipulateState->u.Continue2.ControlSet.CurrentSymbolStart != 1) { KdpCurrentSymbolStart = ManipulateState->u.Continue2.ControlSet.CurrentSymbolStart; KdpCurrentSymbolEnd = ManipulateState->u.Continue2.ControlSet.CurrentSymbolEnd; } } } VOID KdpReadControlSpace( IN PDBGKD_MANIPULATE_STATE m, IN PSTRING AdditionalData, IN PCONTEXT Context ) /*++ Routine Description: This function is called in response of a read control space state manipulation message. Its function is to read implementation specific system data. IMPLEMENTATION NOTE: On the X86, control space is defined as follows: 0: Base of KPROCESSOR_STATE structure. (KPRCB.ProcessorState) This includes CONTEXT record, followed by a SPECIAL_REGISTERs record Arguments: m - Supplies the state manipulation message. AdditionalData - Supplies any additional data for the message. Context - Supplies the current context. Return Value: None. --*/ { PDBGKD_READ_MEMORY a = &m->u.ReadMemory; STRING MessageHeader; ULONG Length, t; PVOID StartAddr; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; ASSERT(AdditionalData->Length == 0); if (a->TransferCount > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE))) { Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE); } else { Length = a->TransferCount; } if ((a->TargetBaseAddress < (PVOID)END_OF_CONTROL_SPACE) && (m->Processor < (USHORT)KeNumberProcessors)) { t = (ULONG)END_OF_CONTROL_SPACE - (ULONG)(a->TargetBaseAddress); if (t < Length) { Length = t; } StartAddr = (PVOID)((ULONG)a->TargetBaseAddress + (ULONG)&(KiProcessorBlock[m->Processor]->ProcessorState)); AdditionalData->Length = (USHORT)KdpMoveMemory( AdditionalData->Buffer, StartAddr, Length ); if (Length == AdditionalData->Length) { m->ReturnStatus = STATUS_SUCCESS; } else { m->ReturnStatus = STATUS_UNSUCCESSFUL; } a->ActualBytesRead = AdditionalData->Length; } else { AdditionalData->Length = 0; m->ReturnStatus = STATUS_UNSUCCESSFUL; a->ActualBytesRead = 0; } KdpSendPacket( PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, AdditionalData ); UNREFERENCED_PARAMETER(Context); } VOID KdpWriteControlSpace( IN PDBGKD_MANIPULATE_STATE m, IN PSTRING AdditionalData, IN PCONTEXT Context ) /*++ Routine Description: This function is called in response of a write control space state manipulation message. Its function is to write implementation specific system data. Control space for x86 is as defined above. Arguments: m - Supplies the state manipulation message. AdditionalData - Supplies any additional data for the message. Context - Supplies the current context. Return Value: None. --*/ { PDBGKD_WRITE_MEMORY a = &m->u.WriteMemory; ULONG Length; STRING MessageHeader; PVOID StartAddr; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; if ((((PUCHAR)a->TargetBaseAddress + a->TransferCount) <= (PUCHAR)END_OF_CONTROL_SPACE) && (m->Processor < (USHORT)KeNumberProcessors)) { StartAddr = (PVOID)((ULONG)a->TargetBaseAddress + (ULONG)&(KiProcessorBlock[m->Processor]->ProcessorState)); Length = KdpMoveMemory( StartAddr, AdditionalData->Buffer, AdditionalData->Length ); if (Length == AdditionalData->Length) { m->ReturnStatus = STATUS_SUCCESS; } else { m->ReturnStatus = STATUS_UNSUCCESSFUL; } a->ActualBytesWritten = Length; } else { AdditionalData->Length = 0; m->ReturnStatus = STATUS_UNSUCCESSFUL; a->ActualBytesWritten = 0; } KdpSendPacket( PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, AdditionalData ); UNREFERENCED_PARAMETER(Context); } VOID KdpReadIoSpace( IN PDBGKD_MANIPULATE_STATE m, IN PSTRING AdditionalData, IN PCONTEXT Context ) /*++ Routine Description: This function is called in response of a read io space state manipulation message. Its function is to read system io locations. Arguments: m - Supplies the state manipulation message. AdditionalData - Supplies any additional data for the message. Context - Supplies the current context. Return Value: None. --*/ { PDBGKD_READ_WRITE_IO a = &m->u.ReadWriteIo; STRING MessageHeader; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; ASSERT(AdditionalData->Length == 0); m->ReturnStatus = STATUS_SUCCESS; // // Check Size and Alignment // switch ( a->DataSize ) { case 1: a->DataValue = (ULONG)READ_PORT_UCHAR(a->IoAddress); break; case 2: if ((ULONG)a->IoAddress & 1 ) { m->ReturnStatus = STATUS_DATATYPE_MISALIGNMENT; } else { a->DataValue = (ULONG)READ_PORT_USHORT(a->IoAddress); } break; case 4: if ((ULONG)a->IoAddress & 3 ) { m->ReturnStatus = STATUS_DATATYPE_MISALIGNMENT; } else { a->DataValue = READ_PORT_ULONG(a->IoAddress); } break; default: m->ReturnStatus = STATUS_INVALID_PARAMETER; } KdpSendPacket( PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, NULL ); UNREFERENCED_PARAMETER(Context); } VOID KdpWriteIoSpace( IN PDBGKD_MANIPULATE_STATE m, IN PSTRING AdditionalData, IN PCONTEXT Context ) /*++ Routine Description: This function is called in response of a write io space state manipulation message. Its function is to write to system io locations. Arguments: m - Supplies the state manipulation message. AdditionalData - Supplies any additional data for the message. Context - Supplies the current context. Return Value: None. --*/ { PDBGKD_READ_WRITE_IO a = &m->u.ReadWriteIo; STRING MessageHeader; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; ASSERT(AdditionalData->Length == 0); m->ReturnStatus = STATUS_SUCCESS; // // Check Size and Alignment // switch ( a->DataSize ) { case 1: WRITE_PORT_UCHAR(a->IoAddress, (UCHAR)a->DataValue); break; case 2: if ((ULONG)a->IoAddress & 1 ) { m->ReturnStatus = STATUS_DATATYPE_MISALIGNMENT; } else { WRITE_PORT_USHORT(a->IoAddress, (USHORT)a->DataValue); } break; case 4: if ((ULONG)a->IoAddress & 3 ) { m->ReturnStatus = STATUS_DATATYPE_MISALIGNMENT; } else { WRITE_PORT_ULONG(a->IoAddress, a->DataValue); } break; default: m->ReturnStatus = STATUS_INVALID_PARAMETER; } KdpSendPacket( PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, NULL ); UNREFERENCED_PARAMETER(Context); } VOID KdpReadMachineSpecificRegister( IN PDBGKD_MANIPULATE_STATE m, IN PSTRING AdditionalData, IN PCONTEXT Context ) /*++ Routine Description: This function is called in response of a read MSR manipulation message. Its function is to read the MSR. Arguments: m - Supplies the state manipulation message. AdditionalData - Supplies any additional data for the message. Context - Supplies the current context. Return Value: None. --*/ { PDBGKD_READ_WRITE_MSR a = &m->u.ReadWriteMsr; STRING MessageHeader; LARGE_INTEGER l; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; ASSERT(AdditionalData->Length == 0); m->ReturnStatus = STATUS_SUCCESS; try { l.QuadPart = RDMSR(a->Msr); } except (EXCEPTION_EXECUTE_HANDLER) { l.QuadPart = 0; m->ReturnStatus = STATUS_NO_SUCH_DEVICE; } a->DataValueLow = l.LowPart; a->DataValueHigh = l.HighPart; KdpSendPacket( PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, NULL ); UNREFERENCED_PARAMETER(Context); } VOID KdpWriteMachineSpecificRegister( IN PDBGKD_MANIPULATE_STATE m, IN PSTRING AdditionalData, IN PCONTEXT Context ) /*++ Routine Description: This function is called in response of a write of a MSR manipulation message. Its function is to write to the MSR Arguments: m - Supplies the state manipulation message. AdditionalData - Supplies any additional data for the message. Context - Supplies the current context. Return Value: None. --*/ { PDBGKD_READ_WRITE_MSR a = &m->u.ReadWriteMsr; STRING MessageHeader; LARGE_INTEGER l; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; ASSERT(AdditionalData->Length == 0); m->ReturnStatus = STATUS_SUCCESS; l.HighPart = a->DataValueHigh; l.LowPart = a->DataValueLow; try { WRMSR (a->Msr, l.QuadPart); } except (EXCEPTION_EXECUTE_HANDLER) { m->ReturnStatus = STATUS_NO_SUCH_DEVICE; } KdpSendPacket( PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, NULL ); UNREFERENCED_PARAMETER(Context); } /*** KdpGetCallNextOffset - compute length of a call instruction * * Purpose: * Compute how many bytes are in a call-type instruction * so that a breakpoint can be set upon this instruction's * return. * * Returns: * length of instruction, or -1 if it wasn't a call instruction. * *************************************************************************/ ULONG KdpGetCallNextOffset ( ULONG Pc ) { UCHAR membuf[2]; UCHAR opcode; ULONG sib; ULONG disp; KdpMoveMemory( membuf, (PVOID)Pc, 2 ); opcode = membuf[0]; if ( opcode == 0xe8 ) { return Pc+5; } else if ( opcode == 0x9a ) { return Pc+7; } else if ( opcode == 0xff ) { sib = ((membuf[1] & 0x07) == 0x04) ? 1 : 0; disp = (membuf[1] & 0xc0) >> 6; switch (disp) { case 0: if ( (membuf[1] & 0x07) == 0x05 ) { disp = 4; // disp32 alone } else { // disp = 0; // no displacement with reg or sib } break; case 1: // disp = 1; // disp8 with reg or sib break; case 2: disp = 4; // disp32 with reg or sib break; case 3: disp = 0; // direct register addressing (e.g., call esi) break; } return Pc + 2 + sib + disp; } return 0; } // KdpGetCallNextOffset