Page 1 of 1

NASM

Posted: Tue Jun 11, 2002 11:00 pm
by krillzip
Hi, I am trying to fix little with asm and is pretty new to it.
I have to questions, here is the code


// C-code
extern unsigned char Input8(unsigned short port);

; ASM code
_Input8:
mov dx, [ebp + 4] ; Setting IO-port address into dx register
in  al, dx   ; Loading IO-port value into al register
mov [ebp], al   ; Moving IO value to stack
ret   ; Returning

Question 1:

How do I read and return parameters from the stack, as in the example using [ebp + something]. Well here is the code give me an example

2:

How do I compile an asm file with nasm into valid file.o format.

// krillzip

RE:NASM

Posted: Tue Jun 11, 2002 11:00 pm
by bdjames
(1)

(VS&PASCAL)

_Input8:
mov dx, [esp+4]
in al, dx
and eax, 0ffh
ret 2

(C)

_Input8:
mov dx, [esp+4]
in al, dx
and eax, 0ffh
ret

This can be donw in C:

unsigned short int port;
char result = inb(port);

(2)

Depends on compiler

http://board.win32asmcommunity.net/show ... threadid=3

RE:NASM

Posted: Tue Jun 11, 2002 11:00 pm
by Schol-R-LEA
In this case, which C compiler you are using (and in some cases, what OS you're developing under) would be more relevant than which assembler you using. Assuming you're using gcc under Linux, then the rules are as follows:

The registers EBX, ESP, EBP, ESI, and EDI are sacred; any function that uses them has to save them first and restore them before returning.

All other user-available registers are not preserved; if you have a value in them hat you'll need later, you have to save them before calling a C function, and restore them afterwards.

Return values 32 bits or less in size are passed through EAX; 64-bit values through EDX and EAX, with the high dword in EDX; all larger values are passed by value in EAX.

Arguments pushed on the stack are cleaned up after return, by the calling function; and

Arguments are pushed onto the stack frame in reverse order.  

Calling a C function is relatively simple: you would want to save anything you didn't want to lose (EAX, etc.), and push the arguments to it in the correct (reversed) order. That is, to call

int foo(int bar, char baz, float quux);

you would push them as

    mov  EAX, quux
    push EAX
    mov  AX,  baz
    push EAX    
    mov  AL,  bar
    push EAX
    call foo

The return value would be in EAX when the call completes. You would then have to clear them off the stack yourself, probably by the expedient of

    mov  ECX, ESP
    sub  ECX, 2 + 1 + 1 ; one quadword, one dword, and one less than dword
    mov  ESP, ECX


Handling a call from a C function is a bit more work, as you have to deal with the frame offsets of the variables.

Assuming that fee(int fie, char foe, float fum) were your assembly function, then the C caller would pass the variables as shown here:

addr             stack contents
------------------------------------------
...  |               ...                 |
FC28 |        (caller's stack frame)     |
FC24 | fum[4] | fum[5] | fum[6] | fum[7] |
FC20 | fum[0] | fum[1] | quux{2]| fum[3] |
FC1C | foe[0] | (empty)| (empty)| (empty)|
FC18 | fie[0] | fie[1] | fie[2] | fie[3] |
FC14 |         (return pointer)          |<------ stack pointer at calling time

(Addresses are arbitrary). Everything between the stack pointer (ESP) and the previous function's stack is pushed on the stack  at calling time by the caller. As a matter of convention, a C function would then push the EBP of the
old program and replace it with the ESP; this allows the caller to use the EBP as a frame pointer, while the stack is now free to be used. In the case of a C program, they would then push any other registers the function uses, followed by the auto variables. Past that, the stack would be used for temporary storage as usual.

When coding in assembly, you do not have to follow these conventions, as long as you are careful not to trash the arguments or the sacred registers, It is probably a wise idea, however, as it reduces the number of possible points of confusion. In any case, I will assume you are following them, in the examples below, to illustrate the issue clearly.


Given that, let's posit that fee() has two auto variables, 'int bang;' and 'char boom;', which would then go on the stack this way:

addr             stack contents
------------------------------------------
...  |               ...                 |
FC28 |        (caller's stack frame)     |
FC24 | fum[4] | fum[5] | fum[6] | fum[7] |
FC20 | fum[0] | fum[1] | quux{2]| fum[3] |
FC1C | foe[0] | (empty)| (empty)| (empty)|
FC18 | fie[0] | fie[1] | fie[2] | fie[3] |
FC14 |         (return pointer)          |
FC10 |          (caller's EBP)           |<------ current EBP points here
FC0C |          (caller's EBX)           |\
FC08 |          (caller's ESI)           | > only needed if you use these regs
FC04 |          (caller's EDI)           |/
FC10 | bang[0]| bang[1]| bang[2]| bang[3]|
FBFC | boom[0]| (empty)| (empty)| (empty)|                  
FBF8 |       (temporary values)          |<------ ESP now here
...  |                ...                |
------------------------------------------      


To access an argument on the stack, you need to use pointer offsets from the EBP. For example, to copy foe to boom by way of BL, you would do something like this (assuming you've already saved EBX):

    mov bl, [EBP + 0x08]
    mov [EBP - 0x10], bl

Normally the work of tracking the offsets is done by the compiler automagically, but when calling an assembly routine, the coder has to provide them manually. It may be useful to use a struct definition to help keep track of them. The struct should be in the calling order, not the order on the stack, so that the offsets are positive from the base pointer.

    struc fee_param
.fie  resw 1
.foe  resb 1,3
.fum  resd 2
    enstruc

    struc fee_local
.boom resb 1,3
.buzz resw 1
I believe you can then replace the previous with a construct like

       mov bl, [EBP + fee_param.foe]
       mov [ESP - fee_local.boom], bl

[Sources: _Assembly Language Step-by-step_ by Jeff Dunteman; the DJGPP homepage (http://www.delorie.com/djgpp/doc/ug/asm/calling.html);  Linux Assembly HOWTO (http://www.linux-embedded.com/howto/@$$ ... WTO-5.html)]

Return values, as stated before, are passed through EAX and EDX.

HTH. Corrections and additions are solicited and appreciated. TIA.

(Considered Silly: All this talk of function environments and state makes me wonder if you could feasibly write a library for C to support continuations... ah, the hell with it, it would either be a real mess, or would be highly non-portable, or both. Too little state to save, with too much of it being hardware specific. Still, it would be interesting, in a 'I can't believe it really works' sort of way...)

Better still...

Posted: Tue Jun 11, 2002 11:00 pm
by Schol-R-LEA
There's a tutorial on just this subject in the 'Developer's Guides' section on the main menu above. Don't feel bad about not noticing it; I missed it too, and I imagine so did most of the others here.

RE:NASM

Posted: Tue Jun 11, 2002 11:00 pm
by bdjames
_Input8:
mov dx, [esp+4]
in al, dx
ret 2

RE:NASM

Posted: Fri Jun 14, 2002 11:00 pm
by krillzip
OOPS..

I think didn't describe what I wanted in the right way.

Wath I'm trying to tell is that, the function I am working on is going to be called from within C++. From C/C++ you call the function called input8. The asm code is this function written in assembly, you see the C code is the C interface to the asm code.

What I wanted to know was, how do I link those asm functions to a C program/kernel.

I am using djgpp+nasm.

Sorry for the lack of description.

I have been summer-working the last days on a factory so I did not have the time to read the board for a while.

// krillzip

RE:NASM

Posted: Fri Jun 14, 2002 11:00 pm
by carbonBased
Just use the linker, like any object files (object files aren't languages specific)...

nasm -f coff blop.asm -o blop.o
gcc -c sauce.c -o sauce.o

ld -oformat bin blop.o sauce.o -o kernel.bin

Jeff