计算机系统(1)第十章LC-3 复习中典型的例题代码(页码,章节,例题) 190. 10.23.ORIG X3000LEA R6, STACKBASE ; 初始化栈指针R6设置栈底LEA R0, PROMPTTRAP x22 ; PUTS输出提示字符串Please enter a sentence:AND R1, R1, #0 ; R1清零用来统计输入字符的个数LOOPTRAP x20 ; GETC读取键盘输入1个字符到R0TRAP x21 ; OUT把刚输入的字符回显到屏幕ADD R3, R0, #-10 ; 判断是否是换行符ASCII(#10换行\n的编码)BRz INPUTDONE ; 若输入换行跳出输入循环JSR PUSH ; 调用PUSH子程序把这个字符压入栈保存ADD R1, R1, #1 ; 字符计数1BRnzp LOOP ; 无条件继续读取下一个字符INPUTDONEADD R1, R1, #0 ; 根据字符数R1设置标志位BRz DONE ; 若一个字符都没输入直接结束LOOP2JSR POP ; 调用POP子程序从栈弹出一个字符到R0TRAP x21 ; OUT输出弹出的字符ADD R1, R1, #-1 ; 剩余计数-1BRp LOOP2 ; 计数0就继续弹出输出DONETRAP x25 ; HALT停机PUSHADD R6, R6, #-2 ; 栈指针向下移动2格预留保存空间STR R0, R6, #0 ; 把R0里的输入字符存入栈RETPOPLDR R0, R6, #0 ; 从栈取出字符加载到R0ADD R6, R6, #2 ; 栈指针向上恢复2格RETPROMPT .STRINGZ Please enter a sentence: ; 输入提示语STACKSPACE .BLKW #50 ; 开辟50字的栈空间STACKBASE .FILL #0 ; 栈底位置.END积累学习# 这份LC-3字符串逆序代码必背核心知识点分4大类考试直接用## 一、必背TRAP中断号代码全部输入输出依赖这个1. TRAP x20 GETC键盘读单个字符 → R0存字符2. TRAP x21 OUT输出R0中单个字符3. TRAP x22 PUTS输出R0起始、\0结尾的字符串4. TRAP x25 HALT程序停止运行记忆口诀20读字符、21打字符、22打整串、25停机 ---## 二、栈操作核心考点你刚才问的移动2格重点### 1. LC-3栈标准规则必考 - 栈指针R6栈**向下生长**从高地址往低地址存数据 - 内存最小操作单位**1个字2字节**STR/LDR只能读写16位字- 入栈PUSH先 R6 R6 - 2再 STR 寄存器, R6, #0- 出栈POP先 LDR 寄存器, R6, #0再 R6 R6 2### 2. 为什么±2简答标准答案 LC-3内存操作以16位字为单位一个字占用2个内存地址因此栈指针必须一次增减2保证完整存储/读取寄存器16位数据避免地址错位。### 3. 模板代码直接背 asm ;PUSH R0 模板PUSHADD R6,R6,#-2STR R0,R6,#0RET ;POP R0 模板POP LDR R0,R6,#0ADD R6,R6,#2RET## 三、程序完整逻辑简答题程序功能### 整体功能背诵标准答案 打印提示字符串循环读取键盘输入字符并实时回显读到换行符ASCII #10停止输入利用栈后进先出特性存储所有字符再逆序弹出、打印实现**字符串反转**。### 关键判断逻辑 1. 换行符ASCII码是#10ADD R3,R0,#-10 检测是否输入回车2. BRz INPUTDONE读到回车退出输入循环3. R1充当计数器记录一共输入多少个字符逆序输出时控制循环次数 ---## 四、指令分支判断高频考点 1. AND R1,R1,#0寄存器清零标准写法 2. BRnzp LOOP无条件跳转永远走分支 3. BRp LOOP2正数才跳转还有字符未输出时循环 4. BRz DONE寄存器为0直接结束无输入时直接停机 5. .STRINGZ自动末尾补\0适配PUTS输出 6. .BLKW #50开辟50个字的内存空间栈缓冲区## 五、考试简答/填空速记清单 1. 存储字符为什么栈指针±2LC-3字长16位内存操作单位为2字节字。 2. 字符串逆序依靠什么数据结构特性栈的后进先出LIFO。 3. 结束输入的判断条件读取到换行符ASCII 10。 4. R1寄存器作用统计输入字符总个数控制逆序输出循环。 5. PUTS输出要求内存字符串必须以0结尾用.STRINGZ定义。189.10.4积累学习我们来逐行拆解这段 LC-3 的PEEK子程序核心是只读栈顶元素、不弹出同时做栈下溢空栈检查。一、先明确前提约定栈的工作方式LC-3 默认栈向下增长压栈时栈指针地址减小弹栈时地址增大。R6是栈指针SPStack Pointer始终指向当前栈顶元素的内存地址。StackBase是栈底栈的高地址端StackMax是栈的低地址边界整个栈共 10 个字空间。空栈状态栈指针R6 StackBase 1指向栈底的上一个位置还没有任何元素。子程序功能成功将栈顶元素值存入R0R5 0标记成功。失败栈空 / 下溢R5 1标记失败不读取数据。因为只读取、不压栈所以不需要检查栈上溢。二、逐行指令解析1. 初始化与下溢判断PEEK AND R5, R5, #0 ; initialize R5将R5清 0默认标记为「成功状态」。LEA R0, StackBaseLEA 是取地址指令把标号StackBase的内存地址加载到 R0即R0 StackBase的地址。NOT R0, R0 ADD R0, R0, #-1 ; R0 -(addr of stackbase 1)这两行是 LC-3 里的补码求负技巧最终得到R0 -(StackBase地址 1)。原理LC-3 没有减法指令要计算A - B需要先算出-B的补码再做加法。 补码规则-x NOT x 1因此推导可得-(x1) NOT x - 1也就是对 x 取反后减 1刚好等于-(x1)比常规的「先加 1 再取反加 1」节省一条指令。ADD R0, R0, R6 ; R6 - stack pointer相当于计算R0 R6 (-(StackBase 1)) R6 - (StackBase 1)本质是比较栈指针 R6 和「空栈标记地址 StackBase1」是否相等。BRZ Underflow如果上一步的计算结果为 0说明R6 StackBase 1也就是栈是空的跳转到Underflow处理下溢。如果结果不为 0说明栈里有元素继续往下执行。2. 正常读取栈顶非空栈LDR R0, R6, #0 ; put the first element in R0LDR 是内存读取指令以R6为基地址偏移 0读取栈顶地址里的数值存入R0。注意这里没有修改 R6栈指针不变元素还留在栈里 —— 这就是PEEK窥视和POP弹出的核心区别。RET子程序返回此时R50成功R0中是栈顶元素的值。3. 下溢错误处理Underflow ADD R5, R5, #1 ; failure栈空无法读取将R5置为 1标记操作失败。RET子程序返回此时R51失败R0 的值无意义。4. 栈空间定义StackMax .BLKW 10, x0000 ;分配10个字的栈空间低地址端 StackBase .FILL x0000 ; 栈底高地址端栈的地址范围低地址StackMax→ 高地址StackBase共 10 个字。栈初始化时需要把R6设为StackBase 1空栈状态。三、完整执行流程总结清 0 错误标记 R5默认成功。计算空栈地址的补码负数用于和栈指针做减法比较。栈指针与空栈地址相减结果为 0 则判定栈空跳错误分支。栈非空则读取栈顶元素到 R0正常返回。栈空则置错误标记失败返回。举个直观例子栈里有 1 个元素R6 StackBase指向栈顶数据计算R6 - (StackBase1) -1 ≠ 0不跳转读取 R6 指向的数据到 R0返回成功。栈为空R6 StackBase 1计算R6 - (StackBase1) 0触发 BRZ跳转到 UnderflowR5 置 1返回失败。