6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Tue Jun 04, 2024 4:31 am

All times are UTC




Post new topic Reply to topic  [ 343 posts ]  Go to page Previous  1 ... 6, 7, 8, 9, 10, 11, 12 ... 23  Next
Author Message
PostPosted: Mon Oct 12, 2020 11:49 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864

In this post I would like to talk about Fleet Forth's DO LOOPs. Just a reminder, Fleet Forth is an ITC Forth for the C64.
Based on an idea from Garth Wilson, Fleet Forth's DO LOOP's place three cells on the return stack. There is no primitive (LEAVE) or (?LEAVE) . LEAVE and ?LEAVE are the primitives. LEAVE and ?LEAVE do not have an inline branch address. They use one of the DO LOOP parameters.
The three parameters placed on the return stack by (DO) and (?DO) are:
The address LEAVE uses to branch out of the loop is placed there first. This address is copied from an inline cell following (DO) or (?DO) .
The loop's limit.
The index.
LEAVE removes the index and limit from the return stack and discards them, it removes the address saved as one of the parameters and stores it in IP, effecting a branch out of the loop. In the event the loop runs to completion, (LOOP) ( or (+LOOP) ) exits the same way LEAVE does.

This short word:
Code:
: TEST
   DO
      CR I .
      LEAVE
   LOOP
;

compiles to this:
Code:
1000 (DO)
1002 1010  \ Address saved on return stack used to branch out of loop
1004 CR
1006 I
1008 .
100A LEAVE
100C (LOOP)
100E 1004  \ Address used to branch back to beginning of loop
1010 EXIT

Note that there is no branch address after LEAVE .
Why have (LOOP) behave so much like LEAVE instead of stepping IP over the branch address? With this version of DO LOOP's there are three reasons:
1. It is faster. Since all three DO LOOP parameters have to be pulled from the return stack anyway, storing the final parameter in IP is faster than adding the value 2 to IP.
2. It is smaller. LEAVE (LOOP) and (+LOOP) use the same code sequence to exit a DO LOOP. (+LOOP) branches into (LOOP) . LEAVE has no body, it's code field points into the appropriate location in the body of (LOOP) .
But wait! there is even more memory to be saved. EXIT also has no body and a code field that points at a later point in (LOOP) since EXIT pulls one cell from the return stack and stores it in IP.
3. Consistency. LEAVE ( and ?LEAVE ) always branch to the same address that (LOOP) and (+LOOP) go to when the loop terminates.

This behavior of LEAVE ( using an address saved as one of the DO LOOP parameters) allows some interesting possibilities. LEAVE normally can only exit the DO LOOP it is a part of. For example.
Code:

   DO
      <high level stuff>
      LEAVE
      <high level stuff>
   LOOP
   <LEAVE branches to here>


In this code fragment, LEAVE branches to the code right after LOOP's branch address as expected.
In this fragment:
Code:

   DO
      <high level stuff>
      DO
         <high level stuff>
         LEAVE
         <high level stuff>
      LOOP
      <LEAVE branches to here>
      <high level stuff>
   LOOP


LEAVE once again branches to the code right after the first LOOP's branch address, also as expected.

However, in this code fragment I introduce UNLOOP , a primitive that discards the loop parameters. It only discards the parameters. This makes it possible to leave nested loops by having an UNLOOP for each level of DO LOOP nesting, then the word EXIT to exit that word from within one or more DO LOOPS.
Code:

   DO
      <high level stuff>
      DO
         <high level stuff>
         UNLOOP UNLOOP EXIT  \ Exit this word from within a nested DO LOOP.
         <high level stuff>
      LOOP
      <high level stuff>
   LOOP


In this next example, UNLOOP is not used to allow EXITing a word with a DO LOOP, but to make it possible for LEAVE in the inner loop to branch out of the outer loop.
Code:

   DO
      <high level stuff>
      DO
         <high level stuff>
         UNLOOP LEAVE
         <high level stuff>
      LOOP
      <high level stuff>
   LOOP
   <LEAVE branches to here>


UNLOOP discards the inner DO LOOP's parameters and LEAVE uses the address from the outer DO LOOP's parameters to branch to right after the second LOOP's branch address.


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 16, 2020 11:50 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864

?LEAVE is not in the Forth-83 Standard. I was just checking the website for Forth 2012 and couldn't find it there either. I'm surprised it is not even an extension word ( unless I missed it somehow) given how useful it is.
In Fleet Forth it, like LEAVE , is a primitive, but it is semantically equivalent to:
Code:
IF  LEAVE  THEN

but smaller and faster.
Fleet Forth's LEAVE is semantically equivalent to:
Code:
2R> 2DROP EXIT

Interestingly, this version of LEAVE ( and ?LEAVE ) will work outside a DO LOOP. If used in a word, it will exit not just that word, but the word that called that word and the word that called that one. I haven't found a use for it yet, but anywhere this is used:
Code:
... 2R> 2DROP EXIT ...

Fleet Forth's version of LEAVE can be used:
Code:
... LEAVE ...

The same for ?LEAVE . This:
Code:
... IF  2R> 2DROP EXIT  THEN ...

can be replaced with:
Code:
... ?LEAVE ...

I realize it's not portable to any Forth system which uses an inline branch address for LEAVE and ?LEAVE , but the equivalent code can be placed in a comment to show what is happening and provide portable alternate source while the faster and smaller LEAVE or ?LEAVE is used.
P.S. Given my enjoyment with Forth experimentation, I wish circumstances had been different and I had a chance to participate in the FORML conferences.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 10, 2020 2:22 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
In another thread,
GARTHWILSON wrote:
Jim, you have quite a few words above that are neither part of any standard I know of, nor defined above. One I'll ask about however is REDEFINE:. It appears to edit the old word to redirect execution to the new one, for secondaries that are already compiled using it, so those secondaries don't need to be recompiled.. Is that what's happening? I've had a way to do that but I like yours more.


I haven't written a word to implement this, but I implemented the idea in my metacompiler. I make use of >FORTH since it is already in Fleet Forth.

In my metacompiler, the word LITERAL needs to behave differently. The word MLITERAL is for metacompiling literals. With the older version of the metacompiler, I redefined each word that used LITERAL. This is somewhat error prone as I could miss one or miss a word that is dependent on one of the redefinitions.
MLITERAL was a colon definition ( a secondary) . It is now a code word that transitions to high level. Here is it's definition:
Code:
HEX
CODE MLITERAL  ( N -- )
   >FORTH
   DUP 100 U<
   IF
      MCOMPILE CLIT  C,-T
      EXIT
   THEN
   MCOMPILE LIT  ,-T ;

Since Fleet Forth is an ITC Forth, all words have a code field.
LITERAL is redirected to perform the function of MLITERAL by altering its code field.
Code:
: META.ON
   ['] M,
   ['] INTERPRET I.OFFSET + !
   ['] MLITERAL @ ['] LITERAL ! ;
: META.OFF
   ['] ,
   ['] INTERPRET I.OFFSET + !
   ['] : @ ['] LITERAL ! ;

Since this is from the source for my metacompiler, there will be some non-standard words. For this discussion, the important thing to note is this phrase in META.ON
Code:
   ['] MLITERAL @ ['] LITERAL !

patches LITERAL to perform the function of MLITERAL.
This phrase in META.OFF
Code:
   ['] : @ ['] LITERAL !

fetches the value from colon's code field ( though it could have been any colon definition) and stores it in the code field of LITERAL, resetting LITERAL to it's former functionality.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 10, 2020 2:48 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
JimBoyd wrote:

barrym95838 wrote:
Have you considered changing your PUSH to PUSHAY?

I've looked over the source for my kernel and I though I saw where PUSHAY would make some things smaller and faster. When I looked on the forum, I found out that PUSHAY pushes the accumulator to the high byte and the Y index register to the low byte. I had it the other way around! I was thinking low byte high byte order and the A for accumulator is before the Y for Y index register. I also called it AYPUSH ( I'd forgotten it was called PUSHAY).


There are some words that need PUSH, or at least need to save the accumulator and the X and Y registers are not available ( Y indexing and data stack access). The version of my kernel I wrote with AYPUSH falling through to NEXT was larger, so I went back to the version with PUSH falling through to NEXT .
I also have AYPUSH and AYPUT ( as well as APUSH ). They are in (FIND) .
Code:
            LABEL PUSH.ONE  //  1
            1 # LDA,
            LABEL APUSH
            0 # LDY,
            LABEL AYPUSH
            DEX,  DEX,
            LABEL AYPUT
            1 ,X STY,  PUT 3 + JMP,

and the location in PUT
Code:
   LABEL PUT
   1 ,X STA,  PLA,  0 ,X STA,
   LABEL NEXT
   <code for NEXT>

Here are the constants my Forth assembler supports:
Code:
  85 CONSTANT N   8D CONSTANT XSAVE
  8E CONSTANT UP  FB CONSTANT IP
  FE CONSTANT W
      83C CONSTANT PUSH
      843 CONSTANT NEXT
      86E CONSTANT SETUP
      889 CONSTANT POPTWO
  PUSH 2+ CONSTANT PUT
POPTWO 2+ CONSTANT POP
 ' 1 @ 2+ CONSTANT APUSH
 APUSH 2+ CONSTANT AYPUSH
AYPUSH 2+ CONSTANT AYPUT



Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 11, 2020 11:09 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864

I've made a slight change to Fleet Forth's word -NUMBER . I've added about 44 bytes of code to check the beginning of a numeric string for one of the following three characters: # $ % .
For # base is temporarily set to decimal.
For $ base is temporarily set to hexadecimal.
For % base is temporarily set to binary.
After conversion, the value of base is restored to what it was prior to the execution of -NUMBER .
If one of these three characters is not the first character of the numeric string to be converted, the number base is unaltered.
I just build this version of Fleet Forth yesterday, so I don't know how useful this change will be. Does anyone have any experience with this?


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 12, 2020 12:51 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8453
Location: Southern California
I like the easy base specification. I don't think I've seen it done in Forth. Doing [ DECIMAL ] and [ HEX ] and [ BINARY ] was so common in my code and taking so much space in the source code that I made IMMEDIATE words [D] and [H] and [B]. The I/O in my kind of work is mostly not human-oriented, so I normally keep the base at hex, not decimal; and after going to a non-hex base, I normally try to keep the return-to-hex word either directly to the right or directly below the other word, to make it easy to find. For example:
Code:
: SYNCSERSETUP   ( -- )
   [B]          VIA1ACR  C@
   00010100 OR                      \ Enable SR to shift out under T2 ctrl w/
   11010111 AND VIA1ACR  C!         \ µP resetting T2, and decr at the φ2 rate.
   00100100     VIA1IER  C!         \ Disable SR-empty and T2 IRQs.
   00001000     VIA1T2CL C!         \ Set T2 for about 100kHz shift rate, low
   [H]          VIA1T2CH C_OFF  ;   \ byte 1st. T2 gets reset automatically;
                                    \ so it's not really 1-shot in SR mode.
Here the [H] is directly below the [B]. I try to take maximum advantage of visual factoring (like your column of CONSTANTs above). I do not however put the ; on the margin, because ; and : look too much alike at first glance. I don't put anything on the margin that will be encountered when COMPILER is on. In a sequence of definitions, I try to align the ; for example:
Code:
: ESC     ( -- )  1B  EMIT       ;
: OUT-2   ( -- )   2  OUT -!     ;

: BOLDON  ESC  ." E"  OUT-2      ;    \ Put printer in bold mode.
: BOLDOFF ESC  ." F"  OUT-2      ;    \ Take printer out of bold.
: TINYON  0F EMIT     OUT  DECR  ;    \ Put printer in compressed.
: TINYOFF 12 EMIT     OUT  DECR  ;    \ Take printer out of compressed.
: ITALON  ESC  ." 4"  OUT-2      ;    \ Put printer in italics mode.
: ITALOFF ESC  ." 5"  OUT-2      ;    \ Cancel printer italics mode.
or
Code:
: TYPE      ( addr len -- )
   DUP OUT +!   type    ;

: CR        ( -- )
   0D emit   OUT OFF
   EOLSEQ C@  ?DUP
   IF emit THEN         ;

: SPACE     ( -- )
   BL  EMIT             ;

: SPACES    ( n -- )
   0 MAX    0
   ?DO  SPACE  LOOP     ;

I see Jim mostly does this, but others don't; so I'm just trying to encourage everyone else in the visual-factoring department.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Fri Nov 13, 2020 7:24 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 249
JimBoyd wrote:
For # base is temporarily set to decimal.
For $ base is temporarily set to hexadecimal.
For % base is temporarily set to binary.
After conversion, the value of base is restored to what it was prior to the execution of -NUMBER .
If one of these three characters is not the first character of the numeric string to be converted, the number base is unaltered.
This is part of the ANS-2012 standard (I checked the 94 and 83 versions and didn't see it mentioned there) in section 3.4.1.3. ANS-2012 also specifies the form 'c' which is for constant characters (rather than using CHAR or [CHAR]). I actually just stumbled onto this last week and realized that Tali doesn't have support for this so I opened an issue on github (and will probably end up submitting a patch for it, eventually).

Seeing as I might be the one to write it, I'm curious where you ended up temporarily putting the BASE value (return stack?) and how you signaled that BASE needed to be restored at the end?


Top
 Profile  
Reply with quote  
PostPosted: Fri Nov 13, 2020 10:56 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
SamCoVT wrote:
This is part of the ANS-2012 standard (I checked the 94 and 83 versions and didn't see it mentioned there) in section 3.4.1.3. ANS-2012 also specifies the form 'c' which is for constant characters (rather than using CHAR or [CHAR]).


Fleet Forth conforms to the Forth-83 Standard, which seems more appropriate considering when the C64 came out, and I like the Forth-83 Standard better. Since Fleet Forth conforms to the Forth-83 Standard, I didn't include the form 'c'. I didn't want to add that much extra code. I use ASCII , the immediate state smart word replaced by CHAR and [CHAR] in the new standard.
Since Fleet Forth conforms to the Forth-83 Standard, there are some differences in how numeric strings are handled. The leading count is ignored and the numeric string must have a trailing space.
Quote:
Seeing as I might be the one to write it, I'm curious where you ended up temporarily putting the BASE value (return stack?) and how you signaled that BASE needed to be restored at the end?

Yes, I put the value of BASE on the return stack, or rather, I used a word that did. Fleet Forth has the word RB (restore base). When RB is used in a word, whatever the base was prior to executing RB is restored when that word exits, unless it aborts. -NUMBER never aborts. It passes a flag, FALSE for successful conversion, TRUE if it failed.
RB uses the word CO (coroutine).
Code:
// COROUTINES
: CO   2R> SWAP 2>R ;
// RESTORE BASE AT END OF WORD
// CALLING RB
: RB  ( -- )
   BASE @ R> 2>R  CO
   R> BASE ! ;

Here is the original version of -NUMBER
Code:
: -NUMBER  ( ADR -- D FLAG )
   DPL ON  0 0 ROT
   DUP 1+ C@ ASCII - =
   DUP>R -  DUP>R
   BEGIN
      CONVERT DUP C@ VALID?
   WHILE
      DUP 1+ C@ VALID? 0=
   WHILE
      DPL OFF
   REPEAT
   THEN
   DUP C@ BL <> SWAP
   2-  DPL @ 0< - R> = OR
   R>  0EXIT  >R DNEGATE R> ;

Here is the modified version of -NUMBER
Code:
HEX
NH
CREATE BASE.TABLE
   0A C, 10 C, 2 C,

: -NUMBER  ( ADR -- D FLAG )
   RB
   DUP 1+ C@ ASCII # - DUP 3 U<
   IF
      BASE.TABLE + C@ BASE !
      1+ DUP
   THEN
   DROP
   DPL ON  0 0 ROT
   DUP 1+ C@ ASCII - =
   DUP>R -  DUP>R
   BEGIN
      CONVERT DUP C@ VALID?
   WHILE
      DUP 1+ C@ VALID? 0=
   WHILE
      DPL OFF
   REPEAT
   THEN
   DUP C@ BL <> SWAP
   2-  DPL @ 0< - R> = OR
   R>  0EXIT  >R DNEGATE R> ;

NH is a metacompiler word that causes the following word to be compiled without a header.
VALID? is a word that takes a character and returns a true flag if it is valid punctuation for a number. It is a deferred word and the default is to only accept a period as valid.
For completeness, here is the source for CONVERT
Code:
: CONVERT  ( D1 ADR1 -- D2 ADR2 )
   1+
   BEGIN
      COUNT BASE @ DIGIT
   WHILE
      2SWAP BASE @ *
      SWAP BASE @ UM* D+
      ROT
      DPL @ 0< 1+ DPL +!
   REPEAT
   1- ;

DIGIT takes the character to be converted and the number base to use. It returns the digit as a single cell number and a true flag or just a false flag if the character is not a digit in the specified base.
( CHR #BASE -- N TF )
( CHR #BASE -- FF )


Top
 Profile  
Reply with quote  
PostPosted: Sat Nov 14, 2020 1:51 am 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 249
JimBoyd wrote:
Fleet Forth conforms to the Forth-83 Standard, which seems more appropriate considering when the C64 came out, and I like the Forth-83 Standard better. Since Fleet Forth conforms to the Forth-83 Standard, I didn't include the form 'c'.
Are the # $ and % forms in the 83 standard? I couldn't find them. I guess what I meant to say is that I've only seen those forms mentioned in the 2012 standard. It does seem useful to me, though, to be able to specify constants in regular Forth code without having to switch the base. I've definitely been bit several times when my 10 was 16 and #10 could have been useful!
JimBoyd wrote:
Yes, I put the value of BASE on the return stack, or rather, I used a word that did. Fleet Forth has the word RB (restore base). When RB is used in a word, whatever the base was prior to executing RB is restored when that word exits, unless it aborts. -NUMBER never aborts. It passes a flag, FALSE for successful conversion, TRUE if it failed.
RB uses the word CO (coroutine).
Code:
// COROUTINES
: CO   2R> SWAP 2>R ;
// RESTORE BASE AT END OF WORD
// CALLING RB
: RB  ( -- )
   BASE @ R> 2>R  CO
   R> BASE ! ;
... because of course your forth has coroutines! That's pretty nifty, and I may have to play around with that.

I will probably just toss the BASE on the return stack because the number processing is written in assembly in Tali2. I think your method of just always tossing the original BASE on the return stack and restoring it regardless of what happened is the way to go. That way I won't need a flag to remember if I need to restore it later or not.

Your trick with the BASE.TABLE is pretty slick. I did not notice before that #, $, and % are all right next to each other in the ASCII table. I was looking through your code trying to see where you compared to those characters and I only saw #. Then I saw you looking up in the table with an index and I had to go pull up an ASCII table. Very nice.

Thanks for the detailed response. It's neat to see how other people tackle a problem. The other neat thing (for me) is that I was able to read/understand your Forth code. When I first starting playing around with Forth, reading other people's code was quite difficult for me. I'll also give a shout out to Garth's suggestions for making things visually clear with indentation and spacing.


Top
 Profile  
Reply with quote  
PostPosted: Sat Nov 14, 2020 3:31 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
SamCoVT wrote:
Are the # $ and % forms in the 83 standard?


No. I guess you could say I added them as a non standard extension.
Quote:
It does seem useful to me, though, to be able to specify constants in regular Forth code without having to switch the base. I've definitely been bit several times when my 10 was 16 and #10 could have been useful!

Exactly. It's a convenience that shouldn't have much impact on portability with, in my case, other Forth-83 systems.
Quote:
Your trick with the BASE.TABLE is pretty slick. I did not notice before that #, $, and % are all right next to each other in the ASCII table. I was looking through your code trying to see where you compared to those characters and I only saw #. Then I saw you looking up in the table with an index and I had to go pull up an ASCII table. Very nice.

Thank you. I didn't notice that #, $, and % are next to each other either, until I looked at their values in binary. I was trying to find a way to manipulate the numbers that would yield a small solution before settling on using the table.
Quote:
Thanks for the detailed response. It's neat to see how other people tackle a problem. The other neat thing (for me) is that I was able to read/understand your Forth code. When I first starting playing around with Forth, reading other people's code was quite difficult for me. I'll also give a shout out to Garth's suggestions for making things visually clear with indentation and spacing.

Poorly formatted source only contributes to Forth's write only reputation. The following is a sample of source code for another Forth for the C64. It's not for the multitasker (this Forth didn't have one), it does the same thing as Fleet Forth's DONE? .
Code:
: PAUSE   ?KEY ?DUP IF 3 <> IF KEY 3 = ELSE TRUE THEN
                    ELSE FALSE THEN ;

?KEY returns a zero if no key was pressed or the value of the key that was pressed.
KEY waits for a keypress and returns the value of that press. It calls ?KEY in a loop.
I don't care that this was from source that was in Forth blocks, it's not worth saving blocks if the control flow is difficult to follow.
Code:
: PAUSE  ( -- F )
   ?KEY ?DUP
   IF
      3 <>
      IF  KEY 3 =  ELSE  TRUE  THEN
   ELSE
      FALSE
   THEN ;

This version of formatting is easier to follow and, I think, easier to improve (hint: TRUE , FALSE , and both ELSEs are not needed in the improved version).


Top
 Profile  
Reply with quote  
PostPosted: Sat Nov 14, 2020 5:00 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8453
Location: Southern California
JimBoyd wrote:
No. I guess you could say I added them as a non standard extension.

As long as things don't conflict, it's nice when you can accommodate multiple standards. The only drawback is that it takes more memory.

Quote:
Your trick with the BASE.TABLE is pretty slick. I did not notice before that #, $, and % are all right next to each other in the ASCII table.

I hadn't noticed that either.

Quote:
The other neat thing (for me) is that I was able to read/understand your Forth code. When I first starting playing around with Forth, reading other people's code was quite difficult for me. I'll also give a shout out to Garth's suggestions for making things visually clear with indentation and spacing.

Forth has been called a write-only language, ie, that it's unreadable. I blame that on the programmer though, not the language. I had an article in Forth dimensions in the early 90's on making Forth more readable. Hopefully my own programming has further improved since then. My article-writing definitely has. To be frank, most people's assembly language on 6502.org is also very difficult to read, and I often have to copy it to my text editor and massage it some to figure out what they're trying to do.

Quote:
The following is a sample <snip>

The first sample follows what so many Forthers are dogmatic about, that definitions should be limited to two lines. This kind of dogma hurts readability. It tends to result in over-factoring too, taking more memory for all the unnecessary headers for factors that are only used once, more time to run because of the extra nesting, and it can be hard sometimes to come up with a descriptive name for the factor that is any shorter than the code it replaces! There was a regular contributor to Forth Dimensions magazine who defended this strongly, and said screens should be viewed as 3x5 cards. He kept pushing this as a plus, yet I just saw it as inadequate room to make things clear and add adequate comments. Then in a private mail exchange (which today we would do by email, but back then it was on paper), he sent me his code for something we were discussing, and he had a definition that was over 100 lines long! I didn't call out his hypocrisy.

Quote:
(hint: TRUE , FALSE , and both ELSEs are not needed in the improved version).

In the two-line version it's particularly easy to miss the fact that those could be eliminated to improve both readability and efficiency.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sat Nov 14, 2020 5:37 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1933
Location: Sacramento, CA, USA
Here's my clumsy stab at it:
Code:
: PAUSE  ( -- F )
   ?KEY DUP IF
      DUP 3 <> IF
         DROP KEY THEN
      3 = THEN
;
Am I in the right ballpark?
[Edited to try to balance the stack, probably while Garth was reading my first attempt ...]

P.S. eFORTH has the stack comment for ?KEY as ( -- c T | F ) , so in that case I would get rid of my second DUP ... at least I think that's what I'd try ... eh, I'm all messed up! I'll try again when my head's screwed on the right way!

P.P.S. I think I would have to DROP the T, like so:
Code:
: PAUSE  ( -- F )
   ?KEY DUP IF
      DROP DUP 3 <> IF
         DROP KEY THEN
      3 = THEN
;
... but that doesn't seem any better than the original! Back to the drawing board!

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sun Nov 15, 2020 8:29 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 864
barrym95838 wrote:
Here's my clumsy stab at it:
Code:
: PAUSE  ( -- F )
   ?KEY DUP IF
      DUP 3 <> IF
         DROP KEY THEN
      3 = THEN
;
Am I in the right ballpark?


Yes, but I like the factoring where both THEN's are one after the other followed by the semicolon. I'll show why.
Quote:
P.S. eFORTH has the stack comment for ?KEY as ( -- c T | F )

In Fleet Forth it's ( -- c | 0 ) because that is what the C64 kernal supports.
PAUSE is the name of Fleet Forth's task switcher (which is set to a no-op when not multitasking), so I'm just going to call this word DONE? , which is what it is called in Fleet Forth.
Code:
: DONE?  ( -- F )
   ?KEY ?DUP
   IF
      3 <>
      IF  KEY 3 =  ELSE  TRUE  THEN
   ELSE
      FALSE
   THEN ;

Notice the ELSE clause at the end of the word?
Code:
ELSE
   FALSE
THEN ;

If ?DUP is replaced with DUP , that clause is no longer needed.
Code:
: DONE?  ( -- F )
   ?KEY DUP
   IF
      3 <>
      IF  KEY 3 =  ELSE  TRUE  THEN
   THEN ;

In my ITC Forth, this saves 3 cells.
The next improvement is a little harder to see.
3 <> is equivalent to 3 = 0=
At first glance, this doesn't seem to save memory. if ?DUP is inserted between = and 0= the final ELSE clause is no longer needed.
Code:
: DONE?  ( -- F )
   ?KEY DUP
   IF
      3 = ?DUP 0=
      IF  KEY 3 =  THEN
   THEN ;

A savings of another cell.
Fleet Forth also has the primitives ?EXIT and 0EXIT .
Both consume the top stack item.
?EXIT exits the word if the top stack item is TRUE (any non-zero value).
0EXIT exits the word if the top stack item is zero.
Using these two words, some more memory savings can be had, as well as a performance boost. Both IF's branch to the exit at the end of the word. They can both be replaced with 0EXIT and the THEN's removed.
Code:
: DONE?  ( -- F )
   ?KEY DUP
   0EXIT
      3 = ?DUP 0=
      0EXIT  KEY 3 = ;

A little reformatting.
Code:
: DONE?  ( -- F )
   ?KEY DUP 0EXIT
   3 = ?DUP 0= 0EXIT
   KEY 3 = ;

Finally, 0= 0EXIT can be replaced with ?EXIT
Code:
: DONE?  ( -- F )
   ?KEY DUP 0EXIT
   3 = ?DUP ?EXIT
   KEY 3 = ;



Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 16, 2020 4:31 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1933
Location: Sacramento, CA, USA
My way of thinking FORTH is brutally incomplete, but I did think of ?EXIT briefly. It's kind of like a conditionally executed RTS in the middle of a subroutine, efficient but a bit lacking in what I perceive to be "structured" technique. Come to think of it, I often break those techniques when I'm doing my own thing, so ...

My (probably flawed) reasoning on the subject of where to place my IFs and ELSEs and THENs is that I feel weird needlessly inserting line breaks between the data or code and the word immediately responsible for consuming or executing it. In my example, the 3 <> is immediately consumed by the following IF, and the DROP KEY is within the execution scope of the following THEN ... kind of an "object verb verb" "noun noun verb" thing, like Yoda tends to use in his sentences. As my understanding hopefully improves, I may start to see things more the way you and Garth format your code, but many miles to go still have I ... :lol:

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 16, 2020 5:16 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8453
Location: Southern California
Nothin' wrong with ?EXIT, as there's nothing pending on either stack like there would be with DO...LOOP (where you can remove the exit address, loop limit, and loop index with UNLOOP). Any of the structures will have stuff on the stack during compilation, but not necessarily at run time.

Putting the condition and flow-control words this way makes if very clear, and there's no visual searching:
Code:
: FOOBAR    ( n1 -- n2 f )
   <do stuff>
   <condition?>
   IF    ┌─────────────────┐
         │ <do this stuff> │
         └─────────────────┘
   ELSE  ┌─────────────────┐
         │ <do this stuff> │
         └─────────────────┘
   THEN
   <do more stuff>    ;

But if you want the IF on the same line with the condition so the line is more stack-autonomous, you can re-arrange it this way:
Code:
: FOOBAR    ( n1 -- n2 f )
   <do stuff>
   <condition?>          IF
   ┌─────────────────┐
   │ <do this stuff> │
   └─────────────────┘   ELSE
   ┌─────────────────┐
   │ <do this stuff> │
   └─────────────────┘   THEN
   <do more stuff>     ;

The latter works out really well if you have a lot of nested IF's, and no visual searching is necessary. (Color syntax highlighting is not only unnecessary when you do this, but totally undesirable, IMO.) Then you can put them one above the other instead of indenting and indenting and indenting way over to ridiculous extremes. You might end with a line of
Code:
   THEN  THEN  THEN  THEN  THEN

Some may turn up their nose at this and say if you're having to do that, then you're taking the wrong approach; but then their "fix" usually ends up overfactoring or doing something else that's just as undesirable.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 343 posts ]  Go to page Previous  1 ... 6, 7, 8, 9, 10, 11, 12 ... 23  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: