Author: vidak

  • Word Processors in BASIC, Part 2

    Word Processors in BASIC, Part 2

    Nibblewriter

    Found in November 1988 issue of Nibble magazine.

    Archive.org link here.

    The project was alerted to the existence of a few text editors and word processors in Nibble magazine by someone on matrix.

    What we found was a great wealth of BASIC programs for the Apple II computer. We uncovered several particularly interesting BASIC word processors. Two that are of particular interest are Quickwriter and this one, Nibblewriter.

    diskM8 File Dump

    Please find an ASCII conversion of the BASIC dump obtained using the following commands:

    $ diskm8 -ingest /path/to/your/disk/images
    $ diskm8 -search "nibblewriter"
    $ diskm8 -with-disk "your_disk_image.do" -file-extract "NIBBLEWRITER.BAS"

    Code Listing

    10  REM *************************
    20  REM *     NIBBLEWRITER      *
    21  REM *     ------------      *
    30  REM *  BY ROBERT T. YUILLE  *
    40  REM *  COPYRIGHT (C) 1988   *
    50  REM *  BY MICROSPARC, INC.  *
    60  REM *  CONCORD, MA  01742   *
    70  REM *************************
    80  HOME : PRINT  CHR$ (21)
    90  REM   SET HIMEM
    100 PRODOS =  PEEK (48896) = 76: IF  NOT PRODOS THEN  HIMEM: 24832: GOTO 170
    110  REM   FOR PRODOS
    120  POKE 768,32: POKE 769,248: POKE 770,190
    130  POKE 771,169: POKE 772,53: POKE 773,32
    140  POKE 774,245: POKE 775,190: POKE 776,96
    150  CALL 768
    160  REM   INITIALIZATION
    170 LM$ = "1.0":RM$ = "1.0":TM$ = "1.0":BM$ = "1.0"
    180 LM = 8:RM = 66:TM = 6:BM = 60
    190  POKE 30,0: POKE 233,0: REM  NEWF & PRNTF
    200 D$ =  CHR$ (4):B$ =  CHR$ (7):E$ =  CHR$ (27):R$ =  CHR$ (13): IF  NOT PRODOS THEN D$ =  CHR$ (13) +  CHR$ (4)
    210 M1$ = "MARGIN VALUE EXCEEDS MAXIMUM"
    220 M2$ = "MARGIN VALUE IS LESS THAN MINIMUM"
    230 M3$ = "CHAR CANNOT BE USED IN THIS INPUT"
    240 FL$ = "WP.40.O"
    250 FADDR = 26124: REM   FILENAME ($660C)
    260 ST = 6:DR = 1: REM  DEFAULT SLOT AND DRIVE
    270  PRINT D$"PR#0"
    280 :
    290  REM   LOAD WP OBJECT FILE
    300 :
    310 LK = 1: ONERR  GOTO 3130
    320  PRINT D$"BLOAD NW.BIN"
    330  POKE 216,0: REM  RESET ERROR FLAG
    340 :
    350  REM    INITIALIZE & READ MENU DATA
    360 :
    370  READ MITEMS: DIM MENU$(9): FOR J = 0 TO MITEMS: READ MENU$(J): NEXT 
    380  DIM X$(1):X$(0) = "   ":X$(1) = "-->"
    390 :
    400  REM   MAIN MENU
    410 :
    420  TEXT : HOME : GOSUB 3040: POKE 216,0: ONERR  GOTO 3130
    430 PX = 38:SX = 4:SE = SE + (SE = 0):MX = MITEMS: GOSUB 2960: IF Z = 20 THEN  PRINT B$: GOTO 420
    440  ON SE GOTO 480,560,670,890,940,990,1480,2750,1740
    450 :
    460  REM   START NEW DOCUMENT
    470 :
    480  IF  PEEK (30) = 2 THEN  GOSUB 2660: IF ESCF = 1 THEN 420
    490  GOSUB 1830: IF ESCF = 1 THEN 420
    500  POKE 30,0
    510  CALL 26112: REM   ENTRY ($6600)
    520  GOTO 420
    530 :
    540  REM   LOAD NEW DOCUMENT
    550 :
    560  IF  PEEK (30) = 2 THEN  GOSUB 2660: IF ESCF = 1 THEN 420
    570  GOSUB 1830: IF ESCF = 1 THEN 420
    580  HTAB 1: VTAB 20: PRINT "LOADING "FL$
    590 LK = 3: ONERR  GOTO 3130
    600  PRINT D$"BLOAD "FL$",S"ST",D"DR
    610  POKE 216,0
    620  POKE 30,1: REM  SET NEWF FOR LOADED FILE
    630  GOTO 510
    640 :
    650  REM   SAVE CURRENT DOCUMENT
    660 :
    670  HOME : IF  PEEK (30) <  > 2 THEN  VTAB 5: PRINT "THERE IS NO CURRENT DOCUMENT": VTAB 10: PRINT "PRESS RETURN FOR MAIN MENU: ";: GET A$: GOTO 420
    680 EF =  PEEK (252) + 256 *  PEEK (253): REM  EFL($FC) & EFH($FD)
    690 LN = EF - 28672
    700  ONERR  GOTO 800
    710  PRINT D$"VERIFY "FL$",S"ST",D"DR
    720  HOME : VTAB 5: PRINT "FILE: '"FL$"' ALREADY EXISTS"
    730  VTAB 13: PRINT "PRESS 'Y' TO REPLACE EXISTING FILE": PRINT : PRINT "PRESS 'N' TO RENAME FILE": VTAB 10: PRINT "DO YOU WANT TO REPLACE": PRINT "THE EXISTING FILE? (Y/N) ";
    740  GET A$
    750  IF A$ = "N" OR A$ = "n" THEN  POKE 216,0: GOTO 840
    760  IF A$ <  > "Y" AND A$ <  > "y" THEN  GOTO 740
    770  HTAB 1: VTAB 20: PRINT "DELETING "FL$
    780 LK = 2: ONERR  GOTO 3130
    790  PRINT D$"DELETE "FL$",S"ST",D"DR
    800  HTAB 1: VTAB 21: PRINT "SAVING "FL$
    810 LK = 2: ONERR  GOTO 3130
    820  PRINT D$"BSAVE "FL$",A$7000,L"LN",S"ST",D"DR
    830  POKE 216,0: GOTO 420
    840  GOSUB 1830: IF ESCF = 1 THEN 420
    850  GOTO 670
    860 :
    870  REM   RENAME CURRENT DOCUMENT
    880 :
    890  GOSUB 1830: IF ESCF = 1 THEN 420
    900  GOTO 510
    910 :
    920  REM   CONTINUE WITH CURRENT DOCUMENT
    930 :
    940  IF  PEEK (30) <  > 2 THEN 670
    950  GOTO 510
    960 :
    970  REM   PRINTER ROUTINE
    980 :
    990  IF  PEEK (30) <  > 2 THEN 670
    1000  HOME : VTAB 2: PRINT "MARGIN VALUES ARE AS FOLLOWS:": PRINT : PRINT "LM= "LM$"  RM= "RM$"  TM= "TM$"  BM= "BM$
    1010  VTAB 10: PRINT "DO YOU WANT TO CHANGE MARGINS (Y/N)? ";
    1020  GET A$
    1030  IF A$ = "Y" OR A$ = "y" THEN 1060
    1040  IF A$ <  > "N" AND A$ <  > "n" THEN 1020
    1050  GOTO 1370
    1060 M$ = "LM":MA$ = "7.0":MI$ = "0.5"
    1070  GOSUB 2330: IF ESCF = 1 THEN 420
    1080 N =  VAL (N$)
    1090  IF N > 7 THEN MSG$ = M1$: GOSUB 2250: GOTO 1060
    1100  IF N < .5 THEN MSG$ = M2$: GOSUB 2250: GOTO 1060
    1110 LM = N * 10 - 2
    1120 LM$ = N$
    1130 M$ = "RM":MA$ = "7.0":MI$ = "0.5"
    1140  GOSUB 2330: IF ESCF = 1 THEN 420
    1150 N =  VAL (N$)
    1160  IF N > 7 THEN MSG$ = M1$: GOSUB 2250: GOTO 1130
    1170  IF N < .5 THEN MSG$ = M2$: GOSUB 2250: GOTO 1130
    1180 RM = 80 - (N * 10 - 4) - LM
    1190  IF RM <  = 0 THEN MSG$ = "LEFT MARGIN EXCEEDS RIGHT MARGIN": GOSUB 2250:RM = 66: GOTO 990
    1200 RM$ = N$
    1210 M$ = "TM":MA$ = "8.0":MI$ = "0.2"
    1220  GOSUB 2330: IF ESCF = 1 THEN 420
    1230 N =  VAL (N$)
    1240  IF N > 8 THEN MSG$ = M1$: GOSUB 2250: GOTO 1210
    1250  IF N < .2 THEN MSG$ = M2$: GOSUB 2250: GOTO 1210
    1260 TM =  INT (6 * N + .5)
    1270 TM$ = N$
    1280 M$ = "BM":MA$ = "8.0":MI$ = "0.2"
    1290  GOSUB 2330: IF ESCF = 1 THEN 420
    1300 N =  VAL (N$)
    1310  IF N > 8 THEN MSG$ = M1$: GOSUB 2250: GOTO 1280
    1320  IF N < .2 THEN MSG$ = M2$: GOSUB 2250: GOTO 1280
    1330 BM = 66 -  INT (6 * N + .5)
    1340  IF TM >  = BM THEN MSG$ = "TOP MARGIN EXCEEDS BOTTOM MARGIN": GOSUB 2250: GOTO 1210
    1350 BM$ = N$
    1360  GOTO 990
    1370  POKE 26,TM: POKE 27,BM: POKE 28,LM: POKE 29,RM
    1380  HOME : VTAB 2: PRINT "BE SURE YOUR PRINTER IS READY": VTAB 10: PRINT "PRESS RETURN TO CONTINUE: ";: GET A$
    1390  HOME : VTAB 15: PRINT "PRESS ESCAPE TO CANCEL PRINTING."
    1400  VTAB 10: PRINT "PRESS SPACE BAR TO CONTINUE: "
    1410  PRINT D$"PR#1"
    1420  PRINT  CHR$ (9)"80N": POKE 233,1: CALL 26112: POKE 233,0
    1430  PRINT D$"PR#0"
    1440  GOTO 420
    1450 :
    1460  REM   HELP SCREEN
    1470 :
    1480  TEXT : HOME 
    1490  NORMAL : PRINT "             HELP SCREEN": REM  13 SPACES
    1500  PRINT : PRINT 
    1510  PRINT "<ARROWS> - MOVE CURSOR"
    1520  PRINT 
    1530  PRINT "<ESCAPE> - RETURN TO MAIN MENU"
    1540  PRINT 
    1550  PRINT "<DELETE> - DELETE CHAR LEFT OF CURSOR"
    1560  PRINT 
    1570  PRINT "<CTRL>-D - DELETE CHAR UNDER CURSOR"
    1580  PRINT 
    1590  PRINT "<CTRL>-E - CHANGE INSERT/REPLACE CURSOR"
    1600  PRINT 
    1610  PRINT "<CTRL>-Y - CLEAR FROM CURSOR TO EOL"
    1620  PRINT 
    1630  PRINT "<CTRL>-Z - SHOW CR'S AS INV ASTERISK"
    1640  PRINT 
    1650  PRINT "   <TAB> - MOVE CURSOR TO NEXT TAB POS"
    1660  PRINT 
    1670  PRINT "<CTRL>-F - MOVE TO FIRST LINE"
    1680  PRINT "<CTRL>-C - MOVE TO CENTER LINE"
    1690  PRINT "<CTRL>-L - MOVE TO LAST LINE";
    1700  HTAB 1: VTAB 24: PRINT "PRESS RETURN TO CONTINUE: ";: GET A$: GOTO 420
    1710 :
    1720  REM   QUIT
    1730 :
    1740  HOME : VTAB 10: PRINT "DO YOU REALLY WANT TO QUIT (Y/N)? ";
    1750  GET A$
    1760  IF A$ = "Y" OR A$ = "y" THEN 1790
    1770  IF A$ = "N" OR A$ = "n" THEN 420
    1780  GOTO 1750
    1790  HOME : END 
    1800 :
    1810  REM   FILENAME INPUT ROUTINE
    1820 :
    1830  HOME :N$ = "":ESCF = 0
    1840  VTAB 2: PRINT "ENTER FILENAME FOR DOCUMENT": PRINT : PRINT 
    1850  PRINT "PRESS '?' TO CATALOG DISK": PRINT 
    1860  PRINT "ESCAPE ON 1ST CHAR RETURNS TO MAIN MENU": PRINT 
    1870  PRINT "ESCAPE ON ANY OTHER CHARACTER": PRINT " CANCELS INPUT AND STARTS OVER": PRINT 
    1880  PRINT "RETURN ACCEPTS INPUT"
    1890  HTAB 1: VTAB 15: PRINT "FILENAME: ";
    1900  GET C$
    1910  IF C$ = E$ THEN ESCF = 1: RETURN 
    1920  IF C$ = "?" THEN  PRINT D$ LEFT$ ("CATALOG",7 - 4 * PRODOS)",S"ST",D"DR: HTAB 1: VTAB 24: PRINT "PRESS RETURN TO CONTINUE: ";: GET A$: GOTO 1830
    1930 AN =  ASC (C$)
    1940  IF AN > 96 AND AN < 123 THEN AN = AN - 32
    1950  IF AN < 65 OR AN > 90 THEN MSG$ = "FIRST POSITION MUST BE A LETTER": GOSUB 2250: GOTO 1830
    1960  PRINT  CHR$ (AN)
    1970 N$ =  CHR$ (AN)
    1980  FOR K = 1 TO 15
    1990  HTAB 1: VTAB 18: CALL  - 958
    2000  VTAB 15: HTAB (11 + K)
    2010  GET C$
    2020  IF C$ = E$ THEN K = 15: NEXT : GOTO 1830
    2030  IF C$ =  CHR$ (8) AND K = 1 THEN K = 15: NEXT : GOTO 1830
    2040  IF C$ =  CHR$ (8) THEN K = K - 1: HTAB (11 + K): PRINT " ":N$ =  LEFT$ (N$,K): GOTO 2000
    2050  IF C$ = R$ THEN LC = K:K = 15: GOTO 2110
    2060 AN =  ASC (C$)
    2070  IF AN > 96 AND AN < 123 THEN AN = AN - 32
    2080  IF (AN < 65 OR AN > 90) AND (AN < 48 OR AN > 57) AND AN <  > 46 THEN MSG$ = "CHARACTER CANNOT BE USED IN FILENAME": GOSUB 2250: GOTO 1990
    2090  PRINT  CHR$ (AN)
    2100 N$ = N$ +  CHR$ (AN)
    2110  NEXT 
    2120  IF C$ <  > R$ THEN MSG$ = "FILENAME TOO LONG, LIMIT - 15 CHARACTERS": GOSUB 2250: GOTO 1830
    2130 FL$ = N$
    2140  FOR I = 0 TO 15
    2150  POKE FADDR + I,0
    2160  NEXT 
    2170  FOR K = 1 TO  LEN (N$)
    2180  POKE FADDR - 1 + K, ASC ( MID$ (N$,K,1)) + 128
    2190  NEXT 
    2200  POKE FADDR + LC,0
    2210  RETURN 
    2220 :
    2230  REM   ERROR MESSAGES
    2240 :
    2250  PRINT B$
    2260  HTAB 1: VTAB 18
    2270  PRINT MSG$: PRINT : PRINT 
    2280  PRINT "PRESS RETURN AND TRY AGAIN: ";: POKE  - 16368,0: GET AA$
    2290  RETURN 
    2300 :
    2310  REM   PRINTER MARGIN ROUTINE
    2320 :
    2330  HOME :N$ = "":ESCF = 0
    2340  VTAB 2: PRINT "ENTER VALUE FOR "M$: PRINT "MAXIMUM "MA$" - MINIMUM "MI$: PRINT 
    2350  PRINT "ESCAPE ON 1ST CHAR RETURNS TO MAIN MENU": PRINT 
    2360  PRINT "ESCAPE ON ANY OTHER CHARACTER": PRINT " CANCELS INPUT AND STARTS OVER": PRINT 
    2370  PRINT "RETURN ACCEPTS INPUT"
    2380  PRINT : PRINT "RETURN ON 1ST CHAR ACCEPTS DEFAULT (1.0)"
    2390  HTAB 1: VTAB 15: PRINT M$":  1.0"
    2400  HTAB 5: VTAB 15
    2410  GET C$
    2420  IF C$ = E$ THEN ESCF = 1: RETURN 
    2430  IF C$ = R$ THEN N$ = "1.0": RETURN 
    2440 AN =  ASC (C$)
    2450  IF AN < 48 OR AN > 57 THEN MSG$ = M3$: GOSUB 2250: GOTO 2330
    2460  PRINT C$;: CALL  - 868
    2470 N$ = C$
    2480  FOR K = 1 TO 3
    2490  HTAB 1: VTAB 18: CALL  - 958
    2500  VTAB 15: HTAB (5 + K)
    2510  GET C$
    2520  IF C$ = E$ THEN K = 3: NEXT : GOTO 2330
    2530  IF C$ =  CHR$ (8) AND K = 1 THEN K = 3: NEXT : GOTO 2330
    2540  IF C$ =  CHR$ (8) THEN K = K - 1: HTAB (5 + K): PRINT " ":N$ =  LEFT$ (N$,K): GOTO 2500
    2550  IF C$ = R$ THEN K = 3: GOTO 2600
    2560 AN =  ASC (C$)
    2570  IF (AN < 48 OR AN > 57) AND AN <  > 46 THEN MSG$ = M3$: GOSUB 2250: GOTO 2490
    2580  PRINT C$
    2590 N$ = N$ + C$
    2600  NEXT 
    2610  IF C$ <  > R$ THEN MSG$ = "INPUT TOO LONG, LIMIT - 3 CHARACTERS": GOSUB 2250: GOTO 2330
    2620  RETURN 
    2630 :
    2640  REM  ERASE CURRENT DOCUMENT?
    2650 :
    2660  HOME :ESCF = 0: VTAB 8: PRINT "YOU ARE ABOUT TO ERASE": PRINT "THE CURRENT DOCUMENT."
    2670  VTAB 12: PRINT "DO YOU REALLY WANT TO DO THIS? (Y/N) ";
    2680  GET A$
    2690  IF A$ = "N" OR A$ = "n" THEN ESCF = 1: RETURN 
    2700  IF A$ <  > "Y" AND A$ <  > "y" THEN 2680
    2710  RETURN 
    2720 :
    2730  REM  SELECT SLOT AND DRIVE
    2740 :
    2750  HOME : VTAB 1: PRINT "WHICH SLOT DO YOU WANT TO USE? (2-7) ";
    2760  GET A$
    2770  IF A$ =  > "2" AND A$ <  =  CHR$ (54 + PRODOS) THEN  PRINT A$:ST =  VAL (A$): GOTO 2790
    2780  GOTO 2760
    2790  VTAB 5: PRINT "WHICH DISK DRIVE": PRINT "DO YOU WANT TO USE? (1-2) ";
    2800  GET A$
    2810  IF A$ = "1" OR A$ = "2" THEN  PRINT A$:DR =  VAL (A$): GOTO 2830
    2820  GOTO 2800
    2830  POKE 216,0: ONERR  GOTO 2870: IF PRODOS THEN  PRINT D$"PREFIX,S";ST;",D";DR:
    2840  IF PRODOS THEN  GOTO 420
    2850  PRINT D$"VERIFY JUNK,S";ST;",D";DR
    2860  GOTO 420
    2870  POKE 216,0:EN =  PEEK (222):EL =  PEEK (218) + 256 *  PEEK (219): IF EL = 2850 AND EN = 6 THEN 420
    2880  VTAB 23: PRINT "DISK ERROR": PRINT "PRESS RETURN TO CONTINUE ";: POKE  - 16368,0: GET AA$: GOTO 420
    2890 :
    2900  REM  DATA FOR MAIN MENU
    2910 :
    2920  DATA  9,MAIN MENU,START NEW DOCUMENT,LOAD DOCUMENT,SAVE DOCUMENT,RENAME DOCUMENT,CONTINUE WITH CURRENT DOCUMENT,PRINT DOCUMENT,HELP,SET SLOT/DRIVE,QUIT
    2930 :
    2940  REM   MENU HANDLER
    2950 :
    2960 SL = SE
    2970 N = SL:OS = SL: GOSUB 3000:Z = 0: VTAB 24: HTAB PX: CALL  - 868: POKE  - 16368,0
    2980  VTAB 24: HTAB PX: PRINT SL;: WAIT  - 16384,128:Z =  PEEK ( - 16384) - 128: IF Z >  = 49 AND Z <  = MX + 48 THEN SL = Z - 48:N = OS: GOSUB 3000: GOTO 2970
    2990 Z = (Z = 21 OR Z = 10) - (Z = 8 OR Z = 11) + 10 * (Z = 13) + 20 * (Z = 27): ON  NOT Z GOTO 2980:SL = SL + Z * (Z < 10):SL = SL - MX * (SL > MX) + MX * (SL < 1):N = OS: GOSUB 3000: ON Z < 10 GOTO 2970:SE = SL: POKE  - 16368,0: RETURN 
    3000  VTAB 3 + 2 * N: HTAB SX: PRINT X$(SL = OS);: RETURN 
    3010 :
    3020  REM   PRINT A MENU
    3030 :
    3040  HOME :TL$ = MENU$(0): VTAB 1: PRINT "NIBBLEWRITER   (C)1988   MICROSPARC,INC.": VTAB 2: FOR I = 1 TO 40: PRINT "-";: NEXT 
    3050  VTAB 3: HTAB  INT ((40 -  LEN (TL$)) / 2)
    3060  PRINT TL$
    3070  FOR I = 1 TO MITEMS: VTAB 3 + 2 * I: HTAB 8: PRINT I". "MENU$(I): NEXT 
    3080  VTAB 24: HTAB 1: PRINT "USE ARROWS/NUMBERS & PRESS RETURN: ";
    3090  RETURN 
    3100 :
    3110  REM  DISK ERRORS
    3120 :
    3130  HOME : VTAB 10
    3140  POKE 216,0
    3150  IF  PEEK (222) = 6 OR  PEEK (222) = 7 THEN MSG$ = "FILE (" + FL$ + ")" +  CHR$ (13) + "IS NOT ON THIS DISK.": ON (( PEEK (218) + 256 *  PEEK (219) = 320) + 1) GOTO 3210,3230
    3160  IF  PEEK (222) = 13 THEN MSG$ = "FILE (" + FL$ + ")" +  CHR$ (13) + "IS NOT A BINARY FILE.": GOTO 3210
    3170  IF  PEEK (222) = 9 OR  PEEK (222) = 17 THEN MSG$ = "DISK OR DIRECTORY IS FULL.": GOTO 3210
    3180  IF  PEEK (222) = 10 THEN MSG$ = "EXISTING FILE (" + FL$ + ")" +  CHR$ (13) + "IS LOCKED.": GOTO 3210
    3190  PRINT "SYSTEM ERROR #"; PEEK (222);" HAS OCCURRED"
    3200  PRINT "AT LINE #"; PEEK (218) +  PEEK (219) * 256:MSG$ = ""
    3210  GOSUB 2250
    3220  GOTO 420
    3230  VTAB 24: PRINT "BINARY FILE NOT FOUND": END
  • Microchess for the KIM-1

    Microchess for the KIM-1

    https://obsolescenceguaranteed.blogspot.com/2014/06/6502-microchess-on-arduino.html

    https://www.benlo.com/microchess/index.html

    Game Hex Dump

    Assembly Listing

    ;***********************************************************************
    ;
    ;  Kim-1 MicroChess (c) 1976-2005 Peter Jennings, www.benlo.com 
    ;
    ;***********************************************************************
    
    ; All rights reserved.
    
    ; Redistribution and use in source and binary forms, with or without
    ; modification, are permitted provided that the following conditions
    ; are met:
    ; 1. Redistributions of source code must retain the above copyright
    ;    notice, this list of conditions and the following disclaimer.
    ; 2. Redistributions in binary form must reproduce the above copyright
    ;    notice, this list of conditions and the following disclaimer in the
    ;    documentation and/or other materials provided with the distribution.
    ; 3. The name of the author may not be used to endorse or promote products
    ;    derived from this software without specific prior written permission.
    
    ; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
    ; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    ; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    ; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    ; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    ; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    ; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
    ;
    ; modified by Daryl Rictor to work over a 
    ; serial terminal connection, August 2002.
    ;
    ; Updated with corrections to earlier OCR errors by Bill Forster, August 2005.
    ;
       cpu 65c02
       page 0,132
    ;
    ; 6551 I/O Port Addresses
    ;
    ACIADat	= 	$7F70
    ACIASta	=	$7F71
    ACIACmd	=	$7F72
    ACIACtl	=	$7F73
    ;
    ; page zero variables
    ;
    BOARD   =	$50 
    BK      =	$60 
    PIECE   =	$B0 
    SQUARE  =	$B1 
    SP2     =	$B2 
    SP1     =	$B3 
    INCHEK  =	$B4 
    STATE   =	$B5 
    MOVEN   =	$B6 
    REV	=       $B7
    OMOVE   =	$DC 
    WCAP0   =	$DD 
    COUNT   =	$DE 
    BCAP2   =	$DE 
    WCAP2   =	$DF 
    BCAP1   =	$E0 
    WCAP1   =	$E1 
    BCAP0   =	$E2 
    MOB     =	$E3 
    MAXC    =	$E4 
    CC      =	$E5 
    PCAP    =	$E6 
    BMOB    =	$E3 
    BMAXC   =	$E4 
    BMCC    =	$E5 		; was BCC (TASS doesn't like it as a label)
    BMAXP   =	$E6 
    XMAXC   =	$E8 
    WMOB    =	$EB 
    WMAXC   =	$EC 
    WCC     =	$ED 
    WMAXP   =	$EE 
    PMOB    =	$EF 
    PMAXC   =	$F0 
    PCC     =	$F1 
    PCP     =	$F2 
    OLDKY   =	$F3 
    BESTP   =	$FB 
    BESTV   =	$FA 
    BESTM   =	$F9 
    DIS1    =	$FB 
    DIS2    =	$FA 
    DIS3    =	$F9 
    temp    =       $FC
    ;
    ;
    ;
    		*=$1000			; load into RAM @ $1000-$15FF
    
    		LDA     #$00		; REVERSE TOGGLE
    		STA     REV
                    JSR     Init_6551
    CHESS		CLD			; INITIALIZE
    		LDX	#$FF		; TWO STACKS
    		TXS	
    		LDX	#$C8
    		STX	SP2
    ;		
    ;       ROUTINES TO LIGHT LED
    ;       DISPLAY AND GET KEY
    ;       FROM KEYBOARD
    ;		
    OUT		JSR	pout		; DISPLAY AND
    		JSR	KIN		; GET INPUT   *** my routine waits for a keypress
    ;		CMP	OLDKY		; KEY IN ACC  *** no need to debounce
    ;		BEQ	OUT		; (DEBOUNCE)
    ;		STA	OLDKY
    ;		
    		CMP	#$43		; [C]
    		BNE	NOSET		; SET UP
    		LDX	#$1F		; BOARD
    WHSET		LDA	SETW,X		; FROM
    		STA	BOARD,X		; SETW
    		DEX	
    		BPL	WHSET
    		LDX	#$1B		; *ADDED
    		STX	OMOVE		; INITS TO $FF
    		LDA	#$CC		; Display CCC
    		BNE	CLDSP
    ;		
    NOSET		CMP	#$45		; [E]
    		BNE	NOREV		; REVERSE
    		JSR	REVERSE        	; BOARD IS
    		SEC
    		LDA	#$01
    		SBC	REV
    		STA	REV		; TOGGLE REV FLAG
    		LDA	#$EE            ; IS
    		BNE	CLDSP
    ;		
    NOREV		CMP	#$40			; [P]
    		BNE	NOGO           	; PLAY CHESS
    		JSR	GO
    CLDSP		STA	DIS1          	; DISPLAY
    		STA	DIS2         	; ACROSS
    		STA	DIS3          	; DISPLAY
    		BNE	CHESS
    ;		
    NOGO		CMP	#$0D            ; [Enter]
    		BNE	NOMV          	; MOVE MAN
    		JSR	MOVE          	; AS ENTERED
    		JMP	DISP
    NOMV		CMP     #$41		; [Q] ***Added to allow game exit***
    		BEQ     DONE		; quit the game, exit back to system.  
    		JMP	INPUT		; process move
    DONE		JMP     $FF00		; *** MUST set this to YOUR OS starting address
    ;		
    ;       THE ROUTINE JANUS DIRECTS THE
    ;       ANALYSIS BY DETERMINING WHAT
    ;       SHOULD OCCUR AFTER EACH MOVE
    ;       GENERATED BY GNM
    ;		
    ;		
    ;
    JANUS		LDX	STATE
    		BMI	NOCOUNT
    ;		
    ;       THIS ROUTINE COUNTS OCCURRENCES
    ;       IT DEPENDS UPON STATE TO INDEX
    ;       THE CORRECT COUNTERS
    ;		
    COUNTS		LDA	PIECE
    		BEQ	OVER           	; IF STATE=8
    		CPX 	#$08           	; DO NOT COUNT
    		BNE	OVER          	; BLK MAX CAP
    		CMP	BMAXP        	; MOVES FOR
    		BEQ	XRT           	; WHITE
    ; 		
    OVER		INC	MOB,X          	; MOBILITY
    		CMP 	#$01           	;  + QUEEN
    		BNE	NOQ           	; FOR TWO
    		INC	MOB,X
    ;		
    NOQ		BVC	NOCAP
    		LDY	#$0F           	; CALCULATE
    		LDA	SQUARE       	; POINTS
    ELOOP		CMP	BK,Y           	; CAPTURED
    		BEQ	FOUN          	; BY THIS
    		DEY			; MOVE
    		BPL	ELOOP
    FOUN		LDA	POINTS,Y
    		CMP	MAXC,X
    		BCC	LESS          	; SAVE IF
    		STY	PCAP,X         	; BEST THIS
    		STA	MAXC,X         	; STATE
    ;		
    LESS		CLC	
    		PHP			; ADD TO
    		ADC	CC,X           	; CAPTURE
    		STA	CC,X           	; COUNTS
    		PLP	
    ;		
    NOCAP		CPX	#$04
    		BEQ	ON4
    		BMI	TREE          	;(=00 ONLY)
    XRT		RTS	
    ;		
    ;      GENERATE FURTHER MOVES FOR COUNT
    ;      AND ANALYSIS	
    ;		
    ON4		LDA	XMAXC        	; SAVE ACTUAL 
    		STA	WCAP0         	; CAPTURE
    		LDA	#$00           	; STATE=0
    		STA	STATE
    		JSR	MOVE          	; GENERATE
    		JSR	REVERSE       	; IMMEDIATE
    		JSR	GNMZ         	; REPLY MOVES  
    		JSR	REVERSE
    ;		
    		LDA	#$08       	; STATE=8
    		STA	STATE        	; GENERATE
    		JSR	GNM          	; CONTINUATION
    		JSR	UMOVE         	; MOVES
    ;		
    		JMP	STRATGY       	; FINAL EVALUATION
    NOCOUNT	CPX	#$F9
    		BNE	TREE
    ;		
    ;      DETERMINE IF THE KING CAN BE
    ;      TAKEN, USED BY CHKCHK
    ;		
    		LDA	BK           	; IS KING
    		CMP	SQUARE       	; IN CHECK?
    		BNE	RETJ          	; SET INCHEK=0
    		LDA	#$00           	; IF IT IS
    		STA	INCHEK
    RETJ		RTS	
    ;		
    ;      IF A PIECE HAS BEEN CAPTURED BY
    ;      A TRIAL MOVE, GENERATE REPLIES &
    ;      EVALUATE THE EXCHANGE GAIN/LOSS
    ;		
    TREE		BVC	RETJ          	; NO CAP
    		LDY	#$07           	; (PIECES)
    		LDA	SQUARE
    LOOPX		CMP	BK,Y
    		BEQ	FOUNX
    		DEY	
    		BEQ	RETJ          	; (KING)
    		BPL	LOOPX         	; SAVE
    FOUNX		LDA	POINTS,Y       	; BEST CAP
    		CMP	BCAP0,X        	; AT THIS
    		BCC	NOMAX         	; LEVEL
    		STA	BCAP0,X
    NOMAX		DEC	STATE
    		LDA	#$FB           	; IF STATE=FB
    		CMP	STATE        	; TIME TO TURN
    		BEQ	UPTREE        	; AROUND
    		JSR	GENRM         	; GENERATE FURTHER
    UPTREE		INC	STATE        	; CAPTURES
    		RTS	
    ;		
    ;      THE PLAYER'S MOVE IS INPUT
    ;		
    INPUT		CMP	#$08           	; NOT A LEGAL
    		BCS	ERROR      	; SQUARE #
    		JSR	DISMV
    DISP		LDX	#$1F
    SEARCH		LDA	BOARD,X
    		CMP	DIS2
    		BEQ	HERE          	; DISPLAY
    		DEX			; PIECE AT    
    		BPL	SEARCH        	; FROM
    HERE		STX	DIS1         	; SQUARE
    		STX	PIECE
    ERROR		JMP	CHESS
    ;		
    ;      GENERATE ALL MOVES FOR ONE
    ;      SIDE, CALL JANUS AFTER EACH
    ;      ONE FOR NEXT STEP
    ;		
    ;
    GNMZ		LDX	#$10            ; CLEAR
    GNMX		LDA	#$00            ; COUNTERS
    CLEAR		STA	COUNT,X
    		DEX	
    		BPL	CLEAR
    ;		
    GNM		LDA	#$10            ; SET UP
    		STA	PIECE        	; PIECE
    NEWP		DEC	PIECE        	; NEW PIECE
    		BPL	NEX           	; ALL DONE?
    		RTS			;    -YES
    ;		
    NEX		JSR	RESET        	; READY
    		LDY	PIECE        	; GET PIECE
    		LDX	#$08
    		STX	MOVEN        	; COMMON START
    		CPY	#$08            ; WHAT IS IT?
    		BPL	PAWN          	; PAWN
    		CPY	#$06
    		BPL	KNIGHT        	; KNIGHT
    		CPY	#$04
    		BPL	BISHOP       	; BISHOP
    		CPY	#$01
    		BEQ	QUEEN         	; QUEEN
    		BPL	ROOK          	; ROOK
    ;		
    KING		JSR	SNGMV         	; MUST BE KING!
    		BNE	KING          	; MOVES
    		BEQ	NEWP          	; 8 TO 1
    QUEEN		JSR	LINE
    		BNE	QUEEN         	; MOVES
    		BEQ	NEWP          	; 8 TO 1
    ;		
    ROOK		LDX	#$04
    		STX	MOVEN        	; MOVES
    AGNR		JSR	LINE          	; 4 TO 1
    		BNE	AGNR
    		BEQ	NEWP
    ;		
    BISHOP		JSR	LINE
    		LDA	MOVEN        	; MOVES
    		CMP	#$04           	; 8 TO 5
    		BNE	BISHOP
    		BEQ	NEWP
    ;		
    KNIGHT		LDX	#$10
    		STX	MOVEN        	; MOVES
    AGNN		JSR	SNGMV         	; 16 TO 9
    		LDA	MOVEN
    		CMP	#$08
    		BNE	AGNN
    		BEQ	NEWP
    ;		
    PAWN		LDX	#$06
    		STX	MOVEN
    P1		JSR	CMOVE         	; RIGHT CAP?
    		BVC	P2
    		BMI	P2
    		JSR	JANUS         	; YES
    P2		JSR	RESET
    		DEC	MOVEN        	; LEFT CAP?
    		LDA	MOVEN
    		CMP	#$05
    		BEQ	P1
    P3		JSR	CMOVE         	; AHEAD
    		BVS	NEWP          	; ILLEGAL
    		BMI	NEWP
    		JSR	JANUS
    		LDA	SQUARE       	; GETS TO
    		AND	#$F0           	; 3RD RANK?
    		CMP	#$20
    		BEQ	P3            	; DO DOUBLE
    		JMP	NEWP
    ;		
    ;      CALCULATE SINGLE STEP MOVES
    ;      FOR K,N	
    ;		
    SNGMV		JSR	CMOVE        	; CALC MOVE
    		BMI	ILL1           	; -IF LEGAL
    		JSR	JANUS           ; -EVALUATE
    ILL1		JSR	RESET
    		DEC	MOVEN
    		RTS	
    ;		
    ;     CALCULATE ALL MOVES DOWN A
    ;     STRAIGHT LINE FOR Q,B,R
    ;		
    LINE		JSR	CMOVE         	; CALC MOVE
    		BCC	OVL            	; NO CHK
    		BVC	LINE		; NOCAP       
    OVL		BMI	ILL             ; RETURN
    		PHP	
    		JSR	JANUS         	; EVALUATE POSN
    		PLP	
    		BVC	LINE          	; NOT A CAP
    ILL		JSR	RESET         	; LINE STOPPED
    		DEC	MOVEN         	; NEXT DIR
    		RTS	
    ;		
    ;      EXCHANGE SIDES FOR REPLY
    ;      ANALYSIS	
    ;		
    REVERSE		LDX	#$0F
    ETC		SEC	
    		LDY	BK,X           	; SUBTRACT
    		LDA 	#$77           	; POSITION
    		SBC  	BOARD,X        	; FROM 77
    		STA  	BK,X
    		STY  	BOARD,X         ; AND
    		SEC	
    		LDA	#$77           	; EXCHANGE
    		SBC 	BOARD,X        	; PIECES
    		STA  	BOARD,X
    		DEX	
    		BPL   	ETC
    		RTS	
    ;		
    ;        CMOVE CALCULATES THE TO SQUARE
    ;        USING SQUARE AND THE MOVE
    ;       TABLE,  FLAGS SET AS FOLLOWS:
    ;       N - ILLEGAL MOVE
    ;       V - CAPTURE (LEGAL UNLESS IN CH)
    ;       C - ILLEGAL BECAUSE OF CHECK
    ;       [MY THANKS TO JIM BUTTERFIELD
    ;        WHO WROTE THIS MORE EFFICIENT
    ;        VERSION OF CMOVE]
    ;		
    CMOVE		LDA	SQUARE       	; GET SQUARE
    		LDX	MOVEN       	; MOVE POINTER
    		CLC	
    		ADC	MOVEX,X        	; MOVE LIST
    		STA	SQUARE       	; NEW POS'N
    		AND	#$88
    		BNE	ILLEGAL       	; OFF BOARD
    		LDA	SQUARE
    ;			
    		LDX	#$20
    LOOP		DEX			; IS TO
    		BMI	NO            	; SQUARE
    		CMP  	BOARD,X        	; OCCUPIED?
    		BNE   	LOOP
    ;			
    		CPX	#$10            ; BY SELF?
    		BMI   	ILLEGAL
    ;			
    		LDA	#$7F		; MUST BE CAP!
    		ADC	#$01            ; SET V FLAG
    		BVS   	SPX 	        ; (JMP)
    ;			
    NO		CLV			; NO CAPTURE
    ;			
    SPX		LDA  	STATE         	; SHOULD WE
    		BMI   	RETL           	; DO THE
    		CMP	#$08 	        ; CHECK CHECK?
    		BPL  	RETL
    ;			
    ;        CHKCHK REVERSES SIDES
    ;       AND LOOKS FOR A KING
    ;       CAPTURE TO INDICATE
    ;       ILLEGAL MOVE BECAUSE OF
    ;       CHECK  SINCE THIS IS
    ;       TIME CONSUMING, IT IS NOT
    ;       ALWAYS DONE	
    ;		
    CHKCHK	        PHA			; STATE
    		PHP	
    		LDA	#$F9
    		STA	STATE         	; GENERATE
    		STA	INCHEK        	; ALL REPLY
    		JSR	MOVE          	; MOVES TO
    		JSR	REVERSE       	; SEE IF KING
    		JSR	GNM           	; IS IN
    		JSR	RUM           	; CHECK
    		PLP	
    		PLA	
    		STA	STATE
    		LDA	INCHEK
    		BMI	RETL           	; NO - SAFE
    		SEC			; YES - IN CHK
    		LDA	#$FF
    		RTS	
    ;		
    RETL		CLC			; LEGAL
    		LDA	#$00            ; RETURN
    		RTS	
    ;		
    ILLEGAL	LDA	#$FF
    		CLC			; ILLEGAL
    		CLV			; RETURN
    		RTS	
    ;		
    ;       REPLACE PIECE ON CORRECT SQUARE
    ;		
    RESET		LDX	PIECE      	; GET LOGAT
    		LDA	BOARD,X        	; FOR PIECE
    		STA	SQUARE       	; FROM BOARD
    		RTS	
    ;		
    ;		
    ;		
    GENRM		JSR	MOVE          	; MAKE MOVE
    GENR2		JSR	REVERSE      	; REVERSE BOARD
    		JSR	GNM          	; GENERATE MOVES
    RUM		JSR	REVERSE   	; REVERSE BACK
    ;		
    ;       ROUTINE TO UNMAKE A MOVE MADE BY
    ;	  MOVE
    ;		
    UMOVE		TSX			; UNMAKE MOVE
    		STX	SP1
    		LDX	SP2           	; EXCHANGE
    		TXS			; STACKS
    		PLA			; MOVEN
    		STA	MOVEN
    		PLA			; CAPTURED
    		STA	PIECE        	; PIECE
    		TAX	
    		PLA			; FROM SQUARE
    		STA	BOARD,X
    		PLA			; PIECE
    		TAX	
    		PLA			; TO SOUARE
    		STA	SQUARE
    		STA	BOARD,X
    		JMP	STRV
    ;		
    ;       THIS ROUTINE MOVES PIECE
    ;       TO SQUARE, PARAMETERS
    ;       ARE SAVED IN A STACK TO UNMAKE
    ;       THE MOVE LATER
    ;		
    MOVE		TSX	
    		STX	SP1          	; SWITCH
    		LDX	SP2          	; STACKS
    		TXS	
    		LDA	SQUARE
    		PHA			; TO SQUARE
    		TAY	
    		LDX	#$1F
    CHECK		CMP	BOARD,X        	; CHECK FOR
    		BEQ	TAKE          	; CAPTURE
    		DEX	
    		BPL	CHECK
    TAKE		LDA	#$CC
    		STA	BOARD,X
    		TXA			; CAPTURED
    		PHA			; PIECE
    		LDX	PIECE
    		LDA	BOARD,X
    		STY	BOARD,X        	; FROM
    		PHA			; SQUARE
    		TXA	
    		PHA			; PIECE
    		LDA	MOVEN
    		PHA			; MOVEN
    STRV		TSX	
    		STX	SP2           	; SWITCH
    		LDX	SP1           	; STACKS
    		TXS			; BACK
    		RTS	
    ;			
    ;       CONTINUATION OF SUB STRATGY
    ;       -CHECKS FOR CHECK OR CHECKMATE
    ;       AND ASSIGNS VALUE TO MOVE
    ;		
    CKMATE	        LDX	BMAXC         	; CAN BLK CAP
    		CPX	POINTS       	; MY KING?
    		BNE	NOCHEK	
    		LDA	#$00           	; GULP!
    		BEQ	RETV          	; DUMB MOVE!
    ;		
    NOCHEK	LDX	BMOB         		; IS BLACK
    		BNE	RETV          	; UNABLE TO
    		LDX	WMAXP        	; MOVE AND
    		BNE	RETV          	; KING IN CH?
    		LDA	#$FF           	; YES! MATE
    ;		
    RETV		LDX	#$04            ; RESTORE
    		STX	STATE        	; STATE=4
    ;		
    ;       THE VALUE OF THE MOVE (IN ACCU)
    ;       IS COMPARED TO THE BEST MOVE AND
    ;       REPLACES IT IF IT IS BETTER
    ;		
    PUSH		CMP	BESTV         	; IS THIS BEST
    		BCC	RETP          	; MOVE SO FAR?
    		BEQ	RETP
    		STA	BESTV        	; YES!
    		LDA	PIECE        	; SAVE IT
    		STA	BESTP
    		LDA	SQUARE
    		STA	BESTM        	; FLASH DISPLAY
    RETP		LDA	#"."		; print ... instead of flashing disp
    		Jmp	syschout	; print . and return
    ;		
    ;       MAIN PROGRAM TO PLAY CHESS
    ;       PLAY FROM OPENING OR THINK
    ;		
    GO		LDX	OMOVE        	; OPENING?
    		BMI	NOOPEN          ; -NO   *ADD CHANGE FROM BPL
    		LDA	DIS3         	; -YES WAS
    		CMP	OPNING,X        ; OPPONENT'S
    		BNE	END            	; MOVE OK?
    		DEX	
    		LDA	OPNING,X       	; GET NEXT
    		STA	DIS1         	; CANNED
    		DEX			; OPENING MOVE
    		LDA	OPNING,X
    		STA	DIS3         	; DISPLAY IT
    		DEX	
    		STX	OMOVE        	; MOVE IT
    		BNE	MV2            	; (JMP)
    ;			
    END		LDA     #$FF		; *ADD - STOP CANNED MOVES
    		STA	OMOVE        	; FLAG OPENING
    NOOPEN	LDX	#$0C            	; FINISHED
    		STX	STATE        	; STATE=C
    		STX	BESTV        	; CLEAR BESTV
    		LDX	#$14           	; GENERATE P
    		JSR	GNMX          	; MOVES
    ;		
    		LDX	#$04           	; STATE=4
    		STX	STATE        	; GENERATE AND
    		JSR	GNMZ          	; TEST AVAILABLE
    ;	MOVES
    ;		
    		LDX	BESTV        	; GET BEST MOVE
    		CPX	#$0F           	; IF NONE
    		BCC	MATE          	; OH OH!
    ;		
    MV2		LDX	BESTP        	; MOVE
    		LDA	BOARD,X         ; THE
    		STA	BESTV        	; BEST
    		STX	PIECE        	; MOVE
    		LDA	BESTM
    		STA	SQUARE       	; AND DISPLAY
    		JSR	MOVE           	; IT
    		JMP	CHESS
    ;		
    MATE		LDA	#$FF           	; RESIGN
    		RTS			; OR STALEMATE
    ;		
    ;       SUBROUTINE TO ENTER THE
    ;       PLAYER'S MOVE
    ;		
    DISMV		LDX	#$04           	; ROTATE
    DROL		ASL	DIS3          	; KEY
    		ROL	DIS2         	; INTO
    		DEX			; DISPLAY
    		BNE	DROL		;
    		ORA	DIS3
    		STA	DIS3
    		STA	SQUARE
    		RTS	
    ;		
    ;       THE FOLLOWING SUBROUTINE ASSIGNS
    ;       A VALUE TO THE MOVE UNDER
    ;       CONSIDERATION AND RETURNS IT IN
    ;	THE ACCUMULATOR
    ;		
    
    STRATGY	CLC	
    		LDA	#$80
    		ADC	WMOB         	; PARAMETERS
    		ADC	WMAXC        	; WITH WEIGHT
    		ADC	WCC          	; OF 0.25
    		ADC	WCAP1
    		ADC	WCAP2
    		SEC	
    		SBC	PMAXC
    		SBC	PCC
    		SBC	BCAP0
    		SBC	BCAP1
    		SBC	BCAP2
    		SBC	PMOB
    		SBC	BMOB
    		BCS	POS           	; UNDERFLOW
    		LDA	#$00           	; PREVENTION
    POS		LSR	
    		CLC			; **************
    		ADC	#$40
    		ADC	WMAXC       	; PARAMETERS
    		ADC	WCC         	; WITH WEIGHT
    		SEC			; OF 0.5
    		SBC	BMAXC
    		LSR			; **************
    		CLC	
    		ADC	#$90
    		ADC	WCAP0       	; PARAMETERS
    		ADC	WCAP0       	; WITH WEIGHT
    		ADC	WCAP0       	; OF 1.0
    		ADC	WCAP0
    		ADC	WCAP1
    		SEC			; [UNDER OR OVER-
    		SBC	BMAXC        	; FLOW MAY OCCUR
    		SBC	BMAXC        	; FROM THIS
    		SBC	BMCC          	; SECTION]
    		SBC	BMCC
    		SBC	BCAP1
    		LDX	SQUARE      	; ***************
    		CPX	#$33
    		BEQ	POSN         	; POSITION
    		CPX	#$34           	; BONUS FOR
    		BEQ	POSN         	; MOVE TO
    		CPX	#$22           	; CENTRE
    		BEQ	POSN           	; OR
    		CPX	#$25           	; OUT OF
    		BEQ	POSN         	; BACK RANK
    		LDX	PIECE
    		BEQ	NOPOSN
    		LDY	BOARD,X
    		CPY	#$10
    		BPL	NOPOSN
    POSN		CLC
    		ADC	#$02
    NOPOSN	JMP	CKMATE       		; CONTINUE
    
    
    ;-----------------------------------------------------------------
    ; The following routines were added to allow text-based board
    ; display over a standard RS-232 port.
    ;
    POUT        	jsr 	pout9		; print CRLF
    		jsr     pout13		; print copyright
    		JSR	POUT10		; print column labels
    		LDY   	#$00		; init board location
    		JSR	POUT5		; print board horz edge
    POUT1		lDA   	#"|"		; print vert edge
    		JSR   	syschout	; PRINT ONE ASCII CHR - SPACE
    		LDX   	#$1F
    POUT2		TYA			; scan the pieces for a location match
                	CMP	BOARD,X		; match found?
                	BEQ   	POUT4		; yes; print the piece's color and type
                	DEX			; no
                	BPL	POUT2		; if not the last piece, try again
    		tya			; empty square	
    		and	#$01		; odd or even column?				
    		sta   	temp		; save it
    		tya  			; is the row odd or even
    		lsr			; shift column right 4 spaces 
    		lsr			;
    		lsr			;
    		lsr			;
    		and   	#$01		; strip LSB  
    		clc			; 
    		adc   	temp		; combine row & col to determine square color  
    		and   	#$01		; is board square white or blk?
    		beq	pout25 		; white, print space
    		lda   	#"*"		; black, print *
    		db	$2c		; used to skip over LDA #$20
    POUT25		LDA   	#$20		; ASCII space
    		JSR   	syschout	; PRINT ONE ASCII CHR - SPACE
    		JSR   	syschout	; PRINT ONE ASCII CHR - SPACE
    POUT3		INY			; 
    	          TYA			; get row number
                	AND   	#$08		; have we completed the row?	 
                	BEQ   	POUT1		; no, do next column
    		LDA   	#"|"		; yes, put the right edge on
    		JSR   	syschout	; PRINT ONE ASCII CHR - |             
    		jsr	pout12		; print row number
    		JSR   	POUT9		; print CRLF
                	JSR   	POUT5		; print bottom edge of board
    		CLC			; 
    		TYA			; 
    		ADC	#$08		; point y to beginning of next row
    		TAY			;
    		CPY   	#$80		; was that the last row?
    		BEQ   	POUT8		; yes, print the LED values
    		BNE   	POUT1		; no, do new row
    
    POUT4		LDA   	REV		; print piece's color & type
    		BEQ   	POUT41		;
    		LDA	cpl+16,X	;
    		BNE	POUT42		;
    POUT41		LDA   	cpl,x		;
    POUT42		JSR	syschout	;
    		lda	cph,x		;
    		jsr   	syschout	; 
    		BNE	POUT3		; branch always
    
    POUT5       	TXA			; print "-----...-----<crlf>"
    		PHA
    		LDX	#$19
    		LDA	#"-"
    POUT6		JSR   	syschout	; PRINT ONE ASCII CHR - "-"
    		DEX
    		BNE	POUT6
    		PLA
    		TAX
    		JSR	POUT9
    		RTS	 		
    
    POUT8		jsr	pout10		; 
    		LDA   	$FB
    		JSR   	syshexout	; PRINT 1 BYTE AS 2 HEX CHRS	
            	LDA   	#$20
    		JSR   	syschout	; PRINT ONE ASCII CHR - SPACE
            	LDA   	$FA
    		JSR   	syshexout	; PRINT 1 BYTE AS 2 HEX CHRS	
            	LDA   	#$20
    		JSR   	syschout	; PRINT ONE ASCII CHR - SPACE
            	LDA   	$F9
    		JSR   	syshexout	; PRINT 1 BYTE AS 2 HEX CHRS	
    
    POUT9      	LDA   	#$0D
    		JSR   	syschout	; PRINT ONE ASCII CHR - CR
            	LDA   	#$0A
    		JSR   	syschout	; PRINT ONE ASCII CHR - LF
                    RTS 
    
    pout10		ldx   	#$00		; print the column labels
    POUT11		lda	#$20		; 00 01 02 03 ... 07 <CRLF>
    		jsr   	syschout
    		txa
    		jsr	syshexout
    		INX
    		CPX   	#$08
    		BNE	POUT11
    		BEQ	POUT9
    POUT12		TYA
    		and 	#$70
    		JSR 	syshexout
    		rts
    
    Pout13		ldx   	#$00		; Print the copyright banner
    Pout14		lda   	banner,x
    		beq   	POUT15
    		jsr   	syschout
    		inx
    		bne   	POUT14
    POUT15		rts         
    
    KIN        	LDA   	#"?"
    		JSR   	syschout	; PRINT ONE ASCII CHR - ?
    		JSR   	syskin		; GET A KEYSTROKE FROM SYSTEM
                	AND   	#$4F            ; MASK 0-7, AND ALPHA'S
                	RTS
    ;
    ; 6551 I/O Support Routines
    ;
    ;
    Init_6551      lda   #$1F               ; 19.2K/8/1
                   sta   ACIActl            ; control reg 
                   lda   #$0B               ; N parity/echo off/rx int off/ dtr active low
                   sta   ACIAcmd            ; command reg 
                   rts                      ; done
    ;
    ; input chr from ACIA1 (waiting)
    ;
    syskin         lda   ACIASta            ; Serial port status             
                   and   #$08               ; is recvr full
                   beq   syskin             ; no char to get
                   Lda   ACIAdat            ; get chr
                   RTS                      ;
    ;
    ; output to OutPut Port
    ;
    syschout       PHA                      ; save registers
    ACIA_Out1      lda   ACIASta            ; serial port status
                   and   #$10               ; is tx buffer empty
                   beq   ACIA_Out1          ; no
                   PLA                      ; get chr
                   sta   ACIAdat            ; put character to Port
                   RTS                      ; done
    
    syshexout      PHA                     ;  prints AA hex digits
                   LSR                     ;  MOVE UPPER NIBBLE TO LOWER
                   LSR                     ;
                   LSR                     ;
                   LSR                     ;
                   JSR   PrintDig          ;
                   PLA                     ;
    PrintDig       AND   #$0F              ;  prints A hex nibble (low 4 bits)
                   PHY
                   TAY                     ;
                   LDA   Hexdigdata,Y      ;
                   PLY
                   jmp   syschout          ;
    
    Hexdigdata	asc	"0123456789ABCDEF"
    banner		asc	"MicroChess (c) 1996-2005 Peter Jennings, www.benlo.com"
    		db	$0d, $0a, $00
    cpl		asc	"WWWWWWWWWWWWWWWWBBBBBBBBBBBBBBBBWWWWWWWWWWWWWWWW"
    cph		asc	"KQRRBBNNPPPPPPPPKQRRBBNNPPPPPPPP"
    		db	$00
    ;
    ; end of added code
    ;
    ; BLOCK DATA
    		*= $1580
    SETW		db 	$03, $04, $00, $07, $02, $05, $01, $06
            	db 	$10, $17, $11, $16, $12, $15, $14, $13
            	db 	$73, $74, $70, $77, $72, $75, $71, $76
    	 	db	$60, $67, $61, $66, $62, $65, $64, $63
    
    MOVEX   	db 	$00, $F0, $FF, $01, $10, $11, $0F, $EF, $F1
    		db	$DF, $E1, $EE, $F2, $12, $0E, $1F, $21
    
    POINTS  	db 	$0B, $0A, $06, $06, $04, $04, $04, $04
    		db 	$02, $02, $02, $02, $02, $02, $02, $02
    
    OPNING  	db 	$99, $25, $0B, $25, $01, $00, $33, $25
    		db	$07, $36, $34, $0D, $34, $34, $0E, $52
            	db 	$25, $0D, $45, $35, $04, $55, $22, $06
    		db	$43, $33, $0F, $CC
    
    ;
    ;
    ; end of file
    ;
    
  • Word Processors in BASIC, Part 1

    Word Processors in BASIC, Part 1

    Quickwriter

    Found in June 1988 issue of Nibble magazine (Volume 9, Number 6).

    Archive.org link here.

    The project was alerted to the existence of a few text editors and word processors in Nibble magazine by someone on matrix.

    What we found was a great wealth of BASIC programs for the Apple II computer. We uncovered several particularly interesting BASIC word processors. Two that are of particular interest are Quickwriter and Nibblewriter.

    Both programs are quite long and involved, so instead of creating one enormous post, we decided to split the discussion up into two feature posts.

    diskM8 File Dump

    Please find an ASCII conversion of the BASIC dump obtained using the following commands:

    $ diskm8 -ingest /path/to/your/disk/images
    $ diskm8 -search "quickwriter"
    $ diskm8 -with-disk "your_disk_image.do" -file-extract "QUICKWRITER.BAS"

    Code Listing

    10  REM **********************
    20  REM *     TYPEWRITER     *
    30  REM *  BY JAMES D EVANS  *
    40  REM * COPYRIGHT (C) 1988 *
    50  REM * BY MICROSPARC, INC *
    60  REM * CONCORD, MA  01742 *
    70  REM **********************
    80  HIMEM: 38144:X =  FRE (0)
    90  ONERR  GOTO 2740
    100  GOSUB 2220
    110  GOTO 230
    120  REM **WARMSTART 80-COL CARD**
    130  ON  NOT PD GOTO 140: PRINT D$;"PR#A$C307": GOTO 150
    140  POKE 54,7: POKE 55,195: CALL 1002: PRINT 
    150  RETURN 
    160  REM **GET CHAR ROUTINE**
    170  POKE  - 16368,0: ON  NOT PLUS GOTO 180: PRINT  CHR$ (0);: GET A$: ON  NOT  LEN (A$) GOTO 170:B =  ASC (A$): GOTO 190
    180  INVERSE : PRINT " ";BS$;: NORMAL : WAIT  - 16384,128:B = ( PEEK ( - 16384) - 128):A$ =  CHR$ (B): POKE  - 16368,0: PRINT " ";BS$;: ON  NOT B GOTO 180
    190  RETURN 
    200  REM 
    210  REM ***MAIN PROGRAM***
    220  REM 
    230  FOR I = M TO M + 80: POKE I,160: NEXT 
    240  GOSUB 850: GOSUB 2050
    250 C = LM:LL = 0: POKE  - 16368,0
    260  VTAB 2: POKE H,C: INVERSE : PRINT  CHR$ ( PEEK (M + (C - LM)));: NORMAL : POKE H,C: PRINT  CHR$ (24);: ON  NOT PLUS GOTO 270: GET A$: GOTO 280
    270  WAIT  - 16384,128:A$ =  CHR$ ( PEEK ( - 16384) - 128): POKE  - 16368,0
    280  IF A$ < " " OR A$ =  CHR$ (127) THEN  GOSUB 330: GOTO 260
    290  PRINT A$: POKE M + C - LM, ASC (A$) + 128
    300 C = C + (C < RM - 1):LL = LL + (LL < RM - LM): POKE H,5: PRINT C + 1;" "
    310  GOTO 260
    320  REM **CTRL-CHAR EVALUATION**
    330  ON ( LEN (A$) = 0) GOTO 610: PRINT  CHR$ ( PEEK (M + (C - LM))); CHR$ (24)
    340  IF A$ =  CHR$ (127) THEN A$ =  CHR$ (4): ON C = LM GOTO 610:C = C - 1: POKE H,5: PRINT C + 1;" "
    350  IF A$ =  CHR$ (4) AND (LL + LM + 1) > C THEN LL = LL - 1: POKE M + RM - LM,160: FOR I = M + C - LM TO M + LL + 1: POKE I, PEEK (I + 1): NEXT : VTAB 2: POKE H,LM: POKE 8,RM - LM: CALL 768: PRINT : GOTO 610
    360  ON A$ <  >  CHR$ (15) OR C = (RM - 1) GOTO 370:LL = LL + (LL < (RM - LM - 1)): FOR I = M + LL TO M + C - LM STEP  - 1: POKE I, PEEK (I - 1): NEXT : POKE M + C - LM,160: VTAB 2: POKE H,LM: POKE 8,RM - LM: CALL 768: PRINT : GOTO 610
    370  IF A$ =  CHR$ (8) THEN C = C - (C > LM): POKE H,5: PRINT C + 1;" ": GOTO 610
    380  IF A$ =  CHR$ (21) THEN C = C + (C < RM - 1): POKE H,5: PRINT C + 1" ":LL = LL + ((C + 1 - LM) > LL): GOTO 610
    390  IF A$ =  CHR$ (9) THEN  GOSUB 940: GOTO 610
    400  IF A$ =  CHR$ (20) THEN TB(C + 1) =  NOT TB(C + 1):X = 160 * ( NOT TB(C + 1)) + 252 * (TB(C + 1)): PRINT : VTAB 4: POKE H,C: PRINT  CHR$ (X): GOTO 610
    410  IF A$ =  CHR$ (13) THEN  GOSUB 660: POP : GOTO 250
    420  IF A$ =  CHR$ (22) THEN RF = 1: VTAB 6: POKE H,20: PRINT "REVERSE LINEFEEDS ENABLED -- PRESS RETURN ";: GOSUB 170: PRINT : VTAB 6: PRINT LC$: GOTO 610
    430  IF A$ =  CHR$ (18) THEN A$ =  CHR$ (93): VTAB 2: POKE H,C: POP : GOTO 290
    440  IF A$ =  CHR$ (12) THEN A$ =  CHR$ (91): VTAB 2: POKE H,C: POP : GOTO 290
    450  IF A$ =  CHR$ (2) THEN A$ =  CHR$ (92):US = 1: VTAB 2: POKE H,C: PRINT A$: POKE M + C - LM,92: POP : GOTO 300
    460  IF A$ =  CHR$ (14) THEN A$ =  CHR$ (64): VTAB 2: POKE H,C: POP : GOTO 290
    470  IF A$ =  CHR$ (26) THEN  GOSUB 990: GOTO 610
    480  IF A$ =  CHR$ (7) THEN A$ =  CHR$ (95): VTAB 2: POKE H,C: POP : GOTO 290
    490  IF A$ =  CHR$ (24) THEN  GOSUB 1850
    500  IF A$ =  CHR$ (10) THEN  GOSUB 1260: POP : GOTO 260
    510  IF A$ =  CHR$ (19) THEN C = LM: PRINT : VTAB 3: POKE H,5: PRINT C + 1;" ": GOTO 610
    520  ON A$ <  >  CHR$ (5) GOTO 550: FOR I = M + (RM - LM - 1) TO M STEP  - 1: IF  PEEK (I) <  > 160 THEN C = LM + I - M + ((I - M) < (RM - LM - 1)):I = M: GOTO 540
    530  NEXT 
    540  PRINT : VTAB 3: POKE H,5: PRINT C + 1;" ": GOTO 610
    550  IF A$ =  CHR$ (23) THEN  GOSUB 1640: GOTO 610
    560  IF A$ =  CHR$ (6) THEN  GOSUB 1780: GOTO 610
    570  IF A$ =  CHR$ (16) THEN  GOSUB 1170: POP : GOTO 260
    580  IF A$ =  CHR$ (17) THEN  GOSUB 1340: GOTO 610
    590  IF A$ =  CHR$ (27) AND (C < (RM - 1)) THEN  GOSUB 1900: GOTO 610
    600  IF A$ =  CHR$ (30) THEN  GOSUB 2000
    610  RETURN 
    620  REM 
    630  REM ***UTILITY SUBROUTINES***
    640  REM 
    650  REM **PRINT LINE**
    660 X$ = "":X =  VAL (X$) +  PEEK (131) + 256 *  PEEK (132): POKE X,RM - LM: POKE X + 1,(M -  INT (M / 256) * 256): POKE X + 2, INT (M / 256):X$ =  MID$ (X$,1):AP$(R) = X$
    670  PRINT : PRINT D$;"PR#";SL
    680  PRINT  CHR$ (9);"80N";
    690  PRINT  TAB( LM);
    700  ON US GOTO 720
    710  PRINT AP$(R): GOTO 790
    720 J = 1
    730  FOR I = M TO M + LL
    740  ON  PEEK (I) <  > 92 GOTO 760:J =  NOT J: ON J GOTO 750: PRINT U1$;: GOTO 770
    750  PRINT U2$;: GOTO 770
    760  PRINT  CHR$ ( PEEK (I));
    770  NEXT 
    780  PRINT U2$
    790 R = R + 1:US = 0
    800  ON SP < 2 GOTO 810: PRINT : ON SP < 3 GOTO 810: PRINT : ON SP < 4 GOTO 810: PRINT 
    810  GOSUB 130
    820  GOSUB 870
    830  RETURN 
    840  REM **WINDOW**
    850  PRINT CL$
    860  FOR I = 1 TO LM: PRINT  CHR$ (255);: NEXT : POKE H,RM: FOR I = RM TO 79: PRINT  CHR$ (255);: NEXT 
    870  VTAB 1: POKE H,LM: POKE 8,RM - LM: CALL 768: PRINT 
    880  FOR I = M TO M + 80: POKE I,160: NEXT : VTAB 2: POKE H,LM: POKE 8,RM - LM: CALL 768: PRINT 
    890  VTAB 3: PRINT "COL: ";LM + 1;" "
    900  VTAB 5: PRINT "LINE:";R
    910  IF R > 300 THEN  VTAB 10: POKE H,19: PRINT  CHR$ (7);"NO MORE ROOM!  RESTART PROGRAM."
    920  RETURN 
    930  REM **TAB**
    940  FOR I = C + 1 + (C < RM - 1) TO RM - 1:C = I * TB(I) - 1: ON C > 0 GOTO 960: NEXT 
    950 C = RM - 1
    960 I = RM - 1: PRINT : VTAB 3: POKE H,5: PRINT C + 1: IF C + 1 > LL THEN LL = C + 1 - LM
    970  RETURN 
    980  REM **CENTER TEXT**
    990  PRINT : VTAB 6: POKE H,23: PRINT "CENTERING.....": PRINT : VTAB 2
    1000  FOR I = 0 TO LL
    1010  IF  PEEK (M + I) <  > 160 THEN X = I:I = LL: GOTO 1030
    1020  NEXT : GOTO 1140
    1030 Y = 0: FOR I = RM - LM - 1 TO 0 STEP  - 1
    1040 Y = Y + 1: IF  PEEK (M + I) <  > 160 THEN Y = Y - 1:I = 0: GOTO 1060
    1050  NEXT 
    1060 XL = RM - LM - X - Y: REM STRING LENGTH     
    1070 K = 0: FOR J = M + X TO M + X + XL - 1:K = K + ( PEEK (J) < 128): NEXT : REM HANDLE <ESC> AND UNDERLINE CODES
    1080 A = M + X:X$ = "":B =  VAL (X$) +  PEEK (131) + 256 *  PEEK (132): POKE B,XL: POKE B + 1,A -  INT (A / 256) * 256: POKE B + 2, INT (A / 256):X$ =  MID$ (X$,1)
    1090 J = 0:T =  INT ((RM - LM - XL - K) / 2) + K: IF (T + XL) > (RM - LM) THEN  PRINT : VTAB 6: POKE H,9: PRINT  CHR$ (7);"TOO MANY <ESC> CODES TO CENTER. PRESS RETURN TO RESUME TYPING ";: GOSUB 170: PRINT : VTAB 6: PRINT LC$: GOTO 1150
    1100  FOR I = M + X TO M + X + XL: POKE I,160: NEXT :
    1110  FOR I = M + T TO M + T + XL - 1:J = J + 1: POKE I, ASC ( MID$ (X$,J)): NEXT 
    1120  POKE H,LM: POKE 8,RM - LM: CALL 768
    1130 LL = T + XL:C = LM + T + XL: IF C >  = RM THEN C = C - 1
    1140  PRINT : VTAB 6: PRINT LC$: VTAB 3: POKE H,5: PRINT C + 1;" "
    1150  RETURN 
    1160  REM **PRINT TEST PATTERN**
    1170  PRINT : PRINT D$;"PR#";SL
    1180  PRINT  CHR$ (9);"80N";
    1190  PRINT  TAB( LM)"." TAB(  INT (((RM - LM) / 2) - 1))"." TAB(  INT (((RM - LM) / 2)))".";
    1200  IF  NOT RF THEN  PRINT : GOTO 1230
    1210  PRINT RF$
    1220  PRINT FF$
    1230  GOSUB 130
    1240  RETURN 
    1250  REM **ISSUE REVERSE LINEFEED**
    1260  ON  NOT RF GOTO 1320
    1270  PRINT : PRINT D$;"PR#";SL
    1280  PRINT  CHR$ (9);"80N";
    1290  PRINT RF$: PRINT RF$
    1300  PRINT FF$
    1310  GOSUB 130
    1320  RETURN 
    1330  REM **QUIT?**
    1340  PRINT : VTAB 8: POKE H,19: PRINT "Q)UIT   R)ESUME   S)TART ANEW": PRINT : HTAB 20: PRINT "PRESS Q, R, OR S:";: GOSUB 170:A$ =  CHR$ ( ASC (A$) - 32 * (A$ > "S"))
    1350  IF A$ = "Q" THEN  GOSUB 1390: PRINT CL$: END 
    1360  IF A$ = "R" THEN  PRINT : VTAB 8: PRINT LC$: VTAB 10: PRINT LC$: RETURN 
    1370  IF A$ = "S" THEN  GOSUB 1390: RUN 
    1380  GOTO 1340
    1390  PRINT : VTAB 8: PRINT LC$: VTAB 10: PRINT LC$;: POKE H,19: PRINT "SAVE THIS DOCUMENT? (Y/N)";: GOSUB 2870
    1400  IF A$ <  > "Y" AND A$ <  >  CHR$ (121) THEN 1620
    1410  PRINT : VTAB 10: PRINT LC$;: POKE H,19: PRINT "ENTER DOCUMENT NAME: ";: FOR I = 1 TO 15: PRINT "_";: NEXT 
    1420 HZ = 0:FL$ = "": POKE H,40
    1430  GOSUB 170: ON A$ >  CHR$ (31) GOTO 1480
    1440  ON A$ =  CHR$ (13) GOTO 1500
    1450  IF A$ =  CHR$ (8) AND (FL$ = "" OR  LEN (FL$) = 1) THEN 1410
    1460  IF A$ =  CHR$ (8) THEN HZ = HZ - 1: POKE H, PEEK (H) - 1:FL$ =  MID$ (FL$,1, LEN (FL$) - 1): GOTO 1430
    1470  ON A$ =  CHR$ (27) GOTO 1410: ON A$ < " " GOTO 1430
    1480  ON HZ = 15 GOTO 1430: IF A$ >  CHR$ (95) THEN A$ =  CHR$ ( ASC (A$) - 32)
    1490 FL$ = FL$ + A$: PRINT A$;:HZ = HZ + 1: GOTO 1430
    1500  ON FL$ = "" GOTO 1620
    1510  IF  MID$ (FL$,1,1) < "A" THEN  PRINT : VTAB 10: POKE H,19: PRINT LC$;"INVALID NAME -- PRESS RETURN TO REENTER ";: GOSUB 170: GOTO 1410
    1520  FOR I = 1 TO  LEN (FL$):X$ =  MID$ (FL$,I,1): IF X$ = " " THEN FL$ =  LEFT$ (FL$,I - 1) + "." +  RIGHT$ (FL$, LEN (FL$) - I): GOTO 1540
    1530  IF ((X$ < "0") AND (X$ <  > ".")) OR ((X$ > "9") AND (X$ < "A")) OR (X$ > "Z") THEN  PRINT : VTAB 10: POKE H,19: PRINT LC$;"INVALID NAME -- PRESS RETURN TO REENTER ";: GOSUB 170: GOTO 1410
    1540  NEXT 
    1550  PRINT : PRINT D$;"OPEN ";FL$;",D1"
    1560  PRINT D$;"CLOSE ";FL$
    1570  PRINT D$;"DELETE ";FL$;",D1"
    1580  PRINT D$;"OPEN "FL$;",D1"
    1590  PRINT D$;"WRITE ";FL$
    1600  FOR I = 1 TO R: PRINT AP$(I): NEXT 
    1610  PRINT D$;"CLOSE"
    1620  RETURN 
    1630  REM **SET MARGINS**
    1640  PRINT : VTAB 8: POKE H,19: PRINT "LEFT MARGIN...": VTAB 10: POKE H,19: PRINT "<-  ->    <RETURN> = ACCEPT"
    1650  PRINT : VTAB 3: POKE H,5: PRINT LM;" ": VTAB 2: POKE H,LM: PRINT  CHR$ (0);: GOSUB 170
    1660  IF A$ =  CHR$ (8) THEN LM = LM - (LM > 1): PRINT : VTAB 2: POKE H,LM: PRINT " "
    1670  IF A$ =  CHR$ (21) OR A$ =  CHR$ (32) THEN  IF LM < (RM - 2) THEN  PRINT : VTAB 2: POKE H,LM: PRINT  CHR$ (255):LM = LM + 1
    1680  IF A$ <  >  CHR$ (13) THEN 1650
    1690  PRINT : VTAB 8: POKE H,19: PRINT "RIGHT MARGIN..."
    1700  PRINT : VTAB 3: POKE H,5: PRINT RM;" ": VTAB 2: POKE H,RM - 1: PRINT  CHR$ (0);: GOSUB 170
    1710  IF A$ =  CHR$ (8) THEN RM = RM - (RM > LM + 2): PRINT : VTAB 2: POKE H,RM: PRINT  CHR$ (255)
    1720  IF A$ =  CHR$ (21) OR A$ =  CHR$ (32) THEN RM = RM + (RM < 79): PRINT : VTAB 2: POKE H,RM - 1: PRINT " "
    1730  IF A$ <  >  CHR$ (13) THEN 1700
    1740  PRINT : VTAB 8: PRINT LC$: VTAB 10: PRINT LC$
    1750 C = LM: PRINT : VTAB 3: POKE H,5: PRINT C + 1;" ": VTAB 2: POKE H,LM: POKE 8,RM - LM: CALL 768: PRINT 
    1760  RETURN 
    1770  REM **SELECT LINE SPACING**
    1780  PRINT : VTAB 10: POKE H,6: PRINT "ENTER NUMBER OF SPACES BETWEEN LINES (1-4):" SPC( 4)"(CURRENT=";SP;")";: POKE H,49: GOSUB 170
    1790  IF A$ =  CHR$ (13) THEN  GOTO 1820
    1800  IF A$ < "1" OR A$ > "4" THEN 1780
    1810 SP =  VAL (A$)
    1820  PRINT : VTAB 10: PRINT LC$
    1830  RETURN 
    1840  REM **TRUNCATE LINE**
    1850  VTAB 6: POKE H,14: PRINT "OKAY TO ERASE TEXT FROM CURSOR TO END OF LINE? (Y/N) ": VTAB 2: POKE H,C: GOSUB 170: ON (A$ = "N" OR A$ =  CHR$ (110)) GOTO 1870: IF A$ <  > "Y" AND A$ <  >  CHR$ (121) THEN 1850
    1860  FOR I = M + C - LM TO M + LL - 1: POKE I,160: NEXT : VTAB 2: POKE H,C: FOR I = 1 TO RM - C: PRINT " ";: NEXT :LL = C - LM: PRINT 
    1870  PRINT : VTAB 6: PRINT LC$
    1880  RETURN 
    1890  REM **ENTER <ESC> CODE**
    1900 A = C: VTAB 6: POKE H,13: PRINT "TYPE <ESC> CODE & PRESS <RETURN> TO RESUME TEXT ENTRY"
    1910  VTAB 2: POKE H,C: GOSUB 170: IF A$ =  CHR$ (13) THEN  PRINT : GOTO 1970
    1920  IF A$ =  CHR$ (8) THEN B =  PEEK (M + C - LM): POKE H,C: PRINT  CHR$ (B + 227 * (B = 27)):C = C - (C > A): POKE M + C - LM,160: VTAB 2: POKE H,C: PRINT " ": GOTO 1960
    1930  IF B < 27 THEN 1910
    1940  POKE M + C - LM,B:B = B + 227 * (B = 27): INVERSE : VTAB 2: POKE H,C: PRINT  CHR$ (B): NORMAL 
    1950 C = C + (C < RM - 1):LL = LL + (LL < C)
    1960  POKE H,5: PRINT C + 1;" ": GOTO 1910
    1970  VTAB 6: PRINT LC$
    1980  RETURN 
    1990  REM **CTRL-CODE TO PRINTER**
    2000  VTAB 6: POKE H,17: PRINT "TYPE CONTROL-CHARACTER TO BE SENT TO PRINTER: ";: GOSUB 170: ON ((B > 26) OR (B = 13)) GOTO 2020
    2010  PRINT : PRINT D$;"PR#";SL: PRINT  CHR$ (9);"80N";: PRINT A$;: GOSUB 130
    2020  PRINT : VTAB 6: PRINT LC$: IF B < 26 AND B <  > 13 THEN  VTAB 4: POKE H,79: INVERSE : PRINT  CHR$ (B + 64): NORMAL 
    2030  RETURN 
    2040  REM **HELP SCREEN**
    2050  PRINT : VTAB 11: FOR I = 1 TO 80: PRINT U$;: NEXT 
    2060  PRINT "<CTRL-B>: "; CHR$ (92);" (UNDERLINE TEXT)";: POKE H,39: PRINT V$ + " <CRTL-P>: PRINT TEST PATTERN"
    2070  PRINT "<CTRL-D>: CLOSE LINE (DELETE CHAR)";: POKE H,39: PRINT V$ + " <CTRL-Q>: QUIT OR START ANEW"
    2080  PRINT "<CTRL-E>: JUMP TO LAST CHARACTER";: POKE H,39: PRINT V$ + " <CTRL-R>: TYPE "; CHR$ (93)
    2090  PRINT "<CTRL-F>: SET PRINTER LINE SPACING";: POKE H,39: PRINT V$ + " <CTRL-S>: JUMP TO LINE START"
    2100  PRINT "<CTRL-G>: TYPE "; CHR$ (95);: POKE H,39: PRINT V$ + " <CTRL-T>: SET OR CLEAR TAB STOP"
    2110  PRINT "<CTRL-I>: TAB";: POKE H,39: PRINT V$ + " <CTRL-V>: ENABLE REVERSE LINEFEEDS"
    2120  PRINT "<CTRL-J>: REVERSE PRINTER LINEFEED";: POKE H,39: PRINT V$ + " <CTRL-W>: SET LINE WIDTH (MARGINS)"
    2130  PRINT "<CTRL-L>: TYPE "; CHR$ (91);: POKE H,39: PRINT V$ + " <CTRL-X>: ERASE TO END OF LINE "
    2140  PRINT "<CTRL-N>: TYPE "; CHR$ (64);: POKE H,39: PRINT V$ + " <CTRL-Z>: CENTER TEXT"
    2150  PRINT "<CTRL-O>: OPEN LINE";: POKE H,39: PRINT V$ + " <DELETE>: DESTRUCTIVE BACKSPACE"
    2160  PRINT "<CTRL-^>: SEND CONTROL CHAR TO PRINTER";: POKE H,39: PRINT V$ + " <ESC>   : EMBED PRINTER <ESC> CODES"
    2170  FOR I = 1 TO 80: PRINT U$;: NEXT 
    2180  RETURN 
    2190  REM 
    2200  REM ***GLOBAL SETUP***
    2210  REM 
    2220  TEXT : HOME 
    2230  DIM TB(80),AP$(300): REM TAB STOP AND TEXT STRING ARRAYS
    2240  REM **INSTALL BUFFER PRINTER**
    2250  FOR I = 768 TO 792: READ X: POKE I,X: NEXT 
    2260  DATA 169,149,133,7,160,0,132,6,177,6,201,27,208,2,169,126,32,7,195,200,196,8,208,240,96
    2270  REM **MACHINE AND DOS ID**
    2280 PLUS = ( PEEK (64435) = 234 OR  PEEK (64435) = 56):PD = ( PEEK (48640) = 76): REM IF PLUS THEN II OR II+; IF PD THEN PRODOS
    2290  REM ***PRINTER CODES***
    2300 U1$ =  CHR$ (27) +  CHR$ (88): REM ESC+X TURNS ON UNDERLINE
    2310 U2$ =  CHR$ (27) +  CHR$ (89): REM ESC+Y TURNS OFF UNDERLINE
    2320 FF$ =  CHR$ (27) +  CHR$ (102): REM ESC+LOWERCASE F ACTIVATES FORWARD LINEFEEDS
    2330 RF$ =  CHR$ (27) +  CHR$ (114): REM ESC+LOWERCASE R ACTIVATES REVERSE LINEFEEDS
    2340  REM **ALL-PURPOSE REALS**
    2350 I = 0:J = 0:K = 0:X = 0:Y = 0
    2360  REM **DEDICATED REALS**
    2370 SL = 1: REM PRINTER SLOT
    2380 RF = 0: REM REVERSE LINEFEED FLAG
    2390 R = 1: REM ROW
    2400 C = 1: REM COLUMN
    2410 LM = 5: REM LEFT MARGIN
    2420 RM = 75: REM RIGHT MARGIN
    2430 LL = 0: REM LINE LENGTH
    2440 M = 38144: REM START OF TEXT BUFFER
    2450 H = 1403: REM HORZ POSITION
    2460 SP = 1: REM DEFAULT LINE SPACING
    2470  REM **STRING EQUIVALENTS**
    2480 X$ = ""
    2490 V$ =  CHR$ (252)
    2500 U$ =  CHR$ (95)
    2510 D$ =  CHR$ (4)
    2520 CL$ =  CHR$ (12): REM CLEAR SCREEN
    2530 LC$ =  CHR$ (29): REM CLEAR LINE
    2540 BS$ =  CHR$ (8): REM BACKSPACE
    2550  REM   
    2560  REM ***TITLE***
    2570  REM   
    2580  VTAB 4: HTAB 9: PRINT "TURN ON PRINTER OR....": HTAB 6: PRINT "PRESS PRINTER 'SELECT' BUTTON."
    2590  PRINT D$;"PR#1": PRINT 
    2600  HOME : POKE 1912,0
    2610  PRINT D$;"PR#3": PRINT CL$
    2620 A$ = "TYPEWRITER"
    2630 CL =  - 16336: SPEED= 150
    2640  VTAB 6: POKE H,31: FOR I = 1 TO 10: PRINT  MID$ (A$,I,1) + " ";:X =  PEEK (CL) +  PEEK (CL) +  PEEK (CL) +  PEEK (CL) +  PEEK (CL): NEXT 
    2650  SPEED= 255
    2660  PRINT : VTAB 10: POKE H,32: PRINT "BY JAMES D EVANS"
    2670  VTAB 14: POKE H,24: PRINT "COPYRIGHT 1988 BY MICROSPARC, INC."
    2680  VTAB 16: POKE H,27: PRINT "* PRESS <RETURN> TO BEGIN *";
    2690  POKE  - 16368,0: WAIT  - 16384,128: POKE  - 16368,0
    2700  RETURN 
    2710  REM 
    2720  REM ***ERROR TRAP***
    2730  REM 
    2740  POKE 216,0:EC =  PEEK (222):LN =  PEEK (218) +  PEEK (219) * 256: CALL 54915
    2750  PRINT : VTAB 10
    2760  IF EC = 4 THEN  PRINT "DISK IS WRITE-PROTECTED -- PLEASE REMOVE TAB FROM NOTCH ";: GOTO 2830
    2770  IF EC = 8 THEN  PRINT "DISK I/O ERROR -- PLEASE CHECK DISK AND DRIVE ";: GOTO 2830
    2780  IF EC = 9 THEN  PRINT "DISK IS FULL -- PLEASE INSERT A DIFFERENT DISK ";: GOTO 2830
    2790  IF EC = 10 THEN  PRINT "FILE LOCKED -- PLEASE THINK OF A DIFFERENT FILE NAME ";: GOTO 2830
    2800  IF EC = 11 THEN  PRINT "ILLEGAL FILE NAME -- PLEASE THINK OF A DIFFERENT NAME ";: GOTO 2830
    2810  IF EC = 255 THEN  PRINT "<CTRL-C> IS NOT AN ACCEPTABLE KEYSTROKE -- PLEASE NOTE ";: GOTO 2830
    2820  PRINT "ERROR #";EC;" HAS OCCURRED IN LINE ";LN;" -- PLEASE NOTE ";
    2830  PRINT "AND HIT <RETURN> ->";
    2840  GOSUB 170: IF A$ <  >  CHR$ (13) THEN  GOTO 2840
    2850  PRINT : VTAB 10: PRINT LC$: ONERR  GOTO 2740
    2860  GOTO 260
    2870  GET A$: IF A$ <  > "Y" AND A$ <  > "N" AND A$ <  > "y" AND A$ <  > "n" THEN 2870
    2880  RETURN 
    
  • tiny-c Reference Manual Excerpt

    tiny-c Reference Manual Excerpt

    The project found this little manual instructive. You could say it is a kind of C flavour that is inspired by BASIC!

    https://archive.org/details/tiny-c_manual

    Preface

    The sources of ideas that went into tiny-c are many. First there is BASIC [Kemeny & Kurtz 1967]. BASIC has become the de facto standard training language in the United States. It industry, is popular in high schools, universities, even in where it is used for some production work. Although BASIC has its faults, its one big strength is that it is easy to learn. This is largely because it offers a single computing environment. You can enter new program lines, change old ones, and start a program running all from one command environment. You do not have to remember the environment you are in, i.e., you edit mode, compile mode, link mode, system mode, run mode, etc., when giving a command. There are no commands to shift from mode to mode. There no relocatable object modules, link editors, and all the other paraphernalia of “real” computers. Is is very simple and very adequate. Thus a focus is made on the essential elements of computing, as opposed to the elements of “wrestling” with a computer.

    The LOGO language [Feurzeig 1975] is in many ways similar to tiny-c. It offers a well-structured language based on BASIC, as well as a single environment for programming and execution. LOGO was used experimentally in public schools with very young children. The experiment showed that children could grasp simple computer concepts and work through a prepared set of exercises, and then do creative work of their own.

    C [Ritchie, Kernighan, & Lesk 1975] is a computer language designed by Dennis Ritchie, at Bell Telephone Laboratories, tiny-c borrows its overall structure from C. C is broadly used in universities and in industry. It has been used to program a very advanced and powerful computer operating system, called UNIX [Ritchie & Thompson 1974]. At yet it is a very simple language. C has no native input/output, e.g., read or print statements. Input/output is done using functions. Thus C concentrates on COMPUTING facilities, and allows external development or elaborations of input/output. tiny-c has adopted this idea.

    The command environment for tiny-c is written in tiny-c. It needs no translation to the micro-processor’s machine language. This corresponds somewhat to the idea of using C as the programming language to implement UNIX. So, although intended as a training language for structured programming, tiny-c is a powerful language.

    The tiny-c OWNER’S MANUAL is trying to reach four audiences at the same time. For those new to structured programming we have a brief tutorial and program walk-through so they can get the gist of it without getting bogged down in details. Experienced users of structured programming will find that the references sections let them quickly discover the features of tiny-c. For those who want to know how the tiny-c interpreter works, we have described its operation. And, finally, for those who want to install tiny-c on their home computer, we have included a complete installation guide.

    Introduction

    What is tiny-c? tiny-c is

    • a language, plus
    • a standard library, plus
    • a program preparation system.

    Without any other software aids, you can prepare tiny-c programs, run them, edit them, store them on a cassette or floppy disk, and read them back later.

    tiny-c is a structured programming language which has if-then-else, while-loops, functions, global and local variables, and character and integer data types, pointers, and arrays.

    tiny-c is independent of operating systems. You can interface it easily to the input/output routines on your computer.

    tiny-c can invoke your own machine language subroutines so the tiny-c programming language can be fitted to your system and your system can be reflected in and extend the language.

    A tiny-c Program Walk-Through

    Figure 1-1 is a complete tiny-c program consisting of two functions.

    FIGURE 1-1

    /* guess a number between 1 and 100
    /* T. A. Gibson, 11/29/76
    
    guessnum [
        int guess, number
        number = random (1,100)
        pl "guess a number between 1 and 100"
        pl "type in your guess now"
        while (guess != number) [
            guess = gn
            if (guess == number) pl "right !!"
            if (guess > number) pl "too high"
            if (guess < number) pl "too low"
            pl""; pl""
        ] /* end of game loop
    ]     /* end of program
    
    /*
    /* random-generates a random number
    
    int seed, last /* globals used by random
    random int little, big [
        int range
        if (seed == 0) seed = last = 99
        range = big - little + 1
        last = last * seed
        if (last < 0) last = -last
        return little + (last/8) % range
    ]

    End of FIGURE 1-1

    How does this program work? Let’s do a program walk-through:

    Starting at the top, the first two lines are COMMENTS. A comment start with /* and goes to the end of the line.

    guessnum” is the name of a FUNCTION which is called to start the program.

    Following “guessnum” is a COMPOUND STATEMENT, which is 12 lines long, the last line being:

    ]     /* end of program

    A compound statement is everything between balanced left-right brackets.

    The first SIMPLE STATEMENT in guessnum is:

    int guess, number

    This declares two INTEGER VARIABLES named “guess” and “number”. All variables in tiny-c must be declared. When executed, the int statement will create the variables, and given them an initial value of zero.

    The second simple statement in guessnum is

    number = random (1,100)

    This sets number equal to the value of the tiny-c program function random executed with its first ARGUMENT equal to 1 and its second argument equal to 100. In our program the function random returns a random number between 1 and 100.

    On the next line, pl is a tiny-c LIBRARY FUNCTION which prints a line. In prints the quoted string which is its argument.

    while sets up a LOOP. The general form of while is:

    while (expression) statement

    In this instance, the EXPRESSION part is

    guess != number

    where != means not equal to. This expression is evaluated, and if it is true, the statement is done, and then the expression is evaluated again. If it is false, the statement is skipped. Initially, guess is 0 and number cannot be less than `, so the expression is initially true. Therefore the statement is executed.

    The statement is compound, and is composed of six simple statements. The first of these statements is

    guess = gn

    gn, which stands for “get number” is another standard library function. It reads a number types in by the user at the terminal, and returns that value. So here the program waits until the user types a number and a carriage return, and then guess is assigned the number typed.

    The next three simple statements are if statements. The general form of the if statement used here is

    if (expression) statement

    where statement is executed if the expression is true.

    Statements five and six of the while’s compound statement are pl"". pl"" goes to a new line, and prints nothing. The semicolon allows you to write more than one simple statement on the same program line. So

    pl"" ; pl""

    prints two blank lines.

    Now we are at the end of the while loop. Since the expression part of the while was true, the while statement is executed again. This starts with another evaluation of the expression to see if it is true or false. If the first guess is not equal to number, the compound statement is executed again. Another guess is read, the appropriate remark is made, and two more blank lines are printed; the while is done yet again. Eventually, the user gets the right number and guess is equal to number. This will cause a “right!!” and two blank lines to be printed. The while condition is then tested again. The expression guess != number is evaluated and found to be false, so the entire compound statement of the while is skipped, which brings us to the end of guessnum. The game is over. The program stops because the end (the last ]) of guessnum is reached.

    Before we walk through random, notice the integers seed and last are declared outside of both guessnum and random. They are called GLOBAL VARIABLES. They will be created once when the program is started. They are initially zero, and are known and usable by both guessnum and random. On the other hand, guess, number, and range are LOCAL VARIABLES. guess and number are known and usable only within guessnum, while range is local to random.

    The first line of random gives the function name. And, before the [, it declares two integer arguments, little and big. A VALUE must be supplied for each argument when a function is called. The call in the sixth line of guessnum sets little to 1, and big to 100. Now we enter the BODY of the function random.

    range is declared an integer and is initially zero. On the first call, seed is zero. Now seed and last are both set to 99. range is calculated, and is 100. last is set to the product of the last and seed which is 9801. This is not less than 0, so the statement part of the if is not evaluated.

    We next come to the return statement. It does two things. First, it evaluates the expression. The result is made the VALUE OF THE FUNCTION. Second, it returns control to the program that called the function. tiny-c expressions are similar to algebraic expressions. The symbol + means add, / means divide, and - means subtract (or take the negative). To indicate multiplication, a * is used. An unusual symbol is %, which means divide the left side by the right side and take the REMAINDER (not the quotient). So, for example,

    1225 % 100

    is 25.

    Thus the return statement calculates the expression:

    little + (last/8) % range
    
    = 1 + (9801/8) remainder 100
    
    = 1 + 1225 remainder 100
    
    = 1 + 25
    
    = 26

    The value 26 is returned as the value of function random. It also leaves 9801 in last, and 99 in seed. Since these are global variables, their values are retained between function calls. This is not true of local variables like range. Their values are retained only during the execution of the function in which they are defined. When that function is left their values are lost.

    On a second call to random, range is recreated, and reinitialised to zero. seed is not zero, so seed and last are not set to 99, but remain 99 and 9801 respectively. range is recalculated as 100. Then

    last = last * seed
    
         = 9801 * 99
         
         = 970299

    This number is too big for tiny-c. Any computer has a limit on the size of the numbers that can be computed. tiny-c numbers must be in the range

    -32768 <= number <= 32767.

    last OVERFLOWS this range. It will be assigned the value -12741! (We explain this more completely in Section 2.11.) This is less than 0, so the next statement assigns last the value 12741. Then the return statement calculates:

      1 + (12741/8) remainder 100
    
    = 1 + 1592 remainder 100
    
    = 93

    This is returned as the second value of random.

    REVIEW OF THE WALK-THROUGH

    The purpose of the walk-through is to get a feeling for programming in tiny-c. We have seen that

    • A tiny-c program is a set of functions.
    • Some functions are standard library functions, line gn and pl.
    • Global variables stay around and hold their values. Local variables come and go.
    • Function and variable names can be as long as you want.
    • A group of statements enclosed in brackets makes a compound statement which can be treated just like a simple statement.

    Structured Programming – What tiny-c Is All About

    Perhaps you have heard structured programming described as “go-to-less” programming. Or programming with just if-then-else and do-while control statements. Such remarks oversimplify what structured programming is all about. The essence of structured programming is PROGRAM CLARITY. You can write programs in small, modular parts, with easy-to-follow program flow. You can use well-chosen, descriptive variable names. This leads to clear, understandable programs. Program clarity is what structured programming is all about.

    We discuss here four principle ideas that make program clarity possible. These are: modularity, predictable program flow, local variables, and the simple idea of meaningful variable names.

    MODULARITY in software is just as important as modularity in hardware. It makes it humanly possible to deal with complexity. A module is a brick or atom used for building bigger modules. Seen from within, a module may be very complex but from the outside it is an indivisible whole. Software modularity is achieved through the use of FUNCTIONS.

    PROGRAM FLOW is predictable if you can point to any statement and easily answer the question “under what conditions is this statement executed?” This is particularly important if the program is 20 or 30 pages long, and still has bugs. Scanning the whole program and drawing arrows is no fair. That’s not considered an easy way to answer the question. Predictable program flow can be achieved in many ways. In tiny-c, it is done with COMPOUND STATEMENTS.

    Compound Statements

    Functions also make it possible to hide variables used in a strictly local context. The variable n is very popular; it’s used frequently to count things. Have you ever had a program blow up because you were using n in two places for two purposes? The fix was to change one of them to n1. A better idea is in the concept of LOCAL and GLOBAL variables.

    As for long, MEANINGFUL NAMES for variables and functions — just look at the sample programs to see the improvement.

    David Gries suggests structured programming be called “simplicity theory”, and characterizes it as “an approach to understanding the complete programming process” [Gries 1974]. As a pleasant dividend, structured programming is more enjoyable than monolithic programming. It should certainly, therefore, be a part of personal computing. To begin our look at tiny-c as a structured programming language, let’s look at the foundation of functions and predictable program flow — the compound statement.

    When you write a program, you write a list of statements:

    x = x-1 
    a = b+c 
    b = b*2-c 
    x = b-a 

    The idea behind a compound statement is to make one statement – a molecule – out of a set of statements – some atoms. This is done in tiny-c by

    [x = x-1 
     a - b+c 
     b = b*2-c 
     x = b-a] 

    Anywhere you can write a simple statement you can also write a compound statement. This sounds simple, but the effect is powerful. For example most programming languages have an if statement similar to this:

    if (logical expression) statement 

    So you can write

    if (x>0) x = x-1 

    But make the statement part compound, and you have this capability:

    if (x>0) [ 
        b = b*2-c 
        a = b+c 
        X = x-1 
    ] 

    This multiline if is not some special kind of if. It is still:

    if (logical expression) statement

    But the statement part is compound. The compound statement is treated as an indivisible unit. It is either all done or all not done depending on the value of the logical expression.

    The compound statement also is a natural for LOOPS. There is a big difference among the various programming languages in how you write loops, but they all have one thing in common. A loop has a beginning and an end. A compound statement can be used to express this. The looping statement is:

    while (logical expression) statement

    Notice the similarity with the if. Only the keyword has changed. Here’s how while works. The logical expression is evaluated. If it is true, then the statement is executed, and then the while is done again. The effect is a repeated if, i.e., a loop. As long as the logical expression remains true, the statement is done again and again. Eventually something in the statement causes the logical expression to become false, and the loop terminates. Of course, the statement can be compound, as in:

    while (x>0) [ 
        a = b+c 
        b = b*2-c 
        x = x-1 
    ] 

    The compound statement is a natural way to delimit the beginning and end of loops.

    With one simple idea, the compound statement, two things are achieved. The if statement is more powerful than is common in non-structured programming languages. The concept of a loop collapses to a simple repeated if or while statement. In both situations you are stating conditions under which the statement — whether simple or compound – is to be executed.

    Nesting Compound Statements

    ANYWHERE YOU CAN WRITE A SIMPLE STATEMENT, YOU CAN WRITE A COMPOUND STATEMENT.

    That is a fundamental rule. A compound statement contains simple statements. Therefore a compound statement can contain compound statements. Figure 1-2 illustrates this.

    FIGURE 1-2

    [ x = x-1
      a = b*c
      b = b*2-a
      x = b-a

    a=b+c is a simple statement. The rule says a compound statement can be written here. For example:

    [ x = x-1 
        [ a=b+c 
          w = y+2*x+w 
          y = 17 
        ] 
      b = b*2-a 
      x = b-a 
    ] 

    End of FIGURE 1-2

    The substitution of a compound for a simple shown in Figure 1-2 is certainly allowable, but is of no practical value.

    The real utility in nested compounds is in writing nested if and while statements. Figure 1-3 is therefore a more realistic example of the use of compound statements.

    FIGURE 1-3

    if (x>0) [ 
        while (x<limit) [
            if (case==1) [
                y = 0; w = 99
            ]
            if (case==2) [ 
                y = 99; w = 0
            ] 
            nextaction 
            x = x+1 
        ] 
    
    ] 

    End of FIGURE 1-3

    In Figure 1-3, if you remove everything except the brackets, you have this:

    [ [ [ ] [ ] ] ] 

    This is what is meant by compound statements. Brackets are used to form program units the same way parentheses are used to create arithmetic statements. The main difference is that a pair of brackets is preceded by a function name, or a logical expression. In the first case you’re naming the contents of the brackets and in the second you’re stating the conditions under which the contents are to be executed.

    Readable Program Flow

    In Figure 1-3, look at the “y=0” in the fourth line. How can it be reached? Only if case is 1, and x is less than limit. No go-to can lead here, either accidentally or on purpose.

    How can “nextaction” be reached? Only if x is less than limit, and then only after possible changes to y and w. This program has simple, predictable flow. The only way a statement other than a while can be reached is from directly above, whiles can also be reached from their matching ] below.

    Indenting and the Placement of Brackets in Compound Statements

    The brackets alone define the “structure” of a program. Indenting means nothing. But one of the purposes of structured programming is to make programs more readable and, hence, more understandable. A good choice of indenting style is very important to program readability. There are several styles to choose from. The actual choice is not too important. But once you choose a style, stick to it. Consistency IS important.

    One easily explained style is to align matching brackets vertically. This looks like:

    if (x<0) 
    [   statement 
        statement 
            "
            "
            "
    ] 

    A problem with this is that when editing the first statement, care must be taken to keep the [ intact. So some use this style:

    if (x<0) 
    [ 
    
        statement 
        statement 
            "
            "
            "
    ] 

    This takes an extra line. Also there is a visual break between the if and its statements. So some take the left bracket and move it to the end of the preceding line:

    if (x<0) [ 
        statement
        statement 
            "
            "
            "
    ] 

    The right bracket is now vertically aligned with the if or while that preceded the compound statement.

    You may pick one of these, or invent a style of your own. But, we repeat, whatever you decide to do, do it consistently.

    Functions

    A large software project can usually be broken into natural parts, and each part programmed and debugged as a separate unit. Each of these units then becomes a reliable building block for the construction of still larger parts of the project. Sometimes units can be designed to be useful in many projects.

    In various programming languages these building blocks are called subprograms, subroutines, or, as in tiny-c, FUNCTIONS. Here is a tiny-c function for any computer versus human game:

    game [ 
        getready 
        while ( stillplaying ()) [ 
           humanturn 
           if (stillplaying ()) computerturn 
        ] 
        gameover 
    ] 

    The name of the function is “game”. The compound statement that follows is called the body of the function. Each [ can be read as “do all of this”, and its matching ] read as “end of this”, game divides the design of a game program into five parts:

    getready (which initializes things, and 
              prints instructions if 
              requested), 
    
    stillplaying (which determines if the 
                  game is still going, and 
                  returns true if it is, 
                  otherwise false), 
    
    humanturn (which conducts the human's 
               turn), 
    
    computerturn (which conducts the 
                  computer's turn) , 
    
    gameover (which computes and prints 
              scores, makes remarks about 
              the human's skill, promotes 
              the human, or whatever). 

    The game function is the first step in divide-and-conquer or top-down program development. Let’s carry this development one step further. The getready function can be expanded this way :

    getready [ 
        ps "Do you want instructions?" 
        if (gc()=='y') instructions 
        setupboard 
    ] 

    getready divides the initialization into two parts: instructions, and setupboard.

    (Note: ps prints a character string, gc() reads a character, and == 'y' tests if the character is a y.)

    Notice that both game and getready are universal. They can be used in many game programs. Programming in this fashion eventually leads to a library of useful, general purpose functions. These can be pulled off the shelf into a software project. You know they work because they were used before. Your programming becomes more productive, and more pleasant.

    The next time you’re programming a sizable project, i.e., anything more than a page, try to identify subsets of the logic usable in other projects. Capture these as functions. It is gratifying to discover a general purpose function where none was suspected.

    Local and Global Variables

    A LOCAL VARIABLE is one that is known only inside a function. It can be used and changed only within the body of the function. Even its name is unknown outside the function. In fact, its name can be used in other functions without conflict. This is what makes local variables useful.

    Take a look at Figure 1-4. There are four local variables in these two functions. The variables n and maximum are local to a function. The variables n and total are local to another function. If either of these functions calls the other, the values of n will not be confused since they only have meaning inside the body of their own functions. It helps to think of local variable names as being preceded by the possessive form of the function to which they are local. For example, a function’s n and another function’s n.

    FIGURE 1-4

    afunction [ 
        int n, maximum 
        n=0 
        while (n<maximum) [ 
              .
              .
              .
            n=n+l 
        ] 
    ] 
    
    anotherfunction [
        int n, total 
              .
              .
              .
        n = n+2 
        total = total+n 
              .
              .
              .
    ] 

    End of FIGURE 1-4

    The value of locals is obvious to anyone who has spent a nasty debugging session trying to find out where, in a huge program, some variable is getting changed.

    Of course not all variables can be local. Some must be shared by many functions. These are called GLOBALS. They should be used infrequently, as they do cause debugging headaches. Choosing good, descriptive names for globals alleviates the problem, . A global named “k” is inviting disaster. Call it “klingonsleft” and you’re less likely to accidentally use it for two purposes. Also you’ve given a reader of your program a pretty good clue to the variable’s use.

    Summary – And Where We Go From Here

    We’ve walked through a simple program to get a feel for tiny-c, and we’ve discussed the virtues of structured programming. These are just the preliminaries. Now it’s time for the main events. First, a complete definition of the tiny-c language. Chapter II is devoted to this task. To prepare programs you need an editor and way of debugging. The Program Preparation System (PPS) is described in Chapter III. Examples are excellent learning tools: Chapter IV has several example programs. Maybe you want to make it bigger, better, or faster? Chapter V explains how tiny-c works. Finally, of course you’ll want to get tiny-c up and running on your own computer. Chapters VI and VII explain how to install tiny-c on an 8080 or PDP-11.

  • SKETCH.BAS

    SKETCH.BAS

    Please find reproduced here a working port of SKETCH for the TRS Model 100 to Stefan’s BASIC for the Arduino micro-controller platform.

    Full code listing:

    https://basiclang.solarpunk.au/d/15-sketchbas

    Please find the archive.org link to the 1984 book here:

    https://archive.org/details/25GamesForYourTrs80Model100/page/n49/mode/1up

    Live Coding Session

    Video File

    Peertube Streaming

    CODE LISTING

    10 REM ***********
    20 REM *         *
    30 REM * Sketch  *
    40 REM *         *
    50 REM ***********
       
    55 REM *** Instructions ***
       
    60 CLS:PRINT:PRINT
    70 PRINT TAB(12)"Instructions?"
    80 PRINT:PRINT TAB(16)"(Y/N)"
    90 GET A$:IF A$="" GOTO 90
    100 IF A$="Y" OR A$="y" THEN GOTO 110 ELSE GOTO 270
    110 CLS:PRINT:PRINT
    120 PRINT TAB(2)"You may use either Sketch or Draft"
    130 PRINT TAB(2)"modes. Sketch uses a moving line to"
    140 PRINT TAB(2)"draw, while Draft draws lines between"
    150 PRINT TAB(2)"two points you specify by hitting ";CHR$(34);"S";CHR$(34);"."
    160 PRINT:PRINT TAB(1)"= Hit any key for Sketch instructions =";
    170 GET A$:IF A$="" GOTO 170
    180 CLS:PRINT
    190 PRINT TAB(12)"==  Sketch =="
    200 PRINT TAB(13)"[Controls:]"
    210 PRINT TAB(2)"Arrow Keys -- Change direction"
    220 PRINT TAB(2)"Number keys -- Slow down cursor"
    230 PRINT TAB(2)CHR$(34);"F";CHR$(34);" -- Return to top speed"
    240 PRINT TAB(2)"Space Bar -- Toggle cursor (ON)-(OFF)"
    250 PRINT:PRINT TAB(11)"== Hit any key =="
    260 GET A$:IF A$="" GOTO 260
        
    265 REM *** Set Delay ***
        
    270 CLS:PRINT:PRINT
    280 PRINT TAB(6)"Enter speed desired:"
    290 PRINT
    300 PRINT TAB(6)"[1] Fast to [9] Slow"
    310 GET A$:IF A$="" GOTO 310
    320 OG=VAL(A$)*10:IF OG<1 GOTO 310
    330 DL=OG
        
    335 REM ***  Choose Sketch or Draw Mode ***
        
    340 CLS:PRINT:PRINT
    350 PRINT TAB(4)"Do you want:"
    360 PRINT:PRINT TAB(6)"[S]ketch mode"
    370 PRINT TAB(6)"[D]raft mode"
    380 GET A$:IF A$="" GOTO 380
    390 IF A$="S" OR A$="s" GOTO 420
    400 IF A$="D" OR A$="d" GOTO 710
    410 GOTO 380
    420 CLS
    430 REM FOR N=1 TO VAL(RIGHT$(TIME$,2))
    440 REM DM=RND(1)
    450 REM NEXT N
    460 X=239:Y=63
    470 X1=INT(RND(X)*X)+1:Y1=INT(RND(X)*Y)+1
    480 COLOR 255: PLOT X1,Y1
        
    485 REM *** Sketch Mode ***
        
    490 GET A$
    500 X1=X1+XD:IF X1>239 THEN X1=239
    510 IF X1<0 THEN X1=0
    520 Y1=Y1+YD:IF Y1>63 THEN Y1=63
    530 IF Y1<0 THEN Y1=0
    540 DELAY DL*10
    550 IF FLAG<>1 GOTO 570
    560 COLOR 255:PLOT X1,Y1:DELAY 100:NEXT N:COLOR 0:PLOT X1,Y1:GOTO 580
    570 COLOR 255:PLOT X1,Y1
    580 IF A$="" GOTO 490
    590 IF VAL(A$)<1 GOTO 610
    600 DL=DL*VAL(A$)
    610 IF A$="F" OR A$="f" THEN DL=OG
    620 A=ASC(A$)
    630 IF A<>32 GOTO 650
    640 IF FLAG=1 THEN FLAG=0 ELSE FLAG=1
        
    650 IF A=4 GOTO 670
    651 IF A=5 GOTO 690
    652 IF A=19 GOTO 680
    653 IF A=24 GOTO 700
    654 GOTO 490
        
    655 REM *** Change direction of Cursor ***
        
    660 REM ON A-27 GOTO 670, 680, 690, 700
    670 YD=0:XD=1:GOTO 490 REM RIGHT
    680 YD=0:XD=-1:GOTO 490 REM LEFT
    690 XD=0:YD=-1:GOTO 490 REM UP
    700 YD=1:XD=0:GOTO 490 REM DOWN
        
    705 REM *** Draft  Mode ***
        
    710 X1=40:Y1=40
    720 CLS
    730 GET A$
    740 X1=X1+XD:IF X1>239 THEN X1=239
    750 IF X1<0 THEN X1=0
    760 Y1=Y1+YD:IF Y1>63 THEN Y1=63
    770 IF Y1<0 THEN Y1=0
    780 DELAY DL*10
    790 COLOR 255:PLOT X1,Y1
    800 DELAY 50
    810 COLOR 0:PLOT X1,Y1
    820 IF A$="" GOTO 730
    830 IF A$="S" or A$="s" GOTO 850
    840 GOTO 990
        
    845 REM *** Set one point ***
        
    850 PO=PO+1
    860 COLOR 255:PLOT X1,Y1
    870 IF PO=1 THEN X2=X1:Y2=Y1:GOTO 730
        
    875 REM ***  Draw Line ***
        
    880 LINE X1,Y1,X2,Y2
    890 PO=0
    900 GET A$:IF A$="" GOTO 900
    910 IF A$="S" OR A$="s" GOTO 950
    920 A=ASC(A$)
    930 IF A<>4 AND A<>5 AND A<>19 AND A<>24 GOTO 900
    940 GOTO 980
    950 PO=1
    960 X2=X1:Y2=Y1
    970 GOTO 900
        
    975 REM *** Change Cursor Direction ***
    
    980 IF A=4 GOTO 990 REM RIGHT
    981 IF A=5 GOTO 1010 REM UP
    982 IF A=19 GOTO 1000 REM LEFT
    983 IF A=24 GOTO 1020 REM DOWN
    984 GOTO 900
    990 YD=0:XD=1:GOTO 730
    1000 YD=0:XD=-1:GOTO 730
    1010 XD=0:YD=-1:GOTO 730
    1020 YD=1:XD=0:GOTO 730
    
  • GOBLINS.BAS Live Coding Session

    GOBLINS.BAS Live Coding Session


    “Coding done quick”.

    I sped a ~1 hour stream up by 400% and this is what I got.

    This game was published as a type-in pamphlet here.

    You can also download the finished game here.

    Original blog post here.


    mailto:vidak@member.fsf.org

    https://matrix.to/#/@vidak:solarpunk.au

    https://retro.social/@permacomputer


    Jan 2026.

  • A Simple Guide to Porting Your BASIC

    A Simple Guide to Porting Your BASIC

    You may have considered porting some BASIC code from one dialect to another.

    Do not be dissuaded, the process can sometimes be rewarding.

    When porting on BASIC to another, I have found the following guides to be useful:

    Strip out all lines with references to POKE and PEEK.

    This is my preferred strategy. Almost every system addresses memory differently, and has a different memory map.

    Check the way arrays work on your target machine. Especially string arrays.

    Many systems will allow simple string arrays, such as: S$(100)

    But how long are these strings? How does the machine assign and evaluate each index? Some machines may even accept two-dimensional string arrays.

    Every application will make use of some subset, and not every single feature of it’s machine’s arrays, so don’t be daunted. Sometimes the job can be done with very little modification.

    INPUT statements may be an issue. Some parse input differently.

    Does you machine have LINE INPUT, or just INPUT?

    INPUT splits the input into multiple variables on commas in the input, whereas LINE INPUT puts the entire line into one variable.

    File operations like OPEN and CLOSE will accept different arguments.

    This can be quite tricky.

    Since you have made it this far into the blog post, please consider the following table of conversions between my most used BASICS:

    DartmouthAPPLESOFTATARI 8-BITBBC BASICCOMMODORE BASIC V2
    OPENFILE #file, “filenameOPEN filenameOPEN #fileno,mode control code,filenamevar = OPENIN, var = OPENOUTOPEN #exp, fileno, mode, “filename”
    CLOSECLOSE “filenameCLOSE #fileno, #filenoCLOSE #fileno; CLOSE #0 (all files)CLOSE #fileno
    LOADLOAD filenameLOAD “disk:filenameLOAD “filenameLOAD “filename“,8,[disk]
    PRINTPRINT #file, record, …PRINT exp, exp, …PRINT #fileno, record, record, …PRINT #filename, record, record, …PRINT #fileno, record, record, …
    INPUTINPUT #file, record, …INPUT [string,] var, var, …INPUT #[file,disk] varINPUT #filename, record, record, …INPUT “string” var, var, …

    Is your BASIC indexed from one (1) or zero (0)?

    Because this is an easy one. Convert every index accordingly:

    (e.g.) DIM A(255) -> DIM A(256)
    
    (e.g.) FOR I=0 TO 255 -> FOR I=1 TO 256
  • Watch Me Code A Procedurally Generated Game in BASIC!

    Watch Me Code A Procedurally Generated Game in BASIC!

    “Coding done quick”.

    I sped a ~2 hour stream up by 800% and cut some boring bits out, and this is what i got.

    Experimenting with a large, procedurally generated world.

    mailto:vidak@member.fsf.org

    https://matrix.to/#/@vidak:solarpunk.au

    https://retro.social/@permacomputer

    jan 2026

    https://sr.ht/~vidak/oats-for-my-goats

    https://basiclang.solarpunk.au/d/6-game-oats-for-my-goats