Disk Read Error while loading sectors into memory

北慕城南 提交于 2019-12-03 12:05:19

"He's making a list, he's checking it twice..."

  • Your bootloader starts in the real address mode, so it is best to force your assembler in using 16-bit code. You achieve this in NASM by writing [bits 16] at the top of your program.

  • When your bootloader starts, BIOS will have placed it at linear address 00007C00h. It can do this in a number of ways with respect to the combination of segment and offset.
    When you explicitly wrote [org 0x7C00] you (kind of) expected this combination to have the segment part equal to zero. But this is by no means an obligation for BIOS! And so it is up to you to set the segment registers (DS, ES, and SS) manually.

  • The BIOS teletype function that you use in your print_string routine uses BL and BH as parameters. So you should never use the BX register to address your text. Sure, some BIOSes don't use these BL and BH parameters (any more) but do try to develop programs for the biggest audience.

  • When you initialized the SP register with 0x9000 you effectively set up a stack that could easily, without you noticing it, overwrite the program beneath it! It would be best to choose a combination of SS and SP that satisfies your needs and nothing more. A 4608 bytes stack that stays above the bootsector at 7C00h and ends at 9000h would require: SS=07E0h SP=1200h. To avoid any problems on 8086 hardware it's best to disable interrupts when changing SS:SP.

  • You used pusha and popa instructions. These are not valid instructions on 8086 hardware. When writing robust software, we should test if the hardware is up to the task. But here the simplest solution is to only push/pop single registers.

  • You have interpreted the return value from the BIOS function that reads from the disk, but you just abort when an incorrect number of sectors were transferred. This is a wrong approach. When BIOS tells you about an incomplete transfer (this can happen if your BIOS is not multitrack enabled) you have to repeat the call for the remaining number of sectors. Obviously some of the parameters will have to be adjusted: next head, maybe next cylinder, and always sector=1. (A perfect solution would involve retrieving the disk geometry from BIOS or reading it from the BPB present on the disk). I assumed basic 1.44 MB floppy operation.

  • When reading from the disk doesn't succeed first time you should retry it a number of times. Such first time fails are perfectly normal. Five retries is a good value. In between tries you call the BIOS function that resets the diskdrive.

  • To make sure QEMU can actually read those additional 15 sectors you should pad this file so it has a total length of 16 sectors worth. The text you linked to did this also!

"Putting it all together"

[bits 16]
[org 0x7C00]

KERNEL_OFFSET equ 0x1000

xor  ax, ax
mov  ds, ax
mov  es, ax    
mov  [BOOT_DRIVE], dl
mov  ax, 0x07E0
cli
mov  ss, ax 
mov  sp, 0x1200
sti
mov  si, MSG_REAL_MODE       
call print_string           
call load_kernel            
jmp  $

print_string:
  push ax
  push bx
  push si
  mov  bx, 0x0007  ;BL=WhiteOnBlack BH=Display page 0
  mov  ah, 0x0E    ;Teletype function
 loop:
  mov  al, [si]
  cmp  al, 0
  je return
  int  0x10
  inc  si
  jmp  loop
 return:
  pop  si
  pop  bx
  pop  ax
  ret

disk_load:
  mov  [SECTORS], dh
  mov  ch, 0x00      ;C=0
  mov  dh, 0x00      ;H=0
  mov  cl, 0x02      ;S=2
 next_group:
  mov  di, 5         ;Max 5 tries
 again: 
  mov  ah, 0x02      ;Read sectors
  mov  al, [SECTORS]
  int  0x13
  jc   maybe_retry
  sub  [SECTORS], al ;Remaining sectors
  jz  ready
  mov  cl, 0x01      ;Always sector 1
  xor  dh, 1         ;Next head on diskette!
  jnz  next_group
  inc  ch            ;Next cylinder
  jmp  next_group
 maybe_retry:
  mov  ah, 0x00      ;Reset diskdrive
  int  0x13
  dec  di
  jnz  again
  jmp  disk_error
 ready:
  ret

disk_error:
  mov  si, DISK_ERROR_MSG 
  call print_string 
  jmp  $

DISK_ERROR_MSG db "Disk read error!", 0

load_kernel: 
  mov  bx, KERNEL_OFFSET       
  mov  dh, 15           
  mov  dl, [BOOT_DRIVE]                      
  call disk_load                                                  
  ret

; Global variables
BOOT_DRIVE     db 0
SECTORS        db 0
MSG_REAL_MODE  db "Started in 16-bit Real Mode", 0 

; Bootsector padding 
times 510-($-$$) db 0 
dw 0xAA55

; 15 sector padding
times 15*256 dw 0xDADA
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!