;Toy3.asm ;Dhananjay V. Gadre ;11th April 1999 ;Final Changes: 1st May, 1999 ;Synopsis: ;--------- ;This is a small toy that has 4 switches, 4 LEDs ;and a small speaker. The speaker generates notes, Sa, Re ;Ga or Ma. The freq of these notes is: 440Hz, 494Hz, 523Hz ;and 587Hz, respectively. ;The game is played as follows: After reset, the user presses ;any switch and in response, a random note is generated. An LED ;corresponding to this note also lights up. In response, The user ;has to press a key corresponding to this note. If the right ;key is pressed, the game goes into the next round where, 2 ;notes are played and the user has to press the keys corresponding ;to these notes back. If successful, it goes to round 3. This goes ;on till the user can enter the note sequence correctly or if it ;reaches the MAX_CNT limit. In which case the user wins and is hailed ;by the toy with a nice sequence of notes. Otherwise, the game starts ;again. .include "2313def.inc" ;These are reload values in TCNT0 for generating ;Sa, Re, Ga and Ma notes. Crystal freq is 4.000 MHz .equ T1 = -71 ; reload value for Tone 1 .equ T2 = -63 ; reload value for Tone 2 .equ T3 = -56 ; reload value for Tone 3 .equ T4 = -49 ; reload value for Tone 4 .equ START_ADD = $60 .equ MAX_CNT = $20 .equ PLAY_TIME = 100 .def save_status = r0 ;used to store the SREG during an ISR .def sundry=r16 .def count=r18 ;Time for which the tone is played on the ;output pin PORTB7 .def key_val=r19 ;key code. Keys are on PORTD0, 1, 2 and 3 ;code is 0, 1, 2 or 3 .def low_del=r20 ;delay variable .def high_del=r21 ;another delay variable .def stat_flag=r22 ;status flag indicates whether to play the tone .def temp=r23 ;temperory variable .def temp1=r24 ;another temperory variable .def temp2=r25 ;yet another temporary variable .def play_t=r26 ;play which note: 0, 1 2 or 3 .def curr_count=r27 ;Number of notes generated. .def temp_count=r28 ;temperory count of notes .cseg .org 0 rjmp RESET ;Reset Handle ;for 2313, the Timer0 ISR starts at offset 6 in the code memory .org 6 rjmp Timer0_int ;Timer0 Interrupt Subroutine RESET: ldi sundry, $A0 out SPL, sundry ;Init the Stack Pointer ;stack grows to higher mem address ldi stat_flag, 0 ;status flag to indicate ;whether to toggle output ;tone bit ldi sundry, 3 ;DIV64 selected for timer0 out TCCR0, sundry ;timer 0 counts up now ldi sundry, 2 ;Set the TOIE0 bit to enable out TIMSK, sundry ;the Timer0 Interrupt to occur ldi sundry, T1 ;Load note 1 count in Timer0 out TCNT0, sundry ; ldi sundry, 0 ;configure PORT D for all inputs out DDRD, sundry ldi sundry, 255 ;put on the pull ups on PORTD so ;that the switches can be read out PORTD, sundry ldi sundry, 255 out DDRB, sundry ;configure PORTB as output ldi sundry, $ff ;everything on PORTB is off out PORTB, sundry sei ;disable interrupts ldi count, 0 ldi stat_flag, 0 ldi key_val, 1 ;default key value ldi play_t, 0 ;default note clr r31 ;create pointer into SRAM ldi r30, START_ADD ;init to start of SRAM ;for a 2313 rcall play_tone ;Announce your arrival! get_sync: rcall get_key ;just get any key stroke genr: cli ;start of critical section ;disable interrupts rcall gen_random ;load the SRAM with random ;numbers using the LFSR method sei ;enable interrupts again ldi curr_count, 0 ;current index is 0 more_play: inc curr_count ldi temp_count, 0 clr r31 ;init the SRAM pointer ldi r30, START_ADD playp: ldi count, 0 ;allow note to be played ldi stat_flag, 255 ld temp, z+ ;get a note. Note is 2 lsbs andi temp, 03 ;from a SRAM entry mov play_t, temp ;put it in the play_t variable rcall big_delay ;allow the note to be completed inc temp_count ;check if play sequence is over cp curr_count, temp_count brne playp ;if not, play the next note clr r31 ;else, reset pointers again ldi r30, START_ADD now_check: ;and get the user sequence ldi temp_count, 0 so_good: rcall get_key mov play_t, key_val ;play the note corresponding to the ldi count, 0 ;pressed key ldi stat_flag, 255 ;check it against what was played ld temp, z+ ;by the machine andi temp, 03 cp temp, key_val brne not_good ;cant match, so abort this round ;and prepare to play all over again inc temp_count ;otherwise, check if the user ;has matched all the notes cp temp_count, curr_count brne so_good ;if no, get the next key rcall big_delay ;or else, increase the sequence rcall big_delay ;length. just make sure that the ;sequence length is not exceeded ;if that is exceeded, we have a ;musical prodigy. cpi curr_count, MAX_CNT brne more_play rcall hail_the_queen rjmp get_sync ;else just continue ;since the user could not enter the required ;sequence. Just play an indicator tone and ;start again not_good: rcall big_delay rcall play_tone rcall big_delay rjmp get_sync ;play a nice tone T1 T2 T1 play_tone: ldi play_t, 00 ldi count, 0 ldi stat_flag, 255 notg1: cpi stat_flag, 0 brne notg1 ldi play_t, 01 ldi count, 0 ldi stat_flag, 255 notg2: cpi stat_flag, 0 brne notg2 ldi play_t, 00 ldi count, 0 ldi stat_flag, 255 notg3: cpi stat_flag, 0 brne notg3 ret ;Congratulate the winner! ;A real musical virtuoso hail_the_queen: rcall play_tone rcall play_tone rcall play_tone ret ;Just a biggish delay routine ;calls the smaller delay routine a few times big_delay: ldi temp, 0 bigd1: rcall delayit inc temp cpi temp, 10 brne bigd1 ret ;routine to get keypress and release of 1 of 4 keys ;connected on PD0, PD1, PD2 and PD3 ;key code is returned in key_val ;valid key codes are: 0, 1, 2 and 3 get_key: nop no_key: nop ok_key: in sundry, PIND ; input PIND ori sundry, $f0 cpi sundry, 255 breq no_key cpi sundry, 0b11111110 brne not1 ldi sundry, 1 rjmp got_key not1: cpi sundry, 0b11111101 brne not2 ldi sundry, 2 rjmp got_key not2: cpi sundry, 0b11111011 brne not3 ldi sundry, 4 rjmp got_key not3: cpi sundry, 0b11110111 brne no_key ldi sundry, 8 got_key: rcall delayit ; else debounce the press no_sw: in temp, PIND ; now wait for the key to be released and temp, sundry breq no_sw rcall delayit ; released, so debounce it again cpi sundry, 1 brne kk1 ldi key_val,0 ret kk1: cpi sundry, 2 brne kk2 ldi key_val,1 ret kk2: cpi sundry,4 brne kk3 ldi key_val,2 ret kk3: ldi key_val,3 ret ;delay routine to debounce the switch press delayit: ldi high_del, 200 load_low: ldi low_del, 200 decit: dec low_del brne decit dec high_del brne load_low ret Timer0_int: in save_status, SREG sbi PORTB, 0 ;first put off all LEDs sbi PORTB, 1 ;on PORTB sbi PORTB, 2 sbi PORTB, 3 sbis PORTB, 7 ;this code increments inc count ;count in alternate ISRs cpi count, PLAY_TIME brne still_time ;if play duration is over ldi stat_flag, 0 ;reset the flag still_time: ;see which note to cpi play_t, 0 ;play brne chk2 ldi play_t, T1 out TCNT0, play_t ldi play_t, 0 sbrc stat_flag, 7 ;first check if note is ;being actually played ;if so, cbi PORTB, 0 ;put on LED on PORTB0 rjmp chk5 chk2: cpi play_t, 1 brne chk3 ldi play_t, T2 out TCNT0, play_t ldi play_t, 1 sbrc stat_flag, 7 cbi PORTB, 1 ;put on LED on PORTB1 rjmp chk5 chk3: cpi play_t, 2 brne chk4 ldi play_t, T3 out TCNT0, play_t ldi play_t, 2 sbrc stat_flag, 7 cbi PORTB, 2 ;put on LED on PORTB2 rjmp chk5 chk4: ldi play_t, T4 out TCNT0, play_t ldi play_t, 3 sbrc stat_flag, 7 cbi PORTB, 3 ;put on LED on PORTB3 ;check if flag to play note ;is set to 255 chk5: cpi stat_flag, 255 breq play_it cbi PORTB, 7 ;if no, then clear PB7, so ;that the speaker does not load ;PB7 rjmp no_tone ;note is not to be played ;so just return back ;if flag is set, then play note ;for that just complement PB7 ;stat_flag is being used as a temp ;register. It's value is restored ;later. play_it: in stat_flag, PORTB ldi temp2, $80 eor stat_flag, temp2 out PORTB, stat_flag ldi stat_flag, 255 no_tone: out SREG, save_status reti ;A routine to generate random number sequence using LFSR ;using a seed from Timer0 which has been running for some ;time. The current value of the Timer0 would be fairly random ;since the user had to press a key to get to here. ;This is an 8-bit LFSR ;temp register is used as the 8-bit LFSR ;The Taps for an 8-bit LFSR are at: 1, 2, 3 and 7 for ;maximal length. gen_random: ldi r30, START_ADD ;init the pointer into SRAM to 60hex clr r31 ;60hex is start of SRAM in 2313 in temp, TCNT0 ; get a seed for the LFSR ; what if the seed is zero? ;Arrrgh.. must check and adjust ldi count, 0 ;will keep track of how many SRAM ;locations have been filled up lfsr: mov temp1, temp andi temp1, 0b10001110 ror temp1 mov temp2, temp1 andi temp2, 0b00000001 ror temp1 mov low_del, temp1 andi low_del, 0b00000001 ror temp1 mov high_del, temp1 andi high_del, 0b00000001 ror temp1 ror temp1 ror temp1 ror temp1 eor temp1, temp2 eor low_del, high_del eor temp1, low_del andi temp1, 0b00000001 breq shift sec rjmp no_shift shift: clc no_shift: rol temp mov temp1, temp ;OK a random num in temp1 ;store it in SRAM now st z+, temp inc count cpi count, MAX_CNT brne lfsr ret