mov ds, ax mov si, 0 ;偏移地址 mov cx, si ;计数(循环次数) xor ax, ax ;空格计数器清零 again: cmp byte ptr [si], 20h ;与空格的ASCII码比较 jne next ;不是空格,转 inc ax ;是空格,空格数加1 next: inc si ;修改地址指针 loop again ;cx=cx-1,如cx=0 退出循环 .exit 0 end start
3.24 编写计算100个16位正整数之和的程序。如果和不超过16位字的范围(65535),则保存其和到wordsum,如超过则显示‘overflow’。 答: ;数据段 count equ 100 parray dw count dup(?) ;假设有100个数据 wordsum dw 0 msg db ‘overflow’,’$’ ;代码段 mov cx,count mov ax,0 mov bx,offset parray again: add ax,[bx] jnc next mov dx,offset msg mov ah,9 int 21h ;显示溢出信息 jmp done ;然后,跳出循环体 next: add bx,2 loop again mov wordsum,ax done: ?
3.25 编程把—个16位无符号二进制数转换成为用8421BCD码表示的5位十进制数。转换算法可以是:用二进制数除以10000,商为“万位”,再用余数除以1000,得到“千位”;依次用余数除以l00、10和l,得到“百位”、“十位”和“个位”。 ;wjxt325.asm .model small .stack 256 .data array dw ? ;源字数据 dbcd db 5 dup(?) ;五位bcd结果,高对高低对低 .code .startup mov dx, array ;取源数据(余数) mov bx, 10000 ;除数 mov cx, 10 ;除数系数
mov si, 4 ;目的数据高位位移量 again: mov ax, dx ;dx.ax中存放被除数 mov dx, 0 div bx ;除于bx,商ax,余数dx mov dbcd[si], al ;商<10,存结果 push dx ;暂存余数 mov ax, bx ;除数除于10 mov dx,0 div cx ;dx.ax除于cx,商ax、余数0存在dx mov bx, ax ;bx是除数 pop dx dec si ;目的数据位移量减1 jnz again mov dbcd, dl ;存个位数( < 10 ) .exit 0 end 3.26 解:
(1)汇编语言中,子程序要用一对过程伪指令PROC和ENDP声明,格式如下: 过程名 PROC [NEAR|FAR]
?? ;过程体 过程名 ENDP
(2)保护用到的寄存器内容,以便子程序返回时进行相应的恢复。 (3)改错: crazy proc
pish bx push cx xor ax,ax xor dx,dx again:add a,[bx] adc dx,0 inc bx inc bx
loop again pop cx pop bx
3.27 编写一个源程序,在键盘上按一个键,将从AL返回的ASCⅡ码值显示出来,如果按下ESC键则程序退出。
解(不需调用HTOASC子程序): again: mov ah,1 int 21h cmp al,1bh ;ESC的ASCII码是1bh je done mov dl,al mov ah,2 int 21h ;是大写字母则转换为小写字母 jmp again done: ?
3.28
解答:
asctob proc push cx and dh,0fh ;先转换十位数 shl dh,1 ;十位数乘以10(采用移位指令) mov ch,dh shl dh,1 shl dh,1 add dh,ch and dl,0fh ;转换个位数 add dh,dl ;十位数加个位数 mov al,dh ;设置出口参数 pop cx ret asctob endp
3.29 调用HTOASC子程序,显示一个字节的16进制数,后跟“H”的子程序。
解: DIPASC proc ;入口参数:AL=要显示的一个16进制数 push cx push dx push ax mov cl,4 ;转换高位 shr al,cl call HTOASC mov dl,al ;显示 mov ah,2 int 21h pop ax ;转换低位 call HTOASC mov dl,al ;显示 mov ah,2 int 21h mov dl,’H’ ;显示一个字母“H” mov ah,2 int 21h pop dx pop cx ret DIPASC endp HTOASC proc ;将AL低4位表达的一位16进制数转换为ASCII码 and al,0fh cmp al,9 jbe htoasc1 add al,37h ;是0AH~0FH,加37H转换为ASCII码 ret ;子程序返回 htoasc1: add al,30h ;是0~9,加30H转换为ASCII码
ret ;子程序返回 HTOASC endp
3.30写一个子程序,根据入口参数AL=0、1、2,依次实现对大写字母转换成小写、小写转换成大写或大小字母互换。欲转换的字符串在string中,用0表示结束
解: lucase case0: next0: case1: next1: case2: next20: next2: done: proc push bx mov bx,offset string cmp al,0 je case0 cmp al,1 jz case1 cmp al,2 jz case2 jmp done cmp byte ptr [bx],0 je done cmp byte ptr [bx],’A’ jb next0 cmp byte ptr [bx],’Z’ ja next0 add byte ptr [bx],20h inc bx jmp case0 cmp byte ptr [bx],0 je done cmp byte ptr [bx],’a’ jb next1 cmp byte ptr [bx],’z’ ja next1 sub byte ptr [bx],20h inc bx jmp case1 cmp byte ptr [bx],0 je done cmp byte ptr [bx],’A’ jb next2 cmp byte ptr [bx],’Z’ ja next20 add byte ptr [bx],20h jmp next2
cmp byte ptr [bx],’a’ jb next2 cmp byte ptr [bx],’z’ ja next2 sub byte ptr [bx],20h inc bx jmp case2 pop bx
ret lucase endp
3.31子程序的参数传递有哪些方法?请简单比较
解:
(1)用寄存器传递参数:
最简单和常用的参数传递方法是通过寄存器,只要把参数存于约定的寄存器中就可以了 由于通用寄存器个数有限,这种方法对少量数据可以直接传递数值,而对大量数据只能传递地址
采用寄存器传递参数,注意带有出口参数的寄存器不能保护和恢复,带有入口参数的寄存器可以保护、也可以不保护,但最好能够保持一致 (2)用共享变量传递参数
子程序和主程序使用同一个变量名存取数据就是利用共享变量(全局变量)进行参数传递
如果变量定义和使用不在同一个源程序中,需要利用PUBLIC、EXTREN声明 如果主程序还要利用原来的变量值,则需要保护和恢复
利用共享变量传递参数,子程序的通用性较差,但特别适合在多个程序段间、尤其在不同的程序模块间传递数据 (3)用堆栈传递参数
参数传递还可以通过堆栈这个临时存储区。主程序将入口参数压入堆栈,子程序从堆栈中取出参数;子程序将出口参数压入堆栈,主程序弹出堆栈取得它们 采用堆栈传递参数是程式化的,它是编译程序处理参数传递、以及汇编语言与高级语言混合编程时的常规方法
3.32采用堆栈传递参数的一般方法是什么?为什么应该特别注意堆栈平衡问题。
解:
方法:主程序将入口参数压入堆栈,子程序从堆栈中取出参数;子程序将出口参数压入堆栈,主程序弹出堆栈取得它们
注意:压栈与弹栈必须要一一对应。
3.33 编写一个求32位数据补码的子程序,通过寄存器传递入口参数 解: 方法1: neg32 proc ;入口参数:DX.AX=32位有符号数 neg ax ;实现0-DX.AX功能 neg dx sbb dx,0 ;这条指令也可以用dec dx代替 ret neg32 endp ;出口参数:DX.AX=32位有符号数的补码 方法2: neg32 proc ;入口参数:DX.AX=32位有符号数 not ax ;实现DX.AX求反加1 not dx add ax,1 adc dx,0 ret neg32 endp ;出口参数:DX.AX=32位有符号数的补码