LC3四位加法程序求助:TRAP指令触发非法指令异常
问题排查与LC-3汇编代码优化指导
一、非法指令错误原因排查
你遇到的IllegalInstructionException(提示Undefined Instruction x0000),核心原因是标签缩进错误:
- 在
GET_NUMBER子程序中,LOOP标签没有顶格书写,LC-3汇编器会将缩进的标签识别为非法操作码,编译后对应机器码为x0000,执行到此处就会触发非法指令异常。 - TRAP指令本身是合法的(
x22=PUTS、x23=GETC、x21=OUT),错误并非TRAP指令本身,而是前面的非法标签导致的指令解析错误。
二、代码逻辑核心问题梳理
除了标签错误,代码还有多处逻辑缺陷:
- 数字输入处理逻辑混乱:
- 读取字符后未正确将ASCII字符转换为数字(需减去
x30即十进制48),也未实现num = num * 10 + digit的累加逻辑,无法正确获取输入的多位数。 - 未将读取到的数字存入
NUM1变量,后续LD R1, NUM1读取的是初始值0,完全丢失输入数据。
- 读取字符后未正确将ASCII字符转换为数字(需减去
- 数值转ASCII逻辑错误:
CONVERT_SUM中用sum - 30的方式完全错误,正确的单数字转ASCII应为digit + x30;且多位数需要逐位拆分后逐个转换输出,不能直接对总和做一次转换。
- 寄存器使用违规:
- LC-3子程序调用时,
R0-R3是临时寄存器,子程序内部修改R1后没有保存现场,会破坏主程序的寄存器值;R4-R7是保存寄存器,使用前应先保存原值。
- LC-3子程序调用时,
三、修正后的完整代码
.ORIG x3000 ; 打印第一个输入提示 LEA R0, PROMPT1 TRAP x22 JSR GET_NUMBER ; 获取第一个数字,结果存在R0 ST R0, NUM1 ; 存入NUM1变量 ; 打印第二个输入提示 LEA R0, PROMPT2 TRAP x22 JSR GET_NUMBER ; 获取第二个数字,结果存在R0 ST R0, NUM2 ; 存入NUM2变量 ; 计算两数之和 LD R1, NUM1 LD R2, NUM2 ADD R3, R1, R2 ; 打印结果提示并输出总和 LEA R0, RESULT TRAP x22 JSR PRINT_NUMBER ; 输出R3中的总和 TRAP x25 ; 终止程序(HALT等价TRAP x25) ; 子程序:GET_NUMBER ; 功能:读取用户输入的最多4位十进制数,结果存在R0 ; 寄存器使用:R0(返回值)、R1(临时存储数字)、R2(读取的字符)、R3(计数器,限制4位) GET_NUMBER ST R1, SAVE_R1 ; 保存寄存器现场 ST R2, SAVE_R2 ST R3, SAVE_R3 AND R0, R0, #0 ; 初始化结果为0 AND R1, R1, #0 AND R3, R3, #0 ADD R3, R3, #4 ; 设置最大输入位数为4 INPUT_LOOP TRAP x23 ; 读取一个字符(存在R0) ADD R2, R0, #0 ; 复制字符到R2 ADD R0, R0, #-10 ; 判断是否为回车(ASCII 10) BRz INPUT_EXIT ; 回车则退出输入 ADD R0, R2, #-48 ; 字符转数字(ASCII '0'-'9'是48-57) BRn INPUT_LOOP ; 非数字字符重新读取 BRp CHECK_DIGIT ; 是数字则继续 CHECK_DIGIT ADD R0, R0, #-9 BRp INPUT_LOOP ; 大于9的字符重新读取 ADD R0, R2, #-48 ; 重新获取数字值 ; 执行 num = num * 10 + digit ADD R1, R1, R1 ; num *=2 ADD R1, R1, R1 ; num *=4 ADD R1, R1, R1 ; num *=8(累计*8) ADD R1, R1, R1 ; num *=2(累计*10) ADD R1, R1, R0 ; 加上当前数字 ADD R3, R3, #-1 ; 位数计数器减1 BRp INPUT_LOOP ; 未到4位继续读取 INPUT_EXIT ADD R0, R1, #0 ; 将结果存入R0作为返回值 LD R1, SAVE_R1 ; 恢复寄存器现场 LD R2, SAVE_R2 LD R3, SAVE_R3 RET ; 子程序:PRINT_NUMBER ; 功能:输出R3中的十进制数(最多5位,因4位相加最大为9999+9999=19998) ; 寄存器使用:R0(输出字符)、R1(除数10)、R2(余数)、R3(待输出数字)、R4(栈指针)、R5(计数器) PRINT_NUMBER ST R0, SAVE_R0 ST R1, SAVE_R1 ST R2, SAVE_R2 ST R4, SAVE_R4 ST R5, SAVE_R5 AND R5, R5, #0 ADD R5, R5, #5 ; 最多5位数字 LD R4, STACK_PTR ; 初始化栈指针(栈从x30FF向下增长) ; 将数字逐位压入栈 STACK_LOOP AND R2, R2, #0 LD R1, DEC10 DIVIDE_LOOP ADD R2, R2, #1 ADD R3, R3, #-10 BRp DIVIDE_LOOP ADD R3, R3, #10 ; 恢复被除数 ADD R2, R2, #-1 ; 得到余数 ADD R0, R2, #48 ; 余数转ASCII STR R0, R4, #-1 ; 压入栈 ADD R4, R4, #-1 ADD R5, R5, #-1 BRz PRINT_LOOP ; 所有位处理完开始输出 ADD R3, R3, #0 ; 判断数字是否为0 BRnp STACK_LOOP ; 从栈中弹出并输出 PRINT_LOOP ADD R4, R4, #1 LDR R0, R4, #0 TRAP x21 ADD R5, R5, #-1 BRp PRINT_LOOP ; 输出换行 LD R0, NEWLINE TRAP x21 LD R0, SAVE_R0 ; 恢复寄存器 LD R1, SAVE_R1 LD R2, SAVE_R2 LD R4, SAVE_R4 LD R5, SAVE_R5 RET ; 数据段 NUM1 .BLKW 1 NUM2 .BLKW 1 PROMPT1 .STRINGZ "Please enter SRC1:\n" PROMPT2 .STRINGZ "Please enter SRC2:\n" RESULT .STRINGZ "The sum is:\n" DEC10 .FILL #10 NEWLINE .FILL #10 STACK_PTR .FILL x30FF ; 寄存器保存区 SAVE_R0 .BLKW 1 SAVE_R1 .BLKW 1 SAVE_R2 .BLKW 1 SAVE_R3 .BLKW 1 SAVE_R4 .BLKW 1 SAVE_R5 .BLKW 1 .END
四、关键优化点说明
- 标签规范:所有标签均顶格书写,避免汇编器解析错误。
- 输入处理:实现了数字合法性校验(仅接受0-9字符)、最多4位限制,正确完成
num*10+digit的累加逻辑,且保存输入值到变量。 - 输出处理:通过栈实现多位数的逆序输出,支持最多5位结果(满足4位相加的最大值19998),输出自动换行。
- 寄存器管理:子程序调用时严格保存/恢复寄存器现场,避免破坏主程序的寄存器状态。
- 代码可读性:添加了清晰的注释,拆分功能为独立子程序,便于维护和扩展。
内容的提问来源于stack exchange,提问作者No uh
相关产品推荐
相关产品推荐

