6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 8:01 am

All times are UTC




Post new topic Reply to topic  [ 87 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next
Author Message
PostPosted: Wed Aug 02, 2017 9:31 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
GARTHWILSON wrote:
True, but that's not a good example, because you can do
Code:
    BuzzCnt OFF
in two words (and two passes through NEXT) and get the same result.

Maybe I did choose a bad example. As you say, OFF works just as well if the the value to be stored is zero. But that won't always be the case -- I just grabbed that code snippet because it was used earlier.

To be clear,
Code:
!> BuzzCnt
... can follow any code sequence -- real work that actually computes something and puts the result on stack. And compared with the FIG way of doing things it avoids one pass through NEXT.

But perhaps my comment about readability is open to challenge. I'll grant it's possible to argue the waters are even further muddied, since we now have THREE alternatives. (I mean CONSTANT VARIABLE and VALUE)

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 02, 2017 11:17 pm 
Offline

Joined: Tue Jun 08, 2004 11:51 pm
Posts: 213
There are many ways to skin a cat.
Like I said, when I first saw VAULE, I didn't like it.
It was different. And as the other fellow said, it didn't seem
Forthish. Still, I've grown to like it.
I've also worked with enough different Forths to not worry so
much about making everything RPN.
I recall one Forth that even strings were handle RPN.
Code:

" MyNewWord" :
    ....
;

There was no forward parsing of the input stream. It made
everything RPNish. It was uncomfortable but worked.
One advantage was that you didn't have to dig into the guts
of Forth if you wanted to pass string to : inside of a :
definition. ( not sure why you'd want to do that )
Dwight


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2019 3:33 pm 
Offline

Joined: Wed Jan 08, 2014 3:31 pm
Posts: 578
Another classic CS-101 is Tower of Hanoi as an introduction to recursion. Forth does that with the recurse keyword, so I made this sample.

Code:
\ Move disk simply prints the move.
\ arguments unused dest_post source_post disk_number
: hanoi_move_disk ( n1 n2 n3 n4 -- )
   ." Move disk " . ." from " . ." to " . cr
   drop ;

\ Recursively moves sub-tower, a disk, and sub-tower
\ arguments spare_post dest_post source_post disk_number
: hanoi_move_tower ( n1 n2 n3 n4 -- )
   dup 0=
   if
      hanoi_move_disk ( needs all arguments except spare_post )
   else
      \ Push arguments dest_post, spare_post, source_post, disk_number - 1
      2over swap 2over 1-
      recurse

      \ Duplicate the stack and move the disk.
      2over 2over
      hanoi_move_disk

      \ Last call, consume arugments rather than duplicate them.
      \ Reformat the stack to contain source, dest, spare, disk - 1
      >r rot rot swap r> 1-
      recurse
   then ;

\ seeds the stack and calls moves tower
: hanoi_move_4 ( -- spare dest source disk )
   2 3 1 4
   cr
   hanoi_move_tower  ;


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2019 4:22 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
Have you profiled the return stack and data stack usage? I'm interested to see how many stack cells a "somewhat typical" recursion algorithm gobbles up while doing its thing.

@Dwight: I think the "all RPN" approach you described is elegant in its consistency, if nothing else, and that alone would probably be enough for me to embrace it.

_________________
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: Tue Jan 29, 2019 8:07 pm 
Offline

Joined: Wed Jan 08, 2014 3:31 pm
Posts: 578
barrym95838 wrote:
Have you profiled the return stack and data stack usage? I'm interested to see how many stack cells a "somewhat typical" recursion algorithm gobbles up while doing its thing.

This might be an embarrassing admission, but does Forth usually contain a run time trace and profile feature? I've never used one.

Reading my code, it looks like the recursion depth is equal to the number of disks, and each recursive call consumes four cells. So four disks would consume sixteen data stack cells, and four return stack cells. On my 6502 Forth system the both stacks are 128 bytes deep, so I think it could theoretically support a recursion depth of 32 before the stack would overflow.

But the Tower of Hanoi execution time gets rapidly out of hand as the number of disks grows. That's because it recursively calls itself twice for each level, so the number of moves equals 2^n - 1, where n is the number of disks. That's over four billion with 32 disks!


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2019 8:20 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8544
Location: Southern California
Martin_H wrote:
This might be an embarrassing admission, but does Forth usually contain a run time trace and profile feature? I've never used one.

This was one of the things I had in mind when I implemented an indirect jump to NEXT in my '816 Forth; but have never used the trace. The idea is that if you want to start/stop a trace, you just put the address of a different (more complex) version of NEXT in variable NEXTadr which you use as the address for the indirect jump. Then JMP NEXT becomes JMP (NEXTadr). You could invent versions of NEXT on the fly to meet special needs.

_________________
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: Tue Jan 29, 2019 9:38 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Just a reminder of a simple technique: if stack depth is all you need to know, then fill the stack areas with copies of a sentinel byte (zero will do; I use $69) before starting Forth and running the program. After the program has run, you can see how many of the sentinel bytes remain intact.

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2019 9:40 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
I like it!


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2019 10:23 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:
Martin_H wrote:
This might be an embarrassing admission, but does Forth usually contain a run time trace and profile feature? I've never used one.

This was one of the things I had in mind when I implemented an indirect jump to NEXT in my '816 Forth; but have never used the trace. The idea is that if you want to start/stop a trace, you just put the address of a different (more complex) version of NEXT in variable NEXTadr which you use as the address for the indirect jump. Then JMP NEXT becomes JMP (NEXTadr). You could invent versions of NEXT on the fly to meet special needs.

Blazin' Forth had TRACE as part of the system loader ( Blazin' Forth included a disk with the system loader in source form on Forth blocks). There was a code word >NEXT that took an address from the stack and patched NEXT with a jump to that address. The new NEXT would be something like:
Code:
   1 # LDY,
   IP )Y LDA,  W 1+ STA,  DEY,
   IP )Y LDA,  W    STA,
   <some.address> JMP,

There was also the code word NEXT> to restore NEXT .
Here is the code from the Blazin' Forth system loader.
Code:
SCR #19
 0 ( UTILITIES -- DEBUGGING: RP@ UNRAVEL ?PARAMS         SDBJUN85)
 1 CODE RP@ ( -- ADDR RETURN STACK 1- )
 2 XSAVE STX, TSX, TXA, PHA, 1 # LDA, XSAVE LDX, PUSH JMP, END-CODE
 3
 4 : ID? ( TRUE IF VALID NAME)
 5       DUP @ [ ' : @ ] LITERAL =
 6      SWAP @ C@ [ ' FORTH @ C@ ] LITERAL = OR ;

SCR #20
 0 ( UTILITIES -- DEBUGGING: ;NEXT NEXT> >NEXT           SDBJUN85)
 1 HEX
 2 VARIABLE 'TRACE   VARIABLE <IP  VARIABLE IP>
 3
 4 CODE ;NEXT ( EXIT THEN NEXT )
 5 ( EXIT) PLA, IP STA, PLA, IP 1+ STA,
 6 ( GET W) PLA, W STA, PLA, W 1+ STA,
 7 ( NEXT) CLC, IP LDA, 2 # ADC, IP STA, CS
 8   IF, IP 1+ INC, THEN, 0 # LDY, W 1- JMP, END-CODE
 9
10 CODE NEXT> ( RESTORE NEXT)
11  018 # LDA, NEXT 0B + STA, 0A5 # LDA, NEXT 0C + STA,
12  084 # LDA, NEXT 0D + STA, NEXT JMP, END-CODE
13
14
15 DECIMAL

SCR #21
 0 ( UTILITIES - DEBUGGING: >NEXT <IP> ID.L              SDBJUL85)
 1 HEX
 2 CODE >NEXT ( N -- POINT NEXT TO N)
 3  04C # LDA, NEXT 0B + STA, BOT LDA, NEXT 0C + STA,
 4  BOT 1+ LDA, NEXT 0D + STA, POP JMP, END-CODE
 5
 6 CREATE <IP>   ASSEMBLER
 7   <IP LDA, IP CMP, <IP 1+ LDA, IP 1+ SBC, CS NOT
 8     IF, IP> LDA, IP CMP, IP> 1+ LDA, IP 1+ SBC, CS
 9      IF, DEX, DEX, IP LDA, BOT STA, IP 1+ LDA, BOT 1+ STA,
10      W 1+ LDA, PHA, W LDA, PHA,
11      'TRACE LDA, W STA, 'TRACE 1+ LDA, W 1+ STA, W 1- JMP,
12     THEN, THEN, ' ;NEXT @ 0C + JMP, DECIMAL
13
14 : ID.L  ( CFA LEN -- )
15    OVER >NAME DUP ID. SPACE ROT 1- - + SPACES ;

SCR #22
 0 ( UTILITIES - DEBUGGERS: 'EXIT STEP TRACE CONT        SDBJUL85)
 1 : 'EXIT ( CFA -- LAST WORD)
 2    BEGIN 1+ DUP @ ['] EXIT = UNTIL ;
 3 VARIABLE CON
 4 : STEP ( CFA -- )
 5    NEXT>  CR @ 10 ID.L .S SPACE KEY CONTROL "{CONTROL P}" OVER = IF
 6     CON OFF DROP CR ." P? "
 7     BEGIN QUERY RUN CR ." P? " CON @ UNTIL
 8    THEN CONTROL "{RUN/STOP}" OVER = IF
 9     DROP CR ." TRACING OFF" QUIT
10    THEN DROP <IP> >NEXT ;NEXT ;  ' STEP 'TRACE !
11
12 : TRACE   NEXT> ' DUP ID? NOT ABORT" CAN'T TRACE"
13           DUP <IP !  'EXIT IP> !  <IP> >NEXT ;
14 : NOTRACE   NEXT>  ." TRACING OFF" QUIT ;
15 : CONT     CON ON 0 ;

I was able to get this to work with Fleet Forth, but I had to change one thing.
Code:
084 # LDA,

Was changed to:
Code:
IP # LDA,

Because Blazin' Forth's IP is at $84-$85 and Fleet Forth's is at $FB-$FC. Besides, using the constant for IP makes it a little more portable.
TRACE was followed by the name of a word to trace but did not initiate a trace, tracing occurred when IP was between two addresses stored in <IP and IP> so the word was traced whenever it was executed until tracing was turned off.
Blazin' Forth used ID? to determine if a word was one that could be traced. It used 'EXIT to find where the word to be traced ended. Since my decompiler does a better job at determining where a word ends and low level code in the trace range is harmless, I put TRACE in its own block and modified the load screen slightly:
Code:
SCR# 1
 0: ( BLAZIN' FORTH 85 SYSTEM LOADER        )
 1:  VIEW OFF
 2:  DECIMAL
 3:  CR 13     LOAD
 4:  CR 15  22 THRU .// UTILITIES
 5:  " SAD" FIND NIP 9 AND 23 LINELOAD
 6:
 7:  CR 50  57 THRU .// STRINGS
 8:  CR 58  70 THRU .// SID SUPPORT
 9:  CR 71 104 THRU .// VIC SUPPORT
10:
11:  VIEW ON    .( LOCATE ENABLED) CR

SCR# 23
 0: // TRACE
 1: DECIMAL
 2: : TRACE
 3:    NEXT>  ' DUP ID? NOT
 4:    ABORT" CAN'T TRACE"
 5:    DUP <IP !  'EXIT IP> !
 6:    <IP> >NEXT ;
 7: ;S
 8: // IF THE DECOMPILER IS AVAILABLE
 9: // HERE IS A BETTER TRACE
10: : TRACE
11:    NEXT>  SAD OFF  SEE
12:    SAD @ ?DUP 0=
13:    ABORT" CAN'T TRACE"
14:    2- <IP !  EAD @ IP> !
15:    <IP> >NEXT ;

;S stops loading of a block ( it will also stop interpretation/compilation of text from the keyboard). it is an alias for EXIT . It is actually a 'code word' with its CFA pointing to the body of EXIT , it has no body of its own.
SAD is a variable that holds the starting address of the latest decompiled word ( the address of its parameter field or body). EAD holds the ending address, or rather the address just past the end of the latest decompiled word.
Code:
EAD @ SAD @ - .

Gives the size of the latest decompiled word's body.
Oh and where there is a // , just think backslash ( \ ). The Commodore 64 has no backslash key.

[Edit: Fixed the source code. This source is from a print dump and the print dump functionality does not support the Commodore 64's quote mode. Where the source shows {CONTROL P} , press the C64's control key and the P key. Where it shows {RUN/STOP} , press the C64's run/stop key.]


Last edited by JimBoyd on Wed Feb 06, 2019 9:18 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 29, 2019 10:34 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8544
Location: Southern California
JimBoyd wrote:
Blazin' Forth had TRACE as part of the system loader ( Blazin' Forth included a disk with the system loader in source form on Forth blocks). There was a code word >NEXT that took an address from the stack and patched NEXT with a jump to that address.

That would be a little more efficient than mine, as it eliminates the indirect jump. The comparison of the overall efficiency might vary, because I also used mine to service interrupts in high-level Forth. If you had a lot of interrupts, the extra overhead of constantly patching and restoring the beginning part of NEXT would make for a performance hit.

_________________
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: Wed Jan 30, 2019 10:46 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:
JimBoyd wrote:
Blazin' Forth had TRACE as part of the system loader ( Blazin' Forth included a disk with the system loader in source form on Forth blocks). There was a code word >NEXT that took an address from the stack and patched NEXT with a jump to that address.

That would be a little more efficient than mine, as it eliminates the indirect jump. The comparison of the overall efficiency might vary, because I also used mine to service interrupts in high-level Forth. If you had a lot of interrupts, the extra overhead of constantly patching and restoring the beginning part of NEXT would make for a performance hit.

Huh? Patching NEXT is waaaay faster than I can type TRACE SOMEWORD .When a word is set to be traced, NEXT stays patched until NOTRACE is executed.
Suppose I have a word, BADWORD , that's not doing what I expect. I type TRACE BADWORD . The trace does not begin right away. Whenever BADWORD is executed, whether directly or from another word, the trace begins. The stack contents and the word to be executed are displayed and Forth waits for a keypress before resuming the trace. When I no longer wish to have BADWORD traced, I type NOTRACE and the trace is stopped.
A range of words can be traced by setting <IP to the low address of the trace range and setting IP> to the high address +1 of the trace range. For example:
Code:
NEXT>
HERE <IP !
<LOAD SOME NEW DEFINITIONS>
HERE IP> !
<IP> >NEXT

Will cause all the definitions just loaded to be traced whenever they are executed.


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 30, 2019 10:51 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
Oh wait! Do you mean if the code level ISR patched the beginning of NEXT to handle high level interrupts and the high level interrupt called the code to unpatch it?


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 30, 2019 11:15 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8544
Location: Southern California
Right, and for the Forth ISR's exit word to know which patch to put back in NEXT. Actually that part could go in a stack as well, so you could have more than one level of different NEXTs stored. Due to the kind of things I've used my workbench computer and other embedded systems for, I've very interrupt-conscious, and always looking for much faster interrupt performance than other users typically think in terms of. I have not had time to think about this much. Hopefully I can get back to it this year. You've had a ton of good posts I hope to be able to digest better in the future.

_________________
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 Feb 01, 2019 11:06 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:
Due to the kind of things I've used my workbench computer and other embedded systems for, I've very interrupt-conscious, and always looking for much faster interrupt performance than other users typically think in terms of.

Good thing you're using an indirect jump to NEXT if you have tracing capability. Blazin' Forth's TRACE slows down the system. The code NEXT is redirected to checks to see if IP is within a specific address range to determine if it should trace or not, and that takes time. It's worth it if you need to trace something though. 64Forth, which was a superset of FIG Forth, had a different TRACE . Any needed parameters were placed on the data stack and the sequence
Code:
TRACE SOME.WORD

was entered. The trace began immediately. This did not slow the system down because the trace took place right then and only then, but it was not the best way to perform a trace, if you ask me. There are two situations where the method used by Blazin' Forth is better. The first is when tracing words like (MAKE) , a high level word compiled by MAKE from Leo Brodie's DOER/MAKE . (MAKE) , like BRANCH and ?BRANCH , must be executed from within a Forth word. Here is a session showing a trace of (MAKE)
Code:
TRACE (MAKE)
(MAKE)
 A071 1205 R>
 A073 1422 DUP
 A075 126B 2+
 A077 1422 DUP
 A079 126B 2+
 A07B 13EA SWAP
 A07D 1313 @
 A07F 1A36 >BODY
 A081 1337 !
 A083 1313 @
 A085 1433 ?DUP
 A087  9F6 ?BRANCH A08D
 A08B 11F5 >R
 A08D  94F EXIT
 OK
RECITAL
YOUR DADDY IS STANDING ON THE TABLE.  ASK HIM 'WHY?'
R>         STACK EMPTY 
DUP         A14F 
2+          A14F  A14F 
DUP         A14F  A151 
2+          A14F  A151  A151 
SWAP        A14F  A151  A153 
@           A14F  A153  A151 
>BODY       A14F  A153  A101 
!           A14F  A153  A103 
@           A14F 
?DUP           0 
?BRANCH        0 
EXIT       STACK EMPTY   OK
WHY?
TO CHANGE THE LIGHT BULB.
R>         STACK EMPTY 
DUP         A171 
2+          A171  A171 
DUP         A171  A173 
2+          A171  A173  A173 
SWAP        A171  A173  A175 
@           A171  A175  A173 
>BODY       A171  A175  A101 
!           A171  A175  A103 
@           A171 
?DUP           0 
?BRANCH        0 
EXIT       STACK EMPTY 
WHY?
BECAUSE IT'S BURNED OUT.
R>         STACK EMPTY 
DUP         A192 
2+          A192  A192 
DUP         A192  A194 
2+          A192  A194  A194 
SWAP        A192  A194  A196 
@           A192  A196  A194 
>BODY       A192  A196  A101 
!           A192  A196  A103 
@           A192 
?DUP           0 
?BRANCH        0 
EXIT       STACK EMPTY 
WHY?
BECAUSE IT WAS OLD.
R>         STACK EMPTY 
DUP         A1AE 
2+          A1AE  A1AE 
DUP         A1AE  A1B0 
2+          A1AE  A1B0  A1B0 
SWAP        A1AE  A1B0  A1B2 
@           A1AE  A1B2  A1B0 
>BODY       A1AE  A1B2  A101 
!           A1AE  A1B2  A103 
@           A1AE 
?DUP           0 
?BRANCH        0 
EXIT       STACK EMPTY 
WHY?
BECAUSE WE PUT IT IN THERE A LONG TIME AGO.
R>         STACK EMPTY 
DUP         A1E2 
2+          A1E2  A1E2 
DUP         A1E2  A1E4 
2+          A1E2  A1E4  A1E4 
SWAP        A1E2  A1E4  A1E6 
@           A1E2  A1E6  A1E4 
>BODY       A1E2  A1E6  A101 
!           A1E2  A1E6  A103 
@           A1E2 
?DUP           0 
?BRANCH        0 
EXIT       STACK EMPTY 
NOTRACE TRACING OFF
WHY?
BECAUSE IT WAS DARK!
WHY?
BECAUSE IT WAS NIGHT TIME!!
WHY?
STOP SAYING WHY?
WHY?
BECAUSE IT'S DRIVING ME CRAZY.
WHY?
JUST LET ME CHANGE THIS LIGHT BULB!
CONSOLE  OK

The other situation is when you suspect SOME.WORD is not working right and you want to trace it. When executing SOME.WORD with the right parameters everything works fine. you execute BAD.WORD , which calls SOME.WORD and the trace begins. You see that SOME.WORD got called with some oddball parameters. Aha! Let's trace BAD.WORD.


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 06, 2019 9:44 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
A few posts back, I showed the source code for Blazin' Forth's TRACE utility. The source was a print dump of the listing of the blocks from Blazin' Forth's system loader disk. The print dump functionality does not support the C64's quote mode and the first instance of using Blazin' Forth's word CONTROL to get the ASCII value of the control-P key combination printed nothing. The next instance to get the ASCII value of the Run/Stop key printed a dot. The original post has been edited and here is a screen shot showing the source from the disk that was modified to support Fleet Forth. It uses ASCII" instead of CONTROL .
Attachment:
step source.png
step source.png [ 21.87 KiB | Viewed 4658 times ]

ASCII" is more efficient:
Code:
: ASCII"
   [COMPILE] ASCII ; IMMEDIATE

and used as:
Code:
ASCII" C"

Where C is one of the C64's reverse video control characters.
Here is Blazin' Forth's CONTROL
Code:
: CONTROL  ASCII " WORD 1+ C@ STATE @
           IF [COMPILE] LITERAL THEN ; IMMEDIATE

And it's used as:
Code:
CONTROL "C"


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 87 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 21 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: