/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Mode-X drawing functions.
 *
 *      By Shawn Hargreaves.
 *
 *      Dominique Biesmans wrote the mode-X <-> linear blitting routines
 *      and the mode-X version of draw_sprite().
 *
 *      Linear -> mode-X blit optimised by Ettore Perazzoli.
 *
 *      See readme.txt for copyright information.
 */
 
 
#include "../i386/asmdefs.inc"


.text


#if (!defined ALLEGRO_LINUX) || ((defined ALLEGRO_LINUX_VGA) && ((!defined ALLEGRO_WITH_MODULES) || (defined ALLEGRO_MODULE)))


/* _x_bank_switch:
 *  Cunning trickey for emulating linear access to mode-X screens, by
 *  copying the image into a temporary buffer and letting the client
 *  write to that in place of the real screen.
 */
FUNC(_x_bank_switch)
   cmpl $0, GLOBL(_x_magic_screen_addr)
   jz no_previous_magic

   call GLOBL(_x_unbank_switch)

no_previous_magic:
   pushl %ecx                    /* save registers */
   pushl %edx
   pushl %esi
   pushl %edi
   pushl %es

   movl BMP_CR(%edx), %ecx       /* store off the width and screen address */
   addl $3, %ecx
   shrl $2, %ecx
   movl %ecx, GLOBL(_x_magic_screen_width)

   movl BMP_LINE(%edx, %eax, 4), %esi
   movl %esi, GLOBL(_x_magic_screen_addr)

   movl $0, %ecx                 /* plane counter in ecx */
   movw BMP_SEG(%edx), %es

bank_plane_loop:
   movb %cl, %ah                 /* select write plane */
   movb $4, %al
   movl $0x3CE, %edx
   outw %ax, %dx

   movl GLOBL(_x_magic_screen_addr), %esi
   movl GLOBL(_x_magic_buffer_addr), %edi
   movl GLOBL(_x_magic_screen_width), %edx
   addl %ecx, %edi

bank_pixel_loop:
   movb %es:(%esi), %al          /* read pixel */
   movb %al, %es:(%edi)          /* write pixel */

   incl %esi                     /* advance pointers */
   addl $4, %edi

   decl %edx                     /* next pixel */
   jg bank_pixel_loop

   incl %ecx                     /* next plane */
   cmpl $4, %ecx
   jl bank_plane_loop

   popl %es                      /* restore registers */
   popl %edi
   popl %esi
   popl %edx
   popl %ecx
   movl GLOBL(_x_magic_buffer_addr), %eax
   ret




/* _x_unbank_switch:
 *  Updates the real screen from the linear emulation buffer.
 */
FUNC(_x_unbank_switch)
   cmpl $0, GLOBL(_x_magic_screen_addr)
   je unbank_done

   pushl %eax                    /* save registers */
   pushl %ecx
   pushl %edx
   pushl %esi
   pushl %edi
   pushl %es

   movl $0, %ecx                 /* plane counter in ecx */
   movw BMP_SEG(%edx), %es

unbank_plane_loop:
   movb $1, %ah                  /* select write plane */
   shlb %cl, %ah
   movb $2, %al
   movl $0x3C4, %edx
   outw %ax, %dx

   movl GLOBL(_x_magic_buffer_addr), %esi
   movl GLOBL(_x_magic_screen_addr), %edi
   movl GLOBL(_x_magic_screen_width), %edx
   addl %ecx, %esi

unbank_pixel_loop:
   movb %es:(%esi), %al          /* read pixel */
   movb %al, %es:(%edi)          /* write pixel */

   addl $4, %esi                 /* advance pointers */
   incl %edi

   decl %edx                     /* next pixel */
   jg unbank_pixel_loop

   incl %ecx                     /* next plane */
   cmpl $4, %ecx
   jl unbank_plane_loop

   movl $0, GLOBL(_x_magic_screen_addr)

   popl %es                      /* restore registers */
   popl %edi
   popl %esi
   popl %edx
   popl %ecx
   popl %eax

unbank_done:
   ret




/* put this here so both the above routines will get locked */
FUNC(_x_bank_switch_end)
   ret




/* void _x_clear_to_color(BITMAP *bitmap, int color);
 *  Clears a mode-X bitmap to the specified color.
 */
FUNC(_x_clear_to_color)
   pushl %ebp
   movl %esp, %ebp
   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es 

   movl $0x0F02, %eax            /* enable all planes */
   movl $0x3C4, %edx
   outw %ax, %dx

   movl ARG1, %edx               /* edx = bmp */
   movl BMP_CT(%edx), %ebx       /* line to start at */

   movw BMP_SEG(%edx), %es       /* select segment */

   movl BMP_CR(%edx), %esi       /* width to clear */
   subl BMP_CL(%edx), %esi
   shrl $2, %esi
   cld

   movb ARG2, %al                /* duplicate color 4 times */
   movb %al, %ah
   shll $16, %eax
   movb ARG2, %al 
   movb %al, %ah

   _align_
clear_loop:
   movl BMP_CL(%edx), %edi       /* get line address */
   shrl $2, %edi
   addl BMP_LINE(%edx, %ebx, 4), %edi

   movl %esi, %ecx               /* width to clear */
   shrl $1, %ecx                 /* halve for 16 bit clear */
   jnc clear_no_byte
   stosb                         /* clear an odd byte */

clear_no_byte:
   shrl $1, %ecx                 /* halve again for 32 bit clear */
   jnc clear_no_word
   stosw                         /* clear an odd word */

clear_no_word:
   jz clear_no_long 

   _align_
clear_x_loop:
   rep ; stosl                   /* clear the line */

clear_no_long:
   incl %ebx
   cmpl %ebx, BMP_CB(%edx)
   jg clear_loop                 /* and loop */

   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_clear_to_color() */




/* void _x_putpixel(BITMAP *bmp, int x, int y, int color);
 *  Draws a pixel onto a mode-X bitmap.
 */
FUNC(_x_putpixel)
   pushl %ebp
   movl %esp, %ebp
   pushl %ebx
   pushw %es

   movl ARG1, %edx               /* edx = bmp */
   movl ARG2, %ecx               /* ecx = x */
   movl ARG3, %ebx               /* ebx = y */

   cmpl $0, BMP_CLIP(%edx)       /* test clip flag */
   je xputpix_noclip
   cmpl %ecx, BMP_CL(%edx)       /* test cl */
   jg xputpix_done
   cmpl %ecx, BMP_CR(%edx)       /* test cr */
   jle xputpix_done
   cmpl %ebx, BMP_CT(%edx)       /* test ct */
   jg xputpix_done
   cmpl %ebx, BMP_CB(%edx)       /* test cb */
   jle xputpix_done

xputpix_noclip:
   movl $0x102, %eax             /* set the write plane */
   andb $3, %cl
   shlb %cl, %ah
   movl $0x3C4, %edx
   outw %ax, %dx

   movl ARG2, %ecx               /* reload ecx = x */
   movl ARG1, %edx               /* reload edx = bmp */
   movw BMP_SEG(%edx), %es 

   cmpl $DRAW_SOLID, GLOBL(_drawing_mode)
   je xputpix_solid_mode         /* solid draw? */

   cmpl $DRAW_XOR, GLOBL(_drawing_mode)
   je xputpix_xor_mode           /* XOR? */

   cmpl $DRAW_TRANS, GLOBL(_drawing_mode)
   je xputpix_trans_mode         /* translucent? */

   LOOKUP_PATTERN_POS(%ecx, %ebx, %eax)
   movb (%ebx, %ecx), %al        /* read pixel from pattern bitmap */

   movl ARG2, %ecx               /* reload clobbered registers */
   movl ARG3, %ebx 

   cmpl $DRAW_COPY_PATTERN, GLOBL(_drawing_mode)
   je xputpix_draw_it            /* draw the pattern pixel? */

   testb %al, %al                /* test the pattern pixel */
   jz xputpix_zero_pattern
   jmp xputpix_solid_mode

   _align_
xputpix_zero_pattern:
   cmpl $DRAW_MASKED_PATTERN, GLOBL(_drawing_mode)
   je xputpix_done               /* skip zero pixels in masked mode */
   jmp xputpix_draw_it           /* draw zero pixels in solid mode */

   _align_
xputpix_xor_mode:
   movb $4, %al                  /* set the read plane */
   movb %cl, %ah
   andb $3, %ah
   movl $0x3CE, %edx
   outw %ax, %dx

   movl ARG4, %eax               /* eax = color */
   shrl $2, %ecx                 /* divide x by 4 */
   movl ARG1, %edx               /* reload edx = bmp */
   movl BMP_LINE(%edx, %ebx, 4), %ebx
   xorb %al, %es:(%ebx, %ecx)    /* XOR */
   jmp xputpix_done

   _align_
xputpix_trans_mode:
   movb $4, %al                  /* set the read plane */
   movb %cl, %ah
   andb $3, %ah
   movl $0x3CE, %edx
   outw %ax, %dx

   xorl %eax, %eax
   movb ARG4, %ah                /* ah = color being drawn */
   shrl $2, %ecx                 /* divide x by 4 */
   movl ARG1, %edx               /* reload edx = bmp */
   movl BMP_LINE(%edx, %ebx, 4), %ebx
   movb %es:(%ebx, %ecx), %al    /* al = existing color */
   movl GLOBL(color_map), %edx   /* color map in edx */
   movb (%edx, %eax), %al        /* lookup color in table */
   movb %al, %es:(%ebx, %ecx)    /* write the pixel */
   jmp xputpix_done

   _align_
xputpix_solid_mode:
   movl ARG4, %eax               /* eax = color */

xputpix_draw_it:
   shrl $2, %ecx                 /* divide x by 4 */
   movl BMP_LINE(%edx, %ebx, 4), %ebx
   movb %al, %es:(%ebx, %ecx)    /* store the pixel */

xputpix_done:
   popw %es
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_putpixel() */




/* int _x_getpixel(BITMAP *bmp, int x, int y);
 *  Reads a pixel from a mode-X bitmap.
 */
FUNC(_x_getpixel)
   pushl %ebp
   movl %esp, %ebp
   pushl %ebx
   pushw %es

   movl ARG1, %edx               /* edx = bmp */
   movl ARG2, %ecx               /* ecx = x */
   movl ARG3, %ebx               /* ebx = y */

   testl %ecx, %ecx              /* test that we are on the bitmap */
   jl xgetpix_skip
   cmpl %ecx, BMP_W(%edx)
   jle xgetpix_skip
   testl %ebx, %ebx
   jl xgetpix_skip
   cmpl %ebx, BMP_H(%edx)
   jg xgetpix_ok

   _align_
xgetpix_skip:                    /* return -1 if we are off the edge */
   movl $-1, %eax
   jmp xgetpix_done

   _align_
xgetpix_ok:
   movb $4, %al                  /* set the correct plane */
   movb %cl, %ah
   andb $3, %ah
   movl $0x3CE, %edx
   outw %ax, %dx

   shrl $2, %ecx                 /* divide x by 4 */

   movl ARG1, %edx               /* reload edx = bmp */

   movw BMP_SEG(%edx), %es 
   movl BMP_LINE(%edx, %ebx, 4), %ebx

   xorl %eax, %eax
   movb %es:(%ebx, %ecx), %al    /* get the pixel */

xgetpix_done:
   popw %es
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_getpixel() */




/* void _x_hline(BITMAP *bmp, int x1, int y, int x2, int color);
 *  Draws a horizontal line onto a mode-X bitmap.
 *
 *  Patterned drawing could probably be done slightly faster by using the
 *  latch registers, but I don't think very much faster: there would be so
 *  much overhead due to the extra code complexity that it would only pay
 *  off for very long lines...
 */

   /* helper for patterned and xor lines, loops through each plane */
   #define HLINE_LOOP(name, putter)                                          \
      movl %ecx, COUNTER                                                   ; \
      movl %ebx, START_X                                                   ; \
      movl %edi, SCREEN_ADDR                                               ; \
      movl $4, PLANE_COUNTER                                               ; \
									   ; \
      _align_                                                              ; \
   xhline_##name##_loop:                                                   ; \
      movl $0x102, %eax          /* set the write plane */                 ; \
      movb START_X, %cl                                                    ; \
      andb $3, %cl                                                         ; \
      shlb %cl, %ah                                                        ; \
      movl $0x3C4, %edx                                                    ; \
      outw %ax, %dx                                                        ; \
									   ; \
      SETUP                                                                ; \
									   ; \
      movl COUNTER, %edx                                                   ; \
      addl $3, %edx                                                        ; \
      shrl $2, %edx              /* edx = the number of bytes to alter */  ; \
									   ; \
      movl SCREEN_ADDR, %esi     /* esi = the place to write it */         ; \
									   ; \
      _align_                                                              ; \
   xhline_##name##_x_loop:                                                 ; \
      putter                     /* write the pixel */                     ; \
      incl %esi                                                            ; \
      decl %edx                                                            ; \
      jg xhline_##name##_x_loop                                            ; \
									   ; \
      incl START_X               /* next plane */                          ; \
      testl $3, START_X                                                    ; \
      jnz xhline_##name##_no_plane_wrap                                    ; \
      incl SCREEN_ADDR                                                     ; \
   xhline_##name##_no_plane_wrap:                                          ; \
      decl COUNTER                                                         ; \
      jle xhline_done                                                      ; \
									   ; \
      decl PLANE_COUNTER                                                   ; \
      jg xhline_##name##_loop    /* repeat for each plane */


FUNC(_x_hline)
   pushl %ebp
   movl %esp, %ebp
   subl $16, %esp                /* 4 local variables: */

   #define COUNTER         -4(%ebp)
   #define START_X         -8(%ebp)
   #define SCREEN_ADDR     -12(%ebp)
   #define PLANE_COUNTER   -16(%ebp)

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es

   movl ARG1, %edx               /* edx = bmp */
   movl ARG2, %ebx               /* ebx = x1 */
   movl ARG3, %eax               /* eax = y */
   movl ARG4, %ecx               /* ecx = x2 */
   cmpl %ebx, %ecx
   jge xhline_no_xswap
   xchgl %ecx, %ebx

   _align_
xhline_no_xswap:
   cmpl $0, BMP_CLIP(%edx)       /* test bmp->clip */
   je xhline_noclip

   cmpl BMP_CT(%edx), %eax       /* test bmp->ct */
   jl xhline_done

   cmpl BMP_CB(%edx), %eax       /* test bmp->cb */
   jge xhline_done

   cmpl BMP_CL(%edx), %ebx       /* test x1, bmp->cl */
   jge xhline_x1_ok
   cmpl BMP_CL(%edx), %ecx       /* test x2, bmp->cl */
   jl xhline_done
   movl BMP_CL(%edx), %ebx       /* clip x1 */

   _align_
xhline_x1_ok:
   cmpl BMP_CR(%edx), %ecx       /* test x2, bmp->cr */
   jl xhline_noclip
   cmpl BMP_CR(%edx), %ebx       /* test x1, bmp->cr */
   jge xhline_done
   movl BMP_CR(%edx), %ecx       /* clip x2 */
   decl %ecx

xhline_noclip:
   subl %ebx, %ecx               /* loop counter */
   incl %ecx

   movw BMP_SEG(%edx), %es       /* segment selector */
   cld

   movl %ebx, %edi               /* dest address in edi */
   shrl $2, %edi
   addl BMP_LINE(%edx, %eax, 4), %edi

   cmpl $DRAW_SOLID, GLOBL(_drawing_mode)
   je xhline_solid_mode          /* solid draw? */

   cmpl $DRAW_XOR, GLOBL(_drawing_mode)
   jne xhline_not_xor            /* XOR? */


   /* helper to set up an XOR mode draw */
   #define SETUP                                                             \
      movb $4, %al               /* set the read plane */                  ; \
      movb %cl, %ah                                                        ; \
      movl $0x3CE, %edx                                                    ; \
      outw %ax, %dx                                                        ; \
									   ; \
      movb ARG5, %al             /* al = color */

   #define LOOP_PUTTER                                                       \
      xorb %al, %es:(%esi)      
   HLINE_LOOP(xor, LOOP_PUTTER)
   #undef LOOP_PUTTER

   jmp xhline_done

   #undef SETUP


   _align_
xhline_not_xor:
   cmpl $DRAW_TRANS, GLOBL(_drawing_mode)
   jne xhline_not_trans          /* translucent? */


   /* helper to set up a translucent draw */
   #define SETUP                                                             \
      movb $4, %al               /* set the read plane */                  ; \
      movb %cl, %ah                                                        ; \
      movl $0x3CE, %edx                                                    ; \
      outw %ax, %dx                                                        ; \
									   ; \
      xorl %eax, %eax                                                      ; \
      movb ARG5, %ah             /* ah = color */                          ; \
									   ; \
      movl GLOBL(color_map), %ecx   /* color map in ecx */

   #define LOOP_PUTTER                                                       \
      movb %es:(%esi), %al ;     /* read existing color */                   \
      movb (%ecx, %eax), %al ;   /* lookup color in table */                 \
      movb %al, %es:(%esi)       /* write pixel */
   HLINE_LOOP(trans, LOOP_PUTTER)
   #undef LOOP_PUTTER

   jmp xhline_done

   #undef SETUP


   _align_
xhline_not_trans:
   /* helper to set up a patterned draw */
   #define SETUP                                                             \
      movl START_X, %edi         /* edi = pattern x */                     ; \
      movl ARG3, %ebx            /* ebx = pattern y */                     ; \
      LOOKUP_PATTERN_POS(%edi, %ebx, %eax)                                 ; \
      movl GLOBL(_drawing_x_mask), %ecx                                    ; \
      movb ARG5, %ah             /* color in ah */

   cmpl $DRAW_COPY_PATTERN, GLOBL(_drawing_mode)
   je xhline_copy_pattern

   cmpl $DRAW_SOLID_PATTERN, GLOBL(_drawing_mode)
   je xhline_solid_pattern

   #define LOOP_PUTTER                                                       \
      movb (%ebx, %edi), %al ;   /* read pixel from pattern bitmap */        \
      testb %al, %al ;           /* test it */                               \
      jz xhline_masked_zero ;                                                \
      movb %ah, %es:(%esi) ;     /* write solid pixel */                     \
   xhline_masked_zero:                                                       \
      addl $4, %edi ;                                                        \
      andl %ecx, %edi            /* advance through pattern bitmap */
   HLINE_LOOP(masked_pattern, LOOP_PUTTER)
   #undef LOOP_PUTTER
   jmp xhline_done

   _align_
xhline_solid_pattern:
   #define LOOP_PUTTER                                                       \
      movb (%ebx, %edi), %al ;   /* read pixel from pattern bitmap */        \
      testb %al, %al ;           /* test it */                               \
      jz xhline_solid_zero ;                                                 \
      movb %ah, %al ;            /* select a colored pixel */                \
   xhline_solid_zero:                                                        \
      movb %al, %es:(%esi) ;     /* write pixel */                           \
      addl $4, %edi ;                                                        \
      andl %ecx, %edi            /* advance through pattern bitmap */
   HLINE_LOOP(solid_pattern, LOOP_PUTTER)
   #undef LOOP_PUTTER
   jmp xhline_done

   _align_
xhline_copy_pattern:
   #define LOOP_PUTTER                                                       \
      movb (%ebx, %edi), %al ;   /* read pixel from pattern bitmap */        \
      movb %al, %es:(%esi) ;     /* write pixel */                           \
      addl $4, %edi ;                                                        \
      andl %ecx, %edi            /* advance through pattern bitmap */
   HLINE_LOOP(copy_pattern, LOOP_PUTTER)
   #undef LOOP_PUTTER
   jmp xhline_done


   _align_
xhline_solid_mode:               /* draw the line in a solid color */
   movl $0x3C4, %edx             /* preload port address */

   andl $3, %ebx                 /* draw odd pixels on the left? */
   jz xhline_laligned

   negl %ebx                     /* how many pixels to draw on the left? */
   addl $4, %ebx
   subl %ebx, %ecx
   jge xhline_l_make_mask

   negl %ecx                     /* special case for very short lines */
   subl %ecx, %ebx
   movb GLOBL(_x_left_mask_table)(%ebx), %ah
   shrb %cl, %ah
   xorl %ecx, %ecx
   jmp xhline_l_made_mask

   _align_
xhline_l_make_mask:
   movb GLOBL(_x_left_mask_table)(%ebx), %ah

xhline_l_made_mask:
   movb $2, %al
   outw %ax, %dx                 /* select planes */

   movb ARG5, %al                /* get color */
   stosb                         /* write odd pixels on the left */

   test %ecx, %ecx
   jle xhline_done

xhline_laligned:
   movl $0x0F02, %eax            /* select all four planes */
   outw %ax, %dx

   movb ARG5, %bl                /* get color */
   movb %bl, %bh
   shrdl $16, %ebx, %eax
   movw %bx, %ax                 /* get four copies of the color */

   movl %ecx, %esi               /* store width in esi */
   shrl $2, %ecx                 /* divide by four for multi-plane writes */
   jle xhline_no_block

   shrl $1, %ecx                 /* halve for 16 bit writes */
   jnc xhline_no_byte
   stosb                         /* write an odd byte (four pixels) */

xhline_no_byte:
   jle xhline_no_block
   shrl $1, %ecx                 /* halve again for 32 bit writes */
   jnc xhline_no_word
   stosw                         /* write an odd word (eight pixels) */

xhline_no_word:
   jle xhline_no_block
   rep ; stosl                   /* write 32 bit values (16 pixels each) */

   _align_
xhline_no_block:
   andl $3, %esi                 /* draw odd pixels on the right? */
   jz xhline_done

   movb GLOBL(_x_right_mask_table)(%esi), %ah
   movb $2, %al 
   outw %ax, %dx                 /* select planes */

   movb ARG5, %al                /* get color */
   stosb                         /* write odd pixels on the right */

   _align_
xhline_done:
   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_hline() */




#define SOURCE       ARG1        /* parameters to the blitting functions */
#define DEST         ARG2
#define SOURCE_X     ARG3
#define SOURCE_Y     ARG4
#define DEST_X       ARG5
#define DEST_Y       ARG6
#define WIDTH        ARG7
#define HEIGHT       ARG8

#define S_ADDR    -4(%ebp)
#define D_ADDR    -8(%ebp)

#define COUNT     -4(%ebp)




/* void _x_blit_from_memory(BITMAP *source, *dest, int source_x, source_y, 
 *                          int dest_x, int dest_y, int width, int height);
 *  Blits from a memory bitmap to a mode-X screen. The area will already have 
 *  been clipped when this routine is called.
 */
FUNC(_x_blit_from_memory)
   pushl %ebp 
   movl %esp, %ebp 
   subl $8, %esp                 /* 2 local variables (S_ADDR and D_ADDR) */

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es

   movl DEST, %eax
   movw BMP_SEG(%eax), %es       /* load segment selector */
   cld

   _align_
x_blit_from_mem_y_loop:          /* for each line... */
   movl SOURCE, %esi 
   movl SOURCE_Y, %eax
   movl BMP_LINE(%esi, %eax, 4), %eax
   addl SOURCE_X, %eax
   movl %eax, S_ADDR             /* got the source address */

   movl DEST, %esi 
   movl DEST_Y, %eax
   movl DEST_X, %ecx
   movl %ecx, %ebx
   shrl $2, %ebx
   addl BMP_LINE(%esi, %eax, 4), %ebx
   movl %ebx, D_ADDR             /* got the dest address */

   andb $3, %cl 
   movl $0x1102, %eax
   shlb %cl, %ah                 /* write plane mask in ax */

   xorl %ebx, %ebx               /* ebx = plane counter */ 

   _align_
x_blit_from_mem_plane_loop:      /* for each plane... */
   movl S_ADDR, %esi
   movl D_ADDR, %edi

   movl WIDTH, %ecx              /* calculate width */
   addl $3, %ecx
   subl %ebx, %ecx
   shrl $2, %ecx
   jz x_blit_from_mem_skip_plane

   movl $0x3C4, %edx
   outw %ax, %dx                 /* select the write plane */

   pushl %ecx                    /* save registers for 8-byte loop */
   pushl %eax

   shrl $3, %ecx                 /* calculate count value for 8-byte loop */
   jz x_blit_resume_from_loop8   /* if zero, only do the 1-byte loop */

   /* 8-byte loop: copy in 8-byte chunks */

   _align_
x_blit_from_mem_x_loop8:
   movb 12(%esi), %ah
   movb 28(%esi), %dh
   movb 8(%esi), %al
   movb 24(%esi), %dl
   shll $16, %eax
   shll $16, %edx
   movb 4(%esi), %ah
   movb 20(%esi), %dh
   movb (%esi), %al
   movb 16(%esi), %dl
   movl %eax, %es:(%edi)
   movl %edx, %es:4(%edi)
   addl $32, %esi                /* 8 bytes planar -> 32 bytes linear */
   addl $8, %edi
   decl %ecx
   jnz x_blit_from_mem_x_loop8

x_blit_resume_from_loop8:
   popl %eax                     /* restore registers after 8-byte loop */
   popl %ecx

   andl $7, %ecx                 /* calculate count for 1-byte loop */
   jz x_blit_from_mem_skip_plane /* if zero, we are done */

   /* 1-byte loop */

   _align_
x_blit_from_mem_x_loop: 
   movsb                         /* copy a pixel */
   addl $3, %esi                 /* fix up linear memory pointer */
   decl %ecx
   jg x_blit_from_mem_x_loop

x_blit_from_mem_skip_plane:
   rolb $1, %ah                  /* advance the plane position */
   adcl $0, D_ADDR
   incl S_ADDR

   incl %ebx                     /* next plane */
   cmpl $4, %ebx
   jl x_blit_from_mem_plane_loop

   incl SOURCE_Y                 /* next line */
   incl DEST_Y
   decl HEIGHT
   jg x_blit_from_mem_y_loop     /* loop */

   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp 
   ret                           /* end of _x_blit_from_memory() */

FUNC(_x_blit_from_memory_end)
   ret 




/* void _x_blit_to_memory(BITMAP *source, *dest, int source_x, source_y,
 *                          int dest_x, int dest_y, int width, int height);
 *  Blits to a memory bitmap from a mode-X screen. The area will already have
 *  been clipped when this routine is called.
 */
FUNC(_x_blit_to_memory)
   pushl %ebp
   movl %esp, %ebp
   subl $8, %esp                 /* 2 local variables (S_ADDR and D_ADDR) */

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %ds

   movl $0x3CE, %edx             /* load port address into edx */
   movl SOURCE, %eax
   movw %es:BMP_SEG(%eax), %ds   /* load segment selector */
   cld

   _align_
x_blit_to_mem_y_loop:            /* for each line... */
   movl DEST, %edi
   movl DEST_Y, %eax
   movl %es:BMP_LINE(%edi, %eax, 4), %eax
   addl DEST_X, %eax
   movl %eax, D_ADDR             /* got the dest address */

   movl SOURCE, %esi
   movl SOURCE_Y, %eax
   movl SOURCE_X, %ecx
   movl %ecx, %ebx
   shrl $2, %ebx
   addl %es:BMP_LINE(%esi, %eax, 4), %ebx
   movl %ebx, S_ADDR             /* got the source address */

   movb $4, %al                  /* set the correct plane */
   movb %cl, %ah
   andb $3, %ah

   xorl %ebx, %ebx               /* ebx = plane counter */

   _align_
x_blit_to_mem_plane_loop:        /* for each plane... */
   movl S_ADDR, %esi
   movl D_ADDR, %edi

   movl WIDTH, %ecx              /* calculate width */
   addl $3, %ecx
   subl %ebx, %ecx
   shrl $2, %ecx
   jz x_blit_to_mem_skip_plane

   outw %ax, %dx                 /* select the read plane */

   _align_
x_blit_to_mem_x_loop:
   movsb                         /* copy a pixel */
   addl $3, %edi                 /* fix up linear memory pointer */
   decl %ecx
   jg x_blit_to_mem_x_loop

x_blit_to_mem_skip_plane:
   addb $1, %ah                  /* advance the plane position */
   test $4, %ah
   jz x_blit_to_mem_no_add
   incl S_ADDR
   xor %ah, %ah
x_blit_to_mem_no_add:
   incl D_ADDR

   incl %ebx                     /* next plane */
   cmpl $4, %ebx
   jl x_blit_to_mem_plane_loop

   incl SOURCE_Y                 /* next line */
   incl DEST_Y
   decl HEIGHT
   jg x_blit_to_mem_y_loop       /* loop */

   popw %ds
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_blit_to_memory() */

FUNC(_x_blit_to_memory_end)
   ret




/* void x_latched_blit(BITMAP *source, BITMAP *dest, int source_x, source_y, 
		       int dest_x, int dest_y, int width, int height);
 *  Blits from one part of a mode-X screen to another, using the VGA latch
 *  registers. It should only be called when the two regions have the same 
 *  plane alignment, and the area should already have been clipped.
 */
   _align_
x_latched_blit:
   subl $8, %esp                 /* 2 local variables: */

   #define LMASK     -4(%ebp)
   #define RMASK     -8(%ebp)

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es
   pushw %ds

   movl $0, LMASK
   movl $0, RMASK

   movl WIDTH, %ecx              /* ecx = width */
   movl SOURCE_X, %eax           /* eax = source x */
   andl $3, %eax
   jz x_latched_blit_laligned

   negl %eax                     /* how many odd pixels on the left? */
   addl $4, %eax
   subl %eax, %ecx               /* adjust width */
   jge x_latched_blit_make_lmask

   negl %ecx                     /* special case for very narrow blits */
   subl %ecx, %eax
   movb GLOBL(_x_left_mask_table)(%eax), %ah
   shrb %cl, %ah
   xorl %ecx, %ecx
   jmp x_latched_blit_made_lmask

   _align_
x_latched_blit_make_lmask:
   movb GLOBL(_x_left_mask_table)(%eax), %ah

x_latched_blit_made_lmask:
   movb $2, %al                  /* store left edge mask */
   movw %ax, LMASK

   _align_
x_latched_blit_laligned:
   movl %ecx, %eax
   shrl $2, %ecx
   movl %ecx, %ebx               /* store main loop counter in ebx */

   andl $3, %eax                 /* how many odd pixels on the right? */
   jz x_latched_blit_raligned

   movb GLOBL(_x_right_mask_table)(%eax), %ah
   movb $2, %al
   movw %ax, RMASK

   _align_
x_latched_blit_raligned:
   movl DEST, %edx               /* load segment selectors */
   movl BMP_SEG(%edx), %eax 
   movw %ax, %ds
   movw %ax, %es
   cld

   movl $0x3CE, %edx
   movb $5, %al
   outb %al, %dx                 /* 3CE index 5 */
   incl %edx
   inb %dx, %al                  /* read+alter */
   andb $0xFC, %al 
   orb $1, %al 
   outb %al, %dx                 /* enable the latches */

   shrl $2, SOURCE_X             /* adjust the x offsets */
   shrl $2, DEST_X

   movl $0x3C4, %edx             /* preload port address for plane enables */

   _align_
x_latched_blit_loop:
   movl SOURCE, %ecx
   movl SOURCE_Y, %eax           /* get source address in esi */
   movl %ss:BMP_LINE(%ecx, %eax, 4), %esi
   addl SOURCE_X, %esi 

   movl DEST, %ecx
   movl DEST_Y, %eax             /* get dest address in edi */
   movl %ss:BMP_LINE(%ecx, %eax, 4), %edi
   addl DEST_X, %edi

   movl LMASK, %eax              /* copy odd pixels on the left? */
   orl %eax, %eax
   jz x_latched_no_left_pixels

   outw %ax, %dx                 /* copy on the left */
   movsb

   _align_
x_latched_no_left_pixels:
   orl %ebx, %ebx                /* main copy loop? */
   movl %ebx, %ecx
   jz x_latched_no_middle_pixels

   movl $0x0F02, %eax            /* enable all planes */
   outw %ax, %dx

   rep ; movsb                   /* do the bulk of the copy */

x_latched_no_middle_pixels:
   movl RMASK, %eax              /* copy odd pixels on the right? */
   orl %eax, %eax
   jz x_latched_no_right_pixels

   outw %ax, %dx                 /* copy on the right */
   movsb

   _align_
x_latched_no_right_pixels:
   incl SOURCE_Y                 /* next line */
   incl DEST_Y
   decl HEIGHT
   jg x_latched_blit_loop        /* loop */

   movl $0x3CE, %edx 
   movb $5, %al                  /* 3CE index 5 */
   outb %al, %dx
   incl %edx
   inb %dx, %al                  /* read+alter */
   andb $0xFC, %al 
   outb %al, %dx                 /* disable the latches */

   popw %ds
   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of x_latched_blit() */




/* void _x_blit(BITMAP *source, BITMAP *dest, int source_x, source_y, 
		int dest_x, int dest_y, int width, int height);
 *  Blits from one part of a mode-X screen to another, drawing plane-by-plane
 *  (only safe when the two areas don't overlap). The area will already have 
 *  been clipped when this routine is called.
 */
FUNC(_x_blit)
   pushl %ebp
   movl %esp, %ebp

   movl SOURCE_X, %eax           /* use latches if the areas are aligned */
   andl $3, %eax
   movl DEST_X, %edx
   andl $3, %edx
   cmpl %eax, %edx
   je x_latched_blit

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es
   pushw %ds

   movl DEST, %edx               /* load segment selectors */
   movl BMP_SEG(%edx), %eax 
   movw %ax, %ds
   movw %ax, %es
   cld

   _align_
x_blit_loop:
   xorl %ebx, %ebx               /* ebx = current plane */

   _align_
x_blit_next_plane:
   movl SOURCE_X, %esi
   addl %ebx, %esi               /* esi = source_x coordinate */

   movl %esi, %ecx
   andb $3, %cl
   movb %cl, %ah
   movb $4, %al
   movl $0x3CE, %edx
   outw %ax, %dx                 /* set the read plane */

   movl DEST_X, %edi
   addl %ebx, %edi               /* edi = dest_x coordinate */

   movl %edi, %ecx
   andb $3, %cl
   movl $0x102, %eax
   shlb %cl, %ah
   movl $0x3C4, %edx
   outw %ax, %dx                 /* set the write plane */

   shrl $2, %esi                 /* get source address in esi */
   movl SOURCE, %edx
   movl SOURCE_Y, %eax
   addl %ss:BMP_LINE(%edx, %eax, 4), %esi

   shrl $2, %edi                 /* get dest address in edi */
   movl DEST, %edx
   movl DEST_Y, %eax
   addl %ss:BMP_LINE(%edx, %eax, 4), %edi

   movl WIDTH, %ecx              /* get counter in ecx */
   addl $3, %ecx
   subl %ebx, %ecx
   shrl $2, %ecx
   jz x_blit_skip_plane

   shrl $1, %ecx                 /* halve for word copy */
   jnc x_blit_no_byte

   movsb                         /* copy an odd byte? */

   _align_
x_blit_no_byte:
   shrl $1, %ecx                 /* halve again for long copy */
   jnc x_blit_no_word

   movsw                         /* copy an odd word? */

   _align_
x_blit_no_word:
   jz x_blit_skip_plane

   rep ; movsl;                  /* copy a plane of data */

x_blit_skip_plane:
   incl %ebx                     /* next plane */
   cmpl $4, %ebx
   jl x_blit_next_plane

   incl SOURCE_Y                 /* next line */
   incl DEST_Y
   decl HEIGHT
   jg x_blit_loop                /* loop */

   popw %ds
   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_blit() */




/* void _x_blit_forward(BITMAP *source, BITMAP *dest, int source_x, source_y, 
			int dest_x, int dest_y, int width, int height);
 *  Blits from one part of a mode-X screen to another, drawing from top to
 *  bottom (much slower than doing it plane-by-plane, but sometimes required
 *  if the regions overlap). The area will already have been clipped when 
 *  this routine is called.
 */
FUNC(_x_blit_forward)
   pushl %ebp
   movl %esp, %ebp

   movl SOURCE_X, %eax           /* use latches if the areas are aligned */
   andl $3, %eax
   movl DEST_X, %edx
   andl $3, %edx
   cmpl %eax, %edx
   je x_latched_blit

   subl $4, %esp                 /* 1 local variable (COUNT) */

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es

   movl DEST, %edx               /* load segment selector */
   movl BMP_SEG(%edx), %eax
   movl %eax, %es

   _align_
x_blit_forward_y_loop:
   movl WIDTH, %eax              /* initialise x loop counter */
   movl %eax, COUNT

   movl SOURCE_X, %esi           /* esi = source_x coordinate */
   movl DEST_X, %edi             /* edi = dest_x coordinate */

   movl %esi, %ebx               /* get read plane mask in bx */
   andb $3, %bl
   movb %bl, %bh
   movb $4, %bl 

   movl %edi, %ecx               /* get write plane mask in cx */
   andb $3, %cl
   movb $0x11, %ch
   shlb %cl, %ch
   movb $2, %cl 

   shrl $2, %esi                 /* get source address in esi */
   movl SOURCE, %edx
   movl SOURCE_Y, %eax
   addl BMP_LINE(%edx, %eax, 4), %esi

   shrl $2, %edi                 /* get dest address in edi */
   movl DEST, %edx
   movl DEST_Y, %eax
   addl BMP_LINE(%edx, %eax, 4), %edi

   _align_
x_blit_forward_x_loop:
   movl $0x3CE, %edx             /* set the read plane */
   movl %ebx, %eax
   outw %ax, %dx 

   movl $0x3C4, %edx             /* set the write plane */
   movl %ecx, %eax
   outw %ax, %dx 

   movb %es:(%esi), %al          /* copy a pixel */
   movb %al, %es:(%edi) 

   incb %bh                      /* advance the read position */
   testb $4, %bh
   jz x_blit_forward_no_read_wrap

   xorb %bh, %bh                 /* wraps every four pixels */
   incl %esi

   _align_
x_blit_forward_no_read_wrap:
   rolb $1, %ch                  /* advance the write position */
   adcl $0, %edi

   _align_
x_blit_forward_no_write_wrap:
   decl COUNT
   jg x_blit_forward_x_loop      /* x loop */

   incl SOURCE_Y                 /* move on to the next line */
   incl DEST_Y
   decl HEIGHT
   jg x_blit_forward_y_loop      /* y loop */

   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_blit_forward() */




/* void _x_blit_backward(BITMAP *source, BITMAP *dest, int source_x, source_y, 
			int dest_x, int dest_y, int width, int height);
 *  Blits from one part of a mode-X screen to another, drawing from bottom 
 *  to top (much slower than doing it plane-by-plane, but sometimes required
 *  if the regions overlap). The area will already have been clipped when 
 *  this routine is called.
 */
FUNC(_x_blit_backward)
   pushl %ebp
   movl %esp, %ebp
   subl $4, %esp                 /* 1 local variable (COUNT) */

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es

   movl HEIGHT, %eax             /* y adjust for starting at the bottom */
   decl %eax
   addl %eax, SOURCE_Y
   addl %eax, DEST_Y

   movl WIDTH, %eax              /* x adjust for starting at the right */
   decl %eax
   addl %eax, SOURCE_X
   addl %eax, DEST_X

   movl DEST, %edx               /* load segment selector */
   movl BMP_SEG(%edx), %eax
   movl %eax, %es

   _align_
x_blit_backward_y_loop:
   movl WIDTH, %eax              /* initialise x loop counter */
   movl %eax, COUNT

   movl SOURCE_X, %esi           /* esi = source_x coordinate */
   movl DEST_X, %edi             /* edi = dest_x coordinate */

   movl %esi, %ebx               /* get read plane mask in bx */
   andb $3, %bl
   movb %bl, %bh
   movb $4, %bl 

   movl %edi, %ecx               /* get write plane mask in cx */
   andb $3, %cl
   movb $0x11, %ch
   shlb %cl, %ch
   movb $2, %cl 

   shrl $2, %esi                 /* get source address in esi */
   movl SOURCE, %edx
   movl SOURCE_Y, %eax
   addl BMP_LINE(%edx, %eax, 4), %esi

   shrl $2, %edi                 /* get dest address in edi */
   movl DEST, %edx
   movl DEST_Y, %eax
   addl BMP_LINE(%edx, %eax, 4), %edi

   _align_
x_blit_backward_x_loop:
   movl $0x3CE, %edx             /* set the read plane */
   movl %ebx, %eax
   outw %ax, %dx 

   movl $0x3C4, %edx             /* set the write plane */
   movl %ecx, %eax
   outw %ax, %dx 

   movb %es:(%esi), %al          /* copy a pixel */
   movb %al, %es:(%edi) 

   decb %bh                      /* advance the read position */
   jge x_blit_backward_no_read_wrap

   movb $3, %bh                  /* wraps every four pixels */
   decl %esi

   _align_
x_blit_backward_no_read_wrap:
   rorb $1, %ch                  /* advance the write position */
   sbbl $0, %edi

   _align_
x_blit_backward_no_write_wrap:
   decl COUNT
   jg x_blit_backward_x_loop     /* x loop */

   decl SOURCE_Y                 /* move on to the next line */
   decl DEST_Y
   decl HEIGHT
   jg x_blit_backward_y_loop     /* y loop */

   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_blit_backward() */




/* void _x_masked_blit(BITMAP *source, *dest, int source_x, source_y, 
 *                     int dest_x, int dest_y, int width, int height);
 *  Blits from a memory bitmap to a mode-X screen, skipping zero pixels. 
 *  The area will already have been clipped when this routine is called.
 */
FUNC(_x_masked_blit)
   pushl %ebp 
   movl %esp, %ebp 
   subl $8, %esp                 /* 2 local variables (S_ADDR and D_ADDR) */

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es

   movl $0x3C4, %edx             /* load port address into edx */
   movl DEST, %eax
   movw BMP_SEG(%eax), %es       /* load segment selector */
   cld

   _align_
x_masked_blit_y_loop:            /* for each line... */
   movl SOURCE, %esi 
   movl SOURCE_Y, %eax
   movl BMP_LINE(%esi, %eax, 4), %eax
   addl SOURCE_X, %eax
   movl %eax, S_ADDR             /* got the source address */

   movl DEST, %esi 
   movl DEST_Y, %eax
   movl DEST_X, %ecx
   movl %ecx, %ebx
   shrl $2, %ebx
   addl BMP_LINE(%esi, %eax, 4), %ebx
   movl %ebx, D_ADDR             /* got the dest address */

   andb $3, %cl 
   movl $0x1102, %eax
   shlb %cl, %ah                 /* write plane mask in ax */

   xorl %ebx, %ebx               /* ebx = plane counter */ 

   _align_
x_masked_blit_plane_loop:        /* for each plane... */
   movl S_ADDR, %esi
   movl D_ADDR, %edi

   movl WIDTH, %ecx              /* calculate width */
   addl $3, %ecx
   subl %ebx, %ecx
   shrl $2, %ecx
   jz x_masked_blit_skip_plane

   outw %ax, %dx                 /* select the write plane */
   pushl %eax

   _align_
x_masked_blit_x_loop: 
   lodsb                         /* read a pixel */
   orb %al, %al                  /* test it */
   jz x_masked_blit_skip_pixel

   stosb                         /* write the pixel */
   addl $3, %esi                 /* fix up linear memory pointer */
   decl %ecx
   jg x_masked_blit_x_loop
   jmp x_masked_blit_loop_done

   _align_
x_masked_blit_skip_pixel:
   incl %edi
   addl $3, %esi                 /* fix up linear memory pointer */
   decl %ecx
   jg x_masked_blit_x_loop

x_masked_blit_loop_done:
   popl %eax

x_masked_blit_skip_plane:
   rolb $1, %ah                  /* advance the plane position */
   adcl $0, D_ADDR
   incl S_ADDR

   incl %ebx                     /* next plane */
   cmpl $4, %ebx
   jl x_masked_blit_plane_loop

   incl SOURCE_Y                 /* next line */
   incl DEST_Y
   decl HEIGHT
   jg x_masked_blit_y_loop     /* loop */

   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp 
   ret                           /* end of _x_masked_blit() */




#undef SOURCE
#undef DEST
#undef SOURCE_X
#undef SOURCE_Y
#undef DEST_X 
#undef DEST_Y 
#undef WIDTH 
#undef HEIGHT 
#undef S_ADDR 
#undef D_ADDR

#define DEST         ARG1        /* parameters to the sprite draw functions */
#define SOURCE       ARG2
#define DEST_X       ARG3
#define DEST_Y       ARG4

#define S_ADDR    -4(%ebp)
#define D_ADDR    -8(%ebp)
#define SOURCE_Y  -12(%ebp)
#define WIDTH     -16(%ebp)
#define HEIGHT    -20(%ebp)
#define SOURCE_X  -24(%ebp)




/* void _x_draw_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y);
 *  Draws a sprite onto a mode-X screen.
 */
FUNC(_x_draw_sprite)
   pushl %ebp
   movl %esp, %ebp
   subl $24, %esp                 /* 6 local variables */

   pushl %ebx
   pushl %esi
   pushl %edi
   pushw %es

   movl $0x3C4, %edx             /* load port address into edx */
   movl DEST, %eax
   movw BMP_SEG(%eax), %es       /* load segment selector */
   cld

   movl SOURCE, %esi
   movl BMP_W(%esi), %eax
   movl %eax, WIDTH
   movl BMP_H(%esi), %eax
   movl %eax, HEIGHT
   movl $0, SOURCE_X
   movl $0, SOURCE_Y             /* set source variables */

   movl DEST, %esi

   cmpl $0, BMP_CLIP(%esi)       /* clipping -> ... */
   jz x_draw_sprite_noclip

   movl BMP_CL(%esi), %eax
   subl DEST_X, %eax
   jle x_drspr_noclip_lx
   addl %eax, DEST_X
   addl %eax, SOURCE_X
   subl %eax, WIDTH
   jle x_draw_sprite_not
x_drspr_noclip_lx:
   movl BMP_CR(%esi), %eax
   subl DEST_X, %eax
   subl WIDTH, %eax
   jge x_drspr_noclip_rx
   addl %eax, WIDTH
   jle x_draw_sprite_not
x_drspr_noclip_rx:
   movl BMP_CT(%esi), %eax
   subl DEST_Y, %eax
   jle x_drspr_noclip_ty
   addl %eax, DEST_Y
   addl %eax, SOURCE_Y
   subl %eax, HEIGHT
   jle x_draw_sprite_not
x_drspr_noclip_ty:
   movl BMP_CB(%esi), %eax
   subl DEST_Y, %eax
   subl HEIGHT, %eax
   jge x_drspr_noclip_by
   addl %eax, HEIGHT
   jle x_draw_sprite_not

   _align_
x_drspr_noclip_by:
x_draw_sprite_noclip:            /* <--- */
x_draw_sprite_y_loop:            /* for each line... */
   movl SOURCE, %esi
   movl SOURCE_Y, %eax
   movl BMP_LINE(%esi,%eax,4), %eax
   addl SOURCE_X, %eax
   movl %eax, S_ADDR             /* got the source address */

   movl DEST, %esi
   movl DEST_Y, %eax
   movl DEST_X, %ecx
   movl %ecx, %ebx
   shrl $2, %ebx
   addl BMP_LINE(%esi, %eax, 4), %ebx
   movl %ebx, D_ADDR             /* got the dest address */

   andb $3, %cl
   movb $0x11, %ah
   shlb %cl, %ah                 /* write plane mask in ax */

   xorl %ebx, %ebx               /* ebx = plane counter */

   _align_
x_draw_sprite_plane_loop:        /* for each plane... */
   movl S_ADDR, %esi
   movl D_ADDR, %edi

   movl WIDTH, %ecx              /* calculate width */
   addl $3, %ecx
   subl %ebx, %ecx
   shrl $2, %ecx
   jz x_draw_sprite_skip_plane

   movb $2, %al
   outw %ax, %dx                 /* select the write plane */

   _align_
x_draw_sprite_x_loop:
   lodsb                         /* load a pixel */
   orb %al, %al 
   jz x_draw_sprite_no_draw      /* skip if zero */

   stosb                         /* store a pixel */
   addl $3, %esi                 /* fix up linear memory pointer */
   decl %ecx
   jg x_draw_sprite_x_loop
   jmp x_draw_sprite_skip_plane

   _align_
x_draw_sprite_no_draw:
   incl %edi                     /* skip a pixel */
   addl $3, %esi                 /* fix up linear memory pointer */
   decl %ecx
   jg x_draw_sprite_x_loop

x_draw_sprite_skip_plane:
   rolb $1, %ah                  /* advance the plane position */
   adcl $0, D_ADDR
   incl S_ADDR

   incl %ebx                     /* next plane */
   cmpl $4, %ebx
   jl x_draw_sprite_plane_loop

   incl SOURCE_Y                 /* next line */
   incl DEST_Y
   decl HEIGHT
   jg x_draw_sprite_y_loop       /* loop */

x_draw_sprite_not:
   popw %es
   popl %edi
   popl %esi
   popl %ebx
   movl %ebp, %esp
   popl %ebp
   ret                           /* end of _x_draw_sprite() */

FUNC(_x_draw_sprite_end)
   ret

.section .note.GNU-stack,"",@progbits
#endif        /* (!defined ALLEGRO_LINUX) || ((defined ALLEGRO_LINUX_VGA) && ... */
