本篇内容:
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磁头 第一个扇区)。 !在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: 附图:window 10和RedHat Enterprise Linux 5运行bochs成功的图片 windows linux: 我在bochs配置遇到不少问题(Linux下),可能会找时间分享。参考与引用:
《Linux内核完全剖析-基于0.12内核》--- 赵炯
博客:
"as86汇编语言" --- AstrayLinux
"CPU如何执行指令(CS/IP)" --- zhliao
