Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save na2co3-ftw/0b8772fa7debc2ddd6a6ec9275d47622 to your computer and use it in GitHub Desktop.
Save na2co3-ftw/0b8772fa7debc2ddd6a6ec9275d47622 to your computer and use it in GitHub Desktop.

異世界のコンピュータ ―2003fの紹介―

創作上の世界

創作界隈「悠里」では、世界観創作を行っています。
そこは、独自の地理、独自の歴史、独自の民族、独自の文化、独自の言語がある、創作上の世界です。

Fafs F. Sashimiによる小説 『異世界転生したけど日本語が通じなかった』 では、
日本人の主人公がその異世界に飛ばされてしまい、言語を学びながら、その世界の様々な文化等に触れている様子が描かれています。

異世界のコンピュータ

現実世界のコンピュータは、英語、ISO、IEEE、IBM、Intel等、様々な物、規格、団体の上で成り立っています。

異世界に、そういった物がそっくりそのままあるとは限りません。
つまり、異世界のコンピュータは、現実世界のものとは、違った基礎の元に成り立ち、違った体系をしているかもしれないのです。

以下の文章では、その世界の中から見た目線で、悠里の世界のコンピュータを紹介していきます。
創作上の都合や進捗などの、メタ的な事情は {{ }} で囲って書くことにします。

コンピュータについてある程度の知識があることを前提に書いています。

目次

2003処理機 (2003'd ferlesyl)

ピリフィアー暦2003年、ユエスレオネ大陸での出来事です。
ターフ・ヴィール・イェスカの率いる共産党が革命を起こし、ユエスレオネ連邦が成立しました。

当時はまだコンピュータの黎明期、既にいくつもの研究所等でコンピュータは作られていましたが、それぞれ独自に開発が進められてきたため、規格はバラバラでした。
そんな状況の中イェスカの指令によって、連邦情報処理研究所、通称FAFssが組織されました。
FAFssの仕事はそれらを集大成し、統一した規格を作ること。ピリフィアー暦2003年に開発が始まったこの規格は、2003処理機、通称2003fと呼ばれます。

2003fのアーキテクチャー

2003fは、CPU、メモリ、入出力機器などを組み合わせた、ひとつのシステムの規格です。
ここではそのうち、CPUとメモリのアーキテクチャーを簡単に紹介します。

2003fのCPU

2003fのCPUには、主に8つの32bitレジスタがあります。

レジスタ名 用途
f0 汎用、関数の返り値
f1 汎用
f2 汎用
f3 汎用
f4 {{未設定}}
f5 スタックポインタ
f6 {{未設定}}
xx プログラムカウンタ (次の命令のアドレス)

(f は レジスタ(firjal)の略、xx は「次に見るところ」(xeumon xelal)の略です。)

CPUの命令セットは主に2オペランド命令で構成されていて、非常に直交性の高いものとなっています。
命令セットについては、後でもう少し詳しく説明します。

2003fのメモリ

2003fのメモリは、1バイト=8ビットごとにメモリアドレスが割り当てられています。
CPUが主に32bitで演算するため、メモリアクセスも通常は32ビット=4バイト単位で行います。

メモリ上で2バイト以上のデータは、ビッグエンディアンで配置されます。
例えば 0x12345678 という値を、アドレス 0x00001000 に置いた場合、次のようになります。

アドレス
0x00001000 0x12
0x00001001 0x34
0x00001002 0x56
0x00001003 0x78

2003処理機言語 (2003f'd lkurftless)

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 (f0f1 にコピー)を行い、偽の場合は何もしません。

(比較命令以外の命令がフラグレジスタを変更するかどうかは未定義です。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 とすることで f0f1 の値を交換する等の使い方ができます。

オペランド順

今までは 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整数)

リンク集

総合

実行環境

  • 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のアセンブラとエミュレータです。

その他

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment