使用16位汇编显示bmp

(写于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色,相对原图可能有些失真)


你可能感兴趣的:(汇编,图片,bmp)