H8マイコンで動く”Hello, world!”を作ってみます。
ここで紹介するのは、以前に公開したh8monが書き込まれているAKI-H8/3069Fマイコンボードで動作するものです。
とは言ってもOSも無ければ標準出力も無い環境です。とりあえず出力はh8monでもコンソール入出力に使っているRS232Cで行うこととします。
方法としては直接SCI1(RS232C)を制御してアスキーコード文字列を出力することが考えられますが、以下のようなソース(hello.c)でも文字を出力することが出来ます。
void putch(char ch)
{
asm("mov.l #0, er2");
asm("trapa #2");
}
static void print(char *str)
{
while(*str) {
putch(*str);
str ++;
}
}
int main(void)
{
print("Hello, H8 world!\r\n");
return 0;
}
このソースだけではまだ実行は出来ません。スタートアッププログラムが必要なのですが、まずはmain()を解説します。
printf()のような標準入出力関数は(使う方法はあるのですが)使えません。printf()の代わりの文字列出力関数としてprint()を書きました。中は文字列の終わり(0)まで一文字づつputch()に引数として渡しているだけです。
putch()の中身はインラインアセンブラで書かれています。
内容はer2レジスタを0にして、「trapa #2」を実行しています。
実はh8monではtrapa #2でSCI1から文字を入出力することが出来ます。er2を0にするとer0に書かれたデータが出力されます。er2を1にするとSCI1が受信したデータをer0に書き込みます。つまり戻り値として参照できます。
(このコードはあまり良い例ではありません。このコードは引数がer0で渡されて、他のレジスタが破壊されないことを期待しています。実際にそうなるのですが、本来は「拡張アセンブリ構文」を使うべきです。今回はソースの見易さを優先してこのようにしています。)
以下はスタートアップルーチン(start.s)です。
.h8300h
.section .text
.global _start
.extern _stack_bottom
.extern _bss_start
.extern _bss_end
_start:
; mov.l #0x600000,er7 ; Set SP
mov.l er0,@-er7
mov.l er1,@-er7
;; .data section initialize
mov.l #_dataRAM_start,er0
mov.l #_dataRAM_end,er1
mov.l #_dataROM_start,er2
bra loop11
loop1:
mov.l @er2,er3
mov.l er3,@er0
add #4,er0
add #4,er2
loop11:
cmp.l er0,er1
bf loop1
;; .bss section initialize
mov.l #_bss_start,er0
mov.l #_bss_end,er1
mov.l #0,er2
bra loop21
loop2:
mov.l er2,@er0
add #4,er0
loop21:
cmp.l er0,er1
bf loop2
mov.l @er7+,er1
mov.l @er7+,er0
jsr _main ; Goto main()
; rts
trapa #3
.end
各セクションを初期化してmain()をコールしています。最後の「trapa #3」はh8monに処理を戻すための物です。h8monではソフトウェアブレークとしてtrapa #3を使用しています。スタックポインタ(er7)の初期化も必要なのですが、上記ではコメントにしています。この場合、スタックポインタはh8monが設定する初期値(0xFFFF20:内蔵SRAM)で動作します。
以下はリンクスクリプト(h83069.lds)です。
OUTPUT_FORMAT("elf32-h8300")
OUTPUT_ARCH(h8300h)
ENTRY("_start")
MEMORY
{
ram(rx) : o = 0x400000, l = 0x080000
}
SECTIONS
{
.text : {
*(.text)
*(.strings)
*(.rodata)
*(.rodata.str1.1)
_dataROM_start = . ;
} > ram
.data ALIGN(4) : {
_dataRAM_start = . ;
*(.data)
_dataRAM_end = . ;
} > ram
.bss ALIGN(4) : {
_bss_start = . ;
*(.bss)
*(COMMON)
_bss_end = . ;
} > ram
}
プログラムを0x400000番地に配置するようにしています。この番地にはAKI-H8/3069FマイコンボードではDRAMが実装されています。h8monでは起動後、DRAMが使用できるようにBSC(バスコントローラ)を初期化しています。
最後にMakefileです。
CROSS = h8300-elf-
CC = $(CROSS)gcc
AS = $(CROSS)as
LD = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
CFLAGS = -mh -g -O2 -Wall
LDSCRIPT= h83069.lds
LDFLAGS = -mh -Wl,-Map,$*.map -Wl,-T$(LDSCRIPT) -nostartfiles
PROGRAM = hello.mot
OBJS = start.o hello.o
SRCS = start.s hello.c
.SUFFIXES: .mot .exe .a .o .c .s .h
.c.o:
$(CC) $(CFLAGS) -c $<
.elf.mot:
$(OBJCOPY) -Osrec $< $@
all: $(PROGRAM)
$(PROGRAM): $(OBJS) $(START) $(LDSCRIPT) $(DEPEND)
$(LD) $(LDFLAGS) -o $*.elf $(START) $(OBJS) -lgcc
$(OBJCOPY) -Osrec $*.elf $*.mot
clean:
rm -f -r $(OBJS) $(PROGRAM) $(DEPEND) *.mot *.map *.elf *~
以前紹介したクロスコンパイラでコンパイルできます。
makeコマンドでコンパイルすると「hello.mot」が出来るはずです。
h8monでは「l」コマンドでhello.motをロードすることが出来ます。ターミナルソフトは「Tera Term Pro」がおすすめです。h8monでlコマンド入力後、「ファイル送信」機能かhello.motファイルをドロップして転送します。
h8monのgコマンドでロードしたプログラムを実行します。
実際に動かした画面は以下のようになります。
H8/300(H) 3069F Monitor Ver. 0.80
(c)2003,2004,2007 Takashi SHUDO
Virtual vector table area : FFBF20 - FFC01F
Monitor used RAM area : FFC020 - FFC475
Monitor stack end address : FFC608
Build date : 11:27:06 May 3 2008
>l
Please send S-records data.
Load address : 400000 - 4000B2
Excution address : 400000
Done.
>g
Hello, H8 world!
Break.
PC = 400062 CCR = 84
R0 = 00FF0000 R1 = 00FFC090 R2 = 00FFC0F0 R3 = 00FFC09D
R4 = 00000000 R5 = 00000000 R6 = 00000000 R7 = 00FFFF20
400062 : 57 30 trapa #0x3:3
>
「Hello, H8 world!」の文字が出力されています。今回のソースはここに置いておきます。
ここまで読んでくれた方、お疲れ様でした。次回はh8monの目玉?である、gdbを使ったソフトの実行、デバッグを予定しています。(いつになったは工作の話になるんだろう(^^;;;)