Quali sono il segmento e l’offset nell’indirizzamento della memoria in modalità reale?

Sto leggendo sull’indirizzamento della memoria. Ho letto sull’offset del segmento e poi sull’offset del descrittore. So come calcolare gli indirizzi esatti in modalità reale. Tutto questo è OK, ma non riesco a capire cos’è esattamente l’offset? Ovunque leggo:

Nella modalità reale, i registri sono solo a 16 bit, quindi è ansible indirizzare solo fino a 64k. Per consentire l’indirizzamento di più memoria, l’indirizzo קד viene calcolato dal segmento * 16 + offset.

Qui posso capire la prima linea. Abbiamo 16 bit, quindi possiamo indirizzare fino a 2 ^ 16 = 64k.

Ma cos’è questa seconda linea? Cosa rappresenta il segmento? Perché lo moltiplichiamo con 16? perché aggiungiamo offset. Non riesco proprio a capire cos’è questo offset? Qualcuno può spiegarmi o darmi un link per questo per favore?

Sotto la memoria in modalità reale x86, l’indirizzo fisico è lungo 20 bit e viene calcolato:

PhysicalAddress = Segment * 16 + Offset 

Controllare anche: Gestione della memoria in modalità reale

Quando Intel stava costruendo l’8086, c’era un caso valido per avere più di 64 KB in una macchina, ma non c’era modo di utilizzare mai uno spazio degli indirizzi a 32 bit. Allora, anche un megabyte era un sacco di memoria. (Ricorda la frase infame “640K dovrebbe essere sufficiente per chiunque”? È essenzialmente una traduzione errata del fatto che all’epoca 1MB era terribilmente grande .) La parola “gigabyte” non sarebbe stata di uso comune per altri 15-20 anni e non si riferirebbe alla RAM per come altri 5-10 anni dopo.

Quindi, invece di implementare uno spazio degli indirizzi così grande da non essere mai “completamente” utilizzato, ciò che hanno fatto è stato implementare gli indirizzi a 20 bit. Hanno ancora usato parole a 16 bit per gli indirizzi, perché dopo tutto questo è un processore a 16 bit. La parola in alto era il “segmento” e la parola in basso era lo “scostamento”. Le due parti si sono sovrapposte considerevolmente, tuttavia – un “segmento” è un blocco di memoria di 64 KB che inizia a (segment) * 16 e “offset” può puntare ovunque all’interno di quel blocco. Per calcolare l’indirizzo effettivo, moltiplichi la parte del segmento dell’indirizzo di 16 (o spostalo a sinistra di 4 bit … stessa cosa), quindi aggiungi l’offset. Quando hai finito, hai un indirizzo a 20 bit.

  19 4 0 +--+--+--+--+ | segment | +--+--+--+--+--+ | offset | +--+--+--+--+ 

Ad esempio, se il segmento era 0x8000 e l’offset era 0x0100, l’indirizzo effettivo viene visualizzato su ((0x8000 << 4) + 0x0100) == 0x80100 .

  8 0 0 0 0 1 0 0 --------------- 8 0 1 0 0 

La matematica è raramente così bella, anche se - 0x80100 può essere rappresentata letteralmente da migliaia di segmenti diversi: combinazioni di offset (4096, se la mia matematica è giusta).

Voglio aggiungere una risposta qui solo perché sto setacciando internet cercando di capire anche questo. Le altre risposte stavano tralasciando un’informazione chiave che ho ricevuto dal link presentato in una delle risposte. Tuttavia, mi mancava quasi del tutto. Leggendo la pagina collegata, non capivo ancora come funzionasse.

Il problema che stavo probabilmente avendo da solo era capire veramente come il Commodore 64 (processore 6502) disponesse la memoria. Usa notazione simile per indirizzare la memoria. Ha 64k di memoria totale e utilizza i valori a 8 bit di PAGE: OFFSET per accedere alla memoria. Ogni pagina ha una lunghezza di 256 byte (numero di 8 bit) e l’offset punta a uno dei valori in quella pagina. Le pagine sono distanziate una dopo l’altra in memoria. Quindi la pagina 2 inizia dove finisce la pagina 1. Stavo andando nella 386 pensando allo stesso stile. Non è così.

La modalità reale utilizza uno stile simile anche se è diversa la dicitura SEGMENT: OFFSET. Un segmento ha una dimensione di 64k. Tuttavia, i segmenti stessi non sono disposti back-to-back come lo era Commodore. Sono distanziati di 16 byte l’uno dall’altro. Offset funziona ugualmente, indicando quanti byte dalla pagina \ segmento iniziano.

Spero che questa spiegazione aiuti chiunque altro a trovare questa domanda, mi ha aiutato a scriverlo.

Vedo che la domanda e le risposte hanno qualche anno, ma c’è un’affermazione errata che esistono solo registri a 16 bit nella modalità reale.

All’interno della modalità reale i registri non sono solo a 16 bit, perché ci sono anche registri a 8 bit. Ognuno di questi registri a 8 bit fa parte di un registro a 16 bit diviso in una parte inferiore e una superiore di un registro a 16 bit.

E iniziando la modalità reale con un 80386+ diventiamo registri a 32 bit e inoltre due nuovi prefissi di istruzioni, uno per sovrascrivere / annullare la dimensione dell’operando di default e uno per sovrascrivere / annullare la dimensione dell’indirizzo predefinito di una istruzione all’interno di un codesegment.

Questi prefissi di istruzioni possono essere usati in combinazione per invertire la dimensione dell’operando e la dimensione dell’indirizzo insieme per un’istruzione. All’interno della modalità reale, la dimensione dell’operando e la dimensione dell’indirizzo predefinite sono 16 bit. Con questi due prefissi di istruzioni possiamo usare un esempio di operando / registro a 32 bit per calcolare un valore a 32 bit in un registro a 32 bit, o per spostare un valore a 32 bit verso e da una posizione memoriale. E possiamo usare tutti i registri a 32 bit (magari in combinazione con una base + indice * scala + spostamento) come un registro di indirizzi, ma la sum dell’indirizzo effettivo non deve superare il limite della dimensione del segmento di 64 kb .

(Nella pagina OSDEV-Wiki possiamo trovare nella tabella il prefisso “Override dimensione di operando e dimensione indirizzo” che “prefisso operando 0x66” e “prefisso indirizzo 0x67” è N / A (non disponibile) per il modalità reale e modalità 8086 virtuale. http://wiki.osdev.org/X86-64_Instruction_Encoding
Ma questo è totalmente sbagliato, perché nel manuale di Intel possiamo trovare questa affermazione: “Questi prefissi possono essere utilizzati in modalità indirizzo reale, nonché in modalità protetta e modalità 8080 virtuale”.)

A partire da un Pentium MMX diventiamo otto registri MMX a 64 bit.
A partire da un Pentium 3 diventiamo otto registri XMM a 128 bit.
..

Se non sbaglio, allora il registro YMM a 256 bit e il registro ZMM a 512 bit e il registro generico a 64 bit di un x64 non possono essere utilizzati nella modalità reale.

pugnale

Esempio minimo

Con:

  • offset = msg
  • segment = ds
 mov $0, %ax mov %ax, %ds mov %ds:msg, %al /* %al contains 1 */ mov $1, %ax mov %ax, %ds mov %ds:msg, %al /* %al contains 2: 1 * 16 bytes forward. */ msg: .byte 1 .fill 15 .byte 2 

Quindi se vuoi accedere alla memoria sopra 64k:

 mov $0xF000, %ax mov %ax, %ds 

Tieni presente che questo consente indirizzi più grandi di 20 bit se si utilizza qualcosa come:

 0x10 * 0xFFFF + 0xFFFF == 0x10FFEF 

Su processori precedenti che avevano solo 20 cavi di indirizzo, è stato semplicemente troncato, ma in seguito le cose si sono complicate con la linea A20 (21 ° indirizzo): https://en.wikipedia.org/wiki/A20_line

Su un repository GitHub con il boilerplate richiesto per eseguirlo.

Un registro a 16 bit può indirizzare solo fino a 0xFFFF (65.536 byte, 64 KB). Quando ciò non bastò, Intel aggiunse i registri di segmento.

Qualsiasi progetto logico avrebbe semplicemente combinato due registri a 16 bit per creare uno spazio di indirizzamento a 32 bit, (ad es. 0xFFFF : 0xFFFF = 0xFFFFFFFF ), ma nooooo … Intel doveva diventare strana con noi.

Storicamente, il bus frontside (FSB) aveva solo 20 linee di indirizzo e quindi poteva trasmettere solo indirizzi a 20 bit. Per “rettificare” ciò, Intel ha ideato uno schema in cui i registri di segmento estendono solo il proprio indirizzo di 4 bit (16 bit + 4 = 20, in teoria).

Per ottenere ciò, il registro del segmento viene spostato a sinistra dal suo valore originale di 4 bit, quindi aggiunto all’indirizzo nel registro generale (ad esempio [es:ax] = ( es << 4 ) + ax ) . Nota: lo spostamento a sinistra di 4 bit equivale a moltiplicare per 16 .

Questo è tutto. Ecco alcuni esempi illustrativi:

 ;; everything's hexadecimal [ 0:1 ] = 1 [ F:1 ] = F1 [ F:0 ] = F0 [ F:FF] = 1EF ; [F becomes F0, + FF = 1EF] [ F000 : FFFF ] = FFFFF (max 20-bit number) [ FFFF : FFFF ] = 10FFEF (oh shit, 21-bit number!) 

Quindi, puoi ancora indirizzare più di 20 bit. Che succede? L'indirizzo "avvolge", come il modulo aritmetico (come conseguenza naturale dell'hardware). Quindi, 0x10FFEF diventa 0xFFEF .

E il gioco è fatto! Intel ha assunto ingegneri stupidi e dobbiamo conviverci.