創作界隈「悠里」では、世界観創作を行っています。
そこは、独自の地理、独自の歴史、独自の民族、独自の文化、独自の言語がある、創作上の世界です。
Fafs F. Sashimiによる小説 『異世界転生したけど日本語が通じなかった』 では、
日本人の主人公がその異世界に飛ばされてしまい、言語を学びながら、その世界の様々な文化等に触れている様子が描かれています。
現実世界のコンピュータは、英語、ISO、IEEE、IBM、Intel等、様々な物、規格、団体の上で成り立っています。
異世界に、そういった物がそっくりそのままあるとは限りません。
つまり、異世界のコンピュータは、現実世界のものとは、違った基礎の元に成り立ち、違った体系をしているかもしれないのです。
以下の文章では、その世界の中から見た目線で、悠里の世界のコンピュータを紹介していきます。
創作上の都合や進捗などの、メタ的な事情は {{ }} で囲って書くことにします。
コンピュータについてある程度の知識があることを前提に書いています。
ピリフィアー暦2003年、ユエスレオネ大陸での出来事です。
ターフ・ヴィール・イェスカの率いる共産党が革命を起こし、ユエスレオネ連邦が成立しました。
当時はまだコンピュータの黎明期、既にいくつもの研究所等でコンピュータは作られていましたが、それぞれ独自に開発が進められてきたため、規格はバラバラでした。
そんな状況の中イェスカの指令によって、連邦情報処理研究所、通称FAFssが組織されました。
FAFssの仕事はそれらを集大成し、統一した規格を作ること。ピリフィアー暦2003年に開発が始まったこの規格は、2003処理機、通称2003fと呼ばれます。
2003fは、CPU、メモリ、入出力機器などを組み合わせた、ひとつのシステムの規格です。
ここではそのうち、CPUとメモリのアーキテクチャーを簡単に紹介します。
2003fのCPUには、主に8つの32bitレジスタがあります。
| レジスタ名 | 用途 |
|---|---|
f0 |
汎用、関数の返り値 |
f1 |
汎用 |
f2 |
汎用 |
f3 |
汎用 |
f4 |
{{未設定}} |
f5 |
スタックポインタ |
f6 |
{{未設定}} |
xx |
プログラムカウンタ (次の命令のアドレス) |
(f は レジスタ(firjal)の略、xx は「次に見るところ」(xeumon xelal)の略です。)
CPUの命令セットは主に2オペランド命令で構成されていて、非常に直交性の高いものとなっています。
命令セットについては、後でもう少し詳しく説明します。
2003fのメモリは、1バイト=8ビットごとにメモリアドレスが割り当てられています。
CPUが主に32bitで演算するため、メモリアクセスも通常は32ビット=4バイト単位で行います。
メモリ上で2バイト以上のデータは、ビッグエンディアンで配置されます。
例えば 0x12345678 という値を、アドレス 0x00001000 に置いた場合、次のようになります。
| アドレス | 値 |
|---|---|
0x00001000 |
0x12 |
0x00001001 |
0x34 |
0x00001002 |
0x56 |
0x00001003 |
0x78 |
2003処理機言語、通称2003lkは、2003fのためのアセンブリ言語です。
再帰的な構文木がなく、改行も単なる空白文字として扱われます。
2003lkは、とても簡単に構文解析ができるように設計されています。
2003lkではオペランドとして、即値、レジスタ、メモリが使用できます。
以下によく使われるオペランドの例を挙げます。
| 例 | 説明 |
|---|---|
16 |
即値は数字をそのまま書きます。 |
f2 |
レジスタです。 |
f5@ |
レジスタ間接参照のメモリアクセスです。 f5レジスタの値をメモリアドレスとして、メモリにアクセスします。 |
f5+4@ |
レジスタ間接参照には、オフセット値を指定できます。 f5レジスタの値に4加えた値をメモリアドレスとして、アクセスします。 |
rinyv |
識別子です。識別子は主にラベル名として使われています。 |
まずはコピー命令 krz の紹介。
krz f0 f1 とすると、レジスタ f0 の値を レジスタ f1 にコピーします。
レジスタ間のコピーだけでなく、krz 1 f0 で即値 1 を レジスタ f0 に代入や、
krz f5@ f5+4@ で メモリ(アドレス: レジスタ f5) から メモリ(アドレス: レジスタ f5 + 4) など、
先に挙げたオペランドを自由に使うことができます。
(即値は読み取り専用なので、krz f0 1 のように、値を書き込むことはできません。)
算術命令も、基本的にはコピー命令 krz と同様です。
減算命令 nta を例として紹介すると、
nta f0 f1 は、 レジスタ f1 の値を、レジスタ f0 の値の分だけ減算します。
同様に、加算や、種々のビット演算の命令もあります。
乗算命令 lat はオペランドを3つ指定します。
lat f0 f1 f2 は、 レジスタ f0 × レジスタ f1 を64bitで計算し、下位32bitを f1 に、上位32bitを f2 に代入します。
乗算した結果の上位が不要な場合は lat f0 f1 f1 のように、上位と下位で同じオペランドを指定することで無視できます。
比較命令は fi オペランド オペランド 比較方法 の形式をしています。
例えば fi f0 f1 xtlo は、レジスタ f0 が レジスタ f1 以下であるかを判定します。
比較の結果は真偽値で、CPUのフラグ用レジスタに代入されます。
比較結果によって動作を変えるには malkrz 命令を使います。
malkrz f0 f1 は、フラグレジスタが真の場合は krz f0 f1 (f0 を f1 にコピー)を行い、偽の場合は何もしません。
(比較命令以外の命令がフラグレジスタを変更するかどうかは未定義です。malkrz は比較命令の直後に置くようにしましょう。)
nll ferlk1 krz f0 f1 とすることで、krz f0 f1 の命令に対して ferlk1 というラベルを付けることができます。
また同じ意味で krz f0 f1 l' ferlk1 と、 命令の後にラベル名を書くこともできます。
ラベル名もオペランドとして使うことができ、そのラベルが付けられた場所のアドレスを表し、即値と同様に扱われます。
2003lkには、フロー制御専用の命令はありません。プログラムカウンタの xx を直接操作してフロー制御を行います。
まず単純なジャンプの例を挙げます。
命令1
命令2
krz ferlk1 xx
命令4
命令5
nll ferlk1 命令6
命令7
この場合、krz ferlk1 xx を実行した時に、プログラムカウンタには、ferlk1 のアドレス、つまり命令6のアドレスが代入されます。
結果として、命令1、命令2、krz ferlk1 xx、 命令6、 命令7 の順で実行されます。
条件によって実行順序を分岐したい場合は、krz の代わりに malkrz を使います。
そうすることで、直前の比較命令の結果が真であった時だけジャンプできます。
2003lkでは レジスタ f5 をスタックポインタとして使用します。スタックはいわゆる「下に伸びる」使い方をします。
; スタックに10を詰む
nta 4 f5
krz 10 f5@
; スタックからポップする
krz f5@ f0
ata 4 f5
上記の前半は、スタックに10を詰む操作です
スタックポインタ f5 を4減算して、スタックポインタの指すメモリに10を書き込んでいます。
後半は、スタックから値をポップし、レジスタ f0 に代入しています。
スタックポインタの指すメモリからレジスタに値をコピーし、スタックポインタに4加算します。
また、f5+4@ のようにすることで、スタックからポップせずに、スタック先頭の1つ前の値を読むようなこともできます。
関数呼び出しは、スタックを利用して行います。
関数を呼び出す時は、引数を左から順番にスタックに詰んでいき、最後に戻り先アドレスをスタックに詰んで、関数の先頭にジャンプします。
これをそのままプログラムで表現すると、例えば cersva1(1, 2) のような関数呼び出しは以下のようになります。
nta 4 f5 ; スタックに1を詰む
krz 1 f5@
nta 4 f5 ; スタックに2を詰む
krz 2 f5@
nta 4 f5 ; スタックに戻り先(dosnudalラベル)を詰む
krz dosnudal f5@
krz cersva xx ; 関数の先頭にジャンプする
nll dosnudal ata 12 f5 ; 戻り先: スタックを元に戻す
この方法だと、戻り先のラベルを用意する必要があって面倒です。そこで、inj という便利な命令を使います。
inj f0 f1 f2 は、 f0 の値を f1 に、また f1 の値を f2 に書き込みます。
inj cersva1 xx f5@ とすることで、cersva1のラベル値をプログラムカウンタに、プログラムカウンタの値をスタックの先頭に書き込みます。
つまり、関数の先頭へジャンプしながら、プログラムカウンタ(次に実行する命令のアドレス)をスタックに詰むことができます。
引数をスタックに詰む操作も、少し最適化することができて、最終的に以下のようなプログラムになります。
nta 12 f5 ; スタックを3つ分準備
krz 1 f5+8@ ; スタックに1を詰む
krz 2 f5+4@ ; スタックに2を詰む
inj cersva1 xx f5@ ; 関数呼び出し
ata 12 f5 ; スタックを元に戻す
値を返す関数の場合、返された値はレジスタ f0 に入っています。
ちなみに inj は関数呼び出し以外でも、inj f0 f1 f0 とすることで f0 と f1 の値を交換する等の使い方ができます。
今までは krz f0 f1 で 「f0 を f1 に」コピーするとして説明してきました。このオペランド順は 'i'c 順 (をに順) と呼ばれます。
一方でこれとは逆の順番 krz f1 f0 で 「f1 に f0 を」コピーすることを表す、 'c'i (にを順) で書くこともできます。
アセンブラにオペランド順を示すため、基本的にはプログラムの最初でそれぞれ、'i'c、 'c'i というディレクティブを書きます。
オペランド順指定のディレクティブはプログラムの途中で書くこともできます。
その場合は、ディレクティブの場所からオペランド順を変更し、以降の部分のオペランド順を指定することになります。
なお、比較命令のオペランド順は変わりません。
2003lkのプログラムをコピペする時は、オペランド順が一致しているか十分に気を付けましょう。
また、プログラムの断片を人とやりとりする場合も、オペランド順を明示するようにしましょう。
2003fを開発したFAFssのメンバーは、様々な地域から集めてきた技術者が含まれていました。
技術者によって母語が違っていたため、それぞれにとって自然な語順が違いました。
そのために、どちらの語順も可能で、また一つのプログラム中でも語順が混在できるような仕組みになったのです。
『異世界転生したけど日本語が通じなかった』 #135 ダラーワ より
'c'i
nll pom2
krz f0 f5+4@
fi f0 2 xylo malkrz xx halt
krz f1 f0
nta f1 1
nta f5 4 krz f5@ f1
nta f5 4 inj f5@ xx pom2 ata f5 4
inj f1 f5@ f0
nta f1 1
nta f5 4 krz f5@ f1
nta f5 4 inj f5@ xx pom2 ata f5 8
ata f0 f5@
ata f5 4
krz xx f5@
l' halt
これは整数を1つ受け取って、整数を返す関数 pom2 を定義しています。再帰呼出を利用してフィボナッチ数を返す関数です。
| 'i'c 順 | 'c'i 順 | 動作 |
|---|---|---|
krz src dst |
krz dst src |
dst ← src |
malkrz src dst |
malkrz dst src |
if (flag) dst ← src |
fen |
fen |
noop |
inj a b c |
inj c b a |
(tmp) ← b b ← a c ← (tmp) |
| 'i'c 順 | 'c'i 順 | 動作 |
|---|---|---|
ata src dst |
ata dst src |
dst ← dst + src |
nta src dst |
nta dst src |
dst ← dst - src |
lat src dstL dstH |
lat dstL dstH src |
(tmp) ← dstL * src dstH ← (tmp)の上位32bit dstL ← (tmp)の下位32bit (オペランドは符号なし整数) |
latsna src dstL dstH |
latsna dstL dstH src |
同上 (オペランドは符号付き整数) |
ada src dst |
ada dst src |
dst ← dst & src |
ekc src dst |
ekc dst src |
dst ← dst | src |
nac dst |
nac dst |
dst ← ~dst |
dal src dst |
dal dst src |
dst ← ~(dst ^ src) |
dto src dst |
dto dst src |
dst ← dst >>> src (srcが64以上の場合は未定義) |
dtosna src dst |
dtosna dst src |
dst ← dst >> src (srcが64以上の場合は未定義) |
dro src dst |
dro dst src |
dst ← dst << src (srcが64以上の場合は未定義) |
| 動作 | |
|---|---|
fi a b xtlo |
flag ← a <= b (符号付き整数) |
fi a b xylo |
flag ← a < b (符号付き整数) |
fi a b clo |
flag ← a == b |
fi a b xolo |
flag ← a >= b (符号付き整数) |
fi a b llo |
flag ← a > b (符号付き整数) |
fi a b niv |
flag ← a != b |
fi a b xtlonys |
flag ← a <= b (符号なし整数) |
fi a b xylonys |
flag ← a < b (符号なし整数) |
fi a b xolonys |
flag ← a >= b (符号なし整数) |
fi a b llonys |
flag ← a > b (符号なし整数) |
| 'i'c 順 | 'c'i 順 | 動作 |
|---|---|---|
krz8i src dst |
krz8i dst src |
dst ← 符号拡張(src) (srcは8bit整数) |
krz16i src dst |
krz16i dst src |
dst ← 符号拡張(src) (srcは16bit整数) |
krz8c src dst |
krz8c dst src |
dst ← srcの下位8ビット (dstは8bit整数) |
krz16c src dst |
krz16c dst src |
dst ← srcの下位16ビット (dstは16bit整数) |
- 悠里総合サイト
創作界隈「悠里」の総合サイトです。 - 悠里世界のOSのエミュレータ作ろうぜという計画
悠里世界のコンピュータ関連の創作の本拠地です。 - 2003lk入門
コンピュータ知識がない人のための2003lk入門です。 - 【人工言語】リパライン語
2003fの用語の多くは、悠里世界で話されている言語の1つ、リパライン語です。
- 2003lkインタプリタ
Haskellで実装された2003lkのインタプリタです。 - 2003'd ferlesyl Editor
ブラウザ上で2003lkやその関連言語を実行できるIDEです。
手軽に2003fを動かせるお勧めの実行環境です。 - Nobuyuki-Tokuchi/2003f
C#で実装された2003lkのアセンブラとエミュレータです。
- tinka
2003lkにコンパイルされる中級言語です。変数に名前を付けられます。 - tinka簡易入門
2003lkや2003fが分からなくても分かるように書いた、tinkaの簡易入門です。
(西暦2019年1月ごろのtinkaの大幅な仕様変更以前に書いたページです。) - cent
2003lkにコンパイルされる中級言語です。スタック指向です。 - ata2003lk
2003lkに少し機能を追加して便利にした言語です。2003lkにコンパイルされます。 - ubpl
2003fに準拠しつつ科学計算用途に特化したアーキテクチャーubplのアセンブラとエミュレータです。
- llvm-2003f
2003lkを出力するLLVMバックエンドです。 - llvm-2003f 処理の流れの例
具体例を出してllvm-2003fの処理を詳細に追った記録です。