/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      256 color sprite drawing (written for speed, not readability :-)
 *
 *      By Shawn Hargreaves.
 *
 *      draw_sprite() and draw_trans_sprite() optimised by Erik Sandberg.
 *
 *      See readme.txt for copyright information.
 */


#include "asmdefs.inc"
#include "sprite.inc"

#ifdef ALLEGRO_COLOR8

.text



/* void _linear_draw_sprite8(BITMAP *bmp, BITMAP *sprite, int x, y);
 *  Draws a sprite onto a linear bitmap at the specified x, y position, 
 *  using a masked drawing mode where zero pixels are not output.
 */
FUNC(_linear_draw_sprite8)

   /* loop for sprites more than 7 pixels wide */
   /* dest: destination seg. */
   /* name: the start of the label names */
   /* init_edi: code to init edi every line */

   #define LINEAR_SPRITE_LOOP(dest, name, init_edi)                          \
      movl S_Y, %eax                         /* load line */               ; \
      movl S_W, %edx                         /* convert x loop counter */  ; \
      addl %edx, S_SGAP                      /* increase sprite gap */     ; \
      subl $4, %edx                          /* less iterations */         ; \
      addl %eax, S_H                         /* S_H = end y */             ; \
      addl %edx, %esi                        /* end of src line in esi */  ; \
      movl S_X, %edi                         /* edi = left edge of dest */ ; \
      addl %edx, %edi                        /* add width to left edge */  ; \
      movl %edi, S_X                         /* store right edge */        ; \
      negl %edx                              /* counter/offset negative */ ; \
      movl %edx, S_W                         /* store converted counter */ ; \
									   ; \
   sprite_y_loop_##name:                                                   ; \
      movl S_BMP, %edx                       /* load bitmap pointer */     ; \
      pushl %ebp                                                           ; \
      movl S_W, %ebp                         /* x loop counter */          ; \
      init_edi                                                             ; \
      leal (%esi, %ebp), %eax                /* check unaligned bytes */   ; \
      testb $1, %al                                                        ; \
      jz sprite_##name##_before_waligned                                   ; \
      movb (%esi, %ebp), %bl                 /* write unaligned byte */    ; \
      incl %ebp                                                            ; \
      testb %bl, %bl                                                       ; \
      jz sprite_##name##_before_waligned                                   ; \
      movb %bl, dest -1(%edi, %ebp)                                        ; \
   sprite_##name##_before_waligned:                                        ; \
      incb %al                                                             ; \
      testb $2, %al                                                        ; \
      jz sprite_##name##_before_laligned                                   ; \
      movw (%esi, %ebp), %bx                 /* write unaligned word */    ; \
      addl $2, %ebp                                                        ; \
      testb %bl, %bl                                                       ; \
      jz sprite_##name##_before_lalign_skip_0                              ; \
      movb %bl, dest -2(%edi, %ebp)                                        ; \
   sprite_##name##_before_lalign_skip_0:                                   ; \
      testb %bh, %bh                                                       ; \
      jz sprite_##name##_before_laligned                                   ; \
      movb %bh, dest -1(%edi, %ebp)                                        ; \
   sprite_##name##_before_laligned:          /* now src is long-aligned */ ; \
                                                                           ; \
   sprite_x_loop_##name##_begin:             /* write longs */             ; \
      movl (%esi, %ebp), %eax                                              ; \
      testl %eax, %eax                                                     ; \
      jz sprite_x_loop_##name##_skip_all     /* skip empty long */         ; \
      leal -0x01010101(%eax), %ebx                                         ; \
      xorl %eax, %ebx                                                      ; \
      andl $0x80808080, %ebx                                               ; \
      jnz sprite_x_loop_##name##_not_full                                  ; \
                                                                           ; \
   sprite_x_loop_##name##_full:              /* write long */              ; \
      movl %eax, dest (%edi, %ebp)                                         ; \
                                                                           ; \
      addl $4, %ebp                          /* move to next long */       ; \
      jle sprite_x_loop_##name##_begin                                     ; \
      jmp sprite_x_loop_##name##_end                                       ; \
                                                                           ; \
   sprite_x_loop_##name##_not_full:                                        ; \
      testb %al, %al                                                       ; \
      jz sprite_x_loop_##name##_skip_0       /* skip byte #0 */            ; \
      movb %al, dest (%edi, %ebp)            /* write byte #0 */           ; \
                                                                           ; \
   sprite_x_loop_##name##_skip_0:                                          ; \
      testb %ah, %ah                                                       ; \
      jz sprite_x_loop_##name##_skip_1       /* skip byte #1 */            ; \
      movb %ah, dest 1(%edi, %ebp)           /* write byte #1 */           ; \
                                                                           ; \
   sprite_x_loop_##name##_skip_1:                                          ; \
      shrl $16, %eax                                                       ; \
      testb %al, %al                                                       ; \
      jz sprite_x_loop_##name##_skip_2       /* skip byte #2 */            ; \
      movb %al, dest 2(%edi, %ebp)           /* write byte #2 */           ; \
                                                                           ; \
   sprite_x_loop_##name##_skip_2:                                          ; \
      testb %ah, %ah                                                       ; \
      jz sprite_x_loop_##name##_skip_3       /* skip byte #3 */            ; \
      movb %ah, dest 3(%edi, %ebp)           /* write byte #3 */           ; \
                                                                           ; \
   sprite_x_loop_##name##_skip_3:                                          ; \
   sprite_x_loop_##name##_skip_all:                                        ; \
      addl $4, %ebp                          /* move to next long */       ; \
      jle sprite_x_loop_##name##_begin                                     ; \
                                                                           ; \
   sprite_x_loop_##name##_end:               /* write leftover data */     ; \
      movl %ebp, %eax                                                      ; \
      testb $1, %al                          /* test for leftover byte */  ; \
      jz sprite_##name##_after_waligned                                    ; \
      movb (%esi, %ebp), %bl                                               ; \
      incl %ebp                                                            ; \
      testb %bl, %bl                                                       ; \
      jz sprite_##name##_after_waligned                                    ; \
      movb %bl, dest -1(%edi, %ebp)          /* write leftover byte */     ; \
                                                                           ; \
   sprite_##name##_after_waligned:                                         ; \
      incb %al                                                             ; \
      testb $2, %al                          /* test for leftover word */  ; \
      jz sprite_##name##_after_laligned                                    ; \
      movw (%esi, %ebp), %bx                                               ; \
      testb %bl, %bl                                                       ; \
      jz sprite_##name##_after_lalign_skip_0 /* skip byte #0 */            ; \
      movb %bl, dest (%edi, %ebp)            /* write byte #0 */           ; \
                                                                           ; \
   sprite_##name##_after_lalign_skip_0:                                    ; \
      testb %bh, %bh                                                       ; \
      jz sprite_##name##_after_lalign_skip_1 /* skip byte #1 */            ; \
      movb %bh, dest 1(%edi, %ebp)           /* write byte #1 */           ; \
                                                                           ; \
   sprite_##name##_after_lalign_skip_1:                                    ; \
   sprite_##name##_after_laligned:                                         ; \
      popl %ebp                              /* need stack variables */    ; \
      movl S_Y, %eax                         /* y counter */               ; \
      incl %eax                                                            ; \
      movl S_X, %edi                         /* reload dest x position */  ; \
      movl %eax, S_Y                         /* store counter */           ; \
      addl S_SGAP, %esi                      /* skip sprite bytes */       ; \
      cmpl S_H, %eax                         /* check if complete */       ; \
      jl sprite_y_loop_##name


   /* draws a tiny sprite (width < 8) onto a video or memory bitmap */
   #define LOOP_TINY                                                         \
      movl S_Y, %eax                         /* load line */               ; \
      movl S_W, %edx                         /* convert x loop counter */  ; \
      addl %edx, S_SGAP                      /* increase sprite gap */     ; \
      addl %eax, S_H                         /* S_H = end y */             ; \
      addl %edx, %esi                        /* end of src line in esi */  ; \
      movl S_X, %edi                         /* edi = left edge of dest */ ; \
      addl %edx, %edi                        /* add width to left edge */  ; \
      decl %edi                                                            ; \
      movl %edi, S_X                         /* store right edge */        ; \
      negl %edx                              /* counter/offset negative */ ; \
      movl %edx, S_W                         /* store converted counter */ ; \
   sprite_y_loop_tiny:                                                     ; \
      movl S_BMP, %edx                                                     ; \
      movl S_Y, %eax                         /* load line */               ; \
      WRITE_BANK()                           /* select bank */             ; \
      addl S_X, %eax                         /* add x offset */            ; \
      movl S_W, %ecx                         /* x loop counter */          ; \
      movb (%esi, %ecx), %bl                                               ; \
      jmp sprite_tiny_next_pixel                                           ; \
   sprite_tiny_write_pixel:                                                ; \
      movb %bl, %es:(%eax, %ecx)                                           ; \
      movb (%esi, %ecx), %bl                                               ; \
      jz sprite_tiny_line_complete                                         ; \
   sprite_tiny_next_pixel:                                                 ; \
      cmpb $1, %bl                                                         ; \
      incl %ecx                                                            ; \
      jnc sprite_tiny_write_pixel                                          ; \
      movb (%esi, %ecx), %bl                                               ; \
      jnz sprite_tiny_next_pixel                                           ; \
   sprite_tiny_line_complete:                                              ; \
      movl S_Y, %eax                         /* y counter */               ; \
      addl S_SGAP, %esi                      /* skip sprite bytes */       ; \
      incl %eax                                                            ; \
      movl %eax, S_Y                         /* store counter */           ; \
      cmpl S_H, %eax                         /* check if complete */       ; \
      jl sprite_y_loop_tiny


   /* the actual sprite drawing routine */
   START_SPRITE_DRAW(sprite)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   subl S_W, %eax                /* - w */
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   movl BMP_LINE(%esi, %eax, 4), %esi
   addl S_LGAP, %esi             /* esi = sprite data ptr */

   cmpl $8, S_W
   jl sprite_tiny                /* sprite width < 8, tiny */

   movl S_BMP, %eax              /* check if dest is video memory */
   testl $(BMP_ID_VIDEO | BMP_ID_SYSTEM), BMP_ID(%eax)
   jnz sprite_video

				 /* dest = memory bitmap */
   #define INIT_EDI_CODE  \
     addl BMP_LINE(%edx, %eax, 4), %edi
   LINEAR_SPRITE_LOOP(/**/ , mem,  INIT_EDI_CODE)
   #undef INIT_EDI_CODE
   jmp sprite_done

sprite_video:                    /* dest = video memory bitmap */
   #define INIT_EDI_CODE  \
     WRITE_BANK();        \
     addl %eax, %edi 
   LINEAR_SPRITE_LOOP(%es: , video, INIT_EDI_CODE)
   #undef INIT_EDI_CODE
   jmp sprite_done

sprite_tiny:
   LOOP_TINY

   _align_
sprite_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_sprite8() */

FUNC(_linear_draw_sprite8_end)
   ret




/* void _linear_draw_sprite_v_flip8(BITMAP *bmp, BITMAP *sprite, int x, y);
 *  Draws a sprite to a linear bitmap, flipping vertically.
 */
FUNC(_linear_draw_sprite_v_flip8)

   START_SPRITE_DRAW(sprite_v_flip)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   addl S_W, %eax                /* + w */
   negl %eax
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   negl %eax                     /* - tgap */
   addl BMP_H(%esi), %eax        /* + sprite->h */
   decl %eax
   movl BMP_LINE(%esi, %eax, 4), %esi
   addl S_LGAP, %esi             /* esi = sprite data ptr */

   _align_
   SPRITE_LOOP(v_flip) 
   movb (%esi), %bl              /* read pixel */
   testb %bl, %bl                /* test */
   jz sprite_v_flip_skip 
   movb %bl, %es:(%eax)          /* write */
sprite_v_flip_skip: 
   incl %esi 
   incl %eax 
   SPRITE_END_X(v_flip)
   SPRITE_END_Y(v_flip)

sprite_v_flip_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_sprite_v_flip8() */




/* void _linear_draw_sprite_h_flip8(BITMAP *bmp, BITMAP *sprite, int x, y);
 *  Draws a sprite to a linear bitmap, flipping horizontally.
 */
FUNC(_linear_draw_sprite_h_flip8)

   START_SPRITE_DRAW(sprite_h_flip)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   addl S_W, %eax                /* + w */
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   movl BMP_W(%esi), %ecx 
   movl BMP_LINE(%esi, %eax, 4), %esi
   addl %ecx, %esi
   subl S_LGAP, %esi 
   decl %esi                     /* esi = sprite data ptr */

   _align_
   SPRITE_LOOP(h_flip) 
   movb (%esi), %bl              /* read pixel */
   testb %bl, %bl                /* test  */
   jz sprite_h_flip_skip 
   movb %bl, %es:(%eax)          /* write */
sprite_h_flip_skip: 
   decl %esi 
   incl %eax 
   SPRITE_END_X(h_flip)
   SPRITE_END_Y(h_flip)

sprite_h_flip_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_sprite_h_flip8() */




/* void _linear_draw_sprite_vh_flip8(BITMAP *bmp, BITMAP *sprite, int x, y);
 *  Draws a sprite to a linear bitmap, flipping both vertically and horizontally.
 */
FUNC(_linear_draw_sprite_vh_flip8)

   START_SPRITE_DRAW(sprite_vh_flip)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   subl S_W, %eax                /* - w */
   negl %eax
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   negl %eax                     /* - tgap */
   addl BMP_H(%esi), %eax        /* + sprite->h */
   decl %eax
   movl BMP_W(%esi), %ecx 
   movl BMP_LINE(%esi, %eax, 4), %esi
   addl %ecx, %esi
   subl S_LGAP, %esi 
   decl %esi                     /* esi = sprite data ptr */

   _align_
   SPRITE_LOOP(vh_flip) 
   movb (%esi), %bl              /* read pixel */
   testb %bl, %bl                /* test  */
   jz sprite_vh_flip_skip 
   movb %bl, %es:(%eax)          /* write */
sprite_vh_flip_skip: 
   decl %esi 
   incl %eax 
   SPRITE_END_X(vh_flip)
   SPRITE_END_Y(vh_flip)

sprite_vh_flip_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_sprite_vh_flip8() */




/* void _linear_draw_trans_sprite8(BITMAP *bmp, BITMAP *sprite, int x, y);
 *  Draws a translucent sprite onto a linear bitmap.
 */
FUNC(_linear_draw_trans_sprite8)

   /* this loop is used when drawing onto video RAM */
   #define TRANS_LOOP_VIDEO                                                  \
      subl $4, S_W                                                         ; \
      _align_                                                              ; \
   sprite_y_loop_trans_video:                                              ; \
      movl S_BMP, %edx                       /* load bitmap pointer */     ; \
      movl S_Y, %eax                         /* load line */               ; \
      READ_BANK()                            /* select read bank */        ; \
      movl %eax, %ecx                        /* read address in ecx */     ; \
      movl S_Y, %eax                         /* reload line */             ; \
      WRITE_BANK()                           /* select write bank */       ; \
      subl %eax, %ecx                        /* convert ecx to offset */   ; \
      addl S_X, %eax                         /* add x offset */            ; \
      pushl %ebp                                                           ; \
      movl S_W, %ebp                         /* x loop counter */          ; \
      testl %ebp, %ebp                                                     ; \
      jle sprite_x_loop_trans_video_few_pixels                             ; \
      _align_                                                              ; \
   sprite_x_loop_trans_video:                                              ; \
      movl %es:(%eax, %ecx), %edx            /* read four pixels */        ; \
      movb %dl, %bl                          /* lookup pixel 1 */          ; \
      movb (%esi), %bh                                                     ; \
      movb (%edi, %ebx), %dl                                               ; \
      movb %dh, %bl                          /* lookup pixel 2 */          ; \
      movb 1(%esi), %bh                                                    ; \
      movb (%edi, %ebx), %dh                                               ; \
      roll $16, %edx                                                       ; \
      movb %dl, %bl                          /* lookup pixel 3 */          ; \
      movb 2(%esi), %bh                                                    ; \
      movb (%edi, %ebx), %dl                                               ; \
      movb %dh, %bl                          /* lookup pixel 4 */          ; \
      movb 3(%esi), %bh                                                    ; \
      movb (%edi, %ebx), %dh                                               ; \
      roll $16, %edx                                                       ; \
      movl %edx, %es:(%eax)                  /* write four pixels */       ; \
      addl $4, %eax                                                        ; \
      addl $4, %esi                                                        ; \
      subl $4, %ebp                                                        ; \
      jg sprite_x_loop_trans_video                                         ; \
   sprite_x_loop_trans_video_few_pixels:                                   ; \
      addl $4, %ebp                                                        ; \
      _align_                                                              ; \
   sprite_x_loop_trans_video_last_pixels:                                  ; \
      movb %es:(%eax, %ecx), %bl             /* read pixel */              ; \
      movb (%esi), %bh                                                     ; \
      movb (%edi, %ebx), %bl                 /* lookup pixel */            ; \
      movb %bl, %es:(%eax)                   /* write pixel */             ; \
      incl %eax                                                            ; \
      incl %esi                                                            ; \
      decl %ebp                                                            ; \
      jg sprite_x_loop_trans_video_last_pixels                             ; \
      popl %ebp                                                            ; \
      SPRITE_END_Y(trans_video)


   /* this loop is used when drawing onto memory bitmaps */
   #define TRANS_LOOP_MEMORY                                                 \
      movl S_Y, %ecx                         /* load line */               ; \
      movl S_W, %edx                         /* convert x loop counter */  ; \
      addl %edx, S_SGAP                      /* increase sprite gap */     ; \
      addl %ecx, S_H                         /* S_H = end y */             ; \
      movl $0, %ebx                          /* clear ebx */               ; \
      cmpl $2, %edx                          /* if sprite width > 2 */     ; \
      jg m_trans_sprite_not_tiny             /* the sprite isn't tiny */   ; \
      pushl %edx                             /* tiny sprite, unaligned */  ; \
      jmp m_trans_sprite_aligned             /* aligning complete */       ; \
      _align_                                                              ; \
   m_trans_sprite_not_tiny:                                                ; \
      subl S_X, %ebx                         /* unaligned bytes */         ; \
      andl $3, %ebx                          /* at start of line in bl */  ; \
      movb %dl, %bh                          /* unaligned bytes */         ; \
      subb %bl, %bh                          /* at end of line in bh */    ; \
      andb $3, %bh                                                         ; \
      pushl %ebx                             /* store on the stack */      ; \
   m_trans_sprite_aligned:                                                 ; \
      movzbl %bh, %eax                       /* subtract number of */      ; \
      subl %eax, %edx                        /* leftover bytes */          ; \
      subl $4, %edx                          /* one iteration less */      ; \
      addl %edx, %esi                        /* end of src line in esi */  ; \
      movl S_X, %eax                         /* eax = left edge of dest */ ; \
      addl %edx, %eax                        /* add width to left edge */  ; \
      movl %eax, S_X                         /* store right edge */        ; \
      negl %edx                              /* counter/offset negative */ ; \
      movl %edx, S_W                         /* store converted counter */ ; \
      _align_                                                              ; \
   m_sprite_y_loop_trans:                                                  ; \
      movl S_BMP, %edx                       /* load bitmap pointer */     ; \
      pushl %ebp                             /* I need ebp as counter */   ; \
      movl S_W, %ebp                         /* x loop counter */          ; \
      addl BMP_LINE(%edx, %ecx, 4), %eax     /* end of dest line in eax */ ; \
      movl $0, %ecx                          /* clear the high word */     ; \
      movl 4(%esp), %ebx                     /* leftover count in ebx */   ; \
      cmpb $0, %bl                           /* check if long-aligned */   ; \
      je m_trans_sprite_before_laligned      /* no aligning */             ; \
      cmpb $2, %bl                           /* check if word-aligned */   ; \
      je m_trans_sprite_before_waligned      /* copy one word */           ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      incl %ebp                              /* increment counter/index */ ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb %dl, -1(%eax, %ebp)               /* write the pixel */         ; \
      cmpb $1, %bl                           /* check if long-aligned */   ; \
      je m_trans_sprite_before_laligned      /* no more aligning */        ; \
   m_trans_sprite_before_waligned:                                         ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      movb 1(%eax, %ebp), %bl                /* load dest pixel 1 */       ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      movb 1(%esi, %ebp), %bh                /* load src pixel 1 */        ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 1 */          ; \
      movw %dx, (%eax, %ebp)                 /* write two pixels */        ; \
      addl $2, %ebp                          /* increment counter/index */ ; \
   m_trans_sprite_before_laligned:           /* src is long-aligned */     ; \
      testl %ebp, %ebp                       /* check number of longs */   ; \
      jg m_sprite_x_loop_trans_few           /* no aligned long in src */  ; \
      movb 2(%eax, %ebp), %cl                /* load dest pixel 2 */       ; \
      jz m_sprite_x_loop_trans_1long         /* one aligned long in src */ ; \
      movb 3(%esi, %ebp), %bh                /* load src pixel 3 */        ; \
      movb 2(%esi, %ebp), %ch                /* load src pixel 2 */        ; \
      movb 3(%eax, %ebp), %bl                /* load dest pixel 3 */       ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 2 */          ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 3 */          ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      shll $16, %edx                                                       ; \
      movb 1(%eax, %ebp), %bl                /* load dest pixel 1 */       ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb 1(%esi, %ebp), %bh                /* load src pixel 1 */        ; \
      movb 6(%eax, %ebp), %cl                /* load next loop pixel 2 */  ; \
      addl $4, %ebp                          /* increment counter/index */ ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 1 */          ; \
      jz m_sprite_x_loop_trans_2longs        /* 2 aligned longs in src */  ; \
      _align_                                                              ; \
   sprite_x_loop_trans_m:                                                  ; \
      movb 2(%esi, %ebp), %ch                /* load src pixel 2 */        ; \
      movb 3(%eax, %ebp), %bl                /* load dest pixel 2 */       ; \
      movb 3(%esi, %ebp), %bh                /* load src pixel 3 */        ; \
      movl %edx, -4(%eax, %ebp)              /* write prev. pixels */      ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 2  */         ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 3  */         ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      shll $16, %edx                         /* high word done; shift */   ; \
      movb 1(%eax, %ebp), %bl                /* load dest pixel 1 */       ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb 1(%esi, %ebp), %bh                /* load src pixel 1 */        ; \
      movb 6(%eax, %ebp), %cl                /* load next loop pixel 2 */  ; \
      addl $4, %ebp                          /* increment counter/index */ ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 1 */          ; \
      jl sprite_x_loop_trans_m                                             ; \
   m_sprite_x_loop_trans_2longs:                                           ; \
      movl %edx, -4(%eax, %ebp)              /* write last pixels */       ; \
   m_sprite_x_loop_trans_1long:                                            ; \
      movb 3(%esi, %ebp), %bh                /* load src pixel 3 */        ; \
      movb 2(%esi, %ebp), %ch                /* load src pixel 2 */        ; \
      movb 3(%eax, %ebp), %bl                /* load dest pixel 3 */       ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 2 */          ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 3 */          ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      shll $16, %edx                                                       ; \
      movb 1(%eax, %ebp), %bl                /* load dest pixel 1 */       ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb 1(%esi, %ebp), %bh                /* load src pixel 1 */        ; \
      addl $4, %ebp                          /* increment counter/index */ ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 1 */          ; \
      movl %edx, -4(%eax, %ebp)              /* write four pixels */       ; \
   m_sprite_x_loop_trans_few:                                              ; \
      movl 4(%esp), %ebx                     /* leftover count in ebx */   ; \
      cmpb $0, %bh                           /* any leftover bytes? */     ; \
      je m_trans_sprite_line_complete        /* no leftovers */            ; \
      cmpb $1, %bh                           /* any leftover word? */      ; \
      je m_trans_sprite_leftover_byte        /* only copy one byte */      ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      movb 1(%eax, %ebp), %bl                /* load dest pixel 1 */       ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      movb 1(%esi, %ebp), %bh                /* load src pixel 1 */        ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb (%edi, %ebx), %dh                 /* lookup pixel 1 */          ; \
      cmpb $2, 5(%esp)                       /* check if complete */       ; \
      movw %dx, (%eax, %ebp)                 /* write two pixels */        ; \
      leal 2(%ebp), %ebp                     /* increment counter/index */ ; \
      je m_trans_sprite_line_complete        /* don't draw any more */     ; \
   m_trans_sprite_leftover_byte:             /* draw the final pixel */    ; \
      movb (%esi, %ebp), %ch                 /* load src pixel 0 */        ; \
      movb (%eax, %ebp), %cl                 /* load dest pixel 0 */       ; \
      movb (%edi, %ecx), %dl                 /* lookup pixel 0 */          ; \
      movb %dl, (%eax, %ebp)                 /* write the pixel */         ; \
   m_trans_sprite_line_complete:                                           ; \
      popl %ebp                              /* I need stack variables */  ; \
      movl S_Y, %ecx                         /* y counter */               ; \
      incl %ecx                                                            ; \
      movl S_X, %eax                         /* reload dest x position */  ; \
      movl %ecx, S_Y                         /* store counter */           ; \
      addl S_SGAP, %esi                      /* esi points to next line */ ; \
      cmpl S_H, %ecx                         /* check if complete */       ; \
      jc m_sprite_y_loop_trans               /* loop */                    ; \
      popl %ebx                              /* pop unaligned counter */


   /* the actual translucent sprite drawing routine */
   START_SPRITE_DRAW(trans_sprite)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   subl S_W, %eax                /* - w */
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   movl BMP_LINE(%esi, %eax, 4), %esi
   addl S_LGAP, %esi             /* esi = sprite data ptr */

   xorl %ebx, %ebx
   movl GLOBL(color_map), %edi   /* edi = color mapping table */

   movl S_BMP, %eax              /* check if it's video memory */
   testl $(BMP_ID_VIDEO | BMP_ID_SYSTEM), BMP_ID(%eax) 
   jnz trans_sprite_video

   TRANS_LOOP_MEMORY
   jmp trans_sprite_done

trans_sprite_video:
   TRANS_LOOP_VIDEO

   _align_
trans_sprite_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_trans_sprite8() */




/* void _linear_draw_lit_sprite8(BITMAP *bmp, BITMAP *sprite, int x, y, color);
 *  Draws a lit sprite onto a linear bitmap.
 */
FUNC(_linear_draw_lit_sprite8)

   #define COLOR     ARG5

   START_SPRITE_DRAW(lit_sprite)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   subl S_W, %eax                /* - w */
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   movl BMP_LINE(%esi, %eax, 4), %esi
   addl S_LGAP, %esi             /* esi = sprite data ptr */

   xorl %ebx, %ebx
   movb COLOR, %bh               /* store color in high byte */
   movl GLOBL(color_map), %edi   /* edi = color mapping table */

   _align_
   SPRITE_LOOP(lit_sprite) 
   movb (%esi), %bl              /* read pixel into low byte */
   orb %bl, %bl
   jz lit_sprite_skip
   movb (%edi, %ebx), %bl        /* color table lookup */
   movb %bl, %es:(%eax)          /* write pixel */
lit_sprite_skip:
   incl %esi
   incl %eax
   SPRITE_END_X(lit_sprite)
   SPRITE_END_Y(lit_sprite)

lit_sprite_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_lit_sprite8() */




/* void _linear_draw_character8(BITMAP *bmp, BITMAP *sprite, int x, y, color, bg);
 *  For proportional font output onto a linear bitmap: uses the sprite as 
 *  a mask, replacing all set pixels with the specified color.
 */
FUNC(_linear_draw_character8)

   #undef COLOR
   #define COLOR  ARG5
   #define BG     ARG6

   START_SPRITE_DRAW(draw_char)

   movl BMP_LINE+4(%esi), %eax
   subl BMP_LINE(%esi), %eax     /* eax = sprite data pitch */
   subl S_W, %eax                /* - w */
   movl %eax, S_SGAP             /* store sprite gap */

   movl S_LGAP, %eax
   addl %eax, S_X                /* X += lgap */

   movl S_TGAP, %eax 
   addl %eax, S_Y                /* Y += tgap */

   movl BMP_LINE(%esi, %eax, 4), %esi
   addl S_LGAP, %esi             /* esi = sprite data ptr */

   movb COLOR, %bl               /* bl = text color */
   movb BG, %bh                  /* bh = background color */
   cmpl $0, BG
   jl draw_masked_char

   /* opaque (bg >= 0) character output */
   _align_
   SPRITE_LOOP(draw_opaque_char) 
   cmpb $0, (%esi)               /* test pixel */
   jz draw_opaque_background
   movb %bl, %es:(%eax)          /* write pixel */
   jmp draw_opaque_done
draw_opaque_background: 
   movb %bh, %es:(%eax)          /* write background */
draw_opaque_done:
   incl %esi 
   incl %eax 
   SPRITE_END_X(draw_opaque_char)
   SPRITE_END_Y(draw_opaque_char)
   jmp draw_char_done

   /* masked (bg -1) character output */
   _align_
draw_masked_char:
   SPRITE_LOOP(draw_masked_char) 
   cmpb $0, (%esi)               /* test pixel */
   jz draw_masked_skip
   movb %bl, %es:(%eax)          /* write pixel */
draw_masked_skip:
   incl %esi 
   incl %eax 
   SPRITE_END_X(draw_masked_char)
   SPRITE_END_Y(draw_masked_char)

draw_char_done:
   END_SPRITE_DRAW()
   ret                           /* end of _linear_draw_character8() */




/* void _linear_draw_rle_sprite8(BITMAP *bmp, RLE_SPRITE *sprite, int x, int y)
 *  Draws an RLE sprite onto a linear bitmap at the specified position.
 */
FUNC(_linear_draw_rle_sprite8)

   /* bank switch routine */
   #define INIT_RLE_LINE()                                                   \
      movl R_Y, %eax                                                       ; \
      WRITE_BANK()                                                         ; \
      movl %eax, %edi                                                      ; \
      addl R_X, %edi


   /* copy a clipped pixel run */
   #define SLOW_RLE_RUN(n)                                                   \
      rep ; movsb


   /* no special initialisation required */
   #define INIT_FAST_RLE_LOOP()


   /* copy a run of solid pixels */
   #define FAST_RLE_RUN()                                                    \
      shrl $1, %ecx                                                        ; \
      jnc rle_noclip_no_byte                                               ; \
      movsb                      /* copy odd byte? */                      ; \
   rle_noclip_no_byte:                                                     ; \
      jz rle_noclip_x_loop                                                 ; \
      shrl $1, %ecx                                                        ; \
      jnc rle_noclip_no_word                                               ; \
      movsw                      /* copy odd word? */                      ; \
   rle_noclip_no_word:                                                     ; \
      jz rle_noclip_x_loop                                                 ; \
      rep ; movsl                /* 32 bit string copy */


   /* tests an RLE command byte */
   #define TEST_RLE_COMMAND(done, skip)                                      \
      testb %al, %al                                                       ; \
      jz done                                                              ; \
      js skip


   /* adds the offset in %eax onto the destination address */
   #define ADD_EAX_EDI()                                                     \
      addl %eax, %edi


   /* zero extend %al into %eax */
   #define RLE_ZEX_EAX()                                                     \
      movzbl %al, %eax


   /* zero extend %al into %ecx */
   #define RLE_ZEX_ECX()                                                     \
      movzbl %al, %ecx 


   /* sign extend %al into %eax */
   #define RLE_SEX_EAX()                                                     \
      movsbl %al, %eax


   /* do it! */
   DO_RLE(rle, 1, b, %al, $0)
   ret

   #undef INIT_RLE_LINE
   #undef SLOW_RLE_RUN
   #undef INIT_FAST_RLE_LOOP
   #undef FAST_RLE_RUN




/* void _linear_draw_trans_rle_sprite8(BITMAP *bmp, RLE_SPRITE *sprite, 
 *                                   int x, int y)
 *  Draws a translucent RLE sprite onto a linear bitmap.
 */
FUNC(_linear_draw_trans_rle_sprite8)

   /* bank switch routine */
   #define INIT_RLE_LINE()                                                   \
      movl R_BMP, %edx                                                     ; \
      movl R_Y, %eax                                                       ; \
      READ_BANK()                /* select read bank */                    ; \
      movl %eax, R_TMP                                                     ; \
      movl R_Y, %eax                                                       ; \
      WRITE_BANK()               /* select write bank */                   ; \
      movl %eax, %edi                                                      ; \
      movl R_TMP, %edx           /* calculate read/write diff */           ; \
      subl %edi, %edx                                                      ; \
      addl R_X, %edi


   /* copy a clipped pixel run */
   #define SLOW_RLE_RUN(n)                                                   \
      pushl %ebx                                                           ; \
      movl GLOBL(color_map), %ebx                                          ; \
      xorl %eax, %eax                                                      ; \
									   ; \
   trans_rle_clipped_run_loop##n:                                          ; \
      movb (%esi), %ah           /* read sprite pixel */                   ; \
      movb %es:(%edi, %edx), %al /* read destination pixel */              ; \
      movb (%ebx, %eax), %al     /* blend */                               ; \
      movb %al, %es:(%edi)       /* write the pixel */                     ; \
      incl %esi                                                            ; \
      incl %edi                                                            ; \
      decl %ecx                                                            ; \
      jg trans_rle_clipped_run_loop##n                                     ; \
									   ; \
      popl %ebx


   /* initialise the drawing loop */
   #define INIT_FAST_RLE_LOOP()                                              \
      movl GLOBL(color_map), %ebx


   /* copy a run of solid pixels */
   #define FAST_RLE_RUN()                                                    \
      xorl %eax, %eax                                                      ; \
									   ; \
      shrl $1, %ecx                                                        ; \
      jnc trans_rle_run_no_byte                                            ; \
									   ; \
      movb (%esi), %ah           /* read sprite pixel */                   ; \
      movb %es:(%edi, %edx), %al /* read destination pixel */              ; \
      movb (%ebx, %eax), %al     /* blend */                               ; \
      movb %al, %es:(%edi)       /* write the pixel */                     ; \
      incl %esi                                                            ; \
      incl %edi                                                            ; \
									   ; \
   trans_rle_run_no_byte:                                                  ; \
      orl %ecx, %ecx                                                       ; \
      jz trans_rle_run_done                                                ; \
									   ; \
      shrl $1, %ecx                                                        ; \
      jnc trans_rle_run_no_word                                            ; \
									   ; \
      pushl %ecx                                                           ; \
      xorl %ecx, %ecx                                                      ; \
      movw (%esi), %ax           /* read two sprite pixels */              ; \
      movw %ax, R_TMP                                                      ; \
      movw %es:(%edi, %edx), %ax /* read two destination pixels */         ; \
      movb %al, %cl                                                        ; \
      movb R_TMP, %ch                                                      ; \
      movb (%ebx, %ecx), %al     /* blend pixel 1 */                       ; \
      movb %ah, %cl                                                        ; \
      movb 1+R_TMP, %ch                                                    ; \
      movb (%ebx, %ecx), %ah     /* blend pixel 2 */                       ; \
      movw %ax, %es:(%edi)       /* write two pixels */                    ; \
      addl $2, %esi                                                        ; \
      addl $2, %edi                                                        ; \
      popl %ecx                                                            ; \
									   ; \
   trans_rle_run_no_word:                                                  ; \
      orl %ecx, %ecx                                                       ; \
      jz trans_rle_run_done                                                ; \
									   ; \
      movl %ecx, R_TMP2                                                    ; \
      xorl %ecx, %ecx                                                      ; \
									   ; \
   trans_rle_run_loop:                                                     ; \
      movl (%esi), %eax          /* read four sprite pixels */             ; \
      movl %eax, R_TMP                                                     ; \
      movl %es:(%edi, %edx), %eax   /* read four destination pixels */     ; \
      movb %al, %cl                                                        ; \
      movb R_TMP, %ch                                                      ; \
      movb (%ebx, %ecx), %al     /* blend pixel 1 */                       ; \
      movb %ah, %cl                                                        ; \
      movb 1+R_TMP, %ch                                                    ; \
      movb (%ebx, %ecx), %ah     /* blend pixel 2 */                       ; \
      roll $16, %eax                                                       ; \
      movb %al, %cl                                                        ; \
      movb 2+R_TMP, %ch                                                    ; \
      movb (%ebx, %ecx), %al     /* blend pixel 3 */                       ; \
      movb %ah, %cl                                                        ; \
      movb 3+R_TMP, %ch                                                    ; \
      movb (%ebx, %ecx), %ah     /* blend pixel 4 */                       ; \
      roll $16, %eax                                                       ; \
      movl %eax, %es:(%edi)      /* write four pixels */                   ; \
      addl $4, %esi                                                        ; \
      addl $4, %edi                                                        ; \
      decl R_TMP2                                                          ; \
      jg trans_rle_run_loop                                                ; \
									   ; \
   trans_rle_run_done:


   /* do it! */
   DO_RLE(rle_trans, 1, b, %al, $0)
   ret 

   #undef INIT_RLE_LINE
   #undef SLOW_RLE_RUN
   #undef INIT_FAST_RLE_LOOP
   #undef FAST_RLE_RUN




/* void _linear_draw_lit_rle_sprite8(BITMAP *bmp, RLE_SPRITE *sprite, 
 *                                  int x, int y, int color)
 *  Draws a tinted RLE sprite onto a linear bitmap.
 */
FUNC(_linear_draw_lit_rle_sprite8)

   /* bank switch routine */
   #define INIT_RLE_LINE()                                                   \
      movl R_BMP, %edx                                                     ; \
      movl R_Y, %eax                                                       ; \
      WRITE_BANK()                                                         ; \
      movl %eax, %edi                                                      ; \
      addl R_X, %edi                                                       ; \
      movl GLOBL(color_map), %edx


   /* copy a clipped pixel run */
   #define SLOW_RLE_RUN(n)                                                   \
      xorl %eax, %eax                                                      ; \
      movb R_COLOR, %ah          /* store color in high byte */            ; \
									   ; \
   lit_rle_clipped_run_loop##n:                                            ; \
      movb (%esi), %al           /* read a pixel */                        ; \
      movb (%edx, %eax), %al     /* lookup in color table */               ; \
      movb %al, %es:(%edi)       /* write the pixel */                     ; \
      incl %esi                                                            ; \
      incl %edi                                                            ; \
      decl %ecx                                                            ; \
      jg lit_rle_clipped_run_loop##n


   /* initialise the drawing loop */
   #define INIT_FAST_RLE_LOOP()                                              \
      xorl %ebx, %ebx                                                      ; \
      movb R_COLOR, %bh          /* store color in high byte */


   /* copy a run of solid pixels */
   #define FAST_RLE_RUN()                                                    \
      shrl $1, %ecx                                                        ; \
      jnc lit_rle_run_no_byte                                              ; \
									   ; \
      movb (%esi), %bl           /* read pixel into low byte */            ; \
      movb (%edx, %ebx), %bl     /* lookup in lighting table */            ; \
      movb %bl, %es:(%edi)       /* write the pixel */                     ; \
      incl %esi                                                            ; \
      incl %edi                                                            ; \
									   ; \
   lit_rle_run_no_byte:                                                    ; \
      orl %ecx, %ecx                                                       ; \
      jz lit_rle_run_done                                                  ; \
									   ; \
      shrl $1, %ecx                                                        ; \
      jnc lit_rle_run_no_word                                              ; \
									   ; \
      movw (%esi), %ax           /* read two pixels */                     ; \
      movb %al, %bl                                                        ; \
      movb (%edx, %ebx), %al     /* lookup pixel 1 */                      ; \
      movb %ah, %bl                                                        ; \
      movb (%edx, %ebx), %ah     /* lookup pixel 2 */                      ; \
      movw %ax, %es:(%edi)       /* write two pixels */                    ; \
      addl $2, %esi                                                        ; \
      addl $2, %edi                                                        ; \
									   ; \
   lit_rle_run_no_word:                                                    ; \
      orl %ecx, %ecx                                                       ; \
      jz lit_rle_run_done                                                  ; \
									   ; \
   lit_rle_run_loop:                                                       ; \
      movl (%esi), %eax          /* read four pixels */                    ; \
      movb %al, %bl                                                        ; \
      movb (%edx, %ebx), %al     /* lookup pixel 1 */                      ; \
      movb %ah, %bl                                                        ; \
      movb (%edx, %ebx), %ah     /* lookup pixel 2 */                      ; \
      roll $16, %eax                                                       ; \
      movb %al, %bl                                                        ; \
      movb (%edx, %ebx), %al     /* lookup pixel 3 */                      ; \
      movb %ah, %bl                                                        ; \
      movb (%edx, %ebx), %ah     /* lookup pixel 4 */                      ; \
      roll $16, %eax                                                       ; \
      movl %eax, %es:(%edi)      /* write four pixels */                   ; \
      addl $4, %esi                                                        ; \
      addl $4, %edi                                                        ; \
      decl %ecx                                                            ; \
      jg lit_rle_run_loop                                                  ; \
									   ; \
   lit_rle_run_done:


   /* do it! */
   DO_RLE(rle_lit, 1, b, %al, $0)
   ret 

   #undef INIT_RLE_LINE
   #undef SLOW_RLE_RUN
   #undef INIT_FAST_RLE_LOOP
   #undef FAST_RLE_RUN




#endif      /* ifdef ALLEGRO_COLOR8 */
