Programma in linguaggio assembly. Caratteristiche generali del sistema di comando del linguaggio Assembler per PC IBM (insieme base di comandi, metodi base di indirizzamento degli operandi). Struttura del programma in linguaggio Assembler. Comandi in linguaggio assembly

informazioni generali sul linguaggio assembly

Il linguaggio assembly simbolico può eliminare in gran parte gli svantaggi della programmazione in linguaggio macchina.

Il suo vantaggio principale è che nel linguaggio assembly tutti gli elementi del programma sono presentati in forma simbolica. La responsabilità di convertire i nomi dei comandi simbolici nei relativi codici binari è di programma speciale- assemblatore, che libera il programmatore dal lavoro ad alta intensità di manodopera ed elimina gli inevitabili errori.

I nomi simbolici immessi durante la programmazione in linguaggio assembly di solito riflettono la semantica del programma e l'abbreviazione dei comandi riflette la loro funzione principale. Ad esempio: PARAM - parametro, TABLE - tabella, MASK - maschera, ADD - addizione, SUB - sottrazione, ecc. ecc. Tali nomi sono facili da ricordare per un programmatore.

Per programmare in linguaggio assembly è necessario disporre di strumenti più complessi che per programmare in linguaggio macchina: sono necessari sistemi informatici basati su microcomputer o PC con un set periferiche(tastiera alfanumerica, display a caratteri, float drive e dispositivo di stampa), nonché sistemi residenti o di programmazione incrociata per i tipi di microprocessori richiesti. Il linguaggio assembly consente di scrivere ed eseguire il debug in modo efficace di programmi molto più complessi rispetto al linguaggio macchina (fino a 1 - 4 KB).

I linguaggi assembly sono orientati alla macchina, cioè dipendenti dal linguaggio macchina e dalla struttura del microprocessore corrispondente, poiché in essi a ciascuna istruzione del microprocessore viene assegnato un nome simbolico specifico.

I linguaggi assembly forniscono un aumento significativo della produttività del programmatore rispetto ai linguaggi macchina e allo stesso tempo mantengono la capacità di utilizzare tutte le risorse hardware disponibili nel software del microprocessore. Ciò consente ai programmatori qualificati di scrivere programmi che possono essere eseguiti in meno di poco tempo e occupano meno memoria rispetto ai programmi creati in un linguaggio di alto livello.

A questo proposito, quasi tutti i programmi per il controllo dei dispositivi di input/output (driver) sono scritti in linguaggio assembly, nonostante la presenza di una gamma abbastanza ampia di linguaggi di alto livello.

Utilizzando il linguaggio assembly, il programmatore può impostare i seguenti parametri:

mnemonici (nome simbolico) di ciascun comando in linguaggio macchina del microprocessore;

un formato standard per le righe di un programma scritto in linguaggio assembly;

formato per indicare in vari modi varianti di indirizzamento e comando;

formato per specificare costanti di carattere e costanti intere in vari sistemi numerici;

pseudo-comandi che controllano il processo di assemblaggio (traduzione) di un programma.

Nel linguaggio assembly il programma viene scritto riga per riga, ovvero per ogni comando viene assegnata una riga.

Per i microcomputer costruiti sulla base dei tipi più comuni di microprocessori, possono esserci diverse varianti del linguaggio assembly, ma di solito nella pratica ne viene ampiamente utilizzato uno: il cosiddetto linguaggio assembly standard

La programmazione a livello di istruzione macchina è il livello minimo al quale è possibile scrivere i programmi. Il sistema di istruzioni della macchina deve essere sufficiente per implementare le azioni richieste impartendo istruzioni all'hardware del computer.

Ogni comando macchina è composto da due parti:

· sala operatoria - determinare “cosa fare”;

· operando - definizione degli oggetti di elaborazione, “cosa fare con”.

Il comando della macchina a microprocessore, scritto in linguaggio assembly, è una riga con la seguente forma sintattica:

etichetta comando/direttiva operando(i) ;commenti

In questo caso, il campo obbligatorio nella riga è un comando o una direttiva.

L'etichetta, il comando/direttiva e gli operandi (se presenti) sono separati da almeno uno spazio o un carattere di tabulazione.

Se è necessario continuare un comando o una direttiva nella riga successiva, viene utilizzato il carattere barra rovesciata: \.

Per impostazione predefinita, il linguaggio assembly non distingue tra lettere maiuscole e minuscole durante la scrittura di comandi o direttive.

Indirizzamento diretto: L'indirizzo effettivo è determinato direttamente dal campo offset dell'istruzione macchina, che può avere una dimensione di 8, 16 o 32 bit.

mov eax, somma; eax = somma

L'assemblatore sostituisce sum con l'indirizzo corrispondente memorizzato nel segmento dati (indirizzato dal registro ds per impostazione predefinita) e inserisce il valore memorizzato in sum nel registro eax.

Indirizzamento indiretto a sua volta ha le seguenti tipologie:

· indirizzamento di base indiretto (di registro);

· indirizzamento indiretto di base (registro) con offset;

· indirizzamento indicizzato indiretto;

· indirizzamento indiretto di indice di base.

Indirizzamento di base indiretto (registro). Con questo indirizzamento, l'indirizzo effettivo dell'operando può essere posizionato in uno qualsiasi dei registri di uso generale, ad eccezione di sp/esp e bp/ebp (questi sono registri specifici per lavorare con il segmento dello stack). Sintatticamente in un comando questa modalità di indirizzamento viene espressa racchiudendo il nome del registro tra parentesi quadre.

mov eax, ; eax = *esi; *valore esi all'indirizzo esi

Introduzione.

Viene chiamata la lingua in cui è scritto il programma sorgente Entrata lingua e la lingua in cui viene tradotta per l'esecuzione da parte del responsabile del trattamento nei giorni liberi lingua. Viene chiamato il processo di conversione della lingua di input nella lingua di output trasmissione. Poiché i processori sono in grado di eseguire programmi in linguaggio macchina binario, che non viene utilizzato per la programmazione, è necessaria la traduzione di tutti i programmi sorgente. Conosciuto due strade trasmissioni: compilazione e interpretazione.

A compilazione il programma sorgente viene prima tradotto completamente in un programma equivalente nella lingua di output, chiamato oggetto programma e poi eseguito. Questo processo viene implementato utilizzando uno speciale programmi, chiamato compilatore. Viene chiamato un compilatore per il quale il linguaggio di input è una forma simbolica di rappresentazione del linguaggio macchina (di output) dei codici binari assemblatore.

A interpretazioni Ogni riga di testo nel programma sorgente viene analizzata (interpretata) e il comando in essa specificato viene immediatamente eseguito. L'implementazione di questo metodo è affidata a programma interprete. L'interpretazione richiede molto tempo. Per aumentare la sua efficienza, invece di elaborare ogni riga, l'interprete prima le converte tutte squadra stringhe in caratteri (

). La sequenza di simboli generata viene utilizzata per eseguire le funzioni assegnate al programma originale.

Il linguaggio assembly discusso di seguito viene implementato utilizzando la compilazione.

Caratteristiche della lingua.

Caratteristiche principali dell'assemblatore:

● invece dei codici binari, il linguaggio utilizza nomi simbolici - mnemonici. Ad esempio, per il comando di addizione (

) vengono utilizzati i mnemonici

Sottrazioni (

moltiplicazione (

Divisioni (

ecc. I nomi simbolici vengono utilizzati anche per indirizzare le celle di memoria. Per programmare in linguaggio assembly, invece che codici e indirizzi binari, è necessario conoscere solo nomi simbolici che l'assembler traduce in codici binari;

ogni affermazione corrisponde un comando della macchina(codice), ovvero esiste una corrispondenza biunivoca tra comandi macchina e operatori in un programma in linguaggio assembly;

● la lingua fornisce l'accesso a tutti gli oggetti e squadre. I linguaggi di alto livello non hanno questa capacità. Ad esempio, il linguaggio assembly consente di controllare i bit del registro flag e il linguaggio di alto livello (ad esempio,

) non ha questa capacità. Si noti che i linguaggi di programmazione dei sistemi (ad esempio C) occupano spesso una posizione intermedia. In termini di accessibilità, sono più vicini al linguaggio assembly, ma hanno la sintassi di un linguaggio di alto livello;

● linguaggio assembly non è una lingua universale. Ogni gruppo specifico di microprocessori ha il proprio assemblatore. I linguaggi di alto livello non presentano questo inconveniente.

A differenza dei linguaggi di alto livello, la scrittura e il debug di un programma in linguaggio assembly richiedono molto tempo. Nonostante ciò, il linguaggio assembly ha ricevuto ampio utilizzo a causa delle seguenti circostanze:

● un programma scritto in linguaggio assembly è di dimensioni significativamente più piccole e viene eseguito molto più velocemente di un programma scritto in un linguaggio di alto livello. Per alcune applicazioni, questi indicatori svolgono un ruolo primario, ad esempio molti programmi di sistema (compresi i compilatori), programmi per carte di credito, telefono cellulare, driver di dispositivo, ecc.;

● alcune procedure richiedono accesso completo all'hardware, cosa che di solito è impossibile da fare in un linguaggio di alto livello. Questo caso include gli interrupt e i gestori degli interrupt nei sistemi operativi, nonché i controller dei dispositivi nei sistemi embedded in tempo reale.

Nella maggior parte dei programmi, solo una piccola percentuale del codice totale è responsabile di gran parte del tempo di esecuzione del programma. In genere, l'1% del programma è responsabile del 50% del tempo di esecuzione e il 10% del programma è responsabile del 90% del tempo di esecuzione. Pertanto, per scrivere un programma specifico in condizioni reali, vengono utilizzati sia l'assembler che uno dei linguaggi di alto livello.

Formato operatore in linguaggio assembly.

Un programma in linguaggio assembly è un elenco di comandi (istruzioni, frasi), ciascuno dei quali occupa una riga separata e contiene quattro campi: un campo etichetta, un campo operazione, un campo operando e un campo commento. Ogni campo ha una colonna separata.

Campo etichetta.

Al campo etichetta è assegnata la colonna 1. L'etichetta è un nome simbolico, o identificatore, indirizzi memoria. È necessario affinché tu possa:

● effettuare una transizione condizionale o incondizionata al comando;

● ottenere l'accesso al luogo in cui sono archiviati i dati.

Tali dichiarazioni sono dotate di un'etichetta. Per indicare un nome si utilizzano lettere (maiuscole) dell'alfabeto inglese e numeri. Il nome deve avere una lettera all'inizio e un separatore di due punti alla fine. L'etichetta dei due punti può essere scritta su una riga separata e il codice operativo può essere scritto sulla riga successiva nella colonna 2, il che semplifica il lavoro del compilatore. L'assenza dei due punti non permette di distinguere un'etichetta da un codice operazione se si trovano su righe separate.

In alcune versioni del linguaggio assembly, i due punti vengono posizionati solo dopo le etichette delle istruzioni, non dopo le etichette dei dati, e la lunghezza dell'etichetta può essere limitata a 6 o 8 caratteri.

Non dovrebbero esserci nomi identici nel campo dell'etichetta, poiché l'etichetta è associata agli indirizzi di comando. Se durante l'esecuzione del programma non è necessario richiamare un comando o dati dalla memoria, il campo etichetta rimane vuoto.

Campo codice operazione.

Questo campo contiene il codice mnemonico per un comando o pseudo-comando (vedi sotto). Il codice mnemonico del comando viene scelto dagli sviluppatori del linguaggio. Nel linguaggio assembly

viene selezionato il mnemonico per caricare un registro dalla memoria

) e per salvare il contenuto del registro in memoria - un mnemonico

). Nei linguaggi assembly

per entrambe le operazioni è possibile utilizzare rispettivamente lo stesso nome

Se la scelta dei nomi mnemonici può essere arbitraria, allora la necessità di utilizzare due istruzioni macchina è determinata dall'architettura del processore

La mnemonica dei registri dipende anche dalla versione dell'assembler (Tabella 5.2.1).

Campo dell'operando.

Qui si trova Informazioni aggiuntive, necessario per eseguire l'operazione. Nel campo degli operandi per i comandi di salto viene indicato l'indirizzo a cui deve essere effettuato il salto, nonché gli indirizzi e i registri che sono operandi per il comando della macchina. Ad esempio, diamo operandi che possono essere utilizzati per processori a 8 bit

● dati numerici,

presentati in diversi sistemi numerici. Per indicare il sistema numerico utilizzato, la costante è seguita da uno di Lettere latine: IN,

Di conseguenza, i sistemi numerici binari, ottali, esadecimali e decimali (

Non è necessario scriverlo). Se la prima cifra di un numero esadecimale è A, B, C,

Quindi viene aggiunto uno 0 (zero) insignificante davanti;

● codici dei registri interni del microprocessore e delle celle di memoria

M (fonti o destinatari di informazioni) sotto forma delle lettere A, B, C,

M o i loro indirizzi in qualsiasi sistema numerico (ad esempio, 10B - indirizzo di registro

nel sistema binario);

● identificatori,

per il registro delle coppie di aeromobili,

Le prime lettere sono B,

N; per una coppia di accumulatori e registri di funzioni -

; per il contatore del programma -

;per il puntatore dello stack -

● etichette che indicano gli indirizzi degli operandi o delle istruzioni successive nel condizionale

(se la condizione è soddisfatta) e transizioni incondizionate. Ad esempio, l'operando M1 nel comando

indica la necessità di una transizione incondizionata al comando, il cui indirizzo nel campo dell'etichetta è contrassegnato con l'identificatore M1;

● espressioni,

che sono costruiti collegando i dati discussi sopra utilizzando operatori aritmetici e logici. Tieni presente che il metodo per riservare lo spazio dati dipende dalla versione della lingua. Sviluppatori di linguaggio assembly per

Definire la parola) e successivamente inserita Opzione alternativa.

che era nel linguaggio dei processori fin dall'inizio

Nella versione linguistica

usato

Definire una costante).

I processori elaborano operandi di diversa lunghezza. Per definirlo, gli sviluppatori assemblatori hanno preso decisioni diverse, ad esempio:

II registri di diversa lunghezza hanno nomi diversi: EAX - per posizionare operandi a 32 bit (tipo

); AX - per 16 bit (tipo

e AN - per 8 bit (tipo

● per i processori

I suffissi vengono aggiunti a ciascun codice operazione: suffisso

Per tipo

; suffisso ".B" per il tipo

vengono utilizzati codici operativi diversi per operandi di diversa lunghezza, ad esempio per caricare un byte, una mezza parola (

) e parole in un registro a 64 bit utilizzando i codici operativi

rispettivamente.

Campo commenti.

Questo campo fornisce spiegazioni sulle azioni del programma. I commenti non influenzano il funzionamento del programma e sono destinati agli esseri umani. Potrebbero essere necessari per modificare un programma, che senza tali commenti potrebbe risultare del tutto incomprensibile anche ai programmatori esperti. Un commento inizia con un simbolo e viene utilizzato per spiegare e documentare i programmi. Il carattere iniziale di un commento può essere:

● punto e virgola (;) nelle lingue destinate ai responsabili del trattamento dell'azienda

Punto esclamativo(!) nelle lingue per

Ogni riga di commento separata è preceduta da un carattere iniziale.

Pseudo-comandi (direttive).

Nel linguaggio assembly esistono due tipi principali di comandi:

di base istruzioni che sono l'equivalente del codice macchina del processore. Questi comandi eseguono tutte le elaborazioni previste dal programma;

pseudo-comandi O direttive, progettato per servire il processo di traduzione di un programma in un linguaggio di combinazione di codici. Come esempio nella tabella. 5.2.2 mostra alcuni pseudo-comandi dell'assembler

per la famiglia

.

Durante la programmazione ci sono situazioni in cui, secondo l'algoritmo, la stessa catena di comandi deve essere ripetuta più volte. Per uscire da questa situazione puoi:

● scrivere la sequenza di comandi richiesta ogni volta che si verifica. Questo approccio porta ad un aumento del volume del programma;

● organizzare questa sequenza in una procedura (subroutine) e richiamarla se necessario. Questo output ha i suoi svantaggi: ogni volta è necessario eseguire un comando di chiamata di procedura speciale e un comando di ritorno che, se la sequenza è breve e utilizzata frequentemente, può ridurre notevolmente la velocità del programma.

Il più semplice e metodo efficace la ripetizione ripetuta di una catena di comandi consiste nell'usare macro, che può essere rappresentato come uno pseudo-comando progettato per ritradurre un gruppo di comandi spesso presenti in un programma.

Una macro, o macrocomando, è caratterizzata da tre aspetti: macrodefinizione, macroinversione e macroestensione.

Definizione macro

Questa è una designazione per una sequenza ripetuta ripetutamente di comandi di programma, utilizzata come riferimento nel testo del programma.

La definizione della macro ha la seguente struttura:

Elenco delle espressioni; Definizione macro

Nella struttura della macrodefinizione data si possono distinguere tre parti:

● titolo

macro, incluso il nome

Pseudo-comando

e una serie di parametri;

● contrassegnato da punti corpo macro;

● squadra

la laurea

definizioni macro.

Il set di parametri di definizione macro contiene un elenco di tutti i parametri forniti nel campo dell'operando per il gruppo di istruzioni selezionato. Se questi parametri sono stati forniti in precedenza nel programma, non è necessario indicarli nell'intestazione della definizione della macro.

Per ricomporre il gruppo selezionato di comandi viene utilizzato un appello costituito dal nome

comandi macro ed elenco di parametri con altri valori.

Quando l'assemblatore incontra una definizione di macro durante il processo di compilazione, la memorizza nella tabella delle definizioni di macro. Alle successive apparizioni nel programma del nome (

) di una macro, l'assembler lo sostituisce con il corpo della macro.

Viene chiamato l'utilizzo di un nome macro come codice operativo macro-inversione(chiamata macro) e sostituendolo con il corpo della macro - espansione macro.

Se un programma viene rappresentato come una sequenza di caratteri (lettere, numeri, spazi, segni di punteggiatura e ritorni a capo per passare a una nuova riga), l'espansione della macro consiste nel sostituire alcune catene di questa sequenza con altre catene.

L'espansione delle macro avviene durante il processo di assemblaggio, non durante l'esecuzione del programma. Sono assegnati metodi per manipolare stringhe di caratteri significa macro.

Viene eseguito il processo di assemblaggio in due passaggi:

● Al primo passaggio, tutte le definizioni delle macro vengono conservate e le chiamate alle macro vengono espanse. In questo caso, il programma originale viene letto e convertito in un programma in cui tutte le definizioni di macro vengono rimosse e ogni chiamata di macro viene sostituita dal corpo della macro;

● il secondo passaggio elabora il programma risultante senza macro.

Macro con parametri.

Per lavorare con sequenze ripetute di comandi, i cui parametri possono assumere valori diversi, vengono fornite le definizioni delle macro:

● con effettivo parametri che vengono inseriti nel campo dell'operando della chiamata macro;

● con formale parametri. Durante l'espansione della macro, ogni parametro formale che appare nel corpo della macro viene sostituito dal corrispondente parametro effettivo.

utilizzando macro con parametri.

Il programma 1 contiene due sequenze simili di comandi, che differiscono in quanto la prima scambia P e

E il secondo

Il programma 2 include una macro con due parametri formali P1 e P2. Durante l'espansione della macro, ogni carattere P1 all'interno del corpo della macro viene sostituito dal primo parametro effettivo (P,

), e il simbolo P2 viene sostituito dal secondo parametro attuale (

) dal programma n. 1. Nella chiamata macro

il programma 2 è contrassegnato: P,

Il primo parametro effettivo,

Secondo parametro effettivo.

Programma 1

Programma 2

MOV EBX,Q MOV EAX,Pl

MOV Q,EAX MOV EBX,P2

MOV P,EBX MOV P2,EAX

Funzionalità estese.

Diamo un'occhiata ad alcune funzionalità linguistiche avanzate

Se una macro contenente un comando di salto condizionale e un'etichetta a cui passare viene richiamata due o più volte, l'etichetta verrà duplicata (problema di etichetta duplicata), causando un errore. Pertanto ad ogni chiamata viene assegnata un'etichetta separata come parametro (da parte del programmatore). Nella lingua

l'etichetta è dichiarata locale (

) e grazie a funzionalità avanzate, l'assemblatore genera automaticamente un'etichetta diversa ogni volta che la macro viene espansa.

consente di definire macro all'interno di altre macro. Questa funzionalità avanzata è molto utile in combinazione con il collegamento condizionale di un programma. Consideriamo

SE PAROLE GT 16 M2 MACRO

La macro M2 può essere definita in entrambe le parti dell'istruzione

Tuttavia, la definizione dipende dal processore su cui è assemblato il programma: 16 bit o 32 bit. Se M1 non viene chiamata, la macro M2 non verrà affatto definita.

Un'altra funzionalità avanzata è che le macro possono chiamare altre macro, incluse se stesse: ricorsivo chiamata. In quest'ultimo caso, per evitare un ciclo infinito, la macro deve passarsi un parametro che cambia ad ogni espansione, e inoltre controllo questo parametro e termina la ricorsione quando il parametro raggiunge un determinato valore.

Sull'uso dei mezzi macro in assembler.

Quando si utilizzano le macro, l'assemblatore deve essere in grado di svolgere due funzioni: salvare le definizioni delle macro E espandere le sfide macro.

Salvataggio delle definizioni delle macro.

Tutti i nomi delle macro vengono memorizzati in una tabella. Ogni nome è accompagnato da un puntatore alla macro corrispondente in modo da poterla richiamare in caso di necessità. Alcuni assemblatori hanno una tabella separata per i nomi delle macro, altri hanno una tabella generale in cui, insieme ai nomi delle macro, si trovano tutte le istruzioni e le direttive della macchina.

Quando si incontra una macro durante l'assemblaggio è creato:

nuovo elemento della tabella con il nome della macro, il numero di parametri e un puntatore ad un'altra tabella di definizione della macro dove verrà memorizzato il corpo della macro;

● elenco formale parametri.

Il corpo della macro, che è semplicemente una stringa di caratteri, viene quindi letto e memorizzato nella tabella di definizione della macro. I parametri formali che si verificano nel corpo del ciclo sono contrassegnati carattere speciale.

Rappresentazione interna di una macro

dall'esempio precedente per il programma 2 (p. 244) è:

MOV EAX, MOV EBX, MOV MOV e

dove il punto e virgola viene utilizzato come carattere di ritorno a capo e la e commerciale & viene utilizzata come carattere di parametro formale.

Estensione delle chiamate macro.

Ogni volta che viene incontrata una definizione di macro durante l'assemblaggio, viene memorizzata nella tabella delle macro. Quando viene richiamata una macro, l'assembler interrompe temporaneamente la lettura dei dati di input dal dispositivo di input e inizia a leggere il corpo della macro memorizzata. I parametri formali estratti dal corpo della macro vengono sostituiti da parametri effettivi e forniti dalla chiamata. I parametri e commerciale e prima consentono all'assemblatore di riconoscerli.

Nonostante esistano molte versioni dell'assemblatore, i processi di assemblaggio hanno caratteristiche comuni e sono simili sotto molti aspetti. Il funzionamento di un assemblatore a due passaggi è discusso di seguito.

Assemblatore a due passaggi.

Un programma è composto da una serie di istruzioni. Pertanto, sembrerebbe che durante l'assemblaggio sia possibile utilizzare la seguente sequenza di azioni:

● tradurlo in linguaggio macchina;

● trasferire il codice macchina risultante in un file e la parte corrispondente dell'elenco in un altro file;

● ripetere le procedure elencate fino a tradurre l'intero programma.

Tuttavia, questo approccio non è efficace. Un esempio è il cosiddetto problema collegamento in avanti. Se la prima istruzione è un salto all'istruzione P, situata alla fine del programma, l'assemblatore non può tradurla. Deve prima determinare l'indirizzo dell'operatore P e per fare questo deve leggere l'intero programma. Viene richiamata ogni lettura completa del programma sorgente passaggio. Mostriamo come risolvere il problema del collegamento lookahead utilizzando due passaggi:

al primo passaggio dovresti raccogliere e memorizzare tutte le definizioni dei simboli (comprese le etichette) nella tabella e, al secondo passaggio, leggere e assemblare ciascun operatore. Questo metodo è relativamente semplice, ma un secondo passaggio attraverso il programma originale richiede tempo aggiuntivo dedicato alle operazioni di I/O;

● al primo passaggio dovresti convertire il programma in una forma intermedia e salvarlo in una tabella, ed eseguire il secondo passaggio non secondo il programma originale, ma secondo la tabella. Questo metodo di assemblaggio fa risparmiare tempo, poiché il secondo passaggio non esegue operazioni di I/O.

Primo passaggio.

Gol di primo passaggio- costruire una tabella dei simboli. Come notato in precedenza, un altro obiettivo del primo passaggio è preservare tutte le definizioni delle macro ed espandere le chiamate man mano che appaiono. Di conseguenza, sia la definizione del simbolo che l'espansione della macro avvengono in un unico passaggio. Il simbolo può essere uno dei due etichetta, O Senso, a cui viene assegnato un nome specifico utilizzando la direttiva -you:

;Valore: dimensione del buffer

Assegnando un significato ai nomi simbolici nel campo dell'etichetta del comando, l'assemblatore specifica essenzialmente gli indirizzi che ogni comando avrà durante l'esecuzione del programma. A questo scopo l'assemblatore immagazzina durante il processo di assemblaggio contatore dell'indirizzo dell'istruzione(

) come variabile speciale. All'inizio del primo passaggio, il valore della variabile speciale viene impostato su 0 e incrementato dopo ogni comando elaborato della lunghezza di quel comando. Come esempio nella tabella. 5.2.3 mostra un frammento di programma che indica la lunghezza dei comandi e i valori dei contatori. Al primo passaggio vengono generate le tabelle nomi simbolici, direttive E codici operativi, e se necessario letterale tavolo. Un valore letterale è una costante per la quale l'assemblatore riserva automaticamente memoria. Notiamo subito che i processori moderni contengono istruzioni con indirizzi immediati, quindi i loro assemblatori non supportano i letterali.

Tabella dei nomi dei simboli

contiene un elemento per ciascun nome (Tabella 5.2.4). Ogni elemento della tabella dei nomi simbolici contiene il nome stesso (o un puntatore ad esso), il suo valore numerico e talvolta alcune informazioni aggiuntive, che possono includere:

● la lunghezza del campo dati associato al simbolo;

● bit di riallocazione della memoria (che indicano se il valore di un simbolo cambia se il programma viene caricato ad un indirizzo diverso da quello previsto dall'assemblatore);

● informazioni sulla possibilità di accesso al simbolo dall'esterno della procedura.

I nomi simbolici sono etichette. Possono essere specificati utilizzando gli operatori (ad esempio,

Tabella direttiva.

Questa tabella elenca tutte le direttive, o pseudo-comandi, che si incontrano durante l'assemblaggio di un programma.

Tabella dei codici operazione.

Per ciascun codice operazione, la tabella ha colonne separate: designazione del codice operazione, operando 1, operando 2, valore esadecimale del codice operazione, lunghezza del comando e tipo di comando (Tabella 5.2.5). I codici operazione sono divisi in gruppi a seconda del numero e del tipo di operandi. Il tipo di comando determina il numero del gruppo e specifica la procedura richiamata per elaborare tutti i comandi in quel gruppo.

Secondo passaggio.

Gol del secondo passaggio- creazione di un programma oggetto e stampa, se necessario, del protocollo assembly; informazioni di output necessarie al linker per collegare procedure assemblate in momenti diversi in un unico file eseguibile.

Nel secondo passaggio (come nel primo), le righe contenenti le istruzioni vengono lette ed elaborate una per una. L'operatore originale e l'operatore di output ne derivano in formato esadecimale oggetto Il codice può essere stampato o inserito in un buffer per la stampa successiva. Dopo aver reimpostato il contatore dell'indirizzo del comando, viene richiamato il comando successiva affermazione.

Il programma sorgente può contenere errori, ad esempio:

il simbolo dato non è definito o è definito più di una volta;

● l'opcode è rappresentato da un nome non valido (a causa di un errore di battitura), non ha abbastanza operandi, o ha troppi operandi;

● nessun operatore

Alcuni assemblatori possono rilevare un simbolo indefinito e sostituirlo. Tuttavia, nella maggior parte dei casi, quando incontra un'istruzione di errore, l'assemblatore visualizza un messaggio di errore sullo schermo e tenta di continuare il processo di assemblaggio.

Articoli dedicati al linguaggio assembly.

Argomento 2.5 Nozioni di base sulla programmazione del processore

All'aumentare della lunghezza del programma diventa sempre più difficile ricordare i codici delle varie operazioni. I mnemonici forniscono qualche aiuto in questo senso.

Viene chiamato il linguaggio di codifica dei comandi simbolici assemblatore.

linguaggio assemblyè un linguaggio in cui ogni enunciato corrisponde esattamente a un comando della macchina.

Assemblea chiamata conversione di un programma dal linguaggio assembly, cioè preparare un programma in linguaggio macchina sostituendo i nomi simbolici delle operazioni con codici macchina e indirizzi simbolici con numeri assoluti o relativi, nonché incorporando programmi di libreria e generando sequenze di istruzioni simboliche specificando specifici parametri in micro-team. Questo programma solitamente situato nella ROM o inserito nella RAM da alcuni supporti esterni.

Il linguaggio assembly ha diverse caratteristiche che lo distinguono dai linguaggi di alto livello:

1. Questa è una corrispondenza biunivoca tra le istruzioni in linguaggio assembly e le istruzioni macchina.

2. Un programmatore in linguaggio assembly ha accesso a tutti gli oggetti e le istruzioni presenti sulla macchina target.

Comprendere le basi della programmazione nei linguaggi orientati alla macchina è utile per:



Migliore comprensione dell'architettura dei PC e uso più competente dei computer;

Sviluppare strutture più razionali di algoritmi per programmi per la risoluzione di problemi applicati;

La possibilità di visualizzare e correggere programmi eseguibili con estensione .exe e .com, compilati da qualsiasi linguaggio di alto livello, in caso di perdita dei programmi sorgente (chiamando i programmi specificati nel debugger del programma DEBUG e decompilando la loro visualizzazione in assembly lingua);

Compilazione di programmi per risolvere i problemi più critici (un programma scritto in un linguaggio orientato alla macchina è solitamente più efficace - più breve e più veloce nel 30-60% dei programmi ottenuti come risultato della traduzione da linguaggi di alto livello)

Implementare le procedure incluse nel programma principale sotto forma di frammenti separati nel caso in cui non possano essere implementate né nel linguaggio di alto livello utilizzato né utilizzando le procedure di servizio del sistema operativo.

Un programma in linguaggio assembly può essere eseguito solo su una famiglia di computer, mentre un programma scritto in un linguaggio di alto livello può potenzialmente essere eseguito su macchine diverse.

L'alfabeto del linguaggio assembly è composto da caratteri ASCII.

I numeri sono solo numeri interi. Ci sono:

I numeri binari terminano con la lettera B;

Numeri decimali che terminano con la lettera D;

I numeri esadecimali terminano con la lettera H.

RAM, registri, presentazione dei dati

Per una certa serie di parlamentari viene utilizzato un linguaggio di programmazione individuale: il linguaggio assembly.

Il linguaggio assembly occupa una posizione intermedia tra i codici macchina e i linguaggi di alto livello. La programmazione in questo linguaggio è più semplice. Un programma in linguaggio assembly fa un uso più efficiente delle capacità di una macchina specifica (più precisamente, un MP) rispetto a un programma in linguaggio di alto livello (che è più semplice per un programmatore che per un assemblatore). Diamo un'occhiata ai principi di base della programmazione in linguaggi orientati alla macchina usando l'esempio del linguaggio assembly per MP KR580VM80. Una metodologia generale viene utilizzata per programmare nella lingua. Tecniche tecniche specifiche per la registrazione dei programmi sono associate alle caratteristiche dell'architettura e del sistema di comando dell'MP target.

Modello software sistema a microprocessore basato su MP KR580VM80

Modello software dell'MPS secondo la Figura 1

Memoria delle porte MP

S Z AC. P C

Immagine 1

Dal punto di vista del programmatore, l'MP KR580VM80 dispone dei seguenti registri accessibili dal programma.

UN– Registro accumulatore a 8 bit. È il registro principale del MP. Qualsiasi operazione eseguita in una ALU comporta l'inserimento nell'accumulatore di uno degli operandi da elaborare. Il risultato di un'operazione nell'ALU viene solitamente memorizzato anche in A.

B, C, D, E, H, L– Registri di uso generale (GPR) a 8 bit. Memoria interiore deputato. Progettato per memorizzare le informazioni elaborate, nonché i risultati dell'operazione. Quando si elaborano parole a 16 bit, i registri formano coppie BC, DE, HL e il doppio registro è chiamato la prima lettera: B, D, H. In una coppia di registri, il più alto è il primo registro. I registri H e L hanno una proprietà speciale, utilizzata sia per memorizzare dati che per memorizzare indirizzi a 16 bit di celle RAM.

Florida– registro flag (registro dei segni) registro a 8 bit in cui sono memorizzati cinque segni del risultato dell'esecuzione di operazioni aritmetiche e logiche nell'MP. Formato FL secondo l'immagine

Bit C (CY - riporto) - riporto, impostato a 1 se si è verificato un riporto dall'ordine più alto del byte durante l'esecuzione di operazioni aritmetiche.

Bit P (parità) – parità, impostato a 1 se il numero di uno nei bit del risultato è pari.

La cifra AC è un riporto aggiuntivo, progettato per memorizzare il valore del riporto dal tetrado di ordine inferiore del risultato.

Bit Z (zero) – impostato a 1 se il risultato dell'operazione è 0.

Bit S (segno) – è impostato su 1 se il risultato è negativo e su 0 se il risultato è positivo.

SP– stack pointer, un registro a 16 bit, progettato per memorizzare l'indirizzo della cella di memoria in cui è stato scritto l'ultimo byte inserito nello stack.

RS– program counter (contatore di programma), un registro a 16 bit, progettato per memorizzare l'indirizzo della successiva istruzione da eseguire. Il contenuto del contatore del programma viene automaticamente incrementato di 1 immediatamente dopo aver recuperato il byte di istruzione successivo.

Nell'area di memoria iniziale dell'indirizzo 0000Н – 07FF si trova programma di controllo e programmi dimostrativi. Questa è l'area ROM.

0800 – 0AFF - area indirizzi per la registrazione dei programmi in studio. (RAM).

0В00 – 0ВВ0 - area di indirizzi per la scrittura dei dati. (RAM).

0ВВ0 – indirizzo iniziale dello stack. (RAM).

Uno stack è un'area di RAM appositamente organizzata destinata alla memorizzazione temporanea di dati o indirizzi. L'ultimo numero scritto nello stack viene estratto per primo. Il puntatore dello stack memorizza l'indirizzo dell'ultima cella dello stack in cui sono scritte le informazioni. Quando viene richiamata una subroutine, l'indirizzo di ritorno al programma principale viene automaticamente memorizzato nello stack. Di norma, all'inizio di ciascuna subroutine il contenuto di tutti i registri coinvolti nella sua esecuzione viene salvato nello stack e alla fine della subroutine viene ripristinato dallo stack.

Formato dei dati e struttura dei comandi del linguaggio assembly

La memoria dell'MP KR580VM80 è un array di parole di 8 bit chiamate byte.Ogni byte ha il proprio indirizzo di 16 bit, che determina la sua posizione nella sequenza delle celle di memoria. L'MP può indirizzare 65536 byte di memoria, che possono essere contenuti sia nella ROM che nella RAM.

Formato dei dati

I dati vengono archiviati in memoria come parole da 8 bit:

D7 D6 D5 D4 D3 D2 D1 D0

Il bit meno significativo è il bit 0, il bit più significativo è il bit 7.

Un comando è caratterizzato dal suo formato, cioè dal numero di bit ad esso assegnati, che sono divisi byte per byte in determinati campi funzionali.

Formato del comando

I comandi MP KR580VM80 hanno il formato a uno, due o tre byte. I comandi multibyte devono essere inseriti in lingue adiacenti. Il formato del comando dipende dalle specifiche dell'operazione eseguita.

Il primo byte del comando contiene il codice dell'operazione, scritto in forma mnemonica.

Determina il formato del comando e le azioni che devono essere eseguite dall'MP sui dati durante la sua esecuzione, nonché il metodo di indirizzamento e può anche contenere informazioni sulla posizione dei dati.

Il secondo e il terzo byte possono contenere dati su cui vengono eseguite le operazioni o indirizzi che indicano la posizione dei dati. I dati su cui vengono eseguite le azioni sono chiamati operandi.

Formato del comando a byte singolo secondo la Figura 2

Figura 4

Nei comandi in linguaggio assembly, il codice operativo ha una forma abbreviata di scrittura delle parole inglesi: una notazione mnemonica. La mnemonica (dal greco mnemonico - l'arte della memorizzazione) rende più facile ricordare i comandi in base al loro scopo funzionale.

Prima dell'esecuzione, il programma sorgente viene tradotto utilizzando un programma di traduzione chiamato assembler nel linguaggio delle combinazioni di codici - linguaggio macchina, in questa forma viene inserito nella memoria dell'MP e viene quindi utilizzato durante l'esecuzione del comando.


Metodi di indirizzamento

Tutti i codici degli operandi (input e output) devono trovarsi da qualche parte. Possono essere posizionati nei registri interni dell'MP (i più convenienti e opzione rapida). Possono trovarsi nella memoria di sistema (l'opzione più comune). Infine, possono trovarsi nei dispositivi I/O (il caso più raro). La posizione degli operandi è determinata dal codice istruzione. Esistere metodi diversi, con il quale il codice istruzione può determinare dove prendere l'operando di input e dove posizionare l'operando di output. Questi metodi sono chiamati metodi di indirizzamento.

Per MP KR580VM80 esistono i seguenti metodi di indirizzamento:

Diretto;

Registrati;

Indiretto;

Impilato.

Diretto l'indirizzamento presuppone che l'operando (di input) si trovi in ​​memoria immediatamente dopo il codice dell'istruzione. L'operando è solitamente una costante che deve essere inviata da qualche parte, aggiunta a qualcosa, ecc. i dati sono contenuti nel secondo o nel secondo e terzo byte del comando, con il byte basso di dati situato nel secondo byte del comando, e il byte alto nel terzo byte di comando.

Dritto L'indirizzamento (noto anche come assoluto) presuppone che l'operando (input o output) si trovi in ​​memoria all'indirizzo, il cui codice si trova all'interno del programma immediatamente dopo il codice dell'istruzione. Utilizzato nei comandi a tre byte.

Registrati l'indirizzamento presuppone che l'operando (ingresso o uscita) sia nel registro interno dell'MP. Utilizzato nei comandi a byte singolo

Indiretto L'indirizzamento (implicito) presuppone che il registro interno dell'MP contenga non l'operando stesso, ma il suo indirizzo in memoria.

Pila l'indirizzamento presuppone che il comando non contenga un indirizzo. Indirizzamento delle celle di memoria utilizzando il contenuto del registro SP a 16 bit (stack pointer).

Sistema di comando

Il sistema di comando dell'MP è un elenco completo di azioni elementari che l'MP è in grado di eseguire. L'MP controllato da questi comandi esegue azioni semplici, come operazioni aritmetiche e logiche elementari, trasferimento di dati, confronto di due valori, ecc. Il numero di comandi dell'MP KR580VM80 è 78 (tenendo conto delle modifiche 244).

Si distinguono i seguenti gruppi di comandi:

Trasmissione dati;

Aritmetica;

Rompicapo;

Comandi di salto;

Comandi di input/output, controllo e stack.


Simboli e abbreviazioni utilizzati nella descrizione dei comandi e nella composizione dei programmi

Simbolo Riduzione
INDIRIZZO Indirizzo a 16 bit
DATI Dati a 8 bit
DATI 16 Dati a 16 bit
PORTA Indirizzo del dispositivo I/O a 8 bit
BYTE2 Secondo byte del comando
BYTE 3 Terzo byte di comando
R, R1, R2 Uno dei registri: A, B, C, D, E, H, L
R.P. Una delle coppie di registri: B - specifica la coppia BC; D - specifica una coppia DE; H – specifica la coppia HL
RH Primo registro della coppia
R.L. Secondo registro della coppia
Λ Moltiplicazione logica
V Aggiunta logica
Addizione modulo due
M Una cella di memoria il cui indirizzo specifica il contenuto della coppia di registri HL, ovvero M = (HL)

1. Architettura del PC………………………………5

    1.1. Registri.

    1.1.1 Registri di carattere generale.

1.1.2. Registri di segmento

1.1.3 Registro delle bandiere

1.2. Organizzazione della memoria.

1.3. Presentazione dei dati.

1.3.1 Tipi di dati

1.3.2 Rappresentazione di caratteri e stringhe

2. Istruzioni di programma in assembler ………………

    1. Comandi in linguaggio assembly

2.2. Modalità di indirizzamento e formati delle istruzioni macchina

3. Pseudo-operatori…………………………….

3.1 Direttive sulla definizione dei dati

3.2 Struttura di un programma assembler

3.2.1 Segmenti di programma. assumere direttiva

3.2.3 Direttiva sulla segmentazione semplificata

4. Assemblaggio e composizione del programma …………….

5. Comandi di trasferimento dati…………….

    5.1 Comandi generali

    5.2 Comandi di stack

5.3 Comandi I/O

5.4 Comandi di inoltro indirizzi

5.5 Comandi di inoltro flag

6. Comandi aritmetici…………………………….

    6.1 Operazioni aritmetiche su interi binari

6.1.1 Addizione e sottrazione

6.1.2 Comandi per incrementare e decrementare il ricevitore di uno

6.2 Moltiplicazione e divisione

6.3 Cambio di segno

7. Operazioni logiche…………………………….

8. Turni e turni ciclici…………………..

9. Operazioni sulle stringhe…………….

10. Logica e organizzazione dei programmi…………….

10.1 Salti incondizionati

10.2 Salti condizionati

10.4 Procedure in linguaggio assembly

10.5 Interrupt INT

10.6 Software di sistema

10.6.1.1 Lettura della tastiera.

10.6.1.2 Visualizzazione dei caratteri sullo schermo

10.6.1.3 Fine dei programmi.

10.6.2.1 Selezione delle modalità di visualizzazione

11. Memoria del disco………………………………………..

11.2 Tabella di distribuzione dei file

11.3 Operazioni di I/O del disco

11.3.1 Scrittura di un file su disco

11.3.1.1 Dati ASCIIZ

11.3.1.2 Numero del file

11.3.1.3 Creazione di un file su disco

11.3.2 Lettura di un file su disco

introduzione

Il linguaggio assembly è una rappresentazione simbolica del linguaggio macchina. Tutti i processi in un personal computer (PC) al livello hardware più basso sono guidati solo da comandi (istruzioni) in linguaggio macchina. È impossibile risolvere veramente problemi legati all'hardware (o anche, inoltre, dipendenti dall'hardware, come l'aumento della velocità di un programma), senza la conoscenza dell'assemblatore.

L'assemblatore è una forma conveniente di comandi direttamente per i componenti del PC e richiede la conoscenza delle proprietà e delle capacità del circuito integrato contenente questi componenti, vale a dire il microprocessore del PC. Pertanto, il linguaggio assembly è direttamente correlato all'organizzazione interna del PC. E non è un caso che quasi tutti i compilatori di linguaggi di alto livello supportino l'accesso al livello di programmazione assembly.

Un elemento della formazione di un programmatore professionista è necessariamente lo studio dell'assemblatore. Questo perché la programmazione in linguaggio assembly richiede la conoscenza dell'architettura del PC, che consente di creare programmi più efficienti in altri linguaggi e combinarli con programmi in linguaggio assembly.

Il manuale tratta la programmazione in linguaggio assembly per computer basati su microprocessori Intel.

Questo tutorial è rivolto a tutti coloro che sono interessati all'architettura del processore e alle basi della programmazione in linguaggio Assembly, principalmente agli sviluppatori di prodotti software.

    Architettura del computer.

L'architettura del computer è una rappresentazione astratta di un computer, che riflette la sua organizzazione strutturale, circuitale e logica.

Tutti i computer moderni hanno alcune proprietà architettoniche comuni e individuali. Le singole proprietà sono univoche per uno specifico modello di computer.

Il concetto di architettura del computer comprende:

    diagramma a blocchi del computer;

    mezzi e modalità di accesso agli elementi dello schema a blocchi del computer;

    insieme e disponibilità dei registri;

    organizzazione e modalità di indirizzamento;

    modalità di presentazione e formato dei dati informatici;

    una serie di istruzioni per la macchina del computer;

    formati di istruzioni macchina;

    gestione delle interruzioni.

Gli elementi principali dell'hardware del computer: unità di sistema, tastiera, dispositivi di visualizzazione, unità disco, dispositivi di stampa (stampante) e varie apparecchiature di comunicazione. Unità di sistemaè composto da scheda madre, alimentatore e celle di espansione per schede aggiuntive. La scheda di sistema contiene un microprocessore, memoria di sola lettura (ROM), RAM(RAM) e coprocessore.

      Registri.

All'interno del microprocessore, le informazioni sono contenute in un gruppo di 32 registri (16 utente, 16 sistema), in un modo o nell'altro, disponibili per l'uso da parte del programmatore. Poiché il manuale è dedicato alla programmazione del microprocessore 8088-i486, è logico iniziare questo argomento con una discussione sui registri interni del microprocessore accessibili all'utente.

I registri utente vengono utilizzati dal programmatore per scrivere programmi. Questi registri includono:

    otto registri a 32 bit (registri per uso generale) EAX/AX/AH/AL, EBX/BX/BH/BL, ECX/CX/CH/CL, EDX/DX/DLH/DL, EBP/BP, ESI/SI, EDI/DI, ESP/SP;

    sei registri di segmenti a 16 bit: CS, DS, SS, ES, FS, GS;

    registri di stato e di controllo: registro flag EFLAGS/FLAGS e registro puntatore comando EIP/IP.

Parti di un registro a 32 bit sono indicate da una barra. Il prefisso E (Extended) indica l'utilizzo di un registro a 32 bit. Per lavorare con i byte, vengono utilizzati i registri con i prefissi L (basso) e H(alto), ad esempio AL, CH, che denota i byte bassi e alti delle parti a 16 bit dei registri.

        Registri per scopi generali.

EAX/AX/AH/AL(registro dell'accumulatore) – batteria. Utilizzato nelle moltiplicazioni e divisioni, nelle operazioni di I/O e in alcune operazioni sulle stringhe.

EBX/BX/BH/BL – registro di base(registro base), spesso utilizzato quando si indirizzano i dati in memoria.

ECX/CX/CH/CL – contatore(registro di conteggio), utilizzato come contatore del numero di ripetizioni del ciclo.

EDX/DX/DH/DL – registro dati(registro dati), utilizzato per memorizzare dati intermedi. In alcune squadre, il suo utilizzo è obbligatorio.

Tutti i registri di questo gruppo consentono l'accesso alle loro parti “inferiori”. Solo le parti inferiori a 16 e 8 bit di questi registri possono essere utilizzate per l'autoindirizzamento. I 16 bit superiori di questi registri non sono disponibili come oggetti indipendenti.

Per supportare i comandi di elaborazione di stringhe che consentono l'elaborazione sequenziale di catene di elementi con lunghezza di 32, 16 o 8 bit, vengono utilizzati:

ESI/SI (registro indice sorgente) – indice fonte. Contiene l'indirizzo dell'elemento sorgente corrente.

EDI/DI (registro indice di destinazione) – indice ricevitore(destinatario). Contiene l'indirizzo corrente nella riga di destinazione.

Nell’architettura del microprocessore, una struttura dati – uno stack – è supportata a livello hardware e software. Esistono istruzioni speciali e registri speciali per lavorare con lo stack. Va notato che lo stack viene riempito verso indirizzi più piccoli.

ESP/SP (registro puntatore stack) – Registrati puntatore pila. Contiene un puntatore alla parte superiore dello stack nel segmento dello stack corrente.

EBP/BP (registro puntatore base) – registro del puntatore base dello stack. Progettato per organizzare l'accesso casuale ai dati all'interno dello stack.

1.1.2. Registri di segmento

Il modello software del microprocessore ne ha sei registri di segmento: CS, SS, DS, ES, GS, FS. La loro esistenza è dovuta alla specifica organizzazione e utilizzo della RAM da parte dei microprocessori Intel. L'hardware del microprocessore supporta l'organizzazione strutturale del programma composto da segmenti. Per indicare i segmenti disponibili in questo momento sono previsti i registri di segmento. Il microprocessore supporta i seguenti tipi di segmenti:

    Segmento di codice. Contiene i comandi del programma. Per accedere a questo segmento, utilizzare il registro CS (registro del segmento di codice) - registro del codice del segmento. Contiene l'indirizzo del segmento di istruzione macchina a cui ha accesso il microprocessore.

    Segmento dati. Contiene i dati elaborati dal programma. Per accedere a questo segmento, utilizzare il registro DS (registro del segmento dati) - registro dei dati del segmento, che memorizza l'indirizzo del segmento di dati del programma corrente.

    Segmento di pila. Questo segmento è un'area di memoria chiamata stack. Il microprocessore organizza lo stack secondo il principio: primo “in”, primo “out”. Per accedere allo stack, utilizzare il registro SS (registro del segmento dello stack) - registro del segmento dello stack, contenente l'indirizzo del segmento dello stack.

    Segmento dati aggiuntivo. I dati elaborati possono essere localizzati in tre segmenti dati aggiuntivi. Per impostazione predefinita, si presuppone che i dati si trovino nel segmento dati. Quando si utilizzano segmenti di dati aggiuntivi, i relativi indirizzi devono essere specificati esplicitamente utilizzando speciali prefissi di sovrascrittura dei segmenti nel comando. Gli indirizzi dei segmenti dati aggiuntivi devono essere contenuti nei registri ES, GS, FS (registri dei segmenti dati di estensione).

        Registri di controllo e di stato

Il microprocessore contiene diversi registri che contengono informazioni sullo stato sia del microprocessore stesso che del programma i cui comandi sono attualmente caricati nella pipeline. Questo:

Registro puntatore istruzione EIP/IP;

    registro flag EFLAGS/FLAGS.

Utilizzando questi registri è possibile ottenere informazioni sui risultati dell'esecuzione del comando e influenzare lo stato del microprocessore stesso.

EIP/IP (registro puntatore istruzioni) – puntatore squadre. Il registro EIP/IP è largo 32 bit o 16 bit e contiene l'offset della successiva istruzione da eseguire rispetto al contenuto del registro del segmento CS nel segmento dell'istruzione corrente. Questo registro non è direttamente accessibile, ma può essere modificato utilizzando le istruzioni di salto.

EFLAGS/FLAGS (registro delle bandiere) – Registrati bandiere. Dimensione bit 32/16 bit. I singoli bit di questo registro hanno uno scopo funzionale specifico e sono chiamati flag. Un flag è un bit che assume il valore 1 ("flag impostato") se viene soddisfatta una condizione e il valore 0 ("flag cancellato") altrimenti. La parte bassa di questo registro è del tutto simile al registro FLAGS per i8086.

1.1.3 Registro delle bandiere

Il registro dei flag è a 32 bit e si chiama EFLAGS (Fig. 1). I singoli bit del registro hanno uno scopo funzionale specifico e sono chiamati flag. A ciascuno di essi viene assegnato un nome specifico (ZF, CF, ecc.). I 16 bit inferiori di EFLAGS rappresentano il registro flag FLAGS a 16 bit utilizzato durante l'esecuzione di programmi scritti per i microprocessori i086 e i286.

Fig.1 Registro delle bandiere

Alcuni flag sono comunemente chiamati flag di condizione; cambiano automaticamente quando i comandi vengono eseguiti e registrano alcune proprietà del loro risultato (ad esempio, se è uguale a zero). Altre bandiere sono chiamate bandiere di stato; cambiano rispetto al programma e influenzano l'ulteriore comportamento del processore (ad esempio bloccano gli interrupt).

Flag di condizione:

CF (porta bandiera) - portare bandiera. Prende il valore 1 se, sommando interi, appariva un'unità di riporto che non "si adattava" alla griglia di bit, o se, sottraendo numeri senza segno, il primo di essi era inferiore al secondo. Nei comandi di spostamento, il bit esterno alla griglia di bit viene inserito in CF. CF cattura anche le caratteristiche dell'istruzione di moltiplicazione.

OF (flag di overflow) - bandiera di trabocco. Impostare su 1 se, sommando o sottraendo numeri interi con segno, il risultato è un risultato che supera il valore consentito in valore assoluto (la mantissa è traboccata e si è “arrampicata” sulla cifra del segno).

ZF (bandiera zero) - bandiera zero. Impostato su 1 se il risultato del comando è 0.

SF (bandiera di segnale) - bandiera cartello. Impostato a 1 se un'operazione su numeri con segno produce un risultato negativo.

PF (bandiera di parità) - bandiera parità. Uguale a 1 se il risultato del comando successivo contiene un numero pari di unità binarie. Solitamente preso in considerazione solo per le operazioni di I/O.

AF (bandiera di trasporto ausiliario) - bandiera di trasporto extra. Corregge le funzionalità di esecuzione di operazioni su numeri decimali binari.

Bandiere di stato:

DF (bandiera di direzione) - bandiera di direzione. Imposta la direzione di visualizzazione delle linee nei comandi di linea: quando DF=0, le linee vengono visualizzate "in avanti" (dall'inizio alla fine), quando DF=1 - nella direzione opposta.

IOPL (livello di privilegio di input/output) – Livello di privilegio I/O. Utilizzato nella modalità protetta del funzionamento del microprocessore per controllare l'accesso ai comandi I/O, a seconda dei privilegi dell'attività.

NT (attività annidata) – flag di nidificazione delle attività. Utilizzato nella modalità protetta del funzionamento del microprocessore per registrare il fatto che un'attività è annidata all'interno di un'altra.

Indicatore di sistema:

SE (flag di interruzione) - flag di interruzione. Quando IF=0, il processore smette di rispondere agli interrupt in ingresso; quando IF=1, il blocco degli interrupt viene rimosso.

TF (flag trappola) - bandiera di traccia. Quando TF=1, dopo aver eseguito ciascun comando, il processore effettua un'interruzione (numerata 1), che può essere utilizzata durante il debug di un programma per tracciarlo.

RF (flag di ripresa) – riprendere la bandiera. Utilizzato durante l'elaborazione degli interrupt dai registri di debug.

VM (modalità virtuale 8086) – bandiera virtuale 8086. 1 processore funziona in modalità virtuale 8086. 0 processori funzionano in modalità reale o protetta.

AC (controllo dell'allineamento) – flag di controllo dell'allineamento. Progettato per consentire il controllo dell'allineamento durante l'accesso alla memoria.

      Organizzazione della memoria.

Viene chiamata la memoria fisica a cui ha accesso il microprocessore RAM ( o memoria ad accesso casuale - RAM). La RAM è una catena di byte che hanno il proprio indirizzo univoco (il suo numero), chiamato fisico. L'intervallo dei valori dell'indirizzo fisico va da 0 a 4 GB. Il meccanismo di gestione della memoria è interamente hardware.

L'hardware del microprocessore supporta diversi modelli di utilizzo della RAM:

    modello segmentato. In questo modello, la memoria per i programmi è divisa in aree di memoria contigue (segmenti) e il programma stesso può accedere solo ai dati che si trovano in questi segmenti;

    modello di pagina. In questo caso la RAM viene considerata come un insieme di blocchi di dimensione fissa pari a 4 KB. L’applicazione principale di questo modello è legata all’organizzazione memoria virtuale, che consente ai programmi di utilizzare più spazio di memoria rispetto alla memoria fisica. Per un microprocessore Pentium, la dimensione della memoria virtuale possibile può raggiungere i 4 TB.

L'utilizzo e l'implementazione di questi modelli dipende dalla modalità operativa del microprocessore:

    Modalità indirizzo reale (modalità reale). La modalità è simile al funzionamento del processore i8086. Necessario per il funzionamento dei programmi sviluppati per i primi modelli di processore.

    Modalità protetta. La modalità protetta ti consente di fare più cose contemporaneamente elaborazione delle informazioni, protezione della memoria utilizzando un meccanismo di privilegi a quattro livelli e la sua organizzazione di paging.

    Modalità 8086 virtuale. In questa modalità diventa possibile eseguire diversi programmi per l'i8086. In questo caso, i programmi in modalità reale possono funzionare.

La segmentazione è un meccanismo di indirizzamento che garantisce l'esistenza di diversi spazi di indirizzi indipendenti. Un segmento è un blocco di memoria indipendente, supportato dall'hardware.

Ogni programma può generalmente consistere di un numero qualsiasi di segmenti, ma ha accesso diretto a tre segmenti principali: codice, dati e stack - e da uno a tre segmenti dati aggiuntivi. Il sistema operativo colloca i segmenti di programma nella RAM in indirizzi fisici specifici, quindi inserisce i valori di questi indirizzi nei registri appropriati. All'interno di un segmento, il programma accede agli indirizzi relativi all'inizio del segmento in modo lineare, cioè partendo dall'indirizzo 0 e terminando con un indirizzo uguale alla dimensione del segmento. Indirizzo relativo o pregiudizio, che il microprocessore utilizza per accedere ai dati all'interno di un segmento viene chiamato efficace.

Formazione di un indirizzo fisico in modalità reale

In modalità reale, l'intervallo di modifiche nell'indirizzo fisico va da 0 a 1 MB. La dimensione massima del segmento è 64 KB. Quando si contatta uno specifico indirizzo fisico La RAM è determinata dall'indirizzo dell'inizio del segmento e dall'offset all'interno del segmento. L'indirizzo iniziale del segmento viene preso dal registro del segmento corrispondente. In questo caso il registro del segmento contiene solo i 16 bit più significativi dell'indirizzo fisico di inizio segmento. I quattro bit bassi mancanti dell'indirizzo a 20 bit si ottengono spostando il valore del registro del segmento a sinistra di 4 bit. L'operazione di spostamento viene eseguita nell'hardware. Il valore risultante di 20 bit è l'indirizzo fisico reale corrispondente all'inizio del segmento. Questo è indirizzo fisicoè specificato come una coppia "segmento:offset", dove "segmento" sono i primi 16 bit dell'indirizzo iniziale del segmento di memoria a cui appartiene la cella e "offset" è l'indirizzo a 16 bit di questa cella, contato da l'inizio di questo segmento di memoria (il valore 16 * segmento +offset fornisce l'indirizzo assoluto della cella). Se, ad esempio, il registro CS memorizza il valore 1234h, allora la coppia di indirizzi 1234h:507h definisce un indirizzo assoluto pari a 16*1234h+507h =12340h+507h = 12847h. Tale coppia è scritta come una doppia parola e (come per i numeri) in una forma "invertita": la prima parola contiene un offset e la seconda un segmento, e ciascuna di queste parole, a sua volta, è presentata in una forma “invertita”. Ad esempio, la coppia 1234h:5678h verrebbe scritta così:| 78| 56| 34| 12|.

Questo meccanismo di generazione di un indirizzo fisico consente di rendere il software rilocabile, cioè indipendente da specifici indirizzi di caricamento nella RAM.

La programmazione a livello di istruzione macchina è il livello minimo al quale è possibile scrivere i programmi. Il sistema di istruzioni della macchina deve essere sufficiente per implementare le azioni richieste impartendo istruzioni all'hardware del computer.

Ogni comando macchina è composto da due parti:

  • operativo: determinare "cosa fare";
  • operando: definisce gli oggetti di elaborazione, "cosa fare con".

Il comando della macchina a microprocessore, scritto in linguaggio assembly, è una riga con la seguente forma sintattica:

etichetta di comando/direttiva operando(i) ;commenti

In questo caso, il campo obbligatorio nella riga è un comando o una direttiva.

L'etichetta, il comando/direttiva e gli operandi (se presenti) sono separati da almeno uno spazio o un carattere di tabulazione.

Se è necessario continuare un comando o una direttiva nella riga successiva, viene utilizzato il carattere barra rovesciata: \.

Per impostazione predefinita, il linguaggio assembly non distingue tra lettere maiuscole e minuscole durante la scrittura di comandi o direttive.

Righe di codice di esempio:

Contare db 1 ;Nome, direttiva, un operando
movimento eax,0 ;Comando, due operandi
cbw; Squadra

Tag

Etichetta in linguaggio assembly può contenere i seguenti simboli:

  • tutte le lettere dell'alfabeto latino;
  • numeri da 0 a 9;
  • caratteri speciali: _, @, $, ?.

È possibile utilizzare un punto come primo carattere di un'etichetta, ma alcuni compilatori sconsigliano l'utilizzo di questo carattere. I nomi assembler riservati (direttive, operatori, nomi di comandi) non possono essere utilizzati come etichette.

Il primo carattere dell'etichetta deve essere una lettera o un carattere speciale (ma non un numero). Lunghezza massima tag – 31 caratteri. Tutte le etichette scritte su una riga che non contiene una direttiva assembler devono terminare con i due punti: .

Squadre

Squadra dice al traduttore quale azione dovrebbe eseguire il microprocessore. In un segmento di dati, un comando (o una direttiva) definisce un campo, un'area di lavoro o una costante. In un segmento di codice, un comando specifica un'azione, come lo spostamento (mov) o l'aggiunta (add).

Direttive

L'assemblatore ha una serie di operatori che consentono di controllare il processo di assemblaggio ed elencazione. Questi operatori vengono chiamati direttive . Agiscono solo durante il processo di assemblaggio del programma e, a differenza dei comandi, non generano codice macchina.

Operandi

Operando – un oggetto su cui viene eseguito un comando macchina o un'istruzione del linguaggio di programmazione.
Un'istruzione può avere uno o due operandi oppure nessun operando. Il numero di operandi è implicitamente specificato dal codice istruzione.
Esempi:

  • Nessun operando ret ;Return
  • Un operando inc ecx ;Aumenta ecx
  • Due operandi aggiungono eax,12 ;Aggiungi 12 a eax

L'etichetta, il comando (direttiva) e l'operando non devono iniziare in una posizione particolare nella riga. Si consiglia comunque di scriverli in colonna per facilitare la lettura del programma.

Gli operandi possono essere

  • identificatori;
  • stringhe di caratteri racchiuse tra virgolette singole o doppie;
  • numeri interi nei sistemi numerici binario, ottale, decimale o esadecimale.
Identificatori

Identificatori – sequenze di caratteri validi utilizzati per denotare oggetti del programma come codici operativi, nomi di variabili e nomi di etichette.

Regole per la registrazione degli identificatori.

  • L'identificatore può essere costituito da uno o più caratteri.
  • Come simboli puoi usare lettere dell'alfabeto latino, numeri e alcuni caratteri speciali: _, ?, $, @.
  • Un identificatore non può iniziare con un carattere numerico.
  • La lunghezza dell'identificatore può contenere fino a 255 caratteri.
  • Il traduttore accetta i primi 32 caratteri dell'identificatore e ignora il resto.
Commenti

I commenti sono separati dalla riga dell'eseguibile da un carattere; . In questo caso tutto ciò che è scritto dopo il punto e virgola e fino alla fine della riga è un commento. L'uso dei commenti in un programma ne migliora la chiarezza, soprattutto quando lo scopo di un insieme di comandi non è chiaro. Il commento può contenere qualsiasi carattere stampabile, compresi gli spazi. Un commento può occupare l'intera riga o seguire un comando sulla stessa riga.

Struttura del programma di assemblaggio

Un programma scritto in linguaggio assembly può essere composto da più parti chiamate moduli . Per ogni modulo possono essere definiti uno o più segmenti di dati, stack e codice. Qualsiasi programma assembler completo deve includere un modulo principale, o principale, da cui inizia la sua esecuzione. Un modulo può contenere segmenti di codice, segmenti di dati e segmenti di stack, dichiarati utilizzando direttive appropriate. Prima di dichiarare i segmenti, è necessario specificare il modello di memoria utilizzando la direttiva .MODEL.

Un esempio di un programma “non fare nulla” in linguaggio assembly:

686P
.MODELLO PIATTO, STDCALL
.DATI
.CODICE
INIZIO:

RET
FINE INIZIO

Questo programma contiene solo un comando del microprocessore. Questo comando è RET. Garantisce che il programma termini correttamente. In generale, questo comando viene utilizzato per uscire da una procedura.
Il resto del programma riguarda il funzionamento del traduttore.
.686P - Sono consentiti i comandi in modalità protetta Pentium 6 (Pentium II). Questa direttiva seleziona il set supportato di istruzioni assembler, indicando il modello del processore. La lettera P indicata alla fine della direttiva informa il traduttore che il processore sta operando in modalità protetta.
.MODEL FLAT, stdcall - modello di memoria flat. Questo modello di memoria viene utilizzato in sala operatoria Sistema Windows. stdcall
.DATA è un segmento di programma contenente dati.
.CODE è un blocco di programma contenente codice.
INIZIO - etichetta. Nell'assemblatore, i tag svolgono un ruolo importante, cosa che non si può dire dei moderni linguaggi di alto livello.
END START - la fine del programma e un messaggio al traduttore che informa che l'esecuzione del programma dovrebbe iniziare con l'etichetta START.
Ogni modulo deve contenere una direttiva END per contrassegnare la fine codice sorgente programmi. Tutte le righe che seguono la direttiva END vengono ignorate. Se si omette la direttiva END, viene generato un errore.
L'etichetta specificata dopo la direttiva END comunica al traduttore il nome del modulo principale da cui inizia l'esecuzione del programma. Se il programma contiene un modulo, l'etichetta dopo la direttiva END può essere omessa.




Superiore