1
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'

Last updated at Posted at 2024-02-21

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.axx
/* 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は式を表すからです。これは、式の中のエスケープキャラクタを処理できないためです。

test.s
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

axx.py
#!/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と、そして、忘れられない誰か達に感謝を述べさせていただきます。ありがとうございます。

一句

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

1
0
12

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
1
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?