自己免费网站建设/网站访问量统计工具
开源地址: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
放几张效果图
此时的目录
相关文章:

自制虚拟机(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> #includ…...

算法题(56):旋转链表
审题: 我们需要根据k的大小把链表向右移动对应次数,并返回移动后的链表的头结点指针 思路: 根据提示中的数据大小我们发现:k的值可以远大于节点数。 也就是说我们对链表的操作存在周期,如果k%len0,说明我们…...

解决PyG安装中torch-sparse安装失败问题:详细指南
1 问题描述 最近在学习GNN,需要使用PyTorch Geometric(PyG)库。在安装PyG的过程中,遇到了torch-sparse安装失败的问题,错误提示为: ERROR: Failed building wheel for torch-sparse本文将详细记录问题的解…...

如何创建折叠式Title
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容,本章回中将介绍SliverAppBar组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似,它们的…...

go-zero学习笔记(三)
利用goctl生成rpc服务 编写proto文件 // 声明 proto 使用的语法版本 syntax "proto3";// proto 包名 package demoRpc;// golang 包名(可选) option go_package "./demo";// 如需为 .proto 文件添加注释,请使用 C/C 样式的 // 和 /* ... */…...

Wildcard工具详解:从入门到精通
1. Wildcard基础知识 什么是Wildcard? Wildcard(通配符)是一种用于匹配文件名或字符串的特殊字符。它允许用户使用简单的符号来表示复杂的匹配规则,从而快速定位目标文件或数据。 常见的Wildcard符号 *:匹配任意数量…...

冰蝎v3.0 beta7来啦
我用了一台kali,一台centos,一台windows,做了一个文件上传和一个反弹shell实验,载荷是AES加密的,终于感受到了对加密流量的无可奈何~ kali(php8.1)centos(php7.1)window…...

React中使用箭头函数定义事件处理程序
React中使用箭头函数定义事件处理程序 为什么使用箭头函数?1. 传递动态参数2. 避免闭包问题3. 确保每个方块的事件处理程序是独立的4. 代码可读性和维护性 示例代码总结 在React开发中,处理事件是一个常见的任务。特别是当我们需要传递动态参数时&#x…...

记忆化搜索和动态规划 --最长回文子串为例
记忆化搜索 记忆化搜索是一种优化递归算法的方法,通过将已经计算过的子问题的结果存储起来(通常使用哈希表或数组),避免重复计算相同的子问题。 本质上是通过缓存中间结果来减少计算的重复性。 动态规划 动态规划是通过将问题分…...

Tree Compass( Codeforces Round 934 (Div. 2) )
Tree Compass( Codeforces Round 934 (Div. 2) ) You are given a tree with n n n vertices numbered 1 , 2 , … , n 1, 2, \ldots, n 1,2,…,n. Initially, all vertices are colored white. You can perform the following two-step operation: …...

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.17 掩码数组:缺失值处理的优雅方案
2.17 掩码数组:缺失值处理的优雅方案 目录 #mermaid-svg-12vjJJbyudPnkYBO {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-12vjJJbyudPnkYBO .error-icon{fill:#552222;}#mermaid-svg-12vjJJbyudPnkYBO…...

PHP 常用函数2025.02
PHP implode() 函数 语法 implode(separator,array) 参数描述separator可选。规定数组元素之间放置的内容。默认是 ""(空字符串)。array必需。要组合为字符串的数组。 技术细节 返回值:返回一个由数组元素组合成的字符串。PHP 版…...

react中如何获取dom元素
实现代码 const inputRef useRef(null) inputRef.current.focus()...

【C++】继承(下)
大家好,我是苏貝,本篇博客带大家了解C的继承(下),如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 5.继承与友元6.继承与静态成员7.复杂的菱形继承及菱形虚拟继承8.继…...

C语言实现字符串排序:从代码到原理深度解析
在编程的世界里,字符串处理是一项基础且重要的技能。今天,我们通过分析一段C语言代码来深入了解如何对字符串进行排序。 一、代码呈现 #include <stdio.h> #include <string.h> int main() { char s[1001]; scanf("%s", s); int…...

Vue3的el-table-column下拉输入实时查询API数据选择的实现方法
由于本人对el-table-column有下拉输入选择的要求,根据网上搜索的资料及本人优化,推出我比较满意的方法,供各位读者参考使用。 效果图 el-table-column写法 <el-table-columnlabel"货品编号"align"center"prop"…...

【数据结构】_链表经典算法OJ:复杂链表的复制
目录 1. 题目链接及描述 2. 解题思路 3. 程序 1. 题目链接及描述 题目链接:138. 随机链表的复制 - 力扣(LeetCode) 题目描述: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,…...

Vue 图片引用方式详解:静态资源与动态路径访问
目录 前言1. 引用 public/ 目录2. assets/ 目录3. 远程服务器4. Vue Router 动态访问5. 总结6. 扩展(图片不显示) 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 在 Vue 开发中&#x…...

chatGPT写的网页版贪吃蛇小游戏
chatGPT写的网页版贪吃蛇小游戏 前言网页版贪吃蛇小游戏 前言 之前无聊,让ChatGPT写了一段基于html语言的贪吃蛇小游戏代码 网页版贪吃蛇小游戏 将以下内容复制到记事本,重命名为xxx.html即可打开浏览器游玩 这里是一个使用HTML、CSS和JavaScript编写…...

Python量化交易助手:xtquant的安装与应用
Python量化交易助手:xtquant的安装与应用 技术背景和应用场景 在量化交易领域,Python因其强大的库支持和灵活性成为了许多开发者的首选语言。其中,xtquant 是迅投官方开发的一个Python包,专门用于与miniqmt通信,实现…...

前缀和算法
文章目录 算法总览题目1371.每个元音包含偶数次的最长子字符串 算法总览 题目 1371.每个元音包含偶数次的最长子字符串 1371.每个元音包含偶数次的最长子字符串 参考博主的讲解 思路分析:就是得使用前缀和记录情况,dp[i][j]表示s[0] 到s[i] 中&…...

Qt常用控件 输入类控件
文章目录 1.QLineEdit1.1 常用属性1.2 常用信号1.3 例子1,录入用户信息1.4 例子2,正则验证手机号1.5 例子3,验证输入的密码1.6 例子4,显示密码 2. QTextEdit2.1 常用属性2.2 常用信号2.3 例子1,获取输入框的内容2.4 例…...

《最小阻力之路》关于愿景的理解和思考
一、愿景的形成机制 1. 愿景的三层来源 来源层级形成机制案例潜在偏差风险① 本能冲动层对快感/痛苦的即时反应"想暴富"源于缺钱焦虑易被短期情绪劫持② 社会镜像层内化外界标准(家庭/社会/文化)"必须考研"因家人期待混淆他人需求…...

Ubuntu 22.04系统安装部署Kubernetes v1.29.13集群
Ubuntu 22.04系统安装部署Kubernetes v1.29.13集群 简介Kubernetes 的工作流程概述Kubernetes v1.29.13 版本Ubuntu 22.04 系统安装部署 Kubernetes v1.29.13 集群 1 环境准备1.1 集群IP规划1.2 初始化步骤(各个节点都需执行)1.2.1 主机名与IP地址解析1.…...

虚幻基础17:动画层接口
能帮到你的话,就给个赞吧 😘 文章目录 animation layer interface animation layer interface 动画层接口:动画图表的集。仅有名字。 添加到动画蓝图中,由动画蓝图实现动画图表。...

无人机PX4飞控 | PX4源码添加自定义uORB消息并保存到日志
PX4源码添加自定义uORB消息并保存到日志 0 前言 PX4的内部通信机制主要依赖于uORB(Micro Object Request Broker),这是一种跨进程的通信机制,一种轻量级的中间件,用于在PX4飞控系统的各个模块之间进行高效的数据交换…...

HTMLCSS :下雪了
这段代码创建了一个动态的雪花飘落加载动画,通过 CSS 技术实现了雪花的下落和消失效果,为页面添加了视觉吸引力和动态感。 大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能…...

如何处理 Typecho Joe 主题被抄袭或盗版的问题
在开源社区中,版权保护是一个非常重要的话题。如果你发现自己的主题(如 Joe 主题)被其他主题(如子比主题)抄袭或盗版,你可以采取以下措施来维护自己的权益。 一、确认侵权行为 在采取任何行动之前…...

利用Vue和javascript分别编写一个“Hello World”的定时更新
目录 一、利用Vue编写一个“Hello World”的定时更新(1)vue编码在Html文件中(2)vue编码在js文件中 二、利用javascript编写一个“Hello World”的定时更新 一、利用Vue编写一个“Hello World”的定时更新 (1ÿ…...

volatile变量需要减少读取次数吗
问题说明 本人在前期读Netty源码时看到这样一段源码和注释: private boolean invokeHandler() {// Store in local variable to reduce volatile reads.int handlerState this.handlerState;return handlerState ADD_COMPLETE || (!ordered && handlerS…...