You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
...

问题根源分析

你的程序存在多个核心逻辑错误,导致文件无法正确读写、程序提前终止:

  1. 主函数开头错误调用:main里第一句用syscall 1(打印整数),但$a0是buffer1的内存地址,所以输出的是地址值268501290,这就是你看到的初始输出。
  2. 文件操作逻辑混乱:重复打开文件、读写函数用错系统调用(比如用syscall 14直接读整数,用syscall 11写整数),PPM是ASCII格式,需要先读取ASCII字符串再转整数,写入时也要把整数转成ASCII字符串。
  3. 头部处理完全错误:读取头部时用写操作函数,没有解析宽高计算总像素数,导致后续像素循环无法终止。
  4. 寄存器管理不当:临时寄存器(如$t0)被多个函数覆盖,文件描述符丢失;$t9的max值在main中初始化,但跨函数调用后可能失效。
  5. 平均值打印错误:混淆了字符串打印和浮点数打印的系统调用,导致无法正确输出平均值。

修正后的代码

.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: 新文件
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:02:53