;Chris Ward's 6502 System
;System/test software
;1/6/99

DTA	EQU $80		;page zero variable space
ACIA	EQU $D000		;6551 ACIA
VIA1	EQU $D100		;6522 VIA #1
VIA2	EQU $D200		;6522 VIA #2
LCD	EQU $D300		;LCD interface

; ACIA
	ORG ACIA
ATXD	.ds 1		;transmit data register (write)
ARXD	EQU ATXD		;receive data register (read)
ARES	.ds 1		;programmed reset (write)
ASTS	EQU ARES		;status register (read)
ACMD	.ds 1		;command register (r/w)
ACTL	.ds 1		;control register (r/w)

; VIA #1
	ORG VIA1
PORT1B	.ds 1
PORT1A	.ds 1
DDR1B	.ds 1
DDR1A	.ds 1

; VIA #2
	ORG VIA2
PORT2B	.ds 1
PORT2A	.ds 1
DDR2B	.ds 1
DDR2A	.ds 1

; LCD
	ORG LCD
LCD0	.ds 1
LCD1	.ds 1

; connected hardware
LEDPORT	EQU PORT1B	;LEDs on VIA #1 port B
LEDDDR	EQU DDR1B
KEYPORT	EQU PORT1A	;keypad on VIA #1 port A
KEYDDR	EQU DDR1A

; variables
	ORG $DTA
KEYCOL	.ds 1	;\
KEYCOLN	.ds 1	; }temp variables used by keypad scanning routine
KEYROW	.ds 1	;/
KEYNEW	.ds 1	;flag indicates key has been pressed
KEYVAL	.ds 1	;value of last key pressed
MSGBASE	.ds 2	;address of message to print

; ***** CODE *****
	ORG $E000

; *** IRQ service routine
IRQSRV	PHA		;save A, X, Y to stack
	TXA
	PHA
	TYA
	PHA
	LDA ASTS		;interrupt from ACIA?
	BMI ASRV
IRQSR1	PLA		;restore A, X, Y
	TAY
	PLA
	TAX
	PLA
	RTI

; *** ACIA interrupt service routine
ASRV	AND #$08		;byte received?
	BEQ IRQSR1
	LDA ARXD		;get byte
	STA LEDPORT	;for now, just display byte on LEDs
	JMP IRQSR1

; *** Entry point on reset
RESET	NOP
	NOP
	NOP
	NOP
	JSR INIT
RESET1	JSR KEYSCAN
	LDA KEYNEW
	BEQ RESET1	;wait for keypress
	LDA KEYVAL	;display value on LEDs
	STA LEDPORT
	CLC
	ADC #$30		;convert to ASCII
	CMP #$3A
	BMI RESET2
	CLC
	ADC #$07
RESET2	JSR LCDPRINT	;print value on the LCD
	JSR TXBYTE	;transmit value on RS232 port
	DEC KEYNEW
	JMP RESET1

; *** Initialise devices and variables
INIT	CLD
	LDA #$00		;clear key press flag
	STA KEYNEW
	LDA #$0F		;set DDR for keypad (iiiioooo)
	STA KEYDDR
	STA KEYPORT	;set keypad columns high
	LDA #$FF		;set DDR for LEDs (oooooooo)
	STA LEDDDR
	LDA #$00		;turn LEDs off
	STA LEDPORT
	JSR AINIT		;setup ACIA
	JSR LINIT		;setup LCD display
	JSR MEMTEST	;perform memory test
	RTS

; *** ACIA initialisation
AINIT	STA ARES		;reset
	LDA #$09		;set options
	STA ACMD
	LDA #$1A		;8-N-1, 2400 baud
	STA ACTL
	RTS

; *** LCD initialisation
LINIT	LDX #$04		;do function set 4 times
LINIT0	LDA #$38		;function set: 8 bit, 2 lines, 5x7
	STA LCD0
	JSR LCDBUSY	;wait for busy flag to clear
	DEX
	BNE LINIT0
	LDA #$06		;entry mode set: increment, no shift
	STA LCD0
	JSR LCDBUSY
	LDA #$0E		;display on, cursor on, blink off
	STA LCD0
	JSR LCDBUSY
	LDA #$01		;clear display
	STA LCD0
	JSR LCDBUSY
	LDA #$80		;DDRAM address set: $00
	STA LCD0
	JSR LCDBUSY
	RTS
LINITMSG	fcs "LCD init done. "
	.byte $00

; *** Memory test
MEMMSG1	fcs "Memory test... "
	.byte $00
MEMMSG2	fcs "OK"
	.byte $00
MEMMSG3	fcs "fail at $"
	.byte $00
MEMTEST	LDX #$01		;X=high byte
	LDY #$FF		;Y=low byte
	LDA #MEMMSG1	;print message
	STA MSGBASE
	LDA #MEMMSG1/256
	STA MSGBASE+1
	JSR LCDSTRING
MEMLOOP	INY
	BNE MEMTST
	INX
	CPX #$80		;reached $8000? no more RAM...
	BNE MEMTST
	LDA #$00		;turn all LEDs off
	STA LEDPORT
	LDA #MEMMSG2	;print 'OK'
	STA MSGBASE
	LDA #MEMMSG2/256
	STA MSGBASE+1
	JSR LCDSTRING
	RTS
MEMTST	STX $0001
	STX LEDPORT	;display high byte on LEDs
	LDA #$FF
	STA ($0000),Y
	CMP ($0000),Y
	BNE MEMFAIL
	LDA #$00
	STA ($0000),Y
	CMP ($0000),Y
	BNE MEMFAIL
	JMP MEMLOOP
MEMFAIL	STY $0000		;store high (x) and low (y) bytes of fail address
	STX $0001
	LDA #MEMMSG3	;print 'fail at $'
	STA MSGBASE
	LDA #MEMMSG3/256
	STA MSGBASE+1
	JSR LCDSTRING
	LDA $0001		;print high byte
	JSR LCDHEX
	LDA $0000		;print low byte
	JSR LCDHEX
MEMFAI1	LDA $0001		;display high byte on leds
	STA LEDPORT
	JSR DLY00
	LDA $0000		;display low byte on leds
	STA LEDPORT
	JSR DLY00
	JSR KEYSCAN	;check for keypress
	LDA KEYNEW
	BEQ MEMFAI1
	RTS

; *** Wait for LCD busy bit to clear
LCDBUSY	PHA
LCDBUSY0	LDA LCD0		;read from LCD register 0
	AND #$80		;check bit 7 (busy)
	BNE LCDBUSY0
	PLA
	RTS

; *** Print character on LCD
LCDPRINT	STA LCD1
	JSR LCDBUSY
	LDA LCD0		;get current DDRAM address
	AND #$7F
	CMP #$14		;wrap from pos $13 (line 1 char 20)...
	BNE LCDPRINT0
	LDA #$C0		;...to $40 (line 2 char 1)
	STA LCD0
	JSR LCDBUSY
LCDPRINT0	RTS

; *** Print 2 digit hex number on LCD
LCDHEX	PHA
	LSR
	LSR
	LSR
	LSR
	ORA #$30
	JSR LCDPRINT
	PLA
	PHA
	AND #$0F
	JSR LCDPRINT
	PLA
	RTS
	
; *** Print string on LCD
LCDSTRING	PHA		;save A, Y to stack
	TYA
	PHA
	LDY #$00
LCDSTR0	LDA (MSGBASE),Y
	BEQ LCDSTR1
	JSR LCDPRINT
	INY
	BNE LCDSTR0
LCDSTR1	PLA		;restore A, Y
	TAY
	PLA
	RTS

; *** Send byte on RS232 interface
TXBYTE	PHA
	LDA #$50		;retry counter
	TAY
TXBYT1	LDA ASTS
	AND #$10		;trasmit register empty?
	BNE TXOUT		;yes, output byte
	DEY
	BNE TXBYT1	;try again
	BEQ TXFAIL
TXOUT	PLA
	STA ATXD
	LDA #$00		;return 0 for success
	RTS
TXFAIL	PLA		;transmit failed, return byte
	RTS

; *** Keypad matrix scanning routine
KEYSCAN	LDA KEYNEW
	BEQ KEYSCAN1	;only continue if key press flag is cleared
	RTS
KEYSCAN1	LDA #$EF		;init variables
	STA KEYCOL
	LDA #$04
	STA KEYCOLN
NEXTCOL	DEC KEYCOLN	;next column
	LDA KEYCOLN
	CMP #$FF
	BNE TESTCOL
	RTS
TESTCOL	LSR KEYCOL	;rotate column-test bit pattern
	LDA KEYCOL
	STA KEYPORT
	LDA KEYPORT
	AND #$F0		;select top 4 bits from port (rows)
	EOR #$F0		;invert top 4 bits
	BEQ NEXTCOL	;no keys pressed in this column
	STA KEYROW
	AND #$10		;test row 0
	BEQ KEYROW1
	LDA #$00
	JMP KEYCODE
KEYROW1	LDA KEYROW
	AND #$20		;test row 1
	BEQ KEYROW2
	LDA #$04
	JMP KEYCODE
KEYROW2	LDA KEYROW
	AND #$40		;test row 2
	BEQ KEYROW3
	LDA #$08
	JMP KEYCODE
KEYROW3	LDA #$0C		;must be row 3
KEYCODE	CLC
	ADC KEYCOLN
	TAX
	LDA KEYTAB,X
	STA KEYVAL
	INC KEYNEW
DEBOUNCE	LDA KEYPORT
	AND #$F0
	EOR #$F0
	BNE DEBOUNCE
	RTS

KEYTAB	.byte $0C, $0B, $00, $0A	;scancode to key value table
	.byte $0D, $09, $08, $07
	.byte $0E, $06, $05, $04
	.byte $0F, $03, $02, $01

; *** Delay routines
DLY00	LDX #$80
	LDY #$00
DLY00A	DEY
	BNE DLY00A
	DEX
	BNE DLY00A
	RTS

DLY01	LDY #$00
DLY01A	DEY
	BNE DLY01A
	RTS

; *** Reset, NMI & IRQ vectors
	ORG $FFFA
	.word IRQSRV	;FIXME: should be NMI service routine
	.word RESET
	.word IRQSRV
	.end

