YACASL2は、UNIX/Linuxのコマンドラインインターフェイスで動作するオープンソースのCASL II処理システムです。CASL IIは情報処理試験で用いられるアセンブラ言語で、次の資料により仕様が公開されています。
試験で使用する情報処理用語・プログラム言語など(2008年10月版)[PDFファイル] 別紙 1 アセンブラ言語の仕様
YACASL2は、CASL IIアセンブラ言語で記述されたファイルをアセンブルし、仮想マシンCOMET II上で実行します。アセンブルと実行は、連続で行うことも別々に行うことも可能です。 YACASL2の動作はCASL IIの仕様に準拠しているため、情報処理試験の問題をはじめ各種参考書やサイトに記載されたCASL IIのプログラムをそのままアセンブルして実行できます。また、本パッケージ中にCASL IIのサンプルプログラムが多数収録されています。
YACASL2は、「ふつうの処理系」として動作します。ほかの多くのCASL IIエミュレータと違い、デバッガとして動作したり、コンピュータ内部の模式図を表示したりすることはありません。そのかわり、YACASL2は、次のような動作内容をすべてテキストで出力します。
出力された動作内容は、GNU/Linuxのさまざまなツール、たとえば、cat less grep wcなどを使って解析できます。
YACASL2の操作は、端末を開いてコマンドを入力・実行することで行います。
YACASL2は、LinuxやMac OS Xなど、UNIX系のOS上で動作します。現在動作を確認しているのは、次の環境です。
インストール時に、tar gcc makeが必要です。Linuxでは多くの場合、標準でインストールされています。Mac OS Xの場合、Xcode
をインストールするといっしょにインストールされます。
YACASL2をインストールするには、Linux上で次の作業をします。
YACASL2のサイトまたはgithubのダウンロードページから最新版のtar.gzファイルをダウンロードします。
ダウンロードが完了したら、md5sumとdiffで正しくファイルがダウンロードができているかを検証します。
$ openssl md5sum yacasl2.tar.gz | diff -s yacasl2.tar.gz.md5sum - Files yacasl2.tar.gz.md5sum and - are identical
YACASL2ファイルをダウンロードしたら、次のコマンドで展開します。
$ tar xvzf yacasl2.tar.gz
展開したら、次のコマンドでcasl2 comet2 dumpwordをビルドします。
$ cd yacasl2 $ make make -C src make[1]: Entering directory ‘/home/kazubito/yacasl2/src’ gcc -c -g -Wall -I ../include casl2.c gcc -c -g -Wall -I ../include word.c gcc -c -g -Wall -I ../include hash.c gcc -c -g -Wall -I ../include cerr.c gcc -c -g -Wall -I ../include struct.c gcc -c -g -Wall -I ../include cmd.c gcc -c -g -Wall -I ../include assemble.c gcc -c -g -Wall -I ../include token.c gcc -c -g -Wall -I ../include label.c gcc -c -g -Wall -I ../include macro.c gcc -c -g -Wall -I ../include exec.c gcc -c -g -Wall -I ../include dump.c gcc -g -Wall -I ../include -o ../casl2 casl2.o word.o hash.o cerr.o st ruct.o cmd.o assemble.o token.o label.o macro.o exec.o dump.o gcc -c -g -Wall -I ../include comet2.c gcc -g -Wall -I ../include -o ../comet2 comet2.o word.o hash.o cerr.o struct.o cmd.o exec.o dump.o gcc -c -g -Wall -I ../include dumpword.c gcc -g -Wall -I ../include -o ../dumpword dumpword.o word.o cerr.o make[1]: Leaving directory ‘/home/kazubito/yacasl2/src’
ビルドしたら、次のコマンドが正常に実行できるかを確認します。 正常に実行された場合は、「Hello, World!」と表示されます。
$ ./casl2 as/hello.casl Hello, World!
次のコマンドを実行すると、正常にビルドできているかどうかを詳細にテストできます。
$ make check 195 / 195 tests passed. Details in /home/username/yacasl2/test/integra tion/casl2/Test.log All tests are succeded. 149 / 149 tests passed. Details in /home/username/yacasl2/test/integra tion/comet2/Test.log All tests are succeded. 4 / 4 tests passed. Details in /home/username/yacasl2/test/integration /dumpword/Test.log All tests are succeded.
環境変数PATH
やCASL2LIB
を設定することで、YACASL2が使いやすくなります。環境変数は、使っているシェルを確認してから設定します。
環境変数の設定方法は使っているシェルによって異なります。シェルは、次のコマンドで確認できます。
$ echo $SHELL /bin/bash
現在もっとも多く使われているシェルは、BASHでしょう。以下、BASHでの設定方法を説明します。
PATH
にYACASL2のディレクトリを追加PATH
にYACASL2のディレクトリを追加すると、どのディレクトリでもディレクトリを指定せずにcasl2、comet2、dumpwordを実行できます。YACASL2をホームディレクトリ~
にインストールしたときは、次のコマンドで設定できます。
$ PATH=$PATH:~/yacasl2 && export PATH
環境変数の設定は、シェルの初期設定ファイルに追加することでログインしたあと自動的に反映されるようになります。BASHではホームディレクトリにある.bashrcが初期設定ファイルです。次のコマンドで追加できます。
$ echo ’PATH=$PATH:~/yacasl2 && export PATH’ >>~/.bashrc
CASL2LIB
の設定環境変数CASL2LIB
を設定しておけば、後述するCASL2LIBを利用するときの手間が小さくなります。YACASL2をホームディレクトリ~
にインストールしたときは、次のコマンドで設定できます。
$ CASL2LIB=~/yacasl2/as/casl2lib && export CASL2LIB
BASHの初期設定ファイル.bashrcには、次のコマンドで追加できます。
$ echo 'CASL2LIB=~/yacasl2/as/casl2lib && export CASL2LIB' >>~/.bashrc
YACASL2 は、テキストファイルに記述されたCASLプログラムを処理します。以下の例で用いられるCASLプログラムのファイルは、テキストエディタなどで作成してください。また、インストールしたディレクトリの中にあるasディレクトリからコピーして作成することもできます。
インストール時にコマンド実行の確認に使ったhello.caslは、次のような内容です。CASL IIのマクロ命令OUTは、文字列を出力します。
$ cat hello.casl MAIN START OUT OBUF,LEN RET OBUF DC 'Hello, World!' LEN DC 13 END
次のコマンドを実行すると、CASL II のアセンブルと仮想マシン COMET II 上での実行が連続で行われ、文字列が出力されます。
$ casl2 hello.casl Hello, World!
addl.caslは、3と1の和を求めます。
$ cat addl.casl ;;; ADDL r,adr MAIN START LD GR1,A ADDL GR1,B RET A DC 3 B DC 1 END
このプログラムには出力命令がないため、オプションなしで実行した場合には結果が出力されません。
$ casl2 addl.casl $
実行内容を確認するには、後述のようにCPU 内にあるレジスタやメモリの内容を表示するか、結果を出力するための処理を追加する必要があります。
casl2の処理途中で行われるアセンブルの結果を表示するには、オプション-aを指定します。また、ラベルとアドレスの対応表を表示するには、オプション-lを指定します。
次のコマンドではhello.caslの、ラベルとアドレスの対応表と、アセンブル結果と、実行結果が表示されます。OUTはアセンブラ命令で複数の機械語命令で構成されているため、命令行1行に対して、複数行のコードが生成されます。
$ casl2 -a -l hello.casl Assemble hello.casl (0) Label:::: MAIN.LEN ---> #0020 MAIN ---> #0000 MAIN.OBUF ---> #0013 Assemble hello.casl (1) hello.casl: 1:MAIN START hello.casl: 2: OUT OBUF,LEN #0000 #7001 #0001 #0000 #0002 #7002 #0003 #0000 #0004 #1210 #0005 #0013 #0006 #1220 #0007 #0020 #0008 #F000 #0009 #0002 #000A #1210 #000B #0021 #0021 #000A #000C #1220 #000D #0022 #0022 #0001 #000E #F000 #000F #0002 #0010 #7120 #0011 #7110 hello.casl: 3: RET #0012 #8100 hello.casl: 4:OBUF DC 'Hello, World!' #0013 #0048 #0014 #0065 #0015 #006C #0016 #006C #0017 #006F #0018 #002C #0019 #0020 #001A #0057 #001B #006F #001C #0072 #001D #006C #001E #0064 #001F #0021 hello.casl: 5:LEN DC 13 #0020 #000D hello.casl: 6: END Hello, World!
addl.caslの、ラベルとアドレスの対応表と、アセンブル結果は、次のようになります。
$ casl2 -a -l addl.casl Assemble addl.casl (0) Label:::: MAIN.A ---> #0005 MAIN.B ---> #0006 MAIN ---> #0000 Assemble addl.casl (1) addl.casl: 1:;;; ADDL r,adr addl.casl: 2:MAIN START addl.casl: 3: LD GR1,A #0000 #1010 #0001 #0005 addl.casl: 4: ADDL GR1,B #0002 #2210 #0003 #0006 addl.casl: 5: RET #0004 #8100 addl.casl: 6:A DC 3 #0005 #0003 addl.casl: 7:B DC 1 #0006 #0001 addl.casl: 8: END
なお、オプション-Aを指定すると、アセンブル結果が表示される時点で処理が終了します。仮想マシンCOMET II での実行は行われません。
YACASL2では実行中のCPUのレジスタとメモリの内容をそれぞれ、-tと-dを指定することで表示できます。
また、-Mで、仮想マシンCOMET II のメモリ容量を語(16 ビット)単位で指定できます。小さいプログラムを実行するときは、メモリ容量を小さくすれば結果が見やすくなります。
addl.caslに必要なメモリ容量は8語のため、次のようにCPUのレジスタとメモリの内容を表示できます。
$ casl2 -t -d -M8 addl.casl | less Assemble addl.casl (0) Assemble addl.casl (1) Executing machine codes #0000: Register:::: #0000: GR0: 0 = #0000 = 0000000000000000 #0000: GR1: 0 = #0000 = 0000000000000000 #0000: GR2: 0 = #0000 = 0000000000000000 #0000: GR3: 0 = #0000 = 0000000000000000 #0000: GR4: 0 = #0000 = 0000000000000000 #0000: GR5: 0 = #0000 = 0000000000000000 #0000: GR6: 0 = #0000 = 0000000000000000 #0000: GR7: 0 = #0000 = 0000000000000000 #0000: SP: 8 = #0008 = 0000000000001000 #0000: PR: 0 = #0000 = 0000000000000000 #0000: FR (OF SF ZF): 000 #0000: Memory:::: #0000: adr : 0000 0001 0002 0003 0004 0005 0006 0007 #0000: 0000: 1010 0005 2210 0006 8100 0003 0001 0000 #0002: Register:::: #0002: GR0: 0 = #0000 = 0000000000000000 #0002: GR1: 3 = #0003 = 0000000000000011 #0002: GR2: 0 = #0000 = 0000000000000000 #0002: GR3: 0 = #0000 = 0000000000000000 #0002: GR4: 0 = #0000 = 0000000000000000 #0002: GR5: 0 = #0000 = 0000000000000000 #0002: GR6: 0 = #0000 = 0000000000000000 #0002: GR7: 0 = #0000 = 0000000000000000 #0002: SP: 8 = #0008 = 0000000000001000 #0002: PR: 2 = #0002 = 0000000000000010 #0002: FR (OF SF ZF): 000 #0002: Memory:::: #0002: adr : 0000 0001 0002 0003 0004 0005 0006 0007 #0002: 0000: 1010 0005 2210 0006 8100 0003 0001 0000 #0004: Register:::: #0004: GR0: 0 = #0000 = 0000000000000000 #0004: GR1: 4 = #0004 = 0000000000000100 #0004: GR2: 0 = #0000 = 0000000000000000 #0004: GR3: 0 = #0000 = 0000000000000000 #0004: GR4: 0 = #0000 = 0000000000000000 #0004: GR5: 0 = #0000 = 0000000000000000 #0004: GR6: 0 = #0000 = 0000000000000000 #0004: GR7: 0 = #0000 = 0000000000000000 #0004: SP: 8 = #0008 = 0000000000001000 #0004: PR: 4 = #0004 = 0000000000000100 #0004: FR (OF SF ZF): 000 #0004: Memory:::: #0004: adr : 0000 0001 0002 0003 0004 0005 0006 0007 #0004: 0000: 1010 0005 2210 0006 8100 0003 0001 0000
addl.caslのレジスタやメモリの中で、実行中に値が変化しているのはGR1だけです。こうした場合は、grepを使って表示される内容を絞り込むことで動作を検証しやすくなります。
$ casl2 -t addl.casl | grep 'GR1:' #0000: GR1: 0 = #0000 = 0000000000000000 #0002: GR1: 3 = #0003 = 0000000000000011 #0004: GR1: 4 = #0004 = 0000000000000100
この内容を、先に出力したアセンブル結果と引き比べてください。 次の表のように、PRとGR1、命令行が対応していることがわかります。
PR | GR1 | 命令行
|
#0000 | #0000
| |
#0002 | #0003 | LD GR1,A
|
#0004 | #0004 | ADDL GR1,B
|
grepとtailを組み合わせれば、プログラム終了時の値を表示できます。
addl.caslでプログラム終了時の値を確認するには、次のようにします。
$ casl2 -t addl.casl | grep 'GR1:' | tail -1 #0004: GR1: 4 = #0004 = 0000000000000100
sum_10.caslは、1から10までの整数の和を求めます。
$ cat sum_10.casl ;;; sum_10.casl ;;; 出力 GR0: 1から10までの整数をすべて加算した値 MAIN START PUSH 0,GR1 LAD GR0,0 ; GR0を初期化 LD GR1,FST ; GR1に初項を転送 LOOP ADDL GR0,GR1 ; ループ先頭 ADDL GR1,STEP ; GR1 <- GR1 + 公差 CPL GR1,LST ; GR1が末項より大きい場合は終了 JPL FIN ; ↓ JUMP LOOP ; ループ終端 FIN POP GR1 RET FST DC 1 ; 初項 LST DC 10 ; 末項 STEP DC 1 ; 公差 END
sum_10.caslでファイル終了時の値を確認するには、次のようにします。
$ casl2 -t sum_10.casl | grep 'GR0:' | tail -1 #0010: GR0: 55 = #0037 = 0000000000110111 = '7'
grepとwcを組み合わせれば、プログラムのステップ数を表示できます。
$ casl2 -t hello.casl | grep 'GR1:' | wc -l 11
$ casl2 -t addl.casl | grep 'GR1:' | wc -l 3
sum_10.caslはプログラム内にループがあるため、ステップ数が大きくなります。
$ casl2 -t sum_10.casl | grep 'GR0:' | wc -l 54
casl2に-Oファイル名を指定すると、オブジェクトファイルを作成できます。
$ casl2 -Ohello.o hello.casl
作成されたオブジェクトファイルの内容は、odを使って確認できます。テキストファイルではないため、catなどでは確認できません。
$ od -t x2 hello.o 0000000 7001 0000 7002 0000 1210 0013 1220 0020 0000020 f000 0002 1210 0021 1220 0022 f000 0002 0000040 7120 7110 8100 0048 0065 006c 006c 006f 0000060 002c 0020 0057 006f 0072 006c 0064 0021 0000100 000d 000a 0001 0000106
オブジェクトファイルの実行には、comet2を使います。
$ comet2 hello.o Hello, World!
CASL IIでは、1語(16ビット)を単位としてデータが処理されます。 dumpwordは、指定した1語を10進数、16進数、2進数で表示します。
$ dumpword 72 72: 72 = #0048 = 0000000001001000 = 'H'
マイナスの数は、次のように--を付けて指定します。
$ dumpword -- -72 -72: -72 = #FFB8 = 1111111110111000
16進数は、次のように次のように'で囲み#を付けて指定します。
$ dumpword '#0048' #0048: 72 = #0048 = 0000000001001000 = 'H'
YACASL2のas/casl2libディレクトリには、CASL IIで記述されたライブラリファイルが格納されています。
このフォルダには、たとえば次のようなプログラムが含まれています。
3と1の和を求めるaddl.caslで演算結果を出力するには、まずaddl.caslを編集します。CASL IIのCALL命令でOUTLを副プログラムとして呼び出します。
$ cat addl_outl.casl MAIN START LD GR1,A ADDL GR1,B CALL OUTL RET A DC 3 B DC 1 END
変更したらcasl2を、複数のファイルを指定して実行します。
$ casl2 addl_outl.casl ~/yacasl2/as/casl2lib/outl.casl 4
casl2は、引数として指定されたCASLファイルをアセンブルし、仮想マシンCOMET II上で実行します。CASLファイルは、アセンブラ言語CASL IIで記述されたテキストファイルです。引数が指定されない場合は、エラーメッセージを表示して終了します。
$ casl2 hello.casl
複数のCASLファイルを指定することで、副プログラムを呼び出せます。
$ casl2 addl_outl.casl ~/yacasl2/as/casl2lib/outl.casl
casl2は、次のオプションを指定できます。
<プログラム名>.<ラベル名> ---> <アドレスの16進数表現>
<PR値の16進数表現>: <レジスタ>: <値の10進数表現> = <値の16進数表現> = <値の2進数表現>[ ='文字']
<PR値の16進数表現>
と<レジスタ>
、<値の16進数表現>
は、4けたの16進数で表されます。<PR値の16進数表現>
と<値の16進数表現>
には、先頭に#
が付きます。範囲は#0000
から#FFFF
です
<値の10進数表現>
は符号の付いた10進数です。範囲は-32768から32767です。
<値の2進数表現>
は、16けたの2進数で表されます。範囲は、0000000000000000
から1111111111111111
です
[ ='文字']
は、レジストリの値が「文字の組」の範囲に含まれる場合に表示されます。
表示されるレジスタには、次の種類があります。
GR0 GR1 GR2 GR3 GR4 GR5 GR6 GR7
SP
PR
FR
例えば、次のように表示されます。
#0002: GR1: 3 = #0003 = 0000000000000011
<値の10進数表現>
は符号の付かない10進数です。値の範囲は0から65535です。
comet2は、引数として指定されたオブジェクトファイルを仮想マシンCOMET II上で実行します。オブジェクトファイルは、caslに-oまたは-Oを指定して出力します。
$ comet2 hello.o
引数で指定できるオブジェクトファイルは1つだけです。引数が指定されない場合は、エラーメッセージを表示して終了します。複数の引数を指定した場合、2番目以降の引数は無視されます。
comet2は、次のオプションを指定できます。
<値の10進数表現>
は符号の付いた10進数です。範囲は-32768から32767です。
<PR値の16進数表現>: <レジスタ>: <値の10進数表現> = <値の16進数表現> = <値の2進数表現>[ ='文字']
<PR値の16進数表現>
と<値の16進数表現>
は、先頭に#
が付いた4けたの16進数で表されます。範囲は、#0000
から#FFFF
です
<値の2進数表現>
は、16けたの2進数で表されます。範囲は、0000000000000000
から1111111111111111
です
[ ='文字']
は、レジストリの値が「文字の組」の範囲に含まれる場合に表示されます。
例えば、次のように表示されます。
#0002: GR1: 3 = #0003 = 0000000000000011
表示されるレジスタには、次の種類があります。
GR0 GR1 GR2 GR3 GR4 GR5 GR6 GR7
SP
PR
FR
<値の10進数表現>
は符号の付かない10進数です。値の範囲は0から65535です。
dumpwordは、引数として指定された数値を、整数、#0000〜#FFFFの範囲の16進数、2進数で表示します。文字の組に該当する場合は、「 = 」のうしろに文字が表示されます。引数は、10進数または先頭に「#」の付いた16進数で指定します。表示される整数は、オプションにより符号付きか符号なしかを指定します。オプションなしの場合は符号付きです。整数の範囲は、符号付きの場合は-32768から32767、符号なしの場合は0から65535です。
$ dumpword 10
引数で指定できる1つだけです。引数が指定されない場合は、使い方を表示して終了します。複数の引数を指定した場合、2番目以降の引数は無視されます。
dumpwordは、次のオプションを指定できます。
GR1を符号付き整数とみなし、絶対値に変換
GR1
GR1
OF
SF
なし
;;; CASL2LIB > ABSを呼び出し、-1の絶対値を表示 MAIN START LD GR1,A CALL ABS CALL OUTA RET A DC -1 END
$ casl2 call_abs.casl $CASL2LIB/abs.casl $CASL2LIB/outa.casl \ $CASL2LIB/divl.casl $CASL2LIB/rev.casl 1
符号付き整数の割算を筆算方式で行う
GR1
GR2
GR0
GR3
OF
SF
ZF
DIVL
;; CASL2LIB > DIVAを呼び出し、-32767 / 255 の商と剰余を表示 MAIN START BEGIN LD GR1,A LD GR2,B CALL DIVA JOV ERR LD GR1,GR0 CALL OUTA LD GR1,GR3 CALL OUTA JUMP FIN ERR OUT EMSG,ELEN FIN RET A DC -32767 B DC 255 EMSG DC 'error' ELEN DC 5 END
$ casl2 call_diva.casl $CASL2LIB/diva.casl $CASL2LIB/divl.casl \ $CASL2LIB/outa.casl $CASL2LIB/abs.casl $CASL2LIB/rev.casl -128 -127
符号なし整数の割算を筆算方式で行う
GR1
GR2
GR0
GR3
OF
SF
ZF
なし
;; CASL2LIB > DIVLを呼び出し、65534 / 255 の商と剰余を表示 MAIN START BEGIN LD GR1,A LD GR2,B CALL DIVL JOV ERR LD GR1,GR0 CALL OUTL LD GR1,GR3 CALL OUTL JUMP FIN ERR OUT EMSG,ELEN FIN RET A DC 65534 B DC 255 EMSG DC 'error' ELEN DC 5 END
$ casl2 call_divl.casl $CASL2LIB/divl.casl $CASL2LIB/outl.casl \ $CASL2LIB/rev.casl 256 254
符号なし整数の入力を受け付ける
標準入力
GR1
GR0
#FFFF
STR2L
次のコマンドを実行すると、数値の入力が受け付けられ、入力された数値が表示される。
;;; casl2 -M1024 call_inl.casl $CASL2LIB/inl.casl $CASL2LIB/str2l.casl $CASL2LIB/mull.casl \ ;;; $CASL2LIB/outl.casl $CASL2LIB/divl.casl $CASL2LIB/rev.casl MAIN START LOOP CALL INL ; GR1に数値、GR0に文字列の長さ JOV OV AND GR0,GR0 JZE FIN CPL GR0,=#FFFF JZE NAN CALL OUTL JUMP LOOP OV CPL GR0,=#FFFF JZE LENOV OUT OVMSG,MLEN1 JUMP LOOP NAN OUT NANMSG,MLEN2 JUMP LOOP LENOV OUT LENOVMSG,MLEN3 FIN RET OVMSG DC 'Over 65535' MLEN1 DC 10 NANMSG DC 'Not integer' MLEN2 DC 11 LENOVMSG DC 'Str too long. Stop' MLEN3 DC 18 END
$ casl2 -M1024 call_inl.casl $CASL2LIB/inl.casl $CASL2LIB/str2l.casl $CASL2LIB/mull.casl \ $CASL2LIB/outl.casl $CASL2LIB/divl.casl $CASL2LIB/rev.casl
符号なし整数を文字列に変換する
GR1
GR2
GR0
DIVL
連続した複数のWORDを符号付き整数とみなして最大値を返す
GR1
GR2
GR0
なし
連続した複数のWORDを符号付き整数とみなして最小値を返す
GR1
GR2
GR0
なし