Я потратил много времени на изучение программирования для GameBoy, так как я уже был знаком с ассемблером Z80 и не боялся начать его использовать. Я бы (конечно) нашел гораздо более продуктивным программировать на C или C++, однако не могу найти полный компилятор для GameBoy, компиляторы, которые я могу найти, управляют всем сами и не дают доступ к системным регистрам программисту, а также имеют некоторые ужасные недостатки, такие как 100% загрузка ЦП и отсутствие поддержки прерываний.
Можно ли адресовать системные регистры так же, как компилятор Arduino AVR? иметь возможность обращаться к ЦП или системному регистру просто по его имени, такому как DDRD = %10101011
Что мне нужно сделать, чтобы добавить прерывания и системные регистры в компилятор? Все системные регистры, кроме одного, представляют собой только однобайтовые адреса памяти, а векторы прерываний, конечно же, представляют собой области памяти, единственный системный регистр, который не является адресом памяти, может быть изменен только с помощью двух ассемблерных инструкций EI
и DI
, но это могут быть встроенные функции. ?
Компилятор GameBoy с системными регистрами и прерываниями
Ответы (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
, но я недостаточно хорошо разбираюсь в ассемблере, чтобы включить соответствующие разделы в этот пост.
Я также не уверен, как избежать петли занятости.
gb/hardware.h
. Однако предоставленная документация не очень хороша, поэтому вы не можете увидеть, какие имена указаны в документе. Просто откройте файл подinclude/gb/hardware.h
и все увидите. Он также поддерживает прерывания — см.add_VBL
,add_LCD
,add_TIM
,add_SIO
иadd_JOY
вgb/gb.h
. - person Pokechu22   schedule 20.01.2015