読者です 読者をやめる 読者になる 読者になる

HoppeSoft

やわらかコーディング

OS自作入門のその後(PCI編その1)

てぃーてつです。

今回はネットワークそのほか諸々を実装するために
PCI(Peripheral Component Interconnect)を認識してみようと思います。
※例のごとく質問などは私にお願いします

そもそもPCIって何者だよって方はWikipediaを参照してください。
Peripheral Component Interconnect - Wikipedia

PC組み立てたことある人はすぐにピンとくるのかもしれない。


さてシリアル通信編でも参考にさせていただいたOSDev.org
PCI - OSDev Wiki

ここに疑似コードがありますので、はりぼてOS向け(というよりC言語)に書き下します。

/* PCI */
unsigned short PCIConfigReadWord(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset)
{
  unsigned int address;
  unsigned int lbus = (unsigned int)bus;
  unsigned int lslot = (unsigned int)slot;
  unsigned int lfunc = (unsigned int)func;
  unsigned short tmp = 0;

  /* コンフィギュレーションアドレスを作成 */
  address = (unsigned int)((lbus << 16) | (lslot << 11) | (lfunc << 8) | (offset & 0xFC) | ((unsigned int)0x80000000));

  /* アドレスの書き出し*/
  io_out32(0xCF8, address);
  /* データの読み込み */
  tmp = (unsigned short)((io_in32(0xCFC) >> ((offset & 2) * 8)) & 0xFFFF);
  return(tmp);
}

unsigned short PCICheckVendor(unsigned char bus, unsigned char slot)
{
  unsigned short vendor, device;
  /* 最初のコンフィギュレーションを読み込むテスト */
  /* ベンダーなし(0xFFFF)の場合、デバイスは存在しないことになる */
  if((vendor = PCIConfigReadWord(bus, slot, 0, 0)) != 0xFFFF)
  {
    device = PCIConfigReadWord(bus, slot, 0, 2);
  }
  return(vendor);
}

コメントは疑似コードにあったものを訳したものなのでガバガバかもしれません()
あとはPCICheckVendorで任意のbusとslotを指定してあげるとベンダIDが返ってきます。
バイスIDを知りたい場合はreturnでdeviceを返してあげてください。

前回せっかくシリアル通信ができるようにしたので、
ベンダIDを調べるテストをしてみました。
なおシリアル通信で文字列を送信する関数は、前回の一文字送る関数を
文字数分だけ繰り返すように組みました。

init_serial(COM1);
sprintf(s, "%x",PCICheckVendor(0,0));
write_serial_string(COM1, s);

charの配列sにsprintfでフォーマットは16進の%xにして書き込み。
これをwrite_serial_string(文字数分COMポートに送り付ける関数)する。
busとslotはとりあえず0に設定してみました。

ちなみにぼくのおすすめのテスト方法は、close_console直後に
使いたい関数をインジェクトする、です。
コンソールを閉じた直後、とタイミングが任意かつ明確で、組み込むのも簡単。

さて例のごとくTera Termでシリアルを受けてみます。

f:id:titets38:20170401144214p:plain

既視感バリバリの数字。。
数字だけじゃ確信持てないのでPCIのデータベースを見てみましょう

PCI Vendor and Device Lists

Vendor Searchの欄に数字を入れてSearchボタン押しましょう。
なおデバイスIDの場合はその下です。

8086でベンダーサーチ……
やっぱりインテルじゃねーか!!!!!


その2に続く(たぶん)

OS自作入門のその後(シリアル通信編)

てぃーてつです。

川合秀実先生の名著『30日でできる!OS自作入門』、
この入門をめでたく果たした後、「さて次は何を実装しよう」と考えるだけで
とても楽しいですね。きっと読破・自作を行った方々は皆そう感じることでしょう。

ただ、我々はまだまだ入門したばかりですから、難しいことはわかりません。
大学などでオペレーティングシステム概論、のような話を聞いたことがある方、
コンピュータアーキテクチャについてある程度の知識がある方などはもしかしたら
次の一手が見えているかもしれません。が、我々OS素人にはちんぷんかんぷんです。


次に何をしようか、ぼくなりに調査も交えながら考えた結果、
「シリアル通信」をやってみようかなと。

というのも、下に示すような大変興味深いサイトを発見したからで
Expanded Main Page - OSDev Wiki

大変すばらしいですね。美味しそうな蜜がそこかしこに……といった感じ。
ずらーっと並んだ項を眺めていくと
Serial Ports - OSDev Wiki

ありました。シリアル通信です。RS232をメインに書かれているようですね。
概要やポートの在り処、設定すべき値なんかも見られますね。


RS232といえば、いまでこそ外部機器との通信はUSB一強といった風ですが、
PCに標準でコネクタが装備されていたりして活躍をみせました。
RS232にまつわる思い出が個人的にありまして、ちょっとしたロボットのセンサの
値をTera Termに突っ込むとか、ボーレートミスってなんじゃこりゃ!とか
……ここまで書いてて思ったんですが、もしかして現代っ子ってあのコネクタ
見たことないんじゃ。。。

さておき、リンクのExample Codeの節を参照して試しにbootpack.cにでも
コピペしてもらえればサクッと……inb, outb関数が未定義でしたね。。。
今後も使えると思うのでインラインアセンブラを用いてしまいましょう。
I/Oポートの読み書きをする関数を書いてみます。
(2017/03/26追記)
inb, outbではなく、はりぼてOSで使用しているio_in8, io_out8でも
実質同じことができるので、以下はインラインアセンブラでもできるんだ~
くらいの気持ちで読むといいと思います。


はりぼてOSを本のまま作成していればGASで吐いてから
諸々やっていたはずですから、Intel記法ではなくAT&Tが無難かも。

unsigned char inb(unsigned short port) {
	/* バイト(8bit)サイズでI/Oポートから値を得る
	** ここではインラインアセンブラ(AT&T記法)を用いた
	*/
	unsigned char val;
  __asm__ __volatile__ (
		"inb %1, %0\n\t" : "=a" (val) : "d" (port)
	);
	return val;
}

void outb(unsigned short port, unsigned char val) {
	/* バイト(8bit)サイズでI/Oポートに値を出力	*/
  __asm__ __volatile__ (
		"outb %1, %0" : : "d" (port), "a" (val)
	);
}

慣れないことはするものじゃないですね…たどたどしいながら組んでみました。
さてinit_serial関数の後にis_transmit_empty→write_serialしてCOM1に一文字飛ばしてみます。
カーネルのメイン関数のどこかに組み込むなりすれば動作確認はすぐできるでしょう。

ちなみにぼくは自作OSをVirtualBoxで走らせて、設定でゲストCOM1 & ホストCOM3→Tera Term(COM4)とセッティングして受信させました。
f:id:titets38:20170318022325j:plain

そして案の定PCにRS232Cなんぞ挿せなかったので(笑)、仮想でシリアルポートを設定しました。
sourceforge.net
それがCOM3とCOM4のペアということですね。

(2017/04/27追記)
ぼくの勉強不足で申し訳ないんですが別ソフト使わなくてもVirtualBoxで直に仮想ポート作れるそうです

qiita.com



Tera Termで受ける際の設定はググればいくらでも出ます。
ボーレートはinit_serial関数で設定した値ですね。

f:id:titets38:20170318021451p:plain

これでホストPCや外部機器とコミュニケーションを図れます。
デバッグに使ってもいいかもしれませんね。

次は何をしましょうか。わくわくです。

(2017/03/20追記)
※記事の内容で誤りや疑問があったらぼくにコメント等で質問してください
決して川合先生のところにご迷惑をかけないように!!

matplotlibで線を部分的に消す

表題の通り、線を部分的に消したかったんだけど
単純だからかググっても引っかからなかったのでメモ

例えば以下のコード

import numpy as np
import matplotlib.pyplot as plt

x=np.array([-3, -2, -1, 1, 2, 3])
y=np.sin(x)

plt.plot(x,y)

で出力される下図のようなグラフ
f:id:titets38:20170119134406p:plain

で、x=0の時の数値は実際出してないが
(当たり前だけど)x=-1と1の点をつないで表示する

ここで、x=0のときのyの値は実際には出ていないから消したい場合
(例えば実験で観測できなかった部分があるとか)
単純に以下のようにする

import numpy as np
import matplotlib.pyplot as plt

x=np.array([-3, -2, -1, 1, 2, 3])
y=np.sin(x)

plt.plot(x[0:3],y[0:3],'b')
plt.plot(x[3:],y[3:],'b')

x[0],y[0]から3点分だけ描画し、追加でx[3],y[3]から先を描画している
また色が自動で変わってしまうので両方青色を指定した
f:id:titets38:20170119140918p:plain

  • 凡例をつける場合

凡例などつけるときはどちらかのプロットにだけラベルをつけるのがいい
両方につけると同じ凡例が2つ表示されてしまう

plt.plot(x[0:3],y[0:3],'b',label="sin")
plt.plot(x[3:],y[3:],'b',label="sin")
plt.legend(loc='lower right')

f:id:titets38:20170119141525p:plain
片方にすると

plt.plot(x[0:3],y[0:3],'b',label="sin")
plt.plot(x[3:],y[3:],'b')
plt.legend(loc='lower right')

f:id:titets38:20170119141654p:plain

Anaconda(Python3.x)でOpenCVのSURFを使おうとした時の話

OpenCVのSURFをPython3.xで動かそうと思ったら大変だった

 

ぼくはWindowsにAnaconda3(64bit)でPython入れたクチなんですが、

OpenCVをインポート、SURFを動かそうと思ったらそんな関数ないよ、と

 

どうやらAnacondaで入ってくるOpenCV3はSURFが標準で入ってない?らしい

調べてみると……

www.pyimagesearch.com

やっぱり入ってなかったんや……SIFTおまえもか……

そういうわけで、githubからopencv_contrivを落としてきて導入しました

テストコードも上記リンクにあったので軽く試験してみたら、動いた!

 

やー、焦った焦った。