|
|
|||||||
| Home | Register | Downloads | FAQ | Members List | Calendar | Arcade | Mark Forums Read |
» Less advertising throughout
» Post and participate in discussions
» Network with other forum members
» Free private messaging
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Last Xbox Emu Author
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jun 2004
Location: Seattle, WA, USA
Posts: 5,843
|
CALL instruction (x86)
Any x86 experts out there? Now, for quite some time I've been having some trouble writing this binary translator (it's a new x86 -> x86 core for my emulator... the interpreter is too slow and a pain to implement and maintain). The big problem is executing a call instruction. What I'm doing is byte encoding x86 instructions in an allocated pointer (char) and jumping to it's address to execute the code within the block and return with a RET instruction at the end. That works fine, but when emulating a specific opcode that can't be emulated easily (i.e. CPUID, WBINVD, IN/OUT, FENCE, etc), I need to make a call to a function that handles the code in software instead. Code:
// The real code is actually much more complicated than this. This is just an example/dramatization.
// void function containing code to emulate the wbinvd instruction
void wbinvd_inst()
{ ... }
// Code block that contains byte encoded x86 code.
unsigned char* code_block = malloc( size_of_code_block_in_bytes );
// Actual address to code block (we're calling this address directly from inline asm)
unsigned int* code_address = (usigned int*) code_block;
// Actual location of the function we're calling from byte code.
unsigned int* inst_address = (unsigned int*) wbinvd_inst;
code_block[0] = 0xE8; // CALL instruction
code_block[1] = (inst_address << 24 ) & 0xFF;
code_block[2] = (inst_address << 16 ) & 0xFF;
code_block[3] = (inst_address << 8 ) & 0xFF;
code_block[4] = (inst_address << 0 ) & 0xFF;
code_block[5] = 0xC3; // RET function (required to continue from the point where we called this block!)
// Call code address to execute this code.
__asm call code_block;
1. 0xE8: CALL rel32 - Call near, relative, displacement 2. 0xFF /2: CALL r/m32 - Call near, absolute indirect, address in r/m32 3. 0x9A: CALL ptr16:32 - Call far, absolute, address in operand 4. 0xFF /3: CALL m16:32 - Call far, absolute indirect address in m16:32 I know the CALL instruction I used in the example was encoded wrong, but that's just an illustration. So which one do you think I should use? Assuming that the code_block is in the data section, I'm guessing the far call would be my best bet. The last two I don't understand how to encode tbh and quite frankly, the Intel documentation doesn't tell me everything I want to know. Once I work this out, I can have some real progress. Any ideas?? Thanks.
__________________
![]() Official Website of Shogun3D's RyuAwai! Shogun3D Game Development Blog Zengjük a Dalt: Manliest Song Ever! ![]() |
|
|
|
| Advertisement | [Remove Advertisement] | ||
|
|
|
#2 |
|
You're already dead...
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Sep 2007
Location: Planet Vegeta
Posts: 5,385
|
pcsx2 uses 0xE8 when it does a CALL32(), in the recompilers. heres the code: Code:
/* call func */
_inline void CALLFunc( uptr func )
{
func -= ( (uptr)x86Ptr + 5 );
assert( (sptr)func <= 0x7fffffff && (sptr)func >= -0x7fffffff );
CALL32(func);
}
/* call rel32 */
_inline void CALL32( u32 to )
{
write8( 0xE8 );
write32( to );
}
so when you use the 0xE8 call function, you have to call from a relative address. as seen in CALLFunc(). (write8() and write32() write the code to x86ptr and increment it by 1 and by 4 respectively) i'm not an expert at x86 asm (i learned most of what i know from reading pcsx2's source code) but i hope that helps a bit
__________________
"It was, of course, a lie what you read about my religious convictions, a lie which is being systematically repeated. I do not believe in a personal God and I have never denied this but have expressed it clearly. If something is in me which can be called religious then it is the unbounded admiration for the structure of the world so far as our science can reveal it." - Albert Einstein check out my blog ![]() |
|
|
|
|
|
#3 |
|
Level 9998
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2006
Location: Java
Posts: 9,377
|
Mmm... use E8 when the opcodes of the sub-routine you are calling is near, in which case, what you feed to E8 is not an address, but more like the distance between the current pointer to that of the sub-routine. Otherwise, use 0xFF for an address call if you are feeding it an address. Note that what this means is that after 0xFF or 0xE8, you have to feed it 32 bits of either the displacement or the address. So anyway, this is a call to a sub-routine with address "XXXXXXXX": FFXXXXXXXX (This is literally PC = XXXXXXXX) And this is a call to a sub-routine whose distance from the current pointer is "XXXXXXXX": E8XXXXXXXX (This is literally PC += XXXXXXXX) Hope that's clearer now? That's how I understand it by the way. I usually just use FF, since FF can be optimized for near calls, anyway. Edit: Oh yeah, totally forgot this, but... if speed is absolutely a problem and the sub-routine isn't like millions of bytes after the current instruction then subtract the current pointer to the sub-routine's address and then feed the difference to 0xE8. 0xE8 is way faster than 0xFF when the sub-routine is extremely close, or when the app is small. cottonvibes: I think in PCSX2's case, func after having x86ptr and 5 substracted from it became the distance between the current pointer and that of the function. Why they don't just do 0xFF and func directly seems to either pertain to 0xE8 being faster at calling near functions, or because of that "x86ptr + 5", which made me raise my eyebrow. ;p
Last edited by runawayprisoner; November 21st, 2008 at 06:21.. |
|
|
|
|
|
#4 | |
|
You're already dead...
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Sep 2007
Location: Planet Vegeta
Posts: 5,385
|
Quote:
as i mentioned, write8 and write32 increment the pointer by 1 and 4 respectively. so 5 in total.
__________________
"It was, of course, a lie what you read about my religious convictions, a lie which is being systematically repeated. I do not believe in a personal God and I have never denied this but have expressed it clearly. If something is in me which can be called religious then it is the unbounded admiration for the structure of the world so far as our science can reveal it." - Albert Einstein check out my blog ![]() |
|
|
|
|
|
|
#5 |
|
Level 9998
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2006
Location: Java
Posts: 9,377
|
Oh wait... I get it now. Nope, it doesn't look like the write functions would increment the pointer at all. It just so happens that the next instruction (the call instruction, which would be written into the data pool) takes 5 bytes, and the relative call/jump happens afterwards, so the distance should also take 5 bytes into account, hence the +5. If 5 bytes weren't taken into account, the jump may skip an instruction or point to something else completely. Tsk... should have realized it. ASM has a lot of traps like that...
|
|
|
|
|
|
#6 | |
|
You're already dead...
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Sep 2007
Location: Planet Vegeta
Posts: 5,385
|
Quote:
please don't say i'm wrong, when you don't know what you're talking about.
__________________
"It was, of course, a lie what you read about my religious convictions, a lie which is being systematically repeated. I do not believe in a personal God and I have never denied this but have expressed it clearly. If something is in me which can be called religious then it is the unbounded admiration for the structure of the world so far as our science can reveal it." - Albert Einstein check out my blog ![]() |
|
|
|
|
|
|
#7 |
|
Level 9998
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2006
Location: Java
Posts: 9,377
|
Okay... yeah, I admit I'm wrong there, because I just assume that there is no need for the write functions to increment the pointer, you have a closer look at the source so you know more about it.
|
|
|
|
|
|
#8 |
|
Last Xbox Emu Author
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jun 2004
Location: Seattle, WA, USA
Posts: 5,843
|
Well, one thing's for sure, that implementation is much better than mine! Now I just need to figure out why all of a sudden my code keeps reading the address @ 0x00000000 everytime I use a global pointer. Anyone understand why this is?Thanks for the replies!
__________________
![]() Official Website of Shogun3D's RyuAwai! Shogun3D Game Development Blog Zengjük a Dalt: Manliest Song Ever! ![]() |
|
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|