Wiesz co, sorki, ale analiza i poszukiwania błędu w kodzie zawierającym takie teksty jak:
;pewnie zgubilem bajta;]
; z tymi pushami i popami przebieg na oscyloskopie jest inny niz bez nich
wskazuje na to, że program pisany był trochę na kolanie metodą prób i błędów, a to nie dobry styl do analizy.
Ale że nie jestem straszny postanowiłem sam napisać coś od siebie. Poniżej znajdziesz najprawdopodobniej poprawny (testowałem i chyba jest dobrze, choć na uP nie sprawdzałem, bo nie chciało mi się :) ).
Oczywiście mój kod jest przykładowy. W praktyce pod uwagę musisz wziąć o wiele wiele wiele więcej rzeczy, bo póki wątki żyją same i nie korzystają z timerów to jest jeszcze dobrze. W momencie kiedy zaczną, to nie masz wyjścia i musisz dodatkowo robić zrzuty timerów, co nie jest proste, bo ich wartości przecież nie mają prawa "drgnąć" podczas zrzutów, tak by nie zaburzyć wątków (no chyba że timery też chodzą na przerywaniach, ale to zupełnie inna sprawa i jeszcze bardziej skomplikowana sytuacja).
W zasadzie powinieneś robić zrzut całej pamięci danych poniżej 0x60 a potem to przywracać. Szkoda, że nie da się przestawiać tego obszaru za jednym zamachem...
Ale póki nie ma timerów to jest w miarę prosto. Tylko należy jeszcze umiejętnie kontrolować stan portów lub robić ich zrzuty jeśli trzeba/można.
Co do kodu, myśle że nie jest skomplikowany. Jak coś to pisz, postaram się uzasadnić dlaczego tak a nie inaczej.
.include "m8def.inc"
.cseg
.org 0x00
rjmp RESET ;ustawiamy wektor dla resetu
.org 0x09
rjmp timer0 ;ustawiamy wektor dla przerywania przeładowania TIMER0
.org 0x13
RESET:
;*********************** sprawy wstępne ***********************
;zapamiętujemy stos dla obu "wątków", czyli ustawiamy jego
;początkowe wartości
ldi r28, high(RAMEND>>1)
sts stosadresA, r28
ldi r28, low(RAMEND>>1)
sts stosadresA+1, r28
ldi r28, high(RAMEND)
sts stosadresB, r28
ldi r28, low(RAMEND)
sts stosadresB+1, r28
;ustawiamy kod startowy dla "wątku" (0 to pierwszy A, 1 to drugi B)
;ustawiliśmy B bo A uruchomimy sami, więc przerwanie powinno wystartować B
ldi r28, 1
sts kod, r28
;*********************** sprawy wstępne dla wątku B ***********************
;ustawiamy stos "wątku" B i dodajemy do niego informacje zwrotne
;dla wątku A nie dodajemy nic, ponieważ wątek ten uruchomimy przed wywołaniem przerwania
;przerywanie dodaje do stosu informacje oraz ściąga ze stosu informacje. Ponieważ
;wątek A uruchomimy ręcznie to stos nie musi zawierać żadnych informacji, natomiast
;wątek B w momencie wywołania przerywania będzie traktowany tak, jakby już był raz przerzucany
;czyli jego stos musi zawierać informacje o stanie wątku
;odczytujemy wskaźnik stosu wątku B
lds r28, stosadresB
out SPH, r28
lds r28, stosadresB+1
out SPL, r28
;zapisujemy adres zwrotny wątku B
ldi r28, low(watekB)
push r28
ldi r28, high(watekB)
push r28
ldi r28, 0
;dla kompatybilności z przerywaniem musimy zapamiętać dokładnie 16 rejestrów (plus 1 zrzu SREG), tak
;by stos miał odpowiedni kształt w momencie pierwszego uruchomienia wątku B
;oczywiście w tym momencie nie ma znaczenia co zapamiętamy, równie dobrze moglibyśmy
;odjąc 16 od wskaźnika stosu (plus 1 zrzut rejestru SREG), ale dla czytelności zrobiliśmy zrzut
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
push r24
push r25
push r26
push r27
push r28
push r29
push r30
push r31
push r28 ;WAŻNE, zapamiętujemy tutaj dowolną wartość dla SREG
;zapisujemy nowy wskaźnik stosu
in r28, SPH
sts stosadresB, r28
in r28, SPL
sts stosadresB+1, r28
;*********************** rozpoczynamy grę ***********************
;*** podszywając się pod wątek A, który potem uruchomimy sami ***
; ustawiamy domyślnie stos wątku A
lds r28, stosadresA
out SPH, r28
lds r28, stosadresA+1
out SPL, r28
ldi r28, 0
out TCNT0, r28
; ldi r28, (1<<CS00) | (1<<CS02) ;dzielenie przez 1024
ldi r28, (1<<CS00) ;dzielenie przez 1
out TCCR0, r28
ldi r28, (1<<TOIE0) ;ustawiamy przerywanie przy przeładowaniu
out TIMSK, r28
sei
rjmp watekA ;ręcznie uruchamiamy wątek A
; ************************ OBSŁUGA PRZERWANIA PRZEŁADOWANIA DLA TIMER0 ************************
timer0:
;zapamiętujemy stan kilku rejestrów (w tym przypadku to połowa)
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
push r24
push r25
push r26
push r27
push r28
push r29
push r30
push r31
;zapamiętujemy stan SREG, bo inaczej po powrocie z przerwania
;stracimy informacje o flagach
in r28, SREG
push r28
;sprawdzamy który kod uruchomić
lds r28, kod
cpi r28, 0
breq uruchomwatekA
;zapamiętujemy również aktualny adres stosu danego wątku
;a ponieważ mamy uruchomić wątek B to zapamiętujemy adres dla wątku A
in r28, SPH
sts stosadresA, r28
in r28, SPL
sts stosadresA+1, r28
;uruchamiamy wątek B
;ustawiamy adres jego stosu
lds r28, stosadresB
out SPH, r28
lds r28, stosadresB+1
out SPL, r28
;zmieniamy informacje o kodzie na kod A
ldi r28, 0
sts kod, r28
rjmp koniectimer0
uruchomwatekA:
;zapamiętujemy również aktualny adres stosu danego wątku
;a ponieważ mamy uruchomić wątek A to zapamiętujemy adres dla wątku B
in r28, SPH
sts stosadresB, r28
in r28, SPL
sts stosadresB+1, r28
;uruchamiamy wątek A
;ustawiamy adres jego stosu
lds r28, stosadresA
out SPH, r28
lds r28, stosadresA+1
out SPL, r28
;zmieniamy informacje o kodzie na kod B
ldi r28, 1
sts kod, r28
koniectimer0:
;przywracamy jego rejestry danego wątku
pop r28
out SREG, r28
pop r31
pop r30
pop r29
pop r28
pop r27
pop r26
pop r25
pop r24
pop r23
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
push r28
ldi r28, 0 ;zerujemy TCNTO
out TCNT0, r28
pop r28
sei
ret
;************************************ WĄTEK A ************************************
watekA:
;przykładowy kod wątku
;ten wątek korzysta odlicza do 10 w rejestrach r20, r21, r22, czyli razem do 30
;potem zwiększa rejestr r23 o jeden i jak ten dojdzie do 5 to zmienia stan bitu 0 w PORTB
;i odlicza kilka razy operując na rejestrze r31
ldi r23, 0
skokA1:
ldi r20, 0
ldi r21, 0
ldi r22, 0
skokA2:
inc r20
cpi r20, 10
brne skokA2
skokA3:
inc r21
cpi r21, 10
brne skokA3
skokA4:
inc r22
cpi r22, 10
brne skokA4
inc r23
cpi r23, 5
brne skokA1
in r28, PORTB
ldi r29, 1
eor r28, r29
out PORTB, r28
ldi r31, 0
skokA5:
dec r31
brne skokA5
rjmp watekA
;************************************ WĄTEK B ************************************
watekB:
;przykładowy kod wątku (kopia wątku A, tylko z minimalnymi zmianami)
;ten wątek korzysta odlicza do 5 w rejestrach r20, r21, r22, czyli razem do 15
;potem zwiększa rejestr r23 o jeden i jak ten dojdzie do 10 to zmienia stan bitu 1 w PORTB
;i odlicza kilka razy operując na rejestrze r31
ldi r23, 0
skokB1:
ldi r20, 0
ldi r21, 0
ldi r22, 0
skokB2:
inc r20
cpi r20, 5
brne skokB2
skokB3:
inc r21
cpi r21, 5
brne skokB3
skokB4:
inc r22
cpi r22, 5
brne skokB4
inc r23
cpi r23, 10
brne skokB1
in r28, PORTB
ldi r29, 2
eor r28, r29
out PORTB, r28
ldi r31, 0
skokB5:
dec r31
brne skokB5
rjmp watekB
.dseg
.org 0x60
stosadresA: .DW 0 ; zapamiętujemy stan stosu dla wątku A
stosadresB: .DW 0 ; zapamiętujemy stan stosu dla wątku B
kod: .DB 0 ; informacja o tym, który wątek wystartować
</quote>
oczywiście miało być
nie dobry -> niedobry
Dalej nie czytam, bo znów wyjdzie 5 postów :P