「10日くらいでできる!プログラミング言語自作入門」の続編#1-7

(1) はじめに

(2) HL-17

#include <acl.c>

typedef unsigned char *String;	// こう書くと String は unsigned char * の代用になる.

int loadText(String path, String t, int siz) → HL-4と同じなので省略

///////////////////////////////////////////////////////////////////////////////

#define MAX_TC  1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当たり平均10バイトを想定.
int tcs = 0, tcb = 0;

AInt var[MAX_TC + 1];	// 変数.

int getTc(String s, int len) → HL-8aと同じなので省略

///////////////////////////////////////////////////////////////////////////////

int isAlphabetOrNumber(unsigned char c) → HL-2と同じなので省略

int lexer(String s, int tc[]) → HL-9aと同じなので省略

int tc[10000];	// トークンコード.

enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, Tc4, Tc5, Tc6, Tc7, Tc8, TcBrOpn, TcBrCls, TcSqBrOpn, TcSqBrCls, TcCrBrOpn, TcCrBrCls,
    TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinus, TcAster, TcSlash, TcPerce, TcAnd, TcShr, TcPlPlus, TcEqu,
    TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, TcTmp3, TcTmp4, TcTmp5, TcTmp6, TcTmp7, TcTmp8, TcTmp9,
    TcSubPrint, TcSubTime, TcSubRgb8, TcSubXorShift, TcSubGetPix, TcSubF16Sin, TcSubF16Cos, TcSubInkey, TcSubPrints, TcSubSetPix0,
    TcSubFilRct0, TcSubDrwStr0, TcSubGprDec, TcSubOpnWin, TcSubWait, TcSubBitBlt, TcSubAryNew, TcSubAryInit };

char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } == != < >= <= > + - * / % & >> ++ = , !!** !!*** _t0 _t1 _t2 _t3 _t4 _t5 _t6 _t7 _t8 _t9 "
    "sub_print sub_time sub_aRgb8 sub_xorShift sub_aGetPix sub_f16Sin sub_f16Cos sub_aInkey sub_prints sub_aSetPix0 "
    "sub_aFilRct0 sub_aDrwStr0 sub_gprDec sub_opnWin sub_aWait sub_bitblt sub_aryNew sub_aryInit";

///////////////////////////////////////////////////////////////////////////////

int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:一致したフレーズの次のトークンをさす, wpc[]:ワイルドカードのトークンの場所をさす.

int phrCmp(int pid, String phr, int pc) → HL-7と同じなので省略

/////////////////////////////////////////////////////////////////////////////// → ここまではHL-9aと全く同じ、ここから改造が始まる.

#define ABI_MSW64   1 // Windows.
#define ABI_SYSV64  0 // Linuxなど.

typedef AInt *IntP; // こう書くと IntP は AInt * の代わりに使えるようになる.

enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpCgt, OpAdd, OpSub, OpMul, OpDiv, OpMod, OpAnd, OpShr, 
    OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, OpJle, OpJgt, OpLop, OpPrint, OpTime, OpEnd,
    OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, OpOpnWin, OpSetPix0, OpM64s, OpRgb8, OpWait,
    OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, OpF16Cos, OpInkey, OpDrwStr0, OpGprDec, OpBitBlt };

unsigned char *ic, *icq; // ic[]:内部コード、icq:ic[]への書き込み用ポインタ.

void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3)  // 移行中の間だけ、以下の形で残しておく.
{
    printf("putIc: error\n");
    exit(1);
}

int getHex(int c) // 16進数に使える文字ならそれを0~15の数に変換、そうでなければ-1を返す関数.
{
    if ('0' <= c && c <= '9') return c - '0';
    if ('a' <= c && c <= 'f') return c - 'a' + 10;
    if ('A' <= c && c <= 'F') return c - 'A' + 10;
    return -1;
}

int get32(unsigned char *p)
{
    return p[0] + p[1] * 256 + p[2] * 65536 + p[3] * 16777216;
}

void put32(unsigned char *p, AInt i)
{
    p[0] =  i        & 0xff; // 1バイト目に、ビット0~7の内容を書き込む.
    p[1] = (i >>  8) & 0xff; // 2バイト目に、ビット8~15の内容を書き込む.
    p[2] = (i >> 16) & 0xff; // 3バイト目に、ビット16~23の内容を書き込む.
    p[3] = (i >> 24) & 0xff; // 4バイト目に、ビット24~31の内容を書き込む.
}

unsigned char *putIcX64_rex;

void putIcX64_sub(String s, IntP a[])
{
    int i, j, k;
    for (i = 0; s[i] != 0; ) {
        if (s[i] == ' ' || s[i] == '\t' || s[i] == '_' || s[i] == ':' || s[i] == ';') {
            i++;
        } else if (getHex(s[i]) >= 0 && getHex(s[i + 1]) >= 0) { // 16進数2桁.
            *icq = getHex(s[i]) * 16 + getHex(s[i + 1]);
            i += 2;
            icq++;
        } else if (s[i] == '%') {
            if (s[i + 1] == 'R') {
                putIcX64_rex = icq;
                *icq = 0x48;
                icq++;
                i += 2;
                continue;
            }
            j = s[i + 1] - '0';
            if (s[i + 2] == 'm') {	// mod r/m.
                k = s[i + 3] - '0';
                if (s[i + 3] >= 'a') { // もしかしたらこの記述は最後まで出番がないかも.
                    k = s[i + 3] - 'a' + 10;
                }
                *icq = 0x85 + (k & 7) * 8;
                if (k >= 8) {
                    *putIcX64_rex |= 4;
                }
                put32(icq + 1, (a[j] - var) * 8);
                icq += 5;
                i += 4;
                continue;
            }
            if (s[i + 2] == 'i') {	// int.
                put32(icq, (AInt) a[j]);
                icq += 4;
            }
            if (s[i + 2] == 'c') {	// char.
                *icq = (AInt) a[j];
                icq++;
            }
            if (s[i + 2] == 'q') {	// long long (qword).
                put32(icq,      (AInt) a[j]);
                put32(icq + 4, ((AInt) a[j]) >> 32);
                icq += 8;
            }
            i += 3;
        } else {
            printf("putIcX64: error: '%s'\n", s);
            exit(1);
        }
    }
}

void putIcX64(String s, IntP p0, IntP p1, IntP p2, IntP p3)  // ic[]へ簡単に書き込むための便利関数.
{
    IntP a[4];
    a[0] = p0;
    a[1] = p1;
    a[2] = p2;
    a[3] = p3;
    putIcX64_sub(s, a);
}

///////////////////////////////////////////////////////////////////////////////

char tmp_flag[10]; // 一時変数の利用状況を管理.

int tmpAlloc() → HL-7と同じなので省略

void tmpFree(int i) → HL-7と同じなので省略

///////////////////////////////////////////////////////////////////////////////

void sub_print(AInt i)
{
    printf("%d\n", (int) i);
}

void initTcSub()
{
    var[TcSubPrint] = (AInt) sub_print;
}

///////////////////////////////////////////////////////////////////////////////

int epc, epc1;	// exprのためのpcとpc1.

int exprSub(int priority);	// exprSub1()が参照するので、プロトタイプ宣言.
int expr(int j);
int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err);

int exprSub1(int i, int priority, int op) → HL-7と同じなので省略

int exprSub(int priority) → HL-9と同じなので省略

int expr(int j) → HL-7と同じなので省略

///////////////////////////////////////////////////////////////////////////////

enum { IfTrue = 0, IfFalse = 1 };

void ifgoto(int i, int not, int label) → HL-8と同じなので省略

int tmpLabelNo;

int tmpLabelAlloc() → HL-8と同じなので省略

#define BInfSiz		10

int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd:block-depth, lbd:loop-block-depth

enum { BlkIf = 1, BlkFor, BlkMain }; // BlkMainをHL-9aで追加.
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01, ForWpc11, ForWpc02, ForWpc12 };

int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err)  // 移行中の間だけ、以下の形で残しておく.
{
    if (phrCmp(pid, phr, pc)) {
        printf("phrCmpPutIc: error\n");
        exit(1);
    }
    return 0;
}

int phrCmpPutIcX64(int pid, String phr, int pc, int *pi, int lenExpr, int sub, int *err)
{
    int e[9], i;
    if (phrCmp(pid, phr, pc)) {
        e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] = e[7] = e[8] = 0;
        for (i = 0; i < lenExpr; i++) {
            e[i] = expr(i);
        }
        #if (ABI_MSW64 != 0)
            if (lenExpr >=  1) { putIcX64("%R_8b_%0m1;", &var[e[0]], 0, 0, 0); } // RCX=arg[0].
            if (lenExpr >=  2) { putIcX64("%R_8b_%0m2;", &var[e[1]], 0, 0, 0); } // RDX=arg[1].
            if (lenExpr >=  3) { putIcX64("%R_8b_%0m8;", &var[e[2]], 0, 0, 0); } // R08=arg[2].
            if (lenExpr >=  4) { putIcX64("%R_8b_%0m9;", &var[e[3]], 0, 0, 0); } // R09=arg[3].
            for (i = 4; i < lenExpr; i++) {
                putIcX64("%R_8b_%0m0; %R_89_44_24_%1c;", &var[e[i]], (IntP) ((AInt) i * 8), 0, 0);
            }
        #elif (ABI_SYSV64 != 0)
            if (lenExpr >=  1) { putIcX64("%R_8b_%0m7;", &var[e[0]], 0, 0, 0); } // RDI=arg[0].
            if (lenExpr >=  2) { putIcX64("%R_8b_%0m6;", &var[e[1]], 0, 0, 0); } // RDX=arg[1].
            if (lenExpr >=  3) { putIcX64("%R_8b_%0m2;", &var[e[2]], 0, 0, 0); } // R08=arg[2].
            if (lenExpr >=  4) { putIcX64("%R_8b_%0m1;", &var[e[3]], 0, 0, 0); } // R09=arg[3].
            if (lenExpr >=  5) { putIcX64("%R_8b_%0m8;", &var[e[4]], 0, 0, 0); } // R08=arg[4].
            if (lenExpr >=  6) { putIcX64("%R_8b_%0m9;", &var[e[5]], 0, 0, 0); } // R09=arg[5].
            for (i = 6; i < lenExpr; i++) {
                putIcX64("%R_8b_%0m0; %R_89_44_24_%1c;", &var[e[i]], (IntP) ((AInt) (i - 6) * 8), 0, 0);
            }
        #endif
        putIcX64("%R_ff_%0m2;", &var[sub], 0, 0, 0); // 間接call.
        for (i = 0; i < lenExpr; i++) {
            if (e[i] < 0) {
                *err = -1;
            }
            tmpFree(e[i]);
        }
        if (pi != 0) {
            *pi = tmpAlloc();
            putIcX64("%R_89_%0m0;", &var[*pi], 0, 0, 0); // RAXの値を書き込む.
        }
        return 1;
    }
    return 0;
}

///////////////////////////////////////////////////////////////////////////////

int compile(String s)
{
    int pc, pc1, i, j;
!   unsigned char *icq1, *icp;
    pc1 = lexer(s, tc);
    tc[pc1++] = TcSemi;	// 末尾に「;」を付け忘れることが多いので、付けてあげる.
    tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = TcDot;	// エラー表示用のために末尾にピリオドを登録しておく.
    icq = ic;
+   putIcX64("41_57; 41_56; 41_55; 41_54; 41_53; 41_52;", 0, 0, 0, 0);
+   putIcX64("41_51; 41_50; 57; 56; 55; 54; 53; 52; 51; 50;", 0, 0, 0, 0);
+   putIcX64("%R_81_ec_f8_01_00_00; %R_bd_%0q;", var, 0, 0, 0); // RBP = var.
    for (i = 0; i < 10; i++) {
        tmp_flag[i] = 0;
    }
    tmpLabelNo = 0;
    bd = lbd = 0;
    for (pc = 0; pc < pc1; ) { // コンパイル開始.
        int e0 = 0, e2 = 0;
        if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
!           putIcX86("8b_%1m0; 89_%0m0;", &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0, 0);
        } else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2 < !!*3) goto !!*4;", pc) && tc[wpc[0]] == tc[wpc[1]] && tc[wpc[0]] == tc[wpc[2]]) {
        (中略)
!       } else if (phrCmpPutIcX86(4, "print !!**0;", pc, 0, 1, sub_print, &e0)) {
        (中略)
    }
    if (bd > 0) {
        printf("block nesting error (bd=%d, lbd=%d, pc=%d, pc1=%d\n", bd, lbd, pc, pc1);
        return -1;
    }
+   putIcX64("%R_81_c4_f8_01_00_00;", 0, 0, 0, 0);
+   putIcX64("58; 59; 5a; 5b; 5c; 5d; 5e; 5f; 41_58; 41_59;", 0, 0, 0, 0);
!   putIcX64("41_5a; 41_5b; 41_5c; 41_5d; 41_5e; 41_5f; c3;", 0, 0, 0, 0);
    icq1 = icq;
    // ここにあった「goto先の設定」はいったん全部削除.
    return icq1 - ic;
err:
    printf("syntax error : %s %s %s %s\n", ts[tc[pc]], ts[tc[pc + 1]], ts[tc[pc + 2]], ts[tc[pc + 3]]);
    return -1;
}

// このあたりにあったexec()関数の記述はすべて削除.
// ついでに変数winの宣言も削除.

int run(String s)
{
    if (compile(s) < 0)
        return 1;
+   void (*func)() = (void (*)()) ic;
!   func();
    return 0;
}

// 以下のmallocRWX()はWindows用の記述. Linux系のOSの場合は本文中で説明します.

#include <windows.h>

void *mallocRWX(int siz)
{
    return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}

///////////////////////////////////////////////////////////////////////////////

void aMain()
{
    unsigned char txt[10000];
    int i;
+   ic = mallocRWX(1024 * 1024); // 1MB
    lexer(tcInit, tc);
+   initTcSub();
    if (aArgc >= 2) {
    (中略)
}

(3) HL-17に関する重要な説明

(4) 今回の改造のあらまし

(5) 生成している機械語の説明(putIcX64()やphrCmpPutIcX64()の説明も含む)





(6) 機械語を調べるためのリンク集

次回に続く

こめんと欄


コメントお名前NameLink

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS