编写子程序(为后续的实验做准备)
这里为后面的课程设计一做准备,贴一篇子程序的设计思路:
- 我们要实现三个功能:
- 显示字符串
- 解决除法溢出问题
数值显示
展示功能:
显示字符串
子程序的参数接口
mov ch, 0 mov di, 16*1+1 ;指定字符串 mov cl, ds:[di] mov si, 900 ;指定位置 mov bl, ds:[16*3+1] ;指定颜色 mov ax, 0b800h ;输出位置 mov es, ax call show_str
程序结构
show_str: jcxz back mov es:[si], cl mov es:[si+1], bl add si, 2 inc di mov cl, ds:[di] jmp show_str back: ret
更改参数,我们也可以得到另一些结果:
字符串位置di:16*0+0
显示位置si:1500
指定颜色bl:ds:[16*3+0]解决除法溢出问题
书中提供了这样一个公式,其原理是:高16位和低16位分别做除法运算,高位除法产生的余数放到低16位再做除法,这样就能得到最终的余数
这样,在除数为16位的前提下,任何一种情况都已经囊括其中
这里我对高、低16位都进行的是32位的除法,如果我使用16位除法,会导致无法存储商大于FFH的情况,div_dw: ;(输入):ax低16位,dx高16位,cx为16位除数 ;(输出):dx结果高16位,ax结果低16位,cx为余数 push bx mov bx, ax ;暂存ax,低位 mov ax, dx mov dx, 0 div cx ;高16作除法,余数并到低16位 push ax ;暂存高16位除法结果中的商 mov ax, bx div cx ;低16位除法 mov cx, dx pop dx pop bx ret
输入内容:
高16位:0fh,低16位:4240h,除数cx:0ah
数值显示 (于23327针对中间数字含0的情况进行修改)
根据输入的十六进制数,转化成十进制数的字符串形式
这里使用了很多的栈操作,是因为得暂时存放数据,避免程序内的操作对外面程序产生影响dtoc: ;X输入ax,(16位) ;结果保存在number字段 ;注意,检测商为0才跳出!! push bx push cx push dx push ds push si mov bx, number mov ds, bx ;目标地址 mov si, 0 ;偏移指针,个数 mov bx, 10 ;除数 mov dx, 0 ;32位除法,但这里只有ax参与,dx得置0 in_num: div bx push dx inc si mov cx, ax ;这里没有loop循环,cx可放心使用 jcxz in_num_exit mov dx, 0 jmp in_num in_num_exit: mov cx, si mov si, 0 mov bx, 0 set_num: pop bx add bx, 30h mov ds:[si], bl inc si loop set_num mov byte ptr ds:[si], 0 ;末尾添0,表示字符串结束 pop si pop ds pop dx pop cx pop bx ret
错误版本,引以为鉴
这里传入的参数ax值为65535,修改显示字符串程序中的di为40h,即可在屏幕中展示
清屏操作
原理是对首页数据全部清零
clear_screen: push bx push cx push es push si mov bx, 0b800h mov es, bx mov cx, 4096 mov si, 0 swap: mov byte ptr es:[si], 0 inc si loop swap pop si pop es pop cx pop bx ret
最后附上整个代码,各个功能需自行改动
(于23327针对中间数字含0的情况进行修改)
assume cs:code ds:data ss:stack
data segment
; 0123456789ABCDEF
db 'fishing is funny',0
db 'welcome to masm!',0
data ends
color segment
db 00000100B ;红色
db 00000010B ;绿色
db 00000001B ;蓝色
color ends
number segment
db 512 dup(255)
number ends
stack segment stack
db 128 dup(0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
call clear_screen
mov ch, 0
mov di, 16*0+0 ;指定字符串
mov cl, ds:[di]
mov si, 1500 ;指定位置
mov bl, ds:[16*3+1] ;指定颜色
mov ax, 0b800h ;输出位置
mov es, ax
call show_str
mov ax, 4240h ;除法,低16位
mov dx, 000fh ;除法,高16位
mov cx, 0ah ;除法,除数,16位
call div_dw
mov ax, 65535 ;要转换的十六进制数
call dtoc
mov ax, 4c00h
int 21h
div_dw:
;(输入):ax低16位,dx高16位,cx为16位除数
;(输出):dx结果高16位,ax结果低16位,cx为余数
push bx
mov bx, ax ;暂存ax,低位
mov ax, dx
mov dx, 0
div cx ;高16作除法,余数并到低16位
push ax ;暂存高16位除法结果中的商
mov ax, bx
div cx ;低16位除法
mov cx, dx
pop dx
pop bx
ret
dtoc:
;X输入ax,(16位)
;结果保存在number字段
;注意,检测商为0才跳出!!
push bx
push cx
push dx
push ds
push si
mov bx, number
mov ds, bx ;目标地址
mov si, 0 ;偏移指针,个数
mov bx, 10 ;除数
mov dx, 0 ;32位除法,但这里只有ax参与,dx得置0
in_num:
div bx
push dx
inc si
mov cx, ax ;这里没有loop循环,cx可放心使用
jcxz in_num_exit
mov dx, 0
jmp in_num
in_num_exit:
mov cx, si
mov si, 0
mov bx, 0
set_num:
pop bx
add bx, 30h
mov ds:[si], bl
inc si
loop set_num
mov byte ptr ds:[si], 0 ;末尾添0,表示字符串结束
pop si
pop ds
pop dx
pop cx
pop bx
ret
show_str:
jcxz back
mov es:[si], cl
mov es:[si+1], bl
add si, 2
inc di
mov cl, ds:[di]
jmp show_str
back: ret
clear_screen:
push bx
push cx
push es
push si
mov bx, 0b800h
mov es, bx
mov cx, 4096
mov si, 0
swap: mov byte ptr es:[si], 0
inc si
loop swap
pop si
pop es
pop cx
pop bx
ret
code ends
end start