; The following routines execute the functions of the debugger.
;
; dump
;
; dumps contents of memory
;
; syntax:
; d [
[ ] ]
;
; the parameters are optional. The default value for the starting
; address is the current address using the current source segment
; register. The default value for the count is 64. If an address
; is given, the count of bytes that will be dumped is the difference
; between the 2 addresses. The maximum number of bytes that will
; be dumped is 64K.
;
dump proc near ; dump the contents of memory
mov bx,(offset buffer)+1 ; point at 1st char after
; command
mov dx,segstore ; get segment address
mov es,dx ; put segment address into es
mov ah,0 ; get source address
call get_param ; get first parameter
jnc dump_01 ; no errors detected
jmp dump_err ; error in hex param
dump_01:
test ah,ah ; if there are no more params
jne do_dump ; dump memory
mov current_src,dx ; save current source address
mov dx,es ; save away source segment
mov segstore,dx
mov dx,current_src ; restore dx value
mov ah,1 ; get dest param
call get_param ; get address or count
jnc dump_02 ; no errors detected
jmp dump_err ; error in hex param
dump_02:
test ah,ah ; if there are more params
je dump_1 ; process them
mov current_count,DEF_COUNT ; dump 64 bytes as default
jmp do_dump
dump_1:
push ax ; use ax as a temp register
mov ax,es ; save away dest segment
mov segstore+2,ax
pop ax ; restore ax value
test al,al ; see if param is count
je dump_addr ; al = 0 .. dx has address
mov current_count,dx ; param was count .. save away
jmp do_dump
dump_addr:
cmp current_src,dx ; param is an addr..get count
ja dump_2 ; there is no wrap
mov current_dest,dx ; save address away
sub dx,current_src ; get count into dx
mov current_count,dx ; save count
jmp do_dump
dump_2:
mov current_count,DEF_COUNT ; dump only default number
do_dump:
mov ax,segstore ; get source segment addr
mov es,ax ; value into segment register
mov bx,current_src ; get current offset into bx
mov cx,current_count ; get count of bytes to dump
dump_start:
mov dx,current_src ; es:dx has current addr
call pr_addr ; print current address
mov dx,offset SPACE_2 ; skip 2 spaces
call prstr
mov dx,16 ; only print 16 chars per line
dump_byte:
mov al,es:[bx] ; get data byte into al
call pr_2hex ; output al as 2 hex digits
mov al,SPACE ; print space between bytes
call putc
inc bx ; point to next byte to print
dec cx ; decrease count
je dump_done ; done if count is zero
dec dx ; see if time to start newline
jne dump_byte ; if not then print next byte
dump_ascii: ; print out line in ascii
mov al,SPACE ; print 1 space
call putc
mov bx,current_src ; get beginning line addr
push cx ; save away current count
mov cx,16 ; always display 16 chars here
dump_char:
mov al,es:[bx] ; get data byte into al
inc bx ; point at next char
cmp al,SPACE ; see if char is printable
jl dump_dot ; print a dot instead if not
cmp al,7fh ; printable char?
jl dump_char1 ; if so .. print it
dump_dot:
mov al,'.' ; print a dot instead of char
dump_char1:
call putc ; print the char out
loop dump_char ; print 16 chars out
pop cx ; restore current count
dump_newline: ; start a new line
mov dx,offset NEWLINE ; print CR,LF
call prstr
mov current_src,bx ; update source addr pointer
sub current_count,16 ; one line less to dump
jmp dump_start
dump_done:
; print last line of ascii
mov bx,current_src ; get addr of beg of line
mov cx,current_count ; get count of chars left
and cx,0fh ; must be less than 16 chars
je dump_last ; if 16 left then dump all
mov al,16 ; calculate how many spaces to
sub al,cl ; fill to get to ascii section
mov cl,3
mul cl ; 3 positions per byte dumped
mov cx,ax ; print out enough spaces
mov al,SPACE
dump_spfill:
call putc
loop dump_spfill
dump_last:
mov cx,current_count ; restore count of chars left
mov al,SPACE ; print 1 space
call putc
dump_lchar:
mov al,es:[bx] ; get data byte into al
inc bx ; point at next char
cmp al,SPACE ; see if char is printable
jl dump_ldot ; print a dot instead if not
cmp al,7fh ; printable char?
jl dump_lchar1 ; if so .. print it
dump_ldot:
mov al,'.' ; print a dot instead of char
dump_lchar1:
call putc ; print the char out
loop dump_lchar ; print remaining chars out
mov current_src,bx ; update source addr pointer
mov current_count,DEF_COUNT ; set default count
mov dx,offset NEWLINE ; print CR,LF
call prstr
dump_ret:
stc
ret
dump_err:
mov dx,offset SYNTAX_MES
call prstr
mov dx,offset S_DUMP
call prstr
jmp dump_ret
dump endp near
;
; enter
;
; allow user to enter byte values into memory.
;
; syntax:
; e [ ]
;
; the address operand is optional. If there is no address given, the
; current source address is used as the place to start data entry.
;
enter proc near
mov bx,(offset buffer)+1 ; point at 1st char after
; command
mov dx,segstore ; get current segment addr
mov es,dx ; put into es register
mov ah,0 ; get address
call get_param ; get first parameter
jnc e_1 ; if not error
jmp enter_err ; error in hex param
e_1:
test ah,ah ; if there are no more params
jne do_enter ; allow data entry
mov current_src,dx ; save current source address
mov dx,es ; save away source segment
mov segstore,dx
do_enter:
mov ax,segstore ; get source segment addr
mov es,ax ; value into segment register
mov bp,current_src ; get current offset into bx
enter_start:
mov dx,bp ; es:dx has current addr
call pr_addr ; print current address
mov dx,offset SPACE_2 ; skip 2 spaces
call prstr
enter_byte:
mov al,es:[bp] ; get data byte into al
call pr_2hex ; output al as 2 hex digits
mov dx,offset DENTRY ; data entry after dot
call prstr
mov ah,1 ; do not convert to UC
call get_command ; get data into command buf
mov bx,offset buffer ; point at beginning of buffer
mov al,byte ptr [bx] ; get first char into al
cmp al,'.' ; stop char
je enter_done ; finish with routine
cmp al,'"' ; this is a string
je enter_string ; put in memory as such
cmp byte ptr [bx],0 ; check for end of line
jne enter_conv ; not end of line
inc bp ; point to next data byte
mov current_src,bp ; save current address
jmp enter_start ; next entry line
enter_conv: ; convert buffer to UC
push bx ; save bx register
enter_conv1:
cmp byte ptr [bx],0 ; while not end of command
je enter_conv3
cmp byte ptr [bx],60h ; if not lower case then
jl enter_conv2 ; next char
and byte ptr [bx],0dfh ; else convert to UC
enter_conv2:
inc bx ; next char in buffer
jmp enter_conv1
enter_conv3:
pop bx ; restore bx register
enter_hex: ; data must be in hex format
call get_hex ; get a byte value from buf
; bx points at buffer
jc enter_start ; error in data entry
mov es:[bp],dl ; dx contains 16 bit value
inc bp ; point to next data byte
mov current_src,bp ; save current address
cmp byte ptr [bx],0 ; check for end of line
je enter_start ; next entry line
jmp enter_hex
enter_string: ; data is ascii data
inc bx ; point at next char in buf
enter_str1:
cmp byte ptr [bx],0 ; check for end of line
je enter_start ; next entry line
mov al,byte ptr [bx] ; put hex char into memory
mov es:[bp],al
inc bp ; next location in memory
mov current_src,bp ; save current address
inc bx ; next char in buffer
jmp enter_str1
enter_done:
stc
ret
enter_err:
mov dx,offset SYNTAX_MES
call prstr
mov dx,offset S_ENTER
call prstr
jmp enter_done
enter endp near
;
; fill
;
; fill memory with specified values.
;
; syntax:
; f []
;
; All parameters must be present. If there is more than one value
; given, the values are put into memory consecutively until the
; ending address or the maximum count is reached.
;
fill proc near
mov bx,(offset buffer)+1 ; point at 1st char after
; command
mov dx,segstore ; get segment address
mov es,dx ; put segment address into es
mov ah,0 ; get source address
call get_param ; get first parameter
jnc fill_01 ; no errors detected
jmp fill_err ; error in hex param
fill_01:
test ah,ah ; if there are more params
je fill_02 ; then process them
jmp fill_err ; else error
fill_02:
mov current_src,dx ; save current source address
mov dx,es ; save away source segment
mov segstore,dx
mov dx,current_src ; restore dx value
mov ah,1 ; get dest param
call get_param ; get address or count
jnc fill_03 ; no errors detected
jmp fill_err ; error in hex param
fill_03:
test ah,ah ; if there are more params
je fill_1 ; process them
jmp fill_err ; else error
fill_1:
push ax ; use ax as a temp register
mov ax,es ; save away dest segment
mov segstore+2,ax
pop ax ; restore ax value
test al,al ; see if param is count
je fill_addr ; al = 0 .. dx has address
mov ax,dx ; get count as temporary
add ax,current_src ; test to see if wrap around
jno fill_10 ; no overflow .. continue
jmp fill_err ; error in count .. too big
fill_10:
mov current_count,dx ; param was count .. save away
jmp fill_value ; get memory fill values
fill_addr:
cmp current_src,dx ; param is an addr..get count
ja fill_2 ; there is no wrap
mov current_dest,dx ; save address away
sub dx,current_src ; get count into dx
mov current_count,dx ; save count
jmp fill_value
fill_2:
jmp fill_err ; wrap on segment is an error
fill_value:
mov byte ptr data_buf,0 ; zero count of data bytes
mov bp,(offset data_buf)+1 ; address of data buffer
cmp byte ptr [bx],0 ; check for end of line
jne fill_hex ; not end of line
jmp fill_err ; error .. no fill values
fill_hex:
call get_hex ; get a byte value from buf
; bx points at buffer
jc fill_err ; error in data entry
mov ds:[bp],dl ; dx contains 16 bit value
inc bp ; point to next data byte
inc data_buf ; count of data bytes
cmp byte ptr data_buf,9 ; too many bytes to fill
je fill_err
cmp byte ptr [bx],0 ; check for end of line
jne fill_hex ; get all data bytes on line
do_fill: ; fill memory with data bytes
mov ax,segstore ; get segment address
mov es,ax
mov bx,current_src ; get destination address
mov cx,current_count ; get count of bytes to fill
mov dl,data_buf ; get fill sequence count
mov bp,(offset data_buf)+1 ; get data buf addr
do_fill_1:
mov al,ds:[bp] ; get data byte
mov es:[bx],al ; put data byte into memory
inc bp ; point to next data byte
inc bx ; next memory location
dec dl ; decr count of fill seq count
jne do_fill_2 ; if not zero then continue
mov dl,data_buf ; otherwise start again
mov bp,(offset data_buf)+1 ; at beg of data buffer
do_fill_2:
loop do_fill_1 ; do until count is zero
mov current_count,DEF_COUNT ; fix count to default
jmp fill_done
fill_err:
mov dx,offset SYNTAX_MES
call prstr
mov dx,offset S_FILL
call prstr
fill_done:
stc
ret
fill endp near
;
; input
;
; get value from i/o port and then allow data entry.
;
; syntax:
; i
;
; The port address is optional. If the port address is not given,
; the last port address is used.
;
input proc near
mov bx,(offset buffer)+1 ; point at 1st char after
; command
cmp byte ptr [bx],0 ; see if address was entered
je do_input ; if none use old port addr
call get_hex ; get 16 bit port address
jc input_err ; bad port value
mov current_port,dx ; save current source address
do_input:
mov ax,current_port ; print port address
call pr_4hex ; print current address
mov dx,offset SPACE_2 ; skip 2 spaces
call prstr
input_byte:
mov dx,current_port ; get port address
in al,dx ; get data byte into al
call pr_2hex ; output al as 2 hex digits
mov dx,offset DENTRY ; data entry after =
call prstr
mov ah,0 ; convert to UC
call get_command ; get data into command buf
mov bx,offset buffer ; point at beginning of buffer
cmp byte ptr [bx],0 ; see if anything entered
je input_done ; done if nothing
call get_hex ; get a byte value from buf
; bx points at buffer
jc input_done ; error in data entry
mov ax,dx ; dx contains 16 bit value
mov dx,current_port ; get port address
out dx,al ; output new value to port
input_done:
stc
ret
input_err:
mov dx,offset SYNTAX_MES
call prstr
mov dx,offset S_INPUT
call prstr
jmp input_done
input endp near
;
; move
;
; syntax:
; m
;
; All the parameters are required. The last parameter must a count.
;
move proc near
mov bx,(offset buffer)+1 ; point at 1st char after
; command
mov dx,segstore ; get segment address
mov es,dx ; put segment address into es
mov ah,0 ; get source address
call get_param ; get first parameter
jnc move_01 ; no errors detected
jmp move_err ; error in hex param
move_01:
test ah,ah ; if there are more params
je move_02 ; then process them
jmp move_err ; else error
move_02:
mov current_src,dx ; save current source address
mov dx,es ; save away source segment
mov segstore,dx
mov ah,0 ; get dest address
call get_param ; get address
jnc move_03 ; no errors detected
jmp move_err ; error in hex param
move_03:
test ah,ah ; if there are more params
je move_1 ; process them
jmp move_err ; else error
move_1:
mov ax,es ; save away dest segment
mov segstore+2,ax
mov current_dest,dx ; save address away
mov ah,1 ; get count of bytes to move
call get_param ; get address or count
jnc move_2 ; no errors detected
jmp move_err ; error in hex param
move_2:
test ah,ah ; if there are more params
je move_3 ; process them
jmp move_err ; else error
move_3:
test al,al ; see if param is count
jne move_4 ; then process command
jmp move_err ; else error
move_4:
mov current_count,dx ; save count of bytes
test dx,dx ; if count is zero then error
je move_err
do_move:
mov si,current_src ; get current source addr
mov di,current_dest ; get destination addr
mov cx,current_count ; get count of bytes to move
mov ax,segstore+2 ; get dest segment
mov es,ax ; put into es register
mov ax,segstore ; get source segment
push ds ; save current ds
mov ds,ax ; put in source segment
cld ; clear dir flag, auto incr
rep movsb ; move block of data
pop ds ; restore data segment reg
mov current_count,DEF_COUNT ; fix count
jmp move_done
move_err:
mov dx,offset SYNTAX_MES
call prstr
mov dx,offset S_MOVE
call prstr
move_done:
stc
ret
move endp near
;
; output
;
; output byte value to port.
;
; syntax:
; o
;
; The port address and value must be given.
;
output proc near
mov bx,(offset buffer)+1 ; point at 1st char after
; command
cmp byte ptr [bx],0 ; see if address was entered
je output_err ; if none use old port addr
call get_hex ; get 16 bit port address
jc output_err ; bad port value
mov di,dx ; save current source address
cmp byte ptr [bx],0 ; see if data byte was entered
je output_err ; if none use old port addr
call get_hex ; get 8 bit data
jc output_err ; bad hex value
mov ax,dx ; get value into ax
mov dx,di ; restore from temp register
mov current_port,dx ; save away port address
out dx,al ; output 8 bit value to port
output_done:
stc
ret
output_err:
mov dx,offset SYNTAX_MES ; print syntax error message
call prstr
mov dx,offset S_OUTPUT
call prstr
jmp output_done
output endp near
;
; jump
;
; Start execution at address.
;
; Syntax:
; j
;
; If the address parameter is not given, execution will start at
; the current source address.
;
jump proc near
mov bx,(offset buffer)+1 ; point at 1st char after
; command
mov dx,segstore ; get current segment addr
mov es,dx ; put into es register
mov ah,0 ; get address
call get_param ; get first parameter
jnc j_1 ; if not error
jmp jump_err ; error in hex param
j_1:
test ah,ah ; if there are no more params
jne do_jump ; jump to address
mov current_src,dx ; save current source address
mov dx,es ; save away source segment
mov segstore,dx
do_jump:
; note that the code jumped to must use a far return
; to get back into the debugger
call far ptr long_jump ; dummy routine to do jump
; should return here if dest
; code used a far return
jump_done:
stc
ret
jump_err:
mov dx,offset SYNTAX_MES ; print syntax error message
call prstr
mov dx,offset S_JUMP
call prstr
jmp jump_done
jump endp near
;
; long_jump
;
; dummy routine to do far jump to code. Only used by 'jump' proc.
;
long_jump proc far
push word ptr segstore ; push segment addr
push word ptr current_src ; push offset addr
ret ; go to that address
long_jump endp far
;
; quit
;
; This command is useful only under MSDOS and use primarily for
; testing. The debugger is exited and control is returned to the
; operating system.
;
quit proc near
clc
ret
quit endp near
; These routines are used by the functions above
;
; get_param
;
; get the next parameter from the command buffer
;
; on entry:
; bx points to the place to start parsing
; ah = 0 for a source operand, ah = 1 for a dest operand
; on return:
; al contains 0 if the parameter was an addr
; and contains 0ffh if the parameter was a count
; note that a count value can only be returned
; if the parameter was a destination parameter (ah = 1)
; ah contains 0ffh if there are no more params on
; the command line. Otherwise ah = 0.
; bx points to position just past parameter parsed
; dx contains the value of the parameter
; es contains the segment value of the parameter
; the carry flag will be set if there was an error in
; the hex value, otherwise it is cleared
; all other registers are unchanged
; usage:
; call get_param
;
get_param proc near ; get the next parameter
cmp byte ptr [bx],SPACE ; scan past leading delimiters
jne gp_1
inc bx ; if space then next char
jmp get_param
gp_1:
cmp byte ptr [bx],0 ; check for end of command
je gp_noparam
cmp byte ptr [bx],'L' ; check for count
je gp_count ; if so then get count value
call get_hex ; get a hex value into dx
jc gp_err ; error if carry flag set
cmp byte ptr [bx],':' ; if this value is a segment
jne gp_adone ; got an addr in dx
mov es,dx ; save source seg reg
gp_addr:
inc bx ; skip past ':'
call get_hex ; get the addr value into dx
jc gp_err ; error if carry flag set
gp_adone:
mov ax,0 ; value in dx is address
jmp gp_ret
gp_count: ; get a count value into dx
test ah,ah ; param must be dest
je gp_err ; error in command line
inc bx ; skip past 'L'
call get_hex ; get hex count value into dx
jc gp_err ; error if carry flag set
mov ah,0 ; not end of command line
mov al,0ffh ; mark as count value
jmp gp_ret
gp_noparam:
mov ah,0ffh ; mark as end of line
gp_ret:
clc ; clear error flag
ret
gp_err:
stc ; mark as error
ret
get_param endp near
;
; get_hex
;
; gets a 16 bit hex value from the command buffer.
;
; on entry:
; bx points to the current position in the command buffer
; on return:
; bx points past hex value
; dx contains hex value
; all other registers are unchanged
; carry bit is set if there was an error in hex value
; otherwise carry bit is cleared
; usage:
; call get_hex
;
get_hex proc near ; get hexidecimal value
push ax ; save value in ax
push cx ; save value in cx
xor ax,ax ; zero ax
xor dx,dx ; clear value in dx
gh_0:
cmp byte ptr [bx],SPACE ; scan past leading delimiters
jne gh_1
inc bx ; if space then next char
jmp gh_0
gh_1:
cmp byte ptr [bx],'0' ; check that first char is hex
jl gh_err ; if not then command error
cmp byte ptr [bx],'F'
jg gh_err
cmp byte ptr [bx],'A'
jge gh_char ; must be A-F
cmp byte ptr [bx],'9'
jg gh_err ; between 9 and A
gh_number: ; must be 0-9
mov al,[bx]
sub al,'0' ; adjust to binary value
jmp gh_sum
gh_char:
mov al,[bx]
sub al,'A'-10 ; adjust to binary value
gh_sum:
mov cl,4
sal dx,cl ; multiply prev value by 16
add dx,ax ; add in new digit
; keep doing until non-hex char is reached
inc bx ; point at next char
cmp byte ptr [bx],'0' ; check that first char is hex
jl gh_done ; if not then command error
cmp byte ptr [bx],'F'
jg gh_done
cmp byte ptr [bx],'A'
jge gh_char ; must be A-F
cmp byte ptr [bx],'9'
jg gh_done ; between 9 and A
jmp gh_number ; char is between 0-9
gh_done:
pop cx ; restore value in cx
pop ax ; restore value in ax
clc
ret
gh_err:
pop cx ; restore register values
pop ax
stc ; set error flag
ret
get_hex endp near
;
; pr_addr
;
; print current address contained in es:dx.
;
; on entry:
; es contains the segment value
; dx contains the offset
; on return:
; no registers are affected
; usage:
; call pr_addr
;
pr_addr proc near
push ax ; save ax register
mov ax,es ; print segment value first
call pr_4hex ; print out 4 digit hex value
mov al,':' ; print separating colon
call putc
mov ax,dx ; print the offset
call pr_4hex ; print value in ax
pop ax ; restore value in ax
ret
pr_addr endp near
;
; pr_4hex
;
; print out address as 4 hex digits.
;
; on entry:
; ax contains 16 bit value to print
; on return:
; no registers are changed
; usage:
; call pr_4hex
;
pr_4hex proc near
push ax ; save register
mov al,ah ; most signif digits first
call pr_2hex ; print out 2 hex digits
pop ax ; get orig value back
call pr_2hex
ret
pr_4hex endp near
;
; pr_2hex
;
; prints 8 bit value out as 2 hex digits.
;
; on entry:
; al contains 8 bit value to print
; on return:
; no registers are affected
; usage:
; call pr_2hex
;
pr_2hex proc near
push cx ; save away registers
push ax
mov ah,al ; store al in ah
mov cl,4 ; shift value into low nibble
shr al,cl
call pr_hex ; print out hex digit
mov al,ah ; get orig value back in al
and al,0fh ; print second hex digit
call pr_hex ; print hex digit
pop ax ; restore registers
pop cx
ret
pr_2hex endp near
;
; pr_hex
;
; print out value in low nibble of al as a hex digit.
;
; on entry:
; al contains value from 0-15
; on return:
; no registers are changed
; usage:
; call pr_hex
;
pr_hex proc near
push ax
and al,0fh ; get low nibble
cmp al,10 ; 0-9 or A-F ?
jl ph_isdigit ; 0-9
add al,'A'-10 ; make the value ascii
jmp ph_printit
ph_isdigit:
add al,'0' ; make value ascii
ph_printit:
call putc ; print char
pop ax
ret
pr_hex endp near