Maskinkode er et computerprogram skrevet i maskinsprog, det vil sige den binære kode, en processor direkte kan forstå og udføre. Maskinkode følger instruktionssættet for en bestemt computerarkitektur og er typisk repræsenteret i binær form (bits og bytes). I praksis opbevares maskinkode ofte i eksekverbare filer eller i hukommelsen som sekvenser af bytes — for mennesker vises den ofte i hexadecimalt format for at gøre den mere læsbar. Maskinkode er det laveste niveau af software, og alle højere programmeringssprog må på et tidspunkt oversættes eller fortolkes til maskinkode, for at CPU’en kan afvikle instruktionerne.

Hvordan en instruktion er opbygget

En enkelt maskininstruktion fortæller processoren, hvilken operation den skal udføre. Hver instruktion består typisk af en opcode (operationskode) og en eller flere operander. Operanderne kan være:

  • værdier (immediate) indlejret i instruktionen,
  • registernavne (processorens interne registre),
  • eller hukommelsesadresser, som peger på data i RAM.

Instruktionsformatet varierer mellem arkitekturer: nogle bruger faste længder for alle instruktioner, andre har variable længder. Der findes også forskellige adressing modes (måder at finde operandens adresse på), fx direkte, indirekte, indeksbaseret eller relativ adressering.

Hvad et instruktionssæt definerer

Et instruktionssæt er en formel beskrivelse af alle opkoder, tilgængelige registre, dataformater og hukommelsesmodel for en given computer. Instruktionssættet bestemmer altså, hvilke operationer CPU’en kan udføre, hvordan data flyttes, og hvordan kontrolflytning (betingede hop, funktionopkald osv.) foretages. Kendte forskelle mellem instruktionssæt er fx CISC (komplekse, ofte variable-længde instruktioner) versus RISC (enkle, ofte faste-længde instruktioner), hvilket påvirker både hardwaredesign og kompilatorstrategier.

Fra kildekode til maskinkode

Programmer skrevet i højniveausprog bliver normalt ikke skrevet direkte i maskinkode. I stedet benyttes værktøjer, som omdanner koden:

  • Kompilere oversætter højniveausprog til maskinkode eller objektfiler (binære moduler).
  • En assembler oversætter samlekode (assembly) — en menneskelæselig repræsentation af maskininstruktioner — direkte til maskinkode.
  • Programbuildere (build tools) og linkere samler objektfiler, løser symboler og producerer eksekverbare filer eller biblioteker, som operativsystemets loader kan indlæse i hukommelsen.
  • Fortolkere og JIT-kompilatorer (just-in-time) kan oversætte eller generere maskinkode ved køretid i stedet for ved bygge-/kompileringstid.

Der findes også mellemformater som bytecode (fx Java eller .NET), der kræver en virtuel maskine eller JIT for at blive til maskinkode på den konkrete platform.

Native kode og bærbarhed

Maskinkode omtales ofte som native kode, når den er lavet til og kan afvikles direkte på en bestemt CPU-arkitektur. Native kode er typisk hurtigere, fordi den kører direkte på hardware uden mellemlag, men den er også bundet til det specifikke instruktionssæt og kan ikke køre på en anden arkitektur uden emulering eller genkompilering. Derfor taler man om, at native kode "kun fungerer på nogle computere" — eksempelvis vil x86-nativ kode ikke køre på en ARM-processor uden en kompatibilitetsløsning.

Samlet set er maskinkode den endelige form for instruktioner, en processor kan udføre. Forståelse af opcodes, operander, instruktionssæt og byggefaserne (kompilering/assembling/linking) er centralt for både systemnær programmering, optimering og portering mellem platforme.