(写于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;
下面汇编代码是以以显示 320*200 的 256 色位图为例:
;------------------------------------------------------------------------------------------
; 数据段定义
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
在dosbox中的显示效果:
(由于将图片处理为256色,相对原图可能有些失真)