+TheBF Posted August 7, 2017 Share Posted August 7, 2017 (edited) 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 August 7, 2017 by TheBF 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 7, 2017 Share Posted August 7, 2017 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 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 7, 2017 Author Share Posted August 7, 2017 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 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 8, 2017 Share Posted August 8, 2017 Converting your code to fbForth 2.0 took more than a little tweaking! 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: \ ===fbForth 2.0 version=============================================== \ INPUT.FTH creates input like BASIC \ *Difference* there is a separate input for numbers and strings HEX : ?NUMBER ( addr -- d flag ) \ (NUMBER) expects a double number (0 to start) and an address 0 0 ROT \ S:d addr DUP 1+ \ first byte assumed to be char count S:d addr addr+1 C@ 02D = \ check for '-' \ S:d addr ?- DUP >R \ dup '-' flag; push to return stack S:d addr ?- R:?- + \ add flag to addr to advance position if '-' -1 \ starting value for DPL \ S:d addr -1 R:?- BEGIN DPL ! \ start DPL with -1 \ S:d addr R:?- \ (NUMBER) returns updated d and address of first non-digit (NUMBER) \ S:d addr DUP C@ \ check first non-digit char S:d addr [addr] R:?- \ if success, 1st num is TRUE flag; 2nd num exits loop \ if '.' found, 1st num is 0 for DPL; 2nd num loops again \ if failure, 1st num is FALSE flag; 2nd num exits loop CASE 0 OF 1 1 ENDOF \ success if null char BL OF 1 1 ENDOF \ success if blank char 02E OF 0 0 ENDOF \ '.' found; loop again for rest of number ELSEOF 0 1 ENDOF \ failure ENDCASE UNTIL \ loop if not 0 S:d addr flag R:?- SWAP DROP \ drop address S:d flag R:?- R> \ S:d flag ?- IF >R \ S:d R:flag DMINUS \ negate d if ?- is TRUE R> \ S:d flag THEN ; : ACCEPT ( addr maxcnt -- chrcnt ) OVER OVER \ S:addr maxcnt addr maxcnt EXPECT \ S:addr maxcnt 1+ 0 DO \ check for null up to maxcnt S:addr DUP \ dup 1st char address S:addr addr I + C@ 0= \ check if next char is null S:addr [addr+I] IF \ we're outta here if null char S:addr DROP \ drop leftover addr S: I \ leave chrcnt S:chrcnt LEAVE \ leave loop THEN LOOP ; 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 DROP \ drop double number REPEAT DROP \ unconditionally drop MSW of double number SWAP ! ; \ store the number in the variable on the stack) \ Usage: \ \ 0 VARIABLE A$ 100 ALLOT \ string variables need more space \ 0 VARIABLE X \ \ A$ $INPUT \ \ X #INPUT ...lee Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 8, 2017 Author Share Posted August 8, 2017 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 Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 8, 2017 Share Posted August 8, 2017 (edited) 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 ereHere'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 August 8, 2017 by Willsy Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 9, 2017 Author Share Posted August 9, 2017 Well that explains that quite nicely. Thanks Willsy BF Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 9, 2017 Share Posted August 9, 2017 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 Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 9, 2017 Share Posted August 9, 2017 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.