|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
最近学习了小甲鱼老师的汇编教程,感觉受益良多,在此把自己的笔记发一发,希望大佬们给点意见,新手们能一起交流。
汇编笔记1(基础知识)
第一章 基础:
寄存器:cup里的存储器,存放指令和数据。
存储器:存放指令和数据。硬盘和内存都是存储器。
存储单元(从0开始)
总线:
地址总线:寻址能力,(64位与32位区别)n根地址(一根1个字节=1个内存单元)
控制总线:cpu对外界器件的控制
数据总线:决定cpu和外界数据传送速度
主板:核心器件
接口:cpu不直接控制外部器件,通过接口卡控制
存储器:
随机存储器(ram):
只读储存器(rom):每种硬件有自己的rom
另:1.不同cpu有不同指令集
2.不同总线来区别二进制数据是什么
3.cpu看到的是逻辑存储器
检测点暴露的问题:
1.B:字节=8b:位
2.一根地址总线对应1B的寻址能力,一根数据总线对应1bit的数据
第二章 寄存器:(8086cpu)
cpu:内部总线连接
运算器:一次最多处理16位
控制器:
寄存器: 最大宽度16位(数据最大值2的16次方-1)
通用寄存器:4个寄存器(AX,BX,CX,DX)可以分为两个独立的8位寄存器。
高位:H,低位:L
字(word)=2个字节(16位兼容两个8位)
例:mov ax,bx 把bx移进ax(支持寄存器,段寄存器,数据,内存到寄存器,寄存器到段寄存器,寄存器到内存)(数据到寄存器sp等)
add ax,bx 把bx加进ax
物理地址:内存单元的唯一地址
8086内部16位与外部20位冲突解决方法:内部用两个16位合成一个20位(段地址+偏移地址)物理地址=段地址*16(二进制左移4位,16进制左移1位)+偏移地址
段:段的划分来自cpu,内存并没有分段,看需要自己看成不同段
段寄存器(不要忘记*16):
CS(代码段地址):CS*16+IP(代码偏移地址) ip=ip+读取的指令长度
修改CS:ip方法:jmp 段地址:偏移地址 jmp ax是令ip=ax
cs+ip决定执行与否,指向则执行,不指向则不执行。
DS(数据段地址):当前操作的数据可以[偏移地址]的方式引用
SS(栈寄存器):SS:sp指向栈顶元素(压入sp-,推出sp+)(先动数据)
sp指向栈的最高地址上一位。(格式化只是移指针)(栈空时指到栈外(+2))
ES:(备用段寄存器)
另:1.汇编指令不区分大小写
2.从后向前运算
3.超出位进到其他寄存器(分高低位时高低位之间也无法直接进位:add al,88)
4.cpu访问内存必须要物理地址
5.段地址和偏移地址可以指示2的16次方个内存单元
检测点暴露的问题:
1.不加H表示10进制,需要先转换为16进制;
2.注意区分mov 和add;
3.sub ax,bx 把ax减去bx;
dosbox的一些操作: (加不加空格都行)
1.r 查看、改变cpu寄存器内容 r 寄存器(改变寄存器的值为指定值)
2.d 查看内存中的内容 d 段地址:偏移地址 n-1(查看该地址开始的n(默认128)个内存单元)
3.e 改写内存中的内容 e 起始地址 数据 数据。。。
4.u 将机器指令翻译为汇编指令
5.t 执行一条机器指令
6.a 以汇编格式写入机器指令(执行汇编) a 起始地址(从起始地址开始写入)
7.p 执行到当前行,有循环则全部执行
8.q退出dubug
9.g 执行到某地址;like 断点
第三章 寄存器(内存):
字的存储:分进高字节中(低字节中存不下的存进高字节)
栈:后入先出
push ax:把ax元素压进栈中 push [0]把0的数据压入栈
pop ax:把栈顶推到ax中 pop [0]把数据推到0
push和pop指令可以修改寄存器和内存
栈顶越界问题:向上越界和向下越界
另:1.mov指令访问内存时可以只给出偏移地址(默认ds)
2.可以用段寄存器表示段地址
3.debug的t命令在执行修改SS的指令时,下一条指令也紧接着被执行
检测点暴露的问题:
1.movadd操作的数据量和使用的寄存器有关,相应大小的寄存器调动相应的数据。
2.e d a命令都是操作内存的,r是直接操作寄存器的。
3.使用栈时先设定SS:sp(最高位)
汇编笔记2(编程)
第4章(第一个程序):
用到的工具:MASM.exe LINK.exe
伪指令:编译器处理
例:
assume cs:codesg(定义一个代码段名称)
codesg segment(代码段)
start: 汇编语句
codesg ends(配合结束)
end
assum假设关联
start :入口(可以自己命名)
编译->可执行文件(pe)
int 中断
结束:
程序返回:mov ax,4c00 int 21
段结束:ends
程序结束:end
加分号可以不用设置过程
编译:masm 文件名(加分号可跳过中间过程)
连接:link 文件名(obj文件,加分号可跳过中间过程)
简单方法:ml 文件名(编译并连接)(要有ml.exe)
psp:存放dos和程序交互命令100H,ds偏移psp后是cs:ip
另:1.dos是单任务操作系统
2.dos修改了CS:IP使程序运行
3.debug装入内存会在cx中存储长度
实际操作遇到的问题:
1.non-digit in number:应当在十六进制数后加H
2.debug默认十六进制。
第5章([bx]和loop循环):
[bx]指令:主要因为在文本编辑和debug中不一样,文本编辑中[常数]相当于常数,所以要先存到bx才表示偏移地址。
():描述符号,表示括号内的内容
idata:描述符号,表示一个常量
loop和cx:loop循环直到cx减为0(通常)
loop语句用法:跳转到s标签循环.(在之前设置s标签(s:代码))
编程考虑过程:是否安全(存放位置)->是否能存下结果->初始化(ds,cx,ss等)->调用循环->结束
段前缀:显式给出段地址(cs: ds: ss:)(ds寄存器的作用可以用es:[ ]替代)
安全空间(底层而不冲突的空间):0:200~0:2ff
另:1.执行循环最好把mov cx ,idata放在要循环的部分前
2.汇编源程序中数据不能以字母开头,在前面加0
3.不同段可以再用一个段寄存器
编程中的问题:
1.loop语句在要循环的语句之后
2.源程序中地址表示形如ds:[idata]
3.注释采用';'
第6章(多个段的程序):
解决的问题:既要有代码又有数据
1.不使用多个段:
例:
assume cs:code
code segment
dw 123ah,34d3H
start:mov ax,cs;设定栈
mov ss,ax
mov sp,0004;偏移地址为dw之和(因为数据从0开始所以直接相当于dw长度和)
mov bx,0
mov ax,4c00h
int 21h
code ends
end start
db :定义字节型数据(8)--->dw:定义字型数据(16)--->dd:定义双字结构(32)
既可以用来设置内存值,又可以用来分配内存。
直接放在代码段,段地址CS,偏移地址在最开始为0
设定start(见前面)入口使cpu区分数据和代码。--->end可以通知入口是谁
2.使用多个段:
assume cs:code,ds:data,ss:stack
data segment
dw 1234h,2334h
data ends
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack;stack,data,code都是存有段地址的常量
mov ss,ax
mov sp,10h;stack段的dw之和
mov ax,4c00h
int 21h
code ends
end start
另:
1.不同段段地址相差至少1
实验暴露的问题:
1.段地址是10倍
2.loop后面要跟段标签
3.不能直接把段标签拿来用,要放到段寄存器里才能用
汇编笔记3(数据)
第7章(更灵活定位内存):
and ah,10011010B 与运算(一0则0)可以把操作位设为0(11111101把第7位设为0)
or ah,10010010B 或运算(一1则1)可以把操作位设为1(00000010把第7位设为0)
'......'的方式表示ASCII码给出
db '1234' = db 31h,32h,33h,34h
[bx+idata]访问偏移地址 其他写法[idata+bx],idata[bx],[bx].idata,[bx][si],[si][bx](注意常数在后有.)
[bx+idata]方便数组访问,idata确定元素开头,dx作为相对地址变化。例:
[0+bx] 'abcde' 0[bx]
[5+bx] 'fghij' 5[bx]
si和di,作为bx的补充,性质相同
[bx+si+idata]类似[bx+idata]
需要暂存数据都采用栈的方法(ss栈段)
另:1.字母大小写相差20h
2.利用and or 和第5位来决定大小写
3.采用0[] 8[]这种形式可以只使用一个偏移寄存器提高效率
基础复习:
1.一个内存单元8bit,表示为两位16进制数,一个字节8bit。
2.不能直接把内存mov到内存
实际操作暴露的问题:
1.能操作数值的就是几个寄存器,没有变量
2.采用的寄存器不同(ax,al)一次读取的内存不同,地址所需偏移不同。
3.tap和空格产生同样对齐效果但占用内存不同
4.要双层循环应当先把cx临时存在其他地方(尽量不要在寄存器,栈),内层循环后再带回
第8章(数据处理):
reg表示寄存器,sreg表示段寄存器
[bx]寻址形式的只有bx,bp,si,di可用(8086),bx与bp,si与di不共存
bp默认段地址为ss,bx默认段地址ds。。。可以显式指定
数据位置:1.idata立即数 2.寄存器 3.内存
指明数据长度方法:
1.根据转移的寄存器确定
2.X ptr 例:inc word ptr [bx]
3.命令默认:push字
数据操作过程:确定位置,确定类型(长度),修改
汇编上的结构体:bx定位整个结构体,idata定位某一数据项,si定位数组项元素
div:除法 被除数位数16(32),除数位数8(16)(div reg或者div 内存单元)
被除数为ax 除数为8位(决定类型),al存商,ah存余
被除数为 dx放高16位,ax放低16位 除数为16位(决定类型),ax存商,dx存余
dup:类型 重复次数 dup(重复内容)例:db 3 dup(1,2)===>db 1,2,1,2,1,2
实际操作的问题:
1.div后面不能直接接除数,要存在寄存器中(除数位数由此决定)使用。
2.一个偏移地址只决定一个内存单元
3.出现结果不写入时,先检查分配内存是否包含
汇编笔记4(跳转)
第9章(转移指令):
8086转移指令:
无条件转移指令,条件转移指令,循环指令,过程,中断
offset(伪指令):取得标号偏移地址(也就是要配合标号使用))取得值相当于一个常数(不能直接给段寄存器)
jmp(无条件跳转):可以修改ip也可以修改cs和ip
(基于偏移) jmp short 标号:修改ip机器指令不包含目的地址,包含偏移地址
short 8位的转移(-128~127),near ptr 16位的转移(-32769~32767)
(基于目的地址)jmp far ptr 标号
(基于寄存器)jmp 16位寄存器(存有转移目的地址)
(基于内存)jmp word ptr 内存单元地址(存有转移目的地址)
word段内转移 ,dword段间转移
jcxz(有条件跳转指令)(都是短转移(基于偏移(-127~128)))
jcxz 标号(若cx=0则转移到标号处)
loop(短转移)循环cx次 loop会先对cx-1再判断cx与0的关系(基于偏移(-127~128))
短跳转越界会报错。
实际操作暴露的问题:
1.32位方式cs在后,ip在前
2.不同数据段偏移地址互不影响
3.数据不能直接赋给内存
4.实验8原理,标号代表的是偏移地址,在执行之前得到并转化,所以向上跳。
实验9(彩色打印):
B8000H~BFFFFH为显示缓冲区,分8页,默认显示第一页(B8000H~B8F9FH)
一行共160个字节,两个字节表示一个字符(偶地址放ascii,奇地址放属性各1字节)(从0到159)
属性: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
BL R G B I R G B
闪烁 背景 高亮 前景
编程暴露的问题:1.ss,cs值不能直接改变,不能有像mov ss,ax这种
2.初始化应在入栈,循环(标号)前
3.注意一个字占两个内存单元,add bx,2而不是inc bx
第10章(call和ret):
ret指令:用栈中命令修改ip(出栈)(ip先正常加再被覆盖)
retf指令:依次出栈ip和cs
call指令:将当前ip(call指令下一个指令开始地址)或cs和ip入栈,转移(非短转移)
基于标号: (段内)call 标号:ip入栈后到标号处执行
(段间)call far ptr 标号 :cs入栈,ip入栈后到标号处执行
基于寄存器:(段内) call 16位寄存器
基于内存:(段内)call word ptr 内存单元地址(ip入栈后执行内存单元中的地址)
(段间)call dword ptr 内存单元地址(存放顺序cs ip)(csip入栈后执行内存单元中的地址)
call+ret实现子程序:call和ret配对
call到mov ax,4c00h int 21h后,再用ret返回回来就可以实现子程序调用
子程序参数和结果需要寄存器较少时存放在寄存器中(参数寄存器,结果寄存器)
参数和结果较多时将批量数据存在内存中或栈中,首地址放在寄存器中
发生寄存器冲突(不同操作依赖同一寄存器)使用栈push pop来避免冲突
mul:乘法命令(mul 寄存器;mul byte ptr 内存地址;mul word存在 ptr 内存地址)
8位*8位(结果放入ax)默认al*
16位*16位(结果放入dx+ax)默认ax*
实验代码不难,鱼c论坛上都有(判断0用cx+jcxz的方法)
检测点暴露的问题:1.sp和bp默认段寄存器都是ss
2.0B8000h部分是显卡地址是动态变化的,debug跟踪会有问题
3.子程序设计时开始入栈,ret出栈以提高通用性
汇编笔记5(标志与中断)
第11章(标志寄存器):
标志寄存器都是16位
flag:按位起作用(其他寄存器是整个起作用)
1,3,5,12,13,14,15为保留位,不具有意义 debug中的对应
零位标志位ZF:(6)上一次指令执行后结果为0则为1 (0)NZ (1) ZR
奇偶标志位PF:(2)二进制1的个数为偶数为1,为奇数为0 PO PE
符号标志位SF:(7)结果为负为1,为正为0(对于有符号数) PL NG
进位标志位CF:(0)无符号运算时记录进位和借位(都为1) NC CY
溢出标志位OF(11)有符号数运算时的进位和错位(只要最高位改变) NV
方向标志位DF(10)控制每次操作后si,di的递增递减
CF的运用:置0方法:sub ax,ax
inc,loop不影响CF add会影响,所以使用inc改变数值而不是add
adc:adc ax,bx==>(ax)=(ax)+(bx)+CF(先算低位后高位)
带进位的加法指令:利用CF位,把上一次的进位加到本次,adc也会影响CF,可以达到任意位相加
sbb:sbb ax,bx==>(ax)-(bx)-CF(先算高位后低位)
cmp:功能相当于减法,但不保存值,只作为判断依据(操作寄存器不变化,只影响flag寄存器中标志)
考虑结果正负需要考虑SF和OF
检测比较结果的条件转移
1.无符号考虑ZF,CF 有符号考虑SF,OF,ZF
(以下指令都是无符号,且都是根据flag寄存器判断,也就是不配合cmp也可以用)
je:等于则转移 ZF
jne:不等于则转移
jb:低于则转移 CF
jnb:不低于则转移
ja:高于则转移 CF和ZF
jna:不高于则转移
DF的运用:
串传宗指令:movsb(以字节为单位传送)movsw(以字为单位)
将ds:si中的字节送入es:di中并对1si,di递增(DF=0)或递减(DF=1)
配合rep(根据cx值执行多次串传送指令)使用
DF设置指令:cld(设为0),std(设为1)
pushf:将标志寄存器结果入栈
popf:将出栈结果传入标志寄存器
另:1.mov,push,pop等传送指令不改变标志位
问题总结:1.10H这种代表十进制,对应1的个数为16
实际操作的问题:
1.call调用子程序用结束程序隔开,je等跳转用jmp隔开
2.cmp也要注意操作对象位数相同
3.注意混合使用跳转命令提高效率
|
|