
*****************************************************************************
*
*  NewBltBitMap()
*
*  Replacement BltBitMap which uses the 68030 instead of the blitter.
*  The following conditions must hold for the CPU routine to be used:
*
*      o Bitmaps aligned on same longword bit offset
*     (i.e. XSrc % 32 == XDest % 32)
*
*      o If source bitmap == destination bitmap, then YSrc != YDest
*
*      o Blitter minterm = $Cx (i.e. straight copy)
*
*  If any of these conditions doesn't hold, then the original BltBitMap
*  is called instead.
*
*  Input:
*     D0 - X Source
*     D1 - Y Source
*     D2 - X Dest
*     D3 - Y Dest
*     D4 - X Size
*     D5 - Y Size
*     D6 - Minterm
*     D7 - Mask, indicating which planes are to be affected
*     A0 - Pointer to source bitmap structure
*     A1 - Pointer to destination bitmap structure
*     A2 - Pointer to temporary bitmap structure (not used)
*
*  Output:
*     D0 - Number of planes actually copied
*
*  The copy routine works as follows. Everything is done in longword
*  units. If the bitmap being copied fits horizontally into a single
*  longword, then the CopySingle() routine is used which copies a
*  single column of longwords, masked out as appropriate. Otherwise,
*  there are at least two longwords involved (the left and right edges
*  of the bitmap), with possibly some longwords inbetween as well.
*  CopyMultiple() is called to perform this copy; it uses two mask
*  values to identify which bits in the left and right longwords should
*  be copied. The longwords (if any) in between are copied verbatim.
*
*  Note that using longwords gives a big win on the A3000 since it can
*  access CHIP ram via the 32 bit bus. This relies on the data being
*  longword aligned of course. In the worst case (where a bitmap width
*  is not a multiple of 4), one out of every two rows will be longword
*  aligned, which is not too bad. In the more common case, every row
*  is longword aligned. For overscan users, it's best to have your
*  screen width a multiple of 32.
*
*****************************************************************************

usagecount: ds.l 1

oldblt2:
   move.l   (sp)+,d6    ; Restore original mask
oldblit:
   jmp   $C0DEC0DE       ; Filled in with correct address later

dummy:   rts

BltBitMap:
   tst.w d4       ; Check if width is zero
   beq.s dummy       ; If it is, don't do anything
   cmp.l a0,a1       ; Copying within the same bitmap?
   bne.s nb2         ; If yes,
   cmp.w d1,d3       ; and Y row is the same, then it's a
   beq.s oldblit        ; sideways blit so use system routine
nb2:
   move.l   d6,-(sp)    ; Save current minterm register
   and.b #$f0,d6        ; Mask out low bits
   cmp.b #$c0,d6        ; Is it standard COPY minterm?
   bne.s oldblt2        ; If not, exit
   move.l   d0,d6       ; See if XSrc % 32 == XDest % 32
   eor.l d2,d6       ; Low 5 bits should be zero if equal
   and.b #$1f,d6        ;
   bne.s oldblt2        ; If not, then have to do normal blit
   tst.b bm_Flags(a0)      ; Is source standard Amiga bitmap?
   bne.s oldblt2        ; If not, use system blit routine
   tst.w bm_Pad(a0)     ;
   bne.s oldblt2        ;
   tst.b bm_Flags(a1)      ; How about destination?
   bne.s oldblt2        ; If it isn't, use system blit
   tst.w bm_Pad(a1)     ;
   bne.s oldblt2        ;
nb3:
;----------------------------------------------------------------------------
;  Where the action starts. Initialises everything and then performs
;  the blits using the CPU. At this stage, all registers are exactly
;  as they were on entry to BltBitMap, except for D6 and A0, and these
;  two are restored to the correct values immediately on entry.
;----------------------------------------------------------------------------
_StartBlit:
;
;  Now we need to determine the masks to be used for clipping, along
;  with the start address in memory of the section of the bit and
;  the modulo of each bitplane (the amount added onto the end of each
;  copied row address to get to the start of the next one). Then loop
;  over all the selected bitplanes, copying those requested.
;
   movem.l  d1-d5/d7/a0-a6,-(sp) ; Save rest of the registers
;
;  Next, we need to make sure that the blitter is free. This is because
;  some other blitter operation that operates on the bitmaps we've been
;  passed may have started but not yet finished. Operations that
;  depend on the blitter are guaranteed to occur in the right order
;  (since the blitter can't multitask with itself) but when we start
;  doing some of them with the CPU, we need to be a bit more careful.
;
;  Note: Since we are now "in" graphics.library, a6 holds GfxBase.
;        WaitBlit() is documented as preserving all registers.
;
  btst #6,_custom+dmaconr
   beq.b .exit
  move.w #$8400,_custom+dmacon
.wait:
  btst #6,_custom+dmaconr
   bne.b .wait
  move.w #$0400,_custom+dmacon
.exit:
   ext.l d0       ; Convert all parameters to long
   ext.l d1       ;
   ext.l d2       ;
   ext.l d3       ;
   ext.l d4       ;
   ext.l d5       ;

   cmp   d1,d3       ; See if we are scrolling up or down
   bhi.b   bltdown        ;
;
;  Since YDest < YSrc, we are copying the bitmap upwards in memory
;  therefore start at the beginning and work down. (This is only
;  important if the source and destination bitmaps are the same, but
;  it doesn't do any harm to check when they are different also.)
;
bltup:
   move.w   (a0),d6   ; Get width of source bitmap
   ext.l d6       ; Extend to full integer
   move.l   d6,a2       ; Initialise modulo for source bitmap
   muls  d6,d1       ; Calculate row offset
   move.l   d0,d6       ; Get XSrc
   lsr.l #3,d6       ; Get #bytes offset of XSrc
   and.b #$fc,d6        ; Adjust to longword boundary
   add.l d6,d1       ; Add on x offset to get bitmap offset
   move.l   d1,a4       ; Now A4 = offset into source bitmap
;
;  Repeat for dest bitmap
;
   move.w   (a1),d6   ; Get width of dest bitmap
   ext.l d6       ; Extend to full integer
   move.l   d6,a3       ; Initialise modulo for dest bitmap
   muls  d6,d3       ; Calculate row offset
   move.l   d2,d6       ; Get XDest
   lsr.l #3,d6       ; (Converted to longword aligned
   and.b #$fc,d6        ; byteoffset)
   add.l d6,d3       ; Add on xoffset to get bitmap offset
   move.l   d3,a5       ; Now A5 = offset into dest bitmap
   bra.s contblit    ; Skip to rest of blitcopy
;
;  If we get here, YDest > YSrc, so we are copying the bitmap downwards
;  which means we need to start from the end and work back. We also
;  need to initialise the modulo to -BytesPerRow instead of BytesPerRow.
;
bltdown:
   add.l d5,d1       ; Add YSize+YSrc to get last row addr
   subq.l   #1,d1       ; Adjust (so we don't have last_row+1)
   move.w   (a0),d6   ; Get width of source bitmap
   ext.l d6       ; Extend to full longword
   muls  d6,d1       ; Calculate row offset
   neg.l d6       ; Negate mod. since copying backwards
   move.l   d6,a2       ; Initialise modulo for source bitmap
   move.l   d0,d6       ; Get XSrc
   lsr.l #3,d6       ; Get #bytes offset of XSrc
   and.b #$fc,d6        ; Adjust to longword boundary
   add.l d6,d1       ; Add on x offset to get bitmap offset
   move.l   d1,a4       ; Now A4 = offset into source bitmap
;
;  Do same calculations for dest bitmap
;
   add.l d5,d3       ; Add YSize+YSrc to get last row addr
   subq.l   #1,d3       ; Adjust (so we don't have last_row+1)
   move.w   (a1),d6   ; Get width of dest bitmap
   ext.l d6       ; Extend to full longword
   muls  d6,d3       ; Calculate row offset
   neg.l d6       ; Negate, since copying backwards
   move.l   d6,a3       ; Initialise modulo for dest bitmap
   move.l   d2,d6       ; Get XDest
   lsr.l #3,d6       ; (Converted to longword aligned
   and.b #$fc,d6        ; byteoffset)
   add.l d6,d3       ; Add on xoffset to get bitmap offset
   move.l   d3,a5       ; Now A5 = offset into dest bitmap
;
;  Now calculate the mask values
;
contblit:
   and.w #$1f,d0        ; Calculate XSrc longword bit offset
   add.l d0,d4       ; Calculate width of bitmap
   move.l   d4,d1       ; Calculate longword bit offset
   and.w #$1f,d1        ;
   lsr.l #5,d4       ; Calc # of longwords needed for copy
   add.l d1,d1       ; Scale XWidth bits to longword index
   add.l d1,d1       ; into the bitmask array
   bne.s contb1         ; If zero,
   subq.l   #1,d4       ; Decrement longword count
contb1:
   lea   RightMask(PC),a6  ; Get address of right mask table
   move.l   0(a6,d1.w),d2     ; Get right bitmask
   add.l d0,d0       ; Scale XSrc bits to longword index
   add.l d0,d0       ; And again
contb2:
   lea   LeftMask(PC),a6      ; Get address of left mask table
   move.l   0(a6,d0.w),d1     ; Get left bitmask
;
;  Calculate minimum number of bitplanes to copy
;
   moveq.l  #0,d6       ; Zero out high bits of D6
   move.b   bm_Depth(a0),d6      ; Get depth of source bitmap
   cmp.b bm_Depth(a1),d6      ; If greater than that of dest bitmap
   blo.s contb3         ;
   move.b   bm_Depth(a1),d6      ; Use dest bitmap depth instead
contb3:
   subq.l   #1,d6       ; Adjust depth to 0-based, not 1-based
   move.l   d4,d0       ; Copy longword count
   addq.l   #1,d0       ; Adjust positively
   add.l d0,d0       ; Convert longword count to byte count
   add.l d0,d0       ;
   sub.l d0,a2       ; Calculate correct modulo for source
   sub.l d0,a3       ; Calculate correct modulo for dest.
   exg   a2,a4       ; Setup A2/A3 = bitmap offsets
   exg   a3,a5       ;  and  A4/A5 = bitmap modulos

   subq.l   #1,d4       ; Adjust longword count to zero-based
   move.l   d4,d3       ; Move to right reg for Copy routine
   move.l   d5,d0       ; Copy YSize to right place also
   lea.l bm_Planes(a0),a0  ; Get pointer to source bitplane array
   lea.l bm_Planes(a1),a1  ; Get pointer to dest bitplane array
   move.l   a0,d4       ; Stash bitplane pointers here
   move.l   a1,d5       ;
   move.l   20(sp),d7      ; Read plane mask value from stack
;
;  Now build a list of bitmaps to be copied on the stack. To this end,
;  we reserve 8 * 8 = 64 bytes of stack for source/destination bitmap
;  pointers.
;
   lea   -64(sp),sp     ; Reserve space for bitmap ptrs
   move.l   sp,a6       ; And point to it using A6
;
;  Loop through bitmaps building bitmap list for bitmaps specified in
;  the copy mask. Bitplanes which have source and/or destination bitmaps
;  set to NULL or -1 get handled immediately (new for WB 2.0). All others
;  get stored on the stack.
;
   move.w   d7,-(sp)    ; Save plane mask as temporary value
   moveq.l  #0,d7       ; Clear bitmap plane count
cmultlp:
   lsr.w (sp)        ; See if need to copy this bitplane
   bcc.s cmultx         ; If not, skip over code
   addq  #1,d7       ; Increment number of bitmaps copied
   move.l   d4,a0       ; Get pointer to source bitplane ptr
   move.l   d5,a1       ; And destination bitplane ptr
   move.l   (a0),d4        ; Read pointers to bitplanes
   move.l   (a1),d5        ;
   not.l d5       ; Check if dest is -1
   beq   skipfill    ; If so, don't copy anything
   not.l d5       ; Check if dest is zero
   beq   skipfill    ; If so, don't copy anything
   not.l d4       ; Check if source is -1
   beq   fillones    ; If so, fill dest with 1's
   not.l d4       ; Check if source is 0
   beq   fillzeros      ; If so, fill dest with 0's
   exg   d4,a0       ; Put registers back in right place
   exg   d5,a1       ;
   add.l a2,a0       ; Add in correct offset for src ptr
   add.l a3,a1       ; Add in correct offset for dest ptr
   move.l   a0,(a6)+    ; Store bitmap pointers on the stack
   move.l   a1,(a6)+    ;  
cmultx:
   addq.l   #4,d4       ; Bump bitplane pointers
   addq.l   #4,d5       ;
   dbf   d6,cmultlp     ; Repeat for remaining bitplanes
   addq.l   #2,sp       ; Pop plane mask from stack
;
;  Now copy all the bitmaps we accumulated on the stack. There will be
;  between 1 and 8 of them. We copy them in groups of 1 to 4, so two
;  operations may be required.
;
;  A quick recap on what the various registers contain:
;
;      D0 - Number of rows to copy
;      D1 - Mask for left edge of bitmap
;      D2 - Mask for right edge of bitmap
;      D3 - Number of longwords _between_ left edge and right edge
;      D7 - Total number of bitplanes copied (including 0 & -1 ptrs)
;      A4 - Modulo of source bitplanes
;      A5 - Modulo of dest bitplanes
;      A6 - Points to end of source/dest bitplane pointers
;      SP - Points to start of source/dest bitplane pointers
;
   sub.l sp,a6       ; Calculate how many bitplanes to copy
   move.l   a6,d6       ; Equals half # of source/dest pairs
   lsr.l #1,d6       ; (giving a range of 0-28)
   subq  #4,d6       ; Adjust to zero based
   bpl.s cmultx2        ; If negative, no bitplanes to copy
   lea   64(sp),sp      ; so pop bitplane pointers from stack
   bra   doneblt        ; and exit without doing any work
cmultx2:
   cmpi.w   #12,d6         ; More than 4 bitplanes to copy?
   bhi.s cmult_db    ; If so, skip to do in two goes
   move.l   d3,d3       ; Does bitmap fits in one longword?
   bpl.s cmult_mm    ; If not, skip to multiple longwords
;
;  We have between 1 and 4 bitplanes to copy, each a single
;  longword wide.
;
   and.l d2,d1       ; Create composite mask
   addq  #8,d6       ; Adjust to index CopySingle() entries
   addq  #8,d6       ; and then fall through.
;
;  We have between 1 and 4 bitplanes to copy, each at least two
;  longwords wide.
;
cmult_mm:
   move.l   FuncTab(pc,d6),a6 ; Else call appropriate routine
   jsr   FuncTab(pc,a6.L)        ;
   lea   64(sp),sp      ; Pop everything off the stack
   bra.b   doneblt        ; And skip to end of blit

cmult_db:
   move.l   d3,d3       ; Does bitplane fit in one longword?
   bpl.s cmult_dbm      ; If not, skip to multiple copy
;
;  We have between 5 and 8 bitplanes to copy, each just one
;  longword wide. Note that when we exit, we branch into the code to
;  copy the remaining bitmaps, but with the function index pointing
;  into the CopySingle() entries rather than CopyMultiple()
;
   and.l d2,d1       ; Create composite mask
   bsr   Copy4Single    ; Copy first four bitplanes
   bra.s cmult_dbm2     ; Skip to exit with correct fn index
;
;  We have between 5 and 8 bitplanes to copy, each at least two
;  longwords wide.
;
cmult_dbm:
   bsr   Copy4Multiple     ; Copy first four bitmaps in one gulp
   subi.w   #16,d6         ; Adjust bitmap count
cmult_dbm2:
   lea   32(sp),sp      ; Pop first four bitmaps off stack
   move.l   FuncTab(pc,d6),a6 ; Copy remaining bitmaps
   jsr   FuncTab(pc,a6.L)        ;
   lea   32(sp),sp      ; Pop remaining bitmaps
   bra.b   doneblt        ; And skip to end of blit

;
;  Index to table of functions for copying from 1 to 4 multiple and
;  single longword bitmaps.
;
FuncTab:
   dc.l  Copy1Multiple-FuncTab
   dc.l  Copy2Multiple-FuncTab
   dc.l  Copy3Multiple-FuncTab
   dc.l  Copy4Multiple-FuncTab
   dc.l  Copy1Single-FuncTab
   dc.l  Copy2Single-FuncTab
   dc.l  Copy3Single-FuncTab
   dc.l  Copy4Single-FuncTab

;
;  Skip past current bitplane without doing anything to bitplane data
;  (used when destination bitmap ptr is 0 or -1).
;
skipfill:
   exg   d4,a0       ; Restore original pointers
   exg   d5,a1       ;
   bra   cmultx         ; Skip back to do next bitplane
;
;  Fill bitplane with one's (source bitplane pointer is -1)
;
fillones:
   exg   d4,a0       ; Restore register order
   exg   d5,a1       ;
   add.l a3,a1       ; Add in correct offset into bitplane
   bsr   Fill_1s        ; Fill the bitplane
   bra   cmultx         ; Skip back to do next bitplane
;
;  Fill bitplane with zero's (source bitplane pointer is NULL)
;
fillzeros:
   exg   d4,a0       ; Restore register order
   exg   d5,a1       ;
   add.l a3,a1       ; Add in correct offset into bitplane
   bsr   Fill_0s        ; Fill the bitplane
   bra   cmultx         ; Skip back to do next bitplane
;
;  That's it -- we're done! Now just pop remaining values off the stack
;  and return to the caller with d0 = number of bitplanes copied.
;
doneblt:
   move.l   d7,d0       ; Set return value = #bitplanes copied
   movem.l  (sp)+,d1-d5/d7/a0-a6 ; Restore registers
   move.l   (sp)+,d6    ; And this one too
   rts            ; Return to caller

*****************************************************************************
*
*  CopyMultiple()
*
*  The following routines copy from 1 to 4 bitplanes which span more
*  than one longword boundary horizontally (i.e. the start and finish
*  bitplanes are in different longwords).
*
*  The routines are constructed mainly out of macros, to keep the source
*  code down to size (and also more manageable). All routines take the
*  following parameters:
*
*  Input:
*     D0 - Number of rows to copy
*     D1 - Mask for left edge of source   (000xxx)
*     D2 - Mask for right edge of source  (xxx000)
*     D3 - Number of longwords to copy
*          A4 - Modulo of source (positive or negative)
*          A5 - Modulo of destination (positive or negative)
*
*  In addition, pointers to the source/destination bitplanes are pushed
*  onto the stack, such that 4(SP) = src bp1, 8(SP) = dest bp1,
*  12(SP) = src bp2, 16(SP) = dest bp2 etc.
*
*  Output:
*     None
*
*****************************************************************************

*****************************************************************************
*
*  Macros used by the copy routines
*
*****************************************************************************

;-----------------------------------------------------------------------------
;  Init_Mult Label
;
;  This macro is the standard entry to each CopyMultiple() routine. It
;  checks to see whether the bitplane being copied contains at least
;  one full longword. If not, it branches to a separate routine
;  (loop?edges) which is smaller; doing this at the start saves having
;  to check for zero longwords each time through the main loop.
;  Label is the name of the routine to perform the separate copy.
;-----------------------------------------------------------------------------

Init_Mult macro
   subq.l   #1,d0       ; Convert row count to zero-based
   move.l   d1,d4       ; Copy left source mask
   not.l d4       ; And change it into destination mask
   move.l   d2,d5       ; Copy right source mask
   not.l d5       ; Change into destination mask
   subq.l   #1,d3       ; Adjust longword count to zero based
   bmi   \1       ; If none to copy use seperate routine
   endm

;-----------------------------------------------------------------------------
;  Left_Mult   src,dest
;
;  Copies the left hand side of the bitplane from register src to the
;  bitplane pointed to by dest, using the masks in d1/d4
;-----------------------------------------------------------------------------

Left_Mult macro
   move.l   (\1)+,d6    ; Read leftmost longword of source
   and.l d1,d6       ; Mask out bits not to be copied
   move.l   (\2),d7        ; Read leftmost longword of dest
   and.l d4,d7       ; Mask out bits to remain the same
   or.l  d6,d7       ; Merge source and dest columns
   move.l   d7,(\2)+    ; Output first word of bitplane again
   endm

;-----------------------------------------------------------------------------
;  Copy_Mult src,dest
;
;  Copies all the full longwords between the left and right extremities
;  of the bitplane row from src to dest. Note that for 68010 upwards, it
;  is faster to copy using MOVE.L/DBF than to play tricks with MOVEM;
;  since this program will only be of use to systems with fast CPU's
;  anyway, this is the route we take.
;-----------------------------------------------------------------------------

Copy_Mult macro
   move.l   d3,d6       ; Copy longword count into scratch reg
loop_m\@:
   move.l   (\1)+,(\2)+    ; Copy longwords
   dbf   d6,loop_m\@    ;
   endm

;-----------------------------------------------------------------------------
;  Right_Mult   src,dest
;
;  Copies the right hand side of the bitplane from register src to the
;  bitplane pointed to by dest, using the masks in d2/d5
;-----------------------------------------------------------------------------

Right_Mult macro
   move.l   (\1)+,d6    ; Read rightmost longword of source
   and.l d2,d6       ; Mask out bits not being copied
   move.l   (\2),d7        ; Read rightmost longword of dest
   and.l d5,d7       ; Mask out bits to remain the same
   or.l  d6,d7       ; Merge source and dest columns
   move.l   d7,(\2)+    ; Output right longword again
   endm

;-----------------------------------------------------------------------------
;  Advance src,dest
;
;  This macro advances the source and destination pointers to point to
;  the next row in the bitplane.
;-----------------------------------------------------------------------------

Advance macro
   add.l a4,\1       ; Increment source pointer
   add.l a5,\2       ; Increment dest pointer
   endm

;-----------------------------------------------------------------------------
;  Copy_Quick src,dest
;
;  This macro copies the left and right edges in one go, when there
;  are no complete longwords in between. It's quicker than having to
;  check for zero longwords each time through the main loop. The masks
;  used are d1/d4 for the left edge of the bitplane, d2/d5 for the
;  right edge.
;-----------------------------------------------------------------------------

Copy_Quick macro
   move.l   (\1)+,d6    ; Read leftmost longword of source
   and.l d1,d6       ; Mask out bits not to be copied
   move.l   (\2),d7        ; Read leftmost longword of dest
   and.l d4,d7       ; Mask out bits to remain the same
   or.l  d6,d7       ; Merge source and dest columns
   move.l   d7,(\2)+    ; Output first word of bitplane again
;
;  Now tidy up right hand edge of bitplane
;
   move.l   (\1)+,d6    ; Read rightmost longword of source
   and.l d2,d6       ; Mask out bits not being copied
   move.l   (\2),d7        ; Read rightmost longword of dest
   and.l d5,d7       ; Mask out bits to remain the same
   or.l  d6,d7       ; Merge source and dest columns
   move.l   d7,(\2)+    ; Output right longword again
   endm

*****************************************************************************
*
*  The actual copy routines, Copy1Multiple() ... Copy4Multiple()
*
*****************************************************************************

;-----------------------------------------------------------------------------
;
;  Copies a single bitplane
;
;-----------------------------------------------------------------------------

Copy1Multiple:
   movem.l  a0-a1/a6/d0-d7,-(sp) ; Save registers
   lea.l    48(sp),a6   ; Get pointer to stack
   move.l      (a6)+,a0 ; Read bitplane pointers from stack
   move.l      (a6)+,a1 ; Read bitplane pointers from stack
   Init_Mult   Copy1Quick  ; Setup registers
c1m_loop:
   Left_Mult   a0,a1    ; Copy left edge of bitplane
   Copy_Mult   a0,a1    ; Copy middle of bitplane
   Right_Mult  a0,a1    ; Copy right edge of bitplane
   Advance     a0,a1    ; Increment bitplane ptrs
   dbf      d0,c1m_loop ; Repeat for remaining rows
   movem.l  (sp)+,a0-a1/a6/d0-d7 ; Restore registers
   rts            ; Return to caller
;
;  Handle inner longword count of zero
;
Copy1Quick:
   Copy_Quick  a0,a1    ; Copy left/right edge of bitplane
   Advance     a0,a1    ; Increment bitplane ptrs
   dbra     d0,Copy1Quick  ; Repeat for all rows
   movem.l  (sp)+,a0-a1/a6/d0-d7 ; Restore registers
   rts            ; Return to caller

;-----------------------------------------------------------------------------
;
;  Copies 2 bitplanes simultaneously
;
;-----------------------------------------------------------------------------

Copy2Multiple:
   movem.l  a0-a3/a6/d0-d7,-(sp) ; Save registers
   lea.l    56(sp),a6   ; Get pointer to bitplanes
   movem.l     (a6),a0-a3  ; Load bitplane ptrs off stack
   Init_Mult   Copy2Quick  ; Setup registers
c2m_loop:
   Left_Mult   a0,a1    ; Copy left edge of bitplane 1
   Left_Mult   a2,a3    ; Copy left edge of bitplane 2
   Copy_Mult   a0,a1    ; Copy middle of bitplane 1
   Copy_Mult   a2,a3    ; Copy middle of bitplane 2
   Right_Mult  a0,a1    ; Copy right edge of bitplane 1
   Right_Mult  a2,a3    ; Copy right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   dbf      d0,c2m_loop ; Repeat for remaining rows
   movem.l  (sp)+,a0-a3/a6/d0-d7 ; Restore registers
   rts            ; Return to caller
;
;  Handle inner longword count of zero
;
Copy2Quick:
   Copy_Quick  a0,a1    ; Copy left/right edge of bitplane 1
   Copy_Quick  a2,a3    ; Copy left/right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   dbra     d0,Copy2Quick  ; Repeat for all rows
   movem.l  (sp)+,a0-a3/a6/d0-d7 ; Restore registers
   rts            ; Return to caller

;-----------------------------------------------------------------------------
;
;  Copies 3 bitplanes simultaneously
;
;-----------------------------------------------------------------------------

Copy3Multiple:
   movem.l  a0-a3/a6/d0-d7,-(sp) ; Save registers
   lea.l    56(sp),a6   ; Get pointer to bitplanes
   movem.l     (a6)+,a0-a3 ; Load bitplane ptrs 1 & 2 off stack
   Init_Mult   Copy3Quick  ; Setup registers
c3m_loop:
   Left_Mult   a0,a1    ; Copy left edge of bitplane 1
   Left_Mult   a2,a3    ; Copy left edge of bitplane 2
   Copy_Mult   a0,a1    ; Copy middle of bitplane 1
   Copy_Mult   a2,a3    ; Copy middle of bitplane 2
   Right_Mult  a0,a1    ; Copy right edge of bitplane 1
   Right_Mult  a2,a3    ; Copy right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   move.l      a3,-(sp) ; Save bitplane 2 ptrs
   move.l      a2,-(sp) ;
   move.l      (a6)+,a2 ; Load bitplane 3 ptrs
   move.l      (a6),a3     ; 
   Left_Mult   a2,a3    ; Copy left edge of bitplane 3
   Copy_Mult   a2,a3    ; Copy middle of bitplane 3
   Right_Mult  a2,a3    ; Copy right edge of bitplane 3
   Advance     a2,a3    ; Increment bitplane 3 ptrs
   move.l      a3,(a6)     ; Save bitplane 3 ptrs
   move.l      a2,-(a6) ;
   move.l      (sp)+,a2 ; Restore bitplane 2 ptrs
   move.l      (sp)+,a3 ;
   dbf      d0,c3m_loop ; Repeat for remaining rows
   movem.l  (sp)+,a0-a3/a6/d0-d7 ; Restore registers
   rts            ; Return to caller
;
;  Handle inner longword count of zero
;
Copy3Quick:
   Copy_Quick  a0,a1    ; Copy left/right edge of bitplane 1
   Copy_Quick  a2,a3    ; Copy left/right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   move.l      a3,-(sp) ; Save bitplane 2 ptrs
   move.l      a2,-(sp) ;
   move.l      (a6)+,a2 ; Load bitplane 3 ptrs
   move.l      (a6),a3     ; 
   Copy_Quick  a2,a3    ; Copy left/right edge of bitplane 2
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   move.l      a3,(a6)     ; Save bitplane 3 ptrs
   move.l      a2,-(a6) ;
   move.l      (sp)+,a2 ; Restore bitplane 2 ptrs
   move.l      (sp)+,a3 ;
   dbra     d0,Copy3Quick  ; Repeat for all rows
   movem.l  (sp)+,a0-a3/a6/d0-d7 ; Restore registers
   rts            ; Return to caller

;-----------------------------------------------------------------------------
;
;  Copies 4 bitplanes simultaneously
;
;-----------------------------------------------------------------------------

Copy4Multiple:
   movem.l  a0-a3/a6/d0-d7,-(sp) ; Save registers
   lea.l    56(sp),a6   ; Get pointer to bitplanes
   movem.l     (a6)+,a0-a3 ; Load bitplane ptrs 1 & 2 off stack
   Init_Mult   Copy4Quick  ; Setup registers
c4m_loop:
   Left_Mult   a0,a1    ; Copy left edge of bitplane 1
   Left_Mult   a2,a3    ; Copy left edge of bitplane 2
   Copy_Mult   a0,a1    ; Copy middle of bitplane 1
   Copy_Mult   a2,a3    ; Copy middle of bitplane 2
   Right_Mult  a0,a1    ; Copy right edge of bitplane 1
   Right_Mult  a2,a3    ; Copy right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   movem.l     a0-a3,-(sp) ; Save bitplane 2 ptrs
   movem.l     (a6),a0-a3  ; Load bitplane 3 ptrs
   Left_Mult   a0,a1    ; Copy left edge of bitplane 1
   Left_Mult   a2,a3    ; Copy left edge of bitplane 2
   Copy_Mult   a0,a1    ; Copy middle of bitplane 1
   Copy_Mult   a2,a3    ; Copy middle of bitplane 2
   Right_Mult  a0,a1    ; Copy right edge of bitplane 1
   Right_Mult  a2,a3    ; Copy right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   movem.l     a0-a3,(a6)  ; Save bitplane 3 ptrs
   movem.l     (sp)+,a0-a3 ; Restore bitplane 2 ptrs
   dbf      d0,c4m_loop ; Repeat for remaining rows
   movem.l  (sp)+,a0-a3/a6/d0-d7 ; Restore registers
   rts            ; Return to caller
;
;  Handle inner longword count of zero
;
Copy4Quick:
   Copy_Quick  a0,a1    ; Copy left/right edge of bitplane 1
   Copy_Quick  a2,a3    ; Copy left/right edge of bitplane 2
   Advance     a0,a1    ; Increment bitplane 1 ptrs
   Advance     a2,a3    ; Increment bitplane 2 ptrs
   movem.l     a0-a3,-(sp) ; Save bitplane 1,2 ptrs
   movem.l     (a6),a0-a3  ; Load bitplane 3,4 ptrs
   Copy_Quick  a0,a1    ; Copy left/right edge of bitplane 3
   Copy_Quick  a2,a3    ; Copy left/right edge of bitplane 4
   Advance     a0,a1    ; Increment bitplane 3 ptrs
   Advance     a2,a3    ; Increment bitplane 4 ptrs
   movem.l     a0-a3,(a6)  ; Save bitplane 3,4 ptrs
   movem.l     (sp)+,a0-a3 ; Restore bitplane 1,2 ptrs
   dbra     d0,Copy4Quick  ; Repeat for all rows
   movem.l  (sp)+,a0-a3/a6/d0-d7 ; Restore registers
   rts            ; Return to caller

*****************************************************************************
*
*  CopySingle()
*
*  The following routines copy from 1 to 4 bitplanes that start and end
*  (horizontally) within a single longword. CopyMultiple can't be used
*  for such cases, since it always copies at least two longwords (one
*  for the left edge and one for the right).
*
*  Input:
*     D0 - Number of rows to copy
*     D1 - Mask of bits to be copied from source  (000xxx000)
*     A4 - Modulo of source bitplane
*     A5 - Modulo of dest bitplane
*
*  In addition, pointers to the source/destination bitplanes are pushed
*  onto the stack, such that 4(SP) = src bp1, 8(SP) = dest bp1,
*  12(SP) = src bp2, 16(SP) = dest bp2 etc.
*
*  Output:
*     None
*
*****************************************************************************

*****************************************************************************
*
*  Macros used by the copy routines
*
*****************************************************************************

;-----------------------------------------------------------------------------
;  Init_Sing
;
;  This macro is the standard entry to each CopySingle() routine. It
;  creates the complement mask used for masking source/destination
;  and adjusts the row counter to be zero based.
;-----------------------------------------------------------------------------

Init_Sing macro
   subq.l   #1,d0       ; Adjust row count to zero-based
   move.l   d1,d2       ; Copy mask
   not.l d2       ; And make mask for dest bitplane
   endm

;-----------------------------------------------------------------------------
;  Copy_Dual src,dest
;
;  Copies the source longword from src to dest, masked with the value
;  in D2/D4
;-----------------------------------------------------------------------------

Copy_Dual macro
   move.l   (\1)+,d3    ; Read src word
   and.l d1,d3       ; Mask out unwanted bits
   move.l   (\2),d4        ; Read dest word
   and.l d2,d4       ; Mask out bits to be replaced
   or.l  d3,d4       ; Combine src and dest bits
   move.l   d4,(\2)+    ; Replace destination word
   endm

*****************************************************************************
*
*  The actual copy routines, Copy1Single() ... Copy4Single()
*
*****************************************************************************

;-----------------------------------------------------------------------------
;
;  Copies a single bitplane one longword wide
;
;-----------------------------------------------------------------------------

Copy1Single:
   movem.l  a0-a1/a6/d0-d4,-(sp) ; Save registers
   lea      36(sp),a6   ; Get pointer to bitplane
   move.l      (a6)+,a0 ; Get bitplane pointers into registers
   move.l      (a6),a1     ;
   Init_Sing         ; Initialise masks etc.
copy1slp:
   Copy_Dual   a0,a1    ; Copy longword
   Advance     a0,a1    ; Move to next longword
   dbra     d0,copy1slp ; Repeat for all rows
   movem.l  (sp)+,a0-a1/a6/d0-d4 ; Restore registers
   rts

;-----------------------------------------------------------------------------
;
;  Copies two bitplanes, each one longword wide
;
;-----------------------------------------------------------------------------

Copy2Single:
   movem.l  a0-a3/a6/d0-d4,-(sp) ; Save registers
   lea      44(sp),a6   ; Get ptr to bitplane
   movem.l     (a6)+,a0-a3 ; Get bitplane ptrs into registers
   Init_Sing         ; Initialise masks etc.
copy2slp:
   Copy_Dual   a0,a1    ; Copy longword for bitplane 1
   Copy_Dual   a2,a3    ; Copy longword for bitplane 2
   Advance     a0,a1    ; Advance bitplane 1 ptrs
   Advance     a2,a3    ; Advance bitplane 2 ptrs
   dbra     d0,copy2slp ; Repeat for all rows
   movem.l  (sp)+,a0-a3/a6/d0-d4 ; Restore registers
   rts

;-----------------------------------------------------------------------------
;
;  Copies three bitplanes, each one longword wide
;
;-----------------------------------------------------------------------------

Copy3Single:
   movem.l  a0-a3/a6/d0-d4,-(sp) ; Save registers
   lea      44(sp),a6   ; Get pointer to bitplane
   movem.l     (a6)+,a0-a3 ; Get bitplane ptrs into registers
   Init_Sing         ; Initialise masks etc.
copy3slp:
   Copy_Dual   a0,a1    ; Copy longword for bitplane 1
   Copy_Dual   a2,a3    ; Copy longword for bitplane 2
   Advance     a0,a1    ; Advance bitplane 1 ptrs
   Advance     a2,a3    ; Advance bitplane 2 ptrs
   move.l      a1,-(sp) ; Save bitplane 2 ptrs
   move.l      a0,-(sp) ;
   move.l      (a6)+,a0 ; Load bitplane 3 ptrs
   move.l      (a6),a1     ; 
   Copy_Dual   a0,a1    ; Copy longword for bitplane 3
   Advance     a0,a1    ; Advance bitplane 3 ptrs
   move.l      a1,(a6)     ; Save bitplane 3 ptrs
   move.l      a0,-(a6) ;
   move.l      (sp)+,a0 ; Restore bitplane 2 ptrs
   move.l      (sp)+,a1 ;
   dbra     d0,copy3slp ; Repeat for all rows
   movem.l  (sp)+,a0-a3/a6/d0-d4 ; Restore registers
   rts

;-----------------------------------------------------------------------------
;
;  Copies four bitplanes, each one longword wide
;
;-----------------------------------------------------------------------------

Copy4Single:
   movem.l  a0-a3/a6/d0-d4,-(sp) ; Save registers
   lea      44(sp),a6   ; Get pointer to bitplane pointers
   movem.l     (a6)+,a0-a3 ; Get bitplane pointers into registers
   Init_Sing         ; Initialise masks etc.
copy4slp:
   Copy_Dual   a0,a1    ; Copy longword for bitplane 1
   Copy_Dual   a2,a3    ; Copy longword for bitplane 2
   Advance     a0,a1    ; Advance bitplane 1 ptrs
   Advance     a2,a3    ; Advance bitplane 2 ptrs
   movem.l     a0-a3,-(sp) ; Save bitplane 1 and 2 ptrs on stack
   movem.l     (a6),a0-a3  ; Read bitplane 3 and 4 ptrs
   Copy_Dual   a0,a1    ; Copy longword for bitplane 3
   Copy_Dual   a2,a3    ; Copy longword for bitplane 4
   Advance     a0,a1    ; Advance bitplane 3 ptrs
   Advance     a2,a3    ; Advance bitplane 4 ptrs
   movem.l     a0-a3,(a6)  ; Save bitplane 3 and 4 ptrs
   movem.l     (sp)+,a0-a3 ; Restore bitplane 1 and 2 ptrs
   dbra     d0,copy4slp ; Repeat for all rows
   movem.l  (sp)+,a0-a3/a6/d0-d4 ; Restore registers
   rts

*****************************************************************************
*
*  Fill_1s(), Fill_0s
*
*  Handles the case new for Workbench 2.0 where the source bitplane
*  pointer points to an array of all ones (ptr = $FFFFFFFF) or all
*  zeros ($ptr = $00000000).
*
*      Input:
*     D0 - Number of rows to copy
*     D1 - Mask for left edge of source   (000xxx)
*     D2 - Mask for right edge of source  (xxx000)
*     D3 - Number of longwords to copy (-1 means single column)
*     A1 - Pointer to dest bitplane
*     A5 - Modulo of dest bitplane
*
*      Output:
*     None
*
*****************************************************************************

Fill_1s:
   movem.l  d0/d3/d6-d7/a1,-(sp) ; Save registers
   moveq.l  #-1,d7         ; Set register ready for fills
   subq.l   #1,d0       ; Adjust row count to zero-based
   move.l   d3,d3       ; Check how many longwords to copy
   bmi.s Fill_1single      ; Branch if only a single longword
   subq  #1,d3       ; Adjust longword count to zero based
   bmi.s Fill_1quick    ; If no complete longwords, handle
;
;  We have more than two longwords to copy, so loop over them all.
;
fill_1lp1:
   or.l  d1,(a1)+    ; Set bits on left edge of bitplane
   move.l   d3,d6       ; Get number of longwords to fill
fill_1lp2:
   move.l   d7,(a1)+    ; Fill all the longwords
   dbra  d6,fill_1lp2      ;
   or.l  d2,(a1)+    ; Set bits on right edge of bitplane
   add.l a5,a1       ; Advance to next bitplane row
   dbra  d0,fill_1lp1      ; And repeat until done
   movem.l  (sp)+,d0/d3/d6-d7/a1 ; Restore registers
   rts            ; Return to caller
;
;  Only two longwords to copy, the left and right edges
;
Fill_1quick:
   or.l  d1,(a1)+    ; Set bits on left edge of bitplane
   or.l  d2,(a1)+    ; Set bits on right edge of bitplane
   add.l a5,a1       ; Move to next row
   dbra  d0,Fill_1quick    ; Repeat for all rows
   movem.l  (sp)+,d0/d3/d6-d7/a1 ; Restore registers
   rts            ; Return to caller
;
;  Only a single longword to copy, with left and right portions masked
;
Fill_1single:
   move.l   d1,d6       ; Create new mask
   and.l d2,d6       ; by combining left and right masks
Fill_1s2:
   or.l  d6,(a1)+    ; Fill longword
   add.l a5,a1       ; Advance to next row
   dbra  d0,Fill_1s2    ; Repeat for all rows
   movem.l  (sp)+,d0/d3/d6-d7/a1 ; Restore registers
   rts            ; Return to caller

;-----------------------------------------------------------------------------
;  Clear bitplane with zeros
;-----------------------------------------------------------------------------

Fill_0s:
   movem.l  d0-d3/d6-d7/a1,-(sp) ; Save registers
   not.l d1       ; Invert masks ready for AND
   not.l d2       ;
   moveq.l  #0,d7       ; Clear register ready for fills
   subq.l   #1,d0       ; Adjust row count to zero-based
   move.l   d3,d3       ; Check how many longwords to copy
   bmi.s Fill_0single      ; Branch if only a single longword
   subq  #1,d3       ; Adjust longword count to zero based
   bmi.s Fill_0quick    ; If no complete longwords, handle
;
;  We have more than two longwords to copy, so loop over them all.
;
fill_0lp1:
   and.l d1,(a1)+    ; Set bits on left edge of bitplane
   move.l   d3,d6       ; Get number of longwords to fill
fill_0lp2:
   move.l   d7,(a1)+    ; Fill all the longwords
   dbra  d6,fill_0lp2      ;
   and.l d2,(a1)+    ; Set bits on right edge of bitplane
   add.l a5,a1       ; Advance to next bitplane row
   dbra  d0,fill_0lp1      ; And repeat until done
   movem.l  (sp)+,d0-d3/d6-d7/a1 ; Restore registers
   rts            ; Return to caller
;
;  Only two longwords to copy, the left and right edges
;
Fill_0quick:
   and.l d1,(a1)+    ; Clear left edge of bitplane
   and.l d2,(a1)+    ; Clear right edge of bitplane
   add.l a5,a1       ; Move to next row
   dbra  d0,Fill_0quick    ; Repeat for all rows
   movem.l  (sp)+,d0-d3/d6-d7/a1 ; Restore registers
   rts            ; Return to caller
;
;  Only a single longword to copy, with left and right portions masked
;
Fill_0single:
   move.l   d1,d6       ; Combine left and right edges
   or.l  d2,d6       ; to create new mask
Fill_0s2:
   and.l d6,(a1)+    ; Fill longword
   add.l a5,a1       ; Advance to next row
   dbra  d0,Fill_0s2    ; Repeat for all rows
   movem.l  (sp)+,d0-d3/d6-d7/a1 ; Restore registers
   rts            ; Return to caller

*****************************************************************************
*
*  These two tables give the mask values used when copying the
*  bits at the edge of each bitplane row. Note that a right edge
*  of zero bits in width is handled as a special case in the code
*  (it gets converted to a bitmap which is one longword narrower
*  but has a right edge 32 bits wide).
*
*****************************************************************************

LeftMask:
   dc.l  $ffffffff,$7fffffff,$3fffffff,$1fffffff
   dc.l  $0fffffff,$07ffffff,$03ffffff,$01ffffff
   dc.l  $00ffffff,$007fffff,$003fffff,$001fffff
   dc.l  $000fffff,$0007ffff,$0003ffff,$0001ffff
   dc.l  $0000ffff,$00007fff,$00003fff,$00001fff
   dc.l  $00000fff,$000007ff,$000003ff,$000001ff
   dc.l  $000000ff,$0000007f,$0000003f,$0000001f
   dc.l  $0000000f,$00000007,$00000003,$00000001

RightMask:
   dc.l  $ffffffff,$80000000,$c0000000,$e0000000
   dc.l  $f0000000,$f8000000,$fc000000,$fe000000
   dc.l  $ff000000,$ff800000,$ffc00000,$ffe00000
   dc.l  $fff00000,$fff80000,$fffc0000,$fffe0000
   dc.l  $ffff0000,$ffff8000,$ffffc000,$ffffe000
   dc.l  $fffff000,$fffff800,$fffffc00,$fffffe00
   dc.l  $ffffff00,$ffffff80,$ffffffc0,$ffffffe0
   dc.l  $fffffff0,$fffffff8,$fffffffc,$fffffffe






