;** THIS PROGRAM COPYRIGHT 1982 80 MICRO MAGAZINE **
;********************************************************
;
;             >>>>>  P E N R A M  <<<<<
;
;          A SCROLLING RAM EDITOR UTILITY
;
;           SOFTWARE IN THE PUBLIC DOMAIN
;
;********************************************************
;
; BY ROXTON BAKER
; BOX 8272, APO SAN FRANCISCO 96555
;
;
; VERSION 2.0
;- - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;COMMANDS:
;  'CLEAR' CHANGES TYPE OF DUMP (HEX/ASCII).
;  'BREAK' BREAKS FROM ERRONEOUS HEX ENTRY.
;  'SHIFT/BREAK' SETS REF. ADDRESS = CURRENT ADDRESS.
;  'SHIFT/UP- OR DOWN-ARROW SCROLLS RAPIDLY.
;  'SHIFT/LEFT-ARROW' ALLOWS NEW STARTING ADDRESS.
;      (REPLY  <ENTER>  TO LEAVE PENRAM)
;
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - 
; PENRAM CAN BE RELOCATED BY CHANGING THE VALUE OF
; 'STARTP' .    ALLOW 1100 (044CH) BYTES TOTAL.
;
STARTP	EQU	0E7B3H		;FOR LINKING WITH RSM48.
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; SYSTEM ROUTINES AND LOCATIONS ---
KI	EQU	002BH		;INPUTS ASCII BYTE
CLS	EQU	01C9H		;CLEARS SCREEN
CURPOS	EQU	4020H		;POS'N OF STD. CURSOR
KIBUF	EQU	4036H		;WORKSPACE FOR KI
LINEIN	EQU	05D9H		;INPUTS WHOLE LINE
BYTDIS	EQU	0033H		;PRINTS ASCII BYTE
DELAY	EQU	0060H		;14.6 USEC PER COUNT BC
NTF2	EQU	0A9DH		;DENOTES INTEGER VALUE
NUMSTR	EQU	0FBDH		;CONVERTS NUM. TO STRING
;
; CONSTANTS ---
KDLAY1	EQU	2500H		;MAIN INTER-KEY DELAY
KDLAY2	EQU	1200H		;AUX DELAY REGULAR CHAR.
BDELAY	EQU	0A0H		;SPEED OF CURSOR BLINK
BLANK	EQU	80H		;BLANK GRAPHICS BLOCK
CURCHR	EQU	8AH		;DEFINES CURSOR CHARACTER
;
; KEYBOARD INPUT VALUES ---
CLEAR	EQU	1FH
BREAK	EQU	4
SHFBRK	EQU	5
SHFTUP	EQU	9
SHFTDN	EQU	17
SHFLFT	EQU	21H
UPAROW	EQU	5BH
DNAROW	EQU	0AH
LFAROW	EQU	08
RTAROW	EQU	09
;
;KEYBOARD SCAN ADDRESSES ---
KEYS1	EQU	3801H		;@-G KEYS
CNTL	EQU	3840H		;DETECT CONTROL KEYS
SHCNTL	EQU	38C0H		;SAME BUT SHIFTED
SHIFT	EQU	3880H		;0 HERE MEANS NO SHIFT
;
; STORAGE LOCATIONS ---
STRPLC	EQU	STARTP+2	;STORES STRING TO PRINT
STORAG	EQU	STARTP+66
ADDR	EQU	STORAG		;ADDRESS TO PRINT NEXT
HOMADD	EQU	STORAG+2	;ADDRESS AT TOP LEFT
EDCUR	EQU	STORAG+4	;LOC'N OF EDIT CURSOR
BYTED	EQU	STORAG+6	;BYTE AT EDIT CURSOR
RGTCUR	EQU	STORAG+8	;# TIMES MOVED RIGHT
DWNCUR	EQU	STORAG+9	;# TIMES MOVED DOWN
HATYPE	EQU	STORAG+10	;TYPE OF DUMP FLAG
KEY	EQU	STORAG+11	;KEY INPUT STORAGE
KEYH	EQU	STORAG+12	;USED IN HEX ENTRY
LSTARW	EQU	STORAG+13	;DIRECTION OF LAST SCROLL
ATADD	EQU	STORAG+14	;HOLDS REFERENCE ADDRESS
;
; VIDEO LOCATIONS ---
LINE01	EQU	3C00H
LINE02	EQU	3C40H
LINE05	EQU	3D00H
LINE16	EQU	3FC0H
VIDFST	EQU	3C00H
VIDLST	EQU	3FFFH
;
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - 
;START OF MAIN PENRAM ROUTINE
;
	ORG	STARTP
	JR	ENTRY
;
	DEFS	80		;WORK AND STORAGE SPACE.
;
ENTRY	DI
	CALL	CLS
	LD	HL,3E38H	;PROMPT FOR ADDRESS.
	LD	(CURPOS),HL
	LD	HL,INMSG
	CALL	OUTSTR		;DISPLAY IT.
;
;       TAKE IN 4-DIGIT HEX ADDRESS.  BREAK FROM PENRAM
;	IF ONLY <ENTER> WAS PRESSED.
ENTRY2	LD	HL,3E78H	;CLEAR ANY PREV. ADDRESS
	LD	(CURPOS),HL	;  AND TAKE IN NEW ONE.
	LD	A,1EH		;ERASE TO END OF LINE.
	CALL	BYTDIS
	LD	B,4		;ALLOW ONLY 4 DIGITS
	LD	HL,STRPLC
	CALL	LINEIN
	LD	A,B		;B HAS # CHAR. TYPED.
	AND	A
;
RETLBL	RET	Z		;LEAVE PENRAM IF NO CHAR.
	NOP			;  ENTERED.  CHANGE THIS
	NOP			;  TO A JUMP IF DESIRED.
;
	CP	4
	JR	NZ,ENTRY2	;DO AGAIN IF < 4 CHAR.
;
;	IS INPUT VALID HEX?  IF NOT, BACK TO PROMPT.
	LD	HL,STRPLC+3
	CALL	FRMBYT		;MAKES ASCII CHAR AT HL
	JR	C,ENTRY2	;  INTO VALID HEX IN A.
	LD	(HOMADD),A	;  RETURNS CARRY SET IF
	CALL	FRMBYT		;  NOT VALID HEX.
	JR	C,ENTRY2
	LD	(HOMADD+1),A
;
;       STARTING ADDRESS IS AT HOMADD.  NOW INITIALIZE
;       SCROLLING DUMP ROUTINE AND BEGIN.
	XOR	A
	LD	(HATYPE),A	;ASSUME HEX DUMP.
;
	LD	(DWNCUR),A	;EDIT CURSOR IS AT
	LD	(RGTCUR),A	;  TOP LEFT
;
	LD	HL,(HOMADD)	;THIS IS FIRST REFER-
	LD	(ATADD),HL	;  ENCE ADDRESS.
;
	LD	HL,3C78H	;SET UP CURSOR AND
	LD	(CURPOS),HL	;  PRINT LOGO AND
	LD	HL,PENMSG	;  OTHER MARKS.
	CALL	OUTSTR
	LD	A,'>'
	LD	(3CF8H),A
	LD	A,'<'
	LD	(3CFFH),A
	LD	A,'='
	LD	(3D78H),A
	LD	A,'H'
	LD	(3D7EH),A
;
REINIT	LD	HL,(HOMADD)	;START WITH REQUESTED
	LD	(ADDR),HL	;  ADDRESS.
	LD	B,16		;FOR 16 LINES.
	LD	HL,VIDFST-1	;INITIALIZE CURSOR.
	LD	(CURPOS),HL
;
INITLP	EXX			;REPAINT SCREEN BY PRINT-
	LD	HL,(CURPOS)	;  ING 16 LINES.
	INC	HL
	LD	(CURPOS),HL
	LD	HL,(ADDR)
	CALL	MAKSTR		;CREATES DUMP STRING FROM
	LD	HL,STRPLC	;  ADDR AND THE 16 BYTES
	CALL	OUTSTR		;  THERE.  PRINT STRING.
	LD	BC,16
	CALL	AADDR		;POINT TO NEXT ADDRESS.
	EXX
	DJNZ	INITLP
;
	LD	A,1		;CURSOR MOVED DOWN LAST.
	LD	(LSTARW),A
;
UPDATE	LD	HL,(EDCUR)	;ERASE OLD EDIT CURSOR.
	LD	(HL),BLANK
;
; NOW CALCULATE NEW EDIT CURSOR POSITION (EDCUR) AND EDIT
; BYTE ADDRESS (BYTED) FROM NEW VALUES OF DWNCUR AND
; RGTCUR.
	LD	A,(DWNCUR)	;INITIALIZE FOR BEING
	INC	A		;  COUNTED.
	LD	B,A
	LD	HL,VIDFST-61
	LD	DE,64
	EXX			;COUNT ADDRESSES IN ALT
	LD	HL,(HOMADD)	;  REGISTERS.
	LD	BC,17
	XOR	A
	SBC	HL,BC
	LD	DE,16
	EXX
;
LINEDN	ADD	HL,DE		;FOR EVERY LINE DOWN, ADD
	EXX			;  64 TO EDCUR AND 16
	ADD	HL,DE		;  TO BYTED.
	EXX
	DJNZ	LINEDN
;
	LD	A,(RGTCUR)
	INC	A
	LD	B,A
	LD	DE,3
;
SPCRGT	ADD	HL,DE		;FOR EVERY SPACE RIGHT,
	EXX			;  ADD 3 TO EDCUR AND 1
	INC	HL		;  TO BYTED.
	EXX
	DJNZ	SPCRGT
;
	LD	(EDCUR),HL	;UPDATE EDIT CURSOR POS'N
	LD	(HL),CURCHR	;  AND PRINT CURSOR.
	EXX
	LD	(BYTED),HL	;UPDATE EDIT BYTE POS'N
	EXX
;
;	DISPLAY THE CURRENT EDIT BYTE ADDRESS ALONG
;	WITH THE REFERENCE ADDRESS AND OFFSET FROM IT.
	LD	HL,3CFAH	;WILL PRINT HERE FIRST.
	LD	(CURPOS),HL
	LD	HL,STRPLC+4	;PUT IN TERMINATOR FOR
	LD	(HL),3		;  STRINGS BELOW.
;
	DEC	HL		;MAKE BYTE THAT EDIT
	LD	BC,(BYTED)	;  CURSOR POINTS TO INTO
	CALL	BSTRNG		;  AN ASCII STRING AND
	CALL	OUTSTR		;  PRINT IT.
;
	LD	HL,3D7AH	;PRINT REF. ADDRESS SIM-
	LD	(CURPOS),HL	;  ILARLY.
	LD	HL,STRPLC+3
	LD	BC,(ATADD)
	CALL	BSTRNG
	CALL	OUTSTR
;
	LD	HL,3DB8H	;TO PRINT OFFSET, FIRST
	LD	(CURPOS),HL	;  CLEAR THE LINE.
	LD	A,1EH
	CALL	BYTDIS
	LD	HL,(BYTED)	;NOW CALC. OFFSET.
	LD	DE,(ATADD)
	XOR	A		;RESET CARRY FLAG.
	SBC	HL,DE		;GET OFFSET IN HL.
	LD	(4121H),HL	;GIVE IT TO THE ROM
	CALL	NTF2		;  AS AN INTEGER.
	CALL	NUMSTR		;ROM MAKES IT A STRING.
	CALL	OUTSTR		;WHICH WE PRINT.
	LD	A,'D'		;FOLLOW WITH D FOR DEC.
	LD	(3DBEH),A
	LD	HL,3DB8H	;NOW SEE IF IT WAS A
	LD	A,(HL)		;  NEGATIVE NUMBER.
	CP	'-'
	JR	Z,KBDSCN
	LD	(HL),'+'	;IF NOT, MARK WITH +.
;
;       THE EDIT CURSOR POSITION AND ALL POINTERS HAVE
;       BEEN COMPLETELY UPDATED AT THIS POINT.  ALL
;	LOCATION INFORMATION HAS BEEN PRINTED.  NOW
;       BLINK THE CURSOR WHILE AWAITING KEYBOARD INPUT.
KBDSCN	LD	B,BDELAY	;BLINK ON COUNTDOWN TO 0.
HALFCK	LD	A,(SHIFT)	;SEE IF SHIFT PRESSED.
	AND	A
	JR	Z,REGKI		;GO IF NO SHIFT
	LD	A,(SHCNTL)	;ELSE CHECK FOR RETURN TO
	CP	SHFLFT		;  ADDRESS PROMPT OR
	JP	Z,ENTRY2	;  FOR SHIFTED UP/DOWN
	CP	SHFTUP		;  ARROWS.  IF UP OR DOWN
	JR	Z,DOUP		;  ARROW, SCROLL WITHOUT
	CP	SHFTDN		;  DELAY.
	JR	Z,DODN
	CP	SHFBRK		;IF SHIFT/BREAK, REPLACE
	JR	NZ,REGKI	;  CURRENT REF. ADDRESS
	LD	HL,(BYTED)	;  WITH THE BYTE POINTED
	LD	(ATADD),HL	;  TO BY THE CURSOR.
	JR	JUPDAT
;
REGKI	CALL	KIR		;SCAN REGULAR KEYS WITH
				;  REPEAT.
	JR	NZ,KPRESS	;GO IF ANY KEY DOWN.
	DJNZ	HALFCK		;BACK UNLESS BLINK TIME.
;
	LD	HL,(EDCUR)
	LD	A,(HL)		; FLIP-FLOP CURSOR CHAR-
	XOR	0AH		;  ACTER AND GO BACK TO
	LD	(HL),A		;  BLINK IT.
	JR	KBDSCN
;
KPRESS	LD	(KEY),A		;A KEY IS DOWN. SAVE IT.
	CP	CLEAR
	JR	NZ,DBNC		;GO IF NOT CLEAR KEY.
	LD	A,(HATYPE)	;ELSE ON CLEAR ONLY, FLIP
	XOR	1		;  -FLOP TYPE OF DUMP.
	LD	(HATYPE),A
	JP	REINIT		;AND REPAINT SCREEN.
;
DBNC	LD	BC,KDLAY1	;DELAY TO AVOID BOUNCE.
	CALL	DELAY
;
	LD	A,(KEY)		;RETRIEVE KEY ENTERED.
;
;       NOW CHECK TO SEE IF THE KEY WAS AN ARROW KEY.
;       GO TO THE APPROPRIATE PROCESSING IF IT WAS.
CKUP	CP	UPAROW
	JR	Z,DOUP
	JR	CKDN
DOUP	CALL	CURSUP		;SCROLLING UP IS EASY -
	JR	JUPDAT		;  CURSUP DOES IT ALL.
;
CKDN	CP	DNAROW
	JR	Z,DODN
	JR	CKLF
DODN	CALL	CURSDN		;SCROLLING DOWN IS EASY -
	JR	JUPDAT		;  CURSDN DOES IT ALL.
;
CKLF	CP	LFAROW
	JR	Z,DOLF
	JR	CKRT
DOLF	LD	A,(RGTCUR)	;LEFT ARROW MAY REQUIRE
	DEC	A		;  MOVING CURSOR UP ONE
	CP	0FFH		;  IF AT EXTREME LEFT.
	LD	(RGTCUR),A
	JR	NZ,JUPDAT	;GO IF NOT AT FAR LEFT.
	LD	A,15		;ELSE MOVE TO FAR RIGHT
	LD	(RGTCUR),A	;  AND SCROLL CURSOR UP
	CALL	CURSUP		;  ONE ROW.
	JR	JUPDAT
;
CKRT	CP	RTAROW
	JR	Z,DORT
	JR	KEYIN
DORT	LD	A,(RGTCUR)	;RIGHT ARROW MAY REQUIRE
	INC	A		;  MOVING CURSOR DOWN ONE
	CP	16		;  IF AT EXTREME RIGHT.
	LD	(RGTCUR),A
	JR	NZ,JUPDAT	;GO IF NOT AT FAR RIGHT.
	XOR	A		;ELSE MOVE TO FAR LEFT
	LD	(RGTCUR),A	;  AND SCROLL CURSOR DOWN
	CALL	CURSDN		;  ONE ROW.
;
JUPDAT	JP	UPDATE		;ARROW KEY PROCESSED.
;
;
;       ARRIVE HERE IF KEY PRESSED WAS NOT A CONTROL
;       KEY.  THAT SIGNIFIES AN ATTEMPT TO ENTER DATA.
;       IF IN THE HEX MODE, CHECK THE DATA FOR 0-F.  IF
;       IN THE ASCII MODE, JUST ENTER AND DISPLAY IT.
KEYIN	LD	BC,KDLAY2
	CALL	DELAY		;DELAY FOR DEBOUNCE.
	LD	A,(HATYPE)	;CHECK TYPE OF DUMP.
	AND	A
	JR	NZ,ASCIN	;GO IF ASCII.
;
	LD	A,(KEY)		;IN HEX MODE. CHECK KEY.
	CALL	VALHEX		;RETURNS CARRY SET IF KEY
	JP	C,UPDATE	;  IS INVALID HEX.
	RLCA			;ELSE RETURNS VALID HEX
	RLCA			;  IN A.
	RLCA			;PUT HEX 0-F IN HIGH
	RLCA			;  NYBBLE OF A.
	LD	(KEYH),A	;SAVE IT.
;
	LD	HL,(EDCUR)	;INCIDENTALLY REDRAW
	LD	A,CURCHR	;  EDIT CURSOR SINCE IT
	LD	(HL),A		;  MAY BE OFF.
;
	LD	A,(KEY)		;RETRIEVE KEY AND DISPLAY
	CALL	CURINC		;  IT AT EDCUR+1.
	CALL	BYTDIS
;
WAITNX	LD	A,(CNTL)	;NOW WAIT FOR NEXT VALID
	CP	BREAK		;  HEX ENTRY.  BREAK BACK
	JP	Z,REINIT	;  TO MAIN ROUTINE IF
	CALL	KIR		;  REQUIRED.
	CALL	VALHEX		;CHECK WHATEVER CAME IN,
	JR	C,WAITNX	;  EVEN IF NOTHING DID.
;
	LD	C,A		;HAVE A VALID HEX CHAR!
	LD	A,(KEYH)	;MERGE IT WITH LAST CHAR.
	OR	C
	LD	HL,(BYTED)	;STORE NEW BYTE AT BYTED.
	LD	(HL),A
;
	LD	A,(HL)		;RETRIEVE AND DISPLAY IT
	CALL	CURINC		;  AT THE EDIT CURSOR.
	LD	C,A		;  THIS VERIFIES THE EDIT
	CALL	ASCII		;  ACTION.
	EX	DE,HL
	LD	A,H
	CALL	BYTDIS
	LD	A,L
	CALL	BYTDIS
;
	LD	BC,KDLAY1+KDLAY2  ;DEBOUNCE DELAY HERE
	CALL	DELAY		;  LIKE OTHER PATHS.
	JP	DORT		;AND GO ACT JUST AS IF
				;  RIGHT ARROW PRESSED.
;
ASCIN	LD	A,(KEY)		;KEY IN SHOULD BE VALID
	LD	C,A		;  ASCII, BUT CHECK IT
	CALL	VALASC		;  TO BE SURE...
	JR	NC,JUPDAT	;GO IF BAD.
	LD	HL,(BYTED)	;ELSE ENTER INTO RAM AND
	LD	(HL),A		;  READ IT BACK TO VERIFY
	LD	A,(HL)		;  THE EDIT ACTION.
	LD	C,A		;CHECK AGAIN FOR ASCII
	CALL	VALASC		;  IN CASE WE'RE IN ROM.
	JR	NC,ENDASC	;GO IF BAD.
	CALL	CURINC
	CALL	BYTDIS		;ELSE DISPLAY IT.
;
ENDASC	JP	DORT		;PRETEND WAS RIGHT-ARROW.
;
;
; END OF MAIN PENRAM ROUTINE.
;- - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; SUBROUTINES ---
;
; VALHEX TAKES AN ASCII CODE IN  A  AND CHECKS TO SEE IF
; IT IS A VALID HEX CHARACTER (0-F).  IF IT IS NOT,
; VALHEX RETURNS WITH THE CARRY FLAG SET.  IF IT IS A
; HEX VALUE, THE  A  REGISTER RETURNS WITH THAT HEX
; VALUE (0-F) IN IT, AND THE CARRY FLAG RESET TO 0.
VALHEX	LD	(KEY),A
	LD	C,A
	LD	A,'F'
	CP	C
	JR	C,NOTHEX	;C SET IF CHAR > F.
	LD	A,'/'
	CP	C
	JR	NC,NOTHEX	;C SET IF CHAR > /.
	LD	A,'@'
	CP	C
	JR	C,ATHRUF	;C SET IF CHAR > @.
	LD	A,C
	CP	':'
	JR	C,ZTHRU9	;C SET IF CHAR < :
NOTHEX	SCF			;TO FLAG NOT-HEX.
	RET
ZTHRU9	SUB	30H		;CONVERT TO HEX.
	RET			;CARRY WON'T BE SET FOR
ATHRUF	LD	A,(KEY)		;  EITHER OF THESE.
	SUB	37H		;  RETURNS ON VALID HEX.
	RET
;
;
; SUBROUTINE VALASC TAKES A BYTE IN C AND CHECKS TO SEE
; IF IT A PRINTABLE ASCII CHARACTER - EITHER UPPER OR
; LOWER CASE.  IF ALPHANUMERIC IT IS SIMPLY RETURNED
; IN A, WITH THE CARRY FLAG SET.  IF NON-PRINTABLE, AN
; UNDERSCORE IS SUBSTITUTED AND THE CARRY FLAG IS RESET.
VALASC	LD	A,7FH
	CP	C
	JR	C,NOTASC	;HAVE CARRY IF CHAR > 7F
	LD	A,1FH		;SEE IF ITS TOO LOW TO
	CP	C		;  BE ASCII.
	LD	A,C
	RET	C		;RETURN IF PRINTABLE.
;
NOTASC	LD	A,5FH		;NOT ASCII.  PUT IN
	AND	A		;  UNDERSCORE AND
	RET			;  RESET CARRY.
;
;
; FRMBYT LOOKS AT (HL), (HL+1) AND IF THE ASCII CODES
; THERE ARE BOTH FOR HEX CHAR 0-F, THEN THE HEX BYTE
; THEY DEFINE IS FORMED IN  A  AND RETURNED, WITH THE
; CARRY FLAG RESET TO 0.  IF EITHER OF THE ASCII CHAR
; IS NOT 0-F, THEN A RETURN IS MADE WITH THE CARRY FLAG
; SET.
FRMBYT	LD	A,(HL)		;GET FIRST CHAR.
	CALL	VALHEX		;CHECK IT FOR 0-F.
	RET	C		;RETURN IF NOT 0-F.
	LD	E,A		;WAS 0:F.  KEEP ITS
	DEC	HL		;  VALUE IN E.
	LD	A,(HL)		;GET SECOND CHAR.
	CALL	VALHEX		;IS IT ALSO HEX?
	RET	C		;RETURN IF NOT.
	RLCA			;BOTH ARE 0-F.  FORM
	RLCA			;  THE HEX BYTE IN A.
	RLCA
	RLCA
	OR	E
	DEC	HL		;PREPARE FOR NEXT CALL.
	RET			;RETURN W/HEX IN A.
;
;
; CURINC POSITIONS THE STANDARD CURSOR AT THE LOCATION
; OF THE EDIT CURSOR PLUS ONE.
CURINC	LD	HL,(EDCUR)
	INC	HL
	LD	(CURPOS),HL
	RET
;
;
; KIR SCANS THE KEYBOARD AND RETURNS IN A THE ASCII VALUE
; OF ANY KEY THAT IS PRESSED AT THAT INSTANT.  IT
; ALSO CLEARS THE KEYBOARD INPUT ROUTINE BUFFER AT
; 4036-403C.  THIS MAKES THE KI ROUTINE THINK THAT
; THE LAST KEY PRESSED WAS SUBSEQUENTLY RELEASED, SO THAT
; IT WILL SCAN AGAIN, THUS GIVING THE EFFECT OF REPEATING
; KEYS.
KIR	CALL	KI		;GET KEY IF PRESSED.
	LD	D,A
	LD	E,7		;WILL PUT 7 ZEROS IN
	LD	HL,KIBUF	;  BUFFER.
	XOR	A
RPT	LD	(HL),A		;CLEAR ONE BYTE.
	INC	HL
	DEC	E
	JR	NZ,RPT
	LD	A,D		;RETRIEVE CHAR.
	AND	A		;SET FLAG IF ZERO.
	RET
;
;
; CURSUP HANDLES ALL MOVEMENT OF THE CURSOR UPWARDS.
; IT CHECKS FIRST TO SEE IF THE TOP OF THE SCREEN HAS
; BEEN REACHED (DWNCUR=0).  IF NOT, IT SIMPLY DECREMENTS
; DWNCUR AND RETURNS TO THE UPDATE.  IF AT THE TOP OF
; SCREEN, THE TOP 15 LINES MUST BE MOVED DOWN ONE, AND
; THE NEW DUMP LINE (AS FORMED BY MAKSTR) PRINTED ON THE
; TOP LINE.  SINCE MAKSTR NEEDS AN ADDRESS ADDR TO FORM
; THE DUMP STRING, CURSUP MUST ALSO DETERMINE WHETHER THE
; LAST SCREEN SCROLLING WAS DUE TO THE CURSOR MOVING UP
; (IN WHICH CASE THIS IS MERELY A CONTINUATION AND ADDR
; IS ALREADY CORRECT) OR DOWN (IN WHICH CASE THE VALUE OF
; ADDR WAS LEFT BY CURSDN AT 272 BYTES MORE THAN WHAT IS
; WANTED HERE).
; BEFORE RETURNING, CURSUP SUBTRACTS 16 FROM BOTH ADDR
; (ANTICIPATING ANOTHER CURSOR-UP SCROLL) AND FROM
; HOMADD, BECAUSE THE ADDRESS DISPLAYED AT THE TOP LEFT
; HAS ALSO DECREASED BY 16 BYTES.
; NOTE THAT ONLY THE FIRST 55 CHAR. OF EACH LINE ARE
; SCROLLED;  THE LAST 9 CHAR. ARE UNTOUCHED.
CURSUP	LD	A,(DWNCUR)
	AND	A
	JR	Z,CONTU1	;GO IF TOP OF SCREEN.
	DEC	A		;ELSE MOVE EDIT CURSOR UP
	LD	(DWNCUR),A	;AND RETURN TO UPDATE.
	RET
;
CONTU1	LD	A,(LSTARW)	;SEE IF LAST SCROLL WAS
	AND	A		;DUE TO CURSOR DOWN.
	JR	Z,CONTUP	;GO IF NOT.
	LD	BC,272		;MUST FIX ADDR.
	CALL	SADDR		;SUBTRACT 272 FROM IT.
;
CONTUP	LD	HL,(EDCUR)	;ADDR IS CORRECT HERE.
	LD	(HL),BLANK	;REMOVE OLD CURSOR.
	LD	HL,(ADDR)	;GET ADDR TO DUMP.
	CALL	MAKSTR		;MAKE DUMP STRING OF IT.
;
	LD	A,15		;SCROLL TOP 15 LINES
	LD	HL,3F80H	;  DOWN, MOVING ONLY THE
	LD	DE,3FC0H	;  LEFT 55 CHARACTERS.
MVLOPS	LD	BC,37H
	LDIR
	LD	BC,-119
	ADD	HL,BC		;POINT TO NEXT LINE.
	EX	DE,HL
	ADD	HL,BC		;AND WHERE IT WILL GO.
	EX	DE,HL
	DEC	A
	JR	NZ,MVLOPS	;BACK IF 15 NOT DONE.
;
	LD	HL,LINE01	;POINT TO FIRST LINE.
	LD	(CURPOS),HL
	LD	HL,STRPLC
	CALL	OUTSTR		;PRINT DUMP STRING THERE.
;
	LD	BC,16		;ADD 16 TO ADDR, HOMADD.
	CALL	SADDR
	CALL	SHOMAD
	XOR	A		;NOTE SCROLL CAUSED BY
	LD	(LSTARW),A	;  UP-ARROW.
	RET
;
;
; CURSDN IS THE MIRROR IMAGE OF CURSUP IN THAT ALL
; THE SAME ACTIONS ARE INVOLVED, BUT THE CURSOR IS
; MOVED DOWNWARD.  IF IT IS ALREADY AT THE BOTTOM OF
; THE SCREEN, THE BOTTOM 15 LINES ARE SCROLLED UP AND
; THE NEW LINE FORMED BY MAKSTR IS PRINTED ON THE
; BOTTOM.  ADDR IS CORRECTED IF THE LAST SCROLL WAS DUE
; TO AN UP-ARROW, AND IS LEFT ANTICIPATING ANOTHER DOWN-
; ARROW COMMAND.  THAT IS, IT POINTS 16 BYTES BEYOND THE
; ADDRESS DISPLAYED AT THE BOTTOM LINE.  HOMADD IS
; UPDATED TO REFLECT THE NEW ADDRESS DISPLAYED IN THE
; TOP LEFT CORNER.
CURSDN	LD	A,(DWNCUR)
	CP	15
	JR	Z,CONTD1	;GO IF BOTTOM OF SCREEN.
	INC	A		;ELSE MOVE EDCURSOR DOWN.
	LD	(DWNCUR),A	;AND RETURN TO UPDATE.
	RET
;
CONTD1	LD	A,(LSTARW)	;SEE IF LAST SCROLL WAS
	AND	A		;DUE TO CURSOR UP.
	JR	NZ,CONTD2	;GO IF NOT.
	LD	BC,272		;MUST FIX ADDR.
	CALL	AADDR		;ADD 272 TO IT.
;
CONTD2	LD	HL,(EDCUR)	;ADDR IS CORRECT HERE.
	LD	(HL),BLANK	;REMOVE OLD CURSOR.
	LD	HL,(ADDR)	;GET ADDR TO DUMP.
	CALL	MAKSTR		;MAKE DUMP STRING OF IT.
;
	LD	A,15		;SCROLL BOTTOM 15 LINES
	LD	HL,3C40H	;  UP, MOVING ONLY THE
	LD	DE,3C00H	;  LEFT 55 CHARACTERS.
MVLOPA	LD	BC,37H
	LDIR
	LD	BC,9
	ADD	HL,BC		;POINT TO NEXT LINE.
	EX	DE,HL
	ADD	HL,BC		;AND WHERE IT WILL GO.
	EX	DE,HL
	DEC	A
	JR	NZ,MVLOPA	;BACK IF 15 NOT DONE.
;
	LD	HL,LINE16	;POINT TO LAST LINE.
	LD	(CURPOS),HL
	LD	HL,STRPLC
	CALL	OUTSTR		;PRINT DUMP STRING THERE.
	LD	BC,16		;SUB 16 FROM ADDR, HOMADD
	CALL	AADDR
	CALL	AHOMAD
	LD	A,1		;NOTE SCROLL CAUSED BY
	LD	(LSTARW),A	;  DOWN-ARROW.
	RET
;
;
; SUBROUTINE MAKSTR EXPECTS TO RECEIVE AN ADDRESS IN HL.
; IT FORMS THIS ADDRESS AND THE 16 BYTES OF DATA STARTING
; THERE INTO AN ASCII STRING AT  STRPLC .  IF THE TYPE OF
; DUMP REQUIRED IS HEX, THEN EACH CHARACTER BLOCK (THERE
; ARE 16 OF THESE AFTER THE ADDRESS STRING) WILL CONTAIN
; THE ASCII REPRESENTATION OF THE BYTE IN MEMORY. IF THE
; TYPE OF DUMP IS ASCII, THEN EACH BLOCK WILL SIMPLY
; CONTAIN THE ASCII CODE OF THE BYTE IN MEMORY.  SPACES
; ARE ADDED SO THAT EITHER WAY EACH CHARACTER BLOCK TAKES
; UP THREE POSITIONS.  A TOTAL OF 64 BYTES IS REQUIRED TO
; HOLD THIS STRING AND ITS '03' TERMINATOR.
MAKSTR	PUSH	HL		;SAVE ADDRESS.
	PUSH	HL
	POP	IX		;PUT IT IN IX.
	POP	BC		;GET MSBYTE OF ADDRESS.
	LD	HL,STRPLC+3	;LAST DIGIT OF ADDRESS.
	CALL	BSTRNG		;BC INTO ASCII AT HL- .
;
	LD	HL,STRPLC+4	;POINT AFTER ADDRESS.
	LD	(HL),':'        ;PUT : AFTER ADDRESS.
	INC	HL
	LD	(HL),' '        ;PUT SPACE AFTER ADDRESS.
;
	INC	HL		;POINTS TO 1ST CHAR. BLK.
	LD	B,16		;WILL DO 16 CHAR. BLOCKS.
CHRBLK	LD	(HL),' '        ;EACH START WITH BLANK.
	INC	HL
	LD	C,(IX+0)	;GET BYTE AT ADDRESS.
	LD	A,(HATYPE)	;CHECK DUMP TYPE.
	AND	A
	JR	NZ,ACHAR	;GO IF ASCII DUMP.
	CALL	ASCII		;HEX DUMP REQ'D.
	LD	(HL),D		;FIRST DIGIT OF BYTE.
	INC	HL
	LD	(HL),E		;SECOND DIGIT OF BYTE.
	JR	CHRDN		;ONE BLOCK DONE.
;
ACHAR	CALL	VALASC		;ASCII DUMP.  CHECK CHAR.
	LD	(HL),A		;PUT FINAL CHAR IN STRING
	INC	HL
	LD	(HL),' '        ;FOLLOW WITH BLANK.
;
CHRDN	INC	IX		;DONE WITH CHAR. BLOCK.
	INC	HL		;POINT TO START NEXT BLK.
	DJNZ	CHRBLK		;DO NEXT ONE.
;
	LD	(HL),0AAH	;ONE CHAR. FOR BORDER.
	INC	HL
	LD	B,8		;EIGHT CURSOR-ADVANCES AT
TRALER	LD	(HL),19H	;  END RESULT IN STRING
	INC	HL		;  PRINTING TO END OF
	DJNZ	TRALER		;  LINE MINUS ONE.
;
	LD	(HL),03H	;TERMINATOR OF STRING.
	RET			;END OF MAKSTR.
;
;
; SUBROUTINE BSTRNG CONVERTS THE TWO BYTES IN BC INTO AN
; ASCII STRING OF FOUR HEX DIGITS WHICH IT LOADS INTO
; HL-3, HL-2, HL-1, HL.
BSTRNG	CALL	ASCII		;CONVERT BYTE IN C.
	LD	(HL),E
	DEC	HL
	LD	(HL),D
	DEC	HL
	LD	C,B		;NOW CONVERT B.
	CALL	ASCII
	LD	(HL),E
	DEC	HL
	LD	(HL),D
	RET
;
;
; SUBROUTINE ASCII TAKES A BYTE IN C AND RETURNS, IN DE,
; THE ASCII CODES REPRESENTING ITS TWO DIGITS.
ASCII	LD	A,C
	CALL	SBRASC		;DO FIRST 4 BITS.
	LD	E,A
	LD	A,C
	RRCA			;BRING NEXT 4 OVER.
	RRCA
	RRCA
	RRCA
	CALL	SBRASC		;CONVERT THEM TOO.
	LD	D,A
	RET
;
SBRASC	AND	0FH		;CHANGE LOWER 4 BITS OF
	OR	30H		;  A TO ASCII BYTE.
	CP	3AH
	RET	C
	ADD	A,07
	RET
;
;
; SUBROUTINE OUTSTR CALLS BYTDIS TO PRINT A STRING
; POINTED TO BY HL AT THE CURRENT CURSOR POSITION.
; IT RECOGNIZES 00, 01, 02, OR 03 AS A TERMINATOR.
OUTSTR	LD	A,(HL)		;GET CHAR. FROM STRING.
	CP	04		;ONE OF THE TERMINATORS?
	RET	C		;RETURN IF SO (0-3).
	CALL	BYTDIS		;ELSE PRINT IT.
	INC	HL
	JR	OUTSTR		;GO DO NEXT BYTE.
;
;
; AADDR - A UTILITY THAT ADDS BC TO ADDR.
AADDR	LD	HL,(ADDR)
	ADD	HL,BC
	LD	(ADDR),HL
	RET
;
;
; AHOMAD - A UTILITY THAT ADDS BC TO HOMADD.
AHOMAD	LD	HL,(HOMADD)
	ADD	HL,BC
	LD	(HOMADD),HL
	RET
;
;
; SADDR - A UTILITY THAT SUBTRACTS BC FROM ADDR.
SADDR	LD	HL,(ADDR)
	XOR	A
	SBC	HL,BC
	LD	(ADDR),HL
	RET
;
;
; SHOMAD - A UTILITY THAT SUBTRACTS BC FROM HOMADD.
SHOMAD	LD	HL,(HOMADD)
	XOR	A
	SBC	HL,BC
	LD	(HOMADD),HL
	RET
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - 
;    MESSAGES
;
INMSG	DEFM	'ADDRESS?'
	DEFB	03
;
PENMSG	DEFM	'-PENRAM-'
	DEFB	03
;
;
	END	STARTP
;
;
;          FIG. 1:  SOURCE CODE FOR PENRAM
;
;********************************************************
