View difference between Paste ID: ZTbVahRz and qZ1USbEt
SHOW: | | - or go back to the newest paste.
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
62+
#include <SD.h>
63
#endif
64
 
65
#if defined(ENET_PORT)
66-
#include
66+
#include <SPI.h>
67-
#include
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(); } // 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)
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-
  cls(); // From line 5
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-
  nmStr[b-1] = '\0';
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-
  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
214+
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-
  //135 IFLV5THENPRINT" Sorry, you are not validated.":GOTO125
320+
321-
  if (lv5)
321+
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-
  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);
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-
line360:
526+
527-
  //360 PRINT"Edit line 1 -"A":";:GOSUB1005:LN=VAL(LEFT$(A$,2)):IFLNA THEN345
527+
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-
  if (lna) goto line345;
535+
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-
  for (b=1; b<=a; b++)   {     printUsingSemi("##>", b);
562+
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-
  //415 PRINT"Read Message 1 -"MS":";:GOSUB1005:A=VAL(LEFT$(A$,2)):IFAMS THEN115
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-
  if (ams) goto line115;
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-
  fStr[ c-b-1] = '\0'; // FIXTHIS - max copy sizes here?
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-
  //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);
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-
  for (c=0; c<strlen(aStr); c++)   {     ch = aStr[ c];     if (ch>96) aStr[ c] = ch-32;
772+
773
      print(F("That message is private."));
774-
    if (ch==92) aStr[ c] = '/';
774+
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-
  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)
837+
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-
      if (cmdLen<len)       {         if ((ch>31) && (ch<127)) // isprint(ch) does not work.
873+
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-
  while(myFile.available() && pos<count)   {     ch = myFile.read();     if (ch==CR)     {       buffer[pos] = '\0';       break;     }     if (ch>=32)
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-
  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)
1148+
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-
  extern int __heap_start, *__brkval;
1233+
    if (ch>=32)
1234-
  int v;
1234+
1235-
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
1235+
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-
//30 A$=INKEY$:IFA$=""THEN30ELSEPRINT@408,A$:LN=VAL(A$):IFLN8THENSOUND50,1:GOTO25
1288+
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-
//285 C=C+1:PRINT#-2,TAB(B)MS$(A,C):IFMS$(A,C)=""THEN290ELSEIFC20THENPRINT@198,NM$ELSENM$=A$
1323+
1324
    print(F("Error creating file."));
1325-
//375 PRINT@262,;:LINEINPUTA$:B=VAL(A$):IFB9THENPRINT@261,LV ELSELV=B
1325+
1326
#else
1327-
//385 PRINT@456," JUMP TO USER # ";:LINEINPUTA$:B=VAL(A$):IFBNM THENSOUND1,5:GOTO335ELSEA=B:GOTO320
1327+
1328-
//390 A=A-1:IFA<0THENA=NM //391 GOTO315 //395 A=A+1:IFA>NM THENA=0
1328+
1329
}
1330
 
1331
/*---------------------------------------------------------------------------*/
1332
// Print (output) routines.
1333-
//415 PRINT@96,"DELETE MESSAGE # 1 -"MS":";:LINEINPUTA$:A=VAL(A$):IFAMS THEN15
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.