APM (Advanced Power Management)のページ

  • AT互換機用の電源管理BIOSみたいなものです、たぶん。
  • とりあえず、今の段階では、いかにしてAT互換機の電源をソフトウェアからOFFにするか、ということを書きたいと思います。

マシンの電源を切ろう!

  • ややこしいことに手を染めたくはないので、リアルモードに限定してマシンの電源の切り方を説明したいと思います。でも実はまだ実験していないので、この方法はうまくいかないかもしれません。その時はとりあえず勘弁してください(テストしたらこの部分の記述は書き直します)。
  • 電源を切る前にリアルモードに戻るなんて朝飯前だと思うので、リアルモード限定でも多分そう問題はないでしょう。電源を切るときは、メモリの内容も破壊していいと思うので(保存しなきゃいけないデータが残っているのに電源を切るなんて訳が分からん!)、リアルモードに戻るのも面倒はありません。
  • いっぱいヒントをくれた、Aliceさんとぐりぽん?さんに感謝!

使うファンクション (すべてINT(0x15);)

  • AX=0x5300:INSTALLATION CHECK (ver.1.0以降)
    • APM-BIOSが存在するかどうかと、バージョンが取得できます。
    • BX=0x0000で呼び出します。
    • エラーがあるとCF=1で帰ってきます。CF=0ならファンクション成功。
    • AXにバージョン番号が入っています。1.1なら0x0101が入っています。AHが整数部分で、ALが小数部分です。
  • AX=0x5301:CONNECT REAL-MODE INTERFACE (ver.1.0以降)
    • APM-BIOSはプロテクトモードでの制御もサポートしていて、このファンクションは、リアルモードからの制御を宣言するためのもののようです。
    • BX=0x0000で呼び出します。
    • エラーがあるとCF=1で帰ってきます。CF=0ならファンクション成功。
    • CF=1でも、AH=2の場合は、「既にリアルモード接続されていますよ」ということらしいです。
  • AX=0x5307:SET POWER STATE (ver.1.0以降)
    • BX=0x0001,CX=0x0003で、全デバイスに対して電源OFFにできるようです。
  • AX=0x5308:EN/DISABLE POWER MANAGEMENT (ver.1.0以降)
    • BX=0x0001,CX=0x0001で呼び出すと全デバイスのAPM機能有効化ができます。
  • AX=0x530d:EN/DISABLE DEVICE POWER MANAGEMENT (ver.1.1以降)
    • BX=0x0001,CX=0x0001で呼び出すと全デバイスのAPM機能有効化ができます。AX=0x5308との違いは、デバイスごとの設定が可能なことです。
    • エラーがあるとCF=1で帰ってきます。CF=0ならファンクション成功。
  • AX=0x530e:DRIVER VERSION (ver.1.1以降)
    • BX=0x0000,CXにバージョン番号で呼び出すと、指定されたバージョンAPMの機能が利用できるようになるらしいです。
    • エラーがあるとCF=1で帰ってきます。CF=0ならファンクション成功。
  • AX=0x530f:ENGAGE/DISENGAGE POWER MANAGEMENT (ver.1.1以降)
    • BX=0x0001,CX=0x0001で呼び出すと全デバイスのAPM設定が連動するようになるようです。
    • エラーがあるとCF=1で帰ってきます。CF=0ならファンクション成功。

正しい(?)切り方

  • バージョン1.1以降でないと切れないそうなのでそれをチェックしたり、APMをイネーブルにしたりをしてから電源を切ります。
  • この方法で切れることをぐりぽん?さんが確認してくださいました。感謝!
    AX = 0x5300; BX = 0; INT(0x15); if (CF != 0) goto err_skip;
    if ((unsigned) AX < 0x0101) goto err_skip;
    PUSH(AX); /* バージョン番号を保存 */
    AX = 0x5301; BX = 0; INT(0x15);
    POP(CX); /* バージョン番号をCXに復帰 */
    if (CF != 0) {
        if (AH != 2) goto err_skip;
    }
    AX = 0x530e; BX = 0; INT(0x15);
    if (CF != 0) goto err_skip;
    AX = 0x530d; BX = 0x0001; CX = 0x0001; INT(0x15);
    if (CF != 0) goto err_skip;
    AX = 0x530f; BX = 0x0001; CX = 0x0001; INT(0x15);
    if (CF != 0) goto err_skip;
    AX = 0x5307; BX = 0x0001; CX = 0x0003; INT(0x15);
    err_skip: for (;;) HLT();
  • エラーが出たら、どうしようもないので、とりあえず人間が電源を切れるように、ハングアップさせています。

雑な切り方 (これは失敗)

  • これ以上簡単な切り方はないと思われる方法。でもこの方法でやっている例をまだ見つけていないので、もしかしたらやばいのかもしれません。
  • だめでした。テストしてくれたぐりぽん?さん、ありがとうございます。
    AX = 0x5300; BX = 0; INT(0x15); if (CF != 0) goto err_skip;
    AX = 0x5301; BX = 0; INT(0x15);
    if (CF != 0) {
        if (AH != 2) goto err_skip;
    }
    AX = 0x5307; BX = 0x0001; CX = 0x0003; INT(0x15);
    err_skip: for (;;) HLT();

ていねいな切り方 (これは不十分)

  • バージョンをチェックしたり、APMをイネーブルにしたりをしてから電源を切ります。
  • 最初はこの方法でいいと思ったのですが、バージョン1.1でないと切れないらしいので、1.1未満の場合は、以下のような余計なことをしないでさっさとあきらめたほうが良さそうです(それが「正しい(?)切り方」のコードになっています)。
    AX = 0x5300; BX = 0; INT(0x15); if (CF != 0) goto err_skip;
    PUSH(AX); /* バージョン番号を保存 */
    AX = 0x5301; BX = 0; INT(0x15);
    if (CF != 0) {
        if (AH != 2) goto err_skip;
    }
    POP(CX); /* バージョン番号 */
    if ((unsigned) CX >= 0x0101) {
        AX = 0x530e; BX = 0; INT(0x15);
        if (CF != 0) goto err_skip;
        AX = 0x530d; BX = 0x0001; CX = 0x0001; INT(0x15);
        if (CF != 0) goto err_skip;
        AX = 0x530f; BX = 0x0001; CX = 0x0001; INT(0x15);
        if (CF != 0) goto err_skip;
    }
    AX = 0x5307; BX = 0x0001; CX = 0x0003; INT(0x15);
    err_skip: for (;;) HLT();

正しい切り方(ぐりぽん?修正版)

  • どこからか拾ったものにエラーチェックを追加しました。うちのノートPCでも電源が切れたので、たぶんこれが今まででいちばん正しいやり方でしょう。-- ぐりぽん?
    AX = 0x5300; BX = 0; INT(0x15); if (CF != 0) goto err_skip;
    if ((unsigned) AX < 0x0101) goto err_skip;
    AX = 0x5301; BX = 0; INT(0x15);
    AX = 0x530e; BX = 0; CX = 0x0101; INT(0x15);
    if (CF != 0) goto err_skip;
    AX = 0x530f; BX = 0x0001; CX = 0x0001; INT(0x15);
    if (CF != 0) goto err_skip;
    AX = 0x5308; BX = 0x0001; CX = 0x0001; INT(0x15);
    if (CF != 0) goto err_skip;
    AX = 0x5307; BX = 0x0001; CX = 0x0003; INT(0x15);
    err_skip: for (;;) HLT();

おまけコーナー (TOWNSの電源制御)

  • まず割込み禁止にする。CLI命令よりもPICレベルで止める方がよい。DMAの転送中もよろしくないので、終わるのを待つか終了させる。
  • I/Oポートの0x0022にバイトアクセスで0x40を書き込んで、HLTする。
  • 簡単でいいなあ。

こめんと欄

  • これで終わればいいものを、僕はあほなので、このAPM-BIOSはいったいどこのI/Oを叩いているんじゃあ!チップセットごとに研究したーい!とか思っちゃいます。ひまになったらやります。10年後か? -- K 2003-08-25 (月) 00:40:57
  • APM-BIOS v1.0は電源断ができないようで。下の丁寧なやり方で、かつv1.1以降でATX電源に限り電源断されるようです。 -- ぐりぽん? 2003-08-25 (月) 11:04:52
  • うちの実験用ノートPCはv1.1ですが、一旦画面が消えたあと復活して止まりました。電源がスライド式スイッチだからなあ。 -- ぐりぽん? 2003-08-25 (月) 11:08:05
  • ご連絡ありがとうございます。1.1でないと切れないんですね。そういう情報はとても助かります。 -- K 2003-08-25 (月) 12:54:39
  • ありがとうございます。>正しい切り方(ぐりぽん修正版) -- K 2003-10-25 (土) 22:44:05
  • SORAの電源断も「正しい切り方(ぐりぽん修正版)」のものに変更する事にしました。(0.0.217以降) -- .oOo.? 2003-10-26 (日) 14:48:40
  • んー、SORAの場合は電源断できてましたよ。でもスマートではない感じがしましたので、差し替えたほうが良いのかもしれません。 -- ぐりぽん? 2003-10-26 (日) 17:06:09
  • 安全の為、全プロセスに終了を呼びかけてから、電源断するので。差し替えても動作に違いはありませんでした。(←多分) -- .oOo.? 2003-10-26 (日) 22:54:29
  • 正しい切り方(ぐりぽん修正版)を用いて、ブートしてすぐに電源を落とすプログラムを作成しましたが、qemu-0.6.0でエラーが出て終了できませんでした。実機だと問題無く終了しました。qemuのバージョンの問題でしょうが。一応報告までに -- 名無しさん 2004-09-19 (日) 19:55:26
  • qemuの場合はbochsと同じ方法で電源が落ちたはず -- sakky 2004-09-21 (火) 05:29:24
  • 電源を落とすプログラムを作りましたが、そのまま落ちませんでした。(Windowsではできたけど、DOSでできなかった) -- mac 2005-12-24 (土) 14:23:52
  • qemuでAPMによる電源終了のプログラムを実行したところqemuが異常終了してしまったのですが・・これはqemuのせいなのでしょうか? -- is 2009-03-05 (木) 21:18:19
  • qemuで「正しい切り方(ぐりぽん修正版)」にあるやつを使って、電源を切ることをしようとおもったのですが、異常終了で終わりました。これはqemuの仕様でしょうか? -- Triangle Ld. 2009-04-15 (水) 21:03:34
  • すいません。実機でやったらハングアップしました。 -- 名無しさん 2009-04-15 (水) 21:06:26
  • 成功しました。例外をコントロールしているタスクは全てストップさせるんですね・・APMはqemuじゃなくてbochsでエミュレーションした方がうまくいきますよ。 -- is 2009-04-15 (水) 23:31:20
  • PC-9821の上位シリーズにも付いているよ。 -- 2010-04-07 (水) 15:21:44
  • VirtualBox4.2.18で「正しい(?)切り方」をテスト。AX=0x530D,0x530Fの2件の処理でエラーチェックに引っかかりました。エラーチェックの分岐をコメントアウトすると電源断に成功し、またこの2処理を行わなくても電源断に成功しました。検証用コード・イメージは http://gigafileupload.com/file/08366770851255045.zip -- みけCAT 2013-09-15 (日) 13:24:56
  • 私の上記のコードをSDカードに書き込んで実機(FMV-BIBLO NF/B50)でテストしたところ、Shutdown failed!と表示されて電源が切れませんでした。 -- 名無しさん 2013-09-17 (火) 20:33:40
  • ↑すいません。名前の入力を忘れたようですが、私です。 -- みけCAT 2013-09-17 (火) 20:34:20
  • VirtualBox 4.2.18だと、AX = 0x5307; BX = 0x0001; CX = 0x0003; INT(0x15);だけで電源が切れるようです。 -- みけCAT 2013-09-17 (火) 22:25:34
  • 実機テストですが、調べたところAH=0x86(APMが存在しない)が帰っていました。このPCでは無理そうですね。 -- みけCAT 2013-09-18 (水) 09:04:50

コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-09-05 (金) 11:51:51 (1596d)