首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

DOS 環境下 8 位 256 色 BMP 顯示:VGA 模式程式設計完整實作與檔案格式深度解析

     編輯:關於C

在 DOS 時代,直接操作 VGA 顯示記憶體(0xA0000)是圖形程式設計的核心技能。本文提供完整的 C 語言實作,展示如何在 320×200、256 色模式(Mode 0x13)下載入並顯示未壓縮的 8 位 BMP 檔案,並深度解析 BMP 檔案格式結構。

核心程式碼實作

關鍵常數與資料結構


#define VGA256       0x13    // 320x200 256色模式
#define TEXT_MODE    0x03    // 80x25 文字模式
#define SCREEN_WIDTH  320
#define SCREEN_HEIGHT 200

// VGA 調色板埠
#define PALETTE_MASK        0x3C6
#define PALETTE_REGISTER_RD 0x3C7
#define PALETTE_REGISTER_WR 0x3C8
#define PALETTE_DATA        0x3C9

// 顯示緩衝區基址
unsigned char far *video_buffer = (char far *)0xA0000000L;

// BMP 檔案頭 (14 bytes)
typedef struct BMP_file {
    unsigned int   bfType;      // 固定 0x4D42 ('BM')
    unsigned long  bfSize;      // 檔案大小
    unsigned int   Reserved1;   // 保留 0
    unsigned int   Reserved2;   // 保留 0
    unsigned long  bfOffset;    // 資料區偏移
} bitmapfile;

// BMP 資訊頭 (40 bytes)
typedef struct BMP_info {
    unsigned long biSize;           // 資訊頭大小 (40)
    unsigned long biWidth;          // 圖寬
    unsigned long biHeight;         // 圖高
    unsigned int  biPlanes;         // 色板數 (1)
    unsigned int  biBitCount;       // 色深 (1/4/8/24/32)
    unsigned long biCompression;    // 壓縮類型 (0=無壓縮)
    unsigned long biSizeImage;      // 影像資料大小
    unsigned long biXPelsPerMeter;  // 水平解析度
    unsigned long biYPelsPerMeter;  // 垂直解析度
    unsigned long biClrUsed;        // 實際使用顏色數
    unsigned long biClrImportant;   // 重要顏色數
} bitmapinfo;

// 調色板項目 (4 bytes)
typedef struct RGB_BMP_typ {
    unsigned char blue;
    unsigned char green;
    unsigned char red;
    unsigned char reserved;
} RGB_BMP, *RGB_BMP_ptr;

// 完整 BMP 非資料區結構
typedef struct bmp_picture_typ {
    bitmapfile file;
    bitmapinfo info;
    RGB_BMP    palette[256];
} bmp_picture, *bmp_picture_ptr;

調色板設定與 BMP 驗證


// 設定單一調色板暫存器
void Set_BMP_Palette_Register(int index, RGB_BMP_ptr color) {
    outp(PALETTE_MASK, 0xFF);
    outp(PALETTE_REGISTER_WR, index);
    outp(PALETTE_DATA, color->red   >> 2);  // 8-bit → 6-bit (VGA DAC)
    outp(PALETTE_DATA, color->green >> 2);
    outp(PALETTE_DATA, color->blue  >> 2);
}

// 驗證 BMP 格式
void Check_Bmp(bmp_picture_ptr bmp_ptr) {
    if (bmp_ptr->file.bfType != 0x4D42) { printf("Not a BMP file!\n"); exit(1); }
    if (bmp_ptr->info.biCompression != 0) { printf("Can not display compressed BMP!\n"); exit(1); }
    if (bmp_ptr->info.biBitCount != 8) { printf("Not a 256 color BMP file!\n"); exit(1); }
}

BMP 載入與顯示主流程


void BMP_Load_Screen(char *bmp) {
    int fp, i, j, n_bytes;
    bmp_picture bmp256;
    unsigned char line_buf[SCREEN_WIDTH];
    unsigned char far *line_ptr;

    if ((fp = open(bmp, O_RDONLY)) == -1) { printf("Can not open file:%s", bmp); exit(1); }

    read(fp, &bmp256.file, sizeof(bitmapfile));
    read(fp, &bmp256.info, sizeof(bitmapinfo));
    Check_Bmp(&bmp256);

    // 讀取調色板 (256 × 4 bytes)
    for (i = 0; i < 256; i++) {
        read(fp, &bmp256.palette[i].blue,     1);
        read(fp, &bmp256.palette[i].green,   1);
        read(fp, &bmp256.palette[i].red,     1);
        read(fp, &bmp256.palette[i].reserved, 1);
    }
    for (i = 0; i < 256; i++)
        Set_BMP_Palette_Register(i, &bmp256.palette[i]);

    // 顯示位圖:BMP 掃描行由下而上,VGA 由上而下
    for (i = SCREEN_HEIGHT - 1; i >= 0; i--) {
        lseek(fp, -(long)(SCREEN_HEIGHT - i) * SCREEN_WIDTH, SEEK_END);
        n_bytes = read(fp, line_buf, SCREEN_WIDTH);
        line_ptr = video_buffer + (SCREEN_HEIGHT - i - 1) * SCREEN_WIDTH;
        for (j = 0; j < n_bytes; j++)
            line_ptr[j] = line_buf[j];
    }
    close(fp);
}

視訊模式切換與主程式


void Set_Video_Mode(int mode) {
    union REGS inregs, outregs;
    inregs.h.ah = 0;
    inregs.h.al = (unsigned char)mode;
    int86(0x10, &inregs, &outregs);  // BIOS 影片中斷
}

void main() {
    Set_Video_Mode(VGA256);
    BMP_Load_Screen("256.bmp");
    getch();  // 等待按鍵
    Set_Video_Mode(TEXT_MODE);  // 恢復文字模式
}

BMP 檔案格式深度解析

1. 檔案頭 BITMAPFILEHEADER (14 bytes)

欄位大小說明
bfTypeWORD (2)固定 'BM' (0x4D42),小端序存儲為 42 4D
bfSizeDWORD (4)整個檔案大小 (bytes)
bfReserved1/2WORD (2+2)保留,必為 0
bfOffBitsDWORD (4)像素資料相對檔案開頭的偏移量

2. 資訊頭 BITMAPINFOHEADER (40 bytes)

欄位關鍵值說明
biSize40資訊頭大小
biWidth/biHeight像素圖片寬高,高度為正表示底部在前 (BMP 特有)
biPlanes1固定為 1
biBitCount1/4/8/24/32色深:8=256色,24=真彩色
biCompression0/1/20=無壓縮,1=RLE8,2=RLE4
biClrUsed0/2^n0 表示使用完整調色板 (2^biBitCount)

3. 調色板 RGBQUAD (僅 biBitCount ≤ 8 時存在)

  • 每項 4 bytes:Blue, Green, Red, Reserved(0)
  • 項目數:biBitCount=1→2, 4→16, 8→256
  • 24/32 位圖無調色板,像素資料直接儲存 RGB 值

4. 像素資料儲存規則

  • 掃描順序:從左下角開始,逐行向上 (BMP 特有,與螢幕座標相反)
  • 位元組對齊:每掃描行大小必須為 4 的倍數,不足補 0
  • 計算公式:DataSizePerLine = ((biWidth * biBitCount + 31) / 32) * 4
  • 總資料大小:DataSize = DataSizePerLine * biHeight

8 位圖像素存取範例

每像素 1 byte,直接作為調色板索引。讀取索引 4 → 調色板第 5 項 → RGB 值。

歷史技術背景與現代啟示

此程式展示了 DOS 時代「直擊硬體」的程式設計風格:直接操作記憶體映射 I/O、BIOS 中斷、連接埠 I/O。雖然現代作業系統禁止使用者程式直接存取硬體,但理解這些底層機制有助於:

  • 嵌入式系統裸機程式開發
  • 圖形驅動程式與 GPU 程式設計基礎
  • 檔案格式解析器開發 (圖片載入庫)
  • 效能關鍵路徑的記憶體佈局優化

BMP 格式雖老舊,但其無壓縮、結構簡單的特性,使其成為教學、嵌入式圖形、簡易圖片處理的首選格式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved