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:
;***********************************************************************
;
; 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
;
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:
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:
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:
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.
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:
Dartmouth
APPLESOFT
ATARI 8-BIT
BBC BASIC
COMMODORE BASIC V2
OPEN
FILE #file, “filename“
OPEN filename
OPEN #fileno,mode control code,filename
var = OPENIN, var = OPENOUT
OPEN #exp, fileno, mode, “filename”
CLOSE
CLOSE “filename“
CLOSE #fileno, #fileno
CLOSE #fileno; CLOSE #0 (all files)
CLOSE #fileno
LOAD
LOAD filename
LOAD “disk:filename“
LOAD “filename“
LOAD “filename“,8,[disk]
PRINT
PRINT #file, record, …
PRINT exp, exp, …
PRINT #fileno, record, record, …
PRINT #filename, record, record, …
PRINT #fileno, record, record, …
INPUT
INPUT #file, record, …
INPUT [string,] var, var, …
INPUT #[file,disk] var
INPUT #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
Scriptor is an easy-to-use, full-scrolling, character-oriented, multi-function word processor, requiring an Atari 400/800 with a minimum of 32K of memory (40K recommended), an Epson MX-80 or Atari 825 printer, and an Atari disk drive. It is programmed in both BASIC and machine language. For instructions on typing in the program, see the section under: Typing It In.
Through The Ruby
Computers don’t just calculate with numbers – they can also work with text. Five inch disks can replace stacks of files. Computers can sort, search, select, and update any kind of information. They can focus information. In this sense, the computer is like the ruby crystal in a laser. Ordinary random light waves are transformed and concentrated through the ruby into a tight, powerful beam. Computers can do the same for information.
Word Processing
Electronic text is more “liquid”, easier to work with, than words solidified on paper (hard copy). This is what makes word processing special: the extraordinary editing power it gives you. Distinctions between a rough draft and a final draft are meaningless; the work is typed, changed dynamically, and stored to disk. It can then later be recalled, revised, and printed out. Very little retyping is necessary. What a boon for anyone who writes.
Converts to word processing immediately notice an improvement in their writing. The entire manuscript becomes “alive”, not committed to paper. Changing a word or a sentence, inserting a line or a paragraph are all accomplished with ease. For example, take just one key, the backspace key (called RUBOUT on some computers or terminals). When this key is struck, the last character typed is erased from the screen. Compare this to the frequently elaborate typewriter correction schemes.
Besides the disk file, which has already been mentioned and which will be explained in greater detail later, an important concept in word processing is the cursor. Named after the clear plastic slide on a slide rule, the cursor shows you where the next character you type is going to appear. It usually looks like an underline, “__“, or a solid square. Users familiar with any computer have already encountered the cursor. The computer itself doesn’t need a cursor; but since you can type anywhere on the screen, the cursor is vital so that you can know where you”are”.
The cursor can be moved up, down, left, and right with special keys, usually with arrows on them. To correct the following line:
The quick brown dox jumped[]
you would either press backspace ten times, erasing the text as you go, or press cursor-left ten times. The cursor moves “over” the characters without erasing them. It is then resting on the “d”:
The quick brown [d]ox jumped
You can correct the error by typing “f”, which overstrikes (replaces) the “d.”
The quick brown f[o]x jumped
The cursor can then be moved to the end of the line (ten cursor-rights), and typing resumed.
This sounds harder than it really is. Cursor editing becomes second nature after only hours of use. The cursor UP/DOWN keys can reach lines of text above and below the current line. It is like rolling a typewriter’s platen up or down, but with one important difference – the “paper” is one continuous, long sheet.
Getting Specific
Two very special functions are insert and delete. Insert lets you add text in the middle of a line, by pressing INSERT to insert spaces in the text, and then typing in the word. For example;
To be or to be, that is the question.[]
The cursor is placed on the second “to,” and INSERT is pressed four times (three for “n-o-t,” and one for a space):
To be or [] to be, that is
the question.
The word “not” is then typed:
To be or not[]to be, that is
the question.
Delete is used to erase text. As distinguished from mere backspacing or spacing over a word, delete closes up the space after the deleted word:
Take out a word[]
Take [o]ut a word
(cursor is moved to “o”)
Take [u]t a word
(DELETE typed; “a” disappears, “ut a word” moves left.)
Take [a] word
(DELETE is typed four times.)
Insert and delete can also act on words, sentences, lines, or entire paragraphs in a similar way.
Disk Files
A “file” is simply a permanent record of your text. When the computer’s power is turned off, it forgets everything except what is “burned” (in ROM memory) into it permanently. Your text is obviously not “burned in,” or you couldn’t ever change it. If you have a blackout, or a fuse blows, say good-bye to your text.
Catastrophes aside, you certainly don’t want to leave your computer on all the time, or keep the computer tied up with your text forever. Fortunately, you can save your text on disk, ready for any later revisions. You can type it one time, save your text, and print it out when convenient.
Since a disk can store more than one document (unless it’s very long), you and the computer must have some way to distinguish and separate one file from another. This is usually done via a directory, a list of file names. You access a file by giving the computer the file’s name.
“Scriptor,” the word processor program at the end of this article, has many features usually found only in professional word processors, but it lacks a few features such as search and replace, justification, data base merge, etc. Also, it is written in BASIC, so it can be rather slow at times. It is, however, aided by several machine language subroutines for time-critical situations such as disk input/output and some editing features.
Typing It In
Program 1 is the Scriptor program itself. Type it carefully, since it contains many critical machine language DATA statements. Extra time spent in typing it in will reward you with a smoother, bug-free word processor. Remember to use the COMPUTE! Listing Conventions (see Typing In COMPUTE’s Programs, published every issue). Use the Atari logo key to enter inverse video.
To give you more memory for text, Scriptor deletes a substantial portion of itself after it initializes (sets up). Don’t worry – the program is busy running while the screen flashes; it just takes a while. The set-up lines from 5000-6999 are automatically erased.
It you quit the program and try to run it again, the program will automatically try to re-RUN itself anew from disk. If you’ve changed disks, you’ll need to reload it yourself. You should SAVE the program with the filename “D:SCRIPTOR” or change line 110 appropriately. Be sure to SAVE Scriptor after you’ve typed it, before you run it, or you will find a sizeable chunk of your typing erased when you exit. You can free up more memory for text by deleting the “help” function. Take out all lines from 1570 to 1700 and remove line 775. If you’d rather keep this handy aid, leave these lines alone.
If you get the message “Error in DATA statements” when you run the program, you need to check your typing on the bank of machine language DATA statements at the end of the program. Also make sure you haven’t typed a letter “O” for a zero (the zero is thinner than the “O”).
If you have an Atari 825 printer, you will need to type in the lines in Program 2. This will replace the lines used for the MX-80 with lines applicable to the 825 80 Column Printer. If you have another printer, refrain from using special characters such as underlining, and you will probably be able to get one of the sets of lines to work.
Getting Started
The Scriptor is a full-scrolling, character-oriented word processor. Usage of cursor control keys is similar to normal Atari editor functions, with these exceptions:
is used only to force a carriage return, as at the end of a paragraph, or to print a blank line. The computer will format your line when you print it out, so just type continuously. Do not press at the end of each line. Pressing prints a back-arrow at the end of the line, and erases all text to the end of that line.
Insert and Delete character (CTRL-INSERT/ CTRL-DELETE) work on whole “paragraphs.” A paragraph is a block of lines from the cursor to a “back-arrow.” If there is no back-arrow, one is assumed at the end of text. Therefore, insert and delete can be quite slow if you don’t have a back- arrow somewhere.
Insert and Delete line work on the entire document. The screen will blank during this operation. This is normal and speeds up the process, as it can be slow on long documents.
All TAB controls work normally, just a little slower. will clear all tab settings.
The break key is disabled. Use to exit the program.
The ESC key enters the “mini-DOS.” (See below.)
The console keys are “live”; see a description of their functions later.
The Atari logo key is disabled for normal typing. Within prompts, it acts as an “abort” key.
Getting Control
Since the Atari is not a dedicated word processor (that means it’s just not a “word processing machine” like a Lanier, but is, rather, a general-purpose computer), it does not have special keys to activate word processing functions. Instead, the combination is used. For example, to quit the program, you would hold down and press . The CTRL key stands for “Control” – it is like a special shift key. The keys are linked mnemonically (easy to remember) to the commands they stand for, such as <P> for Print Text. To get a list of the commands and what they stand for at any time, just press <CTRL-?> (hold down CTRL and press the question mark) for a HELP menu. See Table 1 for a quick-reference chart of the commands.
Going Around The Block
An important feature in a word processor is block move and delete. Scriptor lets you “define” a series of up to 23 lines. You can then move these lines to another place in the text with Line Dupli- cate, or delete the defined lines with (Erase: Defined lines). To define a block of lines, just place the cursor on the first line and press . A flashing arrow will appear to the left of the line. Press cursor-down, and another symbol will appear underneath. Press cursor- down until all the desired lines have an arrow to their left. Then press . If you make a mistake, just try again, or press cursor-up while defining.
To copy these lines to another place, position the cursor at the place you want the lines to ap- pear, and press . If you haven’t defined any lines, this command will be ignored. Note that you can press this key more than once to make many copies of the lines. You may want to delete the defined lines after you move them. Press . You will see the prompt “ERASE:”. Press . The lines will be deleted, just as if you used Delete line multiple times.
A Mini-DOS
The ESC key activates the “mini-DOS.” It lets you look at the directory and scratch, rename, lock, or unlock files. When you press , you will see:
[D]irectory,[L]ock,[U]nlock,[R]ename,
[S]cratch?
Press the appropriate key. For all except the directory, you will need to enter a file name. The cursor, a half box, will be at the top of the screen. The only editing key you can use here is backspace.
Remember that you can abort any time before pressing by pressing the logo key. While the directory is listed, you can press again to keep the directory on the screen while you use one of the other functions. You can also press [SELECT] (see later) to save or recall a file while looking at the directory. If you get an error message at the top of the screen, check the disk and your entry and try again.
For The Record…
To save or recall a document, press [SELECT]. The screen will display:
[S]ave or [R]ecall
Press the appropriate key, enter the file name, and the document will either be stored or re- trieved. If you Recall a document, it loads starting at the line the cursor is on. This lets you add text to a document. Press START twice to home the cursor to the start of the text. If you get an error message, check to see you have the right disk, consult the DOS Manual, and try again. Remember that your file name must start with a capital letter and be followed by up to seven capital letters or numbers. You can optionally put a three-character “extension” on the file if you separate it with a period, e.g., EDITOR.DOC, DRAFT3.CGB, DUNGEON. MAP, etc. You should not enter the “D:” prefix.
Printer A La Mode
Different printers offer special print densities and formats such as boldface, underlining, super- and subscripts, double-width, condensed, proportional spacing, etc. To underline a word or phrase, enclose it in , In other words, <CTRL-,> is underlining on, and <CTRL-.> is underlining off. Underlining works only on the 825 printer. If you have GRAFTRAX installed in your MX-80, underlining produces italics.
The following is an advanced technique. You can “define” up to ten special characters and print them at any spot in your text. To define a character, set up a format line (sec the discussion of format lines, below) with and enter your definitions such as “1 = 123:2 = 123:3 = 27”, etc. You can then output the CHR$ code of the defined characters by embedding a caret (“^”) in your text, followed by the number (e.g., ^ 4). If you don’t put a number after it, a caret will print; otherwise, the character associated with the number (0-9) will be output. You can also output ASCII characters from within a format line with the “as” format command. For example, “as27:as18” will activate proportional spacing on the 825 printer. Use “as27:as69” for emphasized mode on the MX-80.
Formatting Text
Since you are typing in the raw text, with no margins or line breaks, how does the computer print a nice formatted page? The computer assumes a left margin of five, a right margin of 75, single spacing, a page length of 66, and 50 lines to be printed per page. You can change these “default” values with a format line.
A format line is like an embedded command line. The line starts with a “format character” to prevent the line from being printed out. To get the format character, press . You should get a right-pointed “wedge.” Then type in your commands. All commands are two lowercase letters, usually followed by a number. You can put multiple commands on the same line if you separate them with colons. For example, the following line:
>lm10:rm70:sp2<-
will set the left margin to ten, the right margin to 70, and line spacing to two. Here is an explanation of each formatting command. Also see Table 2 for quick reference.
Note that n represents a number, with no space between the command and the number. No real error-checking is performed on the number.
asn
Send byte n to printer.
cm:
Comment line. You can type one screen line of comments. They will not be printed to the printer. They are just for your convenience.
cnn
Centering. If n = 1, then centering will be ON, and all following lines will be centered until reset by cnO. If n = 0, then centering is turned OFF.
fp
Forced paging. Normally, the printer will “page”, or go on to the next page, when the number of lines printed equals your lines per page (lp), which defaults to 50. Forced paging pages to the next page, regardless.
lmn
n = left margin, which should be less than the right margin.
lnn
Print n blank lines.
lpn
Sets lines per page to n – n should be less than the page length, to allow some blank space at the bottom of each page.
nf:filename
Will “chain” to next specified file, permitting a document to be split up into many parts. The nf ensures that they will all print as one big file. The formatting commands carry over to each file.
pln
Sets the page length, which is almost always (and defaults to) 66.
rmn
n = right margin, which should be less than the maximum width and greater than the left margin.
spn
n = 1 single spacing, n = 2 double spacing, n = 3 triple spacing, etc.
Start The Presses
To print your document, press . You should see:
PRINT: (C/F)
To start printing, just press . The printer head should be positioned at about the start of the page. The “C/F” indicates any selected option. “C” stands for Continuous Print. You would use this option with pinfeed or roll paper. It will automatically page to the start of each sheet. If you do not select continuous print, the computer will beep at the end of each page and pause. You should put in another sheet of paper and press to continue printing.
Note that pressing a key any other time during printing will abort the printout. The “F” option stands for Fast Printout. It will blank the screen during the printing, increasing printing speed better than 30%. Some people, however, find a blank screen disconcerting. To select one of the options, press either “C” or “F.” The appropriate letter will light up and flash. To reset the option (cancel it), press the key again. Press when you are ready to print the text.
Customizing Scriptor
The program is fairly well-structured, with separate sections for all functions. The control keys are executed via a branching IF/THEN “bucket brigade.” Just patch in your own command where desired. Some functions you may want to add are: block transfer (performs both block insert and block delete). Search and Replace, Insert from Disk, simple data merge, etc. Machine language programmers may want to try their hand at speeding up certain aspects of the program, such as Insert Line, Delete Line, and even Print Text.
Some useful subroutines are:
GOSUB 540 returns the number of lines the user has typed (not necessarily the maximum number of lines) in EOT. GOSUB 600 clears the top line of the screen and positions the cursor at the first character, ready for a message. GOSUB 460 performs error checking and adjustments on the X-Y position of the cursor. GOSUB 2650 returns an adjusted (uppercase if AL = 1, no cursor controls, etc.) character in A. GOSUB 2730 is a pseudo-INPUT routine that returns IN$. Variable MX controls the maxi- mum number of characters.
TRAP 2170 will vector errors to an “I/O Error” message. There are two re-entry points for the editor proper: GOTO 650 which clears and “refreshes” the screen, and GOTO 680 which just adjusts the cursor and continues keyboard entry (faster).
Primary variables are: CL – the pointer to the top line (from 0-#lines) of the screen; X – the horizontal position of the cursor 2-39; Y – the vertical position of the cursor on the screen, 1-23; TXS – the string that contains all the text and is organized in 38 character substrings, one for each line; T$ and T – “temporary variables”; A – usually a keystroke typed; SCR – the address of the screen memory origin; NL – number of defined lines; FRL – the starting line in text of the defined lines; RL – the starting line in TX$ for reserved lines (the buffer). Several constants are QO, Ql, Q23- which return 0, 1, or 23 (saves memory); L2 = 38; L = 40; B$ is 38 null (CHR$(0)) characters.
Quick Reference
Table 1: Editing Commands
Control Keys
A
Advance one screen forward
B
Back up one screen
D
Define Lines
F
Print Format Character
G
Go to specified line
K
Clear all tab settings
L
Duplicate defined lines
P
Print Document
Q
Quit Program
SHIFT-INSERT
Insert a line
SHIFT-DELETE
Delete a line
CTRL-INSERT
Insert a space
CTRL-DELETE
Delete a character
CLEAR
Erase:
A = All
R = Remainder
D = Defined Lines
CAPS/LOWR
Upper or lowercase
ESC
MiniDOS
Cursor keys
Moves cursor with two-way scrolling
[OPTION]
Non-destructive carriage-return
[SELECT]
Save or Recall text
[START]
“Home” cursor
[CTRL-,]
Underlining ON
[CTRL-.]
Underlining OFF
^x
Print special character
Table 2: Formatting Commands
Command
Description
Default
asn
Send ASCII character n to printer.
–
cm:xxxx
Comment line
–
cnn
Centering 1 = on, 0 = off
0 Off
fp
Forced Paging
–
lmn
Set left margin to n
5
lnn
Do n linefeeds
–
lpn
Set lines per page to n
50
nf:file
Link to Next File
–
pln
Page length
66
rmn
Set right margin to n
75
spn
Set line spacing
1 (single)
Program 1
100 REM SCRIPTOR WORD PROCESSOR
110 GOTO 5000
455 RUN "D:SCRIPTOR"
460 PF=Q0:IF X<2 THEN X=39:Y=Y-Q1
470 IF X>39 THEN X=2:Y=Y+Q1
480 IF Y<Q1 THEN Y=Q1:CL=CL-Q1:PF=Q1
490 IF Y>Q23 THEN Y=Q23:CL=CL+Q1:PF=Q1
500 IF CL<Q0 THEN CL=Q0
510 IF CL>(MXL-Q23) THEN CL=MXL-Q23
520 IF PF=Q0 THEN RETURN
530 LOC=CL*L2+Q1:T=USR(SCRZAP,ADR(TX$(LOC))):RETURN
540 REM *** FIND END OF TEXT
550 P=ADR(TX$):T=P+RL*L2-Q1
560 A=USR(EDCOM,T,P,2):A=A-P
570 LC=A:EOT=INT(A/L2)
580 RETURN
590 REM *** ERASE TOP LINE
600 COLOR 32:PLOT Q1,Q0:DRAWTO L2,Q0:PLOT Q1,Q0:RETURN
610 REM *** START OF EDITOR
611 MXL=INT(FRE(Q0)/40)-25:RL=MXL+1
612 DIM TX$((MXL+Q23)*L2):? CHR$(125);
613 TX$=CHR$(Q0):TX$((MXL+Q23)*L2)=TX$:TX$(2)=TX$
620 SCR=PEEK(88)+256*PEEK(89):POKE 559,46:POKE 842,12
630 X=2:Y=Q1:CL=Q0:POKE 702,Q0
640 REM *** ENTRY FOR EACH PAGE
650 POKE 54286,192
655 POSITION Q0,Q0:? "{7 SPACES}Scriptor Word Processor";:COLOR 32:DRAWTO L2,Q0:PLOT 32,Q0
660 LOC=CL*L2+Q1:T=USR(SCRZP,ADR(TX$(LOC)))
670 IF TF THEN TF=Q0:GOTO 810
675 IF FIRST=Q0 THEN POSITION 31,Q0:? MXL;" Free";:TF=Q1:FIRST=Q1
680 POKE 53248,X*4+44
690 IF Y=OY THEN 740
710 ADJOY=OY*4+16:ADJY=Y*4+16
720 A=USR(CURSOR,PMB+ADJOY,Q0):A=USR(CURSOR,PMB,ADJY,15):OY=Y
740 K=PEEK(53279):IF K<7 THEN 2570
770 T=PEEK(764):IF T=255 OR T=39 OR T=154 THEN 740
775 IF T=166 THEN POKE 764,255:GOTO 1570
790 POKE 694,Q0:A=USR(GCHAR)
800 IF TF THEN 650
810 IF A<32 OR A>122 OR A=96 THEN 880
820 A=A-32*(A<96)
830 POKE SCR+X+L*Y,A
840 LOC=(CL+Y-Q1)*L2+X-Q1
850 TX$(LOC,LOC)=CHR$(A)
860 X=X+Q1-BF:GOSUB 460
870 BF=Q0:GOTO 680
880 IF A<>155 THEN 910
890 GOSUB 2640:POKE SCR+X+L*Y,94:TX$(LOC,LOC+L2-X+Q1)=B$:X=2:Y=Y+1
900 TX$(LOC,LOC)=CHR$(94):GOSUB 460:GOTO 650
910 IF A=6 THEN A=127:GOTO 830
920 IF A=28 THEN Y=Y-Q1:GOSUB 460:GOTO 680
930 IF A=29 THEN Y=Y+Q1:GOSUB 460:GOTO 680
940 IF A=30 THEN X=X-Q1:GOSUB 460:GOTO 680
950 IF A=96 THEN A=74:GOTO 830
960 IF A=31 THEN X=X+Q1:GOSUB 460:GOTO 680
970 IF A=Q0 THEN A=72:GOTO 830
980 IF A=126 THEN X=X-Q1:GOSUB 460:A=Q0:BF=Q1:GOTO 830
1040 IF A<>255 THEN 1070
1050 A=USR(EDCOM,ADR(TX$((CL+Y-Q1)*L2+X-Q1)),ADR(TX$(MXL*L2+37)),Q0)
1060 GOTO 650
1070 IF A<>254 THEN 1100
1080 A=USR(EDCOM,ADR(TX$((CL+Y-Q1)*L2+X-Q1)),ADR(TX$(MXL*L2+37)),Q1)
1090 GOTO 650
1100 IF A<>157 THEN 1160
1110 GOSUB 590:? "Insert line";
1120 GOSUB 540:POKE 559,Q0
1130 FOR I=EOT+(EOT<MXL) TO CL+Y STEP -Q1:T$=TX$((I-Q1)*L2+Q1,I*L2):TX$(I*L2+Q1,I*L2+L2)=T$:NEXT I
1140 T=(CL+Y-Q1)*L2:TX$(T+Q1,T+L2)=B$
1150 X=2:POKE 559,46:GOTO 650
1160 IF A=159 THEN GOSUB 590:? "Tab set at ";X-Q1:TF=Q1:TB$(X-Q1,X-Q1)="*":GOTO 740
1170 IF A=158 THEN GOSUB 590:? "Tab cleared at ";X-Q1:TF=Q1:TB$(X-Q1)=CHR$(Q0):GOTO 740
1180 IF A<>127 THEN 1230
1190 IF TB$=B$ THEN GOSUB 590:? "No tabs set":TF=Q1:GOTO 740
1200 FOR I=X TO L2:IF TB$(I,I)=CHR$(Q0) THEN NEXT I:T=L2:X=2:Y=Y+Q1:GOSUB 460:GOTO 1200
1210 T=I:I=L2:NEXT I
1220 X=T+Q1:GOTO 680
1230 IF A<>156 THEN 1290
1240 GOSUB 590:? "Delete Line";
1250 GOSUB 540:POKE 559,Q0
1260 FOR I=CL+Y-Q1 TO EOT:T$=TX$((I+Q1)*L2+Q1,(I+2)*L2):TX$(I*L2+Q1,I*L2+L2)=T$:NEXT I
1270 T=EOT*L2:TX$(T+Q1,T+L2)=B$
1280 X=2:POKE 559,46:GOTO 650
1290 IF A=11 THEN GOSUB 590:TF=Q1:? "Clear all tabs":TB$=B$:GOTO 740
1320 IF A<>125 THEN 1450
1330 GOSUB 590:? "Erase: ";
1340 GOSUB 2650
1350 IF A=155 THEN 650
1355 IF A<>65 THEN 1370
1360 ? "ALL - ";:GOSUB 2540
1365 GOTO 613
1370 IF A<>82 THEN 1380
1372 ? "Remainder - ";:GOSUB 2540:GOSUB 2640
1375 TX$(LOC)=CHR$(Q0):TX$((MXL+Q23)*L2)=CHR$(Q0):TX$(LOC+Q1)=TX$(LOC):GOTO 650
1380 IF A<>68 OR NL=-Q1 THEN 650
1400 ? "Defined Lines - ";
1410 GOSUB 2540:POKE 559,Q0:GOSUB 540
1420 FOR I=FRL=-Q1 TO EOT:T$=TX$((I+NL+Q1)*L2+Q1,(I+NL+2)*L2):TX$(I*L2+Q1,I*L2+L2)=T$:NEXT I
1430 FOR I=EOT-NL TO EOT:TX$(I*L2+Q1,I*L2+L2)=B$:NEXT I:NL=-Q1
1440 POKE 559,46:GOTO 650
1450 IF A<>4 THEN 1810
1460 GOSUB 590:? "Define Lines";
1470 FL=CL:FR=Y:FRL=FL+FR:NL=Q0
1480 POKE SCR+1+L*(FR+NL),223
1490 LOC=CL*L2+(FR+NL-Q1)*L2:T=RL*L2+NL*L2:T$=TX$(LOC+Q1,LOC+L2):TX$(T+Q1,T+L2)=T$
1500 GOSUB 2650
1510 IF A=29 AND FR+NL<22 THEN NL=NL+Q1:GOTO 1480
1520 IF A=28 AND FR+NL>FR THEN POKE SCR+1+L*(FR+NL),Q0:NL=NL-Q1
1530 IF A=155 THEN 1550
1540 GOTO 1500
1550 FOR I=Q0 TO NL:POKE SCR+1+L*(FR+I),Q0:NEXT I:GOTO 650
1570 POKE 53248,Q0:PRINT CHR$(125):POSITION 13,Q0:? " HELP Screen "
1580 ? "{DOWN}{TAB}{3 SPACES}Control Keys:"
1590 ? "A=Advance Page B=Page Back"
1595 ? "D=Define Lines F=Print form at char."
1610 ? "K=Kill all tabs L=Line Duplicate"
1620 ? "P=Print text{4 SPACES}Q=Quit"
1630 ? "Atari Key=Cancel Command":?
1635 ? "^x Print special character"
1640 ? "{DOWN}[CLEAR] Erase:All Defined Lines":POKE 85,16:? "Remainder"
1650 ? "[OPTION] Non-destructive CR"
1660 ? "{DOWN}[SELECT] Filer:Recall or Save"
1670 ? "{DOWN}[START] 'Home' cursor. Press twice to go to start of text."
1680 ? "{DOWN}[ESC] Mini DOS"
1700 ? "{DOWN}Press RETURN.":A=USR(GCHAR):GOTO 650
1810 IF A<>12 THEN 1910
1820 GOSUB 590:? "Duplicate defined lines";
1830 IF NL<Q0 THEN 650
1840 FOR I=Q0 TO NL
1850 IF CL+Y+I-Q1>MXL THEN I=NL:GOTO 1900
1860 T=RL*L2+I*L2
1870 T2=CL*L2+(Y+I-Q1)*L2
1880 T$=TX$(T+Q1,T+L2)
1890 TX$(T2+Q1,T2+L2)=T$
1900 NEXT I:Y=Y+NL+Q1:GOSUB 460:GOTO 650
1910 IF A<>27 THEN 2400
1920 POSITION 2,Q0:? "Directory,Lock,Unlock,Rename,Scratch?"
1930 GOSUB 2650:J=A
1940 IF J<>76 AND J<>85 AND J<>83 AND J<>68 AND J<>82 THEN 1930
1950 IF J<>ASC("D") THEN 2020
1960 ? CHR$(125):POKE 53248,Q0
1970 TRAP 2170:OPEN #2,6,Q0,"D:*.*"
1980 INPUT #2,T$:? T$:IF LEN (T$)<17 THEN 2000
1990 GOTO 1980
2000 CLOSE #2:TRAP 40000:GOSUB 590:? "Press a key...";:OK=1:GOSUB 2650:IF A=27 THEN 1920
2010 GOTO 650
2020 GOSUB 590:J=A
2030 IF J=76 THEN ? " LOCK >";:J=35
2040 IF J=83 THEN ? "SCRATCH>";:J=33
2050 IF J=ASC("R") THEN 2130
2070 ? "Enter Filename:";
2080 MX=12:AL=Q1:GOSUB 2720
2090 T$(3)=IN$:T$(1,2)="D:":POSITION 10,Q0:? DEL$(1,15);
2100 TRAP 2170:IF J=33 THEN POSITION 24,Q0:GOSUB 2540:COLOR 32:PLOT 24,Q0:DRAWTO 38,Q0
2110 TRAP 2170:XIO J,#2,Q0,Q0,T$:TRAP 40000
2120 TRAP 40000:GOTO 650
2130 GOSUB 590:? "RENAME>Current name? ";:MX=12:GOSUB 2720:T$(3)=IN$:T$(1,2)="D:"
2140 GOSUB 590:? "RENAME>New name? ";:MX=12:GOSUB 2720:T$(LEN(T$)+Q1)=",":T$(LEN(T$)+Q1)=IN$
2150 TRAP 2170:XIO 32,#2,Q0,Q0,T$:TRAP 40000
2160 GOTO 650
2170 TRAP 2170:POKE 559,46:CLOSE #2:CLOSE #3:GOSUB 590:? CHR$(253);"I/O Error #";PEEK(195);:TF=Q1:GOTO 740
2180 GOSUB 590:? "Save or Recall";
2190 ICCOM=834+48:ICBAL=ICCOM+2:ICBLL=ICBAL+4:ICSTAT=835+48:REM IOCB#3
2200 GOSUB 2650:IF A=155 THEN 1380
2210 IC A<>ASC("S") THEN 2290
2220 GOSUB 600:? "SAVE:{3 SPACES}File name? ";:MX=12:GOSUB 2720:T$(3)=IN$:T$(1,2)="D:":GOSUB 550
2230 POSITION 5,0:? DEL$(1,12);"ING";
2232 TRAP 2238:OPEN #3,4,Q0,T$:CLOSE #3:GOSUB 600:? "REPLACE: ";IN$;" - ";:GOSUB 2540
2233 GOSUB 600:? " REPLACING ";IN$:GOTO 2240
2238 CLOSE #3:IF PEEK(195)<>170 THEN 2170
2240 TRAP 2170:OPEN #3,8,Q0,T$
2250 POKE ICCOM,11:P=ADR(TX$):POKE ICBAL+Q1,INT(P/256):POKE ICBAL,P-(INT(P/256)*256)
2260 LN=(CL+EOT+Q1)*L2:POKE ICBLL+Q1,INT(LN/256):POKE ICBLL,LN-(INT(LN/256)*256)
2270 A=USR(ADR(CIO$),48):ERR=PEEK(ICSTAT):POKE 195,ERR:IF ERR>1 THEN 2170
2280 CLOSE #3:TRAP 40000:POKE 53279,Q0:GOTO 650
2290 IF A<>ASC("R") THEN 650
2300 LK=Q0
2310 GOSUB 590:? "RECALL: Filename? ";:MX=12:GOSUB 2720:T$(3)=IN$:T$(1,2)="D:"
2315 LOC=(CL+Y-Q1)*L2+Q1:TX$(LOC)=CHR$(Q0):TX$((MXL+Q23)*L2)=CHR$(Q0):TX$(LOC+Q1)=TX$(LOC)
2320 TRAP 2170:POSITION 8,0:? DEL$(1,8);"ING";:OPEN #3,4,Q0,T$
2330 ICCOM=834+48:ICBAL=ICCOM+2:ICBLL=ICBAL+4
2340 POKE ICCOM,5:P=ADR(TX$((CL+Y-Q1)*L2+Q1)):POKE ICBAL+Q1,INT(P/256):POKE ICBAL,P-(INT(P/256)*256)
2350 LN=(MXL-(CL+Y-Q1))*L2:POKE ICBLL+Q1,INT(LN/256):POKE ICBLL,LN-(INT(LN/256)*256)
2360 A=USR(ADR(CIO$),48):ERR=PEEK(ICSTAT):POKE 195,ERR:IF ERR>1 AND ERR<>136 THEN 2170
2370 CLOSE #3:POKE 53279,Q0:TRAP 40000:IF LK=Q0 THEN 650
2380 CL=Q0:Y=Q1:X=2:T=USR(SCRZAP,ADR(TX$))
2390 GOTO 2950
2400 IF A<>17 THEN 2410
2403 GOSUB 600:? "QUIT: ";:GOSUB 2540
2405 POKE 53277,Q0:POKE 53248,Q0:POKE 53774,192:POKE 16,192:GRAPHICS Q0:POKE 702,64:END
2410 IF A=16 THEN 2840
2420 IF A=Q1 THEN CL=CL+Q23:Y=Q1:GOSUB 460:GOTO 650
2430 IF A=2 THEN CL=CL-Q23:Y=Q1:GOSUB 460:GOTO 650
2500 GOTO 640
2540 ? "Are you sure?";:GOSUB 2650:IF 1-(A=121 OR A=89) THEN POP :GOTO 650
2550 RETURN
2570 REM *** Handle console keys
2580 POKE 764,130:A=USR(GCHAR):POKE 77,Q0
2590 IF K=5 THEN 2180
2600 IF K=3 THEN X=2:Y=Y+Q1:GOSUB 460:GOTO 680
2610 IF K=6 AND Y=Q1 AND X=2 THEN CL=Q0:X=2:GOTO 650
2620 IF K=6 THEN Y=Q1:X=2:GOTO 650
2630 GOTO 740
2640 LOC=(CL+Y-Q1)*L2+X-Q1:RETURN
2650 T=Q0:REM GET A KEY
2660 IF PEEK(20)>20 THEN T=Q1-T:POKE 20,Q0:POKE 755,T*2
2665 IF OK THEN IF PEEK(53279)=5 THEN POKE 755,2:POKE 559,46:POP :POKE 764,130:A=USR(GCHAR):OK=0:GOTO 2180
2670 IF PEEK(764)=255 THEN 2660
2680 IF PEEK(764)=154 THEN 2660
2690 IF PEEK(764)=39 THEN POKE 764,255:SOUND Q0,5,12,4:POP :FOR T=1 TO 5:NEXT T:SOUND Q0,Q0,Q0,Q0:GOSUB 2710:GOTO 650
2700 TRAP 2700:A=USR(GCHAR):TRAP 40000:IF A>96 AND A<123 THEN A=A-32*AL
2710 POKE 755,2:POKE 559,46:RETURN
2720 REM *** PSEUDO-INPUT
2730 IN$=""
2740 ? CHR$(21);CHR$(30);:GOSUB 2650:? CHR$(32);CHR(30);
2750 IF A=155 THEN 2820
2760 IF A=126 AND LEN(IN$)>1 THEN IN$=IN$(1,LEN(IN$)-Q1):? CHR$(A);:GOTO 2740
2770 IF A=126 AND LEN(IN$)=Q1 THEN ? CHR$(A);:GOTO 2730
2780 IF LEN(IN$)=MX THEN 2740
2790 IF (A<32 OR A>90) AND A<96 OR A>122 THEN 2740
2800 ? CHR$(A);:IN$(LEN(IN$)+Q1)=CHR$(A)
2810 GOTO 2740
2820 AL=Q1:IF IN$="" THEN POP :GOTO 650
2830 RETURN
2840 REM *** Printer Output
2850 GOSUB 590:? "PRINT: (C/F)"
2860 CON=Q0:F=Q0:FOR I=Q0 TO 9:PC(I)=48+I:NEXT I
2870 GOSUB 2650:IF A=155 THEN 2910
2880 IF A=67 THEN CON=1-CON:POSITION 10,Q0:? CHR$(67+128*CON);:GOTO 2870
2890 IF A=70 THEN F=1-F:POSITION 12,Q0:? CHR$(70+128*F):GOTO 2870
2900 GOTO 2870
2910 TRAP 2170:OPEN #2,8,Q0,"P:"
2920 GOSUB 590:? "Printing..."
2930 LM=5:RM=75:CN=Q0:NL=Q0
2940 SP=1:PL=66:LP=50:C=LM
2950 GOSUB 540:IF F=1 THEN POKE 559,Q0
2960 FOR P=Q1 TO LC
2970 IF PEEK(764)<255 THEN GOSUB 2650:POP :GOTO 3140
2980 Z=ASC(TX$(P))
2990 IF CN=Q1 AND Z<>127 THEN 3460
3000 IF Z<62 OR (Z>96 AND Z<123) THEN 3070
3010 IF Z=94 THEN GOSUB 3210:GOSUB 3150:GOTO 3120
3020 IF Z=72 THEN UL=Q1:PUT #2,27:PUT #2,52:GOTO 3120
3030 IF Z=74 THEN UL=Q0:PUT #2,27:PUT #2,53:GOTO 3120
3040 T=ASC(TX$(P+Q1)):IF Z=62 AND T>15 AND T<26 THEN PUT #2,PC(T-16):P=P+1:GOTO 3120
3060 IF Z=127 THEN 3230
3070 IF C=LM THEN FOR I=Q1 TO LM:PUT #2,32:NEXT I
3080 C=C+1
3090 PUT #2,Z+32*(Z<64)
3100 T=Q0:IF RM-C>=10 THEN 3110
3105 FOR I=1 TO LEN(BRK$):IF Z+32<>ASC(BRK$(I,I)) THEN NEXT I:GOTO 3110
3107 TT=ASC(TX$(P+Q1)):IF TT=Q0 OR TT=94 OR Z=Q0 OR Z=13 THEN I=LEN(BRK$):NEXT I:GOSUB 3150:T=Q1
3110 IF T=Q1 AND ASC(TX$(P+Q1))=Q0 THEN P=P+Q1:IF P<LC THEN 3110
3120 NEXT P
3130 GOSUB 3150
3140 PRINT #2,CLOSE #2:POKE 559,46:TRAP 40000:GOTO 650
3150 FOR I=Q1 TO SP:PRINT #2:NEXT I
3160 C=LM:NL=NL+SP:PRINT #2:NEXT I
3160 C=LM:NL=NL+SP:IF CN<Q0 THEN CN=Q1
3170 IF NL<LP THEN RETURN
3180 IF CON=Q0 THEN FOR I=Q0 TO 255 STEP 17:SOUND Q0,255-I,Q0,15-INT(I/17):NEXT I:T=USR(GCHAR):GOTO 3200
3190 FOR I=Q1 TO PL-LP:PRINT #2:NEXT I
3200 NL=Q0:RETURN
3210 REM *** SKIP TRAILING BLANKS
3220 T=INT(P/L2):P=(T+Q1-(P/L2=T))*L2:RETURN
3230 REM Handle special formatting
3240 P=P+Q1
3250 CM$=TX$(P,P+Q1):T$=""
3260 FOR I=P+2 TO LC
3270 IF TX$(I,I)>=CHR$(16) AND TX$(I,I)<CHR$(26) THEN T$(LEN(T$)+Q1)=CHR$(ASC(TX$(I,I))+32):NEXT I
3280 V=Q0:P=I:TRAP 3290:V=VAL(T$)
3290 TRAP 2170:IF CM$="cn" THEN CN=V
3300 IF CM$="ln" THEN FOR J=Q1 TO V:GOSUB 3150:NEXT J
3310 IF CM$="sp" THEN SP=V
3320 IF CM$="pl" THEN PL=V
3330 IF CM$="lp" THEN LP=V
3340 IF CM$="lm" AND V>0 THEN LM=V:C=V
3350 IF CM$="rm" AND V>0 THEN RM=V
3360 IF CM$="fp" THEN GOSUB 3180:POKE 559,46-46*F
3370 IF CM$="as" THEN PUT #2,V
3380 IF CM$="cm" THEN FOR I=P TO P+79:IF TX$(I,I)<>"^" THEN NEXT I:I=I-Q1
3390 IF CM$="cm" THEN P=I+Q1:GOTO 3450
3400 IF CM$<>"nf" THEN 3430
3410 T$="D:":FOR I=Q0 TO 11:Z=ASC(TX$(P+I,P+I)):IF Z<>94 AND P+I<=LC THEN T$(3+I)=CHR$(Z+32*(Z<63)):NEXT I
3415 TX$(Q1)=CHR$(Q0):TX$((MXL+Q23)*L2)=CHR$(Q0):TX$(2)=TX$
3420 POKE 559,46:GOSUB 590:? "Printing ";T$:LK=Q1:CL=Q0:Y=Q1:GOTO 2320
3430 IF ASC(CM$)>15 AND ASC(CM$)<26 THEN PC(ASC(CM$)-16)=V
3440 IF TX$(P,P)<>"^" AND P<LC THEN 3240
3450 GOSUB 3220:P=P+Q1:GOTO 2970
3460 REM *** CENTRE STRING
3470 LN=Q0:FOR I=P TO P+79:IF TX$(I,I)<>"^" THEN LN=LN+Q1:NEXT I
3480 WIDTH=RM-LM:UL=Q0:IF TX$(P,P)=CHR$(72) THEN UL=Q1
3490 FOR I=Q1 TO (WIDTH-LN)/2+LM:PUT #2,32:NEXT I
3500 C=C+I:CN=-Q1:GOTO 2990
5000 REM INITIALISATION
5010 GRAPHICS 17:SETCOLOR 4,1,10
5020 DL=PEEK(560)+256*PEEK(561)+4:POKE DL+5,7:POKE DL+10,7:POKE DL+14,7
5030 POSITION 6,4:? #6;"scriptor":POSITION 3,7:? #6;"WORD PROCESSOR"
5040 ? #6:? #6;" ";CHR$(136);CHR$(227);CHR$(153);CHR$(152);CHR$(147)
5045 ? #6:? #6;" small systems svcs";CHR$(14);
5050 ? #6:? #6;"{3 SPACES}CHARLES BRANNON"
5070 Q0=0:Q1=1:Q23=23:RL=MXL+Q1:SCRZAP=1680:CURSOR=1739:EDCOM=1536:AL=1:L2=38:GCHAR=1303:SND=1331
5080 DIM T$(79),IN$(20),B$(L2),TB$(L2),CM$(2),BRK$(8),PC(9),DEL$(20),CIO$(7)
5090 B$=CHR$(Q0):B$(L2)=B$:B$(2)=B$:DEL$=CHR$(254):DEL$(20)=DEL$:DEL$(2)=DEL$
5100 TB$=B$:BRK$=" ,.!?;:-":CIO$="hhh":CIO$(4)=CHR$(170):CIO$(5)="LV":CIO$(7)=CHR$(228)
5110 OPEN #1,4,Q0,"K:"
5120 T=Q0:OY=Q0:CL=Q0:L=40:NL=-Q1
5130 PMB=PEEK(106)-8:POKE 559,46:POKE 53248,Q0
5140 POKE 54279,PMB:POKE 53277,3
5150 PMB=PMB*256+512:POKE 704,56
5160 FOR I=Q0 TO 255:POKE PMB+I,Q0:POKE 708+3*RND(Q0),PEEK(53770):NEXT I
5180 SETCOLOR 4,8,2
5250 FOR I=0 TO 70:READ A:POKE 1280+I,A:CHECKSUM=CHECKSUM+A:POKE 708+3*RND(Q0),PEEK(53770):NEXT I
5290 FOR I=0 TO 247:READ A:POKE 1536+I,A:CHECKSUM=CHECKSUM+A:POKE 708+3*RND(Q0),PEEK(53770):NEXT I
5300 IF CHECKSUM<>47765 THEN PRINT CHR$(253);"Error in DATA statements...":END
5310 DATA 72,138,72,169,10,162,2,141,10,212,141,24,208,141,26,208,142,23,208,104,170,104,64
5320 DATA 104,173,252,2,201,255,240,249,133,124,162,255,142,252,2,32,51,5,32,254,246,133,212,169,0,133,213,96
5330 DATA 162,0,142,0,210,162,15,142,1,210,160,0,234,200,208,252,202,16,244,96
5340 DATA 216,104,104,133,213,104
5350 DATA 133,212,104,133,204,104
5360 DATA 133,203,104,104,208,47
5370 DATA 32,109,6,165,205,76
5380 DATA 43,6,160,0,177,205
5390 DATA 200,145,205,198,205,165
5400 DATA 205,201,255,208,2,198
5410 DATA 206,197,212,208,235,165
5420 DATA 206,197,213,208,229,160
5430 DATA 0,177,205,200,145,205
5440 DATA 136,152,145,205,96,201
5450 DATA 1,240,3,76,221,6
5460 DATA 32,109,6,76,91,6
5470 DATA 160,1,177,212,136,145
5480 DATA 212,230,212,208,2,230
5490 DATA 213,165,213,197,206,208
5500 DATA 237,165,212,197,205,208
5510 DATA 231,169,0,168,145,212
5520 DATA 96,165,212,133,205,165
5530 DATA 213,133,206,160,0,177
5540 DATA 205,201,94,240,18,230
5550 DATA 205,208,2,230,206,165
5560 DATA 206,197,204,208,238,165
5570 DATA 205,197,203,208,232,96
5580 DATA 165,88,133,203,165,89
5590 DATA 133,204,104,104,133,206
5600 DATA 104,133,205,162,24,76
5610 DATA 188,6,160,0,177,205
5620 DATA 200,200,145,203,136,192
5630 DATA 38,208,245,24,169,38
5640 DATA 101,205,133,205,144,2
5650 DATA 230,206,24,169,40,101
5660 DATA 203,133,203,144,2,230
5670 DATA 204,202,208,218,96,104
5680 DATA 104,133,204,104,133,203
5690 DATA 104,168,104,145,203,200
5700 DATA 192,4,208,249,96,160
5710 DATA 0,177,212,208,20,198
5720 DATA 212,165,212,201,255,208
5730 DATA 2,198,213,197,203,208
5740 DATA 238,165,213,197,204,208
5750 DATA 232,96
6000 GRAPHICS 0:POKE 559,Q0:POKE 16,64:POKE 53774,64
6010 FOR I=5000 TO 5900 STEP 100:? CHR$(125):POSITION 2,3:FOR J=I+90 TO I STEP -10:? J:NEXT J:? 110:? "CONT"
6020 POKE 712,PEEK(53770):POKE 842,13:POSITION 0,0:STOP
6030 POKE 842,12:NEXT I
6040 SETCOLOR 2,12,Q0:SETCOLOR 4,8,10:SETCOLOR Q1,Q0,12:POKE 752,Q1
6050 POKE PEEK(560)+256*PEEK(561)+3,194:POKE 512,0:POKE 513,5
6060 ? CHR$(125):? :? :FOR I=6000 TO 6060 STEP 10:? I:NEXT I:? "GOTO610":POSITION 0,0:POKE 842,13:STOP