Jump to content

Photo

Adding an INPUT statement to Forth


8 replies to this topic

#1 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 371 posts
  • Location:The Great White North

Posted Mon Aug 7, 2017 6:46 AM

Many times those who want to try Forth are frustrated by the absence of simple features that they took for granted in BASIC.

One of those things in my experience was the BASIC word INPUT.  It allows us to get user input very easily.

 

Forth was created by a person who started from the premis that he never knew what a program was going to require in precise terms so he assumed very little with his language but gave himself the tools to build what he needed rather than make it in advance. Charles Moore is something of an enigma in programming because he is happy to start from scratch every time. For us normal humans it's handy to have some things "in the can".

 

The following code adds INPUT to Camel Forth.  (it will require tweaking for other flavours of Forth)

 

The important difference is that there must be two "INPUTs" for Forth. One for strings and one for numbers. BASIC knows what kind of variable you use with INPUT so it can run the right code. Forth has no knowledge like that so you have to use $INPUT or #INPUT as needed. The good news is that #INPUT uses $INPUT to gather the number string and then converts it.

 

I have made #INPUT with a much smaller error message that TI BASIC. 

I could not justify all the space for 

 

* WARNING

      INPUT ERROR IN 100

TRY AGAIN"

 

:)

 

But it's simple to change if you want it. 

\ INPUT.FTH   creates input like BASIC
\ *Difference* there is a separate input for numbers and strings

DECIMAL
: "?"      BEEP CR ." ?  " ;   \ we can reuse this gem

: $ACCEPT ( $addr -- )   DUP  1+ 80 ACCEPT  SWAP C!  ;

: $INPUT  ( $addr -- )  "?"  $ACCEPT ;  \ "?" to look like TI-BASIC

: #INPUT  ( variable -- )   \ made to look like TI-BASIC
          BEGIN
            PAD $INPUT      \ $ACCEPT text into temp buffer PAD
            PAD ?NUMBER 0=   \ convert the number in PAD

          WHILE              \ while the conversion is bad we do this
             CR HONK ." input error "
             CR DROP
          REPEAT
          SWAP ! ;           \ store the number in the variable on the stack)


\ Usage:
\
\ VARIABLE A$ 100 ALLOT      \ string variables need more space
\ VARIABLE X
\
\ A$ $INPUT
\
\ X  #INPUT

Edited by TheBF, Mon Aug 7, 2017 1:45 PM.


#2 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,391 posts
  • Location:Silver Run, Maryland

Posted Mon Aug 7, 2017 9:45 AM

Your line,

"?" PAD $ACCEPT  \ $ACCEPT text into temp buffer PAD

should probably be

PAD $INPUT        \ $INPUT text into temp buffer PAD

per your introduction.

 

...lee



#3 TheBF OFFLINE  

TheBF

    Moonsweeper

  • Topic Starter
  • 371 posts
  • Location:The Great White North

Posted Mon Aug 7, 2017 1:44 PM

Your line,

"?" PAD $ACCEPT  \ $ACCEPT text into temp buffer PAD

should probably be

PAD $INPUT        \ $INPUT text into temp buffer PAD

per your introduction.

 

...lee

 

Good catch Lee.  :-)

 

Eagle eyes.

 

B



#4 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,391 posts
  • Location:Silver Run, Maryland

Posted Tue Aug 8, 2017 11:33 AM

Converting your code to fbForth 2.0 took more than a little tweaking! :P  Instead of ?NUMBER ( addr -- n ) and ACCEPT ( addr maxcnt -- chrcnt ), there are NUMBER ( addr -- d ) and EXPECT ( addr maxcnt -- ).  The problems with NUMBER are (1) it returns a double number, i.e., 32 bits, and (2) an error causes it to abort with a system error message.  To keep the ported code as close as possible to your code, I defined ?NUMBER and ACCEPT .  I maintained the double-number output of NUMBER for ?NUMBER to avoid surprises for those used to fbForth and TI Forth.  NUMBER only works with a blank-terminated numeric string, such as stored by WORD , but EXPECT stashes two nulls (ASCII zeroes) after the input string!  Also,  VARIABLE requires an initial value when it defines a new variable.

 

I don’t want to derail this thread with too much discussion about the vagaries of fbForth 2.0, so without further ado, in the spoiler below is the Camel Forth code ported to that other dialect of Forth:

 

Spoiler

 

...lee



#5 TheBF OFFLINE  

TheBF

    Moonsweeper

  • Topic Starter
  • 371 posts
  • Location:The Great White North

Posted Tue Aug 8, 2017 1:28 PM

Didn't mean to create a make work project Lee. Very sorry.

But wow! You really went to town there.  That would take me a very longgg time I think.

 

 

I did this for HsForth for MS DOS to make ANS Forth ACCEPT functionality.

: ACCEPT        ( c-addr +n1 -- +n2 )   EXPECT SPAN @  ;  

But it does not seem to work as I "expect"  in Turbo Forth ?

 

And yes it is sad that Forth does not have a simple text to number conversion word that seems to work across all forth versions.

 

For your reference here is what Brad Rodriguez created for Camel Forth. His ?NUMBER returns a single because CAMEL Forth does not do doubles.

 

The ANS standard word  >NUMBER I  find pretty useless in the real world. The standard is not finished in this area IMHO.

\ ========================================================================
\ S T R I N G  T O  N U M B E R   C O N V E R S I O N


: DIGIT?     ( char -- n -1)                \ if char is a valid digit
\            (      -- x  0 )               \ if char is not valid
              DUP 39 > 100 AND +            \ silly looking
              DUP 140 > 107 AND -  30 -     \ but it works!
              DUP BASE @ U< ;               \ 36 bytes

: ?SIGN       ( adr n -- adr' n')           \  f  get optional sign
\ advance adr/n if sign; return NZ if negative
              OVER C@                       \ -- adr n c
              2C - DUP ABS 1 = AND          \ -- +=-1, -=+1, else 0
              DUP IF 1+                     \ -- +=0, -=+2
                     >R 1 /STRING R>        \ -- adr' n' f
              THEN ;

: UD*         ( ud1 u2 -- ud3)              \ 32*16->32 multiply
              DUP >R * SWAP R> UM* ROT + ;  \ replaced 'UM* DROP' with '*' BF.

: >NUMBER     ( ud adr u -- ud' adr' u' )   \ convert string to number
              BEGIN  DUP
              WHILE
                  OVER C@ DIGIT? 0=
                  IF   DROP EXIT
                  THEN >R 2SWAP BASE @ UD*
                       R> M+ 2SWAP 1 /STRING
              REPEAT ;

: ?NUMBER     ( c-addr -- n -1 )            \ string->number
\ ;Z                   -- c-addr 0          \ if convert error
              DUP  0 0 ROT COUNT            \ -- ca ud adr n
              ?SIGN >R  >NUMBER             \ -- ca ud adr' n'
              IF   R> 2DROP 2DROP 0         \ -- ca 0   (error)
              ELSE 2DROP NIP R>
                 IF NEGATE THEN  -1         \ -- n -1   (ok)
              THEN ;

BF



#6 Willsy OFFLINE  

Willsy

    River Patroller

  • 3,024 posts
  • Location:Uzbekistan (no, really!)

Posted Tue Aug 8, 2017 4:15 PM

I did this for HsForth for MS DOS to make ANS Forth ACCEPT functionality.

: ACCEPT        ( c-addr +n1 -- +n2 )   EXPECT SPAN @  ;  
But it does not seem to work as I "expect"  in Turbo Forth ?


That's because versions 1.2 onwards of TurboForth store keystrokes in VDP. Specifically, TIB returns a *VDP* address (try the phrase TIB @ $. - see? 3420 - that's a VDP address!) and EXPECT interprets the address passed to it as a VDP address.
 
WORD on the other hand is "clever". It knows that the TIB lives in VDP, and when it pulls a word out of the TIB, it copies it to a "word buffer" in CPU memory. 
 
Confused yet? You will be! (You should be hearing the theme tune to the old TV show "Soap", right about now!)
 
This is a design decision that has come back to kick me in the ass a number of times.
 

 
The complexity arises because block buffers live in VDP memory in TF.
 
So, EXPECT stuffs keystrokes into VDP memory. WORD copies them out.
 
It still does actually work. Try this:
: test ( -- )
  tib @ 80 expect \ get up to 80 characters of text into TIB in VDP RAM
  begin bl word dup while cr type repeat
  2drop ;
 
test fred bloggs was ere
Here's ACCEPT and some test code:

: accept ( c-addr +n1 -- +n2 )
  tib @ swap expect      \ get up to n1 chars in tib in vdp
  tib @ swap span @ vmbr \ read from vdp to c-addr
  span @                 \ push n2
  span 0!                \ invalidate tib
;
Test code:
36 load \ load DUMP
create buff 80 chars allot
buff 80 accept
buff 80 dump

Edited by Willsy, Tue Aug 8, 2017 4:17 PM.


#7 TheBF OFFLINE  

TheBF

    Moonsweeper

  • Topic Starter
  • 371 posts
  • Location:The Great White North

Posted Tue Aug 8, 2017 6:02 PM

Well that explains that quite nicely.

 

Thanks Willsy

 

BF



#8 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,391 posts
  • Location:Silver Run, Maryland

Posted Tue Aug 8, 2017 6:17 PM

That's because versions 1.2 onwards of TurboForth store keystrokes in VDP. Specifically, TIB returns a *VDP* address (try the phrase TIB @ $. - see? 3420 - that's a VDP address!) and EXPECT interprets the address passed to it as a VDP address.

 

I will have to take another look at your code, but why does 3420h not also indicate a low RAM address?

 

...lee



#9 Willsy OFFLINE  

Willsy

    River Patroller

  • 3,024 posts
  • Location:Uzbekistan (no, really!)

Posted Wed Aug 9, 2017 12:47 AM

 
I will have to take another look at your code, but why does 3420h not also indicate a low RAM address?
 
...lee


Err... good question!

TF doesn't actually use low memory for anything until you tell it to by writing to the variable H.




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users