6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 12:24 am

All times are UTC




Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 11, 12, 13, 14, 15, 16, 17 ... 24  Next
Author Message
PostPosted: Wed Oct 20, 2021 11:28 pm 
Offline

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

I have previously mentioned the word VALID? . This word is used to test if a non-digit character is valid punctuation for a number. It is a deferred word and by default is set to (VALID?) , which only recognizes the period ( . ) as valid punctuation in a double number. Since VALID? is a deferred word, what is accepted as valid punctuation for a number can be changed. Starting Forth mentions these as valid punctuation for a double number:
Code:
, . / - :

Some of these, as well as other punctuation, could be added by using a new version of (VALID?) . Taking this into consideration, I have decided to use the version of NUMBER? which requires valid non-digit punctuation in a number be separated by at least one digit. I believe a typo would be less likely slip by as a valid number with the tighter restriction and that it is well worth the extra six bytes.
My fellow Forthwrites, am I being a bit too cautious? Is it worth the extra six bytes?


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 20, 2021 11:47 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
I say it's worth it, but you should just follow your gut ... any decision you make can be easily revised later.

_________________
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: Thu Oct 21, 2021 12:40 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
barrym95838 wrote:
I say it's worth it, but you should just follow your gut


Thanks.
This was one of those decisions where I could have gone either way, but leaned toward the more restrictive, and slightly larger, version of NUMBER? .


Top
 Profile  
Reply with quote  
PostPosted: Thu Oct 21, 2021 1:58 am 
Offline

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

I have previously made brief mention of Fleet Forth's word WHERE . It is used to display the location of an error when (ABORT") aborts with an error message. WHERE can be useful apart from (ABORT") to display an error message which is not known beforehand.
-SET is one example:
Code:
: -SET  ( -- )
   WHERE
   R@ 2- @ >NAME CR RON ID. ROFF
   ."  NOT SET" ABORT ; -2 ALLOT

-SET is the vector for a new deferred word until a new vector is set with IS :
Code:
' NOOP IS PAUSE
' (PAUSE) IS PAUSE
' (EMIT) IS EMIT

If the new deferred word, prior to being set to a new vector, is executed from the command line, -SET displays the odd message that EXECUTE is not set. When executed from within a high level Forth word, the message shows the name of the deferred word which is not set.
Another example is ?D , the word which checks the disk drive for an error condition after block read/write.
Code:
: ?D  ( -- )
   $0F >SLF# (?D) 0EXIT
   WHERE
   CR ." DISK ERROR:" CR
   DEB S? ABORT ;  -2 ALLOT

WHERE displays where the error occurred. The message 'DISK ERROR:' is displayed, followed my the message in the Disk Error Buffer or DEB , which comes from the disk drive as a text string.
To get a better feel of what Fleet Forth error messages look like, here are some examples.
This one is an example of using PAD as a buffer for sector 10 of track 37 on disk 0 of the current drive to read (r/w flag value is 1) 256 bytes with the sector read/write word. Device 8 is a 1541 disk drive and does not have 37 tracks. I've added comments after the fact.
Code:
8 DRIVE  OK              \ select device 8 as the current drive
DOPEN  OK                \ open current drive for block access
PAD 10 37 0 1 256 SR/W   \ try to read sector 10 of track 37
PAD 10 37 0 1 256 SR/W   \ this line shown by Fleet Forth in reverse video
                  ^^^^   
DISK ERROR:
66,ILLEGAL TRACK OR SECTOR,37,10

When WHERE shows the line in a screen or the text input buffer or an evaluated string, it shows that line in reverse video so it stands out. The carets ( ^ ) identify the text which caused the error.
Here are some errors due to (intentional) typos.
From the console:
Code:
DECIMAL  OK
CR 1 2 + . 123OOPS 4 5 * .
3
CR 1 2 + . 123OOPS 4 5 * .
           ^^^^^^^
WHAT?

and from a screen:
Code:
1 RAM LOAD 7
SCR# 32769 LINE# 7
2 5 + . 321OOPS  7 9 * .
        ^^^^^^^
WHAT?

Missing delimiter for a string:
Code:
: TEST2
CR ." START TEST2 TEST1
CR ." START TEST2 TEST1
   ^^
" MISSING

and using a deferred word which has not been set:
Code:
DEFER PWM  OK
: TEST  50 PWM ;  OK
TEST
TEST
^^^^
PWM NOT SET

Here is a screen shot of trying to forget a word protected by FORGET's internal 'fence'.
Attachment:
forget test fleet forth.png
forget test fleet forth.png [ 18.96 KiB | Viewed 11140 times ]



Top
 Profile  
Reply with quote  
PostPosted: Sun Oct 31, 2021 8:41 pm 
Offline

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

I've decided to change Fleet Forth's word DIGIT to have the third stack effect mentioned in my previous post.
Code:
DIGIT  ( CHAR -- N TRUE )
       ( CHAR -- CHAR FALSE )

Code:
CODE DIGIT  ( CHAR -- N TRUE )
            ( CHAR -- CHAR FALSE )
   SEC  0 ,X LDA  $30 # SBC
   ' FALSE @ CS NOT BRAN
   $A # CMP
   CS IF
      7 # SBC  $A # CMP
      ' FALSE @ CS NOT BRAN
   THEN
   ' BASE >BODY C@ # LDY
   UP )Y CMP
   ' FALSE @ CS BRAN
   0 ,X STA
   ' TRUE @ JMP END-CODE



I should have seen this before. I changed the jump at the end of DIGIT to a branch.
Code:
CODE DIGIT  ( CHAR -- N TRUE )
            ( CHAR -- CHAR FALSE )
   SEC  0 ,X LDA  $30 # SBC
   PUSH.FALSE CS NOT BRAN
   $A # CMP
   CS IF
      7 # SBC  $A # CMP
      PUSH.FALSE CS NOT BRAN
   THEN
   ' BASE >BODY C@ # LDY
   UP )Y CMP
   PUSH.FALSE CS BRAN
   0 ,X STA
   PUSH.TRUE CS NOT BRAN
END-CODE



Top
 Profile  
Reply with quote  
PostPosted: Sun Oct 31, 2021 8:58 pm 
Offline

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

My fellow Forthwrites, am I being a bit too cautious? Is it worth the extra six bytes?


It's actually eight bytes. Here is the source for both versions of NUMBER? . Just a reminder that DIGIT takes a character, it accesses the value of BASE internally, and returns either:
1) The numerical value of the digit and a true flag.
2) The character if it is not a valid digit and a false flag.
NUMBER? takes the address of a counted string which is followed by a trailing blank and returns a double number and a flag. The flag is true for a successful conversion. NUMBER? also sets the user variable DPL to the count of how many digit characters occur after the last valid punctuation character, or TRUE (-1) if there is no punctuation in the numeric string.
The larger stricter version:
Code:
: 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
   1+
   COUNT ASCII - <> DUP>R +
   COUNT DIGIT NIP >R
   2-
   BEGIN
      CONVERT DUP C@ VALID?
   WHILE
      DPL OFF
      DUP 1+ C@ VALID?
   UNTIL
   THEN
   C@ BL =  R> AND
   R>  ?EXIT
   >R DNEGATE R> ;

The smaller and less strict version:
Code:
: 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
   1+
   COUNT ASCII - <> DUP>R +
   COUNT DIGIT NIP >R
   2-
   BEGIN
      CONVERT DUP C@ VALID?
   WHILE
      DPL OFF
   REPEAT
   C@ BL =  R> AND
   R>  ?EXIT
   >R DNEGATE R> ;



Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 15, 2021 12:42 am 
Offline

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

I previously mentioned fixing Fleet Forth's multitasking support in the kernel. This allows PAUSE to execute while the user is typing at Forth's command line, or even just thinking about what to type. It's nice when your background tasks don't freeze while you're sitting at the keyboard thinking.
If round robin multitasking will not be used, to avoid splitting the stacks into smaller areas for each task, PAUSE can be vectored to a 'run to completion' word. There are two caveats. Any word PAUSE is vectored to must leave the stacks as they were when it began. It, and all the words it executes, must not execute PAUSE to avoid runaway recursion.
Background tasks normally execute PAUSE to switch tasks. In that case PAUSE is vectored to an actual task switcher.
The following Fleet Forth words execute PAUSE directly or indirectly through another word.
Code:
TYPE
QTYPE
KEY
(EXPECT)
INTERPRET
QUIT
DJIFFIES
JIFFIES

As well as any word which displays text, with the exception of EMIT since it displays a single character.
The error handling words ( ABORT ABORT" WHERE ) also execute PAUSE by way of TYPE but, they should not be in a background task. ABORT and ABORT" switch off any kind of multitasking by executing SINGLE , setting PAUSE to the no-op NOOP .

The following will cycle the border through all sixteen colors.
Code:
: BORDER?  ( -- B )
   $D020 C@ ;
: CYCLE  ( -- )
   BORDER? 1+ BORDER ;
' CYCLE IS PAUSE

Something like the cyclic executive Garth mentions can be set up easily:
Code:
: BACKGROUND
   TASK1
   TASK2
   TASK3
   TASK4 ;
' BACKGROUND IS PAUSE

What is missing is a way for a 'task' to run to a certain stopping point and resume where it left off. Leo Brodie's DOER/MAKE could be used. I propose a little structure of my own, inspired by DOER/MAKE, halting colon definitions. One of these runs to the halting point, where it exits. It resumes where it left off.
Code:
: H:
   :  ( -- ADR TRUE )
      HERE 2+ ,
      [ HERE 2+ >A ]
   DOES>  ( -- )
      @ >R ;
: (HALT)  ( -- )
          ( R: ADR -- )
   R@ 2+  R> @ >BODY ! ;
: HALT
   LATEST NAME> @ [ A> ] LITERAL <>
   ABORT" NON HALTING WORD"
   COMPILE (HALT)  [COMPILE] RECURSE
   ; IMMEDIATE

Or if an auxiliary stack is not available, a pair of free zero page locations can be used.
Code:
: H:
   :  ( -- ADR TRUE )
      HERE 2+ ,
      [ HERE 2+ 2 ! ]
   DOES>  ( -- )
      @ >R ;
: (HALT)  ( -- )
          ( R: ADR -- )
   R@ 2+  R> @ >BODY ! ;
: HALT
   LATEST NAME> @ [ 2 @ ] LITERAL <>
   ABORT" NON HALTING WORD"
   COMPILE (HALT)  [COMPILE] RECURSE
   ; IMMEDIATE

Here is a test halting word.
Code:
H: HTEST  ( -- )
   BEGIN
      CR ." STARTING HTEST" HALT
      CR ." MIDDLE OF HTEST" HALT
      CR ." END OF HTEST" HALT
   AGAIN ;

Each time HTEST executes, it displays one of the messages and exits.
Code:
HTEST
STARTING HTEST OK
HTEST
MIDDLE OF HTEST OK
HTEST
END OF HTEST OK
HTEST
STARTING HTEST OK

Here is an example without a BEGIN AGAIN loop.
Code:
H: TEST2  ( -- )
   CR ." BEFORE HALTING." HALT
   CR ." AFTER HALTING." ;

The very first time it is executed, it will display the message "BEFORE HALTING" and every time after that it will, when executed, display "AFTER HALTING" .
Here is what gets compiled for TEST2 .
Code:
' TEST2 >BODY :DIS
 22937 22939
 22939  7024 CR
 22941  6724 (.") BEFORE HALTING.
 22959 22754 (HALT)
 22961 22935 TEST2
 22963  7024 CR
 22965  6724 (.") AFTER HALTING.
 22982  2930 EXIT
47
 OK

Halting colon definitions even support recursion.
Code:
H: TEST  ( N -- N )
   BEGIN
      CR DUP SPACES ." BEGIN"
      1+ DUP 4 <
      IF  RECURSE  THEN
      HALT
      CR DUP SPACES ." MIDDLE"
      1+ DUP 6 <
      IF  RECURSE  THEN
      HALT
      CR DUP SPACES ." END"
      1- DUP 0< 0=
      IF  RECURSE  THEN
      HALT
   AGAIN ;

Code:
0 TEST
BEGIN
 BEGIN
  BEGIN
   BEGIN OK
TEST
    MIDDLE
     MIDDLE OK
TEST
      END
     END
    END
   END
  END
 END
END OK
. 0  OK

If PAUSE is set to a halting colon definition, or a regular colon definition which executes more than one halting colon definition, the halting colon definition(s) will perform an action each time PAUSE is executed. This may be too frequent. Garth also mentioned alarms. An interval timer would be nice. How about the down-counter by Tim Hendtlass?
Here is DOWN-COUNTER for Fleet Forth.
Code:
: DOWN-COUNTER
   2VARIABLE  ( -- )
   DOES>  ( -- ADR )
      JIFFY@ DROP
      OVER 2+ @
      OVER -  0 MIN
      2PICK +!
      OVER 2+ ! ;

That '0 MIN' is to handle the case when the jiffy timer resets to zero when it reaches twenty four hours.
A down-counter, a child word of DOWN-COUNTER , has two cells of storage. The first cell holds a value. The second cell holds the low cell of the Commodore 64 jiffy clock from the last time the down-counter was executed. Each time a down-counter is executed the amount of time, in jiffies, which passed from the last time is subtracted from the value in the first cell.
Code:
DOWN-COUNTER DELAY1 OK
300 DELAY1 ! OK
DELAY1 ? 209  OK
DELAY1 ? 118  OK
DELAY1 ? 5  OK
DELAY1 ? -445  OK

Of course, the values returned each time I type "DELAY1 ?" depends on how long I wait.
Here is sample code to cycle the Commodore 64 border and cursor colors through predetermined values without an actual multitasker other than PAUSE built into certain kernel words. It cycles the colors while I'm still able to use Fleet Forth for other things.
Code:
DOWN-COUNTER DELAY1
H: CYCLE-INK  ( -- )
   BEGIN
      DELAY1 @ 0< 0EXIT
      GREEN INK
      60 DELAY1 !  HALT
      DELAY1 @ 0< 0EXIT
      BROWN INK
      120 DELAY1 !  HALT
      DELAY1 @ 0< 0EXIT
      BLACK INK
      180 DELAY1 !  HALT
   AGAIN ;

DOWN-COUNTER DELAY2
H: CYCLE-BORDER  ( -- )
   BEGIN
      DELAY2 @ 0< 0EXIT
      GREEN BORDER
      30 DELAY2 !  HALT
      DELAY2 @ 0< 0EXIT
      CYAN BORDER
      90 DELAY2 !  HALT
      DELAY2 @ 0< 0EXIT
      YELLOW BORDER
      60 DELAY2 !  HALT
   AGAIN ;

: CYCLE  ( -- )
   CYCLE-INK  CYCLE-BORDER ;

' CYCLE IS PAUSE

Maybe halting colon definitions, or halting words, will be useful outside the realm of simulated multitasking, I don't know. Which will be more useful, halting words or DOER/MAKE? I don't know that either.

Note: My first attempt to port halting words to Blazin' Forth did not work. Blazin' Forth's word ] doesn't just change to the compiling state, it is the colon compiler. As a result, when Blazin' Forth's ] executes, parsing of the text stream and compiling begin immediately.
The only workaround I could come up with was to incorporate the functionality of : into H: without compiling : directly.
Code:
: H:                       \ nothing placed on stack?
   CREATE                  \ create a new header
      SMUDGE               \ hide name
      CURRENT @ CONTEXT !  \ set CONTEXT equal to CURRENT
      !CSP                 \ Blazin' Forth checks for a change in stack depth
      HERE 2+ ,            \ allocate and initialize one cell of storage
      ]                    \ include this AFTER everything up to DOES>
   DOES>
      @ >R ;

The test to make sure HALT is not used in normal colon definitions was left out of the version ported to Blazin' Forth. Because of the way Blazin' Forth handles compilation, it is not easy to test if a new definition is a child of H: until after the first line of text is parsed from the keyboard or until the block it is defined in is finished.

CREATE is used in both Fleet Forth and Blazin' Forth by all the words which create a header. It is basically VARIABLE without storage allotted.
Code:
\ Fleet Forth's VARIABLE
: VARIABLE
   CREATE  0 , ;


In regards to round robin multitasking, the problem with dividing the stacks so each task gets a portion is that it limits the number of tasks, as the background task's portion of the return stack has to have room for the nesting of that task and also for interrupts. One possibility is to have round robin multitasking with fewer background tasks. One or more background tasks could be a cyclic executive. The cyclic executive would need to be in a loop and include PAUSE .
Something like the following should work, but I haven't tested this yet.
Code:
\ define some halting words.
H: BG1 ... ;
H: BG2 ... ;
H: BG3 ... ;
0 $20 $13F TASK BACKGROUND
: CYCLE
   BACKGROUND ACTIVATE
   BEGIN
      BG1  BG2  BG3  PAUSE
   AGAIN ;
BACKGROUND LINK-TASK
\ define and link other tasks
   ...
\ switch on multitasking
MULTI



Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 15, 2021 8:07 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 255
JimBoyd wrote:
What is missing is a way for a 'task' to run to a certain stopping point and resume where it left off. Leo Brodie's DOER/MAKE could be used. I propose a little structure of my own, inspired by DOER/MAKE, halting colon definitions. One of these runs to the halting point, where it exits. It resumes where it left off.
Code:
: H:
   :  ( -- ADR TRUE )
      HERE 2+ ,
      [ HERE 2+ >A ]
   DOES>  ( -- )
      @ >R ;

You are breaking my brain (in a good way, I think). I was not aware you could put : in a colon definition (I would normally use CREATE, but I can see how they are different). Now that I think about it, of course you can do that. Forth lets you run with scissors by holding on to the sharp parts if you want.

Once I made it past that part, I see your DOES> pulls the address you left off (stored right at the beginning of the word's definition) at and puts it on the return stack just before the ; That immediately made me cringe because I've spent too many hours trying to balance my stores and fetches on the return stack, but of course you want that here. That's where it will "return" to and ; will bring you there. It's just like the co-routine code you showed earlier. It reminds me that >R and ; are just tools that can be used in any order if the resulting behavior is your desired behavior.

The rest was pretty easy to understand, once you understood what this part does. I haven't noodled my way all the way through your example yet, but I find it very interesting. Also, bonus points for showing off you auxiliary stack. I wrote one, but haven't really used it too much.


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 18, 2021 12:25 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
SamCoVT wrote:
You are breaking my brain (in a good way, I think). I was not aware you could put : in a colon definition (I would normally use CREATE, but I can see how they are different). Now that I think about it, of course you can do that. Forth lets you run with scissors by holding on to the sharp parts if you want.


The reason for including : in the definition of H: is to include the compile time functionality of colon ( : ). In Fleet Forth this means creating a name and smudging it, setting the CONTEXT vocabulary equal to the CURRENT vocabulary, placing on the control flow stack (data stack) the same parameters as colon, and setting the state to compiling. Since Fleet Forth is an ITC Forth, this is not a problem. All words in Fleet Forth have a two byte code field, no exceptions. Even the child words of a CREATE DOES> word have a two byte code field.
In Fleet Forth, colon executes CREATE , which lays down the code field for do-variable. Colon overwrites that code field with do-colon. H: overwrites the code field with do-does. Do-does uses the Forth virtual machine W register to find the address to leave on the data stack and the high level code to push in the Forth virtual machine's IP register.
As an example, here is the source for Fleet Forth's 2CONSTANT , it is a CREATE DOES> word rather than a CREATE ;CODE word because it isn't used much.
Code:
: 2CONSTANT
   CREATE , , DOES>
      2@ ;

And here is what gets compiled.
Code:
SEE 2CONSTANT
2CONSTANT
 11961  8866 CREATE
 11963  7932 ,
 11965  7932 ,
 11967  8025 DOES
 11969  9483    JMP ' DOES> >BODY 21 +
 11972  4762 2@
 11974  2930 EXIT
15
 OK

DOES> compiles DOES and a jump to do-does, which saves the contents of IP to the return stack and fetches the address from W with an offset of 3 (to skip over the JMP) and places it in IP .
Code:
9483 DIS
  9483   252    LDA IP 1+
  9485          PHA
  9486   251    LDA IP
  9488          PHA
  9489          CLC
  9490   254 )Y LDA  W
  9492     3  # ADC
  9494   251    STA IP
  9496          INY
  9497   254 )Y LDA  W
  9499     0  # ADC
  9501   252    STA IP 1+
  9503  9032    JMP ' CREATE >BODY 164 +
23
 OK

Then jumps to do-variable.
Code:
9032 DIS
  9032          CLC
  9033     2  # LDA
  9035   254    ADC  W
  9037          PHA
  9038     0  # LDA
  9040   255    ADC  W 1+
  9042  2632    JMP PUSH
13
 OK

As I showed in the listing for the version of H: for Blazin' Forth, because Blazin' Forth's ] is the compiler as well as changing state to compiling, it was necessary to use CREATE and include what Blazin' Forth's : does at compile time.
Since Tali Forth is an STC Forth, I ported H: and HALT to Durex Forth version 1.6.3 which is, from what I've seen, a subroutine threaded Forth. Colon words have no code field, but CREATE words have a five byte code field. The first three bytes are a JSR to its equivalent of do-does. The next two bytes are a pointer to the high level forth to execute.
This is the source for the port to Durex Forth.
Code:
\ halting words
create dummy
: h:
   :
      ['] dummy dup c@ c, 1+ @ , 0 ,
      here 1+ ,
   does>
      @ >r ;
: (halt)
   r@ 3 + r> 2+ @ 5 + ! ;
: halt
   postpone (halt) postpone recurse ;
   immediate

\ for demos
: spaces  ( +n -- )
   0 max spaces ;

I was able to include colon to do at compile time, whatever Durex Forth's colon does. I first had to define a dummy CREATE word so I could build up a duplicate of it's code field. I duplicated the JSR and padded the last two bytes with zero. That gets patched by whatever DOES> compiles in Durex Forth.
Notice how (halt) has become more complicated.
I left out the error checking which would abort if HALT were used in a regular colon definition. This was to keep the code to a bare minimum for clarity.
I don't know how Tali Forth's CREATE words differ from Durex Forth's, so caution is advised.
I would also like to mention that Durex Forth's version of SEE was practically useless for investigating H: words. However, Durex Forth does have DUMP , which was indispensable.

Quote:
Once I made it past that part, I see your DOES> pulls the address you left off (stored right at the beginning of the word's definition) at and puts it on the return stack just before the ; That immediately made me cringe because I've spent too many hours trying to balance my stores and fetches on the return stack, but of course you want that here. That's where it will "return" to and ; will bring you there. It's just like the co-routine code you showed earlier. It reminds me that >R and ; are just tools that can be used in any order if the resulting behavior is your desired behavior.


Wait till you see Dynamically Structured Codes by M. L. Gassanenko.


Top
 Profile  
Reply with quote  
PostPosted: Thu Dec 02, 2021 3:49 am 
Offline

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

I noticed a problem with my modification to Fleet Forth' (EXPECT) when I tried to use a feature of VICE, the Commodore 64 'mulator I'm using.
The feature in question is the ability to copy text from a file and paste it into VICE. It's supposed to work as if all that text (each line terminated with a carriage return) is typed in.
The modification to (EXPECT) was so I could have the benefit of both the Commodore 64's screen editor and reliable cooperative multitasking without writing my own screen editor. The problem is caused because (EXPECT) is pushing a carriage return back into the keyboard buffer, but it is also emptying it of everything else. This hasn't been a problem for me because I don't keep typing after hitting the return key without seeing the result first. This slight delay on my part keeps the keyboard buffer from having any key presses to lose when the carriage return is stuffed into the buffer. Apparently, VICE keeps placing characters in the buffer as long as there is room.
I've corrected the way the carriage return is added to the buffer and text pasting in VICE works in Fleet Forth.
Code:
// (EXPECT) -- MULTITASKER SUPPORT
CODE (EXPECT)  ( ADR CNT -- )
   $99 LDA
   0= IF
      >FORTH
      COLS ?CR
      BEGIN
         KEY DUP 13 <>
      WHILE
         DEMIT
      REPEAT
      >ASSEM
      $D3 STY  SEI  $C6 LDY
      0= NOT IF
         BEGIN
            $276 ,Y LDA  $277 ,Y STA  DEY
         0= UNTIL
      THEN
      0 ,X LDA  $277 STA  $C6 INC
      CLI  0 # LDY  INX  INX
   THEN
   2 # LDA  SETUP JSR
   XSAVE STX  SPAN 1+ STY
   BEGIN
         N CPY
      0= WHILE
         N 1+ DEC
      0< NOT WHILE  CS-ROT
      THEN
         $FFCF JSR  // CHRIN
         13 # CMP
      0= NOT WHILE
         N 2+ )Y STA  INY
      CS-DUP 0= UNTIL
      N 3 + INC  SPAN 1+ INC
   AGAIN  CS-SWAP
   THEN
      $99 LDA
      0= IF
         BEGIN
            $FFCF JSR  13 # CMP
         0= UNTIL
      THEN
   THEN
   SPAN STY
   XSAVE.NEXT JMP  END-CODE

This version pushes the carriage return in the keyboard buffer without emptying it.
I also came up with another way to solve this problem.
I could factor a new word out of KEY , the headerless word WKEY . WKEY waits till there is a key in the keyboard buffer without removing it. It blinks the cursor and runs a loop with PAUSE while waiting.
Code:
// WKEY KEY
CODE WKEY  ( --   )
   $CC STY
   >FORTH
   BEGIN
      PAUSE $C6 C@
   UNTIL
   >ASSEM
   INY  $CD STY
   BEGIN
      $CF LDA
   0= UNTIL
   $CC STY
   NEXT JMP  END-CODE
: KEY  ( -- C )
   WKEY  ?KEY ;

This version of (EXPECT) only echoes a key to the screen if that key is not a carriage return. It leaves the first carriage return it finds in the keyboard buffer for the Commodore 64 Kernal routine CHRIN , the screen editor.
Code:
// (EXPECT) -- MULTITASKER SUPPORT
CODE (EXPECT)2 ( ADR CNT -- )
   $99 LDA
   0= IF
      >FORTH
      COLS ?CR
      BEGIN
         WKEY $277 C@ 13 <>
      WHILE
         ?KEY DEMIT
      REPEAT
      $D3 OFF
      >ASSEM
   THEN
   2 # LDA  SETUP JSR
   XSAVE STX  SPAN 1+ STY
   BEGIN
         N CPY
      0= WHILE
         N 1+ DEC
      0< NOT WHILE  CS-ROT
      THEN
         $FFCF JSR  // CHRIN
         13 # CMP
      0= NOT WHILE
         N 2+ )Y STA  INY
      CS-DUP 0= UNTIL
      N 3 + INC  SPAN 1+ INC
   AGAIN
   CS-SWAP
   THEN
      $99 LDA
      0= IF
         BEGIN
            $FFCF JSR  // CHRIN
            13 # CMP
         0= UNTIL
      THEN
   THEN
   SPAN STY
   XSAVE.NEXT JMP  END-CODE

As for which solution I'll go for, I'll have to see which one results in a smaller Fleet Forth kernal.


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 07, 2021 2:08 am 
Offline

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

The feature in question is the ability to copy text from a file and paste it into VICE. It's supposed to work as if all that text (each line terminated with a carriage return) is typed in.
The modification to (EXPECT) was so I could have the benefit of both the Commodore 64's screen editor and reliable cooperative multitasking without writing my own screen editor. The problem is caused because (EXPECT) is pushing a carriage return back into the keyboard buffer, but it is also emptying it of everything else. This hasn't been a problem for me because I don't keep typing after hitting the return key without seeing the result first. This slight delay on my part keeps the keyboard buffer from having any key presses to lose when the carriage return is stuffed into the buffer. Apparently, VICE keeps placing characters in the buffer as long as there is room.
I've corrected the way the carriage return is added to the buffer and text pasting in VICE works in Fleet Forth.


I think I have a slight involuntary pause after hitting the return key, then again, it could be that I'm typing to slowly to add a key to the buffer before the carriage return is processed. Either way, this wasn't a problem until using the text pasting feature of VICE, but I suppose it could be a problem for a really fast typist.
I also think I have a better idea of what VICE is doing during the paste operation, but this is just speculation. I think it fills the keyboard buffer, then waits until the buffer is empty before refilling it with the next ten characters to paste.
As for which version of (EXPECT) I used, the second version which doesn't push anything back into the keyboard buffer resulted in a smaller Fleet Forth kernel.
I modified the editor's screen editor (as in screens in blocks) to properly push the right shift character into the keyboard buffer.
Code:
// XED
EDITOR DEFINITIONS
: XED  ( N -- )
   CREATE  ( N -- )
      C,
   DOES>  ( -- )
      C@ C/L * R# !
      0 TEXT C/L PAD C!
      IBUF BUFMOVE (R)
      >ASSEM
      SEI  $C6 LDY
      0= NOT IF
         BEGIN
            $276 ,Y LDA  $279 ,Y STA
            DEY
         0= UNTIL
      THEN
      ASCII" ]" # LDA  3 # LDY
      BEGIN
         $276 ,Y STA  $C6 INC  DEY
      0= UNTIL
      CLI
      >FORTH QUIT ; -2 ALLOT
// XED WORDS 0: - F:  LQ
HEX
 0 XED 0:    1 XED 1:    2 XED 2:
 3 XED 3:    4 XED 4:    5 XED 5:
 6 XED 6:    7 XED 7:    8 XED 8:
 9 XED 9:   0A XED A:   0B XED B:
0C XED C:   0D XED D:   0E XED E:
0F XED F:
FORTH DEFINITIONS

This worked well for copying source from a text file and pasting it into VICE. I could paste it into a Forth screen by listing the relevant screen and positioning the cursor where I wanted the text to start.
The downside is that the keyboard buffer would sometimes spill over into adjacent memory. It affected the pointer for bottom of memory for the C64 kernal. This isn't really a problem since that pointer isn't used in Forth. The top of memory pointer for the C64 kernal is just above the pointer for the bottom of memory and the one which concerned me. The C64 top of memory pointer normally points to the first address past the usable RAM. If a file is opened on device 2 on the C64, it will be for RS-232 and the C64 will create two 256 byte buffers which end at the top of memory. Fleet Forth's CONFIGURE sets the top of memory pointer to point to the start of the block buffer table so if these two buffers are created, they will not overwrite part of the topmost block buffer. If a keyboard buffer overflow occurs due to XED stuffing three right shift characters into the keyboard buffer and the top of memory pointer is altered, opening a file on device 2 could corrupt the block buffer table or some blocks. There is an address the C64 uses to determine the size of the keyboard buffer, which is used by the ISR to keep from placing too many characters in the buffer. The version of VICE I'm using seems to ignore that value and uses the default size of ten.
I decided to alter (EXPECT) once again to work better with my Forth screen editor. I added the variable 3R to the Fleet Forth kernel source and added the following to (EXPECT) after 'COLS ?CR' .
Code:
                3R C@
      IF  " ]]]" COUNT DTYPE  THEN

Note: those are not three ] characters in the string above. they are three right shift key characters. On the C64 screen they display in reverse video. In the print dump file, they display as shown above.
I also changed this:
Code:
      $D3 OFF
      >ASSEM

to this:
Code:
      >ASSEM
      $D3 STY  $D4 STY  3R STY

which does the same thing as before while also switching 3R off.
Here is the full source for the new (EXPECT)
Code:
// (EXPECT) -- MULTITASKER SUPPORT
CODE (EXPECT)  ( ADR CNT -- )
   $99 LDA
   0= IF
      >FORTH
      COLS ?CR  3R C@
      IF  " ]]]" COUNT DTYPE  THEN
      BEGIN
         WKEY $277 C@ 13 <>
      WHILE
         ?KEY DEMIT
      REPEAT
      >ASSEM
      $D3 STY  $D4 STY  3R STY
   THEN
   2 # LDA  SETUP JSR
   XSAVE STX  SPAN 1+ STY
   BEGIN
         N CPY
      0= WHILE
         N 1+ DEC
      0< NOT WHILE  CS-ROT
      THEN
         $FFCF JSR  // CHRIN
         13 # CMP
      0= NOT WHILE
         N 2+ )Y STA  INY
      CS-DUP 0= UNTIL
      N 3 + INC  SPAN 1+ INC
   AGAIN
   CS-SWAP
   THEN
      $99 LDA
      0= IF
         BEGIN
            $FFCF JSR  // CHRIN
            13 # CMP
         0= UNTIL
      THEN
   THEN
   SPAN STY
   XSAVE.NEXT JMP  END-CODE

and here is the new source for XED
Code:
// XED
EDITOR DEFINITIONS
: XED  ( N -- )
   CREATE  ( N -- )
      C,
   DOES>  ( -- )
      C@ C/L * R# !
      0 TEXT C/L PAD C!
      IBUF BUFMOVE (R)
      3R ON  QUIT ; -2 ALLOT

This arrangement works well without the need to stuff any characters into the keyboard buffer and the Fleet Forth kernel is still smaller than the Blazin' Forth kernel.


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 11, 2021 1:47 am 
Offline

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

I've decided to rename 3R to RC for 'right cursor'. I've also changed this:
Code:
              3R C@
      IF  " ]]]" COUNT DTYPE  THEN

to this:
Code:
              RC C@ $D3 C!

$D3 is the location in zero page the C64 Kernal uses for the cursor's horizontal position, or column. $D6 is for the vertical position. Normally, these locations should be changed by using the C64 Kernal's PLOT routine, which has nothing to do with graphics (in spite of the name), just plotting the position of the text cursor. However, directly altering $D3 to alter the cursor's column position shouldn't be a problem as long as it stays on the same logical line.
Those familiar with the C64 will know what I mean by 'logical line'. The C64 has a screen which is 40 columns wide by 25 lines. These 25 lines are the 'physical lines'. Two adjacent physical lines can be joined together to form an 80 column logical line.
Here is the source for the latest version of (EXPECT) .
Code:
// (EXPECT) -- MULTITASKER SUPPORT
CODE (EXPECT)  ( ADR CNT -- )
   $99 LDA                     // Which device?
   0= IF                       // if keyboard
      INY  $292 STY            // enable auto down scrolling
      >FORTH
      COLS ?CR
      RC C@ $D3 C!             // bump cursor RC places
      BEGIN
         WKEY $277 C@ 13 <>    // wait till keypress in buffer
      WHILE                    // while not a CR
         ?KEY DEMIT            // echo to screen
      REPEAT
      >ASSEM
      $D3 STY  $D4 STY  RC STY // bump cursor to left margin &
   THEN                        // switch off quote mode and RC
   2 # LDA  SETUP JSR
   XSAVE STX  SPAN 1+ STY
   BEGIN
         N CPY
      0= WHILE
         N 1+ DEC
      0< NOT WHILE  CS-ROT
      THEN
         $FFCF JSR  // CHRIN
         13 # CMP
      0= NOT WHILE
         N 2+ )Y STA  INY
      CS-DUP 0= UNTIL
      N 3 + INC  SPAN 1+ INC
   AGAIN
   CS-SWAP
   THEN                        // did not receive CR
      $99 LDA                  // if from keyboard
      0= IF                    // read rest of line from screen
         BEGIN                 // and discard
            $FFCF JSR  // CHRIN
            13 # CMP
         0= UNTIL
      THEN
   THEN
   SPAN STY
   XSAVE.NEXT JMP  END-CODE
' (EXPECT) IS EXPECT

Here is the new source for XED .
Code:
// XED
EDITOR DEFINITIONS
: XED  ( N -- )
   CREATE  ( N -- )
      C,
   DOES>  ( -- )
      C@ C/L * R# !
      0 TEXT C/L PAD C!
      IBUF BUFMOVE (R)
      3 RC C!  QUIT ; -2 ALLOT



Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 12, 2021 10:38 pm 
Offline

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

I've moved the Fleet Forth download to the head post.
When I'm ready to upload the new and improved Fleet Forth, I'll upload it to the head post as well. Before I do that, I would like to simplify the source for Fleet Forth's metacompiler.
I'm thinking about starting a new topic 'Fleet Forth Metacompiler Design Considerations'.
Extending the metacompiler to target compilation is also something I've been thinking about.


Top
 Profile  
Reply with quote  
PostPosted: Thu Dec 16, 2021 2:47 am 
Offline

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

I've considered adding :NONAME to Fleet Forth's system loader so I could define headerless words; words having no use outside of the limited scope where they are used. In this context I am referring to words defined in the process of working in a typical Forth environment, not metacompiling. The Fleet Forth metacompiler maintains a target Forth vocabulary on the host, so even headerless words in the target system have a header on the host.
It would be useful if a headerless word could also be recursive; however, the definition of RECURSE in Fleet Forth is:
Code:
: RECURSE
   LATEST NAME> , ; IMMEDIATE

The word LATEST returns the address of the name field of the latest word in the CURRENT vocabulary.
Code:
: LATEST  ( -- NFA )
   CURRENT @ @ L>NAME ;

This does not work for a headerless word. Without a header, it is not in any VOCABULARY .
I added a 'system value' to Fleet Forth. The value LAST . CREATE sets this value to the CFA of the latest word created. RECURSE now uses LAST .
Code:
: RECURSE
   LAST , ; IMMEDIATE

LAST can be invalidated by FORGET and EMPTY , but that doesn't matter. LAST is only needed during the creation of a colon definition, and then only when recursion is used. Since LAST does not depend on the ability to find a name, it can be set by :NONAME which now allows RECURSE to be used in headerless definitions.
LATEST is still used in words like IMMEDIATE to only affect words with a header.
Oddly enough, with this addition, Fleet Forth's kernel is now the same size as Blazin' Forth's kernel.


Top
 Profile  
Reply with quote  
PostPosted: Thu Dec 16, 2021 4:27 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
In stead of using :NONAME, can one not just use HERE, then write some code, leave HERE on the stack so that when some future word definition needs it, the address to return to is already there. You wouldn't even need to create a colon definition or use :NONAME.

It would just be:

Code:
HERE 0 , CODE
.
.
.
ENDCODE


The 0 , would be needed since CODE does a -2 allot, which stores the CFA which points to the PFA (or body) of a regular word definition. The address for HERE gets left on the stack and is used, when needed, in the assembler calculation. Which I believe needs to be HERE+2 to skip past the CFA pointer.


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

All times are UTC


Who is online

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