Как загружать/считывать секторы из памяти?

Пишу свою ОС. Сейчас разбираюсь как загружать/считывать секторы из памяти.

Опищу ситуацию: заполнил два сектора (по 512 байт) после загрузочного 0xDADA и 0xFACE соотвестственно, считываю их с помощью 0x13 прерывания по адресу 0x9000 (функция disk_load). После чего хочу убедиться в правильности считывания: вывожу первое слово по адресу 0x9000 и первое слово по адресу (0x9000 + 512). Ожидаю увидеть 0xDADA и 0xFACE соответственно, но вижу следущее:

0xDADA

0xZRVW

Была мысль о том, что возможно это прерывание не может за раз несколько секторов считать, но гугл говорит, что может. Возможно проблема в том, что при выводе слов надо явно указать сегментный регистр, но это тоже не работает. Прошу, объясните, что не так. Код прилагаю. Использую эмулятор Bochs и ассемблер NASM.

Главная функция boot_sect.asm :

[org 0x7c00]

    mov [BOOT_DRIVE], dl; Запомимаем на будушее наш загрузочный диск(Зачем это нужно?)

    mov bp, 0x8000 ; Убираем стек с пути
    mov sp, bp ; SP - вершина стека, BP - база стека - нижняя граница

    mov bx, 0x9000 ; ES:BX = 0x0000:0x9000
    mov dh, 2; Считываем два сектора 
    mov dl, [BOOT_DRIVE] ; ?
    
    call disk_load

    mov dx, [0x9000]; Выводим первое машинное слово первого считанного сектора. Ожидаем: 0xDADA
    call print_hex

    call print_nl

    mov dx, [0x9000 + 512]; Выводим первое машинное слово второго считанного сектора. Ожидаем: 0xFACE
    call print_hex

    jmp $


;Includes

%include "functions/print/print_string.asm"
%include "functions/print/print_hex.asm"
%include "functions/disk/disk_load.asm"

; Global variables
BOOT_DRIVE: db 0

; Padding and magic number

times 510-($-$$) db 0
dw 0xaa55

; BIOS загружает только первый 512-байтный сектор из загрузочного диска
; Таким образом если вручную добавим еще пару секторов в загрузочный диск путём
; повторения похожих чисел, то мы сможем убедится в том, что мы действительно
; загрузили(прочитали) эти два дополнительных сектора из диска, с которого бутаемся.

times 256 dw 0xdada ; Первый доп сектор 
times 256 dw 0xface ; Второй доп cектор 

Функция, считывающая секторы functions/dick/disk_load.asm :

disk_load:
    pusha
    push dx ;Сохраним DX в стеке -> потом сможем узнать сколько секторов было запрошено на чтение 
    
    mov ah, 0x02; BIOS disk read function
    mov al, dh;  2 сектора читаем
    mov cl, 0x02 ; Начиная со второго сектора читаем(тот, что сразу после загрузочного)
    mov ch, 0x00 ; Цилиндр 0
    mov dh, 0x00 ; Читаем с первой стороны диска
    
    int 0x13

    jc read_error ; Если CF(carry flag) установлен в 1 => не получилось считать, тогда выполняем инструкции по метке read_error 
    
    pop dx ; Возвращаем значение DX из стека
    
    cmp dh, al ;Если AL(кол-во реально считанных секторов) != DH(ожидаемое кол-во прочитанных секторов) => прыжок на sectors_error 
    jne sectors_error

    popa
    ret 

read_error:
    mov bx, READ_ERROR_MSG
    call print_string
    call print_nl
    jmp $

sectors_error: 
    mov bx, SECTORS_ERROR_MSG
    call print_string
    call print_nl
    jmp $

READ_ERROR_MSG db "Disk read error!", 0
SECTORS_ERROR_MSG db "Read incorrect number of sectors!", 0

Функции вывода строк functions/print/print_string.asm :

print_string:
    pusha
    mov ah, 0x0e
    return_to_check:
        mov al, [bx]
        cmp al, 0
        jne print_symbol
        popa
        ret

    print_symbol:
        int 0x10
        inc bx
        jmp return_to_check

print_nl:
    pusha 

    mov ah, 0x0e ; BIOS teletype routine
    mov al, 0x0a ; LF = line feed = '\n'
    int 0x10 ; print on the screen interrupt
    mov al, 0x0d ; CR = carriage return = '\r'
    int 0x10

    popa
    ret

Функция вывода шестнадцатеричного значения(dx как параметр) functions/print/print_hex.asm :

print_hex:
    pusha ; сохраняем все регистры в стеке
    mov cx, 4 ; Счетчик цикла. Нам надо напечать 4 символа после 0x

char_loop:
    dec cx  ; декрементация счетчика

    mov ax, dx ; в ax хранится текущий выводимый символ
    shr dx, 4  ; смещаем dx на длину одного символа символа
    and ax, 0xf ; Изолируем текущий символ, обнуляя остальные

    mov bx, HEX_OUT ; Устанавливаем BX на адрес памяти нашей строки
    add bx, 2   ; Пропускаем первые два символа '0x'
    add bx, cx  ; пермещамся на текущий заполняемый элемент(зависит от счетчика) - изначально это послений элемент

    cmp ax, 0xa  ; Проверяем явлется текущий выводимый элемент числом или буквой
    jl set_letter ; Если число, то сразу идем устанавливать его в нашу строку
    add byte [bx], 7 ; Если буква, то берем байт по адресу, хранящемуся в BX, складывает значение этого байта с 7 и записывается результат туда же

    ; Добавляем 7 потому что от последней цифры до первой буквы в таблице ASCII расстояние 7 
    ; и чтобы его скомпенсировать мы и добавлем это расстояние

    ; Полное объяснение: код буквы, как и код цифры есть простое смещение относительно НУЛЯ(0x30 = 00110000 = '0')
    ; Но так как в таблице ASCII между цифрами и Заглавными латинскими буквами есть расстояние в 7 символов(там другие символы) 
    ; -> чтобы получить нужный код буквы неоюходимо учитывать это расстояние.
    ; Получается, в случае если очередной символ шестнадцатеричного числа, которое нам надо вывести это буква, то 
    ; мы сначала компенсируем расстояние м/у последней цифрой и первой буквой(прибавлем 7 к коду '0'), а потом прибавляем
    ; уже смещение этой буквы относительно нуля, если бы буквы шли сразу после цифр. 

    jmp set_letter

set_letter:
    add byte [bx], al ; Добавляем значение в al(код буквы/цифры) к символу в BX -> получаем в нужный ASCII код соотв символа в BX.   

    cmp cx, 0 ; Проверяем, не установили ли мы уже все символы
    je print_hex_done ; Если да, то выводим готовую строку на экран
    jmp char_loop  ; Если нет, то продолжаем - выполняем еще одну итерацию кода выше(char_loop)

print_hex_done:
    mov bx, HEX_OUT
    call print_string ; Функция печати строки

    popa ; Восстанавливаем все регистры из стека
    ret


HEX_OUT:
    db '0x0000', 0

Ответы (1 шт):

Автор решения: Иванов Леонид

Проблема была функции print_hex. А именно в том, что после выхода из функции шаблонная строка HEX_OUT не обнулялась, следовательно код символа в новой выводимой строке просто прибавлялся к коду, лежавшего там до этого символа(с прошлого вывода).

→ Ссылка