目的
前に任天堂GBA向けにプログラムを書いていました。
字体を自分で作っていたので表示できる文字に制限がありました。
昨日QEMUのBIOSから字体のみ拝借し、任天堂GBで表示する手順をまとめたので今回はGBAにも応用してみました。
ゲームボーイで文字を表示する(QEMUのBIOSから字体を抽出)
動作確認
QEMUのソースコードから以下の2個のファイルを拝借します。
qemu-11.1.0/roms/openbios/libopenbios/font_8x8.c
/qemu-11.1.0/roms/openbios/include/libopenbiosc/fontdata.h
font_8x8.cを以下の様に変更します。
| 変更前 | 変更後 |
|---|---|
font_8x8.c#include "libopenbios/fontdata.h"
|
font_8x8.c#include "fontdata.h"
|
├── fontdata.h # 字体データのヘッダ
├── font_8x8.c # 8x8字体定義
├── gfx.h # 画面表示関連ヘッダ
├── gfx.c # 画面表示処理
├── link.ld # リンカスクリプト
├── main.c # メイン処理
├── header.s # アセンブリヘッダ
└── logo.bin # 商標画像バイナリ
logo.binの作り方は以下を参照
GBA自作コードを実機で動作させる手順
rm -rf tmp
mkdir tmp
arm-none-eabi-as header.s -o tmp/header.o
arm-none-eabi-gcc -c main.c -o tmp/main.o -nostdlib -ffreestanding
arm-none-eabi-gcc -c font_8x8.c -o tmp/font_8x8.o -nostdlib -ffreestanding
arm-none-eabi-gcc -c gfx.c -o tmp/gfx.o -nostdlib -ffreestanding
arm-none-eabi-ld tmp/header.o tmp/main.o tmp/font_8x8.o tmp/gfx.o -T link.ld -o tmp/out.elf
arm-none-eabi-objcopy -O binary tmp/out.elf tmp/out.gba
link.ld
link.ld
ENTRY(_start)
SECTIONS
{
. = 0x08000000;
.text :
{
*(.text)
*(.rodata)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
*(COMMON)
}
}
header.s
header.s
.arm
.section .text
.global _start
.global ResetHandler
.org 0x00000000
_start:
b ResetHandler
/* 商標 */
.incbin "logo.bin" @ 0x0004-0x009F
/* --- ゲームに関する情報(0x00A0〜0x00BF) --- */
.org 0x000000A0
.ascii "GBADEMO " @ 名前 12文字
.byte 0x00,0x00,0x00,0x00 @ ゲームコード 4バイト
.byte 0x00,0x00 @ メーカーコード 2バイト
.byte 0x96 @ 固定値
.byte 0x00 @ メインユニットコード
.byte 0x00 @ 機器の種類
.space 7 @ 予約領域
.byte 0x00,0x00 @ 補完チェック・マスクROMバージョン
.space 2 @ 残り予約領域
.align 2
ResetHandler:
ldr sp, =0x03007F00
bl main @ Cへ 跳ぶ
InfiniteLoop:
b InfiniteLoop
gfx.h
gfx.h
#pragma once
#define REG_DISPCNT (*(volatile uint16_t*)0x04000000) // 画面制御レジスタ
#define VRAM ((volatile uint16_t*)0x06000000)
// 字体の大きさ
#define FONT_WIDTH 8
#define FONT_HEIGHT 8
// 表示する文字同士の幅(隣り合う文字は1ビット空けて表示)
#define CHAR_SPACING 1
// GBA画面解像度
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 160
// 表示色
#define COLOR_WHITE 0xFFFF
#define COLOR_RED 0x001F
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
void gfx_init(void);
void putchar(char c);
void print(const char *s);
void println(const char *s);
void print_dec(int v);
void gfx_set_text_color(uint16_t color);
gfx.c
gfx.c
#include "gfx.h"
#include "fontdata.h"
// 文字座標位置(IWRAMに配置)
volatile int *text_pos_x=(volatile int*)0x03000000;
volatile int *text_pos_y=(volatile int*)0x03000004;
// 表示する文字の色
volatile uint16_t *draw_color=(volatile uint16_t*)0x03000008;
static inline void putpixel(int x, int y) {
if (x < 0 || x >= SCREEN_WIDTH ||
y < 0 || y >= SCREEN_HEIGHT)
return;
//VRAM[y * SCREEN_WIDTH + x] = c;
VRAM[y * SCREEN_WIDTH + x] = *draw_color;
}
void gfx_set_text_color(uint16_t color) {
*draw_color = color;
}
void printfont(int x, int y, int c) {
for (int row = 0; row < FONT_HEIGHT; row++) {
//uint8_t bits = fontdata_8x8[c][row];
uint8_t bits = fontdata_8x8[c * 8 + row];
for (int col = 0; col < FONT_WIDTH; col++) {
if (bits & (1 << (7 - col))) {
putpixel(x + col, y + row);
}
}
}
}
void putchar(char c) {
// 改行処理
if (c == '\n') {
*text_pos_x = 1; // 左端へ
*text_pos_y += FONT_HEIGHT + 2; // 1 行下げる
return;
}
printfont(*text_pos_x, *text_pos_y, c);
*text_pos_x += FONT_WIDTH + CHAR_SPACING;
/* 画面右端で折り返す */
if (*text_pos_x + FONT_WIDTH >= SCREEN_WIDTH) {
*text_pos_x = 1;
*text_pos_y += FONT_HEIGHT + 2;
}
}
void print(const char *str) {
while (*str) {
putchar(*str++);
}
}
void println(const char *str) {
print(str);
putchar('\n');
}
void print_dec(int value)
{
char buf[12]; // 符号 + 最大10桁 + '\0'
int i = 0;
int sign = 0;
// 負数対応
if (value < 0) {
sign = 1;
value = -value;
}
// 数値を逆順でバッファに格納
do {
buf[i++] = '0' + (value % 10);
value /= 10;
} while (value > 0);
// 符号
if (sign) {
buf[i++] = '-';
}
// 逆順出力
while (i > 0) {
putchar(buf[--i]);
}
}
void gfx_init(void) {
// Mode3 + BG2
REG_DISPCNT = 0x0403;
// 文字表示位置初期化
*text_pos_x=1;
*text_pos_y=1;
//初期文字色=白
*draw_color=COLOR_WHITE;
}
main.c
#include "gfx.h"
void main(void)
{
gfx_init();
println("Display all ASCII characters");
char c = 0;
for(int i=0;i< 256;i++){
putchar(c);
c++;
}
while (1);
};