目录
1.实验目的及内容
理解子程序结构的特点,熟悉子程序参数传递的方法,掌握子程序的编写。
2、实验内容
2.1 二进制输入输出子程序
(1) 二进制输入子程序
编写二进制输入子程序、以及验证子程序的主程序,并运行正确。
代码思路:(1)利用字符输入子程序readc输入一个字符,判断是否是0或1,若合法则直接减去30h转化为数值。
(2) 重复转换字符的同时,需要将前面一次得到的总数值左移一位,并与刚得到的新数值进行相加。
(3) 如果输入了非’‘0’‘或非’‘1’'数值或者数据位数超出边界,则转至报错提示。
include io32.inc
.data
count = 5 ;预留五个空间
array dword count dup(0) ;声明数组
temp dword ? ;工具变量
.code
start:
mov ecx, count
mov ebx, offset array ;初始化数组指针
again: call rdbd ;调用子程序,从键盘读一个数据
mov eax,temp ;获得出口参数
mov [ebx],eax ;存放到输入缓冲区
call dispbd
add ebx,4 ;数组指针移动
loop again
exit 0
rdbd proc ;二进制输入子程序、
push eax ;出口参数,共享工具变量temp
push ebx
push ecx
rdbd1: xor ebx,ebx ;ebx用于存放二进制结果
mov ecx,32 ;限制输入字符的个数到32个
rdbd2: call readc ;输入一个字符
cmp al,'0' ;比0小,转至报错
jb rderr
cmp al,'1' ;比1大,转至报错
ja rderr
sub al,'0' ;获得真正的数据
shl ebx,1 ;前边输入后转化为二进制的数据左移一位再相加
or bl,al ;bl和al相加
loop rdbd2 ;循环键入字符
mov temp,ebx ;把ebx的二进制结果存放于temp返回
call dispcrlf
pop ecx
pop ebx
pop eax
ret
rderr: mov eax,offset errmsg ;显示错误信息
call dispmsg
jmp rdbd1
errmsg byte 0dh,0ah,'Inpput error,enter again:',0
rdbd endp ;子程序结束
end start
运行结果:
(2) 二进制显示子程序
编写二进制显示子程序、以及验证子程序的主程序(教材习题5.8),并运行正确。
代码思路:将得到的二进制数每次右移一位,通过判断CF是否为1来分别处理,处理时加30H转化为ACSII码,后将每次得到的ACSII码入栈,此时在原数据中每个位在栈中就是低位到高位了,输出的时候依次从栈中弹出就是正确的顺序。
include io32.inc
.data
array dword 10110101b,1010000101b,0001000100001000101b,1001010101010b
writebuf byte 12 dup (0) ;显示缓冲区
.code
start:
mov ecx, lengthof array ;数组长度计数器
mov ebx, 0 ;初始化数组指针
again: mov eax, array[ebx*4] ;逐个取出字符数组中元素
call write
call dispcrlf
inc ebx ;数组指针移动
loop again
exit 0
;子程序部分
write proc
push ebx ;数组指针先入栈
push ecx ;计数器再入栈
push edx ;存放每个留在缓冲区的二进制数的edx入栈
mov ebx, offset writebuf ;字符内部数组首指针,指向显示缓冲区
write1: mov ecx,32 ;每个二进制数最多表示到32位
push ecx ;结束标志入栈
mov edx,0
write2: cmp eax,0 ;移位结束标志
jz write3 ;所有有效数字位已经右移完毕
shr eax,1 ;每次将最低位移出来到CF
jnc write_0 ;若最低位为0
jmp write_1
write_0: mov edx,0
add edx,30h ;加30h转换为ACSII码
push edx ;该字符入栈
jmp write2
write_1: mov edx,1
add edx,30h ;加30h转换为ACSII码
push edx ;该字符入栈
jmp write2
write3: pop edx ;对该数从高位到低位依次出栈
cmp edx,ecx ;出栈结束判断
jz write4
mov [ebx],dl ;将栈中弹出的值放入数组
inc ebx ;数组指针移动
jmp write3
write4: mov byte ptr[ebx],0 ;给显示内容加上结尾标志
mov eax,offset writebuf ;打印缓冲区内容
call dispmsg
pop edx
pop ecx
pop ebx
ret
write endp
end start
运行结果:
2.2 主存区域数据显示子程序
编写逐个字节显示主存区域数据的子程序和主程序(教材习题5.13),并运行正确。
代码思路:利用十六进制字节显示程序,将主存区域的数据(如字符数组)按字节从低位(字符数组首元素)到高位进行输出即可,每按字节输出一个两位十六进制数数组指针移动一位。
代码:
include io32.inc
.data
array byte 'This is a test.'
writebuf byte 12 dup (0) ;显示缓冲区
.code
start:
mov eax,offset array ;将数组主存偏移地址给eax
mov ecx,lengthof array ;ecx存放字节数
call dispmem ;调用子程序按字节从低地址到高地址输出
exit 0
;子程序部分
dispmem proc
push eax ;保护eax
push ecx ;保护ecx
mov ebx,eax ;先把整体32位传给ebx,ebx做数组指针
again:
mov eax,[ebx] ;把数组中元素传给eax
call disphb ;利用十六进制字节显示子程序
mov eax, 32 ;输出空格
call dispc
inc ebx ;指针移动
loop again
dispmem endp ;子程序结束
end start
运行结果:
2.3 十进制数的输入和显示子程序
(1) 有符号十进制数输入子程序
编写有符号十进制数的输入子程序、以及验证子程序的主程序,并运行正确。
思路:(1)首先判断数据是否是0、负数或正数,是0则直接显示’0’退出。
(2) 若输入有符号数为负数,显示符号’-’,求数据的绝对值。
(3) 数据除以10,如十进制正数123,
编写无符号十进制数的输入输出子程序、以及验证子程序的主程序,并运行正确。
.data
count = 5
array dword count dup(0)
temp dword ?
readbuf byte 30 dup(0)
.code
begin:
mov ecx,count
mov ebx,offset array ;ebx为数组指针
again: call read ;调用子程序,输入一个数据
mov eax,temp ;获得出口参数
mov [ebx],eax ;存放到数据缓冲区
add ebx,4 ;指针移动
dec ecx ;计数器递减
jnz again ;循环判定条件
exit 0 ;退出主程序
read proc ;输入有符号十进制数的子程序
push eax ;出口参数:变量temp= 补码表示的二进制数值
push ebx ;说明:负数用"-"引导
push ecx
push edx
read0: mov eax,offset readbuf
call readmsg ;输入一个字符串
test eax,eax ;没有输入数据,转向错误处理
cmp eax,12 ;
ja readerr ;输入超过12个字符,转向错误处理
mov edx, offset readbuf ;edx指向输入缓冲区
xor ebx,ebx ;ebx保存结果
xor ecx,ecx ;ecx为正负标志,0为正,-1为负
mov al,[edx] ;取一个字符
cmp al,'+' ;是"+",继续
jz read1
cmp al,'-' ;是'-',设置-1标志
jnz read2 ;转至无符号处理区read2
mov ecx,-1 ;ecx保存符号位
read1: inc edx ;取下一个字符
mov al,[edx]
test al,al ;是结尾0,转向求补码
jz read3
read2: cmp al,'0' ;不是0~9之间的数码,输入错误
jb readerr
cmp al,'9'
ja readerr
sub al,30h ;是0~9之间的数码,转换为二进制数
imul ebx,10 ;原数值乘10:ebx = ebx *10
jc readerr ;乘积溢出处理
movzx eax,al ;零位扩展,便与相加
add ebx,eax ;原数值乘10后与新数值相加
cmp ebx,80000000h ;数据超过2^31,出错
readerr: mov eax,offset errmsg ;显示出错信息
call dispmsg
jmp read0
;
read3: test ecx,ecx ;判断是正数还是负数
jz read4
neg ebx ;是负数,进行求补
jmp read5
read4: cmp ebx,7fffffffh ;正数超过2^31-1,出错
ja readerr
read5: mov temp,ebx ;设置出口参数
pop edx
pop ecx
pop ebx
pop eax
ret
errmsg byte 'error!!',13,10,0
read endp
end begin
(2) 有符号十进制数显示子程序
编写有符号十进制数的显示子程序、以及验证子程序的主程序,并运行正确。
代码思路:(1) 首先判断给的数据是否是0,若为0则直接输出’0’。
(2) 是负数就在数组首端放置’-’,并求数据绝对值。
(3) 数据除以10,余数为10进制数码,加30h转化为ACSII码之后放在字符数组edx中保存。
(3) 重复(3),直到商为0时结束。
程序代码:
include io32.inc
.data
array dword 1234567890,-1234,0,1,-987654321,32767,-32768,5678,-5678,9000
writebuf byte 12 dup (0) ;显示缓冲区
.code
start:
mov ecx, lengthof array ;数组长度计数器
mov ebx, 0
again: mov eax, array[ebx*4] ;逐个取出字符数组中元素
call write ;调用子程序,显示一个数据
call dispcrlf
inc ebx ;数组指针移动
loop again
exit 0
;子程序部分
write proc
push ebx
push ecx
push edx
mov ebx, offset writebuf ;字符内部数组首指针,指向显示缓冲区
test eax,eax ;判断正/负数(结果不为0),0(结果为0),
jnz write1 ;结果不为0,要么正,要么负,转至write1进一步
mov byte ptr[ebx],'0'
inc ebx ;按字节来,数组指针每次增一
jmp write5
write1: jns write2 ;是正数,转至write2处理
mov byte ptr[ebx],'-' ;若为负数,则首先给字符数组传入一个负号
inc ebx ;字符数组指针往后移动
neg eax ;对eax进行求补
write2: mov ecx,10 ;除数 ecx=10
push ecx
write3: cmp eax,0 ;若该位数为0,则转至write4
jz write4
xor edx,edx ;edx,被除数高位清零
div ecx ;除以10,取余
add edx, 30h ;余数转化为ACSII码
push edx ;将最低位数字压栈
jmp write3
write4: pop edx ;从高位到低位依次弹出栈中存放的数字
cmp edx,ecx ;和除数10进行比较,是结束标志10,转至打印处理
je write5
mov [ebx],dl ;将余数edx的低八位dl放进字符数组
inc ebx ;数组指针移动,循环输出存放的数
jmp write4
write5: mov byte ptr[ebx],0 ;字符数组结束标志
mov eax, offset writebuf ;打印缓冲区内容
call dispmsg
pop edx
pop ecx
pop ebx
ret
write endp
end start
运行结果:
(3) 无符号十进制数显示子程序
代码思路:(1) 首先判断给的数据是否是0,若为0则直接输出’0’。
(2 数据除以10,余数为10进制数码,加30h转化为ACSII码之后放在字符数组edx中保存。
(3) 重复(3),直到商为0时结束div。
(4) 持续pop出栈顶元素给edx,此时出栈顺序刚好是从高位到低位。
程序代码:
include io32.inc
.data
array dword 1234567890,1234,0,1,987654321,32767,32768,5678,9000
writebuf byte 12 dup (0) ;显示缓冲区
.code
start: mov ecx, lengthof array ;ecx为数组长度计数器
mov ebx, 0
again:
mov eax, array[ebx*4] ;使用共享变量传递参数
call write ;调用子程序,显示一个数a,需要保存ebx、ecx的值
call dispcrlf
inc ebx ;数组指针移动
loop again
exit 0
;子程序
write proc
push ebx
push ecx
push edx
mov ebx, offset writebuf ;字符内部数组首指针,指向显示缓冲区
test eax,eax ;若为0,则直接输出
jnz write1 ;若不为0,则进一步处理
mov byte ptr[ebx],'0' ;直接填充字符0
inc ebx ;数组指针后移
jmp write4 ;转至打印缓冲区
write1: mov ecx,10 ;被除数ecx= 10
push ecx ;结束标志,当edx==ecx时结束pop
write2: cmp eax,0 ;判断商是否为0
jz write3 ;商为0,准备输出
xor edx,edx ;被除数最高位清零
div ecx ;商不为0,继续作除法
add edx, 30h ;余数转化为ACSII码
push edx ;余数(0~9区间)入栈
jmp write2
write3: pop edx ;从高位到低位依次pop出来
cmp edx,ecx ;判断该数是否pop完毕
jz write4 ;pop完毕,转至输出缓冲区
mov [ebx],dl ;把从栈中弹出的数位存至数组,ACSII<127,用dl即可
inc ebx ;数字内部字符数组,每次增1
jmp write3
write4: mov byte ptr[ebx],0 ;数组末尾加0
mov eax, offset writebuf ;打印缓冲区内容
call dispmsg
pop edx
pop ecx
pop ebx
ret
write endp
end start
(3) 无符号十进制数输入子程序
include io32.inc
.data
count = 5 ;预留5个空间
array dword count dup(0) ;初始化数组
temp dword ? ;工具变量
readbuf byte 30 dup(0) ;输入缓冲区
.code
start:
mov ecx,count
mov ebx,offset array ;ebx为数组指针
again: call read ;调用子程序,输入一个数据
mov eax,temp ;将返回的temp传回eax
mov [ebx*4],eax ;存放到数据缓冲区
inc ebx ;数组指针移动
loop again ;循环将所有数据全部输出
exit 0
;子程序部分
read proc
push ebx ;主程序中数组指针,需要保护
push ecx ;主程序中计数器,需要保护
read0: mov eax,offset readbuf ;输入缓冲区首地址传给eax
call readmsg ;输入一个字符串
test eax,eax ;没有输入数据,转向错误处理
cmp eax,12 ;输入若超过12个字符,则转向错误处理
ja readerr
mov edx, offset readbuf ;edx作为数组指针
xor ebx,ebx ;ebx保存结果
read1:
mov al,[edx] ;从数组中取出一个字符
test al,al ;判断是不是字符串结尾
jz read2 ;是结尾,输出该数
inc edx
cmp al,'0' ;若某一位小于0,输入错误
jb readerr
cmp al,'9'
ja readerr
sub al,30h ;ACSII码转化为0~9区间的数
imul ebx,10 ;ebx = ebx*10,给权重,初始时ebx=0
jc readerr ;乘积溢出处理
movzx eax,al ;零位扩展,便于前数ebx和当前位al相加
add ebx,eax
cmp ebx,0ffffffffh ;判断数据是否超过2^32-1
jbe read1 ;没超过,继续取下一个字符
readerr: mov eax,offset errmsg ;显示出错信息
call dispmsg
jmp read0 ;再次输入
read2: mov temp,ebx ;设置出口参数
pop ecx
pop ebx
ret
errmsg byte 'error!!',13,10,0
read endp
end start