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

LC-3子程序触发Access Violation错误,请求排查解决

LC-3子程序栈规范备份导致Access Violation错误修复

问题描述

  • 开发LC-3的SUB_GET_STRING子程序,功能为统计用户输入字符数(不含换行符),输入以换行符结束,数组以NULL终止。
  • 采用栈规范备份寄存器时,运行触发Access Violation错误,错误发生在执行HALT相关指令时;改用ST/LD旧方式备份寄存器则正常运行。

错误代码

主程序(x3000)

.ORIG x3000
    LEA R1, user_string         ;R1 - 字符数组起始地址
    LD R6, top_stack_addr    ;初始化栈指针
    LD R6,SUB_GET_STRING_PTR            
    JSR SUB_GET_STRING_PTR

HALT

;--------------
;本地数据
;--------------
user_string          .BLKW #100
SUB_GET_STRING_PTR .FILL x3400

;--------------
;栈顶地址
;--------------
top_stack_addr  .FILL xFE00
.END

子程序(x3400)

.ORIG x3400
;----------------------------------------------------------------------------------------------------------------
; Subroutine: SUB_GET_STRING
; 参数(R1): 字符数组起始地址
; 后置条件: 提示用户输入字符串,以回车键结束,字符存储在R1指向的数组中,数组以NULL终止(不存储换行符)
; 返回值(R5): 读取的非终止符字符数,R1保持初始数组地址不变
;-----------------------------------------------------------------------------------------------------------------
SUB_GET_STRING_3400

    ; 按栈规范备份寄存器,先存R7
    ADD R6, R6, #-1
    STR R7, R6, #0
    ADD R6, R6, #-1
    STR R5, R6, #0
    ADD R6, R6, #-1
    STR R4, R6, #0
    ADD R6, R6, #-1
    STR R3, R6, #0
    ADD R6, R6, #-1
    STR R2, R6, #0
    ADD R6, R6, #-1
    STR R1, R6, #0
;--------------------------------------------------------------------
    LEA R0, user_prompt
    PUTS
                    
    LD R6, Hex_0A
    NOT R6, R6
    ADD R6, R6, #1              ;R6 = -x0A(换行符补码)
    AND R5, R5, #0              ;R5初始化为0,用于统计字符数
                    
    INPUT_LOOP
        GETC
        OUT
        ADD R3, R0, #0          ;R3暂存输入字符
        ADD R3, R3, R6          ;判断是否为换行
        BRnp #2             ;非换行则继续
        STR R3, R1, #0      ;存储NULL终止符
        BR Input_Stop 
        STR R0, R1, #0          ;存储输入字符
        ADD R1, R1, #1          ;移动数组指针
        ADD R5, R5, #1          ;字符计数+1
        BR INPUT_LOOP
    Input_Stop
;恢复寄存器            
    LDR R1, R6, #0
    ADD R6, R6, #1
    LDR R2, R6, #0
    ADD R6, R6, #1
    LDR R3, R6, #0
    ADD R6, R6, #1
    LDR R4, R6, #0
    ADD R6, R6, #1
    LDR R5, R6, #0
    ADD R6, R6, #1
    LDR R7, R6, #0
    ADD R6, R6, #1

;--------------------------------------------------------------------
RET
      
;本地数据
user_prompt         .STRINGZ "Enter the string: 
"
Hex_0A              .FILL x0A       ;换行符ASCII值
.END

错误原因分析

  1. 主程序栈指针被错误覆盖:主程序中先通过LD R6, top_stack_addr设置栈指针,随后又执行LD R6,SUB_GET_STRING_PTR将R6覆盖为子程序地址,完全破坏了栈的初始设置。
  2. 子程序复用栈指针R6:LC-3中R6是专用栈指针,子程序中却将R6用来存储换行符的补码,导致栈指针被篡改。后续恢复寄存器时,使用被修改的R6访问内存,必然触发访问违规。

修复后的代码

主程序(x3000)

.ORIG x3000
    LEA R1, user_string         ; R1 - 字符数组起始地址
    LD R6, top_stack_addr       ; 初始化栈指针R6到栈顶
    LD R2, SUB_GET_STRING_PTR   ; R2存储子程序地址
    JSRR R2                     ; 调用子程序

HALT

; 本地数据
user_string          .BLKW #100
SUB_GET_STRING_PTR .FILL x3400

; 栈顶地址
top_stack_addr  .FILL xFE00
.END

子程序(x3400)

.ORIG x3400
SUB_GET_STRING_3400
    ; 按栈规范备份寄存器(R7先入栈)
    ADD R6, R6, #-1
    STR R7, R6, #0
    ADD R6, R6, #-1
    STR R5, R6, #0
    ADD R6, R6, #-1
    STR R4, R6, #0
    ADD R6, R6, #-1
    STR R3, R6, #0
    ADD R6, R6, #-1
    STR R2, R6, #0
    ADD R6, R6, #-1
    STR R1, R6, #0

    ; 输出输入提示
    LEA R0, user_prompt
    PUTS

    ; 计算换行符的补码(-x0A),存储到R4(不再使用R6)
    LD R4, Hex_0A
    NOT R4, R4
    ADD R4, R4, #1

    AND R5, R5, #0              ; R5初始化为0,用于统计字符数

INPUT_LOOP
    GETC
    OUT
    ADD R3, R0, #0              ; R3暂存输入字符
    ADD R3, R3, R4              ; 判断是否为换行(结果为0则是)
    BRnp CONTINUE_INPUT         ; 非换行则继续处理
    STR R3, R1, #0              ; 存储NULL终止符
    BR Input_Stop
CONTINUE_INPUT
    STR R0, R1, #0              ; 存储输入字符到数组
    ADD R1, R1, #1              ; 移动数组指针到下一位
    ADD R5, R5, #1              ; 字符计数+1
    BR INPUT_LOOP
Input_Stop

    ; 按入栈逆序恢复寄存器
    LDR R1, R6, #0
    ADD R6, R6, #1
    LDR R2, R6, #0
    ADD R6, R6, #1
    LDR R3, R6, #0
    ADD R6, R6, #1
    LDR R4, R6, #0
    ADD R6, R6, #1
    LDR R5, R6, #0
    ADD R6, R6, #1
    LDR R7, R6, #0
    ADD R6, R6, #1

    RET

; 本地数据
user_prompt         .STRINGZ "Enter the string: "
Hex_0A              .FILL x0A       ; 换行符ASCII值
.END

修复说明

  • 主程序中保留栈指针R6的正确初始化,改用R2存储子程序地址,通过JSRR R2调用子程序,避免破坏栈指针。
  • 子程序中改用R4存储换行符补码,严格保留R6作为栈指针的专用性,确保寄存器备份/恢复时访问正确的栈内存地址。

内容的提问来源于stack exchange,提问作者henrypham

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:31:03