自制虚拟机(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.mzph.cn/bicheng/69999.shtml

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

相关文章

【游戏设计原理】97 - 空间感知

一、游戏空间的类型 将游戏设计中的空间设计单独提取出来&#xff0c;可以根据其结构、功能和玩家的交互方式划分为以下几种主要类型。这些类型可以单独存在&#xff0c;也可以组合使用&#xff0c;以创造更加复杂和有趣的游戏体验。 1. 线性空间 定义&#xff1a;空间设计是…

基于开源AI智能名片2 + 1链动模式S2B2C商城小程序视角下的个人IP人设构建研究

摘要&#xff1a;本文深入探讨在开源AI智能名片2 1链动模式S2B2C商城小程序的应用场景下&#xff0c;个人IP人设构建的理论与实践。通过剖析个人IP人设定义中的“诉求”“特质”“可感知”三要素&#xff0c;结合该小程序特点&#xff0c;阐述其对个人IP打造的影响与推动作用&…

数据库和数据表的创建、修改、与删除

1.标识符命名规则 数据库名、表名不得超过30个字符&#xff0c;变量名限制为29个 必须只能包含A-Z,a-z,0-9,_共63个字符 数据库名、表名、字段名等对象名中间不能包含空格 同一个MySQL软件中&#xff0c;数据库不能同名&#xff1b;同一个库中&#xff0c;表不能重名&#…

算法日记10:SC62求和(单调栈)(共享求解)

一、题目 二、题解&#xff1a; 1、首先&#xff0c;我们看到题目的第一个想法&#xff0c;就是把样例答案如何求解给列出来&#xff0c;图例如下 2、通过分析样例&#xff0c;可以很清晰的发现每一个数字都有其管辖的区间&#xff0c;因此我们可以想到能否找到一个数字它所管…

Revit二次开发 自适应族添加放样融合

大多数博客给出的方案都是如何在有自适应族的情况下进行修改定位点或是将数据传入自适应族,如何直接在族文件中创建自适应模型并将点转换为自适应点,连接自适应点成为自适应路径这种方式没有文章介绍. 下面的代码中给出了如何在自适应族文件中创建参照点并转换为自适应点连接…

基于VMware的ubuntu与vscode建立ssh连接

1.首先安装openssh服务 sudo apt update sudo apt install openssh-server -y 2.启动并检查ssh服务状态 到这里可以按q退出 之后输入命令 &#xff1a; ip a 红色挡住的部分就是我们要的地址&#xff0c;这里就不展示了哈 3.配置vscode 打开vscode 搜索并安装&#xff1a;…

解决SetWindowCompositionAttribute使控件文本透明的问题

用以下参数调用该API&#xff0c;能实现类似Aero的模糊透明效果。 参数具体含义见 https://zhuanlan.zhihu.com/p/569258181 http://www.memotech.de/WindowComposition/Text.txt http://www.memotech.de/WindowComposition/WindowComposition.zip DWORD accent[4] { 3,0,0,0 …

使用 DeepSeek-R1 与 AnythingLLM 搭建本地知识库

一、下载地址Download Ollama on macOS 官方网站&#xff1a;Ollama 官方模型库&#xff1a;library 二、模型库搜索 deepseek r1 deepseek-r1:1.5b 私有化部署deepseek&#xff0c;模型库搜索 deepseek r1 运行cmd复制命令&#xff1a;ollama run deepseek-r1:1.5b 私有化…

SQL入门到精通 理论+实战 -- 在 MySQL 中学习SQL语言

目录 一、环境准备 1、MySQL 8.0 和 Navicat 下载安装 2、准备好的表和数据文件&#xff1a; 二、SQL语言简述 1、数据库基础概念 2、什么是SQL 3、SQL的分类 4、SQL通用语法 三、DDL&#xff08;Data Definition Language&#xff09;&#xff1a;数据定义语言 1、操…

Java篇之继承

目录 一. 继承 1. 为什么需要继承 2. 继承的概念 3. 继承的语法 4. 访问父类成员 4.1 子类中访问父类的成员变量 4.2 子类中访问父类的成员方法 5. super关键字 6. super和this关键字 7. 子类构造方法 8. 代码块的执行顺序 9. protected访问修饰限定符 10. 继承方式…

使用SpringBoot发送邮件|解决了部署时连接超时的bug|网易163|2025

使用SpringBoot发送邮件 文章目录 使用SpringBoot发送邮件1. 获取网易邮箱服务的授权码2. 初始化项目maven部分web部分 3. 发送邮件填写配置EmailSendService [已解决]部署时连接超时附&#xff1a;Docker脚本Dockerfile创建镜像启动容器 1. 获取网易邮箱服务的授权码 温馨提示…

TensorFlow简单的线性回归任务

如何使用 TensorFlow 和 Keras 创建、训练并进行预测 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 8.完整代码 1. 数据准备与预处理 我们将使用一个简单的线性回归问题&#xff0c;其中输入特征 x 和标…

vue入门到实战 三

目录 3.1 v-bind 3.1.1 v-bind指令用法 ​编辑3.1.2 使用v-bind绑定class 3.1.3 使用v-bind绑定style 3.2.1 v-if指令 3.2.1 v-if指令 3.2.2 v-show指令 ​3.3 列表渲染指令v-for 3.3.1 基本用法 3.3.2 数组更新 3.3.3 过滤与排序 3.4 事件处理 3.4.1 使用v-on指令…

《苍穹外卖》项目学习记录-Day10订单状态定时处理

利用Cron表达式生成器生成Cron表达式 1.处理超时订单 查询订单表把超时的订单查询出来&#xff0c;也就是订单的状态为待付款&#xff0c;下单的时间已经超过了15分钟。 //select * from orders where status ? and order_time < (当前时间 - 15分钟) 遍历集合把数据库…

解决MacOS安装软件时提示“打不开xxx软件,因为Apple无法检查其是否包含恶意软件”的问题

macOS 系统中如何开启“任何来源”以解决安装报错问题&#xff1f; 大家好&#xff01;今天我们来聊聊在使用 macOS 系统 时&#xff0c;遇到安装应用软件时出现报错的情况。这种情况常常发生在安装一些来自第三方开发者的应用时&#xff0c;因为 macOS 会默认阻止不明开发者的…

【FreeRTOS 教程 六】二进制信号量与计数信号量

目录 一、FreeRTOS 二进制信号量&#xff1a; &#xff08;1&#xff09;二进制信号量作用&#xff1a; &#xff08;2&#xff09;二进制信号量与互斥锁的区别&#xff1a; &#xff08;3&#xff09;信号量阻塞时间&#xff1a; &#xff08;4&#xff09;信号量的获取与…

C++模板编程——可变参函数模板之折叠表达式

目录 1. 什么是折叠表达式 2. 一元左折 3. 一元右折 4. 二元左折 5. 二元右折 6. 后记 上一节主要讲解了可变参函数模板和参数包展开&#xff0c;这一节主要讲一下折叠表达式。 1. 什么是折叠表达式 折叠表达式是C17中引入的概念&#xff0c;引入折叠表达式的目的是为了…

DeepSeek回答禅宗三重境界重构交易认知

人都是活在各自心境里&#xff0c;有些话通过语言去交流&#xff0c;还是要回归自己心境内在的&#xff0c;而不是靠外在映射到股票和技术方法&#xff1b;比如说明天市场阶段是不修复不接力节点&#xff0c;这就是最高视角看整个市场&#xff0c;还有哪一句话能概括&#xff1…

数据结构【链栈】

基于 C 实现链表栈&#xff1a;原理、代码与应用 一、引言 栈就是一个容器&#xff0c;可以当场一个盒子&#xff0c;只能一个一个拿&#xff0c;一个一个放&#xff0c;而且是从上面放入。 有序顺序栈操作比较容易【会了链栈之后顺序栈自然明白】&#xff0c;所以我们这里只…

成绩案例demo

本案例较为简单&#xff0c;用到的知识有 v-model、v-if、v-else、指令修饰符.prevent .number .trim等、computed计算属性、toFixed方法、reduce数组方法。 涉及的功能需求有&#xff1a;渲染、添加、删除、修改、统计总分&#xff0c;求平均分等。 需求效果如下&#xff1a…