[vim]CrLfをハイライトする


改行コード混ざってるんだけど <CR> <LF> みたいに表示できないのかなーという話があって、それは無理だけどハイライト(またはアンダーライン)表示するだけなら簡単にできるし、多分それだけでも助かると思うのでやってみた。

結構簡単にできました。

ちなみに fileformat=unix にしておけば CrLf は ^M で表示されるから、まぁそれでもいいような気はする。

.vimrc の colorscheme 設定の前に

augroup highlightCrLf
  autocmd!
  autocmd ColorScheme * highlight CrLf term=underline ctermbg=DarkGreen guibg=DarkGreen
  autocmd VimEnter,WinEnter,BufWinEnter,BufNew * match CrLf /\r\n/
augroup END

こんな感じで書いておけば CrLf がハイライト (端末などでハイライトできない場合はアンダーライン) される。

内容は私もきちんと説明できないのでわからなかったらとりあえずコピペしておくといいよ。
autocmd はまだ勉強中。 VimEnter と WinEnter と BufWinEnter と BufNew でだいたい OK なはず。

match の / / で挟まれているところの中身を書き換えることでハイライトの対象が変えられるから色々応用可能。

組込み開発再入門(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() 関数です。

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

組込み開発再入門(5) – ITU で Lチカ


ITU (Integrated Timer Unit) を使ってみるテスト。

ITU 内のカウンタ TCNT は 0×0000 から 0xFFFF までカウントできる。
オーバフローするとオーバフローフラグ (TSR) を立てて 0×0000 から再びカウントアップする。
カウントに使うクロックはプリスケーラで最大 1/8 まで分周可能。

ということでオーバフローフラグが立っていたら LED を ON/OFF するようにしてみる。

H8 4080F の場合は動作クロックが 16MHz なので、プリスケーラで 1/8 に分周するとして、
16 / 8 = 2 Mhz = 2 x 10^6 tick / sec

0xFFFF までカウントすると 65535 ticks なので、
(65535) / (2 x 10^6) = 32.767 msec

つまり、約 32 msec 毎に明滅する (すっごい早いよ)

/********************************************************************************/
/* led.s - ITUテスト
/********************************************************************************/
 
        .h8300h                 ;CPUを指定
 
        /* ラベル定義 */
        .equ    P5DDR,  0xFFFC8 ;P5 データディレクションレジスタ
        .equ    P5DR,   0xFFFCA ;P5 データレジスタ
        .equ    TSTR,   0xFFF60 ;タイマスタートレジスタ
        .equ    TCR0,   0xFFF64 ;タイマコントロールレジスタ
        .equ    TSR0,   0xFFF67 ;タイマステートレジスタ
 
        .section    .text
 
        .global     _start
        .type       _start,@function
 
_start:
        mov.l   #0xFFF00, sp    ;使わないけど
 
        /* P5 を出力に設定 */
        mov.b   #0xF3, r0l
        mov.b   r0l, @P5DDR
 
        /* ITU の初期化 */
        mov.b   #0x03, r0l      ;1/8 分周
        mov.b   r0l, @TCR0      ;プリスケーラ設定
        bset.b  #0x00, @TSTR:8  ;ITUカウンタスタート
 
LOOP:
        mov.b   @TSR0,  r0l
        btst.b  #0x02,  r0l     ;オーバフローフラグチェック
        beq     LOOP
 
LED_CHECK:
        bclr.b  #0x02,  @TSR0:8 :オーバフローフラグクリア
 
        /* LED の点灯状態を確認 */
        mov.b   @P5DR,  r0l
        btst.b  #0x00,  r0l
        beq     LED_ON
 
LED_OFF:
        bclr.b  #0x00,  @P5DR:8 ;LED1=OFF
        jmp     @LOOP
 
LED_ON:
        bset.b  #0x00,  @P5DR:8 ;LED1=ON
        jmp     @LOOP
 
        .end

飽きてきたからそろそろ C で書きたいなーとか。。。

[Debian] debiandoc2html で日本語で書かれた sgml を html 化する


Debian Linux Kernel Handbook の翻訳が 7 割ほどできたので make してみたらちょっと苦労したのでメモ。

日本語の sgml 文章を debiandoc2html するにはロケールを指定する必要がある。
オプション -l でロケールを指定するようだがうまくいかない。
debiandoc2html -l ja_JP.eucJP hoge.sgml
とやってもエラーになってしまう。
正解は -l の後にスペースを入れずにロケールを指定してやる。
debiandoc2html -lja_JP.eucJP hoge.sgml
ちなみに ja_JP.utf-8 は通るけど文字化けするので使えない。

ファイルのエンコーディングを一括変換するには
find -name '*.sgml' | xargs nkf --overwrite -e
とかやるらしい。

ちょっと文章量が大目の章が残ってしまったので残り頑張らねば。

[設定メモ]offlineimap でたまに変わっちゃう gmail の SSL fingerprint に対処する


メール環境は offlineimap + mutt を使ってるんだけど、最近なんかメールが来ないなぁ、、、と思ったら SSL の fingerprint が変わってた。

cert_fingerprint = keykeykey
で対処してるとたまーに fingerprint が変わったことに気づかなかったりする。大事なメールへの返信が遅くなったりすると大変。
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
と CA certificate file を指定しておけば fingerprint が変わっても大丈夫。

つーか mutt とか offlineimap の設定についてまとめときたいな。

[設定メモ]pdf が gimp で開かれちゃう件


普段はターミナルエミュレータからアプリケーション指定して開くことが多いので MIME の関連付けとかあまり気にしたことがなかったんだけど、電子書籍の管理に使ってる Calibre で pdf を開くときになぜか gimp で開くようになってしまった。
(以前は問題なく evince で開いてた。)

Calibre は外部ビューワでファイルを開く際に
xdg-open
を使っていて、実体は /usr/bin 以下にあるシェルスクリプトなわけだけど、中身を見てみると mimeopen を使ってファイルを開いてたので、
mimeopen -d 適当なpdfファイル
でデフォルトのビューワを指定してやると MIME Info データベースが更新されて指定したビューワで開かれるようになった。

ちなみに xdg-open は統合デスクトップ環境を使っている場合はそれぞれの環境に合ったものでファイルを開くように処理が分岐している(GNOME の場合は gvfs-open とか gnome-open とか)。

該当するものが無い場合、最終的に mimeopen を使うようだ。

.mailcap.local/share/applications/*.list も見てくれなくて困った時は MIME Info のデータベースを疑う。
覚えとこう。

組込み開発再入門(4) – Lチカ再び


リンカスクリプトが(とりあえず)書けたのでアセンブリでもうちょっと遊んでみる。とりあえずスイッチを押している間 LED を点灯するプログラムを書いてみた。

ところで、諸説あるけど個人的には

  • アセンブリ…アセンブリ言語
  • アセンブラ…処理系
  • アセンブル…機械語への翻訳

だと思ってます。が、アセンブラ=アセンブリ言語とする文脈も多いのでその辺はコンテキストで理解するしかなさそう。

/********************************************************************************/
/* led.s - スイッチ入力/LED出力プログラム
/********************************************************************************/
 
        .h8300h                 ;CPUを指定
 
        /* ラベル定義 */
        .equ    P4DDR,  0xFFFC5
        .equ    P4DR,   0xFFFC7
        .equ    P4PCR,  0xFFFDA
        .equ    P5DDR,  0xFFFC8
        .equ    P5DR,   0xFFFCA
 
        .section    .text
 
        .global     _start
        .type       _start,@function
 
_start:
        /* スタックポインタの設定 */
        mov.l   #0xFFF10, sp        ;使わないけど
 
        /* P4 を入力に設定 */
        mov.b   #0x00, r0l
        mov.b   r0l, @P4DDR
 
        /* P4 の内部プルアップを有効化 */
        mov.b   #0xFF, r0l
        mov.b   r0l, @P4PCR
 
        /* P5 を出力に設定 */
        mov.b   #0xF3, r0l
        mov.b   r0l, @P5DDR
 
SW1LED:
        /* SW1 の状態チェック */
        mov.b   @P4DR,  r0l
        btst.b  #0x04,  r0l
        beq     LED1ON
 
        /* LED1 を消灯 */
        bclr.b  #0x00,  @P5DR:8
        jmp     @SW2LED
 
LED1ON:
        /* LED1 を点灯 */
        bset.b  #0x00,  @P5DR:8
 
SW2LED:
        /* SW2 の状態チェック */
        mov.b   @P4DR,  r0l
        btst.b  #0x05,  r0l
        beq     LED2ON
 
        /* LED2 を消灯 */
        bclr.b  #0x01,  @P5DR:8
        jmp     @SW1LED
 
LED2ON:
        /* LED2 を点灯 */
        bset.b  #0x01,  @P5DR:8
        jmp     @SW1LED
 
        .end

ラベル定義

.equ ラベル名, アドレス

でラベルの定義ができます。H8マイコン謹製アセンブラでは

ラベル名 .equ アドレス

とか書く。まぁアセンブリ言語なんて基本命令さえ知っていればあとは雰囲気で読めるようになるので細かい違いとか割とどうでもいいです。はい。AT&T 記法と Intel 記法で操作元と操作先の記述が逆になったりしてややこしいので、どっちが操作元でどっちが操作先かは意識しておくと良いかも。ちなみに objdump とかでディスアセンブルすると AT&T 記法で吐くのでこっちに慣れておくと便利。

  • P4DR : ポート4のデータ・レジスタ
  • P4DDR: ポート4のデータ・ディレクション・レジスタ
  • P4PCR: ポート4の入力プルアップ MOS コントロール・レジスタ
  • P5DR : ポート5のデータ・レジスタ
  • P5DDR: ポート5のデータ・ディレクション・レジスタ

スタックポインタの設定

別にスタックポインタは使ってないので初期化する必要なんてないんですが、一応初期化しておくのがマナーらしいのでスタックポインタ:sp (ER7 レジスタ) に RAM 領域の底番地 (0x0FFF10) を書き込んでおく。

ポートの設定

ポートを設定するレジスタの使い方はハードウェアマニュアルを読むこと。P4 にはスイッチが接続されているので入力に、P5 には LED が接続されているので出力に、それぞれ設定。P4 は内部プルアップを有効に。

Lチカ

SW1LED: から先はスイッチを押している間だけ対応する LED が光る処理。直接I/Oポートの状態を確認する命令が無いので一旦 r0l レジスタに読み込んでから btst 命令でそれが 1 か 0 かを判断させてます。スイッチが押されていれば LED を点灯させる処理に飛び、押されていなければビットクリアします。この処理を 2 つのスイッチに対して行います。

とりあえずここまで。もう C でいいじゃんって気になってきているけど、今後もマイコンの一通りの機能はアセンブリでいじってみるつもり。

ちなみにアセンブリ書いたのは学生の頃以来でした。

組込み開発再入門(3) – リンカスクリプト(2)


組込み開発再入門(2) のリンカスクリプトの続き。

info を読む

Linux とか Unix を使っていて 「man嫁」と言われたことのある人は多いと思う。でも man だと限られた情報しかない。そこで info ですよ。リンカスクリプトに関して言えば 「info嫁」なのである。おもむろに
$info ld
とか打つと ld の info が表示されるので Scripts という章を読むと一通りリンカスクリプトについて知ることができる。

MEMORY ブロックを書いてみる

今まで SECTIONS しかないリンカスクリプトだったので MEMORY も書いてみる。MEMORY ブロックではメモリ空間の定義と空間の属性定義を行うことができる。つまり、

  • メモリ空間の開始アドレス
  • メモリ空間のサイズ
  • Read Only なのか書き込みもできるのか (ROMかRAMか)
  • Executable かどうか (プログラムを格納する空間なのか)
  • 初期化する必要があるか

といったことが定義できる。

書式はこんな感じ。

MEMORY {
    領域名1(属性):ORIGIN=開始アドレス,LENGTH=長さ
    領域名2(属性):ORIGIN=開始アドレス,LENGTH=長さ
}

ORIGIN と LENGTH は o や l と書くこともできる。

属性は省略もできる。詳しくは info を読むこと。

ATTR string 意味
R Read-only セクション (ROMとか)
W Read/write セクション (RAMとか)
X 実行可能セクション
A Allocatable セクション(実際にターゲット上に確保される)
I 初期化対象になるセクション。L でも可。
! 前述の属性を反転させる (←これがよくわからない)

で、定義したメモリ領域名はセクション定義で使うことができる。

SECTIONS {
    セクション名:{} > メモリ領域名
}

ということで、ここまで調べたことをまとめてリンカスクリプトをちょっと手直し。

OUTPUT_FORMAT("elf32-h8300")
OUTPUT_ARCH(h8300h)
ENTRY("_start")
 
MEMORY
{
    romall(rx)          : ORIGIN = 0x000000, LENGTH = 0x020000  /* 128kb */
    vectors(r)          : ORIGIN = 0x000000, LENGTH = 0x000100
    rom(rx)             : ORIGIN = 0x000100, LENGTH = 0x01ff00
 
    ramall(rwx)         : ORIGIN = 0x0fef10, LENGTH = 0x001000 /* 4kb */
    stack(rw)           : ORIGIN = 0x0FFF00, LENGTH = 0x000000 /* end of ram */
}
 
SECTIONS
{
    . = 0x0;
 
    .vectors : {
        LONG(ABSOLUTE(_start))
    } > vectors
 
    .text : {
        _text_start = . ;
        *(.text)
        _etext = . ;
    } > rom
}

なんとなく雰囲気は掴めた気がする。本当はもっといろいろ書かなければならないけどとりあえずこんな感じにしておく。ram とか stack とか定義だけしてみたけどきちんと使うのはもうちょっと後。

組込み開発再入門(2) – リンカスクリプト


組込み開発再入門(1)で適当なリンカスクリプトを書いた。
これをブラシュアップしつつもうちょっと理解してみようと思う。

セクション

とりあえずセクションという概念を理解しなければリンカースクリプトは理解できない。よく使われるセクションについて調べてみた。

.text セクション

プログラムの実行コード(機械語)を格納する。ROM領域に格納する。

.data セクション

変数の初期値を格納する。ROM領域に格納しておく。実行時にRAM領域にコピーすることで書き換え可能にする。

.bss セクション

未初期化のデータを格納する。RAM領域に格納する。

.rodata セクション

文字列定数や const で修飾された値が変わらないデータを格納する。

.stack セクション

スタック領域。H8マイコンのスタック領域は下方伸長なのでRAMの一番最後に格納する。ローカル変数の値などを格納する。

割り込み

H8マイコンはベクタ割り込み方式というのを採用している。割り込み発生時に割り込みベクタテーブルを参照し、そこに登録された割り込みハンドラにジャンプする仕組みになっている。
割り込みベクタの位置はメモリの先頭アドレス 0×000000 から 0x0000FF の 256 バイト分である。主要なベクタアドレスは以下の通り。

割り込み要因 割り込み番号 ベクタアドレス
リセット 0 0×000000
NMI 7 0x00001c
トラップ命令 8〜11 0×000020〜0x00002c
外部割り込み 12〜17 0×000030〜0×000044

メモリマップや割り込み要因と割り込み番号についての詳細は各マイコンのハードウェアマニュアルを参照すること。

ここで大事なことは、電源投入時 (リセットピンが High になったとき) 0×000000 に書かれているアドレスに処理が飛ぶということ。

再びリンカスクリプト

適当に書いてみたリンカスクリプトの SECTIONS を見てみる。
リンカスクリプトの SECTIONS はメモリの配置を決めている。

SECTIONS
{
    . = 0x0;
    .vectors : {
        LONG(ABSOLUTE(_start))
    }
    . = 0x00100;
    .text : {
        *(.text)
    }
}

. はロケーションカウンタといって、メモリのアドレスを指し示す。
ということで、

. = 0x0

は メモリアドレス 0×00000 から書き始めろという指示になる。

    .vectors : {
        LONG(ABSOLUTE(_start))
    }

ここでベクタテーブルの開始アドレス 0×00000 に _start のアドレスを書いている。_start は何かというと、アセンブラで記述したラベル (関数) である。アセンブラのソースコードを見ると、この関数 _start は .text セクションに記述されている。

    . = 0x00100;
    .text : {
        *(.text)
    }

となっていることから、_start が置かれている .text セクションはアドレス 0×00100 から書き始められているということがわかる。

ということで、処理の流れは以下のようになる。

  1. 電源が投入されるとリセット割り込みが発生し、0×00000 を見に行く
  2. 0×00000 に書かれているアドレス 0×00100 へジャンプする
  3. 0×00100 に格納されている機械語 (アセンブラで書いた _start 関数) が実行される

とりあえずここまで。
ブラッシュアップしてないじゃん。というつっこみは無しで。

組込み開発再入門(1) – Lチカ


色々理解できていないので組込み開発を学び直すことにした。

ターゲットボード

死蔵しているH8/3048F(秋月電子:K-00179)とマザーボード(秋月電子:K-00140)を有効活用することにした。
(新規購入するのであればRJ-45端子が付いているH8/3069Fネット対応マイコンLANボードをお勧めする。)

開発環境の用意

「組込みOS自作入門」という本で用意した環境をそのまま使うことにした。
開発環境の準備方法は 著者のホームページが詳しい。

リンカスクリプトの書き方は本書を参考にした。

回路図を眺める

まずは回路図を眺める。
手始めに 5P0 と 5P1 に接続されている LED を点灯させてみることにした。

ソースコード

LED点灯したら無限ループに入るだけのプログラム。

/********************************************************************************/
/* led.s - LED点灯プログラム
/********************************************************************************/
 
        .h8300h 
 
        .equ    P5DDR,  0xFFFC8
        .equ    P5DR,   0xFFFCA
 
        .section    .text
 
        .global     _start
        .type       _start,@function
 
_start:
 
        /* スタックポインタの設定 */
        mov.l   #0xFFF00, sp
 
        /* P5 を出力に設定 */
        mov.b   #0xF3, r0l
        mov.b   r0l, @P5DDR
 
        /* LED を点灯 */
        mov.b   #0x03, r0l
        mov.b   r0l, @P5DR
 
EXIT:
        bra     EXIT

わからなかったこと

  • ビット演算子の使い方がわからない
    • “bset #0, @P5DR” とかで LED ON できると思ったらできなかった
    • “bset #0, @P5DR:8″ とビット幅を指定してやるとコンパイルはできるがやはり無理
  • → “bset.b #0×00, @P5DR:8″ と記述することで無事に動作するようになった
  • .section で開始アドレスは指定できない?

リンカスクリプト

.section で開始アドレス指定できないっぽいのでリンカスクリプトを書いてみる。
とりあえず動くレベル。後でもうちょっとちゃんと書く。

OUTPUT_FORMAT("elf32-h8300")
OUTPUT_ARCH(h8300h)
ENTRY("_start")
 
SECTIONS
{
    . = 0x0;
    .vectors : {
        LONG(ABSOLUTE(_start))
    }
    . = 0x00100;
    .text : {
        *(.text)
    }
}

Makefile

自前でコンパイルしたツールチェインを /usr/local/ に置いてるのでこんな感じ。h8write にはビットレート合わせ込みシーケンスにバグがあるらしいけど問題なく書き込めてるのでまぁいいか。

PREFIX	=	/usr/local
ARCH	=	h8300-elf
BINDIR	=	$(PREFIX)/bin
ADDNAME	=	$(ARCH)-
TARGET	=	led
 
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	=	led.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)
 
$(TARGET):	$(OBJS)
			$(CC) $(OBJS) -o $(TARGET) $(CFLAGS) $(LFLAGS)
			cp $(TARGET)	$(TARGET).elf
			$(STRIP) $(TARGET)
 
$(TARGET).mot:	$(TARGET)
				$(OBJCPY) -O srec $(TARGET) $(TARGET).mot
 
image:		$(TARGET).mot
 
clean:		$(TARGET)
			rm -rf led led.elf led.mot led.o
 
write:		$(TARGET).mot
			h8write -3048 $(TARGET).mot /dev/ttyUSB0

次はビルドしたイメージファイルの中身を覗いたり、リンカスクリプトについてもうちょっと詳しく見ていきたいと思う。