CRTC.java
     1: //========================================================================================
     2: //  CRTC.java
     3: //    en:CRT controller
     4: //    ja:CRTコントローラ
     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: //    垂直同期
    16: //                                     ラスタ0
    17: //                                       ↓
    18: //                             ┌──────────────垂直周期──────────────┐
    19: //                1                      ┏━━┓                                                          ┏━━┓
    20: //    垂直同期信号      垂直パルス間     ┃    ┃                       垂直パルス間                       ┃    ┃     垂直パルス間
    21: //       V-SYNC   0━━━━━━━━━━━┛    ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛    ┗━━━━━━━━━━━
    22: //                             │  垂直  │垂直│  垂直  │                                      │  垂直  │垂直│  垂直  │
    23: //                             │フロント  同期   バック │                                      │フロント  同期   バック │
    24: //                             │ ポーチ  パルス  ポーチ │                                      │ ポーチ  パルス  ポーチ │
    25: //                1━━━━━━┓                        ┏━━━━━━━━━━━━━━━━━━━┓                        ┏━━━━━━1
    26: //    垂直映像信号 垂直映像期間┃      垂直空白期間      ┃             垂直映像期間             ┃      垂直空白期間      ┃垂直映像期間
    27: //       V-DISP   0            ┗━━━━━━━━━━━━┛                                      ┗━━━━━━━━━━━━┛            0
    28: //
    29: //
    30: //    水平同期
    31: //                             ┌──────────────水平周期──────────────┐
    32: //                1                      ┏━━┓                                                          ┏━━┓                      1
    33: //    水平同期信号      水平パルス間     ┃    ┃                       水平パルス間                       ┃    ┃     水平パルス間
    34: //       H-SYNC   0━━━━━━━━━━━┛    ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛    ┗━━━━━━━━━━━0
    35: //                             │  水平  │水平│  水平  │                                      │  水平  │水平│  水平  │
    36: //                             │フロント  同期   バック │                                      │フロント  同期   バック │
    37: //                             │ ポーチ  パルス  ポーチ │                                      │ ポーチ  パルス  ポーチ │
    38: //                 ━━━━━━┓                        ┏━━━━━━━━━━━━━━━━━━━┓                        ┏━━━━━━
    39: //    水平映像信号 水平映像期間┃      水平空白期間      ┃             水平映像期間             ┃      水平空白期間      ┃水平映像期間
    40: //                             ┗━━━━━━━━━━━━┛                                      ┗━━━━━━━━━━━━┛
    41: //                             └┐                                                              └┐
    42: //                1━━━━━━━┓                                                                ┏━━━━━━━━━━━━━━━━━━1
    43: //      CRTC IRQ                 ┃                            IRQラスタ                           ┃
    44: //                0              ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛                                    0
    45: //                               └┐                                                              └┐
    46: //                1                ┏━━━━━━━━━━━━━━━ ∥ ━━━━━━━━━━━━━━━┓                                  1
    47: //    垂直映像信号                 ┃    垂直映像期間開始ラスタ     ∥     垂直映像期間終了ラスタ    ┃
    48: //       V-DISP   0━━━━━━━━┛                               ∥                               ┗━━━━━━━━━━━━━━━━━0
    49: //
    50: //
    51: //    HT  水平周期カラム数
    52: //    HS  水平同期パルスカラム数
    53: //    HB  水平バックポーチカラム数
    54: //    HD  水平映像期間カラム数
    55: //    HF  水平フロントポーチカラム数
    56: //    VT  垂直周期ラスタ数
    57: //    VS  垂直同期パルスラスタ数
    58: //    VB  垂直バックポーチラスタ数
    59: //    VD  垂直映像期間ラスタ数
    60: //    VF  垂直フロントポーチラスタ数
    61: //
    62: //    0x00e80000  .w  R00  HT-1=HS+HB+HD+HF-1
    63: //    0x00e80002  .w  R01  HS-1
    64: //    0x00e80004  .w  R02  HS+HB-5
    65: //    0x00e80006  .w  R03  HS+HB+HD-5
    66: //    0x00e80008  .w  R04  VT-1=VS+VB+VD+VF-1
    67: //    0x00e8000a  .w  R05  VS-1
    68: //    0x00e8000c  .w  R06  VS+VB-1
    69: //    0x00e8000e  .w  R07  VS+VB+VD-1
    70: //    0x00e80029  .b  R20L
    71: //
    72: //    0x00e8e007  bit1  HRL
    73: //
    74: //
    75: //  ----------------------------------------------------------------------------------------------------------------------------
    76: //       解像度  サイズ         色x枚    R00   R01   R02   R03    R04   R05   R06   R07    R20L  HRL       水平同期周波数
    77: //  CRTMOD              実画面                                                                             垂直同期周波数
    78: //                                       HT   HS   HB   HD   HF   VT   VS   VB   VD   VF               垂直周期 理論値 実測値
    79: //  ----------------------------------------------------------------------------------------------------------------------------
    80: //    $00  高   512x512  1024     16x1  $005B $0009 $0011 $0051  $0237 $0005 $0028 $0228   $15    0  69.552MHz/3/8/ 92=31.500kHz
    81: //    $04                 512     16x4   92-1  10-1  22-5  86-5  568-1   6-1  41-1 553-1  %10101          31.500kHz/568=55.458Hz
    82: //    $08                 512    256x2   92   10   12   64    6  568    6   35  512   15                  18032μs   18026μs
    83: //    $0C                 512  65536x1
    84: //  ----------------------------------------------------------------------------------------------------------------------------
    85: //    $01  低   512x512  1024     16x1  $004B $0003 $0005 $0045  $0103 $0002 $0010 $0100   $05    0  38.864MHz/4/8/ 76=15.980kHz
    86: //    $05                 512     16x4   76-1   4-1  10-5  74-5  260-1   2-1  17-1 257-1  %00101          15.980kHz/260=61.462Hz
    87: //    $09                 512    256x2   76    4    6   64    2  260    2   15  240    3                  16270μs   16265μs
    88: //    $0D                 512  65536x1
    89: //  ----------------------------------------------------------------------------------------------------------------------------
    90: //    $02  高   256x256  1024     16x1  $002D $0004 $0006 $0026  $0237 $0005 $0028 $0228   $10    0  69.552MHz/6/8/ 46=31.500kHz
    91: //    $06                 512     16x4   46-1   5-1  11-5  43-5  568-1   6-1  41-1 553-1  %10000          31.500kHz/568=55.458Hz
    92: //    $0A                 512    256x2   46    5    6   32    3  568    6   35  512   15                  18032μs   18027μs
    93: //    $0E                 512  65536x1
    94: //  ----------------------------------------------------------------------------------------------------------------------------
    95: //    $03  低   256x256  1024     16x1  $0025 $0001 $0000 $0020  $0103 $0002 $0010 $0100   $00    0  38.864MHz/8/8/ 38=15.980kHz
    96: //    $07                 512     16x4   38-1   2-1   5-5  37-5  260-1   2-1  17-1 257-1  %00000          15.980kHz/260=61.462Hz
    97: //    $0B                 512    256x2   38    2    3   32    1  260    2   15  240    3                  16270μs   16265μs
    98: //    $0F                 512  65536x1
    99: //  ----------------------------------------------------------------------------------------------------------------------------
   100: //    $10  高   768x512  1024     16x1  $0089 $000E $001C $007C  $0237 $0005 $0028 $0228   $16    0  69.552MHz/2/8/138=31.500kHz
   101: //    $14                 512    256x2  138-1  15-1  33-5 129-5  568-1   6-1  41-1 553-1  %10110          31.500kHz/568=55.458Hz
   102: //    $18                 512  65536x1  138   15   18   96    9  568    6   35  512   15                  18032μs   18026μs
   103: //  ----------------------------------------------------------------------------------------------------------------------------
   104: //    $11  中  1024x424  1024     16x1  $00AF $000F $001F $009F  $01D0 $0007 $0020 $01C8   $16    0  69.552MHz/2/8/176=24.699kHz
   105: //    $15                 512    256x2  176-1  16-1  36-5 164-5  465-1   8-1  33-1 457-1  %10110          24.699kHz/465=53.116Hz
   106: //    $19                 512  65536x1  176   16   20  128   12  465    8   25  424    8                  18827μs   18822μs
   107: //  ----------------------------------------------------------------------------------------------------------------------------
   108: //    $12  中  1024x848  1024     16x1  $00AF $000F $001F $009F  $01D0 $0007 $0020 $01C8   $1A    0  69.552MHz/2/8/176=24.699kHz
   109: //    $16                 512    256x2  176-1  16-1  36-5 164-5  465-1   8-1  33-1 457-1  %11010          24.699kHz/465=53.116Hz
   110: //    $1A                 512  65536x1  176   16   20  128   12  465    8   25  424    8                  18827μs   18862μs
   111: //  ----------------------------------------------------------------------------------------------------------------------------
   112: //    $13  中   640x480  1024     16x1  $0063 $000B $000D $005D  $020C $0001 $0021 $0201   $17    0  50.350MHz/2/8/100=31.469kHz
   113: //    $17        (VGA)    512    256x2  100-1  12-1  18-5  98-5  525-1   2-1  34-1 514-1  %10111          31.469kHz/525=59.940Hz
   114: //    $1B                 512  65536x1  100   12    6   80    2  525    2   32  480   11                  16683μs   16678μs
   115: //  ----------------------------------------------------------------------------------------------------------------------------
   116: //
   117: //
   118: //  CRTMOD$10(768x512)のR20LとHRLを変更して垂直周期を計測し、R20LとHRLとオシレータと分周比の関係を調べた
   119: //
   120: //           オシレータ/分周比    垂直周期               オシレータ/分周比    垂直周期
   121: //     R20L HRL               理論値    実測値     R20L HRL               理論値    実測値
   122: //      $00  0  38.864MHz/8  129082μs  129079μs     $00  1  38.864MHz/8  129082μs  129079μs
   123: //      $01  0  38.864MHz/4   64541μs   64539μs     $01  1  38.864MHz/4   64541μs   64539μs
   124: //      $02  0  38.864MHz/8  129082μs  129078μs     $02  1  38.864MHz/8  129082μs  129079μs
   125: //      $03  0  38.864MHz/8  129082μs  129079μs     $03  1  38.864MHz/8  129082μs  129078μs
   126: //      $04  0  38.864MHz/8  129082μs  129306μs     $04  1  38.864MHz/8  129082μs  129306μs
   127: //      $05  0  38.864MHz/4   64541μs   64653μs     $05  1  38.864MHz/4   64541μs   64653μs
   128: //      $06  0  38.864MHz/8  129082μs  129306μs     $06  1  38.864MHz/8  129082μs  129306μs
   129: //      $07  0  38.864MHz/8  129082μs  129307μs     $07  1  38.864MHz/8  129082μs  129306μs
   130: //      $08  0  38.864MHz/8  129082μs  129306μs     $08  1  38.864MHz/8  129082μs  129307μs
   131: //      $09  0  38.864MHz/4   64541μs   64652μs     $09  1  38.864MHz/4   64541μs   64652μs
   132: //      $0A  0  38.864MHz/8  129082μs  129305μs     $0A  1  38.864MHz/8  129082μs  129306μs
   133: //      $0B  0  38.864MHz/8  129082μs  129307μs     $0B  1  38.864MHz/8  129082μs  129306μs
   134: //      $0C  0  38.864MHz/8  129082μs  129307μs     $0C  1  38.864MHz/8  129082μs  129306μs
   135: //      $0D  0  38.864MHz/4   64541μs   64652μs     $0D  1  38.864MHz/4   64541μs   64653μs
   136: //      $0E  0  38.864MHz/8  129082μs  129305μs     $0E  1  38.864MHz/8  129082μs  129306μs
   137: //      $0F  0  38.864MHz/8  129082μs  129306μs     $0F  1  38.864MHz/8  129082μs  129307μs
   138: //      $10  0  69.552MHz/6   54095μs   54093μs     $10  1  69.552MHz/8   72127μs   72126μs
   139: //      $11  0  69.552MHz/3   27048μs   27047μs     $11  1  69.552MHz/4   36064μs   36060μs
   140: //      $12  0  69.552MHz/2   18032μs   18031μs     $12  1  69.552MHz/2   18032μs   18029μs
   141: //      $13  0  50.350MHz/2   24909μs   24905μs     $13  1  50.350MHz/2   24909μs   24905μs
   142: //      $14  0  69.552MHz/6   54095μs   54094μs     $14  1  69.552MHz/8   72127μs   72124μs
   143: //      $15  0  69.552MHz/3   27048μs   27046μs     $15  1  69.552MHz/4   36064μs   36061μs
   144: //      $16  0  69.552MHz/2   18032μs   18030μs     $16  1  69.552MHz/2   18032μs   18030μs
   145: //      $17  0  50.350MHz/2   24909μs   24906μs     $17  1  50.350MHz/2   24909μs   24906μs
   146: //      $18  0  69.552MHz/6   54095μs   54188μs     $18  1  69.552MHz/8   72127μs   72251μs
   147: //      $19  0  69.552MHz/3   27048μs   27094μs     $19  1  69.552MHz/4   36064μs   36124μs
   148: //      $1A  0  69.552MHz/2   18032μs   18061μs     $1A  1  69.552MHz/2   18032μs   18062μs
   149: //      $1B  0  50.350MHz/2   24909μs   24950μs     $1B  1  50.350MHz/2   24909μs   24950μs
   150: //      $1C  0  69.552MHz/6   54095μs   54189μs     $1C  1  69.552MHz/8   72127μs   72250μs
   151: //      $1D  0  69.552MHz/3   27048μs   27092μs     $1D  1  69.552MHz/4   36064μs   36125μs
   152: //      $1E  0  69.552MHz/2   18032μs   18062μs     $1E  1  69.552MHz/2   18032μs   18062μs
   153: //      $1F  0  50.350MHz/2   24909μs   24951μs     $1F  1  50.350MHz/2   24909μs   24950μs
   154: //
   155: //    perl -e "for$o(38.863632,69.551900,50.349800){for$d(6,8,3,4,2){printf'  //      %6.3fMHz/%d  %6.0fus%c',$o,$d,1/($o/($d*8*138*568)),10;}}"
   156: //
   157: //     オシレータ/分周比
   158: //                    理論値    R20L  HRL
   159: //      38.864MHz/6   96811μs
   160: //      38.864MHz/8  129082μs  %0**00  *
   161: //                             %0**1*  *
   162: //      38.864MHz/3   48406μs
   163: //      38.864MHz/4   64541μs  %0**01  *
   164: //      38.864MHz/2   32270μs
   165: //      69.552MHz/6   54095μs  %1**00  0
   166: //      69.552MHz/8   72127μs  %1**00  1
   167: //      69.552MHz/3   27048μs  %1**01  0
   168: //      69.552MHz/4   36064μs  %1**01  1
   169: //      69.552MHz/2   18032μs  %1**10  *
   170: //      50.350MHz/6   74726μs
   171: //      50.350MHz/8   99634μs
   172: //      50.350MHz/3   37363μs
   173: //      50.350MHz/4   49817μs
   174: //      50.350MHz/2   24909μs  %1**11  *
   175: //
   176: //      オシレータと分周比はR20のbit4,1,0とHRLで決まる
   177: //      HRLをセットすると69.552MHzの3分周と6分周が4分周と8分周に変わる
   178: //      VGAモードのための50.350MHzのオシレータと起動時のVキーとNキーの処理はX68000 Compact(IPLROM 1.2)で追加された
   179: //      X68000 XVI(IPLROM 1.1)までの機種ではVGAモードは使用できない
   180: //
   181: //----------------------------------------------------------------------------------------
   182: //
   183: //  R08 外部同期水平アジャスト
   184: //
   185: //    スーパーインポーズするときビデオの映像とX68000の映像を重ねるために、
   186: //    ビデオとX68000の水平同期パルスの先頭の時間差を38.863632MHzのサイクル数で指定する。
   187: //
   188: //    低解像度512x512(インターレース)のとき
   189: //      水平同期パルス幅は4カラム。R01=4-1=3
   190: //      水平バックポーチは6カラム。R02=4+6-5=5
   191: //      推測される計算式。おそらく正しい?
   192: //        perl -e "print((4.7+4.7)*38.863632-(4*8*(4+6))-1)"
   193: //        44.3181408
   194: //      外部同期水平アジャストは44
   195: //
   196: //    低解像度256x256のとき
   197: //      水平同期パルス幅は2カラム。R01=2-1=1
   198: //      水平バックポーチは3カラム。R02=2+3-5=0
   199: //      推測される計算式。1ドット追加する?
   200: //        perl -e "print((4.7+4.7)*38.863632-(8*(8*(2+3)+1))-1)"
   201: //        36.3181408
   202: //      外部同期水平アジャストは36
   203: //
   204: //    低解像度以外の設定値27の根拠は不明
   205: //
   206: //----------------------------------------------------------------------------------------
   207: //
   208: //  ラスタコピー
   209: //
   210: //    関係するレジスタ
   211: //      $00E8002A  .w  CRTC R21         bit3     1=テキストプレーン3をラスタコピーする,0=しない
   212: //                                      bit2     1=テキストプレーン2をラスタコピーする,0=しない
   213: //                                      bit1     1=テキストプレーン1をラスタコピーする,0=しない
   214: //                                      bit0     1=テキストプレーン0をラスタコピーする,0=しない
   215: //      $00E8002C  .w  CRTC R22         bit15-8  ソースラスタブロック番号(0~255)
   216: //                                      bit7-0   デスティネーションラスタブロック番号(0~255)
   217: //      $00E80480  .w  CRTC 動作ポート  bit3     1=水平フロントポーチでラスタコピーする,0=しない
   218: //      $00E88001  .b  MFP GPIPデータ   bit7     1=水平同期パルス,0=水平パルス間(水平バックポーチ+水平映像期間+水平フロントポーチ)
   219: //                                      bit4     1=垂直映像期間,0=垂直空白期間(垂直フロントポーチ+垂直同期パルス+垂直バックポーチ)
   220: //
   221: //    ラスタコピーの動作
   222: //      水平フロントポーチの先頭で(動作ポート&8)!=0のとき、
   223: //      (R21&1)!=0ならばテキストプレーン0($00E00000~$00E1FFFF)について、
   224: //      (R21&2)!=0ならばテキストプレーン1($00E20000~$00E3FFFF)について、
   225: //      (R21&4)!=0ならばテキストプレーン2($00E40000~$00E5FFFF)について、
   226: //      (R21&8)!=0ならばテキストプレーン3($00E60000~$00E7FFFF)について、それぞれ、
   227: //      ラスタブロック(R22>>>8)(テキストVRAMのオフセット(R22>>>8)*512~(R22>>>8)*512+511)の内容を、
   228: //      ラスタブロック(R22&255)(テキストVRAMのオフセット(R22&255)*512~(R22&255)*512+511)へ、コピーする
   229: //
   230: //      メモ
   231: //        ラスタコピーは水平同期パルスではなく水平フロントポーチで行われる
   232: //          水平同期パルスに入る前に終わっている
   233: //        動作ポートのbit3はMPUがラスタコピーの機能をON/OFFするための単なるスイッチである
   234: //          0→1が動作開始を意味するトリガーのようなものではない
   235: //          CRTCが動作終了を知らせるフラグのようなものでもない
   236: //
   237: //    水平同期パルスの待ち方
   238: //      水平同期パルスは非常に短い
   239: //          画面モード        水平同期パルスの長さ          10MHz換算
   240: //          高解像度768x512   1/31500*10/92*1e6=3.45μs   34クロック
   241: //          低解像度512x512   1/15980*4/76*1e6=3.29μs    32クロック
   242: //          中解像度1024x848  1/24699*16/176*1e6=3.68μs  36クロック
   243: //          VGA 640x480       1/31469*12/100*1e6=3.81μs  38クロック
   244: //      水平同期パルスを待つときは見逃さないように割り込みを禁止してなるべく短い間隔でGPIPデータを監視しなければならない
   245: //      普通の方法
   246: //                  lea.l   GPIPデータ,a0
   247: //                  割り込みを禁止する
   248: //          1:      tst.b   (a0)
   249: //                  bpl.s   1b
   250: //      ショートの条件分岐命令は通過する方が速い
   251: //                  lea.l   GPIPデータ,a0
   252: //                  割り込みを禁止する
   253: //          1:      .rept   9
   254: //                  tst.b   (a0)
   255: //                  bmi.s   2f
   256: //                  .endm
   257: //                  tst.b   (a0)
   258: //                  bpl.s   1b
   259: //          2:
   260: //      水平同期パルスの先頭を通過したかどうかが分かればよいので、多少進み過ぎても問題ない
   261: //                  lea.l   GPIPデータ,a0
   262: //                  割り込みを禁止する
   263: //          1:      move.b  (a0),d0
   264: //                  .rept   9
   265: //                  or.b    (a0),d0
   266: //                  .endm
   267: //                  bpl.s   1b
   268: //
   269: //    ラスタコピーの手順
   270: //      10MHz機ではGPIPデータの監視の条件分岐とR22の更新とループの分岐が水平同期パルスに収まらないので水平パルス間を待つ必要がほとんど無い
   271: //      しかし、水平同期パルスだけを待つ方法ではMPUが速いとき1回の水平同期パルスにR22を2回更新してしまいラスタコピーに失敗する可能性がある
   272: //      MPUがどんなに速くても問題なく動作させるには水平同期パルスと水平パルス間を両方待たなければならない
   273: //                  move.w  #コピーするラスタブロック数-1,d3
   274: //                  bmi.s   5f
   275: //                  割り込みを禁止する
   276: //                  水平同期パルスを待つ
   277: //                  水平パルス間を待つ
   278: //                  R21を設定する
   279: //                  R22を設定する
   280: //                  動作ポートのbit3をセットする
   281: //                  bra.s   4f
   282: //          3:      割り込みを禁止する
   283: //                  水平同期パルスを待つ
   284: //                  水平パルス間を待つ
   285: //                  R22を更新する
   286: //          4:      割り込みを許可する
   287: //                  dbra.w  d3.3b
   288: //                  割り込みを禁止する
   289: //                  水平同期パルスを待つ
   290: //                  水平パルス間を待つ
   291: //                  動作ポートのbit3をクリアする
   292: //                  割り込みを許可する
   293: //          5:
   294: //
   295: //    画面クリア
   296: //        ラスタブロック63が余っているとき
   297: //          同時アクセスを使ってラスタブロック63をクリア
   298: //          ラスタブロック63からラスタブロック(開始行*4+0)へラスタコピー
   299: //          ラスタブロック63からラスタブロック(開始行*4+1)へラスタコピー
   300: //                  :
   301: //          ラスタブロック63からラスタブロック(終了行*4+2)へラスタコピー
   302: //          ラスタブロック63からラスタブロック(終了行*4+3)へラスタコピー
   303: //
   304: //    16ドットスムーススクロールアップ
   305: //        ラスタブロック63が余っているとき
   306: //          同時アクセスを使ってラスタブロック63をクリア
   307: //          垂直映像期間を待つ
   308: //          垂直空白期間を待つ
   309: //          ラスタブロック(開始行*4+4)からラスタブロック(開始行*4)へラスタコピー
   310: //          ラスタブロック(開始行*4+5)からラスタブロック(開始行*4+1)へラスタコピー
   311: //                  :
   312: //          ラスタブロック(終了行*4+2)からラスタブロック(終了行*4-2)へラスタコピー
   313: //          ラスタブロック(終了行*4+3)からラスタブロック(終了行*4-1)へラスタコピー
   314: //          ラスタブロック63からラスタブロック(終了行*4+1)へラスタコピー
   315: //          ラスタブロック63からラスタブロック(終了行*4+1)へラスタコピー
   316: //          ラスタブロック63からラスタブロック(終了行*4+2)へラスタコピー
   317: //          ラスタブロック63からラスタブロック(終了行*4+3)へラスタコピー
   318: //
   319: //    8ドットスムーススクロールアップ
   320: //        ラスタブロック63が余っているとき
   321: //          同時アクセスを使ってラスタブロック63をクリア
   322: //        以下を2回繰り返す
   323: //          垂直映像期間を待つ
   324: //          垂直空白期間を待つ
   325: //          ラスタブロック(開始行*4+2)からラスタブロック(開始行*4)へラスタコピー
   326: //          ラスタブロック(開始行*4+3)からラスタブロック(開始行*4+1)へラスタコピー
   327: //                  :
   328: //          ラスタブロック(終了行*4+2)からラスタブロック(終了行*4)へラスタコピー
   329: //          ラスタブロック(終了行*4+3)からラスタブロック(終了行*4+1)へラスタコピー
   330: //          ラスタブロック63からラスタブロック(終了行*4+2)へラスタコピー
   331: //          ラスタブロック63からラスタブロック(終了行*4+3)へラスタコピー
   332: //
   333: //    4ドットスムーススクロールアップ
   334: //        ラスタブロック63が余っているとき
   335: //          同時アクセスを使ってラスタブロック63をクリア
   336: //        以下を4回繰り返す
   337: //          垂直映像期間を待つ
   338: //          垂直空白期間を待つ
   339: //          ラスタブロック(開始行*4+1)からラスタブロック(開始行*4)へラスタコピー
   340: //          ラスタブロック(開始行*4+2)からラスタブロック(開始行*4+1)へラスタコピー
   341: //                  :
   342: //          ラスタブロック(終了行*4+2)からラスタブロック(終了行*4+1)へラスタコピー
   343: //          ラスタブロック(終了行*4+3)からラスタブロック(終了行*4+2)へラスタコピー
   344: //          ラスタブロック63からラスタブロック(終了行*4+3)へラスタコピー
   345: //
   346: //    16ドットスムーススクロールダウン
   347: //      スクロールダウンのときmemcpyの要領で下からコピーすると途中で走査線と衝突して画面に亀裂が入ってしまい見苦しくなる
   348: //      スクロールダウンのときも上からコピーする
   349: //      画面外の余っているラスタブロックをバッファに使う
   350: //      Aをクリアしておいて0→B,A→0,1→A,B→1,2→B,A→2,3→A,B→3,…の順にコピーする
   351: //      スクロールアップは走査線の4倍の速さで進むがスクロールダウンは2倍なので水平同期パルスを頻繁に見逃すと走査線に追い付かれてしまう
   352: //      ラスタブロック56~63をバッファとして使う
   353: //          同時アクセスを使ってラスタブロック56をクリア
   354: //          ラスタブロック56からラスタブロック57へラスタコピー
   355: //          ラスタブロック56からラスタブロック58へラスタコピー
   356: //          ラスタブロック56からラスタブロック59へラスタコピー
   357: //          垂直映像期間を待つ
   358: //          垂直空白期間を待つ
   359: //          ラスタブロック(開始行*4)からラスタブロック60へラスタコピー
   360: //          ラスタブロック(開始行*4+1)からラスタブロック61へラスタコピー
   361: //          ラスタブロック(開始行*4+2)からラスタブロック62へラスタコピー
   362: //          ラスタブロック(開始行*4+3)からラスタブロック63へラスタコピー
   363: //          ラスタブロック56からラスタブロック(開始行*4)へラスタコピー
   364: //          ラスタブロック57からラスタブロック(開始行*4+1)へラスタコピー
   365: //          ラスタブロック58からラスタブロック(開始行*4+2)へラスタコピー
   366: //          ラスタブロック59からラスタブロック(開始行*4+3)へラスタコピー
   367: //          ラスタブロック(開始行*4+4)からラスタブロック56へラスタコピー
   368: //          ラスタブロック(開始行*4+5)からラスタブロック57へラスタコピー
   369: //          ラスタブロック(開始行*4+6)からラスタブロック58へラスタコピー
   370: //          ラスタブロック(開始行*4+7)からラスタブロック59へラスタコピー
   371: //          ラスタブロック60からラスタブロック(開始行*4+4)へラスタコピー
   372: //          ラスタブロック61からラスタブロック(開始行*4+5)へラスタコピー
   373: //          ラスタブロック62からラスタブロック(開始行*4+6)へラスタコピー
   374: //          ラスタブロック63からラスタブロック(開始行*4+7)へラスタコピー
   375: //                  :
   376: //          ラスタブロック56/60からラスタブロック(終了行*4)へラスタコピー
   377: //          ラスタブロック57/61からラスタブロック(終了行*4+1)へラスタコピー
   378: //          ラスタブロック58/62からラスタブロック(終了行*4+2)へラスタコピー
   379: //          ラスタブロック59/63からラスタブロック(終了行*4+3)へラスタコピー
   380: //      画面外のラスタブロックを利用できない場合は最下行になる行の一部をメモリに退避させてから最下行とその上のラスタブロックをバッファに使う
   381: //
   382: //    8ドットスムーススクロールダウン
   383: //        以下を2回繰り返す
   384: //          同時アクセスを使ってラスタブロック60をクリア
   385: //          ラスタブロック60からラスタブロック61へラスタコピー
   386: //          垂直映像期間を待つ
   387: //          垂直空白期間を待つ
   388: //          ラスタブロック(開始行*4)からラスタブロック62へラスタコピー
   389: //          ラスタブロック(開始行*4+1)からラスタブロック63へラスタコピー
   390: //          ラスタブロック60からラスタブロック(開始行*4)へラスタコピー
   391: //          ラスタブロック61からラスタブロック(開始行*4+1)へラスタコピー
   392: //          ラスタブロック(開始行*4+2)からラスタブロック60へラスタコピー
   393: //          ラスタブロック(開始行*4+3)からラスタブロック61へラスタコピー
   394: //          ラスタブロック62からラスタブロック(開始行*4+2)へラスタコピー
   395: //          ラスタブロック63からラスタブロック(開始行*4+3)へラスタコピー
   396: //                  :
   397: //          ラスタブロック60/62からラスタブロック(終了行*4+2)へラスタコピー
   398: //          ラスタブロック61/63からラスタブロック(終了行*4+3)へラスタコピー
   399: //
   400: //    4ドットスムーススクロールダウン
   401: //        以下を4回繰り返す
   402: //          同時アクセスを使ってラスタブロック62をクリア
   403: //          垂直映像期間を待つ
   404: //          垂直空白期間を待つ
   405: //          ラスタブロック(開始行*4)からラスタブロック63へラスタコピー
   406: //          ラスタブロック62からラスタブロック(開始行*4)へラスタコピー
   407: //          ラスタブロック(開始行*4+1)からラスタブロック62へラスタコピー
   408: //          ラスタブロック63からラスタブロック(開始行*4+1)へラスタコピー
   409: //                  :
   410: //          ラスタブロック62/63からラスタブロック(終了行*4+3)へラスタコピー
   411: //
   412: //----------------------------------------------------------------------------------------
   413: 
   414: package xeij;
   415: 
   416: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
   417: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
   418: 
   419: public class CRTC {
   420: 
   421:   //拡張グラフィック画面
   422:   //  メモリモード5  1024ドット512色(拡張)
   423:   //  メモリモード7  1024ドット65536色(拡張)
   424:   public static final boolean CRT_EXTENDED_GRAPHIC = true;  //true=拡張グラフィック画面をONにできる
   425:   public static boolean crtExtendedGraphicRequest;  //true=次回起動時に拡張グラフィック画面をONにする
   426:   public static boolean crtExtendedGraphicOn;  //true=拡張グラフィック画面がON
   427: 
   428:   //画面モードに変更があったとき描画をリスタートさせるまでの遅延(XEiJ.TMR_FREQ単位)
   429:   public static final long CRT_RESTART_DELAY = XEiJ.TMR_FREQ / 5;  //0.2秒
   430: 
   431:   //レジスタ
   432:   //  R00-R19/R22-R23はwrite onlyでreadすると常に0が返るが読めないと不便なのでreadできるようにする
   433:   //  動作ポートはreadできるが1を書き込んだビットは0を書き込むまで1のまま
   434:   //  未定義ビットは常に0
   435:   public static final int CRT_R00_H_FRONT_END = 0x00e80000;  //R00  bit7-0   水平フロントポーチ終了カラム
   436:   public static final int CRT_R01_H_SYNC_END  = 0x00e80002;  //R01  bit7-0   水平同期パルス終了カラム
   437:   public static final int CRT_R02_H_BACK_END  = 0x00e80004;  //R02  bit7-0   水平バックポーチ終了カラム
   438:   public static final int CRT_R03_H_DISP_END  = 0x00e80006;  //R03  bit7-0   水平映像期間終了カラム
   439:   public static final int CRT_R04_V_FRONT_END = 0x00e80008;  //R04  bit9-0   垂直フロントポーチ終了ラスタ
   440:   public static final int CRT_R05_V_SYNC_END  = 0x00e8000a;  //R05  bit9-0   垂直同期パルス終了ラスタ
   441:   public static final int CRT_R06_V_BACK_END  = 0x00e8000c;  //R06  bit9-0   垂直バックポーチ終了ラスタ
   442:   public static final int CRT_R07_V_DISP_END  = 0x00e8000e;  //R07  bit9-0   垂直映像期間終了ラスタ
   443:   public static final int CRT_R08_ADJUST      = 0x00e80010;  //R08  bit7-0   外部同期アジャスト。TVとX68000の水平同期パルスの立下りの時間差(39MHz)
   444:   public static final int CRT_R09_IRQ_RASTER  = 0x00e80012;  //R09  bit9-0   IRQラスタ。0=垂直同期パルス開始ラスタ
   445:   public static final int CRT_R10_TX_X        = 0x00e80014;  //R10  bit9-0   テキストX方向スクロール
   446:   public static final int CRT_R11_TX_Y        = 0x00e80016;  //R11  bit9-0   テキストY方向スクロール
   447:   public static final int CRT_R12_GR_X_0      = 0x00e80018;  //R12  bit9-0   グラフィックX方向スクロール4bitページ0
   448:   public static final int CRT_R13_GR_Y_0      = 0x00e8001a;  //R13  bit9-0   グラフィックY方向スクロール4bitページ0
   449:   public static final int CRT_R14_GR_X_1      = 0x00e8001c;  //R14  bit8-0   グラフィックX方向スクロール4bitページ1
   450:   public static final int CRT_R15_GR_Y_1      = 0x00e8001e;  //R15  bit8-0   グラフィックY方向スクロール4bitページ1
   451:   public static final int CRT_R16_GR_X_2      = 0x00e80020;  //R16  bit8-0   グラフィックX方向スクロール4bitページ2
   452:   public static final int CRT_R17_GR_Y_2      = 0x00e80022;  //R17  bit8-0   グラフィックY方向スクロール4bitページ2
   453:   public static final int CRT_R18_GR_X_3      = 0x00e80024;  //R18  bit8-0   グラフィックX方向スクロール4bitページ3
   454:   public static final int CRT_R19_GR_Y_3      = 0x00e80026;  //R19  bit8-0   グラフィックY方向スクロール4bitページ3
   455:   public static final int CRT_R20_MODE        = 0x00e80028;  //R20  bit12    テキストストレージ
   456:   //                                                                bit11    グラフィックストレージ
   457:   //                                                                bit10-8  メモリモード  0=512ドット16色
   458:   //                                                                                       1=512ドット256色
   459:   //                                                                                       2=メモリモード2
   460:   //                                                                                       3=512ドット65536色
   461:   //                                                                                       4=1024ドット16色
   462:   //                                                                                       5=1024ドット16色/1024ドット256色(拡張)
   463:   //                                                                                       6=1024ドット16色
   464:   //                                                                                       7=1024ドット16色/1024ドット65536色(拡張)
   465:   //                                                                bit4     解像度  0=低解像度,1=高解像度
   466:   //                                                                bit3-2   垂直解像度  0=256(ラスタ2度読み),1=512,2=1024,3=1024
   467:   //                                                                bit1-0   水平解像度  0=256,1=512,2=768,3=640
   468:   public static final int CRT_R21_SELECT      = 0x00e8002a;  //R21  bit9     1=ビットマスクON
   469:   //                                                                bit8     1=同時アクセスON
   470:   //                                                                bit7     1=プレーン3を同時アクセスする
   471:   //                                                                bit6     1=プレーン2を同時アクセスする
   472:   //                                                                bit5     1=プレーン1を同時アクセスする
   473:   //                                                                bit4     1=プレーン0を同時アクセスする
   474:   //                                                                bit3     1=プレーン3をラスタコピー/高速クリアする
   475:   //                                                                bit2     1=プレーン2をラスタコピー/高速クリアする
   476:   //                                                                bit1     1=プレーン1をラスタコピー/高速クリアする
   477:   //                                                                bit0     1=プレーン0をラスタコピー/高速クリアする
   478:   public static final int CRT_R22_BLOCK       = 0x00e8002c;  //R22  bit15-8  0~255 ソースラスタブロック番号
   479:   //                                                                bit7-0   0~255 デスティネーションラスタブロック番号
   480:   public static final int CRT_R23_MASK        = 0x00e8002e;  //R23  bit15-0  ビットマスク。1のビットに書き込まない
   481:   public static final int CRT_R24             = 0x00e80030;  //R24
   482:   public static final int CRT_ACTION          = 0x00e80480;  //動作ポート  bit0  1=画像入力開始
   483:   //                                                                       bit1  1=次の垂直表示開始で高速クリア開始
   484:   //                                                                       bit3  1=ラスタコピー実行
   485: 
   486:   //ドットクロックオシレータ
   487:   //  int k = crtHRLCurr << 3 | crtHighResoCurr << 2 | crtHResoCurr;
   488:   //  crtColumnTime = (int) ((double) (XEiJ.TMR_FREQ * 8 * CRT_DIVS[k]) / (double) crtFreqs[CRT_OSCS[k]] + 0.5);
   489:   //  10**12/(4*10**6)*8*1024=2048000000。4MHzを下回ると8分周で1024ドットの水平映像期間がintに収まらなくなる
   490:   public static final int[] CRT_OSCS = { 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0, 1, 1, 1, 2 };  //HRLR20410→オシレータの番号
   491:   public static final int[] CRT_DIVS = { 8, 4, 8, 8, 6, 3, 2, 2, 8, 4, 8, 8, 8, 4, 2, 2 };  //HRLR20410→分周比
   492:   public static final int CRT_MIN_FREQ =  10000000;  //オシレータの周波数の下限(Hz)
   493:   public static final int CRT_MAX_FREQ = 400000000;  //オシレータの周波数の上限(Hz)
   494:   public static final int[] CRT_DEFAULT_FREQS = { 38863632, 69551900, 50349800 };  //デフォルトのオシレータの周波数(Hz)
   495:   public static final int[] crtFreqsRequest = new int[3];  //リセット後のオシレータの周波数(Hz)
   496:   public static final int[] crtFreqs = new int[3];  //現在のオシレータの周波数(Hz)
   497: 
   498:   //レジスタ
   499:   //  ゼロ拡張
   500:   public static int crtR00HFrontEndPort;                 //R00 7-0 水平フロントポーチ終了カラム
   501:   public static int crtR00HFrontEndMask;
   502:   public static int crtR00HFrontEndTest;
   503:   public static int crtR00HFrontEndCurr;
   504:   public static int crtR01HSyncEndPort;                  //R01 7-0 水平同期パルス終了カラム
   505:   public static int crtR01HSyncEndMask;
   506:   public static int crtR01HSyncEndTest;
   507:   public static int crtR01HSyncEndCurr;
   508:   public static int crtR02HBackEndPort;                  //R02 7-0 水平バックポーチ終了カラム
   509:   public static int crtR02HBackEndMask;
   510:   public static int crtR02HBackEndTest;
   511:   public static int crtR02HBackEndCurr;
   512:   public static int crtR03HDispEndPort;                  //R03 7-0 水平映像期間終了カラム
   513:   public static int crtR03HDispEndMask;
   514:   public static int crtR03HDispEndTest;
   515:   public static int crtR03HDispEndCurr;
   516:   public static int crtR04VFrontEndPort;                 //R04 9-0 垂直フロントポーチ終了ラスタ
   517:   public static int crtR04VFrontEndMask;
   518:   public static int crtR04VFrontEndTest;
   519:   public static int crtR04VFrontEndCurr;
   520:   public static int crtR05VSyncEndPort;                  //R05 9-0 垂直同期パルス終了ラスタ
   521:   public static int crtR05VSyncEndMask;
   522:   public static int crtR05VSyncEndTest;
   523:   public static int crtR05VSyncEndCurr;
   524:   public static int crtR06VBackEndPort;                  //R06 9-0 垂直バックポーチ終了ラスタ
   525:   public static int crtR06VBackEndMask;
   526:   public static int crtR06VBackEndTest;
   527:   public static int crtR06VBackEndCurr;
   528:   public static int crtVDispStart;                       //        垂直映像期間開始ラスタ。crtR06VBackEndCurr+1
   529:   public static int crtR07VDispEndPort;                  //R07 9-0 垂直映像期間終了ラスタ
   530:   public static int crtR07VDispEndMask;
   531:   public static int crtR07VDispEndTest;
   532:   public static int crtR07VDispEndCurr;
   533:   public static int crtVIdleStart;                       //        垂直空白期間開始ラスタ。crtR07VDispEndCurr+1
   534:   public static int crtR08Adjust;                        //R08 7-0 外部同期水平アジャスト
   535:   public static int crtR09IRQRasterPort;                 //R09 9-0 IRQラスタ。0=垂直同期パルス開始ラスタ
   536:   public static int crtR09IRQRasterMask;
   537:   public static int crtR09IRQRasterTest;
   538:   public static int crtR09IRQRasterCurr;
   539:   public static int crtR10TxXPort;                       //R10 9-0 テキストX方向スクロール
   540:   public static int crtR10TxXMask;
   541:   public static int crtR10TxXTest;
   542:   public static int crtR10TxXCurr;
   543:   public static int crtR11TxYPort;                       //R11 9-0 テキストY方向スクロール
   544:   public static int crtR11TxYMask;
   545:   public static int crtR11TxYTest;
   546:   public static int crtR11TxYCurr;
   547:   public static int crtR11TxYZero;                       //垂直映像期間開始時のテキストY方向スクロール
   548:   public static int crtR11TxYZeroLast;
   549:   public static final int[] crtR12GrXPort = new int[4];  //[0] R12 9-0 グラフィックX方向スクロール0
   550:   //                                                       [1] R14 8-0 グラフィックX方向スクロール1
   551:   //                                                       [2] R16 8-0 グラフィックX方向スクロール2
   552:   //                                                       [3] R18 8-0 グラフィックX方向スクロール3
   553:   public static final int[] crtR12GrXMask = new int[4];
   554:   public static final int[] crtR12GrXTest = new int[4];
   555:   public static final int[] crtR12GrXCurr = new int[4];
   556:   public static final int[] crtR13GrYPort = new int[4];  //[0] R13 9-0 グラフィックY方向スクロール0
   557:   //                                                       [1] R15 8-0 グラフィックY方向スクロール1
   558:   //                                                       [2] R17 8-0 グラフィックY方向スクロール2
   559:   //                                                       [3] R19 8-0 グラフィックY方向スクロール3
   560:   public static final int[] crtR13GrYMask = new int[4];
   561:   public static final int[] crtR13GrYTest = new int[4];
   562:   public static final int[] crtR13GrYCurr = new int[4];
   563:   public static final int[] crtR13GrYZero = new int[4];  //垂直映像期間開始時のグラフィックY方向スクロール
   564:   public static final int[] crtR13GrYZeroLast = new int[4];
   565:   public static int crtTextStorage;                      //R20 12 テキストストレージ 0=OFF,1=ON
   566:   public static int crtGraphicStorage;                   //R20 11 グラフィックストレージ 0=OFF,1=ON
   567:   public static int crtMemoryModePort;                   //R20 10-8 0=512ドット16色,1=512ドット256色,3=512ドット65536色,4=1024ドット16色,5=1024ドット256色(拡張),7=1024ドット65536色(拡張)
   568:   public static int crtMemoryModeMask;
   569:   public static int crtMemoryModeTest;
   570:   public static int crtMemoryModeCurr;
   571:   public static int crtHighResoPort;                     //R20 4    0=低解像度,1=高解像度
   572:   public static int crtHighResoMask;
   573:   public static int crtHighResoTest;
   574:   public static int crtHighResoCurr;
   575:   public static int crtVResoPort;                        //R20 3-2  垂直解像度
   576:   public static int crtVResoMask;
   577:   public static int crtVResoTest;
   578:   public static int crtVResoCurr;
   579:   public static int crtHResoPort;                        //R20 1-0  水平解像度
   580:   public static int crtHResoMask;
   581:   public static int crtHResoTest;
   582:   public static int crtHResoCurr;
   583:   public static boolean crtCCPlane0;                     //R21 0 true=プレーン0をラスタコピー/高速クリアする
   584:   public static boolean crtCCPlane1;                     //    1 true=プレーン1をラスタコピー/高速クリアする
   585:   public static boolean crtCCPlane2;                     //    2 true=プレーン2をラスタコピー/高速クリアする
   586:   public static boolean crtCCPlane3;                     //    3 true=プレーン3をラスタコピー/高速クリアする
   587:   public static boolean crtSimPlane0;                    //    4 true=プレーン0を同時アクセスする
   588:   public static boolean crtSimPlane1;                    //    5 true=プレーン1を同時アクセスする
   589:   public static boolean crtSimPlane2;                    //    6 true=プレーン2を同時アクセスする
   590:   public static boolean crtSimPlane3;                    //    7 true=プレーン3を同時アクセスする
   591:   public static boolean crtSimAccess;                    //    8 true=同時アクセス有効
   592:   public static boolean crtBitMask;                      //    9 true=ビットマスク有効
   593:   public static int crtR22SrcBlock;                      //R22 15-8 ソースラスタブロック番号
   594:   public static int crtR22DstBlock;                      //    7-0  デスティネーションラスタブロック番号
   595:   public static int crtR23Mask;                          //R23 15-0 ビットマスク。1のビットに書き込まない
   596:   public static boolean crtRasterCopyOn;                 //動作ポート 2        true=次の水平フロントポーチでラスタコピー実行
   597:   public static boolean crtClearStandby;                 //動作ポート 1(write) true=次の垂直表示開始で高速クリア開始
   598:   public static int crtClearFrames;                      //  実行中の高速クリアの残りフレーム数。インターレースのとき2、それ以外は1から始めてデクリメントする
   599: 
   600:   public static int crtHRLPort;  //0または1。1のとき69.552MHzの3分周と6分周が4分周と8分周に変わる
   601:   public static int crtHRLMask;
   602:   public static int crtHRLTest;
   603:   public static int crtHRLCurr;
   604: 
   605:   public static boolean crtDuplication;  //true=ラスタ2度読み。crtHighResoCurr==1&&crtVResoCurr==0&&((SpriteScreen.sprReg8ResoCurr&12)!=4)
   606:   public static boolean crtInterlace;  //true=インターレース。crtHighResoCurr+1<=crtVResoCurr
   607:   public static boolean crtSlit;  //true=スリット。crtHighResoCurr==0&&crtVResoCurr==0
   608:   public static boolean crtDupExceptSp;  //true=ラスタ2度読み(スプライトを除く)。crtHighResoCurr==1&&crtVResoCurr==0&&((SpriteScreen.sprReg8ResoCurr&12)==4)
   609:   public static int crtHSyncColumn;  //水平同期パルスカラム数(修正後)
   610:   public static int crtHBackColumn;  //水平バックポーチカラム数(修正後)
   611:   public static int crtHDispColumn;  //水平映像期間カラム数(修正後)
   612:   public static int crtHFrontColumn;  //水平フロントポーチカラム数(修正後)
   613:   public static int crtHTotalColumn;  //水平トータルカラム数(修正後)
   614:   public static double crtVsyncMultiplier;  //垂直同期周波数に掛ける数
   615:   public static int crtColumnTime;  //水平カラム時間(XEiJ.TMR_FREQ単位)
   616:   public static int crtHSyncLength;  //水平同期パルスの長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR01HSyncEndCurr+1)
   617:   public static int crtHBackLength;  //水平バックポーチの長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR02HBackEndCurr-crtR01HSyncEndCurr)
   618:   public static int crtHDispLength;  //水平映像期間の長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR03HDispEndCurr-crtR02HBackEndCurr)
   619:   public static int crtHFrontLength;  //水平フロントポーチの長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR00HFrontEndCurr-crtR03HDispEndCurr)
   620:   public static int crtHBackDispLength;  //水平バックポーチと水平映像期間の長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR03HDispEndCurr-crtR01HSyncEndCurr)
   621:   public static long crtTotalLength;  //垂直周期(ミリ)。0=未確定
   622:   public static long crtTotalLengthMNP;  //垂直周期(マイクロナノピコ)。0=未確定
   623: 
   624:   //  描画のルール
   625:   //    更新されていないラスタのビットマップへの変換を省略する
   626:   //    更新されたラスタを含む矩形をクリッピングエリアとしてペイントする
   627:   //    更新されたラスタは水平映像期間に入った瞬間に1ラスタ分変換する
   628:   //      水平映像期間の途中でパレットレジスタやスクロールレジスタを操作してもそのラスタには反映されない
   629:   //        768x512で256色の画面は作れない
   630:   //      スプライト画面も水平バックポーチが終わる前に書き換えればそのラスタに反映される
   631:   //  更新されたラスタだけを描画する手順
   632:   //    初期化
   633:   //      crtAllStamp=1。2ずつ増やすので0になることはない
   634:   //      for all y
   635:   //        crtRasterStamp[y]=0
   636:   //    画面モードが変更されたりスクロールレジスタやパレットレジスタが操作されて画面の全体が変化した可能性があるとき
   637:   //      crtAllStamp+=2。常に奇数
   638:   //    VRAMやスプライトレジスタが操作されて画面の一部が変化した可能性があるとき
   639:   //      crtRasterStamp[y]=0
   640:   //    垂直映像期間開始ラスタに入るとき
   641:   //      crtDirtyY0=-1。更新なし
   642:   //      crtScreenY=0
   643:   //    描画フレームの垂直映像期間の水平映像期間に入ったとき
   644:   //      crtRasterStamp[crtScreenY]!=crtAllStamp。再描画が必要
   645:   //        crtRasterStamp[crtScreenY]=crtAllStamp
   646:   //        crtDirtyY0<0
   647:   //          crtDirtyY0=crtScreenY。更新あり
   648:   //        crtDirtyY1=crtScreenY
   649:   //        drawRaster(crtScreenY)
   650:   //      crtScreenY++
   651:   //    垂直映像期間終了ラスタから出たとき
   652:   //      crtDirtyY0>=0。更新されたとき
   653:   //        crtDirtyY0からcrtDirtyY1までをrepaintする
   654:   public static int crtDirtyY0;  //垂直映像期間にビットマップが更新された範囲の上端のスクリーンY座標。-1=更新されていない
   655:   public static int crtDirtyY1;  //垂直映像期間にビットマップが更新された範囲の下端のスクリーンY座標。更新された範囲の高さはcrtDirtyY1-crtDirtyY0+1
   656: 
   657:   public static final int[] crtRasterStamp = new int[1024 + 15];  //ラスタスタンプ。0=VRAMが操作されてこのラスタを再描画する必要がある
   658:   public static int crtAllStamp;  //全再描画スタンプ
   659: 
   660:   //  768x512ドット256色
   661:   //    作り方
   662:   //      画面モードを768x512ドットにしてグラフィック画面だけ512x512ドット256色にする
   663:   //      512x512ドット256色ページ0に768x512ドット256色の画像の左1/3と中央1/3を描く
   664:   //      512x512ドット256色ページ1に768x512ドット256色の画像の右1/3と中央1/3を描く
   665:   //      0<=y<=511のすべてのラスタについて
   666:   //        256<=x<=511でページ0をOFF、ページ1をON、768<=xでページ0をON、ページ1をOFFにする
   667:   //        256<=x<=511の範囲はページ0とページ1の両方に画像が描かれているのでどこで切り替えてもよいが、
   668:   //        10MHzのとき256<=x<=511の期間は73サイクル、NOP命令18個分しかないので、かなりシビアな処理になる
   669:   //        (768x512ドットの画面は69.552MHzを2分周して作られるので28.755ns/dot)
   670:   //    方針
   671:   //      描画する必要のないラスタをラスタスタンプと全再描画スタンプが一致するかどうかで見分けているが、
   672:   //      この全再描画スタンプを流用する
   673:   //      水平映像期間開始時から終了時までの間に全再描画スタンプが変化したとき、
   674:   //      水平映像期間終了時にラスタの後半を再描画する
   675:   //      分割する必要がないときのオーバーヘッドをなるべく減らす
   676:   //        分割する必要がないときの1ラスタあたりのオーバーヘッド
   677:   //          static変数のコピーが1回
   678:   //          static変数同士の比較が2回
   679:   //          if分岐が2回
   680:   //    手順
   681:   //      水平映像期間開始時
   682:   //        いろいろ
   683:   //        crtBeginningAllStamp=crtAllStamp;
   684:   //        ラスタ描画(src,dst);
   685:   //      水平映像期間終了時
   686:   //        if crtBeginningAllStamp!=crtAllStamp;
   687:   //          ラスタ描画(src,dst);
   688:   //        ラスタ番号++
   689:   //        いろいろ
   690:   //      ラスタ描画(src,dst);
   691:   //        int da=dst<<XEiJ.PNL_BM_OFFSET_BITS;
   692:   //        int db=da+XEiJ.pnlScreenWidth;
   693:   //        if crtBeginningAllStamp!=crtAllStamp
   694:   //          int half=XEiJ.pnlScreenWidth>>4<<3;
   695:   //          sx+=half;
   696:   //          gx1st+=half<<1;
   697:   //          gx2nd+=half<<1;
   698:   //          gx3rd+=half<<1;
   699:   //          gx4th+=half<<1;
   700:   //          tc=tc+(half>>3)&127;
   701:   //          gx+=half;
   702:   //          da+=half;
   703:   //    参考
   704:   //      https://twitter.com/kugimoto0715/status/800231367699116032
   705:   //      ArimacさんのBMPL.X/LPICL.Xの添付ドキュメントX256.DOC
   706:   public static int crtBeginningAllStamp;  //水平映像期間開始時の全再描画スタンプ
   707: 
   708:   public static int crtRasterNumber;  //ラスタ番号。0=垂直同期パルス開始ラスタ
   709:   public static int crtDataY;  //データY座標
   710:   public static int crtScreenY;  //スクリーンY座標
   711:   public static int crtFrameParity;  //フレームパリティ
   712: 
   713:   //  水平フロントポーチでアクションを起こすべきラスタかどうかの判別を高速化する
   714:   //  垂直空白期間
   715:   //  crtRasterHashIdle = ((crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO) |
   716:   //                       (RasterBreakPoint.RBP_ON && RasterBreakPoint.rbpActiveBreakRaster >= 0 ?
   717:   //                        CRT_RASTER_HASH_MSB >>> RasterBreakPoint.rbpActiveBreakRaster : CRT_RASTER_HASH_ZERO) |
   718:   //                       (crtR09IRQRasterCurr <= crtR04VFrontEndCurr ?
   719:   //                        CRT_RASTER_HASH_MSB >>> crtR09IRQRasterCurr |
   720:   //                        CRT_RASTER_HASH_MSB >>> (crtR09IRQRasterCurr < crtR04VFrontEndCurr ? crtR09IRQRasterCurr + 1 : 0) :
   721:   //                        CRT_RASTER_HASH_ZERO);
   722:   //                       CRT_RASTER_HASH_MSB >>> crtVDispStart |
   723:   //                       CRT_RASTER_HASH_MSB >>> crtR04VFrontEndCurr + 1);
   724:   //  垂直映像期間
   725:   //  crtRasterHashDisp = ((crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO) |
   726:   //                       (RasterBreakPoint.RBP_ON && RasterBreakPoint.rbpActiveBreakRaster >= 0 ?
   727:   //                        CRT_RASTER_HASH_MSB >>> RasterBreakPoint.rbpActiveBreakRaster : CRT_RASTER_HASH_ZERO) |
   728:   //                       (crtR09IRQRasterCurr <= crtR04VFrontEndCurr ?
   729:   //                        CRT_RASTER_HASH_MSB >>> crtR09IRQRasterCurr |
   730:   //                        CRT_RASTER_HASH_MSB >>> (crtR09IRQRasterCurr < crtR04VFrontEndCurr ? crtR09IRQRasterCurr + 1 : 0) :
   731:   //                        CRT_RASTER_HASH_ZERO);
   732:   //                       CRT_RASTER_HASH_MSB >>> crtVIdleStart);
   733:   public static final boolean CRT_RASTER_HASH_ON = true;
   734:   public static final long CRT_RASTER_HASH_ZERO = 0x0000000000000000L;  //crtRasterHashと同じ型の0
   735:   public static final long CRT_RASTER_HASH_MSB  = 0x8000000000000000L;  //crtRasterHashと同じ型のMSBだけセットした値
   736:   public static long crtRasterHashIdle;  //垂直空白期間。intまたはlong
   737:   public static long crtRasterHashDisp;  //垂直映像期間。intまたはlong
   738:   //public static final int CRT_RASTER_HASH_ZERO = 0x00000000;  //crtRasterHashと同じ型の0
   739:   //public static final int CRT_RASTER_HASH_MSB  = 0x80000000;  //crtRasterHashと同じ型のMSBだけセットした値
   740:   //public static int crtRasterHashIdle;  //垂直空白期間。intまたはlong
   741:   //public static int crtRasterHashDisp;  //垂直映像期間。intまたはlong
   742: 
   743:   public static TickerQueue.Ticker crtTicker;
   744:   public static long crtClock;
   745: 
   746:   public static long crtContrastClock;  //次にコントラストを変更する時刻
   747:   public static long crtCaptureClock;  //次に画面をキャプチャする時刻
   748:   public static long crtFrameTaskClock;  //Math.min(crtContrastClock,crtCaptureClock)
   749: 
   750:   //間欠描画
   751:   //  描画するフレームの間隔を空けることで描画の負荷を減らす
   752:   //  間欠間隔 interval>=0
   753:   //    interval=0    デフォルト。すべてのフレームが描画フレーム
   754:   //    interval=1..  描画フレームの後の少なくともintervalフレームを省略フレームとすることで描画フレームの割合を1/(interval+1)以下に抑える
   755:   //                  インターレースの場合は間欠間隔を偶数に切り上げることで偶数フレームと奇数フレームが交互に更新されるようにする
   756:   //  間欠カウンタ counter>=0
   757:   //    counter=0     描画フレーム。画面が更新されたときは描画するフレーム
   758:   //    counter=1~n  省略フレーム。常に描画しないフレーム
   759:   //  描画フレームの垂直映像期間の水平映像期間に入ったとき
   760:   //    ラスタが更新されたとき
   761:   //      描画フレーム(counter==0)のとき
   762:   //        画面の合成と16bit→32bitの変換を行う
   763:   //        更新されたラスタの範囲を記録する
   764:   //      省略フレーム(counter!=0)のとき
   765:   //        何もしない
   766:   //  垂直映像期間終了ラスタから出たとき
   767:   //    描画フレーム(counter==0)のとき
   768:   //      更新されたラスタがあったとき
   769:   //        更新されたラスタの範囲を描画する
   770:   //        counter=interval
   771:   //      更新されたラスタがなかったとき
   772:   //        何もしない
   773:   //    省略フレーム(counter!=0)のとき
   774:   //      counter--
   775:   //!!! XEiJ.PNL_USE_THREADとCRTC.CRT_ENABLE_INTERMITTENTを同時にtrueにしないこと
   776:   public static final boolean CRT_ENABLE_INTERMITTENT = false;  //true=間欠描画を有効にする
   777:   public static int crtIntermittentInterval;  //間欠間隔。描画フレームの間に挟む省略フレームの数の下限
   778:   public static int crtIntermittentCounter;  //間欠カウンタ。0=描画フレーム,1..interval=省略フレーム
   779: 
   780:   //走査線エフェクト
   781:   enum ScanlineEffect {
   782:     OFF {  //なし。そのままコピー
   783:       @Override public void drawRaster (int screenY) {
   784:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   785:         System.arraycopy (XEiJ.pnlBM, da - XEiJ.PNL_BM_WIDTH,  //from
   786:                           XEiJ.pnlBM, da,  //to
   787:                           XEiJ.pnlScreenWidth);  //length
   788:       }
   789:     },
   790:     WEAK {  //弱。7/8倍してコピー
   791:       @Override public void drawRaster (int screenY) {
   792:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   793:         int db = da + XEiJ.pnlScreenWidth;
   794:         while (da < db) {
   795:           int t;
   796:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH    )];
   797:           XEiJ.pnlBM[da    ] = t - ((t >> 3) & 0x001f1f1f);
   798:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 1)];
   799:           XEiJ.pnlBM[da + 1] = t - ((t >> 3) & 0x001f1f1f);
   800:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 2)];
   801:           XEiJ.pnlBM[da + 2] = t - ((t >> 3) & 0x001f1f1f);
   802:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 3)];
   803:           XEiJ.pnlBM[da + 3] = t - ((t >> 3) & 0x001f1f1f);
   804:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 4)];
   805:           XEiJ.pnlBM[da + 4] = t - ((t >> 3) & 0x001f1f1f);
   806:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 5)];
   807:           XEiJ.pnlBM[da + 5] = t - ((t >> 3) & 0x001f1f1f);
   808:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 6)];
   809:           XEiJ.pnlBM[da + 6] = t - ((t >> 3) & 0x001f1f1f);
   810:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 7)];
   811:           XEiJ.pnlBM[da + 7] = t - ((t >> 3) & 0x001f1f1f);
   812:           da += 8;
   813:         }
   814:       }
   815:     },
   816:     MEDIUM {  //中。3/4倍してコピー
   817:       @Override public void drawRaster (int screenY) {
   818:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   819:         int db = da + XEiJ.pnlScreenWidth;
   820:         while (da < db) {
   821:           int t;
   822:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH    )];
   823:           XEiJ.pnlBM[da    ] = t - ((t >> 2) & 0x003f3f3f);
   824:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 1)];
   825:           XEiJ.pnlBM[da + 1] = t - ((t >> 2) & 0x003f3f3f);
   826:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 2)];
   827:           XEiJ.pnlBM[da + 2] = t - ((t >> 2) & 0x003f3f3f);
   828:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 3)];
   829:           XEiJ.pnlBM[da + 3] = t - ((t >> 2) & 0x003f3f3f);
   830:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 4)];
   831:           XEiJ.pnlBM[da + 4] = t - ((t >> 2) & 0x003f3f3f);
   832:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 5)];
   833:           XEiJ.pnlBM[da + 5] = t - ((t >> 2) & 0x003f3f3f);
   834:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 6)];
   835:           XEiJ.pnlBM[da + 6] = t - ((t >> 2) & 0x003f3f3f);
   836:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 7)];
   837:           XEiJ.pnlBM[da + 7] = t - ((t >> 2) & 0x003f3f3f);
   838:           da += 8;
   839:         }
   840:       }
   841:     },
   842:     STRONG {  //強。1/2倍してコピー
   843:       @Override public void drawRaster (int screenY) {
   844:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   845:         int db = da + XEiJ.pnlScreenWidth;
   846:         while (da < db) {
   847:           XEiJ.pnlBM[da    ] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH    )] >> 1) & 0xff7f7f7f;
   848:           XEiJ.pnlBM[da + 1] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 1)] >> 1) & 0xff7f7f7f;
   849:           XEiJ.pnlBM[da + 2] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 2)] >> 1) & 0xff7f7f7f;
   850:           XEiJ.pnlBM[da + 3] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 3)] >> 1) & 0xff7f7f7f;
   851:           XEiJ.pnlBM[da + 4] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 4)] >> 1) & 0xff7f7f7f;
   852:           XEiJ.pnlBM[da + 5] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 5)] >> 1) & 0xff7f7f7f;
   853:           XEiJ.pnlBM[da + 6] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 6)] >> 1) & 0xff7f7f7f;
   854:           XEiJ.pnlBM[da + 7] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 7)] >> 1) & 0xff7f7f7f;
   855:           da += 8;
   856:         }
   857:       }
   858:     },
   859:     BLACK {  //黒
   860:       @Override public void drawRaster (int screenY) {
   861:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   862:         int db = da + XEiJ.pnlScreenWidth;
   863:         Arrays.fill (XEiJ.pnlBM,  //array
   864:                      da,  //from
   865:                      db,  //to
   866:                      0xff000000);  //value
   867:       }
   868:     };
   869:     public abstract void drawRaster (int screenY);
   870:   }  //enum ScanlineEffect
   871:   public static ScanlineEffect crtScanlineEffect;
   872: 
   873:   //1024ドットノンインターレース
   874:   //  R04/R05/R06/R07/R09の幅を10ビットから11ビットに拡張する
   875:   //  垂直映像期間のラスタ数R07-R06が1024を超えてはならない
   876:   public static boolean crtEleventhBitRequest;
   877:   public static boolean crtEleventhBit;
   878:   public static int crtVerticalMask;  //0x03ffまたは0x07ff
   879: 
   880:   //テキスト画面のスクロール
   881:   //  グラフィック画面は球面スクロールでデータのアドレスが1ラスタ毎にループするが、
   882:   //  テキスト画面は円筒スクロールでデータのアドレスが4ラスタ(=1ラスタブロック)毎にループするので、
   883:   //  X方向のスクロール位置に範囲外の値を指定すると画面が乱れる
   884:   //  改造メニューで球面スクロールを選択できる
   885:   public static boolean crtSphericalScrolling;  //false=円筒スクロール,true=球面スクロール
   886:   public static int crtMask3;
   887:   public static int crtMaskMinus4;
   888:   public static int crtMask511;
   889:   public static void crtSetSphericalScrolling (boolean spherical) {
   890:     crtSphericalScrolling = spherical;
   891:     crtMask3 = spherical ? 0 : 3;  //0:3
   892:     crtMaskMinus4 = ~crtMask3;  //-1:-4
   893:     crtMask511 = crtMask3 << 7 | 127;  //127:511
   894:     crtAllStamp += 2;
   895:   }
   896: 
   897:   //CRTC R00のビット0
   898:   public static boolean crtR00Bit0Zero;  //false=1に固定する,true=0を書き込める
   899: 
   900:   //crtInit ()
   901:   //  CRTコントローラを初期化する
   902:   public static void crtInit () {
   903:     //if (CRT_EXTENDED_GRAPHIC) {
   904:     //  crtExtendedGraphicRequest = false;
   905:     //}
   906:     //crtR12GrXPort = new int[4];
   907:     //crtR12GrXMask = new int[4];
   908:     //crtR12GrXTest = new int[4];
   909:     //crtR12GrXCurr = new int[4];
   910:     //crtR13GrYPort = new int[4];
   911:     //crtR13GrYMask = new int[4];
   912:     //crtR13GrYTest = new int[4];
   913:     //crtR13GrYCurr = new int[4];
   914:     //crtR13GrYZero = new int[4];
   915:     //crtR13GrYZeroLast = new int[4];
   916:     //crtRasterStamp = new int[1024 + 15];  //1024以降はスプライトコントローラが更新してしまうので追加したダミー
   917: 
   918:     //走査線エフェクト
   919:     switch (Settings.sgsGetString ("scanline").toLowerCase ()) {
   920:     case "off":
   921:       crtScanlineEffect = ScanlineEffect.OFF;
   922:       break;
   923:     case "weak":
   924:       crtScanlineEffect = ScanlineEffect.WEAK;
   925:       break;
   926:     case "medium":
   927:       crtScanlineEffect = ScanlineEffect.MEDIUM;
   928:       break;
   929:     case "strong":
   930:       crtScanlineEffect = ScanlineEffect.STRONG;
   931:       break;
   932:     case "black":
   933:       crtScanlineEffect = ScanlineEffect.BLACK;
   934:       break;
   935:     }
   936: 
   937:     //ドットクロックオシレータ
   938:     {
   939:       String[] a = (Settings.sgsGetString ("dotclock") + ",,,0").split (",");
   940:       for (int i = 0; i < 3; i++) {
   941:         int freq = -1;
   942:         try {
   943:           freq = Integer.parseInt (a[i], 10);
   944:         } catch (NumberFormatException nfe) {
   945:         }
   946:         crtFreqsRequest[i] = CRT_MIN_FREQ <= freq && freq <= CRT_MAX_FREQ ? freq : CRT_DEFAULT_FREQS[i];
   947:       }
   948:     }
   949: 
   950:     //1024ドットノンインターレース
   951:     crtEleventhBitRequest = Settings.sgsGetOnOff ("eleventhbit");
   952: 
   953:     //テキスト画面のスクロール
   954:     crtSphericalScrolling = Settings.sgsGetOnOff ("sphericalscrolling");
   955:     crtMask3 = 3;
   956:     crtMaskMinus4 = -4;
   957:     crtMask511 = 511;
   958: 
   959:     //CRTC R00のビット0
   960:     crtR00Bit0Zero = Settings.sgsGetOnOff ("r00bit0zero");
   961: 
   962:     if (true) {
   963:       crtCCPlane0 = false;
   964:       crtCCPlane1 = false;
   965:       crtCCPlane2 = false;
   966:       crtCCPlane3 = false;
   967:       crtSimPlane0 = false;
   968:       crtSimPlane1 = false;
   969:       crtSimPlane2 = false;
   970:       crtSimPlane3 = false;
   971:       crtSimAccess = false;
   972:       crtBitMask = false;
   973:     }
   974:     crtReset ();
   975:   }  //crtInit()
   976: 
   977:   //crtTini ()
   978:   //  後始末
   979:   public static void crtTini () {
   980: 
   981:     //走査線エフェクト
   982:     Settings.sgsPutString ("scanline",
   983:                            crtScanlineEffect == ScanlineEffect.OFF ? "off" :
   984:                            crtScanlineEffect == ScanlineEffect.WEAK ? "weak" :
   985:                            crtScanlineEffect == ScanlineEffect.MEDIUM ? "medium" :
   986:                            crtScanlineEffect == ScanlineEffect.STRONG ? "strong" :
   987:                            crtScanlineEffect == ScanlineEffect.BLACK ? "black" :
   988:                            "");
   989: 
   990:     //ドットクロックオシレータ
   991:     {
   992:       StringBuilder sb = new StringBuilder ();
   993:       for (int i = 0; i < 3; i++) {
   994:         if (0 < i) {
   995:           sb.append (',');
   996:         }
   997:         if (crtFreqsRequest[i] != CRT_DEFAULT_FREQS[i]) {
   998:           sb.append (crtFreqsRequest[i]);
   999:         }
  1000:       }
  1001:       Settings.sgsPutString ("dotclock", sb.toString ());
  1002:     }
  1003: 
  1004:     //1024ドットノンインターレース
  1005:     Settings.sgsPutOnOff ("eleventhbit", crtEleventhBitRequest);
  1006: 
  1007:     //テキスト画面のスクロール
  1008:     Settings.sgsPutOnOff ("sphericalscrolling", crtSphericalScrolling);
  1009: 
  1010:     //CRTC R00のビット0
  1011:     Settings.sgsPutOnOff ("r00bit0zero", crtR00Bit0Zero);
  1012: 
  1013:   }  //crtTini
  1014: 
  1015:   //crtReset ()
  1016:   //  リセット
  1017:   //  CRTCのレジスタを初期化する
  1018:   //  レジスタが設定されてCRT_RESTART_DELAYが経過するまでCRTCの動作を停止する
  1019:   //  以下で呼び出される
  1020:   //    初期化
  1021:   //    MPUのreset命令
  1022:   public static void crtReset () {
  1023:     if (CRT_EXTENDED_GRAPHIC) {
  1024:       crtExtendedGraphicOn = crtExtendedGraphicRequest;
  1025:       if (crtExtendedGraphicOn) {
  1026:         System.out.println (Multilingual.mlnJapanese ?
  1027:                             "拡張グラフィック画面が有効になりました" :
  1028:                             "Extended graphic screen has been activated");
  1029:       }
  1030:     }
  1031: 
  1032:     //ドットクロックオシレータ
  1033:     for (int i = 0; i < 3; i++) {
  1034:       crtFreqs[i] = crtFreqsRequest[i];
  1035:     }
  1036: 
  1037:     //1024ドットノンインターレース
  1038:     crtEleventhBit = crtEleventhBitRequest;
  1039:     crtVerticalMask = crtEleventhBit ? 0x07ff : 0x03ff;
  1040: 
  1041:     crtR00HFrontEndPort = 0;
  1042:     crtR00HFrontEndMask = 0;
  1043:     crtR00HFrontEndTest = 0;
  1044:     crtR00HFrontEndCurr = 0;
  1045:     crtR01HSyncEndPort = 0;
  1046:     crtR01HSyncEndMask = 0;
  1047:     crtR01HSyncEndTest = 0;
  1048:     crtR01HSyncEndCurr = 0;
  1049:     crtR02HBackEndPort = 0;
  1050:     crtR02HBackEndMask = 0;
  1051:     crtR02HBackEndTest = 0;
  1052:     crtR02HBackEndCurr = 0;
  1053:     crtR03HDispEndPort = 0;
  1054:     crtR03HDispEndMask = 0;
  1055:     crtR03HDispEndTest = 0;
  1056:     crtR03HDispEndCurr = 0;
  1057:     crtR04VFrontEndPort = 0;
  1058:     crtR04VFrontEndMask = 0;
  1059:     crtR04VFrontEndTest = 0;
  1060:     crtR04VFrontEndCurr = 0;
  1061:     crtR05VSyncEndPort = 0;
  1062:     crtR05VSyncEndMask = 0;
  1063:     crtR05VSyncEndTest = 0;
  1064:     crtR05VSyncEndCurr = 0;
  1065:     crtR06VBackEndPort = 0;
  1066:     crtR06VBackEndMask = 0;
  1067:     crtR06VBackEndTest = 0;
  1068:     crtR06VBackEndCurr = 0;
  1069:     crtVDispStart = 0;
  1070:     crtR07VDispEndPort = 0;
  1071:     crtR07VDispEndMask = 0;
  1072:     crtR07VDispEndTest = 0;
  1073:     crtR07VDispEndCurr = 0;
  1074:     crtVIdleStart = 0;
  1075:     crtR08Adjust = 0;
  1076:     crtR09IRQRasterPort = 1023;
  1077:     crtR09IRQRasterMask = 0;
  1078:     crtR09IRQRasterTest = 1023;
  1079:     crtR09IRQRasterCurr = 1023;
  1080:     crtR10TxXPort = 0;
  1081:     crtR10TxXMask = 0;
  1082:     crtR10TxXTest = 0;
  1083:     crtR10TxXCurr = 0;
  1084:     crtR11TxYPort = 0;
  1085:     crtR11TxYMask = 0;
  1086:     crtR11TxYTest = 0;
  1087:     crtR11TxYCurr = 0;
  1088:     crtR11TxYZero = 0;
  1089:     crtR11TxYZeroLast = -1;
  1090:     for (int i = 0; i < 4; i++) {
  1091:       crtR12GrXPort[i] = 0;
  1092:       crtR12GrXMask[i] = 0;
  1093:       crtR12GrXTest[i] = 0;
  1094:       crtR12GrXCurr[i] = 0;
  1095:       crtR13GrYPort[i] = 0;
  1096:       crtR13GrYMask[i] = 0;
  1097:       crtR13GrYTest[i] = 0;
  1098:       crtR13GrYCurr[i] = 0;
  1099:       crtR13GrYZero[i] = 0;
  1100:       crtR13GrYZeroLast[i] = -1;
  1101:     }
  1102:     crtTextStorage = 0;
  1103:     crtGraphicStorage = 0;
  1104:     crtMemoryModePort = 0;
  1105:     crtMemoryModeMask = 0;
  1106:     crtMemoryModeTest = 0;
  1107:     crtMemoryModeCurr = 0;
  1108:     crtHighResoPort = 0;
  1109:     crtHighResoMask = 0;
  1110:     crtHighResoTest = 0;
  1111:     crtHighResoCurr = 0;
  1112:     crtVResoPort = 0;
  1113:     crtVResoMask = 0;
  1114:     crtVResoTest = 0;
  1115:     crtVResoCurr = 0;
  1116:     crtHResoPort = 0;
  1117:     crtHResoMask = 0;
  1118:     crtHResoTest = 0;
  1119:     crtHResoCurr = 0;
  1120:     if (false) {
  1121:       crtCCPlane0 = false;
  1122:       crtCCPlane1 = false;
  1123:       crtCCPlane2 = false;
  1124:       crtCCPlane3 = false;
  1125:       crtSimPlane0 = false;
  1126:       crtSimPlane1 = false;
  1127:       crtSimPlane2 = false;
  1128:       crtSimPlane3 = false;
  1129:       crtSimAccess = false;
  1130:       crtBitMask = false;
  1131:     }
  1132:     crtR22SrcBlock = 0;
  1133:     crtR22DstBlock = 0;
  1134:     crtR23Mask = 0x0000;
  1135:     crtRasterCopyOn = false;
  1136:     crtClearStandby = false;
  1137:     crtClearFrames = 0;
  1138: 
  1139:     crtHRLPort = 0;
  1140:     crtHRLMask = 0;
  1141:     crtHRLTest = 0;
  1142:     crtHRLCurr = 0;
  1143:     XEiJ.pnlStretchMode = 1.0F;
  1144:     XEiJ.pnlStereoscopicShutter = 0;  //左右OPEN
  1145:     crtDuplication = false;
  1146:     crtInterlace = false;
  1147:     crtSlit = false;
  1148:     crtDupExceptSp = false;
  1149:     crtHSyncColumn = 2;
  1150:     crtHBackColumn = 2;
  1151:     crtHDispColumn = 2;
  1152:     crtHFrontColumn = 2;
  1153:     crtHTotalColumn = 8;
  1154:     crtVsyncMultiplier = 1.0;
  1155:     crtColumnTime = 0;
  1156:     crtHSyncLength = 0;
  1157:     crtHBackLength = 0;
  1158:     crtHDispLength = 0;
  1159:     crtHFrontLength = 0;
  1160:     crtHBackDispLength = 0;
  1161:     crtTotalLength = 0L;
  1162:     crtTotalLengthMNP = 0L;
  1163: 
  1164:     if (!XEiJ.PNL_USE_THREAD) {
  1165:       crtDirtyY0 = -1;
  1166:       crtDirtyY1 = -1;
  1167:     }
  1168: 
  1169:     Arrays.fill (crtRasterStamp, 0);
  1170:     crtAllStamp = 1;  //初回は全再描画
  1171: 
  1172:     crtBeginningAllStamp = 1;
  1173: 
  1174:     crtRasterNumber = 0;
  1175:     crtDataY = 0;
  1176:     crtScreenY = 0;
  1177:     crtFrameParity = 0;
  1178: 
  1179:     crtRasterHashIdle = CRT_RASTER_HASH_ZERO;
  1180:     crtRasterHashDisp = CRT_RASTER_HASH_ZERO;
  1181: 
  1182:     crtContrastClock = XEiJ.FAR_FUTURE;
  1183:     crtCaptureClock = XEiJ.FAR_FUTURE;
  1184:     crtFrameTaskClock = Math.min (crtContrastClock, crtCaptureClock);
  1185: 
  1186:     if (CRT_ENABLE_INTERMITTENT) {  //間欠描画
  1187:       //crtIntermittentInterval = 0;
  1188:       crtIntermittentCounter = 0;
  1189:     }
  1190: 
  1191:     if (crtTicker != null) {
  1192:       TickerQueue.tkqRemove (crtTicker);
  1193:       crtTicker = null;
  1194:     }
  1195:     crtClock = XEiJ.FAR_FUTURE;  //停止
  1196: 
  1197:   }  //crtReset()
  1198: 
  1199:   //crtRestart ()
  1200:   //  CRTCのレジスタが設定されたのでCRT_RESTART_DELAY後にInitialStageを開始する
  1201:   public static void crtRestart () {
  1202:     if (crtTicker != null) {
  1203:       TickerQueue.tkqRemove (crtTicker);
  1204:     }
  1205:     TickerQueue.tkqAdd (crtTicker = InitialStage, crtClock = XEiJ.mpuClockTime + CRT_RESTART_DELAY);  //スクリーンの初期化へ
  1206:   }  //crtRestart()
  1207: 
  1208:   //crtStereoscopicStart ()
  1209:   //  垂直映像開始で
  1210:   public static void crtStereoscopicStart () {
  1211:     if (XEiJ.PNL_USE_THREAD) {
  1212:       XEiJ.pnlBM = (XEiJ.pnlStereoscopicShutter != 1 ? XEiJ.pnlBMLeftArray[XEiJ.pnlBMWrite & 3] :  //0=3=左右OPENまたは2=左OPENのとき左に描く
  1213:                     XEiJ.pnlBMRightArray[XEiJ.pnlBMWrite & 3]);  //1=右OPENのとき右に描く
  1214:     } else {
  1215:       XEiJ.pnlBM = (XEiJ.pnlStereoscopicShutter != 1 ? XEiJ.pnlBMLeft :  //0=3=左右OPENまたは2=左OPENのとき左に描く
  1216:                     XEiJ.pnlBMRight);  //1=右OPENのとき右に描く
  1217:     }
  1218:     crtAllStamp += 2;
  1219:   }
  1220: 
  1221:   //crtStereoscopicDrawRaster (screenY)
  1222:   //  drawRasterの後で
  1223:   public static void crtStereoscopicDrawRaster (int screenY) {
  1224:     if (XEiJ.pnlStereoscopicShutter == 0 ||
  1225:         XEiJ.pnlStereoscopicShutter == 3) {  //0=3=左右OPENのとき
  1226:       if (XEiJ.PNL_USE_THREAD) {
  1227:         System.arraycopy (XEiJ.pnlBMLeftArray[XEiJ.pnlBMWrite & 3], screenY << XEiJ.PNL_BM_OFFSET_BITS,
  1228:                           XEiJ.pnlBMRightArray[XEiJ.pnlBMWrite & 3], screenY << XEiJ.PNL_BM_OFFSET_BITS,
  1229:                           XEiJ.pnlScreenWidth);  //左から右へコピー
  1230:       } else {
  1231:         System.arraycopy (XEiJ.pnlBMLeft, screenY << XEiJ.PNL_BM_OFFSET_BITS,
  1232:                           XEiJ.pnlBMRight, screenY << XEiJ.PNL_BM_OFFSET_BITS,
  1233:                           XEiJ.pnlScreenWidth);  //左から右へコピー
  1234:       }
  1235:     }
  1236:   }
  1237: 
  1238:   //crtUpdateScreen ()
  1239:   //  スクリーンを更新する
  1240:   public static void crtUpdateScreen () {
  1241:     if (!XEiJ.PNL_USE_THREAD) {
  1242:       if (XEiJ.pnlZoomRatioOutY == 1 << 16) {  //拡大なし
  1243:         XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY1 + crtDirtyY0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, crtDirtyY1 - crtDirtyY0 + 1);
  1244:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn && XEiJ.pnlStereoscopicMethod == XEiJ.PNL_TOP_AND_BOTTOM) {
  1245:           XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY3 + crtDirtyY0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, crtDirtyY1 - crtDirtyY0 + 1);
  1246:         }
  1247:       } else {  //拡大あり
  1248:         int y0 = (crtDirtyY0 - 1) * XEiJ.pnlZoomRatioOutY >> 16;
  1249:         int y1 = (crtDirtyY1 + 2) * (XEiJ.pnlZoomRatioOutY + 1) >> 16;
  1250:         XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY1 + y0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, y1 - y0);
  1251:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn && XEiJ.pnlStereoscopicMethod == XEiJ.PNL_TOP_AND_BOTTOM) {
  1252:           XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY3 + y0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, y1 - y0);
  1253:         }
  1254:       }
  1255:       crtDirtyY0 = -1;
  1256:     }
  1257:   }
  1258: 
  1259:   //crtRepaint ()
  1260:   //  再描画
  1261:   //  MPUが止まっていても再描画する
  1262:   //  ダーティラスタのマークはそのままでMFPとのやりとりも行わない
  1263:   //  ラスタ割り込みを使用して作られている画面は再現できない
  1264:   public static void crtRepaint () {
  1265:     crtBeginningAllStamp = crtAllStamp;
  1266:     int l = Math.max (0, Math.min (1024, crtR07VDispEndCurr - crtR06VBackEndCurr));
  1267:     if (crtDuplication) {  //ラスタ2度読み
  1268:       for (int screenY = 0; screenY < l; screenY += 2) {
  1269:         if (SpriteScreen.SPR_THREE_STEPS) {
  1270:           SpriteScreen.sprStep1 (screenY >> 1);
  1271:           SpriteScreen.sprStep2 (screenY >> 1);
  1272:         }
  1273:         VideoController.vcnMode.drawRaster (screenY >> 1, screenY, false);  //スクリーンY座標へ描画
  1274:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1275:           crtStereoscopicDrawRaster (screenY);
  1276:         }
  1277:         //偶数ラスタを奇数ラスタにコピーする
  1278:         System.arraycopy (XEiJ.pnlBM, screenY << XEiJ.PNL_BM_OFFSET_BITS, XEiJ.pnlBM, screenY + 1 << XEiJ.PNL_BM_OFFSET_BITS, XEiJ.pnlScreenWidth);
  1279:       }
  1280:     } else if (crtSlit) {  //スリット
  1281:       for (int screenY = 0; screenY < l; screenY += 2) {
  1282:         if (SpriteScreen.SPR_THREE_STEPS) {
  1283:           SpriteScreen.sprStep1 (screenY >> 1);
  1284:           SpriteScreen.sprStep2 (screenY >> 1);
  1285:         }
  1286:         VideoController.vcnMode.drawRaster (screenY >> 1, screenY, false);  //スクリーンY座標へ描画
  1287:         crtScanlineEffect.drawRaster (screenY + 1);  //走査線エフェクト
  1288:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1289:           crtStereoscopicDrawRaster (screenY);
  1290:           crtStereoscopicDrawRaster (screenY + 1);
  1291:         }
  1292:       }
  1293:     } else if (crtDupExceptSp) {  //ラスタ2度読み(スプライトを除く)
  1294:       for (int screenY = 0; screenY < l; screenY++) {
  1295:         if (SpriteScreen.SPR_THREE_STEPS) {
  1296:           SpriteScreen.sprStep1 (screenY);
  1297:           SpriteScreen.sprStep2 (screenY);
  1298:         }
  1299:         VideoController.vcnMode.drawRaster (screenY >> 1, screenY, false);  //スクリーンY座標へ描画
  1300:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1301:           crtStereoscopicDrawRaster (screenY);
  1302:         }
  1303:       }
  1304:     } else {  //ノーマル,インターレース
  1305:       for (int screenY = 0; screenY < l; screenY++) {
  1306:         if (SpriteScreen.SPR_THREE_STEPS) {
  1307:           SpriteScreen.sprStep1 (screenY);
  1308:           SpriteScreen.sprStep2 (screenY);
  1309:         }
  1310:         VideoController.vcnMode.drawRaster (screenY, screenY, false);  //スクリーンY座標へ描画
  1311:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1312:           crtStereoscopicDrawRaster (screenY);
  1313:         }
  1314:       }
  1315:     }
  1316:     XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY1, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, l);
  1317:     if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn && XEiJ.pnlStereoscopicMethod == XEiJ.PNL_TOP_AND_BOTTOM) {
  1318:       XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY3, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, l);
  1319:     }
  1320:   }  //crtRepaint()
  1321: 
  1322:   //crtDoFrameTask ()
  1323:   //  垂直同期パルスに行う処理
  1324:   //  垂直同期パルスに入ったときXEiJ.mpuClockTime>=crtFrameTaskClockならば呼び出す
  1325:   //  コントラストの調整
  1326:   //  画面キャプチャ
  1327:   public static void crtDoFrameTask () {
  1328:     if (XEiJ.mpuClockTime >= crtContrastClock) {
  1329:       VideoController.vcnCurrentScaledContrast += VideoController.vcnCurrentScaledContrast < VideoController.vcnTargetScaledContrast ? 1 : -1;
  1330:       VideoController.vcnSetContrast (VideoController.vcnCurrentScaledContrast);
  1331:       if (VideoController.vcnCurrentScaledContrast == VideoController.vcnTargetScaledContrast) {
  1332:         crtContrastClock = XEiJ.FAR_FUTURE;
  1333:       } else {
  1334:         crtContrastClock += VideoController.VCN_CONTRAST_DELAY;
  1335:       }
  1336:     }
  1337:     if (XEiJ.mpuClockTime >= crtCaptureClock) {
  1338:       GIFAnimation.gifCaptureFrame ();
  1339:     }
  1340:     crtFrameTaskClock = Math.min (crtContrastClock, crtCaptureClock);
  1341:   }  //crtDoFrameTask()
  1342: 
  1343:   //crtSetMemoryMode (textStorage, graphicStorage, memoryMode)
  1344:   //  テキストストレージ(R20 12)
  1345:   //  グラフィックストレージ(R20 11)
  1346:   //  メモリモード(R20 10-8)を変更する
  1347:   public static void crtSetMemoryMode (int textStorage, int graphicStorage, int memoryMode) {
  1348:     boolean updateMemoryMap = false;  //メモリマップを更新するか
  1349:     textStorage &= 1;
  1350:     if (crtTextStorage != textStorage) {  //テキストストレージを変更する
  1351:       crtTextStorage = textStorage;
  1352:     }
  1353:     graphicStorage &= 1;
  1354:     if (crtGraphicStorage != graphicStorage) {  //グラフィックストレージを変更する
  1355:       crtGraphicStorage = graphicStorage;
  1356:       updateMemoryMap = true;  //メモリマップを更新する
  1357:     }
  1358:     memoryMode &= 7;
  1359:     crtMemoryModePort = memoryMode;
  1360:     int curr = crtMemoryModeMask == 0 ? crtMemoryModePort : crtMemoryModeTest;
  1361:     if (crtMemoryModeCurr != curr) {  //メモリモードを変更する
  1362:       crtMemoryModeCurr = curr;
  1363:       updateMemoryMap = true;  //メモリマップを更新する
  1364:     }
  1365:     if (updateMemoryMap) {  //メモリマップを更新する
  1366:       if (crtGraphicStorage != 0) {  //グラフィックストレージON
  1367:         if (CRT_EXTENDED_GRAPHIC && crtExtendedGraphicOn &&  //拡張グラフィック画面がONかつ
  1368:             (crtMemoryModeCurr == 5 || crtMemoryModeCurr == 7)) {  //メモリモード5または7のとき
  1369:           //メモリモード7相当
  1370:           XEiJ.busSuper (MemoryMappedDevice.MMD_GJ0, 0x00c00000, 0x00e00000);
  1371:         } else {
  1372:           //メモリモード3相当
  1373:           XEiJ.busSuper (MemoryMappedDevice.MMD_GG0, 0x00c00000, 0x00c80000);
  1374:           XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00c80000, 0x00e00000);
  1375:         }
  1376:       } else {  //グラフィックストレージOFF
  1377:         switch (crtMemoryModeCurr) {
  1378:         case 0:  //メモリモード0
  1379:           //512ドット16色
  1380:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE0, 0x00c00000, 0x00c80000);
  1381:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE1, 0x00c80000, 0x00d00000);
  1382:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE2, 0x00d00000, 0x00d80000);
  1383:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE3, 0x00d80000, 0x00e00000);
  1384:           break;
  1385:         case 1:  //メモリモード1
  1386:           //512ドット256色
  1387:           XEiJ.busSuper (MemoryMappedDevice.MMD_GF0, 0x00c00000, 0x00c80000);
  1388:           XEiJ.busSuper (MemoryMappedDevice.MMD_GF1, 0x00c80000, 0x00d00000);
  1389:           XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00d00000, 0x00e00000);
  1390:           break;
  1391:         case 2:  //メモリモード2
  1392:           //メモリモード2
  1393:           XEiJ.busSuper (MemoryMappedDevice.MMD_GM2, 0x00c00000, 0x00e00000);
  1394:           break;
  1395:         case 3:  //メモリモード3
  1396:           //512ドット65536色
  1397:           XEiJ.busSuper (MemoryMappedDevice.MMD_GG0, 0x00c00000, 0x00c80000);
  1398:           XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00c80000, 0x00e00000);
  1399:           break;
  1400:         case 4:  //メモリモード4
  1401:           //1024ドット16色
  1402:           XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1403:           break;
  1404:         case 5:  //メモリモード5
  1405:           if (CRT_EXTENDED_GRAPHIC && crtExtendedGraphicOn) {
  1406:             //1024ドット256色(拡張)
  1407:             XEiJ.busSuper (MemoryMappedDevice.MMD_GI0, 0x00c00000, 0x00e00000);
  1408:           } else {
  1409:             //1024ドット16色
  1410:             XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1411:           }
  1412:           break;
  1413:         case 6:  //メモリモード6
  1414:           //1024ドット16色
  1415:           XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1416:           break;
  1417:         case 7:  //メモリモード7
  1418:           if (CRT_EXTENDED_GRAPHIC && crtExtendedGraphicOn) {
  1419:             //1024ドット65536色(拡張)
  1420:             XEiJ.busSuper (MemoryMappedDevice.MMD_GJ0, 0x00c00000, 0x00e00000);
  1421:           } else {
  1422:             //1024ドット16色
  1423:             XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1424:           }
  1425:           break;
  1426:         }
  1427:       }
  1428:     }
  1429:   }  //crtSetMemoryMode
  1430: 
  1431:   //crtUpdateRasterHash ()
  1432:   //  crtRasterHashIdle,crtRasterHashDispを設定する
  1433:   //    crtRasterCopyOn
  1434:   //    RasterBreakPoint.rbpActiveBreakRaster
  1435:   //    crtR09IRQRasterCurr
  1436:   //    crtVDispStart
  1437:   //    crtR04VFrontEndCurr
  1438:   //    crtVIdleStart
  1439:   //  が更新されたときに呼び出す
  1440:   //  crtR04VFrontEndCurrがcrtRasterNumberよりも小さい値に変更されると通り過ぎてしまうので必ずcrtRasterNumber=0からリスタートさせること
  1441:   public static void crtUpdateRasterHash () {
  1442:     if (CRT_RASTER_HASH_ON) {
  1443:       long t = crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO;  //intまたはlong
  1444:       //int t = crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO;  //intまたはlong
  1445:       if (RasterBreakPoint.RBP_ON && RasterBreakPoint.rbpActiveBreakRaster >= 0) {
  1446:         t |= CRT_RASTER_HASH_MSB >>> RasterBreakPoint.rbpActiveBreakRaster;
  1447:       }
  1448:       if (crtR09IRQRasterCurr <= crtR04VFrontEndCurr) {
  1449:         t |= (CRT_RASTER_HASH_MSB >>> crtR09IRQRasterCurr |
  1450:               CRT_RASTER_HASH_MSB >>> (crtR09IRQRasterCurr < crtR04VFrontEndCurr ? crtR09IRQRasterCurr + 1 : 0));
  1451:       }
  1452:       crtRasterHashIdle = (t |
  1453:                            CRT_RASTER_HASH_MSB >>> crtVDispStart |
  1454:                            CRT_RASTER_HASH_MSB >>> crtR04VFrontEndCurr + 1);
  1455:       crtRasterHashDisp = (t |
  1456:                            CRT_RASTER_HASH_MSB >>> crtVIdleStart);
  1457:     }
  1458:   }  //crtUpdateRasterHash()
  1459: 
  1460:   //高速クリア
  1461:   //  手順
  1462:   //    (1)$00E8002Aを保存する
  1463:   //    (2)クリアページを設定する。$00E8002Aに#$000Fなどを書き込む
  1464:   //    (3)高速クリアを予約する。$00E80480に#$0002を書き込む
  1465:   //    (4)高速クリアの開始を待つ。$00E80480と#$0002のANDが#$0000でなくなるまで待つ
  1466:   //    (5)高速クリアの終了を待つ。$00E80480と#$0002のANDが#$0000になるまで待つ
  1467:   //    (6)$00E8002Aを復元する
  1468:   //  動作ポートのビット1
  1469:   //    書き込み
  1470:   //      0のとき1を書き込むと高速クリアが予約され、自動で開始、終了する
  1471:   //      0を書き込んでもキャンセルや中断はできない
  1472:   //    読み出し
  1473:   //      高速クリアが予約されているとき(インターレースのときは偶数フレームの)VDISP=0→1(の一瞬前)で1になる
  1474:   //      1フレーム(インターレースのときは2フレーム)後のVDISP=0→1(の一瞬前)で0に戻る
  1475:   //      0に戻った直後に1を書き込んだとき次のフレームの高速クリアは(ほとんど)失敗する
  1476:   //  ページ選択
  1477:   //    実画面サイズが512x512のとき
  1478:   //      高速クリアページセレクトの各ビットが4ビットページに対応する
  1479:   //      垂直映像期間の途中で変更できる
  1480:   //    実画面サイズが1024x1024のとき
  1481:   //      高速クリアページセレクトは無効で常にすべてのページが消去される
  1482:   //  範囲
  1483:   //    高速クリアされる範囲は、画面サイズ、実画面サイズ、ページ0のスクロール位置で決まる
  1484:   //      ページ0は表示されている範囲がクリアされるが、ページ1,2,3は表示されている範囲がクリアされるとは限らない
  1485:   //    水平方向のスクロール位置が奇数のときは偶数に切り捨てられる
  1486:   //      256x256で水平方向のスクロール位置が奇数のとき、画面の右端にクリアされないドットが残る
  1487:   //    クリアされる範囲の幅は画面の幅と512の小さい方
  1488:   //      x=512を跨ぐときx=0に戻る
  1489:   //      実画面サイズが1024ドットのとき2箇所クリアする
  1490:   //    クリアされる範囲の高さは画面の高さ
  1491:   //      画面の高さが256ドットのときラスタ2度読みでもクリアされる範囲の高さは256ドット
  1492: 
  1493:   //crtRapidClear (y)
  1494:   //  高速クリア実行
  1495:   public static void crtRapidClear (int y) {
  1496:     y += crtR13GrYZero[0];
  1497:     int x = crtR12GrXCurr[0] & (512 - 2);  //x座標開始位置
  1498:     int w = 8 * crtHDispColumn;  //画面の幅
  1499:     if (512 <= w) {
  1500:       x = 0;
  1501:       w = 512;
  1502:     }
  1503:     if (crtMemoryModeCurr < 4) {  //実画面サイズ512ドット
  1504:       int i = (y & 511) << 9;  //(0,y)のインデックス
  1505:       if (x + w <= 512) {  //x=512を跨がない
  1506:         //    i  i+x          i+x+w    i+512
  1507:         //    |   <------------->       |
  1508:         if (crtCCPlane0) {
  1509:           Arrays.fill (GraphicScreen.graM4,           i + x,           i + x + w, (byte) 0);
  1510:         }
  1511:         if (crtCCPlane1) {
  1512:           Arrays.fill (GraphicScreen.graM4, 0x40000 + i + x, 0x40000 + i + x + w, (byte) 0);
  1513:         }
  1514:         if (crtCCPlane2) {
  1515:           Arrays.fill (GraphicScreen.graM4, 0x80000 + i + x, 0x80000 + i + x + w, (byte) 0);
  1516:         }
  1517:         if (crtCCPlane3) {
  1518:           Arrays.fill (GraphicScreen.graM4, 0xc0000 + i + x, 0xc0000 + i + x + w, (byte) 0);
  1519:         }
  1520:       } else {  //x=512を跨ぐ
  1521:         //    i    i+x+w-512     i+x   i+512  i+x+w
  1522:         //    |------->           <-----|------->
  1523:         if (crtCCPlane0) {
  1524:           Arrays.fill (GraphicScreen.graM4,           i + x,           i +         512, (byte) 0);
  1525:           Arrays.fill (GraphicScreen.graM4,           i    ,           i + x + w - 512, (byte) 0);
  1526:         }
  1527:         if (crtCCPlane1) {
  1528:           Arrays.fill (GraphicScreen.graM4, 0x40000 + i + x, 0x40000 + i +         512, (byte) 0);
  1529:           Arrays.fill (GraphicScreen.graM4, 0x40000 + i    , 0x40000 + i + x + w - 512, (byte) 0);
  1530:         }
  1531:         if (crtCCPlane2) {
  1532:           Arrays.fill (GraphicScreen.graM4, 0x80000 + i + x, 0x80000 + i +         512, (byte) 0);
  1533:           Arrays.fill (GraphicScreen.graM4, 0x80000 + i    , 0x80000 + i + x + w - 512, (byte) 0);
  1534:         }
  1535:         if (crtCCPlane3) {
  1536:           Arrays.fill (GraphicScreen.graM4, 0xc0000 + i + x, 0xc0000 + i +         512, (byte) 0);
  1537:           Arrays.fill (GraphicScreen.graM4, 0xc0000 + i    , 0xc0000 + i + x + w - 512, (byte) 0);
  1538:         }
  1539:       }
  1540:     } else {  //実画面サイズ1024ドット
  1541:       int i = (y & 512) << 10 | (y & 511) << 9;  //(0,y)のインデックス
  1542:       if (x + w <= 512) {  //x=512を跨がない
  1543:         //    i  i+x          i+x+w    i+512
  1544:         //    |   <------------->       |
  1545:         for (int k = 0; k < 2; k++) {  //2箇所クリアする
  1546:           Arrays.fill (GraphicScreen.graM4,            i + x,            i + x + w, (byte) 0);
  1547:           if (crtMemoryModeCurr == 5 || crtMemoryModeCurr == 7) {
  1548:             Arrays.fill (GraphicScreen.graM4, 0x100000 + i + x, 0x100000 + i + x + w, (byte) 0);
  1549:             if (crtMemoryModeCurr == 7) {
  1550:               Arrays.fill (GraphicScreen.graM4, 0x200000 + i + x, 0x200000 + i + x + w, (byte) 0);
  1551:               Arrays.fill (GraphicScreen.graM4, 0x300000 + i + x, 0x300000 + i + x + w, (byte) 0);
  1552:             }
  1553:           }
  1554:           i += 512 << 9;  //(512,y)のインデックス
  1555:         }
  1556:       } else {  //x=512を跨ぐ
  1557:         //    i    i+x+w-512     i+x   i+512  i+x+w
  1558:         //    |------->           <-----|------->
  1559:         for (int k = 0; k < 2; k++) {  //2箇所クリアする
  1560:           Arrays.fill (GraphicScreen.graM4,            i + x,            i +         512, (byte) 0);
  1561:           Arrays.fill (GraphicScreen.graM4,            i    ,            i + x + w - 512, (byte) 0);
  1562:           if (crtMemoryModeCurr == 5 || crtMemoryModeCurr == 7) {
  1563:             Arrays.fill (GraphicScreen.graM4, 0x100000 + i + x, 0x100000 + i +         512, (byte) 0);
  1564:             Arrays.fill (GraphicScreen.graM4, 0x100000 + i    , 0x100000 + i + x + w - 512, (byte) 0);
  1565:             if (crtMemoryModeCurr == 7) {
  1566:               Arrays.fill (GraphicScreen.graM4, 0x200000 + i + x, 0x200000 + i +         512, (byte) 0);
  1567:               Arrays.fill (GraphicScreen.graM4, 0x200000 + i    , 0x200000 + i + x + w - 512, (byte) 0);
  1568:               Arrays.fill (GraphicScreen.graM4, 0x300000 + i + x, 0x300000 + i +         512, (byte) 0);
  1569:               Arrays.fill (GraphicScreen.graM4, 0x300000 + i    , 0x300000 + i + x + w - 512, (byte) 0);
  1570:             }
  1571:           }
  1572:           i += 512 << 9;  //(512,y)のインデックス
  1573:         }
  1574:       }
  1575:     }
  1576:   }  //crtRapidClear(int)
  1577: 
  1578:   //crtDoRasterCopy ()
  1579:   //  ラスタコピー実行
  1580:   public static void crtDoRasterCopy () {
  1581:     int srcOffset = crtR22SrcBlock << 9;
  1582:     int dstOffset = crtR22DstBlock << 9;
  1583:     if (crtCCPlane0) {
  1584:       System.arraycopy (MainMemory.mmrM8, 0x00e00000 + srcOffset, MainMemory.mmrM8, 0x00e00000 + dstOffset, 512);
  1585:     }
  1586:     if (crtCCPlane1) {
  1587:       System.arraycopy (MainMemory.mmrM8, 0x00e20000 + srcOffset, MainMemory.mmrM8, 0x00e20000 + dstOffset, 512);
  1588:     }
  1589:     if (crtCCPlane2) {
  1590:       System.arraycopy (MainMemory.mmrM8, 0x00e40000 + srcOffset, MainMemory.mmrM8, 0x00e40000 + dstOffset, 512);
  1591:     }
  1592:     if (crtCCPlane3) {
  1593:       System.arraycopy (MainMemory.mmrM8, 0x00e60000 + srcOffset, MainMemory.mmrM8, 0x00e60000 + dstOffset, 512);
  1594:     }
  1595:     int y = (dstOffset >> 7) - crtR11TxYZero;
  1596:     crtRasterStamp[y     & 1023] = 0;
  1597:     crtRasterStamp[y + 1 & 1023] = 0;
  1598:     crtRasterStamp[y + 2 & 1023] = 0;
  1599:     crtRasterStamp[y + 3 & 1023] = 0;
  1600:   }  //crtDoRasterCopy()
  1601: 
  1602: 
  1603: 
  1604:   //========================================================================================
  1605:   //
  1606:   //  CRTCのステージ
  1607:   //
  1608:   //    ラスタ(水平周期)
  1609:   //      水平フロントポーチと水平同期パルスと水平バックポーチと水平映像期間を合わせた期間
  1610:   //
  1611:   //    フレーム(垂直周期)
  1612:   //      垂直空白期間(垂直フロントポーチと垂直同期パルスと垂直バックポーチ)と垂直映像期間を合わせた期間
  1613:   //      垂直映像期間と垂直フロントポーチはそれぞれ少なくとも1ラスタ以上必要
  1614:   //        垂直フロントポーチが1ラスタ以上必要なのは垂直映像期間にラスタ番号のラップアラウンドを行なっていないため
  1615:   //        垂直映像期間にラスタ番号のラップアラウンドを行う場合でも垂直空白期間は少なくとも1ラスタ以上必要
  1616:   //
  1617:   //    ラスタ番号
  1618:   //      ラスタの番号。0からラスタ数-1まで。0は垂直同期パルス開始ラスタ、ラスタ数-1は垂直フロントポーチ終了ラスタ
  1619:   //      遷移とIRQ信号の生成に用いる
  1620:   //
  1621:   //    フレームパリティ
  1622:   //      フレーム番号の下位1bit。0または1
  1623:   //      インターレースのときにフレーム毎のデータY座標とスクリーンY座標の初期値として使う
  1624:   //      フレームの末尾で反転する
  1625:   //
  1626:   //    データY座標
  1627:   //      ラスタに供給されるデータのY座標
  1628:   //
  1629:   //    スクリーンY座標
  1630:   //      ラスタを表示するスクリーン上のY座標
  1631:   //
  1632:   //
  1633:   //    ノーマル
  1634:   //      フレームの先頭でデータY座標とスクリーンY座標を0で初期化する
  1635:   //      ラスタを描画してからスクリーンY座標を1増やす
  1636:   //      ラスタの末尾でデータY座標を1増やす
  1637:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを1で初期化する
  1638:   //
  1639:   //    ラスタ2度読み
  1640:   //      フレームの先頭でデータY座標とスクリーンY座標を0で初期化する
  1641:   //      偶数ラスタを描画してからスクリーンY座標を1増やす
  1642:   //      偶数ラスタの末尾ではデータY座標を増やさない
  1643:   //      奇数ラスタを描画してからスクリーンY座標を1増やす
  1644:   //      奇数ラスタの末尾でデータY座標を1増やす
  1645:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを1で初期化する
  1646:   //
  1647:   //    インターレース
  1648:   //      フレームの先頭でデータY座標とスクリーンY座標をフレームパリティで初期化する
  1649:   //      ラスタを描画してからスクリーンY座標を2増やす
  1650:   //      ラスタの末尾でデータY座標を2増やす
  1651:   //      (偶数フレームは偶数ラスタだけ、奇数フレームは奇数ラスタだけ描画する)
  1652:   //      偶数フレームの垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを2で初期化する
  1653:   //
  1654:   //    スリット
  1655:   //      フレームの先頭でデータY座標とスクリーンY座標を0で初期化する
  1656:   //      ラスタを描画してから輝度を落としてコピーし、スクリーンY座標を2増やす
  1657:   //      ラスタの末尾でデータY座標を1増やす
  1658:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを1で初期化する
  1659:   //
  1660:   //
  1661:   //                                         データY座標     スクリーンY座標
  1662:   //    ───────────────────────────────────
  1663:   //        ノーマル          初期化               0                 0
  1664:   //                       ラスタ描画後           +1                +1
  1665:   //    ───────────────────────────────────
  1666:   //      ラスタ2度読み       初期化               0                 0
  1667:   //                     偶数ラスタ描画後         +0                +1
  1668:   //                     奇数ラスタ描画後         +1                +1
  1669:   //    ───────────────────────────────────
  1670:   //     インターレース       初期化       フレームパリティ  フレームパリティ
  1671:   //                       ラスタ描画後           +2                +2
  1672:   //    ───────────────────────────────────
  1673:   //        スリット          初期化               0                 0
  1674:   //                       ラスタ描画後           +1                +2
  1675:   //    ───────────────────────────────────
  1676:   //
  1677:   //
  1678:   //    水平フロントポーチ
  1679:   //      +水平フロントポーチの長さ
  1680:   //      ラスタ番号を1増やす
  1681:   //      [垂直空白期間のとき]
  1682:   //        ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  1683:   //          ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  1684:   //      ラスタコピースイッチがONのとき
  1685:   //        ラスタコピー実行
  1686:   //      ブレークラスタのとき
  1687:   //        ラスタブレークをかける
  1688:   //      IRQ信号を更新
  1689:   //      IRQ信号が変化したとき
  1690:   //        IRQ信号が0になったとき
  1691:   //        | IRQラスタでラスタブレークをかけるとき
  1692:   //        |   ブレークラスタではないとき(ブレークラスタとIRQラスタが同じときは既にラスタブレークがかかっている)
  1693:   //        |      ラスタブレークをかける
  1694:   //        | IRQ開始
  1695:   //        IRQ信号が0でなくなったとき
  1696:   //          IRQ終了
  1697:   //      [垂直空白期間のとき]
  1698:   //      | 垂直映像期間開始ラスタではないとき
  1699:   //      | | [描画フレーム(間欠カウンタ==0)のとき]
  1700:   //      | | | →描画フレームの垂直空白期間の水平同期パルス
  1701:   //      | | [省略フレーム(間欠カウンタ!=0)のとき]
  1702:   //      | |   →省略フレームの垂直空白期間の水平同期パルス
  1703:   //      | 垂直映像期間開始ラスタのとき
  1704:   //      |   垂直映像期間開始
  1705:   //      |   テキストY方向スクロールを保存
  1706:   //      |   グラフィックY方向スクロールを保存
  1707:   //      |   [インターレースではないとき]
  1708:   //      |   | データY座標を0で初期化
  1709:   //      |   [インターレースのとき]
  1710:   //      |     データY座標をフレームパリティで初期化
  1711:   //      |   [高速クリアカウンタが0ではないとき]
  1712:   //      |   | 高速クリアカウンタを1減らす
  1713:   //      |   [インターレースではないとき]
  1714:   //      |   | 高速クリアの要求があるとき
  1715:   //      |   |   高速クリアカウンタを1で初期化
  1716:   //      |   [インターレースのとき]
  1717:   //      |     高速クリアの要求があり、偶数フレームのとき]
  1718:   //      |       高速クリアカウンタを2で初期化
  1719:   //      |   [描画フレーム(間欠カウンタ==0)のとき]
  1720:   //      |   | [インターレースではないとき]
  1721:   //      |   | | スクリーンY座標を0で初期化
  1722:   //      |   | [インターレースのとき]
  1723:   //      |   |   スクリーンY座標をフレームパリティで初期化
  1724:   //      |   | ダーティフラグをクリア
  1725:   //      |   | →描画フレームの垂直映像期間の水平同期パルス
  1726:   //      |   [省略フレーム(間欠カウンタ!=0)のとき]
  1727:   //      |     →省略フレームの垂直映像期間の水平同期パルス
  1728:   //      [垂直映像期間のとき]
  1729:   //        垂直空白期間開始ラスタではないとき
  1730:   //        | [描画フレーム(間欠カウンタ==0)のとき]
  1731:   //        | | [全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき]
  1732:   //        | | | データY座標からスクリーンY座標へ描画
  1733:   //        | | [ノーマルのとき]
  1734:   //        | | | データY座標を1増やす
  1735:   //        | | | スクリーンY座標を1増やす
  1736:   //        | | [ラスタ2度読みのとき]
  1737:   //        | | | スクリーンY座標を1増やす
  1738:   //        | | | [偶数ラスタのとき]
  1739:   //        | | |   データY座標を1増やす
  1740:   //        | | [インターレースのとき]
  1741:   //        | | | スクリーンY座標を2増やす
  1742:   //        | | | データY座標を2増やす
  1743:   //        | | [スリットのとき]
  1744:   //        | |   スクリーンY座標を2増やす
  1745:   //        | |   データY座標を1増やす
  1746:   //        | | →描画フレームの垂直映像期間の水平同期パルス
  1747:   //        | [省略フレーム(間欠カウンタ!=0)のとき]
  1748:   //        |   →省略フレームの垂直映像期間の水平同期パルス
  1749:   //        垂直空白期間開始ラスタのとき
  1750:   //          [全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき]
  1751:   //          | データY座標からスクリーンY座標へ描画
  1752:   //          垂直映像期間終了
  1753:   //          [描画フレーム(間欠カウンタ==0)のとき]
  1754:   //          | ダーティフラグがセットされているとき
  1755:   //          |   スクリーンを更新する
  1756:   //          |   間欠カウンタを間欠間隔に戻す
  1757:   //          [省略フレーム(間欠カウンタ!=0)のとき]
  1758:   //            間欠カウンタを1減らす
  1759:   //          [インターレースのとき]
  1760:   //            フレームパリティを反転
  1761:   //          フレームタスク(コントラスト調整など)
  1762:   //          書き込むビットマップを切り替える
  1763:   //          [描画フレーム(間欠カウンタ==0)のとき]
  1764:   //          | →描画フレームの垂直空白期間の水平同期パルス
  1765:   //          [省略フレーム(間欠カウンタ!=0)のとき]
  1766:   //            →省略フレームの垂直空白期間の水平同期パルス
  1767:   //
  1768:   //    水平同期パルス
  1769:   //      +水平同期パルスの長さ
  1770:   //      水平同期パルス開始
  1771:   //      [垂直空白期間のとき]
  1772:   //      | [描画フレーム(間欠カウンタ==0)のとき]
  1773:   //      | | →描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  1774:   //      | [省略フレーム(間欠カウンタ!=0)のとき]
  1775:   //      |   →省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  1776:   //      [垂直映像期間のとき]
  1777:   //        [ラスタ2度読みではないとき]
  1778:   //        | 高速クリアカウンタが0ではないとき
  1779:   //        |   データY座標を高速クリア
  1780:   //        [ラスタ2度読みのとき]
  1781:   //          [偶数ラスタのとき]
  1782:   //            高速クリアカウンタが0ではないとき
  1783:   //              データY座標を高速クリア
  1784:   //        [描画フレーム(間欠カウンタ==0)のとき]
  1785:   //        | →描画フレームの垂直映像期間の水平バックポーチ
  1786:   //        [省略フレーム(間欠カウンタ!=0)のとき]
  1787:   //          →省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  1788:   //
  1789:   //    水平バックポーチ
  1790:   //      水平同期パルス終了
  1791:   //      [垂直空白期間のとき]
  1792:   //      | [描画フレーム(間欠カウンタ==0)のとき]
  1793:   //      | | +水平バックポーチと水平映像期間の長さ
  1794:   //      | | →描画フレームの垂直空白期間の水平フロントポーチ
  1795:   //      | [省略フレーム(間欠カウンタ!=0)のとき]
  1796:   //      |   +水平バックポーチと水平映像期間の長さ
  1797:   //      |   →省略フレームの垂直空白期間の水平フロントポーチ
  1798:   //      [垂直映像期間のとき]
  1799:   //        [描画フレーム(間欠カウンタ==0)のとき]
  1800:   //        | +水平バックポーチの長さ
  1801:   //        | →描画フレームの垂直映像期間の水平映像期間
  1802:   //        [省略フレーム(間欠カウンタ!=0)のとき]
  1803:   //          +水平バックポーチと水平映像期間の長さ
  1804:   //          →省略フレームの垂直映像期間の水平フロントポーチ
  1805:   //
  1806:   //    描画フレームの垂直映像期間の水平映像期間
  1807:   //      +水平水平映像期間の長さ
  1808:   //      ラスタの更新フラグがセットされているとき
  1809:   //        [ノーマルのとき]
  1810:   //        | ラスタの更新フラグをクリア
  1811:   //        [ラスタ2度読みのとき]
  1812:   //        | [奇数ラスタのとき]
  1813:   //        |   ラスタの更新フラグをクリア
  1814:   //        [インターレースのとき]
  1815:   //        | ラスタの更新フラグをクリア
  1816:   //        [スリットのとき]
  1817:   //          ラスタの更新フラグをクリア
  1818:   //        全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  1819:   //        データY座標からスクリーンY座標へ描画
  1820:   //        [スリットのとき]
  1821:   //          輝度を半分にしてコピーする
  1822:   //        ダーティフラグをセット
  1823:   //      →描画フレームの垂直映像期間の水平フロントポーチ
  1824:   //
  1825:   //
  1826:   //    描画フレーム(間欠カウンタ==0)
  1827:   //      描画フレームの垂直空白期間(垂直フロントポーチと垂直同期パルスと垂直バックポーチ)
  1828:   //        (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)            Start
  1829:   //        (1)描画フレームの垂直空白期間の水平フロントポーチ                              DrawIdleFront
  1830:   //        (2)描画フレームの垂直空白期間の水平同期パルス                                  DrawIdleSync
  1831:   //        (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間                  DrawIdleBackDisp
  1832:   //      描画フレームの垂直映像期間
  1833:   //        奇偶共通
  1834:   //          (4)描画フレームの垂直映像期間の水平フロントポーチ                            DrawDispFront
  1835:   //          (5)描画フレームの垂直映像期間の水平同期パルス                                DrawDispSync
  1836:   //          (6)描画フレームの垂直映像期間の水平バックポーチ                              DrawDispBack
  1837:   //          (7)描画フレームの垂直映像期間の水平映像期間                                  DrawDispDisp
  1838:   //        偶数ラスタ
  1839:   //          (4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ               DrawDispEvenFront
  1840:   //          (5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス                   DrawDispEvenSync
  1841:   //          (6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ                 DrawDispEvenBack
  1842:   //          (7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間                     DrawDispEvenDisp
  1843:   //        奇数ラスタ
  1844:   //          (4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ               DrawDispOddFront
  1845:   //          (5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス                   DrawDispOddSync
  1846:   //          (6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ                 DrawDispOddBack
  1847:   //          (7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間                     DrawDispOddDisp
  1848:   //    省略フレーム(間欠カウンタ!=0)
  1849:   //      省略フレームの垂直空白期間(垂直フロントポーチと垂直同期パルスと垂直バックポーチ)
  1850:   //        (8)省略フレームの垂直空白期間の水平フロントポーチ                              OmitIdleFront
  1851:   //        (9)省略フレームの垂直空白期間の水平同期パルス                                  OmitIdleSync
  1852:   //        (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間                 OmitIdleBackDisp
  1853:   //      省略フレームの垂直映像期間
  1854:   //        奇偶共通
  1855:   //          (11)省略フレームの垂直映像期間の水平フロントポーチ                           OmitDispFront
  1856:   //          (12)省略フレームの垂直映像期間の水平同期パルス                               OmitDispSync
  1857:   //          (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間               OmitDispBackDisp
  1858:   //        偶数ラスタ
  1859:   //          (11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ              OmitDispEvenFront
  1860:   //          (12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス                  OmitDispEvenSync
  1861:   //          (13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間  OmitDispEvenBackDisp
  1862:   //        奇数ラスタ
  1863:   //          (11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ              OmitDispOddFront
  1864:   //          (12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス                  OmitDispOddSync
  1865:   //          (13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間  OmitDispOddBackDisp
  1866:   //
  1867: 
  1868:   public static void crtUpdateLength () {
  1869:     int k = crtHRLCurr << 3 | crtHighResoCurr << 2 | crtHResoCurr;
  1870:     double osc = (double) crtFreqs[CRT_OSCS[k]];  //オシレータ周波数
  1871:     int div = CRT_DIVS[k];  //分周比分母
  1872:     int vTotal = crtR04VFrontEndCurr + 1;
  1873:     double multiplier = 1.0;  //垂直同期周波数に掛ける数
  1874:     if (XEiJ.pnlAdjustVsync && XEiJ.pnlFixedRate != 0.0) {  //ホストのリフレッシュレートに合わせる、かつ、確定済み
  1875:       double hFreq = osc / (8 * div * crtHTotalColumn);  //水平同期周波数
  1876:       double vFreq = hFreq / vTotal;  //垂直同期周波数
  1877:       double rate = XEiJ.pnlFixedRate;  //ホストのリフレッシュレート
  1878:       if (vFreq * 0.75 <= rate && rate <= vFreq * 1.25) {  //ホストのリフレッシュレートが垂直同期周波数の0.75~1.25倍のとき
  1879:         multiplier = rate / vFreq;  //垂直同期周波数に掛ける数
  1880:       }
  1881:     }
  1882:     crtVsyncMultiplier = multiplier;
  1883:     crtColumnTime = (int) ((double) (XEiJ.TMR_FREQ * 8 * div) / (osc * multiplier) + 0.5);
  1884:     crtHSyncLength = crtColumnTime * crtHSyncColumn;
  1885:     crtHBackLength = crtColumnTime * crtHBackColumn;
  1886:     crtHDispLength = crtColumnTime * crtHDispColumn;
  1887:     crtHFrontLength = crtColumnTime * crtHFrontColumn;
  1888:     crtHBackDispLength = crtColumnTime * (crtHBackColumn + crtHDispColumn);
  1889:     long t = (long) crtColumnTime * (long) (crtHTotalColumn * vTotal);
  1890:     crtTotalLength = t / 1000000000L;  //ミリ
  1891:     crtTotalLengthMNP = t - 1000000000L * crtTotalLength;  //マイクロナノピコ
  1892:   }
  1893: 
  1894:   //----------------------------------------------------------------
  1895:   //  スクリーンの初期化
  1896:   public static final TickerQueue.Ticker InitialStage = new TickerQueue.Ticker () {
  1897:     @Override protected void tick () {
  1898:     ret:
  1899:       {
  1900:         //  水平映像期間は1カラム以上128カラム以下でなければならない
  1901:         //  垂直映像期間は1ラスタ以上1024ラスタ以下でなければならない
  1902:         //  垂直同期パルス、垂直バックポーチ、垂直フロントポーチはそれぞれ1ラスタ以上なければならない
  1903:         //  不正な値が設定されているときは停止して少し待つ
  1904:         //  同期信号が動かなくなるので不正な値を設定してから同期信号を待つようなプログラムは止まってしまう
  1905:         if (!(crtR02HBackEndCurr < crtR03HDispEndCurr && crtR03HDispEndCurr - crtR02HBackEndCurr <= 128 &&
  1906:               crtR07VDispEndCurr < crtR04VFrontEndCurr && crtR07VDispEndCurr - crtR06VBackEndCurr <= 1024 &&
  1907:               crtR05VSyncEndCurr < crtR06VBackEndCurr &&
  1908:               crtR06VBackEndCurr < crtR07VDispEndCurr)) {
  1909:           crtRestart ();
  1910:           break ret;
  1911:         }
  1912:         //
  1913:         if (CRT_RASTER_HASH_ON) {
  1914:           crtUpdateRasterHash ();
  1915:         }
  1916:         //  水平周期が水平映像期間よりも3カラム以上長くないとき
  1917:         //    水平周期を伸ばして水平同期パルスと水平バックポーチと水平フロントポーチをすべて1カラムにする
  1918:         //  水平周期が水平映像期間よりも3カラム以上長いとき
  1919:         //    必要ならば水平バックポーチと水平フロントポーチが1カラム以上なるように水平周期の中の水平映像期間の位置を調整する
  1920:         //    動作速度が変わってしまわないように、水平周期はなるべく変更しない
  1921:         //  調整するのは状態遷移の間隔だけで、CRTCの設定値は変更しない
  1922:         int hSync = crtR01HSyncEndCurr + 1;  //水平同期パルスカラム数。0以下は設定のしようがないので1以上
  1923:         int hBack = crtR02HBackEndCurr - crtR01HSyncEndCurr + 4;  //水平バックポーチカラム数。-1以上
  1924:         int hDisp = crtR03HDispEndCurr - crtR02HBackEndCurr;  //水平映像期間カラム数。0以下は除いてあるので1以上
  1925:         int hFront = crtR00HFrontEndCurr - crtR03HDispEndCurr - 4;  //水平フロントポーチカラム数。負数かも知れない
  1926:         if (hSync + hBack + hDisp + hFront < hDisp + 3) {  //水平周期が水平映像期間よりも3カラム以上長くないとき
  1927:           hSync = hBack = hFront = 1;  //水平周期を伸ばして水平同期パルスと水平バックポーチと水平フロントポーチをすべて1カラムにする
  1928:         } else {  //水平周期が水平映像期間よりも3カラム以上長いとき
  1929:           if (hBack <= 0) {  //左に寄り過ぎているとき
  1930:             hFront -= 1 - hBack;  //水平フロントポーチを削って
  1931:             hBack = 1;  //水平バックポーチを1にする
  1932:             if (hFront <= 0) {  //水平フロントポーチを削り過ぎたとき
  1933:               hSync -= 1 - hFront;  //水平同期パルスを削って
  1934:               hFront = 1;  //水平フロントポーチを1にする
  1935:             }
  1936:           } else if (hFront <= 0) {  //右に寄り過ぎているとき
  1937:             hBack -= 1 - hFront;  //水平バックポーチを削って
  1938:             hFront = 1;  //水平フロントポーチを1にする
  1939:             if (hBack <= 0) {  //水平バックポーチを削り過ぎたとき
  1940:               hSync -= 1 - hBack;  //水平同期パルスを削って
  1941:               hBack = 1;  //水平バックポーチを1にする
  1942:             }
  1943:           }
  1944:         }
  1945:         crtHSyncColumn = hSync;
  1946:         crtHBackColumn = hBack;
  1947:         crtHDispColumn = hDisp;
  1948:         crtHFrontColumn = hFront;
  1949:         crtHTotalColumn = hSync + hBack + hDisp + hFront;
  1950:         crtUpdateLength ();
  1951:         //
  1952:         crtDuplication = crtHighResoCurr == 1 && crtVResoCurr == 0 && ((SpriteScreen.sprReg8ResoCurr & 12) != 4);  //ラスタ2度読み
  1953:         crtInterlace = crtHighResoCurr + 1 <= crtVResoCurr;  //インターレース
  1954:         crtSlit = crtHighResoCurr == 0 && crtVResoCurr == 0;  //スリット
  1955:         crtDupExceptSp = crtHighResoCurr == 1 && crtVResoCurr == 0 && ((SpriteScreen.sprReg8ResoCurr & 12) == 4);  //ラスタ2度読み(スプライトを除く)
  1956:         //
  1957:         XEiJ.pnlUpdateArrangement ();
  1958:         //
  1959:         crtAllStamp += 2;
  1960:         (crtDuplication ? DuplicationStart :
  1961:          crtInterlace ? InterlaceStart :
  1962:          crtSlit ? SlitStart :
  1963:          crtDupExceptSp ? DupExceptSpStart :
  1964:          NormalStart).tick ();
  1965:       }  //ret
  1966:     }
  1967:   };
  1968: 
  1969:   //----------------------------------------------------------------
  1970:   //  ノーマル
  1971:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  1972:   public static final TickerQueue.Ticker NormalStart = new TickerQueue.Ticker () {
  1973:     @Override protected void tick () {
  1974:       if (MC68901.mfpGpipHsync != 0) {
  1975:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  1976:       }
  1977:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  1978:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  1979:         crtDoRasterCopy ();  //ラスタコピー実行
  1980:       }
  1981:       if (RasterBreakPoint.RBP_ON) {
  1982:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  1983:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1984:         }
  1985:       }
  1986:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  1987:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  1988:         if (irq == 0) {  //IRQ信号が0になったとき
  1989:           if (RasterBreakPoint.RBP_ON) {
  1990:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  1991:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1992:             }
  1993:           }
  1994:           MC68901.mfpRintFall ();  //IRQ開始
  1995:         } else {  //IRQ信号が0でなくなったとき
  1996:           MC68901.mfpRintRise ();  //IRQ終了
  1997:         }
  1998:       }
  1999:       if (MC68901.mfpGpipVdisp != 0) {
  2000:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  2001:       }
  2002:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  2003:       if (CRT_ENABLE_INTERMITTENT) {
  2004:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  2005:       }
  2006:       if (!XEiJ.PNL_USE_THREAD) {
  2007:         if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2008:           crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2009:         }
  2010:       }
  2011:       TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2012:     }
  2013:   };
  2014:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  2015:   public static final TickerQueue.Ticker NormalDrawIdleFront = new TickerQueue.Ticker () {
  2016:     @Override protected void tick () {
  2017:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2018:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2019:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2020:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2021:         }
  2022:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2023:           crtDoRasterCopy ();  //ラスタコピー実行
  2024:         }
  2025:         if (RasterBreakPoint.RBP_ON) {
  2026:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2027:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2028:           }
  2029:         }
  2030:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2031:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2032:           if (irq == 0) {  //IRQ信号が0になったとき
  2033:             if (RasterBreakPoint.RBP_ON) {
  2034:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2035:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2036:               }
  2037:             }
  2038:             MC68901.mfpRintFall ();  //IRQ開始
  2039:           } else {  //IRQ信号が0でなくなったとき
  2040:             MC68901.mfpRintRise ();  //IRQ終了
  2041:           }
  2042:         }
  2043:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2044:           TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2045:         } else {  //垂直映像期間開始ラスタのとき
  2046:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2047:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2048:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2049:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2050:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2051:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2052:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2053:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2054:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2055:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2056:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2057:             crtR11TxYZeroLast = crtR11TxYZero;
  2058:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2059:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2060:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2061:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2062:             crtAllStamp += 2;
  2063:           }
  2064:           crtDataY = 0;  //データY座標を0で初期化
  2065:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2066:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2067:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  2068:             crtClearStandby = false;
  2069:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2070:           }
  2071:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  2072:           if (!XEiJ.PNL_USE_THREAD) {
  2073:             crtDirtyY0 = -1;  //ダーティフラグをクリア
  2074:           }
  2075:           if (XEiJ.PNL_USE_THREAD) {
  2076:             crtAllStamp += 2;
  2077:           }
  2078:           if (SpriteScreen.SPR_THREE_STEPS) {
  2079:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  2080:             if (SpriteScreen.sprActive) {
  2081:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  2082:                 SpriteScreen.sprLatched = false;
  2083:               }
  2084:               if (!XEiJ.PNL_USE_THREAD) {
  2085:                 crtAllStamp += 2;
  2086:               }
  2087:               //ラスタ(dst=-2,src=-2)
  2088:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2089:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2090:               //ラスタ(dst=-1,src=-1)
  2091:               //表(-1)を表(1)として再利用する
  2092:               SpriteScreen.sprStep1 (1);  //表(1)にスプライト(1)を並べる
  2093:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2094:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2095:             }
  2096:           }
  2097:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2098:             crtStereoscopicStart ();
  2099:           }
  2100:           TickerQueue.tkqAdd (crtTicker = NormalDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  2101:         }
  2102:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2103:         //垂直映像期間開始ラスタではないとき
  2104:         TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2105:       }
  2106:     }
  2107:   };
  2108:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  2109:   public static final TickerQueue.Ticker NormalDrawIdleSync = new TickerQueue.Ticker () {
  2110:     @Override protected void tick () {
  2111:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2112:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2113:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2114:         MercuryUnit.mu4HsyncStart (crtClock);
  2115:       }
  2116:       TickerQueue.tkqAdd (crtTicker = NormalDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2117:     }
  2118:   };
  2119:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2120:   public static final TickerQueue.Ticker NormalDrawIdleBackDisp = new TickerQueue.Ticker () {
  2121:     @Override protected void tick () {
  2122:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2123:       TickerQueue.tkqAdd (crtTicker = NormalDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  2124:     }
  2125:   };
  2126:   //    (4)描画フレームの垂直映像期間の水平フロントポーチ
  2127:   public static final TickerQueue.Ticker NormalDrawDispFront = new TickerQueue.Ticker () {
  2128:     @Override protected void tick () {
  2129:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2130:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2131:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2132:           crtDoRasterCopy ();  //ラスタコピー実行
  2133:         }
  2134:         if (RasterBreakPoint.RBP_ON) {
  2135:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2136:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2137:           }
  2138:         }
  2139:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2140:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2141:           if (irq == 0) {  //IRQ信号が0になったとき
  2142:             if (RasterBreakPoint.RBP_ON) {
  2143:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2144:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2145:               }
  2146:             }
  2147:             MC68901.mfpRintFall ();  //IRQ開始
  2148:           } else {  //IRQ信号が0でなくなったとき
  2149:             MC68901.mfpRintRise ();  //IRQ終了
  2150:           }
  2151:         }
  2152:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2153:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2154:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2155:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2156:               crtStereoscopicDrawRaster (crtScreenY);
  2157:             }
  2158:           }
  2159:           crtScreenY++;  //スクリーンY座標を1増やす
  2160:           crtDataY++;  //データY座標を1増やす
  2161:           TickerQueue.tkqAdd (crtTicker = NormalDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  2162:         } else {  //垂直空白期間開始ラスタのとき
  2163:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2164:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2165:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2166:               crtStereoscopicDrawRaster (crtScreenY);
  2167:             }
  2168:           }
  2169:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2170:           if (!XEiJ.PNL_USE_THREAD) {
  2171:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  2172:               crtUpdateScreen ();  //スクリーンを更新する
  2173:               if (CRT_ENABLE_INTERMITTENT) {
  2174:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  2175:               }
  2176:             }
  2177:           }
  2178:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2179:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2180:           }
  2181:           if (XEiJ.PNL_USE_THREAD) {
  2182:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  2183:           }
  2184:           TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2185:         }
  2186:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2187:         //垂直空白期間開始ラスタではないとき
  2188:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2189:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2190:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2191:             crtStereoscopicDrawRaster (crtScreenY);
  2192:           }
  2193:         }
  2194:         crtScreenY++;  //スクリーンY座標を1増やす
  2195:         crtDataY++;  //データY座標を1増やす
  2196:         TickerQueue.tkqAdd (crtTicker = NormalDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  2197:       }
  2198:     }
  2199:   };
  2200:   //    (5)描画フレームの垂直映像期間の水平同期パルス
  2201:   public static final TickerQueue.Ticker NormalDrawDispSync = new TickerQueue.Ticker () {
  2202:     @Override protected void tick () {
  2203:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2204:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2205:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2206:         MercuryUnit.mu4HsyncStart (crtClock);
  2207:       }
  2208:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2209:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2210:         crtRasterStamp[crtDataY] = 0;
  2211:       }
  2212:       TickerQueue.tkqAdd (crtTicker = NormalDrawDispBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6)描画フレームの垂直映像期間の水平バックポーチ
  2213:     }
  2214:   };
  2215:   //    (6)描画フレームの垂直映像期間の水平バックポーチ
  2216:   public static final TickerQueue.Ticker NormalDrawDispBack = new TickerQueue.Ticker () {
  2217:     @Override protected void tick () {
  2218:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2219:       TickerQueue.tkqAdd (crtTicker = NormalDrawDispDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7)描画フレームの垂直映像期間の水平映像期間
  2220:     }
  2221:   };
  2222:   //    (7)描画フレームの垂直映像期間の水平映像期間
  2223:   public static final TickerQueue.Ticker NormalDrawDispDisp = new TickerQueue.Ticker () {
  2224:     @Override protected void tick () {
  2225:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  2226:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  2227:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  2228:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  2229:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2230:           crtStereoscopicDrawRaster (crtScreenY);
  2231:         }
  2232:         if (!XEiJ.PNL_USE_THREAD) {
  2233:           if (crtDirtyY0 < 0) {
  2234:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  2235:           }
  2236:           crtDirtyY1 = crtScreenY;
  2237:         }
  2238:       }
  2239:       if (SpriteScreen.SPR_THREE_STEPS) {
  2240:         if (SpriteScreen.sprActive) {
  2241:           //ラスタ(dst=src)
  2242:           //表(dst)を表(dst+2)として再利用する
  2243:           SpriteScreen.sprStep1 (crtDataY + 2);  //表(dst+2)にスプライト(src+2)を並べる
  2244:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  2245:           SpriteScreen.sprStep2 (crtDataY + 1);  //表(dst+1)にバックグラウンド(src+1)を並べる
  2246:         }
  2247:       }
  2248:       TickerQueue.tkqAdd (crtTicker = NormalDrawDispFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4)描画フレームの垂直映像期間の水平フロントポーチ
  2249:     }
  2250:   };
  2251:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  2252:   public static final TickerQueue.Ticker NormalOmitIdleFront = new TickerQueue.Ticker () {
  2253:     @Override protected void tick () {
  2254:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2255:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2256:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2257:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2258:         }
  2259:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2260:           crtDoRasterCopy ();  //ラスタコピー実行
  2261:         }
  2262:         if (RasterBreakPoint.RBP_ON) {
  2263:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2264:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2265:           }
  2266:         }
  2267:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2268:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2269:           if (irq == 0) {  //IRQ信号が0になったとき
  2270:             if (RasterBreakPoint.RBP_ON) {
  2271:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2272:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2273:               }
  2274:             }
  2275:             MC68901.mfpRintFall ();  //IRQ開始
  2276:           } else {  //IRQ信号が0でなくなったとき
  2277:             MC68901.mfpRintRise ();  //IRQ終了
  2278:           }
  2279:         }
  2280:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2281:           TickerQueue.tkqAdd (crtTicker = NormalOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2282:         } else {  //垂直映像期間開始ラスタのとき
  2283:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2284:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2285:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2286:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2287:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2288:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2289:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2290:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2291:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2292:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2293:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2294:             crtR11TxYZeroLast = crtR11TxYZero;
  2295:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2296:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2297:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2298:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2299:             crtAllStamp += 2;
  2300:           }
  2301:           crtDataY = 0;  //データY座標を0で初期化
  2302:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2303:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2304:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  2305:             crtClearStandby = false;
  2306:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2307:           }
  2308:           if (XEiJ.PNL_USE_THREAD) {
  2309:             crtAllStamp += 2;
  2310:           }
  2311:           if (SpriteScreen.SPR_THREE_STEPS) {
  2312:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  2313:             if (SpriteScreen.sprActive) {
  2314:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  2315:                 SpriteScreen.sprLatched = false;
  2316:               }
  2317:               if (!XEiJ.PNL_USE_THREAD) {
  2318:                 crtAllStamp += 2;
  2319:               }
  2320:               //ラスタ(dst=-2,src=-2)
  2321:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2322:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2323:               //ラスタ(dst=-1,src=-1)
  2324:               //表(-1)を表(1)として再利用する
  2325:               SpriteScreen.sprStep1 (1);  //表(1)にスプライト(1)を並べる
  2326:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2327:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2328:             }
  2329:           }
  2330:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2331:             crtStereoscopicStart ();
  2332:           }
  2333:           TickerQueue.tkqAdd (crtTicker = NormalOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  2334:         }
  2335:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2336:         //垂直映像期間開始ラスタではないとき
  2337:         TickerQueue.tkqAdd (crtTicker = NormalOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2338:       }
  2339:     }
  2340:   };
  2341:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  2342:   public static final TickerQueue.Ticker NormalOmitIdleSync = new TickerQueue.Ticker () {
  2343:     @Override protected void tick () {
  2344:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2345:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2346:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2347:         MercuryUnit.mu4HsyncStart (crtClock);
  2348:       }
  2349:       TickerQueue.tkqAdd (crtTicker = NormalOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2350:     }
  2351:   };
  2352:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2353:   public static final TickerQueue.Ticker NormalOmitIdleBackDisp = new TickerQueue.Ticker () {
  2354:     @Override protected void tick () {
  2355:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2356:       TickerQueue.tkqAdd (crtTicker = NormalOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  2357:     }
  2358:   };
  2359:   //    (11)省略フレームの垂直映像期間の水平フロントポーチ
  2360:   public static final TickerQueue.Ticker NormalOmitDispFront = new TickerQueue.Ticker () {
  2361:     @Override protected void tick () {
  2362:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2363:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2364:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2365:           crtDoRasterCopy ();  //ラスタコピー実行
  2366:         }
  2367:         if (RasterBreakPoint.RBP_ON) {
  2368:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2369:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2370:           }
  2371:         }
  2372:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2373:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2374:           if (irq == 0) {  //IRQ信号が0になったとき
  2375:             if (RasterBreakPoint.RBP_ON) {
  2376:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2377:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2378:               }
  2379:             }
  2380:             MC68901.mfpRintFall ();  //IRQ開始
  2381:           } else {  //IRQ信号が0でなくなったとき
  2382:             MC68901.mfpRintRise ();  //IRQ終了
  2383:           }
  2384:         }
  2385:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2386:           TickerQueue.tkqAdd (crtTicker = NormalOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  2387:         } else {  //垂直空白期間開始ラスタのとき
  2388:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2389:           if (CRT_ENABLE_INTERMITTENT) {
  2390:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  2391:           }
  2392:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2393:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2394:           }
  2395:           if (XEiJ.PNL_USE_THREAD) {
  2396:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  2397:           }
  2398:           TickerQueue.tkqAdd (crtTicker = NormalOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2399:         }
  2400:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2401:         //垂直空白期間開始ラスタではないとき
  2402:         TickerQueue.tkqAdd (crtTicker = NormalOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  2403:       }
  2404:     }
  2405:   };
  2406:   //    (12)省略フレームの垂直映像期間の水平同期パルス
  2407:   public static final TickerQueue.Ticker NormalOmitDispSync = new TickerQueue.Ticker () {
  2408:     @Override protected void tick () {
  2409:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2410:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2411:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2412:         MercuryUnit.mu4HsyncStart (crtClock);
  2413:       }
  2414:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2415:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2416:         crtRasterStamp[crtDataY] = 0;
  2417:       }
  2418:       TickerQueue.tkqAdd (crtTicker = NormalOmitDispBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  2419:     }
  2420:   };
  2421:   //    (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  2422:   public static final TickerQueue.Ticker NormalOmitDispBackDisp = new TickerQueue.Ticker () {
  2423:     @Override protected void tick () {
  2424:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2425:       TickerQueue.tkqAdd (crtTicker = NormalOmitDispFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11)省略フレームの垂直映像期間の水平フロントポーチ
  2426:     }
  2427:   };
  2428: 
  2429:   //----------------------------------------------------------------
  2430:   //  ラスタ2度読み
  2431:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  2432:   public static final TickerQueue.Ticker DuplicationStart = new TickerQueue.Ticker () {
  2433:     @Override protected void tick () {
  2434:       if (MC68901.mfpGpipHsync != 0) {
  2435:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2436:       }
  2437:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  2438:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2439:         crtDoRasterCopy ();  //ラスタコピー実行
  2440:       }
  2441:       if (RasterBreakPoint.RBP_ON) {
  2442:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2443:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2444:         }
  2445:       }
  2446:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2447:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2448:         if (irq == 0) {  //IRQ信号が0になったとき
  2449:           if (RasterBreakPoint.RBP_ON) {
  2450:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2451:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2452:             }
  2453:           }
  2454:           MC68901.mfpRintFall ();  //IRQ開始
  2455:         } else {  //IRQ信号が0でなくなったとき
  2456:           MC68901.mfpRintRise ();  //IRQ終了
  2457:         }
  2458:       }
  2459:       if (MC68901.mfpGpipVdisp != 0) {
  2460:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  2461:       }
  2462:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  2463:       if (CRT_ENABLE_INTERMITTENT) {
  2464:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  2465:       }
  2466:       if (!XEiJ.PNL_USE_THREAD) {
  2467:         if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2468:           crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2469:         }
  2470:       }
  2471:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2472:     }
  2473:   };
  2474:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  2475:   public static final TickerQueue.Ticker DuplicationDrawIdleFront = new TickerQueue.Ticker () {
  2476:     @Override protected void tick () {
  2477:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2478:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2479:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2480:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2481:         }
  2482:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2483:           crtDoRasterCopy ();  //ラスタコピー実行
  2484:         }
  2485:         if (RasterBreakPoint.RBP_ON) {
  2486:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2487:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2488:           }
  2489:         }
  2490:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2491:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2492:           if (irq == 0) {  //IRQ信号が0になったとき
  2493:             if (RasterBreakPoint.RBP_ON) {
  2494:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2495:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2496:               }
  2497:             }
  2498:             MC68901.mfpRintFall ();  //IRQ開始
  2499:           } else {  //IRQ信号が0でなくなったとき
  2500:             MC68901.mfpRintRise ();  //IRQ終了
  2501:           }
  2502:         }
  2503:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2504:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2505:         } else {  //垂直映像期間開始ラスタのとき
  2506:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2507:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2508:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2509:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2510:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2511:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2512:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2513:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2514:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2515:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2516:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2517:             crtR11TxYZeroLast = crtR11TxYZero;
  2518:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2519:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2520:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2521:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2522:             crtAllStamp += 2;
  2523:           }
  2524:           crtDataY = 0;  //データY座標を0で初期化
  2525:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2526:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2527:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  2528:             crtClearStandby = false;
  2529:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2530:           }
  2531:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  2532:           if (!XEiJ.PNL_USE_THREAD) {
  2533:             crtDirtyY0 = -1;  //ダーティフラグをクリア
  2534:           }
  2535:           if (XEiJ.PNL_USE_THREAD) {
  2536:             crtAllStamp += 2;
  2537:           }
  2538:           if (SpriteScreen.SPR_THREE_STEPS) {
  2539:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  2540:             if (SpriteScreen.sprActive) {
  2541:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  2542:                 SpriteScreen.sprLatched = false;
  2543:               }
  2544:               if (!XEiJ.PNL_USE_THREAD) {
  2545:                 crtAllStamp += 2;
  2546:               }
  2547:               //偶数ラスタ(dst=-2,src=-1)
  2548:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2549:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2550:               //奇数ラスタ(dst=-1,src=-1)
  2551:               //表(-1)を表(1)として再利用する
  2552:               SpriteScreen.sprStep1 (0);  //表(1)にスプライト(0)を並べる
  2553:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2554:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2555:             }
  2556:           }
  2557:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2558:             crtStereoscopicStart ();
  2559:           }
  2560:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2561:         }
  2562:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2563:         //垂直映像期間開始ラスタではないとき
  2564:         TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2565:       }
  2566:     }
  2567:   };
  2568:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  2569:   public static final TickerQueue.Ticker DuplicationDrawIdleSync = new TickerQueue.Ticker () {
  2570:     @Override protected void tick () {
  2571:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2572:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2573:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2574:         MercuryUnit.mu4HsyncStart (crtClock);
  2575:       }
  2576:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2577:     }
  2578:   };
  2579:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2580:   public static final TickerQueue.Ticker DuplicationDrawIdleBackDisp = new TickerQueue.Ticker () {
  2581:     @Override protected void tick () {
  2582:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2583:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  2584:     }
  2585:   };
  2586:   //    (4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2587:   public static final TickerQueue.Ticker DuplicationDrawDispEvenFront = new TickerQueue.Ticker () {
  2588:     @Override protected void tick () {
  2589:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2590:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2591:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2592:           crtDoRasterCopy ();  //ラスタコピー実行
  2593:         }
  2594:         if (RasterBreakPoint.RBP_ON) {
  2595:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2596:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2597:           }
  2598:         }
  2599:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2600:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2601:           if (irq == 0) {  //IRQ信号が0になったとき
  2602:             if (RasterBreakPoint.RBP_ON) {
  2603:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2604:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2605:               }
  2606:             }
  2607:             MC68901.mfpRintFall ();  //IRQ開始
  2608:           } else {  //IRQ信号が0でなくなったとき
  2609:             MC68901.mfpRintRise ();  //IRQ終了
  2610:           }
  2611:         }
  2612:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2613:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2614:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2615:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2616:               crtStereoscopicDrawRaster (crtScreenY);
  2617:             }
  2618:           }
  2619:           crtScreenY++;  //スクリーンY座標を1増やす
  2620:           crtDataY++;  //データY座標を1増やす
  2621:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2622:         } else {  //垂直空白期間開始ラスタのとき
  2623:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2624:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2625:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2626:               crtStereoscopicDrawRaster (crtScreenY);
  2627:             }
  2628:           }
  2629:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2630:           if (!XEiJ.PNL_USE_THREAD) {
  2631:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  2632:               crtUpdateScreen ();  //スクリーンを更新する
  2633:               if (CRT_ENABLE_INTERMITTENT) {
  2634:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  2635:               }
  2636:             }
  2637:           }
  2638:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2639:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2640:           }
  2641:           if (XEiJ.PNL_USE_THREAD) {
  2642:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  2643:           }
  2644:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2645:         }
  2646:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2647:         //垂直空白期間開始ラスタではないとき
  2648:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2649:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2650:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2651:             crtStereoscopicDrawRaster (crtScreenY);
  2652:           }
  2653:         }
  2654:         crtScreenY++;  //スクリーンY座標を1増やす
  2655:         crtDataY++;  //データY座標を1増やす
  2656:         TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2657:       }
  2658:     }
  2659:   };
  2660:   //    (4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2661:   public static final TickerQueue.Ticker DuplicationDrawDispOddFront = new TickerQueue.Ticker () {
  2662:     @Override protected void tick () {
  2663:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2664:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2665:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2666:           crtDoRasterCopy ();  //ラスタコピー実行
  2667:         }
  2668:         if (RasterBreakPoint.RBP_ON) {
  2669:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2670:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2671:           }
  2672:         }
  2673:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2674:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2675:           if (irq == 0) {  //IRQ信号が0になったとき
  2676:             if (RasterBreakPoint.RBP_ON) {
  2677:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2678:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2679:               }
  2680:             }
  2681:             MC68901.mfpRintFall ();  //IRQ開始
  2682:           } else {  //IRQ信号が0でなくなったとき
  2683:             MC68901.mfpRintRise ();  //IRQ終了
  2684:           }
  2685:         }
  2686:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2687:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2688:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2689:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2690:               crtStereoscopicDrawRaster (crtScreenY);
  2691:             }
  2692:           }
  2693:           crtScreenY++;  //スクリーンY座標を1増やす
  2694:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2695:         } else {  //垂直空白期間開始ラスタのとき
  2696:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2697:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2698:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2699:               crtStereoscopicDrawRaster (crtScreenY);
  2700:             }
  2701:           }
  2702:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2703:           if (!XEiJ.PNL_USE_THREAD) {
  2704:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  2705:               crtUpdateScreen ();  //スクリーンを更新する
  2706:               if (CRT_ENABLE_INTERMITTENT) {
  2707:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  2708:               }
  2709:             }
  2710:           }
  2711:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2712:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2713:           }
  2714:           if (XEiJ.PNL_USE_THREAD) {
  2715:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  2716:           }
  2717:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2718:         }
  2719:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2720:         //垂直空白期間開始ラスタではないとき
  2721:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2722:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2723:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2724:             crtStereoscopicDrawRaster (crtScreenY);
  2725:           }
  2726:         }
  2727:         crtScreenY++;  //スクリーンY座標を1増やす
  2728:         TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2729:       }
  2730:     }
  2731:   };
  2732:   //    (5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2733:   public static final TickerQueue.Ticker DuplicationDrawDispEvenSync = new TickerQueue.Ticker () {
  2734:     @Override protected void tick () {
  2735:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2736:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2737:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2738:         MercuryUnit.mu4HsyncStart (crtClock);
  2739:       }
  2740:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2741:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2742:         crtRasterStamp[crtDataY] = 0;
  2743:       }
  2744:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ
  2745:     }
  2746:   };
  2747:   //    (5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2748:   public static final TickerQueue.Ticker DuplicationDrawDispOddSync = new TickerQueue.Ticker () {
  2749:     @Override protected void tick () {
  2750:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2751:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2752:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2753:         MercuryUnit.mu4HsyncStart (crtClock);
  2754:       }
  2755:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ
  2756:     }
  2757:   };
  2758:   //    (6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ
  2759:   public static final TickerQueue.Ticker DuplicationDrawDispEvenBack = new TickerQueue.Ticker () {
  2760:     @Override protected void tick () {
  2761:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2762:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間
  2763:     }
  2764:   };
  2765:   //    (6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ
  2766:   public static final TickerQueue.Ticker DuplicationDrawDispOddBack = new TickerQueue.Ticker () {
  2767:     @Override protected void tick () {
  2768:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2769:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間
  2770:     }
  2771:   };
  2772:   //    (7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間
  2773:   public static final TickerQueue.Ticker DuplicationDrawDispEvenDisp = new TickerQueue.Ticker () {
  2774:     @Override protected void tick () {
  2775:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  2776:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  2777:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  2778:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2779:           crtStereoscopicDrawRaster (crtScreenY);
  2780:         }
  2781:         if (!XEiJ.PNL_USE_THREAD) {
  2782:           if (crtDirtyY0 < 0) {
  2783:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  2784:           }
  2785:           crtDirtyY1 = crtScreenY;  //偶数ラスタで終了する可能性があるので偶数ラスタでもcrtDirtyY1を更新する
  2786:         }
  2787:       }
  2788:       if (SpriteScreen.SPR_THREE_STEPS) {
  2789:         if (SpriteScreen.sprActive) {
  2790:           //偶数ラスタ(dst=src*2)
  2791:           //表(dst)を表(dst+2)として再利用する
  2792:           SpriteScreen.sprStep1 (crtDataY + 1);  //表(dst+2)にスプライト(src+1)を並べる
  2793:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  2794:           SpriteScreen.sprStep2 (crtDataY);  //表(dst+1)にバックグラウンド(src)を並べる
  2795:         }
  2796:       }
  2797:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2798:     }
  2799:   };
  2800:   //    (7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間
  2801:   public static final TickerQueue.Ticker DuplicationDrawDispOddDisp = new TickerQueue.Ticker () {
  2802:     @Override protected void tick () {
  2803:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  2804:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  2805:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  2806:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  2807:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2808:           crtStereoscopicDrawRaster (crtScreenY);
  2809:         }
  2810:         if (!XEiJ.PNL_USE_THREAD) {
  2811:           if (crtDirtyY0 < 0) {
  2812:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット。奇数ラスタの直前でパレットやスクロール位置が変化したとき偶数ラスタでcrtDirtyY0が更新されていない可能性があるので奇数ラスタでもcrtDirtyY0を更新する
  2813:           }
  2814:           crtDirtyY1 = crtScreenY;
  2815:         }
  2816:       }
  2817:       if (SpriteScreen.SPR_THREE_STEPS) {
  2818:         if (SpriteScreen.sprActive) {
  2819:           //奇数ラスタ(dst=src*2+1)
  2820:           //表(dst)を表(dst+2)として再利用する
  2821:           SpriteScreen.sprStep1 (crtDataY + 1);  //表(dst+2)にスプライト(src+1)を並べる
  2822:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  2823:           SpriteScreen.sprStep2 (crtDataY + 1);  //表(dst+1)にバックグラウンド(src+1)を並べる
  2824:         }
  2825:       }
  2826:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2827:     }
  2828:   };
  2829:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  2830:   public static final TickerQueue.Ticker DuplicationOmitIdleFront = new TickerQueue.Ticker () {
  2831:     @Override protected void tick () {
  2832:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2833:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2834:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2835:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2836:         }
  2837:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2838:           crtDoRasterCopy ();  //ラスタコピー実行
  2839:         }
  2840:         if (RasterBreakPoint.RBP_ON) {
  2841:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2842:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2843:           }
  2844:         }
  2845:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2846:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2847:           if (irq == 0) {  //IRQ信号が0になったとき
  2848:             if (RasterBreakPoint.RBP_ON) {
  2849:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2850:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2851:               }
  2852:             }
  2853:             MC68901.mfpRintFall ();  //IRQ開始
  2854:           } else {  //IRQ信号が0でなくなったとき
  2855:             MC68901.mfpRintRise ();  //IRQ終了
  2856:           }
  2857:         }
  2858:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2859:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2860:         } else {  //垂直映像期間開始ラスタのとき
  2861:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2862:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2863:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2864:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2865:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2866:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2867:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2868:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2869:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2870:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2871:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2872:             crtR11TxYZeroLast = crtR11TxYZero;
  2873:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2874:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2875:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2876:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2877:             crtAllStamp += 2;
  2878:           }
  2879:           crtDataY = 0;  //データY座標を0で初期化
  2880:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2881:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2882:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  2883:             crtClearStandby = false;
  2884:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2885:           }
  2886:           if (XEiJ.PNL_USE_THREAD) {
  2887:             crtAllStamp += 2;
  2888:           }
  2889:           if (SpriteScreen.SPR_THREE_STEPS) {
  2890:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  2891:             if (SpriteScreen.sprActive) {
  2892:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  2893:                 SpriteScreen.sprLatched = false;
  2894:               }
  2895:               if (!XEiJ.PNL_USE_THREAD) {
  2896:                 crtAllStamp += 2;
  2897:               }
  2898:               //偶数ラスタ(dst=-2,src=-1)
  2899:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2900:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2901:               //奇数ラスタ(dst=-1,src=-1)
  2902:               //表(-1)を表(1)として再利用する
  2903:               SpriteScreen.sprStep1 (0);  //表(1)にスプライト(0)を並べる
  2904:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2905:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2906:             }
  2907:           }
  2908:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2909:             crtStereoscopicStart ();
  2910:           }
  2911:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2912:         }
  2913:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2914:         //垂直映像期間開始ラスタではないとき
  2915:         TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2916:       }
  2917:     }
  2918:   };
  2919:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  2920:   public static final TickerQueue.Ticker DuplicationOmitIdleSync = new TickerQueue.Ticker () {
  2921:     @Override protected void tick () {
  2922:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2923:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  2924:           crtR05VSyncEndCurr <= crtRasterNumber) {
  2925:         MercuryUnit.mu4HsyncStart (crtClock);
  2926:       }
  2927:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2928:     }
  2929:   };
  2930:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2931:   public static final TickerQueue.Ticker DuplicationOmitIdleBackDisp = new TickerQueue.Ticker () {
  2932:     @Override protected void tick () {
  2933:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2934:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  2935:     }
  2936:   };
  2937:   //    (11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2938:   public static final TickerQueue.Ticker DuplicationOmitDispEvenFront = new TickerQueue.Ticker () {
  2939:     @Override protected void tick () {
  2940:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2941:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2942:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2943:           crtDoRasterCopy ();  //ラスタコピー実行
  2944:         }
  2945:         if (RasterBreakPoint.RBP_ON) {
  2946:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2947:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2948:           }
  2949:         }
  2950:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2951:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2952:           if (irq == 0) {  //IRQ信号が0になったとき
  2953:             if (RasterBreakPoint.RBP_ON) {
  2954:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2955:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2956:               }
  2957:             }
  2958:             MC68901.mfpRintFall ();  //IRQ開始
  2959:           } else {  //IRQ信号が0でなくなったとき
  2960:             MC68901.mfpRintRise ();  //IRQ終了
  2961:           }
  2962:         }
  2963:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2964:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2965:         } else {  //垂直空白期間開始ラスタのとき
  2966:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2967:           if (CRT_ENABLE_INTERMITTENT) {
  2968:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  2969:           }
  2970:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2971:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2972:           }
  2973:           if (XEiJ.PNL_USE_THREAD) {
  2974:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  2975:           }
  2976:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2977:         }
  2978:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2979:         //垂直空白期間開始ラスタではないとき
  2980:         TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2981:       }
  2982:     }
  2983:   };
  2984:   //    (11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2985:   public static final TickerQueue.Ticker DuplicationOmitDispOddFront = new TickerQueue.Ticker () {
  2986:     @Override protected void tick () {
  2987:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2988:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2989:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2990:           crtDoRasterCopy ();  //ラスタコピー実行
  2991:         }
  2992:         if (RasterBreakPoint.RBP_ON) {
  2993:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2994:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2995:           }
  2996:         }
  2997:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2998:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2999:           if (irq == 0) {  //IRQ信号が0になったとき
  3000:             if (RasterBreakPoint.RBP_ON) {
  3001:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3002:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3003:               }
  3004:             }
  3005:             MC68901.mfpRintFall ();  //IRQ開始
  3006:           } else {  //IRQ信号が0でなくなったとき
  3007:             MC68901.mfpRintRise ();  //IRQ終了
  3008:           }
  3009:         }
  3010:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3011:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  3012:         } else {  //垂直空白期間開始ラスタのとき
  3013:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3014:           if (CRT_ENABLE_INTERMITTENT) {
  3015:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  3016:           }
  3017:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3018:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3019:           }
  3020:           if (XEiJ.PNL_USE_THREAD) {
  3021:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  3022:           }
  3023:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3024:         }
  3025:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3026:         //垂直空白期間開始ラスタではないとき
  3027:         TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  3028:       }
  3029:     }
  3030:   };
  3031:   //    (12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  3032:   public static final TickerQueue.Ticker DuplicationOmitDispEvenSync = new TickerQueue.Ticker () {
  3033:     @Override protected void tick () {
  3034:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3035:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3036:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3037:         MercuryUnit.mu4HsyncStart (crtClock);
  3038:       }
  3039:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3040:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3041:         crtRasterStamp[crtDataY] = 0;
  3042:       }
  3043:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間
  3044:     }
  3045:   };
  3046:   //    (12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  3047:   public static final TickerQueue.Ticker DuplicationOmitDispOddSync = new TickerQueue.Ticker () {
  3048:     @Override protected void tick () {
  3049:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3050:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3051:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3052:         MercuryUnit.mu4HsyncStart (crtClock);
  3053:       }
  3054:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間
  3055:     }
  3056:   };
  3057:   //    (13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間
  3058:   public static final TickerQueue.Ticker DuplicationOmitDispEvenBackDisp = new TickerQueue.Ticker () {
  3059:     @Override protected void tick () {
  3060:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3061:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  3062:     }
  3063:   };
  3064:   //    (13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間
  3065:   public static final TickerQueue.Ticker DuplicationOmitDispOddBackDisp = new TickerQueue.Ticker () {
  3066:     @Override protected void tick () {
  3067:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3068:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  3069:     }
  3070:   };
  3071: 
  3072:   //----------------------------------------------------------------
  3073:   //  インターレース
  3074:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  3075:   public static final TickerQueue.Ticker InterlaceStart = new TickerQueue.Ticker () {
  3076:     @Override protected void tick () {
  3077:       if (MC68901.mfpGpipHsync != 0) {
  3078:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3079:       }
  3080:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  3081:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3082:         crtDoRasterCopy ();  //ラスタコピー実行
  3083:       }
  3084:       if (RasterBreakPoint.RBP_ON) {
  3085:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3086:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3087:         }
  3088:       }
  3089:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3090:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3091:         if (irq == 0) {  //IRQ信号が0になったとき
  3092:           if (RasterBreakPoint.RBP_ON) {
  3093:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3094:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3095:             }
  3096:           }
  3097:           MC68901.mfpRintFall ();  //IRQ開始
  3098:         } else {  //IRQ信号が0でなくなったとき
  3099:           MC68901.mfpRintRise ();  //IRQ終了
  3100:         }
  3101:       }
  3102:       if (MC68901.mfpGpipVdisp != 0) {
  3103:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  3104:       }
  3105:       crtFrameParity = 0;  //フレームパリティを0で初期化
  3106:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  3107:       if (CRT_ENABLE_INTERMITTENT) {
  3108:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  3109:       }
  3110:       if (!XEiJ.PNL_USE_THREAD) {
  3111:         if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3112:           crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3113:         }
  3114:       }
  3115:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3116:     }
  3117:   };
  3118:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  3119:   public static final TickerQueue.Ticker InterlaceDrawIdleFront = new TickerQueue.Ticker () {
  3120:     @Override protected void tick () {
  3121:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3122:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3123:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3124:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3125:         }
  3126:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3127:           crtDoRasterCopy ();  //ラスタコピー実行
  3128:         }
  3129:         if (RasterBreakPoint.RBP_ON) {
  3130:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3131:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3132:           }
  3133:         }
  3134:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3135:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3136:           if (irq == 0) {  //IRQ信号が0になったとき
  3137:             if (RasterBreakPoint.RBP_ON) {
  3138:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3139:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3140:               }
  3141:             }
  3142:             MC68901.mfpRintFall ();  //IRQ開始
  3143:           } else {  //IRQ信号が0でなくなったとき
  3144:             MC68901.mfpRintRise ();  //IRQ終了
  3145:           }
  3146:         }
  3147:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3148:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3149:         } else {  //垂直映像期間開始ラスタのとき
  3150:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3151:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3152:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3153:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3154:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3155:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3156:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3157:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3158:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3159:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3160:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3161:             crtR11TxYZeroLast = crtR11TxYZero;
  3162:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3163:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3164:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3165:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3166:             crtAllStamp += 2;
  3167:           }
  3168:           crtDataY = crtFrameParity;  //データY座標をフレームパリティで初期化
  3169:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3170:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3171:           } else if (crtClearStandby &&  //高速クリアの要求があり、
  3172:                      crtFrameParity == 0) {  //偶数フレームのとき
  3173:             crtClearStandby = false;
  3174:             crtClearFrames = 2;  //高速クリアカウンタを2で初期化
  3175:           }
  3176:           crtScreenY = crtFrameParity;  //スクリーンY座標をフレームパリティで初期化
  3177:           if (!XEiJ.PNL_USE_THREAD) {
  3178:             crtDirtyY0 = -1;  //ダーティフラグをクリア
  3179:           }
  3180:           if (XEiJ.PNL_USE_THREAD) {
  3181:             crtAllStamp += 2;
  3182:           }
  3183:           if (SpriteScreen.SPR_THREE_STEPS) {
  3184:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  3185:             if (SpriteScreen.sprActive) {
  3186:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  3187:                 SpriteScreen.sprLatched = false;
  3188:               }
  3189:               if (!XEiJ.PNL_USE_THREAD) {
  3190:                 crtAllStamp += 2;
  3191:               }
  3192:               //ラスタ(dst=-4,src=-4)
  3193:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3194:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3195:               //ラスタ(dst=-2,src=-2)
  3196:               //表(-2)を表(2)として再利用する
  3197:               SpriteScreen.sprStep1 (2);  //表(2)にスプライト(2)を並べる
  3198:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3199:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3200:             }
  3201:           }
  3202:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3203:             crtStereoscopicStart ();
  3204:           }
  3205:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3206:         }
  3207:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3208:         //垂直映像期間開始ラスタではないとき
  3209:         TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3210:       }
  3211:     }
  3212:   };
  3213:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  3214:   public static final TickerQueue.Ticker InterlaceDrawIdleSync = new TickerQueue.Ticker () {
  3215:     @Override protected void tick () {
  3216:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3217:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3218:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3219:         MercuryUnit.mu4HsyncStart (crtClock);
  3220:       }
  3221:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  3222:     }
  3223:   };
  3224:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  3225:   public static final TickerQueue.Ticker InterlaceDrawIdleBackDisp = new TickerQueue.Ticker () {
  3226:     @Override protected void tick () {
  3227:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3228:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  3229:     }
  3230:   };
  3231:   //    (4)描画フレームの垂直映像期間の水平フロントポーチ
  3232:   public static final TickerQueue.Ticker InterlaceDrawDispFront = new TickerQueue.Ticker () {
  3233:     @Override protected void tick () {
  3234:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3235:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3236:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3237:           crtDoRasterCopy ();  //ラスタコピー実行
  3238:         }
  3239:         if (RasterBreakPoint.RBP_ON) {
  3240:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3241:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3242:           }
  3243:         }
  3244:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3245:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3246:           if (irq == 0) {  //IRQ信号が0になったとき
  3247:             if (RasterBreakPoint.RBP_ON) {
  3248:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3249:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3250:               }
  3251:             }
  3252:             MC68901.mfpRintFall ();  //IRQ開始
  3253:           } else {  //IRQ信号が0でなくなったとき
  3254:             MC68901.mfpRintRise ();  //IRQ終了
  3255:           }
  3256:         }
  3257:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3258:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3259:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3260:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3261:               crtStereoscopicDrawRaster (crtScreenY);
  3262:               if (XEiJ.PNL_USE_THREAD) {
  3263:                 crtStereoscopicDrawRaster (crtScreenY ^ 1);
  3264:               }
  3265:             }
  3266:           }
  3267:           crtScreenY += 2;  //スクリーンY座標を2増やす
  3268:           crtDataY += 2;  //データY座標を2増やす
  3269:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3270:         } else {  //垂直空白期間開始ラスタのとき
  3271:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3272:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3273:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3274:               crtStereoscopicDrawRaster (crtScreenY);
  3275:               if (XEiJ.PNL_USE_THREAD) {
  3276:                 crtStereoscopicDrawRaster (crtScreenY ^ 1);
  3277:               }
  3278:             }
  3279:           }
  3280:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3281:           if (!XEiJ.PNL_USE_THREAD) {
  3282:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  3283:               crtUpdateScreen ();  //スクリーンを更新する
  3284:               if (CRT_ENABLE_INTERMITTENT) {
  3285:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  3286:               }
  3287:             }
  3288:           }
  3289:           crtFrameParity ^= 1;  //フレームパリティを反転
  3290:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3291:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3292:           }
  3293:           if (XEiJ.PNL_USE_THREAD) {
  3294:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  3295:           }
  3296:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3297:         }
  3298:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3299:         //垂直空白期間開始ラスタではないとき
  3300:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3301:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3302:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3303:             crtStereoscopicDrawRaster (crtScreenY);
  3304:             if (XEiJ.PNL_USE_THREAD) {
  3305:               crtStereoscopicDrawRaster (crtScreenY ^ 1);
  3306:             }
  3307:           }
  3308:         }
  3309:         crtScreenY += 2;  //スクリーンY座標を2増やす
  3310:         crtDataY += 2;  //データY座標を2増やす
  3311:         TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3312:       }
  3313:     }
  3314:   };
  3315:   //    (5)描画フレームの垂直映像期間の水平同期パルス
  3316:   public static final TickerQueue.Ticker InterlaceDrawDispSync = new TickerQueue.Ticker () {
  3317:     @Override protected void tick () {
  3318:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3319:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3320:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3321:         MercuryUnit.mu4HsyncStart (crtClock);
  3322:       }
  3323:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3324:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3325:         crtRasterStamp[crtDataY] = 0;
  3326:       }
  3327:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6)描画フレームの垂直映像期間の水平バックポーチ
  3328:     }
  3329:   };
  3330:   //    (6)描画フレームの垂直映像期間の水平バックポーチ
  3331:   public static final TickerQueue.Ticker InterlaceDrawDispBack = new TickerQueue.Ticker () {
  3332:     @Override protected void tick () {
  3333:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3334:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7)描画フレームの垂直映像期間の水平映像期間
  3335:     }
  3336:   };
  3337:   //    (7)描画フレームの垂直映像期間の水平映像期間
  3338:   public static final TickerQueue.Ticker InterlaceDrawDispDisp = new TickerQueue.Ticker () {
  3339:     @Override protected void tick () {
  3340:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  3341:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  3342:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  3343:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  3344:         if (XEiJ.PNL_USE_THREAD) {
  3345:           int[] prevBM = XEiJ.pnlBMLeftArray[(XEiJ.pnlBMWrite - 1) & 3];  //直前のフレームのビットマップ
  3346:           int da = (crtScreenY ^ 1) << XEiJ.PNL_BM_OFFSET_BITS;  //コピーするラスタの先頭
  3347:           System.arraycopy (prevBM, da,  //from
  3348:                             XEiJ.pnlBM, da,  //to
  3349:                             XEiJ.pnlScreenWidth);  //length
  3350:         }
  3351:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3352:           crtStereoscopicDrawRaster (crtScreenY);
  3353:           if (XEiJ.PNL_USE_THREAD) {
  3354:             crtStereoscopicDrawRaster (crtScreenY ^ 1);
  3355:           }
  3356:         }
  3357:         if (!XEiJ.PNL_USE_THREAD) {
  3358:           if (crtDirtyY0 < 0) {
  3359:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  3360:           }
  3361:           crtDirtyY1 = crtScreenY;
  3362:         }
  3363:       }
  3364:       if (SpriteScreen.SPR_THREE_STEPS) {
  3365:         if (SpriteScreen.sprActive) {
  3366:           //ラスタ(dst=src)
  3367:           //表(dst)を表(dst+4)として再利用する
  3368:           SpriteScreen.sprStep1 (crtDataY + 4);  //表(dst+4)にスプライト(src+4)を並べる
  3369:           SpriteScreen.sprSwap ();  //表(dst+4)と裏(dst+2)を入れ換える
  3370:           SpriteScreen.sprStep2 (crtDataY + 2);  //表(dst+2)にバックグラウンド(src+2)を並べる
  3371:         }
  3372:       }
  3373:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4)描画フレームの垂直映像期間の水平フロントポーチ
  3374:     }
  3375:   };
  3376:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  3377:   public static final TickerQueue.Ticker InterlaceOmitIdleFront = new TickerQueue.Ticker () {
  3378:     @Override protected void tick () {
  3379:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3380:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3381:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3382:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3383:         }
  3384:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3385:           crtDoRasterCopy ();  //ラスタコピー実行
  3386:         }
  3387:         if (RasterBreakPoint.RBP_ON) {
  3388:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3389:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3390:           }
  3391:         }
  3392:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3393:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3394:           if (irq == 0) {  //IRQ信号が0になったとき
  3395:             if (RasterBreakPoint.RBP_ON) {
  3396:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3397:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3398:               }
  3399:             }
  3400:             MC68901.mfpRintFall ();  //IRQ開始
  3401:           } else {  //IRQ信号が0でなくなったとき
  3402:             MC68901.mfpRintRise ();  //IRQ終了
  3403:           }
  3404:         }
  3405:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3406:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3407:         } else {  //垂直映像期間開始ラスタのとき
  3408:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3409:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3410:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3411:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3412:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3413:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3414:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3415:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3416:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3417:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3418:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3419:             crtR11TxYZeroLast = crtR11TxYZero;
  3420:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3421:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3422:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3423:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3424:             crtAllStamp += 2;
  3425:           }
  3426:           crtDataY = crtFrameParity;  //データY座標をフレームパリティで初期化
  3427:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3428:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3429:           } else if (crtClearStandby &&  //高速クリアの要求があり、
  3430:                      crtFrameParity == 0) {  //偶数フレームのとき
  3431:             crtClearStandby = false;
  3432:             crtClearFrames = 2;  //高速クリアカウンタを2で初期化
  3433:           }
  3434:           if (XEiJ.PNL_USE_THREAD) {
  3435:             crtAllStamp += 2;
  3436:           }
  3437:           if (SpriteScreen.SPR_THREE_STEPS) {
  3438:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  3439:             if (SpriteScreen.sprActive) {
  3440:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  3441:                 SpriteScreen.sprLatched = false;
  3442:               }
  3443:               if (!XEiJ.PNL_USE_THREAD) {
  3444:                 crtAllStamp += 2;
  3445:               }
  3446:               //ラスタ(dst=-4,src=-4)
  3447:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3448:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3449:               //ラスタ(dst=-2,src=-2)
  3450:               //表(-2)を表(2)として再利用する
  3451:               SpriteScreen.sprStep1 (2);  //表(2)にスプライト(2)を並べる
  3452:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3453:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3454:             }
  3455:           }
  3456:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3457:             crtStereoscopicStart ();
  3458:           }
  3459:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3460:         }
  3461:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3462:         //垂直映像期間開始ラスタではないとき
  3463:         TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3464:       }
  3465:     }
  3466:   };
  3467:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  3468:   public static final TickerQueue.Ticker InterlaceOmitIdleSync = new TickerQueue.Ticker () {
  3469:     @Override protected void tick () {
  3470:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3471:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3472:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3473:         MercuryUnit.mu4HsyncStart (crtClock);
  3474:       }
  3475:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3476:     }
  3477:   };
  3478:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3479:   public static final TickerQueue.Ticker InterlaceOmitIdleBackDisp = new TickerQueue.Ticker () {
  3480:     @Override protected void tick () {
  3481:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3482:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  3483:     }
  3484:   };
  3485:   //    (11)省略フレームの垂直映像期間の水平フロントポーチ
  3486:   public static final TickerQueue.Ticker InterlaceOmitDispFront = new TickerQueue.Ticker () {
  3487:     @Override protected void tick () {
  3488:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3489:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3490:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3491:           crtDoRasterCopy ();  //ラスタコピー実行
  3492:         }
  3493:         if (RasterBreakPoint.RBP_ON) {
  3494:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3495:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3496:           }
  3497:         }
  3498:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3499:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3500:           if (irq == 0) {  //IRQ信号が0になったとき
  3501:             if (RasterBreakPoint.RBP_ON) {
  3502:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3503:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3504:               }
  3505:             }
  3506:             MC68901.mfpRintFall ();  //IRQ開始
  3507:           } else {  //IRQ信号が0でなくなったとき
  3508:             MC68901.mfpRintRise ();  //IRQ終了
  3509:           }
  3510:         }
  3511:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3512:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3513:         } else {  //垂直空白期間開始ラスタのとき
  3514:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3515:           if (CRT_ENABLE_INTERMITTENT) {
  3516:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  3517:           }
  3518:           crtFrameParity ^= 1;  //フレームパリティを反転
  3519:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3520:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3521:           }
  3522:           if (XEiJ.PNL_USE_THREAD) {
  3523:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  3524:           }
  3525:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3526:         }
  3527:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3528:         //垂直空白期間開始ラスタではないとき
  3529:         TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3530:       }
  3531:     }
  3532:   };
  3533:   //    (12)省略フレームの垂直映像期間の水平同期パルス
  3534:   public static final TickerQueue.Ticker InterlaceOmitDispSync = new TickerQueue.Ticker () {
  3535:     @Override protected void tick () {
  3536:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3537:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3538:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3539:         MercuryUnit.mu4HsyncStart (crtClock);
  3540:       }
  3541:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3542:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3543:         crtRasterStamp[crtDataY] = 0;
  3544:       }
  3545:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  3546:     }
  3547:   };
  3548:   //    (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  3549:   public static final TickerQueue.Ticker InterlaceOmitDispBackDisp = new TickerQueue.Ticker () {
  3550:     @Override protected void tick () {
  3551:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3552:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11)省略フレームの垂直映像期間の水平フロントポーチ
  3553:     }
  3554:   };
  3555: 
  3556:   //----------------------------------------------------------------
  3557:   //  スリット
  3558:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  3559:   public static final TickerQueue.Ticker SlitStart = new TickerQueue.Ticker () {
  3560:     @Override protected void tick () {
  3561:       if (MC68901.mfpGpipHsync != 0) {
  3562:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3563:       }
  3564:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  3565:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3566:         crtDoRasterCopy ();  //ラスタコピー実行
  3567:       }
  3568:       if (RasterBreakPoint.RBP_ON) {
  3569:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3570:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3571:         }
  3572:       }
  3573:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3574:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3575:         if (irq == 0) {  //IRQ信号が0になったとき
  3576:           if (RasterBreakPoint.RBP_ON) {
  3577:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3578:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3579:             }
  3580:           }
  3581:           MC68901.mfpRintFall ();  //IRQ開始
  3582:         } else {  //IRQ信号が0でなくなったとき
  3583:           MC68901.mfpRintRise ();  //IRQ終了
  3584:         }
  3585:       }
  3586:       if (MC68901.mfpGpipVdisp != 0) {
  3587:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  3588:       }
  3589:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  3590:       if (CRT_ENABLE_INTERMITTENT) {
  3591:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  3592:       }
  3593:       if (!XEiJ.PNL_USE_THREAD) {
  3594:         if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3595:           crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3596:         }
  3597:       }
  3598:       TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3599:     }
  3600:   };
  3601:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  3602:   public static final TickerQueue.Ticker SlitDrawIdleFront = new TickerQueue.Ticker () {
  3603:     @Override protected void tick () {
  3604:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3605:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3606:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3607:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3608:         }
  3609:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3610:           crtDoRasterCopy ();  //ラスタコピー実行
  3611:         }
  3612:         if (RasterBreakPoint.RBP_ON) {
  3613:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3614:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3615:           }
  3616:         }
  3617:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3618:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3619:           if (irq == 0) {  //IRQ信号が0になったとき
  3620:             if (RasterBreakPoint.RBP_ON) {
  3621:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3622:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3623:               }
  3624:             }
  3625:             MC68901.mfpRintFall ();  //IRQ開始
  3626:           } else {  //IRQ信号が0でなくなったとき
  3627:             MC68901.mfpRintRise ();  //IRQ終了
  3628:           }
  3629:         }
  3630:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3631:           TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3632:         } else {  //垂直映像期間開始ラスタのとき
  3633:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3634:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3635:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3636:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3637:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3638:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3639:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3640:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3641:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3642:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3643:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3644:             crtR11TxYZeroLast = crtR11TxYZero;
  3645:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3646:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3647:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3648:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3649:             crtAllStamp += 2;
  3650:           }
  3651:           crtDataY = 0;  //データY座標を0で初期化
  3652:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3653:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3654:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  3655:             crtClearStandby = false;
  3656:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  3657:           }
  3658:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  3659:           if (!XEiJ.PNL_USE_THREAD) {
  3660:             crtDirtyY0 = -1;  //ダーティフラグをクリア
  3661:           }
  3662:           if (XEiJ.PNL_USE_THREAD) {
  3663:             crtAllStamp += 2;
  3664:           }
  3665:           if (SpriteScreen.SPR_THREE_STEPS) {
  3666:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  3667:             if (SpriteScreen.sprActive) {
  3668:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  3669:                 SpriteScreen.sprLatched = false;
  3670:               }
  3671:               if (!XEiJ.PNL_USE_THREAD) {
  3672:                 crtAllStamp += 2;
  3673:               }
  3674:               //ラスタ(dst=-4,src=-2)
  3675:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3676:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3677:               //ラスタ(dst=-2,src=-1)
  3678:               //表(-2)を表(2)として再利用する
  3679:               SpriteScreen.sprStep1 (1);  //表(2)にスプライト(1)を並べる
  3680:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3681:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3682:             }
  3683:           }
  3684:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3685:             crtStereoscopicStart ();
  3686:           }
  3687:           TickerQueue.tkqAdd (crtTicker = SlitDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3688:         }
  3689:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3690:         //垂直映像期間開始ラスタではないとき
  3691:         TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3692:       }
  3693:     }
  3694:   };
  3695:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  3696:   public static final TickerQueue.Ticker SlitDrawIdleSync = new TickerQueue.Ticker () {
  3697:     @Override protected void tick () {
  3698:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3699:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3700:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3701:         MercuryUnit.mu4HsyncStart (crtClock);
  3702:       }
  3703:       TickerQueue.tkqAdd (crtTicker = SlitDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  3704:     }
  3705:   };
  3706:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  3707:   public static final TickerQueue.Ticker SlitDrawIdleBackDisp = new TickerQueue.Ticker () {
  3708:     @Override protected void tick () {
  3709:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3710:       TickerQueue.tkqAdd (crtTicker = SlitDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  3711:     }
  3712:   };
  3713:   //    (4)描画フレームの垂直映像期間の水平フロントポーチ
  3714:   public static final TickerQueue.Ticker SlitDrawDispFront = new TickerQueue.Ticker () {
  3715:     @Override protected void tick () {
  3716:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3717:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3718:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3719:           crtDoRasterCopy ();  //ラスタコピー実行
  3720:         }
  3721:         if (RasterBreakPoint.RBP_ON) {
  3722:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3723:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3724:           }
  3725:         }
  3726:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3727:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3728:           if (irq == 0) {  //IRQ信号が0になったとき
  3729:             if (RasterBreakPoint.RBP_ON) {
  3730:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3731:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3732:               }
  3733:             }
  3734:             MC68901.mfpRintFall ();  //IRQ開始
  3735:           } else {  //IRQ信号が0でなくなったとき
  3736:             MC68901.mfpRintRise ();  //IRQ終了
  3737:           }
  3738:         }
  3739:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3740:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3741:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3742:             crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3743:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3744:               crtStereoscopicDrawRaster (crtScreenY);
  3745:               crtStereoscopicDrawRaster (crtScreenY + 1);
  3746:             }
  3747:           }
  3748:           crtScreenY += 2;  //スクリーンY座標を2増やす
  3749:           crtDataY++;  //データY座標を1増やす
  3750:           TickerQueue.tkqAdd (crtTicker = SlitDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3751:         } else {  //垂直空白期間開始ラスタのとき
  3752:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3753:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3754:             crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3755:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3756:               crtStereoscopicDrawRaster (crtScreenY);
  3757:               crtStereoscopicDrawRaster (crtScreenY + 1);
  3758:             }
  3759:           }
  3760:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3761:           if (!XEiJ.PNL_USE_THREAD) {
  3762:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  3763:               crtUpdateScreen ();  //スクリーンを更新する
  3764:               if (CRT_ENABLE_INTERMITTENT) {
  3765:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  3766:               }
  3767:             }
  3768:           }
  3769:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3770:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3771:           }
  3772:           if (XEiJ.PNL_USE_THREAD) {
  3773:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  3774:           }
  3775:           TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3776:         }
  3777:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3778:         //垂直空白期間開始ラスタではないとき
  3779:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3780:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3781:           crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3782:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3783:             crtStereoscopicDrawRaster (crtScreenY);
  3784:             crtStereoscopicDrawRaster (crtScreenY + 1);
  3785:           }
  3786:         }
  3787:         crtScreenY += 2;  //スクリーンY座標を2増やす
  3788:         crtDataY++;  //データY座標を1増やす
  3789:         TickerQueue.tkqAdd (crtTicker = SlitDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3790:       }
  3791:     }
  3792:   };
  3793:   //    (5)描画フレームの垂直映像期間の水平同期パルス
  3794:   public static final TickerQueue.Ticker SlitDrawDispSync = new TickerQueue.Ticker () {
  3795:     @Override protected void tick () {
  3796:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3797:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3798:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3799:         MercuryUnit.mu4HsyncStart (crtClock);
  3800:       }
  3801:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3802:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3803:         crtRasterStamp[crtDataY] = 0;
  3804:       }
  3805:       TickerQueue.tkqAdd (crtTicker = SlitDrawDispBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6)描画フレームの垂直映像期間の水平バックポーチ
  3806:     }
  3807:   };
  3808:   //    (6)描画フレームの垂直映像期間の水平バックポーチ
  3809:   public static final TickerQueue.Ticker SlitDrawDispBack = new TickerQueue.Ticker () {
  3810:     @Override protected void tick () {
  3811:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3812:       TickerQueue.tkqAdd (crtTicker = SlitDrawDispDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7)描画フレームの垂直映像期間の水平映像期間
  3813:     }
  3814:   };
  3815:   //    (7)描画フレームの垂直映像期間の水平映像期間
  3816:   public static final TickerQueue.Ticker SlitDrawDispDisp = new TickerQueue.Ticker () {
  3817:     @Override protected void tick () {
  3818:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  3819:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  3820:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  3821:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  3822:         crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3823:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3824:           crtStereoscopicDrawRaster (crtScreenY);
  3825:           crtStereoscopicDrawRaster (crtScreenY + 1);
  3826:         }
  3827:         if (!XEiJ.PNL_USE_THREAD) {
  3828:           if (crtDirtyY0 < 0) {
  3829:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  3830:           }
  3831:           crtDirtyY1 = crtScreenY + 1;
  3832:         }
  3833:       }
  3834:       if (SpriteScreen.SPR_THREE_STEPS) {
  3835:         if (SpriteScreen.sprActive) {
  3836:           //ラスタ(dst=src*2)
  3837:           //表(dst)を表(dst+4)として再利用する
  3838:           SpriteScreen.sprStep1 (crtDataY + 2);  //表(dst+4)にスプライト(src+2)を並べる
  3839:           SpriteScreen.sprSwap ();  //表(dst+4)と裏(dst+2)を入れ換える
  3840:           SpriteScreen.sprStep2 (crtDataY + 1);  //表(dst+2)にバックグラウンド(src+1)を並べる
  3841:         }
  3842:       }
  3843:       TickerQueue.tkqAdd (crtTicker = SlitDrawDispFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4)描画フレームの垂直映像期間の水平フロントポーチ
  3844:     }
  3845:   };
  3846:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  3847:   public static final TickerQueue.Ticker SlitOmitIdleFront = new TickerQueue.Ticker () {
  3848:     @Override protected void tick () {
  3849:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3850:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3851:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3852:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3853:         }
  3854:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3855:           crtDoRasterCopy ();  //ラスタコピー実行
  3856:         }
  3857:         if (RasterBreakPoint.RBP_ON) {
  3858:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3859:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3860:           }
  3861:         }
  3862:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3863:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3864:           if (irq == 0) {  //IRQ信号が0になったとき
  3865:             if (RasterBreakPoint.RBP_ON) {
  3866:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3867:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3868:               }
  3869:             }
  3870:             MC68901.mfpRintFall ();  //IRQ開始
  3871:           } else {  //IRQ信号が0でなくなったとき
  3872:             MC68901.mfpRintRise ();  //IRQ終了
  3873:           }
  3874:         }
  3875:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3876:           TickerQueue.tkqAdd (crtTicker = SlitOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3877:         } else {  //垂直映像期間開始ラスタのとき
  3878:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3879:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3880:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3881:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3882:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3883:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3884:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3885:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3886:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3887:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3888:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3889:             crtR11TxYZeroLast = crtR11TxYZero;
  3890:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3891:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3892:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3893:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3894:             crtAllStamp += 2;
  3895:           }
  3896:           crtDataY = 0;  //データY座標を0で初期化
  3897:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3898:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3899:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  3900:             crtClearStandby = false;
  3901:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  3902:           }
  3903:           if (XEiJ.PNL_USE_THREAD) {
  3904:             crtAllStamp += 2;
  3905:           }
  3906:           if (SpriteScreen.SPR_THREE_STEPS) {
  3907:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  3908:             if (SpriteScreen.sprActive) {
  3909:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  3910:                 SpriteScreen.sprLatched = false;
  3911:               }
  3912:               if (!XEiJ.PNL_USE_THREAD) {
  3913:                 crtAllStamp += 2;
  3914:               }
  3915:               //ラスタ(dst=-4,src=-2)
  3916:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3917:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3918:               //ラスタ(dst=-2,src=-1)
  3919:               //表(-2)を表(2)として再利用する
  3920:               SpriteScreen.sprStep1 (1);  //表(2)にスプライト(1)を並べる
  3921:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3922:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3923:             }
  3924:           }
  3925:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3926:             crtStereoscopicStart ();
  3927:           }
  3928:           TickerQueue.tkqAdd (crtTicker = SlitOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3929:         }
  3930:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3931:         //垂直映像期間開始ラスタではないとき
  3932:         TickerQueue.tkqAdd (crtTicker = SlitOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3933:       }
  3934:     }
  3935:   };
  3936:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  3937:   public static final TickerQueue.Ticker SlitOmitIdleSync = new TickerQueue.Ticker () {
  3938:     @Override protected void tick () {
  3939:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3940:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  3941:           crtR05VSyncEndCurr <= crtRasterNumber) {
  3942:         MercuryUnit.mu4HsyncStart (crtClock);
  3943:       }
  3944:       TickerQueue.tkqAdd (crtTicker = SlitOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3945:     }
  3946:   };
  3947:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3948:   public static final TickerQueue.Ticker SlitOmitIdleBackDisp = new TickerQueue.Ticker () {
  3949:     @Override protected void tick () {
  3950:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3951:       TickerQueue.tkqAdd (crtTicker = SlitOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  3952:     }
  3953:   };
  3954:   //    (11)省略フレームの垂直映像期間の水平フロントポーチ
  3955:   public static final TickerQueue.Ticker SlitOmitDispFront = new TickerQueue.Ticker () {
  3956:     @Override protected void tick () {
  3957:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3958:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3959:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3960:           crtDoRasterCopy ();  //ラスタコピー実行
  3961:         }
  3962:         if (RasterBreakPoint.RBP_ON) {
  3963:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3964:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3965:           }
  3966:         }
  3967:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3968:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3969:           if (irq == 0) {  //IRQ信号が0になったとき
  3970:             if (RasterBreakPoint.RBP_ON) {
  3971:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3972:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3973:               }
  3974:             }
  3975:             MC68901.mfpRintFall ();  //IRQ開始
  3976:           } else {  //IRQ信号が0でなくなったとき
  3977:             MC68901.mfpRintRise ();  //IRQ終了
  3978:           }
  3979:         }
  3980:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3981:           TickerQueue.tkqAdd (crtTicker = SlitOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3982:         } else {  //垂直空白期間開始ラスタのとき
  3983:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3984:           if (CRT_ENABLE_INTERMITTENT) {
  3985:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  3986:           }
  3987:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3988:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3989:           }
  3990:           if (XEiJ.PNL_USE_THREAD) {
  3991:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  3992:           }
  3993:           TickerQueue.tkqAdd (crtTicker = SlitOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3994:         }
  3995:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3996:         //垂直空白期間開始ラスタではないとき
  3997:         TickerQueue.tkqAdd (crtTicker = SlitOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3998:       }
  3999:     }
  4000:   };
  4001:   //    (12)省略フレームの垂直映像期間の水平同期パルス
  4002:   public static final TickerQueue.Ticker SlitOmitDispSync = new TickerQueue.Ticker () {
  4003:     @Override protected void tick () {
  4004:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4005:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4006:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4007:         MercuryUnit.mu4HsyncStart (crtClock);
  4008:       }
  4009:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  4010:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  4011:         crtRasterStamp[crtDataY] = 0;
  4012:       }
  4013:       TickerQueue.tkqAdd (crtTicker = SlitOmitDispBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  4014:     }
  4015:   };
  4016:   //    (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  4017:   public static final TickerQueue.Ticker SlitOmitDispBackDisp = new TickerQueue.Ticker () {
  4018:     @Override protected void tick () {
  4019:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4020:       TickerQueue.tkqAdd (crtTicker = SlitOmitDispFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11)省略フレームの垂直映像期間の水平フロントポーチ
  4021:     }
  4022:   };
  4023: 
  4024:   //----------------------------------------------------------------
  4025:   //  ラスタ2度読み(スプライトを除く)
  4026:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  4027:   public static final TickerQueue.Ticker DupExceptSpStart = new TickerQueue.Ticker () {
  4028:     @Override protected void tick () {
  4029:       if (MC68901.mfpGpipHsync != 0) {
  4030:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4031:       }
  4032:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  4033:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4034:         crtDoRasterCopy ();  //ラスタコピー実行
  4035:       }
  4036:       if (RasterBreakPoint.RBP_ON) {
  4037:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4038:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4039:         }
  4040:       }
  4041:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4042:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4043:         if (irq == 0) {  //IRQ信号が0になったとき
  4044:           if (RasterBreakPoint.RBP_ON) {
  4045:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4046:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4047:             }
  4048:           }
  4049:           MC68901.mfpRintFall ();  //IRQ開始
  4050:         } else {  //IRQ信号が0でなくなったとき
  4051:           MC68901.mfpRintRise ();  //IRQ終了
  4052:         }
  4053:       }
  4054:       if (MC68901.mfpGpipVdisp != 0) {
  4055:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  4056:       }
  4057:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  4058:       if (CRT_ENABLE_INTERMITTENT) {
  4059:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  4060:       }
  4061:       if (!XEiJ.PNL_USE_THREAD) {
  4062:         if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  4063:           crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  4064:         }
  4065:       }
  4066:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  4067:     }
  4068:   };
  4069:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  4070:   public static final TickerQueue.Ticker DupExceptSpDrawIdleFront = new TickerQueue.Ticker () {
  4071:     @Override protected void tick () {
  4072:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  4073:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  4074:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  4075:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  4076:         }
  4077:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4078:           crtDoRasterCopy ();  //ラスタコピー実行
  4079:         }
  4080:         if (RasterBreakPoint.RBP_ON) {
  4081:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4082:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4083:           }
  4084:         }
  4085:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4086:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4087:           if (irq == 0) {  //IRQ信号が0になったとき
  4088:             if (RasterBreakPoint.RBP_ON) {
  4089:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4090:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4091:               }
  4092:             }
  4093:             MC68901.mfpRintFall ();  //IRQ開始
  4094:           } else {  //IRQ信号が0でなくなったとき
  4095:             MC68901.mfpRintRise ();  //IRQ終了
  4096:           }
  4097:         }
  4098:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  4099:           TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  4100:         } else {  //垂直映像期間開始ラスタのとき
  4101:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  4102:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  4103:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  4104:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  4105:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  4106:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  4107:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  4108:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  4109:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  4110:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  4111:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  4112:             crtR11TxYZeroLast = crtR11TxYZero;
  4113:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  4114:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  4115:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  4116:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  4117:             crtAllStamp += 2;
  4118:           }
  4119:           crtDataY = 0;  //データY座標を0で初期化
  4120:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  4121:             crtClearFrames--;  //高速クリアカウンタを1減らす
  4122:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  4123:             crtClearStandby = false;
  4124:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  4125:           }
  4126:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  4127:           if (!XEiJ.PNL_USE_THREAD) {
  4128:             crtDirtyY0 = -1;  //ダーティフラグをクリア
  4129:           }
  4130:           if (XEiJ.PNL_USE_THREAD) {
  4131:             crtAllStamp += 2;
  4132:           }
  4133:           if (SpriteScreen.SPR_THREE_STEPS) {
  4134:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  4135:             if (SpriteScreen.sprActive) {
  4136:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  4137:                 SpriteScreen.sprLatched = false;
  4138:               }
  4139:               if (!XEiJ.PNL_USE_THREAD) {
  4140:                 crtAllStamp += 2;
  4141:               }
  4142:               //ラスタ(dst=-2,src=-2)
  4143:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  4144:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  4145:               //ラスタ(dst=-1,src=-1)
  4146:               //表(-1)を表(1)として再利用する
  4147:               SpriteScreen.sprStep1 (1);  //表(1)にスプライト(1)を並べる
  4148:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  4149:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  4150:             }
  4151:           }
  4152:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4153:             crtStereoscopicStart ();
  4154:           }
  4155:           TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4156:         }
  4157:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  4158:         //垂直映像期間開始ラスタではないとき
  4159:         TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  4160:       }
  4161:     }
  4162:   };
  4163:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  4164:   public static final TickerQueue.Ticker DupExceptSpDrawIdleSync = new TickerQueue.Ticker () {
  4165:     @Override protected void tick () {
  4166:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4167:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4168:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4169:         MercuryUnit.mu4HsyncStart (crtClock);
  4170:       }
  4171:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  4172:     }
  4173:   };
  4174:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  4175:   public static final TickerQueue.Ticker DupExceptSpDrawIdleBackDisp = new TickerQueue.Ticker () {
  4176:     @Override protected void tick () {
  4177:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4178:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  4179:     }
  4180:   };
  4181:   //    (4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  4182:   public static final TickerQueue.Ticker DupExceptSpDrawDispEvenFront = new TickerQueue.Ticker () {
  4183:     @Override protected void tick () {
  4184:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  4185:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  4186:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4187:           crtDoRasterCopy ();  //ラスタコピー実行
  4188:         }
  4189:         if (RasterBreakPoint.RBP_ON) {
  4190:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4191:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4192:           }
  4193:         }
  4194:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4195:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4196:           if (irq == 0) {  //IRQ信号が0になったとき
  4197:             if (RasterBreakPoint.RBP_ON) {
  4198:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4199:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4200:               }
  4201:             }
  4202:             MC68901.mfpRintFall ();  //IRQ開始
  4203:           } else {  //IRQ信号が0でなくなったとき
  4204:             MC68901.mfpRintRise ();  //IRQ終了
  4205:           }
  4206:         }
  4207:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  4208:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  4209:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  4210:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4211:               crtStereoscopicDrawRaster (crtScreenY);
  4212:             }
  4213:           }
  4214:           crtScreenY++;  //スクリーンY座標を1増やす
  4215:           crtDataY++;  //データY座標を1増やす
  4216:           TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4217:         } else {  //垂直空白期間開始ラスタのとき
  4218:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  4219:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  4220:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4221:               crtStereoscopicDrawRaster (crtScreenY);
  4222:             }
  4223:           }
  4224:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  4225:           if (!XEiJ.PNL_USE_THREAD) {
  4226:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  4227:               crtUpdateScreen ();  //スクリーンを更新する
  4228:               if (CRT_ENABLE_INTERMITTENT) {
  4229:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  4230:               }
  4231:             }
  4232:           }
  4233:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  4234:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  4235:           }
  4236:           if (XEiJ.PNL_USE_THREAD) {
  4237:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  4238:           }
  4239:           TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  4240:         }
  4241:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  4242:         //垂直空白期間開始ラスタではないとき
  4243:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  4244:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  4245:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4246:             crtStereoscopicDrawRaster (crtScreenY);
  4247:           }
  4248:         }
  4249:         crtScreenY++;  //スクリーンY座標を1増やす
  4250:         crtDataY++;  //データY座標を1増やす
  4251:         TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4252:       }
  4253:     }
  4254:   };
  4255:   //    (4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  4256:   public static final TickerQueue.Ticker DupExceptSpDrawDispOddFront = new TickerQueue.Ticker () {
  4257:     @Override protected void tick () {
  4258:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  4259:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  4260:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4261:           crtDoRasterCopy ();  //ラスタコピー実行
  4262:         }
  4263:         if (RasterBreakPoint.RBP_ON) {
  4264:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4265:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4266:           }
  4267:         }
  4268:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4269:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4270:           if (irq == 0) {  //IRQ信号が0になったとき
  4271:             if (RasterBreakPoint.RBP_ON) {
  4272:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4273:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4274:               }
  4275:             }
  4276:             MC68901.mfpRintFall ();  //IRQ開始
  4277:           } else {  //IRQ信号が0でなくなったとき
  4278:             MC68901.mfpRintRise ();  //IRQ終了
  4279:           }
  4280:         }
  4281:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  4282:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  4283:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  4284:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4285:               crtStereoscopicDrawRaster (crtScreenY);
  4286:             }
  4287:           }
  4288:           crtScreenY++;  //スクリーンY座標を1増やす
  4289:           TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  4290:         } else {  //垂直空白期間開始ラスタのとき
  4291:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  4292:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  4293:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4294:               crtStereoscopicDrawRaster (crtScreenY);
  4295:             }
  4296:           }
  4297:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  4298:           if (!XEiJ.PNL_USE_THREAD) {
  4299:             if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  4300:               crtUpdateScreen ();  //スクリーンを更新する
  4301:               if (CRT_ENABLE_INTERMITTENT) {
  4302:                 crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  4303:               }
  4304:             }
  4305:           }
  4306:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  4307:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  4308:           }
  4309:           if (XEiJ.PNL_USE_THREAD) {
  4310:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  4311:           }
  4312:           TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  4313:         }
  4314:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  4315:         //垂直空白期間開始ラスタではないとき
  4316:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  4317:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  4318:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4319:             crtStereoscopicDrawRaster (crtScreenY);
  4320:           }
  4321:         }
  4322:         crtScreenY++;  //スクリーンY座標を1増やす
  4323:         TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  4324:       }
  4325:     }
  4326:   };
  4327:   //    (5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4328:   public static final TickerQueue.Ticker DupExceptSpDrawDispEvenSync = new TickerQueue.Ticker () {
  4329:     @Override protected void tick () {
  4330:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4331:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4332:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4333:         MercuryUnit.mu4HsyncStart (crtClock);
  4334:       }
  4335:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  4336:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  4337:         crtRasterStamp[crtDataY] = 0;
  4338:       }
  4339:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispEvenBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ
  4340:     }
  4341:   };
  4342:   //    (5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  4343:   public static final TickerQueue.Ticker DupExceptSpDrawDispOddSync = new TickerQueue.Ticker () {
  4344:     @Override protected void tick () {
  4345:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4346:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4347:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4348:         MercuryUnit.mu4HsyncStart (crtClock);
  4349:       }
  4350:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispOddBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ
  4351:     }
  4352:   };
  4353:   //    (6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ
  4354:   public static final TickerQueue.Ticker DupExceptSpDrawDispEvenBack = new TickerQueue.Ticker () {
  4355:     @Override protected void tick () {
  4356:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4357:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispEvenDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間
  4358:     }
  4359:   };
  4360:   //    (6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ
  4361:   public static final TickerQueue.Ticker DupExceptSpDrawDispOddBack = new TickerQueue.Ticker () {
  4362:     @Override protected void tick () {
  4363:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4364:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispOddDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間
  4365:     }
  4366:   };
  4367:   //    (7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間
  4368:   public static final TickerQueue.Ticker DupExceptSpDrawDispEvenDisp = new TickerQueue.Ticker () {
  4369:     @Override protected void tick () {
  4370:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  4371:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  4372:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  4373:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4374:           crtStereoscopicDrawRaster (crtScreenY);
  4375:         }
  4376:         if (!XEiJ.PNL_USE_THREAD) {
  4377:           if (crtDirtyY0 < 0) {
  4378:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  4379:           }
  4380:           crtDirtyY1 = crtScreenY;  //偶数ラスタで終了する可能性があるので偶数ラスタでもcrtDirtyY1を更新する
  4381:         }
  4382:       }
  4383:       if (SpriteScreen.SPR_THREE_STEPS) {
  4384:         if (SpriteScreen.sprActive) {
  4385:           //ラスタ(dst=src)
  4386:           //表(dst)を表(dst+2)として再利用する
  4387:           SpriteScreen.sprStep1 (crtScreenY + 2);  //表(dst+2)にスプライト(src+2)を並べる
  4388:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  4389:           SpriteScreen.sprStep2 (crtScreenY + 1);  //表(dst+1)にバックグラウンド(src+1)を並べる
  4390:         }
  4391:       }
  4392:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispOddFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  4393:     }
  4394:   };
  4395:   //    (7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間
  4396:   public static final TickerQueue.Ticker DupExceptSpDrawDispOddDisp = new TickerQueue.Ticker () {
  4397:     @Override protected void tick () {
  4398:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  4399:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  4400:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  4401:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  4402:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4403:           crtStereoscopicDrawRaster (crtScreenY);
  4404:         }
  4405:         if (!XEiJ.PNL_USE_THREAD) {
  4406:           if (crtDirtyY0 < 0) {
  4407:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット。奇数ラスタの直前でパレットやスクロール位置が変化したとき偶数ラスタでcrtDirtyY0が更新されていない可能性があるので奇数ラスタでもcrtDirtyY0を更新する
  4408:           }
  4409:           crtDirtyY1 = crtScreenY;
  4410:         }
  4411:       }
  4412:       if (SpriteScreen.SPR_THREE_STEPS) {
  4413:         if (SpriteScreen.sprActive) {
  4414:           //ラスタ(dst=src)
  4415:           //表(dst)を表(dst+2)として再利用する
  4416:           SpriteScreen.sprStep1 (crtScreenY + 2);  //表(dst+2)にスプライト(src+2)を並べる
  4417:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  4418:           SpriteScreen.sprStep2 (crtScreenY + 1);  //表(dst+1)にバックグラウンド(src+1)を並べる
  4419:         }
  4420:       }
  4421:       TickerQueue.tkqAdd (crtTicker = DupExceptSpDrawDispEvenFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  4422:     }
  4423:   };
  4424:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  4425:   public static final TickerQueue.Ticker DupExceptSpOmitIdleFront = new TickerQueue.Ticker () {
  4426:     @Override protected void tick () {
  4427:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  4428:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  4429:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  4430:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  4431:         }
  4432:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4433:           crtDoRasterCopy ();  //ラスタコピー実行
  4434:         }
  4435:         if (RasterBreakPoint.RBP_ON) {
  4436:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4437:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4438:           }
  4439:         }
  4440:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4441:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4442:           if (irq == 0) {  //IRQ信号が0になったとき
  4443:             if (RasterBreakPoint.RBP_ON) {
  4444:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4445:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4446:               }
  4447:             }
  4448:             MC68901.mfpRintFall ();  //IRQ開始
  4449:           } else {  //IRQ信号が0でなくなったとき
  4450:             MC68901.mfpRintRise ();  //IRQ終了
  4451:           }
  4452:         }
  4453:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  4454:           TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  4455:         } else {  //垂直映像期間開始ラスタのとき
  4456:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  4457:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  4458:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  4459:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  4460:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  4461:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  4462:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  4463:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  4464:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  4465:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  4466:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  4467:             crtR11TxYZeroLast = crtR11TxYZero;
  4468:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  4469:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  4470:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  4471:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  4472:             crtAllStamp += 2;
  4473:           }
  4474:           crtDataY = 0;  //データY座標を0で初期化
  4475:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  4476:             crtClearFrames--;  //高速クリアカウンタを1減らす
  4477:           } else if (crtClearStandby) {  //高速クリアの要求があるとき
  4478:             crtClearStandby = false;
  4479:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  4480:           }
  4481:           if (XEiJ.PNL_USE_THREAD) {
  4482:             crtAllStamp += 2;
  4483:           }
  4484:           if (SpriteScreen.SPR_THREE_STEPS) {
  4485:             SpriteScreen.sprActive = SpriteScreen.sprLatched;
  4486:             if (SpriteScreen.sprActive) {
  4487:               if ((SpriteScreen.sprReg4BgCtrlCurr & 512) == 0) {
  4488:                 SpriteScreen.sprLatched = false;
  4489:               }
  4490:               if (!XEiJ.PNL_USE_THREAD) {
  4491:                 crtAllStamp += 2;
  4492:               }
  4493:               //ラスタ(dst=-2,src=-2)
  4494:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  4495:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  4496:               //ラスタ(dst=-1,src=-1)
  4497:               //表(-1)を表(1)として再利用する
  4498:               SpriteScreen.sprStep1 (1);  //表(1)にスプライト(1)を並べる
  4499:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  4500:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  4501:             }
  4502:           }
  4503:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  4504:             crtStereoscopicStart ();
  4505:           }
  4506:           TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4507:         }
  4508:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  4509:         //垂直映像期間開始ラスタではないとき
  4510:         TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  4511:       }
  4512:     }
  4513:   };
  4514:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  4515:   public static final TickerQueue.Ticker DupExceptSpOmitIdleSync = new TickerQueue.Ticker () {
  4516:     @Override protected void tick () {
  4517:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4518:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4519:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4520:         MercuryUnit.mu4HsyncStart (crtClock);
  4521:       }
  4522:       TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  4523:     }
  4524:   };
  4525:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  4526:   public static final TickerQueue.Ticker DupExceptSpOmitIdleBackDisp = new TickerQueue.Ticker () {
  4527:     @Override protected void tick () {
  4528:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4529:       TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  4530:     }
  4531:   };
  4532:   //    (11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  4533:   public static final TickerQueue.Ticker DupExceptSpOmitDispEvenFront = new TickerQueue.Ticker () {
  4534:     @Override protected void tick () {
  4535:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  4536:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  4537:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4538:           crtDoRasterCopy ();  //ラスタコピー実行
  4539:         }
  4540:         if (RasterBreakPoint.RBP_ON) {
  4541:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4542:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4543:           }
  4544:         }
  4545:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4546:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4547:           if (irq == 0) {  //IRQ信号が0になったとき
  4548:             if (RasterBreakPoint.RBP_ON) {
  4549:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4550:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4551:               }
  4552:             }
  4553:             MC68901.mfpRintFall ();  //IRQ開始
  4554:           } else {  //IRQ信号が0でなくなったとき
  4555:             MC68901.mfpRintRise ();  //IRQ終了
  4556:           }
  4557:         }
  4558:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  4559:           TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4560:         } else {  //垂直空白期間開始ラスタのとき
  4561:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  4562:           if (CRT_ENABLE_INTERMITTENT) {
  4563:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  4564:           }
  4565:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  4566:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  4567:           }
  4568:           if (XEiJ.PNL_USE_THREAD) {
  4569:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  4570:           }
  4571:           TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  4572:         }
  4573:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  4574:         //垂直空白期間開始ラスタではないとき
  4575:         TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4576:       }
  4577:     }
  4578:   };
  4579:   //    (11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  4580:   public static final TickerQueue.Ticker DupExceptSpOmitDispOddFront = new TickerQueue.Ticker () {
  4581:     @Override protected void tick () {
  4582:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  4583:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  4584:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  4585:           crtDoRasterCopy ();  //ラスタコピー実行
  4586:         }
  4587:         if (RasterBreakPoint.RBP_ON) {
  4588:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  4589:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4590:           }
  4591:         }
  4592:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  4593:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  4594:           if (irq == 0) {  //IRQ信号が0になったとき
  4595:             if (RasterBreakPoint.RBP_ON) {
  4596:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  4597:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  4598:               }
  4599:             }
  4600:             MC68901.mfpRintFall ();  //IRQ開始
  4601:           } else {  //IRQ信号が0でなくなったとき
  4602:             MC68901.mfpRintRise ();  //IRQ終了
  4603:           }
  4604:         }
  4605:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  4606:           TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  4607:         } else {  //垂直空白期間開始ラスタのとき
  4608:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  4609:           if (CRT_ENABLE_INTERMITTENT) {
  4610:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  4611:           }
  4612:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  4613:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  4614:           }
  4615:           if (XEiJ.PNL_USE_THREAD) {
  4616:             XEiJ.pnlBM = XEiJ.pnlBMLeftArray[++XEiJ.pnlBMWrite & 3];  //書き込むビットマップを切り替える
  4617:           }
  4618:           TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  4619:         }
  4620:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  4621:         //垂直空白期間開始ラスタではないとき
  4622:         TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  4623:       }
  4624:     }
  4625:   };
  4626:   //    (12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  4627:   public static final TickerQueue.Ticker DupExceptSpOmitDispEvenSync = new TickerQueue.Ticker () {
  4628:     @Override protected void tick () {
  4629:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4630:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4631:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4632:         MercuryUnit.mu4HsyncStart (crtClock);
  4633:       }
  4634:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  4635:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  4636:         crtRasterStamp[crtDataY] = 0;
  4637:       }
  4638:       TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispEvenBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間
  4639:     }
  4640:   };
  4641:   //    (12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  4642:   public static final TickerQueue.Ticker DupExceptSpOmitDispOddSync = new TickerQueue.Ticker () {
  4643:     @Override protected void tick () {
  4644:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  4645:       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
  4646:           crtR05VSyncEndCurr <= crtRasterNumber) {
  4647:         MercuryUnit.mu4HsyncStart (crtClock);
  4648:       }
  4649:       TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispOddBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間
  4650:     }
  4651:   };
  4652:   //    (13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間
  4653:   public static final TickerQueue.Ticker DupExceptSpOmitDispEvenBackDisp = new TickerQueue.Ticker () {
  4654:     @Override protected void tick () {
  4655:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4656:       TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispOddFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  4657:     }
  4658:   };
  4659:   //    (13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間
  4660:   public static final TickerQueue.Ticker DupExceptSpOmitDispOddBackDisp = new TickerQueue.Ticker () {
  4661:     @Override protected void tick () {
  4662:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  4663:       TickerQueue.tkqAdd (crtTicker = DupExceptSpOmitDispEvenFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  4664:     }
  4665:   };
  4666: 
  4667: 
  4668: 
  4669: }  //class CRTC
  4670: 
  4671: 
  4672: