Volume 1 -- Issue 1 | October 1980

Welcome to the premier issue of the Apple Assembly Line!

This new monthly newsletter is dedicated to the many Apple owners using assembly language, or who would like to learn how. Articles will include commented disassemblies of Apple ROM routines, DOS, and other commercial software; how to augment and modify existing products; beginner's lessons in assembly language; handy subroutines every programmer needs in his tool kit; and many more.

In this issue you will find a tutorial on efficient ways to increment and decrement multiple-byte values, a very powerful subroutine for formatting messages on the screen, and patch code for the S-C ASSEMBLER II Version 4.0 to "adapt" it to the Paymar Lower-Case Adapter. There is also an article describing a recently reported error found in ALL 6502 chips, and a brief announcement of some new products from S-C SOFTWARE.

Since there will be a lot of source code printed in this and forthcoming issues of the Apple Assembly Line, I plan to offer quarterly diskettes containing all published source code (in the format of the S-C ASSEMBLER II Version 4.0) at a nominal price. How does $15 per quarter sound? Of course, you can always type it in.... The articles should be considered copy-righted, but feel free to use the code in any way you can. It is printed here for your enlightenment, entertainment, and for your USE. I hope you find it all helpful.

I do not know all there is to know about the 6502, or the Apple, or about anything! Nor do I have an infinite amount of time. Therefore, I will be happy to accept articles and programs from you. I may print them exactly as you write them, or I may modify them first. In any case, you will get credit, and the satisfaction of knowing you are helping many others in their conquest of the computer.

If you know others who should be receiving this newsletter, spread the word! If you are not subscribing yet, then send your $12 today! If you have any comments about the content, format, or whatever, write now! Or, you can call me during reasonable hours at (ddd) ddd-dddd.

Sincerely, <<signature>> Bob Sander-Cederlof

In This Issue...

- How to Add and Subtract One
- New Products from S-C SOFTWARE
- General Message Printing Subroutine
- Using the Paymar Lower-Case Adapter
- Hardware Error in ALL 6502 Chips!

How to Add and Subtract One | Bob Sander-Cederlof

I suppose there are as many ways to do it as there are programmers. Some are short and fast, some long and slow, some neat, some not so neat.

Adding one to a number is called “incrementing”, and subtracting one is called “decrementing”. The 6502 has two instructions for these two functions: INC and DEC. (For the moment I will overlook the four instructions for doing the same to the X and Y registers: INX, INY, DEX, and DEY.) It is easy to see how to use INC and DEC on single-byte values; with a little more trouble we can also use them for values of two or more bytes.

         Adding one using ADC
         CLC
         LDA VALL   Add 1 to low
         ADC #1        byte
         STA VALL
         LDA VALH   Add carry to
         ADC #0        high byte
         STA VALH
         Adding one using INC
         INC VALL   Add 1 to low byte
         BNE .1     Skip if no carry
         INC VALH   Add carry
    .1   ....

Of course, there are many other variations. Not indicated here are the various address modes possible in addressing VALL and VALH. They may be in page zero, or elsewhere. They may be directly addressed, as shown above; or, you may use the indirect indexed, indexed indirect, and plain indexed modes.

It is easy to see how to extend both methods above to triple precision, or more. Here is a three-byte version using INC:

         INC VALL   Increment low byte
         BNE .1     Unless zero, no carry
         INC VALM   Increment middle byte
         BNE .1     Unless zero, no further carry
         INC VALH   Increment high byte
    .1   ....

Believe it or not, there is one disadvantage to using INC code like this. Sometimes code is required to have a constant running time regardless of the data values. Then, you either must use straight line code with ADC instructions, or add complicated padding code to the INC form.

How about subtracting one? Here are two ways to do it to two-byte values:

         Decrementing with SBC
         SEC
         LDA VALL   Low byte - 1
         SBC #1
         STA VALL
         LDA VALH   High byte - borrow
         SBC #0
         STA VALH
         Decrementing with DEC
         LDA VALL   Need to borrow?
         BNE .1     No
         DEC VALH   Yes, hi-byte - 1
    .1   DEC VALL   Low byte - 1

Which one do you like better? It is still a matter of taste, unless the amount of memory used or time consumed is very important. There are also different side effects, such as the final state of the Carry status. INC and DEC do not change the Carry status, while of course ADC and SBC do. You may wish to preserve Carry through the process, making the INC/DEC code preferable. Or, you may wish to know the resulting Carry status after incrementing or decrementing for some reason; then you should use the ADC/SBC code.

Back to subtracting one...how about doing it to a three-byte value? We just add three more lines:

         LDA VALL   See if need to borrow
         BNE .2     No
         LDA VALM   See if need to borrow again
         BNE .1     No
         DEC VALH   Borrow from high byte
    .1   DEC VALM   Borrow from middle byte
    .2   DEC VALL   Decrement low byte

Easier than you thought, right? You would not believe the many strange ways I have seen this operation coded in commercial software (even some released by Apple themselves!). Yet it seems to me that this method is the same way we would do it with pencil and paper in decimal arithmetic. Think how you would do this:

                   123040
                       -1
                   ------
                   xxxxxx

If you think of each digit as though it were a byte...isn't the algorithm the same?

Now it is time for all of us to go back over the programs we wrote during the past three years for the Apple, and replace a lot of old code!

Bob Sander-Cederlof, October, 1980.

New Products from S-C SOFTWARE

As many of you know, because you have already bought it, version 4.0 of the S-C Assembler II is now on the market. With this new version, the price has gone up from $35 to $55. An upgrade kit for owners of previous versions is only $22.50

Now another new version is available, for those of you without disks! Tape Version 4.0 requires only 16K RAM and a cassette drive. The price is $45 for the complete package, or $22.50 for an upgrade kit from the previous tape version. All of the new features of Disk Version 3.2 and 4.0 are included, except those which require a disk drive. For the time being, the manual consists of a copy of the disk version 4.0 manuals, with a single sheet describing the differences in the tape version. Purchasers of tape version 4.0 will be able to upgrade to the disk version when they get a disk drive, for only $12.50.

And still another version of the assembler! This one is a cross assembler for the Motorola 6800, 6801, and 6802 microprocessors. It has all the features of the S-C Assembler II Disk Version 4.0, but the source language accepted is that of the 6800 family rather than the 6502. The price for this package is only $300, which is less than a month of time-sharing services for an equivalent capability would cost! An Apple, a ROM blower from Mountain Hardware, and the S-C Assembler II-6800 are all you need for a full-blown development system.

General Message Printing Subroutine | Bob Sander-Cederlof

Formatting a series of nice messages or screens-full of messages is hard enough to do in Applesoft...but in assembly language it can really be a difficult job. And it seems to take so much memory to do the equivalent of VTAB, HTAB, HOME, and PRINT. I was recently motivated to do something about this for a large, verbose program. I designed a general subroutine for printing text, which can print all 128 chracters of ASCII, plus do some fancy footwork on the way.

Embedded control codes in the text to be printed perform such handy functions as HTAB, VTAB, HOME, NORMAL, INVERSE, Clear to End of LIne, Clear to End of Page, Two-Second Delay, and Repeat. All characters to be printed directly are entered with the high-order bit set to one; bytes with the high order bit zero are control codes. Comments in lines 1250-1350 of the listing show what the codes are.

To simplify the calling sequence, a table of message addresses is built along with the messages themselves. To print a specific message, merely load the message index number into the A-register (LDA #0 for the first message, LDA #1 for the second, etc.), and JSR MESSAGE.PRINTER. Some sample messages are given in the listing, starting at line 2240.

There are a lot of unused control codes, which you can use to augment the subroutine. I am planning to add a code to switch to a HI-RES TEXT driver, for writing text on either of the two Hi-Res screens. You can probably think of a lot of useful ones yourself. The point is that this type of subroutine can simplify programming of an interactive program, and save memory too.

         1000  *---------------------------------
         1010  MON.CH     .EQ $24
         1020  MON.CV     .EQ $25
         1030  MON.VTAB   .EQ $FC22
         1040  MON.CLREOP .EQ $FC42
         1050  MON.HOME   .EQ $FC58
         1060  MON.CLREOL .EQ $FC9C
         1070  MON.WAIT   .EQ $FCA8
         1080  MON.COUT   .EQ $FDED
         1090  MON.NORMAL .EQ $FE84
         1100  MON.INVERSE .EQ $FE80
         1110  *---------------------------------
         1120  MSG.PNTR   .EQ $18,19
         1130  MSG.SCANNER .EQ $1A
         1140  *---------------------------------
         1150  *      MESSAGE PRINTER
         1160  *
         1170  *   CALL:
         1180  *      (A) = MESSAGE #   (0-N)
         1190  *      JSR MESSAGE.PRINTER
         1200  *
         1210  *   ACTION:
         1220  *      1.  FINDS SPECIFIED MESSAGE
         1230  *      2.  PRINTS ON THE SCREEN
         1240  *      3.  INTERPRETS CHARACTERS AS FOLLOWS:
         1250  *          $00     END OF MESSAGE
         1260  *          $01-28  HTAB 1-40
         1270  *          $40-57  VTAB 1-24
         1280  *          $60     CLEAR SCREEN, HOME CURSOR
         1290  *          $61XXYY REPEAT CHARACTER YY, XX TIMES
         1300  *          $62     DELAY ABOUT TWO SECONDS
         1310  *          $63     NORMAL MODE
         1320  *          $64     INVERSE MODE
         1330  *          $65     CLEAR TO END OF LINE
         1340  *          $66     CLEAR TO END OF SCREEN
         1350  *          $80-FF  PRINT AS IS
         1360  *
         1370  *---------------------------------
         1380  MESSAGE.PRINTER
         1390         ASL          DOUBLE MSG NUMBER TO GET INDEX
         1400         TAY
         1410         LDA MESSAGE.ADDRESS.TABLE,Y
         1420         STA MSG.PNTR
         1430         LDA MESSAGE.ADDRESS.TABLE+1,Y
         1440         STA MSG.PNTR+1
         1450         LDA #0
         1460         STA MSG.SCANNER
         1470  .1     JSR GET.NEXT.CHAR.FROM.MESSAGE
         1480         BNE .3
         1490         RTS          $00:  EOM
         1500  .3     BPL .5       SPECIAL ACTION
         1510         JSR MON.COUT PRINT THE CHARACTER
         1520  .4     JMP .1
         1530  *---------------------------------
         1540  .5     CMP #$40     CHECK FOR VTAB
         1550         BCS .6       YES
         1560         CMP #$29     IN RANGE FOR HTAB?
         1570         BCS .4       NO, IGNORE
         1580         STA MON.CH
         1590         DEC MON.CH
         1600         BCC .4       ...ALWAYS
         1610  *---------------------------------
         1620  .6     CMP #$58     IN RANGE FOR VTAB?
         1630         BCS .7       NO
         1640         AND #$1F     MASK VALUE
         1650         STA MON.CV   YES
         1660         JSR MON.VTAB
         1670         JMP .4
         1680  *---------------------------------
         1690  .7     EOR #$60     CHECK FOR TOKENS
         1700         CMP #7       $60 THROUGH $66
         1710         BCS .4       NOT TOKEN, SO IGNORE
         1720         ASL          MAKE DUBLE INDEX
         1730         TAX
         1740         LDA /.4-1    PUT RETURN ON STACK
         1750         PHA          TO SIMULATE A JSR ADDR,X
         1760         LDA #.4-1
         1770         PHA
         1780         LDA MSGTKNTBL+1,X
         1790         PHA
         1800         LDA MSGTKNTBL,X
         1810         PHA
         1820         RTS
         1830  *---------------------------------
         1840  MSGTKNTBL
         1850         .DA MON.HOME-1
         1860         .DA MSG.REPEAT-1
         1870         .DA LONG.DELAY-1
         1880         .DA MON.NORMAL-1
         1890         .DA MON.INVERSE-1
         1900         .DA MON.CLREOL-1
         1910         .DA MON.CLREOP-1
         1920  *---------------------------------
         1930  MSG.REPEAT
         1940         JSR GET.NEXT.CHAR.FROM.MESSAGE
         1950         TAX          NUMBER OF MULTIPLES
         1960         JSR GET.NEXT.CHAR.FROM.MESSAGE
         1970  .1     JSR MON.COUT
         1980         DEX
         1990         BNE .1
         2000         RTS
         2010  *---------------------------------
         2020  LONG.DELAY
         2030         LDY #12
         2040  .1     JSR MON.WAIT DELAY 167309 CYCLES
         2050         DEY
         2060         BNE .1
         2070         RTS
         2080  *---------------------------------
         2090  GET.NEXT.CHAR.FROM.MESSAGE
         2100         LDY MSG.SCANNER
         2110         LDA (MSG.PNTR),Y
         2120         INC MSG.SCANNER
         2130         BNE .1
         2140         INC MSG.PNTR+1
         2150  .1     CMP #0
         2160         RTS
         2170  *---------------------------------
         2180  MESSAGE.ADDRESS.TABLE
         2190         .DA MSG0
         2200         .DA MSG1
         2210         .DA MSG2
         2220         .DA MSG3
         2230  *---------------------------------
         2240  MSG0   .HS 60       HOME SCREEN
         2250  * CELL 1 -- VOCABULARY CHECK
         2260         .HS 64       INVERSE MODE
         2270         .HS 6129AD   4A DASHES
         2280         .HS 28ADAD   2 DASHES
         2290         .HS 28ADAD
         2300         .HS 28ADAD   2 DASHES
         2310         .HS 28ADAD   2 DASHES
         2320         .HS 28ADAD   2 DASHES
         2330         .HS 28ADAD   2 DASHES
         2340         .HS 286129AD 41 DASHES
         2350         .HS 63       NORMAL MODE
         2360         .HS 4205     VTAB 3, HTAB 5
         2370         .AS -/DEMONSTRATION OF MESSAGE PRINTER/
         2380         .HS 440F     VTAB 5, HTAB 15
         2390         .AS -/S-C SOFTWARE/       [This business closed in 1988]
         2400         .HS 450E     VTAB 6, HTAB 14
         2410         .AS -/P. O. BOX 5537/     [no longer a valid address]
         2420         .HS 460B     VTAB 7, HTAB 11
         2430         .AS -/RICHARDSON, TX 75080/
         2440         .HS 4A       VTAB 11
         2450         .HS 00
         2460  *---------------------------------
         2470  MSG1   .HS 490166   VTAB 10, HTAB 1, CLR EOP
         2480         .AS -/SELECT ONE:  /
         2490         .HS 00
         2500  *---------------------------------
         2510  MSG2   .HS 570165   VTAB 24, HTAB 1, CLR EOL
         2520         .HS 64       INVERSE MODE
         2530         .AS -/ <SPACE> FOR MENU, <RETURN> FOR MORE /
         2540         .HS 6300     NORMAL MODE, EOM
         2550  *---------------------------------
         2560  MSG3   .HS 87878D
         2570         .AS -/***SYNTAX ERROR/
         2580         .HS 8D00     CARRIAGE RETURN AND END OF MESSAGE

Using the Paymar Lower-Case Adapter with S-C ASSEMBLER II Version 4.0 | Bob Matzinger <phone number>

Since purchasing the Paymar adapter, I have spent a lot of time adapting software to effectively use it! The program given here will adapt the version 4.0 of Bob Sander-Cederlof's assembler to allow lower-case comments.

The two patches at lines 1340 and 1390 have to be entered, and the body of the patch loaded at $300. Once installed, typing a control-A will toggle the shift-lock; control-S will perform a single-character upper-case shift; control-K, -L, and -O give access to the characters normally missing from the Apple keyboard.

Only comments can be entered in lower-case. Further modification to the assembler would be required to allow commands, labels, and opcodes to be entered in lower- or mixed-case.

    	1000  *---------------------------------
    	1010  * Lower case conversion for
    	1020  * S-C ASSEMBLER II Version 4.0
    	1030  * Copyright 1980 by S-C SOFTWARE
    	1040  * Complete with 126 ASCII characters
    	1050  *---------------------------------
    	1060  * The CTRL-A and CTRL-S keys are used similar to
    	1070  * shift and lock keys on a standard typewriter.
    	1080  *
    	1090  * CTRL-A is the shift-lock key.
    	1100  * Each time CTRL-A is pressed the case
    	1110  * will toggle to the opposite mode.
    	1120  *
    	1130  * CTRL-S makes the following character
    	1140  * enter in upper-case.
    	1150  *---------------------------------
    	1160  * REMEMBER!
    	1170  * All commands and mnemonic entries
    	1180  * must be in UPPER case!
    	1190  * Use lower case only for comments!
    	1200  *---------------------------------
    	1210  CTRLA  .EQ $81      SHIFT LOCK
    	1220  CTRLK  .EQ $8B      [ or {
    	1230  CTRLL  .EQ $8C      \ or |
    	1240  CTRLO  .EQ $8F      _ or rubout
    	1250  CTRLS  .EQ $93      SHIFT
    	1260  *---------------------------------
    	1270  * Remember:
    	1280  * shift M yields ] or }
    	1290  * shift N yields ^ or ~
    	1300  * shift P yields @ or `
    	1310  RDKEY  .EQ $FD0C
    	1320  *---------------------------------
    	1330         .OR $1380
    	1340         .TF LC.PATCH1
    	1350         JSR LC
    	1360  *---------------------------------
    	1370         .OR $139A
    	1380         .TF LC.PATCH2
    	1390         AND #$FF
    	1400  *---------------------------------
    	1410         .OR $300
    	1420  * CAUTION: Do not assemble your programs into
    	1430  * $0300 up.  You will destroy this routine!!!
    	1440  LC     JSR RDKEY
    	1450         CMP #CTRLA
    	1460         BEQ LOCK
    	1470         CMP #CTRLS
    	1480         BNE CHECK
    	1490  SHIFT  LDA #0
    	1500         STA LCKFLG
    	1510  SHIFT1 LDA #0
    	1520         STA CASE
    	1530         BEQ LC       ...ALWAYS
    	1540  LOCK   LDA LCKFLG
    	1550         EOR #1
    	1560         STA LCKFLG
    	1570         BNE SHIFT1
    	1580         LDA #$20
    	1590         STA CASE
    	1600         BNE LC       ...ALWAYS
    	1610  CHECK  CMP #CTRLK
    	1620         BEQ SPEC
    	1630         CMP #CTRLL
    	1640         BEQ SPEC
    	1650         CMP #CTRLO
    	1660         BNE CONV
    	1670  SPEC   ORA #$50
    	1680  CONV   CMP #$C0
    	1690         BCC RETURN
    	1700         ORA CASE
    	1710  RETURN PHA
    	1720         LDA LCKFLG
    	1730         BNE OUT
    	1740         LDA #$20
    	1750         STA CASE
    	1760  OUT    PLA
    	1770         RTS
    	1780  LCKFLG .DA #0
    	1790  CASE   .DA #$20
    	1800  *---------------------------------
    	1810  * Written by Bob Matzinger
    	1820  * September 6, 1980
    	1830  *---------------------------------

Hardware Error in ALL 6502 Chips! | Bob Sander-Cederlof

INTERFACE, the newsletter of Rockwell International (P. O. Box 3669, RC 55, Anaheim, CA 92803), Issue No. 2, is the source for the following information. It should be noted by all Apple owners working in assembly language, because it could cause an almost unfindable bug!

There is an error in the JUMP INDIRECT instruction of ALL 6500 family CPU chips, no matter where they were made. This means the error is present in ALL APPLES. This fatal error occurs only when the low byte of the indirect pointer location happens to be $FF, as in JMP ($08FF). Normally, the processor should fetch the low-order address byte from location $08FF, increment the program counter to $0900, and then fetch the high-order address byte from $0900. Instead, the high-order byte of the program counter never gets incremented! The high-order address byte gets loaded from $0800 instead of $0900! For this reason, your program should NEVER include an instruction of the type JMP ($xxFF).

Try this example to satisfy yourself that you understand the problem: insert the following data from the monitor.

          *800:09          the actual hi-byte used
          *810:6C FF 08    this is JMP ($08FF) [indirect through the address at $08FF]
          *8FF:50 0A       the lo-byte and the expected hi-byte jump address
          *A50:00          BRK instruction we SHOULD reach
          *950:00          BRK instruction we DO reach!

Execute the instruction at $0810 by typing 810G. If the JMP indirect worked correctly, it would branch to location $0A50 and execute the BRK instruction there. However, since the JMP indirect instruction has this serious flaw, it will actually branch to the BRK instruction at $0950!

Since it is very difficult to predict the final address of all pointers in a large assembly language program, unless they are all grouped in a block at the beginning of the program, I suggest that you take special measures to protect yourself against this hardware problem. (One measure, of course, was suggested in that sentence.) My favorite method is to avoid using the JMP indirect instruction. It takes too long to set it up in most cases anyway. I prefer to push the branch address (less one) onto the stack, and RTS to effect the branch. This allows me to create the effect of an indexed JMP. For example, suppose a command character is being decoded. I process it into a value in the A-register between 0 and N-1 (for N commands), and do the following:

         ASL                  Double to create index
         TAX                  for address table
         LDA JUMP.TABLE+1,X   High order byte
         PHA                  of branch address
         LDA JUMP.TABLE,X     Low order byte
         PHA                  of branch address
         RTS

The jump table looks like this:

         JUMP.TABLE
         .DA COMMANDA-1   The "-1" is
         .DA COMMANDB-1   on each line
         .DA COMMANDC-1   because the RTS
         .DA COMMANDD-1   adds one before
         et cetera        branching.

This trick was described by Steve Wozniak in an article in BYTE magazine back in 1977 or 1978. It is also used by him in the Apple monitor code, and in SWEET-16. In both of these cases, he has arranged all the command processors to be in the same page, so that the high order byte of the address can be loaded into the A-register with a load-A-immediate, and the jump table can be only one-byte-per-command. See your Apple ROMs at locations $FFBE-FFCB (jump table at $FFE3-FFF9) and in SWEET-16 at $F69E, F6A0, F684-F6B8 (jump table at $F6E3-F702).

You can extend this idea of an indexed JMP instruction into a simulated indexed JSR instruction. All you have to do is first push onto the stack the return address (less one), and then the branch address (less one). I use this trick in the Message.Printer program described elsewhere in this issue.