
;	The following routines execute the functions of the debugger.


;
;	dump
;
;	dumps contents of memory
;
;	syntax:
;		d [ <address> [ <address or count> ] ]
;
;	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 [ <address> ]
;
;	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 <address> <address or count> <value> [<value>]
;
;	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 <port address>
;
;	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 <source address> <dest address> <count>
;
;	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 <port address> <value>
;
;	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 <address>
;
;	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



