Tiny Virtual Machine (C)

Tiny Virtual Machine est un programme qui, en lisant un fichier source, va interpreter les commandes donnees par le-dit fichier.

Chaque commande est constituee de quatre parties: une est l’opération, la deuxieme est l’emplacement dans le registre (0x00 - 0x10), la troisieme et quatrieme sont les nombres affectes par l’operation

Les operations:

Methodes d’affichage:

Saisir l’entree du clavier:

Exemple

Creez un fichier vide a l’endroit ou se trouve le fichier source et nommez le “code”. Avec un editeur hexadecimal (comme HexEdit), entrez le code suivant:

01 00 0F 0F 02 01 0F 05 03 02 0F 0F 04 03 10 02 05 04 0F 0F 06 05 0F 0F 07 06 0F 0F 08 07 06 04 09 08 09 03 0A 09 06 05 0B 0A 09 02 20 00 20 01 20 0A 22 0B 20 0B

01 00 0F 0F, qui est la premiere operation a effectuer, pourrait se traduire par: “je veux une addition que je place dans le registre numero 0x00 et cette addition est 0F + 0F "

Compilez le programme:

gcc tinyvirtualmachine.c -o tinyvirtualmachine -Wall -Wextra
./tinyvirtualmachine "code"

Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

#define N_REGISTERS     16

#define ADD  0x01
#define SUB  0x02
#define MUL  0x03
#define DIV  0x04
#define AND  0x05
#define OR   0x06
#define XOR  0x07
#define MOD  0x08
#define BITWISE_L 0x09
#define BITWISE_R 0x0A
#define NOT  0x0B

#define PRNT_I 0x20
#define PRNT_C 0x21
#define GET_I  0x22

enum {
    ERR_INPUT = 1,
    ERR_EXPR,
    ERR_REG,
    ERR_LAST
};

struct s_cpu {
    long int        reg[N_REGISTERS];
    long int        size;      
    unsigned        ins;        
    unsigned char   *code;
};

typedef struct s_cpu    CPU;

long int getfilesize(FILE *file)
{
    long int size;

    fseek(file, 0, SEEK_END);
    size = ftell(file);

    rewind(file);

    return size;
}

CPU *cpu_create(unsigned char *code, unsigned size)  {
    CPU     *cpu = malloc(sizeof(CPU));

    if(!cpu)
        return NULL;

    cpu->ins = 0;
    cpu->size = size;
    cpu->code = code;

    for(int i = 0; i < N_REGISTERS; ++i)
        cpu->reg[i] = 0;

    return cpu;
}

void cpu_destroy(CPU *cpu) {
    if(cpu)
        free(cpu);
}

long get_input(int *err) {
    long    res = 0;
    char    str[16] = {'\0'};
    
    fgets(str, 16, stdin);

    if(isalpha(str[0]) && str[1] == '\n')
        res = (int)str[0];
    else {
        for(unsigned i = 0; i < strlen(str) - 1 && !*err; ++i)
            if(!isdigit(str[i])) {
                *err = ERR_INPUT;
                break;
             }
        if(!*err)
            res = atol(str);
    }
    return res;
}

void handle_err(int err, unsigned instruction) {
    static const char *err_msg[] = {
        "Keyboard input is incorrect",
        "Incorrect expression",
        "Inexistant register"
    };
 
    if(err > 0 && err < ERR_LAST) {
        fprintf(stderr, "Instruction %u\n", instruction);
        fprintf(stderr, "%s\n", err_msg[err - 1]);
    }
}

void cpu_run(CPU *cpu) {
    if(!cpu)
        return;
    
    int err = 0;

    cpu->ins = 0;

    while(cpu->ins < cpu->size && !err) {
        switch(cpu->code[cpu->ins]) {
            #define OPERATION(instruction, op) \
            case instruction: {\
                ++cpu->ins; \
                if(cpu->ins + 2 < cpu->size) {\
                    if(cpu->code[cpu->ins] > N_REGISTERS) \
                        err = ERR_REG; \
                    cpu->reg[cpu->code[cpu->ins]] = cpu->code[cpu->ins + 1] op cpu->code[cpu->ins + 2]; \
                    cpu->ins += 2; \
                } \
                else \
                    err = ERR_EXPR;\
                break; \
            }

            OPERATION(ADD, +)
            OPERATION(SUB, -)
            OPERATION(MUL, *)
            OPERATION(DIV, /)
            OPERATION(AND, &)
            OPERATION(OR,  |)
            OPERATION(XOR, ^)
            OPERATION(MOD, %)
            OPERATION(BITWISE_L, <<)
            OPERATION(BITWISE_R, >>)
            
            #undef OPERATION
            case NOT:
                ++cpu->ins;
                if(cpu->ins + 1 <= cpu->size) {
                    cpu->reg[cpu->code[cpu->ins]] = ~cpu->code[cpu->ins + 1];
                    ++cpu->ins;
                }
            case PRNT_I:
                ++cpu->ins;
                printf("Register[%u] = %ld\n", cpu->code[cpu->ins], cpu->reg[cpu->code[cpu->ins]]);
                break;
            case PRNT_C:
                ++cpu->ins;
                printf("Register[%u] = %c\n", cpu->code[cpu->ins], (isalnum(cpu->reg[cpu->code[cpu->ins]]))?(char)cpu->reg[cpu->code[cpu->ins]]:' ');
                break;
            case GET_I:
                ++cpu->ins;
                printf("? ");                
                    
                cpu->reg[cpu->code[cpu->ins]] = get_input(&err);
                break;
        }
        handle_err(err, cpu->ins);
        ++cpu->ins; 
    }
        
}

int main(int argc, char *argv[]) {
    (void)argc;
    
    unsigned char *code = NULL;
    long int  size;

    if(argv[1] == NULL)
        return -1;

    CPU     *cpu;
    FILE    *file = fopen(argv[1], "rb");

    if(!file) {
        perror("fopen");
        return -1;
    }

    printf("File loaded\n");

    size = getfilesize(file);
    code = malloc(size);

    fread(code, sizeof(unsigned), size, file);
    fclose(file);

    cpu = cpu_create(code, size);

    if(!cpu)
        return -1;

    cpu_run(cpu);
    cpu_destroy(cpu);

    free(code);

    return 0;
}