|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
76 楼』:
【80x86汇编语言学习笔记】
转移指令
可以修改IP或同时修改CS和IP的指令统称为转移指令。
转移指令就是可以控制CPU执行内存中某处的代码的指令……
短转移IP的修改范围: -128~127
近转移IP的修改范围: -32768~32767
8086的CPU转移指令分为以下几类:
无条件转移指令(JMP)
条件转移指令
循环指令(LOOP)
过程
中断
操作符 offset
assume cs:code
code segment
start: mov ax,offset start ; 相当于 mov ax,0
s: mov ax,offset s ; 取s标号处的指令偏移地址……
code ends
end start Debiug ……
Quote: | 0B49:0000 B80000 MOV AX,0000
0B49:0003 B80300 MOV AX,0003 |
|
offset操作符取得了标号start和s的偏移地址。
复制start指令到s0处
assume cs:code
code segment
start: mov ax,bx
mov si,offset start
mov di,offset s0
mov ax,cs:[si]
mov cs:[di],ax
s0: nop
nop
code ends
end start 复制的过程……
Quote: | -r
AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0000 NV UP EI PL NZ NA PO NC
0B49:0000 8BC3 MOV AX,BX
-t
AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0002 NV UP EI PL NZ NA PO NC
0B49:0002 BE0000 MOV SI,0000
-t
AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0005 NV UP EI PL NZ NA PO NC
0B49:0005 BF0E00 MOV DI,000E
-t
AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0008 NV UP EI PL NZ NA PO NC
0B49:0008 2E CS:
0B49:0009 8B04 MOV AX,[SI] CS:0000=C38B
-t
AX=C38B BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000B NV UP EI PL NZ NA PO NC
0B49:000B 2E CS:
0B49:000C 8905 MOV [DI],AX CS:000E=9090
-t
AX=C38B BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000E NV UP EI PL NZ NA PO NC
0B49:000E 8BC3 MOV AX,BX
-t
AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0010 NV UP EI PL NZ NA PO NC |
|
JMP指令
段间转移、段内短转移、段内近转移
范围是指字节。
jmp short s 段内短转移
jmp near ptr 段内近转移
jmp far ptr s 段间转移
段间转移
assume cs:code
code segment
start: mov ax,0
mov bx,0
jmp far ptr s
db 256 dup(0)
s: add ax,1
inc ax
code ends
end start
Quote: | 0B49:0000 B80000 MOV AX,0000
0B49:0003 BB0000 MOV BX,0000
0B49:0006 EA0B01490B JMP 0B49:010B |
|
机器码: EA0B01490B 是目的地址在指令中的存储顺序。
(存放次序->低地址:高地址)即逆序存放。
低地址16位,高地址16位。
高地址代表转移的段地址,低地址代表偏移量。
0B49:010B 目标转移地址在机器码中段地址与偏移地址是逆序存放,段与偏移地址中的高位与低位也是逆序存放~:)
看来编译器在将指令(不管是高级语言还是汇编),最终全是机器码。
只不过这个机器码,不同的机器码代表不同的指令,机器码后面跟着数据,
这个数据是前或后(带符号数)或是所赋的值,然后CPU执行到当前指令时将当前IP+当前指令的长度,就算出下一条指令的地址,然后将 ip指过去。
这个时候CPU再执行已取回的指令,然后执行。
当遇到要转移的指令时,算出要转移到哪里,然后再次修改IP的值指向转移的地址(无论它是8位还是16位还是32位-段间转移),
然后执行IP所指向的指令。
如果当前执行的指令不是转移指令,则执行完后就执行上一次取这次指令时已修改的IP的值,所以它直接执行就行了。
如果用批处理做一个极简单的编译器,那么一个是机器码(这是固定),机器码后面跟着不同的数据,需要编译器来计算它们前后的位移量。
理论上可以实现(至于批处理文件--文本文件无法直接存二进制数据),
但也不是没有办法~:)
且VBS可以向文件写入二进制数据,那么完全可以用VBS做编译器。
乱想……
简单的说,段内转移,段间转移,近转移这些名词,实际上只不过不同的机器码后面跟着8位(段内近转移),它是带符号数,也就是表示这么多寻址能力,当然是近转移。在CPU看来,只不过是 机器码后面跟着一个8位地址。
而近转移,只不过就是一个机器码后面跟着一个16位的地址数据。
16位所能表示出来的带符号数也就那么多。机器还是一样。
而被MASM编译器喜欢的语法,什么JMP FAR PTR 的段间转移,看这指令太长,到了机器码那里还不是: 机器码,后面跟着32位的地址数据。
而这个地址数据还是按照INTEL喜欢的逆序存放,低位后面是高位。
而那个32位的转移地址也是一样,先低后高,不管它什么段地址:偏移量,
到了最后还是段*16+偏移量,所以那个高位(后面的)自然就是表示段,用低位表示偏移量。反正到了CPU那里最后还是物理地址,这些段和偏移量只不过是读着方便和编程方便,它是给我们方便而看的。
按照书上的解释再举一反三的拔开那些迷雾……
寻找机器码,先看看 mov 指令的机器码与寄存器它们的表示:
mov ax,0000
mov bx,0000
mov cx,0000
mov dx,0000 Debug分析如下:
Quote: | C:\Masm50>debug
-A
0AF5:0100 MOV AX,0
0AF5:0103 MOV BX,0
0AF5:0106 MOV CX,0
0AF5:0109 MOV DX,0
0AF5:010C
-U 100 109
0AF5:0100 B80000 MOV AX,0000
0AF5:0103 BB0000 MOV BX,0000
0AF5:0106 B90000 MOV CX,0000
0AF5:0109 BA0000 MOV DX,0000 |
|
得到 MOV AX 的机器码是 B8
MOV BX …… 是 BB
MOV CX …… 是 B9
MOV DX …… 是 BA (这些将来被CPU执行的时候都是二进制0011..所表示的“高”、“低”的电讯号…(先瞎想)
假如要开发向BX寄存器赋 0123H (23存在低位,01存在高位),它们存储状态的表示是 2301H,先读高位再读低位是为了视觉上的方便。而Inter要表示的是低位内存地址存低位,高位内存地址存高位。
现在写 MOV CX 这一个命令的编译器。向CX寄存器赋值 0123H(10进制291)。……(略……)
调用Debug……
理论上相当于
MOV AX,0123
Echo B82301>Demo.com 理论上是这样,但Echo不能直接写入二进制到文件。什么语言能写,什么语言就能够实现。
或是
chcp 437
echo ╕#☺>a.com 用Debug装入还是 mov ax,0123
转移地址在内存中的jmp指令的两种格式
从内存单元地址处开始存放着一个字,是转移的目的偏移地址。
1) jmp word ptr 内存单元地址(段内转移)
2) jmp dword ptr 内存单元地址(段间转移)
assume cs:code
code segment
start: mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0] ; 段间转移 Debug: jmp far [0000]
mov ax,4c00h
int 21h
code ends
end start Debug 对比指令
Quote: | C:\Masm50>debug p170.exe
-r
AX=0000 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0000 NV UP EI PL NZ NA PO NC
0B49:0000 B82301 MOV AX,0123
-u
0B49:0000 B82301 MOV AX,0123
0B49:0003 A30000 MOV [0000],AX
0B49:0006 C70602000000 MOV WORD PTR [0002],0000
0B49:000C FF2E0000 JMP FAR [0000]
0B49:0010 B8004C MOV AX,4C00
0B49:0013 CD21 INT 21
0B49:0015 05508D ADD AX,8D50
0B49:0018 46 INC SI
0B49:0019 8050E83E ADC BYTE PTR [BX+SI-18],3E
0B49:001D 0D83C4 OR AX,C483
-t
AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0003 NV UP EI PL NZ NA PO NC
0B49:0003 A30000 MOV [0000],AX DS:0000=20CD
-t
AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0006 NV UP EI PL NZ NA PO NC
0B49:0006 C70602000000 MOV WORD PTR [0002],0000 DS:0002=9FFF
-t
AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000C NV UP EI PL NZ NA PO NC
0B49:000C FF2E0000 JMP FAR [0000] DS:0000=0123
-t
AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0000 IP=0123 NV UP EI PL NZ NA PO NC
0000:0123 F0 LOCK
0000:0124 F3 REPZ
0000:0125 EE OUT DX,AL
-q |
|
使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?
Quote: | assume cs:code
data segment
db 0ffh ; 界标置
dw 0
db 0ffh ; 边界标置~:)
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
jmp word ptr [bx+1]
code ends
end start |
|
使jmp指令执行后,CS:IP指向程序的第一条指令
assume cs:code
data segment
dd 12345678h
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov [bx],bx
mov [bx+2],cs
jmp dword ptr ds:[0]
code ends
end start 假如内存 2000:1000 BE 00 06 00 00 00 ...
跟据下面指令(不运行代码)直接写出结果 (CS)=? IP=?
mov ax,2000h
mov es,ax
jmp dword ptr es:[1000h] 低位:高位,所以 CS=0006,IP=00BE
JCZX指令
cx=0时退出,8位位移,相当于 if((cx)==0) jmp short 标号。
利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后将它的偏移地址存储在dx中。
assume cs:code
code segment
start: mov ax,2000h
mov ds,ax
mov bx,0
s: mov cl,[bx] ; 传送比对是否为0的数据
jcxz ok
inc bx
jmp short s
ok: mov dx,bx
mov ax,4c00h
int 21h
code ends
end start 运行前提交“安排”数据
-d ds:0
2000:0000 FF FF FF 00 FF 00 00 00-00 00 00 00 00 00 00 00
2000:0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 Loop指令
(cx)=(cx)-1 if (cx) 不等于 0 ... (ip)=(ip)+8位有符号数位移。
相当于C表示的: (cx) -- ;
Debug分析代码执行过程……
assume cs:code
code segment
mov ax,4c00h
int 21h
start: mov ax,0
s: nop
nop
mov di,offset s
mov si,offset s2
mov ax,cs:[si] ; ax=s2地址内容
mov cs:[di],ax ; cs代码段=s2地址内的指令
s0: jmp short s
s1: mov ax,0
int 21h
mov ax,0
s2: jmp short s1
nop
code ends
end start 写视频缓冲区(直接写屏)彩色字符串
assume cs:code
data segment
db 'welcome to masm!'
data ends
code segment
start: mov ax,data
mov ds,ax
mov ax,0b800h
mov es,ax
mov ah,00000010b
mov di,0 ; 列
mov cx,16
s: mov al,ds:[si]
mov es:[159*10+32*2+di],ax
inc ah
inc si ; 读进下一个
add di,2 ; 显示下一列字符
loop s
mov ax,4c00h
int 21h
code ends
end start 运行视图
内存地址: B8000H~~BFFFFH 共32KB的空间,为80*25彩色字符模式的显示缓冲区。它分为8页。
低位:字符ASCII,高位:色值(RGB)二进制表示0~7个位的“开关”。
[ Last edited by redtek on 2006-12-30 at 01:32 PM ]
附件
1: 1.GIF (2006-12-31 02:18, 6.08 K, 下载附件所需积分 1 点
,下载次数: 2)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2006-12-29 22:32 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
77 楼』:
【80x86汇编语言学习笔记】
call和ret指令
ret指令用栈中的数据,修改ip的内容,从而实现近转移;
retf指令用栈中的数据,修改cs和ip的内容,从而实现远转移。
CPU执行ret指令时,进行下面两步操作:
(ip)=((ss)*16+(sp))
(sp)=(sp)+2
CPU执行retf指令时:进行下面四步操作:
(ip)=((ss)*16+(sp))
(sp)=(sp)+2
(cs)=((ss)*16+(sp))
(sp)=(sp)+2
CPU执行ret指令时,相当于进行:
pop ip
CPU执行retf指令时,相当于进行:
pop ip
pop cs
ret
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
mov ax,4c00h
int 21h
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,0
push ax
mov bx,0
ret
code ends
end start
retf使用
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
mov ax,4c00h
int 21h
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,0
push cs
push ax
mov bx,0
retf
code ends
end start
Quote: | AX=0000 BX=0000 CX=0026 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0012 NV UP EI PL NZ NA PO NC
0B4A:0012 BB0000 MOV BX,0000
-t
AX=0000 BX=0000 CX=0026 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0015 NV UP EI PL NZ NA PO NC
0B4A:0015 CB RETF
-t
AX=0000 BX=0000 CX=0026 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8004C MOV AX,4C00 |
|
根据PUSH压栈次序对调发现:
当执行retf时,CPU处理的时候是先计算低位,再计算高位。
所以,压栈是先进后出,所以要先压高位再压低位,才可以保证retf执行时先恢复高位后恢复低位,否则出错(相返)。
实现从内存 1000:0000 处开始执行指令
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,1000h
push ax ; 先压高位再压低位,将来retf时cpu会默认先计算低位。
mov ax,0
push ax
retf
code ends
end start Debug跟踪结果
Quote: | AX=0000 BX=0000 CX=0021 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B MOV AX,0B49
-t
AX=0B49 BX=0000 CX=0021 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0003 NV UP EI PL NZ NA PO NC
0B4A:0003 8ED0 MOV SS,AX
-t
AX=0B49 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0008 NV UP EI PL NZ NA PO NC
0B4A:0008 B80010 MOV AX,1000
-t
AX=1000 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000B NV UP EI PL NZ NA PO NC
0B4A:000B 50 PUSH AX
-t
AX=1000 BX=0000 CX=0021 DX=0000 SP=000E BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000C NV UP EI PL NZ NA PO NC
0B4A:000C B80000 MOV AX,0000
-t
AX=0000 BX=0000 CX=0021 DX=0000 SP=000E BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000F NV UP EI PL NZ NA PO NC
0B4A:000F 50 PUSH AX
-t
AX=0000 BX=0000 CX=0021 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0010 NV UP EI PL NZ NA PO NC
0B4A:0010 CB RETF
-t
AX=0000 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=1000 IP=0000 NV UP EI PL NZ NA PO NC
1000:0000 83D200 ADC DX,+00 |
|
call指令
CPU执行call指令时,进行两步操作:
将当前的IP或CS和IP压入栈中;
转移。
call不能实现短转移。call指令实现转移的方法和jmp指令的原理相同。
call 标号(将当前的IP压栈后,转到标号处执行指令)
CPU执行此种格式的call指令时,进行如下操作:
1) (sp)=(sp-2)
(ss)*16+(sp)=(ip)
2) (IP)=(IP)+16位位移
16位位移由编译程序在编译时算出
相当于 CPU 执行指令 "call 标号" 时进行:
push IP
jmp near ptr 标号
下面程序执行后,ax中的数值为多少?
assume cs:code
code segment
start: mov ax,0
call s
inc ax
s: pop ax
code ends
end start 答:执行过程如下:
1) CPU读入 mov ax,0 ,然后将 ip 指向下一条指令
2) 执行 mov ax,0 以后,读入当前ip所指向的指令(call s)
3) 读入 call s,然后将ip指向下一条指令 inc ax(它的IP是6)
3) 执行 call s 指令-》将当前IP值压栈(6),然后执行跳转……S标号……
4) CPU读入指令 pop ax ,将ip指向下一条指令。
5) ……最后 AX=0006
表面上看上去不应该是6,而应该是当前 call s 所在的指令ip地址,但实际上按照CPU执行以及设置IP的次序,它依上述所示。
转移的目的地址在指令中的call指令
指令 "call far ptr 标号" 实现的是段间转移。
CPU执行此种格式的call指令时,进行如下操作:
1) (sp)=(sp)-2 先压高位
((ss)*16+(sp))=(cs)
(sp)=(sp)-2
((ss)*16+(sp))=(IP) 再压低位
2) (CS)=标号所在段的段地址
(IP)=标号所在段的偏多地址
相当于:
push CS
push IP
jmp far ptr 标号
分析
0B49:0000 B80000 MOV AX,0000
0B49:0003 9A0900490B CALL 0B49:0009
0B49:0008 40 INC AX
0B49:0009 58 POP AX
0B49:000A 03C0 ADD AX,AX
0B49:000C 5B POP BX
0B49:000D 03C3 ADD AX,BX
-t
AX=0000 BX=0000 CX=000F DX=0000 SP=FFFC BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0009 NV UP EI PL NZ NA PO NC
0B49:0009 58 POP AX
-d ss:fffc
0B49:FFF0 08 00 49 0B
-t
AX=0008 BX=0000 CX=000F DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000A NV UP EI PL NZ NA PO NC
0B49:000A 03C0 ADD AX,AX
-t
AX=0010 BX=0000 CX=000F DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000C NV UP EI PL NZ AC PO NC
0B49:000C 5B POP BX
-t
AX=0010 BX=0B49 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000D NV UP EI PL NZ AC PO NC
0B49:000D 03C3 ADD AX,BX
-t
AX=0B59 BX=0B49 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000F NV UP EI PL NZ NA PE NC 转移地址在寄存器中的call指令
指令格式: call 16位寄存器
功能:
(sp)=(sp-2)
((ss)*16+(sp))=(IP)
(IP)=(16位寄存器)
CPU执行 call 16位reg时,相当于进行:
push IP
jmp 16位寄存器
实验
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,0000
call ax
inc ax
mov bp,sp
add ax,[bp]
code ends
end start 1) 压当前IP到栈(只压IP),压了ip是为了Call完以后回来时执行ip指向的地址指令
2) jmp 取指令16位寄存器中要求跳转的地址
-t
AX=0B49 BX=0000 CX=0023 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0003 NV UP EI PL NZ NA PO NC
0B4A:0003 8ED0 MOV SS,AX
-t
AX=0B49 BX=0000 CX=0023 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0008 NV UP EI PL NZ NA PO NC
0B4A:0008 B80000 MOV AX,0000
-t
AX=0000 BX=0000 CX=0023 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000B NV UP EI PL NZ NA PO NC
0B4A:000B FFD0 CALL AX
-t
AX=0000 BX=0000 CX=0023 DX=0000 SP=000E BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B MOV AX,0B49
-d ss:e
0B49:0000 0D 00 .. 转移地址在内存中的call指令
两种格式:
1) call word ptr 内存单元地址
2) call dword ptr 内存单元地址]
这两种格式都是先压入IP,然后取要跳转的指定内存单元地址。
call word ptr ...
assume cs:code
data segment
db 16 dup (0)
data ends
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]
code ends
end start call dword ptr 内存单元地址
dword是32位,所以所取的内存单元地址中足够“包含”(段:偏移量)地址。
但CPU处理次序是先低位再高位,所以必须先压(段)再压(偏移:就是IP),当CPU处理时才可以先处理低地址再高地址
所以,dword内存单元中(双字)排列次序:低位:高位
在sp中存放的是ip地址(16位)(字)
当CPU执行 call dword ptr 内存单元地址时,相当于进行:
push CS
push IP
jmp dword ptr 内存单元地址
即:栈内的数据存的是Call完以后回来时要运行的指令地址(也要保持将来先取到低位再取到高位)
而内存单元内存的数据(低位:高低)是指要 jmp dword ptr 的地址~:)
assume cs:code
data segment
db 16 dup (0)
data ends
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,10h
mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2],0
call dword ptr ds:[0]
code ends
end start 最后 bx中的值是8
call和ret配合使用
assume cs:code
code segment
start: mov ax,1
mov cx,3
call s
mov bx,ax
mov ax,4c00h
int 21h
s: add ax,ax
loop s
ret
code ends
end start 当执行完“子程序段”时,由 ret 指令将call调用前压入栈中的IP再弹出来,这样就恢复的调用前的IP,程序继续执行~:)
关于call与ret
assume cs:code
stack segment
db 8 dup (0)
db 8 dup(0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,1000
call s
mov ax,4c00h
int 21h
s: add ax,ax
ret
code ends
end start mul乘法指令
两个相乘的数,要么都是8位,要么都是16位。
计算:
如果是8位,一个默认放在AH中,另一个放在8位寄存器或是内存字节单元中。
如果是16位,一个默认在AX中,另一个放在16位寄存器或内存字单元中。
结果:
如果是8位乘法,结果默认放在AL中。
如果是16位乘法,结果高位默认放在DX中,低位在AX中存放。
格式: mul reg 或 mul 内存单元
内存单地可以用不同的寻址方式给出:
mul byte ptr ds:[0]
含义:(ax)=(dl)*((ds)*16+0)
mul word ptr [bx+si+8]
含义:(ax)=((ax)*(ds)*16+(bx)+(si)+8) 结果的低16位
(dx)=((ax)*(ds)*16+(bx)+(si)+8) 结果的高16位
计算 100*10000
C:\Masm50>debug
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B464 MOV AH,64
-a
0AF5:0100 mov ax,64
0AF5:0103 mov bx,2710
0AF5:0106 mul bx
0AF5:0108
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B86400 MOV AX,0064
-t
AX=0064 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB1027 MOV BX,2710
-t
AX=0064 BX=2710 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 F7E3 MUL BX
-t
AX=4240 BX=2710 CX=0000 DX=000F SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0108 OV UP EI PL NZ NA PO CY
0AF5:0108 46 INC SI
-q 模块化程序设计
参数和结果传递的问题
计算data段中第一组数据的3次方,结果保存在后面一组dword单元中
assume cs:code
data segment
dw 1,2,3,4,5,6,7,8
dd 0,0,0,0,0,0,0,0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0 ; 指向第一组word单元
mov di,16 ; 指向第二组dword单元
mov cx,8
s: mov bx,[si]
call cube
mov [di],ax ; 先写低位
mov [di].2,dx ; 再写高位
add si,2
add di,4
loop s
mov ax,4c00h
int 21h
cube: mov ax,bx ; 3次方的计算结果存在:ax(低位),dx(高位)
mul bx
mul bx
ret
code ends
end start 结果
Quote: | -d ds:0
0B49:0000 01 00 02 00 03 00 04 00-05 00 06 00 07 00 08 00
0B49:0010 01 00 00 00 08 00 00 00-1B 00 00 00 40 00 00 00
0B49:0020 7D 00 00 00 D8 00 00 00-57 01 00 00 00 02 00 00 |
|
批量数据传送
将批量数据放到内存中,然后将它们所在的内存空间的首地址放在寄存器中,传递给需要的子程序。
即,相当于是按址传送数据。刚才上面的寄存器传送最终结果,相当于按值似递。在c、vb中均有类似的说明~:0
将data段中的字符串转为大写
assume cs:code
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 21
capital: and byte ptr [si],11011111b ; 将ds:si所指单元中的字母转大写
inc si
loop capital
ret
code ends
end start Debug 过程
Quote: | C:\Masm50>debug p190.exe
-r
AX=0000 BX=0000 CX=002A DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B MOV AX,0B49
-u
0B4A:0000 B8490B MOV AX,0B49
0B4A:0003 8ED8 MOV DS,AX
0B4A:0005 BE0000 MOV SI,0000
0B4A:0008 B90C00 MOV CX,000C
0B4A:000B E80500 CALL 0013
0B4A:000E B8004C MOV AX,4C00
0B4A:0011 CD15 INT 15
0B4A:0013 8024DF AND BYTE PTR [SI],DF
0B4A:0016 46 INC SI
0B4A:0017 E2FA LOOP 0013
0B4A:0019 C3 RET
0B4A:001A E1E2 LOOPZ FFFE
0B4A:001C F786E1E8DDE2 TEST WORD PTR [BP+E8E1],E2DD
-g 000e
AX=0B49 BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=000C DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=000E NV UP EI PL NZ NA PE NC
0B4A:000E B8004C MOV AX,4C00
-d ss:0
0B49:0000 43 4F 4E 56 45 52 53 41-54 49 4F 4E 00 00 00 00 CONVERSATION.... |
|
编写子程序注意的问题:
子程序开始: 子程序中使用的寄存器入栈
子程序内容
子程序中使用的寄存器出栈
返回(ret,retf)
子程序例程:用子程序转所有字母为大写
assume cs:code
data segment
db 'word',0
db 'unix',0
db 'wind',0
db 'good',0
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s: mov si,bx
call capital
add bx,5
loop s
mov ax,4c00h
int 21h
capital: push cx
push si
change: mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short change
ok: pop si
pop cx
ret
code ends
end start 上面子程序内,使用栈先保存当前寄存器状态,然后再执行子程序内部计算过程,计算完后再弹出保存的值,不与调用者冲突。
Quote: | -d ss:0
0B49:0000 57 4F 52 44 00 55 4E 49-58 00 57 49 4E 44 00 47 WORD.UNIX.WIND.G
0B49:0010 4F 4F 44 00 00 00 00 00-00 00 00 00 00 00 00 00 OOD............. |
|
编写子程序
要求:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
dh=行号(取值0~24),dl=列号(取值范围0~79)
cl=颜色, ds:si指向字符串的首地址
在屏幕的8行3列,用绿色显示 data 段中的字符串。
assume cs:code
data segment
db 'Welcome to masm!',0
data ends
code segment
start: mov dh,8 ; 子程序入口参数: 行=dh; 列=dl; 颜色=cl
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str ; Call 子程序
mov ax,4c00h
int 21h
show_str: push dx ; 保护寄存器值
push cx
push si
mov ax,0b800h
mov es,ax
mov ax,160 ; dx=行
mul dh
mov dx,ax
xor ax,ax
mov al,2
mul cl ; ax=列
add ax,dx
add bx,ax ; 最终行、列在 bx 寄存器中
str: mov ch,0
mov cl,[si]
jcxz ok
mov ch,2
mov es:[bx+di],cx
inc si
add di,2
jmp short str
ok: pop si ; 恢复寄存器值
pop cx
pop dx
ret
code ends
end start 上面显示方式是直接写显存(直接写屏)方式,在CMD下运行编译的.exe无效,Debug内运行有效。MS-DOS 环境下直接运行编译的.exe文件有效。
[ Last edited by redtek on 2007-1-1 at 02:02 PM ]
附件
1: 1.GIF (2007-1-2 03:00, 11.55 K, 下载附件所需积分 1 点
,下载次数: 2)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2006-12-31 03:57 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
78 楼』:
【80x86汇编语言学习笔记】
解决除法溢出问题
例如下面代码将会溢出:(1000 / 1)
C:\Masm50>debug
-a
0AF5:0100 mov bh,1
0AF5:0102 mov ax,03e8
0AF5:0105 div bh
0AF5:0107
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B701 MOV BH,01
-t
AX=0000 BX=0100 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0102 NV UP EI PL NZ NA PO NC
0AF5:0102 B8E803 MOV AX,03E8
-t
AX=03E8 BX=0100 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0105 NV UP EI PL NZ NA PO NC
0AF5:0105 F6F7 DIV BH
-t
Divide overflow 子程序描述:
名称: divdw
功能: 进行不会产生溢出的除法运算。被除数为dword型,除数为word型,结果为dword型。
下面公式将可能产生溢出的除法运算,转变为多个不会产生溢出的除法运算。
公式: X/N=int(H/N)*6636 + [rem(H/N)*65536+L]/N]
商:(被除数高16位 / 除数) * 65536 + [ 余数:(被除数高16位 / 除数) * 65536 + 被除数低16位 ] / 除数
返回:(dx)=结果的高16位
(ax)=结果的低16位
(cx)=余数
根据公式写出子程序以及完整代码:
;---------------------------------------------------
; 计算 1000000/10 (F4240H/0AH)
; 2000005/10 (1E8485/0AH) = 200000.5 ( 30D40H .5H )
; 2500005/10 (2625A5/0AH) = 250000.5 ( 3D090H .5H )
;---------------------------------------------------
assume cs:code
code segment
start: mov ax,25a5h ; 被除数低16位
mov dx,0026h ; 被除数高16位
mov cx,0ah ; 除数
call divdw ; Call 计算无溢出除法子程序
mov ax,4c00h
int 21h
divdw: mov bx,ax ; 保存被除数低16位的值
mov ax,dx
mov dx,0
div cx ; 计算被除数高16位 / 除数)
; ax=商 dx=余数
mov di,dx ; 保存余数到 di 寄存器
mov dx,1
mul dx
mov si,ax ; 计算公式: 商:(被除数高16位 / 除数) * 65536
; ---------------------------------------------
; BX=被除数低16位 DI=余数 SI=公式左边结果(高16位)
; ---------------------------------------------
mov dx,di ; 省略了 低16位余数*65536的步骤,直接升低16位为高位代替计算
mov ax,bx ; 送入被除数低16位
xor bx,bx ; 清零
div cx ; 计算公式右边: [ 余数 + 被除数低16位 ] / 除数
; ---------------------------------------------
; AX=商 DX=余数
; ---------------------------------------------
mov cx,dx ; 返回值: CX=余数
mov dx,si ; 放入公式左边高位
; --------------------------------- 返回值 ----
; AX=低16位 DX=高16位 CX=余数
; ---------------------------------------------
xor si,si
xor di,di ; 返回前清零
ret
code ends
end start 结果
; ============ 计算 2500005/10 (2625A5/0AH) = 250000.5 ( 3D090H .5H ) ====
;
; AX=D090 BX=0000 CX=0005 DX=0003 SP=0000 BP=0000 SI=0000 DI=0000
; DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000C NV UP EI PL ZR NA PE NC
; 0B49:000C B8004C MOV AX,4C00
;
; =========================================================================== [ Last edited by redtek on 2007-1-3 at 01:46 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-2 04:04 |
|
|
220110
荣誉版主
积分 718
发帖 313
注册 2005-9-26
状态 离线
|
『第
79 楼』:
推荐你看<<黑客反调试技术>>.
PS:这不是一本黑客技术的书,着重是反调试技术.从正常软件生产来看掌握调试技术是必要的,但从不道德的角度,反调试就是破解,反编译.这书有相当的难度.
|
|
2007-1-2 12:09 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
80 楼』:
非常感谢220110版主推荐~:)
在当当网找到一本《黑客反汇编揭秘》,不知道是不是这本书?
还有一本叫做《黑客调试技术揭秘》,作者:(美)卡斯帕克尔。
不知道《黑客反调试技术揭秘》和《黑客调试技术揭秘》这两本是不是指同一本书?
《黑客调试技术揭秘》的目录:
第1部分 调试工具入门
第1章 调试工具简介
第2章 在UNIX环境中进行调试的特性
第3章 模拟调试器和仿真器
第4章 用BoundsChecker进行应用程序分析
第2部分 调试工具入门
第5章 保护机制简介
第6章 熟悉调试器
第7章 IDA崭露头角
第8章 注册保护机制之道
第9章 散列及其克服
第10章 常见的用于演示版的保护机制
第3部分 反调试技术
第11章 反调试技术简介
第12章 各种各样的反调试技术
第13章 UNIX特有的反调试技术
第14章 可自我修改的代码
第15章 使用隐含的自我控制来创建不可破解的保护
第16章 智力调试
第17章 软件保护
第18章 如何使你的应用程序更可靠
第19章 软件测试
第4部分 应用程序和操作系统的严重错误
第20章 应用程序和操作系统的严重错误简介
第21章 战兢苟活还是出死入生
第22章 如何利用内转储
第5部分 PE文件
第23章 PE文件格式
第24章 PE文件中插入和删除代码的技术
附盘说明 [ Last edited by redtek on 2007-1-3 at 01:43 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-4 02:37 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
81 楼』:
【80x86汇编语言学习笔记】
子程序
数值显示
将数据用十进制形式显示到屏幕上,进行两步工作:
1) 将二进制信息存储的数据转变为十进制形式的字符串;
2) 显示十进制表式的字符串
以数字表示的16进制(如 1 , 2, 3, ... )与数字的ASCII码之间相差30H,即1H:如果希望显示1,则必须1H+30H才等于它的ASCII码。
编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。
assume cs:code
data segment
db 10 dup (0)
data ends
code segment
start: mov ax,12666
mov bx,data
mov ds,bx
mov si,0
call dtoc ; 调用二进制转十进制子程序
mov dh,8
mov dl,3
mov cl,2
call show_str ; 调用显示字符串子程序
mov ax,4c00h
int 21h
dtoc: mov bx,10 ; 除以10取余数分解成单个数字
s1: mov cx,ax ; 为jcxz提供判断是否除尽的依据
jcxz dtoc_ret ; 除尽时,当cx为0时结束子程序
xor dx,dx ; 清零
div bx ; AX=商 DX=余数
add dx,30h ; 转十进制数码值为十进制数码字符对应的ASCII码
mov [si],dl ; 存储单个数字
inc si ; 指针下移
jmp s1 ; 如果没除尽,则继续除
dtoc_ret: dec si
xor bx,bx
xor si,si
ret
show_str: xor di,di
mov bx,0b800h ; 视频缓冲区起始
mov es,bx
mov ax,160 ; 每行字符数
mul dh ; 计算n行所占用的字符串,结果在AX中
mov bx,ax ; 显示字符串超始偏移量(行)
xor ax,ax
mov al,2
mul dl ; 计算n列所占用字符数
add bx,ax ; BX=最终显示字符串偏移量
mov dl,cl ; 将颜色送入不与下面计算冲突的寄存器中
view: mov ah,dl ; 定义字符显示颜色
mov al,[si]
mov ch,0
mov cl,al
jcxz ok
mov es:[bx+di],ax ; 将要显示的字符送入视频缓冲区
inc si
add di,2
jmp short view
ok: xor ax,ax
xor bx,bx
xor cx,cx
xor di,di
xor si,si
ret
code ends
end start 因为使用了要显示的数字除以10,做为分解的每一个位余数+30H就等于要显示的这个数字的ASCII,所以出来的是倒序了。
(所以相代码未完,需要显示前倒序一次就变成可以显示的正确的数字了)
这回可以正序显示了~:)
assume cs:code
data segment
db 10 dup (0)
data ends
code segment
start: mov ax,12666
mov bx,data
mov ds,bx
mov si,0
call dtoc ; 调用二进制转十进制子程序
mov dh,8
mov dl,3
mov cl,2
call show_str ; 调用显示字符串子程序
mov ax,4c00h
int 21h
dtoc: mov bx,20h
mov ss,bx
mov sp,20
mov bx,10 ; 除以10取余数分解成单个数字
s1: mov cx,ax ; 为jcxz提供判断是否除尽的依据
jcxz dtoc_ret ; 除尽时,当cx为0时结束子程序
xor dx,dx ; 清零
div bx ; AX=商 DX=余数
add dx,30h ; 转十进制数码值为十进制数码字符对应的ASCII码
push dx ; 将余数压栈,方便将倒序的数字正序存放
inc si ; 指针下移
jmp s1 ; 如果没除尽,则继续除
dtoc_ret: mov cx,si ; 取到几个余数就循环几次
xor si,si
s2: pop ax ; al (低位)存有要正序的数字ASCII码,将其送入ds
mov [si],al
inc si
loop s2
xor bx,bx
xor si,si
xor ax,ax
ret
show_str: xor di,di
mov bx,0b800h ; 视频缓冲区起始
mov es,bx
mov ax,160 ; 每行字符数
mul dh ; 计算n行所占用的字符串,结果在AX中
mov bx,ax ; 显示字符串超始偏移量(行)
xor ax,ax
mov al,2
mul dl ; 计算n列所占用字符数
add bx,ax ; BX=最终显示字符串偏移量
mov dl,cl ; 将颜色送入不与下面计算冲突的寄存器中
view: mov ah,dl ; 定义字符显示颜色
mov al,[si]
mov ch,0
mov cl,al
jcxz ok
mov es:[bx+di],ax ; 将要显示的字符送入视频缓冲区
inc si
add di,2
jmp short view
ok: xor ax,ax
xor bx,bx
xor cx,cx
xor di,di
xor si,si
ret
code ends
end start [ Last edited by redtek on 2007-1-4 at 04:53 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-4 03:22 |
|
|
amao
中级用户
积分 316
发帖 152
注册 2006-6-18
状态 离线
|
|
2007-1-4 04:08 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
83 楼』:
哈哈……
我每往后学一点儿都像看天书一样~:)
不过难就难吧,一天一点儿,总有掌握它的这一天~:)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-5 03:09 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
84 楼』:
【80x86汇编语言学习笔记】
实验 寻址方式在结构化数据访问中的应用
年份 收入(千美元) 雇员(人) 人均收入(千美元)
======================================================================
1975 16 3 ?
1976 22 7 ?
1977 382 9 ?
1978 1356 13 ?
1979 2390 28 ?
1980 8000 38 ?
1981 16000 130 ?
1982 24486 220 ?
1983 50065 476 ?
1984 97479 778 ?
1985 140417 1001 ?
1986 197514 1442 ?
1987 345980 2258 ?
1988 590827 2793 ?
1989 803530 4037 ?
1990 1183000 5635 ?
1991 1843000 8226 ?
1992 2759000 11542 ?
1993 3753000 14430 ?
1994 4649000 15257 ?
1995 5937000 17800 ?
====================================================================== 代码如下:
assume cs:code,ds:data,es:table
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
code segment
start: mov ax,data
mov ds,ax ; DS=要读取的数据段
mov ax,table
mov es,ax ; ES=要写入的数据段
mov bx,0 ; 行偏移量
mov si,0 ; 读取偏移量
mov di,0 ; 写入偏移量
mov dx,0
mov cx,21
mov ax,20h ; 分配栈空间
mov ss,ax
mov sp,20
s: mov ax,ds:[si].0 ; 读取年份后再写入es段
mov es:[bx].0,ax
mov ax,ds:[si].2
mov es:[bx].2,ax
mov ax,ds:[4*21+si].0 ; 读收入, dword 型数据
mov es:[bx].5,ax ; 写
mov ax,ds:[4*21+si].2
mov es:[bx].7,ax ; 写收入数据
push si ; 保存当前si的值
mov si,bp ; word 型公司雇员数据每次只递增2
mov ax,ds:[4*21+4*21+si].0 ; 公司雇员
mov es:[bx].10,ax
add bp,2
pop si ; 恢复si偏移量的值
push cx
mov ax,ds:[4*21+si].0
mov dx,ds:[4*21+si].2
mov cx,es:[bx].10
div cx
mov es:[bx].13,ax ; 写入人均收入
pop cx ; 恢复cx寄存器
add si,4
add bx,10h
loop s
mov ax,4c00h
int 21h
code ends
end start 运行结果
-d ds:0
0B49:0000 31 39 37 35 31 39 37 36-31 39 37 37 31 39 37 38 197519761977197
0B49:0010 31 39 37 39 31 39 38 30-31 39 38 31 31 39 38 32 197919801981198
0B49:0020 31 39 38 33 31 39 38 34-31 39 38 35 31 39 38 36 198319841985198
0B49:0030 31 39 38 37 31 39 38 38-31 39 38 39 31 39 39 30 198719881989199
0B49:0040 31 39 39 31 31 39 39 32-31 39 39 33 31 39 39 34 199119921993199
0B49:0050 31 39 39 35 10 00 00 00-16 00 00 00 7E 01 00 00 1995........~..
0B49:0060 4C 05 00 00 56 09 00 00-40 1F 00 00 80 3E 00 00 L...V...@....>.
0B49:0070 A6 5F 00 00 91 C3 00 00-C7 7C 01 00 81 24 02 00 ._.......|...$.
-d es:0
0B57:0000 31 39 37 35 20 10 00 00-00 20 03 00 20 05 00 20 1975 .... .. ..
0B57:0010 31 39 37 36 20 16 00 00-00 20 07 00 20 03 00 20 1976 .... .. ..
0B57:0020 31 39 37 37 20 7E 01 00-00 20 09 00 20 2A 00 20 1977 ~... .. *.
0B57:0030 31 39 37 38 20 4C 05 00-00 20 0D 00 20 68 00 20 1978 L... .. h.
0B57:0040 31 39 37 39 20 56 09 00-00 20 1C 00 20 55 00 20 1979 V... .. U.
0B57:0050 31 39 38 30 20 40 1F 00-00 20 26 00 20 D2 00 20 1980 @... &. ..
0B57:0060 31 39 38 31 20 80 3E 00-00 20 82 00 20 7B 00 20 1981 .>.. .. {.
0B57:0070 31 39 38 32 20 A6 5F 00-00 20 DC 00 20 6F 00 20 1982 ._.. .. o.
-d
0B57:0080 31 39 38 33 20 91 C3 00-00 20 DC 01 20 69 00 20 1983 .... .. i.
0B57:0090 31 39 38 34 20 C7 7C 01-00 20 0A 03 20 7D 00 20 1984 .|.. .. }.
0B57:00A0 31 39 38 35 20 81 24 02-00 20 E9 03 20 8C 00 20 1985 .$.. .. ..
0B57:00B0 31 39 38 36 20 8A 03 03-00 20 A2 05 20 88 00 20 1986 .... .. ..
0B57:00C0 31 39 38 37 20 7C 47 05-00 20 D2 08 20 99 00 20 1987 |G.. .. ..
0B57:00D0 31 39 38 38 20 EB 03 09-00 20 E9 0A 20 D3 00 20 1988 .... .. ..
0B57:00E0 31 39 38 39 20 CA 42 0C-00 20 C5 0F 20 C7 00 20 1989 .B.. .. ..
0B57:00F0 31 39 39 30 20 18 0D 12-00 20 03 16 20 D1 00 20 1990 .... .. ..
-d
0B57:0100 31 39 39 31 20 38 1F 1C-00 20 22 20 20 E0 00 20 1991 8... " ..
0B57:0110 31 39 39 32 20 58 19 2A-00 20 16 2D 20 EF 00 20 1992 X.*. .- ..
0B57:0120 31 39 39 33 20 28 44 39-00 20 5E 38 20 04 01 20 1993 (D9. ^8 ..
0B57:0130 31 39 39 34 20 28 F0 46-00 20 99 3B 20 30 01 20 1994 (.F. .; 0.
0B57:0140 31 39 39 35 20 68 97 5A-00 20 88 45 20 4D 01 20 1995 h.Z. .E M. [ Last edited by redtek on 2007-1-5 at 04:28 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-5 07:10 |
|
|
9527
银牌会员
努力做坏人
积分 1185
发帖 438
注册 2006-8-28 来自 北京
状态 离线
|
『第
85 楼』:
redtek兄,我也想学学,能把你现在看的书名或者电子书还有网站告诉我吗?期待中ing........
|
我今后在论坛的目标就是做个超级坏人!!! |
|
2007-1-6 00:27 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
86 楼』:
因为书名相同但出版社不同的书很多,所以列出在当当网可以查到的详细关于这本书的介绍信息~:)
(下面这本书如果是自学,极难。整本都是望而生畏的内容,很可能看几眼就再也不想学汇编了。)
(但其内容非常详细,教学方式适合学校有专业老师辅导。)
(但用某些汇编快速入门的书自学一段时间后,还必须要看这本书,因其内容很全)
Intel 80X86/Pentium汇编语言程序设计(第二版)
http://product.dangdang.com/product.aspx?product_id=8936113
(下面这本书适合在没有任何人帮助你的情况下,完全自学汇编语言)
(很少有望而生畏的内容--因全是入门内容)
(不适合学校教学之用--因为适合完全自学,所以此书教学速度极慢)
汇编语言
http://product.dangdang.com/product.aspx?product_id=8732001
(下面这本书内容非常深,从最简单的十几句汇编语言写一个极微小的“操作系统”到完成一个操作系统的每一步底层)
(看完这本书就比较精通操作系统底层并可以自己写较简单的操作系统了……)
(要求熟悉汇编与C语言--但主要以C语言介绍如何写操作系统)
自己动手写操作系统
http://product.dangdang.com/product.aspx?product_id=9024751
(希望精通磁盘和准备自己从头到尾的写一个Linux操作系统出来,看这本书)
Linux 0.01内核分析与操作系统设计(附CD—ROM光盘一张)——创作你自己的操作系统
http://product.dangdang.com/product.aspx?product_id=8956136
建议不要找电子版的书。电子版的书不适合出门带着看、爬在床上看、坐在椅子上在任何地方看、随时翻着看……
其它资料就是lxmxn在这个网志里介绍的那个网站和Google.com上可以找到的网站~:)
一定要花钱买,强迫自己要不就学出来!要不就白花钱惩罚自己~:)
如果不强迫自己花钱买,很可能遇到难的地方就主动放弃了~:)
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-6 03:52 |
|
|
9527
银牌会员
努力做坏人
积分 1185
发帖 438
注册 2006-8-28 来自 北京
状态 离线
|
『第
87 楼』:
谢谢redtek兄提供学习资料,是啊,有时候不给自己一点压力是不成啦,会变的很懒散很颓废很是没用啊
|
我今后在论坛的目标就是做个超级坏人!!! |
|
2007-1-6 04:11 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
88 楼』:
【80x86汇编语言学习笔记】
标置寄存器
ZF标志
flag的第6位,记录相关指令执行后其结果是否为0。0-ZF=1 1-ZF=0
add,mul,div,inc,or,and等运算指令,都会影响标志寄存器。
mov,push,pop等指令对标志寄存器没有影响。
PF标志
flag的第2位是PF,奇偶标志位。记录相关指令执行后,结果的所有二进制位中1的个数是否为偶数,偶数:PF=1,奇数:PF=0
SF标志
flag的第7位是SF,符号标志位。记录相关指令执行后,结果是否为负。负:SF=1,非负:SF=0
它是对有符号数运算结果的一种记录,它记录数据的正负。对于无符号数据计算,SF的值没有意义。
FLAG标置位寄存器结构:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
===================================================
OF DF IF TF SF ZF AF PF CF 在8086 CPU中flag的1、3、5、12、13、14、15没有使用。
在Debug中是 “NV UP EI PL NZ NA PO NC” 次序出现的。
其标置为1还是为0,在Debug中是以其它字母缩写表示的。为1或是为0表示的字母都不一样。
例如:第6位ZF标志(是否为0。结果为零=1,结果非零=0)
在 Debug 中表示其“原始”状态,即均为0时的状态:
NZ就是CF标置的当前表示状态。
至于NZ为什么要出现在从右数向左数的第6位(表面上看是第4位),因为是从FLAG寄存器的低位0表示。
而1、3、5位在8086CPU中未使用,所以“空”过它们,ZF标志自然就出现在了表面上的第4位(实际第6位)。
ZF:零标置位。
当结果为0时,ZF=1,用ZR表示(出现在Debug中)。
当结果为1时,ZF=0,用NZ表示。
记忆其特点:
当看到ZR时,表示结果为0,ZF零标置位当结果为0时为真,所以ZF=1。
当看到NZ时,表示结果非0,ZF零标置位当结果非0时为伪,所以ZF=0。
写出下面每条指令执行后,ZF、PF、SF等标志位的值
sub al,al ZF=1 PF=1 SF=0
mov al,1 ZF=1 PF=1 SF=0
push ax ZF=1 PF=1 SF=0
pop bx ZF=1 PF=1 SF=0
add al,bl ZF=0 PF=0 SF=0
add al,10 ZF=0 PF=1 SF=0
mul al ZF=0 PF=1 SF=0 CF标志
flag的第0位是CF,进位标志位。
在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高的进位值,或从更高位的借位值。
Quote: | -a
0AF5:0100 mov al,97
0AF5:0102 sub al,98
0AF5:0104 sub al,al
0AF5:0106
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B097 MOV AL,97
-t
AX=0097 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0102 NV UP EI PL NZ NA PO NC
0AF5:0102 2C98 SUB AL,98
-t
AX=00FF BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0104 NV UP EI NG NZ AC PE CY
0AF5:0104 28C0 SUB AL,AL
-t
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL ZR NA PE NC |
|
OF标志
flag的第11位是OF,溢出标志位。
OF记录了有符号数运算的结果是否发生了溢出。溢出:OF=1,没有溢出:OF=0。
对于有符号数而言。
CPU在执行add等指令的时候,包含了两种含义:
1) 无符号数运算
CPU用CF位来记录是否产生了进位。
2) 有符号数运算
CPU用OF位来记录是否产生了溢出。
用SF位来记录结果的符号。
写出下面每条指令执行后,ZF、PF、SF、CF、OF等标志位的值。
sub al,al CF=0 OF=0 SF=0 ZF=1 PF=1
mov al,10H CF=0 OF=0 SF=0 ZF=1 PF=1
add al,90H CF=0 OF=0 SF=1 ZF=0 PF=1
mov al,80H CF=0 OF=0 SF=1 ZF=0 PF=1
add al,80H CF=1 OF=1 SF=1 ZF=1 PF=1
mov al,0FCH CF=1 OF=1 SF=1 ZF=1 PF=1
add al,05H CF=1 OF=0 SF=0 ZF=0 PF=0
mov al,7DH CF=1 OF=0 SF=0 ZF=0 PF=0
add al,0BH CF=0 OF=1 SF=1 ZF=0 PF=1 DF标志和串传送指令
flag的第10位是DF,方向标志位。
在串处理指令中,控制每次操作后si,di的增减。
DF=0 每次操作后SI,DI递增。
DF=1 每次操作后SI,DI递减。
标置 值为1的标记 值为0的标记
------------------------
DF DN UP
格式: movsb
功能: 执行movsb指令相当于进行下面几步操作。
1) ((es)*16+(di))=((ds)*16+(si))
2) 如果DF=0则: (si)=(si)+1
(di)=(di)+1
如果DF=1则: (si)=(si)-1
(di)=(di)-1
movsb的功能是将ds:si指向内存单元中的字节送入es:di中,然后根据标志寄存器DF位的值,将si和di递增或递减。
一般movsb(byte)和movsw(word)都和rep配合使用。
用汇编语言来描述 rep movsb 的功能:
s: movsb
loop s
8086 CPU 提供下面两条指令对DF位进行设置:
cld 指令: 将标志寄存器的DF位置0
std 指令: 将标志寄存器的DF位置1
例程:将 data 段中的每一个字符串复制到它后面的空间中。
assume cs:code
data segment
db 'Welcome to masm!'
db 16 dup (0)
data ends
code segment
start: mov ax,data
mov ds,ax
mov es,ax
mov si,0 ; ds:si -> data:0
mov di,16 ; es:di -> data:16
mov cx,16 ; rep循环16次
cld ; 设 DF=0,正向传送
rep movsb ; 字节传送方式
mov ax,4c00h
int 21h
code ends
end start 在 Debug 中 cld 与 rep movsb 指令为:
0B4B:0010 FC CLD
0B4B:0011 F3 REPZ
0B4B:0012 A4 MOVSB 例程:用串传送指令,将F000H段中的最后16个字符复制到data段中。
assume cs:code
data segment
db 16 dup (0)
data ends
code segment
start: mov ax,0f000h
mov ds,ax ; 源 - 读取
mov si,0ffffh ; ds:si -> f000:ffff
mov ax,data ; 目标 - 写入
mov es,ax
mov di,16-1 ; es:di -> data:16
mov cx,16 ; 传送字串长度
std ; 设置 DF=1 逆向传送
rep movsb
mov ax,4c00h
int 21h
code ends
end start
Quote: | -d es:0
0B49:0000 EA 5B E0 00 F0 30 34 2F-30 32 2F 30 34 00 FC 00 .[...04/02/04... |
|
编程:编写一个子程序,将包含任何字符,以0结尾的字符串中的小写字母变成大写字母。
名称: letterc
功能:同上
参数:ds:si 指向字符串首地址
需要进行转化的是字符串中的小写字母‘a’~‘z’,而不是其他字符。
) 小写字母范围: 97 ~~ (97+26-1)
97 ~~ 122
) 结束标置: 0
; 第一次的代码,故障,略…… 调试代码过程中发现一个有意思的错误:)
Quote: | AX=0B49 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0024 NV UP EI PL NZ NA PE CY
0B4D:0024 B500 MOV CH,00
-t
AX=0B49 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0026 NV UP EI PL NZ NA PE CY
0B4D:0026 E2EA LOOP 0012
-d ds:0
0B49:0000 41 42 45 47 49 4E 4E 45-52 27 53 20 41 4C 4C 2D ABEGINNER'S ALL-
0B49:0010 50 55 52 50 4F 53 45 20-53 59 4D 42 4F 4C 49 43 PURPOSE SYMBOLIC
0B49:0020 20 49 4E 53 54 52 55 43-54 49 4F 4E 20 43 4F 44 INSTRUCTION COD
0B49:0030 45 2E 00 00 00 00 00 00-00 00 00 00 00 00 00 00 E...............
0B49:0040 B8 49 0B 8E D8 BE 00 00-E8 05 00 B8 00 4C CD 21 .I...........L.!
0B49:0050 B5 00 8A 0C 80 F9 61 72-0A 80 F9 7A 77 05 80 E1 ......ar...zw...
0B49:0060 DF 88 0C 46 B5 00 E2 EA-C3 2A E4 40 50 8B C3 05 ...F.....*.@P...
0B49:0070 0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50 ..RP..F...P....P
-r
AX=0B49 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0026 NV UP EI PL NZ NA PE CY
0B4D:0026 E2EA LOOP 0012
-t
AX=0B49 BX=0000 CX=FFFF DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0012 NV UP EI PL NZ NA PE CY |
|
当 loop 在递减 CX 寄存器的值的时候发生的上面错误。
所以,代码中不使用 loop ... cx了,重新使用 jcxz 命令。
assume cs:code
data segment
db "Beginner's All-purpose Symbolic Instruction Code.",0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
call letterc ; Call 转小写字母到大写字母子程序
mov ax,4c00h
int 21h
letterc: mov cx,0
s: mov cl,[si]
jcxz return ; 如果取到字符为 0 ,则结束循环
cmp cl,97
jb next ; 如果低于97,则测试下一个字母
cmp cl,122
ja next ; 如果高于 122,则测试下一个字母
ok: and cl,11011111b
mov [si],cl
next: inc si ; 递增指针,指向下一个字符
mov ch,0
jmp short s
return: ret
code ends
end start 遇到一个问题:
在汇编编辑器中,"Beginner”的 'B' 打头字母无论如何更新总是小写方式,后发现在编辑器中 “格式设定” 小写,问题在这里。
将其设置成 “不处理” 即可首字母随意设置。
Debug 调试结果:
Quote: | -d ss:0
0B49:0000 42 65 67 69 6E 6E 65 72-27 73 20 41 6C 6C 2D 70 Beginner's All-p
0B49:0010 75 72 70 6F 73 65 20 53-79 6D 62 6F 6C 69 63 20 urpose Symbolic
0B49:0020 49 6E 73 74 72 75 63 74-69 6F 6E 20 43 6F 64 65 Instruction Code
0B49:0030 2E 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ |
|
[ Last edited by redtek on 2007-1-8 at 02:45 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-6 04:17 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
89 楼』:
【80x86汇编语言学习笔记】
ADC指令
功能: 操作对象1 = 操作对象1 + 操作对象2 + CF
adc是带位加法指令,它利用了CF位上记录的进位值。
指令 adc ax,bx 实现的功能是: (ax)=(ax)+(bx)+CF
Quote: | C:\Masm50>debug
-a
0AF5:0100 mov ax,2
0AF5:0103 mov bx,1
0AF5:0106 sub bx,ax
0AF5:0108 adc ax,1
0AF5:010B
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B80200 MOV AX,0002
-t
AX=0002 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB0100 MOV BX,0001
-t
AX=0002 BX=0001 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 29C3 SUB BX,AX
-t
AX=0002 BX=FFFF CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0108 NV UP EI NG NZ AC PE CY
0AF5:0108 150100 ADC AX,0001
-t
(上面:标置寄存器 CF 进位标置:CY代表进位或是借位,在这里是借位)
(当产生借位状态时,CF=1,所以最后结果是 4)
(上面:标置寄存器 SF 符号标置位为: NG(蓝色) 代表负数,其值为 1)
AX=0004 BX=FFFF CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010B NV UP EI PL NZ NA PO NC |
|
adc指令和add指令相配合可以对更大的数据进行加法运算。
例:计算 1EF000H + 201000H,结果放在AX(高16位)和bx(低16位)中。
Quote: | C:\Masm50>debug
-a
0AF5:0100 mov ax,001e
0AF5:0103 mov bx,f000
0AF5:0106 add bx,1000
0AF5:010A adc ax,0020
0AF5:010D
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B81E00 MOV AX,001E
-t
AX=001E BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB00F0 MOV BX,F000
-t
AX=001E BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 81C30010 ADD BX,1000
-t
AX=001E BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010A NV UP EI PL ZR NA PE CY
0AF5:010A 152000 ADC AX,0020
-t
AX=003F BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010D NV UP EI PL NZ NA PE NC |
|
先低位与低位相加,结果保存在低位中。
当低位相加产生了进位时,其CF的值为1,Debug中表示:CY(代表进位状态)
然后 adc 指令:高位与高位相加,其相加结果由 adc 指令会自动再与CF相加,因刚刚有了进位,所以高位存放的是进位后的值。
高位与低位的组成就是计算在数相加的总值。
计算 1EF0001000H + 2010001EF0H ,结果放在AX(最高16位),BX(次高16位),CX(低16位)中。
Quote: | 13BF:0100 mov ax,1e
13BF:0103 mov bx,f000
13BF:0106 mov cx,1000
13BF:0109 add cx,1ef0
13BF:010D adc bx,1000
13BF:0111 adc ax,0020
13BF:0114
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0100 NV UP EI PL NZ NA PO NC
13BF:0100 B81E00 MOV AX,001E
-t
AX=001E BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0103 NV UP EI PL NZ NA PO NC
13BF:0103 BB00F0 MOV BX,F000
-t
AX=001E BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0106 NV UP EI PL NZ NA PO NC
13BF:0106 B90010 MOV CX,1000
-t
AX=001E BX=F000 CX=1000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0109 NV UP EI PL NZ NA PO NC
13BF:0109 81C1F01E ADD CX,1EF0
-t
AX=001E BX=F000 CX=2EF0 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=010D NV UP EI PL NZ NA PE NC
13BF:010D 81D30010 ADC BX,1000
-t
AX=001E BX=0000 CX=2EF0 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0111 NV UP EI PL ZR NA PE CY
13BF:0111 152000 ADC AX,0020
-t
AX=003F BX=0000 CX=2EF0 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0114 NV UP EI PL NZ NA PE NC |
|
两个1024位数字相加
assume cs:code
data1 segment
dw 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
dw 24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43
dw 44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
dw 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83
dw 84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102
dw 103,104,105,106,107,108,109,110,111,112,113,114,115,116,117
dw 118,119,120,121,122,123,124,125,126,127,128
data1 ends
data2 segment
dw 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
dw 24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43
dw 44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
dw 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83
dw 84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102
dw 103,104,105,106,107,108,109,110,111,112,113,114,115,116,117
dw 118,119,120,121,122,123,124,125,126,127,128
data2 ends
code segment
start: call add1024 ; 调用 1024 位相加计算子程序
mov ax,4c00h
int 21h
add1024: mov ax,data1
mov ds,ax
mov ax,data2
mov es,ax
mov si,0 ; 初始化
mov di,0 ; 内存中低地址放低位,高地址放高位,所以从偏移量0开始
sub ax,ax ; 将CF设置为0
mov cx, 64 ; 1024位数字相加需要循环加 64 次
s: mov ax,[si]
adc ax,es:[di]
mov [si],ax ; 相加结果放回 data1 段中
inc si ; inc 不会改变CF进位标置的值,递增2准备加下一个16位
inc si
inc di
inc di
loop s
ret
code ends
end start sbb指令
sbb是带借位减法指令,它利用了CF位上记录的借位值。
功能:操作对象1 = 操作对象1 -操作对象2 -CF
例: sbb ax,bx 实现的功能是: (ax) = (ax) - (bx) - CF
sbb指令执行后,将对CF进行设置。利用sbb指令可以对任意大的数据进行减法运算。
计算 003E1000H -00202000H,结果放在AX,BX中。
Quote: | C:\Masm50>debug
-a
0AF5:0100 mov bx,1000
0AF5:0103 mov ax,003e
0AF5:0106 sub bx,2000
0AF5:010A sbb ax,0020
0AF5:010D
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 BB0010 MOV BX,1000
-t
AX=0000 BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 B83E00 MOV AX,003E
-t
AX=003E BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 81EB0020 SUB BX,2000
-t
AX=003E BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010A NV UP EI NG NZ NA PE CY
0AF5:010A 1D2000 SBB AX,0020
-t
AX=001D BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010D NV UP EI PL NZ NA PE NC |
|
当执行 SUB BX,2000 时,就是(1000 -2000 )计算,低位中这个计算必然是负数,而且它需要借位。
所以,紫色 NG 符号标置表示这个计算结果是负数。
红色 CY 进位/借位标置表示。
计算时因为是借位,所以 CF=1,在执行比当前低位再高一点的位的时候,则sbb指令会两个数字相减后再减去CF的值,因为借位了,所以要减去。
pushf 和 popf 指令
pushf的功能是将标志寄存器的值压栈。
popf是从栈中弹出数据,送入标志寄存器中。
pushf和popf,为直接访问标志寄存器提供了一种方法。
assume cs:code
code segment
start: mov ax,20h
mov ss,ax
mov sp,16
mov ax,0
push ax
popf
mov ax,0fff0h
add ax,0010h
pushf
pop ax
and al,11000101b
and ah,00001000b
mov ax,4c00h
int 21h
code ends
end start 经过分析,pushf压入栈内的标置寄存器的16位值并非与Debug上显示的位置相对应。
因为标置寄存器的第 1、3、5、12、13、14、15位在8086CPU都没有使用(不具有任何含义),所以压栈后的二进制位表示也是这样安排的。
在80586 CPU中标置寄存器的长度可达32位,有如天书一般。
[ Last edited by redtek on 2007-1-8 at 11:22 AM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-7 07:47 |
|
|
redtek
金牌会员
积分 2902
发帖 1147
注册 2006-9-21
状态 离线
|
『第
90 楼』:
【80x86汇编语言学习笔记】
cmp比较指令
cmp的功能相当于减法指令,只是不保存结果。
cmp指令执行后,将对标志寄存器产生影响。
其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
例: cmp ax,ax
相当于: (ax) -(ax) ,结果为0,但并不在ax中保存。
仅影响 flag 的相关各位。
指令执行后: ZF=1 PF=1 SF=0 CF=0 OF=O
Quote: | C:\Masm50>debug
-a
0AF5:0100 mov ax,8
0AF5:0103 mov bx,3
0AF5:0106 cmp ax,bx
0AF5:0108
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B80800 MOV AX,0008
-t
AX=0008 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB0300 MOV BX,0003
-t
AX=0008 BX=0003 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 39D8 CMP AX,BX
-t
AX=0008 BX=0003 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0108 NV UP EI PL NZ NA PE NC |
|
PE是奇偶标置位,cmp ax,bx 即(8 - 3)=5。
5的二进制表示:00000101 其中有2个1,也就是说1的出现次数是偶数,所以PF=1。
通过cmp(几乎每次都打成cmd)指令执行后,相关标志位的值可以看出比较结果。
CPU在执行cmp指令的时候,包含两种含义:进行无符号数运算和进行有符号数运算。
关于比较计算与补码的关系:
mov ah,0a0h
mov bh,0cbh
cmp ah,bh 分析如下:
AH=0A0H = -96(十进制)
BH=0CBH = -53(十进制)
CMP AH,BH 相当于减法计算:
(AH)=(-96)-(-53)
=(-96)+53
=-43 (十进制)
得到了-43,现在求有符号数表示(-43的补码):
|-43| 绝对值为: 43
43=0010 1011 (二进制表示)
=1101 0100 (将它求反)
=1101 0100 + 1 (加1,防止重码)
=1101 0101 (结果)
=D5H (16进制结果:就是-43的补码)
所以,结果是负数,SF符号位标置为1(负数)。
过程
Quote: | C:\Masm50>debug
-a
0AF5:0100 mov ah,a0
0AF5:0102 mov bh,cb
0AF5:0104 cmp ah,bh
0AF5:0106
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B4A0 MOV AH,A0
-t
AX=A000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0102 NV UP EI PL NZ NA PO NC
0AF5:0102 B7CB MOV BH,CB
-t
AX=A000 BX=CB00 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0104 NV UP EI PL NZ NA PO NC
0AF5:0104 38FC CMP AH,BH
-t
AX=A000 BX=CB00 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI NG NZ AC PO CY |
|
检测比较结果的条件转移指令
大多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改IP。
被cmp指令影响的那些标置位,这些转移指令通常都和cmp相配合使用。
条件转移指令:
je、jne、jb、jnb、ja、jna
上面条件转移指令所检测的标志位,都是cmp指令进行无符号数比较时候,记录比较结果的标志位。
例: je (等于),检测 ZF 位,当ZF=1的时候进行转移。如果在je前面使用了cmp指令,则je对ZF的检测实际上就是间接地检测cmp的比较结果是否为两数相等。
因为 cmp 比较实际上就是两个操作数相减,如果两个操作数值一样,自然相减为0,ZF(是否为0标置位)=1,代表为0。而 je 判断 ZF 标置位,所以就检测到了是相等。
je 检测的是ZF标置,不管 je 前面是什么指令,只要CPU执行JE指令时,ZF=1,那么就发生转移。
cmp ah,bh
je s 如果 ah与bh相等,则 cmp 会将 ZF的值置1,所以后面的 JE 指令判断 ZF=1,则跳转到指定位置。
mov ax,0
add ax,0
je s 虽然没有了 cmp 与 je 的配合,但是 add ax,0 就相当(AX)=(AX)+0 。ax=0,那么ax=ax+0还是0 ,结果为0所以 add 指令将ZF=1。当 je 指令执行时,它判断ZF=1的标置,如果发现ZF=1,则认为相等,进行跳转。
(即,不管是什么指令让CF=1的,只要je判断CF=1,就一定发生跳转)
很多种方法都可以让ZF=1,所以标置寄存器与cmp的原理必须掌握。对这些原理的理解有多少,就意味着对指令深度的理解和灵活开发。
CPU提供了cmp指令,也提供了je等条件转移指令。将它们配合使用,可以实现根据比较结果进行转移的功能。
对于je、jne、jb、jnb、ja、jna等指令和cmp指令配合的使用思和je相同。
例: 在 data 段中统计 8 出现的次数
assume cs:code
data segment
db 8,11,8,1,8,5,63,38
data ends
code segment
start: mov ax,data
mov ds,ax
mov bx,0
mov ax,0
mov cx,8
s: cmp byte ptr [bx],8
jne next ; 不等于8 ,则转到 next
inc ax ; 否则(如果相等)就将计数值加1
next: inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start 出现了3次8。
Quote: | -g 0017
AX=0003 BX=0008 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=0017 NV UP EI PL NZ NA PO NC |
|
例:统计 data 段中数值大于8的字节的个数,用 AX 保存统计结果。
assume cs:code
data segment
db 8,11,8,1,8,5,63,38
data ends
code segment
start: mov ax,data
mov ds,ax
mov ax,0
mov bx,0
mov cx,8
s: cmp byte ptr [bx],8
jna next ; 如果不大于8转到next,续继循环
inc ax
next: inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start 例:统计 data 段中数值小于8的字节个数,用 ax 保存统计结果。
assume cs:code
data segment
db 8,11,8,1,8,5,63,38
data ends
code segment
start: mov ax,data
mov ds,ax
mov ax,0
mov bx,0
mov cx,8
s: cmp byte ptr [bx],8
jnb next ; 如果不小于8,则转到next继续循环
inc ax ; 否则(就是大小8)计数器加1
next: inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start [ Last edited by redtek on 2007-1-7 at 08:19 PM ]
|
Redtek,一个永远在网上流浪的人……
_.,-*~'`^`'~*-,.__.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._,_.,-*~'`^`'~*-,._ |
|
2007-1-7 23:01 |
|
|