Casa / Spam / Riassunto: Piano: Prefazione. Linguaggio di assemblaggio e struttura dell'istruzione. La struttura del file exe (analisi semantica). Comandi in linguaggio assembly (lezione) Comandi tabella in linguaggio assembly

Riassunto: Piano: Prefazione. Linguaggio di assemblaggio e struttura dell'istruzione. La struttura del file exe (analisi semantica). Comandi in linguaggio assembly (lezione) Comandi tabella in linguaggio assembly

Struttura delle istruzioni in linguaggio assembly La programmazione a livello di istruzioni macchina è il livello minimo al quale è possibile la programmazione del computer. Il sistema di istruzioni della macchina deve essere sufficiente per attuare le azioni richieste impartendo istruzioni all'hardware della macchina. Ogni istruzione macchina è composta da due parti: una parte operativa che definisce “cosa fare” e un operando che definisce gli oggetti di elaborazione, ovvero “cosa fare”. L'istruzione macchina del microprocessore, scritta in linguaggio Assembly, è un'unica riga, avente la forma seguente: etichetta istruzione/operandi direttivi ; commenti L'etichetta, il comando/direttiva e l'operando sono separati da almeno uno spazio o un carattere di tabulazione. Gli operandi dell'istruzione sono separati da virgole.

Struttura di un'istruzione in linguaggio assembly Un'istruzione in linguaggio assembly dice al compilatore quale azione deve eseguire il microprocessore. Le direttive di assemblaggio sono parametri specificati nel testo del programma che influiscono sul processo di assemblaggio o sulle proprietà del file di output. L'operando specifica il valore iniziale dei dati (nel segmento dati) o gli elementi su cui deve agire l'istruzione (nel segmento di codice). Un'istruzione può avere uno o due operandi o nessun operando. Il numero di operandi è implicitamente specificato dal codice dell'istruzione. Se è necessario continuare il comando o la direttiva nella riga successiva, viene utilizzato il carattere barra rovesciata: "" . Per impostazione predefinita, l'assembler non distingue tra lettere maiuscole e minuscole nei comandi e nelle direttive. Esempi di direttive e comandi Conteggio db 1 ; Nome, direttiva, un operando mov eax, 0 ; Comando, due operandi

Gli identificatori sono sequenze di caratteri validi utilizzati per designare nomi di variabili e nomi di etichette. L'identificatore può essere costituito da uno o più dei seguenti caratteri: tutte le lettere dell'alfabeto latino; numeri da 0 a 9; caratteri speciali: _, @, $, ? . Un punto può essere utilizzato come primo carattere dell'etichetta. I nomi degli assembler riservati (direttive, operatori, nomi dei comandi) non possono essere utilizzati come identificatori. Il primo carattere dell'identificatore deve essere una lettera o un carattere speciale. La lunghezza massima dell'identificatore è di 255 caratteri, ma il traduttore accetta i primi 32 caratteri e ignora il resto. Tutte le etichette scritte su una riga che non contiene una direttiva assembler devono terminare con i due punti ":". L'etichetta, il comando (direttiva) e l'operando non devono iniziare in una posizione particolare nella stringa. Si consiglia di scriverli in una colonna per una maggiore leggibilità del programma.

Etichette Tutte le etichette scritte su una riga che non contiene una direttiva assembler devono terminare con i due punti ":". L'etichetta, il comando (direttiva) e l'operando non devono iniziare in una posizione particolare nella stringa. Si consiglia di scriverli in una colonna per una maggiore leggibilità del programma.

Commenti L'uso dei commenti in un programma ne migliora la chiarezza, soprattutto quando lo scopo di una serie di istruzioni non è chiaro. I commenti iniziano su qualsiasi riga di un modulo sorgente con un punto e virgola (;). Tutti i caratteri a destra di "; ' alla fine della riga ci sono commenti. Il commento può contenere qualsiasi carattere stampabile, incluso "spazio". Il commento può estendersi sull'intera riga o seguire il comando sulla stessa riga.

Struttura di un programma in linguaggio assembly Un programma in linguaggio assembly può essere composto da più parti, dette moduli, ognuna delle quali può definire uno o più segmenti di dati, stack e codice. Qualsiasi programma in linguaggio assembly completo deve includere un modulo principale, o principale, da cui inizia la sua esecuzione. Un modulo può contenere segmenti di programma, dati e stack dichiarati con le direttive appropriate.

Modelli di memoria Prima di dichiarare i segmenti, è necessario specificare il modello di memoria utilizzando una direttiva. MODEL modificatore memory_model, calling_convention, OS_type, stack_parameter Modelli di memoria in linguaggio assembly di base: Modello di memoria Indirizzamento del codice Indirizzamento dei dati Sistema operativo Interlacciamento del codice e dei dati PICCOLO VICINO a MS-DOS Valido SMALL VICINO a MS-DOS, Windows No MEDIO LONTANO VICINO a MS-DOS, Windows No COMPACT NEAR FAR MS-DOS, Windows No GRANDE FAR MS-DOS, Windows No ENORME FAR MS-DOS, Windows No NEAR Windows 2000, Windows XP, Windows Valid FLAT NEAR NT,

Modelli di memoria Il modello minuscolo funziona solo in applicazioni MS-DOS a 16 bit. In questo modello, tutti i dati e il codice risiedono in un segmento fisico. La dimensione del file di programma in questo caso non supera i 64 KB. Il modello piccolo supporta un segmento di codice e un segmento di dati. Dati e codice quando si utilizza questo modello sono indirizzati come vicino (vicino). Il modello medio supporta più segmenti di codice e un segmento di dati, con tutti i collegamenti nei segmenti di codice considerati lontani (lontani) per impostazione predefinita e i collegamenti nel segmento di dati sono considerati vicini (vicini). Il modello compatto supporta più segmenti di dati che utilizzano l'indirizzamento dei dati lontani (lontano) e un segmento di codice che utilizza l'indirizzamento dei dati vicini (vicino). Il modello grande supporta più segmenti di codice e più segmenti di dati. Per impostazione predefinita, tutti i riferimenti al codice e ai dati sono considerati lontani. Il modello enorme è quasi equivalente al modello di memoria grande.

Modelli di memoria Il modello flat presuppone una configurazione del programma non segmentata e viene utilizzato solo su sistemi operativi a 32 bit. Questo modello è simile al modello minuscolo in quanto i dati e il codice risiedono nello stesso segmento a 32 bit. Sviluppare un programma per il modello flat prima della direttiva. model flat dovrebbe posizionare una delle direttive: . 386, . 486, . 586 o. 686. La scelta della direttiva di selezione del processore determina l'insieme dei comandi disponibili durante la scrittura dei programmi. La lettera p dopo la direttiva sulla selezione del processore indica la modalità operativa protetta. L'indirizzamento dei dati e del codice è vicino, con tutti gli indirizzi e i puntatori a 32 bit.

modelli di memoria. MODEL modificatore memory_model, calling_convention, OS_type, stack_parameter Il parametro modificatore viene utilizzato per definire i tipi di segmento e può assumere i seguenti valori: use 16 (i segmenti del modello selezionato sono usati come 16 bit) use 32 (i segmenti del modello selezionato sono usati come 32 bit). Il parametro calling_convention viene utilizzato per determinare come vengono passati i parametri quando si chiama una procedura da altre lingue, incluse le lingue alto livello(C++, Pascal). Il parametro può assumere i seguenti valori: C, BASIC, FORTRAN, PASCAL, SYSCALL, STDCALL.

modelli di memoria. MODEL modificatore memory_model, calling_convention, OS_type, stack_parameter Il parametro OS_type è OS_DOS per impostazione predefinita e questo momento questo è l'unico valore supportato per questo parametro. Il parametro stack_param è impostato su: NEARSTACK (il registro SS è uguale a DS, i dati e le regioni dello stack si trovano nello stesso segmento fisico) FARSTACK (il registro SS non è uguale a DS, i dati e le regioni dello stack si trovano in segmenti fisici diversi). L'impostazione predefinita è NEARSTACK.

Un esempio di programma "non fare nulla". 686 P. MODELLO APPARTAMENTO, STDCALL. DATI. CODICE START: RET END START RET - comando a microprocessore. Garantisce la corretta conclusione del programma. Il resto del programma è legato al funzionamento del traduttore. . 686 P - Sono consentiti comandi in modalità protetta Pentium 6 (Pentium II). Questa direttiva seleziona il set di istruzioni assembler supportato specificando il modello del processore. . MODELLO FLAT, stdcall - modello flat memory. Questo modello di memoria viene utilizzato nel sistema operativo Windows. stdcall è la convenzione di chiamata della procedura da utilizzare.

Un esempio di programma "non fare nulla". 686 P. MODELLO APPARTAMENTO, STDCALL. DATI. CODICE INIZIO: RET FINE INIZIO . DATA - segmento di programma contenente dati. Questo programma non usa lo stack, quindi segmenta. STACK manca. . CODICE - un segmento del programma contenente il codice. INIZIO - etichetta. END START - la fine del programma e un messaggio al compilatore che il programma deve essere avviato dall'etichetta START. Ogni programma deve contenere una direttiva END che segna la fine del codice sorgente del programma. Tutte le righe che seguono la direttiva END vengono ignorate L'etichetta dopo la direttiva END indica al compilatore 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.

Traduttori di linguaggi assembly Un traduttore è un programma o hardware che converte un programma presentato in uno dei linguaggi di programmazione in un programma nella lingua di destinazione, chiamato codice oggetto. Oltre a supportare i mnemonici delle istruzioni automatiche, ogni traduttore ha il proprio insieme di direttive e macro, spesso incompatibili con qualsiasi altra cosa. I principali tipi di traduttori di linguaggio assembly sono: MASM (Microsoft Assembler), TASM (Borland Turbo Assembler), FASM (Flat Assembler) - un assemblatore multi-pass distribuito liberamente scritto da Tomasz Gryshtar (polacco), NASM (Netwide Assembler) - un assemblatore gratuito per l'architettura Intel x 86 è stato creato da Simon Tatham con Julian Hall ed è attualmente in fase di sviluppo da un piccolo team di sviluppo presso Source. Fucina. rete.

Src="https://present5.com/presentation/-29367016_63610977/image-15.jpg" alt="(!LANG:Traduzione del programma in Microsoft Visual Studio 2005 1) Creare un progetto selezionando File->Nuovo->Progetto menu e"> Трансляция программы в Microsoft Visual Studio 2005 1) Создать проект, выбрав меню File->New->Project и указав имя проекта (hello. prj) и тип проекта: Win 32 Project. В !} opzioni aggiuntive procedura guidata del progetto per specificare "Svuota progetto".

Src="https://present5.com/presentation/-29367016_63610977/image-16.jpg" alt="(!LANG:Traduzione del programma in Microsoft Visual Studio 2005 2) Nell'albero del progetto (Visualizza->Esplora soluzioni) aggiungere"> Трансляция программы в Microsoft Visual Studio 2005 2) В дереве проекта (View->Solution Explorer) добавить файл, в котором будет содержаться текст программы: Source. Files->Add->New. Item.!}

Traduzione del programma in Microsoft Visual Studio 2005 3) Selezionare il tipo di file Code C++, ma specificare il nome con estensione. asm:

Traduzione del programma in Microsoft Visual Studio 2005 5) Impostare le opzioni del compilatore. Seleziona sul pulsante destro nel menu del file di progetto Regole di compilazione personalizzate...

Traduzione del programma in Microsoft Visual Studio 2005 e nella finestra che compare selezionare Microsoft Macro Assembler.

Traduzione del programma in Microsoft Visual Studio 2005 Controllare con il tasto destro nel file ciao. asm dell'albero del progetto dal menu Proprietà e impostare Generale->Strumento: Microsoft Macro Assembler.

Src="https://present5.com/presentation/-29367016_63610977/image-22.jpg" alt="(!LANG:Traduzione del programma in Microsoft Visual Studio 2005 6) Compila il file selezionando Build->Build hello.prj ."> Трансляция программы в Microsoft Visual Studio 2005 6) Откомпилировать файл, выбрав Build->Build hello. prj. 7) Запустить программу, нажав F 5 или выбрав меню Debug->Start Debugging.!}

Programmazione nel sistema operativo Windows La programmazione nel sistema operativo Windows si basa sull'utilizzo di funzioni API (Application Program Interface, ovvero interfaccia applicazione software). Il loro numero raggiunge il 2000. Il programma per Windows consiste in gran parte in tali chiamate. Tutta l'interazione con dispositivi esterni e risorse del sistema operativo avviene, di norma, attraverso tali funzioni. sala operatoria Sistema Windows utilizza un modello di memoria piatta. L'indirizzo di qualsiasi locazione di memoria sarà determinato dal contenuto di un registro a 32 bit. Esistono 3 tipi di strutture del programma per Windows: finestra di dialogo (la finestra principale è una finestra di dialogo), struttura console o senza finestra, struttura classica (finestra, cornice).

Richiamo delle funzioni API di Windows Nel file della guida, qualsiasi funzione API è rappresentata come tipo nome_funzione (FA 1, FA 2, FA 3) Tipo: tipo di valore restituito; FAX – elenco di argomenti formali nel loro ordine, ad esempio int Messaggio. Box (HWND h. Wnd, LPCTSTR lp. Text, LPCTSTR lp. Caption, UINT u. Type); Questa funzione visualizza una finestra con un messaggio e uno o più pulsanti di uscita. Significato dei parametri: h. Wnd - handle alla finestra in cui apparirà la finestra del messaggio, lp. Testo - il testo che apparirà nella finestra, lp. Didascalia - testo nel titolo della finestra, u. Tipo - tipo di finestra, in particolare, è possibile specificare il numero di pulsanti di uscita.

Chiamare le funzioni API di Windows int Messaggio. Box (HWND h. Wnd, LPCTSTR lp. Text, LPCTSTR lp. Caption, UINT u. Type); Quasi tutti i parametri delle funzioni API sono in realtà numeri interi a 32 bit: HWND è un numero intero a 32 bit, LPCTSTR è un puntatore a una stringa a 32 bit, UINT è un numero intero a 32 bit. Il suffisso "A" viene spesso aggiunto al nome delle funzioni per passare a versioni più recenti delle funzioni.

Chiamare le funzioni API di Windows int Messaggio. Box (HWND h. Wnd, LPCTSTR lp. Text, LPCTSTR lp. Caption, UINT u. Type); Quando si utilizza MASM, è necessario aggiungere @N N alla fine del nome, ovvero il numero di byte che gli argomenti passati occupano nello stack. Per le funzioni API di Win 32, questo numero può essere definito come il numero di argomenti n volte 4 (byte in ogni argomento): N=4*n. Per chiamare una funzione, viene utilizzata l'istruzione CALL dell'assembler. In questo caso, tutti gli argomenti della funzione le vengono passati tramite lo stack (comando PUSH). Direzione di passaggio dell'argomento: DA SINISTRA A DESTRA - BASSO SU. Argomento che verrai inserito per primo nello stack. genere. La chiamata alla funzione specificata sarà simile a questa: CALL Message. scatola. [email protetta]

Chiamare le funzioni API di Windows int Messaggio. Box (HWND h. Wnd, LPCTSTR lp. Text, LPCTSTR lp. Caption, UINT u. Type); Il risultato dell'esecuzione di qualsiasi funzione API è in genere un numero intero, che viene restituito nel registro EAX. La direttiva OFFSET è un "segmento offset" o, in termini linguistici di alto livello, un "puntatore" all'inizio di una stringa. La direttiva EQU, come #define in C, definisce una costante. La direttiva EXTERN dice al compilatore che una funzione o un identificatore è esterno al modulo.

Un esempio del programma "Ciao a tutti!" . 686 P. MODELLO APPARTAMENTO, STDCALL. STACK 4096. DATA MB_OK EQU 0 STR 1 DB "Il mio primo programma", 0 STR 2 DB "Ciao a tutti!", 0 HW DD ? messaggio ESTERNO. scatola. [email protetta]: VICINO. CODICE START: PUSH MB_OK PUSH OFFSET STR 1 PUSH OFFSET STR 2 PUSH HW CALL Messaggio. scatola. [email protetta] RIPETI FINE INIZIO

La direttiva INVOKE Il traduttore di lingua MASM consente inoltre di semplificare la chiamata di funzione utilizzando uno strumento macro - la direttiva INVOKE: funzione INVOKE, parametro1, parametro2, ... Non è necessario aggiungere @16 alla chiamata di funzione; i parametri vengono scritti esattamente nell'ordine in cui sono riportati nella descrizione della funzione. le macro del traduttore inseriscono i parametri nello stack. per utilizzare la direttiva INVOKE, è necessario disporre di una descrizione del prototipo della funzione che utilizza la direttiva PROTO nella forma: Messaggio. scatola. UN PROTO: DWORD, : DWORD

Argomento 2.5 Nozioni di base sulla programmazione del processore

All'aumentare della durata del programma, diventa più difficile ricordare i codici per le varie operazioni. I mnemonici forniscono un aiuto in questo senso.

Viene chiamato il linguaggio di codifica delle istruzioni simboliche assemblatore.

linguaggio assemblyè un linguaggio in cui ogni istruzione corrisponde esattamente a un'istruzione macchina.

Assemblea chiamato convertire un programma da linguaggio assembly, cioè preparare un programma in linguaggio macchina sostituendo nomi simbolici di operazioni con codici macchina e indirizzi simbolici con numeri assoluti o relativi, nonché includere programmi di libreria e generare sequenze di istruzioni simboliche specificando parametri specifici nelle microistruzioni. Questo programma viene solitamente inserito nella ROM o inserito nella RAM da un supporto esterno.

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

1. Questa è una corrispondenza uno-a-uno tra istruzioni in linguaggio assembly e istruzioni macchina.

2. Il programmatore in linguaggio assembly ha accesso a tutti gli oggetti e comandi presenti sulla macchina di destinazione.

Una comprensione delle basi della programmazione nei linguaggi orientati alla macchina è utile per:



Migliore comprensione dell'architettura del PC e migliore utilizzo dei computer;

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

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

Compilazione di programmi per risolvere i compiti più critici (un programma compilato in un linguaggio orientato alla macchina è solitamente più efficiente - più breve e veloce del 30-60 percento rispetto ai programmi ottenuti come risultato della traduzione da linguaggi di alto livello)

Per l'implementazione di procedure incluse nel programma principale come frammenti separati nel caso in cui non possano essere implementate né nel linguaggio di alto livello utilizzato né utilizzando procedure di servizio del sistema operativo.

Un programma in linguaggio assembly può essere eseguito solo su computer della stessa famiglia, 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. Distinguere:

Numeri binari che terminano con la lettera B;

Numeri decimali che terminano con D;

Numeri esadecimali che terminano con la lettera N.

RAM, registri, rappresentazione dei dati

Per una determinata 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 utilizza le capacità di una macchina particolare (più precisamente, MP) in modo più razionale rispetto a un programma in un linguaggio di alto livello (che è più facile per un programmatore che per un assemblatore). Considereremo i principi di base della programmazione in linguaggi orientati alla macchina utilizzando come esempio il linguaggio assembly per MP KR580VM80. Per la programmazione nel linguaggio viene utilizzata una tecnica generale. Tecniche specifiche per la registrazione dei programmi sono legate all'architettura e alle caratteristiche del sistema di comando dell'MP di destinazione.

Modello software di un sistema a microprocessore basato su MP KR580VM80

Il modello di programma del MPS secondo la Figura 1

Memoria porte MP

S Z corrente alternata P C

Immagine 1

Dal punto di vista del programmatore, il KR580VM80 MP ha i seguenti registri accessibili dal programma.

MA– Registro accumulatore a 8 bit. È il registro principale di MP. Qualsiasi operazione eseguita nell'ALU comporta il posizionamento di uno degli operandi da elaborare nell'accumulatore. Anche il risultato dell'operazione nell'ALU viene solitamente memorizzato in A.

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

FL– flag register (feature register) Un registro a 8 bit che memorizza cinque caratteristiche 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 c'è stato 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 unità nei bit del risultato è pari.

Il bit AC è un riporto aggiuntivo, progettato per memorizzare il valore di riporto dalla tetrade inferiore del risultato.

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

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

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

RS– contatore di programma (contatore di programma), registro a 16 bit, progettato per memorizzare l'indirizzo della prossima istruzione eseguibile. Il contenuto del contatore di programma viene automaticamente incrementato di 1 subito dopo il recupero del byte di istruzione successivo.

Nell'area di memoria iniziale dell'indirizzo 0000H - 07FF sono presenti un programma di controllo e programmi demo. Questa è l'area ROM.

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

0В00 - 0ВВ0 - area di indirizzo per la registrazione dei dati. (RAM).

0BB0 è l'indirizzo iniziale dello stack. (RAM).

Stack è un'area di RAM appositamente organizzata progettata per l'archiviazione temporanea di dati o indirizzi. L'ultimo numero inserito nella pila è il primo numero uscito dalla pila. Il puntatore dello stack memorizza l'indirizzo dell'ultima posizione dello stack in cui sono archiviate le informazioni. Quando viene chiamata una subroutine, l'indirizzo di ritorno al programma principale viene automaticamente memorizzato nello stack. Di norma, all'inizio di ogni subroutine, i contenuti di tutti i registri coinvolti nella sua esecuzione vengono archiviati nello stack e alla fine del subroutine vengono ripristinati dallo stack.

Formato dei dati del linguaggio assembly e struttura dei comandi

La memoria MP KR580VM80 è un array di parole a 8 bit chiamate byte, ciascuna delle quali ha un proprio indirizzo a 16 bit che ne determina la posizione nella sequenza delle celle di memoria. L'MP può indirizzare 65536 byte di memoria, che possono contenere sia ROM che RAM.

Formato dei dati

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

D7 D6 D5 D4 D3 D2 D1 D0

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

Il comando è caratterizzato dal formato, ovvero dal numero di bit ad esso allocati, che sono divisi byte per byte in determinati campi funzionali.

Formato dei comandi

I comandi MP KR580VM80 hanno un formato a uno, due o tre byte. Le istruzioni multibyte devono essere inserite nei PL adiacenti. Il formato del comando dipende dalle specifiche dell'operazione eseguita.

Il primo byte del comando contiene l'opcode scritto in forma mnemonica.

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

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

Formato del comando a byte singolo secondo la Figura 2

Figura 4

Nelle istruzioni in linguaggio assembly, l'opcode ha una forma abbreviata di scrittura di parole inglesi: una notazione mnemonica. Mnemonico (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 codice - linguaggio macchina, in questa forma viene posto nella memoria dell'MP e quindi utilizzato durante l'esecuzione del comando.


Metodi di indirizzamento

Tutti i codici operandi (input e output) devono trovarsi da qualche parte. Possono essere nei registri interni dell'MP (l'opzione più comoda e veloce). 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 dell'istruzione. Esistono vari metodi con cui il codice dell'istruzione può determinare da dove prendere l'operando di input e dove inserire l'operando di output. Questi metodi sono chiamati metodi di indirizzamento.

Per MP KR580VM80 sono disponibili i seguenti metodi di indirizzamento:

Immediato;

Registrati;

indiretto;

Pila.

Immediato l'indirizzamento presuppone che l'operando (input) sia 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 nel terzo byte del comando, con il byte di dati basso nel secondo byte di comando e il byte di dati alto nel terzo byte di comando.

Dritto (aka assoluto) l'indirizzamento presuppone che l'operando (ingresso o uscita) 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 MP interno. Utilizzato nei comandi a byte singolo

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

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

Sistema di comando

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

Esistono i seguenti gruppi di comandi:

Trasmissione dati;

Aritmetica;

Rompicapo;

Comandi di salto;

Comandi per input-output, controllo e lavoro con lo stack.


Simboli e abbreviazioni usati per descrivere comandi e scrivere programmi

Simbolo Riduzione
INDIRIZZO indirizzo a 16 bit
DATI Dati a 8 bit
DATI 16 Dati a 16 bit
PORTA Indirizzo I/O a 8 bit (dispositivi I/O)
BYTE 2 Secondo byte di comando
BYTE 3 Terzo byte di comando
R, R1, R2 Uno dei registri: A, B, C, D, E, H, L
RP Una delle coppie di registri: B - imposta una coppia di aeromobili; D - imposta una coppia di DE; H - specifica una coppia di HL
RH Primo registro della coppia
RL Secondo registro della coppia
Λ Moltiplicazione booleana
V Aggiunta booleana
Modulo due addizione
M Cella di memoria il cui indirizzo specifica il contenuto della coppia di registri HL, ovvero M = (HL)

Comandi del linguaggio assembly (lezione)

PIANO DI LEZIONI

1. Principali gruppi di operazioni.

Pentium.

1. Principali gruppi di operazioni

I microprocessori eseguono una serie di istruzioni che implementano i seguenti gruppi principali di operazioni:

operazioni di spedizione,

operazioni aritmetiche,

operazioni logiche,

operazioni di turno,

operazioni di confronto e test,

operazioni di bit,

Operazioni di gestione del programma;

Operazioni di controllo del processore.

2. Codici mnemodici dei comandi del processore Pentium

Quando si descrivono i comandi, vengono solitamente utilizzate le loro designazioni mnemoniche (codici mnemonici), che servono a specificare il comando durante la programmazione in linguaggio Assembly. Per diverse versioni dell'Assembler, i codici mnemonici di alcuni comandi possono differire. Ad esempio, per un comando per chiamare una subroutine, viene utilizzato il codice mnemonicoCHIAMATA o JSR ("Salta a sottoprogramma”). Tuttavia, i codici mnemonici della maggior parte dei comandi per i principali tipi di microprocessori sono gli stessi o differiscono leggermente, poiché sono abbreviazioni delle parole inglesi corrispondenti che definiscono l'operazione in corso. Considera i mnemonici dei comandi adottati per i processori Pentium.

Inoltra comandi. Il comando principale di questo gruppo è il comandoMOV , che fornisce il trasferimento di dati tra due registri o tra un registro e una cella di memoria. Alcuni microprocessori implementano un trasferimento tra due celle di memoria, nonché un trasferimento di gruppo del contenuto di più registri dalla memoria. Ad esempio, i microprocessori della famiglia 68 Motorola xxx eseguire il comandoMUOVERSI , che fornisce il trasferimento da una cella di memoria a un'altra, e il comandoMUOVERSI , che scrive in memoria o carica dalla memoria il contenuto di un determinato insieme di registri (fino a 16 registri). SquadraXCHG esegue uno scambio reciproco del contenuto di due registri del processore o un registro e una cella di memoria.

Comandi di input IN e uscita FUORI attuare il trasferimento di dati dal registro del processore a un dispositivo esterno o la ricezione di dati da un dispositivo esterno al registro. Questi comandi specificano il numero del dispositivo di interfaccia (porta I/O) attraverso il quale vengono trasferiti i dati. Si noti che molti microprocessori non dispongono di istruzioni speciali per l'accesso dispositivi esterni. In questo caso, l'input e l'output dei dati nel sistema vengono eseguiti utilizzando il comandoMOV , che specifica l'indirizzo del dispositivo di interfaccia richiesto. Pertanto, un dispositivo esterno viene indirizzato come una cella di memoria e una specifica sezione viene allocata nello spazio degli indirizzi, in cui si trovano gli indirizzi dei dispositivi di interfaccia (porte) collegati al sistema.

Comandi per operazioni aritmetiche. I comandi principali di questo gruppo sono addizione, sottrazione, moltiplicazione e divisione, che hanno diverse opzioni. Comandi di aggiunta INSERISCI e sottrazione SUB eseguire le operazioni appropriate concpossedere due registri, un registro e una posizione di memoria, o utilizzare un operando immediato. Squadre ANNO DOMINI C , SB B eseguire addizioni e sottrazioni, tenendo conto del valore dell'attributoC, impostato durante la formazione del trasferimento in fase di esecuzione dell'operazione precedente. Con l'aiuto di questi comandi, viene implementata l'aggiunta sequenziale di operandi, il cui numero di cifre supera la capacità del processore. Squadra NEG cambia il segno dell'operando, convertendolo in complemento a due.

Le operazioni di moltiplicazione e divisione possono essere eseguite su numeri con segno (comandiio MUL, io DIV ) o non firmato (comandi MUL, DIV ). Il risultato dell'operazione si trova nel registro. Quando si moltiplica (comandiMUL , IMUL ) risulta in un risultato a due cifre, che utilizza due registri per adattarsi. Quando si divide (comandiDIV , IDIV ) come dividendo viene utilizzato un operando a due cifre, collocato in due registri e, di conseguenza, il quoziente e il resto vengono scritti in due registri.

Comandi logici . Quasi tutti i microprocessori eseguono operazioni logiche AND, OR, OR esclusivo, che vengono eseguite sui bit dell'operando con lo stesso nome tramite comandi E, OPPURE, X O . Le operazioni vengono eseguite sul contenuto di due registri, un registro e una posizione di memoria, oppure utilizzando un operando immediato. Squadra NON Inverte il valore di ogni bit dell'operando.

Comandi di spostamento. I microprocessori effettuano spostamenti aritmetici, logici e ciclici degli operandi indirizzati di uno o più bit. L'operando da spostare può trovarsi in un registro o in una posizione di memoria e il numero di bit di spostamento viene specificato utilizzando l'operando immediato contenuto nell'istruzione o determinato dal contenuto del registro specificato. Il segno di trasferimento è solitamente coinvolto nell'attuazione del turnoCnel registro di stato (SR o EFLAG), che contiene l'ultimo bit dell'operando estratto dal registro o dalla posizione di memoria.

Comandi di confronto e test . Il confronto degli operandi viene solitamente eseguito con l'istruzioneCMP , che esegue la sottrazione di operandi con l'impostazione dei valori delle caratteristiche N, Z, V, C nel registro di stato in base al risultato. In questo caso, il risultato della sottrazione non viene salvato e i valori degli operandi non cambiano. La successiva analisi dei valori caratteristici ottenuti permette di determinare il valore relativo (>,<, =) операндов со знаком или без знака. Использование различных способов адресации позволяет производит сравнение содержимого двух регистров, регистра и ячейки памяти, непосредственно заданного операнда с содержимым регистра или ячейки памяти.

Alcuni microprocessori eseguono un comando di test TST , che è una variante a singolo operando dell'istruzione di confronto. Quando questo comando viene eseguito, i segni vengono impostati N, Z in base al segno e al valore (uguale o diverso da zero) dell'operando indirizzato.

Istruzioni per l'uso dei bit . Questi comandi impostano il valore dell'attributoCnel registro di stato in base al valore del bit da testaremld nell'operando indirizzato. In alcuni microprocessori, in base al risultato del test un po', viene impostato un segnoZ. Numero di bit di provanè impostato o dal contenuto del registro specificato nel comando o da un operando immediato.

I comandi di questo gruppo implementano diverse opzioni per modificare il bit testato BT mantiene inalterato il valore di questo bit.Command B T S dopo il test imposta il valore mld=1 e il comando B T C - significato mld=0.Comando B T C inverte il valore di bit bn dopo averlo testato.

Operazioni di gestione del programma. Per controllare il programma viene utilizzato un gran numero di comandi, tra i quali:

- comandi di trasferimento del controllo incondizionato;

- comandi di salto condizionato;

- comandi per l'organizzazione di cicli di programma;

- comandi di interruzione;

- comandi di modifica delle funzioni.

Il trasferimento incondizionato del controllo viene eseguito dal comandoJMP , che viene caricato nel contatore del programmaPCnuovo contenuto che è l'indirizzo del prossimo comando da eseguire. Questo indirizzo è specificato direttamente nel comandoJMP (indirizzo diretto), o calcolato come somma del contenuto correntePCe l'offset specificato nel comando, che è un numero con segno (indirizzamento relativo). PerchéPCcontiene l'indirizzo del comando successivo del programma, quindi l'ultimo metodo imposta l'indirizzo della transizione, spostato rispetto all'indirizzo successivo di un determinato numero di byte. Se l'offset è positivo, viene eseguito il passaggio ai successivi comandi del programma, se l'offset è negativo, ai precedenti.

La subroutine viene anche chiamata tramite trasferimento incondizionato del controllo utilizzando il comandoCHIAMATA (o JSR ). Tuttavia, in questo caso, prima di caricare inPC nuovo contenuto che specifica l'indirizzo della prima istruzione del sottoprogramma, è necessario salvarne il valore corrente (l'indirizzo dell'istruzione successiva) per garantire un ritorno al programma principale dopo l'esecuzione del sottoprogramma (o al sottoprogramma precedente durante l'annidamento di sottoprogrammi). Vengono caricate le istruzioni di salto condizionale (rami del programma).PCnuovo contenuto se vengono soddisfatte determinate condizioni, che di solito sono impostate in base al valore corrente di vari attributi nel registro di stato. Se la condizione non è soddisfatta, viene eseguito il comando di programma successivo.

I comandi di gestione dei tratti forniscono la scrittura - la lettura del contenuto del registro di stato, che memorizza i tratti, nonché la modifica dei valori dei singoli tratti. Ad esempio, i processori Pentium implementano i comandi LAHF e SAHF , che carica il byte basso, che contiene i segni, dal registro di stato EFLAG al byte basso del registro EAX e riempimento di byte basso EFLAG dal registro E AX.. Comandi CLC, STC impostare i valori del flag di trasferimento CF=0, CF=1 e il comando CMC fa invertire il valore di questa caratteristica. Poiché i tratti determinano il flusso di esecuzione del programma durante i salti condizionali, le istruzioni di cambio tratto vengono generalmente utilizzate per controllare il programma.

Comandi di controllo del processore . Questo gruppo include comandi di arresto, nessuna operazione e una serie di comandi che determinano la modalità di funzionamento del processore o dei suoi singoli blocchi. SquadraHLT termina l'esecuzione del programma e pone il processore in uno stato di arresto, uscita dal quale si verifica al ricevimento di segnali di interruzione o riavvio ( Ripristina). Squadra NOP Un'istruzione ("vuota"), che non comporta l'esecuzione di alcuna operazione, viene utilizzata per implementare ritardi di programma o colmare lacune formate nel programma.

Squadre speciali CLI, STI disabilitare e abilitare il servizio di richieste di interruzione. Nei processori Pentium per questo viene utilizzato un bit di controllo (flag).SE nel registro EFLAG.

Molti moderni microprocessori emettono un comando di identificazione che consente all'utente o ad altri dispositivi di ottenere informazioni sul tipo di processore utilizzato in un determinato sistema. Nei processori Pentuimo a questo serve il comando ID CPU , durante la quale i dati necessari sull'incaricato del trattamento entrano nei registri EAX,ebx,ECX,EDX e possono quindi essere letti dall'utente o dal sistema operativo.

A seconda delle modalità operative implementate dal processore e dei tipi di dati elaborati specificati, l'insieme dei comandi eseguibili può essere notevolmente ampliato.

Alcuni processori eseguono operazioni aritmetiche BCD o eseguono speciali istruzioni di correzione dei risultati durante l'elaborazione di tali numeri. Molti processori ad alte prestazioni includono FPU - unità di elaborazione numerica c "virgola mobile".

In un certo numero di processori moderni, viene implementata l'elaborazione di gruppo di più numeri interi o numeri. c “virgola mobile” con un solo comando secondo il principio SIMD ("Istruzione singola - Dati multipli ”) - “Un comando – Molti dati”. L'esecuzione simultanea di operazioni su più operandi aumenta significativamente le prestazioni del processore quando si lavora con dati video e audio. Tali operazioni sono ampiamente utilizzate nell'elaborazione delle immagini, nell'elaborazione del segnale audio e in altre applicazioni. Per eseguire queste operazioni, nei processori vengono introdotti appositi blocchi che implementano i corrispondenti insiemi di istruzioni, che in vari tipi di processori ( Pentium, Atlon) ha preso il nomeMMX (“ Milti-Estensione multimediale ”) – Estensione multimediale,SSE("Estensione SIMD in streaming") – SIMD in streaming - estensione, “3 DEstensione- Espansione 3D.

Una caratteristica dei processori dell'azienda Intel , a partire dal modello 80286, è il controllo di priorità all'accesso alla memoria, che viene fornito quando il processore opera in modalità di indirizzo virtuale protetto - “ Modalità protetta " (modalità protetta). Per implementare questa modalità, vengono utilizzati gruppi speciali di comandi, che servono a organizzare la protezione della memoria secondo l'algoritmo di accesso prioritario accettato.

Argomento 1.4 Mnemonici dell'assemblatore. Struttura e formati dei comandi. Tipi di indirizzamento. Set di istruzioni per microprocessore

Piano:

1 Linguaggio assembler. Concetti basilari

2 Simboli del linguaggio assembly

3 Tipi di istruzioni assembler

4 Direttive di Assemblea

5 Set di istruzioni del processore

1 iolinguaggio assembly. Concetti basilari

linguaggio assemblyè una rappresentazione simbolica del linguaggio macchina. Tutti i processi nella macchina al livello hardware più basso sono guidati solo da comandi (istruzioni) del linguaggio macchina. Da ciò risulta chiaro che, nonostante il nome comune, il linguaggio assembly per ogni tipo di computer è diverso.

Un programma in linguaggio assembly è una raccolta di blocchi di memoria chiamati segmenti di memoria. Un programma può essere costituito da uno o più di questi segmenti di blocco. Ogni segmento contiene una raccolta di frasi linguistiche, ognuna delle quali occupa una riga separata di codice del programma.

Le dichiarazioni di assemblaggio sono di quattro tipi:

1) comandi o istruzioni che sono analoghi simbolici dei comandi della macchina. Durante il processo di traduzione, le istruzioni di montaggio vengono convertite nei comandi corrispondenti del set di istruzioni del microprocessore;

2) macro -le frasi del testo del programma, formalizzate in un certo modo, vengono sostituite da altre frasi durante la messa in onda;

3) direttive,che sono istruzioni al traduttore assembler per eseguire alcune azioni. Le direttive non hanno controparti nella rappresentazione della macchina;

4) righe di commento , contenente qualsiasi carattere, comprese le lettere dell'alfabeto russo. I commenti vengono ignorati dal traduttore.

­ Struttura del programma di montaggio. sintassi dell'assemblatore.

Le frasi che compongono un programma possono essere un costrutto sintattico corrispondente a un comando, una macro, una direttiva o un commento. Affinché il traduttore assembler possa riconoscerli, devono essere formati secondo determinate regole sintattiche. Per fare ciò, è meglio utilizzare una descrizione formale della sintassi della lingua, come le regole della grammatica. I modi più comuni per descrivere un linguaggio di programmazione come questo - diagrammi di sintassi e forme estese di Backus-Naur. Per uso pratico più comodo diagrammi di sintassi. Ad esempio, la sintassi delle istruzioni in linguaggio assembly può essere descritta utilizzando i diagrammi di sintassi mostrati nelle figure 10, 11, 12 seguenti.

Figura 10 - Formato della frase di assemblaggio


­ Figura 11 - Formato delle direttive

­ Figura 12 - Formato dei comandi e delle macro

Su questi disegni:

­ nome dell'etichetta- identificatore, il cui valore è l'indirizzo del primo byte della frase del codice sorgente del programma, che esso denota;

­ nome -un identificatore che distingue questa direttiva da altre direttive con lo stesso nome. A seguito dell'elaborazione da parte dell'assemblatore di una determinata direttiva, alcune caratteristiche possono essere assegnate a questo nome;

­ codice operativo (COP) e direttiva - questi sono simboli mnemonici per la corrispondente istruzione macchina, istruzione macro o direttiva del compilatore;

­ operandi -parti di un comando, una macro o una direttiva assembler, che denotano oggetti su cui vengono eseguite le azioni. Gli operandi Assembler sono descritti da espressioni con costanti numeriche e di testo, etichette di variabili e identificatori che utilizzano segni di operazione e alcune parole riservate.

I diagrammi di sintassi aiutano trova e quindi attraversa il percorso dall'input del diagramma (a sinistra) al suo output (a destra). Se esiste un tale percorso, la frase o la costruzione è sintatticamente corretta. Se non esiste un tale percorso, il compilatore non accetterà questa costruzione.

­ 2 Simboli del linguaggio assembly

I caratteri consentiti durante la scrittura del testo dei programmi sono:

1) tutte lettere latine: A-Z,az. In questo caso, le lettere maiuscole e minuscole sono considerate equivalenti;

2) numeri da 0 prima 9 ;

3) segni ? , @ , $ , _ , & ;

4) separatori , . () < > { } + / * % ! " " ? = # ^ .

Le frasi assembler sono formate da gettoni, che sono sequenze sintatticamente inseparabili di caratteri linguistici validi che hanno senso per il traduttore.

gettoni sono:

1) identificatori - sequenze di caratteri validi utilizzati per designare oggetti di programma come codici operativi, nomi di variabili e nomi di etichette. La regola per la scrittura degli identificatori è la seguente: un identificatore può essere costituito da uno o più caratteri;

2) stringhe di caratteri - sequenze di caratteri racchiuse tra virgolette singole o doppie;

3) numeri interi in uno dei seguenti sistemi numerici : binario, decimale, esadecimale. L'identificazione dei numeri durante la scrittura nei programmi assembler viene eseguita secondo determinate regole:

4) i numeri decimali non richiedono simboli aggiuntivi per la loro identificazione, ad esempio 25 o 139. Per l'identificazione nel codice sorgente del programma numeri binari occorre, dopo aver scritto gli zeri e gli uno compresi nella loro composizione, mettere il latino “ b”, ad esempio 10010101 b.

5) i numeri esadecimali hanno più convenzioni nella loro notazione:

Innanzitutto, sono costituiti da numeri. 0...9 , lettere minuscole e maiuscole dell'alfabeto latino un,b, c,d,e,f o UN,B,C,D,e,F.

In secondo luogo, il traduttore potrebbe avere difficoltà a riconoscere i numeri esadecimali a causa del fatto che possono essere costituiti solo dalle cifre 0...9 (ad esempio, 190845) o iniziare con una lettera dell'alfabeto latino (ad esempio, ef15). Per "spiegare" al traduttore che il lessema dato non è un numero decimale o un identificatore, il programmatore deve allocare in modo speciale il numero esadecimale. Per fare ciò, al termine della sequenza di cifre esadecimali che compongono il numero esadecimale, scrivi la lettera latina “ h". Questo è un prerequisito. Se un numero esadecimale inizia con una lettera, è preceduto da uno zero iniziale: 0 ef15 h.

Quasi ogni frase contiene una descrizione dell'oggetto su cui o con l'aiuto del quale viene eseguita un'azione. Questi oggetti sono chiamati operandi. Si possono definire così: operandi- si tratta di oggetti (alcuni valori, registri o celle di memoria) che sono interessati da istruzioni o direttive, oppure si tratta di oggetti che definiscono o perfezionano l'azione di istruzioni o direttive.

È possibile effettuare la seguente classificazione degli operandi:

­ operandi costanti o immediati;

­ operandi di indirizzo;

­ operandi spostati;

contatore di indirizzi;

­ registrare l'operando;

­ operandi di base e di indice;

­ operandi strutturali;

record.

Gli operandi sono componenti elementari che fanno parte dell'istruzione macchina, denotando gli oggetti su cui viene eseguita l'operazione. In un caso più generale, gli operandi possono essere inclusi come componenti in formazioni più complesse dette espressioni.

Espressioni sono combinazioni di operandi e operatori considerati nel loro insieme. Il risultato della valutazione dell'espressione può essere l'indirizzo di una cella di memoria o un valore costante (assoluto).

­ 3 Tipi di istruzioni assembler

Elenchiamo i tipi possibili dichiarazioni dell'assemblatore e regole sintattiche per la formazione di espressioni assembler:

­ operatori aritmetici;

­ operatori di turno;

­ operatori di confronto;

­ operatori logici;

­ operatore di indice;

­ digitare l'operatore di esclusione;

­ operatore di ridefinizione del segmento;

­ operatore di denominazione del tipo di struttura;

­ operatore per ottenere la componente segmento dell'indirizzo dell'espressione;

­ operatore get offset dell'espressione.

1 Direttive di Assemblea

­ Le direttive dell'Assembler sono:

1) Direttive di segmentazione. Nel corso della discussione precedente, abbiamo scoperto tutte le regole di base per scrivere istruzioni e operandi in un programma in linguaggio assembly. Rimane aperta la questione di come formattare correttamente la sequenza di comandi in modo che il traduttore possa elaborarli e il microprocessore possa eseguirli.

Considerando l'architettura del microprocessore, abbiamo appreso che ha sei registri di segmento, attraverso i quali può funzionare contemporaneamente:

­ con un segmento di codice;

­ con un segmento di pila;

­ con un segmento di dati;

­ con tre segmenti di dati aggiuntivi.

Fisicamente, un segmento è un'area di memoria occupata da comandi e (o) dati i cui indirizzi sono calcolati rispetto al valore nel registro di segmento corrispondente. La descrizione sintattica di un segmento in assembler è la costruzione mostrata in Figura 13:


­ Figura 13 - Descrizione sintattica del segmento in assembler

È importante notare che la funzionalità di un segmento è in qualche modo più ampia della semplice suddivisione del programma in blocchi di codice, dati e stack. La segmentazione fa parte di un meccanismo più generale relativo a concetto di programmazione modulare. Implica l'unificazione della progettazione dei moduli oggetto creati dal compilatore, compresi quelli provenienti da diversi linguaggi di programmazione. Ciò consente di combinare programmi scritti in diverse lingue. È per l'implementazione di varie opzioni per tale unione che sono destinati gli operandi nella direttiva SEGMENT.

2) Elencare le direttive di controllo. Le direttive di controllo dell'elenco sono suddivise nei seguenti gruppi:

­ direttive generali per il controllo dell'elencazione;

­ direttive di output per includere l'elenco dei file;

­ direttive di output per blocchi di assembly condizionali;

­ direttive di output per l'elenco delle macro;

­ direttive per la visualizzazione delle informazioni sui riferimenti incrociati nell'elenco;

­ elencare le direttive di modifica del formato.

2 Set di istruzioni per il processore

Il set di istruzioni del processore è mostrato nella Figura 14.

Considera i principali gruppi di comandi.

­ Figura 14 - Classificazione delle istruzioni di montaggio

I comandi sono:

1 Comandi di trasferimento dati. Queste istruzioni occupano un posto molto importante nel set di istruzioni di qualsiasi processore. Svolgono le seguenti funzioni essenziali:

­ salvare in memoria il contenuto dei registri interni del processore;

­ copiare il contenuto da un'area di memoria all'altra;

­ scrittura su dispositivi I/O e lettura da dispositivi I/O.

In alcuni processori, tutte queste funzioni vengono eseguite da una singola istruzione MOV (per trasferimenti di byte - MOVB ) naso vari metodi indirizzamento degli operandi.

In altri processori oltre all'istruzione MOV ci sono molti altri comandi per eseguire le funzioni elencate. I comandi di trasferimento dati includono anche comandi di scambio di informazioni (la loro designazione si basa sulla parola Scambio ). Può essere possibile prevedere lo scambio di informazioni tra registri interni, tra due metà di un registro ( SCAMBIO ) o tra un registro e una locazione di memoria.

2 Comandi aritmetici. Le istruzioni aritmetiche trattano i codici operandi come codici binari o BCD numerici. Questi comandi possono essere suddivisi in cinque gruppi principali:

­ comandi per operazioni con punto fisso (addizione, sottrazione, moltiplicazione, divisione);

­ istruzioni in virgola mobile (addizione, sottrazione, moltiplicazione, divisione);

­ comandi di pulizia;

­ comandi di incremento e decremento;

­ comando di confronto.

3 Le istruzioni a virgola fissa operano sui codici nei registri del processore o nella memoria come di consueto codici binari. Le istruzioni in virgola mobile (virgola) utilizzano un formato di rappresentazione numerica con un esponente e una mantissa (di solito questi numeri occupano due posizioni di memoria consecutive). In moderno potenti processori il set di istruzioni in virgola mobile non è limitato a sole quattro operazioni aritmetiche, ma contiene anche molte altre istruzioni più complesse, ad esempio il calcolo di funzioni trigonometriche, funzioni logaritmiche e anche funzioni complesse necessario per l'elaborazione del suono e dell'immagine.

4 I comandi Clear sono progettati per scrivere un codice zero in un registro o in una cella di memoria. Questi comandi possono essere sostituiti da istruzioni di trasferimento a codice zero, ma istruzioni chiare speciali sono generalmente più veloci delle istruzioni di trasferimento.

5 Comandi di incremento (aumento di uno) e decremento

(riduzioni di uno) sono anche molto convenienti. In linea di principio potrebbero essere sostituite da istruzioni di aggiunta o sottrazione, ma l'incremento e il decremento sono più veloci dell'aggiunta e della sottrazione. Queste istruzioni richiedono un operando di ingresso che sia anche un operando di uscita.

6 L'istruzione Compare serve per confrontare due operandi di ingresso. Infatti calcola la differenza di questi due operandi, ma non forma l'operando di uscita, ma cambia solo i bit nel registro di stato del processore in base al risultato di questa sottrazione. L'istruzione che segue l'istruzione di confronto (solitamente un'istruzione di salto) analizzerà i bit nel registro di stato del processore ed eseguirà azioni in base ai loro valori. Alcuni processori forniscono istruzioni per il confronto a catena di due sequenze di operandi in memoria.

7 Comandi logici. Le istruzioni logiche eseguono operazioni logiche (bit per bit) sugli operandi, ovvero considerano i codici degli operandi non come un singolo numero, ma come un insieme di singoli bit. In questo differiscono dai comandi aritmetici. I comandi logici eseguono le seguenti operazioni di base:

­ AND logico, OR logico, addizione modulo 2 (XOR);

­ spostamenti logici, aritmetici e ciclici;

­ controllo di bit e operandi;

­ impostazione e cancellazione dei bit (flag) del registro di stato del processore ( PSW).

I comandi operativi logici consentono il calcolo bit per bit della base funzioni logiche da due operandi di ingresso. Inoltre, l'operazione AND serve per forzare l'azzeramento dei bit dati (uno degli operandi è il codice maschera, in cui i bit che richiedono l'azzeramento sono posti a zero). L'operazione OR serve per forzare i bit impostati (come uno degli operandi, viene utilizzato il codice maschera, in cui i bit che richiedono l'impostazione a uno sono uguali a uno). L'operazione XOR viene utilizzata per invertire i bit dati (come uno degli operandi, viene utilizzato il codice maschera in cui i bit da invertire sono impostati a uno). Le istruzioni richiedono due operandi di ingresso e formano un operando di uscita.

8 I comandi di spostamento consentono di spostare il codice dell'operando un po' alla volta a destra (verso i bit inferiori) oa sinistra (verso i bit superiori). Il tipo di spostamento (booleano, aritmetico o ciclico) determina quale sarà il nuovo valore del bit più significativo (se spostato a destra) o meno significativo (se spostato a sinistra), e determina anche se il vecchio valore del più significativo il bit verrà memorizzato da qualche parte (se spostato a sinistra) o il bit meno significativo (se spostato a destra). Gli spostamenti rotanti consentono di spostare i bit del codice dell'operando in un cerchio (in senso orario quando si passa a destra o in senso antiorario quando si passa a sinistra). In questo caso, l'anello del cambio può includere o meno la bandiera di trasporto. Il bit del flag di riporto (se utilizzato) è impostato sul bit più significativo per la rotazione a sinistra e sul bit meno significativo per la rotazione a destra. Di conseguenza, il valore del bit dell'indicatore di riporto verrà riscritto nel bit meno significativo in uno spostamento ciclico a sinistra e nel bit più significativo in uno spostamento ciclico a destra.

9 Comandi di salto. I comandi di salto sono progettati per organizzare tutti i tipi di loop, rami, chiamate di subroutine, ecc., ovvero interrompono il flusso sequenziale del programma. Queste istruzioni scrivono un nuovo valore nel registro del contatore di istruzioni e quindi fanno sì che il processore salti non all'istruzione successiva nell'ordine, ma a qualsiasi altra istruzione nella memoria del programma. Alcuni comandi di salto consentono di tornare al punto da cui è stato effettuato il salto, mentre altri no. Se viene fornito un ritorno, i parametri del processore correnti vengono archiviati nello stack. Se non viene fornito alcun ritorno, i parametri del processore corrente non vengono salvati.

I comandi di salto senza tornare indietro sono divisi in due gruppi:

­ comandi di salti incondizionati;

­ istruzioni di salto condizionale.

Questi comandi usano le parole Branch (ramo) e Jump (salto).

Le istruzioni di salto incondizionato causano un salto a un nuovo indirizzo, non importa quale. Possono causare un salto al valore di offset specificato (avanti o indietro) o all'indirizzo di memoria specificato. Come operando di ingresso viene specificato il valore di offset o il nuovo valore di indirizzo.

I comandi di salto condizionale non provocano sempre un salto, ma solo quando vengono soddisfatte le condizioni specificate. Tali condizioni sono solitamente i valori dei flag nel registro di stato del processore ( PSW ). Cioè, la condizione di transizione è il risultato dell'operazione precedente che modifica i valori dei flag. In totale, possono esserci da 4 a 16 di queste condizioni di salto Alcuni esempi di comandi di salto condizionale:

­ salta se uguale a zero;

­ salta se diverso da zero;

­ salta se c'è un overflow;

­ salta se non c'è troppopieno;

­ salta se maggiore di zero;

­ salta se minore o uguale a zero.

Se la condizione di transizione è soddisfatta, un nuovo valore viene caricato nel registro del contatore di istruzioni. Se la condizione di salto non è soddisfatta, il contatore di istruzioni viene semplicemente incrementato e il processore seleziona ed esegue l'istruzione successiva in sequenza.

Specificamente per controllare le condizioni del ramo, viene utilizzata un'istruzione di confronto (CMP) che precede un'istruzione di salto condizionale (o anche più istruzioni di salto condizionale). Ma i flag possono essere impostati da qualsiasi altro comando, come un comando di trasferimento dati, qualsiasi comando aritmetico o logico. Nota che i comandi di salto in sé non cambiano i flag, il che ti consente semplicemente di inserire diversi comandi di salto uno dopo l'altro.

I comandi di interruzione occupano un posto speciale tra i comandi di salto con un ritorno. Queste istruzioni richiedono un numero di interrupt (indirizzo vettoriale) come operando di ingresso.

Conclusione:

Il linguaggio assembly è una rappresentazione simbolica del linguaggio macchina. Il linguaggio assembly per ogni tipo di computer è diverso. Un programma in linguaggio assembly è una raccolta di blocchi di memoria chiamati segmenti di memoria. Ogni segmento contiene una raccolta di frasi linguistiche, ognuna delle quali occupa una riga separata di codice del programma. Le istruzioni assembly sono di quattro tipi: comandi o istruzioni, macro, direttive, righe di commento.

I caratteri validi quando si scrive il testo dei programmi sono tutte lettere latine: A-Z,az. In questo caso, le lettere maiuscole e minuscole sono considerate equivalenti; figure da 0 prima 9 ; segni ? , @ , $ , _ , & ; separatori , . () < > { } + / * % ! " " ? = # ^ .

Si applicano i seguenti tipi di istruzioni assembler e regole di sintassi per la formazione di espressioni assembler. operatori aritmetici, operatori di spostamento, operatori di confronto, operatori logici, operatore di indice, operatore di ridefinizione del tipo, operatore di ridefinizione del segmento, operatore di denominazione del tipo di struttura, operatore di ottenimento del componente del segmento di indirizzo dell'espressione, operatore di ottenimento dell'offset dell'espressione.

Il sistema di comando è diviso in 8 gruppi principali.

­ Domande di prova:

1 Che cos'è il linguaggio assembly?

2 Quali simboli possono essere usati per scrivere comandi in assembler?

3 Cosa sono le etichette e qual è il loro scopo?

4 Spiegare la struttura delle istruzioni di montaggio.

5 Elenca 4 tipi di istruzioni assembler.

UNIVERSITÀ NAZIONALE DELL'UZBEKISTAN INtitolata A MIRZO ULUGBEK

FACOLTA' DI TECNOLOGIE INFORMATICHE

Sull'argomento: Analisi semantica di un file EXE.

Completato:

Tashkent 2003.

Prefazione.

Linguaggio di assemblaggio e struttura dell'istruzione.

Struttura del file EXE (analisi semantica).

Struttura di un file COM.

Come funziona e si diffonde il virus.

Disassemblatore.

Programmi.

Prefazione

La professione di programmatore è sorprendente e unica. Nel nostro tempo, la scienza e la vita non possono essere immaginate senza ultima tecnologia. Tutto ciò che è connesso con l'attività umana non è completo senza la tecnologia informatica. E questo contribuisce al suo alto sviluppo e perfezione. Sebbene lo sviluppo dei personal computer sia iniziato non molto tempo fa, durante questo periodo sono stati compiuti passi colossali nei prodotti software e altro ancora. per molto tempo questi prodotti saranno ampiamente utilizzati. Il campo della conoscenza relativa al computer è esploso, così come la relativa tecnologia. Se non prendiamo in considerazione la parte commerciale, allora possiamo dire che non ci sono estranei in questo ambito di attività professionale. Molti sono impegnati nello sviluppo di programmi non a scopo di lucro o guadagno, ma di loro spontanea volontà, per passione. Naturalmente, ciò non dovrebbe influire sulla qualità del programma e in questo settore, per così dire, c'è concorrenza e richiesta di prestazioni di qualità, per lavoro stabile e soddisfare tutte le esigenze moderne. Qui vale anche la pena notare l'apparizione dei microprocessori negli anni '60, che sono arrivati ​​​​a sostituire un gran numero di set di lampade. Ci sono alcune varietà di microprocessori che sono molto diversi l'uno dall'altro. Questi microprocessori differiscono l'uno dall'altro in termini di capacità di bit e comandi di sistema integrati. I più comuni sono: Intel, IBM, Celeron, AMD, ecc. Tutti questi processori sono correlati all'architettura avanzata del processore da Intel. La diffusione dei microcomputer ha provocato un ripensamento degli atteggiamenti nei confronti del linguaggio assembly per due ragioni principali. In primo luogo, i programmi scritti in linguaggio assembly richiedono molto meno memoria e runtime. In secondo luogo, la conoscenza del linguaggio assembly e del codice macchina che ne deriva fornisce una comprensione dell'architettura della macchina, che è difficilmente fornita quando si lavora in un linguaggio di alto livello. Sebbene la maggior parte degli ingegneri del software sviluppi in linguaggi di alto livello come Pascal, C o Delphi, che è più facile scrivere programmi, il più potente ed efficiente Software scritto interamente o in parte in linguaggio assembly. I linguaggi di alto livello sono stati progettati per evitare i tecnicismi speciali di determinati computer. E il linguaggio assembly, a sua volta, è progettato per le specifiche specifiche del processore. Pertanto, per scrivere un programma in linguaggio assembly per un determinato computer, è necessario conoscerne l'architettura. Al giorno d'oggi, la vista del principale prodotto softwareè un file EXE. Considerando lati positivi questo, l'autore del programma può essere certo della sua inviolabilità. Ma spesso questo è tutt'altro che vero. C'è anche un disassemblatore. Con l'aiuto di un disassemblatore, puoi scoprire interruzioni e codici di programma. Non sarà difficile per una persona esperta di assembler rifare l'intero programma a suo piacimento. Forse è da qui che viene il problema più insolubile: il virus. Perché le persone scrivono un virus? Alcuni fanno questa domanda con sorpresa, altri con rabbia, ma tuttavia ci sono ancora persone che sono interessate a questo compito non dal punto di vista di causare qualche danno, ma come interesse per la programmazione del sistema. I virus vengono scritti per vari motivi. Ad alcuni piacciono le chiamate di sistema, altri migliorano le proprie conoscenze in assembler. Cercherò di spiegare tutto questo nel mio tesina. Dice anche non solo sulla struttura del file EXE, ma anche sul linguaggio assembly.

^ Linguaggio assembly.

È interessante seguire, a partire dall'epoca della comparsa dei primi computer e terminando ai giorni nostri, la trasformazione delle idee sul linguaggio assembly tra i programmatori.

Un tempo l'assembler era un linguaggio senza sapere quale era impossibile far fare a un computer qualcosa di utile. A poco a poco la situazione è cambiata. Apparvero mezzi di comunicazione più convenienti con un computer. Ma, a differenza di altri linguaggi, l'assemblatore non è morto, inoltre, non poteva farlo in linea di principio. Come mai? Alla ricerca di una risposta, cercheremo di capire cosa sia il linguaggio assembly in generale.

In breve, il linguaggio assembly è una rappresentazione simbolica del linguaggio macchina. Tutti i processi nella macchina al livello hardware più basso sono guidati solo da comandi (istruzioni) del linguaggio macchina. Da ciò risulta chiaro che, nonostante il nome comune, il linguaggio assembly per ogni tipo di computer è diverso. Questo vale anche aspetto esteriore programmi scritti in assembler e le idee di cui questo linguaggio è un riflesso.

È impossibile risolvere realmente problemi hardware (o anche, per di più, hardware, come migliorare la velocità di un programma), senza la conoscenza dell'assembler.

Un programmatore o qualsiasi altro utente può utilizzare qualsiasi strumento di alto livello, fino ai programmi per la costruzione di mondi virtuali, e, forse, nemmeno sospettare che il computer stia effettivamente eseguendo non i comandi del linguaggio in cui è scritto il suo programma, ma i loro rappresentazione trasformata sotto forma di sequenze noiose e noiose di comandi di un linguaggio completamente diverso: il linguaggio macchina. Ora immaginiamo che un tale utente abbia un problema non standard o semplicemente qualcosa sia andato storto. Ad esempio, il suo programma deve funzionare con un dispositivo insolito o eseguire altre azioni che richiedono la conoscenza dei principi dell'hardware del computer. Non importa quanto sia intelligente un programmatore, non importa quanto sia buono il linguaggio in cui ha scritto il suo meraviglioso programma, non può fare a meno della conoscenza dell'assembler. E non è un caso che quasi tutti i compilatori di linguaggi di alto livello contengano mezzi per connettere i propri moduli con moduli in assembler o supportino l'accesso al livello di programmazione assembler.

Certo, il tempo dei vagoni informatici è già passato. Come si suol dire, non puoi abbracciare l'immensità. Ma c'è qualcosa in comune, una sorta di fondamento su cui si basa qualsiasi serio educazione informatica. Questa è la conoscenza dei principi del funzionamento del computer, della sua architettura e del linguaggio assembly come riflesso e incarnazione di questa conoscenza.

Un tipico computer moderno (basato su i486 o Pentium) è costituito dai seguenti componenti (Figura 1).

Riso. 1. Computer e periferiche

Riso. 2. Schema a blocchi personal computer

Dalla figura (Figura 1) si può notare che il computer è costituito da più dispositivi fisici, ciascuno dei quali è connesso ad un'unità, denominata unità di sistema. Logicamente, è chiaro che svolge il ruolo di un dispositivo di coordinamento. Diamo un'occhiata all'interno blocco di sistema(non c'è bisogno di provare ad entrare nel monitor - non c'è niente di interessante lì, inoltre è pericoloso): apriamo la custodia e vediamo alcune schede, blocchi, fili di collegamento. Per capire la loro funzionalità, diamo un'occhiata diagramma a blocchi tipico computer (Fig. 2). Non pretende l'accuratezza assoluta e mira solo a mostrare lo scopo, l'interconnessione e la composizione tipica degli elementi di un moderno personal computer.

Discutiamo il diagramma di Fig. 2 in uno stile un po' non convenzionale.
È la natura umana, incontrare qualcosa di nuovo, cercare alcune associazioni che possano aiutarlo a conoscere l'ignoto. Quali associazioni evoca il computer? Per me, ad esempio, il computer è spesso associato alla persona stessa. Come mai?

Una persona che creava un computer da qualche parte nelle profondità di se stessa pensava di creare qualcosa di simile a se stesso. Il computer ha organi di percezione delle informazioni dal mondo esterno: questa è una tastiera, un mouse, unità disco magnetiche. Sulla fig. 2 questi organi si trovano a destra dei bus di sistema. Il computer ha organi che "digeriscono" le informazioni ricevute - questi sono processore e RAM. E, infine, il computer dispone di organi vocali che forniscono i risultati dell'elaborazione. Questi sono anche alcuni dei dispositivi sulla destra.

I computer moderni, ovviamente, sono tutt'altro che umani. Possono essere paragonati a esseri che interagiscono con il mondo esterno a livello di un insieme ampio ma limitato di riflessi incondizionati.
Questo insieme di riflessi forma un sistema di istruzioni della macchina. Non importa quanto sia alto il livello di comunicazione con un computer, alla fine tutto si riduce a una sequenza noiosa e monotona di istruzioni della macchina.
Ogni comando della macchina è una specie di stimolo per l'eccitazione di questo o quel riflesso incondizionato. La reazione a questo stimolo è sempre inequivocabile ed è "cablata" nel blocco di microcomandi sotto forma di microprogramma. Questo microprogramma implementa anche azioni per l'implementazione di un comando macchina, ma già a livello di segnali forniti a determinati circuiti logici di computer, controllando così vari sottosistemi di computer. Questo è il cosiddetto principio del controllo del microprogramma.

Continuando l'analogia con una persona, notiamo: perché un computer possa alimentarsi correttamente sono stati inventati molti sistemi operativi, compilatori per centinaia di linguaggi di programmazione, ecc.. Ma tutti sono, in effetti, solo un piatto su cui il cibo (programmi) viene consegnato secondo determinate regole stomaco (computer). Solo lo stomaco di un computer ama il cibo dietetico e monotono: dagli informazioni strutturate, sotto forma di sequenze rigorosamente organizzate di zero e uno, le cui combinazioni costituiscono il linguaggio macchina.

Pertanto, essendo esteriormente un poliglotta, il computer comprende solo una lingua: la lingua delle istruzioni della macchina. Certo, per comunicare e lavorare con un computer non è necessario conoscere questo linguaggio, ma quasi tutti i programmatori professionisti prima o poi si trovano ad affrontare la necessità di impararlo. Fortunatamente, il programmatore non ha bisogno di cercare di capire il significato di varie combinazioni di numeri binari, poiché già negli anni '50 i programmatori iniziarono a utilizzare l'analogo simbolico del linguaggio macchina per la programmazione, chiamato linguaggio assembly. Questo linguaggio riflette accuratamente tutte le caratteristiche del linguaggio macchina. Ecco perché, a differenza dei linguaggi di alto livello, il linguaggio assembly è diverso per ogni tipo di computer.

Da quanto precede, possiamo concludere che, poiché il linguaggio assembly per il computer è "nativo", il programma più efficiente può essere scritto solo in esso (a condizione che sia scritto da un programmatore qualificato). C'è un piccolo “ma” qui: questo è un processo molto laborioso che richiede molta attenzione ed esperienza pratica. Pertanto, in realtà, l'assembler scrive principalmente programmi che dovrebbero garantire un lavoro efficiente con l'hardware. A volte le parti critiche del programma in termini di tempo di esecuzione o consumo di memoria vengono scritte in assembler. Successivamente, vengono realizzati sotto forma di subroutine e combinati con codice in un linguaggio di alto livello.

Ha senso iniziare ad imparare il linguaggio assembly di qualsiasi computer solo dopo aver scoperto quale parte del computer è rimasta visibile e disponibile per la programmazione in questo linguaggio. Questo è il cosiddetto modello di programma per computer, parte del quale è il modello di programma a microprocessore, che contiene 32 registri più o meno disponibili per l'uso da parte del programmatore.

Questi registri possono essere divisi in due grandi gruppi:

^16 registri personalizzati;

16 registri di sistema.

I programmi in linguaggio assembly utilizzano molto i registri. La maggior parte dei registri ha uno scopo funzionale specifico.

Come suggerisce il nome, i registri utente vengono chiamati perché il programmatore può utilizzarli durante la scrittura dei suoi programmi. Questi registri includono (Fig. 3):

Otto registri a 32 bit che possono essere utilizzati dai programmatori per memorizzare dati e indirizzi (chiamati anche registri per uso generale (RON)):

registri a sei segmenti: cs, ds, ss, es, fs, gs;

stato e registri di controllo:

I flag registrano eflags/flag;

registro del puntatore di comando eip/ip.

Riso. 3. Registri utente dei microprocessori i486 e Pentium

Perché molti di questi registri sono mostrati con una barra? No, questi non sono registri diversi: sono parti di un grande registro a 32 bit. Possono essere usati nel programma come oggetti separati. Ciò è stato fatto per garantire l'operabilità dei programmi scritti per i modelli di microprocessori a 16 bit più giovani di Intel, a partire dall'i8086. I microprocessori i486 e Pentium hanno principalmente registri a 32 bit. Il loro numero, ad eccezione dei registri dei segmenti, è lo stesso di quello dell'i8086, ma la dimensione è maggiore, il che si riflette nelle loro designazioni: hanno
prefisso e (esteso).

^ Registri di uso generale
Tutti i registri di questo gruppo consentono di accedere alle loro parti “inferiori” (vedi Fig. 3). Osservando questa figura, si noti che 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. Questo viene fatto, come notato sopra, per la compatibilità con i modelli di microprocessori a 16 bit più giovani di Intel.

Elenchiamo i registri appartenenti al gruppo dei registri di uso generale. Poiché questi registri si trovano fisicamente nel microprocessore all'interno dell'unità logica aritmetica (ALU), sono anche chiamati registri ALU:

eax/ax/ah/al (registro accumulatore) - accumulatore.
Utilizzato per memorizzare dati intermedi. In alcuni comandi è richiesto l'uso di questo registro;

ebx/bx/bh/bl (registro di base) - registro di base.
Utilizzato per memorizzare l'indirizzo di base di un oggetto in memoria;

ecx/cx/ch/cl (Registro conteggio) - registro contatore.
Viene utilizzato nei comandi che eseguono alcune azioni ripetitive. Il suo utilizzo è spesso implicito e nascosto nell'algoritmo del comando corrispondente.
Ad esempio, il comando di organizzazione del ciclo, oltre a trasferire il controllo a un comando situato a un determinato indirizzo, analizza e decrementa di uno il valore del registro ecx/cx;

edx/dx/dh/dl (Registro dati) - registro dati.
Proprio come il registro eax/ax/ah/al, memorizza i dati intermedi. Alcuni comandi ne richiedono l'uso; per alcuni comandi questo accade implicitamente.

I seguenti due registri vengono utilizzati per supportare le cosiddette operazioni a catena, ovvero operazioni che elaborano in sequenza catene di elementi, ciascuna delle quali può essere lunga 32, 16 o 8 bit:

esi/si (Source Index register) - indice sorgente.
Questo registro nelle operazioni a catena contiene l'indirizzo corrente dell'elemento nella catena di origine;

edi/di (Destination Index register) - indice del destinatario (destinatario).
Questo registro nelle operazioni a catena contiene l'indirizzo corrente nella catena di destinazione.

Nell'architettura del microprocessore a livello hardware e software, è supportata una struttura di dati come uno stack. Per lavorare con lo stack nel sistema di istruzioni del microprocessore ci sono comandi speciali e nel modello software del microprocessore ci sono registri speciali per questo:

esp/sp (registro del puntatore dello stack) - registro del puntatore dello stack.
Contiene un puntatore all'inizio dello stack nel segmento dello stack corrente.

ebp/bp (registro del puntatore di base) - registro del puntatore di base dello stack frame.
Progettato per organizzare l'accesso casuale ai dati all'interno dello stack.

Uno stack è un'area di programma per la memorizzazione temporanea di dati arbitrari. Naturalmente i dati possono essere memorizzati anche nel segmento dati, ma in questo caso, per ogni dato memorizzato temporaneamente, deve essere creata una cella di memoria denominata separata, che aumenta le dimensioni del programma e il numero di nomi utilizzati. La comodità dello stack è che la sua area viene riutilizzata e la memorizzazione dei dati sullo stack e il loro recupero da lì avviene utilizzando comandi push e pop efficienti senza specificare alcun nome.
Lo stack viene tradizionalmente utilizzato, ad esempio, per memorizzare il contenuto dei registri utilizzati dal programma prima di chiamare una subroutine, che, a sua volta, utilizzerà i registri del processore "per i propri scopi". Il contenuto originale dei registri viene trapelato dallo stack al ritorno dalla subroutine. Un'altra tecnica comune consiste nel passare i parametri necessari a una subroutine tramite lo stack. La subroutine, sapendo in quale ordine i parametri sono inseriti nello stack, può prelevarli da lì e utilizzarli nella sua esecuzione. Caratteristica distintiva stack è una sorta di ordine di campionamento dei dati in esso contenuti: in ogni momento sullo stack è disponibile solo l'elemento in alto, cioè l'ultimo elemento caricato sullo stack. Spuntando l'elemento in cima alla pila rende disponibile l'elemento successivo. Gli elementi dello stack si trovano nell'area di memoria allocata per lo stack, partendo dal fondo dello stack (cioè dal suo indirizzo massimo) fino ad indirizzi decrescenti successivamente. L'indirizzo dell'elemento accessibile in alto è memorizzato nello stack pointer register SP. Come qualsiasi altra area della memoria di programma, lo stack deve essere incluso in un segmento o formare un segmento separato. In entrambi i casi, l'indirizzo del segmento di quel segmento viene inserito nel registro di stack del segmento SS. Pertanto, una coppia di registri SS:SP descrive l'indirizzo di una cella dello stack disponibile: SS memorizza l'indirizzo del segmento dello stack e SP memorizza l'offset degli ultimi dati memorizzati nello stack (Fig. 4, a). Prestiamo attenzione al fatto che nello stato iniziale, il puntatore dello stack SP punta a una cella che si trova sotto il fondo dello stack e non è inclusa in esso.

Fig 4. Organizzazione dello stack: a - lo stato iniziale, b - dopo aver caricato un elemento (c questo esempio- il contenuto del registro AX), c - dopo aver caricato il secondo elemento (il contenuto del registro DS), d - dopo aver scaricato un elemento, e - dopo aver scaricato due elementi e riportato allo stato originale.

Il caricamento sulla pila viene effettuato da un apposito comando push stack. Questa istruzione decrementa prima il contenuto del puntatore dello stack di 2, quindi posiziona l'operando all'indirizzo in SP. Se, ad esempio, vogliamo salvare temporaneamente il contenuto del registro AX nello stack, dovremmo eseguire il comando

Lo stack passa allo stato mostrato in Fig. 1.10, b. Si può notare che il puntatore dello stack viene spostato verso l'alto di due byte (verso gli indirizzi inferiori) e l'operando specificato nel comando push viene scritto a questo indirizzo. Il seguente comando da caricare nello stack, ad esempio,

sposterà la pila nello stato mostrato in Fig. 1.10, c. Lo stack ora conterrà due elementi, con accesso solo a quello superiore, indicato dal puntatore dello stack SP. Se, dopo un po' di tempo, dobbiamo ripristinare il contenuto originale dei registri salvati sullo stack, dobbiamo eseguire i comandi pop (pop) dallo stack:

pop DS
pop AX

Quanto dovrebbe essere grande la pila? Dipende da quanto intensamente viene utilizzato nel programma. Se, ad esempio, prevedi di archiviare una matrice di 10.000 byte nello stack, lo stack deve avere almeno quella dimensione. Va tenuto presente che in alcuni casi lo stack viene utilizzato automaticamente dal sistema, in particolare durante l'esecuzione del comando di interrupt int 21h. Con questo comando, il processore prima inserisce l'indirizzo di ritorno nello stack, quindi DOS inserisce lì il contenuto dei registri e altre informazioni relative al programma interrotto. Pertanto, anche se il programma non utilizza affatto lo stack, deve essere comunque presente nel programma e avere una dimensione di almeno alcune decine di parole. Nel nostro primo esempio, mettiamo in pila 128 parole, il che è decisamente sufficiente.

^ Struttura del programma di assemblaggio

Un programma in linguaggio assembly è una raccolta di blocchi di memoria chiamati segmenti di memoria. Un programma può essere costituito da uno o più di questi segmenti di blocco. Ogni segmento contiene una raccolta di frasi linguistiche, ognuna delle quali occupa una riga separata di codice del programma.

Le dichiarazioni di assemblaggio sono di quattro tipi:

comandi o istruzioni che sono controparti simboliche di istruzioni macchina. Durante il processo di traduzione, le istruzioni di montaggio vengono convertite nei comandi corrispondenti del set di istruzioni del microprocessore;

macro-comandi - frasi del testo del programma che sono progettate in un certo modo e sono sostituite da altre frasi durante la traduzione;

direttive che dicono al compilatore assembler di eseguire alcune azioni. Le direttive non hanno controparti nella rappresentazione della macchina;

righe di commento contenenti qualsiasi carattere, comprese le lettere dell'alfabeto russo. I commenti vengono ignorati dal traduttore.

^ Sintassi del linguaggio assembly

Le frasi che compongono un programma possono essere un costrutto sintattico corrispondente a un comando, una macro, una direttiva o un commento. Affinché il traduttore assembler possa riconoscerli, devono essere formati secondo determinate regole sintattiche. Per fare ciò, è meglio utilizzare una descrizione formale della sintassi della lingua, come le regole della grammatica. I modi più comuni per descrivere un linguaggio di programmazione in questo modo sono i diagrammi di sintassi e le forme estese di Backus-Naur. Per un uso pratico, i diagrammi di sintassi sono più convenienti. Ad esempio, la sintassi delle istruzioni in linguaggio assembly può essere descritta utilizzando i diagrammi di sintassi mostrati nelle figure seguenti.

Riso. 5. Formato frase Assembler

Riso. 6. Direttive di formattazione

Riso. 7. Formato dei comandi e delle macro

Su questi disegni:

nome dell'etichetta - un identificatore il cui valore è l'indirizzo del primo byte della frase del codice sorgente del programma che denota;

name - un identificatore che distingue questa direttiva da altre direttive con lo stesso nome. A seguito dell'elaborazione da parte dell'assemblatore di una determinata direttiva, alcune caratteristiche possono essere assegnate a questo nome;

codice operazione (COP) e direttiva sono designazioni mnemoniche della corrispondente istruzione macchina, istruzione macro o direttiva traduttore;

operandi - parti del comando, delle direttive macro o assembler, che denotano oggetti su cui vengono eseguite le operazioni. Gli operandi Assembler sono descritti da espressioni con costanti numeriche e di testo, etichette di variabili e identificatori che utilizzano segni di operazione e alcune parole riservate.

^ Come utilizzare i diagrammi di sintassi? È molto semplice: tutto ciò che devi fare è trovare e quindi seguire il percorso dall'input del diagramma (a sinistra) al suo output (a destra). Se esiste un tale percorso, la frase o la costruzione è sintatticamente corretta. Se non esiste un tale percorso, il compilatore non accetterà questa costruzione. Quando si lavora con i diagrammi sintattici, prestare attenzione alla direzione del bypass indicato dalle frecce, poiché tra i percorsi potrebbero esserci quelli che possono essere seguiti da destra a sinistra. In effetti, i diagrammi sintattici riflettono la logica del traduttore durante l'analisi delle frasi di input del programma.

I caratteri consentiti durante la scrittura del testo dei programmi sono:

Tutte le lettere latine: A-Z, a-z. In questo caso, le lettere maiuscole e minuscole sono considerate equivalenti;

Numeri da 0 a 9;

Segni?, @, $, _, &;

Separatori, . ()< > { } + / * % ! " " ? \ = # ^.

Le frasi Assembler sono formate da lessemi, che sono sequenze sintatticamente inseparabili di simboli linguistici validi che hanno senso per il traduttore.

I token sono:

gli identificatori sono sequenze di caratteri validi utilizzati per designare oggetti di programma come codici operativi, nomi di variabili e nomi di etichette. La regola per la scrittura degli identificatori è la seguente: un identificatore può essere composto da uno o più caratteri. Come caratteri, puoi usare lettere dell'alfabeto latino, numeri e alcuni personaggi speciali- _, ?, $, @. Un identificatore non può iniziare con un carattere cifra. La lunghezza dell'identificatore può essere fino a 255 caratteri, sebbene il traduttore accetti solo i primi 32 caratteri e ignori il resto. È possibile regolare la lunghezza dei possibili identificatori utilizzando l'opzione riga di comando mv. Inoltre, è possibile dire al traduttore di distinguere tra lettere maiuscole e minuscole o ignorare la loro differenza (cosa che viene eseguita per impostazione predefinita).

^ Comandi in linguaggio assembly.

I comandi Assembler aprono la possibilità di trasferire i propri requisiti al computer, il meccanismo per trasferire il controllo nel programma (loop e salti) per i confronti logici e l'organizzazione del programma. Tuttavia, le attività di programmazione raramente sono così semplici. La maggior parte dei programmi contiene una serie di cicli in cui vengono ripetute diverse istruzioni fino al raggiungimento di un determinato requisito e vari controlli per determinare quale delle varie azioni eseguire. Alcuni comandi possono trasferire il controllo modificando la normale sequenza di passaggi modificando direttamente il valore di offset nel puntatore del comando. Come accennato in precedenza, esistono comandi diversi per processori diversi, ma considereremo alcuni comandi per processori 80186, 80286 e 80386.

Per descrivere lo stato dei flag dopo aver eseguito un determinato comando, utilizzeremo una selezione dalla tabella che riflette la struttura del registro flag eflags:

La riga inferiore di questa tabella elenca i valori dei flag dopo l'esecuzione del comando. In questo caso si utilizzano le seguenti notazioni:

1 - dopo l'esecuzione del comando viene impostato il flag (uguale a 1);

0 - dopo l'esecuzione del comando, il flag viene resettato (pari a 0);

r - il valore del flag dipende dal risultato del comando;

Dopo aver eseguito il comando, il flag è indefinito;

spazio - dopo aver eseguito il comando, il flag non cambia;

Per rappresentare gli operandi in diagrammi di sintassi si usa la seguente notazione:

r8, r16, r32 - operando in uno dei registri di dimensione byte, parola o doppia parola;

m8, m16, m32, m48 - operando nella dimensione della memoria di byte, parola, doppia parola o 48 bit;

i8, i16, i32 - operando immediato di dimensione byte, parola o doppia parola;

a8, a16, a32 - indirizzo relativo (offset) nel segmento di codice.

Comandi (in ordine alfabetico):

*Questi comandi sono descritti in dettaglio.

INSERISCI
(AGGIUNTA)

Aggiunta

^ Schema del comando:

aggiungi destinazione, origine

Scopo: aggiunta di due operandi di origine e destinazione di dimensioni byte, parola o doppia parola.

Algoritmo di lavoro:

aggiungere gli operandi di origine e di destinazione;

scrivere il risultato dell'addizione al destinatario;

impostare le bandiere.

Stato dei flag dopo l'esecuzione del comando:

Applicazione:
Il comando add viene utilizzato per aggiungere due operandi interi. Il risultato dell'addizione viene posto all'indirizzo del primo operando. Se il risultato dell'addizione va oltre i limiti dell'operando di destinazione (si verifica un overflow), allora questa situazione dovrebbe essere presa in considerazione analizzando il flag cf ed eventualmente usando il comando adc. Ad esempio, aggiungiamo i valori nel registro ax e nell'area di memoria ch. Quando aggiungi, dovresti prendere in considerazione la possibilità di overflow.

Registro più registro o memoria:

|000000dw|modregr/rm|

Registro AX (AL) più valore immediato:

|0000010w|--dati--|dati se w=1|

Registro o memoria più valore immediato:

|100000sw|mod000r/m|--data--|data if BW=01|

CHIAMATA
(CHIAMATA)

Chiamare una procedura o un'attività

^ Schema del comando:

Scopo:

passaggio del controllo ad una procedura close o far con memorizzazione dell'indirizzo del punto di ritorno sullo stack;

cambio di attività.

Algoritmo di lavoro:
determinato dal tipo di operando:

L'etichetta è vicina: il contenuto del puntatore di comando eip / ip viene inserito nello stack e nello stesso registro viene caricato un nuovo valore di indirizzo corrispondente all'etichetta;

Far label: il contenuto del puntatore di comando eip/ip e cs viene inserito nello stack. Successivamente vengono caricati negli stessi registri i nuovi valori di indirizzo corrispondenti al segno lontano;

R16, 32 o m16, 32 - definiscono un registro o una cella di memoria contenente gli offset nel segmento di istruzione corrente, in cui viene trasferito il controllo. Quando il controllo viene trasferito, il contenuto del puntatore di comando eip/ip viene inserito nello stack;

Puntatore di memoria: definisce una posizione di memoria contenente un puntatore da 4 o 6 byte alla procedura chiamata. La struttura di un tale puntatore è 2+2 o 2+4 byte. L'interpretazione di tale puntatore dipende dalla modalità operativa del microprocessore:

^ Stato dei flag dopo l'esecuzione del comando (tranne il cambio di attività):

l'esecuzione del comando non ha effetto sui flag

Quando un'attività viene cambiata, i valori dei flag vengono modificati in base alle informazioni sul registro eflags nel segmento di stato TSS dell'attività a cui si passa.
Applicazione:
Il comando call consente di organizzare un trasferimento flessibile e multivariato del controllo a una subroutine mantenendo l'indirizzo del punto di ritorno.

Codice oggetto (quattro formati):

Indirizzamento diretto in un segmento:

|11101000|disp-basso|diep-alto|

Indirizzamento indiretto in un segmento:

|11111111|mod010r/m|

Indirizzamento indiretto tra segmenti:

|11111111|mod011r/m|

Indirizzamento diretto tra segmenti:

|10011010|offset-basso|offset-alto|segmento-basso|segmento-alto|

CMP
(confronta operandi)

Confronto tra operandi

^ Schema del comando:

cmp operando1, operando2

Scopo: confronto di due operandi.

Algoritmo di lavoro:

eseguire la sottrazione (operando1-operando2);

a seconda del risultato, impostare flag, non modificare operando1 e operando2 (ovvero non memorizzare il risultato).

Applicazione:
Questa istruzione viene utilizzata per confrontare due operandi mediante sottrazione, mentre gli operandi non vengono modificati. I flag vengono impostati come risultato dell'esecuzione del comando. L'istruzione cmp viene utilizzata con le istruzioni di salto condizionale e l'istruzione set byte by value setcc.

Codice oggetto (tre formati):

Registro o Memoria Registrata:

|001110dw|modreg/m|

Valore immediato con registro AX (AL):

|0011110w|--dati--|dati se w=1|

Valore immediato con registro o memoria:

|100000sw|mod111r/m|--data--|data if sw=0|

DIC
(Decrementa l'operando di 1)

Operando decrementa di uno

^ Schema del comando:

operando dic

Scopo: diminuire di 1 il valore dell'operando in memoria o registro.

Algoritmo di lavoro:
l'istruzione sottrae 1 dall'operando. Stato dei flag dopo l'esecuzione del comando:

Applicazione:
Il comando dec viene utilizzato per decrementare di uno il valore di un byte, word, double word in memoria o registro. Si noti che il comando non ha effetto sul flag cf.

Registrati: |01001reg|

^ Registro o memoria: |1111111w|mod001r/m|

DIV
(DIVIDE non firmato)

Divisione non firmata

Schema di comando:

divisore div

Scopo: eseguire un'operazione di divisione su due valori binari senza segno.

^ Algoritmo di lavoro:
Il comando richiede due operandi: il dividendo e il divisore. Il dividendo è specificato implicitamente e la sua dimensione dipende dalla dimensione del divisore, che è specificata nel comando:

se il divisore è in byte, il dividendo deve trovarsi nel registro ax. Dopo l'operazione si pone il quoziente in al e il resto in ah;

se il divisore è una parola, allora il dividendo deve trovarsi nella coppia di registri dx:ax, con la parte bassa del dividendo in ax. Dopo l'operazione si pone il quoziente in ax e il resto in dx;

se il divisore è una doppia parola, allora il dividendo deve trovarsi nella coppia di registri edx:eax, con la parte bassa del dividendo in eax. Dopo l'operazione si pone il quoziente in eax e il resto in edx.

^ Stato dei flag dopo l'esecuzione del comando:

Applicazione:
Il comando esegue una divisione intera degli operandi, restituendo il risultato della divisione come quoziente e il resto della divisione. Quando si esegue un'operazione di divisione, può verificarsi un'eccezione: 0 - errore di divisione. Questa situazione si verifica in uno dei due casi: il divisore è 0 o il quoziente è troppo grande per rientrare nel registro eax/ax/al.

Codice oggetto:

|1111011w|mod110r/m|

INT
(Interrompere)

Chiamata di una routine di servizio di interruzione

^ Schema del comando:

int numero_interruzione

Scopo: richiamare la routine del servizio di interrupt con il numero di interrupt specificato dall'operando dell'istruzione.

^ Algoritmo di lavoro:

inserire il registro eflags/flags e l'indirizzo di ritorno nello stack. Quando si scrive l'indirizzo di ritorno, viene prima scritto il contenuto del registro del segmento cs, quindi il contenuto del puntatore di comando eip/ip;

azzerare i flag if e tf;

trasferire il controllo al gestore di interrupt con numero specificato. Il meccanismo di trasferimento del controllo dipende dalla modalità operativa del microprocessore.

^ Stato dei flag dopo l'esecuzione del comando:

Applicazione:
Come puoi vedere dalla sintassi, ci sono due forme di questo comando:

int 3 - ha il proprio codice operativo 0cch e occupa un byte. Questa circostanza rende molto comodo l'uso in vari debugger software per impostare punti di interruzione sostituendo il primo byte di qualsiasi istruzione. Il microprocessore, incontrando un comando con l'opcode 0cch nella sequenza di comandi, chiama il gestore di interrupt con il vettore numero 3, che serve a comunicare con il debugger del software.

La seconda forma dell'istruzione è lunga due byte, ha un codice operativo 0cdh e consente di avviare una chiamata a una routine di servizio di interruzione con un numero vettoriale nell'intervallo 0-255. Le caratteristiche del trasferimento del controllo, come notato, dipendono dalla modalità operativa del microprocessore.

Codice oggetto (due formati):

Registrati: |01000reg|

^ Registro o memoria: |1111111w|mod000r/m|

JCC
JCXZ/JECXZ
(Salta se condizione)

(Salta se CX=Zero/Salta se ECX=Zero)

Salta se la condizione è soddisfatta

Salta se CX/ECX è zero

^ Schema del comando:

etichetta jcc
etichetta jcxz
etichetta jecxz

Scopo: transizione all'interno del segmento corrente dei comandi, a seconda di alcune condizioni.

^ Algoritmo di comando (tranne jcxz/jecxz):
Verifica dello stato dei flag in base all'opcode (rispecchia la condizione in fase di verifica):

se la condizione in esame è vera, andare nella cella indicata dall'operando;

se la condizione da controllare è falsa, passare il controllo al comando successivo.

Algoritmo di comando jcxz/jecxz:
Verificando la condizione che il contenuto del registro ecx/cx sia uguale a zero:

se la condizione verificata