Website hosting…

I have been doing website hosting as a hobby since the mid-1990s. I just set up a “business card” site for a local cafe (Douglas Cafe in Urbandale) we frequent. We’ve also done projects for them including making new menus, table top signs, window vinyl lettering, and street signs. To help boost their new website, I just wanted to post it here for the search engines to find:

https://www.douglascafe.com

While I do not actively persue website hosting anymore, I still have about 75 sites hosted here. My web hosting account is going up 25% my next renewal, so I may very well have to re-activate this as a business that takes money.

More to come…

A life changing letter…

In 1995, I sent this cover letter out with my resume. I managed to get the job, and that forever changed the direction of my career…


Allen C. Huffman
110 Champions Dr. #XXX
Lufkin, TX 75901

Microware Systems Corp.
1900 N.W. 114th St.
Des Moines, IA 50325-7077
Attn: Human Resources

May 7th, 1995

Dear Sir;

I am writing in regards to your Technical Training Engineer position. After learning of it’s availability I immediately wanted to express my interest. I possess a working knowledge of OS-9 which comes from daily use over the past six years and I believe this would be beneficial to your company.

I have programmed under OS-9 Level Two and OS-9/68K with several commercially marketed utilities and applications available. My creations include a sound driver, machine language space game, menu driven user interface library, and various file and printer utilities. Since 1990 I have owned and operated a company which creates and markets OS-9 products. I regularly attend annual conventions as a vendor and also give seminars dealing with OS-9 support and programming.

I have an active interest in Microware’s past, present and future and attempt to follow media coverage of developments such as the use of DAVID in set-top converters and OS-9 in places like Treasure Island in Las Vegas.

I am eager to provide further information about myself and my accomplishments either through an interview or additional correspondence. Feel free to contact me by mail, by telephone at (409) 637-XXXX, or by the internet at “coco-sysop@genie.geis.com”. Thank you for your consideration and I look forward to hearing from you.

Sincerely,

Allen C. Huffman


Almost exactly one month later, I received this e-mail:


INET00# Document Id: UX012.BUX0687704
Item 7490898 95/06/05 04:15
From: XXX@MICROWARE.COM@INET00# Internet Gateway
To: COCO-SYSOP Allen C. Huffman
Sub: Technical Training Engineer

Dear Allen,

I would like to discuss the technical training position Microware has open
with you on the telephone. Please call me at Microware, (515) 224-1929 at
your convenience, or email me a time I can reach you.

Sincerely,

XXX
Manager, Technical Training

=END=


It was (and still is) pretty amazing to me that a kid (well, early 20s) who had mostly worked retail was given a shot like this. And all because I went with a CoCo instead of a Commodore 64… Though, who knows, maybe I would have ended up working for Commodore in that universe…

Until next time…

Old C dog, new C tricks part 5: inline prototypes?

See Also: part 1, part 2, part 3, part 4 and part 5.

This post is a departure from what most of the others are like. I am most certainly not going to be using this “trick” I just learned.

Background

Recently in my day job, I was doing a code review and came across something that was most certainly not legal C code. In fact, I was confident that line wouldn’t even compile without issuing a warning. Yet, the developer said he did not see a warning about it.

The bit of code was supposed to be calling a function that returns a populated structure. Consider this silly example:

#include <stdio.h>

// typedefs
typedef struct {
    unsigned int major;
    unsigned int minor;
    unsigned int patch;
} VersionStruct;

// prototypes
VersionStruct GetVersion (void);

// main
int main()
{
    VersionStruct foo;
    
    foo = GetVersion ();
    
    printf ("Version %u.%u.%u\n", foo.major, foo.minor, foo.patch);

    return 0;
}

// functions
VersionStruct GetVersion ()
{
    VersionStruct ver;
    
    ver.major = 1;
    ver.minor = 0;
    ver.patch = 42;
    
    return ver;
}

But in the code, the call to the function was incomplete. There was no return variable, and even had “void” inside the parens. It looked something like this:

int main()
{
    VersionStruct GetVersion (void);

    return 0;
}

I took one look at that and said “no way that’s working.” But there had been no compiler warning.

So off I went to the Online GDB Compiler to type up a quick example.

And it built without warning.

Well, maybe the default is to ignore this warning… So I added “-Wall” and “-Wextra” to the build flags. That should catch it :)

And it built without warning.

“How can this work? It looks like a prototype in the middle of a function!” I asked.

Yes, Virginia. You can have inline prototypes.

A brief bit of searching told me that, yes, inline prototypes were a thing.

This should give a compiler warning:

#include <stdio.h>

int main()
{
function ();

return 0;
}

void function (void)
{
printf ("Inside function.\n");
}

When I built that, I received two compiler warnings:

main.c: At top level:
main.c:10:6: warning: conflicting types for ‘function’; have ‘void(void)’
10 | void function (void)
| ^~~~~~~~
main.c:5:5: note: previous implicit declaration of ‘function’ with type ‘void(void)’
5 | function ();
| ^~~~~~~~

The first warning is not about the missing prototype, but about “conflicting types”. In C, a function without a prototype is assumed to be a function that returns an int.

Had I made function like this…

int function (void)
{
    printf ("Inside function.\n");
    return 0;
}

…I’d see only one, but different, warning:

main.c: In function ‘main’:
main.c:5:5: warning: implicit declaration of function ‘function’ [-Wimplicit-function-declaration]
5 | function ();
| ^~~~~~~~

For the first example, the compiler makes an assumption about what this function should be, then finds code using it the wrong way. It warns me that I am not using it like the implied prototype says it should be used. Sorta.

For the next, my function matches the implied prototype, so those warnings go away, but a real “implicit declaration” warning is given.

Going back to the original “void” code, I can add an inline prototype in main() to make these all go away:

#include <stdio.h>

int main()
{
    void function(void); // Inline prototype?
    
    function ();

    return 0;
}

void function (void)
{
    printf ("Inside function.\n");
}

I had no idea that was allowed.

I have no idea why one would do that. BUT, I suppose if you wanted to get to one function without an include file with a prototype for it, you could just stick that right before you call the function…

But Why would you want to do that?

I learned this is possible. I do not think I want to ever do this. Am I missing some great benefit for being able to have a prototype inside a function like this? Is there some “clean code” recommendation that might actually say this is useful?

“It wouldn’t be in there if it didn’t have a reason.”

Let me know what you know in the comments. Until next time…

My early 90s CoCo room.

Forty years ago, this is what my room looked like… UPDATED with photos!


Allen's room (so far):

+----------------------------------------------------------------------------+
- | VCR,Jag,Etc.| | | CM8/VCR | | TV | || | C= Mon.| ||
.\ |_____________| |_|_________|_|________|_||_|________|_______________||
. \ | _________ ______ ___ ||____ ____________ ....... ||
. \ | | .CoCo3. | MPI ||Dsk||| HD | Amiga500 | mouse ||
- |_|_________|______||___|||____|____________|________||
|------| CHAIR CHAIR |_____| |
| Boxes| |Term.| |
| | _|_____|_|
|______| | Ans. ||
|Shel| ______ Floor Space! | |---| ||
|-ves| |file| | |DMP| ||
| | |thng| |_|___|_||
| | |____| -----------------------------------------------------
|____| | | "
| | My Double Bed | "
| /\ | | "
| \K\ |___________________________________________________-
|# \4\ | Term.| | Lamp/ |
| # \/ | | | Radio |
+----|.......|---------------------------------------------------------------+

Door into room on top left, closet on bottom left, window on bottom right.
Going clockwise:

Starting at top, metal "erector set" shelving containing misc. junk on top
shelf, two VCR drawer cabinets and Jag on second, SVHS and editing gizmos on
third, fourth has complete Sega Genesis/CD setup, and below is Sub-Etha
Software stuff (paperwork, software, etc.)

First computer desk is CoCo system with CM8 on VCR, and TV to the right. A
set of medium sized powerer stereo speakers sits on the CM8 and another on the
Amiga monitor for great stereo seperation. Dual power strips below this, too.
(One with modem line and CoCo on it, the other is on "all the time" for VCRs
(clock) and TV).

Amiga desk is metal frame (not wood like CoCo) and has Amiga setup and
monitor, and TONS of books on shelves below right of desk. A pull out sliding
"table" at the right has a WYSE terminal on it w/CoCo cube stored below it.

Next is a printer table (came with the desk) with the Friday and printer and
power strip.

Bed.

Table with clock, radio, and lamp.

Another table with hardware terminal (so I can hack while in bed, via serial
port on CoCo).

K4 keyboard setup with amp/speaker, sequencer, Midi disk drive, etc.

Bookshelf with magazines and misc. junk, and filing cabinet (on wheels, lid
opens from top).

Storage bins (used to lug CoCo stuff to 'Fests) stacked three high, full of
junk.

And this, my friends, is my room. <whew>

Allen

And here is me, in all my nerdy glory.

And this is what the other wall looks like, after returning home from a CoCoFest.

Don’t panic! The room didn’t always look like that…

Until next time…

How to crash a CoCo 3 – more accurately.

Recently, I shared this way to crash a CoCo 3 in three easy steps:

  1. Turn it on.
  2. Type in “CLEAR 16500:WIDTH 40”
  3. There is no step three.

I expected to do a follow-up once I had time to look at the Super Extended Color BASIC Unraveled book and try to figure out what was causing this crash. I also planned to figure out what value triggered the first crash. I discovered this in a program that did a “CLEAR 17000” and I just went up and down trying to find a threshold where it crashed, and gave up at 16500.

But I am lazy.

And sometimes dense. It didn’t dawn on me that I might have been able to have the computer try to figure out when it crashed. But it did to Juan Castro. In the comments, Juan wrote:

First thing I thought was to increase the value of CLEAR in a loop to see exactly when it crashes… oops, you can’t, you nuked your loop counter with the CLEAR.

No problem, let’s use fixed memory for the counter. &H400, the start of the (now unused) text screen. We’ll start with 16000 (=&H3E80) and increment by one.

10 WIDTH 40
20 POKE &H400,&H3E:POKE &H401,&H80
30 GOSUB 80
40 AD=AD+1
50 GOSUB 90
60 PRINT AD:CLEAR AD
70 GOTO 30
80 AD=PEEK(&H400)*256+PEEK(&H401):RETURN
90 HI=INT(AD/256)
100 LO=AD-256
HI
110 POKE &H400,HI:POKE &H401,LO
120 RETURN

It locks up at 16356 — suspiciously close to 16384. Only 28 bytes off. That’s probably close to how much the stack occupies. (Modulo some buffer headers, variable descriptors, and maybe some non-fatal stack corruption.)

– Juan Castro

Brilliant!

And I bet once we dig in (I believe William Astle has done this work in the past) we will find that location is where an 8K MMU block gets mapped in/out for manipulating the high res text screens. Or some other words to that affect that I do not know how to properly articulate.

More to come on this…

Could the CoCo run a tiny Large Language Model (AI) thing?

An interesting post appeared on the Color Computer Facebook group the other day… Youngstown Ken posted:

AI on the CoCo? This is a simple language experiment that I think can be done on the coco if anyone would like to try the code. I asked Grok to write a basic program to read a 10MB corpus of sentences from a dictionary and then build a simple Markov graph db to predict the next word in a sequence.

– Youngstown Ken, via Facebook CoCo group

The attached link went to a Grok-generated conversation about the subject. You can read the short interaction here, and see the generated Python and BASIC-ish code:

https://x.com/i/grok/share/sXRGgHcIROsl7PPNXnF8D34MC?fbclid=IwY2xjawI15sRleHRuA2FlbQIxMQABHZVbpT6JU-bIDIQJoVd4KxBf4gJFb2oSJmYX9t9Y3sIShpw0nK6nmP8laA_aem_Q4MMZzgAmLsauqsrU5ID6g

So naturally, someone needs to try this and see if it would even work. AI is terrible at BASIC, though I did have some success talking it through some simple 32-column text screen game code when ChatGPT first made a big public splash a few years ago.

Let’s look at the second code variation it produced:

10 REM Two-Level Markov Chain Predictor for TRS-80 CoCo
20 REM Vocabulary: 1000 words, Trigram model fits in 512KB

30 DIM V$(999): REM Vocabulary array (1000 words)
40 DIM F(999): REM Frequency array for vocab building
50 DIM M(499,3): REM Model: 500 trigrams max (w1,w2,w3,prob)
60 N=0: REM Number of trigrams loaded

70 REM Pass 1: Build vocabulary from corpus.txt
80 OPEN "I",#1,"CORPUS.TXT"
90 FOR I=0 TO 999: F(I)=0: NEXT I: REM Initialize frequency
100 IF EOF(1) THEN 140
110 LINE INPUT #1,L$: REM Read line
120 GOSUB 500: REM Process line for word frequencies
130 GOTO 100
140 CLOSE #1

150 REM Sort top 1000 words by frequency (simplified)
160 FOR I=0 TO 999: V$(I)="<UNK>": NEXT I: REM Default to <UNK>
170 W$="":C=0: REM Temp word and count
180 OPEN "I",#1,"CORPUS.TXT" : REM Reopen for word assignment
190 IF EOF(1) THEN 230
200 LINE INPUT #1,L$
210 GOSUB 600: REM Assign top 1000 words
220 GOTO 190
230 CLOSE #1

240 REM Pass 2: Build trigram model
250 OPEN "I",#1,"CORPUS.TXT"
260 IF EOF(1) THEN 300
270 LINE INPUT #1,L$
280 GOSUB 700: REM Build trigrams
290 GOTO 260
300 CLOSE #1

310 REM Input sequence and predict
320 PRINT "Enter two-word sequence (e.g., 'the quick'):"
330 INPUT S$
340 GOSUB 800: REM Extract last two words and predict
350 PRINT "Next word:";W3$
360 GOTO 320: REM Loop for more predictions

500 REM Subroutine: Count word frequencies
510 W$="":P=1
520 IF P>LEN(L$) THEN RETURN
530 C$=MID$(L$,P,1)
540 IF C$=" " OR P=LEN(L$) THEN GOSUB 550: GOTO 570
550 IF W$<>"" THEN FOR I=0 TO 999: IF V$(I)=W$ THEN F(I)=F(I)+1: GOTO 560: NEXT I
560 W$="":P=P+1:GOTO 520
570 W$=W$+C$:P=P+1:GOTO 520

600 REM Subroutine: Assign top 1000 words (simplified)
610 W$="":P=1
620 IF P>LEN(L$) THEN RETURN
630 C$=MID$(L$,P,1)
640 IF C$=" " OR P=LEN(L$) THEN GOSUB 650: GOTO 670
650 IF W$<>"" THEN FOR I=0 TO 999: IF V$(I)="" AND F(I)>C THEN V$(I)=W$:C=F(I): GOTO 660: NEXT I
660 W$="":P=P+1:GOTO 620
670 W$=W$+C$:P=P+1:GOTO 620

700 REM Subroutine: Build trigrams
710 W1$="":W2$="":W3$="":P=1:W=0
720 IF P>LEN(L$) THEN RETURN
730 C$=MID$(L$,P,1)
740 IF C$=" " OR P=LEN(L$) THEN GOSUB 750: GOTO 770
750 IF W$<>"" THEN W=W+1:IF W=1 THEN W1$=W$ ELSE IF W=2 THEN W2$=W$ ELSE IF W=3 THEN W3$=W$:GOSUB 900
760 W$="":P=P+1:GOTO 720
770 W$=W$+C$:P=P+1:GOTO 720

900 REM Subroutine: Add trigram to model
910 IF N>=500 THEN RETURN: REM Limit to 500 trigrams
920 FOR I=0 TO 999: IF V$(I)=W1$ THEN W1=I:GOTO 930: NEXT I:W1=999
930 FOR I=0 TO 999: IF V$(I)=W2$ THEN W2=I:GOTO 940: NEXT I:W2=999
940 FOR I=0 TO 999: IF V$(I)=W3$ THEN W3=I:GOTO 950: NEXT I:W3=999
950 FOR I=0 TO N-1: IF M(I,0)=W1 AND M(I,1)=W2 THEN M(I,3)=M(I,3)+1:RETURN
960 M(N,0)=W1:M(N,1)=W2:M(N,2)=W3:M(N,3)=1:N=N+1
970 RETURN

800 REM Subroutine: Predict next word
810 W1$="":W2$="":P=1
820 IF P>LEN(S$) THEN GOTO 850
830 C$=MID$(S$,P,1)
840 IF C$=" " THEN W1$=W2$:W2$="":P=P+1:GOTO 820 ELSE W2$=W2$+C$:P=P+1:GOTO 820
850 IF W1$="" THEN W1$=W2$:W2$="": REM Handle single word
860 FOR I=0 TO 999: IF V$(I)=W1$ THEN W1=I:GOTO 870: NEXT I:W1=999
870 FOR I=0 TO 999: IF V$(I)=W2$ THEN W2=I:GOTO 880: NEXT I:W2=999
880 W3$="<UNK>":P=0
890 FOR I=0 TO N-1: IF M(I,0)=W1 AND M(I,1)=W2 AND M(I,3)>P THEN W3$=V$(M(I,2)):P=M(I,3)
900 NEXT I
910 RETURN

This is surprising to me, since it does appear to be honoring the 2-character limit of Color BASIC variables, which is something my early interactions with ChatGPT would not do.

The original prompt told it to do something that could run on a CoCo within 512K. Even Super Extended Color BASIC on a 512K CoCo 3 still only gives you 24K of program space on startup. And, as I recently learned, if you try to use a CoCo 3’s 40 or 80 column screen and clear more than about 16K of string space, you get a crash.

I think I may give this a go. I do not understand how any of this works, so I do not expect good results, BUT I do want to at least try to see if I can make the program functional. If it expects to use 512K of RAM for string storage, this program is doomed to fail. But, 24K would be room for 1000 24-byte words, at least.

To be continued … maybe.

In the meantime, check out that Grok link and see what you can come up with. And let me know in the comments.

Old C dog, new C tricks part 4: no more passing buffers?

In the previous installment, I rambled on about the difference of “char *line;” and “char line[];”. The first is a “pointer to char(s)” and the second is “an array of char(s)”. But, when you pass them into a function, they both are treated as a pointer to those chars.

One “benefit” of using “line[]” was that you could use sizeof(line) on it and get the byte count of the array. This is faster than using strlen().

But if you pass it into a function, all you have is a pointer so strlen() is what you have to use.

While you can’t pass an “array of char” into a function as an array of char, you can pass a structure that contains an “array of char” and sizeof() will work on that:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h>
typedef struct
{
    char buffer[80];
} MyStruct;
void function (MyStruct test)
{
    printf ("sizeof(test.buffer) = %zu\n", sizeof(test.buffer));
}
int main(void)
{
    MyStruct test;
    
    strncpy (test.buffer, "This is a test", sizeof(test.buffer));
    
    function (test);
    return EXIT_SUCCESS;
}

You may notice that was passing a copy of the structure in, but stay with me for a moment.

If you have a function that is supposed to copy data into a buffer:

#define VERSION_STRING "1.0.42b-alpha"
void GetVersion (char *buffer)
{
    if (NULL != buffer)
    {
        strcpy (buffer, VERSION_STRING);
    }
}

…you can easily have a buffer overrun problem if the function writes more data than is available in the caller’s buffer. Because of this potential problem, I add a size parameter to such functions:

void GetVersion (char *buffer, size_t bufferSize)
{
    if (NULL != buffer)
    {
        // Copy up to bufferSize bytes.
        strncpy (buffer, VERSION_STRING, bufferSize);
    }
}

As long as the caller passes the correct parameters, this is safe:

char buffer[20];
GetVersion (buffer, 20);

But the caller could still screw up:

char buffer[20];
GetVersion (buffer, 200); // oops, one too many zeros

But if you use a structure, it is impossible for the caller to mess it up (though, of course, they could mess up the structure on their side before calling your function). The compiler type checking will flag if the wrong data type is passed in. The “buffer” will always be the “buffer.” No chance of a “bad pointer” or “buffer overrun” crashing the program.

To allow the buffer inside the structure to be modified, pass it in by reference:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h>
#define VERSION_STRING "1.0.42b-alpha"
typedef struct
{
    char buffer[80];
} MyStruct;
void GetVersion (MyStruct *test)
{
    strncpy (test->buffer, VERSION_STRING, sizeof(test->buffer));
}
int main(void)
{
    MyStruct test;
    
    GetVersion (&test);
    
    printf ("Version: %s\n", test.buffer);
    return EXIT_SUCCESS;
}

Using this approach, you can safely pass a “buffer” into functions and they can get the sizeof() the buffer to ensure they do not overwrite anything.

But wait, there’s more…

It is pretty easy for a function to get out of control if you are trying to get back more than one thing. If you just want an “int”, that’s easy…

int GetCounter ()
{
    static int s_count = 0;
    return s_count++;
}

But if you wanted to get the major, minor, patch and build version, you end up passing in ints by reference to get something like this:

void GetVersion (int *major, int *minor, int *patch, int *build)
{
   if (NULL != major)
   {
      *major = MAJOR_VERSION;
   }
   if (NULL != minor)
   {
      *major = MINOR_VERSION;
   }
   if (NULL != patch)
   {
      *major = PATCH_VERSION;
   }
   if (NULL != build)
   {
      *major = BUILD_VERSION;
   }
}

Of course, anytime pointers are involved, the caller could pass in the wrong pointer and things could get screwed up. Plus, look at all those NULL checks to make sure the pointer isn’t 0. (This does not help if the pointer is pointing to some random location in memory.)

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#define MAJOR_VERSION 1
#define MINOR_VERSION 0
#define PATCH_VERSION 0
#define BUILD_VERSION 42
typedef struct
{
    int major;
    int minor;
    int patch;
    int build;
} VersionStruct;
VersionStruct GetVersion ()
{
    VersionStruct ver;
    
    ver.major = MAJOR_VERSION;
    ver.minor = MINOR_VERSION;
    ver.patch = PATCH_VERSION;
    ver.build = BUILD_VERSION;
    
    return ver;
}
int main(void)
{
    VersionStruct ver;
    
    ver = GetVersion ();
    
    printf ("Version: %u.%u.%u.%u\n",
        ver.major, ver.minor, ver.patch, ver.build);
    return EXIT_SUCCESS;
}

If you are concerned about overhead of passing structures, you can pass them by reference (pointer) and the compiler should still catch if a wrong pointer type is passed in:

#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#define MAJOR_VERSION 1
#define MINOR_VERSION 0
#define PATCH_VERSION 0
#define BUILD_VERSION 42
typedef struct
{
    int major;
    int minor;
    int patch;
    int build;
} VersionStruct;
void GetVersion (VersionStruct *ver)
{
    if (NULL != ver)
    {
        ver->major = MAJOR_VERSION;
        ver->minor = MINOR_VERSION;
        ver->patch = PATCH_VERSION;
        ver->build = BUILD_VERSION;
    }
}
int main(void)
{
    VersionStruct ver;
    
    GetVersion (&ver);
    
    printf ("Version: %u.%u.%u.%u\n",
        ver.major, ver.minor, ver.patch, ver.build);
    return EXIT_SUCCESS;
}

However, when dealing with pointers, there is always some risk. While the compiler will catch passing in the wrong structure pointer, there are still ways the caller can screw it up. For instance, void pointers:

int main(void)
{
    void *nothing = (void*)0x1234;
    
    GetVersion (nothing);
    return EXIT_SUCCESS;
}

Yep. Crash.

...Program finished with exit code 139
Press ENTER to exit console.

Give someone access to a function in your DLL and they might find a way to crash the program as simply as using a void pointer.

It is a bit trickier when you pass the full structure:

typedef struct
{
    int x;
} BogusStruct;
int main(void)
{
    BogusStruct ver;
    
    ver = GetVersion ();
    
    return EXIT_SUCCESS;
}

Compiler don’t like:

main.c: In function ‘main’:
main.c:38:11: error: incompatible types when assigning to type ‘BogusStruct’ from type ‘VersionStruct’
38 | ver = GetVersion ();
| ^~~~~~~~~~
main.c:36:17: warning: variable ‘ver’ set but not used [-Wunused-but-set-variable]
36 | BogusStruct ver;
| ^~~

And you can’t really cast a return value like this:

int main(void)
{
BogusStruct ver;

(VersionStruct)ver = GetVersion ();

return EXIT_SUCCESS;
}

Compiler don’t like:

main.c: In function ‘main’:
main.c:38:5: error: conversion to non-scalar type requested
38 | (VersionStruct)ver = GetVersion ();
| ^

Though maybe you could cast it if it was passed in as a parameter:

void ShowVersion (VersionStruct ver)
{
    printf ("Version: %u.%u.%u.%u\n",
    ver.major, ver.minor, ver.patch, ver.build);
}
int main(void)
{
    BogusStruct ver;
    
    ShowVersion ((VersionStruct)ver);
    
    return EXIT_SUCCESS;
}

Compiler still don’t like:

main.c: In function ‘main’:
main.c:44:5: error: conversion to non-scalar type requested
44 | ShowVersion ((VersionStruct)ver);
| ^~~~~~~~~~~

Hmm. Is there a way to screw this up? Let me know in the comments.

Until then…

Sub-Etha Net… BBS protocol proposal.

Waybackwhen, I wrote this proposal for a very simple BBS networking protocol. There were several in existence, but the specs were always complicated. I wanted something someone could implement without a Phd ;-)


The following is a proposal for a new CoCo BBS message networking protocol created by Allen Huffman at Sub-Etha Software. This protocol was designed to easily allow the CoCo community to link BBSs together for mass communication within this network.

Background:

Several successful BBS networking protocols are already in existence such as FidoNet and WWIVNet. Although very powerful, these systems require large amounts of disk space to operate and have so far been limited only to systems with hard drives.

Introducing the Sub-Etha Net:

The proposed “Sub-Etha Net”, henceforth known as SENet, will be a low-hardware CoCo specific network designed for the sole purpose of linking the online CoCo community together with one massive public message base.

At this time, no private messages between systems will be supported. This will greatly limit the amount of overhead required by each system. Current protocols have required each system within the net to store a “net list” containing information on ALL systems within the network. SENet will not require such a list and no “hub” systems are needed.

How it Will Work:

In order to link with the network, the Sysop will simply insert the phone number of the system it wishes to net with and configure a few settings which control when and what it will transfer. The system you plan to net with will also add your number to its listing. Once this has been done, no further configuration will be required. A simple message pointer flag stored for each system is all that is required to keep track of messages.

An Example: System A will call System B and transfer messages.

At the specified time, System A dials a number stored in it’s net list.

Upon connection, System A waits for a prompt (such as “Press Return:”) then sends an escape code and it’s phone number (in the form ###-###-####) to System B.

System B will look up that number in it’s net list and either ACKnowledge or request the number to be resent (in case of line noise).

If several unsuccessful attempts are made, System B will disconnect. (If this was caused by line noise, this forces System A to redial and try for a better connection.)

After a successful connection, System A sends a four byte number stored in it’s net list which instructs System B to send all messages it has from that number on.

System B then sends messages (with appropriate error checking).

System A will ACKnowledge the transfer (or request data to be resent).

Depending on System B’s configuration, System B will either send a final ACKnowledgement and disconnect, OR it will send it’s four byte number for a message request.

System A then sends messages (with appropriate error checking).

System B will ACKnowledge the transfer (or request data to be resent).

System A will send a final ACKnowledgemend and disconnect.

Message base structure:

To keep things a simple as possible, the message base will contain only the following information…

  • From: Name of poster (30 characters)
  • Subj: Message subject (40 characters)
  • Date: Date/time posted (6 character packet)

This allows the message “header” to fit in one 80 column line of information. Any other information such as “To:” or “Reply to:” will be contained in the actual message text portion of the file. This allows excellent flexibility for future expansion.


I wonder what else I will find in my archives. I still think something like this was a good idea — low overhead, simple to implement even in BASIC (though requiring an assembly language remote terminal driver), and “just enough to get the basic stuff done.”

Maybe one day we’ll do something like that using a CoCo and a $10 WiFi Modem for it ;-)

Until then…

DeltaBoard: my theme-changing BBS – part 1

It seems I started running a BBS in Lufkin, Texas around 1989 or so. It ran under OS-9 which allowed me to still be programming on my computer and such while a caller was online. Fun times.

Delta, meaning change, was significant here. The BBS would change themes! (This idea came from, I believe, an MC-10 buddy of mine I knew in Houston. Hello, Paul, wherever you are!)

I recently found a few of the themes and thought I’d share them here… Welcome screen, bad password screen, login screen, and goodbye screen.

DeltaBoard (native theme)

    ____    ____       _____
| \ | | | /\
| ) |--- | | / \ Online Since December, 1991!
|___/ |____ |____ | /____\ Originally Online in 1989!

/) The DeltaBoard (\
3/12/2400 Baud - MNP5 - 8/N/1

Co-SysOps: Axel and OSIRIS

"Online Home for Sub-Etha Software!"

To request a password, press [Return] at the UserName prompt.

Special Login Accounts (type at the UserName prompt):

Special \\ Guest - Forget your password?
Login >> OldDelta - To login to the ORIGINAL DeltaBoard circa 1990.
Account // PKZip - To download the latest version of PKZip (2.04G)

---

The password you have entered is invalid.

Please check your password then try your call again.

If you need assistance, or have forgotten your password, you may call back
and logon as "guest" then leave feedback to the SysOp and tell him what you
want for a new password that you can remember...

---

Access granted to Allen's Half-Meg 6809 Based Multi-Tasking Timesharing System.
___ ___ ___ ___ ___
/ / /__ _ /__/ The DeltaBoard - Lufkin, Texas \___ _\__ \ _
/__/ ___/ ___/ Area (409) Hub for StG Net International __\ \__ \__\

---

[Y)es - End call.\
[N)o - Return to main menu.\
[F)eedback - Leave mail to SysOp.\

---

Goodbye, Farewell, Etc...
____ ____ _____
| \ | | | /\
| ) |--- | | / \
|___/ |____ |____ | /____\

/) The DeltaBoard (\
3/12/2400 Baud - 8/N/1

Thanks for calling - please call again.

...and check out:

DataWare - 637-4973
Didinium - 637-7115
EasTex - 637-7728
Junker's - 637-7134
Plus BBS - 637-PLUS
Qwiff - 632-5442

Castle Delta

         Welcome to Castle...
____ ____ _____
| \ | | | /\
| ) |--- | | / \
|___/ |____ |____ | /____\

King: Allen Huffman of Nothingham
Surf: Sire Axel of Oile
Chief Peon: Osiris

"It has been a long fortnight for you, I see," says the Greeter. "Well, come
inside and chat with the King, or have a tankard of our finest ale. All is
well here!" he finishes.

To request a password, press [Return] at the UserName prompt.

Special \\ Guest - Forget your password?
Login >> OldDelta - To login to the ORIGINAL DeltaBoard circa 1990.
Account // PKZip - To download the latest version of PKZip (2.04G)

---

"Halt!" screams the greeter, suddenly not beeing so greetful. "I do not
recognize you! Are you the imposter we have heard of in the village? Please
call back and knock again when you can remember who you are lest the guards
throw you in the dungeon! If you still need help, you may pretend to be
"GUEST" and the castle helper will help you...

The doors shut.


Click.
---

The old wooden doors creak open slowly. Inside, you can see that there
is a ball in progress with ladies dressed in their finest and gentlemen
laughing boisterously. A sense of excitment fills the room as the ladies
pause to glance at you and the gentlemen rush to shake your hand.

"Welcome back! So glad to see that the battles have left you none the
worse for wear!" someone says.

King Allen himself is here, and is waiting to speak with you about your
journeys. But first, you must meet with your beloved mate.

"Tell me about it," your mate requests. "Tell me everything...!"

Of course, your mate cannot hear you over the party so you must write
everything down. For your convenience, Castle Delta has a list of options for
you to use. Enjoy all we have - all one needs to do is ask, and it shall be
given. Enjoy your stay!

---

The time has come for you to depart. Beware the many pitfalls in your path\
and remember: when you need a break from the world, come to Castle Delta.\
We will always welcome you as our most important guest. If you would like\
to leave word with King Allen about your stay, feel free to do so...\
\
[Y)es - Leave the castle.\
[N)o - I forgot something. I gotta go back and get it!\
[F)eedback - Leave the King mail. {Not chain mail, though...}\
[O)ptional - Talk with, or leave a message to, a peon...\

---

As you exit, the castle door slams shut, leaving you to wonder just what
lies ahead in this dark, sinister world. But, you take solice in the fact
that you may return at your next opportunity. After all...your mate is here
behind the walls of Castle Delta!


(Castle Delta script/ideas by Osiris)


Click.

Delta Hotel

             Welcome to...
____ ____ _____
| \ | | | /\
| ) |--- | | / \
|___/ |____ |____ | /____\

H O T E L

Manager: Allen Huffman
Clerk: Axel
Bellhop: Hopsing

[.. VACANCY]

To request a password, press [Return] at the UserName prompt.

Special \\ Guest - Forget your password?
Login >> OldDelta - To login to the ORIGINAL DeltaBoard circa 1990.
Account // PKZip - To download the latest version of PKZip (2.04G)

"Please sign the register..." the clerk requests.

---

"Are you sure this is your credit card?" the clerk asks.

"Please come back again when you have proper identification and sign in
{and make it legible next time, buddy!} or, if you have never stayed with us
before or cannot remember if you have, register as a "GUEST" and someone from
management will be happy to help you with your luggage...

The clerk dissappears into the back room. No amount of bell ringing will
bring him back so you decide to leave...


Click.

---

"Welcome to 'zee Hotel" the clerk says. "Hopsing will take your baggage,
not to worry. In the main lobby you will find a comprehensive directory of
our services. Since you are a Valued Guest, all services are free of charge!
You also have a listing in your room, if you are weary. The hotel manager is
at your service. Press the 'C' button at the elevator and you will belifted
to his suite! If he is not there, just leave him a message. He will have
Hopsing hand deliver it to your door" finished the clerk. {whew!}

"It is encouraged that you talk with other guests and staff so that we
all may be to know each other better," starts the clerk again, "therefore
making your visit more enjoyable! There are many activities to choose from,
and the conversations run from docile to very lively! If you ever need me,
just press the 'H' button at any elevator and I will assist you in any way.
Thank you for checking into Delta Hotel..." stops the clerk.

---

We hope you have enjoyed your stay at Delta Hotel. If you care to, leave\
comments to the manager!\
\
[Y)es - I gotta run! I have to catch a plane and BOY are my arms gonna\
be tired!\
[N)o - I left something in the room! I'll be back shortly...\
[F)eedback - Leave word with the hotel manager.\
[O)ptional - Express sympathy to Hopsing {he never gets mail}...\

---

Thank you for sleeping with us! Tell your friends! Tell your
neighbors! Tell your Uncle Fred in Idaho! The Delta Hotel is open 24 hours
a day to serve you, so feel free to drop by any time. Hopsing has loaded your
baggage and you are all set to go...

{And bring back those two towels in your suitcase when you return, eh?}


(Hotel Delta script/ideas by Osiris)


Click.

From the way these are numbered, there was at least one more theme but the files were not with the others (odd, that).

Meanwhile, in Iowa…

In 1995, I moved to Iowa. I put my BBS online here, with a set of new themes:

  • DeltaBoard – “Online in Iowa since last week!”
  • Star Dreck – The Lost Generation
  • Castle Delta – with a new look
  • Hotel Delta
  • Haunted Mansion – Disney, eh?
  • Phone # – just showed the phone number, and seemed to have snarky menus
  • Al’s Den of Iniquity – “Beware: No Furniture”

I had forgotten about most of these. I’ll have to share those sometime. And a few more I have also found (or at least partially found).

Until then…

How to crash a CoCo 3

From a fresh boot…

CLEAR 16500
WIDTH 40

I have been going through all my OS-9 hard drive images trying to get everything compiled into one place. During this project I ran across a folder called DECB (Disk Extended Color BASIC). It contained the following files:

colours.BAS
contents.txt
DEL.BAK
del.BAS
microwar.txt
new.BAS
reader
copy.asc
reader.asc
reader.BAS
subs.BAS
TEST.DEL
TEST.TXT

I had no recollection of what this was, nor why filenames were in lowercase. That was not normally how files were done from Disk BASIC on a CoCo due to the 32-column screen showing lowercase as inverted video.

The “microwar.txt” was an e-mail response to an inquiry I made to Microware in 1993 about the history of OS-9:

INTERNET# Document Id: UX00f.BUX0039573

Item 6352278 93/08/24 06:03

From: STEVES@MICROWARE.COM@INTERNET# Internet Gateway II

To: COCO-SYSOP Allen C. Huffman

Sub: Re: The History of OS-9

From mcrware!microware.com!steves@uunet.UU.NET Tue Aug 24 17:24:36 1993
Received: from relay1.UU.NET by relay2.geis.com with SMTP
(1.37.109.4
/15.6) id AA23578; Tue, 24 Aug 93 17:24:36 +0100
Received: from spool.uu.net (via LOCALHOST) by relay1.UU.NET with SMTP
(5T.61/UUNET-internet-primary) id AA11760; Tue, 24 Aug 93 12:24:35 -0400
Received: from mcrware.UUCP by uucp2.uu.net with UUCP/RMAIL
(queuweing-rmail) id 122237.10246; Tue, 24 Aug 1993 12:22:37 EDT
Received: from yme by microware.com with SMTP id AA00775
(5.67a8/IDA-1.5 for <coco-sysop@genie.geis.com>); Tue, 24 Aug 1993 10:03:08
-0500
From: Steve Simpson <steves@microware.com>
Received: by yme id <AA00684@yme>; Tue, 24 Aug 93 10:03:04 CDT
Date: Tue, 24 Aug 93 10:03:04 CDT
Message-Id: <9308241503.AA00684@yme>
To: coco-sysop@genie.geis.com
Subject: Re: The History of OS-9

The “Steve” there was Steve “Homer” Simpson, who I would later know when I went to work for Microware two years later. He provide me with a Microware history article, and a Ken Kaplan interview about Microware’s first 15 years. (Note to self: Get this archived somewhere.)

The “TEST.TXT” file was a 300 line text file, obviously created by a program, that was just this:

This is a file of 300 lines or so, and this is line number * 0*...
This is a file of 300 lines or so, and this is line number * 1*...
This is a file of 300 lines or so, and this is line number * 2*...
...snip...
This is a file of 300 lines or so, and this is line number * 298*...
This is a file of 300 lines or so, and this is line number * 299*...
This is a file of 300 lines or so, and this is line number * 300*...

I was a bit confused at the purpose of these two files. Fortunately, “contents.txt” cleared things up a bit.

Jan  '95  #01
Demonstration Issue
1. All About This Program
Allen C. Huffman
INFO

2. REALLY *BIG* FILE
Bob's Big Boy
TEST

3. Microware Information
Microware Staff
MICROWAR

4. Stuff For Sale
Various
FORSALE

5. Useless File
Bob
USELESS

6. Nothing
Dan
NOTHING

7. Something
Bob & Dan
SOMETHIN

8. Movie Reviews
Sis Kehl
MOVIES

9. Spam Recipes
Mr. Cook
SPAMSPAM

F. Another File
Another Writer
ANOTHER

G. Nothin'
Nobody
NOTHING

H. Nothin' Again
Nobody Again
NOTHING

I. More About Flies
Spider-Man
FLYFILE

J. Jump Text
Jumpman Bob
JUMPTEXT

K. KeepThisSecret
Spy
SPYFILE

*. Use Star for This
StarMan
STAR

Seeing my name in there let me know that this was indeed something I was involved in. But what was it? I needed to move those BAS files over to an emulator and take a look.

Fortunately, one of them was already in ASCII – “reader.asc” – and it was indeed a BASIC program that apparently I wrote back in 1995.

0 REM *
1 REM * Text Viewing Program Thingy V1.00 by Allen C. Huffman
2 REM * Copyright (C) 1995 by Sub-Etha Software
3 REM * Written for Terry Simons and the MI&CC Upgrade Diskletter
4 REM *
5 PALETTE0,0:PALETTE8,63:WIDTH80:CLS1

I had zero recollection of writing this, but I remember the Mid-Iowa & Country CoCo Club (MI&CC) and its organizer, Terry Simons. Sub-Etha Software attended their Middle America Fest held in Des Moines in 1993. Somewhere, I am pretty sure I have photos from this even, but they are not in my online CoCoFest photo gallery for some reason.

I could tell this was something I wrote, since that line 5 of doing the background black, foreground white, width 80, CLS1 line was how I started all my CoCo 3 BASIC programs.

A quick scan of this program, which I will include in full, reveals it would read the “contents.txt” file and display a directory of text files (articles) on the disk and let the user select one. It would then load the file and let them read it, with options to go page to page through the file. A text file reader with a menu system – neat!

0 REM *
1 REM * Text Viewing Program Thingy V1.00 by Allen C. Huffman
2 REM * Copyright (C) 1995 by Sub-Etha Software
3 REM * Written for Terry Simons and the MI&CC Upgrade Diskletter
4 REM *
5 PALETTE0,0:PALETTE8,63:WIDTH80:CLS1
10 CLEAR17000:DIMFL$(15,3),A$(200)
50 REM * Read Index File
55 OPEN"I",#1,"contents.txt":LINEINPUT#1,IS$:LINEINPUT#1,SB$:FL=0
60 FORA=0TO3:LINEINPUT#1,FL$(FL,A):NEXT:IFEOF(1)=0THENFL=FL+1:IFFL<30THEN60
65 CLOSE
100 REM * Table of Contents
105 CLS:LOCATE28,0:PRINT"Mid Iowa & Country CoCo":LOCATE40-LEN(IS$)/2,1:PRINTIS$;:LOCATE0,3:ATTR0,7:PRINT:ATTR0,0:LOCATE0,22:ATTR0,7:PRINT:ATTR0,0
110 GOSUB1155:LOCATE35,2:PRINT"Main Menu";:GOSUB1130:LOCATE23,23:PRINT"Select File to View or [Q] to Quit";
115 FORA=0TOFL:LOCATE40*ABS(A>7)+5,(A AND7)*2+5:PRINTFL$(A,0):LOCATE40*ABS(A>7)+23,(A AND7)*2+6:PRINTFL$(A,1):NEXT
120 GOSUB1055:A=0:IFA$="Q"THEN450
125 IFA$=LEFT$(FL$(A,0),1)THENFL$=FL$(A,2):A$=FL$(A,0)+" by "+FL$(A,1):GOTO205ELSEA=A+1:IFA<16THEN125
130 SOUND100,1:GOTO120
200 REM * View a File (FL$)
205 LOCATE40-LEN(A$)/2,2:PRINTA$;:GOSUB1130:LOCATE2,23:PRINT"[UP] Next Page  [DN] Prev Page  [J]ump to Page  [P]rint File  [Q]uit to Menu";:LN=0:
210 GOSUB1105:ONERR GOTO245:OPEN"I",#1,FL$+".TXT":ONERR GOTO
215 LOCATE32,13:ATTR0,0,B:PRINT"Loading File ...":ATTR0,0:MX=0
220 IFEOF(1)=0THENLINEINPUT#1,A$:A$(MX)=LEFT$(A$,65):MX=MX+1:IFMX<200THEN220
225 CLOSE:LOCATE68,2:PRINT"Lines:";MX;:LN=0:LOCATE0,12:PRINT
230 LOCATE0,2:PRINT"Page"INT(LN/18)+1"of"INT(MX/18)+1"  ";:FORA=0TO17:LOCATE8,A+4:IFLN+A<MX THENPRINTA$(LN+A)ELSEPRINT
235 NEXT
240 GOSUB1055:A=INSTR(CHR$(94)+CHR$(10)+"JPQ",A$):IFA<1ORA>5THENSOUND100,1:GOTO240 ELSEONA GOTO255,305,355,405,430
245 FORA=12TO14:LOCATE15,A:PRINT"* File Not Found - Press Any Key for Main Menu *":NEXT:GOSUB1055:GOSUB1105:GOTO110
250 REM * [UP] Next Page
255 IFLN+18<MX THENLN=LN+18
260 GOTO230
300 REM * [DN] Prev Page
305 IFLN-18=>0THENLN=LN-18
310 GOTO230
350 REM * [J]ump to Page
355 LOCATE0,11:PRINT:PRINT:PRINT:LOCATE29,12:PRINT"Jump to Page 1 -"INT(MX/18)+1":";:GOSUB1005:A=VAL(A$)-1:IFA<0ORA>INT(MX/18)THENSOUND100,1:GOTO230ELSELN=A*18:GOTO230
360 IFPG>1THENPG=PG-1
365 GOTO230
400 REM * [P]rint Article
405 LOCATE0,11:PRINT:PRINT:PRINT:LOCATE13,12:PRINT"Ready Printer.  Press [ENTER] to Print or [Q] to Quit";
410 GOSUB1055:IFA$="Q"THEN230ELSEIFA$=CHR$(13)THEN415ELSESOUND100,1:GOTO410
415 REM --- if printer is not online, beep and go back to 201... <- check here
420 A=0
425 PRINT#-2,TAB(8)A$(A):A=A+1:IFA<MX THEN425ELSE230
430 GOTO110
450 REM * Quit
455 CLOSE:CLS:STOP
460 END
1000 REM * Line Input
1005 LINEINPUTA$:RETURN
1050 REM * Inkey$
1055 A$=INKEY$:IFA$=""THEN1055
1060 CH=ASC(A$):IFCH>95THENA$=CHR$(CH-32)
1065 RETURN
1100 REM * Clear Text Area
1105 FORA=4TO21:LOCATE0,A:PRINT:NEXT:RETURN
1125 REM * Clear Bottom Line
1130 LOCATE0,23:PRINTSTRING$(79,32);:RETURN
1150 REM * Clear Top Line (well, third one actually...)
1155 LOCATE0,2:PRINTSTRING$(79,32);:RETURN

I used the Toolshed (new GitHub home) decb command line tool to create a blank 32-track DSK image, and then then copy the “reader.BAS” file over to it. I also copied the “contents.txt” file, since that was needed.

I booted up the Xroar emulator, mounted my new disk image, and loaded the program. When I ran it, it went to a black screen momentarily, then I saw this:

I assumed the file might be corrupted, so I then tried to CLOAD the ASCII file in Xroar. That is a neat feature that lets you “Load” a text file, then when you type CLOAD it simulates loading an ASCII file form tape, using the input text file as that ASCII file.

Same problem.

I visually inspected the program. Maybe there was some EXEC to an assembly routine that needed to loaded before RUNing this BASIC.

Nothing.

Was Xroar defective? I went to the Xroar Online Emulator and tried it there, loading my DSK image through the web page.

Crash.

I then posted to the CoCo e-mail list sharing a Dropbox link to a folder containing these files, as well as the DSK image. I asked if anyone could test it and see if it crashed for them, as well. Michael Kline was the first to respond:

Alan,

I tested all BASIC programmes on a CC3. They all lock up.

Michael

This at least let me know it was not an Xroar-specific issue. But what was this issue?

I experimented with the TRON command to see how far the program would get before locking up. It would print some quick values [0][1][2][3][4][5] before the screen cleared and things crashed, but this time to a blank green screen. The output to the 32-column screen was changing behavior.

I commented out line 5 so it would stay on the 32 column screen, and ran it again. I saw it spew out line numbers, before clearing the screen and printing an “?HP ERROR IN 105”. That is the error you get when you do CoCo 3 40/80 column screen commands on the 32 column screen. From the looks of things, removing the WIDTH80 line allowed it to get to around line 60 or so before one of those commands was used and it errored out.

Why would WIDTH 80 crash a program? I did more experiments. After this error, with the system not locked up, I typed “5 WIDTH 40” just to add a screen width, but no PALLETEs or CLS command.

According to TRON, the program was crashing at WIDTH 40… Huh???

Each time a crash like this happened, the virtual CoCo had to be restarted (power cycle). Just a virtual reset would not work. You might get “OK” back but things were not working.

Very, very odd.

I finally got my program to run by changing the value of the CLEAR command in line 10. (Initially, I deleted the whole line after the WIDTH line just to see what happened, and narrowed it down to something to do with the CLEAR.)

I shared my results on the CoCo list, and William “Lost Wizard” Astle, one of the great minds at understanding how the BASIC ROMs operate, replied with this tidbit:

It won’t be a string space bug – handling that didn’t change in the Coco3.

Have you been running it from the 40 or 80 column screen? If so, that’s probably why. The driver for handling output to the 40/80 column screen doesn’t properly manage the memory map and if the stack ends up too low in memory, it gets swapped out while the 40/80 column screen is mapped in, leading to a system crash. String space and the address parameter to CLEAR are both above the stack.

This crash happens if the stack ends up below $4000 if memory serves.

If you aren’t running from the 40/80 column screen, it may be some other weirdness triggering it.

– William Astle, via the CoCo List

Light has been shedded, and I knew all I would have to do is look up the Super Color BASIC Unraveled book to find a clear and concise explanation of this bug. But, before I got there, William added more details:

The only solution as far as I can tell is do no output on the 40/80 column screen or clear less string space. Given that a lot of the numbers used for CLEAR for string space are effectively random with no real understanding of how big they need to be, it’s entirely possible that 17000 is way more than it needs to be. Something most programmers don’t know is that a string constant in the program that is never modified doesn’t use up any string space so often the CLEAR number is massively larger than it needs to be.

Having the WIDTH before the CLEAR should prevent WIDTH from crashing but any subsequent PRINT, LOCATE, INPUT (from keyboard), or HSTAT probably would crash.

– William Astle, via the CoCo List

My confusion continued, and finally a third response form him added this:

It’s not that straight forward and unravelled doesn’t mention it. I only know because I tripped on it hard back in “the day” and it left me scratching my head for quite a while.

Basically, the stack has to stay above $4000 (the screen gets mapped in the $2000 to $3FFF range) so that would probably put the absolute limit somewhere just under 16K (16384). You’d want 200 bytes or so room for the stack so 16000 would be a nice round number. On the other hand, if you reserve any memory with the second parameter for CLEAR, that number goes down.

Perversely, if you could get the stack to be *below* $2000, it would also not crash, but that means a very small program with no PMODE graphics, probably.

The size of scalar variables, arrays, or program text doesn’t actually matter for this calculation because the screen is not mapped in except when doing I/O on the screen.

– William Astle, via the CoCo List

A bug the Unraveled folks didn’t know about? William stumbled upon it back then, and if this code was a version I tried to run, I must have, as well. But, why would I have left it in this non-working version? I expect I may find a “fixed” version of this program in my Disk BASIC disk archives, eventually. For all I know, maybe I kept this one around as a “what is wrong with this program” example, copying it to my OS-9 hard drive so I could post it on a BBS or online service (or even to the original CoCo List back then).

Hmm, I wonder if the old Princeton CoCo list archives still exist. Maybe I posted about this 30 years ago (1995!) when I was writing this program? (Note to self: Look that up…)

But I digress.

If you CLEAR a big enough number, then do a WIDTH 40 or 80, the CoCo 3 crashes hard. I toyed with that value, and you can get to a spot where it works, then a bit higher and you get some different types of crashes with the screen freaking out in different ways, but all end up the same way: crashed CoCo, must hard-reset/power cycle to recover.

Have you ever run into this bug on your CoCo 3? Please leave a comment with your story…

Until next time…

One more thing…

Oh yeah… Here is what the program looks like when it runs.