BIOS int 13h не может прочитать после первой дорожки
Я пытаюсь загрузить номер сектора из [head = 0, cilinder(track) = 1, sector = 1] с дискеты, используя прерывание BIOS 13h, из моего загрузчика fat12.
Я использую подпрограмму read_sectors, чтобы прочитать сектор и загрузить его в es:bx.
Этот код хорошо работает с любым сектором из первой дорожки, но он читает только 0 с любого сектора из других дорожек, в то время как эти сектора фактически заполнены. Например, в секторе 18 cx равно 0x0041, что верно. Проблема в том, что прерывание устанавливает CF, говоря, что есть ошибка. Он также устанавливает ah (код возврата) в 1 и al(чтение секторов) в 1.
Это полный файл загрузчика.asm
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
Я запускаю этот код на qemu из Ubuntu и компилирую его
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
1 ответ
Я собираюсь предложить исправить, но с одним предположением. Похоже, что lba_to_chs предназначен для работы с дисками меньшего размера, где число цилиндров не превышает 0xff (255). Это хорошо для обычных размеров дискет, так как число цилиндров, как правило, будет намного меньше.
Прежде всего, в этом коде есть ошибка:
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
В последнем разделе AL должен содержать номер цилиндра, а AH должен содержать номер головки. В вашем коде вы изменили это. Последние две строки должны были прочитать:
mov byte [cilinder_number], al
mov byte [head_number], ah
Учитывая предположение о цилиндрах, не превышающих 255, можно немного изменить код read_sector. INT 13h AH = 02h требует, чтобы номер цилиндра и номер сектора были помещены в CX следующим образом:
CX = ---CH--- ---CL--- cylinder : 76543210 98 sector : 543210
Они также дают это уравнение для битовой манипуляции:
CX: = ( (цилиндр и 255) шл 8) или ( (цилиндр и 768) шр 2) или сектор;
Поскольку наши цилиндры не будут превышать 255, уравнение сводится к:
CX: = ( (цилиндр и 255) шл 8) или сектор;
Это то же самое, что просто хранить цилиндр в CH и сектор в CL. Итак, код, который вы использовали для настройки CX (CL и CH), выглядит так:
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
Будет выглядеть примерно так:
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
Приведенный выше код имеет еще один недостаток, и это то, что регистр DL устанавливается в 0. Это номер диска, с которого читается сектор. Это должно быть установлено на номер диска, который BIOS передает в DL нашему загрузчику, когда он переходит на адрес памяти 0x07c00. Мы должны сохранить это значение при запуске и затем скопировать его в DL при чтении сектора. Это позволяет нам загружаться с диска, который, возможно, не был первой загрузочной дискетой (диск с номером 0x00).
Код можно изменить, добавив переменную boot_drive в вашу область данных:
boot_drive db 0
После инициализации регистров сегментов сохраните переданный в наш загрузчик DL (загрузочный диск) с помощью:
mov [boot_drive], dl
И тогда в load_sector меняются:
mov dl, 0
чтобы:
mov dl, [boot_drive]
Окончательный код после всех предложенных исправлений и изменений может выглядеть примерно так:
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