	list	p=16f84
	radix	dec
	#include <p16f84.inc>

RXF#		equ	0
RD#		equ	2
WR#		equ	3
BC1		equ	4
DIR		equ	5
RES#		equ	6

RD#M		equ	(1 << RD#)
BC1M		equ	(1 << BC1)
DIRM		equ	(1 << DIR)

CMD_TMR_INIT	equ	0x0
CMD_TMR_WAIT	equ	0x1
CMD_WR_ADDR	equ	0x2
CMD_WR_DATA	equ	0x3
CMD_RESET	equ	0x4
CMD_PSG0	equ	0x5
CMD_PSG1	equ	0x6

CMD_MASK	equ	b'000000111'

TM_MOD1_75	equ	(-34)
TM_MOD3_5	equ	(-68)
TM_MOD7		equ	(-136)

TM_MOD		equ	TM_MOD7

VARS	udata
psg	res	1
tmp	res	1

STARTUP	code
 	goto	start			; reset vector

PROG	code
start:					; device initialization
	bcf	STATUS, RP0		; bank0
        clrf	PORTA

	movlw	b'01001100'		; BC1=0, DIR=0, RD#=1, WR#=1, RES#=1
	movwf	PORTB

	clrf	INTCON
	movlw	TM_MOD
	movwf	TMR0

        bsf	STATUS, RP0		; bank1
	movlw	0x1F			; set RA0-RA4 inp
	movwf	TRISA

	movlw	b'10000011'		; set  RB0, RB1, RB7 in
					; RB2-RB6 out
	movwf	TRISB

	movlw	b'10000111'		; prescaler=256, pullups disabled
	movwf	OPTION_REG

	bcf	STATUS, RP0		; bank0

	clrf	psg

	call	cmd_reset

main_loop:
	call	wait_rx

        bcf	PORTB, RD#		; RD# -> 0

        movfw	PORTA			; read cmd
	andlw	CMD_MASK		; mask reserved bits

        bsf	PORTB, RD#		; RD# -> 1

	call	cmd_tbl

        goto	main_loop

;-----------------------------------------
	; cmd dispatcher
cmd_tbl:
	addwf	PCL, f
	goto	cmd_tmr_init
	goto	cmd_tmr_wait
	goto	cmd_wr_addr
	goto	cmd_wr_data

	goto	cmd_reset
	goto	cmd_psg0
	goto	cmd_psg1
	return

cmd_tmr_init:
	movlw	TM_MOD
	movwf	TMR0			; reload timer (1.75Mhz/(4*256*34))
	return

cmd_tmr_wait:
        btfss   INTCON, T0IF		; wait timer overflow
        goto	cmd_tmr_wait
        bcf	INTCON, T0IF		; clear overflow flag
	goto	cmd_tmr_init

cmd_wr_addr:
        call	wait_rx

        call	wait_psg

        bcf	PORTB, RD#		; RD# -> 0

        movfw	PORTB
        iorlw	(BC1M | DIRM)		; write address to tsfm
        movwf	PORTB

        andlw	~(BC1M | DIRM)		; deactivate tsfm (DIR=0, BC1=0)
        movwf	PORTB
        bsf	PORTB, RD#		; RD# -> 1
	return

cmd_wr_data:
        call	wait_rx

        call	wait_psg

        bcf	PORTB, RD#		; RD# -> 0

        movfw	PORTB
        iorlw	DIRM
        andlw	~BC1M			; DIR=1 BC1=0
        movwf	PORTB			; write data to tsfm

        andlw	~(BC1M | DIRM)		; deactivate tsfm (DIR=0, BC1=0)
        movwf	PORTB
        bsf	PORTB, RD#		; RD# -> 1
	return

cmd_reset:
        movlw	20			; 20 div periods
        movwf	tmp

        bcf	PORTB, RES#		; RES# -> 0

cmd_reset.loop:
	decfsz	tmp, f
        goto	cmd_reset.loop

        bsf	PORTB, RES#		; RES# -> 1
	clrf	psg
	return

cmd_psg0:
	movlw	b'010'			; FM on, PSG=0
	bcf	psg, 0
	goto	psg_select

cmd_psg1:
	movlw	b'011'			; FM on, PSG=1
	bsf	psg, 0
	goto	psg_select

	; utility routines
psg_select:
	movwf	PORTA

        bsf	STATUS, RP0		; bank1
	movlw	b'11000'		; set RA0-RA2 out, RA3-RA4 inp
	movwf	TRISA
        bcf	STATUS, RP0		; bank0

        movfw	PORTB
        iorlw	(BC1M | DIRM)		; write address to tsfm
        movwf	PORTB

        andlw	~(BC1M | DIRM)		; deactivate tsfm (DIR=0, BC1=0)
        movwf	PORTB

        bsf	STATUS, RP0		; bank1
	movlw	b'11111'		; set RA0-RA4 inp
	movwf	TRISA
        bcf	STATUS, RP0		; bank0

	return

wait_rx:
        btfsc	PORTB, RXF#		; wait rx byte
        goto	wait_rx
	return

wait_psg:				; wait psg ready
	movlw	b'000'			; select status register
	iorwf	psg, w

	call	psg_select

wait_psg.loop:
	bsf	PORTB, BC1		; read data from tsfm (DIR=0, BC1=1)
	movfw	PORTB			; RB7 - psg status
	bcf	PORTB, BC1		; deactivate tsfm (DIR=0, BC1=0)
	andlw	0x80
	btfss	STATUS, Z
	goto	wait_psg.loop

	movlw	b'010'			; select psg registers
	iorwf	psg, w
	call	psg_select

	return
	end
