Page 2 of 3

Re: Self Modifying Code

Posted: Tue Nov 06, 2018 11:14 pm
by JimBoyd
GARTHWILSON wrote:
How 'bout a separate SMC stack? It wouldn't need much space.
Not a separate SMC stack, a general purpose auxiliary stack, the aux stack.
There could be words to move single values from the data stack to the aux stack and back.
analogous to >R and R> they could be called >A and A>.
There could also be words to move two cells between the data and aux stacks 2>A and 2A>.
Words to move control flow data from the control flow stack ( which on most implementations is probably just the data stack ) to the aux stack could be CS>A and A>CS.

Re: Self Modifying Code

Posted: Wed Jan 16, 2019 9:51 pm
by JimBoyd
I don't think SMC is as useful with high level code. The only example I could think of involves a deferred word's 'vector', for lack of a better word, setting the deferred word to another vector.
Case in point: Fleet Forth's (ABORT") , the word compiled by ABORT" , executes the word WHERE . WHERE shows the location of the error ( or tries to ). If loading a block causes an error, WHERE will try to load that block to show the error, causing yet another error ( recursively!)
One solution was to try something like the following:

Code: Select all

DEFER (WHERE)
: SHOW.WHERE
   ['] NOOP IS (WHERE)
   // SHOW THE LOCATION OF THE ERROR
   //
   //
;
: WHERE
   (WHERE)
   ['] SHOW.WHERE IS (WHERE) ;
(WHERE) is set to a no-op by SHOW.WHERE . If an error occurs when SHOW.WHERE is running, WHERE gets executed, but does nothing more than reset (WHERE) back to SHOW.WHERE so it's ready for the next error.
It was actually easier to take care of this with a flag variable like so:

Code: Select all

VARIABLE WHERE?  TRUE WHERE? !
: WHERE
   WHERE? @
   IF
      WHERE? OFF
      // SHOW THE LOCATION OF THE ERROR
      //
      //
   THEN
   WHERE? ON ;
So I'm not sure how useful self modifying high level Forth is. Does anyone have another example of high level Forth self modifying code?

Cheers,
Jim

Re: Self Modifying Code

Posted: Fri Mar 29, 2019 9:02 pm
by JimBoyd
What about manipulating the return stack to control program flow?
I was just reading Dynamically Structured Codes by M. L. Gassanenko. Does this count as self modifying code?

Re: Self Modifying Code

Posted: Sat Mar 30, 2019 3:20 am
by Dr Jefyll
JimBoyd wrote:
What about manipulating the return stack to control program flow?
I can supply an example, and (unsurprisingly) it's rather odd. In FIG Forth, there's one particular word which contains the remarkable sequence R> DROP :shock: The purpose is to unwind the Return Stack and thus exit a BEGIN AGAIN loop that's in progress one level higher.

As we know, the stuff between BEGIN and AGAIN would ordinarily keep happening forever. But in this case, somewhere between BEGIN and AGAIN a condition is tested, and eventually the mystery word is allowed to execute, causing top-of-R to be dropped. Because it's a colon definition, the mystery word concludes with SEMIS which of course invokes the un-nest sequence. And instead of un-nesting to the mystery word's caller (ie, the word with the BEGIN AGAIN loop), we un-nest to that word's caller.

You might wonder whether the BEGIN AGAIN shouldn't just be replaced with a BEGIN WHILE REPEAT but this particular situation doesn't seem amenable to properly structured conditionals, and the R> DROP is used as a GOTO of sorts! A similar need may arise in other situations; I don't suppose the FIG example is unique.

That concludes the summary. To be explicit I'll need to identify the situation, and that entails explaining some cleverness in a different department. The word containing R> DROP has a name one character long, and that character is an ascii Null -- $00. Since Null is unprintable, the FIG Glossary lists the word as X, and interested parties can find it under that pseudonym.

As part of Forth's startup, QUIT calls INTERPRET and INTERPRET is the word with the BEGIN AGAIN loop I mentioned. The loop uses -FIND to get fragments of input text one by one and either compile or execute them. When no more text is available, a fetch from the buffer yields only a null, which is the end-of-buffer marker. -FIND dutifully does a search attempting to find a word in the dictionary named null, and the search is successful! Unprintable/X/Null is executed, and it's a case of, "Scotty, beam me up!" :P INTERPRET ceases to be real, and suddenly we're back in QUIT.

-- Jeff

( QUIT is in Screen 54 of the FIG Forth source. INTERPRET is in Screen 52, and X is in Screen 45.)

Re: Self Modifying Code

Posted: Sun Mar 31, 2019 8:00 pm
by JimBoyd
Fleet Forth does something similar. When WORD parses the text stream , whether a block, the text input buffer, or a string, it places a counted string at here and appends a blank. when the text stream is exhausted, word paces a count of zero and no characters, it still appends a blank. The blank name is found and it is immediate. In Fleet Forth, the blank name has a code field that points to EXIT , but no body, making it an alias for EXIT . Why waste memory on a colon definition when an alias for EXIT will work?
What about the technique used by M. L. Gassanenko? What do you think of it?

Re: Self Modifying Code

Posted: Fri Apr 05, 2019 9:27 pm
by JimBoyd
I don't want to get hung up on assembler control flow workarounds, but since SCAN and SKIP were already mentioned, here is what I think is the best solution since I added an Auxiliary stack to Fleet Forth.

Code: Select all

SCR# 38 
// SCAN
HEX
CODE SCAN  ( AD1 N1 C -- AD2 N2 )
   0= # LDA,   // LOAD D0 ( BNE )
   HERE 1+ >A
   BAD STA,
   3 # LDA,  SETUP JSR,
   AHEAD,
   BEGIN,
      N 4 + INC,
      0= IF,  N 5 + INC,  THEN,
      N 2+ LDA,
      0= IF,  N 3 + DEC,  THEN,
      N 2+ DEC,
   CS-SWAP THEN,
      N 2+ LDA,  N 3 + ORA,

SCR# 39 
// SCAN SKIP
   0= NOT WHILE,
      N 4 + )Y LDA,  N EOR,  .A ASL,
   HERE A> !
   0= UNTIL,
   THEN,
   DEX,  DEX,
   N 4 + LDA,  0 ,X STA,
   N 5 + LDA,  1 ,X STA,
   N 2+ LDA,  PHA,
   N 3 + LDA,
   PUSH JMP,  END-CODE
CODE SKIP  ( A1 L1 C -- A2 L2 )
   0= NOT # LDA,   // LOAD F0 (BEQ)
   ' SCAN @ 2+ JMP,  END-CODE
Given the way my INTERPRET is defined ( with EXECUTE called by INTERPRET rather some word called by INTERPRET ) if I squeeze the code enough to fit both >A and A> on the same source screen I wouldn't even need to use the Auxiliary stack. It just wouldn't be portable.

Re: Self Modifying Code

Posted: Sun Jun 23, 2019 7:08 pm
by JimBoyd
Dr Jefyll wrote:
JimBoyd wrote:
What about manipulating the return stack to control program flow?
I can supply an example, and (unsurprisingly) it's rather odd. In FIG Forth, there's one particular word which contains the remarkable sequence R> DROP :shock: The purpose is to unwind the Return Stack and thus exit a BEGIN AGAIN loop that's in progress one level higher.
Is it as odd as this example from M. L. Gassanenko's paper Dynamically Structured Codes ?

Code: Select all

    : ENTER >R ; \ ( tcf-addr -- ) call the threaded code fragment at tcf-addr
    : SUCC COMPILE R@ COMPILE ENTER ; IMMEDIATE
    : FAIL COMPILE R> COMPILE DROP COMPILE EXIT ; IMMEDIATE
    : 1-10 ( --> i --- i --> ) \ generate numbers from 1 to 10
        0 BEGIN       1+ DUP 11 <
           WHILE      SUCC \ call the continuation, of type ( i -- i )
           REPEAT
           DROP
           FAIL ; \ exit the code fragment that contains the continuation
    : //2 ( i --> i --- i --> i ) \ filter even numbers
           DUP 2 MOD 0=
           IF     SUCC \ call the continuation, of type ( i -- i );
                       \ (in the case of //2 we could just exit)
           THEN
           FAIL ; \ exit the code fragment that contains the continuation
    : .even1-10 ( -- ) 1-10 //2 DUP . ;
.even1-10 yields:

Code: Select all

    .even1-10 2 4 6 8 10    ok

Re: Self Modifying Code

Posted: Tue Apr 14, 2020 7:32 pm
by JimBoyd
Fleet Forth has a word, WHERE , to show where an error occurred. If the value of BLK is zero, WHERE displays the string pointed to by TIB with the length stored in #TIB . If BLK is non zero, WHERE prints the block and line numbers and displays the text on that line. Since I/O is involved (if the block is not in memory) there is a possibility of WHERE causing another error which would lead to WHERE being called which would cause another error and so on.
The solution I went with looks like this:

Code: Select all

NH VARIABLE WHERE?  TRUE WHERE? !
: WHERE  ( -- )
   WHERE? @ IF
      WHERE? OFF
      // The main body of WHERE
            .
            .
            .
   THEN
   WHERE? ON ;
NH is a metacompiler directive that makes the following word headless.
If any error occurs while WHERE executes, WHERE does nothing but set the variable WHERE? to true.
Here's a solution involving self modifying code:

Code: Select all

HEX
: WHERE  ( -- )
   // SWITCH WHERE OFF
   ['] EXIT (IS) RECURSE
   // The main body of WHERE
         .
         .
         .
(IS) is the primitive compiled by IS and RECURSE compiles the CFA of the latest word so the phrase

Code: Select all

   ['] EXIT (IS) RECURSE
makes EXIT the first word in the body of WHERE , effectively switching it off. All error handling in Fleet Forth eventually goes through ABORT . A slight modification to ABORT switches WHERE back on. The original ABORT :

Code: Select all

: ABORT  ( -- )
   ERR SP! AP!
   QUIT ; -2 ALLOT
and the new version :

Code: Select all

: ABORT  ( -- )
   ERR SP! AP!  ['] LIT (IS) WHERE
   QUIT ; -2 ALLOT
WHERE becomes eight bytes smaller while ABORT becomes eight bytes bigger. No net loss or gain there. The headless variable WHERE? is no longer needed for a total savings of four bytes.
By the way, the word ERR is a deferred word to allow extending the error handling. It is normally set to the word NOOP , a no-op.

Re: Self Modifying Code

Posted: Wed Apr 15, 2020 2:26 am
by chitselb
I recall QUAN structures in MMSForth, a syntactically cleaner albeit nonstandard replacement for VARIABLE. The way it worked in your code was:

Code: Select all

quan foo
42 is foo
foo .
at foo .
: compiledfoo   ( -- )
   37 is foo   foo .    at foo . ;
Under the hood, FOO has three different code field addresses, and the words IS and AT are immediate words to select either the 'assignment CFA' or 'address-of CFA'. If neither IS nor AT prefixes FOO, then the 'value-of CFA' is used.

Re: Self Modifying Code

Posted: Mon Jun 15, 2020 7:54 pm
by JimBoyd
chitselb wrote:
I recall QUAN structures in MMSForth, a syntactically cleaner albeit nonstandard replacement for VARIABLE. The way it worked in your code was:

Code: Select all

quan foo
42 is foo
foo .
at foo .
: compiledfoo   ( -- )
   37 is foo   foo .    at foo . ;
Under the hood, FOO has three different code field addresses, and the words IS and AT are immediate words to select either the 'assignment CFA' or 'address-of CFA'. If neither IS nor AT prefixes FOO, then the 'value-of CFA' is used.
I was thinking that maybe instead of IS , use TO.
TO and AT would be used with QUAN's or any QUAN like word ( double QUAN's, floating point QUAN's , etc. ) and IS could still be used to set DEFERred words.

Re: Self Modifying Code

Posted: Tue Jun 16, 2020 8:48 pm
by JimBoyd
I was thinking that QUANs could be a replacement for variable if the default action is to return a QUAN's data's address ( like a variable ). TO could select the set value CFA and AT could select the fetch value CFA.

Code: Select all

QUAN FOO
AT FOO U.  \ get the data AT FOO
137 TO FOO \ send the data TO FOO
FOO U.     \ where is the data ( just like VARIABLE )
Here is some prototype code I wrote last night.

Code: Select all

SCR# 1076 
// QUAN -- CODE FOR CODE FIELDS
HEX
SUBR DO.VAR
   CLC,
   6 # LDA,  W ADC,  PHA,
   TYA,   W 1+ ADC,
   PUSH JMP,  END-CODE
SUBR TO.Q
   CLC,  DEX,  DEX,
   4 # LDA,  W ADC,  0 ,X STA,
   TYA,   W 1+ ADC,  1 ,X STA,
   ' ! @ JMP,  END-CODE
SUBR AT.Q
   ' BL @ JMP,  END-CODE

SCR# 1077 
// TO AT
HEX
// SET A QUAN
: TO ( N -- )  // PARSE TEXT STREAM
   2 ' DUP @ DO.VAR <>
   ABORT" NOT A QUAN"
   +  STATE @
   IF  , EXIT  THEN
   EXECUTE ; IMMEDIATE
// GET A QUAN'S VALUE
: AT  ( -- N )  // PARSE TEXT STREAM
   4 BRANCH [ ' TO >BODY 2+ , ] ;
   -2 ALLOT IMMEDIATE

SCR# 1078 
// QUAN
HEX
: CFA-ALIGN
   >IN @ BL WORD  // AVOID INDIRECT
   SWAP >IN !     // JUMP BUG IN
   COUNT + 1 AND  // NMOS 6510
   ALLOT ;        // PROCESSOR
: QUAN
   CFA-ALIGN CREATE -2 ALLOT
   DO.VAR ,  TO.Q ,  AT.Q ,
   0 , ;
;S
CREATE MAKES SURE THE FIRST CFA
DOES NOT STRADDLE A PAGE BOUNDARY.
CFA-ALIGN HANDLES THE OTHER TWO.

SCR# 1079 
// 2QUAN
HEX
SUBR TO.2Q
   CLC,  DEX,  DEX,
   4 # LDA,  W ADC,  0 ,X STA,
   TYA,   W 1+ ADC,  1 ,X STA,
   ' 2! @ JMP,  END-CODE
SUBR AT.2Q
   CLC,  DEX,  DEX,
   2 # LDA,  W ADC,  0 ,X STA,
   TYA,   W 1+ ADC,  1 ,X STA,
   ' 2@ @ JMP,  END-CODE
: 2QUAN
   CFA-ALIGN  CREATE -2 ALLOT
   DO.VAR , TO.2Q ,  AT.2Q ,
   0 , 0 , ;
With this behavior, QUAN could be renamed VARIABLE and 2QUAN could be renamed 2VARIABLE. As long as TO and AT were not used, these versions of VARIABLE and 2VARIABLE would behave just like the original versions.
I was thinking about removing the ABORT" in TO and have it ( and AT ) execute or compile the word's only CFA if the word is not a 'QUAN' .
[Edit: fixed incorrect stack comment. TO does not return an address, it parses the text stream for the next word.]

Re: Self Modifying Code

Posted: Wed Jun 17, 2020 8:54 pm
by JimBoyd
There are some things I neglected to mention in my previous post.
The defining word SUBR creates a variable with no space alloted, but it switches on the assembler. A word created with SUBR returns its address.
The word CFA-ALIGN is only needed for Forth's running on NMOS 6502's or 6510's.
That funny looking word ;S ends loading of a block ( it's an alias for EXIT ).
And since this is for a Commodore 64, just read \ ( backslash ) for each // ( double forward slash ).
Here is the code for new style variables ( VARs ) cleaned up a little:

Code: Select all


SCR# 1076 
// VAR -- CODE FOR CODE FIELDS
HEX
: DO.VAR  // PRIMARY CODE FIELD
   ;CODE
   CLC,
   6 # LDA,  W ADC,  PHA,
   TYA,   W 1+ ADC,
   PUSH JMP,  END-CODE
SUBR TO.VAR
   CLC,  DEX,  DEX,
   4 # LDA,  W ADC,  0 ,X STA,
   TYA,   W 1+ ADC,  1 ,X STA,
   ' ! @ JMP,  END-CODE
SUBR AT.VAR
   ' BL @ JMP,  END-CODE

SCR# 1077 
// TO AT
HEX
// SET A VAR
: TO ( N -- )
   ' DUP @ [ ' DO.VAR 4 + ] LITERAL
   <> ABORT" NO TO BEHAVIOR"
   2+  STATE @
   IF  , EXIT  THEN
   EXECUTE ; IMMEDIATE
// GET A VAR'S VALUE
: AT  ( -- N )
   ' DUP @ [ ' DO.VAR 4 + ] LITERAL
   <> ABORT" NO AT BEHAVIOR"
   2+ 2+  STATE @
   IF  , EXIT  THEN
   EXECUTE ; IMMEDIATE

SCR# 1078 
// VAR
HEX
: CFA-ALIGN
   >IN @ BL WORD  // AVOID INDIRECT
   SWAP >IN !     // JUMP BUG IN
   COUNT + 1 AND  // NMOS 6510
   ALLOT ;        // PROCESSOR
: VAR
   CFA-ALIGN CREATE 
   TO.VAR ,  AT.VAR ,
   0 ,  DO.VAR ;
;S
CREATE MAKES SURE THE FIRST CFA
DOES NOT STRADDLE A PAGE BOUNDARY.
CFA-ALIGN HANDLES THE OTHER TWO.

SCR# 1079 
// 2VAR
HEX
SUBR TO.2VAR
   CLC,  DEX,  DEX,
   4 # LDA,  W ADC,  0 ,X STA,
   TYA,   W 1+ ADC,  1 ,X STA,
   ' 2! @ JMP,  END-CODE
SUBR AT.2VAR
   CLC,  DEX,  DEX,
   2 # LDA,  W ADC,  0 ,X STA,
   TYA,   W 1+ ADC,  1 ,X STA,
   ' 2@ @ JMP,  END-CODE
: 2VAR
   CFA-ALIGN  CREATE
   TO.2VAR ,  AT.2VAR ,
   0 , 0 ,  DO.VAR ;
Since ANS Forth uses TO to set the data of a VALUE, if one wanted to ad ANS Forth style VALUEs to Forth:

Code: Select all

: VALUE
   CONSTANT ;CODE
   ' BL @ JMP,
END-CODE
: TO   
   ' DUP VALUE-CFA <> // TEST IF NOT A VALUE
   IF   [COMPILE] TO EXIT  THEN
   // PERFORM VALUE RELATED BEHAVIOR
; IMMEDIATE
[Edit: I made some mistakes when changing the code. Sorry, I was away from my desktop. I've corrected the errors. Hopefully. I'm fighting with a weak data connection.]

Re: Self Modifying Code

Posted: Tue Jan 19, 2021 12:06 am
by JimBoyd
JimBoyd wrote:
I was thinking that QUANs could be a replacement for variable if the default action is to return a QUAN's data's address ( like a variable ). TO could select the set value CFA and AT could select the fetch value CFA.

At one time I thought that this behavior ( the default action being that of a VARIABLE rather than that of a VALUE would be the only easy way to tell if a word being parsed by TO or AT was a multi code field word. I no longer feel this is necessary.
I just realized that a clever implementation would allow TO and AT to fetch the address of the first code field, to get to the parent word, and backup to find if there is a word unique to multi code field defining words ( even if that word is a no-op ). This word's address could be the flag used to tell if a word is a multi code field word.

Re: Self Modifying Code

Posted: Tue Mar 02, 2021 1:05 am
by JimBoyd

I'm not sure if this would qualify as self modifying code. The following word modifies itself without modifying the running code.
Fleet Forth has a word CHAR that is used by the following:

Code: Select all

.(  (  ,"  "
.( ABC)  displays the string ABC
( ABC)  comment -- discards the string
," ABC"  compiles the string ABC
" ABC"   If interpreting, stores ABC as a counted string at PAD
         If compiling, compiles helper word (") and ABC as a counted string

CHAR takes the ASCII code for a character to use as a delimiter and parses the text stream until that delimiter is found. It returns the address and count of the string that was parsed. CHAR aborts with the following message if the delimiter was not found:
'X' MISSING
Where 'X' is the delimiter used. For example:

Code: Select all

.( THIS IS A TEST
^^
) MISSING

This is the most streamlined version I had:

Code: Select all

: CHAR  ( C -- ADR CNT )
   DUP>R HERE C!
   'STREAM 2DUP R>
   SCAN DUP 0=
   IF
      WHERE
      CR HERE C@ EMIT ."  MISSING"
      ABORT
   THEN
   ADJUST ;

This version is 13 bytes smaller

Code: Select all

: CHAR  ( C -- ADR CNT )
   DUP>R LIT [ HERE >A 0 , ] C!
   'STREAM 2DUP R>
   SCAN DUP 0=
   [ HERE 3 + A> ! ]
   ABORT"   MISSING"
   ADJUST ;

CHAR modifies the inline text used by (ABORT") , which is compiled by ABORT" , by storing the ASCII code for the delimiter at the address of the first character of the string.

Re: Self Modifying Code

Posted: Sat Oct 29, 2022 12:38 am
by JimBoyd
JimBoyd wrote:
What about manipulating the return stack to control program flow?
I was just reading Dynamically Structured Codes by M. L. Gassanenko. Does this count as self modifying code?

Since the actual code does not get modified, I have concluded that this is not self modifying code.