一. 子程序:显示字符串
实验要求:在屏幕的8行3列,用绿色显示data段中的字符串。
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(0-24取值范围);(dl)=列号(0-79取值范围);(cl)=颜色(是一个二进制排列组合的值);ds:si指向字符串的首地址。
实验目的:
1.熟练掌握在dos屏幕上输出字符的基本操作。掌握显示缓冲区范围。
2.为什么定义字符串用0来结尾?
3.熟练掌握从内存中读取字节单元内容和字单元内容;并将该内容写入我们期望的内存中。
4.掌握8位乘法和16位乘法的操作。
程序分析:
1,在实验九中我们可以知道一些基本信息:
命令提示符窗口或dos窗口,我们可以显示80X25的字符(我的机器行数多,命令提示符窗口,跟设置有关)。每行80个字符,一共是25行。它们在内存中是在一个内存段中存储的,这个内存区域叫做显示缓冲区。从物理地址B8000H~BFFFH这个32K的内存区域就是显示缓冲区。
在显示缓冲区中,偶数字节单元表示的是字符,奇数字节单元表示的是字符的属性(颜色、闪烁等)。也就是说在显示缓冲区中,每2个字节负责屏幕上一个字符的显示(包括显示的属性)。
在显示缓冲区内写入的字符,立即就显示在屏幕上。
因为每行要显示80个字符,故从0000H~009FH是显示的第一行(共160个字节)。每行可以类推。也就是说行的偏移量是160个字节。
2,为什么在定义字符串时候,结尾有个0?
讲解:因为在汇编和其它语言中,字符串存储在内存中,长度不一致,我们统一规定在每个字符串的结尾有个数字0,就代表了这个字符串结束了。规定(也是便于管理)
3, 在写入显示缓冲区中时,我们为什么使用[bx+di+idata]的方式?
在子程序中,我们通过计算得出了在特定行和特定列(由主程序中的dl和dh参数传入)的基于b800:0000的偏移地址是(bx);(di)代表了从这个偏移地址开始,每个字符的偏移地址。(idata)代表了每个字符的二个字节(一个是字符本身,一个是字符的颜色属性)。
实验九,子程序一,代码如下
assume cs:code,ds:data
data segment
db 'Welcome to masm!',0
data ends
code segment
start: mov dh,8
mov dl,80
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00h
int 21h
show_str: push dx
push cx
push si
mov al,160
mov bl,dh
mul bl
mov bx,ax
mov dh,0
add bx,dx
mov ax,0b800h
mov es,ax
mov al,cl
mov di,0
mov si,0
change: mov ch,0
mov cl,ds:[si]
jcxz ok
mov es:[bx+di],cx
mov byte ptr es:[bx+di+1],al
inc si
add di,2
loop change
ok: pop si
pop cx
pop dx
ret
code ends
end start
子程序二:解决除法溢出的问题
div指令格式:div reg或div 内存单元
32bits/16bits规则及16bits/8bits规则 如下:
这里没有考虑 32 bits/8bits时,商大于FFFFH的情况,只用AX存储不下这个值,需要两个16位寄存器存放。
这里给出了一种方法:
这里会用到乘法指令,其格式:mul reg或mul 内存单元,以下是运算规则:
以下是实现的参考代码
assume cs:code
code segment
start:mov ax, 4240h
mov dx,000Fh
mov cx,0Ah
call divdw
mov ax,4c00h
int 21h
divdw:
push ax
mov ax,dx ;原被除数的高字
mov dx,0000H
div cx ;ax=商,dx=余数
mov bx,ax
pop ax ;原被除数的低字
div cx ;ax=结果的低16bits
mov cx, dx ;结果的余数
mov dx, bx ;结果的高16bits
ret
code ends
end start