; irg.m65: CC65 functions for the infrared gateway (IRG) Version 0.1
; ------------------------------------------------------------------
; Copyright 1994 David Deaven version 0.1
; Permission to use, copy, and distribute this code is granted
; provided that it is not used for commercial applications.
; David Deaven -- deaven@iastate.edu
;
; This ra65 library package provides access to an infrared gateway,
; or IRG, connected to an Atari 400/800/XL/XE 8-bit computer.  A
; description of how to build the IRG circuit, which simply plugs into
; a joystick port, can be found in the file README.HARDWARE included 
; with this distribution.  The functions defined here are:
;
; void IRGinit(int sample, int ton, int toff, int repeat,
;   int tonmin, int toffmax, int qtime, int totime)
; int IRGread(char *buffer, int size) = # bytes returned
; int IRGwrite(char *buffer) = 0, or 1 if IRG is active
;
; See the file README for more information, and the C program
; irgdemo.c which demonstrates the use of these functions.
; ------------------------------------------------------------------
rdblen	=	128 ; internal read buffer length (<256)

; Some device things that the standard CC65 headers don't have
PACTL	=	$D302
PORTA	=	$D300
TRIG0	=	$D010
GRACTL	=	$D01D

; ------------------------------------------------------------------
; Start the vertical blank service routine and set parameters.
;
; void IRGinit(int sample, int ton, int toff, int repeat,
;   int tonmin, int toffmax, int qtime, int totime)
;
_irginit:
	jsr	enterfun	; get all arguments
	ldy	#16
	jsr	ldaxysp
	sta	sample
	ldy	#14
	jsr	ldaxysp
	sta	ton
	ldy	#12
	jsr	ldaxysp
	sta	toff
	ldy	#10
	jsr	ldaxysp
	sta	repeat
	ldy	#8
	jsr	ldaxysp
	sta	tonmin
	ldy	#6
	jsr	ldaxysp
	sta	toffmax
	ldy	#4
	jsr	ldaxysp
	sta	qtime
	ldy	#2
	jsr	ldaxysp
	sta	totime

	LDA #%00111001	; set up PIA
	STA PACTL	; output bit A0
	LDA #%00000001
	STA PORTA
	LDA #%00111101
	STA PACTL
	LDA #0		; turn IRG output off
	STA PORTA

	LDA #0
	STA nread	; clear read buffer
	sta nwrite
	sta rdbuf
	sta irgstat	; no invalid events

	LDA #6		; new immediate VBLANK
	LDX #VImm^
	LDY #VImm\
	JSR SETVBV

	LDA #7		; new deferred VBLANK
        LDX #VBlank^
        LDY #VBlank\
        JSR SETVBV

	jmp exitfun

; ------------------------------------------------------------------
; Read the IRG buffer safely into a user buffer.
;
; int IRGread(char *buffer, int size) = # bytes returned
;
_irgread:
	jsr	enterfun
	ldy	#2		; get arguments
	jsr	ldaxysp
	sta	blen
	ldy	#4
	jsr	ldaxysp
	jsr	psave
	sta	ptr1		; set to point at buffer
	stx	ptr1+1

	LDX nread
	ldy #0		; # bytes transferred
fine:	cpx nwrite	; end of buffer?
	beq rddone
	LDA rdbuf,x	; transfer one byte
	STA (ptr1),Y
	iny
	INX
	CPX #rdblen
	BNE skip1
	ldx #0
skip1:  cmp #0		; end of buffer?
	bne skip2
	dey 		; zero byte is meaningless, remove
	jmp rddone
skip2:	cpy blen 	; buffer overflow?
	bmi fine

rddone:	stx nread	; update next read position
	tya		; return # bytes
	ldy irgstat	; bad buffer?
	beq rddo2	
	lda #0
rddo2:	ldx #0
	jsr prest
	jmp exitfun

; ------------------------------------------------------------------
; Send a buffer out (null-terminated string).
;
; int IRGwrite(char *buffer) = 0, or 1 if IRG is active
;
_irgwrite:
	jsr enterfun
	ldy #2
	jsr ldaxysp
	jsr psave
	sta ptr1
	stx ptr1+1

	LDA CRITIC	; is anything else
	BEQ	ok2	; going on?
	ldax	#1	; if so, don't send
	jsr	prest
	jmp	exitfun

ok2:	JSR IStop	; stop DMA etc.
	lda repeat
	sta nwr
wr0:	LDY #$FF
	LDX #8
loop:	INY
	LDA (ptr1),Y
	BEQ eot
	STA bytbuf
sloop:	LDA #0
	ROL bytbuf
	ROL A
	STA PORTA
	TXA
	LDX ton
loop1:  JSR DELAY
	DEX
	BNE loop1
	STX PORTA
	LDX toff
loop0:  JSR DELAY
	DEX
	BNE loop0
	TAX
	DEX
	BNE sloop
	LDX #8
	BNE loop

eot:	ldx qtime
eot8:	jsr delay
	dex
	bne eot8
	dec nwr
	bne wr0
	JSR IStart	; restart DMA etc
	ldax #0
	jsr prest
	jmp exitfun
nwr:	.byte 0

; ------------------------------------------------------------------
; Immediate vertical blank code.  Unlike the Atari OS code, if CRITIC is
; set, _nothing_ gets done.  This state should not be maintained too
; long, but for the length of an IR packet it's OK.
;
VImm:	LDA CRITIC
	BEQ cont
	JMP XITVBV	; outta here
cont:	JMP $E45F	; OS Stage 1

; ------------------------------------------------------------------
; Deferred vertical blank code.  Examine TRIG0 for new IR input,
; catch all input by keeping TRIG0 in hardware latch mode.  When TRIG0 has
; been low, display DMA is turned off, CRITIC is enabled, and this routine
; becomes the "mainline" code with the interrupt return info still on the
; stack. An attempt is made to read a valid code into the buffer, then
; control is returned to the interrupted code.
;
VBlank: LDA CRITIC
	BNE leave	; never happens?
	LDA TRIG0
	BEQ rec		; IRG has been active
	LDA #%100
	STA GRACTL	; set TRIG0 latch
leave:  JMP XITVBV 	; outta here

rec:    JSR IStop
	LDA #0
	STA GRACTL	; clear TRIG0 latch

still:  LDX qtime	; detect record gap
	LDA #1		; IR input bit is LSB
wait:   JSR Delay
	BIT TRIG0
	BEQ still	; (input not quiet)
	DEX
	BNE wait	; we'll wait as long as it takes!

	LDY totime	; looking for a "1" bit to start 
retry:  LDX qtime
mark:   BIT TRIG0
	BEQ st0		; got "1", go for it
	JSR Delay
	DEX
	BNE mark
	DEY
	BNE retry
	JMP exit	; time-out, no harm done

st0:    LDY #8		; bit counter
start:  LDX #0
wton:   JSR Delay
	INX
	BIT TRIG0
	BEQ wton

	CPX tonmin	; bit long enough?
	BPL okbit
	LDA #1		; bad "1" bit
	STA irgstat
	JMP exit

okbit:  SEC		; store a "1" bit
	JSR store
	LDX #0		; wait for "0" to end
wtoff:  JSR Delay
	INX
	BEQ eott	; never? forced EOT (very long "0")
	BIT TRIG0
	BNE wtoff
	CPX qtime	; the space is EOT?
	BPL eott
	CPX toffmax	; a "0" bit?
	BMI start	; normal space
	CLC		; store a "0" bit
	JSR store
	JMP start

eott:   CPY #8		; normal EOT
	BEQ done	; last data byte has been written
	CLC
	JSR store	; pad buffer w/zeroes
	JMP eott

done:   ldy #8 		; terminate buffer with zero
pad:	clc
	jsr store
	cpy #8
	bne pad
	LDA #0
	STA irgstat	; normal operation
	JMP exit

; ------------------------------------------------------------------
; Store bits MSB to LSB in memory at rdbuf
;
; C = bit (0/1)
; X = not saved
; Y = #bits in bytbuf (0-7), 8==0
; A = saved
;
store:  ROL bytbuf	; store byte pre-buffer
	DEY
	BEQ byte
	RTS
byte:   TAX		; save A
	LDA bytbuf
	LDY nwrite
	STA rdbuf,Y
	INY
	cpy #rdblen
	bne byte1
	ldy #0
byte1:	cpy nread	; buffer full?
	beq over
	sty nwrite
	LDY #8		; new byte
	TXA
	RTS

over:	dey		; back off one
	cpy #$ff
	bne over1
	ldy #rdblen
	dey
over1:	lda #0
	sta rdbuf,y	; insert artificial terminator
	sty nwrite
        PLA		; pop "jsr store" return address
	PLA
	LDA #2		; buffer overrun...
	STA irgstat	; ...fall through to exit

exit:   JSR IStart	; restart DMA etc.
	JMP XITVBV	; outta here

; ------------------------------------------------------------------
; Wait one delay time, saving A, X, Y
; time = (20+5*sample) clock pds
; 
Delay:  PHA		; 3 cycles
	TXA		; 2
	LDX sample	; 3
dloop:	DEX		; 2
	BNE dloop	; 3
	TAX		; 2
	PLA		; 4
	RTS		; 6

; ------------------------------------------------------------------
IStop:  LDA #0
	STA DMACTL	; prevent DMA
	LDA #1
	STA CRITIC
	RTS
IStart: LDA SDMCTL
	STA DMACTL	; enable DMA
	LDA #0
	STA CRITIC
	RTS

; ------------------------------------------------------------------
psave:	ldy ptr1	; save system pointer
	sty svp1
	ldy ptr1+1
	sty svp1+1
	rts
prest:	ldy svp1	; restore system pointer
	sty ptr1
	ldy svp1+1
	sty ptr1+1
	rts
svp1:	.word	0

; ------------------------------------------------------------------
nread:		.byte 0
nwrite:		.byte 0
sample:		.byte 2
ton:		.byte 20
toff:		.byte 20
tonmin:		.byte 10
toffmax:	.byte 30
qtime:		.byte 100
totime:		.byte 25
repeat:		.byte 3
bytbuf:		.byte 0
blen:		.byte 0
rdbuf:		.blkb rdblen
irgstat:	.word 0

	.globl _irginit
	.globl _irgread
	.globl _irgwrite

