1)显示字符串
assume cs:codeseg, ds:data
data segment
db 'Welcome to masm!', 0
data ends
codeseg 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
; 显示字符串
; @desc 参数:dh => 行,dl => 列,cl => 颜色,ds:si => 字符串地址,以要 0 结尾
show_str:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 保存寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
show_str_start:
push ax
push bx
push dx
push cx
push es
push si
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 开始实现功能
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 显存 B8000 - BFFFF,我们使用 B800 作为段地址
; 段空间位 64K,而显存只有 32K,所以不会超过段
; 第一步、设置段地址
mov ax, 0B800H
mov es, ax ; es => 写入字符的段地址
; 第二步、计算偏移量
; 每行的长度:0 - 9F | 每列的宽度:两字节,高位颜色,低位字符
; 写入字符的开始地址 = (行号 - 1) x 0A0H + 列号 x 2
sub dh, 1
mov al, dh
mov bl, 0A0H
mul bl ; ax
add dl, dl
mov dh, 0 ; dx
add dx, ax ; ax => 写入字符的开始地址
mov bx, dx
; 第三步、开始写入
mov ah, cl ; 颜色
copy_char:
mov cl, ds:[si] ; 判断字符是否为零
mov ch, 0
jcxz show_str_end
mov al, cl
mov es:[bx], ax ; 将字符写入显存
inc si
add bx, 2
jmp short copy_char
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 恢复寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
show_str_end:
pop si
pop es
pop cx
pop dx
pop bx
pop ax
ret
codeseg ends
end start
2)解决除法溢出问题
assume cs:codeseg
codeseg segment
start: mov ax, 4240H ; LOW
mov dx, 000FH ; HIGH
mov cx, 0AH
call divdw
mov ax, 4c00H
int 21h
; 解决除法溢出问题
; @desc 参数:dx => 被除数高位,ax => 被除数低位,cx => 除数
; @desc 结果:dx => 结果高位,ax => 结果低位,cx => 余数
divdw:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 保存寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 根据题目,并结合我们的实现方式,只有 bx 寄存器是“借用”的,
; 其他寄存器(ax, dx, cx)会被使用,并且内容会被改变
; 因此无需保存
push bx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 开始计算除法
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 第一部分、计算【int(H/N) * 65536】Start
push ax ; 由于需要使用 ax 寄存器,所以先保存 ax 寄存器(即初始的低位值)
mov ax, dx ; 开始计算 int(H/N)
mov dx, 0
div cx ; 此时:ax => 商,这就是“结果的高 16 位”;dx => 余数,即 rem(H/N) 值
mov bx, ax ; 后面需要使用 ax 寄存器,因此将 ax 保存到 bx 中
; 第二部分、计算【[rem(H/N) * 65536 + L]/N】
; [rem(H/N) * 65536 + L]/N 是 16 位除法,因为 N 为 16 位(既初始的 cx 寄存器)
; [rem(H/N) * 65536 + L]/N 等价于:
; (1)将“高位(000FH)除法的余数”放在 dx 寄存器中
; (2)将低位(4240H)放在 ax 寄存器中
; (3)然后除以 cx 寄存器
; (1)而前一步的除法已经将余数放在 dx 寄存器中,
; (2)因此设置 ax 寄存器即可,即将低位放入 ax 中
; (3)然后除以 cx 寄存器
pop ax ; 由于 ax 的值之前写入到栈中,此时弹出
div cx ; => ax...dx
; 按照实验要求,存储计算结果
mov cx, dx
mov dx, bx
; mov ax, ax ; 由于 ax 已经存储低位,因此无需处理
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 恢复寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pop bx
ret
codeseg ends
end start
3)数值显示
assume cs:codeseg
dateseg segment
db 10 dup (0)
dateseg ends
codeseg segment
start:
mov ax, 12666
mov bx, dateseg
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
; 将数值转化为字符换
; @desc 参数:dx => 数据高位,ax => 数据低位,ds:si => 数据写入地址
; @desc 结果:保存到 ds:si 中,并以 0 结尾
dtoc:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 保存寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push ax
push bx
push cx
push dx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 实现逻辑
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 计算数值字符串
mov bx, 0
push bx ; 无需理解此步骤,往下看就知道了
_do_some_stuff_start:
mov dx, 0
mov ax, ax
mov bx, 10
div bx ; dx,ax / bx = ax...dx
add dx, 30H
push dx ; 余数,计算结果与显示结果是相反的。先入栈再弹出
mov cx, ax
jcxz _do_some_stuff_end ; 如果商为零,则结束
jmp short _do_some_stuff_start ; 继续运行,进行下一轮除法
_do_some_stuff_end:
; 将数值写到数据段
mov bx, 0
save_to_dateseg:
pop cx ; 最开始我们在栈底写入零,用于标识数据弹出结束(再弹就不是我们的数据了)
jcxz _append_zero_to_sting ; 跳转到结束
mov ds:[bx], cl ; 这里保留低位即可,高位没有意义,而且字符只占一个字节
inc bx
jmp short save_to_dateseg
; 字符串末尾追加零,因为 show_str 要求字符串以 0 结尾
_append_zero_to_sting:
mov byte ptr ds:[bx], 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 恢复寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dtoc_end:
pop dx
pop cx
pop bx
pop ax
ret
show_str:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 保存寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_show_str_start:
push ax
push bx
push dx
push cx
push es
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 开始实现功能
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 显存 B8000 - BFFFF,我们使用 B800 作为段地址
; 段空间位 64K,而显存只有 32K,所以不会超过段
; 第一步、设置段地址
mov ax, 0B800H
mov es, ax ; es => 写入字符的段地址
; 第二步、计算偏移量
; 每行的长度:0 - 9F | 每列的宽度:两字节,高位颜色,低位字符
; 写入字符的开始地址 = (行号 - 1) x 0A0H + (列号-1) x 2
sub dh, 1
mov al, dh
mov bl, 0A0H
mul bl ; ax
sub dl, 1
add dl, dl
mov dh, 0 ; dx
add dx, ax ; ax => 写入字符的开始地址
mov bx, dx
; 第三步、开始写入
mov ah, cl ; 颜色
_copy_char:
mov cl, ds:[si] ; 判断字符是否为零
mov ch, 0
jcxz _show_str_end
mov al, cl
mov es:[bx], ax ; 将字符写入显存
inc si
add bx, 2
jmp short _copy_char
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 恢复寄存器
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_show_str_end:
pop es
pop cx
pop dx
pop bx
pop ax
ret
codeseg ends
end start
参考文献
WikiNotes/参考答案:实验 10 编写子程序
优快云/汇编语言王爽第三版答案
百度文库/汇编语言实验答案 (王爽)