; 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