Friday, 20 July 2012

The Software for Remote Controlled Audio Processor

The software was assembled using Metalink’s ASM51 assembler, which is freely available for download.The software was assembled using Metalink’s ASM51 assembler, which is freely available for download.
;**************************************************************************
;PROGRAM FOR A DIGITALLY CONTROLLED AUDIO PROCESSING SYSTEM
;AUTHOR - KULAJIT SARMA, SEPTEMBER 2002
;ASSEMBLER - METALINK ASSEMBLER ASM51
;**************************************************************************
;CLOCK - 12 MHz CRYSTAL.
;AUDIO PROCESSOR - TDA7439. EEPROM - 24C02
;INPUT FROM A NEC FORMAT REMOTE OR FROM KEYPAD.
;DISPLAY - 2 DIGIT LED DISPLAY & 8-LED PORT.
;**************************************************************************
;
$MOD51          ;include predefined constants
;$NOPRINT
;
;**************************************************************************
;                Variable & Constant Definitions
;**************************************************************************
;
STACKBASE    EQU    034H        ;stack starts at 35H
;
EEPROM_ADDR    EQU    0A0H        ;address of 24CXX on I2C bus
AUDIOPROC_ADDR    EQU    088H        ;address of TDA7439 on I2C bus
;
C_CODE1        EQU    084H        ;CUSTOM CODE for Creative Remote
C_CODE2        EQU    035H        ;CUSTOM CODE' for Creative Remote
;
;    keypad/remote command codes
VOL_UP        EQU    010H        ;REMOTE- VOL +
VOL_DN        EQU    014H        ;REMOTE- VOL -
SEL        EQU    004H        ;REMOTE- MENU
SOURCE        EQU    008H        ;REMOTE- MOUSE
INGAIN        EQU    018H        ;REMOTE- ZOOM
MUT        EQU    00CH        ;REMOTE- MUTE
STDBY        EQU    016H        ;REMOTE- CANCEL
;
ZDATA        EQU    R1        ;data register for 24C01/02 functions
ADDR        EQU    R2        ;address register for 24C01/02 functions
AUDIODATA    EQU    R3        ;Audio parameter values for audio processor
SUBADDR    EQU    R4        ;Audio processor function selection byte
;
SDA        BIT    P3.0        ;i2c serial data
SCL        BIT    P3.1        ;i2c serial clock
IR_IN        BIT    P3.2        ;IR sensor input line, inverted output from sensor
INDICATOR    BIT    P3.4        ;Status indicator LED
POWER        BIT    P3.5        ;STANDBY relay control
DISP_LATCH    BIT    P3.6        ;Latch control of Display port (CD4543B)
MONITOR    BIT    P2.7        ;Power monitor input
;BIT Variables
; Max bit variables available is 16, as general user RAM starts from 022H
;
STOP        BIT    00H        ;stop bit in IR code
RX_BIT        BIT    01H        ;next received bit in IR stream
NEW_COM    BIT    02H        ;new IR command available
NEW_KEY    BIT    03H        ;new key input available
REPEAT        BIT    04H        ;Repeated Remote command (contineous key press)
STANDBY    BIT    05H        ;Standby status
MUTE        BIT    06H        ;Mute status
GAIN_ON    BIT    07H        ;Bit set if gain function selected
;    BYTE Variables
; Max byte variables available is 18, as stack starts at 35H
;
COMMAND    DATA    022H        ;Received IR command
KEY        DATA    023H        ;Keyboard command
CHANNEL    DATA    024H        ;TDA7439 input channel
VOLUME        DATA    025H        ;TDA7439 input attenuation
BASS        DATA    026H        ;TDA7439 bass adj
MIDDLE        DATA    027H        ;TDA7439 voice adj
TREBLE        DATA    028H        ;TDA7439 treble adj
R_ATTN        DATA    029H        ;TDA7439 Right attenuation (volume)
L_ATTN        DATA    02AH        ;TDA7439 Left attenuation (volume)
GAIN1        DATA    02BH        ;TDA7439 input gain of IN1
GAIN2        DATA    02CH        ;TDA7439 input gain of IN2
GAIN3        DATA    02DH        ;TDA7439 input gain of IN3
GAIN4        DATA    02EH        ;TDA7439 input gain of IN4
GAIN        DATA    02FH        ;TDA7439 input gain of active channel
LED_STATUS    DATA    030H        ;currently loaded value in LED port
;LED_STATUS BITS 0-1-2-3-4-5-6-7 = BASS-MIDDLE-TREBLE-ATTN-IN1-IN2-IN3-IN4
T1_COUNT    DATA    031H        ;TIMER1 Counter
;
;**************************************************************************
;
;//////////////////////////////////////////////////////////////////////////
;            PROGRAM STARTS HERE
;//////////////////////////////////////////////////////////////////////////
; RESET VECTOR
;
    ORG    00H            ;locate routine at 00H
RESET:
    JMP    START            ;jump to START
;
;**************************************************************************
;
; INTERRUPTS    ( place interrupt routines at appropriate memory locations )
    ORG    03H            ;external interrupt 0, used by IR Sensor
    JMP    INTR_EX0
    ORG    0BH            ;timer 0 interrupt
    RETI
    ORG    13H            ;External interrupt 1
    RETI
    ORG    1BH            ;timer 1 interrupt
    JMP    INTR_T1
    ORG    23H            ;serial port interrupt
    RETI
    ORG    25H            ;locate beginning of rest of program
;
;**************************************************************************
;
;//////////////////////////////////////////////////////////////////////////
;            DELAY ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;**************************************************************************
; Delay routine to introduce 6 microSec Delay with 12MHz crystal
;**************************************************************************
BITDLY:            ;2cyl (CALL)
    NOP        ;1 Cyl
    NOP        ;1 Cyl
    RET        ;2 Cyl
;
;**************************************************************************
; Delay routine in miliSecond. (Approximate)
; Entry: R6 = (Delay in mS -0.004)/0.501 OR R6 = Delay in mS * 2
; assuming 12MHz clock, 1 Cyl=1 microSec
; delay in inner loop= R7*2 microSec
; delay in outer loop= R6*(3+R7*2) microSec
; delay in call & ret= 4 microSec
; Total delay = R6*(3+R7*2)+4 microSec
; R7=249(F9H)=>Min delay=0.507 miliSec, Max delay = 127.759 mS
; Destroys R6, R7
;*************************************************************************
DELAYMS:            ;2-Cyl
x95:    MOV    R7,#0F9H    ;1-Cyl
    DJNZ    R7,$        ;2-Cyl
    DJNZ    R6,x95        ;2-Cyl
    RET            ;2-Cyl
;
;*************************************************************************
; Delay routine in Seconds. (Approximate)
; Entry: R5 = Delay in Sec * 10
; Destroys R5, R6, R7
;*************************************************************************
DELAYSEC:
x96:    MOV    R6,#200
    ACALL    DELAYMS
    DJNZ    R5,x96
    RET
;*************************************************************************
;
;//////////////////////////////////////////////////////////////////////////
;            END OF DELAY ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            IR DECODING ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;**************************************************************************
; Receives one bit from IR_IN. The received bit is returned in RX_BIT,
; or STOP is set if it is a stop bit. IR_IN should be LOW while calling
; this function. Uses TIMER 0 already set in Mode1, TR0=1 & GATE0=1. EA=0
; DESTROYS PSW->CARRY AND R0
;**************************************************************************
RECEIVE_BIT:
    MOV    TH0,#0F4H    ;TIMER0 RELOAD VALUE, MAX COUNT=65535-62464(F400H)=3071
                ;BIT WILL BE RECOGNISED AS STOP BIT IF TIMER0 OVERFLOWS
                ;SINCE TL0 IS IGNORED, OVERFLOW WILL BE ANYWHERE BETWEEN 2816 & 3071
    CLR    TF0
    CLR    C
    CLR    STOP
    CLR    RX_BIT        ;ASSUME BIT=0
    JNB    IR_IN,$        ;LOOP UNTIL IR_IN GOES HIGH, TIMER0 STARTS AFTER THIS
x60:    JB    TF0,x62        ;STOP BIT IF TIMER OVERFLOWS
    JB    IR_IN,x60        ;LOOP TILL TIMER0 IS RUNNING
                    ;TIMER0 STOPS HERE, CHECK PULSE LENGTH
    MOV    R0,TH0
    CJNE    R0,#0F8H,x61    ;BIT =0 IF COUNT LESS THAN 1024
x61:    JC    x63            ;RX_BIT=0
    SETB    RX_BIT        ;RX_BIT=1
    AJMP    x63
x62:    SETB    STOP
x63:
    RET
;
;******************************************************************************************
;External interrupt 0 handler -> DECODES A NEC FORMAT IR STREAM
;SETS NEW_COM(02H) IF A NEW COMMAND IS AVAILABLE. SHOULD BE CLEARED IF THE COMMAND IS PROCESSED
;THE COMMAND IS STORED IN THE COMMAND(021H) LOCATION IN INTERNAL RAM
;USES TIMER0
;DESTROYS R0,R2,TMOD0
;*****************************************************************************************
INTR_EX0:
    CLR    EX0            ;Disable Int0
    PUSH    ACC
    PUSH    PSW
;EVERY KEYCODE IS TRANSMITTED TWICE. THE FIRST TIME IS REJECTED
;
    JNB    TF0,IRDECODE    ;SECOND & SUBSEQUENT CONTINEOUS TRANSMISSION
    MOV    R2,#0AAH        ;DELAY 85mS TO REJECT THE FIRST TRANSMISSION
DLY85:
    MOV    R0,#0F9H    ;1-Cyl
    DJNZ    R0,$        ;2-Cyl
    DJNZ    R2,DLY85    ;2-Cyl
    AJMP    x72
;
IRDECODE:                ;DECODE INCOMING IR COMMAND
    ORL    TMOD,#01H        ;SET MODE1 FOR TIMER0 (16-BIT TIMER )
    ANL    TMOD,#0F1H        ;GATE0=0, CT=0
    MOV    TH0,#00H        ;<-CHECK NECESSATY????????????????????????????????
    MOV    TL0,#00H        ;<-CHECK NECESSATY????????????????????????????????
    SETB    TR0            ;START TIMER0
    JNB    IR_IN,$        ;LOOP UNTIL IR_IN GOES HIGH, END OF START PULSE
    CLR    TR0            ;STOP TIMER0
;
;*****check length of start pulse**********************************************************
;
    MOV    A,TH0
    CJNE    A,#020H,x64        ;MIN TIMER0 COUNT = 20OOH = 8192D i.e. 8.1ms START PULSE
    AJMP    x66
x64:    JC    x72
    CJNE    A,#025H,x65        ;MAX TIMER0 COUNT = 25FFH = 9727D i.e. 9.7ms START PULSE
    AJMP    x66
x65:    JNC    x72            ;START PULSE LENGTH INVALID
;
x66:                    ;START PULSE IDENTIFIED, CONTINUE
    JB    IR_IN,$        ;LOOP UNTIL IR_IN GOES LOW, SKIP 4.5mS SILENCE
;TODO->AVOID INFINITE LOOP IF ONLY A NOISE START SIGNAL RECEIVED BUT NO STOP BIT
;
    ORL    TMOD,#08H            ;GATE0=1
    SETB    TR0                ;START TIMER0
    ACALL    RECEIVE_BIT            ;RECEIVE THE INCOMING 0,1 OR STOP BIT
    JB    STOP,STOPBIT        ;STOP BIT RECEIVED, REPEAT LAST COMMAND
    MOV    C,RX_BIT
    RLC    A
    MOV    R2,#07H            ;NUMBER OF BITS IN A BYTE, 7 + 1 ALREADY RECEIVED
x67:                        ;RECEIVE CUSTOM CODE
    ACALL    RECEIVE_BIT            ;RECEIVE THE INCOMING 0,1 OR STOP BIT
    JB    STOP,x72            ;STOP BIT RECEIVED, ERROR
    MOV    C,RX_BIT
    RLC    A
    DJNZ    R2,x67
    CJNE    A,#C_CODE1,x72        ;CHECK CUSTOM CODE
    MOV    R2,#08H            ;NUMBER OF BITS IN A BYTE
x68:                        ;RECEIVE CUSTOM CODE'
    ACALL    RECEIVE_BIT            ;RECEIVE THE INCOMING 0,1 OR STOP BIT
    JB    STOP,x72            ;STOP BIT RECEIVED, ERROR
    MOV    C,RX_BIT
    RLC    A
    DJNZ    R2,x68
    CJNE    A,#C_CODE2,x72        ;CHECK CUSTOM CODE'
    MOV    R2,#08H            ;NUMBER OF BITS IN A BYTE
x69:                        ;RECEIVE COMMAND BYTE
    ACALL    RECEIVE_BIT            ;RECEIVE THE INCOMING 0,1 OR STOP BIT
    JB    STOP,x72            ;STOP BIT RECEIVED, ERROR
    MOV    C,RX_BIT
    RLC    A
    DJNZ    R2,x69
    MOV    COMMAND,A            ;MOVE VALUE TO COMMAND BYTE
    MOV    R2,#08H            ;NUMBER OF BITS IN A BYTE
x70:                        ;RECEIVE INVERTED COMMAND BYTE
    ACALL    RECEIVE_BIT            ;RECEIVE THE INCOMING 0,1 OR STOP BIT
    JB    STOP,x72            ;STOP BIT RECEIVED, ERROR
    MOV    C,RX_BIT
    RLC    A
    DJNZ    R2,x70
    CPL    A
    CJNE    A,COMMAND,x72        ;CHECK INVERTED COMMAND BYTE
    ACALL    RECEIVE_BIT            ;RECEIVE STOP BIT
    JNB    STOP,x72            ;ERROR
    AJMP    x71                ;VALID COMMAND
STOPBIT:
    SETB    NEW_COM            ;SHOULD BE CLEARED IF THE COMMAND IS PROCESSED
    SETB    REPEAT            ;Repeated command<- contionous key press
    MOV    R2,#0AAH
    AJMP    DLY85
x71:                        ;NEW VALID COMMAND
    SETB    NEW_COM            ;SHOULD BE CLEARED IF THE COMMAND IS PROCESSED
    CLR    REPEAT
x72:    CLR    TR0                ;STOP TIMER0
    ANL    TMOD,#0F7H            ;GATE0=0
    CLR    A
    MOV    TH0,A
    MOV    TL0,A
    CLR    TF0        ;TIMER0 WILL OVERFLOW EXCEPT FOR SECOND TIME OR
    SETB    TR0        ;CONTINOUS TRANSMISSION
    POP    PSW
    POP    ACC
    SETB    EX0                ;Enable Int0
    RETI                    ;INT0 Return
;
;//////////////////////////////////////////////////////////////////////////
;            END OF IR DECODING ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            KEY INPUT PROCESSING ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;**************************************************************************
; Checks for keypress, sets NEW_KEY & code in KEY if key pressed
; No mulltiple key press, no continous key press
;**************************************************************************
CHECK_KEY:
    MOV    A,P2
    SETB    ACC.7
    CJNE    A,#0FFH,NE_FF_KEY
    RET                ;no keys pressed
NE_FF_KEY:
    PUSH    IE
    CLR    EA            ;disable interrupts
    PUSH    B
    MOV    B,A
    MOV    R6,#064H        ;50mS delay for key debounce
    MOV    A,P2
    SETB    ACC.7
    CJNE    A,B,END_CHECK_KEY
;key debounced, check for keys one by one, multiple key press not allowed
KEY1:
    CJNE    A,#0FEH,KEY2
    MOV    KEY,#VOL_UP
    AJMP    SET_NEW_KEY
KEY2:
    CJNE    A,#0FDH,KEY3
    MOV    KEY,#VOL_DN
    AJMP    SET_NEW_KEY
KEY3:
    CJNE    A,#0FBH,KEY4
    MOV    KEY,#SEL
    AJMP    SET_NEW_KEY
KEY4:
    CJNE    A,#0F7H,KEY5
    MOV    KEY,#SOURCE
    AJMP    SET_NEW_KEY
KEY5:
    CJNE    A,#0EFH,KEY6
    MOV    KEY,#INGAIN
    AJMP    SET_NEW_KEY
KEY6:
    CJNE    A,#0DFH,KEY7
    MOV    KEY,#MUT
    AJMP    SET_NEW_KEY
KEY7:
    CJNE    A,#0BFH,CONT_KEYPRESS
    MOV    KEY,#STDBY
SET_NEW_KEY:                ;set flag as new key available
    SETB    NEW_KEY            ;cleared by the key processing routine
CONT_KEYPRESS:                ;Wait in a loop till key is released
    MOV    A,P2
    SETB    ACC.7
    CJNE    A,#0FFH,CONT_KEYPRESS
    MOV    R6,#064H            ;Key must be released for at least 50mS
    CALL    DELAYMS
    MOV    A,P2
    SETB    ACC.7
    CJNE    A,#0FFH,CONT_KEYPRESS
END_CHECK_KEY:
    POP    B
    POP    IE
    RET
;
;//////////////////////////////////////////////////////////////////////////
;            END OF KEY INPUT PROCESSING ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            TDA7439 AUDIO PROCESSOR COMMUNICATION ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;**************************************************************************
; TDA7439 communication routine with no incremental bus.
; Called with function select byte in SUBADDR(R4) & data in AUDIODATA(R3).
; Returns CY set to indicate that the bus is not available
; or that the addressed device failed to acknowledge.
; Destroys A, R3, R4
;**************************************************************************
SET_AUDIO:
    CLR    C
    CALL    I2C_START
    JC    x56            ; abort if bus not available
;
    MOV    A,#AUDIOPROC_ADDR    ; setup Device address
    CALL    I2C_SHOUT        ; send device address
    JC    x55            ; abort if no acknowledge
;
    MOV    A,SUBADDR        ; send address
    CLR    ACC.4            ; No Incremental Bus
    CALL    I2C_SHOUT
    JC    x55            ; abort if no acknowledge
;
    MOV    A,AUDIODATA        ; get data
    CALL    I2C_SHOUT        ; send data
    JC    x55            ; abort if no acknowledge
;
    CLR    C                ; clear error flag
x55:
    CALL    I2C_STOP
x56:
    RET
;
;*************************************************************************
; Initialize TDA7439.
; Send audio parameter values except R_ATTN & L_ATTN to audio processor
; with incremental bus.
; Destroys ACC, CY, B, R0
;*************************************************************************
INIT_AUDIO:
    CLR    C
    MOV    B,#05H        ; byte count (6-1=5), leave out R_ATTN & L_ATTN
    MOV    R0,#024H        ; RAM pointer
    CALL    I2C_START
    JC    INIT_AUDIO        ; loop till bus available
    MOV    A,#AUDIOPROC_ADDR    ; setup Device address
    CALL    I2C_SHOUT        ; send device address
    JC    INITAUDEND        ; abort if no acknowledge
;
    MOV    A,#00H        ; send subaddress(function select)
    SETB    ACC.4            ; Incremental Bus
    CALL    I2C_SHOUT
    JC    INITAUDEND        ; abort if no acknowledge
INCBUS:
    MOV    A,@R0            ; get audio parameter value
    CALL    I2C_SHOUT        ; send data
    JC    INITAUDEND        ; abort if no acknowledge
    INC    R0            ; increment RAM pointer, subaddress autoincrease
    DJNZ    B,INCBUS        ; setup next audio parameter
    CLR    C            ; clear error flag
INITAUDEND:
    CALL    I2C_STOP
    RET
;
;//////////////////////////////////////////////////////////////////////////
;        END OF TDA7439 AUDIO PROCESSOR COMMUNICATION ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            24C01/02 COMMUNICATION ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;**************************************************************************
; 24Cxx Current Address Read function.
; Returns data in A.
; Returns CY set to indicate that the bus is not available
; or that the addressed device failed to acknowledge.
; Destroys A
;**************************************************************************
READ_CURRENT:
    CALL    I2C_START
    JC    x43            ; abort if bus not available
;
    MOV    A,#EEPROM_addr    ; setup address
    SETB    ACC.0            ; specify read operation
    CALL    I2C_SHOUT        ; send device address
    JC    x42            ; abort if no acknowledge
;
    CALL    I2C_SHIN        ; receive data byte
    CALL    I2C_NAK        ; do not acknowledge byte
    CLR    C            ; clear error flag
x42:
    CALL    I2C_STOP
x43:
    RET
;
;**************************************************************************
; 24C01/02 Random Read function.
; Called with byte address in ADDR
; Returns data in A.
; Returns CY set to indicate that the bus is not available
; or that the addressed device failed to acknowledge.
; Destroys A, R1
;**************************************************************************
READ_RANDOM:
    CALL    I2C_START
    JC    x45            ; abort if bus not available
;
    MOV    A,#EEPROM_addr    ; setup address
    CLR    ACC.0            ; dummy write command to set internal address
    CALL    I2C_SHOUT        ; send device address
    JC    x44            ; abort if no acknowledge
;
    MOV    A,ADDR        ; send address
    CALL    I2C_SHOUT
    JC    x44            ; abort if no acknowledge
;
    CALL    READ_CURRENT    ; Call Current Address Read function.
    AJMP    x45            ; exit
x44:
    CALL    I2C_STOP
x45:
    RET
;
;*************************************************************************
; 24C01/02 Sequential Read function. Initializes audio parameters in RAM
; Read 11 bytes (01H to 0CH) sequentially from 24Cxx and move to
; internal RAM locations 024H to 02FH. Assumes address counter already
; set to 01H on entry (should be precedeed by read at 00H)
; destroys ACC, B, CY, R0
;*************************************************************************
INIT_MEM:
    CALL    I2C_START
    JC    INITMERR        ; abort if bus not available
;
    MOV    B,#0BH        ; byte count (11 bytes)
    MOV    R0,#024H        ; RAM pointer
    MOV    A,#EEPROM_ADDR    ; setup address
    SETB    ACC.0            ; specify read operation
    CALL    I2C_SHOUT        ; send device address
    JC    INITMERR        ; abort if no acknowledge
SEQREAD:
    CALL    I2C_SHIN        ; receive data byte
    MOV    @R0,A            ; move byte to internal RAM
    INC    R0            ; increment RAM pointer
    DJNZ    B,CONTRX
    CALL    I2C_NAK        ; do not acknowledge byte to end transmission
    AJMP    ENDRX
CONTRX:
    CALL    I2C_ACK        ; acknowledge byte to receive more bytes
    AJMP    SEQREAD
ENDRX:
    CLR    C            ; clear error flag
    CALL    I2C_STOP
INITMERR:
    CALL    BITDLY
    RET
;
;**************************************************************************
; 24C01/02 Byte Write function.
; Called with byte address in register ADDR(R2) & data in register ZDATA(R1)
; Does ACK polling till device responds.
; Does not wait for write cycle to complete.
; Returns CY set to indicate that the addressed device failed to acknowledge.
; Destroys A, R1, R2
;**************************************************************************
BYTEWRITE:
    CLR    C
    CALL    BITDLY        ; wait min time between Stop & Start on I2C bus
    CALL    I2C_START
    JC    BYTEWRITE        ; loop until bus available

    MOV    A,#EEPROM_ADDR    ; setup device address & write operation
    CALL    I2C_SHOUT        ; send device address
    JC    NACKBWR        ; loop till device acknowledges (ACK Polling)

    MOV    A,ADDR        ; send address
    CALL    I2C_SHOUT
    JC    ENDBWR

    MOV    A,ZDATA
    CALL    I2C_SHOUT        ; send data
    AJMP    ENDBWR
NACKBWR:
    CALL    I2C_STOP
    CALL    BITDLY        ; wait some time
    AJMP    BYTEWRITE        ;retry
ENDBWR:
    CALL    I2C_STOP
    RET
;
;*************************************************************************
;save standby status & audio parameters to EEPROM
;Writes 12 Bytes from RAM locations 24H to 2FH & one byte for standby + mute bits
;to EEPROM addresses 00H to 0CH using byte write with ack polling.
;Returns CY set to indicate error.
;Destroys ACC, CY, B, R0, R1, R2
;*************************************************************************
SAVE_STATE:
    mov    l_attn,r_attn    ;REMOVE WHEN BALANCE ADJUSTEMENT IS ADDED???????????
    CLR    A
    MOV    C,MUTE
    RLC    A            ; move standby bit into ACC.0
    MOV    C,STANDBY
    RLC    A            ; move standby bit into ACC.0, MUTE to ACC.1
    MOV    ZDATA,A        ; load data byte
    MOV    ADDR,#00H        ; load address
    CALL    BYTEWRITE        ; write byte
    JC    END_SAVE_STATE
    MOV    B,#0BH        ; BYTE COUNT
    MOV    R0,#024H        ; RAM Pointer
BWRLOOP:
    INC    ADDR            ; increment address
    MOV    A,@R0            ; load data
    MOV    ZDATA,A        ; load data
    CALL    BYTEWRITE        ; write byte
    JC    END_SAVE_STATE
    INC    R0            ; increment RAM pointer
    DJNZ    B,BWRLOOP        ; decrement byte counter & loop
END_SAVE_STATE:
    RET                ;END SAVE_STATE
;
;//////////////////////////////////////////////////////////////////////////
;            END OF 24C01/02 COMMUNICATION FUNCTIONS
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            I2C BUS FUNCTIONS
;//////////////////////////////////////////////////////////////////////////
;
;**************************************************************************
; Send START on i2c bus, defined as high-to-low SDA with SCL high.
; Return with SCL, SDA low.
; Returns CY set if bus is not available.
;**************************************************************************
I2C_START:
    SETB    SDA
    SETB    SCL
;
    ; Verify bus available.
    JNB    SDA, x46        ; jump if not high
    JNB    SCL, x46        ; jump if not high
;
    NOP                ; enforce setup delay and cycle delay
    CLR    SDA
    CALL    BITDLY        ; enforce hold delay
    CLR    SCL
    CLR    C            ; clear error flag
    AJMP    x47
x46:
    SETB    C            ; set error flag
x47:
    RET
;
;**************************************************************************
; Send STOP, defined as low-to-high SDA with SCL high.
; SCL expected low on entry. Return with SCL, SDA high.
;**************************************************************************
I2C_STOP:  
    CLR    SDA
    NOP                ; enforce SCL low and data setup
    NOP
    SETB    SCL
    CALL    BITDLY        ; enforce setup delay
    SETB    SDA
    RET
;
;**************************************************************************
; Shift out a byte to the AT24Cxx, most significant bit first.
; SCL, SDA expected low on entry. Return with SCL low.
; Called with data to send in A.
; Returns CY set to indicate failure by slave to acknowledge.
; Destroys A.
;**************************************************************************
I2C_SHOUT:
    PUSH    B
    MOV    B,#08H        ; bit counter
x48:
    RLC    A            ; move bit into CY
    MOV    SDA,C            ; output bit
    NOP                ; enforce SCL low and data setup
    SETB    SCL            ; raise clock
    CALL    BITDLY        ; enforce SCL high
    CLR     SCL            ; drop clock
    DJNZ     B,x48        ; next bit
;
    SETB    SDA            ; release SDA for ACK
    NOP                ; enforce SCL low and tAA
    NOP
    SETB    SCL            ; raise ACK clock
    CALL    BITDLY        ; enforce SCL high
    MOV    C,SDA            ; get ACK bit
    CLR    SCL            ; drop ACK clock
;
    POP    B
    RET
;
;**************************************************************************
; Shift in a byte from the AT24Cxx, most significant bit first.
; SCL expected low on entry. Return with SCL low.
; Returns received data byte in A.
;**************************************************************************
I2C_SHIN:
    SETB    SDA            ; make SDA an input
;
    PUSH    B
    MOV    B,#08H        ; bit count
x49:
    CALL    BITDLY        ; enforce SCL low and data setup
    SETB    SCL            ; raise clock
    CALL    BITDLY        ; enforce SCL high
    MOV    C,SDA            ; input bit
    RLC    A            ; move bit into byte
    CLR    SCL            ; drop clock
    DJNZ    B,x49            ; next bit
;
    POP    B
    RET
;
;**************************************************************************
; Clock out an acknowledge bit (low).
; SCL expected low on entry. Return with SCL, SDA low.
;**************************************************************************
I2C_ACK:
    CLR    SDA            ; ACK bit
    NOP                ; enforce SCL low and data setup
    NOP
    SETB    SCL            ;raise clock
    CALL    BITDLY        ; enforce SCL high
    CLR    SCL            ; drop clock
    RET
;
;**************************************************************************
; Clock out a negative acknowledge bit (high).
; SCL expected low on entry. Return with SCL low, SDA high.
;**************************************************************************
I2C_NAK:
    SETB    SDA            ; NAK bit
    NOP                ; enforce SCL low and data setup
    NOP
    SETB    SCL            ; raise clock
    CALL    BITDLY        ; enforce SCL high
    CLR    SCL            ; drop clock
    RET
;
;//////////////////////////////////////////////////////////////////////////
;            END OF I2C BUS ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            DISPLAY ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;*************************************************************************
;loads LED_STATUS byte in LED port.
;*************************************************************************
LOAD_LED:
    PUSH    ACC
    MOV    A,LED_STATUS
    CPL    A
    MOV    P0,A
    POP    ACC
    RET
;
;**************************************************************************
;INCREMENT FUNCTION DISPLAY. low nibble of LED_STATUS stores function display
;DESTROYS ACC
;**************************************************************************
;**************************************************************************
INC_SELD:
    MOV    A,LED_STATUS
    ANL    A,#0FH        ;Clear high nibble (source display)
    JNZ    NOTVOL
    MOV    B,BASS
    CALL    DISP_AUD
    MOV    A,#01H            ;BASS LED on
    AJMP    LDFUN
NOTVOL:
    JNB    ACC.0,NOTBASS
    MOV    B,MIDDLE
    CALL    DISP_AUD
    MOV    A,#02H            ;MIDDLE LED on
    AJMP    LDFUN  
NOTBASS:
    JNB    ACC.1,NOTMIDDLE
    MOV    B,TREBLE
    CALL    DISP_AUD
    MOV    A,#04H            ;TREBLE LED on
    AJMP    LDFUN  
NOTMIDDLE:
    JNB    ACC.2,NOTTREBLE
    CALL    DISP_ATT
    MOV    A,#08H            ;ATTENUATION LED on
    AJMP    LDFUN  
NOTTREBLE:
    JNB    ACC.3,NOTATTN
    CALL    DISP_VOL
    MOV    A,#00H            ;All LEDs off(Volume)
    AJMP    LDFUN  
LDFUN:
    ANL    LED_STATUS,#0F0H        ;clear low nibble
    ORL    LED_STATUS,A        ;save function selection display
    ACALL    LOAD_LED
NOTATTN:
    RET
;
;**************************************************************************
;LOAD SOURCE DISPLAY. high nibble of LED_STATUS stores source display
;Also updates GAIN byte.
;DESTROYS A,B,CY
;**************************************************************************
LOAD_SRCD:
    MOV    A,CHANNEL
    CJNE    A,#00H,SRC3
    MOV    B,#080H    ;IN4
    MOV    GAIN,GAIN4
    AJMP    LDSRC
SRC3:    CJNE    A,#01H,SRC2
    MOV    B,#040H    ;IN3
    MOV    GAIN,GAIN3
    AJMP    LDSRC
SRC2:    CJNE    A,#02H,SRC1
    MOV    B,#020H    ;IN2
    MOV    GAIN,GAIN2
    AJMP    LDSRC
SRC1:    MOV    B,#010H    ;IN1
    MOV    GAIN,GAIN1
LDSRC:
    MOV    LED_STATUS,B    ;function select = volume & save status to RAM
    ACALL    LOAD_LED
    RET
;
;**************************************************************************
;        BINARY TO BCD CONVERSION ROUTINE
;SEE P313 OF R. S. GAONKAR'S 8085 BOOK FOR ACTUAL LOGIC
;ACCEPTS A BINARY NUMBER BETWEEN 0-99D(63H) IN ACC & CONVERTS TO BCD
;IF ACC>63H(99D) OUTPUTS 00H
;DESTROYS A,CY & B
;**************************************************************************
HEXBCD:
    MOV    B,#00H
    CJNE    A,#64H,N100        ;NOT EQUAL 100D
N100:    JC    LT99
    AJMP    GT99            ;greater than 99D
LT99:                    ;less than 99D, continue
SUB10:            ;repeatedly subtaract 10D from Acc till reminder less than 10D
    CLR    C
    CJNE    A,#0AH,N10        ;NOT EQUAL 10D
N10:    JC    LT10            ;reminder less than 10D
    SUBB    A,#0AH
    INC    B            ;loop counter
    AJMP    SUB10
GT99:    CLR    A            ;output 00 only if greater than 99D
LT10:    SWAP    A            ;A contains low BCD nibble & B contains high BCD nibble      
    ORL    A,B
    SWAP    A
    RET
;
;*************************************************************************
;loads a byte FROM P1 in DISPLAY port.
;*************************************************************************
LOAD_DISP:
    SETB    DISP_LATCH        ;latch the byte in Display latch
    NOP
    CLR    DISP_LATCH
    RET
;
;**************************************************************************
;Displays input gain settings(0 - 15) on LED Display
;destroys A,CY,P1
;**************************************************************************
DISP_GAIN:
    MOV    A,GAIN
    ACALL    HEXBCD        ;convert to BCD
    MOV    P1,A
    ANL    A,#0F0H        ;clear low nibble
    JNZ    LDGAIN
    ORL    P1,#0F0H        ;set illegal BCD to supress leading zero
LDGAIN:
    ACALL    LOAD_DISP
    RET
;
;**************************************************************************
;Displays volume (1-80)settings on LED Display
;Entry: B=setting value & ACC=max value for the setting(4F/2F)
;destroys A,CY,P1,B
;**************************************************************************
DISP_VOL:
    MOV    A,#050H
    MOV    B,R_ATTN
    CLR    C
    SUBB    A,B
    ACALL    HEXBCD
    MOV    P1,A
    ANL    A,#0F0H        ;clear low nibble
    JNZ    LDVOL
    ORL    P1,#0F0H        ;set illegal BCD to supress leading zero
LDVOL:
    ACALL    LOAD_DISP
    RET
;
;**************************************************************************
;Displays attenuation (0-47)settings on LED Display
;destroys A,P1
;**************************************************************************
DISP_ATT:
    MOV    A,VOLUME
    ACALL    HEXBCD
    MOV    P1,A
    ANL    A,#0F0H        ;clear low nibble
    JNZ    LDATT
    ORL    P1,#0F0H        ;set illegal BCD to supress leading zero
LDATT:
    ACALL    LOAD_DISP
    RET
;
;**************************************************************************
;Displays bass/middle/treble settings(07-00-0-7) on LED Display.
;called with bass/middle/treble settings in B register
;destroys A,P1,CY,B
;**************************************************************************
DISP_AUD:
    JB    B.3,GT8
    MOV    A,#07H        ;if settings less than 8H, subtract from 07H
    CLR    C
    SUBB    A,B
    ACALL    HEXBCD        ;do not supress leading zero => CUT
    AJMP    LDDSPL
GT8:                    ;if settings greater than 8H, subtract from 0FH
    MOV    A,#0FH
    CLR    C
    SUBB    A,B
    ACALL    HEXBCD
    ORL    A,#0F0H        ;set illegal BCD to supress leading zero=>BOOST
LDDSPL:
    MOV    P1,A
    ACALL    LOAD_DISP
    RET
;
;//////////////////////////////////////////////////////////////////////////
;            END OF DISPLAY ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            COMMAND & KEY DECODING ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;*************************************************************************
; Bass_Middle_Treble Up, used by DO_VOL_UP routine
;*************************************************************************
B_M_T_UP:
    CJNE    A,#07H,NE7_BMTUP
    MOV    A,#0FH
    RET
NE7_BMTUP:
    JNC    GT7_BMTUP
    INC    A
    RET
GT7_BMTUP:
    CJNE    A,#08H,NE8_BMTUP
    RET            ;Setting already maximum
NE8_BMTUP:
    DEC    A
    RET
;
;*************************************************************************
; Bass_Middle_Treble Down, used by DO_VOL_DN routine
;*************************************************************************
B_M_T_DN:
    JNZ    NE0_BMTDN
    RET                    ;Already minimum, do nothing
NE0_BMTDN:
    JB    ACC.3,GT7_BMTDN
    DEC    A                ;Decrement if less than 7
    RET
GT7_BMTDN:
    CJNE    A,#0FH,LT15_BMTDN
    MOV    A,#07H
    RET
LT15_BMTDN:
    INC    A                ;Increment if less than 15
    RET
;
;*************************************************************************
; Process VOL_UP command
;*************************************************************************
DO_VOL_UP:
    JNB    GAIN_ON,FUN_UP
    CALL    DO_GAIN_UP
    JMP    DONE_VUP
FUN_UP:
    MOV    A,LED_STATUS
    ANL    A,#0FH            ;CLEAR HIGH NIBBLE (LOW NIBBLE = FUNCTION DISPLAY)
CHECK_VOL_UP:
    JNZ    CHECK_BAS_UP        ; Increase VOL IF ACC=0
    MOV    A,R_ATTN            ;PROCESS RIGHT CHANNEL
    JZ    DONE_VUP            ;Attenuation already 0dB, do nothing
    DEC    A                ;Volume UP
    MOV    AUDIODATA,A
    MOV    SUBADDR,#06H
    CALL    SET_AUDIO
    JC    DONE_VUP
    MOV    R_ATTN,AUDIODATA
;
    MOV    A,L_ATTN            ;PROCESS LEFT CHANNEL
    JZ    DONE_VUP            ;Attenuation already 0dB, do nothing
    DEC    A                ;Volume UP
    MOV    AUDIODATA,A
    MOV    SUBADDR,#07H
    CALL    SET_AUDIO
    JC    DONE_VUP
    MOV    L_ATTN,AUDIODATA
;          
    CALL    DISP_VOL            ;UPDATE LED DISPLAY
    AJMP    DONE_VUP
CHECK_BAS_UP:                ; Increment BASS
    CJNE    A,#01H,CHECK_MID_UP
    MOV    A,BASS
    CALL    B_M_T_UP
    MOV    B,A                ;used by DISP_AUD
    MOV    SUBADDR,#03H
    MOV    AUDIODATA,A
    CALL    SET_AUDIO            ;Change audio settings
    JC    DONE_VUP
    MOV    BASS,AUDIODATA        ;Update RAM
    CALL    DISP_AUD            ;B-reg already contains display settings
    AJMP    DONE_VUP
CHECK_MID_UP:                ; Increment MIDDLE
    CJNE    A,#02H,CHECK_TRE_UP
    MOV    A,MIDDLE
    CALL    B_M_T_UP
    MOV    B,A                ;used by DISP_AUD
    MOV    SUBADDR,#04H
    MOV    AUDIODATA,A
    CALL    SET_AUDIO            ;Change audio settings
    JC    DONE_VUP
    MOV    MIDDLE,AUDIODATA        ;Update RAM
    CALL    DISP_AUD            ;B-reg already contains display settings
    AJMP    DONE_VUP
CHECK_TRE_UP:                ; Increment TREBLE
    CJNE    A,#04H,CHECK_ATT_UP
    MOV    A,TREBLE
    CALL    B_M_T_UP
    MOV    B,A                ;used by DISP_AUD
    MOV    SUBADDR,#05H
    MOV    AUDIODATA,A
    CALL    SET_AUDIO            ;Change audio settings
    JC    DONE_VUP
    MOV    TREBLE,AUDIODATA        ;Update RAM
    CALL    DISP_AUD            ;B-reg already contains display settings
    AJMP    DONE_VUP
CHECK_ATT_UP:
    CJNE    A,#08H,DONE_VUP
    MOV    A,VOLUME
    CJNE    A,#02FH,NE2FVOL
    AJMP    DONE_VUP            ;Already max attenuation
NE2FVOL:
    JNC    DONE_VUP            ;Already max attenuation/Mute
    INC    A
    MOV    AUDIODATA,A
    MOV    SUBADDR,#02H
    CALL    SET_AUDIO
    JC    DONE_VUP
;                        ;UPDATE DISPLAY
    MOV    VOLUME,AUDIODATA
    CALL    DISP_ATT
DONE_VUP:
    RET
;
;*************************************************************************
; Process VOL_DN command
;*************************************************************************
DO_VOL_DN:
    JNB    GAIN_ON,FUN_DN
    CALL    DO_GAIN_DN
    JMP    DONE_VUP
FUN_DN:
    MOV    A,LED_STATUS
    ANL    A,#0FH            ;CLEAR HIGH NIBBLE (LOW NIBBLE = FUNCTION DISPLAY)
CHECK_VOL_DN:
    JNZ    CHECK_BAS_DN        ; Decrease VOL IF ACC=0
    MOV    A,R_ATTN            ;PROCESS RIGHT CHANNEL
    CJNE    A,#04FH,RV_DN
    AJMP    DONE_VDN            ;Attenuation already -79dB, do nothing
  RV_DN:
    INC    A                ;Volume DOWN
    MOV    AUDIODATA,A
    MOV    SUBADDR,#06H
    CALL    SET_AUDIO
    JC    DONE_VDN
    MOV    R_ATTN,AUDIODATA        ;Save settings to RAM
;
    MOV    A,L_ATTN            ;PROCESS LEFT CHANNEL
    CJNE    A,#04FH,LV_DN
    AJMP    DONE_VDN            ;Attenuation already -79dB, do nothing
  LV_DN:
    INC    A                ;Volume DOWN
    MOV    AUDIODATA,A
    MOV    SUBADDR,#07H
    CALL    SET_AUDIO
    JC    DONE_VDN
    MOV    L_ATTN,AUDIODATA        ;Save settings to RAM
;                        ;UPDATE LED DISPLAY
    CALL    DISP_VOL
    AJMP    DONE_VDN
CHECK_BAS_DN:                ; Decrement BASS
    CJNE    A,#01H,CHECK_MID_DN
    MOV    A,BASS
    CALL    B_M_T_DN
    MOV    B,A                ;used by DISP_AUD
    MOV    SUBADDR,#03H
    MOV    AUDIODATA,A
    CALL    SET_AUDIO            ;Change audio settings
    JC    DONE_VDN
    MOV    BASS,AUDIODATA        ;Update RAM
    CALL    DISP_AUD            ;B-reg already contains display settings
    AJMP    DONE_VDN
CHECK_MID_DN:                ; Decrement MIDDLE
    CJNE    A,#02H,CHECK_TRE_DN
    MOV    A,MIDDLE
    CALL    B_M_T_DN
    MOV    B,A                ;used by DISP_AUD
    MOV    SUBADDR,#04H
    MOV    AUDIODATA,A
    CALL    SET_AUDIO            ;Change audio settings
    JC    DONE_VDN
    MOV    MIDDLE,AUDIODATA        ;Update RAM
    CALL    DISP_AUD            ;B-reg already contains display settings
    AJMP    DONE_VDN
CHECK_TRE_DN:                ; Decrement TREBLE
    CJNE    A,#04H,CHECK_ATT_DN
    MOV    A,TREBLE
    CALL    B_M_T_DN
    MOV    B,A                ;used by DISP_AUD
    MOV    SUBADDR,#05H
    MOV    AUDIODATA,A
    CALL    SET_AUDIO            ;Change audio settings
    JC    DONE_VDN
    MOV    TREBLE,AUDIODATA        ;Update RAM
    CALL    DISP_AUD            ;B-reg already contains display settings
    AJMP    DONE_VDN
CHECK_ATT_DN:
    CJNE    A,#08H,DONE_VDN
    MOV    A,VOLUME
    JZ    DONE_VDN            ;Already minimum Attenuation
    DEC    A
    MOV    AUDIODATA,A
    MOV    SUBADDR,#02H
    CALL    SET_AUDIO
    JC    DONE_VDN
;                        ;UPDATE DISPLAY
    MOV    VOLUME,AUDIODATA
    CALL    DISP_ATT
DONE_VDN:
    RET
;
;*************************************************************************
;        Increment Source
;Destroys A,CY
;*************************************************************************
INC_SOURCE:
    MOV    A,CHANNEL
    INC    A
    JNB    ACC.2,ST_SRC
    CLR    A
ST_SRC:                    ;Store source
    MOV    SUBADDR,#00H        ;R4=TDA7439 FUNCTION SELECTION BYTE
    MOV    AUDIODATA,A            ;R3=INPUT CHANNEL
    CALL    SET_AUDIO            ;TDA7439 change channel
    JC    SRC_ERR
    MOV    CHANNEL,AUDIODATA        ;STORE IN RAM
    CALL    LOAD_SRCD            ;update display & GAIN byte
    MOV    SUBADDR,#01H        ;R4=TDA7439 FUNCTION SELECTION BYTE
    MOV    AUDIODATA,GAIN        ;R3=INPUT GAIN
    CALL    SET_AUDIO            ;TDA7439 change gain settings
SRC_ERR:
    RET
;
;*************************************************************************
;    PROCESS GAIN KEY (TOGGLE)
;*************************************************************************
DO_GAIN:
    JB    GAIN_ON,OFF_GAIN
    SETB    GAIN_ON
    ANL    LED_STATUS,#0F0H
    CALL    LOAD_LED
    CALL    DISP_GAIN
    RET
OFF_GAIN:
    CLR    GAIN_ON
    CALL    DISP_VOL
    CALL    LOAD_LED
    RET
;
;*************************************************************************
;    PROCESS GAIN UP
;*************************************************************************
DO_GAIN_UP:
    MOV    A,GAIN
    CJNE    A,#0FH,GAIN_INC
    RET                ;Already maximum
GAIN_INC:
    INC    A
    MOV    AUDIODATA,A
    MOV    SUBADDR,#01H
    CALL    SET_AUDIO
    JC    END_GAIN_UP
    MOV    GAIN,AUDIODATA
    CALL    UPDATE_GAIN
    CALL    DISP_GAIN
END_GAIN_UP:
    RET
;
;*************************************************************************
;    PROCESS GAIN DN
;*************************************************************************
DO_GAIN_DN:
    MOV    A,GAIN
    JZ    END_GAIN_DN        ;Already minimum
    DEC    A
    MOV    AUDIODATA,A
    MOV    SUBADDR,#01H
    CALL    SET_AUDIO
    JC    END_GAIN_DN
    MOV    GAIN,AUDIODATA
    CALL    UPDATE_GAIN
    CALL    DISP_GAIN
END_GAIN_DN:
    RET
;
;*************************************************************************
;    MOVE GAIN TO GAIN1/GAIN2/GAIN3/GAIN4 depending on active channel
;*************************************************************************
UPDATE_GAIN:
    MOV    A,LED_STATUS
    ANL    A,#0F0H
    CJNE    A,#010H,CH2
    MOV    GAIN1,GAIN
    RET
CH2:    CJNE    A,#020H,CH3
    MOV    GAIN2,GAIN
    RET
CH3:    CJNE    A,#040H,CH4
    MOV    GAIN3,GAIN
    RET
CH4:    MOV    GAIN4,GAIN
    RET
;
;*************************************************************************
;    TOGGLE STANDBY STATUS
;*************************************************************************
TOGGLE_STDBY:
    JB    STANDBY,STANDBY_OFF
    CALL    SET_MUTE_ON
    SETB    STANDBY
    CALL    SAVE_STATE
    MOV    R5,#05H            ;0.5 Sec Delay
    CALL    DELAYSEC
    MOV    P0,#0FFH            ;Turn off LEDs
    SETB    POWER                ;Active low
    CLR    INDICATOR            ;Turn on Command Indicator LED
    RET
STANDBY_OFF:
    CLR    STANDBY
    CLR    POWER
    CALL    LOAD_LED
    SETB    INDICATOR            ;Turn off Command Indicator LED
    JNB    MUTE,MUTE_OFF
    MOV    P1,#00H
    CALL    LOAD_DISP
    RET
   MUTE_OFF:
    MOV    R5,#0AH            ;1 Sec Delay
    CALL    DELAYSEC
    CALL    SET_MUTE_OFF
    RET
;
;*************************************************************************
;    TOGGLE MUTE STATUS
;*************************************************************************
DO_MUTE:
    JB    MUTE,DO_MUTE_OFF        ;TURN OFF MUTE
    CALL    SET_MUTE_ON
    JC    END_MUTE
    SETB    MUTE
    MOV    P1,#00H
    CALL    LOAD_DISP
    RET
DO_MUTE_OFF:
    CALL    SET_MUTE_OFF
    JC    END_MUTE
    CLR    MUTE
    CALL    DISP_VOL
END_MUTE:
    RET
;
;*************************************************************************
SET_MUTE_ON:
    MOV    AUDIODATA,#078H        ;MUTE
    MOV    SUBADDR,#06H        ;RIGHT CHANNEL
    CALL    SET_AUDIO
    JC    END_MUTE_ON
    MOV    SUBADDR,#07H        ;LEFT CHANNEL
    CALL    SET_AUDIO
END_MUTE_ON:
    RET
;
;*************************************************************************
SET_MUTE_OFF:
    MOV    AUDIODATA,R_ATTN        ;RIGHT CHANNEL VOLUME
    MOV    SUBADDR,#06H        ;RIGHT CHANNEL
    CALL    SET_AUDIO
    JC    END_MUTE_OFF
    MOV    AUDIODATA,L_ATTN        ;LEFT CHANNEL VOLUME
    MOV    SUBADDR,#07H        ;LEFT CHANNEL
    CALL    SET_AUDIO
    JC    END_MUTE_OFF
    MOV    AUDIODATA,VOLUME        ;ATTNEUATION
    MOV    SUBADDR,#02H
    CALL    SET_AUDIO
END_MUTE_OFF:
    RET
;
;*************************************************************************
;        IMPLEMENTS REMOTE COMMANDS
;*************************************************************************
DO_COM:
    MOV    R0,COMMAND
    CALL    DO_COMMAND
    CLR    NEW_COM
    RET
;
;*************************************************************************
;        IMPLEMENTS KEYPAD COMMANDS
;*************************************************************************
DO_KEY:  
    MOV    R0,KEY
    CALL    DO_COMMAND
    CLR    NEW_KEY
    RET
;
;*************************************************************************
;        IMPLEMENTS KEYPAD/REMOTE COMMANDS
;*************************************************************************
DO_COMMAND:
CHECK_STANDBY:                ;check if STANDBY pressed
    CJNE    R0,#STDBY,CHECK_V_UP
    JB    REPEAT,END_COMMAND    ;No contineous-key-press
    CALL    OFF_GAIN
    CALL    TOGGLE_STDBY
    AJMP    END_COMMAND
CHECK_V_UP:                    ;check if VOL_UP pressed
    JB    STANDBY,END_COMMAND    ;If in standby do nothing
    CJNE    R0,#VOL_UP,CHECK_V_DN
    CALL    DO_VOL_UP
    AJMP    END_COMMAND
CHECK_V_DN:                    ;check if VOL_DN pressed
    CJNE    R0,#VOL_DN,CHECK_SEL
    CALL    DO_VOL_DN
    AJMP    END_COMMAND
CHECK_SEL:                    ;check if SEL pressed
    CJNE    R0,#SEL,CHECK_SOURCE
    JB    REPEAT,END_COMMAND
    CALL    OFF_GAIN
    CALL    INC_SELD            ;Increment function selection
    AJMP    END_COMMAND
CHECK_SOURCE:                ;check if SOURCE pressed
    CJNE    R0,#SOURCE,CHECK_GAIN
    JB    REPEAT,END_COMMAND
    CALL    OFF_GAIN
    CALL    INC_SOURCE
    AJMP    END_COMMAND
CHECK_GAIN:                    ;check if GAIN pressed
    CJNE    R0,#INGAIN,CHECK_MUTE
    JB    REPEAT,END_COMMAND
    CALL    DO_GAIN
    AJMP    END_COMMAND
CHECK_MUTE:                    ;check if MUTE pressed
    CJNE    R0,#MUT,END_COMMAND
    JB    REPEAT,END_COMMAND
    CALL    OFF_GAIN
    CALL    DO_MUTE
END_COMMAND:
    CALL    INIT_TIMER1
    RET
;
;*************************************************************************
INIT_TIMER1:
    CLR    TR1
    MOV    T1_COUNT,#01H
    MOV    TH1,#00H
    MOV    TL1,#00H
    SETB    TR1
    CLR    INDICATOR            ;Turn on INDICATOR LED
    RET
;
;//////////////////////////////////////////////////////////////////////////
;            END OF COMMAND & KEY DECODING ROUTINES
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            TIMER1 INTERRUPT HANDLER ROUTINE
; Volume function (default) selected if T1 is running for 4Sec+.
; Keeps source display flashing if GAIN_ON is set.
;//////////////////////////////////////////////////////////////////////////
INTR_T1:
    PUSH    IE
    CLR    EA            ;disable interrupts
    CLR    TR1            ;stop timer temporarily
    PUSH    ACC
    PUSH    PSW
    MOV    A,T1_COUNT
    CJNE    A,#066H,NE66T1
    MOV    T1_COUNT,#01H
    ANL    LED_STATUS,#0F0H        ;Function select = Volume
    CLR    GAIN_ON
    CALL    DISP_VOL
    CALL    LOAD_LED
    JNB    STANDBY,END_INT_T1
    MOV    P0,#0FFH
    AJMP    END_INT_T1
NE66T1:
    MOV    TH1,#015H        ;T1 reload value =65535-60000=5535D=159FH
    MOV    TL1,#09FH        ;T1 overflow every 60mS
    INC    T1_COUNT
    SETB    TR1
    CJNE    A,#01H,NE01T1
    CLR    INDICATOR        ;Turn on Command Indicator LED
    AJMP    FLASH_DISP
NE01T1:
    JB    STANDBY,FLASH_DISP
    SETB    INDICATOR        ;Turn off Command Indicator LED
FLASH_DISP:
    JNB    GAIN_ON,END_INT_T1
    ANL    A,#07H        ;Clear A except last 3 bits
    JNZ    MULTIPLE_4
    MOV    P0,#0FFH        ;Clear display if multiple of 8
    AJMP    END_INT_T1
MULTIPLE_4:
    CLR    ACC.2
    JNZ    END_INT_T1
    CALL    LOAD_LED        ;load led display if multiple of 4
END_INT_T1:
    POP    PSW
    POP    ACC
    POP    IE
    RETI
;
;//////////////////////////////////////////////////////////////////////////
;            END OF TIMER1 INTERRUPT HANDLER ROUTINE
;//////////////////////////////////////////////////////////////////////////
;
;//////////////////////////////////////////////////////////////////////////
;            MAIN ENTRY ROUTINE
;//////////////////////////////////////////////////////////////////////////
;
START:
;*************************************************************************
;        REGISTER INITIALIZATION
;*************************************************************************
    MOV   SP,#STACKBASE        ;stack base address
    CLR    A
    MOV   TCON,A
    MOV   PSW,A           
    MOV   IE,A                ;Disable Interrupts
    MOV    T1_COUNT,A
    MOV   TMOD,#011H            ;T1 & T0 IN 16-bit MODE1, GATEx=0 & CTx=0
    CLR    TR1
    SETB    TR0                ;START TIMER TO REJECT FIRST TRANSMISSION
    SETB    SDA                ;I2C Initialize
    SETB    SCL
    SETB    POWER                ;Standby on (active low)
    SETB    MONITOR            ;Power monitor as input(active low)
    CLR    REPEAT
    CLR    NEW_COM
    CLR    NEW_KEY
    CLR    DISP_LATCH
    CLR    GAIN_ON
;
INIT:                    ;INITIALIZATION STARTS HERE
    CLR    C
;*************************************************************************
;        STANDBY & MUTE INITIALIZATION
;*************************************************************************
    CLR    A
    MOV    ADDR,#00H            ;READ STANDBY STATUS
    ACALL    READ_RANDOM
    JC    INIT                ;RETRY
    RRC    A                ;move LSB into CY bit
    MOV    STANDBY,C            ;store standby status in RAM
    RRC    A
    MOV    MUTE,C            ;store mute status in RAM
;*************************************************************************
;        AUDIO PARAMETERS INITIALIZATION
;*************************************************************************
;
; Read 8 bytes (01H to 0CH) sequentially from 24Cxx and move to
; internal RAM locations 024H to 02FH
;*************************************************************************
    CALL    INIT_MEM
    JC    INIT
;
;*************************************************************************
;Validity check of memory data bytes
;*************************************************************************
VAL_CH:    MOV    A,CHANNEL
        CJNE    A,#04H,NE04V
NE04V:    JC    VAL_G1
        MOV    CHANNEL,#03H    ;Select IN1
VAL_G1:    MOV    A,GAIN1
        CJNE    A,#010H,NE10G1
NE10G1:    JC    VAL_G2
        MOV    GAIN1,#00H        ;GAIN1 = 0
VAL_G2:    MOV    A,GAIN2
        CJNE    A,#010H,NE10G2
NE10G2:    JC    VAL_G3
        MOV    GAIN2,#00H        ;GAIN2 = 0
VAL_G3:    MOV    A,GAIN3
        CJNE    A,#010H,NE10G3
NE10G3:    JC    VAL_G4
        MOV    GAIN3,#00H        ;GAIN3 = 0
VAL_G4:    MOV    A,GAIN4
        CJNE    A,#010H,NE10G4
NE10G4:    JC    VAL_ATT
        MOV    GAIN4,#00H        ;GAIN4 = 0
VAL_ATT:    MOV    A,VOLUME
        CJNE    A,#030H,NE30ATT
NE30ATT:    JC    VAL_BAS
        MOV    VOLUME,#00H        ;ATTN = 0
VAL_BAS:    MOV    A,BASS
        CJNE    A,#010H,NE10BAS
NE10BAS:    JC    VAL_MID
        MOV    BASS,#07H        ;BASS = 0
VAL_MID:    MOV    A,MIDDLE
        CJNE    A,#010H,NE10MID
NE10MID:    JC    VAL_TRE
        MOV    MIDDLE,#07H        ;VOICE = 0
VAL_TRE:    MOV    A,TREBLE
        CJNE    A,#010H,NE10TRE
NE10TRE:    JC    VAL_VOL
        MOV    TREBLE,#07H        ;TRE = 0
VAL_VOL:    MOV    A,R_ATTN
        CJNE    A,#050H,NE50VOL
NE50VOL:    JC    VAL_LEFT
        MOV    R_ATTN,#04FH    ;VOL = 0
;
VAL_LEFT:    MOV    L_ATTN,R_ATTN    ;Remove if balance adj addeed
;  
;**************************************************************************
;initialize LED & Display port.
;**************************************************************************
    CALL    LOAD_SRCD            ;display input channel & function selected->volume
    CALL    DISP_VOL            ;Display volume settings
    JNB    STANDBY,AUDIO_INIT
    MOV    P0,#0FFH            ;Turn off all LEDs
    CLR    INDICATOR            ;Turn on Command Indicator/Standby LED
    JNB    MUTE,AUDIO_INIT
    MOV    P1,#00H            ;Display 00 if Mute on
    CALL    LOAD_DISP
;*************************************************************************
; Send audio parameter values to audio processor with incremental bus
; except R & L Volume
;*************************************************************************
AUDIO_INIT:
    CALL    BITDLY
    CALL    INIT_AUDIO
    JC    AUDIO_INIT
;
    MOV    C,STANDBY    ;<- POWER SHOULD BE ACTIVATED ONLY AFTER AUDIO SET
    MOV    POWER,C
    MOV    R5,#014H
    CALL    DELAYSEC        ;2SEC Delay
;
    JB    MUTE,GO_EVENTS
    JB    STANDBY,GO_EVENTS
    CALL    SET_MUTE_OFF    ;Initialize volume
GO_EVENTS:
;*************************************************************************
;###############    WAIT-FOR-EVENTS LOOP    ################################
;waits for three events -> power off, key press & remote rx
;*************************************************************************
    SETB    EX0                ;External Int0 for IR Sensor
    SETB    ET1                ;Timer1 interrupt
    SETB    EA                ;Enable Interrupts
;
LOOP:  
    JB    MONITOR,POWER_OFF        ;If MONITOR high, go to POWER_OFF (active low)
    CALL    CHECK_KEY
    JB    NEW_KEY,GO_KEY
    JB    NEW_COM,GO_COM
    AJMP    LOOP
GO_KEY:  
    CLR    EX0                ; disable Int0
    CALL    DO_KEY            ; Process Key
    SETB    EX0                ; Enable Int0
    AJMP    LOOP
GO_COM:  
    CLR    EX0                ; disable Int0
    CALL    DO_COM            ; Process Command
    SETB    EX0                ; Enable Int0
    AJMP    LOOP
;
;*************************************************************************
;##############    END OF WAIT-FOR-EVENTS LOOP    ##########################
;*************************************************************************
;
;*************************************************************************
;            POWER OFF SEQUENCE
;*************************************************************************
POWER_OFF:
    CLR    EA            ; DISABLE INTERRUPTS
    JB    STANDBY,CONT_OFF    ; no need to save settings if already in standby
    CALL    SAVE_STATE        ; save settings to EEPROM
    MOV    R5,#05H
    CALL    DELAYSEC        ;0.5 Sec delay
    JNB    MONITOR,GO_EVENTS
;
;**************************************************************************
;Settings saved on EEPROM. MUTE audio & turn off standby relay
;**************************************************************************
CONT_OFF:
    CALL    SET_MUTE_ON
    SETB    POWER            ;turn off relay (active low)
    MOV    P0,#0FFH        ;turn off LEDs
    JB    MONITOR,$        ;loop till power goes down or comes on
    JMP    RESET            ;reset device
;**************************************************************************
;
;########################   THE END   #####################################
END


No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...