ために策定されたアセンブラ言語です!そのCASL2をWebブラウザ上で書いて実行できるWebサービスをつくりましたという日記です。github.comなぜつくったのか?自分が欲しいとかこういう人に使って欲しいという理由ではなく、自分の勉強のためにつくりました。勉強のための車輪の再発明。今年の2月の記事ですが、「計算機プログラムの構造と解釈」という本を読み始めました。レジスタマシンによる計算」がうーむ。いう感じだったので似たようなものつくってみようと思ったのがきっかけです。アセンブラ言語に全然馴染みがなくパッと思いついたのがCASL2だったので、Webブラウザ上で試せるようなツールをつくってみるか!とTypeScriptで実装しました。UIはJavaScriptでDOMをいじいじ...「計算機プログラムの構造と解釈」のSchemaで実装されたアセンブラを元に、TypeScriptで実装していく形で実装を始めました。途中で家で積ん読になってた「言語実装パターン」という書籍を見つけこちらも途中から参考にします。最初から読んでいれば...という気持ちになるところもあり、すでに作り直したい部分がちらほら。「計算機プログラムの構造と解釈」は、先日のブログ記事でも紹介させていただきましたが、hiroshi-manabeさんによる翻訳を読みました。github.com「言語実装パターン」は、O'Reillyの本です。ParrオライリージャパンAmazon開発メモアセンブラってなんですか?「計算機プログラムの構造と解釈」でのレジスタマシンとアセンブラの記述を自分なりのざっくり理解を書くとレジスタと呼ばれる記憶素子の中身を操作する命令を順に実行するコンピュータがレジスタマシンレジスタマシンを設計するためには、レジスタと演算を表すデータパス、それらの演算を正しく並べるコントローラが必要で、図で書いて表現できる「計算機プログラムの構造と解釈」では最初の例として最大公約数を求めるアルゴリズムを実現するレジスタマシンのデータパスとコントローラについて書かれてるだけど複雑になってくるとすぐに図で記述できなくなるそこで、レジスタマシン用言語で作って記述しよう。ために設計された仮想のコンピュータ=レジスタマシン)の機械語に変換し実行する、ということになる。バイトコードアセンブラ」の章で人間によって読みやすいテキスト形式でできたアセンブリ言語プログラムをバイナリ形式のバイトコード命令へと変換します。同様のことが記述されてる。「言語実装パターン」では、アセンブラは4つの重要要素があるとも記述されている。大域データ空間の大きさコードメモリmainプログラムのアドレス定数プール大域データ空間はメインメモリのことで、COMMET2のメインメモリは、1語16ビットで65536語の容量です。コードメモリというのは、メインメモリにアセンブラによって生成されたバイナリ形式の機械語そのものが格納される、ということ。mainプログラムのアドレスは、メインメモリのどのアドレスがプログラムの開始位置なのかを示す。オペランドをコードメモリ内に収まらないので別のアドレスに保管することを示していて、CASL2で言うとソースコードに記述されるラベルが該当します。テキスト形式のソースコードから変換したバイナリ形式形式の機械語も定数も同じメインメモリ上に置かれる。「計算機プログラムの構造と解釈」を読んでいてあまり腹落ちしなかったのは、この機械語とメインメモリとが具体的に出てこなかったからなのでは。メモリもリストとGCのところで抽象化してたからかもしれないなぁ。書き出しを読み直すと以下のように書いてある。典型的なレジスタマシンの命令は、レジスタの中身に基本的な演算を適用し、その結果をほかのレジスタに割り当てるというものです。レジスタマシンにより実行されるプロセスをこのように記述すると、従来式コンピュータの"機械語"に非常によく似ているように見えます。ここではある特定のコンピュータの機械語を扱うのではなく、いくつかのLisp手続きについて調べ、それぞれの手続を実行するための個別のレジスタマシンを設計していくことにします。そのため、課題に対する私たちのアプローチは、機械語のプログラマというよりも、ハードウェア設計者としての視点によるものとなります。「ある特定のコンピュータの機械語を扱うのではなく」のせいでかえって取っ付きづらくなっている気がする。ハードウェア設計者としての視点...ワカラン...ラベルの取り扱いCASL2のソースコードを見ていて、普段のプログラミング言語と明らかに違うなぁと思うのはラベル。ソースコードのとある行にラベルづけをしておく。if文で条件分岐するようなことをしたい場合は、条件を満たす場合はラベルAにジャンプ、条件を満たさない場合はラベルBにジャンプというように使う。他にも関数呼び出しのようなことをしたい場合もラベルを使うし、定数名のようにラベルを使うこともできる。「計算機プログラムの構造と解釈」では以下のように記述されている。アセンブラは、命令実行手続きを生成する前に、ラベルの参照先をすべて知っておく必要があります。以下のようなコードがあったとする。アドレスにセットされている数値を加算する、というコード。登場していないため、存在するラベルなのか?どの位置を指しているのか?ということが分からない。登場していないラベル(や関数名や変数や色々)を参照することを前方参照と言うらしい。いうMapにラベル毎に集約しておく。集約する過程で各行の命令とオペランドからバイト数が分かるのでラベルのメモリアドレスも分かる。集約後、Mapの全エントリをループすることで各行をもう一度走査することで全てのラベルにセットされた値も把握しながら解析できる。これ、ソースコード全体を2回ループしてるし、同じようなやってるような感じもするけどどうなのか。構文手動変換器」であると書かれている。ここで言う構文手動変換器とは、入力1行を読んだら変換先の1行を生成する、というもので前方参照を扱えないパターンと書かれている。ラベルの前方参照ができないと困るじゃないか。いう方法が紹介されている。フラグというのを用意する。Array<行>>先程のCASL2コード例をもう一度。いうラベルを見つけたときに、以下のようにラベルMapとforwardRefsに追加する。ラベルが定義されると、「後から前方参照を解決すべき被演算数のリスト」を走査して修正する。currentMemAddress})こういうイメージか。たしかにソースコードの行すべてをもう一度走査することに比べたら「後から前方参照を解決すべき被演算数のリスト」を走査した方が効率良さそう。構文手動変換器パターンのように、ソースコード全体を1回だけ走査することを1パスと呼ぶみたい。1パスは単純だけど、そのままだと前方参照ができない、入力と大きく並び順が異なる出力を生成できない、といったデメリットがある。1パスに対して、そうでないものをマルチパスと呼ぶらしい。おわりに学びたいポイントが次から次へと出てきて困るけど楽しいという感じでした!自分の実装は、アセンブラとして機械語を生成する処理と、Commet2上で動きをシミュレートするための関数と、両方をごちゃっと実装してしまっている感じがあって分かりづらい。参考しながら機械語を入力とするインタプリタとして作り直すと良さそう。他にもbackpatchingで書き直したり、全然別の構文手動変換器ではない変換器をつくってみたりもしたいなぁ。1検索はてなブログをはじめよう!bati11さんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。