#include <stdio.h>

/* 
Programme par Josselin Dauphin, MP2I 2026-2027
*/

int main(){
    // Texte d'introduction au fonctionnement du programme. Séparé en multiples printf pour lisibilité.
    printf("Bonjour, ce programme sert d'interpréteur du langage machine qu'on a simulé en cours sur papier.");
    printf(" Je rappelle qu'il admet 3 types d'instruction : \n");
    printf(" - ADD : 00 M1 M2 M3, qui fait la somme de M1 et M2 et place le résultat dans M3 (M1 + M2 -> M3)\n");
    printf(" - SUB : 01 M1 M2 M3, qui fait la différence de M1 et M2 et place le résultat dans M3 (M1 - M2 -> M3)\n");
    printf(" - JMP : 10 M1 aa aa, qui regarde si M1 est différent de 0, et dans ce cas là, remonte de a instructions dans le programme\n");
    printf("\nCette machine admet 4 cases mémoires, de M1 à M4, numérottés en binaire, 00 01 10 11\n");
    printf("L'entrée d'instructions se fait en binaire, avec des espaces : ww xx yy zz\n");
    printf("L'entrée des valeurs en mémoire se fait avec des entier classiques.\n");
    printf("Lorsque vous avez entré toutes vos instructions, rentrez \"11 11 11 11\" pour indiquer la fin de votre programme\n");
    
    // Acquisition des instructions
    printf("Entrez vos instructions : \n");
    int instructions[32 * 4];        // 32 instructions max, de 4 paires de bit chacunes.
    int w, x, y, z;                 // Déclaration des 4 paires de bit
    int nombreInstructions = 0;
    int i;
    for (i = 0; i < 32; i++) {
        w = -1; x = -1; y = -1; z = -1;  // valeur par défaut
        scanf("%d %d %d %d", &w, &x, &y, &z);

        if (w*(w-1)*(w-10)*(w-11) != 0 || x*(x-1)*(x-10)*(x-11) != 0 || y*(y-1)*(y-10)*(y-11) != 0 || z*(z-1)*(z-10)*(z-11) != 0) { // Cas où l'instruction rentrée est invalide (pas un nombre en binaire) ce qui est vérifiée par une expression polynomiale qui est 0 pour chaque entrée lorsqu'elles correspondent à 00, 01, 10, 11 
            printf("Mauvaise entrée : Pas une suite de bits\n");
            return 0;
        }
        if (w == 11 && x == 11 && y == 11 && z == 11) { // Cas où les instructions ont toutes été entrées (cela correspond à l'instruction 11 11 11 11)
            i = 32;
        } else {                        // Place les instructions dans 4 cases adjascentes de la liste d'instructions
            instructions[i * 4] = w;
            instructions[i * 4 + 1] = x;
            instructions[i * 4 + 2] = y;
            instructions[i * 4 + 3] = z;
            nombreInstructions++;
        }
    }
    
    
    //Acquisition des cases mémoires
    int memoire[12];                            // Permet d'indexer 00, 01, 10 et 11 directement dans l'array
    printf("Entrez vos 4 cases mémoires : ");
    scanf("%d %d %d %d", &memoire[00], &memoire[01], &memoire[10], &memoire[11]);
    
    //Execution des instructions
    
    int type; // initialise les variables de lecture des instructions (x, y, z sont déjà initialisés)
    
    int compteur; // Cette variable référence la case de l'array associé à l'instruction à exectuer. C'est donc cette variable qui est modifiée lors de l'instruction de saut
    for (compteur = 0; compteur < nombreInstructions; compteur++) {
        
        type = instructions[compteur * 4];
        x = instructions[compteur * 4 + 1];
        y = instructions[compteur * 4 + 2];
        z = instructions[compteur * 4 + 3];
        if (type == 0) { // Instruction d'addition
            memoire[z] = memoire[x] + memoire[y];
        } else if (type == 1) { // Instruction de soustraction
            memoire[z] = memoire[x] - memoire[y];
        } else if (type == 10) { // Instruction de saut
            if (memoire[x] != 0) { // C'est un saut conditionnel, basé sur la valeur de la mémoire en x
                int valeurDeJump = y * 100 + z; // Combine les deux doublets de bits en un seul nombre
                int bits[4], i;
                for (i = 0; i < 4; i++) {   // sépare les bits individuels dans un array
                    bits[i] = valeurDeJump % 10;
                    valeurDeJump = valeurDeJump / 10;
                }
                
                compteur = compteur - (bits[0] + 2 * bits[1] + 4 * bits[2] + 8 * bits[3]) - 1; // Recombine les bits de l'array avec la bonne valeur
                if (compteur < -1) { // -1 parce que on ajoute 1 au compteur à la fin de l'itération, ce qui donne 0. Même raison pour le -1 à la fin de la ligne précédente. Cela assure qu'on n'essaie pas d'exécuter une instruction non valide
                    break;
                }
            }    
        }
        
        printf("%d %d %d %d\n", memoire[00], memoire[01], memoire[10], memoire[11]); // Affiche l'état de la mémoire après chaque instruction, ainsi que le numéro de l'instruction qui viens de se faire exécuter
    }
    
    return 0;
}