§lm9
§rm73
§jf
§hd"ETB","DEVELOPERS NOTES","ETB"
§ft"Version 1.00","Last Change: 30 April 1995","#"
PROGRAM STATISTICS
==================


                   lines   pages                 
etb.c               618     10
etb.h               106      2
main.c               23      1
mcstd.h              31      1
mcstring.c          214      4
parsecli.c          129      2
                   ----     --
                   1148     20
                   ====     ==



STUNTED GROWTH
==============


This document was originally going to be quite a substantial effort. Since then, I have had a change of heart, and decided to be a bit minimalist. I do have a fair bit of paperwork of interest to programers - most notably, the function descriptions. As it is, I decided to cut all the effort involved in producing a file for it. Let me know if you are interested in more developer documentation, and I will consider writing some of it up.



DESIGN PHILOSOPHY
=================


This software has been written with PORTABILITY and HIERARCHICAL INTEGRITY in mind. 'Portability' means that the code can easily be transported from one machine to another, and from compiler to compiler. The best standard to attain is whereby the code successfully compiles on all machines, using every ANSI-compliant compiler - and it is the SAME code. In other words- there shouldn't be one program for a Unix machine, a different one for a Lattice compiler, and yet another one for the Dice compiler. Write the code once- and it will work everywhere. How can this be achieved? I think the answer lies in using the Makefile and compiler definitions properly. As an example, look at the file 'etb.h'. It contains two structures- etb_line and trans_line. You will notice that 'struct' is preceded by the word 'FAR'. Ordinarily, the program wont compile under Dice. The structures are too big, and interfere with relative addressing calls. The fix in DICE is to declare the structs as __far. This generates absolute addressing code under a large data model. __far is, as far as I know (and I don't know much, because I am new to C), unique to DICE. So, if I had written the lines as
        __far struct etb_line
and 
        __far struct trans_line,
then everything would work fine under DICE, but hopelessly under any other compiler. Notice my design solution to the problem. I define FAR as __far if _DCC is defined (which is an automatic definition under DICE- it is never necessary to define it in the code), but leave it blank if _DCC isn't defined. In this way, the code compiles fine under DICE and is still theoretically okay for every other compiler. I say 'theoretically', of course, because other compilers might encounter the same problem, and require further tinkering to get it to work. Obviously I can't be expected to know how every compiler will behave, but I can eliminate potential sources of confusion and incompatibility, and make life as generally nice as possible.

This brings me onto HIERARCHICAL INTEGRITY. This is term I just made up to refer to the desire that an enhancement should make the program work better after it has been modified, rather than worse. The features in the program should work in a hierarchical fashion. At it's base, we have it's ability to be executed through a shell. Later on, we may want to add a Graphical User Interface. This is a more refined feature. If we add it to the program, we shouldn't just ditch the Command Line Interface stuff. Shell processing is important, and should be maintained. Adding GUI code to the program should be done in a harmonious way, without assuming that the old stuff is crap (unless it really is crap, of course).

The importance of constructing features in a pyramidal manner was impressed upon me when I wrote a program to allocate files to floppy disks. The program had a sexy Graphical User Interface- a real wonder of visual excitement. However, there were costs to this GUI. The first is that it had no CLI processing ability- all the programming effort was invested in the GUI. This is great for novice users, who like to do everything with the mouse. It is not so great for the power user, who might want to take a batch-oriented approach. The floppy disk program might be only one step in a chain of events (although admittedly this is unlikely given the nature of the program). Secondly, OVER HALF of the program line count was dedicated to GUI activities. The 'clever bit' of the program, i.e. the bit that actually worked out which files should be allocated to which floppies, didn't take much effort. I could have saved myself a lot of time by ignoring the graphics, and just coded for the CLI. Later on, if I thought the effort justified it, I could have added the fancy stuff. As a developer-user, I would have settled for the CLI. From a programmer's viewpoint, the less features I put in, the less work I have to do. From a user's viewpoint, I want the programmer to put in loads of features to make my life  as easy as possible. Taking the two together, I try to balance the utility of adding a feature with the sweat expended in producing it. If there is a moral in all this, it is this: keep it simple, functional and flexible, and build up later if needed.

So far, I have not discussed consistency of platform features. In other words, should the features provided by the program on one machine be identical to that on the other machine? Presumably, the answer should be 'Yes'. But let me suggest that the answer is, in fact, 'No'. Adding a GUI for an Amiga is a lot easier than adding a GUI for the Sun system- for the simple fact that I know a lot more about the Amiga than I do about the Sun. So it would be unrealistic for me to attempt a GUI for the Sun, but within the realms of possibility to attempt it for the Amiga. So it is likely that the Amiga version will have more features than the Sun. That's life! Notice how, that by adopting a hierarchical approach, everything has a solid base to work from. It still works on Unix, just not as well. What is more, features might not be consistent between compilers, even on the same platform. For instance, adding Workbench startup code is easier for the DICE compiler relative to Lattice. Now I could code everything using only Operating System routines- so it would be quid pro quo as regards which compiler is the best to write for. But I would prefer to use some of the refinements that DICE provides. So I'd probably take the easy option, and let the Lattice bods add in some of their own code if they thought it necessary.




COMPILER IDIOSYNCRASIES
=======================


This section not intended to be a user manual for the owners of the various compilers. It exists to provide notes on the vagaries of individual compilers to aid the porting of code.

DICE
----

This is an excellent compiler for the Amiga (thank you Matt Dillon). It has nearly always only been available in Public Domain (I think) as a shareware program. Recently, it has been available as a commercial release. There are a fair number of other Public Domain C compilers for the Amiga, but none match the standards of DICE. On the commercial front, people can choose from Lattice, SAS, or Manx (I am probably wrong here- SAS might be the same as Lattice). Lattice seems the more popular one. I see that developers refer to both Lattice and Manx in their distributions. Does this mean that they own BOTH compilers? Surely their drive for comparability can't outweigh the cost of buying two packages?

I heard recently that Lattice is withdrawing support for it's users. I haven't seen an advert for the Lattice compiler for some time, but I have seen a price of about £200 (pounds sterling) in the latest advertisement mentioning C compilers- although I am not sure if it was for Lattice or not. The point is this: most C compilers for the Amiga retail at about £200, most compilers do now not exist, DICE retails as about £100, and DICE has been made popular through the PD scene. I can therefore see that DICE will be the dominant compiler for the Amiga.

Not that this discussion is doing any good at all...

Environment definitions
-----------------------

Amongst other definitions, DICE has the following definitions in it's preprocessor:
        _DCC    : signifies that the compiler is DICE
        AMIGA   : signifies that the computer is an Amiga.

These definitions are created automatically. It is therefore easy to embed DICE- or Amiga- specific code in a program. The following example shows how this might be done:

        #ifdef _DCC
            ... DICE specific code ...
        #else
            ... generic, maybe not so good, code ...
        #endif


Large data model
----------------

DICE uses __far to force variable declarations to be addressed using absolute addressing. Useful (in fact necessary in my program) when lots of variable space is needed.

DMakefile
---------

This is DICE's name for a Makefile. I have only been using makefiles for a short time now (that's how new to C I am!)- so it is difficult to compare DMakfiles with Makefiles. Treat them as being identical, with DICE's facilities at least as powerful as any other.

Compiler commands
-----------------

DICE's compiler is called 'dcc'. Here I list the compiler commands that I used in my DMakefile (DICE has, of course, many, many, other commands)-
        -proto       force prototype checking. You can forget 
                     this for porting purposes.
        -E filename  sends any error messages to filename. Port 
                     this one however you like.

wbmain()
--------

DICE has a novel way of coping with startup from Amiga Workbench. If you port to a non-Amiga environment, then you can ignore this function, as it is part of the GUI. Don't cut it out of your code, though- remember, I want my program to be as uniform as possible ("one program works everywhere").

DICE executes wbmain(), instead of main(), if the program is started from Workbench. Bear this in mind when writing Amiga-specific stuff. Also, people using Lattice, say, with a GUI need to mess about with WB messages, and reply to them. DICE works differently, and a reply to a workbench message will cause problems. The moral is clear for people developing GUI's for the Amiga: the graphics facilities are identical regardless of which Amiga compiler is used, EXCEPT for the workbench startup- so separate the code between DICE and non-DICE.

Note: this subsection is being written before I have finished writing the actual program (the time is now 5.30am on a Saturday morning). Although the program is on the verge of completion, I don't know if I'll chuck in some (unspectacular, and extremely basic) GUI stuff at the last moment. You'll have to actually look at the program to find out.

Library bases
-------------

DICE saves you the fiddle of declaring Library base structures, and doing all the OpenLibrary() and CloseLibrary() stuff. It's all taken care of automatically. Hurrah! This comment is directed at the Amiga GUI writer. In the 'ROM Kernel Reference Manual: Libraries', pages 217 and 218 give examples of using EasyRequest(). But, where did I put my 'structLibrary*IntuitionBase', and the OpenLibrary() and CloseLibrary()? Not there! Surely a mistake? Nope- everything is fine. Lattice people can go write their own routines for this if they want. I don't want to take two bottles into the shower- I just want to Wash and Go- and now I can, with new improved DICE.

I hope that this is genuinely useful stuff. I like the idea that people will actually be developing my code, rather than leaving me to do it all myself, or express no interest in my program at all.