У вас есть более фундаментальная проблема, чем двойное использование times
: вы используете ключ -p
NASM для предварительного включения файла kernel.asm при сборке bootloader.asm. Даже если вы отбросите одно или оба times
использования, ядро будет сначала собрано и выполнено первым, а не загрузчик, выполняющийся первым.
Я исправил ваш пример так, как он должен: Вывод kernel.asm во второй сектор размером 512 байт, затем в загрузчик загрузите этот сектор из загрузочного модуля и перейдите к нему.
Вот фиксированный источник, bootloader.asm:
cpu 8086
bits 16
section loader vstart=7C00h start=0
MOV AX, 0x1000
MOV DS, AX
MOV ES, AX
MOV SS, AX
xor sp, sp
MOV DH, 0x0
MOV CH, 0x0
MOV CL, 0x02
xor bx, bx
mov di, 16
ReadFloppy:
dec di
jz .error
MOV AH, 0x02
MOV AL, 0x01
INT 0x13
JC ReadFloppy
JMP 0x1000:0x0
.error:
mov ax, '!' | 0E00h
mov bx, 7
int 10h
.halt:
sti
hlt
jmp .halt
TIMES 510 - ($ - $$) db 0
DW 0xAA55
%include "kernel.asm"
Это kernel.asm:
section kernel vstart=0 follows=loader
MOV AH, 0x0E
MOV BH, 0x00
MOV BL, 0x07
MOV SI, msg
CALL PrintString
halt:
sti
hlt
jmp halt
PrintString:
nextch:
MOV AL, [SI]
OR AL, AL
JZ exit
INT 0x10
INC SI
JMP nextch
exit:
RET
msg db 'Hello world from the kernel!', 13, 10, 0
TIMES 512 - ($ - $$) db 0
Вот как собрать и запустить весь пример:
$ nasm -f bin bootloader.asm -o bootloader.bin && qemu-system-i386 -drive file=bootloader.bin,if=floppy,format=raw,index=0,media=disk
$ nasm -f bin bootloader.asm -o bootloader.bin && qemu-system-i386 -drive file=bootloader.bin,format=raw,index=0,media=disk
Обратите внимание, что я изменил следующее:
Вместо org
я использовал директиву section для загрузчика с vstart=7C00h
и start=0
. По сути, это то же самое, что и ваш оригинал, но лучше подходит для другого раздела.
Ядро включено с помощью директивы %include
и помещается после загрузчик, а не до него (как при использовании -p
).
Таким образом, файл kernel.asm больше не встречается в командной строке NASM.
Ядро помещено в отдельный раздел, который имеет vstart=0
(вычисляет метки, как если бы этот раздел был загружен в начале сегмента) и follows=loader
(чтобы правильно разместить его в выходном файле как второй сектор).
В инструкции сразу после установки ss
я установил sp
, так как мы не можем быть уверены в значении sp
до этого. Обнуление sp
(с xor
) означает, что стек начинается в верхней части сегмента, то есть в месте, куда мы читаем сектор ядра.
Я отбросил RET
после JMP nextch
. Это никогда не было достигнуто.
Я изменил остановку l oop с JMP $
на sti
\ hlt
\ jmp
на холостой ход при остановке. Это означает, что процесс qemu не будет тратить процессорное время при запуске этого l oop.
Я сбросил ваш MOV DL, 0x80
. Это позволяет загружаться либо с жесткого диска, либо с дискеты; на нашей точке входа ROM-B IOS инициализировал dl
с единицей, из которой мы загружаемся.
Я переместил сегменты и инициализацию стека до чтения сектора. Это гарантирует, что стек не перекрывается с местом назначения чтения.
Я добавил счетчик al oop в di
для попыток чтения. Я добавил дополнительную ветку, если она заканчивается, и на ней отображается восклицательный знак, а затем останавливается. Повторная попытка чтения не является неправильной, но если она не удалась постоянно, максимальное количество попыток должно быть максимальным.
Я отбросил настройки fs
и gs
, поскольку они никогда не используются .