# trap进入处理函数的入口__alltraps, 以及第一次进入时的出口__restore

.altmacro
.macro SAVE_GP n
    sd x\n, \n*8(sp)    # 将寄存器 x_n 的值保存到栈空间中
.endm
.macro LOAD_GP n
    ld x\n, \n*8(sp)    # 从栈空间中加载寄存器 x_n 的值
.endm
.section .text        # 进入 .text 段
.globl __alltraps     # 声明全局符号 __alltraps
.globl __restore      # 声明全局符号 __restore
.align 2               # 对齐到 2^2 = 4 字节

__alltraps:           # __alltraps 符号的实现
    csrrw sp, sscratch, sp   # 交换 sp 和 sscratch 寄存器的值
    # 现在 sp 指向内核栈,sscratch 指向用户栈
    # 在内核栈上分配一个 TrapContext
    addi sp, sp, -34*8      # 分配 34*8 字节的空间
    # 保存通用寄存器
    sd x1, 1*8(sp)          # 保存寄存器 x1 的值  (这一步是为了跳过x0寄存器, 方便下面循环)
    # 跳过 sp(x2),后面会再次保存
    sd x3, 3*8(sp)          # 保存寄存器 x3 的值  (这一步是为了跳过x4寄存器, 方便下面循环)
    # 跳过 tp(x4),应用程序不使用该寄存器
    # 保存 x5~x31
    .set n, 5               # 定义变量 n 的初始值为 5
    .rept 27                # 循环 27 次
        SAVE_GP %n          # 保存寄存器 x_n 的值到栈空间中
        .set n, n+1        # 将 n 加 1
    .endr                   # 结束指令块
    # 我们可以自由使用 t0/t1/t2,因为它们已保存在内核栈上
    csrr t0, sstatus        # 读取 sstatus 寄存器的值
    csrr t1, sepc           # 读取 sepc 寄存器的值
    sd t0, 32*8(sp)         # 保存 sstatus 寄存器的值到栈空间中
    sd t1, 33*8(sp)         # 保存 sepc 寄存器的值到栈空间中
    # 从 sscratch 中读取用户栈,并将其保存到内核栈中
    csrr t2, sscratch       # 读取 sscratch 寄存器的值
    sd t2, 2*8(sp)          # 保存用户栈的地址到内核栈 trap context中
    # 设置 trap_handler(cx: &mut TrapContext) 的输入参数
    mv a0, sp               # 将 TrapContext 的地址赋值给 a0
    call trap_handler       # 调用 trap_handler 分发函数

__restore:                  # __restore 符号的实现
    # case1: 开始运行应用程序
    # case2: 从处理完中断后返回 U 级别
    mv sp, a0               # 将 a0 中保存的内核栈地址赋值给 sp  这个首次进入是在内核进入首次进入sp 这里会被修改为KERNEL_STACK_SIZE, 被接管

    # 恢复 sstatus/sepc
    ld t0, 32*8(sp)         # 从栈空间中读取 sstatus 寄存器的值
    ld t1, 33*8(sp)         # 从栈空间中读取 sepc 寄存器的值
    ld t2, 2*8(sp)          # 从栈空间中读取用户栈的栈顶地址
    csrw sstatus, t0        # 恢复 sstatus 寄存器的值
    csrw sepc, t1           # 恢复 sepc 寄存器的值
    csrw sscratch, t2       # 将栈指针 用户栈 的值 临时保存到 sscratch 寄存器中
    # 现在 sp 指向内核栈,sscratch 指向用户栈

    # 恢复通用寄存器,除了 sp/tp 以外的寄存器
    # 跳过x0
    ld x1, 1*8(sp)          # 从栈空间中读取寄存器 x1 的值
    # 跳过x2
    ld x3, 3*8(sp)          # 从栈空间中读取寄存器 x3 的值
    # 循环恢复
    .set n, 5               # 定义变量 n 的初始值为 5
    .rept 27                # 循环 27 次
        LOAD_GP %n          # 从栈空间中加载寄存器 x_n 的值
        .set n, n+1        # 将 n 加 1
    .endr                   # 结束指令块

    # 现在 sp 指向内核栈,sscratch 指向用户栈, 释放内核栈中的中的 TrapContext, 陷入已经执行完成, 即将返回用户态 不需要这个上下文了
    addi sp, sp, 34*8       # 释放 sizeof<TrapContext>

    csrrw sp, sscratch, sp  # 交换 sp内核栈 和 sscratch用户栈 寄存器的值, 交换之后sscratch保存的是内核栈顶, sp是用户栈 USER_STACK(栈底) 的栈顶
    sret                    # 返回指令, 根据sstatus(陷入/异常之前的特权级) 和 sepc(陷入/异常 之前的pc)返回