Don't I just feel like an idiot - need help w/ NASM 16-bit
Don't I just feel like an idiot - need help w/ NASM 16-bit
I know I'm getting frustrated when I finally give in and create an account here (after literally years of lurking).
I'm working on a PXE boot loader for my kernel. I need to use the PXE API to load a few other files from the TFTP server. I've dug through the PXE doc and while it does have useful information, I've run into an odd stumbling block.
It's been so long since I've done 16-bit assembly that I've been unable to figure out the following MASM construct in NASM:
call dword ptr (s_PXE ptr es:[di]).EntryPointSP
What I have done is stored the !PXE entry point (segment and offset) into two memory areas (pxe_seg and pxe_off). I am trying to do something like the following:
mov ax, [pxe_seg]
mov es, ax
mov ax, [pse_off]
call es:ax
I've also tried the form "call es:[pxe_off]"
Both of these forms fail with the following error message from nasm:
pxeboot.asm:145: error: invalid combination of opcode and operands
I've gone back to the Intel manuals and for the life of me cannot find a solution that will work with NASM syntax.
Anyone have any help?
Thanks!
Mike
EDIT: Okay, have tried more experiments and found that the following will work using immediate values:
call 0x9e95:0x0106
I just cannot figure out how to make this work with the NASM syntax
I'm working on a PXE boot loader for my kernel. I need to use the PXE API to load a few other files from the TFTP server. I've dug through the PXE doc and while it does have useful information, I've run into an odd stumbling block.
It's been so long since I've done 16-bit assembly that I've been unable to figure out the following MASM construct in NASM:
call dword ptr (s_PXE ptr es:[di]).EntryPointSP
What I have done is stored the !PXE entry point (segment and offset) into two memory areas (pxe_seg and pxe_off). I am trying to do something like the following:
mov ax, [pxe_seg]
mov es, ax
mov ax, [pse_off]
call es:ax
I've also tried the form "call es:[pxe_off]"
Both of these forms fail with the following error message from nasm:
pxeboot.asm:145: error: invalid combination of opcode and operands
I've gone back to the Intel manuals and for the life of me cannot find a solution that will work with NASM syntax.
Anyone have any help?
Thanks!
Mike
EDIT: Okay, have tried more experiments and found that the following will work using immediate values:
call 0x9e95:0x0106
I just cannot figure out how to make this work with the NASM syntax
Last edited by kdx7214 on Tue Jun 07, 2011 7:36 pm, edited 1 time in total.
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
In 16bit real mode you probably need to use bx, si or di instead.
If you don't need return back, you may as well use push/retf
Code: Select all
mov bx, [pxe_seg]
mov es, bx
mov bx, [pse_off]
call [es:bx]
Code: Select all
push word [pxe_seg]
push word [pse_off]
retf
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
I thought of that but I need to return back to the caller
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
Did you try: call far [es:bx]?
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
Hi,
if you're still wondering how to make a far call, let me feed you:
An alternative would be to create a far pointer and then make a far call :
where [es:bx] points to that far pointer. This way, you don't need to use a variable.
Note that the far call pushes 'CS' on the stack along with the 'IP' so, the callee must return with retf.
Cheers.
if you're still wondering how to make a far call, let me feed you:
Code: Select all
mov ax,[pxe_seg]
shl eax,16 ; Load the upper 16 bits of eax with the segment
mov ax,[pxe_off] ; Load the lower 16 bits of eax with the offset
mov dword [address],eax
call far [address]
address dd 0 ; This is a variable declaration
Code: Select all
call far [es:bx]
Note that the far call pushes 'CS' on the stack along with the 'IP' so, the callee must return with retf.
Cheers.
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
Hi,
You don't need to use any registers for this (only for setting the function's arguments).
Cheers,
Brendan
Make sure that pxe_off is immediately followed by pxe_seg in memory, then call it with indirect addressing; like this:kdx7214 wrote:What I have done is stored the !PXE entry point (segment and offset) into two memory areas (pxe_seg and pxe_off). I am trying to do something like the following:
mov ax, [pxe_seg]
mov es, ax
mov ax, [pse_off]
call es:ax
Code: Select all
align 4
pxe_entry_point:
pxe_off: dw 0
pxe_seg: dw 0
call far [pxe_entry_point]
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
Well, after many hours of work I have a hackish workaround for this. I tried the indirect call method Brendan mentioned as well as every other suggestion. Turns out the problem is that NASM won't allow you to do that in a binary file. I'm just guessing, but would bet I've tried hundreds of combinations to see what would work.
I did hand assemble a far call with segment override and put it into the code with db and it works just fine. When I use that syntax with NASM it tells me you can't do that in a binary file.
My final workaround for what appears to be a NASM issue is what one poster recommended. I'm setting up the stack frame and doing a retf to make the call. It's the only method I've found to invoke the darn thing. Same goes with the Plug 'n Pray bios calls.
I hate 16-bit coding
Thanks for the suggestions everybody! I'd love to know *why* this isn't working, but oh well
I did hand assemble a far call with segment override and put it into the code with db and it works just fine. When I use that syntax with NASM it tells me you can't do that in a binary file.
My final workaround for what appears to be a NASM issue is what one poster recommended. I'm setting up the stack frame and doing a retf to make the call. It's the only method I've found to invoke the darn thing. Same goes with the Plug 'n Pray bios calls.
I hate 16-bit coding
Thanks for the suggestions everybody! I'd love to know *why* this isn't working, but oh well
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
I've been using NASM for years now and I'm able to make far calls. I'm sure it is not working because you didn't do it properly.kdx7214 wrote: I'd love to know *why* this isn't working, but oh well
And I bet none of the times you followed the correct procedure. Brendan gave the concise method you needed. I just don't believe it didn't work. I gave the fully working code I've been using all the time. I don't believe it didn't work, either. So, we'd be glad to see how you implemented these calls and what result you got.kdx7214 wrote:I'm just guessing, but would bet I've tried hundreds of combinations to see what would work.
And yes, do post the codes- that include different combinations of these calls, when you visit next time. Maybe we could figure out what's wrong.
Cheers.
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
Okay, here's the code taken directly from my pxe_call function. Remember it's 16-bit
and when it is run this way it locks the VM solidly.
Leaving the data stuff the same and using this works just fine though:
Here's another one that doesn't work (same circumstances - locks the VM or a real machine)
and when it is run this way it locks the VM solidly.
Code: Select all
pxe_call:
push ds
push di
push ax
call far [pxe_entry_point]
.done:
add sp, 6
ret
align 4
pxe_entry_point:
pxeapi_off dw 0x0000 ; !PXE API offset
pxeapi_seg dw 0x0000 ; !PXE API segment
Code: Select all
pxe_call:
push ds
push di
push ax
xor ebx, ebx
mov ax, [pxeapi_seg]
mov es, ax
mov bx, [pxeapi_off]
push cs
push .done
push ax
push bx
retf
.done:
add sp, 6
ret
Code: Select all
pxe_call:
push ds ; Push the PXE paramters
push di
push ax
mov ax, [pxeapi_seg]
mov es, ax
mov bx, [pxeapi_off]
call far [es:bx]
.done:
add sp, 6
ret
Re: Don't I just feel like an idiot - need help w/ NASM 16-b
This code works for me. I've tried this and it's completely working. Are you sure you're supplying correct address to pxeapi_seg:pxeapi_off pair?kdx7214 wrote:Okay, here's the code taken directly from my pxe_call function. Remember it's 16-bit
and when it is run this way it locks the VM solidly.
Code: Select all
pxe_call: push ds push di push ax call far [pxe_entry_point] .done: add sp, 6 ret align 4 pxe_entry_point: pxeapi_off dw 0x0000 ; !PXE API offset pxeapi_seg dw 0x0000 ; !PXE API segment
This didn't work becuase [es:bx] must point to the far pointer. But in your case, es:bx points to the far pointer wheras, [es:bx] points to the WORD at the call address. All far calls are made via a memory operand and hence they are 'indirect' calls. But you're issuing this call as a 'direct' one.kdx7214 wrote:Here's another one that doesn't work (same circumstances - locks the VM or a real machine)Code: Select all
pxe_call: push ds ; Push the PXE paramters push di push ax mov ax, [pxeapi_seg] mov es, ax mov bx, [pxeapi_off] call far [es:bx] .done: add sp, 6 ret
If NASM would have allowed something like this,
Code: Select all
mov ax, [pxeapi_seg]
mov es, ax
mov bx, [pxeapi_off]
call far es:bx
To make things more clear, remember that [es:bx] must be pointing to the far pointer. You cannot supply segment in the form of 'ES' and offset in the form of 'BX' then make a far call as: call far [es:bx]. That's because when you supply segment in the form of 'ES' and then offset in the form of 'BX' and make this call, what happens is that, instead of jumping to the address es:bx, the processor fetches the far pointer from [es:bx]. The result is that, the code contained at your destination address is supplied as the far pointer and hence the whole call becomes invalid.
Still confused? Consider this:
Say, 'es' is pointing to 0x0000 and 'bx' is pointing to 0x7C00. The intention is to make a call/jump to 0x0000:0x7C00.
Code: Select all
mov bx,0x0000
mov es,bx
mov bx,0x7c00
call far [es:bx]
What happens before you make this call is that, es:bx points to 0x0000:0x7C00. This implies, [es:bx] points to the WORD(16 bit value) at 0x0000:0x7C00; So when you make the far call, the WORD contained at 0x0000:0x7C00 is supplied as the far pointer (which actually is a pending instruction at that address, that we're supposed to execute). You know the rest......
I know it's kind of messed up but that's all I can explain..............
Programming is not about using a language to solve a problem, it's about using logic to find a solution !