Tilbake
1.4

1.4 LMC-assembly — instruksjonssett og sporing

Little Man Computer: instruksjonssettet (INP/OUT/OTC/LDA/STA/ADD/SUB/BRA/BRZ/BRP/HLT/DAT) og den mekaniske sporemetoden med akkumulator/minne-tabell.

50 min
8 oppgaver
LMC-assemblyinstruksjonssettsporing
Din fremgang i kapitlet
0 / 8 oppgaver

Forkunnskaper — sist du var her

Dette kapitlet bygger på kap. 1.1, men bruker lite av tallapparatet. Det du trenger, er:

1. At et tall og en instruksjon kan være samme ting. Alt i minnet er tresifrede tall. Om et tall leses som data eller som en instruksjon, avhenger av hva maskinen gjør med det — akkurat som en byte kunne leses på to måter i kap. 1.3.

2. Grunnleggende addisjon og subtraksjon. LMC regner i titallssystemet, ikke binært. Det er en pedagogisk forenkling: du skal lære mekanismen, ikke bitfikling.

3. ASCII-tanken: at et tall kan representere et tegn. 72 er H. Du får tabellutdraget du trenger i notasjonslisten.

Vil du se maskinvaren bak, ligger den i Datamaskinen og dens komponenter — men du trenger den ikke her.

Notasjons- og konstruksjonsliste

Løkke 1 — Maskinen med hundre postkasser (~10 min)

Se for deg et lukket rom. Langs veggen står hundre nummererte postkasser, fra 00 til 99. Hver av dem inneholder en lapp med et tresifret tall.

I rommet står en person — «den lille mannen» — med to hjelpemidler: en kalkulator som viser ett tall om gangen, og en teller som viser hvilken postkasse han skal til neste gang. Utenfor rommet er det en innkurv der noen legger tall til ham, og en utkurv der han legger fra seg svar.

Arbeidet hans er alltid det samme: gå til postkassen telleren peker på, les lappen, gjør det lappen sier, øk telleren med én, og begynn på nytt.

Det er hele maskinen. Kalkulatoren heter akkumulator, telleren heter programteller, og postkassene heter minneceller. Alt en ekte prosessor gjør, er dette — i milliarder av runder per sekund.

Little Man Computer (LMC)

En forenklet modell av en datamaskin med 100 nummererte minneceller (00–99), ett arbeidsregister (akkumulatoren), en programteller, en inn-kurv og en ut-kurv.

Modellen brukes fordi den har akkurat nok deler til å vise hvordan en prosessor arbeider, og ikke flere. Den regner i titallssystemet, ikke binært, slik at du kan konsentrere deg om mekanismen.

Den faste feilslutningen: at LMC er «bare et leketøy». Instruksjonssettet er en tro miniatyr av en ekte prosessors: last, lagre, regn, hopp, stopp. Alt du lærer om sporing her, gjelder for ekte maskinkode.

Akkumulator

Det ene arbeidsregisteret i LMC — kalkulatoren i hånden til den lille mannen. All regning skjer her, og den holder nøyaktig ett tall om gangen.

Vil du regne med to tall, må det ene ligge i en minnecelle: LDA henter det ene inn i akkumulatoren, og ADD legger det andre til fra minnet.

Den faste feilen: å tro at akkumulatoren husker gamle verdier. En ny LDA eller INP overskriver den fullstendig. Skal du ta vare på en verdi, må du STA den til en celle først.

Programteller

Registeret som holder adressen til den neste instruksjonen som skal utføres.

Etter hver instruksjon økes programtelleren med 1 — med mindre instruksjonen var et hopp, og hoppet ble utført. Da settes den til hoppadressen i stedet.

Dette er den viktigste kolonnen i sporetabellen din. Mister du oversikten over programtelleren, sporer du feil program.

Den faste feilen: å anta at programmet kjører rett nedover. En BRA midt i programmet kan sende deg tilbake, og da utføres de samme instruksjonene om igjen med nye verdier.

Minnecelle (postkasse)

En av de 100 nummererte plassene i LMC-minnet, hver med plass til ett tresifret tall (000–999).

Cellene brukes til to ting samtidig: de øverste holder programmets instruksjoner, de nederste holder data. Maskinen skiller ikke mellom dem — det er programtelleren som avgjør hva som blir utført.

Det er nettopp dette som gjør selvmodifiserende kode mulig, og det er tema i kap. 1.5.

Mnemonic

Det korte, uttalbare navnet på en instruksjon: LDA, STA, BRZ. Ordet betyr «huskeord».

Mnemonic-en er en menneskevennlig skrivemåte for et tallkode. LDA 08 er det samme som tallet 508 i minnet — se kap. 1.5.

Den faste feilen: å forveksle LDA og STA, som gjør motsatt ting av hverandre. Huskeregel: L som i last inn i akkumulatoren, S som i store, altså lagre ut til minnet.

Assemblerkode

Programkode skrevet med mnemonics og adresser i stedet for rene tall — ett steg over maskinkoden, men fortsatt én linje per maskininstruksjon.

LDA 08 er assemblerkode; 508 er maskinkoden den oversettes til.

Assembler er ikke et høynivåspråk: det finnes ingen løkker, ingen funksjoner og ingen variabelnavn i seg selv. En løkke må du bygge selv med et hopp, og en «variabel» er en minnecelle du har bestemt deg for å bruke.

Løkke 2 — Instruksjonssettet, én kontrakt om gangen (~15 min)

Hver instruksjon er en kontrakt: den sier nøyaktig hva som skjer med akkumulatoren, med minnet og med programtelleren. Ingenting annet skjer.

Les kontraktene med den tredelingen i hodet. Det er den som gjør sporing mekanisk: for hver instruksjon spør du «hva ble akkumulatoren?», «hvilken celle endret seg?» og «hvor peker programtelleren nå?».

📜Instruksjonskontrakten — de tre spørsmålene

Hver LMC-instruksjon endrer nøyaktig én eller to av tre ting:

1. Akkumulatoren — endres av INP, LDA, ADD og SUB. Uendret av alle andre.
2. Minnet — endres bare av STA. Ingen annen instruksjon skriver til en celle.
3. Programtelleren — økes med 1 av alle instruksjoner, unntatt når BRA hopper, eller når BRZ/BRP hopper fordi betingelsen var oppfylt.

En fjerde ting kan skje uten å endre maskinens tilstand: OUT og OTC legger noe i ut-kurven.

Konsekvens for sporing: en OUT endrer ikke akkumulatoren. En STA endrer ikke akkumulatoren. En BRZ som ikke hopper, endrer ingenting i det hele tatt. Det er lett å tro at «noe må ha skjedd» — men kontrakten sier hva som skjer, og bare det.

`INP` — les inn

Leser det neste tallet fra inn-kurven inn i akkumulatoren. Den gamle verdien i akkumulatoren overskrives.

Maskinkode: 901.

Den faste feilen: å tro at INP legger tallet i en minnecelle. Det gjør den ikke — vil du ta vare på inndata, må du følge opp med STA.

`OUT` — skriv ut som tall

Legger verdien i akkumulatoren i ut-kurven, skrevet som et tall. Akkumulatoren er uendret etterpå.

Maskinkode: 902.

Den faste feilen: å tro at OUT tømmer akkumulatoren. Verdien blir liggende, og neste instruksjon ser den fortsatt.

`OTC` — skriv ut som ASCII-tegn

Legger verdien i akkumulatoren i ut-kurven, tolket som en ASCII-tegnkode. Akkumulatorverdien 72 gir tegnet H, ikke tallet 72.

Maskinkode: 922.

Dette er den hyppigst oversette instruksjonen i sjangeren: et program med OTC skriver ut en tekst, ikke en tallrekke. Ser du OTC, skal du slå opp i ASCII-tabellen.

`LDA xx` — last fra minnet

Kopierer innholdet i celle xx inn i akkumulatoren. Cellen er uendret; det er en kopi.

Maskinkode: 5xx.

Den faste feilen: å blande retningen med STA. LDA går fra minnet inn i akkumulatoren.

`STA xx` — lagre til minnet

Kopierer verdien i akkumulatoren inn i celle xx. Akkumulatoren er uendret; cellens gamle innhold er borte.

Maskinkode: 3xx.

Dette er den eneste instruksjonen som endrer minnet. Skal du finne ut hvorfor en celle har endret verdi under sporing, er det alltid en STA som står bak.

`ADD xx` — legg til

Legger innholdet i celle xx til akkumulatoren. Cellen er uendret.

Maskinkode: 1xx.

Merk at det er cellens innhold som legges til, ikke celletallet: ADD 07 legger til det som ligger i celle 07, ikke tallet 7.

`SUB xx` — trekk fra

Trekker innholdet i celle xx fra akkumulatoren. Cellen er uendret.

Maskinkode: 2xx.

SUB brukes nesten alltid til to ting: å telle ned mot null, og å sammenligne to tall. Er A − B null eller større, er A minst like stor som B — og det er nettopp det BRZ og BRP tester på.

`BRA xx` — hopp alltid

Setter programtelleren til xx, uten betingelse. Akkumulatoren og minnet er uendret.

Maskinkode: 6xx.

En BRA bakover lager en løkke; en BRA framover hopper over en del av koden. Nesten hver løkke i LMC slutter med en BRA tilbake til toppen.

`BRZ xx` — hopp hvis null

Setter programtelleren til xx hvis akkumulatoren er nøyaktig 0. Er den noe annet, fortsetter programmet til neste instruksjon.

Maskinkode: 7xx.

Den faste fella: BRZ treffer bare på eksakt null. Teller du ned med to fra et oddetall, passerer du null uten å treffe den, og løkka stopper aldri.

`BRP xx` — hopp hvis ikke negativ

Setter programtelleren til xx hvis akkumulatoren er 0 eller større. Er den negativ, fortsetter programmet til neste instruksjon.

Maskinkode: 8xx.

Merk at null regnes som «positiv» her — navnet er misvisende. Dette er den vanligste forvekslingen med BRZ, og den endrer ofte antall runder i en løkke med nøyaktig én.

`HLT` — stopp

Avslutter programmet. Ingenting utføres etterpå.

Maskinkode: 000.

En HLT kan stå midt i programmet: kommer den etter en BRA som hopper over den, nås den bare når en betingelse sender programmet dit.

`DAT n` — datacelle

Ikke en instruksjon, men en beskjed til assembleren: reserver denne cellen og gi den startverdien n. Står det bare DAT, er startverdien 0.

Datacellene ligger vanligvis nederst i programmet, etter HLT, slik at de aldri blir utført.

Den faste feilen: å tro at maskinen «vet» at en DAT-celle er data. Det gjør den ikke — hopper programtelleren dit, blir tallet utført som en instruksjon.

✏️Eksempel 1: Det minste hele programmet

Hva gjør dette programmet, og hva skriver det ut med inndataene 3 og 4?

00  INP
01  STA 90
02  INP
03  ADD 90
04  OUT
05  HLT
90  DAT

Sporetabellen. Én rad per utført instruksjon. Kolonnene er programtelleren før instruksjonen, instruksjonen selv, akkumulatoren etterpå, celle 90 etterpå og ut-kurven.

PCInstruksjonAkkumulatorCelle 90Ut-kurv
00INP30
01STA 9033
02INP43
03ADD 9073
04OUT737
05HLT737

Utskrift: 7.
Hva programmet gjør: det leser to tall og skriver ut summen.
Legg merke til hvorfor STA 90 trengs. Akkumulatoren holder ett tall. Uten linje 01 ville den andre INP-en overskrevet det første tallet, og ADD 90 ville lagt til innholdet i en tom celle. Dette er kjernen i LMC-programmering: du parkerer verdier i minnet fordi du bare har én hånd.
Legg også merke til at OUT ikke endret akkumulatoren. Den er fortsatt 7 i siste rad.
📝Oppgave 1

(Innsteg — instruksjonskontrakter.) For hver av instruksjonene under: hva endres, og hva forblir uendret?

a) STA 45

b) OUT

c) BRA 12

📝Oppgave 2
Sjanger C

Hva skriver dette programmet ut med inndataene 8 og 5?

00  INP
01  STA 80
02  INP
03  STA 81
04  LDA 80
05  SUB 81
06  OUT
07  HLT
80  DAT
81  DAT

Løkke 3 — Hopp og løkker (~15 min)

Så langt har programmene kjørt rett nedover. Det interessante skjer når programtelleren settes tilbake.

Et hopp er ikke annet enn å skrive en ny verdi i programtelleren. BRA 02 betyr «neste instruksjon er den i celle 02», og hvis vi allerede har passert celle 02, kjører vi de samme instruksjonene om igjen — men med andre verdier i akkumulator og minne. Det er en løkke.

De to betingede hoppene, BRZ og BRP, er det som gjør at løkka kan stoppe.

📜Sporingsmetoden
Oppsettet. Lag en tabell med disse kolonnene:

PCInstruksjonAkkumulatorhver navngitt minnecelleUt-kurv

Framgangsmåten, steg for steg:

1. Start med programteller = 00, akkumulator = 0 og datacellene på sine DAT-verdier.
2. Les instruksjonen i cellen programtelleren peker på. Skriv den i tabellen.
3. Utfør kontrakten: oppdater akkumulator eller minne, og noter eventuell utskrift.
4. Sett programtelleren til neste celle — eller til hoppadressen hvis et hopp ble utført.
5. Gjenta til HLT.

Tre regler som sparer deg for de vanlige bommene:

- Én rad per utført instruksjon, ikke per linje i programmet. Går du åtte ganger gjennom en løkke på fire linjer, får tabellen 32 rader.
- Skriv av verdien selv om den er uendret. En tom celle i tabellen blir til tvil to minutter senere.
- Ved betinget hopp: skriv hva betingelsen var. «akk = 2, ikke 0, hopper ikke» tar tre sekunder og hindrer at du mister en runde.

Går tabellen over tjue rader, ser du som regel et mønster og kan hoppe videre — men bare når du har sett runden gjenta seg minst to ganger identisk.

✏️Eksempel 2: En løkke som teller ned

Hva skriver dette programmet ut med inndata 3?

00  INP
01  STA 20
02  LDA 20
03  OUT
04  SUB 21
05  STA 20
06  BRP 02
07  HLT
20  DAT
21  DAT 1

Sporetabell. Celle 21 er konstant 1 hele veien og er utelatt.

PCInstruksjonAkkumulatorCelle 20Ut-kurv
00INP30
01STA 2033
02LDA 2033
03OUT333
04SUB 21233
05STA 20223
06BRP 02223
02LDA 20223
03OUT223 2
04SUB 21123 2
05STA 20113 2
06BRP 02113 2
02LDA 20113 2
03OUT113 2 1
04SUB 21013 2 1
05STA 20003 2 1
06BRP 02003 2 1
02LDA 20003 2 1
03OUT003 2 1 0
04SUB 21−103 2 1 0
05STA 20−1−13 2 1 0
06BRP 02−1−13 2 1 0
07HLT−1−13 2 1 0

Utskrift: 3 2 1 0.
— naturlig pausepunkt —
Tre ting å merke seg.
1. Null skrives ut. Det er fordi BRP hopper også når akkumulatoren er nøyaktig 0. Hadde linje 06 vært BRZ 02 i stedet, ville programmet oppført seg helt annerledes.
2. Løkka går én gang mer enn du tror. Den intuitive gjetningen er «3 2 1», altså tre runder. Det er fire. Dette er den vanligste bommen i sjangeren, og den fanges bare av sporetabellen.
3. Akkumulatoren blir negativ til slutt. Det er den negative verdien som gjør at BRP ikke hopper, og programmet faller gjennom til HLT.
📝Oppgave 3
Sjanger C

Hva skriver dette programmet ut med inndata 6? Og hva skjer med inndata 5?

00  INP
01  STA 40
02  LDA 40
03  BRZ 08
04  OUT
05  SUB 41
06  STA 40
07  BRA 02
08  HLT
40  DAT
41  DAT 2

📝Oppgave 4
Sjanger C

Programmet under er det samme som i eksempel 2, men BRP 02 på linje 06 er byttet ut med BRZ 02. Hva skriver det ut med inndata 3?

00  INP
01  STA 20
02  LDA 20
03  OUT
04  SUB 21
05  STA 20
06  BRZ 02
07  HLT
20  DAT
21  DAT 1

Løkke 4 — OTC og eksamensformen (~10 min)

Den siste instruksjonen som må sitte, er OTC. Den skriver ut akkumulatoren som tegn, ikke som tall. Ser du OTC i et program, er utskriften en tekst.

ASCII

Tegnkodingen som gir hvert tegn et tallnummer: A er 65, H er 72, a er 97, mellomrom er 32, og siffertegnet 0 er 48.

I LMC brukes den av OTC, som tolker akkumulatorverdien som en tegnkode.

Systematikken er verdt å kunne: store bokstaver ligger sammenhengende fra 65, små fra 97, og siffertegnene fra 48. Vil du skrive ut C, tar du A-koden 65 og legger til 2.

Den faste feilen: å forveksle tallet 5 med siffertegnet 5. Tallet er 5; tegnet har koden 53.

✏️Eksempel 3: Et program som skriver tekst

Hva skriver dette programmet ut?

00  LDA 10
01  OTC
02  LDA 11
03  OTC
04  HLT
10  DAT 72
11  DAT 73

Sporetabell:

PCInstruksjonAkkumulatorUt-kurv
00LDA 1072
01OTC72H
02LDA 1173H
03OTC73HI
04HLT73HI

Utskrift: HI.
Hadde linje 01 og 03 vært OUT i stedet for OTC, ville utskriften vært 72 73.
Dette er hele fella. Programmet ser ut som et tallprogram — datacellene inneholder tall, akkumulatoren inneholder tall — men utskriften er en tekst. To bokstaver i instruksjonen skiller de to svarene.
✏️Eksempel 4: Eksamensform — hva stemmer om programmet?

Svarform F1. Programmet under kjøres med inndataene 4 og 9. Kryss av alle påstandene som stemmer.

00  INP
01  STA 50
02  INP
03  STA 51
04  SUB 50
05  BRP 08
06  LDA 50
07  BRA 09
08  LDA 51
09  OUT
10  HLT
50  DAT
51  DAT

a) Programmet skriver ut 9.

b) Programmet skriver ut det største av de to tallene.

c) Instruksjonen på linje 08 blir utført.

d) Med inndataene 9 og 4 skriver programmet ut 4.

Sporetabell for inndataene 4 og 9:

PCInstruksjonAkkumulatorCelle 50Celle 51Ut-kurv
00INP400
01STA 50440
02INP940
03STA 51949
04SUB 50549
05BRP 08549akk = 5 ≥ 0, hopper til 08
08LDA 51949
09OUT9499
10HLT9499

a) Riktig. Utskriften er 9.
b) Riktig. Linje 04 regner andre − første. Er differansen null eller større, er det andre tallet størst, og programmet henter det (linje 08). Ellers henter det det første (linje 06). Kontroll med 5 og 5: differansen er 0, BRP hopper, og programmet skriver ut 5 — riktig, siden tallene er like.
c) Riktig. Sporetabellen viser at BRP hoppet til 08, som ble utført.
d) Galt. Med inndataene 9 og 4 blir differansen 4 − 9 = −5, som er negativ. BRP hopper ikke, programmet går til linje 06, henter celle 50 (som er 9) og hopper over linje 08 med BRA 09. Utskriften blir 9, ikke 4. Distraktoren bygger på at man antar at programmet alltid skriver ut det siste innleste tallet.
Fasit: a, b og c.
Legg merke til BRA 09 på linje 07. Uten den ville programmet falt rett ned i linje 08 og overskrevet akkumulatoren med feil verdi. Et hopp som «hopper over» en gren, er standardmønsteret for et valg i assembler.
📝Oppgave 5
Sjanger C

Hva skriver dette programmet ut?

00  LDA 20
01  OTC
02  ADD 21
03  OTC
04  ADD 21
05  OTC
06  HLT
20  DAT 65
21  DAT 1

📝Oppgave 6
Sjanger C

Hva skriver dette programmet ut med inndata 3?

00  INP
01  STA 30
02  LDA 30
03  ADD 30
04  STA 30
05  OUT
06  SUB 31
07  BRP 09
08  BRA 02
09  HLT
30  DAT
31  DAT 20

📝Oppgave 7
Eksamensnivå, sjanger C

Kryss av alle påstandene som stemmer om programmet under, kjørt med inndata 1.

00  INP
01  STA 30
02  LDA 30
03  ADD 30
04  STA 30
05  OUT
06  SUB 31
07  BRP 09
08  BRA 02
09  HLT
30  DAT
31  DAT 20

a) Programmet skriver ut inndataverdien 1 først.

b) Det siste tallet som skrives ut, er 32.

c) Programmet skriver ut fem tall.

d) Instruksjonen BRA 02 utføres fire ganger.

📝Oppgave 8
Eksamensnivå, sjanger C

Et program skal lese ett tall og skrive ut tegnet som ligger den plassen ut i alfabetet. Inndata 1 skal gi A, inndata 2 skal gi B, og så videre.

Hvilken verdi må stå i celle 90 for at programmet skal virke?

00  INP
01  ADD 90
02  OTC
03  HLT
90  DAT ?

Begrepsbank til eksamen

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder kjernestoffet.

Sporetabell

Tabellen du fører for å følge et program mekanisk, med kolonner for programteller, instruksjon, akkumulator, hver navngitt minnecelle og ut-kurven — én rad per utført instruksjon.

Går programmet åtte runder i en løkke på fire linjer, får tabellen 32 rader.

Den faste feilen er å føre én rad per programlinje i stedet for per utførelse. Da ser en løkke ut som om den kjører én gang.

Ubetinget hopp

Et hopp som alltid utføres: BRA xx setter programtelleren til xx uansett hva akkumulatoren inneholder.

Et BRA bakover lager en løkke; et BRA framover hopper over en gren, slik at bare den ene av to alternative kodebiter utføres.

Den faste feilen er å tro at et hopp «kaller» koden det hopper til og kommer tilbake etterpå. Det gjør det ikke — LMC har ingen retur.

Betinget hopp

Et hopp som bare utføres hvis en betingelse på akkumulatoren er oppfylt: BRZ når verdien er nøyaktig 0, BRP når den er 0 eller større.

Er betingelsen ikke oppfylt, skjer ingenting i det hele tatt — programtelleren økes med 1 som vanlig, og akkumulator og minne er uendret.

Betingede hopp er den eneste måten et LMC-program kan ta et valg eller avslutte en løkke.

Løkke i LMC

Et mønster der en BRA eller et betinget hopp sender programtelleren tilbake til en tidligere celle, slik at de samme instruksjonene utføres om igjen med nye verdier.

Standardformen har tre deler: en teller i en minnecelle, en kropp som gjør arbeidet, og en test som avgjør om løkka skal gå en runde til.

Antallet runder er nesten alltid det som testes på eksamen — og det er nesten alltid én mer eller én mindre enn intuisjonen sier.

Sammenligning med SUB

Måten LMC sammenligner to tall på: trekk det ene fra det andre, og test resultatet med BRZ eller BRP.

Er A − B lik 0, er tallene like. Er den 0 eller større, er A minst like stor som B. Er den negativ, er A minst.

Dette mønsteret — LDA, SUB, BRP — er standardformen for et valg i assembler, og du finner det i nesten hvert eneste eksamensprogram.

Adresse mot innhold

Skillet mellom cellens nummer og cellens verdi. ADD 07 legger til innholdet i celle 07, ikke tallet 7.

Et program kan derfor endre hva ADD 07 faktisk legger til, uten å endre instruksjonen — bare ved å skrive en ny verdi i celle 07.

Dette skillet er forutsetningen for selvmodifiserende kode, som du møter i kap. 1.5.

Inn-kurv og ut-kurv

LMCs to kanaler til omverdenen. INP tar det neste tallet fra inn-kurven; OUT og OTC legger noe i ut-kurven.

Inn-kurven er en kø: tallene leses i den rekkefølgen de ble lagt inn, og hver INP tar ett.

Den faste feilen er å tro at INP kan lese det samme tallet flere ganger. Vil du bruke en inndataverdi to steder, må du STA den til en celle først.

Etikett

Et symbolsk navn på en minnecelle, brukt i stedet for celletallet i lesbar assemblerkode — for eksempel TELLER i stedet for 20.

Etiketter finnes bare i kildekoden. Assembleren erstatter dem med celletall før programmet kjøres, så maskinen ser aldri navnet.

Oppgavesettene bruker gjennomgående celletall, så denne boka gjør det også — men møter du en etikett, er den bare et navn på et tall.

Repetisjon — sitter instruksjonssettet?

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.