xeij/MemoryMappedDevice.java
//========================================================================================
//  MemoryMappedDevice.java
//    en:Memory mapped device
//    ja:メモリマップトデバイス
//  Copyright (C) 2003-2026 Makoto Kamada
//
//  This file is part of the XEiJ (X68000 Emulator in Java).
//  You can use, modify and redistribute the XEiJ if the conditions are met.
//  Read the XEiJ License for more details.
//  https://stdkmd.net/xeij/
//========================================================================================

//----------------------------------------------------------------------------------------
//  デバイスにアクセスするためのメソッドをenum bodyに記述する
//    mmdPbs,mmdPbz,mmdPws,mmdPwz,mmdPls  ピーク
//    mmdRbs,mmdRbz,mmdRws,mmdRwz,mmdRls  リード
//    mmdWb,mmdWw,mmdWl                   ライト
//  ピーク、リード、ライトの命名規則
//    4文字目  P=ピーク,R=リード,W=ライト
//    5文字目  b=バイト,w=ワード,l=ロング
//    6文字目  s=符号拡張,z=ゼロ拡張
//  ピークとリードの返却値の型はmmdPbsとmmdRbsだけbyte、他はint
//  ピークはSRAMスイッチの読み取りやデバッガなどで使用する
//  ピークはMPUやデバイスの状態を変化させず、例外もスローしない
//  リードとライトはMPUやDMAによる通常のアクセスで使用する
//  リードとライトはバスエラーをスローする場合がある
//  アドレスの未使用ビットはデバイスに渡る前にすべてクリアされていなければならない
//    バスエラーは未使用ビットがクリアされたアドレスで通知されることになる
//  異なるデバイスに跨るアクセスはデバイスに渡る前に分割されていなければならない
//  奇数アドレスに対するワードアクセスはデバイスに渡る前に分割または排除されていなければならない
//  4の倍数でないアドレスに対するロングアクセスはデバイスに渡る前に分割または排除されていなければならない
//  デバイスのメソッドを直接呼び出すときはアドレスのマスクや分割を忘れないこと
//----------------------------------------------------------------------------------------

package xeij;

import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap

public enum MemoryMappedDevice {

  //--------------------------------------------------------------------------------
  //MMD_MMR メインメモリ
  MMD_MMR {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "メインメモリ" : "Main Memory";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a];
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getShort (a);
      } else {
        return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
      }
    }
    @Override protected int mmdPwz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getChar (a);
      } else {
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
    }
    @Override protected int mmdPls (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getInt (a);
      } else {
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getShort (a);
       } else {
         return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getChar (a);
       } else {
         return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ramlong;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getInt (a);
       } else {
         return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       a &= XEiJ.BUS_MOTHER_MASK;
       MainMemory.mmrM8[a    ] = (byte)  d;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         MainMemory.mmrBuffer.putShort (a, (short) d);
       } else {
         MainMemory.mmrM8[a    ] = (byte) (d >> 8);
         MainMemory.mmrM8[a + 1] = (byte)  d;
       }
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ramlong;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         MainMemory.mmrBuffer.putInt (a, d);
       } else {
         MainMemory.mmrM8[a    ] = (byte) (d >> 24);
         MainMemory.mmrM8[a + 1] = (byte) (d >> 16);
         MainMemory.mmrM8[a + 2] = (byte) (d >> 8);
         MainMemory.mmrM8[a + 3] = (byte)  d;
       }
     }
  },  //MMD_MMR

  MMD_MM6 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "メインメモリ (68060)" : "Main Memory (68060)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a];
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getShort (a);
      } else {
        return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
      }
    }
    @Override protected int mmdPwz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getChar (a);
      } else {
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
    }
    @Override protected int mmdPls (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getInt (a);
      } else {
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getShort (a);
       } else {
         return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getChar (a);
       } else {
         return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ramlong;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getInt (a);
       } else {
         return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catWriteMain (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       MainMemory.mmrM8[a    ] = (byte)  d;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catWriteMain (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         MainMemory.mmrBuffer.putShort (a, (short) d);
       } else {
         MainMemory.mmrM8[a    ] = (byte) (d >> 8);
         MainMemory.mmrM8[a + 1] = (byte)  d;
       }
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catWriteMain (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.ramlong;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         MainMemory.mmrBuffer.putInt (a, d);
       } else {
         MainMemory.mmrM8[a    ] = (byte) (d >> 24);
         MainMemory.mmrM8[a + 1] = (byte) (d >> 16);
         MainMemory.mmrM8[a + 2] = (byte) (d >> 8);
         MainMemory.mmrM8[a + 3] = (byte)  d;
       }
     }
  },  //MMD_MM6

  //--------------------------------------------------------------------------------
  //MMD_MM1 1MB搭載機の2MB目
  //  $00100000~$001FFFFF
  //  リードはShodaiは$6100、ACE/PRO/PROIIは$FFFFの繰り返し
  //  ライトは無視
  MMD_MM1 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "1MB 搭載機の 2MB 目" : "2nd MB of machines with 1MB";
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
      return (byte) ((a & 1) == 0 ? mmdRwz (a) >> 8 : mmdRwz (a));
    }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
      return (a & 1) == 0 ? mmdRwz (a) >> 8 : mmdRwz (a) & 0xff;
    }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
      return (short) mmdRwz (a);
    }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
      return XEiJ.currentModel.isShodai () ? 0x6100 : 0xffff;
    }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ramlong;
      return mmdRwz (a) << 16 | mmdRwz (a + 2);
    }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ram;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ramlong;
     }
  },

  //--------------------------------------------------------------------------------
  //MMD_XMM 拡張メモリ
  MMD_XMM {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張メモリ" : "Expansion Memory";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a];
    }
    @Override protected int mmdPbz (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff);
    }
    @Override protected int mmdPwz (int a) {
      a -= XEiJ.busExMemoryStart;
      return (char) (XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff));
    }
    @Override protected int mmdPls (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a] << 24 | (XEiJ.busExMemoryArray[a + 1] & 0xff) << 16 | (char) (XEiJ.busExMemoryArray[a + 2] << 8 | (XEiJ.busExMemoryArray[a + 3] & 0xff));
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       return (char) (XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff));
     }
    @Override protected int mmdRls (int a) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a] << 24 | (XEiJ.busExMemoryArray[a + 1] & 0xff) << 16 | (char) (XEiJ.busExMemoryArray[a + 2] << 8 | (XEiJ.busExMemoryArray[a + 3] & 0xff));
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       XEiJ.busExMemoryArray[a    ] = (byte)  d;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       XEiJ.busExMemoryArray[a    ] = (byte) (d >> 8);
       XEiJ.busExMemoryArray[a + 1] = (byte)  d;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       a -= XEiJ.busExMemoryStart;
       XEiJ.busExMemoryArray[a    ] = (byte) (d >> 24);
       XEiJ.busExMemoryArray[a + 1] = (byte) (d >> 16);
       XEiJ.busExMemoryArray[a + 2] = (byte) (d >> 8);
       XEiJ.busExMemoryArray[a + 3] = (byte)  d;
     }
  },  //MMD_XMM

  MMD_XM6 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張メモリ (68060)" : "Expansion Memory (68060)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a];
    }
    @Override protected int mmdPbz (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff);
    }
    @Override protected int mmdPwz (int a) {
      a -= XEiJ.busExMemoryStart;
      return (char) (XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff));
    }
    @Override protected int mmdPls (int a) {
      a -= XEiJ.busExMemoryStart;
      return XEiJ.busExMemoryArray[a] << 24 | (XEiJ.busExMemoryArray[a + 1] & 0xff) << 16 | (char) (XEiJ.busExMemoryArray[a + 2] << 8 | (XEiJ.busExMemoryArray[a + 3] & 0xff));
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catReadHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catReadHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catReadHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catReadHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       return (char) (XEiJ.busExMemoryArray[a] << 8 | (XEiJ.busExMemoryArray[a + 1] & 0xff));
     }
    @Override protected int mmdRls (int a) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catReadHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       return XEiJ.busExMemoryArray[a] << 24 | (XEiJ.busExMemoryArray[a + 1] & 0xff) << 16 | (char) (XEiJ.busExMemoryArray[a + 2] << 8 | (XEiJ.busExMemoryArray[a + 3] & 0xff));
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catWriteHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       XEiJ.busExMemoryArray[a    ] = (byte)  d;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catWriteHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       XEiJ.busExMemoryArray[a    ] = (byte) (d >> 8);
       XEiJ.busExMemoryArray[a + 1] = (byte)  d;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       if (MC68060.CAT_ON) {
         MC68060.catWriteHigh (a);
       }
       a -= XEiJ.busExMemoryStart;
       XEiJ.busExMemoryArray[a    ] = (byte) (d >> 24);
       XEiJ.busExMemoryArray[a + 1] = (byte) (d >> 16);
       XEiJ.busExMemoryArray[a + 2] = (byte) (d >> 8);
       XEiJ.busExMemoryArray[a + 3] = (byte)  d;
     }
  },  //MMD_XM6

  //--------------------------------------------------------------------------------
  //MMD_GE0 グラフィックス画面(512ドット16色ページ0)
  //
  //  512ドット16色
  //         ------------------参照------------------        --------------格納--------------
  //    GE0  0x00c00000~0x00c7ffff  ............3210  ──  0x00000000~0x0003ffff  ....3210
  //    参照  00000000 11000yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000000yy yyyyyyyx xxxxxxxx  i=0x00000|((a>>1)&0x3ffff)
  //
  MMD_GE0 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 16 色 ページ 0)" : "Graphics Screen (512 dots 16 colors page 0)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[(a >> 1) & 0x3ffff]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[(a >> 1) & 0x3ffff]);
    }
    @Override protected int mmdPws (int a) {
      return GraphicScreen.graM4[(a >> 1) & 0x3ffff];
    }
    @Override protected int mmdPwz (int a) {
      return GraphicScreen.graM4[(a >> 1) & 0x3ffff];
    }
    @Override protected int mmdPls (int a) {
      return (GraphicScreen.graM4[( a      >> 1) & 0x3ffff] << 16 |
              GraphicScreen.graM4[((a + 2) >> 1) & 0x3ffff]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[(a >> 1) & 0x3ffff]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[(a >> 1) & 0x3ffff]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[(a >> 1) & 0x3ffff];
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[(a >> 1) & 0x3ffff];
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       return (GraphicScreen.graM4[( a      >> 1) & 0x3ffff] << 16 |
               GraphicScreen.graM4[((a + 2) >> 1) & 0x3ffff]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         GraphicScreen.graM4[(a >> 1) & 0x3ffff] = (byte) (d & 15);
         int y = (a >> 10) - CRTC.crtR13GrYCurr[0] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       GraphicScreen.graM4[(a >> 1) & 0x3ffff] = (byte) (d & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       GraphicScreen.graM4[( a      >> 1) & 0x3ffff] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[((a + 2) >> 1) & 0x3ffff] = (byte) ( d        & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       if ((a & 1022) == 1022) {
         y = (y + 1) & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GE0

  //--------------------------------------------------------------------------------
  //MMD_GE1 グラフィックス画面(512ドット16色ページ1)
  //
  //  512ドット16色
  //         ------------------参照------------------        --------------格納--------------
  //    GE1  0x00c80000~0x00cfffff  ............7654  ──  0x00040000~0x0007ffff  ....7654
  //    参照  00000000 11001yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000001yy yyyyyyyx xxxxxxxx  i=0x40000|((a>>1)&0x3ffff)
  //
  MMD_GE1 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 16 色 ページ 1)" : "Graphics Screen (512 dots 16 colors page 1)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPws (int a) {
      return GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)];
    }
    @Override protected int mmdPwz (int a) {
      return GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)];
    }
    @Override protected int mmdPls (int a) {
      return (GraphicScreen.graM4[0x40000 + (( a      >> 1) & 0x3ffff)] << 16 |
              GraphicScreen.graM4[0x40000 + (((a + 2) >> 1) & 0x3ffff)]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)];
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)];
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       return (GraphicScreen.graM4[0x40000 + (( a      >> 1) & 0x3ffff)] << 16 |
               GraphicScreen.graM4[0x40000 + (((a + 2) >> 1) & 0x3ffff)]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)] = (byte) (d & 15);
         int y = (a >> 10) - CRTC.crtR13GrYCurr[1] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)] = (byte) (d & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       GraphicScreen.graM4[0x40000 + (( a      >> 1) & 0x3ffff)] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0x40000 + (((a + 2) >> 1) & 0x3ffff)] = (byte) ( d        & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       if ((a & 1022) == 1022) {
         y = (y + 1) & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GE1

  //--------------------------------------------------------------------------------
  //MMD_GE2 グラフィックス画面(512ドット16色ページ2)
  //
  //  512ドット16色
  //         ------------------参照------------------        --------------格納--------------
  //    GE2  0x00d00000~0x00d7ffff  ............ba98  ──  0x00080000~0x000bffff  ....ba98
  //    参照  00000000 11010yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000010yy yyyyyyyx xxxxxxxx  i=0x80000|((a>>1)&0x3ffff)
  //
  MMD_GE2 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 16 色 ページ 2)" : "Graphics Screen (512 dots 16 colors page 2)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPws (int a) {
      return GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)];
    }
    @Override protected int mmdPwz (int a) {
      return GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)];
    }
    @Override protected int mmdPls (int a) {
      return (GraphicScreen.graM4[0x80000 + (( a      >> 1) & 0x3ffff)] << 16 |
              GraphicScreen.graM4[0x80000 + (((a + 2) >> 1) & 0x3ffff)]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)];
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)];
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       return (GraphicScreen.graM4[0x80000 + (( a      >> 1) & 0x3ffff)] << 16 |
               GraphicScreen.graM4[0x80000 + (((a + 2) >> 1) & 0x3ffff)]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)] = (byte) (d & 15);
         int y = (a >> 10) - CRTC.crtR13GrYCurr[2] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)] = (byte) (d & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       GraphicScreen.graM4[0x80000 + (( a      >> 1) & 0x3ffff)] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0x80000 + (((a + 2) >> 1) & 0x3ffff)] = (byte) ( d        & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       if ((a & 1022) == 1022) {
         y = (y + 1) & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GE2

  //--------------------------------------------------------------------------------
  //MMD_GE3 グラフィックス画面(512ドット16色ページ3)
  //
  //  512ドット16色
  //         ------------------参照------------------        --------------格納--------------
  //    GE3  0x00d80000~0x00dfffff  ............fedc  ──  0x000c0000~0x000fffff  ....fedc
  //    参照  00000000 11011yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000011yy yyyyyyyx xxxxxxxx  i=0xc0000|((a>>1)&0x3ffff)
  //
  MMD_GE3 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 16 色 ページ 3)" : "Graphics Screen (512 dots 16 colors page 3)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPws (int a) {
      return GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)];
    }
    @Override protected int mmdPwz (int a) {
      return GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)];
    }
    @Override protected int mmdPls (int a) {
      return (GraphicScreen.graM4[0xc0000 + (( a      >> 1) & 0x3ffff)] << 16 |
              GraphicScreen.graM4[0xc0000 + (((a + 2) >> 1) & 0x3ffff)]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)];
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)];
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       return (GraphicScreen.graM4[0xc0000 + (( a      >> 1) & 0x3ffff)] << 16 |
               GraphicScreen.graM4[0xc0000 + (((a + 2) >> 1) & 0x3ffff)]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)] = (byte) (d & 15);
         int y = (a >> 10) - CRTC.crtR13GrYCurr[3] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)] = (byte) (d & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       GraphicScreen.graM4[0xc0000 + (( a      >> 1) & 0x3ffff)] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0xc0000 + (((a + 2) >> 1) & 0x3ffff)] = (byte) ( d        & 15);
       int y = (a >> 10) - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       if ((a & 1022) == 1022) {
         y = (y + 1) & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GE3

  //--------------------------------------------------------------------------------
  //MMD_GF0 グラフィックス画面(512ドット256色ページ0)
  //
  //  512ドット256色
  //         ------------------参照------------------        --------------格納--------------
  //    GF0  0x00c00000~0x00c7ffff  ........76543210  ─┬  0x00000000~0x0003ffff  ....3210
  //                                                     └  0x00040000~0x0007ffff  ....7654
  //    参照  00000000 11000yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000000yy yyyyyyyx xxxxxxxx  i=0x00000|((a>>1)&0x3ffff)
  //          00000000 000001yy yyyyyyyx xxxxxxxx  i=0x40000|((a>>1)&0x3ffff)
  //
  MMD_GF0 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 256 色 ページ 0)" : "Graphics Screen (512 dots 256 colors page 0)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (byte) ((a & 1) == 0 ? 0 :
                     GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)] << 4 |
                     GraphicScreen.graM4[          ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)] << 4 |
              GraphicScreen.graM4[          ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPws (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0x40000 + i] << 4 |
              GraphicScreen.graM4[          i]);
    }
    @Override protected int mmdPwz (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0x40000 + i] << 4 |
              GraphicScreen.graM4[          i]);
    }
    @Override protected int mmdPls (int a) {
      int i0 = ( a      >> 1) & 0x3ffff;
      int i1 = ((a + 2) >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0x40000 + i0] << 20 |
              GraphicScreen.graM4[          i0] << 16 |
              GraphicScreen.graM4[0x40000 + i1] <<  4 |
              GraphicScreen.graM4[          i1]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return (byte) ((a & 1) == 0 ? 0 :
                      GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)] << 4 |
                      GraphicScreen.graM4[          ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0x40000 + ((a >> 1) & 0x3ffff)] << 4 |
               GraphicScreen.graM4[          ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0x40000 + i] << 4 |
               GraphicScreen.graM4[          i]);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0x40000 + i] << 4 |
               GraphicScreen.graM4[          i]);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ( a      >> 1) & 0x3ffff;
       int i1 = ((a + 2) >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0x40000 + i0] << 20 |
               GraphicScreen.graM4[          i0] << 16 |
               GraphicScreen.graM4[0x40000 + i1] << 4 |
               GraphicScreen.graM4[          i1]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         int i = (a >> 1) & 0x3ffff;
         GraphicScreen.graM4[0x40000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[          i] = (byte) ( d       & 15);
         a >>= 10;
         int y = a - CRTC.crtR13GrYCurr[0] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[1] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       GraphicScreen.graM4[0x40000 + i] = (byte) ((d >> 4) & 15);
       GraphicScreen.graM4[          i] = (byte) ( d       & 15);
       a >>= 10;
       int y = a - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ( a      >> 1) & 0x3ffff;
       int i1 = ((a + 2) >> 1) & 0x3ffff;
       GraphicScreen.graM4[0x40000 + i0] = (byte) ((d >> 20) & 15);
       GraphicScreen.graM4[          i0] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0x40000 + i1] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[          i1] = (byte) ( d        & 15);
       int b = (a + 2) >>> 10;
       int y = b - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = b - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       a >>>= 10;
       if (a != b) {
         y = a - CRTC.crtR13GrYCurr[0] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[1] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GF0

  //--------------------------------------------------------------------------------
  //MMD_GF1 グラフィックス画面(512ドット256色ページ1)
  //
  //  512ドット256色
  //         ------------------参照------------------        --------------格納--------------
  //    GF1  0x00c80000~0x00cfffff  ........fedcba98  ─┬  0x00080000~0x000bffff  ....ba98
  //                                                     └  0x000c0000~0x000fffff  ....fedc
  //    参照  00000000 11001yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000010yy yyyyyyyx xxxxxxxx  i=0x80000|((a>>1)&0x3ffff)
  //          00000000 000011yy yyyyyyyx xxxxxxxx  i=0xc0000|((a>>1)&0x3ffff)
  //
  MMD_GF1 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 256 色 ページ 1)" : "Graphics Screen (512 dots 256 colors page 1)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (byte) ((a & 1) == 0 ? 0 :
                     GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)] << 4 |
                     GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)] << 4 |
              GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
    }
    @Override protected int mmdPws (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0xc0000 + i] << 4 |
              GraphicScreen.graM4[0x80000 + i]);
    }
    @Override protected int mmdPwz (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0xc0000 + i] << 4 |
              GraphicScreen.graM4[0x80000 + i]);
    }
    @Override protected int mmdPls (int a) {
      int i0 = ( a      >> 1) & 0x3ffff;
      int i1 = ((a + 2) >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0xc0000 + i0] << 20 |
              GraphicScreen.graM4[0x80000 + i0] << 16 |
              GraphicScreen.graM4[0xc0000 + i1] <<  4 |
              GraphicScreen.graM4[0x80000 + i1]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return (byte) ((a & 1) == 0 ? 0 :
                      GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)] << 4 |
                      GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0xc0000 + ((a >> 1) & 0x3ffff)] << 4 |
               GraphicScreen.graM4[0x80000 + ((a >> 1) & 0x3ffff)]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0xc0000 + i] << 4 |
               GraphicScreen.graM4[0x80000 + i]);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0xc0000 + i] << 4 |
               GraphicScreen.graM4[0x80000 + i]);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ( a      >> 1) & 0x3ffff;
       int i1 = ((a + 2) >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0xc0000 + i0] << 20 |
               GraphicScreen.graM4[0x80000 + i0] << 16 |
               GraphicScreen.graM4[0xc0000 + i1] << 4 |
               GraphicScreen.graM4[0x80000 + i1]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         int i = (a >> 1) & 0x3ffff;
         GraphicScreen.graM4[0xc0000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[0x80000 + i] = (byte) ( d       & 15);
         a >>= 10;
         int y = a - CRTC.crtR13GrYCurr[2] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[3] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       GraphicScreen.graM4[0xc0000 + i] = (byte) ((d >> 4) & 15);
       GraphicScreen.graM4[0x80000 + i] = (byte) ( d       & 15);
       a >>= 10;
       int y = a - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ( a      >> 1) & 0x3ffff;
       int i1 = ((a + 2) >> 1) & 0x3ffff;
       GraphicScreen.graM4[0xc0000 + i0] = (byte) ((d >> 20) & 15);
       GraphicScreen.graM4[0x80000 + i0] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0xc0000 + i1] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[0x80000 + i1] = (byte) ( d        & 15);
       int b = (a + 2) >>> 10;
       int y = b - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = b - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       a >>>= 10;
       if (a != b) {
         y = a - CRTC.crtR13GrYCurr[2] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[3] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GF1

  //--------------------------------------------------------------------------------
  //MMD_GM2 グラフィックス画面(メモリモード2)
  //
  //  バイトリード
  //    0x00c00000  0x00が読み出される
  //    0x00c00001  4bitページ1-0が読み出される
  //    0x00c80000  0x00が読み出される
  //    0x00c80001  0x00が読み出される
  //    0x00d00000  0x00が読み出される
  //    0x00d00001  4bitページ1-0が読み出される
  //    0x00d80000  0x00が読み出される
  //    0x00d80001  0x00が読み出される
  //
  //  ワードリード
  //    0x00c00000  上位バイトは0x00で下位バイトに4bitページ1-0が読み出される
  //    0x00c80000  上位バイトは0x00で下位バイトに4bitページ3-2が読み出される
  //    0x00d00000  上位バイトは0x00で下位バイトに4bitページ1-0が読み出される
  //    0x00d80000  上位バイトは0x00で下位バイトに4bitページ3-2が読み出される
  //
  //  バイトライト
  //    0x00c00000  060turboの060モードのときデータは無視され0x00が4bitページ3-2に書き込まれる。それ以外はデータが4bitページ3-2に書き込まれる
  //    0x00c00001  データが4bitページ1-0に書き込まれる
  //    0x00c80000  060turboの060モードのときデータは無視され0x00が4bitページ3-2に書き込まれる。それ以外はデータが4bitページ3-2に書き込まれる
  //    0x00c80001  データが4bitページ1-0に書き込まれる
  //    0x00d00000  060turboの060モードのときデータは無視され0x00が4bitページ3-2に書き込まれる。それ以外はデータが4bitページ3-2に書き込まれる
  //    0x00d00001  データが4bitページ1-0に書き込まれる
  //    0x00d80000  060turboの060モードのときデータは無視され0x00が4bitページ3-2に書き込まれる。それ以外はデータが4bitページ3-2に書き込まれる
  //    0x00d80001  データが4bitページ1-0に書き込まれる
  //
  //  ワードライト
  //    0x00c00000  上位バイトは無視され下位バイトが4bitページ3-2と4bitページ1-0の両方に書き込まれる
  //    0x00c80000  上位バイトは無視され下位バイトが4bitページ3-2と4bitページ1-0の両方に書き込まれる
  //    0x00d00000  上位バイトは無視され下位バイトが4bitページ3-2と4bitページ1-0の両方に書き込まれる
  //    0x00d80000  上位バイトは無視され下位バイトが4bitページ3-2と4bitページ1-0の両方に書き込まれる
  //
  MMD_GM2 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (メモリモード 2)" : "Graphics Screen (memory mode 2)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      if ((a & 0x00080001) == 0x00000001) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffffかつ下位バイト
        int i = (a >> 1) & 0x3ffff;
        return (byte) (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                       GraphicScreen.graM4[          i]);  //4bitページ0
      } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffffまたは上位バイト
        return 0x00;
      }
    }
    @Override protected int mmdPbz (int a) {
      if ((a & 0x00080001) == 0x00000001) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffffかつ下位バイト
        int i = (a >> 1) & 0x3ffff;
        return (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                GraphicScreen.graM4[          i]);  //4bitページ0
      } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffffまたは上位バイト
        return 0x00;
      }
    }
    @Override protected int mmdPws (int a) {
      int i = (a >> 1) & 0x3ffff;
      if ((a & 0x00080000) == 0x00000000) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffff
        return (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                GraphicScreen.graM4[          i]);  //4bitページ0
      } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffff
        return (GraphicScreen.graM4[0xc0000 + i] << 4 |  //4bitページ3
                GraphicScreen.graM4[0x80000 + i]);  //4bitページ2
      }
    }
    @Override protected int mmdPwz (int a) {
      int i = (a >> 1) & 0x3ffff;
      if ((a & 0x00080000) == 0x00000000) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffff
        return (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                GraphicScreen.graM4[          i]);  //4bitページ0
      } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffff
        return (GraphicScreen.graM4[0xc0000 + i] << 4 |  //4bitページ3
                GraphicScreen.graM4[0x80000 + i]);  //4bitページ2
      }
    }
    @Override protected int mmdPls (int a) {
      return mmdPws (a) << 16 | mmdPwz (a + 2);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 0x00080001) == 0x00000001) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffffかつ下位バイト
         int i = (a >> 1) & 0x3ffff;
         return (byte) (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                        GraphicScreen.graM4[          i]);  //4bitページ0
       } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffffまたは上位バイト
         return 0x00;
       }
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 0x00080001) == 0x00000001) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffffかつ下位バイト
         int i = (a >> 1) & 0x3ffff;
         return (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                 GraphicScreen.graM4[          i]);  //4bitページ0
       } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffffまたは上位バイト
         return 0x00;
       }
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       if ((a & 0x00080000) == 0x00000000) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffff
         return (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                 GraphicScreen.graM4[          i]);  //4bitページ0
       } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffff
         return (GraphicScreen.graM4[0xc0000 + i] << 4 |  //4bitページ3
                 GraphicScreen.graM4[0x80000 + i]);  //4bitページ2
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       if ((a & 0x00080000) == 0x00000000) {  //0x00c00000..0x00c7ffff,0x00d00000..0x00d7ffff
         return (GraphicScreen.graM4[0x40000 + i] << 4 |  //4bitページ1
                 GraphicScreen.graM4[          i]);  //4bitページ0
       } else {  //0x00c80000..0x00cfffff,0x00d80000..0x00dfffff
         return (GraphicScreen.graM4[0xc0000 + i] << 4 |  //4bitページ3
                 GraphicScreen.graM4[0x80000 + i]);  //4bitページ2
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return mmdRws (a) << 16 | mmdRwz (a + 2);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       if ((a & 0x00000001) == 0x00000000) {  //上位バイト
         if (XEiJ.currentMPU < Model.MPU_MC68LC040) {
           GraphicScreen.graM4[0xc0000 + i] = (byte) ((d >> 4) & 15);  //4bitページ3
           GraphicScreen.graM4[0x80000 + i] = (byte) ( d       & 15);  //4bitページ2
         } else {
           GraphicScreen.graM4[0xc0000 + i] = 0;  //4bitページ3
           GraphicScreen.graM4[0x80000 + i] = 0;  //4bitページ2
         }
         a >>>= 10;
         int y = a - CRTC.crtR13GrYCurr[3] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[2] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       } else {  //下位バイト
         GraphicScreen.graM4[0x40000 + i] = (byte) ((d >> 4) & 15);  //4bitページ1
         GraphicScreen.graM4[          i] = (byte) ( d       & 15);  //4bitページ0
         a >>>= 10;
         int y = a - CRTC.crtR13GrYCurr[1] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[0] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       GraphicScreen.graM4[0xc0000 + i] = (byte) ((d >>  4) & 15);  //4bitページ3
       GraphicScreen.graM4[0x80000 + i] = (byte) ( d        & 15);  //4bitページ2
       GraphicScreen.graM4[0x40000 + i] = (byte) ((d >>  4) & 15);  //4bitページ1
       GraphicScreen.graM4[          i] = (byte) ( d        & 15);  //4bitページ0
       a >>>= 10;
       int y = a - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWw (a, d >> 16);
       mmdWw (a + 2, d);
     }
  },  //MMD_GM2

  //--------------------------------------------------------------------------------
  //MMD_GG0 グラフィックス画面(512ドット65536色)
  //
  //  512ドット65536色
  //         ------------------参照------------------        --------------格納--------------
  //    GG0  0x00c00000~0x00c7ffff  fedcba9876543210  ─┬  0x00000000~0x0003ffff  ....3210
  //                                                     ├  0x00040000~0x0007ffff  ....7654
  //                                                     ├  0x00080000~0x000bffff  ....ba98
  //                                                     └  0x000c0000~0x000fffff  ....fedc
  //    参照  00000000 11000yyy yyyyyyxx xxxxxxx1
  //    格納  00000000 000000yy yyyyyyyx xxxxxxxx  i=0x00000|((a>>1)&0x3ffff)
  //          00000000 000001yy yyyyyyyx xxxxxxxx  i=0x40000|((a>>1)&0x3ffff)
  //    参照  00000000 11000yyy yyyyyyxx xxxxxxx0
  //    格納  00000000 000010yy yyyyyyyx xxxxxxxx  i=0x80000|((a>>1)&0x3ffff)
  //          00000000 000011yy yyyyyyyx xxxxxxxx  i=0xc0000|((a>>1)&0x3ffff)
  //
  MMD_GG0 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (512 ドット 65536 色)" : "Graphics Screen (512 dots 65536 colors)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (byte) ((a & 1) == 0 ?
                     GraphicScreen.graM4[0xc0000 + i] << 4 |
                     GraphicScreen.graM4[0x80000 + i] :
                     GraphicScreen.graM4[0x40000 + i] << 4 |
                     GraphicScreen.graM4[          i]);
    }
    @Override protected int mmdPbz (int a) {
      int i = (a >> 1) & 0x3ffff;
      return ((a & 1) == 0 ?
              GraphicScreen.graM4[0xc0000 + i] << 4 |
              GraphicScreen.graM4[0x80000 + i] :
              GraphicScreen.graM4[0x40000 + i] << 4 |
              GraphicScreen.graM4[          i]);
    }
    @Override protected int mmdPws (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (short) (GraphicScreen.graM4[0xc0000 + i] << 12 |
                      GraphicScreen.graM4[0x80000 + i] <<  8 |
                      GraphicScreen.graM4[0x40000 + i] <<  4 |
                      GraphicScreen.graM4[          i]);
    }
    @Override protected int mmdPwz (int a) {
      int i = (a >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0xc0000 + i] << 12 |
              GraphicScreen.graM4[0x80000 + i] <<  8 |
              GraphicScreen.graM4[0x40000 + i] <<  4 |
              GraphicScreen.graM4[          i]);
    }
    @Override protected int mmdPls (int a) {
      int i0 = ( a      >> 1) & 0x3ffff;
      int i1 = ((a + 2) >> 1) & 0x3ffff;
      return (GraphicScreen.graM4[0xc0000 + i0] << 28 |
              GraphicScreen.graM4[0x80000 + i0] << 24 |
              GraphicScreen.graM4[0x40000 + i0] << 20 |
              GraphicScreen.graM4[          i0] << 16 |
              GraphicScreen.graM4[0xc0000 + i1] << 12 |
              GraphicScreen.graM4[0x80000 + i1] <<  8 |
              GraphicScreen.graM4[0x40000 + i1] <<  4 |
              GraphicScreen.graM4[          i1]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (byte) ((a & 1) == 0 ?
                      GraphicScreen.graM4[0xc0000 + i] << 4 |
                      GraphicScreen.graM4[0x80000 + i] :
                      GraphicScreen.graM4[0x40000 + i] << 4 |
                      GraphicScreen.graM4[          i]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return ((a & 1) == 0 ?
               GraphicScreen.graM4[0xc0000 + i] << 4 |
               GraphicScreen.graM4[0x80000 + i] :
               GraphicScreen.graM4[0x40000 + i] << 4 |
               GraphicScreen.graM4[          i]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (short) (GraphicScreen.graM4[0xc0000 + i] << 12 |
                       GraphicScreen.graM4[0x80000 + i] <<  8 |
                       GraphicScreen.graM4[0x40000 + i] <<  4 |
                       GraphicScreen.graM4[          i]);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0xc0000 + i] << 12 |
               GraphicScreen.graM4[0x80000 + i] <<  8 |
               GraphicScreen.graM4[0x40000 + i] <<  4 |
               GraphicScreen.graM4[          i]);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ( a      >> 1) & 0x3ffff;
       int i1 = ((a + 2) >> 1) & 0x3ffff;
       return (GraphicScreen.graM4[0xc0000 + i0] << 28 |
               GraphicScreen.graM4[0x80000 + i0] << 24 |
               GraphicScreen.graM4[0x40000 + i0] << 20 |
               GraphicScreen.graM4[          i0] << 16 |
               GraphicScreen.graM4[0xc0000 + i1] << 12 |
               GraphicScreen.graM4[0x80000 + i1] <<  8 |
               GraphicScreen.graM4[0x40000 + i1] <<  4 |
               GraphicScreen.graM4[          i1]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       if ((a & 1) == 0) {
         GraphicScreen.graM4[0xc0000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[0x80000 + i] = (byte) ( d       & 15);
         a >>= 10;
         int y = a - CRTC.crtR13GrYCurr[2] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[3] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       } else {
         GraphicScreen.graM4[0x40000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[          i] = (byte) ( d       & 15);
         a >>= 10;
         int y = a - CRTC.crtR13GrYCurr[0] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[1] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = (a >> 1) & 0x3ffff;
       GraphicScreen.graM4[0xc0000 + i] = (byte) ((d >> 12) & 15);
       GraphicScreen.graM4[0x80000 + i] = (byte) ((d >>  8) & 15);
       GraphicScreen.graM4[0x40000 + i] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[          i] = (byte) ( d        & 15);
       a >>= 10;
       int y = a - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = a - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ( a      >> 1) & 0x3ffff;
       int i1 = ((a + 2) >> 1) & 0x3ffff;
       GraphicScreen.graM4[0xc0000 + i0] = (byte) (d >>> 28);
       GraphicScreen.graM4[0x80000 + i0] = (byte) ((d >> 24) & 15);
       GraphicScreen.graM4[0x40000 + i0] = (byte) ((d >> 20) & 15);
       GraphicScreen.graM4[          i0] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0xc0000 + i1] = (byte) ((char) d >> 12);
       GraphicScreen.graM4[0x80000 + i1] = (byte) ((d >>  8) & 15);
       GraphicScreen.graM4[0x40000 + i1] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[          i1] = (byte) ( d        & 15);
       int b = (a + 2) >>> 10;
       int y = b - CRTC.crtR13GrYCurr[0] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = b - CRTC.crtR13GrYCurr[1] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = b - CRTC.crtR13GrYCurr[2] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       y = b - CRTC.crtR13GrYCurr[3] & 511;
       CRTC.crtRasterStamp[y      ] = 0;
       CRTC.crtRasterStamp[y + 512] = 0;
       a >>>= 10;
       if (a != b) {
         y = a - CRTC.crtR13GrYCurr[0] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[1] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[2] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
         y = a - CRTC.crtR13GrYCurr[3] & 511;
         CRTC.crtRasterStamp[y      ] = 0;
         CRTC.crtRasterStamp[y + 512] = 0;
       }
     }
  },  //MMD_GG0

  //--------------------------------------------------------------------------------
  //MMD_GH0 グラフィックス画面(1024ドット16色)
  //
  //  1024ドット16色
  //         ------------------参照------------------        --------------格納--------------
  //    GH0  0x00c00000~0x00dfffff  ............3210  ──  0x00000000~0x000fffff  ....3210
  //    参照  00000000 110Yyyyy yyyyyXxx xxxxxxx1
  //    格納  00000000 0000YXyy yyyyyyyx xxxxxxxx  i=0x000000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //
  MMD_GH0 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (1024 ドット 16 色)" : "Graphics Screen (1024 dots 16 colors)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)]);
    }
    @Override protected int mmdPbz (int a) {
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)]);
    }
    @Override protected int mmdPws (int a) {
      return GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)];
    }
    @Override protected int mmdPwz (int a) {
      return GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)];
    }
    @Override protected int mmdPls (int a) {
      int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      a += 2;
      int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (GraphicScreen.graM4[i0] << 16 |
              GraphicScreen.graM4[i1]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)];
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       return GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)];
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       a += 2;
       int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (GraphicScreen.graM4[i0] << 16 |
               GraphicScreen.graM4[i1]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
         GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)] = (byte) (d & 15);
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)] = (byte) (d & 15);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)] = (byte) ((d >> 16) & 15);
       a += 2;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       GraphicScreen.graM4[((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00)] = (byte) ( d        & 15);
     }
  },  //MMD_GH0

  //--------------------------------------------------------------------------------
  //MMD_GI0 グラフィックス画面(1024ドット256色)
  //
  //  1024ドット256色
  //         ------------------参照------------------        --------------格納--------------
  //    GI0  0x00c00000~0x00dfffff  ........76543210  ─┬  0x00000000~0x000fffff  ....3210
  //                                                     └  0x00100000~0x001fffff  ....7654
  //    参照  00000000 110Yyyyy yyyyyXxx xxxxxxx1
  //    格納  00000000 0000YXyy yyyyyyyx xxxxxxxx  i=0x000000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //          00000000 0001YXyy yyyyyyyx xxxxxxxx  i=0x100000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //
  MMD_GI0 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (1024 ドット 256 色)" : "Graphics Screen (1024 dots 256 colors)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (byte) ((a & 1) == 0 ? 0 :
                     GraphicScreen.graM4[0x100000 + i] << 4 |
                     GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPbz (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return ((a & 1) == 0 ? 0 :
              GraphicScreen.graM4[0x100000 + i] << 4 |
              GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPws (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (GraphicScreen.graM4[0x100000 + i] << 4 |
              GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPwz (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (GraphicScreen.graM4[0x100000 + i] << 4 |
              GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPls (int a) {
      int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      a += 2;
      int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (GraphicScreen.graM4[0x100000 + i0] << 20 |
              GraphicScreen.graM4[           i0] << 16 |
              GraphicScreen.graM4[0x100000 + i1] <<  4 |
              GraphicScreen.graM4[           i1]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (byte) ((a & 1) == 0 ? 0 :
                      GraphicScreen.graM4[0x100000 + i] << 4 |
                      GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return ((a & 1) == 0 ? 0 :
               GraphicScreen.graM4[0x100000 + i] << 4 |
               GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (GraphicScreen.graM4[0x100000 + i] << 4 |
               GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (GraphicScreen.graM4[0x100000 + i] << 4 |
               GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       a += 2;
       int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (GraphicScreen.graM4[0x100000 + i0] << 20 |
               GraphicScreen.graM4[           i0] << 16 |
               GraphicScreen.graM4[0x100000 + i1] <<  4 |
               GraphicScreen.graM4[           i1]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       if ((a & 1) != 0) {
         CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
         int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
         GraphicScreen.graM4[0x100000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[           i] = (byte) ( d       & 15);
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       GraphicScreen.graM4[0x100000 + i] = (byte) ((d >> 4) & 15);
       GraphicScreen.graM4[           i] = (byte) ( d       & 15);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       a += 2;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       GraphicScreen.graM4[0x100000 + i0] = (byte) ((d >> 20) & 15);
       GraphicScreen.graM4[           i0] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0x100000 + i1] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[           i1] = (byte) ( d        & 15);
     }
  },  //MMD_GI0

  //--------------------------------------------------------------------------------
  //MMD_GJ0 グラフィックス画面(1024ドット65536色)
  //
  //  1024ドット65536色
  //         ------------------参照------------------        --------------格納--------------
  //    GJ0  0x00c00000~0x00dfffff  fedcba9876543210  ─┬  0x00000000~0x000fffff  ....3210
  //                                                     ├  0x00100000~0x001fffff  ....7654
  //                                                     ├  0x00200000~0x002fffff  ....ba98
  //                                                     └  0x00300000~0x003fffff  ....fedc
  //    参照  00000000 110Yyyyy yyyyyXxx xxxxxxx1
  //    格納  00000000 0000YXyy yyyyyyyx xxxxxxxx  i=0x000000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //          00000000 0001YXyy yyyyyyyx xxxxxxxx  i=0x100000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //    参照  00000000 110Yyyyy yyyyyXxx xxxxxxx0
  //    格納  00000000 0010YXyy yyyyyyyx xxxxxxxx  i=0x200000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //          00000000 0011YXyy yyyyyyyx xxxxxxxx  i=0x300000|((a>>1)&0x801ff)|((a<<8)&0x40000)|((a>>2)&0x3fe00)
  //
  MMD_GJ0 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "グラフィックス画面 (1024 ドット 65536 色)" : "Graphics Screen (1024 dots 65536 colors)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (byte) ((a & 1) == 0 ?
                     GraphicScreen.graM4[0x300000 + i] << 4 |
                     GraphicScreen.graM4[0x200000 + i] :
                     GraphicScreen.graM4[0x100000 + i] << 4 |
                     GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPbz (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return ((a & 1) == 0 ?
              GraphicScreen.graM4[0x300000 + i] << 4 |
              GraphicScreen.graM4[0x200000 + i] :
              GraphicScreen.graM4[0x100000 + i] << 4 |
              GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPws (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (short) (GraphicScreen.graM4[0x300000 + i] << 12 |
                      GraphicScreen.graM4[0x200000 + i] <<  8 |
                      GraphicScreen.graM4[0x100000 + i] <<  4 |
                      GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPwz (int a) {
      int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (GraphicScreen.graM4[0x300000 + i] << 12 |
              GraphicScreen.graM4[0x200000 + i] <<  8 |
              GraphicScreen.graM4[0x100000 + i] <<  4 |
              GraphicScreen.graM4[           i]);
    }
    @Override protected int mmdPls (int a) {
      int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      a += 2;
      int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
      return (GraphicScreen.graM4[0x300000 + i0] << 28 |
              GraphicScreen.graM4[0x200000 + i0] << 24 |
              GraphicScreen.graM4[0x100000 + i0] << 20 |
              GraphicScreen.graM4[           i0] << 16 |
              GraphicScreen.graM4[0x300000 + i1] << 12 |
              GraphicScreen.graM4[0x200000 + i1] <<  8 |
              GraphicScreen.graM4[0x100000 + i1] <<  4 |
              GraphicScreen.graM4[           i1]);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (byte) ((a & 1) == 0 ?
                      GraphicScreen.graM4[0x300000 + i] << 4 |
                      GraphicScreen.graM4[0x200000 + i] :
                      GraphicScreen.graM4[0x100000 + i] << 4 |
                      GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return ((a & 1) == 0 ?
               GraphicScreen.graM4[0x300000 + i] << 4 |
               GraphicScreen.graM4[0x200000 + i] :
               GraphicScreen.graM4[0x100000 + i] << 4 |
               GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (short) (GraphicScreen.graM4[0x300000 + i] << 12 |
                       GraphicScreen.graM4[0x200000 + i] <<  8 |
                       GraphicScreen.graM4[0x100000 + i] <<  4 |
                       GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (GraphicScreen.graM4[0x300000 + i] << 12 |
               GraphicScreen.graM4[0x200000 + i] <<  8 |
               GraphicScreen.graM4[0x100000 + i] <<  4 |
               GraphicScreen.graM4[           i]);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       a += 2;
       int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       return (GraphicScreen.graM4[0x300000 + i0] << 28 |
               GraphicScreen.graM4[0x200000 + i0] << 24 |
               GraphicScreen.graM4[0x100000 + i0] << 20 |
               GraphicScreen.graM4[           i0] << 16 |
               GraphicScreen.graM4[0x300000 + i1] << 12 |
               GraphicScreen.graM4[0x200000 + i1] <<  8 |
               GraphicScreen.graM4[0x100000 + i1] <<  4 |
               GraphicScreen.graM4[           i1]);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       if ((a & 1) == 0) {
         GraphicScreen.graM4[0x300000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[0x200000 + i] = (byte) ( d       & 15);
       } else {
         GraphicScreen.graM4[0x100000 + i] = (byte) ((d >> 4) & 15);
         GraphicScreen.graM4[           i] = (byte) ( d       & 15);
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       GraphicScreen.graM4[0x300000 + i] = (byte) ((char) d >> 12);
       GraphicScreen.graM4[0x200000 + i] = (byte) ((d >>  8) & 15);
       GraphicScreen.graM4[0x100000 + i] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[           i] = (byte) ( d        & 15);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.gvram * 2;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i0 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       a += 2;
       CRTC.crtRasterStamp[(a >> 11) - CRTC.crtR13GrYCurr[0] & 1023] = 0;
       int i1 = ((a >> 1) & 0x801ff) | ((a << 8) & 0x40000) | ((a >> 2) & 0x3fe00);
       GraphicScreen.graM4[0x300000 + i0] = (byte) (d >>> 28);
       GraphicScreen.graM4[0x200000 + i0] = (byte) ((d >> 24) & 15);
       GraphicScreen.graM4[0x100000 + i0] = (byte) ((d >> 20) & 15);
       GraphicScreen.graM4[           i0] = (byte) ((d >> 16) & 15);
       GraphicScreen.graM4[0x300000 + i1] = (byte) ((char) d >> 12);
       GraphicScreen.graM4[0x200000 + i1] = (byte) ((d >>  8) & 15);
       GraphicScreen.graM4[0x100000 + i1] = (byte) ((d >>  4) & 15);
       GraphicScreen.graM4[           i1] = (byte) ( d        & 15);
     }
  },  //MMD_GJ0

  //--------------------------------------------------------------------------------
  //MMD_TXT テキスト画面
  MMD_TXT {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "テキスト画面" : "Text Screen";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a];
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getShort (a);
      } else {
        return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
      }
    }
    @Override protected int mmdPwz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getChar (a);
      } else {
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
    }
    @Override protected int mmdPls (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getInt (a);
      } else {
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram;
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram;
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getShort (a);
       } else {
         return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getChar (a);
       } else {
         return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram * 2;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getInt (a);
       } else {
         return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram;
       a &= XEiJ.BUS_MOTHER_MASK;
       int x;  //マスク
       if (CRTC.crtSimAccess) {  //同時アクセスあり
         a &= 0x00e1ffff;
         if (CRTC.crtBitMask) {  //同時アクセスあり,ビットマスクあり
           d &= ~(x = CRTC.crtR23Mask >> ((~a & 1) << 3));
           if (CRTC.crtSimPlane0) {
             MainMemory.mmrM8[a             ] = (byte) (MainMemory.mmrM8[a             ] & x | d);
           }
           if (CRTC.crtSimPlane1) {
             MainMemory.mmrM8[a + 0x00020000] = (byte) (MainMemory.mmrM8[a + 0x00020000] & x | d);
           }
           if (CRTC.crtSimPlane2) {
             MainMemory.mmrM8[a + 0x00040000] = (byte) (MainMemory.mmrM8[a + 0x00040000] & x | d);
           }
           if (CRTC.crtSimPlane3) {
             MainMemory.mmrM8[a + 0x00060000] = (byte) (MainMemory.mmrM8[a + 0x00060000] & x | d);
           }
         } else {  //同時アクセスあり,ビットマスクなし
           if (CRTC.crtSimPlane0) {
             MainMemory.mmrM8[a             ] = (byte) d;
           }
           if (CRTC.crtSimPlane1) {
             MainMemory.mmrM8[a + 0x00020000] = (byte) d;
           }
           if (CRTC.crtSimPlane2) {
             MainMemory.mmrM8[a + 0x00040000] = (byte) d;
           }
           if (CRTC.crtSimPlane3) {
             MainMemory.mmrM8[a + 0x00060000] = (byte) d;
           }
         }
       } else if (CRTC.crtBitMask) {  //同時アクセスなし,ビットマスクあり
         x = CRTC.crtR23Mask >> ((~a & 1) << 3);
         MainMemory.mmrM8[a] = (byte) (MainMemory.mmrM8[a] & x | d & ~x);
       } else {  //同時アクセスなし,ビットマスクなし
         MainMemory.mmrM8[a] = (byte) d;
       }
       //同時アクセスやビットマスクで1ピクセルも書き換えなくても更新することになる
       a = ((a & 0x0001ffff) >> 7) - CRTC.crtR11TxYCurr & 1020;
       CRTC.crtRasterStamp[a    ] = 0;
       CRTC.crtRasterStamp[a + 1] = 0;
       CRTC.crtRasterStamp[a + 2] = 0;
       CRTC.crtRasterStamp[a + 3] = 0;
     }  //mmdWb
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram;
       a &= XEiJ.BUS_MOTHER_MASK;
       int e;  //上位バイトのデータ
       int x;  //下位バイトのマスク
       int y;  //上位バイトのマスク
       if (CRTC.crtSimAccess) {  //同時アクセスあり
         a &= 0x00e1ffff;
         if (CRTC.crtBitMask) {  //同時アクセスあり,ビットマスクあり
           e = d >> 8 & ~(y = (x = CRTC.crtR23Mask) >> 8);
           d &= ~x;
           if (CRTC.crtSimPlane0) {
             MainMemory.mmrM8[a             ] = (byte) (MainMemory.mmrM8[a             ] & y | e);
             MainMemory.mmrM8[a + 0x00000001] = (byte) (MainMemory.mmrM8[a + 0x00000001] & x | d);
           }
           if (CRTC.crtSimPlane1) {
             MainMemory.mmrM8[a + 0x00020000] = (byte) (MainMemory.mmrM8[a + 0x00020000] & y | e);
             MainMemory.mmrM8[a + 0x00020001] = (byte) (MainMemory.mmrM8[a + 0x00020001] & x | d);
           }
           if (CRTC.crtSimPlane2) {
             MainMemory.mmrM8[a + 0x00040000] = (byte) (MainMemory.mmrM8[a + 0x00040000] & y | e);
             MainMemory.mmrM8[a + 0x00040001] = (byte) (MainMemory.mmrM8[a + 0x00040001] & x | d);
           }
           if (CRTC.crtSimPlane3) {
             MainMemory.mmrM8[a + 0x00060000] = (byte) (MainMemory.mmrM8[a + 0x00060000] & y | e);
             MainMemory.mmrM8[a + 0x00060001] = (byte) (MainMemory.mmrM8[a + 0x00060001] & x | d);
           }
         } else {  //同時アクセスあり,ビットマスクなし
           e = d >> 8;
           if (CRTC.crtSimPlane0) {
             MainMemory.mmrM8[a             ] = (byte) e;
             MainMemory.mmrM8[a + 0x00000001] = (byte) d;
           }
           if (CRTC.crtSimPlane1) {
             MainMemory.mmrM8[a + 0x00020000] = (byte) e;
             MainMemory.mmrM8[a + 0x00020001] = (byte) d;
           }
           if (CRTC.crtSimPlane2) {
             MainMemory.mmrM8[a + 0x00040000] = (byte) e;
             MainMemory.mmrM8[a + 0x00040001] = (byte) d;
           }
           if (CRTC.crtSimPlane3) {
             MainMemory.mmrM8[a + 0x00060000] = (byte) e;
             MainMemory.mmrM8[a + 0x00060001] = (byte) d;
           }
         }
       } else if (CRTC.crtBitMask) {  //同時アクセスなし,ビットマスクあり
         y = (x = CRTC.crtR23Mask) >> 8;
         MainMemory.mmrM8[a    ] = (byte) (MainMemory.mmrM8[a    ] & y | (d >> 8) & ~y);
         MainMemory.mmrM8[a + 1] = (byte) (MainMemory.mmrM8[a + 1] & x |  d       & ~x);
       } else {  //同時アクセスなし,ビットマスクなし
         if (MainMemory.MMR_USE_BYTE_BUFFER) {
           MainMemory.mmrBuffer.putShort (a, (short) d);
         } else {
           MainMemory.mmrM8[a    ] = (byte) (d >> 8);
           MainMemory.mmrM8[a + 1] = (byte)  d;
         }
       }
       //同時アクセスやビットマスクで1ピクセルも書き換えなくても更新することになる
       a = ((a & 0x0001ffff) >> 7) - CRTC.crtR11TxYCurr & 1020;
       CRTC.crtRasterStamp[a    ] = 0;
       CRTC.crtRasterStamp[a + 1] = 0;
       CRTC.crtRasterStamp[a + 2] = 0;
       CRTC.crtRasterStamp[a + 3] = 0;
     }  //mmdWw
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.tvram * 2;
       a &= XEiJ.BUS_MOTHER_MASK;
       int e;  //下位ワードの上位バイトのデータ
       int f;  //上位ワードの下位バイトのデータ
       int g;  //上位ワードの上位バイトのデータ
       int x;  //下位バイトのマスク
       int y;  //上位バイトのマスク
       if (CRTC.crtSimAccess) {  //同時アクセスあり
         a &= 0x00e1ffff;
         if (CRTC.crtBitMask) {  //同時アクセスあり,ビットマスクあり
           g = d >> 24 & ~(y = (x = CRTC.crtR23Mask) >> 8);
           f = d >> 16 & ~x;
           e = d >>  8 & ~y;
           d &= ~x;
           if (CRTC.crtSimPlane0) {
             MainMemory.mmrM8[a             ] = (byte) (MainMemory.mmrM8[a             ] & y | g);
             MainMemory.mmrM8[a + 0x00000001] = (byte) (MainMemory.mmrM8[a + 0x00000001] & x | f);
             MainMemory.mmrM8[a + 0x00000002] = (byte) (MainMemory.mmrM8[a + 0x00000002] & y | e);
             MainMemory.mmrM8[a + 0x00000003] = (byte) (MainMemory.mmrM8[a + 0x00000003] & x | d);
           }
           if (CRTC.crtSimPlane1) {
             MainMemory.mmrM8[a + 0x00020000] = (byte) (MainMemory.mmrM8[a + 0x00020000] & y | g);
             MainMemory.mmrM8[a + 0x00020001] = (byte) (MainMemory.mmrM8[a + 0x00020001] & x | f);
             MainMemory.mmrM8[a + 0x00020002] = (byte) (MainMemory.mmrM8[a + 0x00020002] & y | e);
             MainMemory.mmrM8[a + 0x00020003] = (byte) (MainMemory.mmrM8[a + 0x00020003] & x | d);
           }
           if (CRTC.crtSimPlane2) {
             MainMemory.mmrM8[a + 0x00040000] = (byte) (MainMemory.mmrM8[a + 0x00040000] & y | g);
             MainMemory.mmrM8[a + 0x00040001] = (byte) (MainMemory.mmrM8[a + 0x00040001] & x | f);
             MainMemory.mmrM8[a + 0x00040002] = (byte) (MainMemory.mmrM8[a + 0x00040002] & y | e);
             MainMemory.mmrM8[a + 0x00040003] = (byte) (MainMemory.mmrM8[a + 0x00040003] & x | d);
           }
           if (CRTC.crtSimPlane3) {
             MainMemory.mmrM8[a + 0x00060000] = (byte) (MainMemory.mmrM8[a + 0x00060000] & y | g);
             MainMemory.mmrM8[a + 0x00060001] = (byte) (MainMemory.mmrM8[a + 0x00060001] & x | f);
             MainMemory.mmrM8[a + 0x00060002] = (byte) (MainMemory.mmrM8[a + 0x00060002] & y | e);
             MainMemory.mmrM8[a + 0x00060003] = (byte) (MainMemory.mmrM8[a + 0x00060003] & x | d);
           }
         } else {  //同時アクセスあり,ビットマスクなし
           g = d >> 24;
           f = d >> 16;
           e = d >>  8;
           if (CRTC.crtSimPlane0) {
             MainMemory.mmrM8[a             ] = (byte) g;
             MainMemory.mmrM8[a + 0x00000001] = (byte) f;
             MainMemory.mmrM8[a + 0x00000002] = (byte) e;
             MainMemory.mmrM8[a + 0x00000003] = (byte) d;
           }
           if (CRTC.crtSimPlane1) {
             MainMemory.mmrM8[a + 0x00020000] = (byte) g;
             MainMemory.mmrM8[a + 0x00020001] = (byte) f;
             MainMemory.mmrM8[a + 0x00020002] = (byte) e;
             MainMemory.mmrM8[a + 0x00020003] = (byte) d;
           }
           if (CRTC.crtSimPlane2) {
             MainMemory.mmrM8[a + 0x00040000] = (byte) g;
             MainMemory.mmrM8[a + 0x00040001] = (byte) f;
             MainMemory.mmrM8[a + 0x00040002] = (byte) e;
             MainMemory.mmrM8[a + 0x00040003] = (byte) d;
           }
           if (CRTC.crtSimPlane3) {
             MainMemory.mmrM8[a + 0x00060000] = (byte) g;
             MainMemory.mmrM8[a + 0x00060001] = (byte) f;
             MainMemory.mmrM8[a + 0x00060002] = (byte) e;
             MainMemory.mmrM8[a + 0x00060003] = (byte) d;
           }
         }
       } else if (CRTC.crtBitMask) {  //同時アクセスなし,ビットマスクあり
         y = (x = CRTC.crtR23Mask) >> 8;
         MainMemory.mmrM8[a    ] = (byte) (MainMemory.mmrM8[a    ] & y | (d >> 24) & ~y);
         MainMemory.mmrM8[a + 1] = (byte) (MainMemory.mmrM8[a + 1] & x | (d >> 16) & ~x);
         MainMemory.mmrM8[a + 2] = (byte) (MainMemory.mmrM8[a + 1] & y | (d >>  8) & ~y);
         MainMemory.mmrM8[a + 3] = (byte) (MainMemory.mmrM8[a + 1] & x |  d        & ~x);
       } else {  //同時アクセスなし,ビットマスクなし
         if (MainMemory.MMR_USE_BYTE_BUFFER) {
           MainMemory.mmrBuffer.putInt (a, d);
         } else {
           MainMemory.mmrM8[a    ] = (byte) (d >> 24);
           MainMemory.mmrM8[a + 1] = (byte) (d >> 16);
           MainMemory.mmrM8[a + 2] = (byte) (d >>  8);
           MainMemory.mmrM8[a + 3] = (byte)  d;
         }
       }
       //同時アクセスやビットマスクで1ピクセルも書き換えなくても更新することになる
       int b = ((a     & 0x0001ffff) >> 7) - CRTC.crtR11TxYCurr & 1020;
       CRTC.crtRasterStamp[b    ] = 0;
       CRTC.crtRasterStamp[b + 1] = 0;
       CRTC.crtRasterStamp[b + 2] = 0;
       CRTC.crtRasterStamp[b + 3] = 0;
       a = ((a + 2 & 0x0001ffff) >> 7) - CRTC.crtR11TxYCurr & 1020;
       if (a != b) {
         CRTC.crtRasterStamp[a    ] = 0;
         CRTC.crtRasterStamp[a + 1] = 0;
         CRTC.crtRasterStamp[a + 2] = 0;
         CRTC.crtRasterStamp[a + 3] = 0;
       }
     }  //mmdWl
  },  //MMD_TXT

  //--------------------------------------------------------------------------------
  //MMD_CRT CRTコントローラ
  //
  //  $00E80000~$00E8002F  ワードレジスタ
  //  $00E80030~$00E8003F  $0000
  //  $00E80040~$00E803FF  $00E80000~$00E8003Fの繰り返し
  //  $00E80400~$00E8047F  バスエラー
  //  $00E80480~$00E80481  ワードレジスタ
  //  $00E80482~$00E804FF  $00E80480~$00E80481の繰り返し
  //  $00E80500~$00E807FF  $00E80400~$00E804FFの繰り返し
  //  $00E80800~$00E81FFF  $00E80000~$00E807FFの繰り返し
  //
  MMD_CRT {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "CRT コントローラ" : "CRT Controller";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.crtc;
       int aa = a & 0x07ff;  //$00E80800~$00E81FFF→$00E80000~$00E807FF
       if (aa < 0x0400) {  //$00E80000~$00E803FF
         aa &= 0x003f;
         switch (aa) {
           //case 0x0001:  //R00の下位
           //  return CRTC.crtR00HFrontEndPort;
           //case 0x0003:  //R01の下位
           //  return CRTC.crtR01HSyncEndPort;
           //case 0x0005:  //R02の下位
           //  return CRTC.crtR02HBackEndPort;
           //case 0x0007:  //R03の下位
           //  return CRTC.crtR03HDispEndPort;
           //case 0x0008:  //R04の上位
           //  return CRTC.crtR04VFrontEndPort >> 8;
           //case 0x0009:  //R04の下位
           //  return CRTC.crtR04VFrontEndPort & 0xff;
           //case 0x000a:  //R05の上位
           //  return CRTC.crtR05VSyncEndPort >> 8;
           //case 0x000b:  //R05の下位
           //  return CRTC.crtR05VSyncEndPort & 0xff;
           //case 0x000c:  //R06の上位
           //  return CRTC.crtR06VBackEndPort >> 8;
           //case 0x000d:  //R06の下位
           //  return CRTC.crtR06VBackEndPort & 0xff;
           //case 0x000e:  //R07の上位
           //  return CRTC.crtR07VDispEndPort >> 8;
           //case 0x000f:  //R07の下位
           //  return CRTC.crtR07VDispEndPort & 0xff;
           //case 0x0011:  //R08の下位
           //  return CRTC.crtR08Adjust;
           //case 0x0012:  //R09の上位
           //  return CRTC.crtR09IRQRasterPort >> 8;
           //case 0x0013:  //R09の下位
           //  return CRTC.crtR09IRQRasterPort & 0xff;
           //case 0x0014:  //R10の上位
           //  return CRTC.crtR10TxXPort >> 8;
           //case 0x0015:  //R10の下位
           //  return CRTC.crtR10TxXPort & 0xff;
           //case 0x0016:  //R11の上位
           //  return CRTC.crtR11TxYCurr >> 8;
           //case 0x0017:  //R11の下位
           //  return CRTC.crtR11TxYCurr & 0xff;
           //case 0x0018:  //R12の上位
           //case 0x001c:  //R14の上位
           //case 0x0020:  //R16の上位
           //case 0x0024:  //R18の上位
           //  return CRTC.crtR12GrXPort[(aa - 0x0018) >> 2] >> 8;
           //case 0x0019:  //R12の下位
           //case 0x001d:  //R14の下位
           //case 0x0021:  //R16の下位
           //case 0x0025:  //R18の下位
           //  return CRTC.crtR12GrXPort[(aa - 0x0018) >> 2] & 0xff;
           //case 0x001a:  //R13の上位
           //case 0x001e:  //R15の上位
           //case 0x0022:  //R17の上位
           //case 0x0026:  //R19の上位
           //  return CRTC.crtR13GrYPort[(aa - 0x0018) >> 2] >> 8;
           //case 0x001b:  //R13の下位
           //case 0x001f:  //R15の下位
           //case 0x0023:  //R17の下位
           //case 0x0027:  //R19の下位
           //  return CRTC.crtR13GrYPort[(aa - 0x0018) >> 2] & 0xff;
         case 0x0028:  //R20の上位
           return (CRTC.crtTextStorage    << 4 |
                   CRTC.crtGraphicStorage << 3 |
                   CRTC.crtMemoryModePort);
         case 0x0029:  //R20の下位
           return (CRTC.crtHighResoPort << 4 |
                   CRTC.crtVResoPort    << 2 |
                   CRTC.crtHResoPort);
         case 0x002a:  //R21の上位
           return ((CRTC.crtBitMask   ? 0b00000010 : 0) |
                   (CRTC.crtSimAccess ? 0b00000001 : 0));
         case 0x002b:  //R21の下位
           return ((CRTC.crtSimPlane3 ? 0b10000000 : 0) |
                   (CRTC.crtSimPlane2 ? 0b01000000 : 0) |
                   (CRTC.crtSimPlane1 ? 0b00100000 : 0) |
                   (CRTC.crtSimPlane0 ? 0b00010000 : 0) |
                   (CRTC.crtCCPlane3  ? 0b00001000 : 0) |
                   (CRTC.crtCCPlane2  ? 0b00000100 : 0) |
                   (CRTC.crtCCPlane1  ? 0b00000010 : 0) |
                   (CRTC.crtCCPlane0  ? 0b00000001 : 0));
           //case 0x002c:  //R22の上位
           //  return CRTC.crtR22SrcBlock;
           //case 0x002d:  //R22の下位
           //  return CRTC.crtR22DstBlock;
           //case 0x002e:  //R23の上位
           //  return CRTC.crtR23Mask >> 8;
           //case 0x002f:  //R23の下位
           //  return CRTC.crtR23Mask & 0xff;
           //case 0x0000:  //R00の上位
           //case 0x0002:  //R01の上位
           //case 0x0004:  //R02の上位
           //case 0x0006:  //R03の上位
           //case 0x0010:  //R08の上位
           //case 0x0030:  //R24の上位
           //case 0x0031:  //R24の下位
           //case 0x0032:  //R25の上位
           //case 0x0033:  //R25の下位
           //case 0x0034:  //R26の上位
           //case 0x0035:  //R26の下位
           //case 0x0036:  //R27の上位
           //case 0x0037:  //R27の下位
           //case 0x0038:  //R28の上位
           //case 0x0039:  //R28の下位
           //case 0x003a:  //R29の上位
           //case 0x003b:  //R29の下位
           //case 0x003c:  //R30の上位
           //case 0x003d:  //R30の下位
           //case 0x003e:  //R31の上位
           //case 0x003f:  //R31の下位
         default:
           return 0x00;
           //return VideoController.vcnMode.ordinal () >> 8;
           //return VideoController.vcnMode.ordinal () & 0xff;
         }
       } else {  //$00E80400~$00E807FF
         aa &= 0xff;  //$00E80500~$00E807FF→$00E80400~$00E804FF
         if (aa < 0x80) {  //$00E80400~$00E8047F
           return super.mmdRbz (a);  //バスエラー
         } else {  //$00E80480~$00E804FF
           aa &= 0x01;  //$00E80482~$00E804FF→$00E80480~$00E80481
           if (aa == 0) {  //動作ポートの上位
             return 0;
           } else {  //動作ポートの下位
             return ((CRTC.crtRasterCopyOn ? 8 : 0) |  //ラスタコピー
                     (CRTC.crtClearFrames != 0 ? 2 : 0));  //高速クリア
           }
         }
       }
     }  //mmdRbz
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.crtc;
       int aa = a & 0x07ff;  //$00E80800~$00E81FFF→$00E80000~$00E807FF
       if (aa < 0x0400) {  //$00E80000~$00E803FF
         aa &= 0x003f;
         switch (aa) {
           //case 0x0000:  //R00
           //  return CRTC.crtR00HFrontEndPort;
           //case 0x0002:  //R01
           //  return CRTC.crtR01HSyncEndPort;
           //case 0x0004:  //R02
           //  return CRTC.crtR02HBackEndPort;
           //case 0x0006:  //R03
           //  return CRTC.crtR03HDispEndPort;
           //case 0x0008:  //R04
           //  return CRTC.crtR04VFrontEndPort;
           //case 0x000a:  //R05
           //  return CRTC.crtR05VSyncEndPort;
           //case 0x000c:  //R06
           //  return CRTC.crtR06VBackEndPort;
           //case 0x000e:  //R07
           //  return CRTC.crtR07VDispEndPort;
           //case 0x0010:  //R08
           //  return CRTC.crtR08Adjust;
           //case 0x0012:  //R09
           //  return CRTC.crtR09IRQRasterPort;
           //case 0x0014:  //R10
           //  return CRTC.crtR10TxXPort;
           //case 0x0016:  //R11
           //  return CRTC.crtR11TxYPort;
           //case 0x0018:  //R12
           //case 0x001c:  //R14
           //case 0x0020:  //R16
           //case 0x0024:  //R18
           //  return CRTC.crtR12GrXPort[(aa - 0x0018) >> 2];
           //case 0x001a:  //R13
           //case 0x001e:  //R15
           //case 0x0022:  //R17
           //case 0x0026:  //R19
           //  return CRTC.crtR13GrYPort[(aa - 0x0018) >> 2];
         case 0x0028:  //R20
           return (CRTC.crtTextStorage    << 12 |
                   CRTC.crtGraphicStorage << 11 |
                   CRTC.crtMemoryModePort <<  8 |
                   CRTC.crtHighResoPort   <<  4 |
                   CRTC.crtVResoPort      <<  2 |
                   CRTC.crtHResoPort);
         case 0x002a:  //R21
           return ((CRTC.crtBitMask   ? 0b00000010_00000000 : 0) |
                   (CRTC.crtSimAccess ? 0b00000001_00000000 : 0) |
                   (CRTC.crtSimPlane3 ? 0b00000000_10000000 : 0) |
                   (CRTC.crtSimPlane2 ? 0b00000000_01000000 : 0) |
                   (CRTC.crtSimPlane1 ? 0b00000000_00100000 : 0) |
                   (CRTC.crtSimPlane0 ? 0b00000000_00010000 : 0) |
                   (CRTC.crtCCPlane3  ? 0b00000000_00001000 : 0) |
                   (CRTC.crtCCPlane2  ? 0b00000000_00000100 : 0) |
                   (CRTC.crtCCPlane1  ? 0b00000000_00000010 : 0) |
                   (CRTC.crtCCPlane0  ? 0b00000000_00000001 : 0));
           //case 0x002c:  //R22
           //  return CRTC.crtR22SrcBlock << 8 | CRTC.crtR22DstBlock;
           //case 0x002e:  //R23
           //  return CRTC.crtR23Mask;
           //case 0x0030:  //R24
           //case 0x0032:  //R25
           //case 0x0034:  //R26
           //case 0x0036:  //R27
           //case 0x0038:  //R28
           //case 0x003a:  //R29
           //case 0x003c:  //R30
           //case 0x003e:  //R31
         default:
           return 0x0000;
         }
       } else {  //$00E80400~$00E807FF
         aa &= 0xff;  //$00E80500~$00E807FF→$00E80400~$00E804FF
         if (aa < 0x80) {  //$00E80400~$00E8047F
           return super.mmdRbz (a);  //バスエラー
         } else {  //$00E80480~$00E804FF  動作ポート
           return ((CRTC.crtRasterCopyOn ? 8 : 0) |  //ラスタコピー
                   (CRTC.crtClearFrames != 0 ? 2 : 0));  //高速クリア
         }
       }
     }  //mmdRwz
    @Override protected int mmdRls (int a) throws M68kException {
       return mmdRwz (a) << 16 | mmdRwz (a + 2);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.crtc;
       int aa = a & 0x07ff;  //$00E80800~$00E81FFF→$00E80000~$00E807FF
       if (aa < 0x0400) {  //$00E80000~$00E803FF
         aa &= 0x003f;
         switch (aa) {
         case 0x0001:  //R00の下位
           CRTC.crtR00HFrontEndPort = (d & 0xff) | (CRTC.crtR00Bit0Zero ? 0x00 : 0x01);
           {
             int curr = CRTC.crtR00HFrontEndMask == 0 ? CRTC.crtR00HFrontEndPort : CRTC.crtR00HFrontEndTest;
             if (CRTC.crtR00HFrontEndCurr != curr) {
               CRTC.crtR00HFrontEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
         return;
         case 0x0003:  //R01の下位
           CRTC.crtR01HSyncEndPort = d & 0xff;
           {
             int curr = CRTC.crtR01HSyncEndMask == 0 ? CRTC.crtR01HSyncEndPort : CRTC.crtR01HSyncEndTest;
             if (CRTC.crtR01HSyncEndCurr != curr) {
               CRTC.crtR01HSyncEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0005:  //R02の下位
           CRTC.crtR02HBackEndPort = d & 0xff;
           {
             int curr = CRTC.crtR02HBackEndMask == 0 ? CRTC.crtR02HBackEndPort : CRTC.crtR02HBackEndTest;
             if (CRTC.crtR02HBackEndCurr != curr) {
               CRTC.crtR02HBackEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0007:  //R03の下位
           CRTC.crtR03HDispEndPort = d & 0xff;
           {
             int curr = CRTC.crtR03HDispEndMask == 0 ? CRTC.crtR03HDispEndPort : CRTC.crtR03HDispEndTest;
             if (CRTC.crtR03HDispEndCurr != curr) {
               CRTC.crtR03HDispEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0008:  //R04の上位
           CRTC.crtR04VFrontEndPort = (d & (CRTC.crtVerticalMask >> 8)) << 8 | (CRTC.crtR04VFrontEndPort & 0xff);
           {
             int curr = CRTC.crtR04VFrontEndMask == 0 ? CRTC.crtR04VFrontEndPort : CRTC.crtR04VFrontEndTest;
             if (CRTC.crtR04VFrontEndCurr != curr) {
               CRTC.crtR04VFrontEndCurr = curr;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0009:  //R04の下位
           CRTC.crtR04VFrontEndPort = (CRTC.crtR04VFrontEndPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR04VFrontEndMask == 0 ? CRTC.crtR04VFrontEndPort : CRTC.crtR04VFrontEndTest;
             if (CRTC.crtR04VFrontEndCurr != curr) {
               CRTC.crtR04VFrontEndCurr = curr;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000a:  //R05の上位
           CRTC.crtR05VSyncEndPort = (d & (CRTC.crtVerticalMask >> 8)) << 8 | (CRTC.crtR05VSyncEndPort & 0xff);
           {
             int curr = CRTC.crtR05VSyncEndMask == 0 ? CRTC.crtR05VSyncEndPort : CRTC.crtR05VSyncEndTest;
             if (CRTC.crtR05VSyncEndCurr != curr) {
               CRTC.crtR05VSyncEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000b:  //R05の下位
           CRTC.crtR05VSyncEndPort = (CRTC.crtR05VSyncEndPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR05VSyncEndMask == 0 ? CRTC.crtR05VSyncEndPort : CRTC.crtR05VSyncEndTest;
             if (CRTC.crtR05VSyncEndCurr != curr) {
               CRTC.crtR05VSyncEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000c:  //R06の上位
           CRTC.crtR06VBackEndPort = (d & (CRTC.crtVerticalMask >> 8)) << 8 | (CRTC.crtR06VBackEndPort & 0xff);
           {
             int curr = CRTC.crtR06VBackEndMask == 0 ? CRTC.crtR06VBackEndPort : CRTC.crtR06VBackEndTest;
             if (CRTC.crtR06VBackEndCurr != curr) {
               CRTC.crtR06VBackEndCurr = curr;
               CRTC.crtVDispStart = curr + 1;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000d:  //R06の下位
           CRTC.crtR06VBackEndPort = (CRTC.crtR06VBackEndPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR06VBackEndMask == 0 ? CRTC.crtR06VBackEndPort : CRTC.crtR06VBackEndTest;
             if (CRTC.crtR06VBackEndCurr != curr) {
               CRTC.crtR06VBackEndCurr = curr;
               CRTC.crtVDispStart = curr + 1;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000e:  //R07の上位
           CRTC.crtR07VDispEndPort = (d & (CRTC.crtVerticalMask >> 8)) << 8 | (CRTC.crtR07VDispEndPort & 0xff);
           {
             int curr = CRTC.crtR07VDispEndMask == 0 ? CRTC.crtR07VDispEndPort : CRTC.crtR07VDispEndTest;
             if (CRTC.crtR07VDispEndCurr != curr) {
               CRTC.crtR07VDispEndCurr = curr;
               CRTC.crtVIdleStart = curr + 1;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000f:  //R07の下位
           CRTC.crtR07VDispEndPort = (CRTC.crtR07VDispEndPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR07VDispEndMask == 0 ? CRTC.crtR07VDispEndPort : CRTC.crtR07VDispEndTest;
             if (CRTC.crtR07VDispEndCurr != curr) {
               CRTC.crtR07VDispEndCurr = curr;
               CRTC.crtVIdleStart = curr + 1;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0011:  //R08の下位
           d &= 0xff;
           if (CRTC.crtR08Adjust != d) {
             CRTC.crtR08Adjust = d;
             CRTC.crtRestart ();
           }
           return;
         case 0x0012:  //R09の上位
           CRTC.crtR09IRQRasterPort = (d & (CRTC.crtVerticalMask >> 8)) << 8 | (CRTC.crtR09IRQRasterPort & 0xff);
           {
             int curr = CRTC.crtR09IRQRasterMask == 0 ? CRTC.crtR09IRQRasterPort : CRTC.crtR09IRQRasterTest;
             if (CRTC.crtR09IRQRasterCurr != curr) {
               CRTC.crtR09IRQRasterCurr = curr;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               if (RasterBreakPoint.RBP_ON) {
                 RasterBreakPoint.rbpCheckIRQ ();
               }
             }
           }
           return;
         case 0x0013:  //R09の下位
           CRTC.crtR09IRQRasterPort = (CRTC.crtR09IRQRasterPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR09IRQRasterMask == 0 ? CRTC.crtR09IRQRasterPort : CRTC.crtR09IRQRasterTest;
             if (CRTC.crtR09IRQRasterCurr != curr) {
               CRTC.crtR09IRQRasterCurr = curr;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               if (RasterBreakPoint.RBP_ON) {
                 RasterBreakPoint.rbpCheckIRQ ();
               }
             }
           }
           return;
         case 0x0014:  //R10の上位
           CRTC.crtR10TxXPort = (d & 0x03) << 8 | (CRTC.crtR10TxXPort & 0xff);
           {
             int curr = CRTC.crtR10TxXMask == 0 ? CRTC.crtR10TxXPort : CRTC.crtR10TxXTest;
             if (CRTC.crtR10TxXCurr != curr) {
               CRTC.crtR10TxXCurr = curr;
               CRTC.crtAllStamp += 2;
             }
           }
           return;
         case 0x0015:  //R10の下位
           CRTC.crtR10TxXPort = (CRTC.crtR10TxXPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR10TxXMask == 0 ? CRTC.crtR10TxXPort : CRTC.crtR10TxXTest;
             if (CRTC.crtR10TxXCurr != curr) {
               CRTC.crtR10TxXCurr = curr;
               CRTC.crtAllStamp += 2;
             }
           }
           return;
         case 0x0016:  //R11の上位
           CRTC.crtR11TxYPort = (d & 0x03) << 8 | (CRTC.crtR11TxYPort & 0xff);
           {
             int curr = CRTC.crtR11TxYMask == 0 ? CRTC.crtR11TxYPort : CRTC.crtR11TxYTest;
             if (CRTC.crtR11TxYCurr != curr) {
               CRTC.crtR11TxYCurr = curr;
               //CRTC.crtAllStamp += 2;  //ラッチするとき更新する
             }
           }
           return;
         case 0x0017:  //R11の下位
           CRTC.crtR11TxYPort = (CRTC.crtR11TxYPort & ~0xff) | (d & 0xff);
           {
             int curr = CRTC.crtR11TxYMask == 0 ? CRTC.crtR11TxYPort : CRTC.crtR11TxYTest;
             if (CRTC.crtR11TxYCurr != curr) {
               CRTC.crtR11TxYCurr = curr;
               //CRTC.crtAllStamp += 2;  //ラッチするとき更新する
             }
           }
           return;
         case 0x0018:  //R12の上位
         case 0x001c:  //R14の上位
         case 0x0020:  //R16の上位
         case 0x0024:  //R18の上位
           {
             int p = (aa - 0x0018) >> 2;  //0,1,2,3
             CRTC.crtR12GrXPort[p] = (d & (p == 0 ? 0x03 : 0x01)) << 8 | (CRTC.crtR12GrXPort[p] & 0xff);
             int curr = CRTC.crtR12GrXMask[p] == 0 ? CRTC.crtR12GrXPort[p] : CRTC.crtR12GrXTest[p];
             if (CRTC.crtR12GrXCurr[p] != curr) {
               CRTC.crtR12GrXCurr[p] = curr;
               CRTC.crtAllStamp += 2;
             }
           }
           return;
         case 0x0019:  //R12の下位
         case 0x001d:  //R14の下位
         case 0x0021:  //R16の下位
         case 0x0025:  //R18の下位
           {
             int p = (aa - 0x0018) >> 2;  //0,1,2,3
             CRTC.crtR12GrXPort[p] = (CRTC.crtR12GrXPort[p] & ~0xff) | (d & 0xff);
             int curr = CRTC.crtR12GrXMask[p] == 0 ? CRTC.crtR12GrXPort[p] : CRTC.crtR12GrXTest[p];
             if (CRTC.crtR12GrXCurr[p] != curr) {
               CRTC.crtR12GrXCurr[p] = curr;
               CRTC.crtAllStamp += 2;
             }
           }
           return;
         case 0x001a:  //R13の上位
         case 0x001e:  //R15の上位
         case 0x0022:  //R17の上位
         case 0x0026:  //R19の上位
           {
             int p = (aa - 0x0018) >> 2;  //0,1,2,3
             CRTC.crtR13GrYPort[p] = (d & (p == 0 ? 0x03 : 0x01)) << 8 | (CRTC.crtR13GrYPort[p] & 0xff);
             int curr = CRTC.crtR13GrYMask[p] == 0 ? CRTC.crtR13GrYPort[p] : CRTC.crtR13GrYTest[p];
             if (CRTC.crtR13GrYCurr[p] != curr) {
               CRTC.crtR13GrYCurr[p] = curr;
               //CRTC.crtAllStamp += 2;  //ラッチするとき更新する
             }
           }
           return;
         case 0x001b:  //R13の下位
         case 0x001f:  //R15の下位
         case 0x0023:  //R17の下位
         case 0x0027:  //R19の下位
           {
             int p = (aa - 0x0018) >> 2;  //0,1,2,3
             CRTC.crtR13GrYPort[p] = (CRTC.crtR13GrYPort[p] & ~0xff) | (d & 0xff);
             int curr = CRTC.crtR13GrYMask[p] == 0 ? CRTC.crtR13GrYPort[p] : CRTC.crtR13GrYTest[p];
             if (CRTC.crtR13GrYCurr[p] != curr) {
               CRTC.crtR13GrYCurr[p] = curr;
               //CRTC.crtAllStamp += 2;  //ラッチするとき更新する
             }
           }
           return;
         case 0x0028:  //R20の上位
           CRTC.crtSetMemoryMode (d >> 4, d >> 3, d);
           return;
         case 0x0029:  //R20の下位
           CRTC.crtHighResoPort = d >>> 4 & 1;
           CRTC.crtVResoPort    = d >>> 2 & 3;
           CRTC.crtHResoPort    = d       & 3;
           SpriteScreen.sprAccessible = SpriteScreen.spr768x512 || (d & 0b10010) != 0b10010;
           int highResoCurr = CRTC.crtHighResoMask == 0 ? CRTC.crtHighResoPort : CRTC.crtHighResoTest;
           int vResoCurr = CRTC.crtVResoMask == 0 ? CRTC.crtVResoPort : CRTC.crtVResoTest;
           int hResoCurr = CRTC.crtHResoMask == 0 ? CRTC.crtHResoPort : CRTC.crtHResoTest;
           if (CRTC.crtHighResoCurr != highResoCurr ||
               CRTC.crtVResoCurr != vResoCurr ||
               CRTC.crtHResoCurr != hResoCurr) {
             CRTC.crtHighResoCurr = highResoCurr;
             CRTC.crtVResoCurr = vResoCurr;
             CRTC.crtHResoCurr = hResoCurr;
             CRTC.crtRestart ();
           }
           return;
         case 0x002a:  //R21の上位
           CRTC.crtBitMask   = XEiJ.TEST_BIT_1_SHIFT ? d << 31 - 1 < 0 : (d & 2) != 0;
           CRTC.crtSimAccess = XEiJ.TEST_BIT_0_SHIFT ? d << 31 - 0 < 0 : (d & 1) != 0;
           return;
         case 0x002b:  //R21の下位
           CRTC.crtSimPlane3 = (byte) d < 0;  //(d & 128) != 0。d << 24 < 0
           CRTC.crtSimPlane2 = d << 25 < 0;  //(d & 64) != 0
           CRTC.crtSimPlane1 = d << 26 < 0;  //(d & 32) != 0
           CRTC.crtSimPlane0 = d << 27 < 0;  //(d & 16) != 0
           CRTC.crtCCPlane3  = XEiJ.TEST_BIT_3_SHIFT ? d << 31 - 3 < 0 : (d & 8) != 0;
           CRTC.crtCCPlane2  = XEiJ.TEST_BIT_2_SHIFT ? d << 31 - 2 < 0 : (d & 4) != 0;
           CRTC.crtCCPlane1  = XEiJ.TEST_BIT_1_SHIFT ? d << 31 - 1 < 0 : (d & 2) != 0;
           CRTC.crtCCPlane0  = XEiJ.TEST_BIT_0_SHIFT ? d << 31 - 0 < 0 : (d & 1) != 0;
           return;
         case 0x002c:  //R22の上位
           CRTC.crtR22SrcBlock = d & 0xff;
           return;
         case 0x002d:  //R22の下位
           CRTC.crtR22DstBlock = d & 0xff;
           return;
         case 0x002e:  //R23の上位
           CRTC.crtR23Mask = (d & 0xff) << 8 | (CRTC.crtR23Mask & 0xff);
           return;
         case 0x002f:  //R23の下位
           CRTC.crtR23Mask = (CRTC.crtR23Mask & ~0xff) | (d & 0xff);
           return;
           //case 0x0000:  //R00の上位
           //case 0x0002:  //R01の上位
           //case 0x0004:  //R02の上位
           //case 0x0006:  //R03の上位
           //case 0x0010:  //R08の上位
           //case 0x0030:  //R24の上位
           //case 0x0031:  //R24の下位
           //case 0x0032:  //R25の上位
           //case 0x0033:  //R25の下位
           //case 0x0034:  //R26の上位
           //case 0x0035:  //R26の下位
           //case 0x0036:  //R27の上位
           //case 0x0037:  //R27の下位
           //case 0x0038:  //R28の上位
           //case 0x0039:  //R28の下位
           //case 0x003a:  //R29の上位
           //case 0x003b:  //R29の下位
           //case 0x003c:  //R30の上位
           //case 0x003d:  //R30の下位
           //case 0x003e:  //R31の上位
           //case 0x003f:  //R31の下位
         default:
           return;
         }
       } else {  //$00E80400~$00E807FF
         aa &= 0xff;  //$00E80500~$00E807FF→$00E80400~$00E804FF
         if (aa < 0x80) {  //$00E80400~$00E8047F
           super.mmdWb (a, d);  //バスエラー
         } else {  //$00E80480~$00E804FF
           aa &= 0x01;  //$00E80482~$00E804FF→$00E80480~$00E80481
           if (aa == 0) {  //動作ポートの上位
             return;
           } else {  //動作ポートの下位
             boolean rasterCopyOn = (d & 8) != 0;
             if (CRTC.crtRasterCopyOn != rasterCopyOn) {
               CRTC.crtRasterCopyOn = rasterCopyOn;  //ラスタコピー
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
             }
             if (CRTC.crtClearFrames == 0) {
               CRTC.crtClearStandby = (d & 2) != 0;  //高速クリア
             }
             return;
           }
         }
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.crtc;
       int aa = a & 0x07ff;  //$00E80800~$00E81FFF→$00E80000~$00E807FF
       if (aa < 0x0400) {  //$00E80000~$00E803FF
         aa &= 0x003f;
         switch (aa) {
         case 0x0000:  //R00
           CRTC.crtR00HFrontEndPort = (d & 0xff) | (CRTC.crtR00Bit0Zero ? 0x00 : 0x01);
           {
             int curr = CRTC.crtR00HFrontEndMask == 0 ? CRTC.crtR00HFrontEndPort : CRTC.crtR00HFrontEndTest;
             if (CRTC.crtR00HFrontEndCurr != curr) {
               CRTC.crtR00HFrontEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0002:  //R01
           CRTC.crtR01HSyncEndPort = d & 0xff;
           {
             int curr = CRTC.crtR01HSyncEndMask == 0 ? CRTC.crtR01HSyncEndPort : CRTC.crtR01HSyncEndTest;
             if (CRTC.crtR01HSyncEndCurr != curr) {
               CRTC.crtR01HSyncEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0004:  //R02
           CRTC.crtR02HBackEndPort = d & 0xff;
           {
             int curr = CRTC.crtR02HBackEndMask == 0 ? CRTC.crtR02HBackEndPort : CRTC.crtR02HBackEndTest;
             if (CRTC.crtR02HBackEndCurr != curr) {
               CRTC.crtR02HBackEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0006:  //R03
           CRTC.crtR03HDispEndPort = d & 0xff;
           {
             int curr = CRTC.crtR03HDispEndMask == 0 ? CRTC.crtR03HDispEndPort : CRTC.crtR03HDispEndTest;
             if (CRTC.crtR03HDispEndCurr != curr) {
               CRTC.crtR03HDispEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0008:  //R04
           CRTC.crtR04VFrontEndPort = d & CRTC.crtVerticalMask;
           {
             int curr = CRTC.crtR04VFrontEndMask == 0 ? CRTC.crtR04VFrontEndPort : CRTC.crtR04VFrontEndTest;
             if (CRTC.crtR04VFrontEndCurr != curr) {
               CRTC.crtR04VFrontEndCurr = curr;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000a:  //R05
           CRTC.crtR05VSyncEndPort = d & CRTC.crtVerticalMask;
           {
             int curr = CRTC.crtR05VSyncEndMask == 0 ? CRTC.crtR05VSyncEndPort : CRTC.crtR05VSyncEndTest;
             if (CRTC.crtR05VSyncEndCurr != curr) {
               CRTC.crtR05VSyncEndCurr = curr;
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000c:  //R06
           CRTC.crtR06VBackEndPort = d & CRTC.crtVerticalMask;
           {
             int curr = CRTC.crtR06VBackEndMask == 0 ? CRTC.crtR06VBackEndPort : CRTC.crtR06VBackEndTest;
             if (CRTC.crtR06VBackEndCurr != curr) {
               CRTC.crtR06VBackEndCurr = curr;
               CRTC.crtVDispStart = curr + 1;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x000e:  //R07
           CRTC.crtR07VDispEndPort = d & CRTC.crtVerticalMask;
           {
             int curr = CRTC.crtR07VDispEndMask == 0 ? CRTC.crtR07VDispEndPort : CRTC.crtR07VDispEndTest;
             if (CRTC.crtR07VDispEndCurr != curr) {
               CRTC.crtR07VDispEndCurr = curr;
               CRTC.crtVIdleStart = curr + 1;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               CRTC.crtRestart ();
             }
           }
           return;
         case 0x0010:  //R08
           d &= 0xff;
           if (CRTC.crtR08Adjust != d) {
             CRTC.crtR08Adjust = d;
             CRTC.crtRestart ();
           }
           return;
         case 0x0012:  //R09
           CRTC.crtR09IRQRasterPort = d & CRTC.crtVerticalMask;
           {
             int curr = CRTC.crtR09IRQRasterMask == 0 ? CRTC.crtR09IRQRasterPort : CRTC.crtR09IRQRasterTest;
             if (CRTC.crtR09IRQRasterCurr != curr) {
               CRTC.crtR09IRQRasterCurr = curr;
               if (CRTC.CRT_RASTER_HASH_ON) {
                 CRTC.crtUpdateRasterHash ();
               }
               if (RasterBreakPoint.RBP_ON) {
                 RasterBreakPoint.rbpCheckIRQ ();
               }
             }
           }
           return;
         case 0x0014:  //R10
           CRTC.crtR10TxXPort = d & 0x03ff;
           {
             int curr = CRTC.crtR10TxXMask == 0 ? CRTC.crtR10TxXPort : CRTC.crtR10TxXTest;
             if (CRTC.crtR10TxXCurr != curr) {
               CRTC.crtR10TxXCurr = curr;
               CRTC.crtAllStamp += 2;
             }
           }
           return;
         case 0x0016:  //R11
           CRTC.crtR11TxYPort = d & 0x03ff;
           {
             int curr = CRTC.crtR11TxYMask == 0 ? CRTC.crtR11TxYPort : CRTC.crtR11TxYTest;
             if (CRTC.crtR11TxYCurr != curr) {
               CRTC.crtR11TxYCurr = curr;
               //CRTC.crtAllStamp += 2;  //ラッチするとき更新する
             }
           }
           return;
         case 0x0018:  //R12
         case 0x001c:  //R14
         case 0x0020:  //R16
         case 0x0024:  //R18
           {
             int p = (aa - 0x0018) >> 2;  //0,1,2,3
             CRTC.crtR12GrXPort[p] = d & (p == 0 ? 0x03ff : 0x01ff);
             int curr = CRTC.crtR12GrXMask[p] == 0 ? CRTC.crtR12GrXPort[p] : CRTC.crtR12GrXTest[p];
             if (CRTC.crtR12GrXCurr[p] != curr) {
               CRTC.crtR12GrXCurr[p] = curr;
               CRTC.crtAllStamp += 2;
             }
           }
           return;
         case 0x001a:  //R13
         case 0x001e:  //R15
         case 0x0022:  //R17
         case 0x0026:  //R19
           {
             int p = (aa - 0x0018) >> 2;  //0,1,2,3
             CRTC.crtR13GrYPort[p] = d & (p == 0 ? 0x03ff : 0x01ff);
             int curr = CRTC.crtR13GrYMask[p] == 0 ? CRTC.crtR13GrYPort[p] : CRTC.crtR13GrYTest[p];
             if (CRTC.crtR13GrYCurr[p] != curr) {
               CRTC.crtR13GrYCurr[p] = curr;
               //CRTC.crtAllStamp += 2;  //ラッチするとき更新する
             }
           }
           return;
         case 0x0028:  //R20
           CRTC.crtSetMemoryMode (d >> 12, d >> 11, d >> 8);
           CRTC.crtHighResoPort = d >>> 4 & 1;
           CRTC.crtVResoPort    = d >>> 2 & 3;
           CRTC.crtHResoPort    = d       & 3;
           SpriteScreen.sprAccessible = SpriteScreen.spr768x512 || (d & 0b10010) != 0b10010;
           int highResoCurr = CRTC.crtHighResoMask == 0 ? CRTC.crtHighResoPort : CRTC.crtHighResoTest;
           int vResoCurr = CRTC.crtVResoMask == 0 ? CRTC.crtVResoPort : CRTC.crtVResoTest;
           int hResoCurr = CRTC.crtHResoMask == 0 ? CRTC.crtHResoPort : CRTC.crtHResoTest;
           if (CRTC.crtHighResoCurr != highResoCurr ||
               CRTC.crtVResoCurr != vResoCurr ||
               CRTC.crtHResoCurr != hResoCurr) {
             CRTC.crtHighResoCurr = highResoCurr;
             CRTC.crtVResoCurr = vResoCurr;
             CRTC.crtHResoCurr = hResoCurr;
             CRTC.crtRestart ();
           }
           return;
         case 0x002a:  //R21
           CRTC.crtBitMask   = d << 22 < 0;  //(d & 512) != 0
           CRTC.crtSimAccess = d << 23 < 0;  //(d & 256) != 0
           CRTC.crtSimPlane3 = (byte) d < 0;  //(d & 128) != 0。d << 24 < 0
           CRTC.crtSimPlane2 = d << 25 < 0;  //(d & 64) != 0
           CRTC.crtSimPlane1 = d << 26 < 0;  //(d & 32) != 0
           CRTC.crtSimPlane0 = d << 27 < 0;  //(d & 16) != 0
           CRTC.crtCCPlane3  = XEiJ.TEST_BIT_3_SHIFT ? d << 31 - 3 < 0 : (d & 8) != 0;
           CRTC.crtCCPlane2  = XEiJ.TEST_BIT_2_SHIFT ? d << 31 - 2 < 0 : (d & 4) != 0;
           CRTC.crtCCPlane1  = XEiJ.TEST_BIT_1_SHIFT ? d << 31 - 1 < 0 : (d & 2) != 0;
           CRTC.crtCCPlane0  = XEiJ.TEST_BIT_0_SHIFT ? d << 31 - 0 < 0 : (d & 1) != 0;
           return;
         case 0x002c:  //R22
           CRTC.crtR22SrcBlock = d >> 8 & 0xff;
           CRTC.crtR22DstBlock = d      & 0xff;
           return;
         case 0x002e:  //R23
           CRTC.crtR23Mask = (char) d;
           return;
           //case 0x0030:  //R24
           //case 0x0032:  //R25
           //case 0x0034:  //R26
           //case 0x0036:  //R27
           //case 0x0038:  //R28
           //case 0x003a:  //R29
           //case 0x003c:  //R30
           //case 0x003e:  //R31
         default:
           return;
         }
       } else {  //$00E80400~$00E807FF
         aa &= 0xff;  //$00E80500~$00E807FF→$00E80400~$00E804FF
         if (aa < 0x80) {  //$00E80400~$00E8047F
           super.mmdWw (a, d);  //バスエラー
         } else {  //$00E80480~$00E804FF  動作ポート
           boolean rasterCopyOn = (d & 8) != 0;
           if (CRTC.crtRasterCopyOn != rasterCopyOn) {
             CRTC.crtRasterCopyOn = rasterCopyOn;  //ラスタコピー
             if (CRTC.CRT_RASTER_HASH_ON) {
               CRTC.crtUpdateRasterHash ();
             }
           }
           if (CRTC.crtClearFrames == 0) {
             CRTC.crtClearStandby = (d & 2) != 0;  //高速クリア
           }
           return;
         }
       }
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWw (a    , d >> 16);
       mmdWw (a + 2, d      );
     }
  },  //MMD_CRT

  //--------------------------------------------------------------------------------
  //MMD_VCN ビデオコントローラ
  //
  //  $00E82000~$00E821FF  ワードレジスタ
  //  $00E82200~$00E823FF  ワードレジスタ
  //  $00E82400~$00E82401  ワードレジスタ
  //  $00E82402~$00E824FF  $00E82400~$00E82401の繰り返し
  //  $00E82500~$00E82501  ワードレジスタ
  //  $00E82502~$00E825FF  $00E82500~$00E82501の繰り返し
  //  $00E82600~$00E82601  ワードレジスタ
  //  $00E82602~$00E826FF  $00E82600~$00E82601の繰り返し
  //  $00E82700~$00E82FFF  $0000
  //  $00E83000~$00E83FFF  $00E82000~$00E82FFFの繰り返し
  //
  MMD_VCN {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "ビデオコントローラ" : "Video Controller";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       int aa = a & 0x1fff;  //$00E83000~$00E83FFF→$00E82000~$00E82FFF
       if (aa < 0x0400) {  //パレット
         XEiJ.mpuClockTime += XEiJ.busWaitTime.palet;
         int d = (aa < 0x0200 ? VideoController.vcnPal16G8Port[aa >> 1] :  //$00E82000~$00E821FF  グラフィック
                  VideoController.vcnPal16TSPort[(aa - 0x0200) >> 1]);  //$00E82200~$00E823FF  テキストスプライト
         return (aa & 1) == 0 ? d >> 8 : d & 0xff;
       } else {  //レジスタ
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         int d = (aa < 0x0500 ? VideoController.vcnReg1Port :  //$00E82400~$00E824FF
                  aa < 0x0600 ? VideoController.vcnReg2Port :  //$00E82500~$00E825FF
                  aa < 0x0700 ? VideoController.vcnReg3Port :  //$00E82600~$00E826FF
                  0);  //$00E82700~$00E82FFF
         return (aa & 1) == 0 ? d >> 8 : d & 0xff;
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       int aa = a & 0x1fff;  //$00E83000~$00E83FFF→$00E82000~$00E82FFF
       if (aa < 0x0400) {  //パレット
         XEiJ.mpuClockTime += XEiJ.busWaitTime.palet;
         return (aa < 0x0200 ? VideoController.vcnPal16G8Port[aa >> 1] :  //$00E82000~$00E821FF  グラフィックスパレット
                 VideoController.vcnPal16TSPort[(aa - 0x0200) >> 1]);  //$00E82200~$00E823FF  テキストスプライトパレット
       } else {  //レジスタ
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         return (aa < 0x0500 ? VideoController.vcnReg1Port :  //$00E82400~$00E824FF
                 aa < 0x0600 ? VideoController.vcnReg2Port :  //$00E82500~$00E825FF
                 aa < 0x0700 ? VideoController.vcnReg3Port :  //$00E82600~$00E826FF
                 0);  //$00E82700~$00E82FFF
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return mmdRwz (a) << 16 | mmdRwz (a + 2);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       int aa = a & 0x1fff;  //$00E83000~$00E83FFF→$00E82000~$00E82FFF
       if (aa < 0x0200) {  //$00E82000~$00E821FF  グラフィックスパレット
         XEiJ.mpuClockTime += XEiJ.busWaitTime.palet;
         if (FlashingLights.FLR_ON) {
           FlashingLights.flrWriteByteG8 (a, d);
         } else {
           d &= 0xff;
           int n = aa >> 1;
           if ((aa & 1) == 0) {  //a=0,2,4,6,8,10,12,14
             int t = VideoController.vcnPal16G8Port[n] = d << 8 | (VideoController.vcnPal16G8Port[n] & 0xff);
             VideoController.vcnPal32G8[n] = VideoController.vcnPalTbl[t];
             if ((n & 1) == 0) {  //a=0,4,8,12 n=0,2,4,6
               VideoController.vcnPal8G16LPort[n] = d;
             } else {  //a=2,6,10,14 n-1=0,2,4,6
               VideoController.vcnPal8G16HPort[n - 1] = d << 8;
             }
           } else {  //a=1,3,5,7,9,11,13,15
             int t = VideoController.vcnPal16G8Port[n] = (VideoController.vcnPal16G8Port[n] & ~0xff) | d;
             VideoController.vcnPal32G8[n] = VideoController.vcnPalTbl[t];
             if ((n & 1) == 0) {  //a=1,5,9,13 n+1=1,3,5,7
               VideoController.vcnPal8G16LPort[n + 1] = d;
             } else {  //a=3,7,11,15 n=1,3,5,7
               VideoController.vcnPal8G16HPort[n] = d << 8;
             }
           }
           if ((VideoController.vcnReg3Curr & 0x001f) != 0) {  //グラフィックス画面が表示されている
             CRTC.crtAllStamp += 2;
           }
         }
       } else if (aa < 0x0400) {  //$00E82200~$00E823FF  テキストスプライトパレット
         XEiJ.mpuClockTime += XEiJ.busWaitTime.palet;
         if (FlashingLights.FLR_ON) {
           FlashingLights.flrWriteByteTS (a, d);
         } else {
           d &= 0xff;
           int n = (aa - 0x0200) >> 1;
           d = ((aa & 1) == 0 ?
                d << 8 | (VideoController.vcnPal16TSPort[n] & 0xff) :
                (VideoController.vcnPal16TSPort[n] & ~0xff) | d);
           VideoController.vcnPal16TSPort[n] = d;
           if (!ScreenModeTest.smtPatternTestOn) {
             VideoController.vcnPal32TS[n] = VideoController.vcnPalTbl[d];
             if ((VideoController.vcnReg3Curr << 31 - 6 < 0 && SpriteScreen.sprReg4BgCtrlCurr << 31 - 9 < 0) ||  //スプライト画面が表示されている
                 (VideoController.vcnReg3Curr << 31 - 5 < 0 && n < 16)) {  //テキスト画面が表示されていてテキストパレットが操作された
               CRTC.crtAllStamp += 2;
             }
           }
         }
       } else if (aa < 0x0500) {  //$00E82400~$00E824FF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         d = (aa & 1) == 0 ? 0x00 : d & 0x07;
         if (VideoController.vcnReg1Port != d) {
           VideoController.vcnReg1Port = d;
           VideoController.vcnReg1Curr = ((VideoController.vcnReg1Port & ~VideoController.vcnReg1Mask) |
                                          (VideoController.vcnReg1Test & VideoController.vcnReg1Mask));
           VideoController.vcnUpdateMode ();
         }
       } else if (aa < 0x0600) {  //$00E82500~$00E825FF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         d = ((aa & 1) == 0 ?
              (d & 0x3f) << 8 | (VideoController.vcnReg2Port & 0xff) :
              (VideoController.vcnReg2Port & ~0xff) | (d & 0xff));
         if (VideoController.vcnReg2Port != d) {
           VideoController.vcnReg2Port = d;
           VideoController.vcnReg2Curr = ((VideoController.vcnReg2Port & ~VideoController.vcnReg2Mask) |
                                          (VideoController.vcnReg2Test & VideoController.vcnReg2Mask));
           VideoController.vcnUpdateMode ();
         }
       } else if (aa < 0x0700) {  //$00E82600~$00E826FF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         d = ((aa & 1) == 0 ?
              (d & 0xff) << 8 | (VideoController.vcnReg3Port & 0xff) :
              (VideoController.vcnReg3Port & ~0xff) | (d & 0xff));
         if (VideoController.vcnReg3Port != d) {
           VideoController.vcnReg3Port = d;
           VideoController.vcnReg3Curr = ((VideoController.vcnReg3Port & ~VideoController.vcnReg3Mask) |
                                          (VideoController.vcnReg3Test & VideoController.vcnReg3Mask));
           VideoController.vcnUpdateMode ();
         }
       } else {  //$00E82700~$00E82FFF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       int aa = a & 0x1fff;  //$00E83000~$00E83FFF→$00E82000~$00E82FFF
       if (aa < 0x0200) {  //$00E82000~$00E821FF  グラフィックスパレット
         XEiJ.mpuClockTime += XEiJ.busWaitTime.palet;
         if (FlashingLights.FLR_ON) {
           FlashingLights.flrWriteWordG8 (a, d);
         } else {
           int n = aa >> 1;  //a=0,2,4,6,8,10,12,14 n=0,1,2,3,4,5,6,7
           int t = VideoController.vcnPal16G8Port[n] = (char) d;
           VideoController.vcnPal32G8[n] = VideoController.vcnPalTbl[t];
           if ((n & 1) == 0) {  //a=0,4,8,12 n=0,2,4,6 n+1=1,3,5,7
             VideoController.vcnPal8G16LPort[n] = d >> 8 & 0xff;
             VideoController.vcnPal8G16LPort[n + 1] = d & 0xff;
           } else {  //a=2,4,6,8 n-1=0,2,4,6 n=1,3,5,7
             VideoController.vcnPal8G16HPort[n - 1] = d & 0xff00;
             VideoController.vcnPal8G16HPort[n] = (d & 0xff) << 8;
           }
           if ((VideoController.vcnReg3Curr & 0x001f) != 0) {  //グラフィックス画面が表示されている
             CRTC.crtAllStamp += 2;
           }
         }
       } else if (aa < 0x0400) {  //$00E82200~$00E823FF  テキストスプライトパレット
         XEiJ.mpuClockTime += XEiJ.busWaitTime.palet;
         if (FlashingLights.FLR_ON) {
           FlashingLights.flrWriteWordTS (a, d);
         } else {
           int n = (aa - 0x0200) >> 1;
           d = (char) d;
           VideoController.vcnPal16TSPort[n] = d;
           if (!ScreenModeTest.smtPatternTestOn) {
             VideoController.vcnPal32TS[n] = VideoController.vcnPalTbl[d];
             if ((VideoController.vcnReg3Curr << 31 - 6 < 0 && SpriteScreen.sprReg4BgCtrlCurr << 31 - 9 < 0) ||  //スプライト画面が表示されている
                 (VideoController.vcnReg3Curr << 31 - 5 < 0 && n < 16)) {  //テキスト画面が表示されていてテキストパレットが操作された
               CRTC.crtAllStamp += 2;
             }
           }
         }
       } else if (aa < 0x0500) {  //$00E82400~$00E824FF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         d &= 0x0007;
         if (VideoController.vcnReg1Port != d) {
           VideoController.vcnReg1Port = d;
           VideoController.vcnReg1Curr = ((VideoController.vcnReg1Port & ~VideoController.vcnReg1Mask) |
                                          (VideoController.vcnReg1Test & VideoController.vcnReg1Mask));
           VideoController.vcnUpdateMode ();
         }
       } else if (aa < 0x0600) {  //$00E82500~$00E825FF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         d &= 0x3fff;
         if (VideoController.vcnReg2Port != d) {
           VideoController.vcnReg2Port = d;
           VideoController.vcnReg2Curr = ((VideoController.vcnReg2Port & ~VideoController.vcnReg2Mask) |
                                          (VideoController.vcnReg2Test & VideoController.vcnReg2Mask));
           VideoController.vcnUpdateMode ();
         }
       } else if (aa < 0x0700) {  //$00E82600~$00E826FF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
         d &= 0xffff;
         if (VideoController.vcnReg3Port != d) {
           VideoController.vcnReg3Port = d;
           VideoController.vcnReg3Curr = ((VideoController.vcnReg3Port & ~VideoController.vcnReg3Mask) |
                                          (VideoController.vcnReg3Test & VideoController.vcnReg3Mask));
           VideoController.vcnUpdateMode ();
         }
       } else {  //$00E82700~$00E82FFF
         XEiJ.mpuClockTime += XEiJ.busWaitTime.vicon;
       }
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWw (a    , d >> 16);
       mmdWw (a + 2, d      );
     }
  },  //MMD_VCN

  //--------------------------------------------------------------------------------
  //MMD_DMA DMAコントローラ
  //
  //  $00E84000~$00E8403F  ワードまたはロングワードレジスタ(CH0)
  //  $00E84040~$00E8407F  ワードまたはロングワードレジスタ(CH1)
  //  $00E84080~$00E840BF  ワードまたはロングワードレジスタ(CH2)
  //  $00E840C0~$00E840FF  ワードまたはロングワードレジスタ(CH4)
  //  $00E84100~$00E85FFF  $00E84000~$00E840FFの繰り返し
  //
  MMD_DMA {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "DMA コントローラ" : "DMA Controller";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.dmac;
       return HD63450.dmaReadByte (a);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.dmac;
       return HD63450.dmaReadWord (a);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.dmac * 2;
       return HD63450.dmaReadLong (a);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.dmac;
       HD63450.dmaWriteByte (a, d);
       return;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.dmac;
       HD63450.dmaWriteWord (a, d);
       return;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.dmac * 2;
       HD63450.dmaWriteLong (a, d);
       return;
     }
  },  //MMD_DMA

  //--------------------------------------------------------------------------------
  //MMD_SVS スーパーバイザ領域設定
  //
  //  $00E86000  バイトアクセスはバスエラー。ワードリードはバスエラー。ワードライトは$??xx
  //  $00E86001  バイトリードはバスエラー。バイトライトは$xx
  //  $00E86002~$00E87FFF  $00E86000~$00E86001の繰り返し
  //
  MMD_SVS {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "スーパーバイザ領域設定" : "Supervisor Area Setting";
    }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0) {  //偶数アドレスへのバイトライト
         super.mmdWb (a, d);  //バスエラー
       } else {  //奇数アドレスへのバイトライト
         XEiJ.mpuClockTime += XEiJ.busWaitTime.sysport;  //!!!
         MainMemory.mmrSetSupervisorArea (d);
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWw (a, d >> 16);
       mmdWw (a + 2, d);
     }
  },  //MMD_SVS

  //--------------------------------------------------------------------------------
  //MMD_MFP MFP
  //
  //  $00E88000~$00E8803F(偶数)  バイトアクセスはバスエラー。ワードリードは$FFxx。ワードライトは$??xx
  //  $00E88000~$00E8803F(奇数)  バイトレジスタ
  //  $00E88040~$00E89FFF  $00E88000~$00E8803Fの繰り返し
  //
  MMD_MFP {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "MFP" : "MFP";  //Multi Function Peripheral
    }
    //ピーク
    @Override protected int mmdPbz (int a) {
      return MC68901.mfpPeekByte (a);
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       return MC68901.mfpReadByte (a);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return mmdRwz (a) << 16 | mmdRwz (a + 2);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       MC68901.mfpWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWw (a, d >> 16);
       mmdWw (a + 2, d);
     }
  },  //MMD_MFP

  //--------------------------------------------------------------------------------
  //MMD_RTC_FIRST RTC
  //
  //  $00E8A000~$00E8A01F(偶数)  バイトアクセスはバスエラー。ワードリードは$FFxx。ワードライトは$??xx
  //  $00E8A000~$00E8A01F(奇数)  バイトレジスタ
  //  $00E8A020~$00E8AFFF  $00E8A000~$00E8A01Fの繰り返し
  //
  MMD_RTC_FIRST {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "RTC" : "RTC";  //Real-Time Clock
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (byte) RP5C15.rtcPeekByte (a);
    }
    @Override protected int mmdPbz (int a) {
      return RP5C15.rtcPeekByte (a);
    }
    @Override protected int mmdPws (int a) {
      return RP5C15.rtcPeekByte (a + 1);
    }
    @Override protected int mmdPwz (int a) {
      return RP5C15.rtcPeekByte (a + 1);
    }
    @Override protected int mmdPls (int a) {
      return (RP5C15.rtcPeekByte (a + 1) << 16 |
              RP5C15.rtcPeekByte (a + 3));
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbs (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       return (byte) RP5C15.rtcReadByte (a);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       return RP5C15.rtcReadByte (a);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       return RP5C15.rtcReadByte (a + 1);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       return RP5C15.rtcReadByte (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc * 2;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       return (RP5C15.rtcReadByte (a + 1) << 16 |
               RP5C15.rtcReadByte (a + 3));
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       RP5C15.rtcWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       RP5C15.rtcWriteByte (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc * 2;
       XEiJ.busSuper (MemoryMappedDevice.MMD_RTC_NEXT, 0x00e8a000, 0x00e8c000);  //RTC RTC
       RP5C15.rtcUpdate ();
       RP5C15.rtcWriteByte (a + 1, d >> 16);
       RP5C15.rtcWriteByte (a + 3, d);
     }
  },  //MMD_RTC_FIRST

  //--------------------------------------------------------------------------------
  //MMD_RTC_NEXT RTC
  MMD_RTC_NEXT {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "RTC" : "RTC";  //Real-Time Clock
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (byte) RP5C15.rtcPeekByte (a);
    }
    @Override protected int mmdPbz (int a) {
      return RP5C15.rtcPeekByte (a);
    }
    @Override protected int mmdPws (int a) {
      return RP5C15.rtcPeekByte (a + 1);
    }
    @Override protected int mmdPwz (int a) {
      return RP5C15.rtcPeekByte (a + 1);
    }
    @Override protected int mmdPls (int a) {
      return (RP5C15.rtcPeekByte (a + 1) << 16 |
              RP5C15.rtcPeekByte (a + 3));
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbs (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       return (byte) RP5C15.rtcReadByte (a);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       return RP5C15.rtcReadByte (a);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       return RP5C15.rtcReadByte (a + 1);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       return RP5C15.rtcReadByte (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc * 2;
       return (RP5C15.rtcReadByte (a + 1) << 16 |
               RP5C15.rtcReadByte (a + 3));
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcWriteByte (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc * 2;
       RP5C15.rtcWriteByte (a + 1, d >> 16);
       RP5C15.rtcWriteByte (a + 3, d);
     }
  },  //MMD_RTC_NEXT

  //--------------------------------------------------------------------------------
  //MMD_RTC_TEST RTC
  MMD_RTC_TEST {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "RTC テスト" : "RTC test";  //Real-Time Clock
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (byte) RP5C15.rtcPeekByte (a);
    }
    @Override protected int mmdPbz (int a) {
      return RP5C15.rtcPeekByte (a);
    }
    @Override protected int mmdPws (int a) {
      return RP5C15.rtcPeekByte (a + 1);
    }
    @Override protected int mmdPwz (int a) {
      return RP5C15.rtcPeekByte (a + 1);
    }
    @Override protected int mmdPls (int a) {
      return (RP5C15.rtcPeekByte (a + 1) << 16 |
              RP5C15.rtcPeekByte (a + 3));
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbs (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcTestUpdate ();
       return (byte) RP5C15.rtcReadByte (a);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcTestUpdate ();
       return RP5C15.rtcReadByte (a);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcTestUpdate ();
       return RP5C15.rtcReadByte (a + 1);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcTestUpdate ();
       return RP5C15.rtcReadByte (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc * 2;
       RP5C15.rtcTestUpdate ();
       return (RP5C15.rtcReadByte (a + 1) << 16 |
               RP5C15.rtcReadByte (a + 3));
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcTestUpdate ();
       RP5C15.rtcWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc;
       RP5C15.rtcTestUpdate ();
       RP5C15.rtcWriteByte (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rtc * 2;
       RP5C15.rtcTestUpdate ();
       RP5C15.rtcWriteByte (a + 1, d >> 16);
       RP5C15.rtcWriteByte (a + 3, d);
     }
  },  //MMD_RTC_TEST

  //--------------------------------------------------------------------------------
  //MMD_PRN プリンタポート
  //
  //  $00E8C000~$00E8C003(偶数)  バイトリードは$FF。バイトライトは$??。ワードリードは$FFxx。ワードライトは$??xx
  //  $00E8C000~$00E8C003(奇数)  バイトリードは$FF。バイトライトは$??
  //  $00E8C004~$00E8DFFF  $00E8C000~$00E8C003の繰り返し(?)
  //
  MMD_PRN {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "プリンタポート" : "Printer Port";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.prnport;
       a &= 3;
       return (a == (PrinterPort.PRN_DATA & 3) ? PrinterPort.prnReadData () :
               a == (PrinterPort.PRN_STROBE & 3) ? PrinterPort.prnReadStrobe () :
               0xff);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.prnport;
       a &= 3;
       if (a == (PrinterPort.PRN_DATA & 3)) {
         PrinterPort.prnWriteData (d);
       } else if (a == (PrinterPort.PRN_STROBE & 3)) {
         PrinterPort.prnWriteStrobe (d);
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_PRN

  //--------------------------------------------------------------------------------
  //MMD_SYS システムポート
  //
  //  $00E8E000~$00E8E00F(偶数)  バイトリードは$FF、バイトライトは$??、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E8E000~$00E8E00F(奇数)  バイトリードは$xx、バイトライトは$xx
  //  $00E8E010~$00E8FFFF  $00E8E000~$00E8E00Fの繰り返し
  //
  MMD_SYS {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "システムポート" : "System Port";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sysport;
       switch (a & 15) {
       case 0x01:
         return 0b11110000 | VideoController.vcnTargetContrastPort;
       case 0x03:
         return 0b11111000 | XEiJ.pnlStereoscopicShutter;
       case 0x07:
         return 0b11110100 | (Keyboard.kbdOn ? 8 : 0) | CRTC.crtHRLPort << 1;
       case 0x0b:
         return (XEiJ.currentModel.isX68030 () ? 0xdc :
                 XEiJ.currentMPU < Model.MPU_MC68020 ?
                 XEiJ.mpuClockMHz <= 10.0 ? 0xff : 0xfe :
                 XEiJ.mpuClockMHz <= 20.0 ? 0xff : 0xfe);
       }
       return 0xff;
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       d &= 0xff;
       switch (a & 15) {
       case 0x01:
         VideoController.vcnTargetContrastPort = d & 15;
         if (FlashingLights.FLR_ON) {
           FlashingLights.flrUpdateContrast ();
         } else {
           int curr = VideoController.vcnTargetContrastMask == 0 ? VideoController.vcnTargetContrastPort : VideoController.vcnTargetContrastTest;
           if (VideoController.vcnTargetContrastCurr != curr) {
             VideoController.vcnTargetContrastCurr = curr;
             VideoController.vcnTargetScaledContrast = VideoController.VCN_CONTRAST_SCALE * VideoController.vcnTargetContrastCurr;
             CRTC.crtContrastClock = XEiJ.mpuClockTime;
             CRTC.crtFrameTaskClock = Math.min (CRTC.crtContrastClock, CRTC.crtCaptureClock);
           }
         }
         return;
       case 0x03:
         XEiJ.pnlStereoscopicShutter = d & 3;
         break;
       case 0x07:
         {
           CRTC.crtHRLPort = d >> 1 & 1;
           int curr = CRTC.crtHRLMask == 0 ? CRTC.crtHRLPort : CRTC.crtHRLTest;
           if (CRTC.crtHRLCurr != curr) {
             CRTC.crtHRLCurr = curr;
             CRTC.crtRestart ();
           }
           if ((d & 1 << 2) != 0) {
             XEiJ.sysResetNMI ();  //NMIリセット
           }
         }
         return;
       case 0x09:
         if (XEiJ.currentModel.isX68030 ()) {
           //X68030のとき
           //  d=ROMウェイト設定値<<4|RAMウェイト設定値
           //  ROMウェイト
           //    ROMウェイト設定値+2
           //  RAMウェイト
           //    RAMウェイト設定値が0のとき
           //      スタティックカラムが有効なとき0
           //      スタティックカラムが無効なとき4
           //    RAMウェイト設定値が0でないとき
           //      RAMウェイト設定値+2
           //  ROMとRAMにはキャッシュが効く
           if (XEiJ.currentModel.isX68030 ()) {  //X68030
             XEiJ.mpuROMWaitCycles = (d >> 4 & 15) + 2;
             XEiJ.mpuRAMWaitCycles = (d & 15) == 0 ? 0 : (d & 15) + 2;
             XEiJ.mpuSetWait ();
           }
         }
         return;
       case 0x0d:
         SRAM.smrWriteEnableOn = d == 0x31;
         return;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_SYS

  //--------------------------------------------------------------------------------
  //MMD_OPM FM音源
  //
  //  $00E90000  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFFF、ワードライトは$??xx
  //  $00E90001  バイトリードは$FF、バイトライトは$xx
  //  $00E90002  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E90003  バイトリードは$xx、バイトライトは$xx
  //  $00E90004~$00E91FFF  $00E90000~$00E90003の繰り返し
  //
  MMD_OPM {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "FM 音源" : "FM Sound Generator";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.opm;
       return (a & 3) == 3 ? OPM.opmYM2151.readStatus () : 0xff;
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.opm;
       a &= XEiJ.BUS_MOTHER_MASK;
       a &= 3;
       d &= 0xff;
       if (a == 3) {  //データレジスタ
         OPM.opmYM2151.generate (SoundSource.SND_CHANNELS *
                                 (OPM.OPM_BLOCK_SAMPLES - Math.max (0, (int) ((double) (SoundSource.sndBlockClock - XEiJ.mpuClockTime) /
                                                                              (double) OPM.OPM_SAMPLE_TIME))));
         OPM.opmYM2151.writeData (d);
       } else if (a == 1) {  //アドレスレジスタ
         OPM.opmYM2151.writeAddress (d);
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_OPM

  //--------------------------------------------------------------------------------
  //MMD_PCM ADPCM音源
  //
  //  $00E92000~$00E92003(偶数)  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E92000~$00E92003(奇数)  バイトリードは$xx、バイトライトは$xx
  //  $00E92004~$00E93FFF  $00E92000~$00E92003の繰り返し
  //
  MMD_PCM {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "ADPCM 音源" : "ADPCM Sound Generator";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.adpcm;
       return (a & 3) == 1 ? (ADPCM.pcmActive ? 0b10000000 : 0) | 0x40 : 0xff;
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.adpcm;
       a &= XEiJ.BUS_MOTHER_MASK;
       a &= 3;
       if (a == 1) {  //コマンド
         if ((d & 0b00000001) != 0) {  //動作終了
           if (ADPCM.pcmActive) {  //動作中
             ADPCM.pcmClock = XEiJ.FAR_FUTURE;
             TickerQueue.tkqRemove (SoundSource.sndPcmTicker);
             ADPCM.pcmActive = false;
             ADPCM.pcmEncodedData = -1;
             ADPCM.pcmDecoderPointer = 0;
             HD63450.dmaRisePCL (3);
             HD63450.dmaRiseREQ (3);
           }
         } else if ((d & 0b00000010) != 0) {  //動作開始
           if (!ADPCM.pcmActive) {  //停止中
             //現在のブロックの残り時間が1サンプルの時間の倍数になるように切り上げる
             int remainingSamples = Math.max (0, (int) ((double) (SoundSource.sndBlockClock - XEiJ.mpuClockTime) / (double) ADPCM.PCM_SAMPLE_TIME));  //現在のブロックの残りサンプル数
             ADPCM.pcmClock = SoundSource.sndBlockClock - ADPCM.PCM_SAMPLE_TIME * (long) remainingSamples;  //書き込み開始時刻
             TickerQueue.tkqAdd (SoundSource.sndPcmTicker, ADPCM.pcmClock);
             ADPCM.pcmActive = true;
             int newPointer = SoundSource.SND_CHANNELS * (ADPCM.PCM_BLOCK_SAMPLES - remainingSamples);  //書き込み開始位置
             if (ADPCM.pcmPointer < newPointer) {
               ADPCM.pcmFillBuffer (newPointer);
             } else {
               ADPCM.pcmPointer = newPointer;  //少し戻る場合がある
             }
             //DMAに最初のデータを要求する
             HD63450.dmaFallPCL (3);
             HD63450.dmaFallREQ (3);
           }
           //} else if ((d & 0b00000100) != 0) {  //録音開始
           //! 非対応
         }
       } else if (a == 3) {  //データ
         if (ADPCM.pcmActive) {
           ADPCM.pcmEncodedData = d & 0xff;
           HD63450.dmaRisePCL (3);
           HD63450.dmaRiseREQ (3);
         }
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_PCM

  //--------------------------------------------------------------------------------
  //MMD_FDC FDコントローラ
  //
  //  $00E94000~$00E94007(偶数)  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E94000~$00E94007(奇数)  バイトリードは$xx、バイトライトは$xx
  //  $00E94008~$00E95FFF  $00E94000~$00E94007の繰り返し
  //
  MMD_FDC {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "FD コントローラ" : "FD Controller";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (byte) mmdPbz (a);
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      switch (a) {
      case FDC.FDC_STATUS_PORT:  //0x00e94001
        return FDC.fdcPeekStatus ();
      case FDC.FDC_DATA_PORT:  //0x00e94003
        return FDC.fdcPeekData ();
      case FDC.FDC_DRIVE_STATUS:  //0x00e94005
        return FDC.fdcPeekDriveStatus ();
      }
      return 0xff;
    }
    @Override protected int mmdPws (int a) {
      return (short) (mmdPbz (a) << 8 | mmdPbz (a + 1));
    }
    @Override protected int mmdPwz (int a) {
      return mmdPbz (a) << 8 | mmdPbz (a + 1);
    }
    @Override protected int mmdPls (int a) {
      return mmdPwz (a) << 16 | mmdPwz (a + 2);
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.fdc;
       a &= XEiJ.BUS_MOTHER_MASK;
       switch (a) {
       case FDC.FDC_STATUS_PORT:  //0x00e94001
         return FDC.fdcReadStatus ();
       case FDC.FDC_DATA_PORT:  //0x00e94003
         return FDC.fdcReadData ();
       case FDC.FDC_DRIVE_STATUS:  //0x00e94005
         return FDC.fdcReadDriveStatus ();
       }
       return 0xff;
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.fdc;
       a &= XEiJ.BUS_MOTHER_MASK;
       switch (a) {
       case FDC.FDC_STATUS_PORT:  //0x00e94001
         FDC.fdcWriteCommand (d);
         break;
       case FDC.FDC_DATA_PORT:  //0x00e94003
         FDC.fdcWriteData (d);
         break;
       case FDC.FDC_DRIVE_STATUS:  //0x00e94005
         FDC.fdcWriteDriveControl (d);
         break;
       case FDC.FDC_DRIVE_SELECT:  //0x00e94007
         FDC.fdcWriteDriveSelect (d);
         break;
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_FDC

  //--------------------------------------------------------------------------------
  //MMD_HDC SASI HDコントローラ
  //
  //  $00E96000~$00E9603F(偶数)  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E96000~$00E9603F(奇数)  バイトリードは$xx、バイトライトは$xx
  //                              X68030の$00E96000~$00E9601Fは$00,$00,$FF,$FFの繰り返し。$00E9603Fは$FF
  //  $00E96040~$00E97FFF  $00E96000~$00E9603Fの繰り返し
  //
  MMD_HDC {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "内蔵 SASI/SCSI ポート" : "Internal SASI/SCSI Port";
    }
    //ピーク
    @Override protected int mmdPbz (int a) {
      if ((a & 0x01) == 0) {  //偶数番地
        return 0xff;
      } else {  //奇数番地
        if (SPC.spcSCSIINOn) {  //SCSI内蔵機
          if ((a & 0x20) == 0) {  //SASIポート
            return ((a & 0x02) == 0 ? 0x00 : 0xff);
          } else {  //SCSIポート
            return SPC.spcSCSIINChip.spiPeek (a);
          }
        } else {  //SASI内蔵機
          switch (a & 0x3f) {
          case HDC.HDC_DATA_PORT & 0x3f:  //0x00e96001
            return HDC.hdcPeekData ();
          case HDC.HDC_STATUS_PORT & 0x3f:  //0x00e96003
            return HDC.hdcPeekStatus ();
          case HDC.HDC_RESET_PORT & 0x3f:  //0x00e96005
            return 0xff;
          case HDC.HDC_SELECTION_PORT & 0x3f:  //0x00e96007
            return 0xff;
          default:
            return 0xff;
          }
        }
      }
    }
    @Override protected int mmdPwz (int a) {
      return 0xff00 | mmdPbz (a + 1);
    }
    @Override protected int mmdPls (int a) {
      return 0xff00ff00 | mmdPbz (a + 1) << 16 | mmdPbz (a + 3);
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;
       if ((a & 0x01) == 0) {  //偶数番地
         if (XEiJ.currentIsSecond) {  //Xellent30の030モード
           return 0xff;
         }
         return super.mmdRbz (a);  //バスエラー
       } else {  //奇数番地
         if (SPC.spcSCSIINOn) {  //SCSI内蔵機
           if ((a & 0x20) == 0) {  //SASIポート
             return ((a & 0x02) == 0 ? 0x00 : 0xff);
           } else {  //SCSIポート
             return SPC.spcSCSIINChip.spiRead (a);
           }
         } else {  //SASI内蔵機
           switch (a & 0x3f) {
           case HDC.HDC_DATA_PORT & 0x3f:  //0x00e96001
             return HDC.hdcReadData ();
           case HDC.HDC_STATUS_PORT & 0x3f:  //0x00e96003
             return HDC.hdcReadStatus ();
           case HDC.HDC_RESET_PORT & 0x3f:  //0x00e96005
             return 0xff;
           case HDC.HDC_SELECTION_PORT & 0x3f:  //0x00e96007
             return 0xff;
           default:
             return 0xff;
           }
         }
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;
       if ((a & 0x01) == 0) {  //偶数番地
         if (XEiJ.currentIsSecond) {  //Xellent30の030モード
           return;
         }
         super.mmdWb (a, d);  //バスエラー
       } else {  //奇数番地
         if (SPC.spcSCSIINOn) {  //SCSI内蔵機
           if ((a & 0x20) == 0) {  //SASIポート
             return;
           } else {  //SCSIポート
             SPC.spcSCSIINChip.spiWrite (a, d);
             return;
           }
         } else {  //SASI内蔵機
           switch (a & 0x3f) {
           case HDC.HDC_DATA_PORT & 0x3f:  //0x00e96001
             HDC.hdcWriteData (d);
             return;
           case HDC.HDC_STATUS_PORT & 0x3f:  //0x00e96003
             HDC.hdcWriteCommand (d);
             return;
           case HDC.HDC_RESET_PORT & 0x3f:  //0x00e96005
             HDC.hdcWriteReset (d);
             return;
           case HDC.HDC_SELECTION_PORT & 0x3f:  //0x00e96007
             HDC.hdcWriteSelect (d);
             return;
           default:
             return;
           }
         }
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_HDC

  //--------------------------------------------------------------------------------
  //MMD_SCC SCC
  //
  //  $00E98000~$00E98007(偶数)  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E98000~$00E98007(奇数)  バイトリードは$xx、バイトライトは$xx
  //  $00E98008~$00E99FFF  $00E98000~$00E98007の繰り返し
  //
  MMD_SCC {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "SCC" : "SCC";  //Serial Communication Controller
    }
    //ピーク
    @Override protected int mmdPbz (int a) {
      return Z8530.sccReadByte (a, true);
    }
    @Override protected int mmdPwz (int a) {
      return 0xff00 | mmdPbz (a + 1);
    }
    @Override protected int mmdPls (int a) {
      return 0xff00ff00 | mmdPbz (a + 1) << 16 | mmdPbz (a + 3);
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       return Z8530.sccReadByte (a, false);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       Z8530.sccWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_SCC

  //--------------------------------------------------------------------------------
  //MMD_PPI PPI
  //
  //  $00E9A000~$00E9A007(偶数)  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E9A000~$00E9A007(奇数)  バイトリードは$xx、バイトライトは$xx
  //  $00E9A008~$00E9BFFF  $00E9A000~$00E9A007の繰り返し
  //
  MMD_PPI {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "PPI" : "PPI";  //Programmable Peripheral Interface
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ppi;
       return PPI.ppiReadByte (a);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ppi;
       PPI.ppiWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_PPI

  //--------------------------------------------------------------------------------
  //MMD_IOI I/O割り込み
  //
  //  $00E9C000~$00E9C00F(偶数)  バイトリードはバスエラー、バイトライトはバスエラー、ワードリードは$FFxx、ワードライトは$??xx
  //  $00E9C000~$00E9C00F(奇数)  バイトリードは$xx、バイトライトは$xx
  //  $00E9C010~$00E9BFFF  $00E9C000~$00E9C00Fの繰り返し
  //
  MMD_IOI {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "I/O 割り込み" : "I/O Interrupt";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトリード
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         return super.mmdRbz (a);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ioi;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (a == IOInterrupt.IOI_STATUS) {
         return IOInterrupt.ioiReadStatus ();
       } else if (a == IOInterrupt.IOI_VECTOR) {  //ライトオンリー。リードすると$FFが返る
         //return IOInterrupt.ioiReadVector ();
         return 0xff;
       } else {
         return super.mmdRbz (a);  //バスエラー
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       return 0xff00 | mmdRbz (a + 1);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return 0xff00ff00 | mmdRbz (a + 1) << 16 | mmdRbz (a + 3);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & 1) == 0 &&  //偶数アドレスのバイトライト
           !XEiJ.currentIsSecond) {  //Xellent30の030モードではない
         super.mmdWb (a, d);  //バスエラー
       }
       XEiJ.mpuClockTime += XEiJ.busWaitTime.ioi;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (a == IOInterrupt.IOI_STATUS) {
         IOInterrupt.ioiWriteEnable (d);
       } else if (a == IOInterrupt.IOI_VECTOR) {
         IOInterrupt.ioiWriteVector (d);
       } else {
         super.mmdWb (a, d);  //バスエラー
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       mmdWb (a + 1, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWb (a + 1, d >> 16);
       mmdWb (a + 3, d);
     }
  },  //MMD_IOI

  //--------------------------------------------------------------------------------
  //MMD_XB1 拡張ボード領域1
  MMD_XB1 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張ボード領域 1" : "Expansion Board Area 1";
    }
    //ピーク
    @Override protected int mmdPbz (int a) {
      if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
        if ((7 & XEiJ.currentCopro1) != 0) {
          return XEiJ.fpuCoproboard1.cirPeekByteZero (a);
        } else {
          return 0xff;
        }
      }
      if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
        if ((7 & XEiJ.currentCopro2) != 0) {
          return XEiJ.fpuCoproboard2.cirPeekByteZero (a);
        } else {
          return 0xff;
        }
      }
      if ((a & (XEiJ.BUS_MOTHER_MASK & -HFS.HFS_ROM_SIZE)) == HFS.HFS_ADDRESS) {  //ホストファイルシステムインタフェイス
        a &= XEiJ.BUS_MOTHER_MASK;
        return MainMemory.mmrM8[a] & 0xff;
      }
      if (Keyboard.KBD_ZKEY_ON &&
          (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
        return Keyboard.kbdZKeyIOReadByte (a);
      }
      return 0xff;
    }
    @Override protected int mmdPwz (int a) {
      if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
        if ((7 & XEiJ.currentCopro1) != 0) {
          return XEiJ.fpuCoproboard1.cirPeekWordZero (a);
        } else {
          return 0xffff;
        }
      }
      if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
        if ((7 & XEiJ.currentCopro2) != 0) {
          return XEiJ.fpuCoproboard2.cirPeekWordZero (a);
        } else {
          return 0xffff;
        }
      }
      if ((a & (XEiJ.BUS_MOTHER_MASK & -HFS.HFS_ROM_SIZE)) == HFS.HFS_ADDRESS) {  //ホストファイルシステムインタフェイス
        a &= XEiJ.BUS_MOTHER_MASK;
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
      if (Keyboard.KBD_ZKEY_ON &&
          (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
        return Keyboard.kbdZKeyIOReadWord (a);
      }
      return 0xffff;
    }
    @Override protected int mmdPls (int a) {
      if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
        if ((7 & XEiJ.currentCopro1) != 0) {
          return XEiJ.fpuCoproboard1.cirPeekLong (a);
        } else {
          return -1;
        }
      }
      if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
        if ((7 & XEiJ.currentCopro2) != 0) {
          return XEiJ.fpuCoproboard2.cirPeekLong (a);
        } else {
          return -1;
        }
      }
      if ((a & (XEiJ.BUS_MOTHER_MASK & -HFS.HFS_ROM_SIZE)) == HFS.HFS_ADDRESS) {  //ホストファイルシステムインタフェイス
        a &= XEiJ.BUS_MOTHER_MASK;
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
      if (Keyboard.KBD_ZKEY_ON &&
          (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
        return Keyboard.kbdZKeyIOReadLong (a);
      }
      return -1;
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
         if ((7 & XEiJ.currentCopro1) != 0) {
           return XEiJ.fpuCoproboard1.cirReadByteZero (a);
         } else {
           return super.mmdRbz (a);  //バスエラー
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
         if ((7 & XEiJ.currentCopro2) != 0) {
           return XEiJ.fpuCoproboard2.cirReadByteZero (a);
         } else {
           return super.mmdRbz (a);  //バスエラー
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -HFS.HFS_ROM_SIZE)) == HFS.HFS_ADDRESS) {  //ホストファイルシステムインタフェイス
         a &= XEiJ.BUS_MOTHER_MASK;
         return MainMemory.mmrM8[a] & 0xff;
       }
       if (Keyboard.KBD_ZKEY_ON &&
           (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
         return Keyboard.kbdZKeyIOReadByte (a);
       }
       return super.mmdRbz (a);  //バスエラー
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
         if ((7 & XEiJ.currentCopro1) != 0) {
           return XEiJ.fpuCoproboard1.cirReadWordZero (a);
         } else {
           return super.mmdRwz (a);  //バスエラー
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
         if ((7 & XEiJ.currentCopro2) != 0) {
           return XEiJ.fpuCoproboard2.cirReadWordZero (a);
         } else {
           return super.mmdRwz (a);  //バスエラー
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -HFS.HFS_ROM_SIZE)) == HFS.HFS_ADDRESS) {  //ホストファイルシステムインタフェイス
         a &= XEiJ.BUS_MOTHER_MASK;
         return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
       if (Keyboard.KBD_ZKEY_ON &&
           (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
         return Keyboard.kbdZKeyIOReadWord (a);
       }
       return super.mmdRwz (a);  //バスエラー
     }
    @Override protected int mmdRls (int a) throws M68kException {
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
         if ((7 & XEiJ.currentCopro1) != 0) {
           return XEiJ.fpuCoproboard1.cirReadLong (a);
         } else {
           return super.mmdRls (a);  //バスエラー
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
         if ((7 & XEiJ.currentCopro2) != 0) {
           return XEiJ.fpuCoproboard2.cirReadLong (a);
         } else {
           return super.mmdRls (a);  //バスエラー
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -HFS.HFS_ROM_SIZE)) == HFS.HFS_ADDRESS) {  //ホストファイルシステムインタフェイス
         a &= XEiJ.BUS_MOTHER_MASK;
         return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
       if (Keyboard.KBD_ZKEY_ON &&
           (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
         return Keyboard.kbdZKeyIOReadLong (a);
       }
       return super.mmdRls (a);  //バスエラー
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
         if ((7 & XEiJ.currentCopro1) != 0) {
           XEiJ.fpuCoproboard1.cirWriteByte (a, d);
           return;
         } else {
           super.mmdWb (a, d);  //バスエラー
           return;
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
         if ((7 & XEiJ.currentCopro2) != 0) {
           XEiJ.fpuCoproboard2.cirWriteByte (a, d);
           return;
         } else {
           super.mmdWb (a, d);  //バスエラー
           return;
         }
       }
       if (Keyboard.KBD_ZKEY_ON &&
           (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
         Keyboard.kbdZKeyIOWriteByte (a, d);
         return;
       }
       super.mmdWb (a, d);  //バスエラー
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
         if ((7 & XEiJ.currentCopro1) != 0) {
           XEiJ.fpuCoproboard1.cirWriteWord (a, d);
           return;
         } else {
           super.mmdWw (a, d);  //バスエラー
           return;
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
         if ((7 & XEiJ.currentCopro2) != 0) {
           XEiJ.fpuCoproboard2.cirWriteWord (a, d);
           return;
         } else {
           super.mmdWw (a, d);  //バスエラー
           return;
         }
       }
       if (Keyboard.KBD_ZKEY_ON &&
           (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
         Keyboard.kbdZKeyIOWriteWord (a, d);
         return;
       }
       super.mmdWw (a, d);  //バスエラー
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e000) {  //数値演算プロセッサボード1
         if ((7 & XEiJ.currentCopro1) != 0) {
           XEiJ.fpuCoproboard1.cirWriteLong (a, d);
           return;
         } else {
           super.mmdWl (a, d);  //バスエラー
           return;
         }
       }
       if ((a & (XEiJ.BUS_MOTHER_MASK & -0x20)) == 0x00e9e080) {  //数値演算プロセッサボード2
         if ((7 & XEiJ.currentCopro2) != 0) {
           XEiJ.fpuCoproboard2.cirWriteLong (a, d);
           return;
         } else {
           super.mmdWl (a, d);  //バスエラー
           return;
         }
       }
       if (Keyboard.KBD_ZKEY_ON &&
           (a & (XEiJ.BUS_MOTHER_MASK & -Keyboard.KBD_ZKEY_IO_SIZE)) == Keyboard.KBD_ZKEY_IO_ADDRESS) {  //Zキーボード
         Keyboard.kbdZKeyIOWriteLong (a, d);
         return;
       }
       super.mmdWl (a, d);  //バスエラー
     }
  },  //MMD_XB1

  //--------------------------------------------------------------------------------
  //MMD_EXS 拡張SCSI
  //  必要なときだけ接続される
  //  拡張SCSIのROMのサイズは8KBなのでリードのときのバスエラーのチェックは不要
  //  ライトのときはROMには書き込めないのでSPCのレジスタでなければバスエラー
  MMD_EXS {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張 SCSI ポート" : "Expansion SCSI Port";
    }
    //ピーク
    @Override protected int mmdPbz (int a) {
      if ((a & (-32 & XEiJ.BUS_MOTHER_MASK)) == SPC.SPC_BASE_EX) {  //レジスタ
        if ((a & 0x01) == 0) {  //偶数番地
          return 0xff;
        } else {  //奇数番地
          return SPC.spcSCSIEXChip.spiPeek (a);
        }
      } else {  //ROM
        return 0xff & MainMemory.mmrM8[a];
      }
    }
    @Override protected int mmdPwz (int a) {
      XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;  //!!!
      if ((a & (-32 & XEiJ.BUS_MOTHER_MASK)) == SPC.SPC_BASE_EX) {  //レジスタ
        return 0xff00 | SPC.spcSCSIEXChip.spiPeek (a + 1);
      } else {  //ROM
        return (0xff & MainMemory.mmrM8[a]) << 8 | (0xff & MainMemory.mmrM8[a + 1]);
      }
    }
    @Override protected int mmdPls (int a) {
      return mmdPwz (a) << 16 | mmdPwz (a + 2);
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;  //!!!
       if ((a & (-32 & XEiJ.BUS_MOTHER_MASK)) == SPC.SPC_BASE_EX) {  //レジスタ
         if ((a & 0x01) == 0) {  //偶数番地
           if (XEiJ.currentIsSecond) {  //Xellent30の030モード
             return 0xff;
           }
           return super.mmdRbz (a);  //バスエラー
         } else {  //奇数番地
           return SPC.spcSCSIEXChip.spiRead (a);
         }
       } else {  //ROM
         return 0xff & MainMemory.mmrM8[a];
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;  //!!!
       if ((a & (-32 & XEiJ.BUS_MOTHER_MASK)) == SPC.SPC_BASE_EX) {  //レジスタ
         return 0xff00 | SPC.spcSCSIEXChip.spiRead (a + 1);
       } else {  //ROM
         return (0xff & MainMemory.mmrM8[a]) << 8 | (0xff & MainMemory.mmrM8[a + 1]);
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       return mmdRwz (a) << 16 | mmdRwz (a + 2);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;  //!!!
       if ((a & (-32 & XEiJ.BUS_MOTHER_MASK)) == SPC.SPC_BASE_EX) {  //レジスタ
         if ((a & 1) == 0) {  //偶数番地
           if (XEiJ.currentIsSecond) {  //Xellent30の030モード
             return;
           }
           super.mmdWb (a, d);  //バスエラー
         } else {  //奇数番地
           SPC.spcSCSIEXChip.spiWrite (a, d);
           return;
         }
       } else {  //ROM
         super.mmdWb (a, d);  //バスエラー
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.hdc;  //!!!
       if ((a & (-32 & XEiJ.BUS_MOTHER_MASK)) == SPC.SPC_BASE_EX) {  //レジスタ
         SPC.spcSCSIEXChip.spiWrite (a + 1, d);
         return;
       } else {  //ROM
         super.mmdWw (a, d);  //バスエラー
       }
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       mmdWw (a, d >> 16);
       mmdWw (a + 2, d);
     }
  },  //MMD_EXS

  //--------------------------------------------------------------------------------
  //MMD_XB2 拡張ボード領域2
  //
  //  $00EAFF80  リードはバスエラー、バイトライトはバスエラー、ワードライトは$00200000~$003FFFFFがあるとき$??xx、ないときバスエラー
  //  $00EAFF81  リードはバスエラー、ライトは$00200000~$003FFFFFがあるとき$xx、ないときバスエラー
  //  $00EAFF82  リードはバスエラー、バイトライトはバスエラー、ワードライトは$00400000~$005FFFFFがあるとき$??xx、ないときバスエラー
  //  $00EAFF83  リードはバスエラー、ライトは$00400000~$005FFFFFがあるとき$xx、ないときバスエラー
  //  $00EAFF84  リードはバスエラー、バイトライトはバスエラー、ワードライトは$00600000~$007FFFFFがあるとき$??xx、ないときバスエラー
  //  $00EAFF85  リードはバスエラー、ライトは$00600000~$007FFFFFがあるとき$xx、ないときバスエラー
  //  $00EAFF86  リードはバスエラー、バイトライトはバスエラー、ワードライトは$00800000~$009FFFFFがあるとき$??xx、ないときバスエラー
  //  $00EAFF87  リードはバスエラー、ライトは$00800000~$009FFFFFがあるとき$xx、ないときバスエラー
  //  $00EAFF88  リードはバスエラー、バイトライトはバスエラー、ワードライトは$00A00000~$00BFFFFFがあるとき$??xx、ないときバスエラー
  //  $00EAFF89  リードはバスエラー、ライトは$00A00000~$00BFFFFFがあるとき$xx、ないときバスエラー
  //  $00EAFF8A~$00EAFF8F  バスエラー
  //  $00EAFF90~$00EAFFFF  $00EAFF80~$00EAFF8Fの繰り返し
  //
  //  拡張
  //  $00EAFF7F  バンクメモリのページ番号
  //
  MMD_XB2 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張ボード領域 2" : "Expansion Board Area 2";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (0x00eaff81 <= a && a <= 0x00eaff89 && (a & 1) != 0) {  //スーパーバイザエリア設定ポート
         return MainMemory.mmrM8[a] & 0xff;  //読み出せるようにしておく(本来はライトオンリー)
       }
       if (XEiJ.bnkOn && a == 0x00eaff7f) {  //バンクメモリのページ番号
         return XEiJ.bnkPageStart >> 17;
       }
       return super.mmdRbz (a);  //バスエラー
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (0x00eaff81 <= a && a <= 0x00eaff89 && (a & 1) != 0) {  //スーパーバイザエリア設定ポート
         MainMemory.mmrM8[a] = (byte) d;  //読み出せるようにしておく(本来はライトオンリー)
         a = (a & 14) + 2 << 20;  //1,3,5,7,9→2,4,6,8,a
         for (int m = 1; m <= 128; m <<= 1) {
           if ((d & m) == 0) {  //ユーザエリア
             XEiJ.busUser ( MemoryMappedDevice.MMD_MMR, a, a + 0x00040000);
           } else {  //スーパーバイザエリア
             XEiJ.busSuper (MemoryMappedDevice.MMD_MMR, a, a + 0x00040000);
           }
           a += 0x00040000;
         }
         return;
       }
       if (XEiJ.bnkOn && a == 0x00eaff7f) {  //バンクメモリのページ番号
         XEiJ.bnkPageStart = (d & 0xff) << 17;
         return;
       }
       super.mmdWb (a, d);  //バスエラー
     }
  },  //MMD_XB2

  //--------------------------------------------------------------------------------
  //MMD_SPR スプライト画面
  //
  //  CRTCのR20の下位5ビットが%1xx1xでないとき
  //    $00EB0000-$00EB03FF  スプライトスクロールレジスタ(0~127)
  //    $00EB0400-$00EB07FF  $FF。256枚表示のときスプライトスクロールレジスタ(128~255)
  //    $00EB0800-$00EB0811  設定
  //    $00EB0812-$00EB7FFF  $FF
  //    $00EB8000-$00EBFFFF  スプライトPCG・テキストエリア
  //
  //  CRTCのR20の下位5ビットが%1xx1xのとき
  //    $00EB0000-$00EB03FF  バスエラー
  //    $00EB0400-$00EB07FF  バスエラー
  //    $00EB0800-$00EB0811  設定
  //    $00EB0812-$00EB7FFF  $FF
  //    $00EB8000-$00EBFFFF  バスエラー
  //
  //  メモ
  //    スプライトPCG・テキストエリアにバイトサイズで書き込むとデータが破壊される
  //
  MMD_SPR {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "スプライト画面" : "Sprite Screen";
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc;
       return (byte) SpriteScreen.sprReadByte (a);
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc;
       return SpriteScreen.sprReadByte (a);
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc;
       return (short) SpriteScreen.sprReadWord (a);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc;
       return SpriteScreen.sprReadWord (a);
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc * 2;
       return SpriteScreen.sprReadLong (a);
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc;
       SpriteScreen.sprWriteByte (a, d);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc;
       SpriteScreen.sprWriteWord (a, d);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sprc * 2;
       SpriteScreen.sprWriteLong (a, d);
     }
  },  //MMD_SPR

  //--------------------------------------------------------------------------------
  //MMD_XB3 拡張ボード領域3
  //  $00EC0000~$00ECFFFF  64KB
  //  0x00ec0000  ユーザI/Oエリア
  //  0x00ec0000  Awesome
  //  0x00ec0000  Xellent30
  //  0x00ecc000  Mercury
  //  0x00ece000  Neptune
  //  0x00ecf000  Venus-X/030
  MMD_XB3 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張ボード領域 3" : "Expansion Board Area 3";
    }
    //リード
    @Override protected int mmdRbz (int a) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (XEiJ.currentAccelerator == XEiJ.ACCELERATOR_XELLENT30 &&
           XEiJ.xt3PortAddress <= a && a < XEiJ.xt3PortAddress + 0x4000) {  //Xellent30設定ポート
         return (a & 1) == 0 ? 0 : XEiJ.xt3PortRead ();
       }
       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
           (a & 0x00ffff80) == 0x00ecc080) {  //Mercury-Unit V4
         return MercuryUnit.mu4ReadByte (a);
       }
       return super.mmdRbz (a);  //バスエラー
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (XEiJ.currentAccelerator == XEiJ.ACCELERATOR_XELLENT30 &&
           XEiJ.xt3PortAddress <= a && a < XEiJ.xt3PortAddress + 0x4000) {  //Xellent30設定ポート
         return XEiJ.xt3PortRead ();
       }
       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
           (a & 0x00ffff80) == 0x00ecc080) {  //Mercury-Unit V4
         return MercuryUnit.mu4ReadWord (a);
       }
       return super.mmdRwz (a);  //バスエラー
     }
    @Override protected int mmdRls (int a) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (XEiJ.currentAccelerator == XEiJ.ACCELERATOR_XELLENT30 &&
           XEiJ.xt3PortAddress <= a && a < XEiJ.xt3PortAddress + 0x4000) {  //Xellent30設定ポート
         return XEiJ.xt3PortRead () << 16 | XEiJ.xt3PortRead ();
       }
       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
           (a & 0x00ffff80) == 0x00ecc080) {  //Mercury-Unit V4
         return MercuryUnit.mu4ReadWord (a) << 16 | MercuryUnit.mu4ReadWord (a + 2);
       }
       return super.mmdRls (a);  //バスエラー
     }
    @Override protected void mmdWb (int a, int d) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (XEiJ.currentAccelerator == XEiJ.ACCELERATOR_XELLENT30 &&
           XEiJ.xt3PortAddress <= a && a < XEiJ.xt3PortAddress + 0x4000) {  //Xellent30設定ポート
         if ((a & 1) == 0) {
         } else {
           XEiJ.xt3PortWrite (d);
         }
         return;
       }
       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
           (a & 0x00ffff80) == 0x00ecc080) {  //Mercury-Unit V4
         MercuryUnit.mu4WriteByte (a, d);
         return;
       }
       super.mmdWb (a, d);  //バスエラー
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (XEiJ.currentAccelerator == XEiJ.ACCELERATOR_XELLENT30 &&
           XEiJ.xt3PortAddress <= a && a < XEiJ.xt3PortAddress + 0x4000) {  //Xellent30設定ポート
         XEiJ.xt3PortWrite (d);
         return;
       }
       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
           (a & 0x00ffff80) == 0x00ecc080) {  //Mercury-Unit V4
         MercuryUnit.mu4WriteWord (a, d);
         return;
       }
       super.mmdWw (a, d);  //バスエラー
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       a &= XEiJ.BUS_MOTHER_MASK;
       if (XEiJ.currentAccelerator == XEiJ.ACCELERATOR_XELLENT30 &&
           XEiJ.xt3PortAddress <= a && a < XEiJ.xt3PortAddress + 0x4000) {  //Xellent30設定ポート
         XEiJ.xt3PortWrite (d >> 16);
         XEiJ.xt3PortWrite (d);
         return;
       }
       if (MercuryUnit.MU4_ON && MercuryUnit.mu4On &&
           (a & 0x00ffff80) == 0x00ecc080) {  //Mercury-Unit V4
         MercuryUnit.mu4WriteWord (a, d >> 16);
         MercuryUnit.mu4WriteWord (a + 2, d);
         return;
       }
       super.mmdWl (a, d);  //バスエラー
     }
  },  //MMD_XB3

  //--------------------------------------------------------------------------------
  //MMD_XTM Xellent30のSRAM
  MMD_XTM {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "Xellent30 の SRAM" : "SRAM on Xellent30";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a -= XEiJ.xt3MemoryPosition;
      return XEiJ.xt3MemoryArray[a];
    }
    @Override protected int mmdPbz (int a) {
      a -= XEiJ.xt3MemoryPosition;
      return XEiJ.xt3MemoryArray[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a -= XEiJ.xt3MemoryPosition;
      return XEiJ.xt3MemoryArray[a] << 8 | (XEiJ.xt3MemoryArray[a + 1] & 0xff);
    }
    @Override protected int mmdPwz (int a) {
      a -= XEiJ.xt3MemoryPosition;
      return (char) (XEiJ.xt3MemoryArray[a] << 8 | (XEiJ.xt3MemoryArray[a + 1] & 0xff));
    }
    @Override protected int mmdPls (int a) {
      a -= XEiJ.xt3MemoryPosition;
      return XEiJ.xt3MemoryArray[a] << 24 | (XEiJ.xt3MemoryArray[a + 1] & 0xff) << 16 | (char) (XEiJ.xt3MemoryArray[a + 2] << 8 | (XEiJ.xt3MemoryArray[a + 3] & 0xff));
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       return XEiJ.xt3MemoryArray[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       return XEiJ.xt3MemoryArray[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       return XEiJ.xt3MemoryArray[a] << 8 | (XEiJ.xt3MemoryArray[a + 1] & 0xff);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       return (char) (XEiJ.xt3MemoryArray[a] << 8 | (XEiJ.xt3MemoryArray[a + 1] & 0xff));
     }
    @Override protected int mmdRls (int a) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       return XEiJ.xt3MemoryArray[a] << 24 | (XEiJ.xt3MemoryArray[a + 1] & 0xff) << 16 | (char) (XEiJ.xt3MemoryArray[a + 2] << 8 | (XEiJ.xt3MemoryArray[a + 3] & 0xff));
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       XEiJ.xt3MemoryArray[a    ] = (byte)  d;
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       XEiJ.xt3MemoryArray[a    ] = (byte) (d >> 8);
       XEiJ.xt3MemoryArray[a + 1] = (byte)  d;
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       a -= XEiJ.xt3MemoryPosition;
       XEiJ.xt3MemoryArray[a    ] = (byte) (d >> 24);
       XEiJ.xt3MemoryArray[a + 1] = (byte) (d >> 16);
       XEiJ.xt3MemoryArray[a + 2] = (byte) (d >> 8);
       XEiJ.xt3MemoryArray[a + 3] = (byte)  d;
     }
  },  //MMD_XTM

  //--------------------------------------------------------------------------------
  //MMD_SMR SRAM
  MMD_SMR {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "SRAM" : "SRAM";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a];
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getShort (a);
      } else {
        return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
      }
    }
    @Override protected int mmdPwz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getChar (a);
      } else {
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
    }
    @Override protected int mmdPls (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getInt (a);
      } else {
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       a &= XEiJ.BUS_MOTHER_MASK;
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       return MainMemory.mmrM8[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       a &= XEiJ.BUS_MOTHER_MASK;
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       return MainMemory.mmrM8[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       return (short) mmdRwz (a);
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       a &= XEiJ.BUS_MOTHER_MASK;
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       int d;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         d = MainMemory.mmrBuffer.getChar (a);
       } else {
         d = (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
       return d;
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram * 2;
       a &= XEiJ.BUS_MOTHER_MASK;
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram * 2;
       int d;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         d = MainMemory.mmrBuffer.getInt (a);
       } else {
         d = MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
       return d;
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       if (SRAM.smrWriteEnableOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
         MainMemory.mmrM8[a] = (byte) d;
         if (a == 0x00ed002b || a == 0x00ed0059) {  //キーボードの配列または字体が変化した
           Keyboard.kbdRepaint ();  //キーボードが表示されているときkbdImageを作り直して再描画する
         } else if (a == 0x00ed005a) {  //HDMAX
           if (0x00fe0000 <= XEiJ.regPC0 && XEiJ.regPC0 <= 0x00ffffff) {  //IPLROMがSRAMのHDMAXを書き換えようとしている 
             SRAM.smrOverride ();  //SRAMの設定を上書きする
           }
         }
       } else {
         System.out.printf ("%08X writing $%02X to $%08X in a write-protected state\n", XEiJ.regPC0, d & 0xff, a);
         if (SRAM.smrSRAMBusErrorOn) {
           super.mmdWb (a, d);  //バスエラー
         }
       }
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
       if (SRAM.smrWriteEnableOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         XEiJ.mpuClockTime += XEiJ.busWaitTime.sram;
         MainMemory.mmrM8[a    ] = (byte) (d >> 8);
         MainMemory.mmrM8[a + 1] = (byte)  d;
         if (a == (0x00ed002b & -2) || a == (0x00ed0059 & -2)) {  //キーボードの配列または字体が変化した
           Keyboard.kbdRepaint ();  //キーボードが表示されているときkbdImageを作り直して再描画する
         }
       } else {
         System.out.printf ("%08X writing $%04X to $%08X in a write-protected state\n", XEiJ.regPC0, d & 0xffff, a);
         if (SRAM.smrSRAMBusErrorOn) {
           super.mmdWw (a, d);  //バスエラー
         }
       }
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       if (SRAM.smrWriteEnableOn) {
         mmdWw (a    , d >> 16);
         mmdWw (a + 2, d      );
       } else {
         System.out.printf ("%08X writing $%08X to $%08X in a write-protected state\n", XEiJ.regPC0, d, a);
         if (SRAM.smrSRAMBusErrorOn) {
           super.mmdWl (a, d);  //バスエラー
         }
       }
     }
  },  //MMD_SMR

  //--------------------------------------------------------------------------------
  //MMD_XB4 拡張ボード領域4
  //  $00EE0000~$00EFFFFF  バンクメモリ
  MMD_XB4 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "拡張ボード領域 4" : "Expansion Board Area 4";
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         return XEiJ.bnkMemory[a];
       }
       return super.mmdRbs (a);  //バスエラー
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         return XEiJ.bnkMemory[a] & 0xff;
       }
       return super.mmdRbz (a);  //バスエラー
     }
    @Override protected int mmdRws (int a) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         return ((XEiJ.bnkMemory[a    ]       ) << 8 |
                 (XEiJ.bnkMemory[a + 1] & 0xff));
       }
       return super.mmdRws (a);  //バスエラー
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         return ((XEiJ.bnkMemory[a    ] & 0xff) << 8 |
                 (XEiJ.bnkMemory[a + 1] & 0xff));
       }
       return super.mmdRwz (a);  //バスエラー
     }
    @Override protected int mmdRls (int a) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         return ((XEiJ.bnkMemory[a    ]       ) << 24 |
                 (XEiJ.bnkMemory[a + 1] & 0xff) << 16 |
                 (XEiJ.bnkMemory[a + 2] & 0xff) <<  8 |
                 (XEiJ.bnkMemory[a + 3] & 0xff));
       }
       return super.mmdRls (a);  //バスエラー
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         XEiJ.bnkMemory[a] = (byte) d;
         return;
       }
       super.mmdWb (a, d);  //バスエラー
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         XEiJ.bnkMemory[a    ] = (byte) (d >> 8);
         XEiJ.bnkMemory[a + 1] = (byte)  d;
         return;
       }
       super.mmdWw (a, d);  //バスエラー
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       if (XEiJ.bnkOn) {
         a &= XEiJ.BUS_MOTHER_MASK;
         a = XEiJ.bnkPageStart + (a - 0x00ee0000);
         XEiJ.bnkMemory[a    ] = (byte) (d >> 24);
         XEiJ.bnkMemory[a + 1] = (byte) (d >> 16);
         XEiJ.bnkMemory[a + 2] = (byte) (d >>  8);
         XEiJ.bnkMemory[a + 3] = (byte)  d;
         return;
       }
       super.mmdWl (a, d);  //バスエラー
     }
  },  //MMD_XB4

  //--------------------------------------------------------------------------------
  //MMD_ROM ROM
  MMD_ROM {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "ROM" : "ROM";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a];
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getShort (a);
      } else {
        return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
      }
    }
    @Override protected int mmdPwz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getChar (a);
      } else {
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
    }
    @Override protected int mmdPls (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getInt (a);
      } else {
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getShort (a);
       } else {
         return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getChar (a);
       } else {
         return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       XEiJ.mpuClockTime += XEiJ.busWaitTime.romlong;
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getInt (a);
       } else {
         return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
     }
  },  //MMD_ROM

  MMD_RO6 {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "ROM (68060)" : "ROM (68060)";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a];
    }
    @Override protected int mmdPbz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      return MainMemory.mmrM8[a] & 0xff;
    }
    @Override protected int mmdPws (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getShort (a);
      } else {
        return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
      }
    }
    @Override protected int mmdPwz (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getChar (a);
      } else {
        return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
      }
    }
    @Override protected int mmdPls (int a) {
      a &= XEiJ.BUS_MOTHER_MASK;
      if (MainMemory.MMR_USE_BYTE_BUFFER) {
        return MainMemory.mmrBuffer.getInt (a);
      } else {
        return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
      }
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a];
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       return MainMemory.mmrM8[a] & 0xff;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getShort (a);
       } else {
         return MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff);
       }
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.rom;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getChar (a);
       } else {
         return (char) (MainMemory.mmrM8[a] << 8 | (MainMemory.mmrM8[a + 1] & 0xff));
       }
     }
    @Override protected int mmdRls (int a) throws M68kException {
       if (MC68060.CAT_ON &&
           !XEiJ.busWaitTime.isDma) {
         MC68060.catReadMainROM (a);
       } else {
         XEiJ.mpuClockTime += XEiJ.busWaitTime.romlong;
       }
       a &= XEiJ.BUS_MOTHER_MASK;
       if (MainMemory.MMR_USE_BYTE_BUFFER) {
         return MainMemory.mmrBuffer.getInt (a);
       } else {
         return MainMemory.mmrM8[a] << 24 | (MainMemory.mmrM8[a + 1] & 0xff) << 16 | (char) (MainMemory.mmrM8[a + 2] << 8 | (MainMemory.mmrM8[a + 3] & 0xff));
       }
     }
  },  //MMD_RO6

  //--------------------------------------------------------------------------------
  //MMD_IBP 命令ブレークポイント
  MMD_IBP {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "命令ブレークポイント" : "Instruction Break Point";
    }
    @Override protected int mmdRwz (int a) throws M68kException {
       if (InstructionBreakPoint.IBP_ON) {
         InstructionBreakPoint.InstructionBreakRecord[] hashTable = InstructionBreakPoint.ibpHashTable;
         for (InstructionBreakPoint.InstructionBreakRecord r = hashTable[a >> 1 & InstructionBreakPoint.IBP_HASH_MASK];
              r != null;
              r = r.ibrNext) {  //同じ物理ハッシュコードを持つ命令ブレークポイントについて
           if (r.ibrPhysicalAddress == a) {  //命令ブレークポイントが設定されているとき
             if (r.ibrValue == r.ibrTarget) {  //現在値が目標値と一致しているとき
               if (r.ibrThreshold < 0) {  //インスタントのとき
                 InstructionBreakPoint.ibpRemove (r.ibrLogicalAddress, XEiJ.regSRS);  //取り除く
                 XEiJ.mpuContinue = true;  //ステップ実行を継続する
               } else if (r.ibrTarget < r.ibrThreshold) {  //インスタント化しているとき
                 r.ibrTarget = r.ibrThreshold;  //目標値を閾値に戻す
                 XEiJ.mpuContinue = true;  //ステップ実行を継続する
               } else {  //インスタントでなくインスタント化していないとき
                 if (r.ibrScriptElement != null &&  //スクリプトが指定されていて
                     r.ibrScriptElement.exlEval (ExpressionEvaluator.EVM_EXPRESSION).exlFloatValue.iszero ()) {  //条件が成立していないとき
                   break;  //続行する
                 }
                 r.ibrTarget++;  //目標値を増やす
                 XEiJ.mpuContinue = false;  //ステップ実行を継続しない
               }
               M68kException.m6eNumber = M68kException.M6E_INSTRUCTION_BREAK_POINT;  //命令ブレークポイントによる停止。XEiJ.mpuContinueを設定すること
               throw M68kException.m6eSignal;  //停止する
             } else {  //現在値が目標値と一致していないとき
               if (r.ibrWaitInstruction != null) {  //待機ポイントがあるとき
                 WaitInstruction.instruction = r.ibrWaitInstruction;  //待機命令を設定して
                 return XEiJ.EMX_OPCODE_EMXWAIT;  //返す
               }
               r.ibrValue++;  //現在値を増やす
               break;  //続行する
             }
           }
         }
       }
       if (DataBreakPoint.DBP_ON) {
         return DataBreakPoint.dbpMemoryMap[a >>> XEiJ.BUS_PAGE_BITS].mmdRwz (a);
       } else {
         return XEiJ.busMemoryMap[a >>> XEiJ.BUS_PAGE_BITS].mmdRwz (a);
       }
     }
  },  //MMD_IBP

  //--------------------------------------------------------------------------------
  //MMD_DBP データブレークポイント
  MMD_DBP {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "データブレークポイント" : "Data Break Point";
    }
    //ピーク
    @Override protected byte mmdPbs (int a) {
      return (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdPbs (a);
    }
    @Override protected int mmdPbz (int a) {
      return (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdPbz (a);
    }
    @Override protected int mmdPws (int a) {
      return (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdPws (a);
    }
    @Override protected int mmdPwz (int a) {
      return (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdPwz (a);
    }
    @Override protected int mmdPls (int a) {
      return (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdPls (a);
    }
    //リード
    @Override protected byte mmdRbs (int a) throws M68kException {
       int d = (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdRbs (a);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_BYTE, a, d, false);
       return (byte) d;
     }
    @Override protected int mmdRbz (int a) throws M68kException {
       int d = (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdRbz (a);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_BYTE, a, d, false);
       return d;
     }
    @Override protected int mmdRws (int a) throws M68kException {
       int d = (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdRws (a);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_WORD, a, d, false);
       return d;
     }
    @Override protected int mmdRwz (int a) throws M68kException {
       int d = (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdRwz (a);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_WORD, a, d, false);
       return d;
     }
    @Override protected int mmdRls (int a) throws M68kException {
       int d = (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdRls (a);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_LONG, a, d, false);
       return d;
     }
    //ライト
    @Override protected void mmdWb (int a, int d) throws M68kException {
       (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdWb (a, d);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_BYTE, a, d, true);
     }
    @Override protected void mmdWw (int a, int d) throws M68kException {
       (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdWw (a, d);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_WORD, a, d, true);
     }
    @Override protected void mmdWl (int a, int d) throws M68kException {
       (XEiJ.regSRS != 0 ? XEiJ.busSuperMap : XEiJ.busUserMap)[a >>> XEiJ.BUS_PAGE_BITS].mmdWl (a, d);
       DataBreakPoint.dbpBreak (DataBreakPoint.DBP_LONG, a, d, true);
     }
  },  //MMD_DBP

  //--------------------------------------------------------------------------------
  //MMD_NUL ヌルデバイス
  MMD_NUL {
    @Override public String toString () {
      return Multilingual.mlnJapanese ? "ヌルデバイス" : "Null Device";
    }
  };  //MMD_NUL

  //--------------------------------------------------------------------------------
  //ピークのデフォルト
  //  エラーや副作用なしでリードする
  //  バスエラーのときは-1をキャストした値を返す
  //  リードがデバイスの状態を変化させる可能性がある場合は個別に処理すること
  protected byte mmdPbs (int a) {
    return (byte) mmdPbz (a);
  }
  protected int mmdPbz (int a) {
    try {
      return mmdRbz (a);
    } catch (M68kException e) {
    }
    return 0xff;
  }
  protected int mmdPws (int a) {
    return (short) mmdPwz (a);
  }
  protected int mmdPwz (int a) {
    try {
      return mmdRwz (a);
    } catch (M68kException e) {
    }
    return 0xffff;
  }
  protected int mmdPls (int a) {
    try {
      return mmdRls (a);
    } catch (M68kException e) {
    }
    return -1;
  }
  //リードのデフォルト
  //  バイトとワードの符号拡張はゼロ拡張を呼び出す
  //  符号なしとロングはバスエラー
  protected byte mmdRbs (int a) throws M68kException {
    return (byte) mmdRbz (a);
  }
  protected int mmdRbz (int a) throws M68kException {
    MC68060.m60BusErrorOnRead ();
    M68kException.m6eNumber = M68kException.M6E_ACCESS_FAULT;
    M68kException.m6eAddress = a;
    M68kException.m6eDirection = XEiJ.MPU_WR_READ;
    M68kException.m6eSize = XEiJ.MPU_SS_BYTE;
    throw M68kException.m6eSignal;
  }
  protected int mmdRws (int a) throws M68kException {
    return (short) mmdRwz (a);
  }
  protected int mmdRwz (int a) throws M68kException {
    MC68060.m60BusErrorOnRead ();
    M68kException.m6eNumber = M68kException.M6E_ACCESS_FAULT;
    M68kException.m6eAddress = a;
    M68kException.m6eDirection = XEiJ.MPU_WR_READ;
    M68kException.m6eSize = XEiJ.MPU_SS_WORD;
    throw M68kException.m6eSignal;
  }
  protected int mmdRls (int a) throws M68kException {
    MC68060.m60BusErrorOnRead ();
    M68kException.m6eNumber = M68kException.M6E_ACCESS_FAULT;
    M68kException.m6eAddress = a;
    M68kException.m6eDirection = XEiJ.MPU_WR_READ;
    M68kException.m6eSize = XEiJ.MPU_SS_LONG;
    throw M68kException.m6eSignal;
  }
  //ポークのデフォルト
  //  エラーや副作用なしでライトする
  //  ライトがデバイスの状態を変化させる可能性がある場合は個別に処理すること
  protected void mmdVb (int a, int d) {
    try {
      mmdWb (a, d);
    } catch (M68kException e) {
    }
  }
  protected void mmdVw (int a, int d) {
    try {
      mmdWw (a, d);
    } catch (M68kException e) {
    }
  }
  protected void mmdVl (int a, int d) {
    try {
      mmdWl (a, d);
    } catch (M68kException e) {
    }
  }
  //ライトのデフォルト
  //  すべてバスエラー
  protected void mmdWb (int a, int d) throws M68kException {
    MC68060.m60BusErrorOnWrite ();
    M68kException.m6eNumber = M68kException.M6E_ACCESS_FAULT;
    M68kException.m6eAddress = a;
    M68kException.m6eDirection = XEiJ.MPU_WR_WRITE;
    M68kException.m6eSize = XEiJ.MPU_SS_BYTE;
    throw M68kException.m6eSignal;
  }
  protected void mmdWw (int a, int d) throws M68kException {
    MC68060.m60BusErrorOnWrite ();
    M68kException.m6eNumber = M68kException.M6E_ACCESS_FAULT;
    M68kException.m6eAddress = a;
    M68kException.m6eDirection = XEiJ.MPU_WR_WRITE;
    M68kException.m6eSize = XEiJ.MPU_SS_WORD;
    throw M68kException.m6eSignal;
  }
  protected void mmdWl (int a, int d) throws M68kException {
    MC68060.m60BusErrorOnWrite ();
    M68kException.m6eNumber = M68kException.M6E_ACCESS_FAULT;
    M68kException.m6eAddress = a;
    M68kException.m6eDirection = XEiJ.MPU_WR_WRITE;
    M68kException.m6eSize = XEiJ.MPU_SS_LONG;
    throw M68kException.m6eSignal;
  }
}  //enum MemoryMappedDevice