Error reading disk when loading sectors into memory

I tried to create a bootloader using this , but when it starts, it shows:

disk read error! 

If I ignore this, in a later part it will show me an incorrect memory mapping. I also followed some other sources, but in vain. Looks like I'm just copying what they do. Even if I am a little different, every new type is generated every time.

Should I use an already built bootloader or what should I do?

The disk boot error code is as follows:

 [org 0x7c00] KERNEL_OFFSET equ 0x1000 mov [BOOT_DRIVE], dl mov bp, 0x9000 mov sp, bp mov bx, MSG_REAL_MODE call print_string call load_kernel jmp $ print_string: pusha mov ah, 0x0e loop: mov al,[bx] cmp al, 0 je return int 0x10 inc bx jmp loop return: popa ret disk_load: push dx mov ah, 0x02 mov al, dh mov ch, 0x00 mov dh, 0x00 mov cl, 0x02 int 0x13 jc disk_error pop dx cmp dh, al jne disk_error ret disk_error : mov bx, DISK_ERROR_MSG call print_string jmp $ DISK_ERROR_MSG db "Disk read error!", 0 [bits 16] load_kernel: mov bx, KERNEL_OFFSET mov dh, 15 mov dl, [BOOT_DRIVE] call disk_load ret ; Global variables BOOT_DRIVE db 0 MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 ; Bootsector padding times 510-($-$$) db 0 dw 0xaa55 

I use this command to build and run my bootloader:

 nasm boot.asm -f bin -o boot.bin && qemu-system-i386 boot.bin 

I am stuck at this point. My bootloader displays disk read error . If I ignore it at this point in time, then it creates problems while running my .c kernel. It seems to be using the wrong memory mapping.

+7
assembly qemu osdev kernel bootloader
source share
1 answer

"He makes a list, he checks it twice ..."

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

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

  • The BIOS teletype function used in your print_string routine uses BL and BH as parameters. Therefore, you should not use the BX register to address your text. Of course, some BIOSes do not use these parameters BL and BH (no more), but try to develop programs for the largest audience.

  • When you initialize the SP register with 0x9000, you effectively configure the stack, which can easily, without your attention, overwrite the program under it! It would be better to choose a combination of SS and SP that will satisfy your needs and nothing more. Byte 4608 bytes, which is located above the boot sector at 7C00h and ends at 9000h, will require: SS = 07E0h SP = 1200h. To avoid problems with 8086 hardware, it is best to disable interrupts when changing SS: SP.

  • You used the pusha and popa commands. These are invalid instructions on 8086 hardware. When writing reliable software, we need to verify that the hardware is up to the task. But here the simplest solution is only one-time or one-time use of registers.

  • You interpreted the return value from a BIOS function that is read from disk, but you simply aborted when the wrong number of sectors was transferred. This is the wrong approach. When the BIOS tells you about incomplete transfer (this can happen if your BIOS is not included in the multitrack), you need to redo the call for the remaining number of sectors. Obviously, some parameters should be adjusted: the next chapter, maybe the next cylinder, and always sector = 1. (The ideal solution is to extract the disk geometry from the BIOS or read it from the BPB present on the disk). I assumed the base floppy work was 1.44 MB.

  • When you are not reading from the disk for the first time, you must repeat it several times. This first time is not quite normal. Five attempts is good value. Between attempts, you call the BIOS function, which flushes the disk.

  • To make sure that QEMU can actually read these additional 15 sectors, you should place this file so that it has a total length of 16 sectors. the text you contacted did this too!

"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 
+5
source share