Tallsystemer

Fra binært til heksadesimalt – forstå hvordan datamaskiner representerer tall

Per Gustav Ousdal

Introduksjon

Datamaskiner lagrer ikke «123», «A» eller «-7» på samme måte som mennesker skriver dem. De lagrer mønstre av bits. Betydningen kommer fra hvordan vi velger å tolke disse mønstrene.

I dette kurset lærer du både å regne med ulike tallsystemer og å kjenne dem igjen i faktisk programvare og maskinvare.

Forkunnskaper

Kurset starter fra første prinsipper og krever ingen forkunnskaper om binær, heksadesimal, assembler eller digital elektronikk.

Du bør være komfortabel med enkel heltallsregning: addisjon, subtraksjon, multiplikasjon og divisjon. Når vi trenger potenser eller annen notasjon, forklares den før den brukes videre.

Programmerings- og maskinvareeksempler brukes for å vise hvor representasjonene dukker opp i praksis. Du trenger ikke kunne C, Python eller assembler for å starte kurset.

Læringsmål

Etter kurset skal du blant annet kunne:

Første idé: tall er ikke skrivemåten

Tallet tolv kan skrives på flere måter:

Verdien er den samme. Representasjonen er forskjellig.

Dette skillet er grunnlaget for resten av kurset.

Posisjonssystemer og baser

I et posisjonssystem bestemmes verdien til et siffer både av selve sifferet og plasseringen.

Det desimale tallet 347 betyr:

3 × 10² + 4 × 10¹ + 7 × 10⁰.

Det binære tallet 1011₂ betyr:

1 × 2³ + 0 × 2² + 1 × 2¹ + 1 × 2⁰ = 11.

Base

Basen forteller hvor mange forskjellige sifre systemet bruker før en ny posisjon må tas i bruk.

Base Navn Sifre
2 binær 0–1
8 oktal 0–7
10 desimal 0–9
16 heksadesimal 0–9, A–F

I programmering brukes ofte prefikser som 0b1011 for binært og 0x2A for heksadesimalt. Syntaksen varierer mellom språk og assemblere.

PREDICT

Hva betyr 10 i base 2, base 8 og base 16?

STEP

I enhver base b betyr skrivemåten 10 verdien 1 × b¹ + 0 × b⁰.

OBSERVE

Dermed er:

EXPLAIN

Sifferrekkefølgen alene er ikke nok. Du må også vite hvilken base som brukes.

Binært: base 2

Binært bruker bare sifrene 0 og 1. Hver posisjon representerer en potens av to.

PREDICT

Hva tror du bitmønsteret 10110110₂ betyr som et vanlig unsigned heltall? Prøv å summere verdien av posisjonene som inneholder 1.

STEP

Bitposisjon 7 6 5 4 3 2 1 0
Verdi 128 64 32 16 8 4 2 1

For 10110110₂ er bit 7, 5, 4, 2 og 1 satt:

128 + 32 + 16 + 4 + 2 = 182.

Dermed er 10110110₂ = 182₁₀ når mønsteret tolkes som et unsigned heltall.

Bits, nibble og byte

En bit kan ha verdien 0 eller 1. Fire bits kalles ofte en nibble. Åtte bits utgjør normalt en byte.

Med n bits finnes 2ⁿ forskjellige bitmønstre. Åtte bits gir derfor 256 mulige mønstre, fra 00000000 til 11111111.

OBSERVE

For forutsigelsen er 10110110₂ = 128 + 32 + 16 + 4 + 2 = 182₁₀ som unsigned heltall.

256 mulige mønstre betyr ikke at det største representerte tallet alltid er 255. Det gjelder bare når de åtte bitene tolkes som et unsigned heltall.

Det samme mønsteret kan senere tolkes som signed heltall, tegn, deler av en instruksjon, bitfelt eller noe helt annet.

EXPLAIN

Binært beskriver et bitmønster. Representasjonen og datatypen bestemmer hva bitmønsteret betyr. Dette skillet blir viktig gjennom resten av kurset.

Sjekk deg selv

  1. Hva er 10000001₂ som unsigned heltall?
  2. Hvor mange forskjellige mønstre kan lagres med 4 bits?
  3. Hvorfor er det ikke alltid riktig å si at 11111111₂ «er 255»?

Heksadesimalt: base 16

Heksadesimalt passer svært godt til binære datamaskiner fordi ett hex-siffer tilsvarer nøyaktig fire bits.

PREDICT

Se på 1101 0010₂. Kan du dele mønsteret i grupper på fire bit og gjette den heksadesimale skrivemåten?

STEP

Hex Binær Desimal
0 0000 0
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7
8 1000 8
9 1001 9
A 1010 10
B 1011 11
C 1100 12
D 1101 13
E 1110 14
F 1111 15

Gruppen 1101₂ er D₁₆, og 0010₂ er 2₁₆. Derfor:

1101 0010₂ = D2₁₆.

Omvendt kan hvert hex-siffer ekspanderes til fire bits. For eksempel:

3A₁₆ = 0011 1010₂.

OBSERVE

For forutsigelsen gir gruppene 1101₂ = D₁₆ og 0010₂ = 2₁₆, altså 1101 0010₂ = D2₁₆.

Hex endrer ikke verdien. Det er en kompakt skrivemåte for binære bitmønstre, og koblingen er eksakt fordi 16 = 2⁴.

Denne egenskapen er grunnen til at hex brukes så mye i adresser, maskinkode, debugger-visninger, registerverdier, farger, protokoller og filformater.

Vanlig misoppfatning

Bokstavene A–F er ikke «tekst» inne i tallet. De er sifre med verdiene 10–15 i base 16.

EXPLAIN

Heksadesimal er nyttig fordi mennesker kan lese lange binære mønstre mer kompakt uten å miste den direkte koblingen til bitene.

Sjekk deg selv

  1. Skriv 11111111₂ i hex.
  2. Skriv 7C₁₆ i binær.
  3. Hvorfor tilsvarer ett hex-siffer akkurat fire bits?

Oktal: base 8

Oktal bruker sifrene 0–7. Hvert oktalsiffer representerer nøyaktig tre binære bit, og derfor er oktal en praktisk kompakt skrivemåte for binære verdier.

PREDICT

Hva tror du binært 111 101 010 blir i oktal? Prøv å oversette hver gruppe på tre bit før du leser videre.

STEP

Posisjonene i base 8 har vektene 8⁰, 8¹, 8² og så videre.

Eksempel:

572₈ = 5×8² + 7×8¹ + 2×8⁰ = 378₁₀.

Binært kan samme verdi grupperes tre og tre bit:

101 111 010₂ = 572₈.

OBSERVE

For forutsigelsen får vi 111₂ = 7₈, 101₂ = 5₈ og 010₂ = 2₈. Dermed er 111 101 010₂ = 752₈.

Tre bit kan uttrykke verdiene 0–7. Derfor passer én gruppe på tre bit perfekt i ett oktalsiffer.

Oktal dukker fortsatt opp i Unix-lignende systemer. Filmodusen 755 består av tre grupper med rettighetsbit: eier, gruppe og andre.

Vanlig misoppfatning

Tallet 755 i en Unix-filmodus leses normalt som oktal når det brukes som numerisk rettighetsverdi. Det betyr ikke det desimale tallet sju hundre og femtifem.

EXPLAIN

Oktal er ikke et annet slags tall. Det er en annen representasjon av samme verdi. Fordelen i databehandling er den direkte koblingen mellom ett oktalsiffer og tre bit.

Sjekk deg selv

  1. Skriv 111111₂ i oktal.
  2. Skriv 17₈ i binær.
  3. Hvorfor er sifferet 8 ugyldig i et oktalt tall?

Konvertering mellom tallsystemer

Å konvertere betyr å endre representasjon uten å endre verdien.

PREDICT

Er 1010₂, 12₈, 10₁₀ og A₁₆ fire forskjellige tall?

STEP

Fra en vilkårlig base til desimal summerer vi sifferverdi ganger posisjonsvekt:

2D₁₆ = 2×16 + 13 = 45₁₀.

Fra desimal til en annen base kan vi bruke gjentatt heltallsdivisjon og lese restene baklengs.

For 45 til binær:

Dermed 45₁₀ = 101101₂.

Mellom binær og hex grupperer vi fire bit: 0010 1101₂ = 2D₁₆. Mellom binær og oktal grupperer vi tre bit: 101 101₂ = 55₈.

OBSERVE

Nei: 1010₂ = 12₈ = 10₁₀ = A₁₆. De fire skrivemåtene representerer samme verdi i forskjellige baser.

Binær fungerer som en nyttig bro mellom oktal og hex fordi gruppestørrelsene er faste.

EXPLAIN

Metodene er mekaniske, men målet er å kjenne igjen mønstre. Etter hvert bør 1111₂ = F₁₆ = 15₁₀ være like naturlig som enkel hoderegning.

Sjekk deg selv

Konverter 42₁₀ til binær og hex, FF₁₆ til desimal, og 377₈ til binær.

Potenser av to

Datamaskiner er fulle av størrelser som følger 2ⁿ. Å kjenne de vanligste toerpotensene gjør adresser, bitmasker og kapasiteter mye enklere å lese.

Her betyr n ganske enkelt hvor mange ganger 2 inngår som faktor. For eksempel er 2³ = 2 × 2 × 2 = 8.

PREDICT

Hvor mange forskjellige verdier kan åtte bit representere?

STEP

Start med:

n 2ⁿ
0 1
1 2
2 4
3 8
4 16
8 256
10 1024
16 65536
20 1048576
32 4294967296

Med n bit finnes 2ⁿ mulige bitmønstre. Åtte bit gir derfor 256 mønstre, fra 0 til 255 når de tolkes som unsigned.

KiB og kB

1 KiB = 1024 bytes, mens SI-prefikset 1 kB = 1000 bytes. Tilsvarende er MiB og GiB binære prefikser.

Vanlig misoppfatning

2⁸ = 256 betyr at åtte bit gir 256 forskjellige mønstre. Det betyr ikke at den største unsigned verdien er 256. Når vi teller fra 0, blir den største verdien 255.

OBSERVE

Åtte bit kan danne 2⁸ = 256 forskjellige bitmønstre, og dermed representere 256 forskjellige verdier når en bestemt tolkning er valgt.

Hex passer også naturlig: ett hexsiffer er fire bit, to hexsifre er én byte, og fire hexsifre dekker 16 bit.

EXPLAIN

Toerpotenser knytter sammen bitbredde, verdiområde, minnestørrelse og adresserom. Dette blir et gjennomgående verktøy resten av kurset.

Sjekk deg selv

  1. Hvor mange mønstre finnes med 16 bit?
  2. Hva er største unsigned 8-bit-verdi?
  3. Hva er forskjellen mellom 1 MiB og 1 MB?

Unsigned heltall

Et unsigned heltall bruker alle bitene til størrelsen på en ikke-negativ verdi.

PREDICT

Hva er den største verdien som kan lagres i åtte bit?

STEP

Med n bit finnes 2ⁿ mønstre. For unsigned heltall representerer de verdiene

0 ... 2ⁿ - 1.

Dermed er områdene blant annet:

Bredde Område
8 bit 0–255
16 bit 0–65 535
24 bit 0–16 777 215
32 bit 0–4 294 967 295

11111111₂ = FF₁₆ = 255₁₀ som unsigned 8-bit-verdi.

Overflow og wraparound

En fast bitbredde kan ikke representere vilkårlig store verdier. I aritmetikk modulo 2ⁿ vil en verdi som passerer maksimum gå rundt dersom bare de nederste n bitene beholdes.

For eksempel blir 255 + 1 = 0 med 8-bit unsigned wraparound.

Vanlig misoppfatning

Åtte bit gir 256 mulige mønstre, men området er 0–255 fordi null også bruker ett av mønstrene.

OBSERVE

For et unsigned 8-bit heltall er maksimum 2⁸ − 1 = 255, altså 11111111₂ = FF₁₆.

Bitmønsteret endrer ikke betydning av seg selv. FF₁₆ er bare 255 dersom vi vet at mønsteret tolkes som unsigned.

EXPLAIN

Datatypen og bitbredden er en del av betydningen. Dette er avgjørende når du leser registre, protokollfelt, filer eller maskinkode.

Sjekk deg selv

Finn området for 12-bit unsigned og beregn hva 250 + 10 blir med 8-bit wraparound.

Signed heltall

For å representere negative heltall må bitmønstrene få en avtalt signed-tolkning.

PREDICT

Kan bitmønsteret 11111111₂ bety både 255 og −1?

STEP

Historisk finnes flere representasjoner.

Sign-magnitude bruker én bit som fortegn og resten som størrelse. Det gir både +0 og −0.

Enerkomplement inverterer alle bit for å lage den negative verdien. Også dette gir to nullrepresentasjoner.

Toerkomplement er den dominerende representasjonen for signed heltall i moderne datamaskiner. For n bit er området

−2ⁿ⁻¹ ... 2ⁿ⁻¹ − 1.

For åtte bit er området −128 til 127.

Vanlig misoppfatning

Den øverste biten i toerkomplement er ikke bare et separat «minusflagg» som kan fjernes fra resten av tallet. Hele bitmønsteret deltar i representasjonen.

Det er også derfor området ikke er symmetrisk: åtte bit har én flere negativ verdi enn positive verdier.

OBSERVE

Samme rå byte kan tolkes forskjellig:

Ingen av tolkningene ligger «i» bitene alene.

EXPLAIN

Representasjon krever kontekst: bitbredde, signedness og kodingsregel. Debuggere og disassemblere må derfor vite hvilken type de skal vise.

Sjekk deg selv

Hva er området for signed 16-bit toerkomplement? Hvorfor er det én flere negativ verdi enn positive verdier?

Toerkomplement i praksis

Toerkomplement gjør at samme binære addisjonsmaskinvare kan brukes for positive og negative heltall.

PREDICT

Hvordan kan 11111011₂ representere −5?

STEP

For å finne representasjonen av −5 i åtte bit:

  1. skriv +5: 00000101
  2. inverter bitene: 11111010
  3. legg til 1: 11111011

Dermed er FB₁₆ signed 8-bit −5.

En rask matematisk tolkning av et mønster med toppbit 1 er å ta den unsigned verdien og trekke fra 2ⁿ:

251 − 256 = −5.

Addisjon

5 + (-5):

00000101 + 11111011 = 1 00000000

Carry utenfor åtte bit forkastes, og resultatet er null.

Negering og minimumsverdien

Negering gjøres med invertering + 1. Men −128 i signed 8-bit har ingen positiv motpart som passer i samme type; +128 ligger utenfor området. Dette er en viktig edge case.

OBSERVE

For forutsigelsen er 11111011₂ mønsteret for −5 i 8-bit toerkomplement: inverter 00000101₂ til 11111010₂ og legg til 1, som gir 11111011₂.

Carry og signed overflow er ikke det samme. CPU-er kan ha separate flagg fordi unsigned og signed tolkning stiller forskjellige spørsmål til samme bitresultat.

EXPLAIN

Toerkomplement er best forstått som aritmetikk modulo 2ⁿ, kombinert med en signed tolkning av halvparten av bitmønstrene.

Sjekk deg selv

Representer −1, −42 og −128 som 8-bit hex. Tolk 80₁₆ både unsigned og signed.

Binær aritmetikk og CPU-flagg

Binær addisjon følger de samme prinsippene som desimal addisjon, men hvert siffer kan bare være 0 eller 1.

PREDICT

Hva blir 1111₂ + 0001₂ dersom registeret bare er fire bit bredt?

STEP

De grunnleggende addisjonene er 0+0=0, 0+1=1, 1+0=1 og 1+1=10₂. Den siste produserer en carry.

For fire bit:

1111 + 0001 = 1 0000

Hvis bare fire resultatbit beholdes, blir resultatet 0000, mens carry-bitten forteller at unsigned-resultatet ikke passet.

Subtraksjon kan utføres direkte med borrow eller som addisjon av en toerkomplement-negativ verdi.

CPU-flagg

Mange CPU-er registrerer egenskaper ved resultatet. Vanlige flagg er:

Eksempel: signed 8-bit 7F₁₆ + 01₁₆ = 80₁₆. Her er resultatmønsteret −128, men det matematiske resultatet +128 passer ikke. Et overflow-flagg settes på arkitekturer som tilbyr et slikt flagg etter denne operasjonen.

Vanlig misoppfatning

Flaggnavn og nøyaktig oppførsel er ikke identiske på alle CPU-er. Du må lese instruksjonssettets dokumentasjon for å vite hvilke flagg en bestemt instruksjon endrer og hva de betyr der.

Carry og signed overflow er heller ikke det samme: en operasjon kan sette det ene uten å sette det andre.

OBSERVE

Med bare fire bit blir 1111₂ + 0001₂ = 1 0000₂. Registeret beholder de fire lave bitene 0000₂, mens den femte biten blir carry ut.

Carry og overflow tolker samme bitoperasjon ut fra henholdsvis unsigned og signed aritmetikk.

EXPLAIN

CPU-flagg lar etterfølgende instruksjoner ta beslutninger uten å gjenta hele beregningen. De er sentrale for sammenligning, hopp og aritmetikk med flere ord.

Sjekk deg selv

Beregn FF₁₆ + 01₁₆ og diskuter C, Z og V for en 8-bit CPU som bruker de vanlige flaggdefinisjonene over.

Bitoperasjoner

Bitvise operasjoner arbeider på hvert bitpar uavhengig.

PREDICT

Hva blir 1010 AND 1100?

STEP

AND gir 1 bare når begge bit er 1. OR gir 1 når minst én bit er 1. XOR gir 1 når bitene er forskjellige. NOT inverterer hvert bit.

For A=1010₂ og B=1100₂:

Skift

Venstreskift flytter bit mot høyere posisjoner. Innen fast bredde tilsvarer ett logisk venstreskift ofte multiplikasjon med 2 når ingen relevant bit går tapt.

Logisk høyreskift fyller med null. Aritmetisk høyreskift bevarer fortegnsbiten på signed toerkomplement-maskiner.

Rotasjon

Rotate flytter bit rundt endene i stedet for å forkaste dem. Mange CPU-er har også rotate-through-carry, der carry-flagget inngår som en ekstra bit.

OBSERVE

For forutsigelsen sammenlignes bitene parvis: 1010 AND 1100 = 1000.

Alle operasjonene må forstås med en eksplisitt bitbredde. NOT 00000000 er 11111111 i åtte bit, men en annen verdi dersom bredden er 16 bit.

EXPLAIN

Bitoperasjoner er grunnverktøy for registre, protokoller, grafikk, komprimering, kryptografiske primitiver og lavnivåprogrammering.

Sjekk deg selv

Beregn AND, OR og XOR for 3C₁₆ og 0F₁₆.

Bitmasker og bitfelt

En bitmaske velger bestemte bit i en verdi uten å påvirke resten.

PREDICT

Et 8-bit register er 10100100₂. Hvordan setter du bit 0 uten å endre de andre bitene?

STEP

La MASK = 00000001₂.

For flere bit kan masken inneholde flere ettall.

Et felt på bit 4–6 kan isoleres med en maske og deretter skiftes ned til bit 0 før det tolkes som et lite heltall.

Registereksempel

Anta at bit 7 betyr ENABLE, bit 3 IRQ og bit 0 READY. Da kan 10001001₂ leses som tre uavhengige boolske flagg i samme byte.

Vanlig misoppfatning

Når du tester en bit med value AND MASK, er poenget vanligvis å sjekke om resultatet er null eller ikke-null. Resultatet trenger ikke være tallet 1. Tester du for eksempel bit 5, kan et satt resultat være 00100000₂.

NOT MASK må også forstås med en bestemt bitbredde. I et 8-bit register inverteres åtte bit; i en bredere datatype finnes flere bit som også kan bli invertert.

OBSERVE

For å sette bit 0 uten å endre de andre bitene kan vi OR-e med masken 00000001₂: 10100100₂ OR 00000001₂ = 10100101₂.

Hex gjør masker lettere å lese: 11110000₂ = F0₁₆ og 00001111₂ = 0F₁₆.

EXPLAIN

Bitfelt pakker mange små verdier effektivt. Masker lar programmet endre eller lese ett felt uten å ødelegge naboene.

Sjekk deg selv

Lag en maske for bit 2 og 5. Vis hvordan bit 5 kan toggles i 34₁₆.

Minne, adresser og offsets

Et minne kan modelleres som en lang rekke adresserbare byte. En adresse identifiserer en posisjon; en verdi kan bruke én eller flere påfølgende byte.

PREDICT

Hvis en 32-bit verdi starter på adresse 0x1000, hvilke byteadresser opptar den?

STEP

I byte-adresserbart minne øker adressen normalt med én for hver byte:

0x1000, 0x1001, 0x1002, 0x1003.

En 32-bit verdi bruker fire byte og dekker derfor disse fire adressene når den starter på 0x1000.

En offset er en forskyvning relativt til en basisadresse. Hvis base er 0x2000 og offset er 0x34, er adressen 0x2034.

Adresserom

Med n adressebit kan 2ⁿ forskjellige adresser kodes. Hvor mye data dette kan adressere avhenger av hva hver adresse peker på. På et byte-adressert system betyr 16 adressebit opptil 65 536 forskjellige byteadresser.

Alignment

Mange arkitekturer foretrekker eller krever at flerbyteverdier starter på bestemte adressegrenser. En 32-bit verdi kan for eksempel være naturlig aligned på en adresse delelig med 4.

Alignment er en maskinregel eller ytelsesegenskap, ikke en egenskap ved selve tallet.

Vanlig misoppfatning

Et adresserom er ikke det samme som mengden fysisk RAM som faktisk er installert. En CPU kan kunne uttrykke flere adresser enn systemet har fysisk minne for, og deler av adresserommet kan dessuten brukes til enheter, ROM eller andre formål.

OBSERVE

For forutsigelsen opptar 32-bit-verdien fire byte: 0x1000, 0x1001, 0x1002 og 0x1003.

Hex passer svært godt til adresser fordi hvert hexsiffer er fire bit. En 16-bit adresse kan skrives med fire hexsifre fra 0000 til FFFF.

EXPLAIN

Når du leser debugger- eller hex-editorvisninger, må du skille mellom adressen til data og verdien som ligger på adressen.

Sjekk deg selv

Hvilket adresseområde opptar en 16-byte blokk som starter på 0x3FF0? Hva er adressen base 0x8000 + offset 0x2A?

Endianness

Endianness beskriver rekkefølgen byte fra en flerbyteverdi lagres i minnet. Det endrer ikke selve tallets verdi.

PREDICT

Hvordan kan verdien 0x12345678 ligge i fire byte fra adresse 0x1000?

STEP

I big-endian lagres mest signifikante byte først:

Adresse Byte
0x1000 12
0x1001 34
0x1002 56
0x1003 78

I little-endian lagres minst signifikante byte først:

Adresse Byte
0x1000 78
0x1001 56
0x1002 34
0x1003 12

Viktig skille

Bitrekkefølgen inne i den vanlige skriftlige byteverdien snus ikke. Endianness handler her om rekkefølgen på byte i en flerbyteverdi.

Motorola 68000-familien forbindes med big-endian, mens x86 bruker little-endian. Protokoller kan definere sin egen byteorden uavhengig av CPU-en.

Memory dump

Ser du bytefølgen 78 56 34 12, kan den være 32-bitverdien 0x12345678 dersom formatet sier little-endian. Uten type, bredde og byteorden er rå byte tvetydige.

OBSERVE

For forutsigelsen kan 0x12345678 ligge som 12 34 56 78 i big-endian eller 78 56 34 12 i little-endian fra den laveste adressen. Byteorden er en del av representasjonen.

En hex-dump viser byte i stigende minneadresse, ikke nødvendigvis sifrene i den rekkefølgen vi skriver den numeriske verdien.

EXPLAIN

Endianness blir viktig når data flyttes mellom CPU-er, nettverk, binærfiler, emulatorer og debuggerverktøy.

Sjekk deg selv

Skriv 0xA1B2C3D4 som fire byte i big- og little-endian.

BCD – Binary-Coded Decimal

BCD lagrer desimalsifre som binære koder i stedet for å representere hele tallet som ett binært heltall.

PREDICT

Hvordan kan desimaltallet 42 lagres dersom hvert desimalsiffer får fire bit?

STEP

I packed BCD bruker hvert desimalsiffer én nibble:

42₁₀ -> 0100 0010₂ -> 0x42

Dette er ikke det samme som vanlig binær representasjon av 42, som er 00101010₂ = 0x2A.

Bare nibbleverdiene 0–9 er gyldige desimalsifre; A–F brukes normalt ikke som BCD-sifre.

I unpacked BCD bruker hvert siffer typisk en hel byte, mens packed BCD legger to sifre i én byte.

Hvorfor BCD?

BCD har vært viktig i kalkulatorer, klokker, tellere, økonomisystemer og eldre maskiner fordi desimalsifre kan bevares direkte og vises uten binær/desimal avrundingsproblematikk.

Vanlig misoppfatning

0x42 betyr ikke automatisk desimalt 42. Som et vanlig binært heltall er 0x42 = 66₁₀; bare når formatet sier packed BCD tolkes de to nibblene som desimalsifrene 4 og 2.

OBSERVE

Med fire bit per desimalsiffer blir 4 kodet som 0100 og 2 som 0010. Packed BCD for 42 blir derfor 0100 0010₂ = 0x42.

Hexdumpen 42 kan bety vanlig binær verdi 66 eller packed BCD-verdi 42. Formatkontekst avgjør.

EXPLAIN

BCD demonstrerer igjen hovedideen i kurset: samme bitmønster kan ha forskjellige betydninger under forskjellige representasjonsregler.

Sjekk deg selv

Skriv 1987 som packed BCD. Hvor mange byte kreves? Hvorfor kan ikke en hex-editor alene fortelle deg om byten 42 er binær 66 eller BCD 42?

Fixed-point og Q-format

Fixed-point representerer brøktall med en implisitt fast plassering av binærpunktet.

PREDICT

Hvis et 8-bit mønster har fire heltallsbit og fire brøkbit, hvilken verdi har 00111000₂?

STEP

I et unsigned Q4.4-format (fire heltallsbit og fire brøkbit) har de fire nederste bitene vektene 2⁻¹, 2⁻², 2⁻³ og 2⁻⁴.

0011.1000₂ = 3 + 1/2 = 3.5.

Det lagrede heltallet er 56. Skalafaktoren er 2⁴ = 16, så den virkelige verdien er 56 / 16 = 3.5.

Med f brøkbit er oppløsningen 1 / 2^f.

Signed fixed-point

Toerkomplement kan kombineres med et fast binærpunkt. Bitmønsteret tolkes først som et signed heltall og skaleres deretter med 1 / 2^f.

Fordeler og begrensninger

Fixed-point gir forutsigbar presisjon og kan være effektivt på mikrokontrollere uten rask floating-point-maskinvare. Til gjengjeld må programmereren holde kontroll på skalering, range og overflow.

OBSERVE

Med fire brøkbit ligger binærpunktet etter de fire øverste bitene: 0011.1000₂ = 3 + 1/2 = 3.5.

Binærpunktet lagres ikke som et eget symbol. Formatdefinisjonen forteller hvor det ligger.

EXPLAIN

Fixed-point er egentlig heltallsaritmetikk med en avtalt skala. Det gjør forbindelsen til bitbredde, toerkomplement og overflow svært tydelig.

Sjekk deg selv

Hva er oppløsningen med åtte brøkbit? Tolk heltallsverdien 384 med åtte brøkbit.

Floating-point og IEEE 754

Floating-point lar binærpunktet «flyte» ved å lagre en signifikand sammen med en eksponent.

PREDICT

Hvorfor kan et svært stort og et svært lite tall begge representeres i samme 32-bit format?

STEP

IEEE 754 binary32 bruker 32 bit fordelt på:

For normale verdier representerer feltet i prinsippet

(-1)^sign × significand × 2^exponent.

Eksponenten lagres med bias, og den ledende 1-biten i normale binære verdier er implisitt.

Standarden definerer også spesialverdier som +0, −0, positive/negative infinity, NaN og subnormale tall.

Regnet eksempel: 1.0 = 0x3F800000

Binary32-mønsteret 0x3F800000 er:

0 01111111 00000000000000000000000

Dermed blir verdien (+1) × 1.0₂ × 2⁰ = 1.0.

Fra desimal brøk til binær brøk

For å konvertere en brøk kan vi gange brøkdelen med 2 gjentatte ganger og lese av heltallsdelen:

For 0.1₁₀ stopper prosessen ikke: bitmønsteret fortsetter periodisk. Derfor finnes ingen endelig binær brøk som er nøyaktig lik en tidel.

Hvorfor 0.1 + 0.2?

Brøken 1/10 har ingen endelig binær brøkrepresentasjon, på samme måte som 1/3 ikke har en endelig desimalrepresentasjon. Verdien må derfor avrundes til nærmeste representerbare floating-point-verdi.

Når flere slike avrundede verdier brukes i aritmetikk, kan resultatet avvike litt fra det matematiske desimaltallet. I vanlig binary64-aritmetikk blir for eksempel 0.1 + 0.2 typisk representert som omtrent 0.30000000000000004, ikke nøyaktig 0.3.

Presisjon og range

Flere eksponentbit gir stor range; flere signifikandbit gir større presisjon. Floating-point gir derfor et kompromiss, ikke «vilkårlig presise reelle tall».

OBSERVE

0x3F800000 viser konkret at sign-, eksponent- og fraction-feltene sammen gir verdien 1.0. Eksemplet med 0.1 viser samtidig hvorfor ikke alle desimalbrøker kan lagres nøyaktig.

Et floating-point-bitmønster kan ikke tolkes som et vanlig heltall og forventes å beholde samme numeriske verdi. Feltstrukturen definerer representasjonen.

EXPLAIN

IEEE 754 gjør floating-point portabelt og veldefinert, men programmer må fortsatt forstå avrunding, spesialverdier og sammenligning.

Sjekk deg selv

Hvorfor er testing av floating-point-likhet ofte mer komplisert enn testing av heltallslikhet?

Andre representasjoner

Bitmønstre brukes til langt mer enn vanlige heltall. Dette kapitlet viser fire viktige eksempler: Gray-kode, biased/excess-notasjon, tegnkoder og packed data.

PREDICT

Må bitmønsteret 01000001 alltid bety tallet 65?

STEP — Gray-kode

I binær-reflektert Gray-kode skiller naboverdier seg med nøyaktig én bit. Dette er nyttig når flere bit ikke bør endre seg samtidig, for eksempel i enkelte posisjonssensorer og enkodere.

De første 3-bit Gray-kodene er:

Desimal Binær Gray
0 000 000
1 001 001
2 010 011
3 011 010
4 100 110

Fra binær verdi b kan Gray-koden beregnes som g = b XOR (b >> 1).

STEP — biased/excess

En eksponent kan lagres som et unsigned felt med en fast bias. Hvis bias er 127, representerer lagret felt 130 eksponenten (130-127=3).

Dette er prinsippet som brukes for eksponentfeltet i IEEE 754 binary32, selv om spesielle feltverdier har egne betydninger.

STEP — tegn som tall

ASCII gir bokstaven A kodeverdien 65, altså 0x41. Unicode definerer code points, for eksempel U+0041 for A. En encoding som UTF-8 bestemmer deretter hvilke byte som faktisk lagres.

Code point og byteencoding er derfor ikke det samme.

STEP — packed data

En byte kan deles i bitfelt. Et tenkt statusregister kan bruke:

Da er byten ikke «ett tall» i vanlig forstand, men flere felt pakket sammen.

OBSERVE

Nei. 01000001₂ = 0x41 er 65 som unsigned heltall, men samme mønster kan for eksempel representere ASCII-tegnet A.

0x41 kan tolkes som unsigned 65, signed 65, ASCII-tegnet A, deler av en instruksjon eller et felt i en struktur. Konteksten bestemmer betydningen.

EXPLAIN

Datamaskinen lagrer bit. Typer, protokoller, instruksjonssett og filformater gir bitene semantikk.

Sjekk deg selv

Beregn Gray-koden for binær 1010. Hva er den virkelige eksponenten når et excess-127-felt inneholder 124?

Tall i assembly

Assembly gjør skillet mellom verdi, syntaks, bitbredde og maskinbetydning svært synlig. Samme tall kan være en immediate konstant, adresse, maske, offset eller del av en maskininstruksjon.

PREDICT

Betyr $10, #10, 0x10 og 10h alltid det samme?

STEP — literals og syntaks

Assemblerdialekter bruker forskjellige notasjoner. Vanlige eksempler er:

Syntaksen må alltid leses i kontekst av den aktuelle assembleren.

Immediate eller adresse?

Et tall i en instruksjon kan beskrive selve verdien eller stedet verdien skal hentes fra.

6502-lignende eksempel

LDA #$2A betyr typisk last den umiddelbare verdien 0x2A.

LDA $2A betyr derimot les fra en minneadresse etter assemblerens og instruksjonens adresseringsregler.

Motorola 68000

MOVE.B #$2A,D0 bruker #$2A som immediate verdi.

MOVE.B $002A,D0 refererer til minne.

Suffix som .B, .W og .L gjør også datastørrelsen eksplisitt i mange 68k-assemblerdialekter.

AVR

AVR har egne registre og instruksjonsbegrensninger. En konstant kan for eksempel lastes med:

LDI R16, 0x2A

Tallene i kildekoden må fortsatt passe operandens tillatte range og instruksjonsencoding.

x86

x86 har flere assemblerdialekter. Intel- og AT&T-syntaks kan uttrykke samme maskininstruksjon forskjellig. Derfor er det farlig å lære én tekstlig form som om den var «assembly-syntaksen».

Registre og bredde

Et register har en definert bredde. Skriver du et større tall enn feltet eller instruksjonen kan representere, må assembleren enten avvise det, redusere det etter definerte regler eller velge en annen encoding.

Bitbredde bestemmer også hvordan samme bitmønster kan tolkes signed eller unsigned.

Masker

Assembly bruker ofte tall som bitmasker:

AND #$0F

kan brukes til å beholde de fire laveste bitene i en byte, avhengig av ISA og syntaks.

Disassembly

En disassembler starter med rå byte og forsøker å tolke dem som instruksjoner for en bestemt ISA og startadresse. En byte som er data i ett område kan derfor bli vist som en tilsynelatende instruksjon dersom verktøyet får feil kontekst.

OBSERVE

Nei. Prefikser og suffikser er assembler-syntaks, og betydningen av blant annet `# Tall i assembly

Assembly gjør skillet mellom verdi, syntaks, bitbredde og maskinbetydning svært synlig. Samme tall kan være en immediate konstant, adresse, maske, offset eller del av en maskininstruksjon.

PREDICT

Betyr $10, #10, 0x10 og 10h alltid det samme?

STEP — literals og syntaks

Assemblerdialekter bruker forskjellige notasjoner. Vanlige eksempler er:

Syntaksen må alltid leses i kontekst av den aktuelle assembleren.

Immediate eller adresse?

Et tall i en instruksjon kan beskrive selve verdien eller stedet verdien skal hentes fra.

6502-lignende eksempel

LDA #$2A betyr typisk last den umiddelbare verdien 0x2A.

LDA $2A betyr derimot les fra en minneadresse etter assemblerens og instruksjonens adresseringsregler.

Motorola 68000

MOVE.B #$2A,D0 bruker #$2A som immediate verdi.

MOVE.B $002A,D0 refererer til minne.

Suffix som .B, .W og .L gjør også datastørrelsen eksplisitt i mange 68k-assemblerdialekter.

AVR

AVR har egne registre og instruksjonsbegrensninger. En konstant kan for eksempel lastes med:

LDI R16, 0x2A

Tallene i kildekoden må fortsatt passe operandens tillatte range og instruksjonsencoding.

x86

x86 har flere assemblerdialekter. Intel- og AT&T-syntaks kan uttrykke samme maskininstruksjon forskjellig. Derfor er det farlig å lære én tekstlig form som om den var «assembly-syntaksen».

Registre og bredde

Et register har en definert bredde. Skriver du et større tall enn feltet eller instruksjonen kan representere, må assembleren enten avvise det, redusere det etter definerte regler eller velge en annen encoding.

Bitbredde bestemmer også hvordan samme bitmønster kan tolkes signed eller unsigned.

Masker

Assembly bruker ofte tall som bitmasker:

AND #$0F

kan brukes til å beholde de fire laveste bitene i en byte, avhengig av ISA og syntaks.

Disassembly

En disassembler starter med rå byte og forsøker å tolke dem som instruksjoner for en bestemt ISA og startadresse. En byte som er data i ett område kan derfor bli vist som en tilsynelatende instruksjon dersom verktøyet får feil kontekst.

, #, 0x og h avhenger av språket og konteksten. Du må lese syntaksreglene for den aktuelle assembleren.

Assemblerkilden inneholder menneskevennlige symboler og tallnotasjoner. CPU-en ser den ferdig kodede instruksjonens bitfelt.

EXPLAIN

Tallforståelse er grunnleggende for assembly fordi operander, adresser, registre, masker, offsets og instruksjonsencoding alle er begrensede bitfelt.

Sjekk deg selv

Hvorfor er #$2A og $2A forskjellige i mange klassiske assemblere? Hvorfor må du vite assemblerdialekten før du tolker 10h?

Tall i C og Python

C og Python kan uttrykke de samme binære og heksadesimale verdiene, men heltallsmodellene deres er svært forskjellige.

PREDICT

Vil 255 + 1 alltid gi samme resultat i C og Python?

STEP — literals

Heksadesimale og desimale literaler fungerer likt i begge språk. Binære literaler som 0b101010 støttes i Python og fra C23 i C (mange kompilatorer, blant annet GCC og Clang, har lenge støttet dem som utvidelse):

0b101010
0x2A
42

I C bestemmes typen også av språkets type- og literalregler. I Python er int ikke begrenset til en fast 8-, 16-, 32- eller 64-bit bredde.

C: eksplisitte bredder

Når maskinbredde betyr noe, er typene fra <stdint.h> nyttige:

#include <stdint.h>

uint8_t  a = 0xFF;
uint16_t b = 0x1234;
int32_t  c = -42;

Eksakt-bredde-typene finnes når implementasjonen tilbyr en heltallstype med den aktuelle bredden.

Unsigned C-aritmetikk følger modulo 2ⁿ for typens bredde. Signed overflow må ikke behandles som om C lover samme wraparound; signed integer overflow er ikke definert på samme måte av språket.

Python: vilkårlig store heltall

x = 255
print(x + 1)   # 256

Python-int vokser etter behov innen praktiske ressursgrenser. Hvis du vil simulere et 8-bit register, må du selv innføre bredden, for eksempel:

x = (255 + 1) & 0xFF

Da blir resultatet 0.

Skift og masker

value & 0x0F
value | 0x80
value ^ 0x01
value << 1
value >> 1

Betydningen av høyreskift for negative verdier og effekten av promotions/typebredder må vurderes etter språket og typen. Ikke flytt maskinantakelser ukritisk mellom språk.

Parsing

Python:

int("101010", 2)
int("2A", 16)
int("0x2A", 0)

C bruker blant annet funksjoner som strtoul, der base og feilhåndtering må behandles eksplisitt.

Formatering

Python:

f"{42:b}"      # 101010
f"{42:02X}"    # 2A
f"{42:08b}"    # 00101010

I C brukes formatmakroer fra <inttypes.h> når portabel formatering av fixed-width heltall er viktig.

OBSERVE

Nei. Resultatet avhenger av type og språkregler. Python-heltall vokser normalt etter behov, mens aritmetikk i en begrenset C-heltallstype kan ha andre regler. Derfor må både type og språksemantikk være kjent.

Kildekoden 0xFF beskriver en numerisk literal. Om du senere tolker eller lagrer den som 8-bit signed, 8-bit unsigned eller en større type er en egen beslutning.

EXPLAIN

Programmeringsspråk legger regler oppå bitrepresentasjonene. God lavnivåkode krever at du forstår både maskinmodellen og språkmodellen.

Sjekk deg selv

Hvorfor gir (255 + 1) & 0xFF verdien 0 i Python? Hvorfor bør du ikke anta at signed C-overflow fungerer på samme måte?

Tall i digital elektronikk

I digital elektronikk kobles bitmønstre til fysiske signaler, registre, målinger og protokoller. Tallene er ikke bare matematikk: de styrer pinner og beskriver virkelige måleverdier.

PREDICT

Hvis et 8-bit GPIO-register inneholder 0x81, hvilke bit er satt?

STEP — logisk og fysisk nivå

En logisk 0 eller 1 er en abstraksjon. Den fysiske kretsen bruker spenningsområder som tolkes som LOW og HIGH. Nøyaktige terskler avhenger av komponent og datablad.

Et signal kan også være active-low. Da betyr det aktive nivået logisk/funksjonelt «på» når den elektriske linjen er LOW. Navn som /RESET, RESET_n eller lignende brukes ofte, men konvensjonen varierer.

GPIO-register

Tenk et register:

PORT = 1010 0001₂ = 0xA1

Hver bit kan styre en separat funksjon. En maske kan endre én bit uten å uttrykke hele registerverdien på nytt.

Sett bit 3:

PORT |= (1 << 3)

Nullstill bit 3:

PORT &= ~(1 << 3)

Toggle bit 3:

PORT ^= (1 << 3)

På virkelig hardware må du lese databladet: enkelte registre har egne SET/CLEAR-registre, write-one-to-clear-flagg eller andre regler som gjør en vanlig read-modify-write feil.

Datablad og registerkart

Et registerkart oppgir typisk:

En verdi som 0x82 er først nyttig når vi vet hvilket register den tilhører og hva hvert bitfelt betyr.

ADC

En idealisert N-bit ADC har 2^N digitale koder. En 10-bit ADC har 1024 koder, normalt 0–1023.

For en enkel idealisert unipolar modell kan en kode omregnes omtrent som:

V ≈ code / (2^N - 1) × Vref

Den nøyaktige overføringsfunksjonen, referansen, toleranser og endpoint-definisjonen må hentes fra databladet. Formelen over er derfor en læringsmodell, ikke en universell ADC-lov.

DAC

En DAC gjør motsatt retning: en digital kode påvirker en analog utgang. Også her bestemmer bitbredde antall koder og dermed den idealiserte kvantiseringen.

Logic analyzer

En logic analyzer samler digitale samples. Verktøyet kan vise dem som:

Byte 10100101₂ = 0xA5 kan være kommando, adresse, flagg eller data. Protokollen gir semantikken.

Ved serielle protokoller må du også kjenne blant annet bitrekkefølge, klokking, framing og byteorden der flerbytefelt brukes.

OBSERVE

For forutsigelsen er 0x81 = 1000 0001₂, så bit 7 og bit 0 er satt.

Hardware-registre og protokoller er praktiske eksempler på hele kursets hovedidé: bitmønster + avtalt representasjon = mening.

EXPLAIN

Når du leser et datablad, oversetter du kontinuerlig mellom bitposisjoner, masker, hex, desimale størrelser og fysisk funksjon.

Sjekk deg selv

Hvilke bit er satt i 0x81? Hvor mange koder har en 12-bit ADC? Hvorfor bør du ikke anta at alle statusflagg kan nullstilles med vanlig read-modify-write? Hvorfor må en ADC-formel alltid kontrolleres mot databladet?

Tall i nettverk og filformater

Nettverkspakker og binære filer består av byte. For å forstå dem må vi vite hvilke byte som hører sammen, hvilken byteorden som brukes, og hva hvert felt betyr.

PREDICT

Bytefølgen 12 34 representerer et 16-bit tall. Er verdien 0x1234 eller 0x3412? Skriv hypotesen din før du går videre.

STEP — nettverksbyteorden

Mange Internett-protokollfelt med flere byte bruker network byte order, som er big-endian.

Et 16-bit felt med verdien 0x1234 ligger da som:

12 34

Dette betyr ikke at maskinen som sender eller mottar pakken selv må være big-endian. Protokollens representasjon og CPU-ens interne representasjon er separate spørsmål.

IPv4

En IPv4-adresse er 32 bit og vises vanligvis som fire desimale oktetter.

192.168.1.10

tilsvarer byte:

C0 A8 01 0A

Punktnotasjonen er menneskevennlig; pakken inneholder byte.

MAC-adresser

En vanlig 48-bit MAC-adresse skrives ofte som seks hex-byte:

02:12:34:56:78:9A

Her er hex spesielt nyttig fordi hvert byte kan skrives med nøyaktig to hex-sifre.

Protokollfelt

En pakke kan inneholde:

Noen felt er bitfelt. Andre er heltall over flere byte. Dokumentasjonen bestemmer bredde, byteorden og semantikk.

Magic numbers

Binære filformater starter ofte med karakteristiske byte som hjelper programvare å identifisere formatet.

For eksempel kan en fil begynne:

89 50 4E 47 0D 0A 1A 0A

Dette er den åtte byte lange signaturen i starten av en PNG-fil. En signatur er nyttig identifikasjon, men alene beviser den ikke at resten av filen er gyldig.

Fra hex-dump til struktur

Tenk denne fiktive posten:

01 03 12 34 41 42 43

Formatet sier:

Da får vi:

Uten formatbeskrivelsen er dette bare sju byte.

Lengde og offsets

Binære parsere arbeider ofte med offsets:

offset + field_length

Et felt som starter ved offset 4 og er 2 byte langt bruker byte 4 og 5. Neste felt starter ved offset 6.

Feil bredde eller feil offset forskyver resten av tolkningen.

OBSERVE

For forutsigelsen finnes det ikke ett riktig svar uten byteorden: big-endian gir 0x1234, mens little-endian gir 0x3412.

Samme byte kan representere del av en IP-adresse, et tegn, et flaggfelt, et heltall eller rå payload.

EXPLAIN

Hex-dumpen viser lagrede eller overførte byte. Format- eller protokollspesifikasjonen forteller hvordan byte skal grupperes og tolkes.

Sjekk deg selv

Hvordan lagres 0xBEEF som et big-endian 16-bit felt? Hvilke byte representerer IPv4-adressen 127.0.0.1? Hvorfor er en magic number ikke nok til å validere en hel fil?

Tall i debugging og reverse engineering

Når et program krasjer, en fil er ukjent eller et gammelt system mangler dokumentasjon, møter vi ofte rå byte før vi kjenner strukturen. Tallrepresentasjon blir da et analyseverktøy.

PREDICT

Du finner bytene:

48 65 6C 6C 6F 00

Er dette seks heltall, maskinkode eller tekst?

STEP — start med observasjon

Skill mellom det du ser og det du antar.

En hex-editor eller memory dump kan vise:

At en tekstkolonne viser lesbare tegn er et spor, ikke et bevis på at feltet faktisk er tekst.

Gjenkjenn mønstre

Nyttige observasjoner kan være:

Hypoteser bør testes mot flere eksempler.

Heltall i en dump

Bytene

34 12

kan være 0x3412 som big-endian eller 0x1234 som little-endian.

Hvis en hypotese sier at feltet er en lengde, kan vi kontrollere om verdien passer med resten av datastrukturen.

Adresser og offsets

Debuggere viser ofte adresser i hex. Forskjellen mellom to adresser er en størrelse:

0x1040 - 0x1000 = 0x40 = 64

Dette er nyttig når vi undersøker buffere, tabeller og strukturer.

Disassembly

En disassembler tolker byte som instruksjoner for en bestemt CPU og et bestemt startpunkt.

Det betyr ikke automatisk at alle viste byte faktisk er kode. Data som tolkes som instruksjoner kan produsere syntaktisk gyldig, men meningsløs disassembly.

Spør derfor:

Kode og data

I eldre og innebygde systemer kan kode, tabeller, strenger og konstanter ligge tett sammen.

En sekvens som 41 42 43 44 kan være teksten ABCD, fire små heltall, deler av større tall eller instruksjonsbyte. Kontekst avgjør.

Rekonstruer en mulig struktur

Tenk denne fiktive dumpen:

03 00 08 00 41 42 43 00

En hypotese kan være:

Dette er foreløpig bare en modell. Vi styrker den ved å undersøke flere poster og se om feltene oppfører seg konsekvent.

Endre én ting

En effektiv metode i egne systemer og testdata er å endre én kjent verdi om gangen og sammenligne før/etter.

Hvis et flagg slås på og nøyaktig én bit endres, har vi et godt spor. Hvis en teller økes og et bestemt felt følger den, lærer vi mer om representasjonen.

OBSERVE

For forutsigelsen er flere tolkninger mulige uten kontekst. Som ASCII/UTF-8 gir 48 65 6C 6C 6F 00 teksten Hello etterfulgt av en nullbyte; det er en sterk hypotese, ikke et bevis alene.

Reverse engineering av data handler ofte mindre om å «gjette riktig» med én gang og mer om å formulere hypoteser som kan motbevises eller styrkes.

EXPLAIN

Kunnskap om baser, signedness, byteorden, bitfelt, adresser og tekstkoding gjør rå byte lesbare. Men kontekst og flere observasjoner er nødvendig for å skille en plausibel tolkning fra en dokumentert struktur.

Sjekk deg selv

Hvorfor kan disassembly av data se ut som gyldig kode? Hva kan 00 01 bety under ulike byteordener? Hvorfor er flere eksempler bedre enn én dump når vi prøver å finne en struktur?

Historiske og uvanlige tallsystemer

Binær, oktal, desimal og heksadesimal dominerer moderne datateknikk, men et posisjonssystem kan i prinsippet bruke mange andre baser. Andre valg viser hvorfor en base er en representasjonsregel, ikke en egenskap ved selve tallet.

PREDICT

Hva tror du 10 betyr i base 3? Skriv svaret før du går videre.

STEP — base 3

Base 3 bruker sifrene 0, 1 og 2.

102₃ = 1 × 9 + 0 × 3 + 2 = 11₁₀

Ternære systemer er interessante fordi de viser at digital representasjon ikke matematisk krever akkurat to symboler. Det har også eksistert forskning og maskiner basert på ternær logikk.

Base 12

Base 12 har tolv sifferverdier per posisjon. I dette kurset bruker vi A for verdien ti og B for verdien elleve, i tillegg til 0–9. Dermed er for eksempel A₁₂ = 10₁₀ og B₁₂ = 11₁₀.

Tolv har mange delere: 2, 3, 4 og 6. Det gjør enkelte brøker kompakte.

I base 12 er for eksempel en halv:

0.6₁₂

fordi 6/12 = 1/2.

Base 20

Vigesimale systemer bruker base 20 og finnes historisk i flere språk og kulturer.

Poenget for oss er representasjon: når basen er 20, betyr 10₂₀ tjue, ikke ti.

Base 36

Base 36 er praktisk i tekst fordi sifrene 0–9 og bokstavene A–Z kan representere 36 sifferverdier.

Da er:

Z₃₆ = 35₁₀

og:

10₃₆ = 36₁₀

Base 36 kan gi kompakte tekstlige representasjoner av ikke-negative heltall, men store/små bokstaver og alfabet må defineres av formatet.

Base 60

Seksagesimale systemer har svært gamle historiske røtter. Spor av base 60 er fortsatt synlige i tids- og vinkelmåling:

Dette er ikke et rent moderne posisjonssystem i alle disse bruksområdene, men viser hvordan en valgt oppdeling kan leve videre svært lenge.

Hvorfor divisibilitet betyr noe

Basens faktorer påvirker hvilke brøker som får endelige representasjoner.

I base 10 er 1/2 = 0.5 og 1/5 = 0.2 endelige, mens 1/3 repeterer.

I base 2 er 1/2 endelig, mens 1/10₁₀ ikke har en endelig binær brøk. Dette knytter direkte tilbake til flyttallskapitlet.

Base er ikke bitbredde

Base og lagringsbredde er forskjellige konsepter.

Et tall kan skrives i base 36 som tekst og likevel lagres internt som et vanlig binært heltall. Omvendt kan en binær byte vises som desimal, hex eller base 36 uten at byteverdien endres.

Historiske maskiner

Datamaskinhistorien inneholder andre representasjoner enn dagens vanligste binære mønstre. Noen maskiner brukte desimalorientert aritmetikk eller andre ordstørrelser og tegnkodinger enn vi forventer i dag.

Derfor bør historiske data alltid leses ut fra den aktuelle maskinens dokumenterte representasjon, ikke moderne antakelser.

OBSERVE

For forutsigelsen betyr 10₃ tre: 1 × 3¹ + 0 × 3⁰ = 3.

10 kan representere 2, 3, 8, 10, 12, 16, 20, 36, 60 eller en annen verdi avhengig av basen.

EXPLAIN

Et posisjonssystem er en avtale om siffer, posisjonsvekter og base. Tallet er den abstrakte verdien; notasjonen er representasjonen.

Sjekk deg selv

Hva er 10₁₂ i desimal? Hva er Z₃₆? Hvorfor kan base 12 representere enkelte vanlige brøker mer kompakt enn base 10?

Sluttprosjekt: analyser en ukjent bytebuffer

Dette prosjektet samler hele EduNumbers. Du skal ikke bare konvertere tall; du skal bruke representasjon, byteorden, signedness, bitfelt, tekst og struktur for å forklare en ukjent buffer.

Oppgaven

Du får denne syntetiske hex-dumpen:

0000: 45 4E 55 4D 01 A5 10 00 34 12 FE FF 50 4C 4F 4F
0010: 53 00 00 00 78 56 34 12

Du får bare tre sikre opplysninger:

  1. formatet er laget for denne oppgaven
  2. alle felt begynner på hele bytegrenser
  3. bufferen er komplett

Resten skal utledes og begrunnes.

PREDICT

Se på dumpen uten å regne først.

Hvilke områder ser ut som tekst? Hvilke byte kan være flagg? Ser du verdier som kan være little-endian heltall?

Skriv hypotesene før du leser videre.

STEP 1 — offsets

Dumpen har 24 byte, med offsets 0x00 til 0x17.

Marker hvert byte med offset. Dette gjør senere hypoteser presise.

STEP 2 — signatur

De første fire bytene er:

45 4E 55 4D

Som ASCII blir dette ENUM.

Det er en sterk kandidat til en magic/signatur, men merk ordet kandidat: mønsteret alene er ikke matematisk bevis.

STEP 3 — små felt

Ved offset 0x04 finner vi 01. Det kan passe som en versjon.

Ved 0x05 finner vi A5:

1010 0101₂

Hvis dette er et flaggfelt, er bit 7, 5, 2 og 0 satt.

STEP 4 — lengde

Offset 0x06–0x07 inneholder:

10 00

Som little-endian 16-bit blir dette 0x0010 = 16.

Som big-endian blir det 0x1000 = 4096.

Siden bufferen bare er 24 byte, kan 16 være en plausibel lengde for alle 16 byte etter den åtte byte lange headeren — men plausibilitet er fortsatt ikke det samme som dokumentasjon.

STEP 5 — heltall og signedness

Offset 0x08–0x09:

34 12

Little-endian gir 0x1234 = 4660.

Offset 0x0A–0x0B:

FE FF

Little-endian unsigned gir 65534. Tolket som signed 16-bit toerkomplement blir samme bitmønster −2.

Dette demonstrerer hvorfor signedness må være del av formatbeskrivelsen.

STEP 6 — tekst

Offset 0x0C–0x13 er:

50 4C 4F 4F 53 00 00 00

ASCII gir PLOOS etterfulgt av tre nullbyte. En mulig modell er et fast 8-byte tekstfelt med null-padding.

STEP 7 — 32-bit verdi

De siste fire bytene:

78 56 34 12

Little-endian gir:

0x12345678

Nå har vi flere felt som passer samme byteorden. Det styrker hypotesen om little-endian format.

En mulig struktur

En konsistent modell er:

Offset Størrelse Mulig felt Tolkning
0x00 4 magic ASCII ENUM
0x04 1 version 1
0x05 1 flags 0xA5
0x06 2 length 16, little-endian
0x08 2 id 0x1234
0x0A 2 delta −2 signed
0x0C 8 name PLOOS, null-padded
0x14 4 value 0x12345678

Denne strukturen er fasiten for den syntetiske oppgaven. I ekte reverse engineering ville vi krevd flere observasjoner eller dokumentasjon før vi kalte modellen sikker.

STEP 8 — verifiser med kode

I Python kan deler av bufferen undersøkes eksplisitt:

data = bytes.fromhex(
    "45 4E 55 4D 01 A5 10 00 34 12 FE FF "
    "50 4C 4F 4F 53 00 00 00 78 56 34 12"
)

length = int.from_bytes(data[6:8], "little")
ident = int.from_bytes(data[8:10], "little")
delta = int.from_bytes(data[10:12], "little", signed=True)
value = int.from_bytes(data[20:24], "little")

print(length, ident, delta, hex(value))

Forventet resultat er 16 4660 −2 0x12345678.

OBSERVE

Ingen av de viktigste bytefølgene endret seg under analysen. Det som endret seg var tolkningen vår.

EXPLAIN

Kursets hovedidé er nettopp dette: tall og byte er verdier og bitmønstre; base, signedness, byteorden, tekstkoding og feltstruktur er representasjonsregler som gir mønstrene mening.

Leveranse

Lag en kort analyserapport som inneholder:

Når du kan forklare hvorfor hver tolkning er rimelig, har du brukt hele EduNumbers som et verktøy.