[Return to top] BBC micro compatible - Keypad interface assembly listing. By Lee Davison.
Back



; assembly code for CUBE keyboard interface

; notes for R6501AQ ....

; internal RAM is from $0040 to $00FF
; the contents of the 32 bytes from $0040 to $005F may be maintained
; by battery (not used)
; the stack pointer is 8 bits and ranges from $00FF down to $0040

LAB_00	= $00				; port A
					; bit	function
					; ---	--------
					; 7	serial Rx
					; 6	serial Tx
					; 5	enable serial Tx buffer
					; 0	buzzer driver
					; other bits unused

LAB_01	= $01				; port B
					; bit	function
					; ---	--------
					; 7	watchdog timer reset
					; other bits unused

LAB_02	= $02				; port C

LAB_12	= $12				; interrupt enable register
LAB_14	= $14				; mode control register
LAB_15	= $15				; serial comms control register
LAB_16	= $16				; serial comms status register
LAB_17	= $17				; serial comms Tx/Rx byte
LAB_18	= $18				; lower latch A
LAB_1A	= $1A				; upper latch A
LAB_1C	= $1C				; lower latch B
LAB_1E	= $1E				; upper latch B

LAB_40	= $40				; keyboard matrix p (written)
LAB_41	= $41				; keyboard matrix q (read)
LAB_42	= $42				; this key
LAB_43	= $43				; last key
LAB_44	= $44				; key held flag (bit 0 only)
LAB_45	= $45				; 1/2 cycle count low byte
LAB_46	= $46				; 1/2 cycle count high byte

					; $47 to $FF is the available stack space
	.ORG	$F000

; reset vector gets here

LAB_F000
	JMP	LAB_F00C		; go do reset

; IRQ vector gets here

LAB_F003
	JMP	LAB_F070		; go do IRQ

; NMI vector gets here

LAB_F006
	JMP	LAB_F00C		; go do NMI

; start main code from here

LAB_F009
	JMP	LAB_F09E		; go do main

; reset vector gets here - eventually
; NMI vector gets here - eventually

LAB_F00C
	LDX	#$40			; set index
	LDA	#$00			; set byte
LAB_F010
	STA	LAB_00,X		; clear memory byte
	INX				; increment index
	CPX	#$00			; all done? (redundant compare)
	BNE	LAB_F010		; loop if not

	LDX	#$FF			; set X
	TXS				; set stack

					; note that the 6501 stack grows down from $00FF
					; not $01FF as on the 6502!

	CLD				; clear decimal flag
	JSR	LAB_F02B		; set A, B and C ports as inputs (floating)
	JSR	LAB_F038		; set serial comms control & status
	JSR	LAB_F041		; set A and B timer values
	JSR	LAB_F052		; set modes and interrupts
	CLI				; enable interrupts
	JMP	LAB_F009		; go do main loop (goes to LAB_F09E)

; set A, B and C ports as inputs

LAB_F02B
	LDA	#$FF			; byte all 1's
	STA	LAB_00			; float port A
	LDA	#$FF			; byte all 1's
	STA	LAB_01			; float port B
	LDA	#$FF			; byte all 1's
	STA	LAB_02			; float port C
	RTS

; set serial comms control & status

LAB_F038
	LDA	#$C0			; set for enable Tx/Rx, ASYNC, 8NO
	STA	LAB_15			; set serial comms control register
	LDA	#$00			; disable wake-up and EOTx
	STA	LAB_16			; set serial comms status register
	RTS

; set A and B timer values

LAB_F041
	LDA	#$19			; set 4800 baud for 2MHz clock
	STA	LAB_18			; save to lower latch A
	LDA	#$00			; set 4800 baud for 2MHz clock
	STA	LAB_1A			; save to upper latch A, init counter, clr flag
	LDA	#$D0			; set 1mS timeout low byte
	STA	LAB_1C			; save to lower latch B
	LDA	#$07			; set 1mS timeout high byte
	STA	LAB_1E			; save to upper latch B, init counter, clr flag
	RTS

; the 6501 is set in 'normal' mode which means 14 bit addressing only
; A13 and A14 are not present.

; set modes and interrupts

LAB_F052
	LDA	#$40			; normal, tristate D, input B, timer B, timer A
	STA	LAB_14			; set mode control register
	LDA	#$20			; counter B underflow only
	STA	LAB_12			; set interrupt enable register
	RTS

; serial Tx byte

LAB_F05B
	RMB	#5,LAB_00		; enable Tx buffer
	PHA				; save A
	STA	LAB_17			; Tx serial comms byte
LAB_F060
	BBR	#6,LAB_16,LAB_F060	; wait for Tx empty

LAB_F063
	BBR	#7,LAB_16,LAB_F063	; wait for Tx under run

	SMB	#5,LAB_00		; disable Tx buffer
	PLA				; restore A
	CLC				; clear carry
	JMP	LAB_F06F		; exit (RTS would be better)

; unreferenced code

	PLA				; restore A
	SEC				; set carry
LAB_F06F
	RTS

; IRQ vector gets here - eventually

LAB_F070
	PHA				; save A
	TXA				; copy X
	PHA				; save X
	TYA				; copy Y
	PHA				; save Y
	LDA	LAB_1C			; read lower counter B, clear flag
	SMB	#7,LAB_01		; watchdog bit high
	RMB	#7,LAB_01		; watchdog bit low
	SMB	#7,LAB_01		; watchdog bit high (reset watchdog)
	LDA	LAB_45			; get 1/2 cycle count low byte
	ORA	LAB_46			; OR with 1/2 cycle count high byte
	BEQ	LAB_F098		; exit if zero

	SEC				; set carry for subtract
	LDA	LAB_45			; get 1/2 cycle count low byte
	SBC	#$01			; decrement it
	STA	LAB_45			; save new 1/2 cycle count low byte
	BCS	LAB_F08E		; branch if no underflow

	DEC	LAB_46			; decrement 1/2 cycle count high byte
LAB_F08E
	BBS	#0,LAB_00,LAB_F096	; go reset sounder bit if set

	SMB	#0,LAB_00		; else set sounder bit if reset
	JMP	LAB_F098		; skip reset

LAB_F096
	RMB	#0,LAB_00		; reset sounder bit
LAB_F098
	PLA				; get A
	TAY				; restore Y
	PLA				; get A
	TAX				; restore X
	PLA				; restore A
	RTI

; after setup we get here
; main keyboard poll loop

LAB_F09E
	JSR	LAB_F0CD		; poll keyboard, Cb=1 if key pressed
	BCS	LAB_F0AC		; branch if key pressed

	RMB	#0,LAB_44		; flag no key held
	LDA	#$00			; clear A
	STA	LAB_43			; clear last key
	JMP	LAB_F09E		; loop

; detected key pressed

LAB_F0AC
	JSR	LAB_F0E8		; get byte from table
	LDA	LAB_42			; read this key (was in A already)
	CMP	LAB_43			; compare with last key
	BEQ	LAB_F0BF		; branch if same

	STA	LAB_43			; else make last key this key
	RMB	#0,LAB_44		; reset key held flag
	JSR	LAB_F113		; do 28.6715mS delay
	JMP	LAB_F09E		; loop

; is same key as last key

LAB_F0BF
	BBS	#0,LAB_44,LAB_F09E	; loop if key held

					; same key as last key and held for ??mS
	SMB	#0,LAB_44		; set key held flag
	JSR	LAB_F126		; set cycle time & count from A
	JSR	LAB_F10F		; serial Tx byte
	JMP	LAB_F09E		; loop

; poll keyboard, Cb=1 if key pressed

LAB_F0CD
	LDA	#$FE			; set start bit pattern
	STA	LAB_40			; save keyboard matrix p
LAB_F0D1
	LDA	LAB_40			; get keyboard matrix p bit pattern
	STA	LAB_01			; to port B
	LDA	LAB_02			; get port C
	EOR	#$FF			; invert it
	BEQ	LAB_F0E0		; branch if no bits set

	STA	LAB_41			; else save keyboard matrix q bit pattern
	SEC				; set carry (flag key)
	BCS	LAB_F0E7		; branch always (exit)

LAB_F0E0
	SEC				; set carry (to move into b0)
	ROL	LAB_40			; rotate keyboard matrix p bit pattern
	BBS	#4,LAB_40,LAB_F0D1	; loop if not all done

	CLC				; clear carry (flag no key)
LAB_F0E7
	RTS

; get byte from table,00pq where ..
; p is # of first bit set in LAB_40 * 8 (keyboard matrix p)
; q is # of first bit set in LAB_41 (keyboard matrix q)
; byte saved in LAB_42 (this key)

LAB_F0E8
	LDA	LAB_41			; get keyboard matrix q
	JSR	LAB_F104		; get # of first A bit set in Y
	STY	LAB_42			; save bit # temp
	LDA	LAB_40			; get keyboard matrix p
	EOR	#$FF			; invert it
	JSR	LAB_F104		; get # of first A bit set in Y
	TYA				; copy # to A
	ASL				; *2
	ASL				; *4
	ASL				; *8
	CLC				; clear carry for add (is always clear here!)
	ADC	LAB_42			; add bit # temp
	TAY				; copy to index
	LDA	LAB_F1F1,Y		; get key from table
	STA	LAB_42			; save this key
	RTS

; get # of first A bit set in Y

LAB_F104
	CLC				; clear carry
	LDY	#$00			; clear bit #
LAB_F107
	LSR				; bit into carry
	BCS	LAB_F10E		; branch if set

	INY				; increment bit #
	JMP	LAB_F107		; loop

LAB_F10E
	RTS

; serial Tx byte

LAB_F10F
	JSR	LAB_F05B		; serial Tx byte
	RTS

; do 28.6715mS delay (57343 cycles @ 2MHz)

LAB_F113
	PHA				; save A
	TYA				; copy Y
	PHA				; save Y
	LDY	#$20			; set outer loop count
LAB_F118
	LDA	#$FF			; set inner loop count
LAB_F11A
	SEC				; set carry for subtract
	SBC	#$01			; -1
	BNE	LAB_F11A		; loop if not = 0

	DEY				; decrement count
	BNE	LAB_F118		; loop if not = 0

	PLA				; get A
	TAY				; restore Y
	PLA				; restore A
	RTS

; set cycle time & count from A

LAB_F126
	PHP				; push status
	SEI				; disable interrupts
	STA	LAB_45			; save in temp
	PHA				; push A
	TYA				; copy Y
	PHA				; save Y
	SEC				; set carry for subtract
	LDA	LAB_45			; restore from temp
	SBC	#$30			; subtract "0"
	ASL				; *2 (2 bytes per word)
	TAY				; copy to index
	LDA	LAB_F14D,Y		; get timer word low byte
	STA	LAB_1C			; save to lower latch B
	LDA	LAB_F14E,Y		; get timer word high byte
	STA	LAB_1E			; save to upper latch B, init counter, clr flag
	LDA	LAB_F19F,Y		; get timer cycles word low byte
	STA	LAB_45			; save 1/2 cycle count low byte
	LDA	LAB_F1A0,Y		; get timer cycles word high byte
	STA	LAB_46			; save 1/2 cycle count high byte
	PLA				; pull A
	TAY				; restore Y
	PLA				; restore A
	PLP				; restore status
	RTS

; timer B 1/2 cycle time words

LAB_F14D
LAB_F14E	= LAB_F14D+1		; product	frequency (Hz)	duration (S)
	.word	$0341			; 30CF0		1200.48		0.09996
	.word	$030D			; 30D00		1280.41		0.099968
	.word	$02DF			; 30CF0		1360.54		0.09996
	.word	$02B6			; 30CC0		1440.92		0.099936
	.word	$0291			; 30C30		1522.07		0.099864
	.word	$0271			; 30D40		1600.00		0.1
	.word	$0253			; 30CF0		1680.67		0.09996
	.word	$0238			; 30D00		1760.56		0.099968
	.word	$021F			; 30C90		1841.62		0.099912
	.word	$0208			; 30C00		1923.08		0.09984
	.word	$01F4			; 30D40		2000.00		0.1
	.word	$01E0			; 30C00		2083.33		0.09984
	.word	$01CE			; 30BA0		2164.50		0.099792
	.word	$01BE			; 30C80		2242.15		0.099904
	.word	$01AF			; 30D30		2320.19		0.099992
	.word	$01A0			; 30C00		2403.85		0.09984
	.word	$0193			; 30CD0		2481.39		0.099944
	.word	$0186			; 30C00		2564.10		0.09984
	.word	$017A			; 30BA0		2645.50		0.099792
	.word	$016F			; 30BE0		2724.80		0.099824
	.word	$0165			; 30CF0		2801.12		0.09996
	.word	$015B			; 30CC0		2881.84		0.099936
	.word	$0151			; 30B50		2967.36		0.099752
	.word	$0148			; 30B00		3048.78		0.099712
	.word	$0140			; 30C00		3125.00		0.09984
	.word	$0138			; 30C00		3205.13		0.09984
	.word	$0130			; 30B00		3289.47		0.099712
	.word	$0129			; 30BA0		3367.00		0.099792
	.word	$0122			; 30B60		3448.28		0.09976
	.word	$011C			; 30D00		3521.13		0.099968
	.word	$0115			; 30B10		3610.11		0.09972
	.word	$010F			; 30B20		3690.04		0.099728
	.word	$0109			; 30A70		3773.58 	0.09964
	.word	$0104			; 30C00		3846.15		0.09984
	.word	$00FF			; 30CF0		3921.57		0.09996
	.word	$00FA			; 30D40		4000.00		0.1
	.word	$00F5			; 30CF0		4081.63		0.09996
	.word	$00F0			; 30C00		4166.67		0.09984
	.word	$00EB			; 30A70		4255.32 	0.09964
	.word	$00E7			; 30BA0		4329.00		0.099792
	.word	$00E3			; 30C50		4405.29		0.09988

; 1/2 cycle count words
; see above for frequencies and durations

LAB_F19F
LAB_F1A0	= LAB_F19F+1
	.word	$00F0
	.word	$0100
	.word	$0110
	.word	$0120
	.word	$0130
	.word	$0140
	.word	$0150
	.word	$0160
	.word	$0170
	.word	$0180
	.word	$0190
	.word	$01A0
	.word	$01B0
	.word	$01C0
	.word	$01D0
	.word	$01E0
	.word	$01F0
	.word	$0200
	.word	$0210
	.word	$0220
	.word	$0230
	.word	$0240
	.word	$0250
	.word	$0260
	.word	$0270
	.word	$0280
	.word	$0290
	.word	$02A0
	.word	$02B0
	.word	$02C0
	.word	$02D0
	.word	$02E0
	.word	$02F0
	.word	$0300
	.word	$0310
	.word	$0320
	.word	$0330
	.word	$0340
	.word	$0350
	.word	$0360
	.word	$0370

; keyboard matrix decode table

LAB_F1F1
	.byte	"QRST","ABCD","7890","EFGH"
	.byte	"456U","IJKL","123V","MNOP"

; vector words at top of ROM

	.ORG	$FFFA

	.word	LAB_F006		; NMI vector
	.word	LAB_F000		; reset vector
	.word	LAB_F003		; IRQ vector

	.END

e-mail me [e-mail]
Last page update: 6th September, 2002.