>> It's a good question. It would be helpful to hear what you *have* read,
>> so we know where you're starting from. Have you read "Thinking Forth"
>> (Google for it)? Although it's somewhat dated, it does address some of
>> these issues.
>
> I've read and own both Starting Forth and Thinking Forth. I treat
> them like bibles and re-read Thinking Forth for fun/encouragement
> often. Though the knowledge in them is starting to get a little
> stale ...
Starting Forth is very stale, particularly the paper copies (the online
versions have been partially updated). Since you say you have
SwiftForth, you have a copy of Forth Programmer's Handbook (pdf). You
might benefit from Forth Application Techniques, and it's pretty cheap.
>> Most of our application work nowadays is in embedded systems, which is
>> reflected in my comments below.
>
> It's frustrating; I know that embedded is where Forth thrives today.
> But as I want pretty badly to become comfortable in Forth to build my
> own apps on Windows, I puzzle without knowing for sure what works and
> what doesn't and often I feel like I'm needlessly re-playing history,
> and one that I don't quite understand. I'd like to see more source
> code from serious applications, even if they're old, so long as they
> competed with the other apps from their time. Ones written in "good"
> Forth style, not some of the sources I've seen that look just like C
> code with lots of nesting, long definitions, each line with a comment
> basically repeating the statement...
There's a fair amount of sample code with SwiftForth. Although they
aren't large real-world apps, they do illustrate useful approaches. And
I highly recommend the SwiftForth email group -- a number of the folks
there have very complex apps, and they can certainly share Windows
experiences and code. They don't chatter a lot, but are always willing
to respond to questions, like "how would you go about doing ?"
>> One of my favorite good examples is the construction of a chip-select
>> table for a 68332. Single bits here and there in the cells of the table
>> control critical functions, and they make little sense examined in hex.
>> We made a set of words that set the appropriate bits at compile time
>> in ways that are absolutely obvious:
>
>
>> { ---------------------------------------------------------------------
>> Chip select table
>>
>> CHIPS is the chip select table for the NMIX-0332.
>> |CHIPS| is the table size in bytes.
>> --------------------------------------------------------------------- }
>>
>> CREATE CHIPS
>> 0 Select16 \ CS10 (A23)
>> Select16 \ CS9 (A22)
>> Select16 \ CS8 (A21)
>> Select16 \ CS7 (A20)
>> Select16 \ CS6 (A19)
>>
>> 0 Select16 \ CS5 (FC2)
>> Select16 \ CS4 (FC1)
>> Select16 \ CS3 (FC0)
>> Select16 \ CS2 (BGACK)
>> Select16 \ CS1 (BG)
>> Select16 \ CS0 (BR)
>> Select16 \ CSBOOT
>>
>> ( CSPAR0) W, ( CSPAR1) W,
>>
>> Async Both Read AS 9 WS S/U 'PROM 128K \ CSBOOT
>> Async Both Read AS 9 WS S/U 'PROM 20000 + 128K \ CS0
>> Async Both R/W AS 1 WS S/U 0 128K \ CS1
>> Async Both R/W AS 1 WS S/U 20000 128K \ CS2
>> N/C \ CS3
>> N/C \ CS4
>> N/C \ CS5
>> N/C \ CS6
>> N/C \ CS7
>> N/C \ CS8
>> N/C \ CS9
>> N/C \ CS10
>>
>> HERE CHIPS - EQU |CHIPS| IDATA
>
> Interesting idea. I can't read the code (probably because I don't
> have that much experience in embedded programming) but here's a
> question I want to know: Were the words for defining the bit patterns
> defined just before this code, or as part of the support code for the
> entire app?
They are support code for configuring a 68332 for any application. Each
of these words is executed interpretively to set a bit in the
chip-select table to configure the chip. I've seen similar strategies
used to initialize other kinds of tables, but this example was easy to find.
>> We don't use wordlists very extensively in applications, but they are
>> absolutely vital in our systems, especially cross- and meta- compilers.
>> They're also critical to SwiftForth's SWOOP facility.
>
> I know you said you don't use them often in apps but how have you used
> them?
In applications where the user interface involves typing Forth words,
it's sometimes desirable to have a closed wordlist of user words, so
users can't/won't inadvertently type words that could cause trouble.
Those user-level words can have a lot of checks built in.
>> Not sure just what you mean, here.
>
> It's from something I read by Dr. Ting. I guess basically it means,
> taking advantage of the data stack to improve readability simply.
("Featuring the data stack") Well, if I had a word that recorded a data
sample, I'd call it SAMPLE. To take samples, I'd define something like:
: SAMPLES ( n -- ) 0 DO SAMPLE LOOP ;
...because if elsewhere in the program you see
... 100 SAMPLES ...
it will be pretty obvious what's going on. The relationship between a
single and plural noun plus the obvious use of the stack aids readability.
>> See Thinking Forth and Forth Application Techniques. We use this
>> capability extensively in applications, most of which have app-specific
>> data structures of some kind. Examples range from circular buffers to
>> named bits in an I/O register. Automatically indexing arrays are also
>> very useful.
>
> What forms have these arrays taken? Are they usually a cell wide, or
> have the been other data-widths? Have you made use of 2D arrays? Do
> you use the [] suffix? What has/have been the most readable form(s),
> if not that?
It totally depends on the data. The thing is, that you can define data
structures to accommodate the natural "shape" of the data, rather than
having to mush the data into a predefined structure. Arrays can have
byte elements, string elements, cell elements, double-cell elements,
floating point elements, whatever your app calls for. They can be 1D,
2D, 3D, whatever fits its inherent structure. I'm unfamiliar with a []
suffix.
> What have circular buffers been useful for?
Two very different examples:
a) polynomial coefficients: each time you invoke the name of the array
you get the next one, and it cycles automatically.
b) a data buffer, into which one task or an interrupt routine is putting
data, and another task is taking it out for further processing. Two
pointers, one where to put the next sample, one where to fetch the next.
Obviously the sample rate has to be slow enough (or the processing
task fast enough) that they don't wrap inauspiciously.
There are many more, but these two come to mind.
>> We don't actually worry a lot about name collisions in applications. A
>> lot of application words are only used locally, and if there's a
>> different one somewhere the compiler will issue a warning. Sometimes we
>> just examine the other version, conclude there's no practical conflict,
>> and forge ahead.
>
> Ah, OK, so you don't try to make as many words as possible "global"?
> I got the sense from Chuck Moore that that was a good approach. But
> somehow it seemed a bit extreme too, since the context isn't always
> clear in his writings or interviews, so I've been confused over it.
They may be technically global, but only interesting within a particular
section of the code. I see no reason to be paranoid about global names
(or variables).
>> We used to have an extensive database package in polyFORTH. We have
>> made a modified version to use for flash databases, which we'll probably
>> include in SwiftX at some point.
>
> Have you ever done apps using the OS's filesystem extensively?
Oh, yes. This is one of the areas where SF and TF are particularly
deficient, because they only considered completely self-contained
Forths. There are examples of using Windows files in Application
Techniques.
> Keeping on the database topic, what good are blocks, really? Are they
> at all applicable to realtime applications? Is it ever useful to
> access blocks only to copy the data to larger static buffers?
Blocks were invented for purely native Forths (no host OS), where they
were far faster and more reliable than OS-style files. They were
retained in ANS Forth for compatibility with native Forths. However,
nowadays, OS-hosted Forths are far more common, and blocks almost never
used (although we embedded systems types use a similar concept for
accessing large banks of flash).
>> You mean like custom structure words? They're discussed here far more
>> than they're used in applications, in my experience.
>
> That's surprising. I implementing a fairly complex set of structures
> for my library to implement a decentralized external object management
> system. But now I'm thinking maybe that's not such a good idea
> because it implies some possible complexities in the future when you
> want to do things differently and the way you load resources (like
> images, sounds) is locked into these words.
You're talking about data structures, here, aren't you? I was talking
about things like loops and conditionals. The standard Forth loops and
conditionals are perfectly adequate for 99%% of applications, IMO. Data
structures, as I said above, should adapt to the natural shape and
structure of the data itself.
> I also meant extensions to the compiler like IMMEDIATE words that look-
> ahead to the next word to implement a special syntax or other
> extensions to Forth as a language. For instance I taught SwiftForth
> how to interpret fixed-point literals. (I plan on contributing it but
> I'm not going to submit it yet, the wordset is incomplete and depends
> on a C library for some words)
That should be very easy to do without involving C libraries.
>> Well, that's what an application is, isn't it? The boundary between
>> "system" and "application" is pretty fuzzy in the Forth world. Every
>> time we write an application we end up with a system adapted to support
>> that application domain. Makes it really easy to do similar projects.
>> For example, GE Multilin in Vancouver has a whole line of multiplexers
>> programmed in Forth, with a lot of shared code (see
>>
http://www.forth.com/resources/appNotes/app-GE-Multilin.html).
>
> That sounds wonderful. But how is it done? Is the application built
> in two parts, a library and "the application"? Where is the
> distinction between the re-used portion and the app-specific portion
> made, if it all? Or do you not worry about that until the first app
> is done and you come across a new but similar problem and then pick
> the old app apart for reusable stuff? Or do you make a copy of the
> old app and modify it until it suits the new problem?
Well, the system itself is organized into a number of files, each of
which is a well-defined set of related functions. There's a file that
loads the ones you need. Similarly, your app will consist of a number
of files of closely-related words, and a master file that loads the ones
you need in the right order. You try, first time around, to layer these
files with generic support functions separate from higher-level
processing, user interface, etc. Your next similar app can probably use
a lot of the same files, but will require some new ones, and some
modified versions of the old ones. It should have its own master load
file, which picks the ones appropriate for this app. Over time, you
build up your store of useful files, and structure them better (to
segregate domain-generic code from application-specific code). You may
find you need to modify some system functions; so you may have a
different system load-file to select your modified ones.
Some people set up complex sets of flags and conditional compilation
statements to manage this, but I don't like that approach. Much easier
to have a master file for each app (or variant) and use the right one.
Hope this helps.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com
"Forth-based products and Services for real-time
applications since 1973."
==================================================