#include <EDK.h>
#include "General.h"
#include "CVIC656x.h"

#define Class CVIC656x
extern Class MSVC4Bug;


/*
  int aiSpriteX[8];      // sprite X positions (0-511)
  byte abSpriteY[8];     // sprite Y positions (0-255)
  byte abSpriteColor[8]; // sprite colors (0-15)
  byte abSpriteCnt[8];   // 6 bit counters from 0-62, 63 means OFF
  int aiSpriteData[8];   // fetched data to be displayed at end of line
  int aiSpriteSave[8];   // saved data for clock, $xxxxxx3F means off
  byte abCollision[664]; // buffer for sprite-sprite collisions
  pfnv apfnSprite[8];    // functions to display a sprite line
  byte bSpriteX8;        // bits 8 of sprite X positions
  byte bSSCR;            // Sprite/Sprite Collision Register $D01E
  byte bSDCR;            // Sprite/Data Collision Register $D01F
  byte bNewSDCR;         // collects data collisions for all 8 sprites
  byte bSpriteExpandX;   // sprites with double width
  byte bSpriteExpandY;   // sprites with double height
  byte bSpriteSameRow;   // flags for double height and sprite stretching
  byte bSpriteEnable;    // Sprite Enable Register $D015
  byte bSpriteBack;      // sprites with background priority
  byte bSpriteMC;        // sprites in multicolor
  byte bSpriteMC1;       // sprite multicolor 1 (0-15)
  byte bSpriteMC2;       // sprite multicolor 2 (0-15)
  byte bSprites;         // flags for sprites on in next line
  byte abSpriteFill[3];
*/

extern void fn(SpriteSC1F)();
proc(ResetSprites)
  xor EAX,EAX
  mov EBX,7
NextSprite:
  mov mvar(aiSpriteX[EBX*4]),EAX
  mov mvar(abSpriteY[EBX]),AL
  mov mvar(abSpriteColor[EBX]),AL
  mov mvar(abSpriteCnt[EBX]),63
  mov mvar(aiSpriteSave[EBX*4]),63
  mov mvar(apfnSprite[EBX*4]),offset fn(SpriteSC1F)
  dec EBX
  jns NextSprite
  mov mvar(bSpriteX8),AL
  mov mvar(bSSCR),AL
  mov mvar(bSDCR),AL
  mov mvar(bSpriteExpandX),AL
  mov mvar(bSpriteExpandY),AL
  mov mvar(bSpriteSameRow),AL
  mov mvar(bSpriteEnable),AL
  mov mvar(bSpriteBack),AL
  mov mvar(bSpriteMC),AL
  mov mvar(bSpriteMC1),AL
  mov mvar(bSpriteMC2),AL
  mov mvar(bSprites),AL
  ret
endp

proc(ReadSprite0X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[0*4])

  pop ESI
  ret
endp

proc(WriteSprite0X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[0*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite0Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[0])

  pop ESI
  ret
endp

proc(WriteSprite0Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[0]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite1X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[1*4])

  pop ESI
  ret
endp

proc(WriteSprite1X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[1*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite1Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[1])

  pop ESI
  ret
endp

proc(WriteSprite1Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[1]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite2X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[2*4])

  pop ESI
  ret
endp

proc(WriteSprite2X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[2*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite2Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[2])

  pop ESI
  ret
endp

proc(WriteSprite2Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[2]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite3X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[3*4])

  pop ESI
  ret
endp

proc(WriteSprite3X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[3*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite3Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[3])

  pop ESI
  ret
endp

proc(WriteSprite3Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[3]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite4X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[4*4])

  pop ESI
  ret
endp

proc(WriteSprite4X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[4*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite4Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[4])

  pop ESI
  ret
endp

proc(WriteSprite4Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite5X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[5*4])

  pop ESI
  ret
endp

proc(WriteSprite5X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[5*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite5Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[5])

  pop ESI
  ret
endp

proc(WriteSprite5Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[5]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite6X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[6*4])

  pop ESI
  ret
endp

proc(WriteSprite6X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[6*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite6Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[6])

  pop ESI
  ret
endp

proc(WriteSprite6Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[6]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite7X)
  push ESI
  mov ESI,ECX

  mov AL,byte ptr mvar(aiSpriteX[7*4])

  pop ESI
  ret
endp

proc(WriteSprite7X)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov byte ptr mvar(aiSpriteX[7*4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite7Y)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteY[7])

  pop ESI
  ret
endp

proc(WriteSprite7Y)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(abSpriteY[7]),AL

  pop ESI
  ret 4
endp

proc(ReadSpriteX8)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteX8)

  pop ESI
  ret
endp

proc(WriteSpriteX8)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bSpriteX8),AL
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[7*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[6*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[5*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[4*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[3*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[2*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[1*4+1]),AH
  xor AH,AH
  add EAX,EAX
  mov byte ptr mvar(aiSpriteX[0*4+1]),AH

  pop ESI
  ret 4
endp

proc(ReadSprite0Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[0])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite0Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[0]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite1Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[1])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite1Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[1]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite2Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[2])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite2Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[2]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite3Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[3])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite3Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[3]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite4Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[4])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite4Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[4]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite5Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[5])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite5Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[5]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite6Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[6])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite6Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[6]),AL

  pop ESI
  ret 4
endp

proc(ReadSprite7Color)
  push ESI
  mov ESI,ECX

  mov AL,mvar(abSpriteColor[7])
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSprite7Color)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(abSpriteColor[7]),AL

  pop ESI
  ret 4
endp

proc(ReadSpriteMC1)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteMC1)
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSpriteMC1)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(bSpriteMC1),AL

  pop ESI
  ret 4
endp

proc(ReadSpriteMC2)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteMC2)
  or AL,0xF0

  pop ESI
  ret
endp

proc(WriteSpriteMC2)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  and AL,0x0F
  mov mvar(bSpriteMC2),AL

  pop ESI
  ret 4
endp

proc(ReadSpriteExpandY)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteExpandY)

  pop ESI
  ret
endp

proc(WriteSpriteExpandY)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bSpriteExpandY),AL
  mov mvar(bSpriteSameRow),AL

  pop ESI
  ret 4
endp

/*
  Sprite/Sprite Collision Register SSCR $D01E

  If a bit in the SSCR is set, the corresponding sprite has collided with
  another sprite.

  Reading SSCR will set all bits back to 0. Writing has no effect.
*/

proc(ReadSSCR)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSSCR)
  mov mvar(bSSCR),0

  pop ESI
  ret
endp

proc(WriteSSCR)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]


  pop ESI
  ret 4
endp

/*
  Sprite/Data Collision Register SDCR $D01A

  If a bit in the SDCR is set, the corresponding sprite has collided with
  display data.

  The bit combination %10 in Multicolor Mode is no display data.

  Reading SDCR will set all bits back to 0. Writing has no effect.
*/

proc(ReadSDCR)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSDCR)
  mov mvar(bSDCR),0

  pop ESI
  ret
endp

proc(WriteSDCR)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]


  pop ESI
  ret 4
endp

proc(ReadSpriteEnable)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteEnable)

  pop ESI
  ret
endp

proc(WriteSpriteEnable)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bSpriteEnable),AL

  pop ESI
  ret 4
endp

/*
  display sprite line: single color, single width, foreground

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (24-1)
*/

proc(SpriteSC1F)
  xor DL,DL
  push EBP
  mov EBP,24
NextPixel:
  add EAX,EAX
  jnc Transparent
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision
  // check for frame
  test AL,01000000b
  jne DontSet
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
Transparent:
  inc EBX
  inc EDI
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet
endp

/*
  display sprite line: single color, single width, background

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (24-1)
*/

proc(SpriteSC1B)
  xor DL,DL
  push EBP
  mov EBP,24
NextPixel:
  add EAX,EAX
  jnc Transparent
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision
  // check for frame and data
  test AL,01010000b
  jne DontSet
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
Transparent:
  inc EBX
  inc EDI
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet
endp

/*
  display sprite line: single color, double width, foreground

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (24-1)
*/

proc(SpriteSC2F)
  xor DL,DL
  push EBP
  mov EBP,24
NextPixel:
  add EAX,EAX
  jnc Transparent
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision1
  // check for frame
  test AL,01000000b
  jne DontSet1
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet1:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
  // read pixel color and flags
  mov AL,[EBX+1]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision2
  // check for frame
  test AL,01000000b
  jne DontSet2
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet2:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+1],CL
  // write back color and flags
  mov [EBX+1],AL
Transparent:
  add EBX,2
  add EDI,2
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision1:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet1
Collision2:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet2
endp

/*
  display sprite line: single color, double width, background

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (24-1)
*/

proc(SpriteSC2B)
  xor DL,DL
  push EBP
  mov EBP,24
NextPixel:
  add EAX,EAX
  jnc Transparent
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision1
  // check for frame and data
  test AL,01010000b
  jne DontSet1
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet1:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
  // read pixel color and flags
  mov AL,[EBX+1]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision2
  // check for frame and data
  test AL,01010000b
  jne DontSet2
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet2:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+1],CL
  // write back color and flags
  mov [EBX+1],AL
Transparent:
  add EBX,2
  add EDI,2
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision1:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet1
Collision2:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet2
endp

/*
  display sprite line: multi color, single width, foreground

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (12-1)
*/

proc(SpriteMC1F)
  xor DL,DL
  push EBP
  mov EBP,12
NextPixel:
  add EAX,EAX
  jc Color23
  add EAX,EAX
  jnc Transparent
  push ECX
  mov CH,mvar(bSpriteMC1)
  jmp Pixel
Color23:
  push ECX
  add EAX,EAX
  jnc Pixel
  mov CH,mvar(bSpriteMC2)
Pixel:
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision1
  // check for frame
  test AL,01000000b
  jne DontSet1
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet1:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
  // read pixel color and flags
  mov AL,[EBX+1]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision2
  // check for frame
  test AL,01000000b
  jne DontSet2
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet2:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+1],CL
  // write back color and flags
  mov [EBX+1],AL
  pop ECX
Transparent:
  add EBX,2
  add EDI,2
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision1:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet1
Collision2:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+1]
  jmp DontSet2
endp

/*
  display sprite line: multi color, single width, background

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (12-1)
*/

proc(SpriteMC1B)
  xor DL,DL
  push EBP
  mov EBP,12
NextPixel:
  add EAX,EAX
  jc Color23
  add EAX,EAX
  jnc Transparent
  push ECX
  mov CH,mvar(bSpriteMC1)
  jmp Pixel
Color23:
  push ECX
  add EAX,EAX
  jnc Pixel
  mov CH,mvar(bSpriteMC2)
Pixel:
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision1
  // check for frame and background
  test AL,01010000b
  jne DontSet1
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet1:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
  // read pixel color and flags
  mov AL,[EBX+1]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision2
  // check for frame and background
  test AL,01010000b
  jne DontSet2
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet2:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+1],CL
  // write back color and flags
  mov [EBX+1],AL
  pop ECX
Transparent:
  add EBX,2
  add EDI,2
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision1:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet1
Collision2:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+1]
  jmp DontSet2
endp

/*
  display sprite line: multi color, double width, foreground

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (12-1)
*/

proc(SpriteMC2F)
  xor DL,DL
  push EBP
  mov EBP,12
NextPixel:
  add EAX,EAX
  jc Color23
  add EAX,EAX
  jnc Transparent
  push ECX
  mov CH,mvar(bSpriteMC1)
  jmp Pixel
Color23:
  push ECX
  add EAX,EAX
  jnc Pixel
  mov CH,mvar(bSpriteMC2)
Pixel:
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision1
  // check for frame
  test AL,01000000b
  jne DontSet1
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet1:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
  // read pixel color and flags
  mov AL,[EBX+1]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision2
  // check for frame
  test AL,01000000b
  jne DontSet2
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet2:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+1],CL
  // write back color and flags
  mov [EBX+1],AL
  // read pixel color and flags
  mov AL,[EBX+2]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision3
  // check for frame
  test AL,01000000b
  jne DontSet3
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet3:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+2],CL
  // write back color and flags
  mov [EBX+2],AL
  // read pixel color and flags
  mov AL,[EBX+3]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision4
  // check for frame
  test AL,01000000b
  jne DontSet4
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet4:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+3],CL
  // write back color and flags
  mov [EBX+3],AL
  pop ECX
Transparent:
  add EBX,4
  add EDI,4
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision1:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet1
Collision2:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+1]
  jmp DontSet2
Collision3:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+2]
  jmp DontSet3
Collision4:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+3]
  jmp DontSet4
endp

/*
  display sprite line: multi color, double width, background

  EAX = sprite data in bits 8-31
  AL  = pixel
  EBX = screen buffer
  EDI = collision buffer
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  CH  = sprite color
  DL  = sprite-data collision in bit 4
  DH  = sprite-sprite collisions
  EBP = pixel counter (12-1)
*/

proc(SpriteMC2B)
  xor DL,DL
  push EBP
  mov EBP,12
NextPixel:
  add EAX,EAX
  jc Color23
  add EAX,EAX
  jnc Transparent
  push ECX
  mov CH,mvar(bSpriteMC1)
  jmp Pixel
Color23:
  push ECX
  add EAX,EAX
  jnc Pixel
  mov CH,mvar(bSpriteMC2)
Pixel:
  // read pixel color and flags
  mov AL,[EBX]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision1
  // check for frame and background
  test AL,01010000b
  jne DontSet1
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet1:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI],CL
  // write back color and flags
  mov [EBX],AL
  // read pixel color and flags
  mov AL,[EBX+1]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision2
  // check for frame and background
  test AL,01010000b
  jne DontSet2
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet2:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+1],CL
  // write back color and flags
  mov [EBX+1],AL
  // read pixel color and flags
  mov AL,[EBX+2]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision3
  // check for frame and background
  test AL,01010000b
  jne DontSet3
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet3:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+2],CL
  // write back color and flags
  mov [EBX+2],AL
  // read pixel color and flags
  mov AL,[EBX+3]
  // collect sprite-data collisions
  or DL,AL
  // check sprite-sprite collision
  test AL,00100000b
  jne Collision4
  // check for frame and background
  test AL,01010000b
  jne DontSet4
  // set sprite color
  and AL,11110000b
  or AL,CH
DontSet4:
  // mark presence of sprite pixel
  or AL,00100000b
  mov [EDI+3],CL
  // write back color and flags
  mov [EBX+3],AL
  pop ECX
Transparent:
  add EBX,4
  add EDI,4
  dec EBP
  jne NextPixel
  pop EBP
  ret
Collision1:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI]
  jmp DontSet1
Collision2:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+1]
  jmp DontSet2
Collision3:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+2]
  jmp DontSet3
Collision4:
  // the current sprite
  or DH,CL
  // with the other sprite
  or DH,[EDI+3]
  jmp DontSet4
endp

static pfnv gapfnSprite[8] = {
  fn(SpriteSC1F), fn(SpriteSC1B), fn(SpriteSC2F), fn(SpriteSC2B),
  fn(SpriteMC1F), fn(SpriteMC1B), fn(SpriteMC2F), fn(SpriteMC2B),
};

proc(SetSpriteFunctions)
  mov AL,mvar(bSpriteMC)
  mov AH,mvar(bSpriteExpandX)
  mov BL,mvar(bSpriteBack)
  mov ECX,7
NextSprite:
  xor EDX,EDX
  add AL,AL
  rcl DL,1
  add AH,AH
  rcl DL,1
  add BL,BL
  rcl DL,1
  mov EDX,gapfnSprite[EDX*4]
  mov mvar(apfnSprite[ECX*4]),EDX
  dec ECX
  jns NextSprite
  ret
endp

proc(ReadSpriteMC)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteMC)

  pop ESI
  ret
endp

proc(WriteSpriteMC)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bSpriteMC),AL
  call fn(SetSpriteFunctions)

  pop ESI
  ret 4
endp

proc(ReadSpriteExpandX)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteExpandX)

  pop ESI
  ret
endp

proc(WriteSpriteExpandX)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bSpriteExpandX),AL
  call fn(SetSpriteFunctions)

  pop ESI
  ret 4
endp

proc(ReadSpriteBack)
  push ESI
  mov ESI,ECX

  mov AL,mvar(bSpriteBack)

  pop ESI
  ret
endp

proc(WriteSpriteBack)
  push ESI
  mov ESI,ECX
  mov EAX,[ESP+8]

  mov mvar(bSpriteBack),AL
  call fn(SetSpriteFunctions)

  pop ESI
  ret 4
endp

/*
  DH  = sprite-sprite collisions
  EBP = sprite number (0-7)
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  EBX = screen buffer
  EDI = collision buffer
  EAX = sprite data in bits 8-31
  CH  = sprite color
  DL  = sprite-data collision in bit 4
*/

proc(Sprites54)
  // add 3 to sprite counters
  mov EBX,7
  mov ECX,0x80
NextSprite:
  mov EAX,mvar(aiSpriteData[EBX*4])
  bswap EAX
  mov AL,mvar(abSpriteCnt[EBX])
  mov mvar(aiSpriteSave[EBX*4]),EAX
  cmp AL,63
  je NoAdd
  or CH,CL
  test mvar(bSpriteSameRow),CL
  jne NoAdd
  add AL,3
  and AL,63
  mov mvar(abSpriteCnt[EBX]),AL
NoAdd:
  shr CL,1
  dec EBX
  jns NextSprite
  mov mvar(bSprites),CH
  // handle Y expansion
  mov AL,mvar(bSpriteExpandY)
  xor mvar(bSpriteSameRow),AL
  // switch on sprites in next line
  mov AL,byte ptr mvar(iLine)
  mov AH,mvar(bSpriteEnable)
  cmp AL,mvar(abSpriteY[0])
  je Sprite0
Cont0:
  cmp AL,mvar(abSpriteY[1])
  je Sprite1
Cont1:
  cmp AL,mvar(abSpriteY[2])
  je Sprite2
Cont2:
  cmp AL,mvar(abSpriteY[3])
  je Sprite3
Cont3:
  cmp AL,mvar(abSpriteY[4])
  je Sprite4
Cont4:
  cmp AL,mvar(abSpriteY[5])
  je Sprite5
Cont5:
  cmp AL,mvar(abSpriteY[6])
  je Sprite6
Cont6:
  cmp AL,mvar(abSpriteY[7])
  je Sprite7
  ret
Sprite0:
  test AH,0x01
  je Cont0
  cmp mvar(abSpriteCnt[0]),63
  jne Cont0
  mov mvar(abSpriteCnt[0]),0
  jmp Cont0
Sprite1:
  test AH,0x02
  je Cont1
  cmp mvar(abSpriteCnt[1]),63
  jne Cont1
  mov mvar(abSpriteCnt[1]),0
  jmp Cont1
Sprite2:
  test AH,0x04
  je Cont2
  cmp mvar(abSpriteCnt[2]),63
  jne Cont2
  mov mvar(abSpriteCnt[2]),0
  jmp Cont2
Sprite3:
  test AH,0x08
  je Cont3
  cmp mvar(abSpriteCnt[3]),63
  jne Cont3
  mov mvar(abSpriteCnt[3]),0
  jmp Cont3
Sprite4:
  test AH,0x10
  je Cont4
  cmp mvar(abSpriteCnt[4]),63
  jne Cont4
  mov mvar(abSpriteCnt[4]),0
  jmp Cont4
Sprite5:
  test AH,0x20
  je Cont5
  cmp mvar(abSpriteCnt[5]),63
  jne Cont5
  mov mvar(abSpriteCnt[5]),0
  jmp Cont5
Sprite6:
  test AH,0x40
  je Cont6
  cmp mvar(abSpriteCnt[6]),63
  jne Cont6
  mov mvar(abSpriteCnt[6]),0
  jmp Cont6
Sprite7:
  test AH,0x80
  je Cont7
  cmp mvar(abSpriteCnt[7]),63
  jne Cont7
  mov mvar(abSpriteCnt[7]),0
Cont7:
  ret
endp

/*
  DH  = sprite-sprite collisions
  EBP = sprite number (0-7)
  CL  = sprite mask (0x01, 0x02, ..., 0x80)
  EBX = screen buffer
  EDI = collision buffer
  EAX = sprite data in bits 8-31
  CH  = sprite color
  DL  = sprite-data collision in bit 4
*/

proc(Sprites63)
  // display sprites
  mov mvar(bNewSDCR),0
  mov DH,mvar(bSSCR)
  push EDI
  push EBP
  xor EBP,EBP
  mov CL,0x01
  jmp FirstSprite
NextSprite:
  inc EBP
  shl CL,1
  jc LastSprite
FirstSprite:
  mov EAX,mvar(aiSpriteSave[EBP*4])
  cmp AL,63
  je NextSprite
  mov EBX,mvar(aiSpriteX[EBP*4])
  add EBX,(16-3)*8
  lea EDI,mvar(abCollision[EBX])
  add EBX,mvar(pbLine)
  mov CH,mvar(abSpriteColor[EBP])
  call mvar(apfnSprite[EBP*4])
  test DL,00010000b
  je NextSprite
  or mvar(bNewSDCR),CL
  jmp NextSprite
LastSprite:
  pop EBP
  pop EDI
  // handle sprite/sprite collisions
  and DH,DH
  je EndSSC
  mov AL,mvar(bSSCR)
  or DH,AL
  mov mvar(bSSCR),DH
  and AL,AL
  jne EndSSC
  or mvar(bIRR),00000100b
  test mvar(bIMR),00000100b
  je EndSSC
  SetLow(mvar(Int), 1)
EndSSC:
  // handle sprite/data collisions
  mov AL,mvar(bNewSDCR)
  and AL,AL
  je EndSDC
  mov AH,mvar(bSDCR)
  or AL,AH
  mov mvar(bSDCR),AL
  and AH,AH
  jne EndSDC
  or mvar(bIRR),00000010b
  test mvar(bIMR),00000010b
  je EndSDC
  SetLow(mvar(Int), 2)
EndSDC:
  ret
endp
