Common D< Forth bug

Topics relating to various Forth models on the 6502, 65816, and related microprocessors and microcontrollers.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Common D< Forth bug

Post by GARTHWILSON »

In the Forth 83 topic, I mentioned a bug in D< that I think is in the in the public-domain material (but if not, it's certainly nothing to defend if it is wrong!), but apparently I never discussed it. D< gets defined as:

Code: Select all

: D<  D- D0<  ;

which looks quite innocent, and the D- and D0< do work right, but the algorithm doesn't take into account the underflow problems that occur when D1 minus D2 has a result that is so negative that it again becomes positive.

My first encounter with the problem occurred with the numbers -70007FFF and FFF8002. D< said the negative number was not less than the positive one, because -70007FFF (8FFF8001) minus FFF8002 gives 7FFFFFFF, which is positive. However if we add 1 to the first number making it -70007FFE, the D- gives an answer of 80000000, and D< correctly says that yes, the first number is indeed less than the second.

I redefined it in 6502 Forth as:

Code: Select all

: D<  ROT
      2DUP =
      IF 2DROP U< EXIT THEN
      > -ROT 2DROP  ;

In my '816 Forth, I defined it as a primitive instead, because although D< is not used that much, the fact that it was not only faster as a primitive but even shorter than the secondary made it doubly worth it:

Code: Select all

       HEADER "D<", NOT_IMMEDIATE    ; ( d1 d2 -- f )
DLT:   PRIMITIVE
       LDA  6,X
       CMP  2,X
       LDA  4,X
       SBC  0,X
       BVC  dlt1         ; If the resulting sign is wrong,
       EOR  #$8000       ; then invert it.
dlt1:  BPL  dult1        ; Now if N is set, d2 < d1.
       JMP  POP3_TRUE
 ;-------------------


Edited: See explanation below.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Common D< Forth bug

Post by barrym95838 »

There's something wrong with your second code sample; it looks like the BPL instruction should branch somewhere other than itself! Sorry that it took me nearly a year to notice ...

Mike
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Common D< Forth bug

Post by GARTHWILSON »

You're right. Thanks for catching that. It's on my DOS computer w/o internet or other networking, and I probably just copied it by hand instead of copying first to SD card and then over, which unfortunately requires resetting the DOS machine because the SD card is on an IDE converter. (Later it got backed up on this computer anyway, with the SD card.) I'll fix it above. I left out a letter. It should say BPL dult1, not dlt1. dult1 is at the end of DU<, and is a jump to POP3_FALSE.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
edx
Posts: 14
Joined: 14 Sep 2014

Re: Common D< Forth bug

Post by edx »

GARTHWILSON wrote:
In the Forth 83 topic, I mentioned a bug in D< that I think is in the in the public-domain material (but if not, it's certainly nothing to defend if it is wrong!), but apparently I never discussed it...
Most early Forths defined '<' and 'D<' in the same manner. It wasn't a bug but a "feature" - the characteristics of which are discussed in the polyForth manual (DB005-120825-PF-REF.pdf). By the time the Forth-79 Standard came out it required '<' operate over the full 16-bit signed range (though it's not clear whether similar provisions applied to 'D<').
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Common D< Forth bug

Post by GARTHWILSON »

edx wrote:
It wasn't a bug but a "feature" - the characteristics of which are discussed in the polyForth manual (DB005-120825-PF-REF.pdf).
Do you have a URL for it? Any way you look at it, for D< to say that -70007FFF was not less than 0FFF8002 in signed 32-bit numbers is just plain wrong.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: Common D< Forth bug

Post by Klaus2m5 »

The manual can be downloaded here: http://www.greenarraychips.com/home/doc ... PF-REF.pdf

off topic edit: The discussion wether something is a bug or a feature reminds me of Monty Python's dead parrot sketch.

"It is dead"
"No it's not. It is resting"

http://www.youtube.com/watch?v=4vuW6tQ0218
Last edited by Klaus2m5 on Sun Sep 14, 2014 5:26 pm, edited 1 time in total.
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Common D< Forth bug

Post by barrym95838 »

edx wrote:
... It wasn't a bug but a "feature" - the characteristics of which are discussed in the polyForth manual (DB005-120825-PF-REF.pdf).
I'm a bit fuzzy this morning (haven't gone to the gym yet), but a simple-minded search of the document yields no "discussion" of D< ... just a brief one-line definition.

Mike
edx
Posts: 14
Joined: 14 Sep 2014

Re: Common D< Forth bug

Post by edx »

barrym95838 wrote:
I'm a bit fuzzy this morning (haven't gone to the gym yet), but a simple-minded search of the document yields no "discussion" of D< ... just a brief one-line definition.
See section 2.2.2
GARTHWILSON wrote:
Any way you look at it, for D< to say that -70007FFF was not less than 0FFF8002 in signed 32-bit numbers is just plain wrong.
Clearly that wasn't the way C.Moore and Forth Inc looked at it.

If there be a mistake it lies in expecting modern behaviours from 35+ year software and being caught out by it. :wink:
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Common D< Forth bug

Post by barrym95838 »

edx wrote:
... If there be a mistake it lies in expecting modern behaviours from 35+ year software and being caught out by it. :wink:
To me, it's not a matter of modern vs. vintage, but more of a matter of correct vs. incorrect. The 6502's overflow flag worked correctly for signed binary from its inception ... it was up to the programmer to make proper use of it. In the case of Forth, I believe that access to the overflow flag was limited for application programs, so that duty should have fallen on the person writing the primitives.

The discussion in Section 2.2.2 makes clever use of the phrases "handles the vast majority" and "is generally safe", so I guess it boiled down to a matter of efficiency vs. exhaustive correctness. I have little doubt that a non-zero percentage of modern software hides similar trade-offs, documented or not.

Mike
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Common D< Forth bug

Post by BigEd »

That clock view of binary arithmetic reminded me - rightly or wrongly - of the timer arithmetic used by Occam for the Transputer. The chip has built in timers, and you want to be able to calculate time intervals regardless of the actual value of the time. You need a circular model.
edx
Posts: 14
Joined: 14 Sep 2014

Re: Common D< Forth bug

Post by edx »

barrym95838 wrote:
To me, it's not a matter of modern vs. vintage, but more of a matter of correct vs. incorrect. ... In the case of Forth, I believe that access to the overflow flag was limited for application programs, so that duty should have fallen on the person writing the primitives.
In Borland Pascal I can mistakenly enter a literal number which exceeds the range of an int and the compiler will detect it. Not so with Borland C which will happily accept the number and produce nonsense leaving me to find the error myself. Which behaviour is "correct"?

IMO it depends on the language and the extent to which it's prepared to protect users from themselves. Could I live with the signed relationals of Fig-Forth and PolyForth? Possibly. I've never had to because my Forth follows later Standards. For all I know it may be as easy as Forth Inc claim - particularly when there is D< to handle the exceptional cases.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Common D< Forth bug

Post by barrym95838 »

edx wrote:
... Which behaviour is "correct"?
That's a rhetorical question, right? :wink:
Quote:
... particularly when there is D< to handle the exceptional cases.
But that was Garth's original beef. D< DIDN'T handle the exceptional cases for him, and he had to re-write the primitive.

Mike
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Common D< Forth bug

Post by barrym95838 »

Hey, Garth. Did you try checking the most significant half first? I haven't tested this, but I think that I might have saved you a whole byte!

Code: Select all

       HEADER "D<", NOT_IMMEDIATE    ; ( d1 d2 -- f )
DLT:   PRIMITIVE
       LDA  4,X        ; compare high words first
       CMP  0,X
       BMI  dlt1
       BNE  dult1      ; ( POP3_FALSE )
       LDA  6,X        ; only need to compare low words
       CMP  2,X        ;  if the high words were equal
       BPL  dult1
dlt1:  JMP  POP3_TRUE
 ;-------------------
Mike
White Flame
Posts: 704
Joined: 24 Jul 2012

Re: Common D< Forth bug

Post by White Flame »

I see you fell into the same trap as the original D<. ;)

BMI/BPL are insufficient for detecting signed less-than; you need to involve the overflow flag as well.

http://www.6502.org/tutorials/compare_beyond.html#5
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Common D< Forth bug

Post by barrym95838 »

White Flame wrote:
I see you fell into the same trap as the original D<. ;)
Oops! ... brain hiccup. Sorry, folks.

But ... looking at the other technique at your link, involving flipping the sign bits then doing an unsigned compare:

Code: Select all

       HEADER "D<", NOT_IMMEDIATE    ; ( d1 d2 -- f )
DLT:   PRIMITIVE
       LDA  4,X        ; flip sign bit of d1
       EOR  #$8000
       STA  4,X
       LDA  0,X        ; flip sign bit of d2
       EOR  #$8000
       STA  0,X
       BRA  DULT+2     ; exit through DULT machine code
dlt1:  JMP  POP3_TRUE
 ;-------------------
Meh, cost me a byte, unless I can be sure that it's okay to get rid of the JMP POP3_TRUE ... never mind!

Mike
Post Reply