# kernel.s: # # This file contains the main application for the I/O Lab ###################################################################### # KERNEL CODE ###################################################################### # Variables for the output buffer. If nextIn and nextOut are equal # then the buffer is empty. If nextIn points to the character just # before nextOut then the buffer is full. .kdata .globl buffer .globl nextIn .globl nextOut buffer: .space 32 # 32 characters of storage space for # characters waiting to be output. nextIn: .word 0 # Index of next place to insert character into # buffer # (rotates circularly throughthe buffer). nextOut: .word 0 # Index of next character to remove from # buffer at interrupt time (rotates circularly # through the buffer). # Variables for input buffer: similar in function to those for output # buffer, except interrupt routine adds characters, getchar removes # them. .globl rcvBuf .globl rcvInput .globl rcvOutput rcvBuf: .space 16 # Storage space. rcvInput: .word 0 # Index at which to insert. rcvOutput: .word 0 # Index at which to remove. .ktext .globl getchar # int getchar() # # This procedure returns the next input character, waiting if necessary # for a character to become available. getchar: lui $t4,0xffff # enable interrupts for receiver addiu $t2,$0,2 # create new control reg value sw $t2,0($t4) # store new control reg value lw $t0,rcvInput # See if buffer empty. lw $t1,rcvOutput beq $t0,$t1,getchar la $t2,rcvBuf add $t2,$t1,$t2 lb $v0,0($t2) # Fetch next character. addiu $t1,$t1,1 # Update buffer index. andi $t1,$t1,15 # wrap index from 16 to 0 sw $t1,rcvOutput jr $ra # void print(char *string) # This procedure adds a string of characters to the output # buffer. If the buffer fills up, then it waits until space is # available in the buffer before adding more characters. .globl print .globl chkfull print: ### LAB Exercise ### Why are the following 3 lines needed? lui $t4,0xffff # enable interrupts for terminal. addiu $t2,$0,2 # Create new control reg value sw $t2,8($t4) # Store new control reg value la $t2,callSeq lw $t3,callSeqIndex add $t2,$t2,$t3 li $t4,'P' sb $t4,0($t2) addi $t3,$t3,1 andi $t3,$t3,31 sw $t3,callSeqIndex lb $t3,0($a0) # Fetch next char to store in buffer. addiu $a0,$a0,1 # Increment input pointer. beq $t3,$0,alldone # Check for end of string. la $t0,nextIn # Fetch current input index lw $t0, 0($t0) addiu $t1,$t0,1 # index after store the next char. andi $t1,$t1,31 # Wrap around from 0 back to 31. chkfull: la $t2,nextOut # Is buffer full? I.e. is nextIn just lw $t2, 0($t2) # before nextOut? beq $t2,$t1,chkfull # If so, just poll la $t4,buffer # If not, get buffer address add $t4,$t4,$t0 # Calculate address in circular buffer. sb $t3,0($t4) # Space in buffer: store character. la $t0, nextIn sw $t1,0($t0) # Update nextIn. j print # Go back for more characters. alldone:jr $ra # Return to caller. # Since we're dealing with a "raw" terminal here, must output both a carriage-return # character (\015, hex 0D) and a newline character (\n) to finish off a line. .kdata string: .asciiz "> !\X0D\n" # Main program: # Set up stack, enable interrupts in the status register (but # not in any specific I/O devices), then call print over and # over with a test string. .ktext .globl main .globl loop main: addi $sp,$sp,-4 # Save $ra. sw $ra,0($sp) ori $t0, $0, 0xFF01 # Enable all interrupts. mtc0 $t0,$12 # Write new interrupt status reg. lui $t4,0xffff # enable interrupts for receiver. addiu $t2,$0,2 # Create new control reg value sw $t2,0($t4) # Store new control reg value loop: jal getchar la $a0,string # Load string address in arg register. sb $v0,string+2 jal print # Print it. j loop # And repeat.