-
1. Data: 2020-09-29 21:35:19
Temat: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: Atlantis <m...@w...pl>
Walczę teraz z pewną dziwną zagadką, która teoretycznie nie powinna mieć
miejsca. Chodzi o jeden z moich projektów retro na 6502, ale
teoretycznie nie powinno mieć to nic wspólnego z procesorem.
Oprogramowanie napisane w C i skompilowane kompilatorem CC65.
Kod źródłowy jest dostępny tutaj:
https://github.com/marekw1986/RetroEG/tree/master/co
de
Wszystko zaczęło się od tego, gdy dodałem do projektu funkcje do obsługi
przycisków - funkcje, które w innym projekcie (na takiej samej płytce,
tylko z nieco innymi peryferiami) działały bez problemu, a tutaj nie
chciały. Po ich dodaniu układ zaczął się zachowywać skrajnie nie
stabilnie - samoczynne resety co kilkadziesiąt sekund, co chwilę
detekcja fałszywych wciśnięć przycisków itp.
Dość długo szukałem błędu w kodzie, ale nie mogłem go znaleźć. Upewniłem
się, że winy nie ponosi uszkodzony RAM albo procesor. Aż w końcu
zacząłem eksperymentować ze zmiennymi - zmniejszyłem rozmiar niektórych
buforów, część zmiennych zmieniłem z globalnych na lokalne. Pomogło!
Samoczynne resety ustały, fałszywe wciśnięcia przycisków występują, ale
bardzo rzadko.
Sprawa jest jednak bardzo dziwna, bo wygląda to tak, jakbym teraz był na
skraju maksymalnego wykorzystani RAM-u. A tak nie powinno być. Układ ma
8kB pamięci RAM, więc naprawdę dużo jak na taki "kontroler". W pamięci
nie ma za dużo zmiennych globalnych. Są tylko dwa większe bufory (po 256
bajtów każdy), poza tym parę mniejszych buforów, trochę stosunkowo
niewielkich struktur i umiarkowna ilość zmiennych. Nie ma szansy, żeby
to nagle zajęło zdecydowaną większość RAM-u. Nie widzę też niczego, co
mogłoby pożerać stos - nie używam nigdzie rekurencji ani nie stosuję
większych zmiennych globalnych, zbyt zagnieżdżonych wywołań funkcji też
nie ma.
Gdyby ktoś wpadł na pomysł, że to wina niewielkiego sprzętowego stosu
6502 (zaledwie 256 bajtów w pierwszej stronie pamięci) to tłumaczę, że
CC65 korzysta z programowego stosu, który działa tak, jak we
współczesnych MCU, zapisując dane od końca pamięci w dół.
Fragment pliku .map wygenerowanego podczas linkowania projektu
potwierdza, że zmienne globalne zajmują bardzo małą część pamięci:
Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
ZEROPAGE 000000 000019 00001A 00001
DATA 000200 00024A 00004B 00001
BSS 00024B 0004FF 0002B5 00001
STARTUP 00804B 008066 00001C 00001
ONCE 008067 008072 00000C 00001
CODE 008073 00A022 001FB0 00001
RODATA 00A023 00A1D6 0001B4 00001
VECTORS 00FFFA 00FFFF 000006 00001
Plik cfg wygląda następująco:
MEMORY {
ZP: start = $0, size = $100, type = rw, define = yes;
RAM: start = $200, size = $1E00, define = yes;
ROM: start = $8000, size = $8000, file = %O;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, define = yes;
DATA: load = ROM, type = rw, define = yes, run = RAM;
BSS: load = RAM, type = bss, define = yes;
HEAP: load = RAM, type = bss, optional = yes;
STARTUP: load = ROM, type = ro;
ONCE: load = ROM, type = ro, optional = yes;
CODE: load = ROM, type = ro;
RODATA: load = ROM, type = ro;
VECTORS: load = ROM, type = ro, start = $FFFA;
}
FEATURES {
CONDES: segment = STARTUP,
type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__;
CONDES: segment = STARTUP,
type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
}
SYMBOLS {
# Define the stack size for the application
__STACKSIZE__: value = $0200, type = weak;
}
Ktoś ma jakiś pomysł co do tego, gdzie można szukać przyczyny? Na chwilę
obecną urządzenie działa w miarę stabilnie, jednak chciałem dodać
jeszcze kilka funkcji, które będą wymagały trochę RAM-u, więc przedtem
muszę rozwiązać ten problem.
-
2. Data: 2020-09-29 22:12:56
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: heby <h...@p...onet.pl>
On 29/09/2020 21:35, Atlantis wrote:
> Dość długo szukałem błędu w kodzie, ale nie mogłem go znaleźć.
Nie myślałeś aby napisać mały emulator cpu (ba, ukraśc gotowca, jest ich
zilion) + trywialny emulator hardware (tych przycisków) i debugować in
vitro, co tam się dzieje? Jak już masz transakcje cpu typu "zapisz bajt"
i "odczytaj bajt" to napisanie kawałka kodu który udaje że jest
przyciskiem, nie jest chyba jakieś strasznie trudne.
Powoli myslę nad zrobieniem małego komputera na CP/M (i mam nadzieje
GEMa, jak mi się uda), ale wiem że pracę zaczne *wirtualnie*, czyli
piszać emulator cpu, hardware i to będzie moje miejsce do debugu, nie
fizyczne hardware, ono powstanie później lub równolegle...
-
3. Data: 2020-09-29 22:36:30
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: heby <h...@p...onet.pl>
On 29/09/2020 21:35, Atlantis wrote:
> zacząłem eksperymentować ze zmiennymi - zmniejszyłem rozmiar niektórych
> buforów, część zmiennych zmieniłem z globalnych na lokalne. Pomogło!
To może wskazywać np. na to że nie wracasz z przerwania (masz jakieś?)
lub się nie wyrabiasz i po jakims czasie przekręca się stos sprzętowy.
Taka luźna myśl. Co prawda widzę CLI na początku maina, ale może cos
przeoczyłem.
Jeszcze jedno: nie przestawiasz gdzieś procesora w tryb BCD?
I jeszcze jedno: Masz gdzieś podpięte SO (pin cpu)?
Sorry, ale review kodu nie zrobie... za duży :)
-
4. Data: 2020-09-29 23:04:13
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: Atlantis <m...@w...pl>
On 29.09.2020 22:36, heby wrote:
> To może wskazywać np. na to że nie wracasz z przerwania (masz jakieś?)
Systemy na 6502 mają tylko jeden wektor przerwań. Linia INT jest
zwierana do masy przez poszczególne urządzenia zgłaszające przerwania,
za pomocą wyjść typu OC. W kodzie obsługi przerwania trzeba po kolei
odpytać wszystkie urządzenia i zdjąć flagi. Jest więc tylko jedna
procedura obsługująca przerwanie, zakończona instrukcją RTI, więc
program wraca z przerwania.
Na początku procedury zapisuję na stosie zawartość rejestrów A oraz X
(tylko z nich korzystam) a przed powrotem je przywracam.
Wszystkie funkcjonalności związane z przerwaniami działają poprawnie.
> lub się nie wyrabiasz i po jakims czasie przekręca się stos sprzętowy.
Co masz na myśli?
> Co prawda widzę CLI na początku maina, ale może cos przeoczyłem.
W 6502 CLI to instrukcja "clear interrupt DISABLE", natomiast SEI
oznacza "set interrupt DISABLE". Czyli CLI włącza przerwania, SEI je
wyłącza. Odwrotnie niż w AVR-ach czy PIC-ach.
> Jeszcze jedno: nie przestawiasz gdzieś procesora w tryb BCD?
Świadomie nie. Cały program zawiera tylko kilka asemblerowych fragmentów
(m.in. procedura obsługi przerwań) i jestem pewien, że w żadnym z nich
nie stosowałem instrukcji SED.
> I jeszcze jedno: Masz gdzieś podpięte SO (pin cpu)?
Podciągnięty do plusa.
-
5. Data: 2020-09-29 23:19:55
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: heby <h...@p...onet.pl>
On 29/09/2020 23:04, Atlantis wrote:
>> lub się nie wyrabiasz i po jakims czasie przekręca się stos sprzętowy.
> Co masz na myśli?
Przerwanie jest przerywane przerwaniem. Dośc częsta wpadka generująca
rózne problemy, podobne do opisanych.
>> Co prawda widzę CLI na początku maina, ale może cos przeoczyłem.
> W 6502 CLI to instrukcja "clear interrupt DISABLE", natomiast SEI
> oznacza "set interrupt DISABLE". Czyli CLI włącza przerwania, SEI je
> wyłącza. Odwrotnie niż w AVR-ach czy PIC-ach.
Wiem, ale nie byłem pewny czy z przerwań korzystasz.
-
6. Data: 2020-09-29 23:27:08
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: heby <h...@p...onet.pl>
On 29/09/2020 23:04, Atlantis wrote:
> Systemy na 6502 mają tylko jeden wektor przerwań.
No niezupełnie, ogólnie rzecz biorąc jest IRQ i NMI, mają osobne wektory.
-
7. Data: 2020-09-29 23:50:15
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: Atlantis <m...@w...pl>
On 29.09.2020 23:19, heby wrote:
> Przerwanie jest przerywane przerwaniem. Dośc częsta wpadka generująca
> rózne problemy, podobne do opisanych.
Nawet w przypadku 6502? Jest jedno wejście INT. Jeśli kolejne urządzenie
zgłosi przerwanie przed skasowaniem flagi poprzedniego nic się nie
stanie, bo system nie będzie miał jak wiedzieć, że przyszło kolejne
przerwanie. Dopiero po opuszczeniu procedury obsługi przerwania zostanie
ona wywołana ponownie.
Nie jestem pewien co do sytuacji, kiedy kolejne przerwania przychodzi
dopiero po zdjęciu flagi poprzedniego (urządzenie peryferyjne odłącza
wejście INT od masy) ale przed wykonaniem RTI.
Kwestię NMT pomijam, bo wcale z niego nie korzystam.
Swoją drogą, jest jakiś prosty sposób na ocenienie użycia stosu
sprzętowego 6502 przez wywołania funkcji C? Może faktyczne mam za dużo
zagnieżdżeń? Niby argumenty i zmienne lokalne są przechowywane na stosie
programowym, ale jednak dresy powrotu ciągle trafiają na sprzętowy...
Tylko z drugiej strony w takim wypadku manipulowanie rozmiarem buforów
nie miałoby wpływu na sytuację...
-
8. Data: 2020-09-30 07:24:18
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: heby <h...@p...onet.pl>
On 29/09/2020 23:50, Atlantis wrote:
> Nie jestem pewien co do sytuacji, kiedy kolejne przerwania przychodzi
> dopiero po zdjęciu flagi poprzedniego (urządzenie peryferyjne odłącza
> wejście INT od masy) ale przed wykonaniem RTI.
Codzi o zagnieżdzenie NMI/IRQ. Jeśli nie używasz jednego z nich to może
to fałszywy trop.
Ale oceń jaki możesz mieć najdłuższy stos głównego programu i jaki
możesz miec najdłuższy stos w przerwaniu, shit happens.
> Swoją drogą, jest jakiś prosty sposób na ocenienie użycia stosu
> sprzętowego 6502 przez wywołania funkcji C?
Emulatorem, własnym.
Np, jeden z ziliona:
https://github.com/redcode/6502
Weryfikacja formalna, jesli masz przerwania, jest skrajnie kłopotliwa,
ale weryfikacja runtime, przez unit testy na emulatorze, raczej prosta i
odwdzięczy się na wiele różnych sposobów poźniej.
Narzędzi do formalnej weryfikacji stosu na 6502 nie znam, wydaje mi się
jednak że widziałem takie do clanga jako ciekawostkę. Ale to Ci się nie
przyda, poza jakimiś hobbystycznymi eksperymentami nie ma backendu do
6502 w clangu.
> Tylko z drugiej strony w takim wypadku manipulowanie rozmiarem buforów
> nie miałoby wpływu na sytuację...
Możliwe że problem tylko tak się objawia, to nie jest przyczyna.
Na ile ufasz, że kod generowany przez kompilator, jest prawidłowy?
-
9. Data: 2020-09-30 11:29:20
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: a...@h...invalid (Arnold Ziffel)
Atlantis <m...@w...pl> wrote:
> Sprawa jest jednak bardzo dziwna, bo wygląda to tak, jakbym teraz był na
> skraju maksymalnego wykorzystani RAM-u.
Też tak myślę. Zupełnie nie znam 6502 ani tego, jak Twój kompilator
alokuje pamięć, ale jesteś w stanie zrobić i wpleść w newralgiczne miejsca
test, który wypluje na dowolne wyjście informację, jeśli stos (wskaźnik
stosu) niebezpiecznie zbliży się do wskaźnika sterty?
Używasz tam w ogóle dynamicznej alokacji pamięci, czy wszystko jest
statycznie? Jeśli używasz dynamicznej alokacji, to masz pewność, że nie
masz żadnych wycieków?
Takie problemy, o jakich mówisz (wszystko działa, dodaję prostą
funkcjonalność i wszystko zaczyna się losowo sypać), miałem w niewielkim
projekcie na AVR i wynikały właśnie z nadpisywania sterty przez stos.
Projekt niewielki, ale okazało się, że wystarczający.
--
Po wybuchu jądrowym na uschniętym kikucie ocalałego drzewa siedzą
dwa szympansy.
- Masz cos do jedzenia? - pyta samiec.
Samica podaje mu jabłko.
- O nie, - protestuje samiec - nie będziemy tej idiotycznej historii
powtarzać od początku!
-
10. Data: 2020-09-30 16:47:33
Temat: Re: Pamięć nadpisuje stos (choć powinno być mnóstwo miejsca)
Od: Atlantis <m...@w...pl>
On 30.09.2020 11:29, Arnold Ziffel wrote:
> Takie problemy, o jakich mówisz (wszystko działa, dodaję prostą
> funkcjonalność i wszystko zaczyna się losowo sypać), miałem w niewielkim
> projekcie na AVR i wynikały właśnie z nadpisywania sterty przez stos.
> Projekt niewielki, ale okazało się, że wystarczający.
Ok. Wygląda na to, że udało mi się namierzyć źródło problemu.
Kod obsługujący przerwanie wywoływał funkcję. która zawierała polecenia
rozkazy SEI/CLI (zabezpieczenie przed uszkodzeniem zawartości zmiennej
przez przerwanie). Jednak w przypadku 6502 użycie tych rozkazów w
trakcie obsługi może spowodować wystąpienie kolejnego przerwania, a w
efekcie nadpisanie stosu sprzętowego i niestabilne działanie.
Przepisałem kod w ten sposób, żeby pozbyć się wywołania tej funkcji, a
więc i SEI/CLI. Teraz urządzenie zdaje się działać poprawnie.
W drugim projekcie tego kodu nie było, więc działało poprawnie.