目的
昨日表計算ソフトでドット絵を描き、手作業でバイナリをアセンブリソース内に打ち込みましたが、途中で心が折れたのでマクロで自動化しました。
ゲームボーイでドット絵を表示
本当は会社でも使っているExcelが良かったのですが、買いたくないので無料のLibreOfficeを使ってマクロを描きました。
やっていること
ドットの色は4種類あり、それぞれ2バイトの値で表します。各ドットは左と右に分けて配置します。
例えば左上のドット00は以下の括弧[]で囲ったように左と右に分けて配置します。
エクセル上のドットに書き込まれた値を以下の形式に変換するマクロを作りました。
タイルは8×8なので、この主人公のように16×16の場合は4回実行することになります。
カーソルを合わせた位置を左上として8×8のドットをバイナリに変換するので、位置がずれると表示もずれます。
db %[0]1000010, %[0]1000010
db %01110010, %01110010
db %01011001, %01011000
db %00111111, %00111111
db %00011111, %00011011
db %00011111, %00011110
db %00001111, %00001001
db %00000111, %00000111
マクロのソースは以下です。
Sub ExportGameBoyTiles
Dim oDoc As Object
Dim oSheet As Object
Dim oOutSheet As Object
Dim oSelection As Object
Dim startCol As Long, startRow As Long
Dim r As Long, c As Long
Dim cellValue As String
Dim dbleft As String
Dim dbRight As String
oDoc = ThisComponent
oSheet = oDoc.getCurrentController().getActiveSheet()
' カーソルの置かれているセル座標を取得
oSelection = oDoc.getCurrentSelection()
startCol = oSelection.getRangeAddress().StartColumn
startRow = oSelection.getRangeAddress().StartRow
' 2枚目のシートを取得(存在しなければ作成)
If oDoc.getSheets().getCount() < 2 Then ' シートが1枚しか無ければもう一枚生成
oDoc.getSheets().insertNewByName("Output", 1)
End If
oOutSheet = oDoc.getSheets().getByIndex(1)
For r = 0 To 7
dbleft = "" ' db左
dbRight = "" ' db右
For c = 0 To 7
cellValue = oSheet.getCellByPosition(startCol + c, startRow + r).getString()
cellValue = Trim(cellValue) ' 前後の空白を除去
If Len(cellValue) >= 2 Then ' 2文字以上あるか(11等)
dbleft = dbleft & Mid(cellValue, 1, 1) ' db左側にcellの1文字目(左側)を連結
dbRight = dbRight & Mid(cellValue, 2, 1) ' db左側にcellの2文字目(右側)を連結
Else ' 空セルは0とする
dbleft = dbleft & "0"
dbRight = dbRight & "0"
End If
Next c
' 出力
oOutSheet.getCellByPosition(0, r).setString(" db %" & dbleft & ", %" & dbRight)
Next r
MsgBox "Sheet2 A列 に出力完了", 0, "Done"
End Sub
流れ
2枚目のシートに値が出力されるので、アセンブリのソースに貼り付けます。
ソースコード
マクロで変換した値をソースコードの中に貼り付けて、表示できるか見てみます。
無事表示されました。
rgbasm -o main.o main.asm && \
rgblink -o main.gb main.o && \
rgbfix -v -p 0xFF main.gb && \
../SameBoy-master/build/bin/SDL/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
; 画面全体を白くする(タイル0を白で初期化して画面全体に適応)
ld hl, white_tile ; 真っ白(全0)のデータが登録されているタイル
ld de, $8000 ; タイル0の開始アドレス
ld bc, 16 ; 1タイルのバイト数
call memcpy
; 画面全体にタイル0(真っ白)を登録する
ld bc, 32*32
ld hl, $9800
tile_init:
ld [hl],0
inc hl
dec bc
ld a, b
or c
jr nz, tile_init
; 主人公
ld hl, tile1
ld de, $8000+1*16
ld bc, 16*4
call memcpy
ld hl, $9800
ld [hl],1 ; 主人公左上
inc hl
ld [hl],2 ; 主人公右上
ld hl, $9800+32
ld [hl],3 ; 主人公左下
inc hl
ld [hl],4 ; 主人公右下
; パレット
ld a, %11100100 ; 4色(11 10 01 00)を設定
ld [$FF47], a
; 画面描画再開: BG有効
ld a, %10010001
ldh [$FF40], a
hang:
jr hang
; メモリ複製関数
; HL=元, DE=先, BC=長さ
memcpy:
ld a, [hl]
ld [de], a
inc hl
inc de
dec bc
ld a, b ; B==CならBC==0 (B==$FF,C==$FFの時は失敗する)
or c
jr nz,memcpy ; BC!=0なら繰り返す
ret
white_tile:
db %00000000, %00000000
db %00000000, %00000000
db %00000000, %00000000
db %00000000, %00000000
db %00000000, %00000000
db %00000000, %00000000
db %00000000, %00000000
db %00000000, %00000000
; ここから下がマクロで変換したデータ
; 主人公左上
tile1:
db %00000000, %00000000
db %00000111, %00000111
db %00001111, %00001000
db %00011111, %00010000
db %00011111, %00010000
db %00111100, %00111000
db %00111111, %00111111
db %01010000, %01010000
; 主人公右上
tile2:
db %00000000, %00000000
db %11100000, %11100000
db %11110000, %00010000
db %11111000, %00001000
db %11111000, %00001000
db %00111100, %00011100
db %11111100, %11111100
db %00001010, %00001010
; 主人公左下
tile3:
db %01000010, %01000010
db %01110010, %01110010
db %01011001, %01011000
db %00111111, %00111111
db %00011111, %00011011
db %00011111, %00011110
db %00001111, %00001001
db %00000111, %00000111
; 主人公右下
tile4:
db %01000010, %01000010
db %01001100, %01001100
db %10011100, %00010100
db %11110100, %11110100
db %11001100, %11001100
db %11001100, %01001100
db %10111000, %10111000
db %00000000, %00000000