Page 2 of 3

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.