vm - virtual machine
Versione: 1.48
1 Gennaio 1997
Alberto Bellina
Via Cà Paletta, 11
37024 S.Peretto di Negrar (VR)
SOMMARIO
Version Data Descrizione e 1.48 24.12. implementato addressing indexed 96 verificato corretta implementazione di istruzioni fino a 5F 1.44 08.12. verificato numeri hex con $ e numeri binari con % 96 migliorato e semplificato macro aggiunto set semplificato dei flag di stato es: set flag Z 1 compilazione con Visual C++ 1.5 1.33 11.11. aggiunto finestre e menu (vms) 96 gestione stringhe in file separato per versioni in lingue diverse output redirezionabile mediante set out filename 1.2 corretto errore lettura codici esadecimali con lettere maiuscole (es.FF) gestione valori binari con %0101010101 migliorato gestione MMU 1.1 inserito MMU software per gestione indirizzi utilizzati documentazione in formato WinWord 2.0 1.0 gestione check se accedute locazioni di memoria miglior gestione di opcode in page2 e page3 migliorato gestione linguaggio con alcune sintassi modificate e gestione automatica default in ultimo parametro gestione automatica id in event e check
Può funzionare semplicemente per emulare processori a 8 bit, ma con poche modifiche si può utilizzare per processori con una word maggiore.
La realizzazione nonostante sia stata realizzata in linguaggio C è del tipo ad oggetti (per quanto permesso dal C) e quindi facilmente trasformabile in linguaggio C++, e inseribile in una interfaccia grafica evoluta come da esempio vms.
Può gestire più macchine virtuali contemporanee di tipo omogeneo (da finire implementazione).
Gestisce i comandi da file o da tastiera, la sintassi viene elencata più avanti.
Consente di operare sui seguenti elementi del processore:
memoria
stack
registri
program counter
registro di stato
tick
Ed inoltre sui seguenti elementi della macchina virtuale:
breakpoints
eventi a tempo
check per accessi a memoria
È possibile inoltre leggere i dati da inserire in memoria da un file in un formato molto libero.
Può essere utile a chiunque necessiti di:
* testing programmi per macchine operatrici
* addestramento programmatori assembler
* simulazione eventi non riproducibili nella realtà
10 decimale
$a esadecimale
%1010 binario
esecuzione monitorata si un programma sottoposto come codice esadecimale.
Memory Management Unit
<-------------low byte--------------->
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 Y Y Y Y Y Y Y Y Y Y Y Y 1 Y Y Y N Y Y N Y Y Y N N 2 Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y 3 Y Y Y Y Y Y N N 4 Y Y Y Y Y Y Y Y Y Y Y 5 Y Y Y Y Y Y Y Y Y Y Y 6 7 8 Y Y Y Y Y Y Y 9 y y y y Y Y Y Y Y y Y y y Y Y Y a b y y Y c Y Y d y y y y Y Y Y Y Y y Y y Y e f y
Page 2: Y 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f,
y 8e, 93, 9c, 9e, 9f, de, df
Page 3: Y ,
y 83, 8c, 93, 9c
Legenda:
Y yes implementato completamente (doppia verifica)
y yes implementato parzialmente (es: flag non tutti gestiti)
? da verificare
N non implementato
blank da fare
opcode non utilizzato
I flag dello status register sono corretti per ZERO e MINUS.
Il flag CARRY non sempre è corretto.
La memoria disponibile è da 0 a $4000 (per problemi di compilazione).
command element parameters...
Le keyword utilizzabili, divise per categorie, sono:
* generali
help, h
info, i
load
echo
* comandi
clear
display, d
set
disasm, da
* elementi
mem
mmu
stack
pc
stat
tick
event
check
trace
debug
out
* running
run, r
step, s
continue, c
break
* argomenti
at
on
off
Per i comandi di maggior utilizzo sono stati creati degli alias composti da una o al massimo due lettere.
I comandi sono:
help, h
info, i
load
save
clear
display, d
set
disasm, da
run, r
step, s
continue, c
help
help "comando"
La prima forma genera un help generale, mentre la seconda consente un help più approfondito su una determinata keyword, per esempio:
help "load" presenta l'help per il comando load
h "display" presenta l'help per il comando display
h "event" presenta l'help per il comando event
Attenzione: i doppi apici sono obbligatori.
L'output tipico è il seguente:
Virtual Machine Info [vm - V. 1.44 - 08.12.96]
Memory : 16384 ($4000)
01 $1000..$1fff OFF
02 $0000..$0fff ON
PC : $0000
Ticks : 0, Time : 0 uSec (10 microseconds for tick)
Stack : Current($03e7) Base($03e7) Max(20)
Status : $00 (-----)
Registers :
(A=$00)(B=$00)(D=$0000)(X=$0000)(Y=$0000)(U=$0000)(S=$03e7)
(DP=$00)(PC=$0000)
Breakpoints :
Events : ID TICK:STEP ADD VAL FILE FLAG REACHED LAST
2 15 5 00c8 01 null RESPAWN 0 0
1 10 0 00c8 01 null SINGLE 0 0
Checks : ID ADD:TO FLAG REACHED
2 00c8:012c SINGLE 0
1 0064:-1 RESPAWN 0
Debug : OFF - level 1
Flags : TRACE ON
echo "stringa da stampare a video"
Questo comando viene utilizzato nei files .vm per evidenziare le operazioni in corso o avvisare l'operatore di eventuali notizie importanti, per esempio:
...
load program
echo "programma caricato dalla locazione $2000"
memoria es: d 0 100
mmu es: d mmu
registri es: d reg
status register es: d stat
stack pointer es: d sp
program counter es: d pc
breakpoints es: d break
eventi es: d event
checks es: d check
tick es: d tick
La maggior parte di queste informazioni possono essere visualizzate contemporaneamente mediante il comando info (abbreviato i).
Di seguito l'elenco delle possibilità:
memoria syntax: set [mem] address [address_to] value
set [mem] address_from address_to {on|off}
es: set $20 $100
set 0 1000 $ff
set 0 99 on
set 100 200 off
mmu syntax: set mmu address address_to {on|off}
es: set mmu $0 $1000 on
registri syntax: set reg register_name value
es: set reg "A" 10
status register syntax: set stat { value | "name" { 0|1} }
es: set stat $01
set stat "Z" 1
set stat "N" 0
stack pointer syntax: set stack address [maxbytes]
es: set sp 1000 100
program counter syntax: set pc value
es: set pc 100
breakpoints syntax: set break {on|off} address
es: set break on $200
eventi syntax: set event ticks [ : ticks ] address {value|filename}
es: set event 100 $64 0
set event 100:20 $64 0
set event 100 1000 data1
checks syntax: set check {address [: to_address] [respawn_flag]}| id off
es: set check 100:200 1
set check 200
set check 1 off
tick syntax: set tick micro_sec_for_tick
es: set tick 10
trace syntax: set trace {on|off}
es: set trace on
set trace off
debug syntax: set debug {level|on|off}
es: set debug 1
set debug on
set debug off
out syntax: set out filename
es: set out pippo
disassemble syntax: da [address] [, address]
es: da 100 200
da 100 , 200
da 100
da
Se non viene fornito alcun indirizzo viene eseguito il disasm dall'indirizzo contenuto nel Program Counter.
Per ogni istruzione viene anche indicato il tipo di addressing utilizzato, che può valere:
imm immediate
rel relative
dir direct
ihn inherent
ext extended
ind indirect
Di seguito un esempio dell'output disassemblato della momoria a partire dall'indirizzo 0:
[$0000] $86 LDA $64 (imm)
[$0002] $c6 LDB $06 (imm)
[$0004] $8e LDX $0064 (imm)
[$0007] $10 $8e LDY $00c8 (imm)
load ident [ at address]
Il files sono identificati dall'estensione .asm che viene automaticamente aggiunta ad ogni nome di file nel comando load.
Il formato del file è alquanto libero, con le seguenti restrizioni:
- il carattere # indica che segue un commento fino al termine
- i caratteri Space e Tab sono ininfluenti
- il carattere $ indica un numero esadecimale
- il carattere % indica un numero binario
- il carattere - indica un carattere decimale negativo
- il carattere : indica un indirizzo assoluto
- i caratteri :+ indicano un indirizzo relativo a quello corrente
- il carattere " indica una stringa terminata da un altro "
Esempio di file:
# carica sempre a indirizzo 0
:0
$0e $00 10 # [0000] jmp to address $000a
$05 # number to decrement
$fd # number to increment
:+5 # salta ad indirizzo corrente +5
$0a $00 $03 # [000a] dec address 3
$26 -13 # bra if not zero to 0
$0c $00 $04 # [000f] inc address 4
$26 -3 # bra if not zero to 000f
$2 # not existent code ----------------------------
"ABCDEF" # stringa dati ascii
La sintassi è la seguente:
save filename
A filename viene automaticamente aggiunta l'estensione .dmp.
run, r
step, s
continue, c
break, b
Eseguendo il comando r 100 si esegue il programma caricato all'indirizzo 100, mentre eseguendo semplicemente il comando r si esegue il programma caricato all'indirizzo contenuto nel Program Counter.
Eseguendo il comando s 100 si eseguono 100 passi del programma in corrente esecuzione, mentre eseguendo semplicemente il comando s si esegue un singolo passo.
Se nessun comando di run è stato dato prima si esegue gli step a partire all'indirizzo contenuto nel Program Counter.
mem
mmu
stack
pc - program counter
stat
tick
event
check
trace
debug
out
La memoria può essere dumpata o disassemblata a partire da qualsiasi indirizzo.
VM è infatti dotata di una versione software di MMU per gestire gli accessi a locazioni di memoria non esistenti.
Si possono definire i range degli indirizzi esistenti mediante il comando:
set mmu $0 $1000 on
Gli indirizzi non esistenti devono essere definiti mediante il comando
set mmu $1001 $1999 off
Mediante il comando d mmu si può visualizzare lo stato della MMU:
Tutti gli accessi ad indirizzi non esistenti saranno seguiti da un messaggio di warning come il seguente:
WARNING: memory address $1008 is out of ranges ($1000..$1fff)
Base indirizzo da cui parte lo stack
Current valore attuale dello stack
Max il massimo numero di bytes utilizzabili dallo stack (attualmente non usato)
Stack : Current($03e7) Base($03e7) Max(20)
DA FARE: ??? direzione dello STACK
Contiene i flag utilizzati dal processore e variano in ogni implementazione, nella tabella di seguito vediamo i vari casi:
Processore Nome flag Descrizione 6809 CARRY OVERFLOW ZERO se l'operando utilizzato vale 0 NEGATIVE se l'operando utilizzato ha l' MSB = 1 HCARRY 68000 80x86
Gli eventi possono essere di due tipi:
settaggio di una locazione di memoria
lettura di un file contenente dati
Cioè si può dire:
- quando raggiungi il numero di tick t setta la locazione di memoria m al valore v.
- quando raggiungi il numero di tick t carica il file f dalla locazione di memoria m
Inoltre è possibile indicare se l'evento deve riattivarsi ed ogni quanti ticks, per esempio:
attivarsi al tick 100 e riattivarsi ogni 20 e resettare la memoria $400:
set event 100:20 $400 0
attivarsi al tick 200 e settare la memoria $400 al valore $ff:
set event 200 $400 $ff
attivarsi al tick 300 e caricare il file pippo.asm alla memoria $400:
set event 300 $400 pippo
Di seguito l'esempio dell'output del comando d event:
Events : ID TICK:STEP ADD VAL FILE FLAG REACHED LAST
2 15: 5 0400 01 null RESPAWN 0 0
1 10: 0 0200 01 null SINGLE 0 0
Il significato delle colonne è il seguente:
ID è il progressivo dell'evento e viene automaticamente assegnato in modo incrementale TICK:STEP con TICK si indica il tick a cui l'evento deve essere attivato, mentre con STEP si indica ogni quanto deve essere riattivato dopo la prima volta (in questo caso FLAG varrà RESPAWN). ADD indica l'indirizzo a cui viene effettuata l'azione associata all'evento VAL se presente è il valore che viene settato all'indirizzo ADD FILE se presente è il nome del file che viene caricato a partire dall'indirizzo ADD FLAG può valere SINGLE o RESPAWN e viene automaticamente determinato in base alla assenza o presenza del valore STEP REACHED è un valore interno ed indica se l'evento è stato raggiunto almeno una volta LAST è un valore interno che indica l'ultimo tick a cui si è verificato l'evento
Il check consente di dire:
- quando leggi la locazione di memoria m avvisami
Inoltre è possibile indicare se il check deve riattivarsi automaticamente, per esempio:
segnalare sempre se acceduta la memoria 100:
set check 100 1
segnalare solo una volta se acceduta la memoria 400:
set check 400 0
segnalare sempre se acceduta la memoria 100..200:
set check 100 200 1
Di seguito un esempio di output del comando d check:
Checks : ID ADD:TO FLAG REACHED
2 00c8:012c SINGLE 0
1 0064:-1 RESPAWN 0
Il significato delle colonne è il seguente:
ID è il progressivo dell'evento e viene automaticamente assegnato in modo incrementale ADD indica l'indirizzo a cui viene effettuato il check TO se presente indica il range di indirizzi da verificare FLAG può valere SINGLE o RESPAWN e premette di rendere un check singolo o ripetitivo REACHED è un valore interno ed indica se l'evento è stato raggiunto almeno una volta
set out filename
Si può ripristinare l'output verso lo screen mediante il comando:
clear out
vm [files]* [-]
dove:
files files che devono essere letti
- legge da tastiera
Per esempio:
lancia vm leggendo il contenuto del file init.vm ed attendendo altri comandi da tastiera:
vm init.vm -
lancia vm leggendo il contenuto di più files di comandi
vm init.vm run.vm
load prog at 10 carica il programma contenuto nel file prog.asm alla locazione 10
set stack 10 setta lo stack alla posizione 10 dec
set mem 100 $a mette nella locazione 100 il valore a hex
set pc 10 setta il program counter a 10 dec
set sp 100 setta lo stack pointer a 100 dec
set event 250 100 $44 setta l'evento ad attivarsi quando il numero di ticks raggiunge 250
ed a settare la memoria 100 al valore 44
set event 500 100 eventi setta l'evento ad attivarsi quando il numero di ticks raggiunge 500
ed a leggere il file eventi alla locazione di memoria 100
display pc visualizza Program Counter
display tick visualizza stato Tick,. contatore e micro secondi per tick
display event visualizza eventi inseriti
disasm disassembla a partire del program counter
execution
run lancia il programma caricato all'indirizzo puntato dal PC
run 100 lancia il programma cariacato all'indirizzo 100
step esegue uno step
step 10 esegue 10 step
continue continua fino al prossimo breckpoint
break on $1000 mette un breakpoint all'indirizzo $1000
break off $1000 toglie il breakpoint all'indirizzo $1000
trace on attiva il trace durante l'esecuzione di istruzioni,
la corrente viene visualizzata
various
info
help
quit
+---------------------------------+
| CPU +---------+|
| +->|registers||
| | +---------+|
+-------+ +--------+ +-------+ +-----|-+ +-------+ | |
| | |file .vm| | | | | | | | | |
|screen |<->| e |<->|parser |-->| vm | |-->| code |<----+ |
| | |kbd/scr | | | | | | | | | +---------+|
+-------+ +--------+ +-------+ +-----|-+ +-------+ | | memory ||
per vms ^ | +->| ||
| | | ||
| | +---------+|
| +---------------------------------+
|
+-------+
| .asm |+
+-------+| files assembler
+-------+
L'interfaccia grafica è stata realizzata utilizzando curses, che per l'abbisogna sono state portate anche in ambiente Ms-Dos.
Il seguente disegno illustra lo stack per l'utilizzazione dell'ambiente grafico:
+-------------+
| vms | nel file screen.c
+-------------+
| vm |
+-------------+
| cmenu | libreria Cmenu by Alberto Bellina
+-------------+
| curses | libreria curses
+-------------+
Semplicità di utilizzo
Utilizzo di menu e tasti
L'aspetto della maschera somiglia al seguente:
1 +-------------------------------------------------------------------------+
2 | File Edit Load Run Operation Util Help |
3 +----------------------------------------------------------+-[Register]---+
4 | | A=0000er |
5 | | B=0000 |
6 | | D=0000 |
7 | | |
8 | +-[Various]----+
9 | | Stack=0000 |
10| | Stat =NZCHV |
11| | Ticks=00000 |
12| +-[Breaks]-----+
13| | |
14| | 1 110:10 |
15| | |
16| +-[Events]-----+
17| | |
18| | |
19| | |
20| +-[Checks]-----+
21| | |
22| | |
23| | |
24+[help] [Run] [Step] [Mem] [Dis] [Info] [Trace] [Break] ---+--------------+
La maggior parte delle operazioni sono eseguite mediante menu o finestre e tasti funzione.
Il tasto ESCAPE consente quasi sempre di ritornare al livello precedente o superiore.
Tra le espansioni si può pensare ad un generatore automatico del programma di gestione delle istruzioni a partire da un meta-linguaggio (vml) di programmazione.
Questo consentirebbe di creare un emulatore mediante le specifiche del linguaggio e questo passato ad un generatore (vmg), genererebbe il codice necessario.
REGISTERS:
A s1
B s1
D s2
DP s1
PC s2
STAT:
SIZE s1
FLAGS ZERO, NEGATIVE, CARRY
INSTRUCTIONS:
$00 NEG b1 { (DP+b1) = 0 - (DP+b1) }
$0e JMP b1 { PC = PC + (DP+b1) }
Ovviamente si deve trovare una sintassi regolare che possa rappresentare tutte le tipologie di operazioni eseguibili con le istruzioni di un processore (sono convinto che esista già senza doverla inventare).
Disegno Generazione Compilazione Output
**********
* editor *
********** +--------+
| | vm.lib |
V +--------+
+----------+ ********* +---------+ |
| 6809.vml | --> * vmg * --+--> | c6809.c | --+ V
+----------+ ********* | +---------+ | ****** **********
| +--> * cc * --> * vm6809 *
| +---------+ | ****** **********
+--> | c6809.h | --+
+---------+
I due ( o più ) files generati sono uniti ai moduli standard e insieme a questi generano la versione di vm adeguata al processore rappresentato nel linguaggio.
Estensione Descrizione .vm File comandi passati sulla linea di comando .dmp File creato con il comando save .asm File contenente i sorgenti assembler
La tabella seguente presenta le portabilità dei programmi vm e vms per le maggiori piattaforme.
Piattaforma VM VMS IBM AIX si si MS-DOS si si HP-UX si (da testare) si (da testare) WIN95 da testare da testare WIN 3.1 si (da testare) no, di deve sostituire il modulo screen.c UNIX generic si (da testare) si (da testare)
NOME DESCRIZIONE c6809.c Contiene tutte le parti specifiche del processore 6809 c6809.h Contiene tutte le definizioni specifiche del processore 6809 instruct.c lex.c Contiene analizzatore lessicale mess.c Contiene i tutti i messaggi stampati dal programma parser.c E' il file generato dal compiletore YACC parser.h parser.y Contiene la sintassi YACC per gestire gli input da file e da tastiera screen.c util.c Contiene funzioni di utilità generale vm.c Contiene tutte le funzioni relative alla Virtual Machine vm.h Contiene tutte le definizioni relative alla Virtual Machine
Stato Descrizione Da fare lettura file dati con formati diversi o personalizzabili per esigenze utente Da fare gestione parallela di più macchine virtuali (il codice è predisposto) Da definire eventi di tipo diverso (per esigenze utente) Da fare emulazione JAVA Virtual Machine Da terminare inserimento in ambiente grafico (per vms già esistente) OK aggiungere elemento out per poter eseguire il comando set out filename per mandare tutti gli output verso un file Da verificare Memory Management Unit Da fare Creare un meta linguaggio che permetta di definire le caratteristiche di un processore e delle sue istruzioni per creare facilmente ed automaticamente qualsiasi emulazione Da fare Controllare incremento Stack oltre il valore Max e definire direzione di crescita dello stesso Da fare Simulazione degli interrupt