Machine-Independent MDL for TOPS-20 and VAX.
[pdp10-muddle.git] / mim / development / mim / vax / mimlib / gc-dump-d.mud
diff --git a/mim/development/mim/vax/mimlib/gc-dump-d.mud b/mim/development/mim/vax/mimlib/gc-dump-d.mud
new file mode 100644 (file)
index 0000000..26be0a5
--- /dev/null
@@ -0,0 +1,405 @@
+<PACKAGE "GC-DUMP-D">
+
+<ENTRY GC-DUMP>
+
+<INCLUDE-WHEN <FEATURE? "COMPILER"> "GC-DUMP-DEFS">
+
+%%<GDECL (PURE-ZONE) <OR ZONE FALSE>>  ;"to override GDECL in compiler"
+
+<DEFINE GC-DUMP (OBJ
+                "OPT" (CHAN .OUTCHAN)  ;"default to OUTCHAN (???)"
+                "AUX" GCP NEW-SIZE CZN NEW-ZONE NGCP AL BLOCK BLOCK-LENGTH
+                      HEADER NEWOBJ FLAG PZN)
+       #DECL ((CHAN) <CHANNEL 'DISK> (OBJ) ANY (GCP NGCP) GC-PARAMS
+              (NEW-SIZE BLOCK-LENGTH) FIX (PZN CZN NEW-ZONE) ZONE 
+              (HEADER) LIST (BLOCK) UVECTOR)
+       <COND
+        (<S=? <CHANNEL-OP .CHAN GET-BYTE-SIZE> "BINARY">
+         <IFSYS ("TOPS20"
+                 <COND (<AND <GASSIGNED? PURE-ZONE> ,PURE-ZONE>
+                        <SET PZN ,PURE-ZONE>
+                        <MAPF <>
+                              <FUNCTION (A) #DECL ((A) AREA)
+                                   <DO-SPACS </ <AMIN .A> ,PSIZE>
+                                             </ <ABOT .A> ,PSIZE>
+                                             ,M$$COPY-ON-WRITE>>
+                              <ALL-SPACES .PZN>>)>)> 
+         <SET GCP <GC-PARAMS <SET CZN ,CURRENT-ZONE>>>
+         ;"calculate how large a zone we (could possibly) need"
+         <SET NEW-SIZE
+              <MAPF ,+
+                    <FUNCTION (A) 
+                            #DECL ((A) AREA)
+                            <COND (<==? <AMIN .A> <GCSMIN .GCP>>
+                                   <AMAX .A <GCSMAX .GCP>>
+                                   <ABOT .A <GCSBOT .GCP>>)>
+                            <- <ABOT .A> <AMIN .A>>>
+                    <ALL-SPACES .CZN>>>
+         <IFSYS ("TOPS20"
+                 <SET NEW-SIZE
+                      <* 261632 </ <+ .NEW-SIZE 261632 -1> 261632>>>)>
+         ;"create the new zone"
+         <SETG NEW-ZONE
+               <SET NEW-ZONE
+                    <CREATE-NEW-GC-SPACE  ;"change to CREATE-NEW-SPACE ???"
+                     <LSH .NEW-SIZE <- ,ADDR-SHIFT>> <GC-CTL .CZN>>>>
+         <SETG AL <GCSMIN <SET NGCP <GC-PARAMS .NEW-ZONE>>>>
+         <SETG SPACE-END <GCSMAX .NGCP>>
+         <SETG WORDS-NEEDED 0>
+         ;"WORDS-NEEDED counts how many words of memory might be needed
+           by GC-READ when it reads in this object."
+         <SETG NUMBER-OF-NEWTYPES 0>
+         <SET FLAG
+              <PROG DUMP-FRAME ()
+                 #DECL ((DUMP-FRAME) <SPECIAL FRAME>)
+                 ;"Provide DUMP-FRAME as an emergency exit"
+                 <SET NEWOBJ <DUMP .OBJ>>
+                 %<>>>
+         <UNMARK .OBJ>  ;"whether dump succeeded or not, clean up"
+         <IFSYS ("TOPS20"
+                 <COND (<ASSIGNED? PZN>
+                        <MAPF <>
+                              <FUNCTION (A) #DECL ((A) AREA)
+                                   <DO-SPACS </ <AMIN .A> ,PSIZE>
+                                             </ <ABOT .A> ,PSIZE>
+                                             ,M$$READ-ONLY-EXECUTE>>
+                              <ALL-SPACES .PZN>>)>)>
+         <COND (<TYPE? .FLAG ATOM> ;"if general error occured"
+                <FLUSH-ZONE .NEW-ZONE>
+                <ERROR .FLAG GC-DUMP>)
+               (.FLAG ;"if he tried to dump something on the stack"
+                <FLUSH-ZONE .NEW-ZONE>
+                <ERROR UNDUMPABLE-OBJECT!-ERRORS .FLAG GC-DUMP>)
+               (ELSE ;"winnage"
+                <SET HEADER <CALL ALLOCL ,AL>>
+                <PUT .HEADER 1 .NEWOBJ>
+                <PUTREST .HEADER .HEADER>
+                ;"dump one more object, the `header', a circular list of one
+                  element, the object dumped"
+                <SET BLOCK-LENGTH
+                     <LSH <- <+ ,AL <LSH ,LENGTH-LIST ,ADDR-SHIFT>>
+                             <GCSMIN .NGCP>>
+                          <- ,ADDR-SHIFT>>>
+                <SET BLOCK
+                     <CALL OBJECT
+                           ,TYPE-C-UVECTOR
+                           .BLOCK-LENGTH
+                           <GCSMIN .NGCP>>>
+                ;"create a uvector out of the dumped object"
+                <CHANNEL-OP .CHAN WRITE-BYTE ,NUMBER-OF-NEWTYPES>
+                <CHANNEL-OP .CHAN WRITE-BYTE ,WORDS-NEEDED>
+                <CHANNEL-OP .CHAN WRITE-BYTE .BLOCK-LENGTH>
+                <CHANNEL-OP .CHAN WRITE-BUFFER .BLOCK>
+                ;"write the data to the file"
+                <FLUSH-ZONE .NEW-ZONE>
+                .OBJ)>)
+        (ELSE <ERROR CHANNEL-HAS-WRONG-BYTE-SIZE!-ERRORS GC-DUMP>)>>
+
+<DEFINE DUMP (X "AUX" (FTYP <ANDB <CALL TYPE .X> 7>) PTYP) 
+       #DECL ((X) ANY (FTYP) FIX (PTYP) ATOM)
+       <CASE ,==?
+             .FTYP
+             (,STYPE-FIX <DUMP-NEWTYPE? .X> .X)
+             (,STYPE-UVECTOR <DUMP-UVECTOR .X>)
+             (,STYPE-STRING <DUMP-STRING .X>)
+             (,STYPE-BYTES <DUMP-BYTES .X>)
+             (,STYPE-VECTOR <DUMP-VECTOR .X>)
+             (,STYPE-LIST <DUMP-LIST .X>)
+             (,STYPE-RECORD
+              <SET PTYP <PRIMTYPE .X>>
+              <COND (<==? .PTYP ATOM> <DUMP-ATOM .X>)
+                    (<==? .PTYP GBIND> <DUMP-GBIND .X>)
+                    (ELSE <RETURN .X .DUMP-FRAME>)>)
+             DEFAULT
+             (<RETURN .X .DUMP-FRAME>)>>
+
+<DEFINE DUMP-NEWTYPE? (OBJ
+                      "AUX" (TYP <LSH <CALL TYPE .OBJ> -6>) TYP-ATM PTYP-ATM
+                            ENTRY)
+       #DECL ((OBJ) ANY (TYP) FIX (ENTRY) TYPE-ENTRY (TYP-ATM PTYP-ATM) ATOM)
+       <COND (<G? .TYP ,OLD-TYPES> ;"if the object is a newtype"
+              <SET ENTRY <NTH ,M$$TYPE-INFO!-INTERNAL <+ .TYP 1>>>
+              <SET TYP-ATM <DUMP-ATOM <M$$NTYPE .ENTRY>>>
+              ;"dump the atom naming the type (or get a pointer to it if it"
+              ;"already has been dumped)"
+              <COND (<NOT <M$$GVAL .TYP-ATM>>
+                     ;"if the atom does not have a GVAL (which marks it as"
+                     ;"being the name of a newtype)"
+                     <SETG NUMBER-OF-NEWTYPES <+ ,NUMBER-OF-NEWTYPES 1>>
+                     <SETG WORDS-NEEDED <+ ,WORDS-NEEDED ,LENGTH-TYPE-ENTRY>>
+                     <SET PTYP-ATM <DUMP-ATOM <M$$PTYPE .ENTRY>>>
+                     ;"then dump the atom naming the primtype"
+                     <M$$GVAL .TYP-ATM
+                              <CALL OBJECT
+                                    ,TYPE-C-GBIND
+                                    ,LENUU-GBIND
+                                    <CALL VALUE .PTYP-ATM>>>
+                     ;"and shove it (directly) into the GVAL slot")>
+              T)>>
+
+<DEFINE DUMP-VECTOR (V "AUX" (TOP-V <CALL TOPU .V>) FX NEW-V OLD-AL NEW-AL)
+        ;"the classic case"
+       #DECL ((V) <PRIMTYPE VECTOR> (TOP-V NEW-V) VECTOR
+              (FX NEW-AL OLD-AL) <OR FIX VECTOR>)
+       <SET FX <CALL MARKUV? .TOP-V 1>> ;"see if the object is marked"
+       <COND (<TYPE? .FX FIX> ;"if not"
+              <DUMP-NEWTYPE? .V> ;"take care of the object if it is a newtype"
+              <SET OLD-AL ,AL> ;"remember where we now are in the zone"
+              <CALL MARKUV .TOP-V <SET NEW-V <CALL ALLOCUV .OLD-AL .TOP-V>>>
+              ;"mark the old vector to point to where the new vector will be"
+              <SET NEW-AL
+                   <+ .OLD-AL <LSH <+ <* <LENGTH .TOP-V> 2> 2> ,ADDR-SHIFT>>>
+              ;"calculate where this will put us in the zone"
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     ;"if we would run out of room, error"
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE ;"otherwise, make it official"
+                     <SETG AL .NEW-AL>)>
+              <CALL BLT .TOP-V .NEW-V <+ <* <LENGTH .TOP-V> 2> 2>>
+              ;"actually copy the object"
+              <CALL MARKUV .NEW-V 0>
+              ;"unmark it (since we copied a marked object, this is"
+              ;"necessary)"
+              <MAPR %<> ;"dump the elements of the old vector and shove the"
+                        ;"results into the new vector"
+                    <FUNCTION (RV) 
+                            #DECL ((RV) VECTOR)
+                            <PUT .RV 1 <DUMP <1 .RV>>>>
+                    .NEW-V>)
+             (ELSE ;"if the object has already been dumped, return it"
+              <SET NEW-V .FX>)>
+       ;"return an object of the correct length and type"
+       <CHTYPE <REST .NEW-V <- <LENGTH .TOP-V> <LENGTH .V>>> <TYPE .V>>>
+
+<DEFINE DUMP-UVECTOR (UV
+                     "AUX" (TOP-UV <CALL TOPU .UV>) FX NEW-UV OLD-AL NEW-AL)
+       #DECL ((UV) <PRIMTYPE UVECTOR> (TOP-UV NEW-UV) UVECTOR
+              (FX) <OR FIX UVECTOR> (NEW-AL OLD-AL) FIX)
+       <SET FX <CALL MARKUU? .TOP-UV 1>>
+       <COND (<TYPE? .FX FIX>
+              <DUMP-NEWTYPE? .UV>
+              <SET OLD-AL ,AL>
+              <CALL MARKUU
+                    .TOP-UV
+                    <SET NEW-UV <CALL ALLOCUU .OLD-AL .TOP-UV>>>
+              <SET NEW-AL
+                   <+ .OLD-AL <LSH <+ <LENGTH .TOP-UV> 2> ,ADDR-SHIFT>>>
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE <SETG AL .NEW-AL>)>
+              <CALL BLT .TOP-UV .NEW-UV <+ <LENGTH .TOP-UV> 2>>
+              <CALL MARKUU .NEW-UV 0>)
+             (ELSE <SET NEW-UV .FX>)>
+       <CHTYPE <REST .NEW-UV <- <LENGTH .TOP-UV> <LENGTH .UV>>> <TYPE .UV>>>
+
+<DEFINE DUMP-STRING (S "AUX" TOP-S FX NEW-S WL OLD-AL NEW-AL) 
+       #DECL ((S) <PRIMTYPE STRING> (TOP-S NEW-S) STRING (FX) <OR FIX STRING>
+              (WL NEW-AL OLD-AL) FIX)
+       <SET TOP-S <CALL TOPU .S>>
+       <SET FX <CALL MARKUS? .TOP-S 1>>
+       <COND (<TYPE? .FX FIX>
+              <DUMP-NEWTYPE? .S>
+              <SET OLD-AL ,AL>
+              <SET WL <+ </ <+ <LENGTH .TOP-S> ,CHARS-WD-1> ,CHARS-WD> 2>>
+              <CALL MARKUS .TOP-S <SET NEW-S <CALL ALLOCUS .OLD-AL .TOP-S>>>
+              <SET NEW-AL <+ .OLD-AL <LSH .WL ,ADDR-SHIFT>>>
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE <SETG AL .NEW-AL>)>
+              <CALL BLT <ADDR-S .TOP-S> <ADDR-S .NEW-S> .WL>
+              <CALL MARKUS .NEW-S 0>)
+             (ELSE <SET NEW-S .FX>)>
+       <CHTYPE <REST .NEW-S <- <LENGTH .TOP-S> <LENGTH .S>>> <TYPE .S>>>
+
+<DEFINE DUMP-BYTES (BS "AUX" TOP-BS NEW-BS FX WL OLD-AL NEW-AL) 
+       #DECL ((BS) <PRIMTYPE BYTES> (TOP-BS NEW-BS) BYTES (FX) <OR FIX BYTES>
+              (WL NEW-AL OLD-AL) FIX)
+       <SET TOP-BS <CALL TOPU .BS>>
+       <SET FX <CALL MARKUB? .TOP-BS 1>>
+       <COND (<TYPE? .FX FIX>
+              <DUMP-NEWTYPE? .BS>
+              <SET OLD-AL ,AL>
+              <SET WL <+ </ <+ <LENGTH .TOP-BS> ,BYTES-WD-1> ,BYTES-WD> 2>>
+              <CALL MARKUB
+                    .TOP-BS
+                    <SET NEW-BS <CALL ALLOCUB .OLD-AL .TOP-BS>>>
+              <SET NEW-AL <+ .OLD-AL <LSH .WL ,ADDR-SHIFT>>>
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE <SETG AL .NEW-AL>)>
+              <CALL BLT <ADDR-S .TOP-BS> <ADDR-S .NEW-BS> .WL>
+              <CALL MARKUB .NEW-BS 0>)
+             (ELSE <SET NEW-BS .FX>)>
+       <CHTYPE <REST .NEW-BS <- <LENGTH .TOP-BS> <LENGTH .BS>>> <TYPE .BS>>>
+
+;"Note that the code to dump (and unmark) lists is extremely hairy because 
+  lists don't have relocation fields, which we need, yet we cannot permenantly 
+  mung the old list.  
+
+Solution:
+
+1.  Dump the list as usual.
+
+--------------------------         --------------------------
+| rest of old list       |         | rest of new list       |
+--------------------------         --------------------------
+| type-c     | length    |         | type-c     | length    |
+--------------------------         --------------------------
+| pointer to old element |         | pointer to new element |
+--------------------------         --------------------------
+
+2.  Use the rest pointer of the old list as the relocation pointer (as is done
+in the copy-gc).  However, store the old rest pointer (which will need to be
+restored) in the type-c and length slots of the new list.
+
+--------------------------         --------------------------
+| pointer to new list    |         | rest of new list       |
+--------------------------         --------------------------
+| type-c     | length    |         | rest of old list       |
+--------------------------         --------------------------
+| pointer to old element |         | pointer to new element |
+--------------------------         --------------------------
+
+3.  During the unmarking phase, copy the old rest pointer from the new list to
+the old list and copy the type-c and length from the new list to the old list.
+
+--------------------------         --------------------------
+| rest of old list       |         | rest of new list       |
+--------------------------         --------------------------
+| type-c     | length    |         | type-c     | length    |
+--------------------------         --------------------------
+| pointer to old element |         | pointer to new element |
+--------------------------         --------------------------"
+
+<DEFINE DUMP-LIST (L "AUX" NEW-L REST-L OLD-AL NEW-AL) 
+       #DECL ((L) <PRIMTYPE LIST> (NEW-L REST-L) LIST (OLD-AL NEW-AL) FIX)
+       <COND (<EMPTY? .L> <DUMP-NEWTYPE? .L> <SET NEW-L .L>)
+             (<==? <CALL MARKL? .L> 0>
+              <DUMP-NEWTYPE? .L>
+              <SET OLD-AL ,AL>
+              <SET NEW-L <CALL ALLOCL .OLD-AL>>
+              <SET NEW-AL <+ .OLD-AL <LSH ,LENGTH-LIST ,ADDR-SHIFT>>>
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE <SETG AL .NEW-AL>)>
+              <SET REST-L <REST .L>>
+              <PUTREST .NEW-L .REST-L>
+              <PUT .NEW-L 1 <1 .L>>
+              <CALL MARKL .L 1>
+              <PUTREST .L .NEW-L>
+              <PUTREST .NEW-L <DUMP .REST-L>>
+              <PUT .NEW-L
+                   1
+                   <CALL OBJECT
+                         <LHW <CALL VALUE .REST-L>>
+                         <RHW <CALL VALUE .REST-L>>
+                         <CALL VALUE <DUMP <1 .NEW-L>>>>>)
+             (ELSE <SET NEW-L <REST .L>>)>
+       <CHTYPE .NEW-L <TYPE .L>>>
+
+<DEFINE DUMP-ATOM (ATM "AUX" OLD-AL NEW-AL FX NEW-ATM) 
+       #DECL ((ATM NEW-ATM) <PRIMTYPE ATOM> (OLD-AL NEW-AL) FIX
+              (FX) <OR FIX ATOM>)
+       <COND (<==? <CHTYPE .ATM ATOM> ROOT>
+              <SET NEW-ATM <CALL OBJECT <CALL TYPE .ATM> ,LENUU-ATOM -1>>)
+             (<TYPE? <SET FX <CALL MARKR? .ATM 1>> FIX>
+              <DUMP-NEWTYPE? .ATM>
+              <SET OLD-AL ,AL>
+              <SETG WORDS-NEEDED <+ ,WORDS-NEEDED ,LENGTH-LIST>>
+              <CALL MARKR .ATM <SET NEW-ATM <CALL ALLOCR .OLD-AL .ATM>>>
+              <SET NEW-AL <+ .OLD-AL <LSH ,LENGTH-ATOM ,ADDR-SHIFT>>>
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE <SETG AL .NEW-AL>)>
+              <CALL BLT .ATM .NEW-ATM ,LENGTH-ATOM>
+              <CALL MARKR .NEW-ATM 0>
+              <M$$GVAL .NEW-ATM %<>> 
+              <M$$LVAL .NEW-ATM %<>>
+              ;"don't dump GVALs or LVALs"
+              ;"Note:  The gval slot IS used to store the primtypes of"
+              ;"newtypes, but that is done by DUMP-NEWTYPE?"
+              <M$$PNAM .NEW-ATM <DUMP-STRING <M$$PNAM .NEW-ATM>>>
+              <M$$OBLS .NEW-ATM <DUMP <M$$OBLS .NEW-ATM>>>)
+             (ELSE <SET NEW-ATM .FX>)>
+       .NEW-ATM>
+
+<DEFINE DUMP-GBIND (GB "AUX" NGB OLD-AL NEW-AL FX) 
+       #DECL ((GB NGB) <PRIMTYPE GBIND> (FX) <OR FIX GBIND>
+              (OLD-AL NEW-AL) FIX)
+       <COND (<TYPE? <SET FX <CALL MARKR? .GB 1>> FIX>
+              <DUMP-NEWTYPE? .GB>
+              <SET OLD-AL ,AL>
+              <CALL MARKR .GB <SET NGB <CALL ALLOCR .OLD-AL .GB>>>
+              <SET NEW-AL <+ .OLD-AL <LSH ,LENGTH-GBIND ,ADDR-SHIFT>>>
+              <COND (<G? .NEW-AL ,SPACE-END>
+                     <RETURN NO-MORE-ROOM!-ERRORS .DUMP-FRAME>)
+                    (ELSE <SETG AL .NEW-AL>)>
+              <CALL BLT .GB .NGB ,LENGTH-GBIND>
+              <CALL MARKR .NGB 0>
+              <M$$VALU .NGB ,M$$UNBOUND>
+              <M$$ATOM .NGB <DUMP <M$$ATOM .NGB>>>
+              <M$$DECL .NGB %<>>
+              ;"don't dump the decl of gbinds")
+             (ELSE <SET NGB .FX>)>
+       .NGB>
+
+<DEFINE UNMARK (OBJ "AUX" FX WEIRD FTYP PTYP)
+        ;"unmark objects in general and repair lists in particular"
+       #DECL ((OBJ WEIRD) ANY (FX) <OR FIX LIST> (PTYP) ATOM (FTYP) FIX)
+       <BIND ((TYP <LSH <CALL TYPE .OBJ> -6>) ENTRY)
+          <COND (<G? .TYP ,OLD-TYPES>
+                 <SET ENTRY <NTH ,M$$TYPE-INFO!-INTERNAL <+ .TYP 1>>>
+                 <UNMARK <M$$NTYPE .ENTRY>>
+                 <UNMARK <M$$PTYPE .ENTRY>>)>>
+       <SET FTYP <ANDB <CALL TYPE .OBJ> 7>>
+       <CASE ,==?
+             .FTYP
+             (,STYPE-UVECTOR <CALL MARKUU .OBJ 0>)
+             (,STYPE-STRING <CALL MARKUS .OBJ 0>)
+             (,STYPE-BYTES <CALL MARKUB .OBJ 0>)
+             (,STYPE-VECTOR
+              <COND (<NOT <TYPE? <CALL MARKUV? .OBJ 1> FIX>>
+                     <CALL MARKUV .OBJ 0>
+                     <MAPF %<> ,UNMARK <CHTYPE .OBJ VECTOR>>)>)
+             (,STYPE-LIST
+              <COND (<N==? <CALL MARKL? .OBJ> 0>
+                     <SET FX <REST .OBJ>>
+                     <SET WEIRD <1 .FX>>
+                     <PUTREST .OBJ
+                              <CALL OBJECT
+                                    ,TYPE-C-LIST
+                                    0
+                                    <PUTLHW <CALL LENUU .WEIRD>
+                                            <CALL TYPE .WEIRD>>>>
+                     <PUT .FX
+                          1
+                          <CALL OBJECT
+                                <CALL TYPE <1 .OBJ>>
+                                <CALL LENUU <1 .OBJ>>
+                                <CALL VALUE .WEIRD>>>
+                     <CALL MARKL .OBJ 0>
+                     <CALL MARKL .FX 0>
+                     <UNMARK <1 .OBJ>>
+                     <UNMARK <REST .OBJ>>)>)
+             (,STYPE-RECORD
+              <SET PTYP <PRIMTYPE .OBJ>>
+              <COND (<==? .PTYP ATOM>
+                     <COND (<NOT <TYPE? <CALL MARKR? .OBJ 1> FIX>>
+                            <CALL MARKR .OBJ 0>
+                            <UNMARK <M$$PNAM <CHTYPE .OBJ ATOM>>>
+                            <UNMARK <M$$OBLS <CHTYPE .OBJ ATOM>>>)>)
+                    (<==? .PTYP GBIND>
+                     <COND (<NOT <TYPE? <CALL MARKR? .OBJ 1> FIX>>
+                            <CALL MARKR .OBJ 0>
+                            <UNMARK <M$$ATOM <CHTYPE .OBJ GBIND>>>)>)>)>
+       T>
+
+<DEFINE DO-SPACS (START LAST MODE) 
+   #DECL ((START LAST MODE) FIX)
+   <REPEAT ()
+      <IFSYS ("TOPS20"
+             <CALL SYSOP SPACS <ORB ,M$$MY-PROC-LH .START> .MODE>)>
+      <COND (<==? .START .LAST> <RETURN>)>
+      <SET START <+ .START 1>>>>
+
+<ENDPACKAGE>