$PAGINATE
$title(Arnold 5 test)
$subtitle(4096 color palette test)
$copyright(Copyright (c) 1989, 1990, Amstrad plc.)
$pagewidth=131

        PUBLIC  Palette4096Test         ;called indirectly from TESTPACK
                                        ; out of MainMenuTable

        EXTERN  ScreenSetMode           ;in SUPPORT
        EXTERN  SetCursorPos            ;in SUPPORT
        EXTERN  ScreenResetPalette      ;in SUPPORT
        EXTERN  PrintStringHL           ;in SUPPORT
        EXTERN  PrintAHex               ;in SUPPORT
        EXTERN  UnlockArn5              ;in SUPPORT
        EXTERN  RelockArn5              ;in SUPPORT
        EXTERN  KeyboardReadNoDelay     ;in SUPPORT

        EXTERN  .Palette4096Mess        ;in MESSAGES
        EXTERN  .Mode0MaskTable         ;in MESSAGES

        EXTERN  ?Pal496Background       ;in TESTVARS
        EXTERN  ?PaletteLastKeyPressed  ;in TESTVARS
        EXTERN  ?PaletteKeyCount        ;in TESTVARS

        DEFSEG  TestCode, CLASS=CODE

        SEG     TestCode

;===============
Palette4096Test:
;===============
;
; It would be really nice to display all 4096 colors on the screen at once
; but we can only manage 16 per scan line and there are 200 line (3200 !). In
; fact :
;
; A scan line lasts for 62.5uS and the processor runs at 4MHz so a T-state
; is 1/4000000S = 0.25uS. There are therefore 62.5/0.25=250 T-states to a
; scan line.
;
; As the 4096 palette is memory mapped we can fill it using an LDIR which takes
; 33*24 + 1*16 = 808 T-States. We also need to load HL, BC and DE and also
; maintain a count of how many blocks down the screen we have done
;
;        ld      b,number of chunks
;fillpalloop:
;        push    bc                       12 T states
;        ld      hl,.Palet                12
;        ld      bc,34                    12  (2 bytes for 16 colors)
;        ld      de,06400h                12
;        ldir                            808
;        pop     bc                       12
;        djnz    fillpalloop              16
;                                        ===
;                                        884
;
; We can then pad out this delay to 1000 T-states which means we can change
; 16 colors in 4 scan lines. So we arrange the screen to be:
;
; ======------======------ ..... ======
; ======------======------ ..... ======
; =PEN0=-PEN1-=PEN2=-PEN3- ..... =PENF=  6 lines make up the color bars
; ======------======------ ..... ======
; ======------======------ ..... ======
; ======------======------ ..... ======
; :
; : 4 blank (PEN 0) lines where we change the palette
; :
; :
; ======------======------ ..... ======
; ======------======------ ..... ======
; =PEN0=-PEN1-=PEN2=-PEN3- ..... =PENF=  6 lines make up the color bars
; ======------======------ ..... ======
; ======------======------ ..... ======
; ======------======------ ..... ======
; :
; : 4 blank (PEN 0) lines where we change the palette
; :
; :
;
; That is, there are 6 lines of 5 byte blocks of PENs 0..F. Then 4 blank lines
; then 6 lines of 5 byte blocks of PENs 0..F for 16 lots. (hope you're followin
; this !). This will use up (16 * 6) + (15 * 4) = 156 of the 200 scan lines.
; The other 44 lines will be used for doing things like reading the keyboard.
;
; This will actually show 256 colors on the screen at once. A bit like Kens
; VGATEST, I will run thru all Blue levels horizontally and Red levels
; vertically. But each screen will only use 1 of the 16 levels of Green so it
; will be possible to scroll thru the 16 blue levels showing 16 different
; "screens".
;
; In fact, the screen will never be touched. It is just wots written into the
; palette that varies.
;
; First we draw the pattern using the algorithm:
;
; loc = 0C050h = scanline
; FOR bars = 1 TO 16
;  FOR lines = 1 TO 6
;   table_point = .Mode0Masks
;   FOR blocks = 1 to 16
;    FOR bytes=1 to 5
;     POKE loc,(table_point)
;     INC loc
;    NEXT bytes
;    INC table_point
;   NEXT blocks
;   loc = NEXT_SCAN_LINE
;  NEXT lines
;  loc = NEXT_SCAN_LINE
;  loc = NEXT_SCAN_LINE
;  loc = NEXT_SCAN_LINE
;  loc = NEXT_SCAN_LINE
; NEXT bars
;
; NEXT_SCAN_LINE:
;  if sl < 0f800h then sl = sl + 800h else sl = sl - 03800h +80
;  loc = sl
;  ret
;

        ld      a,0FFh
        ld      (?PaletteLastKeyPressed),a      ;i.e. we haven't had one !

        ld      a,0
        ld      (?PaletteKeyCount),a    ;pushed no times so far

        ld      hl,0777h
        ld      (?Pal496Background),hl  ;default backdrop

        ld      a,1
        call    ScreenSetMode

        ld      de,00200h
        call    SetCursorPos

        ld      hl,.Palette4096Mess
        ld      b,1
        call    PrintStringHL

        call    UnlockArn5              ;to access the 4096 palette at 6400h..

        ld      hl,0C192h               ;leave 4 charactes above 1st fill.
        push    hl
        pop     ix                      ;'loc' = 'sl' = start of screen

        ld      b,16                    ;FOR blobs: 16 bars down the screen
_PalDrawBarsLoop:
        ld      c,6                     ;FOR lines: 6 scans of colored bits
_PalDrawLinesLoop:
        ld      de,.Mode0MaskTable + 1  ;reset table pointer
        push    bc                      ;save outer 2 loop counters
        ld      b,15                    ;FOR blocks: 16 across the line
_PalDrawBlocksLoop:
        ld      c,5                     ;FOR bytes: 5 in each color block
_PalDrawBytesLoop:
        ld      a,(de)                  ;get mask
        ld      (hl),a                  ;POKE
        inc     hl                      ;INC loc
        dec     c
        jr      nz,_PalDrawBytesLoop    ;NEXT bytes
        inc     de                      ;INC table pointer
        djnz    _PalDrawBlocksLoop      ;NEXT blocks

        call    _Pal496NextScanLine     ;down a scan

        pop     bc                      ;outer 2 loop counters
        dec     c
        jr      nz,_PalDrawLinesLoop    ;NEXT lines

        call    _Pal496NextScanLine     ;now down 4 scan lines (blank)
        call    _Pal496NextScanLine     ;
        call    _Pal496NextScanLine     ;
        call    _Pal496NextScanLine     ;

        djnz    _PalDrawBarsLoop        ;NEXT bars

;
; Now the fixed color bands have been drawn on the screen we can enter the
; horrendous software timed loop where we sync with f/f. Then for 16 times
; we wait 6 scans (375 uS = 1500 T-States) and then enter the 1000 T-State
; loop (4 scan lines) in which we stuff 16 entries (32 bytes) into the 4096
; color palette area.
;
; On the first screen we will start R, B and G at 0, 0, 0 in the upper left.
; for the first palette fill we will use 0g00, 0g01, 0g02, 0g03...0g0F
; on the next bar it will be:            0g10, 0g11, 0g12, 0g13...0g1F
;                                          :    :                  :
;                                        0gF0, 0gF1, 0GF2, 0gF3...0gFF
;
; At the end of a screen we will do a keyboard read and if the user has
; pressed 0 (cursor up) we will increase g (MOD 16). If he pressed 2 (cursor
; down) we will decrease g (MOD 16) and if he pressed 66 (ESC) we will end
; the test.
;
; This first bit (stolen from PAL27) just syncs with F/F.
;
        ld      d,0                     ;start green at level 0

        ld      bc,0F500h               ;8255 port B (bit 0 is f/f)
_PalWaitFFLow:
        in      a,(c)
        bit     0,a
        jr      nz,_PalWaitFFLow        ;need to see it low

_PalWaitFFHigh:
        in      a,(c)
        bit     0,a
        jr      z,_PalWaitFFHigh        ;then wait for the low to high edge

        ld      d,0                     ;start Green (g) at 0

_PalDisplayMainLoop:
        ld      bc,0F500h
_PalMainWaitFFHigh:
        in      a,(c)                   ;first time thru we have just seen F/F
;        rra
;        jr      nc,_PalMainWaitFFHigh
        bit     0,a                     ;so this will fall thru. On subsequent
        jr      z,_PalMainWaitFFHigh    ;loops it'll wait till FF leading edge
;
; At this moment we are syncd with the start of frame flyback. It lasts for 8
; scan lines (=2000 T-States)
;
; We do a bit of a MODE switch at the top so that the title line can be in
; MODE 1 but the rest of the screen will be MODE 0
;
        ld      e,0                     ;start Red and Blue at 0,0 in upper left

        exx
        ld      a,c
        and     0FCh                    ;clear MODE bits
        or      01                      ;make it mode 1
        ld      c,a
        ld      b,07Fh
        out     (c),c
        exx

        ld      hl,(?Pal496Background)  ;quite BLACK
        ld      (6400h),hl              ;set PEN 0 (background)
;        ld      hl,0FFFh                ;quite WHITE
        ld      bc,777h
        add     hl,bc
        ld      (6402h),hl              ;set PEN 1 (text)

        ld      hl,03E0h
;        ld      hl,390h
_PalInsideFFDelay:
        dec     hl
        ld      a,h
        or      l
        jr      nz,_PalInsideFFDelay

        exx
        ld      a,c
        and     0FCh                    ;clear to MODE 0
        ld      c,a
        ld      b,07Fh
        out     (c),c
        exx


_PalBarLoop:
        ld      b,15                    ;(8) number of palette entries to set
        ld      hl,06402h               ;(12) address of palette entries
_PalStuffItLoop:
        ld      (hl),e                  ;(8) set R and B value
        inc     hl                      ;(8)
        ld      (hl),d                  ;(8) and green value
        inc     hl                      ;(8)
        inc     e                       ;(4) step BLUE up one as we go across line
        djnz    _PalStuffItLoop         ;(16/8)
        inc     e                       ;make it wrap to MOD 16 again
;
; We get here after 8+12+16*(8+8+8+8+4)+15*16+8=844 T-States. To get to the end
; of the current 4 line blank period and also delay across the next 6 colored
; lines we need to wait 156+1500 = 1656 T-States
;
; Because we've just set 16 entries the lower nybble of E will now=0 again and
; the upper nybble will have gone up one (RED) ready for next block of 16.
;

        ld      bc,60                   ;(12) We now delay to end of the 4
;
; now down to 1644 T-States to waste. The following delay loop takes (8+4+4+12)
; = 28. So we want 57 iterations (=1596). Then waste 48 T-States (+4 cos last
; JR NZ only took 8). The test to see if E has wrapped takes 28.
;
_PalWaitNext4Loop:
        dec     bc                      ;(8)  blank and across the 6 displayed
        ld      a,b                     ;(4)  lines (1500 T-States)
        or      c                       ;(4)
        jr      nz,_PalWaitNext4Loop    ;(12/8)

;        ld      b,0                     ;(8)
;        ld      b,0                     ;(8)
;        ld      b,0                     ;(8)
;        ld      b,0                     ;(8) this 24 makes up the correct amount

        ld      a,e                     ;(8)
        cp      0                       ;(8) wrapped to 0 yet ? (done 16 bars)
        jr      nz,_PalBarLoop          ;(12)
;
; We've now done 16 bars of 16 blobs so we are 160 scans down the screen. We
; have plenty of time to read the keyboard and get ready for next time round
;
; First we wait for scan to past last 6 scan lines so that the last line of 16
; is seen OK
;
        ld      b,100
_PalSeeLastLine:
        djnz    _PalSeeLastLine

        push    de
        ld      de,00326h
        call    SetCursorPos
        pop     de

        push    de
        ld      a,d
        call    PrintAHex               ;print the green value
        pop     de

        push    de                      ;D has the current green value
        call    KeyboardReadNoDelay
        pop     de                      ;retrieve g
        cp      0FFh                    ;no key
        jr      z,_PalNoKeyPressed
        ld      l,a                     ;store key just read
        ld      a,(?PaletteLastKeyPressed)
        cp      l                       ;is it the same key again ?
        jr      z,_PalIncrementKeyCount
        ld      a,l
        ld      (?PaletteLastKeyPressed),a      ;so just store it this time
        jr      _PalNoKeyPressed

_PalIncrementKeyCount:
        ld      a,(?PaletteKeyCount)
        inc     a
        ld      (?PaletteKeyCount),a
        cp      6                       ;has same key been pressed 6 times yet
        jr      z,_PalWotKeyWasIt
        jr      _PalNoKeyPressed


_PalWotKeyWasIt:
        ld      a,0
        ld      (?PaletteKeyCount),a    ;reset counter
        ld      a,l                     ;get back code of key that was pressed
        cp      000h                    ;cursor up
        jr      z,_PalUpKeyPressed
        cp      72                      ;joy 0 up
        jr      z,_PalUpKeyPressed
        cp      002h                    ;cursor down
        jr      z,_PalDownKeyPressed
        cp      73                      ;joy 0 down
        jr      z,_PalDownKeyPressed
        cp      66                      ;ESC key
        jr      z,_PalEndTest
        cp      76                      ;Joy 0 fire 0 = End
        jr      z,_PalEndTest
        cp      8                       ;left cursor, reduce background
        jr      z,_PalDecBackground
        cp      74                      ;joy 0 left
        jr      z,_PalDecBackground
        cp      1                       ;right cursor, increase background
        jr      z,_PalIncBackground
        cp      75                      ;joy o right
        jr      z,_PalIncBackground
        jr      _PalNoKeyPressed        ;ignore other keys


_PalUpKeyPressed:
        ld      a,d                     ;D is the green palette byte
        inc     a
        and     00Fh                    ;make MOD 16
        ld      d,a
        jp      _PalDisplayMainLoop

_PalDownKeyPressed:
        ld      a,d                     ;D is the green palette byte
        dec     a
        and     00Fh                    ;make MOD 16
        ld      d,a
        jp      _PalDisplayMainLoop

_PalIncBackground:
        ld      hl,(?Pal496Background)
        ld      bc,111h
        add     hl,bc
        ld      (?Pal496Background),hl
        jp      _PalDisplayMainLoop

_PalDecBackground:
        ld      hl,(?Pal496Background)
        ld      bc,-111h
        add     hl,bc
        ld      (?Pal496Background),hl
        jp      _palDisplayMainLoop

_PalNoKeyPressed:
        jp      _PalDisplayMainLoop

_PalEndTest:
;
; the user just pressed ESC.
;
        call    RelockArn5

        call    ScreenResetPalette

        ret

;===================
_Pal496NextScanLine:
;===================
;
; IX holds the address of the start of the current scan line. This routine
; just steps it down one. In each block of 8 lines we just move down 800h but
; when we want to get to the next 8 we subtract 3800h and add 80.
;
        push    bc
        push    ix
        ex      (sp),hl                 ;HL stored on stack, HL=IX=scan line
        pop     ix                      ;now got 'loc' in IX and 'sl' in HL

        ld      a,h                     ;check if we passed F8xx
        cp      0F8h
        jr      nc,_PalNextDown8
        ld      bc,800h                 ;No, so just add 800h
        add     hl,bc                   ;'sl' down one in a block of 8
        jr      _PalNextDone

_PalNextDown8:
        ld      bc,037B0h               ;3800h - 80
        sbc     hl,bc                   ;'sl' down into next block of 8

_PalNextDone:
        push    hl
        pop     ix                      ;make 'loc' = 'sl'

        pop     bc
        ret

        END
