CRTC.java
     1: //========================================================================================
     2: //  CRTC.java
     3: //    en:CRT controller
     4: //    ja:CRTコントローラ
     5: //  Copyright (C) 2003-2024 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: //                 ━━━━━━━━━━━┓    ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓    ┏━━━━━━━━━━━
    20: //    垂直同期信号      垂直パルス間     ┃    ┃                       垂直パルス間                       ┃    ┃     垂直パルス間
    21: //                                       ┗━━┛                                                          ┗━━┛
    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:   public static final boolean CRT_INSTANT_DUPLICATION = false;  //true=ラスタ2度読みのとき奇数ラスタは直前の偶数ラスタのコピー
   422: 
   423:   //拡張グラフィック画面
   424:   //  メモリモード5  1024ドット512色(拡張)
   425:   //  メモリモード7  1024ドット65536色(拡張)
   426:   public static final boolean CRT_EXTENDED_GRAPHIC = true;  //true=拡張グラフィック画面をONにできる
   427:   public static boolean crtExtendedGraphicRequest;  //true=次回起動時に拡張グラフィック画面をONにする
   428:   public static boolean crtExtendedGraphicOn;  //true=拡張グラフィック画面がON
   429: 
   430:   //画面モードに変更があったとき描画をリスタートさせるまでの遅延(XEiJ.TMR_FREQ単位)
   431:   public static final long CRT_RESTART_DELAY = XEiJ.TMR_FREQ / 5;  //0.2秒
   432: 
   433:   //レジスタ
   434:   //  R00-R19/R22-R23はwrite onlyでreadすると常に0が返るが読めないと不便なのでreadできるようにする
   435:   //  動作ポートはreadできるが1を書き込んだビットは0を書き込むまで1のまま
   436:   //  未定義ビットは常に0
   437:   public static final int CRT_R00_H_FRONT_END = 0x00e80000;  //R00  bit7-0   水平フロントポーチ終了カラム
   438:   public static final int CRT_R01_H_SYNC_END  = 0x00e80002;  //R01  bit7-0   水平同期パルス終了カラム
   439:   public static final int CRT_R02_H_BACK_END  = 0x00e80004;  //R02  bit7-0   水平バックポーチ終了カラム
   440:   public static final int CRT_R03_H_DISP_END  = 0x00e80006;  //R03  bit7-0   水平映像期間終了カラム
   441:   public static final int CRT_R04_V_FRONT_END = 0x00e80008;  //R04  bit9-0   垂直フロントポーチ終了ラスタ
   442:   public static final int CRT_R05_V_SYNC_END  = 0x00e8000a;  //R05  bit9-0   垂直同期パルス終了ラスタ
   443:   public static final int CRT_R06_V_BACK_END  = 0x00e8000c;  //R06  bit9-0   垂直バックポーチ終了ラスタ
   444:   public static final int CRT_R07_V_DISP_END  = 0x00e8000e;  //R07  bit9-0   垂直映像期間終了ラスタ
   445:   public static final int CRT_R08_ADJUST      = 0x00e80010;  //R08  bit7-0   外部同期アジャスト。TVとX68000の水平同期パルスの立下りの時間差(39MHz)
   446:   public static final int CRT_R09_IRQ_RASTER  = 0x00e80012;  //R09  bit9-0   IRQラスタ。0=垂直同期パルス開始ラスタ
   447:   public static final int CRT_R10_TX_X        = 0x00e80014;  //R10  bit9-0   テキストX方向スクロール
   448:   public static final int CRT_R11_TX_Y        = 0x00e80016;  //R11  bit9-0   テキストY方向スクロール
   449:   public static final int CRT_R12_GR_X_0      = 0x00e80018;  //R12  bit9-0   グラフィックX方向スクロール4bitページ0
   450:   public static final int CRT_R13_GR_Y_0      = 0x00e8001a;  //R13  bit9-0   グラフィックY方向スクロール4bitページ0
   451:   public static final int CRT_R14_GR_X_1      = 0x00e8001c;  //R14  bit8-0   グラフィックX方向スクロール4bitページ1
   452:   public static final int CRT_R15_GR_Y_1      = 0x00e8001e;  //R15  bit8-0   グラフィックY方向スクロール4bitページ1
   453:   public static final int CRT_R16_GR_X_2      = 0x00e80020;  //R16  bit8-0   グラフィックX方向スクロール4bitページ2
   454:   public static final int CRT_R17_GR_Y_2      = 0x00e80022;  //R17  bit8-0   グラフィックY方向スクロール4bitページ2
   455:   public static final int CRT_R18_GR_X_3      = 0x00e80024;  //R18  bit8-0   グラフィックX方向スクロール4bitページ3
   456:   public static final int CRT_R19_GR_Y_3      = 0x00e80026;  //R19  bit8-0   グラフィックY方向スクロール4bitページ3
   457:   public static final int CRT_R20_MODE        = 0x00e80028;  //R20  bit12    テキストストレージ
   458:   //                                                                bit11    グラフィックストレージ
   459:   //                                                                bit10-8  メモリモード  0=512ドット16色
   460:   //                                                                                       1=512ドット256色
   461:   //                                                                                       2=メモリモード2
   462:   //                                                                                       3=512ドット65536色
   463:   //                                                                                       4=1024ドット16色
   464:   //                                                                                       5=1024ドット16色/1024ドット256色(拡張)
   465:   //                                                                                       6=1024ドット16色
   466:   //                                                                                       7=1024ドット16色/1024ドット65536色(拡張)
   467:   //                                                                bit4     解像度  0=低解像度,1=高解像度
   468:   //                                                                bit3-2   垂直解像度  0=256(ラスタ2度読み),1=512,2=1024,3=1024
   469:   //                                                                bit1-0   水平解像度  0=256,1=512,2=768,3=640
   470:   public static final int CRT_R21_SELECT      = 0x00e8002a;  //R21  bit9     1=ビットマスクON
   471:   //                                                                bit8     1=同時アクセスON
   472:   //                                                                bit7     1=プレーン3を同時アクセスする
   473:   //                                                                bit6     1=プレーン2を同時アクセスする
   474:   //                                                                bit5     1=プレーン1を同時アクセスする
   475:   //                                                                bit4     1=プレーン0を同時アクセスする
   476:   //                                                                bit3     1=プレーン3をラスタコピー/高速クリアする
   477:   //                                                                bit2     1=プレーン2をラスタコピー/高速クリアする
   478:   //                                                                bit1     1=プレーン1をラスタコピー/高速クリアする
   479:   //                                                                bit0     1=プレーン0をラスタコピー/高速クリアする
   480:   public static final int CRT_R22_BLOCK       = 0x00e8002c;  //R22  bit15-8  0~255 ソースラスタブロック番号
   481:   //                                                                bit7-0   0~255 デスティネーションラスタブロック番号
   482:   public static final int CRT_R23_MASK        = 0x00e8002e;  //R23  bit15-0  ビットマスク。1のビットに書き込まない
   483:   public static final int CRT_R24             = 0x00e80030;  //R24
   484:   public static final int CRT_ACTION          = 0x00e80480;  //動作ポート  bit0  1=画像入力開始
   485:   //                                                                       bit1  1=次の垂直表示開始で高速クリア開始
   486:   //                                                                       bit3  1=ラスタコピー実行
   487: 
   488:   //ドットクロックオシレータ
   489:   //  int k = crtHRLCurr << 3 | crtHighResoCurr << 2 | crtHResoCurr;
   490:   //  crtColumnTime = (int) ((double) (XEiJ.TMR_FREQ * 8 * CRT_DIVS[k]) / (double) crtFreqs[CRT_OSCS[k]] + 0.5);
   491:   //  10**12/(4*10**6)*8*1024=2048000000。4MHzを下回ると8分周で1024ドットの水平映像期間がintに収まらなくなる
   492:   public static final int[] CRT_OSCS = { 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0, 1, 1, 1, 2 };  //HRLR20410→オシレータの番号
   493:   public static final int[] CRT_DIVS = { 8, 4, 8, 8, 6, 3, 2, 2, 8, 4, 8, 8, 8, 4, 2, 2 };  //HRLR20410→分周比
   494:   public static final int CRT_MIN_FREQ =  10000000;  //オシレータの周波数の下限(Hz)
   495:   public static final int CRT_MAX_FREQ = 400000000;  //オシレータの周波数の上限(Hz)
   496:   public static final int[] CRT_DEFAULT_FREQS = { 38863632, 69551900, 50349800 };  //デフォルトのオシレータの周波数(Hz)
   497:   public static final int[] crtFreqsRequest = new int[3];  //リセット後のオシレータの周波数(Hz)
   498:   public static final int[] crtFreqs = new int[3];  //現在のオシレータの周波数(Hz)
   499: 
   500:   //レジスタ
   501:   //  ゼロ拡張
   502:   public static int crtR00HFrontEndPort;                 //R00 7-0 水平フロントポーチ終了カラム
   503:   public static int crtR00HFrontEndMask;
   504:   public static int crtR00HFrontEndTest;
   505:   public static int crtR00HFrontEndCurr;
   506:   public static int crtR01HSyncEndPort;                  //R01 7-0 水平同期パルス終了カラム
   507:   public static int crtR01HSyncEndMask;
   508:   public static int crtR01HSyncEndTest;
   509:   public static int crtR01HSyncEndCurr;
   510:   public static int crtR02HBackEndPort;                  //R02 7-0 水平バックポーチ終了カラム
   511:   public static int crtR02HBackEndMask;
   512:   public static int crtR02HBackEndTest;
   513:   public static int crtR02HBackEndCurr;
   514:   public static int crtR03HDispEndPort;                  //R03 7-0 水平映像期間終了カラム
   515:   public static int crtR03HDispEndMask;
   516:   public static int crtR03HDispEndTest;
   517:   public static int crtR03HDispEndCurr;
   518:   public static int crtR04VFrontEndPort;                 //R04 9-0 垂直フロントポーチ終了ラスタ
   519:   public static int crtR04VFrontEndMask;
   520:   public static int crtR04VFrontEndTest;
   521:   public static int crtR04VFrontEndCurr;
   522:   public static int crtR05VSyncEndPort;                  //R05 9-0 垂直同期パルス終了ラスタ
   523:   public static int crtR05VSyncEndMask;
   524:   public static int crtR05VSyncEndTest;
   525:   public static int crtR05VSyncEndCurr;
   526:   public static int crtR06VBackEndPort;                  //R06 9-0 垂直バックポーチ終了ラスタ
   527:   public static int crtR06VBackEndMask;
   528:   public static int crtR06VBackEndTest;
   529:   public static int crtR06VBackEndCurr;
   530:   public static int crtVDispStart;                       //        垂直映像期間開始ラスタ。crtR06VBackEndCurr+1
   531:   public static int crtR07VDispEndPort;                  //R07 9-0 垂直映像期間終了ラスタ
   532:   public static int crtR07VDispEndMask;
   533:   public static int crtR07VDispEndTest;
   534:   public static int crtR07VDispEndCurr;
   535:   public static int crtVIdleStart;                       //        垂直空白期間開始ラスタ。crtR07VDispEndCurr+1
   536:   public static int crtR08Adjust;                        //R08 7-0 外部同期水平アジャスト
   537:   public static int crtR09IRQRasterPort;                 //R09 9-0 IRQラスタ。0=垂直同期パルス開始ラスタ
   538:   public static int crtR09IRQRasterMask;
   539:   public static int crtR09IRQRasterTest;
   540:   public static int crtR09IRQRasterCurr;
   541:   public static int crtR10TxXPort;                       //R10 9-0 テキストX方向スクロール
   542:   public static int crtR10TxXMask;
   543:   public static int crtR10TxXTest;
   544:   public static int crtR10TxXCurr;
   545:   public static int crtR11TxYPort;                       //R11 9-0 テキストY方向スクロール
   546:   public static int crtR11TxYMask;
   547:   public static int crtR11TxYTest;
   548:   public static int crtR11TxYCurr;
   549:   public static int crtR11TxYZero;                       //垂直映像期間開始時のテキストY方向スクロール
   550:   public static int crtR11TxYZeroLast;
   551:   public static final int[] crtR12GrXPort = new int[4];  //[0] R12 9-0 グラフィックX方向スクロール0
   552:   //                                                       [1] R14 8-0 グラフィックX方向スクロール1
   553:   //                                                       [2] R16 8-0 グラフィックX方向スクロール2
   554:   //                                                       [3] R18 8-0 グラフィックX方向スクロール3
   555:   public static final int[] crtR12GrXMask = new int[4];
   556:   public static final int[] crtR12GrXTest = new int[4];
   557:   public static final int[] crtR12GrXCurr = new int[4];
   558:   public static final int[] crtR13GrYPort = new int[4];  //[0] R13 9-0 グラフィックY方向スクロール0
   559:   //                                                       [1] R15 8-0 グラフィックY方向スクロール1
   560:   //                                                       [2] R17 8-0 グラフィックY方向スクロール2
   561:   //                                                       [3] R19 8-0 グラフィックY方向スクロール3
   562:   public static final int[] crtR13GrYMask = new int[4];
   563:   public static final int[] crtR13GrYTest = new int[4];
   564:   public static final int[] crtR13GrYCurr = new int[4];
   565:   public static final int[] crtR13GrYZero = new int[4];  //垂直映像期間開始時のグラフィックY方向スクロール
   566:   public static final int[] crtR13GrYZeroLast = new int[4];
   567:   public static int crtTextStorage;                      //R20 12 テキストストレージ 0=OFF,1=ON
   568:   public static int crtGraphicStorage;                   //R20 11 グラフィックストレージ 0=OFF,1=ON
   569:   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色(拡張)
   570:   public static int crtMemoryModeMask;
   571:   public static int crtMemoryModeTest;
   572:   public static int crtMemoryModeCurr;
   573:   public static int crtHighResoPort;                     //R20 4    0=低解像度,1=高解像度
   574:   public static int crtHighResoMask;
   575:   public static int crtHighResoTest;
   576:   public static int crtHighResoCurr;
   577:   public static int crtVResoPort;                        //R20 3-2  垂直解像度
   578:   public static int crtVResoMask;
   579:   public static int crtVResoTest;
   580:   public static int crtVResoCurr;
   581:   public static int crtHResoPort;                        //R20 1-0  水平解像度
   582:   public static int crtHResoMask;
   583:   public static int crtHResoTest;
   584:   public static int crtHResoCurr;
   585:   public static boolean crtCCPlane0;                     //R21 0 true=プレーン0をラスタコピー/高速クリアする
   586:   public static boolean crtCCPlane1;                     //    1 true=プレーン1をラスタコピー/高速クリアする
   587:   public static boolean crtCCPlane2;                     //    2 true=プレーン2をラスタコピー/高速クリアする
   588:   public static boolean crtCCPlane3;                     //    3 true=プレーン3をラスタコピー/高速クリアする
   589:   public static boolean crtSimPlane0;                    //    4 true=プレーン0を同時アクセスする
   590:   public static boolean crtSimPlane1;                    //    5 true=プレーン1を同時アクセスする
   591:   public static boolean crtSimPlane2;                    //    6 true=プレーン2を同時アクセスする
   592:   public static boolean crtSimPlane3;                    //    7 true=プレーン3を同時アクセスする
   593:   public static boolean crtSimAccess;                    //    8 true=同時アクセス有効
   594:   public static boolean crtBitMask;                      //    9 true=ビットマスク有効
   595:   public static int crtR22SrcBlock;                      //R22 15-8 ソースラスタブロック番号
   596:   public static int crtR22DstBlock;                      //    7-0  デスティネーションラスタブロック番号
   597:   public static int crtR23Mask;                          //R23 15-0 ビットマスク。1のビットに書き込まない
   598:   public static boolean crtRasterCopyOn;                 //動作ポート 2        true=次の水平フロントポーチでラスタコピー実行
   599:   public static boolean crtClearStandby;                 //動作ポート 1(write) true=次の垂直表示開始で高速クリア開始
   600:   public static int crtClearFrames;                      //  実行中の高速クリアの残りフレーム数。インターレースのとき2、それ以外は1から始めてデクリメントする
   601: 
   602:   public static int crtHRLPort;  //0または1。1のとき69.552MHzの3分周と6分周が4分周と8分周に変わる
   603:   public static int crtHRLMask;
   604:   public static int crtHRLTest;
   605:   public static int crtHRLCurr;
   606: 
   607:   public static boolean crtDuplication;  //true=ラスタ2度読み。crtHighResoCurr==1&&crtVResoCurr==0
   608:   public static boolean crtInterlace;  //true=インターレース。crtHighResoCurr+1<=crtVResoCurr
   609:   public static boolean crtSlit;  //true=スリット。crtHighResoCurr==0&&crtVResoCurr==0
   610:   public static int crtColumnTime;  //水平カラム時間(XEiJ.TMR_FREQ単位)
   611:   public static int crtHSyncLength;  //水平同期パルスの長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR01HSyncEndCurr+1)
   612:   public static int crtHBackLength;  //水平バックポーチの長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR02HBackEndCurr-crtR01HSyncEndCurr)
   613:   public static int crtHDispLength;  //水平映像期間の長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR03HDispEndCurr-crtR02HBackEndCurr)
   614:   public static int crtHFrontLength;  //水平フロントポーチの長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR00HFrontEndCurr-crtR03HDispEndCurr)
   615:   public static int crtHBackDispLength;  //水平バックポーチと水平映像期間の長さ(XEiJ.TMR_FREQ単位)。crtColumnTime*(crtR03HDispEndCurr-crtR01HSyncEndCurr)
   616: 
   617:   //  描画のルール
   618:   //    更新されていないラスタのビットマップへの変換を省略する
   619:   //    更新されたラスタを含む矩形をクリッピングエリアとしてペイントする
   620:   //    更新されたラスタは水平映像期間に入った瞬間に1ラスタ分変換する
   621:   //      水平映像期間の途中でパレットレジスタやスクロールレジスタを操作してもそのラスタには反映されない
   622:   //        768x512で256色の画面は作れない
   623:   //      スプライト画面も水平バックポーチが終わる前に書き換えればそのラスタに反映される
   624:   //  更新されたラスタだけを描画する手順
   625:   //    初期化
   626:   //      crtAllStamp=1。2ずつ増やすので0になることはない
   627:   //      for all y
   628:   //        crtRasterStamp[y]=0
   629:   //    画面モードが変更されたりスクロールレジスタやパレットレジスタが操作されて画面の全体が変化した可能性があるとき
   630:   //      crtAllStamp+=2。常に奇数
   631:   //    VRAMやスプライトレジスタが操作されて画面の一部が変化した可能性があるとき
   632:   //      crtRasterStamp[y]=0
   633:   //    垂直映像期間開始ラスタに入るとき
   634:   //      crtDirtyY0=-1。更新なし
   635:   //      crtScreenY=0
   636:   //    描画フレームの垂直映像期間の水平映像期間に入ったとき
   637:   //      crtRasterStamp[crtScreenY]!=crtAllStamp。再描画が必要
   638:   //        crtRasterStamp[crtScreenY]=crtAllStamp
   639:   //        crtDirtyY0<0
   640:   //          crtDirtyY0=crtScreenY。更新あり
   641:   //        crtDirtyY1=crtScreenY
   642:   //        drawRaster(crtScreenY)
   643:   //      crtScreenY++
   644:   //    垂直映像期間終了ラスタから出たとき
   645:   //      crtDirtyY0>=0。更新されたとき
   646:   //        crtDirtyY0からcrtDirtyY1までをrepaintする
   647:   public static int crtDirtyY0;  //垂直映像期間にビットマップが更新された範囲の上端のスクリーンY座標。-1=更新されていない
   648:   public static int crtDirtyY1;  //垂直映像期間にビットマップが更新された範囲の下端のスクリーンY座標。更新された範囲の高さはcrtDirtyY1-crtDirtyY0+1
   649: 
   650:   public static final int[] crtRasterStamp = new int[1024 + 15];  //ラスタスタンプ。0=VRAMが操作されてこのラスタを再描画する必要がある
   651:   public static int crtAllStamp;  //全再描画スタンプ
   652: 
   653:   //  768x512ドット256色
   654:   //    作り方
   655:   //      画面モードを768x512ドットにしてグラフィック画面だけ512x512ドット256色にする
   656:   //      512x512ドット256色ページ0に768x512ドット256色の画像の左1/3と中央1/3を描く
   657:   //      512x512ドット256色ページ1に768x512ドット256色の画像の右1/3と中央1/3を描く
   658:   //      0<=y<=511のすべてのラスタについて
   659:   //        256<=x<=511でページ0をOFF、ページ1をON、768<=xでページ0をON、ページ1をOFFにする
   660:   //        256<=x<=511の範囲はページ0とページ1の両方に画像が描かれているのでどこで切り替えてもよいが、
   661:   //        10MHzのとき256<=x<=511の期間は73サイクル、NOP命令18個分しかないので、かなりシビアな処理になる
   662:   //        (768x512ドットの画面は69.552MHzを2分周して作られるので28.755ns/dot)
   663:   //    方針
   664:   //      描画する必要のないラスタをラスタスタンプと全再描画スタンプが一致するかどうかで見分けているが、
   665:   //      この全再描画スタンプを流用する
   666:   //      水平映像期間開始時から終了時までの間に全再描画スタンプが変化したとき、
   667:   //      水平映像期間終了時にラスタの後半を再描画する
   668:   //      分割する必要がないときのオーバーヘッドをなるべく減らす
   669:   //        分割する必要がないときの1ラスタあたりのオーバーヘッド
   670:   //          static変数のコピーが1回
   671:   //          static変数同士の比較が2回
   672:   //          if分岐が2回
   673:   //    手順
   674:   //      水平映像期間開始時
   675:   //        いろいろ
   676:   //        crtBeginningAllStamp=crtAllStamp;
   677:   //        ラスタ描画(src,dst);
   678:   //      水平映像期間終了時
   679:   //        if crtBeginningAllStamp!=crtAllStamp;
   680:   //          ラスタ描画(src,dst);
   681:   //        ラスタ番号++
   682:   //        いろいろ
   683:   //      ラスタ描画(src,dst);
   684:   //        int da=dst<<XEiJ.PNL_BM_OFFSET_BITS;
   685:   //        int db=da+XEiJ.pnlScreenWidth;
   686:   //        if crtBeginningAllStamp!=crtAllStamp
   687:   //          int half=XEiJ.pnlScreenWidth>>4<<3;
   688:   //          sx+=half;
   689:   //          gx1st+=half<<1;
   690:   //          gx2nd+=half<<1;
   691:   //          gx3rd+=half<<1;
   692:   //          gx4th+=half<<1;
   693:   //          tc=tc+(half>>3)&127;
   694:   //          gx+=half;
   695:   //          da+=half;
   696:   //    参考
   697:   //      https://twitter.com/kugimoto0715/status/800231367699116032
   698:   //      ArimacさんのBMPL.X/LPICL.Xの添付ドキュメントX256.DOC
   699:   public static int crtBeginningAllStamp;  //水平映像期間開始時の全再描画スタンプ
   700: 
   701:   public static int crtRasterNumber;  //ラスタ番号。0=垂直同期パルス開始ラスタ
   702:   public static int crtDataY;  //データY座標
   703:   public static int crtScreenY;  //スクリーンY座標
   704:   public static int crtFrameParity;  //フレームパリティ
   705: 
   706:   //  水平フロントポーチでアクションを起こすべきラスタかどうかの判別を高速化する
   707:   //  垂直空白期間
   708:   //  crtRasterHashIdle = ((crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO) |
   709:   //                       (RasterBreakPoint.RBP_ON && RasterBreakPoint.rbpActiveBreakRaster >= 0 ?
   710:   //                        CRT_RASTER_HASH_MSB >>> RasterBreakPoint.rbpActiveBreakRaster : CRT_RASTER_HASH_ZERO) |
   711:   //                       (crtR09IRQRasterCurr <= crtR04VFrontEndCurr ?
   712:   //                        CRT_RASTER_HASH_MSB >>> crtR09IRQRasterCurr |
   713:   //                        CRT_RASTER_HASH_MSB >>> (crtR09IRQRasterCurr < crtR04VFrontEndCurr ? crtR09IRQRasterCurr + 1 : 0) :
   714:   //                        CRT_RASTER_HASH_ZERO);
   715:   //                       CRT_RASTER_HASH_MSB >>> crtVDispStart |
   716:   //                       CRT_RASTER_HASH_MSB >>> crtR04VFrontEndCurr + 1);
   717:   //  垂直映像期間
   718:   //  crtRasterHashDisp = ((crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO) |
   719:   //                       (RasterBreakPoint.RBP_ON && RasterBreakPoint.rbpActiveBreakRaster >= 0 ?
   720:   //                        CRT_RASTER_HASH_MSB >>> RasterBreakPoint.rbpActiveBreakRaster : CRT_RASTER_HASH_ZERO) |
   721:   //                       (crtR09IRQRasterCurr <= crtR04VFrontEndCurr ?
   722:   //                        CRT_RASTER_HASH_MSB >>> crtR09IRQRasterCurr |
   723:   //                        CRT_RASTER_HASH_MSB >>> (crtR09IRQRasterCurr < crtR04VFrontEndCurr ? crtR09IRQRasterCurr + 1 : 0) :
   724:   //                        CRT_RASTER_HASH_ZERO);
   725:   //                       CRT_RASTER_HASH_MSB >>> crtVIdleStart);
   726:   public static final boolean CRT_RASTER_HASH_ON = true;
   727:   public static final long CRT_RASTER_HASH_ZERO = 0x0000000000000000L;  //crtRasterHashと同じ型の0
   728:   public static final long CRT_RASTER_HASH_MSB  = 0x8000000000000000L;  //crtRasterHashと同じ型のMSBだけセットした値
   729:   public static long crtRasterHashIdle;  //垂直空白期間。intまたはlong
   730:   public static long crtRasterHashDisp;  //垂直映像期間。intまたはlong
   731:   //public static final int CRT_RASTER_HASH_ZERO = 0x00000000;  //crtRasterHashと同じ型の0
   732:   //public static final int CRT_RASTER_HASH_MSB  = 0x80000000;  //crtRasterHashと同じ型のMSBだけセットした値
   733:   //public static int crtRasterHashIdle;  //垂直空白期間。intまたはlong
   734:   //public static int crtRasterHashDisp;  //垂直映像期間。intまたはlong
   735: 
   736:   public static TickerQueue.Ticker crtTicker;
   737:   public static long crtClock;
   738: 
   739:   public static long crtContrastClock;  //次にコントラストを変更する時刻
   740:   public static long crtCaptureClock;  //次に画面をキャプチャする時刻
   741:   public static long crtFrameTaskClock;  //Math.min(crtContrastClock,crtCaptureClock)
   742: 
   743:   //間欠描画
   744:   //  描画するフレームの間隔を空けることで描画の負荷を減らす
   745:   //  間欠間隔 interval>=0
   746:   //    interval=0    デフォルト。すべてのフレームが描画フレーム
   747:   //    interval=1..  描画フレームの後の少なくともintervalフレームを省略フレームとすることで描画フレームの割合を1/(interval+1)以下に抑える
   748:   //                  インターレースの場合は間欠間隔を偶数に切り上げることで偶数フレームと奇数フレームが交互に更新されるようにする
   749:   //  間欠カウンタ counter>=0
   750:   //    counter=0     描画フレーム。画面が更新されたときは描画するフレーム
   751:   //    counter=1~n  省略フレーム。常に描画しないフレーム
   752:   //  描画フレームの垂直映像期間の水平映像期間に入ったとき
   753:   //    ラスタが更新されたとき
   754:   //      描画フレーム(counter==0)のとき
   755:   //        画面の合成と16bit→32bitの変換を行う
   756:   //        更新されたラスタの範囲を記録する
   757:   //      省略フレーム(counter!=0)のとき
   758:   //        何もしない
   759:   //  垂直映像期間終了ラスタから出たとき
   760:   //    描画フレーム(counter==0)のとき
   761:   //      更新されたラスタがあったとき
   762:   //        更新されたラスタの範囲を描画する
   763:   //        counter=interval
   764:   //      更新されたラスタがなかったとき
   765:   //        何もしない
   766:   //    省略フレーム(counter!=0)のとき
   767:   //      counter--
   768:   public static final boolean CRT_ENABLE_INTERMITTENT = true;  //true=間欠描画を有効にする
   769:   public static int crtIntermittentInterval;  //間欠間隔。描画フレームの間に挟む省略フレームの数の下限
   770:   public static int crtIntermittentCounter;  //間欠カウンタ。0=描画フレーム,1..interval=省略フレーム
   771: 
   772:   //走査線エフェクト
   773:   enum ScanlineEffect {
   774:     OFF {  //なし。そのままコピー
   775:       @Override public void drawRaster (int screenY) {
   776:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   777:         System.arraycopy (XEiJ.pnlBM, da - XEiJ.PNL_BM_WIDTH,  //from
   778:                           XEiJ.pnlBM, da,  //to
   779:                           XEiJ.pnlScreenWidth);  //length
   780:       }
   781:     },
   782:     WEAK {  //弱。7/8倍してコピー
   783:       @Override public void drawRaster (int screenY) {
   784:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   785:         int db = da + XEiJ.pnlScreenWidth;
   786:         while (da < db) {
   787:           int t;
   788:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH    )];
   789:           XEiJ.pnlBM[da    ] = t - ((t >> 3) & 0x001f1f1f);
   790:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 1)];
   791:           XEiJ.pnlBM[da + 1] = t - ((t >> 3) & 0x001f1f1f);
   792:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 2)];
   793:           XEiJ.pnlBM[da + 2] = t - ((t >> 3) & 0x001f1f1f);
   794:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 3)];
   795:           XEiJ.pnlBM[da + 3] = t - ((t >> 3) & 0x001f1f1f);
   796:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 4)];
   797:           XEiJ.pnlBM[da + 4] = t - ((t >> 3) & 0x001f1f1f);
   798:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 5)];
   799:           XEiJ.pnlBM[da + 5] = t - ((t >> 3) & 0x001f1f1f);
   800:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 6)];
   801:           XEiJ.pnlBM[da + 6] = t - ((t >> 3) & 0x001f1f1f);
   802:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 7)];
   803:           XEiJ.pnlBM[da + 7] = t - ((t >> 3) & 0x001f1f1f);
   804:           da += 8;
   805:         }
   806:       }
   807:     },
   808:     MEDIUM {  //中。3/4倍してコピー
   809:       @Override public void drawRaster (int screenY) {
   810:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   811:         int db = da + XEiJ.pnlScreenWidth;
   812:         while (da < db) {
   813:           int t;
   814:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH    )];
   815:           XEiJ.pnlBM[da    ] = t - ((t >> 2) & 0x003f3f3f);
   816:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 1)];
   817:           XEiJ.pnlBM[da + 1] = t - ((t >> 2) & 0x003f3f3f);
   818:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 2)];
   819:           XEiJ.pnlBM[da + 2] = t - ((t >> 2) & 0x003f3f3f);
   820:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 3)];
   821:           XEiJ.pnlBM[da + 3] = t - ((t >> 2) & 0x003f3f3f);
   822:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 4)];
   823:           XEiJ.pnlBM[da + 4] = t - ((t >> 2) & 0x003f3f3f);
   824:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 5)];
   825:           XEiJ.pnlBM[da + 5] = t - ((t >> 2) & 0x003f3f3f);
   826:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 6)];
   827:           XEiJ.pnlBM[da + 6] = t - ((t >> 2) & 0x003f3f3f);
   828:           t = XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 7)];
   829:           XEiJ.pnlBM[da + 7] = t - ((t >> 2) & 0x003f3f3f);
   830:           da += 8;
   831:         }
   832:       }
   833:     },
   834:     STRONG {  //強。1/2倍してコピー
   835:       @Override public void drawRaster (int screenY) {
   836:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   837:         int db = da + XEiJ.pnlScreenWidth;
   838:         while (da < db) {
   839:           XEiJ.pnlBM[da    ] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH    )] >> 1) & 0xff7f7f7f;
   840:           XEiJ.pnlBM[da + 1] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 1)] >> 1) & 0xff7f7f7f;
   841:           XEiJ.pnlBM[da + 2] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 2)] >> 1) & 0xff7f7f7f;
   842:           XEiJ.pnlBM[da + 3] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 3)] >> 1) & 0xff7f7f7f;
   843:           XEiJ.pnlBM[da + 4] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 4)] >> 1) & 0xff7f7f7f;
   844:           XEiJ.pnlBM[da + 5] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 5)] >> 1) & 0xff7f7f7f;
   845:           XEiJ.pnlBM[da + 6] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 6)] >> 1) & 0xff7f7f7f;
   846:           XEiJ.pnlBM[da + 7] = (XEiJ.pnlBM[da - (XEiJ.PNL_BM_WIDTH - 7)] >> 1) & 0xff7f7f7f;
   847:           da += 8;
   848:         }
   849:       }
   850:     },
   851:     BLACK {  //黒
   852:       @Override public void drawRaster (int screenY) {
   853:         int da = screenY << XEiJ.PNL_BM_OFFSET_BITS;
   854:         int db = da + XEiJ.pnlScreenWidth;
   855:         Arrays.fill (XEiJ.pnlBM,  //array
   856:                      da,  //from
   857:                      db,  //to
   858:                      0xff000000);  //value
   859:       }
   860:     };
   861:     public abstract void drawRaster (int screenY);
   862:   }  //enum ScanlineEffect
   863:   public static ScanlineEffect crtScanlineEffect;
   864: 
   865:   //1024ドットノンインターレース
   866:   //  R04/R05/R06/R07/R09の幅を10ビットから11ビットに拡張する
   867:   //  垂直映像期間のラスタ数R07-R06が1024を超えてはならない
   868:   public static boolean crtEleventhBitRequest;
   869:   public static boolean crtEleventhBit;
   870:   public static int crtVerticalMask;  //0x03ffまたは0x07ff
   871: 
   872:   //テキスト画面のスクロール
   873:   //  グラフィック画面は球面スクロールでデータのアドレスが1ラスタ毎にループするが、
   874:   //  テキスト画面は円筒スクロールでデータのアドレスが4ラスタ(=1ラスタブロック)毎にループするので、
   875:   //  X方向のスクロール位置に範囲外の値を指定すると画面が乱れる
   876:   //  改造メニューで球面スクロールを選択できる
   877:   public static boolean crtSphericalScrolling;  //false=円筒スクロール,true=球面スクロール
   878:   public static int crtMask3;
   879:   public static int crtMaskMinus4;
   880:   public static int crtMask511;
   881:   public static void crtSetSphericalScrolling (boolean spherical) {
   882:     crtSphericalScrolling = spherical;
   883:     crtMask3 = spherical ? 0 : 3;  //0:3
   884:     crtMaskMinus4 = ~crtMask3;  //-1:-4
   885:     crtMask511 = crtMask3 << 7 | 127;  //127:511
   886:     crtAllStamp += 2;
   887:   }
   888: 
   889:   //CRTC R00のビット0
   890:   public static boolean crtR00Bit0Zero;  //false=1に固定する,true=0を書き込める
   891: 
   892:   //crtInit ()
   893:   //  CRTコントローラを初期化する
   894:   public static void crtInit () {
   895:     //if (CRT_EXTENDED_GRAPHIC) {
   896:     //  crtExtendedGraphicRequest = false;
   897:     //}
   898:     //crtR12GrXPort = new int[4];
   899:     //crtR12GrXMask = new int[4];
   900:     //crtR12GrXTest = new int[4];
   901:     //crtR12GrXCurr = new int[4];
   902:     //crtR13GrYPort = new int[4];
   903:     //crtR13GrYMask = new int[4];
   904:     //crtR13GrYTest = new int[4];
   905:     //crtR13GrYCurr = new int[4];
   906:     //crtR13GrYZero = new int[4];
   907:     //crtR13GrYZeroLast = new int[4];
   908:     //crtRasterStamp = new int[1024 + 15];  //1024以降はスプライトコントローラが更新してしまうので追加したダミー
   909: 
   910:     //走査線エフェクト
   911:     switch (Settings.sgsGetString ("scanline").toLowerCase ()) {
   912:     case "off":
   913:       crtScanlineEffect = ScanlineEffect.OFF;
   914:       break;
   915:     case "weak":
   916:       crtScanlineEffect = ScanlineEffect.WEAK;
   917:       break;
   918:     case "medium":
   919:       crtScanlineEffect = ScanlineEffect.MEDIUM;
   920:       break;
   921:     case "strong":
   922:       crtScanlineEffect = ScanlineEffect.STRONG;
   923:       break;
   924:     case "black":
   925:       crtScanlineEffect = ScanlineEffect.BLACK;
   926:       break;
   927:     }
   928: 
   929:     //ドットクロックオシレータ
   930:     {
   931:       String[] a = (Settings.sgsGetString ("dotclock") + ",,,0").split (",");
   932:       for (int i = 0; i < 3; i++) {
   933:         int freq = -1;
   934:         try {
   935:           freq = Integer.parseInt (a[i], 10);
   936:         } catch (NumberFormatException nfe) {
   937:         }
   938:         crtFreqsRequest[i] = CRT_MIN_FREQ <= freq && freq <= CRT_MAX_FREQ ? freq : CRT_DEFAULT_FREQS[i];
   939:       }
   940:     }
   941: 
   942:     //1024ドットノンインターレース
   943:     crtEleventhBitRequest = Settings.sgsGetOnOff ("eleventhbit");
   944: 
   945:     //テキスト画面のスクロール
   946:     crtSphericalScrolling = Settings.sgsGetOnOff ("sphericalscrolling");
   947:     crtMask3 = 3;
   948:     crtMaskMinus4 = -4;
   949:     crtMask511 = 511;
   950: 
   951:     //CRTC R00のビット0
   952:     crtR00Bit0Zero = Settings.sgsGetOnOff ("r00bit0zero");
   953: 
   954:     if (true) {
   955:       crtCCPlane0 = false;
   956:       crtCCPlane1 = false;
   957:       crtCCPlane2 = false;
   958:       crtCCPlane3 = false;
   959:       crtSimPlane0 = false;
   960:       crtSimPlane1 = false;
   961:       crtSimPlane2 = false;
   962:       crtSimPlane3 = false;
   963:       crtSimAccess = false;
   964:       crtBitMask = false;
   965:     }
   966:     crtReset ();
   967:   }  //crtInit()
   968: 
   969:   //crtTini ()
   970:   //  後始末
   971:   public static void crtTini () {
   972: 
   973:     //走査線エフェクト
   974:     Settings.sgsPutString ("scanline",
   975:                            crtScanlineEffect == ScanlineEffect.OFF ? "off" :
   976:                            crtScanlineEffect == ScanlineEffect.WEAK ? "weak" :
   977:                            crtScanlineEffect == ScanlineEffect.MEDIUM ? "medium" :
   978:                            crtScanlineEffect == ScanlineEffect.STRONG ? "strong" :
   979:                            crtScanlineEffect == ScanlineEffect.BLACK ? "black" :
   980:                            "");
   981: 
   982:     //ドットクロックオシレータ
   983:     {
   984:       StringBuilder sb = new StringBuilder ();
   985:       for (int i = 0; i < 3; i++) {
   986:         if (0 < i) {
   987:           sb.append (',');
   988:         }
   989:         if (crtFreqsRequest[i] != CRT_DEFAULT_FREQS[i]) {
   990:           sb.append (crtFreqsRequest[i]);
   991:         }
   992:       }
   993:       Settings.sgsPutString ("dotclock", sb.toString ());
   994:     }
   995: 
   996:     //1024ドットノンインターレース
   997:     Settings.sgsPutOnOff ("eleventhbit", crtEleventhBitRequest);
   998: 
   999:     //テキスト画面のスクロール
  1000:     Settings.sgsPutOnOff ("sphericalscrolling", crtSphericalScrolling);
  1001: 
  1002:     //CRTC R00のビット0
  1003:     Settings.sgsPutOnOff ("r00bit0zero", crtR00Bit0Zero);
  1004: 
  1005:   }  //crtTini
  1006: 
  1007:   //crtReset ()
  1008:   //  リセット
  1009:   //  CRTCのレジスタを初期化する
  1010:   //  レジスタが設定されてCRT_RESTART_DELAYが経過するまでCRTCの動作を停止する
  1011:   //  以下で呼び出される
  1012:   //    初期化
  1013:   //    MPUのreset命令
  1014:   public static void crtReset () {
  1015:     if (CRT_EXTENDED_GRAPHIC) {
  1016:       crtExtendedGraphicOn = crtExtendedGraphicRequest;
  1017:       if (crtExtendedGraphicOn) {
  1018:         System.out.println (Multilingual.mlnJapanese ?
  1019:                             "拡張グラフィック画面が有効になりました" :
  1020:                             "Extended graphic screen has been activated");
  1021:       }
  1022:     }
  1023: 
  1024:     //ドットクロックオシレータ
  1025:     for (int i = 0; i < 3; i++) {
  1026:       crtFreqs[i] = crtFreqsRequest[i];
  1027:     }
  1028: 
  1029:     //1024ドットノンインターレース
  1030:     crtEleventhBit = crtEleventhBitRequest;
  1031:     crtVerticalMask = crtEleventhBit ? 0x07ff : 0x03ff;
  1032: 
  1033:     crtR00HFrontEndPort = 0;
  1034:     crtR00HFrontEndMask = 0;
  1035:     crtR00HFrontEndTest = 0;
  1036:     crtR00HFrontEndCurr = 0;
  1037:     crtR01HSyncEndPort = 0;
  1038:     crtR01HSyncEndMask = 0;
  1039:     crtR01HSyncEndTest = 0;
  1040:     crtR01HSyncEndCurr = 0;
  1041:     crtR02HBackEndPort = 0;
  1042:     crtR02HBackEndMask = 0;
  1043:     crtR02HBackEndTest = 0;
  1044:     crtR02HBackEndCurr = 0;
  1045:     crtR03HDispEndPort = 0;
  1046:     crtR03HDispEndMask = 0;
  1047:     crtR03HDispEndTest = 0;
  1048:     crtR03HDispEndCurr = 0;
  1049:     crtR04VFrontEndPort = 0;
  1050:     crtR04VFrontEndMask = 0;
  1051:     crtR04VFrontEndTest = 0;
  1052:     crtR04VFrontEndCurr = 0;
  1053:     crtR05VSyncEndPort = 0;
  1054:     crtR05VSyncEndMask = 0;
  1055:     crtR05VSyncEndTest = 0;
  1056:     crtR05VSyncEndCurr = 0;
  1057:     crtR06VBackEndPort = 0;
  1058:     crtR06VBackEndMask = 0;
  1059:     crtR06VBackEndTest = 0;
  1060:     crtR06VBackEndCurr = 0;
  1061:     crtVDispStart = 0;
  1062:     crtR07VDispEndPort = 0;
  1063:     crtR07VDispEndMask = 0;
  1064:     crtR07VDispEndTest = 0;
  1065:     crtR07VDispEndCurr = 0;
  1066:     crtVIdleStart = 0;
  1067:     crtR08Adjust = 0;
  1068:     crtR09IRQRasterPort = 1023;
  1069:     crtR09IRQRasterMask = 0;
  1070:     crtR09IRQRasterTest = 1023;
  1071:     crtR09IRQRasterCurr = 1023;
  1072:     crtR10TxXPort = 0;
  1073:     crtR10TxXMask = 0;
  1074:     crtR10TxXTest = 0;
  1075:     crtR10TxXCurr = 0;
  1076:     crtR11TxYPort = 0;
  1077:     crtR11TxYMask = 0;
  1078:     crtR11TxYTest = 0;
  1079:     crtR11TxYCurr = 0;
  1080:     crtR11TxYZero = 0;
  1081:     crtR11TxYZeroLast = -1;
  1082:     for (int i = 0; i < 4; i++) {
  1083:       crtR12GrXPort[i] = 0;
  1084:       crtR12GrXMask[i] = 0;
  1085:       crtR12GrXTest[i] = 0;
  1086:       crtR12GrXCurr[i] = 0;
  1087:       crtR13GrYPort[i] = 0;
  1088:       crtR13GrYMask[i] = 0;
  1089:       crtR13GrYTest[i] = 0;
  1090:       crtR13GrYCurr[i] = 0;
  1091:       crtR13GrYZero[i] = 0;
  1092:       crtR13GrYZeroLast[i] = -1;
  1093:     }
  1094:     crtTextStorage = 0;
  1095:     crtGraphicStorage = 0;
  1096:     crtMemoryModePort = 0;
  1097:     crtMemoryModeMask = 0;
  1098:     crtMemoryModeTest = 0;
  1099:     crtMemoryModeCurr = 0;
  1100:     crtHighResoPort = 0;
  1101:     crtHighResoMask = 0;
  1102:     crtHighResoTest = 0;
  1103:     crtHighResoCurr = 0;
  1104:     crtVResoPort = 0;
  1105:     crtVResoMask = 0;
  1106:     crtVResoTest = 0;
  1107:     crtVResoCurr = 0;
  1108:     crtHResoPort = 0;
  1109:     crtHResoMask = 0;
  1110:     crtHResoTest = 0;
  1111:     crtHResoCurr = 0;
  1112:     if (false) {
  1113:       crtCCPlane0 = false;
  1114:       crtCCPlane1 = false;
  1115:       crtCCPlane2 = false;
  1116:       crtCCPlane3 = false;
  1117:       crtSimPlane0 = false;
  1118:       crtSimPlane1 = false;
  1119:       crtSimPlane2 = false;
  1120:       crtSimPlane3 = false;
  1121:       crtSimAccess = false;
  1122:       crtBitMask = false;
  1123:     }
  1124:     crtR22SrcBlock = 0;
  1125:     crtR22DstBlock = 0;
  1126:     crtR23Mask = 0x0000;
  1127:     crtRasterCopyOn = false;
  1128:     crtClearStandby = false;
  1129:     crtClearFrames = 0;
  1130: 
  1131:     crtHRLPort = 0;
  1132:     crtHRLMask = 0;
  1133:     crtHRLTest = 0;
  1134:     crtHRLCurr = 0;
  1135:     XEiJ.pnlStretchMode = 1.0F;
  1136:     XEiJ.pnlStereoscopicShutter = 0;  //左右OPEN
  1137:     crtDuplication = false;
  1138:     crtInterlace = false;
  1139:     crtSlit = false;
  1140:     crtColumnTime = 0;
  1141:     crtHSyncLength = 0;
  1142:     crtHBackLength = 0;
  1143:     crtHDispLength = 0;
  1144:     crtHFrontLength = 0;
  1145:     crtHBackDispLength = 0;
  1146: 
  1147:     crtDirtyY0 = -1;
  1148:     crtDirtyY1 = -1;
  1149: 
  1150:     Arrays.fill (crtRasterStamp, 0);
  1151:     crtAllStamp = 1;  //初回は全再描画
  1152: 
  1153:     crtBeginningAllStamp = 1;
  1154: 
  1155:     crtRasterNumber = 0;
  1156:     crtDataY = 0;
  1157:     crtScreenY = 0;
  1158:     crtFrameParity = 0;
  1159: 
  1160:     crtRasterHashIdle = CRT_RASTER_HASH_ZERO;
  1161:     crtRasterHashDisp = CRT_RASTER_HASH_ZERO;
  1162: 
  1163:     crtContrastClock = XEiJ.FAR_FUTURE;
  1164:     crtCaptureClock = XEiJ.FAR_FUTURE;
  1165:     crtFrameTaskClock = Math.min (crtContrastClock, crtCaptureClock);
  1166: 
  1167:     if (CRT_ENABLE_INTERMITTENT) {  //間欠描画
  1168:       //crtIntermittentInterval = 0;
  1169:       crtIntermittentCounter = 0;
  1170:     }
  1171: 
  1172:     if (crtTicker != null) {
  1173:       TickerQueue.tkqRemove (crtTicker);
  1174:       crtTicker = null;
  1175:     }
  1176:     crtClock = XEiJ.FAR_FUTURE;  //停止
  1177: 
  1178:   }  //crtReset()
  1179: 
  1180:   //crtRestart ()
  1181:   //  CRTCのレジスタが設定されたのでCRT_RESTART_DELAY後にInitialStageを開始する
  1182:   public static void crtRestart () {
  1183:     if (crtTicker != null) {
  1184:       TickerQueue.tkqRemove (crtTicker);
  1185:     }
  1186:     TickerQueue.tkqAdd (crtTicker = InitialStage, crtClock = XEiJ.mpuClockTime + CRT_RESTART_DELAY);  //スクリーンの初期化へ
  1187:   }  //crtRestart()
  1188: 
  1189:   //crtStereoscopicStart ()
  1190:   //  垂直映像開始で
  1191:   public static void crtStereoscopicStart () {
  1192:     XEiJ.pnlBM = (XEiJ.pnlStereoscopicShutter != 1 ? XEiJ.pnlBMLeft :  //0=3=左右OPENまたは2=左OPENのとき左に描く
  1193:                   XEiJ.pnlBMRight);  //1=右OPENのとき右に描く
  1194:     crtAllStamp += 2;
  1195:   }
  1196: 
  1197:   //crtStereoscopicDrawRaster (screenY)
  1198:   //  drawRasterの後で
  1199:   public static void crtStereoscopicDrawRaster (int screenY) {
  1200:     if (XEiJ.pnlStereoscopicShutter == 0 ||
  1201:         XEiJ.pnlStereoscopicShutter == 3) {  //0=3=左右OPENのとき
  1202:       System.arraycopy (XEiJ.pnlBMLeft, screenY << XEiJ.PNL_BM_OFFSET_BITS,
  1203:                         XEiJ.pnlBMRight, screenY << XEiJ.PNL_BM_OFFSET_BITS,
  1204:                         XEiJ.pnlScreenWidth);  //左から右へコピー
  1205:     }
  1206:   }
  1207: 
  1208:   //crtUpdateScreen ()
  1209:   //  スクリーンを更新する
  1210:   public static void crtUpdateScreen () {
  1211:     if (XEiJ.pnlZoomRatioOut == 1 << 16) {  //拡大なし
  1212:       XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY1 + crtDirtyY0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, crtDirtyY1 - crtDirtyY0 + 1);
  1213:       if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn && XEiJ.pnlStereoscopicMethod == XEiJ.PNL_TOP_AND_BOTTOM) {
  1214:         XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY3 + crtDirtyY0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, crtDirtyY1 - crtDirtyY0 + 1);
  1215:       }
  1216:     } else {  //拡大あり
  1217:       int y0 = (crtDirtyY0 - 1) * XEiJ.pnlZoomRatioOut >> 16;
  1218:       int y1 = (crtDirtyY1 + 2) * (XEiJ.pnlZoomRatioOut + 1) >> 16;
  1219:       XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY1 + y0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, y1 - y0);
  1220:       if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn && XEiJ.pnlStereoscopicMethod == XEiJ.PNL_TOP_AND_BOTTOM) {
  1221:         XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY3 + y0, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, y1 - y0);
  1222:       }
  1223:     }
  1224:     crtDirtyY0 = -1;
  1225:   }
  1226: 
  1227:   //crtRepaint ()
  1228:   //  再描画
  1229:   //  MPUが止まっていても再描画する
  1230:   //  ダーティラスタのマークはそのままでMFPとのやりとりも行わない
  1231:   //  ラスタ割り込みを使用して作られている画面は再現できない
  1232:   public static void crtRepaint () {
  1233:     crtBeginningAllStamp = crtAllStamp;
  1234:     int l = Math.max (0, Math.min (1024, crtR07VDispEndCurr - crtR06VBackEndCurr));
  1235:     if (crtDuplication) {  //ラスタ2度読み
  1236:       for (int screenY = 0; screenY < l; screenY += 2) {
  1237:         if (SpriteScreen.SPR_THREE_STEPS) {
  1238:           SpriteScreen.sprStep1 (screenY >> 1);
  1239:           SpriteScreen.sprStep2 (screenY >> 1);
  1240:         }
  1241:         VideoController.vcnMode.drawRaster (screenY >> 1, screenY, false);  //スクリーンY座標へ描画
  1242:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1243:           crtStereoscopicDrawRaster (screenY);
  1244:         }
  1245:         //偶数ラスタを奇数ラスタにコピーする
  1246:         System.arraycopy (XEiJ.pnlBM, screenY << XEiJ.PNL_BM_OFFSET_BITS, XEiJ.pnlBM, screenY + 1 << XEiJ.PNL_BM_OFFSET_BITS, XEiJ.pnlScreenWidth);
  1247:       }
  1248:     } else if (crtSlit) {  //スリット
  1249:       for (int screenY = 0; screenY < l; screenY += 2) {
  1250:         if (SpriteScreen.SPR_THREE_STEPS) {
  1251:           SpriteScreen.sprStep1 (screenY >> 1);
  1252:           SpriteScreen.sprStep2 (screenY >> 1);
  1253:         }
  1254:         VideoController.vcnMode.drawRaster (screenY >> 1, screenY, false);  //スクリーンY座標へ描画
  1255:         crtScanlineEffect.drawRaster (screenY + 1);  //走査線エフェクト
  1256:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1257:           crtStereoscopicDrawRaster (screenY);
  1258:           crtStereoscopicDrawRaster (screenY + 1);
  1259:         }
  1260:       }
  1261:     } else {  //ノーマル,インターレース
  1262:       for (int screenY = 0; screenY < l; screenY++) {
  1263:         if (SpriteScreen.SPR_THREE_STEPS) {
  1264:           SpriteScreen.sprStep1 (screenY);
  1265:           SpriteScreen.sprStep2 (screenY);
  1266:         }
  1267:         VideoController.vcnMode.drawRaster (screenY, screenY, false);  //スクリーンY座標へ描画
  1268:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1269:           crtStereoscopicDrawRaster (screenY);
  1270:         }
  1271:       }
  1272:     }
  1273:     XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY1, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, l);
  1274:     if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn && XEiJ.pnlStereoscopicMethod == XEiJ.PNL_TOP_AND_BOTTOM) {
  1275:       XEiJ.pnlPanel.repaint (0L, XEiJ.pnlScreenX1, XEiJ.pnlScreenY3, XEiJ.pnlScreenX4 - XEiJ.pnlScreenX1, l);
  1276:     }
  1277:   }  //crtRepaint()
  1278: 
  1279:   //crtDoFrameTask ()
  1280:   //  垂直同期パルスに行う処理
  1281:   //  垂直同期パルスに入ったときXEiJ.mpuClockTime>=crtFrameTaskClockならば呼び出す
  1282:   //  コントラストの調整
  1283:   //  画面キャプチャ
  1284:   public static void crtDoFrameTask () {
  1285:     if (XEiJ.mpuClockTime >= crtContrastClock) {
  1286:       VideoController.vcnCurrentScaledContrast += VideoController.vcnCurrentScaledContrast < VideoController.vcnTargetScaledContrast ? 1 : -1;
  1287:       VideoController.vcnSetContrast (VideoController.vcnCurrentScaledContrast);
  1288:       if (VideoController.vcnCurrentScaledContrast == VideoController.vcnTargetScaledContrast) {
  1289:         crtContrastClock = XEiJ.FAR_FUTURE;
  1290:       } else {
  1291:         crtContrastClock += VideoController.VCN_CONTRAST_DELAY;
  1292:       }
  1293:     }
  1294:     if (XEiJ.mpuClockTime >= crtCaptureClock) {
  1295:       GIFAnimation.gifCaptureFrame ();
  1296:     }
  1297:     crtFrameTaskClock = Math.min (crtContrastClock, crtCaptureClock);
  1298:   }  //crtDoFrameTask()
  1299: 
  1300:   //crtSetMemoryMode (textStorage, graphicStorage, memoryMode)
  1301:   //  テキストストレージ(R20 12)
  1302:   //  グラフィックストレージ(R20 11)
  1303:   //  メモリモード(R20 10-8)を変更する
  1304:   public static void crtSetMemoryMode (int textStorage, int graphicStorage, int memoryMode) {
  1305:     boolean updateMemoryMap = false;  //メモリマップを更新するか
  1306:     textStorage &= 1;
  1307:     if (crtTextStorage != textStorage) {  //テキストストレージを変更する
  1308:       crtTextStorage = textStorage;
  1309:     }
  1310:     graphicStorage &= 1;
  1311:     if (crtGraphicStorage != graphicStorage) {  //グラフィックストレージを変更する
  1312:       crtGraphicStorage = graphicStorage;
  1313:       updateMemoryMap = true;  //メモリマップを更新する
  1314:     }
  1315:     memoryMode &= 7;
  1316:     crtMemoryModePort = memoryMode;
  1317:     int curr = crtMemoryModeMask == 0 ? crtMemoryModePort : crtMemoryModeTest;
  1318:     if (crtMemoryModeCurr != curr) {  //メモリモードを変更する
  1319:       crtMemoryModeCurr = curr;
  1320:       updateMemoryMap = true;  //メモリマップを更新する
  1321:     }
  1322:     if (updateMemoryMap) {  //メモリマップを更新する
  1323:       if (crtGraphicStorage != 0) {  //グラフィックストレージON
  1324:         if (CRT_EXTENDED_GRAPHIC && crtExtendedGraphicOn &&  //拡張グラフィック画面がONかつ
  1325:             (crtMemoryModeCurr == 5 || crtMemoryModeCurr == 7)) {  //メモリモード5または7のとき
  1326:           //メモリモード7相当
  1327:           XEiJ.busSuper (MemoryMappedDevice.MMD_GJ0, 0x00c00000, 0x00e00000);
  1328:         } else {
  1329:           //メモリモード3相当
  1330:           XEiJ.busSuper (MemoryMappedDevice.MMD_GG0, 0x00c00000, 0x00c80000);
  1331:           XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00c80000, 0x00e00000);
  1332:         }
  1333:       } else {  //グラフィックストレージOFF
  1334:         switch (crtMemoryModeCurr) {
  1335:         case 0:  //メモリモード0
  1336:           //512ドット16色
  1337:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE0, 0x00c00000, 0x00c80000);
  1338:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE1, 0x00c80000, 0x00d00000);
  1339:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE2, 0x00d00000, 0x00d80000);
  1340:           XEiJ.busSuper (MemoryMappedDevice.MMD_GE3, 0x00d80000, 0x00e00000);
  1341:           break;
  1342:         case 1:  //メモリモード1
  1343:           //512ドット256色
  1344:           XEiJ.busSuper (MemoryMappedDevice.MMD_GF0, 0x00c00000, 0x00c80000);
  1345:           XEiJ.busSuper (MemoryMappedDevice.MMD_GF1, 0x00c80000, 0x00d00000);
  1346:           XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00d00000, 0x00e00000);
  1347:           break;
  1348:         case 2:  //メモリモード2
  1349:           //メモリモード2
  1350:           XEiJ.busSuper (MemoryMappedDevice.MMD_GM2, 0x00c00000, 0x00e00000);
  1351:           break;
  1352:         case 3:  //メモリモード3
  1353:           //512ドット65536色
  1354:           XEiJ.busSuper (MemoryMappedDevice.MMD_GG0, 0x00c00000, 0x00c80000);
  1355:           XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00c80000, 0x00e00000);
  1356:           break;
  1357:         case 4:  //メモリモード4
  1358:           //1024ドット16色
  1359:           XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1360:           break;
  1361:         case 5:  //メモリモード5
  1362:           if (CRT_EXTENDED_GRAPHIC && crtExtendedGraphicOn) {
  1363:             //1024ドット256色(拡張)
  1364:             XEiJ.busSuper (MemoryMappedDevice.MMD_GI0, 0x00c00000, 0x00e00000);
  1365:           } else {
  1366:             //1024ドット16色
  1367:             XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1368:           }
  1369:           break;
  1370:         case 6:  //メモリモード6
  1371:           //1024ドット16色
  1372:           XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1373:           break;
  1374:         case 7:  //メモリモード7
  1375:           if (CRT_EXTENDED_GRAPHIC && crtExtendedGraphicOn) {
  1376:             //1024ドット65536色(拡張)
  1377:             XEiJ.busSuper (MemoryMappedDevice.MMD_GJ0, 0x00c00000, 0x00e00000);
  1378:           } else {
  1379:             //1024ドット16色
  1380:             XEiJ.busSuper (MemoryMappedDevice.MMD_GH0, 0x00c00000, 0x00e00000);
  1381:           }
  1382:           break;
  1383:         }
  1384:       }
  1385:     }
  1386:   }  //crtSetMemoryMode
  1387: 
  1388:   //crtUpdateRasterHash ()
  1389:   //  crtRasterHashIdle,crtRasterHashDispを設定する
  1390:   //    crtRasterCopyOn
  1391:   //    RasterBreakPoint.rbpActiveBreakRaster
  1392:   //    crtR09IRQRasterCurr
  1393:   //    crtVDispStart
  1394:   //    crtR04VFrontEndCurr
  1395:   //    crtVIdleStart
  1396:   //  が更新されたときに呼び出す
  1397:   //  crtR04VFrontEndCurrがcrtRasterNumberよりも小さい値に変更されると通り過ぎてしまうので必ずcrtRasterNumber=0からリスタートさせること
  1398:   public static void crtUpdateRasterHash () {
  1399:     if (CRT_RASTER_HASH_ON) {
  1400:       long t = crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO;  //intまたはlong
  1401:       //int t = crtRasterCopyOn ? ~CRT_RASTER_HASH_ZERO : CRT_RASTER_HASH_ZERO;  //intまたはlong
  1402:       if (RasterBreakPoint.RBP_ON && RasterBreakPoint.rbpActiveBreakRaster >= 0) {
  1403:         t |= CRT_RASTER_HASH_MSB >>> RasterBreakPoint.rbpActiveBreakRaster;
  1404:       }
  1405:       if (crtR09IRQRasterCurr <= crtR04VFrontEndCurr) {
  1406:         t |= (CRT_RASTER_HASH_MSB >>> crtR09IRQRasterCurr |
  1407:               CRT_RASTER_HASH_MSB >>> (crtR09IRQRasterCurr < crtR04VFrontEndCurr ? crtR09IRQRasterCurr + 1 : 0));
  1408:       }
  1409:       crtRasterHashIdle = (t |
  1410:                            CRT_RASTER_HASH_MSB >>> crtVDispStart |
  1411:                            CRT_RASTER_HASH_MSB >>> crtR04VFrontEndCurr + 1);
  1412:       crtRasterHashDisp = (t |
  1413:                            CRT_RASTER_HASH_MSB >>> crtVIdleStart);
  1414:     }
  1415:   }  //crtUpdateRasterHash()
  1416: 
  1417:   //crtRapidClear (y)
  1418:   //  高速クリア実行
  1419:   //! 軽量化。水平方向は表示範囲に関係なく常に仮想画面の幅全体をクリアしてしまう
  1420:   public static void crtRapidClear (int y) {
  1421:     if (crtMemoryModeCurr >= 4) {  //1024ドットモード
  1422:       int a = 0x00c00000 | (y + crtR13GrYZero[0] & 1023) << 1 + 10;
  1423:       Arrays.fill (MainMemory.mmrM8, a, a + 2 * 1024, (byte) 0);
  1424:     } else {  //512ドットモード
  1425:       if (crtCCPlane0) {
  1426:         int a = 0x00c00000 | (y + crtR13GrYZero[0] & 511) << 1 + 9;
  1427:         Arrays.fill (MainMemory.mmrM8, a, a + 2 * 512, (byte) 0);
  1428:       }
  1429:       if (crtCCPlane1) {
  1430:         int a = 0x00c80000 | (y + crtR13GrYZero[1] & 511) << 1 + 9;
  1431:         Arrays.fill (MainMemory.mmrM8, a, a + 2 * 512, (byte) 0);
  1432:       }
  1433:       if (crtCCPlane2) {
  1434:         int a = 0x00d00000 | (y + crtR13GrYZero[2] & 511) << 1 + 9;
  1435:         Arrays.fill (MainMemory.mmrM8, a, a + 2 * 512, (byte) 0);
  1436:       }
  1437:       if (crtCCPlane3) {
  1438:         int a = 0x00d80000 | (y + crtR13GrYZero[3] & 511) << 1 + 9;
  1439:         Arrays.fill (MainMemory.mmrM8, a, a + 2 * 512, (byte) 0);
  1440:       }
  1441:     }
  1442:   }  //crtRapidClear(int)
  1443: 
  1444:   //crtDoRasterCopy ()
  1445:   //  ラスタコピー実行
  1446:   public static void crtDoRasterCopy () {
  1447:     int srcOffset = crtR22SrcBlock << 9;
  1448:     int dstOffset = crtR22DstBlock << 9;
  1449:     if (crtCCPlane0) {
  1450:       System.arraycopy (MainMemory.mmrM8, 0x00e00000 + srcOffset, MainMemory.mmrM8, 0x00e00000 + dstOffset, 512);
  1451:     }
  1452:     if (crtCCPlane1) {
  1453:       System.arraycopy (MainMemory.mmrM8, 0x00e20000 + srcOffset, MainMemory.mmrM8, 0x00e20000 + dstOffset, 512);
  1454:     }
  1455:     if (crtCCPlane2) {
  1456:       System.arraycopy (MainMemory.mmrM8, 0x00e40000 + srcOffset, MainMemory.mmrM8, 0x00e40000 + dstOffset, 512);
  1457:     }
  1458:     if (crtCCPlane3) {
  1459:       System.arraycopy (MainMemory.mmrM8, 0x00e60000 + srcOffset, MainMemory.mmrM8, 0x00e60000 + dstOffset, 512);
  1460:     }
  1461:     int y = (dstOffset >> 7) - crtR11TxYZero;
  1462:     crtRasterStamp[y     & 1023] = 0;
  1463:     crtRasterStamp[y + 1 & 1023] = 0;
  1464:     crtRasterStamp[y + 2 & 1023] = 0;
  1465:     crtRasterStamp[y + 3 & 1023] = 0;
  1466:   }  //crtDoRasterCopy()
  1467: 
  1468: 
  1469: 
  1470:   //========================================================================================
  1471:   //
  1472:   //  CRTCのステージ
  1473:   //
  1474:   //    ラスタ(水平周期)
  1475:   //      水平フロントポーチと水平同期パルスと水平バックポーチと水平映像期間を合わせた期間
  1476:   //
  1477:   //    フレーム(垂直周期)
  1478:   //      垂直空白期間(垂直フロントポーチと垂直同期パルスと垂直バックポーチ)と垂直映像期間を合わせた期間
  1479:   //      垂直映像期間と垂直フロントポーチはそれぞれ少なくとも1ラスタ以上必要
  1480:   //        垂直フロントポーチが1ラスタ以上必要なのは垂直映像期間にラスタ番号のラップアラウンドを行なっていないため
  1481:   //        垂直映像期間にラスタ番号のラップアラウンドを行う場合でも垂直空白期間は少なくとも1ラスタ以上必要
  1482:   //
  1483:   //    ラスタ番号
  1484:   //      ラスタの番号。0からラスタ数-1まで。0は垂直同期パルス開始ラスタ、ラスタ数-1は垂直フロントポーチ終了ラスタ
  1485:   //      遷移とIRQ信号の生成に用いる
  1486:   //
  1487:   //    フレームパリティ
  1488:   //      フレーム番号の下位1bit。0または1
  1489:   //      インターレースのときにフレーム毎のデータY座標とスクリーンY座標の初期値として使う
  1490:   //      フレームの末尾で反転する
  1491:   //
  1492:   //    データY座標
  1493:   //      ラスタに供給されるデータのY座標
  1494:   //
  1495:   //    スクリーンY座標
  1496:   //      ラスタを表示するスクリーン上のY座標
  1497:   //
  1498:   //
  1499:   //    ノーマル
  1500:   //      フレームの先頭でデータY座標とスクリーンY座標を0で初期化する
  1501:   //      ラスタを描画してからスクリーンY座標を1増やす
  1502:   //      ラスタの末尾でデータY座標を1増やす
  1503:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを1で初期化する
  1504:   //
  1505:   //    ラスタ2度読み
  1506:   //      フレームの先頭でデータY座標とスクリーンY座標を0で初期化する
  1507:   //      偶数ラスタを描画してからスクリーンY座標を1増やす
  1508:   //      偶数ラスタの末尾ではデータY座標を増やさない
  1509:   //      奇数ラスタを描画してからスクリーンY座標を1増やす
  1510:   //      奇数ラスタの末尾でデータY座標を1増やす
  1511:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを1で初期化する
  1512:   //
  1513:   //    インターレース
  1514:   //      フレームの先頭でデータY座標とスクリーンY座標をフレームパリティで初期化する
  1515:   //      ラスタを描画してからスクリーンY座標を2増やす
  1516:   //      ラスタの末尾でデータY座標を2増やす
  1517:   //      (偶数フレームは偶数ラスタだけ、奇数フレームは奇数ラスタだけ描画する)
  1518:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを2で初期化する
  1519:   //
  1520:   //    スリット
  1521:   //      フレームの先頭でデータY座標とスクリーンY座標を0で初期化する
  1522:   //      ラスタを描画してから輝度を落としてコピーし、スクリーンY座標を2増やす
  1523:   //      ラスタの末尾でデータY座標を1増やす
  1524:   //      垂直映像期間の先頭で高速クリアの要求があるとき高速クリアカウンタを1で初期化する
  1525:   //
  1526:   //
  1527:   //                                         データY座標     スクリーンY座標
  1528:   //    ───────────────────────────────────
  1529:   //        ノーマル          初期化               0                 0
  1530:   //                       ラスタ描画後           +1                +1
  1531:   //    ───────────────────────────────────
  1532:   //      ラスタ2度読み       初期化               0                 0
  1533:   //                     偶数ラスタ描画後         +0                +1
  1534:   //                     奇数ラスタ描画後         +1                +1
  1535:   //    ───────────────────────────────────
  1536:   //     インターレース       初期化       フレームパリティ  フレームパリティ
  1537:   //                       ラスタ描画後           +2                +2
  1538:   //    ───────────────────────────────────
  1539:   //        スリット          初期化               0                 0
  1540:   //                       ラスタ描画後           +1                +2
  1541:   //    ───────────────────────────────────
  1542:   //
  1543:   //
  1544:   //    水平フロントポーチ
  1545:   //      +水平フロントポーチの長さ
  1546:   //      ラスタ番号を1増やす
  1547:   //      [垂直空白期間のとき]
  1548:   //        ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  1549:   //          ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  1550:   //      ラスタコピースイッチがONのとき
  1551:   //        ラスタコピー実行
  1552:   //      ブレークラスタのとき
  1553:   //        ラスタブレークをかける
  1554:   //      IRQ信号を更新
  1555:   //      IRQ信号が変化したとき
  1556:   //        IRQ信号が0になったとき
  1557:   //        | IRQラスタでラスタブレークをかけるとき
  1558:   //        |   ブレークラスタではないとき(ブレークラスタとIRQラスタが同じときは既にラスタブレークがかかっている)
  1559:   //        |      ラスタブレークをかける
  1560:   //        | IRQ開始
  1561:   //        IRQ信号が0でなくなったとき
  1562:   //          IRQ終了
  1563:   //      [垂直空白期間のとき]
  1564:   //      | 垂直映像期間開始ラスタではないとき
  1565:   //      | | [描画フレーム(間欠カウンタ==0)のとき]
  1566:   //      | | | →描画フレームの垂直空白期間の水平同期パルス
  1567:   //      | | [省略フレーム(間欠カウンタ!=0)のとき]
  1568:   //      | |   →省略フレームの垂直空白期間の水平同期パルス
  1569:   //      | 垂直映像期間開始ラスタのとき
  1570:   //      |   垂直映像期間開始
  1571:   //      |   テキストY方向スクロールを保存
  1572:   //      |   グラフィックY方向スクロールを保存
  1573:   //      |   [インターレースではないとき]
  1574:   //      |   | データY座標を0で初期化
  1575:   //      |   [インターレースのとき]
  1576:   //      |     データY座標をフレームパリティで初期化
  1577:   //      |   高速クリアの要求があるとき
  1578:   //      |     [インターレースではないとき]
  1579:   //      |     | 高速クリアカウンタを1で初期化
  1580:   //      |     [インターレースのとき]
  1581:   //      |       高速クリアカウンタを2で初期化
  1582:   //      |   [描画フレーム(間欠カウンタ==0)のとき]
  1583:   //      |   | [インターレースではないとき]
  1584:   //      |   | | スクリーンY座標を0で初期化
  1585:   //      |   | [インターレースのとき]
  1586:   //      |   |   スクリーンY座標をフレームパリティで初期化
  1587:   //      |   | ダーティフラグをクリア
  1588:   //      |   | →描画フレームの垂直映像期間の水平同期パルス
  1589:   //      |   [省略フレーム(間欠カウンタ!=0)のとき]
  1590:   //      |     →省略フレームの垂直映像期間の水平同期パルス
  1591:   //      [垂直映像期間のとき]
  1592:   //        垂直空白期間開始ラスタではないとき
  1593:   //        | [描画フレーム(間欠カウンタ==0)のとき]
  1594:   //        | | [全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき]
  1595:   //        | | | データY座標からスクリーンY座標へ描画
  1596:   //        | | [ノーマルのとき]
  1597:   //        | | | データY座標を1増やす
  1598:   //        | | | スクリーンY座標を1増やす
  1599:   //        | | [ラスタ2度読みのとき]
  1600:   //        | | | スクリーンY座標を1増やす
  1601:   //        | | | [偶数ラスタのとき]
  1602:   //        | | |   データY座標を1増やす
  1603:   //        | | [インターレースのとき]
  1604:   //        | | | スクリーンY座標を2増やす
  1605:   //        | | | データY座標を2増やす
  1606:   //        | | [スリットのとき]
  1607:   //        | |   スクリーンY座標を2増やす
  1608:   //        | |   データY座標を1増やす
  1609:   //        | | →描画フレームの垂直映像期間の水平同期パルス
  1610:   //        | [省略フレーム(間欠カウンタ!=0)のとき]
  1611:   //        |   →省略フレームの垂直映像期間の水平同期パルス
  1612:   //        垂直空白期間開始ラスタのとき
  1613:   //          [全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき]
  1614:   //          | データY座標からスクリーンY座標へ描画
  1615:   //          垂直映像期間終了
  1616:   //          高速クリアカウンタが0ではないとき
  1617:   //            高速クリアカウンタを1減らす
  1618:   //          [描画フレーム(間欠カウンタ==0)のとき]
  1619:   //          | ダーティフラグがセットされているとき
  1620:   //          |   スクリーンを更新する
  1621:   //          |   間欠カウンタを間欠間隔に戻す
  1622:   //          [省略フレーム(間欠カウンタ!=0)のとき]
  1623:   //            間欠カウンタを1減らす
  1624:   //          [インターレースのとき]
  1625:   //            フレームパリティを反転
  1626:   //          フレームタスク(コントラスト調整など)
  1627:   //          [描画フレーム(間欠カウンタ==0)のとき]
  1628:   //          | →描画フレームの垂直空白期間の水平同期パルス
  1629:   //          [省略フレーム(間欠カウンタ!=0)のとき]
  1630:   //            →省略フレームの垂直空白期間の水平同期パルス
  1631:   //
  1632:   //    水平同期パルス
  1633:   //      +水平同期パルスの長さ
  1634:   //      水平同期パルス開始
  1635:   //      [垂直空白期間のとき]
  1636:   //      | [描画フレーム(間欠カウンタ==0)のとき]
  1637:   //      | | →描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  1638:   //      | [省略フレーム(間欠カウンタ!=0)のとき]
  1639:   //      |   →省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  1640:   //      [垂直映像期間のとき]
  1641:   //        [ラスタ2度読みではないとき]
  1642:   //        | 高速クリアカウンタが0ではないとき
  1643:   //        |   データY座標を高速クリア
  1644:   //        [ラスタ2度読みのとき]
  1645:   //          [偶数ラスタのとき]
  1646:   //            高速クリアカウンタが0ではないとき
  1647:   //              データY座標を高速クリア
  1648:   //        [描画フレーム(間欠カウンタ==0)のとき]
  1649:   //        | →描画フレームの垂直映像期間の水平バックポーチ
  1650:   //        [省略フレーム(間欠カウンタ!=0)のとき]
  1651:   //          →省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  1652:   //
  1653:   //    水平バックポーチ
  1654:   //      水平同期パルス終了
  1655:   //      [垂直空白期間のとき]
  1656:   //      | [描画フレーム(間欠カウンタ==0)のとき]
  1657:   //      | | +水平バックポーチと水平映像期間の長さ
  1658:   //      | | →描画フレームの垂直空白期間の水平フロントポーチ
  1659:   //      | [省略フレーム(間欠カウンタ!=0)のとき]
  1660:   //      |   +水平バックポーチと水平映像期間の長さ
  1661:   //      |   →省略フレームの垂直空白期間の水平フロントポーチ
  1662:   //      [垂直映像期間のとき]
  1663:   //        [描画フレーム(間欠カウンタ==0)のとき]
  1664:   //        | +水平バックポーチの長さ
  1665:   //        | →描画フレームの垂直映像期間の水平映像期間
  1666:   //        [省略フレーム(間欠カウンタ!=0)のとき]
  1667:   //          +水平バックポーチと水平映像期間の長さ
  1668:   //          →省略フレームの垂直映像期間の水平フロントポーチ
  1669:   //
  1670:   //    描画フレームの垂直映像期間の水平映像期間
  1671:   //      +水平水平映像期間の長さ
  1672:   //      ラスタの更新フラグがセットされているとき
  1673:   //        [ノーマルのとき]
  1674:   //        | ラスタの更新フラグをクリア
  1675:   //        [ラスタ2度読みのとき]
  1676:   //        | [奇数ラスタのとき]
  1677:   //        |   ラスタの更新フラグをクリア
  1678:   //        [インターレースのとき]
  1679:   //        | ラスタの更新フラグをクリア
  1680:   //        [スリットのとき]
  1681:   //          ラスタの更新フラグをクリア
  1682:   //        全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  1683:   //        データY座標からスクリーンY座標へ描画
  1684:   //        [スリットのとき]
  1685:   //          輝度を半分にしてコピーする
  1686:   //        ダーティフラグをセット
  1687:   //      →描画フレームの垂直映像期間の水平フロントポーチ
  1688:   //
  1689:   //
  1690:   //    描画フレーム(間欠カウンタ==0)
  1691:   //      描画フレームの垂直空白期間(垂直フロントポーチと垂直同期パルスと垂直バックポーチ)
  1692:   //        (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)            Start
  1693:   //        (1)描画フレームの垂直空白期間の水平フロントポーチ                              DrawIdleFront
  1694:   //        (2)描画フレームの垂直空白期間の水平同期パルス                                  DrawIdleSync
  1695:   //        (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間                  DrawIdleBackDisp
  1696:   //      描画フレームの垂直映像期間
  1697:   //        奇偶共通
  1698:   //          (4)描画フレームの垂直映像期間の水平フロントポーチ                            DrawDispFront
  1699:   //          (5)描画フレームの垂直映像期間の水平同期パルス                                DrawDispSync
  1700:   //          (6)描画フレームの垂直映像期間の水平バックポーチ                              DrawDispBack
  1701:   //          (7)描画フレームの垂直映像期間の水平映像期間                                  DrawDispDisp
  1702:   //        偶数ラスタ
  1703:   //          (4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ               DrawDispEvenFront
  1704:   //          (5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス                   DrawDispEvenSync
  1705:   //          (6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ                 DrawDispEvenBack
  1706:   //          (7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間                     DrawDispEvenDisp
  1707:   //        奇数ラスタ
  1708:   //          (4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ               DrawDispOddFront
  1709:   //          (5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス                   DrawDispOddSync
  1710:   //          (6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ                 DrawDispOddBack
  1711:   //          (7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間                     DrawDispOddDisp
  1712:   //    省略フレーム(間欠カウンタ!=0)
  1713:   //      省略フレームの垂直空白期間(垂直フロントポーチと垂直同期パルスと垂直バックポーチ)
  1714:   //        (8)省略フレームの垂直空白期間の水平フロントポーチ                              OmitIdleFront
  1715:   //        (9)省略フレームの垂直空白期間の水平同期パルス                                  OmitIdleSync
  1716:   //        (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間                 OmitIdleBackDisp
  1717:   //      省略フレームの垂直映像期間
  1718:   //        奇偶共通
  1719:   //          (11)省略フレームの垂直映像期間の水平フロントポーチ                           OmitDispFront
  1720:   //          (12)省略フレームの垂直映像期間の水平同期パルス                               OmitDispSync
  1721:   //          (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間               OmitDispBackDisp
  1722:   //        偶数ラスタ
  1723:   //          (11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ              OmitDispEvenFront
  1724:   //          (12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス                  OmitDispEvenSync
  1725:   //          (13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間  OmitDispEvenBackDisp
  1726:   //        奇数ラスタ
  1727:   //          (11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ              OmitDispOddFront
  1728:   //          (12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス                  OmitDispOddSync
  1729:   //          (13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間  OmitDispOddBackDisp
  1730:   //
  1731: 
  1732:   //----------------------------------------------------------------
  1733:   //  スクリーンの初期化
  1734:   public static final TickerQueue.Ticker InitialStage = new TickerQueue.Ticker () {
  1735:     @Override protected void tick () {
  1736:     ret:
  1737:       {
  1738:         //  水平映像期間は1カラム以上128カラム以下でなければならない
  1739:         //  垂直映像期間は1ラスタ以上1024ラスタ以下でなければならない
  1740:         //  垂直同期パルス、垂直バックポーチ、垂直フロントポーチはそれぞれ1ラスタ以上なければならない
  1741:         //  不正な値が設定されているときは停止して少し待つ
  1742:         //  同期信号が動かなくなるので不正な値を設定してから同期信号を待つようなプログラムは止まってしまう
  1743:         if (!(crtR02HBackEndCurr < crtR03HDispEndCurr && crtR03HDispEndCurr - crtR02HBackEndCurr <= 128 &&
  1744:               crtR07VDispEndCurr < crtR04VFrontEndCurr && crtR07VDispEndCurr - crtR06VBackEndCurr <= 1024 &&
  1745:               crtR05VSyncEndCurr < crtR06VBackEndCurr &&
  1746:               crtR06VBackEndCurr < crtR07VDispEndCurr)) {
  1747:           crtRestart ();
  1748:           break ret;
  1749:         }
  1750:         //
  1751:         if (CRT_RASTER_HASH_ON) {
  1752:           crtUpdateRasterHash ();
  1753:         }
  1754:         //  水平周期が水平映像期間よりも3カラム以上長くないとき
  1755:         //    水平周期を伸ばして水平同期パルスと水平バックポーチと水平フロントポーチをすべて1カラムにする
  1756:         //  水平周期が水平映像期間よりも3カラム以上長いとき
  1757:         //    必要ならば水平バックポーチと水平フロントポーチが1カラム以上なるように水平周期の中の水平映像期間の位置を調整する
  1758:         //    動作速度が変わってしまわないように、水平周期はなるべく変更しない
  1759:         //  調整するのは状態遷移の間隔だけで、CRTCの設定値は変更しない
  1760:         int hSync = crtR01HSyncEndCurr + 1;  //水平同期パルスカラム数。0以下は設定のしようがないので1以上
  1761:         int hBack = crtR02HBackEndCurr - crtR01HSyncEndCurr + 4;  //水平バックポーチカラム数。-1以上
  1762:         int hDisp = crtR03HDispEndCurr - crtR02HBackEndCurr;  //水平映像期間カラム数。0以下は除いてあるので1以上
  1763:         int hFront = crtR00HFrontEndCurr - crtR03HDispEndCurr - 4;  //水平フロントポーチカラム数。負数かも知れない
  1764:         if (hSync + hBack + hDisp + hFront < hDisp + 3) {  //水平周期が水平映像期間よりも3カラム以上長くないとき
  1765:           hSync = hBack = hFront = 1;  //水平周期を伸ばして水平同期パルスと水平バックポーチと水平フロントポーチをすべて1カラムにする
  1766:         } else {  //水平周期が水平映像期間よりも3カラム以上長いとき
  1767:           if (hBack <= 0) {  //左に寄り過ぎているとき
  1768:             hFront -= 1 - hBack;  //水平フロントポーチを削って
  1769:             hBack = 1;  //水平バックポーチを1にする
  1770:             if (hFront <= 0) {  //水平フロントポーチを削り過ぎたとき
  1771:               hSync -= 1 - hFront;  //水平同期パルスを削って
  1772:               hFront = 1;  //水平フロントポーチを1にする
  1773:             }
  1774:           } else if (hFront <= 0) {  //右に寄り過ぎているとき
  1775:             hBack -= 1 - hFront;  //水平バックポーチを削って
  1776:             hFront = 1;  //水平フロントポーチを1にする
  1777:             if (hBack <= 0) {  //水平バックポーチを削り過ぎたとき
  1778:               hSync -= 1 - hBack;  //水平同期パルスを削って
  1779:               hBack = 1;  //水平バックポーチを1にする
  1780:             }
  1781:           }
  1782:         }
  1783:         int k = crtHRLCurr << 3 | crtHighResoCurr << 2 | crtHResoCurr;
  1784:         crtColumnTime = (int) ((double) (XEiJ.TMR_FREQ * 8 * CRT_DIVS[k]) / (double) crtFreqs[CRT_OSCS[k]] + 0.5);
  1785:         crtHSyncLength = crtColumnTime * hSync;
  1786:         crtHBackLength = crtColumnTime * hBack;
  1787:         crtHDispLength = crtColumnTime * hDisp;
  1788:         crtHFrontLength = crtColumnTime * hFront;
  1789:         crtHBackDispLength = crtColumnTime * (hBack + hDisp);
  1790:         //
  1791:         crtDuplication = crtHighResoCurr == 1 && crtVResoCurr == 0;  //ラスタ2度読み
  1792:         crtInterlace = crtHighResoCurr + 1 <= crtVResoCurr;  //インターレース
  1793:         crtSlit = crtHighResoCurr == 0 && crtVResoCurr == 0;  //スリット
  1794:         //
  1795:         XEiJ.pnlUpdateArrangement ();
  1796:         //
  1797:         crtAllStamp += 2;
  1798:         (crtDuplication ? DuplicationStart : crtInterlace ? InterlaceStart : crtSlit ? SlitStart : NormalStart).tick ();
  1799:       }  //ret
  1800:     }
  1801:   };
  1802: 
  1803:   //----------------------------------------------------------------
  1804:   //  ノーマル
  1805:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  1806:   public static final TickerQueue.Ticker NormalStart = new TickerQueue.Ticker () {
  1807:     @Override protected void tick () {
  1808:       if (MC68901.mfpGpipHsync != 0) {
  1809:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  1810:       }
  1811:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  1812:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  1813:         crtDoRasterCopy ();  //ラスタコピー実行
  1814:       }
  1815:       if (RasterBreakPoint.RBP_ON) {
  1816:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  1817:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1818:         }
  1819:       }
  1820:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  1821:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  1822:         if (irq == 0) {  //IRQ信号が0になったとき
  1823:           if (RasterBreakPoint.RBP_ON) {
  1824:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  1825:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1826:             }
  1827:           }
  1828:           MC68901.mfpRintFall ();  //IRQ開始
  1829:         } else {  //IRQ信号が0でなくなったとき
  1830:           MC68901.mfpRintRise ();  //IRQ終了
  1831:         }
  1832:       }
  1833:       if (MC68901.mfpGpipVdisp != 0) {
  1834:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  1835:       }
  1836:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  1837:       if (CRT_ENABLE_INTERMITTENT) {
  1838:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  1839:       }
  1840:       if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  1841:         crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  1842:       }
  1843:       TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  1844:     }
  1845:   };
  1846:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  1847:   public static final TickerQueue.Ticker NormalDrawIdleFront = new TickerQueue.Ticker () {
  1848:     @Override protected void tick () {
  1849:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  1850:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  1851:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  1852:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  1853:         }
  1854:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  1855:           crtDoRasterCopy ();  //ラスタコピー実行
  1856:         }
  1857:         if (RasterBreakPoint.RBP_ON) {
  1858:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  1859:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1860:           }
  1861:         }
  1862:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  1863:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  1864:           if (irq == 0) {  //IRQ信号が0になったとき
  1865:             if (RasterBreakPoint.RBP_ON) {
  1866:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  1867:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1868:               }
  1869:             }
  1870:             MC68901.mfpRintFall ();  //IRQ開始
  1871:           } else {  //IRQ信号が0でなくなったとき
  1872:             MC68901.mfpRintRise ();  //IRQ終了
  1873:           }
  1874:         }
  1875:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  1876:           TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  1877:         } else {  //垂直映像期間開始ラスタのとき
  1878:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  1879:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  1880:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  1881:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  1882:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  1883:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  1884:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  1885:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  1886:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  1887:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  1888:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  1889:             crtR11TxYZeroLast = crtR11TxYZero;
  1890:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  1891:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  1892:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  1893:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  1894:             crtAllStamp += 2;
  1895:           }
  1896:           crtDataY = 0;  //データY座標を0で初期化
  1897:           if (crtClearStandby) {  //高速クリアの要求があるとき
  1898:             crtClearStandby = false;
  1899:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  1900:           }
  1901:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  1902:           crtDirtyY0 = -1;  //ダーティフラグをクリア
  1903:           if (SpriteScreen.SPR_THREE_STEPS) {
  1904:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  1905:             if (SpriteScreen.sprActive) {
  1906:               crtAllStamp += 2;
  1907:               //ラスタ(dst=-2,src=-2)
  1908:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  1909:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  1910:               //ラスタ(dst=-1,src=-1)
  1911:               //表(-1)を表(1)として再利用する
  1912:               SpriteScreen.sprStep1 (1);  //表(1)にスプライト(1)を並べる
  1913:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  1914:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  1915:             }
  1916:           }
  1917:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1918:             crtStereoscopicStart ();
  1919:           }
  1920:           TickerQueue.tkqAdd (crtTicker = NormalDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  1921:         }
  1922:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  1923:         //垂直映像期間開始ラスタではないとき
  1924:         TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  1925:       }
  1926:     }
  1927:   };
  1928:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  1929:   public static final TickerQueue.Ticker NormalDrawIdleSync = new TickerQueue.Ticker () {
  1930:     @Override protected void tick () {
  1931:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  1932:       TickerQueue.tkqAdd (crtTicker = NormalDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  1933:     }
  1934:   };
  1935:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  1936:   public static final TickerQueue.Ticker NormalDrawIdleBackDisp = new TickerQueue.Ticker () {
  1937:     @Override protected void tick () {
  1938:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  1939:       TickerQueue.tkqAdd (crtTicker = NormalDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→描画フレームの垂直空白期間の水平フロントポーチ
  1940:     }
  1941:   };
  1942:   //    (4)描画フレームの垂直映像期間の水平フロントポーチ
  1943:   public static final TickerQueue.Ticker NormalDrawDispFront = new TickerQueue.Ticker () {
  1944:     @Override protected void tick () {
  1945:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  1946:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  1947:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  1948:           crtDoRasterCopy ();  //ラスタコピー実行
  1949:         }
  1950:         if (RasterBreakPoint.RBP_ON) {
  1951:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  1952:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1953:           }
  1954:         }
  1955:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  1956:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  1957:           if (irq == 0) {  //IRQ信号が0になったとき
  1958:             if (RasterBreakPoint.RBP_ON) {
  1959:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  1960:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  1961:               }
  1962:             }
  1963:             MC68901.mfpRintFall ();  //IRQ開始
  1964:           } else {  //IRQ信号が0でなくなったとき
  1965:             MC68901.mfpRintRise ();  //IRQ終了
  1966:           }
  1967:         }
  1968:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  1969:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  1970:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  1971:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1972:               crtStereoscopicDrawRaster (crtScreenY);
  1973:             }
  1974:           }
  1975:           crtScreenY++;  //スクリーンY座標を1増やす
  1976:           crtDataY++;  //データY座標を1増やす
  1977:           TickerQueue.tkqAdd (crtTicker = NormalDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  1978:         } else {  //垂直空白期間開始ラスタのとき
  1979:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  1980:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  1981:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  1982:               crtStereoscopicDrawRaster (crtScreenY);
  1983:             }
  1984:           }
  1985:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  1986:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  1987:             crtClearFrames--;  //高速クリアカウンタを1減らす
  1988:           }
  1989:           if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  1990:             crtUpdateScreen ();  //スクリーンを更新する
  1991:             if (CRT_ENABLE_INTERMITTENT) {
  1992:               crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  1993:             }
  1994:           }
  1995:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  1996:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  1997:           }
  1998:           TickerQueue.tkqAdd (crtTicker = NormalDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  1999:         }
  2000:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2001:         //垂直空白期間開始ラスタではないとき
  2002:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2003:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2004:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2005:             crtStereoscopicDrawRaster (crtScreenY);
  2006:           }
  2007:         }
  2008:         crtScreenY++;  //スクリーンY座標を1増やす
  2009:         crtDataY++;  //データY座標を1増やす
  2010:         TickerQueue.tkqAdd (crtTicker = NormalDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  2011:       }
  2012:     }
  2013:   };
  2014:   //    (5)描画フレームの垂直映像期間の水平同期パルス
  2015:   public static final TickerQueue.Ticker NormalDrawDispSync = new TickerQueue.Ticker () {
  2016:     @Override protected void tick () {
  2017:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2018:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2019:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2020:         crtRasterStamp[crtDataY] = 0;
  2021:       }
  2022:       TickerQueue.tkqAdd (crtTicker = NormalDrawDispBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6)描画フレームの垂直映像期間の水平バックポーチ
  2023:     }
  2024:   };
  2025:   //    (6)描画フレームの垂直映像期間の水平バックポーチ
  2026:   public static final TickerQueue.Ticker NormalDrawDispBack = new TickerQueue.Ticker () {
  2027:     @Override protected void tick () {
  2028:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2029:       TickerQueue.tkqAdd (crtTicker = NormalDrawDispDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7)描画フレームの垂直映像期間の水平映像期間
  2030:     }
  2031:   };
  2032:   //    (7)描画フレームの垂直映像期間の水平映像期間
  2033:   public static final TickerQueue.Ticker NormalDrawDispDisp = new TickerQueue.Ticker () {
  2034:     @Override protected void tick () {
  2035:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  2036:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  2037:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  2038:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  2039:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2040:           crtStereoscopicDrawRaster (crtScreenY);
  2041:         }
  2042:         if (crtDirtyY0 < 0) {
  2043:           crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  2044:         }
  2045:         crtDirtyY1 = crtScreenY;
  2046:       }
  2047:       if (SpriteScreen.SPR_THREE_STEPS) {
  2048:         if (SpriteScreen.sprActive) {
  2049:           //ラスタ(dst=src)
  2050:           //表(dst)を表(dst+2)として再利用する
  2051:           SpriteScreen.sprStep1 (crtDataY + 2);  //表(dst+2)にスプライト(src+2)を並べる
  2052:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  2053:           SpriteScreen.sprStep2 (crtDataY + 1);  //表(dst+1)にバックグラウンド(src+1)を並べる
  2054:         }
  2055:       }
  2056:       TickerQueue.tkqAdd (crtTicker = NormalDrawDispFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4)描画フレームの垂直映像期間の水平フロントポーチ
  2057:     }
  2058:   };
  2059:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  2060:   public static final TickerQueue.Ticker NormalOmitIdleFront = new TickerQueue.Ticker () {
  2061:     @Override protected void tick () {
  2062:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2063:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2064:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2065:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2066:         }
  2067:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2068:           crtDoRasterCopy ();  //ラスタコピー実行
  2069:         }
  2070:         if (RasterBreakPoint.RBP_ON) {
  2071:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2072:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2073:           }
  2074:         }
  2075:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2076:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2077:           if (irq == 0) {  //IRQ信号が0になったとき
  2078:             if (RasterBreakPoint.RBP_ON) {
  2079:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2080:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2081:               }
  2082:             }
  2083:             MC68901.mfpRintFall ();  //IRQ開始
  2084:           } else {  //IRQ信号が0でなくなったとき
  2085:             MC68901.mfpRintRise ();  //IRQ終了
  2086:           }
  2087:         }
  2088:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2089:           TickerQueue.tkqAdd (crtTicker = NormalOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2090:         } else {  //垂直映像期間開始ラスタのとき
  2091:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2092:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2093:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2094:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2095:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2096:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2097:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2098:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2099:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2100:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2101:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2102:             crtR11TxYZeroLast = crtR11TxYZero;
  2103:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2104:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2105:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2106:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2107:             crtAllStamp += 2;
  2108:           }
  2109:           crtDataY = 0;  //データY座標を0で初期化
  2110:           if (crtClearStandby) {  //高速クリアの要求があるとき
  2111:             crtClearStandby = false;
  2112:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2113:           }
  2114:           if (SpriteScreen.SPR_THREE_STEPS) {
  2115:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  2116:             if (SpriteScreen.sprActive) {
  2117:               crtAllStamp += 2;
  2118:               //ラスタ(dst=-2,src=-2)
  2119:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2120:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2121:               //ラスタ(dst=-1,src=-1)
  2122:               //表(-1)を表(1)として再利用する
  2123:               SpriteScreen.sprStep1 (1);  //表(1)にスプライト(1)を並べる
  2124:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2125:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2126:             }
  2127:           }
  2128:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2129:             crtStereoscopicStart ();
  2130:           }
  2131:           TickerQueue.tkqAdd (crtTicker = NormalOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  2132:         }
  2133:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2134:         //垂直映像期間開始ラスタではないとき
  2135:         TickerQueue.tkqAdd (crtTicker = NormalOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2136:       }
  2137:     }
  2138:   };
  2139:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  2140:   public static final TickerQueue.Ticker NormalOmitIdleSync = new TickerQueue.Ticker () {
  2141:     @Override protected void tick () {
  2142:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2143:       TickerQueue.tkqAdd (crtTicker = NormalOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2144:     }
  2145:   };
  2146:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2147:   public static final TickerQueue.Ticker NormalOmitIdleBackDisp = new TickerQueue.Ticker () {
  2148:     @Override protected void tick () {
  2149:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2150:       TickerQueue.tkqAdd (crtTicker = NormalOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  2151:     }
  2152:   };
  2153:   //    (11)省略フレームの垂直映像期間の水平フロントポーチ
  2154:   public static final TickerQueue.Ticker NormalOmitDispFront = new TickerQueue.Ticker () {
  2155:     @Override protected void tick () {
  2156:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2157:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2158:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2159:           crtDoRasterCopy ();  //ラスタコピー実行
  2160:         }
  2161:         if (RasterBreakPoint.RBP_ON) {
  2162:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2163:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2164:           }
  2165:         }
  2166:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2167:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2168:           if (irq == 0) {  //IRQ信号が0になったとき
  2169:             if (RasterBreakPoint.RBP_ON) {
  2170:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2171:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2172:               }
  2173:             }
  2174:             MC68901.mfpRintFall ();  //IRQ開始
  2175:           } else {  //IRQ信号が0でなくなったとき
  2176:             MC68901.mfpRintRise ();  //IRQ終了
  2177:           }
  2178:         }
  2179:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2180:           TickerQueue.tkqAdd (crtTicker = NormalOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  2181:         } else {  //垂直空白期間開始ラスタのとき
  2182:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2183:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2184:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2185:           }
  2186:           if (CRT_ENABLE_INTERMITTENT) {
  2187:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  2188:           }
  2189:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2190:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2191:           }
  2192:           TickerQueue.tkqAdd (crtTicker = NormalOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2193:         }
  2194:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2195:         //垂直空白期間開始ラスタではないとき
  2196:         TickerQueue.tkqAdd (crtTicker = NormalOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  2197:       }
  2198:     }
  2199:   };
  2200:   //    (12)省略フレームの垂直映像期間の水平同期パルス
  2201:   public static final TickerQueue.Ticker NormalOmitDispSync = new TickerQueue.Ticker () {
  2202:     @Override protected void tick () {
  2203:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2204:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2205:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2206:         crtRasterStamp[crtDataY] = 0;
  2207:       }
  2208:       TickerQueue.tkqAdd (crtTicker = NormalOmitDispBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  2209:     }
  2210:   };
  2211:   //    (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  2212:   public static final TickerQueue.Ticker NormalOmitDispBackDisp = new TickerQueue.Ticker () {
  2213:     @Override protected void tick () {
  2214:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2215:       TickerQueue.tkqAdd (crtTicker = NormalOmitDispFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11)省略フレームの垂直映像期間の水平フロントポーチ
  2216:     }
  2217:   };
  2218: 
  2219:   //----------------------------------------------------------------
  2220:   //ラスタ2度読み
  2221:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  2222:   public static final TickerQueue.Ticker DuplicationStart = new TickerQueue.Ticker () {
  2223:     @Override protected void tick () {
  2224:       if (MC68901.mfpGpipHsync != 0) {
  2225:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2226:       }
  2227:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  2228:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2229:         crtDoRasterCopy ();  //ラスタコピー実行
  2230:       }
  2231:       if (RasterBreakPoint.RBP_ON) {
  2232:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2233:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2234:         }
  2235:       }
  2236:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2237:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2238:         if (irq == 0) {  //IRQ信号が0になったとき
  2239:           if (RasterBreakPoint.RBP_ON) {
  2240:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2241:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2242:             }
  2243:           }
  2244:           MC68901.mfpRintFall ();  //IRQ開始
  2245:         } else {  //IRQ信号が0でなくなったとき
  2246:           MC68901.mfpRintRise ();  //IRQ終了
  2247:         }
  2248:       }
  2249:       if (MC68901.mfpGpipVdisp != 0) {
  2250:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  2251:       }
  2252:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  2253:       if (CRT_ENABLE_INTERMITTENT) {
  2254:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  2255:       }
  2256:       if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2257:         crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2258:       }
  2259:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2260:     }
  2261:   };
  2262:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  2263:   public static final TickerQueue.Ticker DuplicationDrawIdleFront = new TickerQueue.Ticker () {
  2264:     @Override protected void tick () {
  2265:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2266:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2267:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2268:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2269:         }
  2270:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2271:           crtDoRasterCopy ();  //ラスタコピー実行
  2272:         }
  2273:         if (RasterBreakPoint.RBP_ON) {
  2274:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2275:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2276:           }
  2277:         }
  2278:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2279:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2280:           if (irq == 0) {  //IRQ信号が0になったとき
  2281:             if (RasterBreakPoint.RBP_ON) {
  2282:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2283:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2284:               }
  2285:             }
  2286:             MC68901.mfpRintFall ();  //IRQ開始
  2287:           } else {  //IRQ信号が0でなくなったとき
  2288:             MC68901.mfpRintRise ();  //IRQ終了
  2289:           }
  2290:         }
  2291:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2292:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2293:         } else {  //垂直映像期間開始ラスタのとき
  2294:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2295:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2296:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2297:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2298:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2299:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2300:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2301:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2302:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2303:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2304:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2305:             crtR11TxYZeroLast = crtR11TxYZero;
  2306:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2307:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2308:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2309:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2310:             crtAllStamp += 2;
  2311:           }
  2312:           crtDataY = 0;  //データY座標を0で初期化
  2313:           if (crtClearStandby) {  //高速クリアの要求があるとき
  2314:             crtClearStandby = false;
  2315:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2316:           }
  2317:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  2318:           crtDirtyY0 = -1;  //ダーティフラグをクリア
  2319:           if (SpriteScreen.SPR_THREE_STEPS) {
  2320:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  2321:             if (SpriteScreen.sprActive) {
  2322:               crtAllStamp += 2;
  2323:               //偶数ラスタ(dst=-2,src=-1)
  2324:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2325:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2326:               //奇数ラスタ(dst=-1,src=-1)
  2327:               //表(-1)を表(1)として再利用する
  2328:               SpriteScreen.sprStep1 (0);  //表(1)にスプライト(0)を並べる
  2329:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2330:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2331:             }
  2332:           }
  2333:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2334:             crtStereoscopicStart ();
  2335:           }
  2336:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2337:         }
  2338:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2339:         //垂直映像期間開始ラスタではないとき
  2340:         TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2341:       }
  2342:     }
  2343:   };
  2344:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  2345:   public static final TickerQueue.Ticker DuplicationDrawIdleSync = new TickerQueue.Ticker () {
  2346:     @Override protected void tick () {
  2347:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2348:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2349:     }
  2350:   };
  2351:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2352:   public static final TickerQueue.Ticker DuplicationDrawIdleBackDisp = new TickerQueue.Ticker () {
  2353:     @Override protected void tick () {
  2354:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2355:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  2356:     }
  2357:   };
  2358:   //    (4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2359:   public static final TickerQueue.Ticker DuplicationDrawDispEvenFront = new TickerQueue.Ticker () {
  2360:     @Override protected void tick () {
  2361:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2362:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2363:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2364:           crtDoRasterCopy ();  //ラスタコピー実行
  2365:         }
  2366:         if (RasterBreakPoint.RBP_ON) {
  2367:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2368:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2369:           }
  2370:         }
  2371:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2372:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2373:           if (irq == 0) {  //IRQ信号が0になったとき
  2374:             if (RasterBreakPoint.RBP_ON) {
  2375:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2376:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2377:               }
  2378:             }
  2379:             MC68901.mfpRintFall ();  //IRQ開始
  2380:           } else {  //IRQ信号が0でなくなったとき
  2381:             MC68901.mfpRintRise ();  //IRQ終了
  2382:           }
  2383:         }
  2384:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2385:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2386:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2387:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2388:               crtStereoscopicDrawRaster (crtScreenY);
  2389:             }
  2390:           }
  2391:           crtScreenY++;  //スクリーンY座標を1増やす
  2392:           crtDataY++;  //データY座標を1増やす
  2393:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2394:         } else {  //垂直空白期間開始ラスタのとき
  2395:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2396:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2397:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2398:               crtStereoscopicDrawRaster (crtScreenY);
  2399:             }
  2400:           }
  2401:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2402:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2403:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2404:           }
  2405:           if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  2406:             crtUpdateScreen ();  //スクリーンを更新する
  2407:             if (CRT_ENABLE_INTERMITTENT) {
  2408:               crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  2409:             }
  2410:           }
  2411:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2412:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2413:           }
  2414:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2415:         }
  2416:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2417:         //垂直空白期間開始ラスタではないとき
  2418:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2419:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2420:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2421:             crtStereoscopicDrawRaster (crtScreenY);
  2422:           }
  2423:         }
  2424:         crtScreenY++;  //スクリーンY座標を1増やす
  2425:         crtDataY++;  //データY座標を1増やす
  2426:         TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2427:       }
  2428:     }
  2429:   };
  2430:   //    (4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2431:   public static final TickerQueue.Ticker DuplicationDrawDispOddFront = new TickerQueue.Ticker () {
  2432:     @Override protected void tick () {
  2433:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2434:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2435:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2436:           crtDoRasterCopy ();  //ラスタコピー実行
  2437:         }
  2438:         if (RasterBreakPoint.RBP_ON) {
  2439:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2440:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2441:           }
  2442:         }
  2443:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2444:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2445:           if (irq == 0) {  //IRQ信号が0になったとき
  2446:             if (RasterBreakPoint.RBP_ON) {
  2447:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2448:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2449:               }
  2450:             }
  2451:             MC68901.mfpRintFall ();  //IRQ開始
  2452:           } else {  //IRQ信号が0でなくなったとき
  2453:             MC68901.mfpRintRise ();  //IRQ終了
  2454:           }
  2455:         }
  2456:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2457:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2458:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2459:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2460:               crtStereoscopicDrawRaster (crtScreenY);
  2461:             }
  2462:           }
  2463:           crtScreenY++;  //スクリーンY座標を1増やす
  2464:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2465:         } else {  //垂直空白期間開始ラスタのとき
  2466:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2467:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2468:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2469:               crtStereoscopicDrawRaster (crtScreenY);
  2470:             }
  2471:           }
  2472:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2473:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2474:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2475:           }
  2476:           if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  2477:             crtUpdateScreen ();  //スクリーンを更新する
  2478:             if (CRT_ENABLE_INTERMITTENT) {
  2479:               crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  2480:             }
  2481:           }
  2482:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2483:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2484:           }
  2485:           TickerQueue.tkqAdd (crtTicker = DuplicationDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2486:         }
  2487:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2488:         //垂直空白期間開始ラスタではないとき
  2489:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2490:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2491:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2492:             crtStereoscopicDrawRaster (crtScreenY);
  2493:           }
  2494:         }
  2495:         crtScreenY++;  //スクリーンY座標を1増やす
  2496:         TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2497:       }
  2498:     }
  2499:   };
  2500:   //    (5e)描画フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2501:   public static final TickerQueue.Ticker DuplicationDrawDispEvenSync = new TickerQueue.Ticker () {
  2502:     @Override protected void tick () {
  2503:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2504:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2505:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2506:         crtRasterStamp[crtDataY] = 0;
  2507:       }
  2508:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ
  2509:     }
  2510:   };
  2511:   //    (5o)描画フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2512:   public static final TickerQueue.Ticker DuplicationDrawDispOddSync = new TickerQueue.Ticker () {
  2513:     @Override protected void tick () {
  2514:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2515:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ
  2516:     }
  2517:   };
  2518:   //    (6e)描画フレームの垂直映像期間の偶数ラスタの水平バックポーチ
  2519:   public static final TickerQueue.Ticker DuplicationDrawDispEvenBack = new TickerQueue.Ticker () {
  2520:     @Override protected void tick () {
  2521:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2522:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間
  2523:     }
  2524:   };
  2525:   //    (6o)描画フレームの垂直映像期間の奇数ラスタの水平バックポーチ
  2526:   public static final TickerQueue.Ticker DuplicationDrawDispOddBack = new TickerQueue.Ticker () {
  2527:     @Override protected void tick () {
  2528:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2529:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間
  2530:     }
  2531:   };
  2532:   //    (7e)描画フレームの垂直映像期間の偶数ラスタの水平映像期間
  2533:   public static final TickerQueue.Ticker DuplicationDrawDispEvenDisp = new TickerQueue.Ticker () {
  2534:     @Override protected void tick () {
  2535:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  2536:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  2537:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  2538:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2539:           crtStereoscopicDrawRaster (crtScreenY);
  2540:         }
  2541:         if (crtDirtyY0 < 0) {
  2542:           crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  2543:         }
  2544:         if (CRT_INSTANT_DUPLICATION) {  //奇数ラスタは偶数ラスタのコピー
  2545:           crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  2546:           crtDirtyY1 = crtScreenY + 1;
  2547:           System.arraycopy (XEiJ.pnlBM, crtScreenY << XEiJ.PNL_BM_OFFSET_BITS, XEiJ.pnlBM, crtScreenY + 1 << XEiJ.PNL_BM_OFFSET_BITS, XEiJ.pnlScreenWidth);  //奇数ラスタ
  2548:         } else {  //奇数ラスタは偶数ラスタのコピーではない
  2549:           crtDirtyY1 = crtScreenY;  //偶数ラスタで終了する可能性があるので偶数ラスタでもcrtDirtyY1を更新する
  2550:         }
  2551:       }
  2552:       if (SpriteScreen.SPR_THREE_STEPS) {
  2553:         if (SpriteScreen.sprActive) {
  2554:           //偶数ラスタ(dst=src*2)
  2555:           //表(dst)を表(dst+2)として再利用する
  2556:           SpriteScreen.sprStep1 (crtDataY + 1);  //表(dst+2)にスプライト(src+1)を並べる
  2557:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  2558:           SpriteScreen.sprStep2 (crtDataY);  //表(dst+1)にバックグラウンド(src)を並べる
  2559:         }
  2560:       }
  2561:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispOddFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4o)描画フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2562:     }
  2563:   };
  2564:   //    (7o)描画フレームの垂直映像期間の奇数ラスタの水平映像期間
  2565:   public static final TickerQueue.Ticker DuplicationDrawDispOddDisp = new TickerQueue.Ticker () {
  2566:     @Override protected void tick () {
  2567:       if (!CRT_INSTANT_DUPLICATION) {  //奇数ラスタは偶数ラスタのコピーではない
  2568:         if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  2569:           crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  2570:           crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  2571:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  2572:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2573:             crtStereoscopicDrawRaster (crtScreenY);
  2574:           }
  2575:           if (crtDirtyY0 < 0) {
  2576:             crtDirtyY0 = crtScreenY;  //ダーティフラグをセット。奇数ラスタの直前でパレットやスクロール位置が変化したとき偶数ラスタでcrtDirtyY0が更新されていない可能性があるので奇数ラスタでもcrtDirtyY0を更新する
  2577:           }
  2578:           crtDirtyY1 = crtScreenY;
  2579:         }
  2580:       }
  2581:       if (SpriteScreen.SPR_THREE_STEPS) {
  2582:         if (SpriteScreen.sprActive) {
  2583:           //奇数ラスタ(dst=src*2+1)
  2584:           //表(dst)を表(dst+2)として再利用する
  2585:           SpriteScreen.sprStep1 (crtDataY + 1);  //表(dst+2)にスプライト(src+1)を並べる
  2586:           SpriteScreen.sprSwap ();  //表(dst+2)と裏(dst+1)を入れ換える
  2587:           SpriteScreen.sprStep2 (crtDataY + 1);  //表(dst+1)にバックグラウンド(src+1)を並べる
  2588:         }
  2589:       }
  2590:       TickerQueue.tkqAdd (crtTicker = DuplicationDrawDispEvenFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4e)描画フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2591:     }
  2592:   };
  2593:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  2594:   public static final TickerQueue.Ticker DuplicationOmitIdleFront = new TickerQueue.Ticker () {
  2595:     @Override protected void tick () {
  2596:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2597:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2598:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2599:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2600:         }
  2601:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2602:           crtDoRasterCopy ();  //ラスタコピー実行
  2603:         }
  2604:         if (RasterBreakPoint.RBP_ON) {
  2605:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2606:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2607:           }
  2608:         }
  2609:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2610:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2611:           if (irq == 0) {  //IRQ信号が0になったとき
  2612:             if (RasterBreakPoint.RBP_ON) {
  2613:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2614:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2615:               }
  2616:             }
  2617:             MC68901.mfpRintFall ();  //IRQ開始
  2618:           } else {  //IRQ信号が0でなくなったとき
  2619:             MC68901.mfpRintRise ();  //IRQ終了
  2620:           }
  2621:         }
  2622:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2623:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さDuplicationDrawDispEvenFront
  2624:         } else {  //垂直映像期間開始ラスタのとき
  2625:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2626:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2627:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2628:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2629:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2630:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2631:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2632:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2633:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2634:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2635:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2636:             crtR11TxYZeroLast = crtR11TxYZero;
  2637:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2638:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2639:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2640:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2641:             crtAllStamp += 2;
  2642:           }
  2643:           crtDataY = 0;  //データY座標を0で初期化
  2644:           if (crtClearStandby) {  //高速クリアの要求があるとき
  2645:             crtClearStandby = false;
  2646:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  2647:           }
  2648:           if (SpriteScreen.SPR_THREE_STEPS) {
  2649:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  2650:             if (SpriteScreen.sprActive) {
  2651:               crtAllStamp += 2;
  2652:               //偶数ラスタ(dst=-2,src=-1)
  2653:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2654:               SpriteScreen.sprSwap ();  //表(0)と裏(-1)を入れ換える
  2655:               //奇数ラスタ(dst=-1,src=-1)
  2656:               //表(-1)を表(1)として再利用する
  2657:               SpriteScreen.sprStep1 (0);  //表(1)にスプライト(0)を並べる
  2658:               SpriteScreen.sprSwap ();  //表(1)と裏(0)を入れ換える
  2659:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2660:             }
  2661:           }
  2662:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2663:             crtStereoscopicStart ();
  2664:           }
  2665:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2666:         }
  2667:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2668:         //垂直映像期間開始ラスタではないとき
  2669:         TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2670:       }
  2671:     }
  2672:   };
  2673:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  2674:   public static final TickerQueue.Ticker DuplicationOmitIdleSync = new TickerQueue.Ticker () {
  2675:     @Override protected void tick () {
  2676:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2677:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2678:     }
  2679:   };
  2680:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  2681:   public static final TickerQueue.Ticker DuplicationOmitIdleBackDisp = new TickerQueue.Ticker () {
  2682:     @Override protected void tick () {
  2683:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2684:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  2685:     }
  2686:   };
  2687:   //    (11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2688:   public static final TickerQueue.Ticker DuplicationOmitDispEvenFront = new TickerQueue.Ticker () {
  2689:     @Override protected void tick () {
  2690:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2691:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2692:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2693:           crtDoRasterCopy ();  //ラスタコピー実行
  2694:         }
  2695:         if (RasterBreakPoint.RBP_ON) {
  2696:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2697:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2698:           }
  2699:         }
  2700:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2701:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2702:           if (irq == 0) {  //IRQ信号が0になったとき
  2703:             if (RasterBreakPoint.RBP_ON) {
  2704:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2705:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2706:               }
  2707:             }
  2708:             MC68901.mfpRintFall ();  //IRQ開始
  2709:           } else {  //IRQ信号が0でなくなったとき
  2710:             MC68901.mfpRintRise ();  //IRQ終了
  2711:           }
  2712:         }
  2713:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2714:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2715:         } else {  //垂直空白期間開始ラスタのとき
  2716:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2717:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2718:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2719:           }
  2720:           if (CRT_ENABLE_INTERMITTENT) {
  2721:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  2722:           }
  2723:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2724:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2725:           }
  2726:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2727:         }
  2728:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2729:         //垂直空白期間開始ラスタではないとき
  2730:         TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2731:       }
  2732:     }
  2733:   };
  2734:   //    (11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2735:   public static final TickerQueue.Ticker DuplicationOmitDispOddFront = new TickerQueue.Ticker () {
  2736:     @Override protected void tick () {
  2737:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2738:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2739:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2740:           crtDoRasterCopy ();  //ラスタコピー実行
  2741:         }
  2742:         if (RasterBreakPoint.RBP_ON) {
  2743:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2744:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2745:           }
  2746:         }
  2747:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2748:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2749:           if (irq == 0) {  //IRQ信号が0になったとき
  2750:             if (RasterBreakPoint.RBP_ON) {
  2751:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2752:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2753:               }
  2754:             }
  2755:             MC68901.mfpRintFall ();  //IRQ開始
  2756:           } else {  //IRQ信号が0でなくなったとき
  2757:             MC68901.mfpRintRise ();  //IRQ終了
  2758:           }
  2759:         }
  2760:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2761:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2762:         } else {  //垂直空白期間開始ラスタのとき
  2763:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2764:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2765:             crtClearFrames--;  //高速クリアカウンタを1減らす
  2766:           }
  2767:           if (CRT_ENABLE_INTERMITTENT) {
  2768:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  2769:           }
  2770:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2771:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2772:           }
  2773:           TickerQueue.tkqAdd (crtTicker = DuplicationOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  2774:         }
  2775:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2776:         //垂直空白期間開始ラスタではないとき
  2777:         TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2778:       }
  2779:     }
  2780:   };
  2781:   //    (12e)省略フレームの垂直映像期間の偶数ラスタの水平同期パルス
  2782:   public static final TickerQueue.Ticker DuplicationOmitDispEvenSync = new TickerQueue.Ticker () {
  2783:     @Override protected void tick () {
  2784:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2785:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2786:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  2787:         crtRasterStamp[crtDataY] = 0;
  2788:       }
  2789:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間
  2790:     }
  2791:   };
  2792:   //    (12o)省略フレームの垂直映像期間の奇数ラスタの水平同期パルス
  2793:   public static final TickerQueue.Ticker DuplicationOmitDispOddSync = new TickerQueue.Ticker () {
  2794:     @Override protected void tick () {
  2795:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2796:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間
  2797:     }
  2798:   };
  2799:   //    (13e)省略フレームの垂直映像期間の偶数ラスタの水平バックポーチと水平映像期間
  2800:   public static final TickerQueue.Ticker DuplicationOmitDispEvenBackDisp = new TickerQueue.Ticker () {
  2801:     @Override protected void tick () {
  2802:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2803:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispOddFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11o)省略フレームの垂直映像期間の奇数ラスタの水平フロントポーチ
  2804:     }
  2805:   };
  2806:   //    (13o)省略フレームの垂直映像期間の奇数ラスタの水平バックポーチと水平映像期間
  2807:   public static final TickerQueue.Ticker DuplicationOmitDispOddBackDisp = new TickerQueue.Ticker () {
  2808:     @Override protected void tick () {
  2809:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2810:       TickerQueue.tkqAdd (crtTicker = DuplicationOmitDispEvenFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11e)省略フレームの垂直映像期間の偶数ラスタの水平フロントポーチ
  2811:     }
  2812:   };
  2813: 
  2814:   //----------------------------------------------------------------
  2815:   //インターレース
  2816:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  2817:   public static final TickerQueue.Ticker InterlaceStart = new TickerQueue.Ticker () {
  2818:     @Override protected void tick () {
  2819:       if (MC68901.mfpGpipHsync != 0) {
  2820:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2821:       }
  2822:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  2823:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2824:         crtDoRasterCopy ();  //ラスタコピー実行
  2825:       }
  2826:       if (RasterBreakPoint.RBP_ON) {
  2827:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2828:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2829:         }
  2830:       }
  2831:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2832:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2833:         if (irq == 0) {  //IRQ信号が0になったとき
  2834:           if (RasterBreakPoint.RBP_ON) {
  2835:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2836:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2837:             }
  2838:           }
  2839:           MC68901.mfpRintFall ();  //IRQ開始
  2840:         } else {  //IRQ信号が0でなくなったとき
  2841:           MC68901.mfpRintRise ();  //IRQ終了
  2842:         }
  2843:       }
  2844:       if (MC68901.mfpGpipVdisp != 0) {
  2845:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  2846:       }
  2847:       crtFrameParity = 0;  //フレームパリティを0で初期化
  2848:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  2849:       if (CRT_ENABLE_INTERMITTENT) {
  2850:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  2851:       }
  2852:       if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  2853:         crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  2854:       }
  2855:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2856:     }
  2857:   };
  2858:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  2859:   public static final TickerQueue.Ticker InterlaceDrawIdleFront = new TickerQueue.Ticker () {
  2860:     @Override protected void tick () {
  2861:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2862:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2863:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  2864:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  2865:         }
  2866:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2867:           crtDoRasterCopy ();  //ラスタコピー実行
  2868:         }
  2869:         if (RasterBreakPoint.RBP_ON) {
  2870:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2871:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2872:           }
  2873:         }
  2874:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2875:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2876:           if (irq == 0) {  //IRQ信号が0になったとき
  2877:             if (RasterBreakPoint.RBP_ON) {
  2878:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2879:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2880:               }
  2881:             }
  2882:             MC68901.mfpRintFall ();  //IRQ開始
  2883:           } else {  //IRQ信号が0でなくなったとき
  2884:             MC68901.mfpRintRise ();  //IRQ終了
  2885:           }
  2886:         }
  2887:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  2888:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2889:         } else {  //垂直映像期間開始ラスタのとき
  2890:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  2891:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  2892:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  2893:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  2894:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  2895:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  2896:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  2897:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  2898:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  2899:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  2900:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  2901:             crtR11TxYZeroLast = crtR11TxYZero;
  2902:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  2903:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  2904:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  2905:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  2906:             crtAllStamp += 2;
  2907:           }
  2908:           crtDataY = crtFrameParity;  //データY座標をフレームパリティで初期化
  2909:           if (crtClearStandby) {  //高速クリアの要求があるとき
  2910:             crtClearStandby = false;
  2911:             crtClearFrames = 2;  //高速クリアカウンタを2で初期化
  2912:           }
  2913:           crtScreenY = crtFrameParity;  //スクリーンY座標をフレームパリティで初期化
  2914:           crtDirtyY0 = -1;  //ダーティフラグをクリア
  2915:           if (SpriteScreen.SPR_THREE_STEPS) {
  2916:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  2917:             if (SpriteScreen.sprActive) {
  2918:               crtAllStamp += 2;
  2919:               //ラスタ(dst=-4,src=-4)
  2920:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  2921:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  2922:               //ラスタ(dst=-2,src=-2)
  2923:               //表(-2)を表(2)として再利用する
  2924:               SpriteScreen.sprStep1 (2);  //表(2)にスプライト(2)を並べる
  2925:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  2926:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  2927:             }
  2928:           }
  2929:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2930:             crtStereoscopicStart ();
  2931:           }
  2932:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  2933:         }
  2934:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  2935:         //垂直映像期間開始ラスタではないとき
  2936:         TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  2937:       }
  2938:     }
  2939:   };
  2940:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  2941:   public static final TickerQueue.Ticker InterlaceDrawIdleSync = new TickerQueue.Ticker () {
  2942:     @Override protected void tick () {
  2943:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  2944:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2945:     }
  2946:   };
  2947:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  2948:   public static final TickerQueue.Ticker InterlaceDrawIdleBackDisp = new TickerQueue.Ticker () {
  2949:     @Override protected void tick () {
  2950:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  2951:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  2952:     }
  2953:   };
  2954:   //    (4)描画フレームの垂直映像期間の水平フロントポーチ
  2955:   public static final TickerQueue.Ticker InterlaceDrawDispFront = new TickerQueue.Ticker () {
  2956:     @Override protected void tick () {
  2957:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  2958:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  2959:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  2960:           crtDoRasterCopy ();  //ラスタコピー実行
  2961:         }
  2962:         if (RasterBreakPoint.RBP_ON) {
  2963:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  2964:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2965:           }
  2966:         }
  2967:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  2968:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  2969:           if (irq == 0) {  //IRQ信号が0になったとき
  2970:             if (RasterBreakPoint.RBP_ON) {
  2971:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  2972:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  2973:               }
  2974:             }
  2975:             MC68901.mfpRintFall ();  //IRQ開始
  2976:           } else {  //IRQ信号が0でなくなったとき
  2977:             MC68901.mfpRintRise ();  //IRQ終了
  2978:           }
  2979:         }
  2980:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  2981:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2982:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2983:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2984:               crtStereoscopicDrawRaster (crtScreenY);
  2985:             }
  2986:           }
  2987:           crtScreenY += 2;  //スクリーンY座標を2増やす
  2988:           crtDataY += 2;  //データY座標を2増やす
  2989:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  2990:         } else {  //垂直空白期間開始ラスタのとき
  2991:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  2992:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  2993:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  2994:               crtStereoscopicDrawRaster (crtScreenY);
  2995:             }
  2996:           }
  2997:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  2998:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  2999:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3000:           }
  3001:           if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  3002:             crtUpdateScreen ();  //スクリーンを更新する
  3003:             if (CRT_ENABLE_INTERMITTENT) {
  3004:               crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  3005:             }
  3006:           }
  3007:           crtFrameParity ^= 1;  //フレームパリティを反転
  3008:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3009:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3010:           }
  3011:           TickerQueue.tkqAdd (crtTicker = InterlaceDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3012:         }
  3013:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3014:         //垂直空白期間開始ラスタではないとき
  3015:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3016:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3017:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3018:             crtStereoscopicDrawRaster (crtScreenY);
  3019:           }
  3020:         }
  3021:         crtScreenY += 2;  //スクリーンY座標を2増やす
  3022:         crtDataY += 2;  //データY座標を2増やす
  3023:         TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3024:       }
  3025:     }
  3026:   };
  3027:   //    (5)描画フレームの垂直映像期間の水平同期パルス
  3028:   public static final TickerQueue.Ticker InterlaceDrawDispSync = new TickerQueue.Ticker () {
  3029:     @Override protected void tick () {
  3030:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3031:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3032:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3033:         crtRasterStamp[crtDataY] = 0;
  3034:       }
  3035:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6)描画フレームの垂直映像期間の水平バックポーチ
  3036:     }
  3037:   };
  3038:   //    (6)描画フレームの垂直映像期間の水平バックポーチ
  3039:   public static final TickerQueue.Ticker InterlaceDrawDispBack = new TickerQueue.Ticker () {
  3040:     @Override protected void tick () {
  3041:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3042:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7)描画フレームの垂直映像期間の水平映像期間
  3043:     }
  3044:   };
  3045:   //    (7)描画フレームの垂直映像期間の水平映像期間
  3046:   public static final TickerQueue.Ticker InterlaceDrawDispDisp = new TickerQueue.Ticker () {
  3047:     @Override protected void tick () {
  3048:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  3049:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  3050:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  3051:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  3052:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3053:           crtStereoscopicDrawRaster (crtScreenY);
  3054:         }
  3055:         if (crtDirtyY0 < 0) {
  3056:           crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  3057:         }
  3058:         crtDirtyY1 = crtScreenY;
  3059:       }
  3060:       if (SpriteScreen.SPR_THREE_STEPS) {
  3061:         if (SpriteScreen.sprActive) {
  3062:           //ラスタ(dst=src)
  3063:           //表(dst)を表(dst+4)として再利用する
  3064:           SpriteScreen.sprStep1 (crtDataY + 4);  //表(dst+4)にスプライト(src+4)を並べる
  3065:           SpriteScreen.sprSwap ();  //表(dst+4)と裏(dst+2)を入れ換える
  3066:           SpriteScreen.sprStep2 (crtDataY + 2);  //表(dst+2)にバックグラウンド(src+2)を並べる
  3067:         }
  3068:       }
  3069:       TickerQueue.tkqAdd (crtTicker = InterlaceDrawDispFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4)描画フレームの垂直映像期間の水平フロントポーチ
  3070:     }
  3071:   };
  3072:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  3073:   public static final TickerQueue.Ticker InterlaceOmitIdleFront = new TickerQueue.Ticker () {
  3074:     @Override protected void tick () {
  3075:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3076:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3077:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3078:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3079:         }
  3080:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3081:           crtDoRasterCopy ();  //ラスタコピー実行
  3082:         }
  3083:         if (RasterBreakPoint.RBP_ON) {
  3084:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3085:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3086:           }
  3087:         }
  3088:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3089:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3090:           if (irq == 0) {  //IRQ信号が0になったとき
  3091:             if (RasterBreakPoint.RBP_ON) {
  3092:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3093:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3094:               }
  3095:             }
  3096:             MC68901.mfpRintFall ();  //IRQ開始
  3097:           } else {  //IRQ信号が0でなくなったとき
  3098:             MC68901.mfpRintRise ();  //IRQ終了
  3099:           }
  3100:         }
  3101:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3102:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3103:         } else {  //垂直映像期間開始ラスタのとき
  3104:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3105:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3106:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3107:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3108:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3109:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3110:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3111:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3112:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3113:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3114:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3115:             crtR11TxYZeroLast = crtR11TxYZero;
  3116:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3117:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3118:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3119:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3120:             crtAllStamp += 2;
  3121:           }
  3122:           crtDataY = crtFrameParity;  //データY座標をフレームパリティで初期化
  3123:           if (crtClearStandby) {  //高速クリアの要求があるとき
  3124:             crtClearStandby = false;
  3125:             crtClearFrames = 2;  //高速クリアカウンタを2で初期化
  3126:           }
  3127:           if (SpriteScreen.SPR_THREE_STEPS) {
  3128:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  3129:             if (SpriteScreen.sprActive) {
  3130:               crtAllStamp += 2;
  3131:               //ラスタ(dst=-4,src=-4)
  3132:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3133:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3134:               //ラスタ(dst=-2,src=-2)
  3135:               //表(-2)を表(2)として再利用する
  3136:               SpriteScreen.sprStep1 (2);  //表(2)にスプライト(2)を並べる
  3137:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3138:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3139:             }
  3140:           }
  3141:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3142:             crtStereoscopicStart ();
  3143:           }
  3144:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3145:         }
  3146:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3147:         //垂直映像期間開始ラスタではないとき
  3148:         TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3149:       }
  3150:     }
  3151:   };
  3152:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  3153:   public static final TickerQueue.Ticker InterlaceOmitIdleSync = new TickerQueue.Ticker () {
  3154:     @Override protected void tick () {
  3155:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3156:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3157:     }
  3158:   };
  3159:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3160:   public static final TickerQueue.Ticker InterlaceOmitIdleBackDisp = new TickerQueue.Ticker () {
  3161:     @Override protected void tick () {
  3162:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3163:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  3164:     }
  3165:   };
  3166:   //    (11)省略フレームの垂直映像期間の水平フロントポーチ
  3167:   public static final TickerQueue.Ticker InterlaceOmitDispFront = new TickerQueue.Ticker () {
  3168:     @Override protected void tick () {
  3169:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3170:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3171:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3172:           crtDoRasterCopy ();  //ラスタコピー実行
  3173:         }
  3174:         if (RasterBreakPoint.RBP_ON) {
  3175:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3176:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3177:           }
  3178:         }
  3179:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3180:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3181:           if (irq == 0) {  //IRQ信号が0になったとき
  3182:             if (RasterBreakPoint.RBP_ON) {
  3183:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3184:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3185:               }
  3186:             }
  3187:             MC68901.mfpRintFall ();  //IRQ開始
  3188:           } else {  //IRQ信号が0でなくなったとき
  3189:             MC68901.mfpRintRise ();  //IRQ終了
  3190:           }
  3191:         }
  3192:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3193:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3194:         } else {  //垂直空白期間開始ラスタのとき
  3195:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3196:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3197:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3198:           }
  3199:           if (CRT_ENABLE_INTERMITTENT) {
  3200:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  3201:           }
  3202:           crtFrameParity ^= 1;  //フレームパリティを反転
  3203:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3204:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3205:           }
  3206:           TickerQueue.tkqAdd (crtTicker = InterlaceOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3207:         }
  3208:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3209:         //垂直空白期間開始ラスタではないとき
  3210:         TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3211:       }
  3212:     }
  3213:   };
  3214:   //    (12)省略フレームの垂直映像期間の水平同期パルス
  3215:   public static final TickerQueue.Ticker InterlaceOmitDispSync = new TickerQueue.Ticker () {
  3216:     @Override protected void tick () {
  3217:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3218:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3219:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3220:         crtRasterStamp[crtDataY] = 0;
  3221:       }
  3222:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  3223:     }
  3224:   };
  3225:   //    (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  3226:   public static final TickerQueue.Ticker InterlaceOmitDispBackDisp = new TickerQueue.Ticker () {
  3227:     @Override protected void tick () {
  3228:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3229:       TickerQueue.tkqAdd (crtTicker = InterlaceOmitDispFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11)省略フレームの垂直映像期間の水平フロントポーチ
  3230:     }
  3231:   };
  3232: 
  3233:   //----------------------------------------------------------------
  3234:   //スリット
  3235:   //    (0)開始(描画フレームの垂直空白期間開始ラスタの水平フロントポーチ)
  3236:   public static final TickerQueue.Ticker SlitStart = new TickerQueue.Ticker () {
  3237:     @Override protected void tick () {
  3238:       if (MC68901.mfpGpipHsync != 0) {
  3239:         MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3240:       }
  3241:       int n = crtRasterNumber = crtVIdleStart;  //ラスタ番号は垂直空白期間開始ラスタ
  3242:       if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3243:         crtDoRasterCopy ();  //ラスタコピー実行
  3244:       }
  3245:       if (RasterBreakPoint.RBP_ON) {
  3246:         if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3247:           RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3248:         }
  3249:       }
  3250:       int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3251:       if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3252:         if (irq == 0) {  //IRQ信号が0になったとき
  3253:           if (RasterBreakPoint.RBP_ON) {
  3254:             if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3255:               RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3256:             }
  3257:           }
  3258:           MC68901.mfpRintFall ();  //IRQ開始
  3259:         } else {  //IRQ信号が0でなくなったとき
  3260:           MC68901.mfpRintRise ();  //IRQ終了
  3261:         }
  3262:       }
  3263:       if (MC68901.mfpGpipVdisp != 0) {
  3264:         MC68901.mfpVdispFall ();  //垂直映像期間終了
  3265:       }
  3266:       crtClearFrames = 0;  //高速クリアカウンタを0で初期化
  3267:       if (CRT_ENABLE_INTERMITTENT) {
  3268:         crtIntermittentCounter = 0;  //間欠カウンタを0で初期化(描画フレーム)
  3269:       }
  3270:       if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3271:         crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3272:       }
  3273:       TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3274:     }
  3275:   };
  3276:   //    (1)描画フレームの垂直空白期間の水平フロントポーチ
  3277:   public static final TickerQueue.Ticker SlitDrawIdleFront = new TickerQueue.Ticker () {
  3278:     @Override protected void tick () {
  3279:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3280:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3281:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3282:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3283:         }
  3284:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3285:           crtDoRasterCopy ();  //ラスタコピー実行
  3286:         }
  3287:         if (RasterBreakPoint.RBP_ON) {
  3288:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3289:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3290:           }
  3291:         }
  3292:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3293:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3294:           if (irq == 0) {  //IRQ信号が0になったとき
  3295:             if (RasterBreakPoint.RBP_ON) {
  3296:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3297:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3298:               }
  3299:             }
  3300:             MC68901.mfpRintFall ();  //IRQ開始
  3301:           } else {  //IRQ信号が0でなくなったとき
  3302:             MC68901.mfpRintRise ();  //IRQ終了
  3303:           }
  3304:         }
  3305:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3306:           TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3307:         } else {  //垂直映像期間開始ラスタのとき
  3308:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3309:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3310:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3311:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3312:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3313:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3314:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3315:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3316:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3317:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3318:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3319:             crtR11TxYZeroLast = crtR11TxYZero;
  3320:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3321:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3322:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3323:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3324:             crtAllStamp += 2;
  3325:           }
  3326:           crtDataY = 0;  //データY座標を0で初期化
  3327:           if (crtClearStandby) {  //高速クリアの要求があるとき
  3328:             crtClearStandby = false;
  3329:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  3330:           }
  3331:           crtScreenY = 0;  //スクリーンY座標を0で初期化
  3332:           crtDirtyY0 = -1;  //ダーティフラグをクリア
  3333:           if (SpriteScreen.SPR_THREE_STEPS) {
  3334:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  3335:             if (SpriteScreen.sprActive) {
  3336:               crtAllStamp += 2;
  3337:               //ラスタ(dst=-4,src=-2)
  3338:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3339:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3340:               //ラスタ(dst=-2,src=-1)
  3341:               //表(-2)を表(2)として再利用する
  3342:               SpriteScreen.sprStep1 (1);  //表(2)にスプライト(1)を並べる
  3343:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3344:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3345:             }
  3346:           }
  3347:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3348:             crtStereoscopicStart ();
  3349:           }
  3350:           TickerQueue.tkqAdd (crtTicker = SlitDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3351:         }
  3352:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3353:         //垂直映像期間開始ラスタではないとき
  3354:         TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3355:       }
  3356:     }
  3357:   };
  3358:   //    (2)描画フレームの垂直空白期間の水平同期パルス
  3359:   public static final TickerQueue.Ticker SlitDrawIdleSync = new TickerQueue.Ticker () {
  3360:     @Override protected void tick () {
  3361:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3362:       TickerQueue.tkqAdd (crtTicker = SlitDrawIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  3363:     }
  3364:   };
  3365:   //    (3)描画フレームの垂直空白期間の水平バックポーチと水平映像期間
  3366:   public static final TickerQueue.Ticker SlitDrawIdleBackDisp = new TickerQueue.Ticker () {
  3367:     @Override protected void tick () {
  3368:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3369:       TickerQueue.tkqAdd (crtTicker = SlitDrawIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(1)描画フレームの垂直空白期間の水平フロントポーチ
  3370:     }
  3371:   };
  3372:   //    (4)描画フレームの垂直映像期間の水平フロントポーチ
  3373:   public static final TickerQueue.Ticker SlitDrawDispFront = new TickerQueue.Ticker () {
  3374:     @Override protected void tick () {
  3375:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3376:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3377:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3378:           crtDoRasterCopy ();  //ラスタコピー実行
  3379:         }
  3380:         if (RasterBreakPoint.RBP_ON) {
  3381:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3382:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3383:           }
  3384:         }
  3385:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3386:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3387:           if (irq == 0) {  //IRQ信号が0になったとき
  3388:             if (RasterBreakPoint.RBP_ON) {
  3389:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3390:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3391:               }
  3392:             }
  3393:             MC68901.mfpRintFall ();  //IRQ開始
  3394:           } else {  //IRQ信号が0でなくなったとき
  3395:             MC68901.mfpRintRise ();  //IRQ終了
  3396:           }
  3397:         }
  3398:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3399:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3400:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3401:             crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3402:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3403:               crtStereoscopicDrawRaster (crtScreenY);
  3404:               crtStereoscopicDrawRaster (crtScreenY + 1);
  3405:             }
  3406:           }
  3407:           crtScreenY += 2;  //スクリーンY座標を2増やす
  3408:           crtDataY++;  //データY座標を1増やす
  3409:           TickerQueue.tkqAdd (crtTicker = SlitDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3410:         } else {  //垂直空白期間開始ラスタのとき
  3411:           if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3412:             VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3413:             crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3414:             if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3415:               crtStereoscopicDrawRaster (crtScreenY);
  3416:               crtStereoscopicDrawRaster (crtScreenY + 1);
  3417:             }
  3418:           }
  3419:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3420:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3421:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3422:           }
  3423:           if (crtDirtyY0 >= 0) {  //ダーティフラグがセットされているとき
  3424:             crtUpdateScreen ();  //スクリーンを更新する
  3425:             if (CRT_ENABLE_INTERMITTENT) {
  3426:               crtIntermittentCounter = crtIntermittentInterval;  //間欠カウンタを間欠間隔に戻す
  3427:             }
  3428:           }
  3429:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3430:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3431:           }
  3432:           TickerQueue.tkqAdd (crtTicker = SlitDrawIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(2)描画フレームの垂直空白期間の水平同期パルス
  3433:         }
  3434:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3435:         //垂直空白期間開始ラスタではないとき
  3436:         if (crtBeginningAllStamp != crtAllStamp) {  //全再描画スタンプが水平映像期間開始時の全再描画スタンプと異なるとき
  3437:           VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, true);  //データY座標からスクリーンY座標へ描画
  3438:           crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3439:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3440:             crtStereoscopicDrawRaster (crtScreenY);
  3441:             crtStereoscopicDrawRaster (crtScreenY + 1);
  3442:           }
  3443:         }
  3444:         crtScreenY += 2;  //スクリーンY座標を2増やす
  3445:         crtDataY++;  //データY座標を1増やす
  3446:         TickerQueue.tkqAdd (crtTicker = SlitDrawDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(5)描画フレームの垂直映像期間の水平同期パルス
  3447:       }
  3448:     }
  3449:   };
  3450:   //    (5)描画フレームの垂直映像期間の水平同期パルス
  3451:   public static final TickerQueue.Ticker SlitDrawDispSync = new TickerQueue.Ticker () {
  3452:     @Override protected void tick () {
  3453:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3454:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3455:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3456:         crtRasterStamp[crtDataY] = 0;
  3457:       }
  3458:       TickerQueue.tkqAdd (crtTicker = SlitDrawDispBack, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(6)描画フレームの垂直映像期間の水平バックポーチ
  3459:     }
  3460:   };
  3461:   //    (6)描画フレームの垂直映像期間の水平バックポーチ
  3462:   public static final TickerQueue.Ticker SlitDrawDispBack = new TickerQueue.Ticker () {
  3463:     @Override protected void tick () {
  3464:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3465:       TickerQueue.tkqAdd (crtTicker = SlitDrawDispDisp, crtClock += crtHBackLength);  //+水平バックポーチの長さ→(7)描画フレームの垂直映像期間の水平映像期間
  3466:     }
  3467:   };
  3468:   //    (7)描画フレームの垂直映像期間の水平映像期間
  3469:   public static final TickerQueue.Ticker SlitDrawDispDisp = new TickerQueue.Ticker () {
  3470:     @Override protected void tick () {
  3471:       if (crtRasterStamp[crtDataY] != crtAllStamp) {  //ラスタの更新フラグがセットされているとき
  3472:         crtRasterStamp[crtDataY] = crtAllStamp;  //ラスタの更新フラグをクリア
  3473:         crtBeginningAllStamp = crtAllStamp;  //全再描画スタンプを水平映像期間開始時の全再描画スタンプにコピー
  3474:         VideoController.vcnMode.drawRaster (crtDataY, crtScreenY, false);  //データY座標からスクリーンY座標へ描画
  3475:         crtScanlineEffect.drawRaster (crtScreenY + 1);  //走査線エフェクト
  3476:         if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3477:           crtStereoscopicDrawRaster (crtScreenY);
  3478:           crtStereoscopicDrawRaster (crtScreenY + 1);
  3479:         }
  3480:         if (crtDirtyY0 < 0) {
  3481:           crtDirtyY0 = crtScreenY;  //ダーティフラグをセット
  3482:         }
  3483:         crtDirtyY1 = crtScreenY + 1;
  3484:       }
  3485:       if (SpriteScreen.SPR_THREE_STEPS) {
  3486:         if (SpriteScreen.sprActive) {
  3487:           //ラスタ(dst=src*2)
  3488:           //表(dst)を表(dst+4)として再利用する
  3489:           SpriteScreen.sprStep1 (crtDataY + 2);  //表(dst+4)にスプライト(src+2)を並べる
  3490:           SpriteScreen.sprSwap ();  //表(dst+4)と裏(dst+2)を入れ換える
  3491:           SpriteScreen.sprStep2 (crtDataY + 1);  //表(dst+2)にバックグラウンド(src+1)を並べる
  3492:         }
  3493:       }
  3494:       TickerQueue.tkqAdd (crtTicker = SlitDrawDispFront, crtClock += crtHDispLength);  //+水平水平映像期間の長さ→(4)描画フレームの垂直映像期間の水平フロントポーチ
  3495:     }
  3496:   };
  3497:   //    (8)省略フレームの垂直空白期間の水平フロントポーチ
  3498:   public static final TickerQueue.Ticker SlitOmitIdleFront = new TickerQueue.Ticker () {
  3499:     @Override protected void tick () {
  3500:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3501:       if (!CRT_RASTER_HASH_ON || crtRasterHashIdle << n < CRT_RASTER_HASH_ZERO) {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3502:         if (crtR04VFrontEndCurr < n) {  //ラスタ番号が垂直フロントポーチ終了ラスタを超えたとき
  3503:           n = crtRasterNumber = 0;  //ラスタ番号を0に戻す(垂直空白期間は0を跨ぐ)
  3504:         }
  3505:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3506:           crtDoRasterCopy ();  //ラスタコピー実行
  3507:         }
  3508:         if (RasterBreakPoint.RBP_ON) {
  3509:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3510:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3511:           }
  3512:         }
  3513:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3514:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3515:           if (irq == 0) {  //IRQ信号が0になったとき
  3516:             if (RasterBreakPoint.RBP_ON) {
  3517:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3518:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3519:               }
  3520:             }
  3521:             MC68901.mfpRintFall ();  //IRQ開始
  3522:           } else {  //IRQ信号が0でなくなったとき
  3523:             MC68901.mfpRintRise ();  //IRQ終了
  3524:           }
  3525:         }
  3526:         if (n != crtVDispStart) {  //垂直映像期間開始ラスタではないとき
  3527:           TickerQueue.tkqAdd (crtTicker = SlitOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3528:         } else {  //垂直映像期間開始ラスタのとき
  3529:           MC68901.mfpVdispRise ();  //垂直映像期間開始
  3530:           crtR11TxYZero = crtR11TxYCurr;  //テキストY方向スクロールを保存
  3531:           crtR13GrYZero[0] = crtR13GrYCurr[0];  //グラフィックY方向スクロールを保存
  3532:           crtR13GrYZero[1] = crtR13GrYCurr[1];
  3533:           crtR13GrYZero[2] = crtR13GrYCurr[2];
  3534:           crtR13GrYZero[3] = crtR13GrYCurr[3];
  3535:           if (crtR11TxYZeroLast != crtR11TxYZero ||
  3536:               crtR13GrYZeroLast[0] != crtR13GrYZero[0] ||
  3537:               crtR13GrYZeroLast[1] != crtR13GrYZero[1] ||
  3538:               crtR13GrYZeroLast[2] != crtR13GrYZero[2] ||
  3539:               crtR13GrYZeroLast[3] != crtR13GrYZero[3]) {
  3540:             crtR11TxYZeroLast = crtR11TxYZero;
  3541:             crtR13GrYZeroLast[0] = crtR13GrYZero[0];
  3542:             crtR13GrYZeroLast[1] = crtR13GrYZero[1];
  3543:             crtR13GrYZeroLast[2] = crtR13GrYZero[2];
  3544:             crtR13GrYZeroLast[3] = crtR13GrYZero[3];
  3545:             crtAllStamp += 2;
  3546:           }
  3547:           crtDataY = 0;  //データY座標を0で初期化
  3548:           if (crtClearStandby) {  //高速クリアの要求があるとき
  3549:             crtClearStandby = false;
  3550:             crtClearFrames = 1;  //高速クリアカウンタを1で初期化
  3551:           }
  3552:           if (SpriteScreen.SPR_THREE_STEPS) {
  3553:             SpriteScreen.sprActive = (SpriteScreen.sprReg8ResoCurr & 10) == 0;
  3554:             if (SpriteScreen.sprActive) {
  3555:               crtAllStamp += 2;
  3556:               //ラスタ(dst=-4,src=-2)
  3557:               SpriteScreen.sprStep1 (0);  //表(0)にスプライト(0)を並べる
  3558:               SpriteScreen.sprSwap ();  //表(0)と裏(-2)を入れ換える
  3559:               //ラスタ(dst=-2,src=-1)
  3560:               //表(-2)を表(2)として再利用する
  3561:               SpriteScreen.sprStep1 (1);  //表(2)にスプライト(1)を並べる
  3562:               SpriteScreen.sprSwap ();  //表(2)と裏(0)を入れ換える
  3563:               SpriteScreen.sprStep2 (0);  //表(0)にバックグラウンド(0)を並べる
  3564:             }
  3565:           }
  3566:           if (XEiJ.PNL_STEREOSCOPIC_ON && XEiJ.pnlStereoscopicOn) {
  3567:             crtStereoscopicStart ();
  3568:           }
  3569:           TickerQueue.tkqAdd (crtTicker = SlitOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3570:         }
  3571:       } else {  //垂直空白期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3572:         //垂直映像期間開始ラスタではないとき
  3573:         TickerQueue.tkqAdd (crtTicker = SlitOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3574:       }
  3575:     }
  3576:   };
  3577:   //    (9)省略フレームの垂直空白期間の水平同期パルス
  3578:   public static final TickerQueue.Ticker SlitOmitIdleSync = new TickerQueue.Ticker () {
  3579:     @Override protected void tick () {
  3580:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3581:       TickerQueue.tkqAdd (crtTicker = SlitOmitIdleBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3582:     }
  3583:   };
  3584:   //    (10)省略フレームの垂直空白期間の水平バックポーチと水平映像期間
  3585:   public static final TickerQueue.Ticker SlitOmitIdleBackDisp = new TickerQueue.Ticker () {
  3586:     @Override protected void tick () {
  3587:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3588:       TickerQueue.tkqAdd (crtTicker = SlitOmitIdleFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(8)省略フレームの垂直空白期間の水平フロントポーチ
  3589:     }
  3590:   };
  3591:   //    (11)省略フレームの垂直映像期間の水平フロントポーチ
  3592:   public static final TickerQueue.Ticker SlitOmitDispFront = new TickerQueue.Ticker () {
  3593:     @Override protected void tick () {
  3594:       int n = ++crtRasterNumber;  //ラスタ番号を1増やす
  3595:       if (!CRT_RASTER_HASH_ON || crtRasterHashDisp << n < CRT_RASTER_HASH_ZERO) {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性があるとき
  3596:         if (crtRasterCopyOn) {  //ラスタコピースイッチがONのとき
  3597:           crtDoRasterCopy ();  //ラスタコピー実行
  3598:         }
  3599:         if (RasterBreakPoint.RBP_ON) {
  3600:           if (n == RasterBreakPoint.rbpActiveBreakRaster) {  //ブレークラスタのとき
  3601:             RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3602:           }
  3603:         }
  3604:         int irq = n == crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK;  //IRQ信号を更新
  3605:         if (irq != MC68901.mfpGpipRint) {  //IRQ信号が変化したとき
  3606:           if (irq == 0) {  //IRQ信号が0になったとき
  3607:             if (RasterBreakPoint.RBP_ON) {
  3608:               if (RasterBreakPoint.rbpIRQBreakEnabled) {  //IRQラスタでラスタブレークをかけるとき
  3609:                 RasterBreakPoint.rbpFire ();  //ラスタブレークをかける
  3610:               }
  3611:             }
  3612:             MC68901.mfpRintFall ();  //IRQ開始
  3613:           } else {  //IRQ信号が0でなくなったとき
  3614:             MC68901.mfpRintRise ();  //IRQ終了
  3615:           }
  3616:         }
  3617:         if (n != crtVIdleStart) {  //垂直空白期間開始ラスタではないとき
  3618:           TickerQueue.tkqAdd (crtTicker = SlitOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3619:         } else {  //垂直空白期間開始ラスタのとき
  3620:           MC68901.mfpVdispFall ();  //垂直映像期間終了
  3621:           if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3622:             crtClearFrames--;  //高速クリアカウンタを1減らす
  3623:           }
  3624:           if (CRT_ENABLE_INTERMITTENT) {
  3625:             crtIntermittentCounter--;  //間欠カウンタを1減らす
  3626:           }
  3627:           if (XEiJ.mpuClockTime >= crtFrameTaskClock) {
  3628:             crtDoFrameTask ();  //フレームタスク(コントラスト調整など)
  3629:           }
  3630:           TickerQueue.tkqAdd (crtTicker = SlitOmitIdleSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(9)省略フレームの垂直空白期間の水平同期パルス
  3631:         }
  3632:       } else {  //垂直映像期間の水平フロントポーチでアクションを起こすべきラスタの可能性がないとき
  3633:         //垂直空白期間開始ラスタではないとき
  3634:         TickerQueue.tkqAdd (crtTicker = SlitOmitDispSync, crtClock += crtHFrontLength);  //+水平フロントポーチの長さ→(12)省略フレームの垂直映像期間の水平同期パルス
  3635:       }
  3636:     }
  3637:   };
  3638:   //    (12)省略フレームの垂直映像期間の水平同期パルス
  3639:   public static final TickerQueue.Ticker SlitOmitDispSync = new TickerQueue.Ticker () {
  3640:     @Override protected void tick () {
  3641:       MC68901.mfpHsyncRise ();  //水平同期パルス開始
  3642:       if (crtClearFrames != 0) {  //高速クリアカウンタが0ではないとき
  3643:         crtRapidClear (crtDataY);  //データY座標を高速クリア
  3644:         crtRasterStamp[crtDataY] = 0;
  3645:       }
  3646:       TickerQueue.tkqAdd (crtTicker = SlitOmitDispBackDisp, crtClock += crtHSyncLength);  //+水平同期パルスの長さ→(13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  3647:     }
  3648:   };
  3649:   //    (13)省略フレームの垂直映像期間の水平バックポーチと水平映像期間
  3650:   public static final TickerQueue.Ticker SlitOmitDispBackDisp = new TickerQueue.Ticker () {
  3651:     @Override protected void tick () {
  3652:       MC68901.mfpHsyncFall ();  //水平同期パルス終了
  3653:       TickerQueue.tkqAdd (crtTicker = SlitOmitDispFront, crtClock += crtHBackDispLength);  //+水平バックポーチと水平映像期間の長さ→(11)省略フレームの垂直映像期間の水平フロントポーチ
  3654:     }
  3655:   };
  3656: 
  3657: 
  3658: 
  3659: }  //class CRTC
  3660: 
  3661: 
  3662: