Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

//Program */ //-----------------------------------------------------------------

ID: 3599544 • Letter: #

Question

//Program

*/

//----------------------------------------------------------------------

public class Interpreter {

    static int PC;          //program counter holds address of next instr

    static int AC;          //the accumulator - a register for doing arithmetic

    static boolean    run_bit = true; //a bit that can be turned off to halt the machine

    static int instr;       //a holding register for the current instruction

    static int instr_type; //the instruction type (opcode)

    static int data_loc;    //the address of the data, or -1 if none

    static int data;        //holds the current operand

    //------------------------------------------------------------------

    //This procedure interprets programs for a simple machine. The machine

    //has a register AC (accumulator), used for arithmetic. The interpreter

    //keeps running until the run bit is turned off by the HALT instruction.

    //The state of a process running on this machine consists of the memory,

    //the program counter, the run bit, and the AC. The input parameters

    //consist of the memory image and the starting address.

    public static void interpret ( int memory[], int starting_address ) {

        PC = starting_address;

        run_bit = true;

        while (run_bit) {

            instr = memory[PC]; //fetch next instruction into instr

            PC = PC + 1; //increment program counter

            instr_type = get_instr_type( instr ); //determine instruction type

            data_loc = find_data( instr, instr_type, memory ); //locate data (-1 if none)

            if (data_loc >= 0) { //if data_loc is -1, there is no operand

                data = memory[data_loc]; //fetch the data

            }

            execute( instr_type, data ); //execute instruction

        }

    }

    //------------------------------------------------------------------

    //since our instruction set is so simple, we'll let the opcode and

    // instruction type be the same.

    private static int get_instr_type ( int opcode ) { return opcode; }

    //------------------------------------------------------------------

    private static int find_data ( int opcode, int type, int memory[] ) { ... }

    //------------------------------------------------------------------

    private static void execute ( int type, int data ) { ... }

}

Implement the following additional instructions:

    LDI   x    <-- load the AC with the value x

    LDM   y    <-- load the AC with the value in memory location y

    ST    y    <-- store the value in the AC in memory location y

(If necessary, feel free to pass memory[] as an additional argument to the execute function or make memory[] a static class variable.)

Change the code from the previous assignment to implement fixed length instructions: All instructions are now 2 ints in length (one int for the opcode and one for the operand). For example,

    int m2[] = { 9,

                   -5,

                   CLR,    0,

                   ADDI, 17,

                   ADDI,   2,

                   ST,     0,

                   ADDM,   0,

                   ADDM,   1,

                   CLR,    0,

                   HALT,   0

     };

     interpret( m2, 2 );

Answer the following questions.

Question A: What is the value of the PC and AC after each of the above instructions executes?

Question B: Consider all of the instructions (from this assignment and the previous assignment) that your machine executes. Which instructions are superfluous (i.e., can be implemented by a sequence of one or more of the other instructions)?

Question C: Once again, consider all of the instructions (from this assignment and the previous assignment) that your machine executes. Also consider #4 and #5 from the "Design principles of modern computers" and suggest how one could modify this architecture to achieve principle #4.

As usual, test your "processor" by giving it sample "programs" (at least 2). Email your code, the sample input and output from at least two test runs, and answers to all of the questions above with the subject: Interpreter2.

One of the "Design principles of modern computers" that we discussed was, "Provide plenty of registers." Our architecture has only 1 (the AC)! Modify your simulator to implement an architecture with 5 registers. The instruction set should implement the following:

ADDI    r4      12      <-- add value of 12 to contents of register 4

ADDM    r1      92      <-- add value in memory location 92 to contents of register 1

ADDR    r1      r3      <-- add value in register 3 to the value in register 1 (with the result in r1)

CLR     r0             <-- set value in register 0 to 0

DBG                    <-- turn on debug flag

HALT                   <-- halt the processor

NODBG                  <-- turn off debug flag

LDI     r4      7       <-- load register 4 with the value 7

LDM     r3      6       <-- load register 3 with the value in memory location 6

ST      9       r3      <-- store the value in register 3 in memory location 9

Initialize all register values to random numbers (using one of Java's built in random number generators).

Output the contents of the PC and all other registers when in debug mode.

Again, implement fixed length instructions. For example,

       int m2[] = { 9,

                    -5,

                     CLR,    r0, 0,

                     DBG,    0,   0,

                     CLR,    r1, 0,

                     ADDI,   r0, 17,

                     ADDI,   r3, 2,

                     ADDM,   r0, 0,

                     NODBG, 0,   0,

                     ADDM,   r3, 1,

                     ST,     1,   r3,

                     LDM,    r2, 1,

                     CLR,    r4, 0,

                     HALT,   0,   0

       };

       interpret( m2, 2 );

Answer the following question. Question A: What is the value of the PC and all other after each of the above instructions executes?

As usual, test your "processor" by giving it sample "programs" (at least 2). Email your code, the sample input and output from at least two test runs, and answers to the question(s) above with the subject: Interpreter 3.

Let's recap the addressing modes (methods of specifying the location of operands) that our architecture currently supports:

immediate (e.g., the value 12 in ADDI r4 12),

register direct (e.g., the value in register 3 (and register 1) in ADDR r1 r3),

memory (e.g., the value in memory location 1 in the instruction ADDM r3 1).

One useful addressing mode that is missing is register indirect. In this mode, the value in the register is not the value of the operand but is the location in memory of the operand.

In light of the above, implement the following new instruction:

  ADDRI r1 (r3) <-- add the value in the memory location specified by

                       register 3 to the value in register 1

                       (with the result in r1)

Note: To remind us that we are not adding the value in r3 but are adding the value in memory at r3, we will place ()'s around the register specifier. Keep in mind that this is just "syntactic sugar" - the opcode actually specifies how the operands are interpreted.

Question A: List and describe all of the instructions (from all those that we have implemented so far) that might employ the register indirect addressing mode. (You need only implement ADDRI in your code.)

Question B: Once again, consider "Design principle #4" and all of the new instructions in this exercise (ADDRI and those from Question A). List and describe those that violate Design principle #4.

Answer the following question. Question C: What is the value of the PC and all other registers after each of the below instructions executes?

        int m2[] = { -9,

                      5,

                      0,

                      72,

                      8,

                      81,

                      55,

                      CLR,    r0, 0,

                      CLR,    r1, 0,

                      CLR,    r2, 0,

                      CLR,    r3, 0,

                      CLR,    r4, 0,

                      ADDI,   r1, 1,

                      ADDRI, r0, (r1), //new instruction

                      ADDI,   r1, 1,

                      ADDRI, r0, (r1), //new instruction

                      ADDI,   r1, 1,

                      ADDRI, r0, (r1), //new instruction

                      ADDI,   r1, 1,

                      ADDRI, r0, (r1), //new instruction

                      ADDI,   r0, 17,

                      ADDI,   r3, 2,

                      ADDM,   r0, 0,

                      ADDM,   r3, 1,

                      ST,     0,   r0,

                      LDM,    r2, 0,

                      HALT,   0,   0

        };

        interpret( m2, 7 );

Implement a processor status register (PSR). This (32-bit, integer) register should include debug (D) and run (R) bits. Additionally, it should also have negative (N) and zero (Z) bits. The N bit is 1 when the result of the last instruction is negative; otherwise, N is 0. The Z bit is 1 when the result of the last instruction is zero; otherwise, Z is 0.

Note: Halt should ONLY clear the run bit. Halt should leave all other remaining bits in the state of the previous instruction.

Modify the information that is printed when in debug mode to also include PSR information as well.

As usual, test your "processor" by giving it sample "programs" (at least 2). Email to me your code, the sample input and output from at least two test runs, and answers to the question(s) above with the subject: Interpreter 4.

Explanation / Answer

public class Interpreter {

    static int PC;          //program counter holds address of next instr

    static int AC;          //the accumulator - a register for doing arithmetic

    static boolean    run_bit = true; //a bit that can be turned off to halt the machine

    static int instr;       //a holding register for the current instruction

    static int instr_type; //the instruction type (opcode)

    static int data_loc;    //the address of the data, or -1 if none

    static int data;        //holds the current operand

    //------------------------------------------------------------------

    //This procedure interprets programs for a simple machine. The machine

    //has a register AC (accumulator), used for arithmetic. The interpreter

    //keeps running until the run bit is turned off by the HALT instruction.

    //The state of a process running on this machine consists of the memory,

    //the program counter, the run bit, and the AC. The input parameters

    //consist of the memory image and the starting address.

    public static void interpret ( int memory[], int starting_address ) {

        PC = starting_address;

        run_bit = true;

        while (run_bit) {

            instr = memory[PC]; //fetch next instruction into instr

            PC = PC + 1; //increment program counter

            instr_type = get_instr_type( instr ); //determine instruction type

            data_loc = find_data( instr, instr_type, memory ); //locate data (-1 if none)

            if (data_loc >= 0) { //if data_loc is -1, there is no operand

                data = memory[data_loc]; //fetch the data

            }

            execute( instr_type, data ); //execute instruction

        }

    }