xeij/MainMemory.java
//========================================================================================
//  MainMemory.java
//    en:Main memory
//    ja:メインメモリ
//  Copyright (C) 2003-2022 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/
//========================================================================================

//----------------------------------------------------------------------------------------
//データの格納単位
//  配列の1要素に何バイトずつ格納するのが効率的か
//    1バイトずつの場合
//      + インデックスとアドレスが一致するので扱いやすい
//      - ワードアクセスとロングアクセスの配列参照の回数が増えるのでインデックスの範囲チェックのオーバーヘッドが大きくなる
//    2バイトずつの場合
//      + アラインメントの合っているワードアクセスとロングアクセスの配列参照の回数が減る
//      - ライトバイトはリードも必要になるので配列参照の回数が増える
//      - インデックスを求めるときにアドレスをシフトしなければならない
//      ? テキスト画面はアクセスマスクが16ビットだがCRTCのキャラクタが8ビット単位なので端数が生じることに変わりはない
//      ? グラフィックス画面は1ピクセルが16ビットだがパレットが8ビット単位なので8ビットずつ分解しなければならないことに変わりはない
//    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.nio.*;  //ByteBuffer,ByteOrder
import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap

public class MainMemory {

  public static final boolean MMR_USE_BYTE_BUFFER = false;  //true=ワードとロングのアクセスにバイトバッファを使う。遅くなる

  //メインメモリ
  public static final byte[] mmrM8 = new byte[XEiJ.BUS_ARRAY_SIZE];
  public static ByteBuffer mmrBuffer;  //mmrM8をラップしたバイトバッファ

  public static int mmrHumanVersion;  //Human68kのバージョン。-1=Human68kではない,0=未確認,0x0100/0x0101/0x0200/0x0201/0x0202/0x0203/0x020f/0x025f/0x0301/0x0302=バージョン
  public static boolean mmrFEfuncActivated;  //true=FEファンクション命令が有効になった

  //TwentyOne.xのオプション
  public static int mmrTwentyOneOptionAddress;  //TwentyOne.xのオプションのアドレス,-1=非対応,0=未確認

  //メインメモリのサイズ
  //  0x00100000  1MB
  //  0x00200000  2MB
  //  0x00400000  4MB
  //  0x00600000  6MB
  //  0x00800000  8MB
  //  0x00a00000  10MB
  //  0x00c00000  12MB
  //  のいずれか
  public static int mmrMemorySizeRequest;  //次回のリセット後のメインメモリのサイズ。メニューで設定を変更してからリセットするまでの間、現在のメインメモリのサイズと区別する必要がある
  public static int mmrMemorySizeCurrent;  //現在のメインメモリのサイズ

  //  メインメモリの内容を保存する
  //  レジュームできるわけではないがRAMディスクの内容を保存せずにエミュレータを終了しまったときに役立つ
  //  設定ファイルが大きくなるので起動と終了がややもたつく
  public static boolean mmrMemorySaveOn;  //true=メインメモリの内容を保存する

  //mmrInit ()
  //  初期化
  public static void mmrInit () {

    int mainMemorySizeMB = Settings.sgsGetInt ("memory");  //メインメモリのサイズ
    mmrMemorySizeRequest = (mainMemorySizeMB == 1 ||
                            mainMemorySizeMB == 2 ||
                            mainMemorySizeMB == 4 ||
                            mainMemorySizeMB == 6 ||
                            mainMemorySizeMB == 8 ||
                            mainMemorySizeMB == 10 ||
                            mainMemorySizeMB == 12 ? mainMemorySizeMB << 20 :
                            12 << 20);
    mmrMemorySizeCurrent = mmrMemorySizeRequest;
    System.out.printf (Multilingual.mlnJapanese ?
                       "メインメモリのサイズは %dMB です\n" :
                       "Main memory size is %dMB\n",
                       mmrMemorySizeCurrent >>> 20);

    mmrMemorySaveOn = Settings.sgsGetOnOff ("memorysave");  //メインメモリの内容を保存するか

    byte[] mainMemoryArray = Settings.sgsGetData ("memorydata");  //メインメモリの内容(gzip+base64)
    if (mainMemoryArray.length != 0) {  //復元するデータがある
      System.out.println (Multilingual.mlnJapanese ?
                          "メインメモリのデータを復元します" :
                          "Main memory data is restored");
      System.arraycopy (mainMemoryArray, 0,  //from
                        mmrM8, 0,  //to
                        Math.min (mainMemoryArray.length, mmrMemorySizeCurrent));
      if (mainMemoryArray.length < mmrMemorySizeCurrent) {
        Arrays.fill (mmrM8, mainMemoryArray.length, mmrMemorySizeCurrent, (byte) 0);
      }
    } else {
      System.out.println (Multilingual.mlnJapanese ?
                          "メインメモリをゼロクリアします" :
                          "Main memory is zero-cleared");
      Arrays.fill (mmrM8, 0, mmrMemorySizeCurrent, (byte) 0);
    }

    //mmrM8 = new byte[XEiJ.BUS_MOTHOR_SIZE];
    if (MMR_USE_BYTE_BUFFER) {
      mmrBuffer = ByteBuffer.wrap (mmrM8);
      mmrBuffer.order (ByteOrder.BIG_ENDIAN);
    }

    mmrHumanVersion = 0;
    mmrFEfuncActivated = false;
    if (HFS.HFS_USE_TWENTY_ONE) {
      mmrTwentyOneOptionAddress = 0;
    }

  }  //mmrInit()

  public static void mmrReset () {

    mmrMemorySizeCurrent = mmrMemorySizeRequest;
    mmrHumanVersion = 0;
    mmrFEfuncActivated = false;
    if (HFS.HFS_USE_TWENTY_ONE) {
      mmrTwentyOneOptionAddress = 0;
    }

    XEiJ.busSuper (MemoryMappedDevice.MMD_MMR, 0x00000000, 0x00002000);
    XEiJ.busUser (MemoryMappedDevice.MMD_MMR, 0x00002000, mmrMemorySizeCurrent);
    if (mmrMemorySizeCurrent < 0x00200000) {
      XEiJ.busUser (MemoryMappedDevice.MMD_MM1, mmrMemorySizeCurrent, 0x00200000);
      XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, 0x00200000, 0x00c00000);
    } else {
      XEiJ.busSuper (MemoryMappedDevice.MMD_NUL, mmrMemorySizeCurrent, 0x00c00000);
    }

    mmrSetSupervisorArea (0);

  }  //mmrReset()

  //mmrSetSupervisorArea (d)
  //  スーパーバイザ領域設定
  public static void mmrSetSupervisorArea (int d) {
    int a = ((d & 0xff) + 1) << 13;
    if (mmrMemorySizeCurrent < 0x00200000) {
      //  1MB搭載機
      //    0  a  1     2     3
      //    SSSUUUuuuuuuNNNNNN
      //    0     1  a  2     3
      //    SSSSSSsssuuuNNNNNN
      if (a < 0x00100000) {
        XEiJ.busSuper (MemoryMappedDevice.MMD_MMR, 0x00000000, a);
        XEiJ.busUser (MemoryMappedDevice.MMD_MMR, a, 0x00100000);
        XEiJ.busUser (MemoryMappedDevice.MMD_MM1, 0x00100000, 0x00200000);
      } else {
        XEiJ.busSuper (MemoryMappedDevice.MMD_MMR, 0x00000000, 0x00100000);
        XEiJ.busSuper (MemoryMappedDevice.MMD_MM1, 0x00100000, a);
        XEiJ.busUser (MemoryMappedDevice.MMD_MM1, a, 0x00200000);
      }
    } else {
      //  2MB搭載機
      //    0  a  1     2     3
      //    SSSUUUUUUUUUNNNNNN
      //    0     1  a  2     3
      //    SSSSSSSSSUUUNNNNNN
      XEiJ.busSuper (MemoryMappedDevice.MMD_MMR, 0x00000000, a);
      XEiJ.busUser (MemoryMappedDevice.MMD_MMR, a, 0x00200000);
    }
  }

  //d = mmrRbs (a)
  //  メモリリードバイト符号拡張
  public static byte mmrRbs (int a) {
    //byteの配列からbyteのデータを読み出す
    //       a
    //    +-----+
    //    |  a  |
    //    +-----+
    //    |  d  |
    //    +-----+
    return mmrM8[a & XEiJ.BUS_MOTHER_MASK];
  }  //mmrRbs(int)

  //d = mmrRbz (a)
  //  メモリリードバイトゼロ拡張
  public static int mmrRbz (int a) {
    //byteの配列からbyteのデータを読み出す
    //       a
    //    +-----+
    //    |  a  |
    //    +-----+
    //    |  d  |
    //    +-----+
    return mmrM8[a & XEiJ.BUS_MOTHER_MASK] & 255;
  }  //mmrRbz(int)

  //d = mmrRws (a)
  //  メモリリードワード符号拡張
  public static int mmrRws (int a) {
    if (MMR_USE_BYTE_BUFFER) {
      return mmrBuffer.getShort (a & XEiJ.BUS_MOTHER_MASK);
    } else {
      //byteの配列からshortのデータを読み出す
      //       a    a+1
      //    +-----+-----+
      //    |  a  | a+1 |
      //    +-----+-----+
      //    |     d     |
      //    +-----+-----+
      return mmrM8[a & XEiJ.BUS_MOTHER_MASK] << 8 | mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] & 255;
    }
  }  //mmrRws(int)

  //d = mmrRwz (a)
  //  メモリリードワードゼロ拡張
  public static int mmrRwz (int a) {
    if (MMR_USE_BYTE_BUFFER) {
      return mmrBuffer.getChar (a & XEiJ.BUS_MOTHER_MASK);
    } else {
      //byteの配列からunsigned shortのデータを読み出す
      //       a    a+1
      //    +-----+-----+
      //    |  a  | a+1 |
      //    +-----+-----+
      //    |     d     |
      //    +-----+-----+
      return (char) (mmrM8[a & XEiJ.BUS_MOTHER_MASK] << 8 | mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] & 255);
    }
  }  //mmrRwz(int)

  //d = mmrRls (a)
  //  メモリリードロング符号拡張
  public static int mmrRls (int a) {
    if (MMR_USE_BYTE_BUFFER) {
      return mmrBuffer.getInt (a & XEiJ.BUS_MOTHER_MASK);
    } else {
      //byteの配列からintのデータを読み出す
      //       a    a+1   a+2   a+3
      //    +-----+-----+-----+-----+
      //    |  a  | a+1 | a+2 | a+3 |
      //    +-----+-----+-----+-----+
      //    |           d           |
      //    +-----+-----+-----+-----+
      return mmrM8[a & XEiJ.BUS_MOTHER_MASK] << 24 | (mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] & 255) << 16 | (char) (mmrM8[a + 2 & XEiJ.BUS_MOTHER_MASK] << 8 | mmrM8[a + 3 & XEiJ.BUS_MOTHER_MASK] & 255);
    }
  }  //mmrRls(int)

  //d = mmrRqs (a)
  //  メモリリードクワッド符号拡張
  public static long mmrRqs (int a) {
    if (MMR_USE_BYTE_BUFFER) {
      return mmrBuffer.getLong (a & XEiJ.BUS_MOTHER_MASK);
    } else {
      return ((long) (mmrM8[a     & XEiJ.BUS_MOTHER_MASK] << 24 |
                      mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] << 16 & 0x00ff0000 |
                      mmrM8[a + 2 & XEiJ.BUS_MOTHER_MASK] <<  8 & 0x0000ff00 |
                      mmrM8[a + 3 & XEiJ.BUS_MOTHER_MASK]       & 0x000000ff) << 32 |
              (long) (mmrM8[a + 4 & XEiJ.BUS_MOTHER_MASK] << 24 |
                      mmrM8[a + 5 & XEiJ.BUS_MOTHER_MASK] << 16 & 0x00ff0000 |
                      mmrM8[a + 6 & XEiJ.BUS_MOTHER_MASK] <<  8 & 0x0000ff00 |
                      mmrM8[a + 7 & XEiJ.BUS_MOTHER_MASK]       & 0x000000ff) & 0x00000000ffffffffL);
    }
  }  //mmrRqs(int)

  //mmrWb (a, d)
  //  メモリライトバイト
  public static void mmrWb (int a, int d) {
    //byteの配列にbyteのデータを書き込む
    //       a
    //    +-----+
    //    |  d  |
    //    +-----+
    //    |  a  |
    //    +-----+
    mmrM8[a & XEiJ.BUS_MOTHER_MASK] = (byte) d;
  }  //mmrWb(int,int)

  //mmrWw (a, d)
  //  メモリライトワード
  public static void mmrWw (int a, int d) {
    if (MMR_USE_BYTE_BUFFER) {
      mmrBuffer.putShort (a & XEiJ.BUS_MOTHER_MASK, (short) d);
    } else {
      //byteの配列にshortのデータを書き込む
      //       a    a+1
      //    +-----+-----+
      //    |     d     |
      //    +-----+-----+
      //    |  a  | a+1 |
      //    +-----+-----+
      mmrM8[a     & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 8);
      mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] = (byte)  d;
    }
  }  //mmrWw(int,int)

  //a = mmrWl (a, d)
  //  メモリライトロング
  public static void mmrWl (int a, int d) {
    if (MMR_USE_BYTE_BUFFER) {
      mmrBuffer.putInt (a & XEiJ.BUS_MOTHER_MASK, d);
    } else {
      //byteの配列にintのデータを書き込む
      //       a    a+1   a+2   a+3
      //    +-----+-----+-----+-----+
      //    |           d           |
      //    +-----+-----+-----+-----+
      //    |  a  | a+1 | a+2 | a+3 |
      //    +-----+-----+-----+-----+
      mmrM8[a     & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 24);
      mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 16);
      mmrM8[a + 2 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 8);
      mmrM8[a + 3 & XEiJ.BUS_MOTHER_MASK] = (byte)  d;
    }
  }  //mmrWl(int,int)

  //a = mmrWq (a, d)
  //  メモリライトクワッド
  public static void mmrWq (int a, long d) {
    if (MMR_USE_BYTE_BUFFER) {
      mmrBuffer.putLong (a & XEiJ.BUS_MOTHER_MASK, d);
    } else {
      mmrM8[a     & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 56);
      mmrM8[a + 1 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 48);
      mmrM8[a + 2 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 40);
      mmrM8[a + 3 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 32);
      mmrM8[a + 4 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 24);
      mmrM8[a + 5 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 16);
      mmrM8[a + 6 & XEiJ.BUS_MOTHER_MASK] = (byte) (d >> 8);
      mmrM8[a + 7 & XEiJ.BUS_MOTHER_MASK] = (byte)  d;
    }
  }  //mmrWq(int,long)

  //mmrWba (a, d, ...)
  //  メモリライトバイトアレイ
  public static void mmrWba (int a, int... da) {
    for (int d : da) {
      mmrWb (a, d);
      a++;
    }
  }  //mmrWba(int,int...)

  //mmrWwa (a, d, ...)
  //  メモリライトワードアレイ
  public static void mmrWwa (int a, int... da) {
    for (int d : da) {
      mmrWw (a, d);
      a += 2;
    }
  }  //mmrWwa(int,int...)

  //a = mmrWla (a, d, ...)
  //  メモリライトロングアレイ
  public static void mmrWla (int a, int... da) {
    for (int d : da) {
      mmrWl (a, d);
      a += 4;
    }
  }  //mmrWla(int,int...)

  //len = mmrStrlen (a, l)
  public static int mmrStrlen (int a, int l) {
    for (int i = 0; i < l; i++) {
      if (mmrM8[a + i] == 0) {
        return i;
      }
    }
    return l;
  }  //mmrStrlen(int,int)

  //s = mmrRstr (a, l)
  //sb = mmrRstr (sb, a, l)
  //  メモリリードストリング
  //  文字列を読み出す
  //  対応する文字がないときは'.'または'※'になる
  //  制御コードは'.'になる
  public static String mmrRstr (int a, int l) {
    return mmrRstr (new StringBuilder (), a, l).toString ();
  }  //mmrRstr(int,int)
  public static StringBuilder mmrRstr (StringBuilder sb, int a, int l) {
    for (int i = 0; i < l; i++) {
      int s = mmrRbz (a + i);
      char c;
      if (0x81 <= s && s <= 0x9f || 0xe0 <= s && s <= 0xef) {  //SJISの2バイトコードの1バイト目
        int t = i + 1 < l ? mmrRbz (a + i + 1) : 0;
        if (0x40 <= t && t != 0x7f && t <= 0xfc) {  //SJISの2バイトコードの2バイト目
          c = CharacterCode.chrSJISToChar[s << 8 | t];  //2バイトで変換する
          if (c == 0) {  //対応する文字がない
            c = '※';
          }
          i++;
        } else {  //SJISの2バイトコードの2バイト目ではない
          c = '.';  //SJISの2バイトコードの1バイト目ではなかった
        }
      } else {  //SJISの2バイトコードの1バイト目ではない
        c = CharacterCode.chrSJISToChar[s];  //1バイトで変換する
        if (c < 0x20 || c == 0x7f) {  //対応する文字がないまたは制御コード
          c = '.';
        }
      }
      sb.append (c);
    }
    return sb;
  }  //mmrRstr(StringBuilder,int,int)

  //a = mmrWstr (a, s)
  //  メモリライトストリング
  //  文字列をSJISに変換しながら書き込む
  //  SJISに変換できない文字は'※'になる
  //  文字列の直後のアドレス(マスク済み)を返す
  public static void mmrWstr (int a, String s) {
    int l = s.length ();
    for (int i = 0; i < l; i++) {
      int c = CharacterCode.chrCharToSJIS[s.charAt (i)];
      if (c == 0 && s.charAt (i) != '\0') {  //SJISに変換できないとき'\0'でない文字が0になる
        mmrWw (a, 0x81a6);  //※
        a += 2;
      } else if (c <= 0x00ff) {
        mmrWb (a, c);
        a++;
      } else {
        mmrWw (a, c);
        a += 2;
      }
    }
  }  //mmrWstr(int,String)

  //top = mmrHumanTop ()
  //  Human68kのメモリ管理の先頭のアドレス(HUMAN.SYSのメモリ管理テーブルのアドレス)を返す
  //  -1  Human68kが読み込まれていないか、未知のバージョン
  //  Human68kのメモリ管理の先頭のアドレス。Human200以降は0x1c04に入っているものと同じ
  //    human100 0x00010a10
  //    human101 0x00010a1a
  //    human200 0x00007dae
  //    human201 0x00007dae
  //    human202 0x00007dce
  //    human203 0x00007d50
  //    human215 0x0000841c
  //    human301 0x000082d0
  //    human302 0x00008372 human295
  public static int mmrHumanTop () {
    return (mmrHumanVersion == 0x0302 ||
            mmrHumanVersion == 0x025f ? 0x00008372 :
            mmrHumanVersion == 0x0301 ? 0x000082d0 :
            mmrHumanVersion == 0x020f ? 0x0000841c :
            mmrHumanVersion == 0x0203 ? 0x00007d50 :
            mmrHumanVersion == 0x0202 ? 0x00007dce :
            mmrHumanVersion == 0x0201 ? 0x00007dae :
            mmrHumanVersion == 0x0200 ? 0x00007dae :
            mmrHumanVersion == 0x0101 ? 0x00010a1a :
            mmrHumanVersion == 0x0100 ? 0x00010a10 :
            -1);
  }  //mmrHumanTop()

  //btm = mmrHumanBtm ()
  //  Human68kのメモリ管理の末尾のアドレスを返す
  //  -1  Human68kが読み込まれていないか、未知のバージョン
  public static int mmrHumanBtm () {
    return mmrHumanVersion > 0 ? MC68060.mmuPeekLongData (0x00001c00, 1) : -1;
  }  //mmrHumanBtm()

  //pmm = mmrHumanPmm ()
  //  Human68kの実行中のプロセスのメモリ管理テーブルのアドレス(GETPDB()-16)を返す
  //  -1  Human68kが読み込まれていないか、未知のバージョン
  //  実行中のプロセスのメモリ管理テーブルのアドレス
  //    human100 [0x00008a98]
  //    human101 [0x00008a92]
  //    human200 [0x00012d88]
  //    human201 [0x00012d88]
  //    human202 [0x00012dc8]
  //    human203 [0x00012b54]
  //    human215 [0x00013c9c]
  //    human301 [0x00013bf4]
  //    human302 [0x00013d0a] human295
  public static int mmrHumanPmm () {
    return (mmrHumanVersion == 0x0302 ||
            mmrHumanVersion == 0x025f ? MC68060.mmuPeekLongData (0x00013d0a, 1) :
            mmrHumanVersion == 0x0301 ? MC68060.mmuPeekLongData (0x00013bf4, 1) :
            mmrHumanVersion == 0x020f ? MC68060.mmuPeekLongData (0x00013c9c, 1) :
            mmrHumanVersion == 0x0203 ? MC68060.mmuPeekLongData (0x00012b54, 1) :
            mmrHumanVersion == 0x0202 ? MC68060.mmuPeekLongData (0x00012dc8, 1) :
            mmrHumanVersion == 0x0201 ? MC68060.mmuPeekLongData (0x00012d88, 1) :
            mmrHumanVersion == 0x0200 ? MC68060.mmuPeekLongData (0x00012d88, 1) :
            mmrHumanVersion == 0x0101 ? MC68060.mmuPeekLongData (0x00008a92, 1) :
            mmrHumanVersion == 0x0100 ? MC68060.mmuPeekLongData (0x00008a98, 1) :
            -1);
  }  //mmrHumanPmm()

  //nul = mmrHumanNul ()
  //  Human68kのNULデバイスドライバのアドレスを返す
  //  -1  Human68kが読み込まれていないか、未知のバージョンか、NULデバイスドライバが見つからない
  //  NULデバイスドライバのアドレス
  //    human100 0x0000b83e
  //    human101 0x0000b84c
  //    human200 0x0000ece6
  //    human201 0x0000ece6
  //    human202 0x0000ed36
  //    human203 0x0000eac2
  //    human215 0x0000fa04
  //    human301 0x0000f93a
  //    human302 0x0000fa50 human295
  public static int mmrHumanNul () {
    int a = (mmrHumanVersion == 0x0302 ||
             mmrHumanVersion == 0x025f ? 0x0000fa50 :
             mmrHumanVersion == 0x0301 ? 0x0000f93a :
             mmrHumanVersion == 0x020f ? 0x0000fa04 :
             mmrHumanVersion == 0x0203 ? 0x0000eac2 :
             mmrHumanVersion == 0x0202 ? 0x0000ed36 :
             mmrHumanVersion == 0x0201 ? 0x0000ece6 :
             mmrHumanVersion == 0x0200 ? 0x0000ece6 :
             mmrHumanVersion == 0x0101 ? 0x0000b84c :
             mmrHumanVersion == 0x0100 ? 0x0000b83e :
             -1);
    return (a >= 0 &&
            MC68060.mmuPeekLongData (a + 14, 1) == ('N' << 24 | 'U' << 16 | 'L' << 8 | ' ') &&
            MC68060.mmuPeekLongData (a + 18, 1) == (' ' << 24 | ' ' << 16 | ' ' << 8 | ' ') ? a : -1);
  }  //mmrHumanNul()

  //dev = mmrHumanDev (name1, name2)
  //  Human68kに組み込まれている指定された名前のデバイスドライバのアドレスを返す
  //  同じ名前のデバイスドライバが複数組み込まれているときは最後に見つかったものを返す
  //  最初の100個までに見つからなかったら諦める
  //  -1  Human68kが読み込まれていないか、未知のバージョンか、指定された名前のデバイスドライバが見つからない
  //  CONデバイスの場合
  //    con = mmrHumanDev ('C' << 24 | 'O' << 16 | 'N' << 8 | ' ', ' ' << 24 | ' ' << 16 | ' ' << 8 | ' ');
  public static int mmrHumanDev (int name1, int name2) {
    int dev = -1;
    for (int a = mmrHumanNul (), i = 0; a >= 0 && i < 100; a = MC68060.mmuPeekLongData (a, 1), i++) {
      //!!! ローカルメモリまで辿れない
      if (MC68060.mmuPeekLongData (a + 14, 1) == name1 && MC68060.mmuPeekLongData (a + 18, 1) == name2) {  //見つかった
        dev = a;
        //最後に見つかったものを返すので見つかっても続ける
      }
    }
    return dev;  //最後に見つかったデバイスドライバ
  }  //mmrHumanDev(int,int)

  //mmrCheckHuman ()
  //  Human68kのバージョンを確認してパッチをあてる
  //  IOCS _BOOTINFで呼び出される
  public static void mmrCheckHuman () {
    if (mmrHumanVersion != 0) {  //確認済み
      return;
    }
    //Human68kが起動デバイスを確認するときに呼び出した_BOOTINFであることを確認する
    if (!(MC68060.mmuPeekLongData (0x00001c00, 1) != 0 &&  //_MALLOCできるメモリ空間の末尾アドレス+1が設定されていて
          MC68060.mmuPeekLongData (0x00001c1c, 1) == 0)) {  //最後のデバイスドライバがまだ設定されていない
      return;
    }
    //Human68kのバージョンを確認する
    //  タイトルメッセージの領域はスタックエリアになって$FFで充填されているのでシェル起動後は確認できない
    //  _VERNUMのコードを直接読み出す
    //    human100  0x00009ae4  0x0100
    //    human101  0x00009aee  0x0101
    //    human200  0x00009ee4  0x0200
    //    human201  0x00009ee4  0x0201
    //    human202  0x00009ed6  0x0202
    //    human203  0x00009d7e  0x0203
    //    human215  0x0000a4fa  0x020f  //0x0215ではない
    //    human301  0x0000a3c6  0x0301
    //    human302  0x0000a4ac  0x0302 human295
    //  0000A4AC  303C3638      move.w  #$3638,d0
    //  0000A4B0  4840          swap.w  d0
    //  0000A4B2  303C0302      move.w  #$0302,d0
    //  0000A4B6  4E75          rts
    mmrHumanVersion = (MC68060.mmuPeekWordZeroData (0x0000a4ac + 8, 1) == 0x0302 ? 0x0302 :
                       MC68060.mmuPeekWordZeroData (0x0000a4ac + 8, 1) == 0x025f ? 0x025f :
                       MC68060.mmuPeekWordZeroData (0x0000a3c6 + 8, 1) == 0x0301 ? 0x0301 :
                       MC68060.mmuPeekWordZeroData (0x0000a4fa + 8, 1) == 0x020f ? 0x020f :  //0x0215ではない
                       MC68060.mmuPeekWordZeroData (0x00009d7e + 8, 1) == 0x0203 ? 0x0203 :
                       MC68060.mmuPeekWordZeroData (0x00009ed6 + 8, 1) == 0x0202 ? 0x0202 :
                       MC68060.mmuPeekWordZeroData (0x00009ee4 + 8, 1) == 0x0201 ? 0x0201 :
                       MC68060.mmuPeekWordZeroData (0x00009ee4 + 8, 1) == 0x0200 ? 0x0200 :
                       MC68060.mmuPeekWordZeroData (0x00009aee + 8, 1) == 0x0101 ? 0x0101 :
                       MC68060.mmuPeekWordZeroData (0x00009ae4 + 8, 1) == 0x0100 ? 0x0100 :
                       -1);  //Human68kが読み込まれていないか、未知のバージョン
    if (mmrHumanVersion < 0) {  //Human68kが読み込まれていないか、未知のバージョン
      return;
    }
    //Human68kにパッチをあてる
    int patched = 0;
    int failed = 0;
    switch (mmrHumanVersion) {

    case 0x0215:

      //RAMまたはROMから起動してDISK2HDを初期化するときリクエストヘッダの初期化コマンドを設定していない(human215,human301,human302)
      //                                                   →                          sf.b    ($0002,a5)
      //  0000802E  61005F74      bsr.w   ~00DEFA
      //  00008032  082900070004  btst.b  #$07,($0004,a1)  →                          tst.b   ($0004,a1)
      //  00008038  6618          bne.s   $00008052        →                          bmi.s   $00008052

      //MOVE from SRをMOVE from CCRに変更した後のキャッシュフラッシュを68010~68060に対応させる
      //  000086EC  4E7A0002      movec.l cacr,d0          →  2F01          move.l  d1,-(sp)
      //                                                       7203          moveq.l #3,d1
      //  000086F0  807C0008      or.w    #$0008,d0        →  70AC4E4F      IOCS    _SYS_STAT
      //  000086F4  4E7B0002      movec.l d0,cacr          →  221F          move.l  (sp)+,d1
      //                                                       2048          movea.l a0,a0
      if (MC68060.mmuPeekLongData (0x000086ec, 1) == 0x4e7a0002) {
        MC68060.mmuPokeLongData (0x000086ec, 0x2f017203, 1);
        MC68060.mmuPokeLongData (0x000086f0, 0x70ac4e4f, 1);
        MC68060.mmuPokeLongData (0x000086f4, 0x221f2048, 1);
        patched++;
      } else {
        failed++;
      }

      //x形式のヘッダのメモリアロケーションモードが必要最小ブロックからかどうかをテストするbit番号が間違っている(human215,human301,human302)
      //  00009A5A  08010001      btst.l  #$01,d1          →  00009A5A  08010000      btst.l  #$00,d1
      if (MC68060.mmuPeekLongData (0x00009a5a, 1) == 0x08010001) {
        MC68060.mmuPokeByteData (0x00009a5a + 3, 0x00, 1);
        patched++;
      } else {
        failed++;
      }

      //ディレクトリを延長するときルートディレクトリかどうかを判断するためにセクタ番号をデータ部の先頭セクタ番号と比較するとき上位ワードを無視している(human215,human301,human302)
      //                                                   →            7000          moveq.l #$00,d0
      //  0000B900  30280014      move.w  ($0014,a0),d0
      //  0000B904  B240          cmp.w   d0,d1            →            B280          cmp.l   d0,d1
      //  0000B906  6406          bcc.s   $0000B90E
      //  0000B908  5241          addq.w  #$01,d1
      //  0000B90A  B240          cmp.w   d0,d1
      //  0000B90C  4E75          rts
      //  0000B90E

      //FILESのバッファのアドレスのbit31がセットされているとき拡張部分をコピーするループのループカウンタのレジスタが間違っている(human215)
      //  0000BC3E  7255          moveq.l #$55,d1
      //  0000BC40  12D8          move.b  (a0)+,(a1)+
      //  0000BC42  51C8FFFC      dbra.w  d0,$0000BC40     →  0000BC42  51C9FFFC      dbra.w  d1,$0000BC40
      if (MC68060.mmuPeekWordZeroData (0x0000bc42, 1) == 0x51c8) {
        MC68060.mmuPokeByteData (0x0000bc42 + 1, 0xc9, 1);
        patched++;
      } else {
        failed++;
      }

      //リモートデバイスに対するchmodコマンドのコマンド番号が間違っている(human215)
      //  0000D848  7057          moveq.l #$57,d0          →  0000D848  7046          moveq.l #$46,d0
      if (MC68060.mmuPeekWordZeroData (0x0000d848, 1) == 0x7057) {
        MC68060.mmuPokeByteData (0x0000d848 + 1, 0x46, 1);
        patched++;
      } else {
        failed++;
      }

      //サブのメモリ空間を削除するときサブの管理下で常駐したブロックをメインのメモリ空間からサブのメモリ空間に入る方向に繋いでいない(human215,human301,human302)

      //スレッドを切り替えるためのTimer-D割り込みルーチンがMC68030のコプロセッサ命令途中割り込みに対応していない(human215,human301,human302)

      //IOCTRL(19,1)でBPBテーブルをコピーする長さとPDAとイジェクトフラグを書き込む位置が間違っている(human215,human301,human302)
      //  00010AE8  700B          moveq.l #$0B,d0          →  00010AE8  700F          moveq.l #$0F,d0
      //  00010AEA  10DE          move.b  (a6)+,(a0)+
      //  00010AEC  51C8FFFC      dbra.w  d0,$00010AEA
      if (MC68060.mmuPeekWordZeroData (0x00010ae8, 1) == 0x700b) {
        MC68060.mmuPokeByteData (0x00010ae8 + 1, 0x0f, 1);
        patched++;
      } else {
        failed++;
      }

      //IOCTRL(19,0)でBPBテーブルのハンドルをBPBテーブルのアドレスとして参照しようとしている(human215)
      //  00FCA520  61000084      bsr.w   $00FCA5A6
      //  00FCA524  206D000E      movea.l $000E(a5),a0
      //  ;BPBテーブルのハンドルを求めるときにd0.w=(d0.w&3)*4を計算しているのでd0.wの上位バイトは既に0になっている
      //  00FCA528  4240          clr.w   d0               →  00FCA528  2C56          movea.l (a6),a6
      //  00FCA52A  102E000A      move.b  $000A(a6),d0
      //  00FCA52E  3080          move.w  d0,(a0)

      break;

    case 0x0301:

      //RAMまたはROMから起動してDISK2HDを初期化するときリクエストヘッダの初期化コマンドを設定していない(human215,human301,human302)
      //
      //                                                   →                          sf.b    ($0002,a5)
      //  00007EE2  61005F74      bsr.w   ~00DEFA
      //  00007EE6  082900070004  btst.b  #$07,($0004,a1)  →                          tst.b   ($0004,a1)
      //  00007EEC  6618          bne.s   $00007F06        →                          bmi.s   $00007F06

      //MOVE from SRをMOVE from CCRに変更した後のキャッシュフラッシュを68010~68060に対応させる
      //  000085B8  4E7A0002      movec.l cacr,d0          →  2F01          move.l  d1,-(sp)
      //                                                       7203          moveq.l #3,d1
      //  000085BC  807C0008      or.w    #$0008,d0        →  70AC4E4F      IOCS    _SYS_STAT
      //  000085C0  4E7B0002      movec.l d0,cacr          →  221F          move.l  (sp)+,d1
      //                                                       2048          movea.l a0,a0
      if (MC68060.mmuPeekLongData (0x000085b8, 1) == 0x4e7a0002) {
        MC68060.mmuPokeLongData (0x000085b8, 0x2f017203, 1);
        MC68060.mmuPokeLongData (0x000085bc, 0x70ac4e4f, 1);
        MC68060.mmuPokeLongData (0x000085c0, 0x221f2048, 1);
        patched++;
      } else {
        failed++;
      }

      //x形式のヘッダのメモリアロケーションモードが必要最小ブロックからかどうかをテストするbit番号が間違っている(human215,human301,human302)
      //
      //  00009926  08010001      btst.l  #$01,d1          →  00009926  08010000      btst.l  #$00,d1
      if (MC68060.mmuPeekWordZeroData (0x00009926 + 2, 1) == 0x0001) {
        MC68060.mmuPokeByteData (0x00009926 + 3, 0x00, 1);
        patched++;
      } else {
        failed++;
      }

      //ディレクトリを延長するときルートディレクトリかどうかを判断するためにセクタ番号をデータ部の先頭セクタ番号と比較するとき上位ワードを無視している(human215,human301,human302)
      //
      //サブのメモリ空間を削除するときサブの管理下で常駐したブロックをメインのメモリ空間からサブのメモリ空間に入る方向に繋いでいない(human215,human301,human302)
      //
      //スレッドを切り替えるためのTimer-D割り込みルーチンがMC68030のコプロセッサ命令途中割り込みに対応していない(human215,human301,human302)
      //
      //IOCTRL(19,1)でBPBテーブルをコピーする長さとPDAとイジェクトフラグを書き込む位置が間違っている(human215,human301,human302)
      //
      //  00010A22  700B          moveq.l #$0B,d0          →  00010A22  700F          moveq.l #$0F,d0
      //  00010A24  10DE          move.b  (a6)+,(a0)+
      //  00010A26  51C8FFFC      dbra.w  d0,$00010A24
      if (MC68060.mmuPeekWordZeroData (0x00010a22, 1) == 0x700b) {
        MC68060.mmuPokeByteData (0x00010a22 + 1, 0x0f, 1);
        patched++;
      } else {
        failed++;
      }

      break;

    case 0x0302:
    case 0x025f:

      //デバイスドライバを初期化する直前と初期化した直後
      //<a1.l:初期化されたデバイスドライバのデバイスヘッダのアドレス
      //  00007140  1B7C00160000  move.b  #$16,$0000(a5)  →  00007140  4E04          emxnop
      //                                                  →  00007142  2209          move.l  a1,d1
      //                                                  →  00007144  1ABC0016      move.b  #$16,(a5)
      //  00007146  1B7C00000002  move.b  #$00,$0002(a5)  →
      //                                                  →  00007148  51ED0002      sf.b    $0002(a5)
      //  0000714C  082900050004  btst.b  #$05,$0004(a1)
      //  00007152  6706          beq.s   $0000715A
      //  00007154  1B7C00400002  move.b  #$40,$0002(a5)
      //  0000715A  2B400012      move.l  d0,$0012(a5)
      //  0000715E  10381C75      move.b  $1C75.w,d0
      //  00007162  5200          addq.b  #$01,d0
      //  00007164  1B400016      move.b  d0,$0016(a5)
      //  00007168  2209          move.l  a1,d1           →  00007168  61006D90      bsr.w   $0000DEFA
      //  0000716A  2241          movea.l d1,a1           →
      //  0000716C  60006D8C      bra.w   $0000DEFA       →  0000716C  4E04          emxnop
      //                                                  →  0000716E  4E75          rts
      if (MC68060.mmuPeekLongData (0x00007140, 1) == 0x1b7c0016) {
        MC68060.mmuPokeLongData (0x00007140, XEiJ.EMX_OPCODE_EMXNOP << 16 | 0x2209, 1);
        MC68060.mmuPokeLongData (0x00007144, 0x1abc0016, 1);
        MC68060.mmuPokeLongData (0x00007148, 0x51ed0002, 1);
        MC68060.mmuPokeLongData (0x00007168, 0x61006d90, 1);
        MC68060.mmuPokeLongData (0x0000716c, XEiJ.EMX_OPCODE_EMXNOP << 16 | 0x4e75, 1);
        patched++;
      } else {
        failed++;
      }

      //ブロックデバイスのユニット数が0の場合に対応する
      //  00007FA4  6B00F152      bmi.w   $000070F8           7FA4  6B00F156  bmi.w   $000070FC
      //  
      //  000070E2  082900070004  btst.b  #$07,$0004(a1)      70E2  4A290004  tst.b   $0004(a1)
      //                                                      70E6  6B28      bmi.s   $00007110
      //  000070E8  6626          bne.s   $00007110           70E8  4A2D000D  tst.b   $000D(a5)
      //  000070EA  204D          movea.l a5,a0
      //  000070EC  610011E2      bsr.w   $000082D0           70EC  6722      beq.s   $00007110
      //                                                      70EE  204D      movea.l a5,a0
      //  000070F0  6B12          bmi.s   $00007104           70F0  610011DE  bsr.w   $000082D0
      //  000070F2  61001154      bsr.w   $00008248
      //                                                      70F4  6B0E      bmi.s   $00007104
      //  000070F6  6A1C          bpl.s   $00007114           70F6  61001150  bsr.w   $00008248
      //  000070F8  41F900007DBD  lea.l   $00007DBD.l,a0
      //                                                      70FA  6A18      bpl.s   $00007114
      //                                                      70FC  41FA0CBF  lea.l   $00007DBD(pc),a0
      //  000070FE  6134          bsr.s   $00007134
      //  00007100  6000F826      bra.w   $00006928           7100  6132      bsr.s   $00007134
      //                                                      7102  6008      bra.s   $0000710C
/*
      if (MC68060.mmuPeekLongData (0x00007fa4, 1) == 0x6b00f152) {
        MC68060.mmuPokeLongData (0x00007fa4, 0x6b00f156, 1);
        MC68060.mmuPokeLongData (0x000070e2, 0x4a290004, 1);
        MC68060.mmuPokeLongData (0x000070e6, 0x6b284a2d, 1);
        MC68060.mmuPokeLongData (0x000070ea, 0x000d6722, 1);
        MC68060.mmuPokeLongData (0x000070ee, 0x204d6100, 1);
        MC68060.mmuPokeLongData (0x000070f2, 0x11de6b0e, 1);
        MC68060.mmuPokeLongData (0x000070f6, 0x61001150, 1);
        MC68060.mmuPokeLongData (0x000070fa, 0x6a1841fa, 1);
        MC68060.mmuPokeLongData (0x000070fe, 0x0cbf6132, 1);
        MC68060.mmuPokeWordData (0x00007102, 0x6008, 1);
        patched++;
      } else {
        failed++;
      }
*/

      //RAMまたはROMから起動してDISK2HDを初期化するときリクエストヘッダの初期化コマンドを設定していない(human215,human301,human302)
      //
      //                                                   →                          sf.b    ($0002,a5)
      //  00007F84  61005F74      bsr.w   ~00DEFA
      //  00007F88  082900070004  btst.b  #$07,($0004,a1)  →                          tst.b   ($0004,a1)
      //  00007F8E  6618          bne.s   $00007FA8        →                          bmi.s   $00007FA8
      //

      //MOVE from SRをMOVE from CCRに変更した後のキャッシュフラッシュを68010~68060に対応させる
      //  0000865A  4E7A0002      movec.l cacr,d0          →  2F01          move.l  d1,-(sp)
      //                                                       7203          moveq.l #3,d1
      //  0000865E  807C0008      or.w    #$0008,d0        →  70AC4E4F      IOCS    _SYS_STAT
      //  00008662  4E7B0002      movec.l d0,cacr          →  221F          move.l  (sp)+,d1
      //                                                       2048          movea.l a0,a0
      if (MC68060.mmuPeekLongData (0x0000865a, 1) == 0x4e7a0002) {
        MC68060.mmuPokeLongData (0x0000865a, 0x2f017203, 1);
        MC68060.mmuPokeLongData (0x0000865e, 0x70ac4e4f, 1);
        MC68060.mmuPokeLongData (0x00008662, 0x221f2048, 1);
        patched++;
      } else {
        failed++;
      }

      //プロセスを起動する直前
      //  0000971E  2B48001C      move.l  a0,$001C(a5)    →  0000971E  48ED1F00001C  movem.l a0-a4,$001C(a5)
      //  00009722  2B490020      move.l  a1,$0020(a5)    →
      //                                                      00009724  200C          move.l  a4,d0
      //  00009726  2B4A0024      move.l  a2,$0024(a5)    →  00009726  4A41          tst.w   d1
      //                                                      00009728  6600FE30      bne.w   $0000955A
      //  0000972A  2B4B0028      move.l  a3,$0028(a5)    →
      //                                                      0000972C  4E04          emxnop
      //  0000972E  2B4C002C      move.l  a4,$002C(a5)    →  0000972E  600A          bra.s   $0000973A
      //  00009732  200C          move.l  a4,d0           →
      //  00009734  4A41          tst.w   d1              →
      //  00009736  6600FE22      bne.w   $0000955A       →
      if (MC68060.mmuPeekLongData (0x0000971e, 1) == 0x2b48001c) {
        MC68060.mmuPokeWordData (0x0000971e, 0x48ed, 1);
        MC68060.mmuPokeLongData (0x00009720, 0x1f00001c, 1);
        MC68060.mmuPokeLongData (0x00009724, 0x200c4a41, 1);
        MC68060.mmuPokeLongData (0x00009728, 0x6600fe30, 1);
        MC68060.mmuPokeLongData (0x0000972c, XEiJ.EMX_OPCODE_EMXNOP << 16 | 0x600a, 1);
        patched++;
      } else {
        failed++;
      }

      //x形式のヘッダのメモリアロケーションモードが必要最小ブロックからかどうかをテストするbit番号が間違っている(human215,human301,human302)
      //
      //  000099C4  08010001      btst.l  #$01,d1          →  000099C4  08010000      btst.l  #$00,d1
      if (XEiJ.currentMPU < Model.MPU_MC68LC060) {  //060turbo.sysのパッチと衝突する
        if (MC68060.mmuPeekWordZeroData (0x000099c4 + 2, 1) == 0x0001) {
          MC68060.mmuPokeByteData (0x000099c4 + 3, 0x00, 1);
          patched++;
        } else {
          failed++;
        }
      }

      //プロセスが常駐した直後
      //<a0.l:常駐したプロセスのメモリ管理テーブルのアドレス
      //  0000A088  4A380CBC      tst.b   $0CBC.w
      //  0000A08C  6704          beq.s   $0000A092       →  0000A08C  6702          beq.s   $0000A090
      //  0000A08E  3F3C0000      move.w  #$0000,-(sp)    →  0000A08E  4267          clr.w   -(sp)
      //                                                  →  0000A090  4E04          emxnop
      //  0000A092
      if (MC68060.mmuPeekWordZeroData (0x0000a08c, 1) == 0x6704 &&
          MC68060.mmuPeekLongData (0x0000a08e, 1) == 0x3f3c0000) {
        MC68060.mmuPokeLongData (0x0000a08c, 0x67024267, 1);
        MC68060.mmuPokeWordData (0x0000a090, XEiJ.EMX_OPCODE_EMXNOP, 1);
        patched++;
      } else {
        failed++;
      }

      //仮想ディレクトリを展開して実体のドライブに移るときドライブ管理テーブルのアドレスを変更する命令のオペレーションサイズが間違っている(human302)
      //
      //  0000B2EA  324C          movea.w a4,a1            →  0000B2EA  224C          movea.l a4,a1
      if (MC68060.mmuPeekWordZeroData (0x0000b2ea, 1) == 0x324c) {
        MC68060.mmuPokeByteData (0x0000b2ea + 0, 0x22, 1);
        patched++;
      } else {
        failed++;
      }

      //ディレクトリを延長するときルートディレクトリかどうかを判断するためにセクタ番号をデータ部の先頭セクタ番号と比較するとき上位ワードを無視している(human215,human301,human302)
      //
      //                                                   →            7000          moveq.l #$00,d0
      //  0000B8E2  30280014      move.w  ($0014,a0),d0
      //  0000B8E6  B240          cmp.w   d0,d1            →            B280          cmp.l   d0,d1
      //  0000B8E8  6406          bcc.s   $0000B8F0
      //  0000B8EA  5241          addq.w  #$01,d1          →            5281          addq.l  #$01,d1
      //  0000B8EC  B240          cmp.w   d0,d1            →            B280          cmp.l   d0,d1
      //  0000B8EE  4E75          rts

      //サブのメモリ空間を削除するときサブの管理下で常駐したブロックをメインのメモリ空間からサブのメモリ空間に入る方向に繋いでいない(human215,human301,human302)

      //スレッドを切り替えるためのTimer-D割り込みルーチンがMC68030のコプロセッサ命令途中割り込みに対応していない(human215,human301,human302)

      //IOCTRL(19,1)でBPBテーブルをコピーする長さとPDAとイジェクトフラグを書き込む位置が間違っている(human215,human301,human302)
      //  00010B38  700B          moveq.l #$0B,d0          →  00010B38  700F          moveq.l #$0F,d0
      //  00010B3A  10DE          move.b  (a6)+,(a0)+
      //  00010B3C  51C8FFFC      dbra.w  d0,$00010B3A
      if (MC68060.mmuPeekWordZeroData (0x00010b38, 1) == 0x700b) {
        MC68060.mmuPokeByteData (0x00010b38 + 1, 0x0f, 1);
        patched++;
      } else {
        failed++;
      }
      //
      break;
    }

    //  Humanのline 1111 emulator/privilege violation例外処理ルーチンの手前にFLOATn.Xのマジック'FEfn'を押し込む
    //    手前にある_EXITVC/_CTRLVC/_ERRJVCのコードを詰めて隙間を作る
    //  Human自身がFLOATn.Xのマジックを持つことでFLOATn.Xを組み込めなくする
    //    シェルが正常終了した
    //      00008518  6140          bsr.s   $0000855A       1行改行
    //      0000851A  4879000111AE  pea.l   $000111AE.l     '終了しました。',$00
    //      00008520  FF09          DOS     _PRINT
    //      00008522  588F          addq.l  #$04,sp
    //      00008524  4879000111BD  pea.l   $000111BD.l     'コマンドを、入力してください',$0D,$0A,'#',$00
    //      0000852A  6006          bra.s   $00008532
    //    コマンド入力ループ
    //      0000852C  4879000111DB  pea.l   $000111DB.l     '#',$00
    //      00008532  FF09          DOS     _PRINT
    //          :
    //    _EXITVC/_CTRLVC/_ERRJVC
    //      00008566  FF81          DOS     _GETPDB         human203まではX68030に対応していないため_GETPDBは$FF51
    //      00008568  B0BC00008382  cmp.l   #$00008382,d0   Humanのプロセス管理テーブル
    //      0000856E  6626          bne.s   $00008596       →  0000856E  6622          bne.s   $00008592
    //    シェルが停止した
    //      00008570  4FF900008372  lea.l   $00008372.l,sp  スタック復元
    //      00008576  42A7          clr.l   -(sp)
    //      00008578  FF20          DOS     _SUPER
    //      0000857A  588F          addq.l  #$04,sp
    //      0000857C  207900011090  movea.l $00011090.l,a0  ユーザスタックエリアのアドレス
    //      00008582  41E800F0      lea.l   $00F0(a0),a0
    //      00008586  4E60          move.l  a0,usp
    //      00008588  61D0          bsr.s   $0000855A       1行改行
    //      0000858A  4879000111DD  pea.l   $000111DD.l     '停止しました。',$00
    //      00008590  FF09          DOS     _PRINT          →  00008590  608E          bra.s   $00008520
    //      00008592  588F          addq.l  #$04,sp         →  00008592  FF00          DOS     _EXIT
    //      00008594  608E          bra.s   $00008524       →  00008594  4645          .dc.w   'FE'
    //    シェル以外のプロセスが停止した
    //      00008596  FF00          DOS     _EXIT           →  00008596  666E          .dc.w   'fn'
    //    line 1111 emulator/privilege violation
    //      00008598  48E78006      movem.l d0/a5-a6,-(sp)  human203まではX68030に対応していないためline 1111 emulatorのコードが異なる
    if (FEFunction.fpkRejectFloatOn) {
      int fline = (mmrHumanVersion == 0x0302 ||
                   mmrHumanVersion == 0x025f ? 0x00008598 :
                   mmrHumanVersion == 0x0301 ? 0x000084f6 :
                   mmrHumanVersion == 0x020f ? 0x00008642 :
                   mmrHumanVersion == 0x0203 ? 0x00007f58 :
                   mmrHumanVersion == 0x0202 ? 0x00007fd6 :
                   mmrHumanVersion == 0x0201 ? 0x00007fb6 :
                   mmrHumanVersion == 0x0200 ? 0x00007fb6 :  //human200はhuman201と同じ
                   //human101とhuman100はコードが異なるのでここでは非対応とする
                   -1);
      if (fline > 0) {
        if (MC68060.mmuPeekWordZeroData (0x0000856e - 0x00008598 + fline, 1) == 0x6626 &&
            MC68060.mmuPeekLongData (0x00008590 - 0x00008598 + fline, 1) == 0xff09588f &&
            MC68060.mmuPeekLongData (0x00008594 - 0x00008598 + fline, 1) == 0x608eff00) {
          MC68060.mmuPokeWordData (0x0000856e - 0x00008598 + fline, 0x6622, 1);
          MC68060.mmuPokeLongData (0x00008590 - 0x00008598 + fline, 0x608eff00, 1);
          MC68060.mmuPokeLongData (0x00008594 - 0x00008598 + fline, 0x4645666e, 1);
          patched++;
        } else {
          failed++;
        }
      }
    }

    System.out.println (new StringBuilder ().
                        append ("Human68k version ").
                        append ((char) ('0' + (mmrHumanVersion >> 8) % 10)).
                        append ('.').
                        append ((char) ('0' + (mmrHumanVersion & 255) / 10)).
                        append ((char) ('0' + (mmrHumanVersion & 255) % 10)).
                        append (Multilingual.mlnJapanese ? " にパッチをあてました (" : " was patched (").
                        append (patched).
                        append ('/').
                        append (patched + failed).
                        append (')').toString ());
    //FEファンクション命令を有効にする
    mmrFEfuncActivated = FEFunction.fpkOn;
    if (mmrFEfuncActivated) {
      System.out.println (Multilingual.mlnJapanese ?
                          "FE ファンクション命令が有効になりました" :
                          "FE function instruction has been activated");
    }
  }  //mmrCheckHuman()

  //pc = mmrGetLevelZeroPC ()
  //  DOSコールにレベル0で入ったときのpcを返す。0=DOSコールの中でないか不明
  public static int mmrGetLevelZeroPC () {
    if (0x020f <= mmrHumanVersion) {  //Human 2.15以上
      int level = MC68060.mmuPeekWordZeroData (0x1c08, XEiJ.regSRS);  //DOSコールのレベル
      if (level != 0) {
        int ssp = MC68060.mmuPeekLongData (0x1c5c, XEiJ.regSRS);  //DOSコールにレベル0で入ったときのssp
        int pc = MC68060.mmuPeekLongData (ssp + 0x3a, XEiJ.regSRS);  //DOSコールにレベル0で入ったときのpc
        return pc;
      }
    }
    return 0;
  }  //mmrGetLevelZeroPC

}  //class MainMemory