Department of Electrical and Computer Engineering

The University of Texas at Austin

EE 360N, Fall 2004
Lab Assignment 3
Due:31 October 2004, 11:59 pm
Yale N. Patt, Instructor
Aater Suleman, Huzefa Sanjeliwala, Dam Sunwoo TAs


The goal of this lab assignment is to extend the LC-3b simulator you wrote in Lab 2 to handle interrupts and exceptions. You will augment the existing LC-3b microarchitecture to support detection and handling timer interrupts and protection exceptions.

The input to this new simulator will be:

  1. A file entitled ucode which holds the expanded microcontrol store.
  2. A file holding an LC-3b user program that gets loaded into x3000.
  3. A file holding data for the program in x3000. This data will be loaded into xC000.
  4. A file holding the interrupt/exception vector table that gets loaded into x0200. You should form the contents of this table based on the vectors of each interrupt/exception and starting addresses of the service routine for each interrupt/exception.
  5. A file holding an LC-3b system program that gets loaded into x1200. This will be the interrupt service routine.
  6. A file holding an LC-3b system program that gets loaded into x1600. This will be the protection exception handler.
The simulator will start executing the LC-3b program loaded at x3000. The timer interrupt will occur at cycle 300. You are supposed to provide microarchitectural support for interrupt handling. The interrupt handler you write should increment location x4000 by #1. You will also need to implement the RTI instruction so that the interrupt handler can transfer control back to the user program. If the user program accesses a memory location that can only be accessed in supervisor mode, a protection exception will occur. You are supposed to provide microarchitectural support for handling exceptions. Most of this support is similar to the support you will add for interrupts. The exception service routine you will write should simply halt the machine. (Important note: we should be able to swap out your exception routine with a routine that returns from an exception. Therefore, make sure you implement the exceptions correctly and do not rely on the machine halting.)

New shell code A modified shell code has been written for you:


You will need to copy and paste the code you wrote for Lab 2 into this new shell code.

To run the simulator, type:

lc3bsim3 <micro_code_file> <program_file_1> <program_file_2> ...

The first parameter is the microcode file as before. The second parameter is the user program you will write.


This Lab consists of 2 parts, which we will describe in detail:
1. Adding support for timer interrupts
2. Adding support for protection exceptions

  1. Adding support for timer interrupts

    Timer Interrupt

    In this lab, 1 timer interrupt will occur - at cycle 300. When the timer generates an interrupt, the microarchitecture may be in the middle of executing an instruction. You need to decide exactly when the interrupt is detected and add the necessary microarchitectural support to handle interrupts. When the interrupt is detected the following actions will be taken by the processor:

    1. The privilege mode (most significant bit of the PSR, program status register) is set to 0, which indicates supervisor-level privilege. The interrupt service routine will be executed with supervisor-level privilege.

    2. R6 is set to the supervisor stack pointer if that is not already the case.

    3. The old PSR (PSR before PSR[15] is set to 0) and PC are pushed onto the supervisor stack. Supervisor stack pointer is decremented on each push. Note that the supervisor stack is different from user stack. Interrupt service routines can access the supervisor stack using R6. You should initialize the supervisor stack pointer to address 0x3000. Note that R6 will refer to the user stack while your program is running. It will refer to the supervisor stack while the interrupt service routine is being executed. If the system is in user mode when an interrupt is detected, the microarchitecture should transparently switch R6 so that it points to the supervisor stack. You need to implement this "stack switching" in microcode. You will need to modify the datapath, add new states to the state machine, and possibly add new control signals to support this operation. You may add registers to the datapath to save and restore user and supervisor stack pointers.

    4. The interrupting event supplies its 8-bit interrupt vector (INTV). The interrupt vector for the timer interrupt is 0x01.

    5. The processor left-shifts the interrupt vector one bit, yielding x02, and adds it to the base address of the interrupt/exception vector table (x200), yielding the address of the memory location (x0202), which contains the starting address of the interrupt service routine.

    6. The contents of memory location x0202, which should be x1200 for this assignment, are read and loaded into the PC.

    7. The processor begins execution of the interrupt service routine.

    The first step in adding support for interrupts is to determine how the state diagram of the LC-3b can be modified to handle interrupts. You will have to augment the microsequencer with additional logic to sequence these new states, and extend the existing microinstructions with additional bits for both the microsequencer and the datapath. You may augment current microinstruction fields and add new fields. You may also add new logic to the datapath. You are free to implement this as you wish, but you must document your method.

    RTI Instruction

    Next, you have to implement an instruction for returning from an interrupt. This will be used in the interrupt service routine to transfer control back to the interrupted program. This instruction, called RTI (return from interrupt), has the opcode 1000, and pops the old PC and PSR off the supervisor stack. If the RTI transfers control back to a user-level program, then the microcode should switch the stack pointer back so that R6 points to the user stack. (See the ISA reference for details on the RTI instruction)

  2. Adding support for exceptions

    Protection Exception

    Protection exceptions occur only when the machine is in user mode, and a memory location in system space (locations x0000 - x2FFF) is accessed by the user program. Exception handling for protection exceptions is very similar to interrupt handling as described above with an important difference: the exception-causing instruction should not be allowed to complete before the exception is handled. Hence, the memory access that causes the protection exception also should not be allowed to complete. You will need to change the mode of the machine to supervisor, switch to the supervisor stack, push the decremented PC and PSR on the supervisor stack and load the PC with the address of the exception service routine. The exception vector for protection exception is x02. You can store the exception vector in a separate EXCV register and add this register left-shifted by one to the interrupt/exception vector table base register to get the address of the location that contains the starting address of the exception service routine. You are free to implement this as you wish, but keep in mind the possibility of combining the states used for initiating the interrupt service routine and those used for initiating the exception service routine.

    You will also write the exception service routine for the protection exception. This routine should start at memory location x1600. For the purposes of this assignment, the exception service routine will simply halt the machine. However, don't rely on this. We can test your simulator by replacing your exception routine with our routine which returns from the exception handler using the RTI instruction instead of halting the machine. Upon return from the exception handler, the instruction that caused the exception should be re-executed.

Tips on getting started

When designing the mechanisms to support interrupts and exceptions keep in mind that they are handled very similarly. In your microcode, states for pushing the PSR and PC on stack and loading the address of handler routines could be shared for both exception and interrupt handling.

Writing Code

The user program loaded into x3000 should do the following: initialize memory location x4000 to #1, calculate the sum of the first 20 bytes stored in the memory locations beginning with xC000. This sum should first be stored at xC014. Also, store the sum at x0000. This store will cause a protection exception. The following numbers should be stored at locations xC000:
x12, x11, x39, x23, x02, xF6, x12, x23, x56, x89, xBC, xEF, x00, x01, x02, x03, x04, x05, x06, x07.

The interrupt service routine must increment memory location x4000.

The exception handlers you will submit should simply halt the machine.

What To Submit Electronically

  1. Adequately documented source code of your simulator called lc3bsim3.c.
  2. The assembly code for the interrupt service routine, the interrupt/exception vector table, the protection exception handler, the user program, and the data for locations xC000-xC013, called int.asm, vector_table.asm, except_prot.asm, add.asm, and data.asm respectively.
  3. The machine code for the interrupt service routine, the interrupt/exception vector table, the protection exception handler, the user program, and the data for locations xC000-xC013, called int.hex, vector_table.hex, except_prot.hex, add.hex and data.hex respectively.
  4. The new microcode called ucode3.
  5. A file called dumpsim. To generate this file, simulate the LC-3b assembly programs by following the instructions at the end of this section.
  6. A text file called readme that explains how you implemented this lab assignment. This readme file should describe the following:
    1. Changes you made to the state diagram. Include a picture similar to the state machine that shows the new states you added (only show your changes or mark your changes in a new state diagram). This picture should include the encodings of new states you added. Clearly show where each state fits in the current state diagram. Describe what happens in each new state.
    2. Changes you made to the datapath. Clearly show the new structures added, along with the control signals controlling those structures. Describe the purpose of each structure.
    3. New control signals you added to each microinstruction. Briefly explain what each control signal is used for.
    4. Changes you made to the microsequencer. Draw a logic diagram of your new microsequencer and describe why you made the changes.
    5. If you prefer to submit a hardcopy of your documentation, you can submit it in class on Monday November 1, 2004.

How to generate the dumpsim file

Dump memory location x4000 and the registers once before the 300th cycle, once after the ISR is done, and finally after the protection exception halts the execution of the program.

Things To Consider

The user stack should start at address xFE00 and the supervisor stack should start at address x3000.

  1. The microcode used for starting the exception service routine is very similar to the microcode that is used for starting the interrupt service routine. Are there any differences? With these differences in mind perhaps you can combine the states used for interrupt and exception handling.
  2. The interrupt service routine should not change any register values which are being used by the main program. We should be able to test the service routine you wrote with a main program (and a simulator) written by us and both programs should work correctly. Therefore, if your interrupt service routine destroys any registers, it should save the original values of those registers before destroying them and restore those values before returning control to the main program. Note that this saving/restoring of general purpose registers could also be supported by microcode. Is this a good idea? A related issue is the saving and restoring of condition codes. This has to be done in microcode, it cannot be done by the interrupt service routine. Why?
  3. Make sure you check Appendix A for the description of PSR, the RTI instruction, privilege mode, user stack, supervisor stack, and more information on interrupt processing.
  4. The functionality of your modifications to support interrupts and exceptions should not depend on the interrupt and exception handlers you write for this assignment. We should be able to replace your interrupt and exception handlers with handlers written by us, and your code should still work correctly. Conversely, the functionality of your interrupt and exception handlers should not depend on the user program you wrote for this assignment. We should be able to use your exception and interrupt handlers in our simulator running a user program different from what you wrote.
  5. Basic LC-3b microarchitecture described in Appendix C makes use of 31 of the available 64 states. This leaves you with 33 states to accomodate your changes to support interrupts and exceptions. Try to avoid expanding the size of the control store. You can avoid this by careful thinking and design before starting coding.
  6. You do not need to worry about nested interrupts for this assignment.