.nolist ;Suppress listing of include file .include "4414def.inc" ;Define chip particulars .list ;***** Global register variables .def wreg =R16 ;General use working register .def wreg2 =r17 .def timeout =R18 ;Timeout value passed to subroutine .def lcdstat =R19 ;LCD busy/wait status .def longtime=R20 ;Long timer for powerup .def butnum =r21 ;final press value .def maybe =r24 .def state =R22 .def playing =r23 .def key =r25 .def notemp =r26 .def rcdcnt =r09 .def charcnt =r01 .def butprot =r02 .def lastZL =r03 .def lastZH =r04 .def ZLtemp =r05 .def ZHtemp =r06 .def initialL=r07 .def initialH=r08 .def play1 =r10 .def notenum =r11 .def r0temp1 =r12 .def ZLtemp1 =r13 .def ZHtemp1 =r14 .def r0temp =r15 .def lttemp =r27 ;***** Other equates .equ lcdrs =PD6 ;LCD rs pin connected to PD6 .equ lcdrw =PD5 ;LCD r/w pin connected to PD5 .equ lcde =PD4 ;LCD enable pin connected to PD4 ; Timer/Counter prescaler values .equ TSTOP =0 ;Stop Timer/Counter .equ TCK1 =1 ;Timer/Counter runs from CK .equ TCK8 =2 ;Timer/Counter runs from CK / 8 .equ TCK64 =3 ;Timer/Counter runs from CK / 64 .equ TCK256 =4 ;Timer/Counter runs from CK / 256 .equ TCK1024 =5 ;Timer/Counter runs from CK / 1024 .equ TEXF =6 ;Timer/Counter runs from external falling edge .equ TEXR =7 ;Timer/Counter runs from external rising edge .dseg rcdnts: .byte 110 .cseg ; Interrupt vectors .org $0000 rjmp RESET ;reset entry vector reti reti reti reti rjmp CMP1B_MATCH reti rjmp t0int reti reti reti reti reti ; Main program entry point on reset ;The following table is used to convert raw button-press high/low ;values to a sequence number. Invalid codes caused by multiple ;button presses are ignored. keytbl: .db 0b11101110, 0b11011110, 0b10111110, 0b01111110 .db 0b11101101, 0b11011101, 0b10111101, 0b01111101 .db 0b11101011, 0b11011011, 0b10111011, 0b01111011 .db 0b11100111, 0b11010111, 0b10110111, 0b01110111 addtbl: .db 0x00, C4samtbl, 0x00, Cs4samtbl .db 0x00, D4samtbl, 0x01, Ds4samtbl .db 0x01, E4samtbl, 0x01, F4samtbl .db 0x01, Fs4samtbl, 0x02, G4samtbl .db 0x02, Gs4samtbl, 0x02, A4samtbl .db 0x02, As4samtbl, 0x02, B4samtbl .db 0x03, C5samtbl, 0x03, Cs5samtbl .db 0x03, D5samtbl, 0x03, Ds5samtbl .db 0x03, E5samtbl, 0x03, F5samtbl .db 0x03, Fs5samtbl, 0x03, G5samtbl .db 0x04, Gs5samtbl, 0x00, 0x00 .include "tune.inc" reset: ldi wreg, LOW(RAMEND) ;setup stack pointer out SPL, wreg ldi wreg, HIGH(RAMEND) out SPH, wreg ldi wreg, 0x0a out TCCR1B, wreg ldi wreg, 0x00 out OCR1AH, wreg out OCR1BH, wreg ldi wreg, 0x1a out OCR1AL, wreg ldi wreg, 0x19 out OCR1BL, wreg ldi wreg, 0xff out DDRC, wreg clr wreg out PORTC, wreg ldi wreg, 0x00 out TCCR1A, wreg clr playing ;ldi wreg, 0xff ;setup buttons on proto-board ;out DDRB, wreg ;set PortB to be inputs ;out PINB, wreg ;make sure all the buttons are up ; ************** start LCD startup *********************************** ldi wreg,TSTOP ;Timer 0 off (just in case) out TCCR0,wreg ;Stop timer ldi wreg,0b01100010 ;Enable Timer 0 interrupt out TIMSK,wreg sei ;Enable interrupts ;power down the LCD ldi wreg, 0xff ;LCD power connection out DDRA, wreg ;power it down after reset ldi wreg, 0x00 out PORTA,wreg ldi longtime,100 ; then Wait 1.5 second with LCD power off ldi timeout,0 ;Delay 15 mS offwait:rcall delay dec longtime brne offwait ;now power up LCD ldi wreg, 0xff out PORTA, wreg ldi longtime,100 ;Wait 1.5 second for LCD power up ldi timeout,0 ;Delay 15 mS puwait: rcall delay dec longtime brne puwait ; ************** end LCD startup *********************************** ;hopefully by now the LCD has rebooted rcall lcdinit start: ;instructions sent to LCD, scrolling rcall lcdclr ldi ZH,high(welcome*2);first message addr to Z-ptr ldi ZL,low(welcome*2);Init Z-pointer rcall wrtstr rcall bdelay mov wreg, butprot cpi wreg, 0x7F brne options default:rcall lcdclr ldi ZH,high(rulesa*2);first message addr to Z-ptr ldi ZL,low(rulesa*2);Init Z-pointer rcall wrtstr rcall bdelay mov wreg, butprot cpi wreg, 0x7F brne options rcall lcdclr ldi ZH,high(rulesb*2);first message addr to Z-ptr ldi ZL,low(rulesb*2);Init Z-pointer rcall wrtstr rcall bdelay mov wreg, butprot cpi wreg, 0x7F brne options rcall lcdclr ldi ZH,high(rulesc*2);first message addr to Z-ptr ldi ZL,low(rulesc*2);Init Z-pointer rcall wrtstr rcall bdelay mov wreg, butprot cpi wreg, 0x7F brne options rcall lcdclr ldi ZH,high(rulesd*2);first message addr to Z-ptr ldi ZL,low(rulesd*2);Init Z-pointer rcall wrtstr rcall bdelay mov wreg, butprot cpi wreg, 0x7F brne options rcall lcdclr ldi ZH,high(rulese*2);first message addr to Z-ptr ldi ZL,low(rulese*2);Init Z-pointer rcall wrtstr rcall bdelay mov wreg, butprot cpi wreg, 0x7F brne options rjmp default ;***** jump back to beginning if no button is pressed during scroll of rules ***** default1:rjmp default options:mov wreg, butprot cpi wreg, 0x7E ;single node option breq snglnde cpi wreg, 0x7D ;record option breq record cpi wreg, 0x7B ;memory play option breq memplay1 cpi wreg, 0x77 ;LCD play option breq LCDplay1 cpi wreg, 0x6F ;exit single note option breq endsngl1 cpi wreg, 0x5F ;exit LCD play option breq endLCD1 cpi wreg, 0x3F ;recorded tune breq rectune1 rjmp default ;treat all other pushes as nothing and loop back ;***** single note playback ***** snglnde:clr butprot rcall lcdclr ldi ZH,high(snglnt*2);first message addr to Z-ptr ldi ZL,low(snglnt*2);Init Z-pointer rcall wrtstr ldi wreg, 0x01 ;set flag in flags reg denoting single node state mov state, wreg ;nothing in state so mov is ok ser wreg out DDRB, wreg snglscn:rcall btdbce ;get a button push rcall play cpi butnum, 0xFF ;no recognizable button was pushed brne snglpsh ;branch to a single button press clr playing ;no button is down so stop playing note in wreg, PINA ;get the button push from the proto-board com wreg cpi wreg, 0x10 ;see if the exit single note button was pushed breq endsngl1 ;branch to end single note rjmp snglscn ;loop back and scan for a button push snglpsh:;rcall lcdclr mov wreg, playing ;get a copy of which note is playing cpi wreg, 0x00 ;see if the note is still being played brne snglscn ;if playing=0x00, then no note is being played mov wreg, butnum ;get which note is being played ;swap wreg ;shift value of note button into upper 4 bits of reg ;ori wreg, 0x0F ;set lower 4 bits to F to signify that a note is being played ;swap wreg ori wreg, 0x80 mov playing, wreg rcall play ;mov wreg, butnum ;subi wreg, -0x30 ;rcall lcdput rjmp snglscn ;jump back and scan for another button press ;***** end single note playback ***** memplay1:rjmp memplay2 LCDplay1:rjmp LCDplay2 endsngl1:rjmp endsngl2 endLCD1:rjmp endLCD2 rectune1:rjmp rectune2 record: clr butprot ldi wreg, 0x02 ;set flag in flags reg denoting record state mov state, wreg rcall lcdclr ldi ZH,high(rcdply*2);first message addr to Z-ptr ldi ZL,low(rcdply*2);Init Z-pointer rcall wrtstr clr rcdcnt ;count of how many notes had been played ldi ZL, LOW(rcdnts) ;ptr to RAM set up memory to store notes ldi ZH, HIGH(rcdnts) rcdng: mov wreg, rcdcnt cpi wreg, 100 ;limit of # of notes hit? breq rcddone ;if so branch to done rcall btdbce cpi butnum, 0xFF ;a keyboard button pressed? breq rcdopt st Z,butnum ;store note into RAM ;out PORTC, ZL inc ZL ;increment Z pointer inc rcdcnt ;increment count ;mov wreg, butnum ;subi wreg, -0x30 ;rcall lcdput mov wreg, butnum ;get which note is being played ;swap wreg ;shift value of note button into upper 4 bits of reg ;ori wreg, 0x0F ;set lower 4 bits to F to signify that a note is being played ;swap wreg ori wreg, 0x80 mov playing, wreg rcall play rjmp rcdng rcdopt: clr playing in wreg, PINA ;see if user wants to exit com wreg cpi wreg, 0x04 ;user wants to memory play breq memplay ;start memory play rjmp rcdng ;jump back memplay2:rjmp memplay LCDplay2:rjmp LCDplay endsngl2:rjmp endsngl endLCD2:rjmp endLCD rectune2:rjmp rectune rcddone:clr state clr playing ldi ZH,high(endrcd*2);first message addr to Z-ptr ldi ZL,low(endrcd*2);Init Z-pointer rcall wrtstr rcall bdelay ;delay so user can see rjmp default memplay:clr butprot ldi wreg, 0x04 ;set flag in flags reg denoting memory playback state mov state, wreg rcall lcdclr ldi ZH,high(memply*2);first message addr to Z-ptr ldi ZL,low(memply*2);Init Z-pointer rcall wrtstr ldi ZL, LOW(rcdnts) ;ptr to RAM set up memory to store notes ldi ZH, HIGH(rcdnts) rcall lcdclr mmplyng:mov wreg, rcdcnt cpi wreg, 0x00 breq mmdone ld wreg, Z ;get number from memory ;subi wreg, -0x30 ;rcall lcdput ;ld r0, Z ;out PORTC, ZL ;mov wreg, r0 inc ZL ;swap wreg ;move note # to upper 4 bits ;ori wreg, 0x0F ;note is now playing ;swap wreg ori wreg, 0x80 mov playing, wreg rcall play dec rcdcnt ;count of # of total notes stored rcall mdelay ;delay for certain time rjmp mmplyng mmdone: clr state clr playing clr rcdcnt rcall lcdclr ldi ZH,high(endmem*2);first message addr to Z-ptr ldi ZL,low(endmem*2);Init Z-pointer rcall wrtstr rcall bdelay ;delay so user can see rjmp default LCDplay:clr butprot ldi wreg, 0x08 ;set flag in flags reg denoting LCD playback state mov state, wreg rcall lcdclr ldi ZH,high(lcdply*2);first message addr to Z-ptr ldi ZL,low(lcdply*2);Init Z-pointer rcall wrtstr rcall bdelay rcall lcdclr ldi ZH, high(tunea*2) ldi ZL, low(tunea*2) clr playing clr charcnt mov wreg, charcnt cpi wreg, 15 breq lcdend1 lcdnxt: lpm tst R0 ;See if at end of message breq lcdend1 ;If so, next message mov wreg, charcnt cpi wreg,8 ;addressing changes at char #8! brne lcdwrtit ;at char 8, fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd rcall sdelay lcdwrtit:mov wreg,R0 ;Send it to the LCD rcall lcdput adiw ZL,1 ;Increment Z-pointer inc charcnt ;keep track of chars on display ldi wreg, 0x20 rcall lcdput inc charcnt rcall lcddelay mov wreg, butprot cpi wreg, 0x5F breq lcdends1 rjmp lcdnxt lcdend1:rcall lcdclr ldi ZH, high(tuneb*2) ldi ZL, low(tuneb*2) clr playing clr charcnt mov wreg, charcnt cpi wreg, 15 breq lcdend2 lcdnxt1:lpm tst R0 ;See if at end of message breq lcdend2 ;If so, next message mov wreg, charcnt cpi wreg,8 ;addressing changes at char #8! brne lcdwrt1 ;at char 8, fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd rcall sdelay lcdwrt1:mov wreg,R0 ;Send it to the LCD rcall lcdput adiw ZL,1 ;Increment Z-pointer inc charcnt ;keep track of chars on display ldi wreg, 0x20 rcall lcdput inc charcnt rcall lcddelay mov wreg, butprot cpi wreg, 0x5F breq lcdends rjmp lcdnxt1 lcdend2:rcall lcdclr ldi ZH, high(tunec*2) ldi ZL, low(tunec*2) clr playing clr charcnt mov wreg, charcnt cpi wreg, 15 breq lcdend3 lcdnxt2:lpm tst R0 ;See if at end of message breq lcdend3 ;If so, next message mov wreg, charcnt cpi wreg,8 ;addressing changes at char #8! brne lcdwrt2 ;at char 8, fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd rcall sdelay lcdwrt2:mov wreg,R0 ;Send it to the LCD rcall lcdput adiw ZL,1 ;Increment Z-pointer inc charcnt ;keep track of chars on display ldi wreg, 0x20 rcall lcdput inc charcnt rcall lcddelay mov wreg, butprot cpi wreg, 0x5F breq lcdends rjmp lcdnxt2 lcdends1:rjmp lcdends lcdend3:rcall lcdclr ldi ZH, high(tuned*2) ldi ZL, low(tuned*2) clr playing clr charcnt mov wreg, charcnt cpi wreg, 15 breq lcdends lcdnxt3:lpm tst R0 ;See if at end of message breq lcdends ;If so, next message mov wreg, charcnt cpi wreg,8 ;addressing changes at char #8! brne lcdwrt3 ;at char 8, fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd rcall sdelay lcdwrt3:mov wreg,R0 ;Send it to the LCD rcall lcdput adiw ZL,1 ;Increment Z-pointer inc charcnt ;keep track of chars on display ldi wreg, 0x20 rcall lcdput inc charcnt rcall lcddelay mov wreg, butprot cpi wreg, 0x5F breq lcdends rjmp lcdnxt3 lcdends:rcall lcdclr rjmp endLCD rectune:clr butprot ldi wreg, 0x10 ;set flag in flags reg denoting memory playback state mov state, wreg rcall lcdclr ldi ZH,high(rtune*2);first message addr to Z-ptr ldi ZL,low(rtune*2);Init Z-pointer rcall wrtstr ldi ZH, HIGH(imp*2) ;ptr to RAM set up memory to store notes ldi ZL, LOW(imp*2) clr playing rcall lcdclr rtplyng:lpm mov wreg, r0 cpi wreg, 0x00 breq rtdone mov playing, r0 rcall play adiw ZL, 1 rcall rtdelay ;delay for certain time rjmp rtplyng rtdone: clr state clr playing rcall lcdclr ldi ZH,high(endrt*2);first message addr to Z-ptr ldi ZL,low(endrt*2);Init Z-pointer rcall wrtstr rcall bdelay ;delay so user can see rjmp default endsngl:clr butprot clr state ;return to the default state rcall lcdclr ldi ZH,high(endsgl*2);first message addr to Z-ptr ldi ZL,low(endsgl*2);Init Z-pointer rcall wrtstr rcall bdelay ;delay so user can see rjmp default endLCD: clr butprot clr state ;return to the default state rcall lcdclr ldi ZH,high(enlcd*2);first message addr to Z-ptr ldi ZL,low(enlcd*2);Init Z-pointer rcall wrtstr rcall bdelay rjmp default ;***** write a string to the LCD ***** wrtstr: clr charcnt nextc: lpm ;Get next character from ROM tst R0 ;See if at end of message breq strend ;If so, next message mov wreg, charcnt cpi wreg,8 ;addressing changes at char #8! brne wrtit ;at char 8, fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd rcall sdelay wrtit: mov wreg,R0 ;Send it to the LCD rcall lcdput adiw ZL,1 ;Increment Z-pointer inc charcnt ;keep track of chars on display rjmp nextc ;Loop for more strend: ret ;***** end write a string to the LCD ***** ;***** delay for lcdcmd to go through ***** sdelay: ldi longtime,5 ;Wait 1.5 second for human to read it ldi timeout,0 ;Delay 15 ms wait1: rcall delay dec longtime brne wait1 ret ;***** end delay for lcdcmd to go through ***** ;***** delay for .25 sec ***** mdelay: ldi longtime,25 ;Wait 1.5 second for human to read it ldi timeout,0 ;Delay 15 ms mwait: rcall delay dec longtime brne mwait ret rtdelay:ldi longtime,10 ;Wait 1.5 second for human to read it ldi timeout,0 ;Delay 15 ms rtwait: rcall delay dec longtime brne rtwait ret ;***** delay that polls PINA for press ***** bdelay: ldi longtime,500 ;Wait 1.5 second for human to read it ldi timeout,0 ;Delay 15 ms bwait: in wreg, PINA andi wreg, 0x7F mov butprot, wreg cpi wreg, 0x7F brne bdone rcall delay dec longtime brne bwait bdone: ret ;***** lcd delay ***** lcddelay:ldi longtime,7 ldi timeout, 0 lcdwat: rcall btdbce cpi butnum, 0xFF breq lcdskp mov wreg, butnum ;swap wreg ;move note # to upper 4 bits ;ori wreg, 0x0F ;note is now playing ;swap wreg ori wreg, 0x80 mov playing, wreg rcall play rjmp lcddec lcdskp: clr playing in wreg, PINA andi wreg, 0x7F mov butprot, wreg cpi wreg, 0x5F brne lcddec ret lcddec: dec longtime brne lcdwat ret ;***** delay that polls keyboard for press ***** bkdelay:ldi longtime,300 ;Wait 1.5 second for human to read it ldi timeout,0 ;Delay 15 ms bkwait: rcall btdbce cpi butnum, 0xFF ;no recognizable button was pushed breq bkdec ;branch to a single button press ;mov wreg, butnum ;get which note is being played ;swap wreg ;shift value of note button into upper 4 bits of reg ;ori wreg, 0x0F ;set lower 4 bits to F to signify that a note is being played ori wreg, 0x80 mov playing, wreg ret bkdec: clr playing dec longtime brne bwait bkdone: ret ;********* button debounce *************************** btdbce: rcall butloop ;read in button press mov wreg2, butnum ; cpi wreg2, 0xFF ; breq btdbce mov maybe, butnum ;make a copy of the button previously read in butdcd: ldi lttemp,7 ldi timeout,0 ;Delay 15 ms btwait: rcall delay dec lttemp brne btwait rcall butloop cp maybe, butnum ;butnum contains the button that was pushed breq btdone rjmp btdbce btdone: clr maybe ret ;********* end button debounce *************************** ;================================================ ; Clear entire LCD and delay for a bit lcdclr: ldi wreg,1 ;Clear LCD command rcall lcdcmd ldi timeout,256 ;Delay 15 mS for clear command rcall delay ret ;================================================ ; Initialize LCD module lcdinit: ;cbi PORTA,0 ;Turn on LED 0 ldi wreg,0 ;Setup port pins out PORTD,wreg ;Pull all pins low ldi wreg,0xff ;All pins are outputs out DDRD,wreg ldi timeout,256 ;Wait at least 15 mS at power up rcall delay ; LCD specs call for 3 repetitions as follows ldi wreg,3 ;Function set out PORTD,wreg ;to 8-bit mode nop ;nop is data setup time sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi timeout,256 ;Wait at least 15 mS rcall delay ldi wreg,3 ;Function set out PORTD,wreg nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi timeout,256 ;Wait at least 15 ms rcall delay ldi wreg,3 ;Function set out PORTD,wreg nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi timeout,256 ;Wait at least 15 ms rcall delay ldi wreg,2 ;Function set, 4 line interface out PORTD,wreg nop ; rcall strobe ;Toggle enable line sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi wreg,0b11110000 ;Make 4 data lines inputs out DDRD,wreg ; Finally, ; At this point, the normal 4 wire command routine can be used ldi wreg,0b00100000 ;Function set, 4 wire, 1 line, 5x7 font rcall lcdcmd ldi wreg,0b00001100 ;Display on, no cursor, no blink rcall lcdcmd ldi wreg,0b00000110 ;Address increment, no scrolling rcall lcdcmd ;sbi PORTB,0 ;Turn off LED 0 ret ;============================================ ; Wait for LCD to go unbusy lcdwait: ;cbi PORTA,1 ;Turn on LED 1 ldi wreg,0xF0 ;Make 4 data lines inputs out DDRD,wreg sbi PORTD,lcdrw ;Set r/w pin to read cbi PORTD,lcdrs ;Set register select to command waitloop: sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde in lcdstat,PIND ;Read busy flag ;Read, and ignore lower nibble sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde sbrc lcdstat,3 ;Loop until done rjmp waitloop ;sbi PORTA,1 ;Turn off LED 1 ret ;============================================= ; Send command in wreg to LCD lcdcmd: push wreg ;Save character rcall lcdwait ;Wait for LCD to be ready ldi wreg,0xFF ;Make all port D pins outputs out DDRD,wreg pop wreg ;Get character back push wreg ;Save another copy swap wreg ;Get upper nibble andi wreg,0x0F ;Strip off upper bits out PORTD,wreg ;Put on port nop ;wait for data setup time sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde pop wreg ;Recall character andi wreg,0x0F ;Strip off upper bits out PORTD,wreg ;Put on port nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ldi wreg,0xF0 ;Make 4 data lines inputs out DDRD,wreg ret ;============================================= ; Send character data in wreg to LCD lcdput: push wreg ;Save character rcall lcdwait ;Wait for LCD to be ready ldi wreg,0xFF ;Make all port D pins outputs out DDRD,wreg pop wreg ;Get character back push wreg ;Save another copy swap wreg ;Get upper nibble andi wreg,0x0F ;Strip off upper bits out PORTD,wreg ;Put on port sbi PORTD,lcdrs ;Register select set for data nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde pop wreg ;Recall character andi wreg,0x0F ;Strip off upper bits out PORTD,wreg ;Put on port sbi PORTD,lcdrs ;Register select set for data nop sbi PORTD,lcde ;Toggle enable line cbi PORTD,lcde ;cbi PORTD,lcdrs ;-- ldi wreg,0xF0 ;Make 4 data lines inputs out DDRD,wreg ret ; ******************************* Button detection ******************************** butloop:ldi wreg, 0x0f ;set lower four lines to output out DDRB, wreg ldi wreg, 0xf0 ;and turn on the pullups on the inputs out PORTB, wreg nop ;Need some time for the pullups to nop ;charge the port pins nop nop in wreg, PINB ;read the high nibble mov key, wreg ;and store it (with zeros in the low nibble) ldi wreg, 0xf0 ;set upper four lines to outputs out DDRB, wreg ldi wreg, 0x0f ;and turn on pullups on the inputs out PORTB, wreg nop ;As before wait for the pin to charge nop nop nop in wreg, PINB ;read the low nibble or key, wreg ;combine to make key code ;At the point the raw key code should have exactly one zero each in ;the lower and upper nibbles. Any other number of zeros indicates ;either no-button pressed or multiple-button pressed. ;Now search the table for a match to the raw key code ;and exit with a button number mov ZLtemp1, ZL mov ZHtemp1, ZH mov r0temp1, r0 ldi ZL, low(keytbl*2) ;table pointer in FLASH ldi ZH, high(keytbl*2) ;so convert from word to byte addr ldi wreg, 0 mov butnum, wreg tbllp: lpm ;get the table entry cp key, r0 ;match? breq butfnd inc butnum ;if not, have we exhaused the mov wreg, butnum cpi wreg, 0x10 ;table breq illegal adiw ZL, 1 ;if not, get the next table entry rjmp tbllp butfnd: clr key mov ZL, ZLtemp1 mov ZH, ZHtemp1 mov r0, r0temp1 ret illegal:ser wreg ;no table match means return 0xFF mov butnum, wreg mov ZL, ZLtemp1 mov ZH, ZHtemp1 mov r0, r0temp1 ret ; ******************************* Button detection end ******************************** play: ldi notemp, 0x80 and notemp, playing cpi notemp, 0x80 brne nopres clr notenum ldi notemp, 0x7f and notemp, playing mov ZHtemp1, ZH mov ZLtemp1, ZL mov r0temp1, r0 ldi ZH,high(addtbl*2) ;first message addr to Z-ptr ldi ZL,low(addtbl*2) ;Init Z-pointer nextw: lpm ;Get next word from ROM cp notenum, notemp ;See if at end of message breq foundn adiw ZL, 2 inc notenum rjmp nextw foundn: mov notemp, r0 adiw ZL, 1 lpm mov notenum, r0 adiw ZL, 1 lsl notenum ;rol notemp mov initialL, notenum mov initialH, notemp mov ZH, ZHtemp1 mov ZL, ZLtemp1 mov r0, r0temp1 ret nopres: clr wreg2 out PORTC, wreg2 mov ZH, ZHtemp1 mov ZL, ZLtemp1 mov r0, r0temp1 ret ;============================================= ;***** Timer 0 overflow interrupt handler t0int: ;in save, SREG set ;Set T flag ldi wreg,TSTOP ;Timer 0 off out TCCR0,wreg ;Stop timer ;out SREG, save reti ;Done, return CMP1B_MATCH: ;in save, SREG ;out PORTC, count ;inc count mov ZLtemp, ZL mov ZHtemp, ZH mov r0temp, r0 ldi wreg2, 0x80 and wreg2, playing cpi wreg2, 0x80 brne idle tst play1 brne notepl mov ZH,initialH mov ZL,initialL ;ldi ZH, high(C5samtbl*2) ;ldi ZL, low(C5samtbl*2) mov lastZL, ZL mov lastZH, ZH ldi wreg2, 0xff mov play1, wreg2 notepl: mov ZL, lastZL mov ZH, lastZH lpm out PORTC, r0 ldi wreg2, 128 cp r0, wreg2 brne cont clr play1 cont: adiw ZL, 1 mov lastZL, ZL mov lastZH, ZH mov ZL, ZLtemp mov ZH, ZHtemp mov r0, r0temp ;out SREG, save reti idle: clr wreg2 out PORTC, wreg2 mov ZL, ZLtemp mov ZH, ZHtemp mov r0, r0temp ;out SREG, save reti ;***** Delay n*64 microseconds using timer 0, delay time passed in timeout ; weird construction, interrupt is called like a subroutine delay: in wreg,SREG ;Save status register push wreg out TCNT0,timeout clt ;Clear T ldi wreg,TCK256 ;Timer 0 prescaler, CK / 256 out TCCR0,wreg ;Run timer dwait: brtc dwait ;Wait for timer 0 interrupt to set T pop wreg ;Restore status register out SREG,wreg ret ;============================================= ; text data welcome:.db "Bootup ",0x00 rulesa: .db "1 Single Nte Ply", 0x00 rulesb: .db "2 Record Play ", 0x00 rulesc: .db "3 Mem Playback ", 0x00 rulesd: .db "4 LCD Play ", 0x00 rulese: .db "5 Recorded Tune ", 0x00 snglnt: .db "Single Note Play", 0x00 endsgl: .db "Single Play Done", 0x00 rcdply: .db "Record Play ", 0x00 endrcd: .db "Recording Done", 0x00 memply: .db "Mem Playback ", 0x00 endmem: .db "Mem Plyback Done", 0x00 lcdply: .db "LCD Play", 0x00 enlcd: .db "LCD Play Done", 0x00 tunea: .db "113154 ", 0x00 tuneb: .db "1131B5 ", 0x00 tunec: .db "11*8543 ", 0x00 tuned: .db "9985B5 ", 0x00 endrt: .db "Rcrded Tune Done", 0x00 rtune:.db "Playing Tune", 0x00