; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; > VduPal20

; mjs Sep 2000
;
; not used any more, since kernel/HAL split
; vdupalxx is the file now used for generic palette programming

; Palette programming for VIDC20

; *****************************************************************************

; PaletteV handler
; ----------------

; *****************************************************************************
;
;       MOSPaletteV - Default owner of PaletteV
;

        ASSERT  paletteV_Complete = 0
        ASSERT  paletteV_Read = 1
        ASSERT  paletteV_Set = 2
        ASSERT  paletteV_1stFlashState = 3
        ASSERT  paletteV_2ndFlashState = 4
        ASSERT  paletteV_SetDefaultPalette = 5
        ASSERT  paletteV_BlankScreen = 6
        ASSERT  paletteV_BulkRead = 7
        ASSERT  paletteV_BulkWrite = 8
        ASSERT  paletteV_GammaCorrection = 9
 [ LCDInvert
        ASSERT  paletteV_LCDInvert = 10
 ]
 [ StorkPowerSave
        ASSERT  paletteV_VIDCDisable = 12
        ASSERT  paletteV_VIDCRestore = 13
 ]

MOSPaletteV ROUT
        CMP     r4, #1
        MOVCC   pc, lr
        BEQ     PV_ReadPalette
        CMP     r4, #3
        BCC     PV_SetPalette
        BEQ     PV_1stFlashState
        CMP     r4, #5
        BCC     PV_2ndFlashState
        BEQ     PV_SetDefaultPalette
        CMP     r4, #7
        BCC     PV_BlankScreen
        BEQ     PV_BulkRead
        CMP     r4, #9
        BCC     PV_BulkWrite
        BEQ     PV_GammaCorrect
 [ LCDInvert
        CMP     r4, #11
        BCC     PV_LCDInvert
 ]
 [ StorkPowerSave
        MOVEQ   pc, lr
        CMP     r4, #13
        BCC     PV_VIDCDisable
        BEQ     PV_VIDCRestore
        MOVEQ   pc, lr
 ]
        MOV     pc, lr                  ; reason code not known, so pass it on

; *****************************************************************************

PV_SetDefaultPalette ROUT
        Push    "r0-r3,r5-r9"
        LDR     r0, [WsPtr, #PalIndex]  ; the new index 0-7
        ADR     r1, paldptab
        LDR     r2, [r1, r0, LSL #2]    ; offset from r1 to start of table
        ADD     r0, r0, #1              ; point to next item
        LDR     r5, [r1, r0, LSL #2]    ; offset from r1 to end of table +1
        ADDS    r2, r2, r1              ; r2 -> start of table
        BICMI   pc, r2, #&80000000      ; if negative then it's a routine
        ADD     r5, r5, r1              ; r5 -> end of table
        BIC     r5, r5, #&80000000
        MOV     r0, #0                  ; start at palette index 0
        MOV     r1, #3                  ; set both halves
10
        LDR     r6, [r2], #4
        MOVS    r3, r6, LSL #17         ; get 1st half word and set carry if flashing
        MOV     r3, r3, LSR #17
        MOVCC   r4, #0
        LDRCS   r4, =&FFF               ; flashing so invert 2nd half RGB
        BL      UpdateSettingStraightRGB
        ADD     r0, r0, #1
        MOVS    r3, r6, LSL #1          ; get 2nd half word and set carry if flashing
        MOV     r3, r3, LSR #17
        MOVCC   r4, #0
        LDRCS   r4, =&FFF
        BL      UpdateSettingStraightRGB
        ADD     r0, r0, #1
        TEQ     r2, r5
        BNE     %BT10

; now ensure all palette entries from 0..255 are initialised

        MOV     r3, #0                  ; set unused (and border) to black
        MOV     r4, #0                  ; no flashing
20
        CMP     r0, #256
        BHS     FinishDefault
        BL      UpdateSettingStraightRGB
        ADD     r0, r0, #1
        B       %BT20

FinishDefault
        MOV     r2, #0                  ; set border to black (and setup colours 2,3 in BBC gap modes)
        BL      BorderInitEntry

        MOV     r4, #0                  ; indicate PaletteV operation complete
        Pull    "r0-r3,r5-r9,pc"        ; restore registers and claim vector

        LTORG

; *****************************************************************************

; Table of offsets from paldata_pointer to palette data

paldptab
        &       paldat1-paldptab        ; 2  Colour Modes
        &       paldat2-paldptab        ; 4
        &       paldat4-paldptab        ; 16
        &       (paldat8-paldptab) :OR: &80000000  ; 256 (VIDC10 compatible) - use routine
        &       paldatT-paldptab        ; teletext mode
        &       paldatHR-paldptab       ; Hi-res mono mode
        &       (paldat16-paldptab) :OR: &80000000 ; 16 bpp - use routine
        &       (paldat32-paldptab) :OR: &80000000 ; 32 bpp (or 256 greys - they're identical!) - use routine
        &       paldatend-paldptab      ; end of table marker

paldat1 ; Data for 1 bit modes - only necessary to program registers 0 and 1

;               FSBGR

        DCW     &0000           ; 0  Black
        DCW     &0FFF           ; 1  White

paldat2 ; Data for 2 bit modes - only necessary to program registers 0..3

;               FSBGR

        DCW     &0000           ; 0  Black
        DCW     &000F           ; 1  Red
        DCW     &00FF           ; 2  Yellow
        DCW     &0FFF           ; 3  White

paldat4 ; Data for 4 bit modes - program all registers
        ; Flashing Colours will be needed here

;               FSBGR

        DCW     &0000           ; 0  Black
        DCW     &000F           ; 1  Red
        DCW     &00F0           ; 2  Green
        DCW     &00FF           ; 3  Yellow
        DCW     &0F00           ; 4  Blue
        DCW     &0F0F           ; 5  Magenta
        DCW     &0FF0           ; 6  Cyan
        DCW     &0FFF           ; 7  White
        DCW     &8000           ; 8  Flashing Black
        DCW     &800F           ; 9  Flashing Red
        DCW     &80F0           ; 10 Flashing Green
        DCW     &80FF           ; 11 Flashing Yellow
        DCW     &8F00           ; 12 Flashing Blue
        DCW     &8F0F           ; 13 Flashing Magenta
        DCW     &8FF0           ; 14 Flashing Cyan
        DCW     &8FFF           ; 15 Flashing White

; Routine to initialise palette for VIDC10-compatible 8bpp modes
; Note this must still be in between paldat4 and paldatT

paldat8 ROUT
        MOV     r1, #3                  ; set both halves of palette
        MOV     r0, #0                  ; starting index
10
        AND     r2, r0, #3              ; get tint bits
        ORR     r2, r2, r2, LSL #4      ; and duplicate into bits 8,9,12,13,16,17,20,21,24,25,28,29
        ORR     r2, r2, r2, LSL #8
        ORR     r2, r2, r2, LSL #16
        BIC     r2, r2, #&FF
        TST     r0, #4
        ORRNE   r2, r2, #&00004400
        TST     r0, #8
        ORRNE   r2, r2, #&44000000
        TST     r0, #&10
        ORRNE   r2, r2, #&00008800
        TST     r0, #&20
        ORRNE   r2, r2, #&00440000
        TST     r0, #&40
        ORRNE   r2, r2, #&00880000
        TST     r0, #&80
        ORRNE   r2, r2, #&88000000
        BL      UpdateSettingAndVIDC
        ADD     r0, r0, #1
        TEQ     r0, #&100
        BNE     %BT10
        B       FinishDefault

paldatT ; Data for teletext mode

        DCW     &0000           ; 0 Black
        DCW     &000F           ; 1 Red
        DCW     &00F0           ; 2 Green
        DCW     &00FF           ; 3 Yellow
        DCW     &0F00           ; 4 Blue
        DCW     &0F0F           ; 5 Magenta
        DCW     &0FF0           ; 6 Cyan
        DCW     &0FFF           ; 7 White

; Colours 8 to 15 have supremacy bit set

        DCW     &1000           ; 8 Supremacy+ Black
        DCW     &100F           ; 9            Red
        DCW     &10F0           ; 10           Green
        DCW     &10FF           ; 11           Yellow
        DCW     &1F00           ; 12           Blue
        DCW     &1F0F           ; 13           Magenta
        DCW     &1FF0           ; 14           Cyan
        DCW     &1FFF           ; 15           White

paldatHR  ; data for Hi-res mono mode
        DCW     &0000           ; Only red gun necessary
        DCW     &0111           ; but setting all three makes
        DCW     &0222           ; reading it more natural
        DCW     &0333
        DCW     &0444
        DCW     &0555
        DCW     &0666
        DCW     &0777
        DCW     &0888
        DCW     &0999
        DCW     &0AAA
        DCW     &0BBB
        DCW     &0CCC
        DCW     &0DDD
        DCW     &0EEE
        DCW     &0FFF

        DCW     &0000           ; border black
        DCW     &0010           ; fixed pointer colours
        DCW     &0020
        DCW     &0030

paldat16 ROUT
        ADR     r5, paldat16tab
palmetatab
        MOV     r1, #3                  ; set both halves of palette
        MOV     r0, #0                  ; starting index
10
        MOV     r8, r5
        MOV     r2, #0
        MOV     r6, r0
20
        LDR     r7, [r8], #4
        MOVS    r6, r6, LSR #1
        ORRCS   r2, r2, r7
        BNE     %BT20
        BL      UpdateSettingAndVIDC
        ADD     r0, r0, #1
        TEQ     r0, #&100
        BNE     %BT10
        B       FinishDefault

paldat16tab
        &       &00000800       ; palette bit 0
        &       &00081000       ;             1
        &       &08102100       ;             2
        &       &10214200       ;             3
        &       &21428400       ;             4
        &       &42840000       ;             5
        &       &84000000       ;             6
        &       &00000000       ;             7

paldat32 ROUT
        ADR     r5, paldat32tab
        B       palmetatab

paldat32tab
        &       &01010100       ; palette bit 0
        &       &02020200       ;             1
        &       &04040400       ;             2
        &       &08080800       ;             3
        &       &10101000       ;             4
        &       &20202000       ;             5
        &       &40404000       ;             6
        &       &80808000       ;             7

paldatend

; *****************************************************************************

; PaletteV call to set palette in bulk
; in:   R0 => list of colours, or 0
;       R1 =  colour type (16,17,18,24,25) in b24-31 & number to do in b23-b00
;       R2 => list of palette entries (both flash states if 16, one if 17/18)
;       R4 = PaletteV reason code
;
; out:  R4 = 0, claim vector if recognised
;       otherwise preserve R4 and pass on

PV_BulkWrite    ROUT
        Push    "R0-R3,R5-R11"          ; pc already stacked

        ;register usage:
        ;[R6] colour list
        ;R7   colour type
        ;R8   max number
        ;[R9] palette entries
        ;R10  loop counter
        ;R11  colour number

        MOV     R7,R1,LSR #24
        BIC     R8,R1,#&FF000000
        MOV     R6,R0
        MOV     R9,R2

        MOV     R10,#0
10
        TEQ     R6,#0
        MOVEQ   R11,R10
        LDRNE   R11,[R6],#4

        TEQ     R7,#16
        TEQNE   R7,#17

        MOVEQ   R0,R11
        MOVEQ   R1,#1
        LDREQ   R2,[R9],#4
        BLEQ    UpdateNormalColour

        TEQ     R7,#16
        TEQNE   R7,#18

        MOVEQ   R0,R11
        MOVEQ   R1,#2
        LDREQ   R2,[R9],#4
        BLEQ    UpdateNormalColour

        TEQ     R7,#24

        MOVEQ   R0,R11
        LDREQ   R2,[R9],#4
        BLEQ    BorderColour

        TEQ     R7,#25

        MOVEQ   R0,R11
        LDREQ   R2,[R9],#4
        BLEQ    PointerColour

        ADD     R10,R10,#1
        CMP     R10,R8
        BCC     %BT10

        MOV     R4,#0
        Pull    "R0-R3,R5-R11,PC"

; *****************************************************************************

; PaletteV call to set palette
; in:   R0 = logical colour
;       R1 = colour type (16,17,18,24,25)
;       R2 = BBGGRRS0
;       R4 = PaletteV reason code
;
; out:  R4 = 0, claim vector if recognised
;       otherwise preserve R4 and pass on
;

;amg 19/4/93 - change this routine to make all the calls subroutines rather
; than branches. Although it will slow this down a bit, it makes the bulk
; write a lot simpler and involves less duplication of mungeing code.

PV_SetPalette ROUT
        Push    "r0-r3"
        TEQ     r1, #16                 ; if 16 then set both colours
        MOVEQ   r1, #3
        BEQ     Call_UpdateNormalColour

        TEQ     r1, #17                 ; elif 17 then set 1st colour
        MOVEQ   r1, #1
        BEQ     Call_UpdateNormalColour

        TEQ     r1, #18                 ; elif 18 then set 2nd colour
        MOVEQ   r1, #2
        BEQ     Call_UpdateNormalColour

        TEQ     r1, #24                 ; elif 24 then border colour
        BEQ     Call_BorderColour

        TEQ     r1, #25                 ; elif 25 then pointer colour
        BEQ     Call_PointerColour
10
        Pull    "r0-r3"
        MOV     pc, lr                  ; else not defined

Call_UpdateNormalColour
        BL      UpdateNormalColour
        Pull    "r0-r3,pc"

BorderInitEntry Push "r0-r3,lr"           ; entry used in default palette setting
Call_BorderColour
        BL      BorderColour
        Pull    "r0-r3,pc"

Call_PointerColour
        BL      PointerColour
        Pull    "r0-r3,pc"

; *****************************************************************************

UpdateNormalColour ROUT
        Push    "LR"
        LDR     lr, [WsPtr, #DisplayNColour] ; get the mask
        TEQ     lr, #63                 ; is it brain-damaged VIDC10-compatible 256 colour mode?
        BEQ     %FT10
        AND     r0, r0, lr              ; and mask it off
        AND     r0, r0, #255            ; definitely no more than 256 palette entries
        BL      UpdateSettingAndVIDC
05
        MOV     r4, #0                  ; indicate successful PaletteV op
        Pull    "pc"

10
        AND     r0, r0, #15             ; starting palette entry
20
        LDR     r3, =&88CC8800          ; r3 = bits controlled by bits 4..7 of pixel value
        BIC     r2, r2, r3
        TST     r0, #&10                ; test bit 4 (r3,7)
        ORRNE   r2, r2, #&00008800
        TST     r0, #&20                ; test bit 5 (g2,6)
        ORRNE   r2, r2, #&00440000
        TST     r0, #&40                ; test bit 6 (g3,7)
        ORRNE   r2, r2, #&00880000
        TST     r0, #&80                ; test bit 7 (b3,7)
        ORRNE   r2, r2, #&88000000
        BL      UpdateSettingAndVIDC
        ADD     r0, r0, #&10
        CMP     r0, #&100
        BCC     %BT20
        B       %BT05

BorderColour ROUT
        Push    "LR"
        MOV     r0, #&40000000          ; pseudo-palette-index for border colour
        MOV     r1, #3                  ; both colours
        BL      UpdateSettingAndVIDC

; Now test for BBC gap mode (ie 3 or 6)
; if so then set colour 2 to same as border, and colour 3 to inverse

        LDR     lr, [WsPtr, #DisplayModeFlags]
        TST     lr, #Flag_BBCGapMode
        BEQ     %FT10

        MOV     r0, #2                  ; make colour 2 (gap) same as border
        BL      UpdateSettingAndVIDC

        MOV     r0, #3                  ; make colour 3 inverse gap
        MVN     r2, r2                  ; invert R, G and B
        EOR     r2, r2, #&FF            ; but use same supremacy
        BL      UpdateSettingAndVIDC
10
        MOV     r4, #0                  ; indicate successful PaletteV op
        Pull    "pc"


PointerColour ROUT
        Push    "LR"
        ANDS    r0, r0, #3              ; force pointer colour number in range 1..3
        BEQ     %FT10                   ; zero is invalid
        MOV     r0, r0, LSL #28         ; move up to top nybble
        ORR     r0, r0, #&40000000      ; form pseudo-palette-index
        MOV     r1, #3
        BL      UpdateSettingAndVIDC
10
        MOV     r4, #0                  ; indicate successful PaletteV op
        Pull    "pc"

; UpdateSettingStraightRGB
;
; in:   r0 = logical colour (border = 4 << 28, pointer colours = 5,6,7 << 28)
;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
;       r3 = SBGR
;       r4 = SBGR to EOR with to go from 1st to 2nd flash state

; out:  r0,r1,r2,r4 preserved
;       r3 corrupted

UpdateSettingStraightRGB EntryS "r2,r5,r6,r7"
        ANDS    r5, r3, #1 :SHL: 12     ; get supremacy bit in 1st colour
        MOVNE   r5, #1 :SHL: 27         ; put in r5 in new position
        AND     r6, r3, #&FF0           ; r6 = 00000BG0
        ORR     r5, r5, r6, LSL #8      ; r5 = 0s0BG000
        AND     r6, r3, #&0FF           ; r6 = 000000GR
        ORR     r5, r5, r6, LSL #4      ; r5 = 0s0BGGR0
        AND     r6, r3, #&00F           ; r6 = 0000000R
        ORR     r5, r5, r6              ; r5 = 0s0BGGRR
        AND     r6, r3, #&F00           ; r6 = 00000B00
        ORR     r3, r5, r6, LSL #12     ; r3 = 0sBBGGRR

        ANDS    r5, r4, #1 :SHL: 12     ; get supremacy bit in EOR mask
        MOVNE   r5, #1 :SHL: 27         ; put in r5 in new position
        AND     r6, r4, #&FF0           ; r6 = 00000BG0
        ORR     r5, r5, r6, LSL #8      ; r5 = 0s0BG000
        AND     r6, r4, #&0FF           ; r6 = 000000GR
        ORR     r5, r5, r6, LSL #4      ; r5 = 0s0BGGR0
        AND     r6, r4, #&00F           ; r6 = 0000000R
        ORR     r5, r5, r6              ; r5 = 0s0BGGRR
        AND     r6, r4, #&F00           ; r6 = 00000B00
        ORR     r4, r5, r6, LSL #12     ; r5 = 0sBBGGRR
        B       UpdateSettingCommon


; UpdateSettingAndVIDC
;
; in:   r0 = logical colour (border = 4 << 28, pointer colours = 5,6,7 << 28)
;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
;       r2 = BBGGRRS0
;
; out:  r0, r1, r2 preserved
;       r3, r4 corrupted
;

UpdateSettingAndVIDC ALTENTRY

        ;amg: changed to handle 4 bits of supremacy

        MOV     r3, r2, LSR #8          ; r3 = 00bbggrr
        ANDS    r2, r2, #&F0            ; r2 = 000000s0
        ORRNE   r3, r3, r2, LSL #20     ; r3 = 0sbbggrr

        MOV     r4, #0                  ; indicate no EORing between parts

; ... and drop thru to

; UpdateSettingCommon
;
; in:   r0 = logical colour (border = 4 << 28, pointer colours = 5,6,7 << 28)
;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
;       r3 = 0sBBGGRR   (s in bits 24-27)
;       r2, r5, r6, r7, lr stacked
;
; out:  r0, r1, r2, r4 preserved
;       r3 corrupted
;
UpdateSettingCommon ROUT
        PHPSEI                          ; protect against IRQs

        Push    "r0, r8, r9"
        MOV     r7, #VIDC

        TST     r0, #&40000000          ; if border or pointer
        ORRNE   r3, r3, r0              ; then merge with RGB
        MOVNE   r0, r0, LSR #28         ; and make r0 into index for soft copy
        ADDNE   r0, r0, #(256-4)        ; ie 256, 257, 258 or 259

        ORREQ   r5, r0, #VIDCPalAddress ; else set up palette index register
        STREQ   r5, [r7]

        LDRB    r5, [WsPtr, #ScreenBlankFlag]
        TEQ     r5, #0
        MOVNE   r5, #&0FFFFFFF          ; bits to knock out if blanked (EBBGGRR)

        LDROSB  r2, FlashState          ; 0 => second, 1 => first
        CMP     r2, #1                  ; C=0 => second, C=1 => first

        TST     r1, #1
        BEQ     %FT10                   ; skip if not setting 1st colour
        LDR     r2, [WsPtr, #FirPalAddr]
        ADD     r8, r2, #(256+1+3)*4*4  ; r8 -> rgb transfer tables
        STR     r3, [r2, r0, LSL #2]!   ; store in logical colour and write back pointer
        AND     r6, r3, #&FF            ; r6 = red
        LDRB    r6, [r8, r6]            ; r6 = gamma(red)
        ADD     r8, r8, #&100           ; r8 -> green transfer
        AND     r9, r3, #&FF00          ; r9 = green << 8
        LDRB    r9, [r8, r9, LSR #8]    ; r9 = gamma(green)
        ORR     r6, r6, r9, LSL #8      ; r6 = gamma(red) + (gamma(green)<<8)
        ADD     r8, r8, #&100           ; r8 -> blue transfer
        AND     r9, r3, #&FF0000        ; r9 = blue << 16
        LDRB    r9, [r8, r9, LSR #16]   ; r9 = gamma(blue)
        ORR     r6, r6, r9, LSL #16     ; r6 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
        AND     r9, r3, #&FF000000      ; knock out rgb from original
        ORR     r6, r6, r9              ; and or in new bits
        STR     r6, [r2, #(256+1+3)*4*2] ; store in physical copy

        BICCS   r6, r6, r5              ; knock out bits for blanking
   [ LCDSupport
        Push    "lr", CS
        BLCS    Poke_VIDC
        Pull    "lr", CS
   |
        STRCS   r6, [r7]                ; poke VIDC if setting 1st colour and in 1st state
   ]
10
        EOR     r3, r3, r4              ; toggle requested bits for 2nd half
        TST     r1, #2
        BEQ     %FT20                   ; skip if not setting 2nd colour
        LDR     r2, [WsPtr, #SecPalAddr]
        ADD     r8, r2, #(256+1+3)*4*3  ; r8 -> rgb transfer tables
        STR     r3, [r2, r0, LSL #2]!   ; store in logical copy and write back

        AND     r6, r3, #&FF            ; r6 = red
        LDRB    r6, [r8, r6]            ; r6 = gamma(red)
        ADD     r8, r8, #&100           ; r8 -> green transfer
        AND     r9, r3, #&FF00          ; r9 = green << 8
        LDRB    r9, [r8, r9, LSR #8]    ; r9 = gamma(green)
        ORR     r6, r6, r9, LSL #8      ; r6 = gamma(red) + (gamma(green)<<8)
        ADD     r8, r8, #&100           ; r8 -> blue transfer
        AND     r9, r3, #&FF0000        ; r9 = blue << 16
        LDRB    r9, [r8, r9, LSR #16]   ; r9 = gamma(blue)
        ORR     r6, r6, r9, LSL #16     ; r6 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
        AND     r9, r3, #&FF000000      ; knock out rgb from original
        ORR     r6, r6, r9              ; and or in new bits
        STR     r6, [r2, #(256+1+3)*4*2] ; store in physical copy

        BICCC   r6, r6, r5              ; knock out bits for blanking
   [ LCDSupport
        Push    "lr", CC
        BLCC    Poke_VIDC
        Pull    "lr", CC
   |
        STRCC   r6, [r7]                ; poke VIDC if setting 2nd colour and in 2nd state
   ]
20
        PLP
        Pull    "r0, r8, r9"
        EXITS                           ; restore registers, claim vector

; *****************************************************************************
;
; PV_BulkRead - Read multiple palette entries with one call
;
; in:   R0 => list of colours wanted, or 0 to start with first and increment
;       R1 =  b24-b31 - colour type: 16/17/18/24/25
;             b00-b23 - number of colours to do
;
;       R2 => memory for first flash state colours (and second if R3=0)
;       R3 => memory for second flash state colours (if 0, intermingle with R2 instead)
;
; out:  all preserved (R4 set to 0 to show call handled)

; flags used to control routine

PV_BR_WantFirst *       1               ; doing 16 or 17
PV_BR_WantSecond *      2               ; doing 16 or 18
PV_BR_HaveList *        4               ; we have a list of colours
PV_BR_TwoLists *        8               ; we have two output areas (R2 & R3 valid)
PV_BR_Border   *        16              ; doing 24
PV_BR_Mouse    *        32              ; doing 25

PV_BulkRead ROUT
        Push    "R0-R3,R6-R11"             ; return addr already stacked

        MOV     R6,R1,LSR #24           ; isolate the colour type

        MOV     R7,#(PV_BR_WantFirst + PV_BR_WantSecond)

        CMP     R6,#17                  ; do we want both flash states ?
        BICEQ   R7,R7,#PV_BR_WantSecond ; if 17 only want first flash state

        CMP     R6,#18
        BICEQ   R7,R7,#PV_BR_WantFirst  ; if 18 only want second flash state

        CMP     R6,#24
        ORREQ   R7,R7,#PV_BR_Border
        ORRGT   R7,R7,#PV_BR_Mouse

        ;now set up other control flags
        CMP     R0,#0
        ORRNE   R7,R7,#PV_BR_HaveList   ; we have a list of colours

        CMP     R3,#0
        ORRNE   R7,R7,#PV_BR_TwoLists   ; we have two output areas

        ;set up a mask for the number of colours
        LDR     R8,[WsPtr,#DisplayNColour]
        TEQ     R8,#63
        MOVEQ   R8,#255                 ; deal with braindamaged 8BPP case

        ;the mouse has colours 1-3
        TST     R7,#PV_BR_Mouse
        MOVNE   R8,#3

        ;take the colour type off the top of R1, leaving #colours wanted
        BIC     R1,R1,#&FF000000

        ; register usage:
        ; [R0]: colour list
        ; R1: number of colours
        ; [R2]: first flash state list
        ; [R3]: second flash state list
        ; R7: control flags
        ; R8: mask for colour number
        ; R9: loop counter
        ; R10: misc
        ; LR: misc

        MOV     R9,#0
30
        TST     R7,#PV_BR_HaveList
        LDRNE   LR,[R0],#4
        MOVEQ   LR,R9                   ; LR = wanted colour number

        AND     LR,LR,R8                ; ensure it is sensible

        TST     R7,#PV_BR_Border
        MOVNE   LR,#256                 ; border is stored as colour 256

        TST     R7,#PV_BR_Mouse
        BEQ     %FT40

        TEQ     LR,#0
        BEQ     %FT50                   ;colour 0 is invalid
        ADD     LR,LR,#256              ;bring into range (257-259)

40
        TST     R7,#PV_BR_WantFirst

        LDRNE   R10,[WsPtr,#FirPalAddr]

        LDRNE   R10,[R10,LR,LSL#2]      ; xsbbggrr - could be 4 sup. bits
        MOVNE   R11,R10,LSL #8          ; bbggrr00
        ANDNE   R10,R10,#&0F000000      ; 0s000000
        ORRNE   R11,R11,R10,LSR #20     ; bbggrrs0
        STRNE   R11,[R2],#4

        TST     R7,#PV_BR_WantSecond
        BEQ     %FT60                   ; have to use a branch here - another TST coming up

        LDR     R10,[WsPtr,#SecPalAddr]

        LDR     R10,[R10,LR,LSL#2]      ; xsbbggrr
        MOV     R11,R10,LSL #8          ; bbggrr00
        ANDNE   R10,R10,#&0F000000      ; 0s000000
        ORR     R11,R11,R10,LSR #20     ; bbggrrs0

        TST     R7,#PV_BR_TwoLists

        STREQ   R11,[R2],#4
        STRNE   R11,[R3],#4

60      ADD     R9,R9,#1
        CMP     R9,R1
        BCC     %BT30
50
        MOV     R4,#0
        Pull    "R0-R3,R6-R11,PC"          ; return addr already stacked


; *****************************************************************************
;
;       PV_ReadPalette - PaletteV read palette handler
;
; in:   R0 = logical colour
;       R1 = 16 (read normal colour)
;            24 (read border colour)
;            25 (read cursor colour)
;
; out:  R2 = first flash setting   (BBGGRRS0), supremacy bits 4-7
;       R3 = second flash setting  (BBGGRRS0), supremacy bits 4-7
;

PV_ReadPalette ROUT
        Push    "r10,r11"
        LDR     r10, [WsPtr, #DisplayNColour] ; logical colours in this mode -1
        TEQ     r10, #63                ; if bodgy 256 colour mode
        MOVEQ   r10, #255               ; then use AND mask of 255

        TEQ     r1, #24                 ; is it reading border palette
        MOVEQ   r11, #&100              ; then set up border index
        BEQ     %FT10                   ; and go

        TEQ     r1, #25                 ; is it reading pointer palette
        BEQ     %FT05
        AND     r11, r0, r10            ; no, then force into suitable range
        B       %FT10                   ; always skip
05
        ANDS    r11, r0, #3             ; else force logical colour 0..3
        BEQ     %FT99                   ; and 0 is illegal, so do nothing
        ADD     r11, r11, #&100         ; set up correct index
10

; note no need to fudge 256-colour modes, since we have the correct full 256 entry palette

; bjga: changed to handle 4 bits of supremacy (BulkRead already does)

        LDR     r10, [WsPtr, #FirPalAddr]
        LDR     r10, [r10, r11, LSL #2] ; r10 := 1st XSBBGGRR
        MOV     r2, r10, LSL #8         ; r2  := 1st BBGGRR00
        AND     r10, r10, #&0F000000    ; r10 := 1st 0S000000
        ORR     r2, r2, r10, LSR #20    ; r2  := 1st BBGGRRS0

        LDR     r10, [WsPtr, #SecPalAddr]
        LDR     r10, [r10, r11, LSL #2] ; r10 := 2nd XSBBGGRR
        MOV     r3, r10, LSL #8         ; r3  := 2nd BBGGRR00
        AND     r10, r10, #&0F000000    ; r10 := 2nd 0S000000
        ORR     r3, r3, r10, LSR #20    ; r3  := 2nd BBGGRRS0
99
        MOV     r4, #0
        Pull    "r10, r11, pc"

; *****************************************************************************
;
;       PV_1stFlashState - PaletteV routine to set first flash state
;

PV_1stFlashState ROUT
        Push    "r0-r3"
        LDR     r0, [WsPtr, #FirPalAddr]
DoR0Flash
        MOV     r1, #256                ; just update normal palette
DoAllUpdate
        ADD     r0, r0, #(256+1+3)*4*2  ; move pointer to physical palette copy
        ADD     r1, r0, r1, LSL #2
        MOV     r2, #VIDC
        MOV     r3, #VIDCPalAddress + 0 ; initialise palette address to 0
        PHPSEI                          ; disable IRQs round this bit
        STR     r3, [r2]

        LDRB    r4, [WsPtr, #ScreenBlankFlag]
        TEQ     r4, #0                  ; if unblanked, leave all bits alone
        MOVNE   r4, #&0FFFFFFF          ; blanked, knock off all bits, except register bits
10
        LDR     r3, [r0], #4
        BIC     r3, r3, r4
   [ LCDSupport
        Push    "r6,r7,r14"
        MOV     r6, r3
        MOV     r7, r2
        BL      Poke_VIDC
        Pull    "r6,r7,r14"
   |
        STR     r3, [r2]
   ]
        TEQ     r0, r1
        BNE     %BT10

        PLP
        MOV     r4, #0
        Pull    "r0-r3, pc"

; *****************************************************************************
;
;       PV_2ndFlashState - PaletteV routine to set second flash state
;

PV_2ndFlashState ROUT
        Push    "r0-r3"
        LDR     r0, [WsPtr, #SecPalAddr]
        B       DoR0Flash

; *****************************************************************************
;
;       UpdateAllPalette - Update all VIDC palette entries
;

UpdateAllPalette Entry "r0-r3"          ; "r0-r3,lr" stacked ready to branch to code
        LDROSB  r0, FlashState
        CMP     r0, #1
        LDRCS   r0, [WsPtr, #FirPalAddr] ; FlashState = 1 => 1st state, 0 => 2nd state
        LDRCC   r0, [WsPtr, #SecPalAddr]
        MOV     r1, #260                ; update normal palette and border/pointer
        B       DoAllUpdate

; *****************************************************************************
;
;       PV_BlankScreen - Blank/unblank screen
;
; in:   R0 = -1 => read blank state
;       R0 = 0 => unblank screen
;       R0 = 1 => blank screen
;
; out:  R0 = old state (0=unblanked, 1=blanked)
;       R4 = 0

PV_BlankScreen ROUT
        Push    "r1-r3"
        LDRB    r3, [WsPtr, #ScreenBlankFlag]
        CMP     r0, #1
        BHI     %FT99

        TEQ     r0, r3                  ; changing to same state? (carry preserved)
        BEQ     %FT99                   ; if so, do nothing

        STRB    r0, [WsPtr, #ScreenBlankFlag] ; update new state
        MOV     r4, #VIDC
        LDRB    r0, [WsPtr, #ScreenBlankDPMSState]
        BCC     %FT50

; blanking

        TST     r0, #1                  ; if hsyncs should be off,
        LDRNE   r1, =HorizSyncWidth + ((1:SHL:14) -1)   ; maximum value in h.sync width register
        STRNE   r1, [r4]

        TST     r0, #2                  ; if vsyncs should be off,
        LDRNE   r1, =VertiSyncWidth + ((1:SHL:13) -1)   ; maximum value in v.sync width register
        STRNE   r1, [r4]

        LDR     r1, [WsPtr, #VIDCExternalSoftCopy]
        AND     r0, r0, #3
        TEQ     r0, #3                                         ; if both syncs off
        BICEQ   r1, r1, #Ext_HSYNCbits :OR: Ext_VSYNCbits
        ORREQ   r1, r1, #Ext_InvertHSYNC :OR: Ext_InvertVSYNC  ; set sync signals to low (less power)
        BIC     r1, r1, #Ext_DACsOn                     ; turn off the DACs
        STR     r1, [r4]

        MOV     r0, #(0 :SHL: 10) :OR: (3 :SHL: 8) ; blank:   video DMA off, continuous refresh
        B       %FT60

50

; unblanking

        LDR     r1, [WsPtr, #VIDCExternalSoftCopy]
        STR     r1, [r4]                ; restore DACs and sync type

        TST     r0, #1                  ; if hsyncs were turned off,
        LDRNE   r1, [WsPtr, #HSWRSoftCopy] ; then restore from soft copy
        STRNE   r1, [r4]

        TST     r0, #2                  ; if vsyncs were turned off,
        LDRNE   r1, [WsPtr, #VSWRSoftCopy] ; then restore from soft copy
        STRNE   r1, [r4]

        MOV     r0, #(1 :SHL: 10) :OR: (0 :SHL: 8) ; unblank: video DMA on, no refresh
60
        MOV     r1, #(1 :SHL: 10) :OR: (3 :SHL: 8) ; bits to modify
        SWI     XOS_UpdateMEMC

        PHPSEI  r0, lr                  ; disable IRQs so we don't get a flash in the middle
        BL      UpdateAllPalette        ; update all palette, including border + pointer
        PLP     r0                      ; restore old IRQ state
99
        MOV     r0, r3
        MOV     r4, #0
        Pull    "r1-r3, pc"


; *****************************************************************************
;
;       PV_GammaCorrect - Update gamma correction tables
;
; in:   r0 -> red table
;       r1 -> green table
;       r2 -> blue table
;
; out:  r4 = 0

PV_GammaCorrect ROUT
        Push    "r0-r3,r5-r8"
        LDR     r4, [WsPtr, #FirPalAddr]
        ADD     r4, r4, #(256+1+3)*4*4          ; point to gamma tables
        ADD     r3, r4, #256
10
        LDR     lr, [r0], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT10

        ADD     r3, r4, #256
20
        LDR     lr, [r1], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT20

        ADD     r3, r4, #256
30
        LDR     lr, [r2], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT30

; now go through the logical palette, recomputing the physical from it using the new tables

        SUB     r0, r4, #3*256                  ; r0 -> red table
        SUB     r1, r4, #2*256                  ; r1 -> green table
        SUB     r2, r4, #1*256                  ; r2 -> blue table

        LDR     r4, [WsPtr, #FirPalAddr]        ; r4 -> start of logical palette
        ADD     r5, r4, #260*4*2                ; r5 -> start of physical palette
        MOV     r6, r5                          ; r6 = r5 = end of logical palette
40
        LDR     r7, [r4], #4                    ; get word
        AND     r8, r7, #&FF                    ; r8 = red
        LDRB    r8, [r0, r8]                    ; r8 = gamma(red)
        AND     lr, r7, #&FF00                  ; lr = green << 8
        LDRB    lr, [r1, lr, LSR #8]            ; lr = gamma(green)
        ORR     r8, r8, lr, LSL #8              ; r8 = gamma(red) + (gamma(green)<<8)
        AND     lr, r7, #&FF0000                ; lr = blue << 16
        LDRB    lr, [r2, lr, LSR #16]           ; lr = gamma(blue)
        ORR     r8, r8, lr, LSL #16             ; r8 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
        AND     lr, r7, #&FF000000              ; lr = other bits
        ORR     r8, r8, lr                      ; r8 = gamma-corrected combined value
        STR     r8, [r5], #4                    ; store word
        TEQ     r4, r6
        BNE     %BT40

        BL      UpdateAllPalette

        MOV     r4, #0
        Pull    "r0-r3,r5-r8, pc"


 [ LCDInvert
; *****************************************************************************
;
;       PV_LCDInvert - Invert the LCD palette
;
; in:   r0 = inversion state to use (0=uninverted, 1=inverted)
;
; out:  r4 = 0

PV_LCDInvert ROUT
        MOV     r4, #0
        STRB    r0, [r4, #LCD_Inverted]

        BL      UpdateAllPalette
        MOV     r4, #0
        Pull    "pc"
 ]

 [ StorkPowerSave
; *****************************************************************************
PV_VIDCDisable
        ROUT
        Push    "r2"

        MOV     r4, #VIDC

        LDR     r2, =&C0000003    ;dac off, ereg set to external LUT
        STR     r2, [r4]

        LDR     r2, =&D0004000    ;Vclk off, Pcomp=0
        STR     r2, [r4]

        LDR     r2, =&E0004049    ;PoDown, Hclk
        STR     r2, [r4]

        MOV     r4, #0
        Pull    "r2, pc"

; *****************************************************************************
PV_VIDCRestore
        ROUT
        Push    "r2,r3,R8"
        MOV     r3, #VIDC

        LDR     r2, [WsPtr, #VIDCControlSoftCopy]       ;restore from soft copy
        STR     r2, [r3]

        LDR     r2, [WsPtr, #VIDCExternalSoftCopy]      ;restore from soft copy
        STR     r2, [r3]

        LDR     r2, [WsPtr, #VIDCFSynSoftCopy]          ;restore from soft copy
 [ 1 = 1
        LDR     R8, =FSyn_ResetValue    ; set test bits on, and r > v
        STR     R8, [R3]

; we may need some delay in here...

        LDR     R8, =FSyn_ClearR :OR: FSyn_ClearV :OR: FSyn_ForceLow :OR: FSyn_ForceHigh
        ORR     R2, R2, R8
        BIC     R2, R2, #FSyn_ForceHigh ; force test bits on, except this one
        STR     R2, [R3]

; we may also need some delay in here...

        BIC     R2, R2, R8              ; remove test bits
 ]
        STR     r2, [r3]


        MOV     r4, #0
        Pull    "r2,r3,R8, pc"
 ]
; *****************************************************************************
;
;       Poke_VIDC
;
; in:   r6 = CRT palette VIDC entry &AeBbGgRr (where A is the VIDC register 'address')
;       r7 = address to poke
;
; out:  all registers preserved
;
; NOTE: If LCD_Active in the KernelWS != 0, the value bunged into VIDC is the
; value required by the greyscaler to approximate the luminance of r6 on entry.

Poke_VIDC
        EntryS  "r0-r4"
 [ :LNOT: STB
        MOV     r0, #0
        LDRB    r0, [r0, #LCD_Active]
        CMP     r0, #0
        STREQ   r6,[r7]         ;Ho hum, it's a CRT, so no mucking about
        EXITS   EQ
 ]

        MOV     r0, #&0f
        AND     r4, r0, r6, LSR #28     ;r4 = VIDC register 'address' (in bottom 4 bits)
        MOV     r0, #&ff
        AND     r2, r0, r6, LSR #16     ;r2 = blue
        AND     r1, r0, r6, LSR #8      ;r1 = green
        ADD     r2, r2, r1, LSL #2      ;r2 = 4green + blue
        AND     r1, r0, r6              ;r1 = red
        ADD     r2, r2, r1, LSL #1      ;r2 = 4green + 2red + blue  - _Very_ approx CIE weightings!

 [ :LNOT: STB
        MOV     r0, #0
        LDRB    r0, [r0, #LCD_Inverted]
        CMP     r0, #0
        MOVNE   r0, #255
        RSBNE   r0, r0, r0, LSL #3      ;Move 1785 into R0
        RSBNE   r2, r2, r0
 ]
        ADR     r3, NewPalTab           ; perform binary chop using table (lifted from PortableA4 module, with added comments)

        LDR     r0, [r3, #8*4]                  ;read the middle table value
        CMP     r2, r0
        ADDCC   r1, r3, #0*4
        ADDCS   r1, r3, #8*4
        LDR     r0, [r1, #4*4]
        CMP     r2, r0
        ADDCS   r1, r1, #4*4
        LDR     r0, [r1, #2*4]
        CMP     r2, r0
        ADDCS   r1, r1, #2*4
        LDR     r0, [r1, #1*4]
        CMP     r2, r0
        ADDCS   r1, r1, #1*4
        SUB     r3, r1, r3

        MOV     r2, r3, LSR #2          ;R2 is now in the range 0-14
        CMP     r2, #7
        ADDGT   r2, r2, #1              ;Now R2 in range 0-7,9-15
        AND     r2, r2, #&0f            ;Just to be on the safe side....

        ORR     r2, r2, r2, LSL #8
        ORR     r2, r2, r2, LSL #16     ;Now &0x0x0x0x (where x is 0-7,9-15)
        ORR     r2, r2, r4, LSL #28     ;Now &Ax0x0x0x (A is the VIDC address)
        STR     r2, [r7]                ;And into the VIDC we go!

        EXITS

NewPalTab
        DCD     0, 119, 238, 357, 476, 595, 714, 833, 953, 1072, 1191, 1310, 1429, 1548, 1667, -1

        END