Chapter 3
SpeedScript Source Code
Atari Source Code
The source code for SpeedScript was originally developed using the MAC/65 assembler (from Optimized Systems Software, Inc.). The MAC/65 assembler uses the standard MOS source code format, so this source code can be assembled on a variety of Atari assemblers, including EASMD from OSS and the Atari Assembler/Editor cartridge. The source code was originally broken up into a number of modules, each SAVE#'d to disk. The .INCLUDE pseudo-op was used to link all the modules together. All files must be merged together to be assembled with the Atari Assembler/Editor cartridge. Line numbers are omitted.
Most pseudo-ops are in standard MOS 6502 notation: *= updates the program counter (some assemblers use .ORG instead); .BYTE assembles a list of numbers or an ATASCII character string; .WOR, or .WORD, assembles a list of addresses into low byte/high byte format; < extracts the low byte of a 16-bit expression; > extracts the high byte of a 16-bit expression (some assemblers reverse the use of < and > others, such as EASMD and the Assembler/Editor cartridge, use a suffix of &255 and /256 to achieve the same effect); and = is used to assign an expression to a label (some assemblers use .EQU).
Beginners should make sure they understand Indirect-Y addressing, as in LDA ($FB),Y or LDA (CURR),Y. This mode is used extensively in SpeedScript.
The Atari version of SpeedScript was developed by sending the Commodore 64 source code to the Atari via modem. References to Commodore 64 Kernal ROM routines were replaced with Atari CIO routines. Some routines built into the Commodore 64's ROM had to be programmed into Atari SpeedScript, with resulting code expansion. References to location 1 (which maps banks of ROM in and out in the 64) were omitted. The REFRESH routine, TOPCLR, and a few other routines were changed to compensate for Atari's floating screen memory. The raster interrupt used to highlight the command line in the 64 version became a display-list interrupt. A custom character set was added to take advantage of the Atari's special nine-line character mode. The DOS package was written to support disk functions. But much of the source code did not need to be changed at all, since SpeedScript's machine-specific code is segregated into distinct modules. These modules were rewritten. Approximately one week was required to get a primitive version running, followed by two months of testing, debugging, and refining to complete Atari SpeedScript. Because of the new character set, the DOS package, smoother input/output programming (such as Atari's device-independent I/O), and more logical keyboard layout, the Atari version may be the best version of SpeedScript yet.
SpeedScript is written in small modules. Some people think that subroutines are useful only when a routine is called more than once. I strongly believe in breaking up a problem into a number of discrete tasks. These tasks can be written as subroutines, then tested individually. Once all the modules are working, just link them together with JSRs and you have a working program.
I've also tried to use meaningful labels, but sometimes one just runs out of imagination. Comments are added below as signposts to guide you through the source code (you needn't type them in--if you do, precede each comment with a semicolon for the sake of your assembler). Modules are also set apart with blank lines. Notice that some modules are used in rather creative ways. For example, word left/word right is used both for moving the cursor and in delimiting a word to be erased in the erase mode. Also, note that memory locations are sometimes used instead of meaningful labels. In order to fit the complete source code into memory at once, I sometimes had to compromise readability for the sake of brevity.
Crucial to the understanding of SpeedScript is the REFRESH routine. Study it carefully. REFRESH is the only routine in SpeedScript that writes directly to the screen (CIO is used to print on the command line). It automatically takes care of word-wrap and carriage returns, and provides useful pointers so that the CHECK routine can easily scroll the screen. This frees the rest of SpeedScript to just move and modify contiguous memory. Carriage returns are not padded out in memory with spaces to fill the rest of a line; the REFRESH routine takes care of this transparently.
Speedscript 3.0 Source Code for Atari
Filename: SPEED.0
Location $1F00 is safely above DOS 2.0S, DOS 3,
and OS/A+ DOS. Some DOS's may use more memory, so
you may need to reassemble SpeedScript at a
higher address, usually the address of LOMEM plus
256 bytes to be safe.
*= $1F00
Locations used by high-speed memory move
routines.
FROML = $80
FROMH = $81
DESTL = $82
DESTH = $83
LLEN = $84
HLEN = $85
CURR: Position of cursor within text memory. SCR:
used by the REFRESH routine.
CURR = $86
SCR = $88
TEX: An alternate location used in tandem with
CURR. COLR is used by REFRESH. TEMP is used
throughout as a scratehpad pointer. INDIR is also
a reusable indirect pointer. UNDERCURS stores the
value of the character highlighted by the cursor.
TEX = $8A
TEMP = $8C
INDIR = $8E
UNDERCURS = $90
WTNDCOLOR: Color of command line window supported
by HIGHLIGHT. RETCHAR is the screen-code value of
the return-mark (a left-pointing arrow). SPACE is
the screen-code value of the space character, RED
and BLUE are used as command-line colors
WINDCOLR = $91
RETCHAR = 94
SPACE = 0
RED = $32
BLUE = $74
Input/Output Control System definitions for
input/output control blocks (IOCBs). CIO is the
entry point for all file-oriented input/output.
SHFLOK is the SHiFtLOcK flag.
ICCOM = $0342
ICBADR = $0344
ICBLEN = $0348
ICAUX1 = $034A
ICAUX2 = $034B
ICSTAT = $0343
SHFLOK = $02BE
CIO = $E456
Called only when run from DOS. It is assumed that
the author's initials (that conveniently work out
in hex) are not normally present in memory. If
they are, we know that SpeedScript has been run
before, so we avoid the ERASE routine to preserve
the text in memory.
BEGIN LDA 710
STA 709
JSR INIT
LDA #$CB
CMP FIRSTRUN
STA FIRSTRUN
BEQ SKIPERAS
JSR ERASE
JSR KILLBUFF
We save the DOS reset vector and change this
vector to point to SpeedScript's SYSTEM RESET
routine. Since this routine is called at
power-up, right after DOS.SYS runs, we need to
disable the cold-start flag (location 580) and
set location $09 to signify a successful disk
boot.
LDA $0C
STA JDOS+1
LDA $0D
STA JDOS+2
LDA # <JDOS
STA $0C
LDA # >JDOS
STA $0D
LDA #0
STA 580
LDA #1
STA $09
SKIPERAS JSR INIT2
JMP MAIN
The character set for the ANTIC 3 nine-line
character mode must be on an even 512-Syte
boundary, so we force the assembler's program
counter to address $2000 and merge in the
character set. We then link in each successive
module of SpeedScript. Again, if your assembler
cannot handle .INCLUDE, you'll have to merge all
these files together in the order indicated.
*= $2000
.INCLUDE #D:CHSET.SRC
.INCLUDE #D:SPEED.1
.INCLUDE #D:SUPPORT
.INCLUDE #D:DOSPAK
.INCLUDE #D:SPEED.2
.INCLUDE #D:DATA
.END
Filename: CHSET.SRC
The character set here is stored as eight bytes per line, so each line defines one character. Sheldon Leemon's INSTEDIT character editor was used to create the character set, and I wrote a special program to convert the character set into .BYTE statements. In ANTIC mode 3, each character takes up ten scan lines of vertical screen space. The characters in the lowercase portion of the character set are displayed with a blank line at the top line, then the character data from bytes 1-7 of the character set. Byte 0 of the character's definition is displayed at the ninth line of the character. The tenth line is always blank. This lets you define characters with true descenders. The forced blank line lets you use more of the character matrix for defining a character, so these characters are larger than normal Atari characters. .BYTE 0,0,0,0,0,0,0,0 .BYTE 0,24,24,24,24,24,0,24 .BYTE 0,102,102,102,0,0,0,0 .BYTE 0,102,255,102,102,255,102,0 .BYTE 24,62,96,60,6,124,24,0 .BYTE 0,204,216,48,96,204,140,0 .BYTE 0,56,108,56,112,222,204,118 .BYTE 0,24,24,48,0,0,0,0 .BYTE 0,24,48,96,96,96,48,24 .BYTE 0,48,24,12,12,12,24,48 .BYTE 0,0,102,60,255,60,102,0 .BYTE 0,0,24,24,126,24,24,0 .BYTE 0,0,0,0,0,48,48,96 .BYTE 0,0,0,0,126,0,0,0 .BYTE 0,0,0,0,0,0,48,48 .BYTE 0,0,6,12,24,48,96,192 .BYTE 0,124,206,222,246,230,198,124 .BYTE 0,24,56,24,24,24,24,126 .BYTE 0,124,198,12,24,48,96,254 .BYTE 0,254,12,24,56,12,198,124 .BYTE 0,28,60,108,204,254,12,12 .BYTE 0,254,192,252,6,6,198,124 .BYTE 0,124,192,252,198,198,198,124 .BYTE 0,126,6,12,24,48,96,96 .BYTE 0,124,198,198,124,198,198,124 .BYTE 0,124,198,198,126,12,24,48 .BYTE 0,0,48,48,0,48,48,0 .BYTE 0,0,48,48,0,48,48,96 .BYTE 0,12,24,48,96,48,24,12 .BYTE 0,0,0,126,0,0,126,0 .BYTE 0,48,24,12,6,12,24,48 .BYTE 0,60,102,6,12,24,0,24 .BYTE 0,124,198,222,214,220,224,60 .BYTE 0,124,198,198,198,254,198,198 .BYTE 0,252,198,198,252,198,198,252 .BYTE 0,124,198,192,192,192,198,124 .BYTE 0,248,204,198,198,198,204,248 .BYTE 0,254,192,192,252,192,192,254 .BYTE 0,254,192,192,252,192,192,192 .BYTE 0,124,198,192,222,198,198,124 .BYTE 0,198,198,198,254,198,198,198 .BYTE 0,126,24,24,24,24,24,126 .BYTE 0,62,12,12,12,12,204,120 .BYTE 0,198,204,216,240,216,204,198 .BYTE 0,192,192,192,192,192,192,254 .BYTE 0,198,238,254,214,198,198,198 .BYTE 0,198,230,246,254,222,206,198 .BYTE 0,124,198,198,198,198,198,124 .BYTE 0,252,198,198,198,252,192,192 .BYTE 0,124,198,198,198,222,124,14 .BYTE 0,252,198,198,252,216,204,198 .BYTE 0,124,198,192,124,6,198,124 .BYTE 0,126,24,24,24,24,24,24 .BYTE 0,198,198,198,198,198,198,124 .BYTE 0,198,198,198,198,198,108,56 .BYTE 0,198,198,198,214,254,238,198 .BYTE 0,198,198,108,56,108,198,198 .BYTE 0,102,102,102,60,24,24,24 .BYTE 0,254,12,24,48,96,192,254 .BYTE 0,30,24,24,24,24,24,30 .BYTE 0,64,96,48,24,12,6,0 .BYTE 0,240,48,48,48,48,48,240 .BYTE 0,8,28,54,99,0,0,0 .BYTE 0,0,0,0,0,0,0,255 .BYTE 0,0,0,0,0,0,0,0 .BYTE 124,194,153,153,129,153,153,230 .BYTE 252,130,153,130,153,153,131,252 .BYTE 124,194,153,158,158,153,194,124 .BYTE 252,130,153,153,153,153,130,252 .BYTE 254,130,158,132,156,158,130,254 .BYTE 126,193,206,194,206,204,204,120 .BYTE 124,194,153,158,145,153,194,124 .BYTE 246,153,153,129,153,153,153,246 .BYTE 127,97,115,50,50,115,97,127 .BYTE 62,50,50,50,50,114,198,124 .BYTE 230,153,146,132,146,153,153,230 .BYTE 120,76,76,76,76,78,66,124 .BYTE 230,153,129,129,137,153,153,230 .BYTE 230,153,137,129,145,153,153,230 .BYTE 124,194,153,153,153,153,194,124 .BYTE 254,195,201,201,195,206,200,240 .BYTE 124,194,153,153,153,146,201,118 .BYTE 124,194,201,201,194,201,201,247 .BYTE 126,195,158,194,249,153,195,126 .BYTE 254,194,102,100,100,100,100,124 .BYTE 246,153,153,153,153,153,194,124 .BYTE 230,153,153,153,153,194,100,56 .BYTE 246,153,153,153,137,129,153,246 .BYTE 230,153,153,194,153,153,153,230 .BYTE 230,153,153,195,230,100,100,124 .BYTE 254,193,249,50,228,206,193,254 .BYTE 120,96,120,96,126,24,30,0 .BYTE 0,24,60,126,24,24,24,0 .BYTE 0,24,24,24,126,60,24,0 .BYTE 0,0,0,12,12,88,112,120 .BYTE 0,24,12,126,12,24,0,0 .BYTE 0,0,24,60,126,126,60,24 .BYTE 0,0,0,124,6,126,198,126 .BYTE 0,0,192,252,198,198,198,252 .BYTE 0,0,0,124,198,192,198,124 .BYTE 0,0,6,126,198,198,198,126 .BYTE 0,0,0,124,198,254,192,124 .BYTE 0,0,62,96,252,96,96,96 .BYTE 6,252,0,126,198,198,198,126 .BYTE 0,0,192,192,252,198,198,198 .BYTE 0,0,24,0,56,24,24,60 .BYTE 24,240,24,0,24,24,24,24 .BYTE 0,0,192,204,216,248,204,198 .BYTE 0,0,56,24,24,24,24,60 .BYTE 0,0,0,204,254,254,214,198 .BYTE 0,0,0,252,198,198,198,198 .BYTE 0,0,0,124,198,198,198,124 .BYTE 192,192,0,252,198,198,198,252 .BYTE 6,6,0,126,198,198,198,126 .BYTE 0,0,0,252,198,192,192,192 .BYTE 0,0,0,126,192,124,6,252 .BYTE 0,0,48,254,48,48,48,30 .BYTE 0,0,0,198,198,198,198,126 .BYTE 0,0,0,198,198,198,108,56 .BYTE 0,0,0,198,214,254,124,108 .BYTE 0,0,0,198,108,56,108,198 .BYTE 6,252,0,198,198,198,198,126 .BYTE 0,0,0,254,12,56,96,254 .BYTE 14,0,14,24,24,56,24,24 .BYTE 24,24,24,24,24,24,24,24 .BYTE 112,0,112,24,24,28,24,24 .BYTE 0,0,0,8,24,56,24,8 .BYTE 0,0,0,16,16,24,28,24 *= *+16 .ENDFilename: SPEED.1
This module is chiefly concerned with
the word processor editing functions.
It contains many common subroutines,
such as TOPCLR and PRMSG to clear the
command line and print messages. It
contains the initialization routines
and takes care of memory moves (inserts
and deletes). A second module, SPEED.2,
is responsible for most input/output,
including the printer routines. SPEED.1
is the largest file in the linked
chain. UMOVE is a high-speed memory
move routine. It gets its speed from
selfmodifying code (the $FFFFs at
MOVLOOP are replaced by actual
addresses when UMOVE is called). UMOVE
is used to move an overlapping range of
memory upward (toward location 0), so
it is used to delete. Set FROML/FROMH
to point to the source area of memory,
DESTL/DESTH to point to the
destination, and LLEN/HLEN to hold the
length of the area being moved.
UMOVE LDA FROML
STA MOVLOOP+1
LDA FROMH
STA MOVLOOP+2
LDA DESTL
STA MOVLOOP+4
LDA DESTH
STA MOVLOOP+5
LDX HLEN
BEQ SKIPMOV
MOV1 LDA #0
MOV2 STA ENDPOS
LDY #0
MOVLOOP LDA $FFFF,Y
STA $FFFF,Y
INY
CPY ENDPOS
BNE MOVLOOP
INC MOVLOOP+2
INC MOVLOOP+5
CPX #0
BEQ OUT
DEX
BNE MOV1
SKIPMOV LDA LLEN
BNE MOV2
OUT RTS
DMOVE uses the same variables as UMOVE,
but it is used to move an overlapping
block of memory downward (toward
location $FFFF), so it is used to
insert. If the block of memory to be
moved does not overlap the destination
area, then either routine can be used.
DMOVE LDA HLEN
TAX
ORA LLEN
BNE NOTNULL
RTS
NOTNULL CLC
TXA
ADC FROMH
STA DMOVLOOP+2
LDA FROML
STA DMOVLOOP+1
CLC
TXA
ADC DESTH
STA DMOVLOOP+5
LDA DESTL
STA DMOVLOOP+4
INX
LDY LLEN
BNE DMOVLOOP
BEQ SKIPDMOV
DMOV1 LDY #255
DMOVLOOP LDA $FFFF,Y
STA $FFFF,Y
DEY
CPY #255
BNE DMOVLOOP
SKIPDMOV DEC DMOVLOOP+2
DEC DMOVLOOP+5
DEX
BNE DMOV1
RTS
REFRESH copies a screenful of text from
the area of memory pointed to by
TOPLIN. It works like a printer
routine, fitting a line of text between
the screen margins, wrapping words, and
restarts at the left margin after
printing a carriage return. SpeedScript
constantly calls this routine while the
cursor is blinking, so it has to be
very fast. To eliminate flicker, it
clears out the end of each line instead
of first clearing the screen. It stores
the length of the first screen line for
the sake of the CHECK routine (which
scrolls up by adding that length to
TOPLIN) and the last text location
referenced (so CHECK can see if the
cursor has moved off the visible
screen). REFRESH can automatically
handle different screen widths.
REFRESH LDA #40
INY
CLC
RLM: Left margin. Location $58/$59
points to the address of screen memory.
ADC RLM
CLC
ADC $58
STA SCR
LDA $59
ADC #0
STA SCR+1
TOPLIN points to the first character
within text to be printed at the
top-left comer of the screen.
CLC
LDA TOPLIN
STA TEX
LDA TOPLIN+1
STA TEX+1
LDX #1
LDA INSMODE
STA WINDCOLR
PPAGE LDY #0
PLINE LDA (TEX),Y
STA LBUFF,Y
INY
AND #127
CMP #RETCHAR
BEQ BREAK
CPY LINELEN
BNE PLINE
DEY
SLOOP LDA (TEX),Y
AND #127
NXCUR CMP #SPACE
BEQ SBRK
DEY
BNE SLOOP
LDY LINELEN
DEY
SBRK INY
BREAK STY TEMP
LDY #0
COPY LDA LBUFF,Y
STA (SCR),Y
INY
CPY TEMP
BNE COPY
CLC
TYA
ADC TEX
STA TEX
LDA TEX+1
ADC #0
STA TEX+1
CPX #1
BNE CLRLN
STY LENTABLE
CLRLN CPY LINELEN
BEQ CLEARED
Character #64 (ATASCII value of O)
fills the gap when a line is broken. It
can be redefined to show or not show
these false spaces.
LDA #64
STA (SCR),Y
INY
JMP CLRLN
CLEARED CLC
LDA SCR
ADC #40
STA SCR
BCC INCNOT
INC SCR+1
INCNOT INX
CPX #19
BEQ PDONE
JMP PPAGE
PDONE LDA TEX
STA BOTSCR
LDA TEX+1
STA BOTSCR+1
RTS
The following routine fills the entire
text area with space characters (screen
code 0), effectively erasing all text.
It is called when the program is first
run and when an Erase All is performed.
It also initializes the cursor position
(CURR) and the end-of-text pointer
(LASTLINE).
ERASE LDA TEXSTART
STA TEX
STA TOPLIN
STA LASTLINE
STA CURR
LDA TEXSTART+1
STA TEX+1
STA TOPLIN+1
STA LASTLINE+1
STA CURR+1
SEC
LDA TEXEND+1
SBC TEXSTART+1
TAX
LDA #SPACE
CLRLOOP LDY #255
DEC TEX+1
STA (TEX),Y
INY
INC TEX+1
CLR2 STA (TEX),Y
INY
BNE CLR2
INC TEX+1
DEX
BNE CLR2
STA (TEX),Y
RTS
PRMSG is used anytime we need to print
something at the top of the screen (the
command line). Pass it the address of
the message to be printed by storing
the low byte of the address in the
accumulator and the high byte in the Y
register. The message in memory must
end with a zero byte. The routine does
not add a carriage return. CHROUT
(character out) prints the character in
the accumulator to the screen. CHROUT
is a subroutine in the SUPPORT package.
PRMSG STA TEMP
STY TEMP+1
LDA #1
STA 752
LDY #0
PRLOOP LDA (TEMP),Y
BEQ PREXIT
JSR CHROUT
INY
BNE PRLOOP
PREXIT RTS
GETAKEY JSR GETIN
BEQ GETAKEY
RTS
JDOS JSR PREXIT
LDA BLINK
BEQ NOBLINK
LDY #0
LDA UNDERCURS
STA (CURR),Y
NOBLINK JSR INIT2
JMP MAIN
The initialization routine sets up the
memory map, clears out certain flags,
and enables the display-list interrupt.
INIT LDA #125
JSR CHROUT
LDA #0
STA INSMODE
STA TEXSTART
STA TEXEND
STA TEXBUF
STA BUFEND
STA HUNTLEN
STA REPLEN
STA ESCFLAG
STA SHFLOK
STA RLM
LDA #40
STA LINELEN
Label END is at the end of the source
code, so it points to the last address
used by the object code. We use it to
calculate the start-of-text memory.
LDA # >END
CLC
ADC #1
STA TEXSTART+1
Location 561 points to the display
list, which holds screen information at
the top of memory. We use it as the
last address available for storing text
or buffer text.
LDA 561
SEC
SBC #1
STA BUFEND+1
SEC
SBC #8
STA TEXBUF+1
SEC
SBC #1
STA TEXEND+1
LDA #$FF
STA FPOS+1
If location $4B is 0, then SpeedScript
is booted from disk. if we booted from
cassette, we free up the DOS area
($0700-$1E00) for use as the text
buffer, and free up the text memory
used by disk-based SpeedScript as the
text buffer.
LDA $4B
BEQ DISKBOOT
LDA BUFEND+1
STA TEXEND+1
LDA #$07
STA TEXBUF+1
LDA #$1E
STA BUFEND+1
DISKBOOT RTS
The second initialization routine turns
on the display-list interrupt (HIGH-
LIGHT), homes the cursor, and prints
the credit line.
INIT2 JSR HIGHLIGHT
LDA TEXSTART
STA CURR
LDA TEXSTART+1
STA CURR+1
JSR REFRESH
JSR SYSMSG
LDA # <MSG2
LDY # >MSG2
JSR PRMSG
INC MSGFLG
JMP CHECK
SYSMSG displays "SpeedScript 3.0." The
message flag (MSGFLG) is set when a
message is to be left on the screen
only until the next keystroke. After
that keystroke, SYSMSG is called. The
INIT2 routine prints the credit line
with the MSGFLG set so that you won't
have to stare at the author's name
while you're writing--a modesty
feature.
SYSMSG JSR TOPCLR
LDA # <MSG1
LDY # >MSG1
JSR PRMSG
LDA #0
STA MSGFLG
RTS
TOPCLR keeps the command line clean. It
is called before most messages. It's
like a one-line clear-screen. It also
forces the left margin (82) to 0, and
homes the cursor to the beginning of
the command line by zeroing out the X
and Y cursor positions (84 and 85).
TOPCLR LDY #39
LDA #SPACE
TOPLOOP STA ($58),Y
DEY
BPL TOPLOOP
LDA #0
STA 82
STA 85
STA 84
RTS
Convert ATASCII to screen codes
ASTOIN PHA
AND #128
STA TEMP
PLA
AND #127
CMP #96
BCS LOWR
CMP #32
BCS NOTCTRL
CLC
ADC #64
JMP LOWR
NOTCTRL SEC
SBC #32
LOWR ORA TEMP
RTS
The MAIN loop blinks the cursor, checks
for keystrokes, converts them from
ATASCII to screen codes, puts them in
text at the CURRent position, and
increments the CURRent position and
LASTLINE. It also checks for special
cases like the RETURN key and passes
control characters to the CONTROL
routine. The INSMODE flag is checked to
see if we should insert a space before
a character.
MAIN LDY #0
STY BLINK
LDA (CURR),Y
STA UNDERCURS
MAIN2 LDY #0
STY SELFLAG
LDA (CURR),Y
EOR #$80
STA (CURR),Y
LDA BLINK
EOR #1
STA BLINK
JSR REFRESH
WAIT JSR GETIN
BNE KEYPRESS
We check for the START key, and if
pressed, go to the HOME cursor routine.
LDA #8
STA 53279
LDA 53279
CMP #6
BNE FLIPIT
LDY #0
STY BLINK
LDA UNDERCURS
STA (CURR),Y
JSR HOME
JMP MAIN
The realtime clock (location 20), which
counts in 1/60 seconds, is checked for
16/60 seconds (about 1/5 second) to see
if it's time to blink the cursor.
FLIPIT LDA 20
AND #16
BEQ WAIT
LDA #0
STA 20
JMP MAIN2
A key has been pressed. We check the
SELECT key to see if the keystroke
should be inverted.
KEYPRESS TAX
LDA #8
STA 53279
LDA 53279
CMP #5
BNE NOTSEL
LDA #128
STA SELFLAG
NOTSEL LDY #0
LDA UNDERCURS
STA (CURR),Y
NOTBKS LDA MSGFLG
BEQ NOMSG
TXA
PHA
JSR SYSMSG
PLA
TAX
NOMSG TXA
CMP #155
BNE NOTCR
Change a carriage return into a back
arrow.
LDX #30
JMP OVERCTRL
NOTCR TXA
BIT ESCFLAG
BMI OVERCTRL
CMP #156
BCS CONTROL
AND #127
CMP #32
BCC CONTROL
CMP #123
BCS CONTROL
CMP #92
BEQ CONTROL
CMP #94
BEQ CONTROL
CMP #95
BEQ CONTROL
OVERCTRL TXA
PHA
LDY #0
STY ESCFLAG
LDA (CURR),Y
CMP #RETCHAR
BEQ DOINS
LDA INSMODE
BEQ NOINST
DOINS JSR INSCHAR
NOINST PLA
JSR ASTOIN
AND #127
ORA SELFLAG
LDY #0
Put the character into memory.
STA (CURR),Y
JSR REFRESH
SEC
LDA CURR
SBC LASTLINE
STA TEMP
LDA CURR+1
SBC LASTLINE+1
ORA TEMP
BCC INKURR
LDA CURR
ADC #0
STA LASTLINE
LDA CURR+1
ADC #0
STA LASTLINE+1
Move the cursor forward.
INKURR INC CURR
BNE NOINC2
INC CURR+1
NOINC2 JSR CHECK
JMP MAIN
CONTROL looks up a keyboard command in
the list of control codes at CTBL. The
first byte of CTBL is the actual number
of commands. Once the position is
found, this position is doubled as an
index to the two-byte address table at
VECT. The address of MAIN-1 is put on
the stack, simulating the return
address; then the address of the
command routine taken from VECT is
pushed. We then perform an RTS. RTS
pulls the bytes off the stack as if
they were put there by a JSR. This
powerful technique is used to simulate
ON-GOTO in machine language.
CONTROL LDX CTBL
SRCH CMP CTBL,X
BEQ FOUND
DEX
BNE SRCH
JMP MAIN
FOUND DEX
TXA
ASL A
TAX
LDA # >MAIN-1
PHA
LDA # <MAIN-1
PHA
LDA VECT+1,X
PHA
LDA VECT,X
PHA
RTS
CTBL .BYTE 35
.BYTE 31,30,92,94,2,20,28,29
.BYTE 126,255,4
.BYTE 9,125,124,95,5,12,19
.BYTE 13,18,24,26,16
.BYTE 254,1,11,6,21,127,157
.BYTE 3,7,156,27,15
VECT .WORD RIGHT-1,LEFT-1,WLEFT-1,WRIGHT-1,BORDER-1,LETTERS-1
.WORD SLEFT-1,SRIGHT-1,DELCHAR-1,INSCHAR-1,DELETE-1
.WORD INSTGL-1,CLEAR-1,PARIGHT-1,PARLEFT-1
.WORD ERAS-1,TLOAD-1,TSAVE-1
.WORD DOS-1,INSBUFFER-1,SWITCH-1
.WORD ENDTEX-1,PRINT-1
.WORD DELIN-1,ALPHA-1,KILLBUFF-1,HUNT-1,FREEMEM-1,TAB-1
.WORD LOTTASPACE-1,REPSTART-1,SANDR-1,EATSPACE-1,ESC-1,ONOFF-1
Toggle ESCape mode.
ESC LDA ESCFLAG
EOR #128
STA ESCFLAG
RTS
Change the character definition of the
character used to fill in the end of a
line. It alternates between being a
blank space, and being a blank space
with a tiny dot visible. This lets you
see which spaces are actually part of
your text and which are just used to
parse the screen. Beware of the address
$2204 if you reassemble at a different
address (sorry, I didn't use a label).
ONOFF LDA $2204
EOR #16
STA $2204
RTS
The CHECK routine first prevents the
cursor from disappearing past the
beginning or end-of-text memory and
prevents us from cursoring past the
end-of-text pointer. It also checks to
see if the cursor has left the visible
screen, scrolling with REFRESH to make
the cursor visible. The double-byte
SBCs are used as a 16-bit CMP macro,
setting the Z and C flags just like CMP
does.
CHECK JSR CHECK2
SEC
LDA CURR
SBC TOPLIN
LDA CURR+1
SBC TOPLIN+1
BCS OK1
SEC
LDA TOPLIN
SBC TEXSTART
STA TEMP
LDA TOPLIN+1
SBC TEXSTART+1
ORA TEMP
BEQ OK1
LDA CURR
STA TOPLIN
LDA CURR+1
STA TOPLIN+1
JSR REFRESH
OK1 SEC
LDA BOTSCR
SBC CURR
STA TEX
LDA BOTSCR+1
SBC CURR+1
STA TEX+1
ORA TEX
BEQ EQA
BCS OK2
EQA CLC
LDA TOPLIN
ADC LENTABLE
STA TOPLIN
LDA TOPLIN+1
ADC #0
STA TOPLIN+1
REF JSR REFRESH
JMP OK1
OK2 RTS
CHECK2 SEC
LDA LASTLINE
SBC TEXEND
STA TEMP
LDA LASTLINE+1
SBC TEXEND+1
ORA TEMP
BCC CK3
LDA TEXEND
STA LASTLINE
LDA TEXEND+1
STA LASTLINE+1
CK3 SEC
LDA CURR
SBC TEXSTART
STA TEMP
LDA CURR+1
SBC TEXSTART+1
ORA TEMP
BCS INRANGE
LDA TEXSTART
STA CURR
LDA TEXSTART+1
STA CURR+1
RTS
INRANGE SEC
LDA CURR
SBC LASTLINE
STA TEMP
LDA CURR+1
SBC LASTLINE+1
ORA TEMP
BCS OUTRANGE
RTS
OUTRANGE LDA LASTLINE
STA CURR
LDA LASTLINE+1
STA CURR+1
RTS
Move cursor right. If the OPTION key is
held down, we instead increase the line
length.
RIGHT
LDA #8
STA 53279
LDA 53279
CMP #3
BNE CRIGHT
LDA LINELEN
CMP #40
BEQ NOBIGGER
INC LINELEN
INC LINELEN
DEC RLM
JSR REFRESH
JSR CHECK
LDA #125
JSR CHROUT
NOBIGGER JMP SYSMSG
CRIGHT INC CURR
BNE NOINCR
INC CURR+1
NOINCR JMP CHECK
Move cursor left. If the OPTION key is
held down, we instead decrease the line
length.
LEFT LDA #8
STA 53279
LDA 53279
CMP #3
BNE CLEFT
LDA LINELEN
CMP #2
BEQ TOOSMALL
DEC LINELEN
DEC LINELEN
INC RLM
JSR REFRESH
JSR CHECK
LDA #125
JSR CHROUT
TOOSMALL JMP SYSMSG
CLEFT LDA CURR
BNE NODEC
DEC CURR+1
NODEC DEC CURR
JMP CHECK
Word left. We look backward for a space.
WLEFT LDA CURR
STA TEX
LDA CURR+1
STA TEX+1
DEC TEX+1
LDY #$FF
STRIP LDA (TEX),Y
CMP #SPACE
BEQ STRLOOP
CMP #RETCHAR
BNE WLOOP
STRLOOP DEY
BNE STRIP
WLOOP LDA (TEX),Y
CMP #SPACE
BEQ WROUT
CMP #RETCHAR
BEQ WROUT
DEY
BNE WLOOP
RTS
WROUT SEC
TYA
ADC TEX
STA CURR
LDA TEX+1
ADC #0
STA CURR+1
JMP CHECK
Word right. We scan forward for a
space. OIDS is not a meaningful label.
WRIGHT LDY #0
RLOOP LDA (CURR),Y
CMP #SPACE
BEQ ROUT
CMP #RETCHAR
BEQ ROUT
INY
BNE RLOOP
RTS
ROUT INY
BNE OIDS
INC CURR+1
LDA CURR+1
CMP LASTLINE+1
BCC OIDS
BNE LASTWORD
OIDS LDA (CURR),Y
CMP #SPACE
BEQ ROUT
CMP #RETCHAR
BEQ ROUT
Add the Y register to the CURRent
cursor position to move the cursor.
CHECK prevents illegal cursor movement.
LASTWORD is called if the end of the
word cannot be found before we reach
the end-of-text.
ADYCURR CLC
TYA
ADC CURR
STA CURR
LDA CURR+1
ADC #0
STA CURR+1
WRTN JMP CHECK
LASTWORD LDA LASTLINE
STA CURR
LDA LASTLINE+1
STA CURR+1
JMP CHECK
ENDTEX is tricky. If the end-of-text
pointer would point to an area already
visible on the screen, we just move the
cursor there and call REFRESH.
Otherwise, we step back 1K from the
end-of-text and then scroll to the end.
This is necessary since in the worst
case only 18 characters of return-marks
would fill the screen.
ENDTEX LDA #0
STA TOPLIN
LDA LASTLINE+1
SEC
SBC #4
CMP TEXSTART+1
BCS SAFE
LDA TEXSTART+1
SAFE STA TOPLIN+1
JSR REFRESH
JMP LASTWORD
Change the border color. The display
list interrupt automatically places
SCRCOL into the hardware background
color register #2.
BORDER INC SCRCOL
INC SCRCOL
RTS
SCRCOL .BYTE 8
Change text luminance. TEXCOLR is
stored into hardware color register #1
during the display-list interrupt.
LETTERS INC TEXCOLR
INC TEXCOLR
LDA TEXCOLR
AND #15
STA TEXCOLR
RTS
TEXCOLR .BYTE 2
Sentence left. We look backward for
ending punctuation or a return-mark,
then go forward until we run out of
spaces.
SLEFT LDA CURR
STA TEX
LDA CURR+1
STA TEX+1
DEC TEX+1
LDY #$FF
PMANY LDA (TEX),Y
CMP #'.-32
BEQ PSRCH
CMP #'!-32
BEQ PSRCH
CMP #'?-32
BEQ PSRCH
CMP #RETCHAR
BNE PSLOOP
PSRCH DEY
BNE PMANY
RTS
PSLOOP LDA (TEX),Y
CMP #'.-32
BEQ PUNCT
CMP #'!-32
BEQ PUNCT
CMP #'?-32
BEQ PUNCT
CMP #RETCHAR
BEQ PUNCT
DEY
BNE PSLOOP
DEC TEX+1
LDA TEX+1
CMP TEXSTART
BCS PSLOOP
JMP FIRSTWORD
PUNCT STY TEMP
DEC TEMP
SKIPSPC INY
BEQ REPEAT
LDA (TEX),Y
CMP #SPACE
BEQ SKIPSPC
DEY
JMP WROUT
REPEAT LDY TEMP
JMP PSLOOP
FIRSTWORD LDA TEXSTART
STA CURR
LDA TEXSTART+1
STA CURR+1
JMP CHECK
Sentence right. We look forward for
ending punctuation, then skip forward
until we run out of spaces.
SRIGHT LDY #0
SRLP LDA (CURR),Y
CMP #'.-32
BEQ PUNCT2
CMP #'!-32
BEQ PUNCT2
CMP #'?-32
BEQ PUNCT2
CMP #RETCHAR
BEQ PUNCT2
INY
BNE SRLP
INC CURR+1
LDA CURR+1
CMP LASTLINE+1
BEQ SRLP
BCC SRLP
SREXIT JMP LASTWORD
PUNCT2 INY
BNE NOFIXCURR
INC CURR+1
LDA CURR+1
CMP LASTLINE+1
BCC NOFIXCURR
BEQ NOFIXCURR
JMP LASTWORD
NOFIXCURR LDA (CURR),Y
CMP #SPACE
BEQ PUNCT2
CMP #'.-32
BEQ PUNCT2
CMP #'!-32
BEQ PUNCT2
CMP #'?-32
BEQ PUNCT2
CMP #RETCHAR
BEQ PUNCT2
JMP ADYCURR
The text buffer starts at a fixed
location, but the end of the buffer is
changed as text is added to it. To
clear the buffer, we just set the end
of the buffer to the value of the start
of the buffer. No text is actually
erased.
KILLBUFF LDA TEXBUF
STA TPTR
LDA TEXBUF+1
STA TPTR+1
JSR TOPCLR
LDA # <KILLMSG
LDY # >KILLMSG
JSR PRMSG
LDA #1
STA MSGFLG
RTS
This is the second level of the
general-purpose delete routines. UMOVE
is the primitive core of deleting. For
CTRL-D, the CURRent cursor position is
the source; then a cursor command is
called to update the cursor pointer.
This becomes the destination. For
CTRL-E, the CURRent cursor position is
the destination; a cursor movement
routine is called, and this becomes the
source. UMOVE is then called. We
actually move more than the length from
the source to the end-of-text. Some
extra text is moved from past the
end-of-text. Since everything past the
end-of-text is spaces, this neatly
erases everything past the new
end-of-text position. Naturally, the
end-of-text pointer is updated. Before
the actual delete is performed, the
text to be deleted is stored in the
buffer so that it can be recalled in
case of error. The buffer doubles as a
fail-safe device, and for moving and
copying text. Checks are made to make
sure that the buffer does not overflow.
DEL1 SEC
LDA CURR
SBC TEXSTART
STA TEMP
LDA CURR+1
SBC TEXSTART+1
ORA TEMP
BNE DEL1A
DELABORT PLA
PLA
RTS
DEL1A LDA CURR
STA FROML
LDA CURR+1
STA FROMH
RTS
DEL2 SEC
LDA CURR
STA DESTL
EOR #$FF
ADC FROML
STA GOBLEN
LDA CURR+1
STA DESTH
EOR #$FF
ADC FROMH
STA GOBLEN+1
DELC LDA FROML
STA FROMSAV
LDA FROMH
STA FROMSAV+1
LDA DESTL
STA DESTSAV
STA FROML
LDA DESTH
STA DESTSAV+1
STA FROMH
SEC
LDA GOBLEN+1
ADC TPTR+1
CMP BUFEND+1
BCC GOSAV
JSR TOPCLR
LDA # <BUFERR
LDY # >BUFERR
JSR PRMSG
LDA #1
STA MSGFLG
RTS
GOSAV LDA TPTR
STA DESTL
LDA TPTR+1
STA DESTH
LDA GOBLEN
STA LLEN
CLC
ADC TPTR
STA TPTR
LDA GOBLEN+1
STA HLEN
ADC TPTR+1
STA TPTR+1
JSR UMOVE
LDA FROMSAV
STA FROML
LDA FROMSAV+1
STA FROMH
LDA DESTSAV
STA DESTL
LDA DESTSAV+1
STA DESTH
SEC
LDA LASTLINE
SBC DESTL
STA LLEN
LDA LASTLINE+1
SBC DESTH
STA HLEN
JSR UMOVE
SEC
LDA LASTLINE
SBC GOBLEN
STA LASTLINE
LDA LASTLINE+1
SBC GOBLEN+1
STA LASTLINE+1
RTS
Most delete commands end up calling the
above routines. The single-character
deletes must subtract 1 from the buffer
pointer so that single characters are
not added to the buffer. But note how
short these routines are.
Delete character (BACK S)
DELCHAR JSR DEL1
JSR LEFT
JSR DEL2
FIXTP SEC
LDA TPTR
SBC #1
STA TPTR
LDA TPTR+1
SBC #0
STA TPTR+1
RTS
CTRL-BACK S
DELIN JSR RIGHT
JSR DEL1
JSR LEFT
JSR DEL2
JMP FIXTP
Called by CTRL-D. As mentioned, it
stores CURR into FROML/FROMH, moves the
cursor either by sentence, word, or
paragraph, then stores the new position
of CURR into DESTL and DESTH. The above
routines perform the actual delete.
CTRL-D always discards the previous
contents of the buffer, for deleting
text backward creates a buffer of
out-of-order text. Notice how we change
the color of the command window to red
to warn the user of the impending
deletion.
DELETE JSR KILLBUFF
LDA #RED
STA WINDCOLR
JSR TOPCLR
LDA # <DELMSG
LDY # >DELMSG
JSR PRMSG
JSR GETAKEY
PHA
JSR SYSMSG
PLA
AND #95
ORA #64
CMP #'W
BNE NOTWORD
DELWORD JSR DEL1
JSR WLEFT
JMP DEL2
NOTWORD CMP #'S
BNE NOTSENT
DELSENT JSR DEL1
JSR SLEFT
JMP DEL2
NOTSENT CMP #'P
BNE NOTPAR
JSR DEL1
JSR PARLEFT
JMP DEL2
NOTPAR RTS
Home the cursor. This is called by the
START key. We check to see if START is
held down for at least 1/2 second. If
it is, we move the cursor to the top of
text.
HOME SEC
LDA CURR
SBC TOPLIN
STA TEMP
LDA CURR+1
SBC TOPLIN+1
ORA TEMP
BEQ TOPHOME
LDA TOPLIN
STA CURR
LDA TOPLIN+1
STA CURR+1
WAITST LDA #0
STA 20
STA 53279
HOMEPAUSE LDA 20
CMP #30
BNE HOMEPAUSE
OUTHOME JMP CHECK
TOPHOME LDA TEXSTART
STA CURR
LDA TEXSTART+1
STA CURR+1
JMP WAITST
This deletes all spaces between the
cursor and following nonspace text.
Sometimes inventing labels can be fun.
EATSPACE LDA CURR
STA TEX
STA DESTL
LDA CURR+1
STA TEX+1
STA DESTH
LDY #0
SPCSRCH LDA (TEX),Y
CMP #SPACE
BNE OUTSPACE
INY
BNE SPCSRCH
LDA TEX+1
CMP LASTLINE+1
BCC GOINC
LDA LASTLINE
STA TEX
LDA LASTLINE+1
STA TEX+1
LDY #0
JMP OUTSPACE
GOINC INC TEX+1
JMP SPCSRCH
OUTSPACE CLC
TYA
ADC TEX
STA FROML
LDA #0
ADC TEX+1
STA FROMH
SEC
LDA LASTLINE
SBC DESTL
STA LLEN
LDA LASTLINE+1
SBC DESTH
STA HLEN
SEC
LDA FROML
SBC DESTL
STA GOBLEN
LDA FROMH
SBC DESTH
STA GOBLEN+1
JSR UMOVE
SEC
LDA LASTLINE
SBC GOBLEN
STA LASTLINE
LDA LASTLINE+1
SBC GOBLEN+1
STA LASTLINE+1
RTS
Insert 255 spaces. Notice how it and
other insert routines use TAB2.
LOTTASPACE LDA #255
STA INSLEN
JMP TAB2
TAB LDA #5
STA INSLEN
JSR TAB2
LDA (CURR),Y
CMP #SPACE
BNE NOINCY
INY
NOINCY JMP ADYCURR
TAB2 LDA #0
STA INSLEN+1
JSR INSBLOCK
LDA #SPACE
LDX INSLEN
LDY #0
FILLSP STA (CURR),Y
INY
DEX
BNE FILLSP
RTS
Insert a single space.
INSCHAR LDA #1
STA INSLEN
LDA #0
STA INSLEN+1
JSR INSBLOCK
LDA #SPACE
LDY #0
STA (CURR),Y
JMP CHECK
A general routine to insert as many
spaces as are specified by INSLEN.
INSBLOCK CLC
LDA LASTLINE
ADC INSLEN
LDA LASTLINE+1
ADC INSLEN+1
CMP TEXEND+1
BCC OKINS
PLA
PLA
JMP INOUT
OKINS CLC
LDA CURR
STA FROML
ADC INSLEN
STA DESTL
LDA CURR+1
STA FROMH
ADC INSLEN+1
STA DESTH
SEC
LDA LASTLINE
SBC FROML
STA LLEN
LDA LASTLINE+1
SBC FROMH
STA HLEN
JSR DMOVE
CLC
LDA LASTLINE
ADC INSLEN
STA LASTLINE
LDA LASTLINE+1
ADC INSLEN+1
STA LASTLINE+1
INOUT RTS
Toggle insert mode. The INSMODE nag
doubles as the color of the command
line.
INSTGL LDA INSMODE
EOR #BLUE
STA INSMODE
RTS
Another example of modular code. This
is called anytime a yes/no response is
called for. It prints "Are you sure?
(Y/N)," then returns with the zero flag
set to true if Y was pressed, ready for
the calling routine to use BEQ or BNE
as a branch for yes or no. We trap out
the clear-screen key in case this
routine is called by Erase All, since
otherwise repeating keys may instantly
cancel the command. The AND #223 zaps
out the distinction between uppercase
and lowercase Y.
YORN LDA # <YMSG
LDY # >YMSG
JSR PRMSG
YORNKEY JSR GETIN
AND #127
BEQ YORNKEY
CMP #125
BEQ YORNKEY
AND #223
CMP #'Y
RTS
Erase all text. Allowed only if the
OPTION key is held down with
SHIFT-CLEAR. It calls YORN to affirm
the deadly deed, then calls ERASE to
erase all text, INIT2 to reset some
flags, then jumps back to the MAIN
loop. LDX #$FA / TXS is used to clean
up the stack.
CLEAR LDA #8
STA 53279
LDA 53279
CMP #3
BEQ OKCLEAR
RTS
OKCLEAR LDA #RED
STA WINDCOLR
JSR TOPCLR
LDA # <CLRMSG
LDY # >CLRMSG
JSR PRMSG
JSR YORN
BEQ DOIT
JMP SYSMSG
DOIT LDX #$FA
TXS
JSR ERASE
JSR INIT2
JMP MAIN
Paragraph right.
PARIGHT LDY #0
PARLP LDA (CURR),Y
CMP #RETCHAR
BEQ RETFOUND
INY
BNE PARLP
INC CURR+1
LDA CURR+1
CMP LASTLINE+1
BCC PARLP
BEQ PARLP
JMP LASTWORD
RETFOUND INY
BNE GOADY
INC CURR+1
GOADY JMP ADYCURR
Paragraph left. Notice the trick of
decrementing the high byte of the
pointer, then starting the index at 255
in order to search backward.
PARLEFT LDA CURR
STA TEX
LDA CURR+1
STA TEX+1
DEC TEX+1
LDY #$FF
PARLOOP LDA (TEX),Y
CMP #RETCHAR
BEQ RETF2
PARCONT DEY
CPY #255
BNE PARLOOP
DEC TEX+1
LDA TEX+1
CMP TEXSTART+1
BCS PARLOOP
JMP FIRSTWORD
RETF2 SEC
TYA
ADC TEX
STA TEX
LDA #0
ADC TEX+1
STA TEX+1
SEC
LDA TEX
SBC CURR
STA TEMP
LDA TEX+1
SBC CURR+1
ORA TEMP
BNE TEXTOCURR
STY TEMP
CLC
LDA TEX
SBC TEMP
STA TEX
LDA TEX+1
SBC #0
STA TEX+1
JMP PARCONT
TEXTOCURR LDA TEX
STA CURR
LDA TEX+1
STA CURR+1
JMP CHECK
This enables the display-list interrupt
(DLI). The DLI allows separate
background colors for the command line
and the rest of the screen. It lets us
change the color of the top line to
flag insert mode or to warn the user
with a red color that he/she should be
careful. Since it is an interrupt, it
is always running in the background.
Interrupt routines must always be
careful not to corrupt the main
program.
HIGHLIGHT turns off any DLIs (by
storing #64 into $D40E), sets the NMI
pointer ($200/$201), creates a custom
display list of IRG mode 3 (lowercase
descenders, GRAPHICS 0 1/2) With DLI
set in one line, then enables DLIs ($C0
into $D40E) and returns. The routine
DLI is now running constantly in the
background, changing the screen color
of all text below the DLI.
HIGHLIGHT LDA #64
STA $D40E
LDA # <DLI
STA $0200
LDA # >DLI
STA $0201
LDA 560
STA TEMP
LDA 561
STA TEMP+1
LDY #0
DLOOP LDA DLIST,Y
STA (TEMP),Y
INY
CPY #28
BNE DLOOP
LDY #4
LDA $58
STA (TEMP),Y
LDA $59
INY
STA (TEMP),Y
LDY #26
LDA TEMP
STA (TEMP),Y
LDA TEMP+1
INY
STA (TEMP),Y
LDA #$C0
STA $D40E
RTS
The custom display list.
DLIST .BYTE 112,112,112,3+64+128,0,0
.BYTE 3,3,3,3,3,3,3,3,3,3,3,3,3
.BYTE 3,3,3,3,3,16,65,0,0
The display-list interrupt routine
stores the SCReen COLor and TEXt COLoR
into the appropriate hardware
registers, then stores the WINDow COLoR
into 710, and #10 into 709 to set the
color of the top line of the screen.
This line is automatically set by the
normal vertical- blank interrupt. We
also force the character-set pointer to
keep our character set in place
whenever we're on the editing screen.
DLI PHA
LDA SCRCOL
STA $D40A
STA $D018
STA 712
LDA TEXCOLR
STA $D017
LDA WINDCOLR
STA 710
LDA #10
STA 709
LDA #$20
STA 756
LDA #0
STA $02B6
PLA
RTI
ERAS is called by CTRL-E. It works much
like CTRL-D. Notice that the ORA #64
allows users to press either S, W, P,
or CTRL-S, CTRL-W, CTRL-P, in case they
have a habit of leaving the control key
held down. It must call RE- FRESH after
each move and adjust the new position
of the cursor. If OPTION is held down
with CTRL-E, we don't erase the
previous contents of the buffer,
letting the user chain non-contiguous
sections into the buffer for later
recall.
ERAS LDA #8
STA 53279
LDA 53279
CMP #3
BEQ ERAS1
JSR KILLBUFF
ERAS1 JSR TOPCLR
LDA # <ERASMSG
LDY # >ERASMSG
JSR PRMSG
ERASAGAIN LDY #0
LDA (CURR),Y
EOR #$80
STA (CURR),Y
JSR REFRESH
LDY #0
LDA (CURR),Y
EOR #$80
STA (CURR),Y
LDA #RED
STA WINDCOLR
JSR GETAKEY
AND #95
ORA #64
CMP #'W
BNE NOWORD
ERASWORD JSR ERA1
JSR WRIGHT
JMP ERA2
NOWORD CMP #'S
BNE UNSENT
ERASENT JSR ERA1
JSR SRIGHT
JMP ERA2
UNSENT CMP #'P
BNE NOPAR
JSR ERA1
JSR PARIGHT
JMP ERA2
NOPAR JSR CHECK
JMP SYSMSG
ERA1 LDA CURR
STA DESTL
STA SAVCURR
LDA CURR+1
STA DESTH
STA SAVCURR+1
RTS
ERA2 SEC
LDA CURR
STA FROML
SBC SAVCURR
STA GOBLEN
LDA CURR+1
STA FROMH
SBC SAVCURR+1
STA GOBLEN+1
JSR DELC
LDA SAVCURR
STA CURR
LDA SAVCURR+1
STA CURR+1
JSR REFRESH
JMP ERASAGAIN
The INPUT routine is used to get
responses from the command line. It
returns the complete line in INBUFF.
INLEN is the length of the input. A
zero byte is stored at INBUFF+INLEN
after the user presses RETURN. This
routine is foolproof (I know...), since
no control keys other than BACK S are
allowed, unless preceded by ESCape. The
SELECT key can be held down to enter
inverse-video characters. The system
cursor is turned on for this routine
(by putting #0 into 752), then turned
off when we exit (by putting #1 into
752). This routine also prevents the
user from typing past the end of the
command line. If the limit of typing
length must be set arbitrarily, LIMIT
is preset and INPUT is called at INP1.
CURSIN is the MAIN loop.
INPUT LDA #39
SBC 85
STA LIMIT
INP1 LDY #0
STY INLEN
STY 752
LDA #32
JSR CHROUT
LDA #126
JSR CHROUT
CURSIN STY INLEN
JSR GETAKEY
LDY INLEN
BIT ESCFLAG
BMI ESCKEY
CMP #27
BNE NOESC
LDA #128
STA ESCFLAG
STA $02A2
JMP CURSIN
NOESC CMP #155
BEQ INEXIT
CMP #126
BNE NOBACK
DEY
BPL NOTZERO
INY
JMP CURSIN
NOTZERO LDA #126
JSR CHROUT
JMP CURSIN
NOBACK STA TEMP
AND #127
CMP #32
BCC CURSIN
CMP #125
BCS CURSIN
CPY LIMIT
BEQ CURSIN
LDA TEMP
ESCKEY AND #127
LDX #8
STX 53279
LDX 53279
CPX #5
BNE SKIPSEL
ORA #128
SKIPSEL STA INBUFF,Y
JSR CHROUT
LDA #0
STA ESCFLAG
INY
JMP CURSIN
INEXIT LDX #1
STX 752
LDA #0
STA INBUFF,Y
TYA
RTS
.END
Filename: SUPPORT
This module supports most primitive
input/output functions, including a
routine to clear the screen and reset
the screen editor (OPENEDITOR), print a
character (CHROUT), and get a key from
the keyboard (GETAKEY).
OPENEDITOR LDX #0
LDA #12
STA ICCOM
JSR CIO
LDX #0
LDA # <ENAME
STA ICBADR
LDA # >ENAME
STA ICBADR+1
LDA #2
STA ICBLEN
STX ICBLEN+1
LDA #3
STA ICCOM,X
JMP CIO
Put the ATASCII value of the character
into the accumulator and call CHROUT
to print a character. The Y register is
preserved. We call CIO with a buffer
length of zero.
CHROUT STY CHRYSAVE
LDX #0
STX ICBLEN
STX ICBLEN+1
STX $02FF
LDY #11
STY ICCOM
JSR CIO
LDY CHRYSAVE
RTS
The filename of the Editor device.
ENAME .BYTE "E:"
OUTNUM and PROUTNUM print decimal
numbers to the display or printer. The
integer to be printed is passed with
the low byte in the X register and the
high byte in the accumulator. The
integer to floating-point routine
($D9AA) is called first, followed by
floating-point to ATASCII routine,
which creates a string of ATASCII
digits. The last digit of the number
has bit 7 set, which we use to
terminate printing.
PROUTNUM LDY #128
JMP OVERZAP
OUTNUM LDY #0
OVERZAP STY WHICHFLAG
STX $D4
STA $D5
JSR $D9AA
JSR $D8E6
LDY #0
ONUMLOOP LDA ($F3),Y
PHA
AND #$7F
BIT WHICHFLAG
BMI GOPCHR
JSR CHROUT
JMP OVERPCHR
GOPCHR JSR PCHROUT
OVERPCHR PLA
BMI ONUMEXIT
INY
BNE ONUMLOOP
ONUMEXIT RTS
CHRYSAVE .BYTE 0
The system keyboard fetch routine
interferes with the display-list
interrupt, since the blip of each key
is timed with WSYNC, which freezes the
ANTIC chip for one line. This causes
annoying flicker. This routine uses
POKEY sound decaying from volume 15 to
0 for the keyboard feedback tone. It's
not hard to create any sound effect you
want for the keyboard blip. This
routine mimics the system routine
fairly closely. It's easy to expand it
to allow many more keyboard functions
and full processing of new keystrokes
just by changing some of this code and
the keyboard table.
GETIN LDA 764
CMP #$FF
BNE GETCHAR
LDA #0
RTS
GETCHAR LDA 764
CMP #$FF
BEQ GETCHAR
STA KEYVAL
LDA #$FF
STA 764
Clear break flag.
STA $11
JSR BLIP
LDA KEYVAL
Check for SHIFT+CTRL.
CMP #$C0
BCS GXIT
AND #63
CMP #60
BNE NOTCAPS
LDA KEYVAL
AND #64
BEQ NOTSET
STA SHFLOK
GXIT LDA #0
RTS
The CAPS/LOWR key toggles the SHiFtLOcK
flag to allow either only uppercase, or
both uppercase and lowercase.
NOTSET LDA SHFLOK
EOR #64
STA SHFLOK
LDA #0
RTS
NOTCAPS LDX KEYVAL
LDA KEYBOARD,X
BIT SHFLOK
BVC NOTLOCKED
CMP #'a
BCC NOTLOCKED
CMP #'z+1
BCS NOTLOCKED
AND #223
NOTLOCKED CMP #$80
BEQ GXIT
RTS
The sound effect for the keyboard
"blip."
BLIP PHA
LDA #50
STA $D200
LDX #$AF
SNDLOOP STX $D201
LDY #128
SLOW DEY
BNE SLOW
DEX
CPX #$9F
BNE SNDLOOP
PLA
RTS
KEYBOARD .BYTE 108,106,59,128,128,107
.BYTE 43,42,111,128,112,117
.BYTE 155,105,45,61,118,128
.BYTE 99,128,128,98,120,122
.BYTE 52,128,51,54,27,53
.BYTE 50,49,44,32,46,110
.BYTE 128,109,47,$80,114,128
.BYTE 101,121,127,116,119,113
.BYTE 57,128,48,55,126,56
.BYTE 60,62,102,104,100,128
.BYTE 130,103,115,97,76,74
.BYTE 58,128,128,75,92,94
.BYTE 79,128,80,85,155,73
.BYTE 95,124,86,128,67,128
.BYTE 128,66,88,90,36,128
.BYTE 35,38,27,37,34,33
.BYTE 91,32,93,78,128,77
.BYTE 63,$80,82,128,69,89
.BYTE 159,84,87,81,40,128
.BYTE 41,39,156,64,125,157
.BYTE 70,72,68,128,131,71
.BYTE 83,65,12,10,123,128
.BYTE 128,11,30,31,15,128
.BYTE 16,21,155,9,28,29
.BYTE 22,128,3,128,128,2
.BYTE 24,26,128,128,133,128
.BYTE 27,128,253,128,0,32
.BYTE 96,14,128,13,128,$80
.BYTE 18,128,5,25,158,20
.BYTE 23,17,128,128,128,128
.BYTE 254,128,125,255,6,8
.BYTE 4,128,132,7,19,1
.END
Filename: DOSPAK
DOSPAK is a self-contained substitute
for the DOS menu, although it uses
several routines built into
SpeedScript. The concept of DOSPAK is
that all directory entries should fit
on one screen. A large cursor is used
to move from filename to filename. At
any time, you can delete, rename, lock,
unlock, or load the selected filename,
just by pressing one key, or a CTRL key
combination. Except for Rename, you
don't have to type the filename. You
can also format the entire disk or
redisplay the directory.
CATALOG fits the entire disk directory
onto the screen by skipping over the
sector counts, trimming up spacing, and
placing three items per line. The
cursor position of each filename is
saved into a slot in memory so that the
cursor routine can quickly and easily
skip about.
CATALOG JSR CLOSE7
LDX #$70
LDA # <DIRNAME
STA ICBADR,X
LDA # >DIRNAME
STA ICBADR+1,X
LDA #5
STA ICBLEN,X
LDA #0
STA ICBLEN+1,X
LDA #6
STA ICAUX1,X
LDA #3
STA ICCOM,X
JSR CIO
BMI CLOSE7
LDA #0
STA XPTR
REDIR LDX XPTR
LDA $64
STA SLOT,X
LDA $65
STA SLOT+1,X
INC XPTR
INC XPTR
JSR GET7
BMI CLOSE7
CMP #'*+1
BCS ENDIR
JSR CHROUT
JSR GET7
BMI CLOSE7
LDA #0
STA DIRCOUNT
DIRLOOP JSR GET7
BMI CLOSE7
DNOTCR JSR CHROUT
INC DIRCOUNT
LDA DIRCOUNT
CMP #8
BNE DNOT8
LDA #'.
JSR CHROUT
JMP DIRLOOP
DNOT8 CMP #11
BNE DIRLOOP
LDA #5
STA TEMP
THROW5 JSR GET7
DEC TEMP
LDA TEMP
BNE THROW5
JMP REDIR
CLOSE7 LDX #$70
LDA #12
STA ICCOM,X
JSR CIO
LDX #$70
LDY ICSTAT,X
RTS
ENDIR PHA
LDA #155
JSR CHROUT
PLA
JSR CHROUT
ENDLP JSR GET7
BMI CLOSE7
JSR CHROUT
JMP ENDLP
GET7 LDX #$70
LDA #0
STA ICBLEN,X
STA ICBLEN+1,X
LDA #7
STA ICCOM,X
JMP CIO
The main DOS routine calls the CATALOG
routine to fill the screen with
filenames, then puts the cursor on the
current filename, waiting for a
keypress.
DOS JSR DELITE
JSR OPENEDITOR
JSR DELITE
LDA #1
STA 752
STA 82
LDA #125
JSR CHROUT
JSR CATALOG
JSR DOSMSG
GETNAME LDA SLOT
STA SCR
LDA SLOT+1
STA SCR+1
LDA #0
STA XSLOT
DEC XPTR
DEC XPTR
NAMELP JSR INVNAME
JSR GETAKEY
LDX #1
STX 752
Now that we've got a keypress, we look
it up in the keypress table, then
vector to the appropriate routine. This
is the same ML ON-GOTO routine that
we've used in several places in
SpeedScript, including the CONTROL
routine.
LDX DOSTABLE
CMP #97
BCC NOPROB
AND #95
NOPROB STA TEMP
FINDIT CMP DOSTABLE,X
BEQ FOUNDIT
DEX
BNE FINDIT
JMP JNAME
FOUNDIT DEX
TXA
ASL A
TAX
LDA DOSADR+1,X
PHA
LDA DOSADR,X
PHA
RTS
The braces surround control characters,
some entered with the ESCape key:
cursor-left, cursor-right, cursor-up,
cursor-down, CTRL-D, ESCape, and
CTRL-L.
DOSTABLE .BYTE 15
.BYTE "{LEFT}{RIGHT}{UP}{DOWN}{D}RLUF1234{ESC}{L}"
DOSADR .WORD DLEFT-1,DRIGHT-1,DUP-1,DDOWN-1,DELFILE-1,RENAME-1
.WORD LOCK-1,UNLOCK-1,FORMAT-1,DRIVE-1,DRIVE-1,DRIVE-1
.WORD DRIVE-1,ESCDOS-1,LOADIT-1
Move bar cursor left by decrementing
slot pointer.
DLEFT JSR INVNAME
LDX XSLOT
BEQ NRANGE
DEX
DEX
JMP RESLOT
Move bar cursor right by incrementing
slot pointer.
DRIGHT JSR INVNAME
LDX XSLOT
INX
INX
CPX XPTR
BCS NRANGE
Store new slot index.
RESLOT STX XSLOT
LDA SLOT,X
STA SCR
LDA SLOT+1,X
STA SCR+1
NRANGE JMP NAMELP
Move bar cursor up by subtracting 6
from the slot pointer (each slot is two
bytes).
DUP JSR INVNAME
LDA XSLOT
CMP #6
BCC NRANGE
SEC
SBC #6
TAX
JMP RESLOT
Move bar cursor down by adding 6 to
the slot pointer.
DDOWN JSR INVNAME
LDA XSLOT
CLC
ADC #6
CMP XPTR
BCS NRANGE
TAX
JMP RESLOT
This routine turns a filename pointed
to by the bar cursor into a legal CIO
filename, complete with Dx: and legal
extension.
NAMER LDX #0
COPYD LDA DIRNAME,X
STA FNBUFF,X
INX
CPX #3
BNE COPYD
LDY #1
COPYNAME LDA (SCR),Y
AND #127
JSR INTOAS
CMP #32
BEQ NOSTOR
STA FNBUFF,X
INX
NOSTOR INY
CPY #13
BNE COPYNAME
LDA FNBUFF-1,X
CMP #'.
BNE NOTDOT
DEX
NOTDOT STX FNLEN
LDA #0
STA FNBUFF,X
RTS
This routine passes any CIO command
along with a formed filename.
XIO LDX #$70
STA ICCOM,X
LDA FNLEN
STA ICBLEN,X
LDA #0
STA ICBLEN+1,X
LDA # <FNBUFF
STA ICBADR,X
LDA # >FNBUFF
STA ICBADR+1,X
JMP CIO
The DOS functions are quite short.
NAMER builds the name; then we simply
pass the number of the DOS CIO function
unto XIO. If there's no error, we
return to waiting for the next
keystroke; otherwise, print the DOS
error message and wait for a keystroke.
DELFILE JSR NAMER
LDA #33
Jump to the XIO routine.
GOXIO JSR XIO
BPL JNAME
JMP DOSERR
JNAME JSR INVNAME
JMP NAMELP
Lock a file.
LOCK JSR NAMER
LDA #35
JMP GOXIO
Unlock a file.
UNLOCK JSR NAMER
LDA #36
JMP GOXIO
We ask for the new name of the file,
build the rename string, then jump to
the XIO routine.
RENAME JSR BOTCLR
LDA # <RENMSG
LDY # >RENMSG
JSR PRMSG
LDA #64
STA $02BE
JSR INPUT
LDA #0
STA $02BE
LDA INLEN
BEQ NONAME
JSR NAMER
LDX #0
LDY FNLEN
LDA #',
STA FNBUFF,Y
INY
COPYR LDA INBUFF,X
STA FNBUFF,Y
INY
INX
CPX INLEN
BNE COPYR
STY FNLEN
LDA #0
STA FNBUFF,Y
JSR DOSMSG
LDA #32
JMP GOXIO
NONAME JSR DOSMSG
JMP JNAME
Format routine. We use YORN to af firm
this operation, which erases an entire
disk. BOTCLR clears the bottom line of
the screen.
FORMAT JSR BOTCLR
LDA # <FORMSG
LDY # >FORMSG
JSR PRMSG
JSR YORN
BNE NONAME
JSR DOSMSG
JSR NAMER
LDA #254
JMP GOXIO
Select new drive number and redisplay
directory.
DRIVE LDA TEMP
STA DIRNAME+1
JMP DOS
The Load-from-directory routine opens
the file, then jumps into the SpeedScript
Load routine.
LOADIT LDX #$70
STX IOCB
LDA #4
STA ICAUX1,X
LDA #0
STA INDIR
STA INDIR+1
JSR NAMER
Command 3 is for OPEN file.
LDA #3
JSR XIO
BMI DOSERR
JSR ERASE
JSR LOADLINK
If the load ended with an error, we
display the error; otherwise, we exit
the DOSPAK at ESCDOS.
BMI DOSERR
The ESCape DOS routine clears the
stack, clears the screen, reenables the
display-list interrupt, prints the
"SpeedScript" message, then jumps back
to the editing loop.
ESCDOS LDX #$FA
TXS
LDA #125
JSR CHROUT
JSR HIGHLIGHT
JSR SYSMSG
JMP MAIN
BOTCLR erases the bottom two lines of
the screen by positioning the cursor on
the next-to-the-last line, then
printing two INSERT LINE characters
that push any text on these lines off
the bottom of the screen. Nifty, eh?
BOTCLR LDA #22
STA 84
LDA #157
JSR CHROUT
JMP CHROUT
This is the error routine for the
DOSPAK. We print "ERROR #", then print
the error number with OUTNUM, a bell
character (actually sounds like an
annoying buzzer, appropriate Pavlovian
treatment), then "Press RETURN." We
wait for a keystroke, then return to
getting keys for the DOSPAK commands.
DOSERR STY YSAVE
JSR CLOSE7
JSR BOTCLR
LDA # <ERRMSG
LDY # >ERRMSG
JSR PRMSG
LDX YSAVE
LDA #0
JSR OUTNUM
LDA #253
JSR CHROUT
LDA # <DIRMSG
LDY # >DIRMSG
JSR PRMSG
JSR GETAKEY
JSR DOSMSG
JMP JNAME
Inverse the filename field of the
currently selected filename. Used to
create the bar cursor.
INVNAME LDY #12
INVLP LDA (SCR),Y
EOR #128
STA (SCR),Y
DEY
BPL INVLP
RTS
DOSMSG erases the bottom line of the
screen and prints the DOSPAK command
line, an abbreviated menu.
DOSMSG JSR BOTCLR
LDA # <DIRINS
LDY # >DIRINS
JSR PRMSG
LDA DIRNAME+1
JMP CHROUT
.END
Filename: SPEED.2
This is the main input/output portion
of SpeedScript, responsible for loading,
saving, and all printing functions.
CAST and CINSTOAS (standing for Convert
to ASCII and Convert INTernal code to
ASCII) translate the way SpeedScript
stores text in memory (internal screen
codes) into ASCII so that disk files
will be compatible with most other
software. In addition, the return- mark
is changed to character 155, and vice
versa. This is why you can't load a
machine language file into SpeedScript,
edit it, then save it back as a
runnable modification. All back-arrows
are turned into carriage returns on
output, and all carriage returns
(155's) are turned into back-arrows
(30's) on input.
CAST LDA #0
STA CONVFLAG
JMP CAST1
CINTOAS LDA #128
STA CONVFLAG
CAST1 LDA TEXSTART
STA TEX
LDA TEXSTART+1
STA TEX+1
JMP CIN
CASTOIN LDA #0
STA CONVFLAG
LDA CURR
STA TEX
LDA CURR+1
STA TEX+1
CIN SEC
LDA LASTLINE+1
SBC TEX+1
TAX
INX
LDY #0
CVLOOP LDA (TEX),Y
BIT CONVFLAG
BMI COTHER
CMP #155
BNE NOTRTN
LDA #RETCHAR
JMP OVEROTHER
NOTRTN JSR ASTOIN
JMP OVEROTHER
COTHER CMP #RETCHAR
BNE NOTRC
LDA #155
JMP OVEROTHER
NOTRC JSR INTOAS
OVEROTHER STA (TEX),Y
INY
BNE CVLOOP
INC TEX+1
DEX
BNE CVLOOP
RTS
Here is where most of the input/output
routines start. TSAVE saves the entire
document area using the CIO block
output routine (PUT TEXT). TOPEN is
called by both TSAVE and TLOAD to get
the filename and open the file. The
device specification (D: or C:) must be
typed in by the user.
TSAVE prints the Save: prompt, goes to
TOPEN with an 8 (for output, the same
number in OPEN 1,8,0,"D:file"), and
uses IOCB #7 (LDX #$70) to send a PUT
TEXT command (11). Text is written from
the start-of-text with a length of
LASTLINE - TEXSTART.
TSAVE JSR TOPCLR
LDA # <SAVMSG
LDY # >SAVMSG
JSR PRMSG
LDA #8
JSR TOPEN
BMI ERROR
JSR CINTOAS
LDX #$70
LDA TEXSTART
STA ICBADR,X
LDA TEXSTART+1
STA ICBADR+1,X
SEC
LDA LASTLINE
SBC TEXSTART
STA ICBLEN,X
LDA LASTLINE+1
SBC TEXSTART+1
STA ICBLEN+1,X
LDA #11
STA ICCOM,X
JSR CIO
The N (negative) bit is set when an
error occurs after a call to CIO or a
routine that ends up calling CIO.
Therefore, we can use BMI to branch on
an error condition.
BMI ERR1
JSR CAST
JSR CLOSE7
BMI ERROR
JMP FINE
ERR1 TYA
PHA
JSR CAST
PLA
TAY
The error routine uses the error number
found in the Y register, prints the
error message with PRMSG, and the error
number with OUTNUM. The open file is
closed. If the BREAK key was used to
stop the operation, we distinguish this
from an ordinary error, and print
"BREAK Abort" instead.
ERROR CPY #128
BEQ STOPPED
TYA
PHA
LDA #125
JSR CHROUT
LDA # <ERRMSG
LDY # >ERRMSG
JSR PRMSG
PLA
TAX
LDA #0
JSR OUTNUM
ERXIT JSR IOCLOSE
JSR HIGHLIGHT
LDA #1
STA MSGFLG
RTS
STOPPED JSR TOPCLR
LDA # <BRMSG
LDY # >BRMSG
JSR PRMSG
JMP ERXIT
General file closing routine. IOCB
contains the channel number times 16.
IOCLOSE LDX IOCB
LDA #12
STA ICCOM,X
JMP CIO
TOPEN is used to get a filename,
including the device specification.
It's used by Save, Load, and Print. It
forces the CAPS key to uppercase for
the filename, which is not quite as
satisfactory as converting the filename
if lowercase was used. It does return
the CAPS key to its former value,
though. TOPEN opens the file and
returns with the error code in the Y
register.
TOPEN LDX #$70
STX IOCB
STA ACCESS
Save current CAPS value.
LDA SHFLOK
PHA
CAPS On.
LDA #64
STA SHFLOK
JSR INPUT
Restore CAPS value.
PLA
STA SHFLOK
LDA INLEN
BNE OPCONT
OPABORT JSR SYSMSG
PLA
PLA
JMP HIGHLIGHT
OPCONT JSR IOCLOSE
LDX IOCB
LDA # <INBUFF
STA ICBADR,X
LDA # >INBUFF
STA ICBADR+1,X
LDA INLEN
STA ICBLEN,X
LDA #0
STA ICBLEN+1,X
LDA ACCESS
STA ICAUX1,X
LDA #3
STA ICCOM,X
JMP CIO
The Load routine checks the cursor
position. If the cursor is at the
top-of-text (CURR=TEXSTART), we call
the ERASE routine to wipe out memory
before the load. Otherwise, the load
starts at the cursor position,
performing an append, and we change the
command line to green ($C4, sorry about
not using a label) to warn the user. We
open the file for reading by passing a
4 to TOPEN, then at LOADLINK use GET
TEXT (command 7) to get no more than
the length of the text area. The actual
length loaded is found in ICBLEN, so we
add this to TEXSTART and the offset
between the cursor position and
TEXSTART to get the position of the
end-of-text (LASTLINE).
A funny thing happens, though. Up to
255 garbage characters appear following
an otherwise normal load, after the
end-of-text. I was never able to figure
out why (and I puzzled over it for a
week), so I wrote a stopgap routine to
just clear out one page past the
end-of-text. The bug is not fixed per
se, but it has no effect anymore! I
still think it must be the fault of the
operating system (I know...).
TLOAD SEC
LDA CURR
SBC TEXSTART
STA TEX
STA INDIR
LDA CURR+1
SBC TEXSTART+1
STA TEX+1
STA INDIR+1
ORA TEX
BEQ LOAD2
LDA #$C4
STA WINDCOLR
LOAD2 JSR TOPCLR
LDA # <LOADMSG
LDY # >LOADMSG
JSR PRMSG
LDA #4
JSR TOPEN
BPL OKLOD
GOERROR JMP ERROR
OKLOD LDA WINDCOLR
CMP #$C4
BEQ NOER
JSR ERASE
NOER JSR LOADLINK
CPY #128
BCC JFINE
JMP ERROR
JFINE JMP FINE
Entry point for linked files loading.
LOADLINK LDX IOCB
LDA CURR
STA ICBADR,X
LDA CURR+1
STA ICBADR+1,X
SEC
LDA TEXEND
SBC CURR
STA ICBLEN,X
LDA TEXEND+1
SBC CURR+1
STA ICBLEN+1,X
LDA #7
STA ICCOM,X
JSR CIO
BPL TEXOK
CPY #136
BEQ TEXOK
RTS
TEXOK LDX IOCB
CLC
LDA ICBLEN,X
ADC TEXSTART
STA LASTLINE
LDA ICBLEN+1,X
ADC TEXSTART+1
STA LASTLINE+1
CLC
LDA LASTLINE
ADC INDIR
STA LASTLINE
LDA LASTLINE+1
ADC INDIR+1
STA LASTLINE+1
JSR CASTOIN
LDA LASTLINE
STA TEX
LDA LASTLINE+1
STA TEX+1
LDA #0
TAY
NOGARBAGE STA (TEX),Y
INY
BNE NOGARBAGE
RTS
FINE JSR IOCLOSE
BPL PROKMSG
JMP ERROR
PROKMSG LDA #125
JSR CHROUT
LDA # <OKMSG
LDY # >OKMSG
JSR PRMSG
JMP ERXIT
Disable display-list interrupt and
restore screen colors.
DELITE LDA #$40
STA $D40E
LDA SCRCOL
STA 710
STA 712
LDA TEXCOLR
STA 709
RTS
A rather short routine that converts a
string of ASCII digits into a number in
hex and the accumulator. It takes
advantage of decimal mode. In decimal
mode, the accumulator is adjusted after
additions and subtractions so that it
acts like a two-digit decimal counter.
We shift BCD over a nybble and add in
the left nybble of the ASCII number
until we reach the end of the ASCII
number. We then subtract 1 from BCD and
increment X (which doesn't conform to
decimal mode) until BCD is down to 0.
The X register magically holds the
converted number. Naturally, decimal
mode is cleared before this routine
exits, or it would wreak major havoc.
ASCHEX is used to convert the
parameters of printer commands like
left margin.
ASCHEX LDX #0
STX BCD
STX BCD+1
STX HEX
STX HEX+1
DIGIT SEC
LDA (TEX),Y
SBC #16
BCC NONUM
CMP #10
BCS NONUM
ASL BCD
ROL BCD+1
ASL BCD
ROL BCD+1
ASL BCD
ROL BCD+1
ASL BCD
ROL BCD+1
ORA BCD
STA BCD
INY
BNE DIGIT
INC TEX+1
JMP DIGIT
NONUM SED
DECHEX LDA BCD
ORA BCD+1
BEQ DONENUM
SEC
LDA BCD
SBC #1
STA BCD
LDA BCD+1
SBC #0
STA BCD+1
INC HEX
BNE NOHEXINC
INC HEX+1
NOHEXINC JMP DECHEX
DONENUM LDA HEX
CLD
RTS
Insert the buffer. This is the recall
routine called by CTRL-R. It must not
allow an insertion that would overfill
memory. It calls DMOVE to open a space
in memory, then UMOVE (which is a
little faster than DMOVE) to copy the
buffer to the empty space.
INSBUFFER SEC
LDA TPTR
SBC TEXBUF
STA BUFLEN
LDA TPTR+1
SBC TEXBUF+1
STA BUFLEN+1
ORA BUFLEN
BNE OKBUFF
JSR TOPCLR
LDA # <INSMSG
LDY # >INSMSG
JSR PRMSG
LDA #1
STA MSGFLG
RTS
OKBUFF CLC
LDA CURR
STA FROML
ADC BUFLEN
STA DESTL
LDA CURR+1
STA FROMH
ADC BUFLEN+1
STA DESTH
SEC
LDA LASTLINE
SBC FROML
STA LLEN
LDA LASTLINE+1
SBC FROMH
STA HLEN
CLC
ADC DESTH
CMP TEXEND+1
BCC OKMOV
JSR TOPCLR
LDA # <INSERR
LDY # >INSERR
JSR PRMSG
LDA #1
STA MSGFLG
RTS
OKMOV JSR DMOVE
CLC
LDA BUFLEN
STA LLEN
ADC LASTLINE
STA LASTLINE
LDA BUFLEN+1
STA HLEN
ADC LASTLINE+1
STA LASTLINE+1
LDA CURR
STA DESTL
LDA CURR+1
STA DESTH
LDA TEXBUF
STA FROML
LDA TEXBUF+1
STA FROMH
JSR UMOVE
JMP CHECK
Exchange the character highlighted by
the cursor with the character to the
right of it. Not a vital command, but
it was included due to the brevity of
the code.
SWITCH LDY #0
LDA (CURR),Y
TAX
INY
LDA (CURR),Y
DEY
STA (CURR),Y
INY
TXA
STA (CURR),Y
RTS
Change the case of the character
highlighted by the cursor.
ALPHA LDY #0
LDA (CURR),Y
AND #63
CMP #33
BCC NOTALPHA
CMP #59
BCS NOTALPHA
LDA (CURR),Y
EOR #64
STA (CURR),Y
NOTALPHA JMP RIGHT
Convert internal (screen code) format
to Atari ASCII (ATASCII). Used to
convert the screen-code format of
SpeedScript documents to ASCII for the
sake of printing.
INTOAS PHA
AND #128
STA TEMP
PLA
AND #127
CMP #96
BCS XINT
INCONT CMP #64
BCC INT1
SBC #64
JMP XINT
INT1 ADC #32
XINT ORA TEMP
RTS
The start of the printer routines. This
part could logically be called a
separate program, but many variables
are common to the above code.
DEFTAB: Table of default settings for
left margin, right margin, page length,
top margin, bottom margin, etc. See the
table starting at LMARGIN at the end of
this source code.
DEFTAB .BYTE 5,75,66,5,58,1,1,1,0,1,0,80
Table of default printer codes.
PRCODES .BYTE 27,14,15,18
Another advantage of modular coding is
that you can change the behavior of a
lot of code by just changing one small
common routine. This is a substitute
for the normal CHROUT routine. It
checks to see if the current page
number equals the page number specified
by the user to start printing. It also
checks for the BREAK to abort the
printing and permits printing to be
paused with CTRL-1.
PCHROUT STA PCR
TXA
PHA
TYA
PHA
SEC
LDA PAGENUM
SBC STARTNUM
LDA PAGENUM+1
SBC STARTNUM+1
BCC SKIPOUT
LDA #1
STA 766
LDX #$70
LDA #0
STA ICBLEN,X
STA ICBLEN+1,X
LDA #11
STA ICCOM,X
LDA PCR
JSR CIO
PHP
LDA #0
STA 766
PLP
BPL SHIFTFREEZE
ERRLINK JSR ERROR
LDX #$FA
TXS
JMP MAIN
SHIFTFREEZE LDA $02FF ;CTRL-1
BNE SHIFTFREEZE
SKIPOUT PLA
TAY
PLA
TAX
LDA PCR
RTS
Displays "Printing..."
PRIN JSR TOPCLR
LDA # <PRINMSG
LDY # >PRINMSG
JMP PRMSG
PBORT JMP PEXIT
Called by CTRL-P. We get the filename
to print to (usually P:, although you
can use E: to print to the screen) with
ICAUX1 set to 8 for output. We exit on
any error. The DELITE routine turns off
the display-list interrupt, which might
otherwise interfere with output timing.
PRINT JSR TOPCLR
LDA # <FNMSG
LDY # >FNMSG
JSR PRMSG
JSR DELITE
LDA #8
JSR TOPEN
BPL PROK
JMP PEXIT
Reset several flags (footer length,
header length, true ASCII, underline
mode, and linefeed mode). Notice how
DELITE is called again. This isn't a
mistake. The first time we called
DELITE, we then may have opened a file
to the Editor device. This reset the
screen to the default colors, so the
second DELITE retains the user's true
color choice.
PROK JSR DELITE
JSR PRIN
LDX #0
STX FTLEN
STX HDLEN
STX NEEDASC
STX UNDERLINE
STX LINEFEED
Copy definition tables and default
printercodes.
COPYDEF LDA DEFTAB,X
STA LMARGIN,X
INX
CPX #12
BNE COPYDEF
LDA #$FF
STA LINE
STA NOMARG
LDX #4
COPYDEFS LDA PRCODES-1,X
STA CODEBUFFER+16,X
DEX
BNE COPYDEFS
Reentry point for printing after linked
files.
RETEX LDA TEXSTART
STA TEX
LDA TEXSTART+1
STA TEX+1
Main printing loop. We print the left
margin, grab a line of text, scan
backward until we find a space or a
carriage return, then break the line
there. If printer codes are
encountered, they're passed on to the
SPECIAL routine. Otherwise, we end up
calling BUFPRT to print the line and
process some other control codes.
PLOOP LDY #0
STY POS
CPY NOMARG
BEQ PLOOP1
LDA LMARGIN
STA POS
PLOOP1 LDA (TEX),Y
BPL NOTSP
JMP SPECIAL
NOTSP CMP #RETCHAR
BEQ FOUNDSPACE
NOTRET STA PRBUFF,Y
INY
INC POS
LDA POS
CMP RMARGIN
BCC PLOOP1
STY FINPOS
FINDSPACE LDA (TEX),Y
CMP #SPACE
BEQ FOUNDSPACE
DEC POS
DEY
BNE FINDSPACE
LDY FINPOS
FSPACE INY
LDA (TEX),Y
CMP #SPACE
BEQ FOUNDSPACE
DEY
FOUNDSPACE STY FINPOS
OVERSTOR TYA
SEC
ADC TEX
STA TEX
LDA TEX+1
ADC #0
STA TEX+1
LDY #0
If this is the first page, we need to print
the header, if any, with JSR TOP.
DOBUFF LDA LINE
CMP #$FF
BNE DOBUF2
JSR TOP
DOBUF2 LDA NOMARG
BEQ OVERMARG
JSR LMARG
OVERMARG SEC
ROL NOMARG
LDA FINPOS
STA ENDPOS
LDA # <PRBUFF
STA INDIR
LDA # >PRBUFF
STA INDIR+1
JSR BUFPRT
A line has been printed. We check to
see if we've hit the bottom margin and,
if so, go to PAGE, which goes to the
end of the page, prints the footer (if
any), and feeds to the next page.
ZBUFF JSR CRLF
LDA LINE
CMP BOTMARG
BCC NOTPAGE
JSR PAGE
Have we reached the end-of-text?
NOTPAGE SEC
LDA TEX
SBC LASTLINE
STA TEMP
LDA TEX+1
SBC LASTLINE+1
ORA TEMP
BEQ DORPT
BCC DORPT
If so, we check for a footer. If there
is one, we set HDLEN and TOPMARG to 0
(so that the printhead will end up at
the right place on the last page) and
call PAGE, which prints the footer. If
there is no footer, we leave the
printhead on the same page so that
paper isn't wasted.
LDA FTLEN
BEQ PXIT
LDA #0
STA HDLEN
STA TOPMARG
JSR PAGE
Exit routines. If screen output was
selected, we wait for a keystroke
before going back to editing mode.
PXIT LDA INBUFF
CMP #'E
BNE PEXIT
LDA #155
JSR CHROUT
LDA # <DIRMSG
LDY # >DIRMSG
JSR PRMSG
JSR GETAKEY
PEXIT JSR CLOSE7
LDX #$FA
TXS
JSR HIGHLIGHT
LDA #125
JSR CHROUT
JSR SYSMSG
JMP MAIN
DORPT JMP PLOOP
Paging routines. We skip (PAGELENGTH -
LINE) -- two blank lines to get to the
bottom of the page, print a footer (if
there is one) or a blank line (if not),
then page to the beginning of the next
page, skipping over the paper
perforation. If the wait mode is
enabled, we wait for the user to insert
a new sheet of paper.
PAGE SEC
LDA PAGELENGTH
SBC LINE
TAY
DEY
DEY
BEQ NOSK
BMI NOSK
NEXPAGE JSR CR
DEY
BNE NEXPAGE
NOSK LDA FTLEN
BEQ SKIPFT
STA ENDPOS
LDA # <FTBUFF
STA INDIR
LDA # >FTBUFF
STA INDIR+1
JSR LMARG
JSR BUFPRT
SKIPFT JSR CR
JSR CR
JSR CR
Increment the page number.
INC PAGENUM
BNE NOIPN
INC PAGENUM+1
The page wait mode is inappropriate
when printing to the screen or to disk,
or when skipping over pages with the ?
format command.
NOIPN LDA CONTINUOUS
BNE TOP
SEC
LDA PAGENUM
SBC STARTNUM
LDA PAGENUM+1
SBC STARTNUM+1
BCC TOP
JSR TOPCLR
LDA # <WAITMSG
LDY # >WAITMSG
JSR PRMSG
JSR GETAKEY
JSR PRIN
Print the header; skip to the top
margin.
TOP LDA HDLEN
BEQ NOHEADER
STA ENDPOS
LDA # <HDBUFF
STA INDIR
LDA # >HDBUFF
STA INDIR+1
JSR LMARG
JSR BUFPRT
NOHEADER LDY TOPMARG
STY LINE
DEY
BEQ SKIPTOP
BMI SKIPTOP
TOPLP JSR CR
DEY
BNE TOPLP
SKIPTOP RTS
Left margin routine. This routine is not
called if NOMARG is selected (margin
release).
LMARG LDA #32
LDY LMARGIN
STY POS
BEQ LMEXIT
LMLOOP JSR PCHROUT
DEY
BNE LMLOOP
LMEXIT RTS
CRLF is called at the end of most
printed lines. It increments the LINE
count and takes into account the
current line spacing mode set by the a
format command.
CRLF LDY SPACING
CLC
TYA
ADC LINE
STA LINE
CRLOOP JSR CR
DEY
BNE CRLOOP
RTS
CR just prints a single carriage return
and linefeed (if specified).
CR LDA #155
JSR PCHROUT
LDA LINEFEED
BEQ NOLF
JSR PCHROUT
NOLF RTS
Handle special printer codes like left
margin. This looks up the printer code
using a routine similar to CONTROL.
SPECIAL STA SAVCHAR
AND #127
JSR INTOAS
LDX SPTAB
SRCHSP CMP SPTAB,X
BEQ FSP
DEX
BNE SRCHSP
DEC POS
JMP DEFINE
FSP DEX
TXA
ASL A
TAX
STY YSAVE
LDA # >SPCONT-1
PHA
LDA # <SPCONT-1
PHA
LDA SPVECT+1,X
PHA
LDA SPVECT,X
PHA
RTS
After the format code is processed, we
must skip over the format command
and its parameter so that it's not
printed.
SPCONT SEC
LDA YSAVE
ADC TEX
STA TEX
LDA TEX+1
ADC #0
STA TEX+1
JMP PLOOP
If the format command ends with a
return-mark, we must skip over the
return-mark as well.
SPCEXIT LDA (TEX),Y
CMP #RETCHAR
BEQ NOAD
DEY
NOAD STY YSAVE
RTS
Special format code table. It starts with
the number of format commands, then
the characters for each format
command.
SPTAB .BYTE 17
.BYTE "wlrtbsnhf@p?xmigj"
The address-1 of each format routine.
SPVECT .WORD PW-1,LM-1,RM-1,TP-1
.WORD BT-1,SP-1,NX-1,HD-1,FT-1
.WORD PN-1,PL-1,SPAGE-1,ACROSS-1
.WORD MRELEASE-1,COMMENT-1,LINK-1
.WORD LFSET-1
m Margin release. INY is used to skip
over the format character.
MRELEASE INY
LDA #0
STA NOMARG
JMP SPCEXIT
x Columns across, used by centering.
ACROSS INY
JSR ASCHEX
STA PAGEWIDTH
JMP SPCEXIT
? Start printing at specified page.
SPAGE INY
JSR ASCHEX
STA STARTNUM
LDA HEX+1
STA STARTNUM+1
JMP SPCEXIT
@ Set starting default page number.
PN INY
JSR ASCHEX
STA PAGENUM
LDA HEX+1
STA PAGENUM+1
JMP SPCEXIT
p Page length.
PL INY
JSR ASCHEX
STA PAGELENGTH
JMP SPCEXIT
w Set page wait mode.
PW LDA #0
STA CONTINUOUS
INY
JMP SPCEXIT
j Set linefeed mode.
LFSET LDA #10
STA LINEFEED
INY
JMP SPCEXIT
l Left margin.
LM INY
JSR ASCHEX
STA LMARGIN
JMP SPCEXIT
r Right margin.
RM INY
JSR ASCHEX
STA RMARGIN
JMP SPCEXIT
t Top margin.
TP INY
JSR ASCHEX
STA TOPMARG
JMP SPCEXIT
b Bottom margin.
BT INY
JSR ASCHEX
STA BOTMARG
JMP SPCEXIT
s Set line spacing.
SP INY
JSR ASCHEX
STA SPACING
JMP SPCEXIT
n Jump to next page.
NX LDY YSAVE
INY
TYA
PHA
JSR PAGE
PLA
TAY
STY YSAVE
RTS
h Define header. Copy header into
header buffer.
HD JSR PASTRET
DEY
STY HDLEN
LDY #1
HDCOPY LDA (TEX),Y
STA HDBUFF-1,Y
INY
CPY HDLEN
BCC HDCOPY
BEQ HDCOPY
INY
JMP SPCEXIT
Skip just past the return-mark.
PASTRET INY
LDA (TEX),Y
CMP #RETCHAR
BNE PASTRET
RTS
f Define footer.
FT JSR PASTRET
DEY
STY FTLEN
LDY #1
FTCOPY LDA (TEX),Y
STA FTBUFF-1,Y
INY
CPY FTLEN
BCC FTCOPY
BEQ FTCOPY
JMP SPCEXIT
i Ignore a line of information.
COMMENT JSR PASTRET
JMP SPCEXIT
Define programmable printkeys. We
check for =. If not found, this is not an
assignment, so we just skip past the
code. Otherwise, we use the screen
code value as the index into the
CODEBUFFER and put the value there,
ready to be called during printing by
BUFPRT.
DEFINE INY
LDA (TEX),Y
CMP #'=-32
BEQ DODEFINE
DEY
LDA SAVCHAR
JMP NOTRET
DODEFINE INY
JSR ASCHEX
PHA
LDA SAVCHAR
AND #127
TAX
PLA
STA CODEBUFFER,X
JSR SPCEXIT
JMP SPCONT
g Link to next file. We get the
filename from text and put it into the
input buffer, just as if the filename
were typed in with INPUT. We then jump
into the TOPEN routine to open the
file, and into the Load routine to load
the file. After the load, we check for
a load error, then jump to RETEX to
continue printing.
LINK LDY #1
LDX #0
FNCOPY LDA (TEX),Y
CMP #RETCHAR
BEQ FNEND
JSR INTOAS
STA INBUFF,X
INY
INX
CPX #14
BNE FNCOPY
FNEND STX INLEN
LDA #0
STA INBUFF,X
LDX #$60
STX IOCB
LDA #4
STA ACCESS
JSR OPCONT
BPL LNOERR
JMP ERRLINK
LNOERR LDA #0
STA INDIR
STA INDIR+1
JSR ERASE
JSR LOADLINK
BPL LCONT
JMP ERRLINK
LCONT PLA
PLA
LDX #$70
STA IOCB
JMP RETEX
Global search and replace. This just
links together the search-specify
routine, the replace-specify routine,
then repeatedly calls Hunt and Replace,
until Hunt returns "Not Found." (FPOS+1
is $FF after a search failure.)
SANDR JSR RESET
LDA HUNTLEN
BEQ NOSR
JSR ASKREP
SNR JSR CONTSRCH
LDA FPOS+1
CMP #$FF
BEQ NOSR
JSR REPL
JSR REFRESH
JMP SNR
NOSR JMP SYSMSG
If OPTION is held down with CTRL-F, we
ask for and store the search phrase. If
OPTION is not down, we perform the
actual search. The line in the INBUFF
is compared with characters in text. If
at any point the search fails, we
continue the comparison with the first
character of INBUFF. The search is a
failure if we reach the end-of-text. If
the entire length of INBUFF matches,
the search succeeds, so we change the
CURRent cursor position to the found
position, save the found position for
the sake of the replace routine, then
call CHECK to scroll to the found
position.
HUNT LDA #8
STA 53279
LDA 53279
CMP #3
BNE CONTSRCH
RESET JSR TOPCLR
LDA # <SRCHMSG
LDY # >SRCHMSG
JSR PRMSG
JSR INPUT
STA HUNTLEN
BNE OKSRCH
JMP SYSMSG
OKSRCH LDY #0
TOBUFF LDA INBUFF,Y
STA HUNTBUFF,Y
INY
CPY INLEN
BNE TOBUFF
JMP SYSMSG
CONTSRCH LDA CURR
STA TEX
LDA CURR+1
STA TEX+1
LDA #$FF
STA FPOS+1
LDY #1
SRCH0 LDX #0
LDA HUNTLEN
BEQ NOTFOUND
SRCH1 LDA HUNTBUFF,X
JSR ASTOIN
CMP (TEX),Y
BEQ CY
CPX #0
BNE SRCH0
DEX
CY INY
BNE NOVFL
INC TEX+1
LDA TEX+1
CMP LASTLINE+1
BEQ NOVFL
BCS NOTFOUND
NOVFL INX
CPX HUNTLEN
BNE SRCH1
CLC
TYA
ADC TEX
STA TEMP
LDA TEX+1
ADC #0
STA TEMP+1
LDA LASTLINE
CMP TEMP
LDA LASTLINE+1
SBC TEMP+1
BCC NOTFOUND
SEC
LDA TEMP
SBC HUNTLEN
STA CURR
STA FPOS
LDA TEMP+1
SBC #0
STA CURR+1
STA FPOS+1
JSR CHECK
RTS
NOTFOUND JSR TOPCLR
LDA # <NFMSG
LDY # >NFMSG
JSR PRMSG
LDA #1
STA MSGFLG
RTS
The change (replace) routine checks to
see if OPTION is held down with CTRL-C.
If it is, we ask for a replace phrase,
and exit. If not, we check to see if
the cursor is at the position
previously located by the search
routine. If it is, we delete the found
phrase, then insert the replace phrase.
The cursor is moved past the replace
phrase for the sake of the next search.
This also prevents endless recursion,
as in replacing in with winner.
REPSTART LDA #8
STA 53279
LDA 53279
CMP #3
BNE REPL
ASKREP JSR TOPCLR
LDA # <REPMSG
LDY # >REPMSG
JSR PRMSG
JSR INPUT
STA REPLEN
BEQ NOREP
LDY #0
REPMOV LDA INBUFF,Y
STA REPBUFF,Y
INY
CPY INLEN
BNE REPMOV
NOREP JMP SYSMSG
REPL SEC
LDA CURR
STA DESTL
SBC FPOS
STA TEMP
LDA CURR+1
STA DESTH
SBC FPOS+1
ORA TEMP
BNE NOREPL
LDA #$FF
STA FPOS+1
CLC
LDA HUNTLEN
ADC CURR
STA FROML
LDA #0
ADC CURR+1
STA FROMH
SEC
LDA LASTLINE
SBC DESTL
STA LLEN
LDA LASTLINE+1
SBC DESTH
STA HLEN
JSR UMOVE
SEC
LDA LASTLINE
SBC HUNTLEN
STA LASTLINE
LDA LASTLINE+1
SBC #0
STA LASTLINE+1
LDA REPLEN
BEQ NOREPL
STA INSLEN
LDA #0
STA INSLEN+1
JSR INSBLOCK
LDY #0
REPLOOP LDA REPBUFF,Y
JSR ASTOIN
STA (CURR),Y
INY
CPY REPLEN
BNE REPLOOP
CLC
LDA CURR
ADC REPLEN
STA CURR
LDA CURR+1
ADC #0
STA CURR+1
NOREPL JMP CHECK
Suddenly, we're back to a PRINT
subroutine. This examines the buffer as
it's being printed, checking for
printkeys and Stage 2 commands like
centering.
BUFPRT LDY #0
BUFLP CPY ENDPOS
BEQ ENDBUFF
LDA (INDIR),Y
BMI SPEC2
JSR INTOAS
JSR PCHROUT
In underline mode, after we print the
character, we backspace the printhead
and print an underline character.
LDA UNDERLINE
BEQ NOBRK
LDA #8
JSR PCHROUT
LDA #95
JSR PCHROUT
NOBRK INY
JMP BUFLP
ENDBUFF RTS
Stage 2 format commands.
SPEC2 STY YSAVE
AND #127
STA SAVCHAR
JSR INTOAS
OTHER CMP #'c
BNE NOTCENTER
c Centering looks at the length of the
line, then sends out extra spaces (the
left margin has already been printed) to
move the printhead to the right place.
SEC
LDA PAGEWIDTH
SBC ENDPOS
LSR A
SEC
SBC LMARGIN
TAY
LDA #32
CLOOP JSR PCHROUT
DEY
BNE CLOOP
LDY YSAVE
JMP NOBRK
NOTCENTER CMP #'e
BNE NOTEDGE
e Edge right. This subtracts the length
of the line from the right margin
position and moves the printhead to
this position.
EDGE SEC
LDA RMARGIN
SBC ENDPOS
SEC
SBC LMARGIN
TAY
LDA #32
JMP CLOOP
NOTEDGE CMP #'u
BNE NOTOG
u Toggle underline mode. L
LDA UNDERLINE
EOR #1
STA UNDERLINE
NOTOG CMP #'#
BNE DOCODES
# Substitute the current page number
for the # symbol.
DOPGN STY YSAVE
LDX PAGENUM
LDA PAGENUM+1
JSR PROUTNUM
LDY YSAVE
JMP NOBRK
Do special format codes. This just uses
the screen-code value of the character
as an index into the CODEBUFFER, then
sends out the code. SpeedScript makes
no judgment on the code being sent out.
DOCODES LDX SAVCHAR
LDA CODEBUFFER,X
JSR PCHROUT
JMP NOBRK
Display free memory using OUTNUM.
FREEMEM JSR TOPCLR
SEC
LDA TEXEND
SBC LASTLINE
TAX
LDA TEXEND+1
SBC LASTLINE+1
JSR OUTNUM
LDA #1
STA MSGFLG
RTS
.END
Filename: DATA
Data tables
Messages are stored in ATASCII, with a
zero byte for a delimiter.
MSG1 .BYTE "SpeedScript 3.0"
.BYTE 0
MSG2 .BYTE " by Charles Brannon"
.BYTE 0
KILLMSG .BYTE "Buffer Cleared"
.BYTE 0
BUFERR .BYTE "Buffer Full"
.BYTE 0
DELMSG .BYTE "Delete (S,W,P)"
.BYTE 0
YMSG .BYTE ": Are you sure? (Y/N):"
.BYTE 0
CLRMSG .BYTE "ERASE ALL TEXT"
.BYTE 0
ERASMSG .BYTE "Erase (S,W,P): {inv}RETURN{inv} to exit"
.BYTE 0
SAVMSG .BYTE "Save (Device:Filename)>"
.BYTE 0
ERRMSG .BYTE "Error #"
.BYTE 0
BRMSG .BYTE "BREAK Key Abort"
.BYTE 0
OKMSG .BYTE "No Errors"
.BYTE 0
LOADMSG .BYTE "Load (Device:Filename)>"
.BYTE 0
DIRMSG .BYTE " Press {inv}RETURN{inv}"
.BYTE 0
DIRNAME .BYTE "D1:*.*"
INSERR .BYTE "Memory Full"
.BYTE 0
INSMSG .BYTE "No text in buffer"
.BYTE 0
FNMSG .BYTE "Print (Device:Filename)>"
.BYTE 0
PRINMSG .BYTE "Printing..."
.BYTE 155,155,0
WAITMSG .BYTE "Insert next sheet, press {inv}RETURN{inv}"
.BYTE 0
SRCHMSG .BYTE "Find:"
.BYTE 0
NFMSG .BYTE "Not found"
.BYTE 0
REPMSG .BYTE "Change to:"
.BYTE 0
The {ESC}'s represent the ESCape key.
The arrows are the cursor keys, which
must be preceded by ESC to be entered
into text. There is actually only one
space between the e of Rename and the
E of ESC.
DIRINS .BYTE "{ESC}{up}{ESC}{down}{ESC}{left}
{ESC}{right} {inv}CTRL-D{inv}elete
{inv}L{inv}ock {inv}U{inv}nlock
{inv}R{inv}ename {inv}ESC{inv}
{inv}CTRL-L{inv}oad Drive [
{inv}1{inv} {inv}2{inv}
{inv}3{inv} {inv}4{inv}
]: ",0
RENMSG .BYTE "Rename to:",0
FORMSG .BYTE "Format disk",0
The .OPT NO OBJ and .OPT OBJ pseudo-ops
turn on and off object code generation.
This insures that no object code is
generated for the variable table.
.OPT NO OBJ
TEXSTART *= *+2 ; Start-of-text area
TEXEND *= *+2 ; End-of-text area
TEXBUF *= *+2 ; Start of buffer
BUFEND *= *+2 ; End-of-buffer area
LENTABLE *= *+1 ; Length of first screen line
TOPLIN *= *+2 ; Home position in text
MSGFLG *= *+1 ; Messageflag
INSMODE *= *+1 ; Insertmode
ENDPOS *= *+1 ; Used by delete routines
FINPOS *= *+1 ; "
LASTLINE *= *+2 ; End-of-text position
LIMIT *= *+1 ; Used by INPUT
INLEN *= *+1 ; "
BOTSCR *= *+2 ; Bottom of screen in text
LBUFF *= *+40 ; Linebuffer (REFRESH)
INBUFF *= *+40 ; INPUT buffer
SAVCURR *= *+2 ; Used by delete routines
BCD *= *+2 ; Usedby ASCHEX
HEX *= *+2 ; "
TPTR *= *+2 ; Last character in buffer
BUFLEN *= *+2 ; Buffer length
GOBLEN *= *+2 ; Size of deleted text
FROMSAV *= *+2 ; Used by delete routines
DESTSAV *= *+2 ; "
HDLEN *= *+1 ; Header length
FTLEN *= *+1 ; Footer length
LMARGIN *= *+1 ; Holds left margin
RMARGIN *= *+1 ; Right margin
PAGELENGTH *= *+1 ; Pagelength
TOPMARG *= *+1 ; Topmargin
BOTMARG *= *+1 ; Bottom margin
SPACING *= *+1 ; Linespacing
CONTINUOUS *= *+1 ; Pagewait mode
PAGENUM *= *+2 ; Page number
STARTNUM *= *+2 ; Start printing at #
PAGEWIDTH *= *+1 ; Columns across
NOMARG *= *+1 ; Margin release flag
POS *= *+1 ; POSition within line
LINE *= *+1 ; Linecount
YSAVE *= *+1 ; Preserves Y register
SAVCHAR *= *+1 ; Preserves accumulator
INSLEN *= *+1 ; Length of an insertion
DEVNO *= *+1 ; Device number
NEEDASC *= *+1 ; True ASCII flag
UNDERLINE *= *+1 ; Underline mode flag
FPOS *= *+2 ; Found position
PCR *= *+1 ; Usedby PCHROUT
HUNTLEN *= *+1 ; Length of hunt phrase
HUNTBUFF *= *+30 ; Holds hunt phrase
REPLEN *= *+1 ; Length of replacephrase
REPBUFF *= *+30 ; Holds replace phrase
CODEBUFFER *= *+128 ; Holds definable printkeys
PRBUFF *= *+256 ; Printer line buffer
HDBUFF *= *+256 ; Holds header
FIRSTRUN *= *+1 ; Has program been run before?
FTBUFF *= *+256 ; Holds footer
SAVCOL *= *+1 ; Save SCRCOL
LINEFEED *= *+1 ; Linefeed mode flag
ESCFLAG *= *+1 ; Was ESC pressed?
CONVFLAG *= *+1 ; Used by CAST and CINTOAS
SELFLAG *= *+1 ; The SELECT key flag
IOCB *= *+1 ; Which IOCB is OPEN
ACCESS *= *+1 ; Direction of ACCESS (read/write)
FNBUFF *= *+40 ; Filename buffer
FNLEN *= *+1 ; Filename Length
XSLOT *= *+1 ; Number of filename slots (DOSPAK)
SLOT *= *+130 ; Slot positions (DOSPAK)
XPTR *= *+1 ; Current filename slot (DOSPAK)
WHICHFLAG *= *+1 ; Which key is pressed
DIRCOUNT *= *+1 ; Directory count
BLINK *= *+1 ; Cursor blink flag
LINELEN *= *+1 ; Length of screen lines
RLM *= *+1 ; REFRESH Left margin value
KEYVAL *= *+1 ; Which key is pressed
END = * ; High byte of this +$100 is TEXSTART
.OPT OBJ
Autorun vector
*= $02E2
.WORD BEGIN
.END
Label Cross Reference. This chart makes it easier to find your place in the object code while looking at the source code. The number to the left of each label is its value or position within the object code. Labels preceded by an = mark are equates. Others are internal labels for object code positions.
43BA ACCESS 3A2E ACROSS 294B ADYCURR 3721 ALPHA 3614 ASCHEX 3C3C ASKREP 262C ASTOIN 3FCD BCD 1FOO BEGIN 446A BLINK 3029 BLIP = 0074 BLUE 2983 BORDER 33AA BOTCLR 3FEI BOTMARG 3F79 BOTSCR 24D3 BREAK 3E3A BRMSG 3A92 BT 3F6C BUFEND 3DBB BUFERR 3FD3 BUFLEN 3CDE BUFLP 3CDC BUFPRT 33FF CAST 340C CAST1 3419 CASTOIN 3100 CATALOG 27CF CHECK 282D CHECK2 2F7F CHROUT 2FCB CHRYSAVE 3426 CIN 3407 CINTOAS = E456 CIO 284C CK3 2D02 CLEAR 2501 CLEARED 28E2 CLEFT 3DIF CLOOP 3184 CLOSE7 254C CLR2 24F4 CLRLN 2543 CLRLOOP 3DED CLRMSG 4033 CODEBUFFER 3AFO COMMENT 3FE3 CONTINUOUS 2732 CONTROL 3BB7 CONTSRCH 43B7 CONVELAG 24D7 COPY 3296 COPYD 37EC COPYDEF 3801 COPYDEFS 32A3 COPYNAME 3332 COPYR 3446 COTHER 3998 CR 2SAF CRIGHT 3986 CRLF 3991 CRLOOP 2753 CTBL = 0086 CURR 2EE7 CURSIN 3430 CVLOOP 3BDC CY 3282 DDOWN 3654 DECHEX 3AF6 DEFINE 3752 DEFTAB 2A6C DELI 2A80 DELlA 2A89 DEL2 2A7D DELABORT 2AAO DELC 2B32 DELCHAR 2B5C DELETE 32E5 DELFILE 2B4D DELIN 35FF DELITE 3DC7 DELMSG 288A DELSENT 2B7D DELWORD = 0083 DESTH = 0082 DESTL 3FD9 DESTSAV 3FEF DFVNO 3622 DIGIT 4469 DIRCOUNT 3F00 DIRINS 3156 DIRLOOP 3E6C DIRMSG 3E7A DIRNAME 25E9 DISKBOOT 3247 DLEFT 2EOA DLI 2DEE DUST 2DC8 DLOOP 2474 DMOVI 244D DMOVE 2476 DMOVLOOP 3170 DNOT8 315B DNOTCR 3873 DOBUF2 3869 DOBUFF 3D62 DOCODIIS 3B04 DODEFINE 26F4 DOINS 2D25 DOLT 3678 DONENUM 3D50 DOPGN 38EB DORPT 3IBB DOS 3229 DOSADR 33B6 DOSERR 33FF DOSMSG 3219 DOSTABLE 3254 DRIGHT 336F DRIVE 3271 DUP 2BD9 EATSPACE 3D2F EDGE 2F99 ENAME = 446E END 31300 ENDBUFF 3194 ENDIR 319E ENDLP 3F73 ENDPOS 2967 ENDTEX 2814 EQA 2E97 ERAI 2EA6 ERA2 2E33 ERAS 2E42 ERAS1 2E4C ERASAGAIN 251F ERASE 2E7B ERASENT 3DFC ERASMSG 2E6E ERASWORD 34A7 ERR1 379B ERRLINK 3E32 ERRMSG 34AE ERROR 34C7 ERXIT 27BD ESC 3399 ESCDOS 43B6 ESCFLAG 2F2E ESCKEY 2C75 FILLSP 3201 FINDIT 3841 FINDSPACE 35E8 FINE 3F74 FINPOS 4283 FLRSTRUN 29F4 FIRSTWORD 2B3B FIXTP 2687 FLIPIT 43BB FNBUFF 3B1D FNCOPY 3B2F FNEND 43E3 FNLEN 3E9E FNMSG 3355 FORMAT 3F5A FORMSG 2740 FOUND 320C FOUNDIT 3858 FOUNDSPACE 3FF2 FPOS 3D6E FREEMEM = 0081 FROMH 0080 FROML 3FD7 FROMSAV 39BF FSP 3850 FSPACE 3AD7 FT 42B4 FTBUFF 3AEO FTCOPY 3FDC FTLEN 31A9 GET7 256F GETAKEY 2FD6 GETCHAR 2FCC GETIN 31D6 GETNAME 2D4F GOADY 3FD5 GOBLEN 3565 GOERROR 2C06 GOINC 2FC1 GOPCHR 2AD4 GOSAV 32EA GOXIO 3001 GXIT 3AB5 HD 4183 HDBUFF 3ABE HDCOPY 3FDB HDLEN 3FCF HEX 2DAD HIGHLIGHT = 0085 HLEN 2BAI HOME 2BC3 HOMEPAUSE 3B85 HUNT 3FF6 HUNTBUFF 3FF5 HUNTLEN = 034A ICAUX1 = 034B ICAUX2 = 0344 ICBADR = 0348 ICBLEN = 0342 ICCOM = 0343 ICSTAT 3FA3 INBUFF 250C INCNOT 3744 INCONT = 008E INDIR 2F4D INEXIT 2589 INIT 25EA INIT2 2726 INKURR 3F78 INLEN 2CEI INOUT 2ED5 INP1 2ECE INPUT 2868 INRANGE 2C92 INSBLOCK 367D INSBUFFER 2C7C INSCHAR 3E80 INSERR 3FEE INSLEN 3F72 INSMODE 3E8C INSMSG 2CE2 INSTGL 374D INTl 3738 INTOAS 33E5 INVLP 33E3 INVNAME 3B9 IOCB 34E0 IOCLOSE 2575 JDOS 357B JFINE 32F2 JNAME 3040 KEYBOARD 2694 KEYPRESS 446D KEYVAL 2A50 KILLBUFF 3DAC KILLMSG 3F75 LASTLINE 295A LAST WORD 3F7B LBUFF 3B5A LCONT 28B8 LEFT 3F6E LENTABLE 298B LETTERS 3A68 LFSET 3F77 LIMIT 3FEB LINE 43B5 LINEFEED 446B LINELEN 3B19 LINK = 0084 LLEN 3A74 LM 3975 LMARG 3FDD LMARGIN 3985 LMEXIT 397F LMLOOP 3B49 LNOERR 3554 LOAD2 3377 LOADIT 357E LOADLINK 3E54 LOADMSG 32F8 LOCK 2C4C LOTTASPACE 2645 LOWR 2648 MAIN 2651 MAIN2 2428 MOV1 242A MOV2 242F MOVLOOP 3A25 MRELEASE 3D88 MSG1 3D98 MSG2 3F71 MSGFLG 31E5 NAMELP 3294 NAMER 3FFO NEEDASC 38FC NEXPAGE 3EEB NFMSG 39ED NOAD 2F1B NOBACK 28AC NOBIGGER 2583 NOBLINK 3CFC NOBRK 28E8 NODEC 3571 NOER 2F04 NOESC 2A37 NOFIXCURR 35E2 NOGARBAGE 3963 NOHEADER 3675 NOHEXINC 272C NOINC2 28B5 NOINCR 2C63 NOINCY 3929 NOIPN 39A5 NOLF 3FE9 NOMARG 26B8 NOMSG 334F NONAME 3653 NONUM 2E91 NOPAR 31FF NOPROB 3C5C NOREP 3CD9 NOREPL 3902 NOSK 3B82 NOSR 32B2 NOSTOR 3735 NOTALPHA 26AC NOTBKS 300F NOTCAPS 3D2B NOTCENTER 26C2 NOTCR 2642 NOTCTRL 32BF NOTDOT 3D40 NOTEDGE 3C20 NOTFOUND 26F7 NOTINST 3024 NOTLOCKED 2455 NOTNULL 3D4C NOTOG 389E NOTPAGE 2BAO NOTPAR 344F NOTRC 382F NOTRET 3440 NOTRTN 26A6 NOTSEL 2B93 NOTSENT 3004 NOTSET 382B NOTSP 2B86 NOTWORD 2F13 NOTZERO 3BEA NOVFL 2E77 NOWORD 326E NRANGE 3AA6 NX 24C7 NXCUR 2941 OIDS 27FF OK1 282C OK2 36A5 OKBUFF 2DOF OKCLEAR 2CA9 OKINS 3568 OKLOD 36DF OKMOV 3E4A OKMSG 3BA6 OKSRCH 27C6 ONOFF 2FCA ONUMEXIT 2FBI ONUMLOOP 3508 OPABORT 3510 OPCONT 2F59 OPENEDITOR 3DOC OTHER 244C OUT 2BC9 OUTHOME 2FAO OUTNUM 287A OUTRANGE 2COB OUTSPACE 26E2 OVERCTRL 387B OVERMARG 3452 OVEROTHER 2FC4 OVERPCHR 385B OVERSTOR 2FA2 OVERZAP 38EE PAGE 3FDF PAGELENGTH 3FE4 PAGENUM 3FE8 PAGEWIDTH 2D64 PARCONT 2D31 PARIGHT 2D52 PARLEFT 2D5E PARLOOP 2D33 PARLP 3ACF PASTRET 37BB PBORT 3762 PCHROUT 3FF4 PCR 2514 PDONE 38D7 PEXIT 3A58 PL 24B1 PLINE 3814 PLOOP 3824 PLOOPI 29A7 PMANY 3A48 PN 3FEA POS 24AF PPAGE 40B3 PRBUFF 375E ERCODES 256E PREXIT 37B1 PRIN 3EB7 PRINMSG 37BE PRINT 2564 PRLOOP 2559 PRMSG 37D5 PROK 35F0 PROKMSG 2F9B PROUTNUM 29BD PSLOOP 29B9 PSRCH 29DE PUNCT 2A26 PUNCT2 3A62 PW 38C1 PXIT = 0032 RED 312D REDIR 2826 REF 248B REFRESH 3308 RENAME 3F4F RENMSG 4015 REPBUFF 29EF REPEAT 3C5F REPL 4014 REPLEN 3USD REPLOOP 3C50 REPMOV 3EF5 REPMSG 3C30 REPSTART 3B91 RESET 3261 RESLOT = 005E RETCHAR 380A RETEX 2D75 RETF2 2D4A RETFOUND 2885 RIGHT 446C RLM 2925 RLOOP 3A7E RM 3FDE RMARGIN 2933 ROUT 297A SAFE 3B64 SANDR 3FED SAVCHAR 43B4 SAVCOL 3FCB SAVCURR 3E1A SAVMSG 24D2 SBRK = 0088 SCR 298A SCRCOL 43B8 SELFLAG = O2BE SHFLOK 37A4 SHIFTFREEZE 2481 SKIPDMOV 1F34 SKIPERAS 3918 SKIPFT 2448 SKIPMOV 37A9 SKIPOUT 2F3E SKIPSEL 29E2 SKIPSPC 3974 SKIPTOP 299B SLEFT 24C3 SLOOP 43E5 SLOT 3036 SLOW 3031 SNDLOOP 3B6F SNR 3A9C SP = 0000 SPACE 3FE2 SPACING 3A38 SPACE 39E6 SPCEXIT 39D5 SPCONT 2BE7 SPCSRCH 3D01 SPEC2 39A6 SPECIAL 39F1 SPTAB 3A03 SPVECT 2735 SRCH 3BC6 SRCHO 3BCD SRCH1 3EE5 SRCHMSG 39B1 SRCHSP 2A23 SREXIT 2A01 SRIGHT 2A03 SRLP 3FE6 STARTNUM 34D3 STOPPED 28F9 STRIP 2903 STRLOOP 37B1 SWITCH 260A SYSMSG 2C54 TAB 2C66 TAB2 = 008C TEMP = 008A TEX 3F6A TEXBUF 299A TEXCOLR 3F68 TEXEND 35AB TEXOK 3F66 TEXSTART 2DA2 TEXTOCURR 3178 THROWS 3539 TLOAD 3BA8 TOBUFF 28DF TOOSMALL 394D TOP 261A TOPCLR 34EB TOPEN 2BCC TOPHOME 3F6F TOPLIN 261E TOPLOOP 396E TOPLP 3FEO TOPMARG 3A88 TP 3FD1 TPTR 345D TSAVE 2410 UMOVE = 0090 UNDERCURS 3FF1 UNDERLINE 3300 UNLOCK 2E84 UNSENT 2777 VECT 2667 WAIT 3EC5 WAITMSG 2BBC WAITST 4468 WHICHFLAG = 0091 WINDCOLR 28ED WLEFT 2906 WLOOP 2923 WRIGHT 2914 WROUT 2957 WRTN 374F XINT 32C8 XIO 4467 XPTR 43E4 XSLOT 3DD6 YMSG 2CEB YORN 2CF2 YORNKEY 3FEC YSAVE 3890 ZBUFF
Return to Table of Contents | Previous Chapter

