实验十——三个子程序
assume cs:code,ds:data,ss:stackdata segment
db '000000'
data ends
stack segment
dw 32 dup(0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,32
mov ax,0ffffh
mov dx,0
mov cx,2
call divdw
mov si,0
call dtoc
mov dl,37
mov dh,12
mov cl,00000010b
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00h
int 21h
;数据:ax
;字符串地址:ds:si
dtoc: mov cx,0
push cx
cle2: mov dx,0
mov cx,10
call divdw
add cx,30h
push cx
mov cx,ax
jcxz cle3
jmp cle2
cle3: pop cx
mov ds:,cl
jcxz cle4
inc si
jmp cle3
cle4: ret
;被除数:ax低八位 dx高八位
;除数:cx
;商:ax低八位 dx高八位
;余数:cx
s: dw 0,0
divdw: mov di,offset s
mov bx,0
mov cs:,ax ;将低八位暂存在cs:中
mov ax,dx
mov dx,0
div cx
mov cs:,ax;将高八位除以除数的结果暂存在cs:中
mov ax,cs:
div cx
mov cx,dx
mov dx,cs:
ret
;di 行 1~25 第一行显示不出来刷新没了
;dh 列 1~80
;cl 属性 7闪烁654背景红绿蓝 3高亮 210前景红绿蓝
;si 字符串偏移地址ds:si
show_str:
mov ax,0b800h
mov es,ax
mov bx,0
mov al,dh
sub al,1
mov ch,160
mul ch
mov bx,ax
mov al,dl
sub al,1
mov ch,2
mul ch
add bx,ax
mov al,cl
mov cx,0
mov di,0
cle: mov cl,ds:
jcxz sss
mov es:,cl
mov es:,al
add si,1
add di,2
loop cle
sss: ret
code ends
end start
原程序存在一些语法错误和逻辑问题,下面我将逐一分析并给出修正后的代码,同时保持原有的功能结构。
[*]divdw 子程序的问题:原程序使用 cs: 直接存储数据是错误的,应使用堆栈或数据段变量。这里采用堆栈保存中间结果。
[*]dtoc 子程序的问题:在将数字转换为字符串时,dx 被错误清零,导致只处理了16位数。同时,字符串存储顺序是反的(先低位后高位),需要正序存储。
[*]show_str 子程序的问题:使用 ds: 和 es: 时未指定偏移地址,且循环指令 loop 与字符判断冲突。
修正后的完整程序如下:
assume cs:code, ds:data, ss:stack
data segment
db '0000000000' ; 预留10字节存储字符串
data ends
stack segment
dw 32 dup(0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 32
; 计算 0FFFFh / 2
mov ax, 0FFFFh
mov dx, 0
mov cx, 2
call divdw ; 结果:dx:ax=7FFFh, cx=余数
; 将结果转换为字符串
mov si, 0
call dtoc
; 在屏幕第12行第37列显示字符串
mov dl, 37 ; 列号
mov dh, 12 ; 行号
mov cl, 00000010b ; 属性(绿色)
mov ax, data
mov ds, ax
mov si, 0
call show_str
; 程序退出
mov ax, 4C00h
int 21h
;===============================================
; 子程序:divdw
; 功能:32位除以16位的不溢出除法
; 输入:dx:ax = 被除数(32位),cx = 除数(16位)
; 输出:dx:ax = 商(32位),cx = 余数(16位)
;===============================================
divdw:
push bx ; 保存 bx
mov bx, ax ; 暂存低16位
mov ax, dx ; 高16位移到 ax
mov dx, 0 ; 清零 dx,形成 0:高16位
div cx ; ax = int(H/N), dx = rem(H/N)
push ax ; 保存 int(H/N)
mov ax, bx ; 恢复低16位
div cx ; ax = 商的低16位, dx = 余数
mov cx, dx ; 余数存入 cx
pop dx ; 恢复 int(H/N) 作为商的高16位
pop bx ; 恢复 bx
ret
;===============================================
; 子程序:dtoc
; 功能:将32位无符号数转换为十进制字符串
; 输入:dx:ax = 要转换的数字,ds:si 指向存储位置
; 输出:在 ds:si 处存放以0结尾的字符串
;===============================================
dtoc:
push ax
push dx
push cx
push si
push bx
mov bx, si ; 保存字符串起始位置
jmp dtoc_entry
dtoc_loop:
mov cx, 10 ; 除数为10
call divdw ; 除以10,余数在 cx
add cx, 30h ; 余数转ASCII字符
push cx ; 字符压栈(低位先入)
dtoc_entry:
; 判断商(dx:ax)是否为0
mov cx, dx
or cx, ax
jnz dtoc_loop ; 不为0则继续循环
; 商为0,压入结束标志0
mov cx, 0
push cx
; 从栈中弹出字符,正序存入数据段
mov si, bx ; 恢复字符串起始位置
dtoc_pop:
pop cx
mov , cl ; 存储字符
inc si
jcxz dtoc_done ; 遇到0则结束
jmp dtoc_pop
dtoc_done:
pop bx
pop si
pop cx
pop dx
pop ax
ret
;===============================================
; 子程序:show_str
; 功能:在屏幕指定位置显示字符串
; 输入:dh = 行号(0-24),dl = 列号(0-79)
; cl = 属性,ds:si 指向字符串首地址
; 输出:无
;===============================================
show_str:
push ax
push bx
push cx
push dx
push si
push di
push es
mov ax, 0B800h ; 显存段地址
mov es, ax
; 计算行偏移:行号 * 160
mov al, 160
mul dh ; 结果在 ax
mov bx, ax
; 计算列偏移:列号 * 2
mov al, 2
mul dl ; 结果在 ax
add bx, ax ; bx = 显存中的起始偏移
mov al, cl ; 保存属性到 al
mov di, 0 ; 显存索引
show_str_loop:
mov cl, ; 取字符
mov ch, 0 ; 设置 ch=0,用于判断字符是否为0
jcxz show_str_end ; 字符为0则结束
mov es:, cl ; 写入字符
mov es:, al; 写入属性
inc si
add di, 2
jmp show_str_loop
show_str_end:
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start
主要修改说明:
[*]divdw 子程序:采用标准的不溢出除法算法,使用堆栈暂存中间结果,避免直接操作代码段。
[*]dtoc 子程序:修正了 dx 被错误清零的问题,确保处理32位数。通过栈实现从低位到高位的转换,再正序存储字符串。
[*]show_str 子程序:显存写入时指定完整地址 es:,循环改用 jmp 避免与 cx 冲突。
[*]数据段:预留10字节空间,确保足够存储转换后的字符串。
程序运行逻辑:计算 0FFFFh ÷ 2 = 7FFFh(32767),转换为字符串 "32767",在屏幕第12行第37列以绿色显示。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页:
[1]