Jump to content
IGNORED

fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 06/09/2023]


Lee Stewart

Recommended Posts

I know there is no enforced notion of private words, but there does appear to be some convention to your naming scheme. A brief section in Appendix D, before D.2, that explained the mentality of the naming convention would make the language a bit more infer-able.
Such as all those words that are encased in parenthesis. They seem to be internal in nature. There is a whole slew of words that begin with '?' and have no stack effect. Or is it not as consistent as it appears superficially? Some guidelines on interpreting the naming conventions would be useful, to refresh on before browsing through looking for a word that might do a thing... :)

Some sets of those words encased in parenthesis seem to be internal, part of the implementation more than part of the tool set.. If that is the case, it might be worth breaking them out into their own section, instead of co-mingling them with the 'public API' if you will.

The commented, neatly structured code equivalent of what is packed into the FBLOCKS files would be handy. This would serve as examples. Examples are always welcome.

-M@

  • Like 1
Link to comment
Share on other sites

As usual, Matt, those are good suggestions. There certainly are naming conventions that I will delineate in the preamble to the glossary per your suggestion. Some have changed over the years. Naming any new words I define is influenced by what I think might be clear and informative at the time; but, many of the words in the glossary are inherited from the mostly figForth-influenced TI Forth.

 

Your observation regarding ‘( )’ is correct. Such words (‘< >’ are also used) are the runtime words compiled/executed by words named similarly without the ‘( )’. Such higher-level words are often state-smart and use the runtime words to avoid redundant code. Similarly, ‘[ ]’ are used for compile-time words.

 

A very good reference (which I will also note in the manual update) for explaining/recommending naming conventions is Leo Brodie’s THINKING FORTH: A Language and Philosophy for Solving Problems, available free in electronic format from SourceForge.

 

...lee

  • Like 1
Link to comment
Share on other sites

Here is a first draft of Appdendix D.2: attachicon.giffbForth_2.0_Manual_AppendixD.2.pdf

 

Am I on the right track?

 

...lee

 

That is on the money. This provides a good explanation of rationale, and the example sets illustrate the patterns and spirit of the symbolism. It shows how it is sort of a Forth conjugation.

 

-M@

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

As I was working on adding DOES>CODE: ... ;CODE to do the same thing for DOES>ASM: ... ;ASM that I did for ASM: ... ;ASM with CODE: ... ;CODE , I realized that, by reversing the order of interpreting words and numbers in getCODE for both CODE: and DOES>CODE: , I had made it impossible to perform a calculation on the stack if any of the numbers was not a word. The instant a number, as such, is loaded, it is compiled rather than pushed to the stack. This results in a stack underflow for any calculations and a corruption of the word being defined!

 

You may recall that CODE: ... ;CODE (see posts #1180 and #1214) is used to speed up defining words with machine code by first assuming the next token in the input stream is a number and, if it is, then compiling it into the word definition. This also obviates the necessity of using comma ( , ) to compile each and every number. This trick, however, causes the problem mentioned above. It can be overcome with the following definition:

: N>S ( -- n ) ( IS:n )
BL WORD ( get text of 16-bit number from terminal to HERE)
HERE NUMBER ( convert to 32-bit number on stack)
DROP ; ( drop MSW [should be 0] making it a 16-bit number)

 

Of course, the result of the calculation must be compiled with comma ( , ) because getCODE never sees it.

 

I will give an example of its use in a later post because I have to run now.

 

...lee

Link to comment
Share on other sites

  • 2 months later...

I discovered a bug in M/ while working on adding a couple of words that would allow a user to have a choice of symmetric or floored integer division for words like

 

/MOD / MOD */MOD */

 

The bug occurred when I converted M/ to Assembly Language Code a build or two back. Any remainder in symmetric integer division should have the sign of the dividend and M/ uses the sign of the divisor! :-o I will fix it post haste. This bug fix will be part of the release of fbForth 2.0:9, which should happen before I get to the Chicago Faire next month. I will bring cartridges for sale ($30) or exchange.

 

Speaking of fbForth 2.0:9, I will be adding a handful of words to the resident dictionary:

 

N>S DOES>CODE: \

 

There are two or three other words I would like to add, but I may run out of room. We'll see...

 

...lee

  • Like 4
Link to comment
Share on other sites

I am adding a user variable, S|F (for “Symmetric or Floored”) that will be a flag for M/ to act like one of the ANSI Forth words, SM/REM (symmetric M/ ) or FM/MOD (floored M/ ). All three of these words have the same stack signature:

 

( d n --- rem quot )

 

That is, they take a signed double number (32 bits) d as the dividend and a signed single number (16 bits) n as the divisor, leaving a signed single number remainder rem and a signed single number quotient quot. All of these words use signed integer division. Where they differ is whether the division is symmetric or floored when the signs of divisor and dividend differ. The question is whether to use symmetric (SID) or floored integer division (FID). There are good reasons to choose FID over SID, especially in processes like robotics, where symmetry around 0 is important. Probably the only reason (and, probably, not a good reason!) to choose SID is that most of us expect the answers it gives. Here is a good explanation of signed integer division from the September, 1983 issue of Dr. Dobb's Journal:

 

 

 

Signed Integer Division

  by Robert L. Smith
 
Originally appearing in Dr. Dobb's Journal September 1983
 
Not all methods of integer division produce a uniform result when the
dividend and divisor have opposite signs. This may not be so important in
the area of commerce where negative values are perhaps used less. When
dealing with measurement and control, however, uniformity becomes more
significant. The Forth-83 Standard adopts a method for signed integer
division called "floored" division. While APL has used this method for the
RES function for years and Small-Talk provides it as one of three methods
for integer division, acceptance of the Standard makes Forth the first
"popular" language to embrace this method based on its theoretical merits.
The problem is one of mathematical purity versus user expectation. This
article will attempt to clarify some of the issues involved.
 
Integer division is a mathematical function of two integers (a dividend and
a divisor) that yields an integer quotient and an integer remainder. That
appears to be a fairly straightforward operation, but there is not
universal agreement of the desired results when one or both arguments are
negative. When an integer quotient is used in plotting or machine control,
the desired function is usually _not_ the quotient given by the majority of
computers.
 
Most computers with a divide function produce a quotient that has a
property of symmetry around zero when plotted as a function of the
dividend, due to the fact that the quotient is rounded toward zero.
Speaking mathematically, the property is actually one of anti-symmetry,
where the sign of the quotient is reversed when the sign of the dividend
(or numerator) is reversed. For integer division, this "symmetric"
property leads to a sort of discontinuity around zero. In this case, the
remainder is either zero or it takes the sign of the dividend. Figure 1a
illustrates the quotient q as a function of a variable dividend, and a
constant divisor 3. We readily see the discontinuity near zero. This may
 
 +-------------------------------------------------------+
 |                                                       |
 |                        3 +                 o o o      |
 |                          +           o o o            |
 |       -9                 +     o o o       9          |
 |    +-+-+-+-+-+-+-+-+-o-o-o-o-o-+-+-+-+-+-+-+-+-+-     |
 |                o o o     +                            |
 |          o o o           +                            |
 |    o o o              -3 +                            |
 |                                                       |
 |      (a) Quotient for Symmetric Integer Division      |
 |                                                       |
 |                                                       |
 |                                                       |
 |                          +                            |
 |                        2 +   o     o     o     o      |
 |       -9                 + o     o     o     o        |
 |    +-+-o-+-+-o-+-+-o-+-+-o-+-+-o-+-+-o-+-+-o-+-+-     |
 |      o     o     o     o +                 9          |
 |    o     o     o     o   + -2                         |
 |                          +                            |
 |                                                       |
 |      (b) Remainder for Symmetric Integer Division     |
 |                                                       |
 |                      Figure 1.                        |
 |_______________________________________________________|
 
 
be reasonably serious when this quotient function is used for plotting or
moving robot arms. The integer quotient needs an associated remainder:
 
          r = n - q * d
 
where n is the numerator or dividend, d is the denominator or divisor, q is
the quotient, and r is the remainder. The remainder function for the
constant divisor 3 is illustrated in Figure 1b. If we look at the case of
positive dividends and divisors, we observe the cyclic property that
 
          r(n+d) = r(n)
 
In other word, the remainder usually has a repeating or cyclical property
as the dividend changes. For the remainder shown in Figure 1b, we see that
this simple property is not maintained for dividends between -d and 0.
 
If we require that the remainder be cyclical, then the quotient no longer
has any unusual discontinuities. There are a number of possible choices
here. One obvious choice is to make the remainder the same as the modulus
or residue function (1). In this case the quotient is rounded toward minus
infinity. This rounding procedure is called the "floor" function. Figure
2 shows the floored quotient and its related modulus for the same
arguments used in Figure 1. Notice the quotient behaves in a more nearly
continuous fashion around zero. This is the form used in the Forth-83
Standard, as well as some of the older versions of Forth. The National
16032 microprocessor produces floored division in addition to the older
"rounded toward zero" variety. The modulus function is called MOD in
Forth-83 and in the National 16032. It is called RES in APL.
 
 
 +-------------------------------------------------------+
 |                                                       |
 |                        3 +                 o o o      |
 |                          +           o o o            |
 |       -9                 +     o o o       9          |
 |    +-+-+-+-+-+-+-+-+-+-+-o-o-o-+-+-+-+-+-+-+-+-+-     |
 |                    o o o +                            |
 |              o o o       +                            |
 |        o o o          -3 +                            |
 |                                                       |
 |      (a) Quotient for Floored Integer Division        |
 |                                                       |
 |                                                       |
 |                                                       |
 |                          +                            |
 |            o     o     o +   o     o     o     o      |
 |       -9 o     o     o   + o     o     o     o        |
 |    +-+-o-+-+-o-+-+-o-+-+-o-+-+-o-+-+-o-+-+-o-+-+-     |
 |                          +                 9          |
 |                          + -2                         |
 |                          +                            |
 |                                                       |
 |      (b) Remainder for Floored Integer Division       |
 |                                                       |
 |                      Figure 1.                        |
 |_______________________________________________________|
 
The "floored" quotient shown in Figure 2 is not anti-symmetric around zero.
However, for odd divisors one may easily obtain a symmetric result by
adding a correction factor to the dividend prior to division. Although the
quotient is generally not defined when the divisor is zero, the modulus is
usually defined to take the value of the dividend for this case. If
infinities are not allowed in computer representations, and the product of
any number and zero is always zero, then this definition preserves the
equation
 
          n = q * d + r
 
for all values of d, including zero.
 
Alternative remainder functions include a positive modulus and a remainder
that takes the sign of the quotient (2). Some other possibilities have the
undesirable feature of negative remainders when the dividend and divisor
are both positive.
 
Floored division is simply more useful in the majority of applications
programs. The major objection is that the results are not what most people
expect: -1 divided by 4 gives 0 in the rounded-toward-zero division case,
but -1 for floored division. Both cases give the same results when the
dividend and divisor have the same sign. Timing efficiencies may play a
small role in deciding which form of division to use, but generally the
division process is sufficiently slow that additional tests for different
forms of rounding take only a little extra time. Indeed, for some
processors with built-in signed and unsigned divide functions, it may be
faster in the common case of positive arguments to test signs and use the
unsigned division than to just use the signed division function. If you
have an older Forth system (such as 79-Standard or fig-FORTH), the screen
in Figure 3 shows a high-level conversion from the older form of /MOD to
the newer version. For those unfamiliar with Forth, /MOD takes two
arguments, the dividend and the divisor, and returns two results: the
quotient and the modulus, or remainder. The quotient is returned as the
most accessible element on the stack.
 
The appearance of floored division in some of the newer processor chips and
languages indicates the increasing awareness of its utility. We might note
in passing that even floating-point division will probably be different in
the future than it was in the past due to the new Floating-Point Standard,
which will require proper rounding of the quotient.
 
References
(1) Donald K. Knuth, _The Art of Computer Programming: Volume I,
    Fundamental Algorithms_, Second Edition, Menlo Park: Addison-Wesley, 1973,
    p. 127.
(2) Robert Berkey, "Integer Division, Rounding and Remainders", 1982 FORML
    Conference Proceedings, San Jose, California: Forth Interest Group, 1983,
    pp. 13-23.
 
 +---------------------------------------------------------------+
 |                                                               |
 |   ( Define 83-Standard  /MOD  in terms of old  /MOD )         |
 |   : /MOD   ( num den -- mod quot )                            |
 |      OVER OVER XOR 0<       ( test signs of arguments )       |
 |      IF                     ( signs are different )           |
 |           >R R@ /MOD OVER   ( divide and examine remainder )  |
 |           IF                ( non-zero remainder )            |
 |                1- SWAP R> + SWAP   ( adjust results )         |
 |           ELSE                     ( zero remainder )         |
 |                R> DROP             ( discard old den )        |
 |           THEN                                                |
 |      ELSE                          ( signs just the same )    |
 |           /MOD                     ( just divide normally )   |
 |      THEN ;                        ( end of definition )      |
 |                                                               |
 |                                                               |
 |                            Figure 3.                          |
 |      High Level Forth Code to Convert to Floored Division     |
 |_______________________________________________________________|
 

 

 

 

Currently, M/ uses SID since fbForth is based on fig-Forth, which uses SID. It will continue to be the default to support expectations of TI Forth programmers. However, S|F will make it easy for the user to change the behavior of M/ at will to accommodate FID. Doing so will change all of the following words to use FID because they are based on M/ :

 

/MOD / MOD */MOD */

 

I should note that TurboForth uses FID by default because it complies with the Forth-83 Standard, which, as noted in the above article, was the first standard to make that move. I would actually prefer to make FID the fbForth default, but have chosen not to do so for the reasons in the last paragraph.

 

...lee

Link to comment
Share on other sites

Though the fbForth 2.0:9 (posting soon!) implementation of SM/REM and FM/MOD is in Assembler, I thought some of you might be interested in the high-level Forth code for these words:

 

Spoiler

\ Symmetric Integer Division of signed 32-bit numerator by signed 16-bit denominator
\ yielding signed 16-bit remainder and quotient.

: SM/REM ( d n --- rem quot )
   OVER >R >R     \ copy MSB of numerator and move denominator to return stack
   DABS R ABS     \ make numerator and denomitor both positive
   U/ R> R XOR    \ divide to get remainder and quotient; get sign of quotient
   +- SWAP        \ give quotient proper sign
   R> +- SWAP  ;  \ give any remainder sign of numerator

\ Floored Integer Division of signed 32-bit numerator by signed 16-bit denominator,
\ yielding signed 16-bit remainder and quotient.

: FM/MOD ( d n --- rem quot )
   OVER OVER XOR 0<        \ den and num signs differ?
   IF                      \ signs differ
      >R R SM/REM OVER     \ do symmetric division
      IF                   \ deal with remainder
         1- SWAP R> + SWAP \ floor quotient;rem = rem + den
      ELSE                 \ no remainder
         R> DROP           \ clean up return stack
      THEN
   ELSE                    \ den and num signs same
      SM/REM               \ do symmetric division (same as floored here)
   THEN ;

 

 

The code for FM/MOD is written in terms of SM/REM and is very similar to the Forth code for /MOD at the end of the article cited in my last post.

 

...lee

Link to comment
Share on other sites

FID vs. SID, I remember being driven nuts by this a long time ago, but never knew the concepts behind it. I sadly brute forced my way through the problem at the time.

 

It is awesome that you are squeezing the choice into fbForth!

 

-M@

 

Thanks for that, Matt. I was not sure anyone but me cared about it (with the likely exception of Mark Wills). I will try to post fbForth 2.0:9 before the end of next week. As things stand at the moment, The spare room in the cartridge is

 

Bank# Bytes Left

----- ----------

0 120

1 228

2 236

3 50

 

...lee

  • Like 3
Link to comment
Share on other sites

Wow! Yesterday, while converting FBLOCKS to run with fbForth 2.0:9, I discovered a nasty bug caused by my conversion, in a previous build, of ;CODE from a high-level colon definition to ALC. After I finally realized the bug was not caused by my recent changes, I figured out what was going wrong and fixed it. The fix was a bit convoluted, so I will defer explanation until someone actually wants to know. I should still be able to post this build by next week's end, barring discovery of another such bug.

 

...lee

  • Like 1
Link to comment
Share on other sites

Where's the bug that frees up space after it's rooted out? That's a miniscule about of free space.

 

Hah! That would be nice. Talk to @Willsy about how little space is truly “minuscule”. fbForth 2.0:9 has loads of space compared to TurboForth!

 

I have other options if I really need more space, but I don't see the resident dictionary growing that much from here on out.

 

...lee

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...
  • 2 months later...

I am in the process of developing words to mount/unmount compact flash (CF) volumes as DSK1, DSK2 and/or DSK3 for the nanoPEB and CF7. Such words will obviate the necessity of swapping the fbForth 2.0 cartridge for the E/A cartridge, loading CF2K to make the mounting changes and swapping cartridges again to restart fbForth 2.0! :-o Of course, the process could also be managed by exiting fbForth 2.0, starting TI Basic to execute the appropriate “CALL MOUNT” and “CALL UNMOUNT” commands Jaime provided with the hardware, exiting TI Basic and restarting fbForth 2.0—almost as bad.

 

I am puzzling over a couple of ways to code these words. One is very simple but does not persist the mounts/unmounts after a reset. It involves writing the desired volume number to the desired virtual disk marker (16 bits) above the TI disk buffer space in VRAM—pretty simple.

 

The other involves talking directly to the CF through IDE commands after setting up necessary buffers/variables and coding the commands. Fred Kaal (@F.G. Kaal) was kind enough to share the relevant code from his CF2K. This method would still need the small amount of code for the first option above. This second option is, obviously, a significantly greater programming effort.

 

I am inclined to code option one and name the words something other than MOUNT and UNMOUNT to indicate the lack of persistence. This would provide some convenience while I work on the second option, if I do it at all.

 

Just thinking out loud...

 

...lee

  • Like 1
Link to comment
Share on other sites

How do you 'talk' to the nanoPEB from the TI at a lower level than the DSKx DSR, is it via subroutines in the ROM header, direct calls to ROM code, or something else?

 

The CF card is managed pretty much as a standard IDE drive (master) with 512-byte sectors and a subset of the IDE commands available. You can talk to the nanoPEB/CF7 at the IDE command level through read/write ports in DSR memory space. The difficulty is that the nanoPEB's ports are different from the CF7's! :-o This requires querying the DSR for unique device names. SIO is unique to the nanoPEB V1; COM1 is unique to the nanoPEB V2; and PIO is unique to all the CF7s.

 

When a “disk” sector is read/written, only the first 256 bytes are used. This makes the DSR simpler because data are always accessed from the start of a sector, one word (usually) at a time. If all 512 bytes of a sector were used, odd TI sectors would require a 256-byte dummy read before the first byte of the odd TI sector would be reached. And, of course, a scheme would need to be devised for managing it, which would definitely complicate the DSR. That is why only half of the CF is used for the TI file system. But, I digress...

 

...lee

Link to comment
Share on other sites

Here is my offering for the nanoPEB/CF7 virtual disk mounting:

HEX
: CF? ( -- flag ) \ nanoPEB/CF7 present?
   3FF8 VSBR SWPB 3FF9 VSBR +    \ get magic number
   AA03 = ;                \ leave TRUE if nanoPEB/CF7
0 VARIABLE CFVOL# 4 ALLOT  \ volume # array for DSK1-DSK3
: CFVOLS ( -- volDSK1 volDSK2 volDSK3 ) \ get vol#s in DSKs
   CF? IF                  \ if CF, continue
      3FFA CFVOL# 6 VMBR   \ get vol#s to DSK array
      CFVOL# DUP 6 + SWAP DO
      I @                  \ get vol#s to stack
      2 +LOOP
   ELSE
      ." No CF detected!"  \ else error message
   THEN ;
: CFMOUNT ( vol# dsk# -- ) \ mount CF vol# in DSK<dsk#>
   CF? IF                  \ if CF, continue
      3FFB SWAP            \ stack:vol# 3FFB dsk#
      CASE
         1 OF ENDOF        \ DSK1 stack:vol# 3FFB
         2 OF 2+ ENDOF     \ DSK2 stack:vol# 3FFD
         3 OF 4 + ENDOF    \ DSK3 stack:vol# 3FFF
         ELSEOF ." DSK# must be 1-3!" ABORT ENDOF  \ error!
      ENDCASE
      OVER SWPB OVER 1- \ stack:vol# 3FFB|3FFD|3FFF vol#[LSB-MSB] 3FFA|3FFC|3FFE
      VSBW VSBW            \ copy bytes of vol# to DSK slot in VRAM
   ELSE
      ." No CF detected!"  \ else error message
   THEN ;
DECIMAL
CF? checks for the magic number, >AA03, at VRAM >3FF8, where the nanoPEB/CF7 DSR places it. It leaves 1 (TRUE) if found and 0 (FALSE) if not.

 

CFVOL# is an array of three 16-bit numbers representing the CF volume numbers mounted in DSK1, DSK2 and DSK3.

 

CFVOLS populates the CFVOL# array and leaves on the stack the volume numbers associated with DSK1, DSK2 and DSK3.

 

CFMOUNT mounts the volume supplied in the DSK# supplied. The following entry will mount volume #234 in DSK2:

234 2 CFMOUNT

The volumes mounted by CFMOUNT will persist only through the current session of fbForth 2.0. This includes cycling through COLD and BOOT . A reset to the TI-99/4A title screen will mount the three volumes stored in the CF card before fbForth 2.0 was started. As you can see, you will need to use the TI Basic CALL MOUNT, CF2K or TI99Dir to write the volume mounts to the CF card. I am not sure I will change this anytime soon because of the significantly greater programming effort required.

 

After some time for discussion and testing of these words, I will add them to FBLOCKS.

 

...lee

  • Like 2
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...