cs270 Fall 2013 Programming Assignment 6 - LC3 simulator

Essentials

Due: Thursday 12/12/2013 @ 11:59PM
Key: Use the key PA6 for checkin

About The Assignment

In this assignment you create part of the LC-3 simulator. Upon completion, you will have a fully functional LC-3 simulator, just like the one you have used this semester. In particular, you will be implementing functions in a single file logic.c. In addition to implementing the standard operations of the LC3 machine, you will provide an implemetation for FLADD (details to follow), to provide a 16 bit floating point addition operation.

You will likely add many functions to the code that has been provided to you. However, many of them are likely to be very short (2 to 3 lines).


Revision History:
VersionDate
1.0Nov 19 17:18
2.0Dec 2 10:30
3.0Dec 2 3:30 (fritz - updated instructions, added OSX files)

Getting Started

Perform the following steps
  1. Create a directory for this assignment and cd there.
     
        mkdir PA6
        cd PA6
        
  2. Copy the .tar file for your OS to the PA6 directory.
  3. Unpack the tar file. WARNING: this tar ball will spew files into the current directory. It does not unpack into a subdirectory.
    
        tar -xvf *.tar 
        
  4. Build mysim
        make 
        
    You should see the following (the warning about write_DR is expected).
        ./fixPath install.c mysim-tk
        /bin/chmod 500 mysim-tk
        touch .path
        /usr/bin/gcc -g -Wall -c -std=c99 -DFLADD_OP install.c
        /usr/bin/gcc -g -Wall -c -std=c99 -DFLADD_OP logic.c
        logic.c:32:13: warning: 'write_DR' defined but not used [-Wunused-function]
        /usr/bin/gcc -o mysim mysim.a install.o logic.o
        
  5. Run the simulator using ./mysim-tk -norun or ./mysim -norun if you prefer the command line version.
  6. Inspect the memory at address 0x200. The is the LC-3 operating system code. Note that you see labels, but NO code. What is happening is that the main program is loading the object code for the LC-3 operating system and storing it in memory by calling logic_write_memory(). Because you have not yet implemented this function, nothing is actually stored in memory!
  7. Attempt to set a value in a register. Nothing will happen because the main program is calling logic_write_reg(), but you have not implemented this yet.
  8. Exit the simulator.

Completing the first functions

The first thing you need to do is to implement logic_write_memory() and logic_write_reg(). Study the code for logic_read_reg() and logic_read_memory() and the functions documented in hardware.h. Review all the provided source code and plan what you need to do before writing code. You will NEED to use the functions in hardware.h and definitions in logic.h. The field functions (see documentation in field.h) are avaiable. Read that documentation carefully. When you are prepared, do the following:
  1. Write the code for logic_write_reg() and logic_write_memory().
  2. Save your work.
  3. Use make and re-build your simulator.
  4. Run the simulator using ./mysim-tk -norun or ./mysim -norun..
  5. Set a register and see if its value changes.
  6. Now examine the memory around address 0x200.
  7. You should now see the code for the LC-3 operating system. Do not proceed until both functions work correctly.
  8. Exit the simulator.

Completing the function logic_fetch_instruction()

Now you are ready to begin the meat of the project. Specifically, you will complete code that simulates the execution of an instruction. This involves three main functions that follow the flow of the LC-3. These are:
  1. logic_fetch_instruction()
  2. logic_decode_instruction()
  3. logic_execute_instruction()

You will complete the first function and then work incrementally on the other two to complete your assignment.

The function logic_fetch_instruction() is quite similar to the function logic_read_memory(), but the value needs to end up in the instruction register (IR). Look at the functions documented in hardware.h. Also read the documentation of this function to see what else it must do.

To test this, use the single instruction LC-3 program:


    .ORIG x3000
    ADD R0,R1,R2
    HALT
    .END
Create the program with an editor and assemble it using lc3as. You can now test whether your implementation works. To test it, do the following:
  1. Use make to rebuild the simulator.
  2. Start the simulator (use -norun).
  3. Load your simple program.
  4. Do a single step. This will result in an error about an illegal instruction. This is because you have not yet implemented the code. However, if your implementation of logic_fetch_instruction() is correct, you will see the ADD instruction in the IR.
  5. Do not proceed until this works correctly.

Completing the LC-3 ADD instruction

Next, make the LC-3 ADD instruction work. This involves adding code to both logic_decode_instruction() and execute_ADD(). Looking at the code for logic_execute_instruction(), note that the switch is based on the field opcode. This implies that the function logic_decode_instruction() MUST set that field at a minimum. In setting the fields of this data structure, the field functions ( see documentaion for field.h) will be very useful. Look at the simple LC-3 program above and figure out what additional pieces of information the instruction will require. Add code to logic_decode_instruction() to determine those values. Note that in this program, no immediate value is used, so you can delay implementing that portion of the code. If you wrote the assembler programming project, you will find that the logic_decode_instruction() is essentially doing the inverse of what pass_two() did.

At this point, you are going to take a little detour to make completing the code for execute_ADD() easier. Look at the function write_DR(). Many methods write to a destination register. So, if you decide to implement this method, it will prove useful for other instructions as well. As its name implies, the function stores a value in a register. It does one other thing. Look at the LC-3 ISA operation descriptions in appendix A in the book and determine what else every store into a register does. Add code for this operation. Alternatively, you can delay writing code for this second operation. It will not be needed until you work on BR.

Now return to the execute_ADD() function. The ADD instruction adds two values and stores the result in a register. The two values come from SR1OUT and the SR2MUX. These values can be filled in during logic_decode_instruction(). Note that SR2MUX is an array of two values. Think about how bit5 is defined and how it might be used as an index into SR2MUX.

In summary:

  1. Add code to logic_decode_instruction()to get the opcode.
  2. Add code to logic_decode_instruction()to get the register values needed for ADD.
  3. Add code to write_DR().
  4. Depending on how you choose to use the inst_fields struct (see its comments), you may wish to add code to logic_decode_instruction() to get the SR1OUTand SR2MUX[0] values.
  5. Add code to execute_ADD() to do the add and store the value. Make sure the function returns OK.
  6. If you have not been compiling all along as you made small changes, do a make and fix any problems.
  7. Start up your simulator, load your test file, put values in the registers and step your program. If it works, you will see the sum in the appropriate register. If not, read you code and try to figure out what is wrong.
To complete the LC-3 ADD instruction, you will need to handle immediate values. This requires only a very small change in decoding and execution. Make those changes and test it with another simple test program.

Congratulations, you are done with one instruction. You are really a lot further along that that. You should be able to finish the AND and NOT instructions with minimal effort. And you have a nice utility routine that will be used elsewhere as well.


Completing more LC-3 instructions

Beyond the arithmetic instructions you have implemented, the remaining LC-3 instruction involve addresses. Recall that part of the executon requires computing the effective address for use by the instruction. You may find the LC-3 visualizer useful in understanding what is required.

The computation of the effective address will use the ADDR1MUX and ADDR2MUX. Look at the documentation for the data structure inst_fields under the data structures tab of the assignment webpage.. The MUXs are modeled as arrays. MUXs do not actually store anything, but simply take multiple inputs and select one of the inputs as the output, depending on the select lines. In this model, the array holds the inputs and the select is actually an index into the array. The array values are set as part of logic_decode_instruction() and used in logic_MARMUX().

You will now implement code for decoding/executing a single LC-3 instruction at a time. The following sequence may be useful:

  1. Select one unimplmented LC-3 instruction to work on (e.g. LD).
  2. Understand what information is needed to simulate this instruction.
  3. Add code to logic_decode_instruction() to set these values in the data structure inst_fields.
  4. Add code to execute_instruction() to handle this LC-3 instruction. If you follow the pattern you see in the provided code, your code will be easier to write and debug.
  5. Add enough code to logic_MARMUX() to handle this instruction.
  6. Rebuild your simulator using "make".
  7. Create a simple LC-3 program that has only the instruction you are implementing.
  8. Test it thoroughly. As you complete more instructions, your test programs may become longer and more numerous. You should probably set up regression tests similar to those shown in the graded output of previous assignments.
  9. Repeat until you have implemented all the LC-3 instructions.

For this assignment, you can assume that all input programs are correct in the following ways:

In other words, you do NOT have to do any error checking in this assignment.

TEST! TEST! TEST! Once you are happy, execute the simulator without the -norun. The OS will now load and print out the welcome message. This is a good test of your work because many (but not all) of the LC-3 instructions are used in the OS. You can also set up regression tests that compare the output of ~cs270/lc3tools/lc3sim with mysim.


Debugging your code

Debugging this code is somewhat different than what you have done in the past. You only have source code for logic.c and field.c, but not the other parts.

You may find it useful to run your simulator and the class simulator (from ~cs270/lc3tools) side by side and single step both of them and compare what you see.

If you like to do debugging with printf(), you may do so, but you will NOT be able to use the GUI version of the simulator. This is because printf() is used to to send data from the simulator to the GUI and your printf() may cause the GUI to die. The command line version of the simulator (mysim) has no problems with additional output. However, grading WILL be affected, so you must turn off any degugging in the code you submit!

Even though you do not have all the source code, you may use gdb for debugging.

If you are comfortable with the console lc3 simulator, you may use gdb as you were shown in earlier recitations. Simply do the following:

  1. gdb mysim
  2. Set gdb breakpoint(s) as necessary in the various functions in logic.c. This is the only file you are modifying for this assignment. The general gdb syntax is break functionName
  3. run -norun This start the lc3 simulator, but does not execute any LC3 code. The lc3 simulator program is running, but will halt at any mysim breakpoints you have set.
  4. use the console commands of the lc3 simulator to exercise your code. When you hit a gdb breakpoint, control will transfer to gdb.
  5. when your use of the gdb facilities is complete, use continue to return control to the LC3 console.

If you prefer the GUI version of the lc3 simulator, you may still use gdb but you will need to do several additional steps.

  1. Start the GUI version of the simulator (mysim-tk).
  2. Determine the processID of your simulator. In a terminal, do a
    
       ps -fu yourLogin | grep mysim
    
    and you will see several processes listed. One will be the grep, one will be the mysim-tk and the third will be the actual simulator mysim. The process ID will be the first number on the line.
  3. Run gdb
  4. Use the gdb command attach processID and gdb will be attached to your simulator.
  5. Set gdb breakpoint(s) as necessary in the various functions in logic.c. This is the only file you are modifying for this assignment. The general gdb syntax is break functionName
  6. continue This resumes the lc3 simulator.
  7. use the GUI to interact with the lc3 simulator. If you hit any gdb breakpoints, you will then need to work with the gdb console.
  8. when your use of the gdb facilities is complete, use continue to return control to the LC3 GUI.
Note that there are two kinds of breakpoints under these circumstances, those that stop the simulated execution of an LC3 program at a certain LC3 address in simulated memory, and those of GDB stopping the execution on a given C statement of the simulator.

Grading Criteria


Checking in Your Code

You will submit the single file logic.c using the checkin program. Use the name PA6. At the terminal type:

    ~cs270/bin/checkin PA6 logic.c
  

The above command submits your assignment. For a sanity check, type the following to get the file you checked in and make sure it compiles and runs properly with the provided files:

    mkdir sanityCheck
    cd sanityCheck
    tar -xvf /lc3sim-start-XXX.tar 
    ~cs270/bin/peek PA6 logic.c > logic.c
    make
    // Do LOTS of test cases.
Relax, you are done with your assignment! The semester is almost over. :)