2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

一般化されたアセンブラ 'axx General Assembler'

2
Last updated at Posted at 2024-02-21

GENERAL ASSEMBLER 'axx.py'

pythonで書いたので、ニックネームはPaxxです。axxは、'Arbitrary eXtended X(cross) assembler'の略です。'ASM'にCPU未知数であるXを重ねて、'AXX'にしたという意味もあります。

1986年には既に、axxの元となるアイディアと'AXX'という名前と、Cで書いた原型は、僕が大学時代、アルバイト先の東京電子設計でありましたが、今のような動作するコードを発表したのは、2024年になって、38年を経てから、当時のプログラムリストを見つけて、Pythonで書き直しました。axx のパターンファイルのinstructionは、全ての命令型アセンブリ言語のメタ言語です。DSLですが、これと言った文法は存在しなく、文字列リテラルとシンボルと式などを組み合わせて文法を作る自由構文言語(パターン言語)です。

機械語にメタレベルの複雑性を持つEPIC/VLIW以外のすべての命令型アセンブリは、本質的にinstruction :: error_patterns :: binary_listという単純な構造に還元できます。さらに単純化して、エラーチェックを省けば、instruction::binary_listになります。ここで、axxのbinary_listは実用性のため、複雑な式計算や、アライメント、0ならばバイナリを出力しない;前置修飾子などを含んでいますが、最小モデルの場合、これらは考える必要はありません。instructionは、(文字列リテラル、整数値で置き換えられるシンボル、整数式、整数因子、浮動小数点式)を組み合わせたものです。これにより、任意の命令型アセンブリ言語を処理できます。しかし、バイナリ生成機能は万能ではないため、それにより適合プロセッサは限られますが、インストラクションとマシンコードが1対1の写像であるプロセッサならば、任意のプロセッサを処理できます。axxは、後からの拡張により、Itanium型のEPICや、vliwプロセッサも処理できます。

von Neumannアーキテクチャの本質的な共通性の抽出
命令セットアーキテクチャ(ISA)のメタモデル化
パターンマッチングによる形式化
をしました。

動作試験環境

FreeBSD、Linux terminal

本文

axx.pyはアセンブリ言語を一般化したジェネラル(一般)アセンブラです。ほぼ任意のプロセッサアーキテクチャを処理できます。個々のプロセッサアーキテクチャを処理するために、それ用のパターンファイル(プロセッサ記述ファイル)が必要です。自由なインストラクションを定義できますが、ターゲットプロセッサのアセンブリ言語に準じてパターンファイルを作ると、記法は若干異なるものの、そのプロセッサのアセンブリ言語を処理できます。要するに、インストラクションの文法規則と、それに基づくバイナリ生成だけなのです。axxは仮想CPUだけではなく、“抽象化された実CPU” を対象にしています。実在プロセッサの仕様書をパターンファイルに変換すれば、そのままアセンブルが可能になります。その意味で、巨大ISAのパターンファイル化は、人間の手間を考えると、AI向きです。巨大ISAのパターンファイル作成は手間ですが、一度作ってしまえばそれでそのISAは完成なので、使い回せばよいです。

「幅広く使える」という意味の「汎用アセンブラ」ではありません。「全てに共通する」と言う意味の「一般アセンブラ」です。binary_listは、制御構文を代入と3項演算子と、;修飾子と、アライメントと@@[]の5つしか持ちません。普通の一般アセンブラは、mnemonic operand definitionと並んでいますが、axxのパターン定義は、instruction :: error_pattern :: binay_listと並んでいて、インストラクションのパターンが自由です。そのため、r1 = r2 + r3等の表記も可能で、アセンブリ言語に限らず、汎用バイナリ生成器としても使えます。パターンファイルはチューリング不完全です。チューリング不完全な故、非常にひねくれたアーキテクチャのプロセッサには向きません。プロセッサのアーキテクチャは複雑にしようと思えば、いくらでも複雑になるからです。チューリング完全ならば追随できますが、axx.pyはチューリング不完全な故、「万能アセンブラ」ではありません。なぜ、今の所チューリング不完全にしているかと言うと、チューリング完全だと、DSLが「プログラム」になってしまうからです。つまりは停止性保証のためでもあります。

あまり特殊なプロセッサは扱えません。例えば、一般のプロセッサ以外の下記のプロセッサのISAなどは記述できません。

プロセッサ - 理由

Mill CPU - ベルトアーキテクチャ
ZISC - 命令がない
Thinking Machines - 超並列

実行プラットフォームも特定の処理系に依存しません。DOSファイルの行末のchr(13)も無視するようにしています。pythonが動く処理系だったら動作すると思います。

このヴァージョンはアセンブラの核となる部分だけなので、専用のアセンブラの備えている最適化、構造化・関数型アセンブリなどを命令型アセンブリに落とす高機能マクロ、などの実用的な機能はサポートしていません。実用的な機能について、マクロはマクロプロセッサを使ってください。最適化は未対応です。基本的な機能はあると思うので、応用してください。今のバージョンは実用性が足りません。

パターンファイルとソースファイルが分離されているため、コード化の手間を考えなければ、あるインストラクションセットのソースから別のプロセッサの機械語を生成することも可能です。共通の言語から、異なるプロセッサの機械語コードを生成することも可能です。パターンデータのbinary_listに複数の命令コードを書くとマクロとして機能しますが、あまりスマートではありません。それにより、簡単なコンパイラも書けます。

axxは第1引数から、アセンブラのパターンデータを読み込み、パターンデータに基づき第2引数のソースファイルをアセンブルします。その時、パターンデータは一行ずつ一行のアセンブリラインに照合され、マッチしたパターンのbinary_listが、結果に出力されます。パターンファイルはディレクティブの定義が順序に依存しますが、パターンは順序に依存しません。第2引数を省略したら、端末(標準入力)からソースを入力します。

結果は、-vオプションがあれば、標準出力にテキストとして出力され、-bオプションで指定された引数があれば、カレントディレクトリにバイナリファイルが出力され、-oオプションがあれば、ELF64オブジェクトファイルとして出力されます。-eオプションは、.exportで指定されたラベルをセクション/セグメント情報とともにTSV形式でファイルに出力します。

axxでは、アセンブリ言語ソースファイルや標準入力から入力するラインをアセンブリラインと名付けます。

解説

install and execution(assemble) on unix.

# install
git clone https://github.com/fygar256/axx.git
cd axx
chmod +x axx.py
sudo cp axx.py /usr/bin/axx

# execution(assemble)
axx patternfile.axx [source.s] [-b outfile.bin] [-e expfile.tsv] [-i impfile.tsv] [-o object.o]

patternfile.axx --- パターンファイル
source.s --- アセンブリソース
outfile.bin --- rawバイナリ出力ファイル
expfile.tsv --- セクション・ラベル情報エクスポートファイル
impfile.tsv --- セクション・ラベル情報インポートファイル
object.o ---- ELF リロケータブル・オブジェクトファイル

現在、オブジェクトファイル出力は、ELF64 for x86_64のみ、特殊解として動作しています。

オブジェクト出力は FreeBSD & Linux、x86_64で動いています。

Usage:

usage: axx [-h] [--osabi {FreeBSD,Linux}] [-b OUTFILE] [-e EXPORT_TSV] [-E EXPORT_ELF_TSV] [-i IMPORT_TSV]
           [-o OBJ_FILE] [-m MACHINE] [-v] [-d] [-g] patternfile [sourcefile]

axx general assembler programmed and designed by Taisuke Maekawa

positional arguments:
  patternfile           Pattern definition file (.axx)
  sourcefile            Assembly source file (.s). Omit for interactive mode.

options:
  -h, --help            show this help message and exit
  --osabi {FreeBSD,Linux}
                        ELF OSABI value (default: FreeBSD)
  -b OUTFILE            Output binary file
  -e EXPORT_TSV         Export labels to TSV file (plain format)
  -E EXPORT_ELF_TSV     Export labels to TSV file (ELF section flags format)
  -i IMPORT_TSV         Import labels from TSV file
  -o OBJ_FILE           Write ELF64 relocatable object file (.o)
  -m MACHINE            ELF e_machine value (default 62=EM_X86_64; 183=AArch64, 243=RISC-V, 3=i386, 20=PPC, 40=ARM)
  -v, --verbose         Verbose: print assembly listing to stdout (default: silent)
  -d, --debug           Enable debug output (forward-ref fallback, relaxation log, etc.)
  -g, --gen-debug       Generate DWARF debug information (.debug_info/.debug_abbrev/.debug_line) in the ELF object so
                        that gdb/lldb can do source-level debugging. Effective only together with -o.

パターンファイルの解説

パターンファイルは、プロセッサ記述ファイルで、個々のプロセッサに対応するため、ユーザー定義です。機械語やアセンブリ言語に対する一種のメタ言語です。パターンファイルのDSLは、基礎的ながらもISADL(ISA Description Language)です。

パターンファイルの定義が難しいと感じるならば、最小限のオペランドだけ式の評価に渡して、文字列リテラルで書けばいいです。また、構造化が困難なISAの部分は列挙で解決します。

パターンファイルの中のパターンデータは次のように並んでいます。

instruction  :: error_patterns  :: binary_list 
instruction  :: error_patterns  :: binary_list 
instruction  :: error_patterns  :: binary_list 
:
:

instructionは省略不可です。error_patternsは省略可です。binary_listは省略不可です。
instruction、error_patterns、binary_listは、::で区切ってください。

for ex. (x86_64)

RET :: 0xc3

コメント

パターンファイル内に、/*を書くとその行の/*以降がコメントになります。今の所、*/で閉じることはできません。その行の/*以降だけに有効です。

大文字・小文字の区別、変数

パターンファイルのinstructionの大文字と数字、記号は文字定数として扱われます。大文字は、大文字と小文字両方にマッチします。小文字にすると、1文字の変数として扱われます。アセンブルラインからその位置に当たるシンボルの持つ値が変数に代入されます。!小文字とすると、その位置の整数式の値、!!小文字にするとその位置の因子の値、!F小文字とすると、その位置の32ビット浮動小数点式を整数のビットパターンに変換したもの、!D小文字とすると、その位置の64ビット浮動小数点数式を、!Q小文字とすると、128ビット浮動小数点式を整数のビットパターンに変換したものが代入され、error_patternsとbinary_listから参照されます。代入されてない変数は全て初期値の0です。error_patternsとbinary_listからの参照のときは、!は必要ありません。全て同様に値が参照されます。

大文字と記号とエスケープされたキャラクタ     文字定数
小文字                                 その位置に当たるシンボルの持つ値
!小文字                                整数式の値
!!小文字                               整数因子の値
!F小文字                               32ビット浮動小数点式の値
!D小文字                               64ビット浮動小数点式の値
!Q小文字                               128ビット浮動小数点式の値

小文字変数は、パターンファイルの一行毎に全て0に初期化されます。

アセンブリラインからは、ラベルやセクション名以外は、大文字でも小文字も同じとして受け付けます。

エスケープキャラクタ

instruction内でエスケープキャラクタ\が使えます。

error_patterns

error_patternsは、変数と比較演算子を使い、エラーの出る条件を指定します。

エラーパターンは複数指定可で、,で区切って記述します。例えば、次のようです。

a>3;4,b>7;5

この例では、a>3のとき、エラーコード4を返し、b>7のときエラーコード5を返します。

binary_list

binary_listは、出力するコードを,で区切って指定します。例えば、0x03,dとすると、0x3の次にdが出力されます。

8048を例に取ります。パターンファイルに

ADD    A,R!n ::  n>7;5 :: n|0x68

があるとし、アセンブリラインにadd a,rnを渡すと、n>7のときエラーコード5(Register out of range)を返し、add a,r1で、0x69のバイナリが生成されます

binary_listの要素が空だと、アライメントをします。冒頭から、,で始まったり、0x12,,0x13などとすると、空の部分が丁度のアドレスまでパディングされます。

binary_listの要素の先頭に;がつくと、その要素が0だった場合、出力されません。

@@[]

binary_listの中で、@@[n,<str>]が使えます。これは、<str>をn回繰り返すという意味です。インデックス%%を0にするには、%0を使ってください。

symbol

.setsym :: symbol :: n

と書くと、symbolが値nで定義されます。

シンボルは、アルファベット、数字、いくつかの記号列です。

symbol1でsymbol2を定義するのは以下のように書きます。

.setsym ::symbol1 ::1
.setsym ::symbol2 ::#symbol1

symbol定義のz80の例を挙げます。パターンファイル内に

.setsym ::B ::0
.setsym ::C ::1
.setsym ::D ::2
.setsym ::E ::3
.setsym ::H ::4
.setsym ::L ::5
.setsym ::A ::7
.setsym ::BC ::0x00
.setsym ::DE ::0x10
.setsym ::HL ::0x20
.setsym ::SP ::0x30

と書いておくと、シンボルB,C,D,E,H,L,A,BC,DE,HL,SPを、それぞれ0,1,2,3,4,5,7,0x00,0x10,0x20,0x30として定義します。シンボルには、大文字小文字の区別はありません。

パターンファイル中に同じシンボルの定義が複数あると、新しいものが古いものを更新します。すなわち、

.setsym ::B::0
.setsym ::C::1
ADD A,s

.setsym ::NZ::0
.setsym ::Z::1
.setsym ::NC::2
.setsym ::C ::3
RET s

この場合、ADD A,CのCは1、RET CのCは3になります。

・記号、数字、アルファベットが混在するシンボルの例

.setsym ::$s5:: 21

シンボルのクリアは.clearsymでします。

.clearsym::ax

上の例はaxというシンボルを未定義にします。

全クリアは引数を指定しないでします。

.clearsym

パターンファイル内から、シンボルに使う文字セットを決めることができます。

.symbolc::<characters>

とすると、数字とアルファベット大文字小文字以外の文字をで指定できます。

デフォルトは、アルファベット+数字+_%$-~&|です。

シンボルチェック

.check::x::r1,r2,r3

としておくと、xの位置にr1,r2,r3以外のシンボルが来た場合、パターンミスマッチとして弾きます。
.checkを解除するには、

.clrcheck::x

とします。
シンボルチェックを全解除するには、

.clrcheck

と、引数なしでやります。

バイト長の違うレジスタの同じニモニックの扱いは例えば次のようです。

.setsym::AL::0x00
.setsym::BL::0x01
.setsym::AX::0x00
.setsym::BX::0x01
.check::s::AL,BL
.check::t::AX,BX
MOV s,!a :: 0xb0|s,a
MOV t,!a ::0xb8|t,a,a>>8

これで、(mov al,0x12,mov bl,0x12)、(mov ax,0x1234,mov bx,0x1234)のように書けます。

二重大括弧

instructionの中の省略可能なものは二重大括弧で括れます。z80のinc (ix)命令を示します。

INC	(IX[[+!d]]) ::				    0xdd,0x34,d

この場合、小文字の変数の初期値は0なので、inc (ix+0x12)と、省略しなかった場合は0xdd,0x34,0x12が、inc (ix)と、省略した場合は0xdd,0x34,0x00が出力されます。

パディングのバイトコード指定

パターンファイルから、

.padding::0x12

と、するとパディングするバイトコードは0x12になります。デフォルトは0x00です。

ワードが8ビット単位でないプロセッサのビット数指定

パターンファイルに、

.bits::12

とすると、12ビットのプロセッサを扱えます。デフォルトは8ビットです。

8ビット未満の、例えばビットスライスプロセッサや、機械語のワードがバイト単位でないプロセッサのアセンブルは、このディレクティブでします。axxは8ビット単位の出力ですので、4ビットのプロセッサなら、下位4ビットが、11ビットのプロセッサなら、指定されたバイトオーダーにより、(下位8ビット、上位3ビット)または、(上位3ビット、下位8ビット)がバイナリファイルに8ビット毎に出力されます。8ビット以内の余計なビットは0でマスクされます。

.bitsディレクティブを指定すると、アドレスが示す値はワード単位になります。例えば64bit processorであるx86_64はバイト単位の処理ができるので、.bitsディレクティブの指定は不要です。

バイトオーダーの指定は以下のようにします。

.bits::big::12

bigでビッグエンディアンにバイトが並びます。littleでリトルエンディアンです。
デフォルトはlittleで、指定しなくてもlittleになります。

include

このようにするとファイルをインクルードできます。

.include "file.axx"

パターンファイル内の式表現のエスケープキャラクタ

式は、エスケープキャラクタ\があると、そこで評価がストップします。エスケープされたキャラクタの処置は、後にとっておき、パターンファイル内でまた処理をします。

LEAQ r, [ s + t * !h \+ !i ] :: 0x48,0x8d,0x04,((@h)-1)<<6|t<<3|s,i

この例はx86_64の、leaq rax,[rax+rbx*(2+2)+0x40]のようなアセンブリラインを処理します。

LEAQ r,(s+t*!!h+!!i) :: 0x48,0x8d,0x04,((@h)-1)<<6|t<<3|s,i

この例は、leaq rax,(rax+rbx*(2+2)+0x40)のような場合、使います。

VLIWプロセッサ

.vliwディレクティブ

.vliw::128::41::5::00

とすると、バンドルのビット数128、1命令のビット数41、テンプレートビット数5、NOPコードは0x00(Itaniumの例)のEPICプロセッサを扱うことが出来ます。

例えば、Itaniumでは、41ビットの命令3つ、41*3=123(bit)の長さの命令群+末尾に5ビットのテンプレートビットが付いています。EPICでない場合には、テンプレートビットに0を指定してください。

テンプレートビットが正の数の場合はテンプレートビットが右端、負の数の場合はテンプレートビットが左端に付きます。テンプレートビットのビット数は絶対値です。.bitsディレクティブで、エンディアンにbigを指定すると、デフォルトのlittleの場合と出力されるバイト順が逆転します。

EPICの場合

EPICプロセッサの場合、パターンファイルは以下のように記述されます。

/* VLIW
.setsym::R1::1
.setsym::R2::2
.setsym::R3::3
.setsym::R4::4
.vliw::128::41::5::00
EPIC::1,2::0x8|!!!!
EPIC::1::0x01
AD a,b,c:: ::0x01,0,0,a,b,c::1
LOD d,[!e]:: :: 0x00,0x01,0,d,e,e>>8::2

と書き、!!!!はストップビットを表します。EPIC::1,2::0x8|!!!!はEPICの命令のセットを表し、インデックス1,2の命令のバンドルの、テンプレートが0x8とストップビットのbitwise orのコードを表します。

あとの、AD a,b,c:: ::0x01,0,0,a,b,c::1は、ADD命令 r1,r2,r3がエラーチェック無しで0x01,0,0,a,b,cを出力し、インデックスコードは1で、LOD d,[!e]:: :: 0x00,0x01,0,d,e,e>>8::2は、LOAD命令 r4に[!e]の内容を格納、エラーチェック無しで、0,1,0,0xd,e(下位8ビット)、e(上位8ビット)を出力し、インデックスコードは2のインストラクションを表します。このサンプルは、試験用なので実際のバイトコードと違います。

.viwで指定するパラメータは、(バンドルのビット数-テンプレートのビット数を8(bit)で割った値)+(それが余りがあれば1,なければ0)が、パターンで表されるバイト数と合致しなければいけません。

EPICではエラーパターンの省略指定は明示的に:: ::としなければいけません。

EPICでないVLIWの場合

EPICでないプロセッサの場合、パターンファイルは以下のように記述されます。

/* VLIW
.setsym::R1::1
.setsym::R2::2
.setsym::R3::3
.setsym::R4::4

.vliw::128::32::0::0x00
AD a,b,c::0x01,a,b,c
LOD d,[!e]::0x02,d,e,e>>8
JMP !a ::0x03,a,a>>8,0
命令の連結

VLIWの複数の命令を1バンドルに収めるには、以下のように!!で繋げます。

ad r1,r2,r3 !! lod r4,[0x1234]

パターンファイルのbinary_listに!!!があると、!!!は、!!で連結された命令の数を表します。

連結の最後に、!!!!があると、それはストップビットを立てます。

エンディアン

ビッグ・エンディアンか、リトル・エンディアンかは、binary_listでのデータの出力順で指定します。

アセンブリファイルの解説

label

アセンブルラインからは、ラベルは以下の方法で定義することができます。

.equで定義されたラベルは、リロケーション情報を失い、定数とされます。

label1:
label2: .equ 0x10
label3: nop

ラベルは、数字以外の.かアルファベットかいくつかの記号から始まる、アルファベットと数字といくつかの記号列です。

ラベルでラベルを定義することは以下のようにします。

label4: .equ label1

パターンファイル内から、ラベルに使う文字セットを決めることができます。

.labelc::<characters>

とすると、数字とアルファベット大文字小文字以外の文字を<characters>で指定できます。

デフォルトは、アルファベット+数字+アンダースコア+ピリオドです。

ORG

ORGは、アセンブルラインから、

.org 0x800
または、
.org 0x800,p

とします。.orgはロケーションカウンタの値を変更します。,pがついていれば、以前のロケーションカウンタの値が.orgで指定した値より小さいと、.orgで指定した値までパディングします。

アライメント

アセンブルラインから、

.align 16

とすると、16でアライメントします(16の倍数アドレスまで.paddingで指定されたバイトコードでパディングします)。引数を省略すると、直前の.alignで指定した数値あるいはデフォルト値でアライメントをします。

浮動小数点、数の表記

例えば、(ARM64等)浮動小数点をオペランドに含むプロセッサがあるとし、VMOV.F32 S0, #3.14 で、S0レジスタにfloat(32bit)の3.14がロードされ、そのオペコードは0x80とします。その場合、パターンデータは、

VMOV.F32 S!n,#!Fd ::0x80|n,d>>24,d>>16,d>>8,d

となり、アセンブルラインに、vmov.f32 s0,#3.14を渡すと、バイナリ出力は、0x80,0xc3,0xf5,0x48,0x40と なります。!Fが、!Dになったら、倍精度浮動小数点です。!Qは128ビット浮動小数点数です。

2進数は0bのプリフィックスを付けて下さい。
16進数は0xのプリフィックスを付けて下さい。

文字列

.asciiで、文字列の、.ascizで、末尾に0x00を伴う文字列のバイトコードを出力します。

.ascii "sample1"
.asciz "sample2"

0x00で埋める

.zero <expression>で、指定されたバイト数、0x00で埋めます。

.zero 65536

reserve

n bytesを予約します。ロケーションカウンタをn進めるだけです。

.resb n  ; reserve n bytes

export

下のようにすると、labelをsection/segment情報とともにexportできます。.export命令で指定されたlabelだけがexportされます。

.export label

.global

ラベルを外部に渡します。

.global label

.extern

外部ラベル読み込みの宣言をします。

.extern label

.global,.externは、ELF再配置可能オブジェクトファイル出力機能により処理されます。

.section

下のようにすると、section/segmentを指定できます。

.section .text
または
.segment .text

いまのところ、.sectionと.segmentは同じ意味です。

section sort

例えば、

.section .text
ld a,9
.section .data
.asciiz "test1"
.section .text
ld b,9
.section .data
db 0x12

などとすると、その通りに配置されてしまうので、section sortを使って、整列させてください。

.section .text
ld a,9
ld b,9
.section .data
.asciz "test1"
db 0x12

include

このようにするとファイルをインクルードできます。

.include "file.s"

コメント

アセンブリラインのコメントは;です。

式、演算子、特殊な項

特殊な項として、!!!があります。これは、!!で繋いだ命令の数を表す項です。
%%は、%%の出てきた回数(0から始まるインデックス)を返します。
$$は現在のロケーションカウンタの値を返します。
$.は、その命令の次の命令の先頭アドレスを返します。

アセンブリラインの式も、パターンデータの式も、同じ関数を呼び出しているので、働きは、ほとんど同じです。アセンブリラインからは小文字の変数は参照できません。

演算子の優先順位

演算子と優先順位はpythonを基にして次の通り

(expression)    括弧で囲った式
#               symbolの値を返す演算子
*(x,y)          xの下位からyバイト目 (y>=0)
-,~             負、ビットNOT
@               後に続く値の最高位ビットが右から何ビット目にあるかを返す単項演算子
'c'             'c'のキャラクタコード
:=              代入演算子
**              べき乗
*,/,//          乗算、除算、整数除算
+,-             加算、減算
<<,>>           左シフト、右シフト
&               ビットAND
|               ビットOR
^               ビットXOR
'               符号拡張
<=,<,>,>=,!=,== 比較演算子
not(x)          論理NOT
&&              論理AND
||              論理OR
x?a:b           3項演算子

代入オペレータとして:=があります。d:=24とすると、変数dに24が代入されます。代入オペレータが持つ値は、代入された値です。

前置オペレータ#は、後に続くシンボルの値を取ります。

前置オペレータ@は、後に続く値の最高位ビットが、右から何番目にあるかを返します。これをHebimarumattaオペレータと名付けます。

2項演算子'a'24とすると、aの24ビット目のビットを符号ビットにして符号拡張(Sign EXtend)します。これをSEXオペレータと名付けます。

2項演算子**は、べき乗です。

3項演算子?:は、x?a:bで、xが真のときa,偽のときbを返します。

プロンプトモード

プロンプト>>が出て、キーボードから入力する場合は、labelの表示コマンド?が使えます。

Z80

.setsym:: BC:: 0x00
.setsym:: DE:: 0x10
.setsym:: HL:: 0x20
LD    s,!d::  (s&0xf!=0)||(s>>4)>3;9 :: s|0x01,d&0xff,d>>8

で、ld bc,0x1234, ld de,0x1234, ld hl,0x1234が、それぞれ、0x01,0x34,0x12、0x11,0x34,0x12、0x21,0x34,0x12を出力します。

8086

バイト長の違うレジスタの同じニモニックの扱いは次のようにも書けます。(.checkディレクティブを使うのがおすすめです。)

8086.axx
.setsym::SI::0
.setsym::BX::0

/***********************************************************/
/* この時点でAX,ALが出てくると両方ともパターンにマッチしない */

/* ALを定義。この時点でALはパターンにマッチする
.setsym::AL::0xb0
MOV s,!a :: 0xb0,a
.clearsym::AL /* シンボルAL をクリア

/* AXを定義。この時点でAXはパターンにマッチする
.setsym::AX::0xb8
MOV s,!a::0xb8,a,a>>8
.clearsym::AX /* シンボルAXをクリア
/***********************************************************/

MOV BYTE [e + f + !c],!d::0xc6,c>=0x100?0x80:0x40,c,;c>>8,d
MOV BYTE [e + f],!g :: 0xc6,0,g
MOV BYTE [!a],!b :: 0xc6,0x6,a,a>>8,b
MOV WORD [e + f + !a],!b::0xc7,a>=0x100?0x80:0x40,a,;a>>8,b,b>>8
MOV WORD [e + f],!a :: 0xc7,0,a,a>>8
MOV WORD [!a],!b::0xc7,0x06,a,a>>8,b,b>>8
8086.s
mov byte [bx+si],0x12
mov byte [0x3412],0x56
mov byte [bx+si+0x12],0x34
mov byte [bx+si+0x3412],0x56
mov al,0x12
mov word [bx+si],0x3412
mov word [0x3412],0x7856
mov word [bx+si+0x12],0x5634
mov word [bx+si+0x3412],0x7856
mov ax,0x3412
実行例
$ axx 8086.axx 8086.s
0000000000000000 8086.s 1 mov byte [bx+si],0x12  0xc6 0x00 0x12
0000000000000003 8086.s 2 mov byte [0x3412],0x56  0xc6 0x06 0x12 0x34 0x56
0000000000000008 8086.s 3 mov byte [bx+si+0x12],0x34  0xc6 0x40 0x12 0x34
000000000000000c 8086.s 4 mov byte [bx+si+0x3412],0x56  0xc6 0x80 0x12 0x34 0x56
0000000000000011 8086.s 5 mov al,0x12  0xb0 0x12
0000000000000013 8086.s 6 mov word [bx+si],0x3412  0xc7 0x00 0x12 0x34
0000000000000017 8086.s 7 mov word [0x3412],0x7856  0xc7 0x06 0x12 0x34 0x56 0x78
000000000000001d 8086.s 8 mov word [bx+si+0x12],0x5634  0xc7 0x40 0x12 0x34 0x56
0000000000000022 8086.s 9 mov word [bx+si+0x3412],0x7856  0xc7 0x80 0x12 0x34 0x56 0x78
0000000000000028 8086.s 10 mov ax,0x3412  0xb8 0x12 0x34
$ 

いくつかのプロセッサのいくつかの命令のテスト

テストですので、バイナリは実際のコードとは違います。

test.axx
/* test
.setsym ::a:: 7
.setsym ::b:: 1
LDF A,!Fx :: 0x1,@@[4,x>>(%%*8)]
LDD A,!Dx :: 0x1,@@[8,x>>(%%*8)]
LDQ A,!Qx :: 0x1,@@[16,x>>(%%*8)]
REPEAT !n :: @@[n,0x99],%0@@[n,0x88]

/* ARM64
.setsym ::r1 :: 2
.setsym ::r2 :: 3
.setsym ::r3 :: 4
.setsym ::lsl:: 6
ADD w, x, y z #!d :: 0x88,d
ADD x, y, !e :: 0x91,x,y,e

/* A64FX
.setsym ::v0 :: 0
.setsym ::x0 :: 1
ST1 {x.4S},\[y\] :: 0x01,x,y,0

/* MIPS
.setsym ::$s5 ::21
.setsym ::$v0 ::2
.setsym ::$a0 ::4
ADDI x,y,!d :: (e:=(0x20000000|(y<<21)|(x<<16)|d&0xffff))>>24,e>>16,e>>8,e

/* x86_64
.setsym ::rax:: 0
.setsym ::rbx:: 3
.setsym ::rcx ::1
.setsym ::rep ::1

MMX A,B ::  ,0x12,0x13
LEAQ r,\[s,t,!d,!e\] :: 0x48,0x8d,0x04,((@d)-1)<<6|t<<3|s,e
LEAQ r, \[ s+t*!h\+!i\] :: 0x48,0x8d,0x04,((@h)-1)<<6|t<<3|s,i
[[z]]MOVSB :: ;z?0xf3:0,0xa4
TEST !a:: a==3?0xc0:4,0x12,0x13

/* ookakko test
LD (IX[[+!d]]),(IX[[+!e]]):: 0xfd,0x04,d,e 
NOP :: 0x01
test.s
leaq rax , [ rbx , rcx , 2 , 0x40]
leaq rax , [ rbx + rcx * 2 + 0x40]
addi $v0,$a0,5
st1 {v0.4s},[x0]
add r1, r2, r3 lsl #20
rep movsb
movsb
実行例
$ axx.py test.axx test.s
0000000000000000 test.s 1 leaq rax , [ rbx , rcx , 2 , 0x40]  0x48 0x8d 0x04 0x4b 0x40
0000000000000005 test.s 2 leaq rax , [ rbx + rcx * 2 + 0x40]  0x48 0x8d 0x04 0x4b 0x40
000000000000000a test.s 3 addi $v0,$a0,5  0x20 0x82 0x00 0x05
000000000000000e test.s 4 st1 {v0.4s},[x0]  0x01 0x00 0x01 0x00
0000000000000012 test.s 5 add r1, r2, r3 lsl #20  0x88 0x14
0000000000000014 test.s 6 rep movsb  0xf3 0xa4
0000000000000016 test.s 7 movsb  0xa4

errors

・labelが、パターンファイル内のシンボルと被るとis a pattern file symbol errorになります。

・同じlabelを二度以上定義するとlabel already definedエラーになります。

・構文解析ができないとSyntax errorになります。

・未定義ラベルを参照するとLabel undefinedエラーになります

・シンタックスが合ってないと、Illegal syntax in assembler line or pattern lineになります。

・EPICのテンプレートがセットされてないと、No VLIW instruction-set definedエラーになります。

・VLIWのパターンファイルが間違っていると、解釈時にsome errors in VLIW definitionエラーになります。

・error_patternsの条件を一つでも満たすとエラーになります。その場合、エラーコード1,2,3,5,6に対し、それぞれ(Invalid syntax,Address out of range,Value out of range, Register out of range,Port number out of range)のメッセージが出ます。エラーの種類が足りなかったら、ソースにエラーメッセージを足してください。

コメント

・Sorry for original notation.

・無茶なこと言われましたが、量子コンピュータとLISPマシンには対応していません。量子コンピュータのアセンブリ言語は量子アセンブリと言われ、アセンブリ言語ではありません。LISPマシンのプログラムは、アセンブリ言語ではありません。

・ホームメイドプロセッサから、スーパーコンピュータまでどうぞ。

・axxを評価、拡張、修正をしてください。構造が難解ですが、Pythonで書かれているため、拡張が容易です。ご自由にご拡張ください。

・今の所4倍精度浮動小数点数は定数しか扱えません。python3の仕様でそうなってます。python4で4倍精度浮動小数点が扱えるようになれば良いですが。

・マクロ機能はマクロプロセッサを使ってください。全てのアセンブリ言語をカバーするためには、関数型、構造化アセンブリ言語などの高級アセンブリ言語を命令型アセンブリ言語に落とす高機能マクロプロセッサが必要です。

・オプション-iを指定したら、TSVファイルからラベルがインポートされ、オプション-eを指定したら、.exportで指定したlabelがそのlabelが属するsection/segmentとともに、TSVでファイルにエクスポートされます。

・axxのパターンファイル作成は巨大なISAだと大変で、仕様は固定したので、AIがやってくれるようにならないかと思っています。アセンブラは元々、マシンコードを人間が理解しやすくするためにできたものですが、AIがコードを書くようになった現代、アセンブリ言語とコンピュータ両方向けの一般化アセンブラがあってもいいと思います。

未実装の項目

・コアができたので、axxにパターンファイルを用意して、高機能マクロと最適化機能をつけると立派なシステムになると思いますが、個人ではそんな大きなプロジェクトは完遂するのが難しいので、誰か作ってください。実用化されると幸いです。

お願い

バグを見つけた方がいっしゃいましたら、どう動かないかお知らせ願えると幸いです。

GitHubリポジトリ

LLVM的な巨大、複雑、実存的さは嫌なので、シンプルで美しく本質的な構造にしようと思ったのですが、事情があって、コーディングにAIを使いすぎたため、コードが複雑になってしまいました。残念に思います。最近のプログラミングはこうなのかなあ。それでも、設計思想に純粋さは残っていると思います。

実用的用途

FreeBSD(とLinux)で、リロケータブルELFの出力とリンク・実行ができました。

こちらは、axxでアセンブルをするbrainfuck interpreterです。

こちらは、x86_64用のパターンファイルです。

Misc

axx2(axxの次世代)構想。

axxパターンの代数的性質

謝辞

問題を出してくれて、ヒントをくれた、師匠の浜田純市さんと東京電子設計と、協力してくれた電気通信大学と、計算機科学者さんと、コンピュータ技術者さんと、Qiitaと、Googleと、IEEEと、Turing研究所と、そして、忘れられない誰か達に感謝を述べさせていただきます。情報処理学会の亀田名誉教授からは合格点をいただきました。ありがとうございます。

一句、マスコットキャラクター

冬銀河自由に描く星座かな 公太郎

コード 'axx.py'

履歴

2
0
11

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?