Код Option Rom не может напечатать предполагаемую строку с помощью эмуляции Qemu

Пытался протестировать простую программу ISA Option Rom, скомпилированную с помощью FASM или NASM, которая просто распечатывает простое сообщение «Hello World».

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

Лучшее предположение состоит в том, что необходимо установить команду ORG, потому что неправильный адрес памяти копируется в AL из SI с использованием LODSB. Любые идеи??

use16        ; ISA module operates in the 16-bit segment.

DB      55h, 0AAh          ; Boot signature
DB      01h               ; Block size in sectors (200h each)



xor ax, ax ; make it zero
mov ds, ax

start:

mov si, text_string     ; Put string position into SI
call print_string       ; Call our string-printing routine

.bounce:
jmp .bounce                   ; Jump here - infinite loop!

print_string:                   ; Routine: output string in SI to screen

.repeat:
   ;mov ah, 09h             ; int 10h 'print char' function
   ; mov bh, 0x00
   ; mov bl, 0x03
   ; mov cx, 1h

lodsb                   ; Get character from string

or al,al
jz .done

mov ah, 0x0E
int 0x10

; If char is zero, end of string
; int 10h                 ; Otherwise, print it

mov bh, 00h
mov ah, 03h
int 10h
mov ah, 02h
mov bh, 00h
inc dl
int 10h
jmp .repeat

.done:
mov al, 'D'
mov bh,0x00
mov cx,1
mov ah,0ah
int 10h

ret

text_string db 'Hello World!',,0
times 512-($-$$) db 0   

person david moheban    schedule 27.02.2018    source источник
comment
Пробовали использовать отладчик? QEMU может действовать как удаленный gdb или использовать BOCHS со встроенным отладчиком. Проверьте, каковы начальные значения DS и CS; установка DS=0 может быть неправильной в зависимости от того, как вы это используете.   -  person Peter Cordes    schedule 27.02.2018
comment
Нет, не используя отладчик. Я новичок в сборке. Провел некоторое исследование и для тех, кто разрабатывает загрузчик, правильно установил адрес ORG для работы QEMU. Поскольку я использую Qemu с директивой '-option-rom', я предполагаю, что он извлекает мусор из неправильного адресного пространства. Я попытался следовать приведенному здесь примеру: > stackoverflow.com/questions/33974115/, но не могу заставить его работать. Я имею в виду, что я могу создать загрузчик, преобразовать его в файл IMG с DD и загрузить его с помощью Qemu, и я увижу напечатанную строку, но не для дополнительного ПЗУ!   -  person david moheban    schedule 27.02.2018
comment
Написание asm без использования отладчика похоже на попытку собрать робота с завязанными глазами. Время, затраченное на настройку и отладку, окупится многократно. Возможность исследовать регистры и один шаг — это золото.   -  person Peter Cordes    schedule 27.02.2018
comment
Хорошо поиграл с BOCHS и попытался заставить его работать. Не так легко, как Qemu, встать и пойти. Требует настройки. Мало того, что он вылетает, когда я вставляю туда свой самодельный опциональный ром. Немного сложно. Надеюсь, ты прав.   -  person david moheban    schedule 28.02.2018
comment
Хорошо Наконец-то Bochs перешел в режим отладки. Оказалось, что вам нужно запускать с правами администратора, иначе он не загрузит виртуальную машину. В любом случае, теперь у меня есть другая проблема, заключающаяся в том, что когда я пытаюсь загрузить ПЗУ ISA, добавляя его в «Подменю памяти», Bochs будет жаловаться «Недостаточно места для расширения ПЗУ». Может быть, мне нужно дать ему больше памяти?   -  person david moheban    schedule 28.02.2018
comment
Я не занимаюсь 16-битной разработкой, поэтому IDK. Возможно, задайте новый вопрос (или погуглите еще) об использовании BOCHS для запуска дополнительных ПЗУ ISA. (Я удивлен, что BOCHS потребуются привилегии root для чего-либо, если только по умолчанию он не пытается пройти через ваши настоящие последовательные порты или что-то в этом роде.)   -  person Peter Cordes    schedule 28.02.2018
comment
Хорошо, я решил проблему с отображением строки мусора. Мне пришлось ввести это в начале моей программы: pusha cli mov ax,cs mov ds,ax mov es,ax   -  person david moheban    schedule 02.03.2018
comment
Итак, вы должны принять ответ MichaelPetch, он объясняет настройку сегмента. Вы уверены, что вам нужно cli, а не cld? Почему отключение прерываний помогает?   -  person Peter Cordes    schedule 02.03.2018


Ответы (2)


Основная проблема заключается в том, что вы инициализируете 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
comment
Если у кого-то есть ссылка на лучшие практики и соглашения устаревших вариантов ПЗУ BIOS, в которых указано, что нужно сохранить и восстановить, я был бы более чем счастлив добавить такую ​​информацию в ответ и при необходимости скорректировать код. - person Michael Petch; 01.03.2018
comment
Клянусь, ваш ответ появился сразу после того, как я внес свои исправления, но должен сказать спасибо. Цените понимание и помощь. Спасибо. - person david moheban; 02.03.2018
comment
Копия файла конфигурации Window/Bochs, который работал у меня, находится здесь: capp-sysware.com/misc/stackoverflow/49001298//bochsrc.bxrc. Просто замените s:\optrom.bin именем и расположением вашего файла. - person Michael Petch; 03.03.2018
comment
Спасибо, что поделились этим файлом конфигурации Windows. После исправления проблем с путем и указания правильного загрузочного образа все заработало! Спасибо. - person david moheban; 04.03.2018
comment
Возникла новая проблема с Qemu, связанная с невозможностью загрузки Duet, если кто-то может помочь, очень ценю это. Спасибо.: stackoverflow.com /вопросы/49119624/ - person david moheban; 07.03.2018

Решена проблема с печатью на экран. Должен предшествовать код с этим:

  pusha
  cli
  mov   ax,cs
  mov   ds,ax
  mov   es,ax

Вот окончательный код для всех, кто может его изучить:

use16        ; ISA module operates in the 16-bit segment.

ROM_SIZE_IN_BLOCK = 1  ; 1 means ROM size is 1 block (512 bytes)
ROM_SIZE_IN_BYTE = ROM_SIZE_IN_BLOCK * 512

DB      55h, 0AAh          ; Boot signature
DB      01h               ; Block size in sectors (200h each)





start:


      pusha
      cli
      mov   ax,cs
      mov   ds,ax
      mov   es,ax
      call cls


    mov si, text_string     ; Put string position into SI
    call print_string       ; Call our string-printing routine
    ;retf

   .bounce:
    jmp .bounce                   ; Jump here - infinite loop!





print_string:                   ; Routine: output string in SI to screen

.repeat:
    mov ah, 09h             ; int 10h 'print char' function
    mov bh, 0x00
    mov bl, 0x03
    mov cx, 01h
    cld
    lodsb                   ; Get character from string
    cmp al, 0
    je .done


    ; If char is zero, end of string
    int 10h                 ; Otherwise, print it
    mov bh, 00h
    mov ah, 03h
    int 10h
    mov ah, 02h
    mov bh, 00h
    inc dl
    int 10h
    jmp .repeat

 .done:

ret

clrscr:
   mov dh, 0
   mov dl, 0
   call set_cursor
   mov ah, 0x0a
   mov al, ' '
   mov bh, 0
   mov cx, 2000
   int 0x10
   ret


cls:
  pusha
  mov ah, 0x00
  mov al, 0x03  ; text mode 80x25 16 colours
  int 0x10
  popa
  ret

set_cursor:
   mov ah, 0x02
   int 0x10
   ret


text_string db "Hello World!",0

 ;times 512-($-$$) db 0

times (ROM_SIZE_IN_BYTE-$) db 0 ; use 00h as the padding bytes until we reach the ROM size

 ;patch_byte is calculated and automagically inserted below
PREV_CHKSUM = 0
repeat $
   load CHKSUM byte from %-1
   CHKSUM = (PREV_CHKSUM + CHKSUM) mod 0x100
   PREV_CHKSUM = CHKSUM
end repeat
store byte (0x100 - CHKSUM) at ($-1)  ; store the patch_byte         

Все еще не могу заставить его работать с BOCHS, но теперь Qemu нравится!

person david moheban    schedule 02.03.2018
comment
Я попробовал ваш код в Bochs, и он работал, когда он был помещен в свободное место в памяти 0xd0000. - person Michael Petch; 02.03.2018
comment
Я хотел бы заставить его работать в Bochs. Не могли бы вы поделиться своим файлом конфигурации, чтобы я мог видеть, что я устанавливаю неправильно? Спасибо. - person david moheban; 02.03.2018
comment
Я добавил свою конфигурацию Bochs в конец своего ответа. Это одиночка, потому что я сгенерировал его с помощью инструмента конфигурации Bochs. - person Michael Petch; 02.03.2018
comment
Не удалось заставить ваш файл конфигурации быть принятым Bochs. Возможно, это потому, что я запускаю его в Windows. В следующий раз попробую запустить в линуксе. - person david moheban; 03.03.2018
comment
Когда вы добавляете свой вариант rom в конфигурацию (вы можете сделать это через меню конфигурации Bochs Windows в разделе Memory) - по какому адресу вы его размещаете? Вы пробовали 0xd0000, как я предлагал ранее? - person Michael Petch; 03.03.2018
comment
Привет. Есть еще вопрос о Qemu и usb, если кто-то хочет попытаться ответить на этот: stackoverflow.com/questions/49100056/ - person david moheban; 04.03.2018