TOYVM - Una pequeña y simple máquina virtual en C - Seguimiento -- ampo con assembly campo con virtual-machine camp codereview Relacionados El problema

ToyVM - a small and simple virtual machine in C - follow-up


11
vote

problema

Español

(ver la anterior y iteración inicial )

Ahora he refactorado el código para ajustarse a las sugerencias hechas en las respuestas a la primera iteración. Tengo esto:

toyvm.h :

  #ifndef TOYVM_H #define TOYVM_H  #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include <string.h>  enum {     /* Arithmetics */     ADD = 0x01,     NEG = 0x02,     MUL = 0x03,     DIV = 0x04,     MOD = 0x05,      /* Conditionals */     CMP = 0x10,     JA  = 0x11,     JE  = 0x12,     JB  = 0x13,     JMP = 0x14,      /* Subroutines */     CALL = 0x20,     RET  = 0x21,      /* Moving data */     LOAD  = 0x30,     STORE = 0x31,     CONST = 0x32,      /* Auxiliary */     HALT = 0x40,     INT  = 0x41,     NOP  = 0x42,      /* Stack */     PUSH     = 0x50,     PUSH_ALL = 0x51,     POP      = 0x52,     POP_ALL  = 0x53,     LSP      = 0x54,      /* Registers */     REG1 = 0x00,     REG2 = 0x01,     REG3 = 0x02,     REG4 = 0x03,      /* Interupts */     INTERRUPT_PRINT_INTEGER = 0x01,     INTERRUPT_PRINT_STRING  = 0x02,      /* Miscellaneous */     N_REGISTERS = 4,      OPCODE_MAP_SIZE = 256, };  typedef struct VM_CPU {     int32_t registers[N_REGISTERS];     int32_t program_counter;     int32_t stack_pointer;      struct {         uint8_t BAD_INSTRUCTION        : 1;         uint8_t STACK_UNDERFLOW        : 1;         uint8_t STACK_OVERFLOW         : 1;         uint8_t INVALID_REGISTER_INDEX : 1;         uint8_t BAD_ACCESS             : 1;         uint8_t COMPARISON_BELOW       : 1;         uint8_t COMPARISON_EQUAL       : 1;         uint8_t COMPARISON_ABOVE       : 1;     } status; } VM_CPU;  typedef struct TOYVM {     uint8_t* memory;     int32_t  memory_size;     int32_t  stack_limit;     VM_CPU   cpu;     size_t   opcode_map[OPCODE_MAP_SIZE]; } TOYVM;  /******************************************************************************* * Initializes the virtual machine with RAM memory of length 'memory_size' and  * * the stack fence at 'stack_limit'. *******************************************************************************/ void InitializeVM(TOYVM* vm, int32_t memory_size, int32_t stack_limit);  /******************************************************************************* * Writes 'size' bytes to the memory of the machine. The write begins from the  * * beginning of the memory tape.                                                * *******************************************************************************/ void WriteVMMemory(TOYVM* vm, uint8_t* mem, size_t size);  /******************************************************************************* * Writes a single word 'value' (32-bit signed integer) at address 'address'.   * *******************************************************************************/ void WriteWord(TOYVM* vm, int32_t address, int32_t value);  /******************************************************************************* * Prints the status of the machine to stdout.                                  * *******************************************************************************/ void PrintStatus(TOYVM* vm);  /******************************************************************************* * Runs the virtual machine.                                                    * *******************************************************************************/ void RunVM(TOYVM* vm);  #endif /* TOYVM_H */   

toyvm.c :

  #include "toyvm.h" #include <stdbool.h> #include <stdio.h> #include <string.h>  typedef struct instruction {     uint8_t   opcode;     size_t    size;     bool    (*execute)(TOYVM*); } instruction;  /******************************************************************************* * Return 'true' if the stack is empty.                                         * *******************************************************************************/ static bool StackIsEmpty(TOYVM* vm) {     return vm->cpu.stack_pointer >= vm->memory_size; }  /******************************************************************************* * Return 'true' if the stack is full.                                          * *******************************************************************************/ static bool StackIsFull(TOYVM* vm) {     return vm->cpu.stack_pointer <= vm->stack_limit; }  /******************************************************************************* * Returns the amount of free space in the stack in bytes.                      * *******************************************************************************/ static int32_t GetAvailableStackSize(TOYVM* vm) {     return vm->cpu.stack_pointer - vm->stack_limit; }  /******************************************************************************* * Returns the number of bytes occupied by the stack.                           * *******************************************************************************/ static int32_t GetOccupiedStackSize(TOYVM* vm) {     return vm->memory_size - vm->cpu.stack_pointer; }  /******************************************************************************* * Returns 'true' if the stack has enough room for pushing all registers to it. * *******************************************************************************/ static bool CanPerformMultipush(TOYVM* vm) {     return GetAvailableStackSize(vm) >= sizeof(int32_t) * N_REGISTERS; }  /******************************************************************************* * Returns 'true' if the stack can provide data for all registers.              * *******************************************************************************/ static bool CanPerformMultipop(TOYVM* vm) {     return GetOccupiedStackSize(vm) >= sizeof(int32_t) * N_REGISTERS; }  /******************************************************************************* * Returns 'true' if the instructoin does not run over the memory.              * *******************************************************************************/ static bool InstructionFitsInMemory(TOYVM* vm, uint8_t opcode);  /******************************************************************************* * Returns the length of the instruction with opcode 'opcode'.                  * *******************************************************************************/ static size_t GetInstructionLength(TOYVM* vm, uint8_t opcode);  void InitializeVM(TOYVM* vm, int32_t memory_size, int32_t stack_limit) {     /* Make sure both 'memory_size' and 'stack_limit' are divisible by 4. */     memory_size     += sizeof(int32_t) - (memory_size % sizeof(int32_t));      stack_limit     += sizeof(int32_t) - (stack_limit % sizeof(int32_t));      vm->memory              = calloc(memory_size, sizeof(uint8_t));     vm->memory_size         = memory_size;     vm->stack_limit         = stack_limit;     vm->cpu.program_counter = 0;     vm->cpu.stack_pointer   = (int32_t) memory_size;      /***************************************************************************     * Zero out all status flags.                                               *     ***************************************************************************/     vm->cpu.status.BAD_ACCESS       = 0;     vm->cpu.status.COMPARISON_ABOVE = 0;     vm->cpu.status.COMPARISON_EQUAL = 0;     vm->cpu.status.COMPARISON_BELOW = 0;      vm->cpu.status.BAD_INSTRUCTION        = 0;     vm->cpu.status.INVALID_REGISTER_INDEX = 0;     vm->cpu.status.STACK_OVERFLOW         = 0;     vm->cpu.status.STACK_UNDERFLOW        = 0;      /***************************************************************************     * Zero out the registers and the map mapping opcodes to their respective   *     * instruction descriptors.                                                 *     ***************************************************************************/     memset(vm->cpu.registers, 0, sizeof(int32_t) * N_REGISTERS);     memset(vm->opcode_map, 0, sizeof(vm->opcode_map));      /***************************************************************************     * Build the opcode map.                                                    *     ***************************************************************************/     vm->opcode_map[ADD] = 1;     vm->opcode_map[NEG] = 2;     vm->opcode_map[MUL] = 3;     vm->opcode_map[DIV] = 4;     vm->opcode_map[MOD] = 5;      vm->opcode_map[CMP] = 6;     vm->opcode_map[JA]  = 7;     vm->opcode_map[JE]  = 8;     vm->opcode_map[JB]  = 9;     vm->opcode_map[JMP] = 10;      vm->opcode_map[CALL] = 11;     vm->opcode_map[RET]  = 12;      vm->opcode_map[LOAD]  = 13;     vm->opcode_map[STORE] = 14;     vm->opcode_map[CONST] = 15;      vm->opcode_map[HALT] = 16;     vm->opcode_map[INT]  = 17;     vm->opcode_map[NOP]  = 18;      vm->opcode_map[PUSH]     = 19;     vm->opcode_map[PUSH_ALL] = 20;     vm->opcode_map[POP]      = 21;     vm->opcode_map[POP_ALL]  = 22;     vm->opcode_map[LSP]      = 23;  }  void WriteVMMemory(TOYVM* vm, uint8_t* mem, size_t size) {     memcpy(mem, vm->memory, size); }  static int32_t ReadWord(TOYVM* vm, int32_t address) {     uint8_t b1 = vm->memory[address];     uint8_t b2 = vm->memory[address + 1];     uint8_t b3 = vm->memory[address + 2];     uint8_t b4 = vm->memory[address + 3];      /* ToyVM is little-endian. */     return (int32_t)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); }  void WriteWord(TOYVM* vm, int32_t address, int32_t value) {     uint8_t b1 =  value & 0xff;     uint8_t b2 = (value & 0xff00) >> 8;     uint8_t b3 = (value & 0xff0000) >> 16;     uint8_t b4 = (value & 0xff000000) >> 24;      vm->memory[address]     = b1;     vm->memory[address + 1] = b2;     vm->memory[address + 2] = b3;     vm->memory[address + 3] = b4; }  static uint8_t ReadByte(TOYVM* vm, size_t address) {     return vm->memory[address]; }  /******************************************************************************* * Pops a single word from the stack. Used by some instructions that implicitly * * operate on stack.                                                            * *******************************************************************************/ static int32_t PopVM(TOYVM* vm) {     if (StackIsEmpty(vm))     {         vm->cpu.status.BAD_ACCESS = 1;         return 0;     }      int32_t word = ReadWord(vm, vm->cpu.stack_pointer);     vm->cpu.stack_pointer += 4;     return word; }  /******************************************************************************* * Pushes a single word to the stack. Used by some instructions. Used           * * implicitly by some instructions.                                             * *******************************************************************************/ static void PushVM(TOYVM* vm, uint32_t value) {     WriteWord(vm, vm->cpu.stack_pointer -= 4, value); }  static bool IsValidRegisterIndex(uint8_t byte) {     switch (byte)     {         case REG1:         case REG2:         case REG3:         case REG4:             return true;     }      return false; }  static int32_t GetProgramCounter(TOYVM* vm) {     return vm->cpu.program_counter; }  static bool ExecuteAdd(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, ADD))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index]     += vm->cpu.registers[source_register_index];      /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, ADD);     return false; }  static bool ExecuteNeg(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, NEG))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[register_index] = -vm->cpu.registers[register_index];     vm->cpu.program_counter += GetInstructionLength(vm, NEG);     return false; }  static bool ExecuteMul(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, MUL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index] *=     vm->cpu.registers[source_register_index];     /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, MUL);     return false; }  static bool ExecuteDiv(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, DIV))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index] /=     vm->cpu.registers[source_register_index];     /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, DIV);     return false; }  static bool ExecuteMod(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, MOD))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index] =     vm->cpu.registers[source_register_index] %     vm->cpu.registers[target_register_index];      /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, MOD);     return false; }  static bool ExecuteCmp(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, CMP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index_1 = ReadByte(vm, GetProgramCounter(vm) + 1);     uint8_t register_index_2 = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(register_index_1) ||         !IsValidRegisterIndex(register_index_2))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      int32_t register_1 = vm->cpu.registers[register_index_1];     int32_t register_2 = vm->cpu.registers[register_index_2];      if (register_1 < register_2)     {         vm->cpu.status.COMPARISON_ABOVE = 0;         vm->cpu.status.COMPARISON_EQUAL = 0;         vm->cpu.status.COMPARISON_BELOW = 1;     }     else if (register_1 > register_2)     {         vm->cpu.status.COMPARISON_ABOVE = 1;         vm->cpu.status.COMPARISON_EQUAL = 0;         vm->cpu.status.COMPARISON_BELOW = 0;     }     else     {         vm->cpu.status.COMPARISON_ABOVE = 0;         vm->cpu.status.COMPARISON_EQUAL = 1;         vm->cpu.status.COMPARISON_BELOW = 0;     }      vm->cpu.program_counter += GetInstructionLength(vm, CMP);     return false; }  static bool ExecuteJumpIfAbove(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JA))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (vm->cpu.status.COMPARISON_ABOVE)     {         vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     }     else     {         vm->cpu.program_counter += GetInstructionLength(vm, JA);     }      return false; }  static bool ExecuteJumpIfEqual(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JE))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (vm->cpu.status.COMPARISON_EQUAL)     {         vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     }     else     {         vm->cpu.program_counter += GetInstructionLength(vm, JE);     }      return false; }  static bool ExecuteJumpIfBelow(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JB))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (vm->cpu.status.COMPARISON_BELOW)     {         vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     }     else     {         vm->cpu.program_counter += GetInstructionLength(vm, JB);     }      return false; }  static bool ExecuteJump(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JMP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     return false; }  static bool ExecuteCall(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, CALL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (GetAvailableStackSize(vm) < 4)     {         vm->cpu.status.STACK_OVERFLOW = 1;         return true;     }      /* Save the return address on the stack. */     uint32_t address = ReadWord(vm, GetProgramCounter(vm) + 1);     PushVM(vm, (uint32_t)(GetProgramCounter(vm) +                           GetInstructionLength(vm, CALL)));     /* Actual jump to the subroutine. */     vm->cpu.program_counter = address;     return false; }  static bool ExecuteRet(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, RET))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (StackIsEmpty(vm))     {         vm->cpu.status.STACK_UNDERFLOW = 1;         return true;     }      vm->cpu.program_counter = PopVM(vm);     return false; }  static bool ExecuteLoad(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, LOAD))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      uint32_t address = ReadWord(vm, GetProgramCounter(vm) + 2);     vm->cpu.registers[register_index] = ReadWord(vm, address);     vm->cpu.program_counter += GetInstructionLength(vm, LOAD);     return false; }  static bool ExecuteStore(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, STORE))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      uint32_t address = ReadWord(vm, GetProgramCounter(vm) + 2);     WriteWord(vm, address, vm->cpu.registers[register_index]);     vm->cpu.program_counter += GetInstructionLength(vm, STORE);     return false; }  static bool ExecuteConst(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, CONST))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     int32_t datum = ReadWord(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[register_index] = datum;     vm->cpu.program_counter += GetInstructionLength(vm, CONST);     return false; }  static void PrintString(TOYVM* vm, uint32_t address) {     printf("%s", (const char*)(&vm->memory[address])); }  static bool ExecuteInterrupt(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, INT))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t interrupt_number = ReadByte(vm, GetProgramCounter(vm) + 1);      if (StackIsEmpty(vm))     {         vm->cpu.status.STACK_UNDERFLOW = 1;         return true;     }      switch (interrupt_number)     {         case INTERRUPT_PRINT_INTEGER:             printf("%d", PopVM(vm));             break;          case INTERRUPT_PRINT_STRING:             PrintString(vm, PopVM(vm));             break;          default:             return true;     }      vm->cpu.program_counter += GetInstructionLength(vm, INT);     return false; }  static bool ExecutePush(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, PUSH))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (StackIsFull(vm))     {         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      WriteWord(vm,               vm->cpu.stack_pointer - 4,               vm->cpu.registers[register_index]);      vm->cpu.stack_pointer -= 4;     vm->cpu.program_counter += GetInstructionLength(vm, PUSH);     return false; }  static bool ExecutePushAll(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, PUSH_ALL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (!CanPerformMultipush(vm))     {         vm->cpu.status.STACK_OVERFLOW = 1;         return true;     }      WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG1]);     WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG2]);     WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG3]);     WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG4]);     vm->cpu.program_counter += GetInstructionLength(vm, PUSH_ALL);     return false; }  static bool ExecutePop(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, POP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (StackIsEmpty(vm))     {         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      int32_t datum = ReadWord(vm, GetProgramCounter(vm) + 2);     vm->cpu.registers[register_index] = datum;     vm->cpu.stack_pointer += 4;     vm->cpu.program_counter += GetInstructionLength(vm, POP);     return false; }  static bool ExecutePopAll(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, POP_ALL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (!CanPerformMultipop(vm))     {         vm->cpu.status.STACK_UNDERFLOW = 1;         return true;     }      vm->cpu.registers[REG4] = ReadWord(vm, vm->cpu.stack_pointer);     vm->cpu.registers[REG3] = ReadWord(vm, vm->cpu.stack_pointer + 4);     vm->cpu.registers[REG2] = ReadWord(vm, vm->cpu.stack_pointer + 8);     vm->cpu.registers[REG1] = ReadWord(vm, vm->cpu.stack_pointer + 12);     vm->cpu.stack_pointer += 16;     vm->cpu.program_counter += GetInstructionLength(vm, POP_ALL);     return false; }  static bool ExecuteLSP(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, LSP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[register_index] = vm->cpu.stack_pointer;     vm->cpu.program_counter += GetInstructionLength(vm, LSP);     return false; }  static bool ExecuteNop(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, NOP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      vm->cpu.program_counter += GetInstructionLength(vm, NOP);     return false; }  static bool ExecuteHalt(TOYVM* vm) {     return true; }  void PrintStatus(TOYVM* vm) {     printf("BAD_INSTRUCTION       : %d ", vm->cpu.status.BAD_INSTRUCTION);     printf("STACK_UNDERFLOW       : %d ", vm->cpu.status.STACK_UNDERFLOW);     printf("STACK_OVERFLOW        : %d ", vm->cpu.status.STACK_OVERFLOW);     printf("INVALID_REGISTER_INDEX: %d ",            vm->cpu.status.INVALID_REGISTER_INDEX);      printf("BAD_ACCESS            : %d ", vm->cpu.status.BAD_ACCESS);     printf("COMPARISON_ABOVE      : %d ", vm->cpu.status.COMPARISON_ABOVE);     printf("COMPARISON_EQUAL      : %d ", vm->cpu.status.COMPARISON_EQUAL);     printf("COMPARISON_BELOW      : %d ", vm->cpu.status.COMPARISON_BELOW); }  const instruction instructions[] = {     { 0,        0, NULL       },     { ADD,      3, ExecuteAdd },     { NEG,      2, ExecuteNeg },     { MUL,      3, ExecuteMul },     { DIV,      3, ExecuteDiv },     { MOD,      3, ExecuteMod },      { CMP,      3, ExecuteCmp },     { JA,       5, ExecuteJumpIfAbove },     { JE,       5, ExecuteJumpIfEqual },     { JB,       5, ExecuteJumpIfBelow },     { JMP,      5, ExecuteJump },      { CALL,     5, ExecuteCall },     { RET,      1, ExecuteRet },      { LOAD,     6, ExecuteLoad },     { STORE,    6, ExecuteStore },     { CONST,    6, ExecuteConst },      { HALT,     1, ExecuteHalt },     { INT,      2, ExecuteInterrupt },     { NOP,      1, ExecuteNop },      { PUSH,     2, ExecutePush },     { PUSH_ALL, 1, ExecutePushAll },     { POP,      2, ExecutePop },     { POP_ALL,  1, ExecutePopAll },     { LSP,      2, ExecuteLSP } };  static size_t GetInstructionLength(TOYVM* vm, uint8_t opcode) {     size_t index = vm->opcode_map[opcode];     return instructions[index].size; }  /*******************************************************************************  * Checks that an instruction fits entirely in the memory.                      *  *******************************************************************************/ static bool InstructionFitsInMemory(TOYVM* vm, uint8_t opcode) {     size_t instruction_length = GetInstructionLength(vm, opcode);     return vm->cpu.program_counter + instruction_length <= vm->memory_size; }  void RunVM(TOYVM* vm) {     while (true)     {         int32_t program_counter = GetProgramCounter(vm);          if (program_counter < 0 || program_counter >= vm->memory_size)         {             vm->cpu.status.BAD_ACCESS = 1;             return;         }          uint8_t opcode = vm->memory[program_counter];         size_t index = vm->opcode_map[opcode];          if (index == 0)         {             vm->cpu.status.BAD_INSTRUCTION = 1;             return;         }          bool (*opcode_exec)(TOYVM*) =         instructions[index].execute;          if (opcode_exec(vm))         {             return;         }     } }   

main.c :

  #include <stdio.h> #include "toyvm.h"  static size_t getFileSize(FILE* file) {     long int original_cursor = ftell(file);     fseek(file, 0L, SEEK_END);     size_t size = ftell(file);     fseek(file, original_cursor, SEEK_SET);     return size; }  int main(int argc, const char * argv[]) {     if (argc != 2)     {         puts("Usage: toy FILE.brick ");         return 0;     }      FILE* file = fopen(argv[1], "r");     size_t file_size = getFileSize(file);      TOYVM vm;     InitializeVM(&vm, 10000, 5000);      fread(vm.memory, 1, file_size, file);     fclose(file);      RunVM(&vm);      if (vm.cpu.status.BAD_ACCESS         || vm.cpu.status.BAD_INSTRUCTION         || vm.cpu.status.INVALID_REGISTER_INDEX         || vm.cpu.status.STACK_OVERFLOW         || vm.cpu.status.STACK_UNDERFLOW)     {         PrintStatus(&vm);     } }   

Además, puedes encontrar el programa FIZZBBUZZ PARA TOYVM aquí ; Ejecutar como M0 .

Por favor, dime algo que viene a la mente.

Original en ingles

(See the previous and initial iteration)

Now I have refactored the code in order to conform to the suggestions made in the answers to the first iteration. I have this:

toyvm.h:

#ifndef TOYVM_H #define TOYVM_H  #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include <string.h>  enum {     /* Arithmetics */     ADD = 0x01,     NEG = 0x02,     MUL = 0x03,     DIV = 0x04,     MOD = 0x05,      /* Conditionals */     CMP = 0x10,     JA  = 0x11,     JE  = 0x12,     JB  = 0x13,     JMP = 0x14,      /* Subroutines */     CALL = 0x20,     RET  = 0x21,      /* Moving data */     LOAD  = 0x30,     STORE = 0x31,     CONST = 0x32,      /* Auxiliary */     HALT = 0x40,     INT  = 0x41,     NOP  = 0x42,      /* Stack */     PUSH     = 0x50,     PUSH_ALL = 0x51,     POP      = 0x52,     POP_ALL  = 0x53,     LSP      = 0x54,      /* Registers */     REG1 = 0x00,     REG2 = 0x01,     REG3 = 0x02,     REG4 = 0x03,      /* Interupts */     INTERRUPT_PRINT_INTEGER = 0x01,     INTERRUPT_PRINT_STRING  = 0x02,      /* Miscellaneous */     N_REGISTERS = 4,      OPCODE_MAP_SIZE = 256, };  typedef struct VM_CPU {     int32_t registers[N_REGISTERS];     int32_t program_counter;     int32_t stack_pointer;      struct {         uint8_t BAD_INSTRUCTION        : 1;         uint8_t STACK_UNDERFLOW        : 1;         uint8_t STACK_OVERFLOW         : 1;         uint8_t INVALID_REGISTER_INDEX : 1;         uint8_t BAD_ACCESS             : 1;         uint8_t COMPARISON_BELOW       : 1;         uint8_t COMPARISON_EQUAL       : 1;         uint8_t COMPARISON_ABOVE       : 1;     } status; } VM_CPU;  typedef struct TOYVM {     uint8_t* memory;     int32_t  memory_size;     int32_t  stack_limit;     VM_CPU   cpu;     size_t   opcode_map[OPCODE_MAP_SIZE]; } TOYVM;  /******************************************************************************* * Initializes the virtual machine with RAM memory of length 'memory_size' and  * * the stack fence at 'stack_limit'. *******************************************************************************/ void InitializeVM(TOYVM* vm, int32_t memory_size, int32_t stack_limit);  /******************************************************************************* * Writes 'size' bytes to the memory of the machine. The write begins from the  * * beginning of the memory tape.                                                * *******************************************************************************/ void WriteVMMemory(TOYVM* vm, uint8_t* mem, size_t size);  /******************************************************************************* * Writes a single word 'value' (32-bit signed integer) at address 'address'.   * *******************************************************************************/ void WriteWord(TOYVM* vm, int32_t address, int32_t value);  /******************************************************************************* * Prints the status of the machine to stdout.                                  * *******************************************************************************/ void PrintStatus(TOYVM* vm);  /******************************************************************************* * Runs the virtual machine.                                                    * *******************************************************************************/ void RunVM(TOYVM* vm);  #endif /* TOYVM_H */ 

toyvm.c:

#include "toyvm.h" #include <stdbool.h> #include <stdio.h> #include <string.h>  typedef struct instruction {     uint8_t   opcode;     size_t    size;     bool    (*execute)(TOYVM*); } instruction;  /******************************************************************************* * Return 'true' if the stack is empty.                                         * *******************************************************************************/ static bool StackIsEmpty(TOYVM* vm) {     return vm->cpu.stack_pointer >= vm->memory_size; }  /******************************************************************************* * Return 'true' if the stack is full.                                          * *******************************************************************************/ static bool StackIsFull(TOYVM* vm) {     return vm->cpu.stack_pointer <= vm->stack_limit; }  /******************************************************************************* * Returns the amount of free space in the stack in bytes.                      * *******************************************************************************/ static int32_t GetAvailableStackSize(TOYVM* vm) {     return vm->cpu.stack_pointer - vm->stack_limit; }  /******************************************************************************* * Returns the number of bytes occupied by the stack.                           * *******************************************************************************/ static int32_t GetOccupiedStackSize(TOYVM* vm) {     return vm->memory_size - vm->cpu.stack_pointer; }  /******************************************************************************* * Returns 'true' if the stack has enough room for pushing all registers to it. * *******************************************************************************/ static bool CanPerformMultipush(TOYVM* vm) {     return GetAvailableStackSize(vm) >= sizeof(int32_t) * N_REGISTERS; }  /******************************************************************************* * Returns 'true' if the stack can provide data for all registers.              * *******************************************************************************/ static bool CanPerformMultipop(TOYVM* vm) {     return GetOccupiedStackSize(vm) >= sizeof(int32_t) * N_REGISTERS; }  /******************************************************************************* * Returns 'true' if the instructoin does not run over the memory.              * *******************************************************************************/ static bool InstructionFitsInMemory(TOYVM* vm, uint8_t opcode);  /******************************************************************************* * Returns the length of the instruction with opcode 'opcode'.                  * *******************************************************************************/ static size_t GetInstructionLength(TOYVM* vm, uint8_t opcode);  void InitializeVM(TOYVM* vm, int32_t memory_size, int32_t stack_limit) {     /* Make sure both 'memory_size' and 'stack_limit' are divisible by 4. */     memory_size     += sizeof(int32_t) - (memory_size % sizeof(int32_t));      stack_limit     += sizeof(int32_t) - (stack_limit % sizeof(int32_t));      vm->memory              = calloc(memory_size, sizeof(uint8_t));     vm->memory_size         = memory_size;     vm->stack_limit         = stack_limit;     vm->cpu.program_counter = 0;     vm->cpu.stack_pointer   = (int32_t) memory_size;      /***************************************************************************     * Zero out all status flags.                                               *     ***************************************************************************/     vm->cpu.status.BAD_ACCESS       = 0;     vm->cpu.status.COMPARISON_ABOVE = 0;     vm->cpu.status.COMPARISON_EQUAL = 0;     vm->cpu.status.COMPARISON_BELOW = 0;      vm->cpu.status.BAD_INSTRUCTION        = 0;     vm->cpu.status.INVALID_REGISTER_INDEX = 0;     vm->cpu.status.STACK_OVERFLOW         = 0;     vm->cpu.status.STACK_UNDERFLOW        = 0;      /***************************************************************************     * Zero out the registers and the map mapping opcodes to their respective   *     * instruction descriptors.                                                 *     ***************************************************************************/     memset(vm->cpu.registers, 0, sizeof(int32_t) * N_REGISTERS);     memset(vm->opcode_map, 0, sizeof(vm->opcode_map));      /***************************************************************************     * Build the opcode map.                                                    *     ***************************************************************************/     vm->opcode_map[ADD] = 1;     vm->opcode_map[NEG] = 2;     vm->opcode_map[MUL] = 3;     vm->opcode_map[DIV] = 4;     vm->opcode_map[MOD] = 5;      vm->opcode_map[CMP] = 6;     vm->opcode_map[JA]  = 7;     vm->opcode_map[JE]  = 8;     vm->opcode_map[JB]  = 9;     vm->opcode_map[JMP] = 10;      vm->opcode_map[CALL] = 11;     vm->opcode_map[RET]  = 12;      vm->opcode_map[LOAD]  = 13;     vm->opcode_map[STORE] = 14;     vm->opcode_map[CONST] = 15;      vm->opcode_map[HALT] = 16;     vm->opcode_map[INT]  = 17;     vm->opcode_map[NOP]  = 18;      vm->opcode_map[PUSH]     = 19;     vm->opcode_map[PUSH_ALL] = 20;     vm->opcode_map[POP]      = 21;     vm->opcode_map[POP_ALL]  = 22;     vm->opcode_map[LSP]      = 23;  }  void WriteVMMemory(TOYVM* vm, uint8_t* mem, size_t size) {     memcpy(mem, vm->memory, size); }  static int32_t ReadWord(TOYVM* vm, int32_t address) {     uint8_t b1 = vm->memory[address];     uint8_t b2 = vm->memory[address + 1];     uint8_t b3 = vm->memory[address + 2];     uint8_t b4 = vm->memory[address + 3];      /* ToyVM is little-endian. */     return (int32_t)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); }  void WriteWord(TOYVM* vm, int32_t address, int32_t value) {     uint8_t b1 =  value & 0xff;     uint8_t b2 = (value & 0xff00) >> 8;     uint8_t b3 = (value & 0xff0000) >> 16;     uint8_t b4 = (value & 0xff000000) >> 24;      vm->memory[address]     = b1;     vm->memory[address + 1] = b2;     vm->memory[address + 2] = b3;     vm->memory[address + 3] = b4; }  static uint8_t ReadByte(TOYVM* vm, size_t address) {     return vm->memory[address]; }  /******************************************************************************* * Pops a single word from the stack. Used by some instructions that implicitly * * operate on stack.                                                            * *******************************************************************************/ static int32_t PopVM(TOYVM* vm) {     if (StackIsEmpty(vm))     {         vm->cpu.status.BAD_ACCESS = 1;         return 0;     }      int32_t word = ReadWord(vm, vm->cpu.stack_pointer);     vm->cpu.stack_pointer += 4;     return word; }  /******************************************************************************* * Pushes a single word to the stack. Used by some instructions. Used           * * implicitly by some instructions.                                             * *******************************************************************************/ static void PushVM(TOYVM* vm, uint32_t value) {     WriteWord(vm, vm->cpu.stack_pointer -= 4, value); }  static bool IsValidRegisterIndex(uint8_t byte) {     switch (byte)     {         case REG1:         case REG2:         case REG3:         case REG4:             return true;     }      return false; }  static int32_t GetProgramCounter(TOYVM* vm) {     return vm->cpu.program_counter; }  static bool ExecuteAdd(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, ADD))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index]     += vm->cpu.registers[source_register_index];      /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, ADD);     return false; }  static bool ExecuteNeg(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, NEG))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[register_index] = -vm->cpu.registers[register_index];     vm->cpu.program_counter += GetInstructionLength(vm, NEG);     return false; }  static bool ExecuteMul(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, MUL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index] *=     vm->cpu.registers[source_register_index];     /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, MUL);     return false; }  static bool ExecuteDiv(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, DIV))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index] /=     vm->cpu.registers[source_register_index];     /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, DIV);     return false; }  static bool ExecuteMod(TOYVM* vm) {     uint8_t source_register_index;     uint8_t target_register_index;      if (!InstructionFitsInMemory(vm, MOD))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      source_register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     target_register_index = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(source_register_index) ||         !IsValidRegisterIndex(target_register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[target_register_index] =     vm->cpu.registers[source_register_index] %     vm->cpu.registers[target_register_index];      /* Advance the program counter past this instruction. */     vm->cpu.program_counter += GetInstructionLength(vm, MOD);     return false; }  static bool ExecuteCmp(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, CMP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index_1 = ReadByte(vm, GetProgramCounter(vm) + 1);     uint8_t register_index_2 = ReadByte(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(register_index_1) ||         !IsValidRegisterIndex(register_index_2))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      int32_t register_1 = vm->cpu.registers[register_index_1];     int32_t register_2 = vm->cpu.registers[register_index_2];      if (register_1 < register_2)     {         vm->cpu.status.COMPARISON_ABOVE = 0;         vm->cpu.status.COMPARISON_EQUAL = 0;         vm->cpu.status.COMPARISON_BELOW = 1;     }     else if (register_1 > register_2)     {         vm->cpu.status.COMPARISON_ABOVE = 1;         vm->cpu.status.COMPARISON_EQUAL = 0;         vm->cpu.status.COMPARISON_BELOW = 0;     }     else     {         vm->cpu.status.COMPARISON_ABOVE = 0;         vm->cpu.status.COMPARISON_EQUAL = 1;         vm->cpu.status.COMPARISON_BELOW = 0;     }      vm->cpu.program_counter += GetInstructionLength(vm, CMP);     return false; }  static bool ExecuteJumpIfAbove(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JA))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (vm->cpu.status.COMPARISON_ABOVE)     {         vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     }     else     {         vm->cpu.program_counter += GetInstructionLength(vm, JA);     }      return false; }  static bool ExecuteJumpIfEqual(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JE))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (vm->cpu.status.COMPARISON_EQUAL)     {         vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     }     else     {         vm->cpu.program_counter += GetInstructionLength(vm, JE);     }      return false; }  static bool ExecuteJumpIfBelow(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JB))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (vm->cpu.status.COMPARISON_BELOW)     {         vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     }     else     {         vm->cpu.program_counter += GetInstructionLength(vm, JB);     }      return false; }  static bool ExecuteJump(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, JMP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      vm->cpu.program_counter = ReadWord(vm, GetProgramCounter(vm) + 1);     return false; }  static bool ExecuteCall(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, CALL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (GetAvailableStackSize(vm) < 4)     {         vm->cpu.status.STACK_OVERFLOW = 1;         return true;     }      /* Save the return address on the stack. */     uint32_t address = ReadWord(vm, GetProgramCounter(vm) + 1);     PushVM(vm, (uint32_t)(GetProgramCounter(vm) +                           GetInstructionLength(vm, CALL)));     /* Actual jump to the subroutine. */     vm->cpu.program_counter = address;     return false; }  static bool ExecuteRet(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, RET))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (StackIsEmpty(vm))     {         vm->cpu.status.STACK_UNDERFLOW = 1;         return true;     }      vm->cpu.program_counter = PopVM(vm);     return false; }  static bool ExecuteLoad(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, LOAD))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      uint32_t address = ReadWord(vm, GetProgramCounter(vm) + 2);     vm->cpu.registers[register_index] = ReadWord(vm, address);     vm->cpu.program_counter += GetInstructionLength(vm, LOAD);     return false; }  static bool ExecuteStore(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, STORE))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      uint32_t address = ReadWord(vm, GetProgramCounter(vm) + 2);     WriteWord(vm, address, vm->cpu.registers[register_index]);     vm->cpu.program_counter += GetInstructionLength(vm, STORE);     return false; }  static bool ExecuteConst(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, CONST))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);     int32_t datum = ReadWord(vm, GetProgramCounter(vm) + 2);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[register_index] = datum;     vm->cpu.program_counter += GetInstructionLength(vm, CONST);     return false; }  static void PrintString(TOYVM* vm, uint32_t address) {     printf("%s", (const char*)(&vm->memory[address])); }  static bool ExecuteInterrupt(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, INT))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t interrupt_number = ReadByte(vm, GetProgramCounter(vm) + 1);      if (StackIsEmpty(vm))     {         vm->cpu.status.STACK_UNDERFLOW = 1;         return true;     }      switch (interrupt_number)     {         case INTERRUPT_PRINT_INTEGER:             printf("%d", PopVM(vm));             break;          case INTERRUPT_PRINT_STRING:             PrintString(vm, PopVM(vm));             break;          default:             return true;     }      vm->cpu.program_counter += GetInstructionLength(vm, INT);     return false; }  static bool ExecutePush(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, PUSH))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (StackIsFull(vm))     {         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      WriteWord(vm,               vm->cpu.stack_pointer - 4,               vm->cpu.registers[register_index]);      vm->cpu.stack_pointer -= 4;     vm->cpu.program_counter += GetInstructionLength(vm, PUSH);     return false; }  static bool ExecutePushAll(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, PUSH_ALL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (!CanPerformMultipush(vm))     {         vm->cpu.status.STACK_OVERFLOW = 1;         return true;     }      WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG1]);     WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG2]);     WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG3]);     WriteWord(vm, vm->cpu.stack_pointer -= 4, vm->cpu.registers[REG4]);     vm->cpu.program_counter += GetInstructionLength(vm, PUSH_ALL);     return false; }  static bool ExecutePop(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, POP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (StackIsEmpty(vm))     {         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      int32_t datum = ReadWord(vm, GetProgramCounter(vm) + 2);     vm->cpu.registers[register_index] = datum;     vm->cpu.stack_pointer += 4;     vm->cpu.program_counter += GetInstructionLength(vm, POP);     return false; }  static bool ExecutePopAll(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, POP_ALL))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      if (!CanPerformMultipop(vm))     {         vm->cpu.status.STACK_UNDERFLOW = 1;         return true;     }      vm->cpu.registers[REG4] = ReadWord(vm, vm->cpu.stack_pointer);     vm->cpu.registers[REG3] = ReadWord(vm, vm->cpu.stack_pointer + 4);     vm->cpu.registers[REG2] = ReadWord(vm, vm->cpu.stack_pointer + 8);     vm->cpu.registers[REG1] = ReadWord(vm, vm->cpu.stack_pointer + 12);     vm->cpu.stack_pointer += 16;     vm->cpu.program_counter += GetInstructionLength(vm, POP_ALL);     return false; }  static bool ExecuteLSP(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, LSP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      uint8_t register_index = ReadByte(vm, GetProgramCounter(vm) + 1);      if (!IsValidRegisterIndex(register_index))     {         vm->cpu.status.INVALID_REGISTER_INDEX = 1;         return true;     }      vm->cpu.registers[register_index] = vm->cpu.stack_pointer;     vm->cpu.program_counter += GetInstructionLength(vm, LSP);     return false; }  static bool ExecuteNop(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, NOP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      vm->cpu.program_counter += GetInstructionLength(vm, NOP);     return false; }  static bool ExecuteHalt(TOYVM* vm) {     return true; }  void PrintStatus(TOYVM* vm) {     printf("BAD_INSTRUCTION       : %d\n", vm->cpu.status.BAD_INSTRUCTION);     printf("STACK_UNDERFLOW       : %d\n", vm->cpu.status.STACK_UNDERFLOW);     printf("STACK_OVERFLOW        : %d\n", vm->cpu.status.STACK_OVERFLOW);     printf("INVALID_REGISTER_INDEX: %d\n",            vm->cpu.status.INVALID_REGISTER_INDEX);      printf("BAD_ACCESS            : %d\n", vm->cpu.status.BAD_ACCESS);     printf("COMPARISON_ABOVE      : %d\n", vm->cpu.status.COMPARISON_ABOVE);     printf("COMPARISON_EQUAL      : %d\n", vm->cpu.status.COMPARISON_EQUAL);     printf("COMPARISON_BELOW      : %d\n", vm->cpu.status.COMPARISON_BELOW); }  const instruction instructions[] = {     { 0,        0, NULL       },     { ADD,      3, ExecuteAdd },     { NEG,      2, ExecuteNeg },     { MUL,      3, ExecuteMul },     { DIV,      3, ExecuteDiv },     { MOD,      3, ExecuteMod },      { CMP,      3, ExecuteCmp },     { JA,       5, ExecuteJumpIfAbove },     { JE,       5, ExecuteJumpIfEqual },     { JB,       5, ExecuteJumpIfBelow },     { JMP,      5, ExecuteJump },      { CALL,     5, ExecuteCall },     { RET,      1, ExecuteRet },      { LOAD,     6, ExecuteLoad },     { STORE,    6, ExecuteStore },     { CONST,    6, ExecuteConst },      { HALT,     1, ExecuteHalt },     { INT,      2, ExecuteInterrupt },     { NOP,      1, ExecuteNop },      { PUSH,     2, ExecutePush },     { PUSH_ALL, 1, ExecutePushAll },     { POP,      2, ExecutePop },     { POP_ALL,  1, ExecutePopAll },     { LSP,      2, ExecuteLSP } };  static size_t GetInstructionLength(TOYVM* vm, uint8_t opcode) {     size_t index = vm->opcode_map[opcode];     return instructions[index].size; }  /*******************************************************************************  * Checks that an instruction fits entirely in the memory.                      *  *******************************************************************************/ static bool InstructionFitsInMemory(TOYVM* vm, uint8_t opcode) {     size_t instruction_length = GetInstructionLength(vm, opcode);     return vm->cpu.program_counter + instruction_length <= vm->memory_size; }  void RunVM(TOYVM* vm) {     while (true)     {         int32_t program_counter = GetProgramCounter(vm);          if (program_counter < 0 || program_counter >= vm->memory_size)         {             vm->cpu.status.BAD_ACCESS = 1;             return;         }          uint8_t opcode = vm->memory[program_counter];         size_t index = vm->opcode_map[opcode];          if (index == 0)         {             vm->cpu.status.BAD_INSTRUCTION = 1;             return;         }          bool (*opcode_exec)(TOYVM*) =         instructions[index].execute;          if (opcode_exec(vm))         {             return;         }     } } 

main.c:

#include <stdio.h> #include "toyvm.h"  static size_t getFileSize(FILE* file) {     long int original_cursor = ftell(file);     fseek(file, 0L, SEEK_END);     size_t size = ftell(file);     fseek(file, original_cursor, SEEK_SET);     return size; }  int main(int argc, const char * argv[]) {     if (argc != 2)     {         puts("Usage: toy FILE.brick\n");         return 0;     }      FILE* file = fopen(argv[1], "r");     size_t file_size = getFileSize(file);      TOYVM vm;     InitializeVM(&vm, 10000, 5000);      fread(vm.memory, 1, file_size, file);     fclose(file);      RunVM(&vm);      if (vm.cpu.status.BAD_ACCESS         || vm.cpu.status.BAD_INSTRUCTION         || vm.cpu.status.INVALID_REGISTER_INDEX         || vm.cpu.status.STACK_OVERFLOW         || vm.cpu.status.STACK_UNDERFLOW)     {         PrintStatus(&vm);     } } 

Also, you can find the FizzBuzz program for ToyVM here; run as toy fizzbuzz.brick.

Please, tell me anything that comes to mind.

        
     
     

Lista de respuestas

11
 
vote
vote
La mejor respuesta
 

Veo algunas cosas que pueden ayudarlo a mejorar su código.

No fugas la memoria

El código para InitializeVM Asignar memoria para el miembro vm->memory . Cada asignación debe tener un 99887777766555443322, por lo que sugeriría agregar esta función a la interfaz y luego llamarlo al final de main :

  void DestroyVM(TOYVM* vm) {     free(vm->memory); }   

Verifique los valores de devolución

La llamada a calloc puede fallar y cuando lo hace, la única indicación es que el valor devuelto es igual a NULL . Por esa razón, debe verificar el valor de un programa robusto. Lo mismo ocurre con otras funciones, como fopen .

Piense en firmado versus sin firmar

Hay varios valores en la máquina virtual, como program_counter y stack_pointer1 vm->memory0 que se declaran vm->memory1 < / Código> Tipos. Sin embargo, ¿realmente tiene sentido que alguno de estos sean números negativos? No sospecho que no, así que le aconsejaría hacerlos todos vm->memory2 en su lugar. Podría posiblemente, esto se aplicaría a los valores de retorno de vm->memory3 , 99887766555443314 , etc. También.

Devuelve algo útil de las funciones

El vm->memory5 FUNTO ES UNA FUNCIÓN DE 99887766555443316 En este momento, pero probablemente sería más útil tenerlo devolver vm->memory7 en el caso de Un error o vm->memory8 de lo contrario. Esto permitiría una simple comprobación de errores después de que se haya ejecutado VM.

Simplifique su código

El 99887776655443319 Actualmente tiene esta línea:

  free0  

Realmente no es necesario crear una variable separada aquí. Esto podría ser escrito como una sola línea:

  free1  

Consolidate Tablas de datos

La decodificación de OPODES utiliza dos estructuras; Uno es el free22 que está ordenado por el valor numérico de Opcode, y el otro es 99887766555443323 que es una matriz de índices en la matriz 99887766655443324 . No hay nada intrínsecamente malo con eso, pero como no parece que las instrucciones están destinadas a crear o asignadas dinámicamente por los usuarios del programa, estas estructuras podrían consolidarse en una sola estructura estática, o podría mantenerlas separadas pero estáticamente Cree la estructura free5 que la inicialice en tiempo de ejecución.

No te repitas

En casi todas las funciones free655443326, el código comienza con una verificación de memoria y finaliza actualizando el contador del programa. Por ejemplo, el free7 Instrucciones:

  free8  

Esta repetición podría ser factorizada:

  free9  

Luego, el main0 , la estructura podría tener un miembro booleano adicional 99887766555443331 , por supuesto, decir si la instrucción podría promover el contador del programa a otra que no sea la siguiente instrucción . Llamar se vería así:

  main2  

Separado main3 S

El main4 S EN main5 no son todos los valores mutuamente excluyentes para el mismo concepto que es un poco confuso. Recomendaría tener los valores de instrucciones como uno main6 y luego solo los valores relacionados con el grupo en otro código main7 s. Eso deja claro que cada main8 es una colección de algo en lugar de solo un surtido aleatorio de constantes.

 

I see some things that may help you improve your code.

Don't leak memory

The code for InitializeVM allocate memory for the vm->memory member. Every allocation should have a corresponding free, so I would suggest adding this function to the interface and then calling it at the end of main:

void DestroyVM(TOYVM* vm) {     free(vm->memory); } 

Check return values

The call to calloc can fail and when it does, the only indication is that the value returned is equal to NULL. For that reason, you must check the value for a robust program. The same is true of other functions such as fopen.

Think about signed versus unsigned

There are several values in the virtual machine, such as program_counter and stack_pointer and memory_size which are declared to be int32_t types. However, does it actually make sense for any of these to be negative numbers? I suspect not, so I would advise making them all uint32_t instead. Arguably, this would apply to the return values of GetAvailableStackSize(), GetOccupiedStackSize(), etc. as well.

Return something useful from functions

The RunVM() function is a void function right now, but it would probably be more useful to have it return true in the case of an error or false otherwise. This would allow for simple error checking after the VM has run.

Simplify your code

The RunVM() function currently has these line:

bool (*opcode_exec)(TOYVM*) = instructions[index].execute;  if (opcode_exec(vm))   

There isn't really a need to create a separate variable here. This could instead be written as a single line:

    if (instructions[index].execute(vm)) 

Consolidate data tables

The decoding of opcodes uses two structures; one is the instructions array which is ordered by opcode numerical value, and the other is opcode_map which is an array of indices into the instructions array. There's nothing inherently wrong with that, but since it does not appear that the instructions are intended to be dynamically created or assigned by users of the program, these structures could either be consolidated into a single static structure, or you could keep them separate but statically create the opcode_map structure rather that initializing it at runtime.

Don't Repeat Yourself

In almost every ExecuteXXX function, the code begins with a memory check and ends by updating the program counter. For example, the NOP instruction:

static bool ExecuteNop(TOYVM* vm) {     if (!InstructionFitsInMemory(vm, NOP))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }      vm->cpu.program_counter += GetInstructionLength(vm, NOP);     return false; } 

This repetition could be factored out:

static bool ExecuteWrapper(TOYVM* vm, uint8_t opcode, bool (*execute)(TOYVM*), bool isNotJump) {     if (!InstructionFitsInMemory(vm, opcode))     {         vm->cpu.status.BAD_ACCESS = 1;         return true;     }     if (execute(vm)) {         return true;     }     if (isNotJump) {         vm->cpu.program_counter += GetInstructionLength(vm, opcode);     }     return false; } 

Then the instruction struct could have an additional boolean member isNotJump which would, of course, say whether the instruction might advance the program counter to something other than the next instruction. Calling would then look like this:

bool retval = ExecuteWrapper(vm, opcode,          instructions[index].execute, instructions[index].isNotJump); 

Separate enums

The enums in toyvm.h are not all mutually exclusive values for the same concept which is a little confusing. I would recommend having the instruction values as one enum and then only group related values in other enums. That makes it clear that each enum is a collection of something rather than just a random assortment of constants.

 
 
         
         

Relacionados problema

7  6502 Emulator CPU en Java  ( 6502 cpu emulator in java ) 
Quiero asegurarme de que no tendré que volver y rehacer grandes trozos de código. Tengo cada opcode como un valor en un enumio que implementa Runnable . ¿Hay...

6  Clase de Bitfield y Registrarse Representación  ( Bitfield class and register depiction ) 
Durante unos meses, ahora he estado trabajando con una CPU del brazo. Para ser específico un brazo Cortex M3 de STM (STM32107VC). Ejemplo completo Hasta a...

8  Simulador simpletron en c  ( Simpletron simulator in c ) 
Implementé un simulador de simplón en C para aprender C. Simpletron es una máquina virtual inventada por DEISEL para sus libros. SimpleTron ejecuta programas...

7  Pequeña máquina virtual ejecutando su propio código de bytes  ( Small virtual machine executing its own byte code ) 
Te quiero presente en mi pequeña máquina virtual. No es realmente tan sofisticado. Es una máquina virtual solo con la excepción para algunas variables 'global...

2  Procesar una lista de instrucciones y salida el valor máximo alcanzado  ( Process a list of instructions and output the max value reached ) 
Entradas se ve así Node3 Me preocupa que necesito declarar Node4 como Node5 (pero no 99887776655443326 ). ¿Alguna otra recomendación en este script...

3  Máquina virtual en C para ejecutar c  ( Virtual machine in c to run c ) 
Básicamente otro seguimiento hasta esta revisión aquí pero yo ' Ve dispuesto a completar el proyecto de software. Los cambios de la revisión anterior es que...

3  Script PowerShell para ejecutar el mantenimiento de Docker, opcionalmente no interactivamente  ( Powershell script to execute docker maintenance optionally non interactively ) 
Problema Los desarrolladores en mi equipo son nuevos en Docker. Quería proporcionar una manera de realizar tareas generales de limpieza de Docker (como la p...

2  Intérprete para un lenguaje de ensamblaje con instrucciones variadas  ( Interpreter for an assembly language with variadic instructions ) 
Actualmente estoy implementando una máquina virtual que ejecuta un lenguaje de programación similar al ensamblaje. La diferencia es que una instrucción en es...

9  TOYVM: una pequeña y simple máquina virtual en la demostración de C + Fizzbuzz  ( Toyvm a small and simple virtual machine in c fizzbuzz demonstration ) 
(consulte también la siguiente iteración .) Tengo esta pequeña máquina virtual. lo que hay : pila y, por lo tanto, recursión. saltos condicionales ...

4  Emulador de CPU en Java  ( Cpu emulator in java ) 
Recientemente comencé un nuevo proyecto: un emulador de CPU en Java. El código base está escrito, ahora puedo comenzar a implementar cosas nuevas lentamente, ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos