6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Apr 28, 2024 11:09 pm

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Forth-83 CONVERT
PostPosted: Sun May 12, 2019 8:32 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 855
I've noticed something odd in my copy of the Forth-83 Standard and was wondering if it was an error. I hope it was.
Quote:
CONVERT +d1 addr1 -- +d2 addr2

+d2 is the result of converting the characters within the text beginning at addr1+2 into digits, using the value of BASE , and accumulating each into +d1 after multiplying +d1 by the value of BASE . Conversion continues until an unconvertible character is encounter. addr2 is the location of the first unconvertible character.


I think that addr+2 should be addr+1. Here is a short description of CONVERT from "Real Time Forth".
Quote:
CONVERT ( +d1 adr1 -- +d2 adr2 )
Convert the string starting at adr1+1 to a double number. Add this to d1 to give d2. Leave the
address of the first non-digit in the string (adr2) on the stack

I noticed the description from "Real Time Forth" didn't mention that each digit is accumulated into +d1 after multiplying +d1 by the value of BASE.


Top
 Profile  
Reply with quote  
 Post subject: Re: Forth-83 CONVERT
PostPosted: Mon May 13, 2019 4:41 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
This is the text of the comment from the F83 source code:
Quote:
Starting with the unsigned double number ud1 and the string
at adr1, convert the string to a number in the current base.
Leave result and address of unconvertable digit on stack.

And here's the routine they do:
Code:
: CONVERT   (S +d1 adr1 -- +d2 adr2 )
   BEGIN  1+  DUP >R  C@  BASE @  DIGIT
   WHILE  SWAP  BASE @ UM*  DROP  ROT  BASE @ UM*  D+
      DOUBLE?  IF  1 DPL +!  THEN  R>
   REPEAT  DROP  R>  ;

There's no mention of "+2's" or anything here.

But do notice that the first thing CONVERT does is 1+.

For example:
Code:
create nn 4 c, 49 c, 50 c, 51 c, 52 c, ok
nn count type 1234 ok
nn number? .s 1234 0 0 ok

Here, we have 1234 as a counted string. TYPE shows it ok, NUMBER? converts it ok (for some reason F83 has NUMBER, but it's not in the FORTH vocabulary -- but NUMBER? is, and NUMBER is just DEFERed to that).

However, CONVERT does not take a counted string, it wants raw text. It wants a double, and the address of the text. So:
Code:
0 0 nn 1+ convert .s 234 0 26391 ok

You can see it skipped the first character, and just converted 234.

Here is how it's used:
Code:
: (NUMBER?)   (S adr -- d flag )
   0 0  ROT  DUP 1+  C@  ASCII -  =  DUP  >R  -  -1 DPL !
   BEGIN   CONVERT  DUP C@  ASCII , ASCII / BETWEEN
   WHILE   0 DPL !
   REPEAT  -ROT  R> IF  DNEGATE  THEN   ROT C@ BL =  ;

The reason CONVERT is skipping the first byte is for multiple reasons. Initially, it's because it's expecting a counted string -- and it just ignores the count by skipping over it.

Here, the first line is basically staging the initial double (0 0), and then checks for a sign character. It compares to the '-', if it sees it, then it bumps the address up 1. (= returns TRUE, which is -1, so it subtracts it -- no, this is not intuitive).

However, it's not just skipping the count. It just does that at the beginning. It also consumes any , - . or / characters within the number. So, it's skipping over those as well.

So, if you key in: 123-456-789, (NUMBER?) will call CONVERT 3 times, once just before the 1, (which is normally a count byte), then at each of the dashes. CONVERT implicitly skips those.
Code:
123
123.456
123.456/789
1,234

Are all valid numbers in Forth. When you add those characters, you get double numbers.

So, that's how CONVERT is used in F83. It's documentation is wrong, or at least incomplete, it should talk about skipping the first byte to get actual numbers. I was playing about a while back and I had the dickens of a time trying to make sense of CONVERT.

But now I understand it, at least with F83.


Top
 Profile  
Reply with quote  
 Post subject: Re: Forth-83 CONVERT
PostPosted: Wed May 15, 2019 11:32 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 855
whartung wrote:
This is the text of the comment from the F83 source code:
Quote:
Starting with the unsigned double number ud1 and the string
at adr1, convert the string to a number in the current base.
Leave result and address of unconvertable digit on stack.

And here's the routine they do:
Code:
: CONVERT   (S +d1 adr1 -- +d2 adr2 )
   BEGIN  1+  DUP >R  C@  BASE @  DIGIT
   WHILE  SWAP  BASE @ UM*  DROP  ROT  BASE @ UM*  D+
      DOUBLE?  IF  1 DPL +!  THEN  R>
   REPEAT  DROP  R>  ;

There's no mention of "+2's" or anything here.

That is how Fleet Forth's CONVERT behaves. The wording of the Forth-83 Standard had me wondering if this was one more place in Fleet Forth that would not quite conform to the standard ( not all the 'user variables' from the Forth-83 standard are user variables in Fleet Forth). It looks like F83's DIGIT returns two items regardless of whether the character is a valid digit. Since DIGIT isn't in the Forth-83 Standard, I wrote Fleet Forth's DIGIT to return the same parameters as the one from "All About Forth".
Code:
CODE DIGIT  ( C B -- 0 )     // NOT A DIGIT
            ( C B -- N TF )  // VALID DIGIT

Fleet Forth's CONVERT is coded a little differently to accommodate that difference and to be more memory efficient.
Code:
SCR# 8B
// CONVERT
HEX
: CONVERT  ( D1 ADR1 -- D2 ADR2 )
   1+
   BEGIN
      COUNT BASE @ DIGIT
   WHILE
      2SWAP BASE @ *
      SWAP BASE @ UM* D+
      ROT
      DPL @ 0< 1+ DPL +!
   REPEAT
   1- ;

By the way, UM* DROP produces the same result as * .

Quote:
But do notice that the first thing CONVERT does is 1+.

For example:
Code:
create nn 4 c, 49 c, 50 c, 51 c, 52 c, ok
nn count type 1234 ok
nn number? .s 1234 0 0 ok

Here, we have 1234 as a counted string. TYPE shows it ok, NUMBER? converts it ok (for some reason F83 has NUMBER, but it's not in the FORTH vocabulary -- but NUMBER? is, and NUMBER is just DEFERed to that).

However, CONVERT does not take a counted string, it wants raw text. It wants a double, and the address of the text. So:
Code:
0 0 nn 1+ convert .s 234 0 26391 ok

You can see it skipped the first character, and just converted 234.

Here is how it's used:
Code:
: (NUMBER?)   (S adr -- d flag )
   0 0  ROT  DUP 1+  C@  ASCII -  =  DUP  >R  -  -1 DPL !
   BEGIN   CONVERT  DUP C@  ASCII , ASCII / BETWEEN
   WHILE   0 DPL !
   REPEAT  -ROT  R> IF  DNEGATE  THEN   ROT C@ BL =  ;

The reason CONVERT is skipping the first byte is for multiple reasons. Initially, it's because it's expecting a counted string -- and it just ignores the count by skipping over it.

Here, the first line is basically staging the initial double (0 0), and then checks for a sign character. It compares to the '-', if it sees it, then it bumps the address up 1. (= returns TRUE, which is -1, so it subtracts it -- no, this is not intuitive).

However, it's not just skipping the count. It just does that at the beginning. It also consumes any , - . or / characters within the number. So, it's skipping over those as well.

So, if you key in: 123-456-789, (NUMBER?) will call CONVERT 3 times, once just before the 1, (which is normally a count byte), then at each of the dashes. CONVERT implicitly skips those.
Code:
123
123.456
123.456/789
1,234

Are all valid numbers in Forth. When you add those characters, you get double numbers.

Yes, CONVERT's behavior allows for some interesting tricks. For example if there is a need to convert a time string with the following format: " hh:mm:ss " to the hours minutes and seconds on the stack, this short routine will do it.
Code:
: TIME>  ( ADR -- H M S )
   0 0 ROT CONVERT
   0 SWAP CONVERT
   0 SWAP CONVERT
   2DROP ;

And here is a sample session.
Code:
 OK
" 2:55:00 "  OK
DUP COUNT CR TYPE
2:55:00  OK
TIME>  OK
.S     2    55     0  OK
CONSOLE

This:
Code:
: (NUMBER?)   (S adr -- d flag )
   0 0  ROT  DUP 1+  C@  ASCII -  =  DUP  >R  -  -1 DPL !
   BEGIN   CONVERT  DUP C@  ASCII , ASCII / BETWEEN
   WHILE   0 DPL !
   REPEAT  -ROT  R> IF  DNEGATE  THEN   ROT C@ BL =  ;

Is not how I defined Fleet Forth's number word, -NUMBER .
This version also accepts the following as valid numbers:
Code:
.
,
-
/
..
...
..///,,,---

As well as any variation of those four punctuation marks.
This is how -NUMBER is defined in Fleet Forth.
Code:
SCR# 8D
// -NUMBER
: -NUMBER  ( ADR -- D FLAG )
   DPL ON  0 0 ROT
   DUP 1+ C@ ASCII - =
   DUP>R -  DUP>R
   BEGIN
      CONVERT DUP C@ VALID?
   WHILE
      DUP 1+ C@ VALID? 0=
   WHILE
      DPL OFF
   REPEAT
   THEN
   DUP C@ BL <> SWAP
   2-  DPL @ 0< - R> = OR
   R>  0EXIT  >R DNEGATE R> ;

-NUMBER returns a double number and a true flag if the conversion fails.
It returns a double number and a false flag if the conversion is successful.
For a successful conversion, the number string must contain a valid digit in the current number BASE and there can not be a run of two or more non digit valid characters. In other words, if , - . and / are valid punctuation for a number, these are valid numbers:
Code:
1.2
1.2.
1.2.3.
2,000,000
.1.2.3.456
123-456.12

And these are not:
Code:
1..2
3..4.5
...1
5.....
76.,
231-/..3

VALID? is a DEFERred word, the punctuation which is considered valid punctuation for a number can be changed.


Top
 Profile  
Reply with quote  
 Post subject: Re: Forth-83 CONVERT
PostPosted: Thu May 16, 2019 12:04 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
In FIG, (NUMBER) is essentially CONVERT. FIG only respects '.' in numbers. It's curious that Forth 83 specifies CONVERT in the standard, but not NUMBER.

When I was first playing about, I had a devil of a time figuring out CONVERT because of the "1 off" thing. I found that I couldn't use it to convert numeric data "in place", I had to copy the raw data out in to a counted string for just the number, and then run CONVERT.

I had some fixed length fields in a record I was trying to convert.

Funny looking back at it, (haven't looked at this code in come time), I should just use NUMBER straight up. I was looking at CONVERT initially simply because I didn't want to copy the data out, but convert it in place. Once I copy it out, then I can just use NUMBER.

This is how boats end up in basements...


Top
 Profile  
Reply with quote  
 Post subject: Re: Forth-83 CONVERT
PostPosted: Thu May 16, 2019 12:29 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 855
Don't forget about the blank at the end of the string. If the values in memory after the string are valid digits or valid punctuation, NUMBER will keep right on converting until it runs into something that's not valid. For example:
Code:
HEX
PAD 80 31 FILL  OK

will simulate such a condition, and:
Code:
" 42" -NUMBER  OK
.S 1111 1111 FFFF
. -1  OK
D. 11111111  OK

Is probably not the result you want.


Top
 Profile  
Reply with quote  
 Post subject: Re: Forth-83 CONVERT
PostPosted: Thu May 16, 2019 1:42 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
JimBoyd wrote:
Don't forget about the blank at the end of the string. If the values in memory after the string are valid digits or valid punctuation, NUMBER will keep right on converting until it runs into something that's not valid.


Yea, which makes the whole "convert counted string at address" kind of a lie since it doesn't actually honor the count at all.


Top
 Profile  
Reply with quote  
 Post subject: Re: Forth-83 CONVERT
PostPosted: Fri May 24, 2019 10:21 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 855
There is a way around that. Fleet Forth has a word >HERE which takes an address and a count. It copies the string to HERE as a counted string with a blank appended. It is a primitive to speedup WORD . ID. and ," also use it.
if I want a word which prompts a user for a number, for example, I might try something like this code fragment:
Code:

   BEGIN
      CR ." ENTER A NUMBER: "
      PAD DUP 80 EXPECT  SPAN @ >HERE -NUMBER
   WHILE
      2DROP CR ." TRY AGAIN."
   REPEAT



Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

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