; ; intr.asm - Interrupt source code file ; ; This file contains several useful interrupt routines that ; you might find useful to adapt. These routines are written ; for the 68HC812A4, but should also work on most versions ; of the 68HC12. ; ; Interrupts currently serviced include: ; ; TOC0 - Timer output compare 0 is used to generate ; a square wave ; ; RTI - The Real Time Interrupt is used to vary ; the width of the square wave ; ; ; History: ; 09-15-98 KevinRo@nwlink.com ; Initial version written. ; ; ; The following are port definitions. You can save them to a ; file and use them in your own programs. I recommend using a ; file called hc12regs.inc that you can include with the following ; ; #include hc32regs.inc ; ; I stuck them into this file to keep the number of files ; low ; ;****************************************************************** PORTA equ $00 ;Port A Data PORTB equ $01 ;Port B Data DDRA equ $02 ;Port A Data Direction DDRB equ $03 ;Port B Data Direction PORTC equ $04 ;Port C Data PORTD equ $05 ;Port D Data DDRC equ $06 ;Port C Data Direction DDRD equ $07 ;Port D Data Direction PORTE equ $08 ;Port E Data DDRE equ $09 ;Port E Data Direction PEAR equ $0A ;Port E Assigment MODE equ $0B ;Mode PUCR equ $0C ;Pull Up Control RDRIV equ $0D ;Reduced Drive INITRM equ $10 ;RAM Position INITRG equ $11 ;Register Position INITEE equ $12 ;EEPROM Position MISC equ $13 RTICTL equ $14 ;Real Time Interrupt Control RTIFLG equ $15 ;Real Time Interrupt Flag COPCTL equ $16 ;COP Control COPRST equ $17 ;Arm/Reset COP Timer ITST0 equ $18 ;Internal Test 0 ITST1 equ $19 ;Internal Test 1 ITST2 equ $1A ;Internal Test 2 ITST3 equ $1B ;Internal Test 3 INTCR equ $1E ;Interrupt Control HPRIO equ $1F ;Highest Priority Interrupt KWIED equ $20 ;Key Wakeup Port D Interrupt Enable KWIFD equ $21 ;Key Wakeup Port D Flag RES22 equ $22 ;Reserved on 812A4 RES23 equ $23 ;Reserved on 812A4 PORTH equ $24 ;Port H Data DDRH equ $25 ;Port H Data Direction KWIEH equ $26 ;Key Wakeup Port H Interrupt Enable KWIFH equ $27 ;Key Wakeup Port H Flag PORTJ equ $28 ;Port J Data DDRJ equ $29 ;Port J Data Direction KWIEJ equ $2A ;Key Wakeup Port J Interrupt Enable KWIFJ equ $2B ;Key Wakeup Port J Flag KPOLJ equ $2C ;Key Wakeup Port J Polarity PUPSJ equ $2D ;Key Wakeup Port J Pull-up / Pull-down Select PULEJ equ $2E ;Key Wakeup Port J Pull-up / Pull-down Enable RES2F equ $2F ;Reserved on 812A4 PORTF equ $30 ;Port F Data PORTG equ $31 ;Port G Data DDRF equ $32 ;Port F Data Direction DDRG equ $33 ;Port G Data Direction DPAGE equ $34 ;Data Page PPAGE equ $35 ;Program Page EPAGE equ $36 ;Extra Page WINDEF equ $37 ;Window Definition MXAR equ $38 ;Memory Expansion Assignment RES39 equ $39 ;Reserved on 812A4 RES3A equ $3A ;Reserved on 812A4 RES3B equ $3B ;Reserved on 812A4 CSCTL0 equ $3C ;Chip Select Control 0 CSCTL1 equ $3D ;Chip Select Control 1 CSSTR0 equ $3E ;Chip Select Stretch 0 CSSTR1 equ $3F ;Chip Select Stretch 1 LDV equ $40 ;Loop Divider LDVL equ $41 ;Loop Divider Low Byte RDV equ $42 ;Reference Divider RVDL equ $43 ;Reference Divider Low Byte CLKCTL equ $47 ;Clock Control ; $48-$5F are all reserved on the 812A4 ATDCTL0 equ $60 ;Reserved ATDCTL1 equ $61 ;Reserved ATDCTL2 equ $62 ;ATD Control 2 ATDCTL3 equ $63 ;ATD Control 3 ATDCTL4 equ $64 ;ATD Control 4 ATDCTL5 equ $65 ;ATD Control 5 ATDSTAT equ $66 ;ATD Status ATDSTATL equ $67 ;ATD Status Low Byte ATDTEST equ $68 ;ATD Test ATDTESTL equ $69 ;ATD Test Low Byte PORTAD equ $6F ;Port AD Data Input ; $6A - $6E are all reserved on the 812A4 ADR0 equ $70 ;A/D Converter Result0 RES71 equ $71 ;Reserved on 812A4 ADR1 equ $72 ;A/D Converter Result 1 RES73 equ $73 ;Reserved on 812A4 ADR2 equ $74 ;A/D Converter Result 2 RES75 equ $75 ;Reserved on 812A4 ADR3 equ $76 ;A/D Converter Result 3 RES77 equ $77 ;Reserved on 812A4 ADR4 equ $78 ;A/D Converter Result 4 RES79 equ $79 ;Reserved on 812A4 ADR5 equ $7A ;A/D Converter Result 5 RES7B equ $7B ;Reserved on 812A4 ADR6 equ $7C ;A/D Converter Result 6 RES7D equ $7D ;Reserved on 812A4 ADR7 equ $7E ;A/D Converter Result 7 RES7F equ $7D ;Reserved on 812A4 TIOS equ $80 ;Timer Input Capture/Output Compare Select CFORC equ $81 ;Timer Compare Force OC7M equ $82 ;Output Compare 7 Mask OC7D equ $83 ;Output Compare 7 Data TCNT equ $84 ;Timer Counter TCNTL equ $85 ;Timer Counter Low Byte TSCR equ $86 ;Timer System Control TQCR equ $87 ;Reserved TCTL1 equ $88 ;Timer Control 1 TCTL2 equ $89 ;Timer Control 2 TCTL3 equ $8A ;Timer Control 3 TCTL4 equ $8B ;Timer Control 4 TMSK1 equ $8C ;Timer Interrupt Mask 1 TMSK2 equ $8D ;Timer Interrupt Mask 2 TFLG1 equ $8E ;Timer Interrupt Flag 1 TFLG2 equ $8F ;Timer Interrupt Flag 2 TC0 equ $90 ;TIC/TOC 0 TC0L equ $91 ;TIC/TOC 0 Low Byte TC1 equ $92 ;TIC/TOC 1 TC1L equ $93 ;TIC/TOC 1 Low Byte TC2 equ $94 ;TIC/TOC 2 TC2L equ $95 ;TIC/TOC 2 Low Byte TC3 equ $96 ;TIC/TOC 3 TC3L equ $97 ;TIC/TOC 3 Low TC4 equ $98 ;TIC/TOC 4 TC4L equ $99 ;TIC/TOC 4 Low Byte TC5 equ $9A ;TIC/TOC 5 TC5L equ $9B ;TIC/TOC 5 Low Byte TC6 equ $9C ;TIC/TOC 6 TC6L equ $9D ;TIC/TOC 6 Low Byte TC7 equ $9E ;TIC/TOC 7 TC7L equ $9F ;TIC/TOC 7 Low Byte PACTL equ $A0 ;Pulse Accumulator Control PAFLG equ $A1 ;Pulse Accumulator Flag PACNT equ $A2 ;Pulse Accumulator Count PACNTL equ $A3 ;Pulse Accumulator Counter Low Byte ; $A4 - AC are reserved on 812A4 TIMTST equ $AD ;Timer Test PORTT equ $AE ;Timer Port T Data DDRT equ $AF ;Timer Port T Data Direction ; $B0 - BF are reserved on 812A4 SC0BD equ $C0 ;SCI 0 Baud Rate SC0BDL equ $C1 ;SCI 0 Baud Rate Low Byte SC0CR1 equ $C2 ;SCI 0 Control 1 SC0CR2 equ $C3 ;SCI 0 Control 2 SC0SR1 equ $C4 ;SCI 0 Status 1 SC0SR2 equ $C5 ;SCI 0 Status 2 SC0DR equ $C6 ;SCI 0 Data SC0DRL equ $C7 ;SCI 0 Data Low Byte SC1BD equ $C8 ;SCI 1 Baud Rate SC1BDL equ $C9 ;SCI 1 Baud Rate Low Byte SC1CR1 equ $CA ;SCI 1 Control 1 SC1CR2 equ $CB ;SCI 1 Control 2 SC1SR1 equ $CC ;SCI 1 Status 1 SC1SR2 equ $CD ;SCI 1 Status 2 SC1DR equ $CE ;SCI 1 Data SC1DRL equ $CF ;SCI 1 Data Low Byte SP0CR1 equ $D0 ;SPI 0 Control 1 SP0CR2 equ $D1 ;SPI 0 Control 2 SP0BR equ $D2 ;SPI 0 Baud Rate SP0SR equ $D3 ;SPI 0 Status SP0DR equ $D5 ;SPI 0 Data RESD4 equ $D4 ;Reserved on 812A4 PORTS equ $D6 ;Port S Data DDRS equ $D7 ;Port S Data Direction ; $D8 - EF are reserved on 812A4 EEMCR equ $F0 ;EEPROM Module Configuration EEPROT equ $F1 ;EEPROM Block Protect EETST equ $F2 ;EEPROM Test EEPROG equ $F3 ;EEPROM Control ; $F4 - $1FF are reserved ;****************************************************************** ;****************************************************************** ; Declaring some storage space. Using the 'ds' instruction allocates ; the bytes, but doesn't try to initialize them like the 'db' or 'dw' ; instructions. This is mighty important since you don't want to ; initialize RAM variables! If you do, they will not be valid when ; you try to use them after power cycling. Worse yet, if you do ; and you are trying to program Flash EEPROM, you will crash the ; little RAM based program that writes the Flash! ; ; RAM is mapped from $0800 - $0BFF ; ORG $0800 tStartOfTimers equ * ; This defines a block of tRtiDelay rmb 2 ; WORD sized variables that tSndTimer rmb 2 ; Sound timer tEndOfTimers equ * ; interrupt handler wCurrentFrequency rmb 2 wCurrentSong rmb 2 CHANGE_WAVE_INCR equ 50 PRINT_RATE equ 2000 ; Print every 2 seconds org $0 SNG_FREQ rmb 2 SNG_DURATION rmb 2 SNG_SIZE equ * ;****************************************************************** ; The ORG statement is set to $F000, which is the default starting ; address for the 4k of EEPROM on the 68HC812A4. ; ORG $F000 ; ; Song Table: Each song is made of a list of note/duration pairs ; The note is the frequency measured in clock cycles, ; the duration is measured in RTI cycles, which are assumed ; to be 1ms in this program. Each value is 16 bits in length ; FRQMHZ equ 16000 * In KHZ ECLOCK equ FRQMHZ/2 * in KHZ TC_1US equ ECLOCK/1000 TC_10US equ ECLOCK/100 TC_100US equ ECLOCK/10 TC_1MS equ ECLOCK NOTE_NONE equ 0 NOTE_A equ 2272*TC_1US/2 ; * 440 Hz NOTE_A_ equ 2145*TC_1US/2 ; * 466.2 Hz NOTE_B equ 2025*TC_1US/2 ; * 493.9 Hz NOTE_C equ 1911*TC_1US/2 ; * 523.3 Hz NOTE_C_ equ 1804*TC_1US/2 ; * 554.4 Hz NOTE_D equ 1702*TC_1US/2 ; * 587.4 Hz NOTE_D_ equ 1607*TC_1US/2 ; * 622.4 Hz NOTE_E equ 1517*TC_1US/2 ; * 659.4 Hz NOTE_F equ 1431*TC_1US/2 ; * 698.6 Hz NOTE_F_ equ 1351*TC_1US/2 ; * 740.2 Hz NOTE_G equ 1275*TC_1US/2 ; * 784.2 Hz NOTE_G_ equ 1204*TC_1US/2 ; * 830.9 Hz NOTE_A2 equ 1136*TC_1US/2 ; * 880.0 Hz NOTE_A2_ equ 1072*TC_1US/2 ; * 932.33 NOTE_B2 equ 1012*TC_1US/2 ; * 987.77 NOTE_C2 equ 955*TC_1US/2 ; * 1046.5 NOTE_C2_ equ 901*TC_1US/2 ; * 1108.7 NOTE_D2 equ 851*TC_1US/2 ; * 1174.7 NOTE_D2_ equ 803*TC_1US/2 ; * 1244.5 NOTE_E2 equ 758*TC_1US/2 ; * 1318.5 NOTE_F2 equ 715*TC_1US/2 ; * 1396.9 NOTE_F2_ equ 675*TC_1US/2 ; * 1480.0 NOTE_G2 equ 637*TC_1US/2 ; * 1568.0 NOTE_G2_ equ 601*TC_1US/2 ; * 1661.2 NOTE_A3 equ 568*TC_1US/2 ; * 1760.0 NOTE_A3_ equ 536*TC_1US/2 ; * 1864.7 NOTE_B3 equ 506*TC_1US/2 ; * 1975.5 NT_WHOLE equ 1024 ; whole note is 1 second long NT_HALF equ NT_WHOLE/2 NT_4th equ NT_WHOLE/4 NT_8th equ NT_WHOLE/8 NT_16th equ NT_WHOLE/16 NT_32nd equ NT_WHOLE/32 NT_64th equ NT_WHOLE/64 NT_128th equ NT_WHOLE/128 sngSong1 fdb NOTE_E,NT_4th fdb NOTE_E,NT_4th fdb NOTE_E,NT_4th fdb NOTE_C,NT_WHOLE fdb NOTE_NONE,NT_4th fdb NOTE_D,NT_4th fdb NOTE_D,NT_4th fdb NOTE_D,NT_4th fdb NOTE_B,NT_WHOLE fdb 0,0 sngEntertain fdb NOTE_D_,NT_8th fdb NOTE_D,NT_8th fdb NOTE_E,NT_4th fdb NOTE_C2,NT_8th fdb NOTE_E,NT_8th fdb NOTE_C2,NT_4th fdb NOTE_E,NT_4th fdb NOTE_C2,NT_4th*3 fdb NOTE_D2_,NT_8th fdb NOTE_D2,NT_8th fdb NOTE_E2,NT_8th fdb NOTE_C2,NT_8th fdb NOTE_D2,NT_4th fdb NOTE_E2,NT_8th fdb NOTE_B2,NT_8th fdb NOTE_D2,NT_4th fdb NOTE_C2,NT_4th*3 fdb 0,0 Start: ; ; Set the top of the stack. Using $0C00 is OK, because the 68HC12 ; decrements BEFORE pushing. Many people set it to $BFF, but having ; a word aligned stack is better. ; lds #$0C00 ; ; The 68HC12, unlike the 68HC11, defaults with the COP turned on. ; The COP is a timer function that requires the software to ; periodically write a specific pair of values to a register. If ; this isn't done in time, the processor will reset every 1.04 ; seconds or so. ; ; For this module, the COP is going to be disabled. The following ; line disables the COP by setting the COP Watchdog Rate to zero ; clr COPCTL ; Store zero in COP Control Register jsr serial_init ldx #strProgStart jsr outstr ; ; Enable the RTI interrupts. The $81 enables interrupts, and sets ; the period for the interrupts to 1.024 milliseconds on a system ; with a 8MHZ ECLOCK ; ldaa #$81 staa RTICTL ; ; Setup timer system ; ldaa #$80 ; Timer Enable staa TSCR ldaa #$01 ; Set to toggle bit on compare staa TIOS ; Set TC0 to be an output compare ; The rest of the setup is done in the RTI ; routine ; ; Initialization is now complete. We can now enable interrupts! ; cli ldx #sngSong1 stx wCurrentSong MainLoop: ldd #PRINT_RATE ; Delay approx 2 seconds std tRtiDelay MinorLoop: ; ; Insert whatever code you would like to put here. Once it is ; done, the code below will execute. Only if the countdown ; timer tRtiDelay is zero will it print then call the main ; loop. ; jsr getchar cmpa #'p' bne ML5 jsr putchar ldx #sngEntertain stx wCurrentSong ML5 tst tRtiDelay bne MinorLoop ldx #strProgRunning jsr outstr bra MainLoop ;****************************************************************** ; The timer interrupt routine for TOC0 ; int_Timer_0 ; ; First thing, acknowledge the interrupt ; bset TFLG1,#$01 ; ; TC0 holds the 16 bit value that compared. Add the ; period to TC0 derives the next value to that needs an ; interrupt ; ldd TC0 addd wCurrentFrequency std TC0 rti ;****************************************************************** ; The Real Time Interrupt is serviced by walking the list of timers ; found starting at tStartOfTimers. For each timer, which is a word ; in length, decrement the counter by one unless the timer is already ; zero ; int_Real_Time_Int bset RTIFLG,#$80 ; Acknowledge Interrupt ldx #tStartOfTimers RTI_LOOP cpx #tEndOfTimers beq RTI_ROUTINES ldd 2,x+ ; Notice the autoincrement beq RTI_LOOP subd #1 ; Decrement the value by 1 std -2,x ; Store at the previous location bra RTI_LOOP ; ; Some of the timers represent routines that need to be called ; on a regular basis. We have already counted down these ; timers. Now, for each timer that is zero, call the subroutine ; that handles the function. This is very useful if you need ; to have some function handled on a regular basis ; RTI_ROUTINES ; ; Now that the counters have decremented, check to see if there ; is any work to do in the music department ; ldx wCurrentSong ; Do we have a song? beq RTI_NOTE_OFF ldy tSndTimer ; We have song, time for new note? bne RTI_END ; Branch if not ldd SNG_DURATION,x beq RTI_END_SONG ; ; If the current note is non-zero, then we insert a minor ; pause between notes. This gives us the beat we are looking for ; between notes. Otherwise, it may just sound like a continuous ; long beep. ; ldy wCurrentFrequency beq RTI_NEXT_NOTE ldd #0 ; Play a silent note for a very short duration std wCurrentFrequency ldd #NT_64th std tSndTimer bra RTI_NOTE_OFF RTI_NEXT_NOTE std tSndTimer ldd SNG_FREQ,x ; Load the next frequency leax SNG_SIZE,x ; Update the X pointer to next note stx wCurrentSong std wCurrentFrequency beq RTI_NOTE_OFF ; If freq zero, stop output ; ; Turn the note on ; addd TCNT std TC0 bset TCTL2,#$01 bset TMSK1,#$01 rti RTI_END_SONG ldd #$0 std wCurrentSong RTI_NOTE_OFF ; ; Turn off the interrupt and output driver. This is done ; whenever wCurrentSong is zero, or when silence is the note. ; That allows the main program ; loop to just set wCurrentSong to zero to stop the music ; bclr TMSK1,#$01 ; Stop interrupts bset TFLG1,#$01 bclr TCTL2,#$01 ; Disconnect driver RTI_END rti ;*************************************** ; Test program strings ; strProgStart: db 13,10 fcc "Sound Interrupt Program Started" db 13,10,0 strProgRunning fcc "Still Running!" db 13,10 fcc "Press 'p' to play song" db 13,10,0 CRLF: db 13,10,0 ;************************************************************************* ; ; Some serial port routines. serial_init: ; ; To use the serial port, one must set the baud rate. The 68HC12 ; is capable of speeds up to 38400. Here, we set it to 19200 ; baud. The value here is a 16 bit divisor. ; ldd #26 ; Value from Baud Rate Generation Table std SC0BD ldaa #$0C ; Enable transceiver staa SC0CR2 rts ; ; putchar outputs a character to serial port 0 ; Call with character in register A ; putchar: brclr SC0SR1,#$80,putchar staa SC0DRL rts ; ; getchar retrieves a character from serial port 0 ; Returns with character in A ; getchar ldaa #0 brclr SC0SR1,#$20,getchar2 ldaa SC0DRL getchar2 rts ; ; ; outstr outputs a NULL terminated character string ; On input, register X points to string in memory. Note the use of ; the index auto post increment addressing mode! ; outstr0: jsr putchar outstr: ldaa 1,X+ ; Auto increment X by one bne outstr0 ; If not a NULL character, send it rts ;************************************************************************* ; ; Interrupt vectors. When the CPU starts, or encounters an interrupt, it ; will read this table to determine where to jump to in the code. ; ; Note: If you are using the 5G18E pre production mask, there was an ; error in the addressing of the interrupt vectors. Get a newer part, as ; there were plenty of other errors on that chip as well. ; vec_Unexpected: bgnd ldx _int_Reset jmp [0,x] ; Must start at this specific address ORG $FFCE _int_Key_Wakeup_H dw vec_Unexpected _int_Key_Wakeup_J dw vec_Unexpected _int_ATD dw vec_Unexpected _int_SCI1 dw vec_Unexpected _int_SCI0 dw vec_Unexpected _int_SPI_STC dw vec_Unexpected _int_PAIE dw vec_Unexpected _int_PAO dw vec_Unexpected _int_Timer_Overflow dw vec_Unexpected _int_Timer_7 dw vec_Unexpected _int_Timer_6 dw vec_Unexpected _int_Timer_5 dw vec_Unexpected _int_Timer_4 dw vec_Unexpected _int_Timer_3 dw vec_Unexpected _int_Timer_2 dw vec_Unexpected _int_Timer_1 dw vec_Unexpected _int_Timer_0 dw int_Timer_0 _int_Real_Time_Int dw int_Real_Time_Int _int_IRQ_Key_Wakeup_D dw vec_Unexpected _int_XIRQ dw vec_Unexpected _int_SWI dw vec_Unexpected _int_UIT dw vec_Unexpected _int_COP_Failure dw vec_Unexpected _int_COP_Clock_Monitor_Fail dw vec_Unexpected _int_Reset dw Start end