	PAGE	,132
	TITLE	LOOK - LOOK AT MEMORY
	SUBTTL	Look At Memory In Any Segment
;									      ;
;*****************************************************************************;
;									      ;
;		      John R. Pulliam	 Version 2/21/84		      ;
;									      ;
;	       For Columbia Data Product Computers and Compatibles	      ;
;			  Released to Public Domain			      ;
;									      ;
;*****************************************************************************;
;									      ;
;
;  DOS CALL INT 21H FUNCTIONS USED
;
;
;	AH = 01H => Input One Character From Keyboard
;		    AL = Ascii Character Received
;
;	AH = 02H => Display One Character On CRT
;		    DL = Ascii Character to Display
;
;	AH = 09H => Display Message on CRT
;		    DS = Segment of Message
;		    DX = Offset Address of Message
;
;	AH = 0CH &
;	AL = 0AH => Clear Then Fetch Keyboard Buffer
;		    DI	   = Buffer First Word Address
;		    [DI]   = Number Of Characters Wanted To Input
;		    [DI+1] = Number Of Characters Actually Received
;		    [DI+2] = First Character Received
;
;
;  DOS INT 20H = Terminate Program And Return To DOS
;
;  To Assemble This Program:
;
;	1. Place the source file, LOOK.ASM, in Drive B
;
;	2. MASM B:LOOK,B:LOOK,B:LOOK,NUL.CRF
;
;	3. LINK B:LOOK,B:LOOK,NUL.MAP,NUL.LIB
;	   (There should be 1 warning error message after linking)
;
;	4. EXE2BIN B:LOOK,B:LOOK.COM
;
;	5. B:LOOK.OBJ and B:LOOK.LST may be deleted if desired.
;
;*****************************************************************************;
;
	CSEG	SEGMENT PARA PUBLIC
	ASSUME	CS:CSEG,DS:CSEG,ES:CSEG

	ORG	100H

;  DEFINE THE CONSTANTS

CR	EQU	13		; CARRIAGE RETURN CODE
LF	EQU	10		; LINE FEED CODE
QT	EQU	34		; QUOTE MARK
BYT	EQU	16		; NUMBER OF BYTES PER LINE
LINES	EQU	8		; NUMBER OF LINES TO DISPLAY

;  SET UP THE SEGMENT REGISTERS

LOOK:	MOV	AX,CS		; GET CURRENT SEGMENT
	MOV	DS,AX		; SET DS TO THIS SEG
	MOV	ES,AX		; SET ES TO THIS SEG
	MOV	WORD PTR SEGADD,AX ; INITIALIZE DISPLAY SEGMENT

;  ASK IF ANOTHER SEGMENT IS DESIRED

ASKSEG: MOV	DX,OFFSET SEGMSG ;ADDRESS OF MESSAGE
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS

;  READ REPLY

	MOV	AH,1		; REQUEST "Y" OR "N"
	INT	21H		; GET KEY INPUT
	AND	AL,0DFH 	; ALLOW EITHER UPPER OR LOWER CASE
	CMP	AL,'N'          ; BRANCH TO GET STARTING ADD IF "N"
	JE	DISPSEG
	CMP	AL,'Y'          ; REPEAT QUERY IF NOT "Y"
	JNE	ASKSEG

;  GET THE SEGMENT TO DISPLAY MEMORY FROM

GETSEG: MOV	DX,OFFSET SEGMSG2 ; ADDRESS OF MESSAGE
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS

;  READ DESIRED SEGMENT

	MOV	AH,4		; MAX NUMBER OF CHARACTERS WANTED
	MOV	DI,OFFSET KBUFSZ ; KEYBOARD BUFFER
	CALL	KYBD		; INPUT REPLY

	CMP	AH,4		; WANT 4 CHARS EXCLUDING THE CR
	JE	CV1		; SKIP IF NO LEADING ZEROS
	CALL	INSERT		; INSERT LEADING ZEROS

CV1:	CALL	CRLF		; OUTPUT CR AND LF

;  CONVERT FOUR ASCII CODES INTO TWO HEX BYTES (FOUR HEX DIGITS)

	MOV	AX,WORD PTR KBUF ; FIRST TWO ASCII CODES
	CALL	ASC_HEX 	; RETURNS ONE HEX BYTE
	JC	GETSEG		; REPEAT QUERY IF ILLEGAL INPUT
	MOV	SEGADD+1,AL	; STORE HIGH SEGMENT BYTE
	MOV	AX,WORD PTR KBUF+2 ; THIRD AND FOURTH ASCII CODES
	CALL	ASC_HEX 	; RETURNS ONE HEX BYTE
	JC	GETSEG		; REPEAT QUERY IF ILLEGAL INPUT
	MOV	SEGADD,AL	; STORE LOW SEGMENT BYTE

;  DISPLAY THE SEGMENT FROM WHICH DATA IS TO BE DISPLAYED

DISPSEG:
	MOV	AL,SEGADD+1	; GET THE FIRST SEGMENT BYTE
	CALL	HEX_ASC 	; CONVERT TO ASCII
	MOV	ASCSEG,AX	; SAVE FOR OUTPUT TO CRT
	MOV	AL,SEGADD	; GET THE SECOND SEGMENT BYTE
	CALL	HEX_ASC 	; CONVERT IT TO ASCII TOO
	MOV	ASCSEG+2,AX	; AND SAVE IT ALSO

	MOV	DX,OFFSET SEGMSG3 ; ADDRESS OF MESSAGE
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS

	MOV	DX,OFFSET ASCSEG ; ADDRESS OF MESSAGE
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS

;  ASK FOR THE STARTING ADDRESS

ASKADD: MOV	DX,OFFSET STMSG ; ADDRESS OF MESSAGE
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS

;  READ REPLY

	MOV	AH,4		; MAX NUMBER OF CHARACTERS WANTED
	MOV	DI,OFFSET KBUFSZ ; KEYBOARD BUFFER
	CALL	KYBD		; INPUT REPLY

	CMP	AH,4		; WANT 4 CHARS EXCLUDING THE CR
	JE	CV2		; SKIP IF NO LEADING ZEROS
	CALL	INSERT		; INSERT LEADING ZEROS

CV2:	CALL	CRLF		; OUTPUT CR AND LF
	CALL	CRLF		; OUTPUT CR AND LF

;  CONVERT FOUR ASCII CODES INTO TWO HEX BYTES (FOUR HEX DIGITS)

	MOV	AX,WORD PTR KBUF ; FIRST AND SECOND ASCII CODES
	CALL	ASC_HEX 	; RETURNS ONE HEX BYTE
	JC	ASKADD		; REPEAT QUERY IF ILLEGAL INPUT
	MOV	STADD+1,AL	; STORE HIGH ADDRESS BYTE
	MOV	AX,WORD PTR KBUF+2 ; THIRD AND FOURTH ASCII CODES
	CALL	ASC_HEX 	; RETURNS ONE HEX BYTE
	JC	ASKADD		; REPEAT QUERY IF ILLEGAL INPUT
	MOV	STADD,AL	; STORE LOW ADDRESS BYTE

	MOV	AX,WORD PTR STADD ; GET STARTING ADDRESS
	MOV	SI,AX		; STARTING ADDRESS IN SI REGISTER

	CALL	DISPLA		; DISPLAY MEMORY (AT LAST)

;  ASK IF WE SHOULD REPEAT

EMSG:	MOV	DX,OFFSET ENDMSG ; ADDRESS OF MESSAGE
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS

	MOV	AH,1		; REQUEST "Y" OR "N"
	INT	21H		; GET KEY INPUT
	AND	AL,0DFH 	; ALLOW EITHER UPPER OR LOWER CASE
	CMP	AL,'N'          ; EXIT IF "N"
	JE	EXIT
	CMP	AL,'Y'          ; REPEAT QUERY IF NOT "Y"
	JNE	EMSG
	JMP	ASKSEG		; LOOP TO START OVER

EXIT:	INT	20H		; Return to DOS
	.XLIST
	SUBTTL	Miscellaneous Callable Routines
	PAGE	+
	.LIST
;
;
;  INSERT LEADING ZEROS INTO KEYBOARD INPUT BUFFER
;
INSERT: MOV	AL,4		; NUMBER OF DIGITS IN NUMBER
	SUB	AL,AH		; NUMBER OF LEADING ZEROS
INS1:	MOV	DI,OFFSET KBUF+3 ; DESTINATION ADDRESS
	MOV	SI,OFFSET KBUF+2 ; SOURCE ADDRESS
	MOV	CX,3		; LOOP COUNT
INS2:	MOV	AH,[SI] 	; MOVE ONE
	MOV	[DI],AH 	;    CHARACTER
	DEC	DI		; DEC POINTERS
	DEC	SI
	LOOP	INS2		; SHIFT ALL DIGITS
	MOV	AH,'0'          ; ASCII ZERO
	MOV	[DI],AH 	; ADD LEADING ZERO
	DEC	AL
	JNZ	INS1		; LOOP FOR EACH ZERO TO ADD
;
;  WRITE CR LF TO CRT
;
CRLF:	MOV	DX,OFFSET CRLFM ; ADDRESS OF 'CR,LF'
	MOV	AH,9
	INT	21H		; WRITE CR LF
	RET			; RETURN TO CONTINUE
CRLFM	DB	CR,LF,'$'
;
;
;  DISPLAY MEMORY
;
;	SI REG = STARTING ADDRESS TO DISPLAY
;	SEGADD = SEGMENT TO DISPLAY FROM
;	STADD  = OFFSET TO DISPLAY FROM
;	ASCSEG = ASCII CODE OF SEGMENT
;	ASCADD = ASCII CODE OF OFFSET
;	ALL REGISTERS ARE ALTERED
;
DISPLA: MOV	DX,OFFSET SPACE ; SPACE CODES
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; OUTPUT SPACES
;
;  OUTPUT THE TOP LINE
;
	MOV	CX,BYT		; NUMBER OF BYTES PER LINE
	MOV	AL,STADD	; GET THE LOW ADDRESS BYTE
LUP1:	PUSH	AX		; SAVE FOR NEXT OUTPUT
	CALL	HEX_ASC 	; CONVERT TO ASCII
	MOV	ASCTOP,AH	; SAVE FOR OUTPUT
	MOV	AH,9		; OUTPUT STRING FUNCTION
	MOV	DX,OFFSET ASCTOP ; ADDRESS OF STRING
	INT	21H		; OUTPUT ONE DIGIT ON TOP LINE
	POP	AX		; GET PREVIOUS DIGIT
	INC	AL		; INCREMENT FOR NEXT DIGIT
	LOOP	LUP1		; REPEAT FOR THE REST
	CALL	CRLF		; CR AND LF
;
	MOV	BX,LINES	; NUMBER OF LINES TO DISPLAY
	MOV	AX,WORD PTR SEGADD ; FETCH SEGMENT TO DISPLAY
	MOV	ES,AX		; PUT DESIRED SEGMENT IN ES REGISTER
;
;  OUTPUT ADDRESS AT START OF LINE
;
LUP3:	PUSH	BX		; SAVE LINE COUNTER
	MOV	AL,STADD+1	; GET THE FIRST ADDRESS BYTE
	CALL	HEX_ASC 	; CONVERT TO ASCII
	MOV	ASCADD,AX	; SAVE FOR OUTPUT TO CRT
	MOV	AL,STADD	; GET THE SECOND ADDRESS BYTE
	CALL	HEX_ASC 	; CONVERT IT TO ASCII TOO
	MOV	ASCADD+2,AX	; AND SAVE IT ALSO
;
	MOV	AH,9		; DISPLAY STRING FUNCTION
	MOV	DX,OFFSET ASCADD ; ADDRESS OF MESSAGE
	INT	21H		; CALL DOS
	MOV	AL,STADD	; INCREMENT STARTING ADDRESS
	ADD	AL,BYT		; BY NUMBER OF BYTES IN A LINE
	MOV	STADD,AL
	JNC	DLINES
	INC	STADD+1 	; INCREMENT HIGH BYTE IF NECESSARY
;
;  OUTPUT ONE LINE
;
DLINES: MOV	CX,BYT		; NUMBER OF BYTES TO DISPLAY IN A LINE
	MOV	DI,OFFSET ASCCHAR ; ADDRESS OF ASCII BUFFER
;
LUP4:	MOV	AL,ES:[SI]	; PICK UP NEXT MEMORY BYTE
	PUSH	AX		; SAVE IT
	CMP	AL,7FH		; SEE IF IT CAN BE DISPLAYED ON CRT
	JGE	DASC1		; BRANCH IF NOT
	CMP	AL,20H
	JGE	DASC2		; BRANCH IF YES
DASC1:	MOV	AL,'.'          ; SUBSTITUTE PERIOD
DASC2:	MOV	[DI],AL 	; STORE FOR LATER DISPLAY
	INC	DI		; INCREMENT BUFFER POINTER
	POP	AX		; RETRIEVE MEMORY BYTE
	CALL	HEX_ASC 	; CONVERT IT TO TWO ASCII CODES
	MOV	CHARS,AX	; STORE THEM
	PUSH	SI		; SAVE POINTER
	PUSH	CX		; SAVE BYTE COUNTER
	MOV	DX,OFFSET CHARS ; ADDRESS OF STRING
	MOV	AH,9		; DISPLAY STRING FUNCTION
	INT	21H		; CALL DOS TO DISPLAY THIS BYTE
	POP	CX		; RESTORE BYTE COUNTER
	POP	SI		; RESTORE POINTER
	INC	SI		; INCREMENT SOURCE POINTER
	LOOP	LUP4		; REPEAT UNTIL DONE
;
	MOV	CX,BYT+2	; NUMBER OF TOTAL SYMBOLS
	MOV	DI,OFFSET ASCSYM ; ADDRESS OF STRING
LUP5:	MOV	DL,[DI] 	; NEXT CHARACTER TO DISPLAY
	PUSH	DI		; SAVE REGISTERS
	PUSH	CX
	MOV	AH,2		; DISPLAY CHARACTER FUNCTION
	INT	21H		; CALL DOS TO DISPLAY SYMBOLS
	POP	CX		; RESTORE REGISTERS
	POP	DI
	INC	DI		; INCREMENT SYMBOL ADDRESS
	LOOP	LUP5		; REPEAT UNTIL FINISHED
;
	CALL	CRLF		; OUTPUT CR AND LF
	POP	BX		; RESTORE LINE COUNTER
	DEC	BX		; DECREMENT LINE COUNTER
	JNZ	LUP3		; REPEAT FOR ALL LINES
;
	MOV	AX,DS
	MOV	ES,AX		; RESTORE ES REGISTER
	RET			; RETURN TO CALLING ROUTINE
	PAGE
;									      ;
;*****************************************************************************;
;									      ;
;	KEYBOARD INPUT SUBROUTINE     VERSION 2/19/84			      ;
;									      ;
;	THIS ROUTINE READS ASCII CODES FROM THE KEYBOARD INTO A BUFFER	      ;
;									      ;
;	ENTRY:	AH = MAX NUMBER OF CHARACTERS TO READ EXCLUDING ANY CR CODE   ;
;		DI = FWA OF THE BUFFER IN WHICH TO STORE THE CHARACTERS       ;
;									      ;
;	EXIT:	AL = THE LAST CHARACTER READ EXCLUDING ANY CR CODE	      ;
;		AH = THE NUMBER OF CHARACTERS READ EXCLUDING BS OR CR CODES   ;
;		DI = ADDRESS OF THE LAST CHARACTER READ 		      ;
;		     ONE LESS THAN BUFFER FWA IF ONLY CR IS RECEIVED	      ;
;									      ;
;	BACKSPACE AND CARRIAGE RETURN CODES ARE PROCESSED BY DOS	      ;
;									      ;
;	THE BUFFER MUST HAVE THE FIRST TWO BYTES AVAILABLE FOR STORAGE OF     ;
;	THE MAX NUMBER OF CHARACTERS TO READ AND NUMBER OF CHARACTERS READ    ;
;	INCLUDING THE CARRAIGE RETURN CODE				      ;
;									      ;
;	ALTERS: ALL REGISTERS EXCEPT BX ARE ALTERED			      ;
;									      ;
;*****************************************************************************;
;									      ;
KYBD:	PUSH	BX		; SAVE REGISTER
	PUSH	DI		; SAVE BUFFER ADDRESS
	INC	AH		; ALLOW FOR THE CR CODE
	MOV	[DI],AH 	; STORE MAX NUMBER OF WORDS TO READ
	MOV	AX,0C0AH	; CLEAR AND READ KEYBOARD BUFFER
	MOV	DX,DI		; BUFFER ADDRESS IN DX
	INT	21H		; CALL DOS
	POP	DI		; GET BUFFER ADDRESS
	INC	DI
	MOV	AH,[DI] 	; GET NUMBER OF CHARACTERS READ
	MOV	BL,AH		; PUT IN BASE REGISTER
	XOR	BH,BH
	ADD	DI,BX		; SET DI TO LAST CHARACTER POSITION
	MOV	AL,[DI] 	; GET LAST CHARACTER READ
	POP	BX		; RESTORE REGISTER
	RET			; RETURN TO CALLING ROUTINE
	PAGE
;									      ;
;*****************************************************************************;
;									      ;
;	CONVERT ASCII TO HEX		    VERSION 2/21/84		      ;
;									      ;
;	CONVERT TWO ASCII CODES IN AX TO ONE HEX NUMBER IN AL		      ;
;									      ;
;	ENTRY:	AL = UPPER ASCII CODE					      ;
;		AH = LOWER ASCII CODE					      ;
;									      ;
;	EXIT:	AL = ONE HEX NUMBER  (TWO HEX DIGITS)			      ;
;		CARRY FLAG IS SET IF AN ILLEGAL HEX DIGIT IS IN THE INPUT     ;
;									      ;
;	ALTERS: REGISTERS AL AND AH ARE ALTERED 			      ;
;									      ;
;	NOTE:	VALID FOR ALL HEX NUMBERS 00 TO FF			      ;
;									      ;
;*****************************************************************************;
;									      ;
ASC_HEX:PUSH	BX		; SAVE REGISTERS
	MOV	BX,AX		; SAVE THE ASCII CODES
	CALL	CNVRT1		; RETURNS UPPER DIGIT IN LOWER AL
	SHL	AL,1		; PUT IT IN UPPER AL
	SHL	AL,1
	SHL	AL,1
	SHL	AL,1
	XCHG	AL,BH		; SAVE IN BH & GET LOWER DIGIT
	CALL	CNVRT1		; RETURNS LOWER DIGIT IN LOWER AL
	OR	AL,BH		; COMBINE BOTH HEX DIGITS INTO AL
	POP	BX		; RESTORE REGISTERS
	CLC			; CLEAR CARRY/ERROR FLAG
	RET			; RETURN TO CALLING ROUTINE
;
CNVRT1: SUB	AL,30H		; PARTIAL CONVERSION
	JL	CERR		; AL < 0 => ILLEGAL HEX CODE
	CMP	AL,9		; CHECK FOR 0 - 9
	JLE	CEND		; AL <= 9 => 0 - 9
	CMP	AL,11H		; CHECK FOR A - F
	JL	CERR		; AL < 11H => ILLEGAL (between '9' and 'A')
	SUB	AL,7		; CONVERT A - F
	CMP	AL,0FH		; AL > 0FH => ILLEGAL
	JG	CERR		; ERROR EXIT
CEND:	RET			; RETURN TO CONTINUE
;
CERR:	POP	AX		; ERASE FIRST RETURN ADDRESS
	SUB	AX,AX		; SET RESULT TO ZERO
	POP	BX		; ADJUST STACK
	STC			; SET CARRY/ERROR FLAG
	RET			; RETURN TO CALLING ROUTINE
	PAGE
;									      ;
;*****************************************************************************;
;									      ;
;	CONVERT HEX TO ASCII						      ;
;									      ;
;	CONVERT FROM TWO HEX DIGITS IN AL TO TWO ASCII CODES IN AX	      ;
;									      ;
;	ENTRY:	AL = HEX NUMBER 00H TO FFH				      ;
;									      ;
;	EXIT:	AL = UPPER ASCII CODE					      ;
;		AH = LOWER ASCII CODE					      ;
;									      ;
;	ALTERS: REGISTERS AL AND AH ARE ALTERED 			      ;
;									      ;
;	NOTE:	THIS CONVERSION IS VALID FOR ALL HEX CODES		      ;
;									      ;
;*****************************************************************************;
;									      ;
HEX_ASC:
	MOV	AH,AL		; Save upper hex digit
	CALL	CVRT2		; Convert hex lower digit
	XCHG	AH,AL		; Save it in AH / get upper digit to convert
	SHR	AL,1		; Shift into low nibble
	SHR	AL,1
	SHR	AL,1
	SHR	AL,1
	CALL	CVRT2		; Convert upper hex digit
	RET			; Return to calling routine
;
;  CONVERT ONE HEX DIGIT IN LOWER NIBBLE OF AL INTO ONE ASCII CODE IN AL
;
CVRT2:	AND	AL,0FH		; Separate out one hex digit
	OR	AL,30H		; Convert 0 - 9
	CMP	AL,'9'          ; Check for A - F
	JLE	CVRT4		; Skip if 0 - 9
	ADD	AL,07H		; Convert A - F
CVRT4:	RET			; Return to calling routine
	.XLIST
	SUBTTL	Messages and Data Storage
	PAGE	+
	.LIST
;
;  MESSAGES and DATA STORAGE
;
SEGMSG	DB	CR,LF,LF,'DO YOU WANT TO DISPLAY A DIFFERENT SEGMENT ? (Y/N) $'
;
SEGMSG2 DB	CR,LF,LF,'ENTER THE SEGMENT IN HEX $'
;
SEGMSG3 DB	CR,LF,LF,'DISPLAYING FROM SEGMENT NUMBER  $'
;
STMSG	DB	CR,LF,LF,'ENTER THE HEX STARTING ADDRESS $'
;
ENDMSG	DB	CR,LF,'REPEAT TO LOOK AT MORE ? (Y/N) $'
;
CHARS	DW	'  '
	DB	' $'
;
SPACE	DB	'       $'
ASCTOP	DB	'   $'
;
ASCSYM	DB	'  '
ASCCHAR DB	16 DUP(' ')
;
SEGADD	DB	0,0,0,0
ASCSEG	DW	0,0
	DB	'  $'
;
STADD	DB	0,0,0,0
ASCADD	DW	0,0
	DB	'  $'
KBUFSZ	DB	0,0
KBUF	DB	'    $'
;
	CSEG	ENDS
	END	LOOK
