Booting to the hard drive from a floppy

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
fbelzile
Posts: 18
Joined: Tue Dec 28, 2004 12:00 am

Booting to the hard drive from a floppy

Post by fbelzile »

EDIT: Look down a few posts to see my new question...



I'm still working on a Pascal Operating System. Yes, I'm not letting go of Pascal... Anyways, I'm having trouble writing a sector to a floppy using inline assembly using Turbo Pascal 6.

Code: Select all

... Pascal Stuff ...

asm
mov DX, 0100h
mov CX, 0000h
mov AX, DS
mov ES, AX
mov AX, 0301h
mov BX, offset sector
int 13h

end;
end.
"Sector" is an array of 512 byte variables defined in my pascal code.
The code above works in Bochs, with no problem. I've made another function that will read the sector and display the first 4 bytes of it to the screen to test it.

However, when I actually boot the computer with the floppy, it boots to it, does its little pascal code and then I can hear the floppy drive spinning but when it goes to show the four bytes, it gives me 0000.

Whats the difference from Bochs and the real computer? Can I change something in my code for it to work? I'm just starting to get to know assembly.

Heres a post from me on another forum (where you can find full code on the second page)

http://www.codeguru.com/forum/showthrea ... 742&page=1
Last edited by fbelzile on Thu Jul 19, 2007 7:08 pm, edited 1 time in total.
User avatar
Dex
Member
Member
Posts: 1444
Joined: Fri Jan 27, 2006 12:00 am
Contact:

Post by Dex »

First you should always try to read/write 3 times or more.
Next as a pascal OS you should try and write it, in the code that know best, eg: pascal.
As i did what your doing, when i first started and inflater did too, you end up coding a assembly OS in between bit of pascal code.
Then you have the problem of turbo pascal using Dos function, (but then it would not work with boch either ). Next i would add "uses Crt; " to your program, as this uses BIOS in stead of Dos for write functions etc.
Next write your code like this exampe:

Code: Select all

uses dos; 

Type
  TBootSector = Record
    nJump     : Array [1..3] of Byte;
    OEM       : Array [1..8] of Char;
    SectSize  : Word;
    ClustSize : Byte;
    ResSecs   : Word;
    FATCount  : Byte;
    RootSize  : Word;
    TotalSecs : Word;
    Media     : Byte;
    FATSize   : Word;
    TrackSize : Word;
    HeadCount : Word;
    HiddenSecs: Word;
    HiHidnSecs: Word;
    BTotalSecs: LongInt;
    DriveNo   : Byte;
    Res       : Byte;
    ExtBootSig: Byte;
    SerNo     : LongInt;
    VolLabel  : Array [1..11] of Char;
    FileSysID : Array [1..8] of Char;
    BootCode  : Array [1..450] of Byte
  End;

var
  b : TBootSector;
  f : file;
  r : registers;
  s, sr, try : word;
  ds : string;

function upstr (s : string) : string;
var i : integer;
begin
  if length(s) > 0 then
    for i := 1 to length(s) do
      s[i] := upcase(s[i]);
  upstr := s
end;

label l1, l2;

begin

  try := 0;
l2:
  r.ax := $301; { write fn, 1 sector }
  r.cx := 1;    { track/cyl=0, sector=1 }
  r.dh := 0;    { head=0 }
  r.dl := ord(ds[1])-ord('A'); { disk }
  r.es := seg (b);
  r.bx := ofs (b);
  intr ($13, r);
  inc (try);
  if r.flags and fcarry = fcarry then begin
    writeln ('  An error occured. Error code: ', r.ah);
    if try = 1 then begin
      writeln ('    trying again...');
      r.ah := 0;
      r.dl := 0;
      intr ($13, r);
      goto l2;
    end
    else begin
      writeln ('  Failed.');
      exit
    end;
  end;
  writeln ('  OK.');
end.
NOTE: The above code is to write a new boot sector in Dos, take the use dos; out and add use crt; and use only the write part, as it just a example of write or read.
Unless you want to run it in Dos.
fbelzile
Posts: 18
Joined: Tue Dec 28, 2004 12:00 am

Post by fbelzile »

Didn't work. The floppy drive didn't even spin. It just told me there was an error (with 1 as the error code).

I'm going to give you a code dump of my whole code including the pascal. This code works when in Bochs, but not on a real boot. (It gives me 0000 as the first four bytes instead of the numbers I put in). Take a look at what I'm talking about:

Code: Select all

uses crt;

	var 
		choice1	: byte;
		choice2 : byte;
		choice3 : byte;
		choice4 : byte;
		sector  : array[1..512] of byte;
		rsector : array[1..512] of byte;
		i       : Integer;

procedure final; 
begin

	sector[1] := choice1;
	sector[2] := choice2;
	sector[3] := choice3;
	sector[4] := choice4;

	for i := 5 to 512 do
		sector[i] := 0;

	asm
	mov DX, 0100h
	mov CX, 0000h
	mov AX, DS
	mov ES, AX
	mov AX, 0301h
	mov BX, offset sector
	int 13h
	end;

	asm
	mov DX, 0100h
	mov CX, 0000h
	mov AX, DS
	mov ES, AX
	mov AX, 0201h
	mov BX, offset rsector
	int 13h
	end;
	
	writeln (' ');
	writeln ('These are the first four bytes of the sector:');
	writeln (' ');
	write (rsector[1]);	
	write (rsector[2]);	
	write (rsector[3]);
	write (rsector[4]);

	halt;
	end;

begin
	CLRSCR;
	TextColor(10);
	writeln ('   _______________________________________________________ ');
	writeln ('  |                                                       |');
	writeln ('  |  What programs would you like to start-up upon boot?  |');
	writeln ('  |                                                       |');
	writeln ('  |  1. Itunes                        2. Firefox          |');
	writeln ('  |  3. MSN Messenger                 4. Outlook Express  |');
	writeln ('  |                                                       |');
	writeln ('  |  Enter "0" to end your selections. No letters!        |');
	writeln ('  |_______________________________________________________|');
	writeln (' ');

repeat
	write (':'); 
	readln (choice1);
		if choice1 = 0 then 
		begin
		choice2 := 0;
		choice3 := 0;
		choice4 := 0;

		final;

		end;
		
		if choice1 > 4 then 
		begin
		writeln ('  Number too high. Try again. ');
		end;

until choice1 < 5;

repeat
	write ('?'); 
	readln (choice2);

	if choice2 = 0 then 
		begin
		choice3 := 0;
		choice4 := 0;
		
		final;

		end;
 
		if choice2 > 4 then 
		begin
		writeln ('  Number too high. Try again. ');
		end;

until choice2 < 5;


repeat
	write ('?');
	readln (choice3);

	if choice3 = 0 then 
		begin
		choice4 := 0;
                        
		final;

		end;

	if choice3 > 4 then 
		begin
		writeln ('  Number too high. Try again. ');
		end;

until choice3 < 5;


repeat
	write ('?'); 
	readln (choice4);
	
	if choice4 > 4 then 
		begin
		writeln ('  Number too high. Try again. ');
		end;
	
	final;

until choice4 < 5;
end.
User avatar
XCHG
Member
Member
Posts: 416
Joined: Sat Nov 25, 2006 3:55 am
Location: Wisconsin
Contact:

Post by XCHG »

In your first code, you are setting CX to 0x0000 (zero) which means you are setting the Sector Number in CL to zero too. Using INT 0x13, Sector number starts from 1 and you should never set it to 0. Head starts from 0 and so does Cylinder but Sector starts from 1 and ends at 63.

Sometimes when you attempt to read from the floppy drive, it will report an error and the INT 0x13 will set the Carry Flag and then give the control back to your program. After each attempt of reading or writing, you must check the Carry Flag and then reset the disk system if the Carry Flag is set. Resetting the disk system is done using function 0x00 in INT 0x13.

I have coded something in the first stage of my boot loader in HDD that reads some sectors from the HDD but the routine is almost identical to that of FDD. See if you can get the essence of the code; if not, post back here and I will explain:

Code: Select all

  MOV     DI , 0x000B                  ; Try to read 10 times
  .TryToReadAgain:                     ; Try to read the sectors
    XOR     BX , BX                    ; BX = 0x0000
    MOV     ES , BX                    ; ES = 0x0000
    MOV     BX , $$ + 512              ; ES:BX = 0x0000:ORIGIN + 512
    DEC     DI                         ; Decrement the number of attempts
    JZ      .ReadError                 ; Jump to ... if time out
  .ResetDiskSystem:                    ; Reset the disk system
    XOR     AH , AH                    ; Reset Disk System function
    MOV     DL , 0x80                  ; Reset HDD and FDD at the same time
    INT     0x13                       ; Issue the trap
    JC      .TryToReadAgain            ; Keep trying to read if an error has occurred
  .TryToRead:                          ; Now read the sectors if no error has occurred
    MOV     AL , BYTE PTR [$$ + 440]   ; Number of sectors to read
    INC     AL                         ; Read the Index Sector too
    MOV     AH , 0x02                  ; Read Sector(s) command
    MOV     DL , 0x80                  ; Read from hard drive
    MOV     DH , BYTE PTR [SI + 0x01]  ; Head number
    MOV     CX , WORD PTR [SI + 0x02]  ; CL = Sector number & Cylinder High, CH = Cylinder High
    INC     CL                         ; We don't want to read the current sector; read the next sector
    INT     0x13                       ; Issue the trap
    JC      .TryToReadAgain            ; Keep trying if an error has occurred

Good luck.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
fbelzile
Posts: 18
Joined: Tue Dec 28, 2004 12:00 am

Post by fbelzile »

I changed CX to 0001h, and it worked! 5 times out of 5. I'm not too concerned about writing it multiple times. I don't care if it doesn't work 1 out of 10 times.

I have another question though... A bit more complex. Is there any way to boot to the hard drive after my OS/program completed? I could always blank out the first sector of the floppy and issue a reboot, but I have a BIOS password and I don't want to enter it again after a reboot. I'm not lazy, I'm doing this for simplicity reasons.

I am an real mode (as you can see from my code) and I only have one hard drive with one partition.
Whats the easiest way to give control to the hard drive (so that it can load Windows an so on)?

Thanks again for your help XCHG! You've solved a problem I've been having for ages. At least you got the ball rolling.
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post by urxae »

fbelzile wrote:I have another question though... A bit more complex. Is there any way to boot to the hard drive after my OS/program completed? I could always blank out the first sector of the floppy and issue a reboot, but I have a BIOS password and I don't want to enter it again after a reboot. I'm not lazy, I'm doing this for simplicity reasons.

I am an real mode (as you can see from my code) and I only have one hard drive with one partition.
Whats the easiest way to give control to the hard drive (so that it can load Windows an so on)?
What you're looking for here is called chainloading. The idea here is to set up the environment (memory & registers) as it would have been if the BIOS had just booted from the hard disk instead of your floppy.
That environment is (AFAIK):
a) The bootsector of the hard disk is loaded at 0000:0x7c00-0x7dff
b) CS:IP is equal to 0000:0x7c00 (the start of the boot sector).
c) dl is the BIOS drive number for the hard disk (typically 0x80 for the first hard disk)

You'll want to make sure the code doing this isn't located in the area where the bootsector is loaded, so if you're doing this from a boot sector you should first copy your boot sector (or just the code performing the chainloading) somewhere else and execute it from there[1]. IIRC a popular place is 0000:0x0600+.

[1] Make sure the code in question is either position-independent or assembled for execution at the new location. It shouldn't be too hard to make position-independent, really.
fbelzile
Posts: 18
Joined: Tue Dec 28, 2004 12:00 am

Post by fbelzile »

Thanks urxae!

Keep in mind that I'm new to OS programming and low level programming.
So basically, with what you said I'd have this? :

Code: Select all

MOV AX, 0000h
MOV BX, 7C00h
MOV CS, AX
MOV IP, BX
MOV DL, 80h
This code will be added after the sector has been written to the floppy. This is in the kernel (If I can even call it that) that a bootstrap loader loads. So I understand that two boot sectors can't be put into memory at the same time in the same location, but my loader is done and hands control to my program, does that mean my boot sector is out of the computers memory? If not, I'm sure we have to put the Hard drives boot code into another location in memory... Was that the "0000:0x0600+" you were talking about? Should the code above be changed because of this?

Also, I know there's more code needed to read off the HD and put it into memory to be ran.

I'm a new to these kinds of things... Can you help me with code examples. Be specific please, or else I won't understand a thing.

I found a small article that seems to describe chainloading:
http://www.osdev.org/osfaq2/index.php/R ... BootLoader
It supplied this code deeper in the page, I don't know if it has to do anything with what I'm trying to do:
http://clicker.cvs.sourceforge.net/clic ... iew=markup

Thanks a LOT!
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post by urxae »

fbelzile wrote:So basically, with what you said I'd have this? :

Code: Select all

MOV AX, 0000h
MOV BX, 7C00h
MOV CS, AX
MOV IP, BX
MOV DL, 80h
That won't work, you can't directly assign to CS and IP. The only way to set them is with JMP:

Code: Select all

JMP 0000h:7C00h
This code will be added after the sector has been written to the floppy. This is in the kernel (If I can even call it that) that a bootstrap loader loads. So I understand that two boot sectors can't be put into memory at the same time in the same location, but my loader is done and hands control to my program, does that mean my boot sector is out of the computers memory? If not, I'm sure we have to put the Hard drives boot code into another location in memory... Was that the "0000:0x0600+" you were talking about? Should the code above be changed because of this?

Also, I know there's more code needed to read off the HD and put it into memory to be ran.

I'm a new to these kinds of things... Can you help me with code examples. Be specific please, or else I won't understand a thing.
I've attached a modified version of some simple chainloader code I wrote a while back. It's a boot sector that's supposed to chainload the first sector of the first hard disk.
I haven't tested this version, but the main difference to my original is just that it loads the first sector of the first harddisk (BIOS drive nr 0x80) instead of the second sector of the drive it's on, so AFAIK it should work as long as that exists. (The original was written because GRUB stage1 doesn't fit into the bootsector together with the SFS superblock)
It's very simplistic, it just resets the disk and retries on errors without any counting to avoid infinite loops or anything.
I hope it helps. (Keep in mind that this is probably the biggest piece of real mode asm code I've written :P)
I found a small article that seems to describe chainloading:
http://www.osdev.org/osfaq2/index.php/R ... BootLoader
It supplied this code deeper in the page, I don't know if it has to do anything with what I'm trying to do:
http://clicker.cvs.sourceforge.net/clic ... iew=markup
That code is linked to from the next section, which is just about loading data from disk. It doesn't implement chainloading as far as I can see.
Attachments
chainloader.asm
(2.72 KiB) Downloaded 20 times
User avatar
inflater
Member
Member
Posts: 1309
Joined: Thu Sep 28, 2006 10:32 am
Location: Slovakia
Contact:

Re: Booting to the hard drive from a floppy

Post by inflater »

Yer writing OS in Pascal? Gratulieren ! :) 8) I was doing that too, but I got stuck when implementing FAT in my OS and bang, in ASM (dunno C/C++). Answer to your next question:
Just tell the user to remove any disks from their drives and perform INT 19h. :) Should work, or give a try MOV DL,0x80 and then INT 19h, that may work too.

Offtopic: Phew, new record in SR, the temperature here jumped these days to 39,9 °C! :D I knew I was doing good work when implementing my new electrical multi-speed air conditioner :lol:

Regards
inflater
My web site: http://inflater.wz.cz (Slovak)
Derrick operating system: http://derrick.xf.cz (Slovak and English :P)
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Re: Booting to the hard drive from a floppy

Post by pcmattman »

inflater wrote: Offtopic: Phew, new record in SR, the temperature here jumped these days to 39,9 °C!
We're getting near-zero temperatures here - I've never felt this cold :D

Funny, in December we'll hit 40 degrees and wonder how on earth we ever felt so cold...
User avatar
Brynet-Inc
Member
Member
Posts: 2426
Joined: Tue Oct 17, 2006 9:29 pm
Libera.chat IRC: brynet
Location: Canada
Contact:

Post by Brynet-Inc »

(This is clearly off topic.... but..)

Are you complaining about it being "nearly" 0?? haha 8)..

We get on "average" -20°C during winter seasons here in Ontario.. (It can get much colder though..)

Right now.. I'm dreaming about that type of weather.. :roll:

It's around 19°C right now. (Was nearly 40°C with humidity taken into account this week! - I hate the heat :()
Image
Twitter: @canadianbryan. Award by smcerm, I stole it. Original was larger.
fbelzile
Posts: 18
Joined: Tue Dec 28, 2004 12:00 am

Re: Booting to the hard drive from a floppy

Post by fbelzile »

inflater wrote:Just tell the user to remove any disks from their drives and perform INT 19h. :) Should work, or give a try MOV DL,0x80 and then INT 19h, that may work too.
The first option of having the user remove the disk works, but I'd prefer that the user doesn't have to do anything physical. I tried putting 80h in DL but it still booted to the floppy drive. Am I missing something? Is there anyway to let the BIOS know that I want to just boot from the HD? I saw the end of this page, but its unclear to me:

http://www.ctyme.com/intr/rb-2270.htm

I'm using an old laptop that uses the "ATA-66/IDE interface" I'm assuming this is OK.

Thank you very much Inflater, I will use that method for now, but I'd really like a way that would automatically boot to the HD.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Booting to the hard drive from a floppy

Post by Brendan »

Hi,
fbelzile wrote:Thank you very much Inflater, I will use that method for now, but I'd really like a way that would automatically boot to the HD.
The suggestions and code posted by Urxae are correct - a long time ago I used the same method to boot any disk drive, and even had my OS on 2 floppies and the second hard disk and could boot my OS repeatedly (from different devices) before booting Windows 95 (which was on the first hard drive).

The problem is that it's become difficult to keep the computer in the state the BIOS left it in (or the state the chainloaded code expects the computer to be in). For example, if you switch to protected mode, reprogram the PIC chips, change the PIT timer frequency, change video modes, mess with the floppy controller, etc, then you'd need to reverse everything you changed to restore the state the computer should be in before doing the chainloading.

You may also need to correct the BIOSs "time of day" counter to account for missed ticks, and if you do something like switching the floppy drive motor off manually (without waiting for the BIOS to turn it off) then don't be surprised if the BIOS thinks the motor is still on and tries to read data from the drive while the motor is off (for e.g. imagine booting your OS on A:, then chainloading DOS on C: and immediately typing in "dir a:" when DOS has booted).

Of course I used my chainloading code to speed up the developmenet cycle - the repeated "test the OS, reboot, start windows, make some changes, copy to floppy, reboot" cycle is extremely slow and frustrating (and skipping the BIOSs POST testing helped a little). A lot has changed since the "bad old days" though - between Bochs and network boot my development cycle is much much faster, and (for me) restoring the computers state to do chainloading just isn't worth the hassle now.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Brynet-Inc wrote:(This is clearly off topic.... but..)

Are you complaining about it being "nearly" 0?? haha 8)..

We get on "average" -20°C during winter seasons here in Ontario.. (It can get much colder though..)

Right now.. I'm dreaming about that type of weather.. :roll:

It's around 19°C right now. (Was nearly 40°C with humidity taken into account this week! - I hate the heat :()
Here in Queensland we rarely drop below 10 degrees. Probably the main reason why the Gold (and Sunshine) Coasts are so popular.
fbelzile
Posts: 18
Joined: Tue Dec 28, 2004 12:00 am

Re: Booting to the hard drive from a floppy

Post by fbelzile »

Brendan wrote: The suggestions and code posted by Urxae are correct - a long time ago I used the same method to boot any disk drive, and even had my OS on 2 floppies and the second hard disk and could boot my OS repeatedly (from different devices) before booting Windows 95 (which was on the first hard drive).
Yep I didn't doubt him at all. I tried assembling it as a boot sector and it worked. I then tried to edit the code for it to work in my pascal program. That didn't work. The compiler didn't like the labels and it wouldn't allow me to jump to the new boot sector. I kept getting a compiler errors.

Again, I'm just trying to do this the easy way (Even if this stuff is easy for you). Anyways I tried and I got this thing that doesn't compile:

Code: Select all

Invalid combination of opcode and operands.
jmp 0h,7C00h
       ^

Code: Select all

@@copy_start:
    
    xor AX, AX
    mov ES, AX              
    mov BX, 7C00h
    
    mov AH, 2
    mov AL, 1
    mov CH, 0
    mov CL, 1
    mov DH, 0
    mov DL, 80h
    int 13h
    

    jnc @@done
    
@@reset:
    mov AH, 0 
    mov DL, 80h
    int 13h
    
    jc @@reset
    
    jmp @@copy_start
    
@@done:
    mov DL, 80h
    jmp 0h,7C00h
Notice how I had to put the numbers where the "variables" were. Turbo Pascal liked it better when I did that. I also removed the top part of the code because I thought it had to only do something with the boot sector length or something :?

The compiler error is for the last line when the comma was a colon, it said syntax error. I replaced it with the comma and I now get the error above.

Thanks again for your continued help!
Post Reply