1. 显示字符串
名称:show_str
功能:在指定位置,指定颜色,显示一个0结尾的字符串
参数:dh=行号,dl=列号,cl=颜色
如:在8号3列显示数据段的字符串。
assume cs:code,ds:data,ss:stack
data segment
db 'Hello world!',0
data ends
stack segment
db 128 dup(0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov si,0
;dh行号,dl列号,cl颜色
mov dh,8
mov dl,3
mov cl,00010010B
call show_str
mov ax,4c00h
int 21h
show_str:
mov ax,0b800h
mov es,ax ;数据目的地址
;将此程序段中用到的寄存器都入栈保存
push cx
push si
push di
push es
push dx
;设置行号
mov al,160
mul dh ;ax=160*dh行号偏移ax中(8位乘法)
push ax ;ax=160*dh保存
mov al,2
mul dl ;ax=2*dl
pop bx ;将ax=160*dh保存放入bx中
add ax,bx ;ax=160*dh+2*dl
mov di,ax ;di为es偏移地址
mov ah,cl ;颜色存放在ah中,字符存放al中。
mov cx,0
change:
mov al,ds:[si]
mov cl,al
jcxz ok ;用cx判断
mov es:[di],ax
inc si
add di,2
push cx
jmp short change
ok:
pop dx
pop es
pop di
pop si
pop cx
ret
code ends
end start
解决除法溢出问题
Div指令会存在溢出的可能,100000/10 就会溢出,如何解决溢出呢?解决方法是用书中提供的公式:
名称:divdw
功能:进行不会产生溢出的除法运算。
参数:
Ax=dword型 低16位
Dx=dword型高16位
Cx=除数
返回:
Dx = 结果高16位存放,ax=结果低16位
Cx=余数
范围:被除数X:Ffffffff
除数N:ffff
商:高16H:ffff
低16位L:ffff
Init():取商,比如,init(38/10)=3;
Rem():取余数,rem(38/10)=8
公式:
X/N=init(H/N) * 65535 + [rem(H/N) * 65535 +L]/N
assume ds:data,cs:code,ss:stack
stack segment
db 128 dup(0)
stack ends
data segment
dw 8 dup(0)
data ends
code segment
start:
;数据段存放商高16,低16,余数
mov ax,data
mov ds,ax
;计算100 0000/10
mov ax,4240h ;低16位H
mov dx,000fh ;高16位L
mov cx,0ah ;除数N
call divdw
mov ax,4c00h
int 21h
;X/N=init(H/N) * 65535 + [rem(H/N) * 65535 +L]/N
divdw:
push ax
;手下计算(H/N),让ax存放余数,dx存放商
;这也是一个32位除法
mov ax,dx
mov dx,0
div cx ;ax存放商(商的高16位),dx存放余数
mov ds:[0],ax ;商的高16位
;下一次除法,余数作为高16位,低16位还是ax
pop ax
div cx
mov ds:[2],ax ;商的低16位
mov ds:[4],dx ;dx为余数
mov ax,ds:[2]
mov dx,ds:[0]
mov cx,ds:[4]
ret
code ends
end start
3. 显示数值
该程序将data段中的字型数据,输出为十进制数据,如最下图所示。
分三步:
第一,逐个读取每个字符;
第二,对读取到的字符,除10取模,存放在string段中;
第三,输出string段中的数据。
程序如下(目前没有将每个子功能都封装为函数,这样不便于理解):
assume cs:code,ds:data,ss:stack
data segment
dw 1230,12666,1321,243,86,9888
data ends
;存放dw中每个余数,从右向左存放
string segment
db 10 dup('0'),0
string ends
stack segment
db 128 dup(0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
call clear_screen
call init_reg
call show_number
mov ax,4c00h
int 21h
;========================================
;名称:clear_screen
;功能:清除当前显存
;参数:无
;返回:无
;应用举例:清空屏幕
;========================================
clear_screen:
mov bx,0b800h
mov es,bx ;显存位置
mov bx,0 ;显存地址
mov dx,0700h
mov cx,2000
clearScreen:
mov es:[bx],dx
add bx,2
loop clearScreen
ret
;========================================
;名称:init_reg
;功能:初始化参数,指明数据源和数据的目的地址
;参数:无
;返回:无
;应用举例:清空屏幕
;========================================
init_reg:
mov bx,data ;数据源
mov ds,bx
mov bx,string ;目的位置:来存放余数
mov es,bx
ret
;========================================
;名称:show_number
;功能:在屏幕的中间位置显示转换后的十进制数
;参数:ds=data 数据源
;es=string 数据目的
;返回:无
;应用举例:如显示‘12366’
;========================================
show_number:
mov bx,0 ;bx访问data中每个地址
mov si,9 ;string中最后一个字符位置
mov di,160*10 + 30*2 ;显示的位置
mov cx,6 ;控制循环次数,显示data中的数据。
showNumber:
push cx
push ds
push es
mov ax,ds:[bx]
mov dx,0
;开对对ax中字数据做除法,然后存入到string:[si]
short_div:
mov cx,10 ;被除数
div cx ;ax存放商,dx为余数
add dl,30h ;将余数加30H,转换为ascii码,存放如string中
mov es:[si],dl
mov cx,ax ;利用cx判断,ax是否为0,0则停止
jcxz shortDivRet ;否则,将ax作为新除数,继续除
dec si
mov dx,0
jmp short_div
shortDivRet:
pop es
pop ds
pop cx
;开始输出的结果string中结果
push ds
push es
push cx
push di
push bx
mov bx,string
mov ds,bx ;ds由data,改为指向string
mov bx,0b800h
mov es,bx ;es由string,改为指向显存
mov cx,0
show_string:
mov cl,ds:[si] ;判断待输出的字符是否为0
jcxz showStirngRet ;如果非0,送至es中
mov byte ptr es:[di+0],cl
mov byte ptr es:[di+1],00100100B
add di,2
inc si
jmp show_string
showStirngRet:
pop bx
pop di
pop cx
pop es
pop ds
add di,160 ;显示下一行
add bx,2 ;读取下一个字符
loop showNumber
ret
;=======================================
code ends
end
这个程序有点问题,明天看