Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

Extended Color BASIC to Arduino Sketch, part 4

See also: Part 1Part 2, Part 3, Part 4 and full source.

Put on your protective eyewear, folks. You are about to see something very, very disturbing. So disturbing, in fact, that I am hesitant to share it with you. But I will, anyway.

A few nights ago, I began the process of converting my 1983 BBS program line-by-line from Microsoft Extended Color Basic over to Arduino C. This isn’t a port or a rewrite. It’s a conversion as close as possible to the original BASIC source. And it’s something that should never be done.

So let’s get started, and do it.

I will be including the original BASIC source code as comments, followed by the lines of C that replicate the commands. Anything that was a GOSUB will be turned in to a function, but everything else will just be lines of C code, with liberal use of labels and the C “goto” command. (And if anyone ever asks you if it is possible to use goto in C, tell them no. You do not believe it is. We can stop this insanity before it spreads any further.)

Variables

In BASIC, all variables are global and accessible anywhere, so for this conversion, all (almost) of the variables will be made global as well.

In BASIC, there are two types of variables – strings, and numeric. A string is represented by the name of the variable followed by a dollar sign. For instance, A$ or NM$. A numeric variable has no dollar sign, like X or Y.

While BASIC would let you have long variable names, only the first two characters were processed, so a variable called “NAME$” would really be “NA$”. A clever programmer could make sure that the first two letters were unique, and use long variable names like “NAME” and “USERNUMBER” to make the code easier to read. But since I needed every last byte of memory I could get, I did not waste the space on longer variable names.

In BASIC, you could have a string variable called NM$ and a numeric variable called NM. In C, variable names must be unique. You couldn’t have both “int x;” and “char x;”. Because of this, I chose to rename all the basic string variables and add “Str” to the end of the name. I also converted variable names to lowercase, which wasn’t really necessary, but I was already breaking enough rules without having uppercase variable names. NM$ in BASIC becomes nmStr in C. Numeric variables remained the same, just in lowercase.

In BASIC, strings are dynamic. You can have A$=”HELLO” and B$=”GOODBYE” and create a new string like C$=A$+B$. Standard C does not work this way, so all string variables had to have their maximum size predefined. The ALL RAM BBS was written with certain maximum sizes in mind for user names and message lines, so I chose to use those values for the C string lengths.

Memory

On the original TRS-80 Color Computer, I had 32K of memory to play with. On the Arduino UNO R3, I had less than 2K. In BASIC, and with most C systems, all code and initialized variables (like strings) were stored in the same place. But, the Arduino uses something called Harvard Architecture, where the contents of Flash are separate from the RAM. A normal C program that has any string constants (char *msg=”Hello…”) will have those strings loaded in to RAM, taking up some of the precious 2K. For tiny small programs, this is no big deal. but for this project, every last byte is needed.

There are methods to use the Flash for string storage on the Arduino and I had to learn about them and make extensive use of them. This may be unfamiliar to most C programmers, but we can discuss the techniques I used to save memory in a future article. For now, just know there were some things I had to do for this that would not have been needed if there was more RAM to play with.

Due to the very limited amount of memory, I had to greatly downsize the in-memory storage. Instead of holding 200 users, I might only be able to support three or four. Instead of twenty messages of ten lines each, I might only be able to support three tiny messages of, say, two lines each. And instead of each line being up to 64 characters (twice the screen width of the Color Computer’s screen), I changed that to just 32. This makes Twitter posts look huge by comparison.

And, since I needed a quick way to test the limits, I decided to replace all hard coded values in the BASIC source with #defines in the C version. This let me easily change the maximum number of users or the message size to see how far I could get.

Printing

In BASIC, the PRINT command either prints with a carriage return at the end, or not, depending on the use of a semicolon. PRINT”HELLO” would have a carraige return, but PRINT”HELLO”; would not. Originally, I put in the appropriate Arduino “Serial.println()” or “Serial.print()”, but once it came time to deal with TAB, I had to rethink that. In order to track tabs, I needed my own print routine that could keep track of how many characters had been displayed since the last carriage return.

I decided to create print() and printSemi() to act like PRINT and PRINT;. I also created printTab(), printComma() and even a scaled down version of PRINT USING called printUsing(). They do not quite look the same but they should get the same results

But, that was not enough. When BASIC prints a numeric variable, it puts a space before and after the number. So I created printNum() and printNumSemi(). And due to how Arduino handles printing out different variable types, I had to create a special routine for when I wanted to print a single character (else my print routine would print it as a number).

I also had to deal with the different types of strings the Arduino has — whether variables in RAM, or strings from Flash. It was quite a learning experience, and I will document this, as well, in a future article.

GOTO

Yes, C has a goto, but don’t tell anyone you hear it from me. A label is made (“label:”) and then you simply “goto label;”. There are much better ways to do this in C, but since this is a literal translation, I used goto. So there.

BASIC Commands

BASIC has many functions that do not exist in C, so I created some workalike C functions. They are not full implementations of the ones found in Microsoft BASIC — they were just enough to replicate the functionality needed for the BBS.

I even created versions of the SOUND and CLS commands, even though this Arduino version has no sounds or video screen. Line by line port, remember?

Source Code

And now… the source code. This version is trimmed down a bit, with extra things (like support for Ethernet and SD card storage), removed so we can just focus on the conversion. I will post the full source later.

#include

void setup()
{
    Serial.begin(9600);
    while (!Serial)
        ;
    allram();
}

void loop()
{
}

/---------------------------------------------------------------------------/
// In BASIC, strings are dynamic. For C, we have to pre-allocate buffers for
// the strings.
#define INPUT_SIZE  32  // 64. For aStr, etc.

#define MAX_USERS   3   // NM$(200) Userlog size. (0-200, 0 is Sysop)
#define NAME_SIZE   10  // 20. Username size (nmStr)
#define PSWD_SIZE   8   // 8. Password size (psStr & pwStr)
#define ULOG_SIZE   (NAME_SIZE+1+PSWD_SIZE+1+1)

// To avoid hard coding some values, we define these here, too. Each message
// is made up of lines, and the first line will contain the From, To, and
// Subject separated by a character. So, while the original BASIC version
// hard coded this, we will calculate it, letting the subject be as large
// as whatever is left over (plus room for separaters and NULL at the end).
#define FR_SIZE     NAME_SIZE                      // From
#define TO_SIZE     NAME_SIZE                      // To
#define SB_SIZE     (INPUT_SIZE-FR_SIZE-1-TO_SIZE) // "FromToSubj"

// The original BASIC version was hard-coded to hold 20 messages of 11 lines
// each (the first line was used for From/To/Subject). The Arduino has far
// less RAM, so these have been made #defines so they can be changed.
#define MAX_MSGS    4   // 19  (0-19, 20 messages)
#define MAX_LINE    2   // 10  (0-10, 11 lines)

// Rough estimate of how many bytes these items will take up.
#define ULOG_MEM    ((MAX_USERS+1)(ULOG_SIZE))
#define MBASE_MEM   ((MAX_MSGS+1)MAX_LINE*INPUT_SIZE)

// Validate the settings before compiling.
#if (FR_SIZE+1+TO_SIZE+SB+SIZE > INPUT_SIZE)
#error INPUT_SIZE too small to hold "FromToSub".
#endif

/---------------------------------------------------------------------------/

/// And now... The BASIC code begins.

//0 REM ALL RAM BBS System 1.0
//1 REM   Shareware / (C) 1983
//2 REM     By Allen Huffman
//3 REM  110 Champions Dr, #811
//4 REM     Lufkin, TX 75901
//5 CLS:FORA=0TO8:READA$:POKE1024+A,VAL("&H"+A$):NEXTA:EXEC1024:DATAC6,1,96,BC,1F,2,7E,96,A3

*** We will do a CLS later. This FOR/NEXT loop was to load a bit of assembly language in to memory and execute it. This routine would clear memory and give the maximum amount to BASIC. And no, I haven’t lived there since 1995.

*** Next, we have the variables. You didn’t have to pre-define them in BASIC, but it would speed things up later when they were first used since memory would be set aside ahead of time. In the original, sizes were hard coded (see the 200, 19, and 10, below) but in the C code we will use the #define values defined earlier.

//10 CLEAR21000:DIMNM$(200),MS$(19,10),A$,F$,S$,T$,BR$,CL$,NM$,PS$,PW$,A,B,C,CL,LN,LV,MS,NM,KY,UC

// All variables in BASIC are global, so we are declaring them outside the
// functions to make them global in C as well. Arrays in BASIC are "0 to X",
// and in C they are "0 to X-1", so we add one to them in C to get the same
// number of elements.
char nmArray[MAX_USERS+1][ULOG_SIZE];             // NM$(200)
char msArray[MAX_MSGS+1][MAX_LINE+1][INPUT_SIZE]; // MS$(19,10)
char aStr[INPUT_SIZE];                            // A$
char fStr[FR_SIZE];                               // F$ - From
char sStr[SB_SIZE];                               // S$ - Subj
char tStr[TO_SIZE];                               // T$ - To
char nmStr[NAME_SIZE];                            // NM$ - Name
char psStr[PSWD_SIZE];                            // PS$ - Pswd
char pwStr[PSWD_SIZE];                            // PW$ - Pswd

// To save RAM, these two strings will exist in Flash memory. It will
// require a bit of work later to use them (__FlashStringHelper).
prog_char brStr[] PROGMEM = "============================"; // BR$ - border
prog_char clStr[] PROGMEM = "x0cx0e";                        // CL$ - clear

*** Above, PROGMEM causes these strings to be stored in Flash. You will see later that it also requires special code to access them.

int a, b, c, cl, ln, lv, ms, nm, ky, uc;
// A, B, C - misc.
// CL - Calls
// LN - Line Number
// LV - Level
// MS - Messages
// NM - Names (users)
// KY - Keys (commands entered)
// UC - Uppercase input (1=Yes, 0=No)

*** Above, I chose int as the variable type, but byte would be better since none of my numeric variables ever go negative. I should change this, and save an extra 10 bytes (trust me, it all mattered in this project). The only exception would be the CL number of calls variable, since a byte could only count to 255. But, this isn’t a real useful BBS, so beyond a few test calls, maybe that would be fine.

*** The next function is the main BBS code. The only thing not inside of it are routines that were GOSUBS, and any additional helper functions to replicate certain BASIC commands.

void allram()
{
    // HACK - create adefault Sysop account.
    nm = 0;
    strncpy_P(nmArray[0], PSTR("SYSOP\TEST9"), ULOG_SIZE);

*** The original BASIC version required there to be a User 0 for the system operator account. It could never run without it, since that user could not be deleted. But, since we are not using the editor to create users, I hard coded a SysOp account.

cls(); // From line 5

//15 CL$=CHR$(12)+CHR$(14):BR$="============================":GOSUB555
//char cl[] = "\0xC\0xE";
//char br[] = "============================";
gosub555();

line20:
//20 CLS:PRINTTAB(6)"ALL RAM BBS SYSTEM":PRINT"USERS:"NM,"CALLS:"CL:PRINTTAB(5)"SYSTEM AWAITING CALLER";:GOSUB1005:SOUND200,10
cls();
printTab(6);
print(F("ALL RAM BBS SYSTEM"));
printSemi(F("USERS:"));
printSemi(nm);
printComma();
printSemi(F("CALLS:"));
print(cl);
printTab(5);
printSemi(F("SYSTEM AWAITING CALLER"));
gosub1005();
sound(200,10);

*** As you can see, the print() routines look a bit different, but you should be able to follow them exactly as the original BASIC code. The sound() call doesn’t do anything, but I did try to make it delay the same amount of time the CoCo’s sound would have played.

//25 A$="Welcome To ALL RAM BBS!":GOSUB1055:KY=0:CL=CL+1
strncpy_P(aStr, PSTR("Welcome To ALL RAM BBS!"), INPUT_SIZE);
gosub1055();
ky = 0;
cl = cl + 1;

*** In BASIC, you could assign a string just by setting it to a variable. I figured the closest version of this in C would be the string copy function. So, A$=”whatever” becomes a call to copy “whatever” in to the aStr string. The PSTR() macro causes the string to be stored in Flash, and the special version of strcpy_P() is used to copy from Flash to RAM. The end result is the same.

//30 PRINT:PRINT"Password or 'NEW' :";:UC=1:GOSUB1005:PS$=A$:IFA$=""ORA$="NEW"THEN55ELSEPRINT"Checking: ";:A=0
line30:
print();
printSemi(F("Password or 'NEW' :"));
uc = 1;
gosub1005();
strncpy(psStr, aStr, PSWD_SIZE);
if (aStr[0]=='\0' || strcmp(aStr, "NEW")==0)
{
    goto line55;
}
else
{
    printSemi(F("Checking: "));
    a = 0;
}

*** In my original BASIC version, the function starting at line 1005 was an input routine. It would read a line from the user in to A$. It would use the variable UC to know if it should return the string in ALL UPPERCASE. Even at such a young age, I had already figured out parameter passing… I just didn’t have a programming language that supported it.

line35:
//35 A$=NM$(A):B=INSTR(A$,""):NM$=LEFT$(A$,B-1):PW$=MID$(A$,B+1,LEN(A$)-B-1):LV=VAL(RIGHT$(A$,1)):IFPW$=PS$THEN45ELSEA=A+1:IFA< =NM THEN35
strncpy(aStr, nmArray[a], ULOG_SIZE);
b = instr(aStr, "\");
strncpy(nmStr, aStr, b-1);
nmStr[b-1] = '\0';
strncpy(pwStr, &aStr[b], strlen(aStr)-b-1);
pwStr[strlen(aStr)-b-1] = '\0';
lv = atoi(&aStr[strlen(aStr)-1]);
if (strncmp(pwStr, psStr, PSWD_SIZE)==0)
{
    goto line45;
}
else
{
    a = a + 1;
    if (a<=nm) goto line35;
}

*** I wrote my own INSTR() routines to replicate the BASIC command. Instead of comparing a string with IF A$=B$, the C string compare strcmp() was used.

line40: // for empty userlog bug
//40 PRINT"INVALID":KY=KY+1:IFKY20THEN30
print();
printSemi(F("Full Name :"));
uc = 1;
gosub1005();
strncpy(nmStr, aStr, NAME_SIZE);
if (aStr[0]=='\0' || strlen(aStr)>20) goto line30;

//70 PRINT"Password  :";:UC=1:GOSUB1005:PW$=A$:IFA$=""ORLEN(A$)>8THEN30
printSemi(F("Password  :"));
uc = 1;
gosub1005();
strncpy(pwStr, aStr, PSWD_SIZE);
if (aStr[0]=='\0' || strlen(aStr)>8) goto line30;

//75 PRINT:PRINT"Name :"NM$:PRINT"Pswd :"PW$:PRINT"Is this correct? ";:UC=1:GOSUB1005:IFLEFT$(A$,1)="Y"THEN80ELSE65
print();
printSemi(F("Name :"));
print(nmStr);
printSemi(F("Pswd :"));
print(pwStr);
printSemi(F("Is this correct? "));
uc = 1;
gosub1005();
if (aStr[0]=='Y')
{
    goto line80;
}
else
{
    goto line65;
}

*** I could have written something to handle LEFT$, but since I only use it to look at the first character of a string, I decided to just do it the C way and look at the first character of the string.

line80:
//80 NM=NM+1:NM$(NM)=NM$+""+PW$+"0":LV=0:KY=0
nm = nm + 1;
strncpy(nmArray[nm], nmStr, NAME_SIZE);
strcat_P(nmArray[nm], PSTR("\"));
strncat(nmArray[nm], pwStr, PSWD_SIZE);
//strcat_P(nmArray[nm], PSTR("0"));
strcat_P(nmArray[nm], PSTR("1")); // AUTO VALIDATED
//lv = 0;
lv = 1;
ky = 0;
//85 PRINT"Your password will be validated as soon as time permits.  Press":PRINT"[ENTER] to continue :";:GOSUB1005
print(F("Your password will be validated as soon as time permits.  Press"));
printSemi(F("[ENTER] to continue :"));
gosub1005();

*** BASIC allows building strings just by adding them up. I used the C string concatanate strcat() routine to do the same. I have some lines commented out, because the original made all new users unvalidated, and for testing, I wanted someone to be able to register and actually use the system.

//100 'Main Menu
line105:
//105 A$="ALL RAM BBS Master Menu":GOSUB1055
strncpy_P(aStr, PSTR("ALL RAM BBS Master Menu"), INPUT_SIZE);
gosub1055();

//110 PRINT"C-all Sysop","P-ost Msg":PRINT"G-oodbye","R-ead Msg":PRINT"U-serlog","S-can Titles"
printSemi(F("C-all Sysop"));
printComma();
print(F("P-ost Msg"));
printSemi(F("G-oodbye"));
printComma();
print(F("R-ead Msg"));
printSemi(F("U-serlog"));
printComma();
print(F("S-can Titles"));

line115:
//115 PRINTBR$
print((__FlashStringHelper*)brStr);

*** The weird casting to __FlashStringHelper* is something needed to let the print() routine handle string variables that are stored in Flash.

line120:
//120 KY=KY+1:IFKY>200THENPRINT"Sorry, your time on-line is up.":GOTO210ELSEIFKY>180THENPRINT"Please complete your call soon."
ky = ky + 1;
if (ky>200)
{
    print(F("Sorry, your time on-line is up."));
    goto line210;
}
else if (ky>180)
{
    print(F("Please complete your call soon."));
}

line125:
//125 PRINTTAB(7)"?=Menu/Command :";:UC=1:GOSUB1005:A$=LEFT$(A$,1)
printTab(7);
printSemi(F("?=Menu/Command :"));
uc = 1;
gosub1005();
aStr[1] = '\0';

line130:
//130 LN=INSTR("?CGRSPU%",A$):IFLN=0THENPRINT"Invalid Command":GOTO120
ln = instr("?CGRSPU%", aStr);
if (ln==0)
{
    print(F("Invalid Command"));
    goto line120;
}

//135 IFLV5THENPRINT" Sorry, you are not validated.":GOTO125
if (lv5)
{
    print(F(" Sorry, you are not validated."));
    goto line125;
}

//140 ONLN GOTO105,155,205,405,455,305,255,505
if (ln==1) goto line105;
if (ln==2) goto line155;
if (ln==3) goto line205;
if (ln==4) goto line405;
if (ln==5) goto line455;
if (ln==6) goto line305;
if (ln==7) goto line255;
if (ln==8) goto line505;

*** A C switch()/case might have been closer to ON GOTO, but I just chose to do it the brute force way.

//150 'Call Sysop
line155:
//155 A$="Calling the Sysop":GOSUB1055:A=0
strncpy_P(aStr, PSTR("Calling the Sysop"), INPUT_SIZE);
gosub1055();
a = 0;

*** Annoying. I should have known it was “SysOp” and not “Sysop” back then. It stands for System Operator.


//165 PRINT" BEEP!";:SOUND150,5:IFINKEY$=CHR$(12)THEN175ELSEprintING$(5,8);:A=A+1:IFA";A;:GOSUB1005:MS$(MS,A)=A$:IFA$=""THENA=A-1:GOTO345ELSEIFA", a);
gosub1005();
strncpy(msArray[ms][a], aStr, INPUT_SIZE);
if (aStr[0]=='\0')
{
    a = a - 1;
    goto line345;
}
else if (a

*** Since I did not create a LEFT$ function, I cheat and just hack the original aStr to be 2 characters long, by placing a NULL terminator character in position three. C strings are base-0, so it is [0][1][2].

//365 PRINT"Line currently reads:":PRINTMS$(MS,LN):PRINT"Enter new line:":GOSUB1005:A$=LEFT$(A$,64):IFA$=""THENPRINT"Unchanged"ELSEMS$(MS,LN)=A$:PRINT"Corrected"
print(F("Line currently reads:"));
print(msArray[ms][ln]);
print(F("Enter new line:"));
gosub1005();
aStr[INPUT_SIZE-1] = '\0';
if (aStr[0]=='\0')
{
    print(F("Unchanged"));
}
else
{
    strncpy(msArray[ms][ln], aStr, INPUT_SIZE);
    print(F("Corrected"));
}

*** You will see that wherever I need to shorten a string using LEFT$, I just do the trick of NULL terminating it at that position. The -1 is because the C string is base-0, but BASIC counts the string characters starting at 1. I think.

//370 GOTO360
goto line360;

line375:
//375 CLS:PRINTCL$"Message Reads:":FORB=1TOA:PRINTUSING"##>";B;:PRINTMS$(MS,B):NEXTB:GOTO345
cls();
print((__FlashStringHelper*)clStr);
print(F("Message Reads:"));
for (b=1; b< =a; b++) {
    printUsingSemi("##>", b);
    print(msArray[ms][b]);
}
goto line345;

*** PRINT USING in BASIC could do a bunch of things, but I was only using it to print a number without spaces before and after it, which is what happens in BASIC if you just print a numeric variable.

line380:
//380 MS$(MS,0)=T$+""+F$+""+S$:MS=MS+1:PRINT"Message"MS"stored":GOTO115
strcpy(msArray[ms][0], tStr);
strcat_P(msArray[ms][0], PSTR("\"));
strcat(msArray[ms][0], fStr);
strcat_P(msArray[ms][0], PSTR("\"));
strcat(msArray[ms][0], sStr);
ms = ms + 1;
printSemi(F("Message"));
printSemi(ms);
print(F("stored"));
goto line115;

line385:
//385 PRINT"Message Aborted":GOTO115
print(F("Message Aborted"));
goto line115;

//400 'Read Msg
line405:
//405 IFMS=0THENPRINT"The message base is empty.":GOTO115
if (ms==0)
{
    print(F("The message base is empty."));
    goto line115;
}

//410 CLS:PRINTCL$
cls();
print((__FlashStringHelper*)clStr);

line415:
//415 PRINT"Read Message 1 -"MS":";:GOSUB1005:A=VAL(LEFT$(A$,2)):IFAMS THEN115
printSemi(F("Read Message 1 -"));
printSemi(ms);
printSemi(F(":"));
gosub1005();
aStr[2] = '\0';
a = atoi(aStr);
if (ams) goto line115;

//420 A$=MS$(A-1,0):B=INSTR(A$,""):C=INSTR(B+1,A$,""):T$=LEFT$(A$,B-1):F$=MID$(A$,B+1,C-B-1):S$=RIGHT$(A$,LEN(A$)-C)
strncpy(aStr, msArray[a-1][0], INPUT_SIZE);
b = instr(aStr, "\");
c = instr(b+1, aStr, "\");
strncpy(tStr, aStr, b-1);
tStr[b-1] = '\0';
strncpy(fStr, (aStr-1)+b+1, c-b-1);
fStr[c-b-1] = '\0'; // FIXTHIS - max copy sizes here?
strncpy(sStr, right(aStr, strlen(aStr)-c), SB_SIZE);

//425 IFS$="E-Mail"ANDLV<8THENIFNM$<>T$ANDNM$<>F$THENPRINT"That message is private.":GOTO415
if (strcmp(sStr, "E-Mail")==0 && lv<8)
{
     if (strcmp(nmStr, tStr)!=0 && strcmp(nmStr, fStr)!=0)
    {
        print(F("That message is private."));
        goto line415;
    }
}

//430 CLS:PRINTCL$"Message #"A:PRINT"From :"F$:PRINT"To   :"T$:PRINT"Subj :"S$:PRINT:B=0
cls();
print((__FlashStringHelper*)clStr);
printSemi(F("Message #"));
print(a);
printSemi(F("From :"));
print(fStr);
printSemi(F("To   :"));
print(tStr);
printSemi(F("Subj :"));
print(sStr);
print();
b = 0;

line435:
//435 B=B+1:PRINTMS$(A-1,B):IFMS$(A-1,B)=""THEN440ELSEIFB"?DROWSSAP"THENPRINT"Thank You!":GOTO115
printSemi(F("PASSWORD?"));
gosub1005();
if (strcmp(aStr, "?DROWSSAP")!=0)
{
    print(F("Thank You!"));
    goto line115;
}

*** And now you know the super secret shutdown password.

//515 PRINT"Abort BBS? YES or NO? ";:UC=1:GOSUB1005:IFA$<>"YES"THEN115
printSemi(F("Abort BBS? YES or NO? "));
uc = 1;
gosub1005();
if (strcmp(aStr, "YES")!=0) goto line115;

//520 GOSUB605:STOP
gosub605();
return;
} // end of allram()

*** Next we have the subroutines, which were originally GOSUBs. Since I was not able to hook up a cassette recorder to my Arduino, I chose to just fake the load and save routines.

/---------------------------------------------------------------------------/
    // Subroutines (formerly GOSUBs)
    //
    // 550 '%LOAD%
    void gosub555()
{
    // 555 PRINT"%LOAD% [ENTER] WHEN READY";:GOSUB1005
    printSemi(F("%LOAD% [ENTER] WHEN READY"));
    //  gosub1005();
    print();
    if (aStr[0] == '!')
        return;

    // 560 OPEN"I",#-1,"USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
    loadUserlog();

    // 565 OPEN"I",#-1,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
    loadMsgBase();
}

// 600 '%SAVE%
void gosub605()
{
    // 605 PRINT"%SAVE% [ENTER] WHEN READY";:GOSUB1005:MOTORON:FORA=0TO999:NEXTA
    printSemi(F("%SAVE% [ENTER] WHEN READY"));
    gosub1005();

    // 610 OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
    saveUserlog();

    // 615 OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
    saveMsgBase();
}

// 1000 'User Input
#define CR 13
#define INBUF_SIZE 64
void gosub1005()
{
    byte ch; // Used only here, so we can make it local.

    // 1005 LINEINPUTA$:A$=LEFT$(A$,64):IFUC=0ORA$=""THENRETURN
    lineinput(aStr, INPUT_SIZE);
    if ((uc == 0) || (aStr[0] == '\0'))
        return;

    // 1010 FORC=1TOLEN(A$):CH=ASC(MID$(A$,C,1)):IFCH>96THENMID$(A$,C,1)=CHR$(CH-32)
    for (c = 0; c96)
        aStr[c] = ch - 32;
    // 1015 IFCH=92THENMID$(A$,C,1)="/"
    if (ch == 92)
        aStr[c] = '/';
    // 1020 NEXTC:UC=0:RETURN
}
uc = 0;
}

// 1050 'Function Border
void gosub1055()
{
    // 1055 CLS:PRINTCL$BR$:PRINTTAB((32-LEN(A$))/2)A$:PRINTBR$:RETURN
    cls();
    print((__FlashStringHelper)clStr);
    print((__FlashStringHelper)brStr);
    printTab((32 - strlen(aStr)) / 2);
    print(aStr);
    print((__FlashStringHelper *)brStr);
}

*** Now we try to recreate some of the BASIC commands I used. Or at least just enough of them to get the desired results.

/---------------------------------------------------------------------------/
// The following functions mimic some of the Extended Color BASIC commands.

// CLS
// Clear the screen.
void cls()
{
    print(F("n--------------------------------n"));
}

// SOUND tone, duration
// On the CoCo, tone (1-255), duration (1-255; 15=1 second).
void sound(byte tone, byte duration)
{
    Serial.write(0x07);   // BEL
    delay(duration*66.6); // Estimated delay.
}

/---------------------------------------------------------------------------/
// String functions.

// STRING$(length, charcode)
// Generate a string of length charcode chracters.
void string(byte length, byte charcode)
{
int i;

for (i=0; i0)
{
ch = Serial.read();
}
else
{
continue; // No data. Go back to the while()...
}
switch(ch)
{
case -1: // No data available.
break;
case CR:
  print();
  cmdLine[cmdLen] = '&#92;&#48;';
  done = true;
  break;

case BS:
  if (cmdLen&gt;0)
  {
    printCharSemi(BS);
    printSemi(F(&quot; &quot;));
    printCharSemi(BS);
    cmdLen--;
  }
  break;

default:
  // If there is room, store any printable characters in the cmdline.
  if (cmdLen31) &amp;&amp; (ch&lt;127)) // isprint(ch) does not work.
    {
      printCharSemi(ch);
      cmdLine[cmdLen] = ch; //toupper(ch);
      cmdLen++;
    }
  }
  else
  {
    printCharSemi(BEL); // Overflow. Ring 'dat bell.
  }
  break;
} // end of switch(ch)
} // end of while(!done) return cmdLen; } // INKEY$ // Return character waiting (if any) from standard input (not ethernet). char inkey() { if (Serial.available()==0) return 0; return Serial.read(); } /---------------------------------------------------------------------------/ // File I/O // Ideally, I would have created wrappers for the OPEN, READ, CLOSE commands, // but I was in a hurry, so... //560 OPEN"I",#-1,"USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE void loadUserlog() { print(F("(USERLOG would be loaded from tape here.)")); } //565 OPEN"I",#-1,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN void loadMsgBase() { print(F("(MSGBASE would be loaded from tape here.)")); } //610 OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE void saveUserlog() { print(F("save USERLOG")); } //615 OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN void saveMsgBase() { print(F("save MSGBASE")); }

*** Next, the PRINT routines. Due to the way things work with all these Flash based strings, I had to duplicate some of these routines with the only difference being what type of parameter was passed in (RAM string versus Flash string). This was the first time I really made use of any C++ functionality.

/---------------------------------------------------------------------------/
// Print (output) routines.

// For TAB to work, we need to track where we think we are on the line.
byte tabPos = 0;

// We want to simulate the following:
// PRINT "HELLO"  -- string, with carraige return at end of line
// PRINT A        -- number (space before and after), with carraige return
// PRINT "HELLO"; -- no carraige return
// PRINT TAB(5);  -- tab to position 5
// PRINT "A","B"  -- tab to column 16 (on 32-column screen)

// Due to various types of strings on Arduino (in memory or Flash), we will
// have to duplicate some functions to have versions that take the other
// types of strings.

// printTypeSemi() routines will not put a carraige return at the end.

// PRINT TAB(column);
// PRINT TAB(column);
void printTab(byte column)
{
while(tabPos10)
{
tempNum = tempNum/10;
numDigits++;
}
while(numDigits

And there you have it, for better or for worse. I will follow up with a full posting of all the source, including the Ethernet and SD card routines (but there’s not enough memory to use them both at the same time).

More to come…

Extended Color BASIC to Arduino Sketch, part 3

See also: Part 1Part 2, Part 3, Part 4 and full source.

Just because something can be done, doesn’t mean it should.

“Why are you converting thirty year old BASIC programs to C?” you might ask… Well, mostly just to see how easily it could be done, but I admit, nostalgia has a big part in this. I am not quite sure what led me to looking in to my 1983 BBS program, but once I saw it running on the XRoar CoCo emulator, it brought back all kinds of fond memories of sitting in front of a TV set typing in lines of BASIC code.

I believe I got in to computers at the very best time. A few years earlier, and it was punch cards and flip switches. A decade or so later, and computers were becoming appliances. There was a bit of time in between when owning a computer primarily meant programming a computer. I got in to it late enough that I didn’t have to be a hardware guy to build my own computer, and early enough that I was still compelled to figure out what to do at the blinking cursor after the “OK” prompt. There just wasn’t nearly as much off-the-shelf software to buy back then, you see. Especially on my allowance.

I realized tonight that there were many like myself who once wrote their own programs and experimented. As time moved on, most of us ended up with a PC or Mac or even Linux machine, and do little more than run software written by others. Thirty years ago, if we wanted to print up a sheet of return address mailing labels, we would type in a few lines of BASIC and be done.

10 FOR I=1 TO 25
20 PRINT #-2, "Allen C. Huffman"
30 PRINT #-2, "PO Box 22031"
40 PRINT #-2, "Clive IA 50325-94014"
50 PRINT #-2
60 NEXT I

Today, if we don’t have a template for MS-Word, we might end up Googling to find some freeware or shareware offering. (I suppose we have also passed up the pre-Internet times when folks had to go to a computer store like Babbages or CompUSA to look for something on the shelf to buy.)

But what about all those BASIC coders that perhaps haven’t ever written even the simplest line of code for a Windows PC? Did they all just decide they no longer liked it? Or did the environment change enough that programming just wasn’t as accessible?

32K CoCo BASIC memory.

Certainly, modern computers no longer power up instantly and present you with a place to type and run a program…

My recent exposure to the Arduino has made me want to program for fun again. Just like my TRS-80 Color Computer, I purchased my Arduino UNO R3 at a nearby Radio Shack. But unlike my CoCo, the Arduino required a host PC/Mac/Linux machine to make it do anything, and then you had to know C/C++. Fortunately, I do… I learned it on my CoCo under OS-9 back in the late 1980s! But what about all those non-OS-9 CoCo owners who stuck with Disk Extended Color BASIC? For them, an Arduino would present quite a learning curve.

But could someone who knows BASIC learn enough to write (very poor) C and actually use an Arduino? I think they could.

void program()
{
    for (i = 0; i < = 25; i++)
    {
        Serial.println("Allen C. Huffman");
        Serial.println("P.O. Box 22031");
        Serial.println("Clive IA 50325-9401");
        Serial.println();
    }
}

Tonight, my *ALL RAM* BBS system is fully functional on my Arduino. The code I originally wrote thirty years ago in BASIC was well structured, with subroutines for items like input and output which translated easily to C functions. Program logic was a mess of if statements and gotos, but since C supports both, it was fairly easy to make the jump. But, it wasn’t pretty. Instead of line numbers, I created C lables in the format of “lineXXX:” where XXX was the desired line number:

line120 :
    // 120 KY=KY+1:IFKY>200THENPRINT"Sorry, your time on-line is up.":GOTO210ELSEIFKY>180THENPRINT"Please complete your call soon."
    ky = ky + 1;
if (ky > 200)
{
    Serial.println("Sorry, your time on-line is up.");
    goto line210;
}
else if (ky > 180)
{
    Serial.println("Please complete your call soon.");
}

And, to make things flow a bit better, I created C functions to replicate some of the BASIC keywords:

line130 :
    // 130 LN=INSTR("?CGRSPU%",A$):IFLN=0THENPRINT"*Invalid Command*":GOTO120
    ln = instr("?CGRSPU%", aStr);
if (ln == 0)
{
    print("*Invalid Command*");
    goto line120;
}

Some things did not have a direct equivalent in C, but could be done in a simple, brute-force manner:

//140 ONLN GOTO105,155,205,405,455,305,255,505
if (ln==1) goto line105;
if (ln==2) goto line155;
if (ln==3) goto line205;
if (ln==4) goto line405;
if (ln==5) goto line455;
if (ln==6) goto line305;
if (ln==7) goto line255;
if (ln==8) goto line505;

I was surprised at how well one could program C as if it were BASIC.

As of tonight, *ALL RAM* BBS runs on my Arduino, complete with userlog and message base. Due to limited memory, there is only room for a few users and a few TINY (Twitter-sized) messages, but it all works. I had to do some Arduino tricks to get strings and such moved in to Flash to free up precious RAM, and once I did that, I even had room to use some SD memory card routines for replacing the original cassette tape load/save routines. (Yes, I realize this is rather silly. There’s no reason to run a RAM-based BBS if you have access to disk storage, but I never said this was a practical experiment.)

I even managed to use my Arduino Ethernet shield and telnet in to the micro BBS. The quick modifications I made to allow this weren’t fully baked — once the connection was broken I had to reset the Arduino — but I believe I will work on that next. (Though probably not with SD card support. I just don’t think there’s enough RAM for both.)

So, in coming days or weeks, I will try to find time to share the code, or at least examples of how I created BASIC-like functions in C.

Until then…

*ALL RAM* BBS Logoff Screen

P.S. – For the younger ones of you out there, yes, there was a time when lowercase was not available. The original TRS-80 Color Computer presented inverse letters to represent lowercase. It wasn’t until the later model “Tandy” Color Computer 2s that the machine got true lowercase. (My CoCo 1 had an aftermarket lowercase board installed in it…) I feel so old.

Extended Color BASIC to Arduino Sketch, part 2

See also: Part 1Part 2, Part 3, Part 4 and full source.

Yesterday, I shared a silly little project I was undertaking where I was going to port a BASIC program to the Arduino, line-by-line. I am glad to say I have successfully done just that, but the practicality of such accomplishment is very questionable.

For programs that were not created to use 21K of RAM for a memory-resident userlog and message base (like my *ALL RAM* BBS program does), it seems fairly easy to port over Microsoft BASIC code and get it running on the Arduino with surprisingly easy changes. I am tempted to find some classic BASIC programs (like ELIZA or Hunt the Wumpus) and try to convert them, as well, just to see if 2K of RAM is enough for hopefully less memory-hungry code.

So let’s talk a bit about memory. The Radio Shack TRS-80 Color Computer came out in 1980 with a 4K model (later generations would support as much as 512K, or megabytes through third party suppliers). In a 4K system, all of the BASIC code took memory, as well as any variables the program used. I expect working in an Arduino is similar to the 1980 experience, except for more program space.

On a 4K computer, if you had a BASIC program that took up 3K, you only had 1K left for variables. On the Arduino UNO, there is 2K of RAM, and 32K of Flash storage to hold the program. So, that same 3K program that only had 1K of RAM for variables on a 4K CoCo would do much better on an Arduino since it could store the 3K program in Flash, then have the full 2K for variables.

32K CoCo BASIC memory.

Unfortunately, my BBS program was designed to work on a 32K CoCo. These early 8-bit computers, even with 64K upgrades, still had a BASIC that only recognized 32K (without loading some extra patch program). And, on power up, some of that memory was reserved to hold four graphics pages. Thus, a 32K CoCo would show 24874 bytes available in BASIC.

You could adjust the amount of memory reserved for graphics screens higher or lower with the “PCLEAR” command. A “PCLEAR 8” reduced memory down to 18727 bytes. 6K (6144 bytes, or 1.5K per graphics page) was consumed. For programs not making use of these graphics, we wanted to get rid of that reserve and use it ourselves. Unfortunately, the PCLEAR command only allowed going down to “PCLEAR 1”, which gave 29479 bytes free for BASIC. That’s a nice bit of extra, but someone clever figured out how to achieve a PCLEAR 0 and get the most memory we could use without patching BASIC: 31015 bytes free.

Note that, once you plugged in a Radio Shack Disk Controller and added Disk Extended Color BASIC, the memory available went down a bit due to overhead of supporting the disk system. For this article, we are looking strictly at a circa 1980 style CoCo with a cassette recorder only.

And what does this have to do with the Arduino? I wanted to make sure you understood the limits I was facing when trying to port a cassette based BBS program to a machine with only 2K of RAM (and not all of that is usable by the user program).

My end result was a fully functional clone of the *ALL RAM* BBS, but with some rather impractical limits. For my demo system, I only allocated enough space to support about four users. Even I have more friends than that. And for messages, the limit was about four messages, each having just two or three lines. I would make a comment about how useless this would be, but Twitter seems to have proven people will use something that even gives less space than my 2K BBS.

So how did I do it? One word: gotos. Lots and lots of gotos. My translation was literal. Even though there are much better, more efficient ways to write this program (which I plan to undertake), I thought it would be fun to see just how easily BASIC could be coded in C.

I started out by taking an ASCII listing of the BASIC program, and adding “//” comment markers to the start of each line. I pasted that in to a fresh Arduino sketch, so it looked like this:

//0 REM *ALL RAM* BBS System 1.0
//1 REM Shareware / (C) 1983
//2 REM By Allen Huffman
//3 REM 110 Champions Dr, #811
//4 REM Lufkin, TX 75901
//5 CLS:FORA=0TO8:READA$:POKE1024+A,VAL("&H"+A$):NEXTA:EXEC1024:DATAC6,1,96,BC,1F,2,7E,96,A3
//10 CLEAR21000:DIMNM$(200),MS$(19,10),A$,F$,S$,T$,BR$,CL$,NM$,PS$,PW$,A,B,C,CL,LN,LV,MS,NM,KY,UC
//15 CL$=CHR$(12)+CHR$(14):BR$="*==============*==============*":GOSUB555
//20 CLS:PRINTTAB(6)"*ALL RAM* BBS SYSTEM":PRINT"USERS:"NM,"CALLS:"CL:PRINTTAB(5)"SYSTEM AWAITING //25 A$="Welcome To *ALL RAM* BBS!":GOSUB1055:KY=0:CL=CL+1
//30 PRINT:PRINT"Password or 'NEW' :";:UC=1:GOSUB1005:PS$=A$:IFA$=""ORA$="NEW"THEN55ELSEPRINT"Checking: ";:A=0

And thus it began. I would go line-by-line and try to write C code for each statement. All C variables would be made global, just like they were in BASIC, and blocks of code called by GOSUBs would become standard C functions (since they return just like BASIC). But, for GOTOs, I decided to just use the C goto, and added labels in the format of “line125:” so I could later “goto line125;” in C.

It looked like it just might work… (And, since I started this story in reverse, you already know that it did, but I will share some of the ways I did it in a future posting.)

Until then… Enjoy having more than 2K of RAM!

Extended Color BASIC to Arduino Sketch

See also: Part 2, Part 3, Part 4 and full source.

  • 2014/03/16 Update: The source code to this is now on GitHub. Check the Arduino link at the top of each page of this site.

In 1983, I released a BBS (bulletin board system) for Tandy/Radio Shack TRS-80 Color Computer (CoCo). Unlike all of the other ones available at the time, mine was rather unique. Instead of requiring “3-4 disk drives” to operate, mine would work with… zero disk drives.

The *ALL RAM* BBS system was designed to run on a 32K CoCo with a cassette tape deck. On startup, it would read in the userlog and message base in to RAM, and then as users called in, messages they posted would be stored in memory. When the SysOp (system operator) needed to use the computer, or was ready to shut things down for the day, he would save the system back to tape.

It was small and primitive, but considering how primitive the state-of-the-art was back then, it was still pretty usable.

The software was meant to be marketed by a CoCo software company, but things ended up not happening, and the software was eventually released as freeware (even though, back then, we did not yet have the names “shareware” or “freeware”. I still remember an experiment a software company did for something they called “pass the hat software”… I guess their name just wasn’t catchy enough).

But I digress.

Tonight, thanks to the XRoar Color Computer emulator, I was able to get my thirty year old software running again thanks to the software being available at the wonderful BBS Documentary website.

My oh my. What a simpler time.

Tonight, I wondered how hard it would be to translate Microsoft Extended Color BASIC to Arduino C. After all, C has a “goto” statement…

And here is part of the *ALL RAM* BBS system running on an Arduino:

The 1983 Radio Shack Color Computer BBS package is back... This time on Arduino!
The 1983 Radio Shack Color Computer BBS package is back… This time on Arduino

The userlog and message base is not yet functional, and likely never will be since the Arduino only has 2K of memory versus 32K on the CoCo 1 it was designed for. But, when I have some time, I will finish it up and maybe support a few users, and a few messages. Perhaps I can even use some of the memory saving tricks I have been using on some work projects to compress things quite a bit.

We shall see.

Hello, *ALL RAM*. Nice to see you again.

P.S. – The *ALL RAM* BBS ran in Houston, Texas under the name Cyclops Castle. The version running there was disk enhanced. Instead of loading and saving to cassette, it used a single disk drive. The software was modified to support dozens of separate message bases, and the user could switch them from the menu. It was quite a feat on a single floppy disk. One day I will have to track down the SysOp, Graham, and see what he remembers from this great experiment.

Use a TRS-80 Model 100 keyboard on a PC

Over on the Color Computer mailing list, Frank Swygert (editor of American Motors Cars Magazine) mentioned a project where someone else was using a Teensy to hook an old matrix-style keyboard up to a modern PC.

A bit of Googling led me to a similar project where someone built a Radio Shack TRS-80 Model 100 keyboard interface that lets you use it on a PC via USB. This is precisely one of the things I will be doing with a Radio Shack Color Computer keyboard (also planned to use the Teensy 2.0, since it has enough I/O lines and is very inexpensive). Share and enjoy:

http://www.seanet.com/~karllunt/M100_usb_keyboard.html

I have also been informed that Chris Hawks of HawkSoft has already done this, using a Raspberry Pi running a version of the MESS emulator. He created a scaled down version of MESS to emulate the Radio Shack Color Computer, then used Teensy to interface an actual CoCo keyboard to the Pi. Very cool.

Parsing Hayes modem AT commands, part 2

See also: Part 1

I know I am supposed to be posting my updated joystick code, which I will do as soon as I have a moment, but I wanted to share some other new stuff first so it can start getting in to the Google indexes.

I have created the first part of some quick-n-dirty code to handle Hayes modem “AT” commands. For a bit of background on the Hayes modem command set, check this Wikipedia entry:

http://en.wikipedia.org/wiki/Hayes_command_set

For some more specific details, check out this Wikibooks entry:

http://en.wikibooks.org/wiki/Serial_Programming/Modems_and_AT_Commands

The Hayes modem company needed a way for the controlling system (user, terminal program, BBS, whatever) to switch the modem from Data Mode (where bytes come in, and bytes go out) to Command Mode, where the modem could be controlled (dial a number, hang up, configure how many rings to answer on).

To get in to Data Mode, a series of three characters is sent – the default being “+++” – with a one second pause (called the “guard time”) before and after them. That way, if the string “+++” appears in normal data being transmitted (like this article), the modem doesn’t respond to it. It would take a pause, then the “+++”, then another pause, and the modem would stop sending and receiving data and present the host system/user with an “OK” prompt and then accept commands.

My code was designed to be used in the main loop() where data would be processed. Code would check for incoming data from the remote device and handle it (such as seeing bytes come in over a modem or serial port), and then look for local input (like the user typing, or a program sending data out). If it was a dumb terminal program, that loop might look like this pseudo code:

loop()
{
    char ch;

    ch = readIncomingData();    // Get byte from remote system.
    Serial.write(ch);           // Print byte to local screen.
    ch = Serial.read()          // Get byte from local input.
         writeOutgoingData(ch); // Print byte to remote system.
}

This, of course, is not working code. A few more lines would be needed to check for valid data and such, but hopefully you get the idea. The loop would continuously run, reading data if it were there, or moving on to the next instruction.

My code is designed to plug in to that loop, after each byte is read from the local user:

loop()
{
    char ch;

    ch = readIncomingData();                      // Get byte from remote system.
    Serial.write(ch);                             // Print byte to local screen.
    ch = Serial.read()                            // Get byte from local input.
         if (cmdModeCheck(ch) == true) cmdMode(); // ADDED
    writeOutgoingData(ch);                        // Print byte to remote system.
}

The idea is to let me code inspect each character that comes from the host system, and then see if there has been a pause of at least “guard time” since the previous character, and if so, start evaluating the characters to see if they are the “+++” escape sequence. If three plusses are seen in a row, it then sees if “guard time” has elapsed and, if so, return a true value to let the calling function know it is time to switch in to Command Mode.

According to the Hayes modem standard, the character used for the escape sequence (“+”) is configurable, as is the guard time. Because of this, I use two global variables for these, so the user can modify them and affect my cmdModeCheck() function:

// Define the escape sequence.
#define ESC_GUARD_TIME 1000 // Seconds required before/after escape sequence.
#define ESC_CHARACTER  '+'  // Default escape character.

// For Command Mode.
unsigned int escGuardTime = ESC_GUARD_TIME; // Delay before/after esc sequence.
static char  escCharacter = ESC_CHARACTER;  // Escape character

I am just using the #defines for default values. A function will be written later that will let the user set them, like setCmdModeCharacter(‘?’) to make it “???” or setCmdModeGuardTime(2) to change the guard time to two seconds.

Not much here so far, so let’s look at the actual function. I will insert some comments

// Magic numbers.
#define ESC_TIMES 3 // Number of escape characters ("+++").

boolean cmdModeCheck(char ch)
{
    static unsigned long escCheckTime = 0; // Next time to check.
    static byte escCounter = 0;            // Number of esc chars seen.

    // If no character is being passed in, we are just doing a check to see if
    // we are in a "wait for end guard time" mode.
    if (ch == 0)
    {
        // See if we are waiting to enter command mode.
        if (escCounter == ESC_TIMES)
        {
            // Yep, we have already found all the escape sequence characters.
            if ((long)(millis() - escCheckTime) >= 0)
            {
                // And the pause has been long enough! We found an escape sequence.
                escCounter = 0;
                escCheckTime = millis() + escGuardTime;

                return true; // Yes, it is time for Command Mode.
            }
        }
    }
    else // if (ch==0)
    {
        // If there has been a pause since the last input character...
        if ((long)(millis() - escCheckTime) >= 0)
        {
            // Check to see if it's an escape byte.
            if (ch == escCharacter)
            {
                // Move to next character to look for.
                escCounter++;

                // Are we out of escape characters to check for?
                if (escCounter >= ESC_TIMES)
                {
                    // Set after delay to signify end of escape sequence.
                    escCheckTime = millis() + escGuardTime;
                }
            }
            else
            {
                // Reset. Not an escape character.
                escCounter = 0;
                escCheckTime = millis() + escGuardTime;
            }
        }
        else
        {
            // Reset. Not an escape character.
            escCounter = 0;
            escCheckTime = millis() + escGuardTime;
        }
    } // end of if (ch==0) else

    return false; // No, it is not time for Command Mode.
}

I should explain that, in order for this function to work, it needs to be called often enough to determine if the guard time (1 second) has elapsed between incoming data. If the input is idle, the function still must be called, but with ch=0 (no data) which is why, at the top, it looks for an incoming character of 0 then it will handle timing stuff.

The way it works is by keeping a count of how many escape characters in a row we have seen so far. I use static variables so they maintain their values between calls to the function. Since I do not use them anywhere outside of this function, I saw no reason to make them global.

“escCounter” starts out at 0. If we get a “ch=0” value, we want to see if we are waiting on the ending guard time. i.e., have we gotten as far that we’ve seen 3 escape characters in a row, and are now looking for a 1 second pause? If escCounter is 3 (ESC_TIMES), we use a nice timing rollover routine from the Arduino.cc Playground to see if it’s been at least guard time with no other characters coming in. If it has been that long, we reset out escCounter back to 0, and make a note of some future time (representing now + guard time in the future). More on this in a moment, but by returning “true” at this point, we have reported back that we have seen “(pause)+++(pause)”.

Ideally, I should have explained the previous code after explaining this next bit first, since it is setting some things up we haven’t discussed yet. Hang in there and things should become clearer…

At the else, we handle a situation if “ch!=0” (i.e., if we were passed in a character from the host system/user). When we initialized this function, there was an “escCheckTime” variable set to 0. In this loop, we test to see if the time difference between now “escCheckTime” to see if we have gone past it. The very first time this runs, that will be some number minus 0, which will always be greater than 0, and thus always look like it’s been that long. Since we are just starting up, we always want to consider the first character we see. Subsequent calls will be using an “escCheckTime” that is generated based on “now + guard time”, so we are really checking to see if we have passed that future moment in time.

As you will see in a moment, every time a non escape character comes in, or anything else comes in during our guard time wait period, we just reset everything since it’s clearly not an escape sequence.

BUT, if the character we receive IS our escape character (“+”), we increment our “escCounter” to indicate how many of them we have seen in a row. It would go from 0 to 1 this first time.

If the counter gets up to 3 (ESC_TIMES), we have seen three of the escape characters in a row without being reset, so we set our escCheckTime to be “now plus guard time”. If you go back a few paragraphs, you will see we check this in the “nothing to do here, move along” function, so it can say “oh, it’s been at least guard time, AND we have seen our escape character sequence. Must be Command Mode time!”

But if it was NOT our escape character (else), we reset escCounter back to 0, and reset our check time yet again to “now plus guard time” to start the whole process over.

The next else is from the “has it been longer than guard time” if statement, and if we got a character before our guard time elapsed, we know it’s not an escape character so reset everything (just like the previous paragraph).

I hope to clean that up a bit so it doesn’t have to do it in two places like that.

At the end, we return false, because if we got there, we are in the process of either normal data, or scanning through a potential escape character sequence.

When I return, I will provide a full code example, and hopefully have some enhancements to the above function. Until then… I have to get back to packing. My rent is going up and it looks like I will be moving soon.

Parsing Hayes modem AT commands, part 1

2014-03-03: Latest source code is on GitHub.

Several of my Arduino plans involve the old 1980’s Radio Shack Color Computer (CoCo). I have previously discussed plans to use modern USB mice, joysticks and keyboards with this old home computer, as well as going the other way and allowing old CoCo joysticks to be used on modern PCs.

That seemed like an easy first step, and one I hope to find time for some time before I retire. I haven’t had time to touch any of this for awhile. That, however, does not stop me from planning even more projects.

There is an Arduino Ethernet shield currently available for $17.99 from Sainsmart in China. They ship free to the USA, but it takes awhile. For a bit more, you can buy it from Amazon and have it shipped 2nd day via Amazon Prime.

These shields provide an Ethernet port for the Arduino, and library code is provided to do basic things such as make a TCP socket connection. Samples exist showing how to go retrieve a web page, FTP put/get a file, or even serve up web content so you can create an HTML interface to interact with the device. With the Arduino doing the physical interfacing to the wire, and heavy lifting of the TCP/UDP/IP protocol stack, this might be an easy way to bring internet connectivity to the CoCo.

Over a decade ago at the 2001 Chicago CoCoFEST! convention, Minnesota-based Cloud-9 Tech announced plans for the “Superboard” hardware add-on. It would be a secondary I/O board that would connect inside a CoCo case and bring all kinds of hardware add ons, such as a hard disk interface. The product has yet to be completed, but many spinoff items have been released during development.

One of the proposed features of the Superboard was the addition of an iChip from ConnectOne. This chip implemented TCP/IP as well as common internet protocols like FTP, Telnet and mail. Small devices could talk to the chip via serial (like talking to a modem) and send commands, then the chip would go do the actual work via a dialup ISP or ethernet link. I thought this was the killer feature, and started referring to this project as the “Internet CoCo.”

I even looked in to making an RS232 adapter that would have an iChip in it, so you could plug a cable from an RS232 pak on the CoCo to this adapter, then to a modem and get all the features that way. But, at the time, I didn’t have the money to invest in the ConnectOne reference platform (a modem with iChip built in) to work on it.

Well, today the iChip is still made and only cost $25 in single unit quantities. I still think this adapter would be a great idea, but perhaps there is another way…

It would be relatively trivial to write Arduino code that would do this. A CoCo (or any device with RS232) could send commands to the device to establish network connections and then the data could be passed back as serial data. The only tricky bit would be allowing multiple socket connections (FTP, for instance, uses two at a time). For that, some form of serial multiplexing (using one serial line with a protocol on it to act as multiple lines) could be used.

Side Note: There is an absolutely amazing thing called DriveWire created by a former coworker of mine, Boisy Pitre. He created a serial protocol to allow a PC to host disk files for a CoCo via the bitbanger serial port. Over the years, the protocol has evolved and gained many features well beyond virtual disks. It’s truly amazing.

Well, today, the DriveWire server already implements much of what I am talking about doing, including multiplexing serial ports so a CoCo, connected by a single serial cable at 115Kbaud, can have multiple virtual serial ports, MIDI ports, disks, hard drives, etc. DriveWire even runs on a $25 Raspberry Pi, so one could do this without even needing a full PC nearby…

…but that won’t stop me from learning and experimenting. In fact, after I started my current job almost a year ago, I got access to a Raspberry Pi which I hooked up recently for the first time. I expect whatever I develop for Arduino I will also port to Raspberry Pi. Perhaps I can even make the Arduino code speak some of the DW protocol for virtual serial ports and such.

We shall see.

But I digress.

The real focus of this posting is to share my code for a Hayes modem “AT” command parser. In olden days of dial up modems, there were certain “smart” modems that could dial the phone on their very own. Amazing. On power up, these modems were in “command mode” and you could type commands to them like “ATDT 555-1212”. This mean “(At)tention. (D)ial using (T)ouch tone. 555-1212”. “ATH” was the hang up command. Various other commands existed for things like telling the modem if it should auto-answer the phone after a certain amount of rings, or specify how fast the touch tone dialing should be.

The “AT” command set was created by the Hayes Modem company, and other manufactures had their own system for interacting with their modems, but eventually the Hayes command set won out and became a standard. (There’s a great Wikipedia article on this.)

Since the iChip (and pretty much everything else, including modern cell phone modems) makes use of a Hayes-style command set, I thought I would implement something similar myself.

So, coming up soon, I will share code that handles the basics of this, and then will start piecing things together to interface it to the Arduino Ethernet port so you could do something like “ATDI 127.0.0.1:80″ (Attention, dial over the internet to IP address 127.0.0.1 port 80”.

Sounds fun. If I can find the time.

USB host (read a USB keyboard/mouse) without hardware

I have not had time to spend on any research lately, but I did want to pass along this link:

https://courses.cit.cornell.edu/ee476/FinalProjects/s2007/blh36_cdl28_dct23/blh36_cdl28_dct23/

These folks have a software-only implementation of USB host for the AVR chip like the Arduino uses. It allows, with software only and some wires to a USB connector, the device to read a basic USB device like a mouse or keyboard.

The only thing I noticed was it requires a 16Mhz crystal, instead of the stock 15mhz. They did it by building their own system, but someone wanting to do it on an Arduino would need to desolder and replace the crystal.

However, there are some pretty simple plans to “build a $5 Arduino” with minimal parts — so perhaps it would be very cheap to breadboard something that supports this.

2N2222 transistor from Radio Shack links Arduino UNO and Teensy 2.0

This screen shot may not seem like much, but it marks the first time I have ever used a transistor:

Reading Transistor Switch

My Tandy joystick project has two goals. The first is to allow an analog Tandy joystick be used as a USB device for a modern computer. This part is easy, since the variable resistors in the joysticks can be easily read from the Arduino/Teensy’s analog inputs, and the joystick buttons can be read on a digital pin.

The second part is a bit trickier. The idea is to use a modern input device on an old Tandy 1000 or Color Computer. To do this, the USB device’s analog position (if it is an analog stick or a mouse) has to be turned in to a resistance value that the old computer can read through it’s joystick ports. I found a tutorial on the Arduino site that shows doing this with an AD5206 digital potentiometer chip:

http://arduino.cc/en/Tutorial/SPIDigitalPot

That seems easy enough, but I wasn’t sure how to make a digital output pin from the Teensy turn in to a dry contact switch. A bit of research led me to a few useful tutorials dealing with transistors and optoisolators:

…and I found various projects where they were using these devices to simulate a button press on a camera, to automate it:

http://www.zipfelmaus.com/blog/hack-a-canon-camera-and-controll-it-with-an-arduino/

The above project is precisely the type of thing I want to do, so I wired up an Arduino like his layout, and then instead of running wires to the switch of a Canon camera, I ran them to a digital input of my Teensy 2.0 and its ground. Using the Fritzing program, here is a diagram of what I did:

Hooking digital outputs to digital inputs.
Hooking digital outputs to digital inputs. (Diagram done with Fritzing)

I hacked a bit of code on the Arduino side that just toggles that digital pin HIGH and LOW (basically, I hacked the LED blink example to also do pin 12):

int led = 13;
int transistor = 12;

// the setup routine runs once when you press reset:
void setup()
{
    // initialize the digital pin as an output.
    pinMode(led, OUTPUT);
    pinMode(transistor, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop()
{
    digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
    digitalWrite(transistor, HIGH);
    delay(1000);            // wait for a second
    digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
    digitalWrite(transistor, LOW);
    delay(1000); // wait for a second
}

Then, on the Teensy, I wired up to the transistor similar to the Input Pullup example (which reads a button) and reports if the status of the pin had changed. Here’s some quick and dirty code:

void setup()
{
    // Initialize the serial port.
    Serial.begin(9600);

    pinMode(3, INPUT_PULLUP);

    pinMode(LED_PIN, OUTPUT);

    Serial.println("Ready.");
}

int oldStatus;
int status;

void loop()
{

    status = digitalRead(3);
    if (status != oldStatus)
    {
        Serial.print("Pin 3: ");
        Serial.println(digitalRead(3));
        oldStatus = status;
        digitalWrite(LED_PIN, status);
    }
}

Now, with both devices running, on the Arduino, every time the LED turns on, it is also turning on the pin connected to the transistor. On the Teensy, that pin connects to digital input 3. (I started at 3 since that is just passed the three pins used by SPI, which I will be needing for my real project.)

When the light on the Arduino goes on, the light on the Teensy goes off. I would need to change how I wired them (active high versus active low) to make them sync. But, it works.

I have much learning to do on all this, but this proof-of-concept gets me one step closer to what I am trying to do. I will be next be trying the same thing using optoisolator chips, though I am not sure there really needs to be any isolation between the joystick buttons and the Teensy.

More to come…

Fritzing circuit designer…

I was looking at an Arduino tutorial and noticed they had a rather nice computer diagram of an Arduino, showing how to connect all the wires up for the project

I decided to check out the link for the Fritzing program that was used to make the diagram. This freely downloadable software for PC, Mac and Linux is pretty neat. You can diagram out an Arduino breadboard layout, and then turn that in to a real schematic and even PCB layout. The layout can then be exported and sent off to have boards made. I have never done anything like this before, but I am having fun experimenting…

The following picture shows something I was playing with. It doesn’t work — it was just me experimenting with some objects. But, I think this program might end up being pretty fun to play with. Check it out.

Playing with Fritzing...

(Above was some initial concept work done on “yet another” joystick converter. This one will be designed to hook a modern USB joystick or mouse up to an old Tandy 1000 or Color Computer. I also plan to allow it to do the same thing with a PC keyboard to the CoCo. For emulation fans, the reverse is also planned, allowing an analog TRS-80 joystick to be hooked up to a modern computer via USB, and the same thing with a CoCo keyboard. This is going to take a bit more work than what I have done so far, but it seems pretty straight forward, once I figure out how to use switching transistors…)