Il progetto consiste nella progettazione e nella successiva realizzazione di un emulatore capace di leggere i file .s contenenti un programma in linguaggio assemblativo per 8088.
py88
|-main.py
|-file_checker.py
|-py88.py
|-executer.py
|-exceptions.py
|-divide_file.py
|-register.py
|-stack.py
|-instruction.py
|-variable.py
|-test.s
'-README.md
main.py
: Il file main.py si occupa di inizializzare l'ambiente ricavando le variabili, validando il file e avviando l'esecuzione;registers.py
: La classe che gestisce tutta la parte dei registri; Consente di memorizzare un valore tramite registers[x]=y e di leggere il registro y = registers[x]. Ogni registro è unsigned int da 2 byte, supporta anche i registri HL da 1 byte (unsigned char);file_checker.py
: valida il file. Si occupa di controllare se il file esiste, se non è una directory e se il file è leggibile;exceptions.py
: il file che contiene le varie classi di eccezioni;py88.py
: è la classe che si occupa di interpretare le istruzioni;stack.py
: è la classe che si occupa di emulare il comportamento dello stack memorizzando interi a 2byte (indirizzi e dati);test.s
: file di test scritto in 8088instruction.py
: la classe Instruction contiene l'istruzione ed i suoi operandi, in modo da poterli richiamare al bisogno;variable.py
: la classe Var contiene il nome, la dimensione del dato, in modo da poterli richiamare al bisogno;divide_file.py
: si occupa di fare il parse del programma dividendo sezione text, bss, data e le costanti;executer.py
: la classe core esegue le istruzioni andando a modificare stack, registri e i valori in memoria
richiede python 3.10
Per usare py88 è sufficiente richiamare python3 passando come argomento il file main.py.
python3 py88/main.py test.s
se si volesse eseguire il debug al codice è sufficiente aggiungere l'opzione
-d
dopo il file .s
op, op1, op2 sono operandi generici. Essi possono essere referenze a dati in memoria, costanti o registri.
Costanti di tipo binario (0b000101), decimali (4289), esadecimali (0x69), ottali (0o263).
I puntatori alla memoria vengono dichiarati con le parentesi: (BX) oppure (var).
Si possono usare i registri a 1 o 2 byte (AX, BX...)
Ciascuna istruzione può avere una etichetta in modo da poter essere richiamata.
PUSH op
-> inserisce op nello stack;POP [opzionale op]
-> fa il pop dallo stack, se è presente un operando carica il risultato del pop dentro.
ADD(B) op1, op2
-> esegue l'addizione e poi memorizza il risultato su op1SUB(B) op1, op2
-> esegue la sottrazione e poi memorizza il risultato su op1MUL op
-> esegue la moltiplicazione fra AX e OP e poi memorizza il risultato su AXMULB op
-> esegue la moltiplicazione fra AL e OP e poi memorizza il risultato su ALDIV op
-> esegue la divisione fra AX e OP e poi memorizza il risultato su AX e il resto su DXDIVB op
-> esegue la divisione fra AL e OP e poi memorizza il risultato su AL e il resto su AHINC op
-> incrementa di 1 OPDEC op
-> decrementa di 1 OP
AND op1, op2
-> esegue l'operazione AND fra OP1 e OP2 e poi memorizza il risultato su OP1OR op1, op2
-> esegue l'operazione OR fra OP1 e OP2 e poi memorizza il risultato su OP1NEG op
-> esegue l'operazione NOT su OP e poi memorizza il risultato su OPXOR op1, op2
-> esegue l'operazione XOR fra OP1 e OP2 e poi memorizza il risultato su OP1
-
LOOP etichetta
-> se il registro CX è maggiore o uguale a 0 fa un jump all'etichetta e decrementa CX -
JMP etichetta
-> fa un salto incondizionato a quella etichetta -
CMP op1, op2
-> esegue la comparazione fra OP1 e OP2 -
JCXZ etichetta
-> controlla se CX è uguale a 0 -
JGE etichetta
-> se durante il precedente confronto$a>=b$ allora salta -
JO etichetta
-> se durante il precedente confronto$a+b$ causa un overflow allora salta -
JLE etichetta
-> se durante il precedente confronto$a<=b$ allora salta -
JE etichetta
-> se durante il precedente confronto$a==b$ allora salta -
JL etichetta
-> se durante il precedente confronto$a \lt b$ allora salta -
JG etichetta
-> se durante il precedente confronto$a \gt b$ allora salta -
JNE etichetta
-> se durante il precedente confronto$a!=b$ allora salta -
CALL etichetta
-> chiama una funzione
SYS
-> esegue una syscall al sistema operativo. Le chiamate supportate sono printf, write (solo su stdout), getchar ed exit
MOV(B) op1, op2
-> copia OP2 in OP1