中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net  论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

游客:  注册 | 登录 | 命令行 | 会员 | 搜索 | 上传 | 帮助 »
中国DOS联盟论坛 » 网络日志(Blog) » 【Redtek】 个人网志(学习笔记)
« [1] [2] [3] [4] [5] [6] [7] [8] [9] »
作者:
标题: 【Redtek】 个人网志(学习笔记) 上一主题 | 下一主题
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
状态 离线
『第 82 楼』:  

对我来说有如看天书啊。

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
查看资料  发短消息 网志  OICQ (329429)  编辑帖子  回复  引用回复
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
查看资料  发短消息 网志  OICQ (329429)  编辑帖子  回复  引用回复
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时的状态:

  Quote:
NV UP EI PL NZ NA PO NC

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
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
« [1] [2] [3] [4] [5] [6] [7] [8] [9] »
请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: