小甲鱼8086汇编笔记

一 前言

add al,85H ax:00CH

加完之后ax0058H

一个物理地址可以由多种段地址*16+偏移地址得到
偏移地址为16位 寻址范围为0000H-FFFFH 为64KB

可知在制作病毒时,可以将病毒执行的程序放在杀毒软件之前,抢先将杀毒软件覆盖

二修改CS,IP的指令

JMP 段地址:偏移地址

只修改IP的内容

JMP 合法的寄存器

jmp ax

代码段

所以要将CS:IP指向所定义的代码段的第一条指令的地址

三内存

DS一般储存要读取的段地址
MOV:两个功能

mov ax,1000H mov al,[0]将0地址的数据存到al中[0]是偏移地址,ds里为段地址 mov [0],al

四栈

push入栈 pop出栈 以字为单位

push和pop可以对段寄存器,寄存器,内存操作

高位地址栈底 低位地址栈顶 数据从高往低存 取too 高位地址存放高八位地位地址存放低八位

段寄存器SS 存放栈顶的段地址

寄存器SP 存放栈顶的偏移地址

SS:SP指向栈顶的栈顶元素

没有元素的时候SP=0010H

格式化硬盘只是改变了索引的位置,没有覆盖,类似于pop时SS:SP所指向的位置发生改变,但是数据没有变

栈顶超界的问题

结论

栈段

堆栈是临时存放数据的地方

五第一个程序

定义一个段

End是一个汇编程序的结束标记

end与ends不一样不要搞混

assume假设cs的名称为codesg

标号

程序的返回

DOS

DS储存的是SA的值 但是程序的开始是从SA+10H:0开始的

PSP是与操作系统的接口

六[bx]和Loop指令

先减后判断

汇编程序与debug的区别

想要直接使用[ ]则要加上段地址即DS:[2]

0:200-0:2FF 这段一般为安全的空间

段前缀

assume cs:code
​
code segment
​
    mov bx,0
​
    mov cx,12
​
s:  mov ax,0ffffh
​
    mov ds,ax
​
    mov dl,[bx]
​
    mov ax,0020h
​
    mov ds,ax               
​
    mov [bx],dl
​
    inc bx
​
    loop s
​
    mov ax,4c00h
​
    int 21h
​
code ends
​
end

也可以用两个寄存器存放ffffh,0020h,要用段前缀

七包含多个段的程序(封装)

在代码段中使用数据DW即define word 也有DB
观察到从不同地址开始读取指令,结果完全不同,原因是在13f8:0000开始前几个数据是用来储存数据的,但是系统误读为操作指令,因此这个代码要从13f8:0010开始读取指令并运行,不然结果是错误的。
在代码段中使用栈(后进先出)

可以用dw 0,0,0,0,0,0,0,0来开辟空间以此存放数据

将数据,代码,栈放入不同的段(数据段,代码段,栈段)
assume cs:code,ds:data,ss:stack
data segment
    dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
stack segment
    dw 0,0,0,0,0,0,0,0
stack ends
code segment
start:mov ax,stack
      mov ss,ax
      mov sp,16    ;设置栈顶ss:sp指向stack:16
      mov ax,data
      mov ds,ax    ;ds指向data段
      mov bx,0     ;ds:bx指向data段中的第一个单元
      mov cx,8
    s:push [bx]
      add bx,2
      loop s
                   ;以上将data数据依次入栈
      mov bx,0
      mov cx,8
   s0:pop [bx]
      add bx,2
      loop s0   ;以上使8个字形数据依次出栈并送到data段0-16单元
      
      mov ax,4c00h
      int 21h
code ends
end start

如果段中的数据占N个字节,则程序加载后,该段实际占用的空间为
16*[(N/16)+1] 所以可知段的最小为16位 2字节 1字 /为整除

八更灵活定位内存地址

and和or指令

(1)and:逻辑与指令,按位进行与运算

同时为真才为真

(2)or指令:逻辑或运算,按位进行或运算

一个为真即为真

通过该指令可将操作对象的相应位设为1,其他位不变。

例如 :

将al的第6位设为1 : or al,01000000B

将al的第7位设为1:or al.10000000B

将al的第0位设为1: or al.00000001B

ASCII码表

ASCII码只需要8位来存储即一个字节byte

以下均为合法代码

db 'unIX'
mov al,'a'
mov bl,'b'
大写二进制小写二进制
A01000001a01100001
B01000010b01100010
C01000011c01100011
D01000100d01100100
[bx+idata]

用[bx+idata]的形式进行数组的处理

SI和DI

assume cs:codesg,ds:datasg
datasg segment
db 'welcome to masm!'
db '................'
datasg ends
​
codesg segment
start:mov ax,datasg
      mov ds,ax
      mov si,0
      mov di,16
      mov cx,8   /一次赋值两个字节
    s:mov ax,[si]
      mov [di],ax
      add si,2
      add di,2
      loop s
      
      mov ax,4c00h
      int 21h
code ends
end start     

mov ax,[bx+si]==mov ax,[bx] [si]

[bx+si+idata]和[bx+di+idata]

常数在后面的前面要加.

不同寻址方式的灵活应用(更加结构化)

定义6个字符串均为16个字节
用变量R定位行 R+16到下一行
常量定位列
以此来寻址

在嵌套循环中,我们应该在每次内层循环开始的时候,将外层循环的cx中的数值保存起来,在执行外层循环的loop指令前,再恢复外层循环的cx值。

我们可以用dx来保存cx的值

那么我们就是用内存来储存 那么我们要是用什么结构来储存?

一般来说在暂时储存数据的时候,用栈

assume cs:code,ds:data,ss:stack
data segment
    db 4,4,6,4,7,4           ;单词的字母数
    db '          '           ;补齐
    db '1. file         '
    db '2. edit         '
    db '3. search       '
    db '4. view         '
    db '5. options      '
    db '6. help         '
data ends
​
stack segment
    dw 1,2,3,4,5,6,7,8
stack ends
​
code segment
start:
    mov ax,data
    mov ds,ax
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov bx,16
    mov si,0
    
    mov cx,6       ;外层循环6次
    push cx         ;将外层循环的次数保存
    s:
    mov cx,0
    mov cl,[di]         ;内循环的次数 
       s0:
        mov al,[bx][si+3]
        and al,11011111b
        mov [bx][si+3],al
        inc si
        loop s0
    
    add bx,16
    mov si,0
    inc di
    pop cx              ;恢复外层循环的次数
    loop s
    
    mov ax,4c00h
    int 21h
code ends
end start

九数据处理的两个基本问题

处理的数据在什么地方?

要处理的数据有多长?

reg是寄存器,sreg是段寄存器。

bx,si,di,bp

在8086中只有bx,si,di,bp可以放在[ ]里面

机器指令处理的数据所在位置

处理分为三类:读取,写入,运算

用word ptr指明访问的内存单元的大小
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2

push只进行字操作,不需要进行转换

寻址方式的综合运用

div指令

除数:8位或16位,在寄存器或内存单元中

被除数:(默认)放在ax或dx和ax中

除数被除数
8位16位(AX)
16位32位(DX+AX)

div reg

div 内存单元

例子:div byte ptr ds:[0]

含义:(al)=(ax)/[(dx)*16+0]的商

(ah)=(ax)/[(dx)*16+0]的余数

例子:div word ptr ds:[0]

含义:(ax)=((dx)*10000H+(ax))/[(dx) *16+0]的商

(dx)=((dx)*10000H+(ax))/[(dx) *16+0]的余数

mov dx,1
mov ax,86A1H
mov bx,100
div bx

但是32位除以16位有时会发生溢出现象,因此给出一个公式:

X:被除数 范围:[0,FFFFFFFF]

N:除数 范围:[0,FFFF]

H:X的高16位 范围:[0,FFFF]

L:X的低16位 范围:[0,FFFF]

int( ):描述性运算符,取商

rem( ):描述性运算符,取余

公式:X/N=int(H/N)*65536+[ rem(H/N) *65536+L ]/N

乘以65536即向左移16位

伪指令dd(dword)
data segment
   dd 100001
   dw 100
   dw 0
data ends

第一个除以第二个 存第三个

mov ax,data
mov ds,ax
mov ax,ds:[0];低16位
mov dx,ds:[2];高16位
div word ptr ds:[4]
mov ds:[6],ax
dup操作符

dup是由编译器识别和处理

和db,dw,dd配合使用,用来处理数据的重复

db 3 dup(0)

定义了三个字节,他们的值都是0

相当于db 0,0,0

db 3 dup(0,1,2)

定义了九个字节,他们是0.1.2.0.1.2.0.1.2

相当于db 0,1,2,0,1,2,0,1,2

db 重复的次数 dup (重复的字节型数据)

dw 重复的次数 dup (重复的字型数据)

dd 重复的次数 dup (重复的双字型数据)

十一转移指令的原理(破解经常改变)

8086CPU转移指令分为以下几类
无条件转移指令
条件转移指令
循环指令 如loop
过程
中断
offset操作符(功能是取得标号的偏移地址)
assume cs:codesg
codesg segment
start:mov ax,offset start;相当于mov ax,0/三个字节的机器码
    s:mov ax,offset s    ;相当于mov ax,3
codesg ends
end start
assume cs:codesg
codesg segment
s:mov ax,bx
  mov si,offset s     ;mov ax,bx的机器码占两个字节
  mov di,offset s0
  mov ax,cs:[si]       ;将mov ax,bx送到s0中
  mov cs:[di],ax
s0:nop                 ;nop的机器码占一个字节
   nop
codesg ends
end s
jmp指令(无条件转移)

可以只修改IP,也可以同时修改CS和IP

jmp指令将给出两种信息:

转移的目的地址和转移的距离(段间距离,段内短转移,段内近转移)

jmp short标号(转到标号处执行指令)

对IP修改范围为-128---127

mov ax,0
jmp short s
add ax,q;不执行
s:inc ax

观察到JMP时机器码为EB03,没有显示任何关于08的数据,说明JMP在执行时并不知道08的位置

CPU不需要知道目的地址就可以实现IP的改变

实际上,该指令的功能为(IP)=(IP)+8位位移

8位位移=“标号”处的地址-JMP指令后的第一字节的地址

jmp near ptr 标号 实现的时段内的近转移

(IP)=(IP)+16位位移

16位位移=“标号”出的地址-JMP指令后的第一个字节的地址

范围为-32769----32767

jmp far ptr 标号 实现的是段间转移,又称为远转移

(CS)=标号所在的段的段地址;

(IP)=标号所在的段的偏移地址。

far ptr指明了指令用标号的段地址和偏移地址修改了CS和IP

(cx)--;
if((cx)!=0)
    jmp short 标号;
根据位移进行转移的意义

方便了程序段在内存中的浮动装配

如果转移范围越界会报错

分析指令(考察机器码)
assume cs:codesg
codesg segment
        mov ax,4c00h;正常结束
        int 21h
start:mov ax,0
s:    nop      ;s2到这里也向上跳十个字节而不是跳到s1
      nop
      mov si,offset s
      mov si,offset s2
      mov ax,cs:[si]
      mov cs:[di],ax
s0:   jmp short s
s1:   mov ax,0
      int 21h    ;非正常结束
      mov ax,0
s2:   jmp short s1;向上跳10个字节
      nop
​

十二如何实现汇编语言输出有颜色的字符?

内存地址空间中,B8000H---BFFFFH共32KB的空间,为彩色字符模式的内存缓冲区

在缓冲区中,两个字节表示一个字符,一个字节表示ASCII码,一个字节表示属性

属性字节的格式

76543210
BLRGBIRGB
闪烁背景背景背景高亮前景前景前景

R:红色 G:绿色 B:蓝色

assume cs:code,ds:data,ss:stack
data segment
    db 'welcome to masm!'  ;定义要显示的字符
    db 02h,24h,71h    ;定义三种颜色属性
data ends
​
stack segment
    dw 8 dup(0)
stack ends
​
code segment
​
start:
    mov ax,data
    mov ds,ax
    mov ax,stack
    mov ss,ax
    mov sp,10h
    xor bx,bx    ;bx清零,用来索引颜色
    mov ax,0b872h  ;算出屏幕第十二行中间的显存的段起始位置放入ax中
    mov cx,3       ;s3的循环控制次数,因为要显示三个字符
s3:
    push cx   ;三个进栈操作作为外循环上保存相关寄存器的值,以防止被破坏
    push ax
    push bx
    mov es,ax    ;此时es为屏幕的12行中间显存的段起始位置
    mov si,0    ;si用来索引代码段的字符
    mov di,0    ;di用来定位目标列
    mov cx,10h     ;s1循环控制存放的字符,内循环为10h次,因为一个字符串有10h个字节
s1:
    mov al,ds:[si]
    mov es:[di],al
    inc si
    add di,2 
    loop s1 ;实现偶地址中存放字符
    
    mov di,1 ;di的值设为1;从而为在显存奇地址中存放字符的颜色做准备
    pop bx
    mov al,ds:10h[bx]  ;取颜色属性
    inc bx
    
    mov cx,10h   
s2:
    mov es:[di],al
    add di,2
    loop s2   ;循环存放字符的颜色属性
    
    pop ax
    add ax,0ah
    
    pop cx
    loop s3
    
    mov axc00h
    int 21h
    
    

十三Call和Ret指令

ret和retf

都是转移指令,他们都修改IP或IP和CS。

(一)ret指令用栈中的数据,修改IP的值,从而实现近转移

(1)(IP)=((SS)*16+SP) 栈顶

(2)(SP)=(SP)+2 出栈

(二)retf指令用栈中的数据,改变了CS和IP的值,实现远转移

(1)(IP)=((SS)*16+SP) 栈顶

(2)(SP)=(SP)+2 出栈

(3)(CS)=((SS)*16+SP) 栈顶

(4)(SP)=(SP)+2 出栈

RET RETF
IP IP
CS
栈底 栈底
call指令

(1)将当前IP或CS和IP压入栈中;

(2)转移(jmp)

call 标号(将当前IP压入栈中,转到表好处执行指令)

(1)(SP)=(SP)-2 入栈

(2)((SS)*16+SP)=(IP) 入栈

(3)(IP)=(IP+16位位移

16位位移=“标号”处的地址-call指令后的第一个字节的地址

转移的目的地址在指令中的call指令

call far ptr 标号 远转移

(1)入栈

(SP)=(SP)-2

((ss)*16+(sp))=(CS)

(sp)=(sp)-2

((SS)*16+SP)=(IP)

(2)jmp

(CS)=标号所在的段地址

(IP)=标号所在的偏移地址

转移地址在寄存器的call指令

call 16位reg

(SP)=(SP)-2

((SS)*16+SP)=(IP)

(IP)=16位reg

转移的值在内存中的call指令

(1)call word ptr 内存单元地址

push ip

jmp word ptr 内存单元地址

(2)call dword ptr 内存单元地址

push cs

push ip

jmp dword ptr 内存单元地址

高位地址存放CS 低位地址存放IP

call和ret的配合使用

call指令后面的指令的地址将存储在栈中,可以使用ret来执行跳转回去,执行call指令下一行的地址

mul指令

乘法指令

(1)相乘的两个数:要么都是8位,要么都是16位

8位:AL中和8位寄存器或内存字节单元中

16位:AX中和16位寄存器或内存字单元中

(2)结果

8位:AX中

16位:DX(高位)和AX(低位)中

mul reg

mul 内存单元

模块化程序设计(call and ret)
参数和结果传递的问题(传递参数时要将注释相应寄存器的储存值的作用)
批量数据的传递

将批量数据存取在内存中,然后将内存中的首地址存放在寄存器中

assume cx:code,ds:data
data segment
  db 'conversation'
data ends
code segment
start: mov ax,data
       mov ds,ax
       mov si,0
       mov cx,12
       call capital
       mov ax,4c00h
       int 21h
capital :and byte ptr [si],11011111b
         inc si
         loop capital
         ret
code ends
end start

还有一种通用的方法即使用栈来存储

db 'conversation',0以0为结尾的字符串

jcxz判断cx是否为0(用loop要储存cx,不然cx的复用会导致错误)

显示有颜色的字符
assume cs:code,ds:data
data segment
   db 'welcome to masm',0
data ends
code segment
start:
        mov dh,8
        mov dl,3
        mov cl,2
        mov ax,data
        mov ds,ax
        mov si,0
        call show_str
        mov ax,4c00h
        int 21h
show_str:
        push cx
        push si
        mov al,0A0h   ;每行是有80*2==160字节==0A0h个字节内容,即第n行
        dec dh        ;行号在显存中下标从0开始,所以减1
        mul dh        ;相当于从第(n-1)*0A0h个byte单元开始……
        mov bx,ax     ;定位好的位置偏移地址存放在bx里(行)
        mov al,2    ;每个字符占两个字节
        mul dl       ;定位列,结果ax存放的是定位好的列的位置
        sub ax,2     ;列号在显存中下标从0开始,又因为存放字符,所以减2
        add bx,ax     ;
        mov ax,0B800h   ;显存开始的地址
        mov es,ax    ;es中存放的时现存的第0页(共0-7页)的起始的段地址
        mov di,0       ;di指向显存的偏移地址
        mov al,cl      ;cl是存放颜色的参数,这时候al用来存放颜色了,因为cl下边要用来存放临时的字符
        mov ch,0    ;下边cx存放的是每次准备处理的字符
s:
        mov cl,ds:[si] ;ds:[si]指向data
        jcxz ok      
        mov es:[bx+di],cl   ;字符
        mov es:[bx+di+1],al     :属性
        inc si
        add di,2     ;指向下一个字符
        jmp short s    ;无条件jcxz是离开的关键
ok:
        pop si
        pop cx
        ret   ;显示字符串的子程序
code ends
end start
解决除法溢出的问题

参数:(ax)=dword型数据的低16位

(dx)=dword型数据的高16位

(cx)=除数

返回:(dx)=结果的高16位 (ax)=结果的低16位

(cx)=余数

assume cs:code,ds:data
data segment
   db 10 dup(0) 
data ends
code segment
start:…………
divdw:
        push ax   ;第十六位先保存
        mov ax,dx    ;ax则会时候的值是高16位
        mov dx,0     ;不影响下面的余数位
        div cx       ;H/N
        mov bx,ax    ;ax,bx的值为(int)H/N,dx的值为(rem)H/N
        pop ax   ;ax的值为L
        div cx     ;L/N注意16位除法的时候默认被除数DX为高16为,AX为低16位
        mov cx,dx
        mov dx,bx
        ret
code ends
end start
         
数值显示(30h-39h对应0-9)++前两个++

十四标志寄存器(flag)按位起作用

大多运算指令会影响标志寄存器

ZF标志6零标志位
PF标志2奇偶标志位
SF标志7符号标志位(无符号)
CF标志0进位标志位(假想的最高位)

进位或借位 分8/16

OF标志11溢出标志位(有符号)

SF标志与OF标志的区别在于你做的是什么运算

标志值为1的标记值为0的标记
OFOVNV
SFNGPL
ZFZRNZ
PFPEPO
CFCYNC
DFDNUP
abc指令

带进位的加法指令

adc ax,bx实现的功能是(ax)=(ax)+(bx)+CF

在执行adc指令的时候加上的CF的值的含义,有adc指令前面的指令决定的,也就是说,关键在于所加上的CF的值是被什么指令设置的

显然,如果CF的值是被sub指令设置的,那么他的含义就是借位值;如果是被add指令设置的,那么他的含义就是进位值。

编程计算1EF000H+201000H,结果放在ax(高位)和bx(低位)中

mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H

inc和loop指令不影响CF的值

sbb指令

带借位的减法指令

sub ax,bx的作用:

ax=(ax)-(bx)-CF

cmp指令

比较指令(进行有符号或无符号)

只对寄存器产生变化 相当于减法但不保存结果

cmp ax,ax

ZF=1,PF=1,SF=1,CF=0,OF=0

无符号:

有符号:

在考察正负数也要考察OF 如果有溢出则结果是相反的

检测比较结果的条件转移指令(与cmp配合)
指令含义检测的相关标志位(核心)
je(equal)等于则转移ZF=0
jne不等于则转移ZF=1
jb(blow)低于则转移CF=1
jnb不低于则转移CF=0
ja(above)高于则转移CF=0,ZF=0
jna不高于则转移CF=1或ZF=1
实际上也只是看标志寄存器的值
DF标志10方向标志位and串传送指令
movsb以字节位单位传送

(1)((es)*16+(di))=((ds) *16+(si))

(2)如果DF=0:(si)和(di)++

如果DF=1:(si)和(di)--

将ds:si指向的内存单元中的字节送入es:di中然后再递增或递减

movsw以字位单位传送

+2/-2

一般来说串传送指令与rep配合使用

rep movsw/movsb 根据cx的值重复操作

cld:将DF=0

std:将DF=1

pushf和popf

pushf:将标志寄存器的值压栈;

破平房、:从栈中弹出数据,送入标志寄存器中

pushf和popf为直接访问标志寄存器提供了方法

​​​​​​​

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值