Con il termine codice macchina o linguaggio macchina ci si riferisce a un insieme di istruzioni in codice binario eseguite direttamente dal processore o CPU di un computer. Ogni istruzione compilata seguendo le regole di questo codice esegue solamente un'operazione molto precisa: scrivere dati in una cella di memoria; saltare una cella di memoria e passare alla successiva; compiere un'operazione logico-aritmetica e così via. Ogni programma eseguito direttamente dalla CPU, dunque, è scritto usando una serie di istruzioni di questo genere.
Il linguaggio macchina può essere visto come il più basso livello di rappresentazione di un programma compilato o assemblato o come un linguaggio di programmazione primitivo e strettamente dipendente dall'hardware utilizzato per eseguire le istruzioni. Anche se è possibile realizzare un software utilizzando direttamente il linguaggio macchina, programmare usando il codice binario (un alfabeto digitale composto esclusivamente da due caratteri, convenzionalmente indicati con i numeri 0 e 1) può essere particolarmente noioso e portare facilmente a commettere errori. Per programmare in codice macchina, infatti, si devono costantemente ricordare e calcolare gli indirizzi di memoria che si utilizzano e "maneggiare" con attenzione ogni singolo bit di informazione che è necessario al programma.
Tutti i software che utilizziamo quotidianamente sono realizzati utilizzando linguaggi di programmazione di alto livello (più simili alla lingua parlata e più facilmente comprensibili da chi si trova a programmare). Il codice sorgente del software sarà poi tradotto in istruzioni elementari da appositi strumenti chiamati assembler o compilatori. Fanno eccezione i programmi interpretati, nei quali il codice sorgente è eseguito direttamente (anziché essere tradotto) grazie a speciali tool detti interpreti.
Struttura linguaggio macchina
Se da un punto di vista operativo programmare in linguaggio macchina può apparire particolarmente complesso, dal lato teorico la sua strutturazione e il suo funzionamento sono piuttosto semplici. Come se fosse un linguaggio naturale, il codice macchina si compone di lettere (i valori del codice binario, gli 0 e gli 1), parole (combinazioni di più lettere del codice binario) e frasi (combinazioni di più parole corrispondenti a una singola istruzione data "in pasto" al processore). Per quanto possa apparire semplice, questa organizzazione è alla base del funzionamento di qualunque software in circolazione, compresi i più complessi e moderni.
Volendo fare un'analogia grammaticale, potremmo dire che le frasi del linguaggio macchina assumono la struttura sintattica verbo+complementi (come il complemento oggetto, complemento di tempo e complemento di luogo). In particolare, ogni frase ha inizio con un verbo, corrispondente all'azione che la CPU deve compiere dopo aver processato l'istruzione in ingresso. I complementi, invece, indicano i parametri a cui l'azione deve essere applicata (due numeri, ad esempio, nel caso in cui il verbo sia Somma o Sottrai) oppure l'indirizzo delle celle di memoria nelle quali devono essere lette o salvate delle informazioni.
Come visto, i verbi che danno inizio alla frase sono di natura imperativa (Leggi, Somma, Sottrai, Salta, Salva), il che fa del linguaggio macchina un linguaggio di programmazione imperativo.
Unicità e compatibilità
Parlare di linguaggio macchina in senso assoluto, come se fosse univoco, è un errore. Le varie tipologie di CPU e SoC in commercio, infatti, sono contraddistinte da una propria architettura e organizzazione interna: ciò le rende (a loro modo) uniche e incapaci di eseguire le istruzioni scritte in un linguaggio macchina differente dal loro. Quando, spulciando ad esempio le specifiche di uno smartphone, si leggono termini come Cortex-A73 (nel caso di processori ARM) vuol dire che quel particolare SoC è in grado di eseguire le istruzioni in linguaggio macchina scritte apposta per l'architettura ArmV8 e non le istruzioni scritte per un'architettura hardware differente.
Se un processore P1, invece, è in grado di interpretare ed eseguire le istruzioni di un software scritto con un linguaggio macchina pensato per il processore P2, vuol dire che i due processori sono compatibili tra di loro. Basti pensare al già citato caso delle architetture ArmV8, uno standard (o quasi) nell'ambito dei SoC per smartphone e altri dispositivi portatili. O all'architettura x86, sviluppata inizialmente da Intel e successivamente adottata da quasi tutti gli altri produttori di CPU (come AMD, ad esempio).