Reputation: 141
I am trying to load sector number from [head = 0, cylinder(track) = 1, sector = 1] from floppy using BIOS interrupt 13h, from my FAT12 bootloader.
I use the subroutine read_sectors
to read the sector and load it at es:bx
.
This code works well with any sector from the first track, but it reads just 0s from any sector from the other tracks, while those sectors are actually populated. With sector 18, for example, cx
is 0x0041
, which is right. The problem is, the interrupt sets CF, saying that there is an error. It also sets ah (return code) to 1, and al(sectors read) to 1.
This is the complete bootloader .asm file
bits 16
org 0
start: jmp load
nop
OEM: DB "ptiaOS "
bytesPerSector: DW 512
sectorsPerCluster: DB 1
reservedSectors: DW 1
numberOfFATs: DB 2
rootEntries: DW 224
totalSectors: DW 2880
media: DB 0xf8
sectorsPerFAT: DW 9
sectorsPerTrack: DW 18
headsPerCylinder: DW 2
hiddenSectors: DD 0
totalSectorsBig: DD 0
driveNumber: DB 0
unused: DB 0
extBootSignature: DB 0x29
serialNumber: DD 0xa0a1a2a3
volumeLabel: DB "PTIAOS FLP "
fileSystem: DB "FAT12 "
load:
;The bootloader is loaded at the address 0x7C00 and is 0x200 (512) bytes long
cli
mov ax, 0x07C0 ; setup registers to point to our segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
sti
mov si, hello_string
call prints
mov si, try_string
call prints
mov ax, 18
call lba_to_chs
mov al, 2
mov bx, 0x200
call read_sectors
mov si, success_string
call prints
mov si, 0x200
call prints
cli
hlt ;halt
;--------DATA--------
hello_string db `Hi, bootloader of ptiaOS here\n\r`, 0
success_string db `Successfully loaded from floppy\n\r`, 0
try_string db `Loading more data from floppy...\n\r`, 0
;CHS position of the sector to read
sector_number db 0 ;1 is the first (they're 18 per track)
cilinder_number db 0 ;track number: 0 is the first (they're 80 per side)
head_number db 0 ;0 is the first (the're 2)
;---SOTTOPROGRAMMI---
;print a 0-terminated string pointed by ds:si
prints:
mov ah, 0x0E ;dico all'interrupt del BIOS video di eseguire la funzione di stampa [al: carattere, bh: pagina]
.prints_printchar:
lodsb ;al = *(si++)
cmp al, 0
je .prints_end ;if(al == 0) goto print_string_end
int 0x10 ;chiamo l'interrupt di i/o dello schermo, con ah = 0x0E per stampare il carattere in al
jmp .prints_printchar
.prints_end:
ret
;Read sectors from floppy at the address specified by CHS variables, and load them in es:bx
read_sectors:
mov ah, 0x02 ;function 0x02, interrupt 0x13: read sectors
;al (the number of sectors to read), es:bx (destination) are set as arguments
xor cx, cx
mov cl, [cylinder_number]
shl cl, 6
or cl, [sector_number]
mov dh, [head_number]
mov dl, 0
int 0x13
jnc .sectors_read_successfully ;CF = 0 if no errors, 1 otherwise
;if errors occured, try to reset floppy
.flp_reset:
mov ah, 0 ;function 0, interrupt 0x13: reset disk
mov dl, 0 ;disk to reset: 0=floppy
int 0x13
jc .flp_reset ;CF = 0 if no errors, 1 otherwise
jmp read_sectors
.sectors_read_successfully:
ret
lba_to_chs:
mov cx, ax
mov bl, [sectorsPerTrack]
div bl
inc ah ;ah = lba % 18 + 1
mov byte [sector_number], ah
mov ax, cx
mov bl, [sectorsPerTrack]
div bl ;al = lba / 18
cbw ;ax = lba / 18
mov bl, [headsPerCylinder]
div bl ;al = lba / 18 / 2; ah = lba / 18 % 2
mov byte [cilinder_number], ah
mov byte [head_number], al
ret
times 510-($-$$) db 0
dw 0xAA55
I am running this code on qemu, from Ubuntu, and compiling it with
nasm -f bin -o ptiaos.bin ptiaboot.asm
nasm -f bin -o BSTAGE2.SYS blstage2.asm
mkdir floppy
dd status=noxfer conv=notrunc if=ptiaos.bin of=ptiaos.flp
sudo mount -o loop ptiaos.flp floppy
sudo cp BSTAGE2.SYS floppy
sleep 0.1
sudo umount floppy
rm BSTAGE2.SYS
rm ptiaos.bin
rmdir floppy
Upvotes: 2
Views: 1160
Reputation: 47573
I'm going to suggest a fix, but with one assumption. It appears that lba_to_chs is designed to work with smaller disk sizes where the number of cylinders doesn't exceed 0xff (255). This is fine for conventional floppy disk sizes since the number of cylinders will generally be a lot less than that.
First of all there is a bug in this code:
mov ax, cx
mov bl, [sectorsPerTrack]
div bl ;al = lba / 18
cbw ;ax = lba / 18
mov bl, [headsPerCylinder]
div bl ;al = lba / 18 / 2; ah = lba / 18 % 2
mov byte [cilinder_number], ah
mov byte [head_number], al
In the final division AL should contain the cylinder number, and AH should contain the head number. In your code you have reversed these. The last two lines should have read:
mov byte [cilinder_number], al
mov byte [head_number], ah
Given the assumption that has been made about cylinders not exceeding 255, one can modify the read_sector code a bit. INT 13h AH=02h requires the cylinder number and sector number to be placed in CX in this manner:
CX = ---CH--- ---CL--- cylinder : 76543210 98 sector : 543210
They also give this equation for the bit manipulation:
CX := ( ( cylinder and 255 ) shl 8 ) or ( ( cylinder and 768 ) shr 2 ) or sector;
Since our cylinders will not exceed 255 the equation reduces down to:
CX := ( ( cylinder and 255 ) shl 8 ) or sector;
This is the same as simply storing cylinder in CH and sector in CL . So the code you had for setting CX (CL and CH) that appears as:
mov ah, 0x02 ;function 0x02, interrupt 0x13: read sectors
;al (the number of sectors to read), es:bx (destination) are set as arguments
xor cx, cx
mov cl, [cylinder_number]
shl cl, 6
or cl, [sector_number]
mov dh, [head_number]
mov dl, 0
int 0x13
Would look something like:
mov ah, 0x02 ;function 0x02, interrupt 0x13: read sectors
;al (the number of sectors to read), es:bx (destination) are set as arguments mov ch, [cilinder_number]
mov ch, [cilinder_number]
mov cl, [sector_number]
mov dl, 0
mov dh, [head_number]
int 0x13
The code above has one other flaw, and that is the DL register is being set to 0. This is the drive number to read the sector from. This should be set to the drive number that the BIOS passes in DL to our bootloader when it jumps to memory address 0x07c00. We should save that value at startup and then copy it to DL when we read a sector. This allows us to boot from a drive that may not have been the first boot floppy (disk number 0x00).
The code could be amended by adding a boot_drive variable to your data area:
boot_drive db 0
After initializing the segment registers save the DL (boot drive) passed to our boot loader with:
mov [boot_drive], dl
And then in load_sector change:
mov dl, 0
to:
mov dl, [boot_drive]
The final code after all the suggested fixes and changes above could look something like:
bits 16
org 0
GLOBAL main
main:
start: jmp load
nop
OEM: DB "ptiaOS "
bytesPerSector: DW 512
sectorsPerCluster: DB 1
reservedSectors: DW 1
numberOfFATs: DB 2
rootEntries: DW 224
totalSectors: DW 2880
media: DB 0xf8
sectorsPerFAT: DW 9
sectorsPerTrack: DW 18
headsPerCylinder: DW 2
hiddenSectors: DD 0
totalSectorsBig: DD 0
driveNumber: DB 0
unused: DB 0
extBootSignature: DB 0x29
serialNumber: DD 0xa0a1a2a3
volumeLabel: DB "PTIAOS FLP "
fileSystem: DB "FAT12 "
load:
;The bootloader is loaded at the address 0x7C00 and is 0x200 (512) bytes long
cli
mov ax, 0x07C0 ; setup registers to point to our segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
sti
mov [boot_drive], dl
mov si, hello_string
call prints
mov si, try_string
call prints
mov ax, 18
call lba_to_chs
mov al, 1
mov bx, 0x200
call read_sectors
mov si, success_string
call prints
mov si, 0x200
call prints
cli
hlt ;halt
;--------DATA--------
boot_drive db 0
hello_string db `Hi, bootloader of ptiaOS here\n\r`, 0
success_string db `Successfully loaded from floppy\n\r`, 0
try_string db `Loading more data from floppy...\n\r`, 0
;CHS position of the sector to read
sector_number db 0 ;1 is the first (they're 18 per track)
cilinder_number db 0 ;track number: 0 is the first (they're 80 per side)
head_number db 0 ;0 is the first (the're 2)
;---SOTTOPROGRAMMI---
;print a 0-terminated string pointed by ds:si
prints:
mov ah, 0x0E ;dico all'interrupt del BIOS video di eseguire la funzione di stampa [al: carattere, bh: pagina]
.prints_printchar:
lodsb ;al = *(si++)
cmp al, 0
je .prints_end ;if(al == 0) goto print_string_end
int 0x10 ;chiamo l'interrupt di i/o dello schermo, con ah = 0x0E per stampare il carattere in al
jmp .prints_printchar
.prints_end:
ret
;Read sectors from floppy at the address specified by CHS variables, and load them in es:bx
read_sectors:
mov ah, 0x02 ;function 0x02, interrupt 0x13: read sectors
;al (the number of sectors to read), es:bx (destination) are set as arguments mov ch, [cilinder_number]
mov ch, [cilinder_number]
mov cl, [sector_number]
mov dl, [boot_drive]
mov dh, [head_number]
int 0x13
jnc .sectors_read_successfully ;CF = 0 if no errors, 1 otherwise
;if errors occured, try to reset floppy
.flp_reset:
mov ah, 0 ;function 0, interrupt 0x13: reset disk
mov dl, 0 ;disk to reset: 0=floppy
int 0x13
jc .flp_reset ;CF = 0 if no errors, 1 otherwise
jmp read_sectors
.sectors_read_successfully:
ret
lba_to_chs:
mov cx, ax
mov bl, [sectorsPerTrack]
div bl
inc ah ;ah = lba % 18 + 1
mov byte [sector_number], ah
mov ax, cx
mov bl, [sectorsPerTrack]
div bl ;al = lba / 18
cbw ;ax = lba / 18
mov bl, [headsPerCylinder]
div bl ;al = lba / 18 / 2; ah = lba / 18 % 2
mov byte [cilinder_number], al
mov byte [head_number], ah
ret
times 510-($-$$) db 0
dw 0xAA55
Upvotes: 3