;
;                       T h i n g T r a p
;
;
;   Library support routine for routines that call the THING system
;   trap.   This routine allows for the fact that on many older systems
;   (such as original QDOS) the TRAP#1 calls to the THING system are
;   not available, and that a call has to be made via a THING vector
;   instead.   This is handled completely transparently.
;
;   On entry, it is assumed that d0-d3 and a0-a3 are
;   set up as though the trap was to be called.
;
; AMENDMENT HISTORY
; ~~~~~~~~~~~~~~~~~
;   24 Jan 94   DJW   - First version.   Based on information from the
;                       QDOS Reference Manual from Jochen Merz, and checked
;                       against methods used in code of THING librarys from
;                       Lester Warehams and Johnathan Hudson.
;
;   13 Apr 94   DJW   - The test for whether to use the vector or trap was
;                       flawed, and the code to go back to user mode was
;                       not correct on all paths.
;
;   14 Sep 94   DJW   - Fixed bug in calling via vector - this stopped
;                       all calls from working on QDOS based systems.
;                     - Removed underscores from start of name.  This makes
;                       it impossible to even call this routine directly
;                       from C by accident.
;                     - Put copy of this code into CRT_X startup module for
;                       use when RLLs are in use.

    .text
    .even
    .globl ThingTrap

SMS.INFO    equ     $00         ; trap to get system informatopn
SMS.NTHG    equ     $2b         ; THING trap to get next in list

SYS_THGL    equ     $b8         ; THING list offset in system variables

TH_THING    equ     $10         ; THING base relative to linked list entry
THH_TYPE    equ     $04         ; THING type offset
THH_ENTR    equ     $08         ; THING entry routine pointer

ERR.ITNF    equ     -7          ; Error: Not Found
ERR.NIMP    equ     -19         ; Error: Mot Implemented

thing_vector:
    dc.l    0       ; we use this to decide what to do
                    ; 0    = do not know (initial state)
                    ; -ve = TRAP interface available
                    ; +ve = Address of UTILITY THING

ThingTrap:
    move.l  a4,-(sp)                ; save register
    lea     thing_vector(pc),a4     ; address we save vector at
    tst.l   (a4)                    ; see if we know if traps supported
    bpl     check_trap              ; ... NO (or possibly unknown)

;   This is the simplest case - the operating system DOES support
;   the TRAP #1 interface to the THING system.

use_trap:
    trap    #1

rts_cond:
    move.l  (sp)+,a4
    tst.l   d0                      ; ensure condition code set
    rts

;   If we get here, either the trap interface is not available,
;   or possible we do not yet know the answer.

check_trap:
    beq     test_trap               ; branch if unknown option.

;   It appears that we do not have the trap interface available.
;   We are now calling the THING system via the UTILITY THING.  As a
;   sanity check, we check that our vector still points to it.

use_vector:
    move.l  (a4),a4                 ; get saved thing vector
    cmp.l   #-1,THH_TYPE(a4)        ; check it is still THING we want
    bne     missing                 ; ... apparently not ?
    move.l  THH_ENTR(a4),a4         ; OK, so entry vector
    jsr     (a4)                    ; ... and take it
    bra     rts_cond                ; ... then normal exit setting condition


;   Apparently we do not yet know - time to find out
;   We do this be trying the TRAP #1 interface and seeing
;   if we get a "Not Implemented" error.  If not, then the
;   calls must be available.

test_trap:
    movem.l d0-d3/a0-a3,-(sp)       ; save registers
    moveq   #SMS.NTHG,d0            ; trap that always works if traps available
    sub.l   a0,a0                   ; ... get first THING in list
    trap    #1                      ; try using trap
    tst.l   d0                      ; did it work?
    movem.l (sp)+,d0-d3/a0-a3       ; first restore saved registers
    bne     find_vector             ; ... NO, then we need to find the vector

;   We have found that the trap interface is available.
;   Therefore set ourselves up to use this immediately on
;   future calls.

    lea     thing_vector(pc),a4     ; set to use trap in future
    subq.l  #1,(a4)                 ; set vector negative to say trap OK
    bra     use_trap                ; go and use trap

;   We have now determined that we need to use the UTILITY THING to call
;   the THING system.  The code to find this is based on that in the
;   QDOS Reference manual.

find_vector:
    movem.l d0-d3/a0,-(sp)          ; save registers we might corrupt
    moveq   #SMS.INFO,d0        
    trap    #1

    trap    #0                      ; enter Supervisor mode
    move.l  SYS_THGL(a0),d1         ; this is THING list
    beq     not_found               ; ... empty, very bad
    move.l  d1,a0
loop:
    move.l  (a0),d1                 ; get next list entry
    beq     found                   ; end of list.  Should be THING we want
    move.l  d1,a0                   ; follow link
    bra     loop                    ; ... and try again

;   The vector has been found.  To avoid the
;   overhead of repeating this check every time,
;   we will save the address.

found:
    move.l  TH_THING(a0),a0         ; get THING base
    lea     thing_vector(pc),a4     ; get address to save vector at
    move.l  a0,(a4)                 ; ... and save result
    andi.w  #$dfff,sr               ; go back to user mode
    movem.l (sp)+,d0-d3/a0          ; restore registers we have corrupted.
    bra     use_vector

;   It appears that there is no support for the
;   THING system loaded.  Return an error code.

not_found:
    andi.w  #$dfff,sr               ; go back to user mode
    movem.l (sp)+,d0-d3/a0          ; restore registers we have corrupted.
missing:
    moveq   #ERR.NIMP,d0            ; set not implemented error
    lea     thing_vector(pc),a4     ; get vector address
    clr.l   (a4)                    ; ... and ensure it is cleared
    bra     rts_cond                ; ... and exit

