火竹 发表于 3 天前

实验十——三个子程序

assume cs:code,ds:data,ss:stack

data 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
       

FishC 发表于 3 天前

原程序存在一些语法错误和逻辑问题,下面我将逐一分析并给出修正后的代码,同时保持原有的功能结构。


[*]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]
查看完整版本: 实验十——三个子程序