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

  • (by K, 2021.05.27)

(1) HL-16b

  • HL-16aまでで、JITコンパイラの作り方の説明は一通りできたと思っています。だから次は、JITではない普通のコンパイラの作り方をやりたいと思います。
  • 普通のコンパイラなら、JITコンパイルした機械語を出力すればいいわけなのですが、世間的にコンパイラとして期待されるのは、機械語の出力ではなくてアセンブラ用のソースコードを出力することのようです。ただ機械語を出すだけでよければ、「codedump 1」でほぼできたようなものだったのですが、しょうがないのでもうちょっと作りこむことにします。

  • さてアセンブラを出力するといっても、アセンブラごとに多少の方言があるので、どれにするか決めなければいけません。今回は私の趣味で、 NASM 用の形式で出力させることにしました。ということで、 NASM のインストール方法を紹介します。
    • https://www.nasm.us/ まずここへいきます(ちなみに私は、 http://nasm.sourceforge.net/ のアドレスしか知らなかったのですが、自動で転送してもらえました)。
    • 私は、画面上部のDOWNLOADを選んで、 https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/win32/ まで進んで、zip版をダウンロードしました(Windows用の32bit版が使いたかったので)。
    • (それぞれ自分の環境に合わせてダウンロードしてください。)
    • zipの中にはnasm.exeがあるので、それを適当なところにコピーすれば完了です。

  • 次に考えたのは開発方針です。普通に考えれば、putIcX86()を改造してputIcNasm86()を作るのがよさそうなのですが、それは今から作るにはちょっと面倒そうです。それで、まあ、すごく変わっているのですが、putIcX86()はそのままにして、生成した機械語を逆アセンブラでアセンブラソースコードに変換するという方法で、目的を達成しようと思います。
  • 普通のコンパイラの世界では、コンパイラがアセンブラのソースコード生成してそれをアセンブルして機械語に変換するものですが、HL-16bでは、機械語を生成してからアセンブラのソースコードを作り直すというわけです。
    • まあ結局、こんな変わった作り方をする人は少ないと思うので、これはやってみたら面白いかも?って思ったから、これにしただけです。たぶん素直にputIcNasm86()を作って全体を再構成するほうが総行数は少なくなるはずです。
    • この変な作り方のおかげで、HL-16bは普通のコンパイラとして使えますが、HL-16aまでのJITコンパイラとしても使えるようになっています。

  • ということで、「codedump 2」にしたらアセンブラ用のソースコードが出力されます。もしくは、
    prompt>hl16b mandel.c -asm
    とするだけでいきなりソースコードが出てくるようになるので、すごく普通のコンパイラっぽくなります。

  • [1]関数compile()を以下のように改造
    int aryInitList[100], ailp; // この行追加.
    char varOpt[MAX_TC + 1]; // この行追加.
    
    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 = icq0 = ic;
    !   jp = ailp = 0;
        icq1 = icqSet = 0;
        putIcX86("60; 83_ec_7c;", 0, 0, 0, 0); // PUSHAD(); SUB(ESP,124);
        regVarSaveLoad(RvLoad);
        dump0 = icq;
        for (i = 0; i < 10; i++) {
            tmp_flag[i] = 0;
        }
        tmpLabelNo = 0;
        for (i = 0; i < MAX_TC + 1; i++) {
            vc[i] = 0;
    !       varOpt[i] = 0;
        }
        bd = lbd = 0;
        toExit = tmpLabelAlloc();
        for (pc = 0; pc < pc1; ) { // コンパイル開始.
            (中略)
            } else if (phrCmp( 0, "!!*0:", pc)) {	// ラベル定義命令.
                defLabel(tc[wpc[0]]); // ラベルに対応するicqを記録しておく.
    +           varOpt[tc[wpc[0]]] |= 1;
            } else if (phrCmp( 5, "goto !!*0;", pc)) { // goto.
            (中略)
            } else if (phrCmp(22, "int !!*0[!!**2] = {", pc)) {
                e2 = expr(2);
                putIcX86("8b_%1m0; 89_44_24_00; e8_%2r; 89_%0m0;", &var[tc[wpc[0]]], &var[e2], (IntP) sub_aryNew, 0);
                j = 0;
                for (i = ppc1; i < pc1; i++) {	// コンマ以外のトークンを数える.
                    if (tc[i] == TcCrBrCls) break;
                        if (tc[i] != TcComma) {
                            j++;
                        }
                    }
                    if (i >= pc1) goto err;
                    AInt *ip = malloc(j * sizeof (AInt));
                    j = 0;
                    for (i = ppc1; tc[i] != TcCrBrCls; i++) {
                        if (tc[i] == TcCrBrCls) break;
                        if (tc[i] != TcComma) {
                        ip[j] = var[tc[i]];
                        j++;
                    }
                }
    +           aryInitList[ailp] = icq - ic; // sub_aryInitの呼び出し位置を記憶しておく.
    +           ailp++;
    !           putIcX86("89_44_24_00; b8_%0i; 89_44_24_04; b8_%1i; 89_44_24_08; e8_%2r;", (IntP) ip, (IntP) j, (IntP) sub_aryInit, 0);
                ppc1 = i + 2; // } と ; の分.
            } else if (phrCmpPutIcX86(23, "aSetPix0(!!***8, !!**0, !!**1, !!**2);",                       pc, 0, 3, sub_aSetPix0, &e0)) {
            (中略)
        }
        (中略)
        for (i = 0; i < jp; i++) { // ジャンプ命令の最適化.
            (中略)
        }
    +   if (codedump != 2) {
            for (i = 0; i < jp; i++) { // ジャンプ先の設定(相対値にする).
                icq = jmps[i] + ic;
                j = *(IntP) get32(icq); // ラベル番号を取得.
                icp = j + ic; // icp=飛び先の命令列の先頭.
                put32(icq, icp - (icq + 4));
            }
    +   }
        return icq1 - ic;
        (中略)
    }
  • [2]関数run()の直前に以下の関数群を追加
    String regNamX86[8] = { "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI" };
    
    int modRmX86(unsigned char *p, char *s)
    {
        int c0 = *p;
        if ((c0 & 0xc0) == 0xc0) {
            strcpy(s, regNamX86[c0 & 7]);
            return 1;
        }
        if ((c0 & 0xc7) == 0x05) {
            sprintf(s, "DWORD [.%s]", ts[(IntP) get32(p + 1) - var]);
            return 5;
        }
        if ((c0 & 0xc7) == 0x04 && p[1] == 0x8a) {
            sprintf(s, "DWORD [EDX+ECX*4]");
            return 2;
        }
        if ((c0 & 0xc7) == 0x44 && p[1] == 0x24) {
            sprintf(s, "DWORD [ESP+%d]", p[2]);
            return 3;
        }
        printf("modRmX86: error: %02x\n", c0);
        exit(1);
    }
    
    String asmX86_sub1(int q, String p0, String s, String p1, String t)
    // (q) 1:modRmの直前の1バイトの下位3bitがレジスタを指定, 2:modRm内の中間3bitがレジスタを指定, 3:modRmを解釈した結果.
    //     4:8bit即値, 5:32bit即値, 6:CL, 7:sub関数呼び出し, 8:JMP/Jccの分岐先ラベル.
    {
        if (q == 1) { return regNamX86[p0[-1] & 7]; }
        if (q == 2) { return regNamX86[(p0[0] >> 3) & 7]; }
        if (q == 3) { return s; }
        if (q == 4) { sprintf(t, "%d", *((signed char *) p1)); return t; }
        if (q == 6) { return "CL"; }
        if (q == 8) { sprintf(t, ".%s", ts[((IntP) get32(p1)) - var]); return t; }
        if (q == 5) {
            int i = get32(p1), j;
            sprintf(t, "%d", i);
            for (j = 0; j < tcs; j++) {
                if (var[j] == i && ts[j][0] == 34) {
                    sprintf(t, ".str%d", i);
                    varOpt[j] |= 2;
                }
            }
            return t;
        }
        if (q == 7) {
            void *sub = p1 + 4 + get32(p1);
            if (sub == sub_print)    { return "_sub_print"; }
            if (sub == sub_time)     { return "_sub_time"; }
            if (sub == sub_aRgb8)    { return "_sub_aRgb8"; }
            if (sub == sub_XorShift) { return "_sub_XorShift"; }
            if (sub == sub_aGetPix)  { return "_sub_aGetPix"; }
            if (sub == sub_f16Sin)   { return "_sub_f16Sin"; }
            if (sub == sub_f16Cos)   { return "_sub_f16Cos"; }
            if (sub == sub_aInkey)   { return "_sub_aInkey"; }
            if (sub == sub_prints)   { return "_sub_prints"; }
            if (sub == sub_aSetPix0) { return "_sub_aSetPix0"; }
            if (sub == sub_aFilRct0) { return "_sub_aFilRct0"; }
            if (sub == sub_aDrwStr0) { return "_sub_aDrwStr0"; }
            if (sub == sub_gprDec)   { return "_sub_gprDec"; }
            if (sub == sub_OpnWin)   { return "_sub_OpnWin"; }
            if (sub == sub_aWait)    { return "_sub_aWait"; }
            if (sub == sub_bitblt)   { return "_sub_bitblt"; }
            if (sub == sub_aryNew)   { return "_sub_aryNew"; }
            if (sub == sub_aryInit)  { return "_sub_aryInit"; }
        }
        return "?";
    }
    
    int asmX86_sub0(unsigned char *p, String op)
    {
        char s[100], t[100];
        int len = 0, q0 = op[1] - '0', q1 = op[2] - '0', q2 = op[3] - '0';
        p++;
        String p0 = p;
        if (op[0] == 'm') {
            len = modRmX86(p, s);
            p += len;
        }
        printf("        %-8s", op + 4);
        if (q0 > 0) { printf( "%s", asmX86_sub1(q0, p0, s, p, t)); len += "000014044"[q0] - '0'; }
        if (q1 > 0) { printf(",%s", asmX86_sub1(q1, p0, s, p, t)); len += "000014044"[q1] - '0'; }
        if (q2 > 0) { printf(",%s", asmX86_sub1(q2, p0, s, p, t)); len += "000014044"[q2] - '0'; }
        printf("\n");
        return len + 1;
    }
    
    char asmX86_jcc[16][4] = { "O", "NO", "B", "AE", "E", "NE", "BE", "A", "S", "NS", "P", "NP", "L", "GE", "LE", "G" };
    
    char asmX86_table0[][16] = {
        "0039m230ADD",    "02b9m230SUB",    "0319m320XOR",    "03b9m230CMP",
        "0609_000PUSHAD", "0619_000POPAD",  "0699m235IMUL",   "06b9m234IMUL",
        "0810m350ADD",    "0814m350AND",    "0815m350SUB",    "0816m350XOR",    "0817m350CMP",
        "0830m340ADD",    "0834m340AND",    "0835m340SUB",    "0836m340XOR",    "0837m340CMP",
        "0859m320TEST",   "0899m320MOV",    "08b9m230MOV",    "0999_000CDQ",
        "0c39_000RET",    "0c79m350MOV",    "0e89_700CALL",
        "0c14m340SHL",    "0c15m340SHR",    "0c17m340SAR",	  "0d34m360SHL",    "0d35m360SHR",    "0d37m360SAR",
        "0f73m300NEG",    "0f75m300IMUL",   "0f77m300IDIV",	
        "1ac9m324SHRD",   "1ad9m326SHRD",   "1af9m230IMUL",   "x"
    };
    
    void asmX86()
    {
        int i, j, k = 0;
        if (dump0 == dump1) return;
        printf("BITS 32\n");
        printf("SECTION .text\n");
        printf("GLOBAL _aMain\n");
        printf("        ALIGNB  16\n");
        printf("_aMain:\n");
        for (i = 0; i < jp; i++) {
            icq = jmps[i] + ic;
            j = ((IntP) get32(icq)) - var;
            varOpt[j] |= 1; // 利用したラベルをマークする.
        }
        for (i = 0;;) {
            int c0 = ic[i], c1;
            String op = 0;
            char t[16];
            for (j = 0; j < tcs; j++) {
                if (var[j] == (AInt) i && (varOpt[j] & 1) != 0) {
                    printf(".%s:\n", ts[j]); // ラベル定義出力.
                }
            }
            if (c0 == 0x0f) {
                i++;
                c0 = ic[i] + 0x100;
            }
            if (k < ailp && aryInitList[k] + 4 == i) {
                printf("        MOV     EAX,.ary%d\n", get32(&ic[i + 1]));
                i += 5;
                k++;
                continue;
            }
            c1 = c0 & ~0xf;
            if (c1 == 0x180) {
                sprintf(t, "_800J%s", asmX86_jcc[c0 & 0xf]);
                op = t;
                goto skip;
            }
            if (c1 == 0x190) {
                printf("        SET%-5sAL\n", asmX86_jcc[c0 & 0xf]);
                printf("        MOVZX   EAX,AL\n");
                i += 5;
                continue;
            }
            c1 = c0 & ~0x7;
            if (c1 == 0x040) { op = "_100INC"; goto skip; }
            if (c1 == 0x048) { op = "_100DEC"; goto skip; }
            if (c1 == 0x0b8) { op = "_150MOV"; goto skip; }
            c1 = (ic[i + 1] >> 3) & 7;
            for (j = 0; asmX86_table0[j][0] != 'x'; j++) {
                op = &asmX86_table0[j][4];
                if (c0 != getHex(op[-4]) * 256 + getHex(op[-3]) * 16 + getHex(op[-2])) continue;
                if (op[-1] != '9' && c1 != op[-1] - '0') continue;
                goto skip;
            }
            printf("asmX86: error: %02x-%02x\n", c0, c1);
            exit(1);
    skip:
            i += asmX86_sub0(&ic[i], op);
            if (c0 == 0x0c3) break;
        }
        printf("EXTERN _sub_print,   _sub_time,     _sub_aRgb8,    _sub_XorShift\n");
        printf("EXTERN _sub_aGetPix, _sub_f16Sin,   _sub_f16Cos,   _sub_aInkey\n");
        printf("EXTERN _sub_prints,  _sub_aSetPix0, _sub_aFilRct0, _sub_aDrwStr0\n");
        printf("EXTERN _sub_gprDec,  _sub_OpnWin,   _sub_aWait,    _sub_bitblt\n");
        printf("EXTERN _sub_aryNew,  _sub_aryInit\n");
        printf("SECTION .data\n");
        printf("        ALIGNB  16\n");
        for (j = 0; j < tcs; j++) { // 普通の変数の出力.
            if (vc[j] > 0) {
                printf(".%-6s DD      %d\n", ts[j], var[j]);
            }
        }
        for (k = 0; k < ailp; k++) { // 配列の初期値の出力.
            int j1 = get32(&ic[aryInitList[k] + 13]), *ip = (IntP) get32(&ic[aryInitList[k] + 5]);
            printf(".ary%d:\n", (int) ip);
            for (j = 0; j < j1; j++) {
                printf("        DD      %d\n", ip[j]);
            }
        }
        for (j = 0; j < tcs; j++) { // 文字列リテラルの出力.
            if ((varOpt[j] & 2) != 0) {
                printf(".str%d DD %s,0\n", var[j], ts[j]);
            }
        }
    }
  • [3]関数run()を以下のように改造
    int run(String s)
    {
        if (compile(s) < 0)
            return 1;
        if (codedump == 0) {
            void (*func)() = (void (*)()) ic;
            t0 = clock();
            func();
            if (win != 0) {
                aFlushAll(win);
            }
    !   } else if (codedump == 1) {
            int i, i1 = dump1 - dump0;
            for (i = 0; i < i1; i++) {
                printf("%02x ", dump0[i]);
            }
            printf("\n(len=%d)\n", i1);
            for (i = 0; i < MAX_TC + 1; i++) {
                if (vc[i] != 0) {
                    printf("#%04d(%08x): %06d: %s\n", i, (int) &var[i], vc[i], ts[i]);
                }
            }
    +   } else if (codedump == 2) {
    +       asmX86();
        }
        return 0;
    }
  • [4]関数aMain()を以下のように改造
    void aMain()
    {
        (中略)
        if (aArgc >= 2) {
            int wait = 0;
            if (aArgc >= 3) {
                if (strcmp(aArgv[2], "-asm") == 0) { codedump = 2; }
                if (strcmp(aArgv[2], "-wait") == 0) { wait = -1; }
            }
            if (loadText((String) aArgv[1], txt, 10000) == 0) {
                run(txt);
                if (win != 0) { aWait(wait); }
            }
            exit(0);
        }
        (中略)
    }

  • 以上すべての改造を終えると、プログラムは1287行になります。HL-16aが1081行だったので、206行だけ増えたことになります。
  • それでは早速動かしてみます。
    >codedump 2
    
    >run maze.c
    BITS 32
    SECTION .text
    GLOBAL _aMain
            ALIGNB  16
    _aMain:
            PUSHAD
            SUB     ESP,124
            MOV     EAX,752
            MOV     DWORD [ESP+0],EAX
            MOV     EAX,496
            MOV     DWORD [ESP+4],EAX
            MOV     EAX,.str36703696
            MOV     DWORD [ESP+8],EAX
            CALL    _sub_OpnWin
    (中略)
    .d      DD      0
    .dd     DD      0
    .23     DD      23
    .15     DD      15
    .str36703696 DD "maze",0
  • この出力部分(「BITS 32」以降の部分)をコピー&ペーストして、テキストファイルとして保存します。たとえば、「maze_asm.nas」としましょう。
    • もしくは「 prompt>hl16b maze.c -asm > maze_asm.nas 」でもいいです。
  • これを
    prompt>nasm -f win32 -o maze_asm.obj maze_asm.nas
    としてアセンブルします。-fはelf32とかにすることもできます(その場合の拡張子は.oのほうがいいですね)。・・・自分の使っているOSに合わせましょう。
  • さらに実行ファイルを作りたいわけですが、そのためには前もって以下のファイル(sub32.c)をコンパイルしてオブジェクトファイルを作っておく必要があります。
#include <acl.c>

AWindow *win;

void sub_print(int i)                  { printf("%d\n", i); }
void sub_time()                        { printf("time: %.3f[sec]\n", clock() / (double) CLOCKS_PER_SEC); }
int  sub_aRgb8(int r, int g, int b)    { return aRgb8(r, g, b); }
int  sub_XorShift()                    { return aXorShift32(); }
int  sub_aGetPix(int x, int y)         { return aGetPix(win, x, y); }
int  sub_f16Sin(int x)                 { return (int) (sin(x * (2 * 3.14159265358979323 / 65536)) * 65536); }
int  sub_f16Cos(int x)                 { return (int) (cos(x * (2 * 3.14159265358979323 / 65536)) * 65536); }
int  sub_aInkey(int opt)               { return aInkey(win, opt); }
void sub_prints(char *s)               { printf("%s\n", s); }
void sub_aSetPix0(int x, int y, int c) { aSetPix0(win, x, y, c); }
void sub_aFilRct0(int xsz, int ysz, int x0, int y0, int c)  { aFillRect0(win, xsz, ysz, x0, y0, c); }
void sub_aDrwStr0(int x, int y, int c, int b, char *s)      { aDrawStr0(win, x, y, c, b, s); }
void sub_gprDec(int x, int y, int w, int c, int b, int i)   { char s[100]; sprintf(s, "%*d", w, i); aDrawStr0(win, x, y, c, b, s); }

int sub_OpnWin(int xsz, int ysz, char *s)
{
    win = aOpenWin(xsz, ysz, s, 0);
    return 0;
}

int sub_aWait(int msec)
{
    aWait(msec);
    return 0;
}

void sub_bitblt(int xsz, int ysz, int x0, int y0, int *a)
{
    AInt32 *p32 = &win->buf[x0 + y0 * win->xsiz];
    int i, j;
    for (j = 0; j < ysz; j++) {
        for (i = 0; i < xsz; i++) {
            p32[i] = a[i];
        }
        a += xsz;
        p32 += win->xsiz;
    }
}

AInt *sub_aryNew(int n)
{
    int *p = malloc(n * sizeof (AInt));
    memset((char *) p, 0, n * sizeof (AInt));
    return p;
}

void sub_aryInit(AInt *a, AInt *ip, int n)
{
    memcpy((char *) a, (char *) ip, n * sizeof (AInt));
}
  • これを以下の手順でコンパイルして、sub32.objを作っておきます(下記はWindowsでの例)。
    prompt>gcc -m32 -Wno-unused-function -O3 -I (acl-winへのパス) -c -DAARCH_X86 -o sub32.obj sub32.c
  • sub32.objとmaze_asm.objから、maze_asm.exeを生成できます。
    prompt>gcc -Wl,-s -o maze_asm.exe maze_asm.obj sub32.obj -lgdi32
    本当はldコマンドでリンクすべきなのですが、指定しなければいけないライブラリがたくさんあって面倒なのでgccをリンカ代わりにしています。
  • こちらで試したところ、15.0KBのmaze_asm.exeが生成されました。もちろんこれをそのまま実行することもできます。

(2) プログラムの説明

  • まず全体的な改造の構成を説明したいと思います。
    • 基本的には、compile()が生成した機械語を逆アセンブルして表示すればいいだけです。でもそれだけでは十分ではありません。
    • [1]文字列リテラルの値は、文字列へのポインタが定数として利用されるわけですが、アセンブラで出力する場合には、そこを別処理してやる必要があります。 それで、あとで文字列リテラルの内容を.dataセクション内に展開できるように varOpt[]変数にフラグを付けています。
    • [2]初期値付きの配列宣言の場合、初期値テーブルを.dataセクション内に展開しなければいけません。これを可能にするために、aryInitList[]とailpを用意しています。
    • [3]初期値付きの配列宣言のところで、putIcX86()の内容を少し変えました。
      旧: putIcX86("8b_%0m0; 89_44_24_00; b8_%1i; 89_44_24_04; b8_%2i; 89_44_24_08; e8_%3r;", &var[tc[wpc[0]]], (IntP) ip, (IntP) j, (IntP) sub_aryInit);
      新: putIcX86(         "89_44_24_00; b8_%0i; 89_44_24_04; b8_%1i; 89_44_24_08; e8_%2r;",                   (IntP) ip, (IntP) j, (IntP) sub_aryInit, 0);
      これは最初の8b命令が、直前の89命令の関係でoptimizerX86()関数によって必ず消されてしまうので、それなら最初からそんなものは書かないようにしただけです。
    • [4]アセンブラで出力することを考えると、JMP命令やJcc命令の飛び先を相対値に変更しないでラベル番号のままにしておく方が処理が楽なので、codedump==2のときは相対値に上書きする処理をやめさせました。
    • [5]asmX86()関数では、ラベル宣言に関して、プログラム内で参照されなかったものは出力しないようにしています。これはそうしないと、無意味なラベル宣言がたくさん出てきてしまってみっともないからです。一方それだけど、ユーザ定義したラベルも使われなければ消されてしまって、それだと不便かもしれないので、ユーザ定義ラベルは利用の有無にかかわらずvarOpt[]変数にフラグを付けておくようにしました。
  • それ以外の部分はこんな感じです。
    • regNamX86[] : レジスタ番号をレジスタ名に変換するためのテーブルです。
    • modRmX86(p, s) : pに mod r/m の機械語のアドレスを渡すと、sに mod r/m をでコードした文字列を格納し、関数値としては mod r/m の命令長を返します。
    • asmX86_sub1(q, p0, s, p1, t) : qで指定された情報を返します。その際には p0~t で指定された情報を利用します。
      • q=1: modRmの直前の1バイトの下位3bitによるレジスタ指定(INC, DEC, PUSH, POPなど)
      • q=2: modRm内の中間3bitによるレジスタ指定
      • q=3: modRmを解釈した結果
      • q=4: 8bit即値
      • q=5: 32bit即値
      • q=6: CL
      • q=7: sub関数呼び出し
      • q=8: JMP/Jccの分岐先ラベル
    • asmX86_sub0(p, op) : 1命令を指定された形式で逆アセンブルします。asmX86()の下請け関数です。命令長を返します。
    • asmX86_jcc[] : Jcc命令の表です。
    • asmX86_table0[][] : アセンブラの命令の表です。
      • (例)"0039m230ADD"
      • 最初の"0"は、頭に0x0fが付かないことを表します。
      • 次の"03"は機械語命令が0x03であることを表します。
      • 次の"9"はmod r/m の中の3ビットで命令が変化しないことを意味します。もしここが0~7だと、それも一致するかどうか調べます。
      • 次の"m"は、この命令はmod r/mフィールドを有するという意味です。ここが"_"だとmod r/mフィールドのない命令です。
      • 次の"230"はasmX86_sub1()のためのqの値です。
      • 最後の"ADD"が命令名です。
    • asmX86() : アセンブラ出力のメインルーチンです。

次回に続く

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2021-06-09 (水) 17:06:57 (109d)