Guest User

ALL RAM BBS

a guest
Jun 25th, 2013
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 45.30 KB | None | 0 0
  1. // BOF preprocessor bug prevent - insert me on top of your arduino-code
  2. #if 1
  3. __asm volatile ("nop");
  4. #endif
  5. /*-----------------------------------------------------------------------------
  6.  
  7.  *ALL RAM* BBS for Arduino
  8.  by Allen C. Huffman ([email protected] / www.appleause.com)
  9.  
  10.  This is an experiment to see how easy it is to translate Microsoft BASIC to
  11.  Arduino C. The translated C code is incredibly ugly and will be disturbing
  12.  to most modern programmers. It was done just to see if it could be done, and
  13.  what better thing to translate than a program that was also originally done
  14.  just to see if it could be done -- a cassette based BBS package from 1983!
  15.  
  16.  About the *ALL RAM* BBS:
  17.  
  18.  The *ALL RAM* BBS System was writtin in 1983 for the Radio Shack TRS-80
  19.  Color Computer ("CoCo"). At this time, existing BBS packages for the CoCo
  20.  required "2-4 disk drives" to operate, so *ALL RAM* was created to prove
  21.  a BBS could run from a cassette-based computer. Instead of using floppy
  22.  disks to store the userlog and message base, they were contained entirely
  23.  in RAM. The in-memory databases could be saved to cassette tape and
  24.  reloaded later.
  25.  
  26.  The original BASIC source code is included in comments, followed by a very
  27.  literal line-by-line translation to Arduino C. Remember, it's not a rewrite
  28.  in C -- it's BASIC code done in C.
  29.  
  30.  Be warned. There be "gotos" ahead!
  31.  
  32.  References about memory saving:
  33.  
  34. http://www.controllerprojects.com/2011/05/23/saving-ram-space-on-arduino-when-you-do-a-serial-printstring-in-quotes/
  35.  
  36.  
  37. http://www.adafruit.com/blog/2008/04/17/free-up-some-arduino-sram/
  38.  
  39.  2013-04-02 0.0 allenh - Initial version with working userlog.
  40.  2013-04-03 1.0 allenh - Message base working. Core system fully functional.
  41.  Preliminary support for Arduino Ethernet added.
  42.  2013-04-04 1.1 allenh - SD card support for loading/saving.
  43.  2013-04-05 1.2 allenh - Ethernet (telnet) support.
  44.  2013-04-06 1.3 allenh - Cleanup for posting to www.appleause.com
  45.  2013-04-09 1.4 allenh - Fixed a bug with INPUT_SIZE and input.
  46.  2013-04-12 1.5 allenh - Integration with new Telnet server code, misc fixes.
  47.  -----------------------------------------------------------------------------*/
  48. #include <avr/pgmspace.h>
  49.  
  50. #define VERSION "1.5"
  51.  
  52. // To enable SD card support, define the PIN used by SD.begin(x)
  53. //#define SD_PIN        4
  54.  
  55. // To enable Ethernet support, defined the PORT used by EthernetServer(PORT)
  56. //#define ENET_PORT     23
  57.  
  58. // NOTE: On smaller Arduinos (UNO), there is not enough Flash or RAM to have
  59. // both SD and Ethernet support at the same time.
  60.  
  61. #if defined(SD_PIN)
  62. #include
  63. #endif
  64.  
  65. #if defined(ENET_PORT)
  66. #include
  67. #include
  68.  
  69. extern EthernetClient telnetClient;
  70.  
  71. // Prototypes...
  72. byte telnetRead(EthernetClient client);
  73. byte telnetInput(EthernetClient client, char *cmdLine, byte len);
  74. #endif
  75.  
  76. // And so it begins.
  77. void setup()
  78. {
  79.   Serial.begin(9600);
  80.  
  81.   while(!Serial);
  82.  
  83.   showHeader();
  84.  
  85. #if defined(ENET_PORT)
  86.   telnetInit();
  87. #endif
  88.  
  89.   showConfig();
  90.  
  91.   // Scroll off any leftover console output.
  92.   //for(int i=0; i<24; i++) print(); } // We don't really have anything loop worthy for this program, so we'll // just have some fun and pretend like we are starting up the CoCo each // time through. void loop() {   showCoCoHeader(); // Just for fun...   allram();   print();   print(F("BREAK IN 520"));   print(F("OK"));   delay(5000); } // Show program header. void showHeader() {   // Emit some startup stuff to the serial port.   print(F("\n"     "*ALL RAM* BBS for Arduino "VERSION" - 30 year anniversary edition!\n"     "Copyright (C) 1983 by Allen C. Huffman\n"     "Ported from TRS-80 Color Computer Extended Color BASIC.\n"     "Build Date: "__DATE__" "__TIME__"\n")); } /*---------------------------------------------------------------------------*/ // 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   12   // 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) // "From\To\Subj" // 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    3   // 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)
  93. #error INPUT_SIZE too small to hold "From\To\Sub".
  94. #endif
  95.  
  96. /*---------------------------------------------------------------------------*/
  97.  
  98. //0 REM *ALL RAM* BBS System 1.0
  99. //1 REM   Shareware / (C) 1983
  100. //2 REM     By Allen Huffman
  101. //3 REM  110 Champions Dr, #811
  102. //4 REM     Lufkin, TX 75901
  103. //5 CLS:FORA=0TO8:READA$:POKE1024+A,VAL("&H"+A$):NEXTA:EXEC1024:DATAC6,1,96,BC,1F,2,7E,96,A3
  104. //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
  105.  
  106. // All variables in BASIC are global, so we are declaring them outside the
  107. // functions to make them global in C as well. Arrays in BASIC are "0 to X",
  108. // and in C they are "0 to X-1", so we add one to them in C to get the same
  109. // number of elements.
  110. char nmArray[MAX_USERS+1][ULOG_SIZE];             // NM$(200)
  111. char msArray[MAX_MSGS+1][MAX_LINE+1][INPUT_SIZE+1];// MS$(19,10) 1.4
  112. char aStr[INPUT_SIZE+1];                          // A$ 1.4
  113. char fStr[FR_SIZE];                               // F$ - From
  114. char sStr[SB_SIZE];                               // S$ - Subj
  115. char tStr[TO_SIZE];                               // T$ - To
  116. char nmStr[NAME_SIZE];                            // NM$ - Name
  117. char psStr[PSWD_SIZE];                            // PS$ - Pswd
  118. char pwStr[PSWD_SIZE];                            // PW$ - Pswd
  119.  
  120. // To save RAM, these two strings will exist in Flash memory. It will
  121. // require a bit of work later to use them (__FlashStringHelper*).
  122. prog_char brStr[] PROGMEM = "*==============*==============*"; // BR$ - border
  123. prog_char clStr[] PROGMEM = "\x0c\x0e";                        // CL$ - clear
  124.  
  125. int a, b, c, cl, ln, lv, ms, nm, ky, uc;
  126. // A, B, C - misc.
  127. // CL - Calls
  128. // LN - Line Number
  129. // LV - Level
  130. // MS - Messages
  131. // NM - Names (users)
  132. // KY - Keys (commands entered)
  133. // UC - Uppercase input (1=Yes, 0=No)
  134.  
  135. void allram()
  136. {
  137.   // HACK - create adefault Sysop account.
  138.   nm = 0;
  139.   strncpy_P(nmArray[0], PSTR("SYSOP\\TEST9"), ULOG_SIZE);
  140.  
  141.   cls(); // From line 5
  142.  
  143.   //15 CL$=CHR$(12)+CHR$(14):BR$="*==============*==============*":GOSUB555
  144.   //char cl[] = "\0xC\0xE";
  145.   //char br[] = "*==============*==============*";
  146.   gosub555();
  147.  
  148. //20 CLS:PRINTTAB(6)"*ALL RAM* BBS SYSTEM":PRINT"USERS:"NM,"CALLS:"CL:PRINTTAB(5)"SYSTEM AWAITING CALLER";:go:SOUND200,10
  149. line20:
  150. nmStr[0] = 0; // reset user.
  151.   cls();
  152.   printTab(6);
  153.   print(F("*ALL RAM* BBS SYSTEM"));
  154.   printSemi(F("USERS:"));
  155.   printSemi(nm);
  156.   printComma();
  157.   printSemi(F("CALLS:"));
  158.   print(cl);
  159.   printTab(5);
  160.   printSemi(F("SYSTEM AWAITING CALLER"));
  161.   //gosub1005();
  162.   if (gosub1005()==255) goto line20;
  163.   sound(200,10);
  164.  
  165.   //25 A$="Welcome To *ALL RAM* BBS!":GOSUB1055:KY=0:CL=CL+1
  166.   strncpy_P(aStr, PSTR("Welcome To *ALL RAM* BBS!"), INPUT_SIZE);
  167.   gosub1055();
  168.   ky = 0;
  169.   cl = cl + 1;
  170.  
  171.   showLoginMessage();
  172.  
  173.   //30 PRINT:PRINT"Password or 'NEW' :";:UC=1:GOSUB1005:PS$=A$:IFA$=""ORA$="NEW"THEN55ELSEPRINT"Checking: ";:A=0
  174. line30:
  175.   print();
  176.   printSemi(F("Password or 'NEW' :"));
  177.   uc = 1;
  178.   //gosub1005();
  179.   if (gosub1005()==255) goto line20;
  180.   strncpy(psStr, aStr, PSWD_SIZE);
  181.   if (aStr[0]=='\0' || strcmp(aStr, "NEW")==0)
  182.   {
  183.     goto line55;
  184.   }
  185.   else
  186.   {
  187.     printSemi(F("Checking: "));
  188.     a = 0;
  189.   }
  190.  
  191. line35:
  192.   //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
  193.   strncpy(aStr, nmArray[a], ULOG_SIZE);
  194.   b = instr(aStr, "\\");
  195.   strncpy(nmStr, aStr, b-1);
  196.   nmStr[b-1] = '\0';
  197.   strncpy(pwStr, &aStr[b], strlen(aStr)-b-1);
  198.   pwStr[strlen(aStr)-b-1] = '\0';
  199.   lv = atoi(&aStr[strlen(aStr)-1]);
  200.   if (strncmp(pwStr, psStr, PSWD_SIZE)==0)
  201.   {
  202.     goto line45;
  203.   }
  204.   else
  205.   {
  206.     a = a + 1;
  207.     if (a<=nm) goto line35;
  208.   }
  209.  
  210. line40: // for empty userlog bug
  211.   //40 PRINT"*INVALID*":KY=KY+1:IFKY<3THEN30ELSE215
  212.   print(F("*INVALID*"));
  213.   ky = ky + 1;
  214.   if (ky<3) goto line30;   goto line215; line45:   //45 PRINT"*ACCEPTED*":PRINTBR$:PRINT"On-Line: "NM$:PRINT"Access :"LV:PRINT"Caller :"CL:KY=0:GOTO115   print(F("*ACCEPTED*"));   print((__FlashStringHelper*)brStr);   printSemi(F("On-Line: "));   print(nmStr);   printSemi(F("Access :"));   print(lv);   printSemi(F("Caller :"));   print(cl);   ky = 0;   goto line115;   //50 'New User line55:   //55 A$="Password Application Form":GOSUB1055   strncpy_P(aStr, PSTR("Password Application Form"), INPUT_SIZE);   gosub1055();   //60 IFNM=200THENPRINT"Sorry, the userlog is full now.":GOTO215ELSEPRINT"Name=20 chars, Password=8 chars"   if (nm==MAX_USERS)   {     print(F("Sorry, the userlog is full now."));     goto line215;   }   else   {     printSemi(F("Name="));     printNumSemi(NAME_SIZE);     printSemi(F(" chars, Password="));     printNumSemi(PSWD_SIZE);     printSemi(F(" chars"));   } line65:   //65 PRINT:PRINT"Full Name :";:UC=1:GOSUB1005:NM$=A$:IFA$=""ORLEN(A$)>20THEN30
  215.   print();
  216.   printSemi(F("Full Name :"));
  217.   uc = 1;
  218.   //gosub1005();
  219.   if (gosub1005()==255) goto line20;
  220.   strncpy(nmStr, aStr, NAME_SIZE);
  221.   if (aStr[0]=='\0' || strlen(aStr)>20) goto line30;
  222.  
  223.   //70 PRINT"Password  :";:UC=1:GOSUB1005:PW$=A$:IFA$=""ORLEN(A$)>8THEN30
  224.   printSemi(F("Password  :"));
  225.   uc = 1;
  226.   //gosub1005();
  227.   if (gosub1005()==255) goto line20;
  228.   strncpy(pwStr, aStr, PSWD_SIZE);
  229.   if (aStr[0]=='\0' || strlen(aStr)>8) goto line30;
  230.  
  231.   //75 PRINT:PRINT"Name :"NM$:PRINT"Pswd :"PW$:PRINT"Is this correct? ";:UC=1:GOSUB1005:IFLEFT$(A$,1)="Y"THEN80ELSE65
  232.   print();
  233.   printSemi(F("Name :"));
  234.   print(nmStr);
  235.   printSemi(F("Pswd :"));
  236.   print(pwStr);
  237.   printSemi(F("Is this correct? "));
  238.   uc = 1;
  239.   //gosub1005();
  240.   if (gosub1005()==255) goto line20;
  241.   if (aStr[0]=='Y')
  242.   {
  243.     goto line80;
  244.   }
  245.   else
  246.   {
  247.     goto line65;
  248.   }
  249.  
  250. line80:
  251.   //80 NM=NM+1:NM$(NM)=NM$+"\"+PW$+"0":LV=0:KY=0
  252.   nm = nm + 1;
  253.   strncpy(nmArray[nm], nmStr, NAME_SIZE);
  254.   strcat_P(nmArray[nm], PSTR("\\"));
  255.   strncat(nmArray[nm], pwStr, PSWD_SIZE);
  256.   //strcat_P(nmArray[nm], PSTR("0"));
  257.   strcat_P(nmArray[nm], PSTR("1")); // AUTO VALIDATED
  258.   //lv = 0;
  259.   lv = 1;
  260.   ky = 0;
  261.   //85 PRINT"Your password will be validated as soon as time permits.  Press":PRINT"[ENTER] to continue :";:GOSUB1005
  262.   print(F("Your password will be validated as soon as time permits.  Press"));
  263.   printSemi(F("[ENTER] to continue :"));
  264.   //gosub1005();
  265.   if (gosub1005()==255) goto line20;
  266.  
  267.   //100 'Main Menu
  268. line105:
  269.   //105 A$="*ALL RAM* BBS Master Menu":GOSUB1055
  270.   strncpy_P(aStr, PSTR("*ALL RAM* BBS Master Menu"), INPUT_SIZE);
  271.   gosub1055();
  272.  
  273.   //110 PRINT"C-all Sysop","P-ost Msg":PRINT"G-oodbye","R-ead Msg":PRINT"U-serlog","S-can Titles"
  274.   printSemi(F("C-all Sysop"));
  275.   printComma();
  276.   print(F("P-ost Msg"));
  277.   printSemi(F("G-oodbye"));
  278.   printComma();
  279.   print(F("R-ead Msg"));
  280.   printSemi(F("U-serlog"));
  281.   printComma();
  282.   print(F("S-can Titles"));
  283.  
  284. line115:
  285.   //115 PRINTBR$
  286.   print((__FlashStringHelper*)brStr);
  287.  
  288. line120:
  289.   //120 KY=KY+1:IFKY>200THENPRINT"Sorry, your time on-line is up.":GOTO210ELSEIFKY>180THENPRINT"Please complete your call soon."
  290.   ky = ky + 1;
  291.   if (ky>200)
  292.   {
  293.     print(F("Sorry, your time on-line is up."));
  294.     goto line210;
  295.   }
  296.   else if (ky>180)
  297.   {
  298.     print(F("Please complete your call soon."));
  299.   }
  300.  
  301. line125:
  302.   showFreeRam();
  303.   //125 PRINTTAB(7)"?=Menu/Command :";:UC=1:GOSUB1005:A$=LEFT$(A$,1)
  304.   printTab(7);
  305.   printSemi(F("?=Menu/Command :"));
  306.   uc = 1;
  307.   //gosub1005();
  308.   if (gosub1005()==255) goto line20;
  309.   aStr[1] = '\0';
  310.  
  311. line130:
  312.   //130 LN=INSTR("?CGRSPU%",A$):IFLN=0THENPRINT"*Invalid Command*":GOTO120
  313.   ln = instr("?CGRSPU%", aStr);
  314.   if (ln==0)
  315.   {
  316.     print(F("*Invalid Command*"));
  317.     goto line120;
  318.   }
  319.  
  320.   //135 IFLV5THENPRINT" Sorry, you are not validated.":GOTO125
  321.   if (lv5)
  322.   {
  323.     print(F(" Sorry, you are not validated."));
  324.     goto line125;
  325.   }
  326.  
  327.   //140 ONLN GOTO105,155,205,405,455,305,255,505
  328.   if (ln==1) goto line105;
  329.   if (ln==2) goto line155;
  330.   if (ln==3) goto line205;
  331.   if (ln==4) goto line405;
  332.   if (ln==5) goto line455;
  333.   if (ln==6) goto line305;
  334.   if (ln==7) goto line255;
  335.   if (ln==8) goto line505;
  336.  
  337.   //150 'Call Sysop
  338. line155:
  339.   //155 A$="Calling the Sysop":GOSUB1055:A=0
  340.   strncpy_P(aStr, PSTR("Calling the Sysop"), INPUT_SIZE);
  341.   gosub1055();
  342.   a = 0;
  343.  
  344.   //165 PRINT" BEEP!";:SOUND150,5:IFINKEY$=CHR$(12)THEN175ELSEprintING$(5,8);:A=A+1:IFA<25THEN165
  345. line165:
  346.   printSemi(F(" BEEP!"));
  347.   sound(150, 5);
  348.   if (inkey()==12)
  349.   {
  350.     goto line175;
  351.   }
  352.   else
  353.   {
  354.     string(5, 8);
  355.     a = a + 1;
  356.     if (a<25) goto line165;
  357.   }
  358.  
  359.   //170 PRINT:PRINT" The Sysop is unavaliable now.":GOTO115
  360.   print(F(""));
  361.   print(F(" The Sysop is unavaliable now."));
  362.   goto line115;
  363.  
  364. line175:
  365.   //175 PRINT:PRINTTAB(6)"*Chat mode engaged*"
  366.   print();
  367.   printTab(6);
  368.   print(F("*Chat mode engaged*"));
  369.  
  370. line180:
  371.   //180 GOSUB1005:IFLEFT$(A$,3)="BYE"THEN185ELSE180
  372.   //gosub1005();
  373.   if (gosub1005()==255) goto line20;
  374.   if (strncmp(aStr, "BYE", 3)==0)
  375.   {
  376.     goto line185;
  377.   }
  378.   else
  379.   {
  380.     goto line180;
  381.   }
  382.  
  383. line185:
  384.   //185 PRINTTAB(5)"*Chat mode terminated*":GOTO115
  385.   goto line115;
  386.  
  387.   //200 'Goodbye
  388. line205:
  389.   //205 A$="Thank you for calling":GOSUB1055
  390.   strncpy_P(aStr, PSTR("Thank you for calling"), INPUT_SIZE);
  391.   gosub1055();
  392.  
  393. line210:
  394.   //210 PRINT:PRINT"Goodbye, "NM$"!":PRINT:PRINT"Please call again."
  395.   print(F(""));
  396.   printSemi(F("Goodbye, "));
  397.   printSemi(nmStr);
  398.   print(F("!"));
  399.   print();
  400.   print(F("Please call again."));
  401.  
  402. line215:
  403.   //215 PRINT:PRINT:PRINT"*ALL RAM* BBS disconnecting..."
  404.   print();
  405.   print();
  406.   print(F("*ALL RAM* BBS disconnecting..."));
  407.  
  408.   //220 FORA=1TO1000:NEXTA
  409.   delay(1000);
  410.  
  411. #if defined(ENET_PORT)
  412.   telnetDisconnect();
  413. #endif
  414.  
  415.   //225 GOTO20
  416.   goto line20;
  417.  
  418.   //250 'Userlog
  419. line255:
  420.   //255 A$="List of Users":GOSUB1055:PRINT"Users on system:"NM:IFNM=0THEN115ELSEA=1
  421.   strncpy_P(aStr, PSTR("List of Users"), INPUT_SIZE);
  422.   gosub1055();
  423.   printSemi(F("Users on system:"));
  424.   print(nm);
  425.   if (nm==0)
  426.   {
  427.     goto line115;
  428.   }
  429.   else
  430.   {
  431.     a = 1;
  432.   }
  433. line260:
  434.   //260 A$=NM$(A):PRINTLEFT$(A$,INSTR(A$,"\")-1)TAB(29)RIGHT$(A$,1)
  435.   strncpy(aStr, nmArray[a], INPUT_SIZE);
  436.   {
  437.     char tempStr[NAME_SIZE+1];         // Add room for NULL.
  438.     strncpy(tempStr, aStr, NAME_SIZE+1);
  439.     tempStr[instr(tempStr, "\\")-1] = '\0';
  440.     printSemi(tempStr);
  441.   }
  442.   printTab(29);
  443.   print(right(aStr,1));
  444.  
  445.   //265 IF(A/10)=INT(A/10)THENPRINT"C-ontinue or S-top :";:UC=1:GOSUB1005:IFLEFT$(A$,1)="S"THEN275
  446.   if (a % 10 == 9)
  447.   {
  448.     printSemi(F("C-ontinue or S-top :"));
  449.     uc = 1;
  450.     //gosub1005();
  451.     if (gosub1005()==255) goto line20;
  452.     if (aStr[0]=='S') goto line275;
  453.   }
  454.  
  455.   //270 A=A+1:IFA<=NM THEN260
  456.   a = a + 1;
  457.   if (a<=nm) goto line260;
  458.  
  459. line275:
  460.   //275 PRINT"*End of Userlog*":GOTO115
  461.   print(F("*End of Userlog*"));
  462.   goto line115;
  463.  
  464.   //300 'Post Msg
  465. line305:
  466.   //305 IFMS=20THENPRINT"One moment, making room...":FORA=0TO18:FORB=0TO10:MS$(A,B)=MS$(A+1,B):NEXTB:NEXTA:MS=19
  467.   if (ms==MAX_MSGS+1)
  468.   {
  469.     print(F("One moment, making room..."));
  470.     for(a=0; a<=MAX_MSGS-1; a++)
  471.     {
  472.       for(b=0; b<=MAX_LINE; b++)
  473.       {
  474.         strncpy(msArray[a][b], msArray[a+1][b], INPUT_SIZE);
  475.       }
  476.     }
  477.     ms = MAX_MSGS;
  478.   }
  479.   //310 CLS:PRINTCL$"This will be message #"MS+1:FORA=0TO10:MS$(MS,A)="":NEXTA:F$=NM$
  480.   cls();
  481.   print((__FlashStringHelper*)clStr);
  482.   printSemi(F("This will be message #"));
  483.   print(ms+1);
  484.   for (a=0; a<=MAX_LINE; a++)   {     msArray[ms][a][0] = '\0';   }   strncpy(fStr, nmStr, NAME_SIZE);   //315 PRINT"From :"F$:PRINT"To   :";:UC=1:GOSUB1005:A$=LEFT$(A$,20):T$=A$:IFA$=""THEN115   printSemi(F("From :"));   print(fStr);   printSemi(F("To   :"));   uc = 1;   //gosub1005();   if (gosub1005()==255) goto line20;   aStr[NAME_SIZE] = '\0';   strncpy(tStr, aStr, TO_SIZE);   if (aStr[0]=='\0') goto line115;   //320 PRINT"Is this message private? ";:UC=1:GOSUB1005:IFLEFT$(A$,1)="Y"THENS$="*E-Mail*":GOTO330   printSemi(F("Is this message private? "));   uc = 1;   //gosub1005();   if (gosub1005()==255) goto line20;   if (aStr[0]=='Y')   {     strncpy_P(sStr, PSTR("*E-Mail*"), SB_SIZE);     goto line330;   }   //325 PRINT"Subj :";:UC=1:GOSUB1005:A$=LEFT$(A$,18):S$=A$:IFA$=""THEN115   printSemi(F("Subj :"));   uc = 1;   //gosub1005();   if (gosub1005()==255) goto line20;   aStr[SB_SIZE-1] = '\0';   strncpy(sStr, aStr, SB_SIZE);   if (aStr[0]=='\0') goto line115; line330:   //330 PRINT"Enter up to 10 lines, 64 chars. [ENTER] on a blank line to end.":A=0   printSemi(F("Enter up to"));   printSemi(MAX_LINE);   printSemi(F("lines,"));   printSemi(INPUT_SIZE);   print(F("chars. [ENTER] on a blank line to end."));   a = 0; line335:   //335 A=A+1:PRINTUSING"##>";A;:GOSUB1005:MS$(MS,A)=A$:IFA$=""THENA=A-1:GOTO345ELSEIFA", a);
  485.   //gosub1005();
  486.   if (gosub1005()==255) goto line20;
  487.   strncpy(msArray[ms][a], aStr, INPUT_SIZE);
  488.   if (aStr[0]=='\0')
  489.   {
  490.     a = a - 1;
  491.     goto line345;
  492.   }
  493.   else if (a<MAX_LINE) goto line335;
  494.  
  495. line340:
  496.   //340 PRINT"*Message Buffer Full*"
  497.   print(F("*Message Buffer Full*"));
  498.  
  499. line345:
  500.   //345 PRINT"A-bort, C-ont, E-dit, L-ist, or S-ave Message? ";:UC=1:GOSUB1005:A$=LEFT$(A$,1):IFA$=""THEN345
  501.   printSemi(F("A-bort, C-ont, E-dit, L-ist, or S-ave Message? "));
  502.   uc = 1;
  503.   //gosub1005();
  504.   if (gosub1005()==255) goto line20;
  505.   aStr[1] = '\0';
  506.   if (aStr[0]=='\0') goto line345;
  507.  
  508.   //350 LN=INSTR("ACELS",A$):IFLN=0THEN345ELSEONLN GOTO385,355,360,375,380
  509.   ln = instr("ACELS", aStr);
  510.   if (ln==0) goto line345;
  511.   if (ln==1) goto line385;
  512.   if (ln==2) goto line355;
  513.   if (ln==3) goto line360;
  514.   if (ln==4) goto line375;
  515.   if (ln==5) goto line380;
  516.  
  517. line355:
  518.   //355 IFA<10THENPRINT"Continue your message:":GOTO335ELSE340
  519.   if (a<MAX_LINE)
  520.   {
  521.     printSemi(F("Continue your message:"));
  522.     goto line335;
  523.   }
  524.   else goto line340;
  525.  
  526. line360:
  527.   //360 PRINT"Edit line 1 -"A":";:GOSUB1005:LN=VAL(LEFT$(A$,2)):IFLNA THEN345
  528.   printSemi(F("Edit line 1 -"));
  529.   printSemi(a);
  530.   printSemi(F(":"));
  531.   //gosub1005();
  532.   if (gosub1005()==255) goto line20;
  533.   aStr[2] = '\0';
  534.   ln = atoi(aStr);
  535.   if (lna) goto line345;
  536.  
  537.   //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*"
  538.   print(F("Line currently reads:"));
  539.   print(msArray[ms][ln]);
  540.   print(F("Enter new line:"));
  541.   //gosub1005();
  542.   if (gosub1005()==255) goto line20;
  543.   aStr[INPUT_SIZE] = '\0'; // 1.4
  544.   if (aStr[0]=='\0')
  545.   {
  546.     print(F("*Unchanged*"));
  547.   }
  548.   else
  549.   {
  550.     strncpy(msArray[ms][ln], aStr, INPUT_SIZE);
  551.     print(F("*Corrected*"));
  552.   }
  553.  
  554.   //370 GOTO360
  555.   goto line360;
  556.  
  557. line375:
  558.   //375 CLS:PRINTCL$"Message Reads:":FORB=1TOA:PRINTUSING"##>";B;:PRINTMS$(MS,B):NEXTB:GOTO345
  559.   cls();
  560.   print((__FlashStringHelper*)clStr);
  561.   print(F("Message Reads:"));
  562.   for (b=1; b<=a; b++)   {     printUsingSemi("##>", b);
  563.     print(msArray[ms][b]);
  564.   }
  565.   goto line345;
  566.  
  567. line380:
  568.   //380 MS$(MS,0)=T$+"\"+F$+"\"+S$:MS=MS+1:PRINT"*Message"MS"stored*":GOTO115
  569.   strcpy(msArray[ms][0], tStr);
  570.   strcat_P(msArray[ms][0], PSTR("\\"));
  571.   strcat(msArray[ms][0], fStr);
  572.   strcat_P(msArray[ms][0], PSTR("\\"));
  573.   strcat(msArray[ms][0], sStr);
  574.   ms = ms + 1;
  575.   printSemi(F("*Message"));
  576.   printSemi(ms);
  577.   print(F("stored*"));
  578.   goto line115;
  579.  
  580. line385:
  581.   //385 PRINT"*Message Aborted*":GOTO115
  582.   print(F("*Message Aborted*"));
  583.   goto line115;
  584.  
  585.   //400 'Read Msg
  586. line405:
  587.   //405 IFMS=0THENPRINT"The message base is empty.":GOTO115
  588.   if (ms==0)
  589.   {
  590.     print(F("The message base is empty."));
  591.     goto line115;
  592.   }
  593.  
  594.   //410 CLS:PRINTCL$
  595.   cls();
  596.   print((__FlashStringHelper*)clStr);
  597.  
  598. line415:
  599.   //415 PRINT"Read Message 1 -"MS":";:GOSUB1005:A=VAL(LEFT$(A$,2)):IFAMS THEN115
  600.   printSemi(F("Read Message 1 -"));
  601.   printSemi(ms);
  602.   printSemi(F(":"));
  603.   //gosub1005();
  604.   if (gosub1005()==255) goto line20;
  605.   aStr[2] = '\0';
  606.   a = atoi(aStr);
  607.   if (ams) goto line115;
  608.  
  609.   //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)
  610.   strncpy(aStr, msArray[a-1][0], INPUT_SIZE);
  611.   b = instr(aStr, "\\");
  612.   c = instr(b+1, aStr, "\\");
  613.   strncpy(tStr, aStr, b-1);
  614.   tStr[b-1] = '\0';
  615.   strncpy(fStr, (aStr-1)+b+1, c-b-1);
  616.   fStr[ c-b-1] = '\0'; // FIXTHIS - max copy sizes here?
  617.   strncpy(sStr, right(aStr, strlen(aStr)-c), SB_SIZE);
  618.  
  619.   //425 IFS$="*E-Mail*"ANDLV<8THENIFNM$<>T$ANDNM$<>F$THENPRINT"That message is private.":GOTO415
  620.   if (strcmp(sStr, "*E-Mail*")==0 && lv<8)
  621.   {
  622.     if (strcmp(nmStr, tStr)!=0 && strcmp(nmStr, fStr)!=0)
  623.     {
  624.       print(F("That message is private."));
  625.       goto line415;
  626.     }
  627.   }
  628.  
  629.   //430 CLS:PRINTCL$"Message #"A:PRINT"From :"F$:PRINT"To   :"T$:PRINT"Subj :"S$:PRINT:B=0
  630.   cls();
  631.   print((__FlashStringHelper*)clStr);
  632.   printSemi(F("Message #"));
  633.   print(a);
  634.   printSemi(F("From :"));
  635.   print(fStr);
  636.   printSemi(F("To   :"));
  637.   print(tStr);
  638.   printSemi(F("Subj :"));
  639.   print(sStr);
  640.   print();
  641.   b = 0;
  642.  
  643. line435:
  644.   //435 B=B+1:PRINTMS$(A-1,B):IFMS$(A-1,B)=""THEN440ELSEIFB<10THEN435
  645.   b = b + 1;
  646.   print(msArray[a-1][b]);
  647.   if (msArray[a-1][b][0]=='\0')
  648.   {
  649.     goto line440;
  650.   }
  651.   else if (b<MAX_LINE) goto line435;
  652.  
  653. line440:
  654.   //440 PRINT"*End of Message*":GOTO415
  655.   print(F("*End of Message*"));
  656.   goto line415;
  657.  
  658.   //450 'Scan Titles
  659. line455:
  660.   //455 IFMS=0THENPRINT"The message base is empty.":GOTO115
  661.   if (ms==0)
  662.   {
  663.     print(F("The message base is empty."));
  664.     goto line115;
  665.   }
  666.  
  667.   //460 CLS:PRINTCL$"Message Titles:":A=0
  668.   cls();
  669.   print((__FlashStringHelper*)clStr);
  670.   print(F("Message Titles:"));
  671.   a = 0;
  672.  
  673. line465:
  674.   //465 A$=MS$(A,0):PRINTUSING"[##] SB: ";A+1;:B=INSTR(A$,"\"):C=INSTR(B+1,A$,"\"):PRINTRIGHT$(A$,LEN(A$)-C):PRINTTAB(5)"TO: "LEFT$(A$,B-1):A=A+1:IFA  strncpy(aStr, msArray[a][0], INPUT_SIZE);
  675.   printUsingSemi("[##] SB: ", a+1);
  676.   b = instr(aStr, "\\");
  677.   c = instr(b+1, aStr, "\\");
  678.   print(right(aStr, strlen(aStr)-c));
  679.   printTab(5);
  680.   printSemi(F("TO: "));
  681.   {
  682.     char tempStr[TO_SIZE];
  683.     strncpy(tempStr, aStr, b-1);
  684.     tempStr[b-1] = '\0';
  685.     print(tempStr);
  686.   }
  687.   a = a + 1;
  688.   if (a<ms) goto line465;
  689.  
  690.   //470 PRINT"*End of Messages*":GOTO115
  691.   print(F("*End of Messages*"));
  692.   goto line115;
  693.  
  694.   //500 '%SYSOP MENU%
  695. line505:
  696.   //505 IFLV<9THENA$="Z":GOTO130
  697.   if (lv<9)
  698.   {
  699.     strncpy_P(aStr, PSTR("Z"), INPUT_SIZE);
  700.     goto line130;
  701.   }
  702.  
  703.   //510 PRINT"PASSWORD?";:GOSUB1005:IFA$<>"?DROWSSAP"THENPRINT"Thank You!":GOTO115
  704.   printSemi(F("PASSWORD?"));
  705.   //gosub1005();
  706.   if (gosub1005()==255) goto line20;
  707.   if (strcmp(aStr, "?DROWSSAP")!=0)
  708.   {
  709.     print(F("Thank You!"));
  710.     goto line115;
  711.   }
  712.  
  713.   //515 PRINT"Abort BBS? YES or NO? ";:UC=1:GOSUB1005:IFA$<>"YES"THEN115
  714.   printSemi(F("Abort BBS? YES or NO? "));
  715.   uc = 1;
  716.   //gosub1005();
  717.   if (gosub1005()==255) goto line20;
  718.   if (strcmp(aStr, "YES")!=0) goto line115;
  719.  
  720.   //520 GOSUB605:STOP
  721.   gosub605();
  722.   return;
  723. } // end of allram()
  724.  
  725. /*---------------------------------------------------------------------------*/
  726. // Subroutines (formerly GOSUBs)
  727. //
  728. //550 '%LOAD%
  729. void gosub555()
  730. {
  731.   //555 PRINT"%LOAD% [ENTER] WHEN READY";:GOSUB1005
  732.   printSemi(F("%LOAD% [ENTER] WHEN READY"));
  733.   //gosub1005();
  734.   print();
  735.   if (aStr[0]=='!') return;
  736.  
  737.   //560 OPEN"I",#-1,"USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
  738.   loadUserlog();
  739.  
  740.   //565 OPEN"I",#-1,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  741.   loadMsgBase();
  742. }
  743.  
  744. //600 '%SAVE%
  745. void gosub605()
  746. {
  747.   //605 PRINT"%SAVE% [ENTER] WHEN READY";:GOSUB1005:MOTORON:FORA=0TO999:NEXTA
  748.   printSemi(F("%SAVE% [ENTER] WHEN READY"));
  749.   gosub1005();
  750.  
  751.   //610 OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
  752.   saveUserlog();
  753.  
  754.   //615 OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  755.   saveMsgBase();
  756. }
  757.  
  758. //1000 'User Input
  759. #define CR         13
  760. #define INBUF_SIZE 64
  761. byte gosub1005()
  762. {
  763.   byte ch; // Used only here, so we can make it local.
  764.   byte count;
  765.  
  766.   //1005 LINEINPUTA$:A$=LEFT$(A$,64):IFUC=0ORA$=""THENRETURN
  767.   count = lineinput(aStr, INPUT_SIZE);
  768.   aStr[INPUT_SIZE] = '\0';
  769.   if ((uc==0) || (aStr[0]=='\0')) return count;
  770.  
  771.   //1010 FORC=1TOLEN(A$):CH=ASC(MID$(A$,C,1)):IFCH>96THENMID$(A$,C,1)=CHR$(CH-32)
  772.   for (c=0; c<strlen(aStr); c++)   {     ch = aStr[ c];     if (ch>96) aStr[ c] = ch-32;
  773.     //1015 IFCH=92THENMID$(A$,C,1)="/"
  774.     if (ch==92) aStr[ c] = '/';
  775.     //1020 NEXTC:UC=0:RETURN
  776.   }
  777.   uc = 0;
  778.  
  779.   return count;
  780. }
  781.  
  782. //1050 'Function Border
  783. void gosub1055()
  784. {
  785.   //1055 CLS:PRINTCL$BR$:PRINTTAB((32-LEN(A$))/2)A$:PRINTBR$:RETURN
  786.   cls();
  787.   print((__FlashStringHelper*)clStr);
  788.   print((__FlashStringHelper*)brStr);
  789.   printTab((32-strlen(aStr))/2);
  790.   print(aStr);
  791.   print((__FlashStringHelper*)brStr);
  792. }
  793.  
  794. /*---------------------------------------------------------------------------*/
  795. // The following functions mimic some of the Extended Color BASIC commands.
  796.  
  797. // CLS
  798. // Clear the screen.
  799. void cls()
  800. {
  801.   print();
  802.   print(F("--------------CLS--------------"));
  803. }
  804.  
  805. // SOUND tone, duration
  806. // On the CoCo, tone (1-255), duration (1-255; 15=1 second).
  807. void sound(byte tone, byte duration)
  808. {
  809.   Serial.write(0x07);   // BEL
  810.   delay(duration*66.6); // Estimated delay.
  811. }
  812.  
  813. /*---------------------------------------------------------------------------*/
  814. // String functions.
  815.  
  816. // STRING$(length, charcode)
  817. // Generate a string of length charcode chracters.
  818. void string(byte length, byte charcode)
  819. {
  820.   int i;
  821.  
  822.   for (i=0; i<length; i++) printCharSemi(charcode);
  823. }
  824.  
  825. // RIGHT$(str, length)
  826. // Note: Modifies the passed in string, which is okay for our purpose but
  827. // would not be suitable as a generic replacement for RIGHT$.
  828. char *right(char *str, byte length)
  829. {
  830.   return &str[strlen(str)-length];
  831. }
  832.  
  833. // INSTR(first, str, substr)
  834. // Starting at pos, return position of substr in str, or 0 if not found.
  835. int instr(byte pos, char *str, char *substr)
  836. {
  837.   if (pos<1) return 0;   return instr(aStr+pos, substr) + pos; } int instr(char *str, char *substr) {   char *ptr;   ptr = strstr(str, substr);   if (ptr==NULL) return 0; // No match?   if (ptr==&str[strlen(str)]) return 0; // Matched the \0 at end of line?   return ptr-str+1; } /*---------------------------------------------------------------------------*/ // Input functions. #ifdef ENET_PORT byte lineinput(char *cmdLine, byte len) {   return telnetInput(telnetClient, cmdLine, len); } #else // LINE INPUT str // Read string up to len bytes. This code comes from my Hayes AT Command // parser, so the variables are named differently. #define CR           13 #define BEL          7 #define BS           8 #define CAN          24 byte lineinput(char *cmdLine, byte len) {   int     ch;   byte    cmdLen = 0;   boolean done;   done = false;   while(!done)   {     //ledBlink();     ch = -1; // -1 is no data available     if (Serial.available()>0)
  838.     {
  839.       ch = Serial.read();
  840.     }
  841.     else
  842.     {
  843.       continue; // No data. Go back to the while()...
  844.     }
  845.     switch(ch)
  846.     {
  847.     case -1: // No data available.
  848.       break;
  849.  
  850.     case CR:
  851.       print();
  852.       cmdLine[cmdLen] = '\0';
  853.       done = true;
  854.       break;
  855.  
  856.       /*case CAN:
  857.        print(F("[CAN]"));
  858.        cmdLen = 0;
  859.        break;*/
  860.  
  861.     case BS:
  862.       if (cmdLen>0)
  863.       {
  864.         printCharSemi(BS);
  865.         printSemi(F(" "));
  866.         printCharSemi(BS);
  867.         cmdLen--;
  868.       }
  869.       break;
  870.  
  871.     default:
  872.       // If there is room, store any printable characters in the cmdline.
  873.       if (cmdLen<len)       {         if ((ch>31) && (ch<127)) // isprint(ch) does not work.
  874.         {
  875.           printCharSemi(ch);
  876.           cmdLine[cmdLen] = ch; //toupper(ch);
  877.           cmdLen++;
  878.         }
  879.       }
  880.       else
  881.       {
  882.         printCharSemi(BEL); // Overflow. Ring 'dat bell.
  883.       }
  884.       break;
  885.     } // end of switch(ch)
  886.   } // end of while(!done)
  887.  
  888.   return cmdLen;
  889. }
  890. #endif
  891.  
  892. // INKEY$
  893. // Return character waiting (if any) from standard input (not ethernet).
  894. char inkey()
  895. {
  896.   if (Serial.available()==0) return 0;
  897.   return Serial.read();
  898. }
  899.  
  900. /*---------------------------------------------------------------------------*/
  901.  
  902. // File I/O
  903. // Ideally, I would have created wrappers for the OPEN, READ, CLOSE commands,
  904. // but I was in a hurry, so...
  905.  
  906. //SD Card routines
  907. #if defined(SD_PIN)
  908. #define TEMP_SIZE 4
  909. #define FNAME_MAX (8+1+3+1)
  910. boolean initSD()
  911. {
  912.   static bool sdInit = false;
  913.  
  914.   if (sdInit==true) return true;
  915.  
  916.   printSemi(F("Initializing SD card..."));
  917.   pinMode(SD_PIN, OUTPUT);
  918.   if (!SD.begin(SD_PIN))
  919.   {
  920.     print(F("initialization failed."));
  921.     return false;
  922.   }
  923.   print(F("initialization done."));
  924.   sdInit = true;
  925.  
  926.   return true;
  927. }
  928. #endif
  929.  
  930. //560 OPEN"I",#-1,"USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
  931. void loadUserlog()
  932. {
  933. #if defined(SD_PIN)
  934.   File myFile;
  935.   char tempStr[TEMP_SIZE];
  936.   char filename[FNAME_MAX];
  937.  
  938.   if (!initSD()) return;
  939.  
  940.   strncpy_P(filename, PSTR("USERLOG"), FNAME_MAX);
  941.  
  942.   myFile = SD.open(filename, FILE_READ);
  943.  
  944.   if (myFile)
  945.   {
  946.     printSemi(filename);
  947.     print(F(" opened."));
  948.     fileReadln(myFile, tempStr, TEMP_SIZE);
  949.     cl = atoi(tempStr);
  950.     Serial.print(F("cl = "));
  951.     Serial.println(cl);
  952.     fileReadln(myFile, tempStr, TEMP_SIZE);
  953.     nm = atoi(tempStr);
  954.     Serial.print(F("nm = "));
  955.     Serial.println(nm);
  956.     for (a=0; a<=nm; a++)
  957.     {
  958.       fileReadln(myFile, nmArray[a], ULOG_SIZE);
  959.       Serial.print(a);
  960.       Serial.print(F(". "));
  961.       Serial.println(nmArray[a]);
  962.     }
  963.     myFile.close();
  964.   }
  965.   else
  966.   {
  967.     printSemi(F("Error opening "));
  968.     print(filename);
  969.   }
  970. #else
  971.   print(F("(USERLOG would be loaded from tape here.)"));
  972. #endif
  973. }
  974.  
  975. //565 OPEN"I",#-1,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  976. void loadMsgBase()
  977. {
  978. #if defined(SD_PIN)
  979.   File myFile;
  980.   char tempStr[TEMP_SIZE];
  981.   char filename[FNAME_MAX];
  982.  
  983.   if (!initSD()) return;
  984.  
  985.   strncpy_P(filename, PSTR("MSGBASE"), FNAME_MAX);
  986.  
  987.   myFile = SD.open(filename, FILE_READ);
  988.   if (myFile)
  989.   {
  990.     printSemi(filename);
  991.     print(F(" opened."));
  992.     fileReadln(myFile, tempStr, TEMP_SIZE);
  993.     ms = atoi(tempStr);
  994.     Serial.print("ms = ");
  995.     Serial.println(ms);
  996.     for (a=0; a<=ms-1; a++)
  997.     {
  998.       for (b=0; b<=MAX_LINE; b++)
  999.       {
  1000.         fileReadln(myFile, msArray[a][b], INPUT_SIZE);
  1001.         Serial.print(F("msArray["));
  1002.         Serial.print(a);
  1003.         Serial.print(F("]["));
  1004.         Serial.print(b);
  1005.         Serial.print(F("] = "));
  1006.         Serial.println(msArray[a][b]);
  1007.       }
  1008.     }
  1009.     myFile.close();
  1010.   }
  1011.   else
  1012.   {
  1013.     printSemi(F("Error opening "));
  1014.     print(filename);
  1015.   }
  1016. #else
  1017.   print(F("(MSGBASE would be loaded from tape here.)"));
  1018. #endif
  1019. }
  1020.  
  1021. #if defined(SD_PIN)
  1022. //byte fileReadln(File myFile, char *buffer, byte count)
  1023. {
  1024.   char ch;
  1025.   int  pos;
  1026.  
  1027.   pos = 0;
  1028.   while(myFile.available() && pos<count)   {     ch = myFile.read();     if (ch==CR)     {       buffer[pos] = '\0';       break;     }     if (ch>=32)
  1029.     {
  1030.       //Serial.print(ch);
  1031.       buffer[pos] = ch;
  1032.       pos++;
  1033.     }
  1034.   }
  1035.   if (pos>=count) buffer[pos] = '\0';
  1036.   //Serial.println();
  1037.   return pos;
  1038. }
  1039. #endif
  1040.  
  1041. //610 OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
  1042. void saveUserlog()
  1043. {
  1044. #if defined(SD_PIN)
  1045.   File myFile;
  1046.   char filename[FNAME_MAX];
  1047.  
  1048.   if (!initSD()) return;
  1049.  
  1050.   strncpy_P(filename, PSTR("USERLOG"), FNAME_MAX);
  1051.  
  1052.   if (SD.exists(filename)==true) SD.remove(filename);
  1053.  
  1054.   myFile = SD.open(filename, FILE_WRITE);
  1055.   if (myFile)
  1056.   {
  1057.     printSemi(filename);
  1058.     print(F(" created."));
  1059.     myFile.println(cl);
  1060.     Serial.print(F("cl = "));
  1061.     Serial.println(cl);
  1062.     myFile.println(nm);
  1063.     Serial.print(F("nm = "));
  1064.     Serial.println(nm);
  1065.     for (a=0; a<=nm; a++)
  1066.     {
  1067.       myFile.println(nmArray[a]);
  1068.       Serial.print(a);
  1069.       Serial.print(F(". "));
  1070.       Serial.println(nmArray[a]);
  1071.     }
  1072.     myFile.close();
  1073.   }
  1074.   else
  1075.   {
  1076.     print(F("Error creating file."));
  1077.   }
  1078. #else
  1079.   print(F("save USERLOG"));
  1080. #endif
  1081. }
  1082.  
  1083. //615 OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  1084. void saveMsgBase()
  1085. {
  1086. #if defined(SD_PIN)
  1087.   File myFile;
  1088.   char filename[FNAME_MAX];
  1089.  
  1090.   if (!initSD()) return;
  1091.  
  1092.   strncpy_P(filename, PSTR("MSGBASE"), FNAME_MAX);
  1093.  
  1094.   if (SD.exists(filename)==true) SD.remove(filename);
  1095.  
  1096.   myFile = SD.open(filename, FILE_WRITE);
  1097.   if (myFile)
  1098.   {
  1099.     printSemi(filename);
  1100.     print(F(" created."));
  1101.     myFile.println(ms);
  1102.     for (a=0; a<=ms-1; a++)
  1103.     {
  1104.       for (b=0; b<=MAX_LINE; b++)
  1105.       {
  1106.         myFile.println(msArray[a][b]);
  1107.         Serial.print(F("msArray["));
  1108.         Serial.print(a);
  1109.         Serial.print(F("]["));
  1110.         Serial.print(b);
  1111.         Serial.print(F("] = "));
  1112.         Serial.println(msArray[a][b]);
  1113.       }
  1114.     }
  1115.     myFile.close();
  1116.   }
  1117.   else
  1118.   {
  1119.     print(F("Error creating file."));
  1120.   }
  1121. #else
  1122.   print(F("save MSGBASE"));
  1123. #endif
  1124. }
  1125.  
  1126. /*---------------------------------------------------------------------------*/
  1127. // Print (output) routines.
  1128.  
  1129. // For TAB to work, we need to track where we think we are on the line.
  1130. byte tabPos = 0;
  1131.  
  1132. // We want to simulate the following:
  1133. // PRINT "HELLO"  -- string, with carraige return at end of line
  1134. // PRINT A        -- number (space before and after), with carraige return
  1135. // PRINT "HELLO"; -- no carraige return
  1136. // PRINT TAB(5);  -- tab to position 5
  1137. // PRINT "A","B"  -- tab to column 16 (on 32-column screen)
  1138.  
  1139. // Due to various types of strings on Arduino (in memory or Flash), we will
  1140. // have to duplicate some functions to have versions that take the other
  1141. // types of strings.
  1142.  
  1143. // printTypeSemi() routines will not put a carraige return at the end.
  1144.  
  1145. // PRINT TAB(column);
  1146. void printTab(byte column)
  1147. {
  1148.   while(tabPos<column)   {     printSemi(F(" ")); // Print, and increment tab position.   } } // PRINT, // NOTE: DECB doesn't add a carraige return after a comma. void printComma() {   printTab(tabPos + (16-(tabPos % 16))); } // PRINT "STRING"; // For normal strings in RAM. void printSemi(const char *string) {   tabPos = tabPos + Serial.print(string); #if defined(ENET_PORT)   telnetClient.print(string); #endif } // For strings in Flash. void printSemi(const __FlashStringHelper *string) {   tabPos = tabPos + Serial.print(string); #if defined(ENET_PORT)   telnetClient.print(string); #endif } // For printing a byte as a number (0-255). void printSemi(byte num) {   Serial.print(F(" "));   tabPos = tabPos + Serial.print(num);   Serial.print(F(" "));   tabPos = tabPos + 2; #if defined(ENET_PORT)   telnetClient.print(F(" "));   telnetClient.print(num);   telnetClient.print(F(" ")); #endif } // For printing a single character. void printCharSemi(char ch) {   tabPos = tabPos + Serial.print(ch); #if defined(ENET_PORT)   telnetClient.print(ch); #endif } // For printing a byte as a number with no spaces. void printNumSemi(byte num) {   tabPos = tabPos + Serial.print(num); #if defined(ENET_PORT)   telnetClient.print(num); #endif } // PRINT void print(void) {   Serial.println(); #if defined(ENET_PORT)   telnetClient.println(); #endif } // PRINT "STRING" void print(const char *string) {   Serial.println(string);   tabPos = 0; #if defined(ENET_PORT)   telnetClient.println(string); #endif } void print(const __FlashStringHelper *string) {   Serial.println(string);   tabPos = 0; #if defined(ENET_PORT)   telnetClient.println(string); #endif } // PRINT I void print(int num) {   Serial.print(F(" "));   Serial.println(num);   tabPos = 0; #if defined(ENET_PORT)   telnetClient.print(F(" "));   telnetClient.println(num); #endif } // PRINT USING(format, number); // NOTE: This only emulates printing positive integers, which is the // only way it is used by this program. void printUsingSemi(char *format, byte num) {   byte i;   byte fmtDigits;   byte numDigits;   byte tempNum;   i = 0;   while(format[i]!='\0')   {     if (format[i]!='#') {       printCharSemi(format[i]);       i++;       continue;     }     else     {       // Start counting the run of #s.       // Find end of #'s to know how many to use.       fmtDigits = 0;       while(format[i]=='#' && format[i]!='\0')       {         fmtDigits++;         i++;       }       // Now we know how many # (digits).       tempNum = num;       numDigits = 1;       while(tempNum>10)
  1149.       {
  1150.         tempNum = tempNum/10;
  1151.         numDigits++;
  1152.       }
  1153.       while(numDigits<fmtDigits)
  1154.       {
  1155.         printSemi(F(" "));
  1156.         fmtDigits--;
  1157.       }
  1158.       printNumSemi(num);
  1159.     }
  1160.   }
  1161. }
  1162.  
  1163. /*---------------------------------------------------------------------------*/
  1164. // Show some stuff functions.
  1165.  
  1166. // Emit some configuration information.
  1167. void showConfig()
  1168. {
  1169.   print((__FlashStringHelper*)brStr);
  1170.   print(F("*ALL RAM* Configuration:"));
  1171.   printSemi(F("Userlog size :"));
  1172.   print(MAX_USERS+1);
  1173.   printSemi(F("Input size   :"));
  1174.   print(INPUT_SIZE);
  1175.   printSemi(F("Username size:"));
  1176.   print(NAME_SIZE);
  1177.   printSemi(F("Password size:"));
  1178.   print(PSWD_SIZE);
  1179.   printSemi(F("Msg base size:"));
  1180.   print(MAX_MSGS+1);
  1181.   printSemi(F("Message lines:"));
  1182.   print(MAX_LINE+1);
  1183. #if defined(SD_PIN)
  1184.   print(F("SD card      : Enabled"));
  1185. #endif
  1186. #if defined(ENET_PORT)
  1187.   print(F("Ethernet     : Enabled"));
  1188. #endif
  1189.   printSemi(F("ESTIMATED MEM:"));
  1190.   printSemi(ULOG_MEM + MBASE_MEM);
  1191.   print(F("bytes."));
  1192.   printSemi(F("Free RAM     :"));
  1193.   print(freeRam());
  1194.   print((__FlashStringHelper*)brStr);
  1195. }
  1196.  
  1197. void showLoginMessage()
  1198. {
  1199.   print(F("\nYou are connected to an Arduino UNO R3, a small computer thing you can buy\n"
  1200.     "from Radio Shack for $29.99 (or around $22 online). It has 2K of RAM and\n"
  1201.     "32K of Flash for program storage. It has a SainSmart Ethernet Shield ($17.99)\n"
  1202.     "attached to it. The software running here is a line-by-line port of my\n"
  1203.     "*ALL RAM* BBS program written in 1983. The original Extended Color BASIC\n"
  1204.     "code was converted as literally as possible to Arduino C. It's a travesty.\n"
  1205.     "\n"
  1206.     "This BBS program was designed to run on a cassette based TRS-80 Color\n"
  1207.     "Computer with 32K of RAM. All messages and users were stored in memory.\n"
  1208.     "Obviously, with only 2K of RAM, this is not possible, so this version has\n"
  1209.     "been configured to allow only a few users and a teensy tiny message base\n"
  1210.     "(smaller than Twitter posts!). Enjoy the experiment!\n"
  1211.     "\n"
  1212.     "(If userlog is full, use the Sysop password 'TEST'.)"));
  1213. }
  1214.  
  1215. void showCoCoHeader()
  1216. {
  1217.   cls();
  1218.   print(F("EXTENDED COLOR BASIC 1.1\n"
  1219.     "COPYRIGHT (C) 1982 BY TANDY\n"
  1220.     "UNDER LICENSE FROM MICROSOFT\n"
  1221.     "\n"
  1222.     "OK\n"
  1223.     "CLOAD\"ALLRAM\"\n"
  1224.     "RUN"));
  1225. }
  1226.  
  1227. /*---------------------------------------------------------------------------*/
  1228.  
  1229. // Debug and utility functions.
  1230. //
  1231.  
  1232. unsigned int freeRam() {
  1233.   extern int __heap_start, *__brkval;
  1234.   int v;
  1235.   return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
  1236. }
  1237.  
  1238. void showFreeRam()
  1239. {
  1240.   printSemi(F("Free RAM:"));
  1241.   print(freeRam());
  1242. }
  1243.  
  1244. /*---------------------------------------------------------------------------*/
  1245.  
  1246. void showUserlog()
  1247. {
  1248.   int i;
  1249.   printSemi(F("Users: "));
  1250.   print(nm);
  1251.   for (i=0; i<=nm; i++) {
  1252.     printSemi(i);
  1253.     printSemi(F(". "));
  1254.     print(nmArray[i]);
  1255.   }
  1256. }
  1257.  
  1258. void showMessageBase()
  1259. {
  1260.   int i,j;
  1261.  
  1262.   for (int i=0; i<ms; i++)
  1263.   {
  1264.     for (int j=0; j<=MAX_LINE; j++)
  1265.     {
  1266.       printSemi(F("msArray["));
  1267.       printSemi(i);
  1268.       printSemi(F(","));
  1269.       printSemi(j);
  1270.       printSemi(F("] = "));
  1271.       print(msArray[i][j]);
  1272.     }
  1273.   }
  1274. }
  1275.  
  1276. /*---------------------------------------------------------------------------*/
  1277.  
  1278. //0 REM *ALL RAM* BBS Editor 1.0
  1279. //1 REM   Shareware / (C) 1983
  1280. //2 REM     By Allen Huffman
  1281. //3 REM  110 Champions Dr, #811
  1282. //4 REM     Lufkin, TX 75901
  1283. //5 FORA=0TO8:READA$:POKE1024+A,VAL("&H"+A$):NEXTA:EXEC1024:DATAC6,1,96,BC,1F,2,7E,96,A3
  1284. //10 CLEAR21000:DIMNM$(200),MS$(19,10),A$,F$,S$,T$,NM$,PW$,A,B,C,LN,LV,MS,NM,PR:PR=80
  1285. //15 CLS:PRINTTAB(3)"*ALL RAM* EDITOR COMMANDS:":printING$(32,45)
  1286. //20 PRINTTAB(4)"1. CREATE USERLOG",TAB(4)"2. LOAD USERLOG/MSG BASE",TAB(4)"3. SAVE USERLOG/MSG BASE",TAB(4)"4. PRINT USERLOG",TAB(4)"5. PRINT MESSAGES",TAB(4)"6. EDIT USERS",TAB(4)"7. KILL MESSAGES",TAB(4)"8. QUIT"
  1287. //25 PRINT@392,"ENTER FUNCTION :"
  1288. //30 A$=INKEY$:IFA$=""THEN30ELSEPRINT@408,A$:LN=VAL(A$):IFLN8THENSOUND50,1:GOTO25
  1289. //35 SOUND200,1:ONLN GOTO55,105,155,205,255,305,405,40
  1290. //40 STOP
  1291. //50 'Create Userlog
  1292. //55 CLS:PRINTTAB(7)"SYSOP INFORMATION:":printING$(32,45)
  1293. //60 PRINT@128,"SYSOP'S NAME:    (20 CHARACTERS)>";:LINEINPUTA$:IFA$=""ORLEN(A$)>20THENSOUND50,1:GOTO15ELSENM$=A$
  1294. //65 PRINT@192,"PASSWORD    :     (8 CHARACTERS)>";:LINEINPUTA$:IFA$=""ORLEN(A$)>8THENSOUND50,1:GOTO15ELSEPW$=A$
  1295. //70 PRINT@297,"*VERIFY ENTRY*":PRINT:PRINT"NAME :"NM$:PRINT"PSWD :"PW$:PRINT@456,"IS THIS CORRECT?";
  1296. //75 LINEINPUTA$:IFLEFT$(A$,1)<>"Y"THENSOUND50,1:GOTO55
  1297. //80 NM$(0)=NM$+"\"+PW$+"9":GOTO15
  1298. //100 'Load Userlog/Msg Base
  1299. //105 CLS:PRINTTAB(5)"LOAD USERLOG/MSG BASE:":printING$(32,45)
  1300. //110 LINEINPUT" READY TAPE, THEN PRESS ENTER:";A$:PRINT@168,"...ONE MOMENT..."
  1301. //115 OPEN"I",#-1,"USERLOG":PRINT@232,"LOADING  USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
  1302. //120 OPEN"I",#-1,"MSG BASE":PRINT@240,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:GOTO15
  1303. //150 'Save Userlog/Msg Base
  1304. //155 CLS:PRINTTAB(5)"SAVE USERLOG/MSG BASE:":printING$(32,45)
  1305. //160 LINEINPUT" READY TAPE, THEN PRESS ENTER:";A$:PRINT@168,"...ONE MOMENT...":MOTORON:FORA=1TO1000:NEXTA
  1306. //165 PRINT@232,"SAVING   USERLOG":OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
  1307. //170 PRINT@240,"MSG BASE":OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:GOTO15
  1308. //200 'Print Userlog
  1309. //205 IFNM$(0)=""THENPRINT@454,"*USERLOG NOT LOADED*":SOUND50,1:GOTO25
  1310. //210 CLS:PRINTTAB(9)"PRINT USERLOG:":printING$(32,45)
  1311. //215 LINEINPUT"    PRESS ENTER WHEN READY:";A$:PRINT@169,"...PRINTING..."
  1312. //220 PRINT#-2,TAB((PR-30)/2)"[*ALL RAM* BBS System Userlog]":PRINT#-2,"":PRINT#-2,TAB((PR-46)/2)"[###]  [        NAME        ]  [PASSWORD]  [L]"
  1313. //225 FORA=0TONM: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))
  1314. //230 A$="000........................................0":B=LEN(STR$(A))-1:MID$(A$,4-B,B)=RIGHT$(STR$(A),B):MID$(A$,8,LEN(NM$))=NM$:MID$(A$,32,LEN(PW$))=PW$:MID$(A$,44,1)=RIGHT$(STR$(LV),1)
  1315. //235 PRINT@238,A:PRINT#-2,TAB((PR-44)/2)A$:NEXTA:GOTO15
  1316. //250 'Print Messages
  1317. //255 IFMS$(0,0)=""THENPRINT@454,"*MSG BASE NOT LOADED*":SOUND50,1:GOTO25
  1318. //260 CLS:PRINTTAB(8)"PRINT  MESSAGES:":printING$(32,45)
  1319. //265 LINEINPUT"    PRESS ENTER WHEN READY:";A$:PRINT@169,"...PRINTING..."
  1320. //270 PRINT#-2,TAB((PR-30)/2)"[*ALL RAM* BBS System Messages]":PRINT#-2,""
  1321. //275 FORA=0TOMS-1:A$=MS$(A,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)
  1322. //280 PRINT@238,A+1:B=(PR-64)/2:PRINT#-2,TAB(B)"Message #"A:PRINT#-2,TAB(B)"TO :"T$:PRINT#-2,TAB(B)"FR :"F$:PRINT#-2,TAB(B)"SB :"S$:PRINT#-2,STRING$(64,45):C=0
  1323. //285 C=C+1:PRINT#-2,TAB(B)MS$(A,C):IFMS$(A,C)=""THEN290ELSEIFC20THENPRINT@198,NM$ELSENM$=A$
  1324. //370 PRINT@230,;:LINEINPUTA$:IFA$=""ORLEN(A$)>8THENPRINT@230,PW$ELSEPW$=A$
  1325. //375 PRINT@262,;:LINEINPUTA$:B=VAL(A$):IFB9THENPRINT@261,LV ELSELV=B
  1326. //380 NM$(A)=NM$+"\"+PW$+RIGHT$(STR$(LV),1):GOTO335
  1327. //385 PRINT@456," JUMP TO USER # ";:LINEINPUTA$:B=VAL(A$):IFBNM THENSOUND1,5:GOTO335ELSEA=B:GOTO320
  1328. //390 A=A-1:IFA<0THENA=NM //391 GOTO315 //395 A=A+1:IFA>NM THENA=0
  1329. //396 GOTO315
  1330. //400 'Kill Messages
  1331. //405 IFMS$(0,0)=""THENPRINT@454,"*MSG BASE NOT LOADED*":SOUND50,1:GOTO25
  1332. //410 CLS:PRINTTAB(9)"KILL MESSAGES:":ING$(32,45)
  1333. //415 PRINT@96,"DELETE MESSAGE # 1 -"MS":";:LINEINPUTA$:A=VAL(A$):IFAMS THEN15
  1334. //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)
  1335. //425 PRINT:PRINT"TO: "T$:PRINT"FR: "F$:PRINT"SB: "S$
  1336. //430 PRINT:LINEINPUT"DELETE THIS?";A$:IFLEFT$(A$,1)<>"Y"THEN410
  1337. //435 IFA=MS THENMS=MS-1:GOTO410
  1338. //440 FORB=A-1 TOMS-2:FORC=0TO10:MS$(B,C)=MS$(B+1,C):NEXTC:NEXTB:MS=MS-1:GOTO410
  1339.  
  1340. // End of file.
Advertisement
Add Comment
Please, Sign In to add comment