beq $0, $0, __start add $0, $0, $0 # ########### # # iecho.s # # ########### # # This file contains a skeleton of a program that performs buffered # interrupt-driven input/output. The current implementation has a one # character buffer. Your job is to implement a ring buffer. The program # also handles print and readchar syscalls as well. # # IMPORTANT NOTES: # # (1) Use the commands: # # % stty min 1 # % [x]spim -memio -quiet # # to run this program on spim or xspim. # # (2) The current framework already works to some extent (try running it, try # typing really fast). Your ONLY task for this part is to replace the single # character io buffers with ring buffers. DO NOT CHANGE ANYTHING ELSE! # # (3) You will run your handler on both spim and your interrupt-enabled cpu. # So make sure you only use instructions that your cpu is capable of executing. # We have added some instructions, so your new instruction set is: # # add, addi, lw, sw, lb, sb, beq, bne, lui, ori, andi, jr, slt, # (mfc0, rfe, syscall) # # where the instructions in parenthesis are the instructions you have to # implement. # # (4) Since your code must also run on your interrupt-enabled cpu, it is # necessary that we hard-code the address locations of .kdata and .data. It # is very important that you do not change anything in .kdata or .data! # Otherwise, the address references you make will be incorrect. # # (5) The interaction between io devices and user programs goes something # like this: # # readchar syscall <- rcv buffer <- rcv interrupt # print syscall -> xmt buffer -> xmt interrupt # # where the arrows indicate the direction that characters are being passed # along. So for example, the receiver interrupt puts a character into the # receiver buffer, and the readchar system call takes a character out of # that buffer and passes it to the user program. # ##################### # # Exception Handler # # ##################### # # This exception handler handles two kinds of interrupts: # # (1) receiver interrupts # (2) transmitter interrupts # # and two kinds of traps: # # (1) readchar syscall # (2) print syscall # # Upon entering the exception handler: # # (1) The EPC register is set to PC where the interrupt/trap occured. # (2) The cause register is set to indicate what kind of exception # needs to be handled. # (3) The interrupt enable bit in the status register is set to # zero. For this project, interrupts remain disabled throughout the # entire exception handler. This means you will not have to handle # multiple interrupts at a time. # # Spim sets the EPC, cause, status registers for you. When you write the # processor portion of the project, you will have to make sure that your # processor sets the EPC and cause registers (the status register is taken # care for you already). lui $k1,0x9000 # For the purposes of our exception ori $k1,$k1,0x2000 # handler we are storing all data in # the kernel's data segment between # 0x90001000 and 0x90002000. addi $k1,$k1,-32 sw $at,28($k1) # 0x90001ffc sw $t6,24($k1) # 0x90001ff8 sw $t5,20($k1) # 0x90001ff4 sw $t4,16($k1) # 0x90001ff0 sw $t3,12($k1) # 0x90001fec sw $t2,8($k1) # 0x90001fe8 sw $t1,4($k1) # 0x90001fe4 sw $t0,0($k1) # 0x90001fe0 mfc0 $k0,$13 # Move cause to $k0. andi $k0,$k0,0x3c # We only use the ExcCode. addi $k0,$k0,jumptable # $k0 = cause + jumptable jr $k0 # jumptable: # # $k0 = cause + jumptable. # if cause == 0 then jr $k0 jumps to beq $0,$0,intrp, # else if cause == 8 then jr $k0 jumps to beq $0,$0,syscl, # else jr $k0 jumps to beq $0,$0,otherexcptn. jumptable: beq $0,$0,intrp # 0 beq $0,$0,otherexcptn # 1 beq $0,$0,otherexcptn # 2 beq $0,$0,otherexcptn # 3 beq $0,$0,otherexcptn # 4 beq $0,$0,otherexcptn # 5 beq $0,$0,otherexcptn # 6 beq $0,$0,otherexcptn # 7 beq $0,$0,syscl # 8 beq $0,$0,otherexcptn # 9 beq $0,$0,otherexcptn # 10 beq $0,$0,otherexcptn # 11 beq $0,$0,otherexcptn # 12 beq $0,$0,otherexcptn # 13 beq $0,$0,otherexcptn # 14 beq $0,$0,otherexcptn # 15 # intrp: # # Handle receiver and transmitter interrupts. intrp: # rcvintrp: # # First handle receiver interrupts. The handler checks the receiver ready bit. # If ready is 1, the handler takes a character out of the receiver data # register (memory mapped io) and stores the character into the receiver # buffer. If the buffer is full then the character is dropped. rcvintrp: lui $t0,0xffff # Get base address of device. lw $t3,0($t0) # Get status word for input. andi $t3,$t3,1 # Mask ready bit. beq $t3,$0,xmtintrp # If no char then all done, lb $t6,4($t0) # else get the char. lui $t1, 0x9000 # la $t1, rcvIn ori $t1, $t1, 0x0298 lw $t2, 0($t1) # $t2 = index of receive in lui $t3, 0x9000 # la $t3, rcvOut ori $t3, $t3, 0x029c lw $t4, 0($t3) # $t4 = index of receieve out addi $t5, $t4, -1 andi $t5, $t5, 15 beq $t2, $t5, xmtintrp #buffer full lui $t3,0x9000 # la $t3,rcvRing ori $t3,$t3,0x0288 add $t3, $t3, $t2 sb $t6,0($t3) # Otherwise, there is space in buffer, # so store character. addi $t2, $t2, 1 #inceremnets input andi $t2, $t2, 15 sw $t2, 0($t1) # xmtintrp: # # Next handle transmitter interrupts. The handler checks the transmitter ready # bit. If ready is 1 and the transmitter buffer is not empty, the handler # takes a character out of the transmitter buffer and stores the character # into the transmitter data register (memory mapped io). xmtintrp: lui $t0,0xffff lui $t2,0x9000 # la $t2 xmtIn ori $t2,$t2,0x0278 lw $t3, 0($t2) lui $t4, 0x9000 # la $t3, xmtOut ori $t4, $t4, 0x027c lw $t5, 0($t4) bne $t3, $t5, xmtnotempty #empty? # If so, sw $0,8($t0) # disable interrupts in the transmitter beq $0,$0,intrpdone # and return from handler. xmtnotempty: lw $t2,8($t0) # Get status word for output. andi $t2,$t2,1 # Mask out all but ready bit. beq $t2,$0,intrpdone # Return from handler if not ready. lui $t1,0x9000 # la $t1,xmtRing ori $t1,$t1,0x0258 add $t1, $t1, $t5 #move to next out Index lb $t3,0($t1) # Get the char from the buffer. sw $t3,12($t0) # Output character to terminal. addi $t5, $t5, 1 # increments the next output andi $t5, $t5, 31 sw $t5, 0($t4) intrpdone: mfc0 $k0,$14 # Read the exception program counter. beq $0,$0,cleanup # Return from handler. # syscl: # # Handle readchar and print syscalls. syscl: addi $t0,$0,4 # If $v0 == 4, then handle print beq $v0,$t0,kprint # syscall. addi $t0,$0,12 # If $v0 == 12, then handle readchar beq $v0,$t0,kreadchar # syscall. beq $0,$0,syscldone # Unknown syscall, so return. # kprint: # # Takes each character of the string that $a0 points to and stores it into # the transmitter buffer. If the buffer is full, then the handler returns with # $a0 pointing to the first unhandled character in the string. kprint: lb $t0,0($a0) # Fetch next character to store beq $t0,$0,syscldone # in buffer, check for end of string. lui $t1, 0x9000 # la $t1, xmtIn ori $t1, $t1, 0x0278 lw $t3, 0($t1) lui $t2, 0x9000 # la $t2, xmtOut ori $t2, $t2, 0x027c lw $t4, 0($t2) addi $t4, $t4, -1 andi $t4, $t4, 31 beq $t4, $t3, syscldone #checks if buffer is full lui $t2,0x9000 # la $t2,xmtRing ori $t2,$t2,0x0258 add $t2, $t2, $t3 # moves the first index of xmtRing sb $t0, 0($t2) # saves into the nextIndex of xmtRing addi $t3, $t3, 1 #increment next counter andi $t3, $t3, 31 sw $t3, 0($t1) #saves the next counter lui $t0,0xffff # Make sure interrupts are enabled addi $t3,$0,2 # in the transmitter. sw $t3,8($t0) addi $a0, $a0, 1 beq $0, $0, kprint # kreadchar: # # If the receiver buffer is empty, then the handler returns with $v0 set to # zero. Otherwise, a character is read from the receiver buffer and returned # in $v0. kreadchar: lui $t1, 0x9000 # la $t1, rcvIn ori $t1, $t1, 0x0298 lw $t2, 0($t1) # $t2 = index of receive in lui $t3, 0x9000 # la $t3, rcvOut ori $t3, $t3, 0x029c lw $t4, 0($t3) # $t4 = index of receieve out bne $t2,$t4,rcvnotempty #if the buffer is empty return 0 add $v0, $0, $0 beq $0, $0, syscldone rcvnotempty: lui $t1,0x9000 # la $t1,rcvRing ori $t1,$t1,0x0288 add $t1, $t1, $t4 lb $v0, 0($t1) # Otherwise, fetch the character. addi $t4, $t4, 1 andi $t4, $t4, 15 sw $t4, 0($t3) beq $0,$0,syscldone syscldone: otherexcptn: mfc0 $k0,$14 # Read the exception program counter. addi $k0,$k0,4 cleanup: lui $k1,0x9000 # Restore registers. ori $k1,$k1,0x2000 addi $k1,$k1,-32 lw $t0,0($k1) lw $t1,4($k1) lw $t2,8($k1) lw $t3,12($k1) lw $t4,16($k1) lw $t5,20($k1) lw $t6,24($k1) lw $at,28($k1) rfe # Restore status bits on the way out. jr $k0 # Jump to $k0 which was set to EPC+4. # ############### # # Kernel Data # # ############### # # Variables used for the output and input buffer. # # Transmitter Buffer: a print syscall should add characters to the transmitter # buffer, a transmitter interrupt should remove a character and display it on # the console. # # Receiver Buffer: a receiver interrupt should add a character to the receiver # buffer, a readchar syscall should remove a character and reutrn it in $v0. # # iecho.s currently uses the data: xmtBuf, xmtFull, rcvBuf, and rcvFull to # implement single character input/output buffers. Your task is to implement # input/output ring buffers. You will use the data xmtRing, xmtInput, # xmtOutput, rcvRing, rcvInput, and rcvOutput to do this. # # The inputs are used as the index where the next character should be inserted # into the buffer. The outputs are to be used as the index where the next # character should be removed from the buffer. Both input and output wrap # around once they reach the end of the character array. An easy way to # "wrap" around in MIPS is to use "andi" every time you increment either # input or output index. For example, to increment xmtInput: # # lui $t0,0x9000 # ori $t0,$t0,0x0278 # lw $t1,0($t0) # addi $t1,$t1,1 # andi $t1,$t1,31 # sw $t1,0($t0) # # By definition a ring buffer is empty when input == output, and it is full # when (input+1)%bufsize == output (or simply when input trails output by # a character). # ################ # # User Program # # ################ # # The user program interacts with the kernel by making system calls. # When a syscall is invoked, the program jumps to the exception handler. # Also, when an interrpt occurs, the program jumps to the exception # handler. Remember that interrupts can occur at any time. # main () # # Set up stack, enable interrupts in the status register (but not in any # specific I/O devices), then enter an readchar-print loop. __start: lui $t0,0xffff # Enable receiver interrupts. addi $t1,$0,2 sw $t1,0($t0) loop: readchar: addi $v0,$0,12 # readchar syscall syscall beq $v0,$0,readchar # if the result of the syscall is # zero, then try again. addi $s0, $0, 0xff #[add this line] memio return -1 as a byte, once it gets to the end of the input file. beq $s0, $v0, fin #[add this line] if end of the file, go t fin and halt the processor lui $a0,0x1001 # la $a0,string ori $a0,$a0,0x0000 sb $v0, 20($a0) # Store char 'x' into output string. print: addi $v0,$0,4 # print syscall syscall lb $t0,0($a0) bne $t0,$0,print # if the result of the syscall is # not zero, then try again. beq $0,$0,loop fin: # [add this line] halt # [add this line]