$PAGINATE
$title(Arnold 5 test)
$subtitle(Test of the Sprites)
$copyright(Copyright (c) 1989, 1990, Amstrad plc.)
$pagewidth=131

        PUBLIC  SpritesTest     ;called indirectly from TESTPACK
                                ; out of MainMenuTable

        EXTERN  ScreenResetPalette      ;SUPPORT
        EXTERN  ScreenSetMode           ;SUPPORT
        EXTERN  ScreenPrintChar         ;SUPPORT
        EXTERN  SetCursorPos            ;SUPPORT
        EXTERN  PrintStringHL           ;SUPPORT
        EXTERN  KeyboardReadNoDelay     ;SUPPORT
        EXTERN  DelayASeconds           ;SUPPORT
        EXTERN  UnlockArn5              ;SUPPORT
        EXTERN  RelockArn5              ;SUPPORT

        EXTERN  ?SpriteMag              ;in TESTVARS
        EXTERN  ?SpriteBeingMoved       ;in TESTVARS
        EXTERN  ?SpriteMoveControl      ;in TESTVARS
        EXTERN  ?SpriteDelay            ;in TESTVARS

        EXTERN  .SpriteTestMess         ;in MESSAGES
        EXTERN  .SpriteMountainData     ;in MESSAGES
        EXTERN  .SpriteSunDefinition    ;in MESSAGES
        EXTERN  .SpriteDefinitions      ;in MESSAGES
        EXTERN  .SpritePalette          ;in MESSAGES
        EXTERN  .SpriteStartPositions   ;in MESSAGES
        EXTERN  .SpriteEndPositions     ;in MESSAGES
        EXTERN  .SpriteMoveVectors      ;in MESSAGES
        EXTERN  .SpriteMoveStarts       ;in MESSAGES


        DEFSEG  TestCode, CLASS=CODE

        SEG     TestCode

        include "Equates.inc"

;===========
SpritesTest:
;===========
;
; The spec. has a pretty full description of what this is going to do!
;
; Firstly, I will unlock the Arnold 5 features and define the sprites to
; look like the hex digits 0..F (based on the 8*8 char matrices but blown
; up to 16*16 and anti-aliased by hand) on 0..F colored backgrounds. I will
; set the sprite palette so that:
;
;       sprite 0 GRB = 003  dull blue
;       sprite 1 GRB = 030  dull red
;       sprite 2 GRB = 300  dull green
;       sprite 3 GRB = 333  dull grey
;       sprite 4 GRB = 007  blue
;       sprite 5 GRB = 070  red
;       sprite 6 GRB = 700  green
;       sprite 7 GRB = 777  grey
;       sprite 8 GRB = 00B  pastel blue
;       sprite 9 GRB = 0B0  pastel red
;       sprite A GRB = B00  pastel green
;       sprite B GRB = BBB  white
;       sprite C GRB = 00F  bright blue
;       sprite D GRB = 0F0  bright red
;       sprite E GRB = F00  bright green
;       sprite F GRB = FFF  bright white
;
; Next I will put the screen into one of the three modes (and later repeat
; the whole test in the other 2 modes).
;
; The screen will be filled with the checker board character (127) so that
; the user will be able to see that the sprites are on top of the background.
;
; I will then set all there X,Y,magnification starting values from a fixed
; table. Setting the magnification to non-zero will  cause the sprites to
; appear (I hope !)
;
; The display should then look a bit like:
;
; 
;     ^=16                   2
;     V
;     ##^=16  ##      ##
; <32>#0V     #1      #2
; ^=16<>=16
; V   <--64-->
;     ##
;     #F
; 
; 
;
; Having built the static screen we can enter the movement loop. For each of
; the 16 sprites in turn we will use its number to determine an offset in the
; the sprite movement vector tab2le and will then run thru each of the 23
; vectors in turn adding the +X and +Y values onto that sprites current
; position. These movements will be done as FOR loops where the NEXT statement
; is syncd to F/F so it should be gradual. We will do a STEP 8 cos all the
; movements are multiples of 8 and this makes a horizontal (+128) about .3 of
; a second.
;

        ld      bc,7F01h
        out     (c),c                   ;point palette at color 1 (PEN 1)
        ld      a,052h                  ;BRIGHT GREEN
        out     (c),a                   ;set PEN 1 to GREEN

        ld      bc,7F03h
        out     (c),c                   ;point palette at color 3 (PEN 3)
        ld      a,04Ah                  ;bright yellow
        out     (c),a                   ;set PEN 3 to yello

        ld      hl,0C000h
        ld      de,0C001h
        ld      bc,03FFFh
        ld      (hl),0
        ldir                            ;wipe video RAM

        call    UnlockArn5

        ld      hl,400h
        ld      (?SpriteDelay),hl

        ld      hl,.SpriteMountainData
        ld      d,15
        ld      e,0                     ;will count columns 0 to 79 (X)
_SpriteMountainColLoop:
        ld      c,24                    ;(I) count 24 back up to d
_SpriteMountainRowLoop:
        push    hl
        push    de                      ;save X,Y
        push    bc                      ;save I
        ld      d,c                     ;get row from C
        call    SetCursorPos            ;LOCATE x,i

        ld      a,128                   ;the solid blob character
        call    ScreenPrintChar
        pop     bc                      ;recover I
        pop     de                      ;recover X,Y
        pop     hl
        dec     c                       ;I=I-1 (ie STEP -1)
        ld      a,c
        cp      d                       ;NEXT I (from 25 down to D)
        jr      nz,_SpriteMountainRowLoop

        ld      a,(hl)                  ;get offset
        inc     hl
        add     d                       ;update row
        ld      d,a                     ;put in right place

        inc     e                       ;step X on a column
        ld      a,40                    ;have we got to 40 yet ?
        cp      e                       ;NEXT X
        jr      nz,_SpriteMountainColLoop

        ld      a,1
        call    DelayASeconds           ;show landscape

;        ld      de,00220h               ;start row 2 col 32
;        call    SetCursorPos
;
;        ld      hl,.SpriteSunMess       ;draw the sun !
;        ld      b,1
;        call    PrintStringHL

;
; now draw a "sun" using the following algo..
;
; locn=0C12Ch     (row 3 column 30)
; point=data
; for row=0 to 5
;  for col=0 to 7
;   for scan=0 to 7
;    poke locn,def(point)
;    locn=locn+800h
;   next scan
;   locn=locn - 4000h + 1
;   inc point
;  next col
;  locn=locn + 80 - 8
; next row
;
        ld      hl,0C12Ch
        ld      de,.SpriteSunDefinition
        ld      b,6
_SpriteSunRowLoop:
        ld      c,8
_SpriteSunColLoop:
        push    bc
        ld      b,8
_SpriteSunScanLoop:
        ld      a,(de)
        ld      (hl),a
        push    bc
        ld      bc,800h                 ;down a scan
        add     hl,bc
        pop     bc
        djnz    _SpriteSunScanLoop
        ld      bc,-3FFFh
        add     hl,bc                   ;locn = locn - 4000h + 1
        inc     de                      ;inc point
        pop     bc
        dec     c
        jr      nz,_SpriteSunColLoop
        push    bc
        ld      bc,72
        add     hl,bc                   ;locn=locn + 80 - 8
        pop     bc
        djnz    _SpriteSunRowLoop

        ld      a,1
        call    DelayASeconds

        ld      de,00A00h
        call    SetCursorPos
        ld      hl,.SpriteTestMess
        ld      b,1
        call    PrintStringHL

        ld      a,3                     ;do magnifications 4, 2, 1
_SpriteMagnificationLoop:
        ld      (?SpriteMag),a          ;...they are numbered 1, 2, 3

;
; We now fill in the definitions for the 16 sprites. The sprite data only uses
; the bottom 4 bits of each byte which would be wasteful on storage so I have
; actually packed two pixel values into each byte as stored.
;
; The sprites are 16 by 16 and each pixel requires 4 bits to define it. The
; 16 definitions consists of square blocks drawn in the sprite numbers color
; with a digit in the foreground drawn in color F (bright white) for sprites
; 0..7 and in color 3 (dull grey) for sprites 8..F
;
        ld      hl,.SpriteDefinitions
        ld      de,._.SpriteData        ;where the sprites live
        ld      bc,0800h
_SpriteDefineLoop:
        ld      a,(hl)                  ;data is packed as 2 nybbles
        push    af
        and     0F0h                    ;get upper nybble
        srl     a
        srl     a
        srl     a
        srl     a                       ;move to lower nybble
        ld      (de),a                  ;set left pixel
        inc     de
        pop     af
        and     00Fh
        ld      (de),a                  ;then right
        inc     de
        inc     hl
        dec     bc
        ld      a,b
        or      c
        jr      nz,_SpriteDefineLoop

        ld      hl,.SpritePalette
        ld      de,._.SpritePensPalette ;where sprite colors are kept
        ld      bc,32                   ;16 2 byte entries
        ldir                            ;set the sprite palette

;
; Now the background is drawn we start the sprites off in their initial
; positions by reading their X,Y start values out of a table and setting their
; X and Y magnifications to the value in ?SpriteMag.
;
        ld      b,16                    ;number of sprites
        ld      de,._.SpriteControls    ;start of sprite control regs.
        ld      hl,.SpriteStartPositions
_SpriteStartPosnLoop:
        ld      a,(hl)                  ;get low X
        ld      (de),a                  ;set it
        inc     hl
        inc     de
        ld      a,(hl)                  ;get high X
        ld      (de),a                  ;set it
        inc     hl
        inc     de
        ld      a,(hl)                  ;get low Y
        ld      (de),a                  ;set it
        inc     hl
        inc     de
        ld      a,(hl)                  ;get high Y
        ld      (de),a                  ;set it
        inc     hl                      ;ready for next X,Y position
        inc     de                      ;now points at mag. reg.
        ld      a,(?SpriteMag)          ;current value 1, 2 or 4
        ld      c,a                     ;handy reg to store a copy
        sla     a
        sla     a                       ;move into the X mag position
        or      c                       ;add in C:1 and C:0 (Y mag)
        ld      (de),a
        inc     de
        inc     de
        inc     de
        inc     de                      ;move onto next multiple of 8
        push    bc
        ld      bc,4000
_SpriteShowDelay:
        dec     bc
        ld      a,b
        or      c
        jr      nz,_SpriteShowDelay
        pop     bc
        djnz    _SpriteStartPosnLoop

;
; Now we enter the main loop for the current mode and magnification where for
; each of the 16 sprites in turn we run them thru the movements specified in
; the vector list.
;
        ld      b,16                    ;number of sprites to go thru move seq.
_SpritesMoveLoop:
        push    bc
        ld      a,16
        sub     b                       ;make 16..1 into 0..15
        ld      (?SpriteBeingMoved),a
        ld      c,a
        ld      b,0                     ;get sprite num in BC (for byte offset)
        ld      hl,.SpriteMoveStarts    ;table of starting offsets
        add     hl,bc                   ;point at offset for this sprite
        ld      c,(hl)                  ;B stil = 0
        ld      hl,.SpriteMoveVectors
        add     hl,bc                   ;hl indexes into the vector table
        push    hl                      ;save that pointer for a sec

        ld      a,(?SpriteBeingMoved)
        ld      l,a
        ld      h,0
        add     hl,hl
        add     hl,hl
        add     hl,hl                   ;*8 cos there are 8 bytes per sprite
        ld      bc,06000h               ;base of sprite cotrol registers
        add     hl,bc
        ld      (?SpriteMoveControl),hl ;save the address of its control reg.

        pop     hl                      ;get back pointer to the movement vects.

        ld      b,28                    ;number of vector table entries
_SpriteDoAMoveLoop
        push    bc
;
; here with HL holding the address of the current X,Y offset to use and the
; variable ?SpriteBeingMoved has the number of the sprite in motion, while
; ?SpriteMoveControl holds the address of its control register.
;
; We pick up the X and Y offset values. These will be applied in 32 steps
; (perhaps syncd with F/F). The X values can be -4, 0 or 4 so movement in the
; X direction will be in steps of 4 pixels at a time while the Y value can be
; -1, 0 or 1 so it will move a scan line at a time.
;
; The vectors are stored as -4, 0, 4, -1, 0, 1 in 8 bit bytes but we want to
; apply them to the sprite X and Y positions which are 16 bit words so what
; will actually do (e.g. for X) is get the value in A, add 4 (make 0, 4, 8),
; expand this to a word (0 in the top byte), add this offset, then subtract
; a 16bit 4. This saves trying to do signed 8->16bit conversions !
;
        ld      b,36                    ;gonna do 36 steps. If moving X we really
                                        ;want just 32 so counts 4..0 are ignored
_SpriteStepXYLoop:
        push    bc
        push    hl
        ex      de,hl                   ;store vector pointer in DE

        ld      hl,(?SpriteMoveControl) ;address of this sprites control reg.
        ld      a,(hl)                  ;get current X value (16 bits wide)
        inc     hl
        ld      h,(hl)
        ld      l,a                     ;so HL=current X position

        ex      de,hl                   ;put it in DE and get vector point in HL

        ld      a,(hl)                  ;X offset to add (in 8 bits)

        inc     hl                      ;step on to Y offset entry

        push    hl                      ;save a copy of vector table pointer

        add     a,4                     ;make X offset 0, 4, or 8
        ld      l,a                     ;right place for 16 bit sums
        ld      h,0                     ;pad out to 16 bit width (know it's 0!)

        add     hl,de                   ;add current X to offset
        ld      de,4
        sbc     hl,de                   ;take 4 off again in 16 bit maths
        ex      de,hl                   ;hide new X pos. in DE for a tick

        ld      hl,(?SpriteMoveControl) ;address of sprites X register again

        ld      a,b
        cp      5
        jr      c,_SpriteNoStepX
        ld      (hl),e
        inc     hl
        ld      (hl),d                  ;set the new X position
        inc     hl                      ;now point at Y position register
        jr      _SpriteXStepDone

_SpriteNoStepX:
        inc     hl
        inc     hl                      ;still step HL onto point at Y reg

_SpriteXStepDone:
        ld      (?SpriteMoveControl),hl ;and save for later in this next bit

        ld      a,(hl)
        inc     hl
        ld      h,(hl)
        ld      l,a                     ;HL = current Y position

        ex      de,hl                   ;store current Y safely in DE

        pop     hl                      ;get pointer to Y offset

        ld      a,(hl)                  ;get Y offset (-1, 0 or 1)
        inc     hl

        inc     a                       ;make it 0, 1, 2
        ld      l,a
        ld      h,0                     ;pad out for 16 bit sums in HL

        add     hl,de                   ;add current Y to offset
        dec     hl                      ;adjust HL down by 1 again

        ex      de,hl                   ;hide new Y pos. in DE for a tick

        ld      hl,(?SpriteMoveControl) ;this now points to current Y reg.
        ld      (hl),e
        inc     hl
        ld      (hl),d                  ;update Y posn.
        dec     hl
        dec     hl
        dec     hl                      ;make Spritecontrol back to Xpos for
                                        ;current sprite
        ld      (?SpriteMoveControl),hl

        ld      hl,(?SpriteDelay)
_SpriteMoveDelay:
        dec     hl
        ld      a,h
        or      l
        jr      nz,_SpriteMoveDelay


        pop     hl                      ;back to start of this vector for 32 steps
        pop     bc
        djnz    _SpriteStepXYLoop       ;NEXT X/Y step of 32
        inc     hl
        inc     hl                      ;step to next entry in vector table.

        ld      a,(hl)                  ;check for wrap at end of table
        cp      -1                      ;have we reached end of vector table ?
        jr      nz,_SpriteNotEndVectors
;
; reached end of offsets table so we want to wrap back to the start. We know
; there are 24 entries with 2 bytes for each so the start of the table is 48
; bytes back.
;
        push    de
        ld      de,56
        sbc     hl,de                   ;back to start of the table
        pop     de

_SpriteNotEndVectors:

        push    de
        push    hl
        call    KeyboardReadNoDelay
        cp      66                      ;is ESC pushed
        jp      z,_SpritesEscPushed
        cp      76                      ;is Joy 0 Fire 0 pressed = End
        jp      z,_SpritesEscPushed
        cp      0                       ;is the key UP
        jr      z,_SpriteIncreaseDelay
        cp      72
        jr      z,_SpriteIncreaseDelay
        cp      2
        jr      z,_SpriteDecreaseDelay
        cp      73
        jr      z,_SpriteDecreaseDelay
        jr      _SpriteNoKeyPushed

_SpriteIncreaseDelay:
        push    de
        ld      hl,(?SpriteDelay)
        ld      de,40h
        add     hl,de
        pop     de
        ld      (?SpriteDelay),hl
        jr      _SpriteNoKeyPushed

_SpriteDecreaseDelay:
        push    de
        ld      hl,(?SpriteDelay)
        ld      de,40h
        sbc     hl,de
        pop     de
        ld      a,h
        or      l
        jr      z,_SpriteNoSetZeroDelay
        ld      (?SpriteDelay),hl
_SpriteNoSetZeroDelay:
        jr      _SpriteNoKeyPushed

_SpritesEscPushed:
        pop     hl
        pop     de
        pop     bc
        pop     bc                      ;balance the stack
        jp      _SpritesEndTest

_SpriteNoKeyPushed:
        pop     hl
        pop     de
        pop     bc
        dec     b
        jp      nz,_SpriteDoAMoveLoop
;        djnz    _SpriteDoAMoveLoop      ;NEXT entry in 24 entry vector table

        pop     bc
        dec     b
        jp      nz,_SpritesMoveLoop
;        djnz    _SpritesMoveLoop        ;NEXT of the 16 sprites

        ld      a,(?SpriteMag)
        dec     a
        ld      (?SpriteMag),a
        cp      0                       ;done all 3 magnification steps yet ?
        jp      nz,_SpriteMagnificationLoop


;
; At end of test all 16 sprites are in magnification 4x4 and in their home
; positions. We will now sweep the sprite palette entries thru their 4096
; possible combinations.
;
        %if     0

        ld      b,16                    ;number of sprites
        ld      de,._.SpriteControls    ;start of sprite control regs.
        ld      hl,.SpriteEndPositions
_SpriteEndPosnLoop:
        ld      a,(hl)                  ;get low X
        ld      (de),a                  ;set it
        inc     hl
        inc     de
        ld      a,(hl)                  ;get high X
        ld      (de),a                  ;set it
        inc     hl
        inc     de
        ld      a,(hl)                  ;get low Y
        ld      (de),a                  ;set it
        inc     hl
        inc     de
        ld      a,(hl)                  ;get high Y
        ld      (de),a                  ;set it
        inc     hl                      ;ready for next X,Y position
        inc     de                      ;now points at mag. reg.
        ld      a,0Eh                   ;set mag to x4 x2
        ld      (de),a
        inc     de
        inc     de
        inc     de
        inc     de                      ;move onto next multiple of 8
        djnz    _SpriteEndPosnLoop

;The sprites are defined with background=sprite number (0..F) and for sprites
;0..7 the digit is currently colour F, while for sprites 8..F the digit is
;colour 3. The following reads back the sprite defs. and for the 0..7 data
;it converts and 0Fs found to 00s, while for 8..F any 03 is converted to 00

        ld      hl,4000h                ;start of Sprite 0 data
        ld      bc,800h                 ;count of first 8 sprites data
_SpriteRedefine0To7:
        ld      a,(hl)
        cp      0Fh
        jr      nz,_Sprite0To7NoChange
        ld      (hl),0
_Sprite0To7NoChange:
        inc     hl
        dec     bc
        ld      a,b
        or      c
        jr      nz,_SpriteRedefine0To7

        ld      hl,4800h                ;start of Sprite 8 data
        ld      bc,800h                 ;count of first 8 sprites data
_SpriteRedefine8ToF:
        ld      a,(hl)
        cp      03h
        jr      nz,_Sprite8ToFNoChange
        ld      (hl),0
_Sprite8ToFNoChange:
        inc     hl
        dec     bc
        ld      a,b
        or      c
        jr      nz,_SpriteRedefine8ToF

        ld      hl,._.SpritePensPalette
        ld      a,0
_SpriteResetPalette
        ld      (hl),0                  ;set R/B to 0
        inc     hl
        ld      (hl),a                  ;set G to 0..F
        inc     hl
        inc     a
        djnz    _SpriteResetPalette

        ld      de,4096                 ;number of color steps
_SpriteNext4096Step:
        ld      b,15                    ;number of entries in sprite palette
        ld      hl,6422h
_SpriteNextColourUp:
        push    bc
        ld      c,(hl)
        inc     hl
        ld      b,(hl)
        dec     hl
        inc     bc
        ld      (hl),c
        inc     hl
        ld      (hl),b
        inc     hl
        pop     bc
        djnz    _SpriteNextColourUp
        push    bc
        ld      bc,100h
_SpriteColourDelay:
        dec     bc
        ld      a,b
        or      c
        jr      nz,_SpriteColourDelay
        pop     bc
        dec     de
        ld      a,d
        or      e
        jr      nz,_SpriteNext4096Step

        %endif

_SpritesEndTest:
        ld      hl,6000h
        ld      de,6001h
        ld      bc,07fh
        ld      (hl),0
        ldir                            ;clear sprite X, Y and mag (all off)

        call    ScreenResetPalette

        call    RelockArn5

        ret

;============
randomnumber:
;============
;
; produce a random number in the range 0 to 24
; formula:
;	  for i = 1 to 8 do
;		A = A .sl.1 + ((bit 5 XNOR bit 0) into bit 0)
;	  od
;
; entry: d = last random number
;  exit: d = new random number
;        flags b corrupt
;        all other regs preserved
;
rn10:	ld	b,8		;go round loop 8 times
;
rn20:	ld	a,d
	and	21h
	jp	po,rn30		;jump if not even parity
;
	scf			;must add a 1
;
rn30:	ld	a,d
	rla
	ld	d,a
;
	djnz	rn20
;
	and	3Fh		;mask out 64 & 128
	ld	d,a
        cp      25
	jr	nc,rn10		;loop if number too big
;
	ret



        END
