perm filename DISPLY.MID[S,NET] blob sn#683501 filedate 1983-01-28 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00025 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	Display routines, intended to be .INSRT'ed
C00006 00003	 Data area
C00008 00004	 Display commands and programs
C00010 00005	 DPYINI -- Initialize display package
C00012 00006	 CLRSCN -- Clear display screen (does explicit update as well)
C00014 00007	 SCSTOR -- Store a character at the current screen V/H position
C00015 00008	 CLREOL -- Clear to end of line
C00016 00009	 CLREOF -- Clear to end of screen
C00017 00010	 CLR1CH -- Delete a character at the cursor
C00018 00011	 CSRPOS -- position cursor
C00019 00012	 TERPRI -- CR, LF, CLEOL, scroll if necessary
C00020 00013	 CARRET -- CR
C00021 00014	 LINEFD -- LF, CLEOL, scroll if necessary
C00022 00015	 LINSRV -- Line starve
C00023 00016	 CSRTAB -- Tab cursor
C00024 00017	 CSRAOS -- Forespace cursor
C00025 00018	 CSRSOS -- Backspace cursor
C00026 00019	 INSLIN -- Insert a blank line
C00028 00020	 DELLIN -- Delete a blank line
C00030 00021	 INSCHR -- Insert a blank character
C00033 00022	 DELCHR -- Delete a character
C00036 00023	 SCNUPD -- Update screen
C00039 00024	 GETCSY UDPLIN redisplay subroutines
C00041 00025	 DMCHAR DMOUT Datamedia output routines
C00043 ENDMK
C⊗;
SUBTTL Display routines, intended to be .INSRT'ed

; Mark Crispin, SU-AI, July 1978

;  This is a library of display hacking routines.  Each routine documents its
; calling sequence and what AC's it smashes.  Only AC's 0-6 are used.  A
; pushdown stack is expected in 17.
;  Bugs → MRC.

.BEGIN DISPLY

; Useful definitions

NLINES==40.				; lines in screen matrix
LINEL==88.				; true maximum number of characters/line
DMBUFL==100.				; size of DM buffer
NCHARS==<<<LINEL+5>/5>*5>		; characters/line in line array
NWRDLN==4+NCHARS/5			; number of words in line array
SCRSIZ==NLINES*NWRDLN			; number of words in screen matrix
NGW==<<NCHARS*3>+17.>/16.		; number of graphics words

; Line characteristics bits

DISLIN==400000,,			; III
DMLIN== 040000,,			; DM
DDDLIN==020000,,			; DD

; DM UPGIOT flags

TRUNCA==040000,,			; truncate output lines to 80. characters
NOEEOL==020000,,			; suppress CEOL when moving to a line
NOEEOB==010000,,			; suppress CEOL on blank line

; Generate a Data Disc/III command

DEFINE DDCMD O1,D1,O2,D2,O3,D3
 .BYTE 8.,8.,8.,3.,3.,3.,3.
  D1 ? D2 ? D3 ? O1 ? O2 ? O3 ? 4
 .BYTE
TERMIN

; Insert a command in a DM display program

DEFINE DMCMD CH
 MOVE DMCNT
 CAIGE 10
  PUSHJ 17,DMOUT
 MOVEI 177
 PUSHJ 17,DMCHAR
 MOVEI CH
 PUSHJ 17,DMCHAR
TERMIN
; Data area

DPCBEG==.				; beginning of core area zeroed at DPYINI

; Terminal characteristics

DDP:	BLOCK 1				; -1 → Data Disc
IIIP:	BLOCK 1				; -1 → III
DMP:	BLOCK 1				; -1 → Datamedia
.U"HSIZE:
	BLOCK 1				; horizontal size
.U"VSIZE:
	BLOCK 1				; vertical size
.U"ROLLP:
	BLOCK 1				; -1 → scrolling permission

; Cursor position pointers

VPOS:	BLOCK 1				; vertical position
HPOS:	BLOCK 1				; horizontal position
OVPOS:	BLOCK 1				; old vertical position

; Screen updating flags

SLUPDP:	BLOCK NLINES			; -1 → this line has changed
SCUPDP:	BLOCK 1				; -1 → some update happened someplace
SAUPDP:	BLOCK 1				; -1 → updated whole screen
CRUPDP:	BLOCK 1				; -1 → updated cursor
SCRLCT:	BLOCK 1				; for forcing update in LINEFD scrolling

; DM display fields

DMCNT:	BLOCK 1				; DM program counter
DMPNT:	BLOCK 1				; DM program counter

DPCEND==.-1				; end of core data
; Display commands and programs

IIIHDR:	.BYTE 11.,11.,3.,3.,2.,2.,4.
	-777 ? 640 ? 4 ? 2 ? 1 ? 2 ? 6
	.BYTE

SDISP:	600000,,SCP ? SCPL ? 0 ? SCP	; display screen
SCP:	DDCMD 1,46,4,1,5,10
	DDCMD 3,2,3,2,3,2
SCREEN:	BLOCK SCRSIZ
SCREND=.-1
	0
SCPL==.-SCP
BOTLIN==SCREEN+SCRSIZ-NWRDLN+2-1

CDISP:	SCC ? SCCL ? 0 ? SCC+1		; display cursor
SCC:	DDCMD 1,7,1,7,1,7
	DDCMD 3,1,4,0,5,0
	BLOCK NGW
	DDCMD 0,0,1,46,1,46
	0
SCCL==.-SCC

LDISP:	200000,,LNP ? NWRDLN ? 0 ? LNP	; display line
LNP:	DDCMD 1,46,4,0,5,0
	DDCMD 3,2,3,2,3,2
	BLOCK NWRDLN-3
	0

DMDISP:	TRUNCA\NOEEOL\NOEEOB+DMPGM ? 0 ? 0; DM display program
DMPGM:	BLOCK DMBUFL
; DPYINI -- Initialize display package
; Call:	PUSHJ 17,DPYINI
;	<return>
; Smashes 0.

.U"DPYINI:
	SETZM DPCBEG
	MOVE [DPCBEG,,DPCBEG+1]
	BLT DPCEND			; initialize data area
	HRROI [3000,,]			; real line chars
	TTYSET				; get my line characteristics
	TLNN (DISLIN\DDDLIN\DMLIN)	; display
	 JRST [	OUTSTR [ASCIZ/Not a display!/]
		EXIT]
	TLNE (DISLIN)
	 SETOM IIIP
	TLNE (DDDLIN)
	 SETOM DDP
	TLNE (DMLIN)
	 SETOM DMP
	MOVE [-2,,[6000,,HSIZE ? 15000,,VSIZE]]
	TTYSET				; get screen size information
	MOVNI 2 ? ADDM VSIZE
	SOS HSIZE
	PPACT
	LEYPOS 2000			; flush page printer and line editor
	MOVE VSIZE
	MOVEM SCRLCT
	SETOM ROLLP			; grant scrolling permission
	SKIPN IIIP
	 JRST NOTIII
	SETZM SCP			; different header for III's
	MOVE IIIHDR
	MOVEM SCP+1
NOTIII:	SETOM OVPOS
	SKIPN DMP
	 POPJ 17,
	MOVEI <5*DMBUFL>-4
	MOVEM DMCNT			; initialize DM counter
	MOVE [440700,,DMPGM]
	MOVEM DMPNT			; initialize DM pointer
	POPJ 17,
; CLRSCN -- Clear display screen (does explicit update as well)
; Call:	PUSHJ 17,CLRSCN
;	<return>
; Smashes 0, 1, 2, and 3.

.U"CLRSCN:
	SETZM VPOS
	SETZM HPOS
	MOVE [ASCII/     /+1]
	MOVEM SCREEN
	MOVE [SCREEN,,SCREEN+1]
	BLT SCREND			; initialize screen to spaces
	MOVE [ASCII/
/+1]					; terpri
	SETZ 1,				; line address
	DMOVE 2,[1 ? NLINES]		; blank word/line counter
SCRIN1:	MOVEM 2,SCREEN(1)
	MOVEM 2,SCREEN+1(1)
	MOVEM SCREEN+NWRDLN-2(1)
	MOVEM 2,SCREEN+NWRDLN-1(1)
	ADDI 1,NWRDLN
	SETZM SLUPDP-1(3)		; clear update necessary state for line
	SOJG 3,SCRIN1
	MOVEI 2
	MOVEM SCC+2
	MOVE [SCC+2,,SCC+3]
	BLT SCC+2+NGW-1			; initialize graphics words
	SETOM SAUPDP			; updated entire screen
	PUSHJ 17,SCNUPD
	SKIPN DMP
	 POPJ 17,

	DMCMD ↑L			; cursor ← [0,2]
	MOVEI 140
	PUSHJ 17,DMCHAR
	MOVEI 142
	PUSHJ 17,DMCHAR
	MOVE 1,VSIZE
DMCEOF:	DMCMD ↑W			; clear current line
	DMCMD ↑M			; move to next line
	SOJG 1,DMCEOF
	DMCMD ↑L			; cursor ← [0,2]
	MOVEI 140
	PUSHJ 17,DMCHAR
	MOVEI 142
	PUSHJ 17,DMCHAR
	JRST DMOUT
; SCSTOR -- Store a character at the current screen V/H position
; Call:	MOVE <character>
;	PUSHJ 17,SCSTOR
;	<return>
; Smashes 0, 1, 2, and 3.

.U"SCSTOR:
	MOVE 1,VPOS
	SETOM SLUPDP(1)			; updated flags
	SETOM SCUPDP
	IMULI 1,NWRDLN
	MOVE 2,HPOS
	CAMLE 2,HSIZE
	 JRST [	AOS HPOS		; line too long
		POPJ 17,]
	IDIVI 2,5			; word position in line
	ADDI 1,SCREEN+2(2)		; offset by lines previous
	DPB SCBYTP(3)			; save character on screen
	AOS HPOS
	SKIPE DMP
	 JRST DMCHAR
	POPJ 17,

; Byte pointer table for insertions

SCBYTP:	350700,,(1)
	260700,,(1)
	170700,,(1)
	100700,,(1)
	010700,,(1)
; CLREOL -- Clear to end of line
; Call:	PUSHJ 17,CLREOL
;	<return>
; Smashes 0, 1, 2, 3, and 4.

.U"CLREOL:
	SKIPN DMP
	 JRST CLEOL0
	DMCMD ↑W			; clear to end of line
CLEOL0:	MOVE 4,HPOS
	CAIL 4,LINEL
	 POPJ 17,
	MOVE 1,VPOS
	SETOM SLUPDP(1)			; flag this line changed
	SETOM SCUPDP
	IMULI 1,NWRDLN
	MOVE 2,HPOS
	IDIVI 2,5
	ADDI 1,SCREEN+2(2)		; address of word to hack
	MOVE 2,SCBYTP(3)
	MOVEI <" >
	DPB 2				; clear character
CLEOL1:	ADDI 4,1			; next character position
	CAIL 4,LINEL
	 POPJ 17,
	IDPB 2
	JRST CLEOL1
; CLREOF -- Clear to end of screen
; Call:	PUSHJ 17,CLREOF
;	<return>
; Smashes 0, 1, 2, 3, and 4.

.U"CLREOF:
	PUSH 17,HPOS
	PUSH 17,VPOS
CLEOF1:	PUSHJ 17,CLREOL			; clear to end of line
	DMCMD ↑M			; next line
	SETZM HPOS
	AOS 1,VPOS
	CAIGE 1,NLINES
	 JRST CLEOF1
	POP 17,VPOS
	POP 17,HPOS
	DMCMD ↑L			; cursor ← [0,2]
	MOVEI 140
	PUSHJ 17,DMCHAR
	MOVEI 142
	PUSHJ 17,DMCHAR
	POPJ 17,
; CLR1CH -- Delete a character at the cursor
; Call:	PUSHJ 17,CLR1CH
;	<return>
; Smashes 0, 1, 2, and 3.

.U"CLR1CH:
	SKIPN DMP
	 JRST CLR1C0
	DMCMD <" >			; space then backspace
	DMCMD ↑H
CLR1C0:	MOVEI <" >			; store space at cursor
	MOVE 1,VPOS
	SETOM SLUPDP(1)
	SETOM SCUPDP
	IMULI 1,NWRDLN
	MOVE 2,HPOS
	IDIVI 2,5
	ADDI 1,SCREEN+2(2)
	DPB SCBYTP(3)
	POPJ 17,
; CSRPOS -- position cursor
; Call:	MOVE [xpos,,ypos]
;	PUSHJ 17,CSRPOS
;	<return>
; Smashes 0.

.U"CSRPOS:
	HLRZM HPOS
	HRRZM VPOS
	SETOM CRUPDP
	SKIPN DMP
	 POPJ 17,
	DMCMD ↑L
	MOVE HPOS
	XORI 140
	PUSHJ 17,DMCHAR
	MOVE VPOS
	ADDI 2
	XORI 140
	JRST DMCHAR
; TERPRI -- CR, LF, CLEOL, scroll if necessary
; Call:	PUSHJ 17,TERPRI
;	<return>
; Smashes 0, 1, 2, 3, and 4.

.U"TERPRI:
	PUSHJ 17,CARRET
	PUSHJ 17,LINEFD
	JRST CLREOL
; CARRET -- CR
; Call:	PUSHJ 17,CARRET
;	<return>
; Smashes 0.

.U"CARRET:
	MOVE VPOS			; move to [0,current y]
	JRST CSRPOS
; LINEFD -- LF, CLEOL, scroll if necessary
; Call:	PUSHJ 17,CARRET
;	<return>
; Smashes 0, 1, 2, 3, and 4.

.U"LINEFD:
	AOS 1,VPOS			; and to next line
	CAMGE 1,VSIZE
	 JRST LINFD1
	SKIPN ROLLP
	 JRST [	SETZM VPOS ? JRST LINFD1]
	MOVE [SCREEN+NWRDLN,,SCREEN]
	BLT SCREEN+SCRSIZ-NWRDLN-1
	MOVE [ASCII/     /+1]
	MOVEM BOTLIN+1
	MOVE [BOTLIN+1,,BOTLIN+2]
	BLT BOTLIN+<NCHARS/5>
	SETOM SAUPDP
	MOVE VSIZE ? SOS ? MOVEM VPOS
	SOSG SCRLCT
	 PUSHJ 17,SCNUPD		; update after a screenfull of scrolling
LINFD1:	SETOM CRUPDP
	POPJ 17,
; LINSRV -- Line starve
; Call:	PUSHJ 17,LINSRV
;	<return>
; Smashes 0.

.U"LINSRV:
	SKIPN DMP
	 JRST LINSR1
	DMCMD ↑Z
LINSR1:	SOSGE VPOS
	 SETZM VPOS
	SETOM CRUPDP
	POPJ 17,
; CSRTAB -- Tab cursor
; Call:	PUSHJ 17,CSRTAB
;	<return>
; Smashes 0.

.U"CSRTAB:
	SKIPE DMP
	 JRST CSRTA0
	DMCMD ↑I			; tab
CSRTA0:	MOVE HPOS
	TRZ 7
	ADDI 10
	MOVEM HPOS
	SETOM CRUPDP			; cursor updated
	POPJ 17,
; CSRAOS -- Forespace cursor
; Call:	PUSHJ 17,CSRAOS
;	<return>
; Smashes 0.

.U"CSRAOS:
	SKIPE DMP
	 JRST CSRAO0
	DMCMD ↑\			; forespace
CSRAO0:	AOS HPOS
	SETOM CRUPDP			; cursor updated
	POPJ 17,
; CSRSOS -- Backspace cursor
; Call:	PUSHJ 17,CSRSOS
;	<return>
; Smashes 0.

.U"CSRSOS:
	SKIPE DMP
	 JRST CSRSO0
	DMCMD ↑H			; backspace
CSRSO0:	SOSGE HPOS
	 SETZM HPOS
	SETOM CRUPDP			; cursor updated
	POPJ 17,
; INSLIN -- Insert a blank line
; Call:	MOVE <number of lines to insert>
;	PUSHJ 17,INSLIN
;	<return>
; Smashes 0, 1, 2, and 3.

.U"INSLIN:
	PUSH P,				; save argument
	SKIPN DMP
	 JRST INSLN0
	DMCMD ↑P			; enter i/d mode on DM
INSLN0:	SKIPN DMP
	 JRST INSL1A
	DMCMD ↑J			; insert a line on DM
INSL1A:	MOVE 3,VPOS
	IMULI 3,NWRDLN
	ADDI 3,SCREEN			; address of first word of cursor line
	CAIN 3,SCREEN+<NLINES-1>*NWRDLN	; skip unless at bottom line
	 JRST INSLN2
	MOVE 1,[SCREEN+<NLINES-2>*NWRDLN,,SCREEN+<NLINES-1>*NWRDLN]
INSLN1:	MOVE 2,1
	BLT 2,NWRDLN-1(1)		; move line down
	ADJSP 1,-NWRDLN
	CAIE 3,(1)
	 JRST INSLN1
INSLN2:	MOVE 1,[ASCII/     /+1]
	MOVEM 1,2(3)
	MOVEI 1,NWRDLN-3(3)
	ADDI 3,3
	HRLI 3,-1(3)
	BLT 3,(1)
	SOSLE (P)
	 JRST INSLN0
	ADJSP P,-1			; flush argument
	SETOM SAUPDP
	SKIPN DMP
	 POPJ 17,
	DMCMD ↑X			; leave i/d mode on DM
	POPJ 17,
; DELLIN -- Delete a blank line
; Call:	MOVE <number of lines to delete>
;	PUSHJ 17,DELLIN
;	<return>
; Smashes 0, 1, 2, and 3.

.U"DELLIN:
	PUSH P,				; save argument
	SKIPN DMP
	 JRST DELLN0
	DMCMD ↑P			; enter i/d mode on DM
DELLN0:	SKIPN DMP
	 JRST DELL1A
	DMCMD ↑Z			; delete a line on DM
DELL1A:	MOVE 3,VPOS
	IMULI 3,NWRDLN
	ADDI 3,SCREEN			; address of first word of cursor line
	CAIN 3,SCREEN+<NLINES-1>*NWRDLN	; skip unless at bottom line?
	 JRST DELLN1
	MOVEI 1,(3)
	ADDI 1,NWRDLN
	HRLI 3,(1)
	BLT 3,SCREEN+<NLINES-1>*NWRDLN-1; move the lines up
DELLN1:	MOVE 1,[ASCII/     /+1]
	MOVEM 1,2(3)
	MOVEI 1,NWRDLN-3(3)
	ADDI 3,3
	HRLI 3,-1(3)
	BLT 3,(1)
	SOSLE (P)
	 JRST DELLN0
	ADJSP P,-1			; flush argument
	SETOM SAUPDP
	SKIPN DMP
	 POPJ 17,
	DMCMD ↑X			; leave i/d mode on DM
	POPJ 17,
; INSCHR -- Insert a blank character
; Call:	MOVE <number of spaces to insert>
;	PUSHJ 17,INSCHR
;	<return>
; Smashes 0, 1, 2, 3, 4, 5, and 6.

.U"INSCHR:
	PUSH P,				; save count
	SKIPN DMP			; is this a DM?
	 JRST INSCH0
	DMCMD ↑P			; yes, enter i/d mode
INSCH0: SKIPN DMP			; on a DM?
	 JRST INSC1A
	DMCMD ↑\			; insert a character
INSC1A:	MOVE 1,VPOS			; get vertical position
	IMULI 1,NWRDLN			; now number of words
	MOVE 4,1			; copy it for hacking
	ADDI 4,SCREEN+NWRDLN-3		; address of last text word
	MOVE 2,HPOS			; get horizontal position
	IDIVI 2,5			; make it words
	ADDI 1,SCREEN+2(2)		; address of word with cursor
	LDB 2,[010700,,(1)]		; first character in next word
	LDB 5,[	103400,,(1)
		102500,,(1)
		101600,,(1)
		100700,,(1)
		100000,,(1)](3)
	DPB 5,[	013400,,(1)
		012500,,(1)
		011600,,(1)
		010700,,(1)
		010000,,(1)](3)
	MOVEI 5,<" >			; space in hole
	DPB 5,[	350700,,(1)
		260700,,(1)
		170700,,(1)
		100700,,(1)
		010700,,(1)](3)
	JRST INSCH1			; check for being done

; At each iteration Y has last character, X has next address

INSCH2:	MOVE 3,2			; copy the character
	LDB 2,[010700,,(1)]		; first character in next word
	DPB 3,[000700,,(1)]		; last character here
	MOVE 3,(1)			; get word being hacked
	ROT 3,-7			; put characters in right place
	IORI 3,1			; make sure bit 1.1 is on
	MOVEM 3,(1)			; save character in word
INSCH1:	CAME 1,4			; at last address?
	 AOJA 1,INSCH2
	SETOM SCUPDP			; some update somewhere
	MOVE 1,VPOS			; this line
	SOSLE (P)
	 JRST INSCH0			; loop for more characters
	ADJSP P,-1
	SETOM SLUPDP(1)			; this line was hacked
	SKIPN DMP			; on a DM?
	 POPJ 17,
	DMCMD ↑X			; leave i/d mode
	POPJ 17,
; DELCHR -- Delete a character
; Call:	MOVE <number of characters to delete>
;	PUSHJ 17,INSCHR
;	<return>
; Smashes 0, 1, 2, 3, 4, 5, and 6.

.U"DELCHR:
	PUSH P,				; save argument
	SKIPN DMP			; is this a DM?
	 JRST DELCH0
	DMCMD ↑P			; yes, enter i/d mode
DELCH0:	SKIPN DMP			; on a DM?
	 JRST DELC1A
	DMCMD ↑H			; delete a character
DELC1A:	MOVE 1,VPOS			; get current vertical position
	IMULI 1,NWRDLN			; number of words
	MOVE 4,1			; save it for later
	ADDI 4,SCREEN+NWRDLN-3		; address of last text word in line
	MOVE 2,HPOS			; get horizontal position
	IDIVI 2,5			; number of words
	ADDI 1,SCREEN+2(2)		; address of word with cursor
	LDB 5,[	013400,,(1)
		012500,,(1)
		011600,,(1)
		010700,,(1)
		010000,,(1)](3)
	DPB 5,[	103400,,(1)
		102500,,(1)
		101600,,(1)
		100700,,(1)
		100000,,(1)](3)
	JRST DELCH1			; check for being done

; Each time around the iteration A had address of next word

DELCH2:	LDB 2,[350700,,(1)]		; last character in previous
	DPB 2,[010700,,-1(1)]		; to previous
	LDB 2,[013400,,(1)]		; get last characters in this word
	DPB 2,[103400,,(1)]		; put back left justified
DELCH1:	CAME 1,4			; done?
	 AOJA 1,DELCH2
	MOVEI 2,<" >			; get a space
	DPB 2,[010700,,(1)]		; blank out last column
	SETOM SCUPDP			; screen updated someplace
	MOVE 1,VPOS			; get this line
	SOSLE (P)
	 JRST DELCH0			; hack another character
	ADJSP P,-1
	SETOM SLUPDP(1)			; flag this line hacked
	SKIPN DMP			; on a DM?
	 POPJ 17,
	DMCMD ↑X			; leave i/d mode
	POPJ 17,
; SCNUPD -- Update screen
; Call:	PUSHJ 17,SCNUPD
;	<return>
; Smashes 0, 1, 2, 3, and 4.

.U"SCNUPD:
	MOVSI 1,-NLINES
	SETZ 2,
	SKIPE SLUPDP(1)			; does this line need hacking?
	 ADDI 2,1
	AOBJN 1,.-2
	SKIPN IIIP			; III always updates everything
	 CAIL 2,4			; after four lines update entire screen
	  SETOM SAUPDP
	MOVE VSIZE
	MOVEM SCRLCT
	AOSE SAUPDP			; update entire screen?
	 JRST SCUPD1
	SETZM SCUPDP			; clear other update flags
	SETZM SLUPDP
	MOVE [SLUPDP,,SLUPDP+1]
	BLT SLUPDP+NLINES-1
	SKIPE DMP			; is this a DM?
	 JRST [	PUSHJ 17,DMOUT
		JRST CSRUPD]
	UPGIOT SDISP			; output new screen on DD and III

; Update cursor

CSRUPD:	SKIPE DMP			; is this a DM?
	 JRST [	DMCMD ↑L		; cursor ← [0,2]
		MOVE HPOS
		XORI 140
		PUSHJ 17,DMCHAR
		MOVE VPOS
		ADDI 2
		XORI 140
		PUSHJ 17,DMCHAR
		JRST DMOUT]
	SKIPN DDP			; no cursor stuff yet for III's
	 POPJ 17,
	MOVEI 2
	MOVEM SCC+2
	MOVE [SCC+2,,SCC+3]
	BLT SCC+2+NGW-1			; initialize graphics words
	SKIPGE 1,OVPOS			; got an old position?
	 JRST .+3
	  PUSHJ 17,GETCSY		; get cursor vertical position
	  DDUPG CDISP			; clear cursor
	MOVE 1,HPOS			; horizontal character position
	IMULI 1,6			; horizontal bit position
	ADDI 1,2			; graphics mode hack
	IDIVI 1,32.
	MOVNS 2
	MOVSI 3,740000
	LSH 3,(2)
	LDB 4,[010300,,3]
	ROT 4,-3
	TRZ 3,17
	IORI 3,2
	IORI 4,2
	DMOVEM 3,SCC+2(1)
	MOVE 1,VPOS			; save vertical position
	MOVEM 1,OVPOS
	PUSHJ 17,GETCSY
	DDUPG CDISP			; draw new cursor
	POPJ 17,

SCUPD1:	AOSE SCUPDP			; did any update happen?
	 JRST SCUPD2
	MOVSI 1,-NLINES
SCUP1A:	SKIPE SLUPDP(1)			; need to hack this line?
	 PUSHJ 17,UPDLIN
	AOBJN 1,SCUP1A
	JRST CSRUPD

SCUPD2:	AOSE CRUPDP			; was cursor hacked?
	 POPJ 17,
	JRST CSRUPD
; GETCSY UDPLIN redisplay subroutines

; Set up display program vertical position

GETCSY:	IMULI 1,12.
	ADDI 1,24.+10.
	DPB 1,[140400,,SCC+1]
	LSH 1,-4
	DPB 1,[240500,,SCC+1]
	POPJ 17,

; Display a single line

UPDLIN:	SKIPE DMP
	 POPJ 17,
	SKIPE LDISP+2
	 UPGIOT [0 ? 0 ? 0 ? 0]		; wait for previous
	SETZM SLUPDP(1)
	HRRZ 2,1			; line number
	IMULI 2,NWRDLN			; word position
	MOVSI 2,SCREEN+2(2)		; address of start of line
	HRRI 2,LNP+2
	BLT 2,LNP+NWRDLN-2
	HRRZ 2,1			; get line number again
	IMULI 2,12.
	ADDI 2,24.			; starting raster number
	DPB 2,[140400,,LNP]		; zap in low 4 bits of address
	LSH 2,-4			; throw low bits away
	DPB 2,[240500,,LNP]		; high 5 bits of address
	UPGIOT LDISP			; display the line
	POPJ 17,
; DMCHAR DMOUT Datamedia output routines

; Character output to DM

DMCHAR:	SOSG DMCNT			; any room in buffer?
	 PUSHJ 17,DMOUT
	IDPB DMPNT			; save character
	POPJ 17,

; Buffer output to DM; called when DM buffer full or want to force buffer out

DMOUT:	SKIPN DMPGM			; any program there?
	 POPJ 17,
	HRRZ DMPNT			; get current value of pointer
	SUBI DMPGM-1			; compute number of words used
	MOVEM DMDISP+1			; set number of words to do
	UPGIOT DMDISP			; output DM program
	MOVS HPOS
	HRR VPOS
	ADDI 2-1			; two lines for who line
	CURSOR				; bop the cursor to last position
	SETZM DMPGM
	MOVE [DMPGM,,DMPGM+1]
	BLT DMPGM+DMBUFL-1		; clear the old program
	MOVEI <5*DMBUFL>-4
	MOVEM DMCNT			; initialize DM counter
	MOVE [440700,,DMPGM]
	MOVEM DMPNT			; initialize DM pointer
	MOVEI 177			; quote
	IDPB DMPNT
	MOVEI ↑L			; cursor position
	IDPB DMPNT
	MOVE HPOS			; horizontal position
	XORI 140
	IDPB DMPNT
	MOVE VPOS			; vertical position
	ADDI 2				; who line space
	XORI 140
	IDPB DMPNT
	POPJ 17,

.END DISPLY