組込み開発再入門(6) – そして main() へ

アセンブラ面倒だったのでさっさと c に移行することに。
まぁ実際アセンブラで作るのってスタートアップとディスパッチくらいでしょ?

ということで c で LED 点灯を再現させます。

作ったファイルは

  • linker.x
  • startup.s
  • vectors.c
  • main.c
  • include/defines.h
  • Makefile
スポンサーリンク

リンカスクリプト

まずはリンカスクリプト。
実はこれにはとんでもない欠陥があるわけですが、それはまた今度。

/* H8-3048F 用リンカスクリプト */
OUTPUT_FORMAT("elf32-h8300")
OUTPUT_ARCH(h8300h)
ENTRY("_start")
 
MEMORY
{
    /* ROM 領域の定義 */
    romall(rx)      : ORIGIN = 0x000000, LENGTH = 0x020000  /* 128kb */
    vectors(r)      : ORIGIN = 0x000000, LENGTH = 0x000100
    rom(rx)         : ORIGIN = 0x000100, LENGTH = 0x01FF00
 
    /* RAM 領域の定義 */
    ramall(rwx)     : ORIGIN = 0x0FEF10, LENGTH = 0x001000 /* 4kb */
    data(rxw)       : ORIGIN = 0x0FFC00, LENGTH = 0x000300
    stack(rw)       : ORIGIN = 0x0FFF00, LENGTH = 0x000000
}
 
SECTIONS
{
    . = 0x0;
 
    .vectors : {
        vectors.o(.data)
    } > vectors
 
    .text : {
        _text_start = .;
        *(.text)
        _etext = .;
    } > rom
 
    .rodata : {
        _rodata_start = .;
        *(.strings)
        *(.rodata)
        *(.rodata.*)
        _erodata = .;
    } > rom
 
    .data : {
        _data_start = .;
        *(.data)
        _edata = .;
    } > data
 
    . = ALIGN(4);
    _end = .;
 
    .stack : {
        _stack = .;
    } > stack
}

今までと違うところは、vectors セクションに vectors.o(.data) を指定していることで、これは c のソースコードから生成したオブジェクトファイルから生成したバイナリ(機械語)をそこに配置するからです。このへんは info ld すると一発です。「ググるな、info嫁。」

割り込みベクタ

その vectors.o を生成するための vectors.c はこんな感じで書きます。

#include "include/defines.h"
 
extern void start(void);    /* startup */
 
void (*vectors[])(void) =
{
    start, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};

ベクタエリアに配置されます。
とりあえず殆んど NULL ですが先頭番地に start と書いてあります。
start はアセンブラで書かれた関数で、リセット割り込みが発生すると start に処理が飛び main() 以前の処理をするようになっています。こんな感じ。

    .h8300h
    .section    .text
    .global     _start
 
    .type       _start, @function
 
_start:
    mov.l       #0xffff00, sp
    jsr         @_main
 
1:
    bra         1b

とりあえずスタックポインタの設定して main() に飛ぶだけ。
後から色々書き足します。。。多分。

ん? NULL って c で使えたっけ?使えません。
include/defines.h には NULL の定義とかが書いてあります。

#ifndef _DEFINE_H_
#define _DEFINE_H_
 
#define NULL ((void *)0)
 
typedef unsigned char   uint08;
typedef unsigned short  uint16;
typedef unsigned long   uint32;
 
#endif /* _DEFINE_H_ */

心情的には uint8_t とかしたいのですが stdint.h と被るとなんとなく嫌な気がするので。。。

いよいよmain関数

最後は main() 関数です。

#include "include/defines.h"
 
int main(void)
{
    uint08*  P5DR  = (uint08*)0xFFFCA;
    uint08* P5DDR  = (uint08*)0xFFFC8;
 
    *P5DDR = 0xF3;
    *P5DR  = 0xFF;
 
    while(1)
    {
        ;
    }
}

説明いらないよね…?

余談ですが M16C 系のマイコンは (純正コンパイラを使うのであれば) #pragma address というプリプロセス命令が用意されているのでレジスタへのアクセスを若干スマートに書けます。
とりあえずアレな方法でレジスタにアクセスしてるけど今後どうしようかなーとか。。。

それなりの Makefile を書きましょう。

PREFIX	=/usr/local
ARCH	=h8300-elf
BINDIR	=$(PREFIX)/bin
ADDNAME	=$(ARCH)-
TARGET	=test
 
AR	=$(BINDIR)/$(ADDNAME)ar
AS	=$(BINDIR)/$(ADDNAME)as
CC	=$(BINDIR)/$(ADDNAME)gcc
LD	=$(BINDIR)/$(ADDNAME)ld
NM	=$(BINDIR)/$(ADDNAME)NM
OBJCPY	=$(BINDIR)/$(ADDNAME)objcopy
OBJDMP	=$(BINDIR)/$(ADDNAME)objdump
LANLIB	=$(BINDIR)/$(ADDNAME)ranlib
STRIP	=$(BINDIR)/$(ADDNAME)strip
 
OBJS	=vectors.o startup.o main.o
 
CFLAGS	=-Wall -mh -nostdinc -nostdlib -fno-builtin
CFLAGS	+=-I
CFLAGS	+=-g
CFLAGS	+=-Os
 
LFLAGS	=-static -T linker.x -L.
 
.SUFFIX:.c.o
.SUFFIX:.s.o
 
all:$(TARGET).mot
 
$(TARGET):$(OBJS)
	$(CC) $(OBJS) -o $(TARGET) $(CFLAGS) $(LFLAGS)
	cp $(TARGET)	$(TARGET).elf
	$(STRIP) $(TARGET)
 
$(TARGET).mot:	$(TARGET)
	$(OBJCPY) -O srec $(TARGET) $(TARGET).mot
 
.c.o:$<
	$(CC) -c $(CFLAGS) $<
 
.s.o:$<
	$(CC)	-c	$(CFLAGS) $<
 
image:$(TARGET).mot
 
clean:
	rm -rf $(TARGET) $(TARGET).elf $(TARGET).mot *.o
 
write:$(TARGET).mot
	h8write -3048 $(TARGET).mot /dev/ttyUSB0

objdump でどんな命令に変換されたのかが確認できます。

/usr/local/h8300-elf/bin/objdump -d test.elf

test.elf:     file format elf32-h8300
 
 
Disassembly of section .text:
 
00000100 <_start>:
 100:	7a 07 00 ff 	mov.l	#0xffff00,er7
 104:	ff 00 
 106:	5e 00 01 0c 	jsr	@0x10c:24
 
0000010a <.L11>:
 10a:	40 fe       	bra	.-2 (0x10a)
 
0000010c <_main>:
 10c:	01 00 6d f6 	mov.l	er6,@-er7
 110:	0f f6       	mov.l	er7,er6
 112:	fa f3       	mov.b	#0xf3,r2l
 114:	6a aa 00 0f 	mov.b	r2l,@0xfffc8:32
 118:	ff c8 
 11a:	fa ff       	mov.b	#0xff,r2l
 11c:	6a aa 00 0f 	mov.b	r2l,@0xfffca:32
 120:	ff ca 
 
00000122 <.L2>:
 122:	40 fe       	bra	.-2 (0x122)

objdump はビットフィールドでレジスタにアクセスしても大丈夫? とか確認したいときに便利。 (まぁやらないのが吉ですが)

スポンサーリンク

フォローする