NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: isywum on March 06, 2012, 01:08:44 PM
-
Hi,
I'm programming a little C programme with assembly functions.
This is my code:
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
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.
-
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
-
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.
-
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"?
-
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
-
Hi,
I changed the code but it shouldn't matter:
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
global start
start:
extern main
call main
main.c
extern int paramter(int i);
extern void read();
extern void stop();
int main() {
if (paramter(9) == 9) {
read();
}
read();
stop();
return 0;
}
read.asm
global read
read:
mov ah, 0x0
int 0x16
ret
parameter.asm
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
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. :)
-
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?
-
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
-
What a pity ... :'( I'm using MinGW at the moment. I'm gonna look for DJGPP.
That's my build.bat
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!
-
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
-
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
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
-
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.
-
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!
-
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
-
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
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!
-
Wow again!
I found the paramter at [ebp+66].
Anything is wrong here... :D
I'm gonna safe the files this time.
I think there's just one big problem (linker, compiler or assembly).
Maybe I should try another compiler.
EDIT:
A second paramter destroys the whole esp. I even can't return.
-
Lemmee get this straight... You're still in 16-bit mode, right? Or have you switched to 32-bit pmode?
It looks to me like your linker is doing what you ask of it. It looks to me like gcc is doing what it does - producing 32-bit code. I understand gcc can be tortured into producing 16-bit code, if you do it right. I just did a quick experiment with Nasm, using "-f elf" but adding "bits 16" at the top of the code. It looks like 16-bit code. Using "use16" as a section attribute is rejected. So you "might" be able to get 16-bit code out of the tools you're using, although I think it's the "hard way". I'll have to check the syntax for gcc - maybe later. If you've switched the CPU to 32-bit pmode, the BIOS interrupt isn't going to work, and the DOS interrupt isn't going to work in any case (unless you've provided it).
What is the purpose of introducing C at this early stage? I don't see that it's going to give you much advantage until you've got some libraries written. Maybe I don't see the point of it just because I don't "like" C very much...
Best,
Frank
-
I just read a great tutorial (unfortunately german) an [bp+4] works now!
That's great. I hope [ebp+8] in the PM is going to work.
That's what I just did:
- [BITS 16] at the top of every file
-
mov ax, cs
mov ds, ax
mov ss, ax
mov ax, 0xFFF0
mov sp, ax
in 'kernel.asm'
- push bp
mov bp, sp
mov al, [bp+4]
mov ah, 0x0E
int 0x10
pop bp
ret
in 'print.asm'
Wow, great!
Thank you so much, Frank! :)
EDIT:
I'm gonna tell you about [ebp+8] in Protected Mode.
EDIT2:
What a pity... I can use my function only once. The second time there's no parameter.
I hope you can help me once again. :D
-
I solved the problem now. :)
I added asm(".code16gcc\n"); in the first line of my kernel.c and changed bp to ebp, sp to esp and 4 to 8 and all works! I call the function as often as I want. Wow, I'm happy. :)
Thank you for your help, this is a great forum!
-
Using "native" Linux gcc and ld, I found that ".code16" worked better than ".code16gcc". The latter was giving me a 32-bit "call" - still 16-bit code, with a 0x66 "operand size override". I also found that calling the parameter "int" gives a 32-bit parameter. I thought that calling the parameter(s) "short" might work better (assuming you want 16-bit). I haven't actually tried that.
If it works, it works, but I'm afraid you're getting an almost "random" mixture of 16-bit and 32-bit instructions. In any case, you were correct that you can force gcc to produce 16-bit code, so I learned something there!
Best,
Frank
-
Anyway, I'm gonna use the Protected Mode, so I shouldn't have that problems.
Thanks!
EDIT:
.code16 doesn't work... I don't get the paramters. :D
Do you know why?
-
I really don't know exactly what you're doing, at this point. What I experimented with was some earlier code you posted. It has some "problems", but I imagine you've fixed them. I haven't actually tried any of this, just looked at the disassembly. It "looks like" it would work, as 16-bit code (although rather strange code). What I would do to actually "try" it would be to write up a bootsector which loads the code - somewhere - and jumps to it. I'm guessing you're trying this in an emulator, rather than actually booting to it from a floppy(?). "Should" work the same.
I can show you what I've got, I suppose...
start.asm
bits 16
global start
start:
extern main
call main
main.c
extern int paramter(int i);
extern void read();
extern void stop();
asm(".code16\n");
int main() {
if (paramter(9) == 9) {
read();
}
read();
stop();
return 0;
}
read.asm
bits 16
global read
read:
mov ah, 0x0
int 0x16
ret
paramter.asm
bits 16
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
bits 16
global stop
stop:
mov ah, 0x4C
int 0x21
ret
link.ld
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 = .;
}
00000000 E80D00 call word 0x10
00000003 0000 add [bx+si],al
00000005 0000 add [bx+si],al
00000007 0000 add [bx+si],al
00000009 0000 add [bx+si],al
0000000B 0000 add [bx+si],al
0000000D 0000 add [bx+si],al
0000000F 00 db 0x00
00000010 67668D4C2404 lea ecx,[dword esp+0x4]
00000016 6683E4F0 and esp,byte -0x10
0000001A 6766FF71FC push dword [ecx-0x4]
0000001F 6651 push ecx
00000021 6683EC08 sub esp,byte +0x8
00000025 6766C70424090000 mov dword [dword esp],0x9
-00
0000002E E83F00 call word 0x70
00000031 6683F809 cmp eax,byte +0x9
00000035 7503 jnz 0x3a
00000037 E82600 call word 0x60
0000003A 89F6 mov si,si
0000003C 8DBD0000 lea di,[di+0x0]
00000040 E81D00 call word 0x60
00000043 E83A00 call word 0x80
00000046 66B800000000 mov eax,0x0
0000004C 6683C408 add esp,byte +0x8
00000050 6659 pop ecx
00000052 67668D61FC lea esp,[ecx-0x4]
00000057 C3 ret
00000058 0000 add [bx+si],al
0000005A 0000 add [bx+si],al
0000005C 0000 add [bx+si],al
0000005E 0000 add [bx+si],al
00000060 B400 mov ah,0x0
00000062 CD16 int 0x16
00000064 C3 ret
00000065 0000 add [bx+si],al
00000067 0000 add [bx+si],al
00000069 0000 add [bx+si],al
0000006B 0000 add [bx+si],al
0000006D 0000 add [bx+si],al
0000006F 00 db 0x00
00000070 55 push bp
00000071 89E5 mov bp,sp
00000073 8A4608 mov al,[bp+0x8]
00000076 0430 add al,0x30
00000078 B40E mov ah,0xe
0000007A CD10 int 0x10
0000007C B400 mov ah,0x0
0000007E 5D pop bp
0000007F C3 ret
00000080 B44C mov ah,0x4c
00000082 CD21 int 0x21
00000084 C3 ret
...padded to 4096
Your "paramter:" converts the number 9 to the character '9'. When this returns to the C code, this checks for 9, the number, not the character. This isn't going to work.
Your "stop:" uses int 21h. This isn't going to work.
The "phys" in your "link.ld" looks a little high to me. I doubt if this is going to work (although I doubt if it makes any difference at this point). I think you'd need to enable a20, and alter the "limit" to make this actually work at "some_segment:0x100000". But I don't know how/where you're actually loading this code, and what you've done first, so I could be wrong about that. You keep saying that you're going to use Protected Mode, but you haven't actually switched to pmode yet, right?
I don't know if this is any help or not.
Best,
Frank
-
Yeah, that code is really old and it was just a test file (not the OS).
I'm trying to understand the Protected Mode but I couldn't so far...
This is my new code:
asm(".code16gcc\n");
extern void print();
extern void restart();
extern char read();
int printString(char* szString);
int main() {
char szString[] = "Hello world!";
printString(szString);
while(read() != 'r');
restart();
return 0;
}
int printString(char* szString) {
short i=0;
while (i != 2)
{
print(szString[i]);
i=i+1;
}
return 0;
}
But it just always shows HaHaHaHa and so on. I don't get why the function doesn't return.
My new print.asm is:
[BITS 16]
global print
print:
push ebp
mov ebp, esp
mov al, [ebp+8]
mov ah, 0x0E
int 0x10
pop ebp
ret
I know that this is a strange mixing assembly and C but until I'm in the Protected Mode I'd like to solve it like this.
Thank you!
EDIT:
asm(".code16\n"); makes the OS to return from the function but I don't get a parameter.
-
Well... if you're using ".code16gcc" and ebp in the asm file, I would "expect" the first parameter at [ebp + 8]. You might try "o32 ret" to get it to return(?).
If you're using ".code16" and ebp, I would "expect" the first parameter to be at [ebp + 6]. I would "expect" it to return okay. If you're using ".code16" and bp. I would "expect" the first parameter to be at [bp + 4] as it "should" be in 16-bit code. But I didn't even know you could get 16-bit code out of gcc, so... Who knows?
Best,
Frank
-
Wow, you are genius! o32 ret works! That's great. .code16 and ebp+6 doesn't work but who cares? Can you just tell me what the difference between ret and o32 ret is?
-
"o32 ret" will cause Nasm to generate a 0x66 "operand size override prefix" before the "ret" (0xC3). This will, in turn, tell the CPU to expect a 32-bit return address on the stack - which gcc apparently puts there if you use ".code16gcc". At least, I think that's what's happening...
Best,
Frank
-
Well, thank you. I don't have any problem with parameters right now but there's just another little problem. I know that's not a part of that subject but it's just a little one. Sometimes the size of the kernel.bin is too big. That happens for example if there a too long (many) strings.
char szWelcome[] = "Hello world!"; works but char szWelcome[] = "Hello world, welcome to my OS. Please press a key to start."; doesn't the size of the file is 8192 instead of 4092 bytes. When I change the padding there's a OS.iso but it doesn't work, it jsut craches. I hope you can help me. :)
-
Well, you remember when we were having a problem with "too many call instructions"?
http://forum.nasm.us/index.php?topic=1307.0
As it turned out, the problem was something else (we never did figure out what). Could it be that this time you're really not loading enough sectors? What you showed me back then was loading 5 sectors. Maybe that's not enough with the longer strings? I'm just guessing, of course, but that's the first and most obvious thing that comes to mind.
Best,
Frank
-
You're right! It works with 18 sectors. Great! Thank you so much!