ボンジュール・マドモアゼル

本サイトの情報は自己責任にてご利用下さい。

[Lions' Commentary on Unix] PDP11 第9章 ハードウェア割り込みとトラップ

 
PDP-11 trap or interrupt

Lions' Commentary on UNIX 第9章に割り込み時のCPUの動作説明として
内部レジスタに現在のプロセッサステータスワード(PS)と現在のプログラムカウント(PC)を保存する。
その後、PC と PS はメインメモリの低位領域に存在する連続した2ワードから再ロードされる。

とあります。
この記述をそのまま素直に読んでしまうと、PS については、割込ベクタの2番目のワードがそのままコピー(ロード)されると解釈することが出来ます。
しかし、第10章の割り込み(341-342頁)にてクロック割り込みの動作例には
クロックがプロセッサに割り込んだと考えてみよう。
クロックベクタ位置 100 と 104 は両方とも同じ情報を持っている。PCは kwlp(0570)のラベルの付いた位置のアドレスが設定され、PSは次のように設定される。

現モード = カーネルモード
前モード = カーネルモードかユーザモード
優先度 = 6

PS はベクタ位置から得られる値にかかわらず本当の前モードを格納していることに注意してほしい。

とありますので、どうやら割り込みベクタの2番目の値がそのままPSにコピーされるわけではなさそうです。
それともこれはクロック割り込み時に対する特別仕様なのでしょうか。

そこで、PDP-11の動作を確認するため、simh の ソースプログラム pdp11_cpu.c を覗いて見ることにしました。ソースは当該サイトの Download the latest sources for SIMH のリンクからダウンロードできます。
/* Process a trap or interrupt


1. Exit wait state
2. Save the current SP and PSW
3. Read the new PC, new PSW from trapea, kernel data space
4. Get the mode and stack selected by the new PSW
5. Push the old PC and PSW on the new stack
6. Update SP, PSW, and PC
7. If not stack overflow, check for stack overflow
*/

wait_state = 0; /* exit wait state */
STACKFILE[cm] = SP;
PSW = get_PSW (); /* assemble PSW */
oldrs = rs;
if (CPUT (HAS_MMTR)) { /* 45,70? */
if (update_MM) MMR2 = trapea; /* save vector */
MMR0 = MMR0 & ~MMR0_IC; /* clear IC */
}
src = ReadW (trapea | calc_ds (MD_KER)); /* new PC */
src2 = ReadW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */
t = (src2 >> PSW_V_CM) & 03; /* new cm */
trapea = ~t; /* flag pushes */
WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t));
WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t));
trapea = 0; /* clear trap flag */
src2 = (src2 & ~PSW_PM) | (cm << PSW_V_PM); /* REPLACE prv mode */
put_PSW (src2, 0); /* call calc_is,ds */
if (rs != oldrs) { /* if rs chg, swap */
for (i = 0; i < 6; i++) {
REGFILE[i][oldrs] = R[i];
R[i] = REGFILE[i][rs];
}
}
SP = (STACKFILE[cm] - 4) & 0177777; /* update SP, PC */

http://simh.trailing-edge.com/

ずばり、 src2 = (src2 & ~PSW_PM) | (cm << PSW_V_PM); /* REPLACE prv mode */ の行です。
これは現在のモードが新たな PS の前モードの値として設定されることを示しています。
また、特別な条件判定もないので、クロック割り込みだけの特殊仕様ではないこともわかります。

なお、このソースコードについて一言しておきます。PS の値は、16ビットの一変数で管理されているわけではなく、cm, pm のモード変数、N, Z, V, C などの各フラグ用の変数に、PSの各ビット値が格納されます。このPSW情報へのアクセスは、16ビット列の PS の値から、それらの変数に値を格納するのに put_PSW を使用し、逆に各変数から、PS を16ビット列として組み立てるのに get_PSW を使用しています。
各定数定義については、pdp11_defs.h が参考になるでしょう。

[Lions' Commentary on Unix] mtpi & mfpi

 
PDP-11 の次のふたつの命令について

mfpi : Move From Previous Instruction space
mtpi : Move To Previous Instruction space

Lions' Commentary on UNIX の説明を以下に引用。
mfpi前のアドレス空間中の指定ワードの値をカレントスタックにプッシュする。
mtpiカレントスタックをポップし、その値を前のアドレス空間中の指定ワードに格納する。

一読したところで理解できない。
「前のアドレス」というのは一体、何を指しているのだろうか。

ここで、一旦、次の問題を考えてみる。
PDP-11では、カーネル・メモリ空間とユーザ・メモリ空間の
ふたつの異なった メモリ空間 補足 があるが
では、これらのメモリ空間の間でどのように
データをやり取りするのだろうか(それをプログラミングするのだろうか)。

まず単純にカーネルのアドレスからデータをレジスタに読み込み、
ユーザメモリ空間のアドレスで指定されるメモリへコピーするのはどうだろう。

mov kadr, r0
mov r0, uadr


一見するとよさそうだが、PSW(プロセッサステータスワード)を無視している。
参照されるメモリ空間は、PSWの14,15ビットで決まることを思い出して欲しい。
したがって、参照するメモリ空間が異なる場合は、次のように、PSW を更新しなければならない。

mov kadr, r0
mov $140000, PS / ユーザモードに移行
mov r0, uadr
mov $30000, PS / カーネルモードに移行


少々、PSWの更新命令が怪しいけどやりたいことはこんな感じだ。
この方法の難点は、1ワードもしくは2、3ワードのコピー毎に
モード切替えが必要なことである。
大きなデータをコピーする場合、このモード切替は問題になる。
この問題、少し考えれば、メモリ空間の間でモード切替をせずに
データの受け渡しをするのは、なかなか難しい問題だとわかる。

カーネルモードとユーザモードで物理メモリを共有するという手もあるかもしれない。

しかし、もっと楽な解決方法がある。そう、mtpi, mfpi である。
引用した本文を見直すと、話の流れで「前のアドレス空間」とは、
前モード(PSW 12,13ビットに設定値)で参照されていた
メモリ空間のことであることがわかる。

これをふまえれば、残りの本文の理解は容易なはず。
この命令のメリットは、モード切替を行なわずに
連続してデータをコピーできることにある。
なお、UNIX において、ユーザプログラムとOSとでデータを
やり取りするのに広く用いられているそうである。

(補足)
メモリ空間、いいかえれば、アドレス空間。
このアドレス空間は、カーネル・モードとユーザ・モードに対して
それぞれ8組のAPR(アクティブ・ページ・レジスタ)で制御される。

[Lions' Commentary on Unix] PDP11 相対モード アドレッシング

 
Lions' Commentary on UNIX
PDP-11 のアドレッシング・モードのリラティブ(相対)モード(Relative Mode)がようやく、分かった。

まず、注目に値するのは、この相対モードにて、はじめて、即値を番地として扱う点。この即値はSSR0などのように、識別子で指定される場合もある。
それまでに説明のあったアドレッシングモードでは、
識別子を扱うにしても、メモリ番地としてではなく、単なる即値として扱うだけである。

次の注意点は、以下の本文。
現在のプログラムカウンタの値に相対するアドレスをプログラム列から取り出し、オペランドの絶対アドレスを求めるためにこれを pc の値に加える。

この説明から、
    inc    SSR0

がインクリメントするのは、{現在の pc の値} + {SSR0 (177572)} の番地であり、
pc の値は変わり得るのだから、この命令は実行場所によってインクリメントする番地が変わってしまう、という誤解してしまう。

しかし、 『PDP-11 MACRO-11 Language Reference Manual』 を読んで誤解が解けた。
以下、相対モードの説明から。
    mov   100, R3

これをアセンブると、

番地 20: 0 1 6 7 0 3
番地 22: 0 0 0 0 5 4
番地 24: (次のインストラクション)


となる。実行時、pc は次の命令を指していて、248であり、この mov 命令でオペランドとして参照されるアドレスは、
248 + 548 = 1008 となる。
つまり、アセンブると pc に足される値として、相対アドレスが設定されるのである。

本文に「相対するアドレスをプログラム列から取り出し」とあるから、このアセンブル仕様についても示唆しているのだが、そこまでは読み切れない。