; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
        SUBT    Debugging routines and macros => &.Hdr.Debug

OldOpt  SETA    {OPT}
        OPT     OptNoList+OptNoP1List

; ***********************************
; ***    C h a n g e   L i s t    ***
; ***********************************

; Date       Name  Description
; ----       ----  -----------
; 23-Nov-87  SKS   Added $routine to DREG
; 11-Feb-88  SKS   Added integer printing code
; 19-Feb-88  SKS   Make $nonewline consistent
; 11-Mar-88  SKS   Added DSTRING macro, tweaked others
; 18-Mar-88  SKS   Fixed Tutu_PrintString
; 27-Apr-88  SKS   Fixed DLINE wrt. inversing, DSTRING wrt. r14_svc
; 27-Apr-88  SKS   Fixed DSTRING wrt. r14_svc properly (wrong offset before !)
; 05-May-88  SKS   Fixed DSTRING to give address in invalids
; 10-May-88  SKS   Added Host_Debug so you can switch to Host debug much easier
; 22-Jun-88  SKS   Error trapping for $cc field
; 20-Jul-88  SKS   Fixed DLINE with no arg
; 20-Aug-91  JSR   Add Debug_MaybeIRQ and rewrite to be robust in this mode.
; 19-Aug-92  BC    Added 'Char' mode for printing out a single character
; 26-Nov-93  BC    Removed address validity checking (for Medusa)
; 15-Dec-93  BC    Added all the code and Macros for TML debugging
; 24-Jan-94  BC    Changed macro name InsertHostDebugRoutines to avoid clashing
; 31-Aug-94  SMC   Added file debugging.
; 20-Sep-94  SMC   Added DebugIt module support.
; 18-May-00  BJGA  32-bit compatible.
;
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Use of hdr.Debug: r13 MUST be a FD stack pointer. Do not use otherwise

;       GET     &.Hdr.Debug

; ... defs, etc ...

;       InsertDebugRoutines             ; ensure this is after module header !
;                                       ; conventional to have this just before
;       END
;
; These are the controlling logicals which are provided:
; Debug_MaybeIRQ     -  Set to true to protect against LR_SVC corruptions from SWI calling
; Host_Debug         -  Set to true to send output to the tube
;                       Note that this done by InsertTMLInitialisation
; HostDebugViaTML    -  Set to true to use TML to send debug stream to host
;                       Note that this done by InsertTMLInitialisation
; Debug_File         -  Set to "<filename>" to send debug stream to the specified file
;                       (Host_Debug must be set to false).
; Debug_Module       -  Set to true to use DebugItModule.
; Debug_DADebug      -  Set to true to use DADebug
;
; When using TML it is necessary to initialise the TML code.  To do this the MACRO
; InsertTMLInitialisation must be invoked in the module initialisation code, it assumes
; a valid stack in R13, it preserves all registers EXCEPT R0 if an error occurs.  The
; flags are undefined except V which indicates the error status in the usual way.  If
; the TML hardware is installed in an Expansion card, rather than in the Econet slot
; then the Podule number should be given as an argument to InsertTMLInitialisation.
; Note that InsertTMLInitialisation must be invoked before InsertDebugRoutines.
;
; These are the MACROs provided:
; DREG  <reg>,"string",cc,type  Output reg in 8-digit hex, prefixed by
;                               string (or " " if absent), with newline
;                               absent (,cc present). type specifies alternative
;                               output format: Integer, Nibble, Byte, Word, LongWord,
;                               ByteLine, LongLine, Char
; BREG  <reg>[,"string"[,cc]]   As DREG, but 2-digit hex.
; DLINE ["string"[,cc[,inv]]]   As DREG/BREG without the register. inv<>""
;                               indicates should be inverse video (Tube).
; DSTRING <reg>[,"string"[,cc]] As DREG, but output as a string. NULL
;                               pointer catered for.

                GBLL    Host_Debug
Host_Debug      SETL    False

                GBLL    HostDebugViaTML
HostDebugViaTML SETL    False

                GBLL    Debug_MaybeIRQ
Debug_MaybeIRQ  SETL    False

                GBLS    Debug_File
Debug_File      SETS    ""

                GBLL    Debug_Module
Debug_Module    SETL    False

                GBLL    Debug_DADebug
Debug_DADebug   SETL    False

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Routines for starting and ending debug routines
; Ensure enough stashed so that flags, r0 and lr may be used by debugging routines
; without worry. r1 is set to $reg

        MACRO
$label  DStartDRoutine  $reg
$label  Push    "r0,r1,r2,lr"
  [ :LNOT: ("$reg" = "")
    [ $reg = r13
        ADD     r1, r13, #16
    |
      [ :LNOT: ($reg = r1)
        MOV     r1, $reg
      ]
    ]
  ]
        mymrs  ,r2, CPSR
  [ Debug_MaybeIRQ
        TST     r2, #2_11100
        ORREQ   lr, r2, #SVC_mode   ;   26-bit systems change to SVC_26
        BICNE   lr, r2, #M32_bits   ; \ 32-bit systems change to SVC_32
        ORRNE   lr, lr, #SVC32_mode ; /
        mymsr  ,CPSR_c, lr
        Push    "lr"
  ]
        MEND

        MACRO
$label  DEndDRoutine
$label
  [ Debug_MaybeIRQ
        Pull    "lr"
  ]
        mymsr  ,CPSR_cxsf, r2
        Pull    "r0,r1,r2,lr"
        MEND

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Output a single character
;
; Trashes r0, flags and SVC_lr

; eg. DWriteI F
;     DWriteI " "

        MACRO
$label  DWriteI $char,$cc
  [ Host_Debug :LOR: Debug_DADebug
$label  MOV$cc  r0, #"$char"
    [ HostDebugViaTML :LOR: Debug_DADebug
        BL$cc   TML_WriteC
    |
        SWI$cc  XHostFS_WriteC
    ]
  |
    [ "$Debug_File"<>""
$label  MOV$cc  r0, #"$char"
        BL$cc   File_WriteC
    |
      [ Debug_Module
$label  MOV$cc  r0, #"$char"
        SWI$cc  XDebugIt_WriteC
      |
$label  SWI$cc  XOS_WriteI+"$char"
      ]
    ]
  ]
        MEND

        MACRO
$label  DWriteC $cc
  [ Host_Debug :LOR: Debug_DADebug
    [ HostDebugViaTML :LOR: Debug_DADebug
$label  BL$cc   TML_WriteC
    |
$label  SWI$cc  XHostFS_WriteC
    ]
  |
    [ "$Debug_File"<>""
$label  BL$cc   File_WriteC
    |
      [ Debug_Module
$label  SWI$cc  XDebugIt_WriteC
      |
$label  SWI$cc  XOS_WriteC
      ]
    ]
  ]
        MEND

        MACRO
$label  DNewLine $cc
  [ Host_Debug :LOR: Debug_Module :LOR: Debug_DADebug
$label  BL$cc   Tutu_NewLine
  |
    [ "$Debug_File"<>""
$label  BL$cc   File_NewLine
    |
$label  SWI$cc  XOS_NewLine
    ]
  ]
        MEND

        MACRO
$label  DWrite0 $cc
  [ Host_Debug :LOR: "$Debug_File"<>"" :LOR: Debug_Module :LOR: Debug_DADebug
$label  BL$cc   Tutu_Write0
  |
$label  SWI$cc  XOS_Write0
  ]
        MEND

        MACRO
$label  DWriteN $cc
  [ Host_Debug :LOR: "$Debug_File"<>"" :LOR: Debug_Module :LOR: Debug_DADebug
$label  BL$cc   Tutu_WriteN
  |
$label  SWI$cc  XOS_WriteN
  ]
        MEND


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Output a literal string. Outputs " " if no string specified.
;
; Trashes flags and SVC_lr

; eg. DWriteS "Hello world"

        MACRO
$label  DWriteS $string
  [ "$string" = ""
$label  DWriteI " "
  |
    [ Host_Debug :LOR: "$Debug_File"<>"" :LOR: Debug_Module :LOR: Debug_DADebug
$label  BL      Tutu_WriteS
    |
$label  SWI     XOS_WriteS
    ]
        DCB     "$string",0
        ALIGN
  ]
        MEND



; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Output a register with optional string, preserving all state in all modes

; eg. DREG r0,"register zero is "
;     DREG r1,"r1 is ",cc
;     DREG r2,,,Integer
;     DREG r0,,cc
;     DREG r0,,,Word
;     DREG r1,"Thing is ",,Byte
;     DREG r7,"Character received is ",,Char

        MACRO
$label  DREG    $reg, $string, $cc, $routine
$label  DStartDRoutine $reg
        DWriteS "$string"

  [ "$cc" = ""
    [ "$routine" <> ""
        BL      Tutu_$routine
        DNewLine
    |
        BL      Tutu_LongLine
    ]
  |
    [ "$cc" <> "cc"
        ! 1,"Error in DREG with 'cc': '$cc' used instead"
        MEXIT
    ]
    [ "$routine" <> ""
        BL      Tutu_$routine
    |
        BL      Tutu_LongWord
    ]
  ]
        DEndDRoutine
        MEND


        MACRO
$label  BREG    $reg, $string, $cc
$label  DStartDRoutine $reg
        DWriteS "$string"

  [ "$cc" = ""
        BL      Tutu_ByteLine
  |
    [ "$cc" <> "cc"
        ! 1,"Error in BREG with 'cc': '$cc' used instead"
        MEXIT
    ]
        BL      Tutu_Byte
  ]

        DEndDRoutine
        MEND

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Output a string, preserving all state in all modes

        MACRO
$label  DLINE   $string, $cc, $inverse
$label  DStartDRoutine

  [ "$string" <> ""
    [ "$inverse" <> ""
        BL      Tutu_DoInverse
    ]
        DWriteS "$string"
    [ "$cc" = ""
      [ "$inverse" = ""
        DNewLine
      ]
    |
      [ "$cc" <> "cc"
        ! 1,"Error in DLINE with 'cc': '$cc' used instead"
        MEXIT
      ]
    ]
  |
    [ "$cc" = ""
        DNewLine
    ]
  ]
  [ "$inverse" <> ""
        BL      Tutu_DoInverse
    [ "$cc" = ""
        DNewLine
    |
      [ "$cc" <> "cc"
        ! 1,"Error in DLINE with 'cc': '$cc' used instead"
        MEXIT
      ]
    ]
  ]
        DEndDRoutine
        MEND


        MACRO
$label  DSTRING $reg, $string, $cc
$label  DStartDRoutine $reg
        DWriteS "$string.'"
        BL      Tutu_PrintString
        DWriteI "'"
  [ "$cc" = ""
        DNewLine
  |
    [ "$cc" <> "cc"
        ! 1,"Error in DSTRING with 'cc': '$cc' used instead"
        MEXIT
    ]
  ]
        DEndDRoutine
        MEND

        GBLS     DProc_RegList

        MACRO
$label  DEntryS $reglist
        ALIGN
DProc_RegList SETS "$reglist"
 [ "$label" <> ""
$label  ROUT
 ]
 [ "$DProc_RegList" = ""
        STR     lr, [sp, #-4]!
 |
        Push    "$DProc_RegList, lr"
 ]
 [ :LNOT:No32bitCode
        MRS     lr, CPSR
        STR     lr, [sp, #-4]!
 ]
        MEND

        MACRO
$label  DExitS  $cond,$fields
$label
 [ :LNOT:No32bitCode
  [ "$cond" <> "AL" :LAND: "$cond" <> ""
        ; branch over on opposite condition
        DCD     &1A000000 :EOR: Cond_$cond + ((%FT01 - (. + 8))/4)
  ]
        LDR     lr, [sp], #4
 [ "$fields"=""
        MSR     CPSR_f, lr
 |
        MSR     CPSR_$fields, lr
 ]
 [ "$DProc_RegList" = ""
        LDR     pc, [sp], #4
 |
        Pull    "$DProc_RegList, pc"
 ]
01
 |
 ; 26-bit version
 [ "$DProc_RegList" = ""
        Pull    pc,$cond,^
 |
        Pull    "$DProc_RegList, pc",$cond,^
 ]
 ]
        MEND

        MACRO
        InsertDebugRoutines
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Tutu's infamous debugging routines

Tutu_LongLine
        DEntryS
        BL      Tutu_LongWord
        DNewLine
        DExitS  , cxsf

Tutu_ByteLine
        DEntryS
        BL      Tutu_Byte
        DNewLine
        DExitS  , cxsf

Tutu_LongWord
        DEntryS "r1"
        MOV     r1, r1, ROR #16
        BL      Tutu_Word
        MOV     r1, r1, ROR #32-16
        BL      Tutu_Word
        DExitS  , cxsf

Tutu_Word
        DEntryS "r1"
        MOV     r1, r1, ROR #8
        BL      Tutu_Byte
        MOV     r1, r1, ROR #32-8
        BL      Tutu_Byte
        DExitS  , cxsf

Tutu_Byte
        DEntryS "r1"
        MOV     r1, r1, ROR #4
        BL      Tutu_Nibble
        MOV     r1, r1, ROR #32-4
        BL      Tutu_Nibble
        DExitS  , cxsf

Tutu_Char
        DEntryS "r1"
        DWriteI "'"
        MOV     r0, r1
        DWriteC
        DWriteI "'"
        DExitS  , cxsf

Tutu_Nibble
        DEntryS
        AND     r0, r1, #15
        CMP     r0, #10
        ADDCC   r0, r0, #"0"
        ADDCS   r0, r0, #"A"-10
        DWriteC
        DExitS  , cxsf

Tutu_Integer
        DEntryS "r0-r2"
        SUB     r13, r13, #16
        MOV     r0, r1
        MOV     r1, r13
        MOV     r2, #16
        SWI     XOS_ConvertInteger4
        DWrite0 VC
        ADD     r13, r13, #16
        DExitS  , cxsf


Tutu_DoInverse
        DEntryS "r0, r1"
        ADR     r0, Tutu_InverseString
        MOV     r1, #?Tutu_InverseString
        DWriteN
        DExitS  , cxsf

  [ Host_Debug :LOR: "$Debug_File"<>"" :LOR: Debug_Module :LOR: Debug_DADebug
        InsertHostCompatibleDebugRoutines
  ]

Tutu_InverseString      DCB     23,17,5,0,0,0,0,0,0,0
Tutu_BadString          DCB     "--- Invalid Address ---", 0
Tutu_NullString         DCB     "<Null>", 0
                        ALIGN

Tutu_PrintString
        DEntryS "r0, r1"

        CMP     r1, #0
        ADREQ   r1, Tutu_NullString

10      LDRB    r0, [r1], #1
        CMP     r0, #32
        DExitS  CC, cxsf
        DWriteC
        BVC     %BT10
        DExitS  , cxsf

20      LDRB    r0, [r1], #1
        CMP     r0, #32
        BCC     %FT30
        DWriteC
        BVC     %BT20
        DExitS  , cxsf

30
        DWriteI " "
        DWriteI "(", VC
        LDRVC   r1, [sp, #4]
        BLVC    Tutu_LongWord
        DWriteI ")", VC
        DExitS  , cxsf
        MEND

        MACRO
        InsertHostCompatibleDebugRoutines

Tutu_WriteS ROUT
        Push    "r1,r2"
        mymrs  ,r2, CPSR
        TST     r2, #2_11100         ; if on a 26-bit system
        BICEQ   lr, lr, #ARM_CC_Mask ; knock out PSR bits
        MOV     r1, lr
        B       %FT20
10
        DWriteC
20
        LDRB    r0, [r1], #1
        TEQ     r0, #0
        BNE     %BT10

        ADD     lr, r1, #3
        BIC     lr, lr, #3
        mymsr  ,CPSR_cxsf, r2
        Pull    "r1,r2"
        MOV     pc, lr

  [ "$Debug_File"=""
Tutu_NewLine ROUT
        DEntryS
        MOV     r0, #13
        DWriteC
        MOV     r0, #10
        DWriteC
        DExitS  , cxsf
  ]

Tutu_Write0 ROUT
        DEntryS "r1"
        MOV     r1, r0
        B       %FT20
10
        DWriteC
20
        LDRB    r0, [r1], #1
        TEQ     r0, #0
        BNE     %BT10
        DExitS  , cxsf

Tutu_WriteN ROUT
        DEntryS "r1,r2"
        ADD     r1, r0, r1
        MOV     r2, r0
        B       %FT20
10
        LDRB    r0, [r2], #1
        DWriteC
20
        CMP     r2, r1
        BLO     %BT10
        DExitS  , cxsf

    [ HostDebugViaTML
TML_WriteC      ROUT
        DEntryS "r11"
        LDR     r11, =TML_BaseAddr
10
        LDRB    r14, TML_TxStatus
        TST     r14, #1
        BEQ     %BT10
        STRB    r0, TML_TxData
        DExitS  , cxsf

        LTORG
    ]

    [ "$Debug_File"<>""
File_WriteC
        DEntryS "r1"
        LDR     r1, Debug_FileHandle
        CMP     r1, #0
        BLEQ    Debug_OpenFile
        SWIVC   XOS_BPut
        DExitS  , cxsf

File_NewLine
        DEntryS "r1"
        MOV     r0, #10
        DWriteC
        MOV     r0, #0                  ; Close file after each new line.
        LDR     r1, Debug_FileHandle
        SWI     XOS_Find
        STR     r0, Debug_FileHandle
        DExitS  , cxsf

Debug_OpenFile
        DEntryS "r0,r2"
        MOV     r0, #&CF                ; Open existing file.
        ADR     r1, Debug_FileName
        SWI     XOS_Find
        MOVVS   r0, #&8F                ; If it doesn't exist then create it.
        SWIVS   XOS_Find
        MOVVC   r1, r0                  ; Read it's extent.
        MOVVC   r0, #2
        SWIVC   XOS_Args
        MOVVC   r0, #1                  ; Set file pointer so we append to it.
        SWIVC   XOS_Args
        STRVC   r1, Debug_FileHandle    ; File opened successfully.
        DExitS  , cxsf                  ; Return r1=file handle.

Debug_FileHandle        DCD     0
Debug_FileName          DCB     "$Debug_File",0
                        ALIGN
    ]

    [ Debug_DADebug
TML_WriteC      ROUT
        DEntryS "r0,r1"
        SWI     &731C0 ; XDADebug_GetWriteCAddress
        MOVVS   r0,#0
        MOVS    r1,r0
        LDRNE   r0,[r13,#4]
        MOVNE   lr,pc
        MOVNE   pc,r1
        DExitS  , cxsf
    ]
        MEND

; End of the debug routines
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        MACRO
$label  InsertTMLInitialisation $slot
  [  "$slot" = "0"                      ; Podule0
TML_BaseAddr    * &3340000+&3000
  |
    [  "$slot" = "1"                    ; Podule1
TML_BaseAddr    * &3344000+&3000
    |
      [  "$slot" = "2"                  ; Podule2
TML_BaseAddr    * &3348000+&3000
      |
        [  "$slot" = "3"                ; Podule3
TML_BaseAddr    * &334C000+&3000
        |                               ; Econet
TML_BaseAddr    * &33A0000
        ]
      ]
    ]
  ]
                ^       0, r11
TML_RxData      #       4
TML_TxData      #       4
TML_Control     #       4
TML_TxStatus    #       4

Host_Debug      SETL    True
HostDebugViaTML SETL    True

$label  Push    "r1-r2, r11, lr"
        mymrs  ,r2, CPSR
  [ Debug_MaybeIRQ
        TST     r2, #2_11100
        ORREQ   lr, r2, #SVC_mode   ;   26-bit systems change to SVC_26
        BICNE   lr, r2, #M32_bits   ; \ 32-bit systems change to SVC_32
        ORRNE   lr, lr, #SVC32_mode ; /
        mymsr  ,CPSR_c, lr
        Push    "lr"
  ]
        LDR     r11, =TML_BaseAddr
        MOV     r1, #2
        STRB    r1, TML_Control         ; Reset
        MOV     r14, #&FF00             ; Delay
10
        STRB    r1, TML_RxData
        SUBS    r14, r14, #1
        BGT     %BT10
        LDRB    r1, TML_Control         ; Check booted hardware OK
        TEQ     r1, #&FF
        BNE     %FT20
        ADR     r0, BadHardWare
        SETV
        B       %20

BadHardWare
        DCD     0
        DCB     "TML hardware not found",0
        ALIGN

        LTORG

20
  [ Debug_MaybeIRQ
        Pull    "lr"
  ]
        mymsr  ,CPSR_cxsf, r2
        Pull    "r1-r2, r11, lr"
        MEND

        OPT     OldOpt
        END
