;DODVR2/ASM - 12/29/83 - Model II
;
*MOD
CRTBGN$	EQU	0F800H
$COLS80	EQU	80		;chars / line normal
$COLS40	EQU	40		;chars / line double wide
$ROWS	EQU	24		;video lines
$VSIZE	EQU	$COLS80*$ROWS	;video size normal
$VHSIZE	EQU	$COLS40*$ROWS	;video size double wide
;
DODVR	JR	DOBGN
	DW	DOEND
	DB	3,'$DO'
	DW	DODCB$
	DW	0
DODATA$	EQU	$
DO_MASK	EQU	$-DODATA$
SCRPROT	EQU	7		;Bits 0-2: scroll protect
TABS	EQU	3		;Bit 3: 0=tabs, 1=chars
CTL	EQU	4		;Bit 4, display controls
	IF	@USA
	DB	0		;Tab/Spec, Scroll protect
	ENDIF
	IF	@INTL
	DB	08		;Space compression off
	ENDIF
;
CURSOR	DW	CRTBGN$		;cursor position
CRSAVE	DB	' '		;char under cursor
$CRSCHAR DB	01000111B	;cursor char
CRTD1	DW	$COLS80		;# video columns
CRTD2	DW	$VSIZE+CRTBGN$	;end scroll area
CRTD3	DW	$VSIZE-$COLS80	;scroll count
;
@VDCTL	JP	@_VDCTL		;go vector!
;
DOBGN	LD	IX,DODATA$
	CALL	ENADIS_DO_RAM	;enable video memory
	LD	DE,(CURSOR)	;fetch cursor address
	JR	C,DOGET
	PUSH	BC		;Need to save C
	LD	A,C		;Get char to display
	BIT	CTL,(IX+DO_MASK)
	JR	NZ,$?1A		;No CTL op if set
	OR	A		;Toggle controls?
	JP	Z,TGGLCTL
	CP	20H		;Go if a control
	JP	C,DO_CONTROL
$?1A	CP	0C0H		;Tab or special?
	JR	C,DONORM	;Go on normal characters
;*=*=*
;	Character is => 0C0H
;*=*=*
	BIT	TABS,(IX+DO_MASK) ;Tabs or spec chars
	JR	Z,DO_TABS	;Go if video tabs
;*=*=*
;	Character is not tab expansion - do it
;*=*=*
DONORM	CALL	DO_DSPCHAR	;Display the char
	RES	CTL,(IX+DO_MASK) ;Turn off CTL bit
DO_RET	POP	BC		;Get orig char
DO_RET1	LD	(CURSOR),DE	;Update cursor position
	LD	A,(DE)		;get char under cursor
	LD	(CRSAVE),A	;and save it
;
;	position hardware cursor
;
	PUSH	HL		;save
	LD	HL,LCKFLG$	;lockout flag
	SET	6,(HL)		;skip RTC task
	LD	A,14		;register number
	OUT	($CRTADD),A	;load CRTC address
	LD	A,D		;get MSB address
	AND	7		;remove video page
	OUT	($CRTDAT),A	;load CRTC data
	LD	A,15		;register number
	OUT	($CRTADD),A	;load CRTC address
	LD	A,E		;get LSB address
	OUT	($CRTDAT),A	;load CRTC data
	RES	6,(HL)		;enable RTC
	POP	HL		;restore
	CP	A		;Clear status
;
DOGET1	LD	A,C		;Restore the char
	RET
;*=*=*
;	Get character under cursor
;*=*=*
DOGET	LD	A,(DE)		;get character
	LD	C,A		;  pass it back
	CP	A		;set Z for OK
	RET			;and return it
;*=*=*
;	Routine to stuff the video cursor RAM address
;*=*=*
@VDCTL3	PUSH	HL		;save
	CALL	ROWCOL_2_ADDR	;Calculate video address
	JR	NZ,VDCTL3X	;back on error
	CALL	DO_RET1		;update cursor
VDCTL3X	POP	HL		;restore
	RET			;done
;*=*=*
;	Perform a space compression code expansion (C0-FF
;*=*=*
DO_TABS
	SUB	0C0H		;  else compute tabs
	JR	Z,DO_RET	;Forget it if TAB(0)
	LD	B,A		;Display requested
$?2	LD	C,' '		;  number of spaces
	CALL	DO_DSPCHAR
	DJNZ	$?2
	JR	DO_RET
;*=*=*
;	Routine to move the cursor to begin of line {29}
;*=*=*
CRSBOL	CALL	FNDBEG		;find line beginning
	EX	DE,HL		;DE = new cursor
	RET			;done!
;
;	locate beginning of current line
;
FNDBEG	PUSH	DE		;save cursor
	LD	HL,CRTBGN$	;video start
	LD	BC,(CRTD1)	;BC = # columns
;
CRSBOL1	PUSH	HL		;save start
	ADD	HL,BC		;HL => next line
	SCF			;for test
	SBC	HL,DE		;compare to current
	POP	HL		;restore start
	JR	NC,CRSBOL2	;go if found!
	ADD	HL,BC		;HL => next line
	JR	CRSBOL1		;continue
;
CRSBOL2	POP	DE		;DE=cursor, HL=line begin
	XOR	A		;clear carry, no error
	RET			;done
;*=*=*
;	Routines to turn on/off the cursor {14/15}
;*=*=*
$CRSOFF	LD	A,00100111B	;cursor off
	JR	CRSGO+3		;continue
;
$CRSON	LD	A,($CRSCHAR)	;get cursor char
;
CRSGO	LD	($CRSCHAR),A	;save current cursor
	PUSH	BC		;save
	PUSH	HL		;save
	LD	HL,VFLAG$	;video flag
	SET	6,(HL)		;set NOT blinking
	CP	00100111B	;off?
	JR	Z,$+4		;go if not
	RES	6,(HL)		;set blinking
	LD	HL,LCKFLG$	;lockout flag
	SET	6,(HL)		;set no RTC tasks
	LD	BC,10<8+$CRTADD	;data + port
	OUT	(C),B		;select CRTC register 10
	INC	C		;bump to CRTC data port
	OUT	(C),A		;set cursor char
	LD	BC,11<8+$CRTADD	;data + port
	OUT	(C),B		;select register 11
	INC	C		;to data port
	LD	A,7		;bottom row
	OUT	(C),A		;set cursor lower bounds
	RES	6,(HL)		;enable RTC tasks
	POP	HL		;restore
	POP	BC		;restore
	XOR	A		;set NO error
	RET
;*=*=*
;	Routine moves cursor to start of video page {28}
;*=*=*
SET40	CALL	CRT40		;set 40 columns
	CALL	CRSHOMX		;set normal mode
	JP	CLREOF		;clear video
;
CRSHOME
	CALL	CRT80		;set 80 columns
CRSHOMX	CALL	SETMOD2		;setup for exit
	CALL	DO_INVERT_DIS	;Set to normal video
	LD	DE,CRTBGN$	;start video
	RET			;return with cursor
;
;	locate address of 'HOME' in scroll protected vid
;
FNDHOME	LD	A,(IX+DO_MASK)	;Get scroll protect
	AND	7
	LD	DE,CRTBGN$	;start video memory
;
$?3	RET	Z		;Back if done
	LD	HL,(CRTD1)	;L = chars/line
	ADD	HL,DE
	EX	DE,HL		;DE = new cursor location
	DEC	A		;Count down & loop
	JR	$?3
;*=*=*
;	Routine to backspace & erase cursor {08}
;*=*=*
BACKSPA
	CALL	CRSBKSP		;Backspace the cursor
	LD	C,' '		;  the last position
	JP	PUT_@		;Put the char
;*=*=*
;	Routine to backspace the cursor {24}
;*=*=*
CRSBKSP	DEC	DE		;move cursor left
	JR	CKVTOP		;in range?
;*=*=*
;	Routine to move the cursor up one line {27}
;*=*=*
CRSUP	CALL	FNDBEG		;locate line beginning
	LD	DE,(CRTD1)	;get # columns
	SBC	HL,DE		;HL => new cursor
	EX	DE,HL		;DE => new cursor
;
;	check if cursor still in video
;
CKVTOP	LD	HL,CRTBGN$	;start video
	PUSH	HL		;save start
	SCF			;set for test
	SBC	HL,DE		;cursor in video?
	POP	HL		;get start
	RET	C		;in range, go!
	EX	DE,HL		;else force in video
	RET			;done!
;*=*=*
;	Routine to move the cursor down one line {26}
;*=*=*
CRSDOWN
	LD	HL,(CRTD1)	;get line length
	ADD	HL,DE		;HL = one row down
	EX	DE,HL		;  & switch back to DE
	JP	CKVCUR		;check if cursor valid!
;*=*=*
;	Set to 40 cpl mode
;*=*=*
CRT40	LD	E,00010000B	;bit 4 on
	LD	HL,CRTD40	;crtc data 40 cols
	JR	SETMOD
;
;	set to 80 cpl mode
;
CRT80	LD	E,00000000B	;bit 4 off
	LD	HL,CRTD80	;crtc data 80 cols
;
;	set new mode to CRTC
;
SETMOD	LD	BC,4<8+$CRTDAT	;count + crtc port
	XOR	A		;start register
SETMOD1	OUT	($CRTADD),A	;set crtc address
	INC	A		;next register
	OUTI			;send data to crtc
	JR	NZ,SETMOD1	;go for 4 bytes
;
;	setup new driver data
;
	PUSH	DE		;save mask
	LD	DE,CRTD1	;# columns
	LD	C,4		;4 bytes to move
	LDIR			;move to data block
;
;	setup scroll count for protected video
;
	CALL	FNDHOME		;locate HOME
	LD	HL,(CRTD2)	;get end scroll area
	OR	A		;clear carry
	SBC	HL,DE		;HL = scroll count
	LD	DE,(CRTD1)	;get # columns
	OR	A		;clear carry
	SBC	HL,DE		;HL = scroll count
	LD	(CRTD3),HL	;load scroll count
	POP	DE		;restore mask
	RET			;done!
;
;	correct modout flag for exit
;
SETMOD2	LD	HL,(OPREG_SV_PTR)	;get table pntr
	DEC	HL		;last saved byte
	LD	A,(HL)		;get data
	AND	11101111B	;drop bit 4
	OR	E		;add/delete new bit
	LD	(HL),A		;update data
	RET			;done
;*=*=*
;	Routines to parse control functions
;*=*=*
DO_CONTROL
	LD	HL,DO_RET	;Establish RET
	PUSH	HL
	LD	HL,VIDTBL	;video control table
	CALL	@LOOKUP		;find it!
	RET	NZ		;not found!
	JP	(HL)		;else go driver!
;
;	video control table
;
VIDTBL	DB	8		;backspace
	DW	BACKSPA
	DB	10		;linefeed
	DW	LINFEED
	DB	13		;carriage return
	DW	LINFEED
	DB	14		;cursor on
	DW	$CRSON
	DB	15		;cursor off
	DW	$CRSOFF
	DB	16		;reverse video
	DW	DO_INVERT_ENA
	DB	17		;normal video
	DW	DO_INVERT_DIS
	DB	21		;tab/alternate
	DW	TGGLTAB
	DB	23		;40 cpl
	DW	SET40
	DB	24		;cursor left
	DW	CRSBKSP
	DB	25		;cursor right
	DW	CRSFRWD
	DB	26		;cursor down
	DW	CRSDOWN
	DB	27		;cursor up
	DW	CRSUP
	DB	28		;cursor home
	DW	CRSHOME
	DB	29		;cursor bol
	DW	CRSBOL
	DB	30		;clear eol
	DW	CLREOL
	DB	31		;clear eof
	DW	CLREOF
	DB	-1		;terminator
;*=*=*
;	Routine to enable inverted video
;*=*=*
DO_INVERT_ENA
	LD	A,80H		;Set for enable
	DB	2EH		;Ignore next load
DO_INVERT_DIS
	XOR	A
	LD	(INVIDEO),A	;Set bit-7 mode
;
;	update OPREG$ bit 3 to show state
;
	RRCA			;move bit 7 => 3
	RRCA
	RRCA
	RRCA
	LD	(OPREG$),A	;update flag
	RET
;*=*=*
;	Routine to toggle display of controls
;*=*=*
TGGLCTL	LD	HL,DO_RET
	PUSH	HL
	LD	A,10H
	DB	21H		;Ignore next
;*=*=*
;	Toggle tabs & alternate character set
;*=*=*
TGGLTAB
	LD	A,8		;Toggle bit 3
	XOR	(IX+DO_MASK)	;P/u mask value
	JR	SETMASK
;*=*=*
;	Display character <C> at current cursor position
;*=*=*
DO_DSPCHAR
	CALL	PUT_@		;Display the char
;*=*=*
;	Routine to perform cursor forward {25}
;*=*=*
CRSFRWD	INC	DE		;bump cursor to next
;
;	check if cursor in valid video memory
;
CKVCUR	LD	HL,(CRTD2)	;end cursor memory +1
	SCF			;set for test
	SBC	HL,DE		;cursor in video?
	RET	NC		;yes, go!
;
DO_SCROLL
	CALL	FNDHOME		;locate 'HOME'
	LD	H,D		;pass to HL
	LD	L,E		;HL/DE => video start
	PUSH	BC		;save
	LD	BC,(CRTD1)	;get # columns
	ADD	HL,BC		;HL => next row
	LD	BC,(CRTD3)	;scroll count
	LDIR			;scroll video
	POP	BC		;restore
	JR	CLREOF		;Clear to EOF from DE
;*=*=*
;	Set scroll protect value
;		C = scroll protect <0-7>
;		B = 7
;		SVC = 15, @VDCTL
;*=*=*
SET_SCROLL
	LD	A,C		;Get user value
	AND	7		;Make modulo 8
	LD	C,A
	LD	A,(DODATA$)	;P/u current mask
	AND	0F8H		;Remove current scroll
	OR	C		;Merge in the new value
SETMASK	LD	(DODATA$),A	;  & reload mask
	XOR	A		;Z-flag return
	RET
;*=*=*
;	Routine to move down one line {10/13}
;*=*=*
LINFEED	CALL	CRSBOL		;Move to BOL
	LD	HL,(CRTD1)	;line length
	ADD	HL,DE		;HL => next line
	EX	DE,HL		;DE => next line
	CALL	CKVCUR		;scroll needed?
;
;	clear to end of line
;
CLREOL	CALL	FNDBEG		;locate line beginning
	LD	BC,(CRTD1)	;get line length
	ADD	HL,BC		;HL => line end
	SBC	HL,DE		;HL = blank count
	JR	DOCLR		;clear area
;*=*=*
;	Clear to the end of the frame
;*=*=*
CLREOF	LD	HL,(CRTD2)	;get end scroll area
	OR	A		;clear carry
	SBC	HL,DE		;HL = scroll count
;
;	clear area from DE for HL bytes
;
DOCLR	PUSH	DE		;save cursor
	PUSH	BC		;save count
	LD	B,H		;pass count
	LD	C,L		;BC = count
	EX	DE,HL		;HL => cursor
	LD	A,(INVIDEO)	;get inverse video mask
	OR	' '		;create character
;
DOCLR1	LD	(HL),A		;output char
	CPI			;bump HL, check if done
	JP	PE,DOCLR1	;go for count
	POP	BC		;restore
	POP	DE		;restore cursor
	RET			;done
;*=*=*
;	Video control SVC processor
;*=*=*
@_VDCTL
	PUSH	DE		;save
	CALL	DOVDCTL		;execute
	POP	DE		;unstack
	RET			;return status
;
DOVDCTL	CALL	ENADIS_DO_RAM	;Bring up the video RAM
	PUSH	HL		;save
	LD	HL,VDCTBL	;lookup table
	LD	A,B		;get command
	CALL	@LOOKUP		;locate vector address
	EX	(SP),HL		;leave vector, get HL
	RET	Z		;go vector if found
	POP	AF		;remove vector
	JP	PERR		;param error!
;
VDCTBL	DB	1		;get char at HL
	DW	GET_@_ROWCOL
	DB	2		;put char at HL
	DW	PUT_@_ROWCOL
	DB	3		;set cursor to HL
	DW	@VDCTL3
	DB	4		;get cursor to HL
	DW	ADDR_2_ROWCOL
	DB	5		;buffer to video
	DW	VIDMOV1
	DB	6		;video to buffer
	DW	VIDMOVE
	DB	7		;set scroll protect
	DW	SET_SCROLL
	DB	8		;set cursor char
	DW	SETCC
	DB	9		;video line <> buffer
	DW	VIDLIN
	DB	-1		;terminator
;
VIDLIN	LD	L,0
	PUSH	DE		;Save user buffer =>
	CALL ROWCOL_2_ADDR	;Get address to DE
	POP	HL		;Recover user buffer
	RET	NZ
	INC	C		;Check direction I<>O
	DEC	C		; if Z then to screen
	JR	Z,MOVLIN	;Set to go
	EX	DE,HL		; reverse direction
MOVLIN	LD	BC,(CRTD1)	;get # video columns
	LDIR			;Move it
	XOR	A		;set Z for OK
	RET
;
SETCC	LD	A,($CRSCHAR)	;get cursor char
	PUSH	AF		;save on stack
	LD	A,C		;get new cursor char
	CALL	CRSGO		;setup cursor!
	POP	AF		;restore old cursor
	CP	A		;set Z for OK
	RET			;return old cursor
;*=*=*
;	Routine to move video RAM
;*=*=*
VIDMOVE	LD	A,H		;Check on user buffer
	ADD	A,8		;  not above X'F800' &
	CP	24H+8		;  not below X'2400'
	JR	C,PERR		;out of range!
	LD	DE,CRTBGN$	;video start
	EX	DE,HL
	JR	VIDMOV2		;continue
;
VIDMOV1	LD	DE,CRTBGN$	;video start
VIDMOV2	PUSH	HL		;save source for return
	PUSH	HL		;save source
	LD	HL,(CRTD2)	;get video end
	LD	BC,CRTBGN$	;get video start
	OR	A		;clear carry
	SBC	HL,BC		;HL = count
	LD	B,H		;pass to BC
	LD	C,L		;BC = video length
	POP	HL		;restore start
	LDIR
	POP	HL		;restore start
	XOR	A		;set NO error
	RET
;*=*=*
;	Routine to get the character at row,col
;*=*=*
GET_@_ROWCOL
	CALL	ROWCOL_2_ADDR	;Get Address of req
	LD	A,(DE)		;P/u the character
	RET			;Back on error or no err
;*=*=*
;	Routine to put a character at row,col
;*=*=*
PUT_@_ROWCOL
	CALL	ROWCOL_2_ADDR	;Get address of req
	RET	NZ		;Back on error
PUT_@	LD	A,0		;Merge in reverse video
INVIDEO	EQU	$-1
	OR	C
PUTA@DE	LD	(DE),A		;Put the character
	CP	A		;Set Z-flag for return
	RET
;*=*=*
;	Routine to calculate cursor position from row,col
;*=*=*
ROWCOL_2_ADDR
	LD	A,(CRTD1)	;get video columns
	DEC	A		;adjust for test
	CP	L
	JR	C,PERR		;Error if > cols-1
	LD	A,H		;P/u row number
	CP	24
	JR	NC,PERR		;Error if > 23
;
	PUSH	HL
	PUSH	BC
	LD	C,L		;Save column
	LD	B,CRTBGN$<-8	;Set to start of DO RAM
	LD	HL,(CRTD1)	;get # columns
	CALL	@MUL16
	LD	H,L		;Shift to HL
	LD	L,A
	ADD	HL,BC		;Add in col & RAM start
	EX	DE,HL		;Address to DE
	POP	BC
	POP	HL
	XOR	A
	RET
PERR	LD	A,43
	OR	A
	RET
;*=*=*
;	Routine to get row,col of video cursor
;*=*=*
ADDR_2_ROWCOL
	LD	HL,(CURSOR)	;Get addr in HL
	LD	A,H		;Make address relative
	AND	7		;  to origin 0
	LD	H,A
	LD	A,(CRTD1)	;Set divisor @ #cols
	CALL	@DIV16
	LD	H,L		;Row to register H
	LD	L,A		;Column to register L
	XOR	A		;Set zero return code
	RET
;
;	CRTC initialization data
;
CRTD40	DEFB	40+9		;horizontal total
	DEFB	40		;horiz displayed
	DEFB	40+3		;horiz sync posit
	DEFB	4		;horiz sync width
	DEFW	40		;# video columns
	DEFW	CRTBGN$+$VHSIZE	;end video +1
;
CRTD80	DEFB	80+19		;horiz total
	DEFB	80		;horiz displayed
	DEFB	80+5		;horiz sync posit
	DEFB	8		;horiz sync width
	DEFW	80		;# video columns
	DEFW	CRTBGN$+$VSIZE	;end video+1
;
DOEND	EQU	$-1
