; This source code in this file is licensed to You by Castle Technology
; Limited ("Castle") and its licensors on contractual terms and conditions
; ("Licence") which entitle you freely to modify and/or to distribute this
; source code subject to Your compliance with the terms of the Licence.
; 
; This source code has been made available to You without any warranties
; whatsoever. Consequently, Your use, modification and distribution of this
; source code is entirely at Your own risk and neither Castle, its licensors
; nor any other person who has contributed to this source code shall be
; liable to You for any loss or damage which You may suffer as a result of
; Your use, modification or distribution of this source code.
; 
; Full details of Your rights and obligations are set out in the Licence.
; You should have received a copy of the Licence with this source code file.
; If You have not received a copy, the text of the Licence is available
; online at www.castle-technology.co.uk/riscosbaselicence.htm
; 
        SUBT    Arthur.  File => &.Arthur.Econet.Module
        TTL     Econet for Arthur.

        GET     Hdr:ListOpts
        GET     Hdr:Macros
        GET     Hdr:System
        GET     Hdr:CPU.ARM600
        GET     Hdr:IO.IOC-A1
        GET     Hdr:VIDC.VIDC1A ; VIDC20
        GET     Hdr:MEMM.MEMC1  ; ARM600

        GET     Hdr:Symbols

        GET     Hdr:FSNumbers
        GET     Hdr:CMOS
        GET     Hdr:ModHand
        GET     Hdr:HostFS
        GET     Hdr:Debug
        GET     Hdr:Econet
        GET     Hdr:Services
        GET     Hdr:NewErrors
        GET     Hdr:Tokens
        GET     Hdr:MsgTrans
        GET     Hdr:Portable
        GET     Hdr:Podule
        GET     Hdr:PoduleReg

        GET     s.Macros

        GET     Time+Date

MySWIChunkBase  *       EconetSWI_Base
                ASSERT  MySWIChunkBase = Econet_CreateReceive

        ;       Code configuration constants

BridgeWaitTime  *       300                             ; Three seconds
ClaimTime       *       1000                            ; Ten seconds
PowerDownTimeout *      1600                            ; Sixteen seconds
MaxTxSize       *       8192                            ; 8Kbytes


        ;       Code configuration variables

                GBLL    PerthPowerDown
                GBLL    UsePortableModule
                GBLL    PoduleCapable
                GBLL    ReceiveInBackground
                GBLL    BroadcastByHand

PerthPowerDown  SETL    (:LNOT: OldOs) :LAND: True
UsePortableModule SETL  (:LNOT: OldOs) :LAND: True
PoduleCapable   SETL    (:LNOT: OldOs) :LAND: True
ReceiveInBackground SETL True
BroadcastByHand SETL    ReceiveInBackground :LAND: True

        ;       Debug code configuration variables

                GBLL    ErrorInfo
                GBLL    ControlBlocks
                GBLL    PortInfo
                GBLL    Variables

ErrorInfo       SETL    (:LNOT: ReleaseVersion) :LAND: True
ControlBlocks   SETL    (:LNOT: ReleaseVersion) :LAND: True
PortInfo        SETL    (:LNOT: ReleaseVersion) :LAND: True
Variables       SETL    (:LNOT: ReleaseVersion) :LAND: True

        ;       Debugging controls

                GBLL    Debug
                GBLL    DebugIRQ
                GBLL    DebugSWIs
                GBLL    DebugFindHardware
                GBLL    DebugPowerDown
                GBLL    DebugFindLocalNet
                GBLL    NoAbandons
                GBLL    Host_Debug
                GBLL    Debug_MaybeIRQ
                GBLL    Analyser
                GBLL    DebugFIQ

Debug           SETL    (:LNOT: ReleaseVersion) :LAND: False
DebugIRQ        SETL    (:LNOT: ReleaseVersion) :LAND: False
DebugSWIs       SETL    (:LNOT: ReleaseVersion) :LAND: False
DebugFindHardware SETL  (:LNOT: ReleaseVersion) :LAND: (:LNOT: OldOs) :LAND: False
DebugPowerDown  SETL    (:LNOT: ReleaseVersion) :LAND: (:LNOT: OldOs) :LAND: False  ; Using the border colours
DebugFindLocalNet SETL  (:LNOT: ReleaseVersion) :LAND: False
NoAbandons      SETL    Debug :LAND: ControlBlocks :LAND: False
Host_Debug      SETL    False
Debug_MaybeIRQ  SETL    True
Analyser        SETL    (:LNOT: ReleaseVersion) :LAND: False
DebugFIQ        SETL    (:LNOT: ReleaseVersion) :LAND: False

                GBLS    Alignment
Alignment       SETS    "16, 12"

Origin
        DCD     0                                       ; Start
        DCD     InitModule - Origin                     ; Initialisation
        DCD     KillModule - Origin                     ; Die
        DCD     Service - Origin
        DCD     ModuleTitle - Origin
        DCD     HelpString - Origin
        DCD     HelpTable - Origin                      ; Command Table
        DCD     MySWIChunkBase
        DCD     SVCEntry - Origin
        DCD     SWINameTable - Origin

        GET     s.Memory

        SUBT    Module entry stuff
        OPT     OptPage

HelpString
        DCB     "Econet", 9, 9, "5.", CurrentVersion
        [       ReleaseVersion
        !       0, "Assembling Econet 5.$CurrentVersion$OsString"
        |
        DCB     "/", CurrentIteration
        !       0, "Assembling Econet 5.$CurrentVersion/$CurrentIteration$OsString"
        ]
        DCB     " (", CurrentDate, ")"
        [       :LNOT: ReleaseVersion
        DCB     " assembled  at ", CurrentTime, "."
        ]
        DCB     "$OsString", 0
        ALIGN

MachineVersionNumber
        DCB     7                                       ; Archimedes type byte
        DCB     0                                       ; Acorn Computers
        ; The following converts a two character string expected to
        ; contain "00" to "99" to a BCD value ( in 'Vers' ).
        GBLA    Vers
        GBLA    Count
Count   SETA    48                                      ; Ascii "0"
        WHILE   :CHR: Count <> CurrentVersion :LEFT: 1  ; Is Count the MS digit of CurrentVersion?
Count   SETA    Count + 1                               ; Increment Count and try again
        WEND
        ASSERT  Count>=48 :LAND: Count<=57
Vers    SETA    16 * (Count - 48)                       ; Reduce to binary and put in MS BCD digit
Count   SETA    48                                      ; Ascii "0"
        WHILE   :CHR: Count <> CurrentVersion :RIGHT: 1
Count   SETA    Count + 1
        WEND
        ASSERT  Count>=48 :LAND: Count<=57
Vers    SETA    Vers + (Count - 48)
        [       ReleaseVersion                          ; On release version use Vers
        DCB     Vers
        |                                               ; Otherwise use Vers + 01
        [       ( Vers :AND: &F ) = 9                   ; So that this has the same number
        DCB     Vers + 7                                ; as the release version to follow
        |
        DCB     Vers + 1
        ]
        ]
        DCB     5

        SUBT    Initialisation code
        OPT     OptPage

InitModule ROUT
        Push    lr
        ;       R0-R6 ==> Trashable
        ;       R7-R9 ==> Must be preserved
        ;       R10   ==> Pointer to environment string
        ;       R11   ==> I/O base or instantiation number
        ;       R12   ==> Pointer to private word
        ;       R13   ==> Valid stack pointer
        MOV     r6, r10                                 ; Save the environment string
        [       :LNOT: OldOs
        MOV     r0, #PortableControl_EconetEnable
        MVN     r1, #PortableControl_EconetEnable
        SWI     XPortable_Control                       ; Ignore possible error
        ]
        PHPSEI  r5                                      ; Check for the hardware
        [       DebugFindHardware
        MOV     r4, #0
        ]
        [       PoduleCapable
        LDR     r10, =Podule_BaseAddressANDMask
        AND     r10, r11, r10                           ; Get the pure base address
        [       DebugFindHardware
        DREG    r10, "Separated hardware address = &"
        ]
        CMP     r10, #&03000000
        BGT     AddressKnown
        SWI     XPodule_ReturnNumber
        BVS     AddressUnknown
        MOV     r3, r0                                  ; Number of podules
FindPoduleLoop
        DECS    r3
        BMI     AddressUnknown
        [       DebugFindHardware
        BREG    r3, "Podule_ReadInfo: R3 = &"
        ]
        MOV     r0, #Podule_ReadInfo_SyncBase + Podule_ReadInfo_ID + Podule_ReadInfo_Type
        MOV     r11, #-1
        Push    "r10, r11, r14"                         ; Create a stack frame
        MOV     r1, sp
        MOV     r2, #12
        SWI     XPodule_ReadInfo
        Pull    "r10, r11, r14"
        [       DebugFindHardware
        DREG    r10, "Syncronous base address = &"
        BREG    r11, "ID = &"
        DREG    r14, "Type = &"
        ]
        BVS     FindPoduleLoop
        TEQ     r11, #SimpleType_Econet * 8
        BEQ     AddressKnown
        TEQ     r11, #0
        TEQEQ   r14, #ProdType_Econet
        BEQ     AddressKnown
        B       FindPoduleLoop

AddressUnknown
        LDR     r10, =EconetController                  ; If not from a podule
AddressKnown
        [       DebugFindHardware
        DREG    r10, "Address we are using = &"
        ]
        |
        LDR     r10, =EconetController
        ]
        MOV     r0, #2_11111000                         ; TxRst, RxRst, FrDisc, TxDataSrvRq, RxDataSrvRq, -TIE, -RIE, -AC
        STRB    r0, CReg1
        MOV     r0, #2_01111111                         ; -RTS, ClrTxSt, ClrRxSt, TxLast, FrComp, Flag, 2Byte, PSE
        STRB    r0, CReg2
        LDRB    r1, SReg1
        [       DebugFindHardware
        BREG    r1, "Test #1  SReg1  = &"
        ]
        TST     r1, #2_11101101                         ; IRQ, FC, TxU, -CTS, FlagDet, Loop, -SR2Rq, RDA
        [       DebugFindHardware
        MOVNE   r4, #"1"
        ]
        BNE     NoHardware                              ; Any of these bits means no hardware
        MOV     r0, #2_01111111                         ; -RTS, ClrTxSt, ClrRxSt, TxLast, FrComp, Flag, 2Byte, PSE
        STRB    r0, CReg2
        LDRB    r2, SReg2
        [       DebugFindHardware
        BREG    r2, "Test #2  SReg2  = &"
        ]
        TST     r2, #2_11011011                         ; RxDataAvail, RxOverrun, -DCD, FCSErr, RxAbort, -Idle, FV, AP
        [       DebugFindHardware
        MOVNE   r4, #"2"
        ]
        BNE     NoHardware                              ; Any of these bits means no harware
        MOV     r0, #0                                  ; Write a zero out onto the bus
        STR     r0, CReg4
        LDRB    r0, SReg1                               ; Read them back in
        LDRB    r1, SReg2                               ; One by one to see
        [       DebugFindHardware
        BREG    r0, "Test #3  SReg1  = &"
        BREG    r1, "Test #3  SReg2  = &"
        ]
        TEQ     r0, r1                                  ; If they are all the same
        BNE     Hardware                                ; If they aren't then there is something there
        LDRB    r1, RxByte
        [       DebugFindHardware
        BREG    r1, "Test #3  RxByte = &"
        ]
        TEQ     r0, r1
        BNE     Hardware
        LDRB    r1, CReg4
        [       DebugFindHardware
        BREG    r1, "Test #3  RxByte = &"
        ]
        TEQ     r0, r1
        BNE     Hardware
        TEQ     r0, #0                                  ; Were they all zero ?
        [       DebugFindHardware
        MOVNE   r4, #"3"
        ]
        BNE     NoHardware                              ; All zero means that there was nothing there but empty space

        MOV     r0, #2_10000010                         ; TxReset, RxIE, AC=0
        STRB    r0, CReg1
        MOV     r0, #2_11100101                         ; RTS, ClrTxStatus, ClrRxStatus, FlagIdle, 1Byte, PSE
        STRB    r0, CReg2
        MOV     r0, #&100000                            ; A delay is needed here
26                                                      ; for the turning on of RTS to take effect ??
        DECS    r0
        BNE     %26
        LDRB    r1, SReg1
        [       DebugFindHardware
        BREG    r1, "Test #4  SReg1  = &"
        ]
        TST     r1, #2_11101101                         ; IRQ, FC, TxU, -CTS, FlagDet, Loop, -SR2Rq, RDA
        BNE     Hardware                                ; Any bits set means there was something there
        LDRB    r2, SReg2
        [       DebugFindHardware
        BREG    r2, "Test #4  SReg2  = &"
        ]
        TST     r2, #2_11011011                         ; RxDataAvail, RxOverrun, -DCD, FCSErr, RxAbort, -Idle, FV, AP
        BNE     Hardware                                ; Any bits set means there was something there
        [       DebugFindHardware
        MOV     r4, #"4"
        ]
NoHardware
        PLP     r5
        [       UseMsgTrans
        [       DebugFindHardware
        TEQ     r4, #0
        BEQ     %92
        Push    "r1, r2"
        DEC     sp, 12
        SWI     OS_WriteS
        DCB     "SReg1 = &", 0
        ALIGN
        LDR     r0, [ sp, #12 ]
        MOV     r1, sp
        MOV     r2, #11
        SWI     OS_ConvertHex2
        SWI     OS_Write0
        SWI     OS_WriteS
        DCB     13, 10, "SReg2 = &", 0
        ALIGN
        LDR     r0, [ sp, #16 ]
        MOV     r1, sp
        MOV     r2, #11
        SWI     OS_ConvertHex2
        SWI     OS_Write0
        INC     sp, 12
        Pull    "r1, r2"
        SWI     OS_WriteS
        DCB     13, 10, "Failure #", 0
        ALIGN
        MOV     r0, r4
        SWI     OS_WriteC
        SWI     OS_WriteS
        DCB     ". Press a key to continue.", 13, 10, 0
        ALIGN
        SWI     OS_ReadC
92
        ]
        DEC     sp, ?MessageBlock
        MOV     r0, sp                                  ; Address of the temporary block
        ADRL    r1, MessageFileName
        MOV     r2, #0                                  ; Use the file where it is
        SWI     XMessageTrans_OpenFile
        BVS     ExitNoHardware                          ; Very tragic, exit immediately
        ADR     r0, ErrorNoHardware
        MOV     r1, sp                                  ; Address of the temporary block
        MOV     r2, #0                                  ; Allocate a buffer for me
        MOV     r4, #0
        MOV     r5, #0
        MOV     r6, #0
        MOV     r7, #0
        [       False ; Debug
        DREG    r0, "R0 = &", cc
        DREG    r1, "  R1 = &", cc
        DREG    r2, "  R2 = &", cc
        DREG    r3, "  R3 = &"
        DREG    r4, "R4 = &", cc
        DREG    r5, "  R5 = &", cc
        DREG    r6, "  R6 = &", cc
        DREG    r7, "  R7 = &"
        ]
        SWI     XMessageTrans_ErrorLookup               ; Always returns an error
        MOV     r7, r0                                  ; Keep the error pointer
        MOV     r0, sp                                  ; Address of the temporary block
        SWI     XMessageTrans_CloseFile
        MOV     r0, r7                                  ; Get back the translated error
ExitNoHardware
        INC     sp, ?MessageBlock                       ; Restore the stack
        SETV
        Pull    pc

ErrorNoHardware
        DCD     ErrorNumber_NoHardware
        DCB     "NoHware", 0
        ALIGN
        |
        ADR     Error, ErrorNoHardware
        SETV
        Pull    pc

        Err     NoHardware
        ]

Hardware
        PLP     r5
        ;       R0-R5 ==> Trashable
        ;       R6    ==> Trashable, but contains the environment string pointer
        ;       R7-R9 ==> Must be preserved
        ;       R10   ==> Pointer to the hardware
        ;       R11   ==> Trashable
        ;       R12   ==> Pointer to private word
        ;       R13   ==> Valid stack pointer
        [       DebugFindHardware
        DLINE   "Hardware detected"
        ]
        LDR     r3, [ r12 ]                             ; Private word
        TEQ     r3, #0
        MOVNE   wp, r3
        BNE     ReInitialisation
        MOV     r0, #ModHandReason_Claim
        LDR     r3, =TotalRAMRequired
        SWI     XOS_Module
        Pull    pc, VS
        STR     r2, [ r12 ]
        MOV     wp, r2
        [       Debug
        LDR     r0, =&FCFCFCFC
        LDR     r1, =TotalRAMRequired
StoreInitLoop
        DECS    r3
        STR     r0, [ wp, r3 ]
        BNE     StoreInitLoop
        B       SkipReInitStore

ReInitialisation
        LDR     r0, =&FDFDFDFD
        LDR     r1, =TotalRAMRequired
StoreReInitLoop
        DECS    r3
        STR     r0, [ wp, r3 ]
        BNE     StoreReInitLoop
SkipReInitStore
        |
ReInitialisation
        ]
        [       Debug
        DREG    wp, "Store claimed at &"
        ]
        MOV     r0, #0
        ST      r0, SWILock
        [       UseMsgTrans
        ST      r0, MessageBlockAddress                 ; Mark message file as closed
        ]
        [       PoduleCapable
        STR     r10, HardwareAddress
        ADR     r0, IOC
        ADD     r0, r0, #IOCFIQMSK                      ; Defaults for internal hardware
        MOV     r1, #econet_FIQ_bit
        Push    "r0, r1"
        MOV     r0, #Podule_ReadInfo_FIQMask + Podule_ReadInfo_FIQValue
        MOV     r1, sp
        MOV     r2, #8
        MOV     r3, r10                                 ; Something podule specific
        SWI     XPodule_ReadInfo                        ; Ignore error
        Pull    "r0, r1"
        ST      r0, InterruptAddress
        ST      r1, InterruptMask
        [       Debug
        DREG    r0, "FIQ Interrupt mask reg is at &"
        BREG    r1, "FIQ Interrupt mask value is  &"
        ]
        ]
        [       Debug
        ADRL    r14, NetErrorList
        STR     r14, NetErrorListPointer
        MOV     r14, #-1                                ; Initialise the first record
        STR     r14, NetErrorList
        ]
        MOV     r0, #ReadCMOS
        MOV     r1, #ProtectionCMOS
        SWI     XOS_Byte
        ; The protection stored in CMOS doesn't have values for
        ; MachinePeek and Continue.
        ; The protection bits are stored in the protection word
        ; with the bits for MachinePeek and Continue always clear
        ; So; CMOS bits [ 0..5 ] go to Protection bits [ 0..5 ]
        ;                              Protection bits [ 6, 7 ] are 0
        ;     CMOS bits [ 6, 7 ] go to Protection bits [ 8, 9 ]
        TST     r2, #BitSix                             ; GetRegs protection
        EORNE   r2, r2, #BitEight :OR: BitSix           ; Set BitEight, clear BitSix
        TST     r2, #BitSeven                           ; Protection extra bit
        EORNE   r2, r2, #BitNine :OR: BitSeven          ; Set BitNine, clear BitSeven
        STRVC   r2, Protection
        MOVVC   r0, #ReadCMOS                           ; Read the actual station number
        MOVVC   r1, #NetStnCMOS                         ; Station number
        SWIVC   XOS_Byte
        Pull    pc, VS
        TEQ     r2, #0
        TEQNE   r2, #255
        BNE     StationNumberValid
        [       UseMsgTrans
        [       Debug
        ADRL    r1, TokenBadConfig
        |
        ADR     r1, TokenBadConfig
        ]
        BL      PrettyPrintToken
        |
        [       Debug
        ADRL    r0, UsingOneInstead
        |
        ADR     r0, UsingOneInstead
        ]
        SWI     XOS_PrettyPrint
        ]
        Pull    pc, VS
        MOV     r2, #1
StationNumberValid
        ST      r2, LocalStation
        [       Debug
        DREG    r12, "Initialisation, stage 1, WP = &"
        ]
        [       :LNOT: OldOs
        LDR     r5, MachineVersionNumber
        MOV     r0, #2
        SWI     XOS_ReadSysInfo
        BVS     DefaultMachineType
        AND     r0, r0, #&0000FF00                      ; Check type of IO chip
        TEQ     r0, #&00000100                          ; Is it IOMD?
        ADDEQ   r5, r5, #15-7                           ; Change the machine type byte
DefaultMachineType
        ST      r5, MachinePeekData
        ]

SetTicker                                               ; Branched to at Service_Reset
        PHPSEI  r5                                      ; Must be atomic
        MOV     r1, #FIQ_Released
        ST      r1, FIQStatus
        MOV     r1, #Service_ClaimFIQ                   ; Now claim the exclusive use of FIQ
        SWI     XOS_ServiceCall                         ; away from the Kernel
        Pull    pc, VS, ^
        [       PerthPowerDown
        MOV     r0, #PowerDownTimeout
        ST      r0, PowerDownTime                       ; So it won't go down on me
        [       DebugPowerDown
        LDR     r0, =Border_Yellow
        ADR     r14, VIDC
        STR     r0, [ r14, #0 ]
        ]
        ]
        MOV     r0, #0
        ASSERT  FIQ_Owner = 0
        ST      r0, FIQBusy                             ; Mark as busy
        ST      r0, FIQStatus                           ; We own it
        ST      r0, Time
        ST      r0, LockOut                             ; Set no lock out state
        ST      r0, CurrentHandle                       ; Initialise 'handle manager'
        ST      r0, PeekPokeFlag
        ST      r0, EventSequenceNumber
        [       ReceiveInBackground
        ST      r0, BridgeRxHandle
        ]
        [       BroadcastByHand
        ST      r0, BroadcastTime
        ]
        MOV     r1, #NIL                                ; Means it's the end of the list
        ST      r1, RxCBList                            ; Initialise the RxCBList & the TxCBList
        ST      r1, TxCBList
        PLP     r5                                      ; Restore the interrupt state
        [       Debug
        DREG    r12, "Initialisation, stage 2, WP = &"
        ]
        MOV     r0, #TickerV
        ADRL    r1, Pacemaker                           ; The entry address
        MOV     r2, wp                                  ; The value of R12 on entry
        SWI     XOS_Claim
        MOVVC   r0, #IrqV
        ADRVCL  r1, Interrupt                           ; The entry address
        MOVVC   r2, wp                                  ; The value of R12 on entry
        SWIVC   XOS_Claim
        MOVVC   r0, #EventV
        ADRVCL  r1, Event                               ; The entry address
        MOVVC   r2, wp                                  ; The value of R12 on entry
        SWIVC   XOS_Claim
        MOVVC   r0, #14
        MOVVC   r1, #Event_Econet_OSProc
        SWIVC   XOS_Byte                                ; Turn on the relevant event
        Pull    pc, VS

        [       Debug
        DREG    r12, "Initialisation, stage 3, WP = &"
        ]
        MOV     r0, #2_11                               ; Initialise the Port allocation
        ADR     r2, PortTable
        STR     r0, [ r2 ], #4

        MOV     r0, #0
        MOV     r1, #0
        STMIA   r2!, { r0, r1 }
        STMIA   r2!, { r0, r1 }
        STMIA   r2!, { r0, r1 }
        STMIA   r2!, { r0, r1 }
        STMIA   r2!, { r0, r1 }
        STMIA   r2!, { r0, r1 }
        STMIA   r2!, { r0, r1 }
        MOV     r0, #2_11 :SHL: 30
        STR     r0, [ r2 ]
        MOV     r0, #Port_AllocationMinimum
        ST      r0, PortHint
        [       Debug
        DREG    r12, "Initialisation, stage 4, WP = &"
        ]
        [       ErrorInfo
        ADRL    r0, ErrorLogArea
        ADRL    r2, ErrorLogAreaEnd
InitErrorsLoop
        STR     r1, [ r0 ], #4
        TEQ     r0, r2
        BNE     InitErrorsLoop
        ]
        MOV     r0, #UKSWIV                             ; Look for SWIs whilst not linked in
        ADR     r1, MySWIRoutine
        MOV     r2, wp
        SWI     XOS_Claim
        MOVVC   r1, #Service_ReAllocatePorts
        SWIVC   XOS_ServiceCall
        MOVVC   r0, #UKSWIV
        ADRVC   r1, MySWIRoutine
        MOVVC   r2, wp
        SWIVC   XOS_Release
        Pull    pc, VS
        [       Debug
        DREG    r12, "Initialisation, stage 5, WP = &"
        ]
        BL      DoReset                                 ; Corrupts R0
        [       DebugFIQ
        Push    "r0-r2"
        ADR     r0, DebugSpace
        STR     r0, [ r0, #DebugSpacePointer - DebugSpace ] !
        DREG    r0, "Debug space starts at &", cc
        MOV     r1, #0
        ADR     r2, DebugSpaceEnd
DebugInitLoop
        STR     r1, [ r0, #4 ] !
        CMP     r0, r2
        BLT     DebugInitLoop
        DREG    r2, " and ends at &"
        Pull    "r0-r2"
        ]
        [       Debug
        DREG    r12, "Initialisation, stage 6, WP = &"
        ]
        BL      FindLocalNet                            ; Doesn't return errors, preserves all
        [       Debug
        DREG    r12, "Initialisation, stage 7, WP = &"
        ]
        Pull    pc,, ^

        [       UseMsgTrans
TokenBadConfig
        DCB     "BadConf", 0
        ALIGN
        |
UsingOneInstead
        DCB     13
        DCB     "Configured station number is invalid, using 1 instead."
        DCB     13, 0
        ALIGN
        ]

MySWIRoutine                                            ; Entered when not properly linked in
        LDR     r10, =Econet_ClaimPort
        TEQ     r10, r11
        Pull    lr, EQ
        [       Debug
        BNE     %81
        BREG    r0, "ReAllocate calls ClaimPort &"
81
        ]
        BEQ     ClaimPort
        LDR     r10, =Econet_AllocatePort
        TEQ     r10, r11
        Pull    lr, EQ
        [       Debug
        BNE     %82
        DLINE   "ReAllocate calls AllocatePort"
82
        ]
        BEQ     AllocatePort
        LDR     r10, =Econet_CreateReceive
        TEQ     r10, r11
        Pull    lr, EQ
        [       Debug
        BNE     %83
        DLINE   "ReAllocate calls CreateReceive"
83
        ]
        BEQ     CreateReceive
        LDR     r10, =Econet_HardwareAddresses
        TEQ     r10, r11
        Pull    lr, EQ
        [       Debug
        BNE     %84
        DLINE   "ReAllocate calls HardwareAddresses"
84
        ]
        BEQ     HardwareAddresses
        MOV     pc, r14

        LTORG

KillModule ROUT                                         ; Ignore all errors
        Push    lr
        LDR     wp, [ r12 ]
        MOV     r0, #TickerV
        ADRL    r1, Pacemaker                           ; The entry address
        MOV     r2, wp                                  ; The value of R12 on entry to Pacemaker
        SWI     XOS_Release
        MOV     r0, #13
        MOV     r1, #Event_Econet_OSProc
        SWI     XOS_Byte                                ; Turn off the relevant event
        MOV     r0, #EventV
        ADRL    r1, Event                               ; The entry address
        MOV     r2, wp                                  ; The value of R12 on entry
        SWI     XOS_Release
        MOV     r0, #IrqV
        ADRL    r1, Interrupt
        MOV     r2, wp
        SWI     XOS_Release

        [       UseMsgTrans
        LD      r0, MessageBlockAddress                 ; Is it open?
        MOV     r1, #0
        ST      r1, MessageBlockAddress                 ; Mark it as closed
        TEQ     r0, #0
        SWINE   XMessageTrans_CloseFile                 ; Close it if it was open
        ]       ; UseMsgTrans
        LD      r1, FIQStatus
        TEQ     r1, #FIQ_Owner                          ; Do we own FIQ?
        MOVEQ   r0, #1                                  ; Not able to do SWIs to myself no more
        MOVEQ   r1, #Service_ClaimFIQ                   ; Claim FIQ away if we own it
        BLEQ    ServiceBodge
TrashAllCBs                                             ; JMP'd to, expects LR at [ SP ]
        MOV     r1, #Service_EconetDying
        SWI     XOS_ServiceCall
        ADR     r2, TxCBList - Offset_Link
        LDR     r2, [ r2, #Offset_Link ]
TxDieLoop                                               ; DeAllocate all TxCBS
        TEQ     r2, #NIL
        BEQ     RxDie
        LDR     r3, [ r2, #Offset_Link ]
        MOV     r0, #ModHandReason_Free
        SWI     XOS_Module
        BVS     RxDie                                   ; Any errors cause us to stop
        MOV     r2, r3
        B       TxDieLoop

RxDie
        ADR     r2, RxCBList - Offset_Link
        LDR     r2, [ r2, #Offset_Link ]
RxDieLoop                                               ; DeAllocate all RxCBS
        TEQ     r2, #NIL
        Pull    pc, EQ, ^
        LDR     r3, [ r2, #Offset_Link ]
        MOV     r0, #ModHandReason_Free
        SWI     XOS_Module
        Pull    pc, VS, ^                               ; Any errors cause us to stop
        MOV     r2, r3
        B       RxDieLoop

Service ROUT
        TEQ     r1, #Service_ClaimFIQ
        TEQNE   r1, #Service_ReleaseFIQ
        TEQNE   r1, #Service_ClaimFIQinBackground
        TEQNE   r1, #Service_Reset
        TEQNE   r1, #Service_Portable
        BICNES  pc, lr, #VFlag
        LDR     wp, [ r12 ]
ServiceBodge
        Push    lr
        [       False ;Debug
        BREG    r1, "Service_&", cc
        DREG    r0, " R0 = &", cc
        DREG    r2, " R2 = &", cc
        DREG    r3, " R3 = &", cc
        DREG    r4, " R4 = &"
        ]
        TEQ     r1, #Service_ClaimFIQ
        BEQ     ClaimFIQ
        TEQ     r1, #Service_ReleaseFIQ
        BEQ     ReleaseFIQ
        TEQ     r1, #Service_ClaimFIQinBackground
        BEQ     BackgroundClaimFIQ
        TEQ     r1, #Service_Portable
        BEQ     PowerUpOrDown
ServiceReset                                            ; Note the fall through
        Push    "r0-r6"
        MOV     r0, #0                                  ; Clear SWI lock on reset
        ST      r0, SWILock
        MOV     r0, #253                                ; Now check reset was NOT hard
        MOV     r1, #0
        MOV     r2, #255
        SWI     XOS_Byte                                ; Read last reset type
        BVS     ExitReset                               ; Only do reset when it was soft
        CMP     r1, #0
        BNE     ExitReset                               ; Nowt to do, not a soft reset
        [       Debug
        DLINE   "Actually doing Service_Reset"
        ]
        Push r3
        JumpAddress lr, ReturnFromTrashCBs, Forward
        Push    lr
        B TrashAllCBs                                   ; Returns with Pull PC
ReturnFromTrashCBs
        Pull r3
        JumpAddress lr, ExitReset, Forward
        Push    lr
        B SetTicker                                     ; Returns with Pull PC
ExitReset
        Pull    "r0-r6, pc", ,^

        [       PerthPowerDown
PowerUpOrDown   ROUT
        TST     r3, #PortableControl_EconetEnable       ; Is this for our hardware?
        Pull    pc, EQ                                  ; No so return ASAP
        TEQ     r2, #ServicePortable_PowerUp
        BEQ     DoPowerUpSequence
        TEQ     r2, #ServicePortable_PowerDown
        BEQ     DoPowerDownSequence
        Pull    pc

DoPowerUpSequence
        [       Debug
        DLINE   "Power up"
        ]
        [       True
        Push    "r0"                                    ; Delay to ensure the hardware is alive
        MOV     r0, #19
        SWI     XOS_Byte
        MOV     r0, #19
        SWI     XOS_Byte
        Pull    "r0"
        ]
        PHPSEI  r2                                      ; Must be atomic
        MOV     r14, #FIQ_Released
        ST      r14, FIQStatus
        MOV     r1, #Service_ClaimFIQ                   ; Back from the kernel
        SWI     XOS_ServiceCall                         ; Ignore errors
        [       Debug
        DREG    r1, "Service_ClaimFIQ (from kernel) returns &"
        ]
        MOV     r14, #FIQ_Owner
        ST      r14, FIQStatus                          ; We own FIQ
        BL      DoReset                                 ; Trashes R1
        PLP     r2
        MOV     r2, #ServicePortable_PowerUp
        B       ExitChangePower

DoPowerDownSequence
        LD      r14, FIQStatus
        TEQ     r14, #FIQ_Owner
        BNE     ExitDoPowerDown                         ; Nothing to do if not FIQ owner
        [       Debug
        DLINE   "Power down, we own FIQ"
        ]
        BL      ClaimFIQBodge                           ; Trashes R1
        MOV     r1, #FIQ_PoweredDown
        ST      r1, FIQStatus
        MOV     r1, #Service_ReleaseFIQ                 ; Back to the kernel
        SWI     XOS_ServiceCall                         ; Ignore errors
        [       Debug
        DREG    r1, "Service_ReleaseFIQ (to kernel) returns &"
        ]
ExitDoPowerDown
        MOV     r1, #FIQ_PoweredDown
        ST      r1, FIQStatus
        MOV     r2, #ServicePortable_PowerDown
ExitChangePower
        MOV     r1, #Service_Portable
        [       False ; Debug
        BREG    r1, "Exit Service_&", cc
        DREG    r0, " R0 = &", cc
        DREG    r2, " R2 = &", cc
        DREG    r3, " R3 = &", cc
        DREG    r4, " R4 = &"
        ]
        Pull    "pc"
        |       ; PerthPowerDown
PowerUpOrDown   ROUT
        TST     r3, #PortableControl_EconetEnable       ; Is this for our hardware?
        Pull    pc, EQ                                  ; No so return ASAP
        TEQ     r2, #ServicePortable_PowerDown          ; Is someone trying to power us down?
        BICEQS  r3, r3, #PortableControl_EconetEnable   ; Yes, so don't allow it
        MOVEQ   r1, #Service_Serviced                   ; And if there are no more bits set
        Pull    pc                                      ; Then claim the service to save time
        ]

ReleaseFIQ      ROUT
        LD      r14, FIQStatus
        TEQ     r14, #FIQ_Released
        TEQNE   r14, #FIQ_Owner
        [       Debug
        BEQ     %06
        BREG    r14, "Service_ReleaseFIQ not accepted; FIQStatus = &"
        ]
        Pull    "pc", NE                                ; Nothing to do
        [       Debug
06
        DLINE   "Service_ReleaseFIQ accepted; FIQ taken from releasor"
        ]
        Push    "r0"
        MOV     r14, #FIQ_Owner
        ST      r14, FIQStatus                          ; We own FIQ
        BL      DoReset                                 ; Trashes R1
        B       ExitServiced

ClaimFIQBodge                                           ; Called with BL
        Push    lr
ClaimFIQ
        LD      r14, FIQStatus
        TEQ     r14, #FIQ_Owner
        [       Debug
        BEQ     %07
        BREG    r14, "Service_ClaimFIQ not accepted; FIQStatus = &"
        ]
        Pull    "pc", NE                                ; Nothing to do
        [       Debug
07
        DLINE   "Service_ClaimFIQ accepted; FIQ released to claimant"
        ]
        Push    "r0"
TryForNotBusy
        MOV     r1, psr
        BIC     r1, r1, #FFlag
        TEQP    psr, r1
WaitForNotBusy
        LD      r1, FIQBusy
        TEQ     r1, #0
        BNE     WaitForNotBusy
        MOV     r1, psr
        ORR     r1, r1, #FFlag
        TEQP    psr, r1
        LD      r1, FIQBusy
        TEQ     r1, #0
        BNE     TryForNotBusy
DoClaimFIQ                                              ; Expects R0, LR on the stack
        MOV     r14, #FIQ_Released
        ST      r14, FIQStatus                          ; Show that we have released to the claimant
        [       PoduleCapable
        LDR     r1, HardwareAddress
        |
        LDR     r1, =EconetController
        ]
        MOV     r0, #2_11000000                         ; TxReset, RxReset
        STRB    r0, [ r1, #:INDEX: CReg1 ]
        [       PoduleCapable
        LD      r0, InterruptAddress
        LD      r1, InterruptMask
        LDRB    r14, [ r0, #0 ]
        BIC     r14, r14, r1
        STRB    r14, [ r0, #0 ]
        |
        ADR     r1, IOC
        LDRB    r0, [ r1, #IOCFIQMSK ]                  ; FIQ Mask register
        BIC     r0, r0, #econet_FIQ_bit
        STRB    r0, [ r1, #IOCFIQMSK ]                  ; FIQ Mask register
        ]
ExitServiced
        MOV     r1, #Service_Serviced
        Pull    "r0, pc"

BackgroundClaimFIQ
        LD      r14, FIQStatus                          ; Check that we do own FIQ
        TEQ     r14, #FIQ_Owner
        LD      r14, SWILock, EQ                        ; See if we are executing a SWI
        TEQEQ   r14, #0
        ADREQ   r14, IOC
        LDREQB  r14, [ r14, #IOCIRQREQA ]               ; IRQ Request register
        TSTEQ   r14, #force_bit                         ; If FIQ downgrade pending do not release FIQ
        Pull    "pc", NE
        Push    "r0"
        LD      r14, FIQBusy
        TEQ     r14, #0                                 ; Is it busy?
        BNE     BackgroundClaimFails                    ; Yes
        MOV     r14, psr
        ORR     r14, r14, #FFlag                        ; Disable FIQ
        TEQP    psr, r14
        LD      r14, FIQBusy
        TEQ     r14, #0                                 ; Is it still not busy?
        BEQ     DoClaimFIQ                              ; Yes
        MOV     r14, psr
        BIC     r14, r14, #FFlag                        ; Re-enable FIQ because something has just started
        TEQP    psr, r14
BackgroundClaimFails
        MOV     r1, #Service_ClaimFIQinBackground
        Pull    "r0, pc"

DoReset ROUT                                            ; Install the FIQ code
        ADR     r1, ModuleWorkspace                     ; Stored where FIQ code can find it
        STR     wp, [ r1 ]
        MOV     r1, lr                                  ; The return address, for RTFIQ (from SVC 'lr')
        TEQP    psr, #( FFlag + IFlag + FIQ_mode )      ; Set FIQ flag and goto FIQ mode
        NOP
        ADD     lr, r1, #4                              ; The return address, for RTFIQ (to FIQ 'lr')
        ADR     r12, ModuleWorkspace
        LDR     r12, [ r12 ]                            ; FIQ_R12 points to Econet's workspace
        [       PoduleCapable
        LDR     r10, HardwareAddress
        [       Debug
        MOV     r13, #&F0                               ; A bodge stack
        DREG    r10, "H/W = &"
        ]
        |
        LDR     r10, =EconetController                  ; Initialise FIQ_R10
        ]
        MOV     r13, #NIL
        STR     r13, Record
        MOV     r13, #2_11000000                        ; TxReset, RxReset, AC=0
        STRB    r13, CReg1
        [       PoduleCapable
        LD      r10, InterruptAddress
        LD      r11, InterruptMask
        [       Debug
        MOV     r13, #&F0                               ; A bodge stack
        DREG    r10, "FIQ reg = &"
        BREG    r11, "FIQ msk is = &"
        ]
        LDRB    r13, [ r10, #0 ]
        ORR     r11, r13, r11
        STRB    r11, [ r10, #0 ]
        [       Debug
        MOV     r13, #&F0                               ; A bodge stack
        BREG    r11, "Msk reg now = &"
        ]
        |
        ADR     r11, IOC                                ; Now allow ADLC interrupts to actually give FIQs
        LDRB    r13, [ r11, #IOCFIQMSK ]                ; FIQ Mask register
        ORR     r13, r13, #econet_FIQ_bit
        STRB    r13, [ r11, #IOCFIQMSK ]                ; FIQ Mask register
        ]
ReInit  ; Violently re-initialise things, take no chances
        ; Note that this and the next entry point are
        ; also used as an exit from FIQ code after an
        ; error so it has to EXIT with RTFIQ
        [       PoduleCapable
        ADR     r12, ModuleWorkspace
        LDR     r12, [ r12 ]                            ; FIQ_R12 points to Econet's workspace
        LDR     r10, HardwareAddress
        |
        LDR     r10, =EconetController                  ; Initialise FIQ_R10
        ]
        MOV     r13, #2_11000001                        ; TxReset, RxReset, AC=1
        ST      r13, CReg1
        MOV     r13, #2_00011110                        ; NRZ, Rx8bits, Tx8bits
        ST      r13, CReg4
        MOV     r13, #2_00000000                        ; DTR, NONLOOP
        ST      r13, CReg3
Reset   ; Resets the world to the normal quiescent state
        [       Analyser
        MOV     r13, #2_10000011                        ; TxReset, RxIE, AC=1
        ST      r13, CReg1
        MOV     r13, #2_00000000                        ; -DTR, NONLOOP
        ST      r13, CReg3
        [       DebugFIQ
        LDR     r13, =Border_Grey
        ADR     r10, VIDC
        STR     r13, [ r10, #0 ]
        ]
        ]
        [       PoduleCapable
        ADR     r12, ModuleWorkspace
        LDR     r12, [ r12 ]                            ; FIQ_R12 points to Econet's workspace
        LDR     r10, HardwareAddress
        |
        LDR     r10, =EconetController                  ; Initialise FIQ_R10
        ]
        MOV     r13, #2_10000010                        ; TxReset, RxIE, AC=0
        ST      r13, CReg1
        MOV     r13, #2_01100111                        ; ClrTxStatus, ClrRxStatus, FlagIdle, 2Byte, PSE
        ST      r13, CReg2
        LD      r13, SReg2                              ; Ensure that we keep Status2 up-to-date
        ST      r13, Status2
Ready                                                   ; Restores FIQ_R10 and FIQ_R12
        ADR     r12, ModuleWorkspace
        LDR     r12, [ r12 ]                            ; FIQ_R12 points to Econet's workspace
        MOV     r13, #0
        ST      r13, FIQBusy                            ; Show that FIQ is not busy, by storing a zero
        SetFIQ  StartReception, r8, r9, Long, Init, r10 ; Inits the LDR pc, .+4 instruction as well
        [       PoduleCapable
        LDR     r10, HardwareAddress
        |
        LDR     r10, =EconetController                  ; Initialise FIQ_R10
        ]
        RTFIQ                                           ; Now wait patiently

FindLocalNet    ROUT
        Push    "r0-r11, lr"                            ; Save the interrupt state psr/lr on the stack
        TEQP    psr, #(IFlag + SVC_mode)                ; Enable all interrupts (SVC mode assumed)
        NOP

        MOV     r8, #0                                  ; Initialise the two handles to empty
        MOV     r9, #0

        MOV     r0, #Port_Bridge
        MOV     r1, #0                                  ; Any
        MOV     r2, #0                                  ; Any
        ADR     r3, LocalNetwork
        MOV     r4, #2                                  ; Local net and version numbers
        ST      r1, LocalNetwork                        ; Initialise to the state of a lonely net
        [       ReceiveInBackground
        ST      r1, BridgeRxHandle                      ; Initialise handle
        ]
        BL      CreateReceive                           ; Use internal routines directly
        [       DebugFindLocalNet
        BVS     ExitLocalNet
        DREG    r0, "Rx handle = &"
        ]
        [       ReceiveInBackground
        ST      r0, BridgeRxHandle, VC
        |
        MOVVC   r8, r0                                  ; Save the handle
        ]
        [       BroadcastByHand                         ; Do broadcast by hand
        [       DebugFindLocalNet
        LDR     r0, =Border_Black
        ADR     r1, VIDC
        STR     r0, [ r1, #0 ]
        ]
        BVS     ExitLocalNet

        MOV     r1, #Service_ClaimFIQ                   ; Claim FIQ away if we own it
        BL      ServiceBodge
        [       DebugFindLocalNet
        DLINE   "FIQ Claimed"
        ]
        [       PoduleCapable
        ADR     r7, HardwareAddress
        LDMIA   r7, { r7, r8, r9 }
        MOV     r10, r7
        ADR     r11, IOC
        |       ; PoduleCapable
        ADR     r11, IOC
        ADD     r8, r11, #IOCFIQMSK
        MOV     r9, #econet_FIQ_bit
        LDR     r10, =EconetController
        ]       ; PoduleCapable
        LDRB    r14, [ r8, #0 ]                         ; FIQ Mask register
        BIC     r14, r14, r9                            ; Disable FIQ from the ADLC
        STRB    r14, [ r8, #0 ]                         ; FIQ Mask register
        ADD     r8, r8, #IOCFIQSTA - IOCFIQMSK          ; Now point at the Status register
        [       DebugFindLocalNet
        DREG    r11, "IOC Timers are at &"
        DREG    r10, "ADLC is at &"
        DREG    r8, "FIQ Status register is at &", cc
        BREG    r9, " and the mask value is &"
        ]

        ;       R0  = Data byte
        ;       R1  = Status register 1
        ;       R2  = Status register 2
        ;       R3  = Control values
        ;       R4  = Timer0 start value
        ;       R5  = Timer0 recent value (will be less than R4)
        ;       R6  = Pointer to byte to broadcast
        ;       R7  = Number of bytes remaining to broadcast
        ;       R8  = Address of FIQ Status register
        ;       R9  = Mask for FIQ Status register
        ;       R10 = Address of ADLC
        ;       R11 = Address of IOC/IOMD

        MOV     r3, #2_10000010                         ; TxReset, RxIE, AC=0
        ST      r3, CReg1
        MOV     r3, #2_01100111                         ; ClrTxStatus, ClrRxStatus, FlagIdle, 2Byte, PSE
        ST      r3, CReg2
        LD      r2, SReg2                               ; Ensure that we keep Status2 up-to-date
        ST      r2, Status2
        TST     r2, #DCD
        BEQ     BroadcastHasClock
        [       DebugFindLocalNet
        DLINE   "Broadcast for bridge detects no clock"
        BREG    r2, "Status register 2 is &"
        ]
BroadcastFails
        LD      r0, BridgeRxHandle
        MOV     r14, #0
        ST      r14, BridgeRxHandle
        BL      AbandonAndReadReceive
        B       ExitLocalNet

BroadcastHasClock
        STRB    r14, [ r11, #Timer0LR ]                 ; Latch the timer
        LDRB    r14, [ r11, #Timer0CH ]
        LDRB    r4, [ r11, #Timer0CL ]
        ORR     r4, r4, r14, LSL #8                     ; Now a single 16 bit value
        [       DebugFindLocalNet
        ST      r4, InitTimer0
        MOV     r0, #0
        ]
        MOV     r5, r4
WaitForLineAccess
        LD      r2, SReg2                               ; Note R10 is always the address of the ADLC
        TST     r2, #RxIdle                             ; Now the line access stuff
        BEQ     BroadcastNoLineAccess                   ; Line not idle
        TST     r2, #RxAbort
        BEQ     BroadcastLineAccess
BroadcastNoLineAccess
        BL      GetTimerValue                           ; Returns ticks in R0
        CMP     r0, #400                                ; 200 micro-seconds
        BLT     WaitForLineAccess
        [       DebugFindLocalNet
        DLINE   "Broadcast for bridge detects no line access"
        BREG    r2, "Status register 2 is &"
        ]

        ;       *************
        B       BroadcastFails

BroadcastLineAccess
        [       False ; DebugFindLocalNet
        DREG    r0, "Line access took &", cc
        DLINE   " ticks."
        ]
        MOV     r4, r5                                  ; Throw that time away
        MOV     r3, #2_11100111                         ; RTS, CLRTxST, CLRRxST, FlagIdle, 2Byte, PSE
        ST      r3, CReg2
        MOV     r3, #2_01000100                         ; RxRS, TxIE
        ST      r3, CReg1
        [       DebugFindLocalNet
        ADRL    r6, BridgeData
        |
        ADR     r6, BridgeData
        ]
        MOV     r7, #BridgeDataSize
WaitForFirstInterrupt
        BL      GetTimerValue                           ; Returns ticks in R0
        LDRB    r1, [ r8, #0 ]                          ; Is there an interrupt?
        TST     r1, r9                                  ; From the ADLC?
        BNE     InterruptOK
        CMP     r0, #60                                 ; 30 micro-seconds
        BLT     WaitForFirstInterrupt
        [       DebugFindLocalNet
        BREG    r1, "No Interrupt: last Status read was &"
        LD      r3, InitTimer0
        DREG    r3, "Initial value of Timer0 was &"
        DREG    r4, "R4 = &"
        DREG    r5, "Timer0 is now &"
        DREG    r0, "Ticks = &"
        ]
        ;       ***********************
        ; Error return with Interrupt failure
        B       BroadcastFails

InterruptOK
        [       False ; DebugFindLocalNet
        DREG    r0, "First interrupt took &", cc
        DLINE   " ticks."
        DREG    r5, "Timer0 is now &"
        ]
        LD      r1, SReg1
        TSTS    r1, #TxDRA                              ; TDRA OK ?
        BEQ     BroadcastNetError
        LDRB    r0, [ r6 ], #1
        TEQ     r0, #254                                ; Special value?
        LD      r0, LocalStation, EQ
        ST      r0, TxByte
        DECS    r7
        BEQ     BroadcastFinished
WaitForNextInterrupt
        LDRB    r14, [ r8, #0 ]                         ; Is there an interrupt?
        TST     r14, r9
        BNE     InterruptOK
        BL      GetTimerValue                           ; Returns ticks in R0
        CMP     r0, #6016                               ; 3 milli-seconds
        BLT     WaitForNextInterrupt
        B       InterruptTookTooLong

BroadcastNetError
        [       DebugFindLocalNet
        BREG    r1, "Status register 1 = &"
        LDR     r0, =Border_Blue
        ADR     r1, VIDC
        STR     r0, [ r1, #0 ]
        ]
        B       BroadcastFails

GetTimerValue
        ;       R4 ==> Start value of Timer0
        ;       R5 ==> Most recent value of Timer0
        ;       R0 <== Number of ticks since Start value
        ;       R3 Trashed
        ;       R4 <== Updated if Timer0 wrapped
        ;       R5 <== This value of Timer0
        STRB    r0, [ r11, #Timer0LR ]                  ; Latch the timer
        LDRB    r0, [ r11, #Timer0CH ]
        LDRB    r3, [ r11, #Timer0CL ]
        ORR     r3, r3, r0, LSL #8                      ; Now a single 16 bit value
        CMP     r3, r5                                  ; Current and previous
        ADDGT   r4, r4, r3                              ; Account for the clocking of Timer0
        MOV     r5, r3                                  ; Update most recent value into R5
        [       DebugFindLocalNet
        LDRGT   r0, =Border_Lime
        ADRGT   r3, VIDC
        STRGT   r0, [ r3, #0 ]
        ]
        SUB     r0, r4, r5                              ; How many ticks there have been now
        MOV     pc, lr

BroadcastFinished
        LDR     r1, =2_00111111                         ; -RTS, ClrRxST, TxLast, FC, FlagIdle, 2Byte, PSE
        ST      r1, CReg2
WaitForFinalInterrupt
        LDRB    r14, [ r8, #0 ]                          ; Is there an interrupt?
        TST     r14, r9
        BNE     FinalInterrupt
        BL      GetTimerValue                           ; Returns ticks in R0
        CMP     r0, #6016                               ; 3 milli-seconds
        BLT     WaitForFinalInterrupt
InterruptTookTooLong
        [       DebugFindLocalNet
        DLINE   "Interrupt took too long"
        LD      r3, InitTimer0
        DREG    r3, "Initial value of Timer0 was &"
        DREG    r4, "R4 = &"
        DREG    r5, "Timer0 is now &"
        DREG    r0, "Ticks = &"
        LDR     r0, =Border_Yellow
        ADR     r1, VIDC
        STR     r0, [ r1, #0 ]
        ]
        B       BroadcastFails

FinalInterrupt
        LD      r1, SReg1
        TSTS    r1, #FrameComplete
        BEQ     BroadcastNetError
        MOV     r1, #37                                 ; Magic value determined emperically
        DivRem  r2, r0, r1, r14
        INC     r2
        ASR     r2, 1
        ST      r2, BroadcastTime                       ; In quarters of a micro second
        [       False ; DebugFindLocalNet
        DLINE   "Final interrupt OK"
        LD      r3, InitTimer0
        DREG    r3, "Initial value of Timer0 was &"
        DREG    r4, "R4 = &"
        DREG    r5, "Final value of Timer0 is now &"
        SUB     r1, r4, r5
        DREG    r1, "Ticks = &"
        DLINE   "Econet Clock frequency = ", cc
        MOV     r1, #4000
        DivRem  r0, r1, r2, r14
        ADR     r1, SmallTextBuffer
        MOV     r2, #?SmallTextBuffer
        SWI     OS_ConvertCardinal2
        SWI     OS_Write0
        DLINE   " KHz."
        ]
        |       ; BroadcastByHand
        MOVVC   r0, #2                                  ; Command code for "determine local net number"
        MOVVC   r1, #Port_Bridge
        MOVVC   r2, #255
        MOVVC   r3, #255
        ADRVC   r4, BridgeData
        MOVVC   r5, #BridgeDataSize
        MOVVC   r6, #5
        MOVVC   r7, #5
        BLVC    StartTransmit                           ; Use internal routines directly
        BVS     ExitLocalNet
        MOV     r9, r0                                  ; Save the handle
        [       DebugFindLocalNet
        DREG    r9, "Tx handle = &"
        ]
WaitForLocalNetTx
        MOV     r0, r9
        BL      PollTransmit                            ; Use internal routines directly
        BVS     ExitLocalNet
        TEQ     r0, #Status_TxReady
        TEQNE   r0, #Status_Transmitting
        [       DebugFindLocalNet
        BREG    r0, "Transmit Status is &"
        DREG    psr, "PSR = &"
        ]
        BEQ     WaitForLocalNetTx
        [       DebugFindLocalNet
        DREG    r0, "Tx finishes, status = &"
        ]
        ]       ; BroadcastByHand
        [       ReceiveInBackground
ExitLocalNet
        [       BroadcastByHand
        MOV     r1, #Service_ReleaseFIQ                 ; Restore  normal working
        BL      ServiceBodge
        [       DebugFindLocalNet
        DLINE   "FIQ Released"
        ]
        ]
        Pull    "r0-r11, pc",, ^                        ; Restore interrupt state
        |       ; ReceiveInBackground
WaitForLocalRx
        MOV     r0, r8
        [       DebugFindLocalNet
        DREG    psr, "PSR = &"
        DREG    r0, "ExamineReceive of &", cc
        ]
        BL      ExamineReceive                          ; Use internal routines directly
        BVS     ExitLocalNet
        [       DebugFindLocalNet
        DREG    r0, " returns &"
        Push    "r0-r2"
        MOV     r0, #19
        SWI     XOS_Byte
        Pull    "r0-r2"
        ]
        TEQ     r0, #Status_Received
        BEQ     ExitLocalNet
        LD      r0, Time
        [       DebugFindLocalNet
        CMP     r0, #20
        |
        CMP     r0, #200                                ; Wait only two seconds at the most
        ]
        BLT     WaitForLocalRx
        [       DebugFindLocalNet
        DLINE   "Rx times out"
        ]
ExitLocalNet
        MOVS    r0, r8                                  ; Test the receive handle
        BLNE    AbandonAndReadReceive                   ; Use internal routines directly
        ]       ; ReceiveInBackground
        [       :LNOT: BroadcastByHand
        MOVS    r0, r9                                  ; Same for the transmit handle
        BLNE    AbandonTransmit                         ; Use internal routines directly
        Pull    "r0-r11, pc",, ^                        ; Restore interrupt state
        ]       ; BroadcastByHand

BridgeData
        [       BroadcastByHand
        DCB     255, 255                                ; Broadcast
        DCB     254                                     ; Special, means local station here
        DCB     0                                       ; Local net
        DCB     &82                                     ; Bridge command
        DCB     Port_Bridge                             ; Transmit port
        ]
        DCB     "Bridge"                                ; Identifier
        DCB     Port_Bridge                             ; Reply port
        DCB     0                                       ; Filler
BridgeDataSize  *       . - BridgeData

        LTORG

        [       UseMsgTrans
PrintBanner     ROUT
        Push    "r1, lr"
        LD      r1, Status2
        TST     r1, #DCD                                ; Test the NoClock bit
        ADREQ   r1, Token_Banner
        ADRNE   r1, Token_BannerNoClock
        BL      PrettyPrintToken
        Pull    "r1, pc"

Token_Banner
        DCB     "AcrnEco", 0
Token_BannerNoClock
        DCB     "EcoNClk", 0
        ALIGN

PrettyPrintFrame     *       100

PrettyPrintToken
        Push    "r0-r4, lr"
        DEC     sp, PrettyPrintFrame                    ; Put the string into the stack
        MOV     r2, sp
        MOV     r3, #PrettyPrintFrame - 1
        MOV     r4, #0                                  ; No parameters
        BL      MessageTransGSLookup
        MOVVC   r0, r2                                  ; The passed buffer
        MOV     r1, r3                                  ; Length of the resultant string
        SWIVC   XOS_PrettyPrint
        INC     sp, PrettyPrintFrame                    ; Clear the message away
        STRVS   r0, [ sp, #0 ]
        Pull    "r0-r4, pc"

MakeError       ROUT
        Push    "r1-r4, lr"
        MOV     r4, #0
DoMakeError1
        MOV     r2, #0                                  ; Make MessageTrans give me the space
        MOV     r3, #0
        BL      MessageTransErrorLookup
        Pull    "r1-r4, pc"

MakeErrorWithModuleName
        Push    "r1-r4, lr"
        ADR     r4, ModuleTitle
        B       DoMakeError1

MessageTransGSLookup
        Push    "r5, lr"
        BL      MessageTransGSLookup2
        Pull    "r5, pc"

MessageTransGSLookup2
        Push    "r6, r7, lr"
        LD      r0, MessageBlockAddress
        CMP     r0, #0                                  ; Clears V
        BNE     DoGSLookup
        Push    "r1, r2"
        ADR     r0, MessageBlock
        ADR     r1, MessageFileName
        MOV     r2, #0                                  ; Use the file where she lies
        SWI     XMessageTrans_OpenFile
        ADRVC   r0, MessageBlock
        STRVC   r0, MessageBlockAddress
        Pull    "r1, r2"
DoGSLookup
        MOV     r6, #0
        MOV     r7, #0
        SWIVC   XMessageTrans_GSLookup
        Pull    "r6, r7, pc"

MessageTransErrorLookup
        Push    "r5, r6, r7, lr"
        LD      r1, MessageBlockAddress
        CMP     r1, #0                                  ; Clears V
        BNE     DoErrorLookup
        Push    "r0, r2"
        ADR     r0, MessageBlock
        ADR     r1, MessageFileName
        MOV     r2, #0                                  ; Use the file where she lies
        SWI     XMessageTrans_OpenFile
        ADRVC   r1, MessageBlock
        STRVC   r1, MessageBlockAddress
        Pull    "r0, r2"                                ; Preserve R0 even in the error case
DoErrorLookup
        MOV     r5, #0
        MOV     r6, #0
        MOV     r7, #0
        SWIVC   XMessageTrans_ErrorLookup
        Pull    "r5, r6, r7, pc"

MessageFileName
        DCB     "Resources:$.Resources.Econet.Messages", 0
        ALIGN
        |
PrintBanner     ROUT
        MOV     r11, lr
        Push    r0
        ADRL    r0, BannerString
        SWI     XOS_Write0
        LD      r10, Status2
        TST     r10, #DCD                               ; Ignore V for the moment ...
        BEQ     %10                                     ; Clock OK
        MOVVC   r10, r0
        ADRVCL  r0, NoClockString
        SWIVC   XOS_Write0
        MOVVC   r0, r10
10
        ADDVS   sp, sp, #4
        Pull    r0, VC
        SWIVC   XOS_NewLine
        MOV     pc, r11
        ]       ; UseMsgTrans

        LTORG

        GET     s.Interface
        GET     s.Commands
        GET     s.Receive
        GET     s.Transmit
        GET     s.Background

        [       :LNOT: ReleaseVersion
        InsertDebugRoutines
        ]

        END