Forum: Mikrocontroller und Digitale Elektronik Kniffliges Assemblerprogramm


von Roland N. (eroli)


Lesenswert?

Hallo zusammen,

ich hänge gerade an einem etwas kniffligem Assemblerprogramm...

An PortB und PortD sind LEDs angeschlossen und ich möchte nun so eine 
wandernde "Snake" mittels Timer2 über die LEDs huschen lassen.

Die LEDs sind so angeordnet:

PB0   PB1   PB2   PB3   PB4   PB5   PB6   PB7

PD0   PD1   PD2   PD3   PD4   PD5   PD6   PD7

Und die Schlange sollte sich so bewegen:

1   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0

dann

1   1   0   0   0   0   0   0
0   0   0   0   0   0   0   0

dann
1   1   1   0   0   0   0   0
0   0   0   0   0   0   0   0

dann
0   1   1   1   0   0   0   0
0   0   0   0   0   0   0   0

[usw]

0   0   0   0   0   1   1   1
0   0   0   0   0   0   0   0

dann
0   0   0   0   0   0   1   1
0   0   0   0   0   0   0   1

dann
0   0   0   0   0   0   0   1
0   0   0   0   0   0   1   1

dann
0   0   0   0   0   0   0   0
0   0   0   0   0   1   1   1

und so weiter. Am anderem Ende sollte sie wieder genau so umbrechen, wie 
schon vorgezeigt. Das sollte sich dann so lange wiederholen, bis Timer2 
gestoppt wird.

Ich habe versucht das Ganze mit 3 Registern zu lösen, aber noch läuft es 
nicht wirklich, habe auch eben wieder alles gelöscht, was ich schon 
hatte.

Hat einer von euch eine Idee, wie man das relativ eleganz umsetzen kann?

Ciao,
Roland

von Der E. (rogie)


Lesenswert?

z.B. mit Tabellen kann man das sehr gut umsetzen.

von spess53 (Gast)


Lesenswert?

Hi

Das knifflige ist eigentlich nur der Anfang. Die drei Bits über zwei 
Register rotieren geht so:
1
          ldi r16,7
2
3
aaa:      bst r17,7
4
          lsl r16
5
          rol r17
6
          bld r16,0
7
                      
8
          out PortB,r16
9
          out PortD,r17
10
11
          rjmp aaa

MfG Spess

von Uwe (Gast)


Lesenswert?

mit rotieren bzw. shift operationen und pb7 wird immer an pd7 kopiert 
pd0 immer an pb0. Das solte reichen.

von spess53 (Gast)


Lesenswert?

Hi

Mit 'Softstart':
1
         ldi r16,0b00000111
2
         clr r17
3
         out PortB,r17
4
         out PortD,r17
5
6
aaa:     tst r16
7
         breq aaa10
8
9
         lsr r16
10
         rol r17
11
         out PortB,r17
12
         rjmp aaa
13
14
aaa10:   bst r17,7
15
         lsr r16
16
         rol r17
17
         bld r16,7
18
         
19
         out PortB,r17
20
         out PortD,r16
21
22
         rjmp aaa10

MfG Spess

von Ralf (Gast)


Lesenswert?

spess53 schrieb:
> Das knifflige ist eigentlich nur der Anfang. Die drei Bits über zwei
> Register rotieren geht so:

und warum nicht nur so?
1
  ldi r16, 7
2
  ldi r17, 0
3
xx:
4
  rol r16
5
  rol r17
6
  out PortB, r16
7
  out PortD, r17
8
  rjmp xx

von xydllfehltmalwieder (Gast)


Lesenswert?

Kann man zweimal Rechtshift für negative machen? Wenn man zuerst 80 
eingibt, und zweimal für negative shiftet, erhält man die Hexwerte 
80->C0->E0. Dann normal nach rechts weiter shiften bis Übertrag ->Reg2. 
Reg1 normal weitershiften, reg2 wieder negativ shiften, dann normal, 
Übertrag nach reg1.

Ansonsten wäre das elganteste wohl 1 Register + Ladebefehle, z.B. mov 80 
nach reg1 mov C0 nach reg1 usw oder eben die Werte aus dem Speicher oder 
aus anderen Registern lesen.

von spess53 (Gast)


Lesenswert?

Hi

>und warum nicht nur so?

Weil dann die 'Snake' teilweise nur 2 LEDs lang ist.

>xx:
>  rol r16
>  rol r17

Das Bit, das aus r17 ins Carry-Flag geschoben wird taucht erst beim 
nächsten Durchlauf in r16 auf. Außerdem stimmt der Anfang nicht. Die 
Vorgabe fängt nicht mit drei LEDs an.

MfG Spess

von bitte löschen (Gast)


Lesenswert?

Ich würde das so machen:
1
Anfang:
2
 ldi r16,0
3
 ldi r17,0
4
 ldi r18,0
5
 rcall AusgabeUndWarten
6
 ldi r16,0b10000000
7
 rcall AusgabeUndWarten
8
 ldi r16,0b11000000
9
 rcall AusgabeUndWarten
10
 ldi r16,0b11100000
11
 rcall AusgabeUndWarten
12
Scheife:
13
 lsr r16 ; rechtes Bit ins C-Flag, 
14
 rol r17 ; C-Flag von rechts in r17 übernehmen, linkes Bit von 17 ins C-Flag
15
 ror r18 ; Dieses in Bit 7 von r18 merken
16
 lsl r16 ; nochmal nach links, um
17
 rol r18 ; das linke Bit aus r18
18
 ror r16 ; beim Rechtsschieben zu übernehmen
19
 rcall AusgabeUndWarten
20
 rjmp Scheife

von Thomas T. (knibbel)


Lesenswert?

Hier eine Lösung für beliebige Anfangsbitmuster:
1
        LDI    r20,0b11100000
2
        LDI    r17,0
3
        LDI    r16,0
4
5
Loop:   MOV    r19,r20
6
        ANDI   r19,0b10000000
7
        MOV    r18,r16
8
        ANDI   r18,0b10000000
9
        OR     r18,r19
10
11
        LSL    r20
12
13
        LSR    r17
14
        OR     r17,r18
15
        ROL    r16
16
17
        OUT    PortB,r17
18
        OUT    PortD,r16
19
        RCALL  Wartendasonstzuschnell
20
21
        RJMP   Loop

Kurzer, knackiger Code! Ist sowas Kunst? :-)

Gruß, Thomas

PS: Kommentare hinter den Befehlen bitte als Hausaufgabe ... :-)

von Roland N. (eroli)


Lesenswert?

1
         LDI    r20,0b11100000   ; beliebiges Anfangsmuster
2
         LDI    r17,0
3
         LDI    r16,0
4
 
5
 Loop:   MOV    r19,r20          ; Einmalige Einsengenerierung ("Softstart")
6
         ANDI   r19,0b10000000
7
8
         MOV    r18,r16          ; War bei mir immer 0? Oder zu kurz debugged?
9
         ANDI   r18,0b10000000
10
11
         OR     r18,r19          ; Hier haben wir genau so oft 0b1000 0000
12
                                 ; stehen, wie Einsen im Anfangsmuster sind
13
14
         LSL    r20              ; Anfangsmuster wegrotieren, damit stoppt
15
                                 ; einmalige Einsengenerierung ("Softstart")
16
17
         LSR    r17              ; Eigentliche Rotation
18
19
         OR     r17,r18          ; Ab dem dritten mal ohne Einfluss
20
21
         ROL    r16              ; Verschwundene Einsen wieder auftauchen lassen
22
 
23
         OUT    PortB,r17        ; Ausgabe
24
         OUT    PortD,r16
25
         RCALL  Wartendasonstzuschnell
26
 
27
         RJMP   Loop
> Kurzer, knackiger Code! Ist sowas Kunst? :-)
>
> Gruß, Thomas
>
> PS: Kommentare hinter den Befehlen bitte als Hausaufgabe ... :-)

So in ETWA ist mir der Code jetzt klar. Habe mir ne Din A4-Seite 
geschnappt und die Bitmuster Befehl für Befehl aufgeschrieben :-D

Wirklich sehr sehr clever, wie bist du auf diese Idee gekommen?

Damit hat Thomas das Rennen gemacht, dieser Snippet ist meinem Programm 
gelandet :-D

Vielen Dank für eure fleißige Hilfe :-)

von Thomas T. (knibbel)


Lesenswert?

Gerngeschehen!

Wenn Bedarf besteht kann ich den Code gern Zeile für Zeile erklären. 
Aber erst morgen, ist schon spät (für mich...)

Gruß,
Thomas

von Thomas T. (knibbel)


Lesenswert?

Roland Moch schrieb:
> OR     r17,r18          ; Ab dem dritten mal ohne Einfluss

Deine Kommentare sind leider nicht ganz korrekt.

Hier wird z.B. das Anfangsbitmuster (die ersten drei "1"-Bits), aber 
auch das MSB der unteren Zeile eingesetzt. Und zwar immer! Nur nach dem 
dritten Durchgang gibt es für das Anfangsbitmuster ausschließlich 
"0"-Bits. Die MSB aus der unteren Zeile kommen jedoch immer wieder...

Du solltest dir den Code wesentlich länger im Debugger (Simulator) 
anschauen...

Gruß,
Thomas

von Roland N. (eroli)


Lesenswert?

Das wäre nett. Ich hab zwar oben einige Kommentare geschrieben, aber ich 
glaube die sind nicht alle 100%ig treffend.

Interessant wäre vielleicht auch dein Vorgehen gewesen, wie du auf diese 
Idee gekommen bist.

Nochmal Danke und eine gute Nacht :-)

von bitte löschen (Gast)


Lesenswert?

Philipp Klostermann schrieb:
> Ich würde das so machen:
> ...

Ist natürlich quatsch, da die LEDs links das LSB haben.
Also logisch umdrehen:
1
Anfang:
2
  ldi r16,0
3
  ldi r17,0
4
  ldi r18,0
5
  rcall AusgabeUndWarten
6
  ldi r16,0b1
7
  rcall AusgabeUndWarten
8
  ldi r16,0b11
9
  rcall AusgabeUndWarten
10
  ldi r16,0b111
11
  rcall AusgabeUndWarten
12
 Scheife:
13
  lsl r16 ; "rechtes" Bit (HSB) ins C-Flag,
14
  ror r17 ; C-Flag von "links" in r17 übernehmen, "linkes" Bit von 17 ins
15
 C-Flag
16
  rol r18 ; Dieses in Bit 0 von r18 merken
17
  lsr r16 ; nochmal nach "links", um
18
  ror r18 ; das "linke" Bit aus r18
19
  rol r16 ; beim "Rechts"-schieben zu übernehmen
20
  rcall AusgabeUndWarten
21
  rjmp Scheife

Habe das ganze gerade noch im Debugger getestet, und es scheint zu 
funktionieren.

Ich frage mich nun, was an knibbels 9 Befehlen mit 5 involvierten 
Registern, die zu verstehen und zu kommentieren einigen schwer zu fallen 
scheint, so viel genialer (sorry: "knackiger") ist, als meine 6 Befehle, 
die mit 3 Registern klar und einfach ein paar Bits rotieren. und nicht 
mal einer Erwähnung wert zu sein scheinen. :-?

von spess53 (Gast)


Lesenswert?

Hi

>Ich frage mich nun, was an knibbels 9 Befehlen mit 5 involvierten
>Registern, die zu verstehen und zu kommentieren einigen schwer zu fallen
>scheint, so viel genialer (sorry: "knackiger") ist, als meine 6 Befehle,
>die mit 3 Registern klar und einfach ein paar Bits rotieren. und nicht
>mal einer Erwähnung wert zu sein scheinen. :-?

Geht mir mit 4 Befehlen und 2 benutzten Registern auch so.

MfG Spess

von bitte löschen (Gast)


Lesenswert?

OK, Du hast ein Register weniger, aber einen bedingten Sprung. Das mit 
dem T-Flag ist natürlich eine Idee. :) Ich werde das mal im Hinterkopf 
köcheln lassen.

Ich finde, dass sich die eigentliche Lösung zu vielen Threads irgendwo 
mittendrin befindet, und unbeachtet in Flame-Wars oder gegenseitigem 
Lobgehuddel anderer User untergeht. :D

von Roland N. (eroli)


Lesenswert?

Hey Leute,

ganz ganz ruhig. Ich hab eure Lösungen auch zu Kenntnis genommen und mir 
überlegt sie einzubauen. Zumindest mit der Lösung von Spess ging es 
nicht soo einfach (da müsste ich noch ein zusätzliches Register 
einbauen), da ich das sonst mit meinem Timer nicht hinkriege.

Die 6 Befehle mit 3 Register sind bei mir SO auch nicht so einfach 
unterzubringen, da ich keine AusgabeUndWarten-Funktion habe, sondern 
einfach alles über den Timer machen will und da bräuchte ich dann auch 
wieder ein Register und mehrere Branches, damit ich das benutzen kann.

Natürlich ist das alles kein Aufwand das so umzubiegen, aber wozu, wenn 
Thomas Lösung quasi per Copy and Paste funktioniert...

Versteht mich nicht falsch, ich bin für jeden Beitrag dankbar ;-)

von bitte löschen (Gast)


Lesenswert?

Roland Moch schrieb:
> , da ich keine AusgabeUndWarten-Funktion habe, sondern
> einfach alles über den Timer machen will und da bräuchte ich dann auch
> wieder ein Register und mehrere Branches, damit ich das benutzen kann.

Die mehreren Branches brauchst Du nur, um den Ablauf am Anfang zu 
realisieren. Ansonsten ist es doch egal, ob Du den Rumpf-Code in einer 
Schleife hast, oder aus einer ISR heraus aufrufst. Wozu brauchst Du dann 
ein zusätzliches Register? Als Flag, ob das Intro vorbei ist?

von Roland N. (eroli)


Lesenswert?

Philipp Klostermann schrieb:
> Wozu brauchst Du dann
> ein zusätzliches Register? Als Flag, ob das Intro vorbei ist?

Ganz genau

von spess53 (Gast)


Lesenswert?

Hi

>Philipp Klostermann schrieb:
>> Wozu brauchst Du dann
>> ein zusätzliches Register? Als Flag, ob das Intro vorbei ist?

>Ganz genau

Geht auch ohne.

MfG Spess

von Roland N. (eroli)


Lesenswert?

spess53 schrieb:
>>Philipp Klostermann schrieb:
>>> Wozu brauchst Du dann
>>> ein zusätzliches Register? Als Flag, ob das Intro vorbei ist?
>
>>Ganz genau
>
> Geht auch ohne.

Und wie?

Worum geht es hier überhaupt noch? :-D

von bitte löschen (Gast)


Lesenswert?

Es geht darum, auf ein unnötiges Flag zu verzichten.

Schau Dir mal folgende Tabelle an:
1
  r16      r17      r16 + r17 
2
  00000001 00000000 00000001 (1)
3
  00000011 00000000 00000011 (3)
4
Wiederholung:
5
  00000111 00000000 00000111 (7)
6
  00001110 00000000 00001110 (14)
7
  ...
8
  11100000 00000000 11100000 (224)
9
  11000000 10000000 01000000 (64, C)
10
  10000000 11000000 01000000 (64, C)
11
  00000000 11100000 11100000 (224)
12
  ...
13
  00000000 00000111 00000111 (7)
14
  00000001 00000011 00000100 (4)
15
  00000011 00000001 00000100 (4)
16
  Weiter mit Wiederholung

Die Bedingung in-Intro liegt vor, wenn r16 + r17 (vorzeichenlos) kleiner 
4 ist.

von Loonix (Gast)


Lesenswert?

Roland Moch schrieb:
> Worum geht es hier überhaupt noch? :-D

Philipp Klostermann schrieb:
> Es geht darum, auf ein unnötiges Flag zu verzichten.

Jo, wenn schon Assembler dann gscheit.

von spess53 (Gast)


Lesenswert?

Hi

>Und wie?

In dem du eine RAM-Zelle benutzt. Das Ganze als Interrupt:
1
         .dseg
2
intro:   .byte 1
3
         .cseg
4
5
         .....
6
         ldi r16,0b00000111
7
         sts intro,r16
8
         .....
9
10
interrupt:
11
         push r16
12
         in r16,SREG
13
         push r16
14
         push r17
15
16
         lds r16,intro
17
18
aaa:     tst r16
19
         breq aaa10
20
21
         in r17,PortB       ; Intro
22
         lsr r16
23
         rol r17
24
         out PortB,r17
25
         sts intro,r16
26
         rjmp aaa20
27
28
aaa10:   in r16,PortD       ; Rotieren
29
         in r17,PortB
30
31
         bst r17,7
32
         lsr r16
33
         rol r17
34
         bld r16,7
35
         
36
         out PortB,r17
37
         out PortD,r16
38
39
aaa20:   pop r17
40
         pop r16
41
         out SREG,r16
42
         pop r16
43
         reti

Außerhalb der Routine wird kein Register benötigt.

MfG Spess

von Roland N. (eroli)


Lesenswert?

Also, da ihr mir keine Ruhe gelassen habt und ich die Erklärung mit der 
Tabelle von Philipp so gut fand, hab ich mir die für mich logischste und 
am einfachsten zu verstehende Lösung zusammengebastelt:
1
snake:   ;(befindet sich im Timer-Interrupt)
2
  mov temp, porb
3
  add temp, pord
4
  cpi temp, 4
5
  brsh nointro
6
    ; Schlange aufbauen
7
  com porb  ; Eine 1 rein
8
  lsl porb
9
  com porb
10
  out portB, porb
11
  out portD, pord
12
  rjmp exit
13
nointro:
14
    lsl porb ; "rechtes" Bit (HSB) ins C-Flag,
15
    ror pord ; C-Flag von "links" in r17 übernehmen, "linkes" Bit von 17 ins C-Flag
16
    rol temp ; Dieses in Bit 0 von r18 merken
17
    lsr porb ; nochmal nach "links", um
18
    ror temp ; das "linke" Bit aus r18
19
    rol porb ; beim "Rechts"-schieben zu übernehmen
20
21
  out PortB, porb
22
  out PortD, pord
23
  rjmp exit

Um ehrlich zu sein, denke ich, dass ich mich jetzt lange genug mit 
diesem Thema aufgehalten habe, nehmt es mir nicht übel ;-)

von bitte löschen (Gast)


Lesenswert?

spess53 schrieb:
> In dem du eine RAM-Zelle benutzt.

Oder so. :)

Roland M. schrieb:
> Um ehrlich zu sein, denke ich, dass ich mich jetzt lange genug mit
> diesem Thema aufgehalten habe, nehmt es mir nicht übel ;-)

Wir auch. Dann kannst Du noch die paar Minuten investieren, meine 
abschließende Lösung zu inspizieren. :-)

Ich habe es jetzt wie folgt: (Debugger-getestet)
1
RESET: ldi r16,high(RAMEND); Main program start
2
  out SPH,r16 ; Set Stack Pointer to top of RAM
3
  ldi r16,low(RAMEND)
4
  out SPL,r16
5
  ldi r16, 0
6
  out TCNT2, r16
7
  ; ldi r16,(1 << CS22) | (1 << CS21) | (1 << CS20) ; prescaling = /1024
8
  ldi r16, (0 << CS22) | (0 << CS21) | (1 << CS20) ; no prescaling fürs Debuggen
9
  out TCCR2, r16
10
  ;Interrupt für Timer 2 Aktivieren:
11
  ldi r16, (1<<TOIE2)
12
  out TIMSK, r16
13
14
/* ; Für Echtbetrieb unkommentieren
15
  ldi r16,0xff
16
  out DDRB, r16
17
  out DDRD, r16
18
*/
19
20
   ldi r16,0
21
  ldi r17,0
22
23
  sei ; Enable interrupts
24
25
MAINLOOP:
26
  rjmp MAINLOOP
27
  
28
TIMER_ISR:
29
  ; folgende Abfolge:
30
/*
31
  r16      r17      r16 + r17 
32
  00000001 00000000 00000001 (1)
33
  00000011 00000000 00000011 (3)
34
Wiederholung:
35
  00000111 00000000 00000111 (7)
36
  00001110 00000000 00001110 (14)
37
  ...
38
  11100000 00000000 11100000 (224)
39
  11000000 10000000 01000000 (64, C)
40
  10000000 11000000 01000000 (64, C)
41
  00000000 11100000 11100000 (224)
42
  ...
43
  00000000 00000111 00000111 (7)
44
  00000001 00000011 00000100 (4)
45
  00000011 00000001 00000100 (4)
46
  ab hier Wiederholung
47
*/
48
  push r18
49
  in r18, SREG
50
  push r18
51
  sei ; Die eigentlichen Aufgaben nicht stören
52
  mov r18, r16
53
  add r18, r17
54
  cpi r18, 4
55
  brlo Timer_ISR_Intro
56
  lsl r16 ; "rechtes" Bit (HSB) ins C-Flag,
57
  ror r17 ; C-Flag von "links" in r17 übernehmen, "linkes" Bit von 17 ins C-Flag
58
  rol r18 ; Dieses in Bit 0 von r18 merken
59
  lsr r16 ; nochmal nach "links", um
60
  ror r18 ; das "linke" Bit aus r18
61
  rol r16 ; beim "Rechts"-schieben zu übernehmen
62
  rjmp Ende_Timer_ISR
63
Timer_ISR_Intro:
64
  sec
65
  rol r16
66
Ende_Timer_ISR:
67
; Für Echtbetrieb unkommentieren
68
  ; out PORTB, r16
69
  ; out PORTD, r17
70
  pop r18
71
  out SREG, r18
72
  pop r18
73
  reti

von Roland N. (eroli)


Lesenswert?

Das komplette Programm wäre nicht nötig gewesen ;-)
1
  sec
2
  rol r16

Das hab ich jetzt noch übernommen (also eigentlich fast gleich :-D)

Nochmals Danke an alle Antwortenden und ich denke wir sollten das Thema 
jetzt ruhen lassen ;-)

Einen schönen, sonnigen Mittwoch an Alle :-)

von Ralf (Gast)


Lesenswert?

Was, schon aufgegeben? ;-)
Wie sieht's mit einem Outro aus?

duck und wech...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.