This post is a continuation from the previous post [Writting a Bootloader] which was an attempt to build a Bootloader for the x86 architecture.
Although the executable size is limited to 512 Bytes, there are several important tasks that can be accomplished during booting process. One of them is to read the Geometry from several storage devices.
Interrupt 13h [INT 13h] provides sector-based disk access services using the CHS addressing method (Cylinder/Head/Sectors). This values provide the physical location of the data (although this is not completely true nowadays).
The code below is an example of this interrupt. It reads the CHS from the first Hard Disk using Interrupt 13h.
Briefly, interrupt parameters are set in AX and DX registers. After interrupt is completed, its values can be read from the appropriate registers (CX, DX). The rest of the code includes helper functions to print numbers and strings to present results properly.
After booting our virtual system using the code, we get the following result:
Hello Again
Hard Drive Information
Disk Geometry (C/H/S): 130/16/63
Comparing these results with fdisk on the same drive:
Disk /dev/sdb: 67 MB, 67108864 bytes
255 heads, 63 sectors/track, 8 cylinders, total 131072 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
I am still trying to figure why these values are so different. Anyway, the total size of the drive taking the total oh Cylinders/Heads/Sectors will be close to the drive size described by fdisk.
Finally, notice that 08h service is limited to drives up to 500 MB. If there's required to read properties from bigger drives, service 48h is the appropriate.
As exercise, following improvements can be done:
- Report an error if reading fails
- Read properties from all connected drives
- Determine drive size in LBA format
- Modify code to read bigger drives
- BITS 16 ;Tell compiler that this is 16 Bit code
- org 07C00h ;Tell compiler code will be loaded in this memory section
- mov si, txtStr
- call printTxt
- call crlf
- mov si, txtStr2
- call printTxt
- call crlf
- mov si, txtDisk
- call printTxt
- ;Read HD1 parameters
- mov AH, 08h ;Read Drive Parameters
- mov DL, 80h ; First Drive
- int 13h
- push cx
- mov ax, cx ;Cylinders
- and ax, 0C0h
- shl ax, 2
- and cx, 0FF00h
- shr cx, 8
- or cx, ax
- mov ax, cx
- inc ax
- call printNum
- mov si, txtSlash
- call printTxt
- pop cx
- mov al, dh ;Num Heads
- mov ah, 0
- inc ax
- call printNum
- mov si, txtSlash
- call printTxt
- and cx, 3Fh ;[5..0] Sectors Per Track
- mov ax, cx
- call printNum
- call crlf
- JMP $
- ;********************* Utility Functions ******************
- printNum: ;Print a number (ax)
- push cx
- push dx
- mov cx,000Ah ;divide by 10
- mov bx, sp
- getDigit:
- mov dx,0 ;puting 0 in the high part of the divided number (DX:AX)
- div cx ;DX:AX/cx. ax=dx:ax/cx and dx=dx:ax%cx(modulus)
- push dx
- cmp ax,0
- jne getDigit
- printNmb:
- pop ax
- add al, 30h ;adding the '0' char for printing
- mov ah,0eh ;print char interrupt
- int 10h
- cmp bx, sp
- jne printNmb
- pop dx
- pop cx
- RET
- printTxt: ;Print a String (si)
- lodsb
- cmp al, 0
- je exitFn
- mov ah, 0Eh
- int 10h
- jmp printTxt
- exitFn:
- RET
- crlf: ;Print End of line
- mov ax, 0E0dh
- int 10h
- mov ax, 0E0ah
- int 10h
- RET
- ;AppData
- txtStr db "Hello Again", 0
- txtStr2 db "Hard Drive Information", 0
- txtDisk db "Disk Geometry (C/H/S): ", 0
- txtSlash db "/", 0
- times 510-($-$$) db 0 ;Fill the rest of the Loader with "0"
- dw 0AA55h ;Bootloader Signature
Comments