目的
ARM32のアセンブリを研究していこうと思っています。
Linux上で検証しても良いのですが、筆者はベアメタル環境が好きなので任天堂GBAで実験することにしました。
画面に表示しないとデバッグが困難な為、C言語からアセンブリで書いた関数を呼ぶ形で実行します。
今後困らない為にビルドの手順を記録しておきます。
参考にした筆者の過去記事
C言語からアセンブリを呼ぶ方法
アセンブリに外出しした関数名をC内で明記する
extern int add(int a, int b);
今回C言語から呼び出すアセンブリ
.syntax unified
.arch armv7-a
.arm
.text
.global add
.type add, %function
add:
add r0, r0, r1 @ r0 = a + b
bx lr @ 返り値
.size add, .-add
動作確認
コード
再現可能な様にコード及びコマンドを全掲します。
rmdir tmp
mkdir tmp
arm-none-eabi-as header.s -o tmp/header.o
arm-none-eabi-as add.s -o tmp/add.o
arm-none-eabi-gcc -c main.c -o tmp/main.o -nostdlib -ffreestanding
arm-none-eabi-gcc -c font.c -o tmp/font.o -nostdlib -ffreestanding
arm-none-eabi-ld tmp/header.o tmp/add.o tmp/main.o tmp/font.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
add.s
add.s
.syntax unified
.arch armv7-a
.arm
.text
.global add
.type add, %function
add:
add r0, r0, r1 @ r0 = a + b
bx lr @ 返り値
.size add, .-add
main.c
main.c
extern int add(int a, int b);
#include "font.h"
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#define REG_DISPCNT (*(volatile uint16_t*)0x04000000) // 画面制御レジスタ
#define VRAM ((volatile uint16_t*)0x06000000)
#define FONT_WIDTH 8
#define FONT_HEIGHT 8
#define CHAR_SPACING 1
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 160
//文字座標位置(広域変数がROM領域に置かれてしまい更新不可な為強引な暫定措置)
//作法的に汚いので修正予定。rodataの調整が必要。
volatile int *text_pos_x=(volatile int*)0x03000000;
volatile int *text_pos_y=(volatile int*)0x03000004;
static inline void putpixel(int x, int y, uint16_t c) {
if (x < 0 || x >= SCREEN_WIDTH ||
y < 0 || y >= SCREEN_HEIGHT)
return;
VRAM[y * SCREEN_WIDTH + x] = c;
}
void printfont(int x, int y, int c, uint16_t color) {
for (int row = 0; row < FONT_HEIGHT; row++) {
uint8_t bits = font[c][row];
for (int col = 0; col < FONT_WIDTH; col++) {
if (bits & (1 << (7 - col))) {
putpixel(x + col, y + row, color);
}
}
}
}
/* カーソル付き描画 */
void putchar(char c, uint16_t color) {
// 改行処理
if (c == '\n') {
*text_pos_x = 1; // 左端へ
*text_pos_y += FONT_HEIGHT + 2; // 1 行下げる
return;
}
printfont(*text_pos_x, *text_pos_y, c, color);
*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, uint16_t color) {
while (*str) {
putchar(*str++, color);
}
}
void println(const char *str, uint16_t color) {
print(str, color);
putchar('\n', color);
}
void print_dec(int value, uint16_t color)
{
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], color);
}
}
void main(void)
{
// Mode3 + BG2
REG_DISPCNT = 0x0403;
volatile int result = add(3, 5);
*text_pos_x=1;
*text_pos_y=1;
print("RESULT=", 0xFFFF);
print_dec(result, 0xFFFF);
while (1);
};
font.h
font.h
#ifndef FONT_H
#define FONT_H
typedef unsigned char u8;
extern const u8 font[128][8];
#endif
font.c
font.c
#include "font.h"
const u8 font[128][8] = {
// 0 0x30=48
['0'] =
{0b00111100, // ████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ████
0b00000000}, //
// 1 0x31=49
['1'] =
{0b00011000, // ██
0b00111000, // ███
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00111100, // ████
0b00000000}, //
// 2 0x32=50
['2'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b00000110, // ██
0b00011100, // ██
0b00110000, // ██
0b01100000, // ██
0b01111110, // ████████
0b00000000}, //
// 3 0x33=51
['3'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b00000110, // ██
0b00011100, // ███
0b00000110, // ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// 4 0x34=52
['4'] =
{0b00001100, // ██
0b00011100, // ███
0b00111100, // ████
0b01101100, // ██ ██
0b01111110, // ████████
0b00001100, // ██
0b00001100, // ██
0b00000000}, //
// 5 0x35=53
['5'] =
{0b01111110, // ████████
0b01100000, // ██
0b01111100, // ██████
0b00000110, // ██
0b00000110, // ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// 6 0x36=54
['6'] =
{0b00011100, // ████
0b00110000, // ██
0b01100000, // ██
0b01111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// 7 0x37=55
['7'] =
{0b01111110, // ████████
0b00000110, // ██
0b00001100, // ██
0b00011000, // ██
0b00110000, // ██
0b00110000, // ██
0b00110000, // ██
0b00000000}, //
// 8 0x38=56
['8'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// 9 0x39=57
['9'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111110, // ██████
0b00000110, // ██
0b00001100, // ██
0b00111000, // ████
0b00000000}, //
// = 0x3D=61
[0x3D] = // '='
{0b00000000, //
0b00000000, //
0b01111110, // ████████
0b00000000, //
0b01111110, // ████████
0b00000000, //
0b00000000, //
0b00000000}, //
// A 0x41=65
['A'] =
{0b00011000, // ██
0b00111100, // ████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01111110, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00000000}, //
// B 0x42=66
['B'] =
{0b01111100, // █████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01111100, // █████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01111100, // █████
0b00000000}, //
// C 0x43=67
['C'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// D 0x44=68
['D'] =
{0b01111000, // █████
0b01101100, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01101100, // ██ ██
0b01111000, // █████
0b00000000}, //
// E 0x45=69
['E'] =
{0b01111110, // ████████
0b01100000, // ██
0b01100000, // ██
0b01111100, // ██████
0b01100000, // ██
0b01100000, // ██
0b01111110, // ████████
0b00000000}, //
// F 0x46=70
['F'] =
{0b01111110, // ████████
0b01100000, // ██
0b01100000, // ██
0b01111100, // ██████
0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b00000000}, //
// G 0x47=71
['G'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100000, // ██
0b01100000, // ██
0b01101110, // ██ ███
0b01100110, // ██ ██
0b00111110, // ██████
0b00000000}, //
// H 0x48=72
['H'] =
{0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01111110, // ████████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00000000}, //
// I 0x49=73
['I'] =
{0b00111100, // ██████
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00111100, // ██████
0b00000000}, //
// J 0x4A=74
['J'] =
{0b00001110, // ███
0b00000110, // ██
0b00000110, // ██
0b00000110, // ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// K 0x4B=75
['K'] =
{0b01100110, // ██ ██
0b01101100, // ██ ██
0b01111000, // ████
0b01110000, // ███
0b01111000, // ████
0b01101100, // ██ ██
0b01100110, // ██ ██
0b00000000}, //
// L 0x4C=76
['L'] =
{0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b01111110, // ████████
0b00000000}, //
// M 0x4D = 77
['M'] =
{0b11000110, // █ █
0b11000110, // ██ ██
0b11000110, // ██ ██
0b11101110, // ███ ███
0b11111110, // ████████
0b11000110, // ██ ██
0b11000110, // █ █
0b00000000},
// N 0x4E=78
['N'] =
{0b11000110, // ██ ██
0b11100110, // ███ ██
0b11110110, // ████ ██
0b11111110, // █████ ██
0b11011110, // ██ █████
0b11001110, // ██ ███
0b11000110, // ██ ██
0b00000000}, //
// O 0x4F=79
['O'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// P 0x50=80
['P'] =
{0b01111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01111100, // ██████
0b01100000, // ██
0b01100000, // ██
0b01100000, // ██
0b00000000}, //
// Q 0x51=81
['Q'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01101110, // ██ ███
0b01100110, // ██ ██
0b00111110, // ██████
0b00000000}, //
// R 0x52=82
['R'] =
{0b01111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01111100, // ██████
0b01101100, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00000000}, //
// S 0x53=83
['S'] =
{0b00111100, // ██████
0b01100110, // ██ ██
0b01100000, // ██
0b00111100, // ██████
0b00000110, // ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// T 0x54=84
['T'] =
{0b01111110, // ████████
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00000000}, //
// U 0x55=85
['U'] =
{0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00000000}, //
// V 0x56=86
['V'] =
{0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00011000, // ██
0b00000000}, //
// W 0x57 = 87
['W'] =
{
0b01000010, // █ █
0b01000010, // █ █
0b01000010, // █ █
0b01100110, // ██ ██
0b01111110, // ████████
0b01100110, // ██ ██
0b01000010, // █ █
0b00000000,
},
// X 0x58=88
['X'] =
{0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00011000, // ██
0b00111100, // ██████
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00000000}, //
// Y 0x59=89
['Y'] =
{0b01100110, // ██ ██
0b01100110, // ██ ██
0b01100110, // ██ ██
0b00111100, // ██████
0b00011000, // ██
0b00011000, // ██
0b00011000, // ██
0b00000000}, //
// Z 0x5A=90
['Z'] =
{0b01111110, // ████████
0b00000110, // ██
0b00001100, // ██
0b00011000, // ██
0b00110000, // ██
0b01100000, // ██
0b01111110, // ████████
0b00000000}, //
};
20260718追記
main.cの中に色々なものを詰め込み過ぎていたので、画面表示に関する処理は全てgfx.cにまとめました。
main.c及びgfx.c以外のソースは一切変更していません。
rm -rf tmp
mkdir tmp
arm-none-eabi-as header.s -o tmp/header.o
arm-none-eabi-as add.s -o tmp/add.o
arm-none-eabi-gcc -c main.c -o tmp/main.o -nostdlib -ffreestanding
arm-none-eabi-gcc -c font.c -o tmp/font.o -nostdlib -ffreestanding
arm-none-eabi-gcc -c gfx.c -o tmp/gfx.o -nostdlib -ffreestanding
arm-none-eabi-ld tmp/header.o tmp/add.o tmp/main.o tmp/font.o tmp/gfx.o -T link.ld -o tmp/out.elf
arm-none-eabi-objcopy -O binary tmp/out.elf tmp/out.gba
main.c
main.c
extern int add(int a, int b);
#include "gfx.h"
void main(void)
{
gfx_init();
volatile int result = add(3, 5);
print("RESULT=");
print_dec(result);
println("");
gfx_set_text_color(COLOR_RED); // 赤文字
println("DEBUG");
while (1);
};
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 "font.h"
// 文字座標位置(広域変数がROM領域に置かれてしまい更新不可な為強引な暫定措置)
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 = font[c][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;
}