Ошибка чтения диска при загрузке секторов в память

Я попытался разработать загрузчик, используя это, но при запуске показывает:

disk read error!

Если я проигнорирую это, в более поздней части это покажет мне неправильное отображение памяти. Я также следил за некоторыми другими источниками, но напрасно. Такое ощущение, что я просто копирую то, что они делают. Если я делаю даже немного иначе, каждый раз генерируется новый тип ошибки.

Стоит ли использовать уже построенный загрузчик или что делать?

Код ошибки загрузки диска выглядит следующим образом:

[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

Я использую эту команду для сборки и запуска моего загрузчика:

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

Я застреваю в этом месте. Мой дисплей загрузчика disk read error. Если я проигнорирую его в этот момент времени, это создаст проблемы при выполнении моего ядра. c Похоже, что используется неправильное отображение памяти.


person Palvit Garg    schedule 11.12.2015    source источник
comment
Пожалуйста, проверьте этот код и помогите мне!   -  person Palvit Garg    schedule 12.12.2015
comment
Одна проблема, которая у вас есть, заключается в том, что вы неправильно настроили DS (сегмент данных) при запуске вашей программы. Вы также устанавливаете SP, но фактически не устанавливаете действительный SS (сегмент стека). Это тоже может вызвать проблемы. В disk_load вы не устанавливаете ES (расширенный сегмент), который необходимо установить правильно, чтобы полностью указать место в памяти, где считываются данные (ES:BX — адресный буфер). Если вы создаете дискету 720 КБ, чтение 15 секторов, скорее всего, не сработает, потому что максимальное количество секторов на дорожке (цилиндре) равно 9. Это может вызвать проблемы со чтением.   -  person Michael Petch    schedule 12.12.2015
comment
Также неплохо поставить [bits 16] вверху, чтобы NASM знал, что нужно генерировать весь 16-битный код для загрузчика.   -  person Michael Petch    schedule 12.12.2015
comment
Я использую qemu в качестве эмулятора.   -  person Palvit Garg    schedule 12.12.2015
comment
Команды: nasm boot.asm -f bin -o boot.bin && qemu-system-i386 boot.bin   -  person Palvit Garg    schedule 12.12.2015
comment
Похоже, вы делаете это в Linux? Одна проблема, которую я вижу в том, как вы загружаете свой bin-файл, заключается в том, что файл не будет достаточно большим, чтобы на самом деле иметь данные для других секторов, загружаемых вашим загрузчиком (QEMU не сможет загружать сектора из образа диска, который не т существует). Вам действительно нужно создать образ диска (для начала подойдет что-то размером с образ дискеты). Затем нужно поместить boot.bin в первый сектор. Я предполагаю, что вы вообще не используете dd?   -  person Michael Petch    schedule 12.12.2015
comment
Вы можете попробовать что-то вроде nasm -f bin boot.asm -o boot.bin, затем для создания образа диска (например, размер дискеты 1,44 МБ) выполните dd if=/dev/zero of=disk.img bs=1024 count=1440, затем поместите bin-файл в начало образа диска с помощью dd if=boot.bin of=disk.img conv=notrunc . Затем попробуйте запустить QEMU вот так qemu-system-i386 -fda disk.img . Это загрузит образ диска как дискету A:   -  person Michael Petch    schedule 12.12.2015
comment
Ух ты!! Это работает ...... Можете ли вы дать более четкое объяснение?   -  person Palvit Garg    schedule 12.12.2015
comment
Если вы хотите разместить файл (например, ядро), начиная со 2-го сектора, его можно вставить с помощью чего-то вроде dd if=kernel.bin of=disk.img bs=512 seek=1 conv=notrunc. bs=512 seek=1 ищет в выходном файле первые 512 байт и записывает kernel.bin. Таким образом, добавление подобной команды позволяет вам записывать ядро ​​в сектора сразу после загрузочного сектора, не разрушая загрузочный сектор.   -  person Michael Petch    schedule 12.12.2015
comment
Сэр, пожалуйста, объясните это более ясно ..   -  person Palvit Garg    schedule 14.12.2015
comment
Я столкнулся с той же ошибкой. Проблема заключалась в том, что код пытался прочитать 15 секторов с диска, но мой образ диска был слишком мал (всего 592 байта). Поэтому я воспользовался рекомендацией Майкла и использовал dd для создания образа диска большего размера, и все заработало, как и ожидалось.   -  person ARV    schedule 06.07.2020


Ответы (1)


"Он составляет список, он проверяет его дважды..."

  • Ваш загрузчик запускается в режиме реального адреса, поэтому лучше всего заставить ваш ассемблер использовать 16-битный код. Вы достигаете этого в NASM, написав [bits 16] в верхней части вашей программы.

  • Когда ваш загрузчик запустится, BIOS поместит его по линейному адресу 00007C00h. Это можно сделать несколькими способами в отношении комбинации сегмента и смещения.
    Когда вы явно написали [org 0x7C00], вы (отчасти) ожидали, что эта комбинация будет иметь часть сегмента, равную нулю. Но это ни в коем случае не обязанность BIOS! Таким образом, вы должны установить сегментные регистры (DS, ES и SS) вручную.

  • Функция телетайпа BIOS, которую вы используете в подпрограмме print_string, использует BL и BH в качестве параметров. Таким образом, вы никогда не должны использовать регистр BX для адресации вашего текста. Конечно, некоторые BIOS не используют эти параметры BL и BH (больше), но стараются разрабатывать программы для самой широкой аудитории.

  • Когда вы инициализируете регистр SP значением 0x9000, вы фактически создаете стек, который может легко, незаметно для вас перезаписать программу под ним! Лучше всего будет выбрать комбинацию SS и SP, которая удовлетворяет ваши потребности и не более того. Для стека размером 4608 байт, который находится над загрузочным сектором на 7C00h и заканчивается на 9000h, потребуется: SS=07E0h SP=1200h. Чтобы избежать каких-либо проблем с оборудованием 8086, лучше отключить прерывания при изменении SS:SP.

  • Вы использовали инструкции pusha и popa. Это недействительные инструкции для оборудования 8086. При написании надежного программного обеспечения мы должны проверить, соответствует ли аппаратное обеспечение поставленной задаче. Но здесь самое простое решение — использовать только одиночные регистры.

  • Вы интерпретировали значение, возвращаемое функцией BIOS, которая считывает данные с диска, но вы просто прерываете операцию, когда было передано неправильное количество секторов. Это неправильный подход. Когда BIOS сообщает вам о незавершенной передаче (это может произойти, если ваш BIOS не поддерживает многодорожечность), вы должны повторить вызов для оставшегося количества секторов. Очевидно, что некоторые параметры должны быть скорректированы: следующая головка, может быть, следующий цилиндр, и всегда сектор = 1. (Идеальным решением было бы получение геометрии диска из BIOS или чтение ее из BPB, присутствующего на диске). Я предположил базовую работу с дискетой 1,44 МБ.

  • Если чтение с диска не удается с первого раза, следует повторить попытку несколько раз. Такие первые неудачи совершенно нормальны. Пять попыток — это хорошее значение. В промежутках между попытками вы вызываете функцию BIOS, которая сбрасывает дисковод.

  • Чтобы убедиться, что QEMU действительно может прочитать эти дополнительные 15 секторов, вы должны дополнить этот файл, чтобы его общая длина равнялась 16 секторам. текст, на который вы ссылаетесь сделать это также!

«Собираем все вместе»

[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
person Sep Roland    schedule 20.12.2015
comment
Просто комментарий об обновлении SS:SP . То, что вы говорите, на самом деле верно для совместимости с самым широким спектром оборудования, но основная причина для пары CLI / STI заключается в том, чтобы обойти ошибку в некоторых глючных процессорах 8088 1980-х годов. Когда вы обновляете регистр SS с помощью инструкции MOV, предполагается, что прерывания должны быть отключены до тех пор, пока не будет выполнена инструкция NEXT. Обычно это обновление для SP . К сожалению, на некоторых процессорах 8088 с ошибками прерывания не отключались, как ожидалось, поэтому для избежания ошибки можно было явно использовать CLI/STI. - person Michael Petch; 20.12.2015
comment
Я проголосовал за ваш ответ. Сделаю еще одно маленькое наблюдение. Вы полагаетесь на AL как на количество прочитанных секторов. Есть некоторые старые BIOS, которые возвращают значение в AL только в том случае, если CF=1 (да, звучит немного безумно, но это правда). Большинство загрузчиков (нацеленных на более широкий спектр реального оборудования) не предполагают, что на значение AL можно положиться. Таким образом, обычно большинство загрузчиков считывают один сектор за раз или получают геометрию диска (из BPB или BDA) и выполняют только чтение нескольких секторов, которые никогда не охватывают дорожку (цилиндр). Но ваш код будет работать с большинством оборудования (и со всеми известными мне эмуляторами). - person Michael Petch; 20.12.2015