Loading Sector from Floppy -- resolved

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.
Post Reply
ccoale427
Posts: 5
Joined: Fri Dec 24, 2010 5:18 pm

Loading Sector from Floppy -- resolved

Post by ccoale427 »

Hello all.

I've run into a bit of snag in a boot loader that I am writing. I have spent a considerable amount of time debugging and performing tests and eventually just rewriting the entire boot sector from scratch, but to no luck. So, here is what I am trying to have my boot sector do (very simple):

1. Load LBA sector 1 (the second sector) from a floppy disk into the memory address 0x0:0x7E00 (ES : BX)
2. Jump to 0x0:0x7E00.

Now, when I boot from a floppy disk, the second sector does not load (at all) into my memory location. In fact, it does not load at all, but there is no error code from the BIOS interrupt, nor is the carry flag set. I have tried both a virtual floppy (via qemu's -fda option) and a physical one (via my real computer), and both fail. However, when I change the floppy to a hard drive (via qemu's -hda option), it works 100%. When I say it works, I mean that it executes the code in sector 1 (the second sector) and works just like I expect.

With that said, below are the important parts of my boot sector. I have put a wrapper around the loading code. Hopefully my code is easy to read and understand.

The "LoadSector" procedure takes 2 arguments.
AX = the sector number (LBA, i.e. starting at 0) to load
ES:BX = the buffer to load the sector into

Code: Select all

[BITS 16]
[ORG 0x7C00]

%define GlobalStackSegment	0x7000
%define GlobalStackTop		0xFFFF
%define GlobalStage2Start	0x7E00
%define BootDevice		0x500	; 0x00 = floppy 1, 0x01 = floppy 2, 0x80 = hdd 1, 0x81 = hdd 2
%define BootDeviceType		0x501	; 0 = floppy, 1 = hard drive
%define SectorCount		0x502	; the number of sectors on the boot device, starting at 1
%define HeadCount		0x503	; the number of heads on the boot device
%define CylinderCount		0x504	; the number of cylinders on the boot device
%define DriveCount		0x506	; the number of drives on the system

; jump to our entry point (and set CS to 0)
jmp 0x0:main

; entry point
main:
	; setup our segment registers
	mov AX, 0
	mov DS, AX
	mov ES, AX
	mov GS, AX
	mov FS, AX
	
	; setup our stack
	mov AX, GlobalStackSegment
	mov SS, AX
	mov SP, GlobalStackTop
	

	; copy the ID of the device that loaded this boot sector,
	; and then determine whether it is a floppy or hard drive.
	mov [BootDevice], DL
	
	; now we need to grab boot disk information
	call GetBootDiskInfo

        ; =========================================
        ; PROBLEM AREA	
        ; =========================================
	; load sector #2
	mov AX, 1
	mov BX, 0x7E00
	call LoadSector
	
	jmp 0x7E00
        ; =========================================
	
	cli
	hlt

;; GetBootDiskInfo()
;; Initializes boot disk information such as number of sectors, cylinders, heads, etc.
;; 
;; i.e.
;;
;; call GetBootDiskInfo
;;
GetBootDiskInfo:
	enter 0, 0
	pusha
	
	mov AH, 0x08 ; read drive parameters function
	mov DL, [BootDevice]
	int 0x13 ; BIOS interrupt
	jnc .setBootDiskInfo
	; if clear flag is set, an error occurred
	push InfoLoadErr
	call PrintString
	call HandleFatalError
	
	; no error occurred, we can set our boot data
	.setBootDiskInfo:
		mov [DriveCount], DL
		mov [HeadCount], DH
		mov [SectorCount], CL
		and byte [SectorCount], 00111111b
		and CX, 1111111111000000b
		mov [CylinderCount], CX
	popa
	leave
ret 

;; LoadSector()
;; Loads the given 0-indexed LBA sector into buffer ES:BX.
;; AX = sector to load
;; ES:BX = buffer to load to
LoadSector:
	enter 0, 0
	pusha
	
	
	; first we need to convert the given LBA sector into a CHS tuple
	; C = LBA / (sectors per track * heads per cylinder)
	; H = (LBA / sectors per track) % heads per cylinder
	; S = (LBA % sectors per track) + 1
	div byte [SectorCount] ; AX / SectorCount, AL = quotient, AH = remainder
	mov CL, AH	; S = (LBA % sectors per track) + 1
	add CL, 1	; ^^^
	and CL, 00111111b ; S is only 6 bits.
	
	mov AH, 0	; AX = AL
	div byte [HeadCount]	; AX / HeadCount, AL = quotient, AH = remainder
	mov DH, AH	; H = (LBA / sectors per track) % heads per cylinder
	
	mov AX, 0
	mov AL, byte [SectorCount]
	mul byte [HeadCount]	; AX = AL * HeadCount
	mov DI, AX		; DI = AX
	mov DX, 0
	pop AX			; AX = LBA
	div DI			; LBA / (sectors per track * heads per cylinder),  AX = quotient, DX = remainder
				; C = LBA / (sectors per track * heads per cylinder)
	and AX, 0000001111111111b
	shl AX, 6
	mov CH, 0
	or CX, AX
	
	; CL = sector
	; CH = cylinder
	; DH = head
	mov DL, [BootDevice]
	mov AL, 1	; number of sectors to read
	mov AH, 0x02 	; read sector function
	; ES:BX = buffer
	; now we can actually read the sector
	mov SI, 0
	.tryLoadSector:
		mov AH, 0x00 ;reset drive
		int 0x13
		
		mov AH, 0x02 ; load sector function
		int 0x13
		jnc .finishLoadSector
		inc SI
		cmp SI, 5
		je .badFinishLoadSector
		jmp .tryLoadSector
	
	; something went wrong
	.badFinishLoadSector:
		push SectorLoadErr
		call PrintString
		mov AL, AH
		add AL, 48
		mov AH, 0x0E
		mov BH, 0x00
		mov BL, 0x07
		int 0x10                   ; <--- this is just for debugging, prints '0', meaning the error code from read sectors is 0 (OK).
		call HandleFatalError
	
	; everything is OK!
	.finishLoadSector:
	
	popa
	leave
ret

;; HandleFatalError()
;; This procedure is called when an unrecoverable error occurs.
;; Control is NOT handed back to the caller.
;;
;; i.e.
;;
;; push errorString
;; call PrintString
;; call HandleFatalError
HandleFatalError:
	enter 0, 0
	cli
	hlt
	leave ; <-- this is never called, but put it for consistency
ret
; ----------------------- SIGNATURE --------------------
times 510-($-$$) db 0
dw 0xAA55

push HelloWorld
call PrintString
cli
hlt

times 1024-($-$$) db 0
Thanks in advance to anyone who can help.
Last edited by ccoale427 on Fri Dec 24, 2010 11:26 pm, edited 2 times in total.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Loading Sector from Floppy

Post by Combuster »

Start with not putting your stack in unusable ram - the bios uses that place.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
ccoale427
Posts: 5
Joined: Fri Dec 24, 2010 5:18 pm

Re: Loading Sector from Floppy

Post by ccoale427 »

Whoops, changed. Thanks.

That didn't change the problem, however.
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Loading Sector from Floppy

Post by DavidCooper »

Why not try adding some code just before calling int 0x13 to see if all the right values are in the relevant registers. Put them on the stack and restore them afterwards. You could post most of them to the screen after adding 48 (that's a decimal) to them, and post a colour (e.g. 12 for red, again a decimal - sorry but I don't use hex) to the following byte of screen memory so that they show up.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
ccoale427
Posts: 5
Joined: Fri Dec 24, 2010 5:18 pm

Re: Loading Sector from Floppy

Post by ccoale427 »

I've done that. All of the CHS values are correct. Again, it works 100% on a hard disk (and there is no code change for it).
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Loading Sector from Floppy

Post by DavidCooper »

What are the actual numbers then at that point (including ES)?

I can't follow your code (assembler isn't my thing), but before you called loadsector you put 1 into AX, and then just above the start of the loadsector routine you describe AX as containing the required sector, but won't sector 1 be the boot sector? If I remember rightly, there's no sector zero on a floppy disk. I'm guessing you know that, so your code probably converts it into a 2, but I'd like to see the actual numbers.

Whatever the case though, it should still be loading a sector into memory somewhere, unless resetting the disk system just before reading a sector is upsetting it in some way.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
ccoale427
Posts: 5
Joined: Fri Dec 24, 2010 5:18 pm

Re: Loading Sector from Floppy

Post by ccoale427 »

Thanks for taking a look.

When using LBA, sector numbers start at 0. That is, sector 0 is really C=0, H=0, S=1. Sector 1 is C=0, H=0, S=2 (generally speaking). So when I set AX to 1, that means I want to load sector 1 using 0-based sectors (that is, the second sector with the first being the boot sector).

Ok, that is interesting. Apparently ES was NOT 0. Re-setting it to 0 in the procedure seemed to do the trick. Thanks.
ccoale427
Posts: 5
Joined: Fri Dec 24, 2010 5:18 pm

Re: Loading Sector from Floppy -- resolved

Post by ccoale427 »

Just for everyone's information, it appears that int 0x13 (AH=0x08) -- read drive parameters BIOS interrupt -- is buggy on some BIOS and as a result will change the ES register (according to my test and what another programmer has told me). A little workaround is to save the ES state prior to calling this interrupt and subroutine.

Thanks again everyone.
Post Reply