A bulletin board system for the Timex/Sinclair 2068 and the Timex/Westridge 2050 modem. T/S Tinyboard waits in auto-answer mode, picks up the line when it rings, and asks the caller for a full name. Callers get a menu to Read messages, Leave a message (up to 900 characters, ended with CTRL-C), page the sysop for Chat, see their connect Time, or say Goodbye. The message base holds 20 messages of up to 1,000 characters, and each caller’s name is sent to the printer.
At start-up the program asks whether to load a saved message base and a logon bulletin from tape. Pressing NOT (SYMBOL SHIFT-S) from most modes drops into a sysop editor, which can read, delete and write messages and save the message base to tape. Chat mode is a two-way terminal between sysop and caller.
The REM and title screen read “T/S Tinyboard by FLASHWARE © 1986, Randy/Lucy Gordon, Timex-Sinclair Users Group of Cincinnati, Oh.”, with a revision date of 01/21/86. The T/S Tinyboard BBS article gives the authors’ notes, credits the auto-answer routine to Randy Kuhn, and prints a later revision by Mark Fisher of CATS.
Program Analysis
Structure
- Lines 1000–1090: auto-answer, waiting on
IN 119and taking the line withOUT 119,2andOUT 119,34. - Lines 2000–2070: set up the modem’s USART, zero the frame counter as an online timer, send the logon bulletin, ask for the caller’s name and run the main menu.
- Lines 3000–3040 read messages, 4000–4070 leave one, 5000–5130 hang up and go back to auto-answer, 7000–7162 chat, 8500–8599 the sysop editor.
- Lines 9000–9540: initialise, load the optional message base and bulletin, name every routine and POKE three machine-code routines.
Machine Code
Three short routines are POKEd from DATA: a status read at 65000 (IN port 119, bits 0–1), a receive routine at 65100 that waits for a character on port 115 and echoes it back, and a send-ready wait at 65200. All three return early if bit 7 of port 119 shows the carrier has gone. BASIC then sends each character with RANDOMIZE USR output: OUT 115,code.
Notable Techniques
- Every routine has a named line-number variable (
LET read=3000,LET quit=5000and so on), so the program readsGO TO readandGO SUB logonbull. ON ERR GO TO quitaround every input means a dropped call or line noise sends the board back to auto-answer.- Connect time comes from the system frame counter (23672–23674), divided by 60.
Potential Issues
- If every message is deleted, the read loop at line 3015 searches forever for a non-blank message.
- The name input at line 8010 writes
u$(u)with no upper limit, so a name longer than 32 characters causes an error, which theON ERRtreats as a hang-up.
Source Code
1 REM T/S Tinyboard by FLASHWARE \* 1986 Randy/Lucy Gordon Timex-SinclairUsers Group of Cincinnati, Oh.
10 GO TO 9000
1000 REM autoans
1001 OUT 119,34
1002 OUT 119,0
1010 REM ansloop
1015 LET a=IN 119
1016 IF CODE INKEY$=195 THEN GO TO edit
1020 IF a=5 THEN GO TO ansloop
1066 FOR x=1 TO 5: BEEP .1,40: NEXT x
1070 OUT 119,2
1080 OUT 119,34
1090 PAUSE 300
2000 REM warmstart
2001 FOR x=1 TO 3: BEEP .1,x: NEXT x: OUT 119,64: OUT 119,123: OUT 119,55: POKE 23674,0: POKE 23673,0: POKE 23672,0: REM set online timer
2002 REM menu
2003 PAUSE 120: CLS : GO SUB logonbull: LET x=IN 115: LET p$="Full name: ": FOR x=1 TO LEN p$: RANDOMIZE USR output: OUT 115,CODE p$(x): PRINT p$(x);: NEXT x: GO SUB inname: LPRINT u$
2004 LET y=0: REM set message number to read
2005 REM inputcom
2006 RESTORE 2007
2007 DATA "T/S TINYBOARD MAIN MENU","<R>ead messages","<L>eave message","<C>hat mode","<T>ime on","<G>oodbye"," ","Choice?:"
2008 RANDOMIZE USR output: OUT 115,13: PRINT
2020 FOR p=1 TO 8: PRINT : RANDOMIZE USR output: OUT 115,13: READ p$: FOR x=1 TO LEN p$: POKE 23692,255: PRINT p$(x);: RANDOMIZE USR output: OUT 115,CODE p$(x): NEXT x
2035 NEXT p
2036 ON ERR GO TO quit: LET c$=CHR$ USR input: PRINT c$: PRINT
2040 IF c$="r" OR c$="R" THEN FOR x=1 TO 2: RANDOMIZE USR output: OUT 115,13: NEXT x: GO TO read
2041 IF c$="t" OR c$="T" THEN GO SUB timer: LET p$="Connect time ": LET p$=p$+x$: RANDOMIZE USR output: OUT 115,13: PRINT : FOR x=1 TO LEN p$: PRINT p$(x);: RANDOMIZE USR output: OUT 115,CODE p$(x): NEXT x: GO TO 2006
2045 IF c$="c" OR c$="C" THEN GO TO chat
2047 IF c$="g" OR c$="G" THEN PAUSE 30: GO TO quit
2050 IF c$="l" OR c$="L" THEN GO TO leave
2070 GO TO inputcom
3000 REM read messages
3015 LET y=y+1: IF y>20 THEN LET y=1
3018 IF m$(y,1)=" " THEN GO TO read
3019 PRINT AT 21,0;" ": PRINT AT 21,0;y: LET c$=SCREEN$ (21,0): LET c$=c$+SCREEN$ (21,1): LET p$="MESSAGE NUMBER: "+c$: RANDOMIZE USR output: OUT 115,13: FOR x=1 TO LEN p$: RANDOMIZE USR output: OUT 115,CODE p$(x): PRINT p$(x);: NEXT x: PRINT : RANDOMIZE USR output: OUT 115,13
3024 FOR v=1 TO 1000: POKE 23692,255
3025 IF CODE m$(y,v)=3 THEN LET v=1000: NEXT v: GO TO inputcom
3030 PRINT m$(y,v);: RANDOMIZE USR output: OUT 115,CODE m$(y,v)
3031 IF v>64 THEN NEXT v
3032 IF (v>32 AND CODE m$(y,v)=13) THEN LET v=64: RANDOMIZE USR output: OUT 115,13
3033 IF (v<32 AND CODE m$(y,v)=13) THEN LET v=32
3035 NEXT v
3036 RANDOMIZE USR output: OUT 115,13: PRINT
3040 GO TO inputcom
4000 REM leave message
4010 LET y1=y1+1: IF y1>20 THEN LET y1=1
4011 DIM l$(1000): LET m$(y1)=l$
4012 RANDOMIZE USR output: OUT 115,13: LET p$="Who gets message?: ": FOR x=1 TO LEN p$: RANDOMIZE USR output: OUT 115,CODE p$(x): PRINT p$(x);: NEXT x: ON ERR GO TO quit: LET m$(y1,1 TO 4)="TO: ": FOR x=5 TO 32: LET m$(y1,x)=CHR$ USR input: PRINT m$(y1,x);
4013 IF CODE m$(y1,x)=13 THEN LET x=32
4014 NEXT x
4015 LET m$(y1,33 TO 38)="FROM: ": LET m$(y1,39 TO 64)=u$
4018 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
4019 FOR x=1 TO LEN p$: RANDOMIZE USR output: OUT 115,CODE p$(x): PRINT p$(x);: NEXT x: FOR x=1 TO 2: RANDOMIZE USR output: OUT 115,13: PRINT : NEXT x
4020 ON ERR GO TO quit: FOR v=65 TO 1000: POKE 23692,255: LET m1=USR input: REM ctrl C=03h=ETX
4050 LET m$(y1,v)=CHR$ m1: PRINT m$(y1,v);: IF m1=3 THEN LET v=1000: NEXT v: GO TO inputcom
4055 NEXT v
4070 GO TO inputcom
5000 REM quit message base
5001 ON ERR RESET
5100 RANDOMIZE USR output: OUT 115,13
5105 CLS : PAUSE 10
5110 LET p$="Thank you for calling the T/S TINYBOARD.... Hang up now!!!"
5115 PAUSE 30: CLS : RANDOMIZE USR output: OUT 115,28: RANDOMIZE USR output: OUT 115,31
5120 FOR x=1 TO LEN p$: RANDOMIZE USR output: OUT 115,CODE p$(x): PRINT p$(x);: NEXT x
5121 PAUSE 120
5125 OUT 119,64: OUT 119,0: OUT 119,0
5130 PAUSE 60: CLS : FOR x=60 TO 1 STEP -5: BEEP .1,x: NEXT x: GO TO autoans
6000 REM logonbull
6001 PAUSE 30: FOR x=1 TO 30: RANDOMIZE USR output: OUT 115,0: NEXT x
6002 IF IN 119<128 THEN GO TO quit
6005 RANDOMIZE USR output: OUT 115,28: RANDOMIZE USR output: OUT 115,31
6008 RANDOMIZE USR 65200: OUT 115,28
6010 FOR x=1 TO 10: RANDOMIZE USR 65200: OUT 115,13: PRINT : NEXT x
6020 FOR x=1 TO LEN t$: POKE 23692,255: PRINT t$(x);: RANDOMIZE USR 65200: OUT 115,CODE t$(x): NEXT x
6030 FOR x=1 TO 5: RANDOMIZE USR 65200: OUT 115,13: PRINT : NEXT x
6040 RETURN
7000 REM chat
7001 ON ERR GO TO quit
7005 PRINT : RANDOMIZE USR 65200: OUT 115,13: LET p$="Paging sysop...................": FOR x=1 TO 10: BEEP .1,1: NEXT x: 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
7006 FOR x=1 TO 128: IF INKEY$="" THEN PRINT ".";: RANDOMIZE USR 65200: OUT 115,46: BEEP .1,10: NEXT x: LET p$="The sysop is not available.....": FOR x=1 TO 10: BEEP .1,1: NEXT x: 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: GO TO inputcom
7008 RANDOMIZE USR 65200: OUT 115,13: LET p$="You are now in chat mode:": FOR x=1 TO 10: BEEP .1,1: NEXT x: 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
7009 CLS : PRINT "You are in chat mode with ";u$: PRINT : PRINT "SYMBL SHIFT/NOT to escape..."
7010 IF CODE INKEY$=195 THEN GO TO inputcom: REM 195=NOT
7011 POKE 23692,255
7020 LET RxRDY=0: LET TxRDY=0
7100 LET a=USR 65000: LET TxRDY=1 AND (a=1 OR a=3): LET RxRDY=1 AND (a=2 OR a=3)
7130 IF RxRDY THEN LET k=USR 65100: PRINT CHR$ k;: IF k=13 THEN PRINT ">";
7140 IF TxRDY AND INKEY$<>"" THEN GO SUB 7160
7150 GO TO 7010
7160 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 ">";
7161 IF INKEY$<>"" THEN GO TO 7161
7162 RETURN
8000 REM inname
8001 DIM u$(32): LET u=1
8002 RANDOMIZE USR output: OUT 115,5
8005 ON ERR GO TO quit: LET v$=CHR$ USR input: PRINT v$;
8010 LET u$(u)=v$ AND u<32: LET u=u+1
8011 IF CODE v$=13 THEN RETURN
8100 GO TO 8005
8500 REM edit
8510 PRINT AT 0,0;"SYSOP EDITOR";AT 2,0;"<R>ead messages";AT 4,0;"<D>elete messages";AT 6,0;"<L>eave messages";AT 8,0;"<Q>uit editor";AT 10,0;"<S>ave message base"
8520 LET i$=INKEY$
8525 IF i$="" THEN GO TO 8520
8526 IF i$="s" OR i$="S" THEN GO TO 9999
8530 IF i$="r" OR i$="R" THEN INPUT ;"message #?: ";x: PRINT "Message # ";x: PRINT m$(x): PAUSE 0: GO TO edit
8540 IF i$="d" OR i$="D" THEN INPUT ;"message #?: ";x: PRINT "Message # ";x;" deleted.": LET m$(x)=l$: PAUSE 0: GO TO edit
8550 IF i$="l" OR i$="L" THEN LET y1=y1+1: PRINT "Message # ";y1: PRINT : FOR x=1 TO 1000: PAUSE 0: LET m$(y1,x)=INKEY$: PRINT m$(y1,x);
8554 IF i$="l" OR i$="L" THEN IF CODE INKEY$=195 THEN LET m$(y1,x)=CHR$ 3: GO TO edit: REM 195=NOT
8560 IF i$="l" OR i$="L" THEN NEXT x: PAUSE 0: GO TO edit
8570 IF i$="q" OR i$="Q" THEN GO TO quit
8599 CLS : GO TO edit
9000 REM init
9001 REM 20 meesage base
9004 PAPER 0: BORDER 0: INK 7: CLS
9005 CLEAR 64999: GO SUB 9500
9006 DIM y(1,1): LET y1=0: DIM l$(1000): DIM m$(20,1000): LET m$(1)="No messages found..."+CHR$ 3: FOR x=1 TO 3: BEEP .1,25: NEXT x: PRINT "Do you wish to load a message base?": INPUT x$: IF x$="y" OR x$="Y" THEN PRINT "Start MESSAGE BASE tape... ( 2 Part LOAD... )": LOAD "" DATA m$(): LOAD "" DATA y(): LET y1=y(1,1)
9007 LET t$="T/S Tinyboard FLASHWARE <c> 1985": FOR x=1 TO 3: BEEP .1,25: NEXT x: PRINT "Do you wish to load a logon bulletin?": INPUT x$: IF x$="y" OR x$="Y" THEN PRINT "Start LOGON BULLETIN tape... ( with data as t$ )": LOAD "" DATA t$()
9008 PRINT : PRINT "<NOT> from most modes will exit to SYSOP control mode.": BEEP .1,35: BEEP .1,35: PAUSE 180
9009 LET y=0: LET edit=8500: LET timer=9990
9010 LET chat=7000: LET menu=2002: LET warmstart=2000: LET logonbull=6000: LET statchk=65000: LET input=65100: LET output=65200: LET ansloop=1010: LET inputcom=2005: LET autoans=1000: LET read=3000: LET leave=4000: LET quit=5000
9011 LET inname=8000
9012 PRINT "Now going to AUTOANSWER MODE": FOR x=1 TO 3: BEEP .1,10: NEXT x: PAUSE 180: CLS
9100 GO TO quit
9500 REM code
9510 RESTORE 9515: FOR x=65000 TO 65008: READ x1: POKE x,x1: NEXT x
9515 DATA 175,219,119,230,3,79,6,0,201
9520 RESTORE 9525: FOR x=65100 TO 65132: READ x1: POKE x,x1: NEXT x
9525 DATA 219,119,230,128,200,175,219,119,230,2,40,244,219,115,6,0,79,219,119,230,128,200,175,219,119,230,1,40,244,121,211,115,201
9530 RESTORE 9535: FOR x=65200 TO 65212: READ x1: POKE x,x1: NEXT x
9535 DATA 219,119,230,128,200,175,219,119,230,1,40,244,201
9540 RETURN
9990 REM timer
9992 DIM x$(5): LET time=INT ((PEEK 23672+256*PEEK 23673+256*256*PEEK 23674)/60): LET hr=INT (time/60/60): LET min=INT ((time-(hr*60*60))/60): PRINT AT 21,0;"0" AND hr<10;hr;":";"0" AND min<10;min: FOR x=1 TO 5: LET x$(x)=SCREEN$ (21,x-1): NEXT x
9993 RETURN
9995 INK 7: BORDER 0: PAPER 0: CLS : PRINT AT 3,10;"T/S Tinyboard";AT 14,8;"FLASHWARE \* 1986": CIRCLE 123,110,20: PLOT 127,133: DRAW 20,0: DRAW -5,-20: DRAW -15,0: DRAW -5,-10: DRAW -8,0: DRAW -6,-17: DRAW 0,23: DRAW 6,0: DRAW 4,9: DRAW 6,0: DRAW 2,15: BEEP .2,10: BEEP .2,5: BEEP .2,2: PAUSE 4: BEEP .2,10: BEEP .2,5: BEEP .2,2: PAUSE 8: BEEP .2,10: BEEP .2,5: BEEP .2,2: BEEP .2,5: BEEP .2,10: BEEP .2,10
9996 GO TO 10
9997 SAVE "tinyboard" LINE 9995: REM T/S Tinyboard/Flashware \* 1986 rev. date 01/21/86
9998 STOP
9999 SAVE "messbase" DATA m$(): LET y(1,1)=y1: SAVE "msgcntr" DATA y(): GO TO editNote: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

