Search code examples
assemblycolorsx86-16bios

Returning to the normal BIOS color palette after printing a bmp in assembly


I am working on a game project in 8086 assembly (in DOSBOX).

I have a working snake game that workes on graphic mode, and a menu that works on printing BMP files to the screen.

My only problem is that in the printing process i change the color palette to the bmp color palette.

the color changing procces:

(this is a part of the code that prints a bmp to the screen)

proc ReadPalette
    ; Read BMP file color palette, 256 colors * 4 bytes (400h)
    mov ah,3fh
    mov cx,400h
    mov dx,offset Palette
    int 21h
    ret
endp ReadPalette

proc CopyPal
    ; Copy the colors palette to the video memory registers
    ; The number of the first color should be sent to port 3C8h
    ; The palette is sent to port 3C9h
    mov si,offset Palette
    mov cx,256
    mov dx,3C8h
    mov al,0
    ; Copy starting color to port 3C8h
    out dx,al
    ; Copy palette itself to port 3C9h
    inc dx
    PalLoop:
        ; Note: Colors in a BMP file are saved as BGR values rather than RGB.
        mov al,[si+2] ; Get red value.
        shr al,2 ; Max. is 255, but video palette maximal
         ; value is 63. Therefore dividing by 4.
        out dx,al ; Send it.
        mov al,[si+1] ; Get green value.
        shr al,2
        out dx,al ; Send it.
        mov al,[si] ; Get blue value.
        shr al,2
        out dx,al ; Send it.
        add si,4 ; Point to next color.
        ; (There is a null chr. after every color.)
        loop PalLoop
    ret
endp CopyPal

This code is not mine and all i know is that it changes the color palette for the whole program.

When I use int 10h to print a pixel after printing an image it still is using this color palette.

But when i print a pixel before printing any image, the pixel has the normal BIOS colors.

I would like to know how to change the color palette back to the BIOS color palette.

bmp color palette I've found:

bmp color palette I've found


Solution

  • You have to save the BIOS standard palette before changing it and then store it back when done.

    Sample code to do this may be

    ;WORD Buffer Segment
    ;WORD Buffer  Offset
    ;DF = Direction of saving
    SavePalette:
    push bp
    mov bp, sp
    
    push es
    push di
    push ax
    push dx
    push cx
    
    mov es, WORD [bp+06h]
    mov di, WORD [bp+04h]
    
    xor al, al
    mov dx, 3c7h
    out dx, al      ;Read from index 0
    
    inc dx
    inc dx
    mov cx, 300h        ;3x256 reads
    rep insb    
    
    pop cx
    pop dx
    pop ax
    pop di
    pop es
    
    pop bp
    ret 04h
    
    
    ;WORD Buffer Segment
    ;WORD Buffer  Offset
    ;DF = Direction of loading
    RestorePalette:
    push bp
    mov bp, sp
    
    push ds
    push si
    push ax
    push dx
    push cx
    
    mov ds, WORD [bp+06h]
    mov si, WORD [bp+04h]
    
    xor al, al
    mov dx, 3c8h
    out dx, al      ;Write from index 0
    
    inc dx
    mov cx, 300h        ;3x256 writes
    rep outsb       
    
    pop cx
    pop dx
    pop ax
    pop si
    pop ds
    
    pop bp
    ret 04h
    

    To use this code you need a buffer of 300h bytes, assume it is called PaletteBuffer and it is located in the DS segment.

    push ds
    push WORD PaletteBuffer
    call SavePalette
    
    ;Change palette for bitmap
    
    push ds
    push WORD PaletteBuffer
    call RestorePalette
    

    It is worth noting that changing the whole palette is slow, consider changing the code to save/restore only the colors you need (likely the first 16) or map the bitmap colors to high index only if they are less than 256 in number.

    Also this is NASM assembly and I haven't tested the code, just sketched it as I don't feel like setting a DosBox up.