从6月到10月间断断续续学的汇编,虽然不怎么深入,但已经足以满足我打单机游戏时做简单修改的目的了,最近一次实践是昨天取消了黑魂的死亡惩罚系统。
笔记贴在这里主要是为了方便自已以后查阅。
第一章 基础知识
在汇编源代码中,数据不可以字母开头,若须使用则添加前缀0
第二章 寄存器
1.通用存储器
AX(accumulator),BX(base),CX(count),DX(data),每个都是16位
每个通用存储器都可以拆成高低两个8位寄存器使用,如AX可以拆成AH(High),AL(Low)
指令的两个操作对象应是位数一致的。
2.地址寻址
因为8086CPU每次只能处理16位数据,为了支持20位的寻址,采用了段地址*16+偏移地址=物理地址的方法
3.段寄存器
CS:code segment,代码段寄存器
IP:instruction pointer,命令指针寄存器(通用寄存器)
读取执行指令的步骤:
(1)将CS,IP送入地址加法器求出地址值
(2)地址值->IO控制电路->20位地址总线->内存->取出目标指令->数据总线->IO控制电路->指令缓冲器
(3)IP中的值自增,大小为本次指令的长度
执行控制器执行指令
修改CS,IP的指令:各种转移指令
如:JMP 段地址:位移地址 或 JMP 位移地址
第三章 寄存器(内存访问)
1.DS和[address]
DS(data segment,数据段存储器),用于存放读写时的目标内存单元的段地址
address可以为指定值或BX
实际地址=DS * 16+[address]
如:mov al,[0]
8086CPU因硬件架构不支持直接把数据写入DS,需要先将数据写入通用寄存器再把通用寄存器写入DS
(这里联想到了数组下标从0开始)
2.CPU的栈机制
操作指令:
PUSH入栈,先自减,再赋值
POP出栈,先取值,再自增
SS:Stack Segment,堆栈段寄存器
SP:Stack Pointer,堆栈指针(通用寄存器)
实际地址=SS*16+SP
出栈入栈操作会使得SP自增自减,但不会引起SS变更
栈的地址排布与数组相反,由高到低排布。但每个入栈的数据低字节放低地址,高字节放高地址
栈本身不存在空间限制,需要人工控制
第五章 [bx]和loop
1.[bx]就是可以拿寄存器变量作为偏移地址
2.loop
格式: LOOP 标记
先让CX自减1,再判断CX是否为0,若为0,跳转至标记处
3.段前缀:可以用"段寄存器:[address]"的格式来显式地声明一个内存单元的段地址
如: mov ax,cs:[0]
第六章 包含多个段的程序
assume 段寄存器:代码段名称
尝试将代码段的首行代码的段地址存入指定寄存器中
dw 变量名
dw即define word,定义并开辟一个字大小的内存
end 标签名
将程序的入口指定为特定标签
第七章 更灵活的定位内存地址方法
1.DB指令(define byte): DB ‘XXXX’,表示定义一串字节
2.大小写转换
AND 11011111B得大写
OR 00100000B得小写
3.[address]的扩展
DI(destination index),SI(source index)是两个变址存储器,是通用存储器的一种,可用来表示地址
BP(base pointer),基地址指针寄存器,也是通用寄存器的一种
寻址操作可扩展为[BX/BP+DI/SI+指定值]
4.多重循环可用栈来存储循环变量
第八章 寻址进阶与除法
1.idata表示一个指令缓冲器中的具体数值
2.word/type ptr [address]用来指定内存单元的长度
3.div 除数
当除数为8位,被除数放在AX中,商放在AL中,余数放在AH中
当除数为16位,被除数低字放在AX中,高字放在DX中,上放在AX中,余数放在DX中
4.DD(define dword),和DB,DW用法相同
5.DUP,配合DB,DW,DD使用,用于一次申请多块儿内存
格式:DB,DW,DD 重复次数DUP (重复的数据类型)
如: DB 3 DUP (0,1,2)
第九章 转移指令的原理
1.offset 标号 获取标号对应的偏移地址当成一个idata
2.jmp short 标号 段内短转移,修改范围八位之内
jmp near ptr 标号 段内近转移,修改范围十六位之内
jmp far ptr 标号 段间转移,修改范围十六位
jmp 十六位寄存器 段内转移,修改范围八位
jmp word ptr 内存单元地址 段内转移,范围十六位
jmp dword ptr 内存单元地址 段间转移,范围十六位
jcxz 编号 段内近转移,修改范围八位,只在cx==0时执行
loop 讲过了
第十章 CALL和RET
ret(return):
pop IP
retf(return far):
pop IP
pop CS
call 标号:
push IP
jump near ptr 标号
call far ptr 标号:
push CS
push IP
jmp far ptr 标号
call 十六位寄存器:
push IP
jmp 十六位寄存器
call word ptr 内存单元地址:
push IP
jmp word ptr 内存单元地址
call dword ptr 内存单元地址:
push CS
push IP
jmp dword ptr 内存单元地址
mul [adress]/reg:
若两乘数为八位,一个放在AL中,另一个在[adress]/reg中,结果存放在AX中
若两乘数为十六位,一个放在AX中,另一个放在[adress]/reg中,结果高位放在DX中,地位放在AX中
子程序调用的寄存器冲突:把需要用的寄存器值先压入栈中,再调用子程序,回掉后把寄存器值从栈中取出
参数的传递和返回:二者原理相同,都是把参数放进寄存器里
利用栈传递参数:先把参数压入栈中,再调用子程序,子程序通过计算参数偏移来获取参数值
批量数据的传递:把批量数据放在内存里,把内存首地址作为参数使用
第十一章 标志寄存器
1.标志寄存器(FR,flag register)
ZF: zero,是否为0
PF: parity,1的个数是否为奇数
SF: sign,是否为负号
CF: carry,无符号运算是否进位
OF: overflow,有符号运算是否溢出
2.adc指令:adc ax,bx->(ax)=(ax)+(bx)+CF
sub指令:sbb ax,bx->(ax)=(ax)-(bx)-CF
3.cmp指令:cmp ax,bx->ax-bx,不保存结果,只影响FR,通过各个标志位来判断比较结果
即对于无符号数,=时,ZF=1;<时,CF=1;>时,ZF=CF=0
对于有符号数,=时,有ZF=1;SF = OF时有AX>=BX,反之则AX
JE ZF=1
JNE ZF=0
JB CF=1(below)
JNB CF=0
JA CF=0&ZF=0(above)
JNA CF=1|ZF=1
常配合CMP使用,不必拘泥于实现原理
5.DF方向位标志(Direction)
MOVSB指令(Move String Byte)
(1)ES:[DI]=DS:[SI]
(2)若DF = 0,SI++,DI++
若DF = 1,SI–,DI–
同理有MOVSW(word),MOVSD(dword)
为了操纵DF有CLD(clean direction)和STD(set direction)命令
6.pushf和popf(f即FLAG)
将标志寄存器值入栈出栈
第十二章 内中断
1.产生内中断
除法错误:0
单步执行:1
into指令:4
int n指令:n
2.中断向量表
对9096CPU而言,为0000:0000~0000:03FF的1024个内存单元
每个表项占两个字,低地址放偏移地址,高地址放段地址
3.中断过程
(1)取得中断码 N
(2)PUSHF
(3)TF=0,IF=0
(4)PUSH CS,PUSH IP
(5)(IP)=(N4),(CS)=(N4+2)
4.中断处理程序
(1)保存寄存器值
(2)处理中断
(3)恢复寄存器
(4)iret命令返回
iret等价于:
POP IP
POP CS
POPF
5.REP
REP MOVSB 终止条件:CX=0
6.中断处理程序的安装
开头结尾各设置一个标号
offset endflag-offset startflag得出代码长度
movsb进行复制
7.单步中断
当TF=1时,CPU每处理完一条命令就自动触发一次单步中断
第十三章 INT指令
主要讲了BIOS和DOS里的中断例程
第十四章 端口
1.in:从端口读入
out:向端口输出
2.逻辑位移指令
shl X(shift left)左移一位,空位补零,溢出位存在CF中
shr X 右移,具体同上
X只能为1或CL
3.CMOS RAM中的时间信息
70H为地址端口,71H为数据端口
0秒2分4时7日8月9年
每个内存单元占一字节,分成两个BCD码表示的十位数