Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

DJGPP w/NASM (using PTC)

0 views
Skip to first unread message

Omega (Olivier Dagenais)

unread,
Feb 18, 1998, 3:00:00 AM2/18/98
to

I was wondering.....

I'm trying to rewrite my bitmap effects in assembler (NASM), but just
barebones code (grabbing data from the stack and MOVing it to
assembler variables) doesn't seem to work properly. The first thing
I noticed was that DJGPP was passing arguments "backwards",
c'est-à-dire from left-to-right, on the stack. (isn't this odd?)

I'm passing 4 unsigned char * and then 6 unsigned chars.
Three input buffers, one ouput buffer. I noticed they each took 4
bytes on the stack. Why? Can I fix that?

And I *am* to start reading on the stack, 8 bytes after the extended
base
pointer, right? {mov eax, [ebp + 8]}

I saw some reference to postfixing labels in NASM with
__FPUcPUcPUcPUcUcUcUcUcUcUc, matching my current case. This does not
link properly, maybe I declared it wrong in my C++ code?

What I get on the screen is garbage. At least I know the video
buffer (256K unsigned chars) is writing OK...so I'm assuming. I
tried a straight-copy of another buffer, but
to no apparent result on the screen. (same garbage no matter what I
outputted)

Any conventions that are specific to NASM, that I should be aware of?

Perhaps an example would help? I'm using RHIDE 1.4 and NASM 0.96..
(and PTC,
of course!)

An example piece of code would be appreciated, please and thank you.
Like copying a 320x200x8bit block to a 320x200x32 bit (ARGB) block.
(same byte per primary)

You know, something like that:

mov esi, [dest]
mov edi, 0
xor ecx, ecx
.loopstart:
xor ebx,ebx
mov bh, [edi + map1] ;yes, I could optimize, but suppose I
mov cl, bh ;do something with bh, then put it in cl
shl ecx, 16
mov cl, bh
mov ch, bh
mov [esi], ecx
add esi, 4
inc edi
cmp edi, 64000 - 1
jnz .loopstart

It's the code before I'd like to see; the set-up. I currently have:


offset equ 8

map1 dd 0
map2 dd 0
texture dd 0
dest dd 0

mov eax, [ebp + offset + 12] ;dest
mov [dest], eax
mov eax, [ebp + offset + 8] ;texture
mov [texture], eax
mov eax, [ebp + offset + 4] ;map2
mov [map2], eax
mov eax, [ebp + offset + 0] ;map1
mov [map1], eax


Thanks and werd to whoever can help me.

---------------------------------------------------------------------
Olivier A. Dagenais - 1st Year Computer Science @ Carleton University

ICQ: 2125127 - E-Mail: Om...@inorbit.com
http://wabakimi.carleton.ca/~odagenai/plasma.zip


Petteri Kangaslampi

unread,
Feb 19, 1998, 3:00:00 AM2/19/98
to

On 18 Feb 1998 18:34:07 GMT, Omega (Olivier Dagenais) <Om...@inorbit.com> wrote:
>I'm trying to rewrite my bitmap effects in assembler (NASM), but just
>barebones code (grabbing data from the stack and MOVing it to
>assembler variables) doesn't seem to work properly. The first thing
>I noticed was that DJGPP was passing arguments "backwards",
>c'est-à-dire from left-to-right, on the stack. (isn't this odd?)

Remember that the stack grows downwards from memory, what you think is
"backwards" might actually be forward. I think the arguments are pushed
onto the stack from left to right normally in C calling, but I'm not 100%
sure.


>I'm passing 4 unsigned char * and then 6 unsigned chars.
>Three input buffers, one ouput buffer. I noticed they each took 4
>bytes on the stack. Why? Can I fix that?

All values in stack always take 4 bytes (or a multiple of that) in 32-bit
modes, so there is nothing you can do about this. Not that you'd really
want to anyway, as unaligned memory accesses are _very_ slow.


>And I *am* to start reading on the stack, 8 bytes after the extended
>base
>pointer, right? {mov eax, [ebp + 8]}

4 bytes for the return address, plus 4 bytes per each register you have
pushed to stack. Note that you can use esp directly to access the memory
in 32-bit modes, you don't need to use ebp. There you'll have to keep
track of all pushes and pops you may have done though.


>I saw some reference to postfixing labels in NASM with
>__FPUcPUcPUcPUcUcUcUcUcUcUc, matching my current case. This does not
>link properly, maybe I declared it wrong in my C++ code?

Urgh, are you trying to mangle the function names like the C++ compiler?
A better idea would be to declare the functions as extern "C" in the C++
size, and just use C naming conventions.

extern "C" void foo(void);

GLOBAL _foo
_foo: ...

or something like that, I'm not sure about the underscore with GCC.


Petteri

Todd M Zimnoch

unread,
Feb 19, 1998, 3:00:00 AM2/19/98
to

Excerpts from netnews.comp.sys.ibm.pc.demos: 19-Feb-98 Re: DJGPP w/NASM
(using PTC) by Petteri Kangaslampi@sci.
> >assembler variables) doesn't seem to work properly. The first thing
> >I noticed was that DJGPP was passing arguments "backwards",
> >c'est-à-dire from left-to-right, on the stack. (isn't this odd?)
>
> Remember that the stack grows downwards from memory, what you think is
> "backwards" might actually be forward. I think the arguments are pushed
> onto the stack from left to right normally in C calling, but I'm not 100%
> sure.

C and Pascal compilers each parse function arguments in reverse
direction. Try using PASCAL as part of your function declaration. I do
believe that all winapi are declared as far PASCAL. At least winmain
and windowprocs are. Why this is done, I'll never know.

Kneebiter


Omega (Olivier Dagenais)

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

> >assembler variables) doesn't seem to work properly. The first
thing
> >I noticed was that DJGPP was passing arguments "backwards",
> >c'est-à-dire from left-to-right, on the stack. (isn't this odd?)
> Remember that the stack grows downwards from memory, what you think
is
> "backwards" might actually be forward. I think the arguments are
pushed
> onto the stack from left to right normally in C calling, but I'm
not 100%
> sure.

I thought it was right to left. Unless that was in real mode? Or
maybe I forget. I know that real-mode assembler libraries with
Quick-Basic had right-to-left parameter-passing.

> All values in stack always take 4 bytes (or a multiple of that) in
32-bit
> modes, so there is nothing you can do about this. Not that you'd
really
> want to anyway, as unaligned memory accesses are _very_ slow.

Ah... See, I didn't know that. Again, I was used to real-mode
libraries and using this:
routine_ PROC variablew:word, variabled:dword

with TASM and Watcom.....

> >And I *am* to start reading on the stack, 8 bytes after the
extended
> >base
> >pointer, right? {mov eax, [ebp + 8]}
> 4 bytes for the return address, plus 4 bytes per each register you
have
> pushed to stack. Note that you can use esp directly to access the
memory
> in 32-bit modes, you don't need to use ebp. There you'll have to
keep
> track of all pushes and pops you may have done though.

Well, ebp or esp they're both initially equal. I should read the
first argument as
mov eax, [ebp + 4] ???
Why do all the examples of NASM with DJGPP use 8? Return value? (I
guess that has to be on the stack somewhere...)

> >I saw some reference to postfixing labels in NASM with
> >__FPUcPUcPUcPUcUcUcUcUcUcUc, matching my current case. This does
not
> >link properly, maybe I declared it wrong in my C++ code?
> Urgh, are you trying to mangle the function names like the C++
compiler?

It was an option I read about in a NASM/DJGPP tutorial, for some
strange reason. I thought it was the same as the TASM PROC
declaration.

> A better idea would be to declare the functions as extern "C" in
the C++
> size, and just use C naming conventions.
> extern "C" void foo(void);
> GLOBAL _foo
> _foo: ...

That's what I had. Figured that might have been the problem.

> or something like that, I'm not sure about the underscore with GCC.

Yep, underscore. Well, with NASM.

I'm still puzzled.. Can anybody post (send me) a working example?


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Olivier A. Dagenais - 1st Year Comp. Sci. @ Carleton University

ICQ: 2125127 - E-Mail: Om...@inorbit.com

http://chat.carleton.ca/~odagenai/plasma.zip
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
A nerd is a guy who spends all day in front of his computer..
A geek is a nerd that's proud of it.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Michael Anttila

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

|| C and Pascal compilers each parse function arguments in reverse
|| direction. Try using PASCAL as part of your function declaration. I do
|| believe that all winapi are declared as far PASCAL. At least winmain
|| and windowprocs are. Why this is done, I'll never know.

I think people are a little confused here... The way I learned it, C (at
least, Turbo C) pushes parameters in reverse order, i.e:
"test(i,j)" becomes:
"push word ptr j
push word ptr i
call test
add sp,4"

Notice that C also has to clean the parameters off the stack after the
function call. It doesn't have anything to do with pushing the parameters
in reverse order, it's just part of the wacky convention.

Pascal calling conventions state that the parameters are pushed in order,
and that the called function is responsible for cleaning the stack. Thus
"test(i,j)" becomes:
"push word ptr i
push word ptr j
call test"

In my opinion, the Pascal method is a bit nicer, since you don't have to
do any stack maintenance yourself (and with the "ret imm16" instruction,
the called function can easily clean the stack itself).

Not sure why the C convention was developed... Perhaps they wanted to be
able to push a variable number of parameters more easily. Who knows...

-PsychoMan
___________________________________________________________
3A Pure Math / Computer Science at U. of Waterloo, Canada
E-Mail: mant...@undergrad.math.uwaterloo.ca


Petteri Kangaslampi

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

[The discussion was about mixing DJGPP and NASM. I posted this to the
comp.os.msdos.djgpp newsgroup as well, in case someone there finds this
useful or finds errors in my explanation.]

On 20 Feb 1998 02:25:47 GMT, Omega (Olivier Dagenais) <Om...@inorbit.com> wrote:
>[I wrote:]


>> "backwards" might actually be forward. I think the arguments are
>pushed
>> onto the stack from left to right normally in C calling, but I'm
>not 100%
>> sure.
>
>I thought it was right to left. Unless that was in real mode? Or
>maybe I forget. I know that real-mode assembler libraries with
>Quick-Basic had right-to-left parameter-passing.

Now that I think about it more carefully, C arguments are pushed to the
stack from right to left. Otherwise functions with variable number of
arguments wouldn't be possible. Now when arguments are pushed from right
to left, the code always knows where the first arguments are (right before
the return address), and can deduce the actual number of arguments from
them.


>Well, ebp or esp they're both initially equal. I should read the
>first argument as
>mov eax, [ebp + 4] ???
>Why do all the examples of NASM with DJGPP use 8? Return value? (I
>guess that has to be on the stack somewhere...)

ebp and esp are NOT initially equal unless you make them. The reason most
examples use [ebp+8] and so forth is, that they push ebp to the stack
before setting ebp equal to esp. Then there are 8 bytes of data in the
stack after the arguments, 4 for the return address and 4 for the old ebp
value.

A traditional cdecl function with one argument looks something like this:

_foo:
push ebp
mov ebp,esp
mov eax,[ebp+8]
inc eax
pop ebp
ret

This would give you
extern "C" int foo(int a);
which just returns a+1.

Alternatively, you could access the argument directly with esp:

_foo:
mov eax,[esp+4]
inc eax
ret

Note that the displacement is now 4, because we didn't push anything to
the stack. I prefer to use esp myself, as then I'll have ebp free for
other uses, but you'll need to track all pushes and pops you do in the
code and modify the displacements accordingly.


>I'm still puzzled.. Can anybody post (send me) a working example?

Hope this helps. Try starting with as simple function as possibly, like my
examples above, and gradually build on that. Another possible strategy is
to write a skeleton function in C first, ask the compiler to generate
assembler source from that or disassemble the object file, and see how it
does its magic.


Petteri

Sam

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

On Thu, 19 Feb 1998, Todd M Zimnoch wrote:

> C and Pascal compilers each parse function arguments in reverse
> direction. Try using PASCAL as part of your function declaration. I do
> believe that all winapi are declared as far PASCAL. At least winmain
> and windowprocs are. Why this is done, I'll never know.

Because Pascal call conventions are 5% more efficient (I just remember
that figure vaguely, so it might be wrong) than C call conventions and
somebody decided this was important once upon a time, probably in Windows
1.0.

(Of course, C compilers nowadays use different/modified call conventions
anyway [c++, register passing, etc] so I'm not sure the efficiency things
still apply.)

--sam

Todd M Zimnoch

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

Excerpts from netnews.comp.sys.ibm.pc.demos: 20-Feb-98 Re: DJGPP w/NASM
(using PTC) by Michael Anttila@laplace.
> "push word ptr j
> push word ptr i
> call test
> add sp,4"
>
> Notice that C also has to clean the parameters off the stack after the
> function call. It doesn't have anything to do with pushing the parameters
> in reverse order, it's just part of the wacky convention.

This is not just a wacky convention, it affects the code performance
you expect.

void blah( int a, int b );
...
int x = 0;
blah( x++, x++ );

In the call to blah, a = 1 and b = 0! Rather than the other way
around, which you'd expect.

> Pascal calling conventions state that the parameters are pushed in order,
> and that the called function is responsible for cleaning the stack. Thus
> "test(i,j)" becomes:
> "push word ptr i
> push word ptr j
> call test"
>
> In my opinion, the Pascal method is a bit nicer, since you don't have to
> do any stack maintenance yourself (and with the "ret imm16" instruction,
> the called function can easily clean the stack itself).
>
> Not sure why the C convention was developed... Perhaps they wanted to be
> able to push a variable number of parameters more easily. Who knows...

I hadn't thought about a variable number of parameters reason, but
that makes perfect sense. In any case, I was suggesting a reason why it
looks like his parameters are being pushed in the wrong order.

Kneebiter


Omega (Olivier Dagenais)

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

> Now that I think about it more carefully, C arguments are pushed to
the
> stack from right to left. Otherwise functions with variable number
of
> arguments wouldn't be possible. Now when arguments are pushed from
right
> to left, the code always knows where the first arguments are (right
before
> the return address), and can deduce the actual number of arguments
from
> them.

Well, my statements were confusing.. I always write weird.


You are saying:

void pif(char a, char b, word c)

would create a stack that looks like:

ppppccccbbbbaaaa

Where pppp is the return address (4 bytes) and the other arguments
take four bytes each. Right?

> >Why do all the examples of NASM with DJGPP use 8? Return value?
(I
> >guess that has to be on the stack somewhere...)
> ebp and esp are NOT initially equal unless you make them. The
reason most
> examples use [ebp+8] and so forth is, that they push ebp to the
stack
> before setting ebp equal to esp. Then there are 8 bytes of data in
the
> stack after the arguments, 4 for the return address and 4 for the
old ebp
> value.

Right, I had not thought of that. The first thing done is push ebp;
mov ebp, esp

> A traditional cdecl function with one argument looks something like
this:

What does _cdecl_ stand for again?

> _foo:
> push ebp
> mov ebp,esp
> mov eax,[ebp+8]
> inc eax
> pop ebp
> ret
> This would give you
> extern "C" int foo(int a);
> which just returns a+1.

I'm doing that. Hold on a sec. Does this look OK?

;============ start code ================
BITS 32

GLOBAL _mapper

%define align times ($$ - $) & 3 nop

[SECTION .text]

map1 dd 0
map2 dd 0
texture dd 0
dest dd 0

xar db 0
yar db 0
xag db 0
yag db 0
xab db 0
yab db 0

;
----------------------------------------------------------------------
-----
; Prototype: void mapper ( unsigned char *map1, unsigned char *map2,
; unsigned char *texture, unsigned char
*dest,
; unsigned char xar, unsigned char yar,
; unsigned char xag, unsigned char yag,
; unsigned char xab, unsigned char yab )
; Returns: absolutely nothing!
;
----------------------------------------------------------------------
-----
offset equ 8

align ; should align on dword boundary

_Mapper:
push ebp
mov ebp, esp

push ebx
push esi
push edi

mov al, [ebp + offset + 36] ;yab
mov [yab], al
mov al, [ebp + offset + 32] ;xab
mov [xab], al
mov al, [ebp + offset + 28] ;yag
mov [yag], al
mov al, [ebp + offset + 24] ;xag
mov [xag], al
mov al, [ebp + offset + 20] ;yar
mov [yar], al
mov al, [ebp + offset + 16] ;xar
mov [xar], al

mov eax, [ebp + offset + 12] ;dest
mov [dest], eax
mov eax, [ebp + offset + 8] ;texture
mov [texture], eax
mov eax, [ebp + offset + 4] ;map2
mov [map2], eax
mov eax, [ebp + offset + 0] ;map1
mov [map1], eax

mov esi, [dest]


mov edi, 0
xor ecx, ecx
.loopstart:
xor ebx,ebx
mov bh, [edi + map1]

mov bl, [edi + map2]
mov cl, bl
shl ecx, 16
mov cl, bl
mov ch, bl


mov [esi], ecx
add esi, 4
inc edi
cmp edi, 64000 - 1
jnz .loopstart

pop edi
pop esi
pop ebx

mov esp, ebp
pop ebp
ret

;============ end code ================

That should copy the contents on MAP2 on the ARGB screen, which is
then copied to screen, using PTC....
Does anybody see anything wrong????

> >I'm still puzzled.. Can anybody post (send me) a working example?
> Hope this helps. Try starting with as simple function as possibly,
like my
> examples above, and gradually build on that. Another possible
strategy is
> to write a skeleton function in C first, ask the compiler to
generate
> assembler source from that or disassemble the object file, and see
how it
> does its magic.

I tried to build on small, but I have many arguments to my function.
That, and decompiling the assembler generated by DJGPP will give me
AT&T-type, I think. (shudder)

Thanks.

Petteri Kangaslampi

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

On 20 Feb 1998 16:10:22 GMT, Omega (Olivier Dagenais) <Om...@inorbit.com> wrote:
>You are saying:
>
>void pif(char a, char b, word c)
>
>would create a stack that looks like:
>
>ppppccccbbbbaaaa
>
>Where pppp is the return address (4 bytes) and the other arguments
>take four bytes each. Right?

Umm, more like:
cccc
bbbb
aaaa
pppp <- esp

where pppp is in the _lowest_ address and cccc in _highest_. Remember,
once again, that esp is _decremented_ each time something is pushed
to the stack


>What does _cdecl_ stand for again?

Most PC compilers use it to stand for the normal C calling convention,
GCC uses a similar convention by default.


>I'm doing that. Hold on a sec. Does this look OK?

[clip]

On a quick look yes, at least the argument passing part, but I'd
really suggest you start with a simple function that takes just one
or two arguments and simply adds them together or something. That
way you have less code that might be a potential problem.

However, this

> mov esp, ebp

looks very suspicious. Never EVER move something to esp unless you
really know what you are doing. 99% of the time esp gets modified
correctly automagically when you push and pop things and call
functions. Just make sure you pair each push with a pop, and each
call with a ret.


Petteri

KNakasato

unread,
Feb 20, 1998, 3:00:00 AM2/20/98
to

In article <slrn6eqrv5....@sci.fi>, peka...@sci.fi (Petteri
Kangaslampi)
>Now that I think about it more carefully, C arguments are pushed to the
>stack from right to left. Otherwise functions with variable number of
>arguments wouldn't be possible. Now when arguments are pushed from right
>to left, the code always knows where the first arguments are (right before
>the return address), and can deduce the actual number of arguments from

This can depends on compiler and calling convention. GCC has option to
use 3 regs for parameter passing too.

>Alternatively, you could access the argument directly with esp:
>
>_foo:
> mov eax,[esp+4]

Thanks, I forgot about esp.


Aloha

0 new messages