Volume 3 -- Issue 8 | May 1983

In This Issue...

- Displaying Character Generator EPROMS
- S-C Word Processor Note
- Apple Chips
- S-C Capture
- A PAUSE Directive
- Some New Cards
- FADD -- Find ADDress References
- Generating Parity
- ROGRAM TOO LARGE???

Yet Another Cross Assembler: PDP-11

We are turning the tables at last. When the 6502 was created six or seven years ago, programmers used PDP-11 development systems with cross assemblers to write 6502 code. Now you can use your Apple to write programs for the Digital Equipment Corporation's -11 family. Thanks to Martin Buchholtz for encouraging us to develop this one. He plans to use it for writing programs to run in DEC Falcon SBC-11 based systems. Only $50, if you already own the S-C Macro Assembler. See our ad on page 16 for more about the Cross Assemblers.

We Need Your Help

Does anybody have complete details of the file format of the Apple ///'s relocatable object files? That's the last remaining stumbling block on the road to the S-C Macro Assembler ///. Has anyone figured it out yet?

All Around the World

We are now sending the Apple Assembly Line to subscribers in 32 different countries. (That's about 1200 copies to the USA, and about 100 copies to the other 31 nations.)

Displaying Character Generator EPROMs | Bob Sander-Cederlof

We make our own Character Generator EPROMs for Revision 7 or later Apple II Plusses. I use the Mountain Hardware EPROM Burner to burn the data into 2716 EPROMs. We have several different character sets, and it can be a lot of trouble to check the results.

After designing a character set, and formatting all the bits into the 2048 bytes of EPROM space, and burning it in, we still have to take an Apple apart and plug the chip in to see if all the characters look right.

I decided to write a program which would map the EPROM data onto the hi-res screen, allowing me to test without wasting time burning/erasing EPROMs and dismantling/re-assembling my Apple.

Even if you don't have the same requirements, you can learn a lot about indexing techniques and address shuffling from studying the following program.

Starting at the top.... I set up three page-zero variables in lines 1040-1060. The S-C Macro Assembler is a great environment for making short programs like this one, because I can cycle through edit-assemble-test until it works just right without ever leaving the assembler. S-C Macro allows me to use zero-page locations $00-$1F without fear of inteference ($00-$1E in the Apple //e).

Lines 1080 and 1090 define two buffers where I BLOAD two different EPROM images. I put one at $6800-6FFF, the other at $7000-77FF. There is room on the screen to display one character set in a 16x16 matrix on the left side, and the other on the right side.

For grins, I decided to use the subroutine in Applesoft ROM at $F3E2 to turn on hi-res mode. This is the code executed for the HGR statement, so I called it AS.HGR at line 1110. HGR sets all the soft-switches to hi-res page 1, and clears the screen.

Lines 1160-1180 call the HGR subroutine. Since I was using S-C Macro in the RAM card, and since the Applesoft ROMs are not switched on when a program is executing in the RAM card, I had a problem. The first time I tried to run DISPLAY, I left out lines 1160 and 1180. The result was a total disaster. Line 1170 did a JSR $F3E2 into the RAM card! I had to RESET and reboot the computer to get control again. Look out for these kinds of problems whenever you are trying to use code in both places at once.

Lines 1190-1280 set up the starting addresses to display the first character set on the left half of the screen. Lines 1290-1380 do the same job to show the second set on the right half-screen.

The top line of hi-res page 1 starts at $2000, and goes to $2027. The middle of the line starts at $2014. The starting addresses of subsequent lines can be computed from these two base addresses, although it is a little tricky. More on this later.

The hi-res screen shows the least significant seven bits from each byte. There are forty bytes in each line, making a total of 280 dots across. The dots in each byte are in reverse order: the least significant bit is the leftmost dot. On the other hand, the EPROM image is in normal order. The subroutine DISPLAY.ONE.SET takes care of all the addressing, and REVERSE.BITS handles the reversals.

Lines 1400-1410 pause until I hit any key on the keyboard. During this pause I can examine the screen as long as I wish. When I type any key, the keyboard strobe will be set and $C000 will go negative. Line 1420 will then clear the keyboard strobe, and the RTS at line 1430 returns to the S-C Macro Assembler.

This brings us to a closer examination of the subroutine to actually display a character set, in lines 1440-1770. We will be displaying 16 rows of characters, with 16 characters in each row. It is therefore natural to simplify the problem by writing another subroutine to display one row of characters, and call it sixteen times.

Lines 1480 and 1490 start a loop much like Applesoft's FOR I = 1 TO 16...except in assembly language it is easier to go from 16 to 1. The equivalent to NEXT I is at lines 1750 and 1760, where CNT16 is decremented. In between we have the body of the loop.

Line 1500 calls DISPLAY.ONE.ROW, a subroutine that only gets called from this one line. I made it into a separate subroutine so I could put off writing it until later, and concentrate on one loop at a time. DISPLAY.ONE.ROW expects the addresses at SCREEN.ADR and EPROM.ADR to be already set up for the first byte to be displayed in the current row. After it returns, those addresses will have been modified.

Lines 1510-1580 add 15*8, or 120, to the address in EPROM.ADR. DISPLAY.ONE.ROW already added 8, so the total augment is 128. This moves us up to the beginning of the next set of sixteen characters.

Lines 1590-1740 assume that DISPLAY.ONE.ROW already added $2000 to the address in SCREEN.ADR, and subtracts that value back out. At the same time, we add back in $80, to move to the next group of eight screen lines for the next row of characters. This is sufficient for the first eight rows of characters, but in moving to the ninth row there is a discontinuity which requires adding $28 and subtracting $400 to get the right address. The fact that the ninth row has arrived is apparent by the fact that the high byte of the address goes above $23 (lines 1670 and 1680).

Here is a table of the starting addresses for each of the 24 character rows (we only use the first 16):

    Row  Address     Row  Address     Row  Address
     1    $2000       9    $2028      17    $2050
     2    $2080      10    $20A8      18    $20D0
     3    $2100      11    $2128      19    $2150
     4    $2180      12    $21A8      20    $21D0
     5    $2200      13    $2228      21    $2250
     6    $2280      14    $22A8      22    $22D0
     7    $2300      15    $2328      23    $2350
     8    $2380      16    $23A8      24    $23D0

The starting addresses for the right half-screen can be obtained by just adding $14 to all of the above addresses. What we do is START at $2014, and all the rest are computed automatically.

Now we can talk about what goes on inside one row of characters. Lines 1810-2000 do the job of moving bytes from the EPROM image to the eight screen lines which form the row of characters. Lines 1820-1830 start a loop to count out eight repetitions, and lines 1980-1990 perform the NEXT on this loop.

On each pass through the loop the subroutine GET.PUT is called sixteen times to move a byte for each character to the screen image. GET.PUT is another subroutine only called from one place, but made into a subroutine for ease of understanding. The inner loop of 0 through 15 is controlled by the X-register. Line 1850 sets X=0, and lines 1910-1930 increment, test, and branch ("NEXT X" sequence). The X-register also indexes the STA instruction inside GET.PUT, so that the screen byte for each character is stored into the right place on the screen line. The Y-register is used as an index into the EPROM data by GET.PUT, and parallels the X-register but with an increment of 8 rather than 1. Lines 1870-1900 bump the Y-register by 8 each time through the inner loop.

GET.PUT (lines 2230-2340) does the very simple job of moving one byte from one place in memory to another. Or is it so simple.... Notice that the addresses inside the LDA and STA instructions are filled in when the program runs. This is called self-modifying code, and I normally avoid such code at all costs. It can lead to all sorts of devastating things. Nevertheless, there are exceptions to most rules, and a time for nearly everything. This is one of those, I think. Isolating the offensive code into its own little subroutine appeases my conscience somewhat.

In between LDA and STA I call REVERSE.BITS, yet another simple subroutine which could be written in-line. I prefer making it separate for nicer modularity. The comments show what is going on, bit-by-bit. If you were working from character generator data written for the DOS TOOL KIT or HIGHER TEXT, the bits would already be in the right order. It is just because I am using data for the character generator EPROM that we need to reverse the bits.

Here is a printout done with my NEC PC-8023 and a Grappler+ interface card. The two character sets shown are the ones we sell. The one on the left uses regular lower case characters, with descenders. All the lower case characters are raised up one screen line to leave room for the descenders. The set on the right uses small caps for the lower case, and is the one we use in all the Apples here. The first four rows are the characters used in INVERSE mode, and the next four rows are for FLASH mode. (Doesn't flash too well on paper!)

      1000 *SAVE S.DISPLAY CHAR SET
      1010 *--------------------------------
      1020 *      DISPLAY CHARACTER SET
      1030 *--------------------------------
      1040 CNT8       .EQ $00
      1050 B          .EQ $01
      1060 CNT16      .EQ $02
      1070 *--------------------------------
      1080 EPROM.A.IMAGE  .EQ $6800
      1090 EPROM.B.IMAGE  .EQ $7000
      1100 *--------------------------------
      1110 AS.HGR     .EQ $F3E2
      1120 *--------------------------------
      1130        .OR $803
      1140 DISPLAY
      1150 *---TURN ON HI-RES GRAPHICS------
      1160        LDA $C081    GET A/S ROMS ON MOTHERBOARD
      1170        JSR AS.HGR
      1180        LDA $C080    BACK TO S-C ASM IN RAM CARD
      1190 *---FIRST CHAR SET---------------
      1200        LDA /$2000   TOP LINE, LEFT SIDE
      1210        STA SCREEN.ADR+1
      1220        LDA #$2000
      1230        STA SCREEN.ADR
      1240        LDA /EPROM.A.IMAGE  FIRST CHARACTER SET
      1250        STA EPROM.ADR+1
      1260        LDA #EPROM.A.IMAGE
      1270        STA EPROM.ADR
      1280        JSR DISPLAY.ONE.SET
      1290 *---SECOND CHAR SET--------------
      1300        LDA /$2014   TOP LINE, RIGHT SIDE
      1310        STA SCREEN.ADR+1
      1320        LDA #$2014
      1330        STA SCREEN.ADR
      1340        LDA /EPROM.B.IMAGE  SECOND CHARACTER SET
      1350        STA EPROM.ADR+1
      1360        LDA #EPROM.B.IMAGE
      1370        STA EPROM.ADR
      1380        JSR DISPLAY.ONE.SET
      1390 *---PAUSE UNTIL KEYSTROKE--------
      1400 .1     LDA $C000
      1410        BPL .1
      1420        STA $C010
      1430        RTS          RETURN TO ASSEMBLER
      1440 *--------------------------------
      1450 *      DISPLAY ONE CHARACTER SET IN 16-BY-16 FORMAT
      1460 *--------------------------------
      1470 DISPLAY.ONE.SET
      1480        LDA #16      COUNT 16 ROWS
      1490        STA CNT16
      1500 .1     JSR DISPLAY.ONE.ROW
      1510 *---NEXT ROW IN EPROM DATA-------
      1520        CLC
      1530        LDA EPROM.ADR
      1540        ADC #15*8
      1550        STA EPROM.ADR
      1560        LDA EPROM.ADR+1
      1570        ADC #0
      1580        STA EPROM.ADR+1
      1590 *---NEXT ROW ON SCREEN-----------
      1600        SEC
      1610        LDA SCREEN.ADR
      1620        SBC #$2000-$80
      1630        STA SCREEN.ADR
      1640        LDA SCREEN.ADR+1
      1650        SBC /$2000-$80
      1660        STA SCREEN.ADR+1
      1670        CMP #$24          HIT THE BREAK YET?
      1680        BCC .2            NO, GO ON
      1690        LDA SCREEN.ADR    YES, ADJUST THE ADDRESSES
      1700        SBC #$400-$28
      1710        STA SCREEN.ADR
      1720        LDA SCREEN.ADR+1
      1730        SBC /$400-$28
      1740        STA SCREEN.ADR+1
      1750 .2     DEC CNT16    LAST ROW YET?
      1760        BNE .1       ...NO
      1770        RTS          ...YES, RETURN
      1780 *--------------------------------
      1790 *      DISPLAY ONE ROW OF 16 CHARACTERS
      1800 *--------------------------------
      1810 DISPLAY.ONE.ROW
      1820        LDA #8       8 SCREEN LINES FOR ONE ROW
      1830        STA CNT8
      1840 .1     LDY #0       EPROM DATA INDEX
      1850        LDX #0       SCREEN IMAGE INDEX
      1860 .2     JSR GET.PUT  MOVE ONE BYTE TO SCREEN
      1870        TYA          ADD 8 TO EPROM DATA INDEX
      1880        CLC
      1890        ADC #8
      1900        TAY
      1910        INX          BUMP SCREEN IMAGE INDEX
      1920        CPX #16
      1930        BCC .2       MORE CHARACTERS
      1940        INC EPROM.ADR  BUMP TO NEXT LINE OF EPROM DATA
      1950        LDA SCREEN.ADR+1   +$400
      1960        ADC #3       (CARRY = 1)
      1970        STA SCREEN.ADR+1
      1980        DEC CNT8     NEXT SCREEN LINE
      1990        BNE .1       ...IF ANY
      2000        RTS          RETURN
      2010 *--------------------------------
      2020 *      REVERSE THE ORDER OF BITS 6-0 IN A-REG
      2030 *      (CHANGE XABCDEFG TO 0GFEDCBA)
      2040 *--------------------------------
      2050 REVERSE.BITS
      2060        LSR          REVERSE 7 BITS
      2070        ROL B        A=0XABCDEF  B=XXXXXXXG
      2080        LSR
      2090        ROL B        A=00XABCDE  B=XXXXXXGF
      2100        LSR
      2110        ROL B        A=000XABCD  B=XXXXXGFE
      2120        LSR
      2130        ROL B        A=0000XABC  B=XXXXGFED
      2140        LSR
      2150        ROL B        A=00000XAB  B=XXXGFEDC
      2160        LSR
      2170        ROL B        A=000000XA  B=XXGFEDCB
      2180        LSR
      2190        ROL B        A=0000000X  B=XGFEDCBA
      2200        LDA B
      2210        AND #$7F     0GFEDCBA
      2220        RTS
      2230 *--------------------------------
      2240 *      PICK UP A BYTE OF EPROM DATA,
      2250 *      REVERSE THE BITS, AND STORE
      2260 *      IT ON THE SCREEN.
      2270 *--------------------------------
      2280 GET.PUT
      2290        LDA $FFFF,Y
      2300 EPROM.ADR .EQ *-2
      2310        JSR REVERSE.BITS
      2320        STA $FFFF,X
      2330 SCREEN.ADR .EQ *-2
      2340        RTS
      2350        .LIF

S-C Word Processor Note | Mike Laumer

We recently had one customer give us a great compliment on the S-C Word Processor. He has given up on WORDSTAR! He found that the S-C Word Processor can read and write large text files 20 times faster than WORDSTAR and that scrolling was much quicker. He can be in and out of the S-C Word Processor before WORDSTAR even lets him type a single key. The S-C Word Processor is also much less expensive than WORDSTAR and you don't have to buy a Z-80 card!

His only desire was to have an 80-colun version of the Word Processor. However, that wouldn't be nearly so fast since SCWP re-writes the screen on every keystroke. I have noticed also that the 40-column display never causes me eye strain, but all the 80-column displays do.

Apple Chips | Bob and Bill

You may recall that when Bill reviewed Apple ][ Circuit Description last month, he bemoaned the lack of a "Cross Reference", by board location, of all the Apple's ICs. Well Bob has worked out a couple of tables to fill that gap, and we'll be including those tables in future shipments of the book.

In the meantime, here's another sort of table, showing the locations and descriptions of all the chips in your Apple. This one is organized by chip number.

               Board                Chip
    Chip      Location(s)          Description
    ----      -----------          --------

    555       A13 B3               Timer
    558       H13                  3 Timers
    741       K13                  Op Amp

    2316B     A5  (Rev 7,RFI)      ROM (character generator)
    2513      A5  (Rev 0,1)        ROM (character generator)
    4116      C3-10 D3-10 E3-10    RAM
    6502      H6-9                 Microprocessor
    9316B     F3-11 (6 chips)      ROM (monitor and language)

    74LS00    A2                   4 2-input NAND
    74LS02    A12 A14 B13 B14      4 2-input NOR
    74LS04    C11                  6 Inverters
    74LS08    B11 H1               4 2-input AND
    74LS11    B12                  3 3-input AND
    74LS20    D2                   2 4-input NAND
    74LS32    C14                  4 2-input OR
    74LS51    C13                  AND3-NOR2, AND2-NOR2
    74LS74    A11 B10 J13          2 Flip-Flops
    74LS86    B2                   4 2-input XOR
    74LS138   F12 F13 H2  H12      3-by-8 Decoder
    74LS139   E2  F2               2 3-by-4 Decoders
    74LS151   A9                   1-of-8 Selector
    74LS153   C1  E11 E12 E13      2 1-of-4 selectors
    74LS161   D11-14               Counter
    74  166   A3                   8-bit Shift Register
    74LS174   B5  B8               6 Flip-Flops
    74LS175   B1                   4 Flip-Flops
    74LS194   A10 B4  B9           4-bit Shift Register
    74LS195   C2                   4-bit Shift Register
    74LS251   H14                  1-of-8 Selector
    74LS257   A8  B7  C12 J1       4 1-of-2 Selectors
    74LS259   F14                  8-bit Addressable Latch
    74LS283   E14                  4-bit Full Adder
    74LS367   H3  H4  H5 (on some models)  6 Bus Drivers

    8T97      H3  H4  H5 (on most models)  6 Bus Drivers
    8T28      H10 H11    (on rev 0,1,7)    4 Bus Buffers
    8304      H10        (on ref RFI)      8 Bus Buffers

S-C CAPTURE -- A Modem Program for the Word Processor | Jim Church

If you like to sign on to the The Source or CompuServe or some such system, you should get a copy of the S-C Word Processor. I like to receive the programs from CALL-A.P.P.L.E. magazine by modem and the S-C Word Processor really makes that easy.

What you do is quite simple. Just put a copy of B.SC.CAPTURE on the disk with the Word Processor. Then, whenever you want to capture a session with a remote system, you can choose D from the word processor menu and BLOAD B.SC.CAPTURE. After the routine is loaded, return to the main menu and choose L to load a sign-on file containing the commands necessary to dial the number you want to call. Here is a sample sign-on file, which I use to call up The Source.

           !pr2
           Q_*367-6021      (The Q_ is a Control-Q)
           !pr768

Now choose P from the menu, and your word processor will start dialing the phone! From here on you just operate the remote system as usual. The top line of the screen will show the address where characters are being stored, and the rest of the screen shows the text you are entering and receiving.

When you want to quit, just type a Control Z to hang up your phone and return to the word processor's main menu. Select E and you will see a copy of everything that transpired. Now you can edit the text however you want to, and save it all to your disk.

The !pr768 command above is intended to provide a hook for a user-written printer driver. It sets the output hook at $36-37 to $300. The next time the Word Processor tries to output a character, it wakes up the capture routine, which completely takes over until it is turned off with a Control Z. This is slightly abusing the !pr directive, so if you follow this example for other routines, be sure to have lines like 1570-1590 at the beginning of your routine, and exit to $803 at the end, so the Word Processor can reconnect itself correctly.

That's all there is to it. You could probably do a lot to "smarten up" this dumb terminal program. The way I have done it, it recognizes a Control Z from the keyboard and filters out incoming Control J's. That's all it does. Probably it should filter out Control G too, at the very least. My intention is to demonstrate the simple fact that the word processor is a very versatile creature.

This works, the way it is, with the D. C. Hayes Micromodem II in Slot 2. If your modem is in a different slot, just change line 1260 to show the correct slot number.

      1000 *--------------------------------
      1010 *
      1020 *          S-C CAPTURE
      1030 *
      1040 *   A COMMUNICATIONS MODULE FOR
      1050 *     THE S-C WORD PROCESSOR
      1060 *
      1070 *          BY JIM CHURCH
      1080 *
      1090 *--------------------------------
      1100 * FULL DUPLEX CAPTURE PROGRAM
      1110 * WORKS WITH MICROMODEM II
      1120 * AND S-C WORD PROCESSOR
      1130 *
      1140 * GO INTO EDITOR W/EMPTY BUFFER
      1150 * ENTER COMMANDS AS FOLLOW:
      1160 *
      1170 * !pr2
      1180 * Q*367-6021    THE "Q" IS A CONTROL-Q
      1190 * !pr768
      1200 *
      1210 * LEAVE EDITOR, CHOOSE P ON MENU
      1220 *--------------------------------
      1230        .OR $300
      1240        .TF B.SC.CAPTURE
      1250
      1260 SLOT   .EQ $02
      1270 SLOT16 .EQ SLOT*16
      1280
      1290 PTR    .EQ $00
      1300 WNDTOP .EQ $22
      1310 CH     .EQ $24
      1320
      1330 HOOK   .EQ $3EA
      1340
      1350 BUFFER .EQ $2000
      1360
      1370 KEYBOARD  .EQ $C000
      1380 STROBE    .EQ $C010
      1390 MM.CR2    .EQ $C085+SLOT16
      1400 MM.STATUS .EQ $C086+SLOT16
      1410 MM.DATA   .EQ $C087+SLOT16
      1420
      1430 PRNTAX .EQ $F941
      1440 INIT   .EQ $FB2F
      1450 VTAB   .EQ $FC22
      1460 VTABZ  .EQ $FC24
      1470 HOME   .EQ $FC58
      1480 COUT1  .EQ $FDF0
      1490 SETKBD .EQ $FE89
      1500 SETVID .EQ $FE93
      1510 *--------------------------------
      1520 SC.CAPTURE
      1530        JSR INIT     FIX SCREEN
      1540        JSR HOME     CLEAR SCREEN
      1550        LDA #1       RESERVE TOP LINE
      1560        STA WNDTOP    FOR LOCATION COUNTER
      1570        JSR SETVID   PR#0
      1580        JSR SETKBD   IN#0
      1590        JSR HOOK     TELL DOS
      1600        LDX #0       WORD PROCESSOR
      1610        STX BUFFER    NEEDS 0 AT $2000
      1620        INX
      1630        STX PTR      START POINTER
      1640        LDA /BUFFER   AT $2001
      1650        STA PTR+1
      1660
      1670 TERMINAL
      1680        LDA KEYBOARD KEY DOWN?
      1690        BPL MODEM    NO, CHECK MODEM
      1700        STA STROBE   YES, CLEAR STROBE
      1710        CMP #$9A     CONTROL Z?
      1720        BEQ QUIT     YES, LEAVE
      1730        PHA          SAVE KEYPRESS
      1740 .1     LDA MM.STATUS CHECK IF THE TRANSMIT
      1750        AND #$02       REGISTER EMPTY BIT IS SET
      1760        BEQ .1       NO, WAIT FOR IT
      1770        PLA          YES, GET KEY BACK
      1780        STA MM.DATA  SEND IT
      1790        BMI TERMINAL  AND LOOP AGAIN
      1800
      1810 MODEM  LDA MM.STATUS CHECK IF THE RECEIVER
      1820        AND #$01       REGISTER FULL BIT IS SET
      1830        BEQ TERMINAL NO, LOOP AGAIN
      1840        LDA MM.DATA  YES, GET CHARACTER
      1850        ORA #$80     SET HI BIT
      1860        CMP #$8A     CONTROL J?
      1870        BEQ TERMINAL IGNORE IT
      1880        JSR COUT1    PRINT CHAR
      1890        LDY #0
      1900        STA (PTR),Y  CAPTURE IT IN BUFFER
      1910
      1920 INCR   INC PTR      BUMP POINTER LO
      1930        BNE COUNT     NOT 0
      1940        INC PTR+1    BUMP POINTER HI
      1950        LDA PTR+1    CHECK IF
      1960        CMP #$96      BUFFER END?
      1970        BCS QUIT     FULL BUFFER, LEAVE
      1980
      1990 COUNT  LDA CH       SAVE CH
      2000        PHA           ON STACK
      2010        LDA #0       TOP LINE
      2020        JSR VTABZ     FOR LOCATION COUNTER
      2030        LDA #$14     COL 20
      2040        STA CH        IN CH
      2050        LDA PTR+1    HI BYTE OF LOCATION
      2060        LDX PTR      LO BYTE
      2070        JSR PRNTAX   PRINT ADDRESS
      2080        PLA          GET OLD CH AND RETURN
      2090        STA CH        TO WHERE WE WERE
      2100        JSR VTAB     OLD LINE
      2110        BCC TERMINAL START OVER
      2120
      2130 QUIT   LDA #$00     END-OF-TEXT MARKER
      2140        STA (PTR),Y   FOR WORD PROCESSOR
      2150        LDA #$05     HANG UP PHONE
      2160        STA MM.CR2    AT CONTROL REGISTER
      2170        JMP $803     COLDSTART WORD PROCESSOR
      2190        .LIF

A PAUSE Directive | Mike Laumer

Maybe your source code has outgrown even two disks and you need to know when to swap disks during assembly. Maybe you're using a single-sheet printer and need to change pages. Maybe you want to change typefaces on your letter-quality printer. Maybe you want to check the address of a routine or variable, without having to constantly watch the screen until it comes along. For whatever reason, you need to have the S-C Macro Assembler pause during assembly. Here is a new .US directive to let you do just that!

With this directive, you can insert a line like this anywhere in your code:

         1300        .US SWAP SOURCE DISK

In each pass, when the assembler encounters this line it will pause, display "SWAP SOURCE DISK" in inverse text at the bottom of the screen, beep twice, and wait for a keypress. You can take whatever action you need to, and press any key to resume assembly.

The listing is for the Language Card version of the assembler. If you are using the main memory version, you don't need to worry about write-enabling and -protecting, so you can just delete lines 1220, 1230 and 1280.

The values for the .EQ statements in lines 1170-1180 depend on whether you are using the Main Memory or the Language Card assembler, and whether you have Version 1.0 or 1.1. Here's a table of the values for US.VCTR and SC.CMNT:

               Main   Language
              Memory    Card    Version
              ------  --------  -------

    US.VCTR   $100C    $D00C     Both

    SC.CMNT   $1FD8    $E124     1.0
              $1FCA    $E0E4     1.1

That's all there is to it! Now you don't have to constantly stare at the screen during those long assemblies. Now you can sit back and wait for your Apple to call you when it needs you.

      1000 *--------------------------------
      1010 *   .US DIRECTIVE TO PAUSE DURING ASSEMBLY
      1020 *
      1030 *      SYNTAX:  .US <phrase>
      1040 *      RESULT:  Displays <phrase> in inverse text
      1050 *               and waits for a keypress
      1060 *
      1070 *--------------------------------
      1080 CHR.PTR  .EQ $7B
      1090 WBUF     .EQ $200
      1100 CORNER   .EQ $7D0
      1110 KEYBOARD .EQ $C000
      1120 STROBE   .EQ $C010
      1130 PROTECT  .EQ $C080
      1140 ENABLE   .EQ $C083
      1150 BELL     .EQ $FBE2
      1160
      1170 US.VCTR  .EQ $D00C
      1180 SC.CMNT  .EQ $E124
      1190 *--------------------------------
      1200        .OR $300
      1210 *--------------------------------
      1220        LDA ENABLE       WRITE ENABLE
      1230        LDA ENABLE       RAM CARD
      1240        LDA #PAUSE
      1250        STA US.VCTR+1    POINT .US VECTOR
      1260        LDA /PAUSE
      1270        STA US.VCTR+2    TO PAUSE ROUTINE
      1280        LDA PROTECT      PROTECT CARD
      1290        RTS
      1300 *--------------------------------
      1310 PAUSE  JSR BELL     BEEP
      1320        LDX #0
      1330        LDY CHR.PTR  CHAR POINTER
      1340 .1     LDA WBUF,Y   GET CHAR FROM CALL LINE
      1350        BEQ .2       END OF LINE?
      1360        AND #$3F     NO, INVERT CHAR
      1370        STA CORNER,X AND PUT IT AT BOTTOM OF SCREEN
      1380        INX
      1390        INY
      1400        CPX #40      LINE FULL?
      1410        BCC .1       NO, GET ANOTHER CHAR
      1420
      1430 .2     JSR BELL     BEEP
      1440 .3     LDA KEYBOARD
      1450        BPL .3       WAIT FOR KEYPRESS
      1460        STA STROBE
      1470        JMP SC.CMNT  RETURN TO ASSEMBLY
      1480 *--------------------------------

Some New Cards | Bob Sander-Cederlof

1. Bob Stout just called from Houston to renew his subscription to AAL, and to tell me about a new toy he's getting. It seems that Legend Industries has a new kind of RAM card, containing 18K of static RAM, with battery backup.

16K of the memory on the card is mapped just like a language card, so it can be used in slot 0. The card also has a hardware write-protect switch, that you can throw to completely protect the memory. Once you have done that whatever you have stored in the card is there to stay.

The card can also be used in a higher slot for boot-up operation. The other 2K of memory is mapped at $CN00 and $C800, just like the ROM on a standard peripheral card. Think of the possibilities!

This new card from Legend is available with either NiCad or Lithium batteries. This gives you a choice between rechargeability or very long power-off life (about 2 years). The price is $149.95.

2. Saturn Systems has introduced a card with 64K RAM and a 6502 on it. The CPU runs at 3.6 MHz, compared to Apple's roughly 1 MHz. Comes with a pre-boot disk to let you use this faster processor with Applesoft, Pascal, and Integer BASIC. Price is $599. See their ad in the latest Softalk Magazine.

3. Analytical Engines, Inc. has one-upped the DTACK Grounded board. For only $1550, you can plug in an 8 MHZ 68000 card with 128K RAM (expandable to 512K on the card!). You can upgrade to a 12.5 MHz chip if you really need it. DTACK is NOT grounded on this board, so you have access to the full 16-megabyte address space. The 16K ROM on the board contains monitor functions and diagnostics. YOu can replace the ROM with up to 64K of EPROM if you want. Software? The price includes a complete UCSD P-system (I think he said version 4.1) with Pascal, Basic, and Fortran compilers. You also get an Applesoft-compatible BASIC interpreter that runs entirely inside the 68000. CP/M-68 is optional, and Unix is supposed to be available soon. See their ad in the latest Nibble Magazine.

4. Lee Meador has designed a board with 64K RAM, 4K EPROM, and a 2MHz 6502 on it. This unique board does not talk directly to the Apple bus; instead, there are two parallel ports (I presume implemented with a 6522 chip). One 8-bit port talks to the Apple I/O bus, and the other is available to outside devices. Software runs on the board at 2MHz, and at the same time your Apple chips do their 1MHz processing. I can think of a lot of neat ways to use Lee's board, including as a printer buffer/controller, as a high-speed math processor, as a hard disk interface, and so on. If enough of you are interested, Lee will sell these for around $500 each, along with some demonstration software.

FADD -- Find ADDress references | Brooke Boering

Recently I have been messing around with modifications to DOS. Since I didn't have the complete source code for it, I simply used the explanations in "Beneath Apple DOS". I did find that I also needed a utility to locate all references to certain addresses. FADD was the result, and it's mighty useful. It's much quicker than doing a complete disassembly.

FADD will locate all instructions within 64K of memory referring to a given address. It skips the $C000-$CFFF (I/O) pages and avoids missing memory by doing a double read test.

It is intended to be used by the serious assembly language programmer for debugging and analysis. I'ts faster than doing a disassembly, though not quite so informative.

FADD is origined at $300 (what else?) and uses 8 zero page locations that are generally unused by programs except as scratch. You can alter both the origin point and zero page locations to suit your individual needs.

To use FADD:

     1- BLOAD B.FADD
     2- Get to Monitor
     3- 'Fat finger' your address into 6-7 in HI-LO order.
     4- Execute with a '300G'

NOTE: Use the spacebar to pause/release listing.

      1000 *********************************
      1010 *                               *
      1020 *           F A D D             *
      1030 *                               *
      1040 *  ( FIND ADDRESS REFERENCES )  *
      1050 *   -------------------------   *
      1060 *                               *
      1070 *    A PUBLIC DOMAIN UTILITY    *
      1080 *                               *
      1090 *     BY.. BROOKE W BOERING     *
      1100 *                               *
      1110 *********************************
      1350
      1360 * TO USE:
      1370 * 1- BLOAD FADD.OBJ
      1380 * 2- GET TO MONITOR
      1390 * 3- 'FAT FINGER' YOUR ADDRESS
      1400 *    INTO 6-7 IN HI-LO ORDER.
      1410 *   NOTE ------> ^^ ^^ <-------
      1420 * 4- EXECUTE WITH A '300G'
      1430
      1460 *---------------------------------
      1470 *         E Q U A T E S
      1480
      1490 TARGHI .EQ $6
      1500 TARGLO .EQ $7
      1510 * NOTE: ABOVE REVERSES NORMAL LO/HI-BYTE
      1520 *  ORDER FOR EASIER KEYIN FROM MONITOR
      1530 WHER   .EQ $8
      1540 WHERLO .EQ $8
      1550 WHERHI .EQ $9
      1560
      1570 LENGTH  .EQ $2F
      1580 PCL     .EQ $3A
      1590 PCH     .EQ $3B
      1600 COLOR   .EQ $30
      1610
      1620 INSDS2  .EQ $F88E
      1630 INSTDSP .EQ $F8D0
      1640 PCADJ3  .EQ $F956
      1650 CROUT   .EQ $FD8E
      1660 *---------------------------------
      1670        .OR $300
      1680 *      .TF B.FADD
      1690 *---------------------------------
      1700 START
      1710
      1720  LDX #0
      1730  STX WHERLO   START AT BEGINNING
      1740  STX WHERHI   OF MEMORY
      1750
      1760 *-- CHECK FOR DIRECT REFERENCE
      1770 .1
      1780  LDY #0
      1790  LDA (WHER),Y GET WHERAT-LO
      1800  STA COLOR    SAVE TEMP
      1810  LDA (WHER),Y GET IT AGAIN
      1820  CMP COLOR    STILL THE SAME?
      1830  BNE .8       NO, SKIP IT, NO MEMORY HERE
      1840 * (FALL THROUGH IF MEMORY AT THIS ADDRESS)
      1850
      1860  CMP TARGLO   ? TARGET-LO ?
      1870  BNE .3       NO, GO AHEAD
      1880  INY
      1890  LDA (WHER),Y GET WHERAT-HI
      1900  CMP TARGHI   ? TARGET-HI ?
      1910  BNE .3       NO, GO AHEAD
      1920 * (FALL THROUGH IF 2-BYTE MATCH ON TARGET)
      1930
      1940 *-- APPARENT MATCH;
      1950 *     MAKE SURE IT'S A 3-BYTE INSTRUCTION
      1960 .2
      1970  LDY WHERHI   GET ADDRESS OF MATCH
      1980  LDX WHERLO
      1990  BNE .24
      2000  DEY          POINT TO INSTRUCTION BYTE
      2010 .24
      2020  DEX
      2030  STX PCL      AND SET PROGRAM COUNTER
      2040  STY PCH
      2050
      2060  LDX #0
      2070  LDA (PCL,X)  GET OPCODE
      2080  JSR INSDS2   USE MONITOR DISASSEMBLER ROUTINE
      2090  LDA LENGTH
      2100  CMP #2       3-BYTE INSTRUCTION?
      2110  BEQ .6       OK; GO AHEAD TO DISPLAY
      2120 * (FALL THROUGH WHEN NOT A 3-BYTE INSTR)
      2130
      2140 *-- CHECK FOR RELATIVE BRANCH
      2150 .3
      2160  LDY #0
      2170  LDA (WHER),Y GET INSTRUCTION BYTE
      2180  AND #$1F     ISOLATE SIGNIFICANT BITS
      2190  CMP #$10     A BRANCH INSTRUCTION?
      2200  BNE .8       DEFINITELY NOT
      2210 * (FALL THROUGH WHEN A BRANCH INSTRUCTION)
      2220
      2230 *-- TEST IF BRANCHING TO TARGET
      2240 *     NOTE: USING MONITOR TECHNIQUE
      2250 .4
      2260  LDX WHERLO   PRESET FOR PCADJ3
      2270  LDY WHERHI
      2280  STX PCL      SET PC TO OPCODE BYTE
      2290  STY PCH
      2300  LDY #1
      2310  LDA (WHER),Y GET OFFSET BYTE
      2320  JSR PCADJ3   LEAVES EFFECTIVE ADDRESS-1
      2330 *             IN Y AND A
      2340  TAX
      2350  INX
      2360  BNE .43
      2370  INY
      2380 .43
      2390 *-- NOW 'BRANCH TO' ADDRESS IS IN Y AND X
      2400  CPX TARGLO
      2410  BNE .8
      2420  CPY TARGHI
      2430  BNE .8
      2440 * (FALL THROUGH ON MATCH)
      2450
      2460 *-- DISPLAY MATCHED INSTRUCTION
      2470 .6
      2480 * PCL/PCH ARE SET
      2490  JSR INSTDSP  <= MONITOR ROUTINE
      2500
      2510 *-- ALLOW KEYED PAUSE/RELEASE
      2520 .7
      2530  BIT $C000    KEY DOWN?
      2540  BPL .8       NO, GO AHEAD
      2550  BIT $C010    YES, CLEAR STROBE
      2560 .77
      2570  BIT $C000    RELEASED?
      2580  BPL .77      NO, LOOP TIL SO
      2590  BIT $C010    YES, CLEAR STROBE
      2600
      2610 *-- POST DISPLAY (OR NO MATCH)
      2620 .8
      2630  INC WHERLO   KICK ADDRESS
      2640  BNE .1       LOOP 255 OF 256
      2650  INC WHERHI   KICK ADDR PAGE#
      2660  BEQ .9       EXIT AT 65536 OVFLO
      2670
      2680 *-- AT NEW PAGE !!
      2690  LDA WHERHI
      2700  CMP #$C0     AT THE I/O PORTS ?
      2710  BNE .1       NO, LOOP BACK
      2720  LDA #$D0     YES, SKIP 'EM
      2730  STA WHERHI   : (AVOID PROBLEMS)
      2740  BNE .1       LOOP BACK
      2750
      2760 .9
      2770  JMP CROUT    RETURN THROUGH CROUT

Generating Parity | Bob Sander-Cederlof

When large amounts of data are being moved around it is easy to garble some. When you transmit characters over the telephone, or read them from a tape or disk, you want some kind of assurance that the message does not get modified by the medium.

Lots of schemes have been invented to prevent, detect, and even correct transmission errors: checksums, parity, cyclic redundancy codes, and more.

Checksums are used inside the Apple all the time. If you ever used cassette tapes with your Apple, you were re-assured to know that each program was recorded with a checksum. DOS 3.3 adds a checksum to the end of every sector on the disk. The checksum is re-computed when you read a tape or disk sector; if the result is different, at least one bit in the data is wrong.

Most of the checksums I have seen are of the exclusive-or type. All the bytes in the data record are EORed together, and the result is written at the end of the record. When the data is read, the in-coming bytes are again EORed together, and finally EORed with the checksum itself. If the final result is non-zero, an error occurred.

Checksums in the Apple are usually one byte wide. However, for more security, you could form a wider checksum. Or you could ADD the bytes together and store a two byte sum. Or store the complement of the sum, so that adding all the bytes plus the complement will give a zero result if there are no errors. [Checksums may check out OK even though errors occur, if the errors are sneaky enough to cancel each other out.]

Parity is really a kind of checksum, but only one bit wide. A series of bits is EORed together, and the single-bit result is the parity value. In an ASCII character there is provision for the leading bit position to be used for storing a parity bit. An eight-bit byte holds seven data bits plus a parity bit.

There are two kinds of parity in use: even and odd. Even parity makes the total number of 1-bits in the stream of bits even; odd parity makes the total number of 1-bits odd. Both even and odd are in use today in various kinds of equipment. Many terminals and serial communication boards allow you to select even, odd, or no parity. Looking at the ASCII code for a couple of letters, each could be transmitted in four ways:

    Letter "M"  0 1 0 0 1 1 0 1 -- No parity, 8th bit always 0
                1 1 0 0 1 1 0 1 -- No parity, 8th bit always 1
                0 1 0 0 1 1 0 1 -- Even parity
                1 1 0 0 1 1 0 1 -- Odd parity

    Letter "Q"  0 1 0 1 0 0 0 1 -- No parity, 8th bit always 0
                1 1 0 1 0 0 0 1 -- No parity, 8th bit always 1
                1 1 0 1 0 0 0 1 -- Even parity
                0 1 0 1 0 0 0 1 -- Odd parity

Sometimes I have needed a quick way to generate or verify a parity bit with software. These matters are usually handled in hardware, but not always.

In the 6502, it is a very simple matter to rotate a byte around and count the number of one bits present. Then the parity bit can be merged with the byte, or compared with what is already there.

The following subroutine (PARITY) computes the parity bit and merges it with the data byte. Call PARITY with the character to be merged in the A-register. Only the seven data bits will be counted. As written, the subroutine computes an odd parity bit. You can change line 1030 to "LDX #0" to compute even parity.

    1000 *-------------------------------
    1010 *   Compute and merge parity bit
    1020 *-------------------------------
    1030 PARITY LDX #1     (#0 FOR EVEN PARITY)
    1040        ASL        SHIFT PARITY POSITION OUT
    1050        PHA        SAVE SHIFTED CHARACTER
    1060 .1     BPL .2     IF NEXT BIT = 0, DON'T COUNT
    1070        INX        IF NEXT BIT = 1, COUNT IT
    1080 .2     ASL        SHIFT IN NEXT BIT
    1090        BNE .1     IF ANY REMAINING BITS = 1
    1100        TXA        GET COUNT OF 1-BITS
    1110        LSR        EVEN/ODD BIT OF COUNT INTO CARRY
    1120        PLA        ORIGINAL CHAR BUT SHIFTED
    1130        ROR        SHIFT PARITY BIT INTO BIT 8
    1140        RTS

I wrote a little program to drive the PARITY subroutine, using all possible values from 0 through 127, and print out the results:

    1150 *-------------------------------
    1160 PRHEX  .EQ $FDDA  MONITOR PRINT (A) IN HEX
    1170 COUT   .EQ $FDED  MONITOR PRINT CHAR IN (A)
    1180 *-------------------------------
    1190 DEMO   LDA #0     FOR CHAR = $00 TO $7F
    1200        STA CHAR
    1210 .1     JSR PARITY CALL THE PARITY SUBROUTINE
    1220        JSR PRHEX  PRINT THE CHARACTER
    1230        INC CHAR   NEXT CHAR
    1240        LDA CHAR   SEE IF TIME FOR A NEW LINT
    1250        AND #$07
    1260        BEQ .2     YES
    1270        LDA #$A0   <SPACE>
    1280        BNE .3     ...ALWAYS
    1290 .2     LDA #$8D   <RETURN>
    1300 .3     JSR COUT   PRINT SPACE OR RETURN
    1310        LDA CHAR
    1320        BPL .1     STILL LESS THAN $80
    1330        RTS        DEMO FINISHED

When I set it up for odd parity, here is part of the table printed out by DEMO:

    80 01 02 83 04 85 86 07
    08 89 8A 0B 8C 0D 0E 8F
    10 91 92 13 94 15 16 97
       .
       .
       .

    F8 79 7A FB 7C FD FE 7F

Now, how about a subroutine to check parity? Here is a version that checks an 8-bit value for odd parity. Simply change line 1420 to "LDX #0" to check for even parity instead. The subroutine returns with CARRY CLEAR for good parity, or CARRY SET for bad parity.

    1400 *-----------------------------
    1410 CHECK.PARITY
    1420        LDX #1     (OR #0 FOR EVEN PARITY)
    1430        PHA        SAVE ORIGINAL CHAR
    1440 .1     ASL        SHIFT NEXT BIT INTO CARRY
    1450        BEQ .2     NO REMAINING 1-BITS
    1460        BCC .1     LEADING BIT NOT 1-BIT
    1470        INX        COUNT THE 1-BIT
    1480        BCS .1     ...ALWAYS
    1490 .2     BCC .3     LATEST SHIFTED BIT WAS 0
    1500        INX        LATEST SHIFTED BIT WAS 1
    1510 .3     TXA        BIT COUNT
    1520        LSR        SHIFT EVEN/ODD BIT INTO CARRY
    1530        PLA        RESTORE ORIGINAL CHARACTER
    1540        RTS

ROGRAM TOO LARGE??? | Lee Meador

I was writing an ampersand file-handling routine, using the File Manager in DOS as described in chapter 6 of Beneath Apple DOS. I wanted my Applesoft program to be able to set ONERR and catch errors in files (wrong name, too short, etc.) But I also wanted the error messages to come out in immediate mode or with no ONERR set. Since I was doing my own file-handling, I was going to have to provide my own error outputs. Originally I tried this:

    ERROR  LDY #$0A        error code offset
           LDA ($04),Y     $04 -> FM parmlist
           JMP $A6D2       jump into DOS error handler

This worked OK when used in code that was called from an Applesoft program, but when I called it in immediate mode (from the "]") I would always get "ROGRAM TOO LARGE" when an error occurred.

You might guess that I have found a solution. The problem is caused when we jump into DOS at $A6D2 with the IO hooks still pointing into DOS. The routine starting at $A6D2 saves the error code in a temporary location at $AA5C and calls $A702 to print a "<return><beep><return>". Since we entered illegally that output goes to DOS at $9EBD, then via a JSR to $9ED1 where the accumulator is saved in a temporary location -- $AA5C! This leaves that last <return> in $AA5C.

When control returns to the error handler DOS then tries to look up error message number 141 ($8D) in the 16-entry table of offsets starting at $AA3F. This loads the offset from location $AACC, which happens to contain the high-order byte of the address of the OPEN command handler ($AB22)! This leaves the error message printer with an offset of $AB into the messages at $A971. And that is what produces "ROGRAM TOO LARGE". Look at the routines at $A6D2 and $A702 for more detail. $A702 is meant to be called with the error code in the X register.

Now here's a method to have the error handled correctly:

    OUT.HOOK  .EQ $36
    HARD.COUT .EQ $FDF0

    ERROR  LDA #HARD.COUT  point hook out of DOS
           STA OUT.HOOK
           LDA /HARD.COUT
           STA OUT.HOOK+1  DOS will fix it back
           LDY #$0A        index to error code
           LDA ($04),Y     $04 -> FM parmlist
           JMP $A6D2       do it ...

That takes care of getting the right error messages. Now if I could just figure out some way to make sure that no errors ever occur. . .

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 $15 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $13 postage for other countries. Back issues are available for $1.50 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE, all rights reserved. Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof. (Apple is a registered trademark of Apple Computer, Inc.)