Сборка 32-битной печати для отображения кода запускается на qemu, не работает на реальном оборудовании - PullRequest
0 голосов
/ 20 декабря 2018

Я написал небольшой фрагмент кода на языке ассемблера x86, который работает на "голом" оборудовании, который на данный момент выходит за рамки включения защищенного 32-разрядного режима

Я столкнулся с проблемойТем не менее, относящиеся к печати на экране.Я читал, что для этого без прерываний можно загружать символы в специальную область памяти, а именно в адрес ОЗУ 0xb8000.

Зная это, я написал функцию, которая делает именно это, и она оказалась успешной при тестировании.в кему.Однако возникает проблема: когда я пытаюсь запустить эту программу на реальной машине (а именно на ноутбуке Lenovo G470), она не может отобразить нужную строку, записав ее в область памяти дисплея.Все остальные строки, отображаемые с использованием прерываний BIOS, работают должным образом, увы, функция 32-битной печати, похоже, ничего не делает.Программа не аварийно завершает работу, и мигающий курсор появляется после строк, напечатанных с использованием прерываний.

Если это имеет какое-либо значение, я пытаюсь загрузить его с USB-накопителя

Поставитьнамного более прямо, строка S3 не печатается на моем ноутбуке, она находится в qemu, и я не знаю почему.

Вот мой код, я заранее извиняюсь за отсутствие изящества:

[BITS 16]                       ;NASM 16-BIT MODE
[ORG 0x7C00]                    ;OFFSET MEMORY LOCATIONS TO BSECTOR/ BIOS LOADS MBR INTO THIS ADDRESS.
XOR AX, AX                      ;INITIALIZE SEGMENT REGISTERS BY AX    
MOV DS, AX                      ;INDIRECTLY INITIALIZING DS REGISTER
MOV ES, AX                      ;INDIRECTLY INITIALIZING ES REGISTER
MOV GS, AX                      ;INDIRECTLY SETTING GS REGISTER
MOV FS, AX                      ;INDIRECTLY SETTING DS REGISTER
MOV SP, 0X900                   ;SETTING STACK POINTER TO 0X900, OFFSET FROM 0X0 = 0X7C00
MOV BP, SP                      ;
JMP WORD 0x0:START16            ;JUMP TO START OF PROGRAM

PSR16B:                         ;16-BIT PRINT ROUTINE
  MOV SI, BX                ;ASSIGN SI POSITION OF STRING
  MOV AH, 0XE               ;TTY MODE
PSR16BLOP0:                  ;PRINT LOOP
     LODSB                  ;LOAD SI INTO AL AND +1 ADDRESS OF STRING
     CMP AL, 0X0            ;CONDITIONAL
     JE PSR16LOP0END        ;END LOOP
     INT 0X10               ;BIOS INTERRUPT, PRINT AL TO SCREEN
     JMP PSR16BLOP0         ;LOOP TO LOP
PSR16LOP0END:                ;END OF LOOPA
  MOV AL, 0XA               ;NEW LINE ASCII
  INT 0X10                  ;RAISING PRINT BIOS INTERRUPT
  MOV AL, 0XD               ;NEWLINE 'CHARACTER' ASCII
  INT 0X10                  ;RAISING PRINT BIOS INTERRUPT
  ADD CH, 0X1               ;ADD ONE TO COUNTER
  RET                       ;RETURN TO LAST ADRESS BEFORE CALL 
PSR16BEND:                      ;END OF FUNCTION, UNUSED

S0:                             ;STRING
    DB 'BOOTING SEQUENCE INITIALIZED',0
S1:                             ;STRING
    DB 'LOADING GDT',0
S2:                             ;STRING
    DB 'ENTERING 32-BIT PROTECTED MODE',0

GDTS:                           ;START OF GLOBAL DESCRIPTOS TABLE

       GDTN:                           ;NULL BEGGINING, 8 BYTES
       DQ 0X0
  GDTC:                           ;TABLE FOR CODE SEGMENT
       DW 0XFFFF               ;BITS 0-15 OF LIMIT(MAXADRESSUNIT) ||0X0-0XF
       DW 0X0                  ;BASE(SEGMENTSTART), BITS 0-15 ||0XF-0X1F
       DB 0X0                  ;BASE, BITS 16-23 ||0X20-0X27
       DB 10011010B            ;ACCESS BYTE ||0X28-0X2F
       DB 11001111B            ;SECOND4BITS:LIMITBITS 16-19/FIRST4BITS:FLAGS= ||0X30-0X37
       DB 0X0                  ;BASE, BITS 24-31||0X38-0X3F
  GDTD:                           ;TABLE FOR DATA SEGMENT
       DW 0XFFFF               ;BITS 0-15 OF LIMIT(MAXADRESSUNIT) ||0X0-0XF
       DW 0X0                  ;BASE(SEGMENTSTART), BITS 0-15 ||0XF-0X1F
       DB 0X0                  ;BASE, BITS 16-23 ||0X20-0X27
       DB 10010010B            ;ACCESS BYTE ||0X28-0X2F
       DB 11001111B            ;SECOND4BITS:LIMITBITS 16-19/FIRST4BITS:FLAGS= ||0X30-0X37
       DB 0X0                  ;BASE, BITS 24-31||0X38-0X3F
GDTE:                           ;END OF GLOBAL DESCRIPTION TABLE

GDTDESC:                        ;GDT DESCRIPTOR
    DW GDTE - GDTS - 1      ;SIZE OF GDT, 2 BYTE, MUST BE LESS THAN 1
    DD GDTS                 ;ADRESS OF GDT, 4 BYTE
GDTDESCEND:                     ;END OF GDTDESC, UNUSED

CODESEG EQU GDTC - GDTS         ;CODE SEGMENT ADDRESS CONSTANT
DATASEG EQU GDTD - GDTS         ;DATA SEGMENT ADDRESS CONSTANT

START16:                        ;START OF BOOTSECTOR PROGRAM
    MOV CH, 0X0             ;LINES COUNTER.
    MOV BX, S0              ;SET STRING POINTER
    CALL PSR16B             ;CALL PRINT FUNCTION
    MOV BX, S1              ;SET STRING POINTER
    CALL PSR16B             ;CALL PRINT FUNCTION
    LGDT [GDTDESC]          ;LOADING GDT DESCRIPTOR
    MOV BX, S2              ;SET STRING POINTER
    CALL PSR16B             ;CALL PRINT FUNCTION
    CLI                     ;SEVERING INTERRUPTS
    MOV EAX, CR0            ;INDIRECTLY SETTING PROTECTED MODE BIT
    OR EAX, 0X1             ;SETTING PMODE BIT
    MOV CR0, EAX            ;PROTECTED MODE ENABLED
    JMP CODESEG:START32     ;FAR JUMP TO 32 BIT LAND

[BITS 32]                       ;NASM 32-BIT MODE

S3:                             ;STRING
    DB '32-BIT PROTECTED MODE ENABLED', 0

PSR32B:                        ;PRINT TO DISPLAY ROUTINE FOR 32-BIT   PREOTECTED MODE
 PSR32BLOP0:             ;INITIALIZE VGA REGION POINTER
  CMP CL, 0X0          ;CONDITIONAL, IF FALSE SKIP INITIALIZATION
  JNE PSR32BLOP0END    ;END LOOP
  MOV EBX, 0XB8000      ;INITIALIZING POINTER TO VGA MEMORY REGION
  ADD CL, 0X1          ;ADD TO COUNTER
  JMP PSR32BLOP0       ;LOOP
 PSR32BLOP0END:             ;END OF FUNCTION

 PSR32BLOP1:             ;USED TO INTIALIZE VGA MEMORY POINTER, NEWLINE OFFSET FROM 16-BIT LINES
      CMP CH, 0X0         ;END CONDITIONAL
     JE PSR32BLOP1END;    ;JUMP TO END OF LOOP
     ADD EBX, 0XA0        ;ADD EQUIVALENT OF ONE LINE TO POINTER
     SUB CH, 0X1         ;LOOP END COUNTER
     JMP PSR32BLOP1    ;LOOP
 PSR32BLOP1END:          ;USED TO INTIALIZE VGA MEMORY POINTER, END
    MOV ESI, EDX            ;LOAD LODSW STRING POINTER WITH APPROPIATE ADDRESS
    MOV AH, 0X0F            ;BLACK BACKGROUND, WHITE LETTERS
  PSR32BLOP2:                ;PRNTINH LOOP
     LODSB                    ;LOAD CHARACTER INTO AL
     CMP AL, 0X0             ;CHECK FOR END OF STRING
     JE PSR32BLOP2END         ;IF AX IS 0 JUMP TO END OF LOOP
     MOV [EBX], AX           ;LOAD WORD CHARACTER INTO VGA MEMORY
     ADD EBX, 0X2             ;MOVE TO NEXT CHARACTER WORD IN MEMORY ADDRESS
     JMP PSR32BLOP2           ;LOOP BACK TO START
 PSR32BLOP2END:              ;END OF LOOP

    RET
 ENDPSR32B:                     ;END OF FUNCTION, UNUSED


 START32:                        ;START OF 32 BIT PROTECTED PROGRAM
    MOV AX, DATASEG        ;SET DATA SEGMENT ADDRESS TO POINTER
    MOV DS, AX              ;INITIALIZING SEGMENT REGISTERS
    MOV SS, AX              ;INITIALIZING SEGMENT REGISTERS
    MOV [ES:DI], DL              ;INITIALIZING SEGMENT REGISTERS
    MOV DS, AX              ;INITIALIZING SEGMENT REGISTERS
    MOV GS, AX              ;INITIALIZING SEGMENT REGISTERS
    MOV EDX, S3              ;STRING POINTER DX
    CALL PSR32B             ;CALL PRINT ROUTINE// THIS IS A TEST
    JMP $                   ;LOOP TO INFINITY



 PAD:                          ;BOOTSECTOR PADDING & MAGIC NUMBER
    TIMES 510-($-$$) DB 0   ;FILL 0S TO END OF SECTOR
    DW 0xAA55               ;BOOT SIGNATURE

РЕДАКТИРОВАТЬ: Установка CL в 0 при запуске 32: исправлена ​​проблема

1 Ответ

0 голосов
/ 20 декабря 2018

Ваш код имеет ряд серьезных проблем:

  • Это все прописные буквы и их трудно прочитать.
  • Кажется, что нет понимания того, как в реальном режиме сегмент: смещение адресации работает в реальном режиме.
  • Регистры используются без инициализации.
  • Неправильная настройка регистров сегмента.

Версия кода, котораяделает что-то подобное, код ниже.Код комментируется по большей части.Важные вещи для понимания:

  • Когда BIOS печатает символы, он обновляет текущую строку и столбец в Область данных BIOS (BDA) .В защищенном режиме вы можете прочитать байт в ячейке памяти 0x450 для столбца и 0x451 для строки.Вы можете использовать эту информацию для продолжения с того места, где остановился BIOS.
  • 16-разрядное слово в адресе памяти 0x44a - это ширина экрана текущего режима видео, предварительно установленного BIOS.
  • Каждыйячейка на экране составляет два байта.Текущее смещение байта в видеопамяти может быть вычислено как 0xb8000 + (cur_row * screen_width + cur_col) * 2
  • Включить Блок параметров BIOS (BPB) , чтобы обеспечить правильную загрузку изображения прииспользование USB-носителя в режиме эмуляции дискеты (FDD).Этот код обеспечивает BPB для дискеты размером 1,44 МБ.
  • Чтобы правильно адресовать всю память, вы должны включить линию A20 . Предоставленный код использует метод быстрого включения, но может быть несовместим свсе оборудование, но должно работать на большинстве эмуляторов.
  • Когда строка печатается с использованием print_string_pm, аппаратная позиция курсора обновляется на set_cursor после помещения строки в память дисплея.

bpb.inc :

global bpb_disk_info

    jmp boot_start
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

bpb_disk_info:
    ; Dos 4.0 EBPB 1.44MB floppy
    OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
    bytesPerSector:    dw    512
    sectPerCluster:    db    1
    reservedSectors:   dw    1
    numFAT:            db    2
    numRootDirEntries: dw    224
    numSectors:        dw    2880
    mediaType:         db    0xf0
    numFATsectors:     dw    9
    sectorsPerTrack:   dw    18
    numHeads:          dw    2
    numHiddenSectors:  dd    0
    numSectorsHuge:    dd    0
    driveNum:          db    0
    reserved:          db    0
    signature:         db    0x29
    volumeID:          dd    0x2d7e5a1a
    volumeLabel:       db    "NO NAME    "
    fileSysType:       db    "FAT12   "

boot.asm :

bits 16
ORG 0x7c00

VIDEO_TEXT_ADDR     EQU 0xb8000 ; Hard code beginning of text video memory
ATTR_WHITE_ON_BLACK EQU 0x07    ; White on black attribute

CR                  EQU 0x0d    ; Carriage return
LF                  EQU 0x0a    ; Line feed

; Include a BPB (1.44MB floppy with FAT12) to be more comaptible with USB floppy media
%include "bpb.inc"

boot_start:
    xor ax, ax                  ; DS=SS=0. Real mode code below doesn't use ES
    mov ds, ax
    mov ss, ax                  ; Stack at 0x0000:0x7c00 below bootloader
    mov sp, 0x7c00
    cld                         ; Set string instructions to use forward movement

    mov si, boot_init_msg       ; Print boot initialization message
    call print_string_rm

    ; Fast method of enabling A20 may not work on all x86 BIOSes
    ; It is good enough for emulators and most modern BIOSes
    ; See: https://wiki.osdev.org/A20_Line
    cli                         ; Disable interrupts
    in al, 0x92
    or al, 2
    out 0x92, al                ; Enable A20 using Fast Method

    mov si, load_gdt_msg        ; Print loading GDT message
    call print_string_rm

    lgdt [gdtr]                 ; Load our GDT

    mov si, enter_pm_msg        ; Print protected mode message
    call print_string_rm

    mov eax, cr0
    or eax, 1
    mov cr0, eax                ; Set protected mode flag
    jmp CODE32_SEL:start32      ; FAR JMP to set CS

bits 32
start32:
    mov ax, DATA32_SEL          ; Setup the segment registers with data selector
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov esp, 0x9c000            ; Set the stack to grow down from area under
                                ;     EBDA/Video memory

    xor eax, eax                ; Clear EAX for the instructions below
    mov al, [0x450]             ; Byte at address 0x450 = last BIOS column position
    mov [cur_col], eax          ; Copy to current column
    mov al, [0x451]             ; Byte at address 0x451 = last BIOS row position
    mov [cur_row], eax          ; Copy to current row

    mov ax, [0x44a]             ; Word at address 0x44a = # of columns (screen width)
    mov [screen_width], eax     ; Copy to screen width

    mov eax, in_pm_msg          ; Print message we are in protected mode
    call print_string_pm        ; EAX = first parameter

end_loop:
    hlt
    jmp end_loop

; Function: set_cursor
;           set the hardware cursor position based on the
;           current column (cur_col) and current row (cur_row) coordinates
; See:      https://wiki.osdev.org/Text_Mode_Cursor#Moving_the_Cursor_2
;
; Inputs:   None
; Clobbers: EAX, ECX, EDX

set_cursor:
    mov ecx, [cur_row]          ; EAX = cur_row
    imul ecx, [screen_width]    ; ECX = cur_row * screen_width
    add ecx, [cur_col]          ; ECX = cur_row * screen_width + cur_col

    ; Send low byte of cursor position to video card
    mov edx, 0x3d4
    mov al, 0x0f
    out dx, al                  ; Output 0x0f to 0x3d4
    inc edx
    mov al, cl
    out dx, al                  ; Output lower byte of cursor pos to 0x3d5

    ; Send high byte of cursor position to video card
    dec edx
    mov al, 0x0e
    out dx, al                  ; Output 0x0e to 0x3d4
    inc edx
    mov al, ch
    out dx, al                  ; Output higher byte of cursor pos to 0x3d5

    ret

; Function: print_string_pm
;           Display a string to the console on display page 0 in protected mode.
;           Handles carriage return and line feed.
;           Doesn't handle tabs, backspace, wrapping and scrolling.
;
; Inputs:   EAX = Offset of address to print
; Clobbers: EAX, ECX, EDX

print_string_pm:
    push edi
    push esi
    push ebx
    mov esi, eax                ; Set ESI to beginning of string

    ; Assume base of text video memory is ALWAYS 0xb8000
    mov ebx, VIDEO_TEXT_ADDR    ; EBX = beginning of video memory

    mov eax, [cur_row]          ; EAX = cur_row
    mul dword [screen_width]    ; EAX = cur_row * screen_width
    mov edx, eax                ; EDX = copy of offset to beginning of line
    add eax, [cur_col]          ; EAX = cur_row * screen_width + cur_col
    lea edi, [ebx + eax * 2]    ; EDI = memory location of current screen cell

    mov ah, ATTR_WHITE_ON_BLACK ; Set attribute
    jmp .getch
.repeat:
    cmp al, CR                  ; Is the character a carriage return?
    jne .chk_lf                 ;     If not skip and check for line feed
    lea edi, [ebx + edx * 2]    ; Set current video memory pointer to beginning of line
    mov dword [cur_col], 0      ; Set current column to 0
    jmp .getch                  ; Process next character
.chk_lf:
    cmp al, LF                  ; Is the character a line feed?
    jne .write_chr              ;     If not then write character
    mov eax, [screen_width]
    lea edi, [edi + eax * 2]    ; Set current video memory ptr to same pos on next line
    inc dword [cur_row]         ; Set current row to next line
    mov ah, ATTR_WHITE_ON_BLACK ; Reset attribute
    jmp .getch                  ; Process next character

.write_chr:
    inc dword [cur_col]         ; Update current column
    stosw

.getch:
    lodsb                       ; Get character from string
    test al, al                 ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character

.end:
    call set_cursor             ; Update hardware cursor position

    pop ebx
    pop esi
    pop edi
    ret

bits 16

; Function: print_string_rm
;           Display a string to the console on display page 0 in real mode
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string_rm:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; 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

cur_row:      dd 0x00
cur_col:      dd 0x00
screen_width: dd 0x00

boot_init_msg:
    db "Booting sequence initialized...", CR, LF, 0
load_gdt_msg:
    db "Loading GDT...", CR, LF, 0
enter_pm_msg:
    db "Entering 32-bit Protected Mode...", CR, LF, 0
in_pm_msg:
    db "Executing code in protected mode!", CR, LF, 0

align 8
gdt_start:
    dd 0                        ; null descriptor
    dd 0

gdt32_code:
    dw 0FFFFh                   ; limit low
    dw 0                        ; base low
    db 0                        ; base middle
    db 10011010b                ; access
    db 11001111b                ; 32-bit, 4kb granularity, limit 0xffffffff bytes
    db 0                        ; base high

gdt32_data:
    dw 0FFFFh                   ; limit low (Same as code)
    dw 0                        ; base low
    db 0                        ; base middle
    db 10010010b                ; access
    db 11001111b                ; 32-bit, 4kb granularity, limit 0xffffffff bytes
    db 0                        ; base high
end_of_gdt:

gdtr:
    dw end_of_gdt - gdt_start - 1
                                ; limit (Size of GDT - 1)
    dd gdt_start                ; base of GDT

CODE32_SEL equ gdt32_code - gdt_start
DATA32_SEL equ gdt32_data - gdt_start

; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db  0
dw 0xaa55

Этот код может быть собран и встроен в 1,44Образ дискеты MiB с этими командами:

nasm -f bin boot.asm -o boot.bin

# Build 1.44MB disk image
dd if=/dev/zero of=disk.img bs=1024 count=1440
dd if=boot.bin of=disk.img conv=notrunc

Вывод должен выглядеть примерно так:

enter image description here

...