VSYS JBBS

Developer(s): Joe Newman
Date: 1986
Type: Program
Platform(s): TS 2068
Tags: BBS

VSYS JBBS is a bulletin board system (BBS) program designed to run unattended, accepting incoming modem calls and allowing remote callers to read and leave messages, chat with the SYSOP, view system information, and switch between up to four message bases. The system stores up to 20 messages per base in a 20×1000-character string array, with messages bounded by a CHR$ 3 end-of-message sentinel, and persists data to Microdrive cartridges using named file saves and loads. Three blocks of machine code are POKEd into high RAM at addresses 65000–65212 to handle RS-232 serial I/O: a status check routine at 65000, an input (receive) routine at 65100, and an output (transmit) routine at 65200, all accessed via RANDOMIZE USR calls and OUT/IN instructions on port 119 (modem) and port 115 (serial data). The auto-answer loop polls the modem status port, sounds a multi-tone alert on ring detection, then presents a logon bulletin and menu to the caller, with the NOT key (key code 195) reserved as a local SYSOP escape to a privileged editor mode. Line-feed injection at every 40th character is supported as an option for callers whose terminals require it.


Program Structure

The program is organized into well-defined functional blocks, each introduced by a REM comment. Execution begins at line 5 with GO TO VAL "1700", jumping to the initialization section. The major sections are:

  • Lines 10–40: Universal serial print subroutine (GO SUB 10)
  • Lines 100–199: Main menu display and command dispatch
  • Lines 200–475: Message read routines
  • Lines 480–720: Message leave (compose) routines
  • Lines 800–900: Info/system configuration display
  • Lines 1000–1090: Character filter subroutine
  • Lines 1100–1150: System clock read subroutine
  • Lines 1160–1395: Auto-answer and logon bulletin
  • Lines 1400–1580: Logon name capture and SYSOP chat mode
  • Lines 1590–1690: Remote input subroutine (GO SUB 1590)
  • Lines 1700–1890: Initialization (CLEAR, machine code install, clock set)
  • Lines 1900–1970: Machine code POKE routines
  • Lines 1980–2095: Connect-time and time-of-day calculation/display
  • Lines 2100–2190: SYSOP-local message base switching
  • Lines 3000–3190: SYSOP editor (local keyboard, entered via NOT key)
  • Lines 3900–3915: Message pointer wrap helpers
  • Lines 5000–5100: Remote message base change routine
  • Lines 6000–6090: SYSOP local base-change subroutine
  • Lines 9000–9070: Goodbye/hangup and re-answer loop

Machine Code Routines

Three machine code routines are installed at startup by GO SUB 1900, which RESTOREs DATA statements and POKEs bytes directly into high RAM above the CLEAR boundary (set at 64999):

AddressPurposeCalled via
65000–65008Modem status check — reads port 119, masks bits 0–1, returns in C registerUSR 65000 / LET statchk=65000
65100–65145Serial receive — waits for data-ready on port 119, reads port 119 for data byte, handles timeout via BC counterUSR input (input=65100)
65200–65212Serial transmit ready check — polls port 119 bit 0 in a loop until clear, then returnsRANDOMIZE USR output (output=65200)

The transmit routine at 65200 is used as a “wait until TX ready” gate: the program calls it immediately before every OUT 115, ... to send a character byte. Port 119 is the modem/RS-232 control port and port 115 is the data port. The machine code bytes for the receive routine (lines 1940) are 45 bytes long and include nested polling loops with a timeout counter loaded into BC.

Serial Output Subroutine (Lines 10–40)

The core output routine at line 10 appends a carriage return to p$, PRINTs locally, then iterates character by character. For each character, it calls the TX-ready machine code via RANDOMIZE USR output and then sends the character code via OUT 115, CODE p$(x). The optional line-feed injection at line 12 checks if x is a multiple of 40 and lfeed=1, and if so also sends a line-feed (ASCII 10) for terminals that require CR+LF. POKE 23692,255 at line 10 suppresses the “scroll?” prompt by resetting the line counter.

Remote Input Subroutine (Lines 1590–1690)

The input routine at line 1590 is the primary method for reading a line of text from the remote caller. It polls IN 119 for the data-ready flag (bit 7 set = >128), calls USR input to receive the byte, then passes it through the character filter at line 1020. A local keyboard shortcut at line 1635 also allows the SYSOP to inject a response via INKEY$. A timeout counter uv is incremented on null bytes; reaching 200 null bytes causes a jump to the goodbye routine at line 9000. The received characters are accumulated in u$(), a 32-character DIM string.

Character Filter Subroutine (Lines 1020–1090)

The subroutine at line 1020 accepts known control characters by RETURNing immediately for codes 1, 7, 3, 10, 13, and 8 (SOH, BEL, ETX, LF, CR, BS). Any other character below 32 or above 127 is zeroed out (LET uu=0), which the callers treat as a null/no-data signal.

Message Storage Design

Messages are stored in m$(20,1000), a 20-element array of 1000-character strings. Each message slot begins with a “TO: ” header in positions 1–4, a recipient name through position 32, “FROM: ” in positions 33–38, and the sender’s name through position 64, with the message body starting at position 65. A CHR$ 3 (ETX) byte at position 1000 (or earlier) marks the end of a message. The message pointer variables y1 (current write slot), y2 (oldest message number), y3 (newest message number), and y4 (wrap count) are persisted to Microdrive in a separate y(4,1) numeric array.

Message Base Switching

Both remote (lines 5000–5100) and local SYSOP (lines 6000–6090) base-switching routines save the current m$() array and y() pointer array to named Microdrive files, then load the selected base. Microdrive device specifiers such as "@1,mess1", "@4,msg1", "@7,mess2", and "@:,msg2" indicate specific drive/slot assignments. Up to four message bases are referenced, though bases 3 and 4 are flagged as unavailable in the runtime messages.

Clock and Time Tracking

The system uses the system frame counter at addresses 23672–23674 (the three-byte FRAMES variable) as a software clock. Lines 1840–1880 convert a SYSOP-entered hour and minute into a frame count and POKE it into the FRAMES locations. The subroutine at line 1100 reads those same addresses back and converts to hours and minutes. Connect time is calculated in lines 1980–2040 by comparing the current minute to the stored login-time minute tw, with wrap-around handling at 60 minutes.

Auto-Answer and Call Flow

The auto-answer loop (lines 1160–1195) sends control bytes to port 119 to configure the modem, then polls IN 119 for a ring signal (value 5 = no ring; other values trigger answer). On ring detection, it sounds five short BEEPs, issues off-hook and connect commands, waits 300 frames (about 5 seconds), then falls through to the warmstart/logon sequence. After goodbye, line 9070 sends hangup commands to port 119 and loops back to the auto-answer routine.

SYSOP Chat Mode (Lines 1410–1580)

Chat mode first checks for 128 consecutive INKEY$ reads with no local keypress; if none detected, it reports the SYSOP unavailable. If a local key is pressed during that window, it enters a full-duplex loop: USR 65000 checks TX/RX status, USR 65100 receives incoming characters, and OUT 115, CODE i$ sends local keystrokes. The NOT key (INKEY$ code 195) exits chat and returns to the main menu. The SYSOP editor is also entered by pressing NOT during the auto-answer polling loop at line 1215.

Key BASIC Idioms and Techniques

  • GO TO VAL "nnnn" and GO SUB VAL "nnnn" are used extensively as a memory optimization, storing the line number as a string constant rather than a numeric token.
  • POKE 23692,255 resets the scroll counter before output operations to prevent “scroll?” interrupts during automated output.
  • RESTORE followed by a specific line number and READ into p$ is the standard pattern for emitting multi-line text blocks to the remote terminal.
  • The boolean expression 1 AND (a=1 OR a=3) at line 1510 is used to assign 0 or 1 to TxRDY and RxRDY flags.
  • The expression "0" AND hr<10 at line 2040 is a Spectrum-dialect idiom that produces the string “0” when the condition is true and “” when false, enabling zero-padded time display without an IF statement.

Bugs and Anomalies

  • Line 540 embeds a FOR loop with multiple NEXT x jumps inside IF statements (lines 550, 560, 570, 575), which iterates the FOR loop from within conditional branches — a valid but unusual structural pattern that can be hard to follow.
  • Line 550 uses IF uv=60 THEN LET x=32:NEXT x — the intent is to force the FOR loop to its limit to exit, but this relies on the NEXT incrementing x to 33, not 32; the logic may leave the loop one iteration early in some cases.
  • Line 855 jumps to line 130 but never assigns p$ to a GO SUB 10 call first, so the message is assembled into p$ but never transmitted to the caller.
  • Lines 2100–2190 appear to be unreachable dead code: there is no GO TO or GO SUB targeting line 2100, and line 2130 begins a REM that overlaps the info-files section label with the line 2140 output routine, suggesting these are leftover stubs from an earlier version.
  • The DATA at line 820 contains the author’s name and BBS contact details — the system configuration info screen is effectively hardcoded and cannot be changed without editing the DATA statement.
  • Line 1450 contains a FOR/NEXT loop where the NEXT is embedded inline after a conditional body, relying on the interpreter executing NEXT even after the IF body; if INKEY$ is never empty (key always held), the loop body’s NEXT is skipped and an infinite loop results.

Image Gallery

Source Code

    1 REM VSYS TS BBS-             Latest Update: 8/20/86  by JOE NEWMAN  
    5 GO TO VAL "1700"
   10 POKE 23692,255:LET p$=p$+ CHR$ 13:PRINT p$:FOR x=1 TO LEN p$
   12 IF x/40= INT (x/40) AND lfeed=1 THEN RANDOMIZE USR output:OUT 115,10
   15 RANDOMIZE USR output
   30 OUT 115, CODE p$(x)
   40 NEXT x:RETURN 
  100 REM MENU
  110 RANDOMIZE USR output:OUT 115,13:LET p$="       VSYS Tiny BBS MENU       >>>==========================<<<"+ CHR$ 13:GO SUB VAL "10"
  120 RESTORE 129:FOR z=1 TO 8:READ p$:GO SUB 10:NEXT z
  129  DATA "?) for this menu","R)ead messages","L)eave messages","C)hat with SYSOP","I)nfo files on system config.","M)essage base change","T)ime on system","G)oodbye"
  130 RANDOMIZE USR output:OUT 115,13:LET p$="COMMAND(?=MENU)>":GO SUB VAL "10":GO SUB VAL "1590"
  140 LET c$=u$(1)
  145 IF c$="r" OR c$="R" THEN GO TO VAL "200"
  147 IF c$="l" OR c$="L" THEN GO TO VAL "480"
  148 IF c$="?" THEN GO TO VAL "100"
  149 IF c$="C" OR c$="c" THEN GO TO VAL "1410"
  150 IF c$="m" OR c$="M" THEN GO TO VAL "5000"
  153 IF c$="T" OR c$="t" THEN GO SUB VAL "1980":GO SUB VAL "2080":LET p$="Connect time ":LET p$=p$+x$(1):RANDOMIZE USR output:OUT 115,13:PRINT :GO SUB 10:GO TO VAL "130"
  155 IF c$="i" OR c$="I" THEN GO TO VAL "800"
  160 IF c$="g" OR c$="G" THEN GO TO VAL "9000"
  199 GO TO VAL "130"
  200 REM READ
  210 RANDOMIZE USR output:OUT 115,13
  220 IF y3=0 THEN LET p$="Sorry, no messages found":GO SUB VAL "10":GO TO VAL "130"
  230 LET p$="Messages from "+ STR$ y2+" to "+ STR$ y3+"...":GO SUB VAL "10"
  240 LET p$="Read...(F)orward,(I)ndividual?":GO SUB VAL "10":GO SUB VAL "1590"
  245 IF u$(1)="f" OR u$(1)="F" THEN LET loop=1
  260 LET p$="Start at which message?":GO SUB VAL "10":GO SUB VAL "1590":LET y5= VAL u$( TO 2):IF u$(2)= CHR$ 13 THEN LET y5= VAL u$(1)
  262 IF y5=0 THEN GO TO VAL "230"
  340 IF INT y5<y2 OR INT y5>y3 THEN GO TO VAL "200"
  350 LET y= INT y5:IF y5>20 THEN LET y= INT (y5-(20*y4))
  360 IF y<0 THEN LET y=y+20
  370 IF m$(y,1)=" " THEN RANDOMIZE USR output:OUT 115,13:LET p$="No message at that number...try another":GO SUB 10:GO TO VAL "200"
  380 LET p$="MESSAGE NUMBER: "+ STR$ y5+ CHR$ 13:RANDOMIZE USR output:OUT 115,13:GO SUB 10
  385 PRINT m$(y):FOR x=1 TO 1000
  390 IF CODE m$(y,x)=3 THEN RANDOMIZE USR output:OUT 115,13:GO TO VAL "415"
  400 RANDOMIZE USR output:OUT 115, CODE m$(y,x)
  410 NEXT x
  415 IF loop=1 AND y5+1 <=y3 THEN LET y5=y5+1:GO TO VAL "472"
  470 LET loop=0:GO TO VAL "130"
  472 LET p$="(N)ext, (Q)uit?":GO SUB VAL "10":GO SUB VAL "1590":IF u$(1)="N" OR u$(1)="n" THEN GO TO VAL "262"
  475 GO TO VAL "470"
  480 REM leave
  490 LET y1=y1+1:IF y1>20 THEN LET y1=3
  500 LET y3=y3+1
  510 IF y3>20 THEN LET y2=y2+1
  520 LET y4= INT (y3/20)
  530 DIM l$(1000):LET m$(y1)=l$
  540 RANDOMIZE USR output:OUT 115,13:LET p$="Who gets message: ":GO SUB VAL "10":LET uv=0:LET m$(y1,1 TO 4)="TO: ":FOR x=5 TO 32:IF IN VAL "119"> VAL "128" THEN LET uu= USR input:GO SUB VAL "1020"
  549 IF IN VAL "119"< VAL "128" THEN GO TO VAL "9000"
  550 IF uv=60 THEN LET x=32:NEXT x
  560 IF uu=0 THEN LET x=x-1:LET uv=uv+1:NEXT x
  570 IF uu=8 THEN LET x=x-1:LET uv=0:NEXT x
  575 IF CHR$ uu= CHR$ 13 THEN LET x=32:NEXT x
  580 LET m$(y1,x)= CHR$ uu:PRINT m$(y1,x);:LET uv=0
  590 IF CODE m$(y1,x)=13 THEN LET x=32
  600 NEXT x
  610 IF uv=60 THEN GO TO VAL "9000"
  615 LET m$(y1,33 TO 38)="FROM: "
  620 FOR x=39 TO 64:IF k$(x-38)= CHR$ 13 THEN LET x=64:NEXT x
  622 LET m$(y1,x)=k$(x-38):NEXT x
  630 RANDOMIZE USR output:OUT 115,13:PRINT :LET p$="Input message (900 chars max).. <CTRL C> to save...":PRINT :RANDOMIZE USR output:OUT 115,13:GO SUB VAL "10"
  650 LET uv=0:FOR v= VAL "65" TO VAL "999":POKE 23692,255:IF IN VAL "119"> VAL "128" THEN LET uu= USR input:GO SUB VAL "1020"
  651 IF IN VAL "119"< VAL "128" THEN GO TO VAL "9000"
  660 IF uv= VAL "60" THEN LET v= VAL "999":GO TO VAL "700"
  670 IF uu= VAL "8" THEN LET v=v-1:LET uv= VAL "0"
  680 IF uu=0 THEN LET v=v-1:LET uv=uv+1:NEXT v
  690 LET m$(y1,v)= CHR$ uu:PRINT m$(y1,v);:LET uv= VAL "0":IF uu= VAL "3" THEN LET v= VAL "1000":NEXT v:LET m$(y1,1000)= CHR$ 3:GO TO VAL "100"
  700 NEXT v:LET m$(y1,1000)= CHR$ 3
  710 IF uv= VAL "60" THEN GO TO VAL "9000"
  720 GO TO VAL "130"
  800 REM Info Files
  810 RESTORE VAL "820":PRINT :PRINT :RANDOMIZE USR output:OUT 115,13:FOR z=1 TO 14:READ p$:GO SUB VAL "10":NEXT z:GO TO VAL "130"
  820 DATA "VSYS System Configuration","=========================",""," This is a BBS run off a Timex","Sinclair 2068 computer, and two","A&J Microdrives. The original ","version of the tinyboard program","was written by Randy & Lucy","Gordon. Mods were added by John","Ryan of the RATS DEN BBS","and the program was made into","its present form by yours truly","SYSOP===> Joe Newman <===",""
  830 REM Message Base Change
  840 RESTORE 900:LET p$="Current Message base="+ STR$ mbase+ CHR$ 13:GO SUB VAL "10":FOR z=1 TO 4:READ p$:GO SUB VAL "10":NEXT z
  850 RANDOMIZE USR output:OUT 119,13:LET p$="Which base?":GO SUB VAL "10":GO SUB VAL "1590"
  855 IF u$(1)= STR$ mbase THEN LET p$="This is mbase #"+ STR$ mbase:GO TO VAL "130"
  856 IF u$(1)="4" THEN LET p$="Sorry, not available yet":GO SUB VAL "10":GO TO VAL "130"
  857 LET h= VAL u$(1):IF h<0 OR h>4 THEN GO TO VAL "830"
  858 IF mbase=1 THEN SAVE "@1,mess1":SAVE "@4,msg1"DATA y():
  859 IF mbase=2 THEN SAVE "@7,mess2":SAVE "@:,msg2"DATA y()
  860 IF mbase=3 THEN SAVE "@1,mes3":SAVE "@4,msg3"DATA y():POKE 24201,0
  865 IF mbase=4 THEN SAVE "@7,mess4":SAVE "@:,msg4"DATA y():POKE 24201,0
  870 IF u$(1)="3" OR u$(1)="4" THEN POKE 24201,1
  872 LET o$="@mess"+u$(1):LOAD o$ DATA m$():LET o$="@msg"+u$(1):LOAD o$ DATA y()
  880 GO TO VAL "130"
  900 DATA "1) TS 1000,2068,Spectrum,Zx81","2) QL,buy,sell,trade,misc.","3) BS Base- Debate forum","4) Not avaialable yet! Up soon"
 1000 LET users=0
 1010 GO TO VAL "1700"
 1020 IF uu=1 THEN RETURN 
 1030 IF uu=7 THEN RETURN 
 1040 IF uu=3 THEN RETURN 
 1050 IF uu=10 THEN RETURN 
 1060 IF uu=13 THEN RETURN 
 1070 IF uu=8 THEN RETURN 
 1080 IF uu<32 OR uu>127 THEN LET uu=0
 1090 RETURN 
 1100 LET ty= INT ((65536* PEEK 23674+256* PEEK 23673+ PEEK 23672)/60)
 1110 LET ht= INT (ty/3600)
 1120 LET bz=ty-ht*3600
 1130 LET mt= INT (bz/60)
 1140 IF ht=24 THEN LET ht=0:LET dy=dy+1
 1150 RETURN 
 1160 REM autoans
 1170 OUT 119,34
 1180 OUT 119,0
 1190 REM ansloop
 1200 GO SUB VAL "1100"
 1205 LET loop=0
 1210 LET a= IN 119
 1215 IF CODE INKEY$=195 THEN GO TO VAL "3000"
 1230 IF a=5 THEN GO TO VAL "1190"
 1240 FOR x=1 TO 5:BEEP .1,40:NEXT x
 1250 OUT 119,2
 1260 OUT 119,34
 1270 PAUSE 300
 1280 REM warmstart
 1290 OUT 119,64:OUT 119,123:OUT 119,55:LET tw= INT mt:REM set online timer
 1300 REM logonbull
 1305 LET lfeed=0
 1310 PAUSE 30:FOR x=1 TO 30:RANDOMIZE USR output:OUT 115,0:NEXT x
 1320 IF IN VAL "119"< VAL "128" THEN GO TO VAL "9000"
 1330 RANDOMIZE USR output:OUT 115,28:RANDOMIZE USR output:OUT 115,31
 1340 RANDOMIZE USR VAL "65200":OUT VAL "115", VAL "28"
 1350 FOR x= VAL "1" TO VAL "17":RANDOMIZE USR VAL "65200":OUT 115,13:PRINT :NEXT x
 1360 RESTORE VAL "1365":FOR z=1 TO 8:READ p$:GO SUB VAL "10":NEXT z
 1365 DATA "Welcome to the...","","VSYS Timex/Sinclair BBS","Hours: Weekdays 7PM to 10AM","Fri. 7PM to Sun. 10AM, then","back up Sun. night at 7PM","","Board sponsored by Variety Sales"
 1370 LET p$= CHR$ (13):LET p$=p$+ CHR$ (13):GO SUB VAL "10"
 1380 LET users=users+1:LET p$="You are caller number "+ STR$ users:GO SUB 10:OUT 115,13:
 1390 FOR x=1 TO 5:RANDOMIZE USR 65200:OUT 115,13:PRINT :NEXT x
 1395 LET p$="Last caller="+k$:GO SUB VAL "10":RANDOMIZE USR output:OUT 115,13
 1400 LET logon= VAL "1":LET p$="Full Name=":GO SUB VAL "10":GO SUB VAL "1590":LET p$="Need line Feeds (y/n)?":GO SUB VAL "10":GO SUB VAL "1590":IF u$(1)="y" OR u$(1)="Y" THEN LET lfeed=1
 1401 LET p$="SORRY- Message bases 3 & 4 will NOT be available for a while":GO SUB VAL "10":GO TO VAL "100"
 1410 REM chat
 1440 PRINT :RANDOMIZE USR VAL "65200":OUT 115,13:LET p$="You want to talk to the VSYSOP?!Let's see if I can dig him up...":GO SUB 10:PRINT :RANDOMIZE USR 65200:OUT 115,13
 1450 FOR x=1 TO 128:IF INKEY$="" THEN PRINT ".";:BEEP .1,1:RANDOMIZE USR 65200:OUT 115,46:NEXT x:LET p$="VSYSOP is not available.....":GO SUB 10:PRINT :RANDOMIZE USR 65200:OUT 115,13:GO TO 1300
 1460 RANDOMIZE USR 65200:OUT 115,13:LET p$="Well, you've GOT him!:":FOR x=1 TO LEN p$:POKE 23692,255:PRINT p$(x);:RANDOMIZE USR 65200:OUT 115, CODE p$(x):NEXT x:PRINT :RANDOMIZE USR 65200:OUT 115,13
 1470 CLS :PRINT "You are in chat mode with       ";k$:PRINT :PRINT "SYMBL SHIFT/NOT to escape..."
 1480 IF CODE INKEY$=195 THEN GO TO VAL "130":REM 195=NOT
 1490 POKE 23692,255
 1500 LET RxRDY=0:LET TxRDY=0
 1510 LET a= USR 65000:LET TxRDY=1 AND (a=1 OR a=3):LET RxRDY=1 AND (a=2 OR a=3)
 1520 IF RxRDY THEN LET k= USR 65100:PRINT CHR$ k;:IF k=13 THEN PRINT ">";
 1530 IF TxRDY AND INKEY$ <>"" THEN GO SUB 1550
 1540 GO TO 1480
 1550 IF TxRDY THEN LET i$= INKEY$:PRINT i$;:OUT 115, CODE i$:FOR x=1 TO 5:NEXT x:IF CODE i$=13 THEN PRINT ">";
 1560 IF INKEY$ <>"" THEN GO TO 1560
 1570 IF CODE INKEY$=195 THEN GO TO VAL "130"
 1580 RETURN 
 1590 REM inname
 1600 DIM u$(32):LET u=1
 1610 RANDOMIZE USR output:OUT 115,5:LET uv=0
 1620 :IF IN 119>128 THEN LET uu= USR input:GO SUB 1020
 1630 IF IN 119<128 THEN GO TO VAL "9000"
 1635 IF INKEY$ <>"" THEN LET u$= INKEY$:PRINT u$:RANDOMIZE USR output:OUT 115, CODE u$(1):RETURN 
 1640 IF uv=200 THEN GO TO VAL "9000"
 1650 IF uu=0 THEN LET uv=uv+1:GO TO 1620
 1660 LET v$= CHR$ uu:PRINT v$;
 1670 LET u$(u)=v$ AND u<32:LET u=u+1
 1680 IF CODE v$=13 AND logon=1 THEN LET k$=u$:LET logon=0:RETURN 
 1685 IF CODE v$= VAL "13" THEN RETURN 
 1690 LET uv=0:GO TO 1620
 1700 REM init
 1710 REM 20 message base
 1720 PAPER 0:BORDER 0:INK 7:CLS 
 1730 CLEAR 64999:GO SUB 1900
 1740 LET lfeed=0:LET users=0:LET chatmode=-1
 1750 DIM y(4,1):LET y1=0:LET y2=0:LET y3=0:LET y4=0:DIM l$(1000):DIM m$(20,1000):LET m$(1)="No messages found..."+ CHR$ 3:PRINT "Do you wish to load a message   base?":INPUT x$:IF x$="y" OR x$="Y" THEN LOAD "@mess1"DATA m$():LOAD "@msg1"DATA y():LET y1=y(1,1):LET y2=y(2,1):LET y3=y(3,1):LET y4=y(4,1)
 1760 PRINT :PRINT "<NOT> from most modes will exit to SYSOP control mode.":LET mbase=1
 1770 LET y=0
 1780 LET output=65200:LET input=65100:LET statchk=65000
 1790 INPUT "Day";dy
 1800 INPUT "Hour";ht
 1810 INPUT "Minute";mt
 1820 INPUT "Month ";e$:LET e$=e$+" "
 1830 LET m6=0
 1840 LET aa=(ht*3600+mt*60)*60
 1850 LET bz= INT (aa/65536):POKE 23674,bz
 1860 LET cz=aa-bz*65536
 1870 LET aa= INT (cz/256):POKE 23673,aa
 1880 LET bz=cz-aa*256:POKE 23672,bz
 1885 CLS 
 1890 GO TO VAL "1160"
 1900 REM code
 1910 RESTORE 1920:FOR x=65000 TO 65008:READ x1:POKE x,x1:NEXT x
 1920 DATA 175,219,119,230,3,79,6,0,201
 1930 RESTORE 1940:FOR x=65100 TO 65145:READ x1:POKE x,x1:NEXT x
 1940 DATA 1,255,255,119,230,128,200,175,219,119,230,2,32,10,11,120,177,32,240,6,0,14,1,201,219,115,6,0,79,219,119,230,128,200,175,219,119,230,1,40,244,121,211,115,201
 1950 RESTORE 1960:FOR x=65200 TO 65212:READ x1:POKE x,x1:NEXT x
 1960 DATA 219,119,230,128,200,175,219,119,230,1,40,244,201
 1970 RETURN 
 1980 DIM x$(2,5):GO SUB VAL "1100":LET time= INT mt
 1990 LET tz= INT (time-tw)
 2000 IF tz<0 THEN LET tz= INT (time+60-tw)
 2010 LET hr=0
 2020 IF tz>60 THEN LET hr=1:LET tz=tz-60
 2030 LET min=tz
 2040 PRINT :PRINT AT 21,0;"0" AND hr<10;hr;":";"0" AND min<10;min:FOR x=1 TO 5:LET x$(1,x)= SCREEN$ (21,x-1):NEXT x
 2050 GO SUB 1100
 2060 PRINT :PRINT AT 21,0;"0" AND ht<10;ht;":";"0" AND mt<10;mt:FOR x=1 TO 5:LET x$(2,x)= SCREEN$ (21,x-1):NEXT x
 2070 RETURN 
 2080 RANDOMIZE USR output:OUT 115,32:LET p$=e$+ STR$ dy+",1986"+ CHR$ 13:GO SUB 10
 2090 LET p$="Time of day "+x$(2)+ CHR$ 13:GO SUB 10:
 2095 RETURN 
 2100 INPUT "mbase1,2,3,4>";change
 2110 IF change=1 THEN SAVE "@2,mess1"DATA m$():LET y(1,1)=y1:LET y(2,1)=y2:LET y(3,1)=y3:LET y(4,1)=y4:SAVE "@3,msg1"DATA y():GO TO 1700
 2120 IF change=2 THEN SAVE "@4,mess2"DATA m$():LET y(1,1)=y1:LET y(2,1)=y2:LET y(3,1)=y3:LET y(4,1)=y4:SAVE "@5,msg2"DATA y():GO TO 1700
 2130 REM info files
 2140 RANDOMIZE USR output:OUT 115,13
 2150 IF y1>20 THEN LET y1=y1-20
 2160 RETURN 
 2170 IF y3>20 THEN LET y2=y2+1:RETURN 
 2180 IF mbase=1 THEN SAVE "@1,mess1"DATA m$():LET y(1,1)=y1:LET y(2,1)=y2:LET y(3,1)=y3:LET y(4,1)=y4:SAVE "@4,msg1"DATA y():LOAD "@mess2"DATA m$():LOAD "@msg2"DATA y():LET y1=y(1,1):LET y2=y(2,1):LET y3=y(3,1):LET y4=y(4,1):LET mbase=2:CLS :RETURN 
 2190 IF mbase=2 THEN SAVE "@7,mess2"DATA m$():LET y(1,1)=y1:LET y(2,1)=y2:LET y(3,1)=y3:LET y(4,1)=y4:SAVE "@:,msg2"DATA y():LOAD "@mess1"DATA m$():LOAD "@msg1"DATA y():LET y1=y(1,1):LET y2=y(2,1):LET y3=y(3,1):LET y4=y(4,1):LET mbase=1:CLS :RETURN 
 3000 REM SYSOP EDITOR
 3010 PRINT AT 0,0;"SYSOP EDITOR"; AT 2,0;"<R>ead"; AT 4,0;"<L>eave"; AT 6,0;"<D>elete"; AT 8,0;"<Q>uit"; AT 10,0;"<S>ave mbase"; AT 12,0;"<B>reak program"; AT 14,0;"<C>hange mbase"; AT 16,0;"<N>o chat"
 3050 LET i$= INKEY$:IF i$="" THEN GO TO VAL "3050"
 3070 IF i$="s" OR i$="S" THEN GO TO VAL "4000"
 3080 IF i$="R" OR i$="r" THEN INPUT "Message #?";x:PRINT "Message #";x:PRINT m$(x):PAUSE 0:CLS :GO TO VAL "3000"
 3090 IF i$="d" OR i$="D" THEN INPUT "Message #=";x:PRINT "Message #";x;" deleted.":LET m$(x)=l$:PAUSE 0:CLS :GO TO VAL "3000"
 3100 IF i$="l" OR i$="L" THEN LET y1=y1+1:GO SUB VAL "3900":LET y3=y3+1:GO SUB VAL "3910":PRINT "Message # ";y1:PRINT :FOR x=64 TO 1000:PAUSE 0:LET m$(y1,x)= INKEY$:PRINT m$(y1,x);:IF CODE INKEY$=195 THEN LET m$(y1,x)= CHR$ 3
 3110 IF i$="l" OR i$="L" THEN IF m$(y1,x)= CHR$ 3 THEN GO TO VAL "3170"
 3120 IF i$="l" OR i$="L" THEN NEXT x:PAUSE 0:LET m$(y1,1000)= CHR$ 3:GO TO VAL "3170"
 3130 IF i$="q" OR i$="Q" THEN GO TO VAL "9000"
 3140 IF i$="b" OR i$="B" THEN ©:STOP 
 3150 IF i$="c" OR i$="C" THEN GO SUB VAL "6000"
 3160 GO TO VAL "3000"
 3170 INPUT "Message to-";z$:LET m$(y1,1 TO 32)=z$(1 TO LEN z$)
 3180 LET m$(y1,32)= CHR$ 13:LET m$(y1,33 TO 63)="From VSYSOP JOE":LET m$(y1,64)= CHR$ 13
 3190 CLS :GO TO VAL "3000"
 3900 IF y1>20 THEN LET y1=y1-20:RETURN 
 3905 RETURN 
 3910 IF y3>20 THEN LET y2=y2+1:RETURN 
 3915 RETURN 
 5000 REM MBASE CHANGE
 5010 RANDOMIZE USR output:OUT 115,13
 5020 RESTORE 5100:FOR z=1 TO 4:READ p$:GO SUB VAL "10":NEXT z:RANDOMIZE USR output:OUT 115,13
 5025 LET p$="This is message base #"+ STR$ mbase:GO SUB VAL "10"
 5030 LET p$="Choose mbase (1,2,3,4)>":GO SUB VAL "10"
 5040 GO SUB VAL "1590"
 5050 IF u$(1)= STR$ mbase THEN LET p$="This is Mbase #"+ STR$ mbase:GO SUB VAL "10":GO TO VAL "130"
 5055 LET p$="Tapes Grinding...please stand by":GO SUB VAL "10":
 5060 LET h= VAL u$(1)
 5061 IF h>2 THEN LET p$="Sorry, not available now!":GO SUB VAL "10":GO TO VAL "5000"
 5062 LET y(1,1)=y1:LET y(2,1)=y2:LET y(3,1)=y3:LET y(4,1)=y4
 5065 IF mbase=1 THEN SAVE "@1,mess1"DATA m$():SAVE "@4,msg1"DATA y()
 5067 IF mbase=2 THEN SAVE "@7,mess2"DATA m$():SAVE "@:,msg2"DATA y()
 5070 IF mbase=3 THEN SAVE "@=,mess3"DATA m$():SAVE "@@,msg3"DATA y():
 5072 IF mbase=4 THEN SAVE "@C,mess4"DATA m$():SAVE "@F,msg4"DATA y():
 5075 LET z$="@mess"+ STR$ h:
 5080 :LOAD z$ DATA m$():LET z$="@msg"+ STR$ h:LOAD z$ DATA y():LET y1=y(1,1):LET y2=y(2,1):LET y3=y(3,1):LET y4=y(4,1):LET mbase=h
 5090 LET p$="WHEW...Mbase Changed!":GO SUB VAL "10":GO TO VAL "130"
 5100 DATA "1) TS 2068/1000 ZX81 Spectrum","2) QL,misc.,buy,sell,trade","3) BS Base- Not available now","4) Not available now"
 6000 REM sys change
 6005 INPUT "Base to load=";h
 6010 LET y(1,1)=y1:LET y(2,1)=y2:LET y(3,1)=y3:LET y(4,1)=y4
 6020 IF mbase=1 THEN SAVE "@1,mess1"DATA m$():SAVE "@4,msg1"DATA y()
 6030 IF mbase=2 THEN SAVE "@7,mess2"DATA m$():SAVE "@:,msg2"DATA y()
 6040 IF mbase=3 THEN SAVE "@1,mess3"DATA m$():SAVE "@4,msg3"DATA y():POKE 24201,0
 6050 IF mbase=4 THEN SAVE "@7,mess4"DATA m$():SAVE "@:,msg4"DATA y():POKE 24201,0
 6060 LET z$="@mess"+ STR$ h:
 6070 IF h=1 OR h=2 THEN LOAD z$ DATA m$():LET z$="@msg"+ STR$ h:LOAD z$ DATA y():LET y1=y(1,1):LET y2=y(2,1):LET y3=y(3,1):LET y4=y(4,1):LET mbase=h:RETURN 
 6080 POKE 24201,1:LOAD z$ DATA m$():LET z$="@msg"+ STR$ h:LOAD z$ DATA y():LET y1=y(1,1):LET y2=y(2,1):LET y3=y(3,1):LET y4=y(4,1):LET mbase=h
 6090 RETURN 
 9000 REM QUIT
 9010 IF y1 <>0 THEN LET m$(y1,1000)= CHR$ 3
 9020 RANDOMIZE USR output:OUT 115,13
 9030 CLS :PAUSE 10
 9040 RESTORE 9050:FOR z=1 TO 7:READ p$:GO SUB VAL "10":NEXT z
 9050 DATA "Thank you for calling the","VSYS Timex/Sinclair BBS","","To reach a human during BBS","hours, try calling 201-289-5699","","PLEASE HANG UP NOW!"
 9060 PAUSE VAL "120"
 9070 OUT 119,64:OUT 119,0:OUT 119,0:PAUSE VAL "60":CLS :GO TO VAL "1160"
 9999 REM QUIT

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.