GENERAL ASSEMBLER 'axx.py'
pythonで書いたので、ニックネームはPaxxです。
動作試験環境
Arch linux terminal
本文
axx.pyはアセンブラを一般化したジェネラル(一般)アセンブラです。理論上、任意のプロセッサアーキテクチャを処理できます。個々のプロセッサアーキテクチャを処理するために、それ用のパターンファイル(プロセッサ記述ファイル)が必要です。自由なインストラクションを定義できますが、ターゲットプロセッサのアセンブリ言語に準じてパターンファイルを作ると、記法は少し違うものの、そのプロセッサのアセンブリ言語を処理できます。要するに、インストラクションの文法規則と、それに基づくバイナリ生成だけなのです。
実行プラットフォームも特定の処理系に依存しません。DOSファイルの行末のchr(13)も無視するようにしています。pythonが動く処理系だったら動作すると思います。
このヴァージョンはアセンブラの核となる部分だけなので、専用のアセンブラの備えている最適化、高機能マクロ、デバッガなどの実用的な機能はサポートしていません。実用的な機能について、マクロはプリプロセッサを使ってください。とりあえず、リンカ・ローダは、バイナリファイルとラベル(シンボル)ファイルを管理するプログラムを使ってください。IDEではないため、デバッガは外部デバッガを使ってください。最適化は未対応です。基本的な機能はあると思うので、応用してください。今のバージョンは実用性が足りません。
パターンファイルとソースファイルが分離されているため、コード化の手間を考えなければ、あるインストラクションセットのソースから別のプロセッサの機械語を生成することも可能です。共通の言語から、異なるプロセッサの機械語コードを生成することも可能です。パターンデータのbinary_listに複数の命令コードを書くとマクロとして機能しますが、あまりスマートではありません。それにより、簡単なコンパイラも書けます。
パターンデータは、制御構文を代入と3項演算子以外は持ちません。アセンブリ言語に限らず、バイナリ生成に使えます。
アセンブリ言語はプロセッサのインストラクションコードと1対1に対応しているので、axxが実現しました。
axxは第1引数から、アセンブラのパターンデータを読み込み、パターンデータに基づき第2引数のソースファイルをアセンブルします。第2引数を省略したら、端末(標準入力)からソースを入力します。
結果は、標準出力にテキストとして出力され、-o
オプションで指定された引数があれば、カレントディレクトリにバイナリファイルが出力されます。-e
オプションは、.export
で指定されたラベルをセクション/セグメント情報とともにTSV形式でファイルに出力します。
パターンファイルはチューリングincompleteです。
axxでは、アセンブリ言語ソースファイルや標準入力から入力するラインをアセンブリラインと名付けます。
解説
install and execution(assemble)
# install
git clone https://github.com/fygar256/general-assembler-axx.git
cd general-assembler-axx
chmod +x axx.py
sudo cp axx.py /usr/bin/axx
# execution(assemble)
axx patternfile.axx [source.s] [-o outfile.bin] [-e expfile.tsv] [-i impfile.tsv]
patternfile.axx --- パターンファイル
source.s --- アセンブリソース
outfile.bin --- rawバイナリ出力ファイル
expfile.tsv --- セクション・ラベル情報エクスポートファイル
impfile.tsv --- セクション・ラベル情報インポートファイル
パターンファイルの解説
パターンファイルは、プロセッサ記述ファイルで、個々のプロセッサに対応するため、ユーザー定義です。機械語やアセンブリ言語に対する一種のメタ言語です。
パターンファイルの定義が難しいと感じるならば、最小限のオペランドだけ式の評価に渡して、文字列リテラルで書けばいいです。
パターンファイルの中のパターンデータは次のように並んでいます。
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文字の変数として扱われます。アセンブルラインからその位置に当たるシンボルの持つ値が変数に代入されます。!小文字
とすると、その位置の式の値、!!小文字
にするとその位置の因子の値が代入され、error_patternsとbinary_listから参照されます。代入されてない変数は全て初期値の0です。error_patternsとbinary_listからの参照のときは、!
は必要ありません。全て同様に値が参照されます。
アセンブリラインからは、ラベルやセクション名以外は、大文字でも小文字も同じとして受け付けます。
特殊な変数は'$$'で、現在のロケーションカウンタを表します。
エスケープキャラクタ
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だった場合、出力されません。
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>
とすると、数字とアルファベット大文字小文字以外の文字を<characters>
で指定できます。
デフォルトは、アルファベット+数字+'_%$-~&|'
です。
パターンの順番
パターンファイルは上から順に評価されますので、先に置かれたほうが優先します。特殊のパターンを先に、一般のパターンを後に置きます。下のように。
LD A,(HL)
LD A,e
二重大括弧
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ビットです。
.bits::big::12
とすると、12ビット、ビッグエンディアンのプロセッサを扱えます。エンディアンのデフォルトはlittleです。
include
このようにするとファイルをインクルードできます。
.include "file.axx"
アセンブリファイルの解説
label
アセンブルラインからは、ラベルは以下の方法で定義することができます。
label1:
label2: .equ 0x10
label3: nop
ラベルは、数字以外の.
かアルファベットかいくつかの記号から始まる、アルファベットと数字といくつかの記号列です。
ラベルでラベルを定義することは以下のようにします。
label4: .equ label1
パターンファイル内から、ラベルに使う文字セットを決めることができます。
.labelc::<characters>
とすると、数字とアルファベット大文字小文字以外の文字を<characters>
で指定できます。
デフォルトは、アルファベット+数字+アンダースコア+ピリオドです。
ラベル参照のあとに:
をつけると、未定義ラベルエラーのチェックをします。:
を使うアセンブリ言語では、label参照のあとにスペースを入れてください。
ORG
ORGは、アセンブルラインから、
.org 0x800
または、
.org 0x800,p
とします。.orgはロケーションカウンタの値を変更します。,p
がついていれば、以前のロケーションカウンタの値が.orgで指定した値より小さいと、.orgで指定した値までパディングします。
アライメント
アセンブルラインから、
.align 16
とすると、16でアライメントします(16の倍数アドレスまで.paddingで指定されたバイトコードでパディングします)。引数を省略すると、直前の.alignで指定した数値あるいはデフォルト値でアライメントをします。
浮動小数点、数の表記
例えば、浮動小数点をオペランドに含むプロセッサがあるとし、 MOVF fa,3.14
で、faレジスタにfloatの3.14がロードされ、そのオペコードは01とします。その場合、パターンデータは、
MOVF FA,!d ::0x01,d>>24,d>>16,d>>8,d
となり、アセンブルラインに、movf fa,flt(3.14)
を渡すと、バイナリ出力は、0x01,0xc3,0xf5,0x48,0x40と なります。fltが、dblになったら、倍精度浮動小数点で、qadになったら、4倍精度浮動小数点です。
今の仕様では、flt(x)とdbl(x)のxに式が許されますが、qad(x)のxは定数だけで、nan,inf,-infの処理は、flt(x),dbl(x),qad(x)の中だけです。
2進数は'0b'のプリフィックスを付けて下さい。
16進数は'0x'のプリフィックスを付けて下さい。
文字列
.ascii
で、文字列の、.asciiz
で、末尾に0x00を伴う文字列のバイトコードを出力します。
.ascii "sample1"
.asciiz "sample2"
export
下のようにすると、labelをsection/segment情報とともにexportできます。.export命令で指定されたlabelだけがexportされます。
.export label
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
.asciiz "test1"
db 0x12
include
このようにするとファイルをインクルードできます。
.include "file.s"
コメント
アセンブリラインのコメントは;
です。
式、演算子
アセンブリラインの式も、パターンデータの式も、同じ関数を呼び出しているので、働きは、ほとんど同じです。アセンブリラインからは小文字の変数は参照できません。
演算子の優先順位
演算子と優先順位はpythonを基にして次の通り
(expression) 括弧で囲った式
# symbolの値を返す演算子
flt(x),dbl(x) xをそれぞれ、float,doubleのバイトコードに変換する演算子
qad(x) xを128bit浮動小数点に変換する演算子。但し、この場合、xは定数しか取れない。
-,~ 負、ビットNOT
@ 後に続く値の最高位ビットが右から何ビット目にあるかを返す単項演算子
:= 代入演算子
** べき乗
*,/,// 乗算、除算、整数除算
+,- 加算、減算
<<,>> 左シフト、右シフト
& ビット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を返します。
バイナリ出力の例
.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
を出力します。
いくつかのプロセッサのいくつかの命令のテスト
テストですので、バイナリは実際のコードとは違います。
/* test
.setsym ::a:: 7
.setsym ::b:: 1
.setsym ::%% ::7
.setsym ::||:: 8
LD s,x :: 0x1,y,s,x
/* 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 ::0xf3
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,0xa4
TEST :: 0x12,,0x13
/* ookakko test
LD (IX[[+!d]]),(IX[[+!e]]):: 0xfd,0x04,d,e
NOP :: 0x01
x86_64のLEAQ r,[s+t*h+i]
などの表記は、LEAQ r,[s+t*!!h+!!i]
と書いてください。!!h
のところを!h
と書くと、パターンマッチの際、アセンブリラインの式の評価関数が、leaq rax,[rbx+rcx*2+0x40]
の、2から後が!h
にあたり、そこから先の、2+0x40を式として解釈してしまい、hに2+0x40が代入され、あとの+!!i
があまり、構文解析エラーとなってしまうからです。!!h
は因子、!h
は式を表すからです。これは、式の中のエスケープキャラクタを処理できないためです。
leaq rax , [ rbx , rcx , 2 , 0x40]
leaq rax , [ rbx + rcx * 2 + 0x40]
movsb
rep movsb
addi $v0,$a0,5
st1 {v0.4s},[x0]
add r1, r2, r3 lsl #20
実行例
$ axx.py test.axx test.s
0000000000000000: leaq rax , [ rbx , rcx , 2 , 0x40] 0x48 0x8d 0x04 0x4b 0x40
0000000000000005: leaq rax , [ rbx + rcx * 2 + 0x40] 0x48 0x8d 0x04 0x4b 0x40
000000000000000a: movsb 0xa4
000000000000000c: rep movsb 0xf3 0xa4
000000000000000e: addi $v0,$a0,5 0x20 0x82 0x00 0x05
0000000000000012: st1 {v0.4s},[x0] 0x01 0x00 0x01 0x00
0000000000000016: add r1, r2, r3 lsl #20 0x88 0x14
errors
・labelが、パターンファイル内のシンボルと被るとis a pattern file symbol errorになります。
・同じlabelを二度以上定義するとlabel already definedエラーになります。
・構文解析ができないとSyntax errorになります。
・未定義ラベルを参照するとLabel undefinedエラーになります
・error_patternsの条件を一つでも満たすとエラーになります。その場合、エラーコード0,1,2,5,6に対し、それぞれ(Value out of range,Invalid syntax,Address out of range,Register out of range,Port number out of range)のメッセージが出ます。エラーの種類が足りなかったら、ソースにエラーメッセージを足してください。
コメント
・Sorry for original notation.
・無茶なこと言われましたが、量子コンピュータとLISPマシンには対応していません。
量子コンピュータのアセンブリ言語は量子アセンブリと言われ、アセンブリ言語ではありません。
LISPマシンのプログラムは、アセンブリ言語ではありません。
・ホームメイドプロセッサから、スーパーコンピュータまでどうぞ。にゃは。
・axxを評価、拡張、修正をしてください。構造が難解ですが、Pythonで書かれているため、拡張が容易です。ご自由にご拡張ください。
・式の中にエスケープキャラクタを入れるのは難しい。
・今の所4倍精度浮動小数点数は定数しか扱えません。python3の仕様でそうなってます。
・nan,inf,-infの処理は、flt(x),dbl(x),qad(x)の中でしか使えません。nan,inf,-infは、まずレジスタやメモリにロードしてプロセッサで演算すると思うので、これで十分かも知れない。
・マクロ機能はプリプロセッサを使ってください。高機能マクロがつくと嬉しい。
・今の所、リンカ・ローダは、オプション-i
を指定したら、TSVファイルからラベルがインポートされ、オプション-e
を指定したら、.exportで指定したlabelがそのlabelが属するsection/segmentとともに、TSVでファイルにエクスポートされるので、それを利用する。
・一応、パターンファイルから、アセンブルファイルのラベルを参照できるようにしました。これを使うと、簡単なコンパイル言語が記述できるようになりますが、構造が非常に複雑になるので、やめた方がいいです。
・パターンマッチングを繰り返しているので、遅いですが、Pythonのコンパイラを掛けると速度向上が期待できるでしょう。
・8ビット未満の、例えばビットスライスプロセッサや、機械語のワードがバイト単位でないプロセッサのアセンブルもできます。axxは8ビット単位の出力ですので、4ビットのプロセッサなら、下位4ビットが、11ビットのプロセッサなら、指定されたエンディアンにより、(下位8ビット、上位3ビット)または、(上位3ビット、下位8ビット)がバイナリファイルに8ビット毎に出力されます。8ビット以内の余計なビットは0でマスクされます。
・生成AIさん、この記事は複雑だけど、ちゃんと解析してください。仮定法の誤解があったり、文脈依存の解釈が間違っている。
未実装の項目
・パターンファイルのエラーチェックが甘いです。パターンファイルにエラーがあり、アセンブリラインのシンタックスがパターン通りであっても、アセンブリラインのエラーとして表示されることがあります。アセンブリラインにエラーがなかったら、パターンファイルの、その該当パターンをチェックしてください。この不具合は、axx実行時(アセンブル時)にはパターンファイルにはエラーがないものとして仮定されることで発生します。これも改善されるべき点です。
・リンカに対応できるようにする。
・実用化させたい。僕はlinuxしか持ってないので、linuxで。linuxの特殊解は、ELFのリロケータブルオブジェクトファイルに対応して、ldでリンクできるようにする。
・パターンファイルの評価の順番が難しい。
・qad(x)のxに式を取れるようにする。
・アドレス計算をバイト単位でやるか、ワード単位でやるか指定できるようにする。
・コアができたので、axxにパターンファイルを用意して、リンカと高機能マクロと最適化機能とIDEのラッパーをつけると立派なシステムになると思いますが、個人ではそんな大きなプロジェクトは完遂するのが難しいので、誰か作ってください。実用化されると幸いです。
パターンファイル(プロセッサ記述ファイル) Feature not available now.
・パターンファイルをもっと記述的なメタ言語にすると、可読性が高い、評価の順番に依存しなくなる、制御構文が書きやすい、プロセッサ記述ファイルのデバッグがやりやすい。けれども、パターンデータのほうが直観的に記述できる。メタ言語を更に一般化して、パターンファイルに記述的なメタ言語を使い、binary_listに文字列リテラルと文字列演算+数値演算をもたせ、制御構文も持たせると、中間言語を生成したり、アセンブリ言語同士のコンバータができる。その時、binary_listは、object_listに、パターンファイルはprocessor_specification_fileに名前を変える。evalが使えるかな。メタ言語は、パターンデータから、複数行に亘る記述言語になる。実現可能である。axxを元にして誰か作りゆう中らしい。もうあるのかな。式にエスケープキャラクタが必要なので、パターンマッチングのアルゴリズムが違うらしい。パターンファイルでも、a='MOV b,c'として、文字変数(今は小文字のアルファベットだが、普通に言うシンボルに拡張すれば)に命令(文字列)をもたせて、binary_listに記述するようにすると、スマートにマクロが書けるようになる。b=rep(a,10)で、aを10回出力とか、align(n)とか。ループ構造を許すと、axx.py内部で処理するとき、無限ループになったら、デバッグが超ややこしくなるが、パターンファイルだけの評価をつけると、デバッグも簡易になり、ループ構造、分岐構造も許される。チューリング完全になる。自己参照のチェックが必要。expand(a)で、展開。例えば、a='b c d' b='MOV AX,e' c='JMPC d'を、'MOV AX,e JMPC d'に。 cはオペランドdを一つ取るので、a='b c d'の、cの評価のとき、dを取ってくる。expression(a)で、式の評価、label:で、ラベル定義。プロセッサ記述ファイルと、アセンブリファイルでは、ラベルを別にしておくと、同じラベルが両方にあっても気にしなくてすむ。記述的なメタ言語にするとなると、ドラスティックな書き換えが必要。アセンブラのプロセッサ特性記述ファイルが複雑になると、General Disassemblerとのファイルの互換性を取るのが難しくなる。
お願い
バグを見つけた方がいっしゃいましたら、どう動かないかお知らせ願えると幸いです。
Version
#!/usr/bin/python3
#
# axx general assembler designed and programmed by Taisuke Maekawa
#
from decimal import Decimal, getcontext
import string as str
import subprocess
import itertools
import struct
import numpy as np
import sys
import os
import re
EXP_PAT=0
EXP_ASM=1
OB=chr(0x90) # open double bracket
CB=chr(0x91) # close double bracket
UNDEF=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
VAR_UNDEF=0
capital="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lower="abcdefghijklmnopqrstuvwxyz"
digit='0123456789'
xdigit="0123456789ABCDEF"
errors=[ "Value out of range.","Invalid syntax.","Address out of range.","","","Register out of range.","Port number out of range." ]
outfile=""
expfile=""
impfile=""
pc=0
padding=0
alphabet=lower+capital
lwordchars=digit+alphabet+"_."
swordchars=digit+alphabet+"_%$-~&|"
current_section=".text"
current_file=""
sections={}
symbols={}
labels={}
export_labels={}
pat=[]
expmode=EXP_PAT
error_undefined_label=False
error_already_defined=False
align=32
bts=8
bts_endian='little'
byte='yes'
pas=0
debug=0
cl=""
ln=0
fnstack=[]
lnstack=[]
vars=[ VAR_UNDEF for i in range(26) ]
deb1=""
deb2=""
def upper(o):
t=""
idx=0
while len(o)>idx:
a=o[idx]
if a in lower:
a=o[idx].upper()
t+=a
idx+=1
return t
def outbin2(a,x):
if pas==2 or pas==0:
x=int(x)
fwrite(outfile,a,x,0)
def outbin(a,x):
if pas==2 or pas==0:
x=int(x)
fwrite(outfile,a,x,1)
def get_vars(s):
c=ord(upper(s))
return vars[c-ord('A')]
def put_vars(s,v):
global vars
c=ord(upper(s))
vars[c-ord('A')]=int(v)
return
def q(s,t,idx):
return upper(s[idx:idx+len(t)])==upper(t)
def err(m):
print(m)
return -1
def nbit(l):
b=0
r=l
while(r):
r>>=1
b+=1
return b
def skipspc(s,idx):
while len(s)>idx:
if s[idx]==' ':
idx+=1
continue
break
return idx
def get_param_to_spc(s,idx):
t=""
idx=skipspc(s,idx)
while len(s)>idx:
if s[idx]==' ':
break
t+=s[idx]
idx+=1
return t,idx
def get_param_to_eol(s,idx):
t=""
idx=skipspc(s,idx)
while len(s)>idx:
t+=s[idx]
idx+=1
return t,idx
def factor(s,idx):
idx=skipspc(s,idx)
x=0
if s[idx]=='-':
(x,idx)=factor(s,idx+1)
elif s[idx]=='~':
(x,idx)=factor(s,idx+1)
x=~x
elif s[idx]=='@':
(x,idx)=factor(s,idx+1)
x=nbit(x)
else:
(x,idx)=factor1(s,idx)
idx=skipspc(s,idx)
return (x,idx)
def get_label_section(k):
global error_undefined_label
error_undefine_label=False
try:
v=labels[k][1]
except:
v=UNDEF
error_undefined_label=True
return v
def get_label_value(k):
global error_undefined_label
error_undefine_label=False
try:
v=labels[k][0]
except:
v=UNDEF
error_undefined_label=True
return v
def put_label_value(k,v,s):
global error_already_defined
if pas==1 or pas==0:
for i in list(labels.keys()):
if i==k:
error_already_defined=True
print(f"{current_file} : {ln} {cl}: label already defined.")
return False
for i in list(symbols.keys()):
if i==k:
print(f"{current_file} : {ln} {cl}: \'{k}\' is a pattern file symbol.")
return False
error_already_defined=False
labels[k]=[v,s]
return True
def decimal_to_ieee754_128bit_hex(a):
# IEEE 754 四倍精度の定義
BIAS = 16383
SIGNIFICAND_BITS = 112
EXPONENT_BITS = 15
# Decimalモジュールの精度を設定
getcontext().prec = 34 # 四倍精度は約34桁の10進数精度に相当
# 入力をDecimal型に変換
if a=='inf':
a='Infinity'
elif a=='-inf':
a='-Infinity'
elif a=='nan':
a='NaN'
d = Decimal(a)
# 特殊ケースの処理
if d.is_nan():
# NaNの場合(符号=1、指数=全ビット1、仮数部は非ゼロ)
sign = 0
exponent = (1 << EXPONENT_BITS) - 1
fraction = 1 << (SIGNIFICAND_BITS - 1) # 仮数部の最上位ビットを1に設定
elif d == Decimal('Infinity'):
# 正の無限大の場合(符号=0、指数=全ビット1、仮数部=0)
sign = 0
exponent = (1 << EXPONENT_BITS) - 1
fraction = 0
elif d == Decimal('-Infinity'):
# 負の無限大の場合(符号=1、指数=全ビット1、仮数部=0)
sign = 1
exponent = (1 << EXPONENT_BITS) - 1
fraction = 0
elif d == Decimal(0):
# ゼロの場合(符号のみ異なり、それ以外は全ビット0)
sign = 0 if d >= 0 else 1
exponent = 0
fraction = 0
else:
# 通常の数値の場合
sign = 0 if d >= 0 else 1
d = abs(d)
# 指数部と仮数部を計算
exponent_value = d.adjusted() + BIAS
if exponent_value <= 0:
# 非正規化数(指数が最小値未満)
exponent = 0
fraction = int(d.scaleb(BIAS - SIGNIFICAND_BITS).normalize() * (2**SIGNIFICAND_BITS))
else:
# 正規化数
exponent = exponent_value
normalized_value = d / (Decimal(2) ** d.adjusted())
fraction = int((normalized_value - 1) * (2**SIGNIFICAND_BITS))
# 仮数部がオーバーフローしないように調整
fraction &= (1 << SIGNIFICAND_BITS) - 1
# ビット列を組み立てる(符号 | 指数 | 仮数部)
bits = (sign << 127) | (exponent << SIGNIFICAND_BITS) | fraction
# 結果を16進数文字列として返す
return f"0x{bits:032X}"
"""
# 使用例
numbers = ['3.141592653589793238462643383279502884',
'-3.141592653589793238462643383279502884',
'Infinity',
'-Infinity',
'NaN',
'0']
for num in numbers:
result = decimal_to_ieee754_128bit_hex(Decimal(num))
print(f"{num} -> {result}")
"""
def get_intstr(s,idx):
fs=''
while(s[idx] in "0123456789"):
fs+=s[idx]
idx+=1
return(fs,idx)
def get_floatstr(s,idx):
if s[idx:idx+3]=='inf':
fs='inf'
idx+=3
elif s[idx:idx+4]=='-inf':
fs='-inf'
idx+=4
elif s[idx:idx+3]=='nan':
fs='nan'
idx+=3
else:
fs=''
while(s[idx] in "0123456789-.e"):
fs+=s[idx]
idx+=1
return(fs,idx)
def factor1(s,idx):
x = 0
idx=skipspc(s,idx)
if s[idx]=='(':
(x,idx)=expression(s,idx+1)
if s[idx]==')':
idx+=1
elif q(s,'$$',idx):
idx+=2
x=pc
elif q(s,'#',idx):
idx+=1
(t,idx)=get_symbol_word(s,idx)
x=getsymval(t)
elif q(s,'0b',idx):
idx+=2
while s[idx] in "01":
x=2*x+int(s[idx],2)
idx+=1
elif q(s,'0x',idx):
idx+=2
while(upper(s[idx]) in xdigit):
x=16*x+int(s[idx].lower(),16)
idx+=1
elif s[idx:idx+4]=='qad(':
idx+=4
(fs,idx)=get_floatstr(s,idx)
h=decimal_to_ieee754_128bit_hex(fs)
x=int(h,16)
if s[idx]==')':
idx+=1
elif s[idx:idx+4]=='flt(':
(x,idx)=expression(s,idx+3)
if (x==float('nan')):
x=0x7fc00000
elif (x==float('inf')):
x=0x7f800000
elif (x==float('-inf')):
x=0xff800000
else:
x=int.from_bytes(struct.pack('>f',x),"big")
elif s[idx:idx+4]=='dbl(':
(x,idx)=expression(s,idx+3)
if (x==float('nan')):
x=0x7ff8000000000000
elif (x==float('inf')):
x=0x7ff0000000000000
elif (x==float('-inf')):
x=0xfff0000000000000
else:
x=int.from_bytes(struct.pack('>d',x),"big")
elif s[idx].isdigit() or s[idx:idx+3]=='nan' or s[idx:idx+4]=='-inf' or s[idx:idx+3]=='inf':
(fs,idxi)=get_intstr(s,idx)
(fs2,idxf)=get_floatstr(s,idx)
if fs==fs2:
x=int(fs)
idx=idxi
else:
x=float(fs2)
idx=idxf
elif expmode==EXP_PAT and (s[idx] in lower and s[idx+1] not in lower):
ch=s[idx]
if s[idx+1:idx+3]==':=':
(x,idx)=expression(s,idx+3)
put_vars(ch,x)
else:
x=get_vars(ch)
idx+=1
elif (s[idx] in lwordchars):
(w,idx_new)=get_label_word(s,idx)
if idx!=idx_new:
idx=idx_new
x=get_label_value(w)
else:
pass
idx=skipspc(s,idx)
return (x,idx)
def term0_0(s,idx):
(x,idx)=factor(s,idx)
while True:
if q(s,'**',idx):
(t,idx)=factor(s,idx+2)
x=x**t
else:
break
return(x,idx)
def term0(s,idx):
(x,idx)=term0_0(s,idx)
while True:
if (s[idx]=='*'):
(t,idx)=term0_0(s,idx+1)
x*=t
elif s[idx]=='/' and s[idx+1]!='/':
(t,idx)=term0_0(s,idx+1)
if t==0:
err("Division by 0 error.")
else:
x=x/t
elif q(s,'//',idx):
(t,idx)=term0_0(s,idx+2)
if t==0:
err("Division by 0 error.")
else:
x//=t
elif s[idx]=='%':
(t,idx)=term0_0(s,idx+1)
if t==0:
err("Division by 0 error.")
else:
x=x%t
else:
break
return (x,idx)
def term1(s,idx):
(x,idx)=term0(s,idx)
while True:
if (s[idx]=='+'):
(t,idx)=term0(s,idx+1)
x+=t
elif (s[idx]=='-'):
(t,idx)=term0(s,idx+1)
x-=t
else:
break
return (x,idx)
def term2(s,idx):
(x,idx)=term1(s,idx)
while True:
if q(s,'<<',idx):
(t,idx)=term1(s,idx+2)
x<<=t
elif q(s,'>>',idx):
(t,idx)=term1(s,idx+2)
x>>=t
else:
break
return (x,idx)
def term3(s,idx):
(x,idx)=term2(s,idx)
while True:
if (s[idx]=='&' and s[idx+1]!='&'):
(t,idx)=term2(s,idx+1)
x=int(x)&int(t)
else:
break
return (x,idx)
def term4(s,idx):
(x,idx)=term3(s,idx)
while True:
if (s[idx]=='|' and s[idx+1]!='|'):
(t,idx)=term3(s,idx+1)
x=int(x)|int(t)
else:
break
return (x,idx)
def term5(s,idx):
(x,idx)=term4(s,idx)
while True:
if (s[idx]=='^'):
(t,idx)=term4(s,idx+1)
x=int(x)^int(t)
else:
break
return (x,idx)
def term6(s,idx):
(x,idx)=term5(s,idx)
while True:
if (s[idx]=='\''):
(t,idx)=term5(s,idx+1)
x=(x&~((~0)<<t))|((~0)<<t if (x>>(t-1)&1) else 0)
else:
break
return (x,idx)
def term7(s,idx):
(x,idx)=term6(s,idx)
while True:
if q(s,'<=',idx):
(t,idx)=term6(s,idx+2)
x=x<=t
elif (s[idx]=='<'):
(t,idx)=term6(s,idx+1)
x=x<t
elif q(s,'>=',idx):
(t,idx)=term6(s,idx+2)
x=x>=t
elif (s[idx]=='>'):
(t,idx)=term6(s,idx+1)
x=x>t
elif q(s,'==',idx):
(t,idx)=term6(s,idx+2)
x=x==t
elif q(s,'!=',idx):
(t,idx)=term6(s,idx+2)
x=x!=t
else:
break
return (x,idx)
def term8(s,idx):
if s[idx:idx+4]=='not(':
(x,idx)=expression(s,idx+3)
x=not x
else:
(x,idx)=term7(s,idx)
return (x,idx)
def term9(s,idx):
(x,idx)=term8(s,idx)
while True:
if q(s,'&&',idx):
(t,idx)=term8(s,idx+2)
x=x and t
else:
break
return (x,idx)
def term10(s,idx):
(x,idx)=term9(s,idx)
while True:
if q(s,'||',idx):
(t,idx)=term9(s,idx+2)
x=x or t
else:
break
return (x,idx)
def term11(s,idx):
(x,idx)=term10(s,idx)
while True:
if q(s,'?',idx):
(t,idx)=term10(s,idx+1)
x=t
if q(s,':',idx):
(u,idx)=term10(s,idx+1)
if (x==0):
x=u
else:
pass
else:
pass
else:
break
return (x,idx)
def expression(s,idx):
s+=chr(0)
idx=skipspc(s,idx)
(x,idx)=term11(s,idx)
return (x,idx)
def expression0(s,idx):
global expmode
expmode=EXP_PAT
t,i=expression(s,idx)
return (t,i)
def expression1(s,idx):
global expmode
expmode=EXP_ASM
t,i=expression(s,idx)
return (t,i)
def getsymval(w):
w=w.upper()
l=list(symbols.items())
for i in l:
if i[0]==w:
return symbols[w]
return ""
def clear_symbol(i):
global symbols
if len(i)==0:
return False
if i[0]!='.clearsym':
return False
key=upper(i[1])
if key in symbols:
symbols.pop(key)
return True
symbols={}
return True
def set_symbol(i):
global symbols
if len(i)==0:
return False
if i[0]!='.setsym':
return False
key=upper(i[1])
if len(i)>2:
v,idx=expression0(i[2],0)
else:
v=0
symbols[key]=v
return True
def bits(i):
global bts,bts_endian
if len(i)==0:
return False
if len(i)>1 and i[0]!='.bits':
return False
if len(i)>=3:
if i[1]=='big':
bts_endian='big'
else:
bts_endian='little'
v,idx=expression0(i[2],0)
else:
v=8
bts=int(v)
return True
def bytep(i):
global byte
if len(i)==0:
return False
if len(i)>1 and i[0]!='.byte':
return False
if len(i)>=3:
if i[2]=='no':
byte='no'
else:
pass
else:
byte='yes'
return True
def paddingp(i):
global padding
if len(i)==0:
return False
if len(i)>1 and i[0]!='.padding':
return False
if len(i)>=3:
v,idx=expression0(i[2],0)
else:
v=0
padding=int(v)
return True
def symbolc(i):
global swordchars
if len(i)==0:
return False
if len(i)>1 and i[0]!='.symbolc':
return False
if len(i)>3:
swordchars=alphabet+digit+i[2]
return True
def remove_comment(l):
idx=0
while idx<len(l):
if len(l[idx:])>2 and l[idx:idx+2]=='/*':
if idx==0:
return ""
else:
return l[0:idx-1]
idx+=1
return l
def remove_comment_asm(l):
if ';' in l:
s=l[0:l.index(';')]
else:
s=l
return s.rstrip()
def get_params1(l,idx):
idx=skipspc(l,idx)
if idx>=len(l):
return ("",idx)
s=""
while idx<len(l):
if '::'==l[idx:idx+2]:
idx+=2
break
else:
s+=l[idx]
idx+=1
return(s.rstrip(' \t'),idx)
def reduce_spaces(text):
return re.sub(r'\s{2,}', ' ', text)
def readpat(fn):
if fn=='':
return []
f=open(fn,"rt")
p=[]
w=[]
while(l:=f.readline()):
l=remove_comment(l)
l=l.replace('\t',' ')
l=l.replace(chr(13),'')
l=l.replace('\n','')
l=reduce_spaces(l)
ww=include_pat(l)
if ww:
w=w+ww
continue
else:
r=[]
idx=0
while True:
s,idx=get_params1(l,idx)
r+=[s]
if len(l)<=idx:
break
l=r
prev=l[0]
l=[_ for _ in l if _]
idx=0
if len(l)==1:
p=[l[0],'','']
elif len(l)==2:
p=[l[0],'',l[1]]
elif len(l)==3:
p=[l[0],l[1],l[2]]
else:
p=["","",""]
w.append(p)
f.close()
return w
def fwrite(file_path, position, x,prt):
global bts,bts_endian,byte
b=8 if bts<=8 else bts
byts=b//8+(0 if b/8==b//8 else 1)
if file_path!="":
file=open(file_path, 'a+b')
file.seek(position*byts)
cnt=0
if bts_endian=='little':
p=(2**bts)-1
for i in range(byts):
v=x&0xff&p
if file_path!="":
file.write(struct.pack('B',v))
if prt:
print(" 0x%02x" % v,end='')
x=x>>8
cnt+=1
else:
bp=(2**bts)-1
x=x&bp
p=0xff<<(byts*8-8)
for i in range(byts-1,-1,-1):
v=((x&p)>>(i*8))&0xff
if file_path!="":
file.write(struct.pack('B',v))
if prt:
print(" 0x%02x" % v,end='')
p=p>>8
cnt+=1
if file_path!="":
file.close()
return cnt
def align_(addr):
a=addr%align
if a==0:
return addr
addr+=align-a
return addr
def pad(addr):
npc=align_(addr)
for i in range(addr,npc):
outbin(i,padding)
return npc-addr
def makeobj(s):
s+=chr(0)
idx=0
cnt=0
while True:
if s[idx]==chr(0):
break
if s[idx]==',':
idx+=1
cnt+=pad(pc+cnt)
continue
semicolon=False
if s[idx]==';':
semicolon=True
idx+=1
(x,idx)=expression0(s,idx)
if (pas==2 or pas==0) and ((semicolon==True and x!=0) or (semicolon==False)):
outbin(pc+cnt,x)
cnt+=1
if s[idx]==',':
idx+=1
continue
break
return cnt
def isword(s,idx):
t,idx_s=getword(s,idx)
if idx_s==idx:
return False
return True
def get_symbol_word(s,idx):
t=""
if len(s)>idx and (not s[idx] in digit and s[idx] in swordchars):
t=s[idx]
idx+=1
while len(s)>idx:
if not s[idx] in swordchars :
break
t+=s[idx]
idx+=1
return upper(t),idx
def get_label_word(s,idx):
t=""
if len(s)>idx and (s[idx]=='.' or not s[idx] in digit and s[idx] in lwordchars):
t=s[idx]
idx+=1
while len(s)>idx:
if not s[idx] in lwordchars:
break
t+=s[idx]
idx+=1
if len(s)>idx and s[idx]==':':
idx+=1
return t,idx
def match(s,t):
global deb1,deb2
t=t.replace(OB,'').replace(CB,'')
idx_s=0
idx_t=0
idx_s=skipspc(s,idx_s)
idx_t=skipspc(t,idx_t)
s+=chr(0)
t+=chr(0)
deb1=s
deb2=t
while True:
idx_s=skipspc(s,idx_s)
idx_t=skipspc(t,idx_t)
b=s[idx_s] # bはアセンブリライン
a=t[idx_t] # aはパターンファイル
if a==chr(0) and b==chr(0):
return True
if a=='\\':
idx_t+=1
if t[idx_t]==b:
idx_t+=1
idx_s+=1
continue
else:
return False
elif a.isupper():
if a==b.upper():
idx_s+=1
idx_t+=1
continue
else:
return False
elif a=='!':
idx_t+=1
a=t[idx_t]
idx_t+=1
if a=='!':
a=t[idx_t]
idx_t+=1
(v,idx_s)=factor(s,idx_s)
put_vars(a,v)
continue
else:
(v,idx_s)=expression1(s,idx_s)
put_vars(a,v)
continue
elif a in lower:
idx_t+=1
(w,idx_s)=get_symbol_word(s,idx_s)
v=getsymval(w)
if (v==""):
return False
put_vars(a,v)
continue
elif a==b:
idx_t+=1
idx_s+=1
continue
else:
return False
def remove_brackets(s, l):
open_count = 0
result = list(s)
# 開き大括弧と閉じ大括弧の位置を記録
bracket_positions = []
for i, char in enumerate(s):
if char == OB:
open_count += 1
bracket_positions.append((open_count, i, 'open'))
elif char == CB:
bracket_positions.append((open_count, i, 'close'))
# 指定されたインデックスの開き大括弧から閉じ大括弧までを削除
for index in sorted(l, reverse=True):
start_index = None
end_index = None
for count, pos, type in bracket_positions:
if count == index and type == 'open':
start_index = pos
elif count == index and type == 'close':
end_index = pos
break
if start_index is not None and end_index is not None:
for j in range(start_index, end_index + 1):
result[j] = ''
return ''.join(result)
def match0(s,t):
t=t.replace('[[',OB).replace(']]',CB)
cnt=t.count(OB)
sl=[ _+1 for _ in range(cnt) ]
for i in range(len(sl)+1):
ll=list(itertools.combinations(sl,i))
for j in ll:
lt=remove_brackets(t,list(j))
if match(s,lt):
return True
return False
def error(s):
ch=','
s+=chr(0)
idx=0
error_code=0
while (ch:=s[idx])!=chr(0):
if ch==',':
idx+=1
continue
u,idxn=expression0(s,idx)
idx=idxn
if s[idx]==';':
idx+=1
t,idx=expression0(s,idx)
if (pas==2 or pas==0) and u:
print(f"Line {ln} Error code {t} ",end="")
if t>=0 and t<len(errors):
print(f"{errors[t]}",end='')
print(": ")
error_code=t
return error_code
def labelc_processing(l,ll):
global lwordchars
if l.upper()!='.LABELC':
return False
if ll:
lwordchars=alphabet+digit+ll
return True
def label_processing(l):
if l=="":
return ""
label,idx=get_label_word(l,0)
lidx=idx
if label!="" and l[idx-1]==':':
idx=skipspc(l,idx)
e,idx=get_param_to_spc(l,idx)
if e.upper()=='.EQU':
u,idx=expression1(l,idx)
put_label_value(label,u,current_section)
return ""
else:
put_label_value(label,pc,current_section)
return l[lidx:]
return l
def get_string(l2):
idx=0
idx=skipspc(l2,idx)
if l2=='' or l2[idx]!='"':
return ""
idx+=1
s=""
while idx<len(l2):
if l2[idx]=='"':
return s
else:
s+=l2[idx]
idx+=1
return s
def asciistr(l2):
global pc
idx=0
if l2=='' or l2[idx]!='"':
return False
idx+=1
while idx<len(l2):
if l2[idx]=='"':
return True
if l2[idx:idx+2]=='\\0':
idx+=2
ch=chr(0)
elif l2[idx:idx+2]=='\\t':
idx+=2
ch='\t'
elif l2[idx:idx+2]=='\\n':
idx+=2
ch='\n'
else:
ch=l2[idx]
idx+=1
outbin(pc,ord(ch))
pc+=1
def export_processing(l1,l2):
global export_labels
if not (pas==2 or pas==0):
return False
if upper(l1)!=".EXPORT":
return False
idx=0
l2+=chr(0)
while l2[idx]!=chr(0):
idx=skipspc(l2,idx)
s,idx=get_label_word(l2,idx)
if s=="":
break
if l2[idx]==':':
idx+=1
v=get_label_value(s)
sec=get_label_section(s)
export_labels[s]=[v,sec]
if l2[idx]==',':
idx+=1
return True
def ascii_processing(l1,l2):
if upper(l1)!=".ASCII":
return False
f=asciistr(l2)
return(f)
def asciiz_processing(l1,l2):
global pc
if upper(l1)!=".ASCIIZ":
return False
f=asciistr(l2)
if f:
outbin(pc,0x00)
pc+=1
return True
def include_pat(l):
idx=skipspc(l,0)
i=l[idx:idx+8]
i=i.upper()
if i!=".INCLUDE":
return []
s=get_string(l[8:])
w=readpat(s)
return w
def include_asm(l1,l2):
if upper(l1)!=".INCLUDE":
return False
s=get_string(l2)
if s:
fileassemble(s)
return True
def section_processing(l1,l2):
global current_section,sections
if upper(l1)!="SECTION" and upper(l1)!="SEGMENT":
return False
if l2!='':
current_section=l2
sections[l2]=[pc,0]
return True
def align_processing(l1,l2):
global pc,align
if upper(l1)!=".ALIGN":
return False
if l2!='':
u,idx=expression1(l2,0)
align=int(u)
pc=align_(pc)
return True
def printaddr(pc):
print("%016x: " % pc,end='')
def endsection_processing(l1,l2):
global sections
if upper(l1)!="ENDSECTION" and upper(l1)!="ENDSEGMENT":
return False
start=sections[current_section][0]
sections[current_section]=[start,pc-start]
return True
def org_processing(l1,l2):
global pc
if upper(l1)!=".ORG":
return False
u,idx=expression1(l2,0)
if l2[idx:idx+2].upper()==',P':
if u>pc:
for i in range(u-pc):
outbin2(i+pc,padding)
pc=u
return True
def lineassemble(line):
global pc,ln,error_undefined_label
ln+=1
line=line.replace('\t',' ').replace('\n','')
line=reduce_spaces(line)
line=remove_comment_asm(line)
if line=='':
return False
for i in pat:
if set_symbol(i): continue
line=label_processing(line)
clear_symbol([".clearsym","",""])
(l,idx)=get_param_to_spc(line,0)
(l2,idx)=get_param_to_eol(line,idx)
l=l.rstrip()
l2=l2.rstrip()
l=l.replace(' ','')
if section_processing(l,l2):
return True
if endsection_processing(l,l2):
return True
if ascii_processing(l,l2):
return True
if asciiz_processing(l,l2):
return True
if include_asm(l,l2):
return True
if align_processing(l,l2):
return True
if org_processing(l,l2):
return True
if labelc_processing(l,l2):
return True
if export_processing(l,l2):
return True
if l=="":
return False
idx=0
of=0
se=False
for i in pat:
for a in lower:
put_vars(a,VAR_UNDEF)
#
# i はパターンファイルのデータ
# l はアセンブリライン
#
if i is None: continue
if set_symbol(i): continue
if clear_symbol(i): continue
if paddingp(i): continue
if bits(i): continue
if bytep(i): continue
if symbolc(i): continue
lw=len([_ for _ in i if _])
if lw==0:
continue
lin=l+' '+l2
lin=reduce_spaces(lin)
if i[0]=='':
break
error_undefined_label=False
try:
if match0(lin,i[0])==True:
if lw==3:
error(i[1])
of=makeobj(i[2])
break
except:
se=True
break
else:
se=True
pc+=of
if (pas==2 or pas==0):
if error_undefined_label:
print(f"{current_file} : {ln} {cl}: undefined label error.")
return False
if se:
print(f"{current_file} : {ln} {cl}: Syntax error.")
return False
return True
def lineassemble0(line):
global cl
cl=line.replace('\n','')
if pas==2 or pas==0:
print("%016x " % pc,end='')
print(f"{current_file} {ln} {cl} " ,end='')
f=lineassemble(cl)
if pas==2 or pas==0:
print("")
return f
def option(l,o):
if o in l:
idx=l.index(o)
if idx+1<len(l):
if idx+2<len(l):
return l[0:idx]+l[idx+2:],l[idx+1]
else:
return l[0:idx],l[idx+1]
else:
return l[0:idx],''
return l,''
def file_input_from_stdin():
af=""
while True:
line=sys.stdin.readline().strip()
if line=='':
break
af+=line+'\n'
return af
def fileassemble(fn):
global current_file,fnstack,lnstack,ln,lines
fnstack+=[current_file]
lnstack+=[ln]
current_file=fn
ln=1
if fn=="stdin":
if pas!=2 and pas!=0:
af=file_input_from_stdin()
with open("axx.tmp","wt") as stdintmp:
stdintmp.write(af)
else:
pass
fn="axx.tmp"
f=open(fn,"rt")
af=f.readlines()
f.close()
for i in af:
lineassemble0(i)
if fnstack:
current_file=fnstack.pop()
ln=lnstack.pop()
def imp_label(l):
global labels
idx=skipspc(l,0)
(section,idx)=get_label_word(l,idx)
idx=skipspc(l,0)
(label,idx)=get_label_word(l,idx)
if label=='':
return False
idx=skipspc(l,idx)
(v,new_idx)=expression(l,idx)
if new_idx==idx:
return False
idx=new_idx
put_label_value(label,v,section)
return True
def main():
global pc,pas,ln,outfile,expfile,impfile,current_file,pat
if len(sys.argv)==1:
print("axx general assembler programmed and designed by Taisuke Maekawa")
print("Usage: python axx.py patternfile.axx [sourcefile.s] [-o outfile.bin] [-e export_labels.tsv] [-i import_labels.tsv]")
return
sys_argv=sys.argv
if len(sys_argv)>=2:
pat=readpat(sys_argv[1])
(sys_argv,expfile)=option(sys_argv,"-e")
(sys_argv,expefile)=option(sys_argv,"-E")
(sys_argv,outfile)=option(sys_argv,'-o')
(sys_argv,impfile)=option(sys_argv,"-i")
if impfile!="":
with open(impfile,"rt") as label_file:
while (l:=label_file.readline()):
imp_label(l)
try:
os.remove(outfile)
except:
pass
else:
pass
if outfile:
f=open(outfile,"wb")
f.close()
if len(sys_argv)==2:
pc=0
pas=0
ln=1
current_file="(stdin)"
while True:
printaddr(pc)
try:
line=input(">> ")
line=line.replace("\\\\","\\")
except EOFError: # EOF
break
line=line.strip()
if line=="":
continue
lineassemble0(line)
elif len(sys_argv)>=3:
pc=0
pas=1
ln=1
fileassemble(sys.argv[2])
pc=0
pas=2
ln=1
fileassemble(sys.argv[2])
if expefile!="":
expfile=expefile
elf=1
else:
elf=0
if expfile!="":
h=list(export_labels.items())
key=list(sections.keys())
with open(expfile,"wt") as label_file:
for i in key:
if i=='.text' and elf==1:
flag='AX'
elif i=='.data' and elf==1:
flag='WA'
else:
flag=''
start=sections[i][0]
label_file.write(f"{i}\t{start:#x}\t{sections[i][1]:#x}\t{flag}\n")
for i in h:
label_file.write(f"{i[0]}\t{i[1][0]:#x}\n")
if __name__=='__main__':
main()
exit(0)
GitHubリポジトリ(ソース・サンプルコード)
謝辞
問題を出してくれて、ヒントをくれた、師匠の浜田純市さんと東京電子設計と、協力してくれた電気通信大学と、計算機科学者さんと、コンピュータ技術者さんと、Googleと、Qiitaと、そして、忘れられない誰か達に感謝を述べさせていただきます。ありがとうございます。
一句
冬銀河自由に描く星座かな 公太郎