Author Topic: Mixing C and Assembly parameter problem  (Read 22197 times)

Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Mixing C and Assembly parameter problem
« on: March 06, 2012, 01:08:44 PM »
Hi,

I'm programming a little C programme with assembly functions.
This is my code:

Code: [Select]
global start
global print
global stop

start:

stop:
mov ah, 0x0
int 0x16
mov ah, 0x4C
int 0x21
ret

print:
push bp
mov bp, sp
mov al, [bp+8]
mov ah, 0x0E
int 0x10
mov sp, bp
pop bp
ret


And my C code
Code: [Select]
extern void stop();
extern void print(char character);
 
int main() {
print('a');
stop();
    return 0;
}

The problem is that there's always shown q (ascii 113) but not a.
Thanks in advance.

EDIT:
I use GCC and NASM.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Mixing C and Assembly parameter problem
« Reply #1 on: March 06, 2012, 04:46:21 PM »
Hi isywum,

I think we're above my pay-grade here. I would have said that gcc won't do 16-bit code, period, but this is apparently not correct. The fact that you're getting it to compile/link at all, and seeing any character at all is a very good sign!

Assuming you're actually getting gcc to compile 16-bit code, I would expect the first parameter to be at [bp + 4] if it's a near call, or [bp + 6] if it's a far call - should(?) depend on the "model" directive, or whatever model it defaults to. Easy to try either or both, anyway, and see if it helps.

Googling "gcc 16 bit" turns up some info on the subject - as usual, the "osdev" stuff seems most helpful(?). Let us know if you get it to work. I'm old as the hills (67), but still willing to learn!

Best,
Frank


Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #2 on: March 06, 2012, 05:00:51 PM »
Hi,

this is the tutorial I've read: http://courses.engr.illinois.edu/ece390/books/labmanual/c-prog-mixing.html
It looks like I wouldn't get the paramters (stack) but I actually don't know why I shouldn't ...
I looking for the thing I think to work but that doesn't :D I'm gonna read some articles from osdever.net and tell you more if I have some information ...
Thank you.

Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #3 on: March 11, 2012, 12:28:34 PM »
I think I start understanding the problem.
Parameters are saved in the stack pointer (sp).
The paramters should be at [sp+8] because of the call and the push bp.
So I disassemblied the file and saw that there are some other "push"s before the call.
But how can I know where my paramters start?
Can I "pop" the last "push"?

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Mixing C and Assembly parameter problem
« Reply #4 on: March 11, 2012, 11:18:31 PM »
Hi isywum,

The tutorial you link to up above is good, but the next chapter on 16-bit code is what you want.

No, you can't just "pop" the parameter. The "call" instruction puts the return address on the stack, your parameters are above that... and here is a complication with 16-bit code! If it's a "near" call, only the offset part of the address is on the stack, if it's a "far" call, both the offset and the segment are on the stack (and you need to return from your function with "retf"). Since [sp + ?] is not a valid addressing mode in 16-bit code, we have to use bp, and we need to save it on the stack (caller is expecting it to be preserved). So I would expect your first parameter to be at [bp + 4] (near call, return address offset and bp on the stack), or [bp + 6] (far call, return address offset and segment and bp on the stack). Since I don't have a clue which kind of call gcc generates, I'd look for the first parameter by "trial and error". Since you've got the file disassembled, you can probably tell from that if it's a plain "call" or a "call far". If parameters are passed "by reference" (the address of the data, not its value), there's an issue with whether it's "far data" (both offset and segment is passed) or "near data" (only the offset part of the address is passed). 16-bit code is a real PITA! I'll be glad when I've forgotten it entirely (coming soon - my memory is pretty bad!).

If you can't get it (re-read that chapter on 16-bit), maybe it would help if you posted the relevant part of your disassembly(?).

Best,
Frank


Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #5 on: March 12, 2012, 04:06:14 PM »
Hi,

I changed the code but it shouldn't matter:

Code: [Select]
00000000  E80300            call word 0x6
00000003  0000              add [bx+si],al
00000005  0000              add [bx+si],al
00000007  008D4C24          add [di+0x244c],cl
0000000B  0483              add al,0x83
0000000D  E4F0              in al,0xf0
0000000F  FF71FC            push word [bx+di-0x4]
00000012  55                push bp
00000013  89E5              mov bp,sp
00000015  51                push cx
00000016  83EC10            sub sp,byte +0x10
00000019  6A09              push byte +0x9
0000001B  E83000            call word 0x4e
0000001E  0000              add [bx+si],al
00000020  83C410            add sp,byte +0x10
00000023  83F809            cmp ax,byte +0x9
00000026  7505              jnz 0x2d
00000028  E84300            call word 0x6e
0000002B  0000              add [bx+si],al
0000002D  E83E00            call word 0x6e
00000030  0000              add [bx+si],al
00000032  E84900            call word 0x7e
00000035  0000              add [bx+si],al
00000037  B80000            mov ax,0x0
0000003A  0000              add [bx+si],al
0000003C  8B4DFC            mov cx,[di-0x4]
0000003F  C9                leave
00000040  8D61FC            lea sp,[bx+di-0x4]
00000043  C3                ret
00000044  0000              add [bx+si],al
00000046  0000              add [bx+si],al
00000048  0000              add [bx+si],al
0000004A  0000              add [bx+si],al
0000004C  0000              add [bx+si],al
0000004E  0000              add [bx+si],al
00000050  6655              push ebp
00000052  6689E5            mov ebp,esp
00000055  678A460C          mov al,[esi+0xc]
00000059  0430              add al,0x30
0000005B  B40E              mov ah,0xe
0000005D  CD10              int 0x10
0000005F  B400              mov ah,0x0
00000061  665D              pop ebp
00000063  C3                ret
00000064  0000              add [bx+si],al
00000066  0000              add [bx+si],al
00000068  0000              add [bx+si],al
0000006A  0000              add [bx+si],al
0000006C  0000              add [bx+si],al
0000006E  0000              add [bx+si],al
00000070  B400              mov ah,0x0
00000072  CD16              int 0x16
00000074  C3                ret
00000075  0000              add [bx+si],al
00000077  0000              add [bx+si],al
00000079  0000              add [bx+si],al
0000007B  0000              add [bx+si],al
0000007D  0000              add [bx+si],al
0000007F  00B44CCD          add [si-0x32b4],dh
00000083  21C3              and bx,ax

That's my new code:
start.asm
Code: [Select]
global start

start:
extern main
call main

main.c
Code: [Select]
extern int paramter(int i);
extern void read();
extern void stop();

int main() {
if (paramter(9) == 9) {
read();
}
read();
stop();
return 0;
}

read.asm
Code: [Select]
global read

read:
mov ah, 0x0
int 0x16
ret

parameter.asm
Code: [Select]
global paramter

paramter:
push bp
mov bp, sp
mov al, [bp+8]
add al, 48
mov ah, 0x0E
int 0x10
mov ah, 0
pop bp
ret

stop.asm
Code: [Select]
global stop

stop:
mov ah, 0x4C
int 0x21
ret

When I try retf instead of ret at paramter, I get errors from the ntvdm.exe.
[sp+0], [sp+1], and so on to [sp+9] is zero. I don't know why...
I hope the disassembly file help. :)

Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #6 on: March 13, 2012, 02:23:59 PM »
I found the adress of paramter now! :)
It's [bp+26]. I absolutely don't why but it works in that situation.
But the problem isn't solved because next time I use the function it is another address :D

EDIT:
I think that one of the big problems is that retf doesn't work.
Does call word 0xWhatever mean call far 0xWhatever?
« Last Edit: March 13, 2012, 02:42:47 PM by isywum »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Mixing C and Assembly parameter problem
« Reply #7 on: March 13, 2012, 07:30:26 PM »
Harumph! Well, harumph, I say! I posted (or tried to) an analysis of your disassembly, but it seems to have vanished. I HATE it when that happens!

I ended up by saying that I've learned something: "Always find out what the guy is doing before trying to advise him!" So let's go back to square one (square zero, since it's asm :) ). What flavor of gcc have you got? DJGPP? MinGW? Or ? What command line are you giving Nasm to assemble the asm parts of your code? I should have asked that first!

The disassembly indicates 32-bit code - both the C and your asm. When I saw the dos/bios interrupts, I ASSumed you needed 16-bit code, but that's not true in the case of DJGPP - it includes a "dos extender" that allows dos/bios interrupts from 32-bit code.

The "call" instruction is a "near call" - opcode 0xE8. A "far call" would be opcode 0xEA, so forget what I told you about "retf", that isn't going to work - but you knew that.

ASSuming DJGPP (I'm a slow learner), what you want to do is re-write your "parameter.asm" using ebp and esp instead of bp and sp. I think everything else is okay, and your parameter should be at [ebp + 8], as you figured in the first place. I don't know why you're finding it at [bp + 26]!

Sorry for the bad assumptions, and I'm really irritated that yesterdays post didn't show up. But... onward! From the beginning! What kind of gcc and what command line? We'll solve this yet!

Best,
Frank


Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #8 on: March 13, 2012, 08:46:41 PM »
What a pity ...  :'( I'm using MinGW at the moment. I'm gonna look for DJGPP.
That's my build.bat

Code: [Select]
nasm.exe -f elf -o start.elf start.asm
nasm.exe -f elf -o read.elf read.asm
nasm.exe -f elf -o parameter.elf parameter.asm
nasm.exe -f elf -o stop.elf stop.asm
i586-elf-gcc.exe -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o main.o main.c
i586-elf-ld.exe -T link.ld -o test.com start.elf main.o read.elf parameter.elf stop.elf
PAUSE

That might help.
Thank you very much!

EDIT:
ebp and esp instead of bp and sp and 8 instead of 26 works fine!
« Last Edit: March 13, 2012, 08:53:13 PM by isywum »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Mixing C and Assembly parameter problem
« Reply #9 on: March 13, 2012, 09:38:55 PM »
Every time I think I've reached "peak confusion", I find out I was wrong. I would have said the MinGW gcc would want "-f win32" Nasm output, but I guess what you've got there is a "cross compiler" generating ELF output while running on Windows? Is "link.ld" a linker script? Does it contain "-oformat binary"? It must, if you're really getting a ".com" file (although you can name it that). If your disassembly was the "whole thing", that's what it looks like(?).

If it works, it works - although I don't understand how. I may have been correct when I said that this was "above my pay-grade". In other words, I'm not sure I can help you with this, although I'll keep trying! An important asm skill is "never give up"! :)

Best,
Frank


Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #10 on: March 14, 2012, 02:31:04 PM »
I'm confused ... I think the code is the same as before but it doesn't work now... [ebp+8] is "-" now. I hope I didn't forget re-build the programme yesterday. You're right, that's a cross compiler on windows and link.ld is a linker file. I hope I'll find my mistake. :)
Thanks.

EDIT:
my link.ld
Code: [Select]
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}

EDIT2:
bp, sp and 26 still works. :D
« Last Edit: March 14, 2012, 02:38:14 PM by isywum »

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Mixing C and Assembly parameter problem
« Reply #11 on: March 14, 2012, 02:34:34 PM »
It appears the OP is attempting to build a .COM executable that makes calls to both BIOS and DOS.

However, it's being assembled with -f elf format???  :o
You may be running into some issues with that...

Also, what version of Windows?  Vista/7 won't do 16-bit without an emulator.

Finally, your executable may actually be created as a 32-bit app  even though you're trying to use only 16-bit code.
No such things as 32-bit .com files.

Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #12 on: March 14, 2012, 02:45:33 PM »
I actually write a little OS. That's just a test file.
I'm using Windows 7 32-bit. I tried using bp, sp, ... in my OS but it doesn't work. I don't get any paramter anyway. If you want I can show you that code but I'm sure if that helps.
Thank you!

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Mixing C and Assembly parameter problem
« Reply #13 on: March 16, 2012, 12:15:38 AM »
As Rob says, there's no such thing as a 32-bit .com file. But what you've got is a 32-bit ".com-like" file (that is, no header) with the equivalent of "org 100000h" in it (if I understand your linker script, which I might not).

I started out by saying that gcc won't produce 16-bit code. That's apparently not correct, but there's a "trick" to it. You need an inline "__asm" block telling Gas ".code16"... or so I understand. What you've disassembled is definitely 32-bit code. (disassemble it with "-b32" and see the difference)

BIOS interrupts won't work in 32-bit mode. (DOS interrupts probably won't work at all in your own OS, unless you've provided them). But unless you've switched the CPU to 32-bit mode, you're still in 16-bit mode, so the bios interrupt WILL work. But attempting to execute 32-bit code in 16-bit mode will execute a lot of "nonsense code" (see the disassembly), which is probably why the parameter isn't in the expected place.

You might find it easier to use a compiler that "wants" to produce 16-bit code. OpenWatcom will do it, or some of the old Borland "Turbo C" versions (but I don't know about linker scripts). I know I told you DJGPP, but that provides a 32-bit dos extender, and I doubt if it'll work without dos installed, and I doubt if you do that in your OS. I know you're running Windows 7, but I imagine you're testing your OS by either booting from a floppy, or pretending to boot from a floppy in an emulator.

If you'll correct my incorrect assumptions, maybe we can close in on this...

Best,
Frank


Offline isywum

  • Jr. Member
  • *
  • Posts: 38
Re: Mixing C and Assembly parameter problem
« Reply #14 on: March 17, 2012, 09:55:00 PM »
I stoped testing it with .com files not to create other problems. :)
I just tested all the ideas in my OS but I never get any paramater.
Now I have another idea that could make problems.
There are some problems with the linker because there's often "called" the wrong position (that's often ret).
How have I to link? What's the right position?
That's my actual code
Code: [Select]
kernelA.elf read.elf kernelB.elf
that doesn't work (kernelA.elf kernelB.elf read.elf) works actually, but I don't know why there's a difference for the linker.
I'm going to look for a new compiler.
I hope we will solve that problem sometime but it looks like I isn't an assembly problem but a linker/compiler problem.

Thank you for your great help!