khaba/memo001
の編集
http://k.osask.jp/wiki/?khaba/memo001
[
リロード
] [
新規
|
編集
|
差分
|
添付
] [
トップ
|
一覧
|
単語検索
|
最終更新
|
バックアップ
|
ヘルプ
]
-- 雛形とするページ --
2012_09
ADSL
ADSL1
BarbaraDye@protonmail.com
Books
DVD-R
EPIA_series
EPM
FormatRule
FrontPage
Help
HomeBakery
HomeBakery2016
InterWikiName
InterWikiSandBox
InterWikiテクニカル
K
KHBIOS/0001
KHBIOS/0002
Lojban
MenuBar
OSA/imp060524
OSC_rule
PASS3
PASS3v1
PukiWiki
RecentDeleted
SandBox
SitePolicy
WattChecker
bank
banks
blogs
blogs2
boyaki_a
boyaki_a/00001
boyaki_a/00002
boyaki_a/00003
boyaki_a/00004
boyaki_a/00005
boyaki_a/00006
boyaki_a/00007
boyaki_a/00008
boyaki_a/00009
boyaki_a/00010
boyaki_a/00011
boyaki_a/00012
boyaki_a/00013
boyaki_a/00014
boyaki_a/00015
boyaki_a/00016
boyaki_a/00017
boyaki_a/00018
boyaki_a/00019
boyaki_a/00020
boyaki_a/00021
boyaki_a/00023
boyaki_a/00024
boyaki_a/00025
boyaki_a/00026
boyaki_a/00027
boyaki_a/00028
boyaki_a/00029
boyaki_a/00030
boyaki_a/00031
boyaki_a/00032
boyaki_a/00033
boyaki_a/00034
boyaki_a/00035
boyaki_a/00036
boyaki_a/00037
boyaki_a/00038
boyaki_a/00039
boyaki_a/00040
boyaki_a/00041
boyaki_a/00042
boyaki_a/00043
boyaki_a/00044
boyaki_a/00045
boyaki_a/00046
boyaki_a/00047
boyaki_a/00048
boyaki_a/00049
boyaki_a/00050
boyaki_a/00051
boyaki_a/00052
boyaki_a/00053
boyaki_a/00054
boyaki_a/00055
boyaki_a/00056
boyaki_a/00057
boyaki_a/00058
boyaki_a/00059
boyaki_a/00060
boyaki_a/00061
boyaki_a/00062
boyaki_a/00063
boyaki_a/00064
boyaki_a/00065
boyaki_a/00066
boyaki_a/00067
boyaki_a/00068
boyaki_a/00069
boyaki_a/00070
boyaki_a/00071
boyaki_a/00072
boyaki_a/00073
boyaki_a/00074
boyaki_a/00075
boyaki_a/00076
boyaki_a/00077
boyaki_a/00078
boyaki_a/00079
boyaki_a/00080
boyaki_a/00081
boyaki_a/00082
boyaki_a/00083
boyaki_a/00084
boyaki_a/00085
boyaki_a/00086
boyaki_a/00087
boyaki_a/00088
boyaki_a/00089
boyaki_a/00090
boyaki_a/00091
boyaki_a/00092
boyaki_a/00093
boyaki_a/00094
boyaki_a/00095
boyaki_a/00096
boyaki_a/00097
boyaki_a/00098
boyaki_a/00099
boyaki_a/00100
boyaki_a/00101
boyaki_a/00102
boyaki_a/00103
boyaki_a/00104
boyaki_a/00105
boyaki_a/00106
boyaki_a/00107
boyaki_a/00108
boyaki_a/00109
boyaki_a/00110
boyaki_a/00111
boyaki_a/00112
boyaki_a/00113
boyaki_a/00114
boyaki_a/00115
boyaki_a/00116
boyaki_a/00117
boyaki_a/00118
boyaki_a/00119
boyaki_a/00120
boyaki_a/00121
boyaki_a/00122
boyaki_a/00123
boyaki_a/00124
boyaki_a/00125
boyaki_a/00126
boyaki_a/00127
boyaki_a/00128
boyaki_a/00129
boyaki_a/00130
boyaki_a/00131
boyaki_a/00132
boyaki_a/00133
boyaki_a/00134
boyaki_a/00135
boyaki_a/00136
boyaki_a/00137
boyaki_a/00138
boyaki_a/00139
boyaki_a/00140
boyaki_a/00141
boyaki_a/00142
boyaki_a/00143
boyaki_a/00144
boyaki_a/00145
boyaki_a/00146
boyaki_a/00147
boyaki_a/00148
boyaki_a/00149
boyaki_a/00150
boyaki_a/00151
boyaki_a/00152
boyaki_a/00153
boyaki_a/00154
boyaki_a/00155
boyaki_a/00156
boyaki_a/00157
boyaki_a/00158
boyaki_a/00159
boyaki_a/00160
boyaki_a/00161
boyaki_a/00162
boyaki_a/00163
boyaki_a/00164
boyaki_a/00165
boyaki_a/0022
data/Clover
data/Clover/hrb_A
data/Clover/hrb_Clover
data/Clover/mail0000
data/Clover/mail0001
data/Clover/mail0002
data/Clover/mail0003
data/Clover/mail0004
data/Clover/mail0005
data/Clover/others
dev-j/THE-BBL/nanasi
ideas
ideas/s7st
ideas/tek3
ideas/tek5
impressions
index
isolations/osw_vga
k
k_in_TOMAMI
kclib1_0000
kclib1_0001
kclib1_0002
kclib1_0003
kclib1_0004
keng
khaba/memo001
khaba/memo002
khaba/memo003
khaba/memo004
khaba/memo005
khaba/memo006
khaba/memo007
klog
klog/comment03
klog/comment04
klog/comment05
klog/essay050
klog/essay051
klog/essay052
klog/essay053
klog/essay054
klog/essay055
klog/essay056
klog/essay057
klog/essay058
klog/essay059
klog/essay060
klog/essay061
klog/essay062
klog/essay063
klog/essay064
klog/essay065
klog/essay066
klog/essay067
klog/essay068
klog/essay069
klog/essay070
klog/essay071
klog/essay072
klog/essay073
klog/essay074
klog/essay075
klog/essay076
klog/essay077
klog/essay078
klog/essay079
klog/essay080
klog/essay081
klog/essay082
klog/essay083
klog/essay084
klog/essay085
klog/essay086
klog/essay087
klog/essay088
klog/essay089
klog/essay090
klog/essay091
klog/essay092
klog/essay093
klog/essay094
klog/essay095
klog/essay096
klog/essay097
klog/essay098
klog/essay099
klog/essay100
klog/essay101
klog/essay102
klog/essay103
klog/essay104
klog/essay105
klog/essays
klog/gfghh
klog/monologue0312
klog/monologue0401
klog/monologue0402
klog/monologue0403
klog/monologue0404
klog/monologue0405
klog/monologue0406
klog/monologue0407
klog/monologue0408
klog/monologue0409
klog/monologue0410
klog/monologue0411
klog/monologue0412
klog/monologue0501
klog/monologue0502
klog/monologue0503
klog/monologue0504
klog/monologue0505
klog/monologue0506
klog/monologue0507
klog/monologue0508
klog/monologue0509
klog/monologue0510
klog/monologue0511
klog/monologue0512
klog/monologue0601
klog/monologue0602
klog/monologue0603
klog/monologue0604
klog/monologue0605
klog/monologue0606
klog/monologue0607-12
klog/old1010
klog/oldk00
krdm0000
krdm0001
krdm0002
krdm0003
links
links/pc0000
links/prog0000
links/soft0000
math
math/00
math/01
math/02
math/03
math/04
math/05
math/06
math/07
math/08
math/09
math/10
mc
memo0001
memo0002
memo0003
memo0004
memo0005
memo0006
memo0011
memo0012
memo0013
memo0014
memo0015
memo0016
memo0017
memo0018
memo0019
memo0020
memo0020/old
memo0021
memo0022
memo0023
memo0024
memo0025
memo0026
memo0027
memo0028
memo0029
memo0030
memo0031
memo0032
memo0033
memo0034
memo0035
memo0036
memo0037
memo0038
memo0039
memo0040
memo0041
memo0042
memo0043
memo0044
memo0045
memo0046
memo0047
memo0048
memo0049
memo0050
memo0051
memo007
memo008
memo009
memo010
memo_dos
memo_opera
minimemo
miniquestions
nask/guide000
nask/guide001
notice
osalinks
osask_khb/memo001
osask_khb/memo002
oversampling
p2018
p20181020
p20181021a
p20181023a
p20181024a
p20181026a
p20181026b
p20181026c
p20181102a
p20181115a
p20181127a
p20181208a
p20181214a
p20190119a
p20190122a
p20190126a
p20190129a
p20190131a
p20190201a
p20190201b
p20190206a
p20190206b
p20190208a
p20190209b
p20190213a
p20190218a
p20190225a
p20190306a
p20190513a
p20190524a
p20190528a
p20190917a
p20191006a
p20191025a
p20191030a
p20191122a
p20191125a
p20191126a
p20191226a
p20200109a
p20200221a
p20200309a
p20200315a
p20200423a
p20200513a
p20200808a
p20200821a
p20211014a
p20211017a
p20211028a
p20211223a
p20220106a
pcmemo
physics
populars
prog/01
prog/02
prop/WaseiOs
quake
quake/jsedip
quake/jsedip/data
quake/jsedip/data05
rep_20061028
rep_OSC06_niigata
sam
sdk0000
sdk0001
sdk0002
sdk0003
sdk0004
spam/hrbwiki/rule
spam/kkiwi/boyaki_a
spam/oswiki/ASKA
spam/oswiki/VGA
spam/oswiki/qemu
spam/test
spysee
test_kor
travel
urls
videochips
ヘルプ
整形ルール
練習用ページ
20212021
* khabaのメモ -(by [[K]], 2007.02.02) *** (0) -これは誰が作るの? ・・・誰が作ってもいいが、多分誰も作ってくれないと思うので、自分で作る予定。細部の説明をするよりも自分で作るほうが早そうだし。 -でも僕よりももっとうまく作る人がいるかもしれないし、作りたい人が他にいたらそれを妨げる理由は何もないので、是非勝手に(=断りなく)やってください。 *** (1) 動機・方針 -GBA(GameBoyAdvance)で動くOSASKを作りたい。でもそしたらx86用OSASKとバイナリ互換はできない(当たり前)。ソース互換ならできるかもしれない。 -しかし仮にソース互換にするにしても、エンディアンの問題とかで、完全互換はできないかもしれない(ARM7だけならx86との間にエンディアンの問題は起きないかもしれないが、いずれは他のCPUのことだって考えたい)。それに人によってはソースを公開するのはいやかもしれない。 -それなら中間コード(いわゆるVMのバイトコード)みたいなものを考えて、それを実行時に各CPUのネイティブコードに変換すればいいのではないか。これならエンディアンとかの問題はない。CPUのビット数が違って機種によってint幅が異なる、という問題も起こさずに済ませられる。 -これをkhabaとする。 ---- -結局khabaがあるとどんなことができるようになるの?(ここだけですます調) --khabaはjavaや.netのように、どんなCPUにも適合するようなプログラムを作るためのシステムです。javaや.netよりもいいのは、CPUへの要求がもっとずっと少なくなるように工夫して設計されていることです。16bit-CPUや8bit-CPU、PICマイコンみたいな組み込み系のCPUであってもサポートできるように努力しています。実CPUのbit数とプログラムのint幅は完全に独立しており、たとえ16bit-CPU上のkhabaで32bitのintを使うことができますし、演算結果についても32bit-CPU上で実行した場合と差異はありません。逆に64bit-CPU上で16bitのintを扱うこともできます。また実CPUが8bitの場合に、8bitもあれば十分な演算をわざわざ32bitで処理して遅くなるということもありません。khabaはアプリだけではなくOSも記述できることを目指していますし、khabaで書かれたOSASKも用意するつもりです。khabaアプリを実行する際にはCPUを選びませんが、もちろんOSも選びません。 --khabaのプログラムをjavaや.netに変換することができます。javaや.netのコードをkhabaにすることもできると思います。 --khabaを動かすCPUには以下の機能がなくても基本的には問題ありません(これらがないCPUであっても、まるでこれらの機能を有するCPUであるかのように扱えるし、それによって速度が目に見えて落ちるということもおそらくない)。 ---ページング・セグメンテーション・保護例外・キャッシュ制御(キャッシュ制御はいりませんが、少量の速い普通のRAMはあったほうがいいです。これすらない場合は、残念ながら速度が目に見えて低下します。メモリマップ上の特定のアドレス範囲のRAMは他よりも高速、ということで十分です)・動的な分岐予測・カーネルモード/ユーザモードの切り替え、PUSH/POPやCALLなどのスタック操作命令群、長さの異なるレジスタ(EAXをALやAHに分解して使えること)、レジスタの汎用化(汎用レジスタがなくていい=つまりデータレジスタ/アドレスレジスタの区別があっていい)、割り込み(ハードウェア割り込みとソフトウェア割り込みの両方)、マルチタスク支援、アドレス空間のサイズ拡大(バンク切り替え方式のメモリでもリニアマップ方式のメモリでもかまわないという意味) --もしkhabaを動かせるCPUを作るのならこれらの機能は実装しないで、その分ロジックを小さくし、以下の面での性能向上を図るべきです。 ---レジスタ数を増やす、消費電力を下げる、マルチコア度を上げる、内蔵高速メモリの容量(キャッシュの代わりになる)を増やす、DMAをつける、SIMD命令をつける(レジスタ-レジスタ間演算のみでかまわない) --タスクセーブができます。セーブしたタスクを異なるOS・異なるCPUで再開可能です(だからもしかしたら分散OS向き?)。タスクセーブ後にプログラムを少しいじって、その後に修正されたプログラムに対して(つまり変数値などはそのままで)再開することも可能です。IA-32用OSASKではここまでの再開能力は想定していませんでした。 --メモリの利用状況を細かく監視できるため、メモリダンプを見る際に、ここは○×構造体、みたいなのがすべて分かります。同じことはファイルにも適用され、バイナリファイルでフォーマットが分からないということはまずないですし、フォーマット変換も自動で可能です。「UNIXという考え方」では、バイナリで出力するなんてよくない、ASCIIとかにするべきだという話があったように思いますが、khabaを前提にしてもいいのなら、むしろCPUへの負荷の多い上にバイト数もかさむASCIIにしろだなんて、ナンセンスもはなはだしいです。 *** (2) ターゲット(目的)・最適化 -khabaでは最適化に執着しない。プログラムの2割の部分が実行時間の8割を占めるといわれるが(人によっては1-9ともいうが)、khabaが目指しているのはこのコードのうちの残りの8割の部分の移植の手間をなくすことである。速度が求められる2割の部分については、それぞれのCPU向けのコンパイラ(やアセンブラ)で最適なコードを生成すればよい(つまり手作業で移植する)。もちろんプログラム全体をkhabaだけで書くことはできるが、その場合、実行速度には不満が残るかもしれない。しかしそれは今検討するべき問題ではないと考える。 -khabaは、ネイティブコードと簡単に(=少ないロスで)交信できる。関数とかも気軽に呼べる。これが担保できないと、上記理想が実現できない。 *** (3) -khabaはスタックマシンではなく、レジスタマシンである。総レジスタ数は128個か、もしくはそれより多くする(上限を設けないかもしれない)。レジスタのビット数も規定されない。レジスタを使い始める前に、そのレジスタを何ビットレジスタとして使いたいのかを宣言する。宣言は1bitでも2bitでも27bitでもよい。この宣言には、最低ビット数と最大ビット数がある。たとえば最低ビット数8、最大ビット数指定なし、の場合、8bit以上のレジスタ(もしくはメモリ)が割り当てられると仮定してよい。 for (i = 0; i < 100; i++) { hoge...; } のiのために割り当てるレジスタであれば、まさにこのような指定でいいわけである。符号なしで扱うなら7bitで十分だ。 -レジスタマシンにしたのは、khabaをネイティブコードに変換するプログラムに楽をさせ、しかもネイティブコードとの交信をやりやすくするため。たとえばIA-32の場合、レジスタ番号0は、EAXに割り当てられる。レジスタ番号1はECX(本当にEAXやECXにするかどうかは未定。翻訳をやりやすくするため、EAXとかをリザーブにする可能性もあるから・・・MUL/DIV命令やIN/OUT命令を考えると、EAXでしかできないことが結構あり、もしEAXがレジスタ0として利用可能だとすると、DIVをPUSH(EAX);、POP(EAX);ではさまないといけない。リザーブしておけば、EAXは常に破壊可能と想定していいことになるから、このようなPUSH/POPはいらなくなる)。 -レジスタ番号8番以降はIA-32には割り当てるべきレジスタが存在しない。これはメモリに割り当てられる。リザーブレジスタがあれば、8番よりももっと若い番号でもメモリに割り当てられることになりそうだ。何番からメモリ行きになるかは、動的に決まるわけではない。khaba/IA-32仕様に記載され、確定する。レジスタ番号nがどのレジスタになるのかについても、仕様で決める。 -bit数が仕様で規定されずにプログラムで指定するようにした理由は次のとおり。javaや.NETのように32bit固定ということにすると、286やZ80でこれらを実行する際に、本来は8bitもあれば十分なものをあえて32bit処理することになりかねないからである(キャリーフラグなどを使って多倍長処理することになる)。こんなのは無駄なので、多倍長処理は必要なときだけで済ませられるように、このような仕様にした。最大bit数については、おそらくたいていは指定しないことになるだろうが、ラップアラウンド(っていうんだっけ、127+1=-128みたいなやつ)や繰り上がりによるキャリーフラグの挙動を利用する際には指定することになる。 -IA-32で33bit以上のレジスタを使用する場合、上位部分はメモリに割り当てられる。 -リンクするネイティブコードを書く場合、データのやり取りをするとか、どのレジスタを破壊してはいけないかを意識するとき以外で、この仕様を気にする必要はない。 -EAXはリザーブにすることにした。ARMでもR0とR1はリザーブにする。EDXとR2もリザーブにするかもしれない。ECXがレジスタ番号0になるのはほぼ確定。 *** (4) -khabaではレジスタは原則として汎用ではない。(3)で言っているレジスタはデータレジスタであり、メモリアドレスやI/Oアドレス指定のために使用することはできない。アドレスレジスタのbit数はプログラマによって意識されない。たとえばこれは32bitのnearポインタかもしれないし、48bitのfarポインタかもしれないし、V86の16bit:16bitのfarポインタかもしれない。khabaは実際のネイティブコードを生成するときに、対象となるCPUやOSに応じて、アドレスレジスタのbit数を決定する。したがってネイティブプログラムがkhabaのプログラムとアドレスのやり取りする場合は、CPUが同じでもOSによって別々に準備する必要が出てくることは十分にありうる。 -IA-32の場合、ESPとEBPとESIとEDIがリザーブされており、実レジスタに対応するアドレスレジスタはEBXだけである。他はすべてメモリに置かれる。 (int) [A0] = 1; /* A0(アドレスレジスタ0)で示されたメモリを1にする。 */ (int) [A1] = 2; (int) [A2] = D0; -もしintがリトルエンディアンの16bitであるとしたら(intのエンディアンやビット数はプログラムのどこかで定義してある)、 MOV WORD [EBX],1 MOV ESI,[EBP+?(A1のある場所)] ←これはもっと複雑になるかもしれない MOV WORD [ESI],2 MOV ESI,[EBP+?(A2のある場所)] ←これはもっと複雑になるかもしれない MOV WORD [ESI],ECX -D0 = A1; のような、アドレスレジスタの値をデータレジスタへ取得しようとする行為はすべて禁止する。また A9 = D4; のような、アドレスレジスタへ普通のデータを入れることも禁止する。 A9 += D4; や D0 = A1 - A0; のようなことは許される。 *** (5) アドレッシング(1)・キャッシュ制御 -アドレッシングの一般型は、[An+(Dn*定数)+(定数)]である。括弧内は省略可能。この式にも現れているが、アドレスレジスタを用いないアドレッシングは許さない。これは次のような設計思想を反映している。 -GBAには256KBのRAMと32KBのRAMがあるが、256KBのほうは回路的にCPUからは遠く、アクセスには2クロックのウェイトが入る。32KBのほうはノーウェイト。これはこう考えたらいいだろう、つまり32KBは可視化されたキャッシュメモリである、しかしメモリのどの部分をキャッシュに入れるかとかはハードウェアによる自動制御がなされない。したがってOSなどが面倒を見るべきだと考える(OSがある場合の話)。 -メモリのある部分がこの速い32KBの範囲内にコピーされることはあるし、また256KBのほうへスワップアウト(というかキャッシュアウト?)されることもありうる。これらは動的に行われるべきだ。これらの場合OSは、そのメモリ域を指しているレジスタ(やメモリ)をアプリに気づかれることなくすべて更新する。 --メモリを更新する必要があるかどうかは、ポインタをどのようにメモリに格納させるかにもよる。たとえばブロックID+オフセットであれば、更新はしなくてよい。 -こういうことをやるためには、アドレッシング形式にアドレスレジスタを必ず含ませる必要がある。 -またアドレスレジスタをアドレッシングに含んでいるからといって、それだけでメモリのどこでもアクセスできるというわけではない。(Dn*定数)+(定数)の部分の値の範囲には当然制限が課される。その制限からアクセス範囲を特定し、これをブロックとして速いRAMに転送するなどをOSが行う。 *** (6) タスクセーブ・エンディアン・完全な型管理 -khabaはOSASKを強く意識しており(GBAで動くOSASKを作ることが最初の動機だったので当然)、khabaでもタスクセーブできなければいけない。しかもセーブしたものを他のCPUで再開できたら、しかもエミュレーションなしでできたら、とてもいいだろう。 -68000とかで実行したタスクをセーブしx86でロードすると、メモリイメージ上のエンディアンが合わないので、16bitメモリアクセス前後に XCHG(AL, AH); みたいなコードを挿入しなければいけないことになる。これはコードが肥大化するし、遅い。それならば、メモリ上のintやshortをすべて反転させてしまえばいいではないか。 -これをやるにはメモリのイメージに対して、どこがintでどこがcharでどこがポインタか、みたいなことを完全に把握しなければならない。だから把握する。この情報があるので、メモリダンプの可読性は非常に高い。構造体も認識するので、デバッグはかなり楽になる。 -IA-16でセーブしたものをIA-32でロードする場合、メモリに余裕があるならintの幅を16bit→32bitにしてしまうほうが、動作が速い。しかしこれをやるとメモリのポインタがずれる恐れがある。でもやる。khabaではメモリ上にストアされたポインタは実際のアドレスではなく、ファイルのパスのような抽象的なもので格納する。実際にはパスそのものを保存するわけではないが、 &stack.func_abc.table.i[12] のような形式へたどれるような情報をもつ。 struct STACK { struct MAIN { int i, j, k; /* 関数main()のスタック変数 */ }; struct FUNC_AAA { /* main()がfunc_aaa()を呼んだのでスタックに積まれた */ struct CODEPOINTER ret; /* ここは &code.main.label126 とかが入っているのだろう */ int i, j; float x, y; }; struct FUNC_ABC { /* func_aaa()がfunc_abcを呼んだらしい */ struct CODEPOINTER ret; struct TABLE { int i[100]; }; }; }; -当然ながらこのSTACK構造体は動的に変わることになる。関数呼び出しのオーバヘッドはやや大きい。 -この例ではstackの伸びる方向とは逆に書いたが、実際はスタックの伸びる方向にあわせて書くべきだ。・・・が、khabaのスタックがESPのように伸びていくとは決まっていない。 -もしメモリ使用状況マネージャに書き足すのがつらいのなら、スタックはスタック上にとるのではなく、mallocみたいにしたらいいかもしれない。この場合、旧スタックポインタをスタック内に記憶しておくことになるだろう。 struct FUNC_ABC { struct CODEPOINTER ret; struct DATAPOINTER oldstack; struct TABLE { int i[100]; }; }; }; -つまり呼び出し元を探るにはoldstackをたどっていけばいい。チェイン構造である。この場合、この構造体はもはやスタック上にはないので、先の例はこうなるだろうか。 &heap.func_abc.table.i[12] 。 -再帰などで同じ関数が何度も呼ばれたら、 &heap.func_abc~37.table.i[12] とかになるのだろう。 -このmalloc型スタックは、GBAみたいなマシンでは有利かもしれない。GBAは速いRAMが少ないので、大きなスタックをキャッシュに保持するのは大変である。でもmalloc方式にすれば、小さなオブジェクトがたくさんできるだけなので、今使っているものくらいなら問題なくキャッシュに収まる可能性が大きい。 -このような形式でタスクセーブされるため、タスクセーブ後にプログラムを少々書き換えたとしても、再開できないことはない(khabaバイナリを最適化して、ローカル変数名情報などを適当なIDなどに交換してしまったあとだと、改造しても動くというのはやりにくいかもしれないが)。 -khabaでは「unionを使って、floatで書いたものをintで読む」なんてことは絶対に禁止である。しかしunionが使えないわけではない。書いた型と違う型で読んだ場合に例外が起きるというだけのことである。同じ理由で強引にポインタをキャストして値を読むこともできない。 *** (7)ファイルシステムやプログラミングモデルへの影響 -OSASKではメモリレスアーキテクチャをとっており、これはGBAでも引き継がせる予定だが、ということはつまり、メモリイメージというのは、ただのバイナリファイルである。バイナリファイルにこのような型情報がつくわけだ。これは非常に便利である。バイナリファイルに型情報があるのだから、それなりのエディタを作れば、データの任意の部分を人間にわかりやすい形式で表示したり編集したりできるわけだ。 { view = { color = white; /* enumであればこのように表示できるはず(ただしこのコメントは実在しない) */ fontsize = 15; }; history[5] = { "abc.txt", "def.txt", "ghi.h", "", "" }; copybuf = "hogehoge"; }; -これはテキストエディタの設定ファイル(兼履歴保存ファイル)の例である。もちろんこれくらいの読みやすい設定ファイルを持つテキストエディタは既にごまんとありkhabaのアドバンテージとはいえないが、しかしこのようなファイルが、実際はただの100バイト前後のバイナリであって、解読ルーチンなしできわめて高速に読めるのである(バイナリなので当然)。これはかなりいけていると思う。しかもただのテキストファイルではないので、viewとかの部分(つまりメンバ名の部分)は編集できないだろうし(無理に編集することもできるだろうけど、その場合違う構造体になってしまうのでもはやこのアプリには読み込めない)、whiteのところはwhiteのほかにblueやredなどを選択すればいいだけにもできる。汎用・バイナリ・バリュー・エディタ? -これが現在実行中のアプリのスタックやヒープに対してもできるのだから、デバッガなんてもういらないかも。 -出力についても同じようなことがいえるかもしれない。たとえば、2つの整数aとbを入力して、それらの和であるcを出力するプログラムを考えるとする。普通に書けば、標準入力からscanfして、和を算出し、printfするだろう。もしくは入力をコマンドラインにするかもしれない。なんにせよ、ASCII文字列をバイナリに変換し、計算し、結果をASCIIにしなければいけない。 -khabaならどうだろうか。まずここで述べたように、入力部分をバイナリ化できる。以下C言語で書くが、これは説明のためであって、khaba版Cがこのような文法になることを保証するものではない。 struct IFILE { /* INTが仮に32bitだとすれば、8バイトの構造体(ファイル) */ INT a, b; }; void main(INT argc, STRING *argv) { if (argc >= 2) { struct IFILE *p = mapping_struct(arg[1], "r", struct IFILE); if (p != NULL) { printf("%d\n", p->a + p->b); unmapping(p); } } return; } -とりあえずこれで、scanfはなくなった。しかしprintfは残る。printfは重い関数だし、バイナリで入力したのに結果がASCIIだなんて、なんかみっともない。ということでこうしてみる。 struct IOFILE { INT a, b, c; }; void main(INT argc, STRING *argv) { if (argc >= 2) { struct IOFILE *p = mapping_struct(arg[1], "rw", struct IOFILE); if (p != NULL) { p->c = p->a + p->b; unmapping(p); } } return; } -これはすごい。結果もバイナリだ。しかも重い関数がない。結果がバイナリなので、汎用バイナリバリューエディタがあれば、10進数に限られることなく好きな進法で表示もできる。 -バイナリだと32bitを超える計算ができないという問題があると思うかもしれないが、上記プログラムのkhabaコードを実コードへ変換する際に、INTをint64とみなして処理せよというオプションをつければ、それだけで解決する。その場合読み書き用のバイナリも型変換してから使う(こんなのはもちろんツールで簡単にできる)。つまり、値が同じなら、int幅が変わった程度でバイナリを手で作り直す必要はまったくない。 *** (8) コードラベル・割り込み -タスクをセーブするにあたり、EIP(CPUによってはPCともいうけど)を保存する必要があるが、上記例のように &code.main.label126 のような形に変換しなければいけない。となると対応するラベルを大量に自動生成するか、もしくはタスクセーブ可能なポイントを絞る必要がある。 -タスクセーブ可能なポイントが大量にあってもそれほどのメリットはないだろうと考えられる(むしろラベル情報が多すぎてkhabaコードのファイルサイズが爆発しかねない)。数十〜数百命令に1つの割合でタスクセーブ可能なチェックポイントみたいなものがあれば、実用上の問題はないだろう。それくらいなら、ループ命令の分岐先のラベルやサブルーチンコールの戻り番地用のラベルだけでも十分だろうし、もしかしたらさらに間引いても良い。 -これはつまり任意の個所でタスクセーブができるわけではないことを意味するが、これはむしろタスクセーブにとって都合の悪い中途半端な状況でのセーブを自動的に回避できるというメリットもある。 -ネイティブに変換されたコードは、ラベルのあるチェックポイントに差し掛かるたびに、ワークエリア内のフラグをポーリングして、タスクセーブの準備ができましたというAPIを呼び出すかどうかを判定する。 -これをもう少し推し進めて考えると、そもそもCPUの割り込み機能だってなくてもいいのかもしれない。デメリットとしては、割り込みに対してワーストケースで数百から数千クロック遅れるということがあるが、それくらいはあまり問題にならない気もする(むしろそこまでタイミングにシビアすぎるハードウェアのほうがいけない?・・・せめて小規模なバッファくらいは持っていてほしい)。割り込み制御回路を節約できれば、CPU回路はもっと節約できるだろうし、CLIやSTIなど割り込み禁止・許可の命令を命令セットに準備しておく必要もなくなる。そのぶんレジスタ数を増やしたり、マルチコア度を上げるほうが総合的な性能向上(処理速度、消費電力あたりの処理能力など)が上昇しそうな気がする。 -もちろん割り込みがあるかどうかを判定するためのチェック命令や条件分岐命令の実行時間分のロスはある。しかしこれは数十〜数百命令に1つの割合で挿入されるだけであり、これでネイティブコードの量が増えたり、実行速度が落ちたりはするだろうが、それは1%以下だろうと思われる。 -なお、これを理想的にやるのなら、CPUの割り込み要求ピンの内容がそのままフラグレジスタのあるビットに直結されて(これは各デバイスのIRQ信号のORの結果)、そのビットが1なら分岐するという ji 命令(jump if interrupt)みたいなのがあるといいだろう。これならチェックポイントでのチェックによるロスは最小化できる。 *** (9) フラグ - *** memo //-GBAには(自動の)キャッシュ(制御回路)がない・そもそもRAMも少ない→アクセスウィンドウ→メモリアクセスに制限をつける //-エンディアンの問題・ポインタ変数の自動補正→構造体(データタイプ)の把握→非線形配列アクセス→自動型変換?ファイルシステム? //-コードラベル、CPU機能の削除(割り込み機能は不要?) -これらの長所を完全に生かすためには、リンクするネイティブコードにも多少のしきたりを押し付けるべき。 -レジスタスタック(sparcに似ている?:むしろAMD 29000に近い)、レジスタ間連続代入 -khabaが効率よく動かせるCPUとは?それは理想のCPUか? -定数レジスタ。代入以外のmodifyはできない。個別代入は遅くてもいい。分岐予測ビットのあるオペコード(かオペランド)。 -コンパイラは、ポインタが不要な変数(だから配列はダメ)以外の変数を全部レジスタ変数にする。 -JITに限定しない。クロスでもいい。PICマイコンなどでやろうとするとJITはインタプリタ式にしかできないから、非JITを認めるのは現実的。 -バウンダリチェックをネイティブでやる必要はない。バウンダリチェックは実行頻度が少ないし、機種・環境依存ではないから。 -2000年問題もこれなら少ない労力で解消できたはず。プログラム側は修正するべきだけど、データはYEAR型とかであれば自動で拡張できたはずだから。文字のエンコード問題も解消できるかもしれない。 -データレジスタ128本、定数レジスタ64本、アドレスレジスタ64本くらいが理想かも。グローバルなレジスタ群もほしいな。16+8+8くらいかな。 //x86はきたない、MIPS、使える技術こそ!、文明再出発学 *** こめんと欄 -このページは誰かに意見を求めるためではなく、自分の記憶の整理のためにあるので、こめんと欄はありません。
タイムスタンプを変更しない
* khabaのメモ -(by [[K]], 2007.02.02) *** (0) -これは誰が作るの? ・・・誰が作ってもいいが、多分誰も作ってくれないと思うので、自分で作る予定。細部の説明をするよりも自分で作るほうが早そうだし。 -でも僕よりももっとうまく作る人がいるかもしれないし、作りたい人が他にいたらそれを妨げる理由は何もないので、是非勝手に(=断りなく)やってください。 *** (1) 動機・方針 -GBA(GameBoyAdvance)で動くOSASKを作りたい。でもそしたらx86用OSASKとバイナリ互換はできない(当たり前)。ソース互換ならできるかもしれない。 -しかし仮にソース互換にするにしても、エンディアンの問題とかで、完全互換はできないかもしれない(ARM7だけならx86との間にエンディアンの問題は起きないかもしれないが、いずれは他のCPUのことだって考えたい)。それに人によってはソースを公開するのはいやかもしれない。 -それなら中間コード(いわゆるVMのバイトコード)みたいなものを考えて、それを実行時に各CPUのネイティブコードに変換すればいいのではないか。これならエンディアンとかの問題はない。CPUのビット数が違って機種によってint幅が異なる、という問題も起こさずに済ませられる。 -これをkhabaとする。 ---- -結局khabaがあるとどんなことができるようになるの?(ここだけですます調) --khabaはjavaや.netのように、どんなCPUにも適合するようなプログラムを作るためのシステムです。javaや.netよりもいいのは、CPUへの要求がもっとずっと少なくなるように工夫して設計されていることです。16bit-CPUや8bit-CPU、PICマイコンみたいな組み込み系のCPUであってもサポートできるように努力しています。実CPUのbit数とプログラムのint幅は完全に独立しており、たとえ16bit-CPU上のkhabaで32bitのintを使うことができますし、演算結果についても32bit-CPU上で実行した場合と差異はありません。逆に64bit-CPU上で16bitのintを扱うこともできます。また実CPUが8bitの場合に、8bitもあれば十分な演算をわざわざ32bitで処理して遅くなるということもありません。khabaはアプリだけではなくOSも記述できることを目指していますし、khabaで書かれたOSASKも用意するつもりです。khabaアプリを実行する際にはCPUを選びませんが、もちろんOSも選びません。 --khabaのプログラムをjavaや.netに変換することができます。javaや.netのコードをkhabaにすることもできると思います。 --khabaを動かすCPUには以下の機能がなくても基本的には問題ありません(これらがないCPUであっても、まるでこれらの機能を有するCPUであるかのように扱えるし、それによって速度が目に見えて落ちるということもおそらくない)。 ---ページング・セグメンテーション・保護例外・キャッシュ制御(キャッシュ制御はいりませんが、少量の速い普通のRAMはあったほうがいいです。これすらない場合は、残念ながら速度が目に見えて低下します。メモリマップ上の特定のアドレス範囲のRAMは他よりも高速、ということで十分です)・動的な分岐予測・カーネルモード/ユーザモードの切り替え、PUSH/POPやCALLなどのスタック操作命令群、長さの異なるレジスタ(EAXをALやAHに分解して使えること)、レジスタの汎用化(汎用レジスタがなくていい=つまりデータレジスタ/アドレスレジスタの区別があっていい)、割り込み(ハードウェア割り込みとソフトウェア割り込みの両方)、マルチタスク支援、アドレス空間のサイズ拡大(バンク切り替え方式のメモリでもリニアマップ方式のメモリでもかまわないという意味) --もしkhabaを動かせるCPUを作るのならこれらの機能は実装しないで、その分ロジックを小さくし、以下の面での性能向上を図るべきです。 ---レジスタ数を増やす、消費電力を下げる、マルチコア度を上げる、内蔵高速メモリの容量(キャッシュの代わりになる)を増やす、DMAをつける、SIMD命令をつける(レジスタ-レジスタ間演算のみでかまわない) --タスクセーブができます。セーブしたタスクを異なるOS・異なるCPUで再開可能です(だからもしかしたら分散OS向き?)。タスクセーブ後にプログラムを少しいじって、その後に修正されたプログラムに対して(つまり変数値などはそのままで)再開することも可能です。IA-32用OSASKではここまでの再開能力は想定していませんでした。 --メモリの利用状況を細かく監視できるため、メモリダンプを見る際に、ここは○×構造体、みたいなのがすべて分かります。同じことはファイルにも適用され、バイナリファイルでフォーマットが分からないということはまずないですし、フォーマット変換も自動で可能です。「UNIXという考え方」では、バイナリで出力するなんてよくない、ASCIIとかにするべきだという話があったように思いますが、khabaを前提にしてもいいのなら、むしろCPUへの負荷の多い上にバイト数もかさむASCIIにしろだなんて、ナンセンスもはなはだしいです。 *** (2) ターゲット(目的)・最適化 -khabaでは最適化に執着しない。プログラムの2割の部分が実行時間の8割を占めるといわれるが(人によっては1-9ともいうが)、khabaが目指しているのはこのコードのうちの残りの8割の部分の移植の手間をなくすことである。速度が求められる2割の部分については、それぞれのCPU向けのコンパイラ(やアセンブラ)で最適なコードを生成すればよい(つまり手作業で移植する)。もちろんプログラム全体をkhabaだけで書くことはできるが、その場合、実行速度には不満が残るかもしれない。しかしそれは今検討するべき問題ではないと考える。 -khabaは、ネイティブコードと簡単に(=少ないロスで)交信できる。関数とかも気軽に呼べる。これが担保できないと、上記理想が実現できない。 *** (3) -khabaはスタックマシンではなく、レジスタマシンである。総レジスタ数は128個か、もしくはそれより多くする(上限を設けないかもしれない)。レジスタのビット数も規定されない。レジスタを使い始める前に、そのレジスタを何ビットレジスタとして使いたいのかを宣言する。宣言は1bitでも2bitでも27bitでもよい。この宣言には、最低ビット数と最大ビット数がある。たとえば最低ビット数8、最大ビット数指定なし、の場合、8bit以上のレジスタ(もしくはメモリ)が割り当てられると仮定してよい。 for (i = 0; i < 100; i++) { hoge...; } のiのために割り当てるレジスタであれば、まさにこのような指定でいいわけである。符号なしで扱うなら7bitで十分だ。 -レジスタマシンにしたのは、khabaをネイティブコードに変換するプログラムに楽をさせ、しかもネイティブコードとの交信をやりやすくするため。たとえばIA-32の場合、レジスタ番号0は、EAXに割り当てられる。レジスタ番号1はECX(本当にEAXやECXにするかどうかは未定。翻訳をやりやすくするため、EAXとかをリザーブにする可能性もあるから・・・MUL/DIV命令やIN/OUT命令を考えると、EAXでしかできないことが結構あり、もしEAXがレジスタ0として利用可能だとすると、DIVをPUSH(EAX);、POP(EAX);ではさまないといけない。リザーブしておけば、EAXは常に破壊可能と想定していいことになるから、このようなPUSH/POPはいらなくなる)。 -レジスタ番号8番以降はIA-32には割り当てるべきレジスタが存在しない。これはメモリに割り当てられる。リザーブレジスタがあれば、8番よりももっと若い番号でもメモリに割り当てられることになりそうだ。何番からメモリ行きになるかは、動的に決まるわけではない。khaba/IA-32仕様に記載され、確定する。レジスタ番号nがどのレジスタになるのかについても、仕様で決める。 -bit数が仕様で規定されずにプログラムで指定するようにした理由は次のとおり。javaや.NETのように32bit固定ということにすると、286やZ80でこれらを実行する際に、本来は8bitもあれば十分なものをあえて32bit処理することになりかねないからである(キャリーフラグなどを使って多倍長処理することになる)。こんなのは無駄なので、多倍長処理は必要なときだけで済ませられるように、このような仕様にした。最大bit数については、おそらくたいていは指定しないことになるだろうが、ラップアラウンド(っていうんだっけ、127+1=-128みたいなやつ)や繰り上がりによるキャリーフラグの挙動を利用する際には指定することになる。 -IA-32で33bit以上のレジスタを使用する場合、上位部分はメモリに割り当てられる。 -リンクするネイティブコードを書く場合、データのやり取りをするとか、どのレジスタを破壊してはいけないかを意識するとき以外で、この仕様を気にする必要はない。 -EAXはリザーブにすることにした。ARMでもR0とR1はリザーブにする。EDXとR2もリザーブにするかもしれない。ECXがレジスタ番号0になるのはほぼ確定。 *** (4) -khabaではレジスタは原則として汎用ではない。(3)で言っているレジスタはデータレジスタであり、メモリアドレスやI/Oアドレス指定のために使用することはできない。アドレスレジスタのbit数はプログラマによって意識されない。たとえばこれは32bitのnearポインタかもしれないし、48bitのfarポインタかもしれないし、V86の16bit:16bitのfarポインタかもしれない。khabaは実際のネイティブコードを生成するときに、対象となるCPUやOSに応じて、アドレスレジスタのbit数を決定する。したがってネイティブプログラムがkhabaのプログラムとアドレスのやり取りする場合は、CPUが同じでもOSによって別々に準備する必要が出てくることは十分にありうる。 -IA-32の場合、ESPとEBPとESIとEDIがリザーブされており、実レジスタに対応するアドレスレジスタはEBXだけである。他はすべてメモリに置かれる。 (int) [A0] = 1; /* A0(アドレスレジスタ0)で示されたメモリを1にする。 */ (int) [A1] = 2; (int) [A2] = D0; -もしintがリトルエンディアンの16bitであるとしたら(intのエンディアンやビット数はプログラムのどこかで定義してある)、 MOV WORD [EBX],1 MOV ESI,[EBP+?(A1のある場所)] ←これはもっと複雑になるかもしれない MOV WORD [ESI],2 MOV ESI,[EBP+?(A2のある場所)] ←これはもっと複雑になるかもしれない MOV WORD [ESI],ECX -D0 = A1; のような、アドレスレジスタの値をデータレジスタへ取得しようとする行為はすべて禁止する。また A9 = D4; のような、アドレスレジスタへ普通のデータを入れることも禁止する。 A9 += D4; や D0 = A1 - A0; のようなことは許される。 *** (5) アドレッシング(1)・キャッシュ制御 -アドレッシングの一般型は、[An+(Dn*定数)+(定数)]である。括弧内は省略可能。この式にも現れているが、アドレスレジスタを用いないアドレッシングは許さない。これは次のような設計思想を反映している。 -GBAには256KBのRAMと32KBのRAMがあるが、256KBのほうは回路的にCPUからは遠く、アクセスには2クロックのウェイトが入る。32KBのほうはノーウェイト。これはこう考えたらいいだろう、つまり32KBは可視化されたキャッシュメモリである、しかしメモリのどの部分をキャッシュに入れるかとかはハードウェアによる自動制御がなされない。したがってOSなどが面倒を見るべきだと考える(OSがある場合の話)。 -メモリのある部分がこの速い32KBの範囲内にコピーされることはあるし、また256KBのほうへスワップアウト(というかキャッシュアウト?)されることもありうる。これらは動的に行われるべきだ。これらの場合OSは、そのメモリ域を指しているレジスタ(やメモリ)をアプリに気づかれることなくすべて更新する。 --メモリを更新する必要があるかどうかは、ポインタをどのようにメモリに格納させるかにもよる。たとえばブロックID+オフセットであれば、更新はしなくてよい。 -こういうことをやるためには、アドレッシング形式にアドレスレジスタを必ず含ませる必要がある。 -またアドレスレジスタをアドレッシングに含んでいるからといって、それだけでメモリのどこでもアクセスできるというわけではない。(Dn*定数)+(定数)の部分の値の範囲には当然制限が課される。その制限からアクセス範囲を特定し、これをブロックとして速いRAMに転送するなどをOSが行う。 *** (6) タスクセーブ・エンディアン・完全な型管理 -khabaはOSASKを強く意識しており(GBAで動くOSASKを作ることが最初の動機だったので当然)、khabaでもタスクセーブできなければいけない。しかもセーブしたものを他のCPUで再開できたら、しかもエミュレーションなしでできたら、とてもいいだろう。 -68000とかで実行したタスクをセーブしx86でロードすると、メモリイメージ上のエンディアンが合わないので、16bitメモリアクセス前後に XCHG(AL, AH); みたいなコードを挿入しなければいけないことになる。これはコードが肥大化するし、遅い。それならば、メモリ上のintやshortをすべて反転させてしまえばいいではないか。 -これをやるにはメモリのイメージに対して、どこがintでどこがcharでどこがポインタか、みたいなことを完全に把握しなければならない。だから把握する。この情報があるので、メモリダンプの可読性は非常に高い。構造体も認識するので、デバッグはかなり楽になる。 -IA-16でセーブしたものをIA-32でロードする場合、メモリに余裕があるならintの幅を16bit→32bitにしてしまうほうが、動作が速い。しかしこれをやるとメモリのポインタがずれる恐れがある。でもやる。khabaではメモリ上にストアされたポインタは実際のアドレスではなく、ファイルのパスのような抽象的なもので格納する。実際にはパスそのものを保存するわけではないが、 &stack.func_abc.table.i[12] のような形式へたどれるような情報をもつ。 struct STACK { struct MAIN { int i, j, k; /* 関数main()のスタック変数 */ }; struct FUNC_AAA { /* main()がfunc_aaa()を呼んだのでスタックに積まれた */ struct CODEPOINTER ret; /* ここは &code.main.label126 とかが入っているのだろう */ int i, j; float x, y; }; struct FUNC_ABC { /* func_aaa()がfunc_abcを呼んだらしい */ struct CODEPOINTER ret; struct TABLE { int i[100]; }; }; }; -当然ながらこのSTACK構造体は動的に変わることになる。関数呼び出しのオーバヘッドはやや大きい。 -この例ではstackの伸びる方向とは逆に書いたが、実際はスタックの伸びる方向にあわせて書くべきだ。・・・が、khabaのスタックがESPのように伸びていくとは決まっていない。 -もしメモリ使用状況マネージャに書き足すのがつらいのなら、スタックはスタック上にとるのではなく、mallocみたいにしたらいいかもしれない。この場合、旧スタックポインタをスタック内に記憶しておくことになるだろう。 struct FUNC_ABC { struct CODEPOINTER ret; struct DATAPOINTER oldstack; struct TABLE { int i[100]; }; }; }; -つまり呼び出し元を探るにはoldstackをたどっていけばいい。チェイン構造である。この場合、この構造体はもはやスタック上にはないので、先の例はこうなるだろうか。 &heap.func_abc.table.i[12] 。 -再帰などで同じ関数が何度も呼ばれたら、 &heap.func_abc~37.table.i[12] とかになるのだろう。 -このmalloc型スタックは、GBAみたいなマシンでは有利かもしれない。GBAは速いRAMが少ないので、大きなスタックをキャッシュに保持するのは大変である。でもmalloc方式にすれば、小さなオブジェクトがたくさんできるだけなので、今使っているものくらいなら問題なくキャッシュに収まる可能性が大きい。 -このような形式でタスクセーブされるため、タスクセーブ後にプログラムを少々書き換えたとしても、再開できないことはない(khabaバイナリを最適化して、ローカル変数名情報などを適当なIDなどに交換してしまったあとだと、改造しても動くというのはやりにくいかもしれないが)。 -khabaでは「unionを使って、floatで書いたものをintで読む」なんてことは絶対に禁止である。しかしunionが使えないわけではない。書いた型と違う型で読んだ場合に例外が起きるというだけのことである。同じ理由で強引にポインタをキャストして値を読むこともできない。 *** (7)ファイルシステムやプログラミングモデルへの影響 -OSASKではメモリレスアーキテクチャをとっており、これはGBAでも引き継がせる予定だが、ということはつまり、メモリイメージというのは、ただのバイナリファイルである。バイナリファイルにこのような型情報がつくわけだ。これは非常に便利である。バイナリファイルに型情報があるのだから、それなりのエディタを作れば、データの任意の部分を人間にわかりやすい形式で表示したり編集したりできるわけだ。 { view = { color = white; /* enumであればこのように表示できるはず(ただしこのコメントは実在しない) */ fontsize = 15; }; history[5] = { "abc.txt", "def.txt", "ghi.h", "", "" }; copybuf = "hogehoge"; }; -これはテキストエディタの設定ファイル(兼履歴保存ファイル)の例である。もちろんこれくらいの読みやすい設定ファイルを持つテキストエディタは既にごまんとありkhabaのアドバンテージとはいえないが、しかしこのようなファイルが、実際はただの100バイト前後のバイナリであって、解読ルーチンなしできわめて高速に読めるのである(バイナリなので当然)。これはかなりいけていると思う。しかもただのテキストファイルではないので、viewとかの部分(つまりメンバ名の部分)は編集できないだろうし(無理に編集することもできるだろうけど、その場合違う構造体になってしまうのでもはやこのアプリには読み込めない)、whiteのところはwhiteのほかにblueやredなどを選択すればいいだけにもできる。汎用・バイナリ・バリュー・エディタ? -これが現在実行中のアプリのスタックやヒープに対してもできるのだから、デバッガなんてもういらないかも。 -出力についても同じようなことがいえるかもしれない。たとえば、2つの整数aとbを入力して、それらの和であるcを出力するプログラムを考えるとする。普通に書けば、標準入力からscanfして、和を算出し、printfするだろう。もしくは入力をコマンドラインにするかもしれない。なんにせよ、ASCII文字列をバイナリに変換し、計算し、結果をASCIIにしなければいけない。 -khabaならどうだろうか。まずここで述べたように、入力部分をバイナリ化できる。以下C言語で書くが、これは説明のためであって、khaba版Cがこのような文法になることを保証するものではない。 struct IFILE { /* INTが仮に32bitだとすれば、8バイトの構造体(ファイル) */ INT a, b; }; void main(INT argc, STRING *argv) { if (argc >= 2) { struct IFILE *p = mapping_struct(arg[1], "r", struct IFILE); if (p != NULL) { printf("%d\n", p->a + p->b); unmapping(p); } } return; } -とりあえずこれで、scanfはなくなった。しかしprintfは残る。printfは重い関数だし、バイナリで入力したのに結果がASCIIだなんて、なんかみっともない。ということでこうしてみる。 struct IOFILE { INT a, b, c; }; void main(INT argc, STRING *argv) { if (argc >= 2) { struct IOFILE *p = mapping_struct(arg[1], "rw", struct IOFILE); if (p != NULL) { p->c = p->a + p->b; unmapping(p); } } return; } -これはすごい。結果もバイナリだ。しかも重い関数がない。結果がバイナリなので、汎用バイナリバリューエディタがあれば、10進数に限られることなく好きな進法で表示もできる。 -バイナリだと32bitを超える計算ができないという問題があると思うかもしれないが、上記プログラムのkhabaコードを実コードへ変換する際に、INTをint64とみなして処理せよというオプションをつければ、それだけで解決する。その場合読み書き用のバイナリも型変換してから使う(こんなのはもちろんツールで簡単にできる)。つまり、値が同じなら、int幅が変わった程度でバイナリを手で作り直す必要はまったくない。 *** (8) コードラベル・割り込み -タスクをセーブするにあたり、EIP(CPUによってはPCともいうけど)を保存する必要があるが、上記例のように &code.main.label126 のような形に変換しなければいけない。となると対応するラベルを大量に自動生成するか、もしくはタスクセーブ可能なポイントを絞る必要がある。 -タスクセーブ可能なポイントが大量にあってもそれほどのメリットはないだろうと考えられる(むしろラベル情報が多すぎてkhabaコードのファイルサイズが爆発しかねない)。数十〜数百命令に1つの割合でタスクセーブ可能なチェックポイントみたいなものがあれば、実用上の問題はないだろう。それくらいなら、ループ命令の分岐先のラベルやサブルーチンコールの戻り番地用のラベルだけでも十分だろうし、もしかしたらさらに間引いても良い。 -これはつまり任意の個所でタスクセーブができるわけではないことを意味するが、これはむしろタスクセーブにとって都合の悪い中途半端な状況でのセーブを自動的に回避できるというメリットもある。 -ネイティブに変換されたコードは、ラベルのあるチェックポイントに差し掛かるたびに、ワークエリア内のフラグをポーリングして、タスクセーブの準備ができましたというAPIを呼び出すかどうかを判定する。 -これをもう少し推し進めて考えると、そもそもCPUの割り込み機能だってなくてもいいのかもしれない。デメリットとしては、割り込みに対してワーストケースで数百から数千クロック遅れるということがあるが、それくらいはあまり問題にならない気もする(むしろそこまでタイミングにシビアすぎるハードウェアのほうがいけない?・・・せめて小規模なバッファくらいは持っていてほしい)。割り込み制御回路を節約できれば、CPU回路はもっと節約できるだろうし、CLIやSTIなど割り込み禁止・許可の命令を命令セットに準備しておく必要もなくなる。そのぶんレジスタ数を増やしたり、マルチコア度を上げるほうが総合的な性能向上(処理速度、消費電力あたりの処理能力など)が上昇しそうな気がする。 -もちろん割り込みがあるかどうかを判定するためのチェック命令や条件分岐命令の実行時間分のロスはある。しかしこれは数十〜数百命令に1つの割合で挿入されるだけであり、これでネイティブコードの量が増えたり、実行速度が落ちたりはするだろうが、それは1%以下だろうと思われる。 -なお、これを理想的にやるのなら、CPUの割り込み要求ピンの内容がそのままフラグレジスタのあるビットに直結されて(これは各デバイスのIRQ信号のORの結果)、そのビットが1なら分岐するという ji 命令(jump if interrupt)みたいなのがあるといいだろう。これならチェックポイントでのチェックによるロスは最小化できる。 *** (9) フラグ - *** memo //-GBAには(自動の)キャッシュ(制御回路)がない・そもそもRAMも少ない→アクセスウィンドウ→メモリアクセスに制限をつける //-エンディアンの問題・ポインタ変数の自動補正→構造体(データタイプ)の把握→非線形配列アクセス→自動型変換?ファイルシステム? //-コードラベル、CPU機能の削除(割り込み機能は不要?) -これらの長所を完全に生かすためには、リンクするネイティブコードにも多少のしきたりを押し付けるべき。 -レジスタスタック(sparcに似ている?:むしろAMD 29000に近い)、レジスタ間連続代入 -khabaが効率よく動かせるCPUとは?それは理想のCPUか? -定数レジスタ。代入以外のmodifyはできない。個別代入は遅くてもいい。分岐予測ビットのあるオペコード(かオペランド)。 -コンパイラは、ポインタが不要な変数(だから配列はダメ)以外の変数を全部レジスタ変数にする。 -JITに限定しない。クロスでもいい。PICマイコンなどでやろうとするとJITはインタプリタ式にしかできないから、非JITを認めるのは現実的。 -バウンダリチェックをネイティブでやる必要はない。バウンダリチェックは実行頻度が少ないし、機種・環境依存ではないから。 -2000年問題もこれなら少ない労力で解消できたはず。プログラム側は修正するべきだけど、データはYEAR型とかであれば自動で拡張できたはずだから。文字のエンコード問題も解消できるかもしれない。 -データレジスタ128本、定数レジスタ64本、アドレスレジスタ64本くらいが理想かも。グローバルなレジスタ群もほしいな。16+8+8くらいかな。 //x86はきたない、MIPS、使える技術こそ!、文明再出発学 *** こめんと欄 -このページは誰かに意見を求めるためではなく、自分の記憶の整理のためにあるので、こめんと欄はありません。
テキスト整形のルールを表示する