Sunday, March 30, 2014

0002 - File I/O, string manipulation with MIPS

For my group's final project in COMP 3410, we are working on a text-based maze/adventure game built entirely within MIPS. We are also very stupid.

Building the room framework, one of the first tasks I had to accomplish was reading files into a MIPS application (because we did not want to hard code every room's description, etc., into the application). This also allows for increased flexibility as well as allowing for more code reuse (read: less writing). Since every room of the maze was being read from different folders/files, I had to be able to modify the file paths that were being read from. That's where things got a little bit hairy (string modification/concatenation in MIPS is not simple), mainly because I was married to the idea of storing as many of the strings as possible in memory.

Once I decided on a folder hierarchy (/rooms/00, /rooms/01, etc.), and dropped files into the folder, I was ready to start. What follows is some code snippets that demonstrate the generation of the file names, the reading of the files, and the display of file contents to the terminal window.

.data
buffer: .space 1200
filepath: .asciiz "/home/jonathan/Desktop/3410/Final Project/rooms/" # hard coded rooms directory
room: .asciiz "00/"
readFile: .asciiz "n"
filename: .space 70
description: .space 1200
name: .space 32
input: .space 32
nameHolder: .asciiz "n" #"name" # designates the name file
descHolder: .asciiz "d" # designates the description file
inputHolder: .asciiz "i" # designates the input file

# text segment
.text

####################################################
# general room procedure
# configurable by room files: name, description, input
####################################################
BuildRoom:
jal GetName # get name for room
jal GetDescription # get description for room
jal GetInput # get input for room
j PlayRoom

GetName:
move $t7, $ra
lb $t0, nameHolder
la $t1, readFile
sb $t0, ($t1)
jal GetFilename
jal FileOpen

# print room name
li $v0, 4
la $a0, buffer
syscall
jr $t7

GetDescription:
move $t7, $ra
lb $t0, descHolder
la $t1, readFile
sb $t0, ($t1)
jal GetFilename
jal FileOpen

#print room description
li $v0, 4
la $a0, buffer
syscall
jr $t7

GetInput:
move $t7, $ra
la $t0, inputHolder
sb $t0, readFile
jal GetFilename
jal FileOpen
lw $t0, buffer # buffer now stores inputs
#sw $t0, input
jr $t7

PlayRoom:

blah blah blah

j End

####################################################
# procedure to concatenate filepath, room, and
# specified file to open for room configuration
####################################################
GetFilename:
la $s1, filename # will be writing bytes to filename
la $s2, filepath # loading generic filepath into $s2
la $s3, room # loading room number into $s3
la $s4, readFile # loading name of file to read into $s4
j ConcFilepath

ConcFilepath: 
lb $t0, ($s2) # get character at address 
beqz $t0, ConcRoomNum
sb $t0, ($s1) # else store current character in the buffer 
addi $s2, $s2, 1 # string1 pointer points a position forward 
addi $s1, $s1, 1 # same for filename pointer 
j ConcFilepath # loop

# I hate code repetition, but this makes the app
# more flexible
ConcRoomNum:
lb $t0, ($s3)
beqz $t0, ConcFile
sb $t0, ($s1)
addi $s3, $s3, 1
addi $s1, $s1, 1
j ConcRoomNum

ConcFile:
lb $t0, ($s4)
beqz $t0, ReturnFile
sb $t0, ($s1)
addi $s4, $s4, 1
addi $s1, $s1, 1
j ConcFile

ReturnFile:
jr $ra # filepath is all written out, so return

...

####################################################
# procedure to open file specified in filename space
####################################################
FileOpen:
# open the file for reading
li $v0, 13 # system call code for opening a file
la $a0, filename # loading the file name
li $a1, 0 # loading said file for reading
li $a2, 0 # mode is ignored
syscall # opening the file (file descriptor returned in $v0)
move $s0, $v0 # save the file descriptor

# read from the file
li $v0, 14 # system call for reading from a file
move $a0, $s0 # file descriptor to read
la $a1, buffer # address of buffer to read into
li $a2, 1200 # max read of 1200 characters
syscall # reading from file into buffer

# close the file
li $v0, 16 # system call code for closing a file
move $a0, $s0 # file descriptor to close
syscall # close the file

jr $ra # file has been read into address 'buffer'

####################################################
# final exit code
####################################################
End:
li $v0, 10 # exiting the program
syscall # booyah!


Like I mentioned, this is not all of the code. However, I would like to pass on to whatever poor soul is seeing this while searching for an answer to his MIPS I/O question: everything's about the bytes. The 'lb' and 'sb' syntax is what makes the string manipulation world go 'round. Everything else is pretty simple.