Jump to content
IGNORED

autoboot issue in turboforth


Recommended Posts

Hi again

I have been testing different solutions to my menu task, using the solution about 18 entries into this discussion. This theBF solution is lean and mean and, as it turns out, also about as fast as any alternatives, that I can come up with. But I still had a lot of fun, and I have learned a few things on the way. For a moment I thought I had it beat for speed with my best attempt at printing entire substrings with one line, instead of simply printing one character at a time. But after implementing some of my newfound knowledge on the theBF solution, it took the lead again as far as short strings are concerned. Oh well...a tie, I suppose.

 

Here are the main competitors:

My best shot... index 136 for short substrings. 424 for long strings

 0.0 VALUE SEP 0 VALUE IDX 0 VALUE VLOC 0 VALUE CNT                .            
 1..                                                               .            
 2.: MENU ( ADRS LEN SEP X Y -- )                                  .            
 3.  XMAX * + TO VLOC 0 TO IDX TO SEP                              .            
 4.  0 DO                                                          .            
 5.    DUP I + C@ SEP = IF                                         .            
 6.      DUP IDX + VLOC SWAP CNT VMBW                              .            
 7.      XMAX +TO VLOC I 1 + TO IDX 0 TO CNT                       .            
 8.    ELSE                                                        .            
 9.      1 +TO CNT                                                 .            
10.    THEN                                                        .            
11.  LOOP                                                          .            
12.  IDX + VLOC SWAP CNT VMBW 0 TO CNT ;                           .            
13.                                                                .            
14.: TEST 2 GMODE 100 0 DO S" THIS IS A LARK" 32 5 10 MENU LOOP    .            
15.  $83D6 @ . ;            

 

The enhanced theBF solution... (I added some code for cursor control and the ability to use any separator character to level the field...) index 128 for short strings. 476 for long strings.

  0.0 VALUE Y 0 VALUE X 0 VALUE SEP                                 .            
 1.                                                                .            
 2.: MENU ( ADRS LEN SEP X Y -- )                                  .            
 3.  TO Y TO X TO SEP X Y GOTOXY                                   .            
 4.  0 DO DUP C@ DUP SEP =                                         .            
 5.  IF 1 +TO Y X Y GOTOXY DROP                                    .            
 6.  ELSE EMIT THEN 1+ LOOP DROP ;                                 .            
 7.                                                                .            
 8.: TEST                                                          .            
 9.  100 0 DO S" THIS IS A LARK" 32 15 15 MENU LOOP                .            
10.  CR $83D6 @ . ;           

 

 So what did I learn?

1) Using a VARIABLE seems to be slower than using a VALUE. Noticeably slower!

2) If you start out with more than a few items on the stack, it is imperative to reduce the load by storing them in some VALUE type words. Otherwise the stack manipulation gets really complicated very fast. The 'rule of three' - items on the stack, that is, seems to cover it. as pointed out earlier in the discussion.

3) The added complexity of code to be able to use VMBW to write directly to the screen pays off, if you need longer lines with, say, a total of 40 characters. But for short menu lines the theBF solution wins. Using direct writing to screen means you also have to consider the screen mode, because there is no cursor control.

4) Using TYPE to output can never beat EMIT. I think TYPE uses EMIT internally anyway.

5) Using the PAD to store temporary data may or may not work. Some of Turboforth's own words use it too, it seems. So your data may get overwritten. Not good!

6) Reserving an area using ALLOT inside a word and resetting the value of HERE to free the allotted memory seems to work fine, as long as you keep it inside the word. This is a better alternative than to use the PAD. My final solution did not use this technique, but it was a contender for some time.

7) Using the return stack to store temporary values is a bad idea if you have to retrieve a value inside a DO LOOP, that is stored outside the loop. Loops use the return stack too. So it is actually rather obvious!

8 ) When developing code in a block... Always copy the block to some other block before you change working code. It may not work afterwards and crash on you. I got burned several times. I use this sequence to do it:

1 BLOCK PAD 1024 VMBR (stores block 1 in the PAD) followed by 2 BLOCK PAD 1024 VMBW (stores the PAD content (1024 bytes) to block 2.

9) Keep tabs on the content of the stack on paper. I thought for a while, that I could keep track in my head - it is not happening!

10) Don't spend so much time on Forth! You have more important things to do, right? Just one more try... 😄

 

Thanks to all for help and advices...

 

regards 

Jesper

  • Like 3
Link to comment
Share on other sites

Thanks for sharing your work with us Jesper. It's wonderful for our small group of Forth enthusiasts here to see new thoughts on this unusual language.

Your background I think has allowed you to move quickly. 

 

"my" solution by the way is really the work of other talented people over the last 30 years that I have tried to understand. ( /string scan and skip and using "stack string pairs")

It's only now after forcing myself to really dig to build a system, that get a bit more of this Forth philosophy of programming. 

 

 

  • Like 2
Link to comment
Share on other sites

20 hours ago, jschultzpedersen said:

1) Using a VARIABLE seems to be slower than using a VALUE. Noticeably slower!

A little background on why this is so might be helpful. 

 

The dirty little secret about Forth as it is implemented commonly (indirect threading) is that your program can spend as much as 50% of the time running the 3 instruction address interpreter. (commonly called NEXT) This is because many of the Forth primitives are made from less than 4 instructions, so NEXT is a significant part of the runtime. 

 

When we use a variable we run code to put the address on the the stack, then NEXT runs. 

Putting the address on the stack is 2..3 instructions (depending on implementation)  + NEXT = 6 instructions. 

 

Then say we run fetch which is followed by NEXT as well.

Fetch is 2 instructions + NEXT = 5 instructions.  Total= 3+3+2+3= 11 instructions. 

 

A VALUE gets the address and fetches the contents in one sequence of instructions and then runs NEXT. Total =  3+2+3 = 8 instructions or about 37% faster. 

 

This is why some Forth systems use "directed threaded code" where NEXT is only 2 instructions so the whole system runs a 10..20% faster.

The down side with direct threading is each colon word uses 4 extra bytes to branch into the code. So not ideal for a machine like TI-99. 

 

Modern Forth systems like SwiftForth and VFX Forth (commercial) and Mecrisp Forth (shareware)  generate native code for full machine speed.

The downside there is the compiler is much more complicated but on big machines there is lots of space. Mecrisp kernel is still only about 16K. :)  

 

 

 

  • Like 2
Link to comment
Share on other sites

Hi

 

There was this issue with autoboot and LOAD and a stack out of sync. A good solutions seems to be to add the words SP@ SP! right after the LOAD words. Then it works with autoboot as well as with a manual LOAD of block 1. This is based on the assumption, that any LOAD actions do not leave anything on the stack. The code I have in block 1 for my menu experiment and its result then looks like this. FRAME clears an area and draws a border, and KEYLOOP keeps looping until you press a valid key.

 

 0.97 LOAD                                                         .            
 1.94 LOAD                                                         .            
 2.95 LOAD                                                         .            
 3.                                                                .            
 4.SP@ SP! \ * THIS FIXES STACK PROBLEM IN V.1.2 WHEN AUTOBOOTING  .            
 5.                                                                .            
 6.10 10 20 10 64 32 FRAME                                         .            
 7.S" 1. OPTION 1@2. OPTION 2@3. OPTION 3@4. EXIT" 64 15 13 MENU   .            
 8.52 49 KEYLOOP 

                                                                                
          @@@@@@@@@@@@@@@@@@@@                                                  
          @                  @                                                  
          @                  @                                                  
          @    1. OPTION 1   @                                                  
          @    2. OPTION 2   @                                                  
          @    3. OPTION 3   @                                                  
          @    4. EXIT       @                                                  
          @                  @                                                  
          @                  @                                                  
          @@@@@@@@@@@@@@@@@@@@                                                  
                                                                                

While we are at it... Is there an official upper limit to the length of the TIB buffer? The manual says it is normally 80 characters wide, but apparently the length can be changed by changing the value of CPU address $A04A. But even if I set it to, say, 200, I can see that any string above length 167 causes a crash. So I am probably overwriting something important in VDP ram above that. I am interested, because it would be convenient to be able to feed extra long strings to my MENU word to display bigger text fields and not just menus.

 

regards

Jesper                                                                                
                                                                                
                                                                                
      

  • Like 3
Link to comment
Share on other sites

This is one of those few times when Mark's decision to keep block buffers in VDP RAM gets in your way. 

You might want to just blit the selected block into CPU RAM at PAD, or the buffer of your choosing, and process it from there. 

 

something like:

: RAMBLOCK ( n -- addr)  BLOCK PAD 1024 VMBW PAD ; 

 

Mark or Lee will have to weigh in on any surprises that might bring with it in TF. Not my area of expertise.

Not sure how you point the TF parser to that memory buffer so that WORD operates as expected. 

Might be as simple as setting BLK and resetting >IN , but since blocks are in VDP RAM that might not work.

If you are not using the internal parser, WORD, then it should work. 

 

-OR-

 

It's also possible to rebuild SKIP SCAN and your other string words to use V@ (it is a byte operation) instead of C@ and process the bytes directly from VDP RAM via the DATA stack as a byte by byte buffer. :) 

There is a performance hit but it's not as much as one would expect. I think I measured it at about 15% slower doing strings in VDP RAM, worst case.

 

  • Like 4
Link to comment
Share on other sites

On 4/10/2023 at 7:53 AM, jschultzpedersen said:

While we are at it... Is there an official upper limit to the length of the TIB buffer? The manual says it is normally 80 characters wide, but apparently the length can be changed by changing the value of CPU address $A04A. But even if I set it to, say, 200, I can see that any string above length 167 causes a crash. So I am probably overwriting something important in VDP ram above that. I am interested, because it would be convenient to be able to feed extra long strings to my MENU word to display bigger text fields and not just menus.

 

You can use a CRAM buffer and EVALUATE it, with CRAM-address and string-length on the stack:

 

S" CR 1 2 3 + + ." EVALUATE

 will display

6 ok:0

 

You just cannot use EVALUATE when LOADing blocks because EVALUATE changes the source for INTERPRET from VRAM to CRAM until it is finished. Though the source code comments for EVALUATE state this, it happens to be false. You can, in fact, use EVALUATE while LOADing blocks (see the TurboForth creator, @Willsy’s, post below)! :)

 

...lee

Edited by Lee Stewart
CORRECTION
  • Like 2
Link to comment
Share on other sites

The solution to writing long texts formatted inside a frame is surprisingly simple in combination with a MENU type function. Store the data in a block with a unique separator character. Then do a transfer from the blocks VDP ram to a temporary storage in CPU ram - the PAD is useful here. This combines the convenience of storing the data externally in a compact way with the efficient RAMBLOCK type action and the autoformatting capability of the MENU function. This is exactly what I was looking for.

 

Block 100   CHANGED          Mode:OVER                                          
   0         1         2         3         4         5         6                
  ..................................................................            
 0.NOW IS A GOOD TIME TO WRITE A VERY LONG SENTENCE,@THAT PROVES TH.            
 1.E THEORY, THAT I CAN PROCESS A STRING OF@ALMOST ANY LENGHT IN TH.            
 2.IS SIMPLE WAY@@IS THIS NOT WONDERFULL?.                         .            
 3.                                                                .            

                                                                                
ok:0                                                                            
FLUSH ok:0                                                                      
100 BLOCK PAD 165 VMBR ok:0                                                     
PAD 165 64 10 10 MENU ok:0                                                      
.                                                                               
                                                                                
                                                                                
                                                                                
          NOW IS A GOOD TIME TO WRITE A VERY LONG SENTENCE,                     
          THAT PROVES THE THEORY, THAT I CAN PROCESS A STRING OF                
          ALMOST ANY LENGHT IN THIS SIMPLE WAY                                  
                                                                                
          IS THIS NOT WONDERFULL?                                                
                                                                                
                                                                                
Regards

Jesper                                                                                
                                                                                

  • Like 4
Link to comment
Share on other sites

15 hours ago, Lee Stewart said:

 

You can use a CRAM buffer and EVALUATE it, with CRAM-address and string-length on the stack:

 

S" CR 1 2 3 + + ." EVALUATE

 will display

6 ok:0

 

You just cannot use EVALUATE when LOADing blocks because EVALUATE changes the source for INTERPRET from VRAM to CRAM until it is finished.

 

...lee

 

FWIW, you can use EVALUATE in a block. It saves TIB, >IN etc. to the return stack and restores them after use IIRC.

 

 

  • Like 2
Link to comment
Share on other sites

AHA!!! :grin:

 

Yeah, here's it's implementation. It's written in Forth. You were quite right to comment on it not being used in a block, Lee. The comment appears to be erroneous. My guess/fading memory tells me my initial attempt at the implementation had a limitation, which I subsequently fixed but neglected to update the comment. 

 

; EVALUATE ( i*x c-addr u -- j*x)
; evaluates the string specified by c-addr u 
; the interpretation state is stored before evaluation and restored afterwards
; should not be directly called within a block (or when BLK>0) <<<---- erroneous comment. Ignore this!
evalh   data numbrh,8
        text 'EVALUATE'
eval    data docol
        data in_,fetch,rspush
        data blk,fetch,rspush
        data span,fetch,rspush
        data tib_,fetch,rspush
          
        data in_,store0             ; zero >IN
        data blk,store0             ; zero BLK
        data span,store             ; load #tib with u
        data tib_,store             ; load tib with c-addr

        data litm1,lit,source,store ; set SOURCE-ID to -1
        data interp                 ; call interpreter
        data lit,source,store0      ; zero SOURCE-ID
        
        data rspop,tib_,store
        data rspop,span,store
        data rspop,blk,store
        data rspop,in_,store
        data exit

 

  • Like 3
Link to comment
Share on other sites

I guess there are not many ways to do this. Its similar to mine. 

Difference is the ANS "SOURCE" concept hides TIB and SPAN and there is  SOURCE-ID for console versus files.

I set SOURCE-ID with the file handle, but I don't yet have a seamless method for text redirection from files or console.

 

: EVALUATE ( c-addr u -- )
           SOURCE-ID ON
           SOURCE 2>R
           >IN @ >R
           INTERPRET
           R> >IN !
           2R> 'SOURCE 2!
           SOURCE-ID OFF ;

 

No sooner do I post this do I realize I should be saving SOURCE-ID on the return stack as well..

Onto the bug list is goes. 

Edited by TheBF
comment
  • Like 4
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...