NASM — использовать метки для кода, загруженного с диска

В качестве учебного опыта я пишу загрузчик для 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

person James M. Lay    schedule 20.05.2015    source источник


Ответы (1)


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

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

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

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

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

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