; ############################################################ ; ; Fügen Sie in diese Datei bitte Ihren selbst erarbeiteten ; Assemblercode ein! Nehmen Sie keine Änderungen am vorge- ; gebenen Code vor! ; ; ############################################################ .include "m16def.inc" ; Symbolische Registernamen und bausteinspezifische ; Konstanten importieren .dseg .org $60 ; Blockvariablen ; ================ ; ================================ ; Diese Variablen nicht verändern! ; ================================ block4: .byte 8 block5: .byte 8 block6: .byte 8 block7: .byte 8 block_y: .byte 1 ; ================================ ; Diese Variablen nicht verändern! ; ================================ ; Variablen ; ======================== ; *** (Ihre Lösung aus Abschnitt 3.1) *** figure_pos: .byte 1 block_pos_x: .byte 1 block_pos_y: .byte 1 gameover: .byte 1 seed: .byte 1 start_pos: .byte 1 ; Interrupt-Vektor-Tabelle ; ======================== ; *** (Ihre Lösung aus Abschnitt 3.6) *** .cseg .org $000 ; Dillsauce jmp init .org $00C ; OC1Aaddr jmp t1comp_isr .org $026 ; OC0addr jmp t0comp_isr ; Unterprogramme ; ============== .cseg ; Programmspeicher .org $030 ; ab Adresse $030 verwenden. ; Vorgegebene Unterprogramme einbinden ; ------------------------------------ .include "lcd.asm" .include "draw.asm" ; INIT_ADC ; ---------- ; Initialisierung von A/D-Converter ; I: - ; O: - init_adc: ; *** (Ihre Lösung aus Abschnitt 3.2) *** push r16 ldi r16, (1<<REFS0) | (1<<ADLAR) out ADMUX, r16 ldi r16, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0) out ADCSRA, r16 pop r16 ret ; INIT_TIMER0 ; ---------- ; Initialisierung von Timer/Counter0 ; I: - ; O: - init_timer0: ; *** (Ihre Lösung aus Abschnitt 3.3) *** push r16 ; Register retten. clr r16 ; Normale Betriebsart, ldi r16, (1<<WGM01) | (1<<CS02) | (1<<CS00) ; Bits für Timer Control Register setzen out TCCR0, r16 ; setzen des Timer/Counter 0 Control Register ldi r16, 39 ; Vergleichswert setzen (entspricht Abtastung von 25Hz) out OCR0, r16 ; und speichern ldi r16, (1<<OCIE0) ; Interrupterzeugung bei Erreichen des Vergleichswerts A von Timer 0 aktivieren. out TIMSK, r16 ; Setzen des Timer/Counter Interrupt Mask Register pop r16 ; Register wiederherstellen. ret ; Zurueck. ; INIT_TIMER1 ; ---------- ; Initialisierung von Timer/Counter1 ; I: - ; O: - init_timer1: ; *** (Ihre Lösung aus Abschnitt 3.4) *** push r16 ; Register retten. clr r16 ; Normale Betriebsart, out TCCR1A, r16 ; Setzen des Timer/Counter 1 Control Register A ldi r16, (1<<WGM12) | (1<<CS11) | (1<<CS10) ; keine Ausgabe über OC1A/OC1B, Bits für Timer Control Register setzen out TCCR1B, r16 ; Setzen des Timer/Counter 1 Control Register B ldi r16, high(3906) ; Vergleichswert setzen erst High-...dann Low-Byte out OCR1AH, r16 ; ldi r16, low(3906) ; out OCR1AL, r16 ; in r16, TIMSK ; Interrupterzeugung bei Erreichen des Vergleichswerts A von Timer 1 aktivieren. ori r16, (1<<OCIE1A) ; Hinweis: Beachten Sie dass die Interrupts für alle Timer in dem selben Register gesetzt werden! out TIMSK, r16 ; Setzen des Timer/Counter Interrupt Mask Register pop r16 ; Register wiederherstellen. ret ; Zurueck. ; READ_ADC ; ---------- ; Liest Daten vom ADC und gibt sie in R16 zurück ; I: - ; O: Der gewandelte Wert in R16 read_adc: ; *** (Ihre Lösung aus Abschnitt 3.2) *** sbi ADCSRA, ADSC wait_adc: sbic ADCSRA, ADSC rjmp wait_adc in r16, ADCH ret ; T0COMP_ISR ; ---------- ; (Interrupt-Service-Routine) ; ADC auslesen und Display ansteuern t0comp_isr: ; *** (Ihre Lösung aus Abschnitt 3.3) *** push r16 in r16, SREG push r16 push r17 call read_adc ; Wert vom Poti auslesen ldi r17, 12 call div8u ; AD-Wert durch 12 teilen sts figure_pos, r16 ; neue Position speichern call draw_figure ; Figur zeichnen pop r17 pop r16 out SREG, r16 pop r16 reti ; T1COMP_ISR ; ---------- ; (Interrupt-Service-Routine) ; Inkrementiert die Blockposition und zeichnet Block neu t1comp_isr: ; *** (Ihre Lösung aus Abschnitt 3.4) *** push r16 in r16, SREG push r16 push r17 push r18 lds r17, block_pos_y ; lade block_pos_y inc r17 ; inkrementiere cpi r17, 70 ; vergleiche mit 70 brlo no_collision_check ; ueberpruefe die kollision nur, falls call check_collision ; die y-Position des Blocks >= 70 ist no_collision_check: cpi r17, 78 ; ist die y-Position des Blocks gleich 78, brne not_grounded ; dann setze den Block zurueck lds r17, start_pos ; auf die Startposition call lfsr sts block_pos_x, r16 ; setze die x-Position des Blocks auf einen Zufallswert (lfsr) not_grounded: sts block_pos_y, r17 call draw_box ; passe die Block-Geschwindigkeit an in r16, OCR1AL in r17, OCR1AH subi r16, 10 brpl sub_fertig cpi r17, 5 brlo reached_limit ; hart am Limit! ldi r18, 0xFF add r16, r18 ; Zimt subi r17, 1 ; Muskatnuss rjmp sub_fertig reached_limit: ldi r16, 0 ; zurueck mit dir ldi r17, 4 ; weiche Teufel... sub_fertig: out OCR1AL, r16 ; setze zaehler out OCR1AH, r17 ; neu pop r18 pop r17 pop r16 out SREG, r16 pop r16 reti ; CHECK_COLLISION ; Überprüft, ob Box und Figur kollidieren ; I: - ; O: - check_collision: ; *** (Ihre Lösung aus Abschnitt 3.5) *** push r16 push r17 lds r16, block_pos_x lds r17, figure_pos sub r16, r17 cpi r16, 0 brlo collision_end ; die Box ist links von der Figur cpi r16, 9 brsh collision_end ; die Box ist rechts von der Figur ldi r16, 1 sts gameover, r16 ; Spiel vorbei... collision_end: pop r17 pop r16 ret ; Systeminitialisierung und Hauptprogramm ; ======================================= ; Systeminitalisierung ; -------------------- init: ; *** (Ihre Lösung aus Abschnitt 3.6) *** ldi r16, high(RAMEND) out SPH, r16 ldi r16, low(RAMEND) out SPL, r16 call lcd_init call init_timer0 call init_timer1 call init_adc sbi PINC, PINC0 ; Hauptprogramm ; ------------- main: ; *** (Ihre Lösung aus Abschnitt 3.7) *** ; *** Eigene Lösungsvorschläge sind auch erlaubt *** call lcd_startdisplay ; rufe den Startdisplay auf ldi r16, 0 ; setze gamover auf '0' sts gameover, r16 ; und speichere dies in r17, PINC ; lade PINC in r16 startloop: in r16, PINC ; solange es keine Veränderung gibt cp r16, r17 breq startloop ; springe zurück call lcd_clear in r16, TCNT1L ; wenn Taster gedrückt wurde cpi r16, 0 ; dann lade den Timerwert als seed brne set_seed ; wenn er gleich 0 ist ldi r16, $42 ; dann wähle eine andere Zahl als seed set_seed: sts seed, r16 ; und speichere den seed call lfsr sts block_pos_x, r16 ; setze auch gleich den Start des Blocks lds r17, start_pos sts block_pos_y, r17 ; auch in y-Richtung sei ; und aktiveier die Interrupts mainloop: lds r16, gameover ; gameover? cpi r16, 1 ; wenn nein, dann warte weiter brne mainloop maingameover: ; wenn doch, cli ; dann sperre Interrupts call lcd_gameover ; und schreibe gameover call init_timer1 ; Timer 1 initialisieren rjmp main ; ======================================= ; ======================================= ; VORGEGEBENE UNTERPROGRAMME ; ======================================= ; ======================================= ; LFSR ; -------- ; Linear rückgekoppeltes Schieberegister ; Verwendet für das Fibonacci-LFSR das Generatorpolynom x^8+x^6+x^5+x^4+1 ; Gibt eine Zufallszahl zwischen 11 und 15 zurück ; I: Erwartet in der Variablen "seed" einen Seed bzw. den alten Schieberegisterwert ; O: R16: Zufallszahl lfsr: push R15 ; Register sichern push R17 clr R17 lds R16, seed sbrc R16, 7 ; Bit x^8 inc R17 sbrc R16, 5 ; Bit x^6 inc R17 sbrc R16, 4 ; Bit x^5 inc R17 sbrc R16, 3 ; Bit x^4 inc R17 inc R17 ; +1 andi R17, 1 ; Bitmaske mit 1 lsl R16 ; shifte alten Wert nach links or R16, R17 ; und addiere Wert aus Polynom drauf sts seed, R16 ; speichere Wert wieder ab. ldi R17, 12 ; lade 12 in R17 call div8u ; und berechne R15 = R16/12 mov R16, R15 ; schreibe das Ergebnis in R16 zurück subi R16, -4 ; und addiere eine 4 drauf pop R17 ; Register wieder herstellen pop R15 ret ; DIV8U ; ---------- ; 8/8 Division (unsigned) ; I: R16: Dividend, R17: Divisor ; O: R16: Ergebnis, R15: Rest div8u: push R18 clr r15 ; clear remainder and carry ldi r18,9 ; init loop counter d8u_1: rol r16 ; shift left dividend dec r18 ; decrement counter brne d8u_2 ; if done pop R18 ret ; return d8u_2: rol r15 ; shift dividend into remainder sub r15,r17 ; remainder = remainder - divisor brcc d8u_3 ; if result negative add r15,r17 ; restore remainder clc ; clear carry to be shifted into result rjmp d8u_1 ; else d8u_3: sec ; set carry to be shifted into result rjmp d8u_1 ; Vorgegebenes Unterprogramm "warte": ; Routine zur Verzoegerung des Programmablaufs ; Parameter: R16: Verzoegerungszeit in 100us warte: push R16 ; Register sichern push R17 warte_schleife1: ldi R17, 32 nop warte_schleife2: dec R17 ; innere Schleife (Laufzeit 100us) brne warte_schleife2 ; die dann R16 mal abgearbeitet wird dec R16 brne warte_schleife1 pop R17 pop R16 ; Register wiederherstellen ret ; Vorgegebenes Unterprogramm "warte_1s": ; Routine zur Verzoegerung des Programmablaufs ; Wartet 1 Sekunde warte_1s: push R16 push R17 ldi R17, 250 warte_1s_schleife: ldi R16, 250 call warte dec R17 brne warte_1s_schleife pop R17 pop R16 ret
Comments