; Copyright 2000 Pace Micro Technology plc
;
; 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.
;
; vdupalxx

; mjs Sep 2000
;
; Palette programming for generic video controller, delegating h/w
; specifics to HAL calls. Think of any 'VIDC' references here as for a
; generic video controller.

; Format of a 32-bit palette entry in soft palette tables (eg. FirPalAddr) and
; for calls to HAL is:
;
;    BBGGRRSS
;
; ie. 8-8-8-8 bits for Blue-Green-Red-Supremacy
; 'Supremacy' is expected to be 0=solid .. 255=transparent
; [pre-HAL, Medusa kernels used a soft palette format of 0SBBGGRR, being VIDC20-ish]
;
; soft palette tables are indexed by 0..259, where:
;
;   0..255    are normal (display) palette entries
;             type 0, index 0..255 for HAL calls
;   256       is border entry
;             type 1, index 0 for HAL calls
;   257..259  are pointer palette entries
;             type 2, index 1..3 for HAL calls (HAL index 0 assumed to
;             correspond to transparent, and not used)


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

; 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
        ASSERT  paletteV_LCDInvert = 10
        ASSERT  paletteV_VIDCDisable = 12
        ASSERT  paletteV_VIDCRestore = 13
        ASSERT  paletteV_ReadGammaCorrection = 14
        ASSERT  paletteV_ReadSupremacyTransfer = 15
        ASSERT  paletteV_SetSupremacyTransfer = 16

MOSPaletteV ROUT
        CMP     r4, #16
        ADDLS   pc, pc, r4, LSL #2
        MOV     pc, lr                  ; reason code not known, so pass it on
        MOV     pc, lr                  ; 0
        B       PV_ReadPalette          ; 1
        B       PV_SetPalette           ; 2
        B       PV_1stFlashState        ; 3
        B       PV_2ndFlashState        ; 4
        B       PV_SetDefaultPalette    ; 5
        B       PV_BlankScreen          ; 6
        B       PV_BulkRead             ; 7
        B       PV_BulkWrite            ; 8
        B       PV_GammaCorrect         ; 9
        MOV     pc, lr                  ; 10 (was PV_LCDInvert)
        MOV     pc, lr                  ; 11
        B       PV_VIDCDisable          ; 12
        B       PV_VIDCRestore          ; 13
        B       PV_ReadGammaCorrect     ; 22
        B       PV_ReadSupremacyXfer    ; 23
        B       PV_SetSupremacyXfer     ; 24

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

PV_SetDefaultPalette ROUT
        Push    "r0-r3,r5-r9"
        LDR     r0, [WsPtr, #DisplayModeFlags]
        TST     r0, #ModeFlag_GreyscalePalette
        BNE     %FT30
05
        BL      GetPalIndex             ; the new index 0-6
        ADR     r1, paldptab
        LDR     r2, [r1, r6, LSL #2]    ; offset from r1 to start of table
        ADD     r6, r6, #1              ; point to next item
        LDR     r5, [r1, r6, LSL #2]    ; offset from r1 to end of table +1
        TST     r2, #&80000000          ; if bit 31 set, it's a routine
        BIC     r2, r2, #&80000000      ; clear that flag
        ADD     r2, r2, r1              ; r2 -> start of table
        MOVNE   pc, r2                  ; or call routine
        BIC     r5, r5, #&80000000      ; clear routine flag
        ADD     r5, r5, r1              ; r5 -> end of table
        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

15
        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

30
        ; Check that we know how to generate a greyscale palette for this mode
        ASSERT  ModeFlag_DataFormatFamily_RGB = 0
        TST     r0, #ModeFlag_DataFormatFamily_Mask ; Must be RGB
        BNE     %BT05
        LDR     r6, [WsPtr, #DisplayNColour]
        CMP     r6, #63                 ; not possible in VIDC10 256 colour
        BEQ     %BT05
        LDR     r0, [WsPtr, #Log2BPP]
        CMP     r0, #3                  ; Must be <= 256 colour
        CMPLS   r6, #255                ; Check both values to be sure
        BHI     %BT05
        MOV     r1, #3
        MOV     r2, #0
        ADR     r5, greyscale_mult
        LDR     r5, [r5, r0, LSL #2]
        MOV     r0, #0
35
        MUL     r2, r0, r5
        BL      UpdateNormalColour
        SUBS    r6, r6, #1
        ADD     r0, r0, #1
        BGE     %BT35
        B       %BT15

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

        ; Ensure software pointer palette is refereshed
        LDR     r4, [WsPtr, #GraphicsVFeatures]
        TST     r4, #GVDisplayFeature_HardwarePointer
        LDREQB  r4, [WsPtr, #SWP_Callback]
        TEQEQ   r4, #0
        BLEQ    RegisterSoftwarePointerCallback

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

        LTORG

greyscale_mult
        DCD     &FFFFFF00 ; 1bpp
        DCD     &55555500 ; 2bpp
        DCD     &11111100 ; 4bpp
        DCD     &01010100 ; 8bpp

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

; 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
        &       (paldatLin-paldptab) :OR: &80000000 ; 16bpp, 32bpp, 256 greys - use routine
        &       paldatT-paldptab        ; teletext mode
        &       paldatHR-paldptab       ; Hi-res mono mode
        &       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

; Linear / greyscale palette or gamma table
paldatLin ROUT
        ADR     r5, paldatLintab
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

paldatLintab
 [ DefaultSupremacy
        &       &01010101       ; palette bit 0
        &       &02020202       ;             1
        &       &04040404       ;             2
        &       &08080808       ;             3
        &       &10101010       ;             4
        &       &20202020       ;             5
        &       &40404040       ;             6
        &       &80808080       ;             7
 |
        &       &01010100       ; palette bit 0
        &       &02020200       ;             1
        &       &04040400       ;             2
        &       &08080800       ;             3
        &       &10101000       ;             4
        &       &20202000       ;             5
        &       &40404000       ;             6
        &       &80808000       ;             7
 ]

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


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+4
        LDREQ   R2,[R9],#4
        BLEQ    UpdateNormalColour

        TEQ     R7,#16
        TEQNE   R7,#18

        MOVEQ   R0,R11
        MOVEQ   R1,#2+4
        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

        ; Update greyscale palette mode flag as appropriate
        LDR     r2, [WsPtr, #DisplayNColour]
        CMP     r7, #19
        CMPLO   r2, #256
        BHS     %FT90
        PHPSEI
        LDR     r7, [WsPtr, #DisplayModeFlags]
        CMP     r2, #63
        LDR     r5, [WsPtr, #FirPalAddr]
        MOVEQ   r2, #255
        LDR     r6, [WsPtr, #SecPalAddr]
        BIC     r7, r7, #ModeFlag_GreyscalePalette
30
        LDR     r8, [r5], #4
        LDR     r9, [r6], #4
        EOR     r8, r8, r8, LSL #8
        EOR     r9, r9, r9, LSL #8
        CMP     r8, #&10000
        CMPLO   r9, #&10000
        BHS     %FT40
        SUBS    r2, r2, #1
        BGE     %BT30
        ORR     r7, r7, #ModeFlag_GreyscalePalette
40
        STR     r7, [WsPtr, #DisplayModeFlags]
        ; Update live ModeFlags if not redirected to sprite
        LDR     r8, [WsPtr, #VduSprite]
        TEQ     r8, #0
        STREQ   r7, [WsPtr, #ModeFlags]
        PLP
90

        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 = BBGGRRSS
;       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
        LDR     r4, [WsPtr, #GraphicsVFeatures]
        TST     r4, #GVDisplayFeature_HardwarePointer
        LDREQB  r4, [WsPtr, #SWP_Callback]
        TEQEQ   r4, #0
        LDREQB  r4, [WsPtr, #DisplayLog2BPP]
        TSTEQ   r4, #&FC                ; Callback only needed here for <=8bpp
        BLEQ    RegisterSoftwarePointerCallback
        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, #256                ; 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, #ModeFlag_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
        ADD     r0, r0, #256            ; index in range 257..259
        MOV     r1, #3
        BL      UpdateSettingAndVIDC
10
        LDR     r4, [WsPtr, #GraphicsVFeatures]
        TST     r4, #GVDisplayFeature_HardwarePointer
        LDREQB  r4, [WsPtr, #SWP_Callback]
        TEQEQ   r4, #0
        BLEQ    RegisterSoftwarePointerCallback
        MOV     r4, #0                  ; indicate successful PaletteV op
        Pull    "pc"

; UpdateSettingStraightRGB
;
; in:   r0 = index (0..255 for normal, 256 for border, 257..259 for pointer)
;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
;            bit 2 set to suppress greyscale palette check
;       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 (s) in 1st colour
        MOVNE   r5, #&FF                ; r5 = 000000SS
        AND     r6, r3, #&FF0           ; r6 = 00000BG0
        ORR     r5, r5, r6, LSL #16     ; r5 = 0BG000SS
        AND     r6, r3, #&0FF           ; r6 = 000000GR
        ORR     r5, r5, r6, LSL #12     ; r5 = 0BGGR0SS
        AND     r6, r3, #&00F           ; r6 = 0000000R
        ORR     r5, r5, r6, LSL #8      ; r5 = 0BGGRRSS
        AND     r6, r3, #&F00           ; r6 = 00000B00
        ORR     r3, r5, r6, LSL #20     ; r3 = BBGGRRSS

        ANDS    r5, r4, #1 :SHL: 12     ; get supremacy bit (s) in EOR mask
        MOVNE   r5, #&FF                ; r5 = 000000SS
        AND     r6, r4, #&FF0           ; r6 = 00000BG0
        ORR     r5, r5, r6, LSL #16     ; r5 = 0BG000SS
        AND     r6, r4, #&0FF           ; r6 = 000000GR
        ORR     r5, r5, r6, LSL #12     ; r5 = 0BGGR0SS
        AND     r6, r4, #&00F           ; r6 = 0000000R
        ORR     r5, r5, r6, LSL #8      ; r5 = 0BGGRRSS
        AND     r6, r4, #&F00           ; r6 = 00000B00
        ORR     r4, r5, r6, LSL #20     ; r4 = BBGGRRSS

        B       UpdateSettingCommon


; UpdateSettingAndVIDC
;
; in:   r0 = index (0..255 for normal, 256 for border, 257..259 for pointer)
;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
;            bit 2 set to suppress greyscale palette check
;       r2 = BBGGRRSS
;
; out:  r0, r1, r2 preserved
;       r3, r4 corrupted
;

UpdateSettingAndVIDC ALTENTRY

        MOV     r3, r2
        MOV     r4, #0                  ; indicate no EORing between parts

; ... and drop thru to

; UpdateSettingCommon
;
; in:   r0 = index (0..255 for normal, 256 for border, 257..259 for pointer)
;       r1 = bit mask of which flash states to update (bit 0 set => 1st, bit 1 set => 2nd)
;            bit 2 set to suppress greyscale palette check
;       r3 = BBGGRRSS
;       r4 = BBGGRRSS to EOR with to go from 1st to 2nd flash state
;       r2, r5, r6, r7, lr stacked
;
; out:  r0, r1, r2, r4 preserved
;       r3 corrupted
;
UpdateSettingCommon ROUT
        PHPSEI                          ; protect against IRQs

        Push    "r8, r9"

        LDRB    r5, [WsPtr, #ScreenBlankFlag]
        TEQ     r5, #0
        MOVNE   r5, #&FFFFFFFF          ; bits to knock out if blanked

        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, #Pal_RTable-Pal_LogFirst ; r8 -> rgb transfer tables
        STR     r3, [r2, r0, LSL #2]!   ; store in logical colour and write back pointer
        AND     r6, r3, #&0000FF00      ; r6 = red << 8
        LDRB    r6, [r8, r6, LSR #8]    ; r6 = gamma(red)
        ADD     r8, r8, #&100           ; r8 -> green transfer
        AND     r9, r3, #&00FF0000      ; r9 = green << 16
        LDRB    r9, [r8, r9, LSR #16]   ; 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, #&FF000000      ; r9 = blue << 24
        LDRB    r9, [r8, r9, LSR #24]   ; r9 = gamma(blue)
        ORR     r6, r6, r9, LSL #16     ; r6 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
        ADD     r8, r8, #&100           ; r8 -> supremacy transfer
        AND     r9, r3, #&000000FF      ; r9 = supremacy
        LDRB    r9, [r8, r9]            ; r9 = transfer(supremacy)
        ORR     r6, r9, r6, LSL #8      ; r6 = gamma(rgb) + transfer(supremacy)
        STR     r6, [r2, #Pal_PhysFirst-Pal_LogFirst] ; store in physical copy

        BCC     %FT10                   ; only hit hardware if flash state = first

;;; mjs
;;; Pre HAL code had possible LCD greyscale munging, not supported in interim s.vduhint code
;;;
        Push    "r0-r2,r4,lr"
        LDR     r4, [WsPtr, #CurrentGraphicsVDriver]
        MOV     r4, r4, LSL #24
        ORR     r4, r4, #GraphicsV_WritePaletteEntry
        BIC     r1, r6, r5                ; r1 = palette colour after knocking out blanking bits
        AND     r2, r0, #255              ; reduced index (0..255 normal, 0 border, 1..3 pointer)
        CMP     r0, #256                  ; HI if pointer
        MOVLS   r0, r0, LSR #8            ; type 0=normal, 1=border
        MOVHI   r0, #2                    ; type 2=pointer
        BL      CallGraphicsV
        Pull    "r0-r2,r4,lr"

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, #Pal_RTable-Pal_LogSecond ; r8 -> rgb transfer tables
        STR     r3, [r2, r0, LSL #2]!   ; store in logical copy and write back

        AND     r6, r3, #&0000FF00      ; r6 = red << 8
        LDRB    r6, [r8, r6, LSR #8]    ; r6 = gamma(red)
        ADD     r8, r8, #&100           ; r8 -> green transfer
        AND     r9, r3, #&00FF0000      ; r9 = green << 16
        LDRB    r9, [r8, r9, LSR #16]   ; 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, #&FF000000      ; r9 = blue << 24
        LDRB    r9, [r8, r9, LSR #24]   ; r9 = gamma(blue)
        ORR     r6, r6, r9, LSL #16     ; r6 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
        ADD     r8, r8, #&100           ; r8 -> supremacy transfer
        AND     r9, r3, #&000000FF      ; r9 = supremacy
        LDRB    r9, [r8, r9]            ; r9 = transfer(supremacy)
        ORR     r6, r9, r6, LSL #8      ; r6 = gamma(rgb) + transfer(supremacy)
        STR     r6, [r2, #Pal_PhysSecond-Pal_LogSecond] ; store in physical copy

        BCS     %FT20                   ; only hit hardware if flash state = second

;;; mjs
;;; Pre HAL code had possible LCD greyscale munging, not supported in interim s.vduhint code
;;;
        Push    "r0-r2,r4,lr"
        LDR     r4, [WsPtr, #CurrentGraphicsVDriver]
        MOV     r4, r4, LSL #24
        ORR     r4, r4, #GraphicsV_WritePaletteEntry
        BIC     r1, r6, r5                ; r1 = palette colour after knocking out blanking bits
        AND     r2, r0, #255              ; reduced index (0..255 normal, 0 border, 1..3 pointer)
        CMP     r0, #256                  ; HI if pointer
        MOVLS   r0, r0, LSR #8            ; type 0=normal, 1=border
        MOVHI   r0, #2                    ; type 2=pointer
        BL      CallGraphicsV
        Pull    "r0-r2,r4,lr"

20
        ; Update greyscale palette mode flag as appropriate
        ; NOTE - relies on r3 being 2nd flash state
        LDR     r2, [WsPtr, #DisplayNColour]
        CMP     r0, #256
        CMPLO   r2, #256
        CMPLO   r1, #4
        BHS     %FT90
        EOR     r4, r3, r4              ; r4 = 1st flash state, r3 = 2nd
        EOR     r8, r3, r3, LSL #8
        EOR     r9, r4, r4, LSL #8
        TST     r1, #1
        MOVEQ   r9, #0                  ; treat unmodified flash states as greyscale - the full check below will sort things out properly if required
        TST     r2, #1
        MOVEQ   r8, #0
        CMP     r8, #&10000
        CMPLO   r9, #&10000
        ; LO -> palette potentially greyscale, HS -> definitely not greyscale
        LDR     r7, [WsPtr, #DisplayModeFlags]
        BICHS   r7, r7, #ModeFlag_GreyscalePalette
        BHS     %FT40
        TST     r7, #ModeFlag_GreyscalePalette
        BNE     %FT90
        ; Check the full palette for greyscaleness
        CMP     r2, #63
        LDR     r5, [WsPtr, #FirPalAddr]
        MOVEQ   r2, #255
        LDR     r6, [WsPtr, #SecPalAddr]
30
        LDR     r8, [r5], #4
        LDR     r9, [r6], #4
        EOR     r8, r8, r8, LSL #8
        EOR     r9, r9, r9, LSL #8
        CMP     r8, #&10000
        CMPLO   r9, #&10000
        BHS     %FT90
        SUBS    r2, r2, #1
        BGE     %BT30
        ORR     r7, r7, #ModeFlag_GreyscalePalette
40
        STR     r7, [WsPtr, #DisplayModeFlags]
        ; Update live ModeFlags if not redirected to sprite
        LDR     r8, [WsPtr, #VduSprite]
        TEQ     r8, #0
        STREQ   r7, [WsPtr, #ModeFlags]
90
        PLP
        Pull    "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   R11,[R10,LR,LSL#2]      ; BBGGRRSS
        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     R11,[R10,LR,LSL#2]      ; BBGGRRSS

        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   (BBGGRRSS)
;       R3 = second flash setting  (BBGGRRSS)
;

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

        LDR     r10, [WsPtr, #FirPalAddr]
        LDR     r2, [r10, r11, LSL #2]  ; r2 := 1st BBGGRRSS
        LDR     r10, [WsPtr, #SecPalAddr]
        LDR     r3, [r10, r11, LSL #2]  ; r3 := 2nd BBGGRRSS
99
        MOV     r4, #0
        Pull    "r10, r11, pc"

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

PV_1stFlashState ROUT
        Entry   "r0-r3"
        LDRB    r1, [WsPtr, #ScreenBlankFlag]
        TEQ     r1, #0
        LDREQ   r1, [WsPtr, #FirPalAddr]
        ADDEQ   r1, r1, #Pal_PhysFirst - Pal_LogFirst ; move pointer to physical palette copy
        LDRNE   r1, [WsPtr, #BlankPalAddr]
DoR0Flash
        LDR     r0, [WsPtr, #DisplayNColour]
        CMP     r0, #256
        EXIT    HS                      ; In > 256 colour modes, this is gamma (which shouldn't flash)
        MOV     r0, #0                  ; type 0 (normal)
        MOV     r2, #0                  ; start at entry 0
        MOV     r3, #256                ; 256 entries
        LDR     r4, [WsPtr, #CurrentGraphicsVDriver]
        MOV     r4, r4, LSL #24
        ORR     r4, r4, #GraphicsV_WritePaletteEntries
        BL      CallGraphicsV
        EXIT

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

PV_2ndFlashState ROUT
        ALTENTRY
        LDRB    r1, [WsPtr, #ScreenBlankFlag]
        TEQ     r1, #0
        LDREQ   r1, [WsPtr, #SecPalAddr]
        ADDEQ   r1, r1, #Pal_PhysFirst-Pal_LogFirst ; move pointer to physical palette copy
        LDRNE   r1, [WsPtr, #BlankPalAddr]
        B       DoR0Flash

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

UpdateAllPalette ROUT
        Entry   "r0-r4, r9-r11"
;sort out which palette to use
        LDROSB  r0, FlashState
        CMP     r0, #1
        LDRCS   r1, [WsPtr, #FirPalAddr]   ; FlashState = 1 => 1st state, 0 => 2nd state
        LDRCC   r1, [WsPtr, #SecPalAddr]
        ADD     r1, r1, #Pal_PhysFirst-Pal_LogFirst ; move pointer to physical palette copy
        LDRB    r10, [WsPtr, #ScreenBlankFlag]
        TEQ     r10, #0
        LDRNE   r1, [WsPtr, #BlankPalAddr] ; blank palette after all
;
        ADD     r10, r1, #256*4            ; pointer to border colour
        PHPSEI  r11                        ; disable IRQs round this bit
;first, the normal colours
        MOV     r0, #0                  ; type 0 (normal)
        MOV     r2, #0                  ; start at entry 0
        MOV     r3, #256                ; 256 entries
        LDR     r9, [WsPtr, #CurrentGraphicsVDriver]
        MOV     r9, r9, LSL #24
        ORR     r4, r9, #GraphicsV_WritePaletteEntries
        BL      CallGraphicsV
;next, border colour
        LDR     r1, [r10]               ; border colour
        MOV     r0, #1                  ; type 1
        MOV     r2, #0                  ; index 0
        ORR     r4, r9, #GraphicsV_WritePaletteEntry
        BL      CallGraphicsV
;finally, pointer colours
        ADD     r1, r10, #4             ; pointer to pointer colours (oh yes)
        MOV     r0, #2                  ; type 2
        MOV     r2, #1                  ; start at index 1
        MOV     r3, #3                  ; 3 entries
        ORR     r4, r9, #GraphicsV_WritePaletteEntries
        BL      CallGraphicsV
;
        PLP     r11
        EXIT

; *****************************************************************************
;
;       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, r9"

        LDRB    r3, [WsPtr, #ScreenBlankFlag]
        CMP     r0, #1
        BHI     %FT99                   ; just reading
        TEQ     r0, r3                  ; changing to same state?
        BEQ     %FT99                   ; if so, do nothing

        AND     r0, r0, #1
        STRB    r0, [WsPtr, #ScreenBlankFlag]      ; update new state
        LDRB    r1, [WsPtr, #ScreenBlankDPMSState]
        AND     r1, r1, #3

        LDR     r4, [WsPtr, #CurrentGraphicsVDriver]
        MOV     r4, r4, LSL #24
        ORR     r4, r4, #GraphicsV_SetBlank
        BL      CallGraphicsV

        ; for backward compatibility, show video DMA state in
        ; MEMC soft copy (DON'T call OS_UpdateMEMC, which would also
        ; make redundant call to HAL)
        ;
        SavePSR r2
        LDR     r9, =ZeroPage
        WritePSRc SVC_mode+I_bit+F_bit, r14
        LDR     r1, [r9, #MEMC_CR_SoftCopy]
        TEQ     r0, #1
        BICEQ   r1, r1, #(1 :SHL: 10)
        ORRNE   r1, r1, #(1 :SHL: 10)
        STR     r1, [r9, #MEMC_CR_SoftCopy]
        RestPSR r2

        BL      UpdateAllPalette        ; update all palette, including border + pointer

99
        MOV     r0, r3
        MOV     r4, #0
        Pull    "r1-r3, r9, pc"

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

PV_ReadGammaCorrect ROUT
        Push    "r0-r3"
        LDR     r4, [WsPtr, #FirPalAddr]
        ADD     r4, r4, #Pal_RTable-Pal_LogFirst; point to gamma tables
        ADD     r3, r4, #256
10
        LDR     lr, [r4], #4
        STR     lr, [r0], #4
        TEQ     r4, r3
        BNE     %BT10

        ASSERT  Pal_GTable = Pal_RTable+256
        ADD     r3, r4, #256
20
        LDR     lr, [r4], #4
        STR     lr, [r1], #4
        TEQ     r4, r3
        BNE     %BT20

        ASSERT  Pal_BTable = Pal_GTable+256
        ADD     r3, r4, #256
30
        LDR     lr, [r4], #4
        STR     lr, [r2], #4
        TEQ     r4, r3
        BNE     %BT30

        MOV     r4, #0
        Pull    "r0-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"
        LDR     r4, [WsPtr, #FirPalAddr]
        ADD     r4, r4, #Pal_RTable-Pal_LogFirst; point to gamma tables
        ADD     r3, r4, #256
10
        LDR     lr, [r0], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT10

        ASSERT  Pal_GTable = Pal_RTable+256
        ADD     r3, r4, #256
20
        LDR     lr, [r1], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT20

        ASSERT  Pal_BTable = Pal_GTable+256
        ADD     r3, r4, #256
30
        LDR     lr, [r2], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT30

        BL      RecomputeLogicalPalette

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


; go through the logical palette, recomputing the physical from it using the new tables
;
; out:  r0-r4 corrupted

RecomputeLogicalPalette ROUT
        Push    "r5-r8, lr"

        LDR     r4, [WsPtr, #FirPalAddr]        ; r4 -> start of logical palette
        ADD     r5, r4, #Pal_PhysFirst-Pal_LogFirst ; r5 -> start of physical palette
        MOV     r6, r5                          ; r6 = r5 = end of logical palette
        ADD     r0, r4, #Pal_RTable-Pal_LogFirst; r0 -> red table
        ADD     r1, r4, #Pal_GTable-Pal_LogFirst; r1 -> green table
        ADD     r2, r4, #Pal_BTable-Pal_LogFirst; r2 -> blue table
        ADD     r3, r4, #Pal_STable-Pal_LogFirst; r3 -> supremacy table
40
        LDR     r7, [r4], #4                    ; get word
        AND     r8, r7, #&0000FF00              ; r8 = red << 8
        LDRB    r8, [r0, r8, LSR #8]            ; r8 = gamma(red)
        AND     lr, r7, #&00FF0000              ; lr = green << 16
        LDRB    lr, [r1, lr, LSR #16]           ; lr = gamma(green)
        ORR     r8, r8, lr, LSL #8              ; r8 = gamma(red) + (gamma(green)<<8)
        AND     lr, r7, #&FF000000              ; lr = blue << 24
        LDRB    lr, [r2, lr, LSR #24]           ; lr = gamma(blue)
        ORR     r8, r8, lr, LSL #16             ; r8 = gamma(red) + (gamma(green)<<8) + (gamma(blue)<<16)
        AND     lr, r7, #&000000FF              ; lr = supremacy
        LDRB    lr, [r3, lr]                    ; lr = transfer(supremacy)
        ORR     r8, lr, r8, LSL #8              ; r8 = gamma-corrected BBGGRRSS value
        STR     r8, [r5], #4                    ; store word
        TEQ     r4, r6
        BNE     %BT40

        BL      UpdateAllPalette

        Pull    "r5-r8, pc"

; *****************************************************************************
;
;       PV_ReadSupremacyXfer - Read supremacy transfer table
;
; in:   r0 -> supremacy table
;
; out:  r4 = 0

PV_ReadSupremacyXfer ROUT
        Push    "r0,r3"
        LDR     r4, [WsPtr, #FirPalAddr]
        ADD     r4, r4, #Pal_STable-Pal_LogFirst; point to supremacy table
        ADD     r3, r4, #256
10
        LDR     lr, [r4], #4
        STR     lr, [r0], #4
        TEQ     r4, r3
        BNE     %BT10

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

; *****************************************************************************
;
;       PV_SetSupremacyXfer - Read supremacy transfer table
;
; in:   r0 -> supremacy table
;
; out:  r4 = 0

PV_SetSupremacyXfer ROUT
        Push    "r0-r3"
        LDR     r4, [WsPtr, #FirPalAddr]
        ADD     r4, r4, #Pal_STable-Pal_LogFirst; point to supremacy table
        ADD     r3, r4, #256
10
        LDR     lr, [r0], #4
        STR     lr, [r4], #4
        TEQ     r4, r3
        BNE     %BT10

        BL      RecomputeLogicalPalette

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


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

PV_VIDCDisable  ROUT
        Push    "r0-r3, r9, r12"

        MOV     r0, #1
        AddressHAL
        CallHAL HAL_VideoSetPowerSave

        MOV     r4, #0
        Pull    "r0-r3, r9, r12, pc"

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

PV_VIDCRestore  ROUT
        Push    "r0-r3, r9, r12"

        MOV     r0, #0
        AddressHAL
        CallHAL HAL_VideoSetPowerSave

        MOV     r4, #0
        Pull    "r0-r3, r9, r12, pc"

        END