系统环境:基于Intel CPU的mac电脑,装有vim(自带),nasm(通过brew安装)和virtualbox(从官网下载dmg安装)。
创建虚拟机:在virtualbox中创建一个名为“learn”的虚拟机,虚拟磁盘选择vhd格式,大小2GB,固定大小,命名为learn.vhd,放在/Users/<当前用户>/programs/asm目录下(因为固定大小的vhd最简单,方便后面的写入操作)。
编写代码:打开终端,在vim中输入以下代码,保存为printstr.asm
[bits 16]
mov ax, 0x07c0
mov ds, ax ;ds==0x07c0, boot sector
mov ax, 0xb800
mov es, ax
mov si, 0 ;si is the index of the array
mov di, 0 ;di is the offset of video memory
jmp near print
message db "Hello world!", 0
print:
mov bl, message[si]
inc si
mov [es:di], bl
inc di
mov byte [es:di], 0x07
inc di
cmp byte message[si], 0x00
jne print
halt:
jmp halt
times 510-($-$$) db 0
db 0x55
db 0xaa
在汇编、运行之前,先分析一下代码。
Intel x86 CPU在开机后,会进入16位实模式,[bits 16]告诉汇编器,以16位方式汇编。
在实模式下,CPU用“段地址+偏移地址”方式寻址,默认段寄存器为ds,另外es也可用作段寄存器。0x07c00为硬盘主引导扇区加载到内存后的起始地址,0xb8000为显存起始地址。
程序首先初始化段寄存器ds为0x07c0,以保证后面对message,print和halt标号的寻址正常工作,然后初始化es为0xb800。注意,初始化ds时,不能直接使用“mov ds, 0x07c0”,es同理,因为CPU不允许直接把立即数赋值给段寄存器。本程序用数组形式表示字符串“Hello world!”,si表示message数组的当前索引,di表示相对于显存起始地址的偏移量。jmp near print表示无条件跳转到print。在内存中,指令和数据的形式没有区别,都是二进制数。为了避免把hello world字符串当成指令执行而导致错误,必须跳过这个字符串。
到了print,程序正式开始工作。si的初始值为0,此时,“mov bl, message[si]”把message[0],即字符‘H’加载到寄存器bl。然后,si值加1,指向下一个字符‘e’。di的初始值为0,此时“mov [es:di], bl”将bl中的内容存储到es与di合成的内存地址,即0xb8000。然后,di的值加1,es:di合成的地址变为0xb8001。接着,程序将字体格式数据0x07(黑底,白字,不闪烁)写入0xb8001,di再加1,[es:di]变为0xb8002。然后比较此时si指向的字符ASCII码是否为0,如果不为0,说明字符串没有结束,跳转到print,继续循环;如果为0,说明字符串已结束,退出循环。此时,si指向的字符为‘e’,ASCII码不为0,跳转到print,继续循环。程序再把字符‘e’写到0xb8002,把0x07写到0xb8003......如此循环往复,直到碰到字符串后面的0,退出此循环,来到halt。
halt下的指令为“jmp halt”,是一个死循环,计算机会一直执行“jmp halt”。这个死循环是必要的,因为如果没有这个死循环,计算机会把后面的内存空间的内容当成指令来执行,导致错误。
“times 510-($-$$) db 0”是一条nasm伪指令,表示从当前位置开始填充0,直到第510个字节,db 0x55和db 0xaa表示填充0x55和0xaa。编写上述伪指令也是必要的,因为主引导扇区的相关规范规定主引导扇区为512字节,且以55aa结尾。
汇编:进入printstr.asm所在目录,在终端输入以下命令
nasm printstr.asm -o printstr.bin -f bin
写入vhd虚拟磁盘:在终端输入
dd if=printstr.bin of=../learn.vhd bs=512 count=1 conv=notrunc
此处一定要加上“conv=notrunc”选项。如果不加,learn.vhd会变成512字节。加上后,表示把printstr.bin写入到learn.vhd的前512字节,并保留learn.vhd后面原有的内容。只有加上这个选项,程序才能正常工作。
运行:打开virtualbox,运行learn虚拟机,即可看到在虚拟机开机后,屏幕上显示“Hello world!”