16位汇编调试器-D命令实现

发布时间 2023-04-24 12:14:07作者: ylc0x01

对程序的调试一般有以下三种命令

  • D命令,内存值查看命令
  • T命令,单步中断与断点中断命令
  • U命令,反编译命令

以下是对D命令的实现,效果如图

image实现如下

;程序名称:
;功能:d命令
;输入段值,偏移,展示出段值偏移的从偏移起的128个字符
;不做跨段展示
;命令:
;d 段值:偏移
;=======================================
;执行步骤:
;1:提醒输入段值,可以为空,默认段值为数据段
;2:提醒输入偏移,可以为空,默认偏移为0000
;3:输入的段值转二进制,存入变量
;4:输入的偏移转二进制,存入变量
;5:进入循环
;循环次数计算规则:
;ffff-偏移<128的,输出实际字符个数
;ffff-偏移>128的,输出128个字符
;5->打印规则,cx记循环次数
;1:外层循环,输出换行,输出段值:偏移,输出空格,cx+1
;2:内层循环,输出字符
;2.1:偏移最小值>0,输出(偏移最小值*2)个空格
;2.2:正常输出16进制字符,每个字符用空格分隔
;2.3:输出字符的同时,将字符翻译存入到ascii变量中
;2.4:内循环完成后一次性输出
;3:ascii变量清空还原为空格符
;4:cx=打印的字符数,退出内循环循环
assume      cs:code,ds:data
;常量定义
blank = 20H;空格符
cr = 0dh;回车
data segment
    inputErr   db 'input err: 0~f $'    ;输入格式错误提示
    inputCs    db 'input cs: $'         ;段值输入提示
    inputIp    db 'input ip: $'         ;偏移输入提示
    csSegAsc   db ?,?,?,?               ;段值ascii码输入暂存变量
    ipSegAsc   db ?,?,?,?               ;偏移ascii码输入暂存变量
    minIp      db ?                     ;16进制最小位偏移
    csBit      dw ?                     ;段值
    ipBit      dw ?                     ;偏移
    loopCount  db 127                   ;循环次数,默认128
    ascii      db 16 dup(20H)           ;ascii码变量
               db '$'
    csAscBuff  db 4 dup(0),24h          ;给转换成的十进制数预留槽位
    ipAscBuff  db 4 dup(0),24h          ;给转换成的十进制数预留槽位
    bitBuff    db ?,?                   ;内存内容转ASCII码的预留槽
    memBit     db 0
    blankCount dw 0
data ends

code segment
    start:       
                 mov  ax,data                ;初始化数据段
                 mov  ds,ax

    ;执行步骤:
    ;1:提醒输入段值,可以为空,默认段值为数据段
                 MOV  DX,OFFSET inputCs
                 CALL DISPMESS
    ;1.1:段值最长4位,不足4位,前面补零
                 mov  si,-1
    csLoop:      
                 inc  si
                 cmp  si,4
                 jz   csInputOver
                 call getch                  ;从键盘上获取一个值
                 CMP  AL,cr                  ;是否输入回车
                 jz   csInputOver            ;是回车,段值处理完成,不足4位,补零
                 call ishex                  ;判断是否为16进制数
                 jc   soutInputErr           ;不是16进制数,输出错误信息
                 mov  csSegAsc[si],al        ;将输入数据挪入变量
                 call putch                  ;将该字符输出到屏幕
                 jmp  csLoop
    csInputOver: 
                 call newline                ;换行
                 MOV  DX,OFFSET inputIp
                 CALL DISPMESS
                 mov  si,-1
                 
    ;2:提醒输入偏移,可以为空,默认偏移为0000
    ipLoop:      
                 inc  si
                 cmp  si,4
                 jz   ipInputOver
                 call getch                  ;从键盘上获取一个值
                 CMP  AL,cr                  ;是否输入回车
                 jz   ipInputOver            ;是回车,段值处理完成,不足4位,补零
                 call ishex                  ;判断是否为16进制数
                 jc   soutInputErr           ;不是16进制数,输出错误信息
                 mov  ipSegAsc[si],al        ;将输入数据挪入变量
                 call putch                  ;将该字符输出到屏幕
                 jmp  ipLoop
    ipInputOver: 
    ;3:输入的段值转二进制,存入变量
                 mov  si,-1
                 XOR  bx,bx
    movCs:       
                 inc  si
                 cmp  si,4
                 jz   movCsOver              ;段值转换完成
                 mov  al,csSegAsc[si]        ;需要转二进制的ascii码数挪到AL中
                 CMP  AL,0                   ;比较0值,为0,转换完成
                 jz   movCsOver
                 call atobin                 ;将AL中的ASCII码转为二进制数据,存在AL的低4位中
    ;将转换好的二进制数挪到bx中
                 mov  cx,4
                 shl  al,cl                  ;将AL的低4位挪到高4位
    ;重复4次,将al高4位挪到di低4位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 jmp  movCs                  ;处理下一位ASCII码
    movCsOver:   
                 MOV  csBit,bx               ;将bx的值挪到变量中
                 mov  si,-1
                 XOR  bx,bx
                 XOR  ax,ax
    ;4:输入的偏移转二进制,存入变量
    movIp:       
                 inc  si
                 cmp  si,4
                 jz   movIpOver              ;段值转换完成
                 mov  al,ipSegAsc[si]        ;需要转二进制的ascii码数挪到AL中
                 CMP  AL,0                   ;比较0值,为0,转换完成
                 jz   movIpOver
                 call atobin                 ;将AL中的ASCII码转为二进制数据,存在AL的低4位中
                 mov  ah,al                  ;将al的值挪到ah中做备份
    ;将转换好的二进制数挪到bx中
                 mov  cx,4
                 shl  al,cl                  ;将AL的低4位挪到高4位
    ;重复4次,将al高4位挪到di低4位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 rcl  al,1                   ;左移1位到cf中
                 rcl  bx,1                   ;将cf的1位挪到bx的最后1位
                 jmp  movIp                  ;处理下一位ASCII码
    movIpOver:   
                 mov  minIp,ah               ;将ah的值挪到变量中
                 MOV  ipBit,bx               ;将bx的值挪到变量中

                 MOV  ax,csBit               ;设置需要输出的段值
                 mov  es,ax
    ;5:进入循环
    ;循环次数计算规则:
    ;ffff-偏移<128的,输出实际字符个数
    ;ffff-偏移>=128的,输出128个字符
                 mov  cx,-1                  ;初始化cx
                 mov  al,minIp               ;al+偏移的低4位
                 add  loopCount,al           ;循环次数挪到变量中
                 cmp  al,0
                 jz   outLoop
                 xor  ax,ax
                 mov  al,16                  ;计算空格循环个数
                 sub  al,minIp
                 mov  blankCount,ax
                 and  ipBit,0fff0h           ;清空si低4位
                 
    ;外循环
    outLoop:     
    ;1:外层循环,输出换行,输出段值:偏移,输出空格,cx+1
    ;输出 段值:偏移
    ;输出段值
                 mov  dx,cx
                 add  dx,1
                 cmp  loopCount,dl           ;判断循环次数
                 call newline
                 MOV  AX,csBit               ;转低8位
                 call ahtoasc
                 mov  csAscBuff[2],ah
                 mov  csAscBuff[3],al
                 MOV  AX,csBit
                 mov  al,ah
                 call ahtoasc
                 mov  csAscBuff[0],ah
                 mov  csAscBuff[1],al
                 push dx
                 MOV  dx,offset csAscBuff
                 mov  ah,9
                 int  21H
                 pop  dx
    ;输出:
                 xor  ax,ax
                 MOV  al,':'
                 call putch
    ;输出偏移
                 MOV  AX,ipBit               ;转低8位
                 add  ax,dx
                 call ahtoasc
                 mov  ipAscBuff[2],ah
                 mov  ipAscBuff[3],al
                 MOV  AX,ipBit
                 mov  al,ah
                 call ahtoasc
                 mov  ipAscBuff[0],ah
                 mov  ipAscBuff[1],al
                 MOV  dx,offset ipAscBuff
                 mov  ah,9
                 int  21H                    ;偏移输出完成
                 call putBlank               ;输出空格
                 MOV  di,-1
    ;5->打印规则,cx记循环次数
    ;2:内层循环,输出字符
    inLoop:      
                 inc  di                     ;内层计数器+1
                 mov  memBit,0               ;内存字节变量,初始化为0
                 cmp  di,16                  ;当次内存循环是否完成
                 jz   printAsc               ;内层循环完成,打印ASCII码
                 cmp  loopCount,cl           ;判断循环次数
                 jz   printAsc               ;打印asc码
                 mov  dl,cl
                 inc  dl
                 cmp  dl,minIp               ;对比偏移最小位
                 jb   print2Blank            ;输出空格
                 jmp  printBin               ;正常输出二进制数
    ;2.2:正常输出16进制字符,每个字符用空格分隔
    printBin:    
    ;2.3:输出字符的同时,将字符翻译存入到ascii变量中
    ;地址处的字节放入到AL中
                 inc  cx                     ;外层计数器+1
                 mov  si,cx                  ;cx为外层循环计数器,可做为偏移的增量指针
                 mov  bx,ipBit
                 MOV  al,es:[bx][si]
                 mov  memBit,al
    ;打印输出字符
                 call ahtoasc
                 mov  bitBuff[0],ah
                 mov  bitBuff[1],al
                 mov  al,bitBuff[0]
                 call putch
                 mov  al,bitBuff[1]
                 call putch
    ;判断是否为可见字符,否则用'.'代替
                 mov  al,memBit
                 call isVisible
                 jnc  bitOver                ;是可见字符,直接输入到变量中
                 MOV  al,'.'                 ;不是可见字符,用.号代替
    bitOver:     
                 MOV  ascii[di],al
                 jmp  printBlank             ;输出一个空格
    ;2.1:偏移最小值>0,输出(偏移最小值*2)个空格
    print2Blank: 
                 inc  cx                     ;外层计数器+1
                 call putBlank               ;输出一个空格
                 call putBlank               ;输出一个空格
    printBlank:  
                 call putBlank               ;输出一个空格
                 jmp  inLoop
   
    ;2.4:内循环完成后一次性输出
    printAsc:    
                 call putBlank               ;输出一个空格
    ;输出ascii码
                 cmp  loopCount,cl           ;判断循环次数
                 jz   outBlank
    printAsc1:   
                 MOV  dx,offset ascii
                 mov  ah,9
                 int  21H
                 cmp  loopCount,cl           ;判断循环次数
                 jz   over
    ;ascii变量清空还原为空格符
                 mov  ascii[0],20h
                 mov  ascii[1],20h
                 mov  ascii[2],20h
                 mov  ascii[3],20h
                 mov  ascii[4],20h
                 mov  ascii[5],20h
                 mov  ascii[6],20h
                 mov  ascii[7],20h
                 mov  ascii[8],20h
                 mov  ascii[9],20h
                 mov  ascii[10],20h
                 mov  ascii[11],20h
                 mov  ascii[12],20h
                 mov  ascii[13],20h
                 mov  ascii[14],20h
                 mov  ascii[15],20h
                 jmp  outLoop                ;调回到外层循环
    ;4:cx=打印的字符数,退出内循环循环
    soutInputErr:
                 call newline
                 MOV  DX,OFFSET inputErr
                 CALL DISPMESS
                 jmp  over
    outBlank:    
                 mov  ax,blankCount
                 cmp  al,0
                 mov  si,0
                 jnz  outBlank1
                 jmp  printAsc1
    outBlank1:   
                 call putBlank               ;输出一个空格
                 call putBlank               ;输出一个空格
                 call putBlank               ;输出一个空格
    outBlank2:   
                 inc  si
                 cmp  si,blankCount
                 jz   printAsc1
                 call putBlank               ;输出一个空格
                 call putBlank               ;输出一个空格
                 call putBlank               ;输出一个空格
                 jmp  outBlank2
                 
    over:        
                 mov  ax,4c00h               ;dos中断
                 int  21H
    ;显示由DX所指的提示信息,其他子程序说明信息略
DISPMESS PROC
                 MOV  AH,9
                 INT  21H
                 RET
DISPMESS ENDP
    ;接受一个字符但不显示,存到al中
    ;入口参数:标志输入设备
    ;出口参数:al
getch PROC
                 MOV  AH,8                   ;接受一个字符但不显示,
                 INT  21H
                 RET
getch ENDP
    ;----------------------------------------------
    ;判断是不是16进制数
    ;入口参数:AL
    ;出口参数:CF标,CF置1不是16进制数,CF置0位是16进制数
ishex PROC
                 CMP  AL,'0'
                 JB   ISHEX2
                 CMP  AL,'9'+1
                 JB   ISHEX1
                 CMP  AL,'A'
                 JB   ISHEX2
                 CMP  AL,'F'+1
                 JB   ISHEX1
                 CMP  AL,'a'
                 JB   ISHEX2
                 CMP  AL,'f'+1
    ISHEX1:      CMC
    ISHEX2:      RET
ishex ENDP
    ;----------------------------------------------
    ;-----------------------------
    ;显示一个字符
    ;入口参数:al
    ;出口参数:标志输出设备
putch PROC
                 PUSH DX
                 MOV  DL,AL
                 MOV  AH,2
                 INT  21H
                 POP  DX
                 RET
putch ENDP
    ;-----------------------------
    ;-----------------------------
    ;显示一个字符
    ;入口参数:al
    ;出口参数:标志输出设备
dbg PROC
                 PUSH DX
                 call newline
                 MOV  DL,'d'
                 MOV  AH,2
                 INT  21H
                 POP  DX
                 RET
dbg ENDP
    ;-----------------------------
    ;-----------------------------
    ;显示一个字符
    ;入口参数:al
    ;出口参数:标志输出设备
putBlank PROC
                 PUSH DX
                 MOV  DL,20H
                 MOV  AH,2
                 INT  21H
                 POP  DX
                 RET
putBlank ENDP
    ;-----------------------------
    ;----------------------------------------------
    ;子程序名:newline
    ;功能:形成回车和换行(光标移到下一行首)
    ;入口参数:无
    ;出口参数:无
    ;说明:通过显示回车符形成回车,通过显示换行符形成换行
newline proc
                 push ax
                 push dx
                 mov  dl,0dh                 ;回车符的ASCII码
                 mov  ah,2
    ;显示回车符
                 int  21h
                 mov  dl,0ah                 ;换行符的ASCII码
                 mov  ah,2
    ;显示换行符
                 int  21h
                 pop  dx
                 pop  ax
                 ret
newline endp
    ;----------------------------------------------
    ;----------------------------------------------
    ;ascii转2进制的值
    ;入口参数:al=需要转二进制值的ascii码
    ;出口参数:al=已转好的2进制值
atobin PROC
                 SUB  AL,30H
                 CMP  AL,9
                 JBE  ATOBIN1
                 SUB  AL,7
                 CMP  AL,15
                 JBE  ATOBIN1
                 SUB  AL,20H
    ATOBIN1:     RET
atobin ENDP
    ;----------------------------------------------
    ;子程序名:isVisible
    ;功能:判断字符是否可见
    ;入口参数:al=需要判断的字符
    ;出口参数:无
    ;说明:CF标,CF置1不是可见字符,CF置0位是可见字符
isVisible proc
                 CMP  AL,' '
                 JB   isVisible2
                 CMP  AL,'~'+1
    isVisible1:  CMC
    isVisible2:  RET
isVisible endp
    ;----------------------------------------------
    ;子程序名称:ahtoasc
    ;功能:把8位二进制数转换为2位十六进制数的ASCII
    ;入口参数:AL=欲转换的8位二进制数
    ;出口参数:AH=十六进制数高位的ASCII码,AL=十六进制数低位的ASCII码
    ;其他说明:1.近过程,2.除AX寄存器外,不影响其他寄存器3.调用了htoasc实现十六进制到ASCII码转换
    ;=======================================
ahtoasc PROC
                 mov  ah,al                  ;al复制到ah
                 shr  al,1                   ;AL右移4位
                 shr  al,1
                 shr  al,1
                 shr  al,1
                 call htoasc                 ;调用子程序
                 xchg ah,al                  ;al,ah对调
                 call htoasc                 ;调用子程序
                 RET
ahtoasc ENDP
    ;子程序名称:htoasc
    ;功能:一位十六进制数转换为ASCII
    ;入口参数:al=待转换的十六进制数,ds:bx=存放转换得到的ASCII码串的缓冲区首地址
    ;出口参数:出口参数:al=转换后的ASCII
    ;其他说明:无
    ;=======================================
htoasc PROC
                 and  al,0fh                 ;清空高四位
                 add  al,30h                 ;+30h
                 cmp  al,39h                 ;小于等于39H
                 jbe  htoascl                ;
                 add  al,7h                  ;+7H
    htoascl:     
                 ret
htoasc ENDP
code ends
    end     start