// ----------------------------------------------------------------- // CS 39 Virtual Machine // virtualMachine.cc // Scott F. Kaplan -- sfkaplan@cs.amherst.edu // September 2000 // Modified August 2003 (switch from C++ to C-style output, // modified instruction set) // The code file, which contains all of the procedures and private // types and data for the virtual machine. // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set this value to "true" in order to have the virtual machine // generate debugging information. const bool debug = true; // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Standard C functions. #include // C string formatting functions, for parsing. #include // C string functions for copying and comparison. #include // Allow for sanity-checking at run time by asserting conditions that // should be true unless a problem has occurred. Assertions should be // used to detect *programming bugs* -- errors that arise from // incorrect computations. These are safeguards that can easily be // removed (via a compilation option) when you are confident that the // code for this class has been thoroughly tested, and the safeguards // aren't needed. #include // Allow for easy error-condition checking at run time. This kind of // error checking should be used to detect *external data mistakes* -- // errors that are caused by data provided by the user, by an input // file, or by some other module. These are safeguards that should // *never* be removed -- No matter how much the code is tested, nor // how certain you are that it is correct, these kinds of errors can // always arise if invalid values are provided from the outside. #include "abortIfFalse.hh" // The header file that describes the interface to the virtual // machine. #include "virtualMachine.hh" // Provide a declaration of the kernel booting function, which we must // assume exists. void bootKernel (char* executablePathnames); // Provide a declaration of the kernel trap handling functions so that // this code may jump to those functions. typedef void (*kernelTrapHandler_t) (); // ----------------------------------------------------------------- // ----------------------------------------------------------------- // A list of types used within the virtual machine only. // A structure that holds the needed information about a decoded // instruction. struct instruction_t { // The operation to be performed for this instruction. opcode_t opcode; // The operands (at most three) specified in the instruction. vMachineWord_t operands[3]; }; // struct instruction_t definition. // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Data declared only within this module, not for external use. // Values related to the machine instruction format, helpful for // parsing and managing the program counter. const unsigned int bytesPerInstruction = 32; const unsigned int bytesPerOpCode = 4; const unsigned int bytesPerOperand = 8; const unsigned int bytesBetweenElements = 1; // Pointers to the beginning and end of main memory. vMachineByte_t* memoryBegin; vMachineByte_t* memoryEnd; // The number of general-purpose registers available in this machine. const unsigned int numberGeneralRegisters = 32; // Names for the registers. const unsigned int register_zero = 0x0; const unsigned int register_sp = 0x1; const unsigned int register_fp = 0x2; const unsigned int register_ra = 0x3; const unsigned int register_s0 = 0x4; const unsigned int register_s1 = 0x5; const unsigned int register_s2 = 0x6; const unsigned int register_s3 = 0x7; const unsigned int register_a0 = 0x8; const unsigned int register_a1 = 0x9; const unsigned int register_a2 = 0xa; const unsigned int register_a3 = 0xb; const unsigned int register_a4 = 0xc; const unsigned int register_a5 = 0xd; const unsigned int register_a6 = 0xe; const unsigned int register_a7 = 0xf; const unsigned int register_t0 = 0x10; const unsigned int register_t1 = 0x11; const unsigned int register_t2 = 0x12; const unsigned int register_t3 = 0x13; const unsigned int register_t4 = 0x14; const unsigned int register_t5 = 0x15; const unsigned int register_t6 = 0x16; const unsigned int register_t7 = 0x17; const unsigned int register_g0 = 0x18; const unsigned int register_g1 = 0x19; const unsigned int register_g2 = 0x1a; const unsigned int register_g3 = 0x1b; const unsigned int register_g4 = 0x1c; const unsigned int register_g5 = 0x1d; const unsigned int register_g6 = 0x1e; const unsigned int register_g7 = 0x1f; // General purpose registers that can be put to nearly any use, // although the names above suggest stereotyped uses. The one // exception is the zero register, which is initially set to zero and // cannot be set to any other value. vMachineWord_t generalRegisters[numberGeneralRegisters]; // The special program counter register, which is an offset to the // current instruction in the executable image. vMachineWord_t pc; // The special trap base register, which contains the trap base // address -- AKA a pointer to the beginning of the trap table. vMachineWord_t tbr; // The processor cycle clock register. Assume one instruction per // cycle. vMachineDoubleWord_t clock; // The interrupt period register, storing the time at which a // clock-driven interrupt is sent by calling the interrupt handler set // by the supervisor. vMachineDoubleWord_t interruptPeriod; // The base and limit registers, which contain the offsets into main // memory that mark the beginning and just-beyond-the-end of the // memory for the current process. vMachineWord_t base; vMachineWord_t limit; // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Return a pointer to the first byte of the virtual machine's main // memory. We use a procedure here to make this data read-only. vMachineByte_t* getMemoryBegin () { return memoryBegin; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Return a pointer to the byte that *follows* the last byte of the // virtual machine's main memory. We use a procedure here to make // this data read-only. vMachineByte_t* getMemoryEnd () { return memoryEnd; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the contents of the given general register. vMachineWord_t getGeneralRegister (unsigned int registerNumber) { // Ensure that the register number is valid. assert(registerNumber < numberGeneralRegisters); // Return the machine word held in that register. return generalRegisters[registerNumber]; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the contents of the given general register. void setGeneralRegister (unsigned int registerNumber, vMachineWord_t newValue) { // Ensure that the register number is valid. assert(registerNumber < numberGeneralRegisters); // If the assignment is into the zero register, skip the assignment, // thus failing silently to perform the operation. if (registerNumber != register_zero) { // Assign the given value into the register. generalRegisters[registerNumber] = newValue; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the program counter. vMachineWord_t getPC () { return pc; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the program counter. void setPC (vMachineWord_t newPC) { pc = newPC; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the tranlsation lookaside buffer (TBR). vMachineWord_t getTBR () { return tbr; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the translation lookaside buffer (TBR). void setTBR (vMachineWord_t newTBR) { tbr = newTBR; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the current interrupt period register. vMachineWord_t getInterruptPeriod () { return interruptPeriod; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the current interrupt period. void setInterruptPeriod (vMachineWord_t newInterruptPeriod) { interruptPeriod = newInterruptPeriod; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the current clock. vMachineDoubleWord_t getClock () { return clock; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the current base register. vMachineWord_t getBase () { return base; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the current base register. void setBase (vMachineWord_t newBase) { base = newBase; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Get the current limit register. vMachineWord_t getLimit () { return limit; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the current limit register. void setLimit (vMachineWord_t newLimit) { limit = newLimit; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Vector into the kernel by calling on a particular trap handler. // The trap registers should already be set. void vectorIntoKernel (trap_t trap) { // Use the trap value to calculate the offset from the TBA to find // the correct entry in the TT. Note that this entry is a pointer // to a kernel function. kernelTrapHandler_t* tba = (kernelTrapHandler_t*)tbr; kernelTrapHandler_t ttEntry = tba[trap]; // If no trap handler has been installed for this kind of interrupt, // then abort. (This problem _could_ be handled more gracefully, // but the kernel really should install handlers for every possible // interrupt.) if (ttEntry == NULL) { fprintf(stderr, "VM: No handler for trap %d\n", trap); exit(1); } // Jump to that kernel function. ttEntry(); } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the appropriate actions for a system call trap. void doSystemCallTrap () { // There are no values expected in the trap registers for a system // call. NOTHING TO DO HERE. // Vector into the kernel's system call handler. vectorIntoKernel(SYSTEM_CALL_TRAP); } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the appropriate actions for an illegal memory access trap. void doMemoryAccessTrap (opcode_t opcode, vMachineByte_t* address) { // Store the offending opcode and memory location (within the // virtual machine's memory) into trap register. setGeneralRegister(register_s0, (vMachineWord_t)opcode); setGeneralRegister(register_s1, (vMachineWord_t)address); // Vector into the kernel's memory access handler. vectorIntoKernel(MEMORY_ACCESS_TRAP); } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the appropriate actions for a clock interrupt. void doClockInterruptTrap () { // There are no values expected in the trap registers for a clock // interrupt. NOTHING TO DO HERE. // Vector into the kernel's clock interrupt handler. vectorIntoKernel(CLOCK_INTERRUPT_TRAP); } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the appropriate actions for an illegal instruction (which // may be anything from a badly parsed instruction to a recognizable // instruction with invalid operands.) void doInvalidInstructionTrap () { // There are no valies expected in the trap registers for an invalid // instruction. NOTHING TO DO HERE. // Vector into the kernel's invalid instruction handler. vectorIntoKernel(INVALID_INSTRUCTION_TRAP); } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Given an address that is really an offset into the address space of // a process, check that the pointer is valid. Specifically, is that // address beyond either the limit of the process, or beyond the // capacity of main memory? bool checkAddressValidity (vMachineByte_t* address) { // Take the given address as an offset into the space belonging to // the current process. unsigned int offset = (unsigned int)address; // If base is invalid, as it exceeds the end of main memory, then // the machine is in an unmanageable state. abortIfFalse(memoryBegin + getBase() < memoryEnd, "Base register is invalid"); // If the limit is invalid, as it either it exceeds the end of main // memory, or it preceeds the base, then the machine is in an // unmanageable state. abortIfFalse((memoryBegin + getLimit() <= memoryEnd) && (getBase() < getLimit()), "Limit register is invalid"); // Is the offset invalid? (It can be invalid if it is beyond the // limit, represents a null pointer, or is not word-aligned.) // Assume, by default, that the address is valid. bool valid = true; if ((offset == VMACHINE_NULL) || (getBase() + offset >= getLimit()) || ((unsigned int)offset % sizeof(vMachineWord_t) != 0)) { // It is, so note that it is invalid. valid = false; } // Return whether or not the address was valid. return valid; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Given a pointer to the byte that begins an instruction, determine // (and return) the opcode for that instruction. opcode_t interpretOpCode (vMachineByte_t* rawOpCode) { // Case by case, determine which opcode is represented at the given // pointer. If a match is found, store that match so it can be // returned. opcode_t returnOpCode; if (strncmp(rawOpCode, "SYSC", bytesPerOpCode) == 0) { returnOpCode = SYSC; } else if (strncmp(rawOpCode, "STRI", bytesPerOpCode) == 0) { returnOpCode = STRI; } else if (strncmp(rawOpCode, "STRR", bytesPerOpCode) == 0) { returnOpCode = STRR; } else if (strncmp(rawOpCode, "LOAD", bytesPerOpCode) == 0) { returnOpCode = LOAD; } else if (strncmp(rawOpCode, "STOR", bytesPerOpCode) == 0) { returnOpCode = STOR; } else if (strncmp(rawOpCode, "ADDR", bytesPerOpCode) == 0) { returnOpCode = ADDR; } else if (strncmp(rawOpCode, "ADDI", bytesPerOpCode) == 0) { returnOpCode = ADDI; } else if (strncmp(rawOpCode, "SUBR", bytesPerOpCode) == 0) { returnOpCode = SUBR; } else if (strncmp(rawOpCode, "SUBI", bytesPerOpCode) == 0) { returnOpCode = SUBI; } else if (strncmp(rawOpCode, "MULR", bytesPerOpCode) == 0) { returnOpCode = MULR; } else if (strncmp(rawOpCode, "MULI", bytesPerOpCode) == 0) { returnOpCode = MULI; } else if (strncmp(rawOpCode, "DIVR", bytesPerOpCode) == 0) { returnOpCode = DIVR; } else if (strncmp(rawOpCode, "DIVI", bytesPerOpCode) == 0) { returnOpCode = DIVI; } else if (strncmp(rawOpCode, "ANDR", bytesPerOpCode) == 0) { returnOpCode = ANDR; } else if (strncmp(rawOpCode, "ANDI", bytesPerOpCode) == 0) { returnOpCode = ANDI; } else if (strncmp(rawOpCode, "ORR_", bytesPerOpCode) == 0) { returnOpCode = ORR_; } else if (strncmp(rawOpCode, "ORI_", bytesPerOpCode) == 0) { returnOpCode = ORI_; } else if (strncmp(rawOpCode, "NOTR", bytesPerOpCode) == 0) { returnOpCode = NOTR; } else if (strncmp(rawOpCode, "NOTI", bytesPerOpCode) == 0) { returnOpCode = NOTI; } else if (strncmp(rawOpCode, "SHRL", bytesPerOpCode) == 0) { returnOpCode = SHRL; } else if (strncmp(rawOpCode, "SHRR", bytesPerOpCode) == 0) { returnOpCode = SHRR; } else if (strncmp(rawOpCode, "JMPR", bytesPerOpCode) == 0) { returnOpCode = JMPR; } else if (strncmp(rawOpCode, "JMPI", bytesPerOpCode) == 0) { returnOpCode = JMPI; } else if (strncmp(rawOpCode, "BREQ", bytesPerOpCode) == 0) { returnOpCode = BREQ; } else if (strncmp(rawOpCode, "BRNE", bytesPerOpCode) == 0) { returnOpCode = BRNE; } else if (strncmp(rawOpCode, "BRGT", bytesPerOpCode) == 0) { returnOpCode = BRGT; } else if (strncmp(rawOpCode, "BRLT", bytesPerOpCode) == 0) { returnOpCode = BRLT; } else if (strncmp(rawOpCode, "BRGE", bytesPerOpCode) == 0) { returnOpCode = BRGE; } else if (strncmp(rawOpCode, "BRLE", bytesPerOpCode) == 0) { returnOpCode = BRLE; } else if (strncmp(rawOpCode, "CALL", bytesPerOpCode) == 0) { returnOpCode = CALL; } else { // If we reach this case, then the opcode was not recognized. if (debug) { fprintf(stderr, "VM: Unable to decode opcode = %c%c%c%c\n", rawOpCode[0], rawOpCode[1], rawOpCode[2], rawOpCode[3]); fflush(stderr); } // Vector into the kernel withf an invalid instruction. doInvalidInstructionTrap (); } if (debug) { fprintf(stderr, "VM: Opcode decoded = %c%c%c%c\n", rawOpCode[0], rawOpCode[1], rawOpCode[2], rawOpCode[3]); fflush(stderr); } // Return the interpreted opcode that was found. return returnOpCode; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Given a pointer to the byte that begins an argument within an // instruction, determine the value of the argument. vMachineWord_t interpretOperand (vMachineByte_t* rawArgument) { // The operand that will be returned to the caller. vMachineWord_t returnOperand; // Parse the operand, which needs to be a hexidecimal value // represented as an 8 character string. int itemsAssigned = sscanf(rawArgument, "%8x", &returnOperand); // Exactly one item should have been assigned if this argument is // well-formed. abortIfFalse((itemsAssigned == 1), "Invalid argument value format"); if (debug) { fprintf(stderr, "VM: Operand parsed to be = 0x%x\n", returnOperand); fflush(stderr); } // Return the interpreted argument. return returnOperand; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Fetch and decode an instruction, storing the results in the // provided instruction structure. Return whether or not the attempt // to fetch and decode was successful. bool fetchAndDecode (instruction_t* instruction) { // Ensure that the program counter points to a valid space. bool valid = checkAddressValidity((vMachineByte_t*)pc); // Was the address valid? if (!valid) { // It was not valid, so vector into the kernel for a failed memory // access. doMemoryAccessTrap(LOAD, (vMachineByte_t*)pc); // Return that the fetch failed. return false; } else { // Add the base offset register and the pc to the base memory // address of the virtual machine to get the underlying memory // location. vMachineByte_t* currentLocation = memoryBegin + getBase() + getPC(); // Based on the first four bytes, determine which opcode this // instruction has. instruction->opcode = interpretOpCode(currentLocation); // Extract the three operand values into the instruction // structure. currentLocation += bytesPerOpCode + bytesBetweenElements; instruction->operands[0] = interpretOperand(currentLocation); currentLocation += bytesPerOperand + bytesBetweenElements; instruction->operands[1] = interpretOperand(currentLocation); currentLocation += bytesPerOperand + bytesBetweenElements; instruction->operands[2] = interpretOperand(currentLocation); // Return that the fetch (and decode) succeeded. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the SYSC instruction. bool doSYSC () { // Trap into the system call handler. doSystemCallTrap(); // Indicate that the PC should be advanced. return true; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the STRR instruction. bool doSTRR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // The register is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indicate that the PC should not be advanced. return false; } else { // Set the destination register to hold the same value as the // source register. setGeneralRegister(destinationRegister, getGeneralRegister(sourceRegister)); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the STRI instruction. bool doSTRI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; vMachineWord_t immediateValue = instruction->operands[1]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // The register is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indicate that the PC should not be advanced. return false; } else { // Set the register specified by the value of operand-0 to hold the // value specified in operand-1. setGeneralRegister(destinationRegister, immediateValue); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the LOAD instruction. bool doLOAD (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int addressRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateOffset = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && addressRegister < numberGeneralRegisters); if (!validRegisters) { // The register number was not valid. Vector into the kernel for // an illegal instruction. doInvalidInstructionTrap(); // Indicate that the PC should remain on this instruction if it is // ever re-tried. return false; } else { // Take the value provided in register operand-1 as a main memory // address, and add the value in operand-2 as an offset from that // register's address. vMachineByte_t* requestedAddress = (vMachineByte_t*)getGeneralRegister(addressRegister) + immediateOffset; vMachineByte_t* realLocation = memoryBegin + getBase() + (unsigned int)requestedAddress; vMachineWord_t* realDataPointer = (vMachineWord_t*)realLocation; // Check the validity of the requested address. bool validAddress = checkAddressValidity(requestedAddress); if (!validAddress) { // The address was not valid, so vector into the kernel for a // failed memory access. doMemoryAccessTrap(LOAD, requestedAddress); // Indicate that the PC should remain on this instruction so // that it can be re-issued now that the trap has been handled. return false; } else { // The register and address are valid. Move the word from main // memory into the register. setGeneralRegister(destinationRegister, *realDataPointer); // Indicate that the PC should be advanced. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the STOR instruction. bool doSTOR (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister = (unsigned int)instruction->operands[0]; unsigned int addressRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateOffset = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister < numberGeneralRegisters && addressRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // The register number was not valid. Vector into the kernel for // an illegal instruction. doInvalidInstructionTrap(); // Indicate that the PC should remain on this instruction if it is // ever re-tried. return false; } else { // Take the value provided in register operand-1 as a main memory // address, and add the value in operand-2 as an offset from that // register's address. vMachineByte_t* requestedAddress = (vMachineByte_t*)getGeneralRegister(addressRegister) + immediateOffset; vMachineByte_t* realLocation = memoryBegin + getBase() + (unsigned int)requestedAddress; vMachineWord_t* realDataPointer = (vMachineWord_t*)realLocation; // Check the validity of the requested address. bool validAddress = checkAddressValidity(requestedAddress); if (!validAddress) { // It was not valid, so vector into the kernel for a failed // memory access. doMemoryAccessTrap(STOR, requestedAddress); // Indicate that the PC should remain on this instruction so // that it can be re-issued now that the trap has been handled. return false; } else { // The address is valid. Write the value of the register into // that location of main memory. *realDataPointer = getGeneralRegister(sourceRegister); // Indicate that the PC should be advanced. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the ADDR instruction. bool doADDR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the addition. vMachineWord_t value = (getGeneralRegister(sourceRegister1) + getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the ADDI instruction. bool doADDI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateValue = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the addition. vMachineWord_t value = (getGeneralRegister(sourceRegister) + immediateValue); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the SUBR instruction. bool doSUBR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the subtraction. vMachineWord_t value = (getGeneralRegister(sourceRegister1) - getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the SUBI instruction. bool doSUBI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateValue = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the subtraction. vMachineWord_t value = (getGeneralRegister(sourceRegister) - immediateValue); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the MULR instruction. bool doMULR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the multiplication. vMachineWord_t value = (getGeneralRegister(sourceRegister1) * getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the MULI instruction. bool doMULI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateValue = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the multiplication. vMachineWord_t value = (getGeneralRegister(sourceRegister) * immediateValue); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the DIVR instruction. bool doDIVR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the division. vMachineWord_t value = (getGeneralRegister(sourceRegister1) / getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the DIVI instruction. bool doDIVI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateValue = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the division. vMachineWord_t value = (getGeneralRegister(sourceRegister) / immediateValue); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the ANDR instruction. bool doANDR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the bitwise AND. vMachineWord_t value = (getGeneralRegister(sourceRegister1) & getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the ANDI instruction. bool doANDI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateValue = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the bitwise AND. vMachineWord_t value = (getGeneralRegister(sourceRegister) & immediateValue); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the ORR_ instruction. bool doORR_ (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the bitwise OR. vMachineWord_t value = (getGeneralRegister(sourceRegister1) | getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the ORI_ instruction. bool doORI_ (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; vMachineWord_t immediateValue = instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the bitwise OR. vMachineWord_t value = (getGeneralRegister(sourceRegister) | immediateValue); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the NOTR instruction. bool doNOTR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister = (unsigned int)instruction->operands[1]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the bitwise NOT. vMachineWord_t value = ~getGeneralRegister(sourceRegister); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the NOTI instruction. bool doNOTI (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; vMachineWord_t immediateValue = instruction->operands[1]; // Check the validity of the register. bool validRegister = (destinationRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegister) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the bitwise NOT. vMachineWord_t value = ~immediateValue; // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the SHRL instruction. bool doSHRL (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the shift. vMachineWord_t value = (getGeneralRegister(sourceRegister1) << getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the SHRR instruction. bool doSHRR (instruction_t* instruction) { // Extract the operands. unsigned int destinationRegister = (unsigned int)instruction->operands[0]; unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (destinationRegister < numberGeneralRegisters && sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Perform the shift. vMachineWord_t value = (getGeneralRegister(sourceRegister1) >> getGeneralRegister(sourceRegister2)); // Write the result into the destination register. setGeneralRegister(destinationRegister, value); // Indicate that the PC should be advanced. return true; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the JMPR instruction. bool doJMPR (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister = (unsigned int)instruction->operands[0]; // Check the validity of the register. bool validRegister = (sourceRegister < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegister) { // The register is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indicate that the PC should not be advanced. return false; } else { // Take the address provided in the source register as an offset // into main memory. vMachineByte_t* requestedAddress = (vMachineByte_t*)getGeneralRegister(sourceRegister); // Set the program counter to that address. Note that the address // isn't checked for validity since a later attempt to fetch an // instruction at an invalid address will trigger a trap. setPC((vMachineWord_t)requestedAddress); // Indicate that the PC should be left at this newly assigned // address. return false; } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the JMPI instruction. bool doJMPI (instruction_t* instruction) { // Extract the operands. vMachineWord_t immediateValue = instruction->operands[0]; // Take the immediate value as an offset into main memory. vMachineByte_t* requestedAddress = (vMachineByte_t*)immediateValue; // Set the program counter to that address. Note that the address // isn't checked for validity since a later attempt to fetch an // instruction at an invalid address will trigger a trap. setPC((vMachineWord_t)requestedAddress); // Indicate that the PC should be left at this newly assigned // address. return false; } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the BREQ instruction. bool doBREQ (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Is the value in source register 1 equal to the value in source // register 2? if (getGeneralRegister(sourceRegister1) == getGeneralRegister(sourceRegister2)) { // It is, so jump to the location given by the immediate value // at operand-0. Return the result of performing that jump. return doJMPI(instruction); } else { // Do nothing, and return that the PC should be advanced to the // next instruction. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the BRNE instruction. bool doBRNE (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Is the value in source register 1 not equal to the value in // source register 2? if (getGeneralRegister(sourceRegister1) != getGeneralRegister(sourceRegister2)) { // It is, so jump to the location given by the immediate value // at operand-0. Return the result of performing that jump. return doJMPI(instruction); } else { // Do nothing, and return that the PC should be advanced to the // next instruction. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the BRGT instruction. bool doBRGT (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Is the value in source register 1 greater than the value in // source register 2? if (getGeneralRegister(sourceRegister1) > getGeneralRegister(sourceRegister2)) { // It is, so jump to the location given by the immediate value // at operand-0. Return the result of performing that jump. return doJMPI(instruction); } else { // Do nothing, and return that the PC should be advanced to the // next instruction. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the BRLT instruction. bool doBRLT (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Is the value in source register 1 less than the value in source // register 2? if (getGeneralRegister(sourceRegister1) < getGeneralRegister(sourceRegister2)) { // It is, so jump to the location given by the immediate value // in operand-0. Return the result of performing that jump. return doJMPI(instruction); } else { // Do nothing, and return that the PC should be advanced to the // next instruction. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the BRGE instruction. bool doBRGE (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Is the value in source register 1 greater than or equal to the // value in source register 2? if (getGeneralRegister(sourceRegister1) >= getGeneralRegister(sourceRegister2)) { // It is, so jump to the location given by the immediate value // in operand-0. Return the result of performing that jump. return doJMPI(instruction); } else { // Do nothing, and return that the PC should be advanced to the // next instruction. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the BRLE instruction. bool doBRLE (instruction_t* instruction) { // Extract the operands. unsigned int sourceRegister1 = (unsigned int)instruction->operands[1]; unsigned int sourceRegister2 = (unsigned int)instruction->operands[2]; // Check the validity of the registers. bool validRegisters = (sourceRegister1 < numberGeneralRegisters && sourceRegister2 < numberGeneralRegisters); // Determine what to do based on the validity of the operands. if (!validRegisters) { // One of the registers is invalid, so vector into the kernel. doInvalidInstructionTrap(); // Indate that the PC should not the advanced. return false; } else { // Is the value in source register 1 less than or equal to the // value in source register 2? if (getGeneralRegister(sourceRegister1) <= getGeneralRegister(sourceRegister2)) { // It is, so jump to the location given by the immediate value // in operand-0. Return the result of performing that jump. return doJMPI(instruction); } else { // Do nothing, and return that the PC should be advanced to the // next instruction. return true; } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform the CALL instruction. bool doCALL (instruction_t* instruction) { // Store the address of the _next_ instruction in the return address // register, since we want to proceed the that instruction when the // called function returns. setGeneralRegister(register_ra, getPC() + bytesPerInstruction); // Perform a jump to the location given by the immediate value in // operand-0. return doJMPI(instruction); } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Execute the given instruction. void execute (instruction_t* instruction) { // A variable used to determine whether or not to advance the // program counter after an instruction. bool advancePC; // Jump to the appropriate case for this opcode. Perform the // instruction and record whether or not it was successful. switch (instruction->opcode) { case SYSC: advancePC = doSYSC(); break; case STRI: advancePC = doSTRI(instruction); break; case STRR: advancePC = doSTRR(instruction); break; case LOAD: advancePC = doLOAD(instruction); break; case STOR: advancePC = doSTOR(instruction); break; case ADDR: advancePC = doADDR(instruction); break; case ADDI: advancePC = doADDI(instruction); break; case SUBR: advancePC = doSUBR(instruction); break; case SUBI: advancePC = doSUBI(instruction); break; case MULR: advancePC = doMULR(instruction); break; case MULI: advancePC = doMULI(instruction); break; case DIVR: advancePC = doDIVR(instruction); break; case DIVI: advancePC = doDIVI(instruction); break; case ANDR: advancePC = doANDR(instruction); break; case ANDI: advancePC = doANDI(instruction); break; case ORR_: advancePC = doORR_(instruction); break; case ORI_: advancePC = doORI_(instruction); break; case NOTR: advancePC = doNOTR(instruction); break; case NOTI: advancePC = doNOTI(instruction); break; case SHRL: advancePC = doSHRL(instruction); break; case SHRR: advancePC = doSHRR(instruction); break; case JMPR: advancePC = doJMPR(instruction); break; case JMPI: advancePC = doJMPI(instruction); break; case BREQ: advancePC = doBREQ(instruction); break; case BRNE: advancePC = doBRNE(instruction); break; case BRGT: advancePC = doBRGT(instruction); break; case BRLT: advancePC = doBRLT(instruction); break; case BRGE: advancePC = doBRGE(instruction); break; case BRLE: advancePC = doBRLE(instruction); break; case CALL: advancePC = doCALL(instruction); break; default: // If we reach this case, then the opcode is some unrecognized // one. Such a case should have been caught in decoding, so // something is significantly amiss. abortIfFalse(false, "Unrecognized opcode in execution"); } // end switch on opcode // Should the program counter be advanced? if (advancePC) { // It should, so move to the next instruction. setPC(getPC() + bytesPerInstruction); } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Perform basic initialization of the virtual machine. void initialize (unsigned int mainMemorySize) { // Keep the user updated... printf("VM: Creating VM with %d bytes\n", mainMemorySize); fflush(stdout); // Main memory must be some integral number of machine words. // Ensure that the number of bytes specified by the caller is evenly // divisible by the number of bytes that compose a machine word. abortIfFalse((mainMemorySize % sizeof(vMachineWord_t)) == 0, "Main memory must be an integral number of words"); // Allocate enough memory for the virtual machine's main memory // needs. memoryBegin = (vMachineByte_t*)malloc(mainMemorySize * sizeof(vMachineByte_t)); memoryEnd = memoryBegin + mainMemorySize; // If the allocation wasn't successful, abort. abortIfFalse(memoryBegin != NULL, "Unable to allocate virtual machine main memory"); // Initialize the zero register to 0. setGeneralRegister(register_zero, 0); // Initialize the program counter to zero -- a special value that // cannot hold an actual instruction. Therefore this virtual // machine cannot execute anything until a kernel sets the program // counter to be some other value. setPC(0); // Initialize the TBR to hold zero -- a location that can't actually // be the TBA. setTBR(0); // Initialize the clock to zero. clock = 0; // Initialize the interrupt period to zero, which means that no // clock-driven interrupts will be generated until a different value // is set. setInterruptPeriod(0); } // ----------------------------------------------------------------- // -----------------------------------------------------------------/ void printGeneralRegisters () { if (debug) { fprintf(stderr, "VM: %%zero = %8x %%sp = %8x %%fp = %8x %%ra = %8x\n", getGeneralRegister(register_zero), getGeneralRegister(register_sp), getGeneralRegister(register_fp), getGeneralRegister(register_ra)); fprintf(stderr, "VM: %%s0 = %8x %%s1 = %8x %%s2 = %8x %%s3 = %8x\n", getGeneralRegister(register_s0), getGeneralRegister(register_s1), getGeneralRegister(register_s2), getGeneralRegister(register_s3)); fprintf(stderr, "VM: %%a0 = %8x %%a1 = %8x %%a2 = %8x %%a3 = %8x\n", getGeneralRegister(register_a0), getGeneralRegister(register_a1), getGeneralRegister(register_a2), getGeneralRegister(register_a3)); fprintf(stderr, "VM: %%a4 = %8x %%a5 = %8x %%a6 = %8x %%a7 = %8x\n", getGeneralRegister(register_a4), getGeneralRegister(register_a5), getGeneralRegister(register_a6), getGeneralRegister(register_a7)); fprintf(stderr, "VM: %%t0 = %8x %%t1 = %8x %%t2 = %8x %%t3 = %8x\n", getGeneralRegister(register_t0), getGeneralRegister(register_t1), getGeneralRegister(register_t2), getGeneralRegister(register_t3)); fprintf(stderr, "VM: %%t4 = %8x %%t5 = %8x %%t6 = %8x %%t7 = %8x\n", getGeneralRegister(register_t4), getGeneralRegister(register_t5), getGeneralRegister(register_t6), getGeneralRegister(register_t7)); fprintf(stderr, "VM: %%g0 = %8x %%g1 = %8x %%g2 = %8x %%g3 = %8x\n", getGeneralRegister(register_g0), getGeneralRegister(register_g1), getGeneralRegister(register_g2), getGeneralRegister(register_g3)); fprintf(stderr, "VM: %%g4 = %8x %%g5 = %8x %%g6 = %8x %%g7 = %8x\n", getGeneralRegister(register_g4), getGeneralRegister(register_g5), getGeneralRegister(register_g6), getGeneralRegister(register_g7)); } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Begin execution at the current program counter. void go () { // Keep the user updated... printf("VM: Executing\n"); fflush(stdout); // Repeatedly decode and execute the instruction at which the // program counter points. while (true) { // Advance the clock. clock++; if (debug) { fprintf(stderr, "VM: clock = %qd\n", clock); fprintf(stderr, "VM: pc = 0x%x\n", (unsigned int)getPC()); printGeneralRegisters(); fflush(stderr); } // Should a clock-driven interrupt occur? An interrupt period of // zero implies that such interrupts should never be generated. if ((interruptPeriod > 0) && ((clock % interruptPeriod) == 0)) { // One should, so call the clock interrupt handler. doClockInterruptTrap(); } // Attempt to fetch and decode the next instruction. instruction_t currentInstruction; bool success = fetchAndDecode(¤tInstruction); // Was the fetching and decoding successful? if (success) { // It was, so execute the decoded instruction. execute(¤tInstruction); } else { // If fetching and decoding is not successful, then the kernel // is responsible for fixing the problem (if possible), and the // attempt to fetch and decode should be retried. NOTHING TO DO // HERE. } } } // ----------------------------------------------------------------- // ----------------------------------------------------------------- // Set the initial state of the machine and start its execution. void startVMachine (unsigned int mainMemorySize, char* executablePathname) { // Initialize the machine. initialize(mainMemorySize); // Keep the user updated... printf("VM: Finding kernel\n"); fflush(stdout); // Since our virtual machine has no BIOS, we will cheat by calling a // function that we expect the kernel to provide. This function // will boot the kernel, which should initialize itself, load // executable code, and set the program counter. bootKernel(executablePathname); // Begin the execution of the machine. Note that normally, the // booting of the kernel would require the execution of the // machine. Since our kernel runs to the side of the machine, we // need to begin execution explicitly. go(); } // -----------------------------------------------------------------