非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

昔ながらの BASIC(3)

仮想命令のコードを 68000 のアセンブリ言語に変換する処理を追加しました。ChatGPT でやってもらったところ動かなかったのでそれを改造して作りました。

class VirtualTo68000 {
    unordered_map<string, string> variables;

public:
    void translate(map<int, string> program) {
        int labelCounter = 0;
        vector<int> destnums = getdestnums();
        for (auto& [ln, code] : program) {
            stringstream ss(code);
            string cmd;
            ss >> cmd;
            //cout << ln << " ";
            if (std::find(destnums.begin(), destnums.end(), ln) != destnums.end()) {
                cout << "LABEL_" << ln << ":\n";
            }
            if (cmd == "PUSH") {
                int num;
                ss >> num;
                cout << "    MOVE.L #" << num << ", -(A7)\n";
            }
            else if (cmd == "LOAD") {
                string var;
                ss >> var;
                variables[var] = variables.count(var) ? variables[var] : "VAR_" + var;
                cout << "    MOVE.L " << variables[var] << ", -(A7)\n";
            }
            else if (cmd == "STORE") {
                string var;
                ss >> var;
                variables[var] = variables.count(var) ? variables[var] : "VAR_" + var;
                cout << "    MOVE.L (A7)+, " << variables[var] << "\n";
            }
            else if (cmd == "ADD") {
                binaryOp("ADD.L");
            }
            else if (cmd == "SUB") {
                binaryOp("SUB.L");
            }
            else if (cmd == "MUL") {
                binaryOp("MULS"); // 符号付き乗算
            }
            else if (cmd == "DIV") {
                binaryOp("DIVS"); // 符号付き除算
            }
            else if (cmd == "NEG") {
                cout << "    MOVE.L (A7)+, D0\n";
                cout << "    NEG.L D0\n";
                cout << "    MOVE.L D0, -(A7)\n";
            }
            else if (cmd == "EQ" || cmd == "NE" || cmd == "LT" ||
                cmd == "LE" || cmd == "GT" || cmd == "GE") {
                compareOp(cmd, labelCounter++);
            }
            else if (cmd == "JMP") {
                string label;
                ss >> label;
                cout << "    JMP LABEL_" << label << "\n";
            }
            else if (cmd == "JNZ") {
                string label;
                ss >> label;
                cout << "    MOVE.L (A7)+, D0\n";
                cout << "    TST.L D0\n";
                cout << "    BNE LABEL_" << label << "\n";
            }
            else if (cmd == "CALL") {
                string label;
                ss >> label;
                cout << "    JSR LABEL_" << label << "\n";
            }
            else if (cmd == "RET") {
                cout << "    RTS\n";
            }
            else if (cmd == "OUT") {
                cout << "    JSR OUTPUT" << "\n";
            }
            else if (cmd == "IN") {
                string var;
                ss >> var;
                cout << "    JSR INPUT" << "\n";
                variables[var] = variables.count(var) ? variables[var] : "VAR_" + var;
                cout << "    MOVE.L (A7)+, " << variables[var] << "\n";
            }
            else if (cmd == "REM") {
                // コメント
                string cmt;
                getline(ss, cmt);
                cout << ";" << cmt << "\n";
            }
            else {
                cout << "    ; Unknown instruction: " << cmd << "\n";
            }
        }

        // グローバル変数定義(必要に応じて)
        if (!variables.empty()) {
            cout << "\n    SECTION DATA\n";
            for (const auto& [name, label] : variables) {
                cout << label << ": DC.L 0\n";
            }
        }
    }

private:
    vector<string> tokenize(const string& line) {
        vector<string> tokens;
        size_t start = 0, end;
        while ((end = line.find(' ', start)) != string::npos) {
            tokens.push_back(line.substr(start, end - start));
            start = end + 1;
        }
        if (start < line.size()) tokens.push_back(line.substr(start));
        return tokens;
    }

    void binaryOp(const string& op) {
        cout << "    MOVE.L (A7)+, D0\n";
        cout << "    MOVE.L (A7)+, D1\n";
        if (op == "SUB.L" || op == "DIVS")
            cout << "    " << op << " D0, D1\n"; // D1 - D0 or D1 / D0
        else
            cout << "    " << op << " D0, D1\n"; // D1 + D0 or D1 * D0
        cout << "    MOVE.L D1, -(A7)\n";
    }

    void compareOp(const string& op, int labelId) {
        string trueLabel = "LABEL_TRUE_" + to_string(labelId);
        string endLabel = "LABEL_END_" + to_string(labelId);

        cout << "    MOVE.L (A7)+, D0\n";
        cout << "    MOVE.L (A7)+, D1\n";
        cout << "    CMP.L D0, D1\n";

        string branch;
        if (op == "EQ") branch = "BEQ";
        else if (op == "NE") branch = "BNE";
        else if (op == "LT") branch = "BLT";
        else if (op == "LE") branch = "BLE";
        else if (op == "GT") branch = "BGT";
        else if (op == "GE") branch = "BGE";

        cout << "    " << branch << " " << trueLabel << "\n";
        cout << "    MOVE.L #0, -(A7)\n";
        cout << "    BRA " << endLabel << "\n";
        cout << trueLabel << ":\n";
        cout << "    MOVE.L #1, -(A7)\n";
        cout << endLabel << ":\n";
    }

    // 仮想命令コードの行き先になっている行番号を返す
    int getdestnum(const string& line) {
        stringstream ss(line);
        string cmd;
        ss >> cmd;

        cmd = to_upper(cmd);

        if (cmd == "JMP" || cmd == "JNZ" || cmd == "CALL") {
            int target;
            ss >> target;
            return target;
        }
        return -1;
    }

    // 仮想命令コードの行き先になっているすべての行番号を返す
    vector<int> getdestnums() {
        vector<int> result;
        auto pit = program.begin();
        while (pit != program.end()) {
            int destnum = getdestnum(pit->second);
            if (destnum >= 0) {
                result.push_back(destnum);
            }
            pit++;
        }
        return result;
    }
};

void compileVirtualTo68000() {
    cout << "=== COMPILED OUTPUT ===\n";
    VirtualTo68000 translator;
    translator.translate(program);
    cout << "=== END COMPILE ===\n";
}

フィボナッチ数列の例を変換した結果は以下のようになります。

; フィボナッチ数列
    MOVE.L #10, -(A7)
    MOVE.L (A7)+, VAR_N
    MOVE.L #1, -(A7)
    MOVE.L (A7)+, VAR_I
    MOVE.L #1, -(A7)
    MOVE.L (A7)+, VAR_A
    MOVE.L #1, -(A7)
    MOVE.L (A7)+, VAR_B
LABEL_100:
    MOVE.L VAR_A, -(A7)
    JSR OUTPUT
    MOVE.L VAR_B, -(A7)
    MOVE.L (A7)+, VAR_T
    MOVE.L VAR_A, -(A7)
    MOVE.L VAR_B, -(A7)
    MOVE.L (A7)+, D0
    MOVE.L (A7)+, D1
    ADD.L D0, D1
    MOVE.L D1, -(A7)
    MOVE.L (A7)+, VAR_B
    MOVE.L VAR_T, -(A7)
    MOVE.L (A7)+, VAR_A
    MOVE.L VAR_I, -(A7)
    MOVE.L #1, -(A7)
    MOVE.L (A7)+, D0
    MOVE.L (A7)+, D1
    ADD.L D0, D1
    MOVE.L D1, -(A7)
    MOVE.L (A7)+, VAR_I
    MOVE.L VAR_I, -(A7)
    MOVE.L VAR_N, -(A7)
    MOVE.L (A7)+, D0
    MOVE.L (A7)+, D1
    CMP.L D0, D1
    BLE LABEL_TRUE_0
    MOVE.L #0, -(A7)
    BRA LABEL_END_0
LABEL_TRUE_0:
    MOVE.L #1, -(A7)
LABEL_END_0:
    MOVE.L (A7)+, D0
    TST.L D0
    BNE LABEL_100

    SECTION DATA
VAR_N: DC.L 0
VAR_A: DC.L 0
VAR_I: DC.L 0
VAR_B: DC.L 0
VAR_T: DC.L 0

「JSR OUTPUT」で数値を出力できるとします。これが動くかどうかは確認できていません。