.data
prompt: .asciiz "Input MIPS machine code: "
error_msg: .asciiz "Error: Invalid instruction format.\n"
result_msg: .asciiz "\nMIPS Assembly:\n"
instr_loaded: .asciiz " instructions loaded\n"
reg_names: .asciiz "$zero,$at,$v0,$v1,$a0,$a1,$a2,$a3,$t0,$t1,$t2,$t3,$t4,$t5,$t6,$t7,$s0,$s1,$s2,$s3,$s4,$s5,$s6,$s7,$t8,$t9,$k0,$k1,$gp,$sp,$fp,$ra"
buffer: .space 64 # 用于存储反汇编结果
input_buffer: .space 1024 # 输入缓冲区
instr_buffer: .space 400 # 指令缓冲区 (100条指令)
pc_base: .word 0x00400000 # 程序计数器基址
unknown_instr: .asciiz "???"
unknown_reg: .asciiz "???"
add_op: .asciiz "add "
sub_op: .asciiz "sub "
addi_op: .asciiz "addi "
comma_space: .asciiz ", "
neg_sign: .asciiz "-"
num_buffer: .space 16
space: .asciiz " "
newline: .asciiz "\n"
max_instructions_val: .word 100
instruction_size_val: .word 8
# 全局变量
instructionCount: .word 0 # 指令数量
instructions: .word 0 # 指令数组基址
.text
.globl main
main:
# 初始化指令缓冲区地址
la $t0, instr_buffer
sw $t0, instructions
# 调用 readInstructions
jal readInstructions
# 调用 printResult
jal printResult
# 退出程序
li $v0, 10
syscall
# 函数: readInstructions
readInstructions:
# 保存寄存器上下文
addi $sp, $sp, -20
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $s2, 12($sp)
sw $s3, 16($sp)
# 打印提示信息
li $v0, 4
la $a0, prompt
syscall
# 读取输入
la $a0, input_buffer
li $a1, 1024
li $v0, 8
syscall
# 初始化变量
la $s0, input_buffer # 输入缓冲区地址
la $s1, instr_buffer # 指令缓冲区地址
# 确保缓冲区地址对齐(新增代码)
andi $t0, $s1, 0x3 # 检查地址是否对齐
bnez $t0, align_buffer
li $s2, 0 # 指令计数器
read_loop:
# 从内存加载常量值
lw $t0, max_instructions_val
bge $s2, $t0, read_done
# 查找下一个空格或字符串结束符
lb $t0, 0($s0)
beqz $t0, read_done # 字符串结束
beq $t0, ' ', skip_space
# 解析指令
move $a0, $s0
jal parse_hex_instruction
move $t1, $v0 # 解析结果
# 从内存加载常量值
lw $t2, instruction_size_val
bne $t1, $t2, invalid_instruction
# 转换十六进制字符串为整数
move $a0, $s0
jal hex_str_to_int
move $t3, $v0 # 转换后的指令值
# 计算存储地址(修复对齐问题)
sll $t4, $s2, 2 # 计算偏移量 (×4)
add $t5, $s1, $t4 # 目标地址
# 保存指令到缓冲区(修复后的对齐逻辑)
sw $t3, 0($t5) # 存储指令
# 更新指令计数器
addi $s2, $s2, 1
skip_space:
addi $s0, $s0, 1 # 移动到下一个字符
j read_loop
invalid_instruction:
li $v0, 4
la $a0, error_msg
syscall
j skip_space # 跳过当前无效指令
align_buffer:
# 调整缓冲区地址使其对齐到4字节边界
li $t6, 4 # 加载常量4
sub $t6, $t6, $t0 # 计算需要调整的字节数
# 检查调整后的地址是否超出缓冲区范围
la $t7, instr_buffer # 获取缓冲区起始地址
la $t8, instr_buffer # 获取缓冲区结束地址
addi $t8, $t8, 400 # 缓冲区大小为400字节
add $t9, $t7, $t6 # 计算调整后的地址
bge $t9, $t8, align_error # 如果超出范围,跳转到错误处理
# 使用add指令代替addi,避免立即数表达式问题
add $s1, $s1, $t6 # 正确调整缓冲区地址
# 验证调整后的地址是否对齐
andi $t0, $s1, 0x3 # 重新检查对齐
bnez $t0, align_buffer # 如果仍未对齐,继续调整(理论上不会执行
j read_loop
align_error:
# 处理地址超出范围的错误
li $v0, 4
la $a0, error_msg
syscall
li $v0, 10 # 退出程序
syscall
read_done:
# 保存指令数量到全局变量
sw $s2, instructionCount
# 恢复寄存器上下文并返回
lw $ra, 0($sp)
lw $s0, 4($sp)
lw $s1, 8($sp)
lw $s2, 12($sp)
lw $s3, 16($sp)
addi $sp, $sp, 20
jr $ra
# 函数: parse_hex_instruction
# 输入: $a0 = 字符串地址
# 输出: $v0 = 指令长度(字符数)
parse_hex_instruction:
addi $sp, $sp, -4
sw $s0, 0($sp)
move $s0, $a0 # 保存字符串地址
li $v0, 0 # 长度计数器
parse_loop:
lb $t0, 0($s0)
beqz $t0, parse_done # 字符串结束
beq $t0, ' ', parse_done # 空格分隔
addi $v0, $v0, 1 # 长度加1
addi $s0, $s0, 1 # 移动到下一个字符
j parse_loop
parse_done:
lw $s0, 0($sp)
addi $sp, $sp, 4
jr $ra
# 函数: hex_str_to_int
# 输入: $a0 = 字符串地址
# 输出: $v0 = 转换后的整数
hex_str_to_int:
addi $sp, $sp, -12
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
move $s0, $a0 # 保存字符串地址
li $s1, 0 # 结果
hex_loop:
lb $t0, 0($s0)
beqz $t0, hex_done # 字符串结束
beq $t0, ' ', hex_done # 空格分隔
# 转换单个字符
move $a0, $t0
jal char_to_hex
move $t1, $v0 # 字符对应的十六进制值
# 更新结果
sll $s1, $s1, 4 # 左移4位
or $s1, $s1, $t1 # 合并当前字符的值
addi $s0, $s0, 1 # 移动到下一个字符
j hex_loop
hex_done:
move $v0, $s1 # 返回结果
lw $ra, 0($sp)
lw $s0, 4($sp)
lw $s1, 8($sp)
addi $sp, $sp, 12
jr $ra
# 函数: char_to_hex
# 输入: $a0 = 字符
# 输出: $v0 = 对应的十六进制值 (0-15)
char_to_hex:
li $v0, -1 # 默认返回-1(错误)
# 检查是否为数字
blt $a0, '0', check_alpha
ble $a0, '9', is_digit
check_alpha:
# 检查是否为大写字母
blt $a0, 'A', check_lower
ble $a0, 'F', is_upper
check_lower:
# 检查是否为小写字母
blt $a0, 'a', char_done
ble $a0, 'f', is_lower
char_done:
jr $ra
is_digit:
sub $v0, $a0, '0' # '0'→0, '1'→1, ..., '9'→9
jr $ra
is_upper:
sub $v0, $a0, 'A' # 'A'→0, 'B'→1, ..., 'F'→5
addi $v0, $v0, 10 # 转换为10-15
jr $ra
is_lower:
sub $v0, $a0, 'a' # 'a'→0, 'b'→1, ..., 'f'→5
addi $v0, $v0, 10 # 转换为10-15
jr $ra
# 函数: disassembleInstruction
# 输入: $a0 = 指令值, $a1 = 结果缓冲区地址
# 输出: 无
disassembleInstruction:
addi $sp, $sp, -24
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $s2, 12($sp)
sw $s3, 16($sp)
sw $s4, 20($sp)
move $s0, $a0 # 保存指令值
move $s1, $a1 # 保存结果缓冲区地址
# 提取opcode
srl $t0, $s0, 26
andi $t0, $t0, 0x3F
move $s2, $t0 # 保存opcode
# 判断指令类型
beq $s2, 0, handle_r_type # R-type
# 处理I-type指令
beq $s2, 0x08, handle_addi # addi
# 未知指令
la $t1, unknown_instr
jal strcpy
j disasm_done
handle_r_type:
# 提取R-type字段
srl $t0, $s0, 21
andi $s3, $t0, 0x1F # rs
srl $t0, $s0, 16
andi $s4, $t0, 0x1F # rt
srl $t0, $s0, 11
andi $t1, $t0, 0x1F # rd
andi $t2, $s0, 0x3F # funct
# 判断funct字段
beq $t2, 0x20, handle_add # add
beq $t2, 0x22, handle_sub # sub
# 未知R-type指令
la $t1, unknown_instr
jal strcpy
j disasm_done
handle_add:
la $t1, add_op
jal strcpy
# 添加目标寄存器
move $a0, $t1
jal get_reg_name
jal strcpy
# 添加逗号和空格
la $t1, comma_space
jal strcpy
# 添加源寄存器1
move $a0, $s3
jal get_reg_name
jal strcpy
# 添加逗号和空格
la $t1, comma_space
jal strcpy
# 添加源寄存器2
move $a0, $s4
jal get_reg_name
jal strcpy
j disasm_done
handle_sub:
la $t1, sub_op
jal strcpy
# 添加目标寄存器
move $a0, $t1
jal get_reg_name
jal strcpy
# 添加逗号和空格
la $t1, comma_space
jal strcpy
# 添加源寄存器1
move $a0, $s3
jal get_reg_name
jal strcpy
# 添加逗号和空格
la $t1, comma_space
jal strcpy
# 添加源寄存器2
move $a0, $s4
jal get_reg_name
jal strcpy
j disasm_done
handle_addi:
la $t1, addi_op
jal strcpy
# 添加目标寄存器
move $a0, $s4
jal get_reg_name
jal strcpy
# 添加逗号和空格
la $t1, comma_space
jal strcpy
# 添加源寄存器
move $a0, $s3
jal get_reg_name
jal strcpy
# 添加逗号和空格
la $t1, comma_space
jal strcpy
# 提取并添加立即数
andi $t3, $s0, 0xFFFF # 提取16位立即数
move $a0, $t3
move $a1, $s1
jal print_imm
move $s1, $a1 # 更新缓冲区指针
j disasm_done
disasm_done:
lw $ra, 0($sp)
lw $s0, 4($sp)
lw $s1, 8($sp)
lw $s2, 12($sp)
lw $s3, 16($sp)
lw $s4, 20($sp)
addi $sp, $sp, 24
jr $ra
# 函数: getRegisterName
# 输入: $a0 = 寄存器编号
# 输出: $v0 = 寄存器名称地址
get_reg_name:
addi $sp, $sp, -4
sw $s0, 0($sp)
li $v0, 0 # 初始化返回值为0
# 检查寄存器编号是否有效
blt $a0, 0, reg_done
bge $a0, 32, reg_done
# 计算寄存器名称偏移
la $s0, reg_names
sll $t0, $a0, 2 # 每个名称占4字节(包括逗号)
add $v0, $s0, $t0
# 跳过前导逗号
lb $t1, 0($v0)
beq $t1, ',', skip_comma
j reg_done
skip_comma:
addi $v0, $v0, 1 # 跳过逗号
reg_done:
lw $s0, 0($sp)
addi $sp, $sp, 4
jr $ra
# 函数: printResult
printResult:
addi $sp, $sp, -20
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $s2, 12($sp)
sw $s3, 16($sp)
# 打印结果标题
li $v0, 4
la $a0, result_msg
syscall
# 获取指令数量
lw $s0, instructionCount
beqz $s0, print_done
# 打印指令数量
li $v0, 1
move $a0, $s0
syscall
li $v0, 4
la $a0, instr_loaded
syscall
# 初始化循环变量
li $s1, 0 # 指令索引
la $s2, instr_buffer # 指令缓冲区地址
lw $s3, pc_base # PC基址
print_loop:
bge $s1, $s0, print_done
# 计算指令地址
sll $t0, $s1, 2 # 偏移量 = 索引 × 4
add $t1, $s2, $t0 # 指令实际地址
andi $t8, $t1, 0x3
bnez $t8, print_error # 如果未对齐,跳转到错误
lw $t2, 0($t1) # 加载指令
# 计算PC值
add $t3, $s3, $t0 # PC = 基址 + 偏移量
# 打印PC值(十六进制)
li $v0, 34
move $a0, $t3
syscall
# 打印空格
li $v0, 4
la $a0, space
syscall
# 反汇编指令
la $a1, buffer
move $a0, $t2
jal disassembleInstruction
# 打印反汇编结果
li $v0, 4
la $a0, buffer
syscall
# 打印换行
li $v0, 4
la $a0, newline
syscall
# 更新索引
addi $s1, $s1, 1
j print_loop
print_error:
# 处理地址未对齐的错误
li $v0, 4
la $a0, error_msg
syscall
li $v0, 10 # 退出程序
syscall
print_done:
lw $ra, 0($sp)
lw $s0, 4($sp)
lw $s1, 8($sp)
lw $s2, 12($sp)
lw $s3, 16($sp)
addi $sp, $sp, 20
jr $ra
# 函数: strcpy
# 输入: $t1 = 源字符串地址, $s1 = 目标缓冲区地址
# 输出: 更新后的$s1 = 目标缓冲区地址
strcpy:
lb $t2, 0($t1)
beqz $t2, strcpy_done
sb $t2, 0($s1)
addi $t1, $t1, 1
addi $s1, $s1, 1
j strcpy
strcpy_done:
jr $ra
# 函数: print_imm
# 输入: $a0 = 立即数值, $a1 = 缓冲区地址
# 输出: $a1 = 更新后的缓冲区地址
print_imm:
addi $sp, $sp, -12
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
move $s0, $a0 # 保存立即数
move $s1, $a1 # 保存缓冲区地址
# 检查是否为负数
bgez $s0, print_positive
# 处理负数
la $t0, neg_sign
lb $t1, 0($t0)
sb $t1, 0($s1)
addi $s1, $s1, 1
neg $s0, $s0 # 取绝对值
print_positive:
# 将数字转换为字符串
li $t0, 10
la $t1, num_buffer
li $t2, 0 # 数字位数
digit_loop:
div $s0, $t0
mfhi $t3 # 余数 = 当前位
mflo $s0 # 商 = 剩余数字
addi $t3, $t3, '0' # 转换为ASCII
add $t4, $t1, $t2
sb $t3, 0($t4)
addi $t2, $t2, 1
bnez $s0, digit_loop
# 反转数字字符串
addi $t2, $t2, -1 # 指向最后一位
reverse_loop:
add $t4, $t1, $t2
lb $t3, 0($t4)
sb $t3, 0($s1)
addi $s1, $s1, 1
addi $t2, $t2, -1
bgez $t2, reverse_loop
# 添加字符串结束符
sb $zero, 0($s1)
move $a1, $s1 # 返回更新后的缓冲区地址
lw $ra, 0($sp)
lw $s0, 4($sp)
lw $s1, 8($sp)
addi $sp, $sp, 12
jr $ra 出现错误: