C++内嵌汇编图形模式代码适配与技术咨询(Win11/VS2022)
DOS图形模式代码在现代Windows环境的问题解答
1. 如何让该代码正常运行?
你提供的代码是DOS实模式下的16位VGA图形代码,现代Windows 11运行在保护模式,完全不支持直接调用BIOS中断(int 0x10)和直接访问物理显存地址(0xA0000000),直接在VS2022里编译运行必然崩溃。可行方案有两个:
- 用DOSBox模拟器:把代码编译成16位DOS可执行程序(可使用Turbo C++ 3.0、OpenWatcom等老式编译器),在DOSBox中模拟DOS实环境即可正常执行。
- 重构代码改用现代Windows图形API(对应第三个问题的方案),放弃老式BIOS/显存直接操作逻辑。
2. 该代码在处理器指令层面的执行逻辑是什么?
这段代码完全依赖DOS实模式的硬件特性,指令逻辑如下:
SetGr函数:asm mov AX, 0x13:将寄存器AX设为0x13,其中AH=0(BIOS显示服务的"设置显示模式"功能号),AL=0x13(对应VGA 320x200分辨率、256色的图形模式)。asm int 0x10:触发BIOS视频服务中断,调用BIOS内置驱动切换到指定图形模式。
CloseGr函数:asm mov AX, 3:AX=0x0003,AH=0,AL=3(对应标准80x25文本模式)。asm int 0x10:再次调用BIOS视频中断,切换回文本模式。
PutPixel函数:
实模式下VGA 256色图形模式的显存物理起始地址是0xA0000(段地址0xA000+偏移0),代码里的0xA0000000是错误的(混淆了保护模式线性地址)。正确计算应为0xA0000 + y*320 + x,通过指针直接给显存地址写入颜色值,就能在屏幕对应坐标画出像素——因为实模式下程序可直接访问物理内存。
3. 不依赖第三方库的简便逐像素绘制方法
在现代Windows环境下,用Win32 API就能实现无第三方库的逐像素绘制,推荐两种方式:
方式1:用SetPixel直接绘制(适合入门学习)
这是最直观的方式,通过窗口的设备上下文(HDC)调用系统API画像素,核心示例代码如下:
#include <windows.h> LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { const wchar_t CLASS_NAME[] = L"GraphicsWindowClass"; WNDCLASS wc = {}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Pixel Drawing", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, nullptr, nullptr, hInstance, nullptr); if (hwnd == nullptr) return 0; ShowWindow(hwnd, nCmdShow); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); // 在(100,100)绘制红色像素 SetPixel(hdc, 100, 100, RGB(255,0,0)); // 循环绘制一条绿色直线 for(int x=0;x<200;x++) SetPixel(hdc, x, 150, RGB(0,255,0)); EndPaint(hwnd, &ps); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } }
方式2:用DIB位图(效率更高,适合大量像素绘制)
如果需要绘制大量像素,SetPixel效率较低,可以创建DIB位图,直接操作内存中的像素数据再绘制到窗口:
// 在WM_PAINT消息处理中替换为以下代码 PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); BITMAPINFO bmi = {}; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = 320; bmi.bmiHeader.biHeight = -200; // 负数值表示从上到下的像素顺序 bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 8; // 256色,和原VGA模式一致 bmi.bmiHeader.biCompression = BI_RGB; unsigned char* pixels; HBITMAP hbmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)&pixels, nullptr, 0); // 填充整个屏幕为蓝色(0x1F是VGA蓝色调色板索引,对应RGB(0,0,255)) memset(pixels, 0x1F, 320*200); // 将DIB绘制到窗口 HDC memdc = CreateCompatibleDC(hdc); HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbmp); BitBlt(hdc, 0, 0, 320, 200, memdc, 0, 0, SRCCOPY); SelectObject(memdc, oldbmp); DeleteDC(memdc); DeleteObject(hbmp); EndPaint(hwnd, &ps);
内容的提问来源于stack exchange,提问作者boozda
相关产品推荐
相关产品推荐

