MIPS Coding Assembly Language - Interrupts


My code wouldn't work after attempting this. Can somebody show me how it's done?

Instructions -

Turn off branch and load delays for this program, if you wish. It will make it considerably simpler. Be sure to turn on memory-mapped I/O. Make sure you fully understand what the memory mapped I/O example program from my Web site does before you start designing or programming this. You will not be polling, but you need a clear understanding of memory-mapped I/O.

You may not use syscall for input or output, because the program would then block on the input. Instead, use an interrupt-driven I/O routine to handle all input and output. You must do ALL input and output through the interrupt-driven memory-mapped input/output services, NOT through syscall. You may NOT poll for I/O. The only syscall you may use for this program is a syscall with code 10 to end the program.

Write a program that starts with two arrays of characters (to hold .asciiz strings), of equal length (at least 60 characters in length), labeled 'source' and 'display'. Initially, make the source array contain a character string with a '\n' before the terminating NUL. Include upper- and lower-case letters and other characters (punctuation, digits, and spaces) in the string. Start your program by copying the source array into the display array. Use a subroutine to do this passing in the addresses of the two arrays on the stack. Use the "real-world" subroutine calling convention.

After copying the string, enable interrupts, and then loop, examining a variable (which may be changed by code inside the interrupt handler) until the user tells the program to quit. Whenever an output ready (transmitter) interrupt occurs, the interrupt handler will print the next single character from the display array, wrapping back to the beginning after it prints the '\n'. Whenever the input interrupt (receiver) interrupt occurs, you will extract the user's input (one character) and do one of the following tasks depending on the user's input:

's': sort the display array using any easy sort routine (bubble or ripple is fine). Do not sort the '\n'.

't': toggle the case of every alphabetic character (for example, 'T' becomes 't', 't' becomes 'T' and all non-alphabetic characters stay unchanged).

'a': replace the display array elements with the source elements once again.

'r': reverse the elements in the display array (again, excluding the '\n').

'q': quit -- terminate program execution.

Ignore any other character in input. Handle the commands from the user inside the interrupt handler, not inside the main program.

The 'q' command should just set the variable being queried (repeatedly) by the main program, which will cause the main program to then exit with a syscall with code 10. You MAY NOT use registers to send information between the program and the interrupt handler.

Note: an interrupt could happen in the middle of displaying the array, so the characters being displayed could change mid-line. This program is obviously designed to build on previous assignments, so you should reuse code wherever possible. The primary new feature is the interrupt-driven input and output. Instead of reading entire strings in one syscall, you will read and process each character as it arrives. Again, you MAY NOT use syscall for any I/O. This is about three or four pages of code in total.

This is my code but I can't get it to work -

prompt: .asciiz "SPIM IO Test.\nOnly runs with -mapped_io flag.\nPlease type 6 input lines:\n"
nl:      .asciiz "\n"
      .globl main
      li $v0 4
      la $a0 prompt
# Register usage:
#            s0            loop counter
#            t0            address of recv_ctrl
#            t1            address of recv_buffer
#            t2            address of trans_ctrl
#            t3            address of trans_buffer
#            t4, t5            temporaries
#            t6            char read from input
#            t7            1 => char in t6
      li      $s0, 3            # loop counter
      li       $t0, 0xffff0000      # recv ctrl
      li      $t1, 0xffff0004      # recv buf
      li       $t2, 0xffff0008      # trans ctrl
      li       $t3, 0xffff000c      # trans buf
# First, read and echo 3 lines of input by polling the IO registers, not through
# interrupts:
      mtc0      $0, $12            # Clear IE bit in Status reg to disable interrupts
      lw      $t4, 0($t0)      # Wait for receiver ready
      and       $t4, $t4, 1
      beq      $t4, 0, l1
      lw      $t6, 0($t1)      # Read character
      lw      $t4, 0($t2)      # Wait for transmitter ready
      and      $t4, $t4, 1
      beq      $t4, 0, l2
      sw      $t6, 0($t3)      # Write character
      beq      $t6, 0xa, decr      # New line (nl)
      bne      $t6, 0xd, l1      # Carriage return (cr)
      add      $s0, $s0, -1      # Decrement line counter
      bne     $s0, 0, l1      # If not zero, get another line
# Second, read and echo 3 lines of input by through interrupts:
      mfc0      $t4, $13
      and      $t4, 0xffff00ff      # Clear IP bits in Cause register
      mtc0      $t4, $13
      li      $s0, 3            # loop counter
      li      $t4, 0x2      # Enable device interrupts
      sw      $t4, 0($t0)
      sw      $t4, 0($t2)
      mfc0      $t4, $12      # Enable interrupts and mask in Status reg
      ori      $t4, $t4, 0xff01
      mtc0      $t4, $12
l3:      b      l3            # Loop waiting for interrupts
# Trap handler. Replaces the standard SPIM handler.
      .ktext 0x80000180
      mfc0      $t4, $13      # Get ExcCode field from Cause reg
      srl      $t5, $t4, 2
      and      $t5, $t5, 0x1f      # ExcCode field
      bne      $t5, 0, exception
# An interrupt:
      and      $t5, $t4, 0x800      # Check for IP3 (HW 1)
      beq      $t5, 0, check_trans
# Receiver interrupt:
      lw      $t5, 0($t0)      # Check receiver ready
      and      $t5, $t5, 1
      beq      $t5, 0, no_recv_ready      # Error if receiver is not ready
      lw      $t6, 0($t1)      # Read character
      li      $t7, 1
      beq      $t6, 0xa, decr2      # New line (nl)
      bne      $t6, 0xd, next      # Carriage return (cr)
      add      $s0, $s0, -1      # Decrement line counter
      mfc0      $t4, $13      # Get Cause register
      and      $t4, 0xfffff7ff      # Clear IP3 bit
      mtc0      $t4, $13
      beq      $t7, 0, ret_handler      # No char to write yet
      and      $t5, $t4, 0x400      # Check for IP2 (HW 0)
      beq      $t5, 0, check_loop
# Transmitter interrupt:
      lw      $t5, 0($t2)      # Check transmitter ready
      and      $t5, $t5, 1
      beq      $t5, 0, no_trans_ready
      sw      $t6, 0($t3)      # Write character
      li      $t7, 0
      mfc0      $t4, $13      # Get Cause register
      and      $t4, 0xfffffbff      # Clear IP2 bit
      mtc0      $t4, $13
      bne      $s0, 0, ret_handler      # If line counter not zero, get another line
# Done echoing, so terminate program.
      li      $v0, 10
      syscall                  # syscall 10 (exit)
# Return from handler.
      mfc0      $t4, $12      # Enable interrupts and mask in Status reg
      ori      $t4, $t4, 0xff01
      mtc0      $t4, $12
      eret                  # return to interrupted instruction
      li      $v0, 4            # Non-interrupt exception
      la      $a0, other_str      # Print message and ignore
      b      ret_handler
      li      $v0, 4            # Receiver was not ready after interrupt
      la      $a0, no_recv_str      # Print message and ignore
      b      ret_handler
      li      $v0, 4            # Interrupt was not from recv or trans
      la      $a0, bad_int_str      # Print message and ignore
      b      ret_handler
      li      $v0, 4            # Transmitter was not ready after interrupt
      la      $a0, no_trans_str      # Print message and ignore
      b      ret_handler
      .asciiz "Non-interrupt exception\n"
      .asciiz "Receiver not ready\n"
      .asciiz "Transmitter not ready\n"
      .asciiz "Unknown interrupt\n"
asked on Stack Overflow Dec 10, 2020 by dao fahk

1 Answer


I am working on a very similar assignment and new to MIPS. Looking over the program I think the directive in the kernel code at the bottom of pgm (line 123) should be .kdata and not .data. Also .data directive above main: is missing in the post.

answered on Stack Overflow Dec 16, 2020 by ReignStorm73

User contributions licensed under CC BY-SA 3.0