|  | 
 
| 
最近学习了小甲鱼老师的汇编教程,感觉受益良多,在此把自己的笔记发一发,希望大佬们给点意见,新手们能一起交流。
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.注意混合使用跳转命令提高效率
 
 
 | 
 |