ポータブル・ゲーム・システム

秋月電子のLEDマトリックス表示装置を使って、ポータブル・ゲーム・システムを 作りました。

電池駆動ですから、ポータブルですよ。

ポータブル・ゲーム・システムの完成写真

アジェンダ

回路図

ポータブル・ゲーム・システム回路図

部品リスト

ポータブル・ゲーム・システムの部品表
部品番号品名規格個数メーカ調達先
U1マイコンMC9S08QG8CPB1Freescaledigi-key
U2汎用ロジック74HCT041東芝梅澤無線
U33端子レギュレータTA48M0331東芝秋月電子
-ICソケット14P DIP1-秋月電子
-ICソケット16P DIP1-秋月電子
-32×16ドットLEDマトリックス表示装置パーツセットK-007981-秋月電子
R1カーボン抵抗1.0kΩ 1/4W1KOA秋月電子
R2カーボン抵抗2.2kΩ 1/4W1KOA秋月電子
R3, R4, R5, R6カーボン抵抗100Ω 1/4W1KOA秋月電子
R7カーボン抵抗10kΩ 1/4W1KOA秋月電子
C1, C4アルミ電解コンデンサ100µF 25V2Ruby-conマルツパーツ
C2, C5, C6, C7積層セラミックコンデンサ0.1µF 50V4ムラタ製作所秋月電子
C3積層セラミックコンデンサ0.33µF 50V1ムラタ製作所秋月電子
SW1押しボタンスイッチDP1-1201フジソク梅澤無線
VR1可変抵抗器10kΩ B 16φ1-梅澤無線
-ツマミ-1-梅澤無線
CN4ターミナル・ブロック2P1-秋月電子
PS1圧電スピーカPS17401TDKdigi-key
HD1ボックス・ピン・ヘッダ2×31-ダイセン電子
-片面ユニバーサル基板ICB-96G1サンハヤト-
-ワッシャつきなべビス3mm×10mm4-廣杉計器
-スペーサM3 15mm4-廣杉計器

実体配線図

ポータブル・ゲーム・システムの部品面実体配線図
ポータブル・ゲーム・システムの半田面実体配線図

ハードウェアの解説

直列化シリアル・インターフェース

このアプリケーションは、表示装置として秋月電子でキットとして販売されている 32×16ドットの表示装置を流用しました。 このキットは、48ビットのデータを16ビットずつ3系統のシリアルデータとして 送受信するように設計されています。

もちろん、このまま使用することも出来ますが、 このインターフェースを使用するときには、以下の問題点が有ります。

  • データの送信に4本の端子が必要である。
  • 汎用ポートをソフトウェアで操作しなくてはならない。

どちらも、16ピンのMC9S08QG8を使用するときには大きな問題になります。 そこで、表示装置の配線を工夫して、データ線を完全に直列にしました。

キットでは、3本のデータ出力端子が装備されているので、 そのうちの2本をキットの入力端子に戻してやります。 こうすると、マイコンからは48ビットの1本のシリアルインターフェースとして見る事ができます。 完全なシリアル・インターフェースは、 Serial Peripheral Interface (SPI) モジュールを使用してデータの送信を行うことができます。 そのため、ソフトウェア処理のオーバヘッドが減少し、パフォーマンスの向上が期待できます。

電源の違いを克服

LED表示装置は5Vの電源を必要としますが、 マイコンの最大電源電圧は3.6Vです。 このギャップを埋めるために、74HCT04という5V電源で動作する TTLレベル信号対応の汎用ロジックICを使って、 インターフェースを作成しました。

74HCT04を使ったインターフェースは、マイコンから表示装置への一方通行になります。 表示装置からのシリアル出力を受け取るなどの反対方向のインターフェースでは、 74HC4049という入力端子からVCCへの保護ダイオードを省略したICを使用することができます。 今回は、表示装置にデータを送信するだけなので、反対方向のインターフェースは実装していません。

このアプリケーションでは、5Vの電源のみを取り入れて動作しており、 マイコンで必要な3.3Vの電源は、基板上のTA48033Sで作っています。

ソフトウェアの解説

リアルタイムアプリケーション

このアプリケーションは、ProcessorExpertを基本として作成されていますが、 それだけではゲームに必要なリアルタイムアプリケーションには出来ませんでした。 リアルタイムアプリケーションとするために、 MC9S08QG8に搭載されているSPIモジュールを利用し、ソフトウェアの負担を減らしました。 また、PWMを直接操作することにより、PWM周期を計算するためのオーバヘッドを削減しました。

それでも、なお、基本的な部分はProcessorExpertを利用して記述されているので、 理解のしやすいソフトウェアになっています。

ビーンの設定

COMMING SOON!!

プログラム・リスト

メイン・プログラム : LedMat.c

/** ###################################################################
**     Filename  : LedMat.C
**     Project   : LedMat
**     Processor : MC9S08QG8CPB
**     Compiler  : CodeWarrior HCS08 C Compiler
** ###################################################################*/
/* MODULE LedMat */


/* Including used modules for compiling procedure */
#include "Cpu.h"
#include "Events.h"
#include "SM1.h"
#include "Strobe.h"
#include "Latch.h"
#include "TI1.h"
#include "Pot.h"
#include "Shoot.h"
#include "Tpm.h"
/* Include shared modules, which are used for whole project */
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//================================================================
//  Multiple purpose flag register.
//    All boolean varibales are combined into a structure and
//    assigned to a SHORT addressing segment to reduce RAM
//    area and codes.
//
//    target_exhausted : 
//      indicates if all TARGET objects are gone.
//    action_requested : 
//      indicates an ACTION is expected by periodic timer event.
//    music_played : 
//      indicates a MUSIC is played.
//    hit : 
//      indicates a bullet reached to the ceil.
//================================================================
struct Flag {
  bool target_exhausted:1;
  bool action_requested:1;
  bool music_played:1;
  bool hit:1;
} flag;

#pragma DATA_SECTION DEFAULT
//================================================================
//  State machine management.
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for State-Machine.
//
//    action : 
//      STATE object for game sequence state-machine.
//----------------------------------------------------------------
void        (*action)(void);

#pragma DATA_SECTION DEFAULT
//================================================================
//  Random Number Generator
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for Random number generator.
//
//    n_rand : 
//      Random number register is implemented as a 16-bit
//      counter.
//----------------------------------------------------------------
word        n_rand;

#pragma DATA_SECTION DEFAULT
//================================================================
//  Sono object management.
//================================================================
//----------------------------------------------------------------
// Structure Sono describing a piece of sound.
//----------------------------------------------------------------
typedef struct {
  byte      count;    // Length of Sono
  word      period;   // Period parameter for PWM.
} Sono;

#pragma DATA_SECTION SHORT MY_ZEROPAGE
//--------------------------------------------------------
//  Global variables for SONO object manager.
//
//    Following variables are used by the SONO manager
//    and never used by user program immediately.
//    User programs should use METHODs, like sono_queue()
//    and sono_disable().
//
//    sono_count : 
//      Number of ticks left for current SONO.
//          0 - No SONO executed.
//        255 - Current SONO has infinite length.
//    sono_queue : 
//      Sono in the QUEUE.
//--------------------------------------------------------
byte          sono_count;
Sono          sono_queue;

#pragma DATA_SECTION DEFAULT

//----------------------------------------------------------------
//  sono_dispatch : 
//    Dispatch a Sono object management.  This function is
//    typically invoked by the periodical timer event handler.
//----------------------------------------------------------------
void sono_dispatch(void) {
  // Update PWM period if requested.
  if ((sono_count == 0) || (sono_count == 255)) {
    // If currently played Sono completed,
    // check queue status.
    if (sono_queue.count > 0) {
      // Accept queue if exists.
      TPMMOD = sono_queue.period - 1;   // Set period
      TPMC0V = sono_queue.period / 2;   // Set duty to 50%
      sono_count = sono_queue.count;    // Set SONO timer
      sono_queue.count = 0;             // Reset the queue
    } else {
      // Stop SONO
      TPMC0V = TPMMOD;                // Set duty to 0%
    }
  }
  // ignore infinite length
  if (sono_count == 255) return;
  // Stop SONO at last tick if a MUSIC played.
  if (flag.music_played && sono_count == 1) {
    // Stop SONO
    TPMC0V = TPMMOD;                  // Set duty to 0%
  }
  // Decrement SONO timer
  sono_count--;
}

//----------------------------------------------------------------
//  sono_queue_in : 
//    Queue in line a Sono object.
//
//    Parameter:
//      sono : 
//        Pointer to a SONO object to be queued in.
//    Returns:
//        TRUE if the SONO object is accepted to QUEUE.
//        FaLSE if the QUEUE buffer is full.
//----------------------------------------------------------------
bool sono_queue_in(const Sono *sono){
  bool      result;
  
  // No interrupt allowed to protect QUEUE buffer.
  Cpu_DisableInt();
  if (sono_queue.count == 0) {
    // Add into queue if queue is empty.
    sono_queue = *sono;
    result = TRUE;
  } else {
    // Queue is full.
    result = FALSE;
  }
  Cpu_EnableInt();
  return result;
}

//----------------------------------------------------------------
//  sono_disable : 
//    Disable SONO currently played and discard SONO in queue.
//----------------------------------------------------------------
void sono_disable(void) {
  // No interrupt allowed to protect QUEUE buffer.
  Cpu_DisableInt();
  TPMC0V = TPMMOD;          // Set duty to 0% to stop SONO
  sono_count = 0;           // Discard currently played SONO
  sono_queue.count = 0;     // Discard SONO in QUEUE.
  Cpu_EnableInt();
}
//================================================================
//  MUSIC object management.
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Variables for MUSIC object manager.
//    A MUSIC is described as a list of Sono.
//
//    music_score : 
//      Pointer to a MUSIC currently played.
//    music_index : 
//      Index counter to indicating a Sono.
//----------------------------------------------------------------
const Sono *    music_score;
byte            music_index;

#pragma DATA_SECTION DEFAULT

//----------------------------------------------------------------
//  music_init : 
//    Initialize the MUSIC dispatcher.
//
//    Parameter:
//      score : 
//        Pointer to a MUSIC score to be played.
//----------------------------------------------------------------
void music_init(const Sono *score) {
  // Initialize global variables.
  music_score = score;
  music_index = 0;
  // Set music_played flag to make a MUSIC oriented SONO.
  flag.music_played = TRUE;
}

//----------------------------------------------------------------
//  music_dispatch : 
//    Dispatch function to play a MUSIC score.
//    This function is onvoked by the ACTION states periodically.
//----------------------------------------------------------------
void music_dispatch(void) {
  if (music_score) {
    // Play MUSIC if a music_score specified.
    // Queue a SONO in score.
    if (sono_queue_in(&music_score[music_index])) {
      // Increment index if the SONO was accepted.
      music_index++;
      // Rewind the MUSIC index if a STOP code detected.
      if (music_score[music_index].count == 0) {
        music_index = 0;
      }
    }
  }
}
//================================================================
//  MUSIC Score
//    MUSIC scores are described as an array of SONO object.
//================================================================
//----------------------------------------------------------------
//  Period parameter for music score.
//----------------------------------------------------------------
const word  P_3Fis = 8000000 /  740;
const word  P_3Gis = 8000000 /  831;
const word  P_3A   = 8000000 /  880;
const word  P_3H   = 8000000 /  988;
const word  P_4Cis = 8000000 / 1109;
const word  P_4D   = 8000000 / 1175;
const word  P_4E   = 8000000 / 1319;
const word  P_4Fis = 8000000 / 1480;
const word  P_4Gis = 8000000 / 1661;
const word  P_4A   = 8000000 / 1760;


//----------------------------------------------------------------
//  Piano Sonate K331, Mozart
//----------------------------------------------------------------
const Sono sonate[] = {
   24, P_4Cis, // DO#
    8, P_4D,   // RE
   16, P_4Cis, // DO#
   32, P_4E,   // MI
   16, P_4E,   // MI
   24, P_3H,   // SI
    8, P_4Cis, // DO#
   16, P_3H,   // SI
   32, P_4D,   // RE
   16, P_4D,   // RE
   32, P_3A,   // RA
   16, P_3A,   // RA
   32, P_3H,   // SI
   16, P_3H,   // SI
   32, P_4Cis, // DO#
    8, P_4E,   // MI
    8, P_4D,   // RE
   32, P_4Cis, // DO#
   16, P_3H,   // SI

   24, P_4Cis, // DO#
    8, P_4D,   // RE
   16, P_4Cis, // DO#
   32, P_4E,   // MI
   16, P_4E,   // MI
   24, P_3H,   // SI
    8, P_4Cis, // DO#
   16, P_3H,   // SI
   32, P_4D,   // RE
   16, P_4D,   // RE
   32, P_3A,   // RA
   16, P_3H,   // SI
   32, P_4Cis, // DO#
   16, P_4D,   // RE
   32, P_4Cis, // DO#
   16, P_3H,   // SI
   32, P_3A,   // RA
   16,    0,   // -

   24, P_4E,   // MI
    8, P_4Fis, // FA#
   16, P_4E,   // MI
   32, P_4Fis, // FA#
   16, P_4Fis, // FA#
    4, P_4Fis, // FA#
    4, P_4Gis, // SO#
   16, P_4A,   // RA
    8, P_4Gis, // SO#
   16, P_4Fis, // FA#
   16, P_4Fis, // FA#
   16, P_4E,   // MI
   16, P_4E,   // MI
   16, P_4E,   // MI
   16, P_4Cis, // DO#
   16, P_3A,   // RA
   16, P_4E,   // MI
   16, P_4D,   // RE
   16, P_3H,   // SI
   16, P_4E,   // MI
   16, P_4Cis, // DO#
   16, P_3A,   // RA
   32, P_4Cis, // DO#
   16, P_3H,   // SI

   24, P_4Cis, // DO#
    8, P_4D,   // RE
   16, P_4Cis, // DO#
   32, P_4E,   // MI
   16, P_4E,   // MI
   24, P_3H,   // SI
    8, P_4Cis, // DO#
   16, P_3H,   // SI
   32, P_4D,   // RE
   16, P_4D,   // RE
   32, P_3A,   // RA
   16, P_3H,   // SI
   32, P_4Cis, // DO#
   16, P_4D,   // RE
   32, P_4Cis, // DO#
   16, P_3H,   // SI
   32, P_3H,   // SI
   16, P_4Cis, // DO#

   32, P_4Cis, // DO#
   16, P_4D,   // RE
   32, P_4E,   // MI
    8, P_4Fis, // FA#
    4, P_4Gis, // SO#
    4, P_4A,   // RA
   32, P_3A,   // RA
    8, P_4Cis, // DO#
    8, P_3H,   // SI
   32, P_3A,   // RA
   16, 0,      // -

    0,    0    // STOP
};


//----------------------------------------------------------------
//  Dash!!  noritan.org
//----------------------------------------------------------------
const Sono dash[] = {
    8, P_3Fis, // FA#
    8, P_3Fis, // FA#
    8, P_3A,   // RA
    8, P_3A,   // RA
    8, P_3Fis, // FA#
    8, P_3Fis, // FA#
    8, P_3A,   // RA
    8, P_3A,   // RA
    8, P_3Fis, // FA#
    8, P_3Fis, // FA#
    8, P_3A,   // RA
   16, P_4D,   // RE
    8, P_4Cis, // DO#
    8, P_3H  , // SI
    8, P_3A,   // RA
   
    8, P_3Fis, // FA#
    8, P_3Fis, // FA#
    8, P_3A,   // RA
    8, P_3A,   // RA
    8, P_3Fis, // FA#
    8, P_3Fis, // FA#
    8, P_3A,   // RA
    8, P_3A,   // RA
    8, P_3Fis, // FA#
    8, P_3Fis, // FA#
    8, P_3A,   // RA
   16, P_4D,   // RE
    8, P_4Cis, // DO#
    8, P_3H  , // SI
    8, P_4Cis, // DO#

    8, P_3H  , // SI
    8, P_3H  , // SI
    8, P_4D,   // RE
    8, P_4D,   // RE
    8, P_3H  , // SI
    8, P_3H  , // SI
    8, P_4D,   // RE
    8, P_4D,   // RE
    8, P_3H  , // SI
    8, P_3H  , // SI
    8, P_4D,   // RE
   16, P_4Fis, // FA#
    8, P_4E  , // MI
    8, P_4D,   // RE
    8, P_4Cis, // DO#

    8, P_4Cis, // DO#
    8, P_4Cis, // DO#
    8, P_3H  , // SI
    8, P_3H  , // SI
    8, P_4Cis, // DO#
    8, P_4Cis, // DO#
    8, P_3H  , // SI
    8, P_3H  , // SI
    8, P_4D,   // RE
    8, P_4Cis, // DO#
    8, P_3H  , // SI
   16, P_4D,   // RE
    8, P_4Cis, // DO#
    8, P_3H  , // SI
    8, P_3A,   // RA

    0,    0    // STOP
};
//================================================================
//  SPI communication manager.
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for SPI communication.
//
//    These variables are not directly controlled by user
//    programs because all SPI requests are invoked by
//    METHODs.
//
//    spi_size : 
//      Number of data bytes sent by SPI module.  This value
//      indicates how many bytes should be received by SPI
//      module too.
//    spi_callback : 
//      A callback function executed when an expected size
//      of data received by SPI module.
//----------------------------------------------------------------
word          spi_size;
void          (*spi_callback)(bool);

#pragma DATA_SECTION DEFAULT

//----------------------------------------------------------------
//  spi_start : 
//    Method to start a SPI transfer.
//
//    Parameter:
//      tx_data : 
//        Pointer to data to be sent by SPI module.
//      size : 
//        The number of bytes to be sent by SPI module.
//      callback : 
//        A callback function to be invoked when a transfer
//        completed or failed.
//----------------------------------------------------------------
void spi_start(
    byte *      tx_data,
    byte        size,
    void        (*callback)(bool)
) {
  byte          err_code;
  word          n_sent;
  
  // Flush TX/RX buffers.
  (void)SM1_ClearRxBuf();
  (void)SM1_ClearTxBuf();

  // Register the size of data and the callback function.
  spi_size = size;
  spi_callback = callback;
  
  // Start a SPI communication to send a data block
  err_code = SM1_SendBlock(tx_data, size, &n_sent);
  
  // Confirm error code
  if (err_code == ERR_OK) {
    // Block accepted successfully.
    if (n_sent != size) {
      // Size mismatch - abort transfer.
      (void)SM1_ClearTxBuf();
    }
  } else {
    // Error reported - abort transfer.
    (void)SM1_ClearTxBuf();
  }
}

//----------------------------------------------------------------
//  spi_OnRxChar : 
//
//    An interrupt handler invoked when a byte of data
//    received by SPI module.  A callback function specified
//    by user programs is executed with an argument FALSE
//    to indicate successful transfer when expected size
//    of data are received.
//----------------------------------------------------------------
void spi_OnRxChar(void) {
  if (SM1_GetCharsInRxBuf() >= spi_size) {
    // Received data is larger than expected.
    (*spi_callback)(FALSE);
  }
}

//----------------------------------------------------------------
//  spi_OnError : 
//
//    An interrupt handler invoked when error were detected
//    by SPI module.  A callback function specified by user
//    programs is executed with an argument TRUE to indicate
//    illegal transfer.
//----------------------------------------------------------------
void spi_OnError(void) {
  (*spi_callback)(TRUE);
}
//================================================================
//  LED Display controller
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for LED display.
//
//    fb : 
//      Frame buffer of LED display.  64 bytes of memory array
//      describes the state of 512 LEDs.
//    led_tx : 
//      A packet to be sent to matrix LED display by SPI module.
//----------------------------------------------------------------
byte            fb[64];
byte            led_tx[6];

#pragma DATA_SECTION DEFAULT

//----------------------------------------------------------------
//  Table array for LED display.
//    These tables are assigned as CONST and placed on ROM.
//
//    bit_pattern[] : 
//      Look-up table to calculate (1 << (7-n))
//    bit_pattern_r[] : 
//      Look-up table to calculate (1 << (n))
//----------------------------------------------------------------
const byte bit_pattern[] = {
  0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};
const byte bit_pattern_r[] = {
  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};

//----------------------------------------------------------------
//  led_display : 
//    Method to invoke a SPI transfer driving a ROW.
//
//    Parameter:
//      row : 
//        The ROW number to be driven.  No LEDs driven when
//        row is larger than or equal to 16.
//----------------------------------------------------------------
void led_display(byte row) {
  // Callback function for LED packet transfer.
  void          led_display_cb(bool);
  // Index base for the ROW in fb array.
  byte          index = row * 4;

  if (row < 16) {
    // Copy a row in fb array into led_tx buffer.
    led_tx[0] = fb[index + 3];
    led_tx[1] = fb[index + 2];
    led_tx[2] = fb[index + 1];
    led_tx[3] = fb[index + 0];
    // Select a row.
    if (row < 8) {
      // Upper half case.
      led_tx[4] = bit_pattern_r[row];
      led_tx[5] = 0;
    } else {
      // Lower half case.
      led_tx[4] = 0;
      led_tx[5] = bit_pattern_r[row - 8];
    }
  } else {
    // Blanking period - no ROW selected.
    led_tx[4] = 0;
    led_tx[5] = 0;
  }
  // Send a SPI transfer.
  spi_start(led_tx, 6, led_display_cb);
}

//----------------------------------------------------------------
//  led_display_cb : 
//    Callback function when a transfer for LED display
//    completed.
//
//    Parameter:
//      error : 
//        TRUE if the SPI transfer was failed and FALSE if the
//        SPI transfer is successfully completed.
//----------------------------------------------------------------
void led_display_cb(bool error) {
  if (!error) {    
    // Drop unnecessary received data.
    (void)SM1_ClearRxBuf();
    // Toggle LATCH signal to drive LEDs.
    Latch_SetVal();
    Latch_ClrVal();
    // Set STROBE signal to enable LED drivers.
    Strobe_SetVal();
  } else {
    // Drop characters in buffer.
    (void)SM1_ClearRxBuf();
  }
}

//----------------------------------------------------------------
//  pset : 
//    Low-level function to set a pixel on frame buffer.
//
//    Parameter:
//      x, y : 
//        Position of a pixel to be set.
//----------------------------------------------------------------
void pset(byte x, byte y) {
  fb[((y & 15) << 2) + ((x & 31) >> 3)] |= bit_pattern[x & 7];
}

//----------------------------------------------------------------
//  pclr : 
//    Low-level function to clear a pixel on frame buffer.
//
//    Parameter:
//      x, y : 
//        Position of a pixel to be cleared.
//----------------------------------------------------------------
void pclr(byte x, byte y) {
  fb[((y & 15) << 2) + ((x & 31) >> 3)] &= ~bit_pattern[x & 7];
}

//----------------------------------------------------------------
//  fb_clear : 
//    Clear specified ROWs.
//
//    Parameter:
//      row_start : 
//        The upper-most ROW to be cleared.
//      row_count : 
//        The number of ROWs to be cleared.
//----------------------------------------------------------------
void fb_clear(
  const byte    row_start,
  const byte    row_count
) {
  // Index base for the ROW in fb array.
  byte    index = row_start * 4;
  byte    i;
  
  for (i = 0; i < row_count; i++) {
    fb[index++] = 0;
    fb[index++] = 0;
    fb[index++] = 0;
    fb[index++] = 0;
  }
}

//----------------------------------------------------------------
//  fb_shiftLeft : 
//    Shift left specified row in the frame buffer.
//
//    Parameter:
//      row_start : 
//        The upper-most ROW to be shifted.
//      row_count : 
//        The number of ROWs to be shifted.
//----------------------------------------------------------------
void fb_shiftLeft(
  const byte    row_start,
  const byte    row_count
) {
  // Index base for the ROW in fb array.
  byte    index = row_start * 4;  
  byte    row;
  byte    data;

  for (row = 0; row < row_count; row++) {
    /*
    __asm {
      clrh
      ldx index
      asl @fb+0,X
      rol @fb+1,X
      rol @fb+2,X
      rol @fb+3,X
    }
    */
    data = fb[index + 0] << 1;
    if (fb[index + 1] & 128) {
      data++;
    }
    fb[index + 0] = data;
    data = fb[index + 1] << 1;
    if (fb[index + 2] & 128) {
      data++;
    }
    fb[index + 1] = data;
    data = fb[index + 2] << 1;
    if (fb[index + 3] & 128) {
      data++;
    }
    fb[index + 2] = data;
    fb[index + 3] <<= 1;
    index += 4;
  }
}
//================================================================
//  MESSAGE object management.
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for message manager.
//    These variables are used by both 11 and 7 points fonts.
//
//    message_table : 
//      Pointer to a MESSAGE currently displayed.
//    message_index : 
//      Index indicating a character in a message.
//    message_char : 
//      Index indicating a character in a font list.
//    message_column : 
//      Index indicating a pixel column in a font.
//----------------------------------------------------------------
const byte *    message_table;
byte            message_index;
byte            message_char;
byte            message_column;

#pragma DATA_SECTION DEFAULT


//----------------------------------------------------------------
//  message_init : 
//    Initialize the MESSAGE dispatcher to start a message
//    stream.
//
//    Parameter:
//      message : 
//        Pointer to a MESSAGE.
//----------------------------------------------------------------
void message_init(const byte *message) {
  message_table = message;
  message_index = 0;
  message_column = 0;
}

//----------------------------------------------------------------
//  fonts11[][] : 
//    10cols X 11rows font data table.
//    A character consists of 10 WORD data.  The first WORD
//    of a character describes the left side pixel.  The LSB
//    of the WORD describes the lower pixel.
//----------------------------------------------------------------
const word fonts11[][10] = {
0x3fe, 0x7ff, 0x7ff, 0x401, 0x401, 0x401, 0x401, 0x7ff, 0x7ff, 0x3fe, // 0
0x001, 0x401, 0x401, 0x7ff, 0x7ff, 0x7ff, 0x7ff, 0x001, 0x001, 0x001, // 1
0x39f, 0x79f, 0x79f, 0x411, 0x411, 0x411, 0x411, 0x7f1, 0x7f1, 0x3e1, // 2
0x39e, 0x79f, 0x79f, 0x421, 0x421, 0x421, 0x421, 0x7ff, 0x7ff, 0x3de, // 3
0x0fc, 0x1fc, 0x3fc, 0x604, 0x404, 0x7ff, 0x7ff, 0x7ff, 0x004, 0x004, // 4
0x7ce, 0x7cf, 0x7cf, 0x441, 0x441, 0x441, 0x441, 0x47f, 0x47f, 0x43e, // 5
0x3fe, 0x7ff, 0x7ff, 0x421, 0x421, 0x421, 0x421, 0x7bf, 0x7bf, 0x39e, // 6
0x700, 0x700, 0x700, 0x400, 0x41f, 0x43f, 0x47f, 0x7e0, 0x7c0, 0x780, // 7
0x3de, 0x7ff, 0x7ff, 0x421, 0x421, 0x421, 0x421, 0x7ff, 0x7ff, 0x3de, // 8
0x3ce, 0x7ef, 0x7ef, 0x421, 0x421, 0x421, 0x421, 0x7ff, 0x7ff, 0x3fe, // 9
0x0ff, 0x3ff, 0x3ff, 0x710, 0x410, 0x410, 0x710, 0x3ff, 0x3ff, 0x0ff, // A
0x401, 0x7ff, 0x7ff, 0x7ff, 0x401, 0x401, 0x401, 0x7ff, 0x3fe, 0x1fc, // D
0x401, 0x7ff, 0x7ff, 0x7ff, 0x421, 0x421, 0x471, 0x471, 0x603, 0x603, // E
0x7ff, 0x7ff, 0x7ff, 0x020, 0x020, 0x020, 0x020, 0x7ff, 0x7ff, 0x7ff, // H
0x000, 0x000, 0x401, 0x7ff, 0x7ff, 0x7ff, 0x7ff, 0x401, 0x000, 0x000, // I
0x7ff, 0x7ff, 0x3ff, 0x180, 0x0c0, 0x0c0, 0x180, 0x3ff, 0x7ff, 0x7ff, // M
0x7ff, 0x7ff, 0x7ff, 0x420, 0x430, 0x438, 0x43c, 0x7ef, 0x7c7, 0x383, // R
0x3ce, 0x7ef, 0x7ef, 0x421, 0x421, 0x421, 0x421, 0x7bf, 0x7bf, 0x39e, // S
0x600, 0x400, 0x400, 0x7ff, 0x7ff, 0x7ff, 0x7ff, 0x400, 0x400, 0x600, // T
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000  // blank
};

//----------------------------------------------------------------
//  message_11_dispatch : 
//    Dispatch function to draw a column of a character
//    representing by fonts11[] table.
//
//    Parameter:
//      row_start : 
//        The upper-most ROW number to draw a character.
//----------------------------------------------------------------
void message_11_dispatch(const byte row_start) {
  word        msg_data;
  word        mask;
  byte        row;

  // Select a character to be drawn.
  if (message_column == 0) {
    // Load a new character if left-most pixel selected.
    if (message_table[message_index] == 255) {
      // Rewind index value to ZERO.
      message_index = 0;
    }
    // Get a character from message.
    message_char = message_table[message_index++];
  }
  // Prepare a column data to be drawn in msg_data.
  if (message_column < 10) {
    msg_data = fonts11[message_char][message_column];
  } else {
    // Set blank pixel as gap between characters.
    msg_data = 0;
  }
  // Draw a pixel pattern.
  // Set a pixel if set in character font.
  mask = 1 << (11 - 1);  
  for (row = 0; row < 11; row++) {
    if (msg_data & mask) {
      pset(31, row + row_start);
    }
    mask >>= 1;
  }
  // Revert pixel column index to left-most.
  if (++message_column >= 12) {
    message_column = 0;
  }
}

//----------------------------------------------------------------
//  fonts7[][] : 
//    7cols X 7rows font data table.
//    A character consists of 7 BYTE data.  The first BYTE
//    of a character describes the left side pixel.  The LSB
//    of the BYTE describes the lower pixel.
//----------------------------------------------------------------
const byte fonts7[][7] = {
0x3e, 0x7f, 0x41, 0x41, 0x41, 0x7f, 0x3e, // 0
0x00, 0x00, 0x21, 0x7f, 0x7f, 0x01, 0x00, // 1
0x23, 0x67, 0x4d, 0x49, 0x49, 0x79, 0x31, // 2
0x22, 0x63, 0x49, 0x49, 0x49, 0x7f, 0x36, // 3
0x1e, 0x3e, 0x62, 0x42, 0x7f, 0x7f, 0x02, // 4
0x7a, 0x7b, 0x49, 0x49, 0x49, 0x4f, 0x06, // 5
0x3e, 0x7f, 0x49, 0x49, 0x49, 0x6f, 0x26, // 6
0x60, 0x60, 0x40, 0x47, 0x4f, 0x78, 0x70, // 7
0x36, 0x7f, 0x49, 0x49, 0x49, 0x7f, 0x36, // 8
0x32, 0x7b, 0x49, 0x49, 0x49, 0x7f, 0x3e, // 9
0x7f, 0x3f, 0x10, 0x08, 0x10, 0x3f, 0x7f, // M
0x00, 0x00, 0x41, 0x7f, 0x7f, 0x41, 0x00, // I
0x32, 0x7b, 0x49, 0x49, 0x49, 0x6f, 0x26, // S
0x7f, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x41, // E
0x7f, 0x7f, 0x41, 0x41, 0x63, 0x3e, 0x1c, // D
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // blank
};

//----------------------------------------------------------------
//  message_7_dispatch : 
//    Dispatch function to draw a column of a character
//    representing by fonts7[] table.
//
//    Parameter:
//      row_start : 
//        The upper-most ROW number to draw a character.
//----------------------------------------------------------------
void message_7_dispatch(const byte row_start) {
  byte        msg_data;
  byte        mask;
  byte        row;

  // Select a character to be drawn.
  if (message_column == 0) {
    // Load a new character if left-most pixel selected.
    if (message_table[message_index] == 255) {
      // Rewind index value to ZERO.
      message_index = 0;
    }
    // Get a character from message.
    message_char = message_table[message_index++];
  }
  // Prepare a column data to be drawn in msg_data.
  if (message_column < 7) {
    msg_data = fonts7[message_char][message_column];
  } else {
    // Set blank pixel as gap between characters.
    msg_data = 0;
  }
  // Draw a pixel pattern.
  // Set a pixel if set in character font.
  mask = 1 << (7 - 1);
  for (row = 0; row < 7; row++) {
    if (msg_data & mask) {
      pset(31, row + row_start);
    }
    mask >>= 1;
  }
  // Revert pixel column index to left-most.
  if (++message_column >= 8) {
    message_column = 0;
  }
}
//================================================================
//  ACTION control functions.
//================================================================
//----------------------------------------------------------------
//  request_action : 
//    An ACTION requested by a periodical timer.
//    This function is typically invoked by a timer event
//    handler to set the action_requested flag.  The main
//    function executes an ACTION when the flag is set.
//----------------------------------------------------------------
void request_action(void) {
  flag.action_requested = TRUE;
}
//================================================================
//  TARGET object management.
//================================================================
//----------------------------------------------------------------
//  Constant declarations.
//
//    MAX_TARGET : 
//      Maximum number of TARGETs on the screen.
//    TARGET_PER_STAGE : 
//      Number of TARGETs appears on a stage.
//    TARGET_INTERVAL : 
//      Interval time of TARGET constructions.
//    TARGET_TIMER_OVERFLOW : 
//      Timer count causing OVERFLOW.
//    TARGET_TIMER_EXPLODE : 
//      Timer count to finish explosion.
//----------------------------------------------------------------
#define         MAX_TARGET              (12)
const byte      TARGET_PER_STAGE        = 40;
const byte      TARGET_INTERVAL         = 13;
const byte      TARGET_TIMER_OVERFLOW   = 80;
const byte      TARGET_TIMER_EXPLODE    = 30;

//----------------------------------------------------------------
//  Type definition of the TARGET status enumeration.
//
//    TARGET_NONE : 
//      TARGET is not on the screen.
//    TARGET_ALIVE : 
//      TARGET is on the screend and moving.
//    TARGET_EXPLODE : 
//      TARGET is now exploding.
//----------------------------------------------------------------
typedef enum {
  TARGET_NONE     = 0,
  TARGET_ALIVE    = 1,
  TARGET_EXPLODE  = 2
} TargetStatus;

//----------------------------------------------------------------
//  Property structure describing a TARGET.
//
//    status : TargetStatus
//      Status of the TARGET. 
//    timer : 
//      TARGET specific timer counter.
//    speed : 
//      Speed of the TARGET move.
//    x, y : 
//      Position of the TARGET.
//----------------------------------------------------------------
typedef struct {
  TargetStatus  status;
  byte          timer;
  byte          speed;
  byte          x;
  byte          y;
} Target;
Target        targets[MAX_TARGET];

#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for TARGET management.
//
//    target_count : 
//      Number of TARGETs had been constructed.
//    target_missed : 
//      Number of TARGETs missed and gone.
//    target_on_stage : 
//      Number of TARGETs ib the screen.
//    target_construct_timer : 
//      Timer counter for TARGET construction.
//----------------------------------------------------------------
byte          target_count;
byte          target_missed;
byte          target_on_screen;
byte          target_construct_timer;

#pragma DATA_SECTION DEFAULT

//----------------------------------------------------------------
//  target_draw : 
//    Draw a TARGET on the frame buffer.
//
//    Parameter:
//      target : 
//        Pointer to a TARGET object.
//----------------------------------------------------------------
void target_draw(const Target *target) {  
  byte  x = target->x;
  byte  y = target->y;
  
  // Set 3 pixels
  pset(x + 0, y);
  pset(x + 1, y);
  pset(x + 2, y);
}

//----------------------------------------------------------------
//  target_wipe : 
//    Wipe a TARGET on the frame buffer.
//
//    Parameter:
//      target : 
//        Pointer to a TARGET object.
//----------------------------------------------------------------
void target_wipe(const Target *target) {  
  byte  x = target->x;
  byte  y = target->y;
  
  // Clear 3 pixels
  pclr(x + 0, y);
  pclr(x + 1, y);
  pclr(x + 2, y);
}

//----------------------------------------------------------------
//  target_destructAll : 
//    Destruct all TARGET objects.
//----------------------------------------------------------------
void target_destructAll(void) {
  byte id;

  for (id = 0; id < MAX_TARGET; id++) {
    targets[id].status = TARGET_NONE;
  }
  target_on_screen = 0;
}

//----------------------------------------------------------------
//  target_destruct : 
//    Destruct a specified TARGET objects.
//----------------------------------------------------------------
void target_destruct(Target *target) {
  if (target->status != TARGET_NONE) {
    target->status = TARGET_NONE;
    target_on_screen--;
  }
}

//----------------------------------------------------------------
//  target_construct : 
//    Construct a TARGET object if possible.
//
//    Returns:
//      TRUE if a TARGET is successfully constructed,
//      or FALSE if failed to construction.
//----------------------------------------------------------------
bool target_construct(void) {
  byte        id;
  Target      *target;
  
  for (id = 0; id < MAX_TARGET; id++) {
    // Find an empty slot.
    if (targets[id].status == TARGET_NONE) {
      // Initialize a TARGET object.
      target = &targets[id];
      target->status = TARGET_ALIVE;
      target->x = 29;
      target->y = (n_rand & 7) + 1;
      target->speed = ((n_rand >> 3) & 15) + 10;
      target->timer = 0;
      // Increment count of TARGET constructed.
      target_count++;
      target_on_screen++;
      // Return TRUE on success.
      return TRUE;
    }
  }
  // Return FALSE on fail.
  return FALSE;
}

//----------------------------------------------------------------
//  explode_sono : 
//    An object to make an explosion SONO.
//----------------------------------------------------------------
const Sono explode_sono = {
  10,   53000
};

//----------------------------------------------------------------
//  target_explode : 
//    Start an explosion sequence of a TARGET.
//----------------------------------------------------------------
void target_explode(Target *target) {
  // Initialize timer counter.
  target->timer = 0;
  // Update the state.
  target->status = TARGET_EXPLODE;
  // Start an explosion SONO.
  (void)sono_disable();
  (void)sono_queue_in(&explode_sono);
}

//----------------------------------------------------------------
//  target_action : 
//    Typical ACTION of a TARGET object.
//
//    Parameter:
//      target : 
//        Pointer to a TARGET object.
//----------------------------------------------------------------
void target_action(Target *target) {
  switch (target->status) {
    case TARGET_ALIVE:
      // TARGET is moving on the screen.
      target->timer += target->speed;
      if (target->timer > TARGET_TIMER_OVERFLOW) {
        // Individual timer cause OVERFLOW.
        target->timer -= TARGET_TIMER_OVERFLOW;
        // wipe TARGET to move
        target_wipe(target);
        if (target->x > 0) { // Check the position of TARGET.
          // Move left the TARGET if still on the screen.
          (target->x)--;
          // Draw a TARGET at new position.
          target_draw(target);
        } else {
          // The TARGET is gone.
          target_missed++;    // Increment MISSED counter.
          // Destruct the TARGET.
          target_destruct(target);
        }
      } else {
        // Redraw the TARGET to refresh.
        target_draw(target);
      }
      break;
    case TARGET_EXPLODE:
      // TARGET is exploding.
      if (target->timer & 1) {
        // Draw TARGET if the timer counter is ODD
        target_draw(target);
      } else {
        // Wipe TARGET if the timer counter is EVEN
        target_wipe(target);
      }
      // Check if the explosion time finished.
      if (++(target->timer) > TARGET_TIMER_EXPLODE) {
        // Wipe TARGET certainly
        target_wipe(target);
        // Destruct the TARGET object.
        target_destruct(target);
      }
    case TARGET_NONE:
    default:
      // Nothing to do for TARGET out of screen.
      break;
  }
}
//================================================================
//  CANON object management.
//================================================================
//----------------------------------------------------------------
//  Constant definition for CANON object.
//
//  y_canon : 
//    Y-position of the CANON object.
//  POT_HYSTERISIS : 
//    Hysterisis value for conversion from pot_value to
//    x_canon.  The x_canon value will be stable in spite
//    of the noise on ADC.
//----------------------------------------------------------------
const byte    y_canon           = 14;
const byte    POT_HYSTERISIS    = 4;

#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for CANON.
//
//    x_canon : 
//      X-Position of CANON object. (0 <= x_canon <= 29)
//----------------------------------------------------------------
byte        x_canon;

#pragma DATA_SECTION DEFAULT


//----------------------------------------------------------------
//  draw_canon : 
//    Draw a CANON object on the frame buffer.
//----------------------------------------------------------------
void draw_canon(void) {
  // Set 4 pixels.
  pset(x_canon + 0, y_canon + 1);
  pset(x_canon + 1, y_canon + 1);
  pset(x_canon + 1, y_canon + 0);
  pset(x_canon + 2, y_canon + 1);
}

//----------------------------------------------------------------
//  wipe_canon : 
//    Wipe a CANON object on the frame buffer.
//----------------------------------------------------------------
void wipe_canon(void) {
  // Clear 4 pixels.
  pclr(x_canon + 0, y_canon + 1);
  pclr(x_canon + 1, y_canon + 1);
  pclr(x_canon + 1, y_canon + 0);
  pclr(x_canon + 2, y_canon + 1);
}

//----------------------------------------------------------------
//  canon_action : 
//    Typical ACTION of CANON object.
//
//    Adjustment constant calculation : 
//      x_canon is adjusted to 14.5 when pot_value is 128
//      and pot_value is 4 times faster than x_canon.
//      In addition, the direction of x_canon is opposite to
//      the pot_value's one due to hardware mis-implement.
//      ouch!!!
//
//      -(x_canon - 14.5) * 4 = (pot_value - 128)
//      pot_value = 186 - (x_canon * 4)
//----------------------------------------------------------------
void canon_action(void) {
  byte      exp_pot;      // POT value expected from x_canon.
  byte      pot_value;    // Measured POT value.
  byte      err_code;     // Error code returned by ADC bean.

  // Measure and Get position
  err_code = Pot_Measure(TRUE);
  err_code = Pot_GetValue8(&pot_value);
  // Wipe CANON object  
  wipe_canon();
  // Calculate expected pot_value from x_canon
  exp_pot = 186 - x_canon * 4;
  // Adjust position
  // Move CANON if pot_value is too larger or smaller than
  // expected value.  Hysterisis constant
  if (pot_value >= exp_pot + POT_HYSTERISIS) {
    if (x_canon > 0) {
      x_canon--;
    }
  } else if (pot_value <= exp_pot - POT_HYSTERISIS) {
    if (x_canon < 29) {
      x_canon++;
    }
  }
  // Draw CAONON object.
  draw_canon();
}
//================================================================
//  BULLET object management.
//================================================================
#pragma DATA_SECTION SHORT MY_ZEROPAGE
//----------------------------------------------------------------
//  Global variables for BULLET object.
//
//    x_bullet, y_bullet : 
//      Position of BULLET object. y_bullet==0 means BULLET
//      is not on the screen.
//----------------------------------------------------------------
byte        x_bullet;
byte        y_bullet;

#pragma DATA_SECTION DEFAULT


//----------------------------------------------------------------
//  Constant table declaration.
//
//  bullet_sono[] : 
//    List of SONO object describing the Music Effects of
//    the BULLET object.
//----------------------------------------------------------------
const Sono bullet_sono[16] = {
  2,    4000000 /  523, // DO
  2,    4000000 /  523, // DO
  2,    4000000 /  554, // DO#
  2,    4000000 /  587, // RE
   
  2,    4000000 /  622, // RE#
  2,    4000000 /  660, // MI
  2,    4000000 /  699, // FA
  2,    4000000 /  740, // FA#
   
  2,    4000000 /  784, // SO
  2,    4000000 /  831, // SO#
  2,    4000000 /  880, // RA
  2,    4000000 /  932, // RA#
   
  2,    4000000 /  988, // SI
  2,    4000000 / 1047, // DO
  2,    4000000 / 1109, // DO#
  2,    4000000 / 1175  // RE
};

//----------------------------------------------------------------
//  draw_bullet : 
//    Draw a BULLET object on the frame buffer.
//----------------------------------------------------------------
void draw_bullet(void) {
  // Set 2 pixels
  pset(x_bullet + 0, y_bullet - 1);
  pset(x_bullet + 0, y_bullet - 0);
}

//----------------------------------------------------------------
//  wipe_bullet : 
//    Wipe a BULLET object on the frame buffer.
//----------------------------------------------------------------
void wipe_bullet(void) {
  // Clear 2 pixels
  pclr(x_bullet + 0, y_bullet - 1);
  pclr(x_bullet + 0, y_bullet - 0);
}

//----------------------------------------------------------------
//  bullet_trigger : 
//    Check if a SHOOT event occurs.
//
//    Returns:
//      TRUE if a SHOOT event occurs, and FALSE otherwise.
//----------------------------------------------------------------
byte bullet_trigger(void) {
  // Implement just check the Shoot button status.
  return !Shoot_GetVal();
}

//----------------------------------------------------------------
//  bullet_action : 
//    Typical ACTION of the BULLET object including
//    construction.  Only one BULLET can be put on the
//    screen.
//----------------------------------------------------------------
void bullet_action(void) {
  // Construct bullet if button pushed.
  if (y_bullet == 0) {
    if (bullet_trigger()) {
      x_bullet = x_canon + 1;
      y_bullet = y_canon;
    }              
  }
  // Return immediately if no BULLET exists
  if (y_bullet == 0) {
    return;
  }
  // Wipe to move the BULLET.
  wipe_bullet();
  // Go forward.
  y_bullet--;
  // Check if the BULLET reached to ceil
  if (y_bullet == 0) {
    // Set HIT flag.
    flag.hit = TRUE;
  } else {
    // Draw a BULLET object.
    draw_bullet();
    // Queue a Sono according to y_bullet.
    (void)sono_queue_in(&bullet_sono[y_bullet]);
  }
}

//----------------------------------------------------------------
//  bullet_collision : 
//    Do a collision process if the BULLET collide with
//    the specified TARGET.
//
//    Parameter:
//      target : 
//        Pointer to a TARGET to be checked.
//----------------------------------------------------------------
void bullet_collision(Target *target) {
  // Check if the BULLET collide with the TARGET
  if (
       (y_bullet > 0)
    && (target->status == TARGET_ALIVE)
    && (x_bullet >= (target->x) + 0)
    && (x_bullet <= (target->x) + 2)
    && (y_bullet == (target->y))
  ) {
    // Wipe the BULLET.
    wipe_bullet();
    // Destruct the BULLET.
    y_bullet = 0;
    // Initialize the TARGET for explosion.
    target_explode(target);
  }
}
//================================================================
//  Game algorithm state machine.
//================================================================
//----------------------------------------------------------------
//  shoot_to_start : 
//    An opening message displayed in PROLOGUE.
//    This message uses 11-row font.
//----------------------------------------------------------------
const byte shoot_to_start[] = {
  17,     // 'S'
  13,     // 'H'
  0,      // 'O'
  0,      // 'O'
  18,     // 'T'
  19,     // ' '
  18,     // 'T'
  0,      // 'O'
  19,     // ' '
  17,     // 'S'
  18,     // 'T'
  10,     // 'A'
  16,     // 'R'
  18,     // 'T'
  19,     // ' '
  19,     // ' '
  255     // STOP
};

//----------------------------------------------------------------
//  prologue_action : 
//    State description of the PROLOGUE state.
//----------------------------------------------------------------
void prologue_action(void) {
  // Prototype declaration.
  void        game_init(void);
  
  // Play MUSIC.
  music_dispatch();
  // Move CANON object.
  canon_action();
  // Execute a BULLET related ACTION.
  bullet_action();
  // Shift the screen to LEFT.
  fb_shiftLeft(1, 11);
  // Transition to GAME if a bullet reached to ceil and
  // a complete character of the message appeared.
  if (flag.hit && message_column == 0) {
    game_init();
  } else {
    // Dispatch the message at ROW#1.
    message_11_dispatch(1);  
  }
}

//----------------------------------------------------------------
//  prologue_init : 
//    Initialization part of the PROLOGUE state.
//----------------------------------------------------------------
void prologue_init(void) {
  // Clear the message area.
  fb_clear(0, 14);
  // Initialize MESSAGE dispatcher.
  message_init(shoot_to_start);
  // Initialize MUSIC dispatcher.
  music_init(dash);
  // Clear HIT flag to detect bullet event.
  flag.hit = FALSE;
  // Transition to the PROLOGUE state.
  action = prologue_action;
}

//----------------------------------------------------------------
//  game_action : 
//    State description of the GAME state.
//----------------------------------------------------------------
void game_action(void) {
  byte        id;
  void        epilogue_init(void);
  
  // Do all TARGET's action.  
  for (id = 0; id < MAX_TARGET; id++) {
    target_action(&targets[id]);
  }
  // Move CANON object.
  canon_action();
  // Execute a BULLET related ACTION.
  bullet_action();
  // collision detection.
  for (id = 0; id < MAX_TARGET; id++) {
    bullet_collision(&targets[id]);
  }
  // Construct a TARGET if requested.
  if (target_count < TARGET_PER_STAGE) {
    if (++target_construct_timer > TARGET_INTERVAL) {
      target_construct_timer = 0;
      // Construct a TARGET if possible.
      (void)target_construct();
    }
  }
  // Check if all TARGETs are constructed and disappeared.
  if (target_count >= TARGET_PER_STAGE) {
    if (target_on_screen == 0) {
      epilogue_init();
    }
  }
}

//----------------------------------------------------------------
//  game_init : 
//    Initialization part of the GAME state.
//----------------------------------------------------------------
void game_init(void) {
  // Clear the message area.
  fb_clear(0, 14);
  // Destruct all TARGET objects.
  target_destructAll();
  // Reset TARGET object counter.
  target_count = 0;
  target_missed = 0;
  // Set to Music Effect mode.
  flag.music_played = FALSE;
  // Transition to the GAME state.
  action = game_action;
}

//----------------------------------------------------------------
//  message_missed[] : 
//    A message displayed when a stage is over.
//    The NUMBER part is replaced by a program.
//----------------------------------------------------------------
byte message_missed[] = {
  0,      // '0'
  0,      // '0'
  0,      // '0'
  15,     // ' '
  10,     // 'M'
  11,     // 'I'
  12,     // 'S'
  12,     // 'S'
  13,     // 'E'
  14,     // 'D'
  15,     // ' '
  15,     // ' '
  255     // STOP
};

//----------------------------------------------------------------
//  epilogue_action : 
//    State description of the EPILOGUE state.
//----------------------------------------------------------------
void epilogue_action(void) {
  // Play MUSIC.
  music_dispatch();
  // Move CANON object.
  canon_action();
  // Execute a BULLET related ACTION.
  bullet_action();
  // Shift the screen to LEFT.
  fb_shiftLeft(3, 7);
  // Transition to PROLOGUE if a bullet reached to ceil and
  // a complete message appeared.
  if (flag.hit && message_index == 1) {
    prologue_init();
  } else {
    // Dispatch the message at ROW#3.
    message_7_dispatch(3);  
  }
}

//----------------------------------------------------------------
//  epilogue_init : 
//    Initialization part of the EPILOGUE state.
//----------------------------------------------------------------
void epilogue_init(void) {
  // Clear the message area.
  fb_clear(0, 14);
  // Create a EPILOGUE message.
  // Set the MISSED count to the message.
  message_missed[2] = target_missed % 10;
  if (target_missed < 10) {
    message_missed[1] = 15;
  } else {
    message_missed[1] = (target_missed / 10) % 10;
  }
  if (target_missed < 100) {
    message_missed[0] = 15;
  } else {
    message_missed[0] = (target_missed / 100);
  }
  // Initialize MESSAGE dispatcher.
  message_init(message_missed);
  // Initialize MUSIC dispatcher.
  music_init(sonate);
  // Clear HIT flag to detect bullet event.
  flag.hit = FALSE;
  // Transition to the EPILOGUE state.
  action = epilogue_action;
}

void main(void)
{
  /* Write your local variable definition here */

  /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
  PE_low_level_init();
  /*** End of Processor Expert internal initialization.                    ***/

  /* Write your code here */
  /* For example: for(;;) { } */
  prologue_init();
  for (;;) {
    // Main loop to invoke STATE function
    // if requested by the periodical timer event.
    if (flag.action_requested) {
      flag.action_requested = FALSE;
      (*action)();
    }
    // Random number genertor is implemented as a simple counter.
    n_rand++;
  }

  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;){}
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

/* END LedMat */
/*
** ###################################################################
**
**     This file was created by UNIS Processor Expert 2.99 [03.85]
**     for the Freescale HCS08 series of microcontrollers.
**
** ###################################################################
*/

イベント・ハンドラ : Events.c

/** ###################################################################
**     Filename  : Events.C
**     Project   : LedMat
**     Processor : MC9S08QG8CPB
**     Beantype  : Events
**     Version   : Driver 01.02
**     Compiler  : CodeWarrior HCS08 C Compiler
**     Date/Time : 2007/04/28, 18:01
**     Abstract  :
**         This is user's event module.
**         Put your event handler code here.
**     Settings  :
**     Contents  :
**         SM1_OnRxChar - void SM1_OnRxChar(void);
**         SM1_OnTxChar - void SM1_OnTxChar(void);
**         SM1_OnError  - void SM1_OnError(void);
**
**     (c) Copyright UNIS, spol. s r.o. 1997-2006
**     UNIS, spol. s r.o.
**     Jundrovska 33
**     624 00 Brno
**     Czech Republic
**     http      : www.processorexpert.com
**     mail      : info@processorexpert.com
** ###################################################################*/
/* MODULE Events */


#include "Cpu.h"
#include "Events.h"

/*
** ===================================================================
**     Event       :  TI1_OnInterrupt (module Events)
**
**     From bean   :  TI1 [TimerInt]
**     Description :
**         When a timer interrupt occurs this event is called (only
**         when the bean is enabled - <"Enable"> and the events are
**         enabled - <"EnableEvent">). This event is enabled only if
**         a interrupt service/event is enabled.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void TI1_OnInterrupt(void)
{
  /* Write your code here ... */
  extern void sono_dispatch(void);
  extern void led_display(byte row);
  extern void request_action(void);

  // Static variable for software timer.
  static byte   sono_timer = 0;
  static byte   spi_timer = 0;
  static byte   action_timer = 0;

  // SONO manager invoked every 23msec
  if (++sono_timer >= 26) {
    sono_timer = 0;
    sono_dispatch();
  }
  // SPI commnication scheduling.
  led_display(spi_timer);
  if (++spi_timer >= 16) {
    spi_timer = 0;
  }
  // Action requested every 50msec
  if (++action_timer >= 56) {
    action_timer = 0;
    request_action();
  }
}

/*
** ===================================================================
**     Event       :  SM1_OnRxChar (module Events)
**
**     From bean   :  SM1 [SynchroMaster]
**     Description :
**         This event is called after a correct character is
**         received.
**         The event is available only when the <Interrupt
**         service/event> property is enabled.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void SM1_OnRxChar(void)
{
  /* Write your code here ... */
  extern void spi_OnRxChar(void);
  
  // Invoke actual event handler in MAIN.
  spi_OnRxChar();
}

/*
** ===================================================================
**     Event       :  SM1_OnError (module Events)
**
**     From bean   :  SM1 [SynchroMaster]
**     Description :
**         This event is called when a channel error (not the error
**         returned by a given method) occurs. The errors can be
**         read using <GetError> method.
**         The event is available only when the <Interrupt
**         service/event> property is enabled.
**     Parameters  : None
**     Returns     : Nothing
** ===================================================================
*/
void SM1_OnError(void)
{
  /* Write your code here ... */
  extern void spi_OnError(void);
  
  // Invoke actual event handler in MAIN.
  spi_OnError();
}

/* END Events */

/*
** ###################################################################
**
**     This file was created by UNIS Processor Expert 2.99 [03.85]
**     for the Freescale HCS08 series of microcontrollers.
**
** ###################################################################
*/
2007-07-16 : 暫定的に発行

Updated: $Date: 2007/07/15 15:02:09 $

Copyright (C) 2007 noritan.org ■