Skip to main content
Department of Information Technology

Computer Architecture, First Course

Assembly 2: Interrupts and IO


Learn interrupt controlled and memory mapped IO.


The assignment is to write an interrupt driven message service. The assignment consists of three parts.

  1. Interrupt handler that catches interrupts from a timer and prints a given message at regular intervals
  2. Introduce a new system call in the tkisem kernel that controls the interrupt handler
  3. Write a simple test program that uses the service

About Interrupts

Many IO units in a computer (e.g., timer or serial port), use interrupt driven message services. That means that a processor first sets the timer or the speed for the serial port and continues to perform other tasks. When something happens in the system, for example a certain time point is reached or the serial port has received a character, an interrupt is generated. At this point, the processor will stop its current operations and "jump" to a special code that will take care of this interrupt. For example, the code could write "Its 10 o'clock" or it could read a character from the serial port. This code is called interrupt handler.

Supervisor and User Mode

The SPARC processor has two different modes of operation: supervisor and user mode. User mode is used to run ordinary user programs and the supervisor mode is used for low-level system programs. One of the reasons for this is to protect system programs from erroneous user applications. Typically, operating system and the code for drivers run in supervisor mode, while user applications execute in the user mode.

A system program that is available for tkisem is very simple. It sets simple interrupt handlers and can handle simple system calls (putc, puts, getc, gets, ...). The processor will switch to a supervisor mode when an interrupt occurs or a system call is called. In this supervisor mode, the interrupt handler code or system call code is executed before processor finishes and returns to the user mode.

From tkisem, it is possible to load a program and to execute it in supervisor or user mode. This selection is done by selecting super or user on the left side of a load button. To load a system program (e.g., operating system) you should (1) choose "super", (2) enter the name of the compiled supervisor application and (3) click on the load button. After that, the program is started by "rebooting" the simulator. A user program can now be loaded as in the first assignment. It is also possible to set the environment variable TKISEM_ROM to the name of your modified system file to automatically boot with that file.

export TKISEM_ROM=my_modified_rom_file

The source code to the tkisem kernel is in /stud/kurs/dark/MN1-HT03/tkisem/tkisem_rom.s Copy it so you can modify it. Assemble and link just as user mode programs.

Memory Segments

A SPARC processor can "divide" memory into different memory segments. This way, it is simple to protect supervisor code against (un)intentional changes from user programs. It is also possible to put code in different segments with same addresses so that both supervisor and user programs can be assembled in the same way and can have identical start addresses without any collisions.

The simulator has four different memory segments. They are used to divide data and text in super and user mode. To identify those segments, address space identifiers (ASI) are used (shown below).

ASI Memory Segments
0x08 User Text
0x09 Supervisor Text
0x0a User Data
0x0b Supervisor Text

Ordinary load (ld) and store (st) instructions can only access data segments that are identified by processor's running mode. On the other hand, during the execution time of system calls (e.g., print routines) the system program should be able to read/write data from/to a user program. In a supervisor mode, the program can access both data and text segments with special load/store instructions shown below.

Instruction Explanation
ldsba/stba Load/Store Signed Byte from Alternate Space
ldsha/stha Load/Store Signed Halfword from Alternate Space
lduba/stba Load/Store Unsigned Byte from Alternate Space
lduha/stha Load/Store Unsigned Halfword from Alternate Space
lda/sta Load/Store Word from Alternate Space
ldda/stda Load/Store Doubleword from Alternate Space

The syntax for lda is lda [address]asi, reg. For example, one program can fetch a character from a user data segment that is pointed out by %l4 with lduba [%l4]0x0a, %l5. The ASI number is always written directly after the right bracket.

Timer in tkisem

Tkisem's timer is programmed via memory mapped IO. It has only one address (0x00130000) that is accessible from both supervisor and user mode. It also has two registers and one interrupt-bit. One of the registers, period, can be modified through the address 0x130000. The second register, count, is used for internal purposes and can not be accessed.

When a number is written into the period register, the count register is incremented by one for each clock cycle. This procedure stops when the count register is equal to the value of a period register; one interrupt is generated, interrupt register is set, and count register is reset.

The timer stops and it quits generating interrupts if zero (0) is written to a period register. It is possible to show a timer in tkisem by choosing Devices/Timer from the menu. It is also possible to manually modify period register.

System Calls

Calls to the system in tkisem are done through so called traps. Also calls to puts etc. are performed in the same way, which was "hidden" in the previous assignment with prepared wrappers. To invoke one system call, a register %o0 should contain the parameter that is going to be passed before the following instruction is executed: ta {trap-number}. For example, a string is written with puts by setting the %o0 register to a string-pointer and executing ta 6. (Compare with code from simu_fini.s.)

To extend the system with one new call it is necessary to perform three changes. First, the trap code/routine must be written. Second, a short piece of code should also be written that is later copied to the trap table during the system boot. Finally, the initialization code for a trap table must be changed to correctly load the short code piece from a previous step into the table. Trap table's function in this case is a simple jump-table. The processor is going to jump to different addresses depending on the parameter that is used with a ta-instruction. The short piece of code is simply yet another jump to the corresponding trap routine.

Trap's address in the trap-table can be determined as follows:

address_ta_N = 0x800 + 0x10 * N =(128 + N) * 16

Exampel: ta 6

When the system is booting, a short piece of code is copied into the jump-table to position for "ta 6". This code is shown here:

        set     uputs_vect, %r2
        ldda    [%r2]9, %r4
        ldda    [%r2+%r3]9, %r6
        set     (128+6)*16, %r2         ! trap 6
        stda    %r4, [%r2]9
        stda    %r6, [%r2+%r3]9

Note that this code uses ASI 9, i.e., it copies executable code (see ASI-table above). The code that is copied is found at uputs_vect label. Also, note that ordinary jumps can not be used in this short code sequence. Any idea why?

It is not allowed to use all registers inside one trap routine or an interrupt handler. When a trap (or interrupt) occurs, registers are saved by changing the current register window, but without any control for overflow. In addition, registers %l1 and %l2 are reserved to save PC and nPC of the interrupted program. Register %l0 is also reserved to save processor status register (PSR). PSR contains several important flags and it must be saved if the trap routine changes any of them (to ensure program correctness). The PSR register should be saved to %l0 with rd %psr, %l0 and restored with wr %l0, %psr. In the trap routine (or interrupt handler), it is allowed to only use %l3-%l7 to avoid erroneous execution.

Console Input/Output

The interrupt handler can not read/write characters with system calls. On the other hand, it can "talk" directly with hardware. The IO-unit that writes to Console Out and reads characters from Console In is placed at the following address: 0x110000. If a character is written to that address, the same character is shown in the console.


Introduce a new system call in tkisem. Use ta 7. Its purpose is to control the timer and its interrupt handler. The call takes one or two parameters, depending on the value of the first parameter. The first parameter is transferred through %o0, and the second through %o1.

The first parameter contains a service number. If the service number is one, set the counter in the timer equal to the value of the second parameter and start the timer. If the service number is two, stop the timer. If the service number is 3, the second parameter contains a pointer to a string (in user address space) that should be printed on the screen each time the counter reaches zero.

Write an interrupt handler for the timer. Each time the timer generates an interrupt, the system should print the string set up by service three. If no string is set, the interrupt handler should return immediately. An interrupt handler that only catches each interrupt and returns is already implemented in tkisem_rom.s. Look at the timer: label.

Also write a simple test program that reads a string from the Console Input and repeats it using the new system call and the timer interrupt. The program should continue to ask for new input and replace the repeated string each time a new string is entered. The program should quit when the string "goodbye" is entered.


  • Do not use blocking IO to read new strings from Console Input. This block interrupts from the timer, and thus prevents the system from repeating the string while waiting for the user to enter a new one.
  • A suitable interval for the timer is 1000.
  • In the beginning of tkisem_rom.s, the symbols TIMER and CONOUT are defined.
  • Make sure you stop the timer while updating the string, to prevent strange output.
  • Read the help in tkisem, and get acquainted with the tkisem_rom before you start programming.

Updated  2003-10-06 16:50:03 by Zoran Radovic.