# Introduction # This page introduces the first version of specification for _Simmy_, an educational simulator for _SIM_ computer architecture. _SIM_ stands for _Simple Integer Machine_. It is not an existing architecture, but it implements all significant features of real computer architectures. Its description will be presented step by step. Each new version will add new commands and requirements and also change previous ones. This version provides only an initial information about _SIM_. A very limited, but still operable, set of instructions is introduced and also some helpful tips and requirement to develop the functional simulator (the first version of _Simmy_) are presented in the following sections. . # Hardware specification # --- ## Registers ## --- _SIM_ architecture supports `16` general purpose integer registers. Each registers contains `16` bits for storing an unsigned value and `1` bit for a sign. If the sign bit is equal to `0` the register store a negative value, otherwise, a positive ones. The following figure represents the logical structure of the register file: ``` register number sign unsigned value ┌―――┐ ┌――――――――――――――――――――――┐ r0 │ 0 │ │ 0101 0001 0000 1010 │ ← decimal -20746 r1 │ 1 │ │ 0000 0000 0001 0011 │ ← decimal 19 r2 │ 0 │ │ 0111 1101 0110 0110 │ ← decimal -32102 ... ... ... ... ... ... r15 │ 1 │ │ 0110 01101 0000 1111 │ ← decimal 52495 └―――┘ └――――――――――――――――――――――┘ ``` . # Instruction set # --- This sections provides detailed description for all _SIM_ instructions. In this version each instruction has the same fixed length of _5 Bytes_. The first byte is always an operation code (so-called _opcode_). The content of the other bytes depends on the instruction type. The supported types are presented below. |Note: The terms _instruction_, _operation_ and _command_ are often used as synonyms. | |:-----------------------------------------------------------------------------------------------------------------------| . ## Operations with two operands ## --- This type generally includes main arithmetical and logical instructions. The result of an operation is _always_ saved into the first operand (`op1`), consequently, `op1` _must_ be a register number. The second operand (`op2`) can be either a value or a register number. ### Instruction structure ### As it was previously said, each instruction consists of _5 Bytes_. These bytes are described in the following list. Note that numbers of bytes in the instruction is agreed with numbers of items in the list.
  1. The opcode -- defines an action that should be done by the operation.
  2. The control byte -- contains some control information that can change the behavior of the instruction and the structure of the operands. The following list describes its bits:
    1. Not used and must be equal to 0.
    2. Not used and must be equal to 0.
    3. The type of the op2. This bit defines if op2 is a register or value (0 -- register, 1 -- value).
    4. The sign of op2. Note it is used only if op2 is a value, otherwise, it must be equal to 0. This bit defines if op2 value is positive or negative (0 -- negative, 1 -- positive).
    5. Not used and must be equal to 0.
    6. Not used and must be equal to 0.
    7. Not used and must be equal to 0.
    8. Not used and must be equal to 0.
  3. The value of op1. It is always a register number.
  4. The least significant byte of op2.
  5. The most significant byte of op2.
. For example, let's study the following instruction: ``` Start address Start address of the next instr of the instr ↓ ↓ N+5 N+4 N+3 N+2 N+1 N ↓ ↓ ↓ ↓ ↓ ↓ Next │ MSB of op2│ LSB of op2│ op1 │ Control │ Opcode │ Previous instr │ 0100 0000 │ 0100 1101 │ 0000 0011 │ 0000 0100 │ 0100 1101 │ instr ││ sign of op2 ―┘│ type of op2 ―┘ ``` It encodes some action (it is defined by the opcode `0100 1101`) on two values. Meanwhile, this first value is taken from `r3` and the second value is taken directly from `op2` and it is equal to `-16461`. ### Bitwise logical operations ### | **Opcode** | **Notation** | **Short Description** | |:------------|:-------------|:----------------------| | `0000 0001` | `AND` | Bitwise logical AND | | `0000 0010` | `OR` | Bitwise logical OR | | `0000 0011` | `XOR` | Bitwise exclusive OR | ### AND ### A bitwise `AND` takes two binary representations of equal length and performs the logical `AND` operation on each pair of corresponding bits. For each pair, the result is `1` if the first bit is `1` _and_ the second bit is `1`; otherwise, the result is `0`. For example: ``` # r3 stores `1 0000 0000 0000 0111`, i.e. (decimal 7) # r5 stores `0 1110 0000 0000 0110`, i.e. (decimal -57350) AND r3, r5; # r3 is changed to `0 0000 0000 0000 0110`, i.e. (decimal 5). ``` Note that `AND` also changes the sign bit. ### OR ### A bitwise `OR` takes two bit patterns of equal length and performs the logical inclusive `OR` operation on each pair of corresponding bits. For each pair, the result is `1` if the first bit is `1` _or_ the second bit is `1` _or_ both bits are `1`; otherwise, the result is `0`. For example: ``` # r3 stores `1 0000 0000 0000 0111`, i.e. (decimal 7) # r5 stores `0 1110 0000 0000 0110`, i.e. (decimal -57350) OR r3, r5; # r3 is changed to `1 1110 0000 0000 0111`, i.e. (decimal 57351). ``` Note that `OR` also changes the sign bit. ### XOR ### A bitwise _exclusive_ `OR` takes two bit patterns of equal length and performs the logical `XOR` operation on each pair of corresponding bits. The result in each position is `1` if only the first bit is `1` _or_ only the second bit is `1`, but will be `0` if both are `1` or both are `0`. This is equivalent to being `1` if the two bits are different, and `0` if they are the same. For example: ``` # r3 stores `1 0000 0000 0000 0111`, i.e. (decimal 7) # r5 stores `0 1110 0000 0000 0110`, i.e. (decimal -57350) XOR r3, r5; # r3 is changed to `1 1110 0000 0000 0001`, i.e. (decimal 57345). ``` Note that `XOR` also changes the sign bit. ### Integer arithmetics ### | **Opcode** | **Notation** | **Short Description** | |:------------|:-------------|:----------------------| | `1000 0010` | `ADD` | Integer addition | | `1000 0011` | `SUB` | Integer subtraction | | `1000 0001` | `MUL` | Integer multiplication | | `1000 0000` | `DIV` | Integer devision | | `1000 0100` | `MOV` | Move a value into a register | ### ADD ### The command `ADD` performs simple decimal integer addition of two values of equal length. For example: ``` # r3 stores `1 0000 0000 1000 0111`, i.e. (decimal 135) # r5 stores `0 0000 0000 0000 0110`, i.e. (decimal -6) ADD r3, r5; # r3 is changed to `1 0000 0000 1000 0001`, i.e. (decimal 129 = 135 + (-6)). ``` ### SUB ### The command `SUB` performs simple decimal integer subtraction of two values of equal length. The value from `op2` is subtracted from the value from `op1`. For example: ``` # r3 stores `1 0000 0000 1000 0111`, i.e. (decimal 135) # r5 stores `0 0000 0000 0000 0110`, i.e. (decimal -6) SUB r3, r5; # r3 is changed to `1 0000 0000 1000 1101`, i.e. (decimal 141 = 135 - (-6)). ``` ### MUL ### The command `MUL` performs simple decimal integer multiplication of two values of equal length. For example: ``` # r3 stores `1 0000 0000 1000 0111`, i.e. (decimal 135) # r5 stores `0 0000 0000 0000 0110`, i.e. (decimal -6) MUL r3, r5; # r3 is changed to `0 0000 0011 0010 1010`, i.e. (decimal -810 = 135 * (-6)). ``` ### DIV ### The command `DIV` performs simple decimal integer division of two values of equal length. The value from `op1` is divided by the value from `op2`. The result is rounded down. For example: ``` # r3 stores `1 0000 0000 1000 1000`, i.e. (decimal 136) # r5 stores `0 0000 0000 0000 0110`, i.e. (decimal -6) SUB r3, r5; # r3 is changed to `0 0000 0000 0001 0110`, i.e. (decimal -22 = 136 / (-6)). ``` ### MOV ### The command `MOV` copies the value from `op2` into the register of `op1`. For example: ``` # r3 stores `1 0000 0000 1000 0111`, i.e. (decimal 135) # r5 stores `0 0000 0000 0000 0110`, i.e. (decimal -6) MOV r3, r5; # r3 is changed to `0 0000 0000 0000 0110`, i.e. (decimal -6, a copy of r5). ``` ### Floating point arithmetics ### |Note: floating point calculations are not supported in this version. You can work only with integer numbers. | |:---------------------------------------------------------------------------------------------------------------------------------------------| . ## Operations with single operand ## --- This type generally includes auxiliary arithmetical, logical and system instructions. The operand (`op`) must be a register number and the result is always saved into it. ### Instruction structure ### As it was previously said, each instruction consists of _5 Bytes_. These bytes are described in the following list. Note that numbers of bytes in the instruction is agreed with numbers of items in the list.
  1. The opcode -- defines an action that should be done by the operation.
  2. The control byte -- contains some control information that can change the behavior of the instruction and the structure of the operands. The following list describes its bits:
    1. Not used and must be equal to 0.
    2. The sign. This bit is used in some commands that change the sign of a register.
    3. Not used and must be equal to 0.
    4. Not used and must be equal to 0.
    5. Not used and must be equal to 0.
    6. Not used and must be equal to 0.
    7. Not used and must be equal to 0.
    8. Not used and must be equal to 0.
  3. The value of op. It is always a register number.
  4. Not used and all bits must be equal to 0.
  5. Not used and all bits must be equal to 0.
For example, let's study the following instruction: ``` Start address Start address of the next instr of the instr ↓ ↓ N+5 N+4 N+3 N+2 N+1 N ↓ ↓ ↓ ↓ ↓ ↓ Next │ not used │ not used │ op1 │ Control │ Opcode │ Previous instr │ 0000 0000 │ 0000 0000 │ 0000 0101 │ 0000 0010 │ 0100 1101 │ instr │ └― sign ``` It encodes some action (it is defined by the opcode `0100 1101`) on a value that is taken from `r5`. The `sign` bit is set to 1. ### Bitwise logical operations ### | **Opcode** | **Notation** | **Short Description** | |:------------|:-------------|:----------------------| | `0100 0100` | `NOT` | Bitwise logical negation | ### NOT ### The bitwise `NOT`, or _complement_, is a unary operation that performs logical negation on each bit, forming the ones' complement of the given binary value. Digits which were `0` become `1`, and vice versa. For example: ``` # r3 stores `1 0000 0000 0000 0111` (decimal 7) NOT r3; # r3 is changed to `0 1111 1111 1111 1000` (decimal 177770). ``` Note that `NOT` also changes the sign bit. ### Integer arithmetics ### | **Opcode** | **Notation** | **Short Description** | |:------------|:-------------|:----------------------| | `1100 0000` | `DEC` | Integer decrement | | `1100 0001` | `INC` | Integer increment | | `1100 0010` | `SSGN` | Set a new sign | | `1100 0011` | `ISGN` | Sign inversion | ### DEC ### The command `DEC` decreases the value from `op` by one. ``` # r3 stores `1 0000 0000 0000 0111` (decimal 7) DEC r3; # r3 is changed to `1 0000 0000 0000 0110` (decimal 6). ``` ### INC ### The command `INC` increases the value from `op` by one. ``` # r3 stores `1 0000 0000 0000 0111` (decimal 7) INC r3; # r3 is changed to `1 0000 0000 0000 1000` (decimal 8). ``` ### SSGN ### The command `SSGN` takes the sign bit and set its value into sign bit of the register of `op`, i.e. changes its sign. ``` # r3 stores `0 0000 0000 0000 0111` (decimal -7) SSGN 1, r3; # r3 is changed to `1 0000 0000 0000 0111` (decimal 7). ``` ### ISGN ### The command `ISGN` invert the sign of the register of `op`, i.e. change positive to negative and vice versa. ``` # r3 stores `1 0000 0000 0000 0111` (decimal 7) ISGN r3; # r3 is changed to `0 0000 0000 0000 0111` (decimal -7). ``` . ## Operations without operands ## --- |Note: There is no such operation in this version.| |:--------------------------------------------------------------------------------| . # Simulation # --- In this version _Simmy_ implements only functional simulation. It means that the simulator does not count neither clocks nor any other performance data. The main purpose of this mode is to check the functional correctness of the program. The only way to do that is to execute it. ## External interfaces ## You should create a C++ class called _Simmy_ which implements a functional simulator. It should have only two public methods: its constructor and the _Execute_ method. The constructor receives a pointer to a byte array where instructions of a program is located. The _Execute_ method executes a certain number of instructions (as many as defined by its parameter) beginning from the first instruction in the byte array. Note that the first byte of the first instruction (i.e. its opcode) is located in the first element of the array (`bytes[0]`). When the given number of instructions is executed the method returns a value of `r0`. ``` class Simmy { public: Simmy ( unsigned char* bytes, unsigned int lenght); // execute certain number of instructions int Execute ( unsigned int number_of_instrs); }; ``` You are free to add as many additional private method and classes as you want, but you cannot change these methods. ## Getting instructions from memory ## This section provides helpful example how you can organize reading and parsing of instructions from a byte array. ``` #include using namespace std; int main() { // create a simple instruction basing on the following pattern: // // Start address Start address // of the next instr of the instr // ↓ ↓ // N+5 N+4 N+3 N+2 N+1 N // ↓ ↓ ↓ ↓ ↓ ↓ // Next │ MSB of op2│ LSB of op2│ op1 │ Control │ Opcode │ Previous // instr │ 0100 0000 │ 0100 1101 │ 0000 0011 │ 0000 0100 │ 0100 1101 │ instr // ││ // sign of op2 ―┘│ // type of op2 ―┘ unsigned char bytes[6] = { 77, // 0100 1101 5, // 0000 0100 3, // 0000 0011 77, // 0100 1101 64 // 0100 0000 }; // get the start address of the instr unsigned char* instr_start_addr = bytes; // get the opcode value unsigned char* opcode_addr = instr_start_addr; unsigned char opcode = *opcode_addr; // get the cotrol byte value unsigned char* control_byte_addr = instr_start_addr + 1; unsigned char control_byte = ( *control_byte_addr); bool type_op2 = control_byte & 4; // != 0 only if the 3rd bit is 1; bool sign_op2 = control_byte & 8; // != 0 only if the 4th bit is 1; // get the value of op2 unsigned char* op2_addr = instr_start_addr + 3; int val_op2 = *( ( unsigned short*) op2_addr); // take 2 bytes from op2_addr // apply the sign if ( !sign_op2 && type_op2) { val_op2 = -1 * val_op2; } cout << "Opcode = " << ( unsigned short) opcode << "\n" << "Operand_2 = " << ( type_op2 ? "" : "r") << val_op2 << "\n"; return 0; } ```