(H8移植編その2第15回)シミュレータの送信割り込み対応

2011/05/02

あなたは 人目のお客様です.

第13回で送信割り込み対応を行ったのだけど, その最後にも書いてあるようにシミュレータ側では送信割り込みの対応がされて いないので,シミュレータではうまく動作してくれない.ということで, シミュレータの送信割り込み対応を行おう.

今回もシミュレータ側の device.c の修正がメインになる. まず,NE2000の割り込み状態レジスタであるISRのクリア方法だが, 従来は0xffを書き込んで全クリアしていたのだけど,送信割り込みと受信割り込みの フラグを個別にクリアすることが必要になる.

しかしISRは書き込みがされた際にビットが立っているフラグがクリアされるという 仕様なので,たとえば 0x01 という値をもっているときに 0x01 が書き込まれると クリアされるという動作をする.ということは値が変化しないので,シミュレータ 側では書き込みが行われたことを検知できない.

対策として,最上位ビットを利用していないようなのでそこをシミュレータ側で 立てておいて,そのビットが落とされたら書き込みが行われたものと判断して クリア処理を行うようにする.具体的な処理は ether_run() の以下の部分.

 static int ether_run(host_callback *sim_callback, unsigned char *memory)
 {
   int i, c, ret;
   int size, addr, count = 0;
   int new_curr;
   int vector = VECTOR_NOINTR;
   char buf[4096];
   static int page = 0;
+  static unsigned char isr_save = 0;
 
   memcpy(®isters[page][1], &memory[RTL8019_ADDR + 1], 0x0f);
 
   switch (NE2000_CR & 0xc0) {
   case NE2000_CR_P0: page = 0; break;
   case NE2000_CR_P1: page = 1; break;
   case NE2000_CR_P2: page = 2; break;
   case NE2000_CR_P3: page = 3; break;
   default: page = 0; break;
   }
 
-  /* 割り込みのクリア */
-  if (NE2000_ISR == 0xff)
-    NE2000_ISR = 0;
+  /*
+   * 割り込みのクリア(暫定)
+   * ビットが立った部分のみクリアすべきか?要確認
+   * 書き込みがあった場合にクリアするので,書き込みを検知する必要がある.
+   * 方法として,利用されていない最上位ビットを立てておき,最上位ビットが
+   * 落ちているかどうかで書き込みを検知する.
+   */
+  if ((NE2000_ISR == 0xff) || (NE2000_ISR != isr_save))
+    NE2000_ISR = (isr_save & 0x7f) & ~NE2000_ISR;
 
@@ -706,6 +726,20 @@
     RTL8019_SIM_SR &= ~RTL8019_SIM_SR_NRDY;
   }
 
+  /*
+   * 書き込み検知するために,最上位ビットを立てておく.
+   */
+  NE2000_ISR |= 0x80;
+  isr_save = NE2000_ISR;
さらに,送信完了の際にISRの送信割り込みビットを立ててIRQ5の割り込みを 発生させる処理を追加.これも ether_run() の以下の部分.
       NE2000_ISR |= 0x01;
     }
   }
 
-  if ((NE2000_ISR & 0x01) && (NE2000_IMR & 0x01)) {
-    vector = 17; /* IRQ5 */
-  }
-
   /* 送信処理 */
   if (NE2000_CR & NE2000_CR_TXP) {
     size = NE2000_TBCR1 << 8 | NE2000_TBCR0;
     c = NE2000_TPSR * 256;
 
     for (i = 0; i < size; i++) {
       buf[i] = ether_buf[c];
       if (++c >= NE2000_PSTART * 256)
        c = NE2000_TPSR * 256;
     }
 
     if (ether_fd >= 0)
       if (write(ether_fd, buf, size) != size)
        ;
 
     NE2000_CR &= ~NE2000_CR_TXP;
+
+    NE2000_ISR |= 0x02;
+  }
+
+  /* 本来は0x03は不要だが,とりあえず従来の動作に合わせておく */
+  if ((NE2000_ISR & NE2000_IMR) & 0x03) {
+    vector = 17; /* IRQ5 */
   }
さらに,IMRというレジスタはライトがページ0で,リードがページ2で行われるの だがその考慮が無く,ドライバ側でページ2でリードしてしまうためにシミュレータ ではIMRのリード値が常にゼロになってしまい,IMRのフラグ操作がおかしくなって いた問題を修正する.これのせいで第13回の ソースコードだと,シミュレータでは送受信がうまくいかなくなっていた. (パケットの送受信後にIMRがゼロに設定されてしまい受信割り込みがマスクされ, 受信が一切できなくなってしまう)

対策として,ページ0の値をページ2にコピーする処理を追加する. 他にもページ2でリードするレジスタはいくつかあるためまとめて対処する.

@@ -565,6 +569,14 @@
 #define NE2000_IMR     (registers[0][0x0f])
 #define NE2000_CURR    (registers[1][0x07])
 
+#define NE2000_PSTART_R (registers[2][0x01])
+#define NE2000_PSTOP_R  (registers[2][0x02])
+#define NE2000_TPSR_R   (registers[2][0x04])
+#define NE2000_RCR_R    (registers[2][0x0c])
+#define NE2000_TCR_R    (registers[2][0x0d])
+#define NE2000_DCR_R    (registers[2][0x0e])
+#define NE2000_IMR_R    (registers[2][0x0f])
+
 #define RTL8019_SIM_CR  (memory[RTL8019_ADDR + 0xfe])
 #define RTL8019_SIM_SR  (memory[RTL8019_ADDR + 0xff])
 #define RTL8019_SIM_SR_NRDY (1 << 0)
...(中略)...
+  NE2000_PSTART_R = NE2000_PSTART;
+  NE2000_PSTOP_R  = NE2000_PSTOP;
+  NE2000_TPSR_R   = NE2000_TPSR;
+  NE2000_RCR_R    = NE2000_RCR;
+  NE2000_TCR_R    = NE2000_TCR;
+  NE2000_DCR_R    = NE2000_DCR;
+  NE2000_IMR_R    = NE2000_IMR;
+
   memcpy(&memory[RTL8019_ADDR + 1], ®isters[page][1], 0x0f);
 
   return vector;

で,修正したソースコードは以下.OS側も一部修正してあるため, 今回はシミュレータのdevice.cと,あとOSも配布する.

他にも以下の細かい点を修正してある.

しかしシミュレータで動作させてping応答は問題無くできたが,ブラウザで接続すると 大きなページがダウンロードできない(小さなページは問題なし). どうもTCP/IPでの連続データ送信がうまくいっていないようだ.変更されているのは 割り込みまわりだけなので,OSの送信割り込み処理かシミュレータのether疑似デバイス まわりに問題があるように思われる.うーんやることは山積みだ.
メールは kozos(アットマーク)kozos.jp まで