30日でできる予定のOS自作入門ですが、やっと4日目が終わった・・、正確には読み終わった、だけど、理解度は3日目だ。
自慢じゃないが、30日で終わらない自信がたっぷりある。
そんな訳ですが、まずは
アセンブラの関数で遊んでみる
著者の川合さんが作ったアドレスを指定する関数を使ってちょっと遊んでみる。
遊んでみるというよりも、ちょっと動作確認みたいな感じです。
ソース
#include <stdio.h> extern void _write_mem8(int addr, int data); void func(void) { static int num_1 = 10; char *num_2; // num_1のアドレス printf("%p\n", &num_1); // num_1には10が入っている printf("%d\n", num_1); // _write_mem8で直接アドレスを指定して20にする _write_mem8(&num_1, 20); // 20になってるはず printf("%d\n", num_1); // _write_mem8で直接アドレスを指定して30にする _write_mem8(0x80496e8, 30); // 30になってるはず printf("%d\n", num_1); } int main(void) { func(); return(0); }
実行結果
0x80496e8 10 20 30
思った動作をしてくれました。
では続いていきましょうか。
p[i] と *(p + i)の話って出てくるの3回目だっけ??
いつもぼけーっとしながら本を読みながらも、p[i] i[p] *(p + i) の話は毎回の教本に出てきていると思うんだ。
だが、今回のOS自作入門についてはちょっと違っていた。
じゃなくて、違っていたんじゃなくて、やっとたけけんが考えられる知識が備わったのかもしれない。
では配列と言えば領域違反なので、違反するプログラムを実行して*(p + i)に書き直してみる
元ソース
#include <stdio.h> void messages(void) { printf("hello flow\n"); } void func(void) { int i, a[1]; for(i = 2; i < 10; i++){ printf("%p\n", a[i]); a[i] = (int)messages; } } int main(void) { func(); return(0); }
書き直したソース ループの範囲が違うのは途中のポインタで止まるからだす。
#include <stdio.h> void messages(void) { printf("hello flow\n"); } void func(void) { int i, *a; for(i = 3; i < 10; i++){ printf("%p\n", *(a + i)); *(a + i) = (int)messages; } } int main(void) { func(); return(0); }
実行結果
0xd9ecd0 0xda01b0 0xd9efd0 0x59ec50 0xd9f9c0 0xd9fce0 0xe4de80 hello flow hello flow
うん、うまく動作しているんだけど、この結果を見てたけけんの頭が崩壊した。
分かったようで、分かってなかったんだなぁとバグった頭で頑張って考えた。
サブタイトルにもしたんだけど、この話が出てくるのは3回目だと思うんだ。
なので前回でてきたポインタ完全制覇を取り出して、ササっと読み返したぞ。
宣言の[]と式中の[]は違う、ついでに宣言の*と式中の*も違う これってCを宣言を分かりにくくしているYO
という事だった。なるほど!
っていうか違うということにすら気づいてなかった。
CのAも分かってなかったんだなぁ。。
どういう事かというと日本語は難しいという事だ。
ここでちょっとテストしてみる。
#include <stdio.h> int main(void) { int i, *a; for(i = 0; i < 10; i++){ printf("a[%d]=%p\n", i,a[i]); } for(i = 0; i < 10; i++){ printf("*(a + %d)=%p\n", i,*(a + i)); } return(0); } a[0]=0x192d7c a[1]=(nil) a[2]=(nil) a[3]=0x275cd0 a[4]=0x2771b0 a[5]=0x275fd0 a[6]=0x9fac50 a[7]=0x2769c0 a[8]=0x276ce0 a[9]=0x324e80 *(a + 0)=0x192d7c *(a + 1)=(nil) *(a + 2)=(nil) *(a + 3)=0x275cd0 *(a + 4)=0x2771b0 *(a + 5)=0x275fd0 *(a + 6)=0x9fac50 *(a + 7)=0x2769c0 *(a + 8)=0x276ce0 *(a + 9)=0x324e80 #include <stdio.h> int main(void) { int i, a[3]; for(i = 3; i < 10; i++){ printf("a[%d]=%p\n", i,a[i]); } for(i = 3; i < 10; i++){ printf("*(a + %d)=%p\n", i,*(a + i)); } return(0); } a[3]=0x3 a[4]=0x8048460 a[5]=(nil) a[6]=0xbfa64558 a[7]=0x23cd26 a[8]=0x1 a[9]=0xbfa64584 *(a + 3)=0x3 *(a + 4)=0x8048460 *(a + 5)=(nil) *(a + 6)=0xbfa64558 *(a + 7)=0x23cd26 *(a + 8)=0x1 *(a + 9)=0xbfa64584
うむ、満足した。
それと、今まで何の気もなしに見ていたエラーメッセージで
「error: 添字をつけられた値が配列でもポインタでもありません」
ってのがあるんだが、これはつまりどっちでも一緒という事だったんだろう。
では、この辺でOS自作入門に戻ろうとしよう。
おそらく著者は式中の[]について述べているのだな。
ちょっと引用すると
p[i]はPという配列のi番目という説明はごまかし
うん、なるほど。こんどは分かったよ。
じゃあ、アセンブラの配列はどうなるなるの?と単細胞なたけけんは思っちゃうのだが、配列といえば、同じ型のデータがメモリに並んで入るものだったはずなので、ようするにつまりはそういう事なのだね。
今までの勉強内容がいろいろ交差してきてなかなか楽しいもんだ。
運命の交差点だ。
それから2ページほど読み進めたら、さっきの配列がどうのこうのという部分の答えが
こっちにも書いてあった。
疑問に思ったらそこで立ち止まって考えてしまうのは自分の悪い癖でござるでござる。
書かれていたのがどういうものだったかと言うと
char num[3];
というのを例として、CでいうとChar型の宣言で文字列を入れるもん。
となるけれど、アセンブラだとBYTE用の番地だよと。
という事のようだ。なるる。
続いて、numは定数。アセンブラでいうラベル。
ラベルの値は、もちのろんで番地を意味している。
JMPとかCALLでジャンプしている先頭のアドレスだ。
ってことはポインタでもある。
混乱していた頭もだいぶ落ち着いてきた。
うしろの3はRESB 3 となる。
訳すると、3byteの初期化されないデータ領域で、最後のBはByteを意味している。
やっぱりC言語やってよかったよ!ロベルト!
驚きなのは著者の川合さんは関数を全部アセンブラで作ってしまうので
何もincludeしないのだよね。
何もincludeしないCのプログラムは新鮮だ。
たけけんの脳味噌ではこの本だけでアセンブラを勉強するのはちょっと厳しいので、アセンブラの勉強にもう1個だけ本でもウェブでもどっちかで別のもので勉強をしようと思います。
ちょっと読んだだけだけど、VRAMは相当難しそうだなぁ。
でも多分ナウい言語だったらその辺の関数がきっと用意されているはずだ。
・・と予想してみる。
当たったらなんかほしいものでも買うか。