Trailing-Edge
-
PDP-10 Archives
-
bb-h138f-bm
-
7-sources/fakdyn.mac
There are 3 other files named fakdyn.mac in the archive. Click here to see a list.
TITLE FAKDYN -- User-mode implementation of dynamic libraries
;
; COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1984, 1986.
; ALL RIGHTS RESERVED.
;
; THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND
; COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH
; THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR
; ANY OTHER COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE
; AVAILABLE TO ANY OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE
; SOFTWARE IS HEREBY TRANSFERRED.
;
; THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT
; NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL
; EQUIPMENT CORPORATION.
;
; DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF
; ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
;
SEARCH DDBSYM, MONSYM, MACSYM
SEARCH DYNSYM
SALL
SUBTTL Edit History
; Version 1.0
;.EDIT 4 Fix restart master-init problem for RTL
; DDB,9-MAY-84,SPR:NONE
;.EDIT 10 Fix code to handle new PDV format
; DDB,12-JUN-84,SPR:NONE
; Version 1.1
;.EDIT 50 Formally go to version 1.1, update copyright, insert V1.1
; development changes (formally V2)
; DDB,15-Jan-85,SPR:NONE
;.EDIT 51 Add GLOBbing in build procedure, make DY$CBK work from JCK.
; DDB,30-Apr-85,SPR:NONE
EXTERNAL RTLSER, RTLERS, RTLJER, RTLCER, RTLERR
EXTERNAL RTLPDV, SG.NAS
REGDEF CX, LLB ;EFIW LDLBLK is overwhelmingly important
SUBTTL DY$DYN -- Routine called on first reference through LTVEC
; On call, PC must be in non-zero section, stack pointer must be global,
; and there must be at least <foo> words available on the stack.
SEGMENT CODE
DY$DYN::
;Save registers 0-16 in format DY$DYN expects to restore from
ADJSP P, 17 ;Allocate save block on stack
MOVEM 2, -14(P) ;Put AC2 in save block
XMOVEI 2, 1(P) ;Adr of block end + 1
DMOVEM 0, -16(P) ;Put AC0 and AC1 in save block
DMOVE 0, [EXP -14, 17] ;Set up 0,1 for reverxe XBLT (2 already set)
EXTEND 0, [XBLT] ;Save 3-16 in save block
FAKSAV==17
; Local temp storage:
FAKTMP==23
LCL1==-FAKTMP+1
LCL2==-FAKTMP+2
LCL6==-FAKTMP+4 ;6-word local storage block
LCL12==-FAKTMP+12 ;12-word local storage block
ADJSP P, FAKTMP ;Allocate local work space
; Names for stuff left on stack while calling us:
CLDLBK==-<FAKSAV+FAKTMP> ;Address of local dynamic library block
UCIP1==CLDLBK-1 ;Address of user call instruction + 1
MOVE LLB, CLDLBK(P) ;Address of LDLBLK
; Check LDLBLK format version number to see if we speak that language
PUSH P, [VM%MIG] ;Accept if equal or minor greater
PUSH P, [$DYFVN] ;"Us"
PUSH P, .LDFVN(LLB) ;"Him" is what LDLBLK is
CALL VERMAT
SKIPA ;Fails
JRST FAK002 ;Succeeds
; Error -- local dynamic library block is a version that this FAKDYN
; doesn't understand.
PUSH P, .LDFVN(LLB)
PUSH P, [1]
PUSH P, [DY$DYV]
CALL RTLERS ;Make signal block, return address in T1
JRST FAKLDU
FAK002:
; If library has already been initialized, then we got here as a result of
; a bad offset
MOVX T1, LD%INI
TDNE T1, .LDFLG(LLB) ;Skip if library not initialized
JRST BADOFF
; Look for existing providers of the requested service class
XMOVEI T2, LCL6(P) ;Use LCL6 as PDV argument block
MOVX T0, 6
MOVEM T0, .POCT1(T2)
MOVX T0, .FHSLF
MOVEM T0, .POPHD(T2)
MOVX T0, 12
MOVEM T0, .POCT2(T2)
XMOVEI T0, LCL12(P) ;Use LCL12 as PDV data block
MOVEM T0, .PODAT(T2)
SETZM .POADR(T2)
MOVX T1, <777,,-1> ;Highest possible memory address
MOVEM T1, .POADE(T2)
;.poloc isn't documented to respect .poadr .poade, but it does
MOVX T1, .POLOC ;Function code
GETBP T3, .LDCLS(LLB), T4 ;Pointer to class name string
PDVOP%
ERCAL FAKJSE
; Note: no check yet for whether more providers present than
; would fit into data block. This may not be important?
; We now have a list of providers of the service class requested in LCL12.
; We must step through this list and see if any of them are available to
; satisfy the current request.
FAK003: HRRZ T0, .POCT2(T2) ;Count of words returned
MOVX T1, 0 ;Counter/index for walking list
XMOVEI T2, LCL12(P) ;Address of data block
TXO T2, <<T1>B5> ;!!! Make indexed (global format index)
; Examine the next provider
FAKNXP: CAML T1, T0 ;Skip if there is such a provider
JRST FAKNPR ;No more providers to consider
; For each provider
MOVE T3, @T2 ;Get provider PDVA
;T2 holds an EFIW indexed by T1
XMOVEI T3, @.PVSTR(T3) ;Get DLBLK adr from provider PDV
; Magically determine if this one is available to provide the service
MOVE T5, .DYFLG(T3) ;Get library flag word from DLBLK
TXNE T5, DY%BSY ;Skip if not busy
JRST FAK001
; Provider can provide the service. Prepare registers as required.
MOVE T1, T3 ;FAKFND expects DLBLK address in T1
JRST FAKFND
; Loop through additional providers
FAK001: AOJA T1, FAKNXP ;Go try next one
; If no provider found, make one
FAKNPR:
; If this process is execute-only, ignore job-wide logical names on GTJFN%
; to give the process at least some hope of maintaining security (with help
; from the system manager, of course).
; [RFSTS WORKAROUND] ; XMOVEI T2, LCL12(P) ;Address of status block
XMOVEI T2, RFSHCK ;[RFSTS WORKAROUND] Address of status block
MOVX T1, .RFSFL+1 ;length to at least include status flags
MOVEM T1, .RFCNT(T2)
MOVX T1, <RF%LNG+.FHSLF>
RFSTS%
ERCAL FAKJSE
MOVE T0, .RFSFL(T2) ;Recover status flags returned
MOVX T1, GJ%OLD!GJ%SHT
TXNE T0, RF%EXO ;Skip if process is not execute-only
TXO T1, GJ%PHY ;Ignore job logical names if execute-only
GETBP T2, .LDSPC(LLB), T4
GTJFN%
ERCAL FAKJSE
; JFN is now sitting in T1. Don't break it!
; Address of LDLBLK is sitting in LLB.
; Get the file into the process
; First, save entry vector
MOVEM T1, LCL1(P) ;Save JFN temporarily
MOVX T1, .FHSLF
XGVEC%
ERCAL FAKJSE
DMOVEM T2, LCL2(P) ;Use as temp hold area
MOVE T1, LCL1(P) ;Restore JFN
; Magically allocate a section. Leave section number in T3.
CALL SECTIO ;Allocate a section, leaving result in T3
JRST [
PUSH P, [0]
PUSH P, [DY$SEC]
CALL RTLERS
JRST FAKLDU
]
; Get the file into the chosen section
; JFN still sitting in T1; Section number in T3
XMOVEI T2, LCL12(P) ;Use LCL12 block for data block
MOVEM T3, .GBASE(T2)
MOVX T0, GT%BAS
MOVEM T0, .GFLAG(T2)
TXZ T1, <^-GT%JFN> ;Clear out all non-JFN bits
TXO T1, GT%NOV!GT%ARG ;Set flags
HRLI T1, .FHSLF
GET%
ERCAL FAKJSE
; Restore entry vector
MOVX T1, .FHSLF
DMOVE T2, LCL2(P) ;Get length and address from temp hold
XSVEC%
ERCAL FAKJSE
; Do a PDVOP% over just the new section to find the dynamic library PDV,
; and thus the DLBLK. We do this rather than looping back to the initial
; search because that search, over the full address space, might find a
; previously-loaded copy of this service provider which has become free
; due to asynchronous activity since we last looked. If that were to happen,
; the copy we just loaded would not get initialized, which could cause
; disaster if a later call for service happened to choose it to call.
FAK004: XMOVEI T2, LCL6(P) ;Use LCL6 for PDV arg block
;again. This was its last use.
MOVX T0, 12
MOVEM .POCT2(T2)
XMOVEI T1, LCL12(P) ;Use LCL12 for PDV data block
MOVEM T1, .PODAT(T2)
MOVE T1, .GBASE(T1) ;Still contains GET% args, including section
HRLZM T1, .POADR(T2) ;section,,0 -- start of range to search
HRLOM T1, .POADE(T2) ;section,,-1 -- end of range to search
MOVX T1, .POLOC ;Function -- locate PDV by name
; .poloc isn't documented to respect .poadr .poade, but it does
GETBP T3, .LDCLS(LLB), T4 ;Pointer to class name string
;(By searching for class name instead of for
;the Dynamic Library PDV, we are performing
;a check to make sure the file specified in
;the LDLBLK actually provides the service
;specified in the LDLBLK.)
PDVOP%
ERCAL FAKJSE
HRRZ T1, .POCT2(T2) ;Get number of providers found (better be 1!)
CAIE T1, 1 ;Should be exactly one provider PDV in a single
;dynamic library
JRST [
PUSH P, [0]
PUSH P, [DY$CLS]
CALL RTLERS
JRST FAKLDU
]
MOVE T1, LCL12(P) ;Get the single PDVA
XMOVEI T1, @.PVSTR(T1) ;Get DLBLK from PDV
; Check format version number of DLBLK in library found.
PUSH P, [VM%MIG] ;Accept if equal or minor greater
PUSH P, [$DYFVN] ;"Us"
PUSH P, .DYFVN(T1) ;"Him" is DLBLK format version
CALL VERMAT
SKIPA ;Fails
JRST FAK005 ;Succeeds
; Error -- Master dynamic library block is a version that this FAKDYN
; doesn't understand.
PUSH P, .DYFVN(T1)
PUSH P, [1]
PUSH P, [DY$LBV]
CALL RTLERS
JRST FAKLDU
DY$BS:: ;DYNBOO JRSTs to here when bootstrapping
;the RTL
; This entry point must be offset .RTBOO in CUSTOMER vector.
; Required state:
; LLB/ EFIW LDLBLK
; T1/ EFIW DLBLK
; P2/ Non-zero if library was just mapped in by DYNBOO
; STACK:
; UCIP1,CLDLBK,LCL12,LCL6,LCL2,LCL1,FAKSAV
JUMPE P2, FAKFND ;Don't init unless just mapped in
FAK005:
; Call master init of newly mapped library
XMOVEI T2, @.DYDTV(T1) ;EFIW master transfer vector
PUSH P, T1 ;Preserve regs we count on
PUSH P, LLB
CALL @.DEMIN(T2) ;Call master init through master transfer vector
MOVE T2, T1 ;Preserve error indication
POP P, LLB
POP P, T1
JUMPE T2, FAKFND ;Found, if no master init error
PUSH P, T2 ;Error code
PUSH P, [1]
PUSH P, [DY$MIE]
CALL RTLERS
JRST FAKLDU
FAKFND:
; Come here if we have found a provider.
; DLBLK address in T1 (not available elsewhere!!!!)
; LDLBLK address in LLB
; Verify that version of library found is acceptable to LDLBLK we are looking
; on behalf of.
;This code depends on version match rule being in same bits in .LDFLG
;and .DYFLG. This requirement is documented in DYNSYM.MAC
MOVE T0, .LDFLG(LLB) ;Get local match rule
TXNN T0, LD%VMA ;Skip if local match rule overrides master
MOVE T0, .DYFLG(T1) ;Get master match rule
LDB T0, [POINTR T0,LD%VER] ;Extract version match field
;End of dependency
PUSH P, T0 ;Push version match rule
PUSH P, .LDVER(LLB) ;Push local version ("us")
PUSH P, .DYVER(T1) ;Push actual version ("him")
CALL VERMAT
JRST FAKVER ;Didn't match
JRST FAKVOK ;Did match
FAKVER:
; Print a nifty error message describing why the versions don't match
PUSH P, .DYVER(T1)
PUSH P, .LDVER(LLB)
PUSH P, [2]
PUSH P, [DY$VER]
CALL RTLERS
JRST FAKLDU
FAKVOK: ; Versions meet the matching criteria, we can use this one
; Wave hands over LDLBLK and dependents
XMOVEI T0, @.DYCTV(T1) ;EFIW Address of CTVEC
XMOVEI T2, @.LDCTV(LLB) ;EFIW Address of LCTVEC
CALL WAVHND ;Wave hands over one transfer vector
JRST [
PUSH P, [0]
PUSH P, [DY$CTV]
CALL RTLERS
JRST FAKLDU
]
XMOVEI T0, @.DYDTV(T1) ;EFIW Address of DTVEC
XMOVEI T2, @.LDDTV(LLB) ;EFIW Address of LDTVEC
CALL WAVHND ;Wave hands over the other transfer vector
JRST [
PUSH P, [0]
PUSH P, [DY$DTV]
CALL RTLERS
JRST FAKLDU
]
XMOVEI T0, @.DYDGV(T1) ;EFIW Address of DGVEC
XMOVEI T2, @.LDDGV(LLB) ;EFIW Address of LDGVEC
CALL WAVHND ;Wave hands over the other transfer vector
JRST [
PUSH P, [0]
PUSH P, [DY$DGV]
CALL RTLERS
JRST FAKLDU
]
XMOVEI T0, @.DYCGV(T1) ;EFIW Address of CGVEC
XMOVEI T2, @.LDCGV(LLB) ;EFIW Address of LCGVEC
CALL WAVHND ;Wave hands over the other transfer vector
JRST [
PUSH P, [0]
PUSH P, [DY$CGV]
CALL RTLERS
JRST FAKLDU
]
; Mark library as loaded and initialized
MOVX T0, LD%INI
ORM T0, .LDFLG(LLB) ;Set bit in flag word in LDLBLK
; At this point, we've found or loaded the library, and we've waved
; our hands over the local transfer vectors. It's time to clean up
; the mess we made on the stack and re-execute the user call instruction
; that got us here in the first place.
SOS UCIP1(P) ;Decrement user return to point back
;to call instruction
ADJSP P, -FAKTMP ;Remove temp storage
;Restore registers saved by DYNBOO or FAKDYN
MOVX 0, 14 ;Count for XBLT
XMOVEI 1, -13(P) ;Source address: AC3 in save block
MOVX 2, 3 ;Destination address: AC3
EXTEND 0, [XBLT] ;Restore 3-16
DMOVE 0, -16(P) ;Restore 0,1
MOVE 2, -14(P) ;Restore 2
ADJSP P, -20 ;Remove save block from stack (17)
;Take off the LDLBLK address from the
;extra PUSHJ before the LDLBLK (1)
POPJ P, ;Return to re-execute user call instruction
BADOFF:
; If LTVEC longer than MTVEC and one of those extra locations is called,
; control comes here.
PUSH P, [0]
PUSH P, [DY$BOF]
CALL RTLERS
JRST FAKLDU
SUBTTL VERMAT -- Version matching routine
; This routine compares two TOPS-20 version numbers and decides whether their
; relationship satisfies a specified version matching rule.
; Accepts three arguments on stack:
.VMRUL==-3 ;Version matching rule
;(Use VM%xxx codes from DYNSYM)
.VMUS==-2 ;"Our" version (TOPS-20 version format)
.VMHIM==-1 ;"His" version (TOPS-20 version format)
; Returns +1 if versions don't match, +2 if they do. Removes args from stack.
; Trashes registers T0, T2, T3, T4.
VERMAT:
LDB T2, [POINTR .VMUS(P),VI%MAJ] ;Our major version
LDB T3, [POINTR .VMHIM(P),VI%MAJ] ;His major version
CAMN T3, T2 ;Skip if major versions differ
JRST VMAMEQ ;Major versions are equal
; Compare major versions against major version match rule
SETZ T4,
CAMG T3, T2 ;Skip if he is majorly newer
TXOA T4, VM%MAL ;He is majorly older (and skip)
TXO T4, VM%MAG ;He is majorly newer
TDNE T4, .VMRUL(P) ;Skip if rule not satisfied
JRST VMAVOK ;Rule satisfied; version is ok
JRST VMAVER ;Loses.
; Major version numbers are equal, compare on minor
VMAMEQ:
LDB T2, [POINTR .VMUS(P),VI%MIN] ;Our minor version
LDB T3, [POINTR .VMHIM(P),VI%MIN] ;His minor version
CAMN T3, T2 ;Skip if minor versions differ
JRST VMAVOK ;Perfect match wins
; Compare minor versions against minor version match rule
SETZ T4,
CAMG T3, T2 ;Skip if he is minorly newer
TXOA T4, VM%MIL ;He is minorly older (and skip)
TXO T4, VM%MIG ;He is minorly newer
TDNE T4, .VMRUL(P) ;Skip if rule not satisfied
JRST VMAVOK ;Wins
JRST VMAVER ;Loses
VMAVOK: AOS 0(P) ;Skip return
VMAVER: ;Regular return
; Clean stack and exit
MOVE T2, 0(P) ;Get return address
MOVEM T2, .VMRUL(P) ;Put over first arg
ADJSP P, -3 ;Remove return and last n-1 args
RET ;Return
SUBTTL Error processing routines
; FAKLDU
; JRST to this as part of the process of dying.
; This routine prefixes the signal in T1 with library and user PC information.
; LLB must be set up. The stack must be in normal state for FAKDYN -- the
; usual amount of garbage, nothing extra.
FAKLDU: CALL FAKFLL ;Put the FLL and UPC in front of what's in T1
;JRST FAKSER ;And go make it an error!!!!!
; FAKSER
; JRST to this as part of the process of dying.
; Given the signal we want to send in T1, and a normal stack setup,
; clean up the stack and then call RTLSER
FAKSER: ADJSP P, -FAKTMP ;Get rid of temp stuff
MOVEM T1, -15(P) ;Put our T1 over one in save block
; Restore registers saved by DYNBOO or FAKDYN
MOVX 0, 14 ;Count for XBLT
XMOVEI 1, -13(P) ;Source address: AC3 in save block
MOVX 2, 3 ;Destination address: AC3
EXTEND 0, [XBLT] ;Restore 3-16
DMOVE 0, -16(P) ;Restore 0,1
MOVE 2, -14(P) ;Restore 2
ADJSP P, -20 ;Remove save block from stack (17)
;Take off the LDLBLK address from the
;extra PUSHJ before the LDLBLK (1)
JRST RTLSER
; FAKFLL
; This is a subroutine, call it with a CALL. It takes a signal in T1 and
; prefixes it with library and user PC information. LLB must be set up.
; The stack must be in normal state at the time of the call (obviously there
; will be one extra thing on it when we get here).
FAKFLL: MOVE P7, T1 ;Preserve this signal
MOVE T1, UCIP1-1(P) ;Get address of user call plus 1
;The "-1" is because of the FAKFLL return adr
;sitting on the stack
SOS T1
PUSH P, T1
PUSH P, [1]
PUSH P, [DY$UPC]
CALL RTLERS
MOVE P6, T1
GETBP T1, .LDCLS(LLB), T4
PUSH P, T1
GETBP T1, .LDSPC(LLB), T4
PUSH P, T1
PUSH P, [2]
PUSH P, [DY$FLL]
CALL RTLERS
MOVEM P6, .SGNXT(T1) ;The FLL points to the UPC
MOVEM P7, .SGNXT(P6) ;The UPC points to what the user gave us
RET ;And we're done
; FAKFAT
; JRST here with a signal block address in T1. We will produce the chain
; DY$FLL, DY$UPC, the signal in T1, JSYS error block
; and pass it into RTLSER
FAKFAT: MOVE P1, T1 ;Save given signal block
CALL RTLJER ;Make JSYS error block
MOVEM T1, .SGNXT(P1) ;Make chain given, JSYS
MOVE T1, P1
CALL FAKFLL ;Make chain DY$FLL, DY$UPC, given, JSYS
JRST FAKSER ;Go use it
; FAKJSE
; CALL here with no signal block address anywhere. We produce a chain
; DY$FLL, DY$UPC, DY$EPC, JSYS error
; and pass it into RTLSER
FAKJSE: CALL RTLJER ;Make JSYS error block
MOVEM T1, P1 ;Save address of block produced
; PC at which error occurred is at top of stack from CALL to here
SOS 0(P) ;Point to instruction that failed
PUSH P, [1]
PUSH P, [DY$EPC]
CALL RTLERS
MOVEM P1, .SGNXT(T1) ;EPC points to JSYS
CALL FAKFLL ;Make FLL and UPC pointing to EPC and JSYS
JRST FAKSER
SUBTTL SECTIO -- Allocate a section
; This routine allocates a free section to map a library into.
; This interim version uses Pete Mierswa's "section sniffer". In the future,
; a more complete algorithm including the possibility of user rules for
; controlling section allocation should be employed.
; Call:
; PUSHJ P, SECTIO
; Returns +1 if error (no more sections available!!)
; Returns +2 if no error, with
; T3/ Number of section allocated
SECTIO: PUSH P, T1 ;Need elbow room
PUSH P, T2
MOVEI T3,1 ;Start looking in section 2
NEXTSC: ADDI T3,1 ;Look at the next section
CAIN T3,^D32 ;No free sections on a KL?
JRST SECFAL
MOVE T1, T3 ;Next section to check
HRLI T1,.FHSLF ;For this process
RSMAP% ;Status of this section
ERCAL FAKFAT ;Error?
TXNE T2,PA%PEX ;Does the page exist?
JRST NEXTSC ;Yes, look for a free section
; Section number is in T3
; Success -- skip return
AOS -2(P) ;Two saved registers on stack!
SECFAL:
SECOK: POP P, T2
POP P, T1
SECEX: RET
SUBTTL WAVHND -- Wave hands over one pair of transfer vectors
; This routine takes a master and a local transfer vector, and updates
; the local to point to the routines listed in the master (with EFIW's).
; CALL:
; T0/ EFIW Master transfer vector (in library)
; T2/ EFIW Local transfer vector (in caller)
; LLB/ EFIW LDLBLK (in caller)
; CALL WAVHND
; Returns +1 if error, +2 if ok
; T0, T2, T3, T4, T5 TRASHED
; Rules for overloading:
;
; LTVEC entries not containing their default initial state will
; not be altered. The default initial state is "IFIW LDLBLK-1"
; for the user-mode implementation.
;
; If an MTVEC entry contains 0, the corresponding LTVEC entry is
; not altered. (Section is ignored, check is for n,,0).
;
; If the LTVEC is longer than the MTVEC, excess LTVEC locations
; are not altered.
;
; If the MTVEC is longer than the LTVEC, the additional MTVEC
; entries are ignored.
WAVHND: PUSH P, T1
MOVX T1, 0 ;Offset zero is vector length
TXO T0, <<T1>B5> ;!!! Index by T1
TXO T2, <<T1>B5> ;!!! Index by T1
HRRZ T4, LLB ;LDLBLK
SOS T4 ;LDLBLK-1
TXO T4, IFIW ;IFIW LDLBLK-1 (as found in LTVEC entries)
MOVE T3, @T2 ;Get local transfer vector length
CAMLE T3, @T0 ;Skip if local length less or equal master
MOVE T3, @T0 ;Master is shorter
; T3 now has length of shorter vector
; Leave part of ltvec beyond mtvec unchanged; the invalid entry error will
; be detected in fakdyn if that offset is ever used.
; Wave hands over part of LTVEC within length of MTVEC.
; Need: T0: EFIW MTVEC(T1), T2: EFIW LTVEC(T1)
MOVE T1, T3 ;Start at end of shorter vector
TXO T0, <1B1> ;!!! Make indirect !!!
; Loop through local vector length updating entries
WAVNXT: SOJLE T1, WAVDON ;Vectors may be zero long
MOVEI T3, @T0 ;Get in-section part of address from MTVEC
;Note: this should NOT be an XMOVEI, I only
;want the in-section part of the address!!!
JUMPE T3, WAVNXT ;If zero, skip over this one
XMOVEI T3, @T0 ;Get EFIW routine (note t0 is indirect indexed)
CAMN T4, @T2 ;Skip if this LTVEC entry isn't normal IFIW
MOVEM T3, @T2 ;Store into LTVEC
JRST WAVNXT
WAVDON: AOS -1(P) ;Skip return
WAVERR: ;Normal return
WAVEXI: POP P, T1 ;Restore what we saved
RET
UN$LLB
SUBTTL DY$MIN -- Master initialize all loaded libraries
; Perform master init of all libraries curently mapped in.
; Main programs using dynamic libraries should call this as part of their
; "START" code, before their first call to a dynamic library. This makes
; such programs restartable!!
; Formal Arguments: None
; Value returned: None
; Preserves no registers
MINTMP==26 ;Temp storage space
.PDDAT==-17 ;20 long
.PDARG==-25 ;6 long
DY$MIN::
ADJSP P, MINTMP ;Make temp storage space
; Turn on traps (delayed from master init for section-zero support)
SKIPE SG.NAS ;Skip if traps to be allowed
JRST MIN001 ;Bypass trap initialization
; Turn on the trap system
EXTERNAL %TRPINI
CALL %TRPINI ;Initialize trap system
MIN001:
; Set up PDVOP% arg block
XMOVEI T2, .PDARG(P) ;Adr of PDVOP% arg block
MOVX T0, 6
MOVEM .POCT1(T2) ;Arg block word count
MOVX T0, .FHSLF
MOVEM .POPHD(T2) ;Process handle
XMOVEI T0, .PDDAT(P)
MOVEM T0, .PODAT(T2) ;Adr of PDVOP% data block
; The addresses aren't documented as being used by .POLOC, but
; they are, and wrong too. 0 0 won't be interpreted to mean all memory
SETZM .POADR(T2) ;Start of memory range
MOVX T0, <7777,,-1> ;
MOVEM T0, .POADE(T2) ;End of memory range
; Look for Dynamic Libraries
MINSRC: MOVX T0, 20
MOVEM T0, .POCT2(T2) ;Data block size
MOVX T1, .POGET ;Find all PDVA's
; T2 still set to arg block address
PDVOP%
ERCAL FAKFAT
; Set up for loop through .PDDAT
HRRZ T0, .POCT2(T2) ;Number of entries
XMOVEI T1, .PDDAT(P) ;Index into .PDDAT
; Loop through data block hitting master init for each entry
MINNXT: SOJL T0, MINBFI ;Finished with batch?
MOVE T3, 0(T1) ;Get PDVA
; Decide if this PDV represents a dynamic library
XMOVEI P1, @.PVNAM(T3) ;[10]Address of name string
;[10] ;Has form 0,,adr , must be interpreted
;[10] ; as section-local
;[10] XMOVEI T4, .PVNAM(T3) ;Get section where this came from
;[10] HLL P1, T4 ;Set up as global index
MOVX T4, <POINT 7, 0(P1)> ;Pointer to name string
MOVX T5, <POINT 7, [ASCIZ /DYNLIB$/]> ;Pointer to desired string
MINMAY: ILDB P2, T4 ;Get character of name string
JUMPE P2, MINNBL ;Name shorter than DYNLIB$, can't be equal
ILDB P3, T5 ;Get character of desired string
JUMPE P3, MINDYN ;They matched to end of desired string, it's
;a baby dynamic library!!!
; Note: case folding isn't desired here, name should be in caps!!!
CAMN P2, P3
JRST MINMAY ;So far, so good; keep trying
JRST MINNBL ;Names not equal, not a dynamic library
; This PDV actually represents a dynamic library!!
MINDYN: MOVE P1, T3 ;Preserve PDVA
XMOVEI T3, @.PVSTR(T3) ;Get DLBLK address
XMOVEI T3, @.DYDTV(T3) ;Get MDTVEC address
; Save registers we need around master-init call
PUSH P, T0
PUSH P, T1
PUSH P, T2
CALL @.DEMIN(T3) ;Call master init routine routine
MOVE T3, T1 ;Preserve error code
POP P, T2
POP P, T1
POP P, T0
JUMPE T3, MINNBL ;Proceed if all OK
PUSH P, T3
PUSH P, [1]
PUSH P, [DY$MIE]
CALL RTLERS
MOVE P2, T1 ;Preserve block address
XMOVEI T0, @.PVNAM(P1) ;[10]Get address of name string from PDV block
;[10] HLL T0, P1 ;Interpret as in section with PDB block
PUSH P, T0
PUSH P, [1]
PUSH P, [DY$MIA]
CALL RTLERS
MOVEM P2, .SGNXT(T1)
ADJSP P, -MINTMP ;Clean up our stack
JRST RTLSER ;Signal it internal-style
; Try the next entry in the block.
MINNBL: AOJA T1, MINNXT ;Go try next .PDDAT entry
; Done processing this chunk of data. See if there's another, loop or done.
MINBFI: MOVS T0, .POCT2(T2)
CAMN T0, .POCT2(T2) ;Skip if <available> .ne. <returned>
JRST MINDON
; Although .POLOC isn't documented to use the address limit fields, it seems
; to as of TOPS-20 5.1
MOVE T1, -1(T1) ;Get last PDVA (incremented after ref above)
AOS T1 ;Increment
MOVEM T1, .POADR(T2) ;Set up starting address for next search
JRST MINSRC
MINDON: ADJSP P, -MINTMP
RET
SUBTTL DY$CBK -- Call back into section zero
; This routine is for use by a dynamic library that has been called from
; section zero using the xxxZER mechanism. It is intended for, e.g., calling
; user error-processing routines specified in the call.
; [51] In edit 51, I extended DY$CBK to handle callbacks in cases other
; than when called through the ZER mechanism. The possibility of programs
; including both section zero and non-zero sections is better accounted for.
;
; [51] If the routine to be called back to is in section zero or in the section
; which zero is mapped to (if any), the old action is taken.
;
; [51] If the routine to be called is in a non-zero section other than the one
; to which section zero is mapped, a direct call is made.
;
; The passing of arguments to DY$CBK and to the routine being called
; are intermixed, making things somewhat complicated.
; Arguments to DY$CBK are placed on the stack in the following order:
; -2(P)/ Address of routine to call [51]
; -1(P)/ Return PC of call into library.
; [51] NOTE WELL: if called through the ZER mechanism, this
; must be the return address from the transition from ZMS to
; library. (If not called through ZER, this is ignored.)
; This is used to find the appropriate ZMV in ZMS.
; 0(P)/ Return address of call to DY$CBK (put here by the PUSHJ)
; Arguments to the routine being called may be pushed onto the stack before
; the DY$CBK arguments (which are removed before the routine is called),
; or may be in registers (all registers are preserved to the entry to the
; user routine). Remember that a routine in section 0 will not be able to
; do anything with addresses in a non-zero section!
DY$CBK::
; AC save and local stack offsets
CBKACC==4 ;Save 4 ACs
CBKAC1==-<CBKACC-1>
CBKAC2==-<CBKACC-2>
CBKAC3==-<CBKACC-3>
CBKAC4==-<CBKACC-4>
; Argument stack offsets
CBKRTN==-<CBKACC+2> ;Address of user routine to call
CBKXPC==-<CBKACC+1> ;Return PC of user call to library
CBKRET==-<CBKACC> ;Our return address
ADJSP P, CBKACC ;Make room to save ACs
DMOVEM T1, CBKAC1(P)
DMOVEM T3, CBKAC3(P)
; [51] Determine if we do fancy call or cheap call
HLRZ T1, CBKRTN(P) ;[51] Get section number of routine
JUMPE T1, CBKNOR ;[51] From zero, do it the hard way
CAME T1, DY.ZMP ;[51]
JRST CBKEZ ;[51] Not from ZMS, do it the easy way
CBKNOR: ;[51]
; Get global index to ZV structure in zero-map-section (ZMS)
MOVE T4, CBKXPC(P) ;Get XPC
HRR T4, .DYZVO(T4) ;Make global index to ZV
;Magic constant, see DYNSYM
; Push CBKRET on ZY stack
XMOVEI T3, @.ZVZBL(T4) ;Adr of ZY pointer
AOS @T3 ;Push onto ZY stack
MOVE T2, CBKRET(P)
MOVEM T2, @0(T3) ;Put data into space created
; Calculate proper section 0 local stack pointer
XMOVEI T3, @0(T3) ;Get ZBL stack pointer (we have pushed to it)
SOS T3 ;Now have pointer to ZY block we want
HRLZI T1, CBKRET(P) ;In-section address of stack word
HRLZ T2, .ZYSP(T3)
SUB T2, T1
ADD T2, .ZYSP(T3)
HRRI T2, CBKRET(P) ;Whew!
MOVEM T2, CBKRET(P) ;Final location
; The time has come to talk in detail about stack formats. The next
; bit of code spends most of its time setting up the stack the way it
; should be when we leave this routine.
;
; Currently, the stack looks like this:
;
; CBKRTN/ Address of routine we will call
; CBKXPC/ Return address into ZMS from call to library
; CBKRET/ Originally return address from DY$CBK,
; recently trashed; now contains the
; section-local stack pointer to be restored in
; DY$CS0
; CBKAC1/ Saved AC1
; CBKAC2/ Saved AC2
; CBKAC3/ Saved AC3
; P/--> CBKAC4/ Saved AC4
;
; After this munging around, we are going to RET, which we want to
; take us to the DY$CS0 routine in the section to which zero is mapped
; (the Zero Map Section, or ZMS). The CBKXXX stack offset labels are
; used informally to refer to the absolute location they referred to
; after we had saved registers above.
;
; Obviously to do this we must change the stack around a bit. We want
; it to look like this:
;
; CBKRTN/ 0,,DY$BKR
; CBKXPC/ 0,,address of routine we will call
; CBKRET/ New stack pointer to install in DY$CS0
; P/--> CBKAC1/ ZMS,,DY$CS0
MOVE T1, CBKRTN(P)
HRRZM T1, CBKXPC(P) ;[51] Final location (force 0,,n)
DMOVE T1, CBKAC1(P) ;Restore T1-T2
;
XMOVEI T3, @.ZVCS0(T4)
MOVEM T3, CBKAC1(P) ;ZMS,,DY$CS0
XMOVEI T3, @.ZVBKR(T4)
HRRZM T3, CBKRTN(P) ;0,,DY$BKR
;
DMOVE T3, CBKAC3(P) ;Restore T3-T4
ADJSP P, -<CBKACC-1> ;Remove excess stack stuff
RET ;After our stack doctoring, this "returns"
;to ZMS,,DY$CS0
; [51]
; Do the call-back the easy way (when not going back into zero)
CBKEZ: DMOVE T1, CBKAC1(P) ;[51] Restore T1-T2
DMOVE T3, CBKAC3(P) ;[51] Restore T3-T4
ADJSP P, -CBKACC ;[51] Back to where we entered
EXCH T1, -2(P) ;[51] Save T1, get routine address to call
MOVEM T1, -1(P) ;[51] Put over dummy arg
POP P, T1 ;[51] Get return address
EXCH T1, -1(P) ;[51] Restore T1, save return address
RET ;[51] Call routine
SUBTTL Force loading of RTL
SEGMENT CODE
; [51]
; This routine doesn't do anything; calling it will insure that the RTL
; has been loaded (thus allowing access to galactic variables).
RL$NUL::
RET
SUBTTL Routine stubs for routines not yet implemented
SEGMENT CODE
DY$LOD::
DY$LER::
PUSH P, 0(P) ;Duplicate return address
PUSH P, [1] ;Arg count
PUSH P, [DY$NYI] ;Condition
JRST RTLERR
SEGMENT DATA
; Static block for RFSTS% JSYS (which, as of 16-dec-83, couldn't handle a
; non-local address for its status block)
RFSHCK: BLOCK 5 ;[RFSTS WORKAROUND]
; [51]
; If we are called through ZERBOO from section zero, ZERBOO will fill in this
; location with the number of the section that it mapped zero to. This is
; used in DY$CBK to determine if the routine was called via ZER or JCK.
DY.ZMP:: 0 ;[51] Do NOT master init this!
;[51] On a restart, zero is already mapped
;[51] somewherw!
SEGMENT CODE
END