| Volume 1 -- Issue 2 | November 1980 |
Our second issue is 33% larger than the first! And not only so, but also there is useful information on the back page! I found a source for 6x9 white envelopes, so your address can be external to the newsletter, and so your copy will arrive in better condition. In less than a month since the newsletter was first announced, we already have over 45 paid subscribers. They are sprinkled all over the map, including one in Japan!
In This Issue...
One real bug has turned up, and a few of you have had the bad luck to discover it the hard way. The assembler is free-format, in that opcodes and directives may start in any column after the blank which terminates the label field. However, the ".IN" directive will malfunction unless there are at least six spaces. If you tab over before typing ".IN" there will be no problem. However, if you type your line like "1230 .IN FILE1", with only two spaces between the line number and the period, you are in for a long wait. The processor goes into a loop printing D's. If you have the MONC mode on, you will see "LOADDDDDDDDD....." with D's forever appearing on your screen. Remember to TAB OVER, and it will not malfunction.
One fancied bug has been reported, and I would like to explain it. A user pointed out that you cannont shorten the SAVE command to three letters if you wish to save the source program on a disk file. Why? Because "SAVE" or "SAV" with no file name is not a DOS command. It is an assembler command to save the source program on cassette tape! On the other hand, SAVE with a filename is not an assembler command. It is a DOS command, and the assembler never sees it. The same goes for "LOAD", "LOA", and LOAD with a filename.
| Variable Cross Reference for Applesoft Programs | Bob Sander-Cederlof |
Besides illustrating a lot of programming techniques, the VCR program is a very useful tool when you are writing large Applesoft programs. As listed here, it requires a 48K Apple, and assumes that HIMEM is set to at least $8AA7. You BRUN it, and it sets up the &-vector. When you are ready to print a cross reference, you merely type "&" and a carriage return, and out it comes. It is VERY fast: about 15 times faster than the VCR program included in Apple's DOS Tool Kit. It also takes less memory than Apple's version, both for the program itself and for the tables it constructs during execution.
The main body of the program is in lines 1400 thru 1460. After calling INITIALIZATION, the subroutine PROCESS.LINE is called until there are no more lines. Then PRINT.REPORT is called, and finally INITIALIZATION is called again to restore Applesoft's tables to their original form.
INITIALIZATION sets up PNTR to point to the beginning of the program, and EOT to point to the end of the table area. It also clears out a set of 26 two-byte pointers in HSHTBL (hash table). PROCESS.ONE scans a single line looking for variables by calling SCAN.FOR.VARIABLES, until the end of the program is reached. PRINT.REPORT merely prints a nice orderly report from the data which has been stored in the table by SCAN.FOR.VARIABLES.
The symbol table routines used in VCR are very similar to the ones used inside S-C Assembler II Version 4.0. There are 26 pointers starting at HSHTBL ($280), each one representing one letter of the alphabet. The first letter of a variable name selects one of these pointers. The pointer points at the first entry in a chain of variable names. When a new variable name is found, it is inserted in the appropriate chain at the place where it will be in alphabetical order. A sub-chain is kept for each variable name of all the line numbers from which it is referenced. The line number chain is maintained in numerical order. Thus there is no sorting necessary when it comes time to print the report.
Since no routines from the Applesoft ROMs are used, VCR will work with no changes with the RAM version of Aplesoft. Since it loads below $9000, it will not conflict with Neil Konzen's PLE (Program Line Editor). Since it is just straight-forward code, with no address tables or embedded data, you can easily relocate it to a different running address; only the 3-byte instructions with the third byte equal to $88, $89, or $8A need to be changed. Or, you can type it in, and use a different origin (line 1040).
If you like to modify programs, this one needs one improvement. (Only one?) I forgot to take note of the FN token, so any FN definitions or uses will look like references to an array variable. Another kind of modification, called "major" perhaps, will turn the VCR into LNCR (Line Number Cross Reference).
1000 *---------------------------------
1010 * VARIABLE CROSS REFERENCE
1020 * FOR APPLESOFT PROGRAMS
1030 *---------------------------------
1040 ZZ.BEG .EQ $8800
1050 .OR ZZ.BEG
1060 .TF B.VCR
1070 *---------------------------------
1080 LDA #$4C AMPERSAND VECTOR
1090 STA $3F5
1100 LDA #VCR
1110 STA $3F6
1120 LDA /VCR
1130 STA $3F7
1140 RTS
1150 *---------------------------------
1160 PNTR .EQ $18,19 POINTER INTO PROGRAM
1170 DATA .EQ $1A THRU $1D
1180 LZFLAG .EQ $1A LEADING ZERO FLAG
1190 NEXTLN .EQ $1A,1B ADDRESS OF NEXT LINE
1200 LINNUM .EQ $1C,1D CURRENT LINE NUMBER
1210 STPNTR .EQ $1E,1F POINTER INTO VARIABLE TABLE
1220 TPTR .EQ $9B,9C TEMP POINTER
1230 SYMBOL .EQ $9D THRU $A4 8 BYTES
1240 VARNAM .EQ SYMBOL+1
1250 HSHTBL .EQ $280
1260 ENTRY.SIZE .EQ $A5,A6
1270 *---------------------------------
1280 PRGBOT .EQ $67,68 BEGINNING OF PROGRAM
1290 LOMEM .EQ $69,6A BEGINNING OF VARIABLE SPACE
1300 EOT .EQ $6B,6C END OF VARIABLE TABLE
1310 *---------------------------------
1320 TKN.REM .EQ 178
1330 TKN.DATA .EQ 131
1340 *---------------------------------
1350 MON.CH .EQ $24
1360 MON.PRBL2 .EQ $F94A
1370 MON.COUT .EQ $FDED
1380 MON.CROUT .EQ $FD8E
1390 *---------------------------------
1400 VCR
1410 JSR INITIALIZATION
1420 .1 JSR PROCESS.LINE
1430 BNE .1 UNTIL END OF PROGRAM
1440 JSR PRINT.REPORT
1450 JSR INITIALIZATION ERASE VARIABLE TABLE
1452 LDA #0 CLEAR $A4 SO APPLESOFT WILL
1454 STA $A4 WORK CORRECTLY
1460 RTS
1470 *---------------------------------
1480 INITIALIZATION
1490 LDA LOMEM
1500 STA EOT
1510 LDA LOMEM+1
1520 STA EOT+1
1530 LDX #52 # OF BYTES FOR HASH POINTERS
1540 LDA #0
1550 .1 STA HSHTBL-1,X
1560 DEX
1570 BNE .1
1580 LDA PRGBOT
1590 STA PNTR
1600 LDA PRGBOT+1
1610 STA PNTR+1
1620 RTS
1630 *---------------------------------
1640 PROCESS.LINE
1650 LDY #3 CAPTURE POINTER AND LINE #
1660 .1 LDA (PNTR),Y
1670 STA DATA,Y
1680 DEY
1690 BPL .1
1692 LDA DATA+1 CHECK IF END
1694 BEQ .3 YES
1700 CLC SKIP OVER DATA
1710 LDA PNTR
1720 ADC #4
1730 STA PNTR
1740 BCC .2
1750 INC PNTR+1
1760 .2 JSR SCAN.FOR.VARIABLES
1770 LDA DATA
1780 STA PNTR
1790 LDA DATA+1
1800 STA PNTR+1
1810 * BNE .3
1820 .3 RTS
1830 *---------------------------------
1840 SCAN.FOR.VARIABLES
1850 .1 JSR GET.NEXT.VARIABLE
1860 BEQ .3 END OF LINE
1870 JSR PACK.VARIABLE.NAME
1880 JSR SEARCH.VARIABLE.TABLE
1890 BCC .2 FOUND SAME VARIABLE
1900 LDA #0
1910 STA SYMBOL+4 START OF LINE NUMBER CHAIN
1920 STA SYMBOL+5
1930 LDA LINNUM+1 MSB FIRST
1940 STA SYMBOL+6
1950 LDA LINNUM
1960 STA SYMBOL+7
1970 LDA #8 ADD 8 BYTE ENTRY
1980 JSR ADD.NEW.ENTRY
1990 JMP .1
2000 .2 JSR SEARCH.LINE.CHAIN
2010 BCC .1 FOUND SAME LINE NUMBER
2020 LDA #4 ADD 4 BYTE ENTRY
2030 JSR ADD.NEW.ENTRY
2040 JMP .1
2050 .3 RTS
2060 *---------------------------------
2070 GET.NEXT.VARIABLE
2080 .1 JSR NEXT.CHAR.NOT.QUOTE
2090 BEQ .2 END OF LINE
2100 CMP #TKN.DATA
2110 BEQ .3
2120 CMP #TKN.REM
2130 BEQ .2 SKIP TO NEXT LINE
2140 JSR LETTER LETTER?
2150 BCC .1 NO, KEEP LOOKING
2160 .2 RTS
2170 * DATA, SO SKIP TO NEXT STATEMENT
2180 .3 JSR NEXT.CHAR.NOT.QUOTE
2190 BEQ .2 EOL, RETURN
2200 CMP #': COLON?
2210 BNE .3 NOT END YET
2220 BEQ .1 ...ALWAYS
2230 *---------------------------------
2240 NEXT.CHAR.NOT.QUOTE
2250 .1 JSR NEXT.CHAR
2260 BEQ .2 EOL, RETURN
2270 CMP #' QUOTE?
2280 BEQ .3 YES, SCAN OVER QUOTATION
2290 .2 RTS RETURN
2300 .3 JSR NEXT.CHAR
2310 BEQ .2 EOL, RETURN
2320 CMP #' TERMINAL QUOTE?
2330 BNE .3 NOT YET
2340 BEQ .1 ...ALWAYS
2350 *---------------------------------
2360 * NEXT CHARACTER FROM LINE
2370 * CALL: JSR NEXT.CHAR
2380 * RETURN: (A)=CHAR FROM LINE
2390 * IF CHAR .NE. EOL,
2400 * INCREMENT PNTR AND
2410 * STATUS Z=0
2420 * IF CHAR .EQ. EOL,
2430 * STATUS Z=1
2440 *---------------------------------
2450 NEXT.CHAR
2460 LDY #0
2470 LDA (PNTR),Y
2480 BEQ .1 EOL
2490 INC PNTR BUMP POINTER
2500 BNE .1
2510 INC PNTR+1
2520 .1 RTS
2530 *---------------------------------
2540 PACK.VARIABLE.NAME
2550 STA VARNAM FIRST CHAR OF NAME
2560 LDA #' BLANKS FOR OTHER TWO CHARS
2570 STA VARNAM+1
2580 STA VARNAM+2
2590 JSR NEXT.CHAR
2600 BEQ .5 END OF LINE
2610 JSR LTRDIG
2620 BCC .2 NOT LETTER OR DIGIT
2630 STA VARNAM+1
2640 .1 JSR NEXT.CHAR IGNORE EXCESS NAME
2650 BEQ .5 END OF LINE
2660 JSR LTRDIG
2670 BCS .1 LETTER OR DIGIT
2680 .2 CMP #'$ DOLLAR SIGN?
2690 BEQ .3 YES
2700 CMP #'% PER CENT?
2710 BNE .4 NO
2720 .3 STA VARNAM+2
2730 JSR NEXT.CHAR
2740 BEQ .5 END OF LINE
2750 .4 CMP #'( LEFT PAREN?
2752 BEQ .6 YES
2754 CMP #' QUOTE?
2760 BNE .5 NO
2762 LDA PNTR YES, BACK UP POINTER
2763 BNE .7
2764 DEC PNTR+1
2765 .7 DEC PNTR
2766 RTS
2770 .6 LDA VARNAM+2 SET HIGH BIT
2780 ORA #$80 TO FLAG ARRAY
2790 STA VARNAM+2 REFERENCE
2800 .5 RTS
2810 *---------------------------------
2820 SEARCH.VARIABLE.TABLE
2830 SEC CONVERT 1ST CHAR TO
2840 LDA VARNAM HASH TABLE INDEX
2850 SBC #'A
2860 ASL
2870 ADC #HSHTBL
2880 STA STPNTR
2890 LDA /HSHTBL
2900 ADC #0
2910 STA STPNTR+1
2920 *--- FALL INTO CHAIN SEARCH ROUTINE
2930 *---------------------------------
2940 CHAIN.SEARCH
2950 .1 LDY #0 POINT AT POINTER IN ENTRY
2960 LDA (STPNTR),Y
2970 STA TPTR
2980 INY
2990 LDA (STPNTR),Y
3000 BEQ .4 END OF CHAIN, NOT IN TABLE
3010 STA TPTR+1
3020 LDX #2 2 MORE CHARS IN SYMBOL
3030 LDY #2 POINT AT NAME IN ENTRY
3040 .2 LDA (TPTR),Y COMPARE NAMES
3050 CMP SYMBOL,Y
3060 BCC .3 NOT THIS ONE, BUT KEEP LOOKING
3070 BNE .4 NOT IN THIS CHAIN
3080 DEX
3090 BEQ .5 NAME IS THE SAME
3100 INY NEXT BYTE PAIR
3110 BNE .2 ...ALWAYS
3120 *---------------------------------
3130 .3 JSR .5 UPDATE POINTER, CLEAR CARRY
3140 BCC .1 ...ALWAYS
3150 *---------------------------------
3160 .4 SEC DID NOT FIND
3170 RTS
3180 *---------------------------------
3190 .5 LDA TPTR
3200 STA STPNTR
3210 LDA TPTR+1
3220 STA STPNTR+1
3230 CLC
3240 RTS
3250 *---------------------------------
3260 ADD.NEW.ENTRY
3270 STA ENTRY.SIZE
3280 CLC SEE IF ROOM
3290 LDX #1
3300 LDY #0
3310 STY ENTRY.SIZE+1
3320 .1 LDA (STPNTR),Y GET CURRENT POINTER
3330 STA SYMBOL,Y
3340 LDA EOT,Y
3350 STA (STPNTR),Y
3360 STA TPTR,Y
3370 ADC ENTRY.SIZE,Y
3380 STA EOT,Y
3390 INY
3400 DEX
3410 BPL .1
3420 *--- SEE IF GOING TO BE ENOUGH ROOM
3430 LDA EOT
3440 CMP #ZZ.BEG
3450 LDA EOT+1
3460 SBC /ZZ.BEG
3470 BCS .3 MEM FULL ERR
3480 *--- MOVE ENTRY INTO VARIABLE TABLE
3490 LDY ENTRY.SIZE
3500 DEY
3510 .2 LDA SYMBOL,Y
3520 STA (TPTR),Y
3530 DEY
3540 BPL .2
3550 LDA TPTR
3560 STA STPNTR
3570 LDA TPTR+1
3580 STA STPNTR+1
3590 RTS
3600 .3 JMP MEM.FULL.ERR
3610 MEM.FULL.ERR
3620 BRK
3630 *---------------------------------
3640 SEARCH.LINE.CHAIN
3650 CLC ADJUST POINTER TO START
3660 LDA STPNTR OF LINE # CHAIN
3670 ADC #4
3680 STA SYMBOL
3690 LDA STPNTR+1
3700 ADC #0
3710 STA SYMBOL+1
3720 LDA #SYMBOL
3730 STA STPNTR
3740 LDA /SYMBOL
3750 STA STPNTR+1
3760 LDA LINNUM PUT LINE NUMBER INTO SYMBOL
3770 STA SYMBOL+3
3780 LDA LINNUM+1
3790 STA SYMBOL+2
3800 JMP CHAIN.SEARCH
3810 *---------------------------------
3820 PRINT.REPORT
3830 LDA #'A START WITH A'S
3840 .1 STA VARNAM
3850 SEC
3860 SBC #'A CONVERT TO HSHTBL INDEX
3870 ASL
3880 TAY
3890 LDA HSHTBL+1,Y
3900 BEQ .2 NO ENTRY FOR THIS LETTER
3910 STA PNTR+1
3920 LDA HSHTBL,Y
3930 STA PNTR
3940 JSR PRINT.LETTER.CHAIN
3950 .2 INC VARNAM NEXT LETTER
3960 LDA VARNAM
3970 CMP #'Z+1
3980 BCC .1 STILL MORE LETTERS
3990 RTS FINISHED
4000 *---------------------------------
4010 LTRDIG
4020 CMP #'0 DIGIT?
4030 BCC LD1 NO
4040 CMP #'9+1
4050 BCC LD2 YES
4060 LETTER
4070 CMP #'A LETTER?
4080 BCC LD1 NO
4090 CMP #'Z+1
4100 BCC LD2 YES
4110 CLC NO
4120 LD1 RTS
4130 LD2 SEC
4140 RTS
4150 *---------------------------------
4160 PRINT.LETTER.CHAIN
4170 .1 LDA VARNAM FIRST LETTER
4180 JSR PRINT.CHAR
4190 LDY #1
4200 .2 INY
4210 LDA (PNTR),Y REST OF NAME
4220 AND #$7F
4230 CMP #' BLANK?
4240 BEQ .3
4250 JSR PRINT.CHAR
4260 .3 CPY #3
4270 BCC .2
4280 LDA (PNTR),Y CHECK IF ARRAY
4290 BPL .4
4300 LDA #'(
4310 JSR PRINT.CHAR
4320 .4 CLC POINT AT LINE # CHAIN
4330 LDA PNTR
4340 ADC #4
4350 STA TPTR
4360 LDA PNTR+1
4370 ADC #0
4380 STA TPTR+1
4390 JSR PRINT.LINNUM.CHAIN
4400 JSR MON.CROUT
4410 LDY #1
4420 LDA (PNTR),Y POINTER TO NEXT VARIABLE
4430 BEQ .5 NO MORE
4440 PHA
4450 DEY
4460 LDA (PNTR),Y
4470 STA PNTR
4480 PLA
4490 STA PNTR+1
4500 BNE .1 ...ALWAYS
4510 .5 RTS
4520 *---------------------------------
4530 PRINT.LINNUM.CHAIN
4540 .1 JSR TAB.NEXT.COLUMN
4550 LDY #2 POINT AT LINE #
4560 LDA (TPTR),Y
4570 STA LINNUM+1
4580 INY
4590 LDA (TPTR),Y
4600 STA LINNUM
4610 JSR PRINT.LINE.NUMBER
4620 LDY #1 SET UP NEXT POINTER
4630 LDA (TPTR),Y
4640 BEQ .2
4650 PHA
4660 DEY
4670 LDA (TPTR),Y
4680 STA TPTR
4690 PLA
4700 STA TPTR+1
4710 BNE .1 ...ALWAYS
4720 .2 RTS
4730 *---------------------------------
4740 TAB.NEW.LINE
4750 JSR MON.CROUT
4760 TAB.NEXT.COLUMN
4770 .1 LDA #7 FIRST TAB STOP
4780 .2 CMP MON.CH CURSOR POSITION
4790 BCS .3 PERFORM TAB
4800 ADC #6 NEXT TAB STOP
4810 CMP #33 END OF LINE?
4820 BCC .2
4830 BCS TAB.NEW.LINE ...ALWAYS
4840 .3 BEQ .4 ALREADY THERE
4850 SBC MON.CH CALCULATE # OF BLANKS
4860 TAX
4870 JSR MON.PRBL2
4880 .4 RTS
4890 *---------------------------------
4900 PRINT.LINE.NUMBER
4910 LDX #4 PRINT 5 DIGITS
4920 STX LZFLAG TURN ON LEADING ZERO FLAG
4930 .1 LDA #'0 DIGIT=0
4940 .2 PHA
4950 SEC
4960 LDA LINNUM
4970 SBC PLNTBL,X
4980 PHA
4990 LDA LINNUM+1
5000 SBC PLNTBH,X
5010 BCC .3 LESS THAN DIVISOR
5020 STA LINNUM+1
5030 PLA
5040 STA LINNUM
5050 PLA
5060 ADC #0 INCREMENT DIGIT
5070 BNE .2 ...ALWAYS
5080 .3 PLA
5090 PLA
5100 CMP #'0
5110 BEQ .5 ZERO, MIGHT BE LEADING
5120 SEC TURN OFF LZFLAG
5130 ROR LZFLAG
5140 .4 JSR PRINT.CHAR
5150 DEX
5160 BPL .1
5170 RTS
5180 .5 BIT LZFLAG LEADING ZERO FLAG
5190 BMI .4 NO
5200 LDA #' BLANK
5210 BNE .4 ...ALWAYS
5220 PLNTBL .DA #1
5230 .DA #10
5240 .DA #100
5250 .DA #1000
5260 .DA #10000
5270 PLNTBH .DA /1
5280 .DA /10
5290 .DA /100
5300 .DA /1000
5310 .DA /10000
5320 *---------------------------------
5330 PRINT.CHAR
5340 ORA #$80
5350 JSR MON.COUT
5360 RTS
5370 *---------------------------------
5380 ZZ.END .EQ *
5390 ZZ.SIZ .EQ ZZ.END-ZZ.BEG
|
Since I sell software in stores, I buy a lot of zip-lock bags, cardboard mailing boxes, diskettes, and so on. I thought that maybe you need some of these, and haven't been able to find a source at good prices in small quantities. I will sell you some of mine, at the follwoing prices:
6"x9" zip-lock bags $8.50/100
9"x12" zip-lock bags $12/100
Verbatim diskettes
without hubrings $30 for box of ten, $265 for 100
with hubrings $32 for box of ten, $285 for 100
Anything else you need? Let me know, maybe I have it or can get it for you or tell you where you can get it at a good price.
| Assembly Source on Text Files | Bob Sander-Cederlof |
Version 4.0 of the S-C Assembler II allows you to EXEC a source program, if it is on a DOS text file. This is handy if you have created it with a different editor, or perhaps with a compiler. But what if you want to go the other way? What if you want to SAVE a source program on a text file, so that it can be used in another editor, or by another assembler?
There is no built-in command to allow it, so I have now written a separate program to do it. The program loads at $0800 thru $093C, and does not borrow any code from the assembler. It does use some routines in the Monitor ROMs, and the DOS I/O rehook routine. If you BRUN the program, it will assume the pointers at $CA,CB and $4C,4D are bracketing a valid assembly source program, and try to list it on a text file.
The main body of the program is in lines 1190 thru 1630. Lines 1200 and 1210 serve to un-hook the S-C Assembler II from the output. They will also turn off your printer, if you had it on. Lines 1220 and 1230 tell DOS that it should recognize commands printed after a control-D. Lines 1240 and 1250 change the prompt symblol to a blank, so that the monitor input subroutine will not print a colon or some other character as the prompt when reading the file name.
Lines 1290-1360 request you to enter a file name, read it into the monitor buffer starting at $0200, and move it to a safe place at $0280. It has to be moved, because when we print DOS commands later the area starting at $0200 will be written on by DOS.
Once the file name you have typed is safely stored at $0280 and following, lines 1410 thru 1490 will set up the file for writing. This is done in five steps. First, close all files. Second, issue an OPEN-DELETE-OPEN sequence, with the file name (of course); this will make sure that we are writing on a fresh empty file. Then the WRITE command is sent, and we are ready to roll.
Line 1530 calls a subroutine which lists your source program. Since the file is OPEN and in WRITE mode, the listing goes into your text file. If you have MON O mode set, you will also see the listing on your screen. Note that it is not really necessary for me to use a subroutine at this point. ASM.LIST is only called once, and it is not very long. But I did it anyway, to keep the main body short enough to fit on a page (of paper), easy to understand, modular, structured, etc.
After the listing is completed, line 1570 will close the text file. Lines 1610 and 1620 turn off the DOS run flag, so that DOS will not look for control-D commands. And finally, line 1630 re-enters the S-C Assembler II through its soft entry point.
Lines 1670 thru 1780 are text strings, printed by the subroutine named PRINT.QUOTE. Each string is written with the sign bit of every byte zero except for the last byte. The sign bit of the last byte is 1, telling PRINT.QUOTE that it is finished. For example, the first message is the word "CLOSE" and a carriage return. The carriage return is entered in hex with the sign bit 1 as in $8D. The second message is the word "OPEN", and the letter "N" is preceded by a minus sign in the .AS directive to indicate that the sign bit should be 1.
The PRINT.QUOTE subroutine is at lines 2140 thru 2200. It expects the Y-register to contain the offset of the desired message from the beginning of all the messages at QTS. It calls on PRINT.CHAR to actually send each character.
PRINT.CHAR, at lines 2020 thru 2100, calls on the monitor print character routine at $FDED. This branches through DOS, and DOS writes the character on the text file. PRINT.CHAR saves and restores the Y-register and A-register contents. It also sets the sign bit on each character before printing it. Upon exit, the status will reflect the value of the character printed.
Lines 1820-1980 issue a DOS command. The Y-register points at one of the message strings in QTS. Control-D is printed, followed by the command key word, a space, and file name you previously typed. Since DOS does not allow slot and drive specifications on the WRITE command, and since it is sufficient to specify them only once, the subroutine chops them off after printing them once. The logic for this is in lines 1910-1940: after printing a comma, it is replaced with a carriage return. The next time the name is printed, the carriage return will be the end.
The subroutine which really controls the listing is in lines 2330-2450. The first four instructions set up a zero-page pointer SRCP to point at the beginning of your source program. Lines 2380-2420 compare the pointer with HIMEM to see if the listing is completed. If you really had no source program, we would already be finished at this point. If there is another line (or more), the subroutine named ASM.LIST.LINE is called to list the next lne. The process is repeated until the last line has been printed onto your text file.
At this point it might be helpful to explain how source lines are stored in memory. Each line begins with a single byte which contains the byte-count of the line. Next are a byte-pair containing the line number of the line, in the usual backwards 6502 format. The text of the line follows, and a final byte containing $00 ends the line. No carriage return is stored. Blanks are treated specially. A single blank is stored as $81. Two blanks in a row are replaced by one byte of value $82. Any string of blanks up to 63 blanks is thus replaced by a single token of value $80 plus the blank count. Longer strings of blanks will take more than one token.
For example, the source line
1000 ABC LDA SAM |
is stored as:
0F (total of 15 bytes in line image)
E8 03 (line number 1000)
41 42 43 84 ("ABC" and 4 blanks)
4C 44 41 81 ("LDA" and 1 blank)
53 41 4D ("SAM")
00 (end of line indicator) |
The subroutine ASM.LIST.LINE at lines 2490-2610 prints one source line. A subroutine named GNB ("get next byte") is called to skip over the length byte, and to pick up the line number. PRINT.LINNUM is called to convert the line number to decimal and print it, with leading zeroes if necessary, as a four digit number. The loop at lines 2570-2600 is seeded with a blank (because the blank between the line number and the label field is not actually stored in the source program), and the text of the line is printed. The loop prints a character, and then calls NEXT.TOKEN to get the next one. When the token returned equals $00, the line is finished.
GNB, lines 2630-2690, clears the queued blank count, picks up the character pointed at by SRCP, and increments SRCP.
NEXT.TOKEN, lines 2710-2820, tests the blank count. If it is non-zero, the count is decremented and a blank ($20) character is returned. If the count was zero, the next character is picked up from the line. If this character is not a blank count token, it is returned and the pointer in SRCP is incremented. If the character is a blank count token, it is saved, the SRCP pointer is incremented past the token, and then the count is decremented and a blank returned.
The PRINT.LINNUM routine, lines 2860-3170, is a revision of a routine used in the Integer BASIC ROMs. I think it is commented well enough for you to follow. The general idea is to divide by 1000 and print the quotient; divide the remainder by 100 and print the quotient; then by 10; and finally print the remainder.
Since several of you have asked me to provide the capability to list programs onto text files, you should be pleased with this program. If you do not need it, then maybe it has shed some light on the internal structure of part of the assembler, or served as a tutorial in programming.
1000 .LIST OFF
1010 *---------------------------------
1020 * WRITE ASSEMBLY SOURCE ON A TEXT FILE
1030 *---------------------------------
1040 .OR $800
1050 MON.PROMPT .EQ $33
1060 PP .EQ $CA,CB
1070 HIMEM .EQ $4C,4D
1080 DOS.RUNFLAG .EQ $D9
1090 MON.BUFFER .EQ $200
1100 DOS.BUFFER .EQ $280
1110 MON.GETLN .EQ $FD6A
1120 MON.CROUT .EQ $FD8E
1130 MON.COUT .EQ $FDED
1140 MON.SETVID .EQ $FE93
1150 DOS.REHOOK .EQ $3EA
1160 BLANK.COUNT .EQ $00
1170 SRCP .EQ $01,02
1180 LINNUM .EQ $03,04
1190 *---------------------------------
1200 TEXT.LIST
1210 JSR MON.SETVID
1220 JSR DOS.REHOOK
1230 LDA #$FF
1240 STA DOS.RUNFLAG
1250 LDA #' +$80 SET PROMPT CHAR = BLANK
1260 STA MON.PROMPT
1270 *---------------------------------
1280 * GET FILE NAME
1290 *---------------------------------
1300 LDY #QFILNAM-QTS
1310 JSR PRINT.QUOTE
1320 JSR MON.GETLN
1330 LDY #$7F MOVE FILE NAME TO SEPARATE BUFFER
1340 .1 LDA MON.BUFFER,Y
1350 STA DOS.BUFFER,Y
1360 DEY
1370 BPL .1
1380 *---------------------------------
1390 * SET UP THE TEXT FILE
1400 * (CLOSE, OPEN, DELETE, OPEN, WRITE)
1410 *---------------------------------
1420 JSR CLOSE.FILE
1430 LDY #QOPEN-QTS
1440 JSR ISSUE.DOS.COMMAND
1450 LDY #QDELETE-QTS
1460 JSR ISSUE.DOS.COMMAND
1470 LDY #QOPEN-QTS
1480 JSR ISSUE.DOS.COMMAND
1490 LDY #QWRITE-QTS
1500 JSR ISSUE.DOS.COMMAND
1510 *---------------------------------
1520 * LIST THE SOURCE PROGRAM
1530 *---------------------------------
1540 JSR ASM.LIST
1550 *---------------------------------
1560 * CLOSE THE FILE
1570 *---------------------------------
1580 JSR CLOSE.FILE
1590 *---------------------------------
1600 * RETURN TO CALLER
1610 *---------------------------------
1620 LDA #0
1630 STA DOS.RUNFLAG
1640 JMP $1003
1650 *---------------------------------
1660 * MESSAGE TEXT
1670 *---------------------------------
1680 QTS .EQ *
1690 QCLOSE .AS /CLOSE/
1700 .HS 8D
1710 QOPEN .AS /OPE/
1720 .AS -/N/
1730 QDELETE .AS /DELET/
1740 .AS -/E/
1750 QWRITE .AS /WRIT/
1760 .AS -/E/
1770 QFILNAM .HS 0D
1780 .AS /TEXT FILE NAME:/
1790 .AS -/ /
1800 *---------------------------------
1810 * ISSUE DOS COMMAND
1820 *---------------------------------
1830 ISSUE.DOS.COMMAND
1840 LDA #$84 CONTROL-D
1850 JSR PRINT.CHAR
1860 JSR PRINT.QUOTE
1870 LDY #0
1880 LDA #' PRINT A SPACE
1890 .5 JSR PRINT.CHAR
1900 CMP #$8D
1910 BEQ .7
1920 CMP #$AC COMMA?
1930 BNE .6
1940 LDA #$8D
1950 STA DOS.BUFFER-1,Y
1960 .6 LDA DOS.BUFFER,Y
1970 INY
1980 BNE .5 ...ALWAYS
1990 .7 RTS
2000 *---------------------------------
2010 * PRINT CHARACTER
2020 *---------------------------------
2030 PRINT.CHAR
2040 PHA
2050 STY PC.SAVEY
2060 ORA #$80
2070 JSR MON.COUT
2080 LDY PC.SAVEY
2090 PLA
2100 RTS
2110 PC.SAVEY .BS 1
2120 *---------------------------------
2130 * PRINT A QUOTATION
2140 *---------------------------------
2150 PRINT.QUOTE.NEXT
2160 INY
2170 PRINT.QUOTE
2180 LDA QTS,Y
2190 JSR PRINT.CHAR
2200 BPL PRINT.QUOTE.NEXT
2210 RTS
2220 *---------------------------------
2230 * CLOSE ALL FILES
2240 *---------------------------------
2250 CLOSE.FILE
2260 JSR MON.CROUT
2270 LDA #$84
2280 JSR PRINT.CHAR CONTROL-D
2290 LDY #QCLOSE-QTS
2300 JMP PRINT.QUOTE
2310 *---------------------------------
2320 * LIST SOURCE PROGRAM
2330 *---------------------------------
2340 ASM.LIST
2350 LDA PP
2360 STA SRCP
2370 LDA PP+1
2380 STA SRCP+1
2390 .1 LDA SRCP
2400 CMP HIMEM
2410 LDA SRCP+1
2420 SBC HIMEM+1
2430 BCS .2 FINISHED
2440 JSR ASM.LIST.LINE
2450 JMP .1
2460 .2 RTS
2470 *---------------------------------
2480 * LIST ONE SOURCE LINE
2490 *---------------------------------
2500 ASM.LIST.LINE
2510 JSR GNB SKIP OVER BYTE COUNT
2520 JSR GNB GET LINE NUMBER
2530 STA LINNUM
2540 JSR GNB
2550 STA LINNUM+1
2560 JSR PRINT.LINNUM
2570 LDA #' BLANK
2580 .1 JSR PRINT.CHAR
2590 JSR NEXT.TOKEN
2600 CMP #0
2610 BNE .1
2620 JMP MON.CROUT
2630 *---------------------------------
2640 GNB LDY #0
2650 STY BLANK.COUNT
2660 LDA (SRCP),Y
2670 GNBI INC SRCP
2680 BNE .1
2690 INC SRCP+1
2700 .1 RTS
2710 *---------------------------------
2720 NEXT.TOKEN
2730 LDY #0
2740 LDA BLANK.COUNT
2750 BNE .1
2760 LDA (SRCP),Y
2770 BPL GNBI
2780 AND #$7F
2790 STA BLANK.COUNT
2800 JSR GNBI
2810 .1 DEC BLANK.COUNT
2820 LDA #' BLANK
2830 RTS
2840 *---------------------------------
2850 * PRINT LINE NUMBER
2860 *---------------------------------
2870 PRINT.LINNUM
2880 LDX #3 PRINT 4 DIGITS
2890 .3 LDA #'0 SET DIGIT TO ASCII ZERO
2900 .1 PHA PUSH DIGIT ON STACK
2910 SEC SUBTRACT CURRENT DIVISOR
2920 LDA LINNUM
2930 SBC PLNTBL,X
2940 PHA SAVE BYTE ON STACK
2950 LDA LINNUM+1
2960 SBC PLNTBH,X
2970 BCC .2 LESS THAN DIVISOR
2980 STA LINNUM+1
2990 PLA GET LOW BYTE OFF STACK
3000 STA LINNUM
3010 PLA GET DIGIT FROM STACK
3020 ADC #0 INCREMENT DIGIT
3030 BNE .1 ...ALWAYS
3040 .2 PLA DISCARD BYTE FROM STACK
3050 PLA GET DIGIT FROM STACK
3060 JSR PRINT.CHAR
3070 DEX NEXT DIGIT
3080 BPL .3
3090 RTS RETURN
3100 *---------------------------------
3110 PLNTBL .DA #1
3120 .DA #10
3130 .DA #100
3140 .DA #1000
3150 PLNTBH .DA /1
3160 .DA /10
3170 .DA /100
3180 .DA /1000 |
| A Use for the USR Command | Bob Sander-Cederlof |
The S-C Assembler II Version 4.0 has one user-programmable command, called "USR". (The Quick Reference Card spells it erroneously "USEr".) One good use for it is to re-print the current symbol table.
After an assembly, if the listing was not printed, it is often desirable to be able to see what the spelling or value of a symbol or group of symbols is. If the VAL command is not enough for you, then the following steps will set up the USR command to re-list the symbol table on the screen. And, if your printer is selected, it will also print there.
Get into the assembler, by using BRUN ASMDISK 4.0 from either Applesoft or Integer BASIC. Type "$1E4EL" after the prompt. The first two lines listed should be "LDY #$02" and "STY $E1". If they are not, you have a different version. (It may still be version 4.0, but slightly different.) The "LDY#$02" line is the first instruction of the symbol table printing subroutine.
Patch the USR vector by typing "$1007:4E 1E", and then BSAVE the result like this:
:BSAVE ASMDISK 4.0 (WITH USR),A$1000,L$14FB |
This new version, whenever you type "USR", will print out the current symbol table. It will look exactly the same as the symbol table pritned out at the end of an assembly.
| A Simulated Numeric Key-Pad | Bob Sander-Cederlof |
This little program will turn part of your Apple's keyboard into a simulated numeric key-pad. A lot cheaper than buying a real one! It is set up to run in page 3, and assumes you are using DOS. If not, just change line 1120 to an RTS.
If you BRUN it or CALL it at 768, the input vector is patched to input all characters through the NKP program. Typing a control-S will toggle the numeric key-pad translator on and off. When the translator is off, all keyboard action is normal, except that another control-S will turn it back on again. When the translator is on, all keys which are not part of the simulated key-pad will input normally.
The keys translated by the simulator are listed in line 1390. The slash key duplicates RETURN, because it is easier to hit when yu are entering a lot of numbers. For the same reason, the L-key duplicates "-", in case you are in a hurry to enter negative numbers too. The space bar is used for "0". I set it up to use "NM," for "123", "HJK" for "456", and "YUI" for "789". You shuld be able to easily change these translations to any other combination, by changing lines 1390-1420.
The heart of the translator is the search loop in lines 1240-1280. If the input character is not found in CHRTBL, the search loop drops out and the character is not changed. If the character is found, line 1310 picks up the alias for the key, and returns. That's all there is to it!
1000 *---------------------------------
1010 * NUMERIC KEY PAD FOR APPLE
1020 *---------------------------------
1030 .OR $300
1040 .TF B.NKP
1050 *---------------------------------
1060 LDA #1
1070 STA TOGGLE
1080 LDA #NKP
1090 STA $38
1100 LDA /NKP
1110 STA $39
1120 JMP $3EA
1130 *---------------------------------
1140 TOGGLE .BS 1
1150 SAVEY .BS 1
1160 *---------------------------------
1170 NKP
1180 JSR $FD1B
1190 CMP #$93 CONTROL-S
1200 BEQ .4
1210 BIT TOGGLE
1220 BMI .2 NOT IN NUMERIC MODE
1230 STY SAVEY
1240 LDY #TBLSIZ-1
1250 .1 CMP CHRTBL,Y
1260 BEQ .3 FOUND IN TABLE
1270 DEY
1280 BPL .1
1290 LDY SAVEY
1300 .2 RTS
1310 .3 LDA ALIAS,Y
1320 LDY SAVEY
1330 RTS
1340 .4 LDA TOGGLE
1350 EOR #$80
1360 STA TOGGLE
1370 JMP $FD0C
1380 *---------------------------------
1390 CHRTBL .AS -/L NM,HJKYUI
1400 TBLSIZ .EQ *-CHRTBL
1410 ALIAS .HS 8D
1420 .AS --0123456789
1430 *---------------------------------
|