# Svetlana Simakova, cs61c-bc # Lab : TuTh 1-3pm # ########### # # 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). # .ktext 0x80000080 # Forces exception handler to be # located at address 0x80000080. # .set noat beq $0 $0 __start #first instruction executed. Jump to main. add $0 $0 $0 #delay slot 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 # .set at 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 $t4,4($t0) # else get the char. lui $t2,0x9000 # load address of rcvInput ori $t2,$t2,0x0298 lw $t3,0($t2) # rcvInput index lui $t2, 0x9000 # load address of rcvOutput ori $t2, $t2, 0x029c lw $t6, 0($t2) # rcvOutput index addi $t5, $t3, 1 # new rcvInput index andi $t5, $t5, 15 # Check if buffer is full. beq $t5,$t6,xmtintrp # If equal then drop character. lui $t1,0x9000 # load address of rcvRing ori $t1,$t1,0x0288 add $t1, $t1, $t3 # increment the Ring buffer address by rcvInput index sb $t4,0($t1) # Otherwise, there is space in buffer, # so store character. lui $t2,0x9000 # load address of rcvInput ori $t2,$t2,0x0298 sw $t5, 0($t2) # update rcvInput index # 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,xmtInput ori $t2,$t2,0x0278 lw $t3,0($t2) # load index of xmtInput lui $t2, 0x9000 # la $t4, xmtOutput ori $t2, $t2, 0x027c lw $t4,0($t2) # load index of xmtOutput bne $t3,$t4,xmtnotempty # If so sw $0,8($t0) # disable interrupts in the transmitter beq $0,$0,intrpdone # and return from handler. xmtnotempty: lw $t3,8($t0) # Get status word for output. andi $t3,$t3,1 # Mask out all but ready bit. beq $t3,$0,intrpdone # Return from handler if not ready. lui $t1,0x9000 # la $t1,xmtRing ori $t1,$t1,0x0258 add $t1, $t1, $t4 # increment xmtRing address by xmtOutput lb $t3,0($t1) # Get the char from the buffer sw $t3,12($t0) # Output character to terminal. addi $t4, $t4, 1 # add and wrap around andi $t4, $t4, 31 lui $t2, 0x9000 #la $t2, xmtOutput ori $t2, $t2, 0x027c sw $t4, 0($t2) #store new index in xmtOutput 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 $t4,0($a0) # Fetch next character to store beq $t4,$0,syscldone # in buffer, check for end of string. lui $t2,0x9000 # la $t2,xmtInput ori $t2,$t2,0x0278 lw $t5, 0($t2) #$t5 = xmtInput index lui $t2, 0x9000 #la $t3, xmtOutput ori $t2, $t2, 0x027c lw $t6, 0($t2) #$t6 = xmtOutput index addi $t3, $t5, 1 #increment by xmtInput index by 1 (new xmtInput index = $t0) andi $t3, $t3, 31 #wrap around beq $t3, $t6, syscldone lui $t1,0x9000 # la $t1,xmtRing ori $t1,$t1,0x0258 add $t1, $t1, $t5 #increment xmtRing by xmtInput sb $t4,0($t1) # Otherwise, there is space in buffer, # so store character. lui $t2,0x9000 # la $t2,xmtInput ori $t2,$t2,0x0278 sw $t3,0($t2) # Update xmtInput 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 # Go back for the next character. # 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 $t2,0x9000 # la $t2,rcvInput ori $t2,$t2,0x0298 lw $t4,0($t2) # rcvInput index = $t4 lui $t2, 0x9000 # la $t3, rcvOutput ori $t2, $t2, 0x029c lw $t5,0($t2) # rcvOutput index = $t5 bne $t4,$t5,rcvnotempty # If so, add $v0,$0,$0 # set $v0 set to zero. beq $0,$0,syscldone # and return from handler. rcvnotempty: lui $t1,0x9000 # la $t1,rcvRing ori $t1,$t1,0x0288 add $t1, $t1, $t5 #add rcvOutput index to rcvRing base address lb $v0,0($t1) # Otherwise, fetch the character. addi $t5, $t5, 1 andi $t5, $t5, 15 #increment and wrap around rcvOutput index lui $t2, 0x9000 # la $t2, rcvOutput ori $t2, $t2, 0x029c sw $t5, 0($t2) 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) # .set noat lw $at,28($k1) # .set at 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). # .kdata #xmtRing: .space 32 # 0x90000258 # 32 characters of storage space for # characters waiting to be output. #xmtInput: .word 0 # 0x90000278 # Index of next place to insert # character into buffer #xmtOutput: .word 0 # 0x9000027c # Index of next character to remove # from buffer. #xmtBuf: .space 4 # 0x90000280 #xmtFull: .word 0 # 0x90000284 #rcvRing: .space 16 # 0x90000288 # 16 characters of storage space for # characters waiting to be input. #rcvInput: .word 0 # 0x90000298 # Index at next place to insert # character into buffer. #rcvOutput: .word 0 # 0x9000029c # Index at next character to remove # from buffer. #rcvBuf: .space 4 # 0x900002a0 #rcvFull: .word 0 # 0x900002a4 # ################ # # 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. # .text # main () # # Set up stack, enable interrupts in the status register (but not in any # specific I/O devices), then enter an readchar-print loop. # .globl __start #__start: # addi $t0,$0,0x1801 # Set status register. # mtc0 $t0,$12 # 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. # 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 # # # .data #string: .asciiz "Received character 'x'\X0D\n" __start: lui $t0,0xffff # Enable receiver interrupts. addi $t1,$0,2 sw $t1,0($t0) lui $a0,0x1001 # la $a0,string ori $a0,$a0,0x0000 addi $v0, $0, 70 sb $v0, 0($a0) # Store char 'x' into output string. sb $0, 1($a0) addi $v0,$0,4 # print syscall syscall 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, 0($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]