(写于November 18th, 2013)
因为比赛要求使用16位汇编写一个游戏,开始设想如果可以显示图片的话会更加好。
寻找了各种办法终于实现了显示256色的位图,但是防止数据段超过64k最终还是没有使用。还是写下来留作纪念吧。
首先我们需要了解清楚bmp文件的结构,下面是关于bmp(位图)结构体的介绍:
1. BMP文件组成
BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
2. BMP文件头(14字节)
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
其结构定义如下:
typedef struct tagBITMAPFILEHEADER { WORD bfType; // 位图文件的类型,必须为BM(1-2字节) DWORD bfSize; // 位图文件的大小,以字节为单位(3-6字节) WORD bfReserved1; // 位图文件保留字,必须为0(7-8字节) WORD bfReserved2; // 位图文件保留字,必须为0(9-10字节) DWORD bfOffBits; // 位图数据的起始位置,以相对于位图(11-14字节) // 文件头的偏移量表示,以字节为单位 } BITMAPFILEHEADER;
3. 位图信息头(40字节)
BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; // 本结构所占用字节数(15-18字节) LONG biWidth; // 位图的宽度,以像素为单位(19-22字节) LONG biHeight; // 位图的高度,以像素为单位(23-26字节) WORD biPlanes; // 目标设备的级别,必须为1(27-28字节) WORD biBitCount;// 每个像素所需的位数,必须是1(双色),(29-30字节) // 4(16色),8(256色)16(高彩色)或24(真彩色)之一 DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),(31-34字节) // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 DWORD biSizeImage; // 位图的大小,以字节为单位(35-38字节) LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(39-42字节) LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(43-46字节) DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数(47-50字节) DWORD biClrImportant;// 位图显示过程中重要的颜色数(51-54字节) } BITMAPINFOHEADER;
4. 颜色表
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
typedef struct tagRGBQUAD { BYTE rgbBlue;// 蓝色的亮度(值范围为0-255) BYTE rgbGreen; // 绿色的亮度(值范围为0-255) BYTE rgbRed; // 红色的亮度(值范围为0-255) BYTE rgbReserved;// 保留,必须为0 } RGBQUAD;
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; // 位图信息头 RGBQUAD bmiColors[1]; // 颜色表 } BITMAPINFO;
5. 位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是
4的倍数(即以long为单位),不足的以0填充,
biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
;------------------------------------------------------------------------------------------ ; 数据段定义 data segment pathname db '1.bmp', 00 ; 图片路径 x0 dw 0 y0 dw 0 handle dw ? ; 文件指针 bmpdata1 db 256*4 dup(?) ; 存放位图文件调色板 bmpdata2 db 64000 dup(0) ; 存放位图信息 bmpwidth dw ? ; 位图宽度 bmplength dw ? ; 位图长度 pelsnum dw ? ; temp num num dw ? flag dw ? ; scren set screnwidth dw 320 screnlength dw 200 data ends ;stack stack1 segment stack dw 100h dup(?) stack1 ends ; code prognam segment assume cs:prognam, ds:data, ss:stack1 main proc far ; main part of the program mov ax, data mov ds, ax call openf call getinfo call readf call dispy1 call dispy2 main endp ;--------------------------------------------------- ; 打开文件并存令handle指向文件 openf proc near lea dx, pathname ; ah = 3dh,al = 0 打开文件并读取,成功存入AX mov ah, 3dh mov al, 0 int 21h mov handle, ax ret openf endp ;-------------------------------------------------- ; 获取位图信息函数 getinfo proc near ; 移动文件指针,bx = 文件代号, cx:dx = 位移量, al = 0即从文件头绝对位移 mov ah, 42h mov al, 0 mov bx, handle mov cx, 0 mov dx, 12h ; 跳过18个字节直接指向位图的宽度信息 int 21h ; 读取文件,ds:dx = 数据缓冲区地址, bx = 文件代号, cx = 读取的字节数, ax = 0表示已到文件尾 mov ah, 3fh lea dx, bmpwidth ; 存放位图宽度 mov cx, 2 int 21h mov ah, 42h mov al, 0 mov bx, handle mov cx, 0 mov dx, 16h ; 跳过22个字节直接指向位图的长度信息 int 21h mov ah, 3fh lea dx, bmplength ; 存放位图长度 mov cx, 2 int 21h mov ax, bmpwidth mul bmplength mov pelsnum, ax ; 计算出位图大小放入pelsnum中 ret getinfo endp ;--------------------------------------------------- ; 读取位图颜色信息 readf proc near ; 跳过前54个字节进入颜色信息 mov ah, 42h mov al, 0 mov bx, handle mov cx, 0 mov dx, 36h int 21h mov ah, 3fh lea dx, bmpdata1 ; 将颜色信息放入bmpdata1 mov cx, 256*4 ; 蓝+绿+红+色彩保留(0)一共占256*4个字节 int 21h ret readf endp ;------------------------------------------------------- ;display 函数 dispy1 proc near ; 设置256色,320*200像素 mov ax, 0013h int 10h ; 设置调色板输出色彩索引号及rgb数据共写256次 mov cx, 256 lea si, bmpdata1 ; 颜色信息 p: mov dx, 3c8h ; 设定i/o端口 mov ax, cx dec ax neg ax ; 求补 add ax, 255 ; ax = ffffh(al = ffh, ah = ffh) out dx, al ; 将al中的数据传入dx指向的i/o端口中 inc dx ; bmp调色板存放格式:bgr~bgr~...(~为空00h) ; rgb/4后写入,显卡要求,rgb范围(0~63),位图中(0~255) mov al, [si+2] shr al, 1 shr al, 1 out dx, al mov al, [si+1] shr al, 1 shr al, 1 out dx, al mov al, [si] shr al, 1 shr al, 1 out dx, al add si, 4 loop p ret dispy1 endp ;-------------------------------------------------- dispy2 proc near mov bx, 0a000h ; 40k mov es, bx dp30: mov di, 0 cld ; df清零 mov cx, y0 ; cx = 0 dp00: mov ax, bmpwidth ; ax = 位图宽度 mov dx, ax and dx, 11b jz dp000 mov ax, 4 sub ax, dx add ax, bmpwidth dp000: inc cx mul cx dec cx mov bx, 0 sub bx, ax mov ax, bx mov bx, 0 sbb bx, dx mov dx, bx push cx mov cx, dx mov dx, ax mov bx, handle mov ax, 4202h int 21h mov dx, offset bmpdata2 mov cx, bmpwidth mov ah, 3fh int 21h pop cx cmp ax, bmpwidth jb dp3 mov si, offset bmpdata2 add si, x0 dp0: push di mov dx, 0 dp1: lodsb stosb inc dx cmp dx, 320 jae dp2 push dx add dx, x0 cmp dx, bmpwidth pop dx jb dp1 dp2: pop di add di, 320 inc cx push cx sub cx, y0 cmp cx, 200 pop cx jae dp3 push cx cmp cx, bmplength pop cx jb dp00 dp3: mov ah,0 int 16h cmp ax, 4800h je up cmp ax, 4b00h je left cmp ax, 4d00h je right cmp ax, 5000h je down cmp ax, 011bh je exit jmp dp3 up: mov ax, y0 sub ax, 1 jl dp3 mov y0, ax jmp dp30 down: mov ax, y0 add ax, 1 push ax add ax, 200 cmp ax, bmplength pop ax jae dp3 mov y0,ax jmp dp30 left: mov ax, x0 sub ax, 1 jl dp3 mov x0, ax jmp dp30 right: mov ax, x0 add ax, 1 push ax add ax, 320 cmp ax, bmpwidth pop ax jae dp3 mov x0, ax jmp dp30 exit: mov ax, 3 int 10h mov ax, 4c00h int 21h xp1: mov ax, bmplength mul screnwidth mov num, ax lea si, bmpdata2 mov cx, bmpwidth push cx mov ax, bmplength mov flag, ax dec flag sub num, 320 mov di, num add di, 320 add di, bmpwidth l2: sub di, 320 sub di, bmpwidth cmp flag, 0 jz l3 dec flag pop cx push cx dis: mov al, [si] mov ah, 0 stosb add si, 1 cmp cx, 1 jz l2 loop dis l3: mov ah, 0 int 16h mov ax, 3 int 10h mov ax, 4c00h int 21h dispy2 endp prognam ends end main