本篇内容:
- 8086处理器执行指令原理
- 本程序相关as86汇编基础
- bootsect.S分析
===============================================
1 8086处理器执行指令过程
1.0 相关概念:
>内存单元的物理地址:
所有内存单元构成的存储空间是一个一维的线性空间,每一个单元都有一个唯一的地址即单元的物理地址。
>CPU(8086)内部形成物理地址的方式:
公式:物理地址=段地址 X 16 + 偏移地址
原理: CPU相关部件提供两个16位的地址:段地址和偏移地址,地址加法器将
两个16位地址合成一个20位的物理地址;物理地址将被送上地址总线到达存储器;
地址加法器工作流程如图:
>CS:IP
CS: 代码段寄存器:存放段地址
IP: 指令指针寄存器:存放偏移地址
设任意时刻,CS中的内容为M,IP中的地址为N,8086CPU将从内存M*16+N单元开始,
读取一条指令并执行。
1.1 过程:
<1> CPU从CS:IP指向的内存单元读取指令,读取的指令被送入指令缓冲器;
<2> IP=IP+所读指令的长度,从而指向下一条指令;
<3>执行指令,转到步骤<1>重复这个过程。
2 本程序相关的as86汇编基础
2.0 as86概念
as86是8086..80386处理器下的汇编程序,它所采用的语法与Intel/MS采取的语法类似,而不同于广泛运用于UNIX下的汇编语法(译注,gas中的语法,AT&T汇编)
2.1 本程序部分指令/语法/基础器:
格式 ".xxx" 是伪操作符,具体作用与xxx有关
例如:.txt 指定正文段开始位置
.data 指定数据段开始位置
.bss 指定未初始化数据段开始位置
.org 定义当前汇编的位置,执行结果:将把当前的位置计数器值调为该伪操作符语句上给出的值
格式 "AAA" 标识符,功能之一:可作常量
例如: AAA = 123
格式 "yyy:" 标号, 标号是"标识符+冒号"
例如:.globl 作用为定义随后的标号是外部的或是全局的,并且即使不适用也强制引入(在引入时,标号无需带":")。
.globl yyy begtext zzz
其它时候需要带冒号":"
例如:.txt
begtext:
数据格式:
.ascii 写到输出的ASCII字符串(Ascii string copied to output.)
.asciz 尾部带有一字节的nul(Ascii string copied to output with trailing nul byte.)
.byte/ FCB/ DATA1/ DB 一字节的对象列表
.word/ FDB/ DATA2/ DW 两字节的对象列表
.LONG/ DATA4/ DD 四字节的对象列表
部分指令:
jmpi 段间跳转
例如 jmpi go, BOOTSEG !即CS = BOOTSEG, IP = go (标号go是偏移地址)
int 中断调用
int 0x10 !使用BIOS功能19子功能1,作用显示一字符串到屏幕指定位置
寄存器:
8086寄存器列表如下:
ax bx cx dx 16位寄存器: 通用寄存器/数据寄存器
ah ax的高8位,al ax的低8位 ah&al = ax
bp (base pointer)基址指针寄存器
sp (stack pointer) 堆栈指针寄存器
cs (Code Segment)段寄存器
ds (Data Segment)数据段寄存器
es (Extra Segment)附加段寄存器
ss (Stack Segment)堆栈段寄存器
显示一行字符串可能用到的寄存器:
cx,dx,bx,ax :用于指定字符串的字符串长度/显示位置/字符属性/光标位置
bp :指定要显示的字符串
3 bootsect.S分析
!boot.s/bootsect.s 是磁盘引导程序,驻留在第一个扇区(引导扇区,0磁道 , 0磁头 第一个扇区)。
1.0 相关概念:
>内存单元的物理地址:
所有内存单元构成的存储空间是一个一维的线性空间,每一个单元都有一个唯一的地址即单元的物理地址。
>CPU(8086)内部形成物理地址的方式:
公式:物理地址=段地址 X 16 + 偏移地址
原理: CPU相关部件提供两个16位的地址:段地址和偏移地址,地址加法器将
两个16位地址合成一个20位的物理地址;物理地址将被送上地址总线到达存储器;
地址加法器工作流程如图:
>CS:IP
CS: 代码段寄存器:存放段地址
IP: 指令指针寄存器:存放偏移地址
设任意时刻,CS中的内容为M,IP中的地址为N,8086CPU将从内存M*16+N单元开始,
读取一条指令并执行。
1.1 过程:
<1> CPU从CS:IP指向的内存单元读取指令,读取的指令被送入指令缓冲器;
<2> IP=IP+所读指令的长度,从而指向下一条指令;
<3>执行指令,转到步骤<1>重复这个过程。
2 本程序相关的as86汇编基础
2.0 as86概念
as86是8086..80386处理器下的汇编程序,它所采用的语法与Intel/MS采取的语法类似,而不同于广泛运用于UNIX下的汇编语法(译注,gas中的语法,AT&T汇编)
2.1 本程序部分指令/语法/基础器:
格式 ".xxx" 是伪操作符,具体作用与xxx有关
例如:.txt 指定正文段开始位置
.data 指定数据段开始位置
.bss 指定未初始化数据段开始位置
.org 定义当前汇编的位置,执行结果:将把当前的位置计数器值调为该伪操作符语句上给出的值
格式 "AAA" 标识符,功能之一:可作常量
例如: AAA = 123
格式 "yyy:" 标号, 标号是"标识符+冒号"
例如:.globl 作用为定义随后的标号是外部的或是全局的,并且即使不适用也强制引入(在引入时,标号无需带":")。
.globl yyy begtext zzz
其它时候需要带冒号":"
例如:.txt
begtext:
数据格式:
.ascii 写到输出的ASCII字符串(Ascii string copied to output.)
.asciz 尾部带有一字节的nul(Ascii string copied to output with trailing nul byte.)
.byte/ FCB/ DATA1/ DB 一字节的对象列表
.word/ FDB/ DATA2/ DW 两字节的对象列表
.LONG/ DATA4/ DD 四字节的对象列表
部分指令:
jmpi 段间跳转
例如 jmpi go, BOOTSEG !即CS = BOOTSEG, IP = go (标号go是偏移地址)
int 中断调用
int 0x10 !使用BIOS功能19子功能1,作用显示一字符串到屏幕指定位置
寄存器:
8086寄存器列表如下:
ax bx cx dx 16位寄存器: 通用寄存器/数据寄存器
ah ax的高8位,al ax的低8位 ah&al = ax
bp (base pointer)基址指针寄存器
sp (stack pointer) 堆栈指针寄存器
cs (Code Segment)段寄存器
ds (Data Segment)数据段寄存器
es (Extra Segment)附加段寄存器
ss (Stack Segment)堆栈段寄存器
显示一行字符串可能用到的寄存器:
cx,dx,bx,ax :用于指定字符串的字符串长度/显示位置/字符属性/光标位置
bp :指定要显示的字符串
3 bootsect.S分析
!boot.s/bootsect.s 是磁盘引导程序,驻留在第一个扇区(引导扇区,0磁道 , 0磁头 第一个扇区)。
!在PC机加电ROM BIOS 加电自检后,ROM BIOS 会将boot.s 或者bootsect.s 加载到0x7c00开始处,并且开始运行。
!感叹号用于注释
!此代码段功能:用0x07替换字符串msg的第18个字符,然后在屏幕指定位置显示。
!为什么把操作系统的引导程序加载到0x7c00处:
!BIOS就是将MBR读入0x7C00地址,然后进行后续的引导的。
!操作系统或是bootloader开发者必须假设 他们的汇编代码被加载并从0x7C00处开始执行
.globl begtext,begdata,begdata,begbss,endtext,enddata,endbss
.text !正文段
begtext: !在正文段定义一个标号begtext
.data !数据段
begdata: !在数据段定义一个标号begdata
.bss !未初始化数据段
begbss: !在未初始化数据段定义一个标号begbss
.text !正文段
BOOTSEG = 0x7c0 !BIOS加载原始段地址,根据以上公式推算,
!要跳到物理地址0x7c00, CS 须是:0x7c0 (理由:0x07c00 + 0x0000 = 0x07c00)
entry start !告知链接程序,程序从start标号处开始执行。 boot/bootsect.S与boot/setup.s可以省略
!因为我们并不希望在生成的纯二进制执行文件包含任何符号信息
start:
jmpi go,BOOTSEG !跳转到CS=BOOTSEG,IP=go处执行,其实就是go处
go: mov ax,cs !初始化ds,es ,设为cs便于对程序中的数据进行寻址
mov ds,ax
mov es,ax
mov [msg1+17],ah !替换字符
mov cx, #20 !显示字符总数
mov dx, #0x1004 !显示位置:第17行第5列
mov bx, #0x000c !显示字符属性:红色
mov bp, #msg !指向要显示的字符串(中断调用要求,个人猜测:0x10中断会判断bp是否为空,bp实际就是字符串首地址)
mov ax, #0x1301 !跳转光标到某处
int 0x10 !BIOS调用字符串显示中断
loop0: jmp loop0 !死循环,不退出程序
msg1: .ascii "Loading system ..." !18个字符
.byte 13,10 !再补充两个字符:一个回车一个换行(ASCII)
.ascii "Marry Chritmas"
.byte 13,10
.org 510 !定义当前的位置计数器为510
.word 0xAA55 !再写两个字节,正好512 bytes(一个扇区的大小)
.text !表示代码段的结束位置
endtext:
.data !表示数据段的结束位置
enddata:
.bss !表示未初始化数据段的结束位置
endbss:
!感叹号用于注释
!此代码段功能:用0x07替换字符串msg的第18个字符,然后在屏幕指定位置显示。
!为什么把操作系统的引导程序加载到0x7c00处:
!BIOS就是将MBR读入0x7C00地址,然后进行后续的引导的。
!操作系统或是bootloader开发者必须假设 他们的汇编代码被加载并从0x7C00处开始执行
.globl begtext,begdata,begdata,begbss,endtext,enddata,endbss
.text !正文段
begtext: !在正文段定义一个标号begtext
.data !数据段
begdata: !在数据段定义一个标号begdata
.bss !未初始化数据段
begbss: !在未初始化数据段定义一个标号begbss
.text !正文段
BOOTSEG = 0x7c0 !BIOS加载原始段地址,根据以上公式推算,
!要跳到物理地址0x7c00, CS 须是:0x7c0 (理由:0x07c00 + 0x0000 = 0x07c00)
entry start !告知链接程序,程序从start标号处开始执行。 boot/bootsect.S与boot/setup.s可以省略
!因为我们并不希望在生成的纯二进制执行文件包含任何符号信息
start:
jmpi go,BOOTSEG !跳转到CS=BOOTSEG,IP=go处执行,其实就是go处
go: mov ax,cs !初始化ds,es ,设为cs便于对程序中的数据进行寻址
mov ds,ax
mov es,ax
mov [msg1+17],ah !替换字符
mov cx, #20 !显示字符总数
mov dx, #0x1004 !显示位置:第17行第5列
mov bx, #0x000c !显示字符属性:红色
mov bp, #msg !指向要显示的字符串(中断调用要求,个人猜测:0x10中断会判断bp是否为空,bp实际就是字符串首地址)
mov ax, #0x1301 !跳转光标到某处
int 0x10 !BIOS调用字符串显示中断
loop0: jmp loop0 !死循环,不退出程序
msg1: .ascii "Loading system ..." !18个字符
.byte 13,10 !再补充两个字符:一个回车一个换行(ASCII)
.ascii "Marry Chritmas"
.byte 13,10
.org 510 !定义当前的位置计数器为510
.word 0xAA55 !再写两个字节,正好512 bytes(一个扇区的大小)
.text !表示代码段的结束位置
endtext:
.data !表示数据段的结束位置
enddata:
.bss !表示未初始化数据段的结束位置
endbss:
附图:window 10和RedHat Enterprise Linux 5运行bochs成功的图片
windows
linux:
我在bochs配置遇到不少问题(Linux下),可能会找时间分享。
参考与引用:
《Linux内核完全剖析-基于0.12内核》--- 赵炯
CSDN博客:
"as86汇编语言" --- AstrayLinux
"CPU如何执行指令(CS/IP)" --- zhliao