; 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.
;
; Kernel.Messages
;
; This file deals with translating text and errors in an international
; kernel.
;
;
; TranslateError
; Entry:
;       R0 = Pointer to error block (Error number followed by token:default).
; Exit
;       R0 = pointer to error block to use.
;            If the error semaphore is set a pointer to the original block is
;            returned.
;       V Set.
;

;        EXPORT  Write0_Translated

        GBLL    countmsgusage
countmsgusage SETL {FALSE}
     [ countmsgusage
        ! 0,    "*** WARNING: Cacheing *ALL* messages - even those with substitutions"
     ]

TranslateError_VClear  ROUT
        Push    "r4,LR"
        MOV     r4,#0
        BL      TranslateError_UseR4
        CLRV
        Pull    "r4,PC"

        EXPORT  TranslateError
TranslateError  ROUT
        Push    "r4,LR"
        MOV     r4,#0
        BL      TranslateError_UseR4
        Pull    "r4,PC"

TranslateError_UseR4
        Push    "R8,R9,LR"
        MRS     R8,CPSR
        ORR     R8,R8,#V_bit                    ; V set ready :)

        LDR     LR, =ZeroPage
        LDRB    R9, [LR, #ErrorSemaphore]
        TEQ     R9,#0
        BNE     %FT90

        BIC     R9, R8, #&0F
        ORR     R9, R9, #SVC_mode               ; SVC mode, preserve IRQ state
        MSR     CPSR_c, R9

        Push    "R0-R7"

        MOV     R5,#0
        MOV     R6,#0
        MOV     R7,#0
        MOV     R1,#-1
        LDR     LR, =ZeroPage                   ; We are looking up an error, don't bother
        STRB    R1, [LR, #ErrorSemaphore]       ; translating other errors.

  [ CacheCommonErrors
        BL      CheckCommonErrorCache           ; sets R9 to memory address for cached result
      [ ZeroPage = 0
        MOVNE   R1,#0
      ]
        STRNE   R9,[SP]
        BNE     %FT80
        MOV     R2,R9                           ; 0 - or our cached area!
        MOV     R3,#256
  |
        MOV     R2,#0
  ]

        LDR     R1,=ZeroPage+KernelMessagesBlock+4
        SWI     XMessageTrans_ErrorLookup
        LDR     R14,[R0]
        LDR     R1,[SP]
        LDR     R1,[R1]
        CMP     R14,R1
        STREQ   R0,[SP]
      [ (ZeroPage = 0) :LOR: CacheCommonErrors
        MOV     R1,#0                           ; To clear the semaphore
      ]
  [ CacheCommonErrors
        TEQNE   R9,#0                           ; Did we try to cache this message?
        STRNE   R1,[R9]                         ; blat out the error number
80
  ]
      [ ZeroPage <> 0
        ASSERT  (ZeroPage :AND: 255) = 0
        LDR     R1, =ZeroPage
      ]
        STRB    R1, [R1 ,#ErrorSemaphore]       ; Clear error semaphore

        Pull    "R0-R7"
90
        MSR     CPSR_cf, R8                     ; Back to original mode, V set
        Pull    "R8,R9,PC"

  [ CacheCommonErrors
   ; This block MUST not be empty
CommonErrorAddresses
    [ countmsgusage
        &       ErrorBlock_RMNot32bit
        &       ErrorBlock_RCExc
        &       ErrorBlock_RCNegative
        &       ErrorBlock_BadString
        &       ErrorBlock_VarCantFind
        &       ErrorBlock_BadVarType
        &       ErrorBlock_BadVarNam
        &       ErrorBlock_VarTooLong
        &       ErrorBlock_BadMacVal
        &       ErrorBlock_VarNoRoom
        &       ErrorBlock_BadBra
        &       ErrorBlock_StkOFlo
        &       ErrorBlock_MissOpn
        &       ErrorBlock_MissOpr
        &       ErrorBlock_BadInt
        &       ErrorBlock_StrOFlo
        &       ErrorBlock_NaffItm
        &       ErrorBlock_DivZero
        &       ErrorBlock_BadBase
        &       ErrorBlock_NumbTooBig
        &       ErrorBlock_BadClaimNum
        &       ErrorBlock_SysHeapFull
        &       ErrorBlock_BadDynamicArea
        &       ErrorBlock_AreaAlreadyExists
        &       ErrorBlock_AreaNotOnPageBdy
        &       ErrorBlock_OverlappingAreas
        &       ErrorBlock_CantAllocateArea
        &       ErrorBlock_CantAllocateLevel2
        &       ErrorBlock_UnknownAreaHandler
        &       ErrorBlock_CantGetPhysMem
        &       ErrorBlock_AplWSpaceInUse
        &       ErrorBlock_ChDynamCAO
        &       ErrorBlock_RAMFsUnchangeable
        &       ErrorBlock_HeapBadReason
        &       ErrorBlock_HeapFail_Init
        &       ErrorBlock_HeapFail_BadDesc
        &       ErrorBlock_HeapFail_BadLink
        &       ErrorBlock_HeapFail_Alloc
        &       ErrorBlock_HeapFail_NotABlock
        &       ErrorBlock_HeapFail_BadExtend
        &       ErrorBlock_HeapFail_ExcessiveShrink
        &       ErrorBlock_NoSuchSWI1
        &       ErrorBlock_NoSuchSWI
        &       ErrorBlock_UndefinedInstruction
        &       ErrorBlock_InstructionAbort
        &       ErrorBlock_DataAbort
        &       ErrorBlock_AddressException
        &       ErrorBlock_BranchThrough0
        &       ErrorBlock_BadEnvNumber
        &       ErrorBlock_BadReadSysInfo
        &       ErrorBlock_BadModuleReason
        &       ErrorBlock_NoMoreModules
        &       ErrorBlock_NoMoreIncarnations
        &       ErrorBlock_PostfixNeeded
        &       ErrorBlock_IncarnationExists
        &       ErrorBlock_ChunkNotRM
        &       ErrorBlock_MHNoRoom
        &       ErrorBlock_ModulePostfix
        &       ErrorBlock_NotMod
        &       ErrorBlock_BadRMHeaderField
        &       ErrorBlock_CantKill
        &       ErrorBlock_RMNotFound
        &       ErrorBlock_IncarnationNotFound
        &       ErrorBlock_RMNotFoundInROM
        &       ErrorBlock_ModuleTooOld
        &       ErrorBlock_BadParameters
        &       ErrorBlock_ArgRepeated
        &       ErrorBlock_NaffDevNo
        &       ErrorBlock_BadDevVecRel
        &       ErrorBlock_RedirectFail
        &       ErrorBlock_StackFull
        &       ErrorBlock_OscliLongLine
        &       ErrorBlock_OscliTooHard
        &       ErrorBlock_BadParmString
        &       ErrorBlock_CoreNotWriteable
        &       ErrorBlock_CoreNotReadable
        &       ErrorBlock_BadCommand
        &       ErrorBlock_NoSuchSWI2
        &       ErrorBlock_TooManyParms
        &       ErrorBlock_BadKey
        &       ErrorBlock_BadAddress
        &       ErrorBlock_OutsideFile
        &       ErrorBlock_Escape
        &       ErrorBlock_BadTime
        &       ErrorBlock_BadMODE
        &       ErrorBlock_ModeNotAvailable
        &       ErrorBlock_BadPixelDepth
        &       ErrorBlock_Sprite_BadDPI
        &       ErrorBlock_BadMSFlags
        &       SpriteErr_NoWorkSpace
        &       SpriteErr_NoRoom
        &       SpriteErr_DoesntExist
        &       SpriteErr_NoSprites
        &       SpriteErr_NotGraphics
        &       SpriteErr_NotEnoughRoom
        &       SpriteErr_BadSpriteFile
        &       SpriteErr_NoRoomToMerge
        &       SpriteErr_Bad2ndPtr
        &       SpriteErr_InvalidRowOrCol
        &       SpriteErr_InvalidHeight
        &       SpriteErr_InvalidWidth
        &       SpriteErr_NoRoomToInsert
        &       SpriteErr_SpriteAlreadyExists
        &       SpriteErr_InvalidSpriteMode
        &       SpriteErr_BadReasonCode
        &       SpriteErr_CantInTeletext
        &       SpriteErr_InvalidSaveArea
        &       SpriteErr_SpriteIsCurrentDest
        &       SpriteErr_NoMaskOrPaletteAllowedInThisDepth
     ]
        &       ErrorBlock_ChDynamNotAllMoved
        &       ErrorBlock_NaffRelease
        &       ErrorBlock_BuffOverflow
        &       ErrorBlock_BadNumb
EndCommonErrorAddresses

        GBLA    ECEACount
ECEACount SETA  (EndCommonErrorAddresses-CommonErrorAddresses)/4
        ASSERT  (EndCommonErrorAddresses <> CommonErrorAddresses)

        ! 0, "Requiring ":CC:(:STR:(ECEACount*256)):CC:" bytes for error cache"
        ! 0, "Cached error block pointer at ":CC::STR:CachedErrorBlocks
        ! 0, "Cacheing ":CC:(:STR:ECEACount):CC:" error messages"

; This routine exits with Z clear if it can supply a cached translation; else must set Z
; so that the TranslateError_UseR4 routine continues to function and set R9 to the cache
; block to use for the result (or set R9 to zero to indicate no cacheing for this error)
CheckCommonErrorCache ROUT
        Entry   "r1-r3"
    [ countmsgusage
        MOV     r4, #0                      ; prevents substitutions
    |
        CMP     r4, #1                      ; is R4 = 0?  If so, clear C for next instruction
        SBCS    r9, r4, r4                  ; R9=0,Z set - if R4 was >0, else R9=-1, Z clear
        EXIT    EQ
    ]
      [ ZeroPage = 0
        LDR     r9, [r4, #KernelMessagesBlock] ; R4 guaranteed zero from above
      |
        LDR     r1, =ZeroPage
        LDR     r9, [r1, #KernelMessagesBlock]
      ]
        TEQ     r9, #0
        EXIT    EQ                          ; not initialised messages yet!  Exit R9=0, Z set
      [ ZeroPage = 0
        LDR     r9, [r4, #CachedErrorBlocks]
      |
        LDR     r9, [r1, #CachedErrorBlocks]
      ]
        TEQ     r9, #0
        ; in:  R9=cached error blocks memory pointer
        BLEQ    CommonErrorCacheInit
        ; out: R9=0 and EQ on error; else NE and R9 valid (also case if routine wasn't called)
        EXIT    EQ                          ; not initialised cache yet
        ADR     lr, CommonErrorAddresses
        MOV     r2, #ECEACount - 1
10
        LDR     r1, [lr, r2, LSL #2]
        TEQ     r1, r0
        BEQ     %FT20
        SUBS    r2, r2, #1
        BPL     %BT10
        ; Set Z if message not found, set R9 zero to mark we don't want to cache this
        MOVS    r9, #0
        EXIT
20
    [ countmsgusage
        ADD     r3, r9, #ECEACount*256
        LDR     r1, [r3, r2, LSL #2]
        ADD     r1, r1, #1
        STR     r1, [r3, r2, LSL #2]
    ]
        ; Read the cached error number (0 = we don't have this cached or we have uncached it)
        ; Update R9 to point to the actual error buffer at the same time.
        LDR     r1, [r9, r2, LSL #8]!
        ; Set Z if we don't have that error cached yet, clear it and copy the cached
        ; block to R0 if we do already have this message.
        TEQ     r1, #0
        MOVNE   r0, r9
        EXIT

; On entry, R9 points to the error cache memory, or 0 to indicate we don't have it yet.
; If this routine exits Z clear (NE), it MUST have pointed R9 at the sys heap memory
CommonErrorCacheInit ROUT
        Entry   "r0-r8"
        MOVS    r2, r9                          ; copy R9 to R2 - only claim memory if it was 0
        BNE     %FT10
    [ countmsgusage
        LDR     r3, =ECEACount*260              ; size of block required
    |
        LDR     r3, =ECEACount*256              ; size of block required
    ]
        BL      ClaimSysHeapNode
        MOVS    r9, #0                          ; set Z for STREQ below and for return
        EXIT    VS
10
        MOV     r3, #0
      [ ZeroPage = 0
        STREQ   r2, [R3, #CachedErrorBlocks]
      |
        LDREQ   r4, =ZeroPage
        STREQ   r2, [r4, #CachedErrorBlocks]
      ]

        GBLA    CECLoop
CECLoop SETA    0
     [ countmsgusage
        LDR     r4, =ECEACount*260
90
        SUBS    r4, r4, #4
        STR     r3, [r2, r4]
        BNE     %BT90
     |
        WHILE CECLoop < ECEACount
          STR   r3, [r2, #CECLoop * 256]
CECLoop SETA    CECLoop+1
        WEND
     ]

        MOVS    r9, r2                          ; set up R9; clear Z
        EXIT

; Invoked by the service call handler in the UtilityModule to clear out our cache
; whenever the territory changes or messagetrans says that a messages file was changed.
CacheCommonErrorsReinit
        Entry   "r9"
        LDR     r9, =ZeroPage
        LDR     r9, [r9, #CachedErrorBlocks]
        TEQ     r9, #0
        BLNE    CommonErrorCacheInit
        EXIT
  ]

;----------------------------------------------------------------------------------------
;
;WriteS_Translated
;Entry:
;       R14 -> Token.
;*NOTE*
;      MUST BE TOKEN:DEFAULT
;
;Exit:
;       Message printed
;       Returns to word after end of token.
;
WriteS_Translated       ROUT
        Push    "r0-r8,LR"
        MOV     r4,#0
        B       Int_WriteS_Translated_UseR4

WriteS_Translated_UseR4

        Push    "r0-r8,LR"

Int_WriteS_Translated_UseR4
        MRS     r8,CPSR
        MOV     r1,LR
        LDR     r2,=ZeroPage
        LDR     r0,[r2,#KernelMessagesBlock]
        CMP     r0,#0                           ; If no messages file, try the global one.
        ADDNE   r0,r2,#KernelMessagesBlock+4
        MOV     r2,#0                           ; Use message in place.
        MOV     r3,#0
        MOV     r5,#0
        MOV     r6,#0
        MOV     r7,#0                           ; No arguments.
        SWI     XMessageTrans_Lookup
        BVC     %FT01

        MOV     R2,R1                           ; MessageTrans not present or token not found.
00
        LDRB    r0,[r2],#1                      ; Skip to after ":"
        CMP     r0,#":"
        BNE     %BT00

; Now
; r1 -> terminator
; r2 -> message
; Print the message.

01
        LDRB    r0,[r2],#1                      ; Print all characters of message
        CMP     r0,#" "
        BLT     %FT02
        CMP     r0,#"%"
        SWINE   XOS_WriteC
        BNE     %BT01

        LDRB    r0,[r2],#1                      ; Found a %
        CMP     r0,#" "
        BLT     %FT02                           ; Trailing % sign!
        CMP     r0,#"0"
        SWINE   XOS_WriteI+"%"
        SWINE   XOS_WriteC
        BNE     %BT01                           ; Just in case it isn't %0

        CMP     r4,#0                           ; r4 = original parameter
        BEQ     %BT01

11
        LDRB    R0,[R4],#1
        CMP     R0,#" "
        SWIGE   XOS_WriteC
        BGE     %BT11
        B       %BT01
                                                ; Now skip to end of token.
02
        LDR     r1,[sp,#9*4]                    ; Get original token pointer
03
        LDRB    r0,[r1],#1
        CMP     r0,#32
        BGE     %BT03                           ; Skip to control character.
04
        CMP     r0,#0                           ; Print all control characters to terminating 0.
        SWINE   XOS_WriteC
        LDRNEB  r0,[r1],#1
        BNE     %BT04

; r1 now points at byte after end of token.

        ADD     r1,r1,#3                        ; Round up to next word.
        BIC     r1,r1,#3

        STR     r1,[sp,#9*4]                    ; Store back as return address on stack
        ORRVS   r8,r8,#V_bit
        MSR     CPSR_f,r8

        Pull    "r0-r8,PC"                       ;Return.

;----------------------------------------------------------------------------------------
;
;GSWriteS_Translated
;Entry:
;       R14 -> Token.
;*NOTE*
;      MUST BE TOKEN:DEFAULT
;
;Exit:
;       Message printed
;       Returns to word after end of token.
;
GSWriteS_Translated       ROUT
        Push    "r0-r8,LR"
        MOV     r4,#0
        B       Int_GSWriteS_Translated_UseR4

GSWriteS_Translated_UseR4

        Push    "r0-r8,LR"

Int_GSWriteS_Translated_UseR4
        MRS     r8,CPSR
        MOV     r1,LR
        LDR     r2,=ZeroPage
        LDR     r0,[r2,#KernelMessagesBlock]
        CMP     r0,#0                           ; If no messages file, try the global one.
        ADDNE   r0,r2,#KernelMessagesBlock+4
        LDR     r2,=GeneralMOSBuffer
        MOV     r3,#256
        MOV     r5,#0
        MOV     r6,#0
        MOV     r7,#0                           ; No arguments.
        SWI     XMessageTrans_GSLookup
        BVC     %FT01

        MOV     R2,R1                           ; MessageTrans not present or token not found.
00
        LDRB    r0,[r2],#1                      ; Skip to after ":"
        CMP     r0,#":"
        BNE     %BT00

; Now
; r1 -> terminator
; r2 -> message
; Print the message using OS_PrettyPrint.

01
        LDR     r0, =GeneralMOSBuffer
        MOV     r1, #0
        SWI     XOS_PrettyPrint
                                                ; Now skip to end of token.
02
        LDR     r1,[sp,#9*4]                    ; Get original token pointer
03
        LDRB    r0,[r1],#1
        CMP     r0,#0
        BNE     %BT03                           ; Skip to 0.

; r1 now points at byte after end of token.

        ADD     r1,r1,#3                        ; Round up to next word.
        BIC     r1,r1,#3

        STR     r1,[sp,#9*4]                    ; Store back as return address on stack
        ORRVS   r8,r8,#V_bit
        MSR     CPSR_f,r8

        Pull    "r0-r8,PC"                       ;Return.

;----------------------------------------------------------------------------------------
;FindToken
;
;Entry:
;       R0 -> Token.
;*NOTE*
;      MUST BE TOKEN:DEFAULT
;
;Exit:
;       r0 -> Message, or after the : if MessageTrans is dead.
;
;
FindToken      ROUT
        Push    "r0-r8,LR"
        MRS     r8,CPSR

        MOV     r1,r0
        LDR     r2,=ZeroPage
        LDR     r0,[r2,#KernelMessagesBlock]
        CMP     r0,#0                           ; If no messages file, try the global one.
        ADDNE   r0,r2,#KernelMessagesBlock+4
        MOV     r2,#0
        MOV     r3,#0
        MOV     r4,#0
        MOV     r5,#0
        MOV     r6,#0
        MOV     r7,#0                           ; No arguments.
        SWI     XMessageTrans_Lookup
        BVC     %FT01

        MOV     R2,R1                           ; MessageTrans not present or token not found.
00
        LDRB    r0,[r2],#1                      ; Skip to after ":"
        CMP     r0,#":"
        BNE     %BT00
01
        STR     r2,[sp]

        MSR     CPSR_f,r8
        Pull    "r0-r8,PC"

;----------------------------------------------------------------------------------------
;Write0_Translated
;
;Entry:
;       R0 -> Token.
;*NOTE*
;      MUST BE TOKEN:DEFAULT
;
;Exit:
;        Message printed, r0->Message.
;
Write0_Translated ROUT
        EntryS  "r0,r1"
        BL      FindToken
        MOV     R1,R0
01
        LDRB    R0,[R1],#1
        CMP     R0,#31
        SWIGT   XOS_WriteC
        STRVS   r0,[SP]
        EXIT    VS
        BGT     %BT01

        EXITS

        END