Jump to content
IGNORED

autoboot issue in turboforth


Recommended Posts

Hi

I have run into a problem with a word, that works on it own, but not while used during autoboot. Here is the code in block 1...

 

: MENU ( X Y SEP ADRS LEN -- )

TIB @ -ROT DUP SPAN ! 0 >IN ! VMBW >R

BEGIN

R@ WORD DUP

WHILE

>R >R 1 + 2DUP GOTOXY R> R> TYPE

REPEAT

2DROP 2DROP R> DROP ;

 

PAGE 10 10 32 S" THIS IS A TEST" MENU

 

The result should be:

 

THIS

IS

A

TEST

 

... printed starting at X Y.

 

The code is used to print a menu starting at location X Y and with a part of the string separated by the SEP(arator) character. As a standalone word it performs as expected, but if included in an autoboot sequence, it instead prints the first LEN characters of the block from where it is called.

 

Here is what it is supposed to do:

I initialize the TIB address in VDP and the string ADRS and LEN, use a copy of the LEN to initialize the SPAN value manually, set the >IN value to 0 and transfers the content of the string to the buffer in VDP used by WORD. I also store the LEN value on the return stack. WORD then finds the first section of the string based on the SEP. The LEN is duplicated to be used as a flag for the WHILE-REPEAT loop. If there is a substring (LEN > 0) I temporarily stores the address and length of the substring on the return stack, then uses a copy of the X Y values to locate the cursor, retrieves the substring address and length from the return stack and types the substring. When WORD returns 0 0 the WHILE-REPEAT loop is ignored, and I clean up the stack and return stack, and everyone is happy... except...

 

The characters in the string I wish too print is correctly put at the TIB @ address and remains there at all time, but somehow during the autoboot process WORD grabs data from the first line in the block instead of from the buffer. The same thing happens if a do a 1 LOAD command. I suppose the autoboot capability basically does a 1 LOAD on DSK1, so it makes sense.

 

I am relatively new to TurboForth. So any help is appreciated.

 

PS. Is there anywhere, where I can get my hands on a TurboForth cartridge or an image, that can be used with FG99? Or a guide to convert the images in Classic99 to a FG99?

 

regards

Jesper

  • Like 1
Link to comment
Share on other sites

46 minutes ago, jschultzpedersen said:

Hi

I have run into a problem with a word, that works on it own, but not while used during autoboot.

. . .

 

The problem has to do with the fact that input to the interpreter is coming from block 1 upon autoload and from the TIB when typing on the keyboard. When you change >IN while autoloading, you change where the interpreter continues to read block 1, not the TIB. @Willsy will probably drop by later to give you a better explanation.

 

...lee

  • Like 3
Link to comment
Share on other sites

Hi

Ahh ... thanks. That explains it. Further experiments appear to show, that the issue is with the LOAD word. If I move the code to another block and do a LOAD of that block, it also behaves as you say. That puts up the question - how to trigger that first menu using the MENU word. I could hard code the menu using GOTOXY and ." WORDS, but I had hoped to use a generic WORD like MENU to make all menus from a string argument.

 

There may be a trick. First (late night) tests indicate that it works - somewhat.

 

Content of block 1:

2 LOAD     loads a block with the MENU definition

3 LOAD     loads a block with empty words named to match each line in the menu (like... : THIS ; : IS ; : A ; : LARK ;)

4 LOAD     loads a block with the line THIS IS A LARK (4 valid words with single spaces in between) in the first line to the left. Then call the WORD MENU with parameters including any string of the same length as in...

10 10 32 S" 12345678901234" MENU

 

Now, as you told me, it reads from the top line of block 4, and behold - a menu appears! Unfortunately the problem appears again the next time I need a menu. So it is a bit pointless., and even more so, because it only allows single words in a line.

 

Is it the method of writing directly to the TIB address, that is flawed? WORD was a convenient way of interpreting the content of a string into sub strings, but as I understand it, it takes its data from the TIB address in VDP ram. I suppose I could make something similar, that took its data from CPU ram and pass the string address and length to it.

 

regards

Jesper

  • Like 1
Link to comment
Share on other sites

I am not at home so I can't test anything but I can say that playing with TIB is not something you normally do unless you need to re-work the the way the interpreter works. 

 

I don't how the boot exact process of TurboForth but if I think about putting a menu on the screen from a BLOCK I think of ways that use very little code. 

Type the menu into the block with the editor with each item on a separate line. This wastes 24 chars per line but it's simple. 

Then something like this would print as many lines as you want up to 15 line. 

 

: .MENULINE    ( n -- )  64 *  1 BLOCK + 40  TYPE  ; 

: MENU   ( n  X Y -- ) GOTOXY  0  DO I .MENULINE  CR  LOOP ;   ( n is the number of lines you want to write)

 

You also get fancy and organize the text in the block so that when it is written to VDP RAM it comes out the way you want it.

Then it's just one line using VMBW (VDP multiple byte write) 

This will be instantaneous.  You will need to lookup the VDP address of the screen in turboForth. It might be zero. (0)

Assuming it is zero here is the code. 

 

: MENU      1 BLOCK  0   960 VMBW ;  :) 

 

This is literally blasting 960 bytes from the block buffer to VDP RAM. 

     

 

 

  • Like 2
Link to comment
Share on other sites

Hi

 

One of my earlier attempts was, just as you say, to use VMBW and VMBR to move data to the screen from a block. I made a generic word called VMOVE like this to be able to move data around in VDP ram using the PAD as a temporary storage...

: VMOVE ( TO FROM LEN -- )

>R PAD R@ VMBR PAD R> VMBW ;

 

With this I could copy one block to another on the same disk image just by writing: BLOCK 2 BLOCK 3 1024 VMOVE, that copies the content of block 3 to block 2. Or I could copy the content of the screen (assuming VDP address 0 as start.) by saying 2 BLOCK 0 960 VMOVE (similar to your example), or do it the other way round to display stuff on the screen. All would be well, as long as I remembered to do a UPDATE FLUSH of the buffers afterwards to save changes permanently. My MENU word was an attempt to make a generic mechanism for typing menus on screen without having to format, print and save a screen to a block manually before actually using it.

 

I think I will focus on creating a word, that does, what WORD does, but works from a selected address in CPU ram instead of from the TIB address. In this way I can avoid the practice of messing with the TIB content and SPAN and >IN.

 

Thanks to all for input.

 

regards

Jesper

  • Like 3
Link to comment
Share on other sites

Ah now I remember. Mark put his disk buffers in VDP RAM.

Lee and I put disk buffers in RAM which is slightly more convenient.

 

I was going to say my system source code  could help you, but it has some CODE words that are not in TurboForth and would need some re-writing in TF Assembler.

It is messy because I keep playing with experiments but here is the hilevel code. You can search for PARSE and PARSE-WORD to see a possible solution. 

https://github.com/bfox9900/CAMEL99-ITC/blob/master/SRC.ITC/HILEVELX.HSF

 

The topline is ISO Forth has  PARSE ( char -- TIB len)  which lets you take the string and do whatever you like. 

I added PARSE-WORD ( char -- TIB len)  which skips leading spaces like word but still returns the input buffer string ( addr,len) on the data stack. 

 

It might be easier to just use WORD and take the counted string from HERE where WORD puts it and "place" it in a new string.

 

EDIT: 

if you don't have 2dup:

: 2DUP  OVER OVER ; 


 

CREATE A$    80 ALLOT 

: PLACE ( src n dst -- ) 2DUP C! 1+ SWAP CMOVE ;

: PARSE$  ( addr -- )  1 WORD   COUNT  ROT  PLACE ;


A$ PARSE$ THIS TEXT SHOULD GO INTO A$ 

 

 

 

 

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

Hi

There are a number of extensions to the standard vocabulary in TF, that can be loaded. I have not investigated much into their capabilities, and it may well be, that they provide features, that I am reinventing for myself.

 

As for moving data around in CPU ram, in VDP ram, and between the two, I have so far encountered VMBR, VMBW and CMOVE. But I have not seen a word to move data between two potentially overlapping VDP memory areas, which is why I cooked up the VMOVE word Please let me know if anything better is available.

 

I learned from your code example, that 1 WORD is a good way of getting string input and transferring it to something like a reserved memory area pointed to by A$.

But I still have problems with the combination of LOAD (autoload) and WORD as in this example

 

Content of block 6:

A$ CREATE   80 ALLOT
: TEST3 ( ADRS -- )
1 WORD ROT SWAP CMOVE ; ( ** This code simply moves input from TIB to the word buffer and then to adrs in CPU ram )

 

An example of code in block 1 loaded by autostart:

( * HERE IS A COMMENT * )
6 LOAD
A$ TEST3 THIS IS A TEST
A$ 14 TYPE

 

This produces no output. Any code after the third line is ignored. But if I then manually type: 

A$ 14 TYPE

the output is: THIS IS A TEST

 

So A$ is initialized as expected by the TEST3 word. This implies that LOAD loses track of further content of a block once WORD is used. Probably because it messes with >IN, that is also used by LOAD, as hinted in an earlier entry in this discussion. So I have to avoid WORD.

 

Here is a solution that works:

 

Change block 6 to:

A$ CREATE   80 ALLOT
: TEST4 ( SRC LEN DST -- )
SWAP CMOVE ;

 

Change block 1 to:

( * HERE IS A COMMENT * )
6 LOAD
S" THIS IS A TEST" A$ TEST4
A$ 14 TYPE

 

Autobooting will now produce the output: THIS IS A TEST

 

This means LOAD is not interrupted processing the block, and I can include further calls to words and build my menu. Life is good!

 

PS.
My sources of information comes what I can find on turboforth.net, in the pages of Atariage, and from the two excellent books by Leo Brodie (Starting Forth & Thinking Forth). Unfortunately I know of no persons within physical range here in Denmark, that has this interest in the TI99 apart from myself. So it gets a little lonely sometimes.

 

regards
Jesper

  • Like 1
Link to comment
Share on other sites

As you have discovered, there are many ways to skin this cat. You could also do something like this:

 

: A$. 
   ." THIS IS A TEST"  ;
CR A$. CR

 

Of course, simply including just the “print string” ( ." ) code accomplishes the same thing.

 

For your menu solution, you might try something like this:

 

: MENU1
   ." FIRST" CR
   ." SECOND" CR
   ." THIRD" CR
   ." FORTH" CR  ;

 

Pardon the intentional pun. :waving:

 

...lee

  • Like 1
  • Haha 2
Link to comment
Share on other sites

4 hours ago, jschultzpedersen said:

Hi

There are a number of extensions to the standard vocabulary in TF, that can be loaded. I have not investigated much into their capabilities, and it may well be, that they provide features, that I am reinventing for myself.

 

As for moving data around in CPU ram, in VDP ram, and between the two, I have so far encountered VMBR, VMBW and CMOVE. But I have not seen a word to move data between two potentially overlapping VDP memory areas, which is why I cooked up the VMOVE word Please let me know if anything better is available.

 

I learned from your code example, that 1 WORD is a good way of getting string input and transferring it to something like a reserved memory area pointed to by A$.

But I still have problems with the combination of LOAD (autoload) and WORD as in this example

 

Content of block 6:

A$ CREATE   80 ALLOT
: TEST3 ( ADRS -- )
1 WORD ROT SWAP CMOVE ; ( ** This code simply moves input from TIB to the word buffer and then to adrs in CPU ram )

 

An example of code in block 1 loaded by autostart:

( * HERE IS A COMMENT * )
6 LOAD
A$ TEST3 THIS IS A TEST
A$ 14 TYPE

 

This produces no output. Any code after the third line is ignored. But if I then manually type: 

A$ 14 TYPE

the output is: THIS IS A TEST

I just realized I could run TF on JS99er. :)

 

I did a quick test and learned something. 

WORD in TF is not Forth-83 standard.  It returns an address, length pair. ( a stack string)

FORTH-83 WORD returns a byte counted string. 

 

so  this works.

   1 WORD This is a test  
CR TYPE  This is a test ok

 

There is an old joke that goes "if you have seen one Forth... you have seen one Forth." 

All the DIY systems have differences that you must get familiar with. 

 

The word PLACE that I wrote up earlier has become part of the 2012 unofficial standard because it was used in many systems. 

PLACE takes a stack string pair  and stores it memory as a byte counted string.  

It is the reciprocal  of COUNT which takes a counted string address and converts it to a stack string pair. 

 

These are the most common way to deal with storing and using strings but there are Forth systems written in C that use 0 delimited strings

and there are systems that change the count byte to a cell so you can have strings a long as memory.  :) 

 

I know of a system that renames PLACE to $!  and  COUNT to $@  which is arguably more consistent with !  and @ for integers. 

 

 

 

 

 

 

 

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

18 minutes ago, TheBF said:

I did a quick test and learned something. 

WORD in TF is not Forth-83 standard.  It returns an address, length pair. ( a stack string)

FORTH-83 WORD returns a byte counted string. 

 

Then, of course, there is figForth’s returning nothing except the knowledge that the counted string is at HERE , as with TI Forth and fbForth.

 

...lee

  • Like 4
Link to comment
Share on other sites

Hi

 

Well, it has not bothered me, since I only know how TurboForth behaves 😀 I run TF from Classic99. I would love to get my hands on a TF cartridge or an image that works with FG99. But for now it is Classic99.

 

Anyway, here is a working solution to the task I set myself. The INNER word takes a string and a separator from some global values initiated in the OUTER word, and returns  the address and length of the sub string, which I use to print the section. Eventually it runs out of text and returns 0 0. All very similar to the behaviour of WORD. But, as it all happens in CPU ram, it works during autoload, unlike WORD which apparently does not coexist very well with LOAD.

 

There is no formatting in OUTER yet. And I will see if I can get rid of some of the global values - just on principle.

 

 0.. VALUE NN 0 VALUE MM 0 VALUE MYADRS 0 VALUE MYLEN 32 VALUE SEP             
 1.: INNER                                                                     
 2.  0 TO MM                                                                   
 3.  BEGIN                                                                     
 4.    MYADRS MM + C@ SEP <> NN MYLEN < AND                                    
 5.  WHILE                                                                     
 6.    1 +TO MM 1 +TO NN                                                       
 7.  REPEAT                                                                    
 8.  NN MYLEN <= IF MYADRS MM ELSE 0 0 THEN                                    
 9.  MM 1 + +TO MYADRS 1 +TO NN ;                                              
10.: OUTER ( ADRS LEN SEP -- )                                             
11.  TO SEP TO MYLEN TO MYADRS 0 TO NN                                        
12.  BEGIN                                                                     
13.    NN MYLEN <                                                              
14.    WHILE INNER CR TYPE REPEAT ;                                            
15.S" DETTE ER VIRKELIG FINE SAGER" 32 OUTER  

 

regards

Jesper

  • Like 4
Link to comment
Share on other sites

Try this:

 

: MENU   ( adr len -- )
   0 DO           \ loop through the string
      DUP         \ maintain address on the stack
      C@ DUP      \ get character and copy for test
      BL = IF     \ is it a blank?
         DROP CR  \ yes..drop it and emit <CR><LF>
      ELSE
         EMIT     \ no..emit the just-read character
      THEN
      1+          \ advance to next character
   LOOP
   DROP  ;        \ clean up

S"  ONE TWO THREE FOUR  " MENU

 

...lee

  • Like 2
Link to comment
Share on other sites

4 hours ago, jschultzpedersen said:

Hi

 

Well, it has not bothered me, since I only know how TurboForth behaves 😀 I run TF from Classic99. I would love to get my hands on a TF cartridge or an image that works with FG99. But for now it is Classic99.

 

Anyway, here is a working solution to the task I set myself. The INNER word takes a string and a separator from some global values initiated in the OUTER word, and returns  the address and length of the sub string, which I use to print the section. Eventually it runs out of text and returns 0 0. All very similar to the behaviour of WORD. But, as it all happens in CPU ram, it works during autoload, unlike WORD which apparently does not coexist very well with LOAD.

 

There is no formatting in OUTER yet. And I will see if I can get rid of some of the global values - just on principle.

 

 0.. VALUE NN 0 VALUE MM 0 VALUE MYADRS 0 VALUE MYLEN 32 VALUE SEP             
 1.: INNER                                                                     
 2.  0 TO MM                                                                   
 3.  BEGIN                                                                     
 4.    MYADRS MM + C@ SEP <> NN MYLEN < AND                                    
 5.  WHILE                                                                     
 6.    1 +TO MM 1 +TO NN                                                       
 7.  REPEAT                                                                    
 8.  NN MYLEN <= IF MYADRS MM ELSE 0 0 THEN                                    
 9.  MM 1 + +TO MYADRS 1 +TO NN ;                                              
10.: OUTER ( ADRS LEN SEP -- )                                             
11.  TO SEP TO MYLEN TO MYADRS 0 TO NN                                        
12.  BEGIN                                                                     
13.    NN MYLEN <                                                              
14.    WHILE INNER CR TYPE REPEAT ;                                            
15.S" DETTE ER VIRKELIG FINE SAGER" 32 OUTER  

 

regards

Jesper

Working code is good by any measure. Congratulations Jepser on solving your problem.

I am guessing you are a veteran programmer in other languages. and Ja, "dette er virkelig fine" is very true. :) 

 

It's great to see a new person interested in Forth here. 

Something that jumps out at me is that you defined SEP as 32 and Forth defines the constant BL = 32,  so you could remove that VALUE if it's never going to change. 

 

 

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

Interesting discussion.

 

Yeah, sometimes early, naive design decisions come back to bite you in the ass. Placing blocks (and the TIB) into VDP turned out to be more trouble than it was worth IMO.

 

The key to understanding this is to know that there is some magic that takes place in WORD. Here is it's implementation:

 

; WORD ( delimiter -- address length )
; Moves through TIB in VDP memory, discarding leading delimiters, looking for 
; a word. A word is identified when a trailing delimiter is detected. 
; The word is copied from VDP to CPU memory.
; Pushes the start address of the word (in CPU memory), and the length of
; the word to the stack. 
; If no word is found (for example if we hit the end of the TIB without 
; detecting a word then 0 0 is pushed on the stack.

wordh   data typcmh,4
        text 'WORD'
word    data docol
        ; tib @ blk @ ?dup if nip block then 
        data tib_,fetch
word0   data blk,fetch,qdup,zbrnch,word2,nip,fblock
word2   data word1
        data exit

; at this point, data stack is ( delimeter address -- )
; where address is the address in vdp to start searching from.
; address is either TIB+>IN (if BLK=0) or block address+>IN 
; if BLK>0. (the code to add >IN to the address is in _word)
word1   data $+2
        bl @bank1
        data _word                  ; see 1-08-Parsing.a99

 

Note the second part, where it is stated that "address is either TIB + >IN (if BLK=0) or block address + >IN if BLK>0."

 

So despite manipulating >IN, it is adding the VDP address of the block and reading from there.

 

A potential work around would be to temporarily set BLK to 0 (BLK 0!) and restore it later. That would probably work. It would fool WORD into thinking its no longer reading from a block, and therefore would use the address of TIB + >IN  to read from.

 

Its a bit of a mess! On the other hand, you can have six disk blocks in VDP memory on a 32K 4A which is pretty cool. Well, I thought it was at the time ;)

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

Hi

 

TheBF... You are right about me having programmed quite a lot 😀. I have no formal education in the craft, but it has been a hoppy for me to tinker with all sorts of programming languages over the last 40 years, privately and in my work life. Interestingly it was the purchase of a TI99/4A, that initiated my career in the it world at a late age of 25. I was actually educated in economics, but computing was so much more interesting. So I made a choice and picked the right horse, so to speak.

 

Willsy... Thanks for the explanation. I have bypassed the limitation by making a word, that is similar to WORD, but works exclusively in CPU ram. I am still polishing the solution, but I wil publish it when done.

By the way... There may be an issue with autoboot. Try this in block 1:

 

1 2 3 4 .S

 

Do an autoboot with a cold or a warm restart. It returns 2 3 4 in the stack. The bottom stack entry is lost.

Do a 1 LOAD. It returns 1 2 3 4 as it should.

 

I can sort of bypass the problem by adding a dummy stack entry at the bottom. So for instance 0 1 2 3 4 will leave 1 2 3 4 in the stack if 1 2 3 4 is what I want. But then 1 LOAD has five entries on the stack. So this might be a bug.

 

Lee Stewart... Yes, that is obviously simpler and faster than my solution. My intention was to use any separator for multi-word entries, but that can also be included in your code. So it will be the standard by which I shall measure my next version 😀

 

regards

jesper

  • Like 3
Link to comment
Share on other sites

2 hours ago, jschultzpedersen said:

Hi

 

TheBF... You are right about me having programmed quite a lot 😀. I have no formal education in the craft, but it has been a hoppy for me to tinker with all sorts of programming languages over the last 40 years, privately and in my work life. Interestingly it was the purchase of a TI99/4A, that initiated my career in the it world at a late age of 25. I was actually educated in economics, but computing was so much more interesting. So I made a choice and picked the right horse, so to speak.

Ok so I am going to take "quite a lot" as:  you know programming very well. 

I suspect you have now travelled "through the looking glass" and are intrigued by this weird programming language. 

 

My after retirement time has been spent learning the stuff about Forth that I didn't understand 35 years ago when I got TI-Forth. 

How to write a Forth, how to write a Forth in Forth , writing a Forth cross-compiler and writing a Forth in Forth.

 

For your WORD replacement project here are some words that showed up in the late 1990s. I don't know who invented them.

/STRING It's not in the TF kernel but is easily defined and is now an ISO Forth word. 

SKIP and SCAN are not in the standard but are in GNU Forth, most commercial Forths and many others. 

 

/STRING  is simple yet remarkably useful for strings that live on the data stack as an addr,length pair. 

 

: /STRING ( caddr1 u1 n - caddr2 u2 ) TUCK - >R + R> ;  

 

It is used like this :

 

S" This is a string of arbitrary length"  5 /STRING CR TYPE <enter> 

 

Returns:

is a string of arbitrary length ok 

 

We can use /STRING to make SKIP and SCAN. (These are weird with multiple WHILE statements but think of WHILE as form of IF) 

 

: SKIP ( addr len char -- addr' len') 
    >R 
     BEGIN 
        DUP            \ is length >0
     WHILE 
        OVER C@ R@ =   \ 1stchar = to char ?
     WHILE 
        1 /STRING      \ cut 1 char, update addr,len  
     REPEAT 
     THEN
     R> DROP ;
        

 

Test with 

S" ***********TESTING 1 2 3"  ASCII * SKIP  TYPE 

 

: SCAN ( addr len char -- addr' len') 
    >R 
     BEGIN 
        DUP            \ is length >0
     WHILE 
        OVER C@ R@ <>  \ 1stchar <> to char ?
     WHILE 
        1 /STRING      \ cut 1 char, update addr,len  
     REPEAT 
     THEN
     R> DROP ;
        

 

In each of these words, the output string is left on the data stack as an address,length pair so you can re-run more string processing.

 

Example: -TRAILING removes trailing spaces from a string, therefore this works.

: CLEAN$      -TRAILING  BL SKIP ;

S"                   HELLO                "   CLEAN$ TYPE 

 

Hope you find this useful.

Edited by TheBF
fixed comment on SKIP
  • Like 3
Link to comment
Share on other sites

On 3/29/2023 at 12:02 PM, jschultzpedersen said:

By the way... There may be an issue with autoboot. Try this in block 1:

 

1 2 3 4 .S

 

Do an autoboot with a cold or a warm restart. It returns 2 3 4 in the stack. The bottom stack entry is lost.

Do a 1 LOAD. It returns 1 2 3 4 as it should.

 

I can sort of bypass the problem by adding a dummy stack entry at the bottom. So for instance 0 1 2 3 4 will leave 1 2 3 4 in the stack if 1 2 3 4 is what I want. But then 1 LOAD has five entries on the stack. So this might be a bug.

 

Yep. That looks like a bug to me. TF reads the keyboard just before performing an autoboot. The idea is you can change, or bypass the autoload feature. So, after selecting TurboForth from the cartridge menu, if you quickly press and hold a key, it will boot from DSK<the-key-you-pressed>.BLOCKS

 

So, if you hold down 2, it will look for DSK2.BLOCKS. If you hold down A it will look for DSKA.BLOCKS etc. If you hold down ENTER, it will bypass autoload entirely.

 

The autoload stuff at startup is written in Forth. My guess is, it's calling KEY to get the code of the pressed key, and subsequently DROPs it somewhere. That's fine.... IF you pressed a key! If not, there was nothing on the stack to drop. My guess is it's related to that. That, or the data stack pointer is not correctly initialised. Might be interesting to try and track it down. It's interesting that all the code in block 1 of the standard boot disk runs just fine. One would expect it to barf. Hmmm...

 

But yes, just use a dummy value for now. 

Link to comment
Share on other sites

7 hours ago, Willsy said:

The autoload stuff at startup is written in Forth. My guess is, it's calling KEY to get the code of the pressed key, and subsequently DROPs it somewhere. That's fine.... IF you pressed a key! If not, there was nothing on the stack to drop. My guess is it's related to that. That, or the data stack pointer is not correctly initialised. Might be interesting to try and track it down. It's interesting that all the code in block 1 of the standard boot disk runs just fine. One would expect it to barf. Hmmm...

 

I have not figured out exactly what is going on, but I am accustomed to SP@ and S0 pointing to the same address when I start with an empty stack. However, SP@ points to >A2C4, whereas S0 points to >A2C6. Using 

1 2 3 4 .S

on block 1 shows

2 3 4 <TOP

 with >0001 sitting at >A2C4—as though S0 had started out at >A2C6, but somehow got decremented after autoboot.

 

...lee

Edited by Lee Stewart
correction
  • Like 1
Link to comment
Share on other sites

Hi

theBF... This code construct - WHILE flag some code THEN - is not mentioned in the online guide on turboforth.net. It seems to work like a classic WHILE - WEND loop. Nice to know... I thought that THEN was only of use with IF.

My MENU project is going well. - which means I have working code if not minimalistic code 😀. I have toyed with different ways of moving stack content around and moving some data from the stack to the return stack or as 'local' data (see code) to simplify the stack gymnastics. The current incarnation cuts out a word from a string and returns info about the rest of the string as well as the selected substring based on a specified separator. I then use a crude form of local variables for x and y coordinates in the wrapper word MENU to print each line provided by CUT at a chosen location. I do not know if messing with the content of H is considered good form, but hey, it works. I know there is a code module for local variables included with Turboforth. I just wanted to see, if I could do without. And I am having fun... It reminds me of my late twenties, when, in my first job,  I encountered the similarly weird and powerful APL language. Like Forth it was also a stack oriented language where you read from right to left and invented your own 'words' along the way and topped it off with some powerful matrix and algebra operators and a custom keyboard. And like Forth it was very hard to read other peoples code without supporting comments or plenty of time to dissect the code.

 

Block 50                     Mode:OVER                                          
   0         1         2         3         4         5         6                
  ..................................................................            
 0.. DROPSTACK  ( -- )                                             .            
 1.  DEPTH 0 DO DROP LOOP ;                                        .            
 2.                                                                .            
 3.: CUT ( SEP ADRS LEN -- SEP NEWADRS NEWLEN ADRS LEN )           .            
 4.  DUP 0 = IF DROPSTACK 0 0 0 EXIT THEN                          .            
 5.  0 ROT                                                         .            
 6.  BEGIN                                                         .            
 7.    DUP C@ 4 PICK <> 3 PICK 3 PICK 1+ > AND                     .            
 8.  WHILE                                                         .            
 9.    1+ SWAP 1+ SWAP                                             .            
10.  REPEAT                                                        .            
11.  1+ ROT ROT 1+ 2DUP - -ROT SWAP DROP                           .            
12.  2 PICK OVER - SWAP 2 PICK 0 <> + ;                            .            
13.                                                                .            
14.  -->                                                           .            
15.                                                                .            

                                                                                
Block 51                     Mode:OVER                                          
   0         1         2         3         4         5         6                
  ..................................................................            
 0.: MENU ( SEP ADRS LEN X Y -- )                                  .            
 1.  4 ALLOT HERE 2 - ! HERE 4 - !                                 .            
 2.  BEGIN                                                         .            
 3.    DUP 0 <>                                                    .            
 4.  WHILE                                                         .            
 5.    CUT HERE 4 - @ HERE 2 - @ GOTOXY TYPE                       .            
 6.    HERE 2 - @ 1 + HERE 2 - !                                   .            
 7.  REPEAT                                                        .            
 8.  DROPSTACK HERE 4 - H ! ;                                      .            
 9.                                                                .  

 

Like you I am also retired, and instead of getting bored, I started fulfilling my bucket list. One of the items on the list was going back to my first computer love... aka. the TI99/4A. And here I am some years later. By the way. I have written a number of articles for the TI*MES magazine in England on Extended Basic in the last few years as a member of the TIUGUK user group. I also wrote one about LOGO II from the viewpoint of a newcomer to that language, and I have done one article on TurboForth, also from a newcomers viewpoint, that has yet to be published. I do not suppose there are many readers, but I enjoy writing, and it is a good way to learn the stuff yourself.

 

regards

Jesper

  • Like 1
Link to comment
Share on other sites

5 hours ago, jschultzpedersen said:

Hi

theBF... This code construct - WHILE flag some code THEN - is not mentioned in the online guide on turboforth.net. It seems to work like a classic WHILE - WEND loop. Nice to know... I thought that THEN was only of use with IF.

My MENU project is going well. - which means I have working code if not minimalistic code 😀. I have toyed with different ways of moving stack content around and moving some data from the stack to the return stack or as 'local' data (see code) to simplify the stack gymnastics. The current incarnation cuts out a word from a string and returns info about the rest of the string as well as the selected substring based on a specified separator. I then use a crude form of local variables for x and y coordinates in the wrapper word MENU to print each line provided by CUT at a chosen location. I do not know if messing with the content of H is considered good form, but hey, it works. I know there is a code module for local variables included with Turboforth. I just wanted to see, if I could do without. And I am having fun... It reminds me of my late twenties, when, in my first job,  I encountered the similarly weird and powerful APL language. Like Forth it was also a stack oriented language where you read from right to left and invented your own 'words' along the way and topped it off with some powerful matrix and algebra operators and a custom keyboard. And like Forth it was very hard to read other peoples code without supporting comments or plenty of time to dissect the code.

 

Block 50                     Mode:OVER                                          
   0         1         2         3         4         5         6                
  ..................................................................            
 0.. DROPSTACK  ( -- )                                             .            
 1.  DEPTH 0 DO DROP LOOP ;                                        .            
 2.                                                                .            
 3.: CUT ( SEP ADRS LEN -- SEP NEWADRS NEWLEN ADRS LEN )           .            
 4.  DUP 0 = IF DROPSTACK 0 0 0 EXIT THEN                          .            
 5.  0 ROT                                                         .            
 6.  BEGIN                                                         .            
 7.    DUP C@ 4 PICK <> 3 PICK 3 PICK 1+ > AND                     .            
 8.  WHILE                                                         .            
 9.    1+ SWAP 1+ SWAP                                             .            
10.  REPEAT                                                        .            
11.  1+ ROT ROT 1+ 2DUP - -ROT SWAP DROP                           .            
12.  2 PICK OVER - SWAP 2 PICK 0 <> + ;                            .            
13.                                                                .            
14.  -->                                                           .            
15.                                                                .            

                                                                                
Block 51                     Mode:OVER                                          
   0         1         2         3         4         5         6                
  ..................................................................            
 0.: MENU ( SEP ADRS LEN X Y -- )                                  .            
 1.  4 ALLOT HERE 2 - ! HERE 4 - !                                 .            
 2.  BEGIN                                                         .            
 3.    DUP 0 <>                                                    .            
 4.  WHILE                                                         .            
 5.    CUT HERE 4 - @ HERE 2 - @ GOTOXY TYPE                       .            
 6.    HERE 2 - @ 1 + HERE 2 - !                                   .            
 7.  REPEAT                                                        .            
 8.  DROPSTACK HERE 4 - H ! ;                                      .            
 9.                                                                .  

 

Like you I am also retired, and instead of getting bored, I started fulfilling my bucket list. One of the items on the list was going back to my first computer love... aka. the TI99/4A. And here I am some years later. By the way. I have written a number of articles for the TI*MES magazine in England on Extended Basic in the last few years as a member of the TIUGUK user group. I also wrote one about LOGO II from the viewpoint of a newcomer to that language, and I have done one article on TurboForth, also from a newcomers viewpoint, that has yet to be published. I do not suppose there are many readers, but I enjoy writing, and it is a good way to learn the stuff yourself.

 

regards

Jesper

Oh Jesper. We now know you are cut from special cloth.  APL programmers are right out there on the edge with Forth and LISP programmers BUT closer to the edge. :) 

There is a guy in the USA who wrote an APL type system on top of Forth, CoSy Forth. I am not sure he will ever be healthy again. :) 

 

Glad to hear that you are also enjoying retirement. With you background your article on Forth will be fun to read. 

 

 

Yes IF is typically resolved with THEN but here are the definitions  of the standard branching and looping words in ANS/ISO Forth.

Notice the definition for WHILE.  :)  (POSTPONE is the ANS/ISO word to do the actions of COMPILE and [COMPILE] in TF/FORTH-83)

 

Spoiler
: AHEAD ( -- addr) HERE   0 , ;
: <BACK ( addr --) HERE -  , ;

: THEN   ( addr -- ) HERE OVER - SWAP ! ;     IMMEDIATE
: BEGIN   HERE ;                              IMMEDIATE
: IF      POSTPONE ?BRANCH AHEAD ;            IMMEDIATE
: ELSE    POSTPONE BRANCH  AHEAD SWAP POSTPONE THEN ; IMMEDIATE
: UNTIL   POSTPONE ?BRANCH <BACK ;  IMMEDIATE
: AGAIN   POSTPONE BRANCH  <BACK ;  IMMEDIATE
: WHILE   POSTPONE IF SWAP ;        IMMEDIATE
: REPEAT  POSTPONE AGAIN POSTPONE THEN ; IMMEDIATE

 

 

" I do not know if messing with the content of H is considered good form, but hey, it works."

 

Playing with the dictionary pointer is fair game in Forth. That's what ALLOT does after all.

Your creative use of HERE and ALLOT for local variables however is just delaying the inevitable,  :) 

which is learning to use the data stack and return stack to do your wishes. 

To paraphrase Stars Wars: "USE THE FORTH JESPER"  :) 

 

The data stack is challenging but it is part of the fun of this crazy thing.  

Learning to use it as Chuck Moore intended takes some time.

 

Some general rules of good Forth form that I can think of. 

- Never try to manage more than 3 elements on the data stack in a word definition (some exceptions apply of course but usually >3 means the code needs factoring) 

- Global variables are not a crime any more than they are in Assembly language and Forth is the Assembler for the Forth VM. 

   With the data stack , you need very few of them anyway.

- Factor the code into small definitions to simplify stack manipulation and create a "language" of Forth "WORDS" to describe the parts of the problem.

  Then use the words to solve the problem. (nothing new to you with APL experience) 

 

This line is something only really smart people (APL programmers?  :) ) can understand and would typically be factored into named definitions (IF it was really needed) :) 

1+ ROT ROT 1+ 2DUP - -ROT SWAP DROP   
 2 PICK OVER - SWAP 2 PICK 0 <> + ;  

 

I am going to see if I can re-write your code in more idiomatic Forth just to see if I can. 

I will test it with TurboForth like I did with my other examples. 

 

 

 

 

 

 

 

 

  • Like 2
Link to comment
Share on other sites

I am not sure that is what you wanted to do exactly but if you had a bunch of text in a block,  typing: 

  1 BLOCK  128  2 2  AT .MENU 

would print a menu from the text in the block. 

 

A rule of thumb in the Forth world that I forgot to mention goes: "Don't hide your tools" meaning make words that you can re-use.

So most of the words below would have uses in other parts of a project. 

 

But to be honest I think people would normally build the menu with text and Forth code in the block and then just LOAD the block. No new code required. 

 

PAGE   .( Main Menu for Purposes Unknown) 

CR

CR  .( 1 Stop all wars)

CR  .( 2 Move the moon 5 Metres farther away)

CR  .( 3 Increase the speed of light by 10%)

CR

CR  .( Press the ANY key to continue) :) 

 

 

 

Spoiler
      
: 2SWAP   ( a b c d -- c d a b ) ROT >R ROT R> ;

: /STRING ( caddr1 u1 n - caddr2 u2 ) TUCK - >R + R> ;  

: SCAN ( addr len char -- addr' len')
  >R BEGIN DUP  WHILE OVER C@ R@ <>
  WHILE 1 /STRING REPEAT THEN  R> DROP ;

: SKIP ( addr len char -- addr' len')
  >R BEGIN DUP WHILE OVER C@ R@ =
  WHILE 1 /STRING REPEAT THEN R> DROP ;      

\ find position of char in string
: POS ( addr len char -- addr len n )
  >R 2DUP R>  SCAN NIP OVER SWAP - ;

: /CHOP  ( addr len n -- addr len addr2 len2 )
  >R OVER R@ 2SWAP R> /STRING 2SWAP ;

VARIABLE X
VARIABLE Y

: AT  ( x y -- ) Y ! X ! ;

: .TEXT  ( adr len --)  X @ Y @ GOTOXY TYPE ;

: .MENU ( ADRS LEN sep -- )
   >R
   BEGIN  
     R@ SKIP   \ skip leading separators                                      
     DUP 0 <>                                                  
   WHILE                                                    
      R@ POS /CHOP .TEXT
      2 Y +!
  REPEAT  
  R> DROP  
  2DROP                                                  
;                                    

PAGE
S" ITEM-1  ITEM-2  ITEM-3   ITEM-4 ITEM-5 " 
 5 5 AT BL .MENU

 

image.png.25c32c58742124bad9835c3377efcc9a.png

Link to comment
Share on other sites

Hi

 

theBF... I could use blocks. But they are static. I want my menu word to be dynamic. That is - put its payload wherever I want and be able to modify it as I go along - like showing different content depending on some action taken by the user. I already have a word, that clears a limited area of the screen and put a nice border around it, without effecting the rest of the screen, and in that area I want to put my menu. In this way I can maintain multiple menus on the same screen, or use the areas like windows of text in a screen.

 

So I will keep your advices in mind and se if I can refactor my code and stay within the - max 3 items in stack - rule. I'll be back!

 

regards

Jesper

  • Like 2
Link to comment
Share on other sites

5 hours ago, jschultzpedersen said:

Hi

 

theBF... I could use blocks. But they are static. I want my menu word to be dynamic. That is - put its payload wherever I want and be able to modify it as I go along - like showing different content depending on some action taken by the user. I already have a word, that clears a limited area of the screen and put a nice border around it, without effecting the rest of the screen, and in that area I want to put my menu. In this way I can maintain multiple menus on the same screen, or use the areas like windows of text in a screen.

 

So I will keep your advices in mind and se if I can refactor my code and stay within the - max 3 items in stack - rule. I'll be back!

 

regards

Jesper

I have no doubt about you coming back. 

Look forward to seeing what you develop. 

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...