AKI-H8/3664用スタートアップルーチン「startup3664.src」

Copyright(C) 10Nov2002
coskx 

【1】スタートアップルーチン「startup3664.src」
 このスタートアップルーチンはAKI-H8/3664をWindowsマシンでクロスコンパイルして使用している学習者向けのものです。
 スタートアップルーチンとは,マイコンの電源ONと同時に起動し,必要な処理を行なって,Cで記述された関数main()をcallするまでの作業を行なうプログラムです。
スタートアップルーチンの役割は
(1)リセットベクトルの設定
(2)割り込みベクトルの設定
(3)スタックポインタの設定
(4)必要なROM領域変数のRAM領域へのコピー
です。
これにマシン語でないと記述できない関数や,割り込み関数のレジスタ退避部をおまけとしてつけます。
(Cで記述できるかどうかはコンパイラに依存します。秋月で販売されているCコンパイラ[評価版Ver2]に対応させてあります。)

 AKI-H8/3664のサンプルプログラムで与えられているスタートアップルーチンresetv.marはとても貧弱で,変数が予期せずにROM領域になったりで学習者はとても苦しみます。たとえば
 void func(void)
  {
      static int tick=0;
           :
      tick++;
  }
ではtickがROM領域に割り付けられるため,tickは0から変化しません。

この現象に対処するには次のような操作が必要です。
1)初期値はリンカによってROM領域にセットされます。
2)スタートアップルーチンでROMの変数領域をRAMにコピーする。
3)実行ルーチンではRAM側をアクセスする。
この操作を可能にするのはリンカのROMオプションとスタートアップルーチンです。

なおこのstartup3664.srcから作ったstartup3664.objファイルは
ここ」で取り込んでください。

【2】本スタートアップルーチンの特徴
 (1)初期化されたグローバル変数,関数内static変数を,実行時にはRAMに割り当てて
    正常に動作させるようにしました。
 (2)TimerAのタイマ割り込みの利用をサポートします。
 (3)関数main()から戻ってきても,無限ループに入るようにしました。
 (4)CPUのコントロールレジスタのCCRでの割り込みマスクビットのセット・リセット関数をCプログラムに提供します。
   E_INT()  割り込み許可
   D_INT()  割り込み禁止

【3】本スタートアップルーチンを利用した場合のプログラム中での変数の取り扱い
(1)初期化されていないグローバル変数 RAM領域
(2)初期化されたグローバル変数    RAM領域
(3)初期化された関数内static変数   RAM領域
(4)const修飾子のある初期化されたグローバル変数    ROM領域
(5)const修飾子のある初期化された関数内static変数   ROM領域
 オート変数はconst修飾語をつけてもスタック領域にセットされ,値の変更をコンパイラがチェックするだけです。(値をプログラム中で変更するとコンパイルエラーとなる。)
すなわち,大きな定数表(配列)をグローバル変数で与えるときにはconst修飾子をつけるとROM領域に割り当てられ,少ないRAMしか持たないH8のRAM節約になることを意味します。(関数内で大きな定数表(配列)を与える場合はconst staticにすればよい。)

【4】スタートアップルーチン「startup3664.src」のソース

スタートアップルーチン「startup3664.src」のソース

;************************************************************************
;startup routine    startup3664.src      Copyright (C) 2002 coskx
; 28Jan2004 30 Oct 2002
;************************************************************************
    .CPU 300HN

    .IMPORT _main, _interrupt_cfunc

    .EXPORT _E_INT,_D_INT

    .SECTION A,DATA,LOCATE=H'0000
RSTVEC: .DATA.W START  ;リセットベクトル

    .ORG H'0026
    .DATA.W TIME_A  ;タイマーA割り込みベクトル

    .SECTION P,CODE,ALIGN=2
START:
    MOV.L #H'FF80,ER7 ;スタックポインタ設定

    ;move Section D to Section X
    MOV.L @_D_Head,ER0      ;source address to ER0
    MOV.L @_X_Head,ER1      ;destination address to ER1
    MOV.L @_D_Size,ER2      ;size to be copied to ER2
    OR.L ER2,ER2            ;(ER2 or ER2) to ER2
    JMP @LOOP_11
LOOP_1:
    MOV.B @ER0+,R3H         ;source byte to R3H with ER0++
    MOV.B R3H,@ER1          ;R3H to destination
    ADDS #1,ER1             ;increment destination address
    DEC.L #1,ER2            ;ER2--
LOOP_11:
    BNE LOOP_1

    ;fill 0 to Section B
    MOV.L @_B_Head,ER1      ;destination address to ER1
    MOV.L @_B_Size,ER2      ;size to be copied to ER2
    MOV.B #0,R3H            ;0 to R3H
    OR.L ER2,ER2            ;(ER2 or ER2) to ER2
    JMP @LOOP_21
LOOP_2:
    MOV.B R3H,@ER1          ;R3H to destination
    ADDS #1,ER1             ;increment destination address
    DEC.L #1,ER2            ;ER2--
LOOP_21:
    BNE LOOP_2

    JSR @_main        ; Call main()
EternalLoop: BRA EternalLoop ;万が一戻ってきてもOK

    .SECTION P,CODE,ALIGN = 2;
_E_INT: ANDC.B #B'01111111,CCR ;Clear Interrupt mask = Enable Interrupt
    RTS
    .SECTION P,CODE,ALIGN = 2;
_D_INT: ORC.B #B'10000000,CCR ;Set Interrupt mask = Disable Interrupt
    RTS

IENR1 .EQU   H'FFF4  ;割り込みイネーブルレジスタ
IRR1  .EQU   H'FFF6  ;割り込みフラグレジスタ
IENTA .BEQU  6,IENR1 ;タイマーAイネーブルビット
IRRTA .BEQU  6,IRR1  ;タイマーA割り込みフラグビット
    .SECTION P,CODE,ALIGN = 2;
TIME_A: PUSH.L ER0
    PUSH.L ER1
    PUSH.L ER2
    PUSH.L ER3
    PUSH.L ER4
    PUSH.L ER5
    PUSH.L ER6

    BCLR IENTA  ;割り込み停止
    BCLR IRRTA  ;割り込みフラグをクリアー

    JSR @_interrupt_cfunc

    BSET IENTA  ;割り込み再開

    POP.L ER6
    POP.L ER5
    POP.L ER4
    POP.L ER3
    POP.L ER2
    POP.L ER1
    POP.L ER0
    RTE

; 割り込み停止,Clear IMFA,割り込み再開をここに書いておくことにより
; ユーザは割り込み処理内容だけに専念できる

    .SECTION    D,DATA,ALIGN=2
    .SECTION    X,DATA,ALIGN=2
    .SECTION    B,DATA,ALIGN=2

    .SECTION    C,DATA,ALIGN=2
_D_Head:   .DATA.L     (STARTOF D)     ; D Head Address
_X_Head:   .DATA.L     (STARTOF X)     ; X Head Address
_D_Size:   .DATA.L     (SIZEOF D)      ; D Size
_B_Head:   .DATA.L     (STARTOF B)     ; B Head Address
_B_Size:   .DATA.L     (SIZEOF B)      ; B Size
    .END

SECTION A............................アブソリュート領域(このスタートアップルーチンのみで使用)
SECTION P............................プログラム領域(Cで記述したプログラムのオブジェクトもここに入る)
SECTION C............................コンスタント領域(Cで記述したプログラムの,constのついたグローバル変数とconstのついたstatic変数はここに入る)
SECTION D............................データ領域(Cで記述したプログラムの,初期化されたグローバル変数のROM割り当て領域[初期化時にのみ使用される])
SECTION X............................データ領域(Cで記述したプログラムの,初期化されたグローバル変数のRAM割り当て領域[実行時にのみ使用される])
SECTION B............................データ領域(Cで記述したプログラムの,初期化されていないグローバル変数のRAM割り当て領域)

【5】オブジェクトファイル「startup3664.obj」の作り方
次のコマンドで「startup3664.src」から「startup3664.obj」を作る。

>asm38 startup3664
H8S,H8/300 SERIES CROSS ASSEMBLER Ver. 2.0A Evaluation software
Copyright (C) Hitachi, Ltd. 1994,1998
Copyright (C) HITACHI MICROCOMPUTER SYSTEM LTD. 1994,1998
Licensed Material of Hitachi, Ltd.
  *****TOTAL ERRORS       0
  *****TOTAL WARNINGS     0


【6】リンカオプションを記述したSUBファイルの例
INPUTオプションにstartup3664.objを加える。

OUTPUT xxxxxx
PRINT xxxxxx
INPUT xxxxxx
INPUT c:\Progra~1\h8_3664\startup3664.obj
LIB c:\Progra~1\h8_3664\c38hn.lib
ROM (D,X)
START P,C,D(34),X,B(0F780)
EXIT

「ROM (D,X)」の指示でSECTION DとSECTION Xが結び付けられる。


 【7】このスタートアップを利用したタイマー割り込みプログラミングの例

タイマー割り込みプログラム例1
プログラム中main()側で約33msec(0.033秒)間隔でタイマ割り込みを設定
main()関数内でループ動作をしている最中に,割り込み関数interrupt_cfunc()は0.033秒ごとに起動し,LEDのON-OFFを行なう。
このタイマ割り込みはTimerAが使われている。


/*タイマー割り込みを起動し,LEDの点滅制御を行なう*/
#include "H8_3664.h"

volatile int cnt=0;

main()
{
    initLed();/*LEDの初期化*/
    initTimerAInt(2); /*タイマAによる約30Hz割り込み*/
    E_INT(); /*CPU 割り込み動作許可*/
    startTimerAInt(); /*タイマA起動*/
    while (1) {
        if (cnt<15) {
            turnOnLed(0);
            turnOffLed(1);
        } else {
            turnOnLed(1);
            turnOffLed(0);
        }
    }
}

/*割り込み関数 関数名は変更してはならない*/
void interrupt_cfunc(void)
{
    cnt++;
    if (cnt==30) cnt=0;
}

参考
以下の3つの関数は「h8_3664.h」からの抜粋したタイマー割り込み関係の関数である

/*
TimerAを利用したタイマー割り込み
割り込み関数の名前は void interrupt_cfunc(void)で,これは
startup3664.mar中で決まっている。

void initTimerAInt(short int n)
  TimerAを利用したタイマー割り込み初期化
void startTimerAInt(void)
  TimerAを利用したタイマー割り込みスタート
void stopTimerAInt(void)
 TimerAを利用したタイマー割り込みストップ
*/

/*TimerAを利用したタイマー割り込み
割り込み関数の名前はvoid interrupt_cfunc()で,固定
TimerAを利用したタイマー割り込み初期化
 引数の値とタイマー割り込み周波数
7    7812.5[Hz]
6    1953.125[Hz]
5     488.28125[Hz]
4     244.140625[Hz]
3     122.0703125[Hz]
2      30.51757813[Hz]
1      15.25878906[Hz]
0       7.629394531[Hz]
*/
/*
void initTimerAInt(short int n)
{
    TA.TMA.BIT.CKSI=n;
}
*/
#define initTimerAInt(n) TA.TMA.BIT.CKSI=n

/*TimerAを利用したタイマー割り込みスタート*/
/*
void startTimerAInt(void)
{
    IENR1.BIT.IENTA=1;
}
*/
#define startTimerAInt() IENR1.BIT.IENTA=1

/*TimerAを利用したタイマー割り込みストップ*/
void stopTimerAInt(void)
{
    IENR1.BIT.IENTA=0;
    IRR1.BIT.IRRTA=0;
}