6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 12:58 pm

All times are UTC




Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Jun 05, 2012 2:43 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
In case anyone's playing around with 6502 FIG Forth, I thought it'd be good to gather together info regarding its quirks... and a couple of outright bugs.

BUG #1: In this topic Garth explains a subtle but fundamental flaw in the 6502 FIG Forth multiply routine U* (aka UM*). This manifests as incorrect multiplication results in a tiny minority of cases.

BUG #2: Below I will explain a much less subtle problem with the unsigned "less-than" test, U<. As most of you know, the desired behavior for U< is n1 n2 --- flag where the boolean flag is true if, based on unsigned comparison, n1 is less than n2.

FIG Forth defines U< as
Code:
: U< - 0< ;
but that logic is incorrect. The - (subtraction) makes sense, but following it with 0< does not. The boolean left on stack should be determined by whether the subtraction produces a borrow -- not by whether the subtraction produces a 0< (negative) result.

U< could be correctly coded in high level by extending n1 and n2 to double precision. D- SWAP DROP 0< could then be used to produce a reliable result. But, tallying up the memory cost, I decided I might as well code U< in assembler instead:
Code:
;
;                                       U<
;                                       Unsigned less than
; Replaces buggy original Fig Forth U< definition, namely : U< - 0< ;
;
L1246     .BYTE $82,'U',$BC
          .WORD L1244    ; link to =
ULESS     .WORD *+2
          SEC            ;see PS below. The SEC can be omitted if we use CMP
          LDA 2,X
          SBC 0,X        ; subtract
          LDA 3,X
          SBC 1,X        ;carry flag determines result
          TYA            ; A=0
          STA 3,X        ; store high byte of result flag
          ROL A          ; A=0 or A=1
          EOR #1         ; A=1 or A=0
          STA 2,X        ; store low byte of result flag
          JMP POP

Thanks go to whartung for helping with the U< testing; also to Bill Ragsdale, who created 6502 FIG Forth -- a venerable classic! -- in the first place.

-- Jeff

ps: a slightly smaller and faster version begins as follows:
Code:
ULESS     .WORD *+2
          ;               SEC not req'd
          LDA 2,X
          CMP 0,X        ; use CMP to begin the subtract
          LDA 3,X
          SBC 1,X        ;carry flag determines result
          [ etc ]

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Last edited by Dr Jefyll on Mon Jun 18, 2012 7:53 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 05, 2012 5:28 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Thanks for pointing that out. I never used FIG-Forth exactly, but much of what I started with came from FIG-Forth. My U< says (after forming the header):
Code:
     LDA  3,X
     CMP  1,X
     BNE  1$

     LDA  2,X
     CMP  0,X
1$:  BCS  2$
     JMP  POP_TRUE     ; Remove one stack cell, replace the other input with -1.
2$:  JMP  POP_FALSE    ; Remove one stack cell, replace the other input with 0.
I don't remember if that's what it came with or if it's my own fix.

What I have in my '816 Forth is:
Code:
     LDA  2,X
     CMP  0,X
     BCC  ugtcc       ; like BCClong to POP_TRUE.
     JMP  POP_FALSE


I had a problem early on in the 6502 Forth I was working with which defined D< as:
Code:
: D< D- D0< ;
My first encounter with the problem occurred with the numbers -70007FFF and FFF8002. D< said the negative number was not less than the positive number because -70007FFF (or 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. My own replacement (as a secondary, since I didn't use it often enough to warrant a primitive at the time), was:
Code:
: D< ROT
     2DUP =
     IF 2DROP U< EXIT THEN
     > -ROT 2DROP   ;

What I have in my '816 Forth is:
Code:
       LDA  6,X
       CMP  2,X
       LDA  4,X
       SBC  0,X
dlt2:  bvc  dlt1          ; If the resulting sign is wrong,
       EOR  #$8000        ; invert it.
dlt1:  BPL  dult1         ; Now if N is set, d2 < d1 (or
dlt3:  JMP  POP3_TRUE     ; d1 < d2 if branched here from D> ).
BPL dult1 above is like a BPLlong to POP3_FALSE.

With a quick look, I did not find D< in my FIG-Forth book, but be careful.

Hopefully I copied all the code correctly. Although my '816 Forth was never finished to the point of being ready to publish, I am not aware of any bugs in it, and Wally Daniels used it for awhile in his work in the Pratt & Whitney turbine-engine plant in Nova Scotia.

_________________
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?


Top
 Profile  
Reply with quote  
PostPosted: Mon Jun 18, 2012 7:28 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
GARTHWILSON wrote:
I had a problem early on in the 6502 Forth I was working with which defined D< as
Thanks Garth. It's maybe worth emphasizing that D< (and the latter portion of your post) are concerned with signed comparisons, as opposed to unsigned.

Quote:
What I have in my '816 Forth is:
Code:
       LDA  6,X
       CMP  2,X
       LDA  4,X
       SBC  0,X
       [ etc ]
I like how this uses CMP rather than SBC to begin the subtraction -- no SEC is needed. (BTW the same approach could be used in your first example, U<. In other words set it up as a double-precision operation, and omit the superfluous BNE $1.)

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Wed Sep 05, 2012 3:26 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
U/ BUG
U/ is the name used by FIG Forth for the integer division routine, known as UM/MOD in some other Forths. In this page of the 6502.org Source Code Repository Garth shows why some versions (including FIG) are improperly coded and can yield incorrect results. He lists several versions, new and old; for FIG you'd want to switch to the version 3/4 of the way down the page, "Corrected Forth UM/MOD primitive."

VLIST and ?STACK
The topic Firing up a FIG-Forth mentions two quirks. ?STACK fails to issue the Empty Stack ("Msg #0") when expected -- it tolerates a one-item underflow before alerting the user. The cause and fix are here. Also, some people notice that VLIST prints the last ASCII character of each name with bit 7 set. As discussed later in this thread, this minor issue really isn't a bug. It only occurs when the character-out routine you've installed fails to mask off bit 7 as required in the Fig spec.

Disk I/O
The FIG disk routines aren't broken but they sure can be dizzying to learn, partly due to an excess of words named B/ whatever -- where the B slash might mean either Byte, Block, or Buffer. Later in the same topic Firing up a FIG-Forth whartung remarks, "It's all quite confusing, I don't think they're consistent in their usage" and, a few posts later, offers some clarification of the terms. Also a reminder: after power-up but before you attempt to LOAD any Forth source you need to execute EMPTY-BUFFERS or else the result will be nonsensical/hilarious.

Running FIG in Native Mode on 65816
Few will be affected by this quirk. Zero-page,X and Zero-page,Y address modes on 65xx CPU's can usually be relied upon to wrap around, meaning that they always produce an address in Zero-page. FIG Forth exploits this with tricks such as LDA $FE,X where $FE is intended as a negative displacement; ie, LDA -2,X is intended. But the trick fails if the CPU is 65816 and you've moved the CPU from (default) Emulation Mode to Native Mode. Wraparound doesn't occur, and the trick unexpectedly produces an address in page $01. In the topic Getting started in Forth, Daryl aka 8BIT supplies the fixes necessary for FIG Forth to run on 65816 in Native Mode. (Thanks, Daryl!!) Two of the rewritten code snippets he lists are backward compatible and will also run correctly on earlier 65xx CPUs. But the third fix is trickier and pertains to the stack pointer. The solution listed will only work on 65816.

-- Jeff

Edit: clarify the issue of masking bit-7 on characters to be output

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Last edited by Dr Jefyll on Fri Sep 14, 2012 1:48 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 07, 2012 3:36 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
Thanks for grabbing these all in to one place.

Dr Jefyll wrote:
VLIST and ?STACK
The topic Firing up a FIG-Forth mentions two quirks. VLIST prints the last ASCII character of each name with bit 7 set -- a minor problem with no solution suggested (yet). Also, ?STACK fails to issue the Empty Stack ("Msg #0") when expected -- it tolerates a one-item underflow before alerting the user. The cause and fix are here.


The solution to the VLIST issue is that properly implemented, EMIT is 7-Bit only, with the 8th set to 0. This is documented, but with modern systems and expectations, I overlooked it. With that change, VLIST works properly.


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 07, 2012 4:48 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Quote:
Thanks for grabbing these all in to one place.
I suspect there are more points that could be added, so if you notice anything, please speak up. :)

Quote:
properly implemented, EMIT is 7-Bit only, with the 8th set to 0
That's a reasonable solution -- and very easy to implement. It's great for someone just learning... but may be a tad shortsighted for an experienced user.

EMIT handles all characters output to the console, not just the output from VLIST. So, if EMIT masks off bit 7, then you're barred from ever using box-draw or other special, bit7-set characters. An alternative solution is to do the bit7-masking more selectively. It seems ID. would be a good place to do it. VLIST calls ID. which in turn calls EMIT. ID.'s sole purpose is to print the name of a Forth word, and names can't contain bit7-set characters anyway.

cheers

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 07, 2012 5:05 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Since you mention it, I seem to remember modifying my 6502 Forth to handle the 8-bit characters in names, as the special characters thus made available, including many Greek characters used in engineering, or even the degree symbol, are important to me.

_________________
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?


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 07, 2012 5:29 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
I wrote:
names can't contain bit7-set characters
GARTHWILSON wrote:
I seem to remember modifying my 6502 Forth to handle the 8-bit characters in names
Well, that makes the most sense of all! A quirk of Fig Forth is that it uses bit 7 as a last-character marker when storing names in the dictionary. But that's redundant, since the length of the name is also stored. To eliminate this "feature" you'd have to alter name-searching and -scanning words such as (FIND) and TRAVERSE; possibly others..?

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 13, 2012 10:58 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
Dr Jefyll wrote:
EMIT handles all characters output to the console, not just the output from VLIST. So, if EMIT masks off bit 7, then you're barred from ever using box-draw or other special, bit7-set characters. An alternative solution is to do the bit7-masking more selectively. It seems ID. would be a good place to do it. VLIST calls ID. which in turn calls EMIT. ID.'s sole purpose is to print the name of a Forth word, and names can't contain bit7-set characters anyway.

I should point out that when I say "properly implemented", I meant "properly according to the FIG specification". 7-Bit EMIT is by design for the FIG model. That's why this is not an issue with a compliant FIG implementation.

Now, is it optimal? Hardly, for all the reasons mentioned, especially today. But, Back In The Day(tm), 7-Bit was adequate (ASCII IS a 7-bit character set, after all), and they leveraged that in this specific case.

So, the point is that the 7-Bit EMIT is not just for the VLIST issue, it's by design for FIG. Whether ID. begot 7-Bit EMIT or vice-a-versa, I can't say.


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 14, 2012 1:18 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
You're right, Will -- the spec (mentioned in the Installation Manual) is for 7-bit. Sorry if I seemed to brush off your point. VLIST isn't broken as long as the spec is followed and EMIT masks off bit 7.

As for improving the spec and allowing 8-bit characters, even in word names, that's not trivially simple, given that we need to be able to TRAVERSE names in both directions. There's no problem when we know the start address of a name and need to find the end -- we just refer to the length byte that prepends the first character of the name. But, as for going the other direction, finding the start (the length byte) is problematic. (Fig dedicates bit 7 for marking the length-byte but we don't have that option.) We'd have to work backwards from the end, testing each byte individually. We stop when we find a byte which, treated as a length byte, "adds up" and correctly takes us to the end (the last character). Unfortunately, it's possible, although unlikely, for a false match to occur. A user could create a word name that contained a character in its midst which happened to equal the length from its own position to the end. Two examples: Ascii BEL is 07h, and mustn't appear 7 characters from the end. Ascii ! is 21h, and mustn't appear 21h characters from the end. Huh! Talk about quirks!! Word names would need to be screened at the time they're created, either to look for specific instances of a hazard or else to enforce blanket policies such as "Names must not contain characters below 20h" and "Names must be 1Fh or less in length."

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 14, 2012 4:32 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8545
Location: Southern California
Quote:
or else to enforce blanket policies such as "Names must not contain characters below 20h" and "Names must be 1Fh or less in length."

It would be awfully strange to have a charcter below $21 in a name, as although you could get a display, say of an arrow or musical note, in the DOS/ANSI [Edit: that should say IBM437] character set, you would be more or less ruling out getting a printout. $21 is 33 in decimal anyway, and although my names are limited to 31 characters, I've never gotten that high. It looks like the longest name in my '816 Forth kernel itself is 12 characters. I have no doubt that I've had longer ones in applications-- but 31? Nope.

_________________
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?


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 14, 2012 4:46 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Sorry; I guess I got rambling there, exploring the reasons why there have to be restrictions on the names. But I didn't mean to imply the restrictions are onerous -- you're right; they're easy to accept. Still, when new names are created the compiler could/should check for violations.

J.

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Mon Sep 17, 2012 3:49 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
I have not looked at TRAVERSE, but I did wonder why FIG has both the 8th bit terminator AND a length byte. It didn't make sense to me that it would have both.

I guess it's all there to support NFA and PFA. Those are the only references to TRAVERSE I could find.


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 20, 2012 2:40 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
Dr Jefyll wrote:
U/ BUG
U/ is the name used by FIG Forth for the integer division routine, known as UM/MOD in some other Forths. In this page of the 6502.org Source Code Repository Garth shows why some versions (including FIG) are improperly coded and can yield incorrect results. He lists several versions, new and old; for FIG you'd want to switch to the version 3/4 of the way down the page, "Corrected Forth UM/MOD primitive."


This version of the UM/MOD fix is faster and doesn't require a scratchpad byte:

http://6502org.wikidot.com/errata-software-figforth

Garth's version works fine, but rather than just checking the carry at the appropriate place, his version puts the carry in a zero page variable and uses what is, in effect, a 40-bit / 16-bit division routine.

The 65C816 version in Garth's article (in effect, a 48-bit / 16-bit division routine) is much, much simpler by just checking the carry at the appropriate place.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 07, 2020 6:58 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
Dr Jefyll wrote:
I wrote:
names can't contain bit7-set characters
GARTHWILSON wrote:
I seem to remember modifying my 6502 Forth to handle the 8-bit characters in names
Well, that makes the most sense of all! A quirk of Fig Forth is that it uses bit 7 as a last-character marker when storing names in the dictionary. But that's redundant, since the length of the name is also stored. To eliminate this "feature" you'd have to alter name-searching and -scanning words such as (FIND) and TRAVERSE; possibly others..?

Some Forth's had what I consider an odd feature. I don't know if this was common to all Fig Forths, but 64Forth for the Commodore 64 had a variable, WIDTH . New words would have a count and no more than WIDTH letters in their name. I seem to recall that setting WIDTH to 3 was used to save memory. For example, a word might have a length of 11, but with WIDTH set to 3 only the count of 11 ( with the high bit set ) and the first three characters of that word's name would be saved as part of the header with the last letter's high bit set.
A Forth-83 Standard Forth even had this (mis)-feature. Blazin' Forth for the Commodore 64. At least Blazin' Forth would display something like this using WORDS .
Code:
3 WIDTH !  OK
CREATE TEXT-BUFFER  OK
WORDS
TEX********

64Forth's VLIST didn't display such names properly.
Although WIDTH was usually set to 31 ( in Blazin' Forth and 64Forth, at least ) it could be set to values other than 31 and 3. One gotcha. WIDTH must never be set to a value less than 1 or greater than 31.
I don't have this "feature" in my Forth because I don't like name collisions ( finding the wrong name ) because the length and first 'WIDTH' letters happen to be the same as in CONTEXT and CONVERT when WIDTH is set to 3.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

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