;// =================================================================
;//
;//  YMF294 SSG BOARD FIRMWARE
;//
;//    (c)masa-u 2003.11.19
;//    http://mbin.jp/~masa-u/
;//
;//    Device: PIC16F876
;//    OSC:    11.059200 MHz
;//    Asm:    MPASM
;//
;// =================================================================

	    include	p16f876.inc
	    
	    LIST	P=pic16f876
	    
;//**** device configuration

	    __config	_HS_OSC & _WDT_OFF & _PWRTE_OFF & _LVP_OFF
	    
;//**** pin assignments

;	    RA0 -> SSG0 CS/WR
;	    RA1 -> SSG1 CS/WR
;	    RA2 -> SSG2 CS/WR
;	    RA3 -> SSG3 CS/WR
;	    RA4 -> SSG RESET
;	    RA5 -> SSG ADDR/DATA

SSG0_WR	    equ		0
SSG1_WR	    equ		1
SSG2_WR	    equ		2
SSG3_WR	    equ		3
SSG_RST	    equ		4
SSG_ADDR    equ		5

;	    RBx -> SSG DATA BUS

;	    RC0 -> ACCESS LED
;	    RC1 <- RESET SW
;	    RC2 <- CONFIG 0
;	    RC3 <- CONFIG 1
;	    RC4 <- CONFIG 2
;	    RC5 <- CONFIG 3
;	    RC6 -> 232 OUT
;	    RC7 <- 232 IN

LED_BUSY    equ		0
SW_RESET    equ		1
SW_CFG0	    equ		2
SW_CFG1	    equ		3
SW_CFG2	    equ		4
SW_CFG3	    equ		5


;//---- constants

NCHANNELS   equ		0x0C	;// number of channels


;//---- reserve F's (for direct access)

savedW	    equ		0x20
savedStat   equ		0x21

ttmp0	    equ		0x24
lp0	    equ		0x25
lp1	    equ		0x26

cmd0	    equ		0x28
cmd1	    equ		0x29
cmdx	    equ		0x2A
cmdl	    equ		0x2B

utmp0	    equ		0x30
utmp1	    equ		0x31
utmp2	    equ		0x32
utmp3	    equ		0x33

stmp0	    equ		0x34
stmp1	    equ		0x35
stmp2	    equ		0x36
stmp3	    equ		0x37

arg0	    equ		0x38
arg1	    equ		0x39
arg2	    equ		0x3A
arg3	    equ		0x3B

tmr0_vib    equ		0x40	    ;// process vibrato on next tmr0

mix0	    equ		0x50
mix1	    equ		0x51
mix2	    equ		0x52
mix3	    equ		0x53


;//---- reserve F's (for indirect access in banks 2 and 3)

ch_flag	    equ		0x110
ENV_EN	    equ		0	    ;// envelope is enabled
;//VIB_EN	equ	    1
VIB_UP	    equ		4	    ;// vibrato is going up
VIB_UP_L    equ		0x10

ch_pLo	    equ		0x11C
ch_pHi	    equ		0x128
ch_bend	    equ		0x134
vib_param   equ		0x140	    ;// [speed][depth]
vib_tout    equ		0x14C
vib_ctr	    equ		0x158
vib_ph	    equ		0x164	    ;// [cur bend]

env_p0	    equ		0x190
env_p1	    equ		0x19C
env_p2	    equ		0x1A8
env_t0	    equ		0x1B4
env_t1	    equ		0x1C0
env_t2	    equ		0x1CC
env_ph	    equ		0x1D8
env_ctr	    equ		0x1E4


;//---- vector initialize

	    org		0
_RESET_	    goto	_Main
	    org		4
	    goto	_Intr
	

;//==========================================================================
;//==========================================================================
;//	    MAIN PROGRAM
;//==========================================================================

;//==== main program

	    org		8
_Main	    call	_Init	    ;// initialize

	    ;//==== reset SSGs
	    bcf		PORTC, LED_BUSY
	    movlw	0F
	    movwf	PORTA
	    
	    movlw	20
	    movwf	arg0
	    call	_Wait
	    
	    bsf		PORTA, SSG_RST

	    ;//==== reset mixer
	    movlw	0x38
	    movwf	mix0
	    movwf	mix1
	    movwf	mix2
	    movwf	mix3
	    
	    movwf	arg2
	    movlw	0x07
	    movwf	arg1
	    movlw	0x0F
	    movwf	arg0
	    call	_WriteSSG_F
	    
	    ;//---- clear everything
	    bsf		STATUS, IRP
	    
	    movlw	ch_flag
	    call	_ClrChanF
	    movlw	ch_pLo
	    call	_ClrChanF
	    movlw	ch_pHi
	    call	_ClrChanF
	    movlw	ch_bend
	    call	_ClrChanF
	    
	    movlw	vib_param
	    call	_ClrChanF
	    movlw	vib_tout
	    call	_ClrChanF
	    movlw	vib_ctr
	    call	_ClrChanF
	    movlw	vib_ph
	    call	_ClrChanF
	    
	    movlw	env_p0
	    call	_ClrChanF
	    movlw	env_p1
	    call	_ClrChanF
	    movlw	env_p2
	    call	_ClrChanF
	    movlw	env_t0
	    call	_ClrChanF
	    movlw	env_t1
	    call	_ClrChanF
	    movlw	env_t2
	    call	_ClrChanF
	    movlw	env_ph
	    call	_ClrChanF
	    movlw	env_ctr
	    call	_ClrChanF

	    clrf	tmr0_vib
	    
	    ;//----
	    movlw	20
	    movwf	arg0
	    call	_Wait		
	    bsf		PORTC, LED_BUSY
	    
	    ;//==== loop
	    goto	_MainLP
	    

;//---- main loop
	    
_MainLP	    btfss	PORTC, SW_RESET
	    goto	_RESET_

	    ;// --- check if there's command received
	    btfsc	cmdx, 0
	    call	_ProcCmd

	    ;// --- timer 0 has overflowed
I_TMR0	    btfss	INTCON, T0IF
	    goto	E_TMR0
	    
	    ;// process vibrato or envelope
	    btfss	tmr0_vib, 0
	    goto	I_T0_PrEnv
	    
I_T0_PrVib  call	_ProcVib
	    goto	E_T0_Proc
	    
I_T0_PrEnv  btfsc	tmr0_vib, 1
	    call	_ProcEnv

E_T0_Proc   bcf		INTCON, T0IF
	    incf	tmr0_vib, f
E_TMR0

E_MainLP    ;// --- end of main loop
	    goto	_MainLP


;//---- command processor
	    
_ProcCmd    clrf	cmdx

	    ;// move channel -> utmp0, cmd1 -> utmp1		
	    movf	cmd1, W
	    movwf	utmp1
	    movwf	arg2
	    
	    movf	cmd0, W
	    andlw	0x0F
	    movwf	utmp0
	    movwf	arg0
	    movwf	arg1
	    
	    clrf	PCLATH
	    swapf	cmd0, W
	    andlw	0x0F
	    addwf	PCL, f
	    
	    goto	I_CMDP_0
	    goto	I_CMDP_1
	    goto	I_CMDP_2
	    goto	I_CMDP_3
	    goto	I_CMDP_4
	    goto	I_CMDP_5
	    goto	I_CMDP_6
	    goto	I_CMDP_7
	    goto	I_CMDP_8
	    goto	I_CMDP_9
	    goto	I_CMDP_A
	    goto	I_CMDP_B
	    goto	I_CMDP_C
	    goto	I_CMDP_D
	    goto	I_CMDP_E
	    goto	I_CMDP_F
	    
I_CMDP_0    ;// ** CMD 0 -- do nothing
	    goto	E_CMDP
	    
I_CMDP_1    ;// ** CMD 1 -- set note
	    movlw	ch_flag
	    addwf	utmp0, W
	    movwf	FSR
	    btfss	INDF, ENV_EN
	    goto	I_CMD1_NENV
	    
	    btfsc	utmp1, 7    ;// don't reset envelope if MSB is set
	    goto	I_CMD1_NENV
	    movf	utmp1, W
	    btfsc	STATUS, Z   ;// if (note == 0) release envelope
	    goto	I_CMD1_RENV
	    
	    ;// reset envelope and vibrato
	    call	_StartEnv
	    movf	utmp0, W
	    movwf	arg0
	    call	_StartVib
	    movf	utmp0, W
	    movwf	arg0
	    movf	utmp1, W
	    movwf	arg2
	    
I_CMD1_NENV call	_LoadNote
	    call	_UpdateNote
	    goto	E_CMDP

I_CMD1_RENV call	_ReleaseEnv
	    goto	E_CMDP

	    
I_CMDP_2    ;// ** CMD 2 -- set volume
	    movlw	ch_flag
	    addwf	utmp0, W
	    movwf	FSR
	    
	    btfss	arg2, 5	    ;// use sw envelope?
	    goto	I_CMD2_SETV ;// don't use
	
	    ;// use sw envelope
	    bsf		INDF, ENV_EN
	    goto	E_CMDP
	    
	    ;// set volume
I_CMD2_SETV bcf		INDF, ENV_EN
	    call	_SetVol
	    goto	E_CMDP
	    
I_CMDP_3    ;// ** CMD 3 -- set mode
	    call	_SetMode
	    goto	E_CMDP
	    
I_CMDP_4    ;// ** CMD 4 -- set noise period
	    call	_SetNoiseP
	    goto	E_CMDP
	    
I_CMDP_5    ;// ** CMD 5 -- set hw envelope period (low order)
	    call	_SetEnvPLo
	    goto	E_CMDP

I_CMDP_6    ;// ** CMD 6 -- set hw envelope period (high order)
	    call	_SetEnvPHi
	    goto	E_CMDP

I_CMDP_7    ;// ** CMD 7 -- set hw envelope control
	    call	_SetEnvMode
	    goto	E_CMDP
	    
I_CMDP_8    ;// ** CMD 8 -- set sw envelope level
	    movlw	env_p0
	    btfsc	arg2, 7
	    addlw	NCHANNELS * 2
	    btfsc	arg2, 6
	    addlw	NCHANNELS
	    addwf	arg0, W
	    movwf	FSR
	    
	    movf	arg2, W
	    andlw	0x3F
	    movwf	INDF
	    goto	E_CMDP
	    
I_CMDP_9    ;// ** CMD 9 -- set sw envelope time
	    movlw	env_t0
	    btfsc	arg2, 7
	    addlw	NCHANNELS * 2
	    btfsc	arg2, 6
	    addlw	NCHANNELS
	    addwf	arg0, W
	    movwf	FSR
	    
	    movf	arg2, W
	    andlw	0x3F
	    movwf	INDF
	    goto	E_CMDP
	    
I_CMDP_A    ;// ** CMD A -- set pitch bend
	    movlw	ch_bend
	    addwf	arg0, W
	    movwf	FSR
	    movf	arg2, W
	    movwf	INDF
	    call	_UpdateNote
	    goto	E_CMDP
	    
I_CMDP_B    ;// ** CMD B -- set vibrato params
	    movlw	vib_param
	    addwf	arg0, W
	    movwf	FSR
	    movf	arg2, W
	    movwf	INDF
	    goto	E_CMDP
	    
I_CMDP_C    ;// ** CMD C -- set vibrato timeout
	    movlw	vib_tout
	    addwf	arg0, W
	    movwf	FSR
	    movf	arg2, W
	    movwf	INDF
	    goto	E_CMDP
	    
I_CMDP_D    ;// ** CMD D -- not defined
I_CMDP_E    ;// ** CMD E -- not defined
	    goto	E_CMDP
	    
I_CMDP_F    ;// ** CMD F -- reset if 'FF' 'FF'

	    ;// reset if all bits == 1
	    movlw	0xFF
	    xorwf	cmd0, W
	    btfss	STATUS, Z
	    movlw	0xFF
	    xorwf	cmd1, W
	    btfss	STATUS, Z
	    goto	_RESET_
	    
	    goto	E_CMDP
	    
E_CMDP	    ;// dim access led
	    bsf		PORTC, LED_BUSY

	    return
	    

;//---- envelope processor
	    
_ProcEnv    movlw	D'11'
	    movwf	lp0	;// channel iterator
	    
L_ENV	    ;// --- is envelope enabled ?
	    movlw	ch_flag
	    addwf	lp0, W
	    movwf	FSR
	    btfss	INDF, ENV_EN
	    goto	N_ENV
	    
	    ;// --- has envelope reached end ?
	    movlw	env_ph
	    addwf	lp0, W
	    movwf	FSR
	    movf	INDF, W
	    movwf	utmp0
	    btfsc	utmp0, 6
	    goto	N_ENV
	    
	    ;// --- decrement envelope counter
	    movlw	env_ctr
	    addwf	lp0, W
	    movwf	FSR
	    decfsz	INDF, f
	    goto	N_ENV
	    
	    ;// === step envelope
	    
	    ;// --- get current phase
	    swapf	utmp0, W
	    andlw	3
	    movwf	utmp1
	    
	    ;// --- get target vol
I_ENV_TVOL  movlw	0
	    btfsc	utmp1, 1
	    goto	E_ENV_TVOL	;// release state, target is 0
	    addlw	NCHANNELS
	    btfsc	utmp1, 0
	    addlw	NCHANNELS
	    addlw	env_p0
	    addwf	lp0, W
	    movwf	FSR
	    movf	INDF, W
E_ENV_TVOL  
	    movwf	utmp2
	    
	    ;// --- reached target ?
	    movf	utmp0, W
	    andlw	0x0F		;// current volume
	    movwf	utmp0
	    subwf	utmp2, W	;// target vol - current vol
	    btfsc	STATUS, Z
	    goto	I_ENV_STEP
	    
I_ENV_PROC
	    ;// ---- increment or decrement volume
I_ENV_VOL   btfsc	STATUS, C	;// positive, increment current.
	    incf	utmp0, f
	    btfss	STATUS, C	;// negative, decrement current.
	    decf	utmp0, f
	    
	    ;// --- write channel volume to ssg
	    movf	lp0, W
	    movwf	arg0
	    movf	utmp0, W
	    movwf	arg2
	    call	_SetVol
	    goto	E_ENV_PROC
	    
	    ;// ---- step envelope phase
I_ENV_STEP  btfsc	utmp1, 1
	    goto	I_ENV_END
	    btfsc	utmp1, 0
	    goto	I_ENV_SUS	;// sustain
	    incf	utmp1, f
	    goto	E_ENV_PROC
	    
	    ;// --- sustain at envelope point 2
I_ENV_SUS   movlw	env_ctr
	    addwf	lp0, W
	    movwf	FSR
	    movlw	0xFF
	    movwf	INDF
	    goto	I_ENV_SUPD

	    ;// --- envelope reached end
I_ENV_END   movlw	0x04
	    movwf	utmp1
	    goto	I_ENV_SUPD
	    
E_ENV_PROC  ;// --- reset envelope counter
	    movlw	env_t0
	    btfsc	utmp1, 0
	    addlw	NCHANNELS
	    btfsc	utmp1, 1
	    addlw	0x18
	    addwf	lp0, W
	    movwf	FSR
	    movf	INDF, W
	    addlw	1
	    movwf	utmp2
	    
	    movlw	env_ctr
	    addwf	lp0, W
	    movwf	FSR
	    movf	utmp2, W
	    movwf	INDF
	    
	    ;// --- update envelope status
I_ENV_SUPD  movlw	env_ph
	    addwf	lp0, W
	    movwf	FSR
	    
	    swapf	utmp1, W
	    andlw	0x70
	    iorwf	utmp0, W
	    movwf	INDF
	    
N_ENV	    ;// --- to next channel
	    decf	lp0, f
	    btfss	lp0, 7
	    goto	L_ENV

E_ENV
	    return


;//---- vibrato processor
	    
_ProcVib    movlw	D'11'
	    movwf	lp0	;// channel iterator
	    
L_VIB	    ;// --- is envelope enabled ?
	    movlw	vib_param
	    addwf	lp0, W
	    movwf	FSR
	    movf	INDF, W
	    movwf	utmp0		;// utmp0 = vib_param
	    andlw	0x0F
	    movwf	utmp1		;// utmp1 = vib_depth
	    btfsc	STATUS, Z
	    goto	N_VIB
	    
	    ;// --- decrement vibrato counter
	    movlw	vib_ctr - vib_param
	    addwf	FSR, f
	    decfsz	INDF, f
	    goto	N_VIB
	    
	    ;// === step vibrato
	    
	    ;// --- is vibrato going up ?
	    movlw	ch_flag
	    addwf	lp0, W
	    movwf	FSR
	    btfss	INDF, VIB_UP
	    goto	I_VIB_DOWN
	    
I_VIB_UP    ;// --- increment vibrato phase
	    movlw	vib_ph - ch_flag
	    addwf	FSR, f
	    incf	INDF, f
	    
	    ;// --- vibrato reached top ?
	    btfsc	INDF, 7
	    goto	E_VIB_UPDN
	    
	    movf	utmp1, W
	    subwf	INDF, W
	    btfsc	STATUS, C
	    goto	I_VIB_CHDIR
	    goto	E_VIB_UPDN
	    
I_VIB_DOWN  ;// --- decrement vibrato phase
	    movlw	vib_ph - ch_flag
	    addwf	FSR, f
	    decf	INDF, f
	    
	    ;// --- vibrato reached bottom ?
	    btfss	INDF, 7
	    goto	E_VIB_UPDN
	    
	    movf	utmp1, W
	    addwf	INDF, W
	    btfsc	STATUS, Z
	    goto	I_VIB_CHDIR
	    btfsc	STATUS, C
	    goto	E_VIB_UPDN
	    
	    ;// overshoot
	    clrf	INDF
	    subwf	INDF, f
	    
	    ;// --- change direction
I_VIB_CHDIR movlw	ch_flag
	    addwf	lp0, W
	    movwf	FSR
	    
	    movlw	VIB_UP_L
	    xorwf	INDF, f
	    
E_VIB_UPDN
	    
	    ;// --- reset vibrato counter
I_VIB_CTR   movlw	vib_ctr
	    addwf	lp0, W
	    movwf	FSR
	    
	    swapf	utmp0, W
	    andlw	0x0F
	    addlw	1
	    movwf	INDF
	    
	    ;// --- update ssg
	    movf	lp0, W
	    movwf	arg0
	    call	_UpdateNote
	    
N_VIB	    ;// --- to next channel
	    decf	lp0, f
	    btfss	lp0, 7
	    goto	L_VIB

E_VIB
	    return


;//---- initializer

_Init	    clrf	PORTA
	    clrf	PORTB
	    clrf	PORTC
	    
	    clrf	cmdx
	    clrf	cmdl
	    
	    ;// --- go to bank 1
	    bsf		STATUS, RP0
	    
	    ;// set input-output flag
	    clrf	TRISA
	    clrf	TRISB
	    movlw	0xBE
	    movwf	TRISC
	    
	    ;// --- initialize timer 0
	    bcf		OPTION_REG, T0CS
	    bcf		OPTION_REG, PSA
	    bsf		OPTION_REG, PS2
	    bcf		OPTION_REG, PS1
	    bcf		OPTION_REG, PS0
	    
	    ;// --- back to bank 0
	    bcf		STATUS, RP0
	    
	    ;// --- determine baud rate
	    movf	PORTC, W
	    movwf	stmp0
	    rrf		stmp0, f
	    rrf		stmp0, f
	    movf	stmp0, W
	    andlw	3
	    xorlw	3
	    call	_T_Baud		
	    
	    ;// ==================================
	    ;// --- go to bank 1
	    bsf		STATUS, RP0
	    movwf	SPBRG
	    
	    ;// set USART transmitter
	    movlw	0x24	    ;// 00100100b
	    movwf	TXSTA
	    
	    ;// enable USART receiver interrupt
	    bsf		PIE1, RCIE
			
	    ;// --- back to bank 0
	    bcf		STATUS, RP0
	    
	    ;// set USART receiver
	    movlw	0x90	    ;//10010000b
	    movwf	RCSTA
	    
	    bcf		PIR1, RCIF
	    
	    ;// initialize INTCON register
	    clrf	INTCON
	    bsf		INTCON, PEIE
	    bsf		INTCON, GIE	;// now RCIE interrupt is available
	    return
	    

;//==========================================================================
;//	    INTERRUPTS
;//==========================================================================

_Intr	    ;// save registers
	    movwf	savedW
	    swapf	STATUS, W
	    movwf	savedStat
	    
	    ;// light access LED
	    bcf		PORTC, LED_BUSY
	    
	    ;// do interrupt
	    movf	RCREG, W

	    btfsc	cmdl, 0	    ;// cmd already loaded?
	    goto	I_LD_C1
	    
I_LD_C0	    ;// cmd not loaded. load cmd.
	    movwf	cmd0
	    bsf		cmdl, 0
	    goto	E_LD_Cx
	    
I_LD_C1	    ;// cmd loaded. load arg.
	    movwf	cmd1
	    bcf		cmdl, 0
	    bsf		cmdx, 0
E_LD_Cx

E_Intr	    ;// clear interrupt flag
	    ;// bcf	    PIR1, RCIF
	    
	    ;// dim access LED
	    ;//bsf	    PORTC, LED_BUSY

	    ;// restore registers
	    swapf	savedStat, W
	    movwf	STATUS
	    swapf	savedW, f
	    swapf	savedW, W
	    
	    retfie

	    
;//==========================================================================
;//	    OTHER FUNCTIONS
;//==========================================================================


;//********
_StartEnv   ;// arg0 -> channel ID
	    
	    ;// --- get initial vol
	    movlw	env_p0
	    addwf	arg0, W
	    movwf	FSR
	    movf	INDF, W
	    movwf	stmp0
	    
	    ;// --- get initial envelope time
	    movlw	env_t0 - env_p0
	    addwf	FSR, f
	    movf	INDF, W
	    addlw	1
	    movwf	stmp1
	    
	    ;// --- store envelope counter
	    movlw	env_ctr - env_t0
	    addwf	FSR, f
	    movf	stmp1, W
	    movwf	INDF
	    
	    ;// --- store envelope vol
	    movlw	env_ph - env_ctr
	    addwf	FSR, f
	    movf	stmp0, W
	    movwf	INDF
	    
	    ;// --- write vol to ssg
	    movwf	arg2
	    call	_SetVol		
	    return


;//********
_ReleaseEnv ;// arg0 -> channel ID

	    ;// --- set envelope phase to 2
	    movlw	env_ph
	    addwf	arg0, W
	    movwf	FSR
	    movf	INDF, W
	    
	    andlw	0x0F
	    iorlw	0x20
	    movwf	INDF
	    
	    ;// --- set envelope counter to 1 so that decay starts immediately
	    movlw	env_ctr
	    addwf	arg0, W
	    movwf	FSR
	    movlw	1
	    movwf	INDF
	    return


;//********
_StartVib   ;// arg0 -> channel ID
	    
	    ;// --- reset phase
	    movlw	vib_ph
	    addwf	arg0, W
	    movwf	FSR
	    clrf	INDF
	    
	    ;// --- set counter to (timeout + 1)
	    movlw	vib_tout - vib_ph
	    addwf	FSR, f
	    movf	INDF, W
	    addlw	1
	    movwf	stmp0
	    
	    movlw	vib_ctr - vib_tout
	    addwf	FSR, f
	    movf	stmp0, W
	    movwf	INDF

	    ;// --- go down first
	    movlw	ch_flag - vib_ctr
	    addwf	FSR, f
	    bcf		INDF, VIB_UP
	    return
	    
	    
;//********
_LoadNote   ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> note (midi)
	    
	    movf	arg0, W
	    movwf	stmp1
	    
	    ;// --- load low-order bits
	    movlw	ch_pLo
	    addwf	arg0, W
	    movwf	FSR
	    movf	arg2, W
	    call	_Note2Per_L
	    movwf	INDF
	    
	    ;// --- write high-order bits
	    movlw	ch_pHi - ch_pLo
	    addwf	FSR, f
	    movf	arg2, W
	    call	_Note2Per_H
	    movwf	INDF

	    return

;//********
_UpdateNote ;// arg0 -> channel ID

	    ;// add period and bend param
	    movlw	ch_pLo
	    addwf	arg0, W
	    movwf	FSR
	    movf	INDF, W
	    movwf	arg2
	    
	    movlw	ch_pHi - ch_pLo
	    addwf	FSR, f
	    movf	INDF, W
	    movwf	arg3
	    
	    movlw	ch_bend - ch_pHi
	    addwf	FSR, f
	    movf	INDF, W
	    movwf	arg1
	    
	    call	_AddPeriod
	    
	    ;// add vibrato phase
	    movlw	vib_ph - ch_bend
	    addwf	FSR, f
	    movf	INDF, W
	    movwf	arg1
	    
	    call	_AddPeriod
	    
	    ;// and update ssg
	    
;//********
_SetPeriod  ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> period Lo
	    ;// arg3 -> period Hi
	    
	    call	_Ch2Chip

	    ;// --- write period
	    movf	arg1, W
	    addwf	arg1, f
	    call	_WriteSSG2
	    
	    return
	    
			
;//******** (DEPRECATED)
_SetNote    ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> note (midi)
	    
	    call	_Ch2Chip
	    
	    movf	arg0, W
	    movwf	stmp1
	    
	    ;// --- write low-order bits
	    movf	arg1, W
	    addwf	arg1, f
	    movf	arg2, W
	    movwf	stmp2
	    call	_Note2Per_L
	    movwf	arg2
	    call	_WriteSSG
	    
	    ;// --- write high-order bits
	    movf	stmp1, W
	    movwf	arg0
	    incf	arg1, f
	    movf	stmp2, W
	    call	_Note2Per_H
	    movwf	arg2
	    call	_WriteSSG
	    
	    return
	    

;//********
_SetNoiseP  ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> noise period

	    call	_Ch2Chip
	    movlw	0x06
	    movwf	arg1
	    call	_WriteSSG
	    return
	    
;//********
_SetMode    ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> mode (0 = silence, 1 = tone, 2 = noise, 3 = both)
	    
	    call	_Ch2Chip

	    ;// create base mode mask		
	    movlw	0
	    btfss	arg2, 0
	    iorlw	0x01
	    btfss	arg2, 1
	    iorlw	0x08
	    movwf	stmp0
	    
	    movlw	0xF6
	    movwf	stmp1
	    
	    ;// rotate mode masks so that they match the channel specified
	    movf	arg1, W
	    movwf	stmp2
L_ModeMask  movf	stmp2, f
	    btfsc	STATUS, Z
	    goto	E_ModeMask
	    bsf		STATUS, C
	    rlf		stmp1, f
	    bcf		STATUS, C
	    rlf		stmp0, f
	    decf	stmp2, f
	    goto	L_ModeMask
E_ModeMask

	    ;// update mix* register
	    movf	arg0, W
	    addlw	mix0
	    movwf	FSR
	    
	    movf	INDF, W
	    andwf	stmp1, W
	    iorwf	stmp0, W
	    movwf	INDF
	    movwf	arg2
	    
	    movlw	0x07
	    movwf	arg1		
	    
	    ;// and write.
	    call	_WriteSSG
	    return
	    
;//********
_SetVol	    ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> volume

	    call	_Ch2Chip
	    
	    movlw	0x08
	    addwf	arg1, f
	    call	_WriteSSG
	    return
	    
;//********
_SetEnvPLo  ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> envelope period (low 8 bits)

	    call	_Ch2Chip
	    movlw	0x0B
	    movwf	arg1
	    call	_WriteSSG
	    return
			
;//********
_SetEnvPHi  ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> envelope period (high 8 bits)

	    call	_Ch2Chip
	    movlw	0x0C
	    movwf	arg1
	    call	_WriteSSG
	    return
	    
;//********
_SetEnvMode ;// arg0 -> channel ID
	    ;// arg1	X
	    ;// arg2 -> envelope mode

	    call	_Ch2Chip
	    movlw	0x0D
	    movwf	arg1
	    call	_WriteSSG
	    return
	    
	    
;//********
_WriteSSG   ;// arg0 -> chip no
	    ;// arg1 -> register no.
	    ;// arg2 -> register data
	    
	    movlw	3
	    andwf	arg0, W
	    call	_T_ChMask
	    movwf	arg0
	    goto	I_WRSSG

;//********
_WriteSSG_F ;// arg0 -> chip flag
	    ;// arg1 -> register no.
	    ;// arg2 -> register data
	    
	    movf	arg0, W
	    andlw	0x0F
	    xorlw	0x0F
	    movwf	arg0
	    
	    ;// write address
I_WRSSG	    movlw	0x10
	    iorwf	arg0, W
	    movwf	PORTA
	    movf	arg1, W
	    movwf	PORTB
	    movlw	0x1F
	    movwf	PORTA
	    
	    ;// write data
	    movlw	0x30
	    iorwf	arg0, W
	    movwf	PORTA
	    movf	arg2, W
	    movwf	PORTB
	    movlw	0x3F
	    movwf	PORTA
	    
	    return
	    

;//********
_WriteSSG2  ;// arg0 -> chip no
	    ;// arg1 -> register no.
	    ;// arg2 -> register data[0]
	    ;// arg3 -> register data[1]
	    
	    movlw	3
	    andwf	arg0, W
	    call	_T_ChMask
	    movwf	arg0
	    goto	I_WRSSG_2

;//********
_WriteSSG_F2	;// arg0 -> chip flag
	    ;// arg1 -> register no.
	    ;// arg2 -> register data[0]
	    ;// arg3 -> register data[1]
	    
	    movf	arg0, W
	    andlw	0x0F
	    xorlw	0x0F
	    movwf	arg0
	    
	    ;// write address + 1
I_WRSSG_2   movlw	0x10
	    iorwf	arg0, W
	    movwf	PORTA
	    movf	arg1, W
	    addlw	1
	    movwf	PORTB
	    movlw	0x1F
	    movwf	PORTA
	    
	    ;// write data[1]
	    movlw	0x30
	    iorwf	arg0, W
	    movwf	PORTA
	    movf	arg3, W
	    movwf	PORTB
	    movlw	0x3F
	    movwf	PORTA
	    
	    ;// write address
	    movlw	0x10
	    iorwf	arg0, W
	    movwf	PORTA
	    movf	arg1, W
	    movwf	PORTB
	    movlw	0x1F
	    movwf	PORTA
	    
	    ;// write data[0]
	    movlw	0x30
	    iorwf	arg0, W
	    movwf	PORTA
	    movf	arg2, W
	    movwf	PORTB
	    movlw	0x3F
	    movwf	PORTA
	    
	    return
	    

;//********
_AddPeriod  ;// arg0	X
	    ;// arg1 -> addend
	    ;// arg2 <> period Lo
	    ;// arg3 <> period Hi
	    
	    movf	arg1, W
	    addwf	arg2, f
	    
	    ;// is addend negative ?
	    btfsc	arg1, 7
	    goto	I_ADDP_NEG
	    
I_ADDP_POS  ;// addend is positive, increment hi-order if carry occurred
	    btfsc	STATUS, C
	    incf	arg3, f
	    return
	    
I_ADDP_NEG  ;// addend is negative, decrement hi-order if no carry occurred
	    btfss	STATUS, C
	    decf	arg3, f
	    return
	    
	    
;//********
_Ch2Chip    ;// arg0 -> channel ID
	    ;// arg0 <- chip no.
	    ;// arg1 <- ch no.
	    
	    movf	arg0, W
	    movwf	arg1
	    
	    rrf		arg1, f
	    rrf		arg1, W
	    andlw	3
	    movwf	arg1
	    
	    movlw	3
	    andwf	arg0, f
	    return
	    

;//********
_ClrChanF   ;// W -> starting F no

	    movwf	FSR
	    movlw	NCHANNELS
	    movwf	stmp0
	    
L_CLRCHANF  clrf	INDF
	    incf	FSR, f
	    decfsz	stmp0, f
	    goto	L_CLRCHANF
	    
	    return
	    
	    
;//********
_Wait	    ;// arg0 -> clocks/2 to wait
	    decfsz	arg0, f
	    goto	_Wait
	    return



;//==========================================================================
;//	    TABLE FUNCTIONS
;//==========================================================================

;//********
	    org		650
_T_Baud	    ;// W -> config sw 0 and 1
	    movwf	ttmp0
	    
	    movlw	6
	    movwf	PCLATH
	    movf	ttmp0, W
	    
	    addwf	PCL, f
	    retlw	D'71'	    ;//	 9600bps
	    retlw	D'35'	    ;// 19200bps
	    retlw	D'17'	    ;// 38400bps
	    retlw	D'11'	    ;// 57600bps

;//********
	    org		660
_T_ChMask   ;// W -> channel ID
	    movwf	ttmp0
	    
	    movlw	6
	    movwf	PCLATH
	    movf	ttmp0, W
	    
	    addwf	PCL, f
	    retlw	0x0E
	    retlw	0x0D
	    retlw	0x0B
	    retlw	0x07

;//********
	    org		670
_Note2Per_L ;// W -> note no (midi)
	    movwf	ttmp0
	    
	    movlw	6
	    movwf	PCLATH
	    movf	ttmp0, W
	    andlw	7F
	    
	    addwf	PCL, f
	    retlw	000
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0FF
	    retlw	0D2
	    retlw	0EE
	    retlw	018
	    retlw	04D
	    retlw	08E
	    retlw	0DA
	    retlw	02F
	    retlw	08F
	    retlw	0F7
	    retlw	068
	    retlw	0E1
	    retlw	061
	    retlw	0E9
	    retlw	077
	    retlw	00C
	    retlw	0A7
	    retlw	047
	    retlw	0ED
	    retlw	098
	    retlw	047
	    retlw	0FC
	    retlw	0B4
	    retlw	070
	    retlw	031
	    retlw	0F4
	    retlw	0BC
	    retlw	086
	    retlw	053
	    retlw	024
	    retlw	0F6
	    retlw	0CC
	    retlw	0A4
	    retlw	07E
	    retlw	05A
	    retlw	038
	    retlw	018
	    retlw	0FA
	    retlw	0DE
	    retlw	0C3
	    retlw	0AA
	    retlw	092
	    retlw	07B
	    retlw	066
	    retlw	052
	    retlw	03F
	    retlw	02D
	    retlw	01C
	    retlw	00C
	    retlw	0FD
	    retlw	0EF
	    retlw	0E1
	    retlw	0D5
	    retlw	0C9
	    retlw	0BE
	    retlw	0B3
	    retlw	0A9
	    retlw	09F
	    retlw	096
	    retlw	08E
	    retlw	086
	    retlw	07F
	    retlw	077
	    retlw	071
	    retlw	06A
	    retlw	064
	    retlw	05F
	    retlw	059
	    retlw	054
	    retlw	050
	    retlw	04B
	    retlw	047
	    retlw	043
	    retlw	03F
	    retlw	03C
	    retlw	038
	    retlw	035
	    retlw	032
	    retlw	02F
	    retlw	02D
	    retlw	02A
	    retlw	028
	    retlw	026
	    retlw	024
	    retlw	022
	    retlw	020
	    retlw	01E
	    retlw	01C
	    retlw	01B
	    retlw	019
	    retlw	018
	    retlw	016
	    retlw	015
	    retlw	014
	    retlw	013
	    retlw	012
	    retlw	011
	    retlw	010
	    retlw	00F
	    retlw	00E
	    retlw	00D
	    retlw	00D
	    retlw	00C
	    retlw	00B
	    retlw	00B
	    retlw	00A
	    retlw	0
	    
	    
;//********
	    org		700
_Note2Per_H ;// W -> note no (midi)
	    movwf	ttmp0
	    
	    movlw	7
	    movwf	PCLATH
	    movf	ttmp0, W
	    andlw	7F
	    
	    addwf	PCL, f
	    retlw	000
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00F
	    retlw	00E
	    retlw	00E
	    retlw	00D
	    retlw	00C
	    retlw	00B
	    retlw	00B
	    retlw	00A
	    retlw	009
	    retlw	009
	    retlw	008
	    retlw	008
	    retlw	007
	    retlw	007
	    retlw	007
	    retlw	006
	    retlw	006
	    retlw	005
	    retlw	005
	    retlw	005
	    retlw	004
	    retlw	004
	    retlw	004
	    retlw	004
	    retlw	003
	    retlw	003
	    retlw	003
	    retlw	003
	    retlw	003
	    retlw	002
	    retlw	002
	    retlw	002
	    retlw	002
	    retlw	002
	    retlw	002
	    retlw	002
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	001
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000
	    retlw	000

;//==========================================================================
;//	    END
;//==========================================================================

	    end
