『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








