{
  "$schema": "https://zsl-gitea.mpabi.pl/edu-tools/card-layouts/raw/branch/main/schemas/card-source.schema.json",
  "schema": "esc-card-source.v1",
  "card": {
    "id": "mpabi-inf-c-07-pointers-arrays",
    "series": "c",
    "series_title": "C · Freestanding RV32I and K&R",
    "number": "07",
    "count": "13",
    "slug": "pointers-arrays",
    "title": "Wskaźniki i tablice",
    "topic": "Od K&R 5.1--5.4 do struktur i FreeRTOS heap_4",
    "project": "Alokator pamięci",
    "subject": "Informatyka",
    "level": "PP@2024.06.28 · Inf. · ZP · Kl. 1 · Poz. 1",
    "revision_date": "2026-07-19T00:00:00+02:00",
    "status": "Gotowa",
    "version": "v00.01",
    "uuid": "4abec2d9-79e9-0dca-9040-c60669f51ff8",
    "author": "M. Pabiszczak",
    "year": "2026"
  },
  "generated": {
    "tex": "doc/generated/main.tex",
    "html": "web/index.html",
    "html_css": "web/style.css",
    "html_tree_inspector": false,
    "react_app": true
  },
  "render_dictionary": false,
  "debug_checkpoints": {
    "schema": "stem-debug-checkpoints.v1",
    "semantics": "deterministic-replay",
    "recorded_grid_pattern": "debug-snapshots/{snapshot_ref}.json",
    "artifact": {
      "source": "src/tasks/task04_address_arithmetic_alloc.c",
      "source_git_blob": "a8aa25b3e6b68af6dbfd640937b6660b2ed5c5af",
      "elf": ".stem/instances/hazard3-sim-inf-pointers-t4/build/hazard3/task04_address_arithmetic_alloc/prog.elf",
      "hazard3_image": ".stem/instances/hazard3-sim-inf-pointers-t4/build/hazard3/task04_address_arithmetic_alloc/prog.bin",
      "hazard3_image_sha256": "59e042815059da76a8c5e85c1317abaae9decf5e9ea77ef54e6da4175f71a307"
    },
    "targets": {
      "hazard3-sim": {
        "adapter": "hazard3-reset-load-replay.v1",
        "baseline": "restart-backend",
        "clean_ram": true
      },
      "rp2350": {
        "adapter": "rp2350-sram-reset-load-replay.v1",
        "baseline": "reset-load-elf-sram",
        "requires_probe_lease": true,
        "persistent_flash": false
      }
    },
    "items": {
      "task04.reset.call": {"stop": {"symbol": "main", "offset": 16}, "verify": {"expressions": []}},
      "task04.reset.address": {"stop": {"symbol": "allocator_reset", "offset": 28}, "verify": {"expressions": []}},
      "task04.reset.store": {"stop": {"symbol": "allocator_reset", "offset": 32}, "verify": {"expressions": [{"expr": "allocp - allocbuf", "equals": 0}]}},
      "task04.reset.return": {"stop": {"symbol": "main", "offset": 24}, "verify": {"expressions": [{"expr": "allocp - allocbuf", "equals": 0}]}},
      "task04.alloc5.enter": {"stop": {"symbol": "main", "offset": 28}, "verify": {"expressions": [{"expr": "$a0", "equals": 5}, {"expr": "allocp - allocbuf", "equals": 0}]}},
      "task04.alloc5.cursor": {"stop": {"symbol": "alloc_local", "offset": 40, "condition": "$a0 == 5"}, "verify": {"expressions": [{"expr": "$a0", "equals": 5}, {"expr": "allocp - allocbuf", "equals": 0}]}},
      "task04.alloc5.buffer-address": {"stop": {"symbol": "alloc_local", "offset": 56, "condition": "$a0 == 5"}, "verify": {"expressions": [{"expr": "allocp - allocbuf", "equals": 0}]}},
      "task04.alloc5.used": {"stop": {"symbol": "alloc_local", "offset": 64, "condition": "$a0 == 5"}, "verify": {"expressions": [{"expr": "$a0", "equals": 5}, {"expr": "allocp - allocbuf", "equals": 0}]}},
      "task04.alloc5.commit": {"stop": {"symbol": "alloc_local", "offset": 156, "condition": "$a0 == 5"}, "verify": {"expressions": [{"expr": "$a0", "equals": 5}, {"expr": "allocp - allocbuf", "equals": 5}]}},
      "task04.alloc5.return": {"stop": {"symbol": "main", "offset": 40}, "verify": {"expressions": [{"expr": "allocp - allocbuf", "equals": 5}, {"expr": "first - allocbuf", "equals": 0}]}},
      "task04.alloc7.enter": {"stop": {"symbol": "main", "offset": 44}, "verify": {"expressions": [{"expr": "$a0", "equals": 7}, {"expr": "allocp - allocbuf", "equals": 5}]}},
      "task04.alloc7.cursor": {"stop": {"symbol": "alloc_local", "offset": 40, "condition": "$a0 == 7"}, "verify": {"expressions": [{"expr": "$a0", "equals": 7}, {"expr": "allocp - allocbuf", "equals": 5}]}},
      "task04.alloc7.used": {"stop": {"symbol": "alloc_local", "offset": 64, "condition": "$a0 == 7"}, "verify": {"expressions": [{"expr": "$a0", "equals": 7}, {"expr": "allocp - allocbuf", "equals": 5}]}},
      "task04.alloc7.commit": {"stop": {"symbol": "alloc_local", "offset": 156, "condition": "$a0 == 7"}, "verify": {"expressions": [{"expr": "$a0", "equals": 7}, {"expr": "allocp - allocbuf", "equals": 12}]}},
      "task04.alloc7.return": {"stop": {"symbol": "main", "offset": 56}, "verify": {"expressions": [{"expr": "allocp - allocbuf", "equals": 12}, {"expr": "first - allocbuf", "equals": 0}, {"expr": "second - allocbuf", "equals": 5}]}}
    }
  },
  "template": "templates/karta-klasyczna.json",
  "title_block": {
    "category": "KARTA PRACY · INFORMATYKA",
    "prepared_by": "M. Pabiszczak",
    "prepared_on": "2026-07-19T00:00:00+02:00",
    "title": "Wskaźniki i tablice",
    "url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/98dbe323-c624-53d6-9fe2-4ee688c44b38",
    "repository_url": "https://zsl-gitea.mpabi.pl/edu-c/lab-rv32i-c-pointers-arrays",
    "url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84",
    "url_domain": "mpabi.pl",
    "doc_uuid": "98dbe323-c624-53d6-9fe2-4ee688c44b38",
    "revision": "v00.01",
    "issued_on": "2026-07-19T00:00:00+02:00",
    "series": "C-07",
    "document_type": "karta pracy",
    "tool": "card-layouts",
    "show_qr": true,
    "show_repository_qr": true,
    "height_cm": 2.6,
    "repeat_on_every_page": true,
    "replace_front_matter": true
  },
  "reference_registries": [
    {
      "uuid": "03403a8a-e20d-5b17-9004-26d9fe6c3003",
      "source": "../../../tools/debugging-strategies/json/reference_registry.json"
    }
  ],
  "debug_strategies": {
    "run.runtime": {
      "id": "run.runtime",
      "kind": "run",
      "action": "replay",
      "prerequisites": [
        "Hazard3/GDB online",
        "symbole ELF Task04"
      ],
      "layout": [
        "source",
        "assembly",
        "registers",
        "stack",
        "allocbuf"
      ],
      "commands": [
        "info registers a0 a4 a5 sp s0 pc",
        "print allocp - allocbuf",
        "x/16bx allocbuf"
      ],
      "expected_observations": [
        "allocp przechodzi przez offsety 0, 5 i 12",
        "allocbuf i allocp należą do .bss, a sp do bieżącej ramki stosu"
      ],
      "assertions": [
        "wybrany checkpoint odtwarza deterministyczny stan alokatora",
        "PC i linia źródła odpowiadają wybranej strzałce UML"
      ],
      "evidence_fields": [
        "snapshot_ref",
        "source_ref",
        "PC",
        "registers",
        "allocp offset",
        "ELF hash",
        "timestamp"
      ]
    }
  },
  "front_page_break": true,
  "front_page_scope": {
    "title": "Cel karty",
    "content_tex": "Uczeń implementuje i weryfikuje liniowy alokator z areną \\texttt{allocbuf} i kursorem \\texttt{allocp}; przewiduje offsety, odczytuje je w pamięci oraz dowodzi, że \\texttt{allocp} nie jest wskaźnikiem stosu \\texttt{sp}.",
    "scope_title": "Zakres karty",
    "scope_table": {
      "headers": ["K&R", "Numer tasku", "Najważniejsza idea", "Priorytet", "Status", "Version"],
      "rows": [
        {"chapter": "5.1", "task": "Task01", "idea_tex": "adres, \\texttt{\\&}, \\texttt{*}", "priority": "obowiązkowe", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.2", "task": "Task02", "idea_tex": "wskaźnik jako argument funkcji", "priority": "obowiązkowe", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.3", "task": "Task03", "idea_tex": "relacja tablicy i wskaźnika", "priority": "obowiązkowe", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.4", "task": "Task04", "idea_tex": "arytmetyka adresów, alokator", "priority": "obowiązkowe", "status": "in-progress", "version": "v00.01", "key": true},
        {"chapter": "5.5", "task": "Task05", "idea_tex": "napisy jako \\texttt{char *}, NUL", "priority": "ważne", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.6", "task": "Task06", "idea_tex": "tablica wskaźników", "priority": "ważne", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.7", "task": "Task07", "idea_tex": "tablice wielowymiarowe", "priority": "ważne", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.8", "task": "Task08", "idea_tex": "inicjalizacja tablic wskaźników", "priority": "wspierające", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.9", "task": "Task09", "idea_tex": "\\texttt{int a[R][C]} kontra \\texttt{int *a[]}", "priority": "bardzo ważne", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.10", "task": "Task10", "idea_tex": "\\texttt{argv} i argumenty programu", "priority": "wspierające", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.11", "task": "Task11", "idea_tex": "wskaźniki do funkcji", "priority": "ważne przed callbackami", "status": "not-started", "version": "v00.01"},
        {"chapter": "5.12", "task": "Task12", "idea_tex": "czytanie złożonych deklaracji", "priority": "uzupełniające", "status": "not-started", "version": "v00.01"}
      ]
    }
  },
  "side_margin_tree_layout": {
    "columns": [
      {
        "id": "zawodowe",
        "label": "TECH",
        "side": "left",
        "tree": "WE -> EK -> KW",
        "description": "Umiejętności techniczne: C, RISC-V, debugowanie i pamięć."
      },
      {
        "id": "ogolne",
        "label": "OG",
        "side": "right",
        "tree": "WE -> EN -> KW",
        "description": "Umiejętności ogólne: przewidywanie, pomiar i wnioskowanie."
      }
    ]
  },
  "learning_effects": {
    "PTR.EN01": {
      "bloom_level": "Analiza",
      "label": "Przewidywanie",
      "text": "Uczeń przewiduje wartości, adresy i offsety przed uruchomieniem programu, a następnie porównuje je z pomiarem.",
      "assessment_criteria": ["PTR.KW01", "PTR.KW02"]
    },
    "PTR.EN02": {
      "bloom_level": "Ewaluacja",
      "label": "Dowód",
      "text": "Uczeń zapisuje odtwarzalny dowód i odróżnia wynik od jego interpretacji.",
      "assessment_criteria": ["PTR.KW04"]
    },
    "PTR.EK01": {
      "bloom_level": "Zastosowanie",
      "label": "Wskaźniki i tablice",
      "text": "Uczeń stosuje adresowanie, dereferencję, argumenty wskaźnikowe i arytmetykę wskaźników w granicach tablicy.",
      "assessment_criteria": ["PTR.KW01"]
    },
    "PTR.EK02": {
      "bloom_level": "Analiza",
      "label": "Debugowanie RV32",
      "text": "Uczeń odnajduje argumenty, zmienne lokalne, ramkę stosu, rejestry i bajty pamięci w debuggerze Hazard3.",
      "assessment_criteria": ["PTR.KW03", "PTR.KW04"]
    },
    "PTR.EK03": {
      "bloom_level": "Analiza",
      "label": "Alokator",
      "text": "Uczeń śledzi inwarianty liniowego alokatora i wskazuje elementy potrzebne do przejścia do struktur oraz heap_4.",
      "assessment_criteria": ["PTR.KW02", "PTR.KW03"]
    }
  },
  "assessment_criteria": {
    "PTR.KW01": {
      "text": "Task01--Task03 kończą się kodem 0, przewidywania są zapisane, a wartości i offsety zgadzają się z pomiarem.",
      "learning_effects": ["PTR.EN01", "PTR.EK01"]
    },
    "PTR.KW02": {
      "text": "Task04 daje offsety 0, 5, 12 i 5; odrzuca brak miejsca i żądanie zera; suma znaków wynosi 372.",
      "learning_effects": ["PTR.EN01", "PTR.EK03"]
    },
    "PTR.KW03": {
      "text": "Uczeń pokazuje allocbuf w pamięci, odróżnia allocp od sp oraz potwierdza wyrównanie sp do 16 bajtów.",
      "learning_effects": ["PTR.EK02", "PTR.EK03"]
    },
    "PTR.KW04": {
      "text": "Dowód zawiera wynik AMD64, Hazard3 i -- dla Task04 na zajęciach -- RP2350, wraz z komendą, bramką i obserwacją debuggera.",
      "learning_effects": ["PTR.EN02", "PTR.EK02"]
    }
  },
  "educational_requirements": {
    "PTR.WE01": {
      "text": "Poprawne rozumowanie o adresach, wartościach i przesunięciach wskaźników.",
      "label": "Semantyka wskaźników",
      "learning_effects": ["PTR.EN01", "PTR.EK01"],
      "learning_tree": {
        "schema": "we-learning-tree.v1",
        "policy": "Najpierw przewidywanie, potem pomiar, na końcu wniosek.",
        "ogolne": [
          {
            "effect_ref": "PTR.EN01",
            "display": "EN PTR 01",
            "source": "LOCAL",
            "official": "PTR",
            "local": "01",
            "kind": "EN",
            "text": "Przewidywanie i porównanie wyniku.",
            "tree_id": "PTR.WE01.OG.EN01",
            "kw": [
              {
                "criterion_ref": "PTR.KW01",
                "display": "KW PTR 01",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "01",
                "text": "Task01--Task03: zgodne wartości i offsety."
              }
            ]
          }
        ],
        "zawodowe": [
          {
            "effect_ref": "PTR.EK01",
            "display": "EK PTR 01",
            "source": "LOCAL",
            "official": "PTR",
            "local": "01",
            "kind": "EK",
            "text": "Wskaźniki, tablice i argumenty funkcji.",
            "tree_id": "PTR.WE01.TECH.EK01",
            "kw": [
              {
                "criterion_ref": "PTR.KW01",
                "display": "KW PTR 01",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "01",
                "text": "Task01--Task03: zgodne wartości i offsety."
              }
            ]
          }
        ]
      }
    },
    "PTR.WE02": {
      "text": "Odtwarzalna obserwacja wykonania programu na RV32 i RP2350.",
      "label": "Pomiar debuggera",
      "learning_effects": ["PTR.EN02", "PTR.EK02"],
      "learning_tree": {
        "schema": "we-learning-tree.v1",
        "policy": "Każdy wniosek wskazuje komendę i obserwowany stan.",
        "ogolne": [
          {
            "effect_ref": "PTR.EN02",
            "display": "EN PTR 02",
            "source": "LOCAL",
            "official": "PTR",
            "local": "02",
            "kind": "EN",
            "text": "Dokumentowanie dowodu pomiarowego.",
            "tree_id": "PTR.WE02.OG.EN02",
            "kw": [
              {
                "criterion_ref": "PTR.KW04",
                "display": "KW PTR 04",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "04",
                "text": "Dowód z platform, bramek i debuggera."
              }
            ]
          }
        ],
        "zawodowe": [
          {
            "effect_ref": "PTR.EK02",
            "display": "EK PTR 02",
            "source": "LOCAL",
            "official": "PTR",
            "local": "02",
            "kind": "EK",
            "text": "Rejestry, stos, ASM i pamięć RV32.",
            "tree_id": "PTR.WE02.TECH.EK02",
            "kw": [
              {
                "criterion_ref": "PTR.KW03",
                "display": "KW PTR 03",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "03",
                "text": "allocbuf, allocp, sp i wyrównanie stosu."
              },
              {
                "criterion_ref": "PTR.KW04",
                "display": "KW PTR 04",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "04",
                "text": "Dowód z platform, bramek i debuggera."
              }
            ]
          }
        ]
      }
    },
    "PTR.WE03": {
      "text": "Wyjaśnienie działania alokatora liniowego i jego ograniczeń przed strukturami i heap_4.",
      "label": "Od K&R 5.4 do heap_4",
      "learning_effects": ["PTR.EN01", "PTR.EK03"],
      "learning_tree": {
        "schema": "we-learning-tree.v1",
        "policy": "Stan kursora i granice areny są sprawdzane po każdej operacji.",
        "ogolne": [
          {
            "effect_ref": "PTR.EN01",
            "display": "EN PTR 03",
            "source": "LOCAL",
            "official": "PTR",
            "local": "03",
            "kind": "EN",
            "text": "Przewidywanie osi zmian alokatora.",
            "tree_id": "PTR.WE03.OG.EN01",
            "kw": [
              {
                "criterion_ref": "PTR.KW02",
                "display": "KW PTR 02",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "02",
                "text": "Offsety i przypadki brzegowe Task04."
              }
            ]
          }
        ],
        "zawodowe": [
          {
            "effect_ref": "PTR.EK03",
            "display": "EK PTR 03",
            "source": "LOCAL",
            "official": "PTR",
            "local": "03",
            "kind": "EK",
            "text": "Inwarianty alokatora i przejście do heap_4.",
            "tree_id": "PTR.WE03.TECH.EK03",
            "kw": [
              {
                "criterion_ref": "PTR.KW02",
                "display": "KW PTR 02",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "02",
                "text": "Offsety i przypadki brzegowe Task04."
              },
              {
                "criterion_ref": "PTR.KW03",
                "display": "KW PTR 03",
                "source": "LOCAL",
                "kind": "KW",
                "official": "PTR",
                "local": "03",
                "text": "allocbuf, allocp, sp i wyrównanie stosu."
              }
            ]
          }
        ]
      }
    }
  },
  "sections": [
    {
      "title": "K&R 5.4 -- Task04: Linear Memory Allocator",
      "order": 10,
      "content_kind": "prose",
      "content_tex": "\\textbf{Jak działa alokator liniowy}\n\n\\begin{itemize}[leftmargin=*]\n\\item \\textbf{Model pamięci:}\n  \\begin{itemize}[label=--]\n  \\item \\texttt{allocbuf[ALLOCSIZE]} jest statyczną areną 64 B.\n  \\item \\texttt{allocp} wskazuje pierwszy wolny bajt; offset to \\texttt{allocp - allocbuf}.\n  \\end{itemize}\n\\item \\textbf{Operacje:}\n  \\begin{itemize}[label=--]\n  \\item \\texttt{allocator\\_reset()} ustawia \\texttt{allocp = allocbuf}.\n  \\item \\texttt{alloc\\_local(n)} zwraca bieżący kursor i przesuwa go o \\texttt{n}, jeżeli żądanie jest dodatnie i mieści się w arenie.\n  \\end{itemize}\n\\item \\textbf{Stan po sekwencji reset → alloc(5) → alloc(7):}\n  \\begin{itemize}[label=--]\n  \\item \\texttt{first = allocbuf + 0},\n  \\item \\texttt{second = allocbuf + 5},\n  \\item \\texttt{allocp = allocbuf + 12}.\n  \\end{itemize}\n\\item \\textbf{Ograniczenie:} pojedynczych bloków nie zwalniamy; pamięć odzyskuje \\texttt{allocator\\_reset()} dla całej areny. Dowolne \\texttt{free} pojawi się później przy strukturach i \\texttt{heap\\_4}.\n\\end{itemize}",
      "asset_page_mode": "one-per-page",
      "render_step_map": false,
      "assets": [
        {
          "path": "assets/task04-allocator-arena-model.png",
          "caption": "SCHEMAT — arena allocbuf ma 64 B; po przydziałach 5 B i 7 B kursor allocp wskazuje offset 12. Odczytaj granice obu obszarów i pierwszy wolny bajt.",
          "label": "fig:task04-arena-model",
          "alt": "Schemat areny allocbuf 64 B z przydziałami 5 B i 7 B oraz kursorem allocp na offsecie 12.",
          "kind": "diagram",
          "width": 0.72
        },
        {
          "path": "assets/task04-memory-flow.png",
          "html_path": "assets/task04-memory-flow.svg",
          "source_path": "assets/task04-memory-flow.puml",
          "caption": "UML — sekwencja allocator_reset() → alloc_local(5) → alloc_local(7), ramki aktywacji main()/alloc_local(n) oraz statyczne obiekty allocbuf/allocp.",
          "label": "fig:task04-memory-flow",
          "alt": "Diagram sekwencji UML Task04: main wywołuje allocator_reset(), a następnie dwukrotnie funkcję alloc_local(), która odczytuje allocbuf i aktualizuje allocp.",
          "kind": "diagram",
          "width": 1.0,
          "page_grid": {
            "columns": 1,
            "rows": 1,
            "page_width": 938,
            "page_height": 1184,
            "overview": true
          },
          "interactive": {
            "kind": "uml-sequence",
            "storage_key": "task04-memory-flow",
            "title": "Alokator liniowy — sekwencja wywołań",
            "task": {
              "id": "task04",
              "label": "Task04 · Linear Memory Allocator"
            },
            "block": {
              "id": "allocator-flow",
              "label": "Sekwencja wykonania",
              "description": "Diagram przedstawia kolejno allocator_reset(), alloc_local(5) i alloc_local(7). Wybór numerowanej strzałki synchronizuje Nvim view z odpowiadającym jej stanem wykonania."
            },
            "snapshot_context": {
              "target": "hazard3-sim · RV32I",
              "captured_at": "2026-07-16T14:24:29+02:00",
              "captured_with": "gdb-multiarch + Hazard3 tb_gdb; świeży przebieg, zatrzymania po zatwierdzeniu instrukcji",
              "elf": "/workspace/.stem/instances/hazard3-sim-inf-pointers-t4/build/hazard3/task04_address_arithmetic_alloc/prog.elf",
              "stack_top": "0x80100000"
            },
            "terminal_view": {
              "path": "assets/task04-first-allocation-annotated.png",
              "title": "Termdebug — cztery połączone dowody",
              "alt": "Adnotowany widok Termdebug: wywołanie alloc_local w C, li i jal w listingu, argument pięć w rejestrze a0 oraz bajty allocbuf.",
              "caption": "Numery 1–4 łączą linię C, instrukcje RV32, ABI i pamięć. Dokładny stan wybranego kroku jest odtwarzany w Nvim view."
            },
            "phases": [
              {
                "id": "reset",
                "label": "RESET",
                "description": "Ustanowienie inwariantu allocp = allocbuf i offsetu 0.",
                "svg_label": "RESET",
                "steps": [
                  {
                    "id": "reset-call",
                    "number": 1,
                    "label": "allocator_reset()",
                    "svg_label": "01",
                    "progress_id": "UML04-S01",
                    "description": "main ma już własną ramkę i stoi przed wywołaniem resetu; allocp jest jeszcze zerem z inicjalizacji .bss.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:62",
                    "snapshot_ref": "task04.reset.call",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.main.reset-call", "align": "center"},
                      "annotations": [
                        {"id": "ann.reset.call", "anchor_ref": "src.task04.main.reset-call", "label": "1 · reset", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "allocator_reset();", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000124c",
                      "code": {
                        "source": "allocator_reset();",
                        "source_ref": "task04_address_arithmetic_alloc.c:62",
                        "assembly": "0x8000124c  auipc ra,0x0\n0x80001250  jalr  ra,-284(ra)  # allocator_reset",
                        "note": "PC wskazuje pierwszą instrukcję pary wywołującej; skok wykona się dopiero w następnym kroku procesora."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffd0"}, {"name": "s0/fp", "value": "0x800ffff0"}, {"name": "ra", "value": "0x80001030"}, {"name": "a0", "value": "0x800014b0", "note": "wartość zastana; allocator_reset nie ma argumentów"}, {"name": "a4", "value": "0x00000000"}, {"name": "a5", "value": "0x00000000"}],
                      "frame": {"name": "main", "sp": "0x800fffd0", "fp": "0x800ffff0", "size_bytes": 32, "fields": [{"name": "first", "address": "0x800fffdc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "second", "address": "0x800fffd8", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "out_of_memory", "address": "0x800fffd4", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "zero_request", "address": "0x800fffd0", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x00000000", "note": "jeszcze nie jest poprawnym kursorem"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x00000000", "offset": null, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "To stan wywołującego. Ramka main zajmuje 32 B; cztery sloty lokalne istnieją fizycznie, ale ich wartości C nie zostały jeszcze ustalone."
                    }
                  },
                  {
                    "id": "reset-read-buffer-address",
                    "number": 2,
                    "label": "odczytaj adres &allocbuf[0]",
                    "svg_label": "02",
                    "progress_id": "UML04-S02",
                    "description": "Po prologu allocator_reset rejestr a4 zawiera świeżo wyliczony adres początku areny.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:20",
                    "snapshot_ref": "task04.reset.address",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.reset.assign", "align": "center"},
                      "annotations": [
                        {"id": "ann.reset.address", "anchor_ref": "src.task04.reset.assign", "label": "adres areny", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "allocp = allocbuf;", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000114c",
                      "code": {
                        "source": "allocp = allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:20",
                        "assembly": "0x80001144  lui   a4,0x80001\n0x80001148  addi  a4,a4,1104  # allocbuf\n0x8000114c  sw    a4,1168(a5) # allocp",
                        "note": "Adres początku areny jest już w a4; instrukcja sw wskazywana przez PC nie została jeszcze zatwierdzona."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffc0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001254"}, {"name": "a0", "value": "0x800014b0", "note": "reset nie używa a0"}, {"name": "a4", "value": "0x80001450", "note": "&allocbuf[0]"}, {"name": "a5", "value": "0x80001000", "note": "baza adresowania symbolu allocp"}],
                      "frame": {"name": "allocator_reset", "sp": "0x800fffc0", "fp": "0x800fffd0", "size_bytes": 16, "fields": [{"name": "saved s0", "address": "0x800fffc8", "value": "0x800ffff0", "state": "saved"}, {"name": "saved ra", "address": "0x800fffcc", "value": "0x80001254", "state": "saved"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x00000000"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x00000000", "offset": null, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Adres 0x80001450 został wyliczony w a4, ale zapis do zmiennej allocp jeszcze się nie wykonał."
                    }
                  },
                  {
                    "id": "reset-buffer-address-result",
                    "number": 3,
                    "label": "początek areny",
                    "svg_label": "03",
                    "progress_id": "UML04-S03",
                    "description": "Ta przerywana strzałka interpretuje ten sam stabilny stan: wynikiem odczytu adresu jest 0x80001450 w a4.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:20",
                    "snapshot_ref": "task04.reset.address",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.reset.assign", "align": "center"},
                      "annotations": [
                        {"id": "ann.reset.a4", "anchor_ref": "reg.a4", "label": "a4 = &allocbuf[0]", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a4 0x80001450", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000114c",
                      "code": {
                        "source": "allocp = allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:20",
                        "assembly": "a4 = 0x80001450  # &allocbuf[0]\npc = 0x8000114c  # przed sw do allocp",
                        "note": "Strzałka zwrotna UML interpretuje wartość a4; nie odpowiada osobnej instrukcji."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffc0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001254"}, {"name": "a0", "value": "0x800014b0"}, {"name": "a4", "value": "0x80001450", "note": "początek areny"}, {"name": "a5", "value": "0x80001000"}],
                      "frame": {"name": "allocator_reset", "sp": "0x800fffc0", "fp": "0x800fffd0", "size_bytes": 16, "fields": [{"name": "saved s0", "address": "0x800fffc8", "value": "0x800ffff0", "state": "saved"}, {"name": "saved ra", "address": "0x800fffcc", "value": "0x80001254", "state": "saved"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x00000000"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x00000000", "offset": null, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Krok 03 nie wykonuje dodatkowej instrukcji; pokazuje wartość adresu zwróconą logicznie przez uczestnika allocbuf."
                    }
                  },
                  {
                    "id": "reset-store-cursor",
                    "number": 4,
                    "label": "zapisz allocp = &allocbuf[0]",
                    "svg_label": "04",
                    "progress_id": "UML04-S04",
                    "description": "Instrukcja sw zakończyła zapis do symbolu allocp; od tej chwili kursor ma poprawny offset 0.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:20",
                    "snapshot_ref": "task04.reset.store",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.reset.assign", "align": "center"},
                      "annotations": [
                        {"id": "ann.reset.store", "anchor_ref": "asm.task04.reset.store-allocp", "label": "allocp ← allocbuf", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a4,1168(a5)", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001150",
                      "code": {
                        "source": "allocp = allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:20",
                        "assembly": "0x8000114c  sw    a4,1168(a5) # allocp\n0x80001150  addi  zero,zero,0 # PC po zapisie",
                        "note": "PC przesunął się za sw, więc pamięć pod symbolem allocp zawiera już 0x80001450."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffc0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001254"}, {"name": "a0", "value": "0x800014b0"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x80001000"}],
                      "frame": {"name": "allocator_reset", "sp": "0x800fffc0", "fp": "0x800fffd0", "size_bytes": 16, "fields": [{"name": "saved s0", "address": "0x800fffc8", "value": "0x800ffff0", "state": "saved"}, {"name": "saved ra", "address": "0x800fffcc", "value": "0x80001254", "state": "saved"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Pomiar pamięci pod 0x80001490 daje 0x80001450, więc allocp - allocbuf = 0."
                    }
                  },
                  {
                    "id": "reset-return",
                    "number": 5,
                    "label": "return void",
                    "svg_label": "05",
                    "progress_id": "UML04-S05",
                    "description": "Ramka allocator_reset zniknęła, sp wrócił do ramki main, a statyczny allocp zachował offset 0.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:21",
                    "snapshot_ref": "task04.reset.return",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.main.alloc5-call", "align": "center"},
                      "annotations": [
                        {"id": "ann.reset.return", "anchor_ref": "asm.task04.main.alloc5-arg", "label": "powrót do main", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a0,5", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001254",
                      "code": {
                        "source": "}",
                        "source_ref": "task04_address_arithmetic_alloc.c:21",
                        "assembly": "0x80001154  lw    ra,12(sp)\n0x80001158  lw    s0,8(sp)\n0x8000115c  addi  sp,sp,16\n0x80001160  ret\n0x80001254  li    a0,5",
                        "note": "Po ret aktywna jest ponownie ramka main; następna instrukcja przygotuje argument pierwszego przydziału."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffd0"}, {"name": "s0/fp", "value": "0x800ffff0"}, {"name": "ra", "value": "0x80001254"}, {"name": "a0", "value": "0x800014b0", "note": "void: a0 nie jest wynikiem resetu"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x80001000"}],
                      "frame": {"name": "main", "sp": "0x800fffd0", "fp": "0x800ffff0", "size_bytes": 32, "fields": [{"name": "first", "address": "0x800fffdc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "second", "address": "0x800fffd8", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "out_of_memory", "address": "0x800fffd4", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "zero_request", "address": "0x800fffd0", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Powrót usuwa tylko ramkę aktywacji. allocbuf i allocp w .bss pozostają dostępne przez cały czas działania programu."
                    }
                  }
                ]
              },
              {
                "id": "alloc-5",
                "label": "ALLOC(5)",
                "description": "Pierwsza ramka alloc_local: n=5, used=0, result=allocbuf i końcowy offset 5.",
                "svg_label": "ALLOC(5)",
                "steps": [
                  {
                    "id": "alloc5-call",
                    "number": 6,
                    "label": "first = alloc_local(5)",
                    "svg_label": "06",
                    "progress_id": "UML04-S06",
                    "description": "Po instrukcji li argument 5 jest widoczny w a0, a PC stoi przed jalr; ramka main nadal zawiera cztery lokalne wskaźniki.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:64",
                    "snapshot_ref": "task04.alloc5.enter",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.main.alloc5-call", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.source", "anchor_ref": "src.task04.main.alloc5-call", "label": "1 · wywołanie", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "first = alloc_local(5);", "padding_cells": 1}},
                        {"id": "ann.alloc5.asm", "anchor_ref": "asm.task04.main.alloc5-arg", "label": "2 · li / jal", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "main+24 li a0,5", "padding_cells": 1}},
                        {"id": "ann.alloc5.a0", "anchor_ref": "reg.a0", "label": "3 · a0 = 5", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a0 0x00000005", "padding_cells": 1}},
                        {"id": "ann.alloc5.arena", "anchor_ref": "memory.allocbuf", "label": "4 · arena", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "allocbuf[0]", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001258",
                      "code": {
                        "source": "first = alloc_local(5);",
                        "source_ref": "task04_address_arithmetic_alloc.c:64",
                        "assembly": "0x80001254  li    a0,5\n0x80001258  auipc ra,0x0  # PC\n0x8000125c  jalr  ra,-244(ra) # alloc_local",
                        "note": "To stan tuż przed wywołaniem: li ustawiło a0=5, ale prolog alloc_local jeszcze się nie wykonał."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffd0"}, {"name": "s0/fp", "value": "0x800ffff0"}, {"name": "ra", "value": "0x80001254"}, {"name": "a0", "value": "0x00000005", "note": "pierwszy argument RV32 ABI"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x80001000"}],
                      "frame": {"name": "main", "sp": "0x800fffd0", "fp": "0x800ffff0", "size_bytes": 32, "fields": [{"name": "zero_request", "address": "0x800fffd0", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "out_of_memory", "address": "0x800fffd4", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "second", "address": "0x800fffd8", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "first", "address": "0x800fffdc", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Przed jalr aktywna jest wyłącznie ramka main: sp=0x800fffd0, a0=5 i allocp nadal wskazuje allocbuf+0."
                    }
                  },
                  {
                    "id": "alloc5-read-cursor",
                    "number": 7,
                    "label": "odczytaj bieżący kursor",
                    "svg_label": "07",
                    "progress_id": "UML04-S07",
                    "description": "Stabilny stan po odczycie pokazuje a5=0x80001450, zgodne z wartością zapisaną pod symbolem allocp.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:28",
                    "snapshot_ref": "task04.alloc5.cursor",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.null-check", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.cursor-read", "anchor_ref": "asm.task04.alloc.load-allocp", "label": "allocp", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a5,1168(a5)", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000118c",
                      "code": {
                        "source": "if (allocp == NULL) {\n    return NULL;\n}",
                        "source_ref": "task04_address_arithmetic_alloc.c:28-30",
                        "assembly": "0x80001178  lui   a5,0x80001\n0x8000117c  lw    a5,1168(a5) # allocp\n0x80001180  bne   a5,zero,0x8000118c\n0x8000118c  lui   a5,0x80001",
                        "note": "Użyto stabilnego stanu po gałęzi: a5 zawiera architektonicznie zatwierdzony odczyt allocp."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000005"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x80001450", "note": "odczytana wartość allocp"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "5", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Hazard3 tb_gdb pokazuje wartość ładowania dopiero w stabilnym stanie po gałęzi; dlatego snapshot jest przypięty do PC 0x8000118c, a nie do chwilowego stanu potoku na 0x80001180."
                    }
                  },
                  {
                    "id": "alloc5-cursor-result",
                    "number": 8,
                    "label": "&allocbuf[0]",
                    "svg_label": "08",
                    "progress_id": "UML04-S08",
                    "description": "Wynik logicznego odczytu kursora to początek areny; a5 i pamięć allocp zawierają ten sam adres.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:33",
                    "snapshot_ref": "task04.alloc5.cursor",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.used", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.cursor-value", "anchor_ref": "reg.a5", "label": "&allocbuf[0]", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a5 0x80001450", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000118c",
                      "code": {
                        "source": "used = allocp - allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:33",
                        "assembly": "a5 = 0x80001450  # wartość allocp\npc = 0x8000118c   # stabilny stan po lw/bne",
                        "note": "Strzałka zwrotna UML nazywa wartość odczytaną w kroku 07; nie wykonuje dodatkowej instrukcji."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000005"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x80001450"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "5", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Strzałka zwrotna nie dodaje instrukcji; wiąże wartość a5=0x80001450 z pojęciem pierwszego wolnego bajtu."
                    }
                  },
                  {
                    "id": "alloc5-read-buffer-address",
                    "number": 9,
                    "label": "odczytaj adres początku areny",
                    "svg_label": "09",
                    "progress_id": "UML04-S09",
                    "description": "Po zbudowaniu adresu allocbuf oba rejestry robocze a4 i a5 mają 0x80001450.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:33",
                    "snapshot_ref": "task04.alloc5.buffer-address",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.used", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.buffer-address", "anchor_ref": "asm.task04.alloc.address-allocbuf", "label": "&allocbuf[0]", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a5,a5,1104", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000119c",
                      "code": {
                        "source": "used = allocp - allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:33",
                        "assembly": "0x8000118c  lui   a5,0x80001\n0x80001190  lw    a4,1168(a5) # allocp\n0x80001194  lui   a5,0x80001\n0x80001198  addi  a5,a5,1104  # allocbuf\n0x8000119c  sub   a5,a4,a5",
                        "note": "Tuż przed sub oba operandy mają adres 0x80001450."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000005"}, {"name": "a4", "value": "0x80001450", "note": "allocp"}, {"name": "a5", "value": "0x80001450", "note": "allocbuf"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "5", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Następna instrukcja odejmie a5 od a4. Równe adresy przewidują wynik used=0."
                    }
                  },
                  {
                    "id": "alloc5-buffer-address-result",
                    "number": 10,
                    "label": "&allocbuf[0]",
                    "svg_label": "10",
                    "progress_id": "UML04-S10",
                    "description": "Początek areny używany w odejmowaniu adresów ma adres 0x80001450.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:33",
                    "snapshot_ref": "task04.alloc5.buffer-address",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.used", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.buffer-result", "anchor_ref": "reg.a5", "label": "początek areny", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a5 0x80001450", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000119c",
                      "code": {
                        "source": "used = allocp - allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:33",
                        "assembly": "a4 = 0x80001450  # allocp\na5 = 0x80001450  # &allocbuf[0]\npc = 0x8000119c   # przed sub",
                        "note": "Strzałka zwrotna UML eksponuje drugi operand odejmowania i dzieli stan z krokiem 09."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000005"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x80001450"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "5", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Krok 10 jest odpowiedzią logiczną allocbuf i dzieli snapshot z krokiem 09."
                    }
                  },
                  {
                    "id": "alloc5-used",
                    "number": 11,
                    "label": "used = allocp - allocbuf = 0",
                    "svg_label": "11",
                    "progress_id": "UML04-S11",
                    "description": "Odejmowanie adresów dało 0, a wynik został zapisany do pola used w ramce.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:33",
                    "snapshot_ref": "task04.alloc5.used",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.used", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.used", "anchor_ref": "src.task04.alloc.used", "label": "used = 0", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "used = allocp - allocbuf;", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x800011a4",
                      "code": {
                        "source": "used = allocp - allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:33",
                        "assembly": "0x8000119c  sub   a5,a4,a5\n0x800011a0  sw    a5,-20(s0) # used\n0x800011a4  lw    a5,-36(s0) # n",
                        "note": "PC znajduje się za zapisem, dlatego slot used i a5 z wyniku odejmowania potwierdzają offset 0."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000005"}, {"name": "a4", "value": "0x80001450"}, {"name": "a5", "value": "0x00000000", "note": "wynik odejmowania"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "5", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "0", "state": "initialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001450"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001450", "offset": 0, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Pole used pod 0x800fffbc zawiera już 0. Pole result nadal nie ma wartości języka C."
                    }
                  },
                  {
                    "id": "alloc5-commit",
                    "number": 12,
                    "label": "result = allocp; allocp += 5",
                    "svg_label": "12",
                    "progress_id": "UML04-S12",
                    "description": "result zachował stary kursor 0x80001450, a zapis do allocp przesunął kursor na 0x80001455.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:39-40",
                    "snapshot_ref": "task04.alloc5.commit",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.commit", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.commit", "anchor_ref": "src.task04.alloc.commit", "label": "offset 0 → 5", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "allocp += n;", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001200",
                      "code": {
                        "source": "result = allocp;\nallocp += n;",
                        "source_ref": "task04_address_arithmetic_alloc.c:39-40",
                        "assembly": "0x800011e4  sw    a5,-24(s0)   # result\n0x800011ec  lw    a4,1168(a5) # allocp\n0x800011f0  lw    a5,-36(s0)  # n\n0x800011f4  add   a4,a4,a5\n0x800011fc  sw    a4,1168(a5) # allocp\n0x80001200  lw    a5,-24(s0)  # result",
                        "note": "Stary kursor trafia do result, natomiast suma allocp+n zostaje zapisana jako nowy kursor."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000005"}, {"name": "a4", "value": "0x80001455", "note": "nowy kursor"}, {"name": "a5", "value": "0x80001000"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "5", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "0", "state": "initialised"}, {"name": "result", "address": "0x800fffb8", "value": "0x80001450", "state": "initialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001455"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001455", "offset": 5, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Przydział rezerwuje pięć bajtów bez zapisywania ich treści. Dlatego offset zmienia się na 5, lecz podgląd areny nadal zawiera zera."
                    }
                  },
                  {
                    "id": "alloc5-return",
                    "number": 13,
                    "label": "return first = &allocbuf[0]",
                    "svg_label": "13",
                    "progress_id": "UML04-S13",
                    "description": "Po powrocie a0 i lokalne first mają 0x80001450; ramka alloc_local zniknęła, a allocp pozostał na offsecie 5.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:41",
                    "snapshot_ref": "task04.alloc5.return",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.main.alloc5-call", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc5.return", "anchor_ref": "src.task04.main.alloc5-call", "label": "first = &allocbuf[0]", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "first = alloc_local(5);", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001264",
                      "code": {
                        "source": "return result;\n/* main: */ first = alloc_local(5);",
                        "source_ref": "task04_address_arithmetic_alloc.c:41,64",
                        "assembly": "0x80001200  lw    a5,-24(s0) # result\n0x80001204  mv    a0,a5\n0x80001214  ret\n0x80001260  sw    a0,-20(s0) # first\n0x80001264  li    a0,7",
                        "note": "Wartość wróciła w a0 i została zapisana do lokalnej first w ramce main."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffd0"}, {"name": "s0/fp", "value": "0x800ffff0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x80001450", "note": "wartość zwrotna"}, {"name": "a4", "value": "0x80001455"}, {"name": "a5", "value": "0x80001450"}],
                      "frame": {"name": "main", "sp": "0x800fffd0", "fp": "0x800ffff0", "size_bytes": 32, "fields": [{"name": "first", "address": "0x800fffdc", "value": "0x80001450", "state": "initialised"}, {"name": "second", "address": "0x800fffd8", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "out_of_memory", "address": "0x800fffd4", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "zero_request", "address": "0x800fffd0", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001455"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001455", "offset": 5, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "first jest pierwszą zainicjalizowaną lokalną main. second oraz dwa późniejsze wyniki nadal są nieustalone."
                    }
                  }
                ]
              },
              {
                "id": "alloc-7",
                "label": "ALLOC(7)",
                "description": "Druga ramka alloc_local: n=7, used=5, result=allocbuf+5 i końcowy offset 12.",
                "svg_label": "ALLOC(7)",
                "steps": [
                  {
                    "id": "alloc7-call",
                    "number": 14,
                    "label": "second = alloc_local(7)",
                    "svg_label": "14",
                    "progress_id": "UML04-S14",
                    "description": "Po instrukcji li argument 7 jest widoczny w a0, first jest już zapisany w ramce main, a PC stoi przed drugim jalr.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:65",
                    "snapshot_ref": "task04.alloc7.enter",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.main.alloc7-call", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc7.source", "anchor_ref": "src.task04.main.alloc7-call", "label": "alloc(7)", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "second = alloc_local(7);", "padding_cells": 1}},
                        {"id": "ann.alloc7.a0", "anchor_ref": "reg.a0", "label": "a0 = 7", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a0 0x00000007", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001268",
                      "code": {
                        "source": "second = alloc_local(7);",
                        "source_ref": "task04_address_arithmetic_alloc.c:65",
                        "assembly": "0x80001264  li    a0,7\n0x80001268  auipc ra,0x0  # PC\n0x8000126c  jalr  ra,-260(ra) # alloc_local",
                        "note": "To stan przed drugim wywołaniem: pierwsze zwróciło allocbuf+0 i przesunęło kursor do offsetu 5."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffd0"}, {"name": "s0/fp", "value": "0x800ffff0"}, {"name": "ra", "value": "0x80001260"}, {"name": "a0", "value": "0x00000007"}, {"name": "a4", "value": "0x80001455"}, {"name": "a5", "value": "0x80001450"}],
                      "frame": {"name": "main", "sp": "0x800fffd0", "fp": "0x800ffff0", "size_bytes": 32, "fields": [{"name": "zero_request", "address": "0x800fffd0", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "out_of_memory", "address": "0x800fffd4", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "second", "address": "0x800fffd8", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "first", "address": "0x800fffdc", "value": "0x80001450", "state": "initialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001455"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001455", "offset": 5, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Ramka main pokazuje już first=0x80001450, lecz second pozostaje niezainicjalizowany aż do powrotu drugiego alloc_local."
                    }
                  },
                  {
                    "id": "alloc7-read-cursor",
                    "number": 15,
                    "label": "odczytaj bieżący kursor",
                    "svg_label": "15",
                    "progress_id": "UML04-S15",
                    "description": "Stabilny odczyt allocp daje 0x80001455, czyli pięć bajtów za początkiem areny.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:28",
                    "snapshot_ref": "task04.alloc7.cursor",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.null-check", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc7.cursor-read", "anchor_ref": "asm.task04.alloc.load-allocp", "label": "allocbuf + 5", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a5,1168(a5)", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000118c",
                      "code": {
                        "source": "if (allocp == NULL) {\n    return NULL;\n}",
                        "source_ref": "task04_address_arithmetic_alloc.c:28-30",
                        "assembly": "0x80001178  lui   a5,0x80001\n0x8000117c  lw    a5,1168(a5) # allocp\n0x80001180  bne   a5,zero,0x8000118c\n0x8000118c  lui   a5,0x80001",
                        "note": "Stabilny stan po gałęzi zawiera bieżący kursor 0x80001455, czyli allocbuf+5."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001270"}, {"name": "a0", "value": "0x00000007"}, {"name": "a4", "value": "0x80001455"}, {"name": "a5", "value": "0x80001455", "note": "odczytana wartość allocp"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "7", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x80001450", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001455"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001455", "offset": 5, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "a5 i pamięć pod symbolem allocp są zgodne. Snapshot używa stabilnego PC 0x8000118c z tego samego powodu co krok 07."
                    }
                  },
                  {
                    "id": "alloc7-cursor-result",
                    "number": 16,
                    "label": "&allocbuf[5]",
                    "svg_label": "16",
                    "progress_id": "UML04-S16",
                    "description": "Wynik odczytu kursora to adres 0x80001455, czyli allocbuf + 5.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:33",
                    "snapshot_ref": "task04.alloc7.cursor",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.used", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc7.cursor-value", "anchor_ref": "reg.a5", "label": "&allocbuf[5]", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "a5 0x80001455", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x8000118c",
                      "code": {
                        "source": "used = allocp - allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:33",
                        "assembly": "a5 = 0x80001455  # wartość allocp\npc = 0x8000118c   # stabilny stan po lw/bne",
                        "note": "Strzałka zwrotna UML interpretuje kursor jako &allocbuf[5] i nie dodaje instrukcji."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001270"}, {"name": "a0", "value": "0x00000007"}, {"name": "a4", "value": "0x80001455"}, {"name": "a5", "value": "0x80001455"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "7", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x80001450", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001455"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001455", "offset": 5, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Krok 16 interpretuje adres: 0x80001455 - 0x80001450 = 5."
                    }
                  },
                  {
                    "id": "alloc7-used",
                    "number": 17,
                    "label": "used = allocp - allocbuf = 5",
                    "svg_label": "17",
                    "progress_id": "UML04-S17",
                    "description": "Po odejmowaniu a5=5 i slot used=5 dowodzą, że pierwszy przydział zajął pięć bajtów.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:33",
                    "snapshot_ref": "task04.alloc7.used",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.used", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc7.used", "anchor_ref": "src.task04.alloc.used", "label": "used = 5", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "used = allocp - allocbuf;", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x800011a4",
                      "code": {
                        "source": "used = allocp - allocbuf;",
                        "source_ref": "task04_address_arithmetic_alloc.c:33",
                        "assembly": "0x80001190  lw    a4,1168(a5) # allocp\n0x80001198  addi  a5,a5,1104  # allocbuf\n0x8000119c  sub   a5,a4,a5\n0x800011a0  sw    a5,-20(s0)  # used\n0x800011a4  lw    a5,-36(s0)  # n",
                        "note": "Różnica adresów 0x80001455−0x80001450 daje pięć bajtów i trafia do pola used."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001270"}, {"name": "a0", "value": "0x00000007"}, {"name": "a4", "value": "0x80001455"}, {"name": "a5", "value": "0x00000005"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "7", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "5", "state": "initialised"}, {"name": "result", "address": "0x800fffb8", "value": "raw 0x80001450", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x80001455"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x80001455", "offset": 5, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "used jest już zainicjalizowane. result wciąż pokazuje jedynie surową pozostałość w slocie i pozostaje oznaczone znakiem zapytania."
                    }
                  },
                  {
                    "id": "alloc7-commit",
                    "number": 18,
                    "label": "result = allocp; allocp += 7",
                    "svg_label": "18",
                    "progress_id": "UML04-S18",
                    "description": "result przyjmuje 0x80001455, a allocp przesuwa się o siedem bajtów do 0x8000145c, czyli offsetu 12.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:39-40",
                    "snapshot_ref": "task04.alloc7.commit",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.alloc.commit", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc7.commit", "anchor_ref": "src.task04.alloc.commit", "label": "offset 5 → 12", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "allocp += n;", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001200",
                      "code": {
                        "source": "result = allocp;\nallocp += n;",
                        "source_ref": "task04_address_arithmetic_alloc.c:39-40",
                        "assembly": "0x800011e4  sw    a5,-24(s0)   # result\n0x800011ec  lw    a4,1168(a5) # allocp\n0x800011f0  lw    a5,-36(s0)  # n\n0x800011f4  add   a4,a4,a5\n0x800011fc  sw    a4,1168(a5) # allocp\n0x80001200  lw    a5,-24(s0)  # result",
                        "note": "Stary kursor 0x80001455 staje się result, a zapis 0x8000145c ustala końcowy offset 12."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffa0"}, {"name": "s0/fp", "value": "0x800fffd0"}, {"name": "ra", "value": "0x80001270"}, {"name": "a0", "value": "0x00000007"}, {"name": "a4", "value": "0x8000145c", "note": "nowy kursor"}, {"name": "a5", "value": "0x80001000"}],
                      "frame": {"name": "alloc_local", "sp": "0x800fffa0", "fp": "0x800fffd0", "size_bytes": 48, "fields": [{"name": "n", "address": "0x800fffac", "value": "7", "state": "initialised"}, {"name": "used", "address": "0x800fffbc", "value": "5", "state": "initialised"}, {"name": "result", "address": "0x800fffb8", "value": "0x80001455", "state": "initialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x8000145c"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x8000145c", "offset": 12, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Stan pamięci potwierdza 5 + 7 = 12. Arena nadal zawiera zera, ponieważ sam alokator przesuwa kursor, ale nie inicjalizuje danych użytkownika."
                    }
                  },
                  {
                    "id": "alloc7-return",
                    "number": 19,
                    "label": "return second = &allocbuf[5]",
                    "svg_label": "19",
                    "progress_id": "UML04-S19",
                    "description": "Po powrocie second=0x80001455, first=0x80001450, a statyczny allocp pozostaje na 0x8000145c.",
                    "code_ref": "src/tasks/task04_address_arithmetic_alloc.c:41",
                    "snapshot_ref": "task04.alloc7.return",
                    "view": {
                      "connection_ref": "task04.debug.auto",
                      "layout": "termdebug.source-asm-dashboard-memory",
                      "reveal": {"anchor_ref": "src.task04.main.alloc7-call", "align": "center"},
                      "annotations": [
                        {"id": "ann.alloc7.return", "anchor_ref": "src.task04.main.alloc7-call", "label": "second = &allocbuf[5]", "tone": "danger", "shape": "outline", "anchor": {"kind": "screen-text", "text": "second = alloc_local(7);", "padding_cells": 1}}
                      ]
                    },
                    "snapshot": {
                      "pc": "0x80001274",
                      "code": {
                        "source": "return result;\n/* main: */ second = alloc_local(7);",
                        "source_ref": "task04_address_arithmetic_alloc.c:41,65",
                        "assembly": "0x80001200  lw    a5,-24(s0) # result\n0x80001204  mv    a0,a5\n0x80001214  ret\n0x80001270  sw    a0,-24(s0) # second\n0x80001274  li    a0,64",
                        "note": "Wartość 0x80001455 wróciła w a0 i została zapisana do second; kolejna instrukcja rozpoczyna już test braku pamięci."
                      },
                      "registers": [{"name": "sp", "value": "0x800fffd0"}, {"name": "s0/fp", "value": "0x800ffff0"}, {"name": "ra", "value": "0x80001270"}, {"name": "a0", "value": "0x80001455", "note": "wartość zwrotna"}, {"name": "a4", "value": "0x8000145c"}, {"name": "a5", "value": "0x80001455"}],
                      "frame": {"name": "main", "sp": "0x800fffd0", "fp": "0x800ffff0", "size_bytes": 32, "fields": [{"name": "first", "address": "0x800fffdc", "value": "0x80001450", "state": "initialised"}, {"name": "second", "address": "0x800fffd8", "value": "0x80001455", "state": "initialised"}, {"name": "out_of_memory", "address": "0x800fffd4", "value": "raw 0x00000000", "state": "uninitialised"}, {"name": "zero_request", "address": "0x800fffd0", "value": "raw 0x00000000", "state": "uninitialised"}]},
                      "memory": {"items": [{"name": "allocbuf", "address": "0x80001450", "value": "char[64]"}, {"name": "allocp", "address": "0x80001490", "value": "0x8000145c"}], "arena": {"base": "0x80001450", "size_bytes": 64, "cursor": "0x8000145c", "offset": 12, "bytes": "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"}},
                      "description": "Ramka alloc_local zniknęła. Ramka main zawiera oba wyniki, a stan alokatora w .bss zachowuje offset 12."
                    }
                  }
                ]
              }
            ]
          }
        },
        {
          "path": "assets/task04-first-allocation-annotated.png",
          "caption": "KOD + DEBUG — zatrzymanie przed pierwszym przydziałem: (1) wywołanie w C, (2) li/jal w RISC-V, (3) argument n=5 w a0, (4) allocbuf przed zapisem. Połącz te cztery miejsca z komunikatem alloc(5) na UML.",
          "label": "fig:task04-first-allocation",
          "alt": "Termdebug Task04: GDB Dashboard po lewej, kod C i listing RISC-V po prawej oraz pusty allocbuf",
          "kind": "screenshot",
          "width": 1.0
        }
      ]
    }
  ],
  "tasks": {
    "task01": {
      "prompt_tex": "\\textbf{Adres, \\texttt{\\&} i dereferencja.} Przewidź i zmierz wartości \\texttt{x}, \\texttt{y}, \\texttt{z[0]}, \\texttt{z[1]} oraz relację \\texttt{iq == \\&z[0]}. Oczekiwane: \\texttt{x=0 y=1 z0=7 z1=12 same=1}.",
      "criterion": "Wartości są zgodne, iq wskazuje z[0], a g_task01_pass ma wartość 1.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task02": {
      "prompt_tex": "\\textbf{Wskaźnik jako argument funkcji.} Prześledź \\texttt{swap\\_int} i \\texttt{parse\\_int}; porównaj ramkę funkcji z ramką \\texttt{main} oraz początkowe argumenty w \\texttt{a0/a1}. Oczekiwane: \\texttt{a=19 b=7 parsed=-42 status=1}.",
      "criterion": "Zmiana obiektów wywołującego i przesunięcie wskaźnika wejściowego są wyjaśnione; g_task02_pass = 1.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task03": {
      "prompt_tex": "\\textbf{Tablica i arytmetyka wskaźnika.} Wykaż równoważność \\texttt{a[i]}, \\texttt{*(a+i)} i \\texttt{pa[i]}; zmierz różnicę adresów w elementach i bajtach. Oczekiwane: \\texttt{first=3 p2=8 i2=8 sum=42 len=5}.",
      "criterion": "Wyniki i skala indeksu są wyjaśnione; g_task03_pass = 1.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task04": {
    "title": "K&R 5.4 -- Linear Memory Allocator",
      "uuid": "98dbe323-c624-53d6-9fe2-4ee688c44b38",
      "prompt_tex": "\\textbf{K\\&R 5.4 -- czysty alokator liniowy typu bump.} Zrealizuj wyłącznie reset całej areny oraz przydział przesuwający jeden kursor. Przewidź sekwencję reset $\\rightarrow$ alloc(5) $\\rightarrow$ alloc(7), a następnie potwierdź ją w kodzie, ABI RV32 i pamięci.",
      "criterion": "Potwierdzono first=allocbuf+0, second=allocbuf+5 i allocp=allocbuf+12, bajty A B x y, sumę 372, odmowę braku pamięci i żądania zerowego oraz różnicę między allocp i sp.",
      "conclusion_tex": "Ten alokator nie obsługuje pojedynczego \\texttt{free}; pamięć odzyskuje się przez reset całej areny. Dowolne zwalnianie bloków pojawi się później przy strukturach i \\texttt{heap\\_4}.",
      "flow": [
        {
          "kind": "block",
          "id": "schema",
          "title": "SCHEMAT — przewidź stan areny",
          "content_tex": "Arena \\texttt{allocbuf} ma 64 bajty, a \\texttt{allocp} wskazuje pierwszy wolny bajt. Stan opisuje offset \\texttt{allocp - allocbuf}; bezwzględny adres nie jest częścią wyniku.",
          "steps": [
            {
              "id": "predict-state",
              "title": "Zaznacz na Rys. 1 granice przydziałów i zapisz trzy offsety.",
              "content_tex": "Obserwowalny wniosek przed uruchomieniem: reset daje 0, \\texttt{alloc(5)} daje 5, a \\texttt{alloc(7)} daje 12."
            },
            {
              "id": "predict-results",
              "title": "Przypisz wyniki do początku każdego przydzielonego obszaru.",
              "content_tex": "Oczekiwany stan: \\texttt{first = allocbuf + 0}, \\texttt{second = allocbuf + 5}, \\texttt{allocp = allocbuf + 12}."
            }
          ]
        },
        {
          "kind": "block",
          "id": "uml",
          "title": "UML — połącz wywołania z czasem życia danych",
          "content_tex": "Rys. 2 rozdziela statyczne \\texttt{allocbuf/allocp} od logicznych ramek \\texttt{main()} i \\texttt{alloc\\_local(n)}. Paski aktywacji pokazują czas wykonywania funkcji, nie osobny obszar alokatora.",
          "steps": [
            {
              "id": "classify-storage",
              "title": "Wybierz blok model i rozdziel .bss, ramkę main() oraz ramkę alloc_local(n).",
              "content_tex": "Wniosek: \\texttt{allocbuf/allocp} istnieją przez cały program; \\texttt{first}, \\texttt{second}, \\texttt{out\\_of\\_memory}, \\texttt{zero\\_request} oraz \\texttt{n}, \\texttt{used}, \\texttt{result} mają automatyczny czas życia. Przy \\texttt{-O0} debugger wiąże ramkę z \\texttt{sp/s0}, ale kompilator może przechować wartość w rejestrze."
            },
            {
              "id": "follow-states",
              "title": "Przejdź przez pięć stanów UML i dla każdego zapisz jeden sprawdzalny wniosek.",
              "content_tex": "Użyj zakładek: model/notacja, reset, alloc(5), alloc(7), kod/debug. Nie opisuj każdej strzałki osobno; zakończ stan konkretną wartością, offsetem albo czasem życia."
            }
          ]
        },
        {
          "kind": "block",
          "id": "code-debug",
          "title": "KOD + DEBUG — sprawdź przewidywanie",
          "content_tex": "Implementacja ma dwie operacje: \\texttt{allocator\\_reset()} oraz \\texttt{alloc\\_local(n)}. Przydział zachowuje stary kursor jako wynik, sprawdza granicę areny i przesuwa kursor o \\texttt{n} bajtów.",
          "steps": [
            {
              "id": "implement-bump",
              "title": "Uzupełnij reset i dodatni przydział mieszczący się w arenie.",
              "content_tex": "Wniosek z kodu: wynik jest starym \\texttt{allocp}, a nowy \\texttt{allocp} wskazuje pierwszy wolny bajt; żądanie zerowe i brak miejsca zwracają \\texttt{NULL} bez zmiany offsetu 12."
            },
            {
              "id": "run-native",
              "title": "Uruchom stemctl test native-amd64 inf pointers 4 i porównaj z przewidywaniem.",
              "content_tex": "Dowód kończy się wynikiem \\texttt{first=0 second=5 allocp=12 sum=372 oom=1 zero=1 pass=1}."
            },
            {
              "id": "inspect-hazard3",
              "title": "Na Rys. 3 i w Hazard3 połącz C, li/jal, a0, sp/s0 oraz allocbuf.",
              "content_tex": "Sprawdź \\texttt{a0=5}, ramkę bieżącej funkcji przy \\texttt{sp/s0}, \\texttt{first-allocbuf}, \\texttt{second-allocbuf}, \\texttt{allocp-allocbuf} i \\texttt{x/16bx allocbuf}. Wniosek: \\texttt{allocp} opisuje arenę w .bss, a \\texttt{sp} ramkę stosu."
            },
            {
              "id": "measure-rp2350",
              "title": "Na zajęciach powtórz ten sam pomiar na RP2350.",
              "content_tex": "Przez \\texttt{stemctl probe list} wybierz urządzenie. Wniosek ma zachować offsety i znaczenie zmiennych mimo innych bezwzględnych adresów."
            }
          ]
        }
      ],
      "educational_requirement_refs": ["PTR.WE02", "PTR.WE03"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EN02", "PTR.EK02", "PTR.EK03"],
      "assessment_criterion_ref": "PTR.KW02"
    },
    "task05": {
      "prompt_tex": "\\textbf{K\\&R 5.5 -- wskaźniki znakowe.} Przejdź po buforze do kończącego bajtu NUL i zwizualizuj bajty bufora.",
      "criterion": "Granica napisu i ruch wskaźnika są poprawnie wskazane.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task06": {
      "prompt_tex": "\\textbf{K\\&R 5.6 -- tablica wskaźników.} Posortuj przez zamianę adresów bez kopiowania treści napisów.",
      "criterion": "Uczeń odróżnia tablicę wskaźników od danych, na które wskazuje.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task07": {
      "prompt_tex": "\\textbf{K\\&R 5.7 -- tablica wielowymiarowa.} Zmierz układ kolejnych wierszy w pamięci.",
      "criterion": "Uczeń wyjaśnia ciągłość elementów i krok wskaźnika do wiersza.",
      "educational_requirement_refs": ["PTR.WE01"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task08": {
      "prompt_tex": "\\textbf{K\\&R 5.8 -- inicjalizacja tablicy wskaźników.} Porównaj rozmieszczenie tablicy adresów i wskazywanych danych.",
      "criterion": "Dwa odrębne obszary pamięci są poprawnie rozpoznane.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task09": {
      "prompt_tex": "\\textbf{K\\&R 5.9 -- tablica 2D kontra tablica wskaźników.} Porównaj typ, rozmiar i sposób adresowania.",
      "criterion": "Uczeń nie utożsamia obu reprezentacji i potrafi wskazać koszt dostępu.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task10": {
      "prompt_tex": "\\textbf{K\\&R 5.10 -- argumenty programu.} Przejdź wskaźnikiem po \\texttt{argv} na AMD64.",
      "criterion": "Uczeń wyjaśnia zakończenie tablicy argumentów i rozdział wskaźników od napisów.",
      "educational_requirement_refs": ["PTR.WE01"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01"],
      "assessment_criterion_ref": "PTR.KW01"
    },
    "task11": {
      "prompt_tex": "\\textbf{K\\&R 5.11 -- wskaźnik do funkcji.} Porównaj adres funkcji z pośrednią instrukcją wywołania i przygotuj pojęcie strategii/polityki.",
      "criterion": "Adres funkcji i wywołanie pośrednie są odnalezione w źródle, rejestrze i listingu.",
      "educational_requirement_refs": ["PTR.WE01", "PTR.WE02"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01", "PTR.EK02"],
      "assessment_criterion_ref": "PTR.KW04"
    },
    "task12": {
      "prompt_tex": "\\textbf{K\\&R 5.12 -- złożone deklaracje.} Rozłóż deklarację od identyfikatora na operatory wskaźnika, tablicy i funkcji.",
      "criterion": "Deklaracja jest czytana w poprawnej kolejności i powiązana z typem.",
      "educational_requirement_refs": ["PTR.WE01"],
      "learning_effect_refs": ["PTR.EN01", "PTR.EK01"],
      "assessment_criterion_ref": "PTR.KW01"
    }
  },
  "tasks_order": ["task01", "task02", "task03", "task04", "task05", "task06", "task07", "task08", "task09", "task10", "task11", "task12"]
}
