Основная проблема заключается в том, что вы инициализируете DS значением 0. Когда BIOS передает управление вашей процедуре инициализации, она выполняет FAR CALL по адресу дополнительного ПЗУ +3. Вызов FAR установит CS в сегмент, в котором было загружено дополнительное ПЗУ, и установит IP (указатель инструкции) в 3. 3 — это смещение сразу после байтов подписи и размера.
Установив DS на ноль, вы получите доступ к своей строке относительно сегмента 0x0000. Вы хотите использовать сегмент, в котором загружено дополнительное ПЗУ. Для этого вы инициализируете DS значением регистра CS. Вместо:
xor ax, ax ; make it zero
mov ds, ax
Ты делаешь это:
mov ax, cs ; CS contains segment we are running in
mov ds, ax ; so copy it to DS
Вы также должны установить флаг направления для строковых инструкций для пересылки с помощью CLD. Вы не можете гарантировать, что BIOS установил его таким образом, прежде чем вызывать наш код дополнительного ПЗУ.
Поскольку я никогда не писал дополнительное ПЗУ и не мог найти какой-либо конкретной документации по соглашению о вызовах, я не был уверен, нужно ли сохранять все изменяемые регистры. Я просмотрел дополнительные ПЗУ на своем ПК, используя ree
программа под Linux. Я заметил, что они используют pusha
и popa
для сохранения и восстановления всех регистров общего назначения, и они используют push
/pop
для сохранения/восстановления отдельных сегментных регистров. Вероятно, хорошей практикой будет делать это в собственном дополнительном ПЗУ. Одно требование, восходящее к старый Phoenix BIOS заключается в том, что после байта размера должен быть NEAR JMP к точке входа кода инициализации.
По завершении процедуры инициализации дополнительного ПЗУ вы возвращаетесь обратно в BIOS, используя retf
(возврат FAR), чтобы BIOS мог продолжить поиск других дополнительных ПЗУ и завершить последовательность загрузки.
Я немного исправил ваш код, так как в процедуре печати были некоторые сбои. Этот код должен работать:
use16 ; ISA module operates in the 16-bit segment.
DB 55h, 0AAh ; Boot signature
DB 01h ; Block size in sectors (200h each)
jmp start ; NearJMP part of Phoenix BIOS specification
start:
pushf ; Save the flags as we modify direction bit
pusha ; Save all general purpose registers
push ds ; we modify DS so save it
cld ; Ensure forward string direction
mov ax, cs ; CS contains segment we are running in
mov ds, ax ; so copy it to DS
mov si, text_string ; Put string position into SI
call print_string ; Call our string-printing routine
pop ds ; Restore all registers and flags we saved
popa
popf
retf ; Far return to exit option init routine
print_string: ; Routine: output string in SI to screen
mov ah, 0eh ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 10h ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
; String ends with 0dh (Carriage return) and 0ah (linefeed) to
; advance cursor to the beginning of next line
text_string db 'Hello World!', 0dh, 0ah, 0
times 512-($-$$) db 0
Примечание. В коде используются инструкции pusha
/popa
, которые доступны только на процессорах 80186+. Если вы нацеливаетесь на 8086/8088, вам нужно будет отдельно нажать и вытолкнуть каждый регистр, который вы изменяете.
Также можно не использовать сегмент регистра DS и переопределить LODSB переопределением CS. Его можно изменить на cs lodsb
. При этом вам не нужно сохранять и восстанавливать DS, потому что DS останется без изменений. Вам также не нужно копировать CS в DS. Вы также можете отказаться от сохранения и восстановления флагов и установить бит направления с помощью CLD, если замените cs lodsb
на:
mov al, [cs:si]
inc si
Упрощенный код может выглядеть так:
use16 ; ISA module operates in the 16-bit segment.
DB 55h, 0AAh ; Boot signature
DB 01h ; Block size in sectors (200h each)
jmp start ; NearJMP part of Phoenix BIOS specification
start:
pusha ; Save all generel purpose registers
mov si, text_string ; Put string position into SI
call print_string ; Call our string-printing routine
popa ; Restore all general purpose registers
retf ; Far return to exit option init routine
print_string: ; Routine: output string in SI to screen
mov ah, 0eh ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 10h ; print character
.getch:
mov al, [cs:si]
inc si
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
; String ends with 0x0d (Carriage return) and 0x0a (linefeed) to
; advance cursor to the beginning of next line
text_string db 'Hello World!', 0dh, 0ah, 0
times 512-($-$$) db 0
Мой файл конфигурации Bochs, который я использовал для тестирования на Debian Linux:
# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, iodebug=1
config_interface: textconfig
display_library: x
memory: host=32, guest=32
romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x0, options=none
vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest"
# no floppyb
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=none
ata0-slave: type=none
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata1-master: type=none
ata1-slave: type=none
ata2: enabled=0
ata3: enabled=0
optromimage1: file="optrom.bin", address=0xd0000
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=5, realtime=1
cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU "
cpuid: mmx=1, apic=xapic, simd=sse2, sse4a=0, misaligned_sse=0, sep=1, movbe=0, adx=0
cpuid: aes=0, sha=0, xsave=0, xsaveopt=0, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0
cpuid: smep=0, smap=0, mwait=1
print_timestamps: enabled=0
debugger_log: -
magic_break: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
# no loader
log: -
logprefix: %t%e%d
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
mouse: type=ps2, enabled=0, toggle=ctrl+mbutton
speaker: enabled=1, mode=system
parport1: enabled=1, file=none
parport2: enabled=0
com1: enabled=1, mode=null
com2: enabled=0
com3: enabled=0
com4: enabled=0
Эта конфигурация предполагает, что дополнительное ПЗУ находится в файле optrom.bin
и будет загружено по адресу памяти 0xd0000.
Дополнительные ПЗУ должны иметь контрольную сумму, вычисленную и помещенную в последний байт файла образа. QEMU предоставляет сценарий, который можно использовать для этой цели. Чтобы обновить контрольную сумму изображения, вы можете сделать:
python signrom.py inputimagefile outputimagefile
person
Michael Petch
schedule
01.03.2018
cli
, а неcld
? Почему отключение прерываний помогает? - person Peter Cordes   schedule 02.03.2018