1. 初识DEBUG:汇编调试的瑞士军刀
第一次接触DEBUG是在大学计算机系的实验室里。记得那天教授在黑板上写下一段简单的加法汇编程序,但运行结果总是出错。正当我对着屏幕发愣时,实验室助教走过来敲了几下键盘,屏幕上突然跳出一堆十六进制数字和寄存器状态——这就是我与DEBUG的初次相遇。
DEBUG是DOS/Win9x时代内置的机器级调试工具,虽然界面简陋(只有横线"-"作为提示符),但功能强大到令人惊叹。它可以直接查看和修改CPU寄存器、内存数据,还能单步执行机器指令。就像外科医生的手术刀,DEBUG能让我们看到程序最底层的运行状态。
初学者常有的困惑是:为什么要在黑乎乎的调试器里看晦涩的机器码?举个例子,当你写的加法程序输出错误结果时,高级语言调试器可能只会告诉你"结果不对",而DEBUG能让你看到:
- AX寄存器里的加数是否正确
- 加法指令执行后的标志位变化
- 内存中变量的真实存储形式
2. 环境准备:启动DEBUG的三种姿势
2.1 纯命令行模式
在DOS或CMD中直接输入debug回车,会进入交互界面:
C:\> debug -这时可以手动输入汇编指令,适合练习基础命令。
2.2 加载可执行文件
调试已有的.com或.exe文件:
debug myprog.exe文件会被加载到内存CS:0100处,BX:CX寄存器会记录文件长度。
2.3 重定向输入脚本
把调试命令写在文本文件中(如script.txt),然后:
debug < script.txt > log.txt我常用这种方式批量测试,特别是需要反复验证某个功能时。
注意:Win10之后系统不再内置DEBUG,需要自行下载debug.exe或使用DOSBox模拟器
3. 寄存器侦查兵:R命令实战
3.1 查看所有寄存器
输入-r会显示完整的寄存器状态:
AX=0000 BX=0000 CX=004A DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=073F ES=073F SS=073F CS=073F IP=0100 NV UP EI PL NZ NA PO NC最后一行是标志寄存器:
- NV(溢出) UP(方向) EI(中断)
- PL(符号) NZ(零) NA(辅助进位)
- PO(奇偶) NC(进位)
3.2 修改寄存器值
修改AX寄存器的值:
-r ax AX 0000 :1234 <- 输入新值修改标志寄存器(注意顺序任意):
-r f NV UP EI PL NZ NA PO NC - ov di4. 内存侦探:D/E命令组合拳
4.1 查看内存数据
-d命令的多种用法:
-d # 查看默认地址 -d ds:100 # 查看DS段偏移100H -d 073f:100 110 # 查看073F:100到110 -d 100 L20 # 查看100H开始的32字节输出格式:
073F:0100 41 77 61 72 64 20 53 6F-66 74 77 61 72 65 49 42 Award SoftwarIB 073F:0110 4D 20 43 4F 4D 50 41 54-49 42 4C 45 20 34 38 36 M COMPATIBLE 486右边是ASCII字符显示,不可见字符显示为点号。
4.2 修改内存数据
-e命令两种模式:
- 批量写入:
-e 100 'A'+1A+2B- 交互式修改(空格键切换地址):
-e 100 073F:0100 41.42 <- 修改为42 073F:0101 77. <- 不修改按空格5. 程序手术刀:A/U/T三连击
5.1 编写汇编指令(A)
-a 100 073F:0100 mov ax,1234 073F:0103 mov bx,ax 073F:0105按回车结束输入。DEBUG支持8086基础指令集,但要注意:
- 数字默认是十六进制
- 十进制数需加D后缀(如100D)
5.2 反汇编查看(U)
-u 100 106 073F:0100 B83412 MOV AX,1234 073F:0103 89C3 MOV BX,AX左边是指令地址,中间是机器码,右边是汇编指令。
5.3 单步执行(T)
-t=100 # 从100H执行一条指令执行后会显示所有寄存器变化。要执行多条指令:
-t=100 2 # 从100H执行两条指令6. 断点大师:G命令的妙用
设置断点调试:
-g=100 110 120 # 在110H和120H设断点当执行到断点时,会显示寄存器状态。几个技巧:
- 最多设置10个断点
- 断点只生效一次
- 不加参数则执行到程序结束
曾经调试一个循环程序时,我在循环开始和结束处各设一个断点,通过比较寄存器值快速定位了计数错误。
7. 文件操作:N/L/W三联
7.1 命名文件(N)
-n myfile.com7.2 写入文件(W)
先设置文件长度:
-r cx CX 0000 :100 # 文件长度256字节然后写入:
-w 100 # 从100H开始写入7.3 加载文件(L)
-l 200 # 加载到DS:200H8. 内存操作:F/C/M高级技巧
8.1 批量填充(F)
-f 100 L10 41 # 用41H填充16字节 -f 200 L8 'A' 'B' # 交替填充A/B8.2 内存比较(C)
-c 100 L10 200 # 比较两段内存输出显示差异位置:
073F:0100 41 42 073F:02008.3 内存移动(M)
-m 100 L20 300 # 移动32字节到300H9. 端口操作:I/O指令
9.1 读取端口
-o 70 10 # 向70H端口写10H -i 71 # 从71H端口读9.2 十六进制计算(H)
-h 1A 2B # 计算1A+2B和1A-2B 0045 FFEF # 显示和与差10. 实战案例:调试加法程序
假设我们有以下有问题的汇编程序:
mov ax,5 mov bx,10 add ax,bx ; 应该得到15(0FH),但结果是14H调试步骤:
- 用A命令输入程序
- U命令确认指令正确
- T命令单步执行
- 发现ADD执行后AX=0014(实际应为000F)
- 检查发现误将10D输成了10H
11. 避坑指南:常见问题解决
- 地址越界:D命令显示????时表示超出段限
- 无效指令:U命令反汇编出乱码可能是数据区
- 断点失效:确保断点设在指令起始处
- 文件大小:W命令前必须正确设置BX:CX
- 标志位混淆:NV≠OV,PL≠NG
记得有次我花了两个小时找BUG,最后发现是错把DI当SI用。DEBUG虽然原始,但正是这种赤裸裸的机器级视角,能让我们真正理解计算机的工作原理。