Copy Link
Add to Bookmark
Report

ASSEMBLER (parte I)

eZine's profile picture
Published in 
bakunin
 · 14 Aug 2022

BAKUNIN

bakunin@meganmail.com


Guardate un po' chi si rivede!
Nuovamente il vostro amico Bakunin con una nuova ed avvincente guida.

Ho scelto anche l'assembler per le potenzialità effettive che ha. Con l'assembler potere creare da Word ad Excel, da Windows a Linux, da un Virus a quello che volte voi.

Le potenzialità dell'assembler sono praticamente assolute. Questo perché? perché lavora direttamente sul linguaggio macchina. Crea programmi proprio dalla radice del vostro computer. Crea ciò che è al di sotto di tutto il resto.

Ovviamente questo non con incredibile facilità. Questo è il linguaggio più complicato che possa esistere (almeno per quanto io sappia...).

INDICE

  1. CPU e simili
  2. Decimario, binario, esadecimale
  3. Registri
  4. Uso dei 8 e dei 16 registri (MOV)
  5. Caratteristiche dei registri
  6. Pausetta...
  7. Programmi necessari
  8. Memoria RAM e ROM
  9. Lo Stack
  10. Si programma! PROVA.COM
  11. PROVA2.COM complichiamo il tutto!

Ma ora un po' di teoria! Lo so che è pizzosa ma, senza questa lunga pappardella, non si fa gran che...

1. CPU e simili

Il vostro computer ha un centro ove vengono svolte tutte le operazioni (detto molto male e in breve!). Questo è la CPU (cioè l'unità centrale del processo).

Ci sono molti tipi di processori. In particolare noi ci occuperemo di quelli Intel.
Questa fabbricuccia di processori divenne incredibilmente famosa negli anni 70 quando incominciò a produrre microprocessori sempre più veloci e sempre compatibili. Cosa vuol dire compatibili? Vuol dire che un programma che girava sul vecchissimo 8086 gira pure su un pentium.

Questo perché l'interprete dei comandi binari è rimasto lo stesso. Il linguaggio con cui si programmava su quelle macchine (e sto parlando del linguaggio alto cioè il linguaggio base su cui tutto poggia) è rimasto costante. Al contrario varie altre case produttive creavano processori ma che non comunicavano l'uno con l'altro.

L'Intel è ancora oggi il padrone del monopolio dei microprocessori, ma come già ma microsoft, tira tu che tiro io, con l'aumentare dei prezzi (MA COME FA A COSTARE PIU DI UN MILIONE UN PROCESSORE!!!), nuove case produttive sono nate.
A poco a poco sta per morire il monopolio INTEL.

I vari microprocessori sono:

8089 
8186
8286
8386
8486
pentium
...

A ricordare tutti questi processori mi sento commosso...

Quando vi dicono che Windozz o Linux o altri sistemi fanno eseguire ad un computer più operazioni, ebbene vi dico, è una palla! Infatti è la CPU che fa eseguire le operazioni e lei poveretta ne può eseguire solo una alla volta.

Però c'è da dire 2 cose:

  1. La velocità di funzionamento sembra che le faccia partire in contemporanea
  2. La CPU ha la possibilità di mettere in Standby un'azione

Questo crea l'effetto di simultaneità.

La reale simultaneità c'è quando si usano delle periferiche esterne. Qui il discorso è diverso perché la CPU di occupa soltanto di impostare la lettura della periferica in questione. Poi tutta la prelevazione di informazioni è fatta dalla DMA. Qui c'è la simultaneità reale perché una volta impostata la lettura della periferica, la CPU si preoccupa di altro. Le periferiche sono: il lettore CD, la scheda audio, e chi più ne ha, più ne metta.

2. Decimario, binario, esadecimale

Io parto dal presupposto che voi sappiate già contare in base 10!
Cosa vuol dire contare in base 10? Vuol dire quello che avete sempre fatto ogni volta che contavate. 1+1=2 60-40=20 4+4=8... Insomma contare!

Quello che voglio dirvi oggi, e chi ha un minino di conoscenza matematica già sa, è che non esiste soltanto la base 10 ma anche la 2, la 3, la 4, tutte quelle che volete.

Quelle che vi spiegherò oggi sono la 2 e la 16. Queste vengono rispettivamente dette binaria, esadecimale. Non sono difficili, basta concentrarsi!

Non sappiamo che i numeri nella base binaria vanno dallo 0 al 9 per poi ripetersi con un 1 davanti che poi diventa un 2 e così via.

Cioè 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14.... Si va dallo zero al nove per poi ripersi nel 10 fino al 19. Poi il 20 fino al 29, ecc.

Supponiamo che ora io fermi i numeri non al 9 ma all'8.

Si avrebbe una condizione del genere:

0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,20,...

Se quindi ora devo sommare 2+2 ottengo sempre 4. Ma se sommassi 4+5, ottengo non 9 (perché non c'è!) ma ottengo 10. perché se sommo 4+5 devo ottenere il numero dopo l'otto che nel nostro caso è il 10.

Se poi il al posto di levare solo il nove e limino anche altri numeri potrei arrivare ad averne solo più 2: lo 0 e l'1. Se infatti tolgo 9,8,7,6,5,4,3,2 mi rimangono solo i primi 2.

Cosa vuol dire questo? Che contando non posso superare l'1. Se faccio quindi 1+1, non ottengo 2 ma ottengo 10. perché? perché la sequenza sarebbe:

0,1,10,11,100,101,110,111,1000,1001,1010,1011,1100,110l,1110,1111,...

Che in decimale vuol dire:

0,1,2  ,3 ,4  ,5  ,6  ,7  ,8   ,9    ,10, 11, 12,    13,  14, 15,...

EX CLARO?


Quindi ora sappiamo che 1+1 è uguale a 10. Bel passo in avanti!

Questo metodo di numerazione è molto utile, se non essenziale per l'assembler. Ma un altro metodo forse ancora più essenziale è la base esadecimale. Questa base è come dice la parola stessa, in 16. Quindi 1+1=2, 4+5=9 ma 12+4=22!

Come funziona? Semplice, al posto che togliere numeri (come per le basi precedenti) qui si inseriscono nuovi numeri. Questi sono:

1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20...

Semplice come bere un bicchiere d'acqua!

Quindi ora, a conclusione di questa parte, una bella tabellina!

----------------------------------- 
| Decimali | Binari | Esadecimali |
-----------------------------------
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 10 | 2 |
| 3 | 11 | 3 |
| 4 | 100 | 4 |
| 5 | 101 | 5 |
| 6 | 110 | 6 |
| 7 | 111 | 7 |
| 8 | 1000 | 8 |
| 9 | 1001 | 9 |
| 10 | 1010 | A |
| 11 | 1011 | B |
| 12 | 1100 | C |
| 13 | 1101 | D |
| 14 | 1110 | E |
| 15 | 1111 | F |
| 16 | 10000 | 10 |
| 17 | 10001 | 11 |
| 18 | 10010 | 12 |
| 19 | 10011 | 13 |
| 20 | 10100 | 14 |
| 21 | 10101 | 15 |
| 22 | 10110 | 16 |
| 23 | 10111 | 17 |
| 24 | 11000 | 18 |
| 25 | 11001 | 19 |
| 26 | 11010 | 1A |
| 27 | 11011 | 1B |
| 28 | 10100 | 1C |
| 29 | 10101 | 1D |
| 30 | 10110 | 1E |
| 31 | 10111 | 1F |
| 32 | 11000 | 20 |
| 33 | 11001 | 21 |
| 34 | 11010 | 22 |
| 35 | 11011 | 23 |
| 36 | 11100 | 24 |
| 37 | 11101 | 25 |
| 38 | 11110 | 26 |
| 39 | 11111 | 27 |
| 40 | 100000 | 28 |
-----------------------------------

Non dovrei aver fatto errori!

Questa piccola tabellina vi mostra i valori numeri nelle tre basi che a noi interessano. Poi ci sono infinite altre basi che però non sono utilizzate dal computer.

Ma perché il computer usa le basi 2 e 16?

Il computer come voi sapete, lavora il linguaggio macchina. Ciò vuol dire che tutto ciò che voi vedete in verità non è così com'è, ma è un'insieme di 1 e di 0.
Per questa ragione, quando si vuole comunicare col computer in modo "terra terra" bisogna livellarci al suo linguaggio.

Per quanto riguarda la base 16, questa è perché all'interno del computer, i file funzionano e vengono trasmessi a piccoli pacchetti di 8 unità ciascuno (8 bit) Se noi moltiplichiamo 8 per 2 otteniamo 16. perché non usiamo 8 lo capiremo più avanti.

Ora un po' di calcoli. Come si fa a calcolare? Ci sono vari metodi. Io uso questo:
trasformarsi ogni singolo fattore in decimale, fare l'operazione e poi riconvertire nuovamente il risultato.

Poi con l'abitudine si imparano vari valori a memoria, e quindi tutto diventa più semplice.

Esempio:

110 + 1000 = ?   1110 
| | |
6 + 8 = 14 ---

Per finire vi dico che:

  • h significa un numero esadecimale 16h
  • b significa un numero binario 101b
  • per i numeri decimali non si usa niente (prevalentemente perché non li si usa ;-)

Un trucchettino molto utile per trasformare un numero binario in esadecimale è dividerlo ogni quattro cifre e poi cambiarle così divise.


ESEMPIO:

1000	1010	1101	0010 
8 A D 2

Quindi il numero 1000101011010010b è 8AD2h.

Chiaro?

3. REGISTRI

Vi ho parlato dei bit come quei pacchetti con cui si traferiscono informazioni.
Questi bit vanno in base 8 e sono quelle cose che contengono concretamente tutto ciò ch il computer ha e visualizza.

Questi bit funzionano grazie a dei registri che sono i contenitori dei numeri binari. Questi sono tanti e sono continuamente modificati dall'INTEL, ma sta di fatto che sono compatibili l'un l'altro tra i vari computer.

I registri possono contenere un numero binario che va da 00000000 a 11111111 che, in parole povere vuol dire da 0 a 255.

Abbiamo quindi detto: i bit trasportano le informazioni. Le informazioni sono in binario e questi numeri sono contenuti nei registri dei vari bit. Non dovete però pensare che in ogni bit ci siano dei registri! E' il contrario! In ogni registro ci sono 8 bit. Ma dato che non possiamo slegarli l'un l'altro, non preoccupativi di sapere esattamente la struttura architettonica di un microprocessore.

Questo schema forse è più chiarificatore:

REGISTRO 
|
8 bit

Le informazioni del registro sono in base 2!

Ogni registro ha una struttura simile. Ora beccatevi questo schema:

AH AL - BH BL - CH CL - DH DL  (registri a 8 bit) 
AX BX CX DX (registri a 16 bit)

Dovete, all'incirca impararvelo a memoria!

Cosa sono i registri a 16 bit? Nulla sono due registri presi assieme. Dato che si usa spesso e volentieri prenderli a coppie e considerarli uno solo, si conta anche a 16.

Ecco perché base 16!

Questi comunque sono i registri base. Poi esistono altri tipi di registri nati successivamente allo 8088 e per questa ragione sono detti speciali:


Registri speciali a 16 bit:

IP    (Istruction Pointer) ovvero puntatore alle istruzioni. 
BP (Base Pointer ) ovvero puntatore alla base di dati.
SI (Source Index ) ovvero Indice Sorgente di dati.
DI (Destination Index ) ovvero Indice Destinazione di dati.
SP (Stack Pointer ) ovvero puntatore allo stack.

Registri di segmento a 16 bit:

CS    (Code Segment      ) Punta al segmento dove si trovano le istruzioni 
DS (Data Segment ) Punta al segmento dei dati
ES (Extra Segment ) Punta al segmento dati (extra)
SS (Stack Segment ) Punta al segmento di stack

Io ve li butto li, poi ve li spiegherò.

4. Uso degli 8 e dei 16 registri.

Se io scrivo:

MOV AL,65h 
MOV AH,28h

oppure:

MOV AX,2865h

Il risultato è identico.

perché? perché il comando MOV serve per invertire un valore all'interno di un registro (come se fossero delle variabili). Io quindi dico al computer: inserisci nel registro AL il valore 65h in base 16 e nel registro AH il numero 28h.

Oppure posso dire inserisci in AX il valore di 28 e 65. Il risultato non cambia.

Questo perché AX è il registro a 16 bit che contiene i registri a 8 AL e AH.

Spiegazione accurata di MOV:
il comando MOV serve per copiare un valore in un registro. Questo comporta solo la copiatura e non lo spostamento!

Se io quindi scrivo:

MOV AL,45h 
MOV AH,AL

Che vuol dire attribuisco al registro AL il valore di 45h (h vuol dire esadecimale) e poi attribuisco a AH il valore di AL. Sta di fatto che sia AL e sia AH contengono il valore di 45h.

E' fortemente errore scrivere così:

MOV AL, 45h 
MOV BX, AL

Questo è un grosso errore perché non posso mescolare registri a 16 e a 8 bit.
Questo è un errore grave!

Sottolineo l'importanza di errore perché se si fa un errore in assembler, la macchina si pianta. Bisogna riavviarla e si rischia anche di provocare danni al computer. Ovviamente non sto parlando ancora delle cazzatine che facciamo noi, ma ravviare il computer ogni volta, è una bella rottura!

Si possono utilizzare pure i registri speciali. A condizione però che non si meschiloni 16 con 8.

MOV BP,BX 
ma anche:
MOV BP,1

in quanto in quest'ultimo caso l'Assembler traduce MOV BP,1 in:

MOV BP,00000000000000001b

Non sono invece consentite istruzioni tipo MOV BP,BH o MOV SI,BL in quanto BP e SI sono a 16 bit, mentre BL e BH sono a 8 bit. Solita regola.

5. Caratteristiche dei registri

Ogni registro ha una funzione particolare. Qui cerco di spiegarvela, userò il passato prossimo perché ora come ora non esistono quasi più queste differenze. Ora sto parlando dei processori 8088, i quali le avevano:

  • Il registro AX=(AH+AL) veniva anche definito il Work register o registro di lavoro, e solo ad AX sono consentite alcune operazioni aritmetiche o di manipolazione dei bit (che poi vedremo).
  • Il registro BX=(BH+BL) può essere usato come (e solo lui fra i registri di uso generale) come indice in una operazione di trasferimento di un dato.
  • Il registro CX=(CH+CL) può essere usato (e solo lui) come counter in istruzioni di trasferimenti multipli e in LOOPS.
  • Il registro DX=(DH+DL) non ha invece usi specifici (tranne che in alcuni Interrupt che comunque sono definiti dal BIOS o dal Sistema Operativo e come indice nelle istruzioni IN e OUT.)
  • Il registro IP è il puntatore all'istruzione corrente e la macchina lo utilizza in accoppiata a CS (Code Segment) per individuare ed eseguire l'istruzione corrente. La coppia CS:IP punta dunque all'istruzione corrente e a ogni ciclo IP viene incrementato automaticamente in modo tale che, dopo aver eseguito l'istruzione corrente, la coppia CS:IP punti alla istruzione successiva. IP viene modificato di fatto da istruzioni di salto JMP, da chiamate a subroutine (CALL) o da istruzioni tipo RET (RETurn from subroutine). Anche se è possibile (vedremo in seguito come) manipolare CS e IP, ricordarsi che è sempre preferibile lasciare alla macchina tale compito se non si vuole rischiare di bloccare l'intero sistema. ;-)
  • Lo stesso discorso vale anche per la coppia di registri SS:SP (Stack Segment:Stack Pointer) che pur essendo interessati da istruzioni tipo PUSH e POP (salvataggio temporaneo e ripresa di registri) è preferibile lasciare alla macchina la loro manipolazione, in quanto è proprio nell'area puntata da SS:SP che questa salva gli indirizzi di ritorno quando esegue una CALL.
  • I registri SI e DI (Source Index e Destination Index) hanno invece un utilizzo specifico nel trasferimento in blocco di aree di memoria con istruzioni tipo MOVSB.
  • Il registro BP (Base Pointer) ha anche un uso specifico come puntatore a base dati complesse, che in istruzioni tipiche che usano anche BX,SI e DI consentono ad una singola istruzione di eseguire trasferimenti e/o copiature multiple di dati complessi.
  • I registri di Segmento DS e ES servono a puntare ai segmenti dati e sono sempre usati in coppia con i registri BX, BP, SI, DI che consentono di puntare ai dati in memoria, in qualunque segmento questi si trovino.

Le spiegazioni approfondite su cos'è un interrupt o i trasferimenti multipli e in LOOPS, e di tutte quelle paroline brutte e cattive che vi ho detto sopra, le avrete più avanti.
Quindi non vi preoccupate.

----------------------FINE PRIMA PARTE----------------------

6. Pausa dello scrittore ...

------------------------INIZIO SECONDA PARTE-----------------

7. Programmi necessari

Quando parlo di programmazione intendo 2 parti ben precise, il codice sorgente e il compilatore. Il codice sorgente non è altro che un file di testo con una serie di codici scritti. Il compilatore è un programma che tramuta il codice sorgente in un eseguibile, cioè in una programma.

Per quanto riguarda l'Assembler non esistono veri e propri programmi:

  1. DEBUG.EXE (della Microsoft (che potete trovare in DOS - WINDOWS95 - ecc.))
  2. MASM o TASM (Microsoft o Borland)
  3. Turbo C++
  4. Turbo Pascal (dalla versione 7.00 in poi) [che potete trovare nella sezione Programmi del Tank Commandos Web Site http://go.to/tankcommandos]
  5. Qualunque altro linguaggio che consenta di scrivere routines in Assembler o Linguaggio Macchina.

Tranne che per DEBUG.EXE (che però è molto limitato) per tutte le altre piattaforme è necessario compilare i programmi prima di farli girare.

Per quanto riguarda il C++ i programmi Assembler possono essere scritti all'interno di blocchi del tipo:

main() 
{ /* dichiarazioni e istruzioni C++ varie */
...
...
asm { ; qui possono essere scritte
; tutte le istruzioni Assembler
}
}

Consiste semplicemente di usare il comando asm e poi inserire, come se fosse una procedura, tutto ciò che vi passa per la testa.


MESSAGGIO PROPAGANDISTICO: Scaricate la fantastica guida per il C. Scritta con fare semplice ed elegante, è possibile prelevarla nel fantastico sito del Tank Commandos (http://go.to/tankcommandos) sotto la voce programmazione. E' scritta da quell'esemplare di uomo detto BAKUNIN (che sarei io!). Andate e fate!


Mentre per il TurboPascal si può realizzare un blocco del tipo:

begin 

asm{

}

end.


8. Memoria RAM e ROM

Ogni singolo file, ogni singola esecuzione di programma, tutto quanto richiede memoria nel vostro computer. Tutto è memoria. Noi sappiamo per adesso che la memoria è formata da bit. Se noi raccogliamo a gruppi di 8 i bit otteniamo un byte. I byte sono dei pacchetti di memoria che non contengono altro che 0 e 1 in sequenza. Anche i byte ovviamente sono dei registri. Questi in particolare sono:

  • CS:IP -- Indirizza la Memoria dove risiedono i programmi
  • SS:SP -- Indirizza la Memoria dove risiede lo stack
  • DS:[BP BX SI DI] -- Indirizza la Memoria dove risiedono i dati
  • ES:[BP BX SI DI] -- Indirizza la Memoria dove risiedono i dati

A partire dal 386 esistono altri due registri di segmento FS e GS che possono essere utilizzati per indirizzare la memoria Dati. In ultima analisi quindi, sia un programma che qualunque tipo di dato è memorizzato sotto forma di singoli bit (stato 0 o 1) e indirizzabili a gruppi di 8 (un Byte per volta) o a gruppi di più Bytes secondo il seguente standard:

  • 1 Byte
  • 1 Word (pari a 2 Bytes)
  • 1 DoubleWord (pari a 4 Bytes)
  • 1 QuadWord (pari a 8 Bytes)
  • 1 TenByte (pari a 10 Bytes)

Queste sono ragguppazioni standard.


RAM e ROM?

Il vostro computer ha della memoria. Questa è suddivisa in 2 parti per distinte, la RAM e la ROM. La RAM (Random Access Memory o meglio memoria di accesso casuale) è quella memoria che viene utilizzata dal computer per ricordare file momentanei o per far partire dei programmi. Questa memoria è temporanea nel senso che se spegnete il computer quella memoria si cancella automaticamente e perdete tutto. La classica rottura di cog_ioni di quanto salta la corrente... e dovete riscrivere tutto se non quasi.

Poi c'è la memoria ROM (Read Only Memory o Memoria di sola lettura). Questa è quella permanente. Quella ove stanno i vostri file salvati, come i vostri programmi, come tutto quello che avete ogni volta che ravviate il computer.

Nella ROM c'è il BIOS. Il BIOS (Basic Input-Output System) è un programma (non è proprio così, ma ve la faccio semplice...). che ha il compito dell'accensione del computer. Verifica i dispositivi presenti, esegui il POST (Power on Self Test) e infine carica il sistema operativo, dando libero sfonfo alla MRB. La MRB è una parte della memoria ove sono inseriti i file (quasi inaccessibile) che permettono l'accensione del vostro sistema operativo.

Dopo aver tatto tutto ciò il BIOS lascia tutto il controllo della macchina al vostro sistema operativo. Ma il BIOS continuerà sempre ad essere presente e offre una serie di servizi a cui fanno spesso riferimento gli stessi sistemi operativi. Tra i tanti quelli che ci interessano a noi ci sono gli Interrupt, ma li vedremo.

Come tutta la memoria, la RAM e la ROM sono nient'altro che insieme di bytes. Questi saranno formati come tutti da 0 e da 1. Qui c'è tutto il funzionamento della macchina.

Ogni singolo Byte della RAM (e della ROM) ha un INDIRIZZO UNICO, e a questo indirizzo si fa riferimento per prelevare o memorizzare i dati o i programmi. Gli indirizzi della RAM (e della ROM) sono puntati da registri specifici del Microprocessore.

Per quanto riguarda gli indirizzi dei programmi, si usa normalmente la coppia di registri CS:IP. Per quanto riguarda l'uso dei file di dati, si usano i registri di segmento (DS - ES - FS e GS) in accoppiata con i registri(BX - BP - SI - DI) in diverse combinazioni.

Quindi per l'esecuzione o l'apertura di un programma ci sono i CS:IP mentre per gli altri, cioè i file che contengono dati (cioè tutti quelli che non sono programmi), ci sono i registri di segmento (DS - ES - FS e GS) da utilizzarsi con i registri (BX - BP - SI - DI) in diverse combinazioni.

Poi ci sono gli STACK che vi spiego dopo. Per ora sappiate che l'area della RAM riservata invece agli Stack Pointers viene puntata in genere dalla coppia dei registri SS:SP.

Con gli ultimi microprocessori si è cercato di separare e di proteggere accuratamente l'area destinata ai dati e quella destinata ai programmi. E' però possibile modificare attraverso un programma in assembler la destinazione fisica del programma. Questo è ciò che realmente fanno molti virus creati in assembler. Si collocano nella zona dati rendendosi quasi del tutto invisibili agli antivirus. Chiaro no?

Per quanto concerne la memoria RAM, questa è gestita dal sistema operativo che la cede ad un programma piuttosto che ad un altro. Ma ciò per ora non ci interessa.

Nei nostri programmi, quindi, salvo che per casi particolari, per allocare memoria utilizzeremo le funzioni DOS (che sono identiche a WINDOZZ).

9. Lo Stack

Lo stack e' un'area di memoria dove possono essere salvati "momentaneamente" (perché memoria temporanea!) i registri della CPU. Questa operazione di salvataggio può essere fatta o attraverso l'istruzione PUSH o attraverso l'istruzione CALL. Il primo metodo è voluto direttamente, l'altro è indirettamente.

Gli stessi dati vengono ripresi sia direttamente attraverso istruzioni POP che indirettamente (relativamente al registro IP o alla coppia CS:IP) attraverso istruzioni RETN o RETF (RETurn Near o RETurn Far).

Lo Stack puo' trovarsi sia nello stesso segmento del nostro programma (come nel caso dei files .COM) che in un altro segmento (files .EXE); in ogni caso i dati nello stack vengono puntati dalla coppia di registri SS:SP e i dati vengono inseriti dall'ALTO verso il BASSO, cioe' con valori di SP sempre decrescenti via via che si inseriscono nuovi dati nello stack.

Ma ora vi beccate un esempio per capire meglio le istruzioni PUSH e POP.

MOV AX,0102h 
MOV BX,0304h

Già spiegato quindi vado avanti.

Inizialmente abbiamo

SS:SP = XXXX:0FFFE

che vuol dire

|  ??  | 0FFFE 
| ?? | 0FFFF

Ove SS:SP sono i registri. XXXX vuol dire "non caricati dati" e 0FFFE è il numero dello stack (che come abbiamo detto vanno in ordire decrescente quindi, dal numero FFFF esadecimale a 0000 sempre esadecimale.)

Dopo un'istruzione

PUSH AX

avremo :

SS:SP = XXXX:0FFFC

che vuol dire

|  02  | 0FFFC 
| 01 | 0FFFD
| ?? | 0FFFE
| ?? | 0FFFF

Quindi abbiamo caricato nel (col comando PUSH AX) nello stack i valore che abbiamo prima inserito con MOV. Ricapitolando:

  1. abbiamo inserito i valori attraverso il comando MOV nei registri AX e BX
  2. Abbiamo poi caricato i valori di AX nello stack

Ora abbiamo che in 0FFFD abbiamo il valore di AH e in 0FFFC il valore di AL.

Ve lo ricordate che AX è formato da AH e AL. Se no, andate a rivedere tutto da capo!

Quindi eseguendo ancora

PUSH BX

avremo :

SS:SP = XXXX:0FFFA

Che vuol dire

|  04  | 0FFFA 
| 03 | 0FFFB
| 02 | 0FFFC
| 01 | 0FFFD
| ?? | 0FFFE
| ?? | 0FFFF

Ho messo negli stack successivi i valori di BX che sono per BH 03 e per BL 04. Anche BX come già visto è formato da BH + BL! I valori sono quindi stati inseriti nello stack. Questo li ha immagazzinati automaticamente in ordine decrescente come si vede nella tabellina di sopra.

Eseguendo ora dei POP avremo

POP AX

(ricordate che AX = 0304h)

SS:SP = XXXX:0FFFC

Ottengo che:

|  02  | 0FFFC 
| 01 | 0FFFD
| ?? | 0FFFE
| ?? | 0FFFF

Con il comando POP AX ho richiamato i valori precedentemente inseriti.

Non ha importanza su quale registro si effettua il POP, ne' e necessario rispettare la sequenza originaria; infatti in questo caso noi effettueremo uno scambio tra i registri AX e BX, ed ecco infatti con il prossimo:

POP BX 
SS:SP = XXXX:0FFFE

ritorno a:

|  ??  | 0FFFE 
| ?? | 0FFFF

Abbiamo svuotato lo stack riportandolo al suo valore originario e avremo effettuato uno scambio fra i registri AX e BX.

Incredibile.

Spero di essere stato abbastanza chiaro. Ora comunque lo rispiego:

  1. carico valori in AX e in BX (comando MOV)
  2. Carico i dati di AX all'interno dello stack
  3. Carico i dati di BX all'interno dello stack
  4. Reinserisco i dati in AX (togliendo quelli di BX e mettendoli in AX)
  5. Svuoto poi gli ultimi dati dello stack in BX

E' importante capire COME esattamente si comportano le istruzioni PUSH e POP:

nel caso di istruzioni PUSH lo Stack Pointer (SP) viene PRIMA DECREMENTATO di 2 unita' (quelle che abbiamo voluto noi e che prima erano in AX o in BX)e quindi il dato viene salvato in questo nuovo indirizzo secondo la sequenza HIgh byte - LOw byte.

Nel caso di istruzioni POP il dato viene PRIMA rimosso dallo Stack e soltanto dopo SP viene INCREMENTATO di 2 unita'.

Ricordarsi che i dati nello stack possono essere salvati almeno come WORD e quindi minimo 2 bytes per volta, e ripresi con le stesse regole.

10. Si programma!

Ce ne voluta di introduzione ma finalmente si incomincia!

Incominciamo:

Per ora vi consiglio di utilizzare (se siete sotto DOS e non possedete ne un compilatore per C/C++ e ne uno per PASCAL) il DEBUG.EXE. Se siete sotto LINUX, e se non lo sapete, avete 3 compilatori per C e C++ e 2 compilatori per PASCAL (sto parlando solo di quelli installati normalmente, poi ci sono quelli da professionisti).

Per quanto riguarda DOS:

  1. Se avete PASCAL vedi sopra.
  2. Se avete C/C++ vedi sopra, i compilatori poi cambiano da programma a programma.

Per quanto riguarda LINUX:

  1. Aprite un qualsiasi programma di editatori di testo. Io uso quello avanzato in KDE. Ma non sempre. A volte uso PICO o VI che ritengo i migliori!
  2. Dopo di che scegliete se volete programmare in C o in PASCAL. Io vi consiglio il C.
  3. Create un file come vi ho descritto sopra e con le istruzioni di sotto. Salvatelo come files.c (se per C), altrimenti files.pp o files.pas (se per pascal.)
  4. Compilatelo nei seguenti modi:
    • PER IL C
      • cc file.c
      • oppure
      • c++ file.c
      • Questa è una maniera sulle 3.

    • PER IL PASCAL
      • p2c file.pp
      • e poi
      • cc p2c.c
      • Questa è una maniera tra le 2.

Avrete così ottenuto un file chiamato a.out che possiamo rinominare come vogliamo.
Questo file è un eseguibile.

Ma dato che dubito che tra tutti quelli che leggeranno questo tutorial ci sia 1 che lavori sotto LINUX, parlerò di DEBUG.EXE

Andate nel vostro prompt di DOS e digitate:

DEBUG.EXE

SIMULAZIONE:

c:\windows>

scrivo

cd\ 

c:\>

scrivo

debug.exe 
-

questo è ciò che otteniamo. Ve lo detto che è spartano il linguaggio assembler!

Ora digitate R e premete enter.

-R        (significa Registers)

e vedremo una serie di lettere e numeri più o meno come segue:

AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000  SI=0000  DI=0000 
DS=1C82 ES=1C82 SS=1C82 CS=1C82 IP=0100 NV UP EI PL NZ NA PO NC
1C82:0100 C3 RET
-

Non è importante che sia proprio così. Le differenze posso essere dal sistema operativo che noi usiamo, dallo stato della memoria libera, e da altri innumerevoli motivi.

Da ora in avanti dimenticatevi i numeri decimali! Tutto qui è esadecimale.

Col comando R abbiamo chiesto di visualizzarci lo stato dei vari registri. E in effetti vedete tutti i registri di cui vi ho parlato.

Ora la spiegazione di ogni singola parte:

  • AX=BX=CX=DX=BP=SI=DI=0000 sono i registri di uso generale che DEBUG ci ha dati già inizializati a 0000.
  • SP=FFEE è invece lo Stack Pointer che indirizza la prima area di memoria disponibile per operazioni sullo Stack tipo PUSH e POP. perché FFEE? e non 0000? La risposta sta nel fatto che lo Stack Pointer cresce verso il BASSO cioè verso valori decrescenti (come già detto!).
  • DS=ES=SS=CS=1C82 è un qualunque valore di Segmento a cui puntano i registri di Segmento. Non è importante se il valore è 1C82 o 37A9 o qualunque altro numero, ma è importante il fatto che Tutti i registri di Segmento puntino allo Stesso Segmento. Possiamo infatti, in questo modo, costruire un file .COM , un file cioè che ha un solo segmento per il programma, i dati e lo stack.
  • IP=0100 (Istruction Pointer)=Puntatore alle istruzioni punta alla locazione di memoria 100 (esadecimale). Ciò significa che la prima istruzione del programma che sarà eseguito partirà dalla locazione 100h, e ciò a causa di un retaggio del vecchio sistema operativo CP/M che MS-DOS sostituì fin dalla prima versione. Infatti i primi 256 Bytes (da 0000 a 00FF) erano riservati, e lo rimangono tuttora, al PSP (cioè il Program Segment Prefix o Prefisso del Segmento di Programma) che analizzeremo più avanti. Per ora può bastarci sapere che il Programma DEVE iniziare alla locazione 0100h.
  • NV è lo stato dell'ultima operazione eseguita dal sistema operativo e indica che non si è verificato Overflow (NV = No oVerflow)
  • UP significa che il Direction Flag è settato a UP (in alto) (se dovessimo eseguire trasferimenti multipli, questi avverrebbero dal basso verso l'alto).
  • EI (Enable Interrupt) tutti gli Interrupt sono abilitati.
  • PL (PLus) significa che l'ultima operazione ha dato un risultato positivo.
  • NZ (Not Zero) significa che l'ultima operazione ha dato un risultato diverso da Zero.
  • NA (Not hAlf-carry) significa che l'ultima operazione non ha provocato riporto di bit tra i 4 bit inferiori e i 4 bit superiori.
  • PO (Parity Odd) significa che l'ultima operazione ha dato un risultato con parità dispari. (Il bit 0 era settato a 1)
  • NC (Not Carry) significa che non c'è stato riporto e cioè il risultato dell'ultima operazione non ha superato il limite del Byte.
  • 1C82:0100 C3 ----------- RET ( o qualunque altra cosa vediate) significa che nella locazione 1C82:0100 si trova un valore Casuale (memoria sporca) che DEBUG ci ha allocato così come l'ha trovata. Nel caso C3 (RET) significa che alla locazione 1C82:0100 vi si trova il numero esadecimale C3 che e pari a 1100 0011 binario e che ha il significato Assembler di RET cioè (RETurn from subroutine). Se non vi sentite ancora a vostro agio con i numeri esadecimali, Vi consigliamo di rivedere la parte a loro dedicata.

Se fin qui è tutto chiaro, passo oltre...

Ma rincominciamo col programma e quindi digitiamo:

-F 100 2100 90

e cioè: Fill (riempi) le locazioni di memoria da 100 a 2100 con il valore 90 Il valore 90 vuol dire No Operazioni (NOP). Quindi abbiamo da 100 a 2100 "righe" che per ora sono libere. Non c'è scritto nulla e quindi non compiono operazioni.

Se io ora ridigito R avrò di diverso da prima solo l'ultima riga. Essa è per me:

1C82:0100 90 --------NOP

ciò significa che il nostro comando è stato accettato.

Digitiamo ora:

-N PROVA.COM

Abbiamo semplicemente detto al programma DEBUG di salvare quello che stiamo creando col nome di PROVA.COM

Digitiamo ora:

-R CX

La risposta sarà:

CX 0000 
:

I due punti servono ad indicarci che dobbiamo inserire il nuovo valore di CX. Da 0000 che era noi inseriremo 2000. Digitiamo quindi 2000 e premiamo enter.

Digitando ora:

-R

Vedremo che CX sarà cambiato in 2000.

Digitiamo quindi:

-W

Che significa write. Cioè abbiamo salvato il nostro programma di 2000h byte sul disco.
La directory sarà quella corrente mentre il nome sarà PROVA.COM come abbiamo deciso noi prima.

NON AVVIATE ANCORA IL PROGRAMMA! Se lo fate dovete poi riavviare il computer.
Ora però usciamo da DEBUG con il comando Q.

Ora con un bel DIR vedrete che esiste il programma PROVA.COM . Quello che abbiamo creato noi! La sua ampiezza è di 8.192 byte. Questo perché 2000h è uguale a 8192d.

Ora che sappiamo che esiste la nostra piccola creatura, ritorniamo in DEBUG facendogli fare qualcosa di interessante. Scriviamo quindi:

DEBUG.EXE PROVA.COM

digitiamo quindi:

-R

e accertiamoci che CX=2000 e l'istruzione alla locazione ????:0100 sia 90 ovvero NOP.
Sì è proprio il nostro programma. Avrete dunque capito che DEBUG scrive la dimensione del File nel registro CX, (questo puo' bastare per un file .COM che non supera i 2^16 (-1) = 65535 = FFFF bytes); nel caso di files piu' lunghi in effetti e' la c oppia BX - CX che ne determina la dimensione fino a 2^32 (-1) cioe' FFFFFFFF Bytes; calcolatene a quale valore decimale corrispondono!

Ora inseriamo qualche operazione attraverso il comando A. La locazione però come abbiamo detto un po' sopra deve essere da 100 in avanti per il DOS e quindi scriviamo:

-A 100

Scriviamo quindi ora di fianco ai punti interrogativi e ai numeri che compaiono le nostre operazioni:

????:0100  MOV AH,0F 
????:0102 INT 10

????:0104 MOV BL,39
????:0106 MOV AL,41
????:0108 MOV AH,9
????:010A MOV CX,1
????:010D INT 10

????:010F MOV AH,7
????:0111 INT 21
????:0113 CMP AL,1B
????:0115 JZ 011D
????:0117 INC AL
????:0119 INC BL
????:011B JMP 0108

????:011D MOV AH,4C
????:011F INT 21

Per terminare dove dare una riga a vuoto.

EX CLARO?

Salviamo con W e usciamo con Q.

Ho diviso a gruppi i comandi soltanto per comodità di spiegazione. In verità DEBUG ve li fa tutti uno di seguito all'altro.

Le prime due istruzioni MOV AH,0F e INT 10 servono per regolare la modalità video. Impostiamo semplicemente la grafica. Come potete vedere non mettiamo nulla nel registro BX poiché questo è strettamente legato alla grafica. Quindi non lo usiamo.

INT 10 è un interrupt del BIOS, ovvero un microprogramma che risiede nella ROM e che consente con una semplice istruzione ciò che altrimenti avrebbe richiesto un centinaio di ulteriori istruzioni elementari.

Se poi avrò voglia di fare una seconda lezione sull'assembler vi spiegherò come creare int nostri.
Per ora basti sapere che dopo INT 10 (Funzione 0F in AH) avremo in BH il numero di pagina video che interessa il nostro programma.

Questo ci consente di far girare il nostro programma anche sotto Windozz 95 ovvero in un ambiente Multitasking (che presa per il c_lo...).

Con il secondo gruppo di istruzioni, cioè quelle che vanno da MOV BL,39 fino a INT 10, servono per ripete il contenuto di AL. Sembra complicato ma in effetti (lo è!) basta seguire passaggio per passaggio:

  • MOV BL,39 definisce i colori del carattere nei primi 4 bit (nel nostro caso 3) e dello sfondo nei successivi 4 bit (9 è il nostro caso).
  • MOV AL,41 definisce il carattere da visualizzare (41h = 65 = "A") cioè la lettera A in codice ASCII.(Possiamo mettere qualunque carattere ASCII). Quindi sarà questo per ora il contenuto di AL che il programma dovrà ripetere
  • MOV AH,9 informa il BIOS che si richiede la funzione n. 9 (Visualizza carattere). Detto in modo semplice, dice al BIOS: "fammi vedere il carattere e non il numero". Il BIOS ci farà vedere quindi non 41h cioè 65 ma ci mostrerà la "A".
  • MOV CX,1 informerà il BIOS che il carattere andrà ripetuto 1 sola volta.
  • Ricordandoci che in BH abbiamo GIA' la pagina DOVE visualizzare il carattere, INT 10 esegue la funzione e ci visualizzerà il carattere contenuto in AL. Seguirà il tutto.
  • Il gruppo delle istruzioni successive invece prelevano un carattere dalla tastiera, tramite la funzione 7 dell'interrupt DOS INT 21 (disponibile anche sotto Windozz), e infatti MOV AH,7 - INT 21 attende che venga premuto un tasto dalla tastiera e ritorna in AL il codice ASCII del carattere corrispondente. Quindi il computer aspetta che io schiacci un carattere per poi rinserirlo in AL sotto forma numerica (come prima 41h che era la A).
  • Con l'istruzione CMP AL,1B verifichiamo se il carattere premuto è stato 'Esc'. e in tal caso facciamo terminare il programma. Infatti CMP AL,1B esegue una differenza tra il contenuto di AL e il numero 1B (che è il numero di ESC) scartando il risultato. Se però io premo ESC il programma setta i flag relativi. Di conseguenza se AL contiene 1B, verrà settato il flag Zero e quindi sarà eseguito il salto condizionale a 011D che sarà effettuato dall'istruzione JZ 011D (Jump if Zero to 011D). Quindi il programma inserisce il AL il numero che io voglio. Se questo è uguale a 1B allora, quando più avanti li confronteremo, obbligheremo il programma a saltare (attraverso il comando JZ al punto 011D ove c'è il comando MOV AH,4C). Prima di ripetere il ciclo, e tanto per avere la soddisfazione di 'scherzare' con i caratteri incrementiamo il valore del carattere premuto (tramite INC AL), e cambiamo colore tramite INC BL, quindi eseguiamo un salto incondizionato a 0108 che visualizzerà il carattere contenuto in AL (ma stavolta, quello che abbiamo digitato dalla tastiera, incrementato di 1, e con un colore che via via sarà diverso a causa dell'istruzione INC BL.
  • Se alla fine premiamo il tasto 'Esc', il programma salterà a 011D, dove troviamo le ultime due istruzioni: MOV AH,4C - INT 21 che sono le istruzioni di USCITA dal programma e che sono FORTEMENTE raccomandate dalla Microsoft. Infatti avremmo potuto terminare il nostro programma con una semplice istruzione RET (RETurn) che avrebbe riceduto il controllo al DOS, ma ciò potrebbe creare problemi in futuro e quindi accettiamo i suggerimenti Microsoft e terminiamo i nostri programmi con MOV AH,4C
  • INT 21. Utilizzeremo RET solo nei rientri dalle nostre subroutines, e avendo l'accortezza di usare la RET appropriata: RETN o RETF cioè RET Near o RET Far, a secondo se ci troviamo all'interno o all'esterno di uno stesso segmento

Mi sono spiegato abbastanza?

Ora possiamo tranquillamente uscire ed eseguire il programma da noi creato.
Che ve ne pare?

Pensate quante istruzioni sono state necessarie per queste poche cose. E per fortuna che esistono gli INT altrimenti immaginatevi quante righe (penso oltre il centinaio!) avremmo dovute scrivere.

Il bello è quindi che abbiamo controllato il computer proprio dalla radice del sistema. Lo abbiamo guidato come un cagnolino fino a destinazione!

Riapriamo di nuovo PROVA.COM con

DEBUG PROVA.COM

stavolta digitiamo:

-U            (Unassemble)

Ora che abbiamo disassemblato il nostro programma, possiamo vedere il tabulato in linguaggio macchina. La prima riga ci dice B40F.

Cos'è B40F? E' la traduzione delle prime 2 nostre righe. Cioè

MOV AH   = B4 
,0F = 0F

Questa è la riprova che i nostri codici ASM vengono tradotti con un rapporto 1:1 in linguaggio macchina.

La differenza tra AH, AL e BL è a dir poco irrisoria. Praticamente cambia tutti di solo 3 byte.:

MOV AH    = B4 = 1011 0100 
MOV AL = B0 = 1011 0000
MOV BL = B3 = 1011 0011

Questa è la sequenza complessiva in 8 bit:

Hex       Binary        ASM 

- B0 = 1011 0000 MOV AL,(il valore immediato successivo a MOV AL)
- B1 = 1011 0001 " CL
- B2 = 1011 0010 " DL
- B3 = 1011 0011 " BL
- B4 = 1011 0100 " AH
- B5 = 1011 0101 " CH
- B6 = 1011 0110 " DH
- B7 = 1011 0111 " BH

Come potete vedere la differenza tra AH, AL, CL, DL, ... è praticamente un aumento di 1 del valore dopo B. Non è quindi nemmeno così complicato ricordarseli e immaginare che se io dico questo vuol dire quello:

B407 = MOV AH,07 
B513 = MOV CH,13
B307 = MOV BL,07

Certo! Non è complicato. Però è una pazzia! Infatti voglio vedervi poi a ricordare tutta una tabella ove tra un valore e l'altro cambia solo un 1 in un 2! E' una pazzia. Infatti nessuno si impara il linguaggio macchina. Però molti imparano l'assembler che praticamente è uguale al linguaggio macchina solo che più commestibile.

11. PROVA2.COM complichiamo il tutto!

Copiamo PROVA.COM in PROVA2.COM.

Apriamo il nostro amico DEBUG.EXE PROVA2.COM.

Poi inseriamo questi comandi:

-F 100 2100 90 

-A 100

Quindi immettiamo le nuove istruzioni:

????:0100  MOV     AH,0F 
????:0102 INT 10

????:0104 MOV AH,07
????:0106 INT 21
????:0108 CMP AL,1B
????:010A JZ 0141

????:010C PUSH AX

????:010D MOV CL,04
????:010F ROR AL,CL
????:0111 AND AL,0F
????:0113 OR AL,30
????:0115 CMP AL,39
????:0117 JBE 011B
????:0119 ADD AL,07

????:011B MOV AH,02
????:011D MOV DL,AL
????:011F INT 21

????:0121 NOP

????:0122 POP AX

????:0123 AND AL,0F
????:0125 OR AL,30
????:0127 CMP AL,39
????:0129 JBE 012D
????:012B ADD AL,07

????:012D MOV DL,AL
????:012F MOV AH,02
????:0131 INT 21

????:0133 MOV DL,0D
????:0135 MOV AH,02
????:0137 INT 21
????:0139 MOV DL,0A
????:013B MOV AH,02
????:013D INT 21

????:013F JMP 0104

????:0141 MOV AH,4C
????:0143 INT 21

digitiamo quindi:

-W     per registrare il programma

e quindi:

-Q

per uscire al DOS

Ora cerchiamo di capire:

  • MOV AH,0F e INT 10 Questi comandi permettono di avere all'interno del registro BH la parte grafica. Imposta la grafica in quel registro.
  • MOV AH,07 - INT 21 Aspetta che l'utente inserisca un comandi, un carattere dalla sua tastiera e poi lo inserisce all'interno del registro AL.
  • CMP AL,1B - JZ 0141 Controlla che il carattere inserito sia ESC (che è il numero ASCII 1B) e se questo è, salta alla riga 0141 terminando quindi il programma.
  • PUSH AX Salva temporaneamente il registro AX nello Stack. Come ho già detto salva il registro AX perché questo è a 16 bit. Non avrei potuto salvare solo AL perché il comando PUSH funziona solo con registri a 16 bit.
  • MOV CL,04 - ROR AL,CL Il comando ROR (ROtate) Right serve a spostare il contenuto di AL di 4 posizioni (ovviamente grazie al comando MOV ove ho impostato 4). In questo modo io ho i primi 4 bit a destra liberi mentre ho occupati gli 4 di sinistra.
  • AND AL,0F - OR AL,30 Col primo comando io azzero i bit di sinistra in modo da lasciare i 4 bit di destra invariati. Col secondo comando poi trasformo il numero che l'utente ha inserito in codice ASCII. Poi lo sostituisce dentro ad AL.
  • CMP AL,39 - JBE 011B Questi comandi servono a controllare che il numero inserito sia compreso da 30 e 39. Se ciò è affermativo salta (JBE) alla riga successiva.
  • ADD AL,7 Se il numero è maggiore di 39 vuol dire che è compreso tra 3A e 3F (in esadecimale). Se io aggiungo 7 salterò alla visualizzazione dele cifre esadecimali comprese tra A e F.
  • MOV DL,AL - MOV AH,02 - INT 21 E' una funzione di visualizzazione DOS che fa avanzare il cursore, e che richiede che il carattere sia in DL. La funzione è la n.2 (MOV AH,02).

Facciamo una pausa e spieghiamo fino a qui.

Lo so che sono stato poco chiaro! Ma ora facciamo degli esempi.

Il nostro programma serve a visualizzare, a carattere premuto il corrispettivo codice ASCII. Se quindi io inserisco "A" dovrei quindi ottenere il carattere 41. Come si fa a fare ciò. L'assembler non ha una funzione che permette di scrivere direttamente come in altri linguaggi. Qui bisogna usare solo caratteri ASCII (questa è la parte dura...). Con il programma di prima abbiamo imparato a, inserendo un tasto ricevere sullo schermo proprio quello da noi digitato. Ma come fare a visualizzare il carattere ASCII? Come fare a visualizzare a "A" premuta, proprio il numero 41? Per fare ciò bisogna far in moda da ricevere 2 numeri: il 4 e l'1. perché questi vanno separati!

Dobbiamo quindi trasformare ad uno ad uno il numero in codice ASCII corrispondente.

Così otterremo che il 4 è 34h e il 31h il 1.

Per raggiungere questo scopo io ho ragionato così:

Digitando "A" ho caricato AL=41 . Questo lo sappiamo già fare dal primo programma.

Con il comando MOV Cl,4 e ROR AL,CL avrò AL=14. Questo perché li ho girati.

Con AND AL,0F avrò AL=04

e poi con OR AL,30 avrò AL=34 che è appunto il codice di 4. Il primo che ci serve.
Quindi il lo visualizzo.

Continuiamo col resto del codice.

  • NOP questo comando non significa nulla. Ma proprio nulla! Soltanto che se volete usare spesso il DEBUG (scelta vivamente sconsigliata!) dovrete usarlo molto spesso. La funzione di NOP è avere una riga vuota. Questo mi permette, nel caso di un errore o di una svista, di poter inserire un'istruzione al suo posto.
  • POP AX Vi ricordate che avevo salvato nello Stack il valore originario di AX? Bene! Io ora lo riprendo in modo da poter calcolare il valore del primo carattere del numero.
  • Le operazioni seguenti sono uguali a quelle viste prima per il primo valore del carattere ASCII. Non le rispiego, sono praticamente identiche solo che qui prendo il secondo numero invece che il primo.
  • JMP 0104 mi permette di incominciare il ciclo saltando alla riga 0104. Si ripeterà il tutto finchè non inserisco il carattere ESC.
  • MOV AH,4C - INT 21 Solito comando per uscire.


--------------------------FINE PRIMA LEZIONE-------------------------

Non ne posso più! Basta è finita!

Ci sentiremo poi per una prossima (forse...)

BUON DIVERTIMENTO!!

Bakunin
bakunin@meganmail.com

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT