[HOME]

GNU による AKI-H8 のプログラム開発

ここにかかれている内容は既に古くなっています。
H8 用 GNU ツールは http://h8300-hms.sourceforge.net/で開発が続けられています。
また、環境の構築方法はいろいろなところで説明されているのでそちらを参照ください。

当時の開発環境
当時の開発環境全景

履歴

1998年10月5日 - 公開
1998年10月9日 - メモリ/LCD増設と ldscript を追加
1998年10月15日 - Sレコード 関係と kermit の記述を追加
1998年10月21日 - gdb 移植部分と printf の仕組みの一部を追加
1998年11月15日 - アセンブラ使用時の注意点を追加
1998年12月5日 - Makefile、C で全て書く部分を追加、タイトルとBGを変更
1999年1月6日 - リンクを追加、タイトル変更
1999年1月20日 - ヘッダタグを正しく修正、HOS-H8 移植記事を公開
1999年1月21日 - 誤記を修正
1999年3月16日 - 「-relax をかしこくする」を追加
1999年3月17日 - 「int はデフォルト16ビットであることに注意」を追加
1999年4月11日 - リンクを追加
2001年9月11日 - リンクを変更、一部記述修正

秋月電子 が販売している AKI-H8 を使った開発に関するページです。
AKI-H8 を使って GNU の開発環境を FreeBSD 上で構築し、その環境を使って 電話回線接続のデータ収集装置を製作するのが目標です(いつになることやら)。

現在は AKI-H8 キットに 1Mbit SRAM と LCD表示器、およびボタンを接続して、 日立のフリーモニタを搭載して単体でモニタが起動し、gdb を使ってリモート デバッグができるようになっています。さらに、RTOS も動作しています。


このページでは以下の内容について記述しています。 内容は gcc, gdb を使って、Cプログラムを開発する知識があること、 日立のフリーモニタを構築、搭載できる知識があることを前提にしています。

GNU のクロスコンパイル環境を作成する

GCC は H8 シリーズをサポートしています。 あとはクロスコンパイラの作り方が分かれば作業は非常に簡単です。

今回は普段自分が使用している FreeBSD(-2.2.6RELEASE) で環境を作成します。 プラットフォームに依存する内容はわずかなので、linux など、 別の OS でも構築は難しく無いはずです。

準備

ここでは作成した環境は全て /usr/local/cross の下に置く物とします。

クロスCコンパイラを作るためには次のものが必要です。 適当な ftp サイトや、CD-ROM から入手してください。

また、のちの作業で使うので、次の物も一緒に用意しておきます。 さらに GNU make (gmake-3.76.1)もインストールしておいてください。 GNU make でないと newlib と gdb のコンパイルに失敗します (以下の作業では GNU make を gmake としています)。

一連の作業のために必要なディスク容量はコンパイルに 150MB 程度、 インストール先に 20MB 程度です。

まずは基本的な構築をおこなってから、それから各ツールについて AKI-H8 用の変更、移植を行って行きます。

まずバイナリユーティリティーを作成します

行う作業は非常に簡単です。target に h8300-hms を指定して configure します。 (hms は「Hitachi Micro Systems」から来ているようです。) 以上でアセンブラやリンカ、オブジェクト操作ツール類が用意できました。

次にCコンパイラを作成します

次にCコンパイラ本体を作成します。基本的には binutils の構築方法と一緒です。

標準Cライブラリ newlib のコンパイル

C の標準ライブラリを作成します。ターゲットマシン用のライブラリですので、 これまでに作成した gcc を使用します。
実行パスに /usr/local/cross/bin を追加し、コンパイラが起動できるように した上で次の作業を行います。また GNU make を使ってください。

gdb のコンパイル

ソースデバッガを作成します。H8 にはシミュレータが用意されています。 ですので、作成したプログラムの部分的なデバッグには非常に有効です。
H8(-mh)用の修正が gdb-4.16 から gdb-4.17 の間で行われているので、 gdb-4.16 ではなく gdb-4.17 を用意してください。
また GNU make でないとコンパイルの途中で失敗します。 以上でGNUのクロス開発環境の基本的な部分の作業は終りです。

できた環境を使ってみる

まずはシミュレータを使って動かして見ましょう。 /usr/local/cross/bin をサーチパスに加えてください。

簡単なサンプルソース

次のようなサンプルを用意しました。
hello.c:

#include < stdio.h> int main() { int i; for (i = 0; i < 10; i++){ printf("<%d> Hello World!\n", i); } }
H8 用なのに printf ?と気づかれた方もいると思います。でも気にしないで 先に進みましょう。

コンパイルします

h8300-hms-gcc -o hello -g -mh hello.c
ここで -g と -o についての説明の必要は無いでしょう。
-mh は H8/300H 用のコードを生成するオプションです。 これを付けない時は H8/300 用のコードを作ってしまうので 忘れずに付けてください。

gdb のシミュレータ上で実行します

今作成したオブジェクトを gdb のシミュレータで動かします。 実際に動かした時の様子を次に示します。
% h8300-hms-gdb hello
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i386-unknown-freebsd2.2.6 --target=h8300-hms"...
(gdb) set machine h8300h
(gdb) target sim
Connected to the simulator.
(gdb) load
Loading section .text, size 0x7382 lma 0x100
Loading section .data, size 0x2b8 lma 0x7482
Loading section .stack, size 0x4 lma 0x3fffc
Start address 0x100
Transfer rate: 242160 bits in <1 sec.
(gdb) run
Starting program: /tmp/hello 
<0> Hello World!
<1> Hello World!
<2> Hello World!
<3> Hello World!
<4> Hello World!
<5> Hello World!
<6> Hello World!
<7> Hello World!
<8> Hello World!
<9> Hello World!

Program received signal SIGTRAP, Trace/breakpoint trap.
0x56c in _exit (rc=4352) at _exit.c:14
_exit.c:14: No such file or directory.
(gdb)
動きましたか?さらに printf の結果も出力されています。
最初に set machine h8300h を実行するのを忘れないでください。

まとめ

コンパイル時に注意するのは です。

シミュレータデバッグ時の手順をまとめると

  1. h8300-hms-gdb を起動する(OS標準の gdb を呼ばないこと :-) )
  2. set machine h8300h で H8/300H である事を宣言する。
  3. target sim で実行ターゲットとしてシミュレータに接続する。
  4. load でターゲットにプログラムをロードする。
です。

ブレークポイントの設定の仕方、ソースデバッグの方法は通常の gdb の使い方 と同じです。
クロス開発時には次のコマンドも覚えておくと良いでしょう。

詳しく知りたい方は gdb の info などをお読みください。

日立のモニタ用に環境をいじる

メモリ増設とLCD接続

リセット直後のメッセージ GNU C でちょっとしたプログラムを作成し、 モニタを使ってメモリ上でデバッグすると H8/3048 本体の 4KB のメモリでは 小さすぎて思うようなプログラムが作れません。 そこで 1MBit の SRAM メモリ(HM628128)を増設しました。

接続は簡単です。データ、アドレスラインは1対1に、CS2 は RES(CN2-4)に -CS は P8-3/-CS1(CN1-6)に -WEは P6-5/HWR(CN2-8)に -RDは P6-4/-RD(CN2-7)に 結線するだけです。
あとは CN5 でモード5 に設定し、モニタの起動時各ポートのモードを設定する コードを追加すればモニタから使えるようになります。
実際の設定は次のようにしました。

そして、仮想ベクタ空間を SRAM の先頭番地から配置しました。

同様に LCD 表示器を接続しました。接続は PortB で D0〜D7 を、 PortA-7 が RS、PortA-6 が E 、R/W はGND固定にしました。
こちらもモニタ起動時にポートおよび LCD の初期化のコードを追加しました。

電源をいれると 「Welcome!!」と表示してくれます :-)

メモリ配置を定義

こんどは SRAM 空間でコンパイルしたプログラムが動作するように、 リンカのアドレス定義を変更します。デフォルトの定義は /usr/local/cross/h8300-hms/lib/ldscripts/h8300h.x にされています。

128KByte の SRAM 空間をフルに使用することにします。全てRAM空間ですので 順に先頭からベクタ、コード、データと割り振りました。今回定義した例を 次に示します。

OUTPUT_FORMAT("coff-h8300")
OUTPUT_ARCH(h8300h)
ENTRY("_start")
MEMORY
{
	/* 0xc4 is a magic entry.  We should have the linker just
	   skip over it one day... */
	vectors : o = 0x20000, l = 0x100
	ram    : o = 0x020100, l = 0x1fefc
	stack  : o = 0x03fffc, l = 0x4		/* 0x20000 - 0x3ffff */
}
SECTIONS 				
{ 					
.vectors : {
	/* Use something like this to place a specific function's address
	   into the vector table. 
	SHORT(ABSOLUTE(_foobar)) */
	*(.vectors)
        }  > vectors
.text :	{ 					
	*(.rodata) 				
	*(.text) 				
	*(.strings)
   	 _etext = . ; 
	}  > ram
.tors : {
	___ctors = . ;
	*(.ctors)
	___ctors_end = . ;
	___dtors = . ;
	*(.dtors)
	___dtors_end = . ;
	}  > ram
.data : {
	*(.data)
	*(.tiny)
	 _edata = . ; 
	}  > ram
.bss : {
	 _bss_start = . ;
	*(.bss)
	*(COMMON)
	 _end = . ;  
	}  >ram
.stack : {
	 _stack = . ; 
	*(.stack)
	}  > stack
}
ld に -T オプションで指定すること(gcc には -Wl,-T)で、上のメモリ配置で オブジェクトを作成してくれます。
gcc -mh -o hello.coff -Wl,-T[scriptfile] hello.c

Sフォーマットレコードに変換

作成したオブジェクトをモニタにロードするために Sレコードフォーマットに 変換します。リンカで直接 Sレコードを出力することができますが、 (後の)ソースデバッグの事を考えて、リンカ側で対処せず、objcopy ツール を使用します。

次のように使います。

h8300-hms-objcopy -Osrec 作成オブジェクト Sレコードファイル
これで Sレコードファイルができます。

binutils S2 フォーマットに変換できない問題を修正

objcopy で作成された Sレコードは
前述のメモリ配置 の状態のオブジェクトを変換すると S3 フォーマットで出力します。 実は日立のフリーモニタはこの S3 フォーマットを理解してくれません (Sフォーマットの詳細は各自で調べてください)。
配置アドレスからすると S2 フォーマットで十分のはずです。

調べるとソースがおかしい部分があったので、修正します。 このパッチ(srecfix.diff.gz[231bytes]) を binutils-2.8.1/bfd/srec.c に対して当てて、binutils を make 、make install しなおしてください(オブジェクトの最終アドレスが 16bit 以内で表現できれば S1, 24bit ならば S2, それ以上だと S3 となる コードのようですが、S2 となる条件の評価が間違っているので S3 と判断 してしまいます)。

cd binutils-2.8.1
gunzip -d srecfix.diff.gz | patch -p1
gmake
gmake install
これで objcopy で前述の手順で変換してみてください。 S2 フォーマットで出力されるようになるはずです。

kermit でモニタと通信

モニタとの通信はシリアルポートを通じて行います。 通信ソフトには私は kermit を使っています。

FreeBSD には標準でインストールされていないので自分でインストールします。 インストール手順は簡単です。

  1. ソース cku192.tar.gz を入手する。
  2. ソースを展開する
    mkdir cku192
    cd cku192
    tar xvfz [somewhere]/cku192.tar.gz
    
  3. makefile を必要に応じて修正します。私は次の部分を変更しました。
    • MANDIR を /usr/man/manl から /usr/local/man/man1 に
    • MANEXT を l から 1 に
  4. make freebsd2
  5. make install
  6. ckermit.ini を自分の home ディレクトリに .kermrc としてコピーします。
  7. ~/.mykermrc を次のようにする
    set line [モニタとの通信ポート(/dev/cuaa0)]
    set speed [モニタとの通信レート(19200)]
    set terminal bytesize 8
    set command bytesize 8
    ;
    ; don't wait prompt when transmit (default: wait 0x0a)
    set transmit prompt 0
    
kermit を起動すると C-Kermit> とプロンプトが出るので、c (connect) と入力するとリモートにつながります。
モニタにプログラムをロードする時はモニタで l コマンドを実行してから Ctrl-\, Ctrl-C で kermit のプロンプトを再び出し、
transmit Sレコードファイル
と打ち込むとファイルが読み込まれます。読み込み終了後再び c (connect)とすると モニタのプロンプトに戻ります。

終了する時は Ctrl-\, Ctrl-C でプロンプトに戻り q (quit) とすれば 終了します。


gdb でリモートデバッグができるようにする

gdb を日立のモニタに対応させる

gdb では
前述のシミュレータを使って、 ソースデバッグができます。 一方 H8の方には、日立のモニタが起動し、今回製作したクロスコンパイル環境で コンパイルしたプログラムを RS-232C 経由でロードし、実行できるように なっています。 今度はこれらモニタと gdb を直接接続して、開発プログラムを実際の H8 ボード 上でリモートデバッグできるようにします。

gdb には hms のモニタ用のリモートデバッグ機能が標準で組み込まれています。 しかし、これはそのまま今回使用している日立のモニタに対応していません。 そこで、この hms 用のリモートデバッグ機能の部分を日立のモニタ用に改造 しました。

改造は最初のころ非常に難しいと思っていましたが、gdb のリモートデバッグ機能は 最初からいろいろなモニタに対応できるような仕組みが用意されていて、 その動作が分かってしまったら、以外と簡単にできました。

次の手順で gdb を作り直してください。

以上で改造は終りです。

実機をリモートデバッグだ!

プログラムをコンパイルして gdb でリモートデバッグしてみます。

次のようなサンプルプログラムを用意します。

foo.c:

int func(int i); int main() { int i, sum; sum = 0; for (i = 0; i < 10; i++){ sum += func(i); } } int func(int i) { return i*i; }
これをコンパイルします。
h8300-hms-gcc -mh -o foo.coff -g -Wl,-T[scriptfile] foo.c
テストプログラムが用意できたので、gdb でデバッグします。

シミュレータの時は target に sim を指定しましたが、 今回は hms を指定します。 シリアルポートで接続するので、デバイスとシリアルスピードの指定も行います。 この時 H8 側は電源をいれてモニタが起動している状態にしておいてください。

実際に動かした時の様子を次に示します。

% h8300-hms-gdb foo.coff 
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i386-unknown-freebsd2.2.6 --target=h8300-hms"...
(gdb) set machine h8300h                   <---- H8/300H を指定する
(gdb) set remotebaud 19200                 <---- シリアルポートのボーレート
(gdb) target hms /dev/cuaa0                <---- モニタにコネクト
Remote target hms connected to /dev/cuaa0
0x0 in ?? ()
(gdb) load                                 <---- プログラムをロード
.vectors        : 0x00020000 .. 0x00020000  
.text   : 0x00020100 .. 0x00021054  
.tors   : 0x00021054 .. 0x00021054  
.data   : 0x00021054 .. 0x000212ec  
.stack  : 0x0003fffc .. 0x00040000         <-- section 毎のロード情報を表示
Transfer rate: 5248 bits/sec.              <-- 転送レートを表示
(gdb) b main
Breakpoint 1 at 0x20134: file foo.c, line 6.
(gdb) b func
Breakpoint 2 at 0x2018c: file foo.c, line 15.
(gdb) run
Starting program: /home/work/h83/foo.coff 

Breakpoint 1, main () at foo.c:6
6               sum = 0;
(gdb) n
8               for (i = 0; i < 10; i++){
(gdb) n
9                       sum += func(i);
(gdb) n

Breakpoint 2, func (i=0) at foo.c:15
15              return i*i;
(gdb) p i
$1 = 0
(gdb) n
16      }
(gdb) n
main () at foo.c:8
8               for (i = 0; i < 10; i++){
(gdb) c
Continuing.

Breakpoint 2, func (i=1) at foo.c:15
15              return i*i;
(gdb) c
Continuing.

Breakpoint 2, func (i=2) at foo.c:15
15              return i*i;
(gdb) c
Continuing.

Breakpoint 2, func (i=3) at foo.c:15
15              return i*i;
(gdb) n
16      }
(gdb) n
main () at foo.c:8
8               for (i = 0; i < 10; i++){
(gdb) n
9                       sum += func(i);
(gdb) p sum
$2 = 14
(gdb) q
これでリモートデバッグができるようになりました。
なお、gdb 起動時に毎回同じコマンドを打ち込むのが面倒なので、 gdb 起動時のカレントディレクトリに .gdbinit というファイルにコマンドを あらかじめ記述しておくと良いでしょう。
.gdbinit:

set machine h8300h set remotebaud 19200

RTOS を載せる

フリーの H8 用 uITRON準拠 RTOS HOS-H8 を GNU 開発環境用に移植しました。
こちらを御覧ください。

Tips

Makefile を用意する

毎回自作ライブラリ、リンカスクリプトファイルの指定や、Sフォーマット変換 などを指定するのは大変なので、Makefile を作成します。
私のところでは次のようにしています。
     1	AS      = h8300-hms-as
     2	CC      = h8300-hms-gcc
     3	LD      = h8300-hms-gcc
     4	OBJCOPY = h8300-hms-objcopy
     5	
     6	LOCALDIR = /usr/local/cross/hoso
     7	INCDIR = ${LOCALDIR}/include
     8	LIBDIR = ${LOCALDIR}/lib
     9	LDSCRIPT = ${LIBDIR}/aki-h8.sh
    10	#LDSCRIPT = ${LIBDIR}/sim.sh
    11	LIBC = ${LIBDIR}/libc.a
    12	VECTOR = ${LIBDIR}/vector.o
    13	
    14	CFLAGS = -mh -g -O -I${INCDIR}
    15	LDFLAGS = -mh -nostdlib -Wl,-T${LDSCRIPT} 
    16	MAP = -Wl,-Map,$*.map
    17	
    18	PROG = hello.mot
    19	OBJS = hello.o
    20	
    21	.SUFFIXES:      .mot .coff .o .c .s .h
    22	
    23	.coff.mot:
    24	        ${OBJCOPY} -Osrec ${.IMPSRC} ${.TARGET}
    25	
    26	all:    ${PROG}
    27	
    28	${PROG}:        ${OBJS}
    29	        ${LD} ${LDFLAGS} ${MAP} -o $*.coff ${LIBDIR}/crt0.o ${OBJS} ${LIBC} -lgcc ${VECTOR}
    30	        ${OBJCOPY} -Osrec $*.coff $*.mot
    31	
    32	clean:
    33	        rm -f *.x *.coff *.mot *~ *.o *.map
6行目〜8行目で、自作のライブラリや、インクルードファイル等のディレクトリ を定義し、ここに 自作 libc や AKI-H8 用のメモリ配置定義スクリプトファイ ルをインストールしています。
9行目〜10行目の LDSCIPRT で AKI-H8 と gdb シミュレータ のどちら用にリン クするか切替えています。
15行目でリンカ(cc)に渡すオプションを決めます。 -nostdlib は newlib 標準 ライブラリ(newlib)と、標準 crt0.o をリンクをしないオプションです。これら は自作の物を直接指定します。これを行うと GCC が内部で使用するライブラリ libgcc.a もリンクしなくなるので 29 行目で指定しています(ここで指定して いるのはライブラリの指定順番の関係)。-Wl,-T でリンカスクリプトをリンカ に渡します。
その他の内容については各自で調べてください。

文字列出力(printf)をシミュレータと実機両方で使えるようにする

シミュレータのサンプルプログラムでは printf を使用しました。そして、 そのプログラムを gdb のシミュレータで実行するとその文字列が表示されました。 なぜ、コンソールも持たない H8 の組み込みプログラムからシミュレータに文字 が表示できるのでしょうか?その理由を調べて見ましょう。

リンク時アドレス配置用スクリプトファイル (/usr/local/cross/h8300-hms/lib/ldscripts/h8300h.x) を見てください。

     4  /* The memory size is 256KB to coincide with the simulator.
     5     Don't change either without considering the other.  */
     6  MEMORY
     7  {
     8          /* 0xc4 is a magic entry.  We should have the linker just
     9             skip over it one day... */
    10          vectors : o = 0x0000, l = 0xc4
    11          magicvectors : o = 0xc4, l = 0x3c
"0xc4 is a magic entry" というのは何のことを差しているのでしょうか? この答えは gdb のシミュレータソース (gdb-4.17/sim/h8300/compile.c) にあります。
   442                    /* And a jsr to 0xc4 is turned into a magic trap */
   443  
   444                    if (dst->opcode == O (O_JSR, SB))
   445                      {
   446                        if (dst->src.literal == 0xc4)
   447                          {
   448                            dst->opcode = O (O_SYSCALL, SB);
   449                          }
   450                      }
 
opcode が JSR で literal が 0xc4 は "magic trap" だとあり、 opcode に SYSCALL を代入しています。SYSCALL 実行部分は次のようになっています。
  1278          case O (O_SYSCALL, SB):
  1279            {
  1280              char c = cpu.regs[2];
  1281              sim_callback->write_stdout (sim_callback, &c, 1);
  1282            }
cpu.reg[2] を write_stdout() に渡しています。cpu.reg[2] は ER2 のことです。 つまり、 er2 の値を stdout に表示しているのです。
では newlib で上記部分を呼んでいるところを探してみると、ありました。 (newlib-1.8.0/newlib/libc/sys/h8300hms/write.c) に
     3  int _write(file, ptr, len)
     4       int file;
     5       char *ptr;
     6       int len;
     7  {
     8    int todo;
     9    
    10    for (todo = 0; todo < len; todo++) 
    11      {
    12        asm("mov.b #0,r1l\n mov.b %0l,r2l\njsr @@0xc4"   :  : "r" (*ptr++)  : "r1", "r2");
    13      }
    14    return len;
    15  }
のコードが存在します。

これらのことから次の事が分かります。

ER2 に ASCII コードを設定して、jsr 0xc4 すると、シミュレータ内部では 0xc4 からのコードを処理することなく、設定された ASCII コードを stdout に 出力する。
ということは、ベクタ 0xc4 にそのターゲット環境の1文字表示ルーチンを作る ことで、シミュレータとターゲットとで、同じ表示関数が使えることになります。

私は 0xc4 から LCD に1文字表示するプログラムを割り当ててあります。 LCD は 16x2行ですので、2行分32文字を1行分と定義し、改行が来ると次の表示 は先頭から表示し直すようにして、printf そのものを使えるようにしました。

その部分のメインコードを次に示します。

lcdout:				; r0l :	カーソル
	push.l	er0
	push.l	er3

	cmp.b	#'\n', r2l	; 改行ならカーソルをホームに
	bne	l1
	mov.b	#0, r0l
	mov.b	r0l, @_LcdCurPos
 	bra	l4

l1:	mov.b	@_LcdCurPos,r0l	; 表示するのが先頭ならクリア
	bne	l2

	mov.b	#0b00000001, r0h	
	PUTCMD	r0h		; clear screen
	mov.l	#0x1000,er3	; wait 2ms
w2a:	sub.l	#1,er3
	bne	w2a

l2:	cmp.b	#32, r0l	; 32文字以上は表示できない
	bcc	l4
	mov	r0l, r0h
	cmp.b	#16, r0l	; 16文字以上は2行目に表示
	bcs	l3
	add.b	#(0x40-16), r0l
l3:	or.b	#0x80, r0l
	PUTCMD	r0l		; カーソル位置セット
	PUTDAT	r2l		; 文字出力
	inc.b	r0h
	mov.b	r0h, @_LcdCurPos
	
l4:	pop.l	er3
	pop.l	er0
	rts

C でベクタテーブル、割り込みルーチンを記述する

アセンブラ使用時の注意点

H8/300H の命令セットのビット操作命令(良く使うのは btst や bset などでしょ う)のアドレッシングモードで、絶対アドレス(@aa)をアセンブラ記述する際、 ビット幅指定 :8 を忘れずに付けてください。
許されるのは 8ビット(@aa:8) のみですが、:8 を付け忘れた場合 gas は 24ビットアドレスが指定されたものとして H8/300H では正常に動作しない コードを出力してしまいます。
このコードを AKI-H8 で実行しても暴走しません。しかも該当部分の動作は 行われません。そして、「ちゃんと書いてあるのになんで動かないんだ!」 と悩むことになります。
これらは 内蔵ペリフェラルの操作をアセンブラで記述する場合に良く使用す ると思います。例えば SCI で1文字送信する場合などポーリングを使う場合 次のようにアセンブラで記述すると思います(AKI-H8 のサンプルソースも 同じ書き方)。
        btst.b  #7,@SSR0:8      ; 送信可能か?
        beq     loop
        mov.b   r0l,@TDR0       ; バッファが空で送信可能なら
        bclr.b  #7,@SSR0:8      ; そのまま送信
ここで :8 を付けるのを忘れないようにしましょう。

-relax をかしこくする

リンカのオプションに -relax オプションがあります。リンカのマニュアルによ ると、必要に応じてアドレッシングモードを小さい物に置き換えてくれます。 これはコードサイズを小さくするのと共に、実行スピードを早くするの に効果的です。特に内蔵ペリフェラルは8ビットアドレス空間で、また内蔵RAM は16ビットアドレス空間でアクセスできるようになっています。
ところがCソースから -relax つきで生成したコード(または、アドレスを定数定 義したアセンブラから生成したコード)を逆アセンブルしてみると、 8ビットアドレッシングのコードではなく24ビットアドレッシングのコードのま まです。
これを としても該当部分の逆アセンブラソースは となり、8ビットアドレッシングコードになっていないことがわかります。

-relax はバグっているのか?というと、そうではありません。ではどのような時 に期待している動作ができるかというと、「アドレス解決がリンカによって行わ れる時」だけなのです。
つまり、リンカはアドレスを解決するようにアセンブラによって印が付けられた インストラクション部分についてのみ、アドレッシングモードの最適化を行うの です。
ですので、上記のようにアセンブラの時点までにアドレスが決まってしまった物 に付いてはリンカは処理を素通りさせてしまい、結果として期待通りの動作が 得られないということになっていたのです。

そこで、定数定義した物もリンカにもう一度アドレス解決してもらうように gas を改造します。
このパッチ(gas-relax.diff.gz[319bytes]) を binutils-2.8.1/gas/config/tc-h8300.c にあてて、gas をコンパイルしなお します。
このあと先程のプログラムをコンパイルしなおすと、

となり、期待通りの結果になったことがわかります。

これで、目的は達成できるのですが、relax 処理にいくつかのバグがあるので、 このパッチ(ld-relax.diff.gz[425bytes]) も binutils-2.8.1/bfd/coff-h8300.c に当てておくと、より幸せになります (このパッチはアドレス 0xff8000 の解決でエラーとなる問題と、bsr の 16 → 8の置き換えが signal 6 で core dump する問題に対処しています)。

int はデフォルト16ビットであることに注意

h8300-hms-gcc の int はデフォルト16ビット幅です。通常 gcc が対象とするアー キテクチャではほとんどが int は32ビットとされています。このため、これを 仮定して作られているソースが newlib などに見受けられますので、オーバーフ ローに注意が必要です。
私が引っかかったのは、mktime() です。libc/time/mktime.c のソースには、 とありますが、右辺側の変数は全て int なので、16ビット幅での演算となり、 18時12分35秒を越えるとオーバーフローを起こします。 とキャストします。

リンク

$Id: index.html,v 1.3 2003/06/29 16:03:23 hoso Exp $
[HOME]