【发布时间】:2018-12-20 03:03:13
【问题描述】:
我用 x86 汇编语言编写了一小段代码,它在裸硬件上运行,此时已启用受保护的 32 位模式
但是,我遇到了一个与屏幕打印有关的问题。我已经读到这样做没有中断可以将字符加载到一个特殊的内存区域,即 RAM 地址 0xb8000。
知道了这一点,我写了一个函数来做这件事,并且在 qemu 中测试时证明是成功的。但是,问题来了,当我尝试在真机(即联想 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
编辑:在 start32 将 CL 设置为 0:修复了问题
【问题讨论】:
-
MOV [ES:DI], DL应该做什么? “初始化段寄存器”是什么方式?您甚至没有将ES:DI设置为任何内容。那可能应该崩溃。尽管ES仍然缓存了实模式值,但也许可行? -
你从不设置 SS。通常你会
mov ss, AX/mov SP, whatever。 (Mov to SS 将中断推迟到下一条指令结束,因此您可以自动设置 SS:SP wrt.中断。)设置 SP 而不设置 SS 可能会将您的堆栈放在任何地方,可能会重叠代码。 -
您也可以在
PSR32B中执行CMP CL, 0X0,但由于之前调用PSR16B,CL可能不为零,因此您正在跳过初始化。 PS:检查bochs是否有效。如果没有,则使用其内置调试器。 -
您在使用
lodsb之前也忘记了cld。一些真正的 BIOS 离开 DF 设置。 -
在上述所有其他 cmets 之上。从 USB 启动时,您的 BIOS 是否设置为将 USB 媒体启动为 FDD(软盘驱动器)或 HDD(硬盘驱动器)?
标签: assembly x86 nasm qemu bootloader