首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

8086 汇编显示字符串Bug:push cx 后 jcxz 导致 pop ip 取错值

     編輯:程式設計綜合問答

錯誤代碼片段

show_str:
    mov ch, 0
    push cx          ; 保存 cx (cl=計數器, ch=0)
    mov cl, ds:[di]  ; 取字符
    jcxz excout      ; cl=0 則結束
    ; ... 計算顯存地址 ...
    pop cx           ; 恢復 cx
    mov es:[bx+si+1], cl  ; 寫入屬性字節
    ; ...
    jmp short show_str
excout:
    ret

現象

運行後程序飛執行亂碼或死循環。去掉 push cx / pop cx 並將 mov es:[bx+si+1], cl 改為常數 2 即正常。

根因:jcxz 改變了控制流,導致 pop cx 匹配錯誤

  1. push cx 將 cx 壓棧
  2. jcxz excout 若 cl==0 跳轉到 excout
  3. 關鍵點:excout 標號下是 ret,沒有執行 pop cx
  4. 棧頂殘留舊 cx,後續 ret 彈出的返回地址實際是這個殘留值 → IP 錯誤 → 程序跑飛

修正方案:結構化棧操作

show_str:
    mov ch, 0
next_char:
    mov cl, ds:[di]
    jcxz excout       ; cl=0 直接跳出,棧平衡
    push cx           ; 需要 cl 時再壓棧
    ; ... 計算顯存地址 ...
    pop cx            ; 配對彈出
    mov es:[bx+si+1], cl
    add di, 1
    add si, 2
    jmp short next_char
excout:
    ret

核心原則

  • 棧平衡必須在所有控制流路徑上成立:每個 push 在同一函數內必須有配對 pop,無論 jmp/jcc/call/ret 如何跳轉。
  • 循環不變量:入口/出口棧深度一致。
  • 調試技巧:用 dbg 或 masm 觀察 SP/SS:SP 變化,斷點在 ret 前檢查棧頂是否為返回地址。

補充:字符串顯示更簡潔寫法

; ds:si 指向以 0 結尾的字符串
; es:di 目標顯存地址
print_str:
    lodsb             ; al=[si], si++
    test al, al
    jz done
    stosb             ; es:[di]=al, di++
    inc di            ; 跳過屬性字節 (或 stosw 寫入字符+屬性)
    jmp print_str
done:
    ret
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved