1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

任天堂GBAでASCII全文字を表示

1
Posted at

目的

前に任天堂GBA向けにプログラムを書いていました。
字体を自分で作っていたので表示できる文字に制限がありました。

ARM32_C言語からアセンブリ関数を呼ぶ(任天堂GBA)

昨日QEMUのBIOSから字体のみ拝借し、任天堂GBで表示する手順をまとめたので今回はGBAにも応用してみました。
ゲームボーイで文字を表示する(QEMUのBIOSから字体を抽出)

動作確認

3239ccaac1d818

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);
};
1
1
16

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?