SDFS::RISCOS.$.Coding.Projects.ResFinder.s.ResFinder

; ResFinder
; =========
;
; by Rick Murray
;   Version: 0.07                            < update also embedded version
;   Date   : Saturday, 15th February 2014    < string at the end of the code
;   Started: Friday, 24th January 2014
;
;
; Replacement for basic "ResFind" (apparently not related to the !ResFind app!?)
; as that chokes if the country is "Default". Plus, we could do more. :-)
;
;
; We expect to be called as follows:
;    ResFinder <appname> [<respath>] [-useos]
; where <appname> is the name of the callee application, and <respath> (if specified) is the
; custom system variable to set to point to the resources.
; If <respath> is not specified, then the path will be "<appname>$Path".
; NOTE that this means <App$Dir> and <App$Path> may point to different places, however it
; does mean that you can load resources by using something like App:Templates or
; App:Messages and the appropriate one will be loaded.
; The "-useos" option overrides asking the Territory for the machine language and asks the
; OS. This allows for a different territory and country to be set up, such as RISC OS in
; English (Territory=UK) and applications to prefer German (Country=Gemany), for example.
;
;
; Aplication name approved by Ben Avison on 26th January 2014. While this is not an
; application in the traditional sense (no !directory etc), it allows us to create
; <ResFinder$SomethingHere> system variables.
;
;
; LICENCE:
;
;   ResFinder is Copyright © 2014 Richard Murray
;   Licensed under the EUPL v1.1 only.
;
;
;   EXECUTABLE:  The EXECUTABLE may be freely used without undue restrictions; as laid out
;                in the EUPL licence (link below). Your use and distribution of this
;                EXECUTABLE will be in accorance with the licence.
;
;                One specific note, for clarity, is that the open source licence applies
;                specifically and only to this EXECUTABLE - and any request for source code
;                under the licence will cover ONLY this EXECUTABLE and not any part of that
;                which this EXECUTABLE is used alongside; thus this open source executable
;                may be used in a closed-source commercial package provided that the various
;                terms of the licence are respected (ie, provide source to ResFinder if you
;                modify it, or point people to my website if you don't, etc etc).
;
;   SOURCE CODE: The EUPL v1.1 applies. There should be a PDF describing the EUPL in the
;                archive that you downloaded. Alternatively, you can download the EUPL v1.1
;                in all major European languages from here:
;                  http://ec.europa.eu/idabc/eupl.html   (all have the same legal weight)
;
;
; ----display-width-is-92-characters--------------------------------------------------------



; Our inclusions
        GET      ^.h.EQUx                    ; EQUB, EQUD, EQUSZ, EQUSZA...
        GET      ^.h.SWINamesMini            ; SWI definitions
        GET      ^.h.ResFinder               ; no magic numbers


; Define as '1' in order to build a talkative version
TRACE * 0

 [ TRACE = 1
        MACRO
        TraWrS $one, $two, $var
        ; TraWrS = TRAceWRiteString
        ; Call like:         TraWrS 'A', 'B', workspace
        ; which will print:  AB=<whatever is in workspace>
        STMFD    R13!, {R0-R2}               ; better safe than sorry
        SWI      256+$one
        SWI      256+$two
        SWI      256+'='
        [ "$var"<>"R11"                      ; If <$var> is NOT "R11"
        ADRL     R0, $var                    ; then it's a pointer
        |
        MOV      R0, R11                     ; otherwise, use R11!
        ]
        SWI      OS_Write0
        SWI      OS_NewLine
        LDMFD    R13!, {R0-R2}
        MEND
 ]


; ===========
; Here we go!
; ===========


        ; Within this code:
        ;
        ;   R0 - R5  - working registers, make no assumptions.
        ;        R6  - pointer to end of <appname>, for "$Path" suffix if no custom respath.
        ;            - later used to hold the "platform class" that is detected.
        ;              [->"theextrastuff"]
        ;        R7  - pointer to test paths period prior to territory name being appended.
        ;            - is later used to hold the CPUID. [->"theextrastuff"]
        ;        R8  - is &7FFFFFFF if "-useos" specified, else &00000000.
        ;            - is later used for alphabet version for UTF-8. [->"country_to_name"]
        ;            - is later used by call to HAL via OS_Hardware. [->"theextrastuff"]
        ;        R9  - flag set to indicate that we have a custom resources path (=1 or =0).
        ;       R10  - flag set to indicate custom resources path ends with "$Path" (=1/=0).
        ;       R11  - pointer to composite path (is altered by bytecopy routine).
        ;       R12  - number of current country/territory to skip adding "UK" twice.
        ;       R13  - stack
        ;       R14  - return address (various)
        ; <terrname> - text of country name of current config ("UK", "France", etc)
        ; <fallback> - text of 'fallback' country name (if ResFinder$Fallback is defined).
        ; <progname> - name of caller app (CLI param #1) is copied here.
        ; <respath>  - custom resources path (CLI param #2) is copied here, if specified.
        ; <workspace>- a wodge of memory to use as... gee... workspace, perhaps?
        ; <wrksp>+256- where the composite path is constructed (re. R11)
        ;
        ; Anybody who modifies this code is absolutely REQUIRED to keep the above up to
        ; date, under penalty of being shot repeatedly by high velocity baked beans, at
        ; least as many as are in a standard Heinz can.



        AREA     |ResFinder$Code|, CODE, PIC, A32bit
        ENTRY

entry_point
 [ TRACE = 1
        SWI      256+'['
        SWI      OS_NewLine
 ]
        ; Read end of workspace and command line
        SWI      OS_GetEnv
        MOV      R13, R1                     ; RAM limit is a tidy place to put our stack
        STR      R14, [R13, #-4]!


        ; Process command string [OS_ReadArgs makes life easier]
        MOV      R1, R0
        ADRL     R0, argparams
        ADRL     R2, workspace               ; far away at end of program, needs ADRL
        MOV      R3, #BUFFERSIZE             ; (as do a fair few of the other ADRs)
        SWI      OS_ReadArgs


        ; Set a pointer to our composite path
        ADRL     R0, workspace
        ADD      R11, R0, #256              ; at <workspace>+256 bytes


        ; Remember the "-useos" setting
        LDR      R8, [R2, #ARGPTR_USEOS]


        ; Extract program name
        LDR      R0, [R2, #ARGPTR_PROGNAME]
        CMP      R0, #0
        BLE      syntax_error

        ADRL     R1, progname
        BL       bytecopy                    ; R0 = src, R1 = dst (returned updated)
        MOV      R6, R1                      ; preserve pointer for later
 [ TRACE = 1
        TraWrS   'P', 'N', progname          ; Program Name
 ]

        ; In the copy above and below, there is NO bounds checking as the buffer size given
        ; to OS_ReadArgs is 128 bytes, both of these are 128 bytes, so cannot overrun.


        ; Extract the resources variable, if it exists
        LDR      R0, [R2, #ARGPTR_RESVAR]
        CMP      R0, #0
        MOVLE    R9, #NO_CUSTOM_RESVAR       ; flag no custom resource variable
        BLE      no_resources

        MOV      R9, #CUSTOM_RESVAR          ; flag a custom resource variable
        MOV      R10, #CUSTOM_RESVAR_NOPATH  ; flag not-a-path by default
        ADRL     R1, respath
        BL       bytecopy
 [ TRACE = 1
        TraWrS   'S', 'N', respath           ; Sysvar Name
 ]


        ; Now we need to see if the resources variable ends with "$Path".
        SUB      R1, R1, #6                  ; Back to where '$' would be ($ P a t h <n>)
        LDRB     R0, [R1], #1
        CMP      R0, #'$'                    ; Check there's a $ there
        BNE      no_resources

        MOV      R2, #0                      ; Read the next four bytes into a register
        LDRB     R0, [R1], #1
        ORR      R2, R2, R0
        LDRB     R0, [R1], #1
        ORR      R2, R2, R0, LSL#8
        LDRB     R0, [R1], #1
        ORR      R2, R2, R0, LSL#16
        LDRB     R0, [R1], #1
        ORR      R2, R2, R0, LSL#24
        LDR      R1, lowercase               ; Make it lowercase (assumes plain ASCII)
        ORR      R2, R2, R1                  ; [but, then, anything else won't be "path"!]
        LDR      R1, pathtext
        CMP      R2, R1
        MOVEQ    R10, #CUSTOM_RESVAR_ISPATH


no_resources
        ; At this point, <progname> will contain the name of the program, and <respath> may
        ; contain the name of the resources variable to set (or null).
        ; If there is a resources variable, then that will be set to point to the given
        ; string to call the variable.
        ; If there is NO custom resources variable, then the default will be
        ; "<[progname]$Path>".


        ; Now, to determine the language.
        CMP      R8, #ARG_ISSET              ; "-useos" set?
        BNE      use_territory               ; if NOT, go to ask Territory module
        ; else fall through to ask the OS

        MOV      R0, #OSB_RW_COUNTRY         ; Read country number
        MOV      R1, #JUST_READ_COUNTRY
        SWI      OS_Byte
        CMP      R1, #IS_DEFAULT             ; was it "Default"?
        MOVNE    R0, R1
        BNE      country_to_name

        MOV      R0, #OSB_RW_KEYALPHA        ; Read keyboard
        MOV      R1, #JUST_READ_KEYBOARD     ; (shonky last ditch attempt)
        SWI      OS_Byte

        CMP      R1, #IS_DEFAULT             ; sanity check it!
        MOVEQ    R1, #IS_UK                  ; (4=UK)
        MOV      R0, R1                      ; we need this to be in R0

        CMP      R0, R0                      ; set an EQ state to skip following SWI
use_territory                                ; and save having to have a branch. :-)
        SWINE    Territory_Number

country_to_name
        MOV      R12, R0                     ; keep a copy for later
        ADRL     R1, terrname
        MOV      R2, #16                     ; buffer length
        SWI      Territory_NumberToName
 [ TRACE = 1
        TraWrS   'T', 'N', terrname          ; Territory Name
 ]

        ;; <aside>
        ;; We could alternatively use Service_International (&43) with R2=2, R3=number,
        ;; R4=<buffer>, and R5=<bufflen>.
        ;; Bizarrely, both methods seem to use *different* data sources. The Territory
        ;; Manager calls MessageTrans to parse a message comprised of "T"+"<$num>", you can
        ;; see the list at Resources:$.Resources.TerrMgr.Messages; while the ServiceCall
        ;; method uses a lookup table baked into the International module (which is at the
        ;; end of <devpath>.castle.RiscOS.Sources.Internat.Inter.s.InterBody. There appear
        ;; to be discrepancies - Arabic, Hong Kong, and Maori. Probably of minor importance
        ;; given the pitiful state of internationalisation generally (part of what ResFinder
        ;; is aiming to fix), but it does seem strange to have two not-quite-the-same lists,
        ;; instead of, say, the ServiceCall passing the buck to the Territory Manager or
        ;; something...
        ;; </aside>


        ; Now load <progname> and check if it is "null". This is a special reserved value
        ; for when we want to read the various pieces of info without checking/setting
        ; any resource path.
        ADRL     R3, progname
        LDR      R0, [R3]
        LDR      R1, isnull
        CMP      R0, R1
        BEQ      the_extra_stuff             ; skip the path stuff


        ; Do we have a preferred fallback language?
        ADRL     R0, rffallback
        ADRL     R1, fallback
        MOV      R2, #16
        MOV      R3, #0
        MOV      R4, #3
        SWI      XOS_ReadVarVal
        CMP      R2, #0
        MOVEQ    R0, #TERMINULL              ; no fallback, null-terminate (it should be
        STREQB   R0, [R1]                    ; but don't rely on it if we later rely on it!)


        ; Build the initial path:
        ;   <[progname]$Dir>.Resources.  [and something gets added here]
        ADRL     R1, workspace
        MOV      R2, #'<'                    ; write initial '<'
        STRB     R2, [R1], #1
        ADRL     R0, progname                ; copy <progname>
        BL       bytecopy
        SUB      R1, R1, #1
        ADRL     R0, respathmiddle           ; copy "$Dir>.Resources."
        BL       bytecopy
        SUB      R1, R1, #1
        MOV      R7, R1                      ; preserve this for later


        ; Now look to see if we are operating in a UTF-8 alphabet.
        MOV      R0, #OSB_RW_KEYALPHA        ; read alphabet
        MOV      R1, #JUST_READ_ALPHABET
        SWI      OS_Byte
        MOV      R8, R1                      ; stash this in R8 (##trashes "-useos" flag##)
        CMP      R1, #IS_UTF8
        BNE      check_primary               ; not a UTF-8 setup


check_primary_utf8
        ; If we are here, this machine is operating in UTF-8 mode.
        ; So we look for the UTF-8 version of the primary language.
        ;   <[progname]$Dir>.Resources.[terrname].UTF8
        ADRL     R0, terrname                ; copy primary territory name
        MOV      R1, R7
        BL       bytecopy                    ; from R0(terrname) to R1(wrkspc ptr)
        SUB      R1, R1, #1                  ; suffix ".UTF8"
        MOV      R2, #'.'
        STRB     R2, [R1], #1
        MOV      R2, #'U'                    ; It is four instructions to set up <bytecopy>
        STRB     R2, [R1], #1                ; plus two words of data, and 30 instructions
        MOV      R2, #'T'                    ; with eight branches in order to copy the six
        STRB     R2, [R1], #1                ; bytes of ".UTF8<null>".
        MOV      R2, #'F'                    ;
        STRB     R2, [R1], #1                ; Or we do it the "long winded" way, and it is
        MOV      R2, #'8'                    ; done and dusted in twelve instructions with
        STRB     R2, [R1], #1                ; no branches whatsoever.
        MOV      R2, #TERMINULL              ;
        STRB     R2, [R1], #1                ; nb: ADRL is synthesised from 2 instructions
 [ TRACE = 1
        TraWrS   'P', 'U', workspace         ; Primary Utf8
 ]
        BL       does_this_exist

        ; Note - we store a pointer to the end of the "Resources" part of the path,
        ; and not the start of the .UTF8 as the extra work in re-copying the territory
        ; name is offset by the ease of which we can swap in the fallback language, UK,
        ; and clip to just Resources.


check_primary
        ; Now look for the regular version of the primary language.
        ;   <[progname]$Dir>.Resources.[terrname]
        ADRL     R0, terrname
        MOV      R1, R7
        BL       bytecopy
 [ TRACE = 1
        TraWrS   'P', 'L', workspace         ; Primary Latin
 ]
        BL       does_this_exist
        CMP      R0, #0                      ; don't fallback if primary found
        BNE      check_uk_utf8


        ; We only come through to HERE if we do NOT have a primary language resource.
        ; So now time to consider the fallback language, if one was specified.

        ADRL     R0, fallback
        LDRB     R1, [R0]
        CMP      R1, #TERMINULL
        BEQ      check_uk_utf8               ; no fallback, skip on to checking UK.UTF8


check_fallback_utf8
        ; We have a fallback, so copy this over and look for UTF8 version.
        ;   <[progname]$Dir>.Resources.[fallback].UTF8
        CMP      R8, #IS_UTF8
        BNE      check_fallback              ; not a UTF-8 setup

        ; R0 already set (=fallback)
        MOV      R1, R7
        BL       bytecopy                    ; from R0(fallback) to R1(wrkspc ptr)
        SUB      R1, R1, #1                  ; suffix ".UTF8"
        MOV      R2, #'.'
        STRB     R2, [R1], #1
        MOV      R2, #'U'
        STRB     R2, [R1], #1
        MOV      R2, #'T'
        STRB     R2, [R1], #1
        MOV      R2, #'F'
        STRB     R2, [R1], #1
        MOV      R2, #'8'
        STRB     R2, [R1], #1
        MOV      R2, #TERMINULL
        STRB     R2, [R1], #1
 [ TRACE = 1
        TraWrS   'F', 'U', workspace         ; Fallback Utf8
 ]
        BL       does_this_exist


check_fallback
        ; We have a fallback, so look for it.
        ;   <[progname]$Dir>.Resources.[fallback]
        ADRL     R0, fallback
        MOV      R1, R7
        BL       bytecopy
 [ TRACE = 1
        TraWrS   'F', 'L', workspace         ; Fallback Latin
 ]
        BL       does_this_exist


check_uk_utf8
        ; If current *is* UK, don't construct it again!
        CMP      R12, #1
        BEQ      check_resources_only

        ; Now see if UK.UTF8 exists as a possible fallback language.
        CMP      R8, #IS_UTF8
        BNE      check_uk                    ; not a UTF-8 setup

        MOV      R1, R7
        MOV      R2, #'U'                    ; suffix "UK.UTF8"
        STRB     R2, [R1], #1
        MOV      R2, #'K'
        STRB     R2, [R1], #1
        MOV      R2, #'.'
        STRB     R2, [R1], #1
        MOV      R2, #'U'
        STRB     R2, [R1], #1
        MOV      R2, #'T'
        STRB     R2, [R1], #1
        MOV      R2, #'F'
        STRB     R2, [R1], #1
        MOV      R2, #'8'
        STRB     R2, [R1], #1
        MOV      R2, #TERMINULL
        STRB     R2, [R1], #1
 [ TRACE = 1
        TraWrS   'U', 'U', workspace         ; UK Utf8
 ]
        BL       does_this_exist


check_uk
        ; Now look for UK.
        ;   <[progname]$Dir>.Resources.UK
        MOV      R1, R7
        MOV      R0, #'U'
        STRB     R0, [R1], #1
        MOV      R0, #'K'
        STRB     R0, [R1], #1
        MOV      R0, #TERMINULL
        STRB     R0, [R1]
 [ TRACE = 1
        TraWrS   'U', 'L', workspace         ; UK Latin
 ]
        BL       does_this_exist


check_resources_only
        ; Now we remove the country specifier, to leave just:
        ;   <[progname]$Dir>.Resources
        SUB      R1, R7, #1                  ; back up over the trailing '.'
        MOV      R0, #TERMINULL              ; and terminate it there
        STRB     R0, [R1]
 [ TRACE = 1
        TraWrS   'R', 'D', workspace         ; Resource Directory
 ]
        BL       does_this_exist


check_appdir
        ; And, finally, build and set the application's own directory...
        ;   <[progname]$Dir>
        SUB      R1, R7, #11                 ; back up over ".Resources."
        MOV      R0, #TERMINULL              ; and terminate it at that point
        STRB     R0, [R1]                    ; to leave just "<[progname]$Dir>"
 [ TRACE = 1
        TraWrS   'A', 'D', workspace         ; Application Directory
 ]
        BL       does_this_exist


        ; At this point, <workspace>+256 points to a resources path which will be in
        ; the form:
        ;    <[progname]$Dir>.Resources.[terrname],<[progname]$Dir>.Resources.UK,
        ;    <[progname]$Dir>.Resources,<[progname]$Dir>
        ; Exactly what is present depends upon the situation. It could be all four,
        ; it could be just the base application directory... With or without UTF-8.


        ; Do we have a custom variable name?
        CMP      R9, #CUSTOM_RESVAR
        ADREQ    R0, respath
        BEQ      do_set_path

        ; else...
        SUB      R1, R6, #1
        MOV      R0, #'$'
        STRB     R0, [R1], #1
        MOV      R0, #'P'
        STRB     R0, [R1], #1
        MOV      R0, #'a'
        STRB     R0, [R1], #1
        MOV      R0, #'t'
        STRB     R0, [R1], #1
        MOV      R0, #'h'
        STRB     R0, [R1], #1
        MOV      R0, #TERMINULL
        STRB     R0, [R1]
        ADR      R0, progname


do_set_path
        ; Behaviour is as follows:
        ;
        ; If this is a PATH, we write the entire composite path.
        ; If this is NOT a PATH, we truncate at the first ',' and write only that.

        ; Restore <R11> to point back to start of composite path
        ADRL     R11, workspace
        ADD      R11, R11, #256

        CMP      R9, #NO_CUSTOM_RESVAR       ; if NOT a custom resources variable
        CMPNE    R10, #CUSTOM_RESVAR_ISPATH  ; or custom resources variable IS a path...
        BEQ      skip_to_set_path            ; jump over to set what we have

        MOV      R2, R11                     ; else...
look_for_end_loop
        LDRB     R1, [R2], #1
        CMP      R1, #','                    ; look for comma
        CMPNE    R1, #TERMINULL              ; or terminull (so we don't overshoot)
        BNE      look_for_end_loop
        SUB      R2, R2, #2                  ; back up over comma/terminull and '.'
        MOV      R1, #TERMINULL              ; paste a terminull there
        STRB     R1, [R2]

skip_to_set_path
        ; at this point, the composite string holds what we want to set
        ; count length of string in <workspace>+256
        MOV      R2, #0
        MOV      R3, R11
count_loop
        LDRB     R1, [R3], #1
        CMP      R1, #CTRLCHAR
        ADDGT    R2, R2, #1
        BGT      count_loop
        ; Set the variable - and note that this WILL not blow up on older machines if
        ; the path is long.
        ; I just tried the following on RISC OS 3.70:
        ;    DIM x% 4096
        ;    FOR l% = 0 TO 4095 : x%?l% = ASC("?") : NEXT
        ;    SYS "OS_SetVarVal", "Test$Val", x%, 4096, 0, 0
        ;    *Show Test$Val
        ; And it works.
        ; You *could* replace 4096 with 131072 (128K!), and it *might* work. Seemed to
        ; sometimes in a TaskWindow, but it made WindowManager kinda crashy. Always hung
        ; up the machine in the F12 prompt. Hmmm... Probably not such a smart idea, but
        ; just used to illustrate that it won't immediately keel over dead if the path
        ; hits the command line length limit.
        ; It works, too. I also tried:
        ;    Set Test$Path <OvationPro$Dir>., [repeated +/- a dozen times so it was long]
        ;    Type Test:!Run
        ; and the file appeared. [ yay RISC OS (^_^) ]


        ; R0 set above <progname> or <respath>
        MOV      R1, R11
        ; R2 set above (counter)
        MOV      R3, #0                      ; name pointer
        MOV      R4, #SET_STRING             ; string, GSTrans it
        SWI      OS_SetVarVal
 [ TRACE = 1
        TraWrS   'O', 'V', R11               ; Output Variable
 ]
        ; NOTE:
        ; If you are coming here trying to work out why you get weird results, like:
        ;   *UnSet AppName$Dir
        ;   *ResFinder AppName -useos
        ;   *Show AppName*
        ;   AppName$Path : .
        ;   *
        ; then this is because we pass "<AppName$Dir>." to the OS which tries to expand
        ; that to be a canonicalised path, and fails because "<AppName$Dir>" does not
        ; exist. The result is the path becomes a single dot.
        ; Moral of the story? Set your app$dir stuff first, like any sane person would.


the_extra_stuff
        ; Now some value-added extras
        ;
        ;   ResFinder$Language
        ;     Set to textual name of country/territory *used*. On multilingual
        ;     systems this may change depending on whether or not "-useos" was
        ;     specified. This is mostly provided as an easy way to see what
        ;     language ResFiler was looking for, if it doesn't exist.
        ;
        ;   ResFinder$PlatformClass
        ;     0 = not identified
        ;     1 = 26 bit Archimedes or RiscPC
        ;     2 = 32 bit RiscPC (or RiscPC like)
        ;     3 = Iyonix
        ;     4 = OMAP3 (Beagle, xM, IGPV, etc)
        ;     5 = OMAP4 (Pandaboard, etc)
        ;     6 = RaspberryPi
        ;     7 = }
        ;     8 =  >- undefined
        ;     9 = }
        ;     You can assume that all platform classes EXCEPT #1 are 32bit.
        ;
        ;   ResFinder$CPUID  [undocumented]
        ;     Hex value of CPUID read (except on 26 bit machines).
        ;     Added to aid in supporting anything that is classed as "unknown", and may
        ;     be useful to others (ie to determine various CPU features in later ARMs).


        ; ResFinder$Language
        ; ------------------
        MOV      R2, #0
        ADR      R3, terrname
count_loop_b                                 ; "count_loop_b"? Dude...lame.
        LDRB     R1, [R3], #1
        CMP      R1, #CTRLCHAR
        ADDGT    R2, R2, #1
        BGT      count_loop_b
        ADR      R0, rflangvar
        ADR      R1, terrname
        ; R2 set above (counter)
        MOV      R3, #0                      ; name pointer
        MOV      R4, #SET_STRING             ; string, GSTrans it
        SWI      OS_SetVarVal


        ; ResFinder$PlatformClass
        ; -----------------------
        MOV      R6, #DEVICE_UNKNOWN         ; default to "not idenitfied"

        ; Do the easy one first.
        CMP      PC, PC
        MOVNE    R6, #DEVICE_MESOLITHIC      ; PC does not equal PC, it's a 26 bit machine.
        BNE      set_class                   ; (don't care what, damn thing is Mesolithic!)

        ; After this point, we can assume we are running on a 32 bit machine, which means
        ; we will have - at the very least - CPSR, MSR/MRS, and CP15 to play with.
        ; So read the CPUID into R7. We'll need it later as it's an undocumented SysVar.
        MRS      R1, CPSR                    ; remember our current state (prob. USR32)
        SWI      OS_EnterOS                  ; go SVC32
        MRC      p15, 0, R7, c0, c0, 0       ; read CPU ID
        MSR      CPSR_c, R1                  ; back to previous state

        ; The next round of tests are for OMAP3/OMAP4,RPi. We will basically cheat and ask
        ; the HAL what the CPU type is. I *still* think this ought to be a simple
        ; OS_ReadSysInfo call...

        ; Ensure we actually HAVE a HAL! (or its a RiscPC)
        MOV      R0, #PLATFORMCLASS
        SWI      XOS_ReadSysInfo
        BVS      set_class                   ; a 32 bit RISC OS that doesn't ReadSysInfo 8?
                                             ; (does such a thing exist? we just give up)
                                             ; [A9Home? Does RO4.4x/32 use a HAL?]

        ; While OS_ReadSysInfo 8 can return an ID of RiscPC, A7000, etc - we do not check
        ; this as the 32 bit version of RISC OS, namely RISC OS 5, works with a HAL; so
        ; it will return "HAL" both on a RiscPC and on a RaspberryPi; so we can only assume
        ; that this will indicate the HALness of the OS.
        ; Of course, a non-HAL 32 bit version of RISC OS would be a non-standard oddity, so
        ; we just ignore this unlikely possibility (now that RISC OS Ltd is no longer).

        CMP      R0, #HAS_A_HAL              ; HAL
        BNE      set_class                   ; 32bit and not a HAL, just give up

        MOV      R0, #HALDEVTYPE_COMMS       ; HALDeviceType_Comms
        ADD      R0, R0, #HALDEVCOMMS_GPIO   ; HALDeviceComms_GPIO
        MOV      R1, #0
        MOV      R8, #4                      ; just trashed alphabet number, no biggie now
        SWI      XOS_Hardware                ; prod the HAL gently, for it may be grumpy
        BVS      set_class                   ; failed? just give up
        CMP      R1, #NO_MATCH               ; didn't match?
        BEQ      is_this_an_iyonix           ; (not a GPIO-able HAL; RPC or Iyonix?)

        LDRB     R1, [R2, #2]                ; read processor type into R1
        ADD      R6, R1, #DEVICE_HALCPUTYPE  ; cheat - we have OMAP3=0, OMAP4=1, RPi=2
        B        set_class                   ; and we want OMAP3=4, OMAP4=5, RPi=6, so...
                                             ; (any new HAL CPUs will just extend the list)

is_this_an_iyonix
        ; Okay, so here we know three things:
        ;   #1  We are running a 32 bit version of RISC OS.
        ;   #2  We have a HAL (so it is something RISC OS 5 works with).
        ;   #3  We don't have GPIO capabilities, so it is NOT an OMAP3/4 or Pi.
        ;
        ; While there are build options for the Samsung S32440 (an ARM926T board) and a
        ; Samsung S3C6410 (an ARM11 board), these are not part of the regular downloadable
        ; builds, so they will not be checked for (and thus ought to appear as "Unknown").
        ;
        ; This limits our options to:
        ;   Iyonix       - CPU is an XScale
        ;   RiscPC(like) - CPU is an ARM610/710/SA110/7500(FE).
        ;
        ; Note that we use "magic numbers" in the CPU detection as burying the actual
        ; values in a header file would make it harder to see what is going on. This is
        ; not a problem, however, as the CPU IDs are listed to make it clear how the
        ; numbers relate and what they are for.

        ; CPUID already read, value is in R7.

        ; XScale X80200 ID is &xx052000
        ; XScale X80321 ID is &69052400
        ;            we look for ^^^
        LDR      R1, cpuxscalemask
        AND      R0, R7, R1
        LDR      R1, cpuxscaletype
        CMP      R0, R1
        MOVEQ    R6, #DEVICE_IYONIX          ; XScale - it's an Iyonix
        BEQ      set_class

        ; Now to probe RiscPC class machines
        ; ARM610 ID    is &xx000610
        ;                       ^^
        ; ARM710 ID    is &xx007100
        ;              or &xx047100
        ; ARM7500 ID   is &xx027100
        ; ARM7500FE ID is &xx077100
        ; StrongARM ID is &xx01A100
        ;              or &xx01A110
        ;              or &xx01B110
        ;                      ^^
        ; We will strip this down to a single byte value of the unique parts, for
        ; determining what CPU is in use. This makes a hell of an assumption that we
        ; can ID processors from a single byte, but we are aided greatly by the fact
        ; that older style hardware upon which RISC OS runs is a limited subset of the
        ; amazing plethora of ARM devices available.

        ; First, deal with the ARM610
        MOV      R0, R7, LSR#4               ; &0610 -> &0061
        AND      R0, R0, #&FF                ; clip to a single byte
        CMP      R0, #&61
        MOVEQ    R6, #DEVICE_RISCPC          ; RiscPC-like with ARM610
        BEQ      set_class

        ; Now the ARM710/7500(FE)
        MOV      R0, R7, LSR#8               ; &7100 -> &0071
        AND      R0, R0, #&FF                ; clip to a single byte
        CMP      R0, #&71
        MOVEQ    R6, #DEVICE_RISCPC          ; RiscPC-like with ARM710 or ARM7500(FE)
        BEQ      set_class
        ; The Bush Internet set top box would be classed as RiscPC-like, should anybody
        ; attempt porting RISC OS to it...!
        ; But, then, one could argue that it IS a RiscPC, with all the good bits taken out.

        ; Finally the StrongARM
        CMP      R0, #&A1                    ; is it the 'A1' type?
        CMPNE    R0, #&B1                    ; if not, is it the 'B1' type?
        BNE      set_class                   ; if not, assume unknown
        MOV      R6, #DEVICE_RISCPC          ; RiscPC-like with some sort of StrongARM
        ; fall through to

set_class
        ; Okay, R6 holds some sort of system type ID (can be "dunno" (=0)), so time to set
        ; this as a numeric system variable.
        ADR      R0, rfcpuvar
        ADR      R1, workspace
        STR      R6, [R1]                    ; write value there
        MOV      R2, #4                      ; what is length for a numerical word?!?
        MOV      R3, #0
        MOV      R4, #SET_NUMBER             ; number
        SWI      OS_SetVarVal
        ;   ResFinder null ResFinder$Test
        ;   If "<ResFinder$PlatformClass>"=6 Then Echo This is a RaspberryPi!


        ; ResFinder$CPUID  (undocumented)
        ; ---------------
        CMP      R6, #DEVICE_MESOLITHIC      ; No CPUID for 26 bit systems.
        BEQ      sayonara                    ; ARM2 doesn't have it, ARM3 is different...
        MOV      R0, R7
        ADR      R1, workspace
        MOV      R2, #16
        SWI      OS_ConvertHex8
        ADR      R0, rfcpuidvar
        ADR      R1, workspace
        MOV      R2, #8                      ; Hex8 = 8 characters
        MOV      R3, #0
        MOV      R4, #SET_STRING
        SWI      OS_SetVarVal
 [ TRACE = 1
        TraWrS   'C', 'I', workspace         ; Cpu Id
 ]


        ; ======================
        ; That's it, we're DONE.
        ; ======================
sayonara
 [ TRACE = 1
        SWI      256+']'
        SWI      OS_NewLine
 ]
        LDR      PC, [R13], #4



; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =



bytecopy
        ; Copies a null terminated string from <R0> to <R1> - COPIES TERMINULL.
        ; In:
        ;   R0 = Source (null terminated)
        ;   R1 = Destination
        ; Out:
        ;   R0 = Corrupted
        ;   R1 = Points to null byte at end of string.

        STR      R2, [R13, #-4]!             ; preserve R2
bytecopyloop
        LDRB     R2, [R0], #1
        STRB     R2, [R1], #1
        CMP      R2, #CTRLCHAR
        BGT      bytecopyloop
        LDR      R2, [R13], #4               ; restore R2
        MOV      PC, R14



does_this_exist
        ; Check if the directory pointed to by <workspace> exists. If it does, add it
        ; to the composite path string.
        STMFD    R13!, {R1-R5, R14}
        MOV      R0, #READ_CATALOGUE         ; read catalogue info, no path
        ADR      R1, workspace
        SWI      XOS_File
        MOVVS    R0, #NOT_FOUND              ; error?  [will cause R0=0 on return]
        CMP      R0, #IS_A_DIRECTORY
        LDMNEFD  R13!, {R1-R5, PC}           ; return if nothing found
        ; fall through to...


        ; Append whatever is at <workspace> to the composite path pointed to by <R11>.
        ADR      R0, workspace               ; source pointer
        ADD      R1, R0, #256                ; derive pointer to composite
        CMP      R1, R11                     ; is our pointer same as real pointer?
        MOVNE    R1, #','                    ; if NOT, prefix our path with a comma
        STRNEB   R1, [R11], #1
        MOV      R1, R11                     ; destination pointer
        BL       bytecopy
        SUB      R11, R1, #1                 ; update destination (back over terminull)
        MOV      R0, #'.'
        STRB     R0, [R11], #1               ; add a '.' suffix to the path
        MOV      R0, #TERMINULL              ; then terminate
        STRB     R0, [R11]                   ; but don't update pointer
        MOV      R0, #-1                     ; make R0 non-zero to indicate success
        LDMFD    R13!, {R1-R5, PC}



syntax_error
        ; whinge that the command line options are all wrong, wah, wah, waaaaah!
        ADR      R0, syntaxmsg
        SWI      OS_Write0
        LDR      PC, [R13], #4



; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =



syntaxmsg
        EQUS     "Syntax: ResFinder <program name> [<resource variable>] [-useos]\r\n"
        EQUSZA   "        ResFinder is licensed under the EUPL v1.1 only.\r\n"

argparams
        EQUSZA   "a,b,c,useos/S"              ; us, appname, res string, -useos option

respathmiddle
        EQUSZA   "$Dir>.Resources."

rflangvar
        EQUSZA   "ResFinder$Language"

rffallback
        EQUSZA   "ResFinder$Fallback"

rfcpuvar
        EQUSZA   "ResFinder$PlatformClass"

rfcpuidvar
        EQUSZA   "ResFinder$CPUID"

isnull
        EQUS     "null"

cpuxscalemask
        EQUD     &00FFF000

cpuxscaletype
        EQUD     &00052000

lowercase
        EQUD     &20202020

pathtext
        EQUS     "path"                      ; or &68746170

terrname
        %        16                          ; long enough for "Azerbaijani" etc, so...

fallback
        %        16                          ; as above

progname
        %        BUFFERSIZE

respath
        %        BUFFERSIZE                  ; args string path is same, so can't overrun

workspace
        ; last thing in the program, so from code end to the stack can be workspace
        ; the least this will be is 4096 bytes - (<progsize>+4); which will still be
        ; something like 2K-odd. The more likely... 640K or so; depends if WimpSlot
        ; has already been processed when ResFinder is called.
        ; Anyway, there's plenty.

        ; Embedded string that we will trash... [must finish on a word boundary]
        EQUS     "  \n\nResFinder v0.06 2014/02/12\n© 2014 Richard Murray\n"
        EQUS     "http://www.heyrick.co.uk/software/resfinder/\n\n"

        EQUS     "Sono mukashi hito datta koto mo wasureta shinkaigyo.\n"
        ;        Now I'm a fish in the deep sea, having forgotten
        ;        that I used to be a human long ago.
        ;        [from "Ryugu no Tsukai" by Chitose Hajime,
        ;         http://www.youtube.com/watch?v=BWvvOohnBWA]

        END

Created by ROView by Rick Murray.