mfc0 t1, C0_CAUSE nop and t2, t1, 0x3f lw t2, interrupt_table(t2) nop jr t2 ...
Naturally, most exception routines will require more than just two registers (k0, k1). This means that the exception routine must first save the contents of the registers to a special reserved location in memory, and later restore those register contents before returning from the exception handler. A separate exception handler stack is required when nested exceptions are to be used, or when exception handler routines are allowed to call libary subroutines. In this demo, we reserve and use the memory region in the range of
0x.XXXX.YYYY - 0x.XXXX.ZZZZfor the exception handler subroutines. The following code, taken from Dominic&nbps;Sweetman: See Mips Run, is the quintessential MIPS exception handler routine. It simply increments a counter value stored in main memory each time an exception is generated:
.set noreorder .set noat xcptgen: la k0, xcptcount # get address of counter lw k1, 0(k0) # load counter value nop # load delay addu k1, 1 # increment counter sw k1, 0(k0) # store new value mfc0 k0, C0_EPC # get EPC nop # load delay (mfc0 slow) j k0 # return to program rfe # restore from exception # in branch delay slot
The program uses the same idea, but manages a set of eight counters for each of the different exceptions. The base address of those counters is set to 0x.XXXX.ZZZZ. The main program first initializes those counters to zero, then enters a main loop that just incremements another counter value.
You can now click the input switches to generate interrupts. A rising-edge on any of the interrupt inputs of the TinyMipsCop0 processor will set the corresponding interrupt bit in the status register, and the interrupt will be handled as soon as possible. The interrupt handler will first disable further interrupts, then clear the corresponding interrupt pending bit, increment the corresponding counter, re-enable interrupts, and then re-enter the main program.
Wait until the applet is loaded. You can now open the memory-editor (via popup > edit) The memory editor highlights the last read operation in green color, and the last write operation in cyan color, which allows you to easily watch the program execution. Note that the address bus of the standard RAM component is connected to a subset of the MIPS address bus, namely RAM_ADDR[15:0] = MIPS_ADDR[17:2]. Therefore, MIPS addresses or pointer values have to be divided by 4 to get the actual physical address inside the RAM. For example, the MIPS pointer value 0x00000200 used as the start address of the sieve array corresponds to the RAM address 0x00000080.
If you want to change the simulation speed, just select 'edit' on the clock-generator component, then edit the value of the clock-generator 'period' property, and select 'apply' (and 'ok' to close the clock-generator config dialog).
Similarly, open the TinyMips user-interface window (via popup > edit) to watch the register contents during the program execution.
The binary program running on the processor was compiled and linked with the GNU gcc (184.108.40.206) and binutils cross-compiler toolchain on a Linux x86 host, with the final ELF loading and relocation done via the Hades Elf2rom tool. See
Run the applet | Run the editor (via Webstart)