; 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.
;
; > Sources.Mode

;---------------------------------------------------------------------------
; Mode_Init
;
;       Out:    r0 corrupted
;
;       Get the current screen mode and set up indirect window data
;       appropriately.
;
Mode_Init
        ENTRY   "r1-r6"

        Debug   mode,"Mode_Init"

        BL      Mode_GetCurrent
        BLVC    Mode_GetInfo
        BLVC    Mode_TestPalette
        BLVC    Mode_SetSelection
        BLVC    Mode_SetWindowIcons
        EXIT    VS

        LDR     r1, menu_handle         ; Reopen any open menus (get ticks right).
        TEQ     r1, #0
        BLNE    Menu_Show
        EXIT


;---------------------------------------------------------------------------
; Mode_GetCurrent
;
;       Out:    r0 corrupted
;               r2 = mode specifier for current mode
;
;       Return the current mode specifier.
;
Mode_GetCurrent
        ENTRY   "r1"

        Debug   mode,"Mode_GetCurrent"

 [ Medusa
        MOV     r0, #1
        SWI     XOS_ScreenMode
        MOV     r2, r1                  ; r2=mode specifier (number or pointer)
 |
        MOV     r0, #135
        SWI     XOS_Byte
 ]

        EXIT


;---------------------------------------------------------------------------
; Mode_GetInfo
;
;       In:     r2 = mode specifier
;       Out:    r0 corrupted
;               r3 = X resolution
;               r4 = Y resolution
;               r5 = pixel depth
;               r6 = frame rate (or -1 = unknown).
;
;       Get information about given mode.
;
Mode_GetInfo
        ENTRY   "r1,r2"

        Debug   mode,"Mode_GetInfo: mode =",r2

        ASSERT  mode_spec_flags = 0
        ASSERT  mode_spec_xres = mode_spec_flags + 4
        ASSERT  mode_spec_yres = mode_spec_xres + 4
        ASSERT  mode_spec_depth = mode_spec_yres + 4
        ASSERT  mode_spec_rate = mode_spec_depth + 4
        MOV     r0, r2
        CMP     r0, #256
        LDMCSIB r0, {r3-r6}             ; If it's a mode pointer then get X,Y,pixel depth and rate
        EXIT    CS                      ;   and return.

        MOV     r1, #VduExt_XWindLimit
        SWI     XOS_ReadModeVariable
        ADDVC   r3, r2, #1              ; r3 = X resolution

        MOVVC   r1, #VduExt_YWindLimit
        SWIVC   XOS_ReadModeVariable
        ADDVC   r4, r2, #1              ; r4 = Y resolution

        MOVVC   r1, #VduExt_Log2BPP
        SWIVC   XOS_ReadModeVariable
        MOVVC   r5, r2                  ; r5 = pixel depth

        MOVVC   r6, #-1                 ; Don't know frame rate so specify an impossible one.
        EXIT


;---------------------------------------------------------------------------
; Mode_TestPalette
;
;       In:     r5 = pixel depth
;       Out:    r0 corrupted
;               r1 = flags
;                       bit     meaning when set
;                       0       mode uses grey levels not colour
;                       1-31    reserved
;
;       Read the current palette and determine if it is grey
;       level or colour.
;
Mode_TestPalette
        ROUT
        ENTRY   "r2-r4,r8,r9"

        Debug   mode,"Mode_TestPalette: depth =",r5

        LDRB    r8, flags
        ORR     r8, r8, #f_greylevel    ; Assume grey level until we find otherwise.

        MOV     r3, #1
        MOV     r1, r3, LSL r5          ; Log2BPP -> BPP
        MOV     r1, r3, LSL r1          ; BPP -> no of colours
        CMP     r1, #256                ; If 16 words or less then
        ADRLT   r2, user_data           ;   we can use user_data otherwise claim memory.
        MOVGT   r1, #256                ; Don't want more than 256 entries.
        MOVGE   r0, #ModHandReason_Claim
        MOVGE   r3, r1, LSL #2
        SWIGE   XOS_Module
        EXIT    VS
        Push    "r2"                    ; Remember base of block for later.

        MOV     r0, #0                  ; Start at palette entry 0.
        ORR     r1, r1, #17:SHL:24
        MOV     r3, #0
        MOV     r4, #7                  ; Read block of palette entries.
        MOV     r9, #PaletteV
        SWI     XOS_CallAVector
        BVS     %FT30

        BIC     r0, r1, #&FF000000      ; Get back number of entries we asked for.
        TEQ     r4, #0                  ; If block read didn't work then
        BNE     %FT40                   ;   try single reads.
10
        LDR     lr, [r2], #4            ; lr = &BBGGRRxx
        EOR     lr, lr, lr, LSL #8      ; lr = &BBGGRRxx ^ &GGRRxx00
        MOVS    lr, lr, LSR #16         ; Top 16 bits will be 0 if BB=GG=RR (ie. grey) so
        BICNE   r8, r8, #f_greylevel    ;   must be colour if not 0.
        BNE     %FT20
        SUBS    r0, r0, #1
        BNE     %BT10
20
        BL      freepalettemem
        STRB    r8, flags
        MOV     r1, r8
        EXIT
30
        BL      freepalettemem
        EXIT

40
        SUBS    r0, r0, #1              ; Start at last entry and work down to 0.
        BCC     %BT20                   ; If no more entries then all must be grey.

        MOV     r1, #17
        MOV     r4, #1
        SWI     XOS_CallAVector
        BVS     %BT30

        TEQ     r4, #0                  ; If no response to PaletteV then
        ADDNE   r0, r0, #1              ;   try the entry that failed again but
        BNE     %FT50                   ;   resort to OS_ReadPalette.

        EOR     r2, r2, r2, LSL #8      ; r2 = &BBGGRRxx ^ &GGRRxx00
        MOVS    r2, r2, LSR #16         ; Top 16 bits will be 0 if BB=GG=RR (ie. grey) so
        BICNE   r8, r8, #f_greylevel    ;   must be colour if not 0.
        BNE     %BT20
        B       %BT40

50
        SUBS    r0, r0, #1              ; Start at last entry and work down to 0.
        BCC     %BT20                   ; If no more entries then all must be grey.

        MOV     r1, #17
        SWI     XOS_ReadPalette
        BVS     %BT30

        EOR     r2, r2, r2, LSL #8      ; r2 = &B0G0R0xx ^ &G0R0xx00
        MOVS    r2, r2, LSR #16         ; Top 16 bits will be 0 if B=G=R (ie. grey) so
        BICNE   r8, r8, #f_greylevel    ;   must be colour if not 0.
        BNE     %BT20
        B       %BT50

freepalettemem
        Pull    r2                      ; Get back base of palette block.
        Push    "r0,lr"
        ADR     lr, user_data
        TEQ     r2, lr                  ; If it's not the user area then free it.
        MOVNE   r0, #ModHandReason_Free
        SWINE   XOS_Module
        Pull    "r0,pc",,^              ; Preserve error if there is one.


;---------------------------------------------------------------------------
; Mode_SetSelection
;
;       In:     r1 = flags
;                       bit     meaning when set
;                       0       mode uses grey levels not colour
;                       1-31    reserved
;               r3 = X resolution
;               r4 = Y resolution
;               r5 = pixel depth
;               r6 = frame rate (or -1 = unknown)
;       Out:    r0 corrupted
;               r2 -> enumerated mode descriptor (or -1 if not found)
;
;       Given the resolution and pixel depth set the menu selections
;       and mode which match.
;
Mode_SetSelection
        ROUT
        ENTRY   "r3-r7"

        Debug   mode,"Mode_SetSelection"

        LDR     r0, mode_classlist      ; Scan mode classes for the same resolution.
        TEQ     r0, #0                  ; If there is no class list then
        MOVEQ   r4, #&FF                ;   no selection
        BEQ     %FT20                   ;   and try colours.
        MOV     r7, #0
10
        LDR     lr, [r0], #4            ; Get pointer into mode_sortedlist.
        TEQ     lr, #0                  ; If end of list then
        MOVEQ   r4, #&FF                ;   no selection
        BEQ     %FT20                   ;   and try colours.

        LDR     lr, [lr]                ; Get pointer to mode descriptor.
        ASSERT  mode_desc_yres = mode_desc_xres + 4
        ADD     lr, lr, #mode_desc_xres
        LDMIA   lr, {r2,lr}             ; Get X,Y resolution.
        TEQ     r3, r2                  ; If not the same then
        TEQEQ   r4, lr
        ADDNE   r7, r7, #1              ;   move on to next class.
        BNE     %BT10

        MOV     r4, r7                  ; Found a class with required resolution.
20
        TST     r1, #f_greylevel
        ADRNE   r0, log2bpp_to_greyitem
        ADREQ   r0, log2bpp_to_colouritem
        LDRB    r3, [r0, r5]            ; Get colour selection.

        MOV     r0, #4                  ; Don't downgrade, allow non-menu modes.
        BL      Mode_FindSubClass       ; Find our copy of the mode descriptor.
        STR     r2, selected_subclass
        STRB    r3, selected_colours
        STRB    r4, selected_class
        STR     r5, selected_mode
        CLRV
        MOV     r2, r5                  ; Return mode descriptor in r2.
        EXIT

log2bpp_to_greyitem     DCB     mo_co_mono,mo_co_grey4,mo_co_grey16,mo_co_grey256,mo_co_32K,mo_co_16M
log2bpp_to_colouritem   DCB     mo_co_mono,mo_co_grey4,mo_co_colour16,mo_co_colour256,mo_co_32K,mo_co_16M
                        ALIGN


;---------------------------------------------------------------------------
; Mode_SetWindowIcons
;
;       In:     r2 -> enumerated mode descriptor (or -1)
;               r3 = X resolution (used if r2 = -1)
;               r4 = Y resolution (used if r2 = -1)
;               r6 = frame rate (used if r2 = -1)
;       Out:    r0 corrupted
;
;       Set the colour and resolution icons to the current selection.
;
Mode_SetWindowIcons
        ROUT
        ENTRY   "r1-r4"

        Debug   mode,"Mode_SetWindowIcons",r2

        LDRB    r1, selected_colours    ; Do colours icon first.
        MOV     lr, #-1
        Push    "r1,lr"                 ; Create fake mode selection block.
        ADR     r1, m_coloursmenu
        MOV     r2, sp
        LDR     r3, colours_indirect
        SWI     XWimp_DecodeMenu
        ADD     sp, sp, #8              ; Balance stack.
        EXIT    VS

        LDMIA   sp, {r1-r3}             ; Restore registers for resolution icon.
        CMP     r2, #-1                 ; If not a standard mode then
        BEQ     %FT10                   ;   have to build resolution.

        ADD     r0, r2, #mode_desc_name ; Copy mode string.

        LDRB    lr, [r0]
        TEQ     lr, #0                  ; If we have a blank mode name then
        ADDEQ   lr, r2, #mode_desc_xres
        LDMEQIA lr, {r3,r4}
        BEQ     %FT10                   ;   have to build resolution.

        LDR     r1, resolution_indirect
        LDR     r2, resolution_size
        BL      Mod_CopyString
        B       %FT20
10
        LDR     r1, resolution_indirect
        LDR     r2, resolution_size

        MOV     r0, r3
        SWI     XOS_ConvertCardinal4

        ADRVC   r0, resolution_spacer
        BLVC    Mod_CopyString

        MOVVC   r0, r4
        SWIVC   XOS_ConvertCardinal4
        EXIT    VS
20
        LDR     lr, [sp, #4]                    ; Get back mode descriptor.
        CMP     lr, #-1                         ; If not a standard mode then
        MOVEQ   r0, r6                          ;   use r6
        LDRNE   r0, [lr, #mode_desc_rate]       ;   else use rate from mode descriptor.

 [ SelectFrameRate
        CMP     r0, #-1                 ; If we don't know the frame rate then
        BEQ     %FT30                   ;   fill in "Unknown".

        LDR     r1, rate_indirect
        LDR     r2, rate_size
        SWI     XOS_ConvertCardinal4

        ADRVC   r0, mode_hz
        BLVC    Mod_CopyString
        EXIT
30
        ADR     r1, unknown
        LDR     r2, rate_indirect
        LDR     r3, rate_size
        BL      MsgTrans_Lookup
        EXIT
 |
        CMP     r0, #-1                 ; If we don't know the frame rate then
        EXIT    EQ                      ;   don't add any more.

        MOV     r3, r0

        ADR     r0, open_bracket
        BL      Mod_CopyString

        MOV     r0, r3
        SWI     XOS_ConvertCardinal4

        ADRVC   r0, close_bracket
        BLVC    Mod_CopyString
        EXIT
 ]

resolution_spacer       DCB     " x ",0
unknown                 DCB     "Unknown",0
 [ SelectFrameRate
mode_hz                 DCB     "Hz",0
 |
open_bracket            DCB     " (",0
close_bracket           DCB     "Hz)",0
 ]
                        ALIGN


;---------------------------------------------------------------------------
; Mode_SetModeString
;
;       In:     r1 = flags
;                       bit     meaning when set
;                       0       mode uses grey levels not colour
;                       1-31    reserved
;               r2 = mode specifier
;       Out:    r0 corrupted
;
;       Set mode string icon to the appropriate text.
;
Mode_SetModeString
        ROUT
        ENTRY   "r1-r6"

        Debug   mode,"Mode_SetModeString: flags,mode =",r1,r2

        ASSERT  mode_spec_flags = 0
        ASSERT  mode_spec_xres = mode_spec_flags + 4
        ASSERT  mode_spec_yres = mode_spec_xres + 4
        ASSERT  mode_spec_depth = mode_spec_yres + 4
        CMP     r2, #256
        MOVCC   r0, r2                  ; If it's a mode number then just print it
        LDMCSIB r2, {r3-r6}             ; else get X, Y, pixel depth and frame rate.
        LDR     r1, mode_indirect
        LDR     r2, mode_size
        BCC     %FT10

        ADR     r0, xres                ; Start with X resolution.
        BL      Mod_CopyString
        MOV     r0, r3
        SWI     XOS_ConvertCardinal4
        EXIT    VS

        ADR     r0, yres                ; Add Y resolution.
        BL      Mod_CopyString
        MOV     r0, r4
        SWI     XOS_ConvertCardinal4
        EXIT    VS

        LDR     lr, [sp]                ; Add C or G depending on colour or grey mode.
        TST     lr, #f_greylevel
        ADRNE   r0, greys
        ADREQ   r0, colours
        BL      Mod_CopyString
        ADR     r0, log2bpp_to_colours
        ADD     r0, r0, r5, LSL #2
        BL      Mod_CopyString

 [ Medusa
        Push    "r1,r2"                 ; Save where we are in the buffer.
        LDR     r0, [sp, #12]           ; Get back mode specifier.
        MOV     r1, #VduExt_XEigFactor
        SWI     XOS_ReadModeVariable
        MOVCC   r3, r2                  ; r3 = XEig factor

        MOVCC   r1, #VduExt_YEigFactor
        SWICC   XOS_ReadModeVariable
        MOVCC   r4, r2                  ; r4 = YEig factor
        Pull    "r1,r2"
        BCS     %FT05

        ADR     r0, xeig                ; Add XEig factor.
        BL      Mod_CopyString
        MOV     r0, r3
        SWI     XOS_ConvertCardinal4

        ADR     r0, yeig                ; Add YEig factor.
        BL      Mod_CopyString
        MOV     r0, r4
        SWI     XOS_ConvertCardinal4
05
 ]
        CMP     r6, #-1                 ; If frame rate = -1 then
        EXIT    EQ                      ;   nothing else to do.

        ADR     r0, frate               ; Add frame rate.
        BL      Mod_CopyString
        MOV     r0, r6
10
        SWI     XOS_ConvertCardinal4
        EXIT

xres            DCB     "X",0
yres            DCB     " Y",0
greys           DCB     " G",0
colours         DCB     " C",0
 [ Medusa
xeig            DCB     " EX",0
yeig            DCB     " EY",0
 ]
frate           DCB     " F",0
                ALIGN

log2bpp_to_colours
                DCB     "2",0,0,0
                DCB     "4",0,0,0
                DCB     "16",0,0
                DCB     "256",0
                DCB     "32K",0
                DCB     "16M",0
                ALIGN


;---------------------------------------------------------------------------
; Mode_KeyPressed
;
;       In:     r1 -> key pressed block
;       Out:    r0 corrupted
;
;       The Wimp has informed us of a key press.
;
Mode_KeyPressed
        ENTRY   "r1"

        LDR     r0, [r1]                ; r0 = window handle
        LDR     lr, mode_handle
        TEQ     r0, lr                  ; If it's the mode window then
        LDREQ   r0, [r1, #24]           ;   get the character
        TEQEQ   r0, #13                 ;   and if it's return then
        BEQ     %FT10                   ;   deal with it

        SWI     XWimp_ProcessKey        ; else pass on the key press.
        EXIT

10
        MOV     r1, #0
        STR     r1, menu_handle
        MOV     r1, #-1                 ; Remove menu and dialogue.
        SWI     XWimp_CreateMenu
        BL      Mode_WimpCommand        ; Change mode.
        EXIT


;---------------------------------------------------------------------------
; Mode_WimpCommand
;
;       Out:    r0 corrupted
;
;       Pass the string in the mode dialogue box to *WimpMode.
;
Mode_WimpCommand
        ENTRY   "r1,r2"

        ADR     r0, command_wimpmode    ; Build *WimpMode command.
        ADR     r1, user_data
        MOV     r2, #user_data_size
        BL      Mod_CopyString

        LDR     r0, mode_indirect
        BL      Mod_CopyString

        ADR     r0, user_data           ; Do it!
 [ debugmode :LOR: {FALSE}
        SUB     r0, r0, #4
        MOV     r1, #1
        BL      Mod_ReportError
        ADD     r0, r0, #4
 ]
        SWI     XOS_CLI
        ADRVS   r0, ErrorBlock_Modes_InvalidMode
        BLVS    MsgTrans_ErrorLookup

        EXIT

command_wimpmode
        DCB     "WimpMode ",0
        ALIGN

        MakeErrorBlock  Modes_InvalidMode


;---------------------------------------------------------------------------
; Mode_GetTable
;
;       Out:    r0 corrupted
;
;       Get the list of currently available modes.
;
Mode_GetTable
        ROUT
        ENTRY   "r1-r7"

        Debug   mode,"Mode_GetTable"

        LDR     r2, mode_space
        TEQ     r2, #0                  ; If we already have space allocated then
        MOVNE   lr, #0                  ;   free it.
        STRNE   lr, mode_space
        MOVNE   r0, #ModHandReason_Free
        SWINE   XOS_Module

 [ Medusa
        MOV     r0, #2                  ; Find out how much space we need for the new one.
        MOV     r2, #0
        MOV     r6, #0
        MOV     r7, #0
        SWI     XOS_ScreenMode
        EXIT    VS
 |
        MOV     r2, #-dummy_count
        ADR     r7, dummy_start
        ADRL    lr, dummy_end
        SUB     r7, r7, lr
 ]

        RSBS    r4, r2, #0              ; r4 = number of modes
        STR     r4, mode_count
        BEQ     %FT20
        RSB     r7, r7, #3
        BIC     r7, r7, #3              ; r7 = word aligned space required

        Debug   mode," count,size =",r4,r7

        ASSERT  mi_size = 24
        MOV     r3, r4, LSL #3          ; Determine max space required (start with resolution menu).
        ADD     r3, r3, r3, LSL #1      ; r3 = space for menu items (mode count * 24)
        ADD     r3, r3, #m_headersize   ; Add space for menu header.
        STR     r3, m_resolutionsize
 [ SelectFrameRate
        STR     r3, m_ratesize          ; Add space for rate menu.
        ADD     r3, r3, r3
 ]
        ADD     r3, r3, r7              ; Add space for mode table.
        ADD     r4, r4, #1
        ADD     r3, r3, r4, LSL #2      ; Add space for sorted list ((mode count + 1) * 4).
        ADD     r3, r3, r4, LSL #2      ; Add space for class list.
        ADD     r3, r3, r4, LSL #2      ; Add space for menu list.
        Debug   mode," total space allocated =",r3
        MOV     r0, #ModHandReason_Claim
        SWI     XOS_Module              ; Claim the space.
        EXIT    VS
        ASSERT  mode_space = mode_sortedlist
        STR     r2, mode_sortedlist     ; Store pointers.
        ADD     r6, r2, r4, LSL #2
        STR     r6, mode_classlist
        ADD     r6, r6, r4, LSL #2
        STR     r6, mode_menulist
        ADD     r6, r6, r4, LSL #2
        STR     r6, mode_table
        ADD     r2, r6, r7
        STR     r2, m_resolutionmenu
 [ SelectFrameRate
        LDR     lr, m_resolutionsize
        ADD     r2, r2, lr
        STR     r2, m_ratemenu
 ]

 [ Medusa
        MOV     r2, #0                  ; Fill in the table.
        MOV     r0, #2
        SWI     XOS_ScreenMode
        BVS     %FT10
        TEQ     r1, #0
        ADREQ   r0, ErrorBlock_Modes_CantEnumerate
        BLEQ    MsgTrans_ErrorLookup
 |
        ADR     r2, dummy_start
99
        SUBS    r7, r7, #1
        LDRCSB  lr, [r2], #1
        STRCSB  lr, [r6], #1
        BCS     %BT99
        CLRV
 ]
        BLVC    Mode_SortList
        EXIT    VC
10
        MOV     r7, r0                  ; Save error.
        LDR     r2, mode_space
        MOV     r0, #ModHandReason_Free ; Free the space we claimed.
        SWI     XOS_Module
        MOV     r0, r7                  ; Restore error.
        SETV
20
        MOV     lr, #0
        STR     lr, mode_sortedlist
        STR     lr, mode_classlist
        STR     lr, mode_menulist
        STR     lr, mode_table
        STR     lr, m_resolutionmenu
 [ SelectFrameRate
        STR     lr, m_ratemenu
 ]
        EXIT

 [ :LNOT: Medusa
        ROUT
 [ {FALSE}
dummy_count     *       0
dummy_start
dummy_end
 |
dummy_count     *       44
dummy_start

        DCD     &24,1,&420,&100,0,&46
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,1,&46
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,2,&46
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,3,&46
        DCB     "1056 x 256",0
        ALIGN

        DCD     &24,1,&420,&100,0,&3C
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,1,&3C
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,2,&3C
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,3,&3C
        DCB     "1056 x 256",0
        ALIGN

        DCD     &24,1,&420,&100,0,&32
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,1,&32
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,2,&32
        DCB     "1056 x 256",0
        ALIGN
        DCD     &24,1,&420,&100,3,&32
        DCB     "1056 x 256",0
        ALIGN

        DCD     &24,1,&420,&FA,0,&32
        DCB     "1056 x 250",0
        ALIGN
        DCD     &24,1,&420,&FA,1,&32
        DCB     "1056 x 250",0
        ALIGN
        DCD     &24,1,&420,&FA,2,&32
        DCB     "1056 x 250",0
        ALIGN
        DCD     &24,1,&420,&FA,3,&32
        DCB     "1056 x 250",0
        ALIGN

        DCD     &1C,1,&300,&120,0,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&300,&120,1,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&300,&120,2,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&300,&120,3,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&300,&120,4,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&300,&120,5,&32
        DCB     0
        ALIGN

        DCD     &1C,1,&280,&100,0,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&100,1,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&100,2,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&100,3,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&100,4,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&100,5,&32
        DCB     0
        ALIGN

        DCD     &1C,1,&280,&FA,0,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&FA,1,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&FA,2,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&FA,3,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&FA,4,&32
        DCB     0
        ALIGN
        DCD     &1C,1,&280,&FA,5,&32
        DCB     0
        ALIGN

        DCD     &24,1,&140,&100,0,&32
        DCB     "320 x 256",0
        ALIGN
        DCD     &24,1,&140,&100,1,&32
        DCB     "320 x 256",0
        ALIGN
        DCD     &24,1,&140,&100,2,&32
        DCB     "320 x 256",0
        ALIGN
        DCD     &24,1,&140,&100,3,&32
        DCB     "320 x 256",0
        ALIGN
        DCD     &24,1,&140,&100,4,&32
        DCB     "320 x 256",0
        ALIGN

        DCD     &24,1,&140,&FA,0,&32
        DCB     "320 x 250",0
        ALIGN
        DCD     &24,1,&140,&FA,1,&32
        DCB     "320 x 250",0
        ALIGN
        DCD     &24,1,&140,&FA,2,&32
        DCB     "320 x 250",0
        ALIGN
        DCD     &24,1,&140,&FA,3,&32
        DCB     "320 x 250",0
        ALIGN
        DCD     &24,1,&140,&FA,4,&32
        DCB     "320 x 250",0
        ALIGN

dummy_end
 ]
 ]

        MakeErrorBlock  Modes_CantEnumerate


;---------------------------------------------------------------------------
; Mode_SortList
;
;       Out:    r0 corrupted
;
;       Fill in mode_sortedlist with a sorted list of pointers into
;       mode_table.  The modes are sorted on increasing X, increasing Y,
;       increasing pixel depth then decreasing frame rate.  This call
;       also fills in mode_classlist with pointers to class blocks in
;       mode_sortedlist (ie. block of mode pointers to modes with the
;       same resolution).  This is used to build the resolution menu.
;
Mode_SortList
        ROUT
        ENTRY   "r1-r5"

        Debug   mode,"Mode_SortList"

        LDR     r1, mode_sortedlist
        TEQ     r1, #0
        EXIT    EQ
        Debug   mode," sorted list at",r1

        ASSERT  mode_desc_size = 0
        ASSERT  mode_desc_flags = mode_desc_size + 4
        LDR     r2, mode_table          ; Build list of pointers to valid modes.
        LDR     r3, mode_count
        Debug   mode," table,count =",r2,r3
10
        LDMIA   r2, {r4,lr}             ; Get size and flags.
        AND     lr, lr, #&FF            ; Ignore flag bits 8-31.
        TEQ     lr, #1                  ; If flags are valid then
        STREQ   r2, [r1], #4            ;     use mode.
 [ SortOnPixelShape
        BLEQ    Mode_PixelShape
 ]

        ADD     r2, r2, r4              ; Move on to next mode.
        SUBS    r3, r3, #1              ; Try next mode.
        BNE     %BT10
        STR     r3, [r1]                ; Terminate list with 0.
30
        MOV     lr, #0                  ; Bubble sort list (lr counts the number of swaps).
        LDR     r1, mode_sortedlist
40
        LDMIA   r1, {r2,r3}             ; Get pointers to modes to compare.
        TEQ     r2, #0                  ; If either is 0 then we are at the end of the list.
        TEQNE   r3, #0
        BEQ     %FT70

        ASSERT  mode_desc_yres = mode_desc_xres + 4
        ASSERT  mode_desc_depth = mode_desc_yres + 4
        ASSERT  mode_desc_rate = mode_desc_depth + 4
 [ SortOnPixelShape
        LDR     r4, [r2, #mode_desc_flags]
        LDR     r5, [r3, #mode_desc_flags]
        AND     r4, r4, #flags_squarepixel
        AND     r5, r5, #flags_squarepixel
        TEQ     r4, r5                  ; If both square or both non-square then
        BEQ     %FT45                   ;   do more tests.
        TEQ     r4, #flags_squarepixel  ; else if first is square then second must be non-square so
        BEQ     %FT60                   ;   swap
        B       %FT50                   ; else skip.
45
 ]
        ADD     r2, r2, #mode_desc_xres
        ADD     r3, r3, #mode_desc_xres
        LDR     r4, [r2], #4
        LDR     r5, [r3], #4
        CMP     r5, r4                  ; If next X resolution < this X resolution then
        BCC     %FT60                   ;   swap items.
        BNE     %FT50
        LDR     r4, [r2], #4
        LDR     r5, [r3], #4
        CMP     r5, r4                  ; If next Y resolution < this Y resolution then
        BCC     %FT60                   ;   swap items.
        BNE     %FT50
        LDR     r4, [r2], #4
        LDR     r5, [r3], #4
        CMP     r5, r4                  ; If next pixel depth < this pixel depth then
        BCC     %FT60                   ;   swap items.
        BNE     %FT50
        LDR     r4, [r2], #4
        LDR     r5, [r3], #4
        CMP     r4, r5                  ; If next pixel rate > this pixel rate then
        BCC     %FT60                   ;   swap items
50
        ADD     r1, r1, #4              ; else try next pair.
        B       %BT40
60
        LDMIA   r1, {r2,r3}             ; Swap current pair.
        MOV     r4, r2
        STMIA   r1, {r3,r4}
        ADD     lr, lr, #1
        ADD     r1, r1, #4
        B       %BT40
70
        TEQ     lr, #0                  ; If anything was swapped then
        BNE     %BT30                   ;   do another pass.

        LDR     r0, mode_classlist      ; Now build mode_classlist.
        Debug   mode," class list at",r0
        LDR     r1, mode_sortedlist
        MOV     r2, #-1                 ; Previous X resolution.
        MOV     r3, #-1                 ; Previous Y resolution.
80
        LDR     lr, [r1]                ; Get mode pointer.
        TEQ     lr, #0                  ; If at the end of mode_sortedlist then
        STREQ   lr, [r0]                ;   terminate mode_classlist
 [ SortOnPixelShape
        LDREQ   lr, mode_classlist
        SUBEQ   lr, r0, lr
        MOVEQ   lr, lr, LSR #2
        STREQB  lr, class_count
 ]
        EXIT    EQ                      ;   and exit.

        ASSERT  mode_desc_yres = mode_desc_xres + 4
        ADD     lr, lr, #mode_desc_xres
        LDMIA   lr, {r4,r5}             ; Get X,Y resolution.
        TEQ     r2, r4                  ; If resolution is not the same as previous then
        TEQEQ   r3, r5
        STRNE   r1, [r0], #4            ; Store pointer.
        MOVNE   r2, r4                  ; Current resolution becomes previous.
        MOVNE   r3, r5
        ADD     r1, r1, #4              ; Try next.
        B       %BT80


 [ SortOnPixelShape

;---------------------------------------------------------------------------
; Mode_PixelShape
;
;       In:     r2 -> enumerated mode descriptor
;       Out:    mode descriptor flags updated to reflect pixel shape
;
;       Set mode descriptor flag "flags_squarepixel" if the given mode has
;       square pixels.
;
Mode_PixelShape
        ENTRY   "r0-r3"

        BL      Mode_BuildSpecifier
        MOV     r0, r2
        MOV     r1, #4
        SWI     XOS_ReadModeVariable
        MOV     r3, r2
        MOV     r1, #5
        SWI     XOS_ReadModeVariable
        TEQ     r2, r3
        LDR     r2, [sp, #8]
        LDREQ   lr, [r2, #mode_desc_flags]
        ORREQ   lr, lr, #flags_squarepixel
        STREQ   lr, [r2, #mode_desc_flags]

        EXIT

 ]


;---------------------------------------------------------------------------
; Mode_BuildSpecifier
;
;       In:     r2 -> enumerated mode descriptor
;       Out:    r2 -> mode specifier
;
;       Given the mode descriptor, build a valid mode specifier.
;
Mode_BuildSpecifier
        ENTRY   "r0,r1,r3-r5"

        ASSERT  mode_desc_yres = mode_desc_xres + 4
        ASSERT  mode_desc_depth = mode_desc_yres + 4
        ASSERT  mode_desc_rate = mode_desc_depth + 4
        ADD     r2, r2, #mode_desc_xres
        LDMIA   r2, {r1,r3,r4,r5}       ; Get X,Y,depth and rate.

        ADR     r2, user_data
        MOV     r0, #1                  ; Flags = 1
        MOV     lr, #-1                 ; No mode variables.
        STMIA   r2, {r0,r1,r3,r4,r5,lr}

        EXIT


;---------------------------------------------------------------------------
; Mode_FindSubClass
;
;       In:     r0 = flags
;                       bit     meaning when set
;                       0       downgrade class
;                       1       downgrade colours
;                       2       allow modes which don't appear on menus
;               r3 = selected colours
;               r4 = selected class
;               r6 = selected frame rate (or -1=don't care)
;       Out:    r0 corrupted
;               r2 -> possible subclass (list of pointers to suitable modes)
;               r3 = possible colours
;               r4 = possible class
;               r5 -> possible mode
;
;       Given the selected colour choice and class the mode list is
;       searched for a mode that matches.  If one is not found then the
;       specified choice is downgraded until a match is found or no more
;       downgrading is possible.  If no mode is found then the same process
;       will be applied but stepping up.  If there is still no mode found
;       then an error is returned.  On exit r2 points to a suitable subclass
;       (list of pointers to modes with the same resolution and depth).
;
;       This used to be a nice simple routine but constant demands for changes
;       to the Display Manager's behaviour have turned it into a monster!!
;
Mode_FindSubClass
        ROUT
        ENTRY   "r1,r3,r4,r7-r10"

        Debug   mode,"Mode_FindSubClass ",r3,r4

        MOV     r2, #-1                 ; No subclass yet.
        MOV     r5, #-1                 ; No mode yet.

        TEQ     r3, #&FF                ; If either selection is not supported then
        TEQNE   r4, #&FF
        BEQ     %FT60                   ;   return error.

 [ NewShading
        TST     r0, #4                  ; IF not allowing non-menu modes then check what we're starting from.
        LDREQ   lr, mode_classlist
        ADDEQ   lr, lr, r4, LSL #2
        LDREQ   lr, [lr]                ; Get pointer to class we're starting from.
        LDREQ   lr, [lr]                ; Get pointer to mode descriptor.
        LDREQB  lr, [lr, #mode_desc_name]
        TEQEQ   lr, #0                  ; If the mode we're starting from is not on the menu then
        ORREQ   r0, r0, #4              ;   allow non-menu modes.
 ]

        MOV     r10, #-1                ; Start by stepping down.
10
        ADR     lr, colours_to_log2bpp
        LDRB    r1, [lr, r3]            ; Get the selected pixel depth.

        LDR     r7, mode_classlist
        ADD     r7, r7, r4, LSL #2
        Debug   mode," class list pointer =",r7
        LDMIA   r7, {r7,r8}             ; Get pointers to selected class and next class.
        TEQ     r7, #0                  ; If reached the end of mode_classlist then
        BEQ     %FT60                   ;   return error.
        TEQ     r8, #0
        LDRNE   r8, [r8]                ; Stop when we get to mode r8.
30
        LDR     r9, [r7], #4            ; Get pointer to mode descriptor
        TEQ     r9, r8                  ; If reached the end of this class then
        BEQ     %FT40                   ;   downgrade and try again.

        TST     r0, #4                  ; If not allowing modes not on menus
        LDREQB  lr, [r9, #mode_desc_name]
        TEQEQ   lr, #0                  ;   and this mode is not on the menus then
        BEQ     %BT30                   ;   try next in class.

        LDR     lr, [r9, #mode_desc_depth]      ; Get pixel depth.
        TEQ     r1, lr                  ; If it doesn't match the selected depth then
        BNE     %BT30                   ;   try next in class.

        CMP     r2, #-1                 ; If no subclass yet then
        SUBEQ   r2, r7, #4              ;   this is it.

        CMP     r6, #-1                 ; If no frame rate specified
        LDRNE   lr, [r9, #mode_desc_rate]
        CMPNE   lr, r6                  ;   or frame rate matches then
        MOVEQ	r5, r9			;     return this mode.
        STREQ   r3, [sp, #4]
        STREQ   r4, [sp, #8]
        EXIT    EQ                      ; Exit with V clear.
        B       %BT30                   ; Otherwise, continue search.
40
        TST     r0, #3                  ; If can't downgrade then error.
        BEQ     %FT60

        TST     r0, #2                  ; Decide between class and colours.
        BNE     %FT50

 [ SortOnPixelShape
        SUBS    r4, r4, #1              ; If we fall off the bottom then
        LDRMIB  r4, class_count         ;   start again at the top.
        SUBMI   r4, r4, #1
        LDR     lr, [sp, #8]
        TEQ     r4, lr                  ; If back where we started then
        BEQ     %FT60                   ;   return error.
 |
        ADDS    r4, r4, r10             ; Step class.
        LDRMI   r4, [sp, #8]            ; If gone -ve then restore r4
        MOVMI   r10, #1                 ;   and start stepping up.
        ADDMI   r4, r4, r10
 ]
        B       %BT10                   ; Keep going until we hit the end.
50
        ADD     r3, r3, r10
        TEQ     r3, #mo_co_colour16     ; If it's 16 colours
        TEQNE   r3, #mo_co_grey256      ;   or 256 greys then
        ADDEQ   r3, r3, r10             ;     need an extra step.
        CMP     r3, #0
        LDRLT   r3, [sp, #4]            ; If gone -ve then restore r3
        MOVLT   r10, #1                 ;   and start stepping up.
        BLT     %BT50
        TEQ     r3, #colours_count      ; If not reached the end of the menu then
        BNE     %BT10                   ;   start search.
60
        ADR     r0, ErrorBlock_Modes_Unsupported
        BL      MsgTrans_ErrorLookup
        EXIT

colours_to_log2bpp
        DCB     0,1,2,2,3,3,4,5
        ALIGN

        MakeErrorBlock  Modes_Unsupported


;---------------------------------------------------------------------------
; Mode_ChangeMode
;
;       Out:    r0 corrupted
;
;       Change to the mode which has been selected.
;
Mode_ChangeMode
        ROUT
        ENTRY   "r1-r6"

        Debug   mode,"Mode_ChangeMode"

        LDR     r2, selected_mode
        CMP     r2, #-1                 ; If not enumerated then
        BEQ     %FT20                   ;   try our best.

        LDRB    r3, selected_colours
10
        TEQNE   r3, #mo_co_grey16
        TEQNE   r3, #mo_co_grey256
        MOVEQ   r1, #f_greylevel
        MOVNE   r1, #0
        BL      Mode_BuildSpecifier
        BLVC    Mode_SetModeString
        BLVC    Mode_WimpCommand
        EXIT
20
        MOV     r0, #4                  ; Don't downgrade, allow non-menu modes.
        LDRB    r3, selected_colours
        LDRB    r4, selected_class
        MOV     r6, #-1                 ; Don't care about frame rate.
        BL      Mode_FindSubClass
        CMP     r5, #-1                 ; If we found something then
        MOVNE   r2, r5                  ;   use it!
        BNE     %BT10

        ADR     r0, ErrorBlock_Modes_Unsupported
        BL      MsgTrans_ErrorLookup
        EXIT


 [ LoadModeFiles

;---------------------------------------------------------------------------
; Mode_LoadFile
;
;       In:     r1 -> name of file to load
;       Out:    r0 corrupted
;
;       Load the named mode file using *LoadModeFile.
;
Mode_LoadFile
        ENTRY   "r1,r2"

        Debug   mode,"Mode_LoadFile"

        ADR     r0, command_loadmodefile
        ADR     r1, user_data
        MOV     r2, #user_data_size
        BL      Mod_CopyString

        LDR     r0, [sp]
        BL      Mod_CopyString

        ADR     r0, user_data
 [ debugmode :LOR: {FALSE}
        SUB     r0, r0, #4
        MOV     r1, #1
        BL      Mod_ReportError
        ADD     r0, r0, #4
 ]
        SWI     XOS_CLI

        EXIT

command_loadmodefile    DCB     "LoadModeFile ",0
                        ALIGN
 ]


        END