VisualBoyAdvance-1.8.0-beta3には嬉しいことにデバッグ用のコンソールがTools->Loggingについています。コンソールを使えば処理の順番が正しく行われたかのチェックや、変数の値を見ることも可能になります。俗にいうprintfデバックです。
表示にはVBA方式とMappy方式の2種類あり、Mappy方式の方が優れているのでこちらを利用してください。VBA方式は「swi 0xff」を使用してエミュレータの独自処理にフックさせています。実機で動かすと素直にswi命令が動いてしまい、破綻しますので推奨されません。対するMappy方式はldr、andなどの無意味なシグネチャ処理によって実現しています。とても行儀が良いです。
void mappylog(char* buf) { asm("mov r2, %0; ldr r0,=0xc0ded00d; and r0,r0" :: "r"(buf) : "r2", "r0"); }
void vbalog(char* buf) { asm("mov r0, %0; swi 0xff;" :: "r"(buf) : "r0"); }
mGBAでもprintfデバッグは可能です。ただし最新バージョンの0.10.3を使っても日本語(sjis)は文字化けします。
#define REG_DEBUG_ENABLE *(volatile u16*) 0x4FFF780 #define REG_DEBUG_FLAGS *(volatile u16*) 0x4FFF700 #define REG_DEBUG_STR (char*) 0x4FFF600 void MgbaLog(char* buf) { REG_DEBUG_ENABLE = 0xC0DE; u32 len = _Strlen(buf); while(len) { u32 write = _Min(len, 256); _Memcpy(REG_DEBUG_STR, buf, write); REG_DEBUG_FLAGS = 0x102; // mGBA Warning buf += write; len -= write; } }
テキスト出力できるということはprintf関数やsprintf関数が使用できるな、と考えたくなるものです。しかし標準ライブラリは個人的に極力控えるべきだと思っています。GBAプログラミングの楽しみの1つには、大いなる無駄であることが醍醐味です。printfを自作してみましょう(爆。
typedef char* va_list; #define __vasz(x) ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int) -1)) #define va_start(ap, n) ((ap) = (va_list)&n + __vasz(n)) #define va_arg(ap, type) (*((type *)((va_list)((ap) = (void *)((va_list)(ap) + __vasz(type))) - __vasz(type)))) #define va_end(ap) //--------------------------------------------------------------------------- IWRAM_CODE void _Printf(char* format, ...) { char sprintfBuf[100] ALIGN(4); char* ap; va_start(ap, format); _DoSprintf(sprintfBuf, format, ap); va_end(ap); mappylog(sprintfBuf); } //--------------------------------------------------------------------------- IWRAM_CODE char* _Sprintf(char* buf, char* format, ...) { char* ap; va_start(ap, format); _DoSprintf(buf, format, ap); va_end(ap); return buf; } //--------------------------------------------------------------------------- IWRAM_CODE void _DoSprintf(char* str, char* fmt, char* ap) { s32 val; char* val2; char val3; char c; s32 col = 0; char colChr = ' '; bool isCol; for(;;) { c = *fmt++; if(c == '\0') { *str++ = '\0'; return; } if(c != '%') { *str++ = c; continue; } c = *fmt++; if(c == '0') { colChr = '0'; c = *fmt++; } if(_IsDigit(c) == TRUE) { col = c - '0'; isCol = TRUE; c = *fmt++; } else { isCol = FALSE; } switch(c) { case 'd': val = va_arg(ap, int); if(val < 0) { val *= -1; *str++ = '-'; } str = (isCol == TRUE) ? _SprintfNumCol(val, 10, str, col, colChr, TRUE) : _SprintfNum(val, 10, str); break; case 'x': val = va_arg(ap, int); str = (isCol == TRUE) ? _SprintfHexCol((u32)val, str, col, colChr, TRUE, 'a') : _SprintfHex((u32)val, str, 'a'); break; case 'X': val = va_arg(ap, int); str = (isCol == TRUE) ? _SprintfHexCol((u32)val, str, col, colChr, TRUE, 'A') : _SprintfHex((u32)val, str, 'A'); break; case 's': val2 = va_arg(ap, char*); str = _SprintfString(val2, str); break; case 'c': val3 = va_arg(ap, char); *str++ = val3; break; case '\0': default: *str++ = '\0'; return; } } } //--------------------------------------------------------------------------- IWRAM_CODE char* _SprintfNum(s32 val, s32 base, char* s) { s32 c = Mod(val, base); val = Div(val, base); if(val > 0) { s = _SprintfNum(val, base, s); } *s++ = c+'0'; return s; } //--------------------------------------------------------------------------- IWRAM_CODE char* _SprintfNumCol(s32 val, s32 base, char* s, s32 col, char colChr, bool isTop) { s32 c = Mod(val, base); val = Div(val, base); if(col > 1) { s = _SprintfNumCol(val, base, s, col-1, colChr, FALSE); } if(c != 0 || val != 0 || isTop == TRUE) { *s++ = c+'0'; } else { *s++ = colChr; } return s; } //--------------------------------------------------------------------------- IWRAM_CODE char* _SprintfHexCol(u32 val, char* s, s32 col, char colChr, bool isTop, char hex) { u32 c = val & 0xf; val = val >> 4; if(col > 1) { s = _SprintfHexCol(val, s, col-1, colChr, FALSE, hex); } if(c != 0 || val != 0 || isTop == TRUE) { *s++ = (c>9) ? c-10+hex : c+'0'; } else { *s++ = colChr; } return s; } //--------------------------------------------------------------------------- IWRAM_CODE char* _SprintfHex(u32 val, char* s, char hex) { if(val >= 0x10) { s = _SprintfHex(val >> 4, s, hex); } u32 c = val & 0xf; *s++ = (c>9) ? c-10+hex : c+'0'; return s; } //--------------------------------------------------------------------------- IWRAM_CODE char* _SprintfString(char* val, char* s) { while(*val != '\0') { *s++ = *val++; } return s; } //--------------------------------------------------------------------------- IWRAM_CODE void mappylog(char* buf) { asm("mov r2, %0; ldr r0,=0xc0ded00d; and r0,r0" :: "r"(buf) : "r2", "r0"); }