0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゲームボーイで文字を表示する(QEMUのBIOSから字体を抽出)

0
Posted at

目次

目的

任天堂ゲームボーイで任意の文字列を表示する検証をします。
表示可能とする文字は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は以下のように登録されています。

font_8x8.c
	/* 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となり灰色になります。

以下の記事で自分で定義した字体を表示する検証をしています。
ゲームボーイで文字表示第一歩

実装

截图 2026-10-03 16-00-58
rgbasm -o main.o main.asm && \
rgblink -o main.gb main.o && \
rgbfix -v -p 0xFF main.gb && \
sameboy main.gb
main.asm
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は画面の右と下にも平広がっており、画面に全て表示し切れていません。
全て表示するには折り返し等を考慮する必要がありますが、それは次回に回します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?