MercuryUnit.java
     1: //========================================================================================
     2: //  MercuryUnit.java
     3: //    en:Mercury-Unit V4
     4: //    ja:まーきゅりーゆにっとV4
     5: //  Copyright (C) 2003-2026 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.util.*;  //Arrays
    16: 
    17: public class MercuryUnit {
    18: 
    19:   public static final boolean MU4_ON = true;
    20:   public static boolean mu4OnRequest;
    21:   public static boolean mu4On;
    22:   public static boolean mu4OutputEnable;
    23: 
    24:   public static final boolean MU4_DEBUG = false;
    25: 
    26:   //mu4Init ()
    27:   //  初期化
    28:   public static void mu4Init () {
    29:     //パラメータ
    30:     mu4OnRequest = Settings.sgsGetOnOff ("mercury");
    31:     mu4OutputEnable = Settings.sgsGetOnOff ("mercuryoe");
    32:     //エルミート補間
    33:     mu4Hermite640 = new Hermite (640, 2500);
    34:     mu4Hermite882 = new Hermite (882, 2500);
    35:     mu4Hermite960 = new Hermite (960, 2500);
    36:     mu4Hermite1280 = new Hermite (1280, 2500);
    37:     mu4Hermite1764 = new Hermite (1764, 2500);
    38:     mu4Hermite1920 = new Hermite (1920, 2500);
    39:     //ノイズ抑制
    40:     for (int i = 0; i <= MU4_ATTACK_FRAMES; i++) {  //MU4_ATTACK_FRAMESを含む
    41:       mu4AttackArray[i] =
    42:         (float) ((1.0 + Math.sin (Math.PI *
    43:                                   (double) (i - MU4_ATTACK_FRAMES / 2) /
    44:                                   (double) MU4_ATTACK_FRAMES)) / 2.0);
    45:     }
    46:     if (MU4_DEBUG) {
    47:       for (int i = 0; i <= MU4_ATTACK_FRAMES; i++) {
    48:         System.out.printf ("mu4AttackArray[%d]=%.6f\n", i, mu4AttackArray[i]);
    49:       }
    50:     }
    51:   }  //mu4Init
    52: 
    53:   //mu4Tini ()
    54:   //  後始末
    55:   public static void mu4Tini () {
    56:     //パラメータ
    57:     Settings.sgsPutOnOff ("mercury", mu4OnRequest);
    58:     Settings.sgsPutOnOff ("mercuryoe", mu4OutputEnable);
    59:   }  //mu4Tini
    60: 
    61:   //mu4Reset ()
    62:   //  リセット
    63:   public static void mu4Reset () {
    64:     mu4On = mu4OnRequest;
    65:     //  command=0x1c20
    66:     //  status=0x1c2e
    67:     mu4PortUpper = (MU4_U_RESERVED |
    68:                     MU4_U12_TERR |
    69:                     //MU4_U11_EXPCL |
    70:                     //MU4_U10_EXREQ |
    71:                     MU4_U6_INSEL);
    72:     mu4PortCommand = 0;
    73:     mu4PortStatus = (MU4_S3_N32K |
    74:                      MU4_S2_N44K |
    75:                      MU4_S1_N48K);
    76:     //mu4PortUpper &= ~(MU4_U11_EXPCL | MU4_U10_EXREQ);
    77:     mu4EnablePclReq = true;
    78:     mu4RiseExpcl ();
    79:     mu4RiseExreq ();
    80:     mu4EnablePclReq = false;
    81:     mu4SequenceArray = null;
    82:     Arrays.fill (mu4Buffer, 0);  //出力中にリセットされたとき必要
    83:     TickerQueue.tkqRemove (mu4SequenceTicker);
    84:     mu4PortCommand = -1;  //必ず変化したことにする
    85:     mu4SetCommand (MU4_U_RESERVED |
    86:                    MU4_U12_TERR |
    87:                    MU4_U11_EXPCL |
    88:                    MU4_U10_EXREQ |
    89:                    MU4_U6_INSEL);
    90:   }  //mu4Reset
    91: 
    92:   //d = mu4ReadByte (a)
    93:   //  バイト読み出し
    94:   public static int mu4ReadByte (int a) {
    95:     int a70 = a & 0x70;
    96:     int d = 0;
    97:     if (a70 == 0x10) {  //0x90-0x9f command
    98:       d = (a & 1) == 0 ? mu4PortUpper >> 8 : (mu4PortUpper | mu4PortCommand) & 0xff;
    99:     } else if (a70 == 0x20) {  //0xa0-0xaf status
   100:       d = (a & 1) == 0 ? mu4PortUpper >> 8 : (mu4PortUpper | mu4PortStatus) & 0xff;
   101:     }
   102:     if (MU4_DEBUG) {
   103:       System.out.printf ("%08x mu4ReadByte(0x%08x)=0x%02x\n", XEiJ.regPC0, a, d);
   104:     }
   105:     return d;
   106:   }  //mu4ReadByte
   107: 
   108:   //d = mu4ReadWord (a)
   109:   //  ワード読み出し
   110:   public static int mu4ReadWord (int a) {
   111:     int a70 = a & 0x70;
   112:     int d = 0;
   113:     if (a70 == 0x10) {  //0x90-0x9f command
   114:       d = mu4PortUpper | mu4PortCommand;
   115:     } else if (a70 == 0x20) {  //0xa0-0xaf status
   116:       d = mu4PortUpper | mu4PortStatus;
   117:     }
   118:     if (MU4_DEBUG) {
   119:       System.out.printf ("%08x mu4ReadWord(0x%08x)=0x%04x\n", XEiJ.regPC0, a, d);
   120:     }
   121:     return d;
   122:   }  //mu4ReadWord
   123: 
   124:   //mu4WriteByte (a, d)
   125:   //  バイト書き込み
   126:   public static void mu4WriteByte (int a, int d) {
   127:     d &= 0xff;
   128:     if (MU4_DEBUG) {
   129:       System.out.printf ("%08x mu4WriteByte(0x%08x,0x%02x)\n", XEiJ.regPC0, a, d);
   130:     }
   131:     int a70 = a & 0x70;
   132:     if (a70 == 0x10) {  //0x90-0x9f command
   133:       int t = mu4PortUpper | mu4PortCommand;
   134:       if ((a & 1) == 0) {
   135:         mu4SetCommand ((d << 8) | (t & 0xff));
   136:       } else {
   137:         mu4SetCommand ((t & 0xff00) | d);
   138:       }
   139:     }
   140:   }  //mu4WriteByte
   141: 
   142:   //mu4WriteWord (a, d)
   143:   //  ワード書き込み
   144:   public static void mu4WriteWord (int a, int d) {
   145:     d &= 0xffff;
   146:     if (MU4_DEBUG) {
   147:       System.out.printf ("%08x mu4WriteWord(0x%08x,0x%04x)\n", XEiJ.regPC0, a, d);
   148:     }
   149:     int a70 = a & 0x70;
   150:     if (a70 == 0x00 &&  //0x80-0x8f data
   151:         mu4Mode == MU4_MODE_OUT &&
   152:         mu4OutputEnable &&  //出力許可
   153:         0 <= mu4WritePointer) {  //リクエストあり
   154:       if ((mu4DataChannels == 1 ||  //mono
   155:            (mu4WritePointer & 1) == 0) &&  //left
   156:           (mu4PortCommand & MU4_C2_LG) != 0) {  //L-on
   157:         mu4NaiKamoLeft = false;
   158:         if (mu4NaiDesuLeft) {
   159:           mu4NaiDesuLeft = false;  //ない→ある
   160:           mu4AttackIndexLeft = 1;  //アタック開始
   161:         }
   162:         mu4DataArray[mu4WritePointer] = Math.round (mu4AttackArray[mu4AttackIndexLeft] * (short) d);
   163:         mu4AttackIndexLeft = Math.min (MU4_ATTACK_FRAMES, mu4AttackIndexLeft + 1);
   164:       }
   165:       if ((mu4DataChannels == 1 ||  //mono
   166:            (mu4WritePointer & 1) != 0) &&  //right
   167:           (mu4PortCommand & MU4_C3_RG) != 0) {  //R-on
   168:         mu4NaiKamoRight = false;
   169:         if (mu4NaiDesuRight) {
   170:           mu4NaiDesuRight = false;  //ない→ある
   171:           mu4AttackIndexRight = 1;  //アタック開始
   172:         }
   173:         mu4DataArray[mu4WritePointer + (2 - mu4DataChannels)] = Math.round (mu4AttackArray[mu4AttackIndexRight] * (short) d);
   174:         mu4AttackIndexRight = Math.min (MU4_ATTACK_FRAMES, mu4AttackIndexRight + 1);
   175:       }
   176:       if (false) {  //1回のEXREQに対して複数回wirteされたときfalse=最後,true=最初を有効にする
   177:         mu4WritePointer = -1;
   178:       }
   179:       mu4DataWritten = true;
   180:     } else if (a70 == 0x10) {  //0x90-0x9f command
   181:       mu4SetCommand (d);
   182:     }
   183:   }  //mu4WriteWord
   184: 
   185:   //mu4HsyncStart (hsyncTime)
   186:   //  水平同期パルス開始
   187:   //    M256のときシーケンスを開始する
   188:   //  CRTCが呼び出す
   189:   //    垂直同期パルス期間は除いてある
   190:   public static void mu4HsyncStart (long hsyncTime) {
   191:     if (mu4Mode != MU4_MODE_M256) {
   192:       return;
   193:     }
   194:     mu4SequenceArray = (mu4DataChannels == 1 ?  //mono
   195:                         mu4DataRate < 32000 ? MU4_M256_LOW_MONO : MU4_M256_HIGH_MONO
   196:                         :  //stereo
   197:                         mu4DataRate < 32000 ? MU4_M256_LOW_STEREO : MU4_M256_HIGH_STEREO);
   198:     if (mu4SequenceArray == MU4_M256_LOW_STEREO &&
   199:         (mu4PortUpper & MU4_U54_CLKSEL) == MU4_U54_32K) {
   200:       mu4SequenceArray = MU4_M256_LOW_MONO;  //不自然。シミュレータのバグ?
   201:     }
   202:     //水平同期パルス開始時刻
   203:     //  XEiJ.mpuClockTimeでは水平同期パルスが遅延していたときシーケンスが欠落して画面が乱れる
   204:     //  水平同期パルスと一緒に遅延させることで欠落を避ける
   205:     mu4BlockTime = hsyncTime;
   206:     //フレーム番号
   207:     mu4FrameNumber = 0;
   208:     //シーケンス内インデックス
   209:     mu4SequenceIndex = 0;
   210:     //シーケンスティッカーを予約する
   211:     TickerQueue.tkqAdd (
   212:       mu4SequenceTicker,  //シーケンスティッカー
   213:       mu4BlockTime +  //水平同期パルス開始時刻
   214:       mu4SequenceArray[mu4SequenceIndex++] *  //シーケンス内サイクル数
   215:       XEiJ.TMR_FREQ / mu4DataFrequency);  //サイクル数を時刻に変換する。順序に注意
   216:   }  //mu4HsyncStart
   217: 
   218:   //mu4ExackStart ()
   219:   //  EXACK開始
   220:   //    ACKIGでないときEXREQを出していたら引っ込める
   221:   //  HD63450が呼び出す
   222:   public static void mu4ExackStart () {
   223:     if ((mu4PortUpper & MU4_U8_ACKIG) != 0) {  //ACKIGでない
   224:       mu4RiseExreq ();
   225:     }
   226:   }  //mu4ExackStart
   227: 
   228: 
   229: 
   230:   //周波数変換後のデータの配列
   231:   public static final int[] mu4Buffer = new int[2 * 2500];
   232: 
   233:   //mu4FillBuffer ()
   234:   //  周波数変換後のデータを作る
   235:   public static void mu4FillBuffer () {
   236:     if (mu4Mode != MU4_MODE_OUT) {  //OUTでない
   237:       return;
   238:     }
   239:     if (MU4_DEBUG) {
   240:       System.out.printf ("%08x mu4FillBuffer()\n", XEiJ.regPC0);
   241:     }
   242:     //フレーム番号を巻き戻す
   243:     mu4FrameNumber -= mu4DataFrames;
   244:     //ブロック開始時刻を進める
   245:     mu4BlockTime += MU4_BLOCK_TIME;
   246:     //シーケンスティッカーを再設定する
   247:     TickerQueue.tkqAdd (
   248:       mu4SequenceTicker,  //シーケンスティッカー
   249:       mu4BlockTime +  //ブロック開始時刻
   250:       (mu4DataCycles * mu4FrameNumber + mu4SequenceArray[mu4SequenceIndex - 1]) *  //ブロック内サイクル数
   251:       XEiJ.TMR_FREQ / mu4DataFrequency);  //サイクル数を時刻に変換する。順序に注意
   252:     //書き込みがなければゼロを返して終わり
   253:     if (!mu4DataWritten) {
   254:       Arrays.fill (mu4Buffer, 0);
   255:       return;
   256:     }
   257:     mu4DataWritten = false;
   258:     //周波数変換を行う
   259:     switch (mu4DataFrames) {
   260:     case 640:
   261:       mu4Hermite640.convert ();
   262:       break;
   263:     case 882:
   264:       mu4Hermite882.convert ();
   265:       break;
   266:     case 960:
   267:       mu4Hermite960.convert ();
   268:       break;
   269:     case 1280:
   270:       mu4Hermite1280.convert ();
   271:       break;
   272:     case 1764:
   273:       mu4Hermite1764.convert ();
   274:       break;
   275:     case 1920:
   276:       mu4Hermite1920.convert ();
   277:       break;
   278:     }
   279:     //溢れた位置に書き込まれた4フレーム分のデータを先頭に移す
   280:     for (int i = 0; i < 2 * 4; i++) {
   281:       mu4DataArray[i] = mu4DataArray[2 * mu4DataFrames + i];
   282:       mu4DataWritten = mu4DataWritten || mu4DataArray[i] != 0;
   283:     }
   284:     Arrays.fill (mu4DataArray, 2 * 4, 2 * (mu4DataFrames + 4), 0);
   285:   }  //mu4FillBuffer
   286: 
   287: 
   288: 
   289:   //ポート
   290:   //  command/status bit12-4
   291:   static final int MU4_U_RESERVED = 7 << 13;
   292:   static final int MU4_U12_TERR   = 1 << 12;  //0=TERR
   293:   static final int MU4_U11_EXPCL  = 1 << 11;  //0=EXPCL
   294:   static final int MU4_U10_EXREQ  = 1 << 10;  //0=EXREQ
   295:   static final int MU4_U9_M256    = 1 << 9;  //0=M256
   296:   static final int MU4_U8_ACKIG   = 1 << 8;  //0=ACKIG
   297:   static final int MU4_U7_CLKRATE = 1 << 7;  //0=16k/22k/24k,1=32k/44k/48k
   298:   static final int MU4_U6_INSEL   = 1 << 6;  //0=optical,1=coaxial
   299:   static final int MU4_U54_CLKSEL = 3 << 4;  //01=16k/32k,00/10=22k/44k,11=24k/48k
   300:   static final int MU4_U54_32K    = 1 << 4;
   301:   static final int MU4_U54_48K    = 3 << 4;
   302:   //  command bit3-0
   303:   static final int MU4_C3_RG      = 1 << 3;  //0=R-off,1=R-on
   304:   static final int MU4_C2_LG      = 1 << 2;  //0=L-off,1=L-on
   305:   static final int MU4_C1_STEREO  = 1 << 1;  //0=mono,1=stereo
   306:   static final int MU4_C0_OUT     = 1 << 0;  //0=in,1=out
   307:   //  status bit3-0
   308:   static final int MU4_S3_N32K    = 1 << 3;  //0=32k
   309:   static final int MU4_S2_N44K    = 1 << 2;  //0=44k
   310:   static final int MU4_S1_N48K    = 1 << 1;  //0=48k
   311:   static final int MU4_S0_ERR     = 1 << 0;  //1=error
   312:   //
   313:   static int mu4PortUpper;  //command/status bit12-bit4
   314:   static int mu4PortCommand;  //command bit3-0
   315:   static int mu4PortStatus;  //status bit3-0
   316: 
   317:   //モード
   318:   static final int MU4_MODE_M256 = 0;  //M256
   319:   static final int MU4_MODE_OUT  = 1;  //OUT
   320:   static final int MU4_MODE_IN   = 2;  //IN。未対応
   321:   static int mu4Mode;
   322: 
   323:   //EXPCLとEXREQの出力許可
   324:   static boolean mu4EnablePclReq;  //false=禁止,true=許可
   325: 
   326:   //データオシレータ周波数。1秒あたりのデータオシレータサイクル数
   327:   static int mu4DataFrequency;  //12288000=16k/32k,16934400=22k/44k,18432000=24k/48k
   328: 
   329:   //データフレームサイクル数。1データフレームあたりのデータオシレータサイクル数
   330:   static int mu4DataCycles;  //384=high,768=low
   331: 
   332:   //データサンプリング周波数。1秒あたりのデータフレーム数
   333:   //  データオシレータ周波数/データフレームサイクル数
   334:   static int mu4DataRate;  //16000,22050,24000,32000,44100,48000
   335: 
   336:   //データチャンネル数。1データフレームあたりの要素数
   337:   static int mu4DataChannels;  //1=mono,2=stereo
   338: 
   339:   //ブロック周波数。1秒あたりのブロック数
   340:   //  SoundSource.SND_BLOCK_FREQと同じ
   341:   static final int MU4_BLOCK_RATE = 25;
   342: 
   343:   //1ブロックあたりのデータフレーム数
   344:   //  データサンプリング周波数/ブロック周波数
   345:   static int mu4DataFrames;
   346: 
   347:   //ラインサンプリング周波数。1秒あたりのラインフレーム数
   348:   //  OPM.OPM_SAMPLE_FREQと同じ
   349:   static final int MU4_LINE_RATE = 62500;
   350: 
   351:   //ラインチャンネル数。1ラインフレームあたりの要素数
   352:   //  SoundSource.SND_CHANNELSと同じ
   353:   static final int MU4_LINE_CHANNELS = 2;
   354: 
   355:   //1ブロックあたりのラインフレーム数
   356:   //  ラインサンプリング周波数/ブロック周波数
   357:   static final int MU4_LINE_FRAMES = MU4_LINE_RATE / MU4_BLOCK_RATE;
   358: 
   359:   //1ブロックあたりのライン要素数
   360:   //  ラインチャンネル数*ラインフレーム数
   361:   static final int MU4_LINE_ELEMENTS = MU4_LINE_CHANNELS * MU4_LINE_FRAMES;
   362: 
   363:   //シーケンス
   364:   //  フレームまたは水平同期パルスの先頭を起点として
   365:   //  EXPCLとEXREQをアサートまたはネゲートする時刻を
   366:   //  データオシレータのサイクル数で記述したもの
   367:   //  OUTのときはデータフレームサイクル数を周期として繰り返す
   368:   static final int MU4_FALL_EXPCL = 0;  //EXPCLをアサートする
   369:   static final int MU4_RISE_EXPCL = 1;  //EXPCLをネゲートする
   370:   static final int MU4_FALL_EXREQ = 2;  //EXREQをアサートする
   371:   static final int MU4_RISE_EXREQ = 3;  //EXREQをネゲートする
   372:   //0xfd11 M256 16k mono
   373:   //0xfd13 M256 16k stereo  不自然。シミュレータのバグ?
   374:   //0xfd21 M256 22k mono
   375:   //0xfd31 M256 24k mono
   376:   static final int[] MU4_M256_LOW_MONO = {
   377:     2, MU4_FALL_EXPCL,
   378:     20, MU4_FALL_EXREQ,
   379:     283, MU4_RISE_EXREQ,
   380:     385, MU4_RISE_EXPCL,
   381:   };
   382:   //0xfd23 M256 22k stereo
   383:   //0xfd33 M256 24k stereo
   384:   static final int[] MU4_M256_LOW_STEREO = {
   385:     2, MU4_FALL_EXPCL,
   386:     283, MU4_RISE_EXREQ,
   387:     385, MU4_RISE_EXPCL,
   388:     404, MU4_FALL_EXREQ,
   389:   };
   390:   //0xfd91 M256 32k mono
   391:   //0xfda1 M256 44k mono
   392:   //0xfdb1 M256 48k mono
   393:   static final int[] MU4_M256_HIGH_MONO = {
   394:     2, MU4_FALL_EXPCL,
   395:     10, MU4_FALL_EXREQ,
   396:     142, MU4_RISE_EXREQ,
   397:     193, MU4_RISE_EXPCL,
   398:   };
   399:   //0xfd93 M256 32k stereo
   400:   //0xfda3 M256 44k stereo
   401:   //0xfdb3 M256 48k stereo
   402:   static final int[] MU4_M256_HIGH_STEREO = {
   403:     2, MU4_FALL_EXPCL,
   404:     10, MU4_FALL_EXREQ,
   405:     142, MU4_RISE_EXREQ,
   406:     193, MU4_RISE_EXPCL,
   407:     202, MU4_FALL_EXREQ,
   408:     334, MU4_RISE_EXREQ,
   409:   };
   410:   //0xfe5d OUT 16k mono
   411:   //0xfe6d OUT 22k mono
   412:   //0xfe7d OUT 24k mono
   413:   static final int[] MU4_OUT_LOW_MONO = {
   414:     0, MU4_FALL_EXPCL,
   415:     379, MU4_FALL_EXREQ,
   416:     384, MU4_RISE_EXPCL,
   417:     667, MU4_RISE_EXREQ,
   418:   };
   419:   //0xfe5f OUT 16k stereo
   420:   //0xfe6f OUT 22k stereo
   421:   //0xfe7f OUT 24k stereo
   422:   static final int[] MU4_OUT_LOW_STEREO = {
   423:     0, MU4_FALL_EXPCL,
   424:     283, MU4_RISE_EXREQ,
   425:     379, MU4_FALL_EXREQ,
   426:     384, MU4_RISE_EXPCL,
   427:     667, MU4_RISE_EXREQ,
   428:     763, MU4_FALL_EXREQ,
   429:   };
   430:   //0xfedd OUT 32k mono
   431:   //0xfeed OUT 44k mono
   432:   //0xfefd OUT 48k mono
   433:   static final int[] MU4_OUT_HIGH_MONO = {
   434:     0, MU4_FALL_EXPCL,
   435:     190, MU4_FALL_EXREQ,
   436:     192, MU4_RISE_EXPCL,
   437:     334, MU4_RISE_EXREQ,
   438:   };
   439:   //0xfedf OUT 32k stereo
   440:   //0xfeef OUT 44k stereo
   441:   //0xfeff OUT 48k stereo
   442:   static final int[] MU4_OUT_HIGH_STEREO = {
   443:     0, MU4_FALL_EXPCL,
   444:     142, MU4_RISE_EXREQ,
   445:     190, MU4_FALL_EXREQ,
   446:     192, MU4_RISE_EXPCL,
   447:     334, MU4_RISE_EXREQ,
   448:     382, MU4_FALL_EXREQ,
   449:   };
   450: 
   451:   //シーケンスの配列
   452:   static int[] mu4SequenceArray;
   453: 
   454:   //1ブロックの時間
   455:   //  SoundSource.SND_BLOCK_TIMEと同じ
   456:   static final long MU4_BLOCK_TIME = XEiJ.TMR_FREQ / MU4_BLOCK_RATE;
   457: 
   458:   //ブロック開始時刻
   459:   //  SoundSource.sndBlockClock-MU4_BLOCK_TIME
   460:   //  M256のときは水平同期パルス開始時刻
   461:   static long mu4BlockTime;
   462: 
   463:   //フレーム番号
   464:   //  現在のシーケンスのブロック内フレーム番号
   465:   //  M256のときは0のみ
   466:   static int mu4FrameNumber;
   467: 
   468:   //シーケンス内インデックス
   469:   static int mu4SequenceIndex;
   470: 
   471:   //LRカウンタ
   472:   //  シーケンス側がmu4WritePointerを決めるときに使う
   473:   //  それ以外はmu4DataChannelsとmu4WritePointerの奇遇で判断すること
   474:   static int mu4LRCounter;
   475: 
   476:   //データ書き込み位置
   477:   //  常にステレオ
   478:   //  -1=リクエストなし
   479:   static int mu4WritePointer;
   480: 
   481:   //周波数変換前のデータの配列
   482:   //  常にステレオ
   483:   //  モノラルのデータをフレーム単位でパンできるようにするため
   484:   static final int[] mu4DataArray = new int[2 * (1920 + 4)];
   485: 
   486:   //データ書き込みフラグ
   487:   static boolean mu4DataWritten;
   488: 
   489: 
   490: 
   491:   static void mu4DumpVars () {
   492:     System.out.printf ("mu4Mode=%d\n", mu4Mode);
   493:     System.out.printf ("mu4EnablePclReq=%b\n", mu4EnablePclReq);
   494:     System.out.printf ("mu4DataFrequency=%d\n", mu4DataFrequency);
   495:     System.out.printf ("mu4DataCycles=%d\n", mu4DataCycles);
   496:     System.out.printf ("mu4DataRate=%d\n", mu4DataRate);
   497:     System.out.printf ("mu4DataChannels=%d\n", mu4DataChannels);
   498:     System.out.printf ("mu4DataFrames=%d\n", mu4DataFrames);
   499:     System.out.printf ("mu4BlockTime=%d\n", mu4BlockTime);
   500:     System.out.printf ("mu4FrameNumber=%d\n", mu4FrameNumber);
   501:     System.out.printf ("mu4SequenceIndex=%d\n", mu4SequenceIndex);
   502:     System.out.printf ("mu4LRCounter=%d\n", mu4LRCounter);
   503:     System.out.printf ("mu4WritePointer=%d\n", mu4WritePointer);
   504:     System.out.printf ("mu4DataWritten=%b\n", mu4DataWritten);
   505:   }  //mu4DumpVars
   506: 
   507:   //mu4SetCommand (d)
   508:   //  コマンド書き込み
   509:   static void mu4SetCommand (int d) {
   510:     if (MU4_DEBUG) {
   511:       System.out.printf ("%08x mu4SetCommand(0x%04x)\n", XEiJ.regPC0, d);
   512:     }
   513:     int prevMode = mu4Mode;
   514:     int prevDataRate = mu4DataRate;
   515:     int prevDataChannels = mu4DataChannels;
   516:     int prevUpper = mu4PortUpper;
   517:     int prevCommand = mu4PortCommand;
   518:     mu4PortUpper = ((mu4PortUpper & (MU4_U_RESERVED |
   519:                                      MU4_U12_TERR |
   520:                                      MU4_U11_EXPCL |
   521:                                      MU4_U10_EXREQ)) |
   522:                     (d & (MU4_U9_M256 |
   523:                           MU4_U8_ACKIG |
   524:                           MU4_U7_CLKRATE |
   525:                           MU4_U6_INSEL |
   526:                           MU4_U54_CLKSEL)));
   527:     mu4PortCommand = d & (MU4_C3_RG |
   528:                           MU4_C2_LG |
   529:                           MU4_C1_STEREO |
   530:                           MU4_C0_OUT);
   531:     //
   532:     //EXPCLとEXREQの出力許可
   533:     mu4EnablePclReq = (mu4PortUpper & (MU4_U9_M256 | MU4_U8_ACKIG)) != 0;
   534:     //下位バイトが変化していないときは何もしない
   535:     if ((((mu4PortUpper | mu4PortCommand) ^ (prevUpper | prevCommand)) & 0xff) == 0) {
   536:       return;
   537:     }
   538:     //モード
   539:     mu4Mode = ((mu4PortUpper & MU4_U9_M256) == 0 ? MU4_MODE_M256 :
   540:                (mu4PortCommand & MU4_C0_OUT) != 0 ? MU4_MODE_OUT :
   541:                MU4_MODE_IN);
   542:     //データオシレータ周波数
   543:     mu4DataFrequency = ((mu4PortUpper & MU4_U54_CLKSEL) == MU4_U54_32K ? 12288000 :
   544:                         (mu4PortUpper & MU4_U54_CLKSEL) != MU4_U54_48K ? 16934400 :
   545:                         18432000);
   546:     //データフレームサイクル数
   547:     mu4DataCycles = (mu4PortUpper & MU4_U7_CLKRATE) == 0 ? 768 : 384;
   548:     //データサンプリング周波数
   549:     mu4DataRate = mu4DataFrequency / mu4DataCycles;
   550:     //データチャンネル数
   551:     mu4DataChannels = (mu4PortCommand & MU4_C1_STEREO) == 0 ? 1 : 2;
   552:     //
   553:     if (mu4Mode != prevMode ||  //モードが変わったか
   554:         mu4DataRate != prevDataRate ||  //サンプリング周波数が変わったか
   555:         mu4DataChannels != prevDataChannels) {  //データチャンネル数が変わったとき
   556:       if (mu4Mode == MU4_MODE_M256) {  //M256
   557:         //ここではシーケンスを開始しない
   558:         mu4SequenceArray = null;
   559:       } else if (mu4Mode == MU4_MODE_OUT) {  //OUT
   560:         //1ブロックあたりのデータフレーム数
   561:         mu4DataFrames = mu4DataRate / MU4_BLOCK_RATE;
   562:         //シーケンスの配列
   563:         mu4SequenceArray = (mu4DataChannels == 1 ?  //mono
   564:                             mu4DataRate < 32000 ? MU4_OUT_LOW_MONO : MU4_OUT_HIGH_MONO
   565:                             :  //stereo
   566:                             mu4DataRate < 32000 ? MU4_OUT_LOW_STEREO : MU4_OUT_HIGH_STEREO);
   567:         //ブロック開始時刻
   568:         mu4BlockTime = SoundSource.sndBlockClock - MU4_BLOCK_TIME;
   569:         //フレーム番号
   570:         //  max(0,min(フレーム数,ceil((現在時刻-ブロック開始時刻)*フレーム数/ブロック時間)))
   571:         mu4FrameNumber =
   572:           Math.max (0,
   573:                     Math.min (mu4DataFrames,
   574:                               (int) (((XEiJ.mpuClockTime - mu4BlockTime) * mu4DataFrames +
   575:                                       (MU4_BLOCK_TIME - 1)) / MU4_BLOCK_TIME)));
   576:         //シーケンス内インデックス
   577:         mu4SequenceIndex = 0;
   578:         //LRカウンタ
   579:         mu4LRCounter = 0;
   580:         //データ書き込み位置
   581:         mu4WritePointer = -1;
   582:         if (mu4DataRate != prevDataRate ||  //データサンプリング周波数が変わったか
   583:             mu4DataChannels != prevDataChannels) {  //データチャンネル数が変わったとき
   584:           //データの配列をゼロクリアする
   585:           Arrays.fill (mu4DataArray, 0, 2 * (mu4DataFrames + 4), 0);
   586:         }
   587:         //データ書き込みフラグ
   588:         mu4DataWritten = false;
   589:         if (MU4_DEBUG) {
   590:           mu4DumpVars ();
   591:         }
   592:         //ノイズ抑制
   593:         mu4AttackIndexLeft = 1;
   594:         mu4AttackIndexRight = 1;
   595:         mu4NaiKamoLeft = true;
   596:         mu4NaiKamoRight = true;
   597:         mu4NaiDesuLeft = true;
   598:         mu4NaiDesuRight = true;
   599:         //シーケンスティッカーを予約する
   600:         TickerQueue.tkqAdd (
   601:           mu4SequenceTicker,  //シーケンスティッカー
   602:           mu4BlockTime +  //ブロック開始時刻
   603:           (mu4DataCycles * mu4FrameNumber + mu4SequenceArray[mu4SequenceIndex++]) *  //ブロック内サイクル数
   604:           XEiJ.TMR_FREQ / mu4DataFrequency);  //サイクル数を時刻に変換する。順序に注意
   605:       } else {  //IN
   606:         //!!! 未対応
   607:         mu4SequenceArray = null;
   608:       }
   609:     }
   610:   }  //mu4SetCommand
   611: 
   612:   //mu4SequenceTicker
   613:   //  シーケンスティッカー
   614:   static final TickerQueue.Ticker mu4SequenceTicker = new TickerQueue.Ticker () {
   615:     @Override protected void tick () {
   616:       if (mu4SequenceArray == null) {
   617:         return;
   618:       }
   619:       switch (mu4SequenceArray[mu4SequenceIndex++]) {  //コマンド
   620:       case MU4_FALL_EXPCL:
   621:         mu4FallExpcl ();
   622:         break;
   623:       case MU4_RISE_EXPCL:
   624:         mu4RiseExpcl ();
   625:         break;
   626:       case MU4_FALL_EXREQ:
   627:         if (mu4Mode == MU4_MODE_OUT) {
   628:           //データ書き込み位置
   629:           mu4WritePointer = 2 * (mu4FrameNumber + 3) + mu4LRCounter;
   630:           //LRカウンタ
   631:           mu4LRCounter ^= 1;
   632:           //ノイズ抑制
   633:           mu4DataArray[mu4WritePointer] = (int) (mu4DataArray[mu4WritePointer - 2] * MU4_DECAY_RATIO);  //端数は切り捨てる。roundだと1→1で0にならない
   634:           if (mu4DataChannels == 1 ||  //mono
   635:               (mu4WritePointer & 1) == 0) {  //left
   636:             mu4NaiDesuLeft = mu4NaiDesuLeft || mu4NaiKamoLeft;
   637:             mu4NaiKamoLeft = true;
   638:           }
   639:           if (mu4DataChannels == 1 ||  //mono
   640:               (mu4WritePointer & 1) != 0) {  //right
   641:             mu4NaiDesuRight = mu4NaiDesuRight || mu4NaiKamoRight;
   642:             mu4NaiKamoRight = true;
   643:           }
   644:         }
   645:         mu4FallExreq ();
   646:         break;
   647:       case MU4_RISE_EXREQ:
   648:         mu4RiseExreq ();
   649:         break;
   650:       }
   651:       if (mu4SequenceArray.length <= mu4SequenceIndex) {  //シーケンス終了
   652:         if (mu4Mode == MU4_MODE_M256) {  //M256のとき
   653:           mu4SequenceArray = null;  //ここでは次のシーケンスを開始しない
   654:           return;
   655:         }
   656:         //OUT
   657:         mu4FrameNumber++;
   658:         mu4SequenceIndex = 0;
   659:         mu4LRCounter = 0;
   660:       }
   661:       //シーケンスティッカーを予約する
   662:       TickerQueue.tkqAdd (
   663:         mu4SequenceTicker,  //シーケンスティッカー
   664:         mu4BlockTime +  //ブロック開始時刻
   665:         (mu4DataCycles * mu4FrameNumber + mu4SequenceArray[mu4SequenceIndex++]) *  //ブロック内サイクル数
   666:         XEiJ.TMR_FREQ / mu4DataFrequency);  //サイクル数を時刻に変換する。順序に注意
   667:     }  //tick
   668:   };  //mu4Ticker
   669: 
   670:   //mu4FallExpcl ()
   671:   //  EXPCLをアサートする
   672:   static void mu4FallExpcl () {
   673:     if ((mu4PortUpper & MU4_U11_EXPCL) != 0) {
   674:       mu4PortUpper &= ~MU4_U11_EXPCL;
   675:       if (mu4EnablePclReq) {
   676:         HD63450.dmaFallPCL (2);
   677:       }
   678:     }
   679:   }  //mu4FallExpcl
   680: 
   681:   //mu4RiseExpcl ()
   682:   //  EXPCLをネゲートする
   683:   static void mu4RiseExpcl () {
   684:     if ((mu4PortUpper & MU4_U11_EXPCL) == 0) {
   685:       mu4PortUpper |= MU4_U11_EXPCL;
   686:       if (mu4EnablePclReq) {
   687:         HD63450.dmaRisePCL (2);
   688:       }
   689:     }
   690:   }  //mu4RiseExpcl
   691: 
   692:   //mu4FallExreq ()
   693:   //  EXREQをアサートする
   694:   static void mu4FallExreq () {
   695:     if ((mu4PortUpper & MU4_U10_EXREQ) != 0) {
   696:       mu4PortUpper &= ~MU4_U10_EXREQ;
   697:       if (mu4EnablePclReq) {
   698:         HD63450.dmaFallREQ (2);
   699:       }
   700:     }
   701:   }  //mu4FallExreq
   702: 
   703:   //mu4RiseExreq ()
   704:   //  EXREQをネゲートする
   705:   static void mu4RiseExreq () {
   706:     if ((mu4PortUpper & MU4_U10_EXREQ) == 0) {
   707:       mu4PortUpper |= MU4_U10_EXREQ;
   708:       if (mu4EnablePclReq) {
   709:         HD63450.dmaRiseREQ (2);
   710:       }
   711:     }
   712:   }  //mu4RiseExreq
   713: 
   714: 
   715: 
   716:   //エルミート補間
   717:   static Hermite mu4Hermite640;
   718:   static Hermite mu4Hermite882;
   719:   static Hermite mu4Hermite960;
   720:   static Hermite mu4Hermite1280;
   721:   static Hermite mu4Hermite1764;
   722:   static Hermite mu4Hermite1920;
   723: 
   724:   //class Hermite
   725:   //  エルミート補間
   726:   static class Hermite {
   727:     int inputFrames, outputFrames;
   728:     int inputFactor, outputFactor;
   729:     float[] coeffArray;
   730:     int[] indexArray;
   731:     Hermite (int inputFrames, int outputFrames) {
   732:       this.inputFrames = inputFrames;
   733:       this.outputFrames = outputFrames;
   734:       int g = gcd (inputFrames, outputFrames);
   735:       inputFactor = inputFrames / g;
   736:       outputFactor = outputFrames / g;
   737:       coeffArray = new float[4 * outputFactor];
   738:       indexArray = new int[outputFactor];
   739:       int inputIndex1 = 0;
   740:       int ratio1 = 0;
   741:       for (int outputIndex = 0; outputIndex < outputFactor; outputIndex++) {
   742:         int inputIndex0 = inputIndex1;
   743:         int ratio0 = ratio1;
   744:         ratio1 += inputFactor;
   745:         while (outputFactor <= ratio1) {
   746:           ratio1 -= outputFactor;
   747:           inputIndex1++;
   748:         }
   749:         //
   750:         //  エルミート補間の係数を求める
   751:         //
   752:         //  f(0)=p0
   753:         //  f(1)=p1
   754:         //  f'(0)=(p1-pm)/2
   755:         //  f'(1)=(p2-p0)/2
   756:         //  を満たす3次関数を作る
   757:         //
   758:         //  f(x)=a*x^3+b*x^2+c*x+d
   759:         //  f'(x)=3*a*x^2+2*b*x+c
   760:         //
   761:         //  f:=sub(first(solve({sub(x=0,a*x^3+b*x^2+c*x+d)=p0,
   762:         //                      sub(x=1,a*x^3+b*x^2+c*x+d)=p1,
   763:         //                      sub(x=0,3*a*x^2+2*b*x+c)=(p1-pm)/2,
   764:         //                      sub(x=1,3*a*x^2+2*b*x+c)=(p2-p0)/2},
   765:         //                     {a,b,c,d})),
   766:         //         a*x^3+b*x^2+c*x+d);
   767:         //  cm:=part(coeff(f,pm),2);
   768:         //  c0:=part(coeff(f,p0),2);
   769:         //  c1:=part(coeff(f,p1),2);
   770:         //  c2:=part(coeff(f,p2),2);
   771:         //  f-(cm*pm+c0*p0+c1*p1+c2*p2);
   772:         //
   773:         //  f(x)=cm*pm+c0*p0+c1*p1+c2*p2
   774:         //  cm=(-x^3+2*x^2-x)/2
   775:         //  c0=(3*x^3-5*x^2+2)/2
   776:         //  c1=(-3*x^3+4*x^2+x)/2
   777:         //  c2=(x^3-x^2)/2
   778:         //
   779:         //  周波数変換ではxは高々4桁通りなのでx毎に{cm,c0,c1,c2}をあらかじめ計算しておける
   780:         //  乗算は4回で済むので(見た目よりは)高速に補間できる
   781:         //
   782:         double x = (double) ratio0 / (double) outputFactor;
   783:         coeffArray[4 * outputIndex] = (float) (((-0.5 * x + 1.0) * x - 0.5) * x);  //cm
   784:         coeffArray[4 * outputIndex + 1] = (float) ((1.5 * x - 2.5) * x * x + 1.0);  //c0
   785:         coeffArray[4 * outputIndex + 2] = (float) (((-1.5 * x + 2.0) * x + 0.5) * x);  //c1
   786:         coeffArray[4 * outputIndex + 3] = (float) ((0.5 * x - 0.5) * x * x);  //c2
   787:         indexArray[outputIndex] = inputIndex0;
   788:       }
   789:     }
   790:     void convert () {
   791:       for (int inputIndex = 0, outputIndex = 0;
   792:            inputIndex < inputFrames;
   793:            inputIndex += inputFactor, outputIndex += outputFactor) {
   794:         for (int od = 0; od < outputFactor; od++) {
   795:           int ii = inputIndex + indexArray[od];
   796:           int oi = outputIndex + od;
   797:           float cm = coeffArray[4 * od];
   798:           float c0 = coeffArray[4 * od + 1];
   799:           float c1 = coeffArray[4 * od + 2];
   800:           float c2 = coeffArray[4 * od + 3];
   801:           mu4Buffer[2 * oi + 0] = Math.round (cm * mu4DataArray[2 * ii + 0] +
   802:                                               c0 * mu4DataArray[2 * ii + 2] +
   803:                                               c1 * mu4DataArray[2 * ii + 4] +
   804:                                               c2 * mu4DataArray[2 * ii + 6]);
   805:           mu4Buffer[2 * oi + 1] = Math.round (cm * mu4DataArray[2 * ii + 1] +
   806:                                               c0 * mu4DataArray[2 * ii + 3] +
   807:                                               c1 * mu4DataArray[2 * ii + 5] +
   808:                                               c2 * mu4DataArray[2 * ii + 7]);
   809:         }
   810:       }
   811:     }  //convert
   812:     int gcd (int x, int y) {
   813:       while (y != 0) {
   814:         int t = x % y;
   815:         x = y;
   816:         y = t;
   817:       }
   818:       return x;
   819:     }  //gcd
   820:   }  //class Hermite
   821: 
   822: 
   823: 
   824:   //ノイズ抑制
   825:   //  アタックとディケイに5ms~10msの時間をかけることでノイズを抑制する
   826:   //  アタックは三角関数、ディケイは指数関数を使う
   827:   //  通常のデータはノイズを抑制する加工がされているはず
   828:   //  ディスクアクセス中にデータが欠落したときなどに効果がある
   829:   static final float MU4_DECAY_RATIO = 0.97F;  //ディケイ率
   830:   static final int MU4_ATTACK_FRAMES = 300;  //アタックにかけるフレーム数
   831:   static final float mu4AttackArray[] = new float[MU4_ATTACK_FRAMES + 1];  //アタックの係数の配列。(1+sin(-pi/2..pi/2))/2
   832:   static int mu4AttackIndexLeft;  //mu4AttackArrayのインデックス
   833:   static int mu4AttackIndexRight;
   834:   static boolean mu4NaiKamoLeft;  //データないかも
   835:   static boolean mu4NaiKamoRight;
   836:   static boolean mu4NaiDesuLeft;  //データないです
   837:   static boolean mu4NaiDesuRight;
   838: 
   839: 
   840: 
   841: }  //class MercuryUnit