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