8

Building A Program


 Using what we've learned so far, and adding a couple of new techniques, let's build a useful program. This example will demonstrate many of the techniques we've discussed and will also show some of the thought processes involved in writing ML.

     Among the computer's more impressive talents is searching. It can run through a mass of information and find something very quickly. We can write an ML routine which looks through any area of memory to find matches with anything else. If your BASIC doesn't have a FIND command or its equivalent, this could come in handy. Based on an idea by Michael Erperstorfer published in COMPUTE! Magazine, this ML program will report the line numbers of all the matches it finds.

Safe Havens

Before we go through some typical ML program-building methods, let's clear up the "where do I put it?" question. ML can't just be dropped anywhere in memory. When the Simple Assembler asks "Starting Address?", you can't give it any address you want to. RAM is used in many ways. There is always the possibility that a BASIC program might be residing in part of it (if you are combining ML with a BASIC program). Or BASIC might use part of RAM to store arrays or variables. During execution, these variables might write (POKE) into the area that you placed your ML program, destroying it. Also, the operating system, the disk operating system, cassette/disk loads, printers - they all use parts of RAM for their activities. There are other things going on in the computer beside your hard-won ML program.

     Obviously, you can't put your ML into ROM addresses. That's impossible. Nothing can be POKEd into those addresses. The 64 is an exception to this. You can POKE into ROM areas because a RAM exists beneath the ROM. Refer to the Programmer's Reference Guide or see Jim Butterfield's article on 64 architecture (COMPUTE! Magazine, January 1983) for details.

     Where to put ML? There are some fairly safe areas.

     If you are using Applesoft in ROM, 768 to 1023 ($0300 to $03FF) is safe. Atari's page six, 1536 to 1791 ($0600 to $06FF) is good. The 64 and VIC's cassette buffer at 828 to 1019 ($033C to $03FB) are good if you are not LOADing or SAVEing from tape.

     The PET/CBM makes provision for a second cassette unit. In theory, it would be attached to the computer to allow you to update files or make copies of programs from Cassette #1 to Cassette #2. In practice, no one has mentioned finding a use for a second cassette drive. It is just as easy to use a single cassette for anything that a second cassette could do. As a result, the buffer (temporary holding area) for bytes streaming in from the second cassette unit is very safe indeed. No bytes ever flow in from the phantom unit so it is a perfect place to put ML.

     The "storage problem" can be solved by knowing the free zones, or creating space by changing the computer's understanding of the start or end of BASIC programs. When BASIC is running, it will set up arrays and strings in RAM memory. Knowing where a BASIC program ends is not enough. It will use additional RAM. Sometimes it puts the strings just after the program itself. Sometimes it builds them down from the "top of memory," the highest RAM address. Where are you going to hide your ML routine if you want to use it along with a BASIC program? How are you going to keep BASIC from overwriting the ML code?

Misleading The Computer

If the ML is a short program you can stash it into the safe areas listed above. Because these safe areas are only a couple of hundred bytes long, and because so many ML routines want to use that area, it can become crowded. Worse yet, we've been putting the word "safe" in quotes because it just isn't all that reliable. Apple uses the "safe" place for high-res work, for example. The alternative is to deceive the computer into thinking that its RAM is smaller than it really is. This is the real solution.

     Your ML will be truly safe if your computer doesn't even suspect the existence of set-aside RAM. It will leave the safe area alone because you've told it that it has less RAM than it really does. Nothing can overwrite your ML program after you misdirect your computer's operating system about the size of its RAM memory. There are two bytes in zero page which tell the computer the highest RAM address. You just change those bytes to point to a lower address.

     These crucial bytes are 55 and 56 ($37,38) in the 64 and VIC. They are 52,53 ($34,35) in PET/CBM Upgrade and 4.0 BASIC. In the PET with Original ROM BASIC, they are 134,135 ($86,87). The Apple uses 115,116 ($73,74), and you lower the Top-of-BASIC pointer just as you do in Commodore machines.

     The Atari does something similar, but with the bottom of RAM. It is easier with the Atari to store ML just below BASIC than above it. Bump up the "lomem" pointer to make some space for your ML. It's convenient to start ML programs which are too long to fit into page six ($0600-06FF) at $11700 and then put this address into lomem. The LSB and MSB are reversed, of course, as the 6502 requires its pointers to be like this:

 $02E7 00
 $02E8 1F

     $02E7,8 is Atari's low memory pointer. You should set up this pointer (LDA $00, STA $02E7, LDA #$117, STA $02E8) as part of your ML program. Following that pointer setup, JMP $A000 which initializes BASIC. If you are not combining ML with a BASIC program, these preliminary steps are not necessary.

     Safe Atari zero page locations include $00-04, $CB-D0, $D4-D9 (if floating point numbers are not being used); $0400 (the printer and cassette buffer), $0500-05717 (free), $0580-05FF (if floating point and the Editor are not being used), $0600-06FF (free) are also safe. No other RAM from $0700 (Disk Operating System) to $9FFF or $BFFF is protected from BASIC.

     To repeat: address pointers such as these are stored in LSB, MSB order. That is, the more significant byte comes second (this is the reverse of normal, but the 6502 requires it of address pointers). For example, $8000, divided between two bytes in a pointer, would look like this:

 0073 00
 0074 80

     As we mentioned earlier, this odd reversal is a peculiarity of the 6502 that you just have to get used to. Anyway, you can lower the computer's opinion of the top-of-RAM-memory, thereby making a safe place for your ML, by changing the MSB. If you need one page (256 bytes): POKE 116, PEEK (116)-1 (Apple). For four pages (1024 bytes) on the Upgrade and 4.0 PETs: POKE 53, PEEK (53) -4. Then your BA or start of assembling could begin at (Top-of-RAM-255 or Top-of-RAM-1023, respectively. You don't have to worry much about the LSB here. It's usually zero. If not, take that into account when planning where to begin storage of your object code.


Program 8-1. PET Search (4.0 BASIC Version).


0010
; SEARCH THROUGH BASIC

0015
; PET 4.0 VERSION

0016
;-----------------------------------------------------

0017
; -0-    DEFINE VARIABLES BY GIVING THEM LABELS.

0018
;


0020
L1L
.DE $BA      ;STORE THESE IN

0030
L2L
.DE $BC      ;UNUSED ZERO PG AREA

0040
FOUND
.DE $36

0050
BASIC
.DE $0400

0060
PRINT
.DE $FFD2    ; PRINT A CHAR.

0070
PLINE
.DE $CF7F    ; PRINT LINE#

0100

.BA $0360    ; 2ND CASSETTE BUFFER

0110

.OS

0120
;------------------------------------------------------

0121
; -0- INITIALIZE POINTERS.

0130
;


0140

;
0360- AD 01 04
0150

LDA BASIC+1  ;GET ADDR OF NEXT
0363- 85 BA
0160

STA *L1L     ;BASIC LINE
0365- AD 02 04
0170

LDA BASIC+2
0368- 85 BB
0180

STA *L1L+l

0181
;------------------------------------------------------

0182
; -0- SUBROUTINE TO CHECK FOR 2 ZEROS. IF WE DON'T

0183
; FIND THEM, WE ARE NOT AT THE END OF THE PROGRAM.

0184
;

036A- A0 00
0190
READLINE
LDY #$00
036C- Bl BA
0200

LDA (L1L),Y
036E- D0 06
0210

BNE GO.ON     ; NOT END OF LINE
0370- C8
0220

INY
0371- Bl BA
0230

LDA (L1L),Y   ; 00 00 = END OF PROG.
0373- D0 01
0240

BNE GO.ON
0375- 60
0250
END
RTS           ;RETURN TO BASIC

0251
;------------------------------------------------------

0252
; -0- SUBROUTINE TO UPDATE POINTERS TO THE NEXT LINE

0253
;  AND STORE THE CURRENT LINE NUMBER IN CASE WE

0254
;  FIND A MATCH AND NEED TO PRINT THE LINE #.

0255
;  ALSO, WE ADD 4 TO THE CURRENT LINE POINTER SO THAT

0256
;  WE ARE PAST THE LINE # AND "POINTER-TO-NEXT-LINE"

0257
;  INFORMATION. WE ARE THEN POINTING AT THE 1ST CHAR.

0258
;  IN THE CURRENT LINE AND CAN COMPARE IT TO THE SAMPLE.

0259
;
0376- A0 00
0260
GO.ON
LDY #$00
0378- Bl BA
0270

LDA (L1L),Y     ; GET NEXT LINE
037A- 85 BC
0280

STA *L2L        ; ADDRESS AND
037C- C8
0290

INY             ; STORE IT IN L2L
037D- Bl BA
0300

LDA (L1L),Y
037F- 85 BD
0310

STA *L2L+1
0381- C8
0320

INY
0382- B1 BA
0330

LDA (L1L),Y     ; PUT LINE #
0384- 85 36
0340

STA *FOUND      ; IN STORAGE TOO
0386- C8
0350

INY             ; IN CASE IT
0387- Bl BA 
0360

LDA (L1L),Y     ; NEEDS TO BE
0389- 85 37
0370

STA *FOUND+l    ;PRINTED OUT LATER
038B- A5 BA
0380

LDA *L1L
038D- 18
0390

CLC    ; MOVE FORWARD TO 1ST
038E- 69 04
0400

ADC #$04        ; PART OF BASIC TEXT
0390- 85 BA
0410

STA *L1L        ; (PAST LINE # AND
0392- A5 BB
0420

LDA *L1L+1      ; OF NEXT LINE)
0394- 69 00
0430

ADC #$00
0396- 85 BB
0440

STA *L1L+1

0441
;------------------------------------------------------

0442
; -0- SUBROUTINE TO CHECK FOR ZERO (LINE IS FINISHED?)

0443
;  AND THEN CHECK 1ST CHARACTER IN BASIC LINE AGAINST

0444
;  1ST CHARACTER IN SAMPLE STRING AT LINE 0:. IF THE

0445
;  1ST CHARACTERS MATCH, WE MOVE TO A FULL STRING

0446
;  COMPARISON IN THE SUBROUTINE CALLED "SAME." IF 1ST

0447
;  CHARS. DON'T MATCH, WE RAISE THE "Y" COUNTER AND

0448
;  CHECK FOR A MATCH IN THE 2ND CHAR. OF THE CURRENT

0449
;  BASIC LINE'S TEXT.

0450
;
0398- A0 00
0451

LDY #$00
039A- Bl BA
0460
LOOP
LDA (L1L),Y
039C- F0 1C
0470

BEQ STOPLINE    ; ZERO = LINE FINISHED
039E- CD 06 04
0480

CMP BASIC+6     ; SAME AS 1ST SAMPLE CHAR?
03Al- F0 04
0490

BEQ SAME        ; YES? CHECK WHOLE STRING
03A3- C8
0500

INY             ; NO? CONTINUE SEARCH
03A4- 4C 9A 03
0510

JMP LOOP

0511
;------------------------------------------------------

0512
; -0- SUBROUTINE TO LOOK AT EACH CHARACTER IN BOTH THE

0513
;   SAMPLE (LINE 0) AND THE TARGET (CURRENT LINE) TO

0514
;   SEE IF THERE IS A PERFECT MATCH. Y KEEPS TRACK OF

0515
;   TARGET. X INDEXES SAMPLE. IF WE FIND A MISMATCH

0516
;   BEFORE A LINE-END ZERO, WE FALL THROUGH TO LINE

0517
;   590 AND JUMP BACK UP TO 460 WHERE WE CONTINUE ON

0518
;   LOOKING FOR 1ST CHAR MATCHES IN THE CURRENT LINE.

0519
;

03A7- A2 00
0520
SAME
LDX #$00      ;COMPARE SAMPLE TO
03A9- E8
0530
COMPARE
INX           ;TARGET
03AA- C8
0540

INY
03AB- BD 06 04
0550

LDA BASIC+6,X
03AE- F0 07
0560

BEQ PERFECT   ; LINE ENDS SO PRINT
03B0- D1 BA
0570

CMP (L1L),Y
03B2- F0 F5
0580

BEQ COMPARE   ; CONTINUE COMPARE
03B4- 4C 9A
0590

JMP LOOP      ;NO MATCH
03B7- 20 C5 03
0600
PERFECT
JSR PRINTOUT

0601
;------------------------------------------------------

0602
; -0- SUBROUTINE TO REPLACE "CURRENT LINE" POINTER

0603
; WITH THE "NEXT LINE" POINTER WE SAVED IN THE SUBROUT

0604
; STARTING AT LINE 260.

0605
; THEN JUMP BACK. TO THE START WITH THE CHECK FOR THE

0606
; END-OF-PROGRAM DOUBLE ZERO. THIS IS THE LAST SUBROUT

0607
; IN THE MAIN LOOP OF THE PROGRAM

0608
;

03BA- A5 BC
0610
STOPLINE
LDA *L2L      ;TRANSFER NEXT LINE
03BC- 85 BA
0620

STA *L1L      ; ADDRESS POINTER TO
03BE- A5 BD
0630

LDA *L2L+1    ; CURRENT LINE POINTER
03C0- 85 BB
0640

STA *L1L+l    ; TO GET READY TO READ
03C2- 4C 6A 03
0650

JMP READLINE  ;THE NEXT LINE.

0651
;------------------------------------------------------

0652
; -0- SUBROUTINE TO PRINT OUT A BASIC LINE NUMBER.

0653
;   IN MICROSOFT IT TAKES THE NUMBER STORED IN $36,37

0654
;   AND THE ROM ROUTINE PRINTS THE NUMBER AT THE NEXT

0655
;   CURSOR POSITION ON SCREEN. THEN WE PRINT A BLANK

0656
;   SPACE AND RETURN TO LINE 610 TO CONTINUE ON WITH

0657
;   THE MAIN LOOP AND FIND MORE MATCHES.

0658
;

03C5- 20 7F CF
0660
PRINTOUT
JSR PLINE     ; ROM ROUTINE PRINTS

0661

; A LINE NUMBER FROM THE VALUES FOUND

0662

; IN "FOUND" ($36,37)
03C8- A9 20
0670

LDA #$20      ;PRINT A BLANK
03CA- 20 D2 FF
0680

JSR PRINT     ; SPACE BETWEEN #S
03CD- 60
0690

RTS

0691
;------------------------------------------------------

0692
;


0700

.EN


--- LABEL FILE: ---

BASIC =0400
COMPARE =03A9
END =0375
FOUND =0036
GO.ON =0376
L1L =00BA
L2L =00BC
LOOP =039A
PERFECT =03B7
PLINE =CF7F
PRINT =FFD2
PRINTOUT =03C5
READLINE =036A
SAME =03A7
STOPLINE =03BA

Building The Code

Now we return to the subject at hand - building an ML program. Some people find it easiest to mentally break a task down into several smaller problems and then weave them into a complete program. That's how we'll look at our search program. (See Program 8-1.)

     For this exercise, we can follow the PET/CBM 4.0 BASIC version to see how it is constructed. All the versions (except Atari's) are essentially the same, as we will see in a minute. The only differences are in the locations in zero page where addresses are temporarily stored, the "start-of-BASIC RAM" address, the routines to print a character and to print a line number, and the RAM where it's safe to store the ML program itself. In other words, change the defined variables between lines 20 and 100 in Program 8-1 and you can use the program on another computer.

     We will build our ML program in pieces and then tie them all together at the end. The first phase, as always, is the initialization. We set up the variables and fill in the pointers. Lines 20 and 30 define two, two-byte zero page pointers. L1L is going to point at the address of the BASIC line we are currently searching through. L2L points to the starting address of the line following it.

     Microsoft BASIC stores four important bytes just prior to the start of the code in a BASIC line. Take a look at Figure 8-1. The first two bytes contain the address of the next line in the BASIC program. The second two bytes hold the line number. The end of a BASIC line is signaled by a zero. Zero does not stand for anything in the ASCII code or for any BASIC command. If there are three zeros in a row, this means that we have located the "top," the end of the BASIC program. (The structure of Atari BASIC is significantly different. See Figure 8-2.)

     But back to our examination of the ML program. In line 40 is a definition of the zero page location which holds a two-byte number that Microsoft BASIC looks at when it is going to print a line number on the screen. We will want to store line numbers in this location as we come upon them during the execution of our ML search program. Each line number will temporarily sit waiting in case a match is found. If a match is found, the program will JSR to the BASIC ROM routine we're calling "PLINE," as defined in line 70. It will need the "current line number" to print to the screen.

     Line 50 establishes that BASIC RAM starts at $0400 and line 60 gives the address of the "print the character in the accumulator" ROM routine. Line 100 says to put the object code into the PET's (all BASIC versions) second cassette buffer, a traditional "safe" RAM area to store short ML programs. These safe areas are not used by BASIC, the operating system (OS), or, generally, by monitors or assemblers. If you are working with an assembler or monitor, however, and keep finding that your object code has been messed up - suspect that your ML creating program (the monitor or assembler) is using part of your "safe" place. They consider it safe too. If this should happen, you'll have to find a better location.

     Refer to Program 8-1 to follow the logic of constructing our Microsoft search program. The search is initiated by typing in line zero followed by the item we want to locate. It might be that we are interested in removing all REM statements from a program to shorten it. We would type 0:REM and hit RETURN to enter this into the BASIC program. Then we would start the search by a SYS to the starting address of the ML program. In the PET 4.0 version of Program 8-1, it would be SYS 864 (hex $0360).

     By entering the "sample" string or command into the BASIC program as line zero, we solve two problems. First, if it is a string, it will be stored as the ASCII code for that string, just as BASIC stores strings. If it is a keyword like REM, it will be translated into the "tokenized," one-byte representation of the keyword, just as BASIC stores keywords. The second problem this solves is that our sample is located in a known area of RAM. By looking at Figure 8-1, you can tell that the sample's starting address is always the start of BASIC plus six. In Program 8-1 that means 0406 (see line 550).

A BASIC Program's Structure

Atari Program's Structure

(figure)

Set Up The Pointers

We will have to get the address of the next line in the BASIC program we are searching. And then we need to store it while we look through the current line. The way that BASIC lines are arranged, we come upon the link to the next line's address and the line number before we see any BASIC code itself. Therefore, the first order of business is to put the address of the next line into LIL. Lines 150 through 180 take the link found in start-of-BASIC RAM (plus one) and move it to the storage pointer "L1L."

     Next, lines 190 to 250 check to see if we have reached the end of the BASIC program. It would be the end if we had found two zeros in a row as the pointer to the next line's address. If it is the end, the RTS sends us back to BASIC mode.

     The subroutine in lines 260 through 440 saves the pointer to the following line's address and also the current line number. Note the double-byte addition in lines 390-440. Recall that we CLC before any addition. If adding four to the LSB (line 400) results in a carry, we want to be sure that the MSB goes up by one during the add-with-carry in line 430. It might seem to make no sense to add a zero in that line. What's the point? The addition is with carry; in other words, if the carry flag has been set up by the addition of four to the LSB in line 400, then the MSB will go up by one. The carry will make this happen.

First Characters

It's better to just compare the first character in a word against each byte in the searched memory than to try to compare the entire sample word. If you are looking for MEM, you don't want to stop at each byte in memory and see if M-E-M starts there. Just look for M's. When you come upon a M, then go through the full string comparison. If line 490 finds a first-character match, it transfers the program to "SAME" (line 520) which will do the entire comparison. On the other hand, if the routine starting at line 451 comes upon a zero (line 470), it knows that the BASIC line has ended (they all end with zero. It then goes down to "STOPLINE" (line 610) which puts the "next line" address pointer into the "current line" pointer and the whole process of reading a new BASIC line begins anew.

     If, however, a perfect match was found (line 560 found a zero at the end of the 0:REM line, showing that we had come to the end of the sample string) - we go to "PERFECT" and it makes a JSR to print out the line number (line 660). That subroutine bounces back (RTS) to "STOPLINE" which replaces the "current line" (L1L) pointer with the "next line" pointer (L2L). Then we JMP back to "READLINE" which, once again, pays very close attention to zeros to see if the whole BASIC program has ended with double zeros. We have returned to the start of the main loop of this ML program.

     This sounds more complicated than it is. If you've followed this so far, you can see that there is enormous flexibility in constructing ML programs. If you want to put the "STOPLINE" segment earlier than the "SAME" subroutine - go ahead. It is quite common to see a structure like this:

 INITIALIZATION
 LDA #15
 STA $83
 MAIN LOOP
 START JSR 1
 JSR 2
 JSR 3
 BEQ START
    (until some index runs out)
 RTS         (to BASIC)
 SUBROUTINES
 1
 2         
(each ends with RTS back to the MAIN LOOP)
 3
 DATA
 Table 1
 Table 2
 Table 3


The Atari FIND Utility

The second source listing, Program 8-2, adds a FIND command to Atari BASIC. You access it with the USR command. It is written to assemble in page six (1536 or $0600) and is an example of a full-blown assembly. You'll need the assembler/ editor cartridge to type it in.

After you've entered it, enter "ASM" to assemble it into memory. After it is finished, use the SAVE command to store the object (executable ML) code on tape or disk. Use:

 SAVE#C: > 0600,067E for tape
 SAVE#D:FIND.OBJ < 0600 067E for disk

     You can then put the BASIC cartridge in and enter the machine language with the BASIC loader program, or with the L command of DOS.

     Using FIND from BASIC is simple. Say you want to search a master string, A$ for the substring "hello". If B$ contains "hello", the USR call would look like:

 POS=USR (1536,ADR(A$),LEN(A$),ADR(B$),LEN(B$) )

     POS will contain the position of the match. It will be a memory location within the ADRress of A$. To get the character position within A$, just use POS-ADR(A$)+1. If the substring (B$) is not found, POS will be zero.

     It's easy to add commands like this to Atari BASIC. Also see "Getting The Most Out Of USR" in the November 1982 issue of COMPUTE! Magazine (p. 100).

64, Apple, & VIC Versions

Versions of the search routine for the Commodore 64 and VIC-20 and the Apple II are provided as BASIC loader programs. Remember from Chapter 2 that a loader is a BASIC program which POKEs a machine language program (stored in DATA statements) into memory. Once you have entered and run the BASIC programs, you can examine the ML programs using a disassembler. (See Appendix D.)

These versions are similar to the PET Version outlined in Program 8-1. The characters to be searched for are typed in line 0. To start the search in the 64 version (Program 8-3), type SYS 40800. Use CALL 768 to activate the Apple version (Program 8-4). The VIC version (Program 8-5) is activated with SYS 828.

As your skills improve, you will likely begin to appreciate, and finally embrace, the extraordinary freedom that ML confers on the programmer. Learning it can seem fraught with obscurity and rules. It can even look menacing. But there are flights you will soon be taking through your computer. Work at it. Try things. Learn how to find your errors. It's not circular - there will be considerable advances in your understanding. One day, you might be able to sit down and say that you can combine BASIC with ML and do pretty much anything you want to do with your machine.


Program 8-2.



0100
;========================;

0110
; FIND Utility           ;

0120
; Substring Search       ;

0130
; for Atari BASIC        ;

0140
; Completely relocatable ;

0150
;========================;

0160
;

0170
;

0180
;Variables in zero page for speed

0190
;
00CB
0200
SADRL
=$CB    ;Address
00CC
0210
SADRH
=$CC    ;of search
00CD
0220
SLENL
=$CD    ;Length of
00CE
0230
SLENH
=$CE    ;search space

0240
;

00CF
0250
FNDL
=$CF    ;Search address
00D0
0260
FNDH
=$D0    ;and
00D1
0270
FNDLEN
=$Dl    ;length 0280 ;

0280
;

00D2
0290
FIRSTCHAR
=$D2
00D3
0300
SINDEX
=$D3
00D4
0310
FR0
=$D4    ;Return
00D6
0320
FINDEX
=$D6    ;Source index
00D7
0330
TADRL
=$D7    ;Temp addr
00D8
0340
TADRH
=$D8
00D9
0350
ENDLOOP
=D9

0360
;

0370
;Syntax documentation

0380
;

0390
;FIND:Find Text

0400
;X=USR(FIND,A,B,C,D)

0410
;FIND:Address of utility (1536)

0420
;A: Where to start search

0430
;B: Where to quit searching

0440
;C: Search string address

0450
;D: Length of search string

0460
;X: Position found (=0 if no match)
0000
0470

 *=   $0600

0480
;------------------------------------

0490
;This portion sets up the parameters

0500
;for the search by pulling the values

0510
;passed by BASIC off the stack

0520
;

0530
FIND

0600 68
0540

PLA       ;Count byte
0601 68
0550

PLA       ;hi byte, Source start
0602 85CC
0560

STA SADRH
0604 68
0570

PLA       ;lo byte, Source start
0605 85CB
0580

STA SADRL
0607 68
0590

PLA       ;hi byte, Source end
0608 85CE
0600

STA SLENH
060A 68
0610

PLA       ;lo byte, Source end
060B 85CD
0620

STA SLENL
060D 68
0630

PLA       ;hi byte, Search string
060E 85D0
0640

STA FNDH
0610 68
0650

PLA       ;lo byte, Search string
0611 85CF
0660

STA FNDL
0613 68
0670

PLA       ;hi byte, Search length

0680
;Ignore it
0614 68
0690

PLA       ;lo byte, Search length
0615 85D1
0700

STA FNDLEN

0710
;

0720
;--------------------------

0730
;This is the main loop. We

0740
;search through the search space

0750
;looking for the first character

0760
;of the search string. We

0770
;look through entire 256-byte

0780
;blocks. If the first character

0790
;is found, we exit to a full

0800
;string comparison routine.

0810
;

0820
;If the string is never found,

0830
;we just return a zero to BASIC

0840
;
0617 A000
0850

LDY #0
0619 BlCF
0860

LDA (FNDL),Y   ;Set up first
061B 85D2
0870

STA FIRSTCHAR  ;comparison

0880
;

061D A6CE
0890

LDX SLENH      ;Less than 255
061F F018
0900

BEQ SHORT      ;bytes?

0910
NXTSRCH

0621 A9FF
0920

LDA #255       ;Select end

0930
SEARCH2

0623 85D9
0940

STA ENDLOOP
0625 A000
0950

LDY #0

0960
SEARCHLOOP

0627 BlCB
0970

LDA (SADRL),Y
0629 C5D2
0980

CMP FIRSTCHAR  ;Found a match?
062B F017
0990

BEQ FOUND1     ;yes

1000
NOTFOUND

062D C8
1010

INY    ;no
062E C4D9
1020

CPY ENDLOOP
0630 D0F5 
1030

BNE SEARCHLOOP ;continue

1040
;

0632 E6CC
1050

INC SADRH     ;Next block
0634 CA
1060

DEX           ;Done?
0635 3006
1070

BMI EXIT      ;yes
0637 D0E8
1080

BNE NXTSRCH   ;nope

1090
SHORT

0639 A5CD
1100

LDA SLENL     ;Set up last
063B D0E6
1110

BNE SEARCH2   ;scan

1120
EXIT

063D A900
1130

LDA #0        ;return
063F 85D4
1140

STA FRO       ;=0
0641 85D5
1150

STA FRO+1     ;?no strin
0643 60
1160

RTS           ;found

1170
;


1180
;------------------------------

1190
;Here is where we check for a

1200
;full match, starting with the

1210
;second character of the search string

1220
;We have to use two "pseudo" registers

1230
;in memory, since the same Y register

1240
;is needed to access both areas of memory

1250
;(search space and search string)

1260
;


1270
FOUND 1

0644 84D4
1280

STY FRO       ;Save Y
0646 84D3
1290

STY SINDEX    ;Source index
0648 A001
1300

LDY #1
064A 84D6
1310

STY FINDEX    ;Find index

1320
;


1330
;We use a temporary address, since we don't want

1340
;to change the address in SADR (so we can continue the

1350
;search if no match found)

1360
;

064C A5CB
1370

LDA SADRL     ;Copy to
064E 85D7
1380

STA TADRL     ;temp addr
0650 A5CC
1390

LDA SADRH
0652 85D8
1400

STA TADRH

1410
;


1420
CONTSRCH


1430
;


1440
;As long as each character matches, we

1450
;continue to compare until we get a failed comparison

1460
;or reach the end of the search string,

1470
;which indicates a match

1480
;

0654 A4D6
1490

LDY FINDEX
0656 C4D1
1500

CPY FNDLEN     ;Past end?
0658 F016
1510

BEQ FOUND2     ;yes-matchl
065A BlCF
1520

LDA (FNDL),Y   ;Character n
065C E6D6
1530

INC FINDEX     ;no, increment
065E A4D3
1540

LDY SINDEX     ;Compare to
0660 C8
1550

INY            ;source
0661 D002
1560

BNE SKIPINC    ;Hit page bound?
0663 E6D8
1570

INC TADRH

1580
SKIPINC

0665 84D3
1590

STY SINDEX     ;Update
0667 DlD7
1600

CMP (TADRL),Y  ;equal so far?
0669 F0E9
1610

BEQ CONTSRCH   ;yes, continue

1620
;Comparison failure,

1630
;Return to main loop
066B A4D4
1640

LDY FRO
066D 18
1650

CLC            ;Used in place
066E 90BD
1660

BCC NOTFOUND   ;of JMP (relocatable)

1670
;


1680
;Match!

1690
;Return address in FRO to BASIC

1700
FOUND2

0670 18
1710

CLC
0671 A5D4
1720

LDA FRO
0673 65CB
1730

ADC SADRL
0675 85D4
1740

STA FRO
0677 A5CC
1750

LDA SADRH
0679 6900
1760

ADC #0
067B 85D5
1770

STA FRO+1
067D 60
1780

RTS

1790


067E
1800

.END

=00CB SADRL     =00CC SADRH     =00CD SLENL       =00CE SLENH
=00CF FNDL      =00D0 FNDH      =00Dl FNDLEN      =00D2 FIRSTCHAR
=OOD3 SINDEX    =00D4 FRO       =00D6 FINDEX      =00D7 TADRL
=00D8 TADRH     =00D9 ENDLOOP    0600 FIND         0639 SHORT
 0621 NXTSRCH    0623 SEARCH2    0627 SEARCHLOOP   0644 FOUND1
 062D NOTFOUND   063D EXIT       0654 CONTSRCH     0670 FOUND2
 0665 SKIPINC


Program 8-3. 64 Search BASIC Loader.

799 X=PEEK(55):POKE55,X-1:REM PROTECT ML
800 FOR ADRES=40800TO40913:READ DATTA:
    POKE ADRES,DATTA:NEXT ADRES
900 PRINT"SYS40800 TO ACTIVATE"
4096 DATA 162, 0, 173, 1, 8, 133
4102 DATA 165, 173, 2, 8, 133, 166
4108 DATA 160, 0, 177, 165, 208, 6
4114 DATA 200, 177, 165, 208, 1, 96
4120 DATA 160, 0, 177, 165, 141, 167
4126 DATA 0, 200, 177, 165, 141, 168
4132 DATA 0, 200, 177, 165, 133, 57
4138 DATA 200, 177, 165, 133, 58, 165
4144 DATA 165, 24, 105, 4, 133, 165
4150 DATA 165, 166, 105, 0, 133, 166
4156 DATA 160, 0, 177, 165, 240, 28
4162 DATA 205, 6, 8, 240, 4, 200
4168 DATA 76, 158, 159, 162, 0, 232
4174 DATA 200, 189, 6, 8, 240, 7
4180 DATA 209, 165, 240, 245, 76, 158
4186 DATA 159, 32, 201, 159, 165, 167
4192 DATA 133, 165, 165, 168, 133, 166
4198 DATA 76, 108, 159, 32, 201, 189
4204 DATA 169, 32, 32, 210, 255, 96
READY.



Program 8-4. Apple Version.

700 FOR AD=768TO900: READ DA:POKE A
    D,DA:NEXT AD
768 DATA169,76,141,245,3,169
774 DATA16,141,246,3,169,3
780 DATA141,247,3,96,162,0
786 DATA173,1,8,133,1,173
792 DATA2,8,133,2,160,0
798 DATA177,1,208,6,200,177
804 DATA1,208,1,96,160,0
810 DATA177,1,133,3,200,177
816 DATA1,133,4,200,177,1
822 DATA133,117,200,177,1,133
828 DATA118,165,1,24,105,4
834 DATA133,1,165,2,105,0
840 DATA133,2,160,0,177,1
846 DATA240,28,205,6,8,240
852 DATA4,200,76,76,3,162
858 DATA0,232,200,189,6,8
864 DATA240,7,209,1,240,245
870 DATA76,76,3,'76,119,3
876 DATA165,3,133,1,165,4
882 DATA133,2,76,28,3,169
888 DATA163,32,237,253,32,32
894 DATA237,169,160,32,237,253
900 DATA76,108,3


Program 8-5. VIC-20 Search BASIC Loader.

800 FOR ADRES=828TO941:READ DATTA:POKE ADR
    ES,DATTA:NEXT ADRES
810 PRINT"SYS 828 TO ACTIVATE"
828 DATA 162, 0, 173, 1, 16, 133
834 DATA 187, 173, 2, 16, 133, 188
840 DATA 160, 0, 177, 187, 208, 6
846 DATA 200, 177, 187, 208, 1, 96
852 DATA 160, 0, 177, 187, 141, 190
858 DATA 0, 200, 177, 187, 141, 191
864 DATA 0, 200, 177, 187, 133, 57
870 DATA 200, 177, 187, 133, 58, 165
876 DATA 187, 24, 105, 4, 133, 187
882 DATA 165, 188, 105, 0, 133, 188
888 DATA 160, 0, 177, 187, 240, 28
894 DATA 205, 6, 16, 240, 4, 200
900 DATA 76, 122, 3, 162, 0, 232
906 DATA 200, 189, 6, 16, 240, 7
912 DATA 209, 187, 240, 245, 76, 122
918 DATA 3, 32, 165, 3, 165, 190
924 DATA 133, 187, 165, 191, 133, 188
930 DATA 76, 72, 3, 32, 194, 221
936 DATA 169, 32, 32, 210, 255, 96


Return to Table of Contents | Previous Chapter | Next Chapter