Advertisement
Guest User

ALL RAM BBS aStr[1] Version

a guest
Jun 25th, 2013
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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 (alsplace@pobox.com / 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 <SD.h>
  63. #endif
  64.  
  65. #if defined(ENET_PORT)
  66. #include <SPI.h>
  67. #include <Ethernet.h>
  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();
  93. }
  94.  
  95. // We don't really have anything loop worthy for this program, so we'll
  96. // just have some fun and pretend like we are starting up the CoCo each
  97. // time through.
  98. void loop()
  99. {
  100.   showCoCoHeader(); // Just for fun...
  101.  
  102.   allram();
  103.  
  104.   print();
  105.   print(F("BREAK IN 520"));
  106.   print(F("OK"));
  107.  
  108.   delay(5000);
  109. }
  110.  
  111. // Show program header.
  112. void showHeader()
  113. {
  114.   // Emit some startup stuff to the serial port.
  115.   print(F("\n"
  116.     "*ALL RAM* BBS for Arduino "VERSION" - 30 year anniversary edition!\n"
  117.     "Copyright (C) 1983 by Allen C. Huffman\n"
  118.     "Ported from TRS-80 Color Computer Extended Color BASIC.\n"
  119.     "Build Date: "__DATE__" "__TIME__"\n"));
  120. }
  121.  
  122. /*---------------------------------------------------------------------------*/
  123. // In BASIC, strings are dynamic. For C, we have to pre-allocate buffers for
  124. // the strings.
  125. #define INPUT_SIZE  32  // 64. For aStr, etc.
  126.  
  127. #define MAX_USERS   3   // NM$(200) Userlog size. (0-200, 0 is Sysop)
  128. #define NAME_SIZE   12   // 20. Username size (nmStr)
  129. #define PSWD_SIZE   8   // 8. Password size (psStr & pwStr)
  130. #define ULOG_SIZE   (NAME_SIZE+1+PSWD_SIZE+1+1)
  131.  
  132. // To avoid hard coding some values, we define these here, too. Each message
  133. // is made up of lines, and the first line will contain the From, To, and
  134. // Subject separated by a character. So, while the original BASIC version
  135. // hard coded this, we will calculate it, letting the subject be as large
  136. // as whatever is left over (plus room for separaters and NULL at the end).
  137. #define FR_SIZE     NAME_SIZE                      // From
  138. #define TO_SIZE     NAME_SIZE                      // To
  139. #define SB_SIZE     (INPUT_SIZE-FR_SIZE-1-TO_SIZE) // "From\To\Subj"
  140.  
  141. // The original BASIC version was hard-coded to hold 20 messages of 11 lines
  142. // each (the first line was used for From/To/Subject). The Arduino has far
  143. // less RAM, so these have been made #defines so they can be changed.
  144. #define MAX_MSGS    3   // 19  (0-19, 20 messages)
  145. #define MAX_LINE    2   // 10  (0-10, 11 lines)
  146.  
  147. // Rough estimate of how many bytes these items will take up.
  148. #define ULOG_MEM    ((MAX_USERS+1)*(ULOG_SIZE))
  149. #define MBASE_MEM   ((MAX_MSGS+1)*MAX_LINE*INPUT_SIZE)
  150.  
  151. // Validate the settings before compiling.
  152. #if (FR_SIZE+1+TO_SIZE+SB_SIZE > INPUT_SIZE)
  153. #error INPUT_SIZE too small to hold "From\To\Sub".
  154. #endif
  155.  
  156. /*---------------------------------------------------------------------------*/
  157.  
  158. //0 REM *ALL RAM* BBS System 1.0
  159. //1 REM   Shareware / (C) 1983
  160. //2 REM     By Allen Huffman
  161. //3 REM  110 Champions Dr, #811
  162. //4 REM     Lufkin, TX 75901
  163. //5 CLS:FORA=0TO8:READA$:POKE1024+A,VAL("&H"+A$):NEXTA:EXEC1024:DATAC6,1,96,BC,1F,2,7E,96,A3
  164. //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
  165.  
  166. // All variables in BASIC are global, so we are declaring them outside the
  167. // functions to make them global in C as well. Arrays in BASIC are "0 to X",
  168. // and in C they are "0 to X-1", so we add one to them in C to get the same
  169. // number of elements.
  170. char nmArray[MAX_USERS+1][ULOG_SIZE];             // NM$(200)
  171. char msArray[MAX_MSGS+1][MAX_LINE+1][INPUT_SIZE+1];// MS$(19,10) 1.4
  172. char aStr[INPUT_SIZE+1];                          // A$ 1.4
  173. char fStr[FR_SIZE];                               // F$ - From
  174. char sStr[SB_SIZE];                               // S$ - Subj
  175. char tStr[TO_SIZE];                               // T$ - To
  176. char nmStr[NAME_SIZE];                            // NM$ - Name
  177. char psStr[PSWD_SIZE];                            // PS$ - Pswd
  178. char pwStr[PSWD_SIZE];                            // PW$ - Pswd
  179.  
  180. // To save RAM, these two strings will exist in Flash memory. It will
  181. // require a bit of work later to use them (__FlashStringHelper*).
  182. prog_char brStr[] PROGMEM = "*==============*==============*"; // BR$ - border
  183. prog_char clStr[] PROGMEM = "\x0c\x0e";                        // CL$ - clear
  184.  
  185. int a, b, c, cl, ln, lv, ms, nm, ky, uc;
  186. // A, B, C - misc.
  187. // CL - Calls
  188. // LN - Line Number
  189. // LV - Level
  190. // MS - Messages
  191. // NM - Names (users)
  192. // KY - Keys (commands entered)
  193. // UC - Uppercase input (1=Yes, 0=No)
  194.  
  195. void allram()
  196. {
  197.   // HACK - create adefault Sysop account.
  198.   nm = 0;
  199.   strncpy_P(nmArray[0], PSTR("SYSOP\\TEST9"), ULOG_SIZE);
  200.  
  201.   cls(); // From line 5  
  202.  
  203.   //15 CL$=CHR$(12)+CHR$(14):BR$="*==============*==============*":GOSUB555
  204.   //char cl[] = "\0xC\0xE";
  205.   //char br[] = "*==============*==============*";
  206.   gosub555();
  207.  
  208. //20 CLS:PRINTTAB(6)"*ALL RAM* BBS SYSTEM":PRINT"USERS:"NM,"CALLS:"CL:PRINTTAB(5)"SYSTEM AWAITING CALLER";:go:SOUND200,10
  209. line20:
  210. nmStr[0] = 0; // reset user.
  211.   cls();
  212.   printTab(6);
  213.   print(F("*ALL RAM* BBS SYSTEM"));
  214.   printSemi(F("USERS:"));
  215.   printSemi(nm);
  216.   printComma();
  217.   printSemi(F("CALLS:"));
  218.   print(cl);
  219.   printTab(5);
  220.   printSemi(F("SYSTEM AWAITING CALLER"));
  221.   //gosub1005();
  222.   if (gosub1005()==255) goto line20;
  223.   sound(200,10);
  224.  
  225.   //25 A$="Welcome To *ALL RAM* BBS!":GOSUB1055:KY=0:CL=CL+1
  226.   strncpy_P(aStr, PSTR("Welcome To *ALL RAM* BBS!"), INPUT_SIZE);
  227.   gosub1055();
  228.   ky = 0;
  229.   cl = cl + 1;
  230.  
  231.   showLoginMessage();
  232.  
  233.   //30 PRINT:PRINT"Password or 'NEW' :";:UC=1:GOSUB1005:PS$=A$:IFA$=""ORA$="NEW"THEN55ELSEPRINT"Checking: ";:A=0
  234. line30:
  235.   print();
  236.   printSemi(F("Password or 'NEW' :"));
  237.   uc = 1;
  238.   //gosub1005();
  239.   if (gosub1005()==255) goto line20;
  240.   strncpy(psStr, aStr, PSWD_SIZE);
  241.   if (aStr[0]=='\0' || strcmp(aStr, "NEW")==0)
  242.   {
  243.     goto line55;
  244.   }
  245.   else
  246.   {
  247.     printSemi(F("Checking: "));
  248.     a = 0;
  249.   }
  250.  
  251. line35:
  252.   //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
  253.   strncpy(aStr, nmArray[a], ULOG_SIZE);
  254.   b = instr(aStr, "\\");
  255.   strncpy(nmStr, aStr, b-1);
  256.   nmStr[b-1] = '\0';  
  257.   strncpy(pwStr, &aStr[b], strlen(aStr)-b-1);
  258.   pwStr[strlen(aStr)-b-1] = '\0';
  259.   lv = atoi(&aStr[strlen(aStr)-1]);
  260.   if (strncmp(pwStr, psStr, PSWD_SIZE)==0)
  261.   {
  262.     goto line45;
  263.   }
  264.   else
  265.   {
  266.     a = a + 1;
  267.     if (a<=nm) goto line35;
  268.   }
  269.  
  270. line40: // for empty userlog bug
  271.   //40 PRINT"*INVALID*":KY=KY+1:IFKY<3THEN30ELSE215
  272.   print(F("*INVALID*"));
  273.   ky = ky + 1;
  274.   if (ky<3) goto line30;
  275.   goto line215;
  276.  
  277. line45:
  278.   //45 PRINT"*ACCEPTED*":PRINTBR$:PRINT"On-Line: "NM$:PRINT"Access :"LV:PRINT"Caller :"CL:KY=0:GOTO115
  279.   print(F("*ACCEPTED*"));
  280.   print((__FlashStringHelper*)brStr);
  281.   printSemi(F("On-Line: "));
  282.   print(nmStr);
  283.   printSemi(F("Access :"));
  284.   print(lv);
  285.   printSemi(F("Caller :"));
  286.   print(cl);
  287.   ky = 0;
  288.   goto line115;
  289.  
  290.   //50 'New User
  291. line55:
  292.   //55 A$="Password Application Form":GOSUB1055
  293.   strncpy_P(aStr, PSTR("Password Application Form"), INPUT_SIZE);
  294.   gosub1055();
  295.  
  296.   //60 IFNM=200THENPRINT"Sorry, the userlog is full now.":GOTO215ELSEPRINT"Name=20 chars, Password=8 chars"
  297.   if (nm==MAX_USERS)
  298.   {
  299.     print(F("Sorry, the userlog is full now."));
  300.     goto line215;
  301.   }
  302.   else
  303.   {
  304.     printSemi(F("Name="));
  305.     printNumSemi(NAME_SIZE);
  306.     printSemi(F(" chars, Password="));
  307.     printNumSemi(PSWD_SIZE);
  308.     printSemi(F(" chars"));
  309.   }
  310.  
  311. line65:
  312.   //65 PRINT:PRINT"Full Name :";:UC=1:GOSUB1005:NM$=A$:IFA$=""ORLEN(A$)>20THEN30
  313.   print();
  314.   printSemi(F("Full Name :"));
  315.   uc = 1;
  316.   //gosub1005();
  317.   if (gosub1005()==255) goto line20;
  318.   strncpy(nmStr, aStr, NAME_SIZE);
  319.   if (aStr[0]=='\0' || strlen(aStr)>20) goto line30;
  320.  
  321.   //70 PRINT"Password  :";:UC=1:GOSUB1005:PW$=A$:IFA$=""ORLEN(A$)>8THEN30
  322.   printSemi(F("Password  :"));
  323.   uc = 1;
  324.   //gosub1005();
  325.   if (gosub1005()==255) goto line20;
  326.   strncpy(pwStr, aStr, PSWD_SIZE);
  327.   if (aStr[0]=='\0' || strlen(aStr)>8) goto line30;
  328.  
  329.   //75 PRINT:PRINT"Name :"NM$:PRINT"Pswd :"PW$:PRINT"Is this correct? ";:UC=1:GOSUB1005:IFLEFT$(A$,1)="Y"THEN80ELSE65
  330.   print();
  331.   printSemi(F("Name :"));
  332.   print(nmStr);
  333.   printSemi(F("Pswd :"));
  334.   print(pwStr);
  335.   printSemi(F("Is this correct? "));
  336.   uc = 1;
  337.   //gosub1005();
  338.   if (gosub1005()==255) goto line20;
  339.   if (aStr[0]=='Y')
  340.   {
  341.     goto line80;
  342.   }
  343.   else
  344.   {
  345.     goto line65;
  346.   }
  347.  
  348. line80:
  349.   //80 NM=NM+1:NM$(NM)=NM$+"\"+PW$+"0":LV=0:KY=0
  350.   nm = nm + 1;
  351.   strncpy(nmArray[nm], nmStr, NAME_SIZE);
  352.   strcat_P(nmArray[nm], PSTR("\\"));
  353.   strncat(nmArray[nm], pwStr, PSWD_SIZE);
  354.   //strcat_P(nmArray[nm], PSTR("0"));
  355.   strcat_P(nmArray[nm], PSTR("1")); // AUTO VALIDATED
  356.   //lv = 0;
  357.   lv = 1;
  358.   ky = 0;
  359.   //85 PRINT"Your password will be validated as soon as time permits.  Press":PRINT"[ENTER] to continue :";:GOSUB1005
  360.   print(F("Your password will be validated as soon as time permits.  Press"));
  361.   printSemi(F("[ENTER] to continue :"));
  362.   //gosub1005();
  363.   if (gosub1005()==255) goto line20;
  364.  
  365.   //100 'Main Menu
  366. line105:
  367.   //105 A$="*ALL RAM* BBS Master Menu":GOSUB1055
  368.   strncpy_P(aStr, PSTR("*ALL RAM* BBS Master Menu"), INPUT_SIZE);
  369.   gosub1055();
  370.  
  371.   //110 PRINT"C-all Sysop","P-ost Msg":PRINT"G-oodbye","R-ead Msg":PRINT"U-serlog","S-can Titles"
  372.   printSemi(F("C-all Sysop"));
  373.   printComma();
  374.   print(F("P-ost Msg"));
  375.   printSemi(F("G-oodbye"));
  376.   printComma();
  377.   print(F("R-ead Msg"));
  378.   printSemi(F("U-serlog"));
  379.   printComma();
  380.   print(F("S-can Titles"));
  381.  
  382. line115:
  383.   //115 PRINTBR$
  384.   print((__FlashStringHelper*)brStr);
  385.  
  386. line120:
  387.   //120 KY=KY+1:IFKY>200THENPRINT"Sorry, your time on-line is up.":GOTO210ELSEIFKY>180THENPRINT"Please complete your call soon."
  388.   ky = ky + 1;
  389.   if (ky>200)
  390.   {
  391.     print(F("Sorry, your time on-line is up."));
  392.     goto line210;
  393.   }
  394.   else if (ky>180)
  395.   {
  396.     print(F("Please complete your call soon."));
  397.   }
  398.  
  399. line125:
  400.   showFreeRam();
  401.   //125 PRINTTAB(7)"?=Menu/Command :";:UC=1:GOSUB1005:A$=LEFT$(A$,1)
  402.   printTab(7);
  403.   printSemi(F("?=Menu/Command :"));
  404.   uc = 1;
  405.   //gosub1005();
  406.   if (gosub1005()==255) goto line20;
  407.   aStr[1] = '\0';
  408.  
  409. line130:
  410.   //130 LN=INSTR("?CGRSPU%",A$):IFLN=0THENPRINT"*Invalid Command*":GOTO120
  411.   ln = instr("?CGRSPU%", aStr);
  412.   if (ln==0)
  413.   {
  414.     print(F("*Invalid Command*"));
  415.     goto line120;
  416.   }
  417.  
  418.   //135 IFLV<1ANDLN>5THENPRINT" Sorry, you are not validated.":GOTO125
  419.   if (lv<1 && ln>5)
  420.   {
  421.     print(F(" Sorry, you are not validated."));
  422.     goto line125;
  423.   }
  424.  
  425.   //140 ONLN GOTO105,155,205,405,455,305,255,505
  426.   if (ln==1) goto line105;
  427.   if (ln==2) goto line155;
  428.   if (ln==3) goto line205;
  429.   if (ln==4) goto line405;
  430.   if (ln==5) goto line455;
  431.   if (ln==6) goto line305;
  432.   if (ln==7) goto line255;
  433.   if (ln==8) goto line505;
  434.  
  435.   //150 'Call Sysop
  436. line155:
  437.   //155 A$="Calling the Sysop":GOSUB1055:A=0
  438.   strncpy_P(aStr, PSTR("Calling the Sysop"), INPUT_SIZE);
  439.   gosub1055();
  440.   a = 0;
  441.  
  442.   //165 PRINT" BEEP!";:SOUND150,5:IFINKEY$=CHR$(12)THEN175ELSEprintING$(5,8);:A=A+1:IFA<25THEN165
  443. line165:
  444.   printSemi(F(" BEEP!"));
  445.   sound(150, 5);
  446.   if (inkey()==12)
  447.   {
  448.     goto line175;
  449.   }
  450.   else
  451.   {
  452.     string(5, 8);
  453.     a = a + 1;
  454.     if (a<25) goto line165;
  455.   }
  456.  
  457.   //170 PRINT:PRINT" The Sysop is unavaliable now.":GOTO115
  458.   print(F(""));
  459.   print(F(" The Sysop is unavaliable now."));
  460.   goto line115;
  461.  
  462. line175:
  463.   //175 PRINT:PRINTTAB(6)"*Chat mode engaged*"
  464.   print();
  465.   printTab(6);
  466.   print(F("*Chat mode engaged*"));
  467.  
  468. line180:
  469.   //180 GOSUB1005:IFLEFT$(A$,3)="BYE"THEN185ELSE180
  470.   //gosub1005();
  471.   if (gosub1005()==255) goto line20;
  472.   if (strncmp(aStr, "BYE", 3)==0)
  473.   {
  474.     goto line185;
  475.   }
  476.   else
  477.   {
  478.     goto line180;
  479.   }
  480.  
  481. line185:
  482.   //185 PRINTTAB(5)"*Chat mode terminated*":GOTO115
  483.   goto line115;
  484.  
  485.   //200 'Goodbye
  486. line205:
  487.   //205 A$="Thank you for calling":GOSUB1055
  488.   strncpy_P(aStr, PSTR("Thank you for calling"), INPUT_SIZE);
  489.   gosub1055();
  490.  
  491. line210:
  492.   //210 PRINT:PRINT"Goodbye, "NM$"!":PRINT:PRINT"Please call again."
  493.   print(F(""));
  494.   printSemi(F("Goodbye, "));
  495.   printSemi(nmStr);
  496.   print(F("!"));
  497.   print();
  498.   print(F("Please call again."));
  499.  
  500. line215:
  501.   //215 PRINT:PRINT:PRINT"*ALL RAM* BBS disconnecting..."
  502.   print();
  503.   print();
  504.   print(F("*ALL RAM* BBS disconnecting..."));
  505.  
  506.   //220 FORA=1TO1000:NEXTA
  507.   delay(1000);
  508.  
  509. #if defined(ENET_PORT)
  510.   telnetDisconnect();
  511. #endif
  512.  
  513.   //225 GOTO20
  514.   goto line20;
  515.  
  516.   //250 'Userlog
  517. line255:
  518.   //255 A$="List of Users":GOSUB1055:PRINT"Users on system:"NM:IFNM=0THEN115ELSEA=1
  519.   strncpy_P(aStr, PSTR("List of Users"), INPUT_SIZE);
  520.   gosub1055();
  521.   printSemi(F("Users on system:"));
  522.   print(nm);
  523.   if (nm==0)
  524.   {
  525.     goto line115;
  526.   }
  527.   else
  528.   {
  529.     a = 1;
  530.   }
  531. line260:
  532.   //260 A$=NM$(A):PRINTLEFT$(A$,INSTR(A$,"\")-1)TAB(29)RIGHT$(A$,1)
  533.   strncpy(aStr, nmArray[a], INPUT_SIZE);
  534.   {
  535.     char tempStr[NAME_SIZE+1];         // Add room for NULL.
  536.     strncpy(tempStr, aStr, NAME_SIZE+1);
  537.     tempStr[instr(tempStr, "\\")-1] = '\0';
  538.     printSemi(tempStr);
  539.   }
  540.   printTab(29);
  541.   print(right(aStr,1));
  542.  
  543.   //265 IF(A/10)=INT(A/10)THENPRINT"C-ontinue or S-top :";:UC=1:GOSUB1005:IFLEFT$(A$,1)="S"THEN275
  544.   if (a % 10 == 9)
  545.   {
  546.     printSemi(F("C-ontinue or S-top :"));
  547.     uc = 1;
  548.     //gosub1005();
  549.     if (gosub1005()==255) goto line20;
  550.     if (aStr[0]=='S') goto line275;
  551.   }
  552.  
  553.   //270 A=A+1:IFA<=NM THEN260
  554.   a = a + 1;
  555.   if (a<=nm) goto line260;
  556.  
  557. line275:
  558.   //275 PRINT"*End of Userlog*":GOTO115
  559.   print(F("*End of Userlog*"));
  560.   goto line115;
  561.  
  562.   //300 'Post Msg
  563. line305:
  564.   //305 IFMS=20THENPRINT"One moment, making room...":FORA=0TO18:FORB=0TO10:MS$(A,B)=MS$(A+1,B):NEXTB:NEXTA:MS=19
  565.   if (ms==MAX_MSGS+1)
  566.   {
  567.     print(F("One moment, making room..."));
  568.     for(a=0; a<=MAX_MSGS-1; a++)
  569.     {
  570.       for(b=0; b<=MAX_LINE; b++)
  571.       {
  572.         strncpy(msArray[a][b], msArray[a+1][b], INPUT_SIZE);
  573.       }
  574.     }
  575.     ms = MAX_MSGS;
  576.   }
  577.   //310 CLS:PRINTCL$"This will be message #"MS+1:FORA=0TO10:MS$(MS,A)="":NEXTA:F$=NM$
  578.   cls();
  579.   print((__FlashStringHelper*)clStr);
  580.   printSemi(F("This will be message #"));
  581.   print(ms+1);
  582.   for (a=0; a<=MAX_LINE; a++)
  583.   {
  584.     msArray[ms][a][0] = '\0';
  585.   }
  586.   strncpy(fStr, nmStr, NAME_SIZE);
  587.  
  588.   //315 PRINT"From :"F$:PRINT"To   :";:UC=1:GOSUB1005:A$=LEFT$(A$,20):T$=A$:IFA$=""THEN115
  589.   printSemi(F("From :"));
  590.   print(fStr);
  591.   printSemi(F("To   :"));
  592.   uc = 1;
  593.   //gosub1005();
  594.   if (gosub1005()==255) goto line20;
  595.   aStr[NAME_SIZE] = '\0';
  596.   strncpy(tStr, aStr, TO_SIZE);
  597.   if (aStr[0]=='\0') goto line115;
  598.  
  599.   //320 PRINT"Is this message private? ";:UC=1:GOSUB1005:IFLEFT$(A$,1)="Y"THENS$="*E-Mail*":GOTO330
  600.   printSemi(F("Is this message private? "));
  601.   uc = 1;
  602.   //gosub1005();
  603.   if (gosub1005()==255) goto line20;
  604.   if (aStr[0]=='Y')
  605.   {
  606.     strncpy_P(sStr, PSTR("*E-Mail*"), SB_SIZE);
  607.     goto line330;
  608.   }
  609.  
  610.   //325 PRINT"Subj :";:UC=1:GOSUB1005:A$=LEFT$(A$,18):S$=A$:IFA$=""THEN115
  611.   printSemi(F("Subj :"));
  612.   uc = 1;
  613.   //gosub1005();
  614.   if (gosub1005()==255) goto line20;
  615.   aStr[SB_SIZE-1] = '\0';
  616.   strncpy(sStr, aStr, SB_SIZE);
  617.   if (aStr[0]=='\0') goto line115;
  618.  
  619. line330:
  620.   //330 PRINT"Enter up to 10 lines, 64 chars. [ENTER] on a blank line to end.":A=0
  621.   printSemi(F("Enter up to"));
  622.   printSemi(MAX_LINE);
  623.   printSemi(F("lines,"));
  624.   printSemi(INPUT_SIZE);
  625.   print(F("chars. [ENTER] on a blank line to end."));
  626.   a = 0;
  627.  
  628. line335:
  629.   //335 A=A+1:PRINTUSING"##>";A;:GOSUB1005:MS$(MS,A)=A$:IFA$=""THENA=A-1:GOTO345ELSEIFA<10THEN335
  630.   a = a + 1;
  631.   printUsingSemi("##>", a);
  632.   //gosub1005();
  633.   if (gosub1005()==255) goto line20;
  634.   strncpy(msArray[ms][a], aStr, INPUT_SIZE);
  635.   if (aStr[0]=='\0')
  636.   {
  637.     a = a - 1;
  638.     goto line345;
  639.   }
  640.   else if (a<MAX_LINE) goto line335;
  641.  
  642. line340:
  643.   //340 PRINT"*Message Buffer Full*"
  644.   print(F("*Message Buffer Full*"));
  645.  
  646. line345:
  647.   //345 PRINT"A-bort, C-ont, E-dit, L-ist, or S-ave Message? ";:UC=1:GOSUB1005:A$=LEFT$(A$,1):IFA$=""THEN345
  648.   printSemi(F("A-bort, C-ont, E-dit, L-ist, or S-ave Message? "));
  649.   uc = 1;
  650.   //gosub1005();
  651.   if (gosub1005()==255) goto line20;
  652.   aStr[1] = '\0';
  653.   if (aStr[0]=='\0') goto line345;
  654.  
  655.   //350 LN=INSTR("ACELS",A$):IFLN=0THEN345ELSEONLN GOTO385,355,360,375,380
  656.   ln = instr("ACELS", aStr);
  657.   if (ln==0) goto line345;
  658.   if (ln==1) goto line385;
  659.   if (ln==2) goto line355;
  660.   if (ln==3) goto line360;
  661.   if (ln==4) goto line375;
  662.   if (ln==5) goto line380;
  663.  
  664. line355:
  665.   //355 IFA<10THENPRINT"Continue your message:":GOTO335ELSE340
  666.   if (a<MAX_LINE)
  667.   {
  668.     printSemi(F("Continue your message:"));
  669.     goto line335;
  670.   }
  671.   else goto line340;
  672.  
  673. line360:  
  674.   //360 PRINT"Edit line 1 -"A":";:GOSUB1005:LN=VAL(LEFT$(A$,2)):IFLN<1ORLN>A THEN345
  675.   printSemi(F("Edit line 1 -"));
  676.   printSemi(a);
  677.   printSemi(F(":"));
  678.   //gosub1005();
  679.   if (gosub1005()==255) goto line20;
  680.   aStr[2] = '\0';
  681.   ln = atoi(aStr);
  682.   if (ln<1 || ln>a) goto line345;
  683.  
  684.   //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*"
  685.   print(F("Line currently reads:"));
  686.   print(msArray[ms][ln]);
  687.   print(F("Enter new line:"));
  688.   //gosub1005();
  689.   if (gosub1005()==255) goto line20;
  690.   aStr[INPUT_SIZE] = '\0'; // 1.4
  691.   if (aStr[0]=='\0')
  692.   {
  693.     print(F("*Unchanged*"));
  694.   }
  695.   else
  696.   {
  697.     strncpy(msArray[ms][ln], aStr, INPUT_SIZE);
  698.     print(F("*Corrected*"));
  699.   }
  700.  
  701.   //370 GOTO360
  702.   goto line360;
  703.  
  704. line375:
  705.   //375 CLS:PRINTCL$"Message Reads:":FORB=1TOA:PRINTUSING"##>";B;:PRINTMS$(MS,B):NEXTB:GOTO345
  706.   cls();
  707.   print((__FlashStringHelper*)clStr);
  708.   print(F("Message Reads:"));
  709.   for (b=1; b<=a; b++)
  710.   {
  711.     printUsingSemi("##>", b);
  712.     print(msArray[ms][b]);
  713.   }
  714.   goto line345;
  715.  
  716. line380:
  717.   //380 MS$(MS,0)=T$+"\"+F$+"\"+S$:MS=MS+1:PRINT"*Message"MS"stored*":GOTO115
  718.   strcpy(msArray[ms][0], tStr);
  719.   strcat_P(msArray[ms][0], PSTR("\\"));
  720.   strcat(msArray[ms][0], fStr);
  721.   strcat_P(msArray[ms][0], PSTR("\\"));
  722.   strcat(msArray[ms][0], sStr);
  723.   ms = ms + 1;
  724.   printSemi(F("*Message"));
  725.   printSemi(ms);
  726.   print(F("stored*"));
  727.   goto line115;
  728.  
  729. line385:
  730.   //385 PRINT"*Message Aborted*":GOTO115
  731.   print(F("*Message Aborted*"));
  732.   goto line115;
  733.  
  734.   //400 'Read Msg
  735. line405:
  736.   //405 IFMS=0THENPRINT"The message base is empty.":GOTO115
  737.   if (ms==0)
  738.   {
  739.     print(F("The message base is empty."));
  740.     goto line115;
  741.   }
  742.  
  743.   //410 CLS:PRINTCL$
  744.   cls();
  745.   print((__FlashStringHelper*)clStr);
  746.  
  747. line415:
  748.   //415 PRINT"Read Message 1 -"MS":";:GOSUB1005:A=VAL(LEFT$(A$,2)):IFA<1ORA>MS THEN115
  749.   printSemi(F("Read Message 1 -"));
  750.   printSemi(ms);
  751.   printSemi(F(":"));
  752.   //gosub1005();
  753.   if (gosub1005()==255) goto line20;
  754.   aStr[2] = '\0';
  755.   a = atoi(aStr);
  756.   if (a<1 || a>ms) goto line115;
  757.  
  758.   //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)
  759.   strncpy(aStr, msArray[a-1][0], INPUT_SIZE);
  760.   b = instr(aStr, "\\");
  761.   c = instr(b+1, aStr, "\\");
  762.   strncpy(tStr, aStr, b-1);
  763.   tStr[b-1] = '\0';
  764.   strncpy(fStr, (aStr-1)+b+1, c-b-1);
  765.   fStr[c-b-1] = '\0'; // FIXTHIS - max copy sizes here?
  766.   strncpy(sStr, right(aStr, strlen(aStr)-c), SB_SIZE);
  767.  
  768.   //425 IFS$="*E-Mail*"ANDLV<8THENIFNM$<>T$ANDNM$<>F$THENPRINT"That message is private.":GOTO415
  769.   if (strcmp(sStr, "*E-Mail*")==0 && lv<8)
  770.   {
  771.     if (strcmp(nmStr, tStr)!=0 && strcmp(nmStr, fStr)!=0)
  772.     {
  773.       print(F("That message is private."));
  774.       goto line415;
  775.     }
  776.   }
  777.  
  778.   //430 CLS:PRINTCL$"Message #"A:PRINT"From :"F$:PRINT"To   :"T$:PRINT"Subj :"S$:PRINT:B=0
  779.   cls();
  780.   print((__FlashStringHelper*)clStr);
  781.   printSemi(F("Message #"));
  782.   print(a);
  783.   printSemi(F("From :"));
  784.   print(fStr);
  785.   printSemi(F("To   :"));
  786.   print(tStr);
  787.   printSemi(F("Subj :"));
  788.   print(sStr);
  789.   print();
  790.   b = 0;
  791.  
  792. line435:
  793.   //435 B=B+1:PRINTMS$(A-1,B):IFMS$(A-1,B)=""THEN440ELSEIFB<10THEN435
  794.   b = b + 1;
  795.   print(msArray[a-1][b]);
  796.   if (msArray[a-1][b][0]=='\0')
  797.   {
  798.     goto line440;
  799.   }
  800.   else if (b<MAX_LINE) goto line435;
  801.  
  802. line440:
  803.   //440 PRINT"*End of Message*":GOTO415
  804.   print(F("*End of Message*"));
  805.   goto line415;
  806.  
  807.   //450 'Scan Titles
  808. line455:
  809.   //455 IFMS=0THENPRINT"The message base is empty.":GOTO115
  810.   if (ms==0)
  811.   {
  812.     print(F("The message base is empty."));
  813.     goto line115;
  814.   }
  815.  
  816.   //460 CLS:PRINTCL$"Message Titles:":A=0
  817.   cls();
  818.   print((__FlashStringHelper*)clStr);
  819.   print(F("Message Titles:"));
  820.   a = 0;
  821.  
  822. line465:
  823.   //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<MS THEN465
  824.   strncpy(aStr, msArray[a][0], INPUT_SIZE);
  825.   printUsingSemi("[##] SB: ", a+1);
  826.   b = instr(aStr, "\\");
  827.   c = instr(b+1, aStr, "\\");
  828.   print(right(aStr, strlen(aStr)-c));
  829.   printTab(5);
  830.   printSemi(F("TO: "));
  831.   {
  832.     char tempStr[TO_SIZE];
  833.     strncpy(tempStr, aStr, b-1);
  834.     tempStr[b-1] = '\0';
  835.     print(tempStr);
  836.   }
  837.   a = a + 1;
  838.   if (a<ms) goto line465;
  839.  
  840.   //470 PRINT"*End of Messages*":GOTO115
  841.   print(F("*End of Messages*"));
  842.   goto line115;
  843.  
  844.   //500 '%SYSOP MENU%
  845. line505:
  846.   //505 IFLV<9THENA$="Z":GOTO130
  847.   if (lv<9)
  848.   {
  849.     strncpy_P(aStr, PSTR("Z"), INPUT_SIZE);
  850.     goto line130;
  851.   }
  852.  
  853.   //510 PRINT"PASSWORD?";:GOSUB1005:IFA$<>"?DROWSSAP"THENPRINT"Thank You!":GOTO115
  854.   printSemi(F("PASSWORD?"));
  855.   //gosub1005();
  856.   if (gosub1005()==255) goto line20;
  857.   if (strcmp(aStr, "?DROWSSAP")!=0)
  858.   {
  859.     print(F("Thank You!"));
  860.     goto line115;
  861.   }
  862.  
  863.   //515 PRINT"Abort BBS? YES or NO? ";:UC=1:GOSUB1005:IFA$<>"YES"THEN115
  864.   printSemi(F("Abort BBS? YES or NO? "));
  865.   uc = 1;
  866.   //gosub1005();
  867.   if (gosub1005()==255) goto line20;
  868.   if (strcmp(aStr, "YES")!=0) goto line115;
  869.  
  870.   //520 GOSUB605:STOP
  871.   gosub605();
  872.   return;
  873. } // end of allram()
  874.  
  875. /*---------------------------------------------------------------------------*/
  876. // Subroutines (formerly GOSUBs)
  877. //
  878. //550 '%LOAD%
  879. void gosub555()
  880. {
  881.   //555 PRINT"%LOAD% [ENTER] WHEN READY";:GOSUB1005
  882.   printSemi(F("%LOAD% [ENTER] WHEN READY"));
  883.   //gosub1005();
  884.   print();
  885.   if (aStr[0]=='!') return;
  886.  
  887.   //560 OPEN"I",#-1,"USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
  888.   loadUserlog();
  889.  
  890.   //565 OPEN"I",#-1,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  891.   loadMsgBase();
  892. }
  893.  
  894. //600 '%SAVE%
  895. void gosub605()
  896. {
  897.   //605 PRINT"%SAVE% [ENTER] WHEN READY";:GOSUB1005:MOTORON:FORA=0TO999:NEXTA
  898.   printSemi(F("%SAVE% [ENTER] WHEN READY"));
  899.   gosub1005();
  900.    
  901.   //610 OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
  902.   saveUserlog();
  903.  
  904.   //615 OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  905.   saveMsgBase();
  906. }
  907.  
  908. //1000 'User Input
  909. #define CR         13
  910. #define INBUF_SIZE 64
  911. byte gosub1005()
  912. {
  913.   byte ch; // Used only here, so we can make it local.
  914.   byte count;
  915.  
  916.   //1005 LINEINPUTA$:A$=LEFT$(A$,64):IFUC=0ORA$=""THENRETURN
  917.   count = lineinput(aStr, INPUT_SIZE);
  918.   aStr[INPUT_SIZE] = '\0';
  919.   if ((uc==0) || (aStr[0]=='\0')) return count;
  920.  
  921.   //1010 FORC=1TOLEN(A$):CH=ASC(MID$(A$,C,1)):IFCH>96THENMID$(A$,C,1)=CHR$(CH-32)
  922.   for (c=0; c<strlen(aStr); c++)
  923.   {
  924.     ch = aStr[1];
  925.     if (ch>96) aStr[1] = ch-32;    
  926.     //1015 IFCH=92THENMID$(A$,C,1)="/"
  927.     if (ch==92) aStr[1] = '/';
  928.     //1020 NEXTC:UC=0:RETURN
  929.   }
  930.   uc = 0;
  931.    
  932.   return count;
  933. }
  934.  
  935. //1050 'Function Border
  936. void gosub1055()
  937. {
  938.   //1055 CLS:PRINTCL$BR$:PRINTTAB((32-LEN(A$))/2)A$:PRINTBR$:RETURN
  939.   cls();
  940.   print((__FlashStringHelper*)clStr);
  941.   print((__FlashStringHelper*)brStr);
  942.   printTab((32-strlen(aStr))/2);
  943.   print(aStr);
  944.   print((__FlashStringHelper*)brStr);
  945. }
  946.  
  947. /*---------------------------------------------------------------------------*/
  948. // The following functions mimic some of the Extended Color BASIC commands.
  949.  
  950. // CLS
  951. // Clear the screen.
  952. void cls()
  953. {
  954.   print();
  955.   print(F("--------------CLS--------------"));
  956. }
  957.  
  958. // SOUND tone, duration
  959. // On the CoCo, tone (1-255), duration (1-255; 15=1 second).
  960. void sound(byte tone, byte duration)
  961. {
  962.   Serial.write(0x07);   // BEL
  963.   delay(duration*66.6); // Estimated delay.
  964. }
  965.  
  966. /*---------------------------------------------------------------------------*/
  967. // String functions.
  968.  
  969. // STRING$(length, charcode)
  970. // Generate a string of length charcode chracters.
  971. void string(byte length, byte charcode)
  972. {
  973.   int i;
  974.  
  975.   for (i=0; i<length; i++) printCharSemi(charcode);
  976. }
  977.  
  978. // RIGHT$(str, length)
  979. // Note: Modifies the passed in string, which is okay for our purpose but
  980. // would not be suitable as a generic replacement for RIGHT$.
  981. char *right(char *str, byte length)
  982. {
  983.   return &str[strlen(str)-length];
  984. }
  985.  
  986. // INSTR(first, str, substr)
  987. // Starting at pos, return position of substr in str, or 0 if not found.
  988. int instr(byte pos, char *str, char *substr)
  989. {
  990.   if (pos<1) return 0;
  991.   return instr(aStr+pos, substr) + pos;
  992. }
  993. int instr(char *str, char *substr)
  994. {
  995.   char *ptr;
  996.  
  997.   ptr = strstr(str, substr);
  998.   if (ptr==NULL) return 0; // No match?
  999.   if (ptr==&str[strlen(str)]) return 0; // Matched the \0 at end of line?
  1000.   return ptr-str+1;
  1001. }
  1002.  
  1003. /*---------------------------------------------------------------------------*/
  1004. // Input functions.
  1005.  
  1006. #ifdef ENET_PORT
  1007. byte lineinput(char *cmdLine, byte len)
  1008. {
  1009.   return telnetInput(telnetClient, cmdLine, len);
  1010. }
  1011. #else
  1012. // LINE INPUT str
  1013. // Read string up to len bytes. This code comes from my Hayes AT Command
  1014. // parser, so the variables are named differently.
  1015. #define CR           13
  1016. #define BEL          7
  1017. #define BS           8
  1018. #define CAN          24
  1019. byte lineinput(char *cmdLine, byte len)
  1020. {
  1021.   int     ch;
  1022.   byte    cmdLen = 0;
  1023.   boolean done;
  1024.  
  1025.   done = false;
  1026.   while(!done)
  1027.   {
  1028.     //ledBlink();
  1029.  
  1030.     ch = -1; // -1 is no data available
  1031.  
  1032.     if (Serial.available()>0)
  1033.     {
  1034.       ch = Serial.read();
  1035.     }
  1036.     else
  1037.     {
  1038.       continue; // No data. Go back to the while()...
  1039.     }
  1040.     switch(ch)
  1041.     {
  1042.     case -1: // No data available.
  1043.       break;
  1044.  
  1045.     case CR:
  1046.       print();
  1047.       cmdLine[cmdLen] = '\0';
  1048.       done = true;
  1049.       break;
  1050.  
  1051.       /*case CAN:
  1052.        print(F("[CAN]"));
  1053.        cmdLen = 0;
  1054.        break;*/
  1055.  
  1056.     case BS:
  1057.       if (cmdLen>0)
  1058.       {
  1059.         printCharSemi(BS);
  1060.         printSemi(F(" "));
  1061.         printCharSemi(BS);
  1062.         cmdLen--;
  1063.       }
  1064.       break;
  1065.  
  1066.     default:
  1067.       // If there is room, store any printable characters in the cmdline.
  1068.       if (cmdLen<len)
  1069.       {
  1070.         if ((ch>31) && (ch<127)) // isprint(ch) does not work.
  1071.         {
  1072.           printCharSemi(ch);
  1073.           cmdLine[cmdLen] = ch; //toupper(ch);
  1074.           cmdLen++;
  1075.         }
  1076.       }
  1077.       else
  1078.       {
  1079.         printCharSemi(BEL); // Overflow. Ring 'dat bell.
  1080.       }
  1081.       break;
  1082.     } // end of switch(ch)
  1083.   } // end of while(!done)
  1084.  
  1085.   return cmdLen;
  1086. }
  1087. #endif
  1088.  
  1089. // INKEY$
  1090. // Return character waiting (if any) from standard input (not ethernet).
  1091. char inkey()
  1092. {
  1093.   if (Serial.available()==0) return 0;
  1094.   return Serial.read();
  1095. }
  1096.  
  1097. /*---------------------------------------------------------------------------*/
  1098.  
  1099. // File I/O
  1100. // Ideally, I would have created wrappers for the OPEN, READ, CLOSE commands,
  1101. // but I was in a hurry, so...
  1102.  
  1103. //SD Card routines
  1104. #if defined(SD_PIN)
  1105. #define TEMP_SIZE 4
  1106. #define FNAME_MAX (8+1+3+1)
  1107. boolean initSD()
  1108. {
  1109.   static bool sdInit = false;
  1110.  
  1111.   if (sdInit==true) return true;
  1112.  
  1113.   printSemi(F("Initializing SD card..."));
  1114.   pinMode(SD_PIN, OUTPUT);
  1115.   if (!SD.begin(SD_PIN))
  1116.   {
  1117.     print(F("initialization failed."));
  1118.     return false;
  1119.   }
  1120.   print(F("initialization done."));
  1121.   sdInit = true;
  1122.  
  1123.   return true;
  1124. }
  1125. #endif
  1126.  
  1127. //560 OPEN"I",#-1,"USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
  1128. void loadUserlog()
  1129. {
  1130. #if defined(SD_PIN)
  1131.   File myFile;
  1132.   char tempStr[TEMP_SIZE];
  1133.   char filename[FNAME_MAX];
  1134.  
  1135.   if (!initSD()) return;
  1136.  
  1137.   strncpy_P(filename, PSTR("USERLOG"), FNAME_MAX);
  1138.  
  1139.   myFile = SD.open(filename, FILE_READ);
  1140.  
  1141.   if (myFile)
  1142.   {
  1143.     printSemi(filename);
  1144.     print(F(" opened."));
  1145.     fileReadln(myFile, tempStr, TEMP_SIZE);
  1146.     cl = atoi(tempStr);
  1147.     Serial.print(F("cl = "));
  1148.     Serial.println(cl);
  1149.     fileReadln(myFile, tempStr, TEMP_SIZE);
  1150.     nm = atoi(tempStr);
  1151.     Serial.print(F("nm = "));
  1152.     Serial.println(nm);
  1153.     for (a=0; a<=nm; a++)
  1154.     {
  1155.       fileReadln(myFile, nmArray[a], ULOG_SIZE);
  1156.       Serial.print(a);
  1157.       Serial.print(F(". "));
  1158.       Serial.println(nmArray[a]);
  1159.     }  
  1160.     myFile.close();
  1161.   }
  1162.   else
  1163.   {
  1164.     printSemi(F("Error opening "));
  1165.     print(filename);
  1166.   }
  1167. #else  
  1168.   print(F("(USERLOG would be loaded from tape here.)"));
  1169. #endif
  1170. }
  1171.  
  1172. //565 OPEN"I",#-1,"MSG BASE":INPUT#-1,MS:FORA=0TOMS-1:FORB=0TO10:INPUT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  1173. void loadMsgBase()
  1174. {
  1175. #if defined(SD_PIN)
  1176.   File myFile;
  1177.   char tempStr[TEMP_SIZE];
  1178.   char filename[FNAME_MAX];
  1179.  
  1180.   if (!initSD()) return;
  1181.  
  1182.   strncpy_P(filename, PSTR("MSGBASE"), FNAME_MAX);
  1183.  
  1184.   myFile = SD.open(filename, FILE_READ);
  1185.   if (myFile)
  1186.   {
  1187.     printSemi(filename);
  1188.     print(F(" opened."));
  1189.     fileReadln(myFile, tempStr, TEMP_SIZE);
  1190.     ms = atoi(tempStr);
  1191.     Serial.print("ms = ");
  1192.     Serial.println(ms);
  1193.     for (a=0; a<=ms-1; a++)
  1194.     {
  1195.       for (b=0; b<=MAX_LINE; b++)
  1196.       {
  1197.         fileReadln(myFile, msArray[a][b], INPUT_SIZE);
  1198.         Serial.print(F("msArray["));
  1199.         Serial.print(a);
  1200.         Serial.print(F("]["));
  1201.         Serial.print(b);
  1202.         Serial.print(F("] = "));
  1203.         Serial.println(msArray[a][b]);
  1204.       }
  1205.     }  
  1206.     myFile.close();
  1207.   }
  1208.   else
  1209.   {
  1210.     printSemi(F("Error opening "));
  1211.     print(filename);
  1212.   }
  1213. #else
  1214.   print(F("(MSGBASE would be loaded from tape here.)"));
  1215. #endif
  1216. }
  1217.  
  1218. #if defined(SD_PIN)
  1219. //byte fileReadln(File myFile, char *buffer, byte count)
  1220. {
  1221.   char ch;
  1222.   int  pos;
  1223.  
  1224.   pos = 0;
  1225.   while(myFile.available() && pos<count)
  1226.   {
  1227.     ch = myFile.read();
  1228.     if (ch==CR)
  1229.     {
  1230.       buffer[pos] = '\0';
  1231.       break;
  1232.     }
  1233.     if (ch>=32)
  1234.     {
  1235.       //Serial.print(ch);
  1236.       buffer[pos] = ch;
  1237.       pos++;
  1238.     }
  1239.   }
  1240.   if (pos>=count) buffer[pos] = '\0';
  1241.   //Serial.println();
  1242.   return pos;
  1243. }
  1244. #endif
  1245.  
  1246. //610 OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
  1247. void saveUserlog()
  1248. {
  1249. #if defined(SD_PIN)
  1250.   File myFile;
  1251.   char filename[FNAME_MAX];
  1252.  
  1253.   if (!initSD()) return;
  1254.  
  1255.   strncpy_P(filename, PSTR("USERLOG"), FNAME_MAX);
  1256.  
  1257.   if (SD.exists(filename)==true) SD.remove(filename);
  1258.  
  1259.   myFile = SD.open(filename, FILE_WRITE);
  1260.   if (myFile)
  1261.   {
  1262.     printSemi(filename);
  1263.     print(F(" created."));
  1264.     myFile.println(cl);
  1265.     Serial.print(F("cl = "));
  1266.     Serial.println(cl);
  1267.     myFile.println(nm);
  1268.     Serial.print(F("nm = "));
  1269.     Serial.println(nm);
  1270.     for (a=0; a<=nm; a++)
  1271.     {
  1272.       myFile.println(nmArray[a]);
  1273.       Serial.print(a);
  1274.       Serial.print(F(". "));
  1275.       Serial.println(nmArray[a]);
  1276.     }
  1277.     myFile.close();
  1278.   }
  1279.   else
  1280.   {
  1281.     print(F("Error creating file."));
  1282.   }
  1283. #else
  1284.   print(F("save USERLOG"));
  1285. #endif
  1286. }
  1287.  
  1288. //615 OPEN"O",#-1,"MSG BASE":PRINT#-1,MS:FORA=0TOMS-1:FORB=0TO10:PRINT#-1,MS$(A,B):NEXTB:NEXTA:CLOSE:RETURN
  1289. void saveMsgBase()
  1290. {
  1291. #if defined(SD_PIN)
  1292.   File myFile;
  1293.   char filename[FNAME_MAX];
  1294.  
  1295.   if (!initSD()) return;
  1296.  
  1297.   strncpy_P(filename, PSTR("MSGBASE"), FNAME_MAX);
  1298.  
  1299.   if (SD.exists(filename)==true) SD.remove(filename);
  1300.  
  1301.   myFile = SD.open(filename, FILE_WRITE);
  1302.   if (myFile)
  1303.   {
  1304.     printSemi(filename);
  1305.     print(F(" created."));
  1306.     myFile.println(ms);
  1307.     for (a=0; a<=ms-1; a++)
  1308.     {
  1309.       for (b=0; b<=MAX_LINE; b++)
  1310.       {
  1311.         myFile.println(msArray[a][b]);
  1312.         Serial.print(F("msArray["));
  1313.         Serial.print(a);
  1314.         Serial.print(F("]["));
  1315.         Serial.print(b);
  1316.         Serial.print(F("] = "));
  1317.         Serial.println(msArray[a][b]);
  1318.       }
  1319.     }
  1320.     myFile.close();
  1321.   }
  1322.   else
  1323.   {
  1324.     print(F("Error creating file."));
  1325.   }
  1326. #else
  1327.   print(F("save MSGBASE"));
  1328. #endif
  1329. }
  1330.  
  1331. /*---------------------------------------------------------------------------*/
  1332. // Print (output) routines.
  1333.  
  1334. // For TAB to work, we need to track where we think we are on the line.
  1335. byte tabPos = 0;
  1336.  
  1337. // We want to simulate the following:
  1338. // PRINT "HELLO"  -- string, with carraige return at end of line
  1339. // PRINT A        -- number (space before and after), with carraige return
  1340. // PRINT "HELLO"; -- no carraige return
  1341. // PRINT TAB(5);  -- tab to position 5
  1342. // PRINT "A","B"  -- tab to column 16 (on 32-column screen)
  1343.  
  1344. // Due to various types of strings on Arduino (in memory or Flash), we will
  1345. // have to duplicate some functions to have versions that take the other
  1346. // types of strings.
  1347.  
  1348. // printTypeSemi() routines will not put a carraige return at the end.
  1349.  
  1350. // PRINT TAB(column);
  1351. void printTab(byte column)
  1352. {
  1353.   while(tabPos<column)
  1354.   {
  1355.     printSemi(F(" ")); // Print, and increment tab position.
  1356.   }
  1357. }
  1358.  
  1359. // PRINT,
  1360. // NOTE: DECB doesn't add a carraige return after a comma.
  1361. void printComma()
  1362. {
  1363.   printTab(tabPos + (16-(tabPos % 16)));
  1364. }
  1365.  
  1366. // PRINT "STRING";
  1367. // For normal strings in RAM.
  1368. void printSemi(const char *string)
  1369. {
  1370.   tabPos = tabPos + Serial.print(string);
  1371. #if defined(ENET_PORT)
  1372.   telnetClient.print(string);
  1373. #endif
  1374. }
  1375. // For strings in Flash.
  1376. void printSemi(const __FlashStringHelper *string)
  1377. {
  1378.   tabPos = tabPos + Serial.print(string);
  1379. #if defined(ENET_PORT)
  1380.   telnetClient.print(string);
  1381. #endif
  1382. }
  1383. // For printing a byte as a number (0-255).
  1384. void printSemi(byte num)
  1385. {
  1386.   Serial.print(F(" "));
  1387.   tabPos = tabPos + Serial.print(num);
  1388.   Serial.print(F(" "));
  1389.   tabPos = tabPos + 2;
  1390. #if defined(ENET_PORT)
  1391.   telnetClient.print(F(" "));
  1392.   telnetClient.print(num);
  1393.   telnetClient.print(F(" "));
  1394. #endif
  1395. }
  1396. // For printing a single character.
  1397. void printCharSemi(char ch)
  1398. {
  1399.   tabPos = tabPos + Serial.print(ch);
  1400. #if defined(ENET_PORT)
  1401.   telnetClient.print(ch);
  1402. #endif
  1403. }
  1404. // For printing a byte as a number with no spaces.
  1405. void printNumSemi(byte num)
  1406. {
  1407.   tabPos = tabPos + Serial.print(num);
  1408. #if defined(ENET_PORT)
  1409.   telnetClient.print(num);
  1410. #endif
  1411. }
  1412.  
  1413. // PRINT
  1414. void print(void)
  1415. {
  1416.   Serial.println();
  1417. #if defined(ENET_PORT)
  1418.   telnetClient.println();
  1419. #endif
  1420. }
  1421.  
  1422. // PRINT "STRING"
  1423. void print(const char *string)
  1424. {
  1425.   Serial.println(string);
  1426.   tabPos = 0;
  1427. #if defined(ENET_PORT)
  1428.   telnetClient.println(string);
  1429. #endif
  1430. }
  1431. void print(const __FlashStringHelper *string)
  1432. {
  1433.   Serial.println(string);
  1434.   tabPos = 0;
  1435. #if defined(ENET_PORT)
  1436.   telnetClient.println(string);
  1437. #endif
  1438. }
  1439.  
  1440. // PRINT I
  1441. void print(int num)
  1442. {
  1443.   Serial.print(F(" "));
  1444.   Serial.println(num);
  1445.   tabPos = 0;
  1446. #if defined(ENET_PORT)
  1447.   telnetClient.print(F(" "));
  1448.   telnetClient.println(num);
  1449. #endif
  1450. }
  1451.  
  1452. // PRINT USING(format, number);
  1453. // NOTE: This only emulates printing positive integers, which is the
  1454. // only way it is used by this program.
  1455. void printUsingSemi(char *format, byte num)
  1456. {
  1457.   byte i;
  1458.   byte fmtDigits;
  1459.   byte numDigits;
  1460.   byte tempNum;
  1461.  
  1462.   i = 0;
  1463.   while(format[i]!='\0')
  1464.   {
  1465.     if (format[i]!='#') {
  1466.       printCharSemi(format[i]);
  1467.       i++;
  1468.       continue;
  1469.     }
  1470.     else
  1471.     {
  1472.       // Start counting the run of #s.
  1473.       // Find end of #'s to know how many to use.
  1474.       fmtDigits = 0;
  1475.       while(format[i]=='#' && format[i]!='\0')
  1476.       {
  1477.         fmtDigits++;
  1478.         i++;
  1479.       }
  1480.       // Now we know how many # (digits).
  1481.       tempNum = num;
  1482.       numDigits = 1;
  1483.       while(tempNum>10)
  1484.       {
  1485.         tempNum = tempNum/10;
  1486.         numDigits++;
  1487.       }
  1488.       while(numDigits<fmtDigits)
  1489.       {
  1490.         printSemi(F(" "));
  1491.         fmtDigits--;
  1492.       }
  1493.       printNumSemi(num);
  1494.     }
  1495.   }
  1496. }
  1497.  
  1498. /*---------------------------------------------------------------------------*/
  1499. // Show some stuff functions.
  1500.  
  1501. // Emit some configuration information.
  1502. void showConfig()
  1503. {
  1504.   print((__FlashStringHelper*)brStr);
  1505.   print(F("*ALL RAM* Configuration:"));
  1506.   printSemi(F("Userlog size :"));
  1507.   print(MAX_USERS+1);
  1508.   printSemi(F("Input size   :"));
  1509.   print(INPUT_SIZE);
  1510.   printSemi(F("Username size:"));
  1511.   print(NAME_SIZE);
  1512.   printSemi(F("Password size:"));
  1513.   print(PSWD_SIZE);
  1514.   printSemi(F("Msg base size:"));
  1515.   print(MAX_MSGS+1);
  1516.   printSemi(F("Message lines:"));
  1517.   print(MAX_LINE+1);
  1518. #if defined(SD_PIN)
  1519.   print(F("SD card      : Enabled"));
  1520. #endif
  1521. #if defined(ENET_PORT)
  1522.   print(F("Ethernet     : Enabled"));
  1523. #endif
  1524.   printSemi(F("ESTIMATED MEM:"));
  1525.   printSemi(ULOG_MEM + MBASE_MEM);
  1526.   print(F("bytes."));
  1527.   printSemi(F("Free RAM     :"));
  1528.   print(freeRam());
  1529.   print((__FlashStringHelper*)brStr);
  1530. }
  1531.  
  1532. void showLoginMessage()
  1533. {
  1534.   print(F("\nYou are connected to an Arduino UNO R3, a small computer thing you can buy\n"
  1535.     "from Radio Shack for $29.99 (or around $22 online). It has 2K of RAM and\n"
  1536.     "32K of Flash for program storage. It has a SainSmart Ethernet Shield ($17.99)\n"
  1537.     "attached to it. The software running here is a line-by-line port of my\n"
  1538.     "*ALL RAM* BBS program written in 1983. The original Extended Color BASIC\n"
  1539.     "code was converted as literally as possible to Arduino C. It's a travesty.\n"
  1540.     "\n"
  1541.     "This BBS program was designed to run on a cassette based TRS-80 Color\n"
  1542.     "Computer with 32K of RAM. All messages and users were stored in memory.\n"
  1543.     "Obviously, with only 2K of RAM, this is not possible, so this version has\n"
  1544.     "been configured to allow only a few users and a teensy tiny message base\n"
  1545.     "(smaller than Twitter posts!). Enjoy the experiment!\n"
  1546.     "\n"
  1547.     "(If userlog is full, use the Sysop password 'TEST'.)"));
  1548. }
  1549.  
  1550. void showCoCoHeader()
  1551. {
  1552.   cls();
  1553.   print(F("EXTENDED COLOR BASIC 1.1\n"
  1554.     "COPYRIGHT (C) 1982 BY TANDY\n"
  1555.     "UNDER LICENSE FROM MICROSOFT\n"
  1556.     "\n"
  1557.     "OK\n"
  1558.     "CLOAD\"ALLRAM\"\n"
  1559.     "RUN"));
  1560. }
  1561.  
  1562. /*---------------------------------------------------------------------------*/
  1563.  
  1564. // Debug and utility functions.
  1565. //
  1566.  
  1567. unsigned int freeRam() {
  1568.   extern int __heap_start, *__brkval;
  1569.   int v;
  1570.   return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
  1571. }
  1572.  
  1573. void showFreeRam()
  1574. {
  1575.   printSemi(F("Free RAM:"));
  1576.   print(freeRam());
  1577. }
  1578.  
  1579. /*---------------------------------------------------------------------------*/
  1580.  
  1581. void showUserlog()
  1582. {
  1583.   int i;
  1584.   printSemi(F("Users: "));
  1585.   print(nm);
  1586.   for (i=0; i<=nm; i++) {
  1587.     printSemi(i);
  1588.     printSemi(F(". "));
  1589.     print(nmArray[i]);
  1590.   }
  1591. }
  1592.  
  1593. void showMessageBase()
  1594. {
  1595.   int i,j;
  1596.  
  1597.   for (int i=0; i<ms; i++)
  1598.   {
  1599.     for (int j=0; j<=MAX_LINE; j++)
  1600.     {
  1601.       printSemi(F("msArray["));
  1602.       printSemi(i);
  1603.       printSemi(F(","));
  1604.       printSemi(j);
  1605.       printSemi(F("] = "));
  1606.       print(msArray[i][j]);
  1607.     }
  1608.   }
  1609. }
  1610.  
  1611.  
  1612. /*---------------------------------------------------------------------------*/
  1613.  
  1614. //0 REM *ALL RAM* BBS Editor 1.0
  1615. //1 REM   Shareware / (C) 1983
  1616. //2 REM     By Allen Huffman
  1617. //3 REM  110 Champions Dr, #811
  1618. //4 REM     Lufkin, TX 75901
  1619. //5 FORA=0TO8:READA$:POKE1024+A,VAL("&H"+A$):NEXTA:EXEC1024:DATAC6,1,96,BC,1F,2,7E,96,A3
  1620. //10 CLEAR21000:DIMNM$(200),MS$(19,10),A$,F$,S$,T$,NM$,PW$,A,B,C,LN,LV,MS,NM,PR:PR=80
  1621. //15 CLS:PRINTTAB(3)"*ALL RAM* EDITOR COMMANDS:":printING$(32,45)
  1622. //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"
  1623. //25 PRINT@392,"ENTER FUNCTION :"
  1624. //30 A$=INKEY$:IFA$=""THEN30ELSEPRINT@408,A$:LN=VAL(A$):IFLN<1ORLN>8THENSOUND50,1:GOTO25
  1625. //35 SOUND200,1:ONLN GOTO55,105,155,205,255,305,405,40
  1626. //40 STOP
  1627. //50 'Create Userlog
  1628. //55 CLS:PRINTTAB(7)"SYSOP INFORMATION:":printING$(32,45)
  1629. //60 PRINT@128,"SYSOP'S NAME:    (20 CHARACTERS)>";:LINEINPUTA$:IFA$=""ORLEN(A$)>20THENSOUND50,1:GOTO15ELSENM$=A$
  1630. //65 PRINT@192,"PASSWORD    :     (8 CHARACTERS)>";:LINEINPUTA$:IFA$=""ORLEN(A$)>8THENSOUND50,1:GOTO15ELSEPW$=A$
  1631. //70 PRINT@297,"*VERIFY ENTRY*":PRINT:PRINT"NAME :"NM$:PRINT"PSWD :"PW$:PRINT@456,"IS THIS CORRECT?";
  1632. //75 LINEINPUTA$:IFLEFT$(A$,1)<>"Y"THENSOUND50,1:GOTO55
  1633. //80 NM$(0)=NM$+"\"+PW$+"9":GOTO15
  1634. //100 'Load Userlog/Msg Base
  1635. //105 CLS:PRINTTAB(5)"LOAD USERLOG/MSG BASE:":printING$(32,45)
  1636. //110 LINEINPUT" READY TAPE, THEN PRESS ENTER:";A$:PRINT@168,"...ONE MOMENT..."
  1637. //115 OPEN"I",#-1,"USERLOG":PRINT@232,"LOADING  USERLOG":INPUT#-1,CL,NM:FORA=0TONM:INPUT#-1,NM$(A):NEXTA:CLOSE
  1638. //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
  1639. //150 'Save Userlog/Msg Base
  1640. //155 CLS:PRINTTAB(5)"SAVE USERLOG/MSG BASE:":printING$(32,45)
  1641. //160 LINEINPUT" READY TAPE, THEN PRESS ENTER:";A$:PRINT@168,"...ONE MOMENT...":MOTORON:FORA=1TO1000:NEXTA
  1642. //165 PRINT@232,"SAVING   USERLOG":OPEN"O",#-1,"USERLOG":PRINT#-1,CL,NM:FORA=0TONM:PRINT#-1,NM$(A):NEXTA:CLOSE
  1643. //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
  1644. //200 'Print Userlog
  1645. //205 IFNM$(0)=""THENPRINT@454,"*USERLOG NOT LOADED*":SOUND50,1:GOTO25
  1646. //210 CLS:PRINTTAB(9)"PRINT USERLOG:":printING$(32,45)
  1647. //215 LINEINPUT"    PRESS ENTER WHEN READY:";A$:PRINT@169,"...PRINTING..."
  1648. //220 PRINT#-2,TAB((PR-30)/2)"[*ALL RAM* BBS System Userlog]":PRINT#-2,"":PRINT#-2,TAB((PR-46)/2)"[###]  [        NAME        ]  [PASSWORD]  [L]"
  1649. //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))
  1650. //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)
  1651. //235 PRINT@238,A:PRINT#-2,TAB((PR-44)/2)A$:NEXTA:GOTO15
  1652. //250 'Print Messages
  1653. //255 IFMS$(0,0)=""THENPRINT@454,"*MSG BASE NOT LOADED*":SOUND50,1:GOTO25
  1654. //260 CLS:PRINTTAB(8)"PRINT  MESSAGES:":printING$(32,45)
  1655. //265 LINEINPUT"    PRESS ENTER WHEN READY:";A$:PRINT@169,"...PRINTING..."
  1656. //270 PRINT#-2,TAB((PR-30)/2)"[*ALL RAM* BBS System Messages]":PRINT#-2,""
  1657. //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)
  1658. //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
  1659. //285 C=C+1:PRINT#-2,TAB(B)MS$(A,C):IFMS$(A,C)=""THEN290ELSEIFC<10THEN285
  1660. //290 PRINT#-2,"":NEXTA:GOTO15
  1661. //300 'Edit Users
  1662. //305 IFNM$(0)=""THENPRINT@454,"*USERLOG NOT LOADED*":SOUND50,1:GOTO25
  1663. //310 CLS:PRINTTAB(10)"EDIT  USERS:":printING$(32,45):A=0
  1664. //315 PRINT@70,"USERS ON SYSTEM:"NM
  1665. //320 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))
  1666. //325 PRINT@128,"USER #"A:PRINT:PRINT"NAME: "NM$:PRINT"PSWD: "PW$:PRINT"LVL :"LV
  1667. //330 PRINT@320,STRING$(32,45)TAB(4)"D-LET   UP-BACK   J-UMP",TAB(4)"E-DIT   DN-NEXT   M-ENU"
  1668. //335 PRINT@456,"ENTER FUNCTION :"
  1669. //340 A$=INKEY$:IFA$=""THEN340ELSEPRINT@472,A$;:LN=INSTR("DEJM"+CHR$(94)+CHR$(10),A$):IFLN=0THENSOUND50,1:GOTO335
  1670. //345 SOUND200,1:ONLN GOTO350,365,385,15,390,395
  1671. //350 IFA=0THENSOUND1,5:GOTO335
  1672. //355 IFA=NM THENNM=NM-1:A=A-1:GOTO315
  1673. //360 FORB=A TONM:NM$(B)=NM$(B+1):NEXTB:NM=NM-1:GOTO315
  1674. //365 PRINT@198,;:LINEINPUTA$:IFA$=""ORLEN(A$)>20THENPRINT@198,NM$ELSENM$=A$
  1675. //370 PRINT@230,;:LINEINPUTA$:IFA$=""ORLEN(A$)>8THENPRINT@230,PW$ELSEPW$=A$
  1676. //375 PRINT@262,;:LINEINPUTA$:B=VAL(A$):IFB<1ORB>9THENPRINT@261,LV ELSELV=B
  1677. //380 NM$(A)=NM$+"\"+PW$+RIGHT$(STR$(LV),1):GOTO335
  1678. //385 PRINT@456," JUMP TO USER # ";:LINEINPUTA$:B=VAL(A$):IFB<0ORB>NM THENSOUND1,5:GOTO335ELSEA=B:GOTO320
  1679. //390 A=A-1:IFA<0THENA=NM
  1680. //391 GOTO315
  1681. //395 A=A+1:IFA>NM THENA=0
  1682. //396 GOTO315
  1683. //400 'Kill Messages
  1684. //405 IFMS$(0,0)=""THENPRINT@454,"*MSG BASE NOT LOADED*":SOUND50,1:GOTO25
  1685. //410 CLS:PRINTTAB(9)"KILL MESSAGES:":ING$(32,45)
  1686. //415 PRINT@96,"DELETE MESSAGE # 1 -"MS":";:LINEINPUTA$:A=VAL(A$):IFA<1ORA>MS THEN15
  1687. //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)
  1688. //425 PRINT:PRINT"TO: "T$:PRINT"FR: "F$:PRINT"SB: "S$
  1689. //430 PRINT:LINEINPUT"DELETE THIS?";A$:IFLEFT$(A$,1)<>"Y"THEN410
  1690. //435 IFA=MS THENMS=MS-1:GOTO410
  1691. //440 FORB=A-1 TOMS-2:FORC=0TO10:MS$(B,C)=MS$(B+1,C):NEXTC:NEXTB:MS=MS-1:GOTO410
  1692.  
  1693. // End of file.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement