;Gigi Lam ;Euborn Chiu ;EE476 Final Project, Sp99 .include "8535def.inc" .device AT90S8535 ;specifies to the assembler which chip we are using .def AnaLo =r2 .def AnaHi =r3 .def reload =r4 ;holds timer reload value .def duration =r5 ;interrupt count for .25sec length per note .def charcnt =r6 ;Char position on the display .def charcp =r7 ;holds value to compare charcnt against (8) .def b0 =r8 .def b1 =r9 .def b2 =r10 .def winner =r11 ;was last user a winner? .def Temp =r16 ;temporary register .def iTemp =r17 ;interrupt temporary register .def save =r18 ;SREG save location .def state =r19 ;machine state for debounce subroutines .def compare =r20 ;must exceed this value to win ;---LCD registers .def wreg =r21 ;General use working register .def timeout =r22 ;Timeout value passed to subroutine .def lcdstat =r23 ;LCD busy/wait status .def longtime =r24 ;Long timer for powerup .def chr =r25 ;a variable character to print .def LED =r26 ;LED lighting .def musicout =r27 ;holds output to pin6 .def Notenum =r28 ;note number being played ;---equates .equ TCK1 =1 .equ TCK8 =2 .equ TCK64 =3 ;ck/64 prescalar .equ TCKSTOP =0 ;ck stop .equ TEXF =6 ;Timer/Counter runs from external falling edge .equ TEXR =7 ;Timer/Counter runs from external rising edge .equ AZERO ='0' .equ lcdrs =PC6 ;LCD rs pin connected to PD6 .equ lcdrw =PC5 ;LCD r/w pin connected to PD5 .equ lcde =PC4 ;LCD enable pin connected to PD4 .equ true =0xff .equ false =0x00 ;---machine states .equ welcome =0 ;state 0 =welcome display .equ waitforhit=1 ;state 1 =diplay for loser .equ display =2 ;state 2 =wait for reset .dseg points: .byte 11 ;3 digit score + zero terminate .cseg .org $0000 rjmp RESET ;reset entry vector reti reti reti reti reti reti ;rjmp T1match ;timer1 matchA reti rjmp T1ovfl ;timer1 overflow rjmp T0Ovfl ;timer0 overflow reti reti reti reti rjmp ADCdone ;ADC done handler reti reti ;---messages for display mnotes: .db 01,31,184,220,175,195,136,131,136,131,195,255,195,255,195,255,195,255 .db 184,220,175,195,136,131,136,131,195,255,195,255,195,255,195,255 .db 184,220,175,195,136,131,136,131,195,255,195,255,113,110,113,110 .db 195,255,195,255,96,98,96,98,192,248,192,248,192,248,192,248 .db 184,220,175,195,96,98,96,98,192,248,192,248,192,248,192,248 .db 184,220,175,195,96,98,96,98,192,248,192,248,192,248,192,248 .db 184,220,175,195,96,98,96,98,192,248,192,248,113,110,113,110 .db 192,248,192,248,136,131,136,131,195,255,195,255,195,255,195,255 .db 0x00 snotes: .db 136,80,149,100,160,110,165,130,175,155,184,170,192,180,0x00 fnotes: .db 195,230,192,180,184,170,175,155,165,130,160,110,149,100,136,80,0x00 dnotes: .db 240,255,240,255,240,255,240,255,240,255,01,31,240,255,240,255,240,255,240,255,240,255,01,31,0x00 welcome1: .db "Wack-a-Cap", 0x00 taunt: .db "Show Your Power!", 0x00 lost: .db "Sorry, Try Again", 0x00 won: .db "You're a Winner!", 0x00 ;---reset vector RESET: cli ldi temp, LOW(RAMEND) ;setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ldi temp, 0b00000101 ;T1OE on and OCI1EA on and T0OE on out TIMSK, temp ldi temp, TCK64 out TCCR0, temp ;set timer 0 clk / 64 ldi temp, TCKSTOP out TCCR1B, temp ;stop timer 1 (just in case) ldi temp, 0xFF ;set port B output out DDRB, temp ;bit0=music, bits4..1=LED encoded ldi temp, 0xFF out DDRC, temp ;set port C output for LCD ldi temp, 0x00 out DDRD, temp ;set port D input for buttons ;---init LCD stuff clr notenum clr charcnt ldi temp, 8 mov charcp, temp sei ;enable all interrupts ;power down the LCD ldi wreg, 0xff ;LCD power connection out DDRC, wreg ;power it down after reset ldi wreg, 0x00 out PORTC,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 PORTC, 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 ;hopefully by now the LCD has rebooted main: rcall lcdinit ;Initialize LCD module softreset: ;---Init ADC stuff ldi temp, 0 out ADMUX, temp ldi temp, 0xFF mov AnaLo, temp mov AnaHi, temp ;---init music stuff ldi ZH,high(mnotes*2) ;first music note addr to Z-ptr ldi ZL,low(mnotes*2) lpm ;load first music note RELOAD value out TCNT0, r0 ;move value to timer 0 counter mov Reload, r0 ;save reload value adiw ZL, 1 lpm mov duration, r0 ;load first music note duration value adiw ZL, 1 ldi musicout,0xFF ;set music output to high ;---init LED stuff ldi LED, 0x00 ;disable all lights rcall lcdclr ;Clear LCD screen clr charcnt ;zero the character count ldi ZH,high(welcome1*2) ;load first welcome message to Z pointer ldi ZL,low(welcome1*2) rcall ZtoLCD ;display msg ;---go on to welcome state ldi state, welcome rjmp _state clr winner ;---Welcome state: ;---Plays music and lights LED, waits for keypress ;---When keypress happens, goes to next state _welcome: tst duration ;test to see if we have overflowed duration brne sensebut ;---load next music note lpm tst r0 ;Are we at last note? r0=0=yes brne Noteok ldi ZH,high(mnotes*2) ;first music note addr to Z-ptr ldi ZL,low(mnotes*2) rjmp sensebut Noteok: out TCNT0, r0 ;load next note values mov Reload, r0 adiw ZL, 1 lpm mov duration, r0 ;load next duration value adiw ZL, 1 ;---load LED sequence accordingly ldi temp, 184 cp Reload, temp breq _184 ldi temp, 175 cp Reload, temp breq _175 ldi temp, 136 cp Reload, temp breq _136 ldi temp, 195 cp Reload, temp breq _195 ldi temp, 113 cp Reload, temp breq _113 ldi temp, 96 cp Reload, temp breq _96 ldi temp, 192 cp Reload, temp breq _192 _184: ldi LED, 0x00 rjmp sensebut _175: ldi LED, 0x01 rjmp sensebut _136: ldi LED, 0x04 rjmp sensebut _195: ldi LED, 0x07 rjmp sensebut _113: ldi LED, 0x03 rjmp sensebut _96: ldi LED, 0x02 rjmp sensebut _192: ldi LED, 0x06 rjmp sensebut ;---Check to see which machine state we are in _state: cpi state, welcome breq _welcome cpi state, waitforhit breq _waitforhit cpi state, display breq _display rjmp _state sensebut: sbis pind, 0 rjmp man sbis pind, 1 rjmp woman rjmp _state ;no button pressed, remain in state man: ldi compare, 0x15 ldi state, waitforhit rjmp stopclk woman: ldi compare, 0x18 ldi state, waitforhit rjmp stopclk stopclk: ;---button pressed, set up to exit state ldi temp, TCKSTOP out TCCR0, temp ;stop timer ldi temp, 0b11101101 ;enable ADC, start conversion, free run, intr enabled out ADCSR, temp ;---exit state rjmp _state ;---Wait for Hit state ;---Continuously senses ADC output ;---Advances only when ADC output is _waitforhit: ;moving values from ADC register ldi temp, 0x02 cp AnaHi, temp ;compare with min value to disregard noise brsh _waitforhit ldi temp, 0b01101101 ;disable ADC, leave everything else out ADCSR, temp ldi state, display rjmp _state ;---Display State ;---Displays result of user's attempt ;---First Play Scale, then depending on ADC output value, play either winning or losing messages/music. _display: ldi LED, 0xFF ldi temp, TCK64 out TCCR0, temp ;start timer 0 at clk/64 ldi ZH,high(snotes*2) ;first scale note addr to Z-ptr ldi ZL,low(snotes*2) playscale: lpm ;load first scale note RELOAD value tst r0 ;if r0=0 then we are at the end of scale breq donescale ;we are done w/ this part then out TCNT0, r0 ;move value to timer 0 counter mov Reload, r0 ;save reload value adiw ZL, 1 lpm mov duration, r0 ;load first scale note duration value adiw ZL, 1 inc LED ;control LED lighting sequence scaleloop: tst duration ;test to see if we have overflowed duration brne scaleloop ;loop until we have played this note for entire duration rjmp playscale ;ready for next scale note donescale: ;done with scale here, ready for winning/losing msg lsr AnaHi ror AnaLo lsr AnaHi ror AnaLo lsr AnaHi ror AnaLo ;div by 8 cp AnaLo, compare ;lower result = bigger g force, player wins brlo winmsg rjmp losemsg ;---Winmsg: keeps top LED lit, plays ding-ding music winmsg: ldi temp, 0xff mov winner,temp ;set winner bit add temp, AnaLo ldi LED, 0x07 ;light top LEDs ldi ZH,high(dnotes*2) ;first ding-ding note addr to Z-ptr ldi ZL,low(dnotes*2) playding: lpm ;load first ding note RELOAD value tst r0 ;if r0=0 then we are at the end of ding-ding sequence breq doneding ;we are done w/ this part then out TCNT0, r0 ;move value to timer 0 counter mov Reload, r0 ;save reload value adiw ZL, 1 lpm mov duration, r0 ;load first dingding note duration value adiw ZL, 1 dingloop: tst duration ;cycle through all ding-ding notes brne dingloop rjmp playding doneding: ldi ZH,high(won*2) ;load winning message to Z pointer ldi ZL,low(won*2) rcall ZtoLCD ;go to LCD update rjmp Waitreset ;---Losmsg: scrolls LEDs back down, and scale back down too. losemsg: ldi temp, 0x00 mov winner,temp ;set loser bit ldi temp, 118 ;convert Vout to numerical value sub temp, AnaLo mov AnaLo, temp mov AnaHi, AnaLo ;keep spare copy of AnaLo in AnaHi rcall convertor ;convert value to ASCII ldi ZL, LOW(points) ;ptr to RAM ldi ZH, HIGH(points) rcall ZrtoLCD ;display points ldi LED, 0x08 ldi ZH,high(fnotes*2) ;first falling note addr to Z-ptr ldi ZL,low(fnotes*2) playfall: lpm ;Z ptr already at first note of scale tst r0 ;if r0=0 then we are at the end of scale breq donefall ;we are done w/ this part then out TCNT0, r0 ;move value to timer 0 counter mov Reload, r0 ;save reload value adiw ZL, 1 lpm mov duration, r0 ;load first scale note duration value adiw ZL, 1 dec LED ;control LED lighting sequence fallloop: tst duration ;test to see if we have overflowed duration brne fallloop ;loop until we have played this note for entire duration rjmp playfall ;ready for next scale note donefall: ;done with scale here, ready for winning/losing msg ldi ZH,high(lost*2) ;load losing msg to Z pointer ldi ZL,low(lost*2) rcall ZtoLCD ;---Wait for operator reset Waitreset: mov temp,Anahi ;table for showing strength in LEDs cpi temp, 90 brsh _90s cpi temp, 80 brsh _80s cpi temp, 70 brsh _70s cpi temp, 60 brsh _60s cpi temp, 50 brsh _50s _50s: ldi LED, 3 rjmp fixclk _60s: ldi LED, 4 rjmp fixclk _70s: ldi LED, 5 rjmp fixclk _80s: ldi LED, 6 rjmp fixclk _90s: ldi LED, 7 rjmp fixclk fixclk: ldi temp,TCK8 out TCCR0, temp ;set timer 0 at to/8 ldi temp, 0x01 mov Reload, temp ;mute ldi temp, 0x7A mov Duration, temp ;for .5 second rjmp LEDloop LEDreset: clr LED LEDloop: sbis pind, 3 ;is reset key pressed by operator rjmp Waitdone tst winner breq LEDloop ;loser tst Duration ;Winner, flash LEDs brne LEDloop ldi temp, 0x7A mov Duration, temp cpi LED, 0x07 ;reached last LED breq LEDreset inc LED rjmp LEDloop Waitdone: ldi temp, TCK64 out TCCR0, temp ;set timer 0 clk / 64 ldi state, welcome rjmp softRESET ;----------Interrupts ;---Timer 0 overflow interrupt ;---Toggles music output register, reloads RELOAD value to counter ;---On portx: bit0=audio output, bits4..1=3bit to decoder for LED ;---Read LED and musicout registers, and switches output to PORTx accordingly T0Ovfl: in save, sreg ldi temp,0x01 cp Reload, temp ;if Reload=0x01, then mute! breq continue com musicout ;complement musicout continue: in itemp, portc tst musicout brne lo_out ori itemp, 0x01 rjmp out_LED lo_out: andi itemp, 0xFE out_LED: andi itemp, 0xF1 ;zero out LED bits, leaves rest alone lsl LED or itemp, LED ;include output to LED bits here lsr LED ;shift back out portb, itemp dec duration ;decrease duration counter to keep track of .25sec out TCNT0, Reload ;reload counter out sreg, save reti ;---ADCdone interrupt ;---Called whenever ADC cycle is complete ADCdone: in save, sreg in Analo, ADCL in AnaHi, ADCH out sreg, save reti ;---LCD Stuff ;---ZtoLCD displays message from ROM ZtoLCD: clr charcnt rcall lcdclr dispch: lpm tst R0 breq endZtoLCD ;If so, next message cp charcnt,charcp ;addressing changes at char #8! brne wrtchr ;at char 8, fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd wrtchr: 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 dispch ;Loop for more endZtoLCD: ret ;---ZrtoLCD displays message from RAM ZrtoLCD: clr charcnt ;zero the character count rcall lcdclr ;Clear LCD screen dispchr: ld r0, Z ;Get next chracter from RAM tst R0 ;See if at end of message breq endZrtoLCD ;If so, next message cp charcnt,charcp ;addressing changes at char #8 brne wrtchrr ;fix it ldi wreg,0xC0 ;Set address to last 8 chars rcall lcdcmd wrtchrr: mov wreg,R0 ;Send it rcall lcdput adiw ZL,1 ;Increment Z-pointer inc charcnt ;keep track of chars rjmp dispchr ;Loop for more endZrtoLCD: ret ;done writing to LCD ;---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: ldi wreg,0 ;Setup port pins out PORTC,wreg ;Pull all pins low ldi wreg,0xff ;All pins are outputs out DDRC,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 PORTC,wreg ;to 8-bit mode nop ;nop is data setup time sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde ldi timeout,256 ;Wait at least 15 mS rcall delay ldi wreg,3 ;Function set out PORTC,wreg nop sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde ldi timeout,256 ;Wait at least 15 ms rcall delay ldi wreg,3 ;Function set out PORTC,wreg nop sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde ldi timeout,256 ;Wait at least 15 ms rcall delay ldi wreg,2 ;Function set, 4 line interface out PORTC,wreg nop sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde ldi wreg,0b11110000 ;Make 4 data lines inputs out DDRC,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 ret ;---Wait for LCD to go unbusy lcdwait: ldi wreg,0xF0 ;Make 4 data lines inputs out DDRC,wreg sbi PORTC,lcdrw ;Set r/w pin to read cbi PORTC,lcdrs ;Set register select to command waitloop: sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde in lcdstat,PINC ;Read busy flag ;Read, and ignore lower nibble sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde sbrc lcdstat,3 ;Loop until done rjmp waitloop 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 DDRC,wreg pop wreg ;Get character back push wreg ;Save another copy swap wreg ;Get upper nibble andi wreg,0x0F ;Strip off upper bits out PORTC,wreg ;Put on port nop ;wait for data setup time sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde pop wreg ;Recall character andi wreg,0x0F ;Strip off upper bits out PORTC,wreg ;Put on port nop sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde ldi wreg,0xF0 ;Make 4 data lines inputs out DDRC,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 DDRC,wreg pop wreg ;Get character back push wreg ;Save another copy swap wreg ;Get upper nibble andi wreg,0x0F ;Strip off upper bits out PORTC,wreg ;Put on port sbi PORTC,lcdrs ;Register select set for data nop sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde pop wreg ;Recall character andi wreg,0x0F ;Strip off upper bits out PORTC,wreg ;Put on port sbi PORTC,lcdrs ;Register select set for data nop sbi PORTC,lcde ;Toggle enable line cbi PORTC,lcde ldi wreg,0xF0 ;Make 4 data lines inputs out DDRC,wreg ret ;---converts AnaLo to 3 digit decimal value convertor: clr b0 clr b1 clr b2 begin_conv: tst AnaLo ;test if resultL is zero breq done ;yes, we are done dec_loop: ldi temp, 0x09 ;compare value dec AnaLo ;decrement resultL cp b0, temp ;b0 >= 9? brsh b0_yes inc b0 rjmp begin_conv b0_yes: cp b1, temp ;b0,b1>=9 too? brsh b1_yes clr b0 inc b1 rjmp begin_conv b1_yes: clr b0 clr b1 inc b2 rjmp begin_conv done: ldi ZL, LOW(points) ;ptr to RAM ldi ZH, HIGH(points) ldi temp, azero ;used to convert to ASCII add b0, temp ;only 4 lsdigits needed add b1, temp ldi temp, 'S' st Z, temp inc ZL ldi temp, 'c' st Z, temp inc ZL ldi temp, 'o' st Z, temp inc ZL ldi temp, 'r' st Z, temp inc ZL ldi temp, 'e' st Z, temp inc ZL ldi temp, ':' st Z, temp inc ZL ldi temp, ' ' st Z, temp inc ZL st Z, b1 inc ZL st Z, b0 inc ZL ldi temp, 0x00 st Z, temp ret ;---Timer 1 overflow interrupt handler T1Ovfl: set ;Set T flag ldi wreg,TCKSTOP ;Timer 1 off out TCCR1B,wreg ;Stop timer reti ;Done, return ;---Delay n*64 microseconds using timer 0, delay time passed in timeout Delay: in wreg,SREG ;Save status register push wreg ldi itemp, 0x00 out TCNT1H, itemp out TCNT1L, timeout clt ;Clear T ldi wreg,TCK1 ;Timer 1 prescaler, CK / 1 out TCCR1B,wreg ;Run timer Dwait: brtc dwait ;Wait for timer 1 interrupt to set T pop wreg ;Restore status register out SREG,wreg ret