2

FAT12 ブートローダーから、BIOS 割り込み 13h を使用してフロッピーから [ヘッド = 0、シリンダー (トラック) = 1、セクター = 1] からセクター番号をロードしようとしています。

サブルーチンを使用し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

Ubuntuのqemuでこのコードを実行し、コンパイルしています

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
4

1 に答える 1

3

修正を提案するつもりですが、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にはヘッド番号が含まれている必要があります。あなたのコードでは、これらを逆にしています。最後の 2 行は次のようになっているはずです。

  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 ) shl 8 ) または ( ( シリンダーと 768 ) shr 2 ) またはセクター。

当社のシリンダーは 255 を超えないため、式は次のように縮小されます。

CX := ( ( シリンダーと 255 ) shl 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

上記のコードにはもう 1 つの欠陥があります。それは、DLレジスタが 0 に設定されていることです。これは、セクタを読み取るドライブ番号です。これは、メモリ アドレス 0x07c00 にジャンプするときにBIOS がDLでブートローダーに渡すドライブ番号に設定する必要があります。起動時にその値を保存し、セクターを読み取るときにそれを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
于 2015-12-01T18:00:18.087 に答える