firmware of NBCTRLV1 / AYC01

Dependencies:   SDFileSystemEx mbed

Revision:
0:722cc5360dc3
Child:
1:29f0e76a9999
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Feb 08 05:49:26 2016 +0000
@@ -0,0 +1,1419 @@
+//
+// NLGPLAY : NLG file player for mbed
+//
+// example to write :
+// ./lpc21isp -bin file.bin /dev/cu.usbserial-??? 115200 12000
+//
+// 20151008: updated.
+//
+
+#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.22"
+
+#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 *mode_name;
+  switch(mode)
+  {
+    case MODE_GETLIST:
+      mode_name = "LIST SEL";
+    break;
+    case MODE_GETDIR:
+      mode_name = "DIR  SEL";
+    break;
+    default:
+      mode_name = "FILE SEL";
+  }
+
+  strncpy(buf, g.file, 8);
+  buf[8] = 0;
+
+  // 表示
+  lcd.printStr2(buf, mode_name);
+}
+
+//
+// ファイル/ディレクトリ選択
+// 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();
+
+    // 立ち上がり待ち
+    wait_ms(20);
+
+    // シフトレジスタの初期化
+    SHIFTOUT(0xFF, 0xFF);
+
+    // LCD初期化
+    lcd.init();
+
+    // タイトル表示
+    int key = putTitle();
+
+    // NEXTボタンでメニュー表示
+    if (key & SW_NEXT)
+      menu_mode();
+
+    // 再生モード
+    play_mode();
+}