こんばんは、川合です。 今日はbim2bin4uのリリースです。 http://k.hideyosi.com/bim2bi4u.lzh (206KB) 長ったらしい説明は後にして、まず使い方を紹介します。 多くの人がosa_dir2を使っているだろうと想像しているので、それに あわせて説明します。他の方法でOSASKアプリ開発をしている人は、自 分で適宜読み替えてください。 bim2bin4uをダウンロードして展開したら、bim2bin.exeとt5lzma.exe をosa_dir2の中のz_toolsへコピーして貼り付けてください。 そしてz_tools/osa_qemuのOSASK.EXEを消して、代わりにベータリリ ースされているTWITCH6A.EXEをいれて、これをOSASK.EXEに改名してく ださい。これでインストールは完了です。 次にz_toolsを開いてcom_mak.txtというファイルを見つけてください 。これをメモ帳などで書き換えます。これは全部で79行のテキストファ イルなのですが、以下の3行を修正してください。 9行目: 旧:BIM2BIN = $(TOOLPATH)bim2bin3.exe 新:BIM2BIN = $(TOOLPATH)bim2bin.exe 55行目: 旧:$(BIM2BIN) in:$*.bim out:$*.org malloc:$(MALLOCSIZE) mmarea:$(MMAREA) 新:$(BIM2BIN) -v48a in:$*.bim out:$*.org malloc:$(MALLOCSIZE) mmarea:$(MMAREA) 56行目: 旧:$(BIM2BIN) -osacmp -tek0 in:$*.org out:$*.bin 新:$(BIM2BIN) -osacmp in:$*.org out:$*.bin これで設定も完了です。 あとはあなたのアプリをmake cleanしてからmakeすれば、小さくて速 いアプリに生まれ変わっていることでしょう。ただし、OSASK ver.4.8以 降でしか動かないのでそれは注意してください。55行目を元に戻すと、 サイズは増えますがOSASK ver.4.7でも動くアプリになります。 なお、これらの改訂を済ませたosa_dirもそのうち作ります(その前に gas2naskやobj2bimをバージョンアップしたいので、すぐには出せません )。 --- さて長い説明です。 ・はじめに OSASKアプリのサイズに関する歴史を振り返ると、たとえばinvader5 は次のようになっています。 1293バイト:lcc-win32時代(このときもすでにtek0はあった?) 1258バイト:GO+tek0時代 1152バイト:GO+tek5時代 1108バイト:GO+v48a+tek5時代 lcc時代からOSASKは1バイトを惜しんで最適化されている、とかなん とかいう俗説がありましたが(笑)、この流れを見るとlcc時代は少な くとも185バイトの無駄を有していたわけで、この俗説は的外れだった ということになります。 ・歴史:最初のSFXまでの物語 OSASKアプリの基本として、アプリは書き換え不可能なコードセグメ ントと、初期化されていないワークエリアのみでスタートする、という ルールがあります。これをたとえるなら、アプリケーションがリンク時 に指定した面積の土地を与えるが、それは整地されていないような、も しかしたら以前住んでいた人の家が中途半端に残っているかもしれない ような、そんな状態です。今思えば一番最初にこのルールを決めたこと が、現在のような発展につながったのだと思います。 OSASKアプリは、OsaskMainへ制御を移す前に、スタックの初期値の代 入と、ワークエリアの初期化を自分でおこなっています。つまり、まず 自分が住むための家を建てるわけです。これは他の一般的なOSとは違っ ています。他のOSでは、スタックの初期化やワークエリアの初期化など は、OS(というかアプリケーションローダ)がやっています。アプリケ ーションファイルにはコード以外の情報として、初期データテーブルも 付属していて、これを読んでアプリケーションローダがやってくれるわ けです。いうなれば、アプリケーションファイルに自宅の設計図が付属 しており、OSが家を立てた後にアプリがスタートするわけです。 OSASKアプリの仕様をこのようにしたのは、OSがあれこれかまってや るのではなく、できるだけアプリが自主的に自分の面倒をみるようにし ようという僕の意図がありました。この仕様が決まったのはOSASKが一 般公開される前のことですが、そのときにこの仕様に対して異論が出た のを覚えています。つまり、どうせどのアプリでも最初の手順は同じよ うなものなんだから、アプリの手間を減らすためにも、OSが面倒を見る べきではないか、そのほうがアプリを作るのが楽になるんじゃないか、 というものです。 基本的にこの異論は最初から最後まで全くの正論だったんですが、し かし僕は直感を信じてわが道を進みました。僕の直感はこうでした。OS が面倒を見るようになると、OSが複雑になってでかくなるのです。OSが 家を立てることになると、その家についてあれこれルールを決めてしま いそうな気がしました。そういう注文の多い料理店になるのはどうもい やだったのです。またOSが立ててやるよりも、アプリに立てさせるほう がうまくいくのではないかとも考えました。というのは、アプリはどん な家を建てるかを知っているのです。OSはどんな家でも建てられるよう なアルゴリズムしか選べませんが、アプリは自分の家さえ建てられれば よいので、場合によってはトリッキーなアルゴリズムを選ぶことだって できるはずなのです。そうすれば標準的なアルゴリズムよりも速く動作 することもありうるのではないかと思ったのです。 初期のOSASKアプリは、非常に芸のない方法でワークエリアを初期化 していました。つまりワークエリアのイメージデータをそのままコード 内にもって、ストリング命令で地道に転送していました。こんなことを するくらいなら他のOSと同じ方法でいいじゃないか、といいたくなるよ うなものです。 初期のOSASKにはaballというゲームがあったのですが、こいつのワー クエリアが結構大きい上に、中身は0x00ばかりでした。ところどころに 0x00以外のデータがぱらぱらと存在していたのです。僕はこれこそトリ ッキーなことをすれば小さくできるタイプのデータだと思い、まずワー クエリアをオールクリアした後に、ゼロではないところだけに値を書き 込むような、そんなアルゴリズムで初期化するようにしました。実行フ ァイルはぐっと小さくなりました。これはまさにワークエリアに関して SFX(自己解凍)ルーチンをもった最初のOSASKアプリでした。 ・歴史:tek0の大成功までの物語 これに気をよくしたものの、毎回ワークエリア初期化ルーチンを考え るのはいやだったので、もっと汎用的な方法で圧縮してみようと考えま した。この結果としてl2d3アルゴリズムができました。このことがその 後の僕を圧縮屋に導くわけです(笑)。で、l2d3では、展開速度や圧縮 率ももちろん大事でしたが、展開ルーチンの小ささも大事でした。なぜ かと言うと、アプリケーションの中にこの展開ルーチンを入れなければ いけないからです。標準的なSFXである以上、これは避けられないこと です。そしてl2d3は大成功でした。というのは、苦労してトリッキーな 初期化ルーチンでがんばったaballよりも、l2d3を使ったaballのほうが コンパクトだったからです。 こうしてほとんどのアプリがワークエリアをl2d3でSFXするようにな ると、どのアプリも同じようにl2d3展開ルーチンを持つのが無駄に思え てきました。ということで、OSASKのAPIに(無理やり)l2d3展開ファン クションをつけました。この展開ファンクションは、API初期化ファン クションよりも前に実行しなければならず(というのは初期化ファンク ションコマンド列まで圧縮されているから)、初期化ファンクションよ りも前に実行できるファンクションなど前もって想定していなかったの で、結果的に美しくないコマンド体系になりました。しかし、とにかく つけました。そしてアプリは必要に応じてこのAPIを呼ぶことで、コー ドをほとんど増やすことなくl2d3のSFX機能を利用できるようになりま した。 アプリが展開ルーチンを負担しなくてもよくなったので、僕はもっと 規模の大きい展開ルーチンを採用することにしました。それで生まれた のがtek0です。しかし当時の僕はまだ発想が控えめで、でかい展開ルー チンを持つとOSがその分大きくなってしまうと考えたので、tek0の圧縮 率はすごくよい、というものではありませんでした。l2d3よりはよい、 という程度でした。これでアプリは小さくなりました。 と同時に、僕はワークエリア初期化用データのみが圧縮できる、とい うのに不満を感じてきました。プログラム全体からすると、もちろんワ ークエリア初期化用データは巨大なので、これが圧縮できることは好ま しいのですが、そこを圧縮してしまうと、今度はコード部分のでかさが 気になるのです。 しかしOSASKアプリはコードの書き換えを一切認めていません。した がってUPXのようなコードのSFXはできないのです(コードを自己解凍す るということは書き換えたプログラムが実行可能であることに等しい) 。コードの書き換えを許さないという方針は、同じアプリが複数走って いるときに、共有することでロード時間やメモリを節約できるというこ とでもあったので、僕としては譲れないところでした。それでosacmp形 式が生まれました。 osacmp形式は、OSがファイルをメモリに読み込む段階で、圧縮されて いれば展開する、という仕組みでした。つまり自己解凍ではなく、OS解 凍です。もちろん展開アルゴリズムは既にOSの中に展開ルーチンが埋め 込まれているtek0を採用しました。 osacmpは自己解凍ではないので、実行ファイル以外も圧縮できました 。そしてこれにより馬鹿でかいフォントデータや日本語変換辞書などが 実用的なサイズに抑えられるようになった、という嬉しいおまけもつき ました。 ちょうどこのころ、I.Tak.さんが実によい質問をしました。osacmpで 実行ファイル全体を圧縮することになると、ワークエリア初期化用デー タは結果的に二重圧縮されることになるわけですが、これでよいのか? ということでした。この質問の背景には、二重圧縮はデータが縮まない 、いやむしろ一重圧縮よりも増加する、という一般的な法則があります 。これを避けるにはアプリが自前でSFXするのをやめてただの転送にし て、圧縮はosacmpだけに任せてしまえばいいだけです。だから二重圧縮 は僕が一言、「サイズを優先するのでやめることにしましょう」といえ ばそれだけで解決する問題だったのです。I.Tak.さんは多分そういうこ とを考えてこの質問をしたのだと思います。 当時の僕は、二重圧縮はトータルサイズとしては少し損だけど、でも あえて二重圧縮にすることを推奨する、と明言しました。 なぜかというと、当時のOSASKはosacmpを展開した結果をメモリに持 っていたからです(キャッシュのようなものです)。もし二重圧縮をし ないとなると、このメモリ消費量が増えるわけで、それがいやだと考え たからでした。特にワークエリア初期化用データは圧縮するかしないか でサイズが5倍くらいは変わるので(それに対して、コードは2倍程度の 差しかない)、ここを無圧縮でメモリに保持するのはあまりよくないの です。 それに加えて、アプリサイズ比較の際に、非osacmp時のサイズも参考 値として挙げていたので、ここが大きくなるのもなんとなく嫌だなと思 ったのです(ここは笑うところなんですが、実はこれも重要な問題の一 つだったのです、今思えば)。 ・歴史:現在から未来への物語 僕はOSが展開ルーチンを持つことが非常に重要だと気がついて、そし てアプリの圧縮などよりもむしろフォントや変換辞書データなど、巨大 データ圧縮にこそその真価があると分かり、より汎用的で(つまりワー クエリア初期化データやアプリさえ圧縮できればいいやなんていうわけ ではなく、どんな種類のデータもそれなりに圧縮できて)さらに展開速 度を追求した圧縮フォーマットを開発しました。これはOSASKからの要 請というよりはKHBIOSからの要請でした。つまりOSASKを使っている分 にはtek0でもそんなに不満はないのですが、OSASKを超えて他のKHBIOS 対応OSにもおすすめできるような形式なのかと自問すると、それは違う 、ということになったわけです。 それでtek1/tek2/tek5ができて、これらは満足行く出来栄えでした。 そしてOSASKにtek1〜tek5を導入すると実に満足できる結果になりまし た。 しかし新たな問題も生じました。 tek5は展開が遅いのです。いや今までの速さに慣れている分にはそれ ほど問題はないのですが、やはり(自称世界最速の)tek1と比べると、 ちょっと時間が掛かっているなと感じてしまいます(tek0と比較するな らそれほどの差はない)。まあ200MHz以上の速度ならやはり気がつくこ とはないかもしれないですが。 でもこうなることは前もって予想していたので、僕は動じません。 tek5はちょっと遅いからtek2にしましょう、という解決もOKですが、僕 が薦めるのは、展開キャッシュシステムをOSに内蔵させることです。つ まり毎回まじめに展開するのではなく、そのファイルを過去に展開した ことがあるかどうかを調べて、展開したことがあれば実際の展開作業を 経ずに展開キャッシュから展開結果を得るのです。しかもこの展開キャ ッシュを、オンメモリだけではなくオンディスクでもOKになるようにす れば、再起動してもキャッシュは有効です。つまり初めてファイルを開 いたときはtek5の展開の遅さを感じるかもしれませんが(といってもほ とんどの人はCPUが速すぎて気が付かないっぽいけど)、二度目以降は すいすい進むわけです。 ええと念のために書いておきますが、Twitchell6には展開キャッシュ は実装されていません。将来のOSASKでは実装する予定だという意味で す。 またディスクやメモリの有効利用を考えると、展開キャッシュに無圧 縮の結果を置いておくのは得策ではないでしょう。ということで、tek1 でデータを保持させるつもりです。tek1で持つといっても、tek5を展開 してtek1で再圧縮するのではなく(それはそれなりに時間がかかる)、 tek5から直接tek1に高速に変換するアルゴリズムがありますのでそれを 使います。 展開キャッシュのことを考えると、二重圧縮はただの展開時間の無駄 です。オーバーラップはないほうがいいに決まっています。キャッシュ も浪費してしまいます。 それに非osacmp時のサイズなんぞを多少なりとも気にした自分が今や 情けないです。OSが圧縮をサポートするということは、つまりアプリは 圧縮機能前提で書かれるべきであって、それはつまり圧縮時に好ましい 結果を残せればいいのであって、無圧縮状態ででかかろうが小さかろう が、そんなのは全くどうでもいいことです。以前、「無圧縮状態のサイ ズなんて気にするな。そんなのは未完成OSASKアプリだ、なんなら osacmpじゃないアプリは起動できないような仕組みを付けるくらいの気 持ちで!」というアドバイスをもらったことがあったのですが、今にし て思えば、全くそのとおりだと思います。それくらいの認識ができるよ うになってこそ、「圧縮をサポートしたOS」と名乗るにふさわしいので す。SFX支援APIを持っているくらいで圧縮サポートOSだと思っていた過 去の自分が恥ずかしいです。 ・発展:オンデマンド展開 展開キャッシュと同じくらい重要だと思っている技術が他にもありま す。オンデマンド展開です。今のところワークエリアの初期化はいつも 全部実行されています。たとえば初期化するべきワークエリアが1MBあ って、でもプログラムは今回の実行ではその一部しか使わないかもしれ ません。しかしそういう場合でも、今のOSASKはバカ正直に全部のワー クエリアを初期化します。今回は玄関と居間しか使わないとしても、毎 回律儀に部屋が20個くらいある大邸宅を建てているようなものです。 これは展開キャッシュがあればそれほど気にならないかもしれません 。しかし、できることならこんな無駄なライトアクセスをしないほうが いいのです。それがうまくできるのなら消費メモリだって減るのです( 註:今のOSASKはリンク時にたくさんのワークエリアを指定したとして も、実際にアクセスしなければメモリを消費しない)。 この目的のために、展開コマンドを展開コマンドとは解釈せずに、デ ータマッピングファンクションとして解釈させようと思います。これな ら、実際の展開作業はその領域へのアクセスが起きてからになります( ページ例外が発生すると、そのページを含む展開ブロックだけが展開さ れる)。これで使わない部屋は準備しないということになり、効率がよ くなります。 これはワークエリアに限りません。日本語変換辞書や全角フォントの ような巨大ファイルにも適用可能です。そうするとおそらくteditcの起 動は今よりももっと速くなるでしょう(起動しながらプリロードをかけ ればいいと思っています。プリロードしておかないと、いざ変換キーを 押した瞬間にひっかかりそうなので)。 ・まとめ とまあ、そういういきさつで、将来のことを考えて二重圧縮はやめる ことにしたのです(やめるというか非推奨にした)。しかも無圧縮であ りながらも、ストリング命令による自前転送はやめて、APIで転送して もらうことにしました。どうしてかというと、APIなら実際に転送する も良し、オンデマンド展開用のマッピング処理で済ませるも良しで、シ ェルの意向を反映できるからです。 今回作ったTwitchell6のfffffff1サブファンクションは、まさにその ためのAPIで、bim2bin4uはそのサブファクションを積極的に使ってくれ る仕組み(-v48a)が付いたわけです。ということで今日からは-v48aオ プションの利用が推奨です(まあOSASK ver.4.8がリリースされるまで は、従来どおりの形式でも悪くはないと思います)。 ・おまけ 実は僕とI.Tak.さんくらいしか興味を持たない(=リンカを使わずに 直接naskやNASMのbinモードでアプリを作るような人しか関係ない)や やこしい話があるのですが、なんかもう書きつかれてきたので、それは またの機会にいたします。 それでは。 -- 川合 秀実(KAWAI Hidemi) OSASK計画代表 / システム設計開発担当 E-mail:kawai !Atmark! osask.jp Homepage http://osask.jp/