'_dos_getvect'および'_dos_setvect'の代わりに、それぞれDPMI関数0204hおよび0205hを使用してみてください。
プログラムのランタイム環境は、DOS32AまたはDPMIサーバー/ホストです。したがって、DOS int21h機能を使用する代わりに、提供されたAPIを使用してください。ただし、DOS32Aはint21h割り込みをインターセプトするため、リアルモードに関する限り、コードは正常に機能するはずです。
実際に行ったことは、「_dos_getvect」および「_dos_setvect」関数を使用して、IRQ14のリアルモード割り込みハンドラーのみをインストールすることです。
代わりにDPMI関数を使用することにより、 IRQ14のプロテクトモード割り込みハンドラーをインストールすると、DOS32aはIRQ14割り込みをこのプロテクトモードハンドラーに自動パスアップします。
思い出してください:IRQがアサートされている間、DOSエクステンダ/DPMIサーバーはプロテクトモードまたはリアルモードにすることができます。
これは、アプリケーションがDOSまたはBIOS APIを使用しているため、エクステンダーはそれらを実行するためにリアルモードに切り替え、プロテクトモードアプリケーションに制御を移すためにプロテクトモードに戻る必要があります。
DOS32aは、エクステンダーがリアルモードのときにIRQ14がアサートされた場合に、プロテクトモードハンドラーを呼び出すリアルモードコールバック(少なくともハードウェア割り込みの場合)を割り当てることによってこれを行います。
エクステンダーがプロテクトモードの場合、IRQ14がアサートされている間、エクステンダーは自動的に制御をIRQ14ハンドラーに転送します。
ただし、IRQにプロテクトモードハンドラーをインストールしなかった場合、DOS32aはリアルモードコールバックを割り当てず、リアルモードirqハンドラーが制御を取得しない可能性があります。しかし、それはコントロールAFAIKを受け取るはずです。
とにかく、上記の2つの関数を試してみてください。そして、ショーンが言ったように、前のint76h割り込みハンドラーにチェーンします。
要するに:
DOS32aの場合、「_dos_getvect」および「_dos_setvect」関数を使用する必要はありません。代わりに、プロテクトモードIRQハンドラーをインストールするためにDPMI関数0204hおよび0205hを使用してください。
アドバイス:割り込みハンドラーの最初のステップは、デバイスが実際に割り込みを生成したかどうか、またはこのirq(この場合はIRQ14)を共有している他のデバイスであるかどうかを確認することです。これを行うには、デバイスの「割り込み保留ビット」をチェックします。設定されている場合は、デバイスにサービスを提供し、次のハンドラーにチェーンします。1に設定されていない場合は、次のハンドラーにチェーンするだけです。
編集済み: OWに付属しているものではなく、最新バージョンのDOS32aを
使用してください。
2012年8月14日の更新:
はい、FP_SEGマクロとFP_OFFマクロを使用して、セレクターとオフセットをそれぞれ取得できます。これは、実際のモードでこれらのマクロを使用してセグメントとオフセットを取得する場合と同じです。
MK_FPマクロを使用して、セレクターとオフセットから遠いポインターを作成することもできます。例えば。MK_FP(セレクター、オフセット)。
Cでハンドラーを作成するときは、「__interrupt」キーワードを使用して割り込みハンドラーを宣言する必要があります。
スニペットは次のとおりです。
#include <i86.h> /* for FP_OFF, FP_SEG, and MK_FP in OW */
/* C Prototype for your IRQ handler */
void __interrupt __far irqHandler(void);
.
.
.
irq_selector = (unsigned short)FP_SEG( &irqHandler );
irq_offset = (unsigned long)FP_OFF( &irqHandler );
__dpmi_SetVect( intNum, irq_selector, irq_offset );
.
.
.
または、これを試してください:
extern void sendEOItoMaster(void);
# pragma aux sendEOItoMaster = \
"mov al, 0x20" \
"out 0x20, al" \
modify [eax] ;
extern void sendEOItoSlave(void);
# pragma aux sendEOItoSlave = \
"mov al, 0x20" \
"out 0xA0, al" \
modify [eax] ;
unsigned int old76_selector, new76_selector;
unsigned long old76_offset, new76_offset;
volatile int chain = 1; /* Chain to the old handler */
volatile int tick=0; // global and volatile...
void (__interrupt __far *old76Handler)(void) = NULL; // function pointer declaration
void __interrupt __far new76Handler(void) {
tick = 5; // simple code to change the value of tick...
.
.
.
if( chain ){
// disable irqs if enabled above.
_chain_intr( old76Handler ); // 'jumping' to the old handler
// ( *old76Handler )(); // 'calling' the old handler
}else{
sendEOItoMaster();
sendEOItoSlave();
}
}
__dpmi_GetVect( 0x76, &old76_selector, &old76_offset );
old76Handler = ( void (__interrupt __far *)(void) ) MK_FP (old76_selector, old76_offset)
new76_selector = (unsigned int)FP_SEG( &new76Handler );
new76_offset = (unsigned long)FP_OFF( &new76Handler );
__dpmi_SetVect( 0x76, new76_selector, new76_offset );
.
.
ノート:
最初に、フックしているIRQ#が実際に関連するPCIデバイスの割り込みピンに割り当てられている/マップされていることを再確認する必要があります。IOWは、最初にPCI構成スペースから「割り込みラインレジスタ」(割り込みピンレジスタではない)を読み取り、そのirq#のみをフックします。このレジスタの有効な値は、次のとおりです。0x00から0x0Fまで、0x00はIRQ0を意味し、0x01はIRQ1を意味します。
POST / BIOSコードは、起動中に「割り込みラインレジスタ」に値を書き込みます。このレジスタを変更してはなりません(もちろん、OSライターが処理する割り込みルーティングの問題を処理している場合を除きます)。
また、古いハンドラーにチェーンしている場合は、DPMI呼び出し0204hを使用して、古いハンドラーのセレクターとオフセットを取得して保存する必要があります。そうでない場合は、スレーブPICに属するIRQ(つまり、INT0Ahを含むINT70hから77h)をフックした場合に備えて、マスターPICとスレーブPICの両方にEOI(割り込み終了)を送信することを忘れないでください。マスターPICに属するIRQをフックした場合。
フラットモデルでは、BASEアドレスは0、制限は0xFFFFFで、Gビット(つまり粒度ビット)=1です。
ベースとリミット(セグメントの属性ビット(Gビットなど)とともに)は、特定のセグメントに対応する記述子に存在します。記述子自体は、記述子テーブルにあります。
記述子テーブルは、各エントリが8バイトの配列です。
セレクターは、記述子テーブル(GDTまたはLDT)の8バイト記述子エントリーへのポインター(またはインデックス)にすぎません。したがって、セレクターを0にすることはできません。
16ビットセレクタの下位3ビットには特別な意味があり、記述子テーブルの記述子エントリにインデックスを付けるために使用されるのは上位13ビットのみであることに注意してください。
GDT=グローバル記述子テーブル
LDT=ローカル記述子テーブル
システムは1つのGDTのみを持つことができますが、多くのLDTを持つことができます。
GDTのエントリ番号0として、予約されており、使用できません。AFAIK、DOS32Aは、アプリケーション用のLDTを作成しません。代わりに、GDT自体で、アプリケーションに対応する記述子エントリを割り当てて初期化します。
x86アーキテクチャでは、セレクタを使用してメモリにアクセスしようとすると、0セレクタは無効と見なされるため、セレクタは0であってはなりません。任意のセグメントレジスタに0を正常に配置できますが、そのセグメントにアクセス(読み取り/書き込み/実行)しようとした場合にのみ、CPUが例外を生成します。
割り込みハンドラの場合、フラットモードの場合でもベースアドレスは0である必要はありません。DPMI環境には、これを行う正当な理由が必要です。結局のところ、x86アーキテクチャのあるレベルでセグメンテーションに取り組む必要があります。
PCI device config register 0x5 bit2(Interrupt Disabled) = 0
PCI device config register 0x6 bit3(Interrupt status) = 1
バスマスタコマンドとステータスレジスタをそれぞれ意味していると思います。これらは実際にはI/Oスペースまたはメモリスペースのいずれかに存在しますが、PCI構成スペースには存在しません。したがって、IN / OUTまたはMOV命令を介して、それらを直接読み取り/書き込みできます。
読み取り/書き込みのPCI構成レジスタには、構成の赤/書き込みメソッドまたはPCIBIOSルーチンを使用する必要があります。
ノート:
多くのPCIディスクコントローラには、「割り込みイネーブル/ディセーブル」ビットと呼ばれるビットがあります。このビットを含むレジスタは通常PCI構成スペースにあり、データシートから見つけることができます。
実際、この設定は、PCIコントローラーに接続されたデバイスによって生成された割り込みをPCIバスに「転送」するためのものです。
このビットを介して割り込みが無効になっている場合、デバイス(PCIコントローラーに接続されている)が割り込みを生成している場合でも、割り込みはPCIバスに転送されません(したがって、cpuは割り込みが発生したかどうかを知ることはありません)。 PCIコントローラのビット(このビットは「割り込みの有効化/無効化」ビットとは異なります)は、デバイス(PCIコントローラに接続されている、たとえばハードディスク)が割り込みを生成したことを通知するように設定されているため、プログラムはこのビットと適切なアクションを実行します。プログラミングの観点からは、ポーリングに似ています。
これは通常、バス以外のマスター転送にのみ適用されます。
ただし、バスマスター転送(つまりDMA)を使用しているように見えるため、この場合は適用しないでください。
しかしとにかく、PCIコントローラーのデータシートを注意深く読み、特に割り込み処理に関連するビット/レジスターを探すことをお勧めします。
編集:
アプリケーションレベルのプログラミングに関する限り、プログラムはコードの外部にアクセスしないため、_farポインタに遭遇/使用する必要はありません。
ただし、これは完全には当てはまりません。システムレベルのプログラミングに進む場合は、メモリマップドデバイスレジスタ、外部ROM、または割り込みハンドラの実装などにアクセスする必要があります。
ここで話が変わります。セグメントの作成、つまり記述子の割り当てとそれに関連するセレクターの取得により、コードにバグがあったとしても、現在のコードが実行されている特定のセグメントの外部にあるものを煩わしく変更することはありません。そうしようとすると、CPUは障害を生成します。したがって、外部デバイス(特にメモリマップドデバイスのレジスタ)にアクセスする場合、またはBIOSなどのROMデータにアクセスする場合は、記述子を割り当て、実行する必要のある領域に応じてベースとセグメントの制限を設定することをお勧めします。読み取り/書き込みして続行します。しかし、あなたはそうする義務はありません。
たとえばROMにあるいくつかの外部コードは、far呼び出しで呼び出されると想定しています。
前に述べたように、x86アーキテクチャでは、完全に無効にする方法がないため、あるレベル(さらに下に行く)でセグメンテーションを処理する必要があります。しかし、フラットモデルでは、前述のように、外部(プログラムへの書き込み)にアクセスするときに、プログラマーの補助としてセグメンテーションが存在します。ただし、使用したくない場合は使用する必要はありません。
割り込みハンドラが呼び出されると、割り込みが発生したプログラムのベースと制限がわかりません。割り込みプログラムのセグメント属性、制限などはわかりません。CSとEIPを除いて、すべてのレジスタは割り込みハンドラで未定義の状態にあります。したがって、現在実行中のプログラムの外部のどこかにあることを示すために、far関数として宣言する必要があります。