汇编代码在bochs中可以正确显示字符串,但在VirtualBox虚拟机里却显示不出来。

tzblue 2017-12-19 10:19:50

问题已经找到,创建虚拟机的时候硬盘应该设置为PATA模式


如题,写了两个小程序,其中一个是用户程序,用来显示一句话;另一个是加载程序,位于主引导扇区中,用来把用户程序加载到内存中并执行。

现在出现这个问题,在VirtualBox里面只能看到光标闪动,却不见字符串;用bochs调试最终可以看到这个字符串。下面是程序分别在VirtualBox里运行,在bochs里调试时的截图。







///////////////////////////////////////////////____用户程序__________________________/////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

;用户程序说明:
;头部包含了程序长度program_length、入口点code_entry、重定位表项数和重定位表

;===========================================================
SECTION header align=16 vstart=0
program_length dd program_end ;程序长度

code_entry dw start ;入口点偏移地址
dd section.code.start ;入口点段地址

realloc_tbl_len dw (header_end-code_segment)/4 ;重定位表项

code_segment dd section.code.start ;重定位表,分别是代码段、数据段、栈段的段地址
data_segment dd section.data.start
stack_segment dd section.stack.start

header_end: ;头部的结尾


;===========================================================
SECTION data align=16 vstart=0

msg dd 'Welcome to GoalPower workroom!' ;在屏幕上显示的字符串
dd 0

;===========================================================
SECTION code align=16 vstart=0

start:
mov ax, [stack_segment] ;初始化栈段寄存器
mov ss, ax
mov sp, stack_end ;初始化栈指针寄存器

mov ax, [data_segment] ;初始化数据段寄存器(这里没有初始化ES,因为在加载程序里面已经设置完了)
mov ds, ax

mov bx, msg ;将要显示的字符串的偏移地址赋给BX
call put_string ;调用显示字符串的过程

jmp $ ;无限循环

;----------------------------------------------------------------------------------------------------
;显示字符串的过程,将字符逐个放到显存里
;输入:BX=字符串的首地址

put_string:
push ax
push cx
push dx
push bx
push si
push ds
push es

mov ax, 0xB800
mov es, ax
mov ch, 0x04
xor si, si

.put_char:
mov cl, [bx]
or cl, cl
jz .exit
mov word [ES:si], cx
add si, 2
inc bx
jmp .put_char

.exit:

pop es
pop ds
pop si
pop bx
pop dx
pop cx
pop ax

ret


;===========================================================
SECTION stack align=16 vstart=0 ;栈段

resb 0x100

stack_end: ;栈段的结尾


;===========================================================
SECTION trail align=16
program_end: ;程序的结尾




///////////////////////////////////////////////____加载程序__________________________/////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

app_lba_start equ 100 ;用户程序所在的逻辑扇区号

SECTION mbr align=16 vstart=0x7c00

;设置堆栈段和栈指针
mov ax,0
mov ss,ax
mov sp,ax

mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx
mov ds,ax ;设置DS和ES指向该段以进行操作
mov es,ax

;以下读取程序的起始部分
xor di,di
mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号
xor bx,bx ;加载到DS:0x0000处
call read_hard_disk_0

;以下判断整个程序有多大
mov dx,[2]
mov ax,[0]
mov bx,512 ;512字节/扇区
div bx
cmp dx,0
jnz @1 ;未除尽,因此结果比实际扇区数少1
dec ax ;已经读了一个扇区,扇区总数减1
@1:
cmp ax,0 ;考虑实际长度小于等于512个字节的情况
jz direct

;读取剩余的扇区
push ds ;以下要用到并改变DS寄存器

mov cx,ax ;循环次数(剩余扇区数)
@2:
mov ax,ds
add ax,0x20 ;得到下一个以512字节为边界的段地址
mov ds,ax

xor bx,bx ;每次读时,偏移地址始终为0x0000
inc si ;下一个逻辑扇区
call read_hard_disk_0
loop @2 ;循环读,直到读完整个功能程序

pop ds ;恢复数据段基址到用户程序头部段

;计算入口点代码段基址
direct:
mov dx,[0x08]
mov ax,[0x06]
call calc_segment_base
mov [0x06],ax ;回填修正后的入口点代码段基址

;开始处理段重定位表
mov cx,[0x0a] ;需要重定位的项目数量
mov bx,0x0c ;重定位表首地址

realloc:
mov dx,[bx+0x02] ;32位地址的高16位
mov ax,[bx]
call calc_segment_base
mov [bx],ax ;回填段的基址(用户程序中此位置在编译时放置的是汇编地址,在这里要计算出逻辑段的段地址)
add bx,4 ;下一个重定位项(每项占4个字节)
loop realloc

jmp far [0x04] ;转移到用户程序

;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;输入:DI:SI=起始逻辑扇区号
; DS:BX=目标缓冲区地址
push ax
push bx
push cx
push dx

mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数

inc dx ;0x1f3
mov ax,si
out dx,al ;LBA地址7~0

inc dx ;0x1f4
mov al,ah
out dx,al ;LBA地址15~8

inc dx ;0x1f5
mov ax,di
out dx,al ;LBA地址23~16

inc dx ;0x1f6
mov al,0xe0 ;LBA28模式,主盘
or al,ah ;LBA地址27~24
out dx,al

inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al

.waits: ;等待硬盘回应
in al,dx
and al,0x88
cmp al,0x08
jnz .waits

mov cx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [bx],ax
add bx,2
loop .readw

pop dx
pop cx
pop bx
pop ax

ret

;-------------------------------------------------------------------------------
calc_segment_base: ;计算16位段地址
;输入:DX:AX=32位物理地址
;返回:AX=16位段基地址
push dx

add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02]
shr ax,4
ror dx,4
and dx,0xf000
or ax,dx

pop dx

ret

;-------------------------------------------------------------------------------
phy_base dd 0x10000 ;用户程序被加载的物理起始地址

times 510-($-$$) db 0
db 0x55,0xaa


...全文
1847 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
均陵鼠侠 2021-01-11
  • 打赏
  • 举报
回复
x86汇编语言:从实模式到保护模式
u011498527 2021-01-08
  • 打赏
  • 举报
回复
我将 virtualbox 控制器的型号改为 PIIX4 就可以显示了。
ACgay_caoyue 2020-08-01
  • 打赏
  • 举报
回复
请问楼主是怎么解决的呢?我也有这个问题
远处自有美景 2018-07-17
  • 打赏
  • 举报
回复
我是bochs不能显示,但在VirtualBox里是正常的
tzblue 2017-12-19
  • 打赏
  • 举报
回复
代码没有问题,问题出在设置硬盘镜像时没有选择PATA模式,现在一切都正常了。

21,458

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧