汇编基础
assume cs:code,ds:data,ss:stackdata 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 有人看没人回呀,看不起汇编怎么滴哟,
还是感觉它太简单了?
我正在学/:07 好语言这可要好好学下 支持一下,希望能发布更多好帖。 很不错的东西,注释很详细,收藏下来了 这东西太难了!
学了几天,后有搁置了! 呵呵.下了先好东西哦 感谢楼主 为我们这些新手提供了很值得学习的东西 学习一下
谢谢 楼主发的一点都不基础哇
页:
[1]
2