xeij/PowerControl.java
//========================================================================================
//  PowerControl.java
//    en:Power control
//    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/
//========================================================================================

package xeij;

import java.awt.event.*;  //ActionEvent
import java.util.*;  //TimerTask
import javax.swing.*;  //JCheckBoxMenuItem
import javax.swing.event.*;  //ChangeEvent

//class PowerControl
//  電源制御
//  VCC1は以下のいずれかがLowのときON、すべてHighのときOFF
//    電源スイッチ
//      Low=ON,High=OFF
//      VCC2でプルアップされている
//    RTCのALARM
//      ALARM出力は正論理のアラームと1Hzと16HzのNOR(ORしてからNOT)
//      電源スイッチをOFFにしたときのPOWER LED緑の点滅もこれ
//    (POWER LED緑はここまで)
//    拡張スロットA40のEXPWON
//      VCC2でプルアップされている
//      Compactと030CompactにはEXPWONがない
//    REMOTE端子
//      Low=ショート,High=オープン
//      EXPWONに合流している
//      CompactとX68030と030CompactにはREMOTE端子がない
//    システムコントローラのPOWER OFF
//      Low=システムポート$E8E00Fに$0,$F,$Fが書き込まれた,High=その他
//      初代はBUDDHA、XVIはDOSA、X68030はSAKIなど
//    (POWER LED赤はVCC1がOFF)
public class PowerControl {

  //定数
  public static final boolean PWC_ON = true;
  public static final boolean PWC_DEBUG = false;

  //設定
  public static boolean pwcPowerOn;  //電源スイッチの状態。false=OFF,true=ON
  public static boolean pwcExternalOn;  //外部電源スイッチの状態。false=OFF,true=ON
  public static boolean pwcOnAtStartup;  //true=開始時に電源を入れる
  public static boolean pwcOffAtQuit;  //true=終了時に電源を切る
  public static int pwcQuitWait;  //終了時待機時間(秒)。1~300

  //状態
  public static boolean pwcVcc1On;  //VCC1の状態。false=OFF,true=ON
  public static int pwcSystemOffStep;  //システムコントローラのPOWER OFFの状態。0~2=Low,3=High
  public static volatile int pwcQuitStep;  //終了時待機の状態。0=待機前,1=待機中,2=待機後
  public static volatile javax.swing.Timer pwcQuitTimer;  //終了時待機タイマー

  //コンポーネント
  public static JCheckBoxMenuItem pwcOnAtStartupCheckBox;
  public static JCheckBoxMenuItem pwcPowerCheckBox;
  public static JCheckBoxMenuItem pwcExternalCheckBox;
  public static JCheckBoxMenuItem pwcQuitCheckBox;
  public static SpinnerNumberModel pwcQuitModel;
  public static JSpinner pwcQuitSpinner;  //終了時待機時間スピナー

  //pwcInit ()
  //  初期化
  public static void pwcInit () {
    //設定
    pwcPowerOn = Settings.sgsGetOnOff ("powerswitch", false);
    pwcExternalOn = Settings.sgsGetOnOff ("externalswitch", false);
    pwcOnAtStartup = Settings.sgsGetOnOff ("onatstartup", true);
    pwcOffAtQuit = Settings.sgsGetOnOff ("offatquit", true);
    pwcQuitWait = Settings.sgsGetInt ("quitwait", 15, 1, 300);
    //状態
    pwcVcc1On = false;
    pwcSystemOffStep = 3;
    pwcQuitStep = 0;  //終了時待機前
    pwcQuitTimer = null;
  }  //pwcInit

  //pwcTini ()
  //  後始末
  public static void pwcTini () {
    //設定
    Settings.sgsPutOnOff ("powerswitch", pwcPowerOn);
    Settings.sgsPutOnOff ("externalswitch", pwcExternalOn);
    Settings.sgsPutOnOff ("onatstartup", pwcOnAtStartup);
    Settings.sgsPutOnOff ("offatquit", pwcOffAtQuit);
    Settings.sgsPutInt ("quitwait", pwcQuitWait);
  }  //pwcTini

  //pwcMakeMenu ()
  //  電源制御メニューを作る
  public static JMenu pwcMakeMenu () {
    pwcQuitModel = new SpinnerNumberModel (pwcQuitWait, 1, 300, 1);
    pwcQuitSpinner = ComponentFactory.createNumberSpinner (
      pwcQuitModel,
      2,
      (ce) -> {
        pwcQuitWait = pwcQuitModel.getNumber ().intValue ();
      });
    ActionListener listener = (ae) -> {
      Object source = ae.getSource ();
      String command = ae.getActionCommand ();
      switch (command) {
      case "Power switch":
        pwcSetPower (pwcPowerCheckBox.isSelected ());
        break;
      case "External power switch":
        pwcSetRemote (pwcExternalCheckBox.isSelected ());
        break;
      case "Power on at startup":
        pwcOnAtStartup = pwcOnAtStartupCheckBox.isSelected ();
        break;
      case "Power off at quit":
        pwcOffAtQuit = pwcQuitCheckBox.isSelected ();
        break;
      default:
        System.out.println ("unknown action command " + command);
      }
    };
    return Multilingual.mlnText (
      ComponentFactory.createMenu (
        "Power control",
        pwcPowerCheckBox =
        Multilingual.mlnText (
          ComponentFactory.createCheckBoxMenuItem (
            pwcPowerOn,
            "Power switch",
            listener),
          "ja", "電源スイッチ"),
        pwcExternalCheckBox =
        Multilingual.mlnText (
          ComponentFactory.createCheckBoxMenuItem (
            pwcExternalOn,
            "External power switch",
            listener),
          "ja", "外部電源スイッチ"),
        ComponentFactory.createHorizontalSeparator (),
        pwcOnAtStartupCheckBox =
        Multilingual.mlnText (
          ComponentFactory.createCheckBoxMenuItem (
            pwcOnAtStartup,
            "Power on at startup",
            listener),
          "ja", "開始時に電源を入れる"),
        pwcQuitCheckBox =
        Multilingual.mlnText (
          ComponentFactory.createCheckBoxMenuItem (
            pwcOffAtQuit,
            "Power off at quit",
            listener),
          "ja", "終了時に電源を切る"),
        ComponentFactory.createHorizontalBox (
          Box.createHorizontalStrut (20),
          Multilingual.mlnText (
            ComponentFactory.createLabel ("Wait time "),
            "ja", "待機時間 "),
          pwcQuitSpinner,
          Multilingual.mlnText (
            ComponentFactory.createLabel (" s"),
            "ja", " 秒"),
          Box.createHorizontalGlue ()
          )
        ),
      "ja", "電源制御");
  }  //pwcMakeMenu

  //pwcReset ()
  //  RESET命令が実行された
  public static void pwcReset () {
    pwcSystemOffStep = 0;
    if (pwcPowerOn) {
      if (PWC_DEBUG) {
        System.out.println ("power switch is on");
      }
      MC68901.mfpPowerFall ();
      pwcVcc1On ();
    } else {
      if (PWC_DEBUG) {
        System.out.println ("power switch is off");
      }
      MC68901.mfpPowerRise ();
      pwcVcc1Off ();
    }
    if (pwcExternalOn) {
      if (PWC_DEBUG) {
        System.out.println ("external power switch is on");
      }
      MC68901.mfpExpwonFall ();
      pwcVcc1On ();
    } else {
      if (PWC_DEBUG) {
        System.out.println ("external power switch is off");
      }
      MC68901.mfpExpwonRise ();
      pwcVcc1Off ();
    }
  }  //pwcReset

  //pwcSetPower (on)
  //  電源スイッチの状態を設定する
  public static void pwcSetPower (boolean on) {
    if (pwcPowerOn != on) {
      pwcPowerOn = on;
      if (on) {  //OFF→ON
        if (PWC_DEBUG) {
          System.out.println ("power switch turned on");
        }
        MC68901.mfpPowerFall ();
        pwcVcc1On ();
      } else {  //ON→OFF
        if (PWC_DEBUG) {
          System.out.println ("power switch turned off");
        }
        MC68901.mfpPowerRise ();
        pwcVcc1Off ();
      }
    }
  }  //pwcSetPower

  //pwcSetRemote (on)
  //  外部電源スイッチの状態を設定する
  public static void pwcSetRemote (boolean on) {
    if (pwcExternalOn != on) {
      pwcExternalOn = on;
      if (on) {  //OFF→ON
        if (PWC_DEBUG) {
          System.out.println ("external power switch turned on");
        }
        MC68901.mfpExpwonFall ();
        pwcVcc1On ();
      } else {  //ON→OFF
        if (PWC_DEBUG) {
          System.out.println ("external power switch turned off");
        }
        MC68901.mfpExpwonRise ();
        pwcVcc1Off ();
      }
    }
  }  //pwcSetRemote

  //pwcWritePort (d)
  //  システムポート$00E8E00Fに書き込まれた
  public static void pwcWritePort (int d) {
    d &= 0x0f;
    if (pwcSystemOffStep == 0 && d == 0x00) {
      pwcSystemOffStep = 1;
    } else if (pwcSystemOffStep == 1 && d == 0x0f) {
      pwcSystemOffStep = 2;
    } else if (pwcSystemOffStep == 2 && d == 0x0f) {
      pwcSystemOffStep = 3;
      if (PWC_DEBUG) {
        System.out.println ("system port received 0-F-F");
      }
      pwcVcc1Off ();
    } else {
      pwcSystemOffStep = 0;
    }
  }  //pwcWritePort

  //pwcVcc1On ()
  //  条件を満たしていればVCC1をONにする
  public static void pwcVcc1On () {
    //条件を確認する
    if (pwcQuitStep != 0 ||  //終了時待機前でない
        pwcVcc1On ||  //既にON
        (!pwcPowerOn &&  //電源スイッチがOFF(High)
         RP5C15.rtcAlarmHigh &&  //RTCのALARMがHigh
         !pwcExternalOn &&  //外部電源スイッチがOFF(High)
         pwcSystemOffStep == 3)) {  //システムコントローラのPOWER OFFがHigh。すべてHighでOFFの条件が満たされている
      return;
    }
    //VCC1をONにする
    if (PWC_DEBUG) {
      System.out.println ("start turning on");
    }
    pwcVcc1On = true;  //VCC1をON
    Indicator.indUpdateLED ();
    //RP5C15に通知する
    RP5C15.rtcTurnOn ();
    //ハードウェアリセットをかけてMPUを始動する
    XEiJ.mpuReset (-1, -1);
  }  //pwcVcc1On

  //pwcVcc1Off ()
  //  条件を満たしていればVCC1をOFFにする
  public static void pwcVcc1Off () {
    //条件を確認する
    if (!pwcVcc1On ||  //既にOFF
        (pwcPowerOn ||  //電源スイッチがON(Low)
         !RP5C15.rtcAlarmHigh ||  //RTCのALARMがLow
         pwcExternalOn ||  //外部電源スイッチがON(Low)
         pwcSystemOffStep < 3)) {  //システムコントローラのPOWER OFFがLow。いずれかがLowでONの条件を満たしている
      return;
    }
    //VCC1をOFFにする
    if (PWC_DEBUG) {
      System.out.println ("start turning off");
    }
    pwcVcc1On = false;  //VCC1をOFF
    Indicator.indUpdateLED ();
    //RP5C15に通知する
    RP5C15.rtcTurnOff ();
    //MPUを停止させる
    //  VCC1がOFFなのでMPUが停止しているとき有効なボタンも無効になる
    XEiJ.mpuStop (null);
    //画面を暗くする
    CoreTask.add (() -> {
      VideoController.vcnTargetScaledContrast = 0;
      VideoController.vcnCurrentScaledContrast = 0;
      VideoController.vcnSetContrast (0);
      CRTC.crtRepaint ();
      //終了時待機中ならば直ちに終了する
      if (pwcQuitStep == 1) {  //終了時待機中
        pwcQuitCall (0);
      }
    });
  }  //pwcVcc1Off

  //pwcStartup ()
  //  開始処理
  public static void pwcStartup () {
    //開始時に電源を入れる
    if (pwcOnAtStartup) {
      pwcSystemOffStep = 0;
      if (!pwcPowerOn) {
        pwcPowerOn = true;
        pwcPowerCheckBox.setSelected (true);
      }
    }
    //電源が入る条件が揃っていたら電源を入れる
    pwcVcc1On ();
  }  //pwcStartup

  //pwcQuit ()
  //  終了処理
  //  ウインドウのクローズボタンが押された
  //  ファイルメニューの終了が選択された
  public static void pwcQuit () {
    //終了時待機なしのとき直ちに終了する
    if (!pwcOffAtQuit) {  //終了時待機なし
      pwcQuitEnd ();  //終了する
      return;
    }
    //終了時待機前でないとき何もしない
    if (pwcQuitStep != 0) {  //終了時待機前でない
      return;
    }
    //VCC1がOFFのとき直ちに終了する
    if (!pwcVcc1On) {  //VCC1がOFF
      pwcQuitEnd ();  //終了する
      return;
    }
    //終了時待機中に移行する
    if (PWC_DEBUG) {
      System.out.println ("start quit");
    }
    pwcQuitStep = 1;  //終了時待機中
    //電源スイッチをOFFにする
    //  MFPのI2ピンがHighになり電源OFF割り込みが発生する
    if (pwcPowerOn &&
        pwcPowerCheckBox != null &&
        pwcPowerCheckBox.isSelected ()) {  //電源スイッチがON
      pwcPowerCheckBox.doClick ();  //電源スイッチをOFFにする
    }
    //外部電源スイッチをOFFにする
    //  MFPのI1ピンがHighになり外部電源OFF割り込みが発生する
    if (pwcExternalOn &&
        pwcExternalCheckBox != null &&
        pwcExternalCheckBox.isSelected ()) {  //外部電源スイッチがON
      pwcExternalCheckBox.doClick ();  //外部電源スイッチをOFFにする
    }
    //終了時待機時間後に終了する
    pwcQuitCall (1000 * pwcQuitWait);
  }  //pwcQuit

  //pwcQuitCall (delay)
  //  時間が経ったら終了する
  public static void pwcQuitCall (int delay) {
    if (pwcQuitTimer != null) {
      pwcQuitTimer.stop ();
      //pwcQuitTimer = null;
    }
    pwcQuitTimer = new javax.swing.Timer (
      delay,
      (ae) -> {
        pwcQuitEnd ();  //終了する
      });
    pwcQuitTimer.start ();
  }  //pwcQuitCall

  //pwcQuitEnd ()
  //  終了する
  public static void pwcQuitEnd () {
    //終了時待機タイマーを停止する
    if (pwcQuitTimer != null) {
      pwcQuitTimer.stop ();
      pwcQuitTimer = null;
    }
    //終了時待機後でなければ終了する
    if (pwcQuitStep != 2) {
      pwcQuitStep = 2;  //終了時待機後
      XEiJ.prgTini ();  //終了する
    }
  }  //pwcQuitEnd

}  //class PowerControl