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 即正常。
push cx 將 cx 壓棧jcxz excout 若 cl==0 跳轉到 excoutexcout 標號下是 ret,沒有執行 pop cxcx,後續 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