Page 1 of 1

Don't I just feel like an idiot - need help w/ NASM 16-bit

Posted: Tue Jun 07, 2011 5:43 pm
by kdx7214
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

Re: Don't I just feel like an idiot - need help w/ NASM 16-b

Posted: Tue Jun 07, 2011 7:18 pm
by bluemoon
In 16bit real mode you probably need to use bx, si or di instead.

Code: Select all

mov bx, [pxe_seg]
mov es, bx
mov bx, [pse_off]
call [es:bx]
If you don't need return back, you may as well use push/retf

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

Posted: Tue Jun 07, 2011 7:19 pm
by kdx7214
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

Posted: Wed Jun 08, 2011 2:27 am
by Chandra
Did you try: call far [es:bx]?

Re: Don't I just feel like an idiot - need help w/ NASM 16-b

Posted: Wed Jun 08, 2011 3:06 am
by Chandra
Hi,

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
An alternative would be to create a far pointer and then make a far call :

Code: Select all

call far [es:bx]
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.

Re: Don't I just feel like an idiot - need help w/ NASM 16-b

Posted: Wed Jun 08, 2011 12:50 pm
by Brendan
Hi,
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
Make sure that pxe_off is immediately followed by pxe_seg in memory, then call it with indirect addressing; like this:

Code: Select all

             align 4
pxe_entry_point:
pxe_off:     dw 0
pxe_seg:     dw 0


    call far [pxe_entry_point]
You don't need to use any registers for this (only for setting the function's arguments).


Cheers,

Brendan

Re: Don't I just feel like an idiot - need help w/ NASM 16-b

Posted: Wed Jun 08, 2011 8:38 pm
by kdx7214
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 :D

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

Posted: Thu Jun 09, 2011 12:52 am
by Chandra
kdx7214 wrote: I'd love to know *why* this isn't working, but oh well
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'm just guessing, but would bet I've tried hundreds of combinations to see what would work.
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.

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.

Re: Don't I just feel like an idiot - need help w/ NASM 16-b

Posted: Sat Jun 11, 2011 7:25 pm
by kdx7214
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
Leaving the data stuff the same and using this works just fine though:

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
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

Re: Don't I just feel like an idiot - need help w/ NASM 16-b

Posted: Sun Jun 12, 2011 12:35 am
by Chandra
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 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: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
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.
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
then only this calling convention would be valid. But since it doesn't allow this, you've to use indirect addressing.

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]
To understand this, you need to know the difference between es:bx and [es:bx]. es:bx points to an address wheras [es:bx] points to the value contained in the address 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..............