********************************************************************
* VTIO - NitrOS-9 Video Terminal I/O driver for Atari XE/XL
*
* $Id$
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*  1       2012/02/20  Boisy G. Pitre
* Started from VTIO for the Atari XE/XL
                         
         nam   VTIO      
         ttl   NitrOS-9 Video Terminal I/O driver for Atari XE/XL
                         
         ifp1            
         use   defsfile  
         use   scfdefs   
         use   atari.d
         endc            
                         
tylg     set   Drivr+Objct
atrv     set   ReEnt+rev 
rev      set   $00
edition  set   1
                         
         mod   eom,name,tylg,atrv,start,size
                         
size     equ   V.Last    
                         
         fcb   UPDAT.+EXEC.
                         
name     fcs   /VTIO/    
         fcb   edition   
                         
start    lbra  Init      
         lbra  Read      
         lbra  Write     
         lbra  GetStat   
         lbra  SetStat   
         lbra  Term      
                         

* The display list sets up the ANTIC chip to display the main screen. 
* It is copied to the Atari Screen Area in low memory (see atari.d)
* The size of this code MUST be <= G.DListSize
DList
		fcb	$70,$70,$70	3 * 8 blank scanlines
		fcb	$42			Mode 2 with LMS (Load Memory Scan).  Mode 2 = 40 column hires text, next 2 bytes L/H determine screen origin
		fdbs	G.ScrStart+(G.Cols*0)		origin
		fcb	2,2,2,2,2,2,2,2,2,2
		fcb	2,2,2,2,2,2,2,2,2,2
		fcb	2,2,2
* 23 extra mode 2 lines for total of 24.  240 scanlines can be used for display area, but a hires line cannot be on scanline 240 due to an Antic bug
		fcb	$41			this is the end of Display List command JVB (Jump and wait for Vertical Blank)
         	fdb  $0000
DListSz	equ	*-DList

* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Init      
		stu		>D.KbdSta  store devmem ptr
		pshs	u

* setup static vars
		clra
		clrb
		std	V.CurRow,u

* Clear screen memory
          ldy  #G.DList
          pshs y
          ldy  #G.ScrStart
          ldd  #$0000
clearLoop@
     	std	,y++
     	cmpy	,s
     	bne	clearLoop@
     	puls	u				G.DList address is aleady in U
     	
* copy the display list into our memory area to the globa location in low RAM
		leax	DList,pcr
		ldy	#DListSz
dlcopy@
		ldd	,x++
		std	,u++
		leay	-2,y
		bne	dlcopy@
* patch last word to be address of start of DList (byte swap for ANTIC)
		leau	-DListSz,u
		tfr	u,d
		exg	a,b
		std	DListSz-2,u
		
* tell the ANTIC where the dlist is
		std	DLISTL

* tell the ANTIC where the character set is (page aligned, currently in Krn)		
		lda	#G.CharSetAddr>>8
		sta	CHBASE
		
* set background color
		lda	#$00
 		sta	COLBK

* set text color
		lda	#$0F
 		sta	COLPF0
 		sta	COLPF1
 		sta	COLPF3
		lda	#$94
 		sta	COLPF2
 		
* tell ANTIC to start DMA
		lda	#$22
 		sta	DMACTL

* tell ANTIC to enable character set 2
		lda	#$02
 		sta	CHACTL

* install keyboard ISR
		ldd	#IRQST				POKEY IRQ status address
		leay	IRQSvc,pcr			pointer to our service routine
		leax	IRQPkt,pcr			F$IRQ requires a 3 byte packet
		ldu	,s					use our saved devmem as ISR static
		os9	F$IRQ				install the ISR
		bcs	initex
		
* tell POKEY to enable keyboard scanning
		lda	#%11000000
		sta	IRQEN

* clear carry and return
		clrb
initex	puls	u,pc

  
* Term
*
* Entry:
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Term		
* clear carry and return
		clrb
		rts

                         
* Read
*
* Entry:
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    A  = character read
*    CC = carry set on error
*    B  = error code
*
Read          
         leax  V.InBuf,u  point X to input buffer
         ldb   V.IBufT,u  get tail pointer
         orcc  #IRQMask   mask IRQ
         cmpb  V.IBufH,u  same as head pointer
         beq   Put2Bed    if so, buffer is empty, branch to sleep
         abx              X now points to curr char
         lda   ,x         get char
         bsr   cktail     check for tail wrap
         stb   V.IBufT,u  store updated tail
         andcc  #^(IRQMask+Carry) unmask IRQ
         rts             
                         
Put2Bed  lda   V.BUSY,u   get calling process ID
         sta   V.WAKE,u   store in V.WAKE
         andcc  #^IRQMask  clear interrupts
         ldx   #$0000    
         os9   F$Sleep    sleep forever
         clr   V.WAKE,u   clear wake
         ldx   <D.Proc    get pointer to current proc desc
         ldb   <P$Signal,x get signal recvd
         beq   Read       branch if no signal
         cmpb  #S$Window  window signal?
         bcc   Read       branch if so
         coma            
         rts             
* Check if we need to wrap around tail pointer to zero
cktail   incb             increment pointer
         cmpb  #KBufSz-1  at end?
         bls   readex     branch if not
* clear carry and return
         clrb             else clear pointer (wrap to head)
readex   rts                                      


* Write
*
* Entry:
*    A  = character to write
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Write
		bsr		hidecursor		

		cmpa		#C$SPAC			space or greater?
		bcs		ChDispatch		branch if not
		
wchar	suba		#$20
		pshs		a
		lda		V.CurRow,u
		ldb		#G.Cols
		mul
		addb		V.CurCol,u
		adca		#0
		ldx		#G.ScrStart
		leax		d,x
		puls		a
		sta		,x
		ldd		V.CurRow,u
		incb
		cmpb		#G.Cols
		blt		ok
		clrb
incrow
		inca
		cmpa		#G.Rows
		blt		clrline
SCROLL	EQU		1
		IFNE		SCROLL
		deca						set A to G.Rows - 1
		pshs		d				save off Row/Col
		ldx		#G.ScrStart		get start of screen memory
		ldy		#G.Cols*(G.Rows-1)	set Y to size of screen minus last row
scroll_loop
		ldd		G.Cols,x			get two bytes on next row
		std		,x++				store on this row
		leay		-2,y				decrement Y
		bne		scroll_loop		branch if not 0
		puls		d				recover Row/Col
		ELSE
		clra
		ENDC
* clear line
clrline	std		V.CurRow,u
		bsr		DelLine
		bra		drawcursor
ok		std		V.CurRow,u
		bra		drawcursor
		
* calculates the cursor location in screen memory
* Exit: X = address of cursor
*       All other regs preserved
calcloc
		pshs		d
		lda		V.CurRow,u
		ldb		#G.Cols
		mul
		addb		V.CurCol,u
		adca		#0
		ldx		#G.ScrStart
		leax		d,x
		puls		d,pc

drawcursor
		bsr		calcloc
		lda		,x
		sta		V.CurChr,u
		lda		#$80
		sta		,x
		rts

hidecursor
		pshs		a
		bsr		calcloc
		lda		V.CurChr,u
		sta		,x
		puls		a,pc

ChDispatch
		cmpa  #$0D		$0D?
		bhi   drawcursor	branch if higher than
		leax  <DCodeTbl,pcr	deal with screen codes
		lsla  			adjust for table entry size
		ldd   a,x		get address in D
		jmp   d,x		and jump to routine

* display functions dispatch table
DCodeTbl	fdb   NoOp-DCodeTbl			$00:no-op (null)
		fdb   CurHome-DCodeTbl		$01:HOME cursor
		fdb   CurXY-DCodeTbl		$02:CURSOR XY
		fdb   DelLine-DCodeTbl		$03:ERASE LINE
		fdb   ErEOLine-DCodeTbl		$04:CLEAR TO EOL
		fdb   Do05-DCodeTbl			$05:CURSOR ON/OFF
		fdb   CurRght-DCodeTbl		$005e  $06:CURSOR RIGHT
		fdb   NoOp-DCodeTbl			$07:no-op (bel:handled in VTIO)
		fdb   CurLeft-DCodeTbl		$08:CURSOR LEFT
		fdb   CurUp-DCodeTbl		$09:CURSOR UP
		fdb   CurDown-DCodeTbl		$0A:CURSOR DOWN
		fdb   ErEOScrn-DCodeTbl		$0B:ERASE TO EOS
		fdb   ClrScrn-DCodeTbl		$0C:CLEAR SCREEN
		fdb   Retrn-DCodeTbl		$0D:RETURN
         
DelLine
		lda		V.CurRow,u
		ldb		#G.Cols
		mul
		ldx		#G.ScrStart
		leax		d,x
		lda		#G.Cols
clrloop@	clr		,x+
		deca
		bne		clrloop@
		rts
		
ClrScrn
ErEOScrn
CurUp
NoOp
CurHome
CurXY
ErEOLine
Do05
CurRght
		bra		drawcursor

CurLeft
		ldd		V.CurRow,u
		beq		leave
		decb
		bpl		erasechar
		ldb		#G.Cols-1
		deca
		bpl		erasechar
		clra
erasechar
		std		V.CurRow,u
		ldb		#G.Cols
		mul
		addb		V.CurCol,u
		adca		#0
		ldx		#G.ScrStart
		leax		d,x
		clr		1,x
		
leave	ldd		V.CurRow,u
		bra		drawcursor

CurDown
		ldd		V.CurRow,u
		lbra		incrow

Retrn
		lda		V.CurRow,u
		ldb		#G.Cols
		mul
		addb		V.CurCol,u
		adca		#0
		ldx		#G.ScrStart
		leax		d,x
		lda		#C$SPAC-$20
		sta		,x
		clr		V.CurCol,u
		lbra		drawcursor


* GetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
GetStat
		cmpa		#SS.ScSiz
		bne		gserr
		ldx		PD.RGS,y
		ldd		#G.Cols
		std		R$X,x
		ldd		#G.Rows
		std		R$Y,x
* clear carry and return
		clrb
		rts
gserr
		comb
		ldb		#E$UnkSvc            
		rts             
             
             
* SetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
SetStat
* clear carry and return
		clrb
		rts             

	
IRQPkt	equ	*
Pkt.Flip	fcb	%11000000		flip byte
Pkt.Mask 	fcb	%11000000		mask byte
		fcb 	$0A		priority

	
*
* IRQ routine for keyboard
*
IRQSvc
		ldb	KBCODE	get keyboard code from POKEY
		pshs b
		andb	#$7F		mask out potential CTRL key
		leax	ATASCI,pcr
		lda	b,x		fetch character for code
		tst	,s+		CTRL key down?
		bpl	noctrl@	branch if not
		cmpa	#$40
		bcs	noctrl@
		anda	#$5F
		suba	#$40
noctrl@
		ldb	V.IBufH,u  get head pointer in B
		leax	V.InBuf,u  point X to input buffer
		abx              X now holds address of head
		lbsr	cktail      check for tail wrap
		cmpb	V.IBufT,u  B at tail?
		beq	L012F      branch if so
		stb	V.IBufH,u 
L012F	sta	,x         store our char at ,X
		beq	WakeIt     if nul, do wake-up
		cmpa	V.PCHR,u   pause character?
		bne	L013F      branch if not
		ldx	V.DEV2,u   else get dev2 statics
		beq	WakeIt     branch if none
		sta	V.PAUS,x   else set pause request
		bra	WakeIt    
L013F	ldb	#S$Intrpt  get interrupt signal
		cmpa	V.INTR,u   our char same as intr?
		beq	L014B      branch if same
		ldb	#S$Abort   get abort signal
		cmpa	V.QUIT,u   our char same as QUIT?
		bne	WakeIt     branch if not
L014B	lda	V.LPRC,u   get ID of last process to get this device
		bra	L0153      go for it
WakeIt	ldb	#S$Wake    get wake signal
		lda	V.WAKE,u   get process to wake
L0153	beq	L0158      branch if none
		os9	F$Send     else send wakeup signal
L0158	clr	V.WAKE,u   clear process to wake flag

* Update the shadow register then the real register to disable and
* re-enable the keyboard interrupt
		lda	D.IRQENShdw
		tfr	a,b
		anda	#^%11000000
		orb	#%11000000
		sta	IRQEN
		stb	D.IRQENShdw
		stb	IRQEN
		rts
		
ATASCI	fcb	$6C,$6A,$3B,$80,$80,$6B,$2B,$2A ;LOWER CASE
		fcb	$6F,$80,$70,$75,$0D,$69,$2D,$3D

		fcb	$76,$80,$63,$80,$80,$62,$78,$7A
		fcb	$34,$80,$33,$36,$1B,$35,$32,$31

		fcb	$2C,$20,$2E,$6E,$80,$6D,$2F,$81
		fcb	$72,$80,$65,$79,$7F,$74,$77,$71

		fcb	$39,$80,$30,$37,$08,$38,$3C,$3E
		fcb	$66,$68,$64,$80,$82,$67,$73,$61


		fcb	$4C,$4A,$3A,$80,$80,$4B,$5C,$5E ;UPPER CASE
		fcb	$4F,$80,$50,$55,$9B,$49,$5F,$7C

		fcb	$56,$80,$43,$80,$80,$42,$58,$5A
		fcb	$24,$80,$23,$26,$1B,$25,$22,$21

		fcb	$5B,$20,$5D,$4E,$80,$4D,$3F,$81
		fcb	$52,$80,$45,$59,$9F,$54,$57,$51

		fcb	$28,$80,$29,$27,$9C,$40,$7D,$9D
		fcb	$46,$48,$44,$80,$83,$47,$53,$41


*		fcb	$0C,$0A,$7B,$80,$80,$0B,$1E,$1F ;CONTROL
*		fcb	$0F,$80,$10,$15,$9B,$09,$1C,$1D

*		fcb	$16,$80,$03,$80,$80,$02,$18,$1A
*		fcb	$80,$80,$85,$80,$1B,$80,$FD,$80

*		fcb	$00,$20,$60,$0E,$80,$0D,$80,$81
*		fcb	$12,$80,$05,$19,$9E,$14,$17,$11

*		fcb	$80,$80,$80,$80,$FE,$80,$7D,$FF
*		fcb	$06,$08,$04,$80,$84,$07,$13,$01
        
		emod            
eom		equ	*
		end             
