firmware of NBCTRLV1 / AYC01

Dependencies:   SDFileSystemEx mbed

main.cpp

Committer:
bkc_mbed
Date:
2016-04-18
Revision:
1:29f0e76a9999
Parent:
0:722cc5360dc3

File content as of revision 1:29f0e76a9999:

//
// NLGPLAY : NLG file player for mbed
//
// example to write :
// ./lpc21isp -bin file.bin /dev/cu.usbserial-??? 115200 12000
//
// 20151008: updated.
// 20160418: imporved file/dir selector
// improved init sequence
//

#include <stdio.h>

#include "mbed.h"
#include "SDFileSystem.h"
#include "lcd.h"

#include "nlg_mini.h"

// SPIモード
// #define USE_SPI

#ifdef TARGET_LPC1114
// 高速I/Oモード(LPC1114専用)
#define USE_FASTIO
#endif

// ピンアサイン切り替え
#include "pindef.h"


#define PRG_NAME "NLGPLAY"
#define PRG_VER_BASE "V1.30"

#ifndef USE_SPI
#define PRG_VER PRG_VER_BASE
#else
#define PRG_VER PRG_VER_BASE "S"
#endif


//#pragma O3
//#pragma Otime



// (pinname, mode)
DigitalIn sw_play(DP_PLAY, PullUp);
DigitalIn sw_next(DP_NEXT, PullUp);
DigitalIn sw_prev(DP_PREV, PullUp);

#define MAX_PATH 128
#define MAX_FILENAME 64
#define MAX_DIR 64

// Global Variable
struct
{
  bool stop;
  bool prev;
  bool one_mode;

  int idx;
  int max_entries;

  char playlist[MAX_FILENAME];
  char file[MAX_FILENAME];
  char cwd[MAX_DIR];

  NLG_CTX nlg;
  int total_sec;
} g;

// キーコード
#define SW_PLAY 1
#define SW_NEXT 2
#define SW_PREV 4


// ボタンが離されるのを待つ
void wait_key_up(void)
{
    while(!sw_next || !sw_play || !sw_prev);
}

// ボタンの入力を待つ
void wait_key_down(void)
{
    while(sw_next && sw_play && sw_prev);
}

// キー入力
int get_key(void)
{
  int key = 0;
  
  if (!sw_next)
    key |= SW_NEXT;
  if (!sw_play)
    key |= SW_PLAY;
  if (!sw_prev)
    key |= SW_PREV;
  return key;
}

// キー開放待ち
void wait_relkey(void)
{
  wait_key_up();
  wait(0.1);
}

// キー待ち
int wait_anykey(void)
{
    wait_key_down();
    int key = get_key();

    wait(0.1);
    wait_relkey();

    return key;
}

// LED
DigitalOut led1(LED1, 0);
DigitalOut led2(LED2, 0);

// SPI: MOSI, MISO, SCLK, CS
SDFileSystem sd(DP_MOSI, DP_MISO, DP_SCLK, DP_SCS, "sd");

// LCD
LCD lcd;

#define _WAIT for(int wcnt=0; wcnt < 2; wcnt++)


////////////////
// 高速IO

#define DBS IO_01 // DATA
#define CTS IO_02 // CTRL
#define RCK IO_04 // LATCH
#define SCK IO_03 // SCLK


DigitalOut io01(DP_IO01, 0); // P1_4
DigitalOut io02(DP_IO02, 0); // P0_3
DigitalOut io03(DP_IO03, 0); // P1_8
DigitalOut io04(DP_IO04, 0); // P0_11

#if 0
// NBV3での試し機能
#define IO_A0 io05
#define IO_WR io06

DigitalOut io05(DP_IO05, 0); // P1_9
DigitalOut io06(DP_IO06, 0); // P1_2

#endif

#define IO_01 (1 << 4)  // DBS P1_4
#define IO_02 (1 << 3)  // CTS P0_3
#define IO_03 (1 << 8)  // SCK P1_8
#define IO_04 (1 << 11) // RCK P0_11

#ifdef USE_FASTIO

// ポート出力
#define DATA_HI LPC_GPIO1->DATA |= DBS
#define DATA_LO LPC_GPIO1->DATA &= ~DBS
#define CTRL_HI LPC_GPIO0->DATA |= CTS
#define CTRL_LO LPC_GPIO0->DATA &= ~CTS
#define SCK_HI LPC_GPIO1->DATA |= SCK
#define SCK_LO LPC_GPIO1->DATA &= ~SCK
#define RCK_HI LPC_GPIO0->DATA |= RCK
#define RCK_LO LPC_GPIO0->DATA &= ~RCK

// 出力設定
#define PORT_DIR  LPC_GPIO0->DIR |= (IO_02 | IO_04); \
 LPC_GPIO1->DIR |= (IO_01 | IO_03)

#else

// ポート出力
#define DATA_HI io01 = 1
#define DATA_LO io01 = 0
#define CTRL_HI io02 = 1
#define CTRL_LO io02 = 0
#define SCK_HI io03 = 1
#define SCK_LO io03 = 0
#define RCK_HI io04 = 1
#define RCK_LO io04 = 0

// 出力設定
#define PORT_DIR 

#endif

SPI *spi;

// I/O初期化
void ioInit()
{
  // SPIインスタンスの取得
  spi = sd.getSPI();
  // 出力
  PORT_DIR;
}


#ifndef USE_SPI
// 8x2ビットモード

#define SHIFTOUT io8x2Out

// 8bit 出力
void byteOut(unsigned char data)
{
    int i;

    for(i = 0; i < 8; i++)
    {
        if (data & 0x80)
            DATA_HI;
        else
            DATA_LO;

        data <<= 1;
        
        SCK_HI;
        SCK_LO;
    }
}

// 8x2ビット出力 or 16bit出力選択可能
void io8x2Out(unsigned int ctrl, unsigned int data)
{
    int i;
    // シフトレジスタ直列モード
    if (g.one_mode)
    {
        // データ順
        // <- CCCCCCCC76543210
        
        byteOut(ctrl);
        byteOut(data);
                
        // ラッチ
        RCK_HI;
        RCK_LO;
    }
    else 
    {
    // シフトレジスタ並列モード
        for(i = 0; i < 8; i++)
        {
            /* 2ビット分のデータをそれぞれ出力 */
            if (ctrl & 0x80)
                CTRL_HI;
            else
                CTRL_LO;
    
            if (data & 0x80)
                DATA_HI;
            else
                DATA_LO;
    
            ctrl <<= 1;
            data <<= 1;
            // シフトクロック
            SCK_HI;
            SCK_LO;
        }
    
        // ラッチ
        RCK_HI;
        RCK_LO;
    }
}

#else

// 16bitモード
#define SHIFTOUT io16Out



// 16bit 出力
void io16Out(unsigned char ctrl, unsigned char data)
{
    spi->write(ctrl);
    spi->write(data);

    // byteOut(ctrl);
    // byteOut(data);

    // LATCH
    RCK_HI;
    RCK_LO;
}

#endif

/* 制御信号定義 */
#define CS_FM3 (1 << 0)
#define CS_PSG (1 << 1)
#define CS_FM1 (1 << 2)
#define CS_FM2 (1 << 3)
#define A0     (1 << 4)
#define WR     (1 << 5)
#define ICL    (1 << 6)
#define CTL    (1 << 7)


/* アクティブローの制御信号 */
#define ACTLOW (CS_PSG | CS_FM1 | CS_FM2 | CS_FM3 | WR | ICL)


/* NBV2互換出力 */
void regOutBase(int addr, int data, int select)
{
    /* アドレスを出力 */
    /* A0をローにして待つ */
    int ctrl = ACTLOW;
    
    /* 裏レジスタ */
    if (addr >= 0x100)
        ctrl |= CTL;
    addr &= 0xff;
  
    SHIFTOUT(ctrl, 0x00);
    SHIFTOUT(ctrl & ~(select | WR), addr);
    SHIFTOUT(ctrl, addr);

    /* チップ処理待ち */

    /* データを出力 */
    /* A0をハイにして待つ */
    ctrl |= A0;
    SHIFTOUT(ctrl, 0x00);
    SHIFTOUT(ctrl & ~(select | WR), data);
    SHIFTOUT(ctrl, data);

    /* チップ処理待ち */
}

/* 出力 */
#define regPSGOut(addr, data) regOutBase(addr, data, CS_PSG)
#define regFMOut(addr, data) regOutBase(addr, data, CS_FM1)
#define regFM2Out(addr, data) regOutBase(addr, data, CS_FM2)

/* 音源出力 */
#define WritePSG regPSGOut
#define WriteOPM regFMOut
#define WriteOPM2 regFM2Out


/* ミュート */
void boardMute(void)
{
    int i;

    /* PSG初期化 */
    regPSGOut(0x00,0);
    regPSGOut(0x01,0);

    regPSGOut(0x06, 0x00);
    regPSGOut(0x07, 0x3f); // ALL OFF
    regPSGOut(0x08, 0x00); // CH.A 0
    regPSGOut(0x09, 0x00); // CH.B 0
    regPSGOut(0x0a, 0x00); // CH.C 0

    /* MUTE(disable) */
    for(i = 0x20; i < 0x28; i++)
    {
        regFMOut(i, 0x00);
        regFM2Out(i, 0x00);
    }

    // KEYOFF
    for(i = 0x00; i < 0x08; i++)
    {
        regFMOut(0x08, i & 0x07);
        regFM2Out(0x08, i & 0x07);
    }

    // FORCE RELEASE
    for(i= 0x00; i < 0x20; i++)
    {
        regFMOut(0xE0 + i, 0x0f);
        regFM2Out(0xE0 + i, 0x0f);
    }

    // OPLL ミュート
    for(i= 0x00; i <= 0x08; i++)
    {
        regFMOut(0x20 + i, 0x00);
        regFMOut(0x30 + i, 0x0f);
        regFM2Out(0x20 + i, 0x00);
        regFM2Out(0x30 + i, 0x0f);
    }

}

/* ボード初期化 */
void boardInit(void)
{
    wait_ms(20);
    /* ICLのみをLOW(アクティブ)にする */
    SHIFTOUT(ACTLOW & ~(ICL), 0);
    wait_ms(150);

    /* 元に戻す */
    SHIFTOUT(ACTLOW, 0);
    wait_ms(10);
}

//////////////////////////////////////////

// 表示
void dispTime()
{
    char buf[16];
    sprintf(buf, "%02d:%02d",
        g.total_sec / 60,
        g.total_sec % 60);

    lcd.setCursor(3,1);
    lcd.printStr(buf);
}

#define WAIT_US 10000 // 10ms
#define SEC_US 1000000 // 1sec
#define NEXT_MAX_US 300000 // 300ms

/* NLGの再生 */
int PlayNLG_Loop(void)
{
    int cmd;
    int addr, data;

    int result = 0;

    int us = 0;
    int diff_us = 0;

    int count_us = 0;
    int total_us = 0;

    // NEXTボタンのカウンタ
    int next_count = 0;

    bool wait_skip = false;


    // GetTickUsNLG
    int tick_us = GetTickUsNLG(&g.nlg);

    // printf("tick_us:%d\n", tick_us);

    g.total_sec = 0;

    // タイマースタート
    Timer t;
    t.start();

    // 表示
    dispTime();

    // LED消灯
    led1 = 0;
    led2 = 0;

    // LEDカウンタ
    int led_cnt1 = 0;
    int led_cnt2 = 0;

    // ループ
    while(!g.stop)
    {
        /* ウエイトの処理 */
        while (count_us >= WAIT_US)
        {
            // ボタンチェック
            if (!sw_next)
            {
                // NEXTが押されている
                if (next_count < NEXT_MAX_US)
                    next_count += count_us;
                else
                {
                    // 早送り
                    next_count = NEXT_MAX_US;
                    wait_skip = true;
                }
            }
            else
            {
                // NEXTが離されている
                wait_skip = false;

                // 一瞬だけボタンが押された場合、次の曲へ
                if (next_count > 0 && next_count < NEXT_MAX_US)
                {
                    next_count = 0;
                    goto song_end;
                }

                // 早送りの終了
                if (next_count == NEXT_MAX_US)
                {
                    next_count = 0;
                }
            }
            // PREVが押された
            if (!sw_prev)
            {
                g.prev = true;
                goto song_end;
            }

            // PLAYが押された
            if (!sw_play)
            {
                g.stop = true;
                goto song_end;
            }

            // スキップでなければ次のタイミングまで待つ
            while(!wait_skip && (us = t.read_us()) + diff_us < count_us);

            // タイマーリセット
            if (!wait_skip)
                t.reset();

            // 差分を計算
            diff_us += us;
            while(diff_us >= count_us)
              diff_us -= count_us;

            // 積算する
            total_us += count_us;
            count_us = 0;

            // 1秒で積算カウントをリセットする
            if (total_us >= SEC_US)
            {
                while(total_us >= SEC_US)
                {
                    g.total_sec++;
                    total_us -= SEC_US;
                }
                // 表示
                dispTime();
            }
        }

        /* コマンドの読み出し */
        cmd = ReadNLG(&g.nlg);
        if (cmd == EOF)
        {
            result = EOF;
            break;
        }
        
        if (cmd < 0x80)
        {
            addr = ReadNLG(&g.nlg);
            data = ReadNLG(&g.nlg);
            
            // 裏レジスタ
            if (cmd >= 0x40)
                addr |= 0x100;
            
            switch(cmd & 0x3f)
            {
            case CMD_PSG:
                WritePSG(addr, data);
                break;
            case CMD_FM1:
                WriteOPM(addr, data);
                break;
            case CMD_FM2:
                WriteOPM2(addr, data);
                break;
            }
        }
        else
        {
            /* コマンドの処理 */
            switch (cmd)
            {
                case CMD_IRQ:
                    count_us += tick_us;
        
                    // LED1
                    led_cnt1++;
                    if (led_cnt1 >= 48)
                    {
                        led_cnt1 = 0;
                        led1 = !led1;
                    }

                    // LED2
                    led_cnt2++;
                    if (led_cnt2 >= 192)
                    {
                        led_cnt2 = 0;
                        led2 = !led2;
                    }
                break;
                case CMD_CTC0:
                    data = ReadNLG(&g.nlg);
                    SetCTC0_NLG(&g.nlg, data);
                    tick_us = GetTickUsNLG(&g.nlg);
                    // printf("CTC0:%d tick_us:%d\n", data, tick_us);
        
                break;
                case CMD_CTC3:
                    data = ReadNLG(&g.nlg);
                    SetCTC3_NLG(&g.nlg, data);
                    tick_us = GetTickUsNLG(&g.nlg);
                    // printf("CTC3:%d tick_us:%d\n", data, tick_us);
                break;
            }
        }
    }

song_end:

    return result;
}


// ファイルを再生する
int play_file(const char *filename)
{
    /* NLGファイルを開く */
    if (OpenNLG(&g.nlg, filename) < 0)
    {
        return -1;
    }

    /* 再生する */
    PlayNLG_Loop();

    /* NLGファイルを閉じる */
    CloseNLG(&g.nlg);

    return 0;
}

// error_sdcard()
// エラー!
void error_sdcard(void)
{
    lcd.printStr2("SD CARD", "ERROR!");

    while(1);
}

// COM mode
void loop_for_com(void)
{
    int sw, val;
    int adr = 0x00;
    int baud = 9600;
    char buf[16];
    Serial pc(DP_TX, DP_RX);

    lcd.cls();
    lcd.printStrY(0, "COM MODE");

    sprintf(buf, "%-8d", baud);
    lcd.printStrY(1, buf);

    pc.printf("COM\n");

    // タイマースタート
    Timer t;
    t.start();

    while(1)
    {

        sw = pc.getc();

        // 0は同期リセット
        if (sw == 0x00)
            continue;

        if (sw >= 1 && sw <= 3)
        {
          adr = pc.getc();
          val = pc.getc();
        }

        switch(sw)
        {
            case 1:
                regFMOut(adr, val);
            break;
            case 2:
                regFM2Out(adr, val);
            break;
            case 3:
                regPSGOut(adr, val);
            break;
            case 0x0f:
                // 通信速度設定
                val = pc.getc();
                baud = (9600 * val);
                sprintf(buf, "%-8d", baud);
                lcd.printStrY(1, buf);

                pc.baud(baud);
            break;
            case 0x10:
                //
                val = pc.getc();
                val = (val * 10000);
                while(t.read_us() < val);
                pc.putc('.');
                t.reset();
            break;
            case 0x11:
                t.reset();
            break;
        }

        // pc.printf("sw = %02x, adr = %02x, val = %02x\n", sw, adr, val);
    }
}


// chk_isdir
// ディレクトリか否か
// return : bool
// false if not directory

bool chk_isdir(const char *cwd, const char *dir)
{
    char tmp[256];
    sprintf(tmp,"%s/%s", cwd, dir);

    DIR *dp = opendir(tmp);
    if (!dp)
      return false;

    closedir(dp);
    return true;
}

#define EXT_LIST ".lst"
#define EXT_NLG ".nlg"

// get_fileentry
// index : index of list , -1 = return number of entries
// is_mode : 0 = normal mode
// mode = MODE_GETDIR | MODE_GETLIST | MODE_FILE
// return : int
// -1 = not found or error

#define MODE_FILE 0
#define MODE_GETDIR 1
#define MODE_GETLIST 2

int get_fileentry(int index, int mode)
{
    // return -1 if entry is zero.
    int count = 0;
    DIR *dp = opendir(g.cwd);

    g.file[0] = 0;

    if (!dp)
        return -1;

    struct dirent *dent = NULL;

    while(1)
    {
        // エントリの読み出し
        dent = readdir(dp);

        // 失敗
        if (!dent)
            break;

        // リソースか隠しファイル
        if (dent->d_name[0] == '.')
            continue;

        // 拡張子の取得
        char *ext = strrchr(dent->d_name, '.');

        switch(mode)
        {
          // ディレクトリモード
          case MODE_GETDIR:
            // ディレクトリでなければ継続
            if (!chk_isdir(g.cwd, dent->d_name))
                continue;
          break;

          // プレイリストモード
          case MODE_GETLIST:
            // リストでなければ無視
            if (!ext || strcasecmp(ext, EXT_LIST) != 0)
              continue;
          break;

          // ファイルモード
          case MODE_FILE:
            // NLGファイルでなければ継続
            if (!ext || strcasecmp(ext, EXT_NLG) != 0)
                continue;
          break;
        }

        // カウントアップ
        count++;

        // カウントモードかカウントがindex未満で継続
        if (index < 0 || count <= index)
            continue;

        // ファイル名をコピーして終了
        strcpy(g.file, dent->d_name);
        break;

    }
    closedir(dp);
    return count;
}


//
// タイトル表示
//
int putTitle()
{
    int count_ms = 0;
    int diff_us = 0;
    int us = 0;
    int key = get_key();

    // キー入力時はキャンセル
    if (key)
      return key;

    lcd.printStr2(PRG_NAME, PRG_VER);
    Timer t;
    t.start();

    // 1500msまでループする
    while(count_ms < 1500)
    {
      while((us = t.read_us()) + diff_us < 1000);
      t.reset();

      // キー入力があれば終了
      key = get_key();
      if (key)
        return key;

      // 差分を現在の時間に足して次の差分を作る
      diff_us += us;

      while(diff_us >= 1000)
      {
        count_ms++;
        diff_us -= 1000;
      }
    }

    return 0;
}

// 情報の表示
void show_info(int files)
{
    char buf[16];

    Timer t;
    int result_us = 0;
    t.reset();
    t.start();

    // 実際の書き込み時間を計測する
    regFMOut(0x20, 0x00);
    regFMOut(0x20, 0x00);
    regFMOut(0x20, 0x00);
    regFMOut(0x20, 0x00);
    regFMOut(0x20, 0x00);

    // 経過時間を得る
    result_us = t.read_us();
    t.stop();

    // 平均値
    result_us /= 5;

    // 結果表示
    printf("result_us=%dus\n", result_us);
    sprintf(buf, "R:%dus", result_us);
    lcd.printStrY(1, buf);
    wait(3);

    // コンパイル時の日付
    sprintf(buf, "%8s", __DATE__);
    lcd.printStrYscr(1, buf);
    wait(3);

    if (files < 0)
        lcd.printStrY(1, "NO FILES");
    else
    {
        sprintf(buf, "%3dfiles", files);
        lcd.printStrY(1, buf);
    }
    wait(3);
    lcd.cls();
}

// ビットテスト

void bit_test()
{
    char buf[9];
    lcd.printStrY(0, "BIT_TEST");

    wait_key_up();

    int pin = 0;
    int mode = 0;
    char sig;

    while(1)
    {
        // 表示
        if (mode & 1)
          sig = 'C'; // コントロール
        else
          sig = 'D'; // データ

        if (mode & 2)
          strcpy(buf,"00");
        else
          sprintf(buf, "%c%d", sig, pin);

        lcd.printStrY(1, buf);

        // 出力
        if (mode & 2)
        {
            SHIFTOUT(0xff, 0x00);
        }
        else
        {
            int bit = (1 << pin);
            if (mode & 1)
              SHIFTOUT(0xff & ~(bit), 0); // コントロール
            else
              SHIFTOUT(0xff, bit); // データ
        }

        // キー待ち
        int key = wait_anykey();

        // 再生
        if (key & SW_PLAY)
        {
          mode ^= 2;
          continue;
        }
        // 次
        if (key & SW_NEXT)
        {
          pin++;
          if (pin > 7)
          {
              pin = 0;
              mode = (mode + 1) & 1;
          }
        }
        // 前
        if (key & SW_PREV)
        {
          pin--;
          if (pin < 0)
          {
              pin = 7;
              mode = (mode - 1) & 1;
          }
        }
    }
}

//
// ファイル選択表示
void disp_filesel(int mode)
{
  char buf[16];
  char buf_mode[16];
  
  char *mode_name;
  switch(mode)
  {
    case MODE_GETLIST:
      mode_name = "LIST";
    break;
    case MODE_GETDIR:
      mode_name = "DIR ";
    break;
    default:
      mode_name = "FILE";
  }

  sprintf(buf_mode, "%s %03d", mode_name, g.idx + 1);
  strncpy(buf, g.file, 8);
  buf[8] = 0;

  // 表示
  lcd.printStr2(buf, buf_mode);
}

//
// ファイル/ディレクトリ選択
// mode = MODE_GETLIST | MODE_GETDIR | MODE_FILE
//
int file_select(int mode)
{
    // ファイル数を得る
    int files = get_fileentry(-1, mode);

    // 最大エントリー
    g.max_entries = files - 1;

    // ファイル名の取得
    get_fileentry(g.idx, mode);

    // 表示
    disp_filesel(mode);

    // リリース待ち
    wait_relkey();

    while(1)
    {
        // ファイル名の取得
        get_fileentry(g.idx, mode);

        // 表示
        disp_filesel(mode);

        // キー待ち
        int key = wait_anykey();

        // 次のエントリ
        if (key & SW_NEXT)
        {
            if (g.idx < g.max_entries)
                g.idx++;
            else
                g.idx = 0;
        }

        // 前のエントリ
        if (key & SW_PREV)
        {
            if (g.idx > 0)
                g.idx--;
            else
                g.idx = g.max_entries;
        }
        // 再生ボタンを押した
        if (key & SW_PLAY)
            break;
    }

    return g.idx;
}

// get_playlist()
// index = リスト位置, -1でエントリ数を返す
int get_playlist(int index)
{
    int count = 0;
    FILE *fp = fopen(g.playlist, "r");

    g.file[0] = 0;

    // プレイリストが開けない
    if (!fp)
        return -1;

    while(1)
    {
        // プレイリストから一行読み込む
        char *p = fgets(g.file, MAX_FILENAME, fp);

        // EOFなので終了
        if (!p)
            break;

        // サイズ
        int len = strlen(g.file);

        // CR/LFをトリムする
        while(len > 0 && (unsigned char)g.file[len - 1] < 0x20)
        {
            g.file[len - 1] = 0x00;
            len--;
        }

        // 空行は飛ばす
        if (!len)
            continue;

        count++;

        // カウントモードかカウントがindex未満で継続
        if (index < 0 || count <= index)
            continue;
        else
            break;
    }

    // プレイリストを閉じる
    fclose(fp);

#if defined(__MICROLIB) && defined(__ARMCC_VERSION)
    free(fp);
#endif

    return count;
}


// グローバル変数初期化
void init_globe(void)
{
  memset(&g, 0, sizeof(g));
  strcpy(g.cwd,"/sd");
}

// パスの作成
void make_path(char *path, const char *dir, const char *file)
{
  sprintf(path, "%s/%s", dir, file);
}

// 再生モード
void play_mode(void)
{
  int files = -1;

  // プレイリストモードか否か
  if (g.playlist[0])
      files = get_playlist(-1);
  else
      files = get_fileentry(-1, MODE_FILE);

  // エラー表示
  if (files < 0)
      error_sdcard();

  g.max_entries = files - 1;

  bool repeat_flag = false;

  // ファイルが無い
  if (files < 1)
  {
      lcd.cls();
      lcd.printStrY(0, "NO FILES");
      wait(1);
  }

  // 再生モードループ
  while(1)
  {
      char path[MAX_PATH];
      char buf[16];

      // プレイリストかどうか?
      if (g.playlist[0])
          get_playlist(g.idx);
      else
          get_fileentry(g.idx, MODE_FILE);

      // フルパスを作成
      make_path(path, g.cwd, g.file);

      // 曲番号
      sprintf(buf, "%2d ", g.idx + 1);

      // ファイル名名表示
      lcd.cls();
      lcd.printStrY(0, g.file);
      lcd.printStrY(1, buf);

      // 再生開始
      play_file(path);

      // ミュート
      boardMute();

      // キー開放待ち
      wait_relkey();


      // 再生ボタンが押された
      if (g.stop)
      {
          // ストップ表示
          lcd.printStrY(1, "  STOP  ");

          g.stop = false;
          g.prev = false;
          g.total_sec = 0;

          // ボード初期化
          boardInit();

          // ファイル選択
          file_select(MODE_FILE);
          continue;
      }

      // 前の曲を再生
      if (g.prev)
      {
          g.prev = false;

          // 同じ曲を再生
          if (g.total_sec >= 2)
            continue;

          if (g.idx > 0)
              g.idx--;
          else
              g.idx = g.max_entries;

          continue;
      }

      // 繰り返しではない
      if (!repeat_flag)
      {
          if (g.idx < g.max_entries)
              g.idx++;
          else
              g.idx = 0;
      }
      repeat_flag = false;
  }
}



//
// メニュー選択
//
#define MENU_FILE 0
#define MENU_DIR 1
#define MENU_LIST 2
#define MENU_INFO 3
#define MENU_COM 4
#define MENU_TEST 5
#define MENU_SMODE 6
#define MENU_MAX 6

// メニュー文字列
const char *menu_modes[] =
{
  "FILE", // 0
  "DIR",  // 1
  "LIST", // 2
  "INFO", // 3
  "COM",  // 4
  "TEST", // 5
  "Sx16", // 6
};

// メニュー選択表示
void menu_disp(int sel)
{
  char buf[16];

  // 表示
  lcd.printStrY(0, "MENU SEL");
  sprintf(buf, "%02d %-4s", sel, menu_modes[sel]);
  lcd.printStrY(1, buf);
}

// モード選択
int menu_select()
{
    int count = 0;

    // 初期表示
    menu_disp(0);

    // リリース待ち
    wait_key_up();

    while(1)
    {
        // キー待ち
        int key = wait_anykey();

        // 次
        if (key & SW_NEXT)
        {
            if (count < MENU_MAX)
                count++;
            else
                count = 0;
        }
        // 前
        if (key & SW_PREV)
        {
            if (count > 0)
                count--;
            else
                count = MENU_MAX;
        }
        // 再生
        if (key & SW_PLAY)
            break;

        // 表示
        menu_disp(count);
    }

    return count;
}

// メニューモード
void menu_mode(void)
{
  int files = 0;
  char path[MAX_PATH];

  menu_start:

  // ボードの初期化
  boardInit();

  int sw = menu_select();
  switch(sw)
  {
    // ファイル選択
    case MENU_FILE:
      file_select(MODE_FILE);
    break;
    // ディレクトリ選択
    case MENU_DIR:
        file_select(MODE_GETDIR);
        // パスを結合し、インデックスを初期化
        make_path(path, g.cwd, g.file);
        strcpy(g.cwd, path);
        g.idx = 0;
    break;
    // プレイリスト選択
    case MENU_LIST:
        file_select(MODE_GETLIST);
    break;
    // 情報モード
    case MENU_INFO:
          files = get_fileentry(-1, MODE_FILE);
          show_info(files);
          goto menu_start;
    // 通信モード
    case MENU_COM:
      loop_for_com();
    break;
    // テストモード
    case MENU_TEST:
      bit_test();
    break;
    // ストレートモード
    case MENU_SMODE:
        g.one_mode = true;
        lcd.printStr2("","Sx16 ON");
        wait(0.5);
        goto menu_start;
    }
}

//
// main
//
int main()
{
    // 初期化
    init_globe();

    // I/O初期化
    ioInit();

    // シフトレジスタの初期化
    SHIFTOUT(ACTLOW & ~(ICL), 0x00);

    // 立ち上がり待ち
    wait_ms(20);

    // LCD初期化
    lcd.init();

    // ICLなどをHにする
    SHIFTOUT(ACTLOW, 0x00);
    
    // 消音する
    boardMute();

    // タイトル表示
    int key = putTitle();
    

    // NEXTボタンでメニュー表示
    if (key & SW_NEXT)
      menu_mode();

    // 再生モード
    play_mode();
}