Apple Assembly Line
Volume 5 -- Issue 5 February 1985

In This Issue...

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:

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 is 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.)