RP5C15.java
     1: //========================================================================================
     2: //  RP5C15.java
     3: //    en:RTC -- Real-Time Clock
     4: //    ja:RTC -- リアルタイムクロック
     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: //----------------------------------------------------------------------------------------
    14: //  カウンタ
    15: //    日時をSystem.currentTimeMillis()からのずれとして記憶する
    16: //      ずれの初期値はデフォルトのタイムゾーンのオフセット
    17: //    年カウンタの起点は常に1980年とする
    18: //      X68000のハードウェアが提供する時計なのだからOSによって異なる起点を使用することがあってはならない
    19: //      2079年まで問題なく使用できるのだから起点を変更する必要はない
    20: //    年カウンタの起点が固定されているので閏年カウンタへの書き込みは無効
    21: //      閏年カウンタの値は常に年カウンタの値を4で割った余りに等しい
    22: //      年カウンタに書き込むと閏年カウンタも更新される
    23: //    カウンタとホストの時計の同期はmpuTask.run()で最初にカウンタがアクセスされたときに行う
    24: //      2回目以降はカウンタの値を更新しない
    25: //      mpuTask.run()は10ms間隔で動作するので、ホストの時計の秒針が動いてからRTCの秒カウンタが更新されるまでに最大で10msかかる
    26: //      mpuTask.run()の動作は不連続なので、カウンタが参照される度にホストの時計を読みに行ったとしてもMPUのクロックとRTCの進み方に最大で10msのずれが生じることに変わりはない
    27: //      正規化されていない日時を書き込んだとき、更新後は正規化された日時が読み出される
    28: //! 以下は未対応
    29: //  アラーム
    30: //    アラームが無効のとき、rtcClock=FAR_FUTURE
    31: //    アラームが有効のとき、rtcClock=直近の発動日時(歴通ミリ秒)
    32: //    mpuTask.run()の先頭でrtcClock<=System.currentTimeMillis()ならばアラームを発動する
    33: //----------------------------------------------------------------------------------------
    34: 
    35: package xeij;
    36: 
    37: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
    38: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,TimeZone,Timer,TimerTask,TreeMap
    39: 
    40: public class RP5C15 {
    41: 
    42:   public static final boolean RTC_DEBUG_TRACE = false;
    43:   public static final boolean RTC_DEBUG_WRITE = false;
    44:   public static final boolean RTC_DEBUG_GAP = false;
    45: 
    46:   //RP5C15のレジスタ
    47: 
    48:   //レジスタバンク0
    49:   //  秒カウンタ
    50:   //    00..59 (BCD)
    51:   public static final int RTC_0_SECO0  = 0x01;  //0x00e8a001  bit3-0  1秒カウンタ
    52:   public static final int RTC_0_SECO1  = 0x03;  //0x00e8a003  bit2-0  10秒カウンタ
    53:   //  分カウンタ
    54:   //    00..59 (BCD)
    55:   public static final int RTC_0_MINU0  = 0x05;  //0x00e8a005  bit3-0  1分カウンタ
    56:   public static final int RTC_0_MINU1  = 0x07;  //0x00e8a007  bit2-0  10分カウンタ
    57:   //  時カウンタ
    58:   //    00..31 (BCD)
    59:   //        hour   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
    60:   //    12時間計  00 01 02 03 04 05 06 07 08 09 10 11 20 21 22 23 24 25 26 27 28 29 30 31 (BCD)
    61:   //    24時間計  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (BCD)
    62:   public static final int RTC_0_HOUR0  = 0x09;  //0x00e8a009  bit3-0  1時カウンタ
    63:   public static final int RTC_0_HOUR1  = 0x0b;  //0x00e8a00b  bit1    12時間計のとき12時カウンタ(0=AM,1=PM)
    64:   //                                                                  24時間計のとき20時カウンタ
    65:   //                                                          bit0    10時カウンタ
    66:   //  曜日カウンタ
    67:   //    0..6
    68:   //    7日でオーバーフローする日カウンタ
    69:   //    日カウンタと同時に初期化する必要があり、日カウンタと同時にカウントアップする
    70:   //    0..6のどれが日曜日なのかは初期化したときに決まる
    71:   //    X68000では0=日曜日で初期化されるが、RTC側で0=日曜日と決まっているわけではない
    72:   public static final int RTC_0_WDAY   = 0x0d;  //0x00e8a00d  bit2-0  曜日カウンタ
    73:   //  日カウンタ
    74:   //    01..31 (BCD)。01=1日
    75:   //    年カウンタが4の倍数になっているかどうかとは関係なく、閏年カウンタが0のときに2月を29日までカウントする
    76:   //    閏年カウンタ  月カウンタ  01 02 03 04 05 06 07 08 09 10 11 12
    77:   //          0                   31 29 31 30 31 30 31 31 30 31 30 31
    78:   //          1       日カウンタ  31 28 31 30 31 30 31 31 30 31 30 31
    79:   //          2         の上限    31 28 31 30 31 30 31 31 30 31 30 31
    80:   //          3                   31 28 31 30 31 30 31 31 30 31 30 31
    81:   public static final int RTC_0_MDAY0  = 0x0f;  //0x00e8a00f  bit3-0  1日カウンタ
    82:   public static final int RTC_0_MDAY1  = 0x11;  //0x00e8a011  bit1-0  10日カウンタ
    83:   //  月カウンタ
    84:   //    01..12 (BCD)。01=1月
    85:   public static final int RTC_0_MONT0  = 0x13;  //0x00e8a013  bit3-0  1月カウンタ
    86:   public static final int RTC_0_MONT1  = 0x15;  //0x00e8a015  bit0    10月カウンタ
    87:   //  年カウンタ
    88:   //    00..99 (BCD)
    89:   //    ただの12ヶ月カウンタ
    90:   //    閏年カウンタと同時に初期化する必要があり、閏年カウンタと同時にカウントアップする
    91:   //    00が西暦何年なのかは初期化したときに決まる
    92:   //    X68000では00=1980年で初期化されるが、RTC側で00=1980年と決まっているわけではない
    93:   public static final int RTC_0_YEAR0  = 0x17;  //0x00e8a017  bit3-0  1年カウンタ
    94:   public static final int RTC_0_YEAR1  = 0x19;  //0x00e8a019  bit3-0  10年カウンタ
    95: 
    96:   //レジスタバンク1
    97:   //  CLKOUTセレクタ
    98:   //    0..7。0=点灯,1=16384Hz,2=1024Hz,3=128Hz,4=16Hz,5=1Hz,6=1/64Hz,7=消灯
    99:   //    X68000ではRP5C15のCLKOUT信号がTIMER-LEDに接続されており、このレジスタはTIMER-LEDの状態を制御するために使われている
   100:   //    IOCS _ALARMMODやIOCS _ALARMSETでアラームを許可したとき、0が書き込まれてTIMER-LEDが点灯する
   101:   //    IOCS _ALARMMODでアラームを禁止したとき、7が書き込まれてTIMER-LEDが消灯する
   102:   //    アラームで起動したとき、5が書き込まれてTIMER-LEDが点滅する
   103:   //    KRAMD.SYSは転送開始時と転送終了時に7をEORすることでTIMER-LEDをRAMディスクのアクセスランプにしている
   104:   //    #1000ではCLKOUTがMFPのGPIP5にも接続されることになっていたが、X68000では接続されていない
   105:   public static final int RTC_1_CLKOUT = 0x01;  //0x00e8a001  bit2-0  CLKOUTセレクタ
   106:   //  アジャスト
   107:   //    1=秒を29捨30入する
   108:   public static final int RTC_1_ADJUST = 0x03;  //0x00e8a003  bit0    アジャスト
   109:   //  アラームレジスタ
   110:   //    1分カウンタ~10日カウンタがアラーム1分レジスタ~アラーム10日レジスタとすべて一致したときアラーム出力が始まる
   111:   //    アラームリセット後に一度も書き込まれていないアラームレジスタはdon't careで常に一致していると見なされる
   112:   //    アラームリセットすると全項目がdon't careになりアラーム出力が始まってしまうので、
   113:   //    通常はアラーム出力をOFFにしてからアラームリセットする
   114:   public static final int RTC_1_MINU0  = 0x05;  //0x00e8a005  bit3-0  アラーム1分レジスタ
   115:   public static final int RTC_1_MINU1  = 0x07;  //0x00e8a007  bit2-0  アラーム10分レジスタ
   116:   public static final int RTC_1_HOUR0  = 0x09;  //0x00e8a009  bit3-0  アラーム1時レジスタ
   117:   public static final int RTC_1_HOUR1  = 0x0b;  //0x00e8a00b  bit2-0  アラーム10時レジスタ
   118:   public static final int RTC_1_WDAY   = 0x0d;  //0x00e8a00d  bit2-0  アラーム曜日レジスタ
   119:   public static final int RTC_1_MDAY0  = 0x0f;  //0x00e8a00f  bit3-0  アラーム1日カウンタ
   120:   public static final int RTC_1_MDAY1  = 0x11;  //0x00e8a011  bit1-0  アラーム10日カウンタ
   121:   //  12時間計/24時間計セレクタ
   122:   //    0..1。0=12時間計,1=24時間計
   123:   public static final int RTC_1_RULE   = 0x15;  //0x00e8a015  bit0    12時間計/24時間計セレクタ
   124:   //  閏年カウンタ
   125:   //    0..3。0=今年が閏年,1=3年後が閏年,2=2年後が閏年,3=来年が閏年
   126:   //    4年でオーバーフローする年カウンタ
   127:   //    年カウンタと同時に初期化する必要があり、年カウンタと同時にカウントアップする
   128:   //    曜日カウンタと違ってRTC側で0=閏年と決まっている
   129:   //    年カウンタが4の倍数になっているかどうかとは関係なく、閏年カウンタが0のときに2月を29日までカウントする
   130:   public static final int RTC_1_LEAP   = 0x17;  //0x00e8a017  bit1-0  閏年カウンタ
   131: 
   132:   //バンク0/1共通
   133:   //  モードレジスタ
   134:   public static final int RTC_MODE     = 0x1b;  //0x00e8a01b  bit3    0=秒以後のカウンタ停止,1=計時開始
   135:   //                                                          bit2    0=アラーム出力OFF,1=アラーム出力ON
   136:   //                                                          bit0    0=バンク0,1=バンク1
   137:   //  テストレジスタ(write-only)
   138:   //    参考
   139:   //      https://twitter.com/kugimoto0715/status/745570562102046728
   140:   public static final int RTC_TEST     = 0x1d;  //0x00e8a01d  bit3    1=1年カウンタに16384Hzを入力する
   141:   //                                                          bit2    1=1日カウンタに16384Hzを入力する
   142:   //                                                          bit1    1=1分カウンタに16384Hzを入力する
   143:   //                                                          bit0    1=1秒カウンタに16384Hzを入力する
   144:   //  リセットレジスタ(write-only)
   145:   public static final int RTC_RESET    = 0x1f;  //0x00e8a01f  bit3    0=1Hz ON
   146:   //                                                          bit2    0=16Hz ON
   147:   //                                                          bit1    1=秒以前の分周段リセット
   148:   //                                                          bit0    1=アラームリセット
   149: 
   150:   public static int rtcRule;  //0=12時間計,1=24時間計
   151:   public static int rtcMove;  //0=停止中,8=動作中
   152:   public static int rtcAlarm;  //0=アラーム出力OFF,4=アラーム出力ON
   153:   public static int rtcBank;  //0=バンク0,1=バンク1
   154:   public static final byte[] rtcRegBank0 = new byte[32];  //レジスタバンク0
   155:   public static final byte[] rtcRegBank1 = new byte[32];  //レジスタバンク1
   156:   public static byte[] rtcRegCurrent;  //現在選択されているレジスタバンク。rtcBank==0?rtcRegBank0:rtcRegBank1
   157:   public static int rtcTest;  //0=通常動作,1..15=テスト動作のスケール
   158:   public static MemoryMappedDevice rtcFirst;  //rtcTest==0?MemoryMappedDevice.MMD_RTC_FIRST:MemoryMappedDevice.MMD_RTC_TEST
   159:   public static long rtcTestStartCmil;  //現在のスケールでテスト動作を開始したときの日時カウンタの値の歴通ミリ秒
   160:   public static long rtcTestStartTime;  //現在のスケールでテスト動作を開始したときのXEiJ.mpuClockTime
   161: 
   162:   //  rtcWeekGap
   163:   //    曜日のずれ
   164:   //    0..6
   165:   //    これをcdayに加えて7で割った余りが曜日カウンタの値と一致するように調整する
   166:   //    (cday+4)%7==0のとき日曜日なので初期値は4
   167:   //    曜日カウンタに書き込まれたとき増分を加え、日付カウンタに書き込まれたとき増分を引く
   168:   public static int rtcWeekGap;  //曜日のずれ
   169:   //  rtcCmilGap
   170:   //    日時のずれ
   171:   //    rtcCmil-System.currentTimeMillis()
   172:   //    通常動作で動作中のときだけ有効
   173:   //    初期値はローカルタイムを求めるときUTCに加えるオフセット
   174:   //      夏時間を考慮しない場合はTimeZone.getDefault ().getRawOffset()と等しい
   175:   //      JSTのとき1000*60*60*9=32400000
   176:   //    日時カウンタに書き込まれたとき増分にスケールを掛けた値を加える
   177:   //    通常動作で停止中から動作中に移行するとき日時カウンタの値からホストの日時を引いた値を設定する
   178:   public static long rtcInitialCmilGap;  //日時のずれの初期値
   179:   public static long rtcCmilGap;  //日時のずれ
   180:   public static long rtcCmil;  //日時カウンタの値の歴通ミリ秒
   181:   public static int rtcCday;  //日付カウンタの値の歴通日
   182:   public static int rtcDsec;  //時刻カウンタの値の日通秒
   183: 
   184:   //rtcInit ()
   185:   //  RTCを初期化する
   186:   public static void rtcInit () {
   187:     if (RTC_DEBUG_TRACE) {
   188:       System.out.printf ("rtcInit()\n");
   189:     }
   190:     rtcRule = 1;  //24時間計
   191:     rtcMove = 8;  //動作
   192:     rtcAlarm = 0;  //アラーム出力OFF
   193:     rtcHzReset ();
   194:     rtcBank = 0;  //バンク0
   195:     rtcRegCurrent = rtcBank == 0 ? rtcRegBank0 : rtcRegBank1;
   196:     rtcTest = 0;  //通常動作
   197:     rtcFirst = rtcTest == 0 ? MemoryMappedDevice.MMD_RTC_FIRST : MemoryMappedDevice.MMD_RTC_TEST;
   198:     rtcTestStartCmil = 0;
   199:     rtcTestStartTime = 0;
   200:     if (RTC_DEBUG_TRACE) {
   201:       System.out.printf ("  rtcRule=%d\n", rtcRule);
   202:       System.out.printf ("  rtcMove=%d\n", rtcMove);
   203:       System.out.printf ("  rtcBank=%d\n", rtcBank);
   204:       System.out.printf ("  rtcTest=%d\n", rtcTest);
   205:     }
   206:     //  00:00:00
   207:     rtcRegBank0[RTC_0_SECO0] = 0;
   208:     rtcRegBank0[RTC_0_SECO1] = 0;
   209:     rtcRegBank0[RTC_0_MINU0] = 0;
   210:     rtcRegBank0[RTC_0_MINU1] = 0;
   211:     rtcRegBank0[RTC_0_HOUR0] = 0;
   212:     rtcRegBank0[RTC_0_HOUR1] = 0;
   213:     //  1980-01-01
   214:     rtcRegBank0[RTC_0_WDAY] = 2;  //火曜日
   215:     rtcRegBank0[RTC_0_MDAY0] = 1;
   216:     rtcRegBank0[RTC_0_MDAY1] = 0;
   217:     rtcRegBank0[RTC_0_MONT0] = 1;
   218:     rtcRegBank0[RTC_0_MONT1] = 0;
   219:     rtcRegBank0[RTC_0_YEAR0] = 0;
   220:     rtcRegBank0[RTC_0_YEAR1] = 0;
   221:     rtcRegBank1[RTC_1_CLKOUT] = 0;
   222:     rtcRegBank1[RTC_1_ADJUST] = 0;
   223:     rtcRegBank1[RTC_1_MINU0] = 16;
   224:     rtcRegBank1[RTC_1_MINU1] = 16;
   225:     rtcRegBank1[RTC_1_HOUR0] = 16;
   226:     rtcRegBank1[RTC_1_HOUR1] = 16;
   227:     rtcRegBank1[RTC_1_WDAY] = 16;
   228:     rtcRegBank1[RTC_1_MDAY0] = 16;
   229:     rtcRegBank1[RTC_1_MDAY1] = 16;
   230:     rtcRegBank1[RTC_1_RULE] = (byte) rtcRule;
   231:     rtcRegBank1[RTC_1_LEAP] = 0;
   232:     rtcRegBank1[RTC_MODE] = (byte) (rtcMove + rtcBank);
   233:     rtcRegBank1[RTC_TEST] = (byte) rtcTest;
   234:     rtcRegBank1[RTC_RESET] = 0;
   235:     rtcWeekGap = 4;  //曜日のずれ
   236:     rtcInitialCmilGap = TimeZone.getDefault ().getOffset (System.currentTimeMillis ());
   237:     rtcCmilGap = rtcInitialCmilGap;  //日時のずれ
   238:     if (RTC_DEBUG_GAP) {
   239:       System.out.printf ("rtcInit  rtcCmilGap=%d\n", rtcCmilGap);
   240:     }
   241:     rtcCmil = 315532800000L;
   242:     rtcCday = 3652;
   243:     rtcDsec = 0;
   244:     if (RTC_DEBUG_TRACE) {
   245:       System.out.printf ("  rtcWeekGap=%d\n", rtcWeekGap);
   246:       System.out.printf ("  rtcCmilGap=%d\n", rtcCmilGap);
   247:       System.out.printf ("  rtcCmil=%d (%s)\n", rtcCmil, DnT.dntSdttmCmil (rtcCmil));
   248:       System.out.printf ("  rtcCday=%d (%s)\n", rtcCday, DnT.dntSdtCday (rtcCday));
   249:       System.out.printf ("  rtcDsec=%d (%s)\n", rtcDsec, DnT.dntStmDsec (rtcDsec));
   250:       rtcDumpReg ();
   251:       System.out.printf ("  3652=%d\n", DnT.dntCdayYearMontMday (1980, 1, 1));
   252:       System.out.printf ("  40176=%d\n", DnT.dntCdayYearMontMday (2079, 12, 31));
   253:       System.out.printf ("  315532800000=%d\n", DnT.dntCmilYearMontMdayHourMinuSecoMill (1980, 1, 1, 0, 0, 0, 0));
   254:       System.out.printf ("  3471292799999=%d\n", DnT.dntCmilYearMontMdayHourMinuSecoMill (2079, 12, 31, 23, 59, 59, 999));
   255:       System.out.printf ("  1980-01-01=%s\n", DnT.dntSdtCday (3652));
   256:       System.out.printf ("  2079-12-31=%s\n", DnT.dntSdtCday (40176));
   257:       System.out.printf ("  1980-01-01 00:00:00.999=%s\n", DnT.dntSdttmCmil (315532800000L));
   258:       System.out.printf ("  2079-12-31 23:59:59.999=%s\n", DnT.dntSdttmCmil (3471292799999L));
   259:     }
   260:   }  //rtcInit()
   261: 
   262:   //リセット
   263:   public static void rtcReset () {
   264:     if (RTC_DEBUG_TRACE) {
   265:       System.out.printf ("rtcReset()\n");
   266:     }
   267:   }  //rtcReset()
   268: 
   269:   //rtcSetByHost ()
   270:   //  ホストの時計に合わせる
   271:   public static void rtcSetByHost () {
   272:     if (RTC_DEBUG_TRACE) {
   273:       System.out.printf ("rtcSetByHost()\n");
   274:     }
   275:     XEiJ.tmrTimer.schedule (new TimerTask () {
   276:       @Override public void run () {
   277:         if (rtcTest != 0) {
   278:           //テスト動作→通常動作
   279:           XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_FIRST, 0x00e8a000, 0x00e8c000);
   280:           rtcTest = 0;
   281:           rtcFirst = MemoryMappedDevice.MMD_RTC_FIRST;
   282:           if (RTC_DEBUG_TRACE) {
   283:             System.out.printf ("  rtcTest=%d\n", rtcTest);
   284:           }
   285:         }
   286:         if (rtcMove == 0) {
   287:           //停止中→動作中
   288:           rtcMove = 8;
   289:           if (RTC_DEBUG_TRACE) {
   290:             System.out.printf ("  rtcMove=%d\n", rtcMove);
   291:           }
   292:         }
   293:         rtcCmilGap = rtcInitialCmilGap;  //日時のずれ
   294:         if (RTC_DEBUG_GAP) {
   295:           System.out.printf ("rtcSetByHost  rtcCmilGap=%d\n", rtcCmilGap);
   296:         }
   297:         if (RTC_DEBUG_TRACE) {
   298:           System.out.printf ("  rtcCmilGap=%d\n", rtcCmilGap);
   299:         }
   300:         rtcUpdate ();
   301:       }
   302:     }, 10L);
   303:   }  //rtcSetByHost()
   304: 
   305:   public static final long[] RTC_TEST_FREQ = {
   306:     //                                     年       日    分   秒
   307:     0L,                                                               //$00
   308:     XEiJ.TMR_FREQ      / (16384L * (                           1L)),  //$01  1秒カウンタ以上
   309:     XEiJ.TMR_FREQ      / (16384L * (                      1L     )),  //$02  1分カウンタ以上
   310:     XEiJ.TMR_FREQ      / (16384L * (                     60L + 1L)),  //$03  1秒カウンタ以上
   311:     XEiJ.TMR_FREQ      / (16384L * (                1L           )),  //$04  1日カウンタ以上
   312:     XEiJ.TMR_FREQ      / (16384L * (            86400L +       1L)),  //$05  1秒カウンタ以上
   313:     XEiJ.TMR_FREQ      / (16384L * (             1440L +  1L     )),  //$06  1分カウンタ以上
   314:     XEiJ.TMR_FREQ      / (16384L * (            86400L + 60L + 1L)),  //$07  1秒カウンタ以上
   315:     XEiJ.TMR_FREQ      / (16384L * (       1L                    )),  //$08  1年カウンタ以上
   316:     XEiJ.TMR_FREQ      / (16384L * (31557600L +                1L)),  //$09  1秒カウンタ以上
   317:     XEiJ.TMR_FREQ      / (16384L * (  525960L +           1L     )),  //$0A  1分カウンタ以上
   318:     XEiJ.TMR_FREQ      / (16384L * (31557600L +          60L + 1L)),  //$0B  1秒カウンタ以上
   319:     XEiJ.TMR_FREQ * 4L / (16384L * (    1461L +     4L           )),  //$0C  1日カウンタ以上。365.25+1=1461/4+1=(1461+4)/4
   320:     XEiJ.TMR_FREQ      / (16384L * (31557600L + 86400L +       1L)),  //$0D  1秒カウンタ以上
   321:     XEiJ.TMR_FREQ      / (16384L * (  525960L +  1440L +  1L     )),  //$0E  1分カウンタ以上
   322:     XEiJ.TMR_FREQ      / (16384L * (31557600L + 86400L + 60L + 1L)),  //$0F  1秒カウンタ以上
   323:   };
   324:   public static final long[] RTC_TEST_SCALE = {
   325:     //         年          日       分      秒
   326:     0L,                                           //$00
   327:     (                                    1000L),  //$01  1秒カウンタ以上
   328:     (                           60000L        ),  //$02  1分カウンタ以上
   329:     (                                    1000L),  //$03  1秒カウンタ以上
   330:     (               86400000L                 ),  //$04  1日カウンタ以上
   331:     (                                    1000L),  //$05  1秒カウンタ以上
   332:     (                           60000L        ),  //$06  1分カウンタ以上
   333:     (                                    1000L),  //$07  1秒カウンタ以上
   334:     (31557600000L                             ),  //$08  1年カウンタ以上
   335:     (                                    1000L),  //$09  1秒カウンタ以上
   336:     (                           60000L        ),  //$0A  1分カウンタ以上
   337:     (                                    1000L),  //$0B  1秒カウンタ以上
   338:     (               86400000L                 ),  //$0C  1日カウンタ以上
   339:     (                                    1000L),  //$0D  1秒カウンタ以上
   340:     (                           60000L        ),  //$0E  1分カウンタ以上
   341:     (                                    1000L),  //$0F  1秒カウンタ以上
   342:   };
   343: 
   344:   //rtcTestUpdate ()
   345:   //  カウンタを更新する(テスト動作)
   346:   public static void rtcTestUpdate () {
   347:     if (RTC_DEBUG_TRACE) {
   348:       System.out.printf ("rtcTestUpdate()\n");
   349:     }
   350:     if (rtcMove != 0) {  //動作中
   351:       rtcSetCmil (rtcTestStartCmil +
   352:                   ((XEiJ.mpuClockTime - rtcTestStartTime) / RTC_TEST_FREQ[rtcTest]) * RTC_TEST_SCALE[rtcTest]);
   353:     }
   354:   }  //rtcTestUpdate()
   355: 
   356:   //rtcUpdate ()
   357:   //  カウンタを更新する(通常動作)
   358:   //  mpuTask.run()で最初にカウンタがアクセスされたときに呼び出す
   359:   //    00100000  42A7                  clr.l   -(sp)                       Bァ
   360:   //    00100002  FF20                  DOS     _SUPER                      . 
   361:   //    00100004  4A3900E8A001          tst.b   $00E8A001.l                 J9.陟.
   362:   //    0010000A  60F8                  bra.s   $00100004                   `.
   363:   //  mew 100000 42a7 ff20 4a39 00e8 a001 60f8
   364:   //  g=100000
   365:   public static void rtcUpdate () {
   366:     if (RTC_DEBUG_TRACE) {
   367:       System.out.printf ("rtcUpdate()\n");
   368:       XEiJ.prgPrintStackTrace ();
   369:     }
   370:     if (rtcMove != 0) {  //動作中
   371:       rtcSetCmil (System.currentTimeMillis () + rtcCmilGap);
   372:     }
   373:   }  //rtcUpdate()
   374: 
   375:   //rtcSetCmil (cmil)
   376:   //  カウンタに歴通ミリ秒を設定する
   377:   public static void rtcSetCmil (long cmil) {
   378:     if (RTC_DEBUG_TRACE) {
   379:       System.out.printf ("rtcSetCmil(%d)\n", cmil);
   380:     }
   381:     //               日時               cday       cmil
   382:     //  1980-01-01 (Tue) 00:00:00.000   3652   315532800000
   383:     //  2079-12-31 (Sun) 23:59:59.999  40176  3471292799999
   384:     //  2080-01-01 (Mon) 00:00:00.000  40177  3471292800000
   385:     long shift = DnT.dntFdiv (cmil - 315532800000L, 3471292800000L - 315532800000L) * (3471292800000L - 315532800000L);
   386:     if (shift != 0L) {
   387:       cmil -= shift;  //315532800000..3471292799999
   388:       rtcCmilGap -= shift;
   389:       if (RTC_DEBUG_GAP) {
   390:         System.out.printf ("rtcSetCmil  rtcCmilGap=%d\n", rtcCmilGap);
   391:       }
   392:       if (RTC_DEBUG_TRACE) {
   393:         System.out.printf ("  rtcCmilGap=%d\n", rtcCmilGap);
   394:       }
   395:     }
   396:     if (rtcCmil != cmil) {
   397:       rtcCmil = cmil;  //315532800000..3471292799999
   398:       if (RTC_DEBUG_TRACE) {
   399:         System.out.printf ("  rtcCmil=%d (%s)\n", rtcCmil, DnT.dntSdttmCmil (rtcCmil));
   400:       }
   401:       int cday = DnT.dntCdayCmil (cmil);
   402:       int dsec = DnT.dntDsecCmil (cmil);
   403:       if (rtcCday != cday) {
   404:         rtcCday = cday;  //3652..40176
   405:         if (RTC_DEBUG_TRACE) {
   406:           System.out.printf ("  rtcCday=%d (%s)\n", rtcCday, DnT.dntSdtCday (rtcCday));
   407:         }
   408:         int date = DnT.dntDateCday (cday);  //1980-01-01..2079-12-31
   409:         int t = XEiJ.FMT_BCD4[DnT.dntYearDate (date) - 1980];  //year=00..99
   410:         rtcRegBank0[RTC_0_YEAR1] = (byte) (t >> 4);
   411:         rtcRegBank0[RTC_0_YEAR0] = (byte) (t & 15);
   412:         rtcRegBank1[RTC_1_LEAP] = (byte) (t & 3);
   413:         t = XEiJ.FMT_BCD4[DnT.dntMontDate (date)];  //mont=01..12
   414:         rtcRegBank0[RTC_0_MONT1] = (byte) (t >> 4);
   415:         rtcRegBank0[RTC_0_MONT0] = (byte) (t & 15);
   416:         t = XEiJ.FMT_BCD4[DnT.dntMdayDate (date)];  //mday=01..31
   417:         rtcRegBank0[RTC_0_MDAY1] = (byte) (t >> 4);
   418:         rtcRegBank0[RTC_0_MDAY0] = (byte) (t & 15);
   419:         rtcRegBank0[RTC_0_WDAY] = (byte) DnT.dntFrem (cday + rtcWeekGap, 7);
   420:         if (RTC_DEBUG_TRACE) {
   421:           rtcDumpReg ();
   422:         }
   423:       }  //if rtcCday!=cday
   424:       if (rtcDsec != dsec) {
   425:         rtcDsec = dsec;  //0..86399
   426:         if (RTC_DEBUG_TRACE) {
   427:           System.out.printf ("  rtcDsec=%d (%s)\n", rtcDsec, DnT.dntStmDsec (rtcDsec));
   428:         }
   429:         int time = DnT.dntTimeDsec (dsec);
   430:         int t = DnT.dntHourTime (time);
   431:         t = XEiJ.FMT_BCD4[rtcRule == 0 && 12 <= t ? t + 8 : t];  //hour=00..31
   432:         rtcRegBank0[RTC_0_HOUR1] = (byte) (t >> 4);
   433:         rtcRegBank0[RTC_0_HOUR0] = (byte) (t & 15);
   434:         t = XEiJ.FMT_BCD4[DnT.dntMinuTime (time)];  //minu=00..63
   435:         rtcRegBank0[RTC_0_MINU1] = (byte) (t >> 4);
   436:         rtcRegBank0[RTC_0_MINU0] = (byte) (t & 15);
   437:         t = XEiJ.FMT_BCD4[DnT.dntSecoTime (time)];  //seco=00..63
   438:         rtcRegBank0[RTC_0_SECO1] = (byte) (t >> 4);
   439:         rtcRegBank0[RTC_0_SECO0] = (byte) (t & 15);
   440:         if (RTC_DEBUG_TRACE) {
   441:           rtcDumpReg ();
   442:         }
   443:       }  //if rtcDsec!=dsec
   444:     }  //if rtcCmil!=cmil
   445:   }  //rtcSetCmil(long)
   446: 
   447:   //rtcDumpReg ()
   448:   public static void rtcDumpReg () {
   449:     System.out.printf ("  L YY-MM-DD W  R HH:MM:SS\n" +
   450:                        "  %01X %01X%01X-%01X%01X-%01X%01X %01X  %01X %01X%01X:%01X%01X:%01X%01X\n",
   451:                        rtcRegBank1[RTC_1_LEAP],
   452:                        rtcRegBank0[RTC_0_YEAR1], rtcRegBank0[RTC_0_YEAR0],
   453:                        rtcRegBank0[RTC_0_MONT1], rtcRegBank0[RTC_0_MONT0],
   454:                        rtcRegBank0[RTC_0_MDAY1], rtcRegBank0[RTC_0_MDAY0],
   455:                        rtcRegBank0[RTC_0_WDAY],
   456:                        rtcRegBank1[RTC_1_RULE],
   457:                        rtcRegBank0[RTC_0_HOUR1], rtcRegBank0[RTC_0_HOUR0],
   458:                        rtcRegBank0[RTC_0_MINU1], rtcRegBank0[RTC_0_MINU0],
   459:                        rtcRegBank0[RTC_0_SECO1], rtcRegBank0[RTC_0_SECO0]);
   460:   }  //rtcDumpReg()
   461: 
   462:   //rtcAddSeco (seco)
   463:   public static void rtcAddSeco (int seco) {
   464:     if (RTC_DEBUG_TRACE) {
   465:       System.out.printf ("rtcAddSeco(%d)\n", seco);
   466:     }
   467:     long mill = (long) seco * 1000L;
   468:     rtcCmilGap += mill;
   469:     if (RTC_DEBUG_GAP) {
   470:       System.out.printf ("rtcAddSeco  rtcCmilGap=%d\n", rtcCmilGap);
   471:     }
   472:     rtcCmil += mill;
   473:     rtcCday = DnT.dntCdayCmil (rtcCmil);
   474:     rtcDsec = DnT.dntDsecCmil (rtcCmil);
   475:     if (RTC_DEBUG_TRACE) {
   476:       System.out.printf ("  rtcCmilGap=%d\n", rtcCmilGap);
   477:       System.out.printf ("  rtcCmil=%d (%s)\n", rtcCmil, DnT.dntSdttmCmil (rtcCmil));
   478:       System.out.printf ("  rtcCday=%d (%s)\n", rtcCday, DnT.dntSdtCday (rtcCday));
   479:       System.out.printf ("  rtcDsec=%d (%s)\n", rtcDsec, DnT.dntStmDsec (rtcDsec));
   480:     }
   481:     if (rtcTest != 0) {
   482:       rtcTestStartCmil = rtcCmil;
   483:       rtcTestStartTime = XEiJ.mpuClockTime;
   484:       if (RTC_DEBUG_TRACE) {
   485:         System.out.printf ("  rtcTestStartCmil=%d (%s)\n", rtcTestStartCmil, DnT.dntSdttmCmil (rtcTestStartCmil));
   486:         System.out.printf ("  rtcTestStartTime=%d\n", rtcTestStartTime);
   487:       }
   488:     }
   489:   }  //rtcAddSeco(int)
   490: 
   491:   //rtcAddMday (mday)
   492:   public static void rtcAddMday (int mday) {
   493:     if (RTC_DEBUG_TRACE) {
   494:       System.out.printf ("rtcAddMday(%d)\n", mday);
   495:     }
   496:     rtcAddSeco (mday * 86400);
   497:     rtcWeekGap = DnT.dntFrem (rtcWeekGap - mday, 7);
   498:     if (RTC_DEBUG_TRACE) {
   499:       System.out.printf ("  rtcWeekGap=%d\n", rtcWeekGap);
   500:     }
   501:   }  //rtcAddMday(int)
   502: 
   503:   //rtcPeekByte (a)
   504:   //  ピークバイト
   505:   public static int rtcPeekByte (int a) {
   506:     int d = rtcRegCurrent[a & 31] & 15;
   507:     if (RTC_DEBUG_TRACE) {
   508:       System.out.printf ("rtcPeekByte(0x%08x)=0x%02x\n", a, d & 255);
   509:     }
   510:     return d;
   511:   }  //rtcPeekByte(int)
   512: 
   513:   //rtcReadByte (a)
   514:   //  リードバイト
   515:   public static int rtcReadByte (int a) {
   516:     int d = rtcRegCurrent[a & 31] & 15;
   517:     if (RTC_DEBUG_TRACE) {
   518:       System.out.printf ("rtcReadByte(0x%08x)=0x%02x\n", a, d & 255);
   519:     }
   520:     return d;
   521:   }  //rtcReadByte(int)
   522: 
   523:   public static final String[] RTC_REG_NAME = (
   524:     "one second," +         //0 $01 1秒カウンタ
   525:     "ten seconds," +        //0 $03 10秒カウンタ
   526:     "one minute," +         //0 $05 1分カウンタ
   527:     "ten minutes," +        //0 $07 10分カウンタ
   528:     "one hour," +           //0 $09 1時カウンタ
   529:     "ten hours," +          //0 $0B 10時カウンタ
   530:     "day of week," +        //0 $0D 曜日カウンタ
   531:     "one day," +            //0 $0F 1日カウンタ
   532:     "ten days," +           //0 $11 10日カウンタ
   533:     "one month," +          //0 $13 1月カウンタ
   534:     "ten months," +         //0 $15 10月カウンタ
   535:     "one year," +           //0 $17 1年カウンタ
   536:     "ten years," +          //0 $19 10年カウンタ
   537:     "mode," +               //0 $1B モードレジスタ
   538:     "test," +               //0 $1D テストレジスタ(write-only)
   539:     "reset," +              //0 $1F リセットレジスタ(write-only)
   540:     "clkout," +             //1 $01 CLKOUTセレクタ
   541:     "adjust," +             //1 $03 アジャスト
   542:     "alarm one minute," +   //1 $05 アラーム1分レジスタ
   543:     "alarm ten minutes," +  //1 $07 アラーム10分レジスタ
   544:     "alarm one hour," +     //1 $09 アラーム1時レジスタ
   545:     "alarm ten hours," +    //1 $0B アラーム10時レジスタ
   546:     "alarm day of week," +  //1 $0D アラーム曜日レジスタ
   547:     "alarm one day," +      //1 $0F アラーム1日レジスタ
   548:     "alarm ten days," +     //1 $11 アラーム10日レジスタ
   549:     "," +                   //1 $13
   550:     "twenty four," +        //1 $15 12時間計/24時間計セレクタ
   551:     "leap year," +          //1 $17 閏年カウンタ
   552:     "," +                   //1 $19
   553:     "mode," +               //1 $1B モードレジスタ
   554:     "test," +               //1 $1D テストレジスタ(write-only)
   555:     "reset"                 //1 $1F リセットレジスタ(write-only)
   556:     ).split (",");
   557: 
   558:   //rtcWriteByte (a, d)
   559:   //  ライトバイト
   560:   public static void rtcWriteByte (int a, int d) {
   561:     if (RTC_DEBUG_TRACE ||
   562:         (RTC_DEBUG_WRITE &&
   563:          (a & 31) != RTC_MODE && (byte) (d & (8 + 4)) != rtcMove + rtcAlarm)) {  //バンク切り替えを除くすべての書き込み
   564:       System.out.printf ("%08X rtcWriteByte@%d(0x%08x(%s),0x%02x)\n",
   565:                          XEiJ.regPC0, rtcBank, a,
   566:                          (a & 1) == 0 ? "" : RTC_REG_NAME[rtcBank << 4 | ((a & 31) >> 1)],
   567:                          d & 255);
   568:     }
   569:     switch (rtcBank << 5 | a & 31) {
   570:       //バンク0
   571:     case 0 << 5 | RTC_0_SECO0:  //0x00e8a001  bit3-0  1秒カウンタ
   572:       d &= 15;  //0..15
   573:       rtcAddSeco (d - rtcRegBank0[RTC_0_SECO0]);
   574:       rtcRegBank0[RTC_0_SECO0] = (byte) d;
   575:       if (RTC_DEBUG_TRACE) {
   576:         rtcDumpReg ();
   577:       }
   578:       return;
   579:     case 0 << 5 | RTC_0_SECO1:  //0x00e8a003  bit2-0  10秒カウンタ
   580:       d &= 7;  //0..7
   581:       rtcAddSeco ((d - rtcRegBank0[RTC_0_SECO1]) * 10);
   582:       rtcRegBank0[RTC_0_SECO1] = (byte) d;
   583:       if (RTC_DEBUG_TRACE) {
   584:         rtcDumpReg ();
   585:       }
   586:       return;
   587:     case 0 << 5 | RTC_0_MINU0:  //0x00e8a005  bit3-0  1分カウンタ
   588:       d &= 15;  //0..15
   589:       rtcAddSeco ((d - rtcRegBank0[RTC_0_MINU0]) * 60);
   590:       rtcRegBank0[RTC_0_MINU0] = (byte) d;
   591:       if (RTC_DEBUG_TRACE) {
   592:         rtcDumpReg ();
   593:       }
   594:       return;
   595:     case 0 << 5 | RTC_0_MINU1:  //0x00e8a007  bit2-0  10分カウンタ
   596:       d &= 7;  //0..7
   597:       rtcAddSeco ((d - rtcRegBank0[RTC_0_MINU1]) * 600);
   598:       rtcRegBank0[RTC_0_MINU1] = (byte) d;
   599:       if (RTC_DEBUG_TRACE) {
   600:         rtcDumpReg ();
   601:       }
   602:       return;
   603:     case 0 << 5 | RTC_0_HOUR0:  //0x00e8a009  bit3-0  1時カウンタ
   604:       d &= 15;  //0..15
   605:       rtcAddSeco ((d - rtcRegBank0[RTC_0_HOUR0]) * 3600);
   606:       rtcRegBank0[RTC_0_HOUR0] = (byte) d;
   607:       if (RTC_DEBUG_TRACE) {
   608:         rtcDumpReg ();
   609:       }
   610:       return;
   611:     case 0 << 5 | RTC_0_HOUR1:  //0x00e8a00b  bit1-0  10時カウンタ
   612:       d &= 3;  //0..3
   613:       rtcAddSeco (((d >> 1) - (rtcRegBank0[RTC_0_HOUR1] >> 1)) * (rtcRule == 0 ? 43200 : 72000) +
   614:                   ((d & 1) - (rtcRegBank0[RTC_0_HOUR1] & 1)) * 36000);
   615:       rtcRegBank0[RTC_0_HOUR1] = (byte) d;
   616:       if (RTC_DEBUG_TRACE) {
   617:         rtcDumpReg ();
   618:       }
   619:       return;
   620:     case 0 << 5 | RTC_0_WDAY:  //0x00e8a00d  bit2-0  曜日カウンタ
   621:       d &= 7;  //0..7
   622:       {
   623:         int t = rtcWeekGap + d - rtcRegBank0[RTC_0_WDAY] + 7;  //1..20
   624:         //perl optdiv.pl 20 7
   625:         //  x/7==x*19>>>7 (0<=x<=26) [20*19==380]
   626:         rtcWeekGap = t - (t * 19 >>> 7) * 7;  //0..6
   627:         rtcRegBank0[RTC_0_WDAY] = (byte) d;
   628:         if (RTC_DEBUG_TRACE) {
   629:           System.out.printf ("  rtcWeekGap=%d\n", rtcWeekGap);
   630:           rtcDumpReg ();
   631:         }
   632:       }
   633:       return;
   634:     case 0 << 5 | RTC_0_MDAY0:  //0x00e8a00f  bit3-0  1日カウンタ
   635:       d &= 15;  //0..15
   636:       rtcAddMday (d - rtcRegBank0[RTC_0_MDAY0]);
   637:       rtcRegBank0[RTC_0_MDAY0] = (byte) d;
   638:       if (RTC_DEBUG_TRACE) {
   639:         rtcDumpReg ();
   640:       }
   641:       return;
   642:     case 0 << 5 | RTC_0_MDAY1:  //0x00e8a011  bit1-0  10日カウンタ
   643:       d &= 3;  //0..3
   644:       rtcAddMday ((d - rtcRegBank0[RTC_0_MDAY1]) * 10);
   645:       rtcRegBank0[RTC_0_MDAY1] = (byte) d;
   646:       if (RTC_DEBUG_TRACE) {
   647:         rtcDumpReg ();
   648:       }
   649:       return;
   650:     case 0 << 5 | RTC_0_MONT0:  //0x00e8a013  bit3-0  1月カウンタ
   651:       d &= 15;  //0..15
   652:       rtcAddMday (DnT.dntCdayYearMontMday (1980 + rtcRegBank0[RTC_0_YEAR1] * 10 + rtcRegBank0[RTC_0_YEAR0],
   653:                                            rtcRegBank0[RTC_0_MONT1] * 10 + d,
   654:                                            rtcRegBank0[RTC_0_MDAY1] * 10 + rtcRegBank0[RTC_0_MDAY0]) - rtcCday);
   655:       rtcRegBank0[RTC_0_MONT0] = (byte) d;
   656:       if (RTC_DEBUG_TRACE) {
   657:         rtcDumpReg ();
   658:       }
   659:       return;
   660:     case 0 << 5 | RTC_0_MONT1:  //0x00e8a015  bit0  10月カウンタ
   661:       d &= 1;  //0..1
   662:       rtcAddMday (DnT.dntCdayYearMontMday (1980 + rtcRegBank0[RTC_0_YEAR1] * 10 + rtcRegBank0[RTC_0_YEAR0],
   663:                                            d * 10 + rtcRegBank0[RTC_0_MONT0],
   664:                                            rtcRegBank0[RTC_0_MDAY1] * 10 + rtcRegBank0[RTC_0_MDAY0]) - rtcCday);
   665:       rtcRegBank0[RTC_0_MONT1] = (byte) d;
   666:       if (RTC_DEBUG_TRACE) {
   667:         rtcDumpReg ();
   668:       }
   669:       return;
   670:     case 0 << 5 | RTC_0_YEAR0:  //0x00e8a017  bit3-0  1年カウンタ
   671:       d &= 15;  //0..15
   672:       rtcAddMday (DnT.dntCdayYearMontMday (1980 + rtcRegBank0[RTC_0_YEAR1] * 10 + d,
   673:                                            rtcRegBank0[RTC_0_MONT1] * 10 + rtcRegBank0[RTC_0_MONT0],
   674:                                            rtcRegBank0[RTC_0_MDAY1] * 10 + rtcRegBank0[RTC_0_MDAY0]) - rtcCday);
   675:       rtcRegBank0[RTC_0_YEAR0] = (byte) d;
   676:       rtcRegBank1[RTC_1_LEAP] = (byte) (rtcRegBank0[RTC_0_YEAR1] * 10 + d & 3);
   677:       if (RTC_DEBUG_TRACE) {
   678:         rtcDumpReg ();
   679:       }
   680:       return;
   681:     case 0 << 5 | RTC_0_YEAR1:  //0x00e8a019  bit3-0  10年カウンタ
   682:       d &= 15;  //0..15
   683:       rtcAddMday (DnT.dntCdayYearMontMday (1980 + d * 10 + rtcRegBank0[RTC_0_YEAR0],
   684:                                            rtcRegBank0[RTC_0_MONT1] * 10 + rtcRegBank0[RTC_0_MONT0],
   685:                                            rtcRegBank0[RTC_0_MDAY1] * 10 + rtcRegBank0[RTC_0_MDAY0]) - rtcCday);
   686:       rtcRegBank0[RTC_0_YEAR1] = (byte) d;
   687:       rtcRegBank1[RTC_1_LEAP] = (byte) (d * 10 + rtcRegBank0[RTC_0_YEAR0] & 3);
   688:       if (RTC_DEBUG_TRACE) {
   689:         rtcDumpReg ();
   690:       }
   691:       return;
   692:       //バンク1
   693:     case 1 << 5 | RTC_1_CLKOUT:  //0x00e8a001  bit2-0  CLKOUTセレクタ
   694:       //! 未対応。TIMER-LED
   695:       return;
   696:     case 1 << 5 | RTC_1_ADJUST:  //0x00e8a003  bit0    アジャスト
   697:       //! 未対応。アジャスト
   698:       return;
   699:       //case 1 << 5 | RTC_1_MINU0:  //0x00e8a005  bit3-0  アラーム1分レジスタ
   700:       //  return;
   701:       //case 1 << 5 | RTC_1_MINU1:  //0x00e8a007  bit2-0  アラーム10分レジスタ
   702:       //  return;
   703:       //case 1 << 5 | RTC_1_HOUR0:  //0x00e8a009  bit3-0  アラーム1時レジスタ
   704:       //  return;
   705:       //case 1 << 5 | RTC_1_HOUR1:  //0x00e8a00b  bit2-0  アラーム10時レジスタ
   706:       //  return;
   707:       //case 1 << 5 | RTC_1_WDAY:  //0x00e8a00d  bit2-0  アラーム曜日レジスタ
   708:       //  return;
   709:       //case 1 << 5 | RTC_1_MDAY0:  //0x00e8a00f  bit3-0  アラーム1日カウンタ
   710:       //  return;
   711:       //case 1 << 5 | RTC_1_MDAY1:  //0x00e8a011  bit1-0  アラーム10日カウンタ
   712:       //  return;
   713:     case 1 << 5 | RTC_1_RULE:  //0x00e8a015  bit0    12時間計/24時間計セレクタ
   714:       d &= 1;
   715:       if (rtcRule != d) {
   716:         //12時カウンタ/20時カウンタの値を変えずに解釈を変更する
   717:         rtcAddSeco ((rtcRegBank0[RTC_0_HOUR1] >> 1) *
   718:                     ((d == 0 ? 43200 : 72000) - (rtcRule == 0 ? 43200 : 72000)));
   719:         rtcRegBank1[RTC_1_RULE] = (byte) d;
   720:         rtcRule = d;
   721:         if (RTC_DEBUG_TRACE) {
   722:           System.out.printf ("  rtcRule=%d\n", rtcRule);
   723:           rtcDumpReg ();
   724:         }
   725:       }
   726:       return;
   727:     case 1 << 5 | RTC_1_LEAP:  //0x00e8a017  bit1-0  閏年カウンタ
   728:       //何もしない
   729:       return;
   730:       //共通
   731:     case 0 << 5 | RTC_MODE:  //0x00e8a01b  モードレジスタ
   732:     case 1 << 5 | RTC_MODE:  //0x00e8a01b  モードレジスタ
   733:       d &= 8 + 4 + 1;
   734:       rtcBank = d & 1;
   735:       if (RTC_DEBUG_TRACE) {
   736:         System.out.printf ("  rtcBank=%d\n", rtcBank);
   737:       }
   738:       rtcRegCurrent = rtcBank == 0 ? rtcRegBank0 : rtcRegBank1;
   739:       if (rtcMove < (d & 8)) {  //停止中→動作中
   740:         if (rtcTest == 0) {  //通常動作
   741:           rtcCmilGap = rtcCmil - System.currentTimeMillis ();
   742:           rtcCmilGap = rtcCmilGap < 0L ? -(((-rtcCmilGap + 500L) / 1000L) * 1000L) : ((rtcCmilGap + 500L) / 1000L) * 1000L;  //秒単位に丸める
   743:           if (RTC_DEBUG_GAP) {
   744:             System.out.printf ("rtcWriteByte  rtcCmilGap=%d\n", rtcCmilGap);
   745:           }
   746:           if (RTC_DEBUG_TRACE) {
   747:             System.out.printf ("  rtcCmilGap=%d\n", rtcCmilGap);
   748:           }
   749:         } else {  //テスト動作
   750:           rtcTestStartCmil = rtcCmil;
   751:           rtcTestStartTime = XEiJ.mpuClockTime;
   752:           if (RTC_DEBUG_TRACE) {
   753:             System.out.printf ("  rtcTestStartCmil=%d (%s)\n", rtcTestStartCmil, DnT.dntSdttmCmil (rtcTestStartCmil));
   754:             System.out.printf ("  rtcTestStartTime=%d\n", rtcTestStartTime);
   755:           }
   756:         }
   757:       }
   758:       rtcAlarm = d & 4;
   759:       rtcHzUpdate ();
   760:       rtcMove = d & 8;
   761:       if (RTC_DEBUG_TRACE) {
   762:         System.out.printf ("  rtcMove=%d\n", rtcMove);
   763:       }
   764:       rtcRegBank0[RTC_MODE] = rtcRegBank1[RTC_MODE] = (byte) d;
   765:       return;
   766:     case 0 << 5 | RTC_TEST:  //0x00e8a01d  テストレジスタ
   767:     case 1 << 5 | RTC_TEST:  //0x00e8a01d  テストレジスタ
   768:       d &= 15;
   769:       if (rtcTest != d) {
   770:         if (d == 0) {  //テスト動作→通常動作
   771:           XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_FIRST, 0x00e8a000, 0x00e8c000);
   772:           if (rtcMove != 0) {  //動作中
   773:             rtcCmilGap = rtcCmil - System.currentTimeMillis ();
   774:             rtcCmilGap = rtcCmilGap < 0L ? -(((-rtcCmilGap + 500L) / 1000L) * 1000L) : ((rtcCmilGap + 500L) / 1000L) * 1000L;  //秒単位に丸める
   775:             if (RTC_DEBUG_GAP) {
   776:               System.out.printf ("rtcWriteByte  rtcCmilGap=%d\n", rtcCmilGap);
   777:             }
   778:             if (RTC_DEBUG_TRACE) {
   779:               System.out.printf ("  rtcCmilGap=%d\n", rtcCmilGap);
   780:             }
   781:           }
   782:         } else if (rtcTest == 0) {  //通常動作→テスト動作
   783:           XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_TEST, 0x00e8a000, 0x00e8c000);
   784:           if (rtcMove != 0) {  //動作中
   785:             rtcTestStartCmil = rtcCmil;
   786:             rtcTestStartTime = XEiJ.mpuClockTime;
   787:             if (RTC_DEBUG_TRACE) {
   788:               System.out.printf ("  rtcTestStartCmil=%d (%s)\n", rtcTestStartCmil, DnT.dntSdttmCmil (rtcTestStartCmil));
   789:               System.out.printf ("  rtcTestStartTime=%d\n", rtcTestStartTime);
   790:             }
   791:           }
   792:         } else {  //テスト動作→テスト動作
   793:           if (rtcMove != 0) {  //動作中
   794:             rtcTestStartCmil = rtcCmil;
   795:             rtcTestStartTime = XEiJ.mpuClockTime;
   796:             if (RTC_DEBUG_TRACE) {
   797:               System.out.printf ("  rtcTestStartCmil=%d (%s)\n", rtcTestStartCmil, DnT.dntSdttmCmil (rtcTestStartCmil));
   798:               System.out.printf ("  rtcTestStartTime=%d\n", rtcTestStartTime);
   799:             }
   800:           }
   801:         }
   802:         rtcTest = d;
   803:         rtcFirst = rtcTest == 0 ? MemoryMappedDevice.MMD_RTC_FIRST : MemoryMappedDevice.MMD_RTC_TEST;
   804:         if (RTC_DEBUG_TRACE) {
   805:           System.out.printf ("  rtcTest=%d\n", rtcTest);
   806:         }
   807:         rtcRegBank0[RTC_TEST] = rtcRegBank1[RTC_TEST] = (byte) d;
   808:       }
   809:       return;
   810:     case 0 << 5 | RTC_RESET:  //0x00e8a01f  リセットコントローラ
   811:     case 1 << 5 | RTC_RESET:  //0x00e8a01f  リセットコントローラ
   812:       rtcResetHz = d & 12;
   813:       rtcHzUpdate ();
   814:       return;
   815:     }
   816:   }  //rtcWriteByte(int,int)
   817: 
   818: 
   819:   //1Hz/16Hz
   820:   //  MODE bit2=0でALARMを無効にするとALARM端子から1Hz/16Hzが出力される
   821:   //  RESET bit3=0は1Hz、bit2=0は16Hz。両方0のとき(正論理のGPIP0で見ると)AND
   822:   //  RTCのALARMは負論理だがMFPに入る時点で正論理なのでここでは正論理で作る
   823:   //  MODE bit2=0     0s                                                              1s
   824:   //  RESET bit3-2=00 ┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐                                ┌┐
   825:   //       1Hz & 16Hz ┘└┘└┘└┘└┘└┘└┘└┘└────────────────┘└
   826:   //  RESET bit3-2=01 ┌───────────────┐                              ┌─
   827:   //              1Hz ┘                              └───────────────┘
   828:   //  RESET bit3-2=10 ┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐
   829:   //             16Hz ┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└
   830:   //  RESET bit3-2=11 
   831:   //              Low ──────────────────────────────────
   832:   //                   0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30  32
   833:   //                     1   3   5   7   9  11  13  15  17  19  21  23  25  27  29  31  33
   834:   //  hzMode
   835:   //  000  1Hz & 16Hz
   836:   //  001  1Hz
   837:   //  010  16Hz
   838:   //  011  Low
   839:   //  1xx  Low
   840:   public static final long RTC_HZ_UNIT = XEiJ.TMR_FREQ / 32;  //(1/32)s
   841:   public static int rtcResetHz;  //RESET bit3-2
   842:   public static int rtcHzMode;  //1Hz/16Hzモード。modeAlarm|resetHz>>2
   843:   public static boolean rtcHzLowHigh;  //false=Low,true=High
   844:   public static int rtcHzStage;  //0~31
   845:   public static long rtcHzTime;  //Tickerの実行時刻
   846:   public static void rtcHzReset () {
   847:     rtcResetHz = 12;  //Low
   848:     rtcHzMode = -1;  //未初期化
   849:     rtcHzLowHigh = false;  //Low
   850:     rtcHzStage = -1;
   851:     rtcHzTime = XEiJ.FAR_FUTURE;
   852:   }  //rtcHzReset
   853:   public static final TickerQueue.Ticker RTC_HZ_TICKER = new TickerQueue.Ticker () {
   854:     @Override protected void tick () {
   855:       rtcHzLowHigh = !rtcHzLowHigh;
   856:       if (rtcHzLowHigh) {  //山の始まり
   857:         MC68901.mfpAlarmRise ();
   858:       } else {  //谷の始まり
   859:         MC68901.mfpAlarmFall ();
   860:       }
   861:       int length = (rtcHzMode == 0 ? rtcHzStage < 15 ? 1 : 17 :
   862:                     rtcHzMode == 1 ? 16 :
   863:                     1);  //山または谷の長さ
   864:       rtcHzStage += length;
   865:       if (rtcHzStage == 32) {
   866:         rtcHzStage = 0;
   867:       }
   868:       rtcHzTime += RTC_HZ_UNIT * length;
   869:       TickerQueue.tkqAdd (RTC_HZ_TICKER, rtcHzTime);
   870:     }  //tick
   871:   };  //RTC_HZ_TICKER
   872:   public static void rtcHzUpdate () {
   873:     int hzMode = rtcAlarm | rtcResetHz >> 2;
   874:     if (rtcHzMode != hzMode) {
   875:       rtcHzMode = hzMode;
   876:       if (rtcHzMode < 3) {  //1Hz/16Hz出力あり
   877:         if (!rtcHzLowHigh) {
   878:           rtcHzLowHigh = true;
   879:           MC68901.mfpAlarmRise ();
   880:         }
   881:         rtcHzStage = (rtcHzMode == 0 ||
   882:                       rtcHzMode == 2 ? 1 :
   883:                       16);  //次のステージ=最初の山の長さ
   884:         rtcHzTime = XEiJ.mpuClockTime + RTC_HZ_UNIT * rtcHzStage;
   885:         TickerQueue.tkqAdd (RTC_HZ_TICKER, rtcHzTime);
   886:       } else {  //1Hz/16Hz出力なし
   887:         if (rtcHzLowHigh) {
   888:           rtcHzLowHigh = false;
   889:           MC68901.mfpAlarmFall ();
   890:         }
   891:         rtcHzStage = -1;
   892:         rtcHzTime = XEiJ.FAR_FUTURE;
   893:         TickerQueue.tkqRemove (RTC_HZ_TICKER);
   894:       }
   895:     }
   896:   }  //rtcHzUpdate
   897: 
   898: 
   899: }  //class RP5C15
   900: 
   901: 
   902: