MIPS汇编PPM图像处理程序故障:无法写入输出文件
问题描述
我写了一段MIPS汇编程序,功能是读取PPM图像文件,将每个RGB值加10后写入同目录下的question1_output.ppm新文件,同时计算原图像和新图像的RGB平均值并打印到控制台。但运行时新文件始终为空,在QtSpim中执行只输出"268501290"就停止了。以下是我的代码和PPM文件示例:
原代码
.data original_image: .asciiz "C:/Users/erinl/OneDrive - University of Cape Town/2. Second Year/Semester 2/CSC2002S/Assignments/Arch_Assigmnet1/house_64_in_ascii_crlf.ppm" bright_image: .asciiz "C:/Users/erinl/OneDrive - University of Cape Town/2. Second Year/Semester 2/CSC2002S/Assignments/Arch_Assigmnet1/question1_output.ppm" buffer: .space 12 total_pixels: .word 0 max_value: .word 255 newline: .asciiz "\n" buffer1: .space 4096 .text .globl main #Function to open file for reading open_file_read: li $v0, 13 syscall move $t0, $v0 #Save file descriptor jr $ra #Function to open a file for writing open_file_write: li $v0, 13 li $a1, 1 syscall move $t0, $v0 #Save the file descriptor jr $ra #Function to read integer from file integer_read: li $v0, 14 syscall jr $ra #Function to write integer to file integer_write: li $v0, 11 syscall jr $ra #Function to read header information of original PPM file read_ppm_header: #Open original PPM file for reading la $a0, original_image jal open_file_read #Read & ignore first four lines (header) li $t1, 4 read_header_loop: beqz $t1, read_header_complete la $a1, buffer jal integer_write addi $t1, $t1, -1 j read_header_loop read_header_complete: jr $ra #Function to write header information to new PPM file write_ppm_header: #Open the new PPM file for writing la $a0, bright_image jal open_file_write #Write first four lines (header) li $t1, 4 write_header_loop: beqz $t1, header_write_done la $a1, buffer jal write_integer addi $t1, $t1, -1 j write_header_loop header_write_done: jr $ra #Function to process PPM image process_ppm_image: la $a0, original_image jal open_file_read #Open new PPM file for writing la $a0, bright_image jal open_file_write #Read & write header information jal read_ppm_header jal write_ppm_header #Initialize variables li $t2, 0 #Sum of R values li $t3, 0 #Sum of G values li $t4, 0 #Sum of B values li $t5, 0 #Pixel count #Main loop to process pixels process_pixels: #Read RGB values from original file la $a1, buffer jal integer_read lw $t6, 0($a1) addi $a1, $a1, 4 jal integer_read lw $t7, 0($a1) addi $a1, $a1, 4 jal integer_read lw $t8, 0($a1) #Increase each RGB value by 10 addi $t6, $t6, 10 #R value addi $t7, $t7, 10 #G value addi $t8, $t8, 10 #B value #Stop values at 255 if exceed bgt $t6, $t9, stop_red bgt $t7, $t9, stop_green bgt $t8, $t9, stop_blue j update_pixel stop_red: li $t6, 255 #Stop R value to 255 j update_pixel stop_green: li $t7, 255 #Stop G value to 255 j update_pixel stop_blue: li $t8, 255 #Stop B value to 255 update_pixel: #Write updated RGB values to new file la $a1, buffer sw $t6, 0($a1) addi $a1, $a1, 4 sw $t7, 0($a1) addi $a1, $a1, 4 sw $t8, 0($a1) #Update sums & pixel count add $t2, $t2, $t6 add $t3, $t3, $t7 add $t4, $t4, $t8 addi $t5, $t5, 1 #Check if processed all pixels beq $t5, $t0, processing_complete #Otherwise cont. processing next pixel j process_pixels processing_complete: #Calculate average RGB values for original image div $t2, $t5 #Calculate R average mflo $t6 div $t3, $t5 #Calculate G average mflo $t7 div $t4, $t5 #Calculate B average mflo $t8 #Print averages as double values mtc1 $t6, $f0 mtc1 $t7, $f1 mtc1 $t8, $f2 cvt.d.w $f0, $f0 cvt.d.w $f1, $f1 cvt.d.w $f2, $f2 #Print average R value li $v0, 4 la $a0, newline syscall mov.s $f12, $f0 syscall #Print average G value li $v0, 4 la $a0, newline syscall mov.s $f12, $f1 syscall #Print average B value li $v0, 4 la $a0, newline syscall mov.s $f12, $f2 syscall #Close files li $v0, 16 #Close file syscall move $a0, $t0 #File descriptor to close syscall li $v0, 16 #Close file syscall move $a0, $t1 #File descriptor to close syscall jr $ra main: li $v0, 1 la $a0, buffer1 li $a1, 12 syscall #Initialize total_pixels with total number pixels li $t0, 0 # Replace with the actual number of pixels la $t1, buffer1 li $t2, 4096 loop: lbu $t3, 0($t1) addi $t0, $t0, 1 #Increment t0 by 1 addi $t1, $t1, 1 #Increment the address by 1 addi $t2, $t2, -1 #Decrement the remaining bytes by 1 bnez $t2, loop #If there are remaining bytes, continue the loop done: #Initialize max_value with maximum value for stopping li $t9, 255 #Call function to process PPM image jal process_ppm_image #Exit program li $v0, 10 syscall
PPM文件示例
P3 #header # Hse 64 64 #image dimensions 255 #max-value 177 192 179 184 196 174 171 192 174 189 205 192 176 196 180 183 190 168 172 196 185 ...
问题根源分析
你的程序存在多个核心逻辑错误,导致文件无法正确读写、程序提前终止:
- 主函数开头错误调用:
main里第一句用syscall 1(打印整数),但$a0是buffer1的内存地址,所以输出的是地址值268501290,这就是你看到的初始输出。 - 文件操作逻辑混乱:重复打开文件、读写函数用错系统调用(比如用
syscall 14直接读整数,用syscall 11写整数),PPM是ASCII格式,需要先读取ASCII字符串再转整数,写入时也要把整数转成ASCII字符串。 - 头部处理完全错误:读取头部时用写操作函数,没有解析宽高计算总像素数,导致后续像素循环无法终止。
- 寄存器管理不当:临时寄存器(如
$t0)被多个函数覆盖,文件描述符丢失;$t9的max值在main中初始化,但跨函数调用后可能失效。 - 平均值打印错误:混淆了字符串打印和浮点数打印的系统调用,导致无法正确输出平均值。
修正后的代码
.data original_image: .asciiz "C:/Users/erinl/OneDrive - University of Cape Town/2. Second Year/Semester 2/CSC2002S/Assignments/Arch_Assigmnet1/house_64_in_ascii_crlf.ppm" bright_image: .asciiz "C:/Users/erinl/OneDrive - University of Cape Town/2. Second Year/Semester 2/CSC2002S/Assignments/Arch_Assigmnet1/question1_output.ppm" buffer: .space 64 # 足够存ASCII整数和换行 width: .word 0 height: .word 0 max_val: .word 255 newline: .asciiz "\n" space: .asciiz " " avg_r_msg: .asciiz "Original R Average: " avg_g_msg: .asciiz "Original G Average: " avg_b_msg: .asciiz "Original B Average: " new_avg_r_msg: .asciiz "Brightened R Average: " new_avg_g_msg: .asciiz "Brightened G Average: " new_avg_b_msg: .asciiz "Brightened B Average: " .text .globl main # 打开文件读取,返回文件描述符在$v0 open_read: li $v0, 13 li $a1, 0 # 只读模式 li $a2, 0 syscall jr $ra # 打开文件写入,返回文件描述符在$v0 open_write: li $v0, 13 li $a1, 1 # 写入模式 li $a2, 0 syscall jr $ra # 从文件读取一个ASCII整数到$v0 # $a0: 文件描述符 read_int: la $t0, buffer li $t1, 0 # 缓冲区索引 read_int_loop: li $v0, 14 # 读一个字节 move $a1, $t0 add $a1, $a1, $t1 li $a2, 1 syscall beq $v0, $zero, read_int_done # 文件结束 lbu $t2, 0($a1) # 跳过空格、换行、制表符 beq $t2, 32, read_int_loop beq $t2, 10, read_int_loop beq $t2, 9, read_int_loop # 跳过注释(#开头) beq $t2, 35, skip_comment # 是数字,开始解析 parse_int: sub $t2, $t2, 48 # 转成数字 mul $v0, $v0, 10 add $v0, $v0, $t2 addi $t1, $t1, 1 # 读下一个字节 li $v0, 14 move $a1, $t0 add $a1, $a1, $t1 li $a2, 1 syscall lbu $t2, 0($a1) blt $t2, 48, read_int_done bgt $t2, 57, read_int_done j parse_int skip_comment: # 读到换行为止 skip_comment_loop: li $v0, 14 move $a1, $t0 add $a1, $a1, $t1 li $a2, 1 syscall lbu $t2, 0($a1) bne $t2, 10, skip_comment_loop j read_int_loop read_int_done: jr $ra # 将$a0的整数写入文件($a1是文件描述符) write_int: # 先把整数转成ASCII字符串 la $t0, buffer li $t1, 0 # 数字位数 li $t2, 10 # 处理0的情况 beq $a0, $zero, write_zero write_int_rev: div $a0, $t2 mflo $a0 mfhi $t3 add $t3, $t3, 48 sb $t3, 0($t0) addi $t0, $t0, 1 addi $t1, $t1, 1 bne $a0, $zero, write_int_rev # 反转字符串 la $t4, buffer add $t5, $t4, $t1 addi $t5, $t5, -1 reverse_loop: bge $t4, $t5, reverse_done lbu $t6, 0($t4) lbu $t7, 0($t5) sb $t7, 0($t4) sb $t6, 0($t5) addi $t4, $t4, 1 addi $t5, $t5, -1 j reverse_loop reverse_done: # 写入字符串 li $v0, 15 move $a0, $a1 la $a1, buffer move $a2, $t1 syscall jr $ra write_zero: li $t3, 48 sb $t3, 0($t0) li $v0, 15 move $a0, $a1 la $a1, buffer li $a2, 1 syscall jr $ra # 写入换行符到文件 write_newline: li $v0, 15 move $a1, newline li $a2, 1 syscall jr $ra # 写入空格到文件 write_space: li $v0, 15 move $a1, space li $a2, 1 syscall jr $ra # 读取PPM头部,返回文件描述符在$v0,宽存在width,高存在height,max_val更新 read_ppm_header: la $a0, original_image jal open_read move $s0, $v0 # 保存文件描述符到$s0 # 读取魔数P3 jal read_int # 读取宽度 jal read_int sw $v0, width # 读取高度 jal read_int sw $v0, height # 读取max值 jal read_int sw $v0, max_val move $v0, $s0 # 返回文件描述符 jr $ra # 写入PPM头部到新文件 # $a0: 新文件
相关产品推荐
相关产品推荐

