home *** CD-ROM | disk | FTP | other *** search
- Xref: sparky comp.os.msdos.programmer:10652 news.answers:4001
- Newsgroups: comp.os.msdos.programmer,news.answers
- Path: sparky!uunet!zaphod.mps.ohio-state.edu!magnus.acs.ohio-state.edu!usenet.ins.cwru.edu!ncoast!brown
- From: brown@NCoast.ORG (Stan Brown)
- Subject: comp.os.msdos.programmer FAQ part 2 of 4
- Expires: Wed, 30 Dec 1992 17:49:54 GMT
- Organization: Oak Road Systems, Cleveland Ohio USA
- Date: Sun, 15 Nov 1992 17:49:54 GMT
- Approved: news-answers-request@MIT.Edu
- Message-ID: <msdos-faq.921115.2@NCoast.ORG>
- Followup-To: comp.os.msdos.programmer
- References: <msdos-faq.921115.1@NCoast.ORG>
- Supersedes: <msdos-faq.921020.2@NCoast.ORG>
- Lines: 558
-
- Archive-name: msdos-programmer-faq/part2
- Last-modified: 15 November 1922
-
- (continued from part 1) (no warranty on the code or information)
-
- If the posting date is more than six weeks in the past, see instructions
- at the end of this article for how to get an updated copy.
-
- Copyright (C) 1992 Stan Brown, Oak Road Systems
-
-
- section 1. General questions
- ============================
-
- Q101. Why won't my code work?
-
- First you need to try to determine whether the problem is in your
- use of the programming language or in your use of MSDOS and your PC
- hardware. (Your manual should tell you which features are standard
- and which are vendor- or MSDOS- or PC-specific. You _have_ read
- your manual carefully, haven't you?)
-
- If the feature that seems to be working wrong is something related
- to your PC hardware or to the internals of MS-DOS, this group is the
- right place to ask. (Please check this list first, to make sure
- your question isn't already answered.)
-
- On the other hand, if your problem is with the programming language,
- the comp.lang hierarchy (including comp.lang.pascal and comp.lang c)
- is probably a better resource. Please read the other group's FAQ
- list thoroughly before posting. (These exist in comp.lang.c,
- comp.lang.c++, comp.lang.modula3, comp.lang.lisp, comp.lang.perl;
- they may exist in other groups as well.) It's almost never a good
- idea to crosspost between this group and a language group.
-
- Before posting in either place, try to make your program as small as
- possible while still exhibiting the bad behavior. Sometimes this
- alone is enough to show you where the trouble is. Also edit your
- description of the problem to be as short as possible. This makes
- it look more like you tried to solve the problem on your own, and
- makes people more inclined to try to help you.
-
- When you do post a question, it's good manners to say "email please;
- I'll post a summary." Then everybody else in the group doesn't have
- to read ten virtually identical responses. Of course, then you have
- to follow through. A summary is not simply pasting together all the
- email you received. Instead, write your own (brief) description of
- the solution: this is the best way to make sure you really
- understand it. Definitely don't repost people's cute signatures.
-
- Q102. What is this newsgroup about?
-
- comp.os.msdos.programmer (comp.sys.ibm.pc.programmer until September
- 1990) concerns programming for MS-DOS systems. The article "USENET
- Readership report for Oct 92" in news.lists shows 40,000 readers of
- this newsgroup worldwide.
-
- Much of our traffic is about language products (chiefly from Borland
- and Microsoft). More programming topics focus on C than on any one
- other language.
-
- Since most MS-DOS systems run on hardware that is roughly compatible
- with the IBM PC, on Intel 8088, 80188, or 80x86 chips, we tend to
- get a lot of questions and answers about programming other parts of
- the hardware.
-
- Q103. What's the difference from comp.sys.ibm.pc.programmer?
-
- c.s.i.p.programmer is the old name of comp.os.msdos.programmer, and
- has been obsolete since September 1990. However, many systems have
- not removed the old group, or have removed it but aliased it to the
- new name. This means that some people still think they're posting
- to c.s.i.p.programmer even though they're actually posting to
- c.o.m.programmer.
-
- You can easily verify the non-existence of c.s.i.p.programmer by
- reference to the "List of Active Newsgroups" posted to news.groups.
- It's available as /pub/usenet/news.answers/active-newsgroups/part1
- from the archives (see "Where are FAQ lists archived?" in section C,
- "More information").
-
- Q104. What other newsgroups should I know about?
-
- Your best bet is to read the periodic information postings in the
- comp.binaries.ibm.pc newsgroup. Specially helpful articles:
- Using the comp.binaries.ibm.pc.d groups
- Beginner's guide to binaries
- Starter kit
- About archives and archivers
- Please wait for these articles to come around; don't post a request.
-
- Also check out news.announce.newusers, even if you're not a new
- user. You may be surprised how much useful information is in the
- monthly postings there. Lots of old-timers also get useful stuff
- from news.newusers.questions, especially the periodic postings.
-
- Remember that it's good manners to subscribe to any newsgroup and
- read it for a while before you post a question. When you post, it's
- also good manners to ask for replies to be emailed and then to post
- a summary, which you've edited down to the absolute minimum size.
-
- You may also be interested in the following newsgroups. Caution:
- Some of them have specialized charters; you'll probably get (and
- deserve) some flames if you post to an inappropriate group.
-
- - misc.forsale.computers and misc.forsale.computers.pc-clone are
- where you post notices of equipment, software, or computer books
- that you want to sell. Please don't post or crosspost those
- notices here.
-
- - comp.os.ms-windows.programmer.tools and ...misc (formerly part of
- comp.windows.ms.programmer): Similar to this group, but focus
- on programming for the MS-Windows platform.
-
- - comp.sys.ibm.pc.hardware is for more hardware-oriented discussions
- of the machines that run DOS.
-
- - comp.binaries.ibm.pc.wanted: AFTER you have looked in the other
- groups, this is the place to post a request for a particular
- binary program.
-
- - comp.binaries.ibm.pc.archives used to explain how to use the
- archive sites, especially garbo and Simtel; but the archive
- managers have stopped posting notices of uploads. There is a vote
- (through 7 December 1992) on creating a moderated group,
- comp.archives.msdos.announce, to replace it. In the meantime, you
- can subscribe to upload announcements by sending mail containing
- the text line "subscribe msdos-ann" and no signature to
- listserv@tacom-emh1.army.mil.
-
- - comp.binaries.ibm.pc.d is for discussions about programs posted in
- comp.binaries.ibm.pc, and only those programs. This is a good
- place to report bugs in the programs, but not to ask where to find
- them (see cbip.wanted, above). cbip.d is NOT supposed to be a
- general PC discussion group.
-
- - comp.sources.misc: a moderated group for source code for many
- computer systems. It tends to get lots of Unix stuff, but you may
- also pick up some DOS-compatible code here.
-
- - alt.sources: an unmoderated group for source code. Guidelines are
- posted periodically.
-
-
- section 2. Compile and link
- ===========================
-
- Q201. What the heck is "DGROUP > 64K"?
-
- DGROUP is a link-time group of data segments, and the compiler
- typically generates code that expects DS to be pointing to DGROUP.
- (Exception: Borland's huge model has no DGROUP.)
-
- Here's what goes into DGROUP:
-
- - tiny model (all pointers near): DGROUP holds the entire program.
-
- - small and medium models (data pointers near): DGROUP holds all
- globals and static variables including string literals, plus the
- stack and the heap.
-
- - large, compact, and huge models in Microsoft (data pointers far):
- DGROUP holds only initialized globals and static variables
- including string literals, plus the stack and the near heap.
-
- - large and compact models in Borland (data pointers far): DGROUP
- holds initialized and uninitialized globals and static variables
- including string literals, but not the stack or heap.
-
- - huge model in Borland (data pointers far): there is no DGROUP, so
- the 64K limit doesn't apply.
-
- In all of the above, which is to say all six models in Microsoft C
- and all but huge in Borland C, DGROUP is limited to 64K including
- string literals (which are treated as static data). This limitation
- is due to the Intel CPU's segmented architecture.
-
- See the next Q for possible remedies.
-
- For more information, see topics like "memory models" and "memory
- management" in the index of your compiler manual. Also see
- TI738.ASC in PD1:<MSDOS.TURBO-C>BCHELP10.ZIP at Simtel for an
- extended general discussion of memory usage in Borland C programs,
- of which much applies to any C compiler in DOS.
-
- Q202. How do I fix "automatic data segment exceeds 64K" or "stack plus
- data exceed 64K"?
-
- These messages are a variation of "DGROUP > 64K". For causes,
- please see the preceding Q.
-
- If you get this error in tiny model, your program is simply too big
- and you must use a different memory model. If you get this link
- error in models S, C, M, L, or Microsoft's H, there are some things
- you can do. (This error can't occur in Borland's huge model.)
-
- If you have one or two big global arrays, simply declare them far.
- The compiler takes this to mean that any references to them will use
- 32-bit pointers, so they'll be in separate segments and no longer
- part of DGROUP.
-
- Or you can use the /Gt[number] option with Microsoft or -Ff[=size]
- with Borland C++ 2.0 and up. This will automatically put variables
- above a certain size into their own segments outside of DGROUP.
-
- Yet another option is to change global arrays to far pointers. Then
- at the beginning of your program, allocate them from the far heap
- (_fmalloc in Microsoft, farmalloc in Borland).
-
- Finally, you can change to huge model (with Borland compilers, not
- Microsoft). Borland's H model still uses far pointers by default,
- but "sets aside the [64K] limit" and has no DGROUP group, according
- to the BC++ 2.0 Programmer's Guide. Microsoft's H model does use
- huge data pointers by default but retains DGROUP and its 64K limit,
- so switching to the H model doesn't buy you anything if you have
- DGROUP problems.
-
- Q203. Will Borland C code and Microsoft C code link together?
-
- Typically this question is asked by someone who owns compiler A and
- is trying to write code to link with a third-party library that was
- compiled under compiler B.
-
- The answer to the question is, Not in general. Here are some of the
- reasons:
-
- - "Helper" functions (undocumented functions for stack checking,
- floating-point arithmetic, and operations on longs) differ between
- the two compilers.
-
- - The compilers may embed instructions in the object code that tell
- the linker to look for their own run-time libraries.
-
- Those problems will generate link-time errors. Others may not show
- up until run time:
-
- - Borland's compact, large, and huge models don't assume DS=SS, but
- Microsoft's do. The -Fs option on the Borland compiler, or one of
- the /A options on Microsoft, should take care of this problem --
- once you know that's what's going on.
-
- - Check conventions for ordering and packing structure members, and
- for alignment of various types on byte, word, paragraph, or other
- boundaries. Again, you can generally adjust your code to match if
- you know what conventions were used in compiling the "foreign"
- libraries.
-
- - Check the obvious and make sure that your code was compiled under
- the same memory model as the code you're trying to link with.
- (That's necessary, but no guarantee. Microsoft and Borland don't
- use exactly the same conventions for segments and groups,
- particularly in the larger memory models.)
-
- That said, there are some circumstances where you can link hybrids.
- Your best chance of success comes if you avoid longs and floating
- point, use only 16-bit pointers, suppress stack checking, and
- specify all libraries used in the link.
-
- Q204. Why did my program bomb at run time with "floating point formats
- not linked"?
-
- First, is that the actual message, or did it say "floating point not
- loaded"? If it was the latter, see the next Q.
-
- You're probably using a Borland compiler for C or C++ (including
- Turbo C and Turbo C++). Borland's compilers try to be smart and not
- link in the floating-point (f-p) library unless you need it. Alas,
- they all get the decision wrong. One common case is where you don't
- call any f-p functions, but you have %f or other f-p formats in
- scanf/printf calls. The cure is to call an f-p function, or at
- least force one to be present in the link.
-
- To do that, define this function somewhere in a source file but
- don't call it:
-
- static void forcefloat(float *p)
- { float f = *p; forcefloat(&f); }
-
- It doesn't have to be in the module with the main program, as long
- as it's in a module that will be included in the link.
-
- A new solution for Borland C++ 3.0 was posted, but I don't own the
- product and have not been able to verify it. Insert these
- statements in your program:
-
- extern unsigned _floatconvert;
- #pragma extref _floatconvert
-
- Q205. Why did my program bomb with "floating point not loaded"?
-
- That is Microsoft C's run-time message when the code requires a
- numeric coprocessor but your computer doesn't have one installed.
-
- If the program is yours, relink it using the xLIBCE or xLIBCA
- library (where x is the memory model).
-
- Q206. How can I change the stack size in Borland's C compilers?
-
- In Turbo C, Turbo C++, and Borland C++, you may not find "stack
- size" in the index but the global variable _stklen should be there.
- The manual will instruct you to put a statement like
-
- extern unsigned _stklen = 54321U;
-
- in your code, outside of any function. You must assign the value
- right in the extern statement; it won't work to assign a value at
- run time. (The "extern" in this context isn't ANSI C and ought not
- to be required, but the above statement is a direct quote from the
- Library Reference manual of Borland C++ 2.0.) The linker may give
- you a duplicate symbol warning, which you can ignore.
-
- Q207. What's the difference between .COM and .EXE formats?
-
- To oversimplify: a .COM file is a direct image of core, and an .EXE
- file will undergo some further relocation when it is run (and so it
- begins with a relocation header). A .COM file is limited to 64K for
- all segments combined, but an .EXE file can have as many segments as
- your linker will handle and be as large as RAM can take.
-
- Among the books that detail formats of executable files are {DOS
- Programmer's Reference: 2d Edition} by Terry Dettman and Jim Kyle,
- ISBN 0-88022-458-4; and {Microsoft MS-DOS Programmer's Reference},
- ISBN 1-55615-329-5. Ralf Brown's interrupt list also documents the
- .EXE header (including extensions by Borland's TLINK) at INT 21
- function 4B, including New Executable and Linear Executable formats.
-
-
- section 3. Keyboard
- ===================
-
- Q301. How can I read a character without echoing it to the screen, and
- without waiting for the user to press the Enter key?
-
- The C compilers from Microsoft and Borland offer getch (or getche to
- echo the character); Turbo Pascal has ReadKey.
-
- In other programming languages, load 8 in register AH and execute
- INT 21; AL is returned with the character from standard input
- (possibly redirected). If you don't want to allow redirection, or
- you want to capture Ctrl-C and other special keys, use INT 16 with
- AH=10; this will return the scan code in AH and ASCII code (if
- possible) in AL, except that AL=E0 with AH nonzero indicates one of
- the grey "extended" keys was pressed. (If your BIOS doesn't
- support the extended keyboard, use INT 16 function 0 not 10.)
-
- Q302. How can I find out whether a character has been typed, without
- waiting for one?
-
- In Turbo Pascal, use KeyPressed. Both Microsoft C and Turbo C offer
- the kbhit( ) function. All of these tell you whether a key has been
- pressed. If no key has been pressed, they return that information
- to your program. If a keystroke is waiting, they tell your program
- that but leave the key in the input buffer.
-
- You can use the BIOS call, INT 16 function 01 or 11, to check
- whether an actual keystroke is waiting; or the DOS call, INT 21
- function 0B, to check for a keystroke from stdin (subject to
- redirection). See Ralf Brown's interrupt list.
-
- Q303. How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?
-
- You can download the file PD1:<MSDOS.KEYBOARD>CADEL.ZIP from Simtel.
- It contains a TSR to disable those keys, with source code in ASM.
-
- To disable only Ctrl-Alt-Del (actually, to change the boot keys to
- leftShift-Alt-Del), use DEBOOT.COM. Along with KEYKILL.COM, which
- lets you disable up to three keys of your choice, it is at Simtel in
- the file PD1:<MSDOS.KEYBOARD>KEYKILL.ARC.
-
- C programmers who simply want to make sure that the user can't
- Ctrl-Break out of their program can use the ANSI-standard signal( )
- function; the Borland compilers also offer ctrlbrk( ) for handling
- Ctrl-Break. However, if your program uses normal DOS input, the
- characters ^C will appear on the screen when the user presses Ctrl-C
- or Ctrl-Break. There are many ways to work around that, including:
- use INT 21 function 7, which allows redirection but doesn't display
- the ^C (or echo any other character, for that matter); or use INT 16
- function 0 or 10; or call _bios_keybrd( ) in MSC or bioskey( ) in
- BC++; or hook INT 9 to discard Ctrl-C and Ctrl-Break before the
- regular BIOS keyboard handler sees them; etc., etc.
-
- You should be aware that Ctrl-C and Ctrl-Break are processed quite
- differently internally. Ctrl-Break, like all keystrokes, is
- processed by the BIOS code at INT 9 as soon as the user presses the
- keys, even if earlier keys are still in the keyboard buffer: by
- default the handler at INT 1B is called. Ctrl-C is not special to
- the BIOS, nor is it special to DOS functions 6 and 7; it _is_
- special to DOS functions 1 and 8 when at the head of the keyboard
- buffer. You will need to make sure BREAK is OFF to prevent DOS
- polling the keyboard for Ctrl-C during non-keyboard operations.
-
- Some good general references are {Advanced MS-DOS} by Ray Duncan,
- ISBN 1-55615-157-8; {8088 Assembler Language Programming: The IBM
- PC}, ISBN 0-672-22024-5, by Willen & Krantz; and {COMPUTE!'s Mapping
- the IBM PC}, ISBN 0-942386-92-2.
-
- Q304. How can I disable the print screen function?
-
- There are really two print screen functions: 1) print current
- screen snapshot, triggered by PrintScreen or Shift-PrtSc or
- Shift-grey*, and 2) turn on continuous screen echo, started and
- stopped by Ctrl-P or Ctrl-PrtSc.
-
- 1) Screen snapshot to printer
- --------------------------
-
- The BIOS uses INT 5 for this. Fortunately, you don't need to mess
- with that interrupt handler. The standard handler, in BIOSes dated
- December 1982 or later, uses a byte at 0040:0100 (alias 0000:0500)
- to determine whether a print screen is currently in progress. If it
- is, pressing PrintScreen again is ignored. So to disable the screen
- snapshot, all you have to do is write a 1 to that byte. When the
- user presses PrintScreen, the BIOS will think that a print screen is
- already in progress and will ignore the user's keypress. You can
- re-enable PrintScreen by zeroing the same byte.
-
- Here's some simple code:
-
- void prtsc_allow(int allow) /* 0=disable, nonzero=enable */ {
- unsigned char far* flag = (unsigned char far*)0x00400100UL;
- *flag = (unsigned char)!allow;
- }
-
- 2) Continuous echo of screen to printer
- ------------------------------------
-
- If ANSI.SYS is loaded, you can easily disable the continuous echo of
- screen to printer (Ctrl-P or Ctrl-PrtSc). Just redefine the keys by
- "printing" strings like these to the screen (BASIC print, C printf,
- Pascal Write statements, or ECHO command in batch files):
-
- <27>[0;114;"Ctrl-PrtSc disabled"p
- <27>[16;"^P"p
-
- Change <27> in the above to an Escape character, ASCII 27.
-
- If you haven't installed ANSI.SYS, I can't offer an easy way to
- disable the echo-screen-to-printer function. Please send any tested
- solutions to brown@ncoast.org and I'll add them to this list.
-
- Actually, you might not need to disable Ctrl-P and Ctrl-PrtSc. If
- your only concern is not locking up your machine, when you see the
- "Abort, Retry, Ignore, Fail" prompt just press Ctrl-P again and then
- I. As an alternative, install one of the many print spoolers that
- intercept printer-status queries and always return "Printer ready".
-
- Q305. How can my program turn NumLock (CapsLock, ScrollLock) on or off?
-
- You need to twiddle bit 5, 6, or 4 of location 0040:0017. Here's
- some code: lck( ) turns on a lock state, and unlck( ) turns it off.
- (The status lights on some keyboards may not reflect the change. If
- yours is one, call INT 16 function 2, "get shift status", and that
- may update them. It will certainly do no harm.)
-
- #define NUM_LOCK (1 << 5)
- #define CAPS_LOCK (1 << 6)
- #define SCRL_LOCK (1 << 4)
- void lck(int shiftype) {
- char far* kbdstatus = (char far*)0x00400017UL;
- *kbdstatus |= (char)shiftype;
- }
- void unlck(int shiftype) {
- char far* kbdstatus = (char far*)0x00400017UL;
- *kbdstatus &= ~(char)shiftype;
- }
-
- Q306. How can I speed up the keyboard's auto-repeat?
-
- The keyboard speed has two components: delay (before a key that you
- hold down starts repeating) and typematic rate (the speed once the
- key starts repeating). Most BIOSes since 1986 let software change
- the delay and typematic rate by calling INT 16 function 3, "set
- typematic rate and delay"; see Ralf Brown's interrupt list. If you
- have DOS 4.0 or later, you can use the MODE CON command that you'll
- find in your DOS manual.
-
- On 83-key keyboards (mostly XTs), the delay and typematic rate can't
- easily be changed. According to the {PC Magazine} of 15 Jan 1991,
- page 409, to adjust the typematic rate you need "a memory-resident
- program which simply '[watches]' the keyboard to see if you're
- holding down a key ... and after a certain time [starts] stuffing
- extra copies of the held-down key into the buffer." No source code
- is given in that issue; but I'm told that the QUICKEYS utility that
- {PC} published in 1986 does this sort of watching; you can download
- source and object code in PD1:<MSDOS.PCMAG>VOL5N05.ARC from Simtel.
-
- Q307. What is the SysRq key for?
-
- There is no standard use for the key. The BIOS keyboard routines in
- INT 16 simply ignore it; therefore so do the DOS input routines in
- INT 21 as well as the keyboard routines in libraries supplied with
- high-level languages.
-
- When you press or release a key, the keyboard triggers hardware line
- IRQ1, and the CPU calls INT 9. INT 9 reads the scan code from the
- keyboard and the shift states from the BIOS data area.
-
- What happens next depends on whether your PC's BIOS supports an
- enhanced keyboard (101 or 102 keys). If so, INT 9 calls INT 15
- function 4F to translate the scan code. If the translated scan code
- is 54 hex (for the SysRq key) then INT 9 calls INT 15 function 85
- and doesn't put the keystroke into the keyboard buffer. The default
- handler of that function does nothing and simply returns. (If your
- PC has an older BIOS that doesn't support the extended keyboards,
- INT 15 function 4F is not called. Early ATs have 84-key keyboards,
- so their BIOS calls INT 15 function 85 but nor 4F.)
-
- Thus your program is free to use SysRq for its own purposes, but at
- the cost of some programming. You could hook INT 9, but it's
- probably easier to hook INT 15 function 85, which is called when
- SysRq is pressed or released.
-
- Q308. How can my program tell what kind of keyboard is on the system?
-
- Ralf Brown's Interrupt List includes MEMORY.LST, a detailed
- breakdown by Robin Walker of the contents of the BIOS system block
- that starts at 0040:0000. Bit 4 of byte 0040:0096 is "1=enhanced
- keyboard installed". C code to test the keyboard type:
- char far *kbd_stat_byte3 = (char far *)0x00400096UL;
- if (0x10 & *kbd_stat_byte3)
- /* 101- or 102-key keyboard is installed */
-
- {PC Magazine}'s 15 Jan 1991 issue suggests on page 412 that "for
- some clones [the above test] is not foolproof". If you use this
- method in your program you should provide the user some way to
- override this test, or at least some way to tell your program to
- assume a non-enhanced keyboard. The {PC Magazine} article suggests
- a different approach to determining the type of keyboard.
-
- Q309. How can I tell if input, output, or stderr has been redirected?
-
- Normally, input and output are associated with the console (i.e.,
- with the keyboard and the screen, respectively). If either is not,
- you know that it has been redirected. Some source code to check
- this is available at the usual archive sites.
-
- If you program in Turbo Pascal, download the /pc/ts/tspa*.zip
- collection of Turbo Pascal units from garbo; or from Simtel,
- PD1:<MSDOS.TURBOPAS>TSPA*.ZIP. (Choose TSPA3060.ZIP, TSPA3055.ZIP,
- TSPA3050.ZIP, or TSPA3040.ZIP for Turbo Pascal 6.0, 5.5, 5.0, or 4.0
- respectively.) Source code is not included. Also see the
- information in garbo.uwasa.fi:/pc/ts/tsfaq*.zip Frequently Asked
- Questions, the Turbo Pascal section.
-
- If you program in C, use isatty( ) if your implementation has it.
- Otherwise, you can download PD1:<MSDOS.SYSUTL>IS_CON10.ZIP from
- Simtel; it includes source code.
-
- Good references for the principles are {PC Magazine} 16 Apr 1991
- (vol 10 nr 7) pg 374; Ray Duncan's {Advanced MS-DOS}, ISBN
- 1-55615-157-8, or Ralf Brown's interrupt list for INT 21 function
- 4400; and Terry Dettman and Jim Kyle's {DOS Programmer's Reference:
- 2d edition}, ISBN 0-88022-458-4, pp 602-603.
-
- (continued in part 3)
- --
- Stan Brown, Oak Road Systems brown@Ncoast.ORG
- Cleveland, Ohio, USA
-