- VMWare Workstation Pro 16
- Ubuntu 16.04-i386
org 07c00h ; 告诉编译程序加载到 7c00 处
mov ax, cs
mov ds, ax
mov es, ax
call DispStr ; 调用显示字符串例程
jmp $ ; 无限循环
DispStr:
mov ax, BootMessage
mov bp, ax ; ES : BP = 串地址
mov cx, 16 ; CX = 串长度
mov ax, 01301h ; AH = 13 , AL = 01h
mov bx, 000ch ; 页号为 0 (BH = 0) 黑底红字 (BL = 0Ch, 高亮)
mov dl, 0
int 10h ; 10h 号中断
ret
BootMessage: db "Hello, OS World!"
times 510-($-$$) db 0 ; $-$$ 表示本行距离程序开始处的相对距离
; 用 0 填充剩下的空间,使生成二进制恰好 512 字节
dw 0xaa55 ; 结束标志
megs: 32
display_library: sdl
floppya: 1_44=a.img, status=inserted
boot: floppy
eaglebear2002@ubuntu:~/Desktop$ nasm boot.asm -o boot.bin
eaglebear2002@ubuntu:~/Desktop$ bximage
========================================================================
bximage
Disk Image Creation Tool for Bochs
$Id: bximage.c 11315 2012-08-05 18:13:38Z vruppert $
========================================================================
Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] fd
Choose the size of floppy disk image to create, in megabytes.
Please type 0.16, 0.18, 0.32, 0.36, 0.72, 1.2, 1.44, 1.68, 1.72, or 2.88.
[1.44]
I will create a floppy image with
cyl=80
heads=2
sectors per track=18
total sectors=2880
total bytes=1474560
What should I name the image?
[a.img]
The disk image 'a.img' already exists. Are you sure you want to replace it?
Please type yes or no. [no] yes
Writing: [] Done.
I wrote 1474560 bytes to a.img.
The following line should appear in your bochsrc:
floppya: image="a.img", status=inserted
eaglebear2002@ubuntu:~/Desktop$ dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes copied, 0.000178427 s, 2.9 MB/s
eaglebear2002@ubuntu:~/Desktop$ bochs -f bochsrc
实验当中遇到问题:在共享文件夹 /mnt/hgfs/Lab-01
中,生成 a.img
文件并写入内容后使用 vi -b a.img
查看文件内容发现文件内容为空,并且一段时间后该文件会消失,推测是该文件在 Windows 系统中被识别为格式不对的图片并被自动删除以保护系统。该推测没有得到证实。
实验者将实验目录移动至虚拟机桌面后顺利完成了实验。
- 可以正确计算非负大整数加法
- 可以正确计算非负大整数乘法
- 输入
q
命令时退出程序 - 对于缺少运算符等错误,输出
Invalid
报错信息并可以继续输入,程序不会崩溃
见文件 lab1.asm
。
复杂指令集(Complex Instruction Set Computing,CISC),对 CPU 逻辑电路的设计要求高,简化了对编译器的要求,但是带来了 CPU 成本和功耗的增加。
精简指令集(Reduced Instruction Set Computing,RISC),通过多条简单指令拼凑一个复杂功能,对编译器优化要求高,但是功耗低,CPU 设计简单,主要用在端侧。
CISC 指令长度不固定,指令较多,这将导致指令切割复杂,通常需要切割未多个微操作码,然后执行计算。
RISC 指令长度固定,指令较少,指令码切割简单,这将更容易并行化,执行效率高。
80x86 采用的是复杂指令集。
“大端”和“小端”表示多字节值的哪一端存储在该值的起始地址处;小端存储在起始地址处,即是小端字节序;大端存储在起始地址处,即是大端字节序。
- 大端存储模式:数据的低位保存在内存中的高地址中,数据的高位保存在内存中的低地址中(从高位开始存储)
- 小端存储模式:数据的低位保存在内存中的低地址中,数据的高位保存在内存中的高地址中(从低位开始存储)
80x86 是小端存储。
在实验代码中,该段数据定义及其在 ELF 文件中对应的十六进制表示如下:
section .data
errorMessage db "Invalid", 000Ah, 00h
- 数据寄存器:AX(accumulator)、BX(base)、CX(counter)、DX
- 指针寄存器:SP(堆栈指针)、BP(基数指针)
- 变址寄存器:SI(源变址)、DI(目的变址)
- 控制寄存器:IP(指令指针)、FLAG(状态标记)
- 段寄存器:CS(代码段)、DS(数据段)、SS(堆栈段)、ES(附加段)
前三类称为通用寄存器。
系统中共有 4 个 16 位段寄存器,即代码段寄存器 CS、数据段寄存器 DS、堆栈段寄存器 SS 和附加段寄存器 ES。这些段寄存器的内容与有效的地址偏移量一起,可确定内存的物理地址。通常 CS 划定并控制程序区,DS 和 ES 控制数据区,SS 控制堆栈区。
寻址方式是指寻找指令或操作数有效地址的方式,从而能够取出操作数或指令。
8086 有七种寻址方式:
- 立即寻址:
mov ax 1234h
- 直接寻址:
mov ax [1234h]
- 寄存器寻址:
mov ax bx
- 寄存器间接寻址:
mov ax [bx]
,操作数有效地址在寄存器当中(SI、DI、BX、BP) - 寄存器相对寻址:
mov ax [si+3]
- 基址加变址寻址:
mov ax [bx+di]
,把一个基址寄存器(BX、BP)的内容,加上变址寄存器(SI、DI)的内容 - 相对基址加变址寻址:
mov ax [bx+di+3]
直接寻址是一种基本的寻址方法,其特点是:在指令格式的地址的字段中直接指出操作数在内存的地址。由于操作数的地址直接给出而不需要经过某种变换,所以称这种寻址方式为直接寻址方式,如 mov ax [add]
。
直接寻址的优点是简单,指令在执行阶段仅访问一次主存,不需要专门计算操作的地址;缺点是 add
的位数决定了该指令操作数的寻址范围,操作数的地址不易修改。
- 利用寄存器传递参数:缺点是能传递的参数有限,因为寄存器有限
- 利用约定的存储单元传递参数
- 利用堆栈传递参数(常用)
- 利用 CALL 后续区传递参数:CALL 后续区是指位于 CALL 指令后的存储区,主程序在调用子程序之前,把入口参数存入 CALL 指令后面的存储区,子程序根据保存在堆栈中的返回地址找到入口参数。由于这种方法把数据和代码混在一起,在 x86 系列中使用的不多
getline
函数调用系统中断,从键盘读入字符串;puts
函数使用调用系统中断,输出内容到屏幕。
其中 eax
表示中断调用号, ebx, ecx, edx, esi, edi
依次用于传参,最多传五个参数。
见代码注释。
传送指令 MOV
表示把一个字节、字或双字的操作数从源位置传送到目的位置,源操作数的内容不变。
加载有效地址指令 LEA
(Load effective address) 指令实际上仅仅做寄存器和立即数的计算,并不真实访问内存,其载入的内存地址,仅仅是一个数值,并不需要对该内存地址有访问权限,该指令类似 C 语言取地址操作符 &
。
org 07c00h
的作用:告诉汇编器(而不是引导程序),当前这段代码会放在 07c00h 处。所以,如果之后遇到需要绝对寻址的指令,那么绝对地址就是 07c00h 加上相对地址。在第一行加上 org 07c00h
只是让编译器从相对地址 07c00h 处开始编译第一条指令,相对地址被编译加载后就正好和绝对地址吻合。
如果删去这一行,装载进入内存时不会出错,但计算地址时会出错。
$-$$
可能会经常被用到,它表示本行距离程序开始处的相对距离。times 510-($-$$) db 0
的意思就是将 0 这个字节重复 510-($-$$)
遍,直到数据内容有 510B 为止。这样,加上结束标志 0XAA55
占用 2B,恰好是 512B。软盘、硬盘的最小粒度为扇区,每个扇区固定为 512 字节字节,所以我们要把程序加满到能填充一个扇区。
display_library
:Bochs 使用的 GUI 库megs
:虚拟机内存大小(MB)floppya
:虚拟机外设,软盘为a.img
⽂件boot
:虚拟机启动方式,从软盘启动
boot.bin
需要放在软盘的第一个扇区。因为 BIOS 程序检查软盘 0 面 0 磁道 1 扇区,如果扇区以 0xaa55
结束,则认定为引导扇区,将其 512B 的数据加载到内存的 0x7c00 处,然后设置 PC,跳到内存 0x7c00 处开始执行代码。
-
跳⼊保护模式:最开始的 x86 处理器 16 位,寄存器用 ax, bx 等表示,称为实模式。后来扩充成 32 位,eax,ebx 等,为了向前兼容,提出了保护模式。必须从实模式跳转到保护模式,才能访问 1M 以上的内存。
-
启动内存分页。
-
从
kernel.bin
中读取内核,并放入内存,然后跳转到内核所在的开始地址,运行内核 。跟 boot 类似,使用汇编直接在软盘下搜索kernel.bin
。但是,不能把整个kernel.bin
放在内存,而是要以 ELF 文件的格式读取并提取代码。