; Replacement for Mostek MK3870 with 14246 ROM mask as used in Philips 22AH798 and others from 1979 ; Needs custom 28-pin to 40-pin adapter ; This project is dedicated to my brother Warner who tragically took his life on 4 May 2021. I blame Mark Rutte. -Tjerk ; GPL Copyleft 2021 pic@polonai.se ; 211110 v0.00 Start project, "Hello World" PoC (tESt) ; 211111 v0.01 PLL PoC (98.0 MHz) ; 211114 v0.02 Read Rotary Encoder (RB5:4) ; 211115 v0.03 DATA riedels universalized (LOADPLL) ; 211115 v0.04 FREQ to frequency display (FREQDISPLAY) ; 211118 v0.05 LOADSYS universalized, STBY, Time display ; 211123 v0.06 Keyboardscanner: TIME function (set min, hour with tuning knob) ; 211124 v0.07 System function select (FM, TAPE, PU) ; 211124 v0.08 Startup splash screen, save/read state (EEPROM) ; 211125 v0.09 Memory tuning (store/read), MUTE behavior ; 211128 v0.10 MW, LW receive (PLL+function) ; 211128 v0.11 AM & FM IF offset (diodes) ; 211201 v1.00 Release ; 211202 v1.01 MUTE during bandswitching and solid dot when time not adjusted after powerup ; 211206 v1.02 IF offset also in band edges LIST P=18F2420, F=INHX32 INCLUDE <P18F2420.INC> ; Micro-controller oscillator CONFIG OSC=ECIO6, IESO=OFF, LPT1OSC=OFF ; Micro-controller configuration CONFIG PWRT=ON, MCLRE=ON, PBADEN=OFF, CCP2MX=PORTC, STVREN=OFF ; Micro-contoller monitoring CONFIG WDT=OFF, WDTPS=512, FCMEN=OFF, BOREN=OFF, BORV=0 ; Programming/Debugging CONFIG DEBUG=OFF, XINST=OFF, LVP=OFF ; Code Protection CONFIG CP0=OFF, CP1=OFF, CPB=OFF, CPD=OFF ; Write Protection CONFIG WRT0=OFF, WRT1=OFF, WRTC=OFF, WRTB=OFF, WRTD=OFF ; Table Read Protection CONFIG EBTR0=OFF, EBTR1=OFF, EBTRB=OFF ; Registers FLAGS EQU 0x00 ; Flags FLAGZ EQU 0x01 ; More flags ROTCUR EQU 0x03 ; Current value of rotary encoder ROTPREV EQU 0x04 ; Previous value of rotary encoder FREQHI EQU 0x05 ; PLL Frequency High Byte FREQLO EQU 0x06 ; PLL Frequency Low Byte DISPHI EQU 0x07 ; 3-digit value for BCD conversion High Byte DISPLO EQU 0x08 ; 3-digit value for BCD conversion Low Byte D0 EQU 0x09 ; LED1704 (digit 1, LSB) D1 EQU 0x0A ; LED1703 (digit 2) D2 EQU 0x0B ; LED1702 (digit 3) D3 EQU 0x0C ; LED1701+1700 (digits 4 and 5) TEMP EQU 0x0D DATABUF EQU 0x0E ; Buffer used for shifting data out SYS EQU 0x0F ; System state/select MEM EQU 0x10 ; Memory pointer BCD, 0x0A is frequency at last switch to standby TICKS EQU 0x11 ; Timer0 interrupt counter (10 per second) SECS EQU 0x12 ; Seconds for TIME function (not displayed) MINS EQU 0x13 ; Minutes for TIME function TMINS EQU 0x14 ; 10 Minutes for TIME function HOURS EQU 0x15 ; Hours for TIME function THOURS EQU 0x16 ; 10 Hours for TIME function STBY_SYS EQU 0x17 ; Saved system state in STBY COUNTDOWN EQU 0x18 ; Countdown counter for briefly displaying stuff SAVESTATECTR EQU 0x19 ; Counter for testing for changed state every 25.6 s SAVEDSTATE EQU 0x1A ; Previous FREQLO value used to detect changed state MEMCTR EQU 0x1B ; Timeout counter for STORE button MEMSAV EQU 0x1C ; Saved memory value during store MUTECTR EQU 0x1D ; Mute counter for band edges and memory tuning FM_IF100 EQU 0x1F ; FM IF offset value low byte >=100 MHz FM_IF90 EQU 0x20 ; FM IF offset value low byte >=90 MHz FM_IF80 EQU 0x21 ; FM IF offset value low byte >=80 MHz FM_BE_HI EQU 0x22 ; FM High Band Edge (low byte only) FM_BE_LO EQU 0x23 ; FM Low Band Edge (low byte only) MW_BE_HI EQU 0x24 ; MW High Band Edge (low byte only) MW_BE_LO EQU 0x25 ; MW Low Band Edge (low byte only) LW_BE_HI EQU 0x26 ; LW High Band Edge (low byte only) LW_BE_LO EQU 0x27 ; LW Low Band Edge (low byte only) AM_IFLO EQU 0x28 ; Low byte of AM IF adjustment FMHISAV EQU 0x29 ; FM Frequency high byte saved when changing to MW or LW FMLOSAV EQU 0x2A ; FM Frequency low byte saved when changing to MW or LW MWHISAV EQU 0x2B ; MW Frequency high byte saved when changing to FM or LW MWLOSAV EQU 0x2C ; MW Frequency low byte saved when changing to FM or LW LWHISAV EQU 0x2D ; LW Frequency high byte saved when changing to FM or MW LWLOSAV EQU 0x2E ; LW Frequency low byte saved when changing to FM or MW ; Defines #define CLB PORTA,0 ; MK Pin 10 PIC Pin 2 OUT Clock #define DATA PORTA,1 ; MK Pin 11 PIC Pin 3 OUT Data #define DLEN1 PORTA,2 ; MK Pin 12 PIC Pin 4 OUT Enable for IC6701 #define DLEN0 PORTA,3 ; MK Pin 9 PIC Pin 5 OUT Enable for IC6700 and IC6601 (!) #define STORE PORTA,4 ; MK Pin 19 PIC Pin 6 IN Memory store button (external pullup) #define STBY PORTA,5 ; MK Pin 15 PIC Pin 7 IN Standby button (external pullup) #define IF PORTA,6 ; MK Pin 6 PIC Pin 10 IN Select IF Filter center frequency (external pullup) ; PORTA,7 ; MK Pin 2 PIC Pin 9 External Clock (ECIO mode) #define EXT_INT PORTB,0 ; MK Pin 38 PIC Pin 21 IN Remote decoder interrupt (not implemented) #define KB1 PORTB,1 ; MK Pin 5 PIC Pin 22 IN Keyboard scanner read line #define KB2 PORTB,2 ; MK Pin 4 PIC Pin 23 IN Keyboard scanner read line #define KB3 PORTB,3 ; MK Pin 3 PIC Pin 24 IN Keyboard scanner read line #define UP PORTB,4 ; MK Pin 16 PIC Pin 25 IN Up signal from tuning knob #define DN PORTB,5 ; MK Pin 17 PIC Pin 26 IN Down signal from tuning knob #define MUTE PORTB,6 ; MK Pin 26 PIC Pin 27 OUT Used during memory tuning (active LOW) #define TETS PORTB,7 ; MK Pin - PIC Pin 28 OUT TETS (sic) port for debugging (GRN LED) #define P10 PORTC,0 ; MK Pin 37 PIC Pin 11 OUT Keyboard scanner drive line #define P11 PORTC,1 ; MK Pin 36 PIC Pin 12 OUT Keyboard scanner drive line #define P12 PORTC,2 ; MK Pin 35 PIC Pin 13 OUT Keyboard scanner drive line #define P13 PORTC,3 ; MK Pin 34 PIC Pin 14 OUT Keyboard scanner drive line #define P14 PORTC,4 ; MK Pin 22 PIC Pin 15 OUT Keyboard scanner drive line #define P15 PORTC,5 ; MK Pin 23 PIC Pin 16 OUT Keyboard scanner drive line #define P16 PORTC,6 ; MK Pin 24 PIC Pin 17 OUT Keyboard scanner drive line #define P17 PORTC,7 ; MK Pin 25 PIC Pin 18 OUT Keyboard scanner drive line ; MCLR MK Pin 39 PIC Pin 1 #define SYS_ON FLAGS,0 ; System is ON, STBY system state is restored #define TIME_ON FLAGS,1 ; System is ON but shows time #define KEYPRESS FLAGS,2 ; Key pressed, cleared when exiting Timer0 interrupt handler #define HELD FLAGS,3 ; Key held, cleared when exiting Timer0 interrupt handler and no key is pressed #define ShowFreq FLAGS,4 ; Used for switching between time and frequency displays to ensure correct LEDs are lit #define SETMIN FLAGS,5 ; SET + MIN keys pressed #define SETHOUR FLAGS,6 ; SET + HOURS keys pressed #define SYSCHG FLAGS,7 ; System function change, used for one second function splash screen #define KILLRBIE FLAGZ,0 ; System function active that does not need rotary encoder #define Splash1 FLAGZ,1 ; Show startup splash screen POLO #define Splash2 FLAGZ,2 ; Show startup splash screen nAi #define Splash3 FLAGZ,3 ; Show startup splash screen .SE #define Splash FLAGZ,4 ; Show startup splash message #define MEMSCAN FLAGZ,5 ; Set when STORE is pressed #define AM_MHZ FLAGZ,6 ; Show "1" on LED5 #define TIME_UNSET FLAGZ,7 ; Don't flash dot in time display to show time not set #define FM FREQHI,5 ; Used to set REF1 to 0 in LOADPLL in MW and LW #define SYS_TIME SYS,1 ; TIME system function #define SYS_FM SYS,2 ; FM system function #define SYS_MW SYS,3 ; MW system function #define SYS_LW SYS,4 ; LW system function #define SYS_PU SYS,5 ; PHONO system function #define SYS_Relay SYS,6 ; Relay system function, set when not STBY #define SYS_TAPE SYS,7 ; TAPE function ; Reset program location vector ORG 0x0000 BRA INIT ; Interupt program location vector, high prio (default because RCON:IPEN not set - compatability mode) ORG 0x0008 BTFSC INTCON,TMR0IF ; Timer0 overflow? BRA TESTSTATE ; Yes, test the State Machine BRA ROTTEST ; Decode the rotary encoder (aka pulser) RETFIE ; There are no other interrupts but just in case ; Interupt program location vector, low prio (not used because RCON:IPEN not set) ORG 0x0018 RETFIE ORG 0x001A BCD7SEG ; BCD to 7-segment LED display table ADDWF PCL RETLW 0xC0 ; Return segment code for 0 RETLW 0xF9 ; Return segment code for 1 RETLW 0xA4 ; Return segment code for 2 RETLW 0xB0 ; Return segment code for 3 RETLW 0x99 ; Return segment code for 4 RETLW 0x92 ; Return segment code for 5 RETLW 0x82 ; Return segment code for 6 RETLW 0xF8 ; Return segment code for 7 RETLW 0x80 ; Return segment code for 8 RETLW 0x90 ; Return segment code for 9 RETLW 0xFF ; Return segment code for <blank> RETLW 0xBF ; Return segment code for - RETLW 0xBF ; Return segment code for - RETLW 0xBF ; Return segment code for - RETLW 0xBF ; Return segment code for - RETLW 0xBF ; Return segment code for - ; Initialize micro INIT CLRF INTCON CLRF STATUS MOVLW 0x07 ; Switch off comparators MOVWF CMCON MOVLW 0x70 MOVWF OSCCON ; 8 MHz oscillator MOVLW 0x0F ; Make all pins digital. MOVWF ADCON1 CLRF ADCON0 ; Switch off A/D pins, all pins digital. CLRF INTCON2 ; Weak pullups. Set PORTB as a low pritority interupt CLRF IPR1 ; Set all interupt priorities to low priority. MOVLW B'10000000' MOVWF T0CON ; Timer0 16 bit, prescaler :2, 100 ms period MOVLW B'01110000' ; PORTA<6:4> inputs MOVWF TRISA MOVLW B'00111111' ; PORTB<7:6> outputs MOVWF TRISB CLRF TRISC ; PORTC all outputs CLRF PORTA CLRF PORTB CLRF PORTC CLRF FLAGS CLRF FLAGZ MOVLW 0x00 CALL EEPROM_READ CALL LOADPLL CALL LOADSYS MOVLW 0x0A ; <blank> MOVWF MEM ; Memory display (LED1706) MOVLW 0x02 MOVWF THOURS MOVLW 0x03 MOVWF HOURS MOVLW 0x04 MOVWF TMINS MOVLW 0x05 MOVWF MINS ; Preload time 23:45 MOVLW 0x2A MOVWF FMHISAV MOVLW 0x76 ; Preload FM 98.00 MHz MOVWF FMLOSAV MOVLW 0x0D MOVWF MWHISAV MOVLW 0xF0 ; Preload MW 1332 kHz MOVWF MWLOSAV MOVLW 0x05 MOVWF LWHISAV MOVLW 0x14 ; Preload LW 198 kHz MOVWF LWLOSAV CALL ReadDiodes BSF SYS_ON ; Make sure receiver is actually on and not in standby! BSF INTCON,TMR0IE ; Enable Timer0 interrupt BSF INTCON,GIE BSF Splash ; Enable splash screen message BSF Splash1 ; Show "POLO" BSF TIME_UNSET ; Fast flash dot in time display until time is set MOVLW 0x05 MOVWF COUNTDOWN ; Show for 0.5 s MOVLW B'10001100' ; Show "P" MOVWF D3 MOVLW B'11000000' ; Show "O" MOVWF D2 MOVWF D0 MOVLW B'11000111' ; Show "L" MOVWF D1 CALL UpdateDISP BRA $ ; Run forever ; Interrupt routines ROTTEST ; Rotary encoder (pulser) MOVF PORTB,W ; Mandatory read to clear mismatch BCF INTCON,RBIF ; Clear interrupt flag ANDLW B'00110000' ; Blank all bits except rotary encoder PORTB<5:4> MOVWF ROTCUR ; Save current position MOVLW B'00000000' SUBWF ROTCUR,W BTFSC STATUS,Z ; Pos 00? BRA TEST00 ; Yes MOVLW B'00010000' SUBWF ROTCUR,W BTFSC STATUS,Z ; Pos 01? BRA TEST01 ; Yes MOVLW B'00110000' SUBWF ROTCUR,W BTFSC STATUS,Z ; Pos 11? BRA TEST11 ; Yes MOVLW B'00000000' SUBWF ROTPREV,W BTFSC STATUS,Z ; Prev pos 00? BRA ROTDN ; Yes BRA ROTUP TEST00 MOVLW B'00010000' SUBWF ROTPREV,W BTFSC STATUS,Z ; Prev pos 01? BRA ROTDN ; Yes, DN BRA ROTUP TEST01 MOVLW B'00110000' SUBWF ROTPREV,W BTFSC STATUS,Z ; Prev pos 11? BRA ROTDN ; Yes, DN BRA ROTUP TEST11 MOVLW B'00100000' SUBWF ROTPREV,W BTFSC STATUS,Z ; Prev pos 10? BRA ROTDN ; Yes, DN BRA ROTUP ROTDN BTFSC SYS_TIME ; TIME display? BRA TUNE_DN ; No BTFSC SETMIN ; Set Min pressed? BRA MIN_DN ; Yes BTFSC SETHOUR ; Set Hours pressed? BRA HOUR_DN ; Yes TUNE_DN ; Nothing pressed, test for lower edge of frequency range (87.50 MHz) BCF TIME_ON ; Clear flag to allow frequency display MOVLW 0x0A MOVWF MEM CALL LOADSYS ; Blank memory display BTFSC SYS_FM ; FM Mode? BRA TUNE_DN_AM ; No, tune down MW/LW MOVF FREQLO,F BTFSC STATUS,Z ; Low byte zero? DECF FREQHI,F ; Yes DECF FREQLO,F DECF FM_BE_LO,W SUBWF FREQLO,W BTFSS STATUS,Z BRA DO_UPDATE MOVF FREQHI,W SUBLW 0x26 BTFSS STATUS,Z ; Zero? BRA DO_UPDATE MOVLW 0x2E MOVWF FREQHI MOVLW 0x5D MOVFF FM_BE_HI,FREQLO MOVLW 0x05 MOVWF MUTECTR BCF MUTE ; Mute for 0.4 s BRA DO_UPDATE TUNE_DN_AM ; Tune down MW/LW BTFSC SYS_MW ; MW Mode? BRA TUNE_DN_LW ; No, tune down LW MOVF FREQLO,F ; Test for lower edge of frequency range (520 kHz) BTFSC STATUS,Z ; Low byte zero? DECF FREQHI,F ; Yes DECF FREQLO,F DECF MW_BE_LO,W SUBWF FREQLO,W BTFSS STATUS,Z ; LSB part lower edge? BRA DO_UPDATE ; No MOVF FREQHI,W SUBLW 0x07 BTFSS STATUS,Z ; MSB part lower edge? BRA DO_UPDATE ; No MOVLW 0x10 ; Preload upper edge (1620 kHz) MOVWF FREQHI MOVFF MW_BE_HI,FREQLO MOVLW 0x03 MOVWF MUTECTR BCF MUTE ; Mute for 0.3 s BRA DO_UPDATE TUNE_DN_LW ; Tune down LW MOVF FREQLO,F ; Test for lower edge of frequency range (150 kHz) BTFSC STATUS,Z ; Low byte zero? DECF FREQHI,F ; Yes DECF FREQLO,F DECF LW_BE_LO,W SUBWF FREQLO,W BTFSS STATUS,Z ; LSB part lower edge? BRA DO_UPDATE ; No MOVF FREQHI,W SUBLW 0x04 BTFSS STATUS,Z ; MSB part lower edge? BRA DO_UPDATE ; No MOVLW 0x05 ; Preload upper edge (280 kHz) MOVWF FREQHI MOVLW 0xB7 MOVFF LW_BE_HI,FREQLO MOVLW 0x03 MOVWF MUTECTR BCF MUTE ; Mute for 0.3 s BRA DO_UPDATE MIN_DN DECF MINS BTFSS STATUS,N ; MINS negative? BRA EXIT_TIMESET ; No MOVLW 0x09 MOVWF MINS DECF TMINS BTFSS STATUS,N ; TMINS negative? BRA EXIT_TIMESET MOVLW 0x05 MOVWF TMINS BRA EXIT_TIMESET HOUR_DN DECF HOURS BTFSS STATUS,N ; HOURS negative? BRA EXIT_TIMESET ; No MOVLW 0x09 MOVWF HOURS DECF THOURS BTFSS STATUS,N ; THOURS negative (midnight)? BRA EXIT_TIMESET MOVLW 0x02 MOVWF THOURS MOVLW 0x03 MOVWF HOURS BRA EXIT_TIMESET ROTUP BTFSC SYS_TIME ; TIME display? BRA TUNE_UP ; No BTFSC SETMIN ; Set Min pressed? BRA MIN_UP ; Yes BTFSC SETHOUR ; Set Hours pressed? BRA HOUR_UP ; Yes TUNE_UP BCF TIME_ON ; Clear flag to allow frequency display MOVLW 0x0A MOVWF MEM ; Blank memory display BTFSC SYS_FM ; FM Mode? BRA TUNE_UP_AM ; No, tune up MW/LW CALL LOADSYS INCFSZ FREQLO,F BRA TESTUPFM INCF FREQHI,F ; no need to test this value because it is already tested below BRA DO_UPDATE TESTUPFM ; Test for upper edge of frequency range (107.99 MHz) INCF FM_BE_HI,W ; Get low byte upper edge SUBWF FREQLO,W BTFSS STATUS,Z BRA DO_UPDATE MOVF FREQHI,W SUBLW 0x2E BTFSS STATUS,Z ; Zero? BRA DO_UPDATE ; No MOVLW 0x26 ; Preload lower edge (87.50 MHz) MOVWF FREQHI MOVFF FM_BE_LO,FREQLO ; Get low byte lower edge MOVLW 0x03 MOVWF MUTECTR BCF MUTE ; Mute for 0.3 s BRA DO_UPDATE TUNE_UP_AM ; Tune up MW/LW BTFSC SYS_MW ; MW Mode? BRA TUNE_UP_LW ; No, tune up LW CALL LOADSYS INCFSZ FREQLO,F BRA TESTUPAM INCF FREQHI,F ; no need to test this value because it is already tested below BRA DO_UPDATE TESTUPAM ; Test for upper edge of frequency range (1620 kHz) INCF MW_BE_HI,W SUBWF FREQLO,W BTFSS STATUS,Z BRA DO_UPDATE MOVF FREQHI,W SUBLW 0x10 BTFSS STATUS,Z ; Zero? BRA DO_UPDATE MOVLW 0x07 ; Preload lower edge (520 kHz) MOVWF FREQHI MOVFF MW_BE_LO,FREQLO MOVLW 0x03 MOVWF MUTECTR BCF MUTE ; Mute for 0.3 s BRA DO_UPDATE TUNE_UP_LW ; Tune up LW CALL LOADSYS INCFSZ FREQLO,F BRA TESTUPLW INCF FREQHI,F ; no need to test this value because it is already tested below BRA DO_UPDATE TESTUPLW ; Test for upper edge of frequency range (280 kHz) INCF LW_BE_HI,W SUBWF FREQLO,W BTFSS STATUS,Z BRA DO_UPDATE MOVF FREQHI,W SUBLW 0x05 BTFSS STATUS,Z ; Zero? BRA DO_UPDATE MOVLW 0x04 ; Preload lower edge (150 kHz) MOVWF FREQHI MOVFF LW_BE_LO,FREQLO MOVLW 0x05 MOVWF MUTECTR BCF MUTE ; Mute for 0.3 s DO_UPDATE CALL LOADPLL CALL FREQDISPLAY BSF SYS_TIME CALL LOADSYS MOVFF ROTCUR,ROTPREV BCF INTCON,RBIF RETFIE MIN_UP INCF MINS MOVLW 0x0A SUBWF MINS,W BTFSS STATUS,C ; Overflow? BRA EXIT_TIMESET ; No CLRF MINS INCF TMINS MOVLW 0x06 SUBWF TMINS,W BTFSS STATUS,C ; TMINS overflow? BRA EXIT_TIMESET CLRF TMINS BRA EXIT_TIMESET HOUR_UP MOVLW 0x02 ; Test for midnight SUBWF THOURS,W ; if true, C=1, check for midnight BTFSC STATUS,C BRA MIDNIGHT_UP INCF HOURS MOVLW 0x0A SUBWF HOURS,W BTFSS STATUS,C ; Overflow? BRA EXIT_TIMESET ; No CLRF HOURS INCF THOURS BRA EXIT_TIMESET MIDNIGHT_UP INCF HOURS,F MOVLW 0x04 ; Test for midnight SUBWF HOURS,W ; if true, C=1, new day BTFSS STATUS,C BRA EXIT_TIMESET CLRF HOURS CLRF THOURS EXIT_TIMESET CLRF SECS CLRF TICKS ; Make sure time gets set at a whole minute CALL SHOW_TIME MOVFF ROTCUR,ROTPREV BCF INTCON,RBIF RETFIE ; Subroutines LOADPLL BSF DLEN0 ; Load IC6601 (SAA1056) CALL DEL10US CALL SHIFTZERO ; Leading zero BTFSC FM ; AM mode (REF1=0)? BRA $+8 ; No (why is this not $+6?!?!!? Oh wait, CALL has an extra NOP) CALL SHIFTZERO ; REF1=0 BRA $+4 CALL SHIFTONE ; REF1=1 MOVFF FREQHI,DATABUF ; Load high byte RLCF DATABUF,F ; Dummyshift bit 15 CALL SHIFTPLL ; Shift bit 14 (NP9) CALL SHIFTPLL ; Shift bit 13 (NP8) CALL SHIFTPLL ; Shift bit 12 (NP7) CALL SHIFTPLL ; Shift bit 11 (NP6) CALL SHIFTPLL ; Shift bit 10 (NP5) CALL SHIFTPLL ; Shift bit 9 (NP4) CALL SHIFTPLL ; Shift bit 8 (NP3) MOVFF FREQLO,DATABUF ; Load low byte CALL SHIFTPLL ; Shift bit 7 (NP2) CALL SHIFTPLL ; Shift bit 6 (NP1) CALL SHIFTPLL ; Shift bit 5 (NP0) CALL SHIFTPLL ; Shift bit 4 (NS4) CALL SHIFTPLL ; Shift bit 3 (NS3) CALL SHIFTPLL ; Shift bit 2 (NS2) CALL SHIFTPLL ; Shift bit 1 (NS1) CALL SHIFTPLL ; Shift bit 0 (NS0) CALL DEL10US BCF DLEN0 BCF DATA CALL DEL10US BSF CLB CALL DEL10US BCF CLB ; Load pulse CALL DEL200US ; Wait time to prevent PLL not latching in some conditions (not documented) RETURN FREQDISPLAY ; Convert FREQHI:LO to display BTFSS FM BRA SHOW_AM MOVFF FREQHI,DISPHI ; Subtract 110.70 MHz to get three digit BCD MOVLW 0x2B SUBWF DISPHI,F MOVFF FREQLO,DISPLO MOVF FM_IF100,W ; Get low byte FM IF offset SUBWF DISPLO,F BTFSS STATUS,C DECF DISPHI,F BTFSS DISPHI,7 ; Result negative? BRA DISP100MC ; No, display >=100 MHz MOVFF FREQHI,DISPHI ; Subtract 100.70 MHz to get three digit BCD MOVLW 0x27 SUBWF DISPHI,F MOVFF FREQLO,DISPLO MOVF FM_IF90,W ; Get lower byte FM IF offset SUBWF DISPLO,F BTFSS STATUS,C DECF DISPHI,F BTFSS DISPHI,7 ; Result negative? BRA DISP90MC ; No, display >=90 MHz MOVFF FREQHI,DISPHI ; Subtract 90.70 MHz to get three digit BCD <90 MHz MOVLW 0x23 SUBWF DISPHI,F MOVFF FREQLO,DISPLO MOVF FM_IF80,W ; Get lower byte FM IF offset SUBWF DISPLO,F BTFSS STATUS,C DECF DISPHI,F MOVLW B'10000000' ; Show 8x MHz MOVWF D3 BRA Bin2BCD DISP90MC MOVLW B'10010000' ; Show 9x MHz MOVWF D3 BRA Bin2BCD DISP100MC MOVLW B'01000000' ; Show 10x MHz MOVWF D3 BRA Bin2BCD SHOW_AM MOVFF FREQHI,DISPHI ; Subtract AM IF to get three digit BCD MOVLW 0x03 SUBWF DISPHI,F MOVFF FREQLO,DISPLO MOVF AM_IFLO,W SUBWF DISPLO,F BTFSS STATUS,C ; Carry? DECF DISPHI,F ; Yes, decrement high byte BCF STATUS,C ; Make sure carry is clear RRCF DISPHI ; Divide displayed frequency by 2 RRCF DISPLO Bin2BCD ; 12 bit routine from http://www.piclist.com/techref/microchip/math/radix/b2bu-10b4d-eag.htm movf DISPHI,w iorlw 0xF0 ;w=H2-16 movwf D1 ;D1=H2-16 addwf D1,f ;D1=H2*2-32 addwf D1,f ;D1=H2*3-48 movwf D2 ;D2=H2-16 addlw -D'5' ;w=H2-21 addwf D2,f ;D2=H2*2-37 Done! addlw D'41' ;w=H2+20 movwf D0 ;D0=H2+20 swapf DISPLO,w iorlw 0xF0 ;w=H1-16 addwf D1,f ;D1=H2*3+H1-64 addwf D0,f ;D0=H2+H1+4, C=1 rlcf D0,f ;D0=(H2+H1)*2+9, C=0 comf D0,f ;D0=-(H2+H1)*2-10 rlcf D0,f ;D0=-(H2+H1)*4-20 movf DISPLO,w andlw 0x0F ;w=H0 addwf D0,f ;D0=H0-(H2+H1)*4-20 Done! rlcf D1,f ;C=0, D1=H2*6+H1*2-128 Done! movlw D'5' movwf TEMP movlw D'10' mod0 addwf D0,f ;D(X)=D(X)mod10 decfsz D1,f ;D(X+1)=D(X+1)+D(X)div10 NOP btfss STATUS,C bra mod0 mod1 addwf D1,f decfsz D2,f NOP btfss STATUS,C bra mod1 mod2 addwf D2,f decfsz TEMP,f NOP btfss STATUS,C bra mod2 ; Done Bin2BCD BTFSC FM ; AM? CALL Calc_D2D0FM ; No BTFSS FM ; FM? (testing is cheap) CALL Calc_DISP_AM ; No ; Done BCD to 7-segment UpdateDISP ; D3:D0 must already be 7-segment values BCF DATA ; Load IC6700 (SAA1060) register B BSF DLEN0 CALL DEL10US BSF CLB CALL DEL10US BCF CLB ; Leading zero CALL DEL10US MOVFF D1,DATABUF ; Get second digit (100 kHz) into buffer CALL SHIFTDISP ; Q1 a2 CALL SHIFTDISP ; Q2 b2 CALL SHIFTDISP ; Q3 c2 CALL SHIFTDISP ; Q4 d2 CALL SHIFTDISP ; Q5 e2 CALL SHIFTDISP ; Q6 f2 CALL SHIFTDISP ; Q7 g2 CALL SHIFTDISP ; Q8 dp2 MOVFF D3,DATABUF ; Get fourth digit (10 MHz) into buffer CALL SHIFTDISP ; Q9 a4 CALL SHIFTDISP ; Q10 b4 CALL SHIFTDISP ; Q11 c4 CALL SHIFTDISP ; Q12 d4 CALL SHIFTDISP ; Q13 e4 CALL SHIFTDISP ; Q14 f4 CALL SHIFTDISP ; Q15 g4 CALL SHIFTDISP ; Q16 b5, c5 CALL SHIFTZERO ; !B BCF DLEN0 CALL DEL10US BSF CLB CALL DEL10US BCF CLB ; Load pulse CALL DEL10US ; Wait 20 µs CALL DEL10US BSF DLEN0 ; Load IC6700 (SAA1060) register A CALL DEL10US CALL SHIFTZERO ; Leading zero MOVFF D0,DATABUF ; Get first digit (10 kHz) into buffer CALL SHIFTDISP ; Q1 a1 CALL SHIFTDISP ; Q2 b1 CALL SHIFTDISP ; Q3 c1 CALL SHIFTDISP ; Q4 d1 CALL SHIFTDISP ; Q5 e1 CALL SHIFTDISP ; Q6 f1 CALL SHIFTDISP ; Q7 g1 CALL SHIFTDISP ; Q8 (none) MOVFF D2,DATABUF ; Get third digit (1 MHz) into buffer CALL SHIFTDISP ; Q9 a3 CALL SHIFTDISP ; Q10 b3 CALL SHIFTDISP ; Q11 c3 CALL SHIFTDISP ; Q12 d3 CALL SHIFTDISP ; Q13 e3 CALL SHIFTDISP ; Q14 f3 CALL SHIFTDISP ; Q15 g3 CALL SHIFTDISP ; Q16 dp3 CALL SHIFTONE ; A BCF DLEN0 BCF DATA CALL DEL10US BSF CLB CALL DEL10US BCF CLB ; Load pulse CALL DEL200US ; Wait 200 µs RETURN SHIFTPLL ; Shift one bit onto data line (PLL) BSF CLB RLCF DATABUF,F ; Rotate MSB into carry BTFSC STATUS,C ; Carry clear? BSF DATA ; No, set DATA BTFSS STATUS,C ; Carry set? BCF DATA ; No, clear DATA CALL DEL10US BCF CLB ; Clock in data CALL DEL10US RETURN SHIFTDISP ; Shift one bit onto data line (display/sys/mem) BSF CLB RRCF DATABUF,F ; Rotate LSB into carry BTFSC STATUS,C ; Carry clear? BSF DATA ; No, set DATA BTFSS STATUS,C ; Carry set? BCF DATA ; No, clear DATA CALL DEL10US BCF CLB ; Clock in data CALL DEL10US RETURN SHIFTZERO BSF CLB BCF DATA CALL DEL10US BCF CLB ; Clock in data CALL DEL10US RETURN SHIFTONE BSF CLB BSF DATA CALL DEL10US BCF CLB ; Clock in data CALL DEL10US RETURN DEL10US ; 10-ish µs delay NOP NOP NOP NOP RETURN DEL200US ; something-ish µs delay MOVLW 0x20 MOVWF TEMP DECFSZ TEMP BRA $-2 RETURN Calc_D2D0FM MOVLW 0x0F ; Look up 7-segment code for 1 MHz ANDWF D2,F ; Sanitize pointer RLNCF D2,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D2 BCF D2,7 ; Switch on decimal point MOVLW 0x0F ; Look up 7-segment code for 0.1 MHz ANDWF D1,F ; Sanitize pointer RLNCF D1,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D1 MOVLW 0x0F ; Look up 7-segment code for 10 kHz ANDWF D0,F ; Sanitize pointer RLNCF D0,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D0 BTFSC SYS_ON BCF D0,7 ; Switch on MHz LED (if not STBY) RETURN Calc_DISP_AM BCF AM_MHZ BTFSC TEMP,0 ; >= 1000 kHz? BSF AM_MHZ ; Yes MOVFF D2,D3 MOVLW 0x0F ; Look up 7-segment code for 100 kHz ANDWF D3,F ; Sanitize pointer RLNCF D3,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D3 BTFSC AM_MHZ ; >= 1000 kHz? BCF D3,7 ; Yes, show 1 in LED5 ("D4") MOVFF D1,D2 MOVLW 0x0F ; Look up 7-segment code for 10 kHz ANDWF D2,F ; Sanitize pointer RLNCF D2,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D2 MOVFF D0,D1 ; Shift all digits left MOVLW 0x0F ; Look up 7-segment code for 1 kHz ANDWF D1,F ; Sanitize pointer RLNCF D1,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D1 BCF D1,7 ; Switch on decimal point and kHz LED (if not STBY) MOVLW 0x0A ; Adjusted for two byte steps in table MOVWF D0 BTFSS FREQLO,0 ; 0.5 kHz? CLRF D0 ; No MOVF D0,W ; Load W for lookup CALL BCD7SEG MOVWF D0 RETURN ; End rotary stuff TESTSTATE ; Keyboard scanner every 100 ms BCF INTCON,TMR0IF MOVLW 0x3C MOVWF TMR0H MOVLW 0xB6 MOVWF TMR0L ; Preload for 100000 µs timebase BTFSC Splash ; Startup splash screen in progress? BRA DO_SPLASH ; Yes CALL DO_CLOCK ; Update clock BTFSC STBY ; Power switch in Standby position? BRA DO_STBY ; Yes, go into standby and ignore any keypress CALL DO_UNSTBY BTFSC KILLRBIE ; Set when in a function that does not need rotary encoder BCF INTCON,RBIE BTFSC SYSCHG ; System function changed? CALL ShowFunc ; Yes, show function in display during countdown, then time (TIME_ON) BTFSC TIME_ON ; Time display? CALL SHOW_TIME DECFSZ SAVESTATECTR,F BRA $+4 CALL SAVESTATE ; Every 25.6 s check if state has changed BTFSS STORE ; Store pressed? BRA MEMSTORE ; Yes BTFSC MEMSCAN ; Wait for memory store selection? BRA MEMSTORE2 ; Yes DECFSZ MUTECTR ; MUTE period ended BRA $+4 ; No BSF MUTE ; Keyboard scanning starts here. Note that the first key encountered wins. MOVLW B'11111110' ; Scan P10 MOVWF PORTC BTFSS KB2 ; P9 pressed? BRA DO_P9 ; Yes, recall P9 BTFSS KB3 ; P1 pressed? BRA DO_P1 ; Yes, recall P1 MOVLW B'11111101' ; Scan P11 MOVWF PORTC BTFSS KB1 ; TAPE pressed? BRA DO_TAPE ; Yes, select SYS_TAPE BTFSS KB3 ; P2 pressed? BRA DO_P2 ; Yes, recall P2 MOVLW B'11111011' ; Scan P12 MOVWF PORTC BTFSS KB2 ; FM pressed? BRA DO_FM ; Yes, select SYS_FM BTFSS KB3 ; P3 pressed? BRA DO_P3 ; Yes, recall P3 MOVLW B'11110111' ; Scan P13 MOVWF PORTC BTFSS KB2 ; FM pressed? BRA DO_MW ; Yes, select SYS_MW BTFSS KB3 ; P4 pressed? BRA DO_P4 ; Yes, recall P4 MOVLW B'11101111' ; Scan P14 MOVWF PORTC BTFSS KB1 ; PHONO pressed? BRA DO_PU ; Yes, select SYS_PU BTFSS KB2 ; FM pressed? BRA DO_LW ; Yes, select SYS_LW BTFSS KB3 ; P5 pressed? BRA DO_P5 ; Yes, recall P5 MOVLW B'11011111' ; Scan P15 MOVWF PORTC BTFSS KB2 ; SET + HOURS pressed? BRA DO_SETHOUR ; Yes, adjust hours BTFSS KB3 ; P6 pressed? BRA DO_P6 ; Yes, recall P6 MOVLW B'10111111' ; Scan P16 MOVWF PORTC BTFSS KB2 ; SET + MIN pressed? BRA DO_SETMIN ; Yes, adjust minutes BTFSS KB3 ; P7 pressed? BRA DO_P7 ; Yes, recall P7 MOVLW B'01111111' ; Scan P17 MOVWF PORTC BTFSS KB2 ; TIME pressed? BRA DO_TIME ; Yes, show time BTFSS KB3 ; P8 pressed? BRA DO_P8 ; Yes, recall P8 EXIT_KBD MOVLW B'11111111' ; Reset scanlines MOVWF PORTC BTFSC KEYPRESS ; Did a keypress occur? BRA DoneKEYPRESS ; Yes BCF HELD ; No keys held BCF SETMIN BCF SETHOUR DoneKEYPRESS BCF KEYPRESS RETFIE MEMSTORE ; STORE is pressed BTFSC SYS_FM ; FM mode? BRA Test_MW ; No BRA MEMSTORE1 Test_MW BTFSC SYS_MW ; MW mode? BRA Test_LW ; No BRA MEMSTORE1 Test_LW BTFSC SYS_LW ; LW mode? RETFIE ; No MEMSTORE1 MOVLW 0x64 ; 10 seconds timeout MOVWF MEMCTR BTFSC MEMSCAN ; STORE still pressed? BRA MEMSTORE2 ; Yes MOVFF MEM,MEMSAV ; Save memory display to allow flashing "-" (MEM=0x0B) MOVLW 0x0A MOVWF MEM ; Show blank display BSF MEMSCAN MEMSTORE2 DECFSZ MEMCTR BRA DO_MEMSCAN MOVFF MEMSAV,MEM ; Restore previous memory display MOVLW 0x0B SUBWF MEM,W BTFSC STATUS,Z ; Memory pointer 0x0B ("-")? BCF MEM,0 ; Yes BCF MEMSCAN CALL LOADSYS ; Show previous memory display RETFIE DO_MEMSCAN BTFSS TICKS,2 ; Flash "-" BCF MEM,0 BTFSC TICKS,2 BSF MEM,0 CALL LOADSYS ; Show "-" or " " ; Memory keyboard scanning starts here MOVLW B'11111110' ; Scan P10 MOVWF PORTC BTFSS KB3 ; P1 pressed? BRA STORE_P1 ; Yes BTFSS KB2 ; P9 pressed? BRA STORE_P9 ; Yes MOVLW B'11111101' ; Scan P11 MOVWF PORTC BTFSS KB3 ; P2 pressed? BRA STORE_P2 ; Yes MOVLW B'11111011' ; Scan P12 MOVWF PORTC BTFSS KB3 ; P3 pressed? BRA STORE_P3 ; Yes MOVLW B'11110111' ; Scan P13 MOVWF PORTC BTFSS KB3 ; P4 pressed? BRA STORE_P4 ; Yes MOVLW B'11101111' ; Scan P14 MOVWF PORTC BTFSS KB3 ; P5 pressed? BRA STORE_P5 ; Yes MOVLW B'11011111' ; Scan P15 MOVWF PORTC BTFSS KB3 ; P6 pressed? BRA STORE_P6 ; Yes MOVLW B'10111111' ; Scan P16 MOVWF PORTC BTFSS KB3 ; P7 pressed? BRA STORE_P7 ; Yes MOVLW B'01111111' ; Scan P17 MOVWF PORTC BTFSS KB3 ; P8 pressed? BRA STORE_P8 ; Yes MOVLW B'11111111' ; Set P17 MOVWF PORTC RETFIE ; Nothing pressed STORE_P1 MOVLW 0x01 BRA WRITE_Px STORE_P2 MOVLW 0x02 BRA WRITE_Px STORE_P3 MOVLW 0x03 BRA WRITE_Px STORE_P4 MOVLW 0x04 BRA WRITE_Px STORE_P5 MOVLW 0x05 BRA WRITE_Px STORE_P6 MOVLW 0x06 BRA WRITE_Px STORE_P7 MOVLW 0x07 BRA WRITE_Px STORE_P8 MOVLW 0x08 BRA WRITE_Px STORE_P9 MOVLW 0x09 WRITE_Px MOVWF MEM CALL LOADSYS CALL EEPROM_WRITE ; Store FREQHI, FREQLO, SYS BCF MEMSCAN RETFIE DO_STBY ; Not a CALL so exit with RETFIE BTFSS SYS_ON ; System already in STBY? BRA STBY_COUNTDOWN ; Yes BCF INTCON,RBIE ; Disable PORTB<7:4> change interrupt MOVF PORTB,W ; Mandatory read of PORTB BCF INTCON,RBIF ; Make sure flag is cleared MOVFF SYS,STBY_SYS ; Save current system state MOVLW B'10111101' ; Clear all functions, switch off Relay, show TIME MOVWF SYS MOVFF MEM,MEMSAV MOVLW 0x0A MOVWF MEM ; Switch of memory display CALL LOADSYS BCF SYS_ON ; Clear flag MOVLW B'10010010' ; Code for 'S' MOVWF D3 MOVLW B'10000111' ; Code for 't' MOVWF D2 MOVLW B'10000011' ; Code for 'b' MOVWF D1 MOVLW B'10010001' ; Code for 'Y' MOVWF D0 CALL UpdateDISP MOVLW 0x0A MOVWF COUNTDOWN ; Show "StbY" for one second RETFIE STBY_COUNTDOWN ; Countdown for time display in STBY DECFSZ COUNTDOWN,F RETFIE INCF COUNTDOWN,F ; Make sure next decrement is zero again CALL SHOW_TIME RETFIE DO_TIME BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt BTFSS ShowFreq ; Function with frequency in display? BRA EXIT_KBD ; No MOVLW B'00000010' XORWF FLAGS,F ; Toggle TIME_ON BSF HELD BTFSC TIME_ON ; Time in display? BRA Fix_TIME_LED ; Yes CALL FREQDISPLAY ; Show frequency BSF SYS_TIME CALL LOADSYS ; Switch off TIME LED BRA EXIT_KBD Fix_TIME_LED ; Switch on TIME LED because of time display BCF SYS_TIME CALL LOADSYS CALL SHOW_TIME_NOW BRA EXIT_KBD DO_SETMIN BSF KEYPRESS BCF TIME_UNSET ; Well, time is being set so start flashing dot BSF SETMIN ; Set SETMIN flag BSF INTCON,RBIE ; Enable time setting in PU and TAPE BRA EXIT_KBD DO_SETHOUR BSF KEYPRESS BCF TIME_UNSET ; Well, time is being set so start flashing dot BSF SETHOUR ; Set SETHOUR flag BSF INTCON,RBIE ; Enable time setting in PU and TAPE BRA EXIT_KBD DO_PU BTFSS SYS_PU ; Already in PHONO? BRA EXIT_KBD ; Yes BSF KEYPRESS BTFSS SYS_FM ; Previous function FM? CALL SAVE_FM ; Yes BTFSS SYS_MW ; Previous function MW? CALL SAVE_MW ; Yes BTFSS SYS_LW ; Previous function LW? CALL SAVE_LW ; Yes MOVLW B'11011111' ; Clear SYS_PU MOVWF SYS MOVFF MEM,MEMSAV ; Save any memory display MOVLW 0x0A MOVWF MEM ; and clear it CALL LOADSYS MOVLW B'10001100' ; Show "P" MOVWF D3 MOVLW B'11000001' ; Show "U" MOVWF D2 MOVLW 0xFF ; Show " " MOVWF D1 MOVWF D0 CALL UpdateDISP BCF INTCON,RBIE ; Disable rotary encoder (pulser) because PU does not need it BSF KILLRBIE ; And keep it disabled MOVLW 0x0A MOVWF COUNTDOWN BCF TIME_ON BCF ShowFreq ; Make sure LEDs are correct (DO_TIME) BSF SYSCHG ; Set up for PU in display for 1 sec (ShowFunc) BRA EXIT_KBD DO_TAPE BTFSS SYS_TAPE ; Already in TAPE? BRA EXIT_KBD ; Yes BSF KEYPRESS BTFSS SYS_FM ; Previous function FM? CALL SAVE_FM ; Yes BTFSS SYS_MW ; Previous function MW? CALL SAVE_MW ; Yes BTFSS SYS_LW ; Previous function LW? CALL SAVE_LW ; Yes MOVLW B'01111111' ; Clear SYS_TAPE MOVWF SYS MOVFF MEM,MEMSAV ; Save any memory display MOVLW 0x0A MOVWF MEM ; and clear it CALL LOADSYS MOVLW B'10000111' ; Show "t" MOVWF D3 MOVLW B'10001000' ; Show "A" MOVWF D2 MOVLW B'10001100' ; Show "P" MOVWF D1 MOVLW B'10000110' ; Show "E" MOVWF D0 CALL UpdateDISP BCF INTCON,RBIE ; Disable rotary encoder (pulser) because TAPE does not need it BSF KILLRBIE ; And keep it disabled MOVLW 0x0A MOVWF COUNTDOWN BCF TIME_ON BCF ShowFreq ; Make sure LEDs are correct (DO_TIME) BSF SYSCHG ; Set up for tAPE in display for 1 sec (ShowFunc) BRA EXIT_KBD DO_FM BTFSS SYS_FM ; Already in FM? BRA EXIT_KBD ; Yes BSF KEYPRESS BCF MUTE ; Mute audio to allow PLL to settle MOVLW 0x04 MOVWF MUTECTR ; Set mute counter for 0.4 s BTFSS SYS_MW ; Previous function MW? CALL SAVE_MW ; Yes BTFSS SYS_LW ; Previous function LW? CALL SAVE_LW ; Yes MOVLW B'11111011' MOVWF SYS MOVLW 0x0A MOVWF MEM ; Clear any memory display CALL LOADSYS CALL RESTORE_FM ; Restore FREQHI:LO for FM CALL LOADPLL MOVLW B'10001110' ; Show "F" MOVWF D3 MOVLW B'11001000' ; Show "M" MOVWF D2 MOVLW B'11001000' ; Show "M" MOVWF D1 MOVLW 0xFF ; Show " " MOVWF D0 CALL UpdateDISP MOVLW 0x0A MOVWF COUNTDOWN BCF TIME_ON ; Switch off time display BSF ShowFreq ; Make sure LEDs are correct (DO_TIME) BSF SYSCHG ; Set up for FM in display for 1 sec (ShowFunc) BSF INTCON,RBIE ; Re-enable rotary encoder (pulser) because FM needs it BCF KILLRBIE ; And keep it enabled BRA EXIT_KBD DO_MW BTFSS SYS_MW ; Already in MW? BRA EXIT_KBD ; Yes BSF KEYPRESS BCF MUTE ; Mute audio to allow PLL to settle MOVLW 0x04 MOVWF MUTECTR ; Set mute counter for 0.4 s BTFSS SYS_FM ; Previous function FM? CALL SAVE_FM ; Yes BTFSS SYS_LW ; Previous function LW? CALL SAVE_LW ; Yes MOVLW B'11110111' ; Clear Q4 (MW) of IC6701 MOVWF SYS ; Thus set system to MW MOVLW 0x0A MOVWF MEM ; Clear any memory display CALL LOADSYS CALL RESTORE_MW ; Restore FREQHI:LO for MW CALL LOADPLL MOVLW B'11001000' ; Show "M" MOVWF D3 MOVWF D2 MOVLW B'11000001' ; Show "U" MOVWF D1 MOVWF D0 CALL UpdateDISP MOVLW 0x0A MOVWF COUNTDOWN BCF TIME_ON ; Switch off time display BSF ShowFreq ; Make sure LEDs are correct (DO_TIME) BSF SYSCHG ; Set up for MW in display for 1 sec (ShowFunc) BSF INTCON,RBIE ; Re-enable rotary encoder (pulser) because MW needs it BCF KILLRBIE ; And keep it enabled BRA EXIT_KBD DO_LW BTFSS SYS_LW ; Already in LW? BRA EXIT_KBD ; Yes BSF KEYPRESS BCF MUTE ; Mute audio to allow PLL to settle MOVLW 0x04 MOVWF MUTECTR ; Set mute counter for 0.4 s BTFSS SYS_FM ; Previous function FM? CALL SAVE_FM ; Yes BTFSS SYS_MW ; Previous function MW? CALL SAVE_MW ; Yes MOVLW B'11101111' ; Clear Q5 (LW) of IC6701 MOVWF SYS ; Thus set system to LW MOVLW 0x0A MOVWF MEM ; Clear any memory display CALL LOADSYS CALL RESTORE_LW ; Restore FREQHI:LO for LW CALL LOADPLL MOVLW B'11000111' ; Show "L" MOVWF D3 MOVLW B'11000001' ; Show "U" MOVWF D2 MOVWF D1 MOVLW 0xFF ; Show " " MOVWF D0 CALL UpdateDISP MOVLW 0x0A MOVWF COUNTDOWN BCF TIME_ON ; Switch off time display BSF ShowFreq ; Make sure LEDs are correct (DO_TIME) BSF SYSCHG ; Set up for MW in display for 1 sec (ShowFunc) BSF INTCON,RBIE ; Re-enable rotary encoder (pulser) because MW needs it BCF KILLRBIE ; And keep it enabled BRA EXIT_KBD DO_P1 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x01 ; Test for P1 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x01 BRA READ_Px DO_P2 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x02 ; Test for P2 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x02 BRA READ_Px DO_P3 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x03 ; Test for P3 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x03 BRA READ_Px DO_P4 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x04 ; Test for P4 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x04 BRA READ_Px DO_P5 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x05 ; Test for P5 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x05 BRA READ_Px DO_P6 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x06 ; Test for P6 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x06 BRA READ_Px DO_P7 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x07 ; Test for P7 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x07 BRA READ_Px DO_P8 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x08 ; Test for P8 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x08 BRA READ_Px DO_P9 BSF KEYPRESS BTFSC HELD ; Key held? BRA EXIT_KBD ; Yes, exit interrupt MOVLW 0x09 ; Test for P9 already selected SUBWF MEM,W BTFSC STATUS,Z ; Is it? BRA EXIT_KBD ; Yes, exit interrupt BSF HELD MOVLW 0x09 READ_Px MOVWF MEM ; Load memory location for preset to read EEPROM CALL EEPROM_READ ; Get PLL and SYS BCF MUTE ; Mute audio to allow PLL to settle MOVLW 0x04 MOVWF MUTECTR ; Set mute counter for 0.4 s CALL LOADSYS ; Set system and show memory CALL FREQDISPLAY ; Show frequency CALL LOADPLL ; Tune PLL BCF TIME_ON ; Switch off time display in case of PU, TAPE BSF ShowFreq ; Make sure LEDs are correct (DO_TIME) BSF INTCON,RBIE ; Re-enable rotary encoder (pulser) because FM needs it BCF KILLRBIE ; And keep it enabled BRA EXIT_KBD ; Subroutines TESTSTATE DO_UNSTBY BTFSC SYS_ON ; System state already restored from STBY? RETURN ; Yes MOVFF STBY_SYS,SYS ; Get saved system state MOVFF MEMSAV,MEM ; and memory display CALL LOADSYS ; Restore system state BSF SYS_ON ; Set corresponding flag CALL FREQDISPLAY ; Show frequency BSF INTCON,RBIE ; Re-enable PORTB<7:4> change interrupt RETURN DO_CLOCK ; Handles the clock, not the time display INCF TICKS,F ; every Timer0 interrupt MOVLW 0x0A ; Test for SECS overflow SUBWF TICKS,W BTFSS STATUS,C ; Overflow? RETURN ; No CLRF TICKS ; Increment seconds INCF SECS,F ; SECS are not used on the display MOVLW 0x3C ; Test for SECS overflow (60) SUBWF SECS,W BTFSS STATUS,C ; Overflow? RETURN ; No CLRF SECS ; Increment minutes INCF MINS,F MOVLW 0x0A ; Test for overflow SUBWF MINS,W ; if true, C=1, increment 10 minutes BTFSS STATUS,C ; Overflow? RETURN ; No CLRF MINS ; Increment 10 minutes INCF TMINS,F MOVLW 0x06 ; Test for overflow SUBWF TMINS,W ; if true, C=1, increment hours BTFSS STATUS,C RETURN CLRF TMINS ; Test for midnight MOVLW 0x02 SUBWF THOURS,W ; if true, C=1, check for midnight BTFSC STATUS,C BRA MIDNIGHT ; Increment hours INCF HOURS,F MOVLW 0x0A ; Test for overflow SUBWF HOURS,W ; if true, C=1, increment 10 hours BTFSS STATUS,C RETURN CLRF HOURS ; Increment 10 hours INCF THOURS,F RETURN MIDNIGHT INCF HOURS,F MOVLW 0x04 ; Test for midnight SUBWF HOURS,W ; if true, C=1, new day BTFSS STATUS,C RETURN CLRF HOURS CLRF THOURS RETURN SHOW_TIME ; Convert time registers to D3:D0 values TSTFSZ TICKS ; Only update once a second to allow rotary encoder setting time RETURN SHOW_TIME_NOW TSTFSZ THOURS ; Earlier than 10:00? BRA $+8 ; No MOVLW 0xFF ; Blank ten hours digit (D3) MOVWF D3 BRA SHOW_TIME2 MOVLW 0x01 SUBWF THOURS,W BTFSS STATUS,Z ; 1x:xx? BRA $+8 ; No, time is past 20:00 MOVLW B'11111001' ; Code for '1' MOVWF D3 BRA SHOW_TIME2 MOVLW B'10100100' ; Code for '2' MOVWF D3 SHOW_TIME2 ; Convert BCD to 7-segment (D2-D0) MOVFF HOURS,D2 MOVFF TMINS,D1 MOVFF MINS,D0 MOVLW 0x0F ; Look up 7-segment code for hours ANDWF D2,F ; Sanitize pointer RLNCF D2,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D2 BTFSS TIME_UNSET ; Don't flash dot if time not set BTFSS SECS,0 BCF D2,7 ; Switch on decimal point on even seconds MOVLW 0x0F ; Look up 7-segment code for ten minutes ANDWF D1,F ; Sanitize pointer RLNCF D1,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D1 MOVLW 0x0F ; Look up 7-segment code for minutes ANDWF D0,F ; Sanitize pointer RLNCF D0,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF D0 CALL UpdateDISP RETURN ShowFunc DECFSZ COUNTDOWN,F RETURN BTFSC SYS_PU ; PU in display? BRA TEST_TAPE ; No BCF SYSCHG ; Switch off function display BSF TIME_ON BCF SYS_TIME ; Switch on TIME LED CALL LOADSYS CALL SHOW_TIME_NOW RETURN TEST_TAPE BTFSC SYS_TAPE ; tAPE in display? BRA TEST_FM ; No BCF SYSCHG ; Switch off function display BSF TIME_ON BCF SYS_TIME ; Switch on TIME LED CALL LOADSYS CALL SHOW_TIME_NOW RETURN TEST_FM ; Also MW, LW CALL FREQDISPLAY BCF SYSCHG ; Switch off function display RETURN LOADSYS BSF DLEN1 ; Load IC6701 (SAA1060), system function and memory preset CALL DEL10US CALL SHIFTZERO ; Leading zero MOVFF SYS,DATABUF CALL SHIFTDISP ; Q1 (none) SYS<0> CALL SHIFTDISP ; Q2 SYS_TIME (SYS<1>) CALL SHIFTDISP ; Q3 SYS_FM <2> CALL SHIFTDISP ; Q4 SYS_MW <3> CALL SHIFTDISP ; Q5 SYS_LW <4> CALL SHIFTDISP ; Q6 SYS_PU <5> CALL SHIFTDISP ; Q7 SYS_Relay <6> CALL SHIFTDISP ; Q8 SYS_TAPE <7> MOVFF MEM,DATABUF ; Retrieve memory display pointer MOVLW 0x0F ANDWF DATABUF,F ; Sanitize pointer RLNCF DATABUF,W ; Adjust for two byte steps in table CALL BCD7SEG MOVWF DATABUF CALL SHIFTDISP ; Q9 a6 MEM<0> CALL SHIFTDISP ; Q10 b6 MEM<1> CALL SHIFTDISP ; Q11 c6 MEM<2> CALL SHIFTDISP ; Q12 d6 MEM<3> CALL SHIFTDISP ; Q13 e6 MEM<4> CALL SHIFTDISP ; Q14 f6 MEM<5> CALL SHIFTDISP ; Q15 g6 MEM<6> CALL SHIFTDISP ; Q16 (none) MEM<7> CALL SHIFTONE ; A BCF DLEN1 BCF DATA CALL DEL10US BSF CLB CALL DEL10US BCF CLB ; Load pulse CALL DEL200US ; Wait 200 µs RETURN DO_SPLASH DECFSZ COUNTDOWN,F RETFIE BTFSS Splash1 ; POLO in display? BRA TEST_Splash2 ; No MOVLW 0x05 MOVWF COUNTDOWN ; Show for 0.5 s MOVLW B'11111111' ; Show " " MOVWF D3 MOVLW B'10101011' ; Show "n" MOVWF D2 MOVLW B'10001000' ; Show "A" MOVWF D1 MOVLW B'11111011' ; Show "i" MOVWF D0 CALL UpdateDISP BCF Splash1 BSF Splash2 RETFIE TEST_Splash2 BTFSS Splash2 ; nAi in display? BRA TEST_Splash3 ; No MOVLW 0x05 MOVWF COUNTDOWN ; Show for 0.5 s MOVLW B'11111111' ; Show " " MOVWF D3 MOVLW B'01111111' ; Show "." MOVWF D2 MOVLW B'10010010' ; Show "S" MOVWF D1 MOVLW B'10000110' ; Show "E" MOVWF D0 CALL UpdateDISP BCF Splash2 BSF Splash3 RETFIE TEST_Splash3 BTFSS Splash3 ; .SE in display? BRA End_Splash ; No MOVLW 0x07 MOVWF COUNTDOWN ; Show for 0.7 s MOVLW B'11111111' ; Show " " MOVWF D3 MOVLW B'01111001' ; Show "1." MOVWF D2 MOVLW B'11000000' ; Show "0" MOVWF D1 MOVLW B'10100100' ; Show "2" MOVWF D0 CALL UpdateDISP BCF Splash3 RETFIE End_Splash BCF Splash ; Switch off splash message BSF ShowFreq ; Tuner function active BSF MUTE ; Unmute IC6062 (TDA1029) CALL FREQDISPLAY BSF INTCON,RBIE ; Enable PORTB<7:4> change interrupt RETFIE SAVE_FM MOVFF FREQHI,FMHISAV MOVFF FREQLO,FMLOSAV RETURN SAVE_MW MOVFF FREQHI,MWHISAV MOVFF FREQLO,MWLOSAV RETURN SAVE_LW MOVFF FREQHI,LWHISAV MOVFF FREQLO,LWLOSAV RETURN RESTORE_FM MOVFF FMHISAV,FREQHI MOVFF FMLOSAV,FREQLO RETURN RESTORE_MW MOVFF MWHISAV,FREQHI MOVFF MWLOSAV,FREQLO RETURN RESTORE_LW MOVFF LWHISAV,FREQHI MOVFF LWLOSAV,FREQLO RETURN SAVESTATE ; Gets polled every 25.6 s from Timer0 interrupt (SAVESTATECTR) MOVF FREQLO,W SUBWF SAVEDSTATE,W BTFSC STATUS,Z ; Frequency changed? (lower byte) RETURN ; No MOVFF FREQLO,SAVEDSTATE BSF TETS ; Test LED ON CLRF EEADR ; Write FREQHI to EEPROM at address 0x00 MOVFF FREQHI,EEDATA BCF EECON1,EEPGD ; Point to DATA memory BCF EECON1,CFGS ; Access EEPROM BSF EECON1,WREN ; Enable writes MOVLW 0x55 MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1,WR BTFSC EECON1,WR ; EEPROM write completed? BRA $-2 ; No INCF EEADR ; Write FREQLO to EEPROM at address 0x01 MOVFF FREQLO,EEDATA MOVLW 0x55 MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1,WR BTFSC EECON1,WR ; EEPROM write completed? BRA $-2 ; No INCF EEADR ; Write SYS to EEPROM at address 0x02 MOVFF SYS,EEDATA MOVLW 0x55 MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1,WR BTFSC EECON1,WR ; EEPROM write completed? BRA $-2 ; No BCF EECON1,WREN ; Disable writes on write complete BCF TETS RETURN EEPROM_WRITE BSF TETS ; Test LED ON MOVFF MEM,EEADR ; Get EEPROM memory location RLNCF EEADR RLNCF EEADR ; Multiply by four MOVFF FREQHI,EEDATA BCF EECON1,EEPGD ; Point to DATA memory BCF EECON1,CFGS ; Access EEPROM BSF EECON1,WREN ; Enable writes MOVLW 0x55 MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1,WR BTFSC EECON1,WR ; EEPROM write completed? BRA $-2 ; No INCF EEADR ; Write FREQLO to EEPROM at next address MOVFF FREQLO,EEDATA MOVLW 0x55 MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1,WR BTFSC EECON1,WR ; EEPROM write completed? BRA $-2 ; No INCF EEADR ; Write SYS to EEPROM at next address MOVFF SYS,EEDATA MOVLW 0x55 MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1,WR BTFSC EECON1,WR ; EEPROM write completed? BRA $-2 ; No BCF EECON1,WREN ; Disable writes on write complete BCF TETS RETURN EEPROM_READ ; Read three bytes from EEPROM with MEM as pointer BSF TETS MOVF MEM ; Read FREQHI from EEPROM (first byte) MOVWF EEADR ; Data Memory Address RLNCF EEADR RLNCF EEADR ; Multiply by four BCF EECON1,EEPGD ; Point to DATA memory BCF EECON1,CFGS ; Access EEPROM BSF EECON1,RD ; EEPROM Read MOVF EEDATA,W MOVWF FREQHI INCF EEADR,F ; Data Memory Address (FREQLO) BCF EECON1,EEPGD ; Point to DATA memory BCF EECON1,CFGS ; Access EEPROM BSF EECON1,RD ; EEPROM Read MOVF EEDATA,W MOVWF FREQLO INCF EEADR,F ; Data Memory Address (SYS) BCF EECON1,EEPGD ; Point to DATA memory BCF EECON1,CFGS ; Access EEPROM BSF EECON1,RD ; EEPROM Read MOVF EEDATA,W MOVWF SYS ; Restore function BCF TETS RETURN ReadDiodes MOVLW B'11111110' ; Scan P10 MOVWF PORTC BTFSS IF ; D6049 installed? BRA D6049 ; Yes, set FM IF to 10.76 MHz MOVLW B'11111101' ; Scan P11 MOVWF PORTC BTFSS IF ; D6048 installed? BRA D6048 ; Yes, set FM IF to 10.73 MHz MOVLW B'11111011' ; Scan P12 MOVWF PORTC BTFSS IF ; D6046 installed? BRA D6046 ; Yes, set FM IF to 10.67 MHz MOVLW B'11110111' ; Scan P13 MOVWF PORTC BTFSS IF ; D6045 installed? BRA D6045 ; Yes, set FM IF to 10.64 MHz MOVLW 0x3E ; No diodes, set to 10.70 MHz MOVWF FM_IF100 MOVLW 0x56 MOVWF FM_IF90 MOVLW 0x6E MOVWF FM_IF80 MOVLW 0x5D MOVWF FM_BE_HI ; High band edge (low byte) MOVLW 0x5C MOVWF FM_BE_LO ; Low band edge (low byte) BRA SCAN_AM_IF D6049 ; 10.76 MHz MOVLW 0x44 MOVWF FM_IF100 MOVLW 0x5C MOVWF FM_IF90 MOVLW 0x74 MOVWF FM_IF80 MOVLW 0x63 MOVWF FM_BE_HI ; High band edge (low byte) MOVLW 0x62 MOVWF FM_BE_LO ; Low band edge (low byte) BRA SCAN_AM_IF D6048 ; 10.73 MHz MOVLW 0x41 MOVWF FM_IF100 MOVLW 0x59 MOVWF FM_IF90 MOVLW 0x71 MOVWF FM_IF80 MOVLW 0x60 MOVWF FM_BE_HI ; High band edge (low byte) MOVLW 0x5F MOVWF FM_BE_LO ; Low band edge (low byte) BRA SCAN_AM_IF D6046 ; 10.67 MHz MOVLW 0x3B MOVWF FM_IF100 MOVLW 0x53 MOVWF FM_IF90 MOVLW 0x6B MOVWF FM_IF80 MOVLW 0x5A MOVWF FM_BE_HI ; High band edge (low byte) MOVLW 0x59 MOVWF FM_BE_LO ; Low band edge (low byte) BRA SCAN_AM_IF D6045 ; 10.64 MHz MOVLW 0x38 MOVWF FM_IF100 MOVLW 0x50 MOVWF FM_IF90 MOVLW 0x68 MOVWF FM_IF80 MOVLW 0x57 MOVWF FM_BE_HI ; High band edge (low byte) MOVLW 0x56 MOVWF FM_BE_LO ; Low band edge (low byte) ; Done scan FM IF SCAN_AM_IF MOVLW B'11101111' ; Scan P14 MOVWF PORTC BTFSS IF ; D6044 installed? BRA D6044 ; Yes, set AM IF to 468 kHz MOVLW B'11011111' ; Scan P15 MOVWF PORTC BTFSS IF ; D6042 installed? BRA D6042 ; Yes, set AM IF to 455 kHz MOVLW B'10111111' ; Scan P16 MOVWF PORTC BTFSS IF ; D6041 installed? BRA D6041 ; Yes, set AM IF to 452 kHz MOVLW 0x98 MOVWF AM_IFLO ; No diodes installed, preload AM IF frequency to 460 kHz MOVLW 0x3F MOVWF MW_BE_HI ; MW high band edge (low byte) MOVLW 0xA8 MOVWF MW_BE_LO ; MW Low band edge (low byte) MOVLW 0xC7 MOVWF LW_BE_HI ; LW high band edge (low byte) MOVLW 0xC4 MOVWF LW_BE_LO ; LW low band edge (low byte) BRA End_ReadDiodes D6041 MOVLW 0x88 MOVWF AM_IFLO ; Preload AM IF frequency 452 kHz MOVLW 0x2F MOVWF MW_BE_HI ; MW high band edge (low byte) MOVLW 0x98 MOVWF MW_BE_LO ; MW Low band edge (low byte) MOVLW 0xB7 MOVWF LW_BE_HI ; LW high band edge (low byte) MOVLW 0xB4 MOVWF LW_BE_LO ; LW low band edge (low byte) BRA End_ReadDiodes D6042 MOVLW 0x8E MOVWF AM_IFLO ; Preload AM IF frequency 455 kHz MOVLW 0x35 MOVWF MW_BE_HI ; MW high band edge (low byte) MOVLW 0x9E MOVWF MW_BE_LO ; MW Low band edge (low byte) MOVLW 0xBD MOVWF LW_BE_HI ; LW high band edge (low byte) MOVLW 0xBA MOVWF LW_BE_LO ; LW low band edge (low byte) BRA End_ReadDiodes D6044 MOVLW 0xA8 MOVWF AM_IFLO ; Preload AM IF frequency 468 kHz MOVLW 0x4F MOVWF MW_BE_HI ; MW high band edge (low byte) MOVLW 0xB8 MOVWF MW_BE_LO ; MW Low band edge (low byte) MOVLW 0xD7 MOVWF LW_BE_HI ; LW high band edge (low byte) MOVLW 0xD4 MOVWF LW_BE_LO ; LW low band edge (low byte) End_ReadDiodes MOVLW B'11111111' ; Unscan Port C MOVWF PORTC RETURN ; End interrupts ORG 0xF00000 ; EEPROM preload DE 0x2C, 0xA5, B'11111011', 0x00 ;103.60 with offset because of weird RBIF bug in my code DE 0x05, 0x14, B'11101111', 0x00 ;P1 - 198 kHz DE 0x2A, 0x76, B'11111011', 0x00 ;P2 - 98.00 MHz DE 0x0D, 0xF0, B'11110111', 0x00 ;P3 - 1332 kHz DE 0x27, 0x9C, B'11111011', 0x00 ;P4 - 90.70 MHz DE 0x27, 0xC4, B'11111011', 0x00 ;P5 - 91.10 MHz DE 0x2C, 0xA6, B'11111011', 0x00 ;P6 - 103.60 MHz DE 0x29, 0xB8, B'11111011', 0x00 ;P7 - 96.10 MHz DE 0x2B, 0x66, B'11111011', 0x00 ;P8 - 100.40 MHz DE 0x2E, 0x5D, B'11111011', 0x00 ;P9 - 107.99 MHz END