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

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

昔ながらの BASIC(4)

『16ビットアセンブリ言語』という 8086 の本があったので、今度は仮想命令のコードを 8086 のアセンブリ言語に変換する処理を追加しました。これも ChatGPT で作ってもらったものを改造しました。「CALL OUTPUT」で数値を出力できるものとします。本があるからといって動くかどうかは確認できません。『独習アセンブラ 新版』という本もあるようなので見てみようと思っています。

class VirtualTo8086 {
    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 instr;
            ss >> instr;
            if (std::find(destnums.begin(), destnums.end(), ln) != destnums.end()) {
                cout << "LABEL_" << ln << ":\n";
            }
            if (instr == "PUSH") {
                int num;
                ss >> num;
                cout << "    MOV AX, " << num << "\n";
                cout << "    PUSH AX\n";
            }
            else if (instr == "LOAD") {
                string var_src;
                ss >> var_src;
                string var = getVariable(var_src);
                cout << "    MOV AX, [" << var << "]\n";
                cout << "    PUSH AX\n";
            }
            else if (instr == "STORE") {
                string var_src;
                ss >> var_src;
                string var = getVariable(var_src);
                cout << "    POP AX\n";
                cout << "    MOV [" << var << "], AX\n";
            }
            else if (instr == "ADD") {
                binaryOp("ADD");
            }
            else if (instr == "SUB") {
                binaryOp("SUB");
            }
            else if (instr == "MUL") {
                cout << "    POP BX\n";
                cout << "    POP AX\n";
                cout << "    MUL BX\n"; // AX = AX * BX (unsigned)
                cout << "    PUSH AX\n";
            }
            else if (instr == "DIV") {
                cout << "    POP BX\n";
                cout << "    POP AX\n";
                cout << "    XOR DX, DX\n";  // Clear remainder
                cout << "    DIV BX\n";      // AX = AX / BX, remainder in DX
                cout << "    PUSH AX\n";
            }
            else if (instr == "NEG") {
                cout << "    POP AX\n";
                cout << "    NEG AX\n";
                cout << "    PUSH AX\n";
            }
            else if (isComparison(instr)) {
                compareOp(instr, labelCounter++);
            }
            else if (instr == "JMP") {
                string label;
                ss >> label;
                cout << "    JMP LABEL_" << label << "\n";
            }
            else if (instr == "JNZ") {
                string label;
                ss >> label;
                cout << "    POP AX\n";
                cout << "    CMP AX, 0\n";
                cout << "    JNE LABEL_" << label << "\n";
            }
            else if (instr == "CALL") {
                string label;
                ss >> label;
                cout << "    CALL LABEL_" << label << "\n";
            }
            else if (instr == "RET") {
                cout << "    RET\n";
            }
            else if (instr == "OUT") {
                cout << "    CALL OUTPUT" << "\n";
            }
            else if (instr == "IN") {
                string var_src;
                ss >> var_src;
                string var = getVariable(var_src);
                cout << "    CALL INPUT" << "\n";
                cout << "    POP AX\n";
                cout << "    MOV [" << var << "], AX\n";
            }
            else if (instr == "REM") {
                // コメント
                string cmt;
                getline(ss, cmt);
                cout << ";" << cmt << "\n";
            }
            else if (instr.back() == ':') {
                cout << code << "\n"; // ラベル表示
            }
            else {
                cout << "    ; Unknown instruction\n";
            }
        }

        // メモリ変数の定義
        if (!variables.empty()) {
            cout << "\nDATA SEGMENT\n";
            for (const auto& [name, label] : variables) {
                cout << label << " DW 0\n";
            }
            cout << "DATA ENDS\n";
        }
    }

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

    string getVariable(const string& varName) {
        if (!variables.count(varName)) {
            variables[varName] = "var_" + varName;
        }
        return variables[varName];
    }

    bool isComparison(const string& op) {
        return op == "EQ" || op == "NE" || op == "LT" ||
            op == "LE" || op == "GT" || op == "GE";
    }

    void compareOp(const string& op, int labelId) {
        string labelTrue = "CMP_TRUE_" + to_string(labelId);
        string labelEnd = "CMP_END_" + to_string(labelId);

        cout << "    POP BX\n";
        cout << "    POP AX\n";
        cout << "    CMP AX, BX\n";

        string jmp;
        if (op == "EQ") jmp = "JE";
        else if (op == "NE") jmp = "JNE";
        else if (op == "LT") jmp = "JL";
        else if (op == "LE") jmp = "JLE";
        else if (op == "GT") jmp = "JG";
        else if (op == "GE") jmp = "JGE";

        cout << "    " << jmp << " " << labelTrue << "\n";
        cout << "    MOV AX, 0\n";
        cout << "    PUSH AX\n";
        cout << "    JMP " << labelEnd << "\n";
        cout << labelTrue << ":\n";
        cout << "    MOV AX, 1\n";
        cout << "    PUSH AX\n";
        cout << labelEnd << ":\n";
    }

    void binaryOp(const string& op) {
        cout << "    POP BX\n";
        cout << "    POP AX\n";
        cout << "    " << op << " AX, BX\n";
        cout << "    PUSH AX\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 compileVirtualTo8086() {
    VirtualTo8086 translator;
    translator.translate(program);
}

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

; フィボナッチ数列
    MOV AX, 10
    PUSH AX
    POP AX
    MOV [var_N], AX
    MOV AX, 1
    PUSH AX
    POP AX
    MOV [var_I], AX
    MOV AX, 1
    PUSH AX
    POP AX
    MOV [var_A], AX
    MOV AX, 1
    PUSH AX
    POP AX
    MOV [var_B], AX
LABEL_100:
    MOV AX, [var_A]
    PUSH AX
    CALL OUTPUT
    MOV AX, [var_B]
    PUSH AX
    POP AX
    MOV [var_T], AX
    MOV AX, [var_A]
    PUSH AX
    MOV AX, [var_B]
    PUSH AX
    POP BX
    POP AX
    ADD AX, BX
    PUSH AX
    POP AX
    MOV [var_B], AX
    MOV AX, [var_T]
    PUSH AX
    POP AX
    MOV [var_A], AX
    MOV AX, [var_I]
    PUSH AX
    MOV AX, 1
    PUSH AX
    POP BX
    POP AX
    ADD AX, BX
    PUSH AX
    POP AX
    MOV [var_I], AX
    MOV AX, [var_I]
    PUSH AX
    MOV AX, [var_N]
    PUSH AX
    POP BX
    POP AX
    CMP AX, BX
    JLE CMP_TRUE_0
    MOV AX, 0
    PUSH AX
    JMP CMP_END_0
CMP_TRUE_0:
    MOV AX, 1
    PUSH AX
CMP_END_0:
    POP AX
    CMP AX, 0
    JNE LABEL_100

DATA SEGMENT
var_N DW 0
var_A DW 0
var_I DW 0
var_B DW 0
var_T DW 0
DATA ENDS