目次
目的
任天堂ゲームボーイで任意の文字列を表示する検証をします。
表示可能とする文字はASCIIコードの範囲に絞ります。
全ての文字の形を自分で作るのは面倒な為、オープンソースのQEMUから字体のみ拝借します。
QEMUから8×8×128の字体を抽出
QEMUのソースコードのダウンロード方法は以下の通りです。
wget https://download.qemu.org/qemu-11.1.0.tar.xz
tar xvJf qemu-11.1.0.tar.xz
以下の2ファイルを使用します。
qemu-11.1.0/roms/openbios/libopenbios/font_8x8.c
/qemu-11.1.0/roms/openbios/include/libopenbiosc/fontdata.h
Hファイルの読み込み部分のみ変更します。
| 変更前 | 変更後 |
|---|---|
font_8x8.c#include "libopenbios/fontdata.h"
|
font_8x8.c#include "fontdata.h"
|
以下の様に同じフォルダに配置しコンパイルします。
gbfont/
├── font_8x8.c ← #include "fontdata.h" に変更済み
└── fontdata.h ← #define FONTDATAMAX_8X8 2048
gcc -c font_8x8.c -o font_8x8.o
ELF形式から生バイナリのみ取り出します。
objcopy -O binary font_8x8.o font_8x8.bin
抽出されたバイナリの大きさを確認します
2048バイト = 256文字 × 8バイト/文字 = 8×8文字丁度となっています。
test@test-fujitsu:~/kaihatsu/gbfont$ ls -l font_8x8.bin
-rw-rw-r-- 1 test test 2048 10月 3 14:27 font_8x8.bin
ゲームボーイで文字を表示する原理
タイルの登録
0x8000からの領域に8×8の字体を登録していきます。
1文字=64bit=8バイトです。
ここでGBのタイル形式は16バイトです。
よって以下の様に16倍バイトずつずらしながら登録していきます。
| 文字 | アドレス範囲 |
|---|---|
| 0文字目 | 0x8000 ~ 0x800F |
| 1文字目 | 0x8010 ~ 0x801F |
| 2文字目 | 0x8020 ~ 0x802F |
| 3文字目 | 0x8030 ~ 0x803F |
タイルを画面に登録
0x9800〜の領域にタイル番号を登録します。
例えば
0x8000〜0x800FにH、0x8010〜0x801FにIの字体を登録しておき、0x9800へ0,0x9801へ1を入れると画面左上から順にHIと表示されます。
GBのタイル形式
パレットを以下のように登録した場合で説明します。
ld a, %11100100
ld [$FF47], a
パレットをこのように設定した場合、黒が11、白が00となります。
例えばAと表示する場合を見ていきます。
font_8x8.cはASCIIコード順にビットマップ形式で文字が登録されており、Aは以下のように登録されています。
/* 65 0x41 'A' */
0x38, /* 00111000 */
0x6c, /* 01101100 */
0xc6, /* 11000110 */
0xfe, /* 11111110 */
0xc6, /* 11000110 */
0xc6, /* 11000110 */
0xc6, /* 11000110 */
0x00, /* 00000000 */
Aの頂点は00111000です。
この場合、タイルとしてはdb %00111000, %00111000のように2回同じビット列を登録すると各ピクセルが00の白又は11の黒となります。
分かり辛いのでもう一つ例を出すと
db %11111111, %00000000の様に登録した場合は全8ピクセルが10となり灰色になります。
以下の記事で自分で定義した字体を表示する検証をしています。
ゲームボーイで文字表示第一歩
実装
rgbasm -o main.o main.asm && \
rgblink -o main.gb main.o && \
rgbfix -v -p 0xFF main.gb && \
sameboy main.gb
SECTION "Entry", ROM0[$0100]
jp start
SECTION "Main", ROM0[$0150]
start:
di ; 割り込み禁止
ld sp, $FFFE ; スタック設定
; 画面描画停止
xor a
ldh[$FF40], a
call bj_clear
ld hl, font_8x8 ; 元データ(1文字8バイト)
ld de, $8000 ; VRAM 開始
ld b, 256 ; 256文字分
call copy_font_to_vram
ld hl, $9800
ld [hl],1 ; Hタイル
ld hl, $9800
ld b, 0 ; 256回(0→255で一周)
fill_loop:
ld [hl], b ; タイル番号 b を書く
inc hl
inc b ; 0→255→0 で0へ戻る
jr nz, fill_loop
; パレット
ld a, %11100100 ; 4色(11 10 01 00)を設定
ld [$FF47], a
; 画面描画再開: BG有効
ld a, %10010001
ldh [$FF40], a
hang:
jr hang
; HL = 字体データ元(1文字8バイト、1行1バイト)
; DE = VRAM 宛先
; B = 文字数
copy_font_to_vram:
push bc
ld b, 8 ; 1文字あたり8行
.row_loop:
ld a, [hl] ; 1行分読み込み
inc hl
ld [de], a
inc de
ld [de], a
inc de
dec b
jr nz, .row_loop
pop bc
dec b
jr nz, copy_font_to_vram
ret
; 画面全体にタイル255(真っ白)を登録する
; メモリは初めから0で初期化されているのでタイル255を故意に書き換え無ければ全て0
bj_clear:
ld bc, 32*32
ld hl, $9800
loop1:
ld [hl],255
inc hl
dec bc
ld a, b
or c
jr nz, loop1
ret
font_8x8:
INCBIN "font_8x8.bin"
VRAMは画面の右と下にも平広がっており、画面に全て表示し切れていません。
全て表示するには折り返し等を考慮する必要がありますが、それは次回に回します。