# This program uses a small set of procedures to # manage pgm (Portable Gray Map) formatted files. # The getPGM and putPGM procedures in this program only # work with binary (P5) formatted pgm files, not the # associated text (P2) formatted files. You can use the # free imageviewer program Irfanview to convert files # from any image format to the binary pgm format. # The procedure getPGM reads a binary pgm file and returns # an image header block and an array of integer image values. # The procedure putPGM writes the data back out in binary pgm format. # Assuming that $a0 points to the image header block, the # elements of the header are formatted as follows: # # 0($a0) column count # 4($a0) row count # 8($a0) max value # 12($a0) image buffer address # # The entries in the image buffer are integer values. The values # for the top row of pixels are first, followed by the values for # the second row, etc. # pgm is a simple format for files holding gray scale images. # getPGM and putPGM convert between the header and image buffer # format described above and the pgm file format. # At the time of this writing, information about the netpbm # family of image formats is available at # http://netpbm.sourceforge.net/doc/pgm.html # usage: ... # # Name of an existing binary format Portable Gray Map file # Name of the output file # Number of a filter procedure, eg, 0 or 1 or ... # As many filters as desired can be specified. .data # The following entries go in the data segment strAuthor: .asciiz "Author: Doug Johnson\n" strHdrA: .asciiz "(col x row) : (" strHdrB: .asciiz " x " strHdrC: .asciiz "), max allowed value: " strNewline: .asciiz "\n" strSpace: .asciiz " " strRunComplete: .asciiz "\nRun is complete.\n" filterTable: .word filter0 # stats (print to console) .word filter1 # invert image .word filter2 # darker by 10 .word filter3 # brighter by 10 .word filter4 # posterize .word filter5 # stretch gray range .word filter6 # TBD filterTableEnd: # main program arguments # $a0 - argc - number of user arguments # $a1 - argv - address of array of string pointers, one per user arg # $a2 - envp - address of array of string pointers, one per # environment string. # stack frame layout # 44($sp) reserved # 40($sp) $ra # 36($sp) $s5 # 32($sp) $s4 # 28($sp) $s3 # 24($sp) $s2 # 20($sp) $s1 # 16($sp) $s0 # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space .text # The following entries go in the text (program code) segment main: subu $sp,$sp,48 # create stack frame sw $ra,40($sp) # save return address sw $s5,36($sp) # save $s5 sw $s4,32($sp) # save $s4 sw $s3,28($sp) # save $s3 sw $s2,24($sp) # save $s2 sw $s1,20($sp) # save $s1 sw $s0,16($sp) # save $s0 move $s0,$a0 # $s0 = argc move $s1,$a1 # $s1 = argv move $s2,$a2 # $s2 = envp la $a0,strAuthor # authorship credit li $v0,4 syscall blt $s0,1,mainExit # exit if user did not specify a source file # print the argument list move $t0,$s1 # $t0 = next = argv sll $t1,$s0,2 # convert arg count to byte offset add $t1,$t0,$t1 # $t1 = limit address mainArgs: lw $a0,0($t0) # string address li $v0,4 # print_string syscall # print la $a0,strSpace li $v0,4 syscall addi $t0,4 # next address bne $t0,$t1,mainArgs # loop if not done la $a0,strNewline li $v0,4 syscall # read the source file lw $a0,0($s1) # address of file name string jal getPGM # read the PGM file move $s3,$v0 # $s3 = header address # print information from the image header la $a0,strHdrA # begin info printout li $v0,4 syscall lw $a0,0($s3) # print column count li $v0,1 syscall la $a0,strHdrB li $v0,4 syscall lw $a0,4($s3) # print row count li $v0,1 syscall la $a0,strHdrC li $v0,4 syscall lw $a0,8($s3) # print max value li $v0,1 syscall la $a0,strNewline li $v0,4 syscall # exit if user only specified an input file blt $s0,2,mainExit # did user specify any filter operations? move $s4,$s1 # $s4 = argv add $s4,$s4,8 # $s4 = address of first filter number move $s5,$s0 # $s5 = argc sll $s5,$s5,2 # $s5 = number of bytes in argv add $s5,$s5,$s1 # $s5 = address after argv beq $s4,$s5,mainWrite # skip if no filters specified # run the specified filters mainFilter: lw $a0,0($s4) # filter number argument jal parseInt # decode it la $t0,filterTable # beginning of filter address list sll $v0,$v0,2 # byte offset into table add $t0,$t0,$v0 # address in table to use la $t1,filterTableEnd # check table limits bge $t0,$t1,mainSkip # skip if invalid filter move $a0,$s3 # image header address lw $t0,0($t0) # load filter procedure address jalr $t0 # jump to the filter mainSkip: add $s4,$s4,4 # point to next argument bne $s4,$s5,mainFilter # loop if more filters # write the destination file mainWrite: lw $a0,4($s1) # address of file name string move $a1,$s3 # address of image header block jal putPGM # write the PGM file la $a0,strRunComplete li $v0,4 syscall mainExit: lw $ra,40($sp) # restore return address lw $s5,36($sp) # restore $s5 lw $s4,32($sp) # restore $s4 lw $s3,28($sp) # restore $s3 lw $s2,24($sp) # restore $s2 lw $s1,20($sp) # restore $s1 lw $s0,16($sp) # restore $s0 addu $sp,$sp,48 # release stack frame jr $ra # return #-------------------------------------------------------------------- # void filter6(int *header) # The function of this filter is not defined. .text filter6: jr $ra #-------------------------------------------------------------------- # void filter5(int *header) # Stretch the shade pixel values to cover the range 0 to max value. # If the entire image is one color, nothing is done to the image. # Otherwise, the min and max values are found, then every value is # adjusted using ((pixel-min)*(max allowed)) / (max-min). # stack frame # # 36($sp) reserved # 32($sp) $ra # 28($sp) $s3 # 24($sp) $s2 # 20($sp) $s1 # 16($sp) $s0 # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space # register usage # $s0 image header address # $s1 min pixel # $s2 max pixel # $s3 unused .text filter5: subu $sp,$sp,40 # reserve stack space sw $ra,32($sp) # save $ra sw $s3,28($sp) # save $s3 sw $s2,24($sp) # save $s2 sw $s1,20($sp) # save $s1 sw $s0,16($sp) # save $s0 # find min and max pixel values move $s0,$a0 # $s0 = image header address move $a0,$s0 # image header address jal minPixel # find min move $s1,$v0 # $s1 = min move $a0,$s0 # image header address jal maxPixel # find max move $s2,$v0 # $s2 = max beq $s1,$s2,f5exit # can't stretch a single color # set up to look at each pixel move $t0,$s0 # $t0 = image header address lw $t1,12($t0) # $t1 = image buffer address lw $t2,0($t0) # $t2 = col count lw $t3,4($t0) # $t3 = row count mul $t4,$t2,$t3 # $t4 = pixel count sll $t4,$t4,2 # $t4 = number of bytes in image buffer add $t4,$t1,$t4 # $t4 = address following image buffer # calculate scale factor elements lw $t5,8($s0) # $t5 = max allowed pixel value subu $t7,$s2,$s1 # $t7 = max - min # adjust each pixel f5a: lw $t6,0($t1) # $t6 = pixel value subu $t6,$t6,$s1 # $t6 = subtract offset mul $t6,$t6,$t5 # $t6 = mul by range div $t6,$t6,$t7 # $t6 = div by domain sw $t6,0($t1) # store new pixel value add $t1,4 # point to next word bne $t1,$t4,f5a # loop if more pixels f5exit: lw $ra,32($sp) # restore $ra lw $s3,28($sp) # restore $s3 lw $s2,24($sp) # restore $s2 lw $s1,20($sp) # restore $s1 lw $s0,16($sp) # restore $s0 addu $sp,$sp,40 # release stack space jr $ra #-------------------------------------------------------------------- # void filter4(int *header) # Posterize the image by zeroing out the low order 4 bits. This reduces # the number of distinct shades in the image by a factor of 16. .text filter4: move $t0,$a0 # $t0 = image header address # set up to look at each pixel lw $t1,12($t0) # $t1 = image buffer address lw $t2,0($t0) # $t2 = col count lw $t3,4($t0) # $t3 = row count mul $t4,$t2,$t3 # $t4 = pixel count sll $t4,$t4,2 # $t4 = number of bytes in image buffer add $t4,$t1,$t4 # $t4 = address following image buffer # mask off the low order bits in each pixel li $t5,0xFFFFFFF0 # $t5 = mask f4a: lw $t6,0($t1) # $t6 = pixel value and $t6,$t6,$t5 # $t6 = pixel value with low order masked out sw $t6,0($t1) # store new pixel value add $t1,4 # point to next word bne $t1,$t4,f4a # loop if more pixels jr $ra #-------------------------------------------------------------------- # void filter3(int *header) # Brighten image by 10 counts # stack frame layout # 20($sp) reserved # 16($sp) $ra # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space .text filter3: subu $sp,$sp,24 # reserve stack frame sw $ra,16($sp) # save return address li $a1,10 # delta count is 10 jal filterBrightness lw $ra,16($sp) # restore return address addu $sp,$sp,24 # release stack frame jr $ra #-------------------------------------------------------------------- # void filter2(int *header) # Darken image by 10 counts # stack frame layout # 20($sp) reserved # 16($sp) $ra # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space .text filter2: subu $sp,$sp,24 # reserve stack frame sw $ra,16($sp) # save return address li $a1,-10 # delta count is -10 jal filterBrightness lw $ra,16($sp) # restore return address addu $sp,$sp,24 # release stack frame jr $ra #-------------------------------------------------------------------- # void filterBrightness(int *header,int delta) # Darken or lighten the image by a given amount # $a0 image header address # $a1 delta amount. The pixel value is clamped at 0 and max value. .text filterBrightness: move $t0,$a0 # $t0 = image header address # set up to look at each pixel lw $t1,12($t0) # $t1 = image buffer address lw $t2,0($t0) # $t2 = col count lw $t3,4($t0) # $t3 = row count mul $t4,$t2,$t3 # $t4 = pixel count sll $t4,$t4,2 # $t4 = number of bytes in image buffer add $t4,$t1,$t4 # $t4 = address following image buffer # change each pixel as requested lw $t5,8($t0) # $t5 = max value allowed f23a: lw $t6,0($t1) # $t6 = pixel value add $t6,$t6,$a1 # $t6 = pixel + delta ble $t6,$t5,f23b # skip if pixel <= max move $t6,$t5 # pixel = max f23b: bgez $t6,f23c # skip if pixel >= 0 move $t6,$zero # pixel = 0 f23c: sw $t6,0($t1) # store new pixel value add $t1,4 # point to next word bne $t1,$t4,f23a # loop if more pixels jr $ra #-------------------------------------------------------------------- # void filter1(int *header) # Invert the image by subtracting each value from the given max value .text filter1: move $t0,$a0 # $t0 = image header address # set up to look at each pixel lw $t1,12($t0) # $t1 = image buffer address lw $t2,0($t0) # $t2 = col count lw $t3,4($t0) # $t3 = row count mul $t4,$t2,$t3 # $t4 = pixel count sll $t4,$t4,2 # $t4 = number of bytes in image buffer add $t4,$t1,$t4 # $t4 = address following image buffer # invert each pixel lw $t5,8($t0) # $t5 = max value allowed f1a: lw $t6,0($t1) # $t6 = pixel value sub $t6,$t5,$t6 # $t6 = max - pixel sw $t6,0($t1) # store new pixel value add $t1,4 # point to next word bne $t1,$t4,f1a # loop if more pixels jr $ra #-------------------------------------------------------------------- # void filter0(int *header) # Print various statistics related to the image. # stack frame # # 28($sp) reserved # 24($sp) $ra # 20($sp) $s1 # 16($sp) $s0 # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space .data strF0a: .asciiz "(min,max) : (" strF0b: .asciiz "," strF0c: .asciiz ")" .text filter0: subu $sp,$sp,32 # reserve stack space sw $ra,24($sp) # save $ra sw $s1,20($sp) # save $s1 sw $s0,16($sp) # save $s0 # find min and max pixel values move $s0,$a0 # $s0 = image header address la $a0,strF0a # begin info printout li $v0,4 syscall move $a0,$s0 # image header address jal minPixel # find min move $a0,$v0 # print it li $v0,1 syscall la $a0,strF0b li $v0,4 syscall move $a0,$s0 # image header address jal maxPixel # find max move $a0,$v0 # print it li $v0,1 syscall la $a0,strF0c li $v0,4 syscall la $a0,strNewline li $v0,4 syscall lw $ra,24($sp) # restore $ra lw $s1,20($sp) # restore $s1 lw $s0,16($sp) # restore $s0 addu $sp,$sp,32 # release stack space jr $ra #-------------------------------------------------------------------- # int minPixel(int *header) # Find the minimum pixel value in the image. # $a0 image header address # $v0 the minimum pixel value in the image .text minPixel: move $t0,$a0 # $t0 = image header address # set up to look at each pixel lw $t1,12($t0) # $t1 = image buffer address lw $t2,0($t0) # $t2 = col count lw $t3,4($t0) # $t3 = row count mul $t4,$t2,$t3 # $t4 = pixel count sll $t4,$t4,2 # $t4 = number of bytes in image buffer add $t4,$t1,$t4 # $t4 = address following image buffer li $t5,0x7FFFFFFF # $t5 = max possible int value minPixelA: lw $t6,0($t1) # $t6 = pixel value bgt $t6,$t5,minPixelB # skip if pixel > current min move $t5,$t6 # remember the new min value minPixelB: add $t1,4 # point to next word bne $t1,$t4,minPixelA # loop if more pixels move $v0,$t5 # return the min value jr $ra #-------------------------------------------------------------------- # int maxPixel(int *header) # Find the maximum pixel value in the image. # $a0 image header address # $v0 the maximum pixel value in the image .text maxPixel: move $t0,$a0 # $t0 = image header address # set up to look at each pixel lw $t1,12($t0) # $t1 = image buffer address lw $t2,0($t0) # $t2 = col count lw $t3,4($t0) # $t3 = row count mul $t4,$t2,$t3 # $t4 = pixel count sll $t4,$t4,2 # $t4 = number of bytes in image buffer add $t4,$t1,$t4 # $t4 = address following image buffer li $t5,0 # $t5 = min possible int value maxPixelA: lw $t6,0($t1) # $t6 = pixel value blt $t6,$t5,maxPixelB # skip if pixel < current min move $t5,$t6 # remember the new max value maxPixelB: add $t1,4 # point to next word bne $t1,$t4,maxPixelA # loop if more pixels move $v0,$t5 # return the max value jr $ra #-------------------------------------------------------------------- # int putPGM(char *s, int *header) # $a0 - address of file name string # $a1 - address of image header to write # $v0 - 1 if success, 0 if error # # Format of the image header block is defined in getPGM. # # The max value is whatever the pgm file said it was. # It will generally be 255 for 1 byte per pixel gray maps. # # The image buffer has integer entries, one 32-bit word per pixel. # The data is in row order (across the first row, across the # second row, etc.) There are column*row words in the buffer. # stack frame layout # 52($sp) unused # 48($sp) $ra # 44($sp) $s7 # 40($sp) $s6 # 36($sp) $s5 # 32($sp) $s4 # 28($sp) $s3 # 24($sp) $s2 # 20($sp) $s1 # 16($sp) $s0 # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space # register usage # $s0 file handle # $s1 file buffer address # $s2 current character address # $s3 column count # $s4 row count # $s5 max value # $s6 file name string address # $s7 image header address .data strFileWriteError: .asciiz "\nPGM file write error.\n" .text putPGM: subu $sp,$sp,56 # create stack frame sw $ra,48($sp) # save return address sw $s7,44($sp) # save $s7 sw $s6,40($sp) # save $s6 sw $s5,36($sp) # save $s5 sw $s4,32($sp) # save $s4 sw $s3,28($sp) # save $s3 sw $s2,24($sp) # save $s2 sw $s1,20($sp) # save $s1 sw $s0,16($sp) # save $s0 move $s6,$a0 # $s6 = file name string address move $s7,$a1 # $s7 = image header address lw $s3,0($s7) # $s3 = column count lw $s4,4($s7) # $s4 = row count lw $s5,8($s7) # $s5 = max value li $a0,1024 # buffer size li $v0,9 # allocate memory code syscall move $s1,$v0 # $s1 = the buffer address move $s2,$s1 # $s2 = current character address # encode the values needed in the file header li $t0,'P' # required first byte sb $t0,0($s2) add $s2,$s2,1 li $t0,'5' # required second byte sb $t0,0($s2) add $s2,$s2,1 li $t0,'\n' # newline sb $t0,0($s2) add $s2,$s2,1 move $a0,$s2 move $a1,$s3 # column count jal encodeInt move $s2,$v0 li $t0,'\n' # newline sb $t0,0($s2) add $s2,$s2,1 move $a0,$s2 move $a1,$s4 # row count jal encodeInt move $s2,$v0 li $t0,'\n' # newline sb $t0,0($s2) add $s2,$s2,1 move $a0,$s2 move $a1,$s5 # max value jal encodeInt move $s2,$v0 li $t0,'\n' # newline sb $t0,0($s2) add $s2,$s2,1 # open file for writing (create if necessary) move $a0,$s6 # file name li $a1,1 # O_WRONLY ori $a1,0x8000 # O_BINARY ori $a1,0x0100 # O_CREAT ori $a1,0x0200 # O_TRUNC li $a2,0 # mode = 0 ori $a2,0x80 # _S_IWRITE: 0000200 li $v0,13 # open_file syscall move $s0,$v0 # $s0 = the file handle bltz $v0,putError # negative handle indicates error # write the file header move $a0,$s0 # file handle to write move $a1,$s1 # buffer for the file header sub $a2,$s2,$s1 # calculate number of bytes in header li $v0,15 # write_file syscall bltz $v0,putError # negative byte count indicates error # allocate an output file buffer mul $a0,$s3,$s4 # pixel count = col * row sll $a0,$a0,1 # 2 bytes per pixel max li $v0,9 # allocate memory code syscall move $s1,$v0 # $s1 = the buffer address # pack the image data to byte or half word boundaries mul $t0,$s3,$s4 # $t0 = pixel count = col * row lw $t1,12($s7) # $t1 = source address move $t2,$s1 # $t2 = destination address blt $s5,256,putterC # 1 or 2 bytes per pixel? # 2 bytes per pixel # no test cases have been run on this format ... putterB: lw $t3,0($t1) # get pixel word sh $t3,0($t2) # save pixel halfword add $t1,$t1,4 # point to next source word add $t2,$t2,2 # point to next destination byte sub $t0,$t0,1 # decrement pixel count bnez $t0,putterB # loop if more to do b putterD # all done # 1 byte per pixel putterC: lw $t3,0($t1) # get pixel word sb $t3,0($t2) # save pixel byte add $t1,$t1,4 # point to next source word add $t2,$t2,1 # point to next destination byte sub $t0,$t0,1 # decrement pixel count bnez $t0,putterC # loop if more to do # write the image data putterD: move $a0,$s0 # file handle to write move $a1,$s1 # buffer for the image data mul $a2,$s3,$s4 # pixel count = col * row blt $s5,256,putterE # 1 or 2 bytes per pixel? sll $a2,$a2,1 # 2 bytes per pixel putterE: li $v0,15 # write_file syscall bltz $v0,putError # negative byte count indicates error move $a0,$s0 # file handle li $v0,16 # close_file syscall li $v0,1 # success return putExit: lw $ra,48($sp) # restore return address lw $s7,44($sp) # restore $s7 lw $s6,40($sp) # restore $s6 lw $s5,36($sp) # restore $s5 lw $s4,32($sp) # restore $s4 lw $s3,28($sp) # restore $s3 lw $s2,24($sp) # restore $s2 lw $s1,20($sp) # restore $s1 lw $s0,16($sp) # restore $s0 addu $sp,$sp,56 # release stack frame jr $ra # return putError: la $a0,strFileWriteError li $v0,4 syscall li $v0,0 # error return b putExit #-------------------------------------------------------------------- # void *getPGM(char *s) # $a0 - address of file name string # $v0 - pgm image header block address or 0 if error # # Format of the returned image header block is: # # 0($v0) column count # 4($v0) row count # 8($v0) max value # 12($v0) image buffer address # # The max value is whatever the pgm file said it was. # It will generally be 255 for 1 byte per pixel gray maps. # # The image buffer is integer entries, one 32-bit word per pixel. # The data is in row order (across the first row, across the # second row, etc.) There are column*row words in the buffer. # stack frame layout # 52($sp) unused # 48($sp) $ra # 44($sp) $s7 # 40($sp) $s6 # 36($sp) $s5 # 32($sp) $s4 # 28($sp) $s3 # 24($sp) $s2 # 20($sp) $s1 # 16($sp) $s0 # 12($sp) reserved for callee: $a3 save space # 8($sp) reserved for callee: $a2 save space # 4($sp) reserved for callee: $a1 save space # 0($sp) reserved for callee: $a0 save space # register usage # $s0 file handle # $s1 file read buffer address # $s2 current character address # $s3 column count # $s4 row count # $s5 max value # $s6 file name string address # $s7 image header address .data strFileFormatError: .asciiz "\nPGM file format error.\n" .text getPGM: subu $sp,$sp,56 # create stack frame sw $ra,48($sp) # save return address sw $s7,44($sp) # save $s7 sw $s6,40($sp) # save $s6 sw $s5,36($sp) # save $s5 sw $s4,32($sp) # save $s4 sw $s3,28($sp) # save $s3 sw $s2,24($sp) # save $s2 sw $s1,20($sp) # save $s1 sw $s0,16($sp) # save $s0 # remember file name string address move $s6,$a0 # open file, read the first part, and close move $a0,$s6 # file name li $a1,0 # O_RDONLY ori $a1,0x8000 # O_BINARY li $a2,0 # mode = 0 li $v0,13 # open_file syscall bltz $v0,pgmError # negative handle indicates error move $s0,$v0 # $s0 = the file handle li $a0,1024 # buffer size li $v0,9 # allocate memory code syscall move $s1,$v0 # $s1 = the buffer address move $s2,$s1 # $s2 = current character address move $a0,$s0 # file handle to read move $a1,$s1 # buffer for the header info li $a2,1024 # number of bytes to read li $v0,14 # read_file syscall bltz $v0,pgmError # negative byte count indicates error move $a0,$s0 # file handle li $v0,16 # close_file syscall # analyse the file header and extract the interesting values lbu $t0,0($s2) # check first byte bne $t0,'P',pgmError add $s2,$s2,1 lbu $t0,0($s2) # check second byte bne $t0,'5',pgmError add $s2,$s2,1 move $a0,$s2 # skip over whitespace jal skipOverWhitespace move $s2,$v0 # remember start of column count token move $a0,$s2 # parse column count jal parseInt move $s3,$v0 # $s3 = column count move $a0,$s2 # skip over column count token jal skipToWhitespace move $a0,$v0 # skip over whitespace jal skipOverWhitespace move $s2,$v0 # remember start of row count token move $a0,$s2 # parse row count jal parseInt move $s4,$v0 # $s4 = row count move $a0,$s2 # skip over row count token jal skipToWhitespace move $a0,$v0 # skip over whitespace jal skipOverWhitespace move $s2,$v0 # remember start of max value token move $a0,$s2 # parse max value jal parseInt move $s5,$v0 # $s5 = max value move $a0,$s2 # skip over max value token jal skipToWhitespace add $s2,$v0,1 # skip over single whitespace character # reopen the file move $a0,$s6 # file name string address li $a1,0 # O_RDONLY ori $a1,0x8000 # O_BINARY li $a2,0 # mode = 0 li $v0,13 # open_file syscall bltz $v0,pgmError # negative handle indicates error move $s0,$v0 # $s0 = the file handle # read just the file header move $a0,$s0 # file handle to read move $a1,$s1 # buffer for the file header sub $a2,$s2,$s1 # calculate number of bytes in header li $v0,14 # read_file syscall bltz $v0,pgmError # negative byte count indicates error # allocate an image buffer mul $a0,$s3,$s4 # pixel count = col * row sll $a0,$a0,2 # 4 bytes per pixel eventually li $v0,9 # allocate memory code syscall move $s1,$v0 # $s1 = the buffer address # read the image data move $a0,$s0 # file handle to read move $a1,$s1 # buffer for the file header mul $a2,$s3,$s4 # pixel count = col * row blt $s5,256,pgmA # 1 or 2 bytes per pixel? sll $a2,$a2,1 # 2 bytes per pixel pgmA: li $v0,14 # read_file syscall bltz $v0,pgmError # negative byte count indicates error bne $v0,$a2,pgmError # all bytes must be available else error move $a0,$s0 # file handle li $v0,16 # close_file syscall # redistribute the image data to word boundaries mul $t0,$s3,$s4 # $t0 = pixel count = col * row sll $t1,$t0,2 # $t1 = destination byte count add $t1,$s1,$t1 # $t1 = buffer last word address + 4 sub $t1,$t1,4 # $t1 = buffer last word address blt $s5,256,pgmC # 1 or 2 bytes per pixel? # 2 bytes per pixel # no test cases have been run on this format ... sll $t0,$t0,1 # $t0 = number of source bytes add $t0,$s1,$t0 # $t0 = src last byte address + 2 sub $t0,$t0,2 # $t0 = src last byte address pgmB: lhu $t2,0($t0) # get pixel half word sw $t2,0($t1) # save pixel word sub $t0,$t0,2 # point to previous source half word sub $t1,$t1,4 # point to previous destination word bne $t0,$t1,pgmB # loop if more to do lhu $t2,0($t0) # get pixel half word sw $t2,0($t1) # save pixel word b pgmE # all done # 1 byte per pixel pgmC: add $t0,$s1,$t0 # $t0 = src last byte address + 1 sub $t0,$t0,1 # $t0 = src last byte address pgmD: lbu $t2,0($t0) # get pixel byte sw $t2,0($t1) # save pixel word sub $t0,$t0,1 # point to previous source byte sub $t1,$t1,4 # point to previous destination word bne $t0,$t1,pgmD # loop if more to do lbu $t2,0($t0) # get pixel byte sw $t2,0($t1) # save pixel word # create and fill in image header pgmE: li $a0,20 # image header size li $v0,9 # allocate memory code syscall move $s7,$v0 # $s7 = image header address sw $s3,0($s7) # save column count sw $s4,4($s7) # save row count sw $s5,8($s7) # save max value sw $s1,12($s7) # save image buffer address move $v0,$s7 # return the header address pgmExit: lw $ra,48($sp) # restore return address lw $s7,44($sp) # restore $s7 lw $s6,40($sp) # restore $s6 lw $s5,36($sp) # restore $s5 lw $s4,32($sp) # restore $s4 lw $s3,28($sp) # restore $s3 lw $s2,24($sp) # restore $s2 lw $s1,20($sp) # restore $s1 lw $s0,16($sp) # restore $s0 addu $sp,$sp,56 # release stack frame jr $ra # return pgmError: la $a0,strFileFormatError li $v0,4 syscall li $v0,0 # error b pgmExit #-------------------------------------------------------------------- # char *skipToWhitespace(char *s) # $a0 - address of first character (probably not whitespace) # $v0 - address of next whitespace character # For the purposes of this procedure, whitespace is defined as # blank, tab, newline, carriage return. # Comments start with a '#' and are # counted as whitespace too. # register usage # $a0 pointer to current character # $t0 sign, either +1 or -1 skipToWhitespace: lbu $t0,0($a0) # load character beq $t0,' ',stwExit # space beq $t0,'\t',stwExit # tab beq $t0,'\n',stwExit # newline beq $t0,13,stwExit # return beq $t0,35,stwExit # pgm comment character is '#' addi $a0,$a0,1 # point to next character b skipToWhitespace # look at the next character stwExit: move $v0,$a0 # return current address jr $ra #-------------------------------------------------------------------- # char *skipOverWhitespace(char *s) # $a0 - address of first character (probably whitespace) # $v0 - address of next non-whitespace character # For the purposes of this procedure, whitespace is defined as # blank, tab, newline, carriage return. # Comments start with a '#' and extend to end-of-line and are # counted as whitespace too. # register usage # $a0 pointer to current character # $t0 current character skipOverWhitespace: lbu $t0,0($a0) # load character beq $t0,' ',sowA # space beq $t0,'\t',sowA # tab beq $t0,'\n',sowA # newline beq $t0,13,sowA # return beq $t0,35,sowB # pgm comment character is '#' move $v0,$a0 # return current address jr $ra sowA: # move to the next character addi $a0,$a0,1 # point to next character b skipOverWhitespace # look at the next character sowB: # skip over comment addi $a0,$a0,1 # point to next character lbu $t0,0($a0) # load character beq $t0,'\n',skipOverWhitespace # newline beq $t0,13,skipOverWhitespace # return b sowB #-------------------------------------------------------------------- # char *encodeInt(char *s,int k) # $a0 - address to start writing the encoded integer # $a1 - the integer value to encode # $v0 - next available character address # This procedure uses an 11 character wide field to encode the number # right adjusted then copies it back to the left edge of the field. # register usage # $a0 first available character address # $a1 value to encode # $t0 value remaining to encode # $t1 one digit or encoded numeric character # $t2 put next digit here address # $t3 put rightmost digit here address encodeInt: abs $t0,$a1 # $t0 = positive value to encode add $t3,$a0,10 # $t3 = put rightmost digit here move $t2,$t3 # $t2 = put next digit here eiLoop: rem $t1,$t0,10 # $t1 = least significant remaining digit add $t1,$t1,'0' # add the ascii offset sb $t1,0($t2) # store the character sub $t2,$t2,1 # point to previous position div $t0,$t0,10 # $t0 = digits remaining to encode bnez $t0,eiLoop # encode remaining digits, if any bgez $a1,eiA # no sign for positive values li $t1,'-' # leading minus sb $t1,0($t2) # store it sub $t2,$t2,1 # point to previous position eiA: add $t2,$t2,1 # point to next source character lbu $t1,0($t2) # load character sb $t1,0($a0) # store character add $a0,$a0,1 # point to next destination position bne $t2,$t3,eiA # loop if more to copy move $v0,$a0 # return next open character address jr $ra # return #-------------------------------------------------------------------- # int parseInt(char *s) # $a0 - address of string to parse as signed integer # $v0 - the integer value # register usage # $a0 pointer to current character # $t0 sign, either +1 or -1 # $t1 decoded value so far # $t2 current character / value parseInt: li $v0,0 # error return value is 0 li $t0,1 # default sign is positive move $t1,$zero # initial value is 0 lbu $t2,0($a0) # check for leading sign bne $t2,'+',piA # branch if not + addi $a0,$a0,1 # skip the plus sign b piB # look at the rest of the characters piA: bne $t2,'-',piB # branch if not - addi $a0,$a0,1 # skip the minus sign li $t0,-1 # the sign is negative piB: lbu $t2,0($a0) # load the current character piLoop: sub $t2,$t2,'0' # subtract the ascii offset bltz $t2,piDone # done if invalid character bgt $t2,9,piDone # done if invalid character mul $t1,$t1,10 # shift one decimal column add $t1,$t1,$t2 # add the current digit addi $a0,$a0,1 # point to next character lbu $t2,0($a0) # load the current character b piLoop # try to decode it piDone: move $v0,$t1 # return the decoded value bgtz $t0,piExit # skip if sign is positive neg $v0,$v0 # negate if sign is negative piExit: jr $ra # return