x86从实模式到保护模式
可能大多数人入门汇编语言都是以8086为起点的(相对较为简单),本文也是以此为起点来讲解有关在8086实模式相关知识点(下一节会更新实模式下的更多内容以及PE模式的相关内容)
相关资料:
认识8086处理器
8086的通用寄存器
# 均为16位寄存器
AX: [AH-AL] SI
BX: [BH-BL] DI
CX: [CH-CL] SP
DX: [DH-DL] BP
8086的内存访问和字节序
8086的数据线为16根,与寄存器一致,因此一个寄存器可以完全接收从内存来的数据
字节序分类
低端字节序:寄存器的低字节占据低地址,高字节占据高地址
高端字节序:寄存器的低字节占据高地址,高字节占据低地址
8086内存访问
8086有20根地址线,因此寻找内存地址的时候需要20个b,最大内存地址为[FFFFF]
DS: 数据段寄存器 16b
CS: 代码段寄存器 16b
IP: 段偏移寄存器 16b
而当不看最后四位比特时,则可以看作是一个16位的地址,此时就可以用DS或CS进行访存,则此时有一个IP寄存器表示段内偏移量,相加则会产生一个20位地址。
如何执行编译好的程序
跳转指令
jmp 0xfe05:0x000b
硬盘的构造和工作原理
硬盘分类
- USB:软盘
- HDD:机电式硬盘
- SSD:集成电路硬盘
一个扇区是512字节
两个模式
- CHS(cylinder-header-sector):磁头号-柱面号-扇区号
- LBA(logical-block-address):使用逻辑扇区号,由CHS转换而来
主引导扇区
一般情况下,在BIOS中有一段代码,他将硬盘的主引导扇区中的程序读到内存地址[07c00]处,然后用一个跳转指令跳到那里继续执行
db 0 ;位指令,用来向程序中添加一个字节的数据
times 502 db 0;重复位指令db 502次
;主引导扇区程序要求最后两个字节是55和AA
db 0x55
db 0xAA
在屏幕上显示文本
显卡和显存
色彩混合的原理:
RGB每个颜色用8个b表示,即2^8=256,三种颜色融合形成色彩混合
显存位置:[B8000~BFFFF]
将显存内的数据通过字符解析器传送到屏幕上
这段显存可以看作是一个段
mov指令
;mov 段寄存器, 通用寄存器
;mov 段寄存器, [内存地址]
;mov ax, 0xb800
;mov ds, ax 此后所有的[xxxx]都是以该ds为段地址的偏移地址
;mov byte [0x00], 0x41 表示从偏移0x00位置mov一个字节的数据
不允许将一个操作数直接传给段寄存器
字符的编码和显示属性
ASCII编码
字符在内存中的表示:字符编码-字符属性
字符属性:背景色 + 前景色(此处只有黑白,因此一个b表示黑或白)
在汇编程序中使用标号
标号只是一个对汇编地址的别名
start: ;该行有地址则用该地址,无地址则用其以后第一条指令地址
...
段间直接绝对跳转指令
在不同的段进行跳转执行
;jmp 段地址:偏移地址
jmp 0x0000:0x7c00
寄存器的绝对间接近跳转
段内跳转
mov bx, 0x7c00 + again
again:
jmp bx ;使用寄存器跳转,段地址保持不变,bx是偏移地址
使用相对偏移量的短跳转和近跳转
jmp short start ;EB操作码-1B 8位的相对偏移量
jmp near start ;当跳转范围<-127 || >128 使用三个字节 E9 16位的相对偏移量
在屏幕上显示数字
显示数字的基本原理
第一步:将数字125分解为三个数字1、2和5(此时要进行数字拆分,即%10得余数)
125 -> 0111 1101
1 -> 0000 0001
2 -> 0000 0010
5 -> 0000 0101
第二步:将数字1、2和5分别转换成对应的数字字符
根据ASCII编码
无符号数除法指令div
- 如果在指令中指定的是8位寄存器或者8位操作数的内存地址,则意味着被除数在寄存器AX里。相除后,商在寄存器AL里,余数在寄存器AH里。
- 如果在指令中指定的是16位寄存器或者16位操作数的内存地址,则意味着被除数是32位的,低16位在寄存器AX里;高16位在寄存器DX里。
相除后,商在寄存器AX里,余数在寄存器DX里。 - 如果在指令中指定的是32位寄存器或者32位操作数的内存地址,则意味着被除数是64位的,低32位在寄存器EAX里;高32位在寄存器EDX里。
相除后,商在寄存器EAX里,余数在寄存器EDX里。(80386支持) - 如果在指令中指定的是64位寄存器或者64位操作数的内存地址,则意味着被除数是128位的,低64位在寄存器RAX里;高64位在寄存器RDX里。
相除后,商在寄存器RAX里,余数在寄存器RDX里。(64位处理器支持)
;div 除数所在的寄存器或者内存地址
div hb;bh是一个8位寄存器
div byte [0x2002]; 除数是一个8位内存单元(1B)
异或指令xor
xor bh, bh;相同为0,不同为1
加法指令add
add ax, 3;此处ax是16位寄存器,所以3被当作16进制下的3进行运算
使用标号访问内存数据
mov [0x7c00], dl;将dl数据传送给偏移地址在0x7c00地方
mov [0x7c00 + 1], dl ;将dl数据传送给偏移地址在0x7c00 + 1B地方
段超越前缀的使用
mov byte [0x01], 0x2f;表示移动1B数据
循环、批量传送和条件转移
跳过非指令的数据区
把代码和数据分开
mytext db 'L', 0x07, 'a', 0x07 \;\表示换行
'a', 0x07, 'L', 0x07
把数据放在段的前面,在读内存的时候会直接读mytext,但是这是数据,是不可执行的,因此
jmp start mytext ... start: ...
逻辑段的地址的重新设定
jmp start
mytext ...
start:
mov ax, 0x7c00 ;设置数据段基地址
mov ds, ax
mov ax, 0xb800 ;设置附加段基地址
mov es, ax
串传送指令和标志寄存器
;按字节进行传送
movsb
;按字进行传送
movsw
DS:SI:原始数据串的段地址:偏移地址
ES:DI:目标位置的段地址:偏移地址
cld;方向标志清0指令,std将DF置为1
mov si, mytext
mov di, 0
mov cx, (start - mytext) / 2
rep movsw;
标志寄存器FLAGES的方向标志位DF表示是正向传送还是反向传送
NASM的 和 和 和$
$;表示当前行的汇编地址
$$;表示当前程序段的汇编地址
循环指令loop
digit:
loop digit
基址寻址和INC指令
在8086处理器上,如果要用寄存器来提供偏移地址,只能使用BX、SI、DI、BP,不能使用其它寄存器。
寄存器Bx在设计之初的作用之一就是用来提供数据访问的基地址,所以又叫基址寄存器(Base Address Register) 。
mov bx, ax
mov [bx], dl; 正确
mov [ax], cl;非法
inc指令
inc bx;自增1B
dec指令
dec bx;自减1
基址变址寻址和条件转移指令
mov si, 4
mov al, [bx + si];bx:基址,si:变址
在8086处理器上,只允许以下几种基址变址的组合:
bx + si
bx + di
bp + si
bp + di
jns:FLAGS的符号位SF是否为0,为0则跳转
jns
8086的标志寄存器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c7WFIrHJ-1635446292575)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20211029014410948.png)]
- CF:进位标志。当一个算术操作在结果的最高位产生进位或者借位时,此标志是1;否则是0。
- PF:奇偶标志:当一个算术操作的结果在低8位中有偶数个“1”,此标志是1﹔否则为0
- AF:调整标志。当一个算术操作在结果的位3产生进位或者借位时,此标志是1﹔否则是0。此标志用于二进制编码的十进制数算法里。
- ZF:零标志。当运算的结果为O时,此标志为1;否则为0。
- SF:符号标志。用运算结果的最高位来设置此标志(一般来说,这一位是有符号数的符号位。0表示正数,1表示负数)。
- OF:溢出标志。对任何一个算术操作,假定它进行的是有符号运算。那么,当结果超出目标位置所能容纳的最大正数或者最小负数时,此标志为1,表示有符号整数运算的结果已经溢出﹔否则为0。
现有指令对标志位的影响
指令 | 标志位变化 |
---|---|
cbw/cwde/cdqe/cwd/cdq/cqo | 不影响任何标志位。 |
cld | DF=0,对CF、OF、ZF、SF、AF和PF的影响未定义。 |
std | DF=1,不影响其他标志位。 |
inc/dec | CF标志不受影响;对OF、SF、ZF、AF和PF的影响依计算结果而定。 |
add / sub | OF、SF、ZF、AF、CF和PF的状态依计算结果而定。 |
div / idiv | 对CF、OF、SF、ZF、AF和PF的影响未定义。 |
mov/ movs | 这类指令不影响任何标志位。 |
neg | 如果操作数为0,则CF=O,否则CF=1;对OF、SF、ZF、AF和PF的影响依 |
计算结果而定。 | |
xor | OF=O,CF=O;对SF、ZF和PF依计算结果而定;对AF的影响未定义。 |
条件转移指令和CMP
CMP:比较两个操作数的大小。影响到CF、OF、SF、ZF、 AF和PF标志位
cmp ax, bx
条件转移指令:略(太多了)
栈的原理和应用
字符串的定义和累加过程
jmp start
message db '1+2+3+...+100=';等同于db '1','+','2','+','3','.','.','.','1','0','0','='
start:
mov ax, 0x7c00;设置数据段的段基地址
mov ds, ax
mov ax, 0xb800
mov es, ax
;以下显示字符串
mov si, message
mov di, 0
mov cx, start-message
showmsg:
mov al, [si]
mov [es:di], al
inc di
mov byte [es:di], 0x07
inc di
inc si
loop showmsg
;以下计算1到100的和
xor ax, ax ;ax用于存放累加结果
mov cx, 1;cx用于充当加数
summate:
add ax, cx
inc cx
cmp cx, 100;cx是否等于100
jle summate;小于等于100时跳转,进行累加
jmp $
times 510-($-$$) db 0
db 0x55, 0xaa
栈的原理和使用
在内存中划分一块区域给栈,为栈段,由SS寄存器指向其段基址,SP寄存器指向栈顶
;入栈
pop dx
push word [0x2002]
;出栈
pop dx
pop word [0x80]
push 的执行过程:
- SP<-SP-操作数的大小(字节数);
- 段寄存器SS左移4位,加上SP里的偏移地址,生成物理地址;
- 将操作数写入上述地址处。
pop 的执行过程:
- 段寄存器SS左移4位,加上SP里的偏移地址,生成物理地址;
- 从上述地址处取得数据,存人由操作数提供的目标位置处;
- SP<- SP +2
栈在数位分解和显示中的应用
summate:
add ax, cx
inc cx
cmp cx, 100
jle summate
;一下分解累加和的每个数位
xor cx, cx;设置栈 段的段基地址
mov ss, cx;和代码段共用一个段地址
mov sp, cx
mov bx, 10;用作除数
xor cx, cx
decompo:
inc cx
xor dx, dx
div bx;dx(高16位) ax(低16位)除以bx,商放在AX,余数在DX
add dl, 0x30;得到字符编码
push dx
cmp ax, 0;查看商是否等于0
jne decompo;不等于0则继续跳转
;以下显示各个数位
shownum:
pop dx
mov [es:di], dl
inc di
mov byte [es:di], 0x07;0x07为字符属性
inc di
loop shownum
jmp $
times 510-($-$$) db 0
db 0x55, 0xaa
在8086中push默认为一个字
栈的推进方向是从高地址到低地址的,据此可以理解push和pop
逻辑或和逻辑与
;or:有一个为1,则输出1,否则为0
;and:两个一样,则输出1,否则为0
8086寻址的方式
寄存器、立即数和立即寻址
;目的操作数是寄存器,因此是寄存器寻址
mov ax, cx
add bx, 0xf000
inc dx
;如果源操作数的内存地址指定的,则是立即数寻址
add bx, 0xf000
mov dx, mydata
;直接内存寻址
mov ax, [0x5c0f]
add word [0x0130], 0x5000
add word [es:di], 0x5000
基址寻址
mov bx, buffer
lp:
inc word [bx];bx是基址寄存器
变址寻址
mov si, buffer
lp:
inc word [si+0x100];si是变址寄存器
基址变址寻址
mov bx, buffer
mov si, 0
lp:
inc word [bx+si];bx是基址寄存器