;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
ifdef DCAF
        PAGE     55,132
        TITLE    Packing
        SUBTITLE Header
;/*****************************************************************************
;*
;* SOURCE FILE NAME = PACKING.ASM
;*
;* DESCRIPTIVE NAME = DCAF packed/planar conversion code.
;*
;*
;* VERSION      V2.0
;*
;* DATE         04/30/92
;*
;* DESCRIPTION
;*             These routines convert VGA 4bpp data between packed
;*             and planar formats.
;*
;* FUNCTIONS   PackBuffer
;*             UnPackBuffer
;*
;* NOTES       NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/30/92                     Created for DCAF 1.3
;*   08/01/92                     Converted to 32-bit for DCAF 2.0
;*
;*****************************************************************************/

        .386p
        .xlist
DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1
        include pmgre.inc
        include driver.inc
        include assert.mac
        include protos.inc
        include egafam.inc

        include dcafmac.inc
        .list

        .MODEL  FLAT
        ASSUME  CS:FLAT, SS:FLAT, DS:FLAT, ES:FLAT
        .CODE

;/*
;** The pack table converts a 4 bit index into a 2 byte value holding
;** the same 4 bits spaced one per nibble.
;** The words are byte swapped, so that we can write them out using a
;** word write, but still get them in Motorola order.
;*/
ALIGN 4

        public PackTable
PackTable:
        WORD   0000h            ; 0000b -> 0000h byte swapped
        WORD   0100h            ; 0001b -> 0001h byte swapped
        WORD   1000h            ; 0010b -> 0010h byte swapped
        WORD   1100h            ; 0011b -> 0011h byte swapped
        WORD   0001h            ; 0100b -> 0100h byte swapped
        WORD   0101h            ; 0101b -> 0101h byte swapped
        WORD   1001h            ; 0110b -> 0110h byte swapped
        WORD   1101h            ; 0111b -> 0111h byte swapped
        WORD   0010h            ; 1000b -> 1000h byte swapped
        WORD   0110h            ; 1001b -> 1001h byte swapped
        WORD   1010h            ; 1010b -> 1010h byte swapped
        WORD   1110h            ; 1011b -> 1011h byte swapped
        WORD   0011h            ; 1100b -> 1100h byte swapped
        WORD   0111h            ; 1101b -> 1101h byte swapped
        WORD   1011h            ; 1110b -> 1110h byte swapped
        WORD   1111h            ; 1111b -> 1111h byte swapped

;/*
;** The unpack table is split into 2 halves as we access it using
;** words.
;** The table again takes a 4 bit value, and returns the bits spaced 1 per
;** bytes.
;*/

UnPackTableLo:
                                ; This is the low word table
                                ;                 |
                                ;                 v
        WORD   0000h            ; 0000b -> 0000:0000h
        WORD   0001h            ; 0001b -> 0000:0001h
        WORD   0100h            ; 0010b -> 0000:0100h
        WORD   0101h            ; 0011b -> 0000:0101h
        WORD   0000h            ; 0100b -> 0001:0000h
        WORD   0001h            ; 0101b -> 0001:0001h
        WORD   0100h            ; 0110b -> 0001:0100h
        WORD   0101h            ; 0111b -> 0001:0101h
        WORD   0000h            ; 1000b -> 0100:0000h
        WORD   0001h            ; 1001b -> 0100:0001h
        WORD   0100h            ; 1010b -> 0100:0100h
        WORD   0101h            ; 1011b -> 0100:0101h
        WORD   0000h            ; 1100b -> 0101:0000h
        WORD   0001h            ; 1101b -> 0101:0001h
        WORD   0100h            ; 1110b -> 0101:0100h
        WORD   0101h            ; 1111b -> 0101:0101h

UnPackTableHi:
                                ; This is the high word table
                                ;            |
                                ;            v
        WORD   0000h            ; 0000b -> 0000:0000h
        WORD   0000h            ; 0001b -> 0000:0001h
        WORD   0000h            ; 0010b -> 0000:0100h
        WORD   0000h            ; 0011b -> 0000:0101h
        WORD   0001h            ; 0100b -> 0001:0000h
        WORD   0001h            ; 0101b -> 0001:0001h
        WORD   0001h            ; 0110b -> 0001:0100h
        WORD   0001h            ; 0111b -> 0001:0101h
        WORD   0100h            ; 1000b -> 0100:0000h
        WORD   0100h            ; 1001b -> 0100:0001h
        WORD   0100h            ; 1010b -> 0100:0100h
        WORD   0100h            ; 1011b -> 0100:0101h
        WORD   0101h            ; 1100b -> 0101:0000h
        WORD   0101h            ; 1101b -> 0101:0001h
        WORD   0101h            ; 1110b -> 0101:0100h
        WORD   0101h            ; 1111b -> 0101:0101h

;--------------------------Internal-Routine-----------------------------;
;/*********************************************************************
;* PackBuffer                                                         *
;*                                                                    *
;* This routine performs the planar to packed conversion.             *
;* It picks up the planar data directly from VRAM                     *
;*                                                                    *
;**********************************************************************
;*
;* Entry:
;*       None
;* Returns:
;*       None
;* Error Returns:
;*       None
;* Registers Preserved:
;*       16 bit  SI,DI,BP,DS,ES
;*       32 bit  ESI,EDI,EBX,EBP,DS,ES,FS,GS
;* Registers Destroyed:
;*       16 bit  AX,BX,CX,DX,FLAGS
;*       32 bit  EAX,ECX,EDX,FLAGS
;* History:
;*
;* Thur-30-Apr-1992       -by-    Mark Berry
;*     Wrote it.
;**********************************************************************/

PackBuffer PROC SYSCALL USES edi esi,
        pUnpacked :DWORD,
        pPacked :DWORD,
        cPels :DWORD,
        ulPlaneOffset :DWORD

LOCAL   planes0and1 :WORD,
        planes2and3 :WORD,
        pels0to3 :WORD,
        pels4to7 :WORD

ifdef FIREWALLS
;/*
;** This code assumes that the no of pels is a multiple of 8
;*/
        test    cPels,7
        jz      cPels_ok
        int     3
cPels_ok:
endif ; FIREWALLS

        mov     esi, pUnpacked
        ASSUME  ESI:PTR BYTE
        mov     edi, pPacked
        ASSUME  EDI:PTR BYTE

;/*
;** Initialize the locals which provide our fast path route
;** NB. 0,0,0,0 is the packed version of 0,0,0,0 planar !
;*/
        xor     eax,eax
        mov     planes0and1,ax
        mov     planes2and3,ax
        mov     pels0to3,ax
        mov     pels4to7,ax

pack_8_pels:
;/*
;** Because the pels are held in planes format, we have to read each
;** plane separately and then combine the planes to produce packed pels.
;** Each read will actually get 8 pels worth of information, so when
;** we've finished all four planes we will have 8 packed pels available.
;*/

;/*
;** Get the first plane
;*/
        mov     dx, EGA_BASE+GRAF_ADDR
        mov     al, GRAF_READ_MAP
        mov     ah, RM_C0
        out     dx, ax

        mov     bh, [esi]       ; get plane 0 into bh

;/*
;** And the second plane
;*/
        mov     ah, RM_C1
        out     dx, ax

        mov     bl, [esi]       ; get plane 1 into bl

;/*
;** The third plane
;*/
        mov     ah, RM_C2
        out     dx, ax

        mov     ch, [esi]       ; get plane 2 into ch

;/*
;** The final plane
;*/
        mov     ah, RM_C3
        out     dx, ax

        mov     cl, [esi]       ; get plane 3 into cl

got_planar_bytes:
;/*
;** bx:cx now holds 8 planar pels.
;*/

;/*
;** Check if these 8 pels are the same as the last pels we
;** packed.
;** cmp mem, reg is faster than cmp reg, mem !
;*/
        cmp     planes0and1, bx
        jne     must_pack_pels
        cmp     planes2and3, cx
        jne     must_pack_pels

        mov     ax, pels4to7
        mov     di, pels0to3
        jmp     packed_the_pels

must_pack_pels:
;/*
;** Save these for our fast path next time
;*/
        mov     planes0and1,bx
        mov     planes2and3,cx

;/*
;**
;** Looks like we really have to pack these pels!
;**
;** The planes data now needs to be packed, which basically involves
;** taking the four bits (one for each plane) of a pel and ORing them
;** together so that plane 3 ends up in the leftmost position and
;** plane 0 ends up in the rightmost position.
;*/

;/*
;** This is done 4 pels at a time using the PackTable
;*/

;/*
;** Now move the planes into dx:cx as required.
;*/
        mov     dx,bx

;/*
;** Register usage
;**   esi           points at PackTable
;**   bx            work register used to index PackTable
;**   dx:cx         planar data (unpacked pels)
;**   eax           working reg (holding up to 4 packed pels)
;**   di            used to store the first 4 packed pels
;*/

        push    esi
        lea     esi, PackTable
        ASSUME  ESI:PTR WORD
        ASSUME  EBX:PTR WORD

        mov     bl, cl             ; get plane 3
        and     ebx, 0Fh           ; mask out all but the right 4 pels
                                   ; (ie zero bh in the process)
        add     ebx,ebx            ; turn the value into a Word offset
        mov     ax, [esi+ebx]      ; get the corresponding value from the table
        add     eax,eax            ; now move the value left by one position
                                   ; ready for the next plane to be ORed in
        mov     bl, ch             ; get plane 2
        and     bl, 0Fh            ; mask out the left 4 pels
        add     ebx,ebx            ; turn the value into a Word offset
        or      ax, [esi+ebx]      ; get the corresponding value from
                                   ; the table and OR it with plane 3
        add     eax,eax            ; now move the value left by one position
                                   ; ready for the next plane to be ORed in
        mov     bl, dl             ; and so on .........
        and     bl, 0Fh
        add     ebx,ebx
        or      ax, [esi+ebx]
        add     eax,eax
        mov     bl, dh
        and     bl, 0Fh
        add     ebx,ebx
        or      ax, [esi+ebx]

;/*
;** Save this value
;*/
        mov     pels0to3,ax
        mov     edi, eax        ; also keep it safe whilst we do pels 4 to 7

        and     dx,0f0f0h       ; keep just the upper nibbles
        and     cx,0f0f0h

        shr     dx, 3           ; get ready to repeat the above for the next
        shr     cx, 3           ; 4 pels

;/*
;** Note we only shifted by 3 rather than by 4 because we actually
;** want the nibble value * 2 as a word index into a table.
;** Because they are nibbles, they can't overflow the byte when
;** multiplied by 2
;*/

        mov     bl, cl
        mov     ax, [esi+ebx]
        add     eax,eax
        mov     bl, ch
        or      ax, [esi+ebx]
        add     eax,eax
        mov     bl, dl
        or      ax, [esi+ebx]
        add     eax,eax
        mov     bl, dh
        or      ax, [esi+ebx]

        mov     pels4to7,ax
        pop     esi

packed_the_pels:
;/*
;** At this point we have the pels in ax:di
;**
;** pel  7  6  5  4  3  2  1  0
;**       al    ah :     di
;** Save these pels in the packed buffer.
;*/
        mov     ebx,pPacked
        ASSUME  EBX:PTR WORD
        mov     [ebx], ax       ; pels 4 to 7
        add     ebx,2
        mov     [ebx], di       ; pels 0 to 3
        add     bx, 2
        mov     pPacked, ebx

        inc     esi             ; move to next 8 pels for next pass of loop

        sub     cPels, 8
        jnz     pack_8_pels
        ret

PackBuffer ENDP

;--------------------------Internal-Routine-----------------------------;
;/*********************************************************************
;* UnPackBuffer                                                       *
;*                                                                    *
;* This routine performs the packed to planar conversion.             *
;* It puts the planar data into a memory planar arrangement.          *
;*                                                                    *
;**********************************************************************
;
; Entry:
;       None
; Returns:
;       None
; Error Returns:
;       None
; Registers Preserved:
;       16 bit  SI,DI,BP,DS,ES
;       32 bit  ESI,EDI,EBX,EBP,DS,ES,FS,GS
; Registers Destroyed:
;       16 bit  AX,BX,CX,DX,FLAGS
;       32 bit  EAX,ECX,EDX,FLAGS
; History:
;
; Thur-30-Apr-1992       -by-    Mark Berry
;     Wrote it.
;*********************************************************************/

UnPackBuffer PROC SYSCALL USES edi esi,
        pUnpacked :DWORD,
        pPacked :DWORD,
        cPels :DWORD,
        ulPlaneOffset :DWORD

LOCAL   planes0and1 :WORD,
        planes2and3 :WORD,
        pels0to3 :WORD,
        pels4to7 :WORD

ifdef FIREWALLS
;/*
;** This code assumes that the no of pels is a multiple of 8
;*/
        test    cPels,7
        jz      cPels_ok2
        int     3
cPels_ok2:
endif ; FIREWALLS

;/*
;** 16 bit version of the code - converted to 32-bit.
;*/
        mov     esi, pPacked
        mov     edi, pUnpacked

;/*
;** Initialize the locals which provide our fast path route
;** NB. 0,0,0,0 is the packed version of 0,0,0,0 planar !
;*/
        xor     eax,eax
        mov     planes0and1,ax
        mov     planes2and3,ax
        mov     pels0to3,ax
        mov     pels4to7,ax

unpack_8_pels:
;/*
;** Get 8 packed pels
;*/
        lodsw           ; get pels 4 to 7
        mov     cx,ax
        lodsw           ; get pels 0 to 3

;/*
;** Check if these 8 bytes are the same as the last bytes we
;** unpacked.
;** cmp mem, reg is faster than cmp reg, mem !
;*/
        cmp     pels0to3, ax
        jne     must_unpack_pels
        cmp     pels4to7, cx
        jne     must_unpack_pels

        mov     ax, planes0and1
        mov     dx, planes2and3
        jmp     unpacked_the_pels

must_unpack_pels:
        ; Save these for our fast path next time
        mov     pels0to3, ax
        mov     pels4to7, cx

;/*
;** The packed pels are in cx:pels0to3
;**
;** pel  7  6  5  4  3  2  1  0
;**       cl    ch : pels0to3
;*/

;/*
;** Register usage:
;**   bx            work register
;**   dx:ax         unpacked pels
;**   cx:pels0to3   packed pels
;*/
        mov     bx,cx
        and     ebx, 000f0h      ; keep just pel 7
        shr     ebx,3            ; bring bits to bottom 4 then mult by 2 !
        mov     dx, WORD PTR UnPackTableHi[ebx]
        mov     ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     bx,cx
        and     ebx, 0000fh      ; keep just pel 6
        add     ebx,ebx          ; mult by 2 (table is words)
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     bx,cx
        and     ebx, 0f000h     ; keep just pel 5
        shr     ebx,11          ; bring bits to bottom 4 then mult by 2 !
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     bx,cx
        and     ebx, 00f00h      ; keep just pel 4
        shr     ebx,7            ; bring bits to bottom 4 then mult by 2 !
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     cx, pels0to3    ; get the other 4 pels

        mov     bx,cx
        and     ebx, 000f0h      ; keep just pel 3
        shr     ebx,3            ; bring bits to bottom 4 then mult by 2 !
        and     ebx, 00011110b   ; keep just the bits we are working on
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     bx,cx
        and     ebx, 0000fh      ; keep just pel 2
        add     ebx,ebx          ; mult by 2 (table is words)
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     bx,cx
        and     ebx, 0f000h      ; keep just pel 1
        shr     ebx,11           ; bring bits to bottom 4 then mult by 2 !
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]
        add     ax,ax           ; shift left by 1
        adc     dx,dx           ; shift left by 1 including bit from ax

        mov     bx,cx
        and     ebx, 00f00h      ; keep just pel 0
        shr     ebx,7            ; bring bits to bottom 4 then mult by 2 !
        or      dx, WORD PTR UnPackTableHi[ebx]
        or      ax, WORD PTR UnPackTableLo[ebx]

;/*
;** At this point we have the pels in dx:ax
;*/
        mov     planes0and1, ax
        mov     planes2and3, dx

unpacked_the_pels:
;/*
;** Save these pels in the planar form
;*/
        mov     ebx,ulPlaneOffset
        mov     [edi],al
        mov     BYTE PTR [edi+ebx],ah
        add     ebx,ebx
        mov     BYTE PTR [edi+ebx],dl
        add     ebx,ulPlaneOffset
        mov     BYTE PTR [edi+ebx],dh
        inc     edi

        sub     cPels, 8
        jnz     unpack_8_pels
        ret

UnPackBuffer ENDP


endif ; DCAF

end
