;*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.;
;*****************************************************************************/

;---------------------------Module-Header-------------------------------;
; Module Name: COMPRESS.MAC
;
; Macros for compression of rectangle data.
;
;
;----------------------------------------------------------------------;

ensure_rows_are_buffered macro
local  all_rows_fit
local  rows_buffered
local  first_row

        ; Check that we have the required rows in the Transfer Buffer.
        ; (the current row, the 2 previous and the following 1).
        ; If this is the first row then we definitely need to
        ; load data into the Transfer Buffer.
        ; If this is the last row (but not the first - this could
        ; be a single row!) then it must be loaded already (as
        ; the previous row + 1 is loaded).
        mov     eax, yCurrentRow
        cmp     eax, yStartRow
        je      short first_row
        cmp     eax,yEndRow
        je      rows_buffered
        inc     eax                     ; Is (row+1) buffered?
        cmp     eax, yLastBufferedRow
        jle     rows_buffered

first_row:
        ; We need to move more rows from the screen into the Transfer Buffer.
        ; Reset the source pointer to the beginning of the Transfer Buffer.
        mov     esi, Spad.gsb_pTransferBufferVirt

        ; Calculate the first row number to load into the Transfer Buffer.
        mov     eax, yCurrentRow
        cmp     eax, yStartRow    ; Is this the first row of the rect?
        je      short @F

        ; This is not the first row, so this must be a reload.
        ; We want the 2 previous scanlines in the buffer to allow
        ; us to check for scanline pairs.
        sub     eax, 2

        ; Bump up the source address by two lines (as the current line
        ; will be the third line in the buffer).
        add     esi, cbBytesPerScanline
        add     esi, cbBytesPerScanline
@@:
        ; Load ebx with the number of rows that we want to copy.
        mov     ebx, yEndRow
        sub     ebx, eax
        inc     ebx

        ; The number of rows that we want to copy may be more than will
        ; actually fit into the Transfer Buffer.
        ; Check for this, and limit the number of lines accordingly.
        cmp     ebx, cyMaxRowsInTransferBuffer
        jle     short all_rows_fit

        ; There are more lines than will fit into the Transfer Buffer!
        ; Load as many as we can - we will deal with the rest later.
        mov     ebx, cyMaxRowsInTransferBuffer

all_rows_fit:
ifndef S3
        ; Save the current source pointer
        push    esi

        ; Load up the pointer to the XGA regs (the seg reg was set
        ; up earlier).
        movzx   esi, word ptr pXGARegs

        ; The height of the pixblt is the number of rows (currently in ebx).
        ; The hardware is loaded with the required value - 1.
        dec     ebx
        memregwrite     dim2, bx

        ; Calculate the last buffered row.
        ; ebx contains the number of rows that we are loading - 1.
        ; eax contains the number of the current row.
        add     ebx, eax
        mov     yLastBufferedRow, ebx

        ; Load the hardware with the source row number.
        memregwrite     src_map_y, ax

        ; Set up the source x position
        mov     ebx,Spad.gsb_prclCurrent
        mov     eax, [ebx].rcl_xLeft
        memregwrite     src_map_x, ax

        ; Set up the destination x and y positions.
        ; These are always zero.
        xor     eax,eax
        memregwrite     dest_map, eax

        ; Set up the width of the operation
        mov     eax, cxRectWidth
        dec     eax
        memregwrite     dim1, ax

        ; Copy the new rectangle into the Transfer Buffer (PHUNK)
        mov     eax, Spad.gsb_ulXGAPixOp
        memregwrite     pixel_op, eax

        ; Wait for the copy to complete.
        waitshort

        ; Restore the original source pointer
        pop     esi
else ;S3

        push    esi                     ;Save our pointer into the PHUNK.

        ;--------------------------------------------------------------
        ; Fetch our BPP format.
        ;--------------------------------------------------------------
        mov     esi, p8514Regs
        memregread      dl,pi_map_format_A
        movzx   edx,dl

        ;--------------------------------------------------------------
        ; Push the Pixel Format for _CopyVRAMtoMemory()
        ;--------------------------------------------------------------
        push    edx

        ;--------------------------------------------------------------
        ; The height of the pixblt is the number of rows (currently in ebx).
        ; The hardware is loaded with the required value - 1.
        ; Push the rectangle height for _CopyVRAMtoMemory().
        ;--------------------------------------------------------------
        dec     ebx
        push    ebx

        ;--------------------------------------------------------------
        ; Set up the width of the operation for _CopyVRAMtoMemory().
        ;--------------------------------------------------------------
        mov     edx, cxRectWidth
        dec     edx
        push    edx

        ;--------------------------------------------------------------
        ; Calculate the last buffered row.
        ; ebx contains the number of rows that we are loading - 1.
        ; eax contains the number of the current row.
        ;--------------------------------------------------------------
        add     ebx, eax
        mov     yLastBufferedRow, ebx

        ;--------------------------------------------------------------
        ; Move the Y coord. into the high word of EAX.
        ;--------------------------------------------------------------
        ror     eax,16

        ;--------------------------------------------------------------
        ; Move the X coord. into the low word of EAX.
        ; Rotate EAX to create a VRAM pointer and push it for _CopyVRAMtoMemory().
        ;--------------------------------------------------------------
        mov     ebx,Spad.gsb_prclCurrent
        mov     edx, [ebx].rcl_xLeft
        mov     ax,dx
        ror     eax,16
        push    eax

        ;--------------------------------------------------------------
        ; Push the System Memory (PHUNK) address for _CopyVRAMtoMemory().
        ; Make sure we copy the data into the PHUNK starting at the
        ; BEGINNING of the buffer.  Except for the first case, this
        ; give us the two preceeding scanlines so that we can check
        ; for duplicates properly.  After the call to CopyVRAMToMemory(),
        ; we will pop off the current pointer into the PHUNK which, if
        ; we're reloading, points to the THIRD scanline.
        ;--------------------------------------------------------------
        mov     esi, Spad.gsb_pTransferBufferVirt
        push    esi

        call    _CopyVRAMToMemory
        add     esp,20

        pop     esi                     ;Restore our pointer into the PHUNK.

endif

rows_buffered:
endm

;----------------------------------------------------------------------;
; count_repeating_data counts the number of consecutive repeating
; data fields.
;
; Parameters:
;       non_repeating_data - label to jump to if no repeating data found
;
;       field_size - size of fields (8 or 16 bits)
;
;
; Entry:
;       esi -> source data
;       edi -> destination buffer (value not used here, but must
;              be preserved)
;       cbBytesToCompress = source data bytes remaining
;
; Exit:
;       ecx = number of consecutive repeating data fields
;
; Preserved:
;       esi
;       edi
;       cbBytesToCompress
;
;----------------------------------------------------------------------;
count_repeating_data macro non_repeating_data,field_size
local run_to_end

        ; initialise the values for the @FLD8 and @FLD16 macros
        INITFIELDSIZE        field_size

        ; Load up the max number of source bytes to compare
        mov     ecx,cbBytesToCompress

        ; Convert bytes to data fields
@FLD16 <shr     ecx,1>

        ; Check that we have at least 3 fields remaining
        ; (the minimum required for a repeating run)
        cmp     ecx, 2

        ; Skip out now if we can
        jle     short non_repeating_data

        ; Do a quick check to see if we have repeating data
        mov     ax, [esi]
@FLD8  <cmp     al, ah>
@FLD16 <cmp     ax,[esi+2]>

        ; Exit now if first two fields differ
        jne     short non_repeating_data

        ; We have at least a pair of matching data fields.
        ; Use a super-fast "scas" to see how many more there are.
        ; For this, we need [edi] to point to the source data
        ; (which is a shame, as we already have [esi] pointing there).

        push    edi             ; Save edi on stack

        ; Load source pointer into edi.
        mov     edi,esi

        ; Save current value of ecx in ebx
        mov     ebx,ecx

@FLD8  <repe    scasb>
@FLD16 <repe    scasw>
        ; unless we have reached the end of the string the count
        ; was decremented once too many
        jz      short run_to_end
        inc     ecx

run_to_end:

        ; Now calculate how many matches were found.

        ; ebx contains the number of fields remaining before the scan
        ; ecx contains the number of remaining fields after the scan
        sub     ebx,ecx
        mov     ecx,ebx

        pop     edi             ; Restore edi from stack

        ; If we only had two fields the same in a row, we do not have a run
        cmp     ecx, 2
        jle     short non_repeating_data

endm

;----------------------------------------------------------------------;
; count_non_repeating_data counts the number of data fields before we
; have 3 fields that are repeating
;
; Entry:
;       esi -> source data
;       edi -> destination buffer (value not used here, but must
;                  be preserved)
;       cbBytesToCompress = source data bytes remaining
;
; Exit:
;       ecx = number of fields before repeating data
;
; Preserved:
;       esi
;       edi
;       cbBytesToCompress
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
count_non_repeating_data macro   field_size
local   no_more_runs
local   no_more_runs_quick_exit
local   run_found
local   cnrd_exit
local   third_field_different
local   repeat_scan

        ; initialise the values for the @FLD8 and @FLD16 macros
        INITFIELDSIZE        field_size

        ; Load up the max number of source bytes to compare
        mov     ecx,cbBytesToCompress

        ; Convert bytes to data fields
@FLD16 <shr     ecx,1>

;------------------------------------------------------------
;  We find the next repeating data by using a cmps instruction
;  with the destination one field further on than the source
;
;  This will find us the first pair of matching fields.
;  We can then check the third one against these fields to
;  see if we have enough in a row to be a run.
;------------------------------------------------------------

        ; Find max no. of comparisons to do
        dec     ecx

        ; Skip out now if we can
        jz      short no_more_runs_quick_exit

        ; store the destination and source pointers as we are about to use them
        ; note don't need to push ds as the source segment will not be changed

        push    edi
        push    esi

        ; set the destination pointer to be one field on from the source

        mov     edi, esi

@FLD8  <inc     edi>
@FLD16 <add     edi,2>

repeat_scan:
        ; find a pair of repeating fields

@FLD16 <repne   cmpsw>
@FLD8  <repne   cmpsb>

        ; See if we reached the end of the fields
        jecxz   no_more_runs

        ; See if we have 3 in a row.
        ; esi points to the second field of the pair, edi points to the
        ; third field if they are the same we have 3 in a row
@FLD16 <cmpsw>
@FLD8  <cmpsb>
        jnz     short third_field_different

        ; We have found the next run!

        ; The number of fields before the run is given by:
        ;    cbBytesToCompress - ecx - 2
        ; as ecx started 1 less than the number of fields and has been
        ; decremented once too often by the cmps instuction.
        mov     eax, cbBytesToCompress
        ; Convert eax to fields
@FLD16 <shr     eax,1>
        sub     eax, ecx
        sub     eax, 2
        mov     ecx, eax
        jmp     short run_found

third_field_different:
        ; Although we found a pair, the third field was different -
        ; we want to be able to carry on the CMPS as if we had not stopped.
        ; The extra CMPS to check the third field will not have decremented
        ; ecx, so we do that here
        dec     ecx
        jz      short no_more_runs
        jmp     short repeat_scan

no_more_runs_quick_exit:
        ; The rest of the line consists of non-repeating data
        ; and we have not pushed the source and destination pointers
        mov     ecx,cbBytesToCompress
@FLD16 <shr     ecx,1>
        jmp     short cnrd_exit

no_more_runs:
        ; The rest of the line consists of non-repeating data
        mov     ecx,cbBytesToCompress
@FLD16 <shr     ecx,1>

run_found:
        ; Restore the source and destination pointers to their original
        ; values
        pop     esi
        pop     edi

cnrd_exit:
endm

;----------------------------------------------------------------------;
; write_repeating_cell is a macro to fill in a repeating cell
; (count and data) in the destination buffer.
;
; Parameters:
;       field_size       - the size of the data cells (8 or 16)
;
; Entry:
;       edi -> destination buffer
;       esi -> source buffer
;       ecx =  count of data fields
;
; Exit:
;       edi -> next free pos in destination buffer
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
write_repeating_cell macro     field_size
local   write_another_rpt_cell
local   rd_size_ok

        ; initialise the values for the @FLD8 and @FLD16 macros
        INITFIELDSIZE        field_size

        ; Store the original total count in ebx
        mov     ebx,ecx

        ; If using 8-bit data fields the maximum run length is 127 bytes.
        ; If the run is longer than this, we will have to write multiple
        ; run length/data cells.
        ; During the following loop:
        ;    eax - "clipped" run length
        ;    ecx - total remaining data fields to process

write_another_rpt_cell:
        ; Get length of run into eax, clipping to maximum run length if
        ; necessary.
        mov     eax,ecx
@FLD8  <cmp     eax,CD_MAX_8BIT_RUN_LENGTH>; See if we exceed max run length
@FLD8  <jle     short rd_size_ok>          ; Skip forward if not
@FLD8  <mov     eax,CD_MAX_8BIT_RUN_LENGTH>; Set length of this run to max
rd_size_ok:

        ; Store length of run in output buffer
@FLD8  <stosb>
@FLD16 <stosw>

        ; Fetch data value from source, and write to output buffer
        ; Note that the "movs" will increment the source pointer, which
        ; we do not really want, so we immediately adjust it back.
        ; The alternative would be a "mov al/ax,[esi], stosb/stosw",
        ; but this would trash our count in al/ax!
@FLD8  <movsb>
@FLD16 <movsw>

        ; Adjust source pointer back to its original value.
        ; If the data is 16-bit then we have to decrement it twice to
        ; move back a word.
@FLD8  <dec     esi>
@FLD16 <sub     esi, 2>

@FLD8  <sub     ecx,CD_MAX_8BIT_RUN_LENGTH>; Adjust total count
@FLD8  <jg      short write_another_rpt_cell> ; Loop back if more cells needed


        ; Convert data fields into bytes
@FLD16 <shl     ebx,1>

        ; Update cbBytesToCompress by the length of the run
        sub     cbBytesToCompress,ebx

        ; Update source pointer
        add     esi,ebx
endm

;----------------------------------------------------------------------;
; write_non_repeating_cell is a macro to fill in a complete non-repeating
; cell (count+data) in the destination buffer.
;
; Parameters:
;       field_size - size of cell - must be 8 or 16
;
; Entry:
;       edi -> destination buffer
;       esi -> source buffer
;       ecx =  count of data fields
;
; Exit:
;       edi -> next free pos in destination buffer
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
write_non_repeating_cell macro     field_size
local   write_another_nrpt_cell
local   nrd_size_ok


        ; initialise the values for the @FLD8 and @FLD16 macros
        INITFIELDSIZE        field_size

        ; Store the original total count in ebx
        mov     ebx,ecx

        ; If using 8-bit data fields the maximum run length is 127 bytes.
        ; If the run is longer than this, we will have to write multiple
        ; run length/data cells.
        ; During the following loop:
        ;    eax - "clipped" run length
        ;    ecx - total remaining data fields to process

write_another_nrpt_cell:
        ; Get length of run into eax, clipping to maximum run length if
        ; necessary.
        mov     eax,ecx
@FLD8  <cmp     eax,CD_MAX_8BIT_RUN_LENGTH>; See if we exceed max run length
@FLD8  <jle     short nrd_size_ok>            ; Skip forward if not
@FLD8  <mov     eax,CD_MAX_8BIT_RUN_LENGTH>; Set length of this run to max

nrd_size_ok:

        ; Set top bit to indicate that this is non-repeating data
@FLD8  <or      al,CD_NON_REPEATING_DATA_FLAG_8>
@FLD16 <or      ax,CD_NON_REPEATING_DATA_FLAG_16>

        ; Store length of run in output buffer
@FLD8  <stosb>
@FLD16 <stosw>

        ; Clear top bit to get back the true data count
@FLD8  <and     al,not CD_NON_REPEATING_DATA_FLAG_8>
@FLD16 <and     ax,not CD_NON_REPEATING_DATA_FLAG_16>

        ; Copy the non-repeating data from source to destination.
        ; For this we need:
        ;   - the field count in ecx (which is currently in eax)
        ;   - esi -> source data (already set up)
        ;   - edi -> dest buffer (already set up)
        xchg    eax,ecx         ; Move eax into ecx, saving ecx value

        ; Move it!
@FLD8  <rep     movsb>
@FLD16 <rep     movsw>

@FLD8  <mov     ecx,eax>                ; Restore ecx value
@FLD8  <sub     ecx,CD_MAX_8BIT_RUN_LENGTH>; Adjust total count
@FLD8  <jg      short write_another_nrpt_cell>; Loop back if more cells needed


        ; Convert data fields into bytes
@FLD16 <shl     ebx,1>

        ; Update cbBytesToCompress
        sub     cbBytesToCompress,ebx
endm


;----------------------------------------------------------------------;
; write_dup_scanline_cell is a macro to fill in a duplicate scanline cell
; in the destination buffer.
; Note the number of duplicates is set to 1
;
; Entry:
;       edi -> destination buffer
;
; Exit:
;       edi -> next free pos in destination buffer
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
write_dup_scanline_cell macro
local   field_size_16
local   written_cell

        ; A duplicate scan cell is a zero followed by a count
        ; The count is set to one here and adjusted if more
        ; duplicate scans are found later
        ; 
        ; Therefore store the bytes  00 01     if cell is 8 bits wide
        ;                        or  0000 0001 if cell is 16 bits wide

        cmp     Spad.gsb_ulDataFieldSize,8
        jne     short field_size_16
        mov     eax, 0100h
        stosw
        sub     Spad.gsb_cbFreeBytesInDestBuffer, 2
        jmp     short written_cell

field_size_16:
        mov     eax, 00010000h
        stosd
        sub     Spad.gsb_cbFreeBytesInDestBuffer, 4

written_cell:
endm

;----------------------------------------------------------------------;
; write_dup_scanline_pair_cell is a macro to fill in a duplicate scanline pair
; cell in the destination buffer.
; Note the number of duplicates is set to 1
;
; Entry:
;       edi -> destination buffer
;
; Exit:
;       edi -> next free pos in destination buffer
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
write_dup_scanline_pair_cell macro     field_size
local   field_size_16
local   written_cell

        ; A duplicate scanline pair is two zero fields followed by a count
        ; The count is set to one here and adjusted if more duplicate
        ; scan pairs are found later
        ; 
        ; Therefore store the bytes 00 00 01        if cell is 8 bits wide
        ;                        or 0000 0000 0001  if cell is 16 bits wide

        xor     eax, eax        ; Zero out accumulator

        cmp     Spad.gsb_ulDataFieldSize,8
        jne     short field_size_16

        ; Store the two zero fields
        stosw

       ; Initialise the count
        inc     eax

       ; Store the count
        stosb

        ; Update the bytes free in buffer
        sub     Spad.gsb_cbFreeBytesInDestBuffer, 3
        jmp     short written_cell

field_size_16:
        ; Store the two zero fields
        stosd

        ; Initialise the count
        inc     eax

        ; Store the count
        stosw

        ; Update the bytes free in buffer
        sub     Spad.gsb_cbFreeBytesInDestBuffer, 6

written_cell:
endm





;----------------------------------------------------------------------;
; Macro to compress a row of data.
;
; Parameters:
;       field_size - Specifies the size of a single data field.
;                    Must be either 8 or 16 (bits).
;
; Entry:
;   edi -> next free position in destination buffer
;   esi -> first byte in current source row
;   ecx =  number of data fields to compress
;
; Exit:
;   edi -> updated next free position in destination buffer
;   esi -> byte after end of current source row
;   ecx =  0
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
compress_row_local_vars  macro
        localWD cbBytesToCompress
endm

compress_row  macro   field_size
local   main_loop
local   non_rpting_data
local   end_loop
local   no_more_src_bytes

        ; initialise the values for the @FLD8 and @FLD16 macros
        INITFIELDSIZE        field_size

        ; Convert the number of data fields to a byte count.
@FLD16 <shl     ecx,1>

        ; Store source data length
        mov     cbBytesToCompress,ecx

main_loop:
        ; Count consecutive repeating data fields.
        ; The count is returned in ecx.
        count_repeating_data    non_rpting_data   field_size

        ; Write out the repeating data cell
        write_repeating_cell field_size

        jmp     end_loop

non_rpting_data:

        ; Count how many fields we have before the next repeating data
        count_non_repeating_data  field_size

        ; Write out the non-repeating data cell
        write_non_repeating_cell  field_size

end_loop:
        ; Check whether there are more source bytes to compress
        mov     ecx,cbBytesToCompress
        or      ecx,ecx
        jnz     main_loop
endm


;----------------------------------------------------------------------;
; Macro which appends the correct data field size (either 8 or 16)
; onto the end of the supplied argument.
;
; This macro is very dependent upon which adapter is being used.
;    VGA always uses 8- bit fields (always 4bpp)
;    BGA always uses 16-bit fields (always 8bpp)
;    XGA may use either 8 or 16-bit fields (depending on the bits/pel)
;
; Parameters:
;    instruction        The instruction to which "8" or "16" will
;                       be appended. This will typically be a
;                       "write_cell" macro.
; Entry:
;    ulBitsPerPel       Screen bits per pel (used for XGA only)
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
append_data_size  macro      instruction
local   field_size_16
local   ads_exit

        mov     eax, ulBitsPerPel
        cmp     eax, 4
        jne     short field_size_16
        instruction 8
        jmp     short ads_exit

field_size_16:
        instruction 16

ads_exit:
endm


;----------------------------------------------------------------------;
; Macro which compares two rows of data
;
; Parameters:
;
; Entry:
;   edi -> row 1
;   esi -> row 2
;   ecx =  length of each row in bytes
;
; Exit:
;   edi -> ????
;   esi -> ????
;   ecx =  ????
;   zero flag set to indicate if rows were the same or not
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
compare_rows macro

        ; Compare the rows
        repe    cmpsb

endm

;----------------------------------------------------------------------;
; Macro which moves the pointer onto the next source row
; This will jump to an exit point if we go past the last row
;
; Parameters:
;    exit_label         The place to jump to if the last row is past
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
inc_source_row macro exit_label

        ; Increment the row count
        inc     yCurrentRow

        ; Exit if we have processed the last row
        mov     eax, yCurrentRow
        cmp     eax, yEndRow
        jg      exit_label

        ; Adjust the source pointer onto the next scan line
        mov     eax, cbBytesPerScanline
        add     pCurrentSrcPos,eax
        add     esi, eax

endm

;----------------------------------------------------------------------;
; Macro which moves the pointer on by two source rows
; This will jump to an exit point if we go past the last row
;
; This is functionally equivalent to
;
;       inc_source_row  exit_label
;       inc_source_row  exit_label
;
; Parameters:
;    exit_label         The place to jump to if the last row is past
;
; Note if the jump is made to the exit label, the yCurrentRow could
;      have been incremented one row too far - this is not a problem as
;      the yCurrentRow is only needed again if the buffer was not large,
;      this jump will only occur when we have finished without the buffer
;      running out.
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
inc_source_row_twice macro exit_label

        ; Update the row count
        add     yCurrentRow, 2

        ; Exit if we have processed the last row
        mov     eax, yCurrentRow
        cmp     eax, yEndRow
        jg      exit_label

        ; adjust the source pointer onto the next scan line
        mov     eax, cbBytesPerScanline
        shl     eax, 1
        add     pCurrentSrcPos,eax
        add     esi, eax

endm

;----------------------------------------------------------------------;
; Macro which increments the 'count' field within a cell
;
; If the cell size is 8, there is a maximum of 255 imposed on the
; cell count, if this is exceeded, the duplicate cell is repeated
; to allow for the extra lines
;
; Parameters:
;       cell_type - the type of cell (<dup_scan> or <dup_scan_pair>)
;
; Entry:
;   edi -> next free byte in destination buffer
;               (so cell count is in the preceding BYTE/WORD
;
; Exit:
;   edi -> next free byte in destination buffer (updated if required)
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
inc_cell_count  macro   cell_type
local   field_size_16
local   cell_incremented

        ; check parameters
ifdif <cell_type>, <dup_scan>
ifdif <cell_type>, <dup_scan_pair>
.err cell_type invalid
endif
endif

        ; Increment the count (the count filed is the field just
        ; before the next free place in the destination)
        cmp     Spad.gsb_ulDataFieldSize,8
        jne     short field_size_16

        inc     byte ptr [edi-1]

        ; See if we wrapped around (only appropiate with 8 bit fields)
        jnz     short cell_incremented

        ; The count has wrapped, set it back to the maximum
        ; value and repeat the cell
        dec     byte ptr [edi-1]
ifidn <cell_type>,<dup_scan>
        write_dup_scanline_cell
else
        write_dup_scanline_pair_cell
endif
        jmp     short cell_incremented

field_size_16:
        inc     word ptr [edi-2]

cell_incremented:
endm
