text: include(__link__.m4)
  
  #ifndef __SP1_ZX_H__
  #define __SP1_ZX_H__
  
  ///////////////////////////////////////////////////////////
  //                  SPRITE PACK v3.0                     //
  //             Sinclair Spectrum Version                 //
  //            aralbrec - April / May 2006                //
  ///////////////////////////////////////////////////////////
  //                                                       //
  //    Software sprite engine for bitmapped displays      //
  //                                                       //
  //    * Differential screen update                       //
  //    * Any number and any size of sprites               //
  //    * Clipping to character cell boundaries            //
  //    * Background tile animation                        //
  //                                                       //
  //       See the wiki for documentation details          //
  //             http://www.z88dk.org/wiki                 //
  //                                                       //
  ///////////////////////////////////////////////////////////
  
  #include <stdint.h>
  
  ///////////////////////////////////////////////////////////
  //                  DATA STRUCTURES                      //
  ///////////////////////////////////////////////////////////
  
  struct sp1_Rect {
  
     uint8_t row;
     uint8_t col;
     uint8_t width;
     uint8_t height;
  
  };
  
  struct sp1_update;
  struct sp1_ss;
  struct sp1_cs;
  
  struct sp1_update {                   // "update structs" - 10 bytes - Every tile in the display area managed by SP1 is described by one of these
  
     uint8_t              nload;          // +0 bit 7 = 1 for invalidated, bit 6 = 1 for removed, bits 5:0 = number of occluding sprites present + 1
     uint8_t              colour;         // +1 background tile attribute
     uint16_t             tile;           // +2 background 16-bit tile code (if MSB != 0 taken as address of graphic, else lookup in tile array)
     struct sp1_cs       *slist;          // +4 BIG ENDIAN ; list of sprites occupying this tile (MSB = 0 if none) points at struct sp1_cs.attr_mask
     struct sp1_update   *ulist;          // +6 BIG ENDIAN ; next update struct in list of update structs queued for draw (MSB = 0 if none)
     uint8_t             *screen;         // +8 address in display file where this tile is drawn
  
  };
  
  struct sp1_ss {                       // "sprite structs" - 20 bytes - Every sprite is described by one of these
  
     uint8_t              row;            // +0  current y tile-coordinate
     uint8_t              col;            // +1  current x tile-coordinate
     uint8_t              width;          // +2  width of sprite in tiles
     uint8_t              height;         // +3  height of sprite in tiles
  
     uint8_t              vrot;           // +4  bit 7 = 1 for 2-byte graphical definition else 1-byte, bits 2:0 = current vertical rotation (0..7)
     uint8_t              hrot;           // +5  current horizontal rotation (0..7)
  
     uint8_t             *frame;          // +6  current sprite frame address added to graphic pointers
  
     uint8_t              res0;           // +8  "LD A,n" opcode
     uint8_t              e_hrot;         // +9  effective horizontal rotation = MSB of rotation table to use
     uint8_t              res1;           // +10 "LD BC,nn" opcode
     uint16_t             e_offset;       // +11 effective offset to add to graphic pointers, equals result of vertical rotation + frame addr
     uint8_t              res2;           // +13 "EX DE,HL" opcode
     uint8_t              res3;           // +14 "JP (HL)" opcode
  
     struct sp1_cs     *first;          // +15 BIG ENDIAN ; first struct sp1_cs of this sprite
  
     uint8_t              xthresh;        // +17 hrot must be at least this number of pixels for last column of sprite to be drawn (1 default)
     uint8_t              ythresh;        // +18 vrot must be at least this number of pixels for last row of sprite to be drawn (1 default)
     uint8_t              nactive;        // +19 number of struct sp1_cs cells on display (written by sp1_MoveSpr*)
  
  };
  
  struct sp1_cs {                       // "char structs" - 24 bytes - Every sprite is broken into pieces fitting into a tile, each of which is described by one of these
  
     struct sp1_cs     *next_in_spr;    // +0  BIG ENDIAN ; next sprite char within same sprite in row major order (MSB = 0 if none)
  
     struct sp1_update *update;         // +2  BIG ENDIAN ; tile this sprite char currently occupies (MSB = 0 if none)
  
     uint8_t              plane;          // +4  plane sprite occupies, 0 = closest to viewer
     uint8_t              type;           // +5  bit 7 = 1 occluding, bit 6 = 1 last column, bit 5 = 1 last row, bit 4 = 1 clear pixelbuffer
     uint8_t              attr_mask;      // +6  attribute mask logically ANDed with underlying attribute, default = 0xff for transparent
     uint8_t              attr;           // +7  sprite colour, logically ORed to form final colour, default = 0 for transparent
  
     void                *ss_draw;        // +8  struct sp1_ss + 8 bytes ; points at code embedded in sprite struct sp1_ss
  
     uint8_t              res0;           // +10 typically "LD HL,nn" opcode
     uint8_t             *def;            // +11 graphic definition pointer
     uint8_t              res1;           // +13 typically "LD IX,nn" opcode
     uint8_t              res2;           // +14
     uint8_t             *l_def;          // +15 graphic definition pointer for sprite character to left of this one
     uint8_t              res3;           // +17 typically "CALL nn" opcode
     void                *draw;           // +18 & draw function for this sprite char
  
     struct sp1_cs       *next_in_upd;    // +20 BIG ENDIAN ; & sp1_cs.attr_mask of next sprite occupying the same tile (MSB = 0 if none)
     struct sp1_cs       *prev_in_upd;    // +22 BIG ENDIAN ; & sp1_cs.next_in_upd of previous sprite occupying the same tile
  
  };
  
  struct sp1_ap {                       // "attribute pairs" - 2 bytes - A struct to hold sprite attribute and mask pairs
  
     uint8_t              attr_mask;      // +0 attribute mask logically ANDed with underlying attribute = 0xff for transparent
     uint8_t              attr;           // +1 sprite colour, logically ORed to form final colour = 0 for transparent
  
  };
  
  struct sp1_tp {                       // "tile pairs" - 3 bytes - A struct to hold background colour and tile pairs
  
     uint8_t              attr;           // +0 colour
     uint16_t             tile;           // +1 tile code
  
  };
  
  struct sp1_pss {                      // "print string struct" - 11 bytes - A struct holding print state information
  
     struct sp1_Rect     *bounds;         // +0 rectangular boundary within which printing will be allowed
     uint8_t              flags;          // +2 bit 0=invalidate?, 1=xwrap?, 2=yinc?, 3=ywrap?
     uint8_t              x;              // +3 current x coordinate of cursor with respect to top left corner of bounds
     uint8_t              y;              // +4 current y coordinate of cursor with respect to top left corner of bounds
     uint8_t              attr_mask;      // +5 current attribute mask
     uint8_t              attr;           // +6 current attribute
     struct sp1_update   *pos;            // +7 RESERVED struct sp1_update associated with current cursor coordinates
     void                *visit;          // +9 void (*visit)(struct sp1_update *) function, set to 0 for none
     
  };
  
  ///////////////////////////////////////////////////////////
  //                      SPRITES                          //
  ///////////////////////////////////////////////////////////
  
  // sprite type bits
  
  #define SP1_TYPE_OCCLUDE   0x80       // background and sprites underneath will not be drawn
  #define SP1_TYPE_BGNDCLR   0x10       // for occluding sprites, copy background tile into pixel buffer before draw
  
  #define SP1_TYPE_2BYTE     0x40       // sprite graphic consists of (mask,graph) pairs, valid only in sp1_CreateSpr()
  #define SP1_TYPE_1BYTE     0x00       // sprite graphic consists of graph only, valid only in sp1_CreateSpr()
  
  // sprite attribute masks, logically AND together if necessary
  // zx.h contains defines for INK and PAPER colours
  
  #define SP1_AMASK_TRANS    0xff       // attribute mask for a transparent-colour sprite
  #define SP1_AMASK_INK      0xf8       // attribute mask for an ink-only sprite
  #define SP1_AMASK_PAPER    0xc7       // attribute mask for a paper-only sprite
  #define SP1_AMASK_NOFLASH  0x7f       // attribute mask for no-flash
  #define SP1_AMASK_NOBRIGHT 0xbf       // attribute mask for no-bright
  #define SP1_ATTR_TRANS     0x00       // attribute for a transparent-colour sprite
  
  // prototype struct_sp1_ss and struct_sp1_cs that can be used to initialize empty structures
  
  extern struct sp1_cs  sp1_struct_cs_prototype;
  extern struct sp1_ss  sp1_struct_ss_prototype;
  
  // draw functions for sprites with two-byte graphical definitions, ie (mask,graphic) pairs
  
  extern void    SP1_DRAW_MASK2(void);        // masked sprite 2-byte definition (mask,graph) pairs ; sw rotation will use MASK2_NR if no rotation necessary
  extern void    SP1_DRAW_MASK2NR(void);      // masked sprite 2-byte definition (mask,graph) pairs ; no rotation applied, graphic always drawn at exact tile boundary
  extern void    SP1_DRAW_MASK2LB(void);      // masked sprite 2-byte definition (mask,graph) pairs ; sw rotation as MASK2 but for left boundary of sprite only
  extern void    SP1_DRAW_MASK2RB(void);      // masked sprite 2-byte definition (mask,graph) pairs ; sw rotation as MASK2 but for right boundary of sprite only
  
  extern void    SP1_DRAW_LOAD2(void);        // load sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation will use LOAD2_NR if no rotation necessary
  extern void    SP1_DRAW_LOAD2NR(void);      // load sprite 2-byte definition (mask,graph) pairs mask ignored; no rotation applied, always drawn at exact tile boundary
  extern void    SP1_DRAW_LOAD2LB(void);      // load sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as LOAD2 but for left boundary of sprite only
  extern void    SP1_DRAW_LOAD2RB(void);      // load sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as LOAD2 but for right boundary of sprite only
  
  extern void    SP1_DRAW_OR2(void);          // or sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation will use OR2_NR if no rotation necessary
  extern void    SP1_DRAW_OR2NR(void);        // or sprite 2-byte definition (mask,graph) pairs mask ignored; no rotation applied, always drawn at exact tile boundary
  extern void    SP1_DRAW_OR2LB(void);        // or sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as OR2 but for left boundary of sprite only
  extern void    SP1_DRAW_OR2RB(void);        // or sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as OR2 but for right boundary of sprite only
  
  extern void    SP1_DRAW_XOR2(void);         // xor sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation will use XOR2_NR if no rotation necessary
  extern void    SP1_DRAW_XOR2NR(void);       // xor sprite 2-byte definition (mask,graph) pairs mask ignored; no rotation applied, always drawn at exact tile boundary
  extern void    SP1_DRAW_XOR2LB(void);       // xor sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as XOR2 but for left boundary of sprite only
  extern void    SP1_DRAW_XOR2RB(void);       // xor sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as XOR2 but for right boundary of sprite only
  
  extern void    SP1_DRAW_LOAD2LBIM(void);    // load sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as LOAD2 but for left boundary of sprite w/ implied mask
  extern void    SP1_DRAW_LOAD2RBIM(void);    // load sprite 2-byte definition (mask,graph) pairs mask ignored; sw rotation as LOAD2 but for right boundary of sprite w/ implied mask
  
  // draw functions for sprites with one-byte graphical definitions, ie no mask just graphics
  
  extern void    SP1_DRAW_LOAD1(void);        // load sprite 1-byte definition graph only no mask; sw rotation will use LOAD1_NR if no rotation necessary
  extern void    SP1_DRAW_LOAD1NR(void);      // load sprite 1-byte definition graph only no mask; no rotation applied, always drawn at exact tile boundary
  extern void    SP1_DRAW_LOAD1LB(void);      // load sprite 1-byte definition graph only no mask; sw rotation as LOAD1 but for left boundary of sprite only
  extern void    SP1_DRAW_LOAD1RB(void);      // load sprite 1-byte definition graph only no mask; sw rotation as LOAD1 but for right boundary of sprite only
  
  extern void    SP1_DRAW_OR1(void);          // or sprite 1-byte definition graph only no mask; sw rotation will use OR1_NR if no rotation necessary
  extern void    SP1_DRAW_OR1NR(void);        // or sprite 1-byte definition graph only no mask; no rotation applied, always drawn at exact tile boundary
  extern void    SP1_DRAW_OR1LB(void);        // or sprite 1-byte definition graph only no mask; sw rotation as OR1 but for left boundary of sprite only
  extern void    SP1_DRAW_OR1RB(void);        // or sprite 1-byte definition graph only no mask; sw rotation as OR1 but for right boundary of sprite only
  
  extern void    SP1_DRAW_XOR1(void);         // xor sprite 1-byte definition graph only no mask; sw rotation will use XOR1_NR if no rotation necessary
  extern void    SP1_DRAW_XOR1NR(void);       // xor sprite 1-byte definition graph only no mask; no rotation applied, always drawn at exact tile boundary
  extern void    SP1_DRAW_XOR1LB(void);       // xor sprite 1-byte definition graph only no mask; sw rotation as XOR1 but for left boundary of sprite only
  extern void    SP1_DRAW_XOR1RB(void);       // xor sprite 1-byte definition graph only no mask; sw rotation as XOR1 but for right boundary of sprite only
  
  extern void    SP1_DRAW_LOAD1LBIM(void);    // load sprite 1-byte definition graph only no mask; sw rotation as LOAD1 but for left boundary of sprite with implied mask
  extern void    SP1_DRAW_LOAD1RBIM(void);    // load sprite 1-byte definition graph only no mask; sw rotation as LOAD1 but for right boundary of sprite with implied mask
  
  // draw functions for sprites without graphics
  
  extern void    SP1_DRAW_ATTR(void);         // pixels are not drawn, only attributes
  
  // void *hook1  <->  void [ __FASTCALL__ ] (*hook1)(uint count, struct sp1_cs *c)      // if __FASTCALL__ only struct sp1_cs* passed
  // void *hook2  <->  void [ __FASTCALL__ ] (*hook2)(uint count, struct sp1_update *u)  // if __FASTCALL__ only struct sp1_update* passed
  //
  // void *drawf  <->  void (*drawf)(void)     // sprite draw function containing draw code and data for struct_sp1_cs
  
  __DPROTO(,,struct sp1_ss,*,sp1_CreateSpr,void *drawf,uint16_t type,uint16_t height,int graphic,uint16_t plane)
  __DPROTO(,,uint16_t,,sp1_AddColSpr,struct sp1_ss *s,void *drawf,uint16_t type,int graphic,uint16_t plane)
  __DPROTO(,,void,,sp1_ChangeSprType,struct sp1_cs *c,void *drawf)
  __DPROTO(,,void,,sp1_DeleteSpr,struct sp1_ss *s)// only call after sprite is removed from screen
  
  __DPROTO(,,void,,sp1_MoveSprAbs,struct sp1_ss *s,struct sp1_Rect *clip,void *frame,uint16_t row,uint16_t col,uint16_t vrot,uint16_t hrot)
  __DPROTO(,,void,,sp1_MoveSprRel,struct sp1_ss *s,struct sp1_Rect *clip,void *frame,int rel_row,int rel_col,int rel_vrot,int rel_hrot)
  __DPROTO(,,void,,sp1_MoveSprPix,struct sp1_ss *s,struct sp1_Rect *clip,void *frame,uint16_t x,uint16_t y)
  
  __DPROTO(,,void,,sp1_IterateSprChar,struct sp1_ss *s,void *hook1)
  __DPROTO(,,void,,sp1_IterateUpdateSpr,struct sp1_ss *s,void *hook2)
  
  __DPROTO(,,void,,sp1_GetSprClrAddr,struct sp1_ss *s,uint8_t **sprdest)
  __DPROTO(,,void,,sp1_PutSprClr,uint8_t **sprdest,struct sp1_ap *src,uint16_t n)
  __DPROTO(,,void,,sp1_GetSprClr,uint8_t **sprsrc,struct sp1_ap *dest,uint16_t n)
  
  __DPROTO(,,void,*,sp1_PreShiftSpr,uint16_t flag,uint16_t height,uint16_t width,void *srcframe,void *destframe,uint16_t rshift)
  
  // some functions for displaying independent struct_sp1_cs not connected with any sprites; useful as foreground elements
  // if not using a no-rotate (NR) type sprite draw function, must manually init the sp1_cs.ldef member after calling sp1_InitCharStruct()
  
  __DPROTO(,,void,,sp1_InitCharStruct,struct sp1_cs *cs,void *drawf,uint16_t type,void *graphic,uint16_t plane)
  __DPROTO(,,void,,sp1_InsertCharStruct,struct sp1_update *u,struct sp1_cs *cs)
  __DPROTO(,,void,,sp1_RemoveCharStruct,struct sp1_cs *cs)
  
  ///////////////////////////////////////////////////////////
  //                       TILES                           //
  ///////////////////////////////////////////////////////////
  
  #define SP1_RFLAG_TILE          0x01
  #define SP1_RFLAG_COLOUR        0x02
  #define SP1_RFLAG_SPRITE        0x04
  
  #define SP1_PSSFLAG_INVALIDATE  0x01
  #define SP1_PSSFLAG_XWRAP       0x02
  #define SP1_PSSFLAG_YINC        0x04
  #define SP1_PSSFLAG_YWRAP       0x08
  
  __DPROTO(,,void,*,sp1_TileEntry,uint16_t c,void *def)
  
  __DPROTO(,,void,,sp1_PrintAt,uint16_t row,uint16_t col,uint16_t colour,uint16_t tile)
  __DPROTO(,,void,,sp1_PrintAtInv,uint16_t row,uint16_t col,uint16_t colour,uint16_t tile)
  __DPROTO(`b,c',`b,c',uint16_t,,sp1_ScreenStr,uint16_t row,uint16_t col)
  __DPROTO(`b,c',`b,c',uint16_t,,sp1_ScreenAttr,uint16_t row,uint16_t col)
  
  __DPROTO(,,void,,sp1_PrintString,struct sp1_pss *ps,uint8_t *s)
  __DPROTO(,,void,,sp1_SetPrintPos,struct sp1_pss *ps,uint16_t row,uint16_t col)
  
  __DPROTO(,,void,,sp1_GetTiles,struct sp1_Rect *r,struct sp1_tp *dest)
  __DPROTO(,,void,,sp1_PutTiles,struct sp1_Rect *r,struct sp1_tp *src)
  __DPROTO(,,void,,sp1_PutTilesInv,struct sp1_Rect *r,struct sp1_tp *src)
  
  __DPROTO(,,void,,sp1_ClearRect,struct sp1_Rect *r,uint16_t colour,uint16_t tile,uint16_t rflag)
  __DPROTO(,,void,,sp1_ClearRectInv,struct sp1_Rect *r,uint16_t colour,uint16_t tile,uint16_t rflag)
  
  ///////////////////////////////////////////////////////////
  //                      UPDATER                          //
  ///////////////////////////////////////////////////////////
  
  #define SP1_IFLAG_MAKE_ROTTBL      0x01
  #define SP1_IFLAG_OVERWRITE_TILES  0x02
  #define SP1_IFLAG_OVERWRITE_DFILE  0x04
  
  // void *hook  <->  void [ __FASTCALL__ ] (*hook)(struct sp1_update *u)
  
  __DPROTO(,,void,,sp1_Initialize,uint16_t iflag,uint16_t colour,uint16_t tile)
  __OPROTO(,,void,,sp1_UpdateNow,void)
  
  __DPROTO(`b,c',`b,c',struct sp1_update,*,sp1_GetUpdateStruct,uint16_t row,uint16_t col)
  __DPROTO(,,void,,sp1_IterateUpdateArr,struct sp1_update **ua,void *hook)// zero terminated array
  __DPROTO(,,void,,sp1_IterateUpdateRect,struct sp1_Rect *r,void *hook)
  
  __DPROTO(,,void,,sp1_InvUpdateStruct,struct sp1_update *u)
  __DPROTO(`a,b,c,d,e,h,l',`b,c,d,e',void,,sp1_ValUpdateStruct,struct sp1_update *u)
  
  __DPROTO(,,void,,sp1_DrawUpdateStructIfInv,struct sp1_update *u)
  __DPROTO(,,void,,sp1_DrawUpdateStructIfVal,struct sp1_update *u)
  __DPROTO(,,void,,sp1_DrawUpdateStructIfNotRem,struct sp1_update *u)
  __DPROTO(,,void,,sp1_DrawUpdateStructAlways,struct sp1_update *u)
  
  __DPROTO(`b,c',`b,c',void,,sp1_RemoveUpdateStruct,struct sp1_update *u)
  __DPROTO(`a,b,c,d,e,h,l',`b,c,d,e',void,,sp1_RestoreUpdateStruct,struct sp1_update *u)
  
  __DPROTO(,,void,,sp1_Invalidate,struct sp1_Rect *r)
  __DPROTO(,,void,,sp1_Validate,struct sp1_Rect *r)
  
  #endif
  