Компилятор GameBoy с системными регистрами и прерываниями

Я потратил много времени на изучение программирования на GameBoy, так как я уже был знаком с Z80 Assembly, я не боялся использовать его. Я (конечно) нашел бы гораздо более продуктивным программирование на C или C++, но не могу найти полноценный компилятор для GameBoy, компиляторы, которые я могу найти, сами все управляют и не дают доступа к системным регистрам программисту, а также имеют некоторые ужасные недостатки, такие как 100% загрузка процессора и отсутствие поддержки прерываний.
Можно ли было бы обращаться к системным регистрам так же, как компилятор Arduino AVR? возможность адресации процессора или системного реестра просто с его именем, таким как DDRD = %10101011
Что мне нужно сделать, чтобы добавить прерывания и системные регистры в компилятор? Все, кроме одного системного регистра, являются только однобайтовыми адресами памяти, а векторы прерываний, конечно же, являются ячейками памяти, единственный системный регистр, который не является адресом памяти, может быть изменен только двумя инструкциями по сборке. EI а также DI но что может быть встроенных функций правильно?

2 ответа

Решение

Обычная тактика - создавать собственные указатели на системные регистры. Я не знаю адрес DDRD, но что-то вроде этого должно сработать:

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

Большинство компиляторов C не поддерживают двоичные константы, но вы можете использовать их с некоторыми макросами. И вы можете использовать макросы для создания немного более интуитивного синтаксиса:

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

Нет смысла изменять компилятор, когда ванильный C-код может работать так же хорошо.

Обработка прерываний сводится к решению 3 задач. Первый заключается в том, чтобы адрес вектора прерывания выполнял переход к функции C. Поскольку это в ПЗУ, вам нужно изменить среду выполнения C, чтобы инициализировать это. Это зависит от системы, но обычно вам нужно добавить файл на языке ассемблера, который выглядит следующим образом:

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

Это должно заставить процессор перейти на intr_function() в вашей C-программе. Вы можете или не можете нуждаться в подчеркивании. И вы не сможете установить адрес назначения в файле ассемблера с помощью 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 (), и если вы хотите зациклить, то это ваше дело. Это правда, что C-подобный язык, используемый в Arduino SDK, имеет свой собственный встроенный бесконечный цикл, который вызывает функции, которые вы пишете, но это не обычный C.

Во-первых, вы можете использовать GBDK, который является компилятором C и библиотекой для Gameboy. Это обеспечивает доступ к регистрам в gb/hardware.h (но это не указано в файле документации, так как нет комментариев для каждого отдельного регистра). Он также предоставляет доступ к прерываниям через методы в gb/gb.h: add_VBL, add_LCD, add_TIM, add_SIO, а также add_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 */

Это делается так же, как ответ Джорджа Филлипса, и, следовательно, может использоваться как обычные переменные.

Код, который используется GBDK для добавления и удаления прерываний, находится в libc\gb\crt0.s, но я не знаю достаточно сборки, чтобы включить соответствующие разделы в этом посте.

Я также не уверен, как избежать петли занятости.

Другие вопросы по тегам