Emuforums.com

Go Back   Emuforums.com > General Discussion > Web development / Programming
Home Register Downloads FAQ Members List Calendar Arcade Mark Forums Read

WON'T YOU JOIN US?
You are not a registered member and
are viewing this site as a guest.
Registration is simple and FREE.
Join this CrowdGather community today.
Registration offers the following perks:

» Less advertising throughout
» Post and participate in discussions
» Network with other forum members
» Free private messaging

join

Reply
 
Thread Tools Display Modes
Old November 21st, 2008, 03:28   #1
blueshogun96
Last Xbox Emu Author
 
blueshogun96's Avatar
 
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;
The problem is that I need to byte encode the CALL instruction myself and I'm not sure which one to use. I'm assuming that the code is being called from the data section (DS). Here's a list of different CALL variations for x86 (32-bit versions only):

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!
blueshogun96 is offline   Reply With Quote

Advertisement [Remove Advertisement]
Old November 21st, 2008, 03:49   #2
cottonvibes
You're already dead...
 
cottonvibes's Avatar
 
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
cottonvibes is offline   Reply With Quote
Old November 21st, 2008, 06:09   #3
runawayprisoner
Level 9998
 
runawayprisoner's Avatar
 
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..
runawayprisoner is offline   Reply With Quote
Old November 21st, 2008, 07:36   #4
cottonvibes
You're already dead...
 
cottonvibes's Avatar
 
Join Date: Sep 2007
Location: Planet Vegeta
Posts: 5,385
Quote:
Originally Posted by runawayprisoner View Post
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
the x86ptr+5 is because after CALL32() is called, it increments x86ptr by 5.
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
cottonvibes is offline   Reply With Quote
Old November 21st, 2008, 09:01   #5
runawayprisoner
Level 9998
 
runawayprisoner's Avatar
 
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...
runawayprisoner is offline   Reply With Quote
Old November 21st, 2008, 09:13   #6
cottonvibes
You're already dead...
 
cottonvibes's Avatar
 
Join Date: Sep 2007
Location: Planet Vegeta
Posts: 5,385
Quote:
Originally Posted by runawayprisoner View Post
Nope, it doesn't look like the write functions would increment the pointer at all.
the write instructions do increment x86ptr.
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
cottonvibes is offline   Reply With Quote
Old November 21st, 2008, 10:02   #7
runawayprisoner
Level 9998
 
runawayprisoner's Avatar
 
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.
runawayprisoner is offline   Reply With Quote
Old November 22nd, 2008, 01:35   #8
blueshogun96
Last Xbox Emu Author
 
blueshogun96's Avatar
 
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!
blueshogun96 is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

All times are GMT +1. The time now is 15:01.

© 2006 - 2012 Emu Forums | About Emu Forums | Advertisers | Investors | Legal | A member of the Crowdgather Forum Community


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2013, vBulletin Solutions, Inc.