【问题标题】:GameBoy compiler with system registers and interrupts带有系统寄存器和中断的 GameBoy 编译器
【发布时间】:2015-01-20 00:01:22
【问题描述】:

我花了很多时间学习 GameBoy 编程,因为我已经熟悉 Z80 组装,所以我不害怕投入使用它。我(当然)会发现用 C 或 C++ 编程效率更高,但是找不到适用于 GameBoy 的完整编译器,我可以找到的编译器自己管理所有内容,并且不向程序员提供对系统寄存器的访问权限,并且还有一些可怕的缺点,例如 100% 的 CPU 利用率和不支持中断。
是否可以像 Arduino 的 AVR 编译器一样寻址系统寄存器?能够简单地使用名称来寻址 CPU 或系统寄存器,例如 DDRD = %10101011
我需要做什么才能将中断和系统寄存器添加到编译器中?除了一个系统寄存器之外,所有寄存器都只有一个字节的内存地址,中断向量当然是内存位置,唯一一个不是内存地址的系统寄存器只能用两条汇编指令EIDI修改,但这可以内联函数是否正确?

【问题讨论】:

  • GBDK 是一个 C 编译器,它允许您直接与寄存器交互 - 使用 gb/hardware.h。但是,提供的文档并不好,因此您实际上无法看到文档中的名称。只需打开include/gb/hardware.h 下的文件即可看到所有内容。它还支持中断 - 请参阅 gb/gb.h 中的 add_VBLadd_LCDadd_TIMadd_SIOadd_JOY
  • 哇谢谢@Pokechu22。抱歉没有及时回复。

标签: c compiler-construction z80 gameboy


【解决方案1】:

通常的策略是创建自己的指向系统寄存器的指针。我不知道 DDRD 的地址,但是这样的东西应该是诀窍:

volatile unsigned char *reg_DDRD = (unsigned char *)0xE000;
*reg_DDRD = 0xAB;

大多数 C 编译器不支持二进制常量,但您可以将它们与一些宏hacky 一起使用。您可以使用宏来制作更直观的语法:

#define DDRD (*reg_DDRD)
DDRD = 0xAB;

当普通 C 代码也能正常工作时,修改编译器是没有意义的。

处理中断归结为解决 3 个问题。首先是让中断向量地址跳转到 C 函数。由于它在 ROM 中,因此您需要修改 C 运行时环境以对其进行初始化。这变得非常依赖系统,但通常你想要做的是添加一个如下所示的汇编语言文件:

     org 38h   ; or wherever the gameboy CPU jumps to on interrupt
 jp _intr_function

这应该会导致 CPU 在您的 C 程序中转到 intr_function()。您可能需要也可能不需要前导下划线。而且您可能无法在汇编文件中使用org 设置目标地址,而是不得不使用链接器和节来玩弄。

第二个问题是 C 函数不一定会保存它应该保存的所有寄存器。您可以通过向其中添加内联程序集来执行此操作,如下所示:

void intr_function()
{
     asm(" push af");
     asm(" push bc");
     asm(" push de");
     asm(" push hl");

     // ... now do what you like here.

     asm(" pop hl");
     asm(" pop de");
     asm(" pop bc");
     asm(" pop af");
 }

最后,可能必须通过操作硬件寄存器来确认中断。但是你可以在 C 代码中做到这一点,所以没什么特别的。

我不清楚等待循环的问题。标准 C 编译器没有内置这样的功能。它们调用 main() ,如果你想循环它取决于你。确实,Arduino SDK 中使用的类 C 语言有自己的内置无限循环,可以调用您编写的函数,但这不是普通的 C。

【讨论】:

  • 这实际上是所有这些供应商提供的头文件如何为您完成的。我对 Z80 内部结构并不十分熟悉,但请注意指针不一定是任何指针的架构。只能通过特殊指令访问的银行交换和单独的地址空间已经存在,这使得 C 对平面内存地址空间的看法无效。
  • @JonathonReinhart GameBoy 确实使用了银行切换,但幸运的是仅适用于 16K 的 ROM,因此系统寄存器永远不会移动。
  • @LeeAllan 感谢您的澄清。我没想到他们会 - 这会使系统很难与之交互。
  • 关于消除 100% CPU 负载(等待循环)的任何技巧或提示?关于如何添加中断向量的任何想法? GB有一些固定的位置向量,所以肯定只是在我的函数中添加一个跳转..
【解决方案2】:

首先,您可以使用GBDK,它是 Gameboy 的 C 编译器和库。它确实提供了对gb/hardware.h 中寄存器的访问(但未在 doc 文件中列出,因为每个单独的寄存器都没有注释)。它还通过gb/gb.h 中的方法提供对中断的访问:add_VBLadd_LCDadd_TIMadd_SIOadd_JOY。 (还有名为 remove_ 的删除方法)。

供参考和/或您自己使用,这是gb/hardware.h的内容:

#define __REG   volatile UINT8 *

#define P1_REG      (*(__REG)0xFF00)    /* Joystick: 1.1.P15.P14.P13.P12.P11.P10 */
#define SB_REG      (*(__REG)0xFF01)    /* Serial IO data buffer */
#define SC_REG      (*(__REG)0xFF02)    /* Serial IO control register */
#define DIV_REG     (*(__REG)0xFF04)    /* Divider register */
#define TIMA_REG    (*(__REG)0xFF05)    /* Timer counter */
#define TMA_REG     (*(__REG)0xFF06)    /* Timer modulo */
#define TAC_REG     (*(__REG)0xFF07)    /* Timer control */
#define IF_REG      (*(__REG)0xFF0F)    /* Interrupt flags: 0.0.0.JOY.SIO.TIM.LCD.VBL */
#define NR10_REG    (*(__REG)0xFF10)    /* Sound register */
#define NR11_REG    (*(__REG)0xFF11)    /* Sound register */
#define NR12_REG    (*(__REG)0xFF12)    /* Sound register */
#define NR13_REG    (*(__REG)0xFF13)    /* Sound register */
#define NR14_REG    (*(__REG)0xFF14)    /* Sound register */
#define NR21_REG    (*(__REG)0xFF16)    /* Sound register */
#define NR22_REG    (*(__REG)0xFF17)    /* Sound register */
#define NR23_REG    (*(__REG)0xFF18)    /* Sound register */
#define NR24_REG    (*(__REG)0xFF19)    /* Sound register */
#define NR30_REG    (*(__REG)0xFF1A)    /* Sound register */
#define NR31_REG    (*(__REG)0xFF1B)    /* Sound register */
#define NR32_REG    (*(__REG)0xFF1C)    /* Sound register */
#define NR33_REG    (*(__REG)0xFF1D)    /* Sound register */
#define NR34_REG    (*(__REG)0xFF1E)    /* Sound register */
#define NR41_REG    (*(__REG)0xFF20)    /* Sound register */
#define NR42_REG    (*(__REG)0xFF21)    /* Sound register */
#define NR43_REG    (*(__REG)0xFF22)    /* Sound register */
#define NR44_REG    (*(__REG)0xFF23)    /* Sound register */
#define NR50_REG    (*(__REG)0xFF24)    /* Sound register */
#define NR51_REG    (*(__REG)0xFF25)    /* Sound register */
#define NR52_REG    (*(__REG)0xFF26)    /* Sound register */
#define LCDC_REG    (*(__REG)0xFF40)    /* LCD control */
#define STAT_REG    (*(__REG)0xFF41)    /* LCD status */
#define SCY_REG     (*(__REG)0xFF42)    /* Scroll Y */
#define SCX_REG     (*(__REG)0xFF43)    /* Scroll X */
#define LY_REG      (*(__REG)0xFF44)    /* LCDC Y-coordinate */
#define LYC_REG     (*(__REG)0xFF45)    /* LY compare */
#define DMA_REG     (*(__REG)0xFF46)    /* DMA transfer */
#define BGP_REG     (*(__REG)0xFF47)    /* BG palette data */
#define OBP0_REG    (*(__REG)0xFF48)    /* OBJ palette 0 data */
#define OBP1_REG    (*(__REG)0xFF49)    /* OBJ palette 1 data */
#define WY_REG      (*(__REG)0xFF4A)    /* Window Y coordinate */
#define WX_REG      (*(__REG)0xFF4B)    /* Window X coordinate */
#define KEY1_REG    (*(__REG)0xFF4D)    /* CPU speed */
#define VBK_REG     (*(__REG)0xFF4F)    /* VRAM bank */
#define HDMA1_REG   (*(__REG)0xFF51)    /* DMA control 1 */
#define HDMA2_REG   (*(__REG)0xFF52)    /* DMA control 2 */
#define HDMA3_REG   (*(__REG)0xFF53)    /* DMA control 3 */
#define HDMA4_REG   (*(__REG)0xFF54)    /* DMA control 4 */
#define HDMA5_REG   (*(__REG)0xFF55)    /* DMA control 5 */
#define RP_REG      (*(__REG)0xFF56)    /* IR port */
#define BCPS_REG    (*(__REG)0xFF68)    /* BG color palette specification */
#define BCPD_REG    (*(__REG)0xFF69)    /* BG color palette data */
#define OCPS_REG    (*(__REG)0xFF6A)    /* OBJ color palette specification */
#define OCPD_REG    (*(__REG)0xFF6B)    /* OBJ color palette data */
#define SVBK_REG    (*(__REG)0xFF70)    /* WRAM bank */
#define IE_REG      (*(__REG)0xFFFF)    /* Interrupt enable */

这些操作与George Phillips's answer 相同,因此可以像普通变量一样使用。

GBDK 用于添加和删除中断的代码在libc\gb\crt0.s 中找到,但我对汇编的了解还不够,无法在本文中包含相关部分。

我也不确定如何避免繁忙循环。

【讨论】:

    猜你喜欢
    • 2013-07-26
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 2014-08-25
    • 2013-08-25
    • 1970-01-01
    • 2020-09-26
    • 1970-01-01
    相关资源
    最近更新 更多