Tilbake
1.5

1.5 LMC-maskinkode og selvmodifiserende kode

Maskinkode (3-sifrede opkoder), dekoding til assembly, selvmodifiserende kode og reverse tracing («hvilken input gir utskrift X?»).

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

Forkunnskaper — sist du var her

Dette kapitlet bygger direkte på kap. 1.4. Tre ting du trenger, ferdig oppfrisket:

1. Sporetabellen. Kolonner for programteller, instruksjon, akkumulator, hver navngitt minnecelle og ut-kurven — én rad per utført instruksjon.

2. Instruksjonskontrakten. Bare STA skriver til minnet. Bare INP, LDA, ADD og SUB endrer akkumulatoren. Programtelleren økes med 1 med mindre et hopp utføres.

3. De tre hoppene. BRA xx hopper alltid. BRZ xx hopper når akkumulatoren er nøyaktig 0. BRP xx hopper når den er 0 eller større.

Og én ting fra kap. 1.3 som får ny betydning her: et bitmønster bærer ikke sin egen tolkning. Der var det byten som kunne leses på to måter. Her er det minnecellen: det samme tresifrede tallet er en instruksjon eller et datatall, avhengig av hva maskinen gjør med det.

Notasjons- og konstruksjonsliste

Løkke 1 — Instruksjonen er et tall (~12 min)

I forrige kapittel skrev vi programmer som LDA 08. Men den lille mannen kan ikke lese bokstaver — postkassene inneholder bare tresifrede tall.

LDA 08 er derfor egentlig tallet 508. Første siffer sier hva som skal gjøres, de to siste sier hvor.

Det høres ut som en teknisk detalj. Det er det ikke: det er hele grunnen til at en datamaskin kan være en generell maskin i det hele tatt. Programmet ligger i det samme minnet som dataene, i det samme formatet, og kan derfor behandles som data. Denne innsikten har et navn — von Neumann-arkitekturen — og du møter den igjen i kap. 2.4.

Maskinkode

Programmet slik det faktisk ligger i minnet: rene tall, uten mnemonics. I LMC er hver instruksjon et tresifret tall.

LDA 08 er maskinkoden 508; STA 05 er 305; HLT er 000.

Maskinkode er det maskinen utfører. Assemblerkoden med mnemonics finnes bare i kildekoden din og i lærebøker — den oversettes bort før programmet kjøres.

Opkode

Den delen av instruksjonen som sier hva som skal gjøres. I LMC er det det første av de tre sifrene.

Opkoden 5 betyr «last inn», 3 betyr «lagre», 6 betyr «hopp». Opkoden 9 er spesiell: den brukes bare til de tre faste kodene 901, 902 og 922, der de to siste sifrene ikke er en adresse.

Den faste feilen er å forveksle 5xx (LDA) og 3xx (STA). De gjør motsatt ting av hverandre, og forvekslingen snur hele dataflyten i programmet.

Operand (adressefelt)

De to siste sifrene i en instruksjon: hvilken celle operasjonen gjelder.

I 508 er operanden 08, altså celle 08. I 730 er den 30.

Merk at operanden alltid er en adresse, aldri en verdi. 108 legger til innholdet i celle 08, ikke tallet 8. Det er dette skillet som gjør at et program kan endre hva en instruksjon virker på, uten å endre hva den gjør.

For 901, 902 og 922 er de to siste sifrene ikke en adresse, men en del av selve koden.

📜Dekoding av maskinkode
Fra tall til instruksjon:

1. Del det tresifrede tallet i første siffer og de to siste.
2. Slå opp første siffer i opkodetabellen.
3. De to siste sifrene er celleadressen.

604 → opkode 6 (BRA) og adresse 04 → BRA 04.

Fra instruksjon til tall: gang opkoden med 100 og legg til adressen.

STA 123 · 100 + 12 = 312.

Tre unntak å kjenne: 901 (INP), 902 (OUT) og 922 (OTC) har ingen adresse — hele det tresifrede tallet er koden. Og 000 er HLT.

Kontrollen som fanger de fleste bommene: gjør dekodingen begge veier. Har du dekodet 508 til LDA 08, kod tilbake: 5 · 100 + 8 = 508. Stemmer det, er du trygg.

✏️Eksempel 1: Dekod et helt program

Minnet fra celle 00 inneholder tallene under. Hva gjør programmet?

celle:   00   01   02   03   04   05   90   91 tall:   901  390  901  190  902  000  000  000

Dekod celle for celle.

CelleTallOpkodeAdresseInstruksjon
00901INP
01390390STA 90
02901INP
03190190ADD 90
04902OUT
05000HLT
90000datacelle, startverdi 0

Programmet er:
00  INP
01  STA 90
02  INP
03  ADD 90
04  OUT
05  HLT
90  DAT
Det leser to tall og skriver ut summen — det samme programmet du sporet i kap. 1.4.
Legg merke til celle 90. Den inneholder 000, som dekodet som instruksjon ville vært HLT. Men programtelleren kommer aldri dit, fordi HLT i celle 05 stopper alt. Om 000 er data eller instruksjon, avgjøres altså ikke av tallet, men av om programtelleren peker på det.
📝Oppgave 1

(Innsteg — dekoding.) Oversett hver maskinkode til assemblerkode.

a) 508

b) 305

c) 801

d) 922

📝Oppgave 2
Eksamensnivå, sjanger C

Maskinkodesekvensen 508 305 000 ligger i cellene 00, 01 og 02. Kryss av alle påstandene som stemmer.

a) Celle 00 inneholder ADD 08.

b) Celle 01 inneholder STA 05.

c) Celle 02 stopper programmet.

d) Celle 00 inneholder LDA 08.

Løkke 2 — Selvmodifiserende kode (~15 min)

Nå kommer konsekvensen. Hvis en instruksjon er et tall, kan et program regne på den.

LDA 30 er 530. Legger du til 1, får du 531, som er LDA 31. Legger du til 2, får du LDA 32. Med andre ord: å legge til et tall i en instruksjon flytter adressen dens.

Det gir LMC noe den ellers ikke har — muligheten til å velge celle mens programmet kjører. I moderne språk heter det å slå opp i en liste med en indeks. I LMC må du bygge instruksjonen selv.

Selvmodifiserende kode

Et program som endrer sine egne instruksjoner mens det kjører, ved å regne ut en ny instruksjonsverdi og skrive den til en kodecelle med STA.

Mønsteret er alltid det samme: last en instruksjonsmal, legg til noe, STA resultatet inn i den cellen som snart skal utføres.

Dette er mulig fordi kode og data ligger i samme minne i samme format — og fordi STA kan skrive til hvilken som helst celle, også en som inneholder kode.

Den faste feilen (felle #7): å spore programmet som om kodecellene var uforanderlige. Fra det øyeblikket en STA treffer en kodecelle, må sporetabellen din ha en egen kolonne for den cellen.

📜Å lese selvmodifiserende kode
Kjennetegnet: en STA med en adresse som ligger i kodeområdet, altså blant de lave cellenumrene der instruksjonene står.

Ser du STA 04 i et program der celle 04 er en av instruksjonene, er det ikke en skrivefeil. Det er en modifikasjon.

Framgangsmåten:

1. Finn ut hvilken celle som blir skrevet til, og gi den en egen kolonne i sporetabellen.
2. Regn ut hvilken verdi som skrives inn.
3. Dekod den nye verdien som instruksjon — det er den som blir utført når programtelleren kommer dit.
4. Fortsett sporingen med den nye instruksjonen på plass.

De to vanlige variantene:

- Endret adresse: malen legges til en liten verdi, slik at samme opkode peker på en annen celle. 130 pluss 2 gir 132, altså ADD 32 i stedet for ADD 30.
- Endret hoppmål: en DAT-celle med verdien 600 er BRA 00. Legger programmet til brukerens inndata, blir hoppet til celle 00 pluss inndataen — altså en beregnet forgrening.

Nøkkelspørsmålet du alltid skal stille: rekker cellen å bli endret før programtelleren kommer dit? Skrives celle 04 av en STA i celle 02, er svaret ja. Skrives den av en STA i celle 07, er svaret nei — da utføres den gamle verdien først.

✏️Eksempel 2: Et program som velger datacelle selv

Programmet under leser et tall og skriver ut en verdi fra en tabell. Hva skriver det ut med inndata 1?

00  INP
01  ADD 20
02  STA 04
03  LDA 21
04  DAT 0
05  OUT
06  HLT
20  DAT 130
21  DAT 0
30  DAT 7
31  DAT 19
32  DAT 45

Se etter kjennetegnet først. Linje 02 er STA 04 — og celle 04 ligger midt i koden. Dette er selvmodifiserende kode.

Sporetabell. Celle 04 får sin egen kolonne, med instruksjonen den tilsvarer i parentes.

PCInstruksjonAkkumulatorCelle 04Ut-kurv
00INP1000 (HLT)
01ADD 20131000
02STA 04131131 (ADD 31)
03LDA 210131
04ADD 3119131
05OUT1913119
06HLT1913119

Utskrift: 19.
Hva som skjedde. Celle 20 inneholder 130, som er malen ADD 30. Inndata 1 legges til, og resultatet 131 er ADD 31. Den skrives inn i celle 04, som deretter utføres — og henter celle 31, altså 19.
Kontroll med de andre inndataene:
InndataInstruksjonen i celle 04 blirUtskrift
0130 = ADD 307
1131 = ADD 3119
2132 = ADD 3245

Legg merke til linje 03. LDA 21 setter akkumulatoren til 0 før tabelloppslaget. Uten den ville ADD lagt tabellverdien til 131, og utskriften blitt 150 i stedet for 19. Nullstillingen er ikke pynt — den er en nødvendig del av mønsteret.
📝Oppgave 3
Sjanger C

I programmet fra eksempel 2: hva skriver programmet ut med inndata 2, og hvilken instruksjon står i celle 04 når HLT nås?

📝Oppgave 4
Eksamensnivå, sjanger C

Kryss av alle påstandene som stemmer om programmet fra eksempel 2.

a) Celle 20 inneholder malen for en ADD-instruksjon.

b) Med inndata 3 ville programmet skrevet ut innholdet i celle 33.

c) Linje 03 kan fjernes uten at utskriften endres.

d) Programmet ville virket like godt om linje 02 var STA 05.

Løkke 3 — Baklengs sporing (~13 min)

Den andre vrien snur oppgaven: du får utskriften og skal finne inndataen.

Her er hovedpoenget ikke å regne baklengs steg for steg — det går sjelden. Poenget er å forstå hva programmet gjør, og deretter spørre hvilke inndata som gir det resultatet.

Og det avgjørende: svaret er ofte mer enn én verdi. Et program som regner ut en absoluttverdi, gir samme utskrift for −7 og 7. Krysser du bare av den ene, mister du halve poenget.

Baklengs sporing

Oppgavetypen der utskriften er gitt og du skal finne hvilken inndata som produserer den.

Framgangsmåten er ikke å reversere hver instruksjon, men å beskrive hva programmet gjør som en helhet, og deretter løse det spørsmålet.

Den faste feilen: å oppgi én inndata når flere gir samme utskrift. Programmer med BRP eller SUB som «snur fortegnet» har typisk to løsninger, og oppgaven er som regel en «velg ett eller flere»-oppgave nettopp derfor.

Flertydig inndata

Situasjonen der to eller flere ulike inndataverdier gir nøyaktig samme utskrift.

Det oppstår når programmet mister informasjon underveis: en absoluttverdi glemmer fortegnet, en heltallsdivisjon glemmer resten, en test på «større enn» glemmer hvor mye større.

På eksamen er dette nesten alltid tilsiktet. Ser du et program med en BRP som velger mellom to grener som ender i samme utskrift, skal du lete etter to svar.

✏️Eksempel 3: Hvilke inndata gir utskriften 2?

Hva gjør programmet under, og hvilke inndata gir utskriften 2?

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

Steg 1 — hva gjør programmet? Les det som to grener.

Linje 00–01 leser inndataen og parkerer den i celle 50. Akkumulatoren inneholder fortsatt inndataen når BRP 06 vurderes.

- Er inndataen 0 eller større, hopper BRP til linje 06, som henter celle 50 tilbake. Utskriften er inndataen selv.
- Er inndataen negativ, hopper den ikke. Linje 03 setter akkumulatoren til 0 (celle 51 er 0), og linje 04 trekker inndataen fra: 0 − (−7) = 7. Utskriften er inndataen med motsatt fortegn.

Programmet regner altså ut absoluttverdien.

Steg 2 — hvilke inndata gir 2?

- Inndata 2: BRP hopper (2 er større enn 0), celle 50 hentes, utskrift 2. ✓
- Inndata −2: BRP hopper ikke, akkumulatoren blir 0 − (−2) = 2, utskrift 2. ✓

Svar: både 2 og −2 gir utskriften 2.

Sporetabell for den negative grenen, som er den man lettest bommer på:

PCInstruksjonAkkumulatorCelle 50Ut-kurv
00INP−20
01STA 50−2−2
02BRP 06−2−2negativ, hopper ikke
03LDA 510−2
04SUB 502−2
05BRA 072−2
07OUT2−22
08HLT2−22

Merk BRA 07 på linje 05. Uten den ville den negative grenen falt rett ned i linje 06, som ville hentet celle 50 (altså −2) tilbake i akkumulatoren, og utskriften blitt −2. Hoppet som «hopper over den andre grenen» er standardmønsteret for et valg.
📝Oppgave 5
Sjanger C

Bruk programmet fra eksempel 3. Hvilke inndata gir utskriften 0? Begrunn.

📝Oppgave 6
Eksamensnivå, sjanger C

Programmet under skriver ut ett tall. Kryss av alle inndataene som gir utskriften 12.

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

a) 6

b) 12

c) 3

d) 10

Løkke 4 — Beregnet hopp (~10 min)

Den siste varianten kombinerer de to foregående: et program som regner ut hvor det skal hoppe.

Trikset er det samme som før. 600 er BRA 00. Legger du til et tall, flytter du hoppmålet.

Beregnet hopp

En hoppinstruksjon der adressen regnes ut mens programmet kjører, ved å legge en verdi til en hoppmal og skrive resultatet inn i den cellen som snart skal utføres.

Malen er en DAT-celle med en verdi som 600, altså BRA 00. Legges brukerens inndata til, blir hoppmålet celle 00 pluss inndataen.

Dette er LMCs versjon av en meny eller en valgtabell: én inndataverdi velger mellom flere rutiner.

Den faste feilen er å spore programmet som om hoppet gikk til malens adresse. Hoppmålet finnes bare i den beregnede verdien, og den må dekodes før du vet hvor programtelleren havner.

✏️Eksempel 4: Et beregnet hopp

Hva skriver programmet ut med inndata 2?

00  INP
01  ADD 20
02  STA 03
03  DAT 0
04  HLT
05  LDA 21
06  BRA 12
07  LDA 22
08  BRA 12
09  LDA 23
10  BRA 12
11  HLT
12  OUT
13  HLT
20  DAT 603
21  DAT 11
22  DAT 22
23  DAT 33

Kjennetegnet: STA 03, der celle 03 ligger midt i koden og inneholder DAT 0. Beregnet hopp.

Sporetabell:

PCInstruksjonAkkumulatorCelle 03Ut-kurv
00INP2000
01ADD 20605000
02STA 03605605 (BRA 05)
03BRA 05605605
05LDA 2111605
06BRA 1211605
12OUT1160511
13HLT1160511

Utskrift: 11.
Regnestykket: malen i celle 20 er 603, altså BRA 03. Inndata 2 gir 603 + 2 = 605, altså BRA 05. Programmet hopper til rutinen som starter i celle 05.
De tre rutinene:
InndataBeregnet instruksjonRutineUtskrift
2605 = BRA 05celle 0511
4607 = BRA 07celle 0722
6609 = BRA 09celle 0933

Bare partallene 2, 4 og 6 treffer en rutine — rutinene er to celler lange, så hoppmålene ligger to fra hverandre. Inndata 3 ville gitt BRA 06, og celle 06 inneholder BRA 12 — programmet ville hoppet videre rett til OUT og skrevet ut akkumulatorverdien 606, altså en tullverdi.
Det er poenget med denne konstruksjonen: den er skjør. Et beregnet hopp gir ingen feilmelding hvis adressen bommer — det bare hopper et vilkårlig sted og fortsetter.
📝Oppgave 7
Eksamensnivå, sjanger C

Bruk programmet fra eksempel 4.

a) Hva skriver programmet ut med inndata 6?

b) Hvilken verdi må stå i celle 20 for at inndata 1, 2 og 3 skulle valgt de tre rutinene i stedet?

📝Oppgave 8
Sjanger C

Et program inneholder cellene under. Hvilke av dem vil bli utført som instruksjoner hvis programtelleren starter på 00 og ingen hopp forekommer?

celle:   00   01   02   03   04   05 tall:   901  302  902  000  005  007

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.

Assemblering

Oversettelsen fra assemblerkode med mnemonics til maskinkode med tall: LDA 08 blir 508.

Regelen er mekanisk: opkode ganget med 100, pluss adressen.

Assembleren gjør også en annen jobb — den oversetter eventuelle etiketter til celletall. Etter assemblering finnes verken mnemonics eller navn; bare tresifrede tall.

Disassemblering

Den motsatte veien: fra maskinkode til lesbar assemblerkode. 604 blir BRA 04.

Dette er det du gjør når en eksamensoppgave gir deg minneinnholdet som rene tall og spør hva programmet gjør.

Kontrollen som fanger bommene: kod tilbake igjen. Har du disassemblert 604 til BRA 04, sjekk at 6 · 100 + 4 = 604.

Opkodene 1xx, 2xx, 3xx og 5xx

De fire instruksjonene som arbeider med en minnecelle:

KodeMnemonicVirkning
1xxADDLegg celleinnholdet til akkumulatoren
2xxSUBTrekk celleinnholdet fra akkumulatoren
3xxSTALagre akkumulatoren i cellen
5xxLDALast cellen inn i akkumulatoren

Merk at 4xx er ubrukt i LMC. Den vanligste forvekslingen er 3xx mot 5xx, altså lagre mot laste.

Opkodene 6xx, 7xx og 8xx

De tre hoppene:

KodeMnemonicHopper når
6xxBRAalltid
7xxBRZakkumulatoren er nøyaktig 0
8xxBRPakkumulatoren er 0 eller større

I selvmodifiserende kode er 6xx den viktigste: en DAT-celle med verdien 600 er en hoppmal som kan flyttes ved å legge til et tall.

De faste kodene 901, 902, 922 og 000

De fire instruksjonene uten adressefelt:

KodeMnemonicVirkning
901INPLes inn til akkumulatoren
902OUTSkriv ut som tall
922OTCSkriv ut som ASCII-tegn
000HLTStopp

Her er hele det tresifrede tallet koden. 901 er ikke «opkode 9, adresse 01» — de to siste sifrene skiller bare de tre 9-kodene fra hverandre.

Kodeområde og dataområde

Skillet i praksis: de lave cellenumrene inneholder instruksjonene, de høye inneholder DAT-cellene.

Skillet finnes bare i programmererens hode. Maskinen har ingen markering, og programtelleren kan i prinsippet peke på hvilken som helst celle.

Det er derfor du skal reagere når du ser en STA med lav adresse: den skriver inn i kodeområdet, og da er programmet selvmodifiserende.

Instruksjonsmal

En DAT-celle som inneholder en ferdig instruksjonsverdi, ment å bli modifisert: DAT 130 er malen ADD 30, DAT 600 er malen BRA 00.

Malen ligger i dataområdet og blir aldri utført der. Den lastes, endres og skrives inn i en kodecelle med STA.

Å kjenne igjen en mal er den raskeste måten å oppdage selvmodifiserende kode på: en DAT-verdi som ser ut som en gyldig instruksjon, er sjelden tilfeldig.

Å flytte en adresse

Å legge en liten verdi til en instruksjon, slik at opkoden er uendret mens adressen flyttes: 130 pluss 2 gir 132, altså ADD 30 blir ADD 32.

Dette er LMCs eneste måte å slå opp i en tabell med en beregnet indeks på.

Grensen: legger du til 100 eller mer, ruller du over i opkoden og endrer hva instruksjonen gjør. 130 pluss 100 er 230, altså SUB 30 — sjelden det du ville.

Grenmønsteret med to utganger

Standardformen for et valg i LMC: en test, et betinget hopp til den ene grenen, den andre grenen rett etter, og et BRA som hopper over den første.

02  BRP 06      ; hopp til gren A
03  ...         ; gren B
05  BRA 07      ; hopp over gren A
06  ...         ; gren A
07  OUT         ; felles fortsettelse

Uten BRA på linje 05 ville gren B falt rett ned i gren A. Det er den vanligste feilen når man skriver et slikt mønster selv — og et fast sted å lete når et program oppfører seg rart.

Grenseverdien i en test

Verdien der et betinget hopp akkurat skifter oppførsel. For BRP er det null: null hopper, −1 hopper ikke.

Er testen SUB 61 med celle 61 lik 10, ligger grensen på inndata 10: den gir differansen 0, og BRP hopper.

Eksamensoppgavene plasserer nesten alltid ett alternativ nøyaktig på grensen. Regn den ut eksplisitt før du krysser av.

Informasjonstap

At et program forkaster informasjon underveis, slik at flere ulike inndata gir samme utskrift.

En absoluttverdi glemmer fortegnet; en test på «minst 10» glemmer hvor mye over 10 verdien var.

Dette er grunnen til at baklengs sporing ofte har flere svar. Ser du at programmet mister informasjon, skal du lete etter mer enn én løsning.

Von Neumann-prinsippet i LMC

At kode og data ligger i det samme minnet, i det samme formatet, uten noe skille.

I LMC ser du prinsippet direkte: 508 er et tall som kan legges sammen med andre tall, og samtidig en instruksjon som kan utføres.

Dette er ikke en svakhet ved modellen, men den arkitekturen nesten alle datamaskiner er bygd etter. Du møter den formelt i kap. 2.4.

Rekkefølgekravet ved modifikasjon

Regelen om at en kodecelle må skrives før programtelleren når den, ellers utføres den gamle verdien.

Skrives celle 04 av en STA i celle 02, rekker modifikasjonen fram. Skrives den av en STA i celle 07, er celle 04 for lengst utført med sin opprinnelige verdi.

Dette er det første spørsmålet du skal stille når du ser selvmodifiserende kode: rekker cellen å bli endret i tide? Svaret avgjør om modifikasjonen påvirker denne runden eller først den neste.

Repetisjon — sitter maskinkoden?

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.