Sesja 13 — Licznik instrukcji: skoki zamiast tylko ‘do przodu’
✅ Sprawdź się z sesji 12
Cel
Zamienić pętlę for (która zawsze idzie po kolei, o jeden do przodu) na jawny licznik instrukcji (pc, program counter) — zmienną, którą sama instrukcja może zmienić, żeby wykonanie “skoczyło” gdzie indziej. To dokładnie mechanizm JP z ćwiczenia 1 CHIP-8.
Teoria w pigułce
Ograniczenie pętli for
for (int i = 0; i < n; i++) zawsze przechodzi przez instrukcje po kolei, co jeden. Nie da się nią wyrazić “wróć do instrukcji 2” ani “przeskocz od razu do instrukcji 8” — a bez tego nie da się napisać pętli w samym wykonywanym programie (w odróżnieniu od pętli w kodzie C, który go wykonuje).
pc jako zwykła zmienna, while zamiast for
int pc = 0; // "program counter" -- ktora instrukcja jest teraz wykonywana
while (pc < liczba_instrukcji) {
struct Instrukcja instr = program[pc];
pc++; // domyslnie: idz do nastepnej
switch (instr.kod) {
case OP_JUMP:
pc = instr.wartosc; // NADPISUJE pc -- to jest skok!
break;
// ... reszta operacji jak dotad
}
}Kluczowa różnica względem sesji 9-12: pc++ wykonuje się najpierw, zanim switch zdąży cokolwiek zrobić — dokładnie tak samo, jak w zadaniu 4 ćwiczenia 1 CHIP-8 (“zwiększ pc o 2 zanim wykonasz instrukcję”). Dzięki temu OP_JUMP może po prostu nadpisać pc swoją docelową wartością, bez martwienia się, że zaraz zostanie ona jeszcze raz zmieniona.
chip8.pc z ćwiczenia 1
Różnica jest kosmetyczna: tam pc rośnie o 2 (bo instrukcja zajmuje 2 bajty), tutaj o 1 (bo Wasza tablica trzyma całe struktury, nie bajty). Mechanizm — “zwiększ najpierw, skok może to nadpisać” — jest identyczny.
Zadania
Zadanie 1 — dodaj OP_JUMP
Przepisz maszynę z sesji 12 na while z jawnym pc, jak w sekcji teorii. Dodaj OP_JUMP do enum OpCode — pole wartosc instrukcji będzie teraz oznaczać “docelowy indeks w tablicy program”, nie literał do wpisania w rejestr.
Zadanie 2 — pętla nieskończona (celowo!)
Napisz “program” z instrukcją OP_JUMP wskazującą wstecz, na samą siebie albo wcześniejszą instrukcję — tak, żeby stworzyć celową pętlę nieskończoną. Uruchom (i przerwij Ctrl+C). To pokazuje, że nic w samej maszynie nie chroni przed nieskończonym wykonywaniem — dokładnie tak samo jak w prawdziwym procesorze: jeśli program ma błąd logiczny prowadzący do pętli bez wyjścia, procesor będzie ją wykonywał w nieskończoność, absolutnie posłusznie.
Zadanie 3 — pętla z warunkiem końca (bez if w maszynie — jeszcze)
Spróbuj napisać “program”, który liczy w dół od 5 do 0, używając tylko OP_JUMP i rejestrów (bez żadnej instrukcji warunkowej — jeszcze jej nie macie, to temat sesji 14). Zorientujesz się, że nie da się tego zrobić poprawnie — OP_JUMP zawsze skacze, bezwarunkowo, więc pętla nigdy się nie zatrzyma sama. Zanotuj to doświadczenie — następna sesja rozwiąże dokładnie ten problem.
Kryterium sukcesu
- Maszyna używa jawnego
pcw pętliwhile, nieforz automatycznymi++. OP_JUMPpoprawnie przenosi wykonanie do wskazanego indeksu — potrafisz to pokazać zarówno skokiem do przodu, jak i wstecz.- Rozumiesz i potrafisz wytłumaczyć, dlaczego bezwarunkowy skok sam w sobie nie wystarcza do napisania sensownej pętli — i że dokładnie to jest powód, dla którego następna sesja wprowadza warunki.