いものやま。

雑多な知識の寄せ集め

コーダーブルームの多摩サイライドに行ってきた。

先週の土曜日、7/15(土)にコーダーブルームのオーナーズライドが開催されたので、自分も参加してきた。

前回のライドの様子は、以下から:

今回のルート

ということで、さっそく今回のルートから。

今回のライドは、二子玉川に集合して多摩川サイクリングロードを走り、途中、イタリアンのお店に入って昼食をとり、そのあと再び多摩川サイクリングロードを走って、『耳をすませば』の舞台になった「いろは坂」を登って、多摩センターまでの約30km弱のコース。

ただ、調べてみると、集合場所の二子玉川は自宅から約45kmの距離。
集合時間も今回は10時集合開始ということだったので、これなら自走でいけるかな、と。

ということで、自分が走ったコースは、以下。

自宅を出て、国道4号を使って秋葉原まで行き、そこから靖国通り〜国道246号で二子玉川を目指した。

自宅〜二子玉川

今回自宅を出たのは、7時半。

ホントは7時には出ようと思ってたんだけど、少し遅くなってしまった。
とはいえ、3時間弱あれば、45kmとかは余裕だろうと。

前回の帰り、暑さでけっこうやられてしまった感じがあったので、今回は念入りに暑さ対策をしていった。
ボトルには水を入れ、体にかけられるようにしたのはもちろん、出かける前にスーパーマルチビタミン&ミネラルを飲んでミネラルの補給もバッチリ。
さらに、熱中飴という塩飴も装備し、体に吹きかけるとひんやり気持ちいいスプレーも持った。

そんなバッチリの態勢でスタートして、実際、最初は順調な感じだった。
1時間走って20km弱くらい。

ただ、そのあとがまったくの誤算で、まず、お腹の調子が悪くなって、コンビニに二度駆け込み(^^;
思えば、熱中症の前触れだったのかも。

さらに、信号でストップ&ゴーを繰り返させられて、まったく進まない。
都内の道はこれがツラい・・・

それに加えて、国道246号の走りにくいこと走りにくいこと。

国道246号は以前にも走ったことがあって、それこそ『ろんぐらいだぁす!』の聖地巡りをしようとつきみ野に向かったときに走ったんだけど、やっぱり走りにくい。
前に走ったとき、この道はもう二度と走りたくないと思ったのが、二子玉川を越えてからだったので(オーバーパス禁止の場所が多くて、側道に降りるために緩い坂を下っては登りを何度も何度も何度も何度も繰り返させられるうえ、交流量も多くて危険)、二子玉川まではそんなでもなかっただろうと思ってたんだけど、やっぱり酷い。
ろんぐらいだぁす!』の亜美ちゃんがロードを買って一番最初に走ったのがこの道とか、ちょっと信じられない。

何が酷いのかというと、バスがめちゃくちゃ多い。
なので、2車線あるものの、片側は常にバスが出たり入ったりしていて(さらにそんな使われ方なので、路駐してる車もめっちゃ多い)、バスや停まっている車、後ろから走ってくる車などにいろいろ気をつけながら走らないと、めちゃくちゃ危ない。
それでさらに信号で毎度毎度捕まるため、消耗がハンパ無いし、まったく進まない。

国道246号の二子玉川越えてからのルートは二度と使うまいと心に決めてるルートだけど(実際、前回は行きで心が折れたので、帰りは別ルートにした)、渋谷〜二子玉川のルートも極力避けるようにした方がいいし、初心者は絶対に走らない方がいいと思った。
こんなところを初心者が走ったら、事故らない方が不思議。
マジ危険。

そんなこんなで、残り25km強を走るのに2時間近くかかってしまって(ママチャリかよ!)、また遅刻で集合場所に到着。。。
しかも、割と熱のダメージが。

二子玉川を出発

二子玉川に到着して、軽く自己紹介したら、前回同様に3班に分かれて出発。
出発したら、多摩川沿いの土手を進んでいく。

多摩川サイクリングロードを走るのは初めてだったけど、なんというか、江戸川サイクリングロードに慣れてしまってると、走りにくい(^^;
細いのもそうだけど、未舗装の部分があったのにはさすがに驚いた。
荒川サイクリングロードや境川サイクリングロードも数回走っているけど、こんなサイクリングロードもあるんだなというのは、ちょっと驚き。
まぁ、今回走った区間がちょうど走りにくい区間だったという可能性は全然あるけど。

まったく関係ないけど、二子玉川で思い出すのは、『フタコイオルタナティブ』w

このアニメ、面白かったなぁw
もう12年前とか、驚く。
二子玉川も再開発が進んで、聖地とかの様子はけっこう変わっちゃってるみたい。

さらに関係ないけど、ufotable繋がりで『コヨーテ・ラグタイム・ショー』も個人的にはオススメ。

2話以降の評判悪いけど、個人的には悪くないと思うんだけどなぁ。
主人公がムサいオッサンなのがあかんのかなぁ?
めっちゃかっこいいと思うんだけどw

閑話休題

で、そんな多摩川サイクリングロードを走って登戸へ。
そこから一度サイクリングロードを降りて、お昼を食べるお店へ向かっていった。

ポルト・ディ・マーレ

今回お昼を食べたのは、オサレなイタリアンのお店、ポルト・ディ・マーレ。

外はすごく暑くなっていたので、クーラーの効いた店内が気持ちいいw
店員さんも小まめにお水を注ぎにきてくれて、暑い中走ってきた身にはすごく嬉しかった。

今回自分が食べたのは、海老のアンチョビクリームスパゲッティ。
それに前菜とオレンジシャーベット、あとアイスコーヒー。

f:id:yamaimo0625:20170718001343p:plain

f:id:yamaimo0625:20170718001356p:plain

いや〜、とても美味しかった。
自転車に乗ってこんなオサレなお店に入るのも、悪くないかもしれない。

みんなで食べながらいろいろお喋りして、けっこう長い時間いたかも。
ホントにクーラーが気持ちよすぎて、ここで眠ってしまいたい気分だったw

いろは坂

しかし、今回の目的地はここではないので、名残惜しい気持ちを残しつつ、再び炎天下へw

再び多摩川サイクリングロードに戻って、ひたすら西へと進んでいく。

この辺りはけっこう走りやすく整備されてて、他の自転車乗りや、ランニングしている人をよく見かけた。
ランニングしてる人の上半身裸率が凄かったけど、そういうものなんだろうかw

ただ、暑さはホントにハンパなくて、運動強度は全然低いんだけど、体がどんどん熱くなっていってしまうので、もうひたすら冷やすことに必死。
ボトルに入れた水もお湯みたいになってしまってたけど、飲むのはもちろん、体にかけて熱を飛ばしていく。
それもすぐに乾いてしまうくらいに暑かったのだけど。

あと、個人的にちょっときつかったのが、スピードを落とすためにつけられてた道の凹凸。
暑くない環境なら全然問題なかったんだけど、暑さで若干頭痛が出てきてたので、この凹凸で車体が揺らされると脳に響いて痛みが。。。

まぁ、そんな感じで暑さと軽い頭痛には困らされたものの、ゆるゆると進むうちに聖蹟桜ヶ丘、そしていろは坂に到着。

いろは坂

ということで、今回のメインイベント、いろは坂
耳をすませば』の舞台だったらしく、そういえば自転車で登ってるシーンあったよなぁ、と。

さっそく登り始めたんだけど、なるほど、確かにけっこう斜度がある。
と、思ってた直後で、一度公園へ。
そこで街中を見下ろしたり、記念撮影をしたり。

f:id:yamaimo0625:20170718003546p:plain

そして、再び登りへ。

そこからの斜度はそこまででもなかったんだけど、なにぶん暑くて、少し強度を上げるだけで一気に顔が暑くなって、ズキンズキンとヤバい頭痛が(^^;
仕方ないので、脚は余裕なんだけど、一番軽いギヤでなるだけ力を入れないようにクルクルと回していく。
もう頭が痛くてアカンw

救いだったのは、距離がそんなになかったこと。
なので、割とアッサリとクリア。

それにしても、みんな速いw
スイスイと登っていくねw

f:id:yamaimo0625:20170718004100p:plain

登りきって少し進んだランナバウトで一休み。
なんと、ここにコーダーブルームの車がやってきて、差し入れをもらえたw

多摩センター〜帰路

このあとは坂を下って、解散場所の多摩センターへ。
途中、ちょっと道を間違えてまた登りを走ったりもしたけど(頭痛持ちにはけっこう堪えた)、なんとか無事解散地点まで到着。
一本締めを行なって、解散となった。

解散後、自分は多摩センター駅まで移動して、そこから輪行

ただ、やっぱり軽い熱中症になってた感じ。
幸い、電車で座れたので家まで無事帰れたけど、頭痛が収まらないのと、吐き気がちょっと酷くて、かなりグロッキーだった。
暑さで胃にもダメージがきてたみたい。
それと、今回の頭痛の感じからすると、ミネラル不足で頭痛が起きてたというよりかは、熱が頭に回って、それでダメージを受けて起きてたように感じる。
実際、前回も運動強度を上げると頭痛が酷くなる感じがあったし。
なので、今後は頭ももっと冷やすようにした方がいいのかもしれない。
水をバシャバシャ頭からかぶれるようにしないとなw


で、次回のオーナーズライドだけど、8月、9月はかなり暑い時期なので、オーナーズライドはお休みの予定とのこと。
なので、次は10月になる予定みたい。
今回は暑さでかなりやられてしまったけど、10月ならかなり走りやすくなってそう。
楽しみw

今日はここまで!

『人工知能のための哲学塾・東洋編 第弐夜「井筒俊彦と内面の人工知能」生中継』を観てみた。

以前、三宅先生の書いた『人工知能のための哲学塾』を読んだ感想を書いた。

その続きということで、東洋編が現在進行形で展開されている:

上に挙げた通り、ニコ生でタイムシフト視聴出来るので、両方とも観てみた。
で、第弐夜に関してちょっと思うことがあったので、そのことを。

ちなみに、第弐夜のスライドは、SlideShareで見ることが出来るので、そちらから。
めっちゃ枚数あるけどw

意識に関する2つの図

さて、自分が話題にしたいのは、次の2つの図。

まず1つ目は、西洋型のボトムアップ機能モデルの図:

f:id:yamaimo0625:20170711220557p:plain

※いくつかの図をまとめて整理してある

そして2つ目は、東洋型のトップダウン存在モデルの図:

f:id:yamaimo0625:20170711224013p:plain

※スライドだと、矢印の向きが上から下に降りてきてるけど、あとで説明する通り、下から上への矢印が正しいと思われる

この2つの図は、形こそ似ているものの、実際には全くの別物。
というか、2つ目の方は上下を逆にした方がいいw

実際、三宅先生はあとでこの2つの図を融合させようとしているんだけど、何を表そうとしているのかが正直分からない(^^;
実のところ、この2つの図を融合させるには、この2つの図がそれぞれまったく別のレベルで書かれていることに気づかないといけない。

クラス図とオブジェクト図

このことに関して、自分は次のようなツイートをした:

そう、クラス図とオブジェクト図というのが、ここでは重要な鍵になってくる。

まず、クラス図というのは、簡単にいうと、「モノ」(オブジェクト)の設計図。
「モノ」がどんな「種類」(クラス)のモノで出来ているかを示してくれる。

例えば、車の(すごく簡単な)クラス図を描いてみると、次のような感じ:

f:id:yamaimo0625:20170711225905p:plain

車体があって、ハンドルがあって、アクセルペダルとブレーキペダルがあって、エンジンとブレーキがあって、タイヤがある、というような感じ。

重要なのは、「どんな種類のモノ」によって出来ているかを示していて、「どんなモノ」によって出来ているかを示しているわけではない、ということ。
なので、車にタイヤは4つついているけど、図には「タイヤ」という種類(クラス)は1つしか出てきていない。
(※厳密には、「多重度」というのを使って、そのクラスのオブジェクトが何個あるのかを示したりするけど、ここでは省略)

一方、オブジェクト図というのは、クラス図を元に実際に作られる「モノ」(オブジェクト)自体を描いた図。

例えば、車の(すごく簡単な)オブジェクト図は、次のような感じ:

f:id:yamaimo0625:20170711231430p:plain

ポイントは、クラス図では「タイヤ」は図に1つしか出てこないけど、オブジェクト図では「タイヤ」が図にちゃんと4つ出てきてるということ。

ここがクラス図とオブジェクト図との大きな違いで、

  • クラス図は「種類」(クラス)のつながりを描くので、ある「種類」のモノは図に1つしか出てこない
  • オブジェクト図は「モノ」(オブジェクト)のつながりを描くので、同じ「種類」のモノが図にいくつも出てくる

となっている。

ちなみに、これはあくまで1つの車について描いたオブジェクト図で、複数の車について描いたオブジェクト図というのも考えることが出来る。
その場合、以下のようになる:

f:id:yamaimo0625:20170711233717p:plain

2つの図の違い

さて、元の2つの図について、改めて考えてみる。

この2つの図はどちらも層になっていて、下から上へ積み上がっているように見える。
なので、とても似ている。

ただ、前にも述べた通り、後者の図は実際には逆さまなので、次のようになっている。

f:id:yamaimo0625:20170711234820p:plain

この形だからこそ、「トップダウン型」なわけ。

そして、それ以上に重要なこと。
それは、前者がクラス図であるのに対し、後者がオブジェクト図だということ。

後者の図をちゃんと描くと、次のようになる:

f:id:yamaimo0625:20170712000237p:plain

一番下にあるのは、分割される前の「あるがままの世界」。
この世界を「そのまま」見ることが出来るのは(そして、それはつまり「何も」見てないということになるのだけど)、(あえて名前をつければ)神様だけとなる。
「絶対一者性の領域」というのは、つまりそういうこと。
客観的世界、とも言えるけど、その「客観」というのは科学の「客観」をはるかに超えたもので(「観測」されてしまったら、それはもう「客観」ではない!)、観測される以前の、可能性の海が広がっていて、全てがそこにあるけど、何者でもないというような、そんな世界。

その上にあるのが、生物として知覚される「知覚の世界」。
この世界は、生物がその身体をもって知覚することで、「あるがままの世界」からその生物が知覚できる要素が切り出された世界。
ポイントは、生物のその身体的特徴によって、ここで切り出される世界というのは生物の種類ごとに異なってくるということ。
なので、オブジェクトは複数存在している。
しかし、それはまだ「1種類の生物としての世界」であって、「1個体の生物の世界」というところまでは分割されていない。
それがゆえ、「潜在的分節化の領域」となる。

そして、そのさらに上にあるのが、言葉や文化、あるいはその人の経験などから意味をもって切り出された「意識の世界」。
知覚された世界にさらに「意味」ーー例えば「これはお父さん」とか「このりんごは美味しそう」だとかーーが与えられた、主観的な世界になっている。
このレベルになると、各個体ごとにその世界は異なってくるので、オブジェクトは各個体分(多重人格なら、複数もありえる?)存在している。
なので、「存在的多者の領域」となる。

なんで東洋哲学でこんな図を考えるのかというと、一言でいえば、「主観=客観」の先入観を取り除くため。

普通だと、自分が見ている世界(主観的世界)が世界そのもの(客観的世界)であり、他の誰もが自分と同じように世界を見ているものだと思ってしまう。
自分の見ていること、感じていること、考えていること、信じていることが、世界の真理、すべてであり、それ以外に別の世界(のインスタンス)が存在するだなんて、思いもしない。
例えば、自分が「ツラい」と思っていることは、世界においても「ツラい」という「客観的真理」であり、それが覆ることはない、と思ってしまう。

でも、そうやって「客観的真理」だと思っていたことが、実は「主観的真理」に過ぎないよね、と。
人間は言葉によって、あるいは、様々な関係性において、世界を切り分け、「それ」を「それ」として認識しているに過ぎないでしょ、というのが、仏教や道家の教えるところ。
言葉によってレッテルを貼ったり、固定した考えに捉われてしまったりするのではなく、それらは自分がそのように世界を切り出しているだけだと気付き(悟り)、そこから自由になったらより善く生きていけるでしょ、と。

飲茶さんの『史上最強の哲学入門 東洋の哲人たち』に書かれている次の内容が面白い。

 このことを理解するために、ちょっとこんな想像をしてみてほしい。
 あなたが「耳を見て興奮を感じる文化の国」に生まれたとする。 その国には「女性はみんな生まれたときからずっと耳を隠して暮らしており、本当に愛した男性にしか耳を見せない」という奇妙な風習があった。 あなたが男性だと仮定してそういう国で子供の頃から育ったとしたら・・・間違いなく、あなたを含めた国中の男たちはみんな「うおおおぉ、女の子の耳が見てええ!」と思うはずである。
(中略)
 そして、あなた自身、「耳」のことを考えては下劣な情欲に燃え上がり、そのことについて自己嫌悪に陥って苦しみもだえていた。
(中略)
 そして、ついには思考が途切れ、分別が消え去ったその瞬間ーー。 それはたった一瞬の隙間。 だが、その一瞬のなかに「智慧」が現われ、ひとつの奇跡がおとずれる。 あなたは、赤子のような無垢な境地で、知識ではなく、論理ではなく、言葉ではなく、「いま起きていることの本質」を実感として、体感として理解する。
 あなたはついに究極の真理を悟った。

「こ れ は た だ の み み だ」

「うわああああ!」と、あなたは頭を抱えて、へたりこむ。
(中略)
 あなたは、今まで自分が必死になってきたことの、あまりの馬鹿馬鹿しさと恥ずかしさに笑うしかなかった。
 夜が明け、あなたが街に下りてみると世界がすっかり変わって見えた。 物心がついたときから、まとわりついていた鎖が外れたような、晴れ晴れとした自由な気分。 そんな幸福感とともに世界を見ることができた。
(『史上最強の哲学入門 東洋の哲人たち』飲茶、著より引用)

全く余談だけど、この本はめっちゃ面白いしすごく読みやすいので、オススメ。
西洋編もあって(というか、西洋編が先に出ている)、そっちも面白いので、やはりオススメ。

閑話休題

そんなわけで、言葉や思考(思い込み)から解放され、より自由になろう、と。
さらにいえば、環世界の話があるように、生物が見ている世界というのは、その身体(インタフェース)を通して見られた、各生物ごとの世界でしかない。
なので、さらには身体まで捨て、そのさらなる奥の根源、あえて名前をつけて(本質的に名前がつけられない)「空」や「道」に至ろう、と。
これが図でいう「上昇過程」。
ニコ生でも話題に上がってた、十牛図の8枚目の絵に至るまで過程になっている。

でも、ここで勘違いしてしまってはいけなくて。

心頭滅却すれば火もまた涼し」なんていうけど、熱いものは熱い。
心の持ちようでどうとでもなるでしょ、という話ではないことに気をつけないといけない。
「熱くないんだ、熱くないんだ」と必死に否定するのでも、「心を無にすれば熱くないんだ」と冷静に否定するのでもなく、「あ、自分は今『熱い』と感じているんだ」と気づいてそれを認めることが重要。

色即是空のあとに空即是色と繋がるように、あるいは、十牛図の9枚目、10枚目で再び戻ってくるように、「主観≠客観」なんだと気づいた後で、その主観を否定するのではなく、その主観を「主観である」と肯定しないといけない。
これが図でいう「下降過程」。
なんでそうしないといけないかというと、だって、何事からも解放され、自由になろうとしたって、実際には自分たちは身体があり、その身体を通してでしか世界と交われないのだから。
そんなに身体が嫌で嫌で仕方ない「身体の軽蔑者」は、ニーチェがいうように、さっさと身体を捨て去ってこの世からいなくなってしまえばいい。

 身体を軽蔑する者に、わたしはわたしの言葉を言いたい。 かれらが考えなおし、説をあらためることなどは、わたしは求めるところでない。 かれらはさっさと自分の身体に別れをつげて、ーー口をきかなくなってもらいたいものだ。
(『ツァラトゥストラはこう言った』「身体の軽蔑者」より引用)

ニーチェは「三段の変化」を説いたわけだけど、これはこの「上昇過程」「下降過程」というのにとても似ている。
盲目的な信者(駱駝)からあらゆる価値を否定し壊していき(獅子)、しかし最後には肯定して世界を創造する(幼子)。

 精神はかつては「汝なすべし」を自分の最も神聖なものとして愛した。 いま精神はこの最も神聖なものも、妄想と恣意の産物にすぎぬと見ざるをえない。 こうしてかれはその愛していたものからの自由を奪取するにいたる。 この奪取のために獅子が必要なのである。
(中略)
 そうだ、創造の遊戯のためには、わが兄弟たちよ、聖なる肯定が必要なのだ。 ここに精神は自分の意志を意志する。 世界を失っていた者は自分の世界を獲得する。
(『ツァラトゥストラはこう言った』「三段の変化」より引用)

あるいは、客観というものを一度忘れ(エポケー)、主観から世界を構築していき、そして身体性へと還ってきた現象学に似ているとも言える。

2つの図を融合させる

東洋哲学の話が長くなってしまったけど、元の2つの図の話に。

このように、後者の図はオブジェクト図になっているというのが重要なこと。
なので、クラス図の前者とそのまま融合させようとしたって、レベルが違うのでうまく組み合わさらない。

じゃあ、どうすればいいかというと、これは簡単で、そう、前者もオブジェクト図にしてしまえばいい。
これがツイートで書いていた「オブジェクト図で考えればいいのに」という発言の意図。

生物の種類だけ環世界は存在し、そして個体の数だけ主観的世界は存在するわけだから、次のようになる:

f:id:yamaimo0625:20170712004921p:plain

こうやって見ると、2つの図がキレイに融合しているのが分かると思う。

西洋型の図で見ていたのは、1個の個体に関するボトムアップの図。
世界から環世界へ、環世界から主観的世界へと情報はインプットされ、主観的世界から環世界へ、環世界から世界へと作用がアウトプットされていくことを示している。

一方、東洋型の図で見ていたのは、複数の個体に関するトップダウンの図。
主観的世界から言葉や思考を取り去って環世界へ至り、環世界からさらに身体を取り去って世界に至る。
逆に、世界から生物の種類だけ環世界が生まれ、さらに環世界から個体の数だけ主観的世界が生まれる。
そうやって、各々に異なった主観的世界が生まれてきているんだということを示している。

人工知能は「悟る」のか?

さて、こうして融合された図から、人工知能のエンジニアリングに活かせることは何か。

一つは、オブジェクトの構成のさせ方。

具体的な構成の議論は省くけど、上の図に書いたようなオブジェクトが生成されれば、各個体ごとが異なる「自分の世界」を持つことになるので、そこから生成される行動は、各個体ごとに個性的(けど、「身体」のレイヤーがあるので、そこからは大きく外れられない)ものとなると考えられる。

もう一つは、人工知能に「悟り」を与えるということ。

これは、普通の「悟り」とはちょっと違うけど、「この見ている世界は一つの世界の見方にすぎないんだ」という気づきから、意識レイヤーの内容を固定したままにせず、柔軟に組み替えていくようなアルゴリズムになる。
人工知能が、自分自身の世界の見方を柔軟に変化させていく。
これが出来れば、決まりきった行動をするのではなく、ダイナミックに行動が変化する人工知能が生まれてきそう。

今日はここまで!

人工知能のための哲学塾

人工知能のための哲学塾

史上最強の哲学入門 (河出文庫)

史上最強の哲学入門 (河出文庫)

ツァラトゥストラはこう言った 上 (岩波文庫 青 639-2)

ツァラトゥストラはこう言った 上 (岩波文庫 青 639-2)

『OS自作入門』を読んでみた。(その6)

前回はリンカとリンカスクリプトを使ってアドレスの問題を解決した。

これで最初のOSっぽいプログラムは一応完了で、次はちゃんとフロッピーディスクの内容をメモリに読み込んで実行するIPL(Initial Program Loader)を作っていくことになる。
ただ、その前にもう一仕事。

フロッピーディスクのイメージ作成の改善

ここまではフロッピーディスクのイメージをまるごと作っていたわけだけど、この先はファイルも増えてくるし、ブートセクタに書き込むプログラム(IPL)とOS本体をちゃんと分けておきたいので、フロッピーディスクのイメージ作成の改善を。

具体的には、IPLとOS本体はそれぞれ別のリンク単位にして、ELFファイルおよびバイナリファイルをそれぞれ作成し、IPLはブートセクタとして、そしてOS本体はフロッピーディスクの最初のファイルとして、ディスクイメージに配置するようにしていきたい。

本ではここでedimgという著者お手製のツールを使っているんだけど、これはWindows用のプログラム。
Windows用だからそもそもMacだと動かないというのはあるんだけど、それを抜きにしても、なんともよく分からないツールを使うというのはあまり気が進まない。
(ネットで検索しても情報が出てこないから)
それしか方法がないのなら仕方ないけど、よく使われている標準的なツールがすでにあるなら、そっちを使った方がいい。

ちなみに、Macの場合、hdiutilというコマンドラインツールが標準としてあるみたい。
少し触ってみたけど、使えなくはなさそうな感じ。

ただ、問題点として、当然Macでしか使えないのでUnixではダメだし、もう少し重い問題として、ディスクイメージを操作するためにマウントすると、隠しファイルがコソッと作られてしまうというのがあった。
これから作ろうとしているIPLは、OS本体が一番先頭のファイルとして置かれていることが前提となっているので、これだとダメ。
もちろん、ちゃんとFATファイルシステムを扱えるようにIPLを作ればいいんだけど・・・それだと本よりかなり頑張らないといけなくなる。

そんなわけで、どうしたものかと調べていたんだけど、どうやらGNUのmtoolsに含まれるmformatおよびmcopyというツールを使ってやるといいみたいだった。

Mtools - GNU Project - Free Software Foundation

mtoolsはUnix環境でMS-DOSファイルシステムを扱うためのユーティリティで、mformatを使うとディスクイメージの作成が、そしてmcopyを使うとそのディスクイメージへのファイルのコピーが出来る。
(他にもいろいろツールが入ってる)

このmtoolsは多くのLinuxディストリビューションなら最初から入っているみたい。
なので、Linuxなら特に何もせず使えると思う。

ただ、Macには入っていないので、ビルドするところから。

mtoolsのビルド

毎度おなじみ、tarボールを落としてきて、展開。

$ tar zxvf mtools-4.0.18.tar.gz
$ cd mtools-4.0.18

で、あとは./configureしてmakeといきたいところだったんだけど、ちょっと問題があって、修正が必要だった。

そのままビルドすると、以下のようなエラーが出てしまう:

gcc  -DHAVE_CONFIG_H -DSYSCONFDIR=\"/usr/local/etc\" -DCPU_i386 -DVENDOR_apple -DOS_darwin16_6_0 -DOS_darwin16 -DOS_darwin -g -O2 -Wall -fno-strict-aliasing -I.  -I.  -c mainloop.c
mainloop.c:89:15: error: expected ')'
int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp, char *arg,
              ^
./sysincludes.h:106:47: note: expanded from macro 'UNUSED'
#  define UNUSED(x) x __attribute__ ((unused));x

そこで、ネットで調べてmainloop.cを以下のように修正:

--- mainloop.org.c
+++ mainloop.c
@@ -86,7 +86,7 @@
 }

 int unix_dir_loop(Stream_t *Stream, MainParam_t *mp); 
-int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp, char *arg,
+int unix_loop(Stream_t *Stream UNUSEDP, MainParam_t *mp, char *arg,
           int follow_dir_link);

 static int _unix_loop(Stream_t *Dir, MainParam_t *mp,
@@ -95,7 +95,7 @@
     return unix_dir_loop(Dir, mp);
 }

-int unix_loop(UNUSED(Stream_t *Stream), MainParam_t *mp,
+int unix_loop(Stream_t *Stream UNUSEDP, MainParam_t *mp,
           char *arg, int follow_dir_link)
 {
     int ret;

そして、単に./configureをすると、iconv関係のシンボルが見つからないというリンクエラーが出たので、以下のようにライブラリを追加で指定してビルド:

$ LIBS='-liconv' ./configure
$ make
$ sudo make install

これでOK。

mformatを使ったディスクイメージの作成

mtoolsがインストールできたので、さっそくmformatを使ってディスクイメージを作ってみる。

mformatを使ってディスクイメージを作るには、以下のようにすればいい:

$ mformat -f 1440 -B (ブートセクターのバイナリファイル) -C -i (イメージファイル名) ::

ちなみに、-f 1440は、サイズの指定。
あと、-vオプションを使うと、ボリュームラベルを指定できるみたい。
(指定しない場合、NO NAMEというボリュームラベルになるっぽい)

そして、一番気になるのは、最後の::の部分だと思う。

実は、mtoolsでは:というのが特別なドライブレター(AドライブのAやCドライブのCのこと)になっていて、:というドライブレターはディスクイメージのドライブレターになっている。
つまり、A:でAドライブ、C:でCドライブを指し示すように、::でディスクイメージのドライブを指し示すことになっている。
そして、::を使った場合には、一緒に-iオプションを使ってディスクイメージのファイルを指定してやる。

mcopyを使ったファイルのコピー

これでディスクイメージは作れるようになったので、あとはOS本体のバイナリファイルをこのディスクイメージにコピー出来さえすればいい。
それを行うには、mcopyを使う:

$ mcopy -i (イメージファイル名) (OS本体のバイナリファイル) ::

一番最後の::は前述の通り。
これでOS本体のバイナリファイルがディスクイメージにコピーされることになる。
(そして、一番最初のファイルとして配置される)


これでIPLを作っていくための準備は完了。
次はIPLを作っていく予定。

今日はここまで!

30日でできる! OS自作入門

30日でできる! OS自作入門

『OS自作入門』を読んでみた。(その5)

またまた間が空いてしまったけど、前回の続き。

今回はアドレスの問題を解決していく。

アセンブル

おさらいで、何が問題だったかというと、ブートセクタはBIOSによってメモリの0x7c00に読み込まれるのだけど、このコードだと先頭が0x0000だと思ってリンクが解決されてしまうので、正しく動作しないということ。

とはいえ、実際にどのようになっているのか分からないので、まずは逆アセンブルして確認を。

アセンブルには、objdumpを使えばいい。
これもBinutilsに入っているツール。
-dオプションをつけると、逆アセンブルをしてくれる。

$ i386-elf-objdump -d hello-os.o

この結果は、以下のとおり:

hello-os.o:     file format elf32-i386

Disassembly of section .text:

00000000 <pbr>:
       0:   eb 4e                   jmp    50 <entry>
       2:   90                      nop
// 省略

00000050 <entry>:
      50:   b8 00 00 8e d0          mov    $0xd08e0000,%eax
      55:   bc 00 7c 8e d8          mov    $0xd88e7c00,%esp
      5a:   8e c0                   mov    %eax,%es
      5c:   be                      .byte 0xbe
      5d:   74 00                   je     5f <putloop>

0000005f <putloop>:
      5f:   8a 04 83                mov    (%ebx,%eax,4),%al
      62:   c6 01 3c                movb   $0x3c,(%ecx)
      65:   00 74 09 b4             add    %dh,-0x4c(%ecx,%ecx,1)
      69:   0e                      push   %cs
      6a:   bb 0f 00 cd 10          mov    $0x10cd000f,%ebx
      6f:   eb ee                   jmp    5f <putloop>

00000071 <fin>:
      71:   f4                      hlt    
      72:   eb fd                   jmp    71 <fin>

00000074 <message>:
// 以下略

ただ、ちょっとおかしいのに気づくかもしれない。

例えば、0x50を見てみると、直値0xd08e0000をレジスタeaxに代入している。
ここはホントは、0x00をレジスタaxに代入すると出て欲しいところ。
それに、0x5cを見てみると、命令があるはずなのになぜかデータが出てしまっている。

これはなぜかというと、32ビットモード(プロテクトモード)で逆アセンブルされてしまっているから。
前回書いたとおり、ここは.code16擬似命令を使って16ビットモード(リアルモード)の出力をしているので、逆アセンブルも16ビットモードで行ってやらないと、正しくならない。

16ビットモードであることを指定するには、-M addr16,data16オプションを追加する:

$ i386-elf-objdump -d -M addr16,data16 hello-os.o

すると、以下のような出力になる:

hello-os.o:     file format elf32-i386

Disassembly of section .text:

00000000 <pbr>:
       0:   eb 4e                   jmp    50 <entry>
       2:   90                      nop
// 省略

00000050 <entry>:
      50:   b8 00 00                mov    $0x0,%ax
      53:   8e d0                   mov    %ax,%ss
      55:   bc 00 7c                mov    $0x7c00,%sp
      58:   8e d8                   mov    %ax,%ds
      5a:   8e c0                   mov    %ax,%es
      5c:   be 74 00                mov    $0x74,%si

0000005f <putloop>:
      5f:   8a 04                   mov    (%si),%al
      61:   83 c6 01                add    $0x1,%si
      64:   3c 00                   cmp    $0x0,%al
      66:   74 09                   je     71 <fin>
      68:   b4 0e                   mov    $0xe,%ah
      6a:   bb 0f 00                mov    $0xf,%bx
      6d:   cd 10                   int    $0x10
      6f:   eb ee                   jmp    5f <putloop>

00000071 <fin>:
      71:   f4                      hlt    
      72:   eb fd                   jmp    71 <fin>

00000074 <message>:
// 以下略

このとおり、ちゃんと16ビットモードで逆アセンブルがされるようになる。

アドレスの確認

さて、注目したいのは、0x5cの部分:

      5c:   be 74 00                mov    $0x74,%si

もともとのコードは、次のようになっていた:

    mov     $message, %si

つまり、messageのアドレスをsiレジスタに代入していたわけだけど、messageのアドレスが0x74として解決されてしまっている。
このファイルだけ見れば、それは正しそうに思えるんだけど、実際にはこのファイルの先頭が0x7c00にロードされるので、messageのアドレスは、0x7c00 + 0x74 = 0x7c74でないといけない。
なので、このままでは正しく動作しない。
(messageが置かれている0x7c74ではなく、何が置かれているか分からない0x74のデータを読み込んで出力しようとする)

リンカとリンカスクリプト

ということで、このセクションが0x7c00にロードされて動作することを伝え、正しくアドレスの解決を行わないといけない。

そこで必要になるのが、リンカとリンカスクリプト

リンカは、各オブジェクトファイルのセクションをまとめ、それが動作するアドレスにもとづいてアドレスの解決を行ってくれる。
そのとき、どのセクションをどのアドレスにロードし動作させるか記述するのがリンカスクリプト

今回の場合、このセクションは0x7c00にロードされ、そのまま動作するので、そのようなリンカスクリプトを書いてリンクを行えばいい。

ということで、以下のような簡単なリンカスクリプトhello-os.ldscriptを用意した:

SECTIONS
{
    .text 0x7c00 : { *(.text) }
}

このリンカスクリプトを使うようにリンカ(ld、これもBinutilsに入っている)に指示してリンクを行うには、以下のようにすればいい:

$ i386-elf-ld -o hello-os.elf -T hello-os.ldscript hello-os.o

こうして出来たELFファイルを逆アセンブルしてみると、以下のような感じ:

hello-os.elf:     file format elf32-i386
    
Disassembly of section .text:
  
00007c00 <pbr>:
    7c00:       eb 4e                   jmp    7c50 <entry>
    7c02:       90                      nop
// 省略

00007c50 <entry>:
    7c50:       b8 00 00                mov    $0x0,%ax
    7c53:       8e d0                   mov    %ax,%ss
    7c55:       bc 00 7c                mov    $0x7c00,%sp
    7c58:       8e d8                   mov    %ax,%ds
    7c5a:       8e c0                   mov    %ax,%es
    7c5c:       be 74 7c                mov    $0x7c74,%si

00007c5f <putloop>:
    7c5f:       8a 04                   mov    (%si),%al
    7c61:       83 c6 01                add    $0x1,%si
    7c64:       3c 00                   cmp    $0x0,%al
    7c66:       74 09                   je     7c71 <fin>
    7c68:       b4 0e                   mov    $0xe,%ah
    7c6a:       bb 0f 00                mov    $0xf,%bx
    7c6d:       cd 10                   int    $0x10
    7c6f:       eb ee                   jmp    7c5f <putloop>

00007c71 <fin>:
    7c71:       f4                      hlt
    7c72:       eb fd                   jmp    7c71 <fin>

00007c74 <message>:
// 以下略

ちゃんと正しくアドレスが解決されているのが分かると思う。

ちなみに、このELFファイルをobjcopyでバイナリファイルに変換した場合、どう出力されるのか気になるところだけど、objcopyはロードされるアドレスの一番先頭をファイルの先頭として出力してくれるようなので、ちゃんと期待したバイナリファイルになってくれる。

Makefileの修正

最後の仕上げで、以前書いたMakefileを修正して、リンクも行うように。

.PHONY: all clean do

all: hello-os.img

hello-os.img: hello-os.elf
  i386-elf-objcopy -O binary $< $@

hello-os.elf: hello-os.ldscript

hello-os.elf: hello-os.o
  i386-elf-ld -o $@ -T hello-os.ldscript $<

hello-os.o: hello-os.s
  i386-elf-as -o $@ $<

clean:
   -rm *.img *.elf *.o

do: hello-os.img
  qemu-system-i386 -fda $<

これでmakeを実行したときに、リンクを行ってELFファイルを作成し、そこからフロッピーディスクのイメージを作成するようになる。

今日はここまで!

30日でできる! OS自作入門

30日でできる! OS自作入門

コーダーブルームの江ノ島ライドに行ってきた。

もうすっかりおなじみになってきた、コーダーブルームのオーナーズライド。
6月17日(土)に開催されたので、自分も参加してきた。

実は、自分が前回参加した手賀沼ライドの後にも、2回ほど企画があったり。
ただ、体調的な問題で、残念ながら2回とも参加できず。
なので、今回はひさびさの参加となった。

なお、前回のオーナーズライドの様子は、以下から:

今回のルート

さっそく今回のルートから。
今回のルートは、以下のような感じ:

町田駅の南口に集合して、そこから境川をひたすら南下。
そして、江ノ島に到着したら、そこで美味しい海鮮丼を食べて解散、という、30km強のコース。

ただ、これまた前回と同じように、自分はもうちょっと頑張って、実際に走ったルートは、以下のような感じ:

町田までは輪行をするけど、オーナーズライドを走って江ノ島で解散した後は、輪行では帰らず、自宅まで頑張って自走するというルート。
これで約120km。

正直、久々に自転車に乗るので、この距離はキツかった(^^;
素直に輪行して帰るのが正解だったかも。
ただ、頑張ったおかげで、思わぬ出来事もあったので、それはかなり嬉しかったり。
それについてはまた後ほど。

前日

さて、オーナーズライドの話をする前に、ちょっと前日の話など。

前回の記事などを見てもらえれば分かる通り、自分のロードはバーテープを白にしてたんだけど、この黒と白のモノトーンが渋くていいものの、白いバーテープは汚れがとにかく目立つ・・・
そこで、前日にちょっとバーテープの替えを買いに行ったんだけど、そしたら他にもいろいろ商品が目に入るわけで。
そんな中、目に留まったのが、ハンドルバー。
これまで使ってたハンドルバーは幅が420mmで、ちょっと大きいなと思ってたんだけど、400mmのコンパクトなやつを試してみたら、なかなか良さげな感じで。
しかも、思ってたよりも全然安い。
なので、バーテープと一緒に買ってしまったw

そして、せっかく買ったのだから、さっそく付け替えてみたいと思うのが人間というもので、帰宅後に作業を始めてしまったのが大失敗というか。
交換自体は問題なく出来たんだけど、そこからいろいろと調整をしたり、バーテープを巻いてたりとかやっていたら、0時を回ってしまった(^^;

町田駅

ということで、当日。

本当だったら前日の段階で輪行状態にまでしておくつもりだったんだけど、ハンドルバーの交換なんて作業をやってたものだから、やっていなくて。
タイヤの空気圧を確認したり、ポジションを調整するために外してたサドルバッグやライト、ベルを付け直したり、久々にやった輪行セットアップに手間取ったりで、家を出る時間が予定よりも30分近く遅れてしまった・・・
(それでも8時少し過ぎには家を出てるんだけど)

さらに、電車が少し遅れて府中本町での乗り換えに失敗したり、途中でトイレに行きたくなって駆け込んだりしたせいで、町田駅に着いたのが10時10分とか。
町田駅に着いたあとも、小田急線の駅に到着していたので、そこから横浜線の駅の南口に移動するのに時間を取られて、結局集合場所に着いたのが10時20分近くになってしまった。
9時半集合開始、10時出発予定だったので、大遅刻。。。
すみませんでした。。。

今回は何班かに分かれて走るということだったので、すでに出てる組もあるかなと思ったんだけど、ありがたいことに待っててくれたらしく、みんな揃ったところで集合写真。
そのあと、各班ごとで出発していった。

自分は、いそいそと輪行状態を解除して、最後の班で出発することになった。

境川

さて、そんな感じで迷惑かけまくりの波乱の出だしだったんだけど、走り出してしまえば平穏そのもので。
梅雨の間の晴れ間で、気候は穏やか、風もほとんどなく、なんとも気持ちいいサイクリング日和だった。
走りやすい境川沿いの道をゆるゆると進んでいく。

境川といえば、アニメにもなった『ろんぐらいだぁす!』で、主人公の亜美ちゃんが最初にポン太くんで走ったコース。

ろんぐらいだぁす!: 1 (REXコミックス)

ろんぐらいだぁす!: 1 (REXコミックス)

つきみ野(作中だと「ほしみ野」)駅のすぐそばがそのスタート地点で、走ってしばらくしたら、その聖地に到着。
さっそくみんなで撮影大会w

f:id:yamaimo0625:20170622233852p:plain

作中の亜美ちゃんのごとく写真を撮るスタッフさん、の写真を撮る自分w

f:id:yamaimo0625:20170622233919p:plain

ちなみに、ここに来るのは自分は2回目。
前回来たときも、やっぱり写真を撮ったw

余談だけど、このときは『ろんぐらいだぁす!』の聖地巡りをしてて、他にも写真を撮ったので、せっかくだからアップw

https://twitter.com/yappy0625/status/583485302188085248:ebmed

さてさて、閑話休題

写真を撮った後は再出発して、ひたすら境川沿いを進んでいく。
途中、ちょっと迷ったっぽかったけどw

飯田牧場へ

そんなこんなで、ゆるゆる進んでるうちに、あっという間に飯田牧場に到着。
先に進んでいたグループとも合流して、みんなでジェラートを食べながらゆるりと休憩。

f:id:yamaimo0625:20170622235554p:plain

うーん、ここのジェラートはやはり何度食べても美味しいw
特に、気温がけっこう上がってきていたので、冷たいジェラートは最高に気持ちよかったw

サイクルラックにはたくさんのコーダーブルームw

f:id:yamaimo0625:20170622235800p:plain

それにしても、GIGLIOいいなぁ・・・
以前、試乗させてもらったとき、こう、重心がしっかり下にあって安心して乗れる感じがすごくよかった。
ロングライドだと、この安心感は絶対にいいと思うんだよね。
緑色というのも、自分の大好きな色だし。
欲しい・・・

さて、一休みしたあとは、またグループに分かれて出発。
ゴール地点の江ノ島を目指していく。

江ノ島

飯田牧場を出発して、しばらくしたら再び境川沿いの道に戻り、さらに南下。
そして、境川サイクリングロードもいよいよ終わり、国道467号に合流。

と、ここで、左折ではなく右折。
左折して国道467号を進めば江ノ島は目と鼻の先なのに、右折したもんだから、内心穏やかでなかった。
江ノ島からどんどん離れ、小田原の方へ進んでいくw

「これ、道あってますかねぇ・・・?」
「さぁ・・・?」

なんて会話を交わしつつ、ぐんぐん西へと進んでいく。
かなり不安だったんだけど、引地川を渡ったところで左折し、江ノ島方向に進んでいったので、一安心。
おそらく、藤沢駅周辺が混んでいるだろうから、迂回したのかな・・・?

引地川沿いの歩道をゆるゆると走り(思った以上に交通量があった)、国道134号に合流。
あとは、国道134号を東に進んでいけば、江ノ島に到着!

f:id:yamaimo0625:20170625113853p:plain

ゑじま

さっそくお昼ということで、自転車を駐輪場に停め、お店へ。
今回お昼を食べたのは、『ゑじま』さん。
大賑わいの通りから細い裏道に入ったところにひっそりとあって、こんなところにお店があったんだな、とw

自分が注文したのは、5色丼。

f:id:yamaimo0625:20170625115532p:plain

最初、丼の上に乗せるお刺身だけが出てきたのかと思ったw
よく見てみると、お刺身の下にひっそりとご飯がw

すごく美味しかったんだけど、ちょっと物足りなかったかも。
大盛りにしておけばよかったか(^^;

お昼を食べた後は、少しぶらぶらと観光したり、記念撮影をしたり。

f:id:yamaimo0625:20170625120002p:plain

片瀬江ノ島駅

みんなで集合写真を撮った後は、片瀬江ノ島駅へ。
これで今回のオーナーズライド自体は一応完了。

f:id:yamaimo0625:20170625123139p:plain

ただ、走り足りないという人たちは、スタッフさんたちと一緒に町田まで走って帰ってたみたい。

そして、自分はというと、ここからが本番というか、自宅まで自走w
ということで、80km強のサイクリングをスタートw

江ノ島〜戸塚〜横浜

片瀬江ノ島を出発した後は、もう一度江の島入口まで戻り、そこから国道467号へ。
混雑する道を北上し、藤沢駅を越え、藤沢橋まで進んだところで右折して、県道30号を進んでいく。
ここが今回一番の登りだったw

県道30号から国道1号に合流してしまえば、あとはひたすら進んでいくだけ。

ただ、戸塚のあたりがかなりよく分からない。。。
オーバーパスは軽車両進入禁止になっているので、下に降りるしかなく、なんとか戻れないかなと道を探ってみたものの、ダメ。
仕方ないので、戸塚駅の方までぐっと進み、しばらく線路沿いに進んで、県道401号経由で国道1号に復帰するというルートをとった。
戸塚駅のすぐ北のアンダーパスを通れれば簡単なんだけど、そこも軽車両は通れないんだよねぇ・・・
みんな、どんなルートで戸塚周辺を越してるんだろう?

まぁ、ここさえ越えてしまえば、あとは完全に道なり。
少し登ったり下ったりはあるけど、斜度も距離もそんなにないので、気にならない。

そんなこんなで、横浜に到着。

横浜〜川崎〜品川〜秋葉原

このあたりから、暑さのせいか、頭痛がけっこう酷いことに。。。
持ち歩いてたアスピリンを飲んだり、塩飴を舐めたり、頭から水かぶって体を冷やしたりして、なんとか対処しようとするも、あまり改善せず。
水分自体はこまめに補給しているんで、たぶんミネラルのバランスが崩れてるんだろうなぁ・・・
(ネットで見かけて評判がよかった「スーパーマルチビタミン&ミネラル」を、買ってはあるんだけど、持ってくるのを忘れてた)

とりあえず、水を補給しなきゃということで自販機で水を購入。
と、そしたら、なんか当たったw

f:id:yamaimo0625:20170625123123p:plain

こんなの初めて見たよw
(ちなみに、なんかワンピースのキーホルダーが入ってたw)

そんな休憩をしつつ、再び走り出したんだけど、国道1号と国道15号の分岐地点で右折レーンに入りはぐって、あ、右折できない、と、引き返したり(^^;
そんなときに、なんか見覚えのある顔が・・・

なんと、その日一緒に走ってた天野さん(@Gp1Nao)と合流!

同じ方へ進むということだったので、ご一緒させてもらった。

頭痛とかでかなりへばっていたので、まさに天の助けというか。
このあと、ずっと引いてもらって、すごく助けられた。
ホントにありがとうございました。

ということで、天野さんと一緒に、川崎、品川、そして秋葉原へと進んでいった。
そして、天野さんは千葉方面へ向かうということだったので、秋葉原でお別れ。

ちなみに、天野さんは行きも町田まで自走、帰りも自走で、160kmくらい走ってたみたい。
すごい。

秋葉原〜自宅

天野さんのおかげもあって、なんとか秋葉原まで戻ってこれたので、あともうちょっと。

と、その前に、だいぶお腹も減っていたので、ご飯。
カロリーと塩分の補給を兼ねて、すた丼を食べる。

f:id:yamaimo0625:20170625124724p:plain

そうそう、ドンブリっていうのは、こういうのをいうんだよ・・・
たっぷりあるご飯にニンニクの効いたお肉が乗っかってて、めちゃ旨い。
そして、塩分を失った身体に、お味噌汁の旨さが染み渡る。

がっつりと食べて元気を取り戻した後は、国道4号をひたすら北上し、自宅を目指す。

頭痛が治ったわけではないので、国道4号のガタガタ道はかなり頭に響いてキツかったんだけど、よく知ってる道なので、あまり不安は感じず。
事故だけは起こさないように、慎重に進んでいく。

千住大橋を越え、千住新橋を渡り、埼玉に入ればあともう少し。
そして、やっとのことで自宅に到着!

いや〜、無事に走りきれてよかった(^^)

それにしても、やはり頭痛対策は何か考えないとなぁ。
今回に限らず、暑い時期に自転車に乗るといつも頭痛に悩まされるので・・・
塩飴は舐めてるんだけどねぇ。

今日はここまで!

ろんぐらいだぁす!: 1 (REXコミックス)

ろんぐらいだぁす!: 1 (REXコミックス)

『OS自作入門』を読んでみた。(その4)

またまた間が空いてしまったけど、前回の続き。

今日は実際のブートコードの部分を見ていく。

Intel構文とAT&T構文

前回も少し触れた通り、本はNASMに似た筆者作のアセンブラを使っていて、その構文はIntel構文になっている。
一方、自分の使っているGNU asは、AT&T構文で、いろいろ違ってくる。
詳細は以下のページなどを参照:

Linux のアセンブラー: GAS と NASM を比較する

基本的なところを押さえておくと、以下のとおり:

  • オペランドのディスティネーションとソースが逆
    • NASMはディスティネーション、ソースの順
    • GNU asは、ソース、ディスティネーションの順
  • 即値の指定の仕方が違う
    • NASMは数字やラベルをそのまま書く
    • GNU asは数字やラベルの前に$をつける
  • レジスタの指定の仕方が違う
  • 数字やラベルによるメモリの参照の仕方が違う
    • NASMは数字やラベルを[]で囲う
      バイト幅の指定が必要な場合、[]の前にbyte(1-byte)、word(2-byte)、dword(4-byte)をつける
      なお、NASMは数字やラベルの前にbyte ptr(1-byte)、word ptr(2-byte)、dword ptr(4-byte)をつけるのでもOKっぽい(※)
    • GNU asは数字やラベルをそのまま書き、バイト幅を示すためにオペコードにb(1-byte)、w(2-byte)、l(4-byte)をつける
  • レジスタによるメモリの参照の仕方が違う
    • NASMは
      [セグメントレジスタ:ベースレジスタ + インデックスレジスタ * スケーラ + オフセット]
      とする(使わないところは省略可能)
      (セグメントレジスタ x 16 + ベースレジスタ + インデックスレジスタ x スケーラ + オフセット)を参照することになる
    • GNU asは
      セグメントレジスタ:オフセット(ベースレジスタ, インデックスレジスタ, スケーラ) とする(使わないところは省略可能)
      (セグメントレジスタ x 16 + ベースレジスタ + インデックスレジスタ x スケーラ + オフセット)を参照することになる

※上記のIBMのページだとこう書かれているけど、なんか怪しい・・・

なお、NASMに関してと、レジスタによるメモリの参照は、ネット上の資料を漁っただけなので、正しくないかも・・・
(しっかりした資料が見つかってないので、自信がない・・・ちなみに、これを調べてたせいで更新が遅くなった)

16ビットモード

まず、x86のCPUには、16ビットモード(リアルモード)と32ビットモード(プロテクトモード)がある。
起動した直後は16ビットモードになっているので、アセンブラで出力されるコードも16ビットモード用のものになっていないと困る。

そこで、次のように、擬似命令を使ってこのコードが16ビットモード用のものであることを示してやる:

    // 16-bit code
    .code16

ブートコード

そして、前回見た通り、ジャンプコード、BIOSパラメータブロックが続いて、そのあとにブートコードが来ることになる。
ジャンプコードでは、このブートコードの先頭に飛んでくるようにジャンプを行なっている。

最初にやっているのは、レジスタの初期化。

entry:
    mov     $0, %ax
    mov     %ax, %ss
    mov     $0x7c00, %sp
    mov     %ax, %ds
    mov     %ax, %es
    // 続く

まずアキュムレータレジスタ(ax)を0で初期化し、さらにスタックのセグメントレジスタ(ss)、データのセグメントレジスタ(ds)、追加のセグメントレジスタ(es)をaxを使って0に初期化。
そして、スタックポインタ(sp)を0x7c00で初期化している。

ここからは文字列の出力。

まず、文字列が置かれているmessageというラベルのアドレスをソースインデックスレジスタ(si)に代入。

    // 続き
    mov     $message, %si
    // 続く

アドレスの内容ではなく、アドレスの値をそのまま入れるので、即値になるように$をつける必要があるのに注意。
(最初、つけるのを忘れて動かなかった)

そしたら、siの指す内容をaxの下位8ビット(al)に読み込む。

    // 続き
putloop:
    movb    (%si), %al
    // 続く

最初はsiの値はmessageのアドレスになっているので、改行文字¥nが読み込まれることになる。
そして、次々と読み込んで出力したいので、siの値を1つずつ増やしていくことになる。

    // 続き
    add     $1, %si
    // 続く

ここで、もし読み込んだ文字がヌル文字¥0だった場合、文字列の終端になっているので、ループを抜け出すことになる。

    // 続き
    cmp     $0, %al
    je      fin
    // 続く

あとは、実際に文字を出力する処理。
これにはBIOSAPIを利用する。

    // 続き
    mov     $0x0e, %ah
    mov     $15, %bx
    int     $0x10
    // 続く

決められたレジスタに値をセットして、ソフトウェア例外を起こしている(int命令)。
これで制御がBIOSに移って、文字を出力したらまた戻ってきてくれる。

最後に、次の文字を出力するために、ループの先頭に戻る。

    // 続き
    jmp     putloop
    // 続く

一つ一つ読み解くと、こんな感じ。

Cで書いてみると、次のようなイメージ:

char* si = message;
while (1)
{
    char al = *si;
    si++;
    if (al == '¥0')
    {
        break;
    }
    putchar(al);
}

文字列の出力が終わったら、あとは無限ループ。

    // 続き
fin:
    hlt
    jmp     fin

hlt命令はCPUを休止状態にする命令で、例外が起きるまでCPUを休ませておいてくれる。


さて、これでブートコードも見たわけだけど、実はこれではまだ正しく動かなかったり。
というのも、ブートセクタはBIOSによってメモリの0x7c00に読み込まれるのだけど、このコードだと先頭が0x0000だと思ってリンクが解決されてしまうから。
なので、アドレスが正しくなっていない。

このアドレスの問題を解決するには、もう一工夫必要になってくる。
それについては、また次で。

今日はここまで!

30日でできる! OS自作入門

30日でできる! OS自作入門

『OS自作入門』を読んでみた。(その3)

前回からだいぶ間が空いてしまったけど、続き。

前回は擬似命令を使ってただデータを作っただけだったけど、それではアセンブラを使ったとは言い難いので、今回はちゃんとしたアセンブラを書いてみる。

ちゃんとしたアセンブラのコード

と言うことで早速、ちゃんとしたアセンブラのコード。

    .file   "hello-os.s"

    // 16-bit code
    .code16

    // partition boot record (1sector)
pbr:
    // jump to boot code
    jmp     entry
    nop

    // OEM name (8byte)
    .ascii  "HELLOIPL"

    // BIOS Parameter Block (FAT12/FAT16)
    .short  512                 // bytes per sector
    .byte   1                   // sectors per cluster
    .short  1                   // reserved sectors
    .byte   2                   // number of file allocation table
    .short  224                 // root directory entries
                                // (32byte/entry * 224entry = 7168byte = 512byte/sector * 14sector)
    .short  2880                // total sectors
    .byte   0xf0                // media type (0xf0: floppy disk, 0xf8: hard disk)
    .short  9                   // sectors per file allocation table
    .short  18                  // sectors per track
    .short  2                   // number of heads
    .int    0                   // hidden sectors
    .int    2880                // large total sectors
    .byte   0x00                // physical disk number
    .byte   0x00                // current head
    .byte   0x29                // extended boot signature (0x29 means DOS 4.0 EBPB)
    .int    0xffffffff          // volume serial number
    .ascii  "HELLO-OS   "       // volume label (11byte)
    .ascii  "FAT12   "          // file sytem type (8byte)

    .skip 18, 0x00

entry:
    mov     $0, %ax
    mov     %ax, %ss
    mov     $0x7c00, %sp
    mov     %ax, %ds
    mov     %ax, %es
    mov     $message, %si
putloop:
    movb    (%si), %al
    add     $1, %si
    cmp     $0, %al
    je      fin
    mov     $0x0e, %ah
    mov     $15, %bx
    int     $0x10
    jmp     putloop
fin:
    hlt
    jmp     fin

message:
    .string "\n\nhello, world\n"

    .org    0x0200 - 0x02
    .byte   0x55, 0xaa  // boot signature

    // file allocation table (9sectors = 512byte/sector * 9sector = 4608byte)
fat:
    .byte   0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    .skip   0x200 * 9 - 8, 0x00

    // file allocation table (copy)
fat_copy:
    .byte   0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    .skip   0x200 * 9 - 8, 0x00

    // root directory entries (14sectors = 512byte/sector * 14sector = 7168byte)
root_directories:
    .skip   0x200 * 14, 0x00

    // data area (2847sectors = 2880 - 1 - 9 * 2 - 14)
files:
    .skip   0x200 * 2847, 0x00

本のコードを参考にしてるけど、だいぶ違ってる。
というのも、まず本はNASMに似たnaskという著者作のアセンブラを使っていて、Intel構文だけど、自分はGNU asを使っているので、AT&T構文になっているから。
それに、いろいろ分からなかったことを調べた結果が盛り込まれているというのもある。

FATファイルシステム

まず、FATファイルシステムについてから。

フロッピーディスクはセクタと呼ばれる区画で区切られていて、セクタ単位でデータを読み書きする。
1つのセクタが512バイトで、フロッピーディスクの場合、2880セクタ用意されている。
このセクタがディスク上にどう並んでいるのかはあとで扱うとして、論理的にはセクタが配列のように一列に並んでいると考えておいていい。
そう考えたときに、このセクタの配列は、FATフォーマットではいくつかの領域に分けられる:

  • ブートセクタ
    PCが起動したときにBIOSによってメモリにロードされて実行されるセクタ
  • FAT(File Allocation Table)領域
    クラスタ(1つ以上のセクタの集まり)の情報を管理するための領域
  • ルートディレクトリ領域
    ルート直下のディレクトリの情報が置かれる領域
  • データ領域
    各ファイルの情報が置かれる領域

上のコードだと、以下のように分けられている:

pbr:

    // ブートセクタ

fat:

    // FAT領域

root_directories:

    // ルートディレクトリ領域

files:

    // データ領域

ブートセクタは1セクタ、FAT領域は1つのFile Allocation Tableが普通9セクタで、冗長化でコピーが用意されているので合計18セクタ、ルートディレクトリ領域はFAT12だと普通は14セクタ、そして、残りがデータ領域となるので、データ領域は2847セクタということになる。

Cでイメージを書いておくと、以下のような感じ:

// セクタは512バイトのデータ
typedef struct sector_ {
    unsigned char data[512];
} sector_t;

// フロッピーディスク
typedef union floppy_ {
    // 2880セクタからなる
    sector_t sectors[2880];

    // 領域
    struct {
        // ブートセクタ
        sector_t boot_sector;

        // FAT領域
        struct {
            // File Allocation Table
            sector_t fat[9];

            // File Allocation Table (copy)
            sector_t fat_copy[9];
        } fat_area;

        // ルートディレクトリ領域
        sector_t root_directories_area[14];

        // データ領域
        sector_t data_area[2847];
    } areas;
} floppy_t;

アセンブラのコードを見てみると、今のところちゃんとしたデータが置いてあるのはブートセクタだけで、他の領域は.skipを使ってほぼ0にしていることが分かると思う。
これは、今書いているのがブート時に読み込まれて実行されるプログラムで、他にファイルをディスク上に作っていないから。

ブートセクタ

さらに、ブートセクタを見てみる。

パーティションが1つの場合、ブートセクタはただ1つなんだけど、ハードディスクとかだと複数のパーティションに分かれている場合があって、その場合、各パーティションの1つめのセクタが論理的にブートセクタになる。
そこで、ディスクの先頭にあるブートセクタを、マスターブートレコードMBR)、各パーティションの先頭にあるブートセクタをパーティションブートレコード(PBR)と言ったりする。
フロッピーディスクの場合、パーティションは分けられていない(=パーティションは1つだけ存在する)ので、MBRがそのままPBRになっている。

そして、MBRとPBRでは微妙に構造が違っていて、MBRはセクタの最後の方にパーティションの情報を持つパーティションテーブルというのが置かれることになっている。
もちろん、フロッピーディスクの場合、パーティションテーブルは不要なので、構造的にはPBRになっている。
ということで、ラベルはmbrではなくpbrにしている。

    // partition boot record (1sector)
pbr:
    ...

PBR(およびMBR)は、以下のような構造になっている:

  • ジャンプコード (3byte)
    以下のデータを飛び越して実際のブートコードに行くためのジャンプ命令+α
  • OEM (8byte)
    ブートセクタの名前とか
  • BIOSパラメータブロック
    ディスクの情報をまとめた領域
  • ブートコード
    ブート処理を行うコード
  • パーティションテーブル (64byte) ※MBRのみ
    パーティションの情報
  • ブートシグニチャ (2byte)
    このセクタがブートセクタであることを示す

まずは、ジャンプコード。

    // jump to boot code
    jmp     entry
    nop

見ての通り、実際のブート処理を行うコード(entry以下)へジャンプを行なっている。
なお、ジャンプ命令には2byteのものと3byteのものがあるので、データの位置を揃えるために、2byteのジャンプ命令の後にはnop命令を入れている。
(本だとニーモニックnopとは書かず、直接機械語DB 0x90と書いている)

次はOEM名で、特に書くことなし。

    // OEM name (8byte)
    .ascii  "HELLOIPL"

その次がBIOSパラメータブロック。

    // BIOS Parameter Block (FAT12/FAT16)
    .short  512                 // bytes per sector
    .byte   1                   // sectors per cluster
    .short  1                   // reserved sectors
    .byte   2                   // number of file allocation table
    .short  224                 // root directory entries
                                // (32byte/entry * 224entry = 7168byte = 512byte/sector * 14sector)
    .short  2880                // total sectors
    .byte   0xf0                // media type (0xf0: floppy disk, 0xf8: hard disk)
    .short  9                   // sectors per file allocation table
    .short  18                  // sectors per track
    .short  2                   // number of heads
    .int    0                   // hidden sectors
    .int    2880                // large total sectors
    .byte   0x00                // physical disk number
    .byte   0x00                // current head
    .byte   0x29                // extended boot signature (0x29 means DOS 4.0 EBPB)
    .int    0xffffffff          // volume serial number
    .ascii  "HELLO-OS   "       // volume label (11byte)
    .ascii  "FAT12   "          // file sytem type (8byte)

ここが正直大変だったところで、本だと「その値にしておくものだから」で片付けられている値がけっこうある(^^;
しかも、ネットで調べても仕様がハッキリしない記述が多くて、かなり困った。

以下のwikipedia(en)の記述が結構役に立った感じ:

BIOS parameter block - Wikipedia

ここから18byteほど隙間を空けて、適当にアラインを揃えたところから、実際のブートコードは始まっている。
その詳細はまた後で。

そして、ブートセクタの一番最後にあるのが、ブートシグニチャ

    .org    0x0200 - 0x02
    .byte   0x55, 0xaa  // boot signature

ブートセクタの最後の方までスキップさせるために、.orgを使っている。
この擬似命令は、ファイルの先頭のアドレスを0として、引数で指定したアドレスまでスキップをしてくれる。
ということで、1セクタが512(= 0x200)バイトなので、そこからブートシグニチャの2バイトを引いたところまで、一気にスキップ。
そして、ブートシグニチャ0x55, 0xaaを書き込んでいる。
BIOSはこのブートシグニチャが入っていないセクタはブートセクタとして扱わないらしい。

さて、あとは肝心のブートコードの部分だけど、けっこう長くなったので、一旦区切り。

今日はここまで!

30日でできる! OS自作入門

30日でできる! OS自作入門