6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Nov 21, 2024 6:51 pm

All times are UTC




Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 13, 14, 15, 16, 17, 18, 19 ... 24  Next
Author Message
PostPosted: Sat Jun 25, 2022 2:34 am 
Offline

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

I've changed the way WORD is defined in Fleet Forth.
Both versions use 'STREAM to obtain the address of the current position in the text stream and the remaining length. Here is the latest definition of 'STREAM .
Code:
: 'STREAM  ( -- ADR N )
   BLK @ ?DUP
   IF
      BLOCK B/BUF
   ELSE
      TIB #TIB @
   THEN
   >IN @
   OVER UMIN /STRING ;

Both use SKIP to skip leading occurrences of the delimiter and SCAN to scan for the delimiter at the end of the parsed text.
The new version of Fleet Forth's WORD is different in how it adjusts >IN . The old WORD saved the length returned by 'STREAM , the address returned by SKIP , and the address and length returned by SCAN . They were passed to the headerless word ADJUST in this order: address returned by SKIP , length returned by 'STREAM , address returned by SCAN , and length returned by SCAN . ADJUST calculated how far to move >IN without making it point further than one byte past the text stream. Even though ADJUST was headerless, it did have a name in the source used by the metacompiler.
For the old and new versions of WORD , as well as CHAR , when the source is a block, WORD will never leave >IN with a value larger than B/BUF ( 1024), otherwise >IN will never be left with a value larger than that in #TIB . Although >IN can be manually set to a larger value, doing so causes 'STREAM to return a size of zero for the remaining length of the text stream.

The new version of Fleet Forth's WORD does away with the headerless word ADJUST . The length returned by 'STREAM is added to >IN , pushing it just past the text stream. The length remaining returned by SCAN is decremented and the maximum of it and zero is subtracted from >IN .

The change to CHAR also did away with the need for ADJUST . CHAR takes a delimiter and returns an address and length for the parsed text. CHAR does not skip leading delimiters and the text stream can not be exhausted without an error condition since the delimiter must be found. The only place CHAR alters >IN is where the returned length plus one is added to >IN .
Here are the old versions of WORD and CHAR .
Code:
NH
2VARIABLE HISTORY
NH
: ADJUST  ( A1 L1 A2 L2 -- A1 CNT )
   DUP 0<> + NEGATE ROT +  \ adr1 adr2 len3
   >IN +!                  \ adr1 adr2
   OVER - ;                \ adr1 cnt
: WORD  ( C -- ADR )
   >R 'STREAM              \ adr1 len1
   BLK 2@ HISTORY 2!       \ save BLK and >IN
   TUCK R@ SKIP            \ len1 adr2 len2
   >R TUCK R> R> SCAN      \ adr2 len1 adr3 len3
   ADJUST >HERE ;          \ adr2 cnt

: CHAR  ( C -- ADR CNT )
   DUP>R LIT [ HERE >A 0 , ] C!
   'STREAM 2DUP R>         \ adr1 len1 adr1 len1 c
   SCAN DUP 0=             \ adr1 len1 adr2 len2 flag
   [ HERE 3 + A> ! ]
   ABORT"   MISSING"
   ADJUST ;                \ adr1 cnt


Here are the new versions.
Code:
NH
2VARIABLE HISTORY
: WORD  ( C -- HERE )
   'STREAM                 \ c adr1 len1
   BLK 2@ HISTORY 2!       \ save BLK and >IN
   DUP >IN +!
   2PICK SKIP              \ c adr2 len2
   ROT 2PICK -ROT SCAN     \ adr2 adr3 len3
   1- 0 MAX NEGATE >IN +!  \ adr2 adr3
   OVER - >HERE ;          \ here

: CHAR  ( C -- ADR CNT )
   DUP>R LIT [ HERE >A 0 , ] C!
   'STREAM 2DUP R>         \ adr1 len1 adr1 len1 c
   SCAN 0=                 \ adr1 len1 adr2 flag
   [ HERE 3 + A> ! ]
   ABORT"   MISSING"
   NIP OVER -              \ adr2 cnt
   DUP 1+ >IN +! ;         \ adr2 cnt

The line:
Code:
   BLK 2@ HISTORY 2!       \ save BLK and >IN

saves the values of BLK and >IN in a headerless double variable HISTORY for use by WHERE , the word which displays where an error occurred.
The extra size of WORD and CHAR exactly offset the memory saved by removing the headerless word ADJUST ; however, there are fewer trips through NEXT with WORD and CHAR so they will be slightly faster.


Top
 Profile  
Reply with quote  
PostPosted: Wed Jul 13, 2022 11:47 pm 
Offline

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

I've changed the definition for Fleet Forth's LOAD . Here is the old definition:
Code:
: LOAD  ( U -- )
   0 SWAP LINELOAD ;

And the new:
Code:
: LOAD  ( U -- )
   0 SWAP BRANCH
   [ ' LINELOAD >BODY , ] ; -2 ALLOT

This does not change the size of LOAD , nor will it make a noticeable speed improvement. It does save a cell on the return stack for each nesting of nested LOADs .
I've also changed the source for Fleet Forth's warm start routine from this:
Code:
HSUBR WARM
   $FF # LDX  TXS
   (WARM) JSR
   >FORTH
   FORTH DEFINITIONS
   ." {REV}WARM START"
   ABORT ; -2 ALLOT

To this:
Code:
HSUBR WARM
   $FF # LDX  TXS
   (WARM) JSR
   >FORTH
   FORTH DEFINITIONS
   ." {REV}WARM START"
   QUIT ; -2 ALLOT

Note: {REV} is the reverse print character in the C64 PETSCII. A carriage return clears the reverse print mode.
Here is the definition for Fleet Forth's ABORT
Code:
: ABORT  ( -- )
   SINGLE ERR SP! AP!
   QUIT ; -2 ALLOT

Since the stacks are cleared and multitasking is disabled in the subroutine called by the warm start and cold start routines, there is no need to go to ABORT rather than QUIT in the warm start routine. This also avoids executing the deferred word ERR during a warm start which could have been initiated because ERR was set to a bad vector.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 19, 2022 12:40 am 
Offline

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

Back near the end of 2020 I presented the source code for Fleet Forth's new (ABORT") , which is so much faster than the old version when there is no error.
Code:
CODE (ABORT")  ( F -- )
   0 ,X LDA,  1 ,X ORA,
   0= IF,
      IP )Y LDA,  SEC,
      IP ADC,  IP STA,
      CS IF,  IP 1+ INC,  THEN,
      POP JMP,
   THEN,
   >FORTH
   WHERE CR R@ S?
   ABORT ;
   -2 ALLOT

The following snippet of code skips Fleet Forth's instruction pointer, IP , over an inline counted string.
Code:
   IP )Y LDA,  SEC,
   IP ADC,  IP STA,
   CS IF,  IP 1+ INC,  THEN,

There are two other words in Fleet Forth which skip over an inline string. They are (.") and (") .
Code:
: (.")  ( -- )
   R> COUNT 2DUP + >R TYPE ;
: (")  ( -- ADR )
   R> DUP COUNT + >R ;

Fleet Forth's kernel can be made smaller by taking the string skipping code out of (ABORT") and placing it in (") .
Code:
: (")  ( -- ADR )
   R@
   >ASSEM
   IP )Y LDA  SEC
   IP ADC  IP STA
   CS IF  IP 1+ INC  THEN
   NEXT JMP  END-CODE
: (.")  ( -- )
   R@ COUNT TYPE
   BRANCH
   [ ' (") >BODY 2+ , ] ; -2 ALLOT

The new (") is six bytes bigger and the new (.") is four bytes smaller.
The newest (ABORT") is twelve bytes smaller for a net savings of ten bytes.
Code:
CODE (ABORT")  ( F -- )
   INX  INX
   $FE ,X LDA  $FF ,X ORA
   ' (") >BODY 4 +  0= BRAN
   >FORTH
   WHERE  CR R> S?
   ABORT ; -2 ALLOT

(ABORT") is still fast. When there is no error, this version of (ABORT") should be two cycles faster. The branch does not cross a page boundary, my metacompiler reports page boundary crossings. I get a print file of the metacompiler messages by typing LOGGER before loading the kernel source.

There is also this code in ?BRANCH
Code:
      LABEL 2.IP.+!
      CLC
      IP LDA  2 # ADC  IP STA
      NEXT CS NOT BRAN
      IP 1+ INC
      NEXT 0= NOT BRAN  // ALWAYS

which skips IP over a cell.
Note: LABEL creates a label in the metacompiler's host vocabulary with the present value of THERE , the target's HERE , as its value. It does not increase the size of the target in any way.
This code fragment, 2.IP.+! can also be used by (IS) , the word compiled by IS , and COMPILE .
The original source.
Code:
: (IS)
   R> DUP 2+ >R @ >BODY ! ;
: COMPILE
   ?COMP R> DUP 2+ >R @ , ;

And the new.
Code:
: (IS)  ( -- )
   R@ @ >BODY !
   LABEL SKIP.CELL
   >ASSEM
   2.IP.+! JMP  END-CODE
: COMPILE
   ?COMP  R@ @ ,  BRANCH
   [ SKIP.CELL , ] ; -2 ALLOT

The new (IS) is three bytes smaller and the new COMPILE is four bytes smaller.


Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 16, 2022 2:35 am 
Offline

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

I mentioned in this post that I was going to write an experimental kernel. I've written the experimental kernel and I like it. Here is the source for the following words:
?EXIT 0EXIT ?LEAVE (LOOP) ?BRANCH

Code:
CODE ?EXIT  ( F -- )
   INX  INX
   $FE ,X LDA  $FF ,X ORA
   0= IF CS>A
   0= NOT IF CS>A  END-CODE
CODE 0EXIT  ( F -- )
   INX  INX
   $FE ,X LDA  $FF ,X ORA
   0= NOT IF CS>A
   0= IF CS>A  END-CODE
CODE ?LEAVE  ( F -- )
   INX  INX
   $FE ,X LDA  $FF ,X ORA
   0= IF CS>A
   0= NOT IF CS>A  END-CODE

CODE (LOOP)
   XSAVE STX  TSX  $101 ,X INC
   0= IF  // BRANCHING OUT OF WORD
   SEC  TYA
   LABEL I.HI+A
   $102 ,X ADC  $102 ,X STA
   VS IF  // BRANCHING OUT OF WORD
   XSAVE LDX
   A>CS A>CS THEN
   LABEL LEAVE.BODY
   PLA  PLA  PLA  PLA
   A>CS A>CS THEN
   A>CS A>CS THEN
   LABEL EXIT.BODY
   PLA  IP STA   PLA  IP 1+ STA
   THEN  THEN  THEN   CS>A  CS>A
   LABEL NEXT
   1 # LDY
   IP )Y LDA  W 1+ STA  DEY
   IP )Y LDA  W    STA  CLC
   IP LDA  2 # ADC  IP STA
   CS NOT IF
      W 1- JMP
   THEN
   IP 1+ INC
   W 1- JMP  END-CODE

CODE ?BRANCH  ( F -- )
   INX  INX
   $FE ,X LDA  $FF ,X ORA
   0= NOT IF
      LABEL 2.IP.+!
      CLC
      IP LDA  2 # ADC  IP STA
      NEXT CS NOT BRAN
      IP 1+ INC
      NEXT 0= NOT BRAN  // ALWAYS
      A>CS THEN  A>CS THEN
      LABEL XBRANCH
      XSAVE LDX
   THEN
   IP )Y LDA  PHA  INY
   IP )Y LDA  IP 1+ STA
   PLA  IP STA
   NEXT 2+ JMP  END-CODE

The label XBRANCH is no longer needed since it is now only used in the phrase XBRANCH 2+ . I will remove it and add a label just under the line with XSAVE LDX . This new label will be BRANCH.BODY . Of course, I'll need to change the two occurrences of XBRANCH 2+ to BRANCH.BODY .

In Fleet Forth, an item of control flow data consists of an address and a security number. The words which supply the address and security number are
>MARK <MARK
the words which use the address and security number are
>RESOLVE <RESOLVE
The control flow security is to make sure that a >MARK is matched with a >RESOLVE and a <MARK is matched with a <RESOLVE . These two conditions are to make sure that branches are properly resolved, avoiding an upcoming system crash. As long as they are met, control flow words can be used in any way the programmer sees fit.

The word CS>A copies one item of control flow data to the auxiliary stack and the word A>CS copies one item of control flow data from the auxiliary stack.
Using the auxiliary stack to temporarily hold the control flow data makes it easier to write branches out of the words ?EXIT 0EXIT and ?LEAVE and into the body of (LOOP) . It also makes it easier to write two branches out of (LOOP) and into the body of ?BRANCH (really into the part that has the code for BRANCH ).

Here is the source for the words BRANCH LEAVE and EXIT , which have no bodies.
Code:
CODE BRANCH  ( -- )
   -2 ALLOT XBRANCH 2+ ,
END-CODE
CODE LEAVE  ( -- )
   -2 ALLOT  LEAVE.BODY ,
END-CODE
CODE EXIT  ( -- )
   -2 ALLOT  EXIT.BODY ,
END-CODE

With the new kernel, EXIT and LEAVE fall through to NEXT because their code is in the body of (LOOP) .
(LOOP) falls through to NEXT when the DO LOOP terminates.

Here is the new source for LIT .
Code:
CODE LIT  ( -- W )
   DEX  DEX
   IP )Y LDA  0 ,X STA  INY
   IP )Y LDA  1 ,X STA
   2.IP.+! JMP  END-CODE

2.IP.+! is a metacompiler label for the address in ?BRANCH which, as the label name implies, adds two to IP .

Here is the new source for CLIT .
Code:
CODE CLIT  ( -- B )
   IP )Y LDA
   IP INC
   0= IF  IP 1+ INC  THEN
   AYPUSH JMP
// SETUP SETS CARRY AS SIDE EFFECT
LABEL SETUP
   .A ASL  N 1- STA
   BEGIN
      0 ,X LDA  N ,Y STA
      INX  INY  N 1- CPY
   0= UNTIL
   0 # LDY
   RTS  END-CODE

Placing the code for SETUP right after CLIT was done as a convenience (or laziness on my part). I could just as well have placed END-CODE after the jump to AYPUSH with HSUBR SETUP (headerless subroutine) used rather than LABEL SETUP .

LIT and CLIT are used in LITERAL .
Code:
: LITERAL  ( N -- )
   DUP SPLIT NIP
   IF  COMPILE LIT , EXIT  THEN
   COMPILE CLIT C, ; IMMEDIATE


I have removed the constants PUSH and PUT from Fleet Forth's assembler. Fleet Forth has AYPUSH to push a cell on the data stack with the low byte in the accumulator and high byte in the Y-register, and the word AYPUT to replace a cell on the data stack. If the high byte were in the accumulator, I would have named them YAPUSH and YAPUT to reflect the (low byte, high byte) order of the 16 bit cell.
It seemed awkward to me to have AYPUSH AYPUT as well as PUSH PUT in the assembler, almost like Fleet Forth's kernel was not a single unified design. With the new kernel, a jump to PUSH and PUT now cost three more clocks cycles, so I got rid of them.
If a code word needs them, the following code fragments can be defined.
Code:
SUBR PUSH  ( -- W )
   TAY  PLA
   AYPUSH JMP
END-CODE

SUBR PUT  ( -- W )
   TAY  PLA
   AYPUT JMP
END-CODE

These are used just like the constants PUSH and PUT in the FIG Forth assembler.

I have a disk of Blazin' Forth utilities ported to Fleet Forth. When I modified the words using PUSH and PUT to use AYPUSH and AYPUT , they were smaller. These words were a more natural fit for AYPUSH and AYPUT than PUSH and PUT .

The experimental kernel was slightly smaller until I saw how I could squeeze a few more cycles out of the code portion of BLOCK and made a few other speed/size trade offs before rebuilding once again. The experimental kernel is now about three bytes bigger than the other one.
I'm going to run a few more tests, maybe look for potential optimizations. My metacompiler reports where a branch crosses a page boundary, I'd like to minimize the occurrence of those. They're not so bad when the branch is not part of a loop, but I really don't like the extra cycle in a loop. As I said, I like this experimental kernel. It is Fleet Forth's new kernel.


Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 30, 2022 12:49 am 
Offline

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

In this post I discussed Fleet Forth's WORD .
I think UMIN and 2PICK were the only non-standard words which I didn't define.
UMIN is the unsigned version of MIN .
Fleet Forth's 2PICK has the same stack effect as the phrase '2 PICK'.
Here is Fleet Forth's 2PICK
Code:
CODE 2PICK  ( N1 N2 N3 -- N1 N2 N3 N1 )
   4 ,X LDA  5 ,X LDY
   AYPUSH JMP  END-CODE

Fleet Forth's double number version of PICK is DPICK
As with Mosaic Forth's DPICK , it is zero based and copies the double number whose high cell is the Nth item on the data stack, not counting N. This double number could also be thought of as a pair of single numbers.
Code:
DPICK  ( D WN-1 . . . W0 +N -- D WN-1 . . . W0 D )

Here is another way to look at it. Not counting N, PICK skips the top N numbers on the stack and copies the next one to push onto the stack.
2PICK skips the top number, the next to top number and copies the third number to push onto the stack.
DPICK also skips the top N numbers on the stack but it copies two cells ( a double or a pair of singles ) to push onto the stack.


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 03, 2022 3:07 pm 
Offline

Joined: Wed Aug 21, 2019 6:10 pm
Posts: 217
In xForth I use THIRD for what you have called 2PICK. FOURTH is what would be 3PICK in that naming system, but I stop at THIRD, so the verbal confusion of FOURTH and FORTH is not an issue.


Top
 Profile  
Reply with quote  
PostPosted: Sun Sep 04, 2022 6:52 pm 
Offline

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

The only problem I had with the name 2PICK was the possibility of it being mistaken for the double number version of PICK . I think DPICK is a better name for the double number version of PICK .
I like that the name 2PICK describes what it is doing just as the name DUP>R describes what it is doing.
2PICK performs 2 PICK
DUP>R performs DUP >R
If I needed to perform 3 PICK often enough to justify writing a primitive, there would be no verbal confusion with the name 3PICK , unlike the name FOURTH in your example.


Top
 Profile  
Reply with quote  
PostPosted: Sun Sep 04, 2022 7:40 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
For newcomers: I have a list of these at viewtopic.php?p=72734#p72734 .

_________________
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 Sep 10, 2022 12:55 am 
Offline

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

Fleet Forth's LIT no longer falls through to NEXT and it was rewritten so it no longer has the functionality of PUSH or PUT , which made the combined size of LIT and CLIT smaller. For those new to Forth, Some Forth's for the Commodore 64 have an address in the kernel, PUSH , where a code word pushes the low byte to the return stack, places the high byte in the accumulator and jumps to PUSH (in these Forths, PUSH is a constant in the assembler). The code at PUSH would push the value onto the data stack. PUT is similar but the value overwrites the current top of stack. An excerpt from the source for Fleet Forth's previous kernel.
Code:
   LABEL PUSH
   DEX  DEX
   LABEL PUT
   1 ,X STA  PLA  0 ,X STA
   LABEL NEXT

Fleet Forth has AYPUSH , which takes the low byte in the accumulator and the high byte in the Y-register and pushes that value onto the data stack. AYPUT is similar but the value overwrites the current top of stack. An excerpt from the source for Fleet Forth's current kernel.
Code:
   LABEL APUSH
   0 # LDY
   LABEL AYPUSH
   DEX  DEX
   LABEL AYPUT
   1 ,X STY  0 ,X STA
   NEXT JMP

This excerpt is from the source for Fleet Forth's (FIND) , so both sets of operations were supported in the previous kernel. With the changes to Fleet Forth's kernel, I've tried to remove the dependencies on PUSH and PUT . Some primitives were relatively easy to change without increasing the code size.
Code:
CODE UD/MOD  ( UD1 U1 -- U2 UD2 )
   DEX  DEX
   2 ,X LDA  N 2+ STA  0 ,X STA
   3 ,X LDA  N 3 + STA  1 ,X STA
   2 ,X STY  3 ,X STY
   ' UM/MOD @ 6 + JSR
   1 ,X LDA  PHA        \ swapped these
   0 ,X LDA  PHA        \ two lines
   N 2+ LDA  0 ,X STA
   N 3 + LDA  1 ,X STA
   ' UM/MOD @ 6 + JSR
   ' R> @ JMP  END-CODE

I swapped the order the bytes were pushed to the return stack and replaced the PLA and jump to PUSH with a jump to the body of R> . I did the same thing with ROLL .
Code:
CODE ROLL
   0 ,X LDA
   POP.JMP 0= BRAN
   XSAVE STX  .A ASL  TAY
   XSAVE ADC
   // POP.JMP  0< BRAN
   TAX  INX  INX
   1 ,X LDA  PHA  0 ,X LDA  PHA
   BEGIN
      $FF ,X LDA  1 ,X STA
      DEX  DEY
   0= UNTIL
   ' R> @ 2+ JMP  END-CODE

By the way, POP.JMP is a label in the header-less word TRAVERSE .
Code:
NH
CODE TRAVERSE  ( ADR1 DIR -- ADR2 )
   BEGIN
      CLC
      0 ,X LDA 2 ,X ADC 2 ,X STA
      1 ,X LDA 3 ,X ADC 3 ,X STA
      2 X) LDA
   0< UNTIL
   LABEL POP.JMP
   POP JMP  END-CODE

I took a different approach with @ . This is the original source.
Code:
CODE @  ( ADR -- N )
   0 X) LDA  PHA  0 ,X INC
   0= IF  1 ,X INC  THEN
   0 X) LDA
   PUT JMP  END-CODE

And here is the new source.
Code:
CODE @  ( ADR -- N )
   0 ,X LDA  N STA
   1 ,X LDA  N 1+ STA
   LABEL (N)PUT
   N )Y LDA  0 ,X STA  INY
   N )Y LDA  1 ,X STA
   NEXT JMP  END-CODE

Six bytes bigger and slightly faster than the original in the previous kernel; however, BLOCK , which starts out as a code word, is six bytes smaller because it now has a jump to the label (N)PUT in @ .
A few days ago I got an idea that I thought was either clever or mad. I thought what if I replace N in @ and BLOCK with W ?
I could change CONSTANT from this:
Code:
: CONSTANT  ( N -- )
   CREATE , ;CODE  ( -- N )
   LABEL DO.CONSTANT
   2 # LDY
   W )Y LDA  PHA  INY
   W )Y LDA
   PUSH JMP  END-CODE

to this:
Code:
: CONSTANT  ( N -- )
   CREATE , ;CODE  ( -- N )
   LABEL DO.CONSTANT
   2 # LDY
   DEX  DEX
   (W)PUT JMP  END-CODE

This would save 4 bytes and remove one more reference to PUSH . The loop index word, I , is the last place where PUSH is referenced. In the new kernel it is:
Code:
CODE I  ( -- LOOP-INDEX )
   XSAVE STX  TSX  CLC
   $101 ,X LDA  $103 ,X ADC  PHA
   $102 ,X LDA  $104 ,X ADC
   XSAVE LDX
   LABEL PUSH
   TAY  PLA  AYPUSH JMP  END-CODE

Now that CONSTANT doesn't need to jump to PUSH , it can be coded as this:
Code:
CODE I  ( -- LOOP-INDEX )
   XSAVE STX  TSX  CLC
   $101 ,X LDA  $103 ,X ADC  TAY
   $102 ,X LDA  $104 ,X ADC
   XSAVE LDX
   DEX  DEX
   1 ,X STA  0 ,X STY
   NEXT JMP  END-CODE

It's 4 bytes bigger than the version in the new kernel and 5 bytes bigger than the version in the previous kernel, but it's faster.


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 10, 2022 2:14 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
JimBoyd wrote:
I like that the name 2PICK describes what it is doing just as the name DUP>R describes what it is doing.
2PICK performs 2 PICK
DUP>R performs DUP >R
I too like the idea of a compound name that describes what the compound word does. And I wrote a bunch of them years ago, but I had a slightly different naming convention.

Using my convention, it would be 2&PICK that performs 2 PICK and DUP&>R that performs DUP >R. The ampersand has a special significance that's recognized by the reader, making it clear what's going on.

Quote:
If I needed to perform 3 PICK often enough to justify writing a primitive, there would be no verbal confusion with the name
If you have a lot of these compound primitives, it's not unlikely that verbal confusion will crop up. By contrast, the ampersand system is immune to that, although I'll admit it may look a little clunky on the screen -- less terse. :|


Parenthetically: I recall musing that the ampersand could be more than just a hint to the human. Based on the ampersand, the compiler could quite easily be given an extra level of FINDing capability.

The premise is: the source code might be from some other time or place, and might include a compound word that's not implemented on that particular Forth system. For example, imagine 2&PICK is encountered in the source, but the word isn't found. The name would be scanned and any ampersands replaced by spaces. Then, one by one, we'd FIND the now-unglued constituent pieces! The compiler adapts to the unfamilar compound word.

But the benefit is modest. :roll: Moreover, it's perhaps not very Forth-like to grant superpowers to the ampersand. Interesting muse, though!

-- 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: Sat Sep 10, 2022 7:39 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
I've used the _ character for visual separation without Forth treating them as separate words. Early Forth always used the hyphen, probably because the _ was not on the keyboards. The search for & and replacing with a space makes sense, if the need arises.

Hmmm... I just had a thought, and looked at the IBM437 character set I use, and character $FF also displays as a blank, so maybe I could have Forth words that have a non-breaking space in them. It could be confusing though, LOL.

_________________
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 Sep 13, 2022 12:41 am 
Offline

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

After rewriting Fleet Forth's CONSTANT I started thinking about 2CONSTANT . In Fleet Forth, 2CONSTANT is a CREATE DOES> word. For just 9 more bytes, it can be a CREATE ;CODE word.
Here is the source for 2CONSTANT .
Code:
: 2CONSTANT  ( D -- )
   CREATE , ,
   DOES>     ( -- D )
      2@ ;

And here is the source for a CREATE ;CODE version.
Code:
: 2CONSTANT  ( D -- )
   CREATE
      , ,
   ;CODE     ( -- D )
      DEX  DEX
      4 # LDY
      W )Y LDA  0 ,X STA  INY
      W )Y LDA  1 ,X STA
      DO.CONSTANT JMP  END-CODE

DO.CONSTANT is a metacompiler label in the source for CONSTANT .
Code:
: CONSTANT  ( N -- )
   CREATE , ;CODE  ( -- N )
   LABEL DO.CONSTANT
   2 # LDY
   DEX  DEX
   (W)PUT JMP  END-CODE
: VALUE  ( N -- )
   CONSTANT  ;CODE  ( -- N )
   DO.CONSTANT JMP
   END-CODE

I just don't know if 2CONSTANT would be used often enough to justify the change. I haven't found a single instance of a double constant in my own code.


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 17, 2022 2:18 am 
Offline

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

I'm so used to using >FORTH when I want to run high level Forth in a primitive that I overlooked a case where it was not necessary.
Here is the source for Fleet Forth's EXECUTE .
Code:
CODE EXECUTE  ( ADR -- )
   0 ,X LDA  1 ,X LDY
   INX  INX
   LABEL (EXECUTE)
   W STA  W 1+ STY
   0 # LDY
   W 1- JMP  END-CODE

Because the Y-register needs to be set back to zero, it is two bytes larger than a version which does not use it. EXECUTE is defined like this because R/W has two places where it branches six bytes into the body of EXECUTE to the code following the metacompiler label (EXECUTE) . The Accumulator and Y-register hold the low and high bytes respectively of the word to execute (a pair of deferred words) without returning to R/W.

The word (T&S) is defined in the system loader. It is smaller as a code word than as a high level word.
Code:
CODE (T&S)  ( ADR BLK# -- S/T ADR S T D DSI )
   DRB LDY  DPT ,Y LDA
   0< IF
      >FORTH  T&S81 EXIT
      >ASSEM -2 ALLOT
   THEN
   >FORTH  T&S41 ;
' (T&S) IS T&S

This extends T&S so it supports blocks on the Commodore 1581 drive as well as the 1541 and 1571.
Each place where I've used >FORTH only needs to execute one word then exit, and not a longer high level Forth thread. I can use Fleet Forth's EXECUTE just as I did in R/W. The destination address for JMP is six bytes into the body of EXECUTE .
Code:
CODE (T&S)  ( ADR BLK# -- S/T ADR S T D DSI )
   DRB LDY  DPT ,Y LDA
   0< IF
      ' T&S81 SPLIT SWAP
      # LDA  # LDY
      ' EXECUTE @ 6 + JMP
   THEN
   ' T&S41 SPLIT SWAP
   # LDA  # LDY
   ' EXECUTE @ 6 + JMP
   END-CODE
' (T&S) IS T&S

The SWAP after SPLIT is personal preference. I like to load the Accumulator before the Y-register if they hold two bytes of a 16 bit value. SWAP could be left off and this line:
# LDA # LDY
would be:
# LDY # LDA

This version of (T&S) is the same size but faster.
This is what the disassembly looks like:
Code:
(T&S)
 16015  2252    LDY ' DRB >BODY
 16018 10000 ,Y LDA ' DPT >BODY
 16021 16030    BPL
 16023    55  # LDA
 16025    62  # LDY
 16027  3873    JMP ' EXECUTE >BODY 6 +
 16030   234  # LDA
 16032    39  # LDY
 16034  3873    JMP ' EXECUTE >BODY 6 +
22

Commodore 64 drives start at device 8 and go up from there. My setup (a simulation on VICE) has three disk drives. Device 10, the third drive, is a 1581. In the drives property table DPT , that would be entry 2, so this line sets device 10 to a Commodore 1581 drive for block access.
Code:
$83 DPT 2+ C!

To summarize: With Fleet Forth's version of EXECUTE , when a word which isn't a primitive needs to be effectively branched to, it is more efficient to use EXECUTE than >FORTH .


Top
 Profile  
Reply with quote  
PostPosted: Sun Sep 18, 2022 10:48 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
Dr Jefyll wrote:
Parenthetically: I recall musing that the ampersand could be more than just a hint to the human. Based on the ampersand, the compiler could quite easily be given an extra level of FINDing capability.

The premise is: the source code might be from some other time or place, and might include a compound word that's not implemented on that particular Forth system. For example, imagine 2&PICK is encountered in the source, but the word isn't found. The name would be scanned and any ampersands replaced by spaces. Then, one by one, we'd FIND the now-unglued constituent pieces! The compiler adapts to the unfamilar compound word.

But the benefit is modest. :roll: Moreover, it's perhaps not very Forth-like to grant superpowers to the ampersand. Interesting muse, though!

-- Jeff


Interesting muse, indeed! I did think of a case where granting the ampersand special powers might not be such a good idea. Some Forths have the word T&S to map a block to the initial track and sector for that block. Suppose Forth source with T&S is loaded on a system without this word. This system does; however, have two unrelated words, T and S .


Top
 Profile  
Reply with quote  
PostPosted: Sat Oct 01, 2022 1:42 am 
Offline

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

Fleet Forth has the following control flow manipulation words:
Code:
CS-DUP
CS-DROP
CS-SWAP
CS-ROT

as well as a pair of words to transfer control flow data to and from the auxiliary stack.
These four were chosen over AnsForth's CS-PICK and CS-ROLL because they are code words without bodies. They are immediate aliases for the following.
Code:
2DUP
2DROP
2SWAP
2ROT

The inclusion of CS-DROP allows defining a word such as AFT without knowing the number of cells used for control flow data. Drop the control flow data for a backward branch and the branch is not compiled. Drop the control flow data for a forward branch and the new word could crash the system. On the Forth-83 systems I've seen, a BRANCH to address zero is compiled because >MARK is usually defined as the following (when it doesn't add the compiler security data)
Code:
: >MARK  ( -- ADDR )
   HERE 0 , ;

and IF , for example, is defined with >MARK .
Code:
: IF  ( flag -- )
      ( -- sys )  \ compiling
   COMPILE ?BRANCH
   >MARK <possible compiler security data> ; IMMEDIATE

The Forth-83 Standard states:
Code:
>MARK        -- addr                       C,83   "forward-mark"
     Used at the source of a forward branch.  Typically used
     after either BRANCH or ?BRANCH .  Compiles space in the
     dictionary for a branch address which will later be resolved
     by >RESOLVE .

so >MARK could even have this definition.
Code:
: >MARK  ( -- ADDR )
   HERE  2 ALLOT ;

The following will compile successfully. Depending on how >MARK is defined, it may crash at run time.
Code:
: CRASH
   FALSE
   IF  CS-DROP ;

This trivial example reveals a potential problem. Trying to implement tricky control flow mechanisms and not quite getting it right, or being careless with existing control flow structures, such as trying multiple uses of AFT in a FOR NEXT loop could also cause the compilation of a branch to a non valid address.
Why does >MARK comma a zero in the dictionary on some systems? It's a place holder, obviously. Why zero?
I'm going to try the following with the next build of Fleet Forth's kernel.
Code:
NH
CREATE BAD.BRANCH
]  TRUE ABORT" BAD BRANCH"  [
: >MARK  ( -- ADR 1 )
   HERE 1  BAD.BRANCH , ;

NH (no header) is a metacompiler word which causes the next target word to be headerless. The name can be found while metacompiling, but will not be in the target system. The 1 in this definition of >MARK is because Fleet Forth compiler security is handled by the words >MARK >RESOLVE <MARK and <RESOLVE to keep it minimal so it stays out of the way of the programmer as much as possible. BAD.BRANCH is just a code field which, like all variables, points to its body. The body is just a fragment of threaded code which aborts every time with the message "BAD BRANCH", although any meaningful message could be used.
This is the address >MARK will compile rather than zero. The error isn't caught at compile time, but it is at run time.
The Forth-83 Standard does not specify what is to be compiled for the place holder. It's an extra one to two dozen bytes, depending on the length of the error message. There is no additional overhead and no extra restrictions. This may be a worthwhile addition to any Forth-83 Standard system meant to be used by more than just the author of said system, especially if it's used by someone who likes to experiment in Forth.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 13, 14, 15, 16, 17, 18, 19 ... 24  Next

All times are UTC


Who is online

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