目录
题目1、将BUF开始的10个单元中的二进制数转换成两位十六进制数的ASCII码,在屏幕上显示出来。要求码型转换通过子程序HEXAC实现,在转换过程中,通过子程序DISP实现显示。
题目3、请编写一个程序来实现简单的程序菜单显示。(采用多模块程序设计方法)
题目1、将BUF开始的10个单元中的二进制数转换成两位十六进制数的ASCII码,在屏幕上显示出来。要求码型转换通过子程序HEXAC实现,在转换过程中,通过子程序DISP实现显示。
实现程序如下所示:
;将二进制数转换为两位十六进制数,则每个二进制数最多8位
;使用了子程序的近程调用
assume cs:code,ds: data
data segment
;二进制最高位FF,即255
buf db 00010001b,00100010b,00110011b,01000100b,01010101b
db 10111011b,11001100b,11011101b,11101110b,11111111b
buf_size equ $ - buf
output db 'Array: $'
data ends
code segment
;一般来说,主过程应当定义为far属性,这是由于八程序的主过程看作dos调用的一个子过程
;因而dos对main的调用以及main中的ret就是far属性的
main proc far
start:
mov ax,data
mov ds,ax
mov cx,buf_size
mov si,0
lea dx,output
mov ah,09h
int 21h
lop: call hexac
inc si
loop lop
jmp exit
hexac proc near
;将二进制数转化为十六进制对应的ASCII码,其每位十六进制必然为数字或A-F
push cx
mov ax,0
mov al,[buf+si]
mov cl,4
shl ax,cl
shr al,cl ;ah,al分别存储高四位和底四位的数字
cmp al,9h
ja low_char
jbe low_digit
highTrans:
cmp ah,9h
ja high_char
jbe high_digit
low_digit:
add al,30h
jmp highTrans
low_char:
add al,37h
jmp highTrans
high_digit:
add ah,30h
call disp
jmp return
high_char:
add ah,37h
call disp
return:
pop cx
ret
hexac endp
disp proc near
;输出两个字符
mov dl,ah
call outputchar
mov dl,al
call outputchar
mov dl,' '
call outputchar
ret
outputchar:
mov ah,02h
int 21h
ret
exit:
mov ax,4c00h
int 21h
disp endp
main endp
code ends
end start
该程序将10个二进制数据:
00010001b,00100010b,00110011b,01000100b,01010101b
10111011b,11001100b,11011101b,11101110b,11111111b
输出,运行结果如下所示:
|
题目2、编写一个主程序,从键盘接收若干个字符,然后用远调用的方法,调用子程序统计字符串中小写字符’x’的个数.子程序的参数是字符串的首地址TABLE,字符串长度N及字符”x”.子程序返回字符"x”的个数.参数传送采用堆栈实现.主程序在子程序返回后,显示字符”x”及其个数(设为一位十六进制数)。
子程序的远调用,统计字符串中小写字符‘x’的个数
实现程序如下所示:
;从键盘接收若干个字符,使用远程调用,统计字符串中小写字符x的个数
assume cs:code,ds: data,es:data
data segment
input db 'Enter your string:$'
table db 50h,100 dup(?)
count db ?
output db 0ah,'The num of x:$'
data ends
code segment
start:
mov ax,data
mov ds,ax
mov es,ax
lea dx,input
mov ah,09h
int 21h
lea dx,table
mov ah,0ah
int 21h
push dx ;传入字符串的首地址
mov dl,[table+1]
push dx ;传入参数字符串长度
mov dx,'x'
push dx ;传入要统计的字符'x'
call far ptr getX ;远程调用push cx,push ip
pop dx
mov count,dl
lea dx,output
mov ah,09h
int 21h
mov dl,count
add dl,30h
mov ah,02h
int 21h
jmp exit
exit:
mov ax,4c00h
int 21h
code ends
code2 segment
getX proc far
pop bx ;call指令自动调用push cs和ip堆栈中保留了返回地址的段地址和偏移地址
pop si ;先得到cs和ip的值,其中si保存cs的值,bx保存ip的值
pop ax ;获取参数'x'
pop cx ;获取参数字符串长度
pop di ;获取参数字符串的首地址
add di,2
;将偏移地址压入堆栈中
mov dx,0
cld
scan proc far
repne scasb ;每次di和al相等时退出,使用dx计数
inc dx
cmp cx,0 ;xxaxxb
je check
ja scan
check proc far
cmp al,[di-1]
je return ;判断最后一个字符是否为x,如果不是则dx-1
dec dx
check endp
return proc far
push dx
push si ;push cs
push bx ;push ip
return endp
ret ;ret指令自动调用pop ip pop cs
scan endp
getX endp
code2 ends
end start
使用debug指令查看远程调用时堆栈段的变化情况,如下所示:
可以观察到远程调用时,将下一条要执行的指令CS = 0775,IP = 0026压入堆栈中 Push(CS),Push(IP) 查看堆栈段的数据可以观察到调用子程序的下一条指令的CS和IP值 |
运行程序的结果如下所示,与预期结果一致。
|
题目三、请编写一个程序来实现简单的程序菜单显示。(采用多模块程序设计方法)
显示一个菜单要求用户从下表中选择:
(1) HEXAC GAME (对应-->程序1)
(2) CHAR STATISTICS PROGRAM (对应-->程序2)
(3) PRESS “ESC” TO QUIT
用户作出选择1~3时,显示输出要执行操作的名字,如“Your selection is 1,the program will execute HEXAC NUMBER GAME!”然后转向相应的程序去执行,执行结束前进行退出的判断,显示一个提示信息“will you continue program really?(Y:N)”要求用户从键盘键入字符,若是“Y”键,则继续程序的执行,若是“N”键,则返回到菜单。若用户选择3,要求程序显示提示信息“Please press ESC key to exit!”,并能按要求正确退出。
文件1(主程序)程序如下所示:、
;从键盘接收若干个字符,使用远程调用,统计字符串中小写字符x的个数
; source module1
;引入far 模块
extrn game1:far,game2:far
assume cs:code,ds: data,es:data
data segment
continue db 0dh,0ah,'will you continue progrqam really?(Y:N):$'
option1 db 0dh,0ah,'Your selection is 1,the program will execute HEXAC NUMBER GAME!',0dh,0ah,'$'
option2 db 0dh,0ah,'Your selection is 2,the program will execute CHAR STATISTICS PROGRAM!',0dh,0ah,'$'
option3 db 0dh,0ah,'Please press ESC key to exit:$'
menu db 0dh,0ah,'1: HEXAC NUMBER GAME',0dh,0ah
db '2: CHAR STATISTICS PROGRAM',0dh,0ah
db '3: PRESS "ESC" TO QUIT',0dh,0ah
db 'Please input your option:',0dh,0ah,'$'
bye db 0dh,0ah,'GoodBye!',0dh,0ah,'$'
data ends
code segment
main proc far
start:
mov ax,data
mov ds,ax
mov es,ax
showmenu:
lea dx,menu
mov ah,09h
int 21h
mov ah,01h
int 21h
cmp al,'1'
je routine_1
jb exit
cmp al,'2'
je routine_2
cmp al,'3'
je routine_3
ja exit
routine_1:
lea dx,option1
call output_string
push ds
call far ptr game1 ;远程调用前往其它模块
pop ds ;需要恢复数据段
call _continue
jmp routine_1
routine_2:
lea dx,option2
call output_string
push ds ;由于后续程序需要使用到es,保存es的值
call far ptr game2
pop ds ;恢复数据段
call _continue
jmp routine_2
routine_3:
lea dx,option3
call output_string
mov ah,01h
int 21h
cmp al,1Bh ;1Bh即esc的ascii码值
je exit
jne routine_3
_continue proc near
lea dx,continue
call output_string
mov ah,01h
int 21h
cmp al,'Y' ;59H
je con_return
cmp al,'N' ;5AH
je showmenu
jne _continue ;输入不符合Y/N,则重新输入
con_return:
ret
_continue endp
output_string proc near
mov ah,09h
int 21h
ret
output_string endp
exit:
lea dx,bye
call output_string
mov ax,4c00h
int 21h
main endp
code ends
end start
文件二(子程序):
;source module2
assume cs:code,ds:data
public game1,game2
data segment
;----------------------------game1-----------------------------
buf db 00010001b,00100010b,00110011b,01000100b,01010101b
db 10111011b,11001100b,11011101b,11101110b,11111111b
buf_size equ $ - buf
output_1 db 'Array: $'
;----------------------------game2-----------------------------
input db 'Enter your string:$'
table db 50h,100 dup(?)
count db ?
output_2 db 0ah,'The num of x:$'
data ends
code segment
game1 proc far
mov ax,data
mov ds,ax
mov cx,buf_size
mov si,0
lea dx,output_1
mov ah,09h
int 21h
lop: call hexac
inc si
loop lop
ret ;回到原过程
hexac proc near
;多模块调用,data和code
;需要将cs和ip保存起来
push cx
mov ax,0
mov al,[buf+si]
mov cl,4
shl ax,cl
shr al,cl ;ah,al分别存储高四位和底四位的数字
cmp al,9h
ja low_char
jbe low_digit
highTrans:
cmp ah,9h
ja high_char
jbe high_digit
low_digit:
add al,30h
jmp highTrans
low_char:
add al,37h
jmp highTrans
high_digit:
add ah,30h
call disp
jmp return1
high_char:
add ah,37h
call disp
return1:
pop cx
ret
hexac endp
disp proc near
;输出两个字符
mov dl,ah
call outputchar
mov dl,al
call outputchar
mov dl,' '
call outputchar
ret
outputchar:
mov ah,02h
int 21h
ret
disp endp
mov ax,4c00h
int 21h
game1 endp
game2 proc far
mov ax,data
mov ds,ax
mov es,ax
lea dx,input
mov ah,09h
int 21h
lea dx,table
mov ah,0ah
int 21h
push dx ;传入字符串的首地址
mov dl,[table+1]
push dx ;传入参数字符串长度
mov dx,'x'
push dx ;传入要统计的字符'x'
call getX ;远程调用push cx,push ip
pop dx
mov count,dl
lea dx,output_2
mov ah,09h
int 21h
mov dl,count
add dl,30h
mov ah,02h
int 21h
ret
getX proc near
pop bx ;段内近调用,保存ip值
pop ax ;获取参数'x'
pop cx ;获取参数字符串长度
pop di ;获取参数字符串的首地址
add di,2
;将偏移地址压入堆栈中
mov dx,0
cld
scan:
repne scasb ;每次di和al相等时退出,使用dx计数
inc dx
cmp cx,0
je check
ja scan
check:
cmp al,[di-1]
je return2 ;判断最后一个字符是否为x,如果不是则dx-1
dec dx
return2:
push dx
push bx ;push ip,恢复ip值
ret ;ret指令自动调用pop ip pop cs
getX endp
game2 endp
code ends
end
注意:
在链接时,需要将两个模块的目标文件链接在一起:
|
运行结果如下所示:
功能1如下图所示:输入Y,程序继续运行,输入N,停止程序,返回菜单,输入其它字符,则提示继续输入。 功能2实现如下所示,统计x的字符个数 功能3实现如下所示,点击esc键退出程序,否则提示继续输入 |
使用debug调试不同模块之间的子程序调用,如下所示:
进行多模块调用之前,CS:IP的值为077F:002B,并且使用U指令可以观察到下一条执行的指令IP的值为30,执行调用之后观察到CS:IP的值为0792:002B,使用D指令查看堆栈段的数据可以观察到主程序的CS和IP的值 |