; 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    SWI entry code.  File => &.Arthur.Econet.Interface
        OPT     OptPage

SVCEntry        ROUT
        Push    lr
        LDR     wp, [ r12 ]
        LD      r14, SWILock
        INC     r14
        ST      r14, SWILock
        [       DebugSWIs
        MOV     r10, psr
53
        CMP     r14, #1
        BLE     %63
        DLINE   "   ", cc
        DECS    r14
        BGT     %53
63
        ADD     r14, r11, #MySWIChunkBase
        DREG    r14, "SWI &", cc
        DREG    r0, ", R0=&", cc
        DREG    r1, ", R1=&", cc
        DREG    r2, ", R2=&", cc
        DREG    r3, ", R3=&"
        TEQP    psr, r10
        NOP
        ]
        [       PerthPowerDown
        PHPSEI                                          ; Hold IRQ state in R14
        ; Turn IRQs off so that a centi-second tick
        ; can't turn it off when we think it's on
        LD      r10, Time
        ADD     r10, r10, #PowerDownTimeout             ; Compute new down time
        ST      r10, PowerDownTime
        LD      r10, FIQStatus
        PLP
        TEQ     r10, #FIQ_PoweredDown
        [       DebugPowerDown
        LDREQ   r14, =Border_Cyan                       ; Powering up
        LDRNE   r14, =Border_Green                      ; Resetting timer only
        ADR     r10, VIDC
        STR     r14, [ r10, #0 ]
        ]
        BNE     PoweredUp
        [       UsePortableModule
        Push    "r0, r1"
        MOV     r0, #PortableControl_EconetEnable
        MVN     r1, #PortableControl_EconetEnable
        SWI     XPortable_Control                       ; Ignore error
        Pull    "r0, r1"
        |
        Push    "r0-r3"
        MOV     r1, #Service_Portable
        MOV     r2, #ServicePortable_PowerUp
        MOV     r3, #PortableControl_EconetEnable
        SWI     XOS_ServiceCall
        Pull    "r0-r3"
        ]
PoweredUp
        ]
        ADR     lr, ExitSwi                             ; Where to return to, no mode or flags
        LDR     r10, [ sp, #0 ]                         ; Entry value of LR
        AND     r10, r10, #ARM_CC_Mask                  ; Now just mode and flags
        ORR     lr, lr, r10                             ; Now its where to return to with mode and flags
        CMP     r11, #( EndOfJumpTable - JumpTable ) / 4
        ADRCS   r0, ErrorOutOfRange
        ADDCC   pc, pc, r11, LSL #2
        [       UseMsgTrans
        B       MakeErrorWithModuleName                 ; Only one instruction after the ADD pc, pc
        |
        ORRS    pc, lr, #VFlag                          ; Only one instruction after the ADD pc, pc
        ]       ; UseMsgTrans
JumpTable                                               ; Then the jump table
        B       CreateReceive                           ; &40000, 0
        B       ExamineReceive                          ; &40001, 1
        B       ReadReceive                             ; &40002, 2
        B       AbandonReceive                          ; &40003, 3
        B       WaitForReception                        ; &40004, 4
        B       EnumerateRxCBs                          ; &40005, 5
        B       StartTransmit                           ; &40006, 6
        B       PollTransmit                            ; &40007, 7
        B       AbandonTransmit                         ; &40008, 8
        B       DoTransmit                              ; &40009, 9
        B       ReadLocalStation                        ; &4000A, 10
        B       ConvertStatusToString                   ; &4000B, 11
        B       ConvertStatusToError                    ; &4000C, 12
        B       ReadProtection                          ; &4000D, 13
        B       SetProtectionMask                       ; &4000E, 14
        B       ReadStationNumber                       ; &4000F, 15
        B       PrintBanner                             ; &40010, 16
        B       ReadTransportType                       ; &40011, 17
        B       ReleasePort                             ; &40012, 18
        B       AllocatePort                            ; &40013, 19
        B       DeAllocatePort                          ; &40014, 20
        B       ClaimPort                               ; &40015, 21
        B       StartImmediate                          ; &40016, 22
        B       DoImmediate                             ; &40017, 23
        B       AbandonAndReadReceive                   ; &40018, 24
        B       Version                                 ; &40019, 25
        B       NetworkState                            ; &4001A, 26
        B       PacketSize                              ; &4001B, 27
        B       ReadTransportName                       ; &4001C, 28
        B       InetRxDirect                            ; &4001D, 29
        B       EnumerateMap                            ; &4001E, 30
        B       EnumerateTransmit                       ; &4001F, 31
        B       HardwareAddresses                       ; &40020, 32
        B       NetworkParameters                       ; &40021, 33
EndOfJumpTable

        ASSERT  EconetSWICheckValue-Econet_CreateReceive=(EndOfJumpTable-JumpTable)/4

ExitSwi
        [       DebugSWIs
        MOV     r10, psr
        LD      r14, SWILock                            ; Unwind entry count before exiting
54
        CMP     r14, #1
        BLE     %64
        DLINE   "   ", cc
        DECS    r14
        BGT     %54
64
        DLINE   "Exit", cc
        DREG    r0, ", R0=&"
        TEQP    psr, r10
        NOP
        ]
        LD      r14, SWILock                            ; Unwind entry count before exiting
        DEC     r14
        ST      r14, SWILock
        Pull    pc

        LTORG

        [       UseMsgTrans
ErrorOutOfRange
        DCD     ErrorNumber_OutOfRange
        DCB     "BadSWI", 0
        ALIGN
        |
        Err     OutOfRange
        ]

        [       :LNOT: UseMsgTrans
BannerString                                            ; Share 'Econet' with ...
        DCB     "Acorn "
        ]       ; UseMsgTrans
ModuleTitle
SWINameTable
        DCB     EconetSWI_Name, 0
        DCB     "CreateReceive", 0
        DCB     "ExamineReceive", 0
        DCB     "ReadReceive", 0
        DCB     "AbandonReceive", 0
        DCB     "WaitForReception", 0
        DCB     "EnumerateReceive", 0
        DCB     "StartTransmit", 0
        DCB     "PollTransmit", 0
        DCB     "AbandonTransmit", 0
        DCB     "DoTransmit", 0
        DCB     "ReadLocalStationAndNet", 0
        DCB     "ConvertStatusToString", 0
        DCB     "ConvertStatusToError", 0
        DCB     "ReadProtection", 0
        DCB     "SetProtection", 0
        DCB     "ReadStationNumber", 0
        DCB     "PrintBanner", 0
        DCB     "ReadTransportType", 0
        DCB     "ReleasePort", 0
        DCB     "AllocatePort", 0
        DCB     "DeAllocatePort", 0
        DCB     "ClaimPort", 0
        DCB     "StartImmediate", 0
        DCB     "DoImmediate", 0
        DCB     "AbandonAndReadReceive", 0
        DCB     "Version", 0
        DCB     "NetworkState", 0
        DCB     "PacketSize", 0
        DCB     "ReadTransportName", 0
        DCB     "InetRxDirect", 0
        DCB     "EnumerateMap", 0
        DCB     "EnumerateTransmit", 0
        DCB     "HardwareAddresses", 0
        DCB     "NetworkParameters", 0
        DCB     0
        ALIGN

        SUBT    SWI Interface routines.
        OPT     OptPage

        ; ***************************************************
        ; ***   S W I   R e c e i v e   R o u t i n e s   ***
        ; ***************************************************

CreateReceive   ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Port
        ;       R1 => Station
        ;       R2 => Net
        ;       R3 => Buffer Address
        ;       R4 => Size
        ;       R0 <= Handle
        ;       Trashes R10 and R11

        CMP     r0, #255
        BHI     BadPortExit
        CMP     r1, #255
        BHI     BadStationExit
        CMP     r2, #255
        BHI     BadNetExit
        BNE     %10
        TEQ     r1, #255                                ; The only legal partner (255.255)
        BEQ     %50                                     ; Its OK so go ahead
        TEQ     r1, #0
        BEQ     BadStationExit
        B       BadNetExit

10
        TEQ     r2, #0                                  ; Net number 0 is always legal
        BEQ     %55
        TEQ     r1, #0                                  ; Station 0 illegal with non-zero net number
        BEQ     BadStationExit
50
        LD      r11, LocalNetwork
        TEQ     r11, r2
        MOVEQ   r2, #0
55
        Push    "r1-r3, lr"
        MOV     r1, r0                                  ; The port number
        MOV     r0, #ModHandReason_Claim                ; Preserves R1
        MOV     r3, #Size_RxCB
        SWI     XOS_Module
        BVS     ExitCreateReceive
        PHPSEI                                          ; Do the atomic part with IRQs off
        LDR     r0, =RxCBIdentifier
        STR     r0, [ r2, #Offset_Identifier ]
        LD      r0, CurrentHandle
        INC     r0, 4
        ST      r0, CurrentHandle
        STR     r0, [ r2, #Offset_Handle ]
        TEQ     r1, #0                                  ; Map port number zero to 255
        MOVEQ   r1, #255
        STRB    r1, [ r2, #Offset_RxPort ]
        MOV     r3, #Status_RxReady
        STRB    r3, [ r2, #Offset_Status ] 
        LDMIA   sp, { r3, r11 }                         ; Station (R1), Net (R2)
        ORRS    r10, r3, r11                            ; Check for 0.0, only flags important
        MOVEQ   r3, #255                                ; Map to 255.255
        MOVEQ   r11, #255
        STRB    r3, [ r2, #Offset_RxStation ]
        STRB    r11, [ r2, #Offset_RxNetwork ]
        TEQ     r1, #255                                ; Check for wild port
        TEQNE   r3, #255                                ; Or wild station
        LDR     r3, [ sp, #8 ]                          ; Buffer
        ADD     r1, r2, #Offset_Start                   ; Calculate the address of the pair in the record
        STMIA   r1, { r3, r4 }                          ; R4 still has the size
        MOV     r1, #-1                                 ; True
        STRB    r1, [ r2, #Offset_Station ]
        STRB    r1, [ r2, #Offset_Network ]
        STRB    r1, [ r2, #Offset_Port ]
        MOV     r1, #Event_NotReady                     ; Not yet needing eventing
        STR     r1, [ r2, #Offset_Event ]               ; It is still waiting for it's Event
        ; NE means NOT wild stick in the middle of the list
        ; EQ means that some part was wild so it goes at the end
        ADR     r10, RxCBList - Offset_Link             ; Head of the list
        BEQ     InsertAtTheEnd
60                                                      ; Find either the tail of the list or the first wild one
        LDR     r11, [ r10, #Offset_Link ]              ; Look for the end of the list
        TEQ     r11, #NIL                               ; Is this the end of the list?
        BEQ     InsertInList                            ; There were no wilds so insert at the end 
        LDRB    r3, [ r11, #Offset_RxStation ]
        TEQ     r3, #255                                ; Look for a 255 in any one of these
        LDRNEB  r3, [ r11, #Offset_RxPort ]
        TEQNE   r3, #255                                ; two fields; that means it is 'wild'
        MOVNE   r10, r11                                ; Move on to the next record
        BNE     %60
        B       InsertInList

InsertAtTheEnd
        LDR     r11, [ r10, #Offset_Link ]              ; Look for the end of the list
        TEQ     r11, #NIL
        MOVNE   r10, r11                                ; Get the next one
        BNE     InsertAtTheEnd
InsertInList
        STR     r11, [ r2, #Offset_Link ]               ; Make us point to the next (may be NIL)
        STR     r2, [ r10, #Offset_Link ]               ; Make the this one point at us, may be RxCBList
        PLP                                             ; Restore the IRQ state
ExitCreateReceive
        Pull    "r1-r3, pc"

ExamineReceive  ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Handle
        ;       R0 <= Status
        ;       Trashes R10 and R11

        Push    "r9, lr"                                ; Save IRQ state on stack
        PHPSEI                                          ; Also keep in R14
        ADR     r10, RxCBList - Offset_Link             ; Must be done with interrupts OFF
        LDR     r9, =RxCBIdentifier
ExamineLoop                                             ; Jumped to from PollTransmit
        LDR     r10, [ r10, #Offset_Link ]
        TEQ     r10, #NIL
        BEQ     PLPBadHandleExit
        LDR     r11, [ r10, #Offset_Identifier ]
        TEQ     r11, r9
        BNE     PLPInternalErrorExit
        LDR     r11, [ r10, #Offset_Handle ]
        TEQ     r0, r11
        BNE     ExamineLoop
        Pull    "r9, lr"                                ; Get IRQ state in LR
        LDRB    r0, [ r10, #Offset_Status ] 
        BICS    pc, lr, #VFlag                          ; Claer V and restore IRQ state

PLPBadHandleExit
        PLP                                             ; Restore IRQ state from R14
        Pull    "r9, lr"
        B       BadHandleExit

PLPInternalErrorExit
        PLP                                             ; Restore IRQ state from R14
        Pull    "r9, lr"
InternalErrorExit
        ADR     r0, ErrorInternal
        [       UseMsgTrans
        B       MakeErrorWithModuleName
        |
        ORRS    pc, lr, #VFlag
        ]

ErrorInternal
        DCD     ErrorNumber_EconetInternalError
        [       UseMsgTrans
        DCB     "Fatal"
        |
        DCB     "Internal inconsistency failure in module Econet"
        ]
        DCB     0
        ALIGN


ReadReceive     ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Handle
        ;       R0 <= Status byte
        ;       R1 <= Control byte
        ;       R2 <= Port
        ;       R3 <= Station
        ;       R4 <= Net
        ;       R5 <= Buffer Address
        ;       R6 <= Size
        ;       R10 is trashed
        ;       R11 is preserved
        
        Push    "r9, lr"                                ; Save IRQ state on stack
        PHPSEI                                          ; Also keep in R14
        ADR     r10, RxCBList - Offset_Link             ; Must be done with interrupts OFF
        LDR     r9, =RxCBIdentifier
ReadLoop
        LDR     r10, [ r10, #Offset_Link ]
        TEQ     r10, #NIL
        BEQ     PLPBadHandleExit
        LDR     r1, [ r10, #Offset_Identifier ]
        TEQ     r1, r9
        BNE     PLPInternalErrorExit
        LDR     r1, [ r10, #Offset_Handle ]
        TEQ     r0, r1
        BNE     ReadLoop
        PLP                                             ; Restore IRQ state from R14
        Pull    "r9, lr"
ReadReceptionRecord                                     ; In R10 into R0-R6
        LDRB    r0, [ r10, #Offset_Status ] 
        TEQ     r0, #Status_Received                    ; Received ?
        LDREQB  r1, [ r10, #Offset_Control ]
        ANDEQ   r1, r1, #2_01111111                     ; Only the bottom seven bits
        LDREQB  r2, [ r10, #Offset_Port ]
        LDREQB  r3, [ r10, #Offset_Station ]
        LDREQB  r4, [ r10, #Offset_Network ]
        LDREQ   r5, [ r10, #Offset_Start ]
        LDREQ   r6, [ r10, #Offset_RxSize ]
        BICEQS  pc, lr, #VFlag
        MOV     r1, #0                                  ; Fake control value
        LDRB    r2, [ r10, #Offset_RxPort ]
        LDRB    r3, [ r10, #Offset_RxStation ]
        LDRB    r4, [ r10, #Offset_RxNetwork ]
        ADD     r5, r10, #Offset_Start
        LDMIA   r5, { r5, r6 }                          ; Start and Size
        BICS    pc, lr, #VFlag

AbandonReceive ROUT
        ;       R0 => Handle
        ;       R0 <= Status
        ;       R10 is trashed
        ;       R11 is trashed
        Push    "r1-r6, lr"
        BL      AbandonAndReadReceive
        Pull    "r1-r6, pc"

AbandonAndReadReceive
        ;       R0 => Handle
        ;       R0 <= Status byte
        ;       R1 <= Control byte
        ;       R2 <= Port
        ;       R3 <= Station
        ;       R4 <= Net
        ;       R5 <= Buffer Address
        ;       R6 <= Size
        ;       R10 is trashed
        ;       R11 is trashed
        Push    "r9, lr"
        PHPSEI
        ADR     r10, RxCBList - Offset_Link
        LDR     r9, =RxCBIdentifier
AbandonLoop
        MOV     r11, r10                                ; Keep the previous pointer
        LDR     r10, [ r10, #Offset_Link ]
        TEQ     r10, #NIL
        BEQ     PLPBadHandleExit
        LDR     r1, [ r10, #Offset_Identifier ]
        TEQ     r1, r9
        BNE     PLPInternalErrorExit
        LDR     r1, [ r10, #Offset_Handle ]
        TEQ     r0, r1
        BNE     AbandonLoop
        [       NoAbandons
        ORR     r1, r1, #NIL
        STR     r1, [ r10, #Offset_Handle ]             ; Mark the handle as EVIL
        |
        LDR     r0, [ r10, #Offset_Link ]               ; The next link
        STR     r0, [ r11, #Offset_Link ]               ; Unlink it
        ]
        PLP                                             ; Restore IRQ state
        LDRB    r14, [ r10, #Offset_Status ]
        TEQ     r14, #Status_Receiving
        BNE     %97                                     ; Not active
95
        LD      r14, FIQBusy                            ; See if we are busy
        TEQ     r14, #0
        BNE     %95                                     ; It is busy, probably with this record
97
        BL      ReadReceptionRecord
        [       ErrorInfo
        ADRL    r14, RxErrors
        LDR     r11, [ r14, #:INDEX:RxCount - :INDEX:RxErrors ]
        INC     r11
        STR     r11, [ r14, #:INDEX:RxCount - :INDEX:RxErrors ]
        LDR     r11, [ r14, r0, ASL #2 ]
        INC     r11
        STR     r11, [ r14, r0, ASL #2 ]
        ]
        Push    "r0, r2"
        MOV     r0, #ModHandReason_Free
        MOV     r2, r10                                 ; The address of the record
        [       NoAbandons
        CLRV
        |
        SWI     XOS_Module
        ]
        STRVS   r0, [ sp, #0 ]
        Pull    "r0, r2, r9, pc"


WaitForReception ROUT                                   ; Now able to cope with IRQs enabled
        ;       R0 => Handle
        ;       R1 => Delay
        ;       R2 => ESCapableFlag : zero for inESCapable, non-zero for ESCapable
        ;       R0 <= Status byte
        ;       R1 <= Control byte
        ;       R2 <= Port
        ;       R3 <= Station
        ;       R4 <= Net
        ;       R5 <= Buffer Address
        ;       R6 <= Size
        ;       Trashes R11, and R10 indirectly

        Push    "r9, lr"
        MOV     r11, r0
        MOV     r0, #Econet_StartReception
        MOV     r9, #EconetV
        SWI     XOS_CallAVector                         ; Turn on the hourglass
        SWIVC   XOS_ReadMonotonicTime
        Pull    "r9, pc", VS
        ADD     r1, r1, r0                              ; Calculate the finish time
        MOV     r9, psr                                 ; Keep the mode and interrupt state
        TEQP    psr, #0                                 ; Enable all interrupts and go to user mode
        NOP
ReceptionWaitLoop
        MOV     r0, r11
        SWI     XEconet_ExamineReceive
        BVS     RxPollError                             ; Most likely a BadHandle
        TEQ     r0, #Status_Received
        BEQ     ReceptionCompleted
        SWI     XOS_ReadMonotonicTime
        BVS     RxPollError                             ; Very unlikely
        CMP     r0, r1
        MOVPL   r0, #Status_NoReply
        BPL     ReceptionCompleted
        TEQ     r2, #0                                  ; Are we checking for ESCape?
        BEQ     ReceptionWaitLoop
        SWI     XOS_ReadEscapeState                     ; Doesn't enable IRQ, ignore the error
        CLC     VS
        BCC     ReceptionWaitLoop
EscapedOutOfWait
        MOV     r0, #126                                ; Acknowledge ESC
        SWI     XOS_Byte                                ; Doesn't enable IRQ, ignore the error
        MOV     r0, #Status_Escape
ReceptionCompleted
        SWI     XOS_EnterOS                             ; Get back to SVC mode, ignore error
        TEQP    psr, r9                                 ; Restore interrupts and mode (might be IRQ)
        MOV     r1, r0                                  ; The final status to return
        MOV     r0, #Econet_FinishReception
        MOV     r9, #EconetV                            ; Turn off the hourglass
        SWI     XOS_CallAVector                         ; Ignore error
        MOV     r9, r1
        MOV     r0, r11                                 ; The handle
        BL      AbandonAndReadReceive
        TEQ     r0, #Status_Received
        MOVNE   r0, r9                                  ; In the event it has just arrived
        Pull    "r9, pc"                                ; dont't return Escape or NoReply

RxPollError
        MOV     r11, r0                                 ; Keep the error
        SWI     XOS_EnterOS                             ; Get back to SVC mode, ignore error
        TEQP    psr, r9                                 ; Restore interrupts and mode (might be IRQ)
        MOV     r0, #Econet_FinishReception
        MOV     r9, #EconetV                            ; Turn off the hourglass
        SWI     XOS_CallAVector
        MOV     r0, r11                                 ; Restore the original error
        SETV                                            ; Indicate that it was an error
        Pull    "r9, pc"


EnumerateRxCBs  ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Index
        ;       R0 <= Handle
        ;       Trashes R10
        ;       Trashes R11

        TEQ     r0, #0
        BICEQS  pc, lr, #VFlag
        Push    "r1-r4"
        PHPSEI  r4
        ADR     r1, RxCBList - Offset_Link
        LDR     r2, =RxCBIdentifier
EnumerateControlBlockLoop                               ; Common to EnumerateTransmit
        LDR     r1, [ r1, #Offset_Link ]
        TEQ     r1, #NIL
        MOVEQ   r0, #0
        BEQ     ExitEnumerateControlBlocks
        LDR     r3, [ r1, #Offset_Identifier ]
        TEQ     r3, r2
        BNE     ErrorExitEnumerateControlBlocks
        DECS    r0
        BNE     EnumerateControlBlockLoop
        LDR     r0, [ r1, #Offset_Handle ]
ExitEnumerateControlBlocks
        PLP     r4                                      ; Restore IRQ state
        Pull    "r1-r4"
        BICS    pc, lr, #VFlag

ErrorExitEnumerateControlBlocks
        PLP     r4                                      ; Restore IRQ state
        Pull    "r1-r4"
        B       InternalErrorExit

        OPT     OptPage
        ; *****************************************************
        ; ***   S W I   T r a n s m i t   R o u t i n e s   ***
        ; *****************************************************

StartTransmit   ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Flag byte
        ;       R1 => Port
        ;       R2 => Station
        ;       R3 => Net
        ;       R4 => Buffer Address
        ;       R5 => Size
        ;       R6 => Count
        ;       R7 => Delay
        ;       R0 <= Handle
        ;       R2 <= Buffer Address
        ;       R3 <= Station
        ;       R4 <= Net

        ORR     r0, r0, #BitSeven
        CMP     r0, #255
        BHI     BadControlExit
        CMP     r1, #255
        BLO     %20
BadPortExit
        ADR     r0, ErrorBadPort
        [       UseMsgTrans
        B       MakeError

ErrorBadPort
        DCD     ErrorNumber_BadPort
        DCB     "BadPort", 0
        ALIGN
        |
        ORRS    pc, lr, #VFlag

        Err     BadPort
        ]
20
        TEQ     r1, #0
        BEQ     BadPortExit
        CMP     r2, #255                                ; Check the station number, it must be (1..255)
        BLS     %30                                     ; Well, it is small enough
BadStationExit
        ADR     r0, ErrorBadStation
        [       UseMsgTrans
        B       MakeError
        |
        ORRS    pc, lr, #VFlag
        ]
30
        TEQ     r2, #0                                  ; Zero is illegal
        BEQ     BadStationExit
        CMP     r3, #255                                ; Check the network number, it must be (0..255)
        BLS     %40
BadNetExit
        ADR     r0, ErrorBadNetwork
        [       UseMsgTrans
        B       MakeError
        |
        ORRS    pc, lr, #VFlag
        ]
40
        LD      r11, LocalNetwork
        TEQ     r3, r11                                 ; Are we trying to transmit on the local net
        MOVEQ   r3, #0
45
        CMP     r5, #MaxTxSize
        BLS     %50
BadSizeExit
        ADR     r0, ErrorBadSize
        [       UseMsgTrans
        B       MakeError

ErrorBadSize
        DCD     ErrorNumber_BadSize
        DCB     "TooBig", 0
        ALIGN
        |
        ORRS    pc, lr, #VFlag

        Err     BadSize
        ]
50
        Push    "r0-r3, lr"                             ; Preserve over SWI
        MOV     r0, #ModHandReason_Claim
        MOV     r3, #Size_TxCB
        SWI     XOS_Module
        MOV     r10, r2
        STRVS   r0, [ sp ]                              ; Put heap error into stack for exit conditions
        Pull    "r0-r3, lr"
        MOVVS   pc, lr
        STRB    r0, [ r10, #Offset_Control ]
        STRB    r1, [ r10, #Offset_Port ]
        [       DebugIRQ
        LDR     r0, =Border_Red
        ADR     r1, VIDC
        STR     r0, [ r1, #0 ]
        ]
AddTxRecordToList
        LDR     r0, = TxCBIdentifier
        STR     r0, [ r10, #Offset_Identifier ]
        MOV     r0, #-1
        STRB    r2, [ r10, #Offset_Station ]
        STRB    r3, [ r10, #Offset_Network ]
        TEQ     r2, #255
        BNE     %70                                     ; Station number <> 255 ==> is not a broadcast
        TEQ     r3, #0                                  ; Network number = 0    ==> is a broadcast
        CMPNE   r3, #&FC                                ; Network number >= &FC ==> is a broadcast
        BLT     %70
        MOV     r0, #0                                  ; It is a broadcast
70
        STRB    r0, [ r10, #Offset_Broadcast ]          ; Zero for "is a broadcast"
        ADDS    r0, r10, #Offset_Start                  ; Clears V, so that R5 is OK later
        STMIA   r0, { r4, r5, r6, r7 }                  ; Start, Size, Count, Delay
        MOV     r0, r4                                  ; Save start address
        MOV     r4, r3                                  ; LDR r4, [ r10, #Offset_Network ]
        MOV     r3, r2                                  ; LDR r3, [ r10, #Offset_Station ]
        MOV     r2, r0                                  ; Start address
        LD      r0, Time
        STR     r0, [ r10, #Offset_Time ]
        ADD     r11, r0, #ClaimTime                     ; Ten seconds to poll for line free
        STR     r11, [ r10, #Offset_ClaimTime ]
        MOV     r11, #-1                                ; True
        STRB    r11, [ r10, #Offset_Local ]             ; No local transmission occured yet
        MOV     r11, #Event_NotReady                    ; Not yet needing eventing
        STR     r11, [ r10, #Offset_Event ]
        MOV     r11, #Status_TxReady                    ; Set it going
        STRB    r11, [ r10, #Offset_Status ]
        Push    "r2-r7, lr"                             ; Note use of Non FIQ registers
        [       False ; Debug
        DREG    psr, "Tx PSR = &"
        ]
        MOV     r5, psr                                 ; Keep the mode and interrupt state
        MOV     r14, #IFlag
        TST     r14, psr                                ; Is the IRQ flag set?
        TEQEQP  r14, psr                                ; No, so set it for this atomic stuff
        [       False ; Debug
        NOP
        DREG    psr, "Tx PSR = &"
        ]
        LD      r0, CurrentHandle
        INC     r0, 4
        ST      r0, CurrentHandle
        STR     r0, [ r10, #Offset_Handle ]
        ADR     r11, TxCBList - Offset_Link             ; Head of the list
FindEndOfTxList
        LDR     r1, [ r11, #Offset_Link ]               ; Check the next entry
        TEQ     r1, #NIL                                ; Is it the end?
        MOVNE   r11, r1                                 ; No, so skip on to the next
        BNE     FindEndOfTxList                         ; And try again
        STR     r1, [ r10, #Offset_Link ]               ; Mark as the new end of list
        STR     r10, [ r11, #Offset_Link ]              ; Make this one the new tail
        LD      r6, Time
        MOV     r7, r10                                 ; The record to start
        [       DebugSWIs
        LDRB    r4, [ r7, #Offset_Network ]
        BREG    r4, "DoTransmit to station &", cc
        LDRB    r4, [ r7, #Offset_Station ]
        BREG    r4, ".&"
        ]
        B       RestartRecord                           ; Restores mode and flags from R5

DoTransmit      ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Flag byte
        ;       R1 => Port
        ;       R2 => Station
        ;       R3 => Net
        ;       R4 => Buffer Address
        ;       R5 => Size
        ;       R6 => Count
        ;       R7 => Delay
        ;       R0 <= Status
        ;       R2 <= Buffer Address
        ;       R3 <= Station
        ;       R4 <= Net

        Push    "r9, lr"
        BL      StartTransmit                           ; Returns with the record in R10
PollToCompletion
        MOVVC   r0, #Econet_StartTransmission
        MOVVC   r9, #EconetV
        SWIVC   XOS_CallAVector
        Pull    "r9, pc", VS
        LDR     r10, [ r10, #Offset_Handle ]            ; Keep the handle for polling with
        MOV     r9, psr                                 ; Keep the mode and interrupt state
        TEQP    psr, #0                                 ; Enable all interrupts and go to user mode
TxPollLoop
        SWI     XOS_ReadEscapeState                     ; Doesn't enable IRQ, ignore the error
        CLC     VS
        BCS     EscapedOutOfTx
        MOV     r0, r10
        SWI     XEconet_PollTransmit
        BVS     TxPollError                             ; Most likely a BadHandle
        TEQ     r0, #Status_TxReady
        TEQNE   r0, #Status_Transmitting
        BEQ     TxPollLoop
        SWI     XOS_EnterOS                             ; Get back to SVC mode, ignore error
        TEQP    psr, r9                                 ; Restore interrupts and mode (might be IRQ)
        MOV     r0, #Econet_FinishTransmission
        MOV     r9, #EconetV                            ; Turn off the hourglass
        SWI     XOS_CallAVector
        MOV     r0, r10
        BL      AbandonTransmit                         ; Find out how it finished up
        Pull    "r9, pc"

EscapedOutOfTx
        SWI     XOS_EnterOS                             ; Get back to SVC mode, ignore error
        TEQP    psr, r9                                 ; Restore interrupts and mode (might be IRQ)
        MOV     r0, #Econet_FinishTransmission
        MOV     r9, #EconetV                            ; Turn off the hourglass
        SWI     XOS_CallAVector
        MOV     r0, r10
        BL      AbandonTransmit
        MOV     r0, #126                                ; Acknowledge ESC
        SWI     XOS_Byte                                ; Doesn't enable IRQ, ignore the error
        MOV     r0, #Status_Escape
        CLRV
        Pull    "r9, pc"

TxPollError
        MOV     r10, r0                                 ; Keep the error
        SWI     XOS_EnterOS                             ; Get back to SVC mode, ignore error
        TEQP    psr, r9                                 ; Restore interrupts and mode (might be IRQ)
        MOV     r0, #Econet_FinishTransmission
        MOV     r9, #EconetV                            ; Turn off the hourglass
        SWI     XOS_CallAVector
        MOV     r0, r10                                 ; Restore the original error
        SETV                                            ; Indicate that it was an error
        Pull    "r9, pc"

PollTransmit    ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Handle
        ;       R0 <= Status
        ;       Trashes R10 and R11

        Push    "r9, lr"                                      ; Save IRQ state on stack
        PHPSEI                                          ; Also keep in R14
        ADR     r10, TxCBList - Offset_Link
        LDR     r9, =TxCBIdentifier
        B       ExamineLoop

AbandonTransmit ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Handle
        ;       R0 <= Status
        ;       Trashes R10 and R11

        Push    "r1-r3, lr"
        PHPSEI
        ADR     r2, TxCBList - Offset_Link
        LDR     r10, =TxCBIdentifier
AbandonTxLoop
        MOV     r1, r2
        LDR     r2, [ r2, #Offset_Link ]
        TEQ     r2, #NIL
        BNE     AbandonTxCheckIdentifier
        PLP
        Pull    "r1-r3, lr"
BadHandleExit
        ADR     r0, ErrorBadEconetHandle
        [       UseMsgTrans
        B       MakeError

ErrorBadEconetHandle
        DCD     ErrorNumber_BadEconetHandle
        DCB     "Channel", 0
        ALIGN
        |
        ORRS    pc, lr, #VFlag

        Err     BadEconetHandle
        ]

AbandonTxCheckIdentifier
        LDR     r11, [ r2, #Offset_Identifier ]
        TEQ     r11, r10
        BEQ     AbandonTxCheckHandle
        PLP
        Pull    "r1-r3, lr"
        B       InternalErrorExit

AbandonTxCheckHandle
        LDR     r11, [ r2, #Offset_Handle ]
        TEQ     r0, r11
        BNE     AbandonTxLoop
        [       NoAbandons
        ORR     r11, r11, #NIL
        STR     r11, [ r2, #Offset_Handle ]             ; Mark the handle as EVIL
        |
        LDR     r11, [ r2, #Offset_Link ]               ; Get the next item in the list
        STR     r11, [ r1, #Offset_Link ]               ; Link this record out
        ]
        PLP                                             ; Restore IRQs as soon as possible
        LDRB    r10, [ r2, #Offset_Status ] 
        TEQ     r10, #Status_Transmitting
        BNE     %97
95
        LD      r11, FIQBusy                            ; See if we are busy
        TEQ     r11, #0
        BNE     %95                                     ; It is busy
        LDRB    r11, [ r2, #Offset_Status ]
        TEQ     r11, #Status_Transmitted
        MOVEQ   r10, r11
97
        [       ErrorInfo
        ADRL    r3, TxErrors
        LDR     r11, [ r3, #:INDEX:TxCount - :INDEX:TxErrors ]
        INC     r11
        STR     r11, [ r3, #:INDEX:TxCount - :INDEX:TxErrors ]
        LDR     r11, [ r3, r10, ASL #2 ]
        INC     r11
        STR     r11, [ r3, r10, ASL #2 ]
        ]
        MOV     r0, #ModHandReason_Free
        [       NoAbandons
        CLRV
        |
        SWI     XOS_Module
        ]
        MOVVC   r0, r10                                 ; The final status
        Pull    "r1-r3, pc"

EnumerateTransmit ROUT                                  ; Now able to cope with IRQs enabled
        ;       R0 => Index
        ;       R0 <= Handle

        TEQ     r0, #0
        BICEQS  pc, lr, #VFlag
        Push    "r1-r4"
        PHPSEI  r4
        ADR     r1, TxCBList - Offset_Link
        LDR     r2, =TxCBIdentifier
        B       EnumerateControlBlockLoop               ; Common code in EnumerateReceive

        OPT     OptPage
        ; ***********************************************************
        ; ***   S W I   I m m e d i a t e   O p e r a t i o n s   ***
        ; ***********************************************************

StartImmediate  ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Flag byte
        ;       R1 => Destination Address (if needed)
        ;       R2 => Station
        ;       R3 => Net
        ;       R4 => Buffer Address
        ;       R5 => Size
        ;       R6 => Count
        ;       R7 => Delay
        ;       R0 <= Handle
        ;       R2 <= Buffer Address
        ;       R3 <= Station
        ;       R4 <= Net

        ORR     r0, r0, #BitSeven
        CMP     r0, #255
        BLS     %10                                     ; Control value OK (0..255) => (128..255)
BadControlExit
        ADR     r0, ErrorBadControl
        [       UseMsgTrans
        B       MakeError

ErrorBadControl
        DCD     ErrorNumber_BadControl
        DCB     "BadFlag", 0
        ALIGN
        |
        ORRS    pc, lr, #VFlag

        Err     BadControl
        ]
10                                                      ; Now check the port number, it must be (1..255)
        TEQ     r2, #0
        BEQ     BadStationExit
        CMP     r2, #255
        BHS     BadStationExit
        CMP     r3, #255
        BHS     BadNetExit
        LD      r11, LocalNetwork
        TEQ     r3, r11                                 ; Are we trying to transmit on the local net
        MOVEQ   r3, #0
45
        CMP     r5, #MaxTxSize
        BHI     BadSizeExit
        Push    "r0-r3, lr"                             ; Preserve over SWI
        MOV     r0, #ModHandReason_Claim
        MOV     r3, #Size_TxCB
        SWI     XOS_Module
        MOV     r10, r2
        STRVS   r0, [ sp, #0 ]                          ; Put heap error into stack for exit conditions
        Pull    "r0-r3, lr"
        MOVVS   pc, lr
        STRB    r0, [ r10, #Offset_Control ]
        STR     r1, [ r10, #Offset_Destination ]
        MOV     r0, #0
        STRB    r0, [ r10, #Offset_Port ]
        B       AddTxRecordToList

DoImmediate     ROUT                                    ; Now able to cope with IRQs enabled
        ;       R0 => Flag byte
        ;       R1 => Destination Address (if needed)
        ;       R2 => Station
        ;       R3 => Net
        ;       R4 => Buffer Address
        ;       R5 => Size
        ;       R6 => Count
        ;       R7 => Delay
        ;       R0 <= Status
        ;       R2 <= Buffer Address
        ;       R3 <= Station
        ;       R4 <= Net

        Push    "r9, lr"
        BL      StartImmediate                          ; Returns with the record in R10
        B       PollToCompletion

        LTORG

        [       UseMsgTrans
ErrorBadStation
        DCD     ErrorNumber_BadStation
        DCB     "BadStn", 0
        ALIGN
ErrorBadNetwork
        DCD     ErrorNumber_BadNetwork
        DCB     "BadNet", 0
        ALIGN
        |
        Err     BadStation
        Err     BadNetwork
        ]


        ; *********************************************
        ; ***   S W I   M i s c   R o u t i n e s   ***
        ; *********************************************

ReadTransportType ROUT
        MOV     r2, #2
        BICS    pc, lr, #VFlag

Version         ROUT
        LDR     r2, =500+$CurrentVersion
        BICS    pc, lr, #VFlag

NetworkState    ROUT
        LD      r2, Status2
        ANDS    r2, r2, #DCD
        MOVNE   r2, #1
        BICS    pc, lr, #VFlag

PacketSize      ROUT
        MOV     r2, #&500
        BICS    pc, lr, #VFlag

ReadTransportName
        ADRL    r2, ModuleTitle
        BICS    pc, lr, #VFlag


        [       UseMsgTrans
ConvertStatusToError ROUT                               ; Now able to cope with IRQs enabled
        ; R0 => Status number
        ; R1 => Pointer to core if required (or used)
        ; R2 => Maximum buffer size
        ; R3 => Station number (if used) or a pointer to text
        ; R4 => Network number (if used)
        ; R0 <= Error pointer (maybe R1, maybe ROM)
        AND     r0, r0, #&FF                            ; Only bother with the bottom byte
        CMP     r0, #Status_MaxValue
        BGT     BadStatus
        Push    "r1-r4, lr"
        TEQ     r0, #Status_NotListening
        ADREQ   r11, ErrorStationNotListening
        BEQ     ConvertErrorWithArgument
        TEQ     r0, #Status_NoReply
        ADREQ   r11, ErrorNoReplyFromStation
        BEQ     ConvertErrorWithArgument
        TEQ     r0, #Status_NotPresent
        ADREQ   r11, ErrorStationNotPresent
        BEQ     ConvertErrorWithArgument
        TEQ     r0, #Status_NetError
        BNE     StandardError
        ADR     r11, ErrorStationNetError
        TEQ     r3, #&FF                                ; Is this a broadcast?
        BNE     ConvertErrorWithArgument                ; No, so add number to message
        MOV     r0, #Status_BroadcastNetError
StandardError
        ADR     r14, TokenTable
        ADD     r14, r14, r0, LSL #3                    ; TokenTable + Status * 8
        ADD     r0, r14, r0, LSL #2                     ; TokenTable + Status * 12
        MOV     r4, #0
        MOV     r3, r2                                  ; Buffer size
        MOV     r2, r1                                  ; Buffer address
        BL      MessageTransErrorLookup
        Pull    "r1-r4, pc"

BadStatus
        ADR     r0, ErrorBadStatus
        B       MakeError

ConvertErrorWithArgument
        TEQ     r3, #0                                  ; Is the station number zero?
        BEQ     StandardError                           ; Yes so no substitution
        Push    "r0-r6"                                 ; Inputs to conversion
        CMP     r3, #255                                ; Is this an invalid station number?
        BHI     TryErrorWithText                        ; Yes, give up treating as station number
        ADD     r0, sp, #12                             ; Point at the input parameters
        ADD     r1, sp, #20                             ; Point at the output string space
        MOV     r2, #8                                  ; String size
        SWI     XOS_ConvertNetStation
        BVS     ErrorWithTextFails
        ADD     r4, sp, #20
ErrorWithText
        Pull    "r0-r2"
        MOV     r0, r11                                 ; Selected error block
        MOV     r3, r2                                  ; Buffer size
        MOV     r2, r1                                  ; Buffer address
        BL      MessageTransErrorLookup
        INC     sp, 16                                  ; Remove stack frame
        Pull    "r1-r4, pc"

TryErrorWithText
        MOV     r0, r3
        MOV     r1, r3
ErrorWithTextLoop
        SWI     XOS_ValidateAddress
        BVS     ErrorWithTextFails
        BCS     ErrorWithTextFails
        LDRB    r2, [ r1 ], #1
        TEQ     r2, #0                                  ; Is this the terminator?
        BNE     ErrorWithTextLoop
        MOV     r4, r0                                  ; The argument pointer
        B       ErrorWithText

ErrorWithTextFails
        Pull    "r0-r2"
        INC     sp, 16                                  ; Remove stack frame
        B       StandardError                           ; Do it as normal

ConvertStatusToString ROUT                              ; Now able to cope with IRQs enabled
        ;       R0 => Status number
        ;       R1 => Destination core
        ;       R2 => Number of bytes remaining in the buffer
        ;       R3 => Station number (if used) or a pointer to text
        ;       R4 => Network number (if used)
        ;       R0 <= Input value of R1
        ;       R1 <= Updated pointer
        ;       R2 <= Bytes remaining, updated
        AND     r0, r0, #&FF                            ; Only bother with the bottom byte
        CMP     r0, #Status_MaxValue
        BGT     BadStatus
        Push    "r3-r4, lr"
        TEQ     r0, #Status_NotListening
        ADREQ   r11, TokenStationNotListening
        BEQ     ConvertStringWithArgument
        TEQ     r0, #Status_NoReply
        ADREQ   r11, TokenNoReplyFromStation
        BEQ     ConvertStringWithArgument
        TEQ     r0, #Status_NotPresent
        ADREQ   r11, TokenStationNotPresent
        BEQ     ConvertStringWithArgument
        TEQ     r0, #Status_NetError
        BNE     StandardConvert
        ADR     r11, TokenStationNetError
        TEQ     r3, #&FF                                ; Is this a broadcast?
        BNE     ConvertStringWithArgument               ; No, so add number to message
        MOV     r0, #Status_BroadcastNetError
StandardConvert
        Push    r2                                      ; Save the buffer length for later
        MOV     r3, r2                                  ; Buffer size
        MOV     r2, r1                                  ; Buffer address
        ADR     r14, TokenTable + 4                     ; Skip the error numbers
        ADD     r14, r14, r0, LSL #3                    ; TokenTable + Status * 8
        ADD     r1, r14, r0, LSL #2                     ; TokenTable + Status * 12
        MOV     r4, #0
        BL      MessageTransGSLookup
        MOVVC   r0, r2                                  ; Address of result
        ADDVC   r1, r0, r3                              ; Point at the message
        Pull    r2
        SUBVC   r2, r2, r3                              ; Work out how much space is left
        Pull    "r3-r4, pc"

ConvertStringWithArgument
        TEQ     r3, #0                                  ; Is the station number zero?
        BEQ     StandardConvert                         ; Yes so no substitution
        Push    "r0-r6"                                 ; Inputs to conversion
        CMP     r3, #255                                ; Is this an invalid station number?
        BHI     TryStringWithText                       ; Yes, give up treating as station number
        ADD     r0, sp, #12                             ; Point at the input parameters
        ADD     r1, sp, #20                             ; Point at the output string space
        MOV     r2, #8                                  ; String size
        SWI     XOS_ConvertNetStation
        BVS     StringWithTextFails
        ADD     r4, sp, #20
StringWithText
        Pull    "r0-r1"
        LDR     r3, [ sp, #0 ]                          ; Buffer size
        MOV     r2, r1                                  ; Buffer address
        MOV     r1, r11                                 ; Selected token
        ADD     r4, sp, #12
        BL      MessageTransGSLookup
        MOVVC   r0, r2                                  ; Address of result
        ADDVC   r3, r3, #1                              ; Allow for the terminator
        ADDVC   r1, r0, r3                              ; Point at the message
        Pull    r2
        SUBVC   r2, r2, r3                              ; Work out how much space is left
        INC     sp, 16                                  ; Remove stack frame
        Pull    "r3-r4, pc"

TryStringWithText
        MOV     r0, r3
        MOV     r1, r3
StringWithTextLoop
        SWI     XOS_ValidateAddress
        BVS     StringWithTextFails
        BCS     StringWithTextFails
        LDRB    r2, [ r1 ], #1
        TEQ     r2, #0                                  ; Is this the terminator?
        BNE     StringWithTextLoop
        MOV     r4, r0                                  ; The argument pointer
        B       StringWithText

StringWithTextFails
        Pull    "r0-r2"
        INC     sp, 16                                  ; Remove stack frame
        B       StandardConvert                         ; Do it as normal

ValidateReadAddress
        Push    lr
        SWI     XOS_ValidateAddress
        Pull    pc, VS
        ADRCS   Error, ErrorCoreNotReadable
ValidateExit
        BLCS    MakeError
        Pull    pc

ErrorCoreNotReadable
        DCD     ErrorNumber_CoreNotReadable
        DCB     "BadRead", 0
        ALIGN

ErrorBadStatus
        DCD     ErrorNumber_BadStatus
        DCB     "BadStat", 0
        ALIGN

TokenTable
        DCD     ErrorNumber_Transmitted
        DCB     "TxOK", 0
        ALIGN
        ASSERT  ( Status_LineJammed * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_LineJammed
        DCB     "LineJam", 0
        ALIGN
        ASSERT  ( Status_NetError * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_NetError
        DCB     "NetErr", 0
        ALIGN
        ASSERT  ( Status_NotListening * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_NotListening
        DCB     "NotLstn", 0
        ALIGN
        ASSERT  ( Status_NoClock * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_NoClock
        DCB     "NoClk", 0
        ALIGN
        ASSERT  ( Status_TxReady * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_TxReady
        DCB     "TxReady", 0
        ALIGN
        ASSERT  ( Status_Transmitting * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_Transmitting
        DCB     "Txing", 0
        ALIGN
        ASSERT  ( Status_RxReady * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_RxReady
        DCB     "RxReady", 0
        ALIGN
        ASSERT  ( Status_Receiving * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_Receiving
        DCB     "Rxing", 0
        ALIGN
        ASSERT  ( Status_Received * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_Received
        DCB     "Rxd", 0, 0
        ALIGN
        ASSERT  ( Status_NoReply * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_NoReply
        DCB     "NoReply", 0
        ALIGN
        ASSERT  ( Status_Escape * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_Escape
        DCB     "Escape", 0
        ALIGN
        ASSERT  ( Status_NotPresent * 12 ) = ( . - TokenTable )
        DCD     ErrorNumber_NotPresent
        DCB     "NotPres", 0
        ALIGN
        ASSERT  ( ( Status_MaxValue + 1 ) * 12 ) = ( . - TokenTable )
Status_BroadcastNetError * Status_MaxValue + 1
        DCD     ErrorNumber_NetError
        DCB     "BrdcstE", 0
        ALIGN

ErrorStationNotListening
        DCD     ErrorNumber_NotListening
TokenStationNotListening
        DCB     "StnNLsn", 0
        ALIGN
ErrorNoReplyFromStation
        DCD     ErrorNumber_NoReply
TokenNoReplyFromStation
        DCB     "StnNRpy", 0
        ALIGN
ErrorStationNotPresent
        DCD     ErrorNumber_NotPresent
TokenStationNotPresent
        DCB     "StnNPrs", 0
        ALIGN
ErrorStationNetError
        DCD     ErrorNumber_NetError
TokenStationNetError
        DCB     "StnNErr", 0
        ALIGN

        |       ; UseMsgTrans
ConvertStatusToError ROUT                               ; Now able to cope with IRQs enabled
        ;       R0 => Status number
        ;       R1 => Pointer to core if required (or used)
        ;       R2 => Maximum buffer size
        ;       R3 => Station number (if used)
        ;       R4 => Network number (if used)
        ;       R0 <= Error pointer (maybe R1, maybe ROM)
        ;       R1 and R2 Trashed

        MOV     r10, r0                                 ; Save for later
        AND     r0, r0, #&FF                            ; Only bother with the bottom byte
        CMP     r0, #Status_MaxValue
        BGT     %95                                     ; Report error
        TEQ     r1, #0                                  ; Check the buffer given by the client
        BEQ     %10                                     ; No good
        TST     r1, #ARM_CC_Mask
        BNE     %10                                     ; No good either
        TEQ     r0, #Status_NotListening
        ADREQ   r0, StartOfNotListening
        BEQ     %15                                     ; Do the special cases
        TEQ     r0, #Status_NoReply
        ADREQ   r0, StartOfNoReply
        BEQ     %15                                     ; Do the special cases
        TEQ     r0, #Status_NotPresent
        ADREQ   r0, StartOfNotPresent
        BEQ     %15                                     ; Do the special cases
10
        ADR     r11, ErrorStringTable
        LDR     r0, [ r11, r0, LSL #2 ]
        ADRL    r11, Origin
        ADD     r0, r0, r11                             ; Don't forget runtime offset
        ORRS    pc, lr, #VFlag

15      ; Special case for not listening etc.
        ; R0 is the Address of the first half of the message.
        ; R1 is the client's buffer, which is probably OK.
        ; R2 is size
        ; R4.R3 is the number
        Push    "r1, r3, r4, lr"
        MOV     r3, r0
        CMP     r2, #6
        BLT     %30
        LDR     r0, [ r3 ], #4
        STR     r0, [ r1 ], #4
        DEC     r2, 4
20
        TEQ     r2, #0
        BEQ     %30
        LDRB    r0, [ r3 ], #1
        TEQ     r0, #0                                  ; Terminator
        STRNEB  r0, [ r1 ], #1
        SUBNE   r2, r2, #1                              ; One less spare byte in the buffer
        BNE     %20
        ADD     r0, sp, #4                              ; Address of data to convert
        SWI     XOS_ConvertNetStation
        BVS     %30
25
        TEQ     r2, #0
        BEQ     %30
        LDRB    r0, [ r3 ], #1
        STRB    r0, [ r1 ], #1
        DEC     r2
        TEQ     r0, #0                                  ; Terminator
        BNE     %25
        Pull    "r0, r3, r4, lr"
        ORRS    pc, lr, #VFlag

30
        Pull    "r1, r3, r4, lr"
        AND     r0, r10, #&FF                           ; Restore status, but only bother with the bottom byte
        B       %10                                     ; Return the ROM value

ConvertStatusToString
        ; R0 => Status number
        ; R1 => Destination core
        ; R2 => Number of bytes remaining in the buffer
        ; R3 => Station number (if used)
        ; R4 => Network number (if used)
        ; R0 <= Input value of R1
        ; R1 <= Updated pointer
        ; R2 <= Bytes remaining, updated

        AND     r0, r0, #&FF                            ; Only bother with the bottom byte
        CMP     r0, #Status_MaxValue
        BGT     %95                                     ; Report error
        MOV     r10, r1                                 ; Hold the input value
        TEQ     r3, #0
        BEQ     %40
        TEQ     r0, #Status_NotListening
        ADREQ   r0, StringOfNotListening
        BEQ     %60                                     ; Do the special cases
        TEQ     r0, #Status_NoReply
        ADREQ   r0, StringOfNoReply
        BEQ     %60                                     ; Do the special cases
        TEQ     r0, #Status_NotPresent
        ADREQ   r0, StringOfNotPresent
        BEQ     %60                                     ; Do the special cases
40
        MOV     r0, r0, LSL #2
        ADR     r11, ErrorStringTable
        LDR     r0, [ r11, r0 ]
        ADRL    r11, Origin
        ADD     r0, r0, r11                             ; Don't forget runtime offset
        INC     r0, 4                                   ; Point to the string, not the error number
50
        TEQ     r2, #0
        BEQ     %85
        LDRB    r11, [ r0 ], #1
        STRB    r11, [ r1 ], #1
        DEC     r2
        TEQ     r11, #0
        BNE     %50
        DEC     r1
        MOV     r0, r10                                 ; Input value of R1
        BICS    pc, lr, #VFlag

60      ; Special case for not listening etc.
        ; R0 is the Address of the first half of the message.
        ; R1 is the client's buffer, which is probably OK.
        ; R2 is size
        ; R4.R3 is the number
        Push    "r3, r4, lr"                            ; Puts the conversion data in store
        MOV     r3, r0                                  ; Source pointer
65
        TEQ     r2, #0
        BEQ     %80
        LDRB    r0, [ r3 ], #1
        TEQ     r0, #0                                  ; Terminator
        STRNEB  r0, [ r1 ], #1
        SUBNE   r2, r2, #1                              ; One less spare byte in the buffer
        BNE     %65
        MOV     r0, sp                                  ; Address of data to convert
        SWI     XOS_ConvertNetStation
        Pull    "r3, r4, pc", VS
70
        TEQ     r2, #0
        BEQ     %80
        LDRB    r0, [ r3 ], #1
        STRB    r0, [ r1 ], #1
        DEC     r2
        TEQ     r0, #0                                  ; Terminator
        BNE     %70
        Pull    "r3, r4, lr"
        DEC     r1
        MOV     r0, r10                                 ; Input value of R1
        BICS    pc, lr, #VFlag

80
        Pull    "r3, r4, lr"
85
        ADR     r0, ErrorCDATBufferOverflow
        ORRS    pc, lr, #VFlag

        Err     CDATBufferOverflow

95
        ADR     r0, ErrorBadStatus
        ORRS    pc, lr, #VFlag

        Err     BadStatus

        MACRO
        StringEntry $name
        ASSERT  ( Status_$name * 4 ) = ( . - ErrorStringTable )
        DCD     Error$name - Origin
        MEND

ErrorStringTable ; Must be in the same order as the status entries

        StringEntry     Transmitted
        StringEntry     LineJammed
        StringEntry     NetError
        StringEntry     NotListening
        StringEntry     NoClock
        StringEntry     TxReady
        StringEntry     Transmitting
        StringEntry     RxReady
        StringEntry     Receiving
        StringEntry     Received
        StringEntry     NoReply
        StringEntry     Escape
        StringEntry     NotPresent
        ASSERT  ( ( Status_MaxValue + 1 ) * 4 ) = ( . - ErrorStringTable )

        Err     Transmitted
        Err     LineJammed
        Err     NetError
        Err     NotListening
StartOfNotListening
        DCD     ErrorNumber_NotListening
StringOfNotListening
        DCB     ErrorString_NotListening1, 0
        DCB     ErrorString_NotListening2, 0
        ALIGN
        Err     NoClock
        Err     TxReady
        Err     Transmitting
        Err     RxReady
        Err     Receiving
        Err     Received
        Err     NoReply
StartOfNoReply
        DCD     ErrorNumber_NoReply
StringOfNoReply
        DCB     ErrorString_NoReply1, 0
        DCB     ErrorString_NoReply2, 0
        ALIGN
        Err     Escape
        Err     NotPresent
StartOfNotPresent
        DCD     ErrorNumber_NotPresent
StringOfNotPresent
        DCB     ErrorString_NotPresent1, 0
        DCB     ErrorString_NotPresent2, 0
        ALIGN
        ]       ; UseMsgTrans

ReadProtection
        LD      r0, Protection
        BICS    pc, lr, #VFlag

SetProtectionMask
        LD      r10, Protection
        AND     r11, r10, r1
        EOR     r11, r11, r0
        TST     r11, #Econet_Prot_Continue :OR: Econet_Prot_MachinePeek
        BNE     ExitBadMask
        BIC     r11, r11, #Econet_Prot_WriteThrough     ; Don't let the write through bit in
        ST      r11, Protection
        TST     r0, #Econet_Prot_WriteThrough
        MOVEQ   r0, r10
        BICEQS  pc, lr, #VFlag
        Push    "r1, r2, lr"
        MOV     r0, #WriteCMOS
        MOV     r1, #ProtectionCMOS
        MOV     r2, r11
        TST     r2, #Econet_Prot_GetRegisters
        EORNE   r2, r2, #BitEight :OR: BitSix           ; Set BitSix, clear BitEight
        TST     r2, #BitNine                            ; Save the reserved bit as well
        EORNE   r2, r2, #BitNine :OR: BitSeven          ; Set BitSeven, clear BitNine
        SWI     XOS_Byte
        MOVVC   r0, r10
        Pull    "r1, r2, pc"

ExitBadMask
        ADR     r0, ErrorBadMask
        [       UseMsgTrans
        B       MakeError

ErrorBadMask
        DCD     ErrorNumber_BadMask
        DCB     "BadMask", 0
        ALIGN
        |
        ORRS    pc, lr, #VFlag

        Err     BadMask
        ]

ReadLocalStation
        LD      r0, LocalStation
        LD      r1, LocalNetwork
        BICS    pc, lr, #VFlag

; ***************************************************
; ***  R E A D   A   S T A T I O N   N U M B E R  ***
; ***************************************************
;
; Inputs   R1 : Address of number
; Outputs  R1 : Updated
;          R2 : Station number (-1 for not found)
;          R3 : Network number (-1 for not found)

ReadStationNumber ROUT                                  ; Now able to cope with IRQs enabled
        Push    "r0, lr"
        MOV     r10, #-1                                ; Set up the default values
        MOV     r11, #-1
        MOV     r2, #0                                  ; Handle no text at all
        LDRB    r0, [ r1 ]                              ; Check the first character
        TEQ     r0, #"."                                ; In case we have the special case
        BEQ     DefaultLeadingZero
        CMP     r0, #" "
        BLE     ReadCompleted
        MOV     r0, #10                                 ; Default base
        SWI     XOS_ReadUnsigned                        ; First number, could be either type
        BVS     ExitReadStation
        LDRB    r0, [ r1 ]                              ; Check the terminating character
        TEQ     r0, #"."
        ADDEQ   r1, r1, #1
        MOVEQ   r11, r2                                 ; It was a net nunber
        BEQ     ReadStationNumberPart
ReadCompleted
        CMP     r2, #-1
        BEQ     %10
        LDRB    r0, [ r1 ]                              ; Check the terminating character again
        CMP     r0, #" "
        BGT     %50                                     ; Must be bad
        MOV     r10, r2                                 ; It was a station number
DoneReadingStation
        CMP     r10, #-1
        BEQ     %30                                     ; No station number to check
        TEQ     r10, #0
        BEQ     %10
        CMP     r10, #254
        BLS     %30
10
        ADRL    r0, ErrorBadStation
20
        [       UseMsgTrans
        BL      MakeError
        |
        SETV
        ]
ExitReadStation
        STRVS   r0, [ sp ]
        MOV     r2, r10
        MOV     r3, r11
        Pull    "r0, pc"

        [       UseMsgTrans
60
        ADRL    r0, ErrorBadNetwork
        B       %20
        ]

30
        CMP     r11, #-1
        BEQ     %40
        CMP     r11, #254
        BHI     %60
40
        CLRV
        B       ExitReadStation

DefaultLeadingZero
        MOV     r11, #0
        INC     r1
ReadStationNumberPart
        CMP     r2, #-1
        BEQ     %60
        LDRB    r0, [ r1 ]                              ; Check the first character
        CMP     r0, #" "
        BLE     DoneReadingStation                      ; Exit if not any more characters
        MOV     r0, #10                                 ; Default base
        SWI     XOS_ReadUnsigned                        ; Second number will be station
        BVS     ExitReadStation
        B       ReadCompleted

50
        ADR     r0, ErrorBadNumb
        B       %20

        [       UseMsgTrans
ErrorBadNumb
        DCD     ErrorNumber_BadNumb
        DCB     "BadNumb", 0
        ALIGN
        |
60
        ADRL    Error, ErrorBadNetwork
        B       %20

        Err     BadNumb
        ]

HardwareAddresses ROUT
        ;       R0 ==> Address of the MC68B54
        ;       R1 ==> Address of the FIQ mask register
        ;       R2 ==> Bit mask to use on the FIQ mask register

        [       PoduleCapable
        ASSERT  InterruptAddress - HardwareAddress = 4
        ASSERT  InterruptMask - InterruptAddress = 4
        ADR     r0, HardwareAddress
        LDMIA   r0, {r0, r1, r2}                        ; Hardware, Interrupt, Mask
        [       ?InterruptMask = 1
        AND     r2, r2, #&FF
        |
        [       ?InterruptMask <> 4
        !       1, "InterruptMask is neither a byte or a word"
        ]
        ]
        |
        LDR     r0, =EconetController                   ; If not from a podule
        ADR     r1, IOC
        ADD     r1, r1, #IOCFIQMSK                      ; Defaults for internal hardware
        MOV     r2, #econet_FIQ_bit
        ]
        BICS    pc, lr, #VFlag

        LTORG

InetRxDirect
        BICS    pc, lr, #VFlag

EnumerateMap
        ;       R0 <== Flags, all reserved must be zero
        ;       R4 <== Enumeration reference, 0 to start
        ;       R1 ==> Net number
        ;       R2 ==> Pointer to the net name
        ;       R3 ==> IP subnetwork address
        ;       R4 ==> Next enumeration reference, -1 for no more
        MOV     r4, #-1
        BICS    pc, lr, #VFlag

NetworkParameters ROUT
        ;       R0 ==>  Network clock period in quarter micro seconds
        ;               e.g. 5 micro seconds is &16
        ;       R1 ==>  Network clock frequency in KHz
        ;               e.g. &C8
        ;       R2 ==>  Reserved
        ;       R3 ==>  Reserved
        [       BroadcastByHand
        LD      r0, BroadcastTime
        TEQ     r0, #0
        MOVEQ   r1, #0
        BEQ     ExitNetworkParameters
        MOV     r2, #4000
        DivRem  r1, r2, r0, r3
        |
        MOV     r0, #0
        MOV     r1, #0
        ]
ExitNetworkParameters
        [       DebugFindLocalNet
        DREG    r0, "Period in 250ns: "
        DREG    r1, "Frequency in KHz: "
        ]
        BICS    pc, lr, #VFlag

        OPT     OptPage

AllocatePort    ROUT
        ;       R0 <= Port
        ;       Ports are stored in an array of words, each Port has an enumerated type
        ;       representing its state, 2_00 for not allocated, 2_01 for allocated and
        ;       2_10 for permanently allocated.  These are stored 16 to a word in 16 words
        ;       the first word contains Ports 0 to 15 etc. and within each word the Ports
        ;       are stored low Port in low bits so bits 2 and 3 have Port 1's data.

        Push    "r1-r4, lr"
        LD      r0, PortHint
        MOV     r4, r0                                  ; Save hint for limit check
TryNextPort
        INC     r0
        [       True                                    ; New Port allocation strategy
        TEQ     r0, #Port_AllocationMaximum + 1
        MOVEQ   r0, #Port_AllocationMinimum
        |
        AND     r0, r0, #&FF                            ; Keep it down to a byte
        ]
        TEQ     r0, r4
        BEQ     NoMorePortsExit
        BL      FindPort
        LDR     r14, [ r1 ]
        MOV     r3, #2_11                               ; Mask value
        TST     r14, r3, LSL r2                         ; Check
        BNE     TryNextPort
        MOV     r3, #2_01                               ; New value
        ORR     r14, r14, r3, LSL r2                    ; OR is OK since field must have been zero
        ST      r0, PortHint
UpdatePortTable
        STR     r14, [ r1 ]
        CLRV
        Pull    "r1-r4, pc"

DeAllocatePort
        ;       R0 => Port
        Push    lr
        BL      CheckPort
        BL      FindPort
        LDR     r14, [ r1 ]
        MOV     r3, r14, LSR r2                         ; Put old state at the bottom
        AND     r3, r3, #2_11                           ; Mask to get only this value
        TEQ     r3, #2_01
        BNE     PortNotAllocatedExit
ClearPort
        MOV     r3, #2_11                               ; Clearance mask
        BIC     r14, r14, r3, LSL r2
        B       UpdatePortTable

ClaimPort
        ;       R0 => Port
        Push    lr
        BL      CheckPort
        BL      FindPort
        LDR     r14, [ r1 ]
        MOV     r3, #2_11                               ; Mask
        TST     r14, r3, LSL r2
        BNE     PortAllocatedExit
        MOV     r3, #2_10                               ; New value
        ORR     r14, r14, r3, LSL r2                    ; OR is OK since field must have been zero
        B       UpdatePortTable

ReleasePort
        ;       R0 => Port
        Push    lr
        BL      CheckPort
        BL      FindPort
        LDR     r14, [ r1 ]
        MOV     r3, r14, LSR r2                         ; Put old state at the bottom
        AND     r3, r3, #2_11                           ; Mask to get only this value
        TEQ     r3, #2_10
        BEQ     ClearPort
PortNotReservedExit
PortNotAllocatedExit
        ADR     Error, ErrorPortNotAllocated
        B       PortExit

FindPort        ROUT
        ;       Takes a Port and returns the address and shift value
        ;       R0 => Port
        ;       R1 <= Address
        ;       R2 <= Shift value [ 0, 2, 4, ... 28, 30 ]
        AND     r2, r0, #2_11110000                     ; Mask off the offset part
        ADR     r1, PortTable
        ADD     r1, r1, r2, LSR #2                      ; Add in the offset
        AND     r2, r0, #2_00001111
        LSL     r2, 1                                   ; Calculate the shift value
        MOV     pc, lr

CheckPort       ROUT
        TEQ     r0, #0
        BEQ     PortBadExit
        CMP     r0, #254
        BHI     PortBadExit
        Push    "r1-r4"
        MOV     pc, lr

PortAllocatedExit
        ADR     Error, ErrorPortAllocated
        B       PortExit

NoMorePortsExit
        ADR     Error, ErrorNoMorePorts
PortExit
        [       UseMsgTrans
        BL      MakeError
        |
        SETV
        ]
        Pull    "r1-r4, pc"

PortBadExit
        ADRL    r0, ErrorBadPort
        [       UseMsgTrans
        BL      MakeError
        Pull    pc

ErrorPortNotAllocated
        DCD     ErrorNumber_PortNotAllocated
        DCB     "PtNtAlc", 0
        ALIGN
ErrorPortAllocated
        DCD     ErrorNumber_PortAllocated
        DCB     "PortAlc", 0
        ALIGN
ErrorNoMorePorts
        DCD     ErrorNumber_NoMorePorts
        DCB     "NoPorts", 0
        ALIGN
        |
        SETV
        Pull    pc

        Err     PortNotAllocated
        Err     PortAllocated
        Err     NoMorePorts
        ]

        LTORG

        END