Fix white spaces.
[pforth] / fth / utils / clone.fth
index 99c0297..98254b5 100644 (file)
-\ @(#) clone.fth 97/12/10 1.1\r
-\ Clone for PForth\r
-\\r
-\ Create the smallest dictionary required to run an application.\r
-\\r
-\ Clone decompiles the Forth dictionary starting with the top\r
-\ word in the program.  It then moves all referenced secondaries\r
-\ into a new dictionary.\r
-\\r
-\ This work was inspired by the CLONE feature that Mike Haas wrote\r
-\ for JForth.  Mike's CLONE disassembled 68000 machine code then\r
-\ reassembled it which is much more difficult.\r
-\\r
-\ Copyright Phil Burk & 3DO 1994\r
-\\r
-\ O- trap custom 'C' calls\r
-\ O- investigate ALITERAL, XLITERAL, use XLITERAL in [']\r
-\r
-anew task-clone.fth\r
-decimal\r
-\r
-\ move to 'C'\r
-: PRIMITIVE? ( xt -- flag , true if primitive )\r
-       ['] FIRST_COLON <\r
-;\r
-\r
-: 'SELF ( -- xt , return xt of word being compiled )\r
-       ?comp\r
-       latest name>\r
-       [compile] literal\r
-; immediate\r
-\r
-\r
-:struct CL.REFERENCE\r
-       long  clr_OriginalXT    \ original XT of word\r
-       long  clr_NewXT         \ corresponding XT in cloned dictionary\r
-       long  clr_TotalSize     \ size including data in body\r
-;struct\r
-\r
-variable CL-INITIAL-REFS \ initial number of refs to allocate\r
-100 cl-initial-refs !\r
-variable CL-REF-LEVEL    \ level of threading while scanning\r
-variable CL-NUM-REFS     \ number of secondaries referenced\r
-variable CL-MAX-REFS     \ max number of secondaries allocated\r
-variable CL-LEVEL-MAX    \ max level reached while scanning\r
-variable CL-LEVEL-ABORT  \ max level before aborting\r
-10 cl-level-abort !\r
-variable CL-REFERENCES   \ pointer to cl.reference array\r
-variable CL-TRACE        \ print debug stuff if true\r
-\r
-\ Cloned dictionary builds in allocated memory but XTs are relative\r
-\ to normal code-base, if CL-TEST-MODE true.\r
-variable CL-TEST-MODE\r
\r
-variable CL-INITIAL-DICT \ initial size of dict to allocate\r
-20 1024 * cl-initial-dict !\r
-variable CL-DICT-SIZE    \ size of allocated cloned dictionary\r
-variable CL-DICT-BASE    \ pointer to virtual base of cloned dictionary\r
-variable CL-DICT-ALLOC   \ pointer to allocated dictionary memory\r
-variable CL-DICT-PTR     \ rel pointer index into cloned dictionary\r
-0 cl-dict-base !\r
-\r
-       \r
-: CL.INDENT ( -- )\r
-       cl-ref-level @ 2* 2* spaces\r
-;\r
-: CL.DUMP.NAME ( xt -- )\r
-       cl.indent\r
-       >name id. cr\r
-;\r
-\r
-: CL.DICT[] ( relptr -- addr )\r
-       cl-dict-base @ +\r
-;\r
-\r
-: CL,  ( cell -- , comma into clone dictionary )\r
-       cl-dict-ptr @ cl.dict[] !\r
-       cell cl-dict-ptr +!\r
-;\r
-\r
-\r
-: CL.FREE.DICT ( -- , free dictionary we built into )\r
-       cl-dict-alloc @ ?dup\r
-       IF\r
-               free dup ?error\r
-               0 cl-dict-alloc !\r
-       THEN\r
-;\r
-\r
-: CL.FREE.REFS ( -- , free dictionary we built into )\r
-       cl-references @ ?dup\r
-       IF\r
-               free dup ?error\r
-               0 cl-references !\r
-       THEN\r
-;\r
-\r
-: CL.ALLOC.REFS ( --  , allocate references to track )\r
-       cl-initial-refs @  \ initial number of references\r
-       dup cl-max-refs ! \ maximum allowed\r
-       sizeof() cl.reference *\r
-       allocate dup ?error\r
-       cl-references !\r
-;\r
-\r
-: CL.RESIZE.REFS ( -- , allocate references to track )\r
-       cl-max-refs @   \ current number of references allocated\r
-       5 * 4 / dup cl-max-refs ! \ new maximum allowed\r
-\ cl.indent ." Resize # references to " dup . cr\r
-       sizeof() cl.reference *\r
-       cl-references @ swap resize dup ?error\r
-       cl-references !\r
-;\r
-\r
-\r
-: CL.ALLOC.DICT ( -- , allocate dictionary to build into )\r
-       cl-initial-dict @  \ initial dictionary size\r
-       dup cl-dict-size !\r
-       allocate dup ?error\r
-       cl-dict-alloc !\r
-\\r
-\ kludge dictionary if testing\r
-       cl-test-mode @\r
-       IF\r
-               cl-dict-alloc @ code-base @ - cl-dict-ptr +!\r
-               code-base @ cl-dict-base !\r
-       ELSE\r
-               cl-dict-alloc @  cl-dict-base !\r
-       THEN\r
-       ." CL.ALLOC.DICT" cr\r
-       ."   cl-dict-alloc = $" cl-dict-alloc @ .hex cr\r
-       ."   cl-dict-base  = $" cl-dict-base @ .hex cr\r
-       ."   cl-dict-ptr   = $" cl-dict-ptr @ .hex cr\r
-;\r
-\r
-: CODEADDR>DATASIZE { code-addr -- datasize }\r
-\ Determine size of any literal data following execution token.\r
-\ Examples are text following (."), or branch offsets.\r
-       code-addr @\r
-       CASE\r
-       ['] (literal) OF cell ENDOF   \ a number\r
-       ['] 0branch   OF cell ENDOF   \ branch offset\r
-       ['] branch    OF cell ENDOF\r
-       ['] (do)      OF    0 ENDOF\r
-       ['] (?do)     OF cell ENDOF\r
-       ['] (loop)    OF cell ENDOF\r
-       ['] (+loop)   OF cell ENDOF\r
-       ['] (.")      OF code-addr cell+ c@ 1+ ENDOF  \ text\r
-       ['] (s")      OF code-addr cell+ c@ 1+ ENDOF\r
-       ['] (c")      OF code-addr cell+ c@ 1+ ENDOF\r
-       0 swap\r
-       ENDCASE\r
-;\r
-\r
-: XT>SIZE  ( xt -- wordsize , including code and data )\r
-       dup >code\r
-       swap >name\r
-       dup latest =\r
-       IF\r
-               drop here\r
-       ELSE\r
-               dup c@ 1+ + aligned 8 + \ get next name\r
-               name> >code \ where is next word\r
-       THEN\r
-       swap -\r
-;\r
-\r
-\ ------------------------------------------------------------------\r
-: CL.TRAVERSE.SECONDARY { code-addr ca-process | xt dsize --  }\r
-\ scan secondary and pass each code-address to ca-process\r
-\ CA-PROCESS ( code-addr -- , required stack action for vector )\r
-       1 cl-ref-level +!\r
-       cl-ref-level @ cl-level-abort @ > abort" Clone exceeded CL-ABORT-LEVEL"\r
-       BEGIN\r
-               code-addr @ -> xt\r
-\ cl.indent ." CL.TRAVERSE.SECONDARY - code-addr = $" code-addr .hex ." , xt = $" xt .hex cr\r
-               code-addr codeaddr>datasize -> dsize      \ any data after this?\r
-               code-addr ca-process execute              \ process it\r
-               code-addr cell+ dsize + aligned -> code-addr  \ skip past data\r
-\ !!! Bummer! EXIT called in middle of secondary will cause early stop.\r
-               xt  ['] EXIT  =                           \ stop when we get to EXIT\r
-       UNTIL\r
-       -1 cl-ref-level +!\r
-;\r
-\r
-\ ------------------------------------------------------------------\r
-\r
-: CL.DUMP.XT ( xt -- )\r
-       cl-trace @\r
-       IF\r
-               dup primitive?\r
-               IF   ." PRI:  "\r
-               ELSE ." SEC:  "\r
-               THEN\r
-               cl.dump.name\r
-       ELSE\r
-               drop\r
-       THEN\r
-;\r
-\r
-\ ------------------------------------------------------------------\r
-: CL.REF[] ( index -- clref )\r
-       sizeof() cl.reference *\r
-       cl-references @ +\r
-;\r
-\r
-: CL.DUMP.REFS ( -- , print references )\r
-       cl-num-refs @ 0\r
-       DO\r
-               i 3 .r ."  : "\r
-               i cl.ref[]\r
-               dup s@ clr_OriginalXT >name id. ."  => "\r
-               dup s@ clr_NewXT .\r
-               ." , size = "\r
-               dup s@ clr_TotalSize . cr\r
-               drop \ clref\r
-       loop\r
-;                      \r
-               \r
-: CL.XT>REF_INDEX { xt | indx flag -- index flag , true if found }\r
-       BEGIN\r
-\ cl.indent ." CL.XT>REF_INDEX - indx = " indx . cr\r
-               indx cl-num-refs @ >=\r
-               IF\r
-                       true\r
-               ELSE\r
-                       indx cl.ref[] s@ clr_OriginalXT\r
-\ cl.indent ." CL.XT>REF_INDEX - clr_OriginalXT = " dup . cr\r
-                       xt  =\r
-                       IF\r
-                               true\r
-                               dup -> flag\r
-                       ELSE\r
-                               false\r
-                               indx 1+ -> indx\r
-                       THEN\r
-               THEN\r
-       UNTIL\r
-       indx flag\r
-\ cl.indent ." CL.XT>REF_INDEX - " xt >name id. space  indx . flag . cr\r
-;                      \r
-\r
-: CL.ADD.REF  { xt | clref -- , add referenced secondary to list }\r
-       cl-references @ 0= abort" CL.ADD.REF - References not allocated!"\r
-\\r
-\ do we need to allocate more room?\r
-       cl-num-refs @ cl-max-refs @ >=\r
-       IF\r
-               cl.resize.refs\r
-       THEN\r
-\\r
-       cl-num-refs @ cl.ref[] -> clref    \ index into array\r
-       xt clref s! clr_OriginalXT\r
-       0 clref s! clr_NewXT\r
-       xt xt>size clref s! clr_TotalSize\r
-\\r
-       1 cl-num-refs +!\r
-;\r
-\r
-\ ------------------------------------------------------------------\r
-\r
-\ called by cl.traverse.secondary to compile each piece of secondary\r
-: CL.RECOMPILE.SECONDARY { code-addr | xt clref dsize -- ,  }\r
-\ recompile to new location\r
-\ cl.indent ." CL.RECOMPILE.SECONDARY - enter - " .s cr\r
-       code-addr @ -> xt\r
-\ cl.indent ." CL.RECOMPILE.SECONDARY - xt = $" dup .hex dup >name id. cr\r
-       xt cl.dump.xt\r
-       xt primitive?\r
-       IF\r
-               xt cl,\r
-       ELSE\r
-               xt CL.XT>REF_INDEX\r
-               IF\r
-                       cl.ref[] -> clref\r
-                       clref s@ clr_NewXT\r
-                       dup 0= abort" CL.RECOMPILE.SECONDARY - unresolved NewXT"\r
-                       cl,\r
-               ELSE\r
-                       cl.indent ." CL.RECOMPILE.SECONDARY - xt not in ref table!" cr\r
-                       abort\r
-               THEN\r
-       THEN\r
-\\r
-\ transfer any literal data\r
-       code-addr codeaddr>datasize -> dsize\r
-       dsize 0>\r
-       IF\r
-\ cl.indent ." CL.RECOMPILE.SECONDARY - copy inline data of size" dsize . cr\r
-               code-addr cell+  cl-dict-ptr @ cl.dict[]  dsize  move\r
-               cl-dict-ptr @ dsize + aligned cl-dict-ptr !\r
-       THEN\r
-\ cl.indent ." CL.RECOMPILE.SECONDARY - leave - " .s cr\r
-;\r
-\r
-: CL.RECOMPILE.REF { indx | clref codesize datasize -- }\r
-\ all references have been resolved so recompile new secondary\r
-       depth >r\r
-       indx cl.ref[] -> clref\r
-       cl-trace @\r
-       IF\r
-               cl.indent\r
-               clref s@ clr_OriginalXT >name id. ."  recompiled at $"\r
-               cl-dict-ptr @ .hex cr    \ new address\r
-       THEN\r
-       cl-dict-ptr @  clref s! clr_NewXT\r
-\\r
-\ traverse this secondary and compile into new dictionary\r
-       clref s@ clr_OriginalXT\r
-       >code ['] cl.recompile.secondary cl.traverse.secondary\r
-\\r
-\ determine whether there is any data following definition\r
-       cl-dict-ptr @\r
-       clref s@ clr_NewXT - -> codesize \ size of cloned code\r
-       clref s@ clr_TotalSize \ total bytes\r
-       codesize - -> datasize\r
-       cl-trace @\r
-       IF\r
-               cl.indent\r
-               ." Move data: data size = " datasize . ." codesize = " codesize . cr\r
-       THEN\r
-\\r
-\ copy any data that followed definition\r
-       datasize 0>\r
-       IF\r
-               clref s@ clr_OriginalXT >code codesize +\r
-               clref s@ clr_NewXT cl-dict-base @ + codesize +\r
-               datasize move\r
-               datasize cl-dict-ptr +!  \ allot space in clone dictionary\r
-       THEN\r
-       \r
-       depth r> - abort" Stack depth change in CL.RECOMPILE.REF"\r
-;\r
-\r
-\ ------------------------------------------------------------------\r
-: CL.SCAN.SECONDARY ( code-addr -- , scan word and add referenced secondaries to list )\r
-       depth 1- >r\r
-\ cl.indent ." CL.SCAN.SECONDARY - enter - " .s cr\r
-       cl-ref-level @ cl-level-max @  MAX cl-level-max !\r
-       @ ( get xt )\r
-\ cl.indent ." CL.SCAN.SECONDARY - xt = " dup . dup >name id. cr\r
-       dup cl.dump.xt\r
-       dup primitive?\r
-       IF\r
-               drop\r
-\ cl.indent ." CL.SCAN.SECONDARY - found primitive." cr\r
-       ELSE\r
-               dup CL.XT>REF_INDEX\r
-               IF\r
-                       drop \ indx   \ already referenced once so ignore\r
-                       drop \ xt\r
-               ELSE\r
-                       >r \ indx\r
-                       dup cl.add.ref\r
-                       >code 'self cl.traverse.secondary   \ use 'self for recursion!\r
-                       r> cl.recompile.ref    \ now that all refs resolved, recompile\r
-               THEN\r
-       THEN\r
-\ cl.indent ." CL.SCAN.SECONDARY - leave - " .s cr\r
-       depth r> - abort" Stack depth change in CL.SCAN.SECONDARY"\r
-;\r
-\r
-: CL.CLONE.XT ( xt -- , scan top word and add referenced secondaries to list )\r
-       dup primitive? abort" Cannot CLONE a PRIMITIVE word!"\r
-       0 cl-ref-level !\r
-       0 cl-level-max !\r
-       0 cl-num-refs !\r
-       dup cl.add.ref     \ word being cloned is top of ref list\r
-       >code ['] cl.scan.secondary cl.traverse.secondary\r
-       0 cl.recompile.ref\r
-;\r
-\r
-\ ------------------------------------------------------------------\r
-: CL.XT>NEW_XT ( xt -- xt' , convert normal xt to xt in cloned dict )\r
-       cl.xt>ref_index 0= abort" not in cloned dictionary!"\r
-       cl.ref[] s@ clr_NewXT\r
-;\r
-: CL.XT>NEW_ADDR ( xt -- addr , addr in cloned dict )\r
-       cl.xt>New_XT\r
-       cl-dict-base @ +\r
-;\r
-\r
-: CL.REPORT ( -- )\r
-       ." Clone scan went " cl-level-max @ . ." levels deep." cr\r
-       ." Clone scanned " cl-num-refs @ . ." secondaries." cr\r
-       ." New dictionary size =  " cl-dict-ptr @ cl-dict-base @ - . cr\r
-;\r
-\r
-\r
-\ ------------------------------------------------------------------\r
-: CL.TERM ( -- , cleanup )\r
-       cl.free.refs\r
-       cl.free.dict\r
-;\r
-\r
-: CL.INIT ( -- )\r
-       cl.term\r
-       0 cl-dict-size !\r
-       ['] first_colon cl-dict-ptr !\r
-       cl.alloc.dict\r
-       cl.alloc.refs\r
-;\r
-\r
-: 'CLONE ( xt -- , clone dictionary from this word )\r
-       cl.init\r
-       cl.clone.xt\r
-       cl.report\r
-       cl.dump.refs\r
-       cl-test-mode @\r
-       IF ." WARNING - CL-TEST-MODE on so we can't save cloned image." cr\r
-       THEN\r
-;\r
-\r
-: SAVE-CLONE  ( <filename> -- )\r
-       bl word\r
-       ." Save cloned image in " dup count type\r
-       drop ." SAVE-CLONE unimplemented!" \ %Q\r
-;\r
-\r
-: CLONE ( <name> -- )\r
-       ' 'clone\r
-;\r
-\r
-if.forgotten cl.term\r
-\r
-\ ---------------------------------- TESTS --------------------\r
-\r
-\r
-: TEST.CLONE ( -- )\r
-       cl-test-mode @ not abort" CL-TEST-MODE not on!"\r
-       0 cl.ref[] s@ clr_NewXT  execute\r
-;\r
-\r
-\r
-: TEST.CLONE.REAL ( -- )\r
-       cl-test-mode @ abort" CL-TEST-MODE on!"\r
-       code-base @\r
-       0 cl.ref[] s@ clr_NewXT  \ get cloned execution token\r
-       cl-dict-base @ code-base !\r
-\ WARNING - code-base munged, only execute primitives or cloned code\r
-       execute\r
-       code-base !   \ restore code base for normal \r
-;\r
-\r
-\r
-: TCL1\r
-       34 dup +\r
-;\r
-\r
-: TCL2\r
-       ." Hello " tcl1  . cr\r
-;\r
-\r
-: TCL3\r
-       4 0\r
-       DO\r
-               tcl2\r
-               i . cr\r
-               i 100 + . cr\r
-       LOOP\r
-;\r
-\r
-create VAR1 567 ,\r
-: TCL4\r
-       345 var1 !\r
-       ." VAR1 = " var1 @ . cr\r
-       var1 @ 345 -\r
-       IF\r
-               ." TCL4 failed!" cr\r
-       ELSE\r
-               ." TCL4 succeded! Yay!" cr\r
-       THEN\r
-;\r
-\r
-\ do deferred words get cloned!\r
-defer tcl.vector\r
-\r
-: TCL.DOIT ." Hello Fred!" cr ;\r
-' tcl.doit is tcl.vector\r
-\r
-: TCL.DEFER\r
-       12 . cr\r
-       tcl.vector\r
-       999 dup + . cr\r
-;\r
-\r
-trace-stack on\r
-cl-test-mode on\r
-\r
+\ @(#) clone.fth 97/12/10 1.1
+\ Clone for PForth
+\
+\ Create the smallest dictionary required to run an application.
+\
+\ Clone decompiles the Forth dictionary starting with the top
+\ word in the program.  It then moves all referenced secondaries
+\ into a new dictionary.
+\
+\ This work was inspired by the CLONE feature that Mike Haas wrote
+\ for JForth.  Mike's CLONE disassembled 68000 machine code then
+\ reassembled it which is much more difficult.
+\
+\ Copyright Phil Burk & 3DO 1994
+\
+\ O- trap custom 'C' calls
+\ O- investigate ALITERAL, XLITERAL, use XLITERAL in [']
+
+anew task-clone.fth
+decimal
+
+\ move to 'C'
+: PRIMITIVE? ( xt -- flag , true if primitive )
+    ['] FIRST_COLON <
+;
+
+: 'SELF ( -- xt , return xt of word being compiled )
+    ?comp
+    latest name>
+    [compile] literal
+; immediate
+
+
+:struct CL.REFERENCE
+    long  clr_OriginalXT    \ original XT of word
+    long  clr_NewXT         \ corresponding XT in cloned dictionary
+    long  clr_TotalSize     \ size including data in body
+;struct
+
+variable CL-INITIAL-REFS \ initial number of refs to allocate
+100 cl-initial-refs !
+variable CL-REF-LEVEL    \ level of threading while scanning
+variable CL-NUM-REFS     \ number of secondaries referenced
+variable CL-MAX-REFS     \ max number of secondaries allocated
+variable CL-LEVEL-MAX    \ max level reached while scanning
+variable CL-LEVEL-ABORT  \ max level before aborting
+10 cl-level-abort !
+variable CL-REFERENCES   \ pointer to cl.reference array
+variable CL-TRACE        \ print debug stuff if true
+
+\ Cloned dictionary builds in allocated memory but XTs are relative
+\ to normal code-base, if CL-TEST-MODE true.
+variable CL-TEST-MODE
+
+variable CL-INITIAL-DICT \ initial size of dict to allocate
+20 1024 * cl-initial-dict !
+variable CL-DICT-SIZE    \ size of allocated cloned dictionary
+variable CL-DICT-BASE    \ pointer to virtual base of cloned dictionary
+variable CL-DICT-ALLOC   \ pointer to allocated dictionary memory
+variable CL-DICT-PTR     \ rel pointer index into cloned dictionary
+0 cl-dict-base !
+
+
+: CL.INDENT ( -- )
+    cl-ref-level @ 2* 2* spaces
+;
+: CL.DUMP.NAME ( xt -- )
+    cl.indent
+    >name id. cr
+;
+
+: CL.DICT[] ( relptr -- addr )
+    cl-dict-base @ +
+;
+
+: CL,  ( cell -- , comma into clone dictionary )
+    cl-dict-ptr @ cl.dict[] !
+    cell cl-dict-ptr +!
+;
+
+
+: CL.FREE.DICT ( -- , free dictionary we built into )
+    cl-dict-alloc @ ?dup
+    IF
+        free dup ?error
+        0 cl-dict-alloc !
+    THEN
+;
+
+: CL.FREE.REFS ( -- , free dictionary we built into )
+    cl-references @ ?dup
+    IF
+        free dup ?error
+        0 cl-references !
+    THEN
+;
+
+: CL.ALLOC.REFS ( --  , allocate references to track )
+    cl-initial-refs @  \ initial number of references
+    dup cl-max-refs ! \ maximum allowed
+    sizeof() cl.reference *
+    allocate dup ?error
+    cl-references !
+;
+
+: CL.RESIZE.REFS ( -- , allocate references to track )
+    cl-max-refs @   \ current number of references allocated
+    5 * 4 / dup cl-max-refs ! \ new maximum allowed
+\ cl.indent ." Resize # references to " dup . cr
+    sizeof() cl.reference *
+    cl-references @ swap resize dup ?error
+    cl-references !
+;
+
+
+: CL.ALLOC.DICT ( -- , allocate dictionary to build into )
+    cl-initial-dict @  \ initial dictionary size
+    dup cl-dict-size !
+    allocate dup ?error
+    cl-dict-alloc !
+\
+\ kludge dictionary if testing
+    cl-test-mode @
+    IF
+        cl-dict-alloc @ code-base @ - cl-dict-ptr +!
+        code-base @ cl-dict-base !
+    ELSE
+        cl-dict-alloc @  cl-dict-base !
+    THEN
+    ." CL.ALLOC.DICT" cr
+    ."   cl-dict-alloc = $" cl-dict-alloc @ .hex cr
+    ."   cl-dict-base  = $" cl-dict-base @ .hex cr
+    ."   cl-dict-ptr   = $" cl-dict-ptr @ .hex cr
+;
+
+: CODEADDR>DATASIZE { code-addr -- datasize }
+\ Determine size of any literal data following execution token.
+\ Examples are text following (."), or branch offsets.
+    code-addr @
+    CASE
+    ['] (literal) OF cell ENDOF   \ a number
+    ['] 0branch   OF cell ENDOF   \ branch offset
+    ['] branch    OF cell ENDOF
+    ['] (do)      OF    0 ENDOF
+    ['] (?do)     OF cell ENDOF
+    ['] (loop)    OF cell ENDOF
+    ['] (+loop)   OF cell ENDOF
+    ['] (.")      OF code-addr cell+ c@ 1+ ENDOF  \ text
+    ['] (s")      OF code-addr cell+ c@ 1+ ENDOF
+    ['] (c")      OF code-addr cell+ c@ 1+ ENDOF
+    0 swap
+    ENDCASE
+;
+
+: XT>SIZE  ( xt -- wordsize , including code and data )
+    dup >code
+    swap >name
+    dup latest =
+    IF
+        drop here
+    ELSE
+        dup c@ 1+ + aligned 8 + \ get next name
+        name> >code \ where is next word
+    THEN
+    swap -
+;
+
+\ ------------------------------------------------------------------
+: CL.TRAVERSE.SECONDARY { code-addr ca-process | xt dsize --  }
+\ scan secondary and pass each code-address to ca-process
+\ CA-PROCESS ( code-addr -- , required stack action for vector )
+    1 cl-ref-level +!
+    cl-ref-level @ cl-level-abort @ > abort" Clone exceeded CL-ABORT-LEVEL"
+    BEGIN
+        code-addr @ -> xt
+\ cl.indent ." CL.TRAVERSE.SECONDARY - code-addr = $" code-addr .hex ." , xt = $" xt .hex cr
+        code-addr codeaddr>datasize -> dsize      \ any data after this?
+        code-addr ca-process execute              \ process it
+        code-addr cell+ dsize + aligned -> code-addr  \ skip past data
+\ !!! Bummer! EXIT called in middle of secondary will cause early stop.
+        xt  ['] EXIT  =                           \ stop when we get to EXIT
+    UNTIL
+    -1 cl-ref-level +!
+;
+
+\ ------------------------------------------------------------------
+
+: CL.DUMP.XT ( xt -- )
+    cl-trace @
+    IF
+        dup primitive?
+        IF   ." PRI:  "
+        ELSE ." SEC:  "
+        THEN
+        cl.dump.name
+    ELSE
+        drop
+    THEN
+;
+
+\ ------------------------------------------------------------------
+: CL.REF[] ( index -- clref )
+    sizeof() cl.reference *
+    cl-references @ +
+;
+
+: CL.DUMP.REFS ( -- , print references )
+    cl-num-refs @ 0
+    DO
+        i 3 .r ."  : "
+        i cl.ref[]
+        dup s@ clr_OriginalXT >name id. ."  => "
+        dup s@ clr_NewXT .
+        ." , size = "
+        dup s@ clr_TotalSize . cr
+        drop \ clref
+    loop
+;
+
+: CL.XT>REF_INDEX { xt | indx flag -- index flag , true if found }
+    BEGIN
+\ cl.indent ." CL.XT>REF_INDEX - indx = " indx . cr
+        indx cl-num-refs @ >=
+        IF
+            true
+        ELSE
+            indx cl.ref[] s@ clr_OriginalXT
+\ cl.indent ." CL.XT>REF_INDEX - clr_OriginalXT = " dup . cr
+            xt  =
+            IF
+                true
+                dup -> flag
+            ELSE
+                false
+                indx 1+ -> indx
+            THEN
+        THEN
+    UNTIL
+    indx flag
+\ cl.indent ." CL.XT>REF_INDEX - " xt >name id. space  indx . flag . cr
+;
+
+: CL.ADD.REF  { xt | clref -- , add referenced secondary to list }
+    cl-references @ 0= abort" CL.ADD.REF - References not allocated!"
+\
+\ do we need to allocate more room?
+    cl-num-refs @ cl-max-refs @ >=
+    IF
+        cl.resize.refs
+    THEN
+\
+    cl-num-refs @ cl.ref[] -> clref    \ index into array
+    xt clref s! clr_OriginalXT
+    0 clref s! clr_NewXT
+    xt xt>size clref s! clr_TotalSize
+\
+    1 cl-num-refs +!
+;
+
+\ ------------------------------------------------------------------
+
+\ called by cl.traverse.secondary to compile each piece of secondary
+: CL.RECOMPILE.SECONDARY { code-addr | xt clref dsize -- ,  }
+\ recompile to new location
+\ cl.indent ." CL.RECOMPILE.SECONDARY - enter - " .s cr
+    code-addr @ -> xt
+\ cl.indent ." CL.RECOMPILE.SECONDARY - xt = $" dup .hex dup >name id. cr
+    xt cl.dump.xt
+    xt primitive?
+    IF
+        xt cl,
+    ELSE
+        xt CL.XT>REF_INDEX
+        IF
+            cl.ref[] -> clref
+            clref s@ clr_NewXT
+            dup 0= abort" CL.RECOMPILE.SECONDARY - unresolved NewXT"
+            cl,
+        ELSE
+            cl.indent ." CL.RECOMPILE.SECONDARY - xt not in ref table!" cr
+            abort
+        THEN
+    THEN
+\
+\ transfer any literal data
+    code-addr codeaddr>datasize -> dsize
+    dsize 0>
+    IF
+\ cl.indent ." CL.RECOMPILE.SECONDARY - copy inline data of size" dsize . cr
+        code-addr cell+  cl-dict-ptr @ cl.dict[]  dsize  move
+        cl-dict-ptr @ dsize + aligned cl-dict-ptr !
+    THEN
+\ cl.indent ." CL.RECOMPILE.SECONDARY - leave - " .s cr
+;
+
+: CL.RECOMPILE.REF { indx | clref codesize datasize -- }
+\ all references have been resolved so recompile new secondary
+    depth >r
+    indx cl.ref[] -> clref
+    cl-trace @
+    IF
+        cl.indent
+        clref s@ clr_OriginalXT >name id. ."  recompiled at $"
+        cl-dict-ptr @ .hex cr    \ new address
+    THEN
+    cl-dict-ptr @  clref s! clr_NewXT
+\
+\ traverse this secondary and compile into new dictionary
+    clref s@ clr_OriginalXT
+    >code ['] cl.recompile.secondary cl.traverse.secondary
+\
+\ determine whether there is any data following definition
+    cl-dict-ptr @
+    clref s@ clr_NewXT - -> codesize \ size of cloned code
+    clref s@ clr_TotalSize \ total bytes
+    codesize - -> datasize
+    cl-trace @
+    IF
+        cl.indent
+        ." Move data: data size = " datasize . ." codesize = " codesize . cr
+    THEN
+\
+\ copy any data that followed definition
+    datasize 0>
+    IF
+        clref s@ clr_OriginalXT >code codesize +
+        clref s@ clr_NewXT cl-dict-base @ + codesize +
+        datasize move
+        datasize cl-dict-ptr +!  \ allot space in clone dictionary
+    THEN
+
+    depth r> - abort" Stack depth change in CL.RECOMPILE.REF"
+;
+
+\ ------------------------------------------------------------------
+: CL.SCAN.SECONDARY ( code-addr -- , scan word and add referenced secondaries to list )
+    depth 1- >r
+\ cl.indent ." CL.SCAN.SECONDARY - enter - " .s cr
+    cl-ref-level @ cl-level-max @  MAX cl-level-max !
+    @ ( get xt )
+\ cl.indent ." CL.SCAN.SECONDARY - xt = " dup . dup >name id. cr
+    dup cl.dump.xt
+    dup primitive?
+    IF
+        drop
+\ cl.indent ." CL.SCAN.SECONDARY - found primitive." cr
+    ELSE
+        dup CL.XT>REF_INDEX
+        IF
+            drop \ indx   \ already referenced once so ignore
+            drop \ xt
+        ELSE
+            >r \ indx
+            dup cl.add.ref
+            >code 'self cl.traverse.secondary   \ use 'self for recursion!
+            r> cl.recompile.ref    \ now that all refs resolved, recompile
+        THEN
+    THEN
+\ cl.indent ." CL.SCAN.SECONDARY - leave - " .s cr
+    depth r> - abort" Stack depth change in CL.SCAN.SECONDARY"
+;
+
+: CL.CLONE.XT ( xt -- , scan top word and add referenced secondaries to list )
+    dup primitive? abort" Cannot CLONE a PRIMITIVE word!"
+    0 cl-ref-level !
+    0 cl-level-max !
+    0 cl-num-refs !
+    dup cl.add.ref     \ word being cloned is top of ref list
+    >code ['] cl.scan.secondary cl.traverse.secondary
+    0 cl.recompile.ref
+;
+
+\ ------------------------------------------------------------------
+: CL.XT>NEW_XT ( xt -- xt' , convert normal xt to xt in cloned dict )
+    cl.xt>ref_index 0= abort" not in cloned dictionary!"
+    cl.ref[] s@ clr_NewXT
+;
+: CL.XT>NEW_ADDR ( xt -- addr , addr in cloned dict )
+    cl.xt>New_XT
+    cl-dict-base @ +
+;
+
+: CL.REPORT ( -- )
+    ." Clone scan went " cl-level-max @ . ." levels deep." cr
+    ." Clone scanned " cl-num-refs @ . ." secondaries." cr
+    ." New dictionary size =  " cl-dict-ptr @ cl-dict-base @ - . cr
+;
+
+
+\ ------------------------------------------------------------------
+: CL.TERM ( -- , cleanup )
+    cl.free.refs
+    cl.free.dict
+;
+
+: CL.INIT ( -- )
+    cl.term
+    0 cl-dict-size !
+    ['] first_colon cl-dict-ptr !
+    cl.alloc.dict
+    cl.alloc.refs
+;
+
+: 'CLONE ( xt -- , clone dictionary from this word )
+    cl.init
+    cl.clone.xt
+    cl.report
+    cl.dump.refs
+    cl-test-mode @
+    IF ." WARNING - CL-TEST-MODE on so we can't save cloned image." cr
+    THEN
+;
+
+: SAVE-CLONE  ( <filename> -- )
+    bl word
+    ." Save cloned image in " dup count type
+    drop ." SAVE-CLONE unimplemented!" \ %Q
+;
+
+: CLONE ( <name> -- )
+    ' 'clone
+;
+
+if.forgotten cl.term
+
+\ ---------------------------------- TESTS --------------------
+
+
+: TEST.CLONE ( -- )
+    cl-test-mode @ not abort" CL-TEST-MODE not on!"
+    0 cl.ref[] s@ clr_NewXT  execute
+;
+
+
+: TEST.CLONE.REAL ( -- )
+    cl-test-mode @ abort" CL-TEST-MODE on!"
+    code-base @
+    0 cl.ref[] s@ clr_NewXT  \ get cloned execution token
+    cl-dict-base @ code-base !
+\ WARNING - code-base munged, only execute primitives or cloned code
+    execute
+    code-base !   \ restore code base for normal
+;
+
+
+: TCL1
+    34 dup +
+;
+
+: TCL2
+    ." Hello " tcl1  . cr
+;
+
+: TCL3
+    4 0
+    DO
+        tcl2
+        i . cr
+        i 100 + . cr
+    LOOP
+;
+
+create VAR1 567 ,
+: TCL4
+    345 var1 !
+    ." VAR1 = " var1 @ . cr
+    var1 @ 345 -
+    IF
+        ." TCL4 failed!" cr
+    ELSE
+        ." TCL4 succeded! Yay!" cr
+    THEN
+;
+
+\ do deferred words get cloned!
+defer tcl.vector
+
+: TCL.DOIT ." Hello Fred!" cr ;
+' tcl.doit is tcl.vector
+
+: TCL.DEFER
+    12 . cr
+    tcl.vector
+    999 dup + . cr
+;
+
+trace-stack on
+cl-test-mode on
+