Page 1 of 1

Boot Loader in D, trouble calling methods

Posted: Mon Dec 06, 2010 9:27 pm
by lambert
Hi all! :)

First of all, I am new to the forum, and new to such low-level development in general. Please pardon this if this isn't the best question... I'll learn over time. :)

I am trying to write my own boot loader in the D programming language, using the Digital Mars DMD compiler and linker for the binaries, and qEmu for the emulation. I've managed to get as far as writing single letters on the screen and even issuing int 13h interrupts to read and execute code from another sector on the HDD, so this isn't a question on how I should start. Rather, I'm having trouble calling other functions, perhaps because of my rather incomplete knowledge about the exact structure of real-mode memory and how the stack exactly works, as well as potentially huge differences between real-mode and protected mode that I do not know about.

My problem
My entire code is in an extern(C) block, so it would be using the C calling convention.

So far, this code works fine, and prints 'A' to the screen:

Code: Select all

pragma(startaddress, kmain); //Tell the compiler where kmain() is
void kmain()
{
	asm
	{
		mov AH, 0x0E;
		mov AL, 65;  //ASCII for A
		mov BH, 0;
		mov BL, 0;
		int 0x10;
	}
}
However, if I try this, it fails (i.e., the function call prints 'A' only once, and never returns):

Code: Select all

void putc()
{
	asm
	{
		naked; //Don't generate any other code for stack and such
		mov AH, 0x0E;
		mov AL, 65;
		mov BH, 0;
		mov BL, 0;
		int 0x10;
		ret;
	}
}
pragma(startaddress, kmain); //Tell the compiler where kmain() is
void kmain()
{
	asm
	{
		naked;
		call putc; //Works fine
		call putc; //Doesn't ever happen... code never executes
	}
}
I've tried every possible combination of calling code with jmp, with call, with/without naked, etc... but none have worked.

I guess the question is: What am I doing wrong? If there's a huge idea that I'm missing, I'll be glad to read up on it if I know where to look. But so far, I've read quite a few guides on the web, and none of them have helped me solve this.

If the disassembly of the code or other information would help, please let me know. :)

Thank you!!

Re: Boot Loader in D, trouble calling methods

Posted: Mon Dec 06, 2010 10:16 pm
by Hangin10
A boot loader is not a good project to begin to learn low level development. D is not the right tool for a boot loader. AFAIK, it will not produce real mode code (although for the posted chunks, I think it would execute roughly the same).

I would recommend loading an older version of Windows (or FreeDOS) in an emulator and doing some DOS programs to better understand the environment.

Re: Boot Loader in D, trouble calling methods

Posted: Mon Dec 06, 2010 10:37 pm
by lambert
Thanks for the reply!

I was wondering, is it the language itself that is the problem, the compiler, or the linker? Would using C instead of D solve my problem? Would switching to the GNU C (or D) compiler? Or would I need to change my linker?
I'm asking this because, since I'm really just embedding assembly code, I'm a little confused about why D would be any different from C or even from NASM -- shouldn't they all be using exactly the same opcodes underneath? Why would they differ?

Edit: I thought the Digital Mars D compiler could create 16-bit code, since according to the URL (http://www.digitalmars.com/ctg/ctgLinkS ... tml#binary), it can create both binary (flat) and (16-bit) COM files... so is this actually the problem? (I'm actually using the Binary switch, by the way.)

Re: Boot Loader in D, trouble calling methods

Posted: Mon Dec 06, 2010 11:15 pm
by Hangin10
Sorry, I did not know that :oops: . I would suggest using an assembler for assembly, at least then you know exactly what you are going to get. Can you disassemble the D object file and see what the compiler is giving you?

Re: Boot Loader in D, trouble calling methods

Posted: Mon Dec 06, 2010 11:45 pm
by lambert
Sorry, at first I thought I'd spotted the problem in the disassembly, but it turns out that I'd spotted a different problem -- the original problem is still there.

So, having fixed my original code, here's the new version:

Code: Select all

void kmain()
{
	putc('A');
	putc('B');
	putc('A');
	putc('B');
}

void putc(char c)
{
	asm
	{
		mov AH, 0x0E;
		mov AL, c;
		mov BH, 0;
		mov BL, 0;
		int 0x10;
	}
}
The output is incorrect characters (but there's still output). If instead of saying "mov AL, c;" I say "mov AL, 65;", 'A' is printed all four times -- so the method is getting called correctly, but c isn't being moved to the AL register correctly. If I used naked; in the putc() function (along with RET at the end), I still get four weird characters, but the're different this time.

Here is the disassembly of the above code, produced with NDISASM.exe:

Code: Select all

//kmain()
00000000  6A41    push byte +0x41 //'A'
00000002  E81900  call word 0x1e  //putc()
00000005  0000    add [bx+si],al  //zero bytes... no actual instruction
00000007  6A42    push byte +0x42 //'B'
00000009  E81200  call word 0x1e  //putc()
0000000C  0000    add [bx+si],al  //zero bytes... no actual instruction
0000000E  6A41    push byte +0x41 //'A'
00000010  E80B00  call word 0x1e  //putc()
00000013  0000    add [bx+si],al  //zero bytes... no actual instruction
00000015  6A42    push byte +0x42 //'B'
00000017  E80400  call word 0x1e  //putc()
0000001A  0000    add [bx+si],al  //zero bytes... no actual instruction
0000001C  83C410  add sp,byte +0x10
0000001F  C3      ret
//Return from kmain

//putc(char)
00000020  55      push bp
00000021  8BEC    mov bp,sp
00000023  53      push bx
00000024  57      push di
00000025  B40E    mov ah,0xe
00000027  8A4508  mov al,[di+0x8]
0000002A  B700    mov bh,0x0
0000002C  B300    mov bl,0x0
0000002E  CD10    int 0x10
00000030  5F      pop di
00000031  5B      pop bx
00000032  5D      pop bp
00000033  C3      ret
//Lots of zero bytes and some other data emitted by the compiler, plus the bootable tail
000001FE  55      push bp //0x55 tail, not code
000001FF  AA      stosb   //0xAA tail, not code
The parameter is not being loaded correctly, but I can't spot where it's going wrong... any ideas?

Thank you!

Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:11 am
by Hangin10
I would call compiler bug on that. It's plain using the wrong register to get the parameter.

Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:18 am
by lambert
Hm...
I'm playing around with a few things, and this is what I've noticed so far:

If I remove the extern(C), and if I pass the parameter as an int instead of char (and play around with some register to extract the char), then it works fine (or at least seems so). But now, I'm trying to print a null-terminated string, and it's not working.

Is there anything special I have to do when the computer boots, such as assigning a special value to EBP or ESP, that I might be missing? :? Because it's hard for me to see this as being a compiler bug.

And thank you for the help by the way.

Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:20 am
by Brendan
Hi,
lambert wrote:Here is the disassembly of the above code, produced with NDISASM.exe:
That's disassembled wrong. It looks exactly like 32-bit code disassembled as 16-bit code.

For example, this (including a CALL with a 16-bit relative offset):

Code: Select all

00000000  6A41    push byte +0x41 //'A'
00000002  E81900  call word 0x1e  //putc()
00000005  0000    add [bx+si],al  //zero bytes... no actual instruction
Is actually this (including a CALL with a 32-bit relative offset):

Code: Select all

00000000  6A41    push byte +0x41 //'A'
00000002  E819000000  call dword 0x0000001e  //putc()
Of course this probably means the compiler is creating 32-bit code and not 16-bit code...


Cheers,

Brendan

Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:26 am
by Hangin10
doh. I should have seen that one. That's what I get for trying to parse a disassembly at midnight #-o .
EDIT: Hey, look! I have three stars now! cool!

[SOLVED] Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:29 am
by lambert
Hi,

Oh wow, really?? So it's generating flat binary files that are actually 32-bit?? :shock:

I believe that... but I'm confused why they talk about 16-bit binaries so much on the website http://www.digitalmars.com/ctg/ctgLinkS ... tml#binary... do you think they are referring to COM files only, and not the flat binaries?

Thank you very much for the info, it's saving me hours of finding bugs that aren't there.

Re: [SOLVED] Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:51 am
by Brendan
Hi,
lambert wrote:I believe that... but I'm confused why they talk about 16-bit binaries so much on the website http://www.digitalmars.com/ctg/ctgLinkS ... tml#binary... do you think they are referring to COM files only, and not the flat binaries?
It looks like at least one version of their toolchain supports 16-bit code if/when you use the right options, etc. This doesn't mean that all versions of their toolchain support 16-bit code (I have no idea), or that it's supported for flat binaries, or that you're using the right options, etc.


Cheers,

Brendan

Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 12:56 am
by lambert
Ah, all right; thank you for the help!

Re: Boot Loader in D, trouble calling methods

Posted: Tue Dec 07, 2010 9:41 pm
by lambert
Thank you for the suggestion. :) What I actually ended up doing was writing a rather short piece of assembly code (my endless thanks to this tutorial: http://www.osdever.net/tutorials/pdf/ch01.pdf) that switched into 32-bit mode and then called my D code.

All of that went fine, except for when I used arrays (specifically, strings) in my D code, because they were based from address 0 and not from the 1-MB boundary. I played around with it for a long time until I realized that the DMD compiler almost did what I wanted: It "changed" the base by N bytes, but it also cut off the first N bytes of the code... so that would've required me to have 1 MB of NOP's in my code. I actually tried that, but ran into numerous limitations, such as the <=64-KB segments limit.

So after giving up for a bit, I was forced to switch to the GNU D compiler (it was actually a linker problem, but each linker only understood its own compiler) and then link into a 32-bit PE file, then use ObjCopy to convert it to a flat 32-bit binary file. It worked, but the problem now is that the DMD D compiler used version 2 of the language, while GDC uses version 1... and version 1 is nowhere nearly as powerful as version 2. But it's still usable, so for anyone reading this who's also attempting to make a boot loader/boot sector like me, I just wanted to describe the process so that they know it's possible... I managed to print things to the screen. :)

Thanks for all the help again!

Edit: There was also this thread that was somewhat vague but gave me some info: http://www.digitalmars.com/d/archives/c++/1171.html