Hm... Od czego by tu zacząć
Po pierwsze: jeśli chcesz zgłębić wiedzę o asemblerze w avr, a kiepsko u Ciebie z angielskim, to polecam książkę Jarosława Dolińskiego "Mikrokontrolery AVR w praktyce".
Po drugie: zapomnij o wpisywaniu czegokolwiek do pamięci Flash w trakcie działania programu. Tego się nie robi (a jeśli mamy działać "jak Pan Bóg przykazał", to przyjmijmy, że po prostu się nie da). Nie robi się tego po pierwsze dlatego, że pamięć Flash jest pamięcią, w której siedzi praktycznie tylko treść samego programu - instrukcje do wykonania dla procesora. Po drugie dlatego, że pamięć Flash można zapisać jedynie około 10 000 razy, więc gdyby tak cały czas na niej dokonywać operacji zapisu, szybko byśmy ją zdewastowali.
Pamięcią dostępną dla operacji dziejących się podczas wykonywania programu, swego rodzaju obszarem roboczym jest pamięć RAM. Brudnopis - który można "piłować" do woli, a operacje zapisu/odczytu są w miarę szybkie.
Dlatego pozostańmy przy RAM-ie.
Jako że pamięć RAM i Flash to dwie osobne bazy o nieco innej naturze, należy pamiętać, że każda z nich ma własną przestrzeń adresową. Pamięć RAM nie zaczyna się od któregoś tam adresu pamięci Flash ani odwrotnie.
Dlatego kiedy zaczynasz pisać swój kod, wpisujesz gdzieś na początku dyrektywę np. ".cseg 0" czy ".cseg 0x0000" etc. Oznacza to, że od tego miejsca w kodzie wszystkie składniki kodu zaczną być zapisywane w pamięci Flash (cseg = code segment), począwszy od adresu 0 (ale uwaga! to jest adres we Flash, nie mający nic wspólnego z RAM).
Jeśli chcesz zająć (lub "zarezerwować" coby brzmiało ładniej
) trochę pamięci RAM, musisz się "przełączyć na ten tryb" poprzez dyrektywę ".dseg". Proponuję wydzielić sobie trochę miejsca na początku lub na końcu kodu - tak, żeby łatwo Ci było tam zajrzeć i napisać od nowej linii: .dseg 0x60
Czemu 0x60 a nie 0x00? W niższych od 0x60 adresach siedzą informacje m.in. o rejestrach we/wy, więc powinniśmy zostawić je w spokoju. Natomiast od 0x60 w górę można śmiało zapisywać informacje.
Jeśli potrzebujesz, powiedzmy, 10-bajtowej przestrzeni w pamięci RAM, to z powodzeniem możesz to zrobić tak:
.dseg 0x60 ; od tej pory mozemy rezerwowac sobie miejsce w ramie
MojeDane: ; tutaj definiujemy etykiete wskazujaca na adres w ktorym "znajduje sie" nasz kod, w tym wypadku MojeDane = 0x60
.byte 10 ; rezerwujemy 10 bajtow (kolejna rezerwacja zostanie zapoczatkowana od adresu MojeDane+10)
A teraz wisienka na torcie - czyli jak operować taką przestrzenią, skoro już ją mamy
Oczywiście operuje na niej nasz program, czyli kod zapisany w pamięci Flash, a zatem wracamy z powrotem do naszego kodu zawartego w .cseg i tam dokonujemy operacji takich jak np.:
sts MojeDane, r16 ; zapisanie do komorki 0x60 wartosci rejestru r16
sts MojeDane + 1, r20 ; zapisanie do komorki ram 0x61 wartosci rejestru r20
lds r16, MojeDane ; wczytanie do rejestru r16 wartosci przechowywanej w komorce ram 0x60
lds r18, MojeDane + 6 ; wczytanie do r18 wartosci przechowywanej w komorce ram o adresie 0x66
itd.
Dwie powyższe instrukcje wystarczą do prostego wpisywania i wyciągania bajtów z pamięci RAM. Zapomnij o używaniu instrukcji LPM, która (jak podpowiada jej nazwa Load Program Memory) zajmuje się odczytem z pamięci Flash.
Żeby jednak bardziej zautomatyzować swój program, można posłużyć się specjalnymi rejestrami, tzw. X, Y i Z (które tak naprawdę są parami rejestrów kolejno: 26 i 27, 28 i 29, 30 i 31). Rejestry są czymś na kształt wskaźników z języków wysokiego poziomu, tj. potrafią jakby wskazywać na dane miejsca, a także skakać po nich w sposób uporządkowany. Jednym z takich skoków jest inkrementacja i dekrementacja - czyli dokonujesz operacji na tym rejestrze, a potem on sam przeskakuje na kolejny adres. Stąd już bardzo niedaleko do zbudowania jakiejś ładnej pętli
Żeby użyć takiego rejestru "wskaźnikowego", trzeba mu najpierw nadać wartość, czyli adres początku naszego 10-bajtowego bloku. Weźmy dla przykładu Y-greka. A zatem słynne już:
ldi YH, HIGH(MojeDane)
ldi YL, LOW(MojeDane)
Od tej pory Y wskazuje na 0x60. Następnie możemy dokonać jakiejś operacji na nim, np. zapisu lub odczytu. Odpowiednikami wcześniej wspomnianych sts i lds są tutaj st i ld. A zatem:
st Y, r16 ; zapisanie do Y (czyli 0x60) wartosci r16
ld r16, Y ; pobranie do r16 zawartosci Y (czyli 0x60)
st Y+, r16 ; zapisanie do Y (czyli 0x60) wartosci r16 i przeskoczenie Y na 0x61
ld r16, Y+ ; pobranie do r16 zawartosci Y i przeskoczenie na 0x62
(Przy dekrementacji trzeba pamiętać, że występuje ona zawsze przed operacją)
st -Y, r16 ; najpierw przeskoczenie Y o jeden w dol (z powrotem na 0x61) i potem zapisanie r16 do nowego Y
ld r16, -Y ; najpierw przeskok Y na 0x60, a potem zaladowanie do r16 wartosci spod nowego Y
To tak w dużym skrócie
Mam nadzieję, że się przyda. Gdybyś jednak przemógł się trochę do angielskiego lub jeśli znasz niemiecki, to bardzo dobry jest blog Gerharda Schmidta:
http://www.avr-asm-tutorial.net/avr_en/ ... index.html