【GameBoy模拟器 6】栈与跳转指令

先做到流程控制再说

目标

  • 实现并理解栈:从硬件角度理解栈的物理基础,从软件角度理解栈的逻辑概念
  • 实现所有栈相关的指令(CALL、RET、RETI、RST、PUSH、POP…)
  • 实现跳转相关的指令(JR、JP…),满足流程控制需求

知识点

  • CALL指令讲解
    • 功能:父函数调用一个子函数
    • 实现:将当前PC的值压入栈中后(以备后续RET指令使用),让PC跳转到指定的地址
  • RET指令讲解
    • 功能:从子函数返回调用它的父函数
    • 实现:从栈中弹出一个值,并让PC跳转到该值(前面备的就用上了),这样PC就会回到父函数执行完子函数的地方,去执行下一条指令
  • 栈的生长方向:从高地址到低地址

在GameBoy的CPU初始化函数中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void cpu_init() {
    ctx.regs.pc = 0x100;
    ctx.regs.sp = 0xFFFE;
    *((short *)&ctx.regs.a) = 0xB001;
    *((short *)&ctx.regs.b) = 0x1300;
    *((short *)&ctx.regs.d) = 0xD800;
    *((short *)&ctx.regs.h) = 0x4D01;
    ctx.ie_register = 0;
    ctx.int_flags = 0;
    ctx.int_master_enabled = false;
    ctx.enabling_ime = false;

    timer_get_context()->div = 0xABCC;
}

可以看到SP指针被初始化为0xFFFE,是相当大的一个地址,这意味着栈可以最大程度地往0x0的方向生长(Stack RAM就是High RAM,FF80h-FFFEh,共127字节)

  • 栈的逻辑概念
    • 存储一系列相同的元素、对元素可以进行push和pop的一个数据结构
  • 栈的物理基础:地址和寄存器
    • 地址提供元素的存储,SP寄存器标识了栈顶地址(更本质的说法是下一个要被pop的元素位置)
    • gameboy的CPU(8080)栈只依赖SP寄存器
    • 更复杂的CPU(如8086)提供BP和SP寄存器,可以有更复杂的栈逻辑
    • 围绕SP寄存器构建的CPU指令(PUSH和POP),enable了栈的功能

寄存器和地址的物理基础使得CPU的栈指令成为可能,汇编语言得以栈式地管理元素。高级编程语言中的函数调用机制,在底层就是利用CPU的栈指令(类似我们实现的CALL指令)实现的。

Licensed under CC BY-NC-SA 4.0
最后更新于 Jun 25, 2025 15:02 UTC
Built with Hugo
Jimmy 设计的 Stack 主题