#include #include #include /* ================================================================ * VM — Machine Virtuelle à pile (mini-Forth simplifié) * ================================================================ * Registres : * ip — instruction pointer (compteur ordinal) * sp — stack pointer (sommet de pile) * Mémoire : * mem[] — mémoire de données (256 mots) * stack[] — pile de travail (128 mots) * ================================================================ * Jeu d'instructions (postfixé, comme en Forth) : * PUSH v — empiler la valeur v * DUP — a → a a * DROP — a → * SWAP — a b → b a * OVER — a b → a b a * ADD — a b → (b + a) * SUB — a b → (b - a) * MUL — a b → (b * a) * NEG — a → (-a) * INC — a → (a + 1) * DEC — a → (a - 1) * EQ — a b → (b == a) [rend 0 ou 1] * NE — a b → (b != a) * GT — a b → (b > a) * LT — a b → (b < a) * JMP a — saut inconditionnel vers l'adresse a * JZ a — sauter si sommet = 0 * JNZ a — sauter si sommet ≠ 0 * LOAD a — empiler mem[a] * STORE a — depiler → mem[a] * PRINT — depiler et afficher le nombre * PRTS a — afficher la chaîne en mémoire à l'adresse a * HALT — arrêter la machine * ================================================================ */ /* --------------------------------------------------------------- * Opcodes * --------------------------------------------------------------- */ enum { PUSH, DUP, DROP, SWAP, OVER, ADD, SUB, MUL, NEG, INC, DEC, EQ, NE, GT, LT, JMP, JZ, JNZ, LOAD, STORE, PRINT, PRTS, HALT }; /* --------------------------------------------------------------- * Ressources de la VM * --------------------------------------------------------------- */ #define MEM_SIZE 256 #define STACK_SIZE 128 int ip; /* instruction pointer */ int sp; /* stack pointer */ int mem[MEM_SIZE]; /* mémoire de données */ int stack[STACK_SIZE]; /* pile de travail */ int pas_a_pas; /* mode pas à pas (déverminage) */ /* --------------------------------------------------------------- * Primitives de pile * --------------------------------------------------------------- */ void pousser(int v) { if (sp >= STACK_SIZE) { fprintf(stderr, "DEBORDEMENT DE PILE (sp=%d)\n", sp); exit(1); } stack[sp++] = v; } int depiler(void) { if (sp <= 0) { fprintf(stderr, "PILE VIDE (ip=%d)\n", ip); exit(1); } return stack[--sp]; } int sommet(void) { if (sp <= 0) { fprintf(stderr, "PILE VIDE (ip=%d)\n", ip); exit(1); } return stack[sp - 1]; } /* --------------------------------------------------------------- * Afficher la pile (pour déverminage) * --------------------------------------------------------------- */ void afficher_pile(void) { printf(" pile["); for (int i = 0; i < sp; i++) printf("%s%d", (i == 0) ? "" : " ", stack[i]); printf("]"); } /* --------------------------------------------------------------- * Afficher une instruction (pour déverminage) * --------------------------------------------------------------- */ void afficher_instr(int code[], int pos) { static const char *nom[] = { "PUSH", "DUP", "DROP", "SWAP", "OVER", "ADD", "SUB", "MUL", "NEG", "INC", "DEC", "EQ", "NE", "GT", "LT", "JMP", "JZ", "JNZ", "LOAD", "STORE", "PRINT", "PRTS", "HALT" }; int op = code[pos]; if (op == PUSH || op == JMP || op == JZ || op == JNZ || op == LOAD || op == STORE || op == PRTS) printf(" %4d: %-5s %d\n", pos, nom[op], code[pos + 1]); else printf(" %4d: %s\n", pos, nom[op]); } /* --------------------------------------------------------------- * Exécuter un programme * --------------------------------------------------------------- */ void executer(int code[], int taille) { ip = 0; /* ne pas réinitialiser sp — la valeur initiale est déjà sur la pile */ while (ip < taille) { if (pas_a_pas) { afficher_instr(code, ip); afficher_pile(); printf("\n"); getchar(); /* attendre une touche */ } int instr = code[ip++]; switch (instr) { /* --- Poussée --- */ case PUSH: pousser(code[ip++]); break; /* --- Duplication / suppression / permutation --- */ case DUP: { int v = depiler(); pousser(v); pousser(v); break; } case DROP: { depiler(); break; } case SWAP: { int a = depiler(); int b = depiler(); pousser(a); pousser(b); break; } case OVER: { int a = depiler(); int b = depiler(); pousser(b); pousser(a); pousser(b); break; } /* --- Arithmétique --- */ case ADD: { int a = depiler(); int b = depiler(); pousser(b + a); break; } case SUB: { int a = depiler(); int b = depiler(); pousser(b - a); break; } case MUL: { int a = depiler(); int b = depiler(); pousser(b * a); break; } case NEG: { pousser(-depiler()); break; } case INC: { pousser(depiler() + 1); break; } case DEC: { pousser(depiler() - 1); break; } /* --- Comparaisons --- */ case EQ: { int a = depiler(); int b = depiler(); pousser(b == a); break; } case NE: { int a = depiler(); int b = depiler(); pousser(b != a); break; } case GT: { int a = depiler(); int b = depiler(); pousser(b > a); break; } case LT: { int a = depiler(); int b = depiler(); pousser(b < a); break; } /* --- Sauts --- */ case JMP: { ip = code[ip]; break; } case JZ: { int a = code[ip++]; if (depiler() == 0) ip = a; break; } case JNZ: { int a = code[ip++]; if (depiler() != 0) ip = a; break; } /* --- Accès mémoire --- */ case LOAD: { int a = code[ip++]; pousser(mem[a]); break; } case STORE: { int a = code[ip++]; mem[a] = depiler(); break; } /* --- Entrées-sorties --- */ case PRINT: printf("%d", depiler()); break; case PRTS: { int a = code[ip++]; while (mem[a] != 0) printf("%c", mem[a++]); break; } /* --- Arrêt --- */ case HALT: ip = taille; break; default: fprintf(stderr, "ERREUR : opcode %d inconnu (ip=%d)\n", instr, ip - 1); exit(1); } } } /* --------------------------------------------------------------- * Désassembleur — afficher le code VM en texte lisible * --------------------------------------------------------------- */ static const char *nom_opcode(int op) { static const char *noms[] = { "PUSH", "DUP", "DROP", "SWAP", "OVER", "ADD", "SUB", "MUL", "NEG", "INC", "DEC", "EQ", "NE", "GT", "LT", "JMP", "JZ", "JNZ", "LOAD", "STORE", "PRINT", "PRTS", "HALT" }; if (op >= 0 && op < HALT + 1) return noms[op]; return "???"; } void desassembler(int code[], int taille) { int i = 0; while (i < taille) { printf("%4d: ", i); int op = code[i]; if (op == PUSH || op == JMP || op == JZ || op == JNZ || op == LOAD || op == STORE || op == PRTS) { printf("%-5s %d\n", nom_opcode(op), code[i + 1]); i += 2; } else { printf("%s\n", nom_opcode(op)); i++; } } } /* --------------------------------------------------------------- * Programme : Factorielle * --------------------------------------------------------------- * On définit le programme directement sous forme de tableau * d'entiers. Les adresses de saut sont données par des * constantes symboliques pour rester lisibles. * * Algorithme (assembleur VM) : * * STORE N — mémoriser n * PUSH 1 — res = 1 * PUSH 2 — i = 2 * LOOP: * DUP — i i * LOAD N — i i n * GT — i (i>n) * JNZ END — si i>n, fin * SWAP — i res * OVER — i res i * MUL — i res*i * SWAP — res*i i * INC — res*i i+1 * JMP LOOP * END: * DROP — res * PRINT — afficher res * HALT * --------------------------------------------------------------- */ /* adresses mémoires réservées */ enum { N = 0, MSG = 200 }; /* étiquettes de saut */ enum { LOOP = 6, END = 19 }; int programme[] = { /* 0 */ STORE, N, /* 2 */ PUSH, 1, /* 4 */ PUSH, 2, /* 6 */ DUP, /* 7 */ LOAD, N, /* 9 */ GT, /*10 */ JNZ, END, /*12 */ SWAP, /*13 */ OVER, /*14 */ MUL, /*15 */ SWAP, /*16 */ INC, /*17 */ JMP, LOOP, /*19 */ DROP, /*20 */ PRTS, MSG, /*22 */ PRINT, /*23 */ HALT, }; int taille_prog = sizeof(programme) / sizeof(programme[0]); /* --------------------------------------------------------------- * Initialiser la mémoire avec les chaînes et les zéros * --------------------------------------------------------------- */ void initialiser_memoire(void) { for (int i = 0; i < MEM_SIZE; i++) mem[i] = 0; mem[MSG] = ' '; mem[MSG + 1] = '='; mem[MSG + 2] = ' '; mem[MSG + 3] = 0; } /* --------------------------------------------------------------- * Tests * --------------------------------------------------------------- */ int main(int argc, char *argv[]) { if (argc > 1 && strcmp(argv[1], "--pas-a-pas") == 0) pas_a_pas = 1; initialiser_memoire(); printf("=== Désassemblage du programme ===\n"); desassembler(programme, taille_prog); printf("\n=== Factorielle 0 à 12 ===\n"); long long attendus[] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600}; int ok = 0, echec = 0; for (int n = 0; n <= 12; n++) { sp = 0; pousser(n); executer(programme, taille_prog); int res = stack[0]; if (res == attendus[n]) { printf(" OK ! %d = %d\n", n, res); ok++; } else { printf(" FAIL ! %d = %d (attendu %lld)\n", n, res, attendus[n]); echec++; } } printf("\n %d passes, %d echoues\n", ok, echec); return echec > 0 ? 1 : 0; }