Volume 5 -- Issue 5 | February 1985

In This Issue...

- 18-Digit Arithmetic, Part 10
- Questions and Answers
- Patches Available for Time/Date in Titles
- Write Guard Disk Modification Kit
- Assembly Language for the Applesoft Programmer, a Review
- Making DOS-Less Disks
- Correction for Symbol Table Source Maker
- Building Hi-Res Pre-Shift Tables

65816 News -- Talked with Bill Mensch a few days ago, and he expects full production in just a few weeks. There should be a lot of sources soon. Bill has a few more great chips in mind, upgrading the 6502 family even further.

David Eyes is writing a detailed programmer's reference manual for the 65816, to be published about July by Brady. Bill says it should answer all our questions. I'll be reviewing it as soon as possible.

We hear of a 6MHz 65816 board with 256K RAM for plugging into Apples. Let you know when we learn more details.

Woz News -- We hear Steve, Wendell Sander (/// designer), and Joe Ennis (//c designer have teamed up to form a new enterprise, outside Apple, with plans to produce a device for the home video market.

Apple II Forever College -- If you would like in-depth training in Cupertino, $500 buys 3 days under the masters. One session starts March 6th, another May 8th. Call Marian Djurovich at (408) 973-6411 for details.

18-Digit Arithmetic, Part 10 | Bob Sander-Cederlof

At least one error crept into the PRINT USING program we printed last month. A line should be inserted to correct the problem:

          3045     JSR PRUS.CLEAR    YES, NEW FIELD

This is what I expect to be the final installment of the DP18 series. Some of you have been typing in and trying out the various installments, and others buying the source code on the various quarterly disks. We plan to make the composite DP18 source available at a reasonable price: all parts will be properly integrated as a set of 12 source files, ready to assemble with the S-C Macro Assembler. The disk will also include example programs illustrating the various features, the object file of DP18, and a loader program for installing DP18. The price for all of it, on one diskette, will be $50.

Normal Applesoft INPUT statements can be written in several ways. An optional quotation can be used for a prompting message; if one is used a semicolon must follow the quotation. A list of one or more variables follows.

           INPUT variable
           INPUT "quote";variable

In DP18 we implemented the two forms of the INPUT statement shown above, except that only a single variable may be used in each statement. We also implemented two additional kinds of INPUT statements. INPUT# statements allow expressions to be entered during execution. INPUT$ statements allow picture- controlled input.

           INPUT # variable
           INPUT # "quote";variable
           INPUT $ string,variable-list

The INPUT# statement allows you to read expressions and evaluate them during an INPUT operation. This can greatly simplify entering some numbers. For example, one-third can be entered as either ".3333333333333333333333" or simply as "1/3". You can enter values such as SQR(2), 2*PI, and so on. You can even refer to variables used in the program. After you have entered the expression and typed RETURN, DP18 calls on Applesoft to tokenize the line, evaluates the expression to a numeric value, and stores the value in the INPUT variable your program specified.

We call the INPUT$ statement "INPUT using". It is analogous to "PRINT using", or the PRINT$ statement discussed last month. All characters in the INPUT$ picture are proccessed the same as for PRINT$ until characters defining a numeric or string field are encountered. Then the magic begins....

For a numeric field, underlines are printed to indicate digit positions. The cursor is placed after the last underline. If there is a decimal point in the picture it will be printed. A plus sign in the picture will also be printed. All other positions of the field will be printed as underlines. Once the field has been displayed in this fashion, DP18 will check the current value in the variable corresponding with the field. If the current value is zero, DP18 merely waits for you to enter digits. If the current value is non-zero, that value is displayed in the field on the screen, to be used as a default value.

When INPUT$ is waiting for you to enter a numeric value, you can type the RETURN key to accept the default value. If no default value is displayed and you type the RETURN key, you will be entering a value of zero. If you begin to type digits, they will enter the field from the right end in "calculator style". Using backspace will cause the displayed value to be popped to the right, deleting the last digit you typed. One digit will be deleted each time you type backspace.

If you type a period, enough zeroes will be automatically entered to reach the displayed decimal point. This makes the digits you typed before the period into an integer. Then as you continue to type digits they will be appended after the decimal point. If you type more fractional digits than can be seen in the displayed field, they do become part of the input value; you just cannot see them on the screen. The value on the screen is rounded up if necessary.

A control-X will erase everything you have typed in the current field and allow you to start over. A control-C will immediately BREAK, stopping the program.

If you type a backspace when there are no digits remaining in a field, DP18 will attempt to go back to the previous field in the same picture. This will only work if the screen has not scrolled during the development of the picture, and requires a little bit of planning. (Isn't that what programming is all about?)

Probably it is time for an example.

          100 &DP: INPUT $ "HV>>'ENTER X:  '###.#/
              'ENTER Y:  '###.#",X(0),Y(0)

Remember how to read pictures from last month's article? The "H" all by itself sets the horizontal cursor position to 0 (beginning of the line). Likewise, "V" sets us to the top line. The ">>" clears from cursor to end of screen. Therefore the "HV>>" does the same thing as a normal HOME command, but from within a picture. The string between apostrophes is printed on the screen. Then "###.#" defines a numeric field, corresponding to the variable X(0). The "/" causes a carriage return to be displayed, and then "ENTER Y:" and the second field.

During execution you will first see the screen clear and the top line become "ENTER X: ___._" followed by a flashing cursor. You can type digits, a sign, a decimal point, backspace, and so on. When you finally type the RETURN a second line will appear: "ENTER Y: ___._". If you then type a backspace, the cursor will move back to the first line, displaying as a default value whatever you left in that line.

And what about string fields in the INPUT$ command? Again, underlines will be displayed for each position of the string field. If the string already is non-null, its current value will be displayed as a default.

The code that follows is, as has been our practice throughout the DP18 series, preceded by some .EQ lines to define routines previously published, or part of the Apple ROMs. Variable storage is also defined. In the integrated source all these definitions are only done once, and the whole program is assembled together.

When the main execution loop of DP18 encounters the INPUT token, we land at line 1840. Lines 1850-1860 get the character following INPUT, and abort with SYNTAX ERROR if that character is a colon or end-of-line token. Lines 1870-1910 handle INPUT$, by merely loading up zero in the A-register and jumping to PRINT.INPUT (which was listed last month as part of the PRINT USING code). The zero value will be stored in a flag, indicating to PRINT.INPUT later on that it was called from INPUT$ rather than PRINT$. When the picture processor encounters a numeric or string field description in the picture either INPUT.NUM or INPUT.STR will be called, rather than PRINT.NUM or PRINT.STR.

Lines 1930-2510 handle the normal INPUT and INPUT# modes. The character which follows INPUT is stored at INPUT.TYPE, to be checked later. If that character was "#", line 1960 gets the next character to position properly for scanning optional quote or the variable name. Lines 1970-2120 process the optional quote. If it is not there, a "?" prompt is used; it it is there, the string itself is printed. Lines 2090-2110 make a ";" optional after the quote. Normal Applesoft INPUT requires a semicolon after the quote, but DP18's INPUT makes it optional. In fact, you could even get by with a whole bunch of semicolons, if you feel like it....

Lines 2140-2190 read a line of text. If the first character of the line is a control-C, we abort just like Applesoft. An empty line returns a zero value, using line 2500-2510.

Lines 2210-2270 set up the input line, which begins at $0200 (WBUF), so that it can be scanned using CHRGET, after pushing current TXTPTR value on the stack. If the INPUT.TYPE was "#", AS.PARSE and DP.EVALUATE convert the expression down to a value. If not, FIN converts the number string to a value. I could have used PARSE and EVALUATE regardless, but it would take a lot more time to convert plain numbers that way. Lines 2400-2430 restore the old value of TXTPTR, so that we can continue scanning the program.

Lines 2440-2480 scan the input variable name, and store the converted value in that variable. Then back to DP18's main loop to get the next command!

If we are processing an INPUT$ statement, chances are good that we will input a number. If so, the picture processor will call on INPUT.NUM at line 2530. WBUF at this time holds the image of the numeric field description, as amplified from the picture. Lines 2540-2600 copy it into IBUF, because we are going to clobber the WBUF version everytime we re-display the value being entered. IBUF is currently assembled as a 256 byte buffer, which is quite extravagant. Probably this is an area where things could be tightened up, if you need the memory space.

The code beyond line 2530 is hard to follow. I am reminded of the original Adventure game, and its twisty little passages, little twisty passages, and so on. I am going to give it a broad brush, and those of you with an intense interest can explore in more detail on your own.

As each digit is typed, it is appended to the numeric value by ACCUMULATE.DIGIT. Then, after refreshing the picture of the field from IBUF, the value is reconverted to display format and shown on the screen. It may sound inefficient, but it all works nicely. Trimming off digits when backspace is typed is done by truncating the DP18 value and then redisplaying.

LAST.FLD is the routine that tries to back up input to a previous field when you type backspace beyond the first digit. At the beginning of each field, all the necessary parameters are pushed on DP18's stack. LAST.FLD pops these back to move to a previous field. Guess what ... I forgot to check for stack overflow in the STACK.IT subroutine. Should be no problem, however, because only five bytes are stacked for each field, there is room for 24 fields. Since a picture must necessarily be less than 256 characters (maximum length of an Applesoft string) thereby limiting the number of fields, it is unlikely that you will have more than 24 fields stack up. If you think it important to have more, you had better increase the size of STACK.

String input is handled in an analogous fashion by INPUT.STR, starting at line 4970.

As I mentioned before, this is my final article on DP18. But maybe not, if you want more. Some of you might send improvements, corrections, or whatever, and I might pass them along in these pages.

DP18 works, and works well; we're proud of it. You can use DP18 in your programs, even those you plan to sell. Just give us credit where appropriate in your documentation. Remember, you can get all the source code already typed in and integrated together from us for only $50.

      1000 *SAVE S.DP18 INPUT
      1010 *-------------------------------
      1020 *    APPLESOFT SUBROUTINES
      1030 *-------------------------------
      1040 AS.INLIN     .EQ $D52E   READ A LINE
      1050 AS.PARSE     .EQ $D559   PARSE INPUT BUFFER
      1060 AS.BREAK     .EQ $D863   CTRL-C BREAK
      1070 AS.ADDON     .EQ $D998   ADD (Y) TO TXTPTR
      1080 AS.COUT      .EQ $DB5C    PRINT A CHARACTER
      1090 AS.CHKCOM    .EQ $DEBE    CHECK FOR COMMA
      1100 AS.SYNERR    .EQ $DEC9    SYNTAX ERROR
      1110 AS.GETSPA    .EQ $E452
      1120 AS.MOVSTR    .EQ $E5E2
      1130 *--------------------------------
      1140 *   MONITOR SUBROUTINES
      1150 *--------------------------------
      1160 MON.RDKEY    .EQ $FD0C
      1170 MON.LF       .EQ $FC66
      1180 *--------------------------------
      1190 *      DP SUBROUTINES PRINTED ELSEWHERE
      1200 *--------------------------------
      1210 DP.NEXT.CMD         .EQ $FFFF
      1220 DP.EVALUATE         .EQ $FFFF
      1230 MOVE.DAC.YA         .EQ $FFFF
      1240 DP.VTAB             .EQ $FFFF
      1250 DP.INT              .EQ $FFFF
      1260 DP.FALSE            .EQ $FFFF
      1270 MOVE.DAC.TEMP1      .EQ $FFFF
      1280 MOVE.TEMP1.DAC      .EQ $FFFF
      1290 PRINT.INPUT         .EQ $FFFF
      1300 FIN                 .EQ $FFFF
      1310 GET.A.VAR           .EQ $FFFF
      1320 CHECK.DP.VAR        .EQ $FFFF
      1330 MOVE.YA.DAC         .EQ $FFFF
      1340 PRUS.CLEAR          .EQ $FFFF
      1350 PRUS.NEXT           .EQ $FFFF
      1360 ACCUMULATE.DIGIT    .EQ $FFFF
      1370 PRT.NUM.1           .EQ $FFFF
      1380 PRINT.STR.1         .EQ $FFFF
      1390 *-------------------------------
      1400 *      PAGE ZERO USAGE
      1410 *-------------------------------
      1420 AS.VALTYP    .EQ $11
      1430 MON.WNDWIDTH .EQ $21
      1440 MON.CH       .EQ $24
      1450 MON.CV       .EQ $25
      1460 AS.FRESPA    .EQ $71,72
      1470 AS.CHRGET    .EQ $B1
      1480 AS.CHRGOT    .EQ $B7
      1490 TXTPTR       .EQ $B8,B9
      1500 P2           .EQ $F9
      1510 P1           .EQ $FD      GP POINTER
      1520 *--------------------------------
      1530 WBUF         .EQ $0200
      1540 *-------------------------------
      1550 *      WORK AREAS FOR DPFP
      1560 *-------------------------------
      1570 DECFLG              .BS 1
      1580 DAC.EXPONENT        .BS 1
      1590 DAC.SIGN            .BS 1
      1600 IBUF                .BS 256
      1610 STACK.PNTR          .BS 1
      1620 STACK               .BS 12*10
      1630 W                   .BS 1
      1640 D                   .BS 1
      1650 OLD.W               .BS 1
      1660 OLD.D               .BS 1
      1670 DGTCNT              .BS 1
      1680 INPUT.TYPE          .BS 1
      1690 FOUND.NUM           .BS 1
      1700 FOUND.STR           .BS 1
      1710 FOUND.LEN           .BS 1
      1720 FOUND.CHAR          .BS 1
      1730 FILL.CHAR           .BS 1
      1740 ZERO.CHAR           .BS 1
      1750 FLD.FLAG            .BS 1
      1760 FLD.START           .BS 1
      1770 TEMP                .BS 2
      1780 RESULT              .BS 2
      1790 DEFAULT.FLAG        .BS 1
      1800 LEN                 .BS 1
      1810 *--------------------------------
      1820 DP.SYN3 JMP AS.SYNERR
      1830 *--------------------------------
      1840 DP.INPUT
      1850        JSR AS.CHRGET
      1860        BEQ DP.SYN3  ...COLON OR EOL
      1870 *---INPUT USING------------------
      1880        CMP #'$'     INPUT USING PICTURE?
      1890        BNE .1       ...NO
      1900        LDA #0       ...YES, SIGNAL "INPUT" AND JOIN
      1910        JMP PRINT.INPUT       "PRINT $"
      1920 *---INPUT AN EXPRESSION----------
      1930 .1     STA INPUT.TYPE    ="#" IF EXP, ELSE <>"#"
      1940        CMP #'#'          INPUT AN EXPRESSION?
      1950        BNE .2            ...NO
      1960        JSR AS.CHRGET     ...YES, GET NEXT CHAR
      1970 .2     LDX #"?"     PROMPT CHAR FOR NO QUOTE
      1980        CMP #'"'     QUOTE?
      1990        BNE .6       ...NO, SIMPLE INPUT
      2000        LDY #0       ...YES, PRINT IT NOW
      2010 .3     INY
      2020        LDA (TXTPTR),Y    NEXT QUOTED CHARACTER
      2030        BEQ DP.SYN3       ...NO CLOSING QUOTE
      2040        CMP #'"'          CLOSING QUOTE YET?
      2050        BEQ .4            ...YES
      2060        JSR AS.COUT       ...NO, PRINT CHARACTER
      2070        BNE .3            ...ALWAYS
      2080 .4     JSR AS.ADDON      ADD (Y) TO TXTPTR
      2090 .5     JSR AS.CHRGET     SCAN NEXT CHAR
      2100        CMP #';'          ALLOW OPTIONAL SEMICOLON
      2110        BEQ .5            ...KEEP LOOKING TILL NOT ';'
      2120        LDX #$80          NULL PROMPT CHARACTER
      2130 *---READ A LINE OF TEXT----------
      2140 .6     JSR AS.INLIN      '?' OR NULL PROMPT
      2150        LDA WBUF          CHECK FOR EMPTY LINE
      2160        BEQ .11           ...EMPTY LINE
      2170        CMP #$03          CTRL-C?
      2180        BNE .7            ...NO
      2190        JMP AS.BREAK      ABORT INPUT
      2200 *---PARSE THE INPUT LINE---------
      2210 .7     LDA TXTPTR        SAVE TXTPTR, WHICH POINTS
      2220        PHA               AT THE PROGRAM
      2230        LDA TXTPTR+1
      2240        PHA
      2250        STX TXTPTR        MAKE TXTPTR POINT AT INPUT BUFFER
      2260        STY TXTPTR+1
      2270        JSR AS.CHRGET     GET FIRST CHAR FROM LINE
      2280        LDY INPUT.TYPE    SEE IF SIMPLE OR EXPRESSIONS
      2290        CPY #'#'
      2300        BNE .8            SIMPLE NUMERIC INPUT
      2310        JSR AS.PARSE      EXPRESSION INPUT, SO PARSE
      2320        LDA #WBUF-1       POINT AT INPUT BUFFER AGAIN
      2330        STA TXTPTR        SO EVALUATE CAN PROCESS THE
      2340        LDA /WBUF-1       PARSED LINE
      2350        STA TXTPTR+1
      2360        JSR AS.CHRGET     SCAN FIRST CHAR
      2370        JSR DP.EVALUATE   EVALUATE THE EXPRESSION
      2380        JMP .9
      2390 .8     JSR FIN           SIMPLE NUMERIC INPUT
      2400 .9     PLA               RESTORE TXTPTR TO PROGRAM
      2410        STA TXTPTR+1
      2420        PLA
      2430        STA TXTPTR
      2440 .10    JSR AS.CHRGOT     GET CURRENT PROGRAM CHAR
      2450        JSR GET.A.VAR     GET INPUT VARIABLE
      2460        JSR CHECK.DP.VAR  MUST BE DP18 VARIABLE
      2470        JSR MOVE.DAC.YA   STORE INPUT VALUE
      2480        JMP DP.NEXT.CMD   ...FINISHED?
      2490 *---EMPTY INPUT LINE-------------
      2500 .11    JSR DP.FALSE      RETURN VALUE = 0
      2510        JMP .10
      2520 *--------------------------------
      2530 INPUT.NUM
      2540        LDA #0       TERMINATE STRING IN BUFFERS
      2550        STA IBUF,X
      2560        STA WBUF,X
      2570 .1     LDA WBUF-1,X      COPY STRING TO IBUF
      2580        STA IBUF-1,X
      2590        DEX
      2600        BNE .1
      2610        LDA FILL.CHAR
      2620        STA TEMP
      2630        JSR STACK.IT
      2640        JSR AS.CHKCOM     MUST HAVE COMMA
      2650        JSR GET.A.VAR
      2660        JSR CHECK.DP.VAR
      2670        STA RESULT   SAVE ADR OF VARIABLE
      2680        STY RESULT+1
      2690        JSR MOVE.YA.DAC  MOVE DEFAULT INTO DAC
      2700        LDA W
      2710        STA OLD.W
      2720        LDA #1
      2730        STA DEFAULT.FLAG
      2740        LDA DAC.EXPONENT  IS DAC 0?
      2750        BNE INP.X1   NO
      2760 INP.X  JSR INP.ZERO.DAC DEFAULT IS 0 OR CTRL-X
      2770 INP.X1 LDA #0
      2780        STA FLD.FLAG
      2790        STA DGTCNT
      2800        STA DECFLG
      2810        LDA D
      2820        STA OLD.D
      2830        LDA #$5F     UNDERLINE
      2840        STA FILL.CHAR
      2850 INP.NEXT.ZERO.CHAR
      2860        STA ZERO.CHAR
      2870 *--------------------------------
      2880 INP.NEXT
      2890        JSR INP.PRINT.NUM PRINT THE NUMBER
      2900        JSR MOVE.TEMP1.DAC
      2910        JSR MON.RDKEY
      2920        AND #$7F
      2930        CMP #$0D     RETURN?
      2940        BEQ .2       ...YES
      2950        LDX DEFAULT.FLAG
      2960        BEQ .1       NO DEFAULT
      2970        JSR INP.ZERO.DAC IGNORE DEFAULT
      2980        CMP #8       BACKSPACE?
      2990        BEQ INP.NEXT YES,IGNORE
      3000 *---DIGIT------------------------
      3010 .1     CMP #'0      SEE IF NUMBER
      3020        BCC .4       NO
      3030        CMP #'9+1
      3040        BCS .4       NO
      3050        JSR ACCUMULATE.DIGIT
      3060        JMP INP.NEXT
      3070 *---CARRIAGE RETURN--------------
      3080 .2     LDA DGTCNT   IS NUMBER 0?
      3090        ORA DEFAULT.FLAG
      3100        BNE .3       NO
      3110        STA DAC.EXPONENT YES,SO ZERO THE EXPONENT
      3120 .3     LDA RESULT   GET ADR OF VAR
      3130        LDY RESULT+1
      3140        JSR MOVE.DAC.YA  PUT IT IN VAR
      3150        LDA TEMP     RESTORE ORIGINAL FILL CHAR
      3160        STA FILL.CHAR
      3170        LDA #'0
      3180        STA ZERO.CHAR
      3190        JMP INP.PRINT.NUM PRINT THE NUMBER
      3200 *                        AND RETURN
      3210 *---DECIMAL POINT----------------
      3220 .4     CMP #'.      DEC POINT?
      3230        BNE .5       ...NO
      3240 *      SEC          'CMP' LEFT CARRY SET
      3250        ROR DECFLG   FOUND DEC PT
      3260        BIT DECFLG
      3270        BVS INP.NEXT       TWO DEC PTS.
      3280        LDA #$40
      3290        CLC
      3300        ADC DGTCNT
      3310        STA DAC.EXPONENT
      3320        LDA #'0
      3330        BEQ INP.NEXT.ZERO.CHAR  ALWAYS
      3340 *---MINUS SIGN-------------------
      3350 .5     CMP #'-      MINUS?
      3360        BNE .6
      3370 *      SEC          'CMP' LEFT CARRY SET
      3380        ROR DAC.SIGN MAKE DAC NEGATIVE
      3390        BNE INP.NEXT ...ALWAYS
      3400 *---PLUS SIGN--------------------
      3410 .6     CMP #'+      PLUS?
      3420        BNE .7       ...NO
      3430        STA DAC.SIGN PUT POSITIVE VALUE IN SIGN
      3440        BEQ INP.NEXT ...ALWAYS
      3450 *---CTRL-X-----------------------
      3460 .7     CMP #$18     CTRL-X?
      3470        BNE .8
      3480        LDA OLD.D
      3490        STA D
      3500        JMP INP.X
      3510 *---CTRL-C-----------------------
      3520 .8     CMP #$3      CTRL-C?
      3530        BNE .9       ...NO, TRY BACKSPACE
      3540        JMP AS.BREAK
      3550 *---BACKSPACE--------------------
      3560 .9     CMP #$08     BACKSPACE?
      3570        BNE .17      ...NO, TAKE PATH TO INP.NEXT
      3580        LDA DECFLG
      3590        BPL .10
      3600        LDA DAC.EXPONENT
      3610        SEC
      3620        SBC #$40
      3630        CMP DGTCNT
      3640        BEQ .15      REMOVE DEC PT ONLY
      3650 *--------------------------------
      3660 .10    LDA DAC.EXPONENT
      3670        PHA          SAVE EXPONENT
      3680        LDA DGTCNT
      3690        CLC
      3700        ADC #$3F
      3710        STA DAC.EXPONENT
      3720        JSR DP.INT   CHOP OFF LAST DIGIT
      3730        LDA DAC.EXPONENT
      3740        BEQ .14      THE NUMBER IS 0, SO RESET EVERYTHING
      3750 .11    PLA
      3760        STA DAC.EXPONENT
      3770        LDA DGTCNT
      3780        BNE .12
      3790        JSR LAST.FLD
      3800        JMP INP.NEXT
      3810 .12    DEC DGTCNT
      3820        BNE .13
      3830        DEC DAC.EXPONENT
      3840 .13    LDA DECFLG
      3850        BPL .16      DELETE BY SHIFT
      3860        BMI .17      ALWAYS
      3870 *--------------------------------
      3880 .14    LDA DECFLG
      3890        BPL .11
      3900        PLA
      3910 .15    LDA #$3F
      3920        SEC
      3930        SBC OLD.D
      3940        ADC DGTCNT
      3950        STA DAC.EXPONENT
      3960        LDA #0
      3970        STA DECFLG
      3980        LDA #$5F
      3990        JMP INP.NEXT.ZERO.CHAR
      4000 *--------------------------------
      4010 .16    LDA DGTCNT
      4020        BEQ .17
      4030        DEC DAC.EXPONENT
      4040 .17    JMP INP.NEXT
      4050 *--------------------------------
      4060 INP.PRINT.NUM
      4070        LDX #-1      COPY IBUF TO WBUF
      4080 .1     INX
      4090        LDA IBUF,X
      4100        STA WBUF,X
      4110        BNE .1
      4120        JSR RESTORE.HV.FROM.STACK
      4130        LDA OLD.W
      4140        STA W
      4150        LDA OLD.D
      4160        STA D
      4170        JSR MOVE.DAC.TEMP1
      4180        LDA DECFLG
      4190        PHA
      4200        JSR PRT.NUM.1
      4210        PLA
      4220        STA DECFLG
      4230        RTS
      4240 *--------------------------------
      4250 INP.ZERO.DAC
      4260        PHA
      4270        JSR DP.FALSE PUT 0 IN DAC
      4280        LDA #$40
      4290        SEC
      4300        SBC D        CALCULATE EXPONENT
      4310        STA DAC.EXPONENT
      4320        LDA #0
      4330        STA DEFAULT.FLAG
      4340        PLA
      4350        RTS
      4360 *--------------------------------
      4370 LAST.FLD
      4380        LDY STACK.PNTR
      4390        DEY
      4400        DEY
      4410        DEY
      4420        DEY
      4430        DEY
      4440        BNE .1
      4450        RTS          FIRST FIELD
      4460 .1     PLA          DISCARD JSR LAST.FLD
      4470        PLA               "
      4480        PLA          DISCARD JSR INPUT.NUM
      4490        PLA               "
      4500        PLA          DISCARD Y-REG
      4510        PLA          DISCARD JSR PRT.NUM.IF.NEEDED
      4520        PLA               "
      4530        PLA          DISCARD JSR LOOKUP
      4540        PLA               "
      4550        DEY
      4560        LDA STACK,Y
      4570        STA TXTPTR+1
      4580        DEY
      4590        LDA STACK,Y
      4600        STA TXTPTR
      4610        DEY
      4620        LDA STACK,Y
      4630        PHA          SAVE INDEX INTO PICTURE
      4640        DEY
      4650        LDA STACK,Y
      4660        JSR DP.VTAB
      4670        DEY
      4680        LDA STACK,Y
      4690        STA MON.CH
      4700        STY STACK.PNTR
      4710        PLA          RESTORE INDEX INTO PICTURE
      4720        TAY
      4730        JSR PRUS.CLEAR
      4740        JMP PRUS.NEXT
      4750 *--------------------------------
      4760 STACK.IT
      4770        LDY STACK.PNTR
      4780        LDA MON.CH   SAVE WHERE THE FIELD IS
      4790        STA STACK,Y
      4800        INY
      4810        LDA MON.CV
      4820        STA STACK,Y
      4830        INY
      4840        DEC FLD.START
      4850        LDA FLD.START
      4860        STA STACK,Y
      4870        INY
      4880        LDA TXTPTR
      4890        STA STACK,Y  SAVE TXTPTR
      4900        INY
      4910        LDA TXTPTR+1
      4920        STA STACK,Y
      4930        INY
      4940        STY STACK.PNTR
      4950        RTS
      4960 *--------------------------------
      4970 INPUT.STR
      4980        JSR STACK.IT
      4990        JSR AS.CHKCOM   MUST HAVE COMMA
      5000        JSR GET.A.VAR   GET ADR OF VAR
      5010        LDX AS.VALTYP   STR OR NUM
      5020        BMI .1       OK
      5030        JMP AS.SYNERR   MUST BE STRING
      5040 .1     STA P1
      5050        STY P1+1
      5060        LDY #0       GET STRING
      5070        STY DEFAULT.FLAG
      5080        STY FLD.FLAG
      5090        STY LEN
      5100        LDA (P1),Y   LENGTH
      5110        BEQ .3       NULL STRING, SO DO NOTHING
      5120        STA LEN
      5130        INY
      5140        LDA (P1),Y   ADR OF STRING
      5150        STA P2       LO ADR
      5160        INY
      5170        LDA (P1),Y
      5180        STA P2+1     HI ADR
      5190        LDY LEN      GET LENGTH
      5200        DEY
      5210 .2     LDA (P2),Y
      5220        STA WBUF,Y
      5230        DEY
      5240        BNE .2
      5250        LDA (P2),Y   MOVE LAST BYTE
      5260        STA WBUF
      5270        INY          Y = 1
      5280        STA DEFAULT.FLAG  YES THERE IS A DEFAULT
      5290 .3     LDA #WBUF
      5300        STA P2
      5310        LDA /WBUF
      5320        STA P2+1
      5330        BNE IS.X1    ALWAYS
      5340 *--------------------------------
      5350 IS.X   LDA #0
      5360        STA LEN
      5370 IS.X1  LDA FOUND.LEN
      5380        PHA
      5390        LDA FOUND.CHAR
      5400        PHA
      5410        JSR RESTORE.HV.FROM.STACK
      5420        LDA #$5F     UNDERLINE
      5430        STA FILL.CHAR
      5440        LDA LEN
      5450        JSR PRINT.STR.1
      5460        PLA
      5470        STA FOUND.CHAR
      5480        PLA
      5490        STA FOUND.LEN
      5500        CMP LEN
      5510        BCC .3       OVERFLOW
      5520 *---FIND END OF STRING & PUT CURSOR THERE---
      5530        JSR RESTORE.HV.FROM.STACK
      5540        CLC
      5550        ADC LEN      ADD LENGTH OF STRING
      5560 .1     CMP MON.WNDWIDTH LONGER THAN WINDOW?
      5570        BCC .2
      5580        SBC MON.WNDWIDTH WRAP AROUND
      5590        PHA
      5600        JSR MON.LF   JUMP DOWN TO NEXT LINE
      5610        PLA
      5620        JMP .1
      5630 .2     STA MON.CH   PUT COLUMN BACK IN CH
      5640 *---INPUT A CHAR NOW-------------
      5650 .3     JSR MON.RDKEY
      5660        AND #$7F
      5670 *---CARRIAGE RETURN--------------
      5680        CMP #$0D     RETURN?
      5690        BNE .5       ...NO
      5700        LDA DEFAULT.FLAG
      5710        BNE .4       DEFAULT, SO LEAVE IT ALONE
      5720        LDA LEN      GET LENGTH
      5730        JSR AS.GETSPA MAKE ROOM FOR STRING
      5740        LDY #0       MOVE DATA INTO VARIABLE
      5750        STA (P1),Y   LENGTH
      5760        LDA AS.FRESPA
      5770        INY
      5780        STA (P1),Y   LO ADDRESS
      5790        LDA AS.FRESPA+1
      5800        INY
      5810        STA (P1),Y   HI ADDRESS
      5820        LDX #WBUF
      5830        LDY /WBUF
      5840        LDA LEN
      5850        JSR AS.MOVSTR
      5860 .4     JSR RESTORE.HV.FROM.STACK
      5870        LDA #$20     SPACE
      5880        STA FILL.CHAR
      5890        LDA LEN
      5900        JMP PRINT.STR.1   PRINT IT ONE MORE TIME
      5910 *--------------------------------
      5920 .5     LDX DEFAULT.FLAG
      5930        BEQ .6       ...NO DEFAULT
      5940        LDX #0
      5950        STX DEFAULT.FLAG GET RID OF DEFAULT
      5960        STX LEN      NULL STRING
      5970        CMP #8       BACKSPACE AND DEFAULT?
      5980        BNE .8
      5990        JMP IS.X1
      6000 *---BACKSPACE--------------------
      6010 .6     CMP #8       BACKSPACE?
      6020        BNE .8
      6030        LDA LEN
      6040        BNE .7
      6050        JSR LAST.FLD BACKUP A FIELD
      6060        JMP IS.X1
      6070 .7     DEC LEN
      6080        JMP IS.X1
      6090 *---CTRL-X-----------------------
      6100 .8     CMP #$18     CTRL-X?
      6110        BNE .9
      6120        JMP IS.X
      6130 *---CTRL-C-----------------------
      6140 .9     CMP #3       CTRL-C?
      6150        BNE .10      ...NO
      6160        JMP AS.BREAK
      6170 *---CHAR FOR STRING--------------
      6180 .10    LDY LEN      NORMAL CHAR,
      6190        STA WBUF,Y   SAVE IT
      6200        INC LEN
      6210        JMP IS.X1
      6220 *--------------------------------
      6230 RESTORE.HV.FROM.STACK
      6240        LDY STACK.PNTR
      6250        LDA STACK-4,Y
      6260        JSR DP.VTAB
      6270        LDA STACK-5,Y
      6280        STA MON.CH
      6290        RTS
      6300 *--------------------------------

Questions and Answers | Q: Various Readers A: Bill and Bob

First Question:

I have just finished installing Version 2.0 of your assembler, and I have a few questions.

a. First, how is the line length of the escape-L changed? The short line looks ridiculous on an 80-column screen. I would also like to change the first character from "*" to ";".

b. How can I get the assembler to initialize things with DOS's MON CI modes set?

c. In working with big programs, it is easy to exceed line number 9999. It happens all the time. As new lines get added, the formatting of lines around 9999 goes haywire, as the spacing is done according to the line number at the time of entry. Thus when a line number changes from 4 to 5 digits or vice versa due to renumbering the opcode and operand columns no longer line up properly. What can be done about the erratic column alignment?

d. I noticed that the symbol table generated by an assembly takes more memory with version 2.0 than it did with 1.1. Why?

e. There appear to be two errors in the sample program S.INLINE on the Macro 2.0 disk. The comment on how to use it shows a comma between the &INPUT and the string variable, when the program in fact requires that there be NO comma. Then, the first line of the main routine does a CMP, which should be an LDA. With these corrections, the program is great. &INPUT will accept input from keyboard or disk, and reads the complete record including commas, quotes, and colons. This I find rather useful.

Mike Lawrie, South Africa

Our Answer:

> a. The routine which generates the star-dash line starts at $DB21, with the following:

>            TXA
>            BEQ ...
>            LDA #$AA        change to $BB for ";"
>            JSR ...
>            LDA $D01E   ("-" CHAR)
>            CPX #$26        increase as you like

> For example, I changed mine just now like this:

>            $C083 C083 DB25:BB N DB2D:46

> b. Whatever selections you have turned on with the MON command are turned off by the DOS "INT" or "FP" commands. I guess if you want the MONCI modes all the time you could add code to the assembler to set the proper bits inside DOS. The flags are in $AA5E: C=$40, I=$20, O=$10. Store $60 into $AA5E to effect MONCI.

> c. I agree with you that it is annoying the way the columns stagger when the line numbers are near 9999. There are several possible solutions. One solution, is to start line numbers at 10000. You can do this by changing the code at $D32B:

>            LDA #990       change to #9990
>            STA ...
>            LDA /990       change to /9990

> A better way is to make a the line numbers always print with five digits. To effect this, change the code at $DE63:

>            LDX #3  change to LDX #4

>            $C083 C083 DE64:4

> d. The symbol table does indeed take more space in version 2.0 than it did in previous versions. This is due to the fact that symbols can have values up to 32-bits long. Every symbol has two more bytes in the table now.

> e. Right on both counts. Disks with serial numbers 1186 and larger have the corrections you give.

Second Question:

Is there any way of loading a program from the monitor (without going back to Basic) or reload DOS or reboot without losing what is in memory?

Munson Compton, Shreveport, LA

Our Answer:

> If you entered the monitor via CALL-151 from Basic, or MNTR or MGO-151 from the S-C Macro Assembler, DOS is still alive and will still respond to commands. You can BLOAD or LOAD a program, but of course using LOAD will flip you into either Applesoft, Integer BASIC, or the Macro Assembler depending on file type and what languages are around. If you want to stay in the monitor after the LOAD file has been read into memory, you could temporarily patch the DOS LOAD code which starts at $A413. The book "Beneath Apple DOS" would be helpful here. It looks to me like you could so subvert type A files by patching the JMP ($9D60) at $A44D to RTS (by putting 60 at $A44D). Type I files might be tricked by putting an RTS (60) at $A5AF. I don't know what other ramifications these patches might have. Beware!!!

> You can reboot a slave disk without losing the actual text of an assembler source file from memory. However, the pointer which tells the assembler where the program starts will be reset. Before rebooting, record the value stored in $CA and $CB, and after getting back into the assembler restore those two bytes. Of course, if the assembler is in the language card rebooting DOS marks it as not being there. From the monitor you can put it all back by typing:

>            ]CALL-151
>            *C081 C081 E000:20
>            *INT
>            :$CA:...(whatever values you recorded earlier)
>            :LIST   (Voila!)

Third Question:

I have the Apple ToolKit and the Big Mac assemblers, and use them primarily to key in source files from articles such as yours. I've figured out how to transpose most of the different labels and opcodes, but would like some enlightenment on the use of the .1, .2, .3 etc. labels that are repeated in the code. I assume this is a capability of your assembler that others don't have.

David Roberson

Our Answer:

> For help in converting our listings to other assemblers and vice versa, you should refer to my "Directory of Assembler Directives" article in the September 1982 AAL. You are correct in assuming that most other assemblers do not have the kind of local labels as the S-C assemblers, but some do. These numeric labels are one or two digits after a period, and are very convenient for branch points within a sub- routine. They are defined below a normal label, and are only accessible within that area. The local labels are defined internally relative to the preceding normal label, and must be within a 255-byte range after the normal label. Once a new normal label is defined, a whole new set of local labels is available. The use of local labels simplifies programming, because there is no need to think up dozens of unique names like LOOP1, LOP2, LUPA, LUPB, and so on. Local labels also encourage writing good modular code, with only one entry point per module, since the local labels are not accessible outside the routine in which they are defined.

> The LISA assembler uses a different type of numeric label, which I call a near-by label. These are redefinable at will, and when they are referenced a pointer must be included to tell the assembler which direction to search for the definition. You can refer to the nearest definition in either a forward or backward direction. I get thoroughly confused trying to read and/or modify programs using these.

Patches Available for Time/Date in Titles | R. M. Yost

I have implemented a patch to include a Thunderclock (or compatible) time string in the .TItle for version 2.0 of the S-C Macro Assembler. The patch program automatically loads the assembler and my favorite I/O driver, installs the time patch and several others I like, and writes the assembler back on the disk. The new file includes both assembler and driver, with the patches, as well as a loader which allows the whole thing to be executed with a single BRUN.

I will gladly send a listing of the source code to any Assembly Line reader who is interested. Just send a stamped self- addressed envelope to R.M.Yost, 7436 Pointe, Canton, MI 48187.

Write Guard Disk Modification Kit | Bob Sander-Cederlof

Mark IV Designs (Mark Hansen) has come up with a neat way to override the write protect switch in a disk drive. Sometimes you want to write on the back side of a disk, in spite of all good breeding. Yet it is a nuisance to have to cut a notch in the other edge of the disk. We finally bought a hole punch, but it is still a nuisance. Other times you want to write protect a disk, but not put one of those little sticky things over the existing notch. What to do?

Instructions for adding an external toggle switch in series or in parallel with the internal sensor are easy to come by, but who wants to drill holes and solder? The Write Guard kit from Mark IV Desings accomplishes all you could wish for without any drilling, cutting, or soldering.

You get a small (1x2x3 inches) box with three-position toggle switch and LED. A short flat cable runs out the back, and you plug that into a socket inside the disk drive (after removing the 74125 from that socket). A piece of velcro attaches the plastic box to either side of your drive. The switch selects normal, always protected, or always unprotected. The LED lights whenever the disk is not protected. One chip on the disk controller card also is replaced with a chip from the kit.

If this kit sounds like something you have been waiting for, you can order one from us at $40.

Review of "Assembly Language for the Applesoft Programmer" | reviewed by Bob Sander-Cederlof

Roy E. Myers (author of Microcomputer Graphics) and C.W. Finley, Jr., are the authors of the new book named above, and published by Addison-Wesley. We like it.

Until August of last year we consistently recommended Roger Wagner's "Assembly Lines: the Book" when you asked us which book would best help you learn Apple assembly language. It was especially well-suited to beginners at assembly language who were nevertheless somewhat familiar with the Apple and Applesoft. But it went out of print with the demise of Softalk Publishing, and we can't get them now.

Finley and Myers have not only filled the void, they have improved on our previous favorite. Physically, the book is larger (7x9, paper, 361 + vi pages). It is set in large clear type. And it only costs $16.95 (Wagner's book was $19.95). I especially like the fact that they use the S-C assembler for all of the examples. However, if you don't use our assembler, the book loses no value; all the examples are written so as to be as compatible as possible with other possible assemblers.

Take another look at that title: "Assembly Language for the Applesoft Programmer." There is a double meaning there. This is not only a text for the Applesoft programmer who wants to learn beginning assembly language. It also for the person who wants to USE assembly language along with Applesoft programs. Combining both languages gives the best of both worlds, but doing so involves a lot of work. This book will help.

The book divides into five main sections:

- Introduction
- Fundamentals of 6502 Programming: 6502 architecture, instruction set; addressing; branches, loops, nesting; logical operations and bit manipulation.
- Linkage: fitting a program into the Apple; accessing machine language programs via BLOAD, POKE, USR, ctrl-Y, and "&"; soft switches; using Applesoft ROM subroutines, esp. floating point math; development of a working example.
- Graphics: the Screen, its organization and addressing with text, lo-res, and hi-res; ROM routines for lo- and hi-res graphics; bit-pattern images and animation; bit-masking techniques and complementary drawing; development of a working shoot-em-up video game (GREMLIN).
- Searching and Sorting: &-routine to sort array elements; another to search strings.

There are five useful appendices and an index.

We think enough of this book to add it to our stock. Check our list of books on page 3 for price.

Making Dos-less Disks | Bob Sander-Cederlof

Last night I re-invented the wheel, and I think I made a pretty good one. I learned a little at the same time.

When you use the DOS "INIT" command, a copy of DOS is written on tracks 0 through 2. If the disk is meant to be a data disk, that wastes three perfectly good tracks. Because of the way DOS checks for the end of track-sector lists and various other things, a standard DOS cannot allow files to be written into track 0. But it is perfectly all right to leave the DOS image off of tracks 1 and 2 and use them for files. Of course it is a good idea to change the image on track 0 so that it will not begin to boot DOS and get lost (when you forget it is DOS-less and try to boot it anyway).

There are some more wasted sectors in track 17, the catalog track. INIT sets up 15 sectors for the catalog, which is enough for 105 files. I have never needed that many, but some of you might have even needed more. Last night I needed only about 30 files, and I needed every sector I could get to store them all. My "wheel" sets up only seven catalog sectors, enough for only 49 files. This frees up eight more sectors for data.

With the help of "Beneath Apple DOS" I examined the code in the DOS File Manager which handles the INIT command ($AE8E-AF07). This routine calls RWTS to initialize 35 empty tracks on a diskette, writes a VTOC in track 17 sector 0 and writes 15 empty catalog sectors on the rest of track 17. Then it scoots back to track 0 and writes the DOS image on the first three tracks.

I used Rak-Ware's DISASM to make a source file out of the INIT code, and then loaded it into the S-C Macro Assembler. Then step-by-step I proceeded to add meaningful labels and comments, and modify the code to do what I wanted.

The File Manager INIT code expects various parameters to have been set up by the DOS command parser, and those will not be set up when my program runs. I decided I would let my program assume that the last disk drive you accessed is the one where you have placed the blank disk you want to initialize.

I also decided to make the volume number always 001. I always do this anyway, and generally consider the volume number to be a nuisance (since I don't have a Corvus which uses the volume numbers for something useful). If you want to be able to choose the volume number, you could add the code for that purpose. Lines 1240-1270 set the volume number into the VTOC image and into the RWTS parameter block (IOB).

Lines 1290-1300 call RWTS to format the blank diskette. Beware! It is entirely too easy to forget to remove your heavily loaded program diskette before running this program! Be absolutely SURE you have the diskette in the drive which you WANT to initialize. After this program runs, the disk will have no remnant of any data which may have been on it before.

Lines 1310-1570 set up a VTOC image. The program assumes that part of the VTOC image at $B3BB is already set up, because you could not run this program without having read at least one VTOC somewhere along the way. The VTOC bitmap is set up first to $FFFF0000 at each sector position, and then the entry for track 0 is cleared. Finally the bits for sector 0 and sectors 9 through 15 of track 17 are cleared. Then lines 1580-1640 call on RWTS to write out the VTOC on track 17, sector 0.

The catalog sectors are chained together with a series of pointers. A pointer in the VTOC points to the first catalog sector, which is almost always track 17 sector 15. A pointer in the first catalog sector points to the second one, and so on. The last catalog sector points at track 0, which is a flag indicating the end of the catalog. (Too bad, because if DOS tested for a final pointer to 0,0 instead of just 0,x we could put the catalog for this data disk all in track 0 and free up even more sectors.)

Lines 1650-1700 clear the catalog buffer, and then lines 1710-1900 insert the forward pointers and call on RWTS to write each sector on the disk.

Finally, lines 1910-2000 write out a bootup program on track 0 sector 0. BOOTER is the code that will be executed if you accidentally try to boot our DOS-less disk.

Lines 2010-2090 finish setting up a call to RWTS, and check for an I/O error. I didn't bother to write any error handler into this program, as you can see by the BRK in line 2090. If you want you can printout the DOS error code at this point, or at least get it in the A-register before the BRK.

The BOOTER program is tricker than it looks. Anyway it tricked me a lot. First notice the .PH and .EP directives in lines 2120 and 2280. These tell the assembler to continue assembling bytes following the preceding code, but to assemble it with the assumption that at execution time it will be origined at $0800. The boot ROM on the disk controller reads track 0 sector 0 into $800-$8FF, so BOOTER has to be set up to run there.

Notice line 2140, which is ".HS 01" The boot ROM reads the first sector into $800-8FF, then checks location $800 to see how many sectors you want the boot ROM to read. About the only disk I have heard of which has anything other than 01 in this byte is the BASICS disk. If you put, for example, 03 in that byte sectors 1 and 2 would be read into $900 and $A00. You can read up to 16 sectors this way, but remember that the sector numbers will not be the same as the ones you use when you write them with RWTS. (RWTS uses a table to convert logical sector numbers into physical sector numbers.)

Line 2150 turns off the disk motor. I forgot the first time, and of course the drive just kept spinning.

Lines 2160-2210 print out the message from lines 2240-2270. My first attempt I called the standard COUT subroutine at $FDED to print each character, and I lost an hour finding out why I never saw my message. Instead, the drive just kept grinding the head to track 0, over and over and over.... But it worked if I first copied the boot ROM code from $C600 down to $8600, and typed 8600G to boot. I finally figured out that PR#6 sets the output hook to slot 6 and leaves it there. Then the next character that is printed (usually the prompt character for whatever language you are in) through COUT goes to the disk interface and proceeds to boot. My message sent another character to COUT and restarted the boot, ad infinitum. Changing line 2190 to "JSR $FDF0" fixed it all.

After printing the message line 2220 jumps to the initial entry point of the monitor, so you get a "*" prompt. If you previously had DOS in memory, you will probably be able to use 3D0G to get back to BASIC or the assembler or whatever. Otherwise, stick in a disk that DOES have DOS and try booting again.

Line 2300 is just window dressing. It assures that the rest of track 0 sector 0 will have nothing but zeroes in it. No particular value, but I like it that way.

      1000 *SAVE S.DOSLESS INIT
      1010 *--------------------------------
      1020 RWTS       .EQ $03D9
      1030 GETIOB     .EQ $03E3
      1040 *--------------------------------
      1050 VTOC       .EQ $B3BB
      1060 V.VOLUME   .EQ $B3C1
      1070 V.NXTTRK   .EQ $B3EB
      1080 V.DIRECT   .EQ $B3EC
      1090 V.BITMAP   .EQ $B3F3
      1100 *--------------------------------
      1110 CATALOG.BUFFER .EQ $B4BB
      1120 C.TRACK        .EQ $B4BC
      1130 C.SECTOR       .EQ $B4BD
      1140 *--------------------------------
      1150 R.PARMS    .EQ $B7E8
      1160 R.VOLUME   .EQ $B7EB
      1170 R.TRACK    .EQ $B7EC
      1180 R.SECTOR   .EQ $B7ED
      1190 R.BUFFER   .EQ $B7F0,B7F1
      1200 R.OPCODE   .EQ $B7F4
      1210 *--------------------------------
      1220        .OR $800
      1230 *--------------------------------
      1240 DOSLESS.INIT
      1250        LDA #1       INIT AS VOLUME 001
      1260        STA R.VOLUME
      1270        STA V.VOLUME
      1280 *--------------------------------
      1290        LDA #$04     INIT OPCODE FOR RWTS
      1300        JSR CALL.RWTS.OP.IN.A
      1310 *---MAKE A GENERIC VTOC----------
      1320        LDA #$11
      1330        STA V.NXTTRK
      1340        STA R.TRACK
      1350        LDY #1
      1360        STY V.DIRECT      FORWARD DIRECTION
      1370        DEY          Y=0
      1380        STY R.SECTOR
      1390 *---PREPARE BITMAP---------------
      1400        LDY #4*35
      1410 .1     LDA #0
      1420        DEY
      1430        STA V.BITMAP,Y
      1440        DEY
      1450        STA V.BITMAP,Y
      1460        DEY
      1470        LDA #$FF
      1480        STA V.BITMAP,Y
      1490        DEY
      1500        STA V.BITMAP,Y
      1510        BNE .1
      1520        STY V.BITMAP      CANNOT ALLOCATE TRACK 0
      1530        STY V.BITMAP+1
      1540        INY                  Y=1, RESERVE F...9
      1550        STY 4*17+V.BITMAP         FREE SECTOR 8
      1560        LDA #$FE                  RESERVE 0
      1570        STA 4*17+V.BITMAP+1       FREE 7...1
      1580 *---WRITE VTOC ON NEW DISK-------
      1590        LDA #VTOC
      1600        STA R.BUFFER
      1610        LDA /VTOC
      1620        STA R.BUFFER+1
      1630        LDA #2            RWTS WRITE OPCODE
      1640        JSR CALL.RWTS.OP.IN.A
      1650 *---PREPARE CATALOG SECTOR-------
      1660        LDX #$00
      1670        TXA
      1680 .2     STA CATALOG.BUFFER,X
      1690        INX
      1700        BNE .2
      1710 *---WRITE CATALOG CHAIN----------
      1720        LDA #CATALOG.BUFFER
      1730        STA R.BUFFER
      1740        LDA /CATALOG.BUFFER
      1750        STA R.BUFFER+1
      1760        LDA #17      TRACK 17
      1770        LDY #15      START IN SECTOR 15
      1780 .3     STA C.TRACK
      1790 .4     STY R.SECTOR
      1800        DEY
      1810        STY C.SECTOR
      1820        JSR CALL.RWTS
      1830        LDY C.SECTOR
      1840        CPY #9
      1850        BNE .4
      1860        STY R.SECTOR
      1870        LDY #0
      1880        STY C.TRACK
      1890        STY C.SECTOR
      1900        JSR CALL.RWTS
      1910 *---WRITE BOOT SECTOR------------
      1920 .5     LDA #BOOTER
      1930        STA R.BUFFER
      1940        LDA /BOOTER
      1950        STA R.BUFFER+1
      1960        LDA #0
      1970        STA R.TRACK
      1980        STA R.SECTOR
      1990        JSR CALL.RWTS
      2000        RTS
      2010 *--------------------------------
      2020 CALL.RWTS.OP.IN.A
      2030        STA R.OPCODE
      2040 CALL.RWTS
      2050        JSR GETIOB
      2060        JSR RWTS
      2070        BCS .1       ERROR
      2080        RTS
      2090 .1     BRK
      2100 *--------------------------------
      2110 BOOTER
      2120        .PH $800
      2130 BOOTER.PHASE
      2140        .HS 01
      2150        LDA $C088,X  MOTOR OFF
      2160        LDY #0
      2170 .1     LDA MESSAGE,Y
      2180        BEQ .2
      2190        JSR $FDF0
      2200        INY
      2210        BNE .1
      2220 .2     JMP $FF59
      2230 *--------------------------------
      2240 MESSAGE
      2250        .HS 8D8D8787
      2260        .AS -/NO DOS IMAGE ON THIS DISK/
      2270        .HS 8D8D00
      2280        .EP
      2290 *--------------------------------
      2300        .BS 256,0
      2310 *--------------------------------

Correction for Symbol Table Source Maker | Bob Sander-Cederlof

I went to great lengths to verify the address of the entry into RENUMBER used by Peter's and Bruce's program, and the day after picking up the printed newsletters Bill discovered that I had used a pre-release copy of Version 2.0. The address in the actual release is different. The correct line 1060 for the version we are sending out is:

           1060 RENUMBER .EQ $D658   for the D000 version
        or 1060 RENUMBER .EQ $1658   for the 1000 version

In any case, just be sure the address is the location of the CPX #$06 instruction.

Building Hi-Res Pre-Shift Tables | Gianluca Pomponi Pisa, Italy

Given my interest in everything related to graphics, I read eagerly Bob's article "Generating Tables..." in the Dec 94 issue of AAL. I haven't yet had the chance to read the Apple Supplement of Byte (my local newsstand receives it discontinuously); however, I had already heard about the use of preshift tables in animation. I experimented with this technique some time ago, getting excellent results in moving colored shapes against some very complex backgrounds with relatively simple code.

Maybe one of the most challenging steps is typing in the preshift tables. Writing a program to generate the tables is not difficult, and is probably better. The code that follows only takes $68 bytes as a subroutine, using two page zero variables. And it only takes 24 milliseconds to generate the tables, which is many times faster than reading them from a disk.

The Byte article used 14 tables of 256 bytes each. They correspond to left and right portions of each possible 8-bit value shifted any amount from 1 to 7 bits. No columns are kept in memory for shifting 0 bits, as the result is entirely too predictable.

Since, in hi-res graphics, the high bit does not get shifted, you can deal with it separately. Before looking up the preshifted values you can split off the high bit and rejoin it later. The extra code for this is very minor, and it results in a vast memory saving. By doing it this way we get by with 12 tables of 128 bytes each (six pages instead of 14!). Six tables for the left side results and six for the right, for every possible shift of from 1 to 6 bits, for every possible value from $00 to $7F.

I sometimes find it worthwhile to limit the quotient-remainder tables such as Bob generated in the December article to only 256 bytes each (instead of 280), using code like the following to read them when the X-coordinate is larger than 255:

           LDX XCOORD      low byte of xcoord
           LDA QUO+4,X
           CLC
           ADC #$24
           STA XBYTE
           LDA REM+4,X
           STA XBIT

Here now is my program to generate the preshift tables, as modified by Bob. LInes 1080-1210 allocate space for the 12 tables, each 128 bytes long. I put them at $0900 for this example, but of course you can put them wherever you wish.

Lines 1230-1310 are a macro definition. The macro is called out six times in the main loop, once for each shift of a value. For the benefit of those without a macro assembler, I have shown the expansion in the listing of lines 1430-1480. Some of the code in the macro could have been handled by a subroutine, but it would save a negligible amount of space at a cost of an non-negligible amount of time.

The shifting algorithm is familiar to those of you who have been fiddling with hi-res for a while. Remember that the picture bits are stored backwards in each byte, so that shifting the picture on the screen right one bit requires shifting the bits in memory left within each byte, stepping over bit 7, and from byte to byte in a left-to-right direction.

The little program called TIME, lines 1530-1660, calls the BUILD program 1000 times. I ran it and clocked it at a little less than 24 seconds, which means building once took less than 24 milliseconds. The tables would take up six disk sectors if they were stored part of the program on disk. The disk spins at 300 rpm, or 200 milliseconds per revolution. The absolute minimum time to read six sectors would be 67.5 milliseconds, but in actual practice it takes closer to a half second. It depends on whether it is part of a larger file or stored as a separate file, the latter taking longer. Since the program only needs to be executed once, even the memory it occupies it available to the program for other purposes.

      1000 *SAVE S.BUILD.PRESHIFT.TABLES
      1010 *--------------------------------
      1020 *   WRITTEN BY G. L. POMPONI, PISA, ITALY
      1030 *   MODIFIED BY BOB SANDER-CEDERLOF
      1040 *--------------------------------
      1050 L.BYTE .EQ 0
      1060 R.BYTE .EQ 1
      1070 *--------------------------------
      1080            .OR $900
      1090 SHIFT.1    .BS 128
      1100 SHIFT.2    .BS 128
      1110 SHIFT.3    .BS 128
      1120 SHIFT.4    .BS 128
      1130 SHIFT.5    .BS 128
      1140 SHIFT.6    .BS 128
      1150 *--------------------------------
      1160 REMND.1    .BS 128
      1170 REMND.2    .BS 128
      1180 REMND.3    .BS 128
      1190 REMND.4    .BS 128
      1200 REMND.5    .BS 128
      1210 REMND.6    .BS 128
      1220 *--------------------------------
      1230        .MA SHIFT
      1240        ASL L.BYTE
      1250        ROL R.BYTE
      1260        LDA L.BYTE
      1270        LSR
      1280        STA SHIFT.]1,X
      1290        LDA R.BYTE
      1300        STA REMND.]1,X
      1310        .EM
      1320 *--------------------------------
      1330        .OR $800
      1340 *--------------------------------
      1350 BUILD.PRESHIFT.TABLES
      1360        LDX #0       FOR X = 0 TO $7F
      1370 *--------------------------------
      1380 .1     STX L.BYTE
      1390        LDA #0
      1400        STA R.BYTE
      1410        ASL L.BYTE
      1420 *--------------------------------
      1430        >SHIFT 1
      1440        >SHIFT 2
      1450        >SHIFT 3
      1460        >SHIFT 4
      1470        >SHIFT 5
      1480        >SHIFT 6
      1490 *--------------------------------
      1500        INX          NEXT X
      1510        BPL .1       (...UNTIL $80)
      1520        RTS
      1530 *--------------------------------
      1540 *   BUILDS 1000 TIMES IN LESS THAN 24 SECONDS,
      1550 *      SO LESS THAN 24 MILLISECONDS TO BUILD ONCE
      1560 *--------------------------------
      1570 TIME   LDA #4       4*250 = 1000
      1580        STA $500
      1590 .1     LDY #250
      1600 .2     JSR BUILD.PRESHIFT.TABLES
      1610        DEY
      1620        BNE .2
      1630        DEC $500
      1640        BNE .1
      1650        RTS
      1660 *--------------------------------

Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $12 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE CORPORATION, all rights reserved. (Apple is a registered trademark of Apple Computer, Inc.)