自制虚拟机(C/C++)(三、做成标准GUI Windows软件,扩展指令集,直接支持img软盘)

开源地址:VMwork

要使终端不弹出,

#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")

还要实现jmp near 0x01类似的

本次的main.cpp

#include <graphics.h>
#include <conio.h>
#include <windows.h>
#include <commdlg.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <thread>
#include <mutex>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <shlwapi.h>using namespace std;#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")void VM(const string& file);void ShowErrorDialog(const string& message) {MessageBox(GetHWnd(), message.c_str(), "VMwork 错误", MB_ICONERROR | MB_OK);
}string GetCurrentDirectoryPath() {char buffer[MAX_PATH] = {0};GetModuleFileNameA(NULL, buffer, MAX_PATH);string::size_type pos = string(buffer).find_last_of("\\/");return string(buffer).substr(0, pos);
}// 虚拟机配置结构体
struct VMConfig {string name;string filePath;  // 统一文件路径字段string fileType;  // "ASM" 或 "IMG"
};// 全局配置存储
map<string, VMConfig> vmConfigs;
const char* CONFIG_FILE = "vm.dll";// 图形界面尺寸参数
const int WIDTH = 800;
const int HEIGHT = 600;
const int BTN_WIDTH = 200;
const int BTN_HEIGHT = 40;// 当前操作状态
enum class AppState {MAIN_MENU,CREATE_VM,OPEN_VM,SETTINGS
};
AppState currentState = AppState::MAIN_MENU;char vmNameInput[64] = {0};
char asmPathInput[256] = {0};void InitGUI() {initgraph(WIDTH, HEIGHT);HWND hWnd = GetHWnd();SetWindowText(hWnd, "VMwork 虚拟机");ShowWindow(hWnd, SW_SHOWNORMAL);setbkcolor(WHITE);cleardevice();settextcolor(BLACK);settextstyle(30, 0, "宋体");
}void SaveConfig() {ofstream fout(CONFIG_FILE);for (auto& entry : vmConfigs) {auto& name = entry.first;auto& config = entry.second;fout << name << endl;fout << config.filePath << endl;fout << config.fileType << endl;}fout.close();
}void LoadConfig() {vmConfigs.clear();ifstream fin(CONFIG_FILE);string name, path, type;while (getline(fin, name) && getline(fin, path) && getline(fin, type)) {vmConfigs[name] = {name, path, type};}fin.close();
}void DrawMainMenu() {cleardevice();setbkcolor(WHITE);settextcolor(BLUE);settextstyle(30, 0, "宋体");outtextxy(100, 50, "VMwork 虚拟机");setfillcolor(LIGHTGRAY);fillroundrect(300, 150, 300 + BTN_WIDTH, 150 + BTN_HEIGHT, 5, 5);outtextxy(330, 155, "新建虚拟机");fillroundrect(300, 250, 300 + BTN_WIDTH, 250 + BTN_HEIGHT, 5, 5);outtextxy(330, 255, "打开虚拟机");fillroundrect(300, 350, 300 + BTN_WIDTH, 350 + BTN_HEIGHT, 5, 5);outtextxy(350, 355, "设置");
}string SelectFile() {OPENFILENAMEA ofn;char szFile[260] = {0};ZeroMemory(&ofn, sizeof(ofn));ofn.lStructSize = sizeof(ofn);ofn.lpstrFile = szFile;ofn.nMaxFile = sizeof(szFile);ofn.lpstrFilter = "IMG Files (*.img)\0*.img\0All Files (*.*)\0*.*\0";ofn.nFilterIndex = 1;ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;if (GetOpenFileNameA(&ofn)) {return szFile;}return "";
}bool InputBox(const char* title, char* buffer, int bufferSize) {char prompt[10] = "";return InputBox(buffer, bufferSize, prompt, title); 
}void CreateVMProcess() {int fileType = MessageBox(GetHWnd(), "是否启用读取nasm语法?\n[否]则以img软盘读取", "VMwork", MB_YESNO | MB_ICONQUESTION);bool isASM = (fileType == IDYES);OPENFILENAMEA ofn;char szFile[260] = {0};ZeroMemory(&ofn, sizeof(ofn));ofn.lStructSize = sizeof(ofn);ofn.lpstrFile = szFile;ofn.nMaxFile = sizeof(szFile);ofn.lpstrFilter = isASM ? "ASM Files (*.asm)\0*.asm\0All Files (*.*)\0*.*\0" :"IMG Files (*.img)\0*.img\0All Files (*.*)\0*.*\0";ofn.nFilterIndex = 1;ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;if (!GetOpenFileNameA(&ofn)) return;if (!InputBox("输入虚拟机名称", vmNameInput, sizeof(vmNameInput))) return;vmConfigs[vmNameInput] = {vmNameInput, szFile, isASM ? "ASM" : "IMG"};SaveConfig();
}void DisassembleImg(const string& imgPath, const string& asmPath) {string cmd = "cmd /c \"\"" + GetCurrentDirectoryPath() + "\\ndisasm.exe\" \"" + imgPath + "\" > \"" + asmPath + "\"\"";WinExec(cmd.c_str(), SW_HIDE);
}void ProcessAsm(const string& asmPath, const string& finalAsmPath) {string cmd = "cmd /c \"\"" + GetCurrentDirectoryPath() + "\\toasm.exe\" \"" + asmPath + "\" \"" + finalAsmPath + "\"\"";WinExec(cmd.c_str(), SW_HIDE);
}void OpenVMProcess() {LoadConfig();cleardevice();settextcolor(BLACK);outtextxy(50, 50, "选择虚拟机:");int y = 100;for (auto& entry : vmConfigs) {std::string text = entry.first + " (" + entry.second.filePath + ")";outtextxy(100, y, const_cast<char*>(text.c_str()));y += 50;}int choice = 0;while (true) {if (MouseHit()) {MOUSEMSG msg = GetMouseMsg();if (msg.uMsg == WM_LBUTTONDOWN) {int clickY = msg.y;int index = 0;for (auto& entry : vmConfigs) {int itemTop = 100 + index * 40;if (clickY > itemTop && clickY < itemTop + 50) {choice = index + 1;break;}index++;}}}if (choice!= 0) break;}if (choice > 0 && choice <= vmConfigs.size()) {auto it = vmConfigs.begin();advance(it, choice - 1);string selectedPath = it->second.filePath;string tempAsmPath = "temp.asm";string finalAsmPath = "final.asm";if (it->second.fileType == "IMG") {DisassembleImg(selectedPath, tempAsmPath);ProcessAsm(tempAsmPath, finalAsmPath);}else {finalAsmPath = selectedPath;}VM(finalAsmPath);}
}void SetVMProcess() {LoadConfig();cleardevice();settextcolor(BLACK);outtextxy(30, 50, "虚拟机");outtextxy(50, 90, "处理器  : 1");outtextxy(50, 130, "内存    : 64KB");outtextxy(50, 170, "启动方式: 软盘(引导扇区)");outtextxy(50, 210, "光盘    : 无");outtextxy(50, 250, "BIOS    : VMwork");outtextxy(50, 290, "方式    : .IMG/.ASM");while (1) {}
}void MainLoop() {AppState prevState = AppState::MAIN_MENU; while (true) {if (MouseHit()) {MOUSEMSG msg = GetMouseMsg();if (msg.uMsg == WM_LBUTTONDOWN) {// 主界面按钮处理if (currentState == AppState::MAIN_MENU) {if (msg.x > 300 && msg.x < 300 + BTN_WIDTH) {if (msg.y > 150 && msg.y < 150 + BTN_HEIGHT) {CreateVMProcess();} else if (msg.y > 250 && msg.y < 250 + BTN_HEIGHT) {OpenVMProcess();} else if (msg.y > 350 && msg.y < 350 + BTN_HEIGHT) {SetVMProcess(); }}}}}switch (currentState) {case AppState::MAIN_MENU:if (prevState!= AppState::MAIN_MENU) {//cleardevice();}DrawMainMenu();break;case AppState::CREATE_VM:if (prevState!= AppState::CREATE_VM) {//cleardevice();}DrawMainMenu();break;case AppState::OPEN_VM:if (prevState!= AppState::OPEN_VM) {//cleardevice();}DrawMainMenu();break;case AppState::SETTINGS:if (prevState!= AppState::SETTINGS) {//cleardevice();}DrawMainMenu();break;}prevState = currentState;Sleep(30);}
}// 寄存器声明
unsigned char al = 0, ah = 0, bl = 0, bh = 0, cl = 0, ch = 0, dl = 0, dh = 0, si = 0;
unsigned short ax = 0, bx = 0, cx = 0, dx = 0, sp = 0x8000, bp = 0;
unsigned int org = 0, end_times = 0, end_AA55 = 0;
bool ZF = false, CF = false, SF = false;unordered_map<string, size_t> labels;
size_t current_line = 0;
size_t new_current_line;
vector<string> program_lines;vector<unsigned char> memory(0x10000, 0);mutex fileMutex;int textX = 0;
int textY = 48;
const int CHAR_WIDTH = 8;
const int LINE_HEIGHT = 16;
bool graphicsInitialized = false;enum class InstructionError {INVALID_OPCODE,INVALID_OPERAND,LABEL_NOT_FOUND,UNKNOWN_INTERRUPT,OTHER_ERROR
};void printError(const InstructionError& error, const string& details = "") {//cerr << "ERROR: ";switch (error) {case InstructionError::INVALID_OPCODE: MessageBox(GetHWnd(), "无效的操作码", "运行时错误", MB_ICONERROR); break;case InstructionError::INVALID_OPERAND: MessageBox(GetHWnd(), "无效的操作数", "运行时错误", MB_ICONERROR); break;case InstructionError::LABEL_NOT_FOUND: MessageBox(GetHWnd(), "标签未找到", "运行时错误", MB_ICONERROR); break;case InstructionError::UNKNOWN_INTERRUPT: MessageBox(GetHWnd(), "未知的中断号", "运行时错误", MB_ICONERROR); break;case InstructionError::OTHER_ERROR: MessageBox(GetHWnd(), "未知错误", "运行时错误", MB_ICONERROR); break;}if (!details.empty()) cerr << " - " << details;cerr << endl;
}int parseImmediate(const string& immediateStr) {string result;bool inQuote = false;char quoteChar = '\0';for (size_t i = 0; i < immediateStr.size(); ++i) {char c = immediateStr[i];if (c == '\'' || c == '"') {if (!inQuote) {inQuote = true;quoteChar = c;} else if (c == quoteChar) {inQuote = false;}} else if (!inQuote && isspace(c)) {continue;}result += c;}if (result.empty()) return 0;if (result.length() == 3 && result[0] == '\'' && result[2] == '\'') {return static_cast<int>(result[1]);}else if (result.find("0x") == 0) {return stoi(result.substr(2), nullptr, 16);}else if (result.back() == 'h') {return stoi(result.substr(0, result.length() - 1), nullptr, 16);}else {return stoi(result);}
}int parseImmediate1(const string& immediateStr) {string processed = immediateStr;// 移除跳转修饰符(near/short/far)vector<string> modifiers = {"near ", "short ", "far "};for (const auto& mod : modifiers) {size_t pos = processed.find(mod);if (pos != string::npos) {processed.erase(pos, mod.length());break;}}// 处理段:偏移格式(仅取偏移部分)size_t colon = processed.find(':');if (colon != string::npos) {processed = processed.substr(colon + 1);}// 处理返回指令的立即数(ret 4 -> 解析4)size_t space = processed.find(' ');if (space != string::npos) {processed = processed.substr(0, space);}// 处理引号字符('A' -> 65)if (processed.length() == 3 && processed[0] == '\'' && processed[2] == '\'') {return static_cast<int>(processed[1]);}// 处理十六进制格式if (processed.find("0x") == 0) {return stoi(processed.substr(2), nullptr, 16);}// 处理h结尾的十六进制if (!processed.empty() && processed.back() == 'h') {return stoi(processed.substr(0, processed.length()-1), nullptr, 16);}// 处理二进制格式(新增支持)if (processed.find("0b") == 0) {return stoi(processed.substr(2), nullptr, 2);}// 处理符号(+/-)bool negative = false;if (!processed.empty()) {if (processed[0] == '+') {processed = processed.substr(1);} else if (processed[0] == '-') {negative = true;processed = processed.substr(1);}}// 最终数值转换try {int value = stoi(processed);return negative ? -value : value;} catch (...) {//throw invalid_argument("无法解析的立即数: " + immediateStr);MessageBox(GetHWnd(), ("无法解析的立即数: " + immediateStr).c_str(), "运行时错误", MB_ICONERROR);}
}unordered_map<string, unsigned char*> createRegister8BitMap() {return {{"al", &al}, {"ah", &ah}, {"bl", &bl}, {"bh", &bh},{"cl", &cl}, {"ch", &ch}, {"dl", &dl}, {"dh", &dh},{"si", &si}};
}unordered_map<string, unsigned short*> createRegister16BitMap() {return {{"ax", &ax}, {"bx", &bx}, {"cx", &cx}, {"dx", &dx},{"sp", &sp}, {"bp", &bp}};
}void UpdateTextPosition() {textX += CHAR_WIDTH;if (textX > 620) {textX = 0;textY += LINE_HEIGHT;}if (textY + LINE_HEIGHT > 480) {cleardevice();textX = 0;textY = 0;}
}void MovInstruction(const string& line) {string processedLine = line;replace(processedLine.begin(), processedLine.end(), ',', ' ');istringstream iss(processedLine);string opcode, dest, src;iss >> opcode >> dest >> src;auto reg8 = createRegister8BitMap();auto reg16 = createRegister16BitMap();auto parseOperand = [&](const string& op) -> int {if (reg8.count(op)) return *reg8[op];if (reg16.count(op)) return *reg16[op];return parseImmediate(op);};int value = parseOperand(src);if (reg8.count(dest)) {*reg8[dest] = static_cast<unsigned char>(value);}else if (reg16.count(dest)) {*reg16[dest] = static_cast<unsigned short>(value);}
}void AddInstruction(const string& line) {string processedLine = line;replace(processedLine.begin(), processedLine.end(), ',', ' ');istringstream iss(processedLine);string opcode, dest, src;iss >> opcode >> dest >> src;auto reg8 = createRegister8BitMap();auto reg16 = createRegister16BitMap();auto parseOperand = [&](const string& op) -> int {if (reg8.count(op)) return *reg8[op];if (reg16.count(op)) return *reg16[op];return parseImmediate(op);};int val1 = parseOperand(dest);int val2 = parseOperand(src);int result = val1 + val2;if (reg8.count(dest)) {*reg8[dest] = static_cast<unsigned char>(result);}else if (reg16.count(dest)) {*reg16[dest] = static_cast<unsigned short>(result);}ZF = (result == 0);SF = (result < 0);CF = (static_cast<unsigned>(result) < static_cast<unsigned>(val1));
}void SubInstruction(const string& line) {string processedLine = line;replace(processedLine.begin(), processedLine.end(), ',', ' ');istringstream iss(processedLine);string opcode, dest, src;iss >> opcode >> dest >> src;auto reg8 = createRegister8BitMap();auto reg16 = createRegister16BitMap();auto parseOperand = [&](const string& op) -> int {if (reg8.count(op)) return *reg8[op];if (reg16.count(op)) return *reg16[op];return parseImmediate(op);};int val1 = parseOperand(dest);int val2 = parseOperand(src);int result = val1 - val2;if (reg8.count(dest)) {*reg8[dest] = static_cast<unsigned char>(result);}else if (reg16.count(dest)) {*reg16[dest] = static_cast<unsigned short>(result);}ZF = (result == 0);SF = (result < 0);CF = (static_cast<unsigned>(val1) < static_cast<unsigned>(val2));
}void IncInstruction(const string& line) {istringstream iss(line);string opcode, operand;iss >> opcode >> operand;auto reg8 = createRegister8BitMap();auto reg16 = createRegister16BitMap();if (reg8.count(operand)) {*reg8[operand] += 1;ZF = (*reg8[operand] == 0);SF = (*reg8[operand] < 0);}else if (reg16.count(operand)) {*reg16[operand] += 1;ZF = (*reg16[operand] == 0);SF = (*reg16[operand] < 0);}
}void CmpInstruction(const string& line) {string processedLine = line;replace(processedLine.begin(), processedLine.end(), ',', ' ');istringstream iss(processedLine);string opcode, op1, op2;iss >> opcode >> op1 >> op2;auto reg8 = createRegister8BitMap();auto reg16 = createRegister16BitMap();auto parseOperand = [&](const string& op) -> int {if (reg8.count(op)) return *reg8[op];if (reg16.count(op)) return *reg16[op];return parseImmediate(op);};int val1 = parseOperand(op1);int val2 = parseOperand(op2);int result = val1 - val2;ZF = (result == 0);SF = (result < 0);CF = (static_cast<unsigned>(val1) < static_cast<unsigned>(val2));
}void JmpInstruction(const string& line) {istringstream iss(line);string opcode, modifier, operand;iss >> opcode >> modifier;// 处理跳转修饰符(short/near)if (modifier == "short" || modifier == "near" || modifier == "nearptr") {iss >> operand;} else {operand = modifier;}// 优先尝试标签跳转if (labels.count(operand)) {new_current_line = labels[operand];return;}// 尝试解析为数值跳转try {int offset = parseImmediate(operand);// 计算相对于下一条指令的偏移new_current_line = current_line + 1 + offset;} catch (...) {printError(InstructionError::LABEL_NOT_FOUND, "JMP指令中的目标未找到: " + operand);}
}void JeInstruction(const string& line) {istringstream iss(line);string opcode, modifier, operand;iss >> opcode >> modifier;if (modifier == "short" || modifier == "near" || modifier == "nearptr") {iss >> operand;} else {operand = modifier;}if (ZF) {if (labels.count(operand)) {new_current_line = labels[operand];} else {try {int offset = parseImmediate(operand);new_current_line = current_line + 1 + offset;} catch (...) {printError(InstructionError::LABEL_NOT_FOUND, "JE指令中的目标未找到: " + operand);}}} else {new_current_line = current_line + 1;}
}void JneInstruction(const string& line) {istringstream iss(line);string opcode, modifier, operand;iss >> opcode >> modifier;if (modifier == "short" || modifier == "near" || modifier == "nearptr") {iss >> operand;} else {operand = modifier;}if (!ZF) {if (labels.count(operand)) {new_current_line = labels[operand];} else {try {int offset = parseImmediate(operand);new_current_line = current_line + 1 + offset;} catch (...) {printError(InstructionError::LABEL_NOT_FOUND, "JNE指令中的目标未找到: " + operand);}}} else {new_current_line = current_line + 1;}
}void CallInstruction(const string& line) {istringstream iss(line);string opcode, modifier, operand;iss >> opcode;  // 读取call// 读取剩余部分作为操作数getline(iss, operand);operand = operand.substr(operand.find_first_not_of(" \t"), operand.find_last_not_of(" \t")+1);// 处理修饰符(如far)size_t space = operand.find(' ');if (space != string::npos) {operand = operand.substr(space+1);}// 压入返回地址sp -= 2;memory[sp] = (current_line + 1) & 0xFF;memory[sp + 1] = ((current_line + 1) >> 8) & 0xFF;try {int offset = parseImmediate1(operand);new_current_line = current_line + 1 + offset;} catch (...) {if (labels.count(operand)) {new_current_line = labels[operand];  // 修正拼写错误} else {// 使用图形界面提示错误MessageBox(GetHWnd(), ("CALL目标未找到: " + operand).c_str(), "VM错误", MB_ICONERROR);}}
}// 处理ret指令(支持立即数参数)
void RetInstruction(const string& line) {istringstream iss(line);string opcode, operand;iss >> opcode;  // 读取ret// 读取返回地址unsigned short return_addr = memory[sp] | (memory[sp + 1] << 8);sp += 2;// 处理带参数的ret(ret 4)if (iss >> operand) {int adjust = parseImmediate1(operand);sp += adjust;}new_current_line = return_addr;
}void PushInstruction(const std::string& line) {std::istringstream iss(line);std::string opcode, src;iss >> opcode >> src;auto reg16 = createRegister16BitMap(); unsigned short value = reg16.count(src)? *reg16[src] : parseImmediate(src);sp -= 2;memory[sp] = value & 0xFF;memory[sp + 1] = (value >> 8) & 0xFF;
}void PopInstruction(const std::string& line) {std::istringstream iss(line);std::string opcode, dest;iss >> opcode >> dest;auto reg16 = createRegister16BitMap(); if (reg16.count(dest)) {*reg16[dest] = memory[sp] | (memory[sp + 1] << 8);sp += 2;}
}void XorInstruction(const std::string& line) {std::string processedLine = line;std::replace(processedLine.begin(), processedLine.end(), ',', ' ');std::istringstream iss(processedLine);std::string opcode, dest, src;iss >> opcode >> dest >> src;auto reg8 = createRegister8BitMap(); auto reg16 = createRegister16BitMap(); auto parseOperand = [&](const std::string& op) -> int {if (reg8.count(op)) return *reg8[op];if (reg16.count(op)) return *reg16[op];return parseImmediate(op);};int val1 = parseOperand(dest);int val2 = parseOperand(src);int result = val1 ^ val2;if (reg8.count(dest)) {*reg8[dest] = static_cast<unsigned char>(result);}else if (reg16.count(dest)) {*reg16[dest] = static_cast<unsigned short>(result);}ZF = (result == 0);SF = (result < 0);CF = false;
}void PreprocessLabels() {for (size_t i = 0; i < program_lines.size(); ++i) {std::string line = program_lines[i];std::istringstream iss(line);std::string address;if (iss >> address) {// 去除地址中的冒号address.erase(std::remove(address.begin(), address.end(), ':'), address.end());labels[address] = i;}size_t colonPos = line.find(':');if (colonPos!= std::string::npos) {std::string label = line.substr(0, colonPos);labels[label] = i;program_lines[i] = line.substr(colonPos + 1);//std::cout << "Label found: " << label << " at line " << i << std::endl;}}
}void IntInstruction(const std::string& line) {std::string processedLine = line;std::replace(processedLine.begin(), processedLine.end(), ',', ' ');std::istringstream iss(processedLine);std::string opcode, interrupt;iss >> opcode >> interrupt;BYTE keyState[256];if (interrupt == "0x10" || interrupt == "10h") {if (ah == 0x0E) {if (!graphicsInitialized) {initgraph(640, 480);HWND hwnd = GetHWnd();SetWindowPos(hwnd, HWND_TOP, 100, 100, 0, 0, SWP_NOSIZE);setbkcolor(BLACK);cleardevice();settextcolor(CYAN);settextstyle(16, 0, _T("Courier New Bold"));graphicsInitialized = true;outtextxy(textX, 0, "  VMwork BIOS (PCI)");outtextxy(textX, 16, "  This VGA/VBE BIOS is released under the GNU LGPL");settextcolor(RGB(192, 192, 192));}// 处理特殊字符if (al == 0x0D) {outtextxy(textX, textY, " ");textY += LINE_HEIGHT;}else if (al == 0x0A) {outtextxy(textX, textY, " ");textX = 0;}else {char str[2] = { static_cast<char>(al) };outtextxy(textX, textY, " ");outtextxy(textX, textY, str);UpdateTextPosition();outtextxy(textX, textY, "|");}}if (ah == 0x02 && bh == 0) {textX = 0;textY = 0;}if (ax == 0x0600 && bx == 0x0700 && cx == 0 && dx == 0x184f) {setfillcolor(BLACK);bar(0, 0, 640, 480);}}else if (interrupt == "0x16" || interrupt == "16h") {if (ah == 0) {bool consoleAllocated = false;if (!consoleAllocated) {  // 确保只创建一次控制台AllocConsole();freopen("CONIN$", "r", stdin);freopen("CONOUT$", "w", stdout);system("title VMwork控制和调试终端(在此输入键盘事件):");consoleAllocated = true;}INPUT_RECORD inputRec;DWORD eventsRead;while (true) {if (ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inputRec, 1, &eventsRead)) {if (inputRec.EventType == KEY_EVENT && inputRec.Event.KeyEvent.bKeyDown) {// 使用系统API转换字符BYTE keyState[256];GetKeyboardState(keyState);WORD charCode = 0;ToAscii(inputRec.Event.KeyEvent.wVirtualKeyCode,inputRec.Event.KeyEvent.wVirtualScanCode,keyState,&charCode,0);al = LOBYTE(charCode);  // 低字节是ASCII码if (LOBYTE(charCode) == '\n') {al = 0x0D;}break;}}Sleep(10);}}
}else {printError(InstructionError::UNKNOWN_INTERRUPT, "警告:未知的中断号: " + interrupt);}
}void OrgInstruction(const std::string& line) {std::string processedLine = line;std::replace(processedLine.begin(), processedLine.end(), ',', ' ');std::istringstream iss(processedLine);std::string opcode, interrupt;iss >> opcode >> interrupt;if (interrupt == "0x7c00" || interrupt == "0x7C00") {org = 0x7c00;} else {printError(InstructionError::INVALID_OPERAND, "ORG指令的操作数无效: " + interrupt);}
}void TimesInstruction(const std::string& line) {std::string processedLine = line;std::replace(processedLine.begin(), processedLine.end(), ',', ' ');std::istringstream iss(processedLine);std::string opcode, interrupt;iss >> opcode >> interrupt;if (interrupt == "510-($-$$) db 0" || interrupt == "510-($-$$)") {end_times = 1;} else {printError(InstructionError::INVALID_OPERAND, "TIMES指令的操作数无效: " + interrupt);}
}void DwInstruction(const std::string& line) {std::string processedLine = line;std::replace(processedLine.begin(), processedLine.end(), ',', ' ');std::istringstream iss(processedLine);std::string opcode, interrupt;iss >> opcode >> interrupt;if (interrupt == "0xAA55" || interrupt == "0xaa55") {end_AA55 = 1;} else {printError(InstructionError::INVALID_OPERAND, "DW指令的操作数无效: " + interrupt);}
}void VM(const std::string& asmPath) {HWND hWnd = initgraph(640, 480, SHOWCONSOLE);SetWindowText(hWnd, "VMwork 虚拟机运行中");setbkcolor(BLACK);cleardevice();// 显示加载动画settextcolor(WHITE);settextstyle(24, 0, "宋体");outtextxy(50, 200, "正在启动虚拟机...");for (int i = 0; i < 10; ++i) {setfillcolor(HSVtoRGB(i*36, 1, 1));solidcircle(100 + i*50, 300, 15);Sleep(100);}cleardevice();std::ifstream file(asmPath);if (!file.is_open()) {std::cerr << "无法打开文件: " << asmPath << std::endl;return;}std::string line;while (std::getline(file, line)) {size_t commentPos = line.find(';');if (commentPos!= std::string::npos) {line = line.substr(0, commentPos);}// 去除行首尾的空白字符while (!line.empty() && std::isspace(line.front())) {line.erase(line.begin());}while (!line.empty() && std::isspace(line.back())) {line.erase(line.length() - 1);}if (!line.empty()) {program_lines.push_back(line);}}file.close();for (auto& progLine : program_lines) {for (size_t i = 0; i < progLine.size(); ++i) {if (i < progLine.size() - 2 && progLine[i] == '\'' && progLine[i + 1] ==' ' && progLine[i + 2] == '\'') {progLine[i] = static_cast<char>(0x20);progLine.erase(i + 1, 2);  // 移除后面的空格和单引号}if (i < progLine.size() - 2 && progLine[i] == '\'' && progLine[i + 1] ==':' && progLine[i + 2] == '\'') {progLine[i] = '3';progLine[i + 1] = 'A';progLine[i + 2] = 'h';}if (i < progLine.size() - 2 && progLine[i] == '\'' && progLine[i + 1] ==',' && progLine[i + 2] == '\'') {progLine[i] = '2';progLine[i + 1] = 'C';progLine[i + 2] = 'h';}if (i < progLine.size() - 2 && progLine[i] == '\'' && progLine[i + 1] =='_' && progLine[i + 2] == '\'') {progLine[i] = '5';progLine[i + 1] = 'F';progLine[i + 2] = 'h';}}}PreprocessLabels();// 重置指令指针和新的指令指针new_current_line = current_line;while (current_line < program_lines.size()) {std::istringstream iss(program_lines[current_line]);std::string opcode;iss >> opcode;if (opcode == "mov") MovInstruction(program_lines[current_line]);else if (opcode == "int") IntInstruction(program_lines[current_line]);else if (opcode == "org") OrgInstruction(program_lines[current_line]);else if (opcode == "times") TimesInstruction(program_lines[current_line]);else if (opcode == "dw") DwInstruction(program_lines[current_line]);else if (opcode == "cmp") CmpInstruction(program_lines[current_line]);else if (opcode == "jmp") {std::string label;iss >> label;// 处理相对跳转地址表示法,假设这里的相对跳转是相对于当前行号(根据实际情况调整)if (label.find("0x") == 0) {try {size_t targetAddress = std::stoi(label.substr(2), nullptr, 16);// 如果找到地址标签,更新当前行号if (labels.count(label)) {new_current_line = labels[label];} else {// 处理相对地址size_t relativeAddress = targetAddress - (current_line - labels.begin()->second);new_current_line = current_line + relativeAddress;}} catch (const std::invalid_argument& e) {printError(InstructionError::LABEL_NOT_FOUND, "JMP指令中的非法地址标签: " + label);} catch (const std::out_of_range& e) {printError(InstructionError::LABEL_NOT_FOUND, "JMP指令中的地址标签超出范围: " + label);}} else {JmpInstruction(program_lines[current_line]);}}else if (opcode == "je" || opcode == "jz") JeInstruction(program_lines[current_line]);else if (opcode == "jne" || opcode == "jnz") JneInstruction(program_lines[current_line]);else if (opcode == "push") PushInstruction(program_lines[current_line]);else if (opcode == "pop") PopInstruction(program_lines[current_line]);else if (opcode == "xor") XorInstruction(program_lines[current_line]);else if (opcode == "call") CallInstruction(program_lines[current_line]);else if (opcode == "add") AddInstruction(program_lines[current_line]);else if (opcode == "sub") SubInstruction(program_lines[current_line]);else if (opcode == "inc") IncInstruction(program_lines[current_line]);else if (opcode == "hlt") break;if (opcode == "jmp" || opcode == "je" || opcode == "jne") {current_line = new_current_line;}else {current_line++;}new_current_line = current_line + 1; }if (graphicsInitialized) {_getch();//closegraph();}settextcolor(LIGHTGRAY);outtextxy(100, 200, "虚拟机已安全停止");outtextxy(100, 240, "点击任意键返回管理器");FlushMouseMsgBuffer();while (!MouseHit()) Sleep(100);closegraph();
}int main() {if (GetSystemMetrics(SM_CLEANBOOT) != 0) {MessageBox(NULL, "系统处于安全模式,无法启动虚拟机", "启动错误", MB_ICONSTOP);return 1;}// 设置当前目录if (!SetCurrentDirectoryA(GetCurrentDirectoryPath().c_str())) {MessageBox(NULL, "无法设置工作目录", "路径错误", MB_ICONERROR);return 1;}// 设置当前工作目录SetCurrentDirectoryA(GetCurrentDirectoryPath().c_str());// 初始化图形界面(直接初始化)initgraph(WIDTH, HEIGHT);HWND hWnd = GetHWnd();SetWindowText(hWnd, "VMwork 虚拟机管理器");ShowWindow(hWnd, SW_SHOWNORMAL);// 检查必要组件if (!PathFileExistsA("ndisasm.exe") || !PathFileExistsA("toasm.exe")) {MessageBox(hWnd, "缺少必要组件:ndisasm.exe 或 toasm.exe", "启动错误", MB_ICONERROR);return 1;}// 加载配置try {LoadConfig();} catch (...) {ShowErrorDialog("配置文件加载失败");}// 主循环MainLoop();// 保存配置SaveConfig();closegraph();return 0;
}

这样就可以让用户选择img和asm了

为程序加一个ico

ico.rc

1 ICON "VMwork.ico" 

toasm.py

import re
import sys
import chardetdef process_files(input_file_path, output_file_path):# 探测原始文件的编码with open(input_file_path, 'rb') as f:result = chardet.detect(f.read())encoding = result['encoding']pattern = re.compile(r'^([0-9A-Fa-f]{8})\s+([0-9A-Fa-f ]+)\s+(.*)$')with open(input_file_path, 'r', encoding=encoding) as input_file, open(output_file_path, 'w',encoding='utf - 8') as output_file:for line in input_file:line = line.rstrip()if match := pattern.match(line):addr_str, _, instr = match.groups()addr = int(addr_str, 16)output_file.write(f"0x{addr:x}:\n")output_file.write(f"{instr}\n")else:output_file.write(line + "\n")if __name__ == "__main__":if len(sys.argv)!= 3:print("Usage: python script_name.py input_file output_file")sys.exit(1)input_file_path = sys.argv[1]output_file_path = sys.argv[2]process_files(input_file_path, output_file_path)

 

 

写一个makefile

all:make icomake toasmcopy .\dist\toasm.exe .\make VMwork
VMwork:g++ -o VMwork main.cpp ico.res -std=c++11 -leasyx -lcomdlg32 -lshlwapi -lmsimg32 -mwindows -static-libgcc -static-libstdc++
ico:windres -O coff -o ico.res ico.rc
toasm:Pyinstaller -F toasm.py

这样就可以编译整个项目:

make

顺带写一个README.md

###(c) 2025 Lin Honghan# VMwork Virtual Machine Manager## 📖 中文说明### 项目简介
VMwork 是一款基于Windows的虚拟机模拟器,提供图形化界面管理,支持运行汇编程序(.asm)和软盘镜像(.img)。模拟BIOS中断调用,实现基本的显示和输入功能。### 主要功能
- 🖥️ 图形界面管理虚拟机配置
- ⚙️ 支持汇编源码直接运行
- 💾 可加载/反编译.img软盘镜像
- ⌨️ 模拟BIOS键盘中断(INT 16h)
- 📺 模拟文本模式显示(INT 10h)
- 🔄 历史配置自动保存
- 🎨 彩色控制台输出支持### 运行要求
- Windows 7+ (或Linux)
- C/C++ 运行时库
- 管理员权限(部分功能需要)### 安装使用
1. 下载最新Release包
2. 解压到英文路径(避免空格)
3. 确保包含以下文件:- `VMwork.exe`- `ndisasm.exe`- `toasm.exe`
4. 双击运行`VMwork.exe`### 使用说明
1. **新建虚拟机**  - 选择.asm或.img文件- 输入虚拟机名称- 自动保存配置2. **运行虚拟机**  - 从列表选择配置- 进入全屏模拟模式- 按任意键返回管理器3. **键盘输入**  - 专用控制台窗口接收输入- 支持Shift/Caps Lock状态- Esc键退出程序### 注意事项
⚠️ 系统安全模式下不可用  
⚠️ 路径不要包含中文或空格  
⚠️ 杀毒软件可能误报(添加信任)  
⚠️ 需要保留同目录工具程序### 代码结构
main.cpp
├── GUI/ # 图形界面模块├── VM/ # 虚拟机核心│ ├── BIOS/ # 中断模拟│ └── Parser/ # 指令解析├── config/ # 配置管理└── tools/ # 反编译工具### 问题反馈
提交Issue至[项目仓库](https://github.com/linhhanpy/VMwork)  
或联系:lhh_88888888@qq.com---## 📖 English Documentation### Project Overview
VMwork is a Windows-based virtual machine emulator with GUI management. Supports running assembly programs (.asm) and floppy disk images (.img), emulating BIOS interrupts for basic display and input operations.### Key Features
- 🖥️ Graphical VM Configuration Management
- ⚙️ Direct Assembly Source Execution
- 💾 .IMG Floppy Image Loading/Disassembly
- ⌨️ BIOS Keyboard Interrupt Simulation (INT 16h)
- 📺 Text Mode Display Emulation (INT 10h)
- 🔄 Automatic Configuration Saving
- 🎨 Color Console Output Support### System Requirements
- Windows 7+ (or Linux)
- C/C++ Redistributable
- Administrator Privileges (for certain features)### Installation
1. Download latest Release package
2. Extract to English path (no spaces)
3. Verify required files:- `VMwork.exe`- `ndisasm.exe`- `toasm.exe`
4. Double-click `VMwork.exe`### Quick Start
1. **Create VM**  - Select .asm/.img file- Name your VM- Config auto-saves2. **Run VM**  - Select from config list- Enter full emulation mode- Press any key to return3. **Keyboard Input**  - Dedicated console window- Supports Shift/Caps Lock states- Esc to exit program### Important Notes
⚠️ Not compatible with Safe Mode  
⚠️ Use ASCII-only paths  
⚠️ Add exclusion in antivirus  
⚠️ Keep tool files in same directory### Code Structure
main.cpp├── GUI/ # Graphical Interface├── VM/ # VM Core│ ├── BIOS/ # Interrupt Emulation│ └── Parser/ # Instruction Parsing├── config/ # Configuration└── tools/ # Disassembly Tools

放几张效果图

 

 

 

此时的目录

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/11748.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tomcat核心组件及原理概述

目录 1. tomcat概述 1.1 概念 1.2 官网地址 2. 基本使用 2.1下载 3. 整体架构 3.1 核心组件 3.2 从web.xml配置和模块对应角度 3.3 如何处理请求 4. 配置JVM参数 5. 附录 1. tomcat概述 1.1 概念 什么是tomcat Tomcat是一个开源、免费、轻量级的Web服务器。 Tomca…

docker gitlab arm64 版本安装部署

前言&#xff1a; 使用RK3588 部署gitlab 平台作为个人或小型团队办公代码版本使用 1. docker 安装 sudo apt install docker* 2. 获取arm版本的gitlab GitHub - zengxs/gitlab-arm64: GitLab docker image (CE & EE) for arm64 git clone https://github.com/zengxs…

LabVIEW在电机自动化生产线中的实时数据采集与生产过程监控

在电机自动化生产线中&#xff0c;实时数据采集与生产过程监控是确保生产效率和产品质量的重要环节。LabVIEW作为一种强大的图形化编程平台&#xff0c;可以有效实现数据采集、实时监控和自动化控制。详细探讨如何利用LabVIEW实现这一目标&#xff0c;包括硬件选择、软件架构设…

python算法和数据结构刷题[1]:数组、矩阵、字符串

一画图二伪代码三写代码 LeetCode必刷100题&#xff1a;一份来自面试官的算法地图&#xff08;题解持续更新中&#xff09;-CSDN博客 算法通关手册&#xff08;LeetCode&#xff09; | 算法通关手册&#xff08;LeetCode&#xff09; (itcharge.cn) 面试经典 150 题 - 学习计…

【PyTorch】7.自动微分模块:开启神经网络 “进化之门” 的魔法钥匙

目录 1. 梯度基本计算 2. 控制梯度计算 3. 梯度计算注意 4. 小节 个人主页&#xff1a;Icomi 专栏地址&#xff1a;PyTorch入门 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&#xff0c;为构建和训练神经网络提供了高效且灵活…

C++基础day1

前言&#xff1a;谢谢阿秀&#xff0c;指路阿秀的学习笔记 一、基础语法 1.构造和析构: 类的构造函数是一种特殊的函数&#xff0c;在创建一个新的对象时调用。类的析构函数也是一种特殊的函数&#xff0c;在删除所创建的对象时调用。 构造顺序&#xff1a;父类->子类 析…

深入理解linux中的文件(上)

1.前置知识&#xff1a; &#xff08;1&#xff09;文章 内容 属性 &#xff08;2&#xff09;访问文件之前&#xff0c;都必须打开它&#xff08;打开文件&#xff0c;等价于把文件加载到内存中&#xff09; 如果不打开文件&#xff0c;文件就在磁盘中 &#xff08;3&am…

Spring Boot Web项目全解析:从前端请求到后端处理

第一章&#xff1a;对静态资源的整合 ConfigurationProperties(prefix"spring.resources", ignoreUnknownFieldsfalse)public class ResourceProperties implements ResourceLoaderAware {//可以设置和静态资源有关的参数&#xff0c;缓存时间等WebMvcAuotConfigura…

Java创建对象有几种方式?

大家好&#xff0c;我是锋哥。今天分享关于【Java创建对象有几种方式?】面试题。希望对大家有帮助&#xff1b; Java创建对象有几种方式? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Java 中&#xff0c;创建对象有几种常见的方式&#xff0c;具体如下&…

基于Spring Security 6的OAuth2 系列之八 - 授权服务器--Spring Authrization Server的基本原理

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…

深入浅出并查集(不相交集合实现思路)

引言 并查集&#xff08;Disjoint Set Union&#xff0c;简称DSU&#xff09;是一种用于处理一些不交集的合并及查询问题。它主要支持两种操作&#xff1a;查找&#xff08;Find&#xff09;和合并&#xff08;Union&#xff09;。 查找&#xff1a;确定某个元素属于哪一个子…

如何运行Composer安装PHP包 安装JWT库

1. 使用Composer Composer是PHP的依赖管理工具&#xff0c;它允许你轻松地安装和管理PHP包。对于JWT&#xff0c;你可以使用firebase/php-jwt这个库&#xff0c;这是由Firebase提供的官方库。 安装Composer&#xff08;如果你还没有安装的话&#xff09;&#xff1a; 访问Co…

享元模式——C++实现

目录 1. 享元模式简介 2. 代码示例 1. 享元模式简介 享元模式是一种结构型模式。 享元模式用于缓存共享对象&#xff0c;降低内存消耗。共享对象相同的部分&#xff0c;避免创建大量相同的对象&#xff0c;减少内存占用。 享元模式需要将对象分成内部状态和外部状态两个部分…

网络原理(4)—— 网络层详解

目录 一. IP协议报头结构 二. 地址管理 2.1 路由器 2.1.1 路由选择 2.1.2 WAN口&#xff08;Wide Area Network&#xff09; 2.1.3 LAN口&#xff08;Local Area Network&#xff09; 2.1.4 WLAN口&#xff08;Wireless Local Area Network&#xff09; 2.2 网段划分…

基于深度学习的输电线路缺陷检测算法研究(论文+源码)

输电线路关键部件的缺陷检测对于电网安全运行至关重要&#xff0c;传统方法存在效率低、准确性不高等问题。本研究探讨了利用深度学习技术进行输电线路关键组件的缺陷检测&#xff0c;目的是提升检测的效率与准确度。选用了YOLOv8模型作为基础&#xff0c;并通过加入CA注意力机…

Android --- handler详解

handler 理解 handler 是一套Android 消息传递机制&#xff0c;主要用于线程间通信。 tips&#xff1a; binder/socket 用于进程间通信。 参考&#xff1a; Android 进程间通信-CSDN博客 handler 就是主线程在起了一个子线程&#xff0c;子线程运行并生成message &#xff0c;l…

vim如何解决‘’文件非法关闭后,遗留交换文件‘’的问题

过程描述&#xff1a; 由于我修改文件时&#xff08;一定得修改了文件&#xff0c;不做任何修改不会产生这个问题&#xff09;的非法关闭&#xff0c;比如直接关闭虚拟机&#xff0c;或者直接断开远程工具的远程连接&#xff0c;产生了以下遗留交换文件的问题&#xff1a; 点击…

六十分之三十七——一转眼、时光飞逝

一、目标 明确可落地&#xff0c;对于自身执行完成需要一定的努力才可以完成的 1.第三版分组、激励、立体化权限、智能设备、AIPPT做课 2.8本书 3.得到&#xff1a;头条、吴军来信2、卓克科技参考3 4.总结思考 二、计划 科学规律的&#xff0c;要结合番茄工作法、快速阅读、…

Linux环境下的Java项目部署技巧:安装 Mysql

查看 myslq 是否安装&#xff1a; rpm -qa|grep mysql 如果已经安装&#xff0c;可执行命令来删除软件包&#xff1a; rpm -e --nodeps 包名 下载 repo 源&#xff1a; http://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm 执行命令安装 rpm 源(根据下载的…

css三角图标

案例三角&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><s…