You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

8086汇编递归实现十六进制转十进制前缀输出问题求助

8086汇编递归子程序问题排查与修复

作为工科学生完成作业时遇到8086汇编难题,耗时10小时未解决。需求是编写递归子程序,通过栈接收16位无符号十六进制数,将其转为十进制并按前缀逐行输出(例如输入B26Eh需输出45678、4567等)。现有代码存在两个问题:

  1. 调试时递归返回后程序跳转至内存随机位置
  2. 修复部分问题后,无法在递归回溯时正确获取余数DX

原代码:

.model small
.data
.stack 100h
.code
START:

    ; Setting screen data into ES register
    MOV AX, 0B800h 
    MOV ES, AX   
    
    ; Setting the data segment
    MOV AX, @data   
    MOV DS, AX
    
    ; Screen offset
    MOV CX, 0d 
    MOV BX, 340h
    
    ; Testing result
    MOV AX, 746FH
    PUSH AX
    CALL numPrefix
    
    
    ; Exit and print both runs
    MOV AX, 4C00h
    INT 21h
    
numPrefix PROC
    
    ; Save registers
    PUSH BP
    PUSH DX
    
    MOV BP, SP
    MOV AX, [BP+6d]
    
    ; Base condition
    CMP AX, 0
    JE baseCase
    
    ; Else, divide the input by 16d to get the quotient and remainder
    PUSH CX ; Save print address 
    mov cx, 10  ; Divisor
    xor dx, dx  ; Clear dx for the quotient
    div cx      ; Divide ax by cx, quotient in ax, remainder in dx
    POP CX
    
    ; Call numPrefix recursively with the quotient
    push ax     ; Push the quotient onto the stack
    call numPrefix
    
    
; PRINT LOGIC #################################################################################
    ; Now we print what we got for each row in the recursion
    MOV DH, 2Eh
    
    SUB BX, 0A0h
    PUSH BX
    PUSH CX
    
    CMP CX, 0d
    JE equalZero
    
    printLoop:
    ; For each row in the recursion, print the appropriate row of numbers
    MOV SI, BX
    MOV DI, [BX+0A0h]
    PUSH AX
    MOV AX, ES:[SI]
    MOV ES:[DI], AX
    POP AX
    
    ADD BX, 1
    loop printLoop
    
    equalZero:
    POP CX
    ; Add new member
    ADD BX, 1
    MOV ES:[BX], DX
    POP BX
    INC CX
    
; PRINT LOGIC #################################################################################
    ; Finish and restore registers
    
    ; Also Base case where we only print and not do recursion again
    baseCase:
    pop DX
    pop BP
    ret 2
    
numPrefix ENDP
END START   

问题根源分析

  1. 递归返回跳转异常:

    • 子程序仅保存BP和DX,但递归过程中修改了BX、CX等全局寄存器,未对这些寄存器做现场保存,导致递归返回后寄存器状态被破坏,引发执行流异常。
    • 打印逻辑直接修改BX、CX,且未在子程序入口完整保存,递归调用时这些寄存器的值被后续递归实例覆盖。
  2. 回溯时余数DX丢失:

    • div cx得到的余数存于DX,但递归调用numPrefix时,子程序会执行push DX/pop DX操作,导致当前余数被覆盖,回溯时无法获取正确值。

修复后的代码

.model small
.data
.stack 100h
.code
START:
    ; 设置显存段
    MOV AX, 0B800h 
    MOV ES, AX   
    
    ; 设置数据段
    MOV AX, @data   
    MOV DS, AX
    
    ; 初始化屏幕偏移和计数器
    MOV CX, 0d 
    MOV BX, 340h  ; 初始输出位置(第5行左右)
    
    ; 测试:746FH = 29711,应输出29711、2971、297、29、2
    MOV AX, 746FH
    PUSH AX
    CALL numPrefix
    
    ; 程序退出
    MOV AX, 4C00h
    INT 21h

numPrefix PROC
    ; 完整保存现场:递归中用到的寄存器全部入栈
    PUSH BP
    MOV BP, SP
    PUSH BX
    PUSH CX
    PUSH DX
    
    MOV AX, [BP+10]  ; 栈结构:BP+2=DX, BP+4=CX, BP+6=BX, BP+8=返回地址, BP+10=传入参数
    
    ; 递归终止条件:AX为0时直接返回
    CMP AX, 0
    JE baseCase
    
    ; 除以10,得到商(AX)和余数(DX)
    MOV CX, 10
    XOR DX, DX
    DIV CX
    
    ; 保存当前余数到栈(递归返回后取出使用)
    PUSH DX
    ; 递归调用:传入商作为新参数
    PUSH AX
    CALL numPrefix
    ; 回溯时取出之前保存的余数
    POP DX
    
    ; -------------------------- 打印逻辑 --------------------------
    ; 1. 复制上一行的所有数字到当前行
    SUB BX, 0A0h       ; 上一行起始位置(每行80*2=160=0A0h字节)
    MOV SI, BX
    MOV DI, BX + 0A0h  ; 当前行起始位置
    MOV CX, [BP+4]     ; 取出递归前的CX值(已打印数字个数)
    JCXZ skip_copy      ; CX为0时无需复制
    
copy_loop:
    MOV AX, ES:[SI]
    MOV ES:[DI], AX
    ADD SI, 2          ; 每个字符占2字节(ASCII+显示属性)
    ADD DI, 2
    LOOP copy_loop
    
skip_copy:
    ; 2. 在当前行末尾添加新的余数(转为ASCII)
    MOV AL, DL
    ADD AL, 30h        ; 数字转ASCII码
    MOV AH, 07h        ; 黑底白字显示属性
    MOV ES:[DI], AX
    
    ; 更新计数器和下一行起始位置
    INC WORD PTR [BP+4]  ; CX自增(已打印数字个数+1)
    MOV BX, DI + 2       ; 下一行起始位置为当前行末尾+2
    
baseCase:
    ; 恢复现场
    POP DX
    POP CX
    POP BX
    POP BP
    RET 2  ; 清理栈上的16位参数
numPrefix ENDP
END START

修复说明

  • 寄存器完整保存:子程序入口保存BP、BX、CX、DX,确保递归过程中寄存器状态不被破坏,解决跳转异常问题。
  • 余数栈存储:递归调用前将余数DX压入栈,回溯时弹出,保证能获取当前层级的正确余数。
  • 显存访问修正:针对VGA显存双字节(ASCII+属性)的存储结构,修复单字节操作导致的显示异常。
  • 递归终止逻辑优化:AX为0时直接返回,避免执行不必要的打印逻辑。

内容的提问来源于stack exchange,提问作者Zig302

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 10:22:03