* アセンブラ短歌のページ #0000
-(by [[K]], 2013.07.22)
** (0) はじめに
-さてそもそもアセンブラ短歌とは何か?
--アセンブラ短歌とは、坂井弘亮先生が発明した非常に高度で文化的な遊びです(これはきっと10年後のテストに出ます!)。
--好きなCPUを選んで、そのCPU向けに5+7+5+7+7=31バイトのプログラムを作ります。
--そしてそのプログラムについて、バイトの並びなどを人間が鑑賞するのです。
--http://kozos.jp/documents/LT_asm_tanka.pdf  ← 将来重要文化財に指定される予定の資料ですので丁寧に扱ってください。

-いくつか細かいルールがあります。それは以下で補足します。
--(1) アプリケーションヘッダ的なものは基本的に31バイトに加算されない。OSECPUでいえば、最初の 05 E1 はノーカウント。
--(2) 31バイトはすべてCPUによって詠まれる(=実行される)必要がある。
---つまり文字列データを31バイト内のどこかに置いて、それを1バイトずつ読みながら文字表示するなんていうのはルール違反。
---メモリを確保して、そこへ機械語でデータを書き込んで、そのデータを使って表示させるのはOK。
--(3) 機械語命令は、5,7,5,7,7の区切りで切れなければいけない。つまり8バイト命令とかは使用不能。
---7のところに3+2+2を詰め込むことはOK。

-アセンブラ短歌では文字表示が特に好まれていますが、Kの独自の解釈によれば(=川合派)、これはアセンブラ短歌にとって必須の要素ではありません。文字表示にこだわるのは、もはや古典と言ってもいいかもしれません。
--しかしそれゆえに正統派とも言えます。
--なんにせよ、まずは基本である正統派を理解していただきます。

-[基本]
--上記のPDFにあるとおり、x86(32bit)のLinux向けのアセンブラ短歌では、13バイトの任意のアルファベットを出力することができます。すごいですね。
--しかしこれはもう限界を極めてしまった感があって、この記録は越えられそうにないので、この方面で競ってもあまり面白くはありません(それでも基本は大切なので、タイ記録を作って遊ぶことはできます)。

--アセンブラ短歌では、みんなが知らないマニアックな命令(たとえばJECXZとか)をうまく混ぜてみたり、もしくは何度か同じバイトで終わって「韻を踏んで」みたり、まあそんな感じで、鑑賞に値する何かをやればいいのです。
--なんかえらく複雑で、逆アセンブルしても(実行してみるまで)「結局何が表示されるのかさっぱりわからない」なんていう、超難読化プログラムも面白そうです。
--僕たちはOS屋なので、ブートセクタ用にBIOSを叩いて面白いことをやることもできそうです。
--結局問われているのは、プログラミングテクニックではなく、機械語に対する愛なのです。

-[発展]
--さて僕たちにはOSECPUがあります。OSECPUだって立派なアーキテクチャだと思うので、これで短歌を詠むことができます。
--というかx86はすでに時代遅れですよね。これからはOSECPUですよ。10年後の教科書には、x86は古文として紹介されると思います。
--で、OSECPUの機械語でアセンブラ短歌すると、22バイトの任意のアルファベットを出力することができます。
--というか5+7+5の17バイトだけでも12バイト出力できちゃいます。グラデーションも11バイト(ヘッダは数えないから)で書けるので、これを5+7+5に収めることはできそうです。
--ただまあ、OSECPUは機能密度において世界最強なのは当たり前なので、これで圧倒しても圧倒されないかもしれません。
--だとすると、人間を面白がらせるためには何をしたらいいんでしょうかねえ・・・。

-以上の文章を書いた後で坂井先生による解説ページを発見: http://kozos.jp/asm-tanka/

** (1) OSECPUの実力
-アセンブラ短歌においてOSECPUがどれほどすごいかを分かってもらうために、いくつかのプログラム例を紹介しようと思います。
-おっとその前に・・・もしOSECPUの機能密度のことをよく知らないままこのページを読み始めた人は、[[page0040]]を先に読んでおくといいと思います。

~
-[1-1] それではまずは古典で22文字出力の例を(22BPT):
 #include "osecpu_ask.h"
 
 junkApi_putConstString0('Wak');
 junkApi_putConstString0('ayama');
 junkApi_putConstString0('Bok');
 junkApi_putConstString0('usuiW');
 junkApi_putConstString6('akayam');
 
 51 03 57 61 6B
 51 05 61 79 61 6D 61
 51 03 42 6F 6B
 51 05 75 73 75 69 57
 51 6C 3A F0 F9 C3 B4
--解説。OSECPUでは0.5バイト単位で命令を記述します。そして"5"はAPI呼出し命令で、その後に引数が後続します。"5"の次の"1"は機能番号であり、これは文字列表示APIを意味します。だからみんな51で始まっているわけです。
--「51 03 57 61 6B」についてもう少し詳しく見てみます。51の次の0は、文字列をどのようにエンコードしたかという情報を表しています。モード0は、UInt8形式です。次の3は文字列長を意味しています。「57 61 6B」はもちろん'Wak'のASCIIコードです。
--つまりOSECPUでは、たった5バイトだけでも任意の3文字を出力できてしまうのです。
--これはルールの(2)に違反しているように見えるかもしれません。・・・いやいや、とんでもない。これはデータなどではなく、れっきとしたプログラムです。JITコンパイラによって実行されるときには、
---R30レジスタに機能番号が代入されて、
---R31レジスタには文字長3が代入され、
---スタックから3バイトを確保し、
---そこへ律儀に57, 61, 6Bをひとつずつ書き込んで、
---そのポインタをP31に格納して、
---PCALL(P28);を実行して、
---その後スタックを開放する、
--という長いプログラムにちゃんと変換されているのです。なにかデータのように見えてしまうのは、OSECPUは徹底して機能密度を追求した結果、必要最低限の制御情報だけになり、データばかりになってしまったためです。
--ここまでの説明で、最初の4行については完全に理解できると思います。問題は最後の1行です。これはモード6です。
--モード6は文字列長の記述はなく、0という終端文字がくるまで文字列が続くとみなします。しかもモード6はUInt8ではなくUInt7なのです。つまり7ビットエンコードなのです。「C3AF0F9C3B4」を2進数に変換して、それを7ビットずつ区切って、それを16進数で表すと「61 6B 61 79 61 6D」にちゃんとなります。これでは終端文字がないではないかと思うでしょう。しかしOSECPUではファイル終端を検出すると0終端処理が行われるのでこれで問題ありません。とまあそんなわけで、最後の行に関しては7バイトで6文字を出力できているわけです。
--おっとこれは終了処理が何もありませんね。でもOSECPUは終了処理を書かなくてもファイル終端に正常終了処理を付与してから実行するので問題ありません。

~
-[1-2] 5+7+5の川柳で12文字を出力する際には、以下のようにします。もはや説明は不要でしょう。
 51 03 xx xx xx
 51 05 xx xx xx xx xx
 51 6x xx xx xx
-[1-3] 「そもそも1文字しか出さなくていいとしたら、どこまで短くできるのか」なんてことを検証したこともあります。任意の1文字、ではなくて、0とかPとか、そういう下位3ビットが000のものに限定すれば、2バイトでできてしまいます。
 51 66 // "0"が出力されます
-x86だと2バイトではレジスタに文字コードを入れるのが精いっぱいですね。

~
-[1-4] 下の[4-1]をベースに短歌を作りました。
 60 0F FF FC 65
 59 61 11 14 18 30 B0
 51 0F F2 20 3D
 59 61 11 14 28 80 B0
 51 0F FF FF F2 62 0A
--出力結果はこうなります。
   0 =       0b
   1 =       1b
   2 =      10b
   3 =      11b
   4 =     100b
   5 =     101b
   6 =     110b
   7 =     111b
   8 =    1000b
   9 =    1001b
  10 =    1010b
 (中略)
  98 = 1100010b
  99 = 1100011b
 100 = 1100100b
--596系の命令は、まあとにかく優秀でまさにprintf(っぽいもの)なのですが、しかし命令長が長いので短歌ではなかなかうまく使えません。しょうがないので"%d"(相当)だけにして7バイトに切り詰めました。そしたらやっと短歌化できました。
--ここまで作ってみて気づいたのですが、OSECPUにおいてはアセンブラ短歌のときは5命令、川柳のときは3命令になる傾向があるようです。5バイト命令と7バイト命令ばかり使っています。

-(つづく)

** (2) ニブル短歌、ニブル川柳
-(1)の川柳で12文字が出せてしまうことからもわかるように、OSECPUは機能密度が高くて、アセンブラ短歌で競っても勝って当たり前になってしまい面白くありません。もちろんこれで浮かせたバイト数を使って韻を踏むなどの「文学的装飾」をほどこすこともできますが、やっぱりそれもOSECPUだからそれくらいできて当たり前だろ、という感じになって、なんか楽しめません。ということで難易度を上げます。
-どうするのかというと、バイト単位で57577するのではなく、ニブル単位で57577します。ニブルというのは4bitのことです。OSECPUは命令体系がニブル単位なので、ちょうどいいです。
-そもそも短歌の31文字は音というかリズムを楽しむものです。256バイトを短歌のリズムで読み上げるためには、256種類の音が必要です。日本語は51音で、まあ濁音とか拗音を入れれば2~3倍には増やせるかもしれませんが、それでも256音というのはちょっと無理くさいと思います。ニブルなら16音しかいらないので余裕で読めます。
-ルールは基本的にはアセンブラ短歌と同じですが、命令を行に分割してもいいことにします。ただし命令の第一オペランドと第二オペランドの境界で切れてもいいですが、第一オペランドの途中で切れるとかはダメです。
-ニブル川柳なんてもはや不可能と思うかもしれませんが、ちゃんとできるということをまずは示そうと思います。8バイトのcharsはこうでした(参考:[[page0040]])。
 60C20C7F
 514B
-これをニブル川柳にしてみます。読み仮名も付けました。
 60C20   - むおしにお
 C7FFF51 - しなふふふごい
 FF4B0   - ふふよぶお
-あちこちにFが入っていますがこれはOSECPUにおけるNOPみたいなものです。NOP命令(0)との違いは、NOPは「何もしない命令」であるのに対し、Fは「意味のないプリフィクス」なのです。Fは命令に対するプリフィクスではなく可変長数値エンコードに対するプリフィクスなので、オペコードの前はもちろんのこと、オペランドの前にも好きなだけ付けられます(つける個数に制約はない)。
-読み仮名は「おいにみよごむなやくあぶしでえふ」(0123456789ABCDEF)とかはどうかなと思って割り振りました。0をレと読ませる余地もあります。3をサと読ませる余地もあります。
-このコードにはまだいじる余地があります。FF5はF85と書くことができますし、C05と書くこともできます。

~
-次は13バイトのグラデーションをニブル短歌にしてみます。ヘッダを取って11バイト、つまり22ニブルです。川柳には収まりませんが、短歌にはできそうです。加工前の元のプログラムはこれです。
 600DB0 610DB0 4520 942DB0
-これを短歌にしたらこうなりました。
 68080   - むやおやお
 DB06180 - でぶおむいやお
 FFDB0   - ふふでぶお
 4520942 - よごにおくよに
 F74FFB0 - ふなよふふぶお
-ちなみに何度も出てくる「でぶお」は256という定数を表しています。とても切りのいい数字なので美しいです。「デブ夫」などと想像してはいけません。

~
-川柳は17ニブルで8.5バイト、つまりヘッダ付きでも11バイト未満なので、さすがにOSECPUでもどんどんアプリを作れるというわけではなさそうです。しかし短歌のほうは31ニブルで、これはつまりヘッダ込みで18バイト弱もあるので、いろいろできます。hello, worldもできそうです。
-俳句では季語を入れることが義務になっていて、それが文学性を著しく高めましたが、ニブル川柳、ニブル短歌にも「固有名詞(らしきもの)」を入れることを要請するといいかもしれません。charsでは「ヨブ夫」くんが入っています。


-(つづく)

** (3) hh4のエンコードテーブル
-hh4(えいち・えいち・ふぉー)というのは、OSECPUのフロントエンドバイトコードが採用しているエンコード方式のことです。OSASK計画で開発された技術です。
    4ビット形式: 0xxx (ただし0111は使えない) U:0...6,     S:-3...+3
    8ビット形式: 10xx xxxx                     U:0...3F,    S:-F...+1F,  R00...R10
   12ビット形式: 110x xxxx xxxx                U:0...1FF,   S:-B0...+FF, R00...R3F, 100, 200, 400, 800,...
   16ビット形式: 1110 xxxx xxxx xxxx           U:0...FFF,   S:
   24ビット形式: 0111 0100 xxxx xxxx xxxx xxxx U:0...FFFF,
   28ビット形式: 0111 0101 xxxx ...
   32ビット形式: 0111 0110 xxxx ...
   36ビット形式: 0111 1000 0111 xxxx ...
   以下略
--符号付き整数の4bit形式では、0110が-1, 0101が-2, 0100が-3を表す。
--符号付き整数の8bit形式では、10110000がR00を表す。10101111はR01を表し、10100000はR10を表す。
-- -10以下の整数をエンコードするときは、最低でも12ビット形式にする必要がある。-10は1101_1010_0000になる。イメージとしては50を引いた値を押し込む。

** (4) 没ネタ集
-ここは、短歌を目指してやってみたけど、うまくいかなかったもののうち、まあお蔵入りさせるはさすがにもったいないかなと思ったものを書き溜めるところです。

~
-[4-1] 進数変換表表示プログラム
   0(10) =       0(2) =  0(16)
   1(10) =       1(2) =  1(16)
   2(10) =      10(2) =  2(16)
   3(10) =      11(2) =  3(16)
   4(10) =     100(2) =  4(16)
 (中略)
  98(10) = 1100010(2) = 62(16)
  99(10) = 1100011(2) = 63(16)
 100(10) = 1100100(2) = 64(16)
--この表示を得るために必要なアプリを作ってみたところ、ヘッダ抜きで32バイトでした。1バイト多い!・・・これじゃあ区切り調整以前の問題です。ということで没。
--まあ(2)をBにして、(16)をHにしたりすれば4バイトくらい減るかもしれないけど、なんかそれは短歌のために表示内容を犠牲にする感じがして、ちょっと気が乗りません。
 6 0 0 C65   // for (i = 0; i != 101; i++) {
 5 96 6 02A18B0 5281E81 50C94A0 7A05431 6CA4500  // "\1(10) =\1(2) =\1(16)\n"
      1 8C 4 3 0 B0 2 88 0 B0 0 3 0 B  // (10進, 3桁, スペース削らない, i), (2進, 8桁, スペース削らない, i), (16進, 3桁, スペース削らない, i)
--とかいいつつ、結局短歌にしました。→[1-4]

* こめんと欄
#comment

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS