cs270 Programming Assignment - LC3 Simulator

Essentials

Due: see the Progress page of the course web site for due date
Key: Use the key LC3SIM 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, identical to the one you have used this semester. In particular, you will be implementing functions in a single file logic.c. You will be reusing the file(s) fields.c and/or symbol.c you wrote for previous assignment(s).

You will likely add many functions to the code that has been provided to you. However, most of them are should be very short (2 to 3 lines). The reference implementation of this code contained less than 300 lines. The complexity lies in understanding the pieces and how they fit together.


Getting Started

  1. Create a directory for this assignment and cd there.
  2. Download the file appropriate for your OS
  3. Unpack the lc3sim.XXX.tar file. This will create a subdirectory lc3SIM containing all of the files for the project.
    
        tar -xvpf lc3sim.XXX.tar
  4. cd lc3SIM and do all your work in that directory
  5. Replace the file field.c by your version. In lieu of this, you may simply write code for the necessary functions. (equivalent of getBit() and getField()) in your logic.c file.
  6. Replace the file symbol.c by your version.
  7. Review the Makefile and make sure the variable GCC is appropriate for your C compiler. Also, verify that sed in in your $PATH. This will be OK for the department linux machines.
  8. Examine the files install.c.MASTER and mysim-tk.MASTER and look for the string abs_path_to_install_dir. It occurs once in each file.
  9. Configure the build by
    
        ./fixPath install.c mysim-tk
        
    Examine the files install.c and mysim-tk and verify the string has been replaced by the path to this directory. This script uses sed and will not work if it is not in your $PATH or is not available. In this case, you may use an editor to update the files.
  10. Build mysim
        make 
        
    You should see the following on linux (the warning about write_DR is expected).
        gcc -g -std=c11 -Wall -c -DSTACK_OPS -DDEBUG Debug.c
        gcc -g -std=c11 -Wall -c -DSTACK_OPS -DDEBUG field.c
        gcc -g -std=c11 -Wall -c -DSTACK_OPS -DDEBUG install.c
        gcc -g -std=c11 -Wall -c -DSTACK_OPS -DDEBUG logic.c
        logic.c:31:13: warning: ‘write_DR’ defined but not used [-Wunused-function]
         static void write_DR (instruction_t* inst, LC3_WORD value) {
                 ^~~~~~~~
        gcc -g -std=c11 -Wall -c -DSTACK_OPS -DDEBUG symbol.c
        gcc -g -std=c11 -Wall Debug.o field.o            install.o       logic.o symbol.o lc3simPA.a -o mysim
        
  11. Run the simulator using ./mysim-tk -norun or ./mysim -norun if you prefer the command line version.
  12. 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!
  13. 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.
  14. Exit the simulator.

Completing the first functions

All of your work will be done in the file logic.c. However, if the functions of symbol.c are not correct, you may need to revisit them.

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. 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 very simple 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, you will need to write C code involving shifting and masking to extract the values from the 16 bit instruction. 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 do not have source code for ALL the modules.

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 mysim.tar using the checkin program or the Checkin tab of the web page.This file may created by executing make submission. type:

    ~cs270/bin/checkin LC3SIM mysim.tar
  

Relax, you are done with your assignment! The semester is almost over. :)