В качестве учебного опыта я пишу загрузчик для BIOS в NASM в 16-битном реальном режиме в моем эмуляторе x86 Qemu.

BIOS загружает ваш загрузочный сектор по адресу 0x7C00. NASM предполагает, что вы начинаете с 0x0, поэтому ваши ярлыки бесполезны, если вы не укажете источник с помощью [org 0x7C00] (или, предположительно, других методов) . Но когда вы загружаете загрузчик 2-го уровня, его ОЗУ отличается, что до чертиков усложняет использование меток в этом недавно загруженном коде.

Какой рекомендуемый способ справиться с этим? Это территория компоновщика? Должен ли я использовать сегментные регистры вместо org?

Заранее спасибо!

P.s. Вот код, который работает прямо сейчас:

[bits 16]
[org 0x7c00]
LOAD_ADDR: equ 0x9000   ; This is where I'm loading the 2nd stage in RAM.
start:
    mov bp, 0x8000      ; set up the stack
    mov sp, bp          ; relatively out of the way

    call disk_load      ; load the new instructions
                        ; at 0x9000

    jmp LOAD_ADDR

%include "disk_load.asm"
times 510 - ($ - $$) db 0
dw 0xaa55 ;; end of bootsector

seg_two:

    ;; this is ridiculous. Better way?

    mov cx, LOAD_ADDR + print_j - seg_two
    jmp cx
    jmp $

print_j:
    mov ah, 0x0E
    mov al, 'k'
    int 0x10
    jmp $

times 2048 db 0xf
1
James M. Lay 20 Май 2015 в 22:11

1 ответ

Лучший ответ

Возможно, вы усложняете это дело (не то чтобы это было тривиально!)

Ваши ярлыки работают нормально и будут продолжать работать нормально. Помните, что если вы посмотрите под капот на сгенерированный машинный код, ваши короткие прыжки (все после seg_two в том, что вы опубликовали) являются относительными прыжками. Это означает, что ассемблеру фактически не нужно вычислять реальный адрес, ему просто нужно вычислить смещение от текущего кода операции. Однако, когда вы загружаете свой код в ОЗУ по адресу 0x9000, это совсем другая история.

Лично, когда вы пишете именно такой код, я бы разделил его. Загрузочный сектор останавливается на dw 0xaa55, а второй этап получает свой собственный файл с ORG 0x9000 наверху.

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

Надеюсь, это имеет смысл. :)

1
James M. Lay 21 Май 2015 в 07:55
Вы имеете в виду, что я могу поместить 1-й и 2-й этапы в разные файлы, собранные независимо с их собственной директивой org, и объединить полученные двоичные файлы? Я попробую это ....
 – 
James M. Lay
21 Май 2015 в 07:01
Да, это работает! И это имеет смысл; Я собираюсь переждать ночь, чтобы посмотреть, есть ли у кого-нибудь дополнительные данные, но это вероятный кандидат на принятый ответ, @DavidHoelzer!
 – 
James M. Lay
21 Май 2015 в 07:15
1
@ JamesM.Lay :) Поздравляю. Я понимаю что ты чувствуешь. Я еще один человек, который получает странное удовольствие от написания кода загрузочного сектора. FWIW, я бы рекомендовал использовать этот подход до тех пор, пока вы не написали драйвер файловой системы, особенно если вы переключаетесь в защищенный режим. Пока вы ни с чем не делитесь диском, это самый простой подход, если вы отслеживаете, какая часть находится в каком секторе.
 – 
David Hoelzer
21 Май 2015 в 13:21