fanhao 发表于 2008-4-21 02:56:11

汇编基础

assume cs:code,ds:data,ss:stack

data segment
c dw 0            ;计数器,存储X中11B的个数
x   dw 0ccff   ;待计算11B的个数的参数
data ends

stack segment
dw 64 dup(0)
stack ends

code segment
start:
    mov ax,data
    mov ds,ax   ;初始化数据段
   
    mov ax,stack    ;初始化堆栈段
    mov ss,ax
    mov sp,128
   
    call Count         ;调用子程序计算11B的个数
   
    mov ax,4c00h   ;返回
    int 21h


Count proc near   
    push ax    ;保护在子程序中要使用的寄存器
    push bx
    push cx
   
    mov c,0    ;初始化计数器为0
    mov cx,8    ;设置循环次数为8,因一个字单元可分为8个“四分之一字节”
    mov bx,c000h;0c00h=1100 0000 0000 0000b,即从左到右始每次测试二位,直到bx移至为0
s:
    mov ax,x          ;(AX)=X
    and ax,bx         ;测试AX中的某二位是否为11B
    cmp ax,bx   ;比较是否为11B
    jz counter   ;如果为11B,跳转到counter处使计数器C加1
    jmp s1       ;比较不为11B,直接跳过inc c
counter:
    inc c
s1:         
    push cx             ;由于使用cl作移动位数寄存器,因此先保存一下
    mov cl,2    ;设置向右移动两位
    shr bx,cl      ;(BX)向右移动两位,即如果(BX)=1100 0000 0000 0000B,则移动后为(BX)=0011 0000 0000 0000B
    pop cx     ;恢复CX内容

    loop s                ;如果(CX)=(CX)-1不为0,则返回开始循环处

    pop cx     ;恢复各个寄存器
    pop bx
    pop ax
   
    ret                     ;返回函数调用处,严格来说这个算不上是真正的函数,但为了好听点,就叫它函数吧
Count endp

code ends
end start
代码:
assume cs:code,ds:data,ss:stack

data segment
cdw 0                                        ;计数器,存储字符串中“MOON”的个数
str db 'MOONMOONMOONF','$'    ;待求字符串,传说中的数组,不过这里是以'$'结束的
data ends

stack segment
dw 16 dup(0)               
stack ends

code segment
start:
   mov ax,data                  ;初始化数据段和堆栈
   mov ds,ax

   mov ax,stack
   mov ss,ax

   mov cx,0                        ;初始化计数器为0
   call Count        ;调用子程序计算字符串中“MOON”的个数,结果存储在C中

   mov ah,4ch       ;返回
   int 21h

Count proc near
    push si                        ;保护一下SI的内容,因下面要使用到它

    mov si,0                        ;初始化数组下标SI为0
s2:
    cmp byteptr str,'$'       ;以下连续比较四个字符中是否有'$',因为后面0~4个字符之中如果有'$',则不必再检测是为为四个字符为“MOON”,经我反汇编查看了一下WIN-TC输出的汇编源码,我发现我写的这个检测方法要比它编译的稍微好一点点.如果没检测到’$',则继续往下检测这四个字符是否为“MOON”,否则跳到S3处结束循环
    jz s3
    cmp byte ptr str,'$'
    jz s3
    cmp byte ptr str,'$'
    jz s3
    cmp byte ptr str,'$'
    jz s3

    cmp byte ptr str,'M'   ;比较字符串str中是当前下标指向的及后面的字符是否为“MOON”,如果Y,则继续往下执行,否则返回S2处执行
    jnz s2
    cmp byte ptr str,'O'
    jnz s2
    cmp byte ptr str,'O'
    jnz s2
    cmp byte ptr str,'N'
    jnz s2

    inc c             ;如果运行到这里,则说明在str中检测到了一个"MOON"
    add si,4            ;(SI)=(SI)+4,指针指向字符串后四个字符的开始处
    jmp s2                                 ;进入下一轮检测

s3:
   pop si                                    ;恢复SI
   
    ret                                          ;返回函数调用处
Count endp

code ends
end start

这个程由于我使用了优化后的算法,因此算法的时间复杂度由原来的o(n^2)变为了o(n)。大概思想如下:

代码:
void DeleteAllZero(int iarray[],int len)
{
   int i,p;
   for(i=p=0;i<len-1;i++){
       if(iarray!=0){
         p++;
       }
      else{
         if(iarray!=0){
               iarray=iarray;
               iarray=0;
               p++;
            }
      }
   }

}

只需要存当前扫描到的第一个0的位置p即可。每次扫到一个不为0的数就移到p,并将p++;扫到一个0的话p不变,继续扫下一个数。
以下为完整的程序,可以在程序中看到上面优化后的循环的影子。


代码:
assume cs:code,ds:data,ss:stack

data segment
iarray dw 1,0,2,0,3,0,4,0,5,0            ;100个字型元素,好多啊,为了方便,我使用了小且顺序规律的元素
      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0

      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0
      dw 1,0,2,0,3,0,4,0,5,0

len dw 100                                          ;数组长度
data ends

stack segment
dw 128 dup(0)
stack ends

code segment
start:
   mov ax,data                            ;初始化各段寄存器
   mov ds,ax

   mov ax,stack
   mov ss,ax
   mov sp,256

   call DelAllZero                           ;调用子程序对数组进行“删除”和”压缩“处理

   mov ah,4ch
   int 21h

DelAllZero proc near
    push ax                                    ;保护各个使用到的寄存器
    push bx
    push si
    push di

    mov si,0                                    ;SI存储当前数组中第一个为0的位置,这里的第一个并不仅仅指数组开始时的,它还指删除一个或几个0后的数组时的第一个为0的位置
    mov di,0                                    ;DI用来遍历数组中的每一个元素
    mov ax, len                              ;AX表示数组的长度,这个函数有两个数组:数组首地址和数组长度
    add ax,len                                  ;(AX)=(AX)+LEN,之所以要再加一次LEN,因为数组元素为字单元,每次下标SI要加2才能取下一个元素,因为下标SI和AX的比较结果为循环退出的条件。为了方便,决定要将AX内容加倍
    sub ax,2                                     ;AX内容减2,表示SI只要扫描到数组倒数第二个元素即可
s4:
    cmp si,ax            ;(SI)>=(AX)时表示检测完毕,应该退出循环
    jnc exit1                                    ;不再满足条件时退出循环
    cmp word ptr iarray,0            ;检测SI指向的元素是否为0,如果为0则SI加2检测下一个元素
    jz s5

    add si,2                                    ;SI加2指向下一个元素
    add di,2                                    ;DI加2指向下一个元素,直到指向首个为0的元素
    jmp s4
s5:
    add si,2             ;移动SI检测下一个元素
    cmp word ptr iarray,0     ;如果非0,则将该元素移至DI指向的位置,覆盖前面为0的元素
    jz s4              ;为0,返回继续检测

    mov bx,iarray        ;将SI指向的元素移至DI指向的位置,即覆盖前面的0
    mov iarray,bx         
    mov word ptr iarray,0    ;移动之后将SI指向的元素清除为0
    add di,2                                       ;DI加2为指向被覆盖后的元素的后一个位置
    jmp s4               ;返回继续检测

exit1:
    pop di                                             ;恢复各个寄存器
    pop si
    pop bx
    pop ax
    ret                 ;返回
DelAllZero endp

code ends
end start

echo 发表于 2008-4-22 08:21:40

有人看没人回呀,看不起汇编怎么滴哟,
还是感觉它太简单了?
我正在学/:07

wjxw 发表于 2008-4-22 09:54:39

好语言这可要好好学下

xingke 发表于 2008-4-22 15:09:46

支持一下,希望能发布更多好帖。

风影子 发表于 2008-4-22 22:48:42

很不错的东西,注释很详细,收藏下来了

zhangck 发表于 2008-4-24 14:40:04

这东西太难了!

学了几天,后有搁置了!

lifengfengdeai 发表于 2008-4-24 19:21:22

呵呵.下了先好东西哦

arice 发表于 2008-4-25 14:59:18

感谢楼主 为我们这些新手提供了很值得学习的东西

jcly 发表于 2008-4-25 18:44:05

学习一下
谢谢

小子贼野 发表于 2008-4-25 20:35:46

楼主发的一点都不基础哇
页: [1] 2
查看完整版本: 汇编基础