深入理解 LC-3 的堆栈与基于中断的 I/O 机制 📦🔄
在计算机系统中,堆栈(Stack) 和 中断(Interrupts) 是至关重要的机制。堆栈是一种后进先出的数据结构,用于临时存储数据,如函数调用和局部变量;而中断允许设备与处理器高效通信,尤其在处理输入/输出(I/O)操作时。本文将详细讲解堆栈的工作原理、Push 和 Pop 操作、中断机制如何结合堆栈完成任务,并通过例子帮助你掌握这些概念。
1. 什么是堆栈?📦
堆栈(Stack) 是一种 后进先出(LIFO, Last In First Out) 的数据结构。它常用于存储临时数据,例如函数调用、局部变量和中断处理时保存的寄存器值。在 LC-3 中,堆栈的操作通过寄存器 R6 实现,R6 作为 堆栈指针(Stack Pointer, SP),指向当前堆栈的顶端。
例子:
想象一个 硬币架,当你把硬币放进架子时,它总是放在顶部。取硬币时,你只能从顶部取走,这与堆栈的操作非常相似。压入(Push)操作将数据放入堆栈,弹出(Pop)操作则移除堆栈顶端的数据。
2. 堆栈的基本操作 💻
在 LC-3 中,R6 被用作堆栈指针,指向堆栈的顶部。堆栈指针 R6 会随着数据的压入(Push)和弹出(Pop)进行增减。
压入(Push)操作:
- R6 减少(堆栈向下增长)。
- 新数据被存储在 R6 指向的内存位置。
弹出(Pop)操作:
- 从 R6 指向的内存位置读取数据。
- R6 增加,指向堆栈的下一个数据位置。
示例代码:
PUSH:
ADD R6, R6, #-1 ; 堆栈指针减少
STR R0, R6, #0 ; 将 R0 的值压入堆栈
POP:
LDR R0, R6, #0 ; 从堆栈中弹出数据到 R0
ADD R6, R6, #1 ; 堆栈指针增加
错误处理:
- 堆栈溢出(Stack Overflow):当堆栈超过其分配的空间时,程序会发生错误。
- 堆栈下溢(Stack Underflow):当弹出数据时,如果堆栈为空,也会导致错误。
3. 中断驱动的 I/O:如何与堆栈协作 🔄
中断驱动的 I/O(Interrupt-driven I/O) 是一种高效的处理输入/输出的机制。当 I/O 设备准备好时,它发出中断信号,通知处理器暂停当前任务,转而执行中断服务例程(Interrupt Service Routine, ISR)。在中断期间,处理器会使用堆栈保存当前程序的状态(如寄存器和 PC),处理完成后再从堆栈中恢复状态,继续执行原程序。
中断处理的步骤:
- 设备发出中断信号。
- 处理器保存当前状态:寄存器和程序计数器(PC)被保存到堆栈。
- 执行中断服务例程:处理器跳转到中断处理程序(ISR)地址,处理设备请求。
- 恢复状态并返回:从堆栈恢复寄存器和 PC,继续执行中断前的程序。
例子:
ISR:
STR R0, R6, #0 ; 保存寄存器 R0
LDI R0, KBDR ; 读取键盘数据
; 处理中断逻辑
LDR R0, R6, #0 ; 恢复寄存器 R0
RTI ; 返回中断前的程序
4. 程序状态寄存器(Program Status Register, PSR)⚙️
程序状态寄存器(PSR) 存储当前程序的状态信息,包括:
- 特权级(Privilege Level):PSR[15] 表示程序是否在 用户模式(User Mode, 1) 或 特权模式(Supervisor Mode, 0) 下运行。
- 优先级(Priority Level):PSR[10:8] 用于表示当前的优先级,从 PL0(最低)到 PL7(最高)。
- 条件码(Condition Codes):PSR[2:0] 用于存储条件码(NZP)。
在中断时,PSR 也会被保存到堆栈,处理完成后再恢复,以确保中断处理前后的程序状态一致。
5. 用户堆栈与特权堆栈 🌐
LC-3 使用两种不同的堆栈:
- 用户堆栈(User Stack, USP):用于存储用户模式下的数据。
- 特权堆栈(Supervisor Stack, SSP):用于在特权模式下存储数据,通常用于中断处理和操作系统调用。
当系统切换到特权模式时,R6 指向特权堆栈;在用户模式下,R6 则指向用户堆栈。通过 Saved.USP 和 Saved.SSP 记录切换时的堆栈指针。
6. 中断向量表(Interrupt Vector Table)🛑
中断向量表(Interrupt Vector Table) 是一个存储各个中断服务例程(ISR)地址的表格。每个中断源都有一个对应的中断向量,用于确定发生中断时应跳转到哪个 ISR 来处理事件。
7. 实战示例:基于堆栈的中断处理 ✍️
下面的代码展示了如何处理键盘输入中断。在发生中断时,处理器保存当前寄存器状态,读取键盘数据,处理完成后恢复寄存器并返回到原程序。
示例代码:
; 中断处理程序:处理键盘输入
ISR_KEYBOARD:
STR R0, R6, #0 ; 保存寄存器 R0 的值
STR R1, R6, #1 ; 保存寄存器 R1 的值
LDI R0, KBDR ; 从键盘数据寄存器读取数据
; 处理中断逻辑
LDR R1, R6, #1 ; 恢复寄存器 R1
LDR R0, R6, #0 ; 恢复寄存器 R0
RTI ; 返回中断前的程序
MAIN:
; 主程序代码
HALT ; 停止程序
详细解释:
- 当发生键盘中断时,当前的寄存器 R0 和 R1 会被压入堆栈。
- 键盘数据通过
LDI R0, KBDR
读取到寄存器 R0 中。 - 处理中断逻辑后,从堆栈中恢复寄存器 R0 和 R1 的值。
RTI
指令返回中断前的程序,继续执行主程序。
总结 🎯
在 LC-3 系统中,堆栈(Stack) 与 中断驱动的 I/O(Interrupt-driven I/O) 是相互协作的机制。堆栈作为临时数据的存储空间,用于保存和恢复程序状态;中断机制允许设备高效地与处理器通信,而不需要轮询设备状态。结合使用堆栈和中断,处理器能够快速响应外部事件并在处理中断后继续执行主程序。理解这些原理,你将能在 LC-3 中编写更加高效的程序,特别是在处理 I/O 和中断时。