; 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", GVPixelFormat_Size

        Debug   mode,"Mode_Init"

        BL      Mode_GetCurrent
        MOV     r5, sp
        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, #ScreenModeReason_ReturnMode
        SWI     XOS_ScreenMode
        MOV     r2, r1                  ; r2=mode specifier (number or pointer)
 |
        MOV     r0, #135
        SWI     XOS_Byte
 ]

        EXIT


;---------------------------------------------------------------------------
; Mode_PixelFormatFromLog2BPP
;
;       In:     r4 = Log2BPP
;               r5 -> pixel format block
;       Out:    r5 filled in
;
Mode_PixelFormatFromLog2BPP ROUT
        Entry   "r4"
        STR     r4, [r5, #GVPixelFormat_Log2BPP]
        CMP     r4, #3
        MOVEQ   lr, #ModeFlag_FullPalette
        MOVNE   lr, #0
        STR     lr, [r5, #GVPixelFormat_ModeFlags]
        MOV     lr, #1
        MOV     r4, lr, LSL r4          ; log2bpp -> bpp
        RSB     r4, lr, lr, LSL r4      ; bpp -> ncolour
        STR     r4, [r5, #GVPixelFormat_NColour]
        EXIT

;---------------------------------------------------------------------------
; Mode_IsOldPixelFormat
;
;       In:     r1 -> pixel format block
;       Out:    EQ if can be represented as pixel depth
;
Mode_IsOldPixelFormat ROUT
        Entry   "r0,r2-r3"
        ASSERT  GVPixelFormat_NColour = 0
        ASSERT  GVPixelFormat_ModeFlags = 4
        ASSERT  GVPixelFormat_Log2BPP = 8
        LDMIA   r1, {r0,r2-r3} ; Get ncolour, modeflags, log2bpp
        CMP     r3, #5
        EXIT    HI
        CMP     r3, #3
        MOVEQ   lr, #ModeFlag_FullPalette
        MOVNE   lr, #0
        CMP     r2, lr
        EXIT    NE
        MOV     r2, #1
        MOV     r3, r2, LSL r3
        RSB     r3, r2, r2, LSL r3
        CMP     r0, r3
        EXIT

;---------------------------------------------------------------------------
; Mode_GetInfo
;
;       In:     r2 = mode specifier
;               r5 -> pixel format block
;       Out:    r0 corrupted
;               r3 = X resolution
;               r4 = Y resolution
;               r5 filled in
;               r6 = frame rate (or -1 = unknown).
;
;       Get information about given mode.
;
Mode_GetInfo    ROUT
        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
        MOV     r0, r2
        CMP     r0, #256
        BCC     %FT10
        LDR     r4, [r0, #mode_spec_depth]
        BL      Mode_PixelFormatFromLog2BPP
        ; Scan mode variable list
        ADD     r3, r0, #mode_spec_vars
05
        LDR     r4, [r3], #4
        CMP     r4, #-1
        BEQ     %FT09
        LDR     r6, [r3], #4
        CMP     r4, #VduExt_NColour
        STREQ   r6, [r5, #GVPixelFormat_NColour]
        CMP     r4, #VduExt_ModeFlags
        LDREQ   r4, =ModeFlag_FullPalette :OR: ModeFlag_64k :OR: ModeFlag_ChromaSubsampleMode :OR: ModeFlag_DataFormat_Mask
        ANDEQ   r6, r6, r4
        STREQ   r6, [r5, #GVPixelFormat_ModeFlags]
        B       %BT05
09

        LDMIB   r0, {r3-r4}             ; get X,Y
        LDR     r6, [r0, #mode_spec_rate]
        EXIT

10
        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
        STRVC   r2, [r5, #GVPixelFormat_Log2BPP]

        MOVVC   r1, #VduExt_NColour
        SWIVC   XOS_ReadModeVariable
        STRVC   r2, [r5, #GVPixelFormat_NColour]

        MOVVC   r1, #VduExt_ModeFlags
        SWIVC   XOS_ReadModeVariable
        LDRVC   r6, =ModeFlag_FullPalette :OR: ModeFlag_64k :OR: ModeFlag_ChromaSubsampleMode :OR: ModeFlag_DataFormat_Mask
        ANDVC   r2, r2, r6
        STRVC   r2, [r5, #GVPixelFormat_ModeFlags]

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


;---------------------------------------------------------------------------
; Mode_TestPalette
;
;       In:     r5 -> pixel format
;       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"

        LDR     r9, [r5, #GVPixelFormat_Log2BPP]
        Debug   mode,"Mode_TestPalette: log2bpp =",r9

        LDRB    r8, flags
        CMP     r9, #3
        BICHI   r1, r8, #f_greylevel    ; Assume high colour displays aren't greyscale
        STRHIB  r1, flags
        EXIT    HI

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

        MOV     r3, #1
        MOV     r1, r3, LSL r9          ; 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.
      [ No32bitCode
        Push    "r0,lr"
      |
        Push    "r0,r4,lr"
        SavePSR r4
      ]
        ADR     lr, user_data
        TEQ     r2, lr                  ; If it's not the user area then free it.
        MOVNE   r0, #ModHandReason_Free
        SWINE   XOS_Module
      [ No32bitCode
        Pull    "r0,pc",,^
      |
        RestPSR r4,,f                   ; Preserve error if there is one.
        Pull    "r0,r4,pc"
      ]


;---------------------------------------------------------------------------
; 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 format
;               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_desc0_yres = mode_desc0_xres + 4
        ASSERT  mode_desc1_xres = mode_desc0_xres
        ADD     lr, lr, #mode_desc0_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
        MOV     r0, r1
        BL      Mode_PixelFormatToMenuItem
        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


;---------------------------------------------------------------------------
; Mode_PixelFormatToMenuItem
;
;       In:     r0 = flags
;                       bit     meaning when set
;                       0       mode uses grey levels not colour
;                       1-31    reserved
;               r5 -> pixel format
;       Out:    r0 corrupted
;               r3 = menu item
;
;       Set the colour and resolution icons to the current selection.
;
Mode_PixelFormatToMenuItem
        ROUT
        Entry
        TST     r0, #f_greylevel
        LDR     lr, [r5, #GVPixelFormat_Log2BPP]
        ADREQ   r0, log2bpp_to_colouritem
        ADRNE   r0, log2bpp_to_greyitem
        LDRB    r3, [r0, lr]            ; Get colour selection.
        TEQEQ   lr, #4                  ; 16bpp colour needs extra logic
        EXIT    NE
        LDR     lr, [r5, #GVPixelFormat_NColour]
        ADD     lr, lr, #1
        CMP     lr, #4096
        MOVEQ   r3, #mo_co_4K
        EXIT    EQ
        LDR     lr, [r5, #GVPixelFormat_ModeFlags]
        TST     lr, #ModeFlag_64k
        MOVNE   r3, #mo_co_64K
        EXIT

log2bpp_to_greyitem     DCB     mo_co_mono,mo_co_grey4,mo_co_grey16,mo_co_grey256,mo_co_32K,mo_co_16M,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,mo_co_16M

                        ALIGN

;---------------------------------------------------------------------------
; Mode_DescriptorToColourMenuItem
;
;       In:     r0 = flags
;                       bit     meaning when set
;                       0       mode uses grey levels not colour
;                       1-31    reserved
;               r6 -> mode descriptor
;       Out:    r5 = colour menu item
;
Mode_DescriptorToColourMenuItem
        ROUT
        Entry   "r0,r3,r4", GVPixelFormat_Size
        LDR     lr, [r6, #mode_desc_flags]
        TST     lr, #2
        ADDNE   r5, r6, #mode_desc1_format
        BNE     %FT30

        LDR     r4, [r6, #mode_desc0_depth]
        MOV     r5, sp
        BL      Mode_PixelFormatFromLog2BPP
30
        BL      Mode_PixelFormatToMenuItem
        MOV     r5, r3
        EXIT

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

        LDR     r0, [r2, #mode_desc_flags]
        TST     r0, #2
        ADDEQ   r0, r2, #mode_desc0_name ; Copy mode string.
        ADDNE   r0, r2, #mode_desc1_name

        LDRB    lr, [r0]
        TEQ     lr, #0                  ; If we have a blank mode name then
        ASSERT  mode_desc0_xres = mode_desc1_xres
        ADDEQ   lr, r2, #mode_desc0_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
        BEQ     %FT25
        LDR     r0, [lr, #mode_desc_flags]
        TST     r0, #2
        LDREQ   r0, [lr, #mode_desc0_rate]      ;   else use rate from mode descriptor.
        LDRNE   r0, [lr, #mode_desc1_rate]

25
 [ 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
 ]
xres                    DCB     "X",0
yres                    DCB     " Y",0
greys                   DCB     " G",0
colours                 DCB     " C",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-r3"

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

        CMP     r2, #256
        BCC     %FT10                   ; If it's a mode number then just print it
        MOV     r0, #ScreenModeReason_ModeSpecifierToString
        MOV     r1, r2
        LDR     r2, mode_indirect
        LDR     r3, mode_size
        SWI     XOS_ScreenMode
        ; Swallow any error/overflow and ensure string is terminated
        MOVVS   r3, #-1
        CMP     r3, #0
        MOVLT   r0, #0
        STRLTB  r0, [r2]
        EXIT
10
        MOV     r0, r2
        LDR     r1, mode_indirect
        LDR     r2, mode_size
        SWI     XOS_ConvertCardinal4
        EXIT

;---------------------------------------------------------------------------
; 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, #ScreenModeReason_EnumerateModes ; 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, #ScreenModeReason_EnumerateModes
        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_ComparePixelFormatsAndFramerates
;
;       In:     r2 -> mode descriptor 1
;               r3 -> mode descriptor 2
;       Out:    CC = descriptor 2 should appear first
;               NE = descriptor 1 should appear first
;               EQ = both identical
Mode_ComparePixelFormatsAndFramerates
        ROUT
        Entry   "r0-r1,r4-r7", GVPixelFormat_Size*2
        ; Get pixel formats
        ASSERT  mode_desc0_depth = mode_desc1_format
        ASSERT  mode_desc0_rate = mode_desc0_depth+?mode_desc0_depth
        ASSERT  mode_desc1_rate = mode_desc1_format+?mode_desc1_format

        LDR     r0, [r2, #mode_desc_flags]
        MOV     r5, sp
        ADD     r2, r2, #mode_desc1_format
        TST     r0, #2
        LDMIA   r2!, {r4,r6-r7} ; Get pixel format, or just the depth
        STMNEIA r5, {r4,r6-r7}  ; Copy as-is if pixel format
        SUBEQ   r2, r2, #?mode_desc1_format-?mode_desc0_depth ; Leave r2 -> rate
        BLEQ    Mode_PixelFormatFromLog2BPP ; Convert if depth

        LDR     r0, [r3, #mode_desc_flags]
        ADD     r5, sp, #GVPixelFormat_Size
        ADD     r3, r3, #mode_desc1_format
        TST     r0, #2
        LDMIA   r3!, {r4,r6-r7}
        STMNEIA r5, {r4,r6-r7}
        SUBEQ   r3, r3, #?mode_desc1_format-?mode_desc0_depth
        BLEQ    Mode_PixelFormatFromLog2BPP

        ; Compare formats.
        ; First, we sort by the basic pixel formats available from the menu
        ; Then we sort by framerate
        ; Then, we'll sort by expected software compatibility:
      [ Allow24bpp
        ;   32bpp -> 24bpp
      ]
        ;   TBGR -> TRGB -> ABGR -> ARGB
        ; This should ensure that for a given colour depth & framerate, the
        ; most compatible mode is the one that will be selected on mode changes
        Push    "r3"
        MOV     r0, #0
        BL      Mode_PixelFormatToMenuItem
        MOV     r4, r3
        ADD     r5, sp, #4
        MOV     r0, #0
        BL      Mode_PixelFormatToMenuItem
        CMP     r4, r3
        Pull    "r3"
        EXIT    NE

        ; Sort by framerate
        LDR     r4, [r2], #4
        LDR     r5, [r3], #4
        CMP     r4, r5
        EXIT    NE

      [ Allow24bpp
        ; Handle 32bpp/24bpp case
        LDR     r0, [sp, #GVPixelFormat_Log2BPP]
        LDR     r1, [sp, #GVPixelFormat_Log2BPP+GVPixelFormat_Size]
        CMP     r1, r0
        EXIT    NE
      ]
        ; Handle transparency & RGB ordering
        ASSERT  ModeFlag_DataFormatSub_RGB < ModeFlag_DataFormatSub_Alpha ; Use &RGB order before alpha
        LDR     r0, [sp, #GVPixelFormat_ModeFlags]
        LDR     r1, [sp, #GVPixelFormat_ModeFlags+GVPixelFormat_Size]
        AND     r0, r0, #ModeFlag_DataFormatSub_Mask
        AND     r1, r1, #ModeFlag_DataFormatSub_Mask
        CMP     r1, r0
        EXIT

;---------------------------------------------------------------------------
; Mode_CheckIdentical
;
;       In:     r2 -> mode descriptor 1
;               r3 -> mode descriptor 2
;       Out:    EQ = both are identical apart from RGB/BGR order or alpha mode
;               NE = both are different
Mode_CheckIdentical
        ROUT
        Entry   "r0-r7", GVPixelFormat_Size*2

      [ SortOnPixelShape
        ; Check square pixel flag
        LDR     r0, [r2, #mode_desc_flags]
        LDR     r1, [r3, #mode_desc_flags]
        ASSERT  flags_squarepixel = 1:SHL:31
        TEQ     r0, r1
        EXIT    MI
      ]

        ; Compare width & height
        ASSERT  mode_desc0_xres = mode_desc1_xres
        ASSERT  mode_desc0_yres = mode_desc1_yres
        LDR     r0, [r2, #mode_desc0_xres]
        LDR     r1, [r2, #mode_desc0_yres]
        LDR     r4, [r3, #mode_desc0_xres]
        LDR     r5, [r3, #mode_desc0_yres]
        CMP     r0, r4
        CMPEQ   r1, r5
        EXIT    NE

        ; Get pixel formats
        ASSERT  mode_desc0_depth = mode_desc1_format
        ASSERT  mode_desc0_rate = mode_desc0_depth+?mode_desc0_depth
        ASSERT  mode_desc1_rate = mode_desc1_format+?mode_desc1_format

        LDR     r0, [r2, #mode_desc_flags]
        MOV     r5, sp
        ADD     r2, r2, #mode_desc1_format
        TST     r0, #2
        LDMIA   r2!, {r4,r6-r7} ; Get pixel format, or just the depth
        STMNEIA r5, {r4,r6-r7}  ; Copy as-is if pixel format
        SUBEQ   r2, r2, #?mode_desc1_format-?mode_desc0_depth ; Leave r2 -> rate
        BLEQ    Mode_PixelFormatFromLog2BPP ; Convert if depth

        LDR     r0, [r3, #mode_desc_flags]
        ADD     r5, sp, #GVPixelFormat_Size
        ADD     r3, r3, #mode_desc1_format
        TST     r0, #2
        LDMIA   r3!, {r4,r6-r7}
        STMNEIA r5, {r4,r6-r7}
        SUBEQ   r3, r3, #?mode_desc1_format-?mode_desc0_depth
        BLEQ    Mode_PixelFormatFromLog2BPP

        ; Check if the pixel formats decode to the same item in the colours menu
        Push    "r3"
        MOV     r0, #0
        BL      Mode_PixelFormatToMenuItem
        MOV     r4, r3
        ADD     r5, sp, #4
        MOV     r0, #0
        BL      Mode_PixelFormatToMenuItem
        CMP     r4, r3
        Pull    "r3"
        EXIT    NE

        ; Compare framerate
        LDR     r4, [r2], #4
        LDR     r5, [r3], #4
        CMP     r4, r5
        EXIT    NE

        ; Compare mode name
10
        LDRB    r4, [r2], #1
        LDRB    r5, [r3], #1
        CMP     r4, r5
        EXIT    NE
        CMP     r4, #0
        BNE     %BT10

        EXIT


;---------------------------------------------------------------------------
; 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-r6"

        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
        BEQ     %FT28                   ;     use mode
        TEQ     lr, #3                  ; If new format, must skip unwanted pixel formats (e.g. YUV). TODO - Use a whitelist of accepted formats?
        LDR     lr, [r2, #mode_desc1_format+GVPixelFormat_Log2BPP]
      [ Allow24bpp
        CMP     lr, #6                  ; Log2BPP 7+ is YUV or completely unknown
      |
        CMP     lr, #5                  ; Log2BPP 6+ is 24bpp packed, YUV, or completely unknown
      ]
        BHI     %FT29
        LDR     lr, [r2, #mode_desc1_format+GVPixelFormat_ModeFlags]
        ASSERT  ModeFlag_DataFormatFamily_RGB = 0
        TST     lr, #ModeFlag_DataFormatFamily_Mask ; Must be RGB family
        BNE     %FT29

28
        STR     r2, [r1], #4            ;     use mode.
 [ SortOnPixelShape
        BL      Mode_PixelShape
 ]

29
        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     r6, #0                  ; Bubble sort list (r6 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_desc0_yres = mode_desc0_xres + 4
        ASSERT  mode_desc1_xres = mode_desc0_xres
        ASSERT  mode_desc1_yres = mode_desc0_yres
 [ 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_desc0_xres
        ADD     r3, r3, #mode_desc0_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], #-mode_desc0_yres
        LDR     r5, [r3], #-mode_desc0_yres
        CMP     r5, r4                  ; If next Y resolution < this Y resolution then
        BCC     %FT60                   ;   swap items.
        BNE     %FT50
        BL      Mode_ComparePixelFormatsAndFramerates
        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     r6, r6, #1
        ADD     r1, r1, #4
        B       %BT40
70
        TEQ     r6, #0                  ; If anything was swapped then
        BNE     %BT30                   ;   do another pass.

        ; Go through and discard any modes which are identical to their
        ; predecessor, except for RGB/BGR ordering & alpha mode
        ; This avoids the framerate menu showing lots of entries which appear
        ; to be identical from the user's POV
        LDR     r0, mode_sortedlist
        ADD     r1, r0, #4
        LDR     r2, [r0]
        CMP     r2, #0
        BEQ     %FT72
71
        LDR     r3, [r1], #4
        CMP     r3, #0
        STREQ   r3, [r0, #4]
        BEQ     %FT72
        BL      Mode_CheckIdentical
        STRNE   r3, [r0, #4]!
        MOVNE   r2, r3
        B       %BT71
72

        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_desc0_yres = mode_desc0_xres + 4
        ASSERT  mode_desc1_xres = mode_desc0_xres
        ADD     lr, lr, #mode_desc0_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"

        MOV     r1, #0
        BL      Mode_BuildSpecifier
        MOV     r0, r2
        MOV     r1, #VduExt_XEigFactor
        SWI     XOS_ReadModeVariable
        MOV     r3, r2
        MOV     r1, #VduExt_YEigFactor
        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:     r1 = flags
;                       bit     meaning when set
;                       0       mode uses grey levels not colour
;                       1-31    reserved
;               r2 -> enumerated mode descriptor
;       Out:    r2 -> mode specifier
;
;       Given the mode descriptor, build a valid mode specifier.
;
Mode_BuildSpecifier
        Entry   "r0-r1,r3-r6", GVPixelFormat_Size

        ASSERT  mode_desc0_xres = mode_desc_flags + 4
        ASSERT  mode_desc0_yres = mode_desc0_xres + 4
        ASSERT  mode_desc0_depth = mode_desc0_yres + 4
        ASSERT  mode_desc1_xres = mode_desc0_xres
        ADD     r2, r2, #mode_desc_flags
        LDMIA   r2!, {r0,r1,r3,r4}      ; Get flags,X,Y & depth (for old format)
        TST     r0, #2                  ; New or old format?
        MOV     r0, #1                  ; specifier flags = 1
        ADR     r6, user_data
        ASSERT  mode_spec_flags = 0
        ASSERT  mode_spec_xres = 4
        ASSERT  mode_spec_yres = 8
        STMIA   r6!, {r0,r1,r3}
        MOVEQ   r5, sp
        SUBNE   r5, r2, #4
        ADDNE   r2, r2, #8
        BLEQ    Mode_PixelFormatFromLog2BPP

        ASSERT  GVPixelFormat_NColour = 0
        ASSERT  GVPixelFormat_ModeFlags = 4
        ASSERT  GVPixelFormat_Log2BPP = 8
        LDMIA   r5, {r0,r3,r4}
        LDR     r5, [r2]                ; Grab framerate
        ASSERT  mode_spec_depth = 12
        ASSERT  mode_spec_rate = 16
        ASSERT  mode_spec_vars = 20
        MOV     lr, #VduExt_NColour
        STMIA   r6!, {r4,r5,lr}         ; depth, rate, NColour index
        ; Check if we need to set the greyscale flag
        FRAMLDR r1
        TST     r1, #f_greylevel
        ORRNE   r3, r3, #ModeFlag_GreyscalePalette
        MOV     r2, #VduExt_ModeFlags
        MOV     r4, #-1
        STMIA   r6, {r0,r2-r3,r4}       ; NColour value, ModeFlags pair, terminator
        ADR     r2, user_data
        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 menu item
;               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-r11"

        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.

        TST     r0, #4                  ; IF not allowing non-menu modes then check what we're starting from.
        BNE     %FT09
        LDR     lr, mode_classlist
        ADD     lr, lr, r4, LSL #2
        LDR     lr, [lr]                ; Get pointer to class we're starting from.
        LDR     lr, [lr]                ; Get pointer to mode descriptor.
        LDR     r10, [lr, #mode_desc_flags]
        TST     r10, #2
        LDREQB  lr, [lr, #mode_desc0_name]
        LDRNEB  lr, [lr, #mode_desc1_name]
        TEQ     lr, #0                  ; If the mode we're starting from is not on the menu then
        ORR     r0, r0, #4              ;   allow non-menu modes.

09
        MOV     r10, #-1                ; Start by stepping down.
10
        ADR     lr, colours_to_pixelformat
        ASSERT  GVPixelFormat_Size = 12
        ADD     lr, lr, r3, LSL #3
        ADD     r1, lr, r3, LSL #2      ; Get the selected pixel format.

        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.

        LDR     lr, [r9, #mode_desc_flags]
        TST     lr, #2
        BNE     %FT35

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

        BL      Mode_IsOldPixelFormat   ; If this isn't an old pixel format
        BNE     %BT30                   ;   try next in class.

        LDR     lr, [r9, #mode_desc0_depth]      ; Get pixel depth.
        LDR     r11, [r1, #GVPixelFormat_Log2BPP]
        TEQ     r11, 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_desc0_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.

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

        ; Fuzzy matching: We don't require the transparency mode or RGB order to match, as currently there's no way for the user to select those from the menus
      [ Allow24bpp
        ; We also need to deal with 24bpp vs. 32bpp packing
      ]
        LDR     lr, [r9, #mode_desc1_format+GVPixelFormat_NColour]
        LDR     r11, [r1, #GVPixelFormat_NColour]
        TEQ     r11, lr
      [ Allow24bpp
        ORRNE   lr, lr, lr, LSL #24 ; try converting 24bpp NColour to 32bpp
        TEQNE   r11, lr
      ]
        BNE     %BT30
        LDR     lr, [r9, #mode_desc1_format+GVPixelFormat_ModeFlags]
        LDR     r11, [r1, #GVPixelFormat_ModeFlags]
        EOR     lr, lr, r11
        TST     lr, #ModeFlag_FullPalette :OR: ModeFlag_64k
        BNE     %BT30
        LDR     lr, [r9, #mode_desc1_format+GVPixelFormat_Log2BPP]
        LDR     r11, [r1, #GVPixelFormat_Log2BPP]
      [ Allow24bpp
        ; If descriptor has log2bpp of 6, downgrade it to 5
        CMP     lr, #6
        MOVEQ   lr, #5
      ]
        TEQ     r11, lr
        BNE     %BT30

        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_desc1_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_pixelformat
        ASSERT  GVPixelFormat_NColour = 0
        ASSERT  GVPixelFormat_ModeFlags = 4
        ASSERT  GVPixelFormat_Log2BPP = 8
        ASSERT  GVPixelFormat_Size = 12
        DCD     1, 0, 0 ; mo_co_mono
        DCD     3, 0, 1 ; mo_co_grey4
        DCD     15, 0, 2 ; mo_co_grey16
        DCD     15, 0, 2 ; mo_co_colour16
        DCD     255, ModeFlag_FullPalette, 3 ; mo_co_grey256
        DCD     255, ModeFlag_FullPalette, 3 ; mo_co_colour256
        DCD     4095, 0, 4 ; mo_co_4K
        DCD     65535, 0, 4 ; mo_co_32K
        DCD     65535, ModeFlag_64k, 4 ; mo_co_64K
        DCD     -1, 0, 5 ; mo_co_16M

        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