Page 1 of 1

Loading Sectors in Real Mode (Using the BIOS)

Posted: Wed Aug 24, 2011 11:28 am
by sdfkjsdfkj
Hello, I've recently gotten started trying to make a little hobby OS, and I've wanted to do something a little more useful than printing out 'Hello, World!', so I tried to write
a tiny bootsector that is supposed to read the next sector on the hard drive (I'm running it in qemu so my file appears as that). However it always gives me the
'failed to read sector' message, is there a way that I can treat a disk as a "flat" bunch of numbered sectors? Am I approaching this completely wrong?
Help would be very much appreciated.

I guess I should mention this is done in Assembly using nasm on an Ubuntu machine, being tested on qemu.
http://pastebin.com/FVjEm5iN (bootload.asm)

~ I'm sorry for the noob question :P

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Wed Aug 24, 2011 1:16 pm
by Combuster
Please set your registers before using them (DS/ES/SS/SP). Actually calling the bios helps too.

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Wed Aug 24, 2011 1:34 pm
by azblue
You want function 2, not function 1, to read sectors. You also have to specify what sectors you want to read. Google "Ralf Brown's Interrupt List."

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Wed Aug 24, 2011 2:14 pm
by sdfkjsdfkj
*facepalm* thanks, added the call to the BIOS and it at least says it works (need to play around with it to test).
What should I initialize the pointers to? 0?

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Wed Aug 24, 2011 6:51 pm
by Bietje
ajtgarber wrote:*facepalm* thanks, added the call to the BIOS and it at least says it works (need to play around with it to test).
What should I initialize the pointers to? 0?
Yes. It is possible that your BIOS, or other BIOSes where your OS might run on is a bit bogus and doesn't set them to zero by default. Programming and especially OS programming is very explicit - you should not assume things. When booting you should set at least your data segment, extra segment and stack segment to 0. I recommend that you also set your code segment and other extra segments (fs and gs) to zero. Besides setting segment register you should also set up a stack.

Also other assumptions in your code should be avoided:

Code: Select all

mov ah, 0x02 ; read sector
        mov al, 1
        mov ch, 1 ; track
        mov cl, 1 ; sector
        mov dh, 1 ; head
        mov dl, 0x80 ; read from first hd
        mov bx, 0x7E00
Here you want to load a piece of code from disk. I see several strange things:
  • The sector number is 1 based, so you are reading your masterboot again, which is not very useful
    The track and head numbers are NOT one based, they are zero based.
    You want to load the the physical address 0x7e00, which is fine, but you are assuming the es register is set to 0. Do not assume, set it manually.
Greets,

Bietje

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Wed Aug 24, 2011 11:16 pm
by Combuster
or other BIOSes where your OS might run on is a bit bogus and doesn't set them to zero by default.
Actually, the BIOS is more likely bogus (or predates the relevant boot standards) if it does set all segments to zero.

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Fri Aug 26, 2011 7:44 am
by turdus
ajtgarber wrote:is there a way that I can treat a disk as a "flat" bunch of numbered sectors?
Yep, it's called LBA (linear block address). Also check RBIL INT13/AH=42h, that's what you need.

Re: Loading Sectors in Real Mode (Using the BIOS)

Posted: Sun Sep 04, 2011 11:36 am
by miker00lz
here is the boot sector code i am using for my OS (it's simple as the floppy has no actual file system yet, i read code raw from pre-defined sectors. i've got it commented, hopefully it's easy to understand.

Code: Select all

org 7C00h

jmp entry
nop

;BIOS parameter block data
oem          db 'MT86',0,0,0,0 ;8-byte OEM identifier
bytespersect dw 512
sectperclust db 1 ;no real FAT filesystem on disk yet, this value is irrelevant
reservedsect dw 1 ;so is this one
numoffats    db 1 ;and this one
rootentries  dw 0 ;yep, you guessed it. irrelevant!
totalsects   dw 2880 ;ditto
descriptor   db 0F0h ;media descriptor (0F0h = 1.44 MB floppy)
sectsperfat  dw 2 ;irrelevant for now
spt          db 18, 0 ;sectors per track
heads        db 2 ;what it says
hiddensects  dw 0,0 ;32-bit, but meaningless value on a floppy
totalsects2  dw 0,0 ;same deal

entry:
  mov ax, cs
  mov ds, ax ;our variables are right here in the code segment, so change ds to cs

  mov si, offset banner
  call printmsg ;print "loading" message

  mov ax, 0E00h ;the sector after the boot sector contains the MT86 OS
  mov es, ax    ;multi-tasking code, and it needs to be loaded into 0E00h:0000h
  mov bx, 0     ;offset 0

  mov [retry], 0

read1:
  cmp [retry], 8 ;if we've retried 8 times due to errors,
  je loadfail    ;then we throw in the towel
  mov ah, 2 ;int 13h function 2 = read sector(s)
  mov al, 1 ;we want one sector
  mov cl, [sect]
  mov ch, [cyl]
  mov dh, [head]
  int 13h ;do the call
  inc [retry]
  jc read1
  inc [sect]

  mov ax, 1000h ;the rest of the data on this disk is the main kernel,
  mov es, ax    ;and we want it at 1000h:0000h
  mov bx, 0 ;offset 0

  mov [retry], 0

read:
  cmp [retry], 8
  je loadfail
  push bx ;save our current destination offset on the stack, in case a buggy BIOS destroys BX
  mov ah, 2
  mov al, 1
  mov cl, [sect]
  mov ch, [cyl]
  mov dh, [head]
  int 13h
  pop bx ;pop offset back into BX
  inc [retry]
  jc read
  mov [retry], 0 ;if successful read, reset the retry count for the next sector

  mov ah, 0Eh ;print a dot after each sector is read to indicate progress
  mov al, '.'
  int 10h

  add bx, 512 ;increase destination offset with each new sector read
  dec [readcount]
  cmp [readcount], 0 ;have we read all the sectors we wanted to now?
  jz done ;then finished with disk reads...

  inc [sect] ;increment current sector
  mov al, [sect]
  cmp al, [spt] ;is it now higher than our max sector value?
  ja inchead ;then go wrap it around and increment the head
  jmp read ;otherwise, read next sector

inchead:
  mov [sect], 1 ;reset current sector number
  inc [head] ;increment current head
  mov al, [head]
  cmp al, [heads] ;has it reached the maximum?
  je inccyl ;go wrap it around and increment the cylinder
  jmp read ;otherwise, read next sector

inccyl:
  mov [head], 0 ;reset current head
  inc [cyl] ;increment cylinder
  jmp read ;read next sector

done: ;now jump to the kernel entry point 1000h:0100h
  mov ax, 1000h ;set up segments and stack
  mov ss, ax
  mov sp, 0000h
  mov ds, ax
  mov es, ax
  push ax ;we'll use the CS:IP push and retf trick
  mov ax, 100h
  push ax

  retf

printmsg: ;enter with near offset to ASCII-Z string in SI
  cld
  nextchar:
    lodsb
    cmp al, 0
    jz printdone
    mov ah, 0Eh
    int 10h
    jmp nextchar
  printdone:
    ret

loadfail:
  mov si, offset error
  call printmsg
  hlt

sect db 2
head db 0
cyl  db 0

retry db 0

readcount db 32 ;number of sectors to read when loading kernel

banner db 'Loading MT86 kernel', 0
error  db 'disk error!',13,10,13,10,'System halted.', 0

and, proof it actually works :lol:

Image
booting my own "OS" in an 8086 PC emulator i wrote myself.. when it's time to geek out, i spare no expenses. :)