Assembly 8088

Share Embed Donate


Short Description

Manuale di Assembly 8088...

Description

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

Appunti di programmazione in linguaggio

Assembly 8088

“la presente dispensa rappresenta un riassunto della parte di programmazione   in linguaggio assemblativo, tratta dal libro1 di testo usato a lezione”

1 Andrew S. Tanenbaum, Architettura dei calcolatori, Un approccio strutturale - 5^ed.

Pagina 1

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

Indice generale PARTE 1, IL LINGUAGGIO ASSEMBLY 1.1 – Introduzione PARTE 2, IL PROCESSORE 8088 2.1 – Introduzione 2.2 – Ciclo di Neumann 2.3 – Registri dell'architettura 8088 2.3.1 – Registri ad uso generale 2.3.2 – Registri puntatore 2.3.3 – Registri indice 2.3.4 – Registro di flag 2.3.5 – Registro IP (Instruction Pointer) 2.3.6 – Registri di segmento PARTE 3, INDIRIZZAMENTO DELLA MEMORIA 3.1 – Introduzione all'indirizzamento 3.2 – Indirizzamento diretto 3.3 – Indirizzamento indiretto 3.4 – Indirizzamento con spiazzamento 3.5 – Indirizzamento a registro con indice 3.6 – Indirizzamento immediato 3.7 – Indirizzamento implicito PARTE 4, ISTRUZIONI DELL'8088 4.1 – Trasferimento, copia e aritmetica 4.2 – Cicli e operazioni iterative su stringhe 4.3 – Istruzioni di salto e di chiamata 4.3.1 – Salti condizionali 4.4 – Chiamate di subroutine 4.5 – Chiamate di sistema e subroutine di sistema PARTE 5, ASSEMBLATORE as88 5.1 – Introduzione all'assemblatore PARTE 6, ESEMPIO DI PROGRAMMAZIONE ASSEMBLY 6.1 – Esempio di “Hello, World!”

Pagina 2

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

PARTE 1, LINGUAGGIO ASSEMBLATIVO 1.1 – Introduzione L'assemblatore è un programma che riceve in ingresso un file contenente un  programma in  linguaggio assembly, e genera un programma che contiene  codice  binario, pronto ad essere eseguito. Gli assemblatori usano nomi simbolici per  variabili   e   costanti,   ed  etichette  per   le   istruzioni   e   gli   indirizzi   di  memoria.

PARTE 2, IL PROCESSORE 8088 2.1 – Introduzione Il processore è composto da un insieme di registri, utili per memorizzare le  informazioni importanti per il suo funzionamento. Tra i vari registri, quello  più importante è il registro  PC  (Program Counter), il quale contiene al suo  interno   l'indirizzo   della   prossima   istruzione   da   eseguire.   A   volte   questo  registro viene detto anche IP (Instruction Pointer). 2.2 – Ciclo di Neumann L'attività   dei   processori   consiste   nell'esecuzione   di   istruzioni   in   rapida  successione. L'esecuzione di ogni singola istruzione viene scomposta in più  fasi: ­ Fetch dell'istruzione dalla memoria tramite il registro PC ­ Incremento l'indirizzo del registro PC ­ Decodifica dell'istruzione prelevata ­ Fetch dei dati necessari dalla memoria o dai registri ­ Esecuzione dell'istruzione ­ Memorizzazione dell'istruzione in memoria o nei registri ­ Ritorno al punto iniziale 2.3 – Registri dell'architettura 8088 L'architettura   8088   ha   a   disposizione  14   registri,   che   costituiscono   lo  spazio di lavoro a disposizione del programmatore. I registri all'interno di  questa   architettura   si   dividono   a   seconda   l'uso   a   cui   sono   destinati:  Registri ad uso generale, Registri di segmento, Registri puntatore, Registri   indici, Codici di condizioni, Registro     Instruction Pointer (IP)    . 2.3.1 – Registri ad uso generale I   registri   ad   uso   generale   possono   essere   visti   come   registri   di   16bit,  oppure   come   delle   coppie   di   registri   formate   da   8bit.   Quando   andiamo   ad  accedere al registri AX in realtà quello che stiamo facendo è accedere a due  registri, ovvero AH:AL, dove AL rappresenta i primi 8bit del dato (partendo  da sinistra) e AH rappresenta gli altri 8bit (quelli più significativi). I  registri che fanno parte in questo gruppo sono: AX  →  registro   accumulatore,   viene   usato   spesso   per   contenere   il   risultato 

Pagina 3

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

delle operazioni. BX  →  registro di base, può essere usato al posto di AX, ma ha anche alcune  funzioni che invece il registro AX non possiede. Ad esempio il registro BX  può contenere un puntatore alla memoria, mentre AX no. Vediamo un esempio: MOV AX, BX “copia in AX il contenuto di BX” MOV AX,(BX) “copia in AX la parola di memoria che si   trova all'indirizzo specificato da BX” CX  →  registro contatore, viene usato spesso nei cicli e viene decrementato  automaticamente dall'istruzione LOOP e i cicli terminano quando CX ha valore  zero (“0”). DX  →  registro   dati,   è   usato   spesso   insieme   al   registro   AX   per   contenere  istruzioni lunghe 32 bit (DX:AX). 2.3.2 – Registri puntatore Tra   questi   registri   troviamo   il   puntatore   allo   Stack   detto  SP  (Stack  Pointer). Un'altro registro importante è il BP (Base Pointer) il quale punta  alla base dello Stack. Dunque il BP punta alla base del record di attivazione  corrente (al suo indirizzo più grande) e SP punta alla cima del record di  attivazione   (al   suo   indirizzo   più   piccolo),   perciò   questi   due   registri  delimitano il record di attivazione. 2.3.3 – Registri indice Appartengono   a   questo   gruppo   i   due   registri  SI  (indice   sorgente),   e  DI  (indice destinazione). Questi registri sono spesso usati in combinazione con  BP per fare riferimento ai dati nello Stack, o con BX per localizzare i dati  in memoria. 2.3.4 – Registro di flag E'   un   insieme   di   registri   da   1bit   ciascuno.   Alcuni   di   questi   bit  rappresentano il risultato di espressioni aritmetiche, in questo modo: Z – risultato zero S – risultato negativo V – risultato di overflow C – risultato con riporto A – risultato con riporto ausiliario (oltre i 3bit) P – parità del risultato I – attivazione degli interrupt T – attiva la modalità di tracing D – controlla la direzione delle operazioni su stringhe 2.3.5 – Registro IP (Instruction Pointer) Il   registro   IP   punta   all'indirizzo   dell'istruzione   successiva   a   quella  attualmente in esecuzione. Questo registro non viene direttamente usato dalle  istruzioni.

Pagina 4

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

2.3.6 – Registri di segmento L'architettura 8088 usa il concetto di  segmento  per distinguere le diverse  porzioni di memoria. Esistono 4 segmenti: CS segmento di codice, Contiene le istruzioni del programma. Il registro PC è interpretato come un  registro appartenente a questo segmento. DS segmento dati, Contiene i dati inizializzati e non del programma. SS segmento di stack, Contiene le variabili locali e i risultati intermedi. Gli indirizzi SP e BP  fanno riferimento a questo segmento. Il segmento di Stack è costituito da parole di 2byte, per cui il puntatore  allo   stack   (SP)   contiene   sempre   un   numero   pari.   Esso   è   riempito   in   senso  decrescente a partire dagli indirizzi più grandi. Il   comando  PUSH  decrementa   di   2  il   puntatore  allo   stack   (SP),   e  memorizza  l'operando in cima allo stack. Il comando POP recupera il valore in cima allo  stack e incrementa di 2 il puntatore allo stack (SP).  ES segmento extra,  è   un   segmento   di   riserva   che   può   essere   allocato   in   memoria   ovunque   sia  necessario. L'indirizzo   iniziale   di   un   segmento   si   ottiene   estendendo   i   16bit   del  registro   concatenandogli   4   zeri   sulla   destra   (nelle   posizioni   meno  significative).   Questo   vuol   dire   che   gli   indirizzi   dei   segmenti   indicano  sempre indirizzi multipli di 16. In pratica un  indirizzo fisico  si calcola  moltiplicando il registro di segmento per 16 e sommandogli l'offset. L'indirizzo fisico della successiva istruzione da eseguire si ottiene facendo  scorrere il contenuto del registro CS di 4 posizioni a sinistra e sommando il  valore del PC.

PARTE 3, INDIRIZZAMENTO DELLA MEMORIA 3.1 – Introduzione all'indirizzamento L'8088   mette   a   disposizione   alcune   modalità   di   indirizzamento   per   far  riferimento   ai   dati   in   memoria.   Molte   istruzioni   contengono   2   operandi  chiamati in genere destinazione e sorgente. Ad esempio le istruzioni: MOV  AX, BX ADD  CX, 270

“esegue una copia del contenuto di BX in AX” “somma al contenuto di CX il valore 270”

L'architettura 8088 richiede in un istruzione, che almeno uno degli operandi  sia   un   registro.   Esistono   alcune   istruzioni   a   un   operando   (come   gli  incrementi, decrementi, scorrimenti, negazioni, etc.,). In questi casi non ci  sono vincoli sui registri. L'8088 supporta 4 tipi di dati fondamentali:

Pagina 5

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

byte,  word  (2 byte),  long  (4 byte) e  decimali binari  (BCD2, non supportati  dal   assemblatore   in   allegato   al   libro).   Un   indirizzo   di   memoria   fa  riferimento sempre ad 1 byte, ma in caso di riferimento ad una parola ci si  riferisce   implicitamente   anche   ai   byte   che   seguono   quello   indicato.   Ad  esempio la parola all'indirizzo 20 comprende le locazioni di memoria 20 e 21.  L'8088   assume   un   ordinamento   di   tipo  little­endian,   cioè   la   parte   meno  significativa di una parola è memorizzata all'indirizzo minore. All'interno   dello   Stack   le   parole   devono   essere   allocate   in   corrispondenza  degli  indirizzi   pari.   La   coppia   di   registri  DX:AX  è   l'unica   messa   a  disposizione   dal   processore   per   i   dati   long   (AX   contiene   la   parte   meno   significativa). 3.2 – Indirizzamento diretto Questa modalità di indirizzamento  è quella più semplice, mediante la quale  l'istruzione contiene l'indirizzo dei dati nell'operando stesso. Ad esempio  l'istruzione: ADD CX, (20) specifica   che   bisogna   sommare   al   contenuto   del   registro   CX,   il   contenuto  della parola di memoria agli indirizzi 20 e 21. 3.3 – Indirizzamento indiretto In   questo   caso   l'indirizzo   dell'operando   viene   memorizzato   in   uno   dei  registri  BX,  SI,  DI. In tutti e tre i casi si tratta di un operando che fa  riferimento al segmento dati. 3.4 – Indirizzamento con spiazzamento Questo   tipo   di   indirizzamento   è   particolarmente   utile   per   il   trattamento  degli   array.   Ad   esempio   se   il   registro  SI   =   5,   è   possibile   caricare   nel  registro   AL   il   quinto   carattere   della   stringa   FORMAT,   con   la   seguente  istruzione: MOVB AL, FORMAT(SI) L'intera   stringa   può   essere   scandita  incrementando  o  decrementando  il  registro ad ogni passo. 3.5 – Indirizzamento a registro con indice L'indirizzamento a registro con indice è molto simile all'indirizzamento con  spiazzamento.   In   questo   caso   si   memorizza   la   base   dell'array   (cioè   il   suo  indirizzo minore) nel registro BX e si usa il registro SI o DI per scandirlo. Ad esempio: PUSH (BX)(DI) L'istruzione   effettua   la   somma   degli   indirizzi   tra   il   registro   BX   e  DI,  e  successivamente si posiziona al indirizzo dato dalla somma dei due registri,  preleva il contenuto e lo posiziona in cima allo Stack. 3.6 – Indirizzamento immediato In questo caso l'operando dell'istruzione è una costante. Ad esempio: CMP AX, 50 2 Binary Coded Decimal

Pagina 6

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

Confronta il registro AX con il valore 50 e  memorizza  l'esito del confronto  nel registro dei flag. 3.7 – Indirizzamento implicito L'operando   o   gli   operandi   dell'istruzione   sono   specificati   implicitamente  dall'istruzione stessa: PUSH AX Pone in cima allo stack il contenuto di AX, e allo stesso tempo decrementa  il valore di SP.

PARTE 4, ISTRUZIONI DELL'8088 4.1 – Trasferimento, copia e aritmetica MOV dest, src  L'istruzione   più   utilizzata   è   MOV,   che   specifica   una  sorgente  ed   una  destinazione.   Se   la   sorgente   è   un   registro,   allora   la   destinazione   deve  essere   un   indirizzo   effettivo.   E'   possibile   anche   avere   per   sorgente   un  indirizzo   effettivo   e   per   destinazione   un   registro.   E'   possibile   fare  riferimento anche alla parola chiave  MOVB,  per i trasferimenti di byte. Le  istruzioni di trasferimento non hanno alcun effetto sul registro dei flag. E'  necessario notare che le istruzioni di trasferimento non trasferiscono dati,  ma copiano solamente. XCHG dest, src Quest'istruzione   serve   a   scambiare   il   contenuto   di   un   registro   con   il  contenuto   di   un'indirizzo   effettivo   e   viceversa.   Esiste   anche   per   questa  parola chiave la sua versione per il trasferimento dei byte, XCHGB. LEA dest, src LEA   sta   per   “Load   Effective   Address”,   ovvero   “Caricamento   di   un   indirizzo   effettivo”. Serve a calcolare il valore numerico dell'indirizzo effettivo e a  memorizzarlo in un registro. (dest=registro, src=indirizzo effettivo). PUSH opr L'istruzione PUSH serve ad inserire operandi in cima allo Stack. L'operando  in   questione   può   essere   una   costante   oppure   un   indirizzo.   In   realtà   ogni  volta   che   si   inserisce   nello   Stack   con   quest'istruzione,   viene  “implicitamente” decrementato il valore del puntatore allo Stack (SP “Stack  Pointer”)   di   2  e   viene   inserito  il   valore   alla   nuova   locazione   in   memoria  puntata da SP. POP addr Rimuove   l'operando   dallo   Stack   e   lo   salva   nell'indirizzo   effettivo   (addr).  Anche   in   questo  caso   viene   modificato   il   valore  del  registro   SP,   il  quale  viene incrementato di 2.

Pagina 7

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

PUSHF opr  |  POPF addr Effettuano la PUSH e la POP del registro dei flag. XLAT Serve a caricare nel registro AL il byte che si trova all'indirizzo AL + BX.  E' utile per fare ricerche rapide nelle tabelle di dimensioni pari a 256byte. IN e OUT Sono definite dall'8088 (ma non sono implementate nell'interprete fornito con  il   libro   di   Tanenbaum).   Si   tratta   di   istruzioni   di   trasferimento   da/verso  dispositivi di I/O. Altre istruzioni del linguaggio Assembly: Operatori aritmetici Tutte   le   istruzioni   in   questo   gruppo   modificano   lo   stato   dei   registri   di  flag, in base all'istruzione eseguita. ADD  a,b “Somma: Aggiunge 'b' in 'a'” ADC  a,b “Somma con riporto” SUB  a,b “Sottrazione” SBB  a,b “Sottrazione con prestito” MUL  a,b “Moltiplicazione senza segno” DIV  a,b “Divisione senza segno” IMUL a,b “Moltiplicazione con segno” IDIV a,b “Divisione con segno” Altri operatori aritmetici INC  dest “Incremento della destinazione” DEC  dest “Decremento della destinazione” NEG  dest “Complemento binario” CMP a,b “Confronto tra gli operandi a e b” CLC “Azzeramento del flag di riporto” Operatori logici NOT  dest “Complemento logico” AND a,b “a AND b” OR a,b “a OR  b” XOR a,b “a XOR b” Operazioni di salto ICC et “Salto condizionato (et: etichetta)” 4.2 – Cicli e operazioni iterative su stringhe LOOP etichetta Decrementa il registro CX e se il risultato è positivo salta all'etichetta  specificata. LOOPZ etichetta Confronta   il   registro   CX   con   il   registro   di   flag   Z   per   decidere   se  interrompere   l'esecuzione   del   ciclo.   Se  DEC(CX)      ≥   0 e  Z=1  il   ciclo   si  interrompe.

Pagina 8

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

LOOPNZ etichetta Confronta   il   registro   CX   con   il   registro   di   flag   Z   per   decidere   se  interrompere   l'esecuzione   del   ciclo.   Se  DEC(CX)≥0  e  Z=0  il   ciclo   si  interrompe. Esistono   anche   meccanismi   iterativi   per   il   trattamento   delle   stringhe,  disponibili   dalle   istruzioni  REP,  REPZ,  REPNZ.   Queste   istruzioni   prevedono  che   il   registro  SI  punti   al  segmento   dati,   mentre  DI  al  segmento   extra  localizzato dall'indirizzo ES.  4.3 – Istruzioni di salto e di chiamata L'operazione   di   salto   più   semplice   è   svolta   dall'istruzione  JMP  che   può  contenere   come   destinazione   un   indirizzo   oppure   un   etichetta.   Esistono   due  tipi di salti:  NEAR  (vicino) e  FAR  (lungo). Si ha un salto corto quando la  destinazione si trova nel segmento di codice corrente, che non può modificare  durante   l'esecuzione   del   programma.   In   caso   di   salto   lungo   invece   il  contenuto del registro CS (Code Segment) viene modificato. 4.3.1 – Salti condizionati L'8088 ha 15 istruzioni di salto condizionato: JNA (Jump if Not Above), JBE (Jump if Below or Equal)

Al di sotto o uguale

JNB (Jump if Not Below), JAE (Jump if Above or Equal), JNC (Jump if Not Carry)

Non al di sotto

CF=0

Zero, Uguale

ZF=1

Maggiore di..

SF=OF, ZF=0

JE (Jump if Equal),  JZ (Jump if Zero) JNLE (Jump if Not Less than),  JG (Jump if Greater)

Maggiore o uguale

JNL (Jump if Not Less than),  JGE (Jump if Greater or Equal) 

CF=1, ZF=0

SF=OF

Overflow

OF=1

Segno negativo

SF=1

CX=0

CX=0

JB (Jump if Below),  JNAE (Jump if Not Above or Equal),  JC (Jump if Carry)

Al di sotto

CF=1

JNBE (Jump if Not Below or Equal), JA  (Jump if Above)

Al di sopra

CF=0 & ZF=0

JO (Jump if Overflow) JS JCXZ (Jump if CX Equal Zero)

JNE (Jump if Not Equal),  JNZ (Jump if Not Zero) JL (Jump if Less than),  JNGE (Jump if Not Greater or Equal)

Diverso da zero

ZF=0

Minore di..

SF≠OF

Minore o uguale

JLE (Jump if Less than),  JNG (Jump if Not Greater than) JNO (Jump if Not Overflow) JNS

Pagina 9

SF≠OF, ZF=1

No overflow

OF=0

Non negativo

SF=0

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

4.4 – Chiamate di subroutine L'8088   dispone   di   un'istruzione   per   la   chiamata   di   procedura,   detta   anche  subroutine. Analogamente alle istruzioni di salto, anche per le subroutine si  fa distinzione tra  chiamate vicine  e  chiamate a distanza  (NEAR  e  FAR CALL).  L'interprete di Tanenbaum implementa solo le  NEAR  CALL. La destinazione può  essere un etichetta oppure un indirizzo. I parametri della subroutine devono  essere inseriti nello Stack in ordine inverso. Dopo il  PUSH  degli argomenti  si   esegue   l'istruzione  CALL,   che   comincia   con   l'inserire   sullo   Stack   il  valore corrente del registro PC, per salvare l'indirizzo di ritorno (in modo  da riprendere l'esecuzione del programma al termine della subroutine). L'istruzione RET recupera il vecchio valore del registro PC dallo Stack e lo  scrive   all'interno   del   PC,   così   l'esecuzione   riprende   dall'istruzione  successiva alla CALL.  Gli argomenti devono essere accessibili alla subroutine per cui, in genere,  l'inizio di una nuova subroutine inizia con il PUSH del BP (Base Pointer), e  successivamente   si  copia  il   valore   corrente   di   SP   in   BP.   Così   facendo  l'indirizzo base punta al suo valore precedente, e l'indirizzo di ritorno si  trova   alla   posizione   BP+2,   mentre   il   1°   e   2°   argomento   si   trovano  rispettivamente agli indirizzi BP+4 e BP+6.  Al   ritorno   da   una   subroutine   è   necessario   copiare   il   puntatore   base   nel  puntatore   allo   Stack,   effettuare   la   POP   del   vecchio   BP   ed   infine   eseguire  l'istruzione RET.  4.5 – Chiamate di sistema e subroutine di sistema Queste routine possono essere chiamate con la sequenza di chiamata standard:  ­ PUSH degli argomenti sullo Stack in ordine inverso  ­ PUSH del numero di chiamata che identifica la subroutine  ­ si esegue l'istruzione SYS senza parametri I risultati sono inseriti all'interno del registro  AX, oppure nel registro  DX:AX nel caso il risultato sia un long. Suggerimento:   per   comodità   è   possibile   definire   all'inizio   del   programma  alcune   costanti   per   i   nomi   delle   chiamate   di   sistema,   in   modo   da   poterle  chiamare per nome invece che per numero. OPEN *fname, 0/1/2 Questo comando serve per l'apertura di un file. Prende come primo argomento  l'indirizzo della stringa contenente il nome del file. Il secondo argomento  stabilisce   la   modalità   di   accesso   al   file:   (0=lettura,   1=scrittura,  2=lettura/scrittura).   Se   il   file   è   aperto   in   scrittura   ma   non   esiste,   la  funzione provvede a crearlo. CREAT *fname, 0/1/2 La chiamata  CREAT  crea un file vuoto con i permessi specificati dal secondo  argomento. La   funzione  OPEN  e  CREAT  restituiscono   entrambe   un   intero   (come   risultato  dell'operazione)   all'interno   del   registro  AX;   questo   numero   viene   detto  descrittore di file  e può essere usato per la lettura, scrittura o chiusura  del file. In caso il descrittore di file è negativo, indica un errore nella 

Pagina 10

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

chiamata di routine. READ  fd, buffer, nbytes WRITE fd, buffer, nbytes Le chiamate READ e WRITE hanno 3 argomenti: il descrittore di file, un buffer  per   i   dati  e   il  numero   di   byte   da   trasferire.   Siccome   lo   Stack   usa   la  politica LIFO allora è necessario inserire gli argomenti in ordine inverso: ­ PUSH del numero di byte da leggere/scrivere ­ PUSH dell'indirizzo del buffer ­ PUSH del descrittore di file ­ PUSH del numero di chiamata che identifica la routine (READ o WRITE) CLOSE fd Questa   chiamata   serve   a   chiudere   un   file.   Richiede   solo   il   descrittore   di  file, e restituisce il valore  0  all'intero del registro  AX  per indicare che  l'operazione ha avuto successo. EXIT status Inserisce sullo Stack lo stato di uscita del processo e non restituisce alcun  valore. LSEEK fd, offset, 0/1/2 Modifica il puntatore di lettura/scrittura di un file già aperto. Il primo  argomento   è   il   descrittore   di   file,   il   secondo   argomento   è   un   dato   long  perciò va scomposto nelle due parole che lo costituiscono e va inserito nello  Stack   per   prima  la   metà  più  significativa   (anche   nel   caso   in   cui   l'offset  fosse   contenuto   tutto   nella   metà   meno   significativa).   Il   terzo   argomento  indica se il puntatore di lettura/scrittura va calcolato: (0=all'inizio del  file, 1=alla posizione corrente, 2=alla fine del file). Il valore restituito  è la nuova posizione del puntatore relativa all'inizio del file, memorizzato  come long nei registri DX:AX. GETCHAR La funzione legge un carattere dall'input standard, lo salva nel registro AL  e azzera AH. In caso di fallimento il registro AX vale ­1. PUTCHAR char Scrive un byte sullo standard output e, in caso di successo restituisce lo  stesso byte scritto, mentre restituisce ­1 in caso di fallimento. PRINTF *format, arg Stampa   informazioni   formattate.   Il   primo   argomento   è   l'indirizzo   della  stringa. Per formattare l'output sono presenti dei caratteri speciali:   ­ “%d” → notazione decimale   ­ “%x” → notazione esadecimale    ­ “%o” → notazione ottale   ­ “%s” → notazione di stringa Ad   esempio   la   stringa   “%d”   indica   che   l'argomento   successivo   è   un   intero,  presente   sullo   Stack,   e   che   dovrà   essere   convertito   in   notazione   decimale  prima della stampa. SPRINTF buffer, *format, arg In questo caso la funzione riceve in input un buffer, che contiene la stringa  che dovrà stampata sullo standard output. Gli altri argomenti sono gli stessi 

Pagina 11

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

della PRINTF. SSCANF buffer, *format, arg Questa   funzione   è   il   contrario   della   funzione  PRINTF.   Legge   gli   argomenti  contenuti   nel  buffer  seguendo   il   formato   specificato   all'indirizzo  format  secondo gli argomenti arg.

PARTE 5, ASSEMBLATORE as88 5.1 – Introduzione all'assemblatore Per   la   memorizzazione   del   codice   e   dei   dati   all'interno   di   un   file   .s,  l'assemblatore usa 3 sezioni diverse: ­ .TEXT (per le istruzioni del processore) ­ .DATA (per la dichiarazione di variabili inizializzate) ­ .BSS  (per la dichiarazione di variabili non inizializzate) Ci   sono   due   tipi   di   etichette,   quelle  globali  (costituite   da   caratteri  alfanumerici seguite dal simbolo “:”) e quelle locali (costituite da un unico   carattere seguito dal simbolo “:”). Tutti   gli   identificatori   del   linguaggio   sono   definiti   dai   loro   primi   8  caratteri: ad esempio BLOCKSIZE e BLOCKSIZZ sono lo stesso simbolo BLOCKSIZ. I   valore   numerici   possono   essere  ottali,  decimali  (iniziano   con   0),  esadecimali  (iniziano   con   0x).   Gli   operatori   logici   sono  &  ^  ~  per   le  operazioni   bit­a­bit   di   AND,   OR,   NOT.   Le   espressioni   possono   contenere  parentesi   quadre,   ma   non   tonde,   per   evitare   di   fare   confusione   con   le  modalità di indirizzamento. Vediamo alcune delle parole chiavi messe a disposizione dal assemblatore:  ­ .SECT .TEXT → rappresenta la sezione delle istruzioni  ­ .SECT .DATA → indica la parte dei dati inizializzati  ­ .SECT .BSS → parte dei dati non inizializzati  ­ .BYTE → definisce una sequenza di byte  ­ .WORD → definisce una sequenza di word  ­ .LONG → definisce una sequenza di long  ­ .ASCII  “str”→ salva “str” senza inserire il carattere di terminazione  ­ .ASCIIZ “str”→ salva “str” inserendo il carattere di terminazione  ­ .SPACE n → incremento il contatore di locazioni di n posizioni  ­ .ALIGN n → allinea il contatore di loc. alla loc. che dista n byte  ­ .EXTERN → identifica un nome esterno La definizione di stringhe consente l'uso di caratteri di escape, tra cui:  ­ “\n” → Invio a capo  ­ “\t” → Tabulazione  ­ “\\” → Backslash  ­ “\b” → Backspace  ­ “\f” → Avanzamento modulo  ­ “\r” → Ritorno a capo  ­ “\”” → Virgolette

Pagina 12

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

L'assemblatore  as88  è modellato sugli assemblatori standard di UNIX, e come  tale differenzia dagli altri assemblatori per l'8088:  MASM  (di Microsoft) e  TASM  (di   Borland),   entrambi   progettati   per   sistemi   DOS.   MASM   e   TASM  supportano entrambi i 3 modelli di memoria resi disponibili dall'architettura  8088:  ­ tiny , tutto il codice e i dati devono essere contenuti in 64Kb.  ­ small, il segmento di codice e dati possono raggiungere ciascuno i 64Kb.  ­ large, ammette più segmenti per il codice e per i dati. L'assemblatore as88 usa un modello di memoria che somiglia al modello small,  anche se quando è usato senza il tracer, può gestire i registri di segmento  senza restrizioni.

PARTE 6, ESEMPIO DI PROGRAMMAZIONE ASSEMBLY 6.1 – Esempio di “Hello, World!” Analizziamo   un   semplice   programma   in   linguaggio   Assembly,   in   grado   di  stampare sull'output video il testo “Hello World”

_EXIT = 1 _WRITE = 4 _STDOUT = 1 .SECT .TEXT start: MOV  CX, de­hw PUSH  CX PUSH  hw PUSH  _STDOUT PUSH  _WRITE SYS ADD  SP, 8 SUB  CX, AX PUSH  CX PUSH  _EXIT  SYS .SECT .DATA hw: .ASCII   "Hello World\n" de: .BYTE  0 .SECT .BSS

Pagina 13

!  1  !  2  !  3  !  4  !  5  !  6  !  7  !  8  !  9  ! 10  ! 11  ! 12  ! 13  ! 14  ! 15  ! 16  ! 17  ! 18  ! 19  ! 20  ! 21

© Copyleft 2010 Fabian Priftaj

Appunti di programmazione in assembly 8088

DETTAGLI L'istruzione .SECT .TEXT nella riga 4 specifica che le righe successive sono  delle istruzioni al processore. Analogamente la  riga 17  indica che le righe  sottostanti fanno parte della sezione delle variabili inizializzate.  La riga 19 inizializza una stringa di dati da 12byte, compresi lo spazio e il  carattere di fine riga (\n).  Le righe 5, 18 e 20 contengono delle etichette, riconoscibili dalla presenza  del simbolo “:”. Queste etichette rappresentano valori numerici, per cui sono  simili a delle costanti; tuttavia il valore numerico non può essere stabilito  a priori, ed è quindi compito dell'assemblatore calcolarlo.  L'etichetta start vale 0, ma il valore di un eventuale etichetta all'interno  della sezione .TEXT dipenderebbe dal numero di byte che la precedono.  Nella riga 6 si ha la differenza di due etichette. Questa riga è equivalente  all'istruzione “MOV CX, 12”, con la differenza che in questo caso il calcolo  della   lunghezza   della   stringa   è   lasciato   al   programmatore,   mentre  nell'esempio è compito dell'assemblatore farlo.  Le  righe   da   7   a   11  mostrano   l'istruzione   equivalente   alla   funzione   in   C:  write(1,   hw,   12),   dove   in   questo   caso   il   primo   parametro   indica   il  descrittore   di   file   per   lo   standard   output   (1),   il   secondo   parametro   è  l'indirizzo   della   stringa   (hw),   ed   il   terzo   parametro   rappresenta   la  lunghezza della stringa (12).  La  riga 10  impila sullo Stack il numero della chiamata  write  e la  riga 11  effettua la chiamata.  La riga 12 ripristina il puntatore allo Stack (SP) alla posizione precedente  alla chiamata. Se l'operazione di scrittura ha avuto successo il numero di  byte scritti è contenuto nel registro AX.  La  riga 13  sottrae il risultato della chiamata della  riga 11  al valore del  registro CX per stabilire se la chiamata ha avuto successo. Di conseguenza,  lo stato di uscita del programma sarà 0 solo in caso di successo. Le righe 14  e 15 preparano l'esecuzione per la chiamata di sistema exit, impilando sullo  Stack lo stato d'uscita e il codice della funzione EXIT.

Pagina 14

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF