OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 3:12 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Thu Nov 21, 2019 9:07 pm 
Offline

Joined: Tue Jun 05, 2018 4:43 pm
Posts: 13
First of all, my source code: https://git.scd31.com/stephen/SteveOS2/src/master

I am having an issue where my boot sector is loading the first 18 sectors properly, then when I increment the head it gets all garbled up and loads from some completely random place on the disk.

My code reads 1 sector at a time. When it gets to sector 19, it instead resets the sector count to 0 and increments the head. When the head gets to 2, it instead resets the head and increments cylinder. This repeats until it loads 19 cylinders. However, it is not loading anything starting from head 1. If I pad my floppy image, I can see it load some of the padding from somewhere else on the disk. I'm not sure what I'm doing wrong - perhaps I'm forgetting to set some register.

Also, I changed around some code slightly when debugging, so the comments aren't 100% accurate. For example, where it says "Read 19 cylinders" it only reads 5.

I have written two operating systems in the past, but after a long hiatus I have forgotten basic principles like this. Help is appreciated!

Edit: This issue appears in qemu 4.1.0, but not in bochs 2.6.9


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Thu Nov 21, 2019 10:10 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
After a quick look over, I think it is that you are assuming that the interrupt call preserves BX. The Interrupt call is not required to preserve the BX register. It may be altered during a call, possibly after reading the last sector of the first track.

I believe that the code is working correctly and you are actually reading the correct sectors. However, since BX can now be anything on the return from the interrupt, you are actually reading memory outside of the read sector (at line 53), giving you the idea that it is wrong. Simply place an 'XOR BX,BX' before line 53 and try again, just to see if this is the case.

Anyway, a suggestion to simplify the code would be to use an LBA and convert to CHS instead of the way you are doing it. For example:

0) set LBA = 1 (remembering that LBA's are zero based)
1) call LBA to CHS
2) Read the sector
3) Increment LBA / ES / etc.
4) If LBA < Desired Count, loop to 1

The call to convert the LBA to CHS can simply be:

Before call:
EAX = LBA
Call conversion
After call:
DH = head
CH/CL = Cylinder/sector

Don't forget to set DL before the INT instruction.

An older version of my code has an example conversion routine.

Ben
- http://www.fysnet.net/osdesign_book_series.htm


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 5:09 am 
Offline

Joined: Tue Jun 05, 2018 4:43 pm
Posts: 13
BenLunt wrote:
After a quick look over, I think it is that you are assuming that the interrupt call preserves BX. The Interrupt call is not required to preserve the BX register. It may be altered during a call, possibly after reading the last sector of the first track.

I believe that the code is working correctly and you are actually reading the correct sectors. However, since BX can now be anything on the return from the interrupt, you are actually reading memory outside of the read sector (at line 53), giving you the idea that it is wrong. Simply place an 'XOR BX,BX' before line 53 and try again, just to see if this is the case.

Anyway, a suggestion to simplify the code would be to use an LBA and convert to CHS instead of the way you are doing it. For example:

0) set LBA = 1 (remembering that LBA's are zero based)
1) call LBA to CHS
2) Read the sector
3) Increment LBA / ES / etc.
4) If LBA < Desired Count, loop to 1

The call to convert the LBA to CHS can simply be:

Before call:
EAX = LBA
Call conversion
After call:
DH = head
CH/CL = Cylinder/sector

Don't forget to set DL before the INT instruction.

An older version of my code has an example conversion routine.

Ben
- http://www.fysnet.net/osdesign_book_series.htm


I'm doing a `mov bx, 0` on line 40, right before the interrupt. However, I tried pushing es before the interrupt and popping it after to no avail.

What is the advantage to the method you describe?


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 7:41 am 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
scdown wrote:
I'm doing a `mov bx, 0` on line 40, right before the interrupt. However, I tried pushing es before the interrupt and popping it after to no avail.

Yes, you are. But if you will notice, *after* the interrupt call, you use BX to access a word in memory, assuming BX is still zero. This probably is not that case.

I think your code is correctly reading all the given sectors. However, since you are retrieving a word from memory and printing it to the screen to verify that it has been read correctly, the actual word that is displayed is wrong, even though the sector was correctly read (possibly).

scdown wrote:
What is the advantage to the method you describe?

Cleaner code, easier to read, smaller code, our brains think in LBA's a lot easier than CHS's, many reasons. However, it is only a suggestion.

Ben


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 10:25 am 
Offline

Joined: Tue Jun 05, 2018 4:43 pm
Posts: 13
BenLunt wrote:
scdown wrote:
I'm doing a `mov bx, 0` on line 40, right before the interrupt. However, I tried pushing es before the interrupt and popping it after to no avail.

Yes, you are. But if you will notice, *after* the interrupt call, you use BX to access a word in memory, assuming BX is still zero. This probably is not that case.

I think your code is correctly reading all the given sectors. However, since you are retrieving a word from memory and printing it to the screen to verify that it has been read correctly, the actual word that is displayed is wrong, even though the sector was correctly read (possibly).

scdown wrote:
What is the advantage to the method you describe?

Cleaner code, easier to read, smaller code, our brains think in LBA's a lot easier than CHS's, many reasons. However, it is only a suggestion.

Ben


Those print statements are just debugging. The actual print statement that shows the memory isn't getting loaded is in the second stage.


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 1:46 pm 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
BenLunt wrote:
After a quick look over, I think it is that you are assuming that the interrupt call preserves BX. The Interrupt call is not required to preserve the BX register. It may be altered during a call, possibly after reading the last sector of the first track.

An older version of my code has an example conversion routine.

Like *most* BIOS calls in the absence of a documented bug (and none that I am aware of with Int 13h/ah=2 involving BX), BX is preserved.

I looked at your code and noticed it relies on a 386+ processor and won't run on anything before that. It might be a caveat you might want to put in the header documentation.

I have a version that works with 16-bit code (and should run on an 8086) where the 32-bit LBA is passed in SI:DI and then sets up the registers as expected by int 13h/ah=2 etc:

Code:
;    Function: lba_to_chs
; Description: Translate a 32-bit Logical block address (LBA)
;              to CHS (Cylinder, Head, Sector).
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              https://stackoverflow.com/q/47118827/3857942
;   http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_9/CH09-4.html
;
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = Lower 16-bits of LBA
;              DI = Upper 16-bits of LBA
;              DI:SI = 32 bit LBA number
;
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;              This routine should work on an 8086 processor.

lba_to_chs:
    push bx                    ; Save temporary registers
    push ax
    xor dx, dx                 ; Set up 32-bit by 16-bit DIV to determine high order
    mov ax, di                 ;     of Quotient (HOQ), DX:AX = (0x0000:DI)
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 1 (HOQ)
    mov bx, ax                 ; Save high order of Quotient (HOQ)
    mov ax, si                 ; Do division to compute low order of Quotient (LOQ)
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 2 (LOQ)
    mov cl, dl                 ; CL = S = LBA mod SPT
    inc cl                     ; CL = S = (LBA mod SPT) + 1
    mov dx, bx                 ; Move saved HOQ to DX (Upper 16-bits of DX:AX)
    div word [NumberOfHeads]   ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                 ; DH = H = (LBA / SPT) mod HEADS
;    mov dl, [boot_device]      ; boot device, not necessary to set but convenient
    mov ch, al                 ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    ror ah, 1                  ; Rotate upper 2 bits of 10-bit Cylinder
    ror ah, 1                  ;     into top of AH
    and ah, 0xC0               ;     set lower 6 bits to 0
    or  cl, ah                 ; Place upper 2 bits of 10-bit Cylinder
                               ;    Into the upper 2 bits of the sector number
    pop ax                     ; Restore temporary registers
    pop bx
    ret
This can be dramatically simplified for anything that would support FAT 12 media where you only need a 16-bit LBA:
Code:
;    Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;              Works for all valid FAT12 compatible disk geometries.
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = LBA
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
    push ax                    ; Preserve AX
    mov ax, si                 ; Copy LBA to AX
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl                 ; CL = S = LBA mod SPT
    inc cl                     ; CL = S = (LBA mod SPT) + 1
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [NumberOfHeads]   ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                 ; DH = H = (LBA / SPT) mod HEADS
;    mov dl, [boot_device]      ; boot device, not necessary to set but convenient
    mov ch, al                 ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    ror ah, 1                  ; Rotate upper 2 bits of 10-bit Cylinder
    ror ah, 1                  ;     into top of AH
    and ah, 0xC0               ;     set lower 6 bits to 0
    or  cl, ah                 ; Place upper 2 bits of 10-bit Cylinder
                               ;    Into the upper 2 bits of the sector number
    pop ax                     ; Restore scratch registers
    ret
This would be simplified a bit more if the media is well known IBM-PC floppy format.


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 3:41 pm 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
If I had to guess I'd suspect it might be how you are using QEMU. QEMU has some peculiarities when dealing with hard drive media vs floppy media. What command do you to launch QEMU?

In theory your code should work with:

qemu-system-i386 -fda steveos2.bin

It likely would fail with these which both assume booting from a hard drive image:

qemu-system-i386 steveos2.bin
qemu-system-i386 -hda steveos2.bin

I'm not in a place to actually test/run your code so I'm just guessing as you mention it works thi BOCHs but not QEMU.


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 6:40 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
Hi Michael,
MichaelPetch wrote:
Like *most* BIOS calls in the absence of a documented bug (and none that I am aware of with Int 13h/ah=2 involving BX), BX is preserved.

Please point me to where this is documented. I am not doubting you by any means, I just would like to know your source. I have read that BX is preserved (though I can't remember where) as well as no general purpose registers are preserved unless the service specifically states that one or more is preserved. i.e.: I have read it as both ways.

MichaelPetch wrote:
I looked at your code and noticed it relies on a 386+ processor and won't run on anything before that. It might be a caveat you might want to put in the header documentation.

Yes, but this is the ever so delightful discussion on "who would ever have a 16-bit processor anymore". :-) It is not a bad suggestion though. On the next update, I will see about adding something as such.

Thanks,
Ben


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 7:52 pm 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
Each BIOS call is different, although it happens to be how most were designed. Some do modify BX AFAIR (but they are rare), but that is documented. The registers that are documented as being modified are identified. If something isn't mentioned you assume it is not modified. This convention for documenting BIOS (and even DOS) has been the norm for decades and as far back as the mid 80s when I got into this gig. Before Ralf Brown came along books like PC Intern, Dvorak's books, Waite Group's books, SAMS that used the same convention. If its not mentioned it is preserved, but over the years some BIOS manufacturers have introduced bugs. Ralf Brown's interrupt list is one of the most comprehensive resources decades later identifying some of the buggy BIOSes (and DOS oddities as well).

I know of no documentation that has ever said BX is every modified by Int 13h/AH=2, so one can assume that it is preserved unless you have run into a buggy BIOS.

You'd probably note my code doesn't even use 80186 instructions (like pushing immediates, and don't use bit shift and rotate instructions with an immediate value > 1, and I don't use pusha and popa). I wrote my first bootloader in the days of Wendin DOS and I generally design them to be 8086 as it is the lowest common denominator. You can still write 8086 code that sets up the registers, relocates a boot sector in memory, searches a FAT12 partitions root directory for a file, load it, and run it in the constraint of a bootloaders limited space even with a BPB or Partition table present.

If you are writing a bootloader that is going to boot a protected mode program then of course you aren't going to need to do that and using 386 instructions are a non-issue.

I didn't say your bootloader was wrong, only that if it has processor requirements that aren't 8086 it might be a good idea to put that in the documentation at the top of the file.


Top
 Profile  
 
 Post subject: Re: 16 bit mode - BIOS only loads some sectors properly
PostPosted: Fri Nov 22, 2019 8:35 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
MichaelPetch wrote:
I didn't say your bootloader was wrong...
And I didn't feel that you did say so. A 386+ comment is in store for a future update.

Thanks for the Wendin recall. I haven't heard that name in years. It brings back memories to me as well.

As for the other comments, I don't like to assume, so I don't (if space allows), but thank you, I appreciate you getting back with me on that.

Ben


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 70 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group