SNOBOL言語で8080アセンブラ
2018年4月、歴史的に貴重なDEC社 TOPS-20のSNOBOL言語で8080アセンブラ(ASM80.SNO)を作成しました。このブログでは、ASM80.SNOのコード編を書きます。
ASM80.SNOの設計編は、下記ブログを参照してください。
ASM80.SNOのSNOBOLコード説明
ASM80.SNOのSNOBOLコードの全体は、このブログの最後に示します。
SNOBOL言語で記述して約400ステップ規模で、8080アセンブラを作成することができました。SNOBOL言語の強力なパターンマッチ能力の力ですね。
入出力デバイス定義
ソースファイル名を入力して、入出力用デバイスの定義を行います。
*====================================================== * 8080 Assembler * OUTPUT("CON_DEV" , "TTY:" ,"T") INPUT( "KEY_DEV" , "TTY:") CON_DEV = "Input i8080 ASM Source ? " ISRC = KEY_DEV OLST = ISRC OLST (".ASM" | ".asm") = ".LST" INPUT( "INPUT_PS1" , "DSK:" ISRC) INPUT( "INPUT_PS2" , "DSK:" ISRC) OUTPUT("OUTPUT_LST" , "DSK:" OLST)
- 変数 ISRC : .ASM ソースファイル名(KEY_DEVから入力)
- 変数 OLST : .LST リスト出力ファイル名 (拡張子.ASMを.LSTに置換)
デバイスの定義を行います。
デバイス名 | デバイス |
---|---|
CON_DEV | TTY:出力 ターミナルモード |
KEY_DEV | TTY:入力 |
INPUT_PS1 | DSK:ISRC ソース入力 パス1用 |
INPUT_PS2 | DSK:ISRC ソース入力 パス2用 |
OUTPUT_LST | DSK:OLST リスト出力 |
関数定義
プログラムで使用するユーザー関数と演算子を定義します。
*------------------------------------------ * Function Define * DEFINE('ADDLTBL(LABEL,VAL) V') DEFINE('REFLTBL(LABEL) ') DEFINE('GETIMM(STR) IMM') DEFINE('TOUPPER(STR) UC,LC') DEFINE('CVSTR2BIN(STR,BASE) C,E,DIGIT,DGSET,N') DEFINE('CVBIN2HEX(BIN,N) Q,R') DEFINE('CVOCT2HEX(STRO) ') OPSYN( "%" , "REMDR" ,2) *================================================== :(START)
関数名 | 機能 | 概要 |
---|---|---|
ADDLTBL (LABEL,VAL) |
ラベル登録 | ラベルテーブルにLABELでVALを登録 |
REFLTBL (LABEL) |
ラベル参照 | ラベルテーブルからLABELを参照 |
GETIMM (STR) |
直値参照 | STRから直値を参照 |
TOUPPER (STR) |
英大文字変換 | STRを英大文字に変換 |
CVSTR2BIN (STR,BASE) |
文字列基数変換 | 文字列STRを基数(BASE)で整数に変換 |
CVBIN2HEX (BIN,N) |
整数ヘキサ変換 | 整数BINをN桁16進数に変換 |
CVOCT2HEX (STR) |
8進to16進変換 | 8進数文字列STRを16進数2桁に変換 |
演算子「%」を演算子シノニム定義で、剰余を算出する組み込み関数 REMDR()の呼び出しに定義します。すなわち、「N % D」は、REMDR(N,D)を呼び出します。
STARTラベルに分岐して、関数定義本体の実行をスキップします。
関数本体の定義
ラベル登録
ラベルテーブルにLABELをキーとしてVALを登録します。
*================================================== * Add Label Table ADDLTBL(LABEL,VAL) * ADDLTBL ADDLTBL = "" V = EQ(PASS ,1) TBL_LAB1<label> V = EQ(PASS ,2) TBL_LAB2<label> IDENT(V,"") :F(FRETURN) TBL_LAB1<label> = EQ(PASS ,1) VAL TBL_LAB2<label> = EQ(PASS ,2) VAL :(RETURN)
PASSによりふたつのラベルテーブルTBL_LAB1<○>とTBL_LAB2<○>を使い分けます。
- LABLEが登録済みの場合は、二重登録エラーなので FRETURNします。
- ラベルテーブル<LABEL>にVALを登録します。
ラベル参照
ラベルテーブルからLABELキーとして参照します。
*================================================== * Ref Label Table REFLTBL(LABEL) * REFLTBL REFLTBL = "0" EQ(PASS ,1) :F(REF.NX) IDENT(TBL_LAB1<LABEL>,"") :S(RETURN) REF.NX REFLTBL = TBL_LAB1<LABEL> :(RETURN)
PASS=1のとき、LABELが未登録なら “0”を返します。
パス1用のラベルテーブルTBL_LAB1<LABEL>を参照します。
直値参照
STRから直値を参照します。
*================================================== * Get Immediate Value GETIMM(STR) * GETIMM GETIMM = 0 PT_IMMH = SPAN("0123456789ABCDEF") . IMM "H" . HB REM PT_IMMB = SPAN("01") . IMM "B" . HB REM PT_IMM = SPAN("0123456789") . IMM REM PT_LAB = SPAN(LABEL_X) . IMM REM GETIMM.N HB = "D" STR (PT_IMMH | PT_IMMB | PT_IMM | PT_LAB) :F(FRETURN) BASE = 10 BASE = IDENT(HB ,"H") 16 BASE = IDENT(HB ,"B") 2 GETIMM = CVSTR2BIN(IMM,BASE) :S(RETURN) STR = REFLTBL(IMM) :(GETIMM.N)
- 直値のパターンに一致しない場合は、パターン不一致エラーで FRETURNします。
- 直値のパターンに一致した場合は、一致部分のIMMとHB識別を設定します。HB識別(”H” or “B” or “”)によりBASE(16 or 2 or 10)に変換します。
- 一致部分 IMMをBASE進数の数値に変換します。
- 数値変換が正常なら、数値を戻り値にします。
- 数値変換が異常なら、ラベルとみなしてIMMでラベル参照してSTRに設定して直値のパターンチェックを繰り返します。
英大文字変換
STRを英大文字に変換します。
*================================================== * Convert String Case Lower To Upper TOUPPER(STR) * TOUPPER TOUPPER = STR UC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' LC = 'abcdefghijklmnopqrstuvwxyz' TOUPPER = REPLACE(STR,LC,UC) :(RETURN)
文字列基数変換
文字列STRを基数(BASE)で整数に変換します。
*================================================== * Convert String To Binary CVSTR2BIN(STR,BASE) * CVSTR2BIN CVSTR2BIN = 0 C = 1 E = SIZE(STR) DGSET = SUBSTR("0123456789ABCDEF" ,1,BASE) CVS2B.LOOP DIGIT = SUBSTR(STR ,C ,1) DGSET BREAK(DIGIT) . N :F(FRETURN) CVSTR2BIN = CVSTR2BIN * BASE + SIZE(N) C = LT(C,E) C + 1 :S(CVS2B.LOOP) :(RETURN)
整数ヘキサ変換
整数BINをN桁16進数に変換します。
*================================================== * Convert Binary To Hex CVBIN2HEX(BIN,N) * CVBIN2HEX CVBIN2HEX = "" CVB2H.LOOP Q = BIN / 16 R = REMDR(BIN ,16) BIN = Q "0123456789ABCDEF" LEN(R) LEN(1) . S CVBIN2HEX = S CVBIN2HEX N = GT(N,1) N - 1 :S(CVB2H.LOOP) :(RETURN)
8進to16進変換
8進数文字列STRを16進数2桁に変換します。
*================================================== * Convert STR(Oct) To STR(Hex) CVOCT2HEX(STRO) * CVOCT2HEX CVOCT2HEX = "" CVOCT2HEX = CVBIN2HEX(CVSTR2BIN(STRO,8),2) :(RETURN)
各種の定義
*====================================================== START OUTPUT = "i8080 Assembler START " ISRC " ---> " OLST *---------------------------- * EQU / LABEL Define * TBL_LAB1 = TABLE() TBL_LAB2 = TABLE() LABEL_X = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" PT_LABEL = (POS(0) SPAN(LABEL_X) . LABEL ":")
ASM80の開始メッセージを表示します。
ラベルテーブルを定義します。
テーブル名 | 用途 |
---|---|
TBL_LAB1 | パス1用ラベルテーブル |
TBL_LAB2 | パス2用ラベルテーブル |
ラベルのパターン(PT_LABEL)を定義します。左端から英字、数字、アンダーラインの組合せの後にコロン(:)がついた文字列をラベルとします。
8080命令コードの定義
* * 8080 Operation Code Define OP Code Octal "377" "+Len" * OPCODE = TABLE() OPCODE<"MOV" > = "1DS+1"; OPCODE<"MVI" > = "0D6+2" OPCODE<"INR" > = "0R4+1"; OPCODE<"DCR" > = "0R5+1" OPCODE<"ADD" > = "20R+1"; OPCODE<"ADC" > = "21R+1" OPCODE<"SUB" > = "22R+1"; OPCODE<"SBB" > = "23R+1" OPCODE<"ANA" > = "24R+1"; OPCODE<"XRA" > = "25R+1" OPCODE<"ORA" > = "26R+1"; OPCODE<"CMP" > = "27R+1" OPCODE<"ADI" > = "306+2"; OPCODE<"ACI" > = "316+2" OPCODE<"SUI" > = "326+2"; OPCODE<"SBI" > = "336+2" OPCODE<"ANI" > = "346+2"; OPCODE<"XRI" > = "356+2" OPCODE<"ORI" > = "366+2"; OPCODE<"CPI" > = "376+2" OPCODE<"RLC" > = "007+1"; OPCODE<"RRC" > = "017+1" OPCODE<"RAL" > = "027+1"; OPCODE<"RAR" > = "037+1" OPCODE<"OUT" > = "323+2"; OPCODE<"IN" > = "333+2" OPCODE<"HLT" > = "166+1"; OPCODE<"RST" > = "3V7+1" OPCODE<"PUSH"> = "3R5+1"; OPCODE<"POP" > = "3R1+1" OPCODE<"SHLD"> = "042+3"; OPCODE<"LHLD"> = "052+3" OPCODE<"STA" > = "062+3"; OPCODE<"LDA" > = "072+3" OPCODE<"LXI" > = "0P1+3"; OPCODE<"DAD" > = "0Q1+1" OPCODE<"STAX"> = "0P2+1"; OPCODE<"LDAX"> = "0Q2+1" OPCODE<"INX" > = "0P3+1"; OPCODE<"DCX" > = "0Q3+1" OPCODE<"JMP" > = "303+3" OPCODE<"JC" > = "332+3"; OPCODE<"JNC" > = "322+3" OPCODE<"JZ" > = "312+3"; OPCODE<"JNZ" > = "302+3" OPCODE<"JP" > = "362+3"; OPCODE<"JM" > = "372+3" OPCODE<"JPE" > = "352+3"; OPCODE<"JPO" > = "342+3" OPCODE<"CALL"> = "315+3" OPCODE<"CC" > = "334+3"; OPCODE<"CNC" > = "324+3" OPCODE<"CZ" > = "314+3"; OPCODE<"CNZ" > = "304+3" OPCODE<"CP" > = "364+3"; OPCODE<"CM" > = "374+3" OPCODE<"CPE" > = "354+3"; OPCODE<"CPO" > = "344+3" OPCODE<"RET" > = "311+1" OPCODE<"RNZ" > = "300+1"; OPCODE<"RZ" > = "310+1" OPCODE<"RNC" > = "320+1"; OPCODE<"RC" > = "330+1" OPCODE<"RPO" > = "340+1"; OPCODE<"RPE" > = "350+1" OPCODE<"RP" > = "360+1"; OPCODE<"RM" > = "370+1" OPCODE<"XCHG"> = "353+1"; OPCODE<"XTHL"> = "343+1" OPCODE<"SPHL"> = "371+1"; OPCODE<"PCHL"> = "351+1" OPCODE<"CMA" > = "057+1"; OPCODE<"STC" > = "067+1" OPCODE<"CMC" > = "077+1"; OPCODE<"DAA" > = "047+1" OPCODE<"EI" > = "373+1"; OPCODE<"DI" > = "363+1" OPCODE<"NOP" > = "000+1"
OPCODEテーブルに命令をキーとして命令コードと命令長を登録します。命令コードは、8進数3桁で8bitを表します。プラス(+)の後ろに命令長を1~3で定義します。
「OPCODE<“JMP” > = “303+3″」は、JMP命令の定義です。
3バイト命令長で、命令コードは8進数で(303)=2進数で(11 000 011)=16進数で(C3)です。
「OPCODE<“MOV” > = “1DS+1″」は、MOV命令の定義です。
1バイト命令長で、命令コードは8進数で(1DS)=2進数で(01 ddd sss)で、dddは転送先レジスタ、sssは転送元レジスタです。
疑似命令の定義
*----------------------------------- * PSEUDO INSTRUCTION * P_PSEUDO = "ORG" | "DB" | "DW" | "DS" | "EQU"
以下の疑似命令をサポートします。
機能 | 疑似命令 |
---|---|
定数定義 | ラベル名 EQU 数値 |
アドレス設定 | ORG アドレス |
1バイトの定数定義 | DB バイト定数 |
2バイトの定数定義 | DW ワード定数 |
領域定義 | DS 領域数 |
8080命令のオペランド定義
*----------------------------------- * 8080 MPU INSTRUCTION * A_BD = "LDAX" | "STAX" A_BDHS = "DAD" | "INX" | "DCX" A_RM_RM = "MOV" A_RM_IMM = "MVI" A_BDHS_I = "LXI" A_BDHA = "PUSH" | "POP" A_VEC = "RST" A_RM = "INR" | "DCR" | "ADD" | "ADC" | "SUB" | "SBB" | "ANA" | "XRA" | "ORA" | "CMP" A_IMM = "ADI" | "ACI" | "SUI" | "SBI" | "ANI" | "XRI" | "ORI" | "CPI" A_PORT = "IN" | "OUT" A_NON = "HLT" | "RLC" | "RRC" | "RAL" | "RAR" A_NON = A_NON | "RET" | "RC" | "RNC" | "RZ" | "RNZ" | "RPE" | "RPO" | "RP" | "RM" A_NON = A_NON | "XCHG" | "XTHL" | "SPHL" | "PCHL" A_NON = A_NON | "CMA" | "STC" | "CMC" | "DAA" | "EI" | "DI" | "NOP" A_ADR = "JMP" | "JC" | "JNC" | "JZ" | "JNZ" | "JPE" | "JPO" | "JP" | "JM" A_ADR = A_ADR | "CALL" | "CC" | "CNC" | "CZ" | "CNZ" | "CPE" | "CPO" | "CP" | "CM" A_ADR = A_ADR | "LDA" | "STA" | "LHLD" | "SHLD" * P_INST1 = A_BDHS | A_BD P_INST2 = A_RM_RM | A_RM_IMM | A_RM | A_IMM | A_VEC | A_NON | A_BDHA | A_ADR | A_BDHS_I | A_PORT
8080命令をオペランドの形式で分類して定義します。
形式 | 命令 | オペランド部 |
---|---|---|
RM_RM | MOV | Rd,Rs |
RM_IMM | MVI | Rd,Imd8 |
RM | INR/DCR ADD/ADC/SUB/SBB ANA/XRA/ORA/CMP |
R |
IMM | ADI/ACI/SUI/SBI ANI/XRI/ORI/CPI |
Imd8 |
PORT | IN /OUT | Port |
NON | HLT/EI /DI /NOP RLC/RRC/RAL/RAR RET RC /RNC/RZ /RNZ RP /RM /RPE/RPO XCHG/XTHL/SPHL/PCHL CMA/STC/CMC/DAA |
|
ADR | JMP JC /JNC/JZ /JNZ JP /JM /JPE/JPO CALL CC /CNC/CZ /CNZ CP /CM /CPE/CPO LDA /STA/LHLD/SHLD |
Adr |
VEC | RST | Vect |
BDHA | PUSH/POP | Rp |
BD | LDAX/STAX | Rp |
BDHS | DAD/INX/DCX | Rp |
BDHS_I | LXI | Rp,Imd16 |
命令のパターン P_INST を定義します。
LINEの開始処理
OBJHEX = ARRAY(3) PASS = 1 FILE_START INPUT_DEV = EQ(PASS ,1) "INPUT_PS1" INPUT_DEV = EQ(PASS ,2) "INPUT_PS2" PC = 0 LN = 1 LINE_START MSG_ERR = ""; ERSW = 0 OPLEN = 0 LINESRC = $INPUT_DEV :F(FILE_END) LINE = TOUPPER(LINESRC) LINE (";" REM) = LINE PT_LABEL = :F(LINE_NX) ADDLTBL(LABEL,PC) :F(ERR_LABEL) LINE_NX LINE ( " END" ) = :S(OUT_OBJ) LINE = TRIM(LINE) EQ(SIZE(LINE),0) :S(OUT_OBJ)
- 生成オブジェクトを格納するOBJHEX配列を3個定義します。
- PASS←1に設定
- PASSにより INPUT_DEVデバイスを設定します。INPUT_PSx (x=1or2)
- プログラムカウンタ PC←0に設定します。
- 行カウンタ LN←1に設定します。
- エラーをリセット(ERSW←0)します。
- 命令長 OPLEN←0に設定します。
- 変数LINESRCにソースコードを入力します。EOFなら、:F(FILE_END)に分岐します。
- 変数LINEに英大文字変換して格納します。
- セミコロン(;)から後ろを消して、コメントを除去します。
- ラベルパターンに一致した場合は、ラベル部をLABELに設定してラベルテーブルにキーLABELでPCを登録します。
- END文なら、OUT_OBJに分岐します。
- 行の右端の不要な空白を除去し、空行ならOUT_OBJに分岐します。
疑似命令と命令のパターン一致
* LINE (P_PSEUDO . PSEUDO (NULL | " ")) = :S($PSEUDO) LINE (P_INST1 . INST (NULL | " ")) = :S(INST_OP) LINE (P_INST2 . INST (NULL | " ")) = :S(INST_OP) :(ERR_INST) INST_OP OPC = OPCODE OPC ("+" ANY("0123")) . OPLEN = INST (A_BD ) :S(X_BD) INST (A_BDHS ) :S(X_BDHS) INST (A_RM_RM ) :S(X_RM_RM) INST (A_RM_IMM ) :S(X_RM_IMM) INST (A_RM ) :S(X_RM) INST (A_IMM ) :S(X_IMM) INST (A_PORT ) :S(X_PORT) INST (A_NON ) :S(X_NON) INST (A_ADR ) :S(X_ADR) INST (A_VEC ) :S(X_VEC) INST (A_BDHA ) :S(X_BDHA) INST (A_BDHS_I ) :S(X_BDHS_I) :(ERR_INST)
疑似命令(P_PSEUDO)にパターン一致した場合は、一致部分を変数PSEUDOに格納して S($PSEUDO)に分岐します。変数PSEUDOの内容に($PSEUDO)で分岐できるのは、SNOBOL言語の凄さです。
ラベル ORG or DB or DW or DS or EQU に分岐します。
P_PSEUDO = “ORG” | “DB” | “DW” | “DS” | “EQU”
命令パターン(P_INST1/P_INST2)に一致した場合は、一致部分を変数INSTに格納します。命令パターンに一致しない場合は、ERR_INSTに分岐して命令エラーとします。
P_INST1 と P_INST2 に分離したのは、意味があります。SNOBOLパターンマッチの記述方法に不備があるのか、ミスマッチが発生しました。
- LDAX命令が”LDA”に一致
- STAX命令が”STA”に一致
- INX命令が”IN”に一致
そこで、最初にP_INST1でパターンマッチし、不一致の場合だけP_INS2のパターンマッチを行います。命令コードを変数 OPCに設定して、 プラス(+)の後ろの命令長を変数 OPLENに設定します。命令 INST の種別により、各オペランド解析形式へ分岐します。
エラー処理
* ERR_REG MSG_ERR = " ; Error! Registor " :(ERROR_LST) ERR_INST MSG_ERR = " ; Error! Instruction " :(ERROR_LST) ERR_ADR MSG_ERR = " ; Error! Address " :(ERROR_LST) ERR_IMM MSG_ERR = " ; Error! Immediate " :(ERROR_LST) ERR_PORT MSG_ERR = " ; Error! IO Port " :(ERROR_LST) ERR_VEC MSG_ERR = " ; Error! Vec (0..7) " :(ERROR_LST) ERR_LABEL MSG_ERR = " ; Error! Duplication LABEL " :(ERROR_LST) * ERROR_LST OPLEN = 0; ERSW = 1 *
各エラーによりMSG_ERRにエラーメッセージを格納して、命令長 OPLEN←0として、ERSW←1とします。
オブジェクト出力
OUT_OBJ EQ(PASS,2) :F(OUT_NX) ADR = CVBIN2HEX(PC,4) OBJ = EQ(OPLEN ,0) " " " " OBJ = GE(OPLEN ,1) ADR " " OBJ = GE(OPLEN ,1) OBJ OBJHEX<1> OBJ = GE(OPLEN ,2) OBJ OBJHEX<2> OBJ = GE(OPLEN ,3) OBJ OBJHEX<3> OBJ = OBJ DUPL(" ",14 - SIZE(OBJ)) OUTPUT_LST = OBJ LINESRC MSG_ERR EQ(ERSW,0) :S(OUT_NX) ERR = LPAD(LN ,5 ," ") " " LINESRC MSG_ERR OUTPUT = ERR OUT_NX PC = PC + OPLEN LN = LN + 1 :(LINE_START)
- PASS=2のとき、LSTファイルを作成してデバイスOUTPUT_LSTに出力します。
- エラーがある場合(ERSW=1)は、行番号、ソース(LINESRC)、エラーメッセージ(MSG_ERR)を出力します。
- プログラムカウンタ PCを命令長 OPLEN進めます。
- 行カウンタ LNをインクリメントします。
- LINE_STARTに分岐して、次の行へ進みます。
EOFとパスの終了
FILE_END PASS = LT(PASS,2) PASS + 1 :S(FILE_START) PASS_END OUTPUT = "i8080 Assembler END.. " ISRC " ---> " OLST :(END)
- ソースファイル入力がEOFになれば、PASSを進めます。
- パス2が終了すれば、ASM80の終了メッセージを表示しENDに分岐して終了します。
各疑似命令の処理
疑似命令 ORG、DB、DW、DS、EQUの処理を行います。
ORG 疑似命令
*-------------------------------------------------(ORG xxxx) ORG PC = GETIMM(LINE) :F(ERR_ADR) :(OUT_OBJ)
- LINEからGETIMM()でアドレスを取得して、PCに設定します。
DB 疑似命令
*-------------------------------------------------(DB IMM8) DB IMM = GETIMM(LINE) :F(ERR_IMM) GT(IMM,255) :S(ERR_IMM) OPLEN = 1 OBJHEX<1> = CVBIN2HEX(IMM,2) :(OUT_OBJ)
- LINEからGETIMM()で直値(IMM)を取得します。1バイトの定数定義なので、255を超える場合はIMMエラーに分岐します。
- 命令長 OPLEN←1として、オブジェクトの1バイト目に定数IMMを設定します。
DW 疑似命令
*-------------------------------------------------(DW IMM16) DW AD = GETIMM(LINE) :F(ERR_IMM) OPLEN = 2 OBJHEX<1> = CVBIN2HEX(AD % 256 ,2) OBJHEX<2> = CVBIN2HEX(AD / 256 ,2) :(OUT_OBJ)
- LINEからGETIMM()で直値(AD)を取得します。
- 命令長 OPLEN←2として、オブジェクトの1バイト目に定数ADの下位バイト、2バイト目にADの上位バイトを設定します。
DS 疑似命令
*-------------------------------------------------(ORG xxxx) DS IMM = GETIMM(LINE) :F(ERR_IMM) OPLEN = IMM OBJHEX<1> = "??" OBJHEX<2> = "??" OBJHEX<3> = "??" :(OUT_OBJ)
- LINEからGETIMM()で直値(IMM)を取得します。
- 命令長 OPLEN←IMMとして領域を確保します。
- オブジェクトの1~3バイト目には、”??”を設定します。
EQU 疑似命令
*-------------------------------------------------(EQU xxxx) EQU LINE (SPAN(LABEL_X) . DEFLAB " ") = :F(ERR_LABEL) IMM = GETIMM(LINE) :F(ERR_IMM) ADDLTBL(DEFLAB,IMM) :F(ERR_LABEL) :(OUT_OBJ)
- ラベルの形式でなければ、LABELエラーに分岐します。ラベルの場合は、DEFLABにラベルを設定します。
- GETIMM()で、直値(IMM)を取得します。
- ラベルDEFLABをキーとしてIMMをラベル登録します。
8080命令の処理
オペランド形式ごとに、オペランドの解析をしてオブジェクトコードをOBJHEX<>配列に設定します。
命令 オペランド RM_RM形式
MOV命令は、オペランドに転送先レジスタと転送元レジスタを指定します。
転送先 | 転送元 |
---|---|
A,B,C,D,E,H,L | A,B,C,D,E,H,L |
A,B,C,D,E,H,L | M |
M | A,B,C,D,E,H,L |
* *-------------------------------------------------(MOV RM,RM) * MOV X_RM_RM REGNM = "ABCDEHL" MOVRR = ANY(REGNM) . REGD "," ANY(REGNM) . REGS MOVRM = ANY(REGNM) . REGD "," "M" . REGS MOVMR = "M" . REGD "," ANY(REGNM) . REGS LINE (MOVRR | MOVRM | MOVMR) = :F(ERR_REG) OPC "D" = REPLACE(REGD ,"BCDEHLMA" ,"01234567") OPC "S" = REPLACE(REGS ,"BCDEHLMA" ,"01234567") OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- 該当レジスタ以外のときは、レジスタエラーとします。
- レジスタ名(B,C,D,E,H,L,M,A)をコード(0,1,2,3,4,5,6,7)に変換します。
- 命令コード OPCのレジスタ部分(D S)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド RM_IMM形式
MVI命令は、オペランドにレジスタ(B,C,D,E,H,L,M,A)とバイト定数を指定します。
*-------------------------------------------------(MVI RM,IMD8) * MVI X_RM_IMM LINE (ANY("ABCDEHLM") . REGD "," ) = :F(ERR_REG) OPC "D" = REPLACE(REGD ,"BCDEHLMA" ,"01234567") IMM = GETIMM(LINE) :F(ERR_IMM) GT(IMM,255) :S(ERR_IMM) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(IMM,2) :(OUT_OBJ)
- 該当レジスタ以外のときは、レジスタエラーとします。
- GETIMM()で、カンマ”,”の右側のバイト定数(IMM)を取得します。1バイトの定数定義なので、255を超える場合はIMMエラーとします。
- レジスタ名(B,C,D,E,H,L,M,A)をコード(0,1,2,3,4,5,6,7)に変換します。
- 命令コード OPCのレジスタ部分(D)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
- オブジェクトの2バイト目にIMMを設定します。
命令 オペランド RM形式
下記命令は、オペランドにレジスタ(B,C,D,E,H,L,M,A)とバイト定数を指定します。
INR/DCR
ADD/ADC/SUB/SBB/ANA/XRA/ORA/CMP
*-------------------------------------------------(xxx RM) * INR/DCR * ADD/ADC/SUB/SBB/ANA/XRA/ORA/CMP X_RM LINE (ANY("ABCDEHLM") . REG) = :F(ERR_REG) OPC "R" = REPLACE(REGD ,"BCDEHLMA" ,"01234567") OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- 該当レジスタ以外のときは、レジスタエラーとします。
- レジスタ名(B,C,D,E,H,L,M,A)をコード(0,1,2,3,4,5,6,7)に変換します。
- 命令コード OPCのレジスタ部分(D)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド IMM形式
下記命令は、オペランドにバイト定数を指定します。
ADI/ACI/SUI/SBI/ANI/XRI/ORI/CPI
*-------------------------------------------------(xxx IMM8) * ADI/ACI/SUI/SBI/ANI/XRI/ORI/CPI X_IMM IMM = GETIMM(LINE) :F(ERR_IMM) GT(IMM,255) :S(ERR_IMM) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(IMM,2) :(OUT_OBJ)
- GETIMM()で、バイト定数(IMM)を取得します。1バイトの定数定義なので、255を超える場合はIMMエラーとします。
- オブジェクトの1バイト目に命令コード OPCを設定します。
- オブジェクトの2バイト目にバイト定数 IMMを設定します。
命令 オペランド PORT形式
下記命令は、オペランドにバイト定数を指定します。
IN/OUT
*-------------------------------------------------(xxx PORT) * IN/OUT X_PORT PORT = GETIMM(LINE) :F(ERR_IMM) GT(PORT,255) :S(ERR_PORT) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(PORT,2) :(OUT_OBJ)
- GETIMM()で、バイト定数(PORT)を取得します。1バイトの定数定義なので、255を超える場合はIMMエラーとします。
- オブジェクトの1バイト目に命令コード OPCを設定します。
- オブジェクトの2バイト目にバイト定数 PORTを設定します。
命令 オペランド NON形式
下記命令は、オペランドがありません。
HLT/RLC/RRC/RAL/RAR
RET/RC /RNC/RZ /RNZ/RP /RM /RPE/RPO
XCHG/XTHL/SPHL/PCHL
CMA/STC/CMC/DAA/EI /DI /NOP
*-------------------------------------------------(xxx) * HLT/RLC/RRC/RAL/RAR * RET/RC /RNC/RZ /RNZ/RP /RM /RPE/RPO * XCHG/XTHL/SPHL/PCHL * CMA/STC/CMC/DAA/EI /DI /NOP X_NON OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド ADR形式
下記命令は、オペランドにアドレスを指定します。
JMP /JC /JNC/JZ /JNZ/JP /JM /JPE/JPO
CALL/CC /CNC/CZ /CNZ/CP /CM /CPE/CPO
LDA /STA/LHLD/SHLD
*-------------------------------------------------(xxx ADR) * JMP /JC /JNC/JZ /JNZ/JP /JM /JPE/JPO * CALL/CC /CNC/CZ /CNZ/CP /CM /CPE/CPO * LDA /STA/LHLD/SHLD X_ADR ADR = GETIMM(LINE) :F(ERR_ADR) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(ADR % 256 ,2) OBJHEX<3> = CVBIN2HEX(ADR / 256 ,2) :(OUT_OBJ)
- GETIMM()で、ワード定数(ADR)を取得します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
- オブジェクトの2バイト目にADRの下位バイト、3バイト目にADRの上位バイトを設定します。
命令 オペランド VEC形式
RST命令は、オペランドにベクトルを指定します。
*-------------------------------------------------(RST Vect) * RST X_VEC VEC = GETIMM(LINE) :F(ERR_IMM) GT(VEC,7) :S(ERR_VEC) OPC "V" = VEC OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- GETIMM()で、定数(VEC)を取得します。0~7の定数定義なので、7を超える場合はVECエラーとします。
- ベクトル(0,1,2,3,4,5,6,7)をコード(0,1,2,3,4,5,6,7)に変換します。
- 命令コード OPCのベクトル部分(V)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド BDHA形式
PUSH/POP命令は、オペランドにレジスタ(B,D,H,PSW)を指定します。
*-------------------------------------------------(xxx BDHA) * PUSH/POP X_BDHA LINE (ANY("BDH") | "PSW") . REG = :F(ERR_REG) REG "PSW" = "A" OPC "R" = REPLACE(REG ,"BDHA" ,"0246") OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- 該当レジスタ以外のときは、レジスタエラーとします。
- レジスタ名(B,D,H,PSW)をコード(0,2,4,6)に変換します。
- 命令コード OPCのレジスタ部分(R)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド BD形式
LDAX/STAX命令は、オペランドにレジスタ(B,D)を指定します。
*-------------------------------------------------(xxx BD) * LDAX/STAX X_BD LINE (ANY("BD") . REG) = :F(ERR_REG) REG = REPLACE(REG ,"BD" ,"02") OPC "P" = REG OPC "Q" = REG + 1 OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- 該当レジスタ以外のときは、レジスタエラーとします。
- レジスタ名(B,D)をコード(0,2)に変換します。
- 命令コード OPCのレジスタ部分(P Q)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド BDHS形式
DAD/INX/DCX命令は、オペランドにレジスタ(B,D,H,SP)を指定します。
*-------------------------------------------------(xxx BDHS) * DAD/INX/DCX X_BDHS LINE (ANY("BDH") | "SP") . REG = :F(ERR_REG) REG "SP" = "S" REG = REPLACE(REG ,"BDHS" ,"0246") OPC "P" = REG OPC "Q" = REG + 1 OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ)
- 該当レジスタ以外のときは、レジスタエラーとします。
- レジスタ名(B,D,H,SP)をコード(0,2,4,6)に変換します。
- 命令コード OPCのレジスタ部分(P Q)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
命令 オペランド BDHS_I形式
LXI命令は、オペランドにレジスタ(B,D,H,PSW)とワード定数 を指定します。
*-------------------------------------------------(xxx BDHS,IMM16) * LXI X_BDHS_I LINE (ANY("BDH") | "SP") . REG "," = :F(ERR_REG) REG "SP" = "S" ADR = GETIMM(LINE) :F(ERR_ADR) OPC "P" = REPLACE(REG ,"BDHS" ,"0246") OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(ADR % 256 ,2) OBJHEX<3> = CVBIN2HEX(ADR / 256 ,2) :(OUT_OBJ) END
- 該当レジスタ以外のときは、レジスタエラーとします。
- GETIMM()で、カンマ”,”の右側のワード定数(ADR)を取得します。
- レジスタ名(B,D,H,PSW)をコード(0,2,4,6)に変換します。
- 命令コード OPCのレジスタ部分(P)をコードで置換します。
- オブジェクトの1バイト目に命令コード OPCを設定します。
- オブジェクトの2バイト目にADRの下位バイト、3バイト目にADRの上位バイトを設定します。
SNOBOLによる8080アセンブラコード完成
SNOBOL言語による8080アセンブラのコードが完成しました。強力なパターンマッチ能力で簡潔にASM80.SNOを記述できました。
ASM80.SNOのテストについては、次回に書きます。
ASM80.SNO ソースコード
ASM80.SNOのSNOBOLコードの全体を示します。
ソースコード
*====================================================== * 8080 Assembler * OUTPUT("CON_DEV" , "TTY:" ,"T") INPUT( "KEY_DEV" , "TTY:") CON_DEV = "Input i8080 ASM Source ? " ISRC = KEY_DEV OLST = ISRC OLST (".ASM" | ".asm") = ".LST" INPUT( "INPUT_PS1" , "DSK:" ISRC) INPUT( "INPUT_PS2" , "DSK:" ISRC) OUTPUT("OUTPUT_LST" , "DSK:" OLST) *------------------------------------------ * Function Define * DEFINE('ADDLTBL(LABEL,VAL) V') DEFINE('REFLTBL(LABEL) ') DEFINE('GETIMM(STR) IMM') DEFINE('TOUPPER(STR) UC,LC') DEFINE('CVSTR2BIN(STR,BASE) C,E,DIGIT,DGSET,N') DEFINE('CVBIN2HEX(BIN,N) Q,R') DEFINE('CVOCT2HEX(STRO) ') OPSYN( "%" , "REMDR" ,2) *================================================== :(START) *================================================== * Add Label Table ADDLTBL(LABEL,VAL) * ADDLTBL ADDLTBL = "" V = EQ(PASS ,1) TBL_LAB1<LABEL> V = EQ(PASS ,2) TBL_LAB2<LABEL> IDENT(V,"") :F(FRETURN) TBL_LAB1<LABEL> = EQ(PASS ,1) VAL TBL_LAB2<LABEL> = EQ(PASS ,2) VAL :(RETURN) *================================================== * Ref Label Table REFLTBL(LABEL) * REFLTBL REFLTBL = "0" EQ(PASS ,1) :F(REF.NX) IDENT(TBL_LAB1<LABEL>,"") :S(RETURN) REF.NX REFLTBL = TBL_LAB1<LABEL> :(RETURN) *================================================== * Get Immediate Value GETIMM(STR) * GETIMM GETIMM = 0 PT_IMMH = SPAN("0123456789ABCDEF") . IMM "H" . HB REM PT_IMMB = SPAN("01") . IMM "B" . HB REM PT_IMM = SPAN("0123456789") . IMM REM PT_LAB = SPAN(LABEL_X) . IMM REM GETIMM.N HB = "D" STR (PT_IMMH | PT_IMMB | PT_IMM | PT_LAB) :F(FRETURN) BASE = 10 BASE = IDENT(HB ,"H") 16 BASE = IDENT(HB ,"B") 2 GETIMM = CVSTR2BIN(IMM,BASE) :S(RETURN) STR = REFLTBL(IMM) :(GETIMM.N) *================================================== * Convert String Case Lower To Upper TOUPPER(STR) * TOUPPER TOUPPER = STR UC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' LC = 'abcdefghijklmnopqrstuvwxyz' TOUPPER = REPLACE(STR,LC,UC) :(RETURN) *================================================== * Convert String To Binary CVSTR2BIN(STR,BASE) * CVSTR2BIN CVSTR2BIN = 0 C = 1 E = SIZE(STR) DGSET = SUBSTR("0123456789ABCDEF" ,1,BASE) CVS2B.LOOP DIGIT = SUBSTR(STR ,C ,1) DGSET BREAK(DIGIT) . N :F(FRETURN) CVSTR2BIN = CVSTR2BIN * BASE + SIZE(N) C = LT(C,E) C + 1 :S(CVS2B.LOOP) :(RETURN) *================================================== * Convert Binary To Hex CVBIN2HEX(BIN,N) * CVBIN2HEX CVBIN2HEX = "" CVB2H.LOOP Q = BIN / 16 R = REMDR(BIN ,16) BIN = Q "0123456789ABCDEF" LEN(R) LEN(1) . S CVBIN2HEX = S CVBIN2HEX N = GT(N,1) N - 1 :S(CVB2H.LOOP) :(RETURN) *================================================== * Convert STR(Oct) To STR(Hex) CVOCT2HEX(STRO) * CVOCT2HEX CVOCT2HEX = "" CVOCT2HEX = CVBIN2HEX(CVSTR2BIN(STRO,8),2) :(RETURN) *====================================================== START OUTPUT = "i8080 Assembler START " ISRC " ---> " OLST *---------------------------- * EQU / LABEL Define * TBL_LAB1 = TABLE() TBL_LAB2 = TABLE() LABEL_X = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" PT_LABEL = (POS(0) SPAN(LABEL_X) . LABEL ":") * * 8080 Operation Code Define OP Code Octal "377" "+Len" * OPCODE = TABLE() OPCODE<"MOV" > = "1DS+1"; OPCODE<"MVI" > = "0D6+2" OPCODE<"INR" > = "0R4+1"; OPCODE<"DCR" > = "0R5+1" OPCODE<"ADD" > = "20R+1"; OPCODE<"ADC" > = "21R+1" OPCODE<"SUB" > = "22R+1"; OPCODE<"SBB" > = "23R+1" OPCODE<"ANA" > = "24R+1"; OPCODE<"XRA" > = "25R+1" OPCODE<"ORA" > = "26R+1"; OPCODE<"CMP" > = "27R+1" OPCODE<"ADI" > = "306+2"; OPCODE<"ACI" > = "316+2" OPCODE<"SUI" > = "326+2"; OPCODE<"SBI" > = "336+2" OPCODE<"ANI" > = "346+2"; OPCODE<"XRI" > = "356+2" OPCODE<"ORI" > = "366+2"; OPCODE<"CPI" > = "376+2" OPCODE<"RLC" > = "007+1"; OPCODE<"RRC" > = "017+1" OPCODE<"RAL" > = "027+1"; OPCODE<"RAR" > = "037+1" OPCODE<"OUT" > = "323+2"; OPCODE<"IN" > = "333+2" OPCODE<"HLT" > = "166+1"; OPCODE<"RST" > = "3V7+1" OPCODE<"PUSH"> = "3R5+1"; OPCODE<"POP" > = "3R1+1" OPCODE<"SHLD"> = "042+3"; OPCODE<"LHLD"> = "052+3" OPCODE<"STA" > = "062+3"; OPCODE<"LDA" > = "072+3" OPCODE<"LXI" > = "0P1+3"; OPCODE<"DAD" > = "0Q1+1" OPCODE<"STAX"> = "0P2+1"; OPCODE<"LDAX"> = "0Q2+1" OPCODE<"INX" > = "0P3+1"; OPCODE<"DCX" > = "0Q3+1" OPCODE<"JMP" > = "303+3" OPCODE<"JC" > = "332+3"; OPCODE<"JNC" > = "322+3" OPCODE<"JZ" > = "312+3"; OPCODE<"JNZ" > = "302+3" OPCODE<"JP" > = "362+3"; OPCODE<"JM" > = "372+3" OPCODE<"JPE" > = "352+3"; OPCODE<"JPO" > = "342+3" OPCODE<"CALL"> = "315+3" OPCODE<"CC" > = "334+3"; OPCODE<"CNC" > = "324+3" OPCODE<"CZ" > = "314+3"; OPCODE<"CNZ" > = "304+3" OPCODE<"CP" > = "364+3"; OPCODE<"CM" > = "374+3" OPCODE<"CPE" > = "354+3"; OPCODE<"CPO" > = "344+3" OPCODE<"RET" > = "311+1" OPCODE<"RNZ" > = "300+1"; OPCODE<"RZ" > = "310+1" OPCODE<"RNC" > = "320+1"; OPCODE<"RC" > = "330+1" OPCODE<"RPO" > = "340+1"; OPCODE<"RPE" > = "350+1" OPCODE<"RP" > = "360+1"; OPCODE<"RM" > = "370+1" OPCODE<"XCHG"> = "353+1"; OPCODE<"XTHL"> = "343+1" OPCODE<"SPHL"> = "371+1"; OPCODE<"PCHL"> = "351+1" OPCODE<"CMA" > = "057+1"; OPCODE<"STC" > = "067+1" OPCODE<"CMC" > = "077+1"; OPCODE<"DAA" > = "047+1" OPCODE<"EI" > = "373+1"; OPCODE<"DI" > = "363+1" OPCODE<"NOP" > = "000+1" *----------------------------------- * PSEUDO INSTRUCTION * P_PSEUDO = "ORG" | "DB" | "DW" | "DS" | "EQU" *----------------------------------- * 8080 MPU INSTRUCTION * A_BD = "LDAX" | "STAX" A_BDHS = "DAD" | "INX" | "DCX" A_RM_RM = "MOV" A_RM_IMM = "MVI" A_BDHS_I = "LXI" A_BDHA = "PUSH" | "POP" A_VEC = "RST" A_RM = "INR" | "DCR" | "ADD" | "ADC" | "SUB" | "SBB" | "ANA" | "XRA" | "ORA" | "CMP" A_IMM = "ADI" | "ACI" | "SUI" | "SBI" | "ANI" | "XRI" | "ORI" | "CPI" A_PORT = "IN" | "OUT" A_NON = "HLT" | "RLC" | "RRC" | "RAL" | "RAR" A_NON = A_NON | "RET" | "RC" | "RNC" | "RZ" | "RNZ" | "RPE" | "RPO" | "RP" | "RM" A_NON = A_NON | "XCHG" | "XTHL" | "SPHL" | "PCHL" A_NON = A_NON | "CMA" | "STC" | "CMC" | "DAA" | "EI" | "DI" | "NOP" A_ADR = "JMP" | "JC" | "JNC" | "JZ" | "JNZ" | "JPE" | "JPO" | "JP" | "JM" A_ADR = A_ADR | "CALL" | "CC" | "CNC" | "CZ" | "CNZ" | "CPE" | "CPO" | "CP" | "CM" A_ADR = A_ADR | "LDA" | "STA" | "LHLD" | "SHLD" * P_INST1 = A_BDHS | A_BD P_INST2 = A_RM_RM | A_RM_IMM | A_RM | A_IMM | A_VEC | A_NON | A_BDHA | A_ADR | A_BDHS_I | A_PORT *-------------------------------------------- OBJHEX = ARRAY(3) PASS = 1 FILE_START INPUT_DEV = EQ(PASS ,1) "INPUT_PS1" INPUT_DEV = EQ(PASS ,2) "INPUT_PS2" PC = 0 LN = 1 LINE_START MSG_ERR = ""; ERSW = 0 OPLEN = 0 LINESRC = $INPUT_DEV :F(FILE_END) LINE = TOUPPER(LINESRC) LINE (";" REM) = LINE PT_LABEL = :F(LINE_NX) ADDLTBL(LABEL,PC) :F(ERR_LABEL) LINE_NX LINE ( " END" ) = :S(OUT_OBJ) LINE = TRIM(LINE) EQ(SIZE(LINE),0) :S(OUT_OBJ) * LINE (P_PSEUDO . PSEUDO (NULL | " ")) = :S($PSEUDO) LINE (P_INST1 . INST (NULL | " ")) = :S(INST_OP) LINE (P_INST2 . INST (NULL | " ")) = :S(INST_OP) :(ERR_INST) INST_OP OPC = OPCODE<INST> OPC ("+" ANY("0123")) . OPLEN = INST (A_BD ) :S(X_BD) INST (A_BDHS ) :S(X_BDHS) INST (A_RM_RM ) :S(X_RM_RM) INST (A_RM_IMM ) :S(X_RM_IMM) INST (A_RM ) :S(X_RM) INST (A_IMM ) :S(X_IMM) INST (A_PORT ) :S(X_PORT) INST (A_NON ) :S(X_NON) INST (A_ADR ) :S(X_ADR) INST (A_VEC ) :S(X_VEC) INST (A_BDHA ) :S(X_BDHA) INST (A_BDHS_I ) :S(X_BDHS_I) :(ERR_INST) * ERR_REG MSG_ERR = " ; Error! Registor " :(ERROR_LST) ERR_INST MSG_ERR = " ; Error! Instruction " :(ERROR_LST) ERR_ADR MSG_ERR = " ; Error! Address " :(ERROR_LST) ERR_IMM MSG_ERR = " ; Error! Immediate " :(ERROR_LST) ERR_PORT MSG_ERR = " ; Error! IO Port " :(ERROR_LST) ERR_VEC MSG_ERR = " ; Error! Vec (0..7) " :(ERROR_LST) ERR_LABEL MSG_ERR = " ; Error! Duplication LABEL " :(ERROR_LST) * ERROR_LST OPLEN = 0; ERSW = 1 * OUT_OBJ EQ(PASS,2) :F(OUT_NX) ADR = CVBIN2HEX(PC,4) OBJ = EQ(OPLEN ,0) " " " " OBJ = GE(OPLEN ,1) ADR " " OBJ = GE(OPLEN ,1) OBJ OBJHEX<1> OBJ = GE(OPLEN ,2) OBJ OBJHEX<2> OBJ = GE(OPLEN ,3) OBJ OBJHEX<3> OBJ = OBJ DUPL(" ",14 - SIZE(OBJ)) OUTPUT_LST = OBJ LINESRC MSG_ERR EQ(ERSW,0) :S(OUT_NX) ERR = LPAD(LN ,5 ," ") " " LINESRC MSG_ERR OUTPUT = ERR OUT_NX PC = PC + OPLEN LN = LN + 1 :(LINE_START) FILE_END PASS = LT(PASS,2) PASS + 1 :S(FILE_START) PASS_END OUTPUT = "i8080 Assembler END.. " ISRC " ---> " OLST :(END) *-------------------------------------------------(ORG xxxx) ORG PC = GETIMM(LINE) :F(ERR_ADR) :(OUT_OBJ) *-------------------------------------------------(DB IMM8) DB IMM = GETIMM(LINE) :F(ERR_IMM) GT(IMM,255) :S(ERR_IMM) OPLEN = 1 OBJHEX<1> = CVBIN2HEX(IMM,2) :(OUT_OBJ) *-------------------------------------------------(DW IMM16) DW AD = GETIMM(LINE) :F(ERR_IMM) OPLEN = 2 OBJHEX<1> = CVBIN2HEX(AD % 256 ,2) OBJHEX<2> = CVBIN2HEX(AD / 256 ,2) :(OUT_OBJ) *-------------------------------------------------(ORG xxxx) DS IMM = GETIMM(LINE) :F(ERR_IMM) OPLEN = IMM OBJHEX<1> = "??" OBJHEX<2> = "??" OBJHEX<3> = "??" :(OUT_OBJ) *-------------------------------------------------(EQU xxxx) EQU LINE (SPAN(LABEL_X) . DEFLAB " ") = :F(ERR_LABEL) IMM = GETIMM(LINE) :F(ERR_IMM) ADDLTBL(DEFLAB,IMM) :F(ERR_LABEL) :(OUT_OBJ) * *-------------------------------------------------(MOV RM,RM) * MOV X_RM_RM REGNM = "ABCDEHL" MOVRR = ANY(REGNM) . REGD "," ANY(REGNM) . REGS MOVRM = ANY(REGNM) . REGD "," "M" . REGS MOVMR = "M" . REGD "," ANY(REGNM) . REGS LINE (MOVRR | MOVRM | MOVMR) = :F(ERR_REG) OPC "D" = REPLACE(REGD ,"BCDEHLMA" ,"01234567") OPC "S" = REPLACE(REGS ,"BCDEHLMA" ,"01234567") OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(MVI RM,IMD8) * MVI X_RM_IMM LINE (ANY("ABCDEHLM") . REGD "," ) = :F(ERR_REG) OPC "D" = REPLACE(REGD ,"BCDEHLMA" ,"01234567") IMM = GETIMM(LINE) :F(ERR_IMM) GT(IMM,255) :S(ERR_IMM) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(IMM,2) :(OUT_OBJ) *-------------------------------------------------(xxx RM) * INR/DCR * ADD/ADC/SUB/SBB/ANA/XRA/ORA/CMP X_RM LINE (ANY("ABCDEHLM") . REG) = :F(ERR_REG) OPC "R" = REPLACE(REGD ,"BCDEHLMA" ,"01234567") OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(xxx IMM8) * ADI/ACI/SUI/SBI/ANI/XRI/ORI/CPI X_IMM IMM = GETIMM(LINE) :F(ERR_IMM) GT(IMM,255) :S(ERR_IMM) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(IMM,2) :(OUT_OBJ) *-------------------------------------------------(xxx PORT) * IN/OUT X_PORT PORT = GETIMM(LINE) :F(ERR_IMM) GT(PORT,255) :S(ERR_PORT) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(PORT,2) :(OUT_OBJ) *-------------------------------------------------(xxx) * HLT/RLC/RRC/RAL/RAR * RET/RC /RNC/RZ /RNZ/RP /RM /RPE/RPO * XCHG/XTHL/SPHL/PCHL * CMA/STC/CMC/DAA/EI /DI /NOP X_NON OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(xxx ADR) * JMP /JC /JNC/JZ /JNZ/JP /JM /JPE/JPO * CALL/CC /CNC/CZ /CNZ/CP /CM /CPE/CPO * LDA /STA/LHLD/SHLD X_ADR ADR = GETIMM(LINE) :F(ERR_ADR) OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(ADR % 256 ,2) OBJHEX<3> = CVBIN2HEX(ADR / 256 ,2) :(OUT_OBJ) *-------------------------------------------------(RST Vect) * RST X_VEC VEC = GETIMM(LINE) :F(ERR_IMM) GT(VEC,7) :S(ERR_VEC) OPC "V" = VEC OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(xxx BDHA) * PUSH/POP X_BDHA LINE (ANY("BDH") | "PSW") . REG = :F(ERR_REG) REG "PSW" = "A" OPC "R" = REPLACE(REG ,"BDHA" ,"0246") OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(xxx BD) * LDAX/STAX X_BD LINE (ANY("BD") . REG) = :F(ERR_REG) REG = REPLACE(REG ,"BD" ,"02") OPC "P" = REG OPC "Q" = REG + 1 OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(xxx BDHS) * DAD/INX/DCX X_BDHS LINE (ANY("BDH") | "SP") . REG = :F(ERR_REG) REG "SP" = "S" REG = REPLACE(REG ,"BDHS" ,"0246") OPC "P" = REG OPC "Q" = REG + 1 OBJHEX<1> = CVOCT2HEX(OPC) :(OUT_OBJ) *-------------------------------------------------(xxx BDHS,IMM16) * LXI X_BDHS_I LINE (ANY("BDH") | "SP") . REG "," = :F(ERR_REG) REG "SP" = "S" ADR = GETIMM(LINE) :F(ERR_ADR) OPC "P" = REPLACE(REG ,"BDHS" ,"0246") OBJHEX<1> = CVOCT2HEX(OPC) OBJHEX<2> = CVBIN2HEX(ADR % 256 ,2) OBJHEX<3> = CVBIN2HEX(ADR / 256 ,2) :(OUT_OBJ) END
ピンバック: DEC社 TOPS-20のSNOBOL言語で8080アセンブラを作成(テスト編) | ある計算機屋さんの手帳