; 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.
;
        TTL     => Kernel : SWI Despatch, simple SWIs
        SUBT    Arthur Variables
        OPT     4

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; handy macros:
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

         MACRO
$l       CheckSpaceOnStack   $space, $faildest, $tmp
 [ True ; SKS
$l      MOV     $tmp, sp, LSR #15       ; Stack base on 32K boundary
        SUB     $tmp, sp, $tmp, LSL #15 ; Amount of stack left
        CMP     $tmp, #$space           ; Must have at least this much left
        BMI     $faildest
 |
$l       MOV        $tmp, #32*1024   ; assume stack ends at next 32K boundary
         SUB        $tmp, $tmp, #1
         AND        $tmp, $tmp, stack
         CMP        $tmp, #$space
         BLT        $faildest
 ]
         MEND

        MACRO
        assert  $condition
 [ :LNOT: ($condition)
 ! 1,"Assert failed: $condition"
 ]
        MEND

; one that builds a module command table entry:
; set Module_BaseAddr to module base before use.

                GBLA    Module_BaseAddr
Module_BaseAddr SETA    0

;        Command $cmd, $max, $min   - declared in hdr.macros.

; debug macro: set the border colour

        MACRO
$l      SetBorder  $reg1, $reg2, $red, $green, $blue, $delay
        ! 0, "Setborder used"
$l      LDR     $reg1, =VIDC
 [ VIDC_Type = "VIDC20"
; Note $reg, $green and $blue are 4 bit values
        LDR     $reg2, =&40000000+(($red)*&11)+(($green)*&1100)+(($blue)*&110000)
 |
        LDR     $reg2, =&40000000+ $red + $green *16 + $blue *256
 ]
        STR     $reg2, [$reg1]
 [ "$delay"<>""
        MOV     $reg1, #$delay
10
        SUBS    $reg1, $reg1, #1
        BNE     %BT10
 ]
        MEND

; Fake a 26-bit pc, given a PSR currently in lr, and the 32-bit address on
; the stack. The stacked address is pulled, and the result is left in lr.

        MACRO
        FakeLR  $temp, $dontpull
        AND     $temp,lr,#&F0000003
        AND     lr,lr,#I32_bit+F32_bit
        ORR     $temp,$temp,lr,LSL #IF32_26Shift
 [ "$dontpull" = "dontpull"
        LDR     lr,[sp]
 |
        LDR     lr,[sp],#4
 ]
        BIC     lr,lr,#ARM_CC_Mask
        ORR     lr,lr,$temp
        MEND

; Ickle macros. We want to be able to turn IRQs on and off fast in the
; code in various places. To do this easily, have a name for the
; SVC26/32 mode we run in

 [ No26bitCode
USR2632 * USR32_mode
SVC2632 * SVC32_mode
 |
USR2632 * USR26_mode
SVC2632 * SVC26_mode
 ]

;

    [ AddTubeBashers
 [ TubeType = Tube_Normal
DebugTUBE  * &03340000+3*&4000         ; tube in podule #3
; Tube register offsets
 ^ 0
R1STAT # 4
R1DATA # 4
 |
DebugTUBE * &03000000           ; simulator tube address
R1DATA * 0
 ]
   ]

; routine to stuff a char down the Tube
; should be inside above conditional, but AAsm winges pitifully.
     MACRO
$l   TubeChar $reg1, $reg2, $charset, $stackthere
  !  0, "TubeChar used."
$l
   [ "$stackthere"=""
   Push "$reg1, $reg2"
   ]
     LDR  $reg1, =DebugTUBE
 [ TubeType = Tube_Normal               ; normal tubes have status register, simulator one doesn't
01   LDRB $reg2, [$reg1, #R1STAT]
     TST $reg2, #&40
     BEQ %BT01
 ]
     $charset
     STRB $reg2, [$reg1, #R1DATA]
   [ "$stackthere"=""
   Pull "$reg1, $reg2"
   ]
     MEND

        MACRO
$l      TubeString $reg1, $reg2, $reg3, $string, $cc
        LDR     $reg1, =DebugTUBE
        ADR     $reg2, %FT20
10
 [ TubeType = Tube_Normal
        LDRB    $reg3, [$reg1, #R1STAT]
        TST     $reg3, #&40
        BEQ     %BT10
 ]

        LDRB    $reg3, [$reg2], #1
        TEQ     $reg3, #0
        STRNEB  $reg3, [$reg1, #R1DATA]
        BNE     %BT10
        B       %FT30
20
        =       "$string"
 [ "$cc" = ""
        =       10, 13
 ]
        =       0
        ALIGN
30
        MEND

        MACRO
$l      TubeDumpNoStack $dump, $t1, $t2, $t3
$l      MOV    $t1, #7
01
        ADRL   $t2, HexTable
        TubeChar $t3, $t2, "LDRB $t2, [$t2, $dump, LSR #28]", NoStack
        MOV    $dump, $dump, ROR #28
        SUBS   $t1, $t1, #1
        BPL    %BT01
        TubeChar $t3, $t2, "MOV $t2, #"" """, NoStack
        MEND

        MACRO
$l      TubeNewlNoStack $t1, $t2
$l      TubeChar $t1, $t2, "MOV $t2, #10", NoStack
        TubeChar $t1, $t2, "MOV $t2, #13", NoStack
        MEND

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Various constants
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

MinAplWork * 40*1024         ; minimum size of AplWork

; Fixed addresses

MEMCADR  * &3600000
ROM      * &3800000
OSMD     * &11111111

VideoPhysRam *  &02000000               ; Amazing - it's in the same place!
DRAM0PhysRam *  &10000000               ; 4 DRAM banks
DRAM1PhysRam *  &14000000
DRAM2PhysRam *  &18000000
DRAM3PhysRam *  &1C000000
DRAMBaseAddressMask * &1C000000         ; used to mask off bits after stealing video RAM
PhysSpaceSize * &20000000               ; IOMD physical map is 512M big
PhysROM *       &00000000               ; and real ROM starts at 0
 [ STB
PhysExtROM *    &01000000               ; 2nd ROM bank starts at 16M
 ]
SAMLength *     512*4                   ; SAM length in bytes for 1 bank of VRAM
EASISpacePhys * &08000000
EASISpace *     PhysSpace + EASISpacePhys

; Manifests

CR * 13
LF * 10
space * " "

; Registers

SPIRQ RN R13

; Callback byte bits:
CBack_OldStyle  * 1
CBack_Postpone  * 2
CBack_VectorReq * 4

; Set up symbols for the SWIs which aren't really there yet

AbortTrapSWI * NoSuchSWI

        SUBT    Arthur Code
        OPT     4

; *****************************************************************************
;
;  Now ready to start the code: off we go!
;
; *****************************************************************************

        ORG     ROM

        GBLS    DoMorrisROMHeader

 [ MorrisSupport
DoMorrisROMHeader SETS  " GET s.Morris"
 |
DoMorrisROMHeader SETS  ""
 ]

; now include the test code, if there is any

        GBLS    DoTestThings

 [ IncludeTestSrc
DoTestThings    SETS    " GET TestSrc.Begin"
 |
DoTestThings    SETS    ""
 ]
        $DoTestThings

 [ IncludeTestSrc
DoMorrisROMHeader SETS  ""
 ]

        $DoMorrisROMHeader


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; If there is no test code then we want a branch table at the start of ROM to
; handle reset and any aborts etc. in the reset code.
; If MorrisSupport we've already generated 16/32 ROM entry code, so skip this bit

  [ :LNOT: IncludeTestSrc :LAND: :LNOT: MorrisSupport
   [ ResetIndirected
        LDR     pc, .+ResetIndirection ; load PC, PC relative
   |
    [ MEMC_Type = "IOMD"
        B       CONT                            ; PhysROM is at zero on IOMD
    |
        B       MOSROMVecs+CONT                 ; executed out of ROM or RAM
    ]
   ]
        B       UndInstInReset
        B       SWIInReset
        B       PrefAbInReset
        B       DataAbInReset
        B       AddrExInReset
        B       IRQInReset

UndInstInReset
        SUB     pc, pc, #8
SWIInReset
        SUB     pc, pc, #8
PrefAbInReset
        SUB     pc, pc, #8
DataAbInReset
        SUB     pc, pc, #8
AddrExInReset
        SUB     pc, pc, #8
IRQInReset
        SUB     pc, pc, #8
  ]

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; This bit (up to EndFiq) is copied to location 0.  Processor vectors are
; indirected through 0 page locations so that they can be claimed using
; OS_ClaimProcessorVector.  IRQs are initially handled specially so that the
; keyboard can be handled during reset but the load is replaced with the
; standard one later on.

MOSROMVecs
        LDR     pc, MOSROMVecs+ProcVec_Branch0
        LDR     pc, MOSROMVecs+ProcVec_UndInst
        LDR     pc, MOSROMVecs+ProcVec_SWI
        LDR     pc, MOSROMVecs+ProcVec_PrefAb
        LDR     pc, MOSROMVecs+ProcVec_DataAb
        LDR     pc, MOSROMVecs+ProcVec_AddrEx
        LDR     pc, MOSROMVecs+InitKbdHandler

        MOV     r10, #IOC               ; we dunno what to do with it -
                                        ; so cut it off - right off!
        STRB    r10, [r10, #IOCFIQMSK]  ; :LSB: IOC = 0
        SUBS    pc, r14, #4
EndFiq

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; This is the table of default processor vectors which is copied to 0 page.

DefaultProcVecs
        &       RESET1
        &       UndPreVeneer
        &       SVC
        &       PAbPreVeneer
        &       DAbPreVeneer
        &       AdXPreVeneer
        &       Initial_IRQ_Code

        ASSERT  (.-DefaultProcVecs) = ProcVec_End-ProcVec_Start

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; These are preveneers which must be copied to 0 page locations so that the
; relevant handler can be branched to.  This is mainly for non-ARM600 platforms
; although the address exception preveneer (which should not actually be required
; on ARM600) is always copied.

DefaultPreVeneers
 [ No26bitCode
UndPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+UndHan
PAbPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+PAbHan
        DCD     0
 |
        DCD     0
        DCD     0
        DCD     0
 ]
AdXPreVeneer    *       ProcVecPreVeneers+(.-DefaultPreVeneers)
        LDR     PC, DefaultPreVeneers-ProcVecPreVeneers+AdXHan

        ASSERT  (.-DefaultPreVeneers) = ProcVecPreVeneersSize


 [ ResetIndirected :LAND: :LNOT: IncludeTestSrc

; We now waste space until the offset into the ROM is the same as the RAM address of ResetIndirection
; This is so that
;  a) on a reset, ROM is paged in at the bottom, so we jump to CONT, and
;  b) on a break, RAM is paged in at the bottom, so we jump to CONT_Break

        ASSERT  .-ROM <= ResetIndirection
        %       ResetIndirection-(.-ROM)
        &       CONT-ROM+PhysROM        ; address of reset code in physical space
 ]

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Now some initialised workspace/vectors that go at &100

; All the stuff from here to after the DirtyBranch instruction is read
; consecutively out of ROM, so don't put anything in between without changing
; the code


StartData
        ASSERT IRQ1V = &100
        & DefaultIRQ1V

        ASSERT ESC_Status = IRQ1V+4
        & &00FF0000       ; IOCControl set to FF on reset

        ASSERT IRQsema = ESC_Status+4
        & 0
EndData

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI return handler: checks callback

SVCDespatcher ROUT

SWIRelocation * SVCDespatcher-SWIDespatch

SLVK_SetV * {PC}-SWIRelocation

        ORR     lr, lr, #V_bit

SLVK_TestV * {PC}-SWIRelocation
 ! 0,"SLVK_TestV at ":CC:(:STR:SLVK_TestV)

        ORRVS   lr, lr, #V_bit

SLVK * {PC}-SWIRelocation
 ! 0,"SLVK       at ":CC:(:STR:SLVK)

        LDR     r11, [sp], #4
        TST     lr, #V_bit
        BEQ     %FT40

 ! 0,"VSetReturn at ":CC:(:STR:({PC}-SWIRelocation))
        TST     r11, #Auto_Error_SWI_bit
        BEQ     VSet_GenerateError + SWIRelocation

SWIReturn * {PC}-SWIRelocation
 ! 0,"SWIReturn  at ":CC:(:STR:SWIReturn)

40      MOV     r10, #0
        LDRB    r11, [r10, #CallBack_Flag]
        CMP     r11, #0
        BNE     callback_checking + SWIRelocation

SWIReturnNoCallback * {PC}-SWIRelocation
 ! 0,"SWIReturnNoCallback at ":CC:(:STR:SWIReturnNoCallback)
back_to_user * SWIReturnNoCallback
        msr AL, CPSR_c, #I32_bit + SVC32_mode           ; IRQs off for SPSR use
back_to_user_irqs_already_off * {PC}-SWIRelocation
        msr     AL, SPSR_cxsf, lr
        LDR     lr, [sp], #4
        Pull    "r10-r12"
        MOVS    pc, lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; The SWI Despatch routine

SVC * {PC}-SWIRelocation

        Push    "r10-r12"
        LDR     r11, [r14, #-4]         ; r11 = calling instruction
        STR     r14, [r13, #-4]!        ; push return address
        mrs     AL, r14, SPSR           ; r14 = saved PSR

SVCContinue * {PC}-SWIRelocation

        BIC     r11, r11, #&FF000000
        STR     r11, [r13, #-4]!        ; push SWI number
        [ StrongARM
SVC_CallASWI * {PC}-SWIRelocation       ; CallASWI,CallASWIR12 re-entry point
        ]

        BICS    r11, r11, #Auto_Error_SWI_bit
        BEQ     SWIWriteC + SWIRelocation

        AND     r10, r14, #I32_bit+F32_bit
        ORR     r10, r10, #SVC2632      ; set IFTMMMMM = IF0x0011
        msr     AL, CPSR_c, r10         ; restore caller's IRQ state

        CMP     r11, #OS_BreakPt
        CMPNE   r11, #OS_CallAVector
        BICNE   r14, r14, #V_bit        ; clear V unless BreakPoint/CallVector

        CMP     r11, #OS_WriteI
        LDRLO   pc, [pc, r11, LSL #2]

        B       NotMainMOSSwi + SWIRelocation

 ASSERT {PC}-SVCDespatcher = SWIDespatch_Size

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; The SWI table

JTABLE  & SWIWriteC             ; this entry never gets used (see ^)
        & SWIWriteS
        & SWIWrite0
        & SWINewLine

; next section is one where VectorNumber = SWINumber
        & VecSwiDespatch        ; readc
        & VecSwiDespatch        ; cli
        & NoIrqVecSwiDespatch   ; byte
        & NoIrqVecSwiDespatch   ; word
        & VecSwiDespatch        ; file
        & VecSwiDespatch        ; args
        & BGetSWI               ; bget
        & BPutSWI               ; bput
        & VecSwiDespatch        ; gbpb
        & VecSwiDespatch        ; find
        & VecSwiDespatch        ; readline

        & SCTRL
        & SWI_GetEnv_Code
        & SEXIT
        & SSTENV
        & SINTON
        & SINTOFF
        & SCALLB
        & SENTERSWI
        & SBRKPT
        & SBRKCT
        & SUNUSED
        & SSETMEMC
        & SSETCALL
        & VecMouse
        & HeapEntry
        & ModuleHandler
        & ClaimVector_SWICode
        & ReleaseVector_SWICode
        & ReadUnsigned_Routine
        & GenEvent
        & ReadVarValue
        & SetVarValue
        & GSINIT
        & GSREAD
        & GSTRANS
        & CvtToDecimal
        & FSControlSWI
        & ChangeDynamicSWI
        & GenErrorSWI
        & ReadEscapeSWI
        & ReadExpression
        & SwiSpriteOp
        & SWIReadPalette
        & Issue_Service_SWI
        & SWIReadVduVariables
        & SwiReadPoint
        & DoAnUpCall
        & CallAVector_SWI
        & SWIReadModeVariable
        & SWIRemoveCursors
        & SWIRestoreCursors
        & SWINumberToString_Code
        & SWINumberFromString_Code
        & ValidateAddress_Code
        & CallAfter_Code
        & CallEvery_Code
        & RemoveTickerEvent_Code
        & InstallKeyHandler
        & SWICheckModeValid
        & ChangeEnvironment
        & SWIClaimScreenMemory
        & ReadMetroGnome
        & XOS_SubstituteArgs_code
        & XOS_PrettyPrint_code
        & SWIPlot
        & SWIWriteN
        & Add_ToVector_SWICode
        & WriteEnv_SWICode
        & RdArgs_SWICode
        & ReadRAMFSLimits_Code
        & DeviceVector_Claim
        & DeviceVector_Release
        & Application_Delink
        & Application_Relink
        & HeapSortRoutine
        & TerminateAndSodOff
        & ReadMemMapInfo_Code
        & ReadMemMapEntries_Code
        & SetMemMapEntries_Code
        & AddCallBack_Code
        & ReadDefaultHandler
        & SWISetECFOrigin
        & SerialOp
        & ReadSysInfo_Code
        & Confirm_Code
        & SWIChangedBox
        & CRC_Code
        & ReadDynamicArea
        & SWIPrintChar
        & ChangeRedirection
        & RemoveCallBack
        & FindMemMapEntries_Code
        & SWISetColour
        & NoSuchSWI                     ; Added these to get round OS_ClaimSWI and
        & NoSuchSWI                     ; OS_ReleaseSWI (should not have been allocated here).
 [ AssemblePointerV
        & PointerSWI
 |
        & NoSuchSWI
 ]
        & ScreenModeSWI
        & DynamicAreaSWI
        & AbortTrapSWI
 [ CPU_Type = "ARM600"
        & MemorySWI
 |
        & NoSuchSWI
 ]
 [ ProcessorVectors
        & ClaimProcVecSWI
 |
        & NoSuchSWI
 ]
        & PerformReset
 [ CPU_Type = "ARM600"
        & MMUControlSWI
 |
        & NoSuchSWI
 ]
 [ STB
        & NoSuchSWI
 |
        & ResyncTimeSWI
 ]
 [ StrongARM
        & PlatFeatSWI
        & SyncCodeAreasSWI
        & CallASWI
        & AMBControlSWI
        & CallASWIR12
 |
        & NoSuchSWI
        & NoSuchSWI
        & NoSuchSWI
        & NoSuchSWI
        & NoSuchSWI
 ]
; The following SWIs are not available in this kernel.
	& NoSuchSWI	; SpecialControl
	& NoSuchSWI	; EnterUSR32SWI
	& NoSuchSWI	; EnterUSR26SWI
; End of unavailable SWIs.
; Should not cause any problems on any machine.  STB flag just to be safe though.
 [ STB :LAND: {TRUE}
	& VIDCDividerSWI
 |
	& NoSuchSWI
 ]
        & NVMemorySWI

MaxSwi * (.-JTABLE)/4

 ASSERT MaxSwi < OS_ConvertStandardDateAndTime

; SWIs for time/date conversion are poked in specially

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; The fudge branch to exit a dirty SWI handler

DirtyBranch
        B       SLVK +DirtyBranch-BranchToSWIExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 [ StrongARM
;StrongARM needs these

;SWI number passed in r10
CallASWI ROUT
        LDR     r11, [sp, #8]        ;pick-up target SWI code (r10 pushed by dispatcher)
        BIC     r11, r11, #&FF000000 ;just in case
        STR     r11, [sp, #0]        ;CallASWI now incognito as target SWI
        B       SVC_CallASWI         ;re-dispatch

;SWI number passed in r12 (better for C veneers)
CallASWIR12 ROUT
        LDR     r11, [sp, #16]       ;pick-up target SWI code (r12 pushed by dispatcher)
        BIC     r11, r11, #&FF000000 ;just in case
        STR     r11, [sp, #0]        ;CallASWIR12 now incognito as target SWI
        B       SVC_CallASWI         ;re-dispatch
 ]


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    return address, r10-r12 stacked, lr has SPSR for return

VSet_GenerateError ROUT

        Push    lr
        MOV     r1, #Service_Error
        BL      Issue_Service

        MOV     r10, #ErrorV
        BL      CallVector              ; Normally gets to default handler...

        Pull    lr                      ; which raises error; otherwise just
        BIC     lr, lr, #V_bit          ; return with V clear: error claimed!
        B       SWIReturn

        LTORG

; ....................... default owner of ErrorV .............................
; In    r0  -> error in current error block

; Out   Exits to user's error handler routine as given by ErrHan
;       r1-r9 possibly corrupt. Indeed r10-r12 MAY be duff ... eg. REMOTE

ErrHandler ROUT

        BL      OscliTidy               ; close redirection, restore curr FS

        MOV     r10, #0
        LDR     r11, [r10, #ErrBuf]     ; Get pointer to error buffer

  [ No26bitCode
        LDR     sp_svc, =SVCSTK-4*4     ; Just below top of stack
        Pull    r14
        STR     r14, [r11], #4          ; Return PC for error
  |
        LDR     sp_svc, =SVCSTK-5*4     ; Just below top of stack
        Pull    r14                     ; PSR will be on stack if error at top level
        FakeLR  r12                     ; Fake up the PC+PSR
        STR     r14, [r11], #4
  ]

        LDR     r14, [r0], #4           ; Copy error number
        STR     r14, [r11], #4

        ; Copy error string - truncating at 252
        MOV     r12, #256-4

10      LDRB    r14, [r0], #1
        SUBS    r12, r12, #1
        MOVLS   r14, #0
        STRB    r14, [r11], #1
        TEQ     r14, #0
        BNE     %BT10

        LDR     r14, [r10, #ErrHan]     ; And go to error handler
  [ :LNOT: No26bitCode
        BIC     r14, r14, #ARM_CC_Mask
  ]
        STR     r14, [r10, #Curr_Active_Object]
        LDR     r0,  [r10, #ErrHan_ws]  ; r0 is his wp

        LDRB    r11, [r10, #CallBack_Flag]
        CMP     r11, #0

        mrs    ,r12, CPSR
        BIC     r12, r12, #I32_bit+F32_bit+&0F  ; USR26/32 mode, ARM, IRQs enabled

  [ :LNOT: NoSPSRcorruption
        msr EQ, CPSR_c, #I32_bit+SVC32_mode ; disable interrupts for SPSR use
  ]
        msr EQ, SPSR_cxsf, r12

        Pull    "r10-r12", EQ
        MOVEQS  pc, r14                 ; USR mode, IRQs enabled

        Push    r14                     ; Stack return address
        MOV     r14, r12                ; Put PSR in R14
        B       Do_CallBack             ; Can't need postponement, r0,r14,stack
                                        ; such that callback code will normally
                                        ; call error handler

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; check for CallBack possible

callback_checking

        TST     lr, #I32_bit+&0F        ; user 26/32 mode, ints enabled?
 [ {FALSE} ; original code
        Pull    "r10-r12", NE
        MOVNES  pc, lr                  ; Skip the branch for SVC code speed
 |
        BNE     SWIReturnNoCallback     ; hey, it'll be in the cache (probably)
 ]

; Further checks: postpone callback if returning V set and R0->RAM

        TST     lr, #V_bit
        BEQ     Do_CallBack
        TST     r11, #CBack_Postpone    ; only one postponement
        BNE     Do_CallBack             ; allowed.
        CMP     r0, #ROM
        BGE     Do_CallBack

        msr    ,CPSR_c, #I32_bit + SVC32_mode  ; ints off while flag updated
        LDRB    r11, [r10, #CallBack_Flag]
        ORR     r11, r11, #CBack_Postpone      ; signal to IRQs
        STRB    r11, [r10, #CallBack_Flag]
        B       back_to_user_irqs_already_off

Do_CallBack                                    ; CallBack allowed:
        TST     r11, #CBack_VectorReq          ; now process any vector entries
        MOV     r12,lr
        BLNE    process_callback_chain
        MOV     lr,r12

        TST     r11, #CBack_OldStyle
        BEQ     back_to_user
 [ :LNOT:No26bitCode
        TST     r14, #&10                       ; must be returning to 26-bit
        BNE     back_to_user                    ; on 26-bit systems
 ]
 [ {TRUE}                                       ; LRust, Fix RP-0609
; Check that SVC_sp is empty (apart from r14,r10-r12), i.e. system truly is idle

        LDR     r11, =SVCSTK-4*4                ; What SVC_sp should be if system idle
        CMP     sp, r11                         ; Stack empty?
        BLO     back_to_user                    ; No then no call back
 ]
        msr    ,CPSR_c, #I32_bit + SVC2632      ; ints off while flag updated
        LDRB    r11, [r10, #CallBack_Flag]
        BIC     r11, r11, #CBack_Postpone+CBack_OldStyle
        STRB    r11, [r10, #CallBack_Flag]

        MOV     r12, #0
        LDR     R12, [R12, #CallBf]
  [ No26bitCode
        STR     r14, [r12, #4*16]             ; user PSR
        Pull    r14
        STR     r14, [r12, #4*15]             ; user PC
  |
        FakeLR  r10
        STR     r14, [r12, #4*15]             ; user PC/PSR
  ]
        MOV     r14, r12
        Pull   "r10-r12"
  [ SASTMhatbroken
        STMIA   r14!,{r0-r12}
        STMIA   r14,{r13,r14}^                ; user registers
        NOP                                   ; doesn't matter that r14 is different
  |
        STMIA   r14, {r0-r14}^                ; user registers
  ]

        MOV     R12, #CallAd_ws
        LDMIA   R12, {R12, PC}                ; jump to CallBackHandler


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Also called from source.pmf.key, during readc

process_callback_chain ROUT

        Push   "r0-r6, r10-r12, lr"             ; save some for the callee too.
        MOV     r10, #0

        msr    ,CPSR_c, #I32_bit + SVC2632      ; ints off while flag updated
        LDRB    r11, [r10, #CallBack_Flag]
        BIC     r11, r11, #CBack_VectorReq
        STRB    r11, [r10, #CallBack_Flag]

01
        msr    ,CPSR_c, #I32_bit + SVC2632      ; ints off while flag updated
        MOV     r2, #0
        LDR     r2, [r2, #CallBack_Vector]
        TEQ     r2, #0
      [ No26bitCode
        msr EQ ,CPSR_c, #SVC2632                ; ensure exit with ints on
        Pull   "r0-r6, r10-r12, PC",EQ
      |
        Pull   "r0-r6, r10-r12, PC",EQ,^
      ]

        LDMIA   r2, {r10, r11, r12}             ; link, addr, r12
        MOV     r0, #HeapReason_Free
        STR     r10, [r0, #CallBack_Vector-HeapReason_Free] ; Keep head valid

        msr    ,CPSR_c, #SVC2632                ; enable ints for long bits

  [ ChocolateSysHeap
        ASSERT  ChocolateCBBlocks = ChocolateBlockArrays + 0
        MOV     r1,#ChocolateBlockArrays
        LDR     r1,[r1,#0]
        BL      FreeChocolateBlock
        LDRVS   r1, =SysHeapStart
        SWIVS   XOS_Heap
  |
        LDR     r1, =SysHeapStart
        SWI     XOS_Heap
  ]

        MOV     lr, pc
        MOV     pc, r11                         ; call im, with given r12

        B       %BT01                           ; loop

        LTORG

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_WriteC

; In    r11 = 0 (look, up there ^) !

SWIWriteC ROUT

        msr    ,CPSR_c, #SVC2632        ; enable interrupts

        BIC     lr, lr, #V_bit          ; clear caller's V cos we didn't before
        Push    lr

        LDR     r11, [r11, #VecPtrTab+WrchV*4] ; load top node pointer
        CMP     r11, #ROM
  [ StrongARM
        BCC     WrchThruVector
        Push    pc, CS                 ; need to get to ReturnFromVectoredWrch - push PC+12 (old ARM) or PC+8 (StrongARM)
        BCS     PMFWrchDirect
        MOV     R0,R0                  ; NOP for PC+8
  |
        Push    pc, CS                 ; push address of ReturnFromVectoredWrch (PC+12)
        BCS     PMFWrchDirect
        BCC     WrchThruVector
  ]
ReturnFromVectoredWrch
        Pull    lr
        B       SLVK_TestV


WrchThruVector
        MOV     r10, #WrchV
        BL      CallVector
        B       ReturnFromVectoredWrch

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

SWINewLine ROUT

        MOV     r11, lr
        SWI     XOS_WriteI+10
        SWIVC   XOS_WriteI+13
        MOV     lr, r11
        B       SLVK_TestV

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_WriteI+n

SWIWriteI ROUT

        MOV     r10, r0
        AND     r0, r11, #&FF
        MOV     r11, lr                 ; NB. Order !!!
        SWI     XOS_WriteC
        MOVVC   r0, r10
        MOV     lr, r11
        B       SLVK_TestV              ; return setting V

; .............................................................................
; define module SWI node format

ModSWINode_CallAddress * 0
ModSWINode_MListNode   * 4
ModSWINode_Link        * 8
ModSWINode_Number      * 12
ModSWINode_Size        * 16     ; not a field - the node size!

        MACRO
$l      ModSWIHashvalOffset     $swino, $startreg
     [ "$startreg"=""
$l      MOV     $swino, $swino, LSR #4
     |
$l      MOV     $swino, $startreg, LSR #4
     ]
        AND     $swino, $swino, #(ModuleSHT_Entries-1)*4
        MEND

        MACRO
$l      ModSWIHashval   $swino, $startreg
$l      ModSWIHashvalOffset $swino, $startreg
        ADD     $swino, $swino, #ModuleSWI_HashTab
        MEND



NotMainMOSSwi ; Continuation of SWI despatch

        CMP     R11, #&200
        BCC     SWIWriteI

; .............................................................................
; Look round RMs to see if they want it

ExtensionSWI ROUT

        Push    "r9, lr"                        ; first construct the link to pass on.
      [ No26bitCode
        ADR     lr, %FT02
      |
        AND     r10, lr, #&F0000000             ; copy in user CCodes
        AND     r12, lr, #F32_bit+I32_bit       ; (inc. IRQ state)
        ORR     r10, r10, r12, LSL #IF32_26Shift
        ADR     lr, %FT02 + SVC_mode
        ORR     lr, lr, r10
      ]

        BIC     r12, r11, #Module_SWIChunkSize-1
        ModSWIHashvalOffset r10, r12
        LDR     r10, [r10, #ModuleSWI_HashTab]
loopoverhashchain
        CMP     r10, #0
        BEQ     VectorUserSWI
        LDR     r9, [r10, #ModSWINode_Number]
        CMP     r9, r12
        LDRNE   r10, [r10, #ModSWINode_Link]
        BNE     loopoverhashchain

        LDMIA   r10, {r10, r12}
        LDR     r12, [r12, #Module_incarnation_list]  ; preferred life
        CMP     r12, #0

 [ FixR9CorruptionInExtensionSWI
        Pull    "r9", NE                ; restore corrupted r9 before calling SWI handler
                                        ;RCM added 'NE' above to fix MED=04655
 ]

        ANDNE   r11, r11, #Module_SWIChunkSize-1
        ADDNE   r12, r12, #Incarnation_Workspace
        MOVNE   pc, r10


VectorUserSWI                   ; Not in a module, so call vec
 [ FixR9CorruptionInExtensionSWI
        Pull    "r9"            ; restore corrupted r9 before calling UKSWIV
 ]
        MOV     r10, #UKSWIV    ; high SWI number still in R11
        B       CallVector      ; lr still has user CCs (if 26bit) & points at%FT02


02
 [ FixR9CorruptionInExtensionSWI
        Pull    "lr"                    ; r9 already pulled off stack before calling SWI handler or UKSWIV
 |
        Pull    "r9,lr"
 ]
        mrs     AL, r10, CPSR
        BIC     lr, lr, #&F0000000      ; Can mangle any/all of punter NZCV flags
        AND     r10, r10, #&F0000000
        ORR     lr, lr, r10
        B       SLVK

; ....................... default owner of UKSWIV .............................
; Call UKSWI handler
; Also used to call the upcall handler

; In    r12 = HiServ_ws (or UpCallHan_ws)

CallUpcallHandler
HighSWI ROUT                          ; no one on vec wants it: give to handler

        Pull    lr                    ; the link pointing at %BT02 to pass in.
        LDMIA   r12, {r12, pc}

; ........................ default UKSWI handler ..............................

NoSuchSWI ROUT

        Push    lr
        BL      NoHighSWIHandler
        Pull    lr
        B       SLVK_SetV

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; .................... default Unused SWI handler .............................

NoHighSWIHandler ROUT

        MOV     r0, #0
        LDR     r0, [r0, #IRQsema]
        CMP     r0, #0
        ADR     r0, ErrorBlock_NoSuchSWI ; Must return static error here
 [ No26bitCode
        BEQ     %FT01
        SETV
        MOV     pc, lr
01
 |
        ORRNES  pc, lr, #V_bit
 ]

; Not in IRQ: can safely build a dynamic error
      [ International
        Push    "r1-r4, lr"
        SUB     sp, sp,#12
        MOV     r1, sp
        MOV     r2, #12
        MOV     r0, r11
        SWI     XOS_ConvertHex6         ; SWI argument is 00xxxxxx

        MOV     r4, r0                  ; now strip leading 0s
02      LDRB    r2, [r4], #1
        CMP     r2, #"0"
        BEQ     %BT02

        SUB     r4,r4,#1
        ADR     r0, ErrorBlock_NoSuchSWI1
        BL      TranslateError_UseR4
        ADD     sp,sp,#12

        Pull    "r1-r4, lr"
 [ No26bitCode
        SETV
        MOV     pc, lr
 |
        ORRS    pc, lr, #V_bit
 ]

        MakeErrorBlock NoSuchSWI1

      |
        Push    "r1-r3, lr"
        LDR     r1, =EnvString
        LDMIA   r0!, {r2, r3}           ; number, "SWI "
        STMIA   r1!, {r2, r3}
        MOV     r2, #"&"
        STRB    r2, [r1], #1
        MOV     r3, r0
        MOV     r0, r11
        MOV     r2, #256
        SWI     XOS_ConvertHex6         ; SWI argument is 00xxxxxx

; now strip leading 0s

        MOV     r1, r0
02      LDRB    r2, [r1], #1
        CMP     r2, #"0"
        BEQ     %BT02
        CMP     r2, #0
        ADDEQ   r1, r0, #1
        BEQ     %FT03
04      STRB    r2, [r0], #1
        LDRB    r2, [r1], #1
        CMP     r2, #0
        BNE     %BT04
        MOV     r1, r0
03      MOV     r2, #" "
01      STRB    r2, [r1], #1
        CMP     r2, #0
        LDRNEB  r2, [r3], #1
        BNE     %BT01

        Pull    "r1-r3, lr"
        LDR     r0, =EnvString
 [ No26bitCode
        SETV
        MOV     pc, lr
 |
        ORRS    pc, lr, #V_bit
 ]
       ]

        MakeErrorBlock NoSuchSWI


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Fast SWI handlers for BGet and BPut caches

BGetSWI ROUT                            ; Done separately for highest speed

        Push    lr
        MOV     r10, #BGetV             ; Cache hit failed, call victor
        BL      CallVector
        Pull    lr                      ; Punter lr has VClear
        BIC     lr, lr, #C_bit          ; Copy C,V to punter lr
        ORRCS   lr, lr, #C_bit
        B       SLVK_TestV

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

BPutSWI ROUT                            ; Done separately for highest speed

        Push    "lr"
        MOV     r10, #BPutV                     ; Cache hit failed, call victor
        BL      CallVector
        Pull    "lr"                            ; Destack lr(VC)
        B       SLVK_TestV

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI handlers for all the vectored SWIs that have vecnum=swinum

; All defined to affect C & V at most

FSControlSWI ROUT

        MOV     r11, #FSCV              ; Pretend to be vecnum = swinum swi
                                        ; and just drop through to ...

VecSwiDespatch ROUT

        Push    lr                      ; this is user's link (or PSR in 32-bit case)
        MOV     r10, r11                ; SWI number from R11->R10

  [ No26bitCode
        mrs     AL, r11, CPSR
        AND     r14, r14, #&F0000000    ; extract caller's CCs
        BIC     r11, r11, #&F0000000    ; mask out ours
        BIC     r11, r11, #I32_bit      ; enable IRQs
        ORR     r11, r11, r14           ; add in CCs
        msr    ,CPSR_cf, r11            ; and set it all up
  |
        ORR     r14, lr, #SVC_mode
        BIC     r14, r14, #I_bit        ; Enable IRQs
        TEQP    r14, #0
  ]

        BL      CallVector

; So the vectored routine can update the pushed link CCodes if wanted
; No update return is therefore LDMIA stack!, {PC}^ (sort of)
; Update return pulls lr, molests it, then MOVS PC, lr
; Note either return enables IRQ, FIQ

; ???? Is the DEFAULT owner allowed to corrupt r10,r11 IFF he claims it ????

        Pull    lr                      ; Punter lr has VClear
        BICCC   lr, lr, #C_bit          ; Copy C,V to punter lr
        ORRCS   lr, lr, #C_bit
        B       SLVK_TestV


NoIrqVecSwiDespatch ROUT

        Push    lr                      ; this is user's link
        MOV     r10, r11                ; SWI number from R11->R10
  [ No26bitCode
        mrs     AL, r11, CPSR
        AND     r14, r14, #&F0000000    ; extract caller's CCs
        BIC     r11, r11, #&F0000000    ; mask out ours
        ORR     r11, r11, #I32_bit      ; disable IRQs
        ORR     r11, r11, r14           ; add in CCs
        msr     AL, CPSR_cf, r11        ; and set it all up
  |
        ORR     r14, lr, #SVC_mode+I_bit ; Disable IRQ
        TEQP    r14, #0
  ]
        BL      CallVector
        Pull    lr                      ; Punter lr has VClear
        BICCC   lr, lr, #C_bit          ; Copy C,V to punter lr
        ORRCS   lr, lr, #C_bit
        B       SLVK_TestV

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_GetEnv

SWI_GetEnv_Code ROUT

        LDR     r0, =EnvString
        MOV     r1, #0
        LDR     r1, [r1, #MemLimit]
        LDR     r2, =EnvTime
        B       SLVK

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_Exit

SEXIT ROUT

        BL      OscliTidy               ; shut redirection, restore FS

; now see if it's an abort Exit

        LDR     r12, ABEX
        CMP     r1, r12
 [ No26bitCode
        TSTEQ   r0, #3
 |
        TSTEQ   r0, #ARM_CC_Mask
 ]
        MOVNE   r2,  #0
        MOV     r12, #0
        STR     r2,  [r12, #ReturnCode]
        LDR     r12, [r12, #RCLimit]
        CMP     r2, r12
        SWIHI   OS_GenerateError        ; really generate an error

        ADD     sp, sp, #8              ; junk SWI no and R14 on stack
        Pull    "r10-r12"
        MOV     r0, #0
        LDR     lr, [r0, #SExitA]
        STR     lr, [r0, #Curr_Active_Object]
        LDR     r12, [r0, #SExitA_ws]
        LDR     sp_svc, =SVCSTK
 [ No26bitCode
        mrs     AL, r0, CPSR
   [ :LNOT: NoSPSRcorruption
        msr    ,CPSR_c, #I32_bit+SVC2632 ; IRQs off (to protect SPSR_svc)
   ]
        BIC     r0, r0, #I32_bit+F32_bit+&0F
        msr     AL, SPSR_cxsf, r0       ; Get ready for USR26/32, IRQs on
        MOVS    pc, lr                  ; lr->pc, SPSR->CPSR
 |
        BICS    pc, lr, #ARM_CC_Mask
 ]

ABEX    =       "ABEX"                  ; Picked up as word

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_CallBack: Set/read callback buffer and handler

SCALLB  MOV     r10, #CallBackHandler

handlecomm
        Push    "r2, r3, lr"
        MOV     r3, r0          ; buffer
        MOV     r0, r10
        BL      CallCESWI
        MOV     r0, r3
        Pull    "r2, r3, lr"
        B       SLVK_TestV

; .............................................................................
; SWI OS_BreakSet: Set/read breakpoint buffer and handler

SBRKCT  MOV     r10, #BreakPointHandler
        B       handlecomm

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_ReadEscapeState

ReadEscapeSWI ROUT

        MOV     r10, #0
        LDRB    r10, [r10, #ESC_Status]
        TST     r10, #1 :SHL: 6
        BICEQ   lr, lr, #C_bit
        ORRNE   lr, lr, #C_bit
        B       SLVK

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_ServiceCall

Issue_Service_SWI ROUT

        Push    lr
        BL      Issue_Service
        Pull    lr
        B       SLVK

 [ StrongARM
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI XOS_PlatformFeatures

PlatFeatSWI ROUT
        Push    lr
        CMP     r0, #0                  ;Is it a known reason code?
        BNE     %FT50                   ;No, so send out a service call

        ;Ok, it's the 'code_features' reason code.
        LDRB    r0,[r0, #ProcessorFlags]
        TST     r0, #2                  ;Is the 'no irq enable/disable' bit set?
        ADRNE   r1, platfeat_irqinsert  ;Yep, so point R1 to the delay routine
        MOVEQ   r1, #0
        Pull    lr
        B       SLVK                    ;Return

platfeat_irqinsert
        MOV     r0, r0
        MOV     r0, r0
        MOV     r0, r0
        MOV     r0, r0
        MOV     r0, r0
        MOV     pc, lr

50
  [ {FALSE}
        Push    "r1-r8"
        MOV     r1, #Service_UnknownPlatformFeatures
        Pull    "r2-r9"
        BL      Issue_Service
        CMP     r1, #0
        BNE     %FT75
        Push    "r2-r9"
        Pull    "r1-r8"
        B       SLVK                    ;Return
  ]

75      ;Get here if the service call isn't claimed.
        ADR     R0,platfeaterror
    [ International
        Push    "lr"
        BL      TranslateError
        Pull    "lr"
    ]
        B       SLVK_SetV

platfeaterror
        &       0
    [ International
        =       "BadPlatReas", 0
    |
        =       "Unknown OS_PlatformFeatures reason code", 0
    ]
        ALIGN
  ]

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; SWI OS_GenerateError

GenErrorSWI * SLVK_SetV

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        END