汇编语言学习与Makefile入门
这一章的内容如章节标题所示,纯粹对汇编语言以及Makefile基本语法的入门。没有太多关于操作系统方面的知识。学过汇编后直接看代码即可。
接下来要用文本编辑器进行开发。
这里推荐个文本编辑器:Notepad++
继续昨天的内容开发。
汇编语言入门
把 \30天自制操作系统\projects\02_day\helloos3目录复制到 30天自制操作系统\tolset中
源码
; hello-os
; TAB=4
ORG 0x7c00 ; 指名启动区的装载地址,不能随便指定其他位置
; 以下代码为启动区
; 以下段落为标准FAT12格式软盘专用代码
JMP entry
DB 0x90 ; 此处没有上一节中的0xeb, 0x4e
DB "HELLOIPL" ; 启动区名称,可取任意字符串(8字节)
DW 512 ; 每个扇区的大小(必须为512字节)
DB 1 ; 簇大小,(必需为1个扇区)
DW 1 ; FAT的起始位置(一般是从第一个扇区开始)
DB 2 ; FAT的个数(必须为2)
DW 224 ; 根目录大小,(一般设置为224项)
DW 2880 ; 该硬盘大小(必须为2880扇区)
DB 0xf0 ; 硬盘种类(必须是0xf0)
DW 9 ; FAT长度(必须是9个扇区)
DW 18 ; 1个磁道有几个扇区(必须是18个)
DW 2 ; 磁头数(软盘必须是2个)
DD 0 ; 不使用分区,此处为0
DD 2880 ; 重写一次,硬盘大小
DB 0,0,0x29 ; 不知道是干什么的,但必要写
DD 0xffffffff ; 可能是卷标号码
DB "HELLO-OS " ; 硬盘名称(11字节)
DB "FAT12 " ; 硬盘文件系统名称(8字节)
RESB 18 ; 空出18字节
; 程序主体
entry:
MOV AX,0 ; 初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX
MOV SI,msg ; 把msg的地址赋值给SI
putloop:
MOV AL,[SI] ; 把SI中地址所指的内存单元中的值赋值给AL
ADD SI,1 ; SI的内容+1
CMP AL,0 ; AL和0比较
JE fin ; AL等于0时跳转fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用BIOS中的显卡中断
JMP putloop ; 无条件跳转putloop
fin:
HLT ; 让CPU待机,等待指令
JMP fin ; 循环
;到DB 0x55, 0xaa为止,以下代码是要在屏幕上显示的内容。
msg:
DB 0x0a, 0x0a ; 2个换行
DB "hello, world"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$ ; 当前到0x1fe地址的位置全部填0
DB 0x55, 0xaa ; 启动区末尾标识,必须是0x55, 0xaa
; 启动区结束
; 以下是程序以外的输出部分
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 4600
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 1469432
8个16位寄存器
- AX——accumulator,累加寄存器
- CX——counter,计数寄存器
- DX——data,数据寄存器
- BX——base,基址寄存器
- SP——stack pointer,栈指针寄存器
- BP——base pointer,基址指针寄存器
- SI——source index,源变址寄存器
- DI——destination index,目的变址寄存器
8个8位急寄存器,实际上就是把AX、CX、DX、BX这4个16位寄存器拆成8个8位寄存器。
- AL——累加寄存器低位(accumulator low)
- CL——计数寄存器低位(counter low)
- DL——数据寄存器低位(data low)
- BL——基址寄存器低位(base low)
- AH——累加寄存器高位(accumulator high)
- CH——计数寄存器高位(counter high)
- DH——数据寄存器高位(data high)
- BH——基址寄存器高位(base high)
6个16位段寄存器
- ES——附加段寄存器(extra segment)
- CS——代码段寄存器(code segment)
- SS——栈段寄存器(stack segment)
- DS——数据段寄存器(data segment)
- FS——没有名称(segment part 2)
- GS——没有名称(segment part 3)
汇编命令:
- ORG 指名程序装载到内存中的哪个地址,此时$的含义也变了,变成了内存单元的开始地址
- JMP 跳转指令,JMP entry指的是跳转到entry标签执行程序
- ADD 加法指令ADD SI,1 即SI中的值+1
- CMP 比较指令 CMP AL,0 即AL中的值和0比较
- JE 条件跳转指令中之一,要和CMP连用,如果比较结果相等,则跳转到指定的地址
- INT 软件中断指令,后接调用的中断指令
- HLT 让CPU进入待机状态
- MOV 基本传送指令MOV AX,0相当于把0送入AX寄存器,MOV SS,AX把AX中的内容送到SS中
MOV指令简单介绍
- MOV AX,0 相当于把0送入AX寄存器
- MOV SS,AX 把AX中的值送到SS中
- MOV AL,[SI] 把SI中的值作为地址,把地址所指的内存单元中的值送到SS中(寄存器间接寻址)
汇编中源操作数和目的操作数位数必须相同,所以MOV中有了BYTE、WORD、DWORD等汇编保留字。BX为16位寄存器,AL为8位寄存器,故无法直接MOV BX, AL。
- MOV AL, BYTE [BX] 把BX中的值作为地址,送到AL中(寄存器间接寻址)
- MOV BYTE [678],123 把123送入内存678号单元中,BYTE表示8位
- MOV WORD [678],123 把123送入内存678号单元中,WORD表示16位,所以678和679号单元都需要用上
BIOS中断介绍
原书中的网址已经打不开了,这里给一个新网址
BIOS中断调用介绍相关的网站,根据其中介绍的中段值和寄存器值来进行BIOS调用
https://www.ctyme.com/intr/int.htm
以https://www.ctyme.com/intr/int.htm为例
打开网站,随便选择一个值
此处选择10,之后便进入到INT 0x10的中段说明界面,里面包含了需要设置的寄存器的值。以及所代表的功能。
选择AH=0Eh,可见以下代码为完整的BIOS中断调用,以ASCII码的形式输出AL中的值
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用BIOS中的显卡中断
完整的输出代码如下
代码中这部分为了在屏幕上打印"hello, world"
MOV SI,msg ; 把msg的地址赋值给SI
putloop:
MOV AL,[SI] ; 把SI中地址所指的内存单元中的值赋值给AL
ADD SI,1 ; SI的内容+1
CMP AL,0 ; AL和0比较
JE fin ; AL等于0时跳转fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用BIOS中的显卡中断
JMP putloop ; 无条件跳转putloop
fin:
HLT ; 让CPU待机,等待指令
JMP fin ; 循环
;到DB 0x55, 0xaa为止,以下代码是要在屏幕上显示的内容。
msg:
DB 0x0a, 0x0a ; 2个换行
DB "hello, world"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$ ; 当前到0x1fe地址的位置全部填0
DB 0x55, 0xaa ; 启动区末尾标识,必须是0x55, 0xaa
详细说明:此段代码把msg的地址赋值给SI——即DB 0x0a, 0x0a
中第一个0x0a
在内存中的地址。MOV AL,[SI]
采用寄存器间接寻址的方式,把存在内存中的0x0a
存入Al中,以BIOS调用的方式显示一个换行(0x0a在ASCII码中表示换行)。之后ADD SI,1
,SI中的地址指向了下一个字符——第二个0x0a
在内存中的存储单元。以此循环,每循环一次便显示一个字符,直到显示到DB 0x0a
为止。因为下一条指令为DB 0
,所以AL中的值也为0,会执行跳转指令JE。
内存分布图
硬盘中的程序需要装载到内存中才能运行,所以程序一开始指明了整个程序的装载地址ORG 0x7c00。这个地址不能随便指定,需要符合规范,否则计算机很可能会出错。
原书给的网址依旧打不开
新的内存分布图介绍网站
https://wiki.osdev.org/Memory_Map_(x86)
start | end | size | description | type | |
---|---|---|---|---|---|
Real mode address space (the first MiB) | |||||
0x00000000 | 0x000003FF | 1 KiB | Real Mode IVT (Interrupt Vector Table) | unusable in real mode | 640 KiB RAM (“Low memory”) |
0x00000400 | 0x000004FF | 256 bytes | BDA (BIOS data area) | ||
0x00000500 | 0x00007BFF | 29.75 KiB | Conventional memory | usable memory | |
0x00007C00 | 0x00007DFF | 512 bytes | Your OS BootSector | ||
0x00007E00 | 0x0007FFFF | 480.5 KiB | Conventional memory | ||
0x00080000 | 0x0009FFFF | 128 KiB | EBDA (Extended BIOS Data Area) | partially used by the EBDA | |
0x000A0000 | 0x000BFFFF | 128 KiB | Video display memory | hardware mapped | 384 KiB System / Reserved (“Upper Memory”) |
0x000C0000 | 0x000C7FFF | 32 KiB (typically) | Video BIOS | ROM and hardware mapped / Shadow RAM | |
0x000C8000 | 0x000EFFFF | 160 KiB (typically) | BIOS Expansions | ||
0x000F0000 | 0x000FFFFF | 64 KiB | Motherboard BIOS |
图中明确指出了0x00007C00——0x00007DFF为操作系统引导扇区。
制作启动区
其实就是把今天代码中DB 0x55, 0xaa ; 启动区末尾标识,必须是0x55, 0xaa之前的单独作为一个文件保存而已
把 \30天自制操作系统\projects\02_day\helloos4目录复制到 \30天自制操作系统\tolset中
源码保存为ipl.nas文件,这样启动区便制作完成。
; hello-os
; TAB=4
ORG 0x7c00 ; 指明程序装载地址,不能随便指定其他位置
; 以下代码为启动区
; 以下段落为标准FAT12格式软盘专用代码
JMP entry
DB 0x90 ; 此处没有上一节中的0xeb, 0x4e
DB "HELLOIPL" ; 启动区名称,可取任意字符串(8字节)
DW 512 ; 每个扇区的大小(必须为512字节)
DB 1 ; 簇大小,(必需为1个扇区)
DW 1 ; FAT的起始位置(一般是从第一个扇区开始)
DB 2 ; FAT的个数(必须为2)
DW 224 ; 根目录大小,(一般设置为224项)
DW 2880 ; 该硬盘大小(必须为2880扇区)
DB 0xf0 ; 硬盘种类(必须是0xf0)
DW 9 ; FAT长度(必须是9个扇区)
DW 18 ; 1个磁道有几个扇区(必须是18个)
DW 2 ; 磁头数(必须是2个)
DD 0 ; 不使用分区,此处为0
DD 2880 ; 重写一次,硬盘大小
DB 0,0,0x29 ; 不知道是干什么的,但必要写
DD 0xffffffff ; 可能是卷标号码
DB "HELLO-OS " ; 硬盘名称(11字节)
DB "FAT12 " ; 硬盘文件系统名称(8字节)
RESB 18 ; 空出18字节
; 程序主体
entry:
MOV AX,0 ; 初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX
MOV SI,msg ; 把msg的地址赋值给SI
putloop:
MOV AL,[SI] ; 把SI中地址所指的内存单元中的值赋值给AL
ADD SI,1 ; SI的内容+1
CMP AL,0 ; AL和0比较
JE fin ; AL等于0时跳转fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符颜色
INT 0x10 ; 调用BIOS中的显卡中断
JMP putloop ; 无条件跳转putloop
fin:
HLT ; 让CPU待机,等待指令
JMP fin ; 循环
msg:
DB 0x0a, 0x0a ; 2个换行
DB "hello, world"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$ ; 当前到0x1fe地址的位置全部填0
DB 0x55, 0xaa ; 启动区末尾标识,必须是0x55, 0xaa
; 启动区结束
Makefile入门
Makefile算是linux中的某种基本操作了,没什么难的
自定义的命令: 文件1 文件2。。。
<tab> 标准命令
之后保存名为”Makefile“文件,无后缀名
新建记事本,写入以下代码
#请把文件保存在\30天自制操作系统\tolset\helloos4
#自定义命令ipl.bin
#执行后会自动查找ipl.nas、Makefile文件是否存在
#存在的话执行../z_tools/nask.exe ipl.nas ipl.bin ipl.lst命令
ipl.bin : ipl.nas Makefile
../z_tools/nask.exe ipl.nas ipl.bin ipl.lst
helloos.img : ipl.bin Makefile
../z_tools/edimg.exe imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img
#保存为Makefile
同时把\30天自制操作系统\tolset\z_new_w中的make.bat复制到\30天自制操作系统\tolset\helloos4中
cmd命令行窗口进入\30天自制操作系统\tolset\helloos4,执行命令make -r ipl.bin
之后ipl.nas便被编译成ipl.bin这一启动区文件。
\30天自制操作系统\projects\02_day\helloos5中仅仅对Makefile文件进行了优化,并添加了其他功能,有兴趣的可自行用文本编辑器查看。
之后的内容想要模拟运行需要cmd进入工作目录(\30天自制操作系统\tolset\xxxxxx),用make命令把代码编译成img文件,再执行make run命令即可。