汇编32位打印以显示代码在qemu上运行,无法在实际硬件上运行 [英] Assembly 32-bit print to display code runs on qemu, fails to work on real hardware

查看:148
本文介绍了汇编32位打印以显示代码在qemu上运行,无法在实际硬件上运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经用x86汇编语言编写了一小段代码,该代码在裸机上运行,​​目前,它可以启用受保护的32位模式

I've written a small piece of code in x86 assembly language that runs on bare hardware which, at this point, goes as far as enabling protected 32-bit mode

但是,我遇到了与屏幕打印有关的问题.我读过要做到这一点而不会中断,可能会将字符加载到特殊的内存区域中,即RAM地址0xb8000.

I've run into a problem, however, pertaining printing to the screen. I've read that to do so without interrupts one may load characters into a special memory region, namely RAM address 0xb8000.

知道这一点后,我编写了一个函数来完成该任务,并且在qemu中进行测试时证明是成功的.但是,问题来了,当我尝试在真实计算机(即Lenovo G470笔记本电脑)上运行此程序时,它无法通过写入显示内存区域来显示想要显示的字符串.使用BIOS中断显示的所有其他字符串均按预期工作,可惜32位打印功能似乎无能为力.但是该程序不会崩溃,并且在使用中断打印的行之后会出现一个闪烁的光标.

Knowing this, I wrote a function that does exactly that, and it proved a success when tested in qemu. However, and here comes the problem, when I attempt to run this program on a real machine (namely a Lenovo G470 laptop) it fails to display the string I want shown by writing to the display memory region. All other strings displayed using BIOS interrupts work as intended, alas, the 32-bit printing function does not seem to do anything. The program doesn't crash though, and a blinking cursor appears after the lines printed using interrupts.

如果有任何重要意义,我正在尝试从USB驱动器启动它

If it's of any importance, I'm trying to boot this off a USB drive

坦率地说,字符串S3不在笔记本电脑中打印,而是在qemu中打印,我不知道为什么.

Put much more bluntly, string S3 isn't printing in my laptop, it is in qemu and I've no idea why.

这是我的代码,由于缺乏技巧,我向您致歉:

Here is my code, I apologize in advance for the lack of finesse:

[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

在start32处将CL设置为0:解决了问题

Setting CL to 0 at start32: fixed the problem

推荐答案

您的代码有很多严重的问题:

Your code has a number of serious problems:

  • It is all upper case and hard to read.
  • There seems to be lack of understanding of how realmode segment:offset addressing works in real mode.
  • Registers used without being initialized.
  • Improperly setting segment registers.

执行类似操作的代码版本如下.该代码大部分被注释.重要事项:

A version of the code that does something similar is the code below. The code is commented for the most part. Important things to understand:

  • BIOS打印字符时,它会更新 BIOS数据区(BDA).在保护模式下,您可以读取存储位置0x450的列(字节)和0x451(行)的字节.您可以使用此信息在BIOS中断处继续操作.
  • 内存地址0x44a的16位字是BIOS先前设置的当前视频模式的屏幕宽度.
  • 屏幕上的每个单元格为两个字节.视频内存中的当前字节偏移可以计算为0xb8000 +(cur_row * screen_width + cur_col)* 2
  • 包括 BIOS参数块(BPB),以便在以下情况下正确加载映像在软盘(FDD)仿真模式下使用USB磁盘介质.该代码为1.44MiB软盘提供了BPB.
  • 要正确处理所有内存,您应该启用A20线.提供的代码使用快速启用方法,但可能不与所有硬件兼容,但应可在大多数仿真器上使用.
  • 使用print_string_pm打印字符串时,将字符串放置在显示存储器中后,硬件光标位置将用set_cursor更新.
  • When the BIOS prints characters it updates the current row and column in the BIOS Data Area (BDA). When in protected mode you can read the byte in memory location 0x450 for the column and 0x451 for the row. You can use this information to continue where the BIOS left off.
  • The 16-bit word at memory address 0x44a is the screen width of the current video mode previously set by the BIOS.
  • Each cell on the screen is two bytes. The current byte offset in video memory can be computed as 0xb8000+(cur_row * screen_width + cur_col) * 2
  • Include a BIOS Parameter Block (BPB) to allow the image to be properly loaded when using USB disk media in floppy disk (FDD) emulation mode. This code provides a BPB for a 1.44MiB floppy.
  • To properly address all memory you should enable the A20 line.The code provided uses the fast enable method, but may not be compatible with all hardware but should work on most emulators.
  • When a string is printed using print_string_pm the hardware cursor position is updated with set_cursor after the string is placed in display memory.

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.44MiB软盘映像中:

This code can be assembled and built into a 1.44MiB floppy disk image with these commands:

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


输出应类似于:


The output should look something like:

这篇关于汇编32位打印以显示代码在qemu上运行,无法在实际硬件上运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆