第一题
编程,显示字符串
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用 0 结束的字符串。
参数:(dh)=行号(取值范围 0~24),(dl)=列号(取值范围 0~79)
(cl)=颜色,ds:si 指向字符串的首地址
返回:无
应用举例:在屏幕的 8 行 3 列,用绿色显示 data 段中的字符串。
首先要明白两个点:
- 显存的物理地址是B8000~BFFFF,一般用es:di来存储,di偏移地址存储在屏幕上的位置,如:8行3列应该这样存:[di] == 8 * 160 + 3 *2
- 显示字符的属性(颜色)用8位二进制来表示,具体如下:
[di+1] == L R G B I R G B
高亮 闪烁
下面来看思路:
assume cs:code
data segment
db 'This is my assembly thing 612348',0
data ends
code segment
start:
设置题干里的参数
ds和data关联
调用show_str
show_str:
保护数据(push)
处理显存的逻辑地址:先b800h放入es
处理显存的偏移地址:放入di
这里需要释放cx用于循环判定
mov dl,cl
s:
将ch置0,cl读出data数据,然后用jcxz判定,这样只要读到了最后的0就能退出程序了
将数据写到显存中
inc si
add di,2
jmp short s
exit: 释放保护数据(pop)
ret
code ends
end start
具体代码如下
assume cs:code
data segment
db 'This is my assembly thing 612348',0
data ends
code segment
start:
mov dh,8
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00h
int 21h
show_str:
push ax
push bx
push cx
push dx
push si
mov ax,0b800h
mov es,ax
mov ah,dh
mov dh,160
mul dh
mov bx,0
mov bl,dl
add ax,bx
add ax,bx
mov di,ax
mov dl,cl
s:
mov ch,0
mov cl,[si]
jcxz exit
mov byte ptr es:[di],cl
mov byte ptr es:[di+1],dl
inc si
add di,2
jmp short s
exit: pop si
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start
第二题
编程,解决除法溢出的问题
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为 dword 型,除数为 word 型,结果为 dword
型。
参数:(ax)=dword 型数据的低 16 位
(dx)=dword 型数据的高 16 位
(cx)=除数
返回:(dx)=结果的高 16 位,(ax)=结果的低 16 位
(cx)=余数
应用举例:计算 1000000/10(F4240H/0AH)
提示
给出一个公式:
X:被除数,范围:[0, FFFFFFFF]
N:除数,范围:[0, FFFF]
H:X 高 16 位,范围:[0, FFFF]
L:X 低 16 位,范围:[0, FFFF]
int():描述性运算符,取商,比如,int(38/10) =3
rem():描述性运算符,取余数,比如,rem(38/10)= 8
公式:X/N = int(H/N) * 65536 +[rem(H/N) * 65536 + L] / N
这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算。公式中,
等号右边的所有除法运算都可以用 div 指令来做,肯定不会导致除法溢出。
首先要明白的几个点:
- 本题是16位除数(cx),所以被除数就是32位,其中dx是高位,ax是低位;在进行
div cx
后,ax存的是商,dx存的是余数,具体的div指令见王爽书169页 - 题干中的公式不必深究,否则很容易绕晕,我们用十进制除法来类比,比如做97 / 2
想想我们列竖式是怎么做的:先商一个4,用9 - 2 * 4得到高位的余数 1,然后将低位的7移下来,注意这个移下来,在程序里面我们需要将高位的余数 1放到高位寄存器dx,低位寄存器ax此时是7,然后做17 / 2,然后商8,17 - 2 * 8得到最终余数 1
思路:(建议先把下面的代码实现复制到记事本,然后一步一步跟着思路来理解)
start中做的事情:
定义被除数、除数、商:
dx ax / cx
000fh 4240h / 0ah
做完这些之后就调用了divdw
divdw中做的事情:
三个push用来保护数据,这其实算是一个好的变成习惯,在子程序中修改了哪些参数,在一开始先放到栈中保护起来
接着把被除数、除数、商复制一份
dx ax / cx
si di / bx
000fh 4240h / 0ah
然后进入do标号(do标号实际上没任何作用,只是区分一下上面的步骤):
mov ax,dx
mov dx,0
上面这两句把原始数据的高位放入ax进行div运算,意在得到高位的余数
而由于做的是32位除法运算,所以dx不设0的话会干扰运算
div cx
div运算后就得到了高位的余数,并且自动存在dx中
在上面的第2点中的“移下来”这个说法中解释了,在程序中就是把高位的余数放到dx中
而程序自动帮我们做了这件事,所以直接进入下一步
mov ax,di ;把我们提前复制的一份数据拿出来用
div cx ;“dx ax / cx”
这一步得到了真正的余数,存在dx中;得到了低位的商,存在ax中
这是我们要的数据所以把他们放到栈中保护起来
至此低位已经搞定,下面来搞高位:
mov ax,si
mov dx,0 ;同样防止dx影响运算
div cx ;这一步得到了高位的商,存在ax中
我们需要将最终的结果高位保存在dx中,低位保存在ax中
所以:
mov dx,ax
pop ax
结束
具体代码如下
assume cs:code
code segment
start:
mov ax,4240h
mov ds,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw:
push bx
push si
push di
mov si,dx
mov di,ax
mov bx,cx
do: mov ax,dx
mov dx,0
div cx
mov ax,di
div cx
push ax
push dx
mov ax,si
mov dx,0
div cx
mov dx,ax
pop cx
pop ax
pop di
pop si
pop bx
ret
code ends
end start
第三题
数值显示
名称:dtoc
功能:将 word 型数变为表示十进制数的字符串,字符串以 0 为结尾符
参数:(ax)=word 型数据,ds:si 指向字符串的首地址
返回:无
应用举例:编程,将数据 12666 以十进制的形式在屏幕的 8 行 3 列,用绿色显示出来。在显
示我们调用本次实验的第一个子程序 show_str。
assume cs:code,ds:data
data segment
db 10 dup (0)
data ends
code segment
start:
mov ax,12666
mov bx,data
mov ds,bx
mov si,0
call dtoc
mov dh,8
mov dl,3
mov cl,2
call show_str
mov ax,4c00h
int 21h
dtoc:
push ax
push cx
push dx
push si
s1:
mov cx,ax
jcxz exit
mov cx,10
mov dx,0
call divdw
add cl,30h
push cx ;每次把ax的末位数字压入栈
inc si
jump short s1
ok:
mov cx,si
mov si,0 ;从data的0号开始存
s2:
pop dx
mov [si],dl
inc si
loop s2
pop si
pop dx
pop cx
pop ax
ret
divdw:
push bx
push si
push di
mov si,dx
mov di,ax
mov bx,cx
mov ax,dx
mov dx,0
div cx
mov ax,di
div cx
push ax
push dx
mov ax,si
mov dx,0
div cx
mov dx,ax
pop cx
pop ax
pop di
pop si
pop bx
ret
show_str:
push ax
push bx
push cx
push dx
push si
mov ax,0b800h
mov es,ax
mov ah,dh
mov dh,160
mul dh
mov bx,0
mov bl,dl
add ax,bx
add ax,bx
mov di,ax
mov dl,cl
s:
mov ch,0
mov cl,[si]
jcxz exit
mov byte ptr es:[di],cl
mov byte ptr es:[di+1],dl
inc si
add di,2
jmp short s
exit:
pop si
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start