assume cs:code,ds:data,ss:stacks
;统计字符串长度
stacks segment ;手动开辟一个内存,默认是自动的
db 16 dup(0) ;申请了16字节空间大小
stacks ends
data segment
strmessage db 'abcdefg',0,24h
len dw ?,0,24h
data ends
code segment
start:
;存储堆栈
mov ax,stacks
mov ss,ax
mov sp,10h ;SP堆栈指针寄存器,存放堆栈的偏移地址,这里的10h对应的是十进制的16,
; 存储数据
mov ax,data
mov ds,ax
; 获取strmessage的偏移地址
mov si,offset strmessage
push ds
push si
call strlen ; 子程序调用;CALL指令段内调用,会将将指令指针IP压入堆栈
add sp,4h
; 将16进制结果按照ASCII码输出
lea si,len
add ax,30h
mov ds:[si],ax
lea dx,len
; 在这里需要输出一个回车,不然会有异常符号显示
call enterkey
; 调用9号功能输出
mov ax,0900h
int 21h
mov ax,4c00h
int 21h
; 回车输出
enterkey proc
push ax
push dx
mov dl,0dh
mov ah,02h
int 21h
pop dx
pop ax
ret
enterkey endp
;入口参数:堆栈中字符串的起始位置和偏移地址
;出口参数:AX
strlen proc
push bp
mov bp,sp ;利用bp基址指针寄存器在堆栈中寻址
;保护寄存器
push ds
push si
;取入口参数,根据堆栈图可以得到以下偏移
mov ds,ss:[bp+6]
mov si,ss:[bp+4]
;进行比较
mov al,0
strlen1:
cmp ds:[si],al
jz strlen2
inc si
jmp strlen1
strlen2:
mov ax,si
sub ax,ss:[bp+4]
;恢复寄存器,堆栈平衡
pop si
pop ds
pop bp
ret
strlen endp
code ends
end start
从call指令进到堆栈的时候,要画一下堆栈图
堆栈遵循先进后出的规则,最底下的DS是第一条push DS的值,将DS压入堆栈,以下的push以此类推,
在获取取入口参数的地方,因为默认段前缀是ds,但是我们其实是要在堆栈区获取参数,就要声明为ss堆栈寄存器,否则获取的就还是ds自己的数据
最后在POP完之后,还剩下一个最开始的的DS和和SI,要修正一下sp指针的值,可以用add sp,4h,或者ret 4,都可以恢复堆栈平衡