Apple Assembly Line
Volume 7 -- Issue 4January 1987

In This Issue...

Jerry Lewis (not the entertainer), a subscriber from New Boston, Michigan, recently worked with Phil Wershba at SCRG to modify chip 0 on the QuikLoader card. The result is the ability to include ProDOS in chip 0. If you already have a QuikLoader, Phil says upgrades are available (there is a small fee this time). Call Phil at 1-800-635-8310. If you do not have a QuikLoader yet, and want one, you can order from us (we wil even save you a few dollars!).

Jim Davis (not the cartoonist), a subscriber in Upland, California, has a business that may be of interest to a lot of you. He will buy broken computers, especially Apple II and IIPlus models. He will also do chip-level repair of computers, power supplies, and 5.25" disk drives. If you have a II or IIPlus that will not quite work with a 65C02, and you wish it would, he will modify the motherboard for you so that it works. I haven't tried out his services yet, but if it sounds interesting call him at (714) 982-0860. Or write to him at Beehive Enterprises, 1476 N. Grove Ave., Upland, CA 91786.


//gs I/O Soft SwitchesBob Sander-Cederlof

In spite of the ton of documentation Apple has made available to developers about the //gs, there is a lot of nitty-gritty detail that we need and have not got. Or at least it is very difficult to find the important, weed out the inaccurate, sift through the mutually contradictory, and so on.

I have been trying to get a complete and accurate map of the I/O addresses in the $C000-$C07F range, and I think I am getting close. Here is a summary of what I have learned.

The following addresses have exactly the same function in the //e and the //gs:

     $C000 - $C01F     Keyboard & Memory mapping
     $C030             Speaker Toggle
     $C050 - $C05F     Graphics modes & Gameport ANx
     $C061 - $C067     Gameport buttons & Paddles
     $C070             Paddle Trigger
     $C080 - $C08F     Language card control

The following //e addresses have no function in the //gs, but are reserved for future expansion:

     $C020     was cassette output toggle
     $C040     was gameport utility strobe

The address $C060 was used for cassette input in //e, II+, and II systems. In the //gs some sources say $C060 is used for a fourth gameport switch input, called switch 3 (I cannot find such a signal on any gameport pin in my //gs). $C061 through $C063 are still called switches 0, 1, and 2. $C061 is still attached to the Open-Apple key, and $C062 to the Solid-Apple.

$C071-$C07F is used by the //gs for ROM-based code, rather than soft swtiches. This range of memory is always available, so interrupts vector here.

       C071: E2 40        SEP #$40   SET V-BIT
       C073: 50           (BVC OPCODE, SKIPS NEXT BYTE)
       C074: B8           CLV        CLR V-BIT
       C075: 5C 10 00 E1  JMP $E10010
       C079: 38           SEC
       C07A: 90           (BCC OPCODE, SKIPS NEXT BYTE)
       C07B: 18           CLC
       C07C: 5C 5B B6 FF  JMP $FFB65B

Address $C021 controls the NTSC video output. Store $80 to kill color output, or $00 to enable color. This has no effect on the RGB output.

Address $C022 controls the RGB video output text and background color. The high four bits determine the text (foreground) color, and the low four bits determine the background color. The border color is controlled by the low four bits of $C034. In all of these, the color code is the same as lo-res graphics.

$C023 = VGC interrupt status and enable byte.

$C024 = Mouse data byte

$C025 = Keyboard data register
        bit 7 = Open-Apple
        bit 6 = Solid-Apple
        bit 5 = flags this data updated without a keypress
        bit 4 = numeric keypad key depressed
        bit 3 = repeat active
        bit 2 = caps lock down
        bit 1 = control key down
        bit 0 = shift key down

$C026 = data register for talking to 50740 chip

$C027 = keyboard and mouse status byte

$C028 = ROM bank toggle.  In the normal state with LCROM and I/O shadowing on addresses $D000-FFFF map directly into the same locations in bank $FF.  After one LDA $C028, $D000-FFFF access $9000-BFFF in bank $FF.  State of the toggle may be read in $C068 (state register) bit 1.

$C029 = control for new video states.
        bit 7 = enable super hi-res
        bit 6 = enable linear addressing for hi-res
        bit 5 = force monochrome in super hi-res
        bit 0 = enable bank 1 latch
        Whatever you do to the data in this byte, be sure you 
        do not change bit 0.  First read the byte, then change 
        bits 5,6,7 as you like, then store the byte.

$C02A = reserved

$C02B = foreign language selection
        bits 765 = 0 for USA English
                 = 1 for UK English
                 = 2 for French
                 = 3 for Danish
                 = 4 for Spanish
                 = 5 for Italian
                 = 6 for German
                 = 7 for Swedish
        bit 4 = 0 for NTSC, = 1 for PAL
        bit 3 = 1 for above languages, = 0 for USA
        The ASCII codes which change are $23, 40,
        5B, 5C, 5D, 60, 7B, 7C, 7D, and 7E.

$C02C = Address for test mode read of character ROM

$C02D = slot ROM enables, where bit x controls slot x.
        (do not enable slot 0 or 3!)

$C02E = vertical video address / 2

$C02F = horizontal video address in bits 6-0
        lsb of vertical address in bit 7
        In NTSC mode, the vertical address runs from $0FA 
        through $1FF, for 262 lines.  The horizontal address 
        runs from 40 through 7F, and 00 (65 different values)

$C031 = 3.5" disk drive control
        bit 7 = head select
        bit 6 = drive enable

$C032 = Clear Scan interrupt
        bit 6 = 0 to clear 1 second interrupt
        bit 5 = 0 to clear scanline interrupt

$C033 = clock chip data register

$C034 = clock chip control register & border color
        bit 7 = 1 to strobe data into clock
              = 0 when data has been accepted
        bit 6 = 1 to read chip, =0 to write chip
        bit 5 = chip enable after transfer
        bits 3-0 = border color

$C035 = shadow control register
        bit 6 = 0 to shadow I/O and "Language card".  When IOLC shadowing is on, the RAM at $C000-CFFF is folded into a second bank at $D000-DFFF, making a "language card".
        bit 4 = 0 to shadow aux hi-res page (used with bits 1 and 2 to inhibit shadowing in bank $01)
        bit 3 = 0 to shadow 32K video buffer ($2000-9FFF in bank $00)
        bit 2 = 0 to shadow hi-res page 2 ($4000-5FF in banks $00 and $01)
        bit 1 = 0 to shadow hi-res page 1 ($2000-3FFF in banks $00 and $01)
        bit 0 = 0 to shadow text pages ($400-7FF in both banks $00 and $01.  Text page 2, $800-BFF, is never shadowed.)
        RESET clears this register, enabling all shadowing.

$C036 = speed control and disk motor on detection
        bit 7 = 0 for slow speed
        bit 4 = 0 (if you make it =1, it will stop you cold!  I got FATAL SYSTEM ERROR 0681.  Supposedly it causes shadowing in ALL banks!)
        bits 3-0 = motor on detection bits for slots 7-4.  For example, if bit 2 is on, the system will slow down to 1MHz operation when the address $C0E9 (slot 6 motor-on command) is detected.  The system will speed up again when $C0E8 (slot 6 motor-off) is detected, unless bit 7 = 0.

$C037 = DMA bank address register

$C038 = SCC channel B command register
$C039 = SCC channel A command register
$C03A = SCC channel B data register
$C03B = SCC channel A data register

$C03C = sound control
        bit 7 = 1 if busy
        bit 6 = 1 to enable auto-increment
        bit 5 = 0 to access chip, =1 to access RAM
        bits 3-0 = volume of DAC

$C03D = sound data register
$C03E = sound address low
$C03F = sound address high

$C041-$C048 used for mouse
$C049-$C04F reserved

$C068 = state register (both read and write)
                       same as
                       bit 7 of
        bit 7 = ALTZP   $C016
        bit 6 = PAGE2   $C01C
        bit 5 = RAMRD   $C013
        bit 4 = RAMWRT  $C014
        bit 3 = RDROM   $C012
        bit 2 = LCBANK  $C011
        bit 1 = ROMBNK  ----- reads $C028 toggle
        bit 0 = SLOTCX  $C015

$C069-$C06C reserved

$C06D = test mode bit register
$C06E = clear test mode
$C06F = set test mode

I don't have the foggiest idea what the test mode is or how to use it, but it sounds interesting.

I hope with this information as a start, you can learn a lot more about your //gs. If you find any errors above, please let us know.

       C02x       C03x        C04x       C05x     C06x

    0  --rsvd--   Speaker     --rsvd--   Graphics Switch 3??
    1  MONO       3.5 Disk    Mouse Int  Text     Switch 1
    2  TXT Color  Scan Int    --rsvd--   Unmixed  Switch 1
    3  VGC Int    Clock Data  --rsvd--   Mixed    Switch 2
    4  MouseData  Clock Ctrl  M Delta X  Text 1   Paddle 0
    5  Key Mods   Shadow      M Delta Y  Text 2   Paddle 1
    6  KM Data    Speed       M Flags    Lo Res   Paddle 2
    7  KM Status  DMA Bank    VBL Int    Hi Res   Paddle 3
    8  ROM Bank   SCC B Cmd   XY Int     Clr AN0  State Reg
    9  New Video  SCC A Cmd   --rsvd--   Set AN0  --rsvd--
    A  --rsvd--   SCC B Data  --rsvd--   Clr AN1  --rsvd--
    B  Lang Sel   SCC A Data  --rsvd--   Set AN1  --rsvd--
    C  CharROM    Sound Ctrl  --rsvd--   Clr AN2  --rsvd--
    D  Slot ROM   Sound Data  --rsvd--   Set AN2  Test Mode
    E  Vert Cnt   Sound AdrL  --rsvd--   Clr AN3  Test Off
    F  Horiz Cnt  Sound AdrH  --rsvd--   Set AN3  Test On

Improved & Safer Text I/O Speedup for DOSLouis Pitz

I have been trying to piece together a lot of DOS 3.3 improvements published in back issues of Apple Assembly Line, and I especially wanted to fit the Text File I/O improvements made by Paul Schlyter. (See AAL July 1983: "Speeding Up Text File I/O", by Paul Schlyter; and "Making Paul's Patches Fit in DOS", by Bob Sander-Cederlof.) Bob has said in several phone conversations that he continues to have reservations about using this patch, because of the possibility of DOS becoming confused over the state of the File Manager Workarea. He even suggestED using the space for some other improvements.

Why didn't I follow his advice? I guess the idea of a faster way of handling TEXT files is so attractive that I keep thinking the approach can be made to work. I made some small changes to the 1983 patches, and I believe they are now safe. Bob's last-minute warning on page 17 of the 1983 article still holds, however: do not try to install the patch from inside an EXEC file. You should not patch a piece of code while it is being used! The code as it is here can be BRUN from within a small HELLO program with no trouble.

The boxed comment on page 16 of the 1983 article, about patching $A1B1 to $14, was interesting. This value determines how many locations will be cleared to zero by the loop starting at $A1AE. The loop as coded in DOS clears too many bytes, so Bob's patch fixes it. Then he can use those bytes at the end for a variable of his own. Another loop at $ABDC also clears out the location Bob wanted to use, but it did not interfere with Bob's use. I chose to avoid the problem by using a different location for my PNTR.SAVE variable: $B5FE,B5FF. This is still close enough to $B5C7 (start of FM workarea) to use the indexing trick (at lines 1740,1750 in my program).

I saved the one extra byte Bob mentioned on page 15 of his article, by avoiding the SEC in the CHECK.OPCODE subroutine. I changed the origin of the patch from $B6B3 to $B6B4, to give one more byte to another set of patches I am using.

Bob's main reservation about using the speedup was the danger of FLAG being set without a valid address in PNTR.SAVE. I avoid this by not setting FLAG until after placing a valid address in the pointer (lines 1820-1840), and by clearing FLAG before using the pointer to save the workarea (lines 1730-1760).

FLAG is cleared by all FM commands other than Read and Write, including INIT, so there is no way an initialized disk will have the sign bit of FLAG set. If you follow all abnormal exits from handling text files with the CLOSE command, that will clear FLAG in those cases; it was good policy to do this even before these patches. [Maybe we should find all those abnormal exits, and make them specifically clear FLAG...]

I hope you like my version and agree that the enhancement can be safely used. All my tests, so far, indicate it is safe.

  1000 *SAVE S.FAST TEXT (PITZ)
  1010 *--------------------------------
  1020        .OR $300
  1030 *--------------------------------
  1040 PPNTR  .EQ $00,01
  1050 PATCH  .EQ $02,03
  1060 *--------------------------------
  1070 PATCHER
  1080        LDA #PATCHES-1    POINT TO SET OF PATCHES
  1090        STA PPNTR
  1100        LDA /PATCHES-1
  1110        STA PPNTR+1
  1120        LDY #0            Y = 0 FROM NOW ON
  1130 .1     JSR GET.BYTE      LENGTH OF NEXT PATCH
  1140        BEQ .4            ...FINISHED PATCHING
  1150        TAX               LENGTH IN X
  1160        JSR GET.BYTE      ADDRESS OF PATCH
  1170        STA PATCH         ...INTO POINTER
  1180        JSR GET.BYTE
  1190        STA PATCH+1
  1200 .2     JSR GET.BYTE      NEXT BYTE OF PATCH
  1210        STA (PATCH),Y     TUCK IT AWAY
  1220        INC PATCH         POINT TO NEXT ONE
  1230        BNE .3
  1240        INC PATCH+1
  1250 .3     DEX               ARE WE FINISHED?
  1260        BNE .2            ...NO
  1270        BEQ .1           ...ALWAYS
  1280 .4     RTS
  1290 *--------------------------------
  1300 GET.BYTE
  1310        INC PPNTR         ADVANCE POINTER
  1320        BNE .1
  1330        INC PPNTR+1
  1340 .1     LDA (PPNTR),Y     GET BYTE
  1350        RTS
  1360 *--------------------------------
  1370        .MA PATCH
  1380        .DA #LEN.]1,]2
  1390 PAT.]1 .PH ]2
  1400        .EM
  1410 *--------------------------------
  1420        .MA ENDP
  1430        .EP
  1440 LEN.]1 .EQ *-PAT.]1
  1450        .EM
  1460 *--------------------------------
  1470 PATCHES
  1480 *--------------------------------
  1490        >PATCH 1,$B6B4
  1500 *--------------------------------
  1510 *   Paul Schlyter's Text File Speed-up,
  1520 *   as modified by Bob Sander-Cederlof (June 1983)
  1530 *   as modified by Louis Pitz      (December 1986)
  1540 *--------------------------------
  1550 PNTR   .EQ $42,43
  1560 *--------------------------------
  1570 COPY.BUFFER.TO.WORKAREA .EQ $AE6A
  1580 SAVE.WORKAREA           .EQ $AE81
  1590 POINT.TO.WORKAREA       .EQ $AF08
  1600 SETUP.PNTR              .EQ $AF12
  1610 FM.OPCODE               .EQ $B5BB
  1620 FM.WORKAREA             .EQ $B5C7
  1630 PNTR.SAVE               .EQ $B5FE,B5FF    ****
  1640 *--------------------------------
  1650 PATCH1 JSR CHECK.OPCODE
  1660        BCC .1       NOT READ/WRITE
  1670        CMP PNTR.SAVE
  1680        BNE .1       NO
  1690        CPX PNTR.SAVE+1
  1700        BEQ PATCH3   YES, RETURN NOW
  1710 .1     BIT FLAG
  1720        BPL .2
  1730        LSR FLAG     CLEAR SIGN BIT     ****
  1740        LDX #PNTR.SAVE-FM.WORKAREA
  1750        JSR SETUP.PNTR
  1760        JSR SAVE.WORKAREA               ****
  1770 .2     JMP COPY.BUFFER.TO.WORKAREA
  1780 *--------------------------------
  1790 PATCH2 JSR CHECK.OPCODE
  1800        BCS .1       ... READ OR WRITE  ****
  1810        JMP SAVE.WORKAREA               ****
  1820 .1     STA PNTR.SAVE
  1830        STX PNTR.SAVE+1
  1840        ROR FLAG     SET SIGN BIT
  1850 PATCH3 RTS
  1860 *--------------------------------
  1870 CHECK.OPCODE
  1880        JSR POINT.TO.WORKAREA
  1890        LDA FM.OPCODE
  1900        CMP #3       READ?              ****
  1910        BEQ .1       YES
  1920        CMP #4       WRITE?             ****
  1930        BEQ .1
  1940        CLC
  1950 .1     LDA PNTR
  1960        LDX PNTR+1
  1970        RTS
  1980 *--------------------------------
  1990 FLAG   .HS 00       MUST START WITH FLAG=0
  2000 *--------------------------------
  2010        >ENDP 1
  2020 *--------------------------------
  2030    .LIST MOFF
  2040        >PATCH 2,$AB0B
  2050        .DA PATCH1
  2060        >ENDP 2
  2070 *--------------------------------
  2080        >PATCH 3,$B38F
  2090        .DA PATCH2
  2100        >ENDP 3
  2110 *--------------------------------
  2120        .DA #0       <<< END OF PATCHES >>>
  2130 *--------------------------------

The Monitor ctrl-Y Command in the //gsBob Sander-Cederlof

The Apple monitor has always had a control-Y command, called the "user" command. It allows you to add at least one command to the monitor on a temporary basis. The older versions merely JMPed to $03F8, but the //gs does a little more.

If you ran my program from last month's AAL, which printed out the addresses in the //gs monitor command table for each command, you found out that the ctrl-Y command starts at $FE13. To see what that is, enter the monitor L command as follows:

       *FF/FE13L

The code in my //gs is CLC, XCE, and JMP $E100A0. The instruction installed at $E100A0 when you turn on the //gs is JMP $FFFE19, and the code at FFFE19 is SEC, XCE, and JMP $03F8. So we end up at $3F8 after all!

If you want to, you can ignore the new stuff and just use ctrl-Y like you did in the old days. Or, you can put a 3-byte address for a Native mode program into the JMP instruction at $E100A0.

I wrote a program to link into the $E100A0 vector and print out the registers, the mode, and the M and X status bits. I wanted to see for certain in what state the monitor called my ctrl-Y code.

Lines 1080-1150 install the address of my ctrl-Y code in the bank $E1 vector.

Lines 1180-1240 save the mode and the three main registers. Lines 1180-1210 on entry operate together with lines 1590-1610 at the exit, allowing me to run in Emulation mode regardless of the mode my caller was in. My program returns to the caller's mode and mx-state when it is finished.

Lines 1250-1350 show the A-, X-, and Y-registers as they were upon entry. I am only showing the low-byte of each of these, even though in the 65816 they have two bytes. Shame on me!

Lines 1410-1540 print either "EMULATION MODE" or "NATIVE MODE: M=1, X=1". If you write a little driver program to call MY.Y with various other combinations of M and X, you will see that MY.Y prints them as you have them.

Lines 1640-1680 print a message followed by the contents of the A-register in hexadecimal. I use it to print the contents of the three registers.

Lines 1700-1740 print "0" if the Z status bit is set upon entry; otherwise, they print a "1".

Lines 1760-1800 are just about the shortest loop for printing out an ASCII string I have come up with in my ten years of Apple programming. Strings are assembled with bit 7 high, and terminated by a 00 byte. On entry, the Y-register points to the string to be printed, relative to the base address QTS. If you read AAL a lot, you have surely seen this little gem a lot.

After assembling the program with the S-C Macro Assembler, type MGO MY.Y to see the state of things when the program is called that way. Then type dollar sign, control-O, control-Y, and the RETURN key, to see the state of things when the ctrl-Y command is used. Or, go to the monitor with the MNTR command, and then just type a control-Y and RETURN. You might also try it with various configurations of hex values before the control-Y.

  1000 *SAVE TEST.CTRL.Y
  1010 *--------------------------------
  1020        .OP 65816
  1030 *--------------------------------
  1040 MON.COUT   .EQ $FDED
  1050 MON.PRBYTE .EQ $FDDA
  1060 MON.CROUT  .EQ $FD8E
  1070 *--------------------------------
  1080 T
  1090        LDA #MY.Y    INSTALL CTRL-Y VECTOR
  1100        STA $E100A1
  1110        LDA /MY.Y
  1120        STA $E100A2
  1130        LDA #0
  1140        STA $E100A3
  1150        RTS
  1160 *--------------------------------
  1170 MY.Y
  1180        PHP          SAVE MX BITS
  1190        SEC
  1200        XCE
  1210        PHP          SAVE E-BIT
  1220        PHA
  1230        PHX
  1240        PHY
  1250 *---Display registers------------
  1260        LDY #Q.AEQ
  1270        LDA 3,S
  1280        JSR DISPLAY.REG
  1290        LDY #Q.XEQ
  1300        LDA 2,S
  1310        JSR DISPLAY.REG
  1320        LDY #Q.YEQ
  1330        LDA 1,S
  1340        JSR DISPLAY.REG
  1350        JSR MON.CROUT
  1360 *---Display mode & mx bits-------
  1370        LDA 4,S      E-BIT
  1380        AND #1
  1390        BNE .1       ...emulation mode
  1400 *---Native mode------------------
  1410        LDY #Q.NATIVE
  1420        JSR QTO
  1430        LDA 5,S      MX BITS
  1440        AND #$20     ISOLATE M BIT
  1450        JSR PRINT.0.OR.1
  1460        LDY #Q.XEQ
  1470        JSR QTO
  1480        LDA 5,S      MX BITS
  1490        AND #$10     ISOLATE X BIT
  1500        JSR PRINT.0.OR.1
  1510        BRA .2
  1520 *---Emulation mode---------------
  1530 .1     LDY #Q.EMUL
  1540        JSR QTO
  1550 *---Return to caller-------------
  1560 .2     PLY
  1570        PLX
  1580        PLA
  1590        PLP          RESTORE E-BIT
  1600        XCE
  1610        PLP          RESTORE MX BITS
  1620        RTS
  1630 *--------------------------------
  1640 DISPLAY.REG
  1650        PHA
  1660        JSR QTO
  1670        PLA
  1680        JMP MON.PRBYTE
  1690 *--------------------------------
  1700 PRINT.0.OR.1
  1710        BEQ .1
  1720        LDA #1
  1730 .1     ORA #"0"     CHANGE TO ASCII "0" OR "1"
  1740        JMP MON.COUT
  1750 *--------------------------------
  1760 QTO1   JSR MON.COUT
  1770        INY
  1780 QTO    LDA QTS,Y
  1790        BNE QTO1
  1800        RTS
  1810 *--------------------------------
  1820 QTS
  1830 Q.AEQ      .EQ *-QTS
  1840            .AS -/A=/
  1850            .HS 00
  1860 Q.XEQ      .EQ *-QTS
  1870            .AS -/, X=/
  1880            .HS 00
  1890 Q.YEQ      .EQ *-QTS
  1900            .AS -/, Y=/
  1910            .HS 00
  1920 Q.NATIVE   .EQ *-QTS
  1930            .AS -/NATIVE MODE:  M=/
  1940            .HS 00
  1950 Q.EMUL     .EQ *-QTS
  1960            .AS -/EMULATION MODE/
  1970            .HS 00
  1980 *--------------------------------

How to Use the USR Command
in the DOS 3.3 $D000 Version
of the S-C Macro Assembler
Bob Sander-Cederlof

If you are running the DOS 3.3 version of my assembler in a 64K or larger Apple, and if you are not using the Laumer Research Full Screen Editor, then you have a 4K bank of RAM available for adding features. It is a little tricky to use, but I am going to show you a secret which will make it easier.

Version 2.0 of the S-C Macro Assembler has several built-in handles for user extensions: USR, PRT, Esc-U, the "." command, and the .US directive. The program below illustrates how to vector the USR directive into the alternate bank at $D000, and how to get back.

Lines 1160-1480 are a program to install your USR code. The first section copies your code into the $D000 bank, and the second section patches the USR vector in the S-C Macro Assembler. I put the origin at $2000 for the installation code, because that is a free area (big enough to hold all the code which will fit at $D000) right after loading the S-C Macro Assembler. You can either manually install it by typing "BRUN B.USR.CODE", or add that line to the EXEC file used to load the S-C Macro Assembler.

The only patch made in the S-C Macro code is to replace the JMP $D028 which is at $D006 with a LDA $C08B instruction. When you type the USR command S-C will jump to $D006, execute the LDA $C08B, and suddenly find itself at $D009 in the alternate bank. That is where your USR program should begin, as you can see in my program below at lines 1570-1620. I made my first instruction in the USR code be another LDA $C08B, which write-enables the RAM. If my code used any of the $Dxxx area for variables, I would need it to be write-enabled.

To get back to the S-C code, the USR program jumps to $D000 (see line 1720 and lines 1510-1530). The instruction there is a LDA $C083, which switches back to S-C's $D000 bank and continues execution at $D003, which is one of S-C's soft entry points.

You could use the six bytes from $D003 through $D008 for variables, if you wish.

You can also link into the PRT command in this same manner, putting the LDA $C08B instruction at $D009 in the S-C bank, and gaining control at $D00C in the alternate bank. If you want both USR and PRT, you can do that too. Just make the instruction at $D009 in the USR-PRT bank a JMP USR, and start the PRT code at $D00C. The "." command can be set up the same way. The .US directive and the Esc-U keyboard function would take a little more setup, because they do not return to the soft re-entry of S-C Macro.

My code at lines 1630-1700 is purely a demonstration, to prove that the USR command really works. Use your imagination, create some really useful functions, and replace my demo with them.

Phil Goetz has sent me the source code for a Symbolic Step/Trace/Disassembly Utility that would fit into this USR patch very nicely. In fact, he had it set up in a similar manner, and his work led me to the discovery of this technique. If all goes well, we should be able to publish his program soon.

Meanwhile, see what you can do with this new tool.

  1000 *SAVE S.USER DEMO
  1010 *--------------------------------
  1020 *   Demonstration of linking the USR command
  1030 *      in the DOS 3.3 version of S-C Macro
  1040 *      Assembler (loaded at $D000), to a
  1050 *      program located in the alternate bank
  1060 *      at $D000.
  1070 *--------------------------------
  1080        .OR $2000
  1090        .TF B.USR.CODE
  1100 *--------------------------------
  1110 SRCP   .EQ $00,01
  1120 DEST   .EQ $02,03
  1130 *--------------------------------
  1140 USR.VECTOR .EQ $D006 thru $D008
  1150 *--------------------------------
  1160 INSTALLER
  1170        LDA $C08B    Select alternate $D000 bank
  1180        LDA $C08B    and write-enable it
  1190 *---Set up pointers for move-----
  1200        LDA #USR.CODE
  1210        STA SRCP
  1220        LDA /USR.CODE
  1230        STA SRCP+1
  1240        LDA #$D0
  1250        STA DEST+1
  1260        LDY #0
  1270        STY DEST
  1280        LDX /USR.CODE.LEN+255
  1290 *---Move code into destination---
  1300 .1     LDA (SRCP),Y
  1310        STA (DEST),Y
  1320        INY
  1330        BNE .1
  1340        INC SRCP+1
  1350        INC DEST+1
  1360        DEX
  1370        BNE .1
  1380 *---Install User Vector----------
  1390        LDA $C083    Select S-C $D000 bank
  1400        LDA $C083    and write-enable it
  1410        LDA #$AD     "LDA $C08B"
  1420        STA USR.VECTOR
  1430        LDA #$C08B
  1440        STA USR.VECTOR+1
  1450        LDA /$C08B
  1460        STA USR.VECTOR+2
  1470 *---Return to S-C Macro----------
  1480        JMP $D003
  1490 *--------------------------------
  1500 USR.CODE   .PH $D000
  1510 RETURN.TO.SC.MACRO
  1520        LDA $C083    Next instruction in other bank
  1530 *                   at S-C Macro "SOFT" entry.
  1540 *--------------------------------
  1550            .BS 6    These six bytes could be used
  1560 *                   for variables if you wish.
  1570 *--------------------------------
  1580 *   USR.ENTRY must be at $D009, because the USR
  1590 *   command in S-C is a bank switch at $D006-8.
  1600 *--------------------------------
  1610 USR.ENTRY
  1620        LDA $C08B    Finish write-enabling this bank
  1630 *---User program here------------
  1640        LDY #0       You substitute a REAL program here!
  1650 .1     LDA MESSAGE,Y
  1660        BEQ .2
  1670        JSR $FDED    PRINT A CHARACTER
  1680        INY
  1690        BNE .1       ...ALWAYS
  1700 .2     JSR $FBE2    RING THE BELL JUST FOR FUN
  1710 *---Return to S-C Macro----------
  1720        JMP RETURN.TO.SC.MACRO
  1730 *--------------------------------
  1740 MESSAGE
  1750        .HS 8D
  1760        .AS -/This is just a demonstration/
  1770        .HS 8D
  1780        .AS -/of the USR command./
  1790        .HS 8D.8D.00
  1800 *--------------------------------
  1810        .EP
  1820 *--------------------------------
  1830 USR.CODE.LEN .EQ *-USR.CODE
  1840 *--------------------------------

Little Bugs in ProDOS /RAMBob Sander-Cederlof

When a ProDOS device driver reads or writes a block, it is supposed to return an error code in the A-register. If there was an error, this number will be nonzero, and the Carry status bit will be set. If there was no error, the A-register is supposed to contain zero, and Carry will be clear.

The other day I was working with a program that was supposed to read and write blocks from the /RAM drive under ProDOS. My program printed the error code returned by the call, and I was getting a non-zero value in the A-register even when there was no error. I just assumed this was a trivial bug in ProDOS, and ignored it. However, I was not using the standard ProDOS /RAM driver, because I had installed the Applied Engineering driver which uses the entire RAMWORKS card for /RAM. Anyway, I just went on about my business.

A few days later the January 1987 issue of Nibble arrived at my doorstep. In a letter to the editor on page 123, from Steven Humpage, there was an explanation of the bug. The part of the device driver that resides in Main RAM is located at $FF00. At $FF47 is a routine named EXIT, which restores some zero-page locations, restores a vector in page 3, and returns. This code is called with either zero in A and Carry clear, or an error code in A and carry set. The routine begins with PHP, PHA and ends with PLP, PLA. WRONG! This swaps the P- and A-registers. Since the C-bit is bit 0 in the P-register, a zero (from the A- register) results in carry clear. However, the P-register con- tents come back in A, something like $36. Since the only error code the /RAM driver returns is $27, and this has bit 0 set, swapping registers leaves carry set. It also sets the V-bit, which could be unexpected. The error code in A now becomes what the status was, so we get an error code of $35, I believe.

The same bug exists in AE Prodrive. The bug can easily be fixed, by the following sequence. I am showing it as I do it from within the S-C Macro Assembler. If you do it from within BASIC.SYSTEM, you will have to CALL-151 to get into the monitor to install the patch.

       :UNLOCK PRODOS                 :UNLOCK PRODRIVE
       :BLOAD PRODOS,TSYS,A$2000      :BLOAD PRODRIVE
       :$2C5F:68 28                   :$245F:68 28
       :BSAVE PRODOS,TSYS,A$2000      :BSAVE PRODRIVE
       :LOCK PRODOS                   :LOCK PRODRIVE

I am assuming ProDOS 1.1.1. The bug has already been fixed in version 1.2.

While we are at it, Humpage also pointed out another bug which affects both ProDOS and Prodrive. The code which ProDOS puts into pages 2 and 3 in the AuxRAM has an error. The CMP #$0D at $34A should be CMP #$0E. As it is it allows writing to block 7, which is then mapped back over the top of page zero and one, clobbering things disastrously. This change write protects that block. You only need to change the PRODOS file, because Prodrive uses it also. Change $2B4B from $0D to $0E.


New Features in Apple //gs MonitorBob Sander-Cederlof

The //gs Monitor (that which you get by typing CALL -151 in Applesoft, not the video screen) has a lot of neat new features. There are a significant number of new commands, and new capablilities to old ones.

The first thing you need to know is that you can now use 24-bit addresses in the monitor. These are entered by typing the bank number, a slash, and the 16-bit address within the bank. The monitor remembers the last bank number entered, so you do not necessarily have to type the bank number. For example, to examine memory in the monitor ROM itself, you could type the following:

       *FF/F800.FFFF

If you try that, you will notice that both hex and ASCII values are shown for each location. If you are in 40-column mode, you get eight bytes per line as in older monitors; however, in 80-column mode you now get sixteen bytes per line. This means you can easily see an entire page in both hex and ASCII on the screen at once.

Another difference in addressing involves accessing so-called "language card" RAM. In older monitors you could flip the soft switches using monitor commands, and then address the language card. Of course, if you flipped it into a mode to read from the language card RAM, you had to first be sure to make a copy of the monitor ROM in RAM. Anyway, this no longer works. Now you cannot really flip those switches, because the //gs monitor will flip them right back before the next command. Now you can address all the RAM from $C000 to $FFFF in any bank directly.

If you have I/O shadowing on, and you almost always do, then addresses in banks $00 and $01 in the range $C000-CFFF are I/O addresses. The I/O addresses are really in banks $E0 and $E1, but shadowing makes them accessible also in these other banks. To access the $Cxxx addresses in RAM in those four banks, you can see them at $Dxxx by changing the value of a monitor variable "L". Type "1=L" to see the $Cxxx region (2nd bank of language card), or "0=L" to see the real $Dxxx region (1st bank of language card).

That reminds me of another difference. Most of the time the //gs monitor does not care whether you type in upper or lower case. However, some commands are case sensitive. Commands which use a register name, such as the "=" command, are case sensitive. Register names MUST be in the proper case. Some registers must be in upper case, and some must be in lower case.

The disassembler in the //gs monitor handles the entire 65816 opcode set. It has all the expected trouble handling immediate mode disassembly, since there is no way the monitor can tell what mode code will be in when it is executed. You have to control the state of the disassembler's m- and x-bits yourself, by entering commands at the appropriate times. You control their state by typing any of these four commands:

       0=m    1=m    0=x    1=x

This can be pretty messy and painful, but it is one of those things we have to put up with. Of course, the monitor could be a little smarter, and TRY to follow the XCE, REP, and SEP commands, assuming that normal code will be "nice". Then we could just override when absolutely necessary. However, we have to live with what is there for now.

The disassembler now puts out a status line at the top of each set of 20 lines, which shows the state of the m- and x-bits, and the L-variable mentioned above. This is somewhat of a nuisance when using "LLLLLLL" to print out a long range, but we can live with it too. Another difference is that BRK disassembles with a one-byte value in the operand field. This has always been the correct way to handle BRK, but nobody knew it in the assembler and dis-assembler business until the 65816 came out.

The mini-assembler is also included in the //gs monitor. You get into it by using the "!" command. The prompt changes from "*" to "!". You can leave the mini-assembler and go back to the monitor by typing a RETURN key immediately after the prompt. Operation inside the mini-assembler is just like the old mini-assembler, with a few enhancements. You can assemble to any bank of RAM, specifying the bank before a slash. All of the 65816 opcodes can be assembled. You indicate 16-bit immediate by typing 3- or 4-digit values after a "#", 8-bit immediate by typing 1- or 2-digit values.

You can also enter ASCII and hex values while in the mini- assembler. Type a quotation mark followed by the characters you wish to enter in ASCII mode. Terminate the string with a RETURN. Type a colon to indicate that you are entering hex values. The number of bytes of hex data you store depends on the number of digits you type; you can type from 1 to 8 digits, occupying 1 to 4 bytes. Multi-byte values will be stored with the least significant byte first. Practice with these a while and you will get the hang of it.

Step and Trace commands are included in the //gs monitor, but they are not "plugged in". They point to vectors in bank $E1, and all they do now is print "Step" and "Trace". The TRACE vector is at $E1/0074, and the STEP vector is at $E1/0078. By loading a real Step/Trace program into RAM and hooking into the vectors, you can get them to work. I have seen them work, but I do not know where to put my hands on the working code or how to load it if I had it. Do any of you?

Here are the commands, with brief commentary. (The ^ means "Control-"; "addr" may be in form "bb/aaaa" or "aaaa".)

     ^B             JMP $00E000, initial entry to Applesoft.
     ^C             JMP $00E003, re-enters Applesoft.
     Q              JMP $0003D0, re-enters DOS or ProDOS.
     ^Y             JMP $E000A0, which normally does JMP $0003F8.

     ^E             Examine Stored Registers.  Displays the contents
                    of A X Y S D P B K M Q L m x e.
     ^R             Restore Stored Registers to normal monitor mode:
                    A=0000 X=0000 Y=0000 S=01FB D=0000 P=00
                    B=00 K=00 M=0C Q=80 L=1 m=1 x=1 e=1
     value=reg      Assign value to a stored register.  The register
                    names are case-sensitive:  A X Y S D P B K M Q
                    m x e L F.  ("F" is Filter for entering ASCII,
                    and will normally be 7F or FF.)

     s^K            Connect input hook to slot s.
     s^P            Connect output hook to slot s.

     ^S             During output, pause-restart.  (There appears
                    to also be an entry at the end of the command
                    table for ^S, but it is not really accessible
                    due to the loop counter used.)
     ^X             During memory-range display, terminate command.
                    (This, like ^S, is not really a command.)

     ^T             Similar to Applesoft TEXT command, changes
                    screen display to text page 1.
     I              Switch to Inverse display mode.
     N              Switch to Normal display mode.

     ^6c            Change cursor to new character c.
                    Control-6 DELETE restores original cursor.
                    Control-6 UNDERLINE is especially nice.

     addrG          Pick up stored registers and JSR addr.
                    Only works properly in bank 00, because
                    assumes user returns with RTS.
     addrX          Pick up stored registers and JSL addr.
                    Assumes user returns with RTL.
     addrR          Pick up stored registers and JMP addr.

     addrL          Disassemble 20 lines starting at addr.
     L              Disassemble next 20 lines.

     addr<addr.addrM   Move the range to the target.
     addr<addr.addrV   Compare the range to the target.
     zz<addr.addrZ     Fill the range with single byte value zz.

     \pattern\<addr.addrP   Search range for pattern, which may
                    contain hex values of 1 to 8 digits, hi-ASCII
                    strings ("abcd"), lo-ASCII strings ('abcd'),
                    and may be up to 236 bytes long!

     \#bytesin #bytesout parm1 ... parmN func# tool#\U
                    Call Tool (tool#) in Toolbox (func#) with
                    parameters (parm1 ... parmN) on the stack.
                    All the items between the \\ must be separated
                    with spaces. #bytesin is total # of bytes
                    that will be pushed onto the stack before
                    calling the tool.  #bytesout is the # of bytes 
                    the tool will leave on the stack.  Wow!
                    I dare you to try it!

     addrS or S     Step, only if loaded into RAM
     addrT or T     Trace, only if loaded into RAM

     hexvalue=      convert hexvalue to decimal
     =decvalue      convert decvalue to hexadecimal

     =T             display date & time mm/dd/yy hh:mm:ss
     =T=mm/dd/yy hh:mm:ss    Set date & time
                    (both must be capital T)

     addr           Display contents of addr.
     addr.addr      Display contents of range.
     addr:stuff     Enter information in hex or ASCII.
     value+value    add
     value-value    subtract
     value*value    multiply
     value_value    divide   

New ProDOS Version of S-C XREFBob Sander-Cederlof

An upgrade is now available for the ProDOS version of the S-C XREF program. XREF stands for Cross Reference, and if you are working on large programs with the S-C Macro Assembler you really need it. If you already have it, and are developing programs under ProDOS, you will enjoy this new upgrade.

The new version still fits in the same space, loading between $800 and $FFF. (It is actually a little shorter than the previous release.) There are, however, a lot of new features.

First and foremost, I added the ability to automatically handle a source program which uses the .IN or .INB directive for multiple source files. The previous release required you to create a complex EXEC file to control XREF if you wanted to make a complete cross reference for a program that was spread over several files. Sometimes a lot of juggling was required. For example, when I run XREF on the source code of the S-C Macro Assembler (which consists of over two dozen files on two floppies), I had to make two passes: one pass for symbols beginning with A-L, another for symbols starting with M-Z. The EXEC file for each consisted of over 50 lines. Too much!

Anyway, with the new version I can simply LOAD the main assembly control file (the source file containing the .IN directives) and type "-XREF". The new version automatically letters each included file for purposes of identifying where each cross reference came from. The main file, which contains the .IN directives, is identified by not having a letter. In other words, its identifier is a blank. The first 26 included files are lettered A-Z; the next 26 are lettered a-z; the next 10 are numbered 0-9; if you have another, it gets a punctuation symbol (:). Sixty-four files should be enough!

One reason I have so many separate source files for one program is so that I could squeeze out a meaningful cross reference. With this new version the file size is not so important. During operation, when XREF sees an include directive (either .IN or .INB), only 512 bytes are needed for reading the file. It is read in only one block at a time, equivalent to the normal .INB or .INB1. This leaves a lot more room for symbols.

The new version also prints a legend of the included files, showing the assigned letter or number and the file name for each. This makes it a lot easier to read the printed cross reference listing.

I simplified the syntax analysis portion of XREF, so that tables particularizing it to the opcodes of a single micro- processor are no longer necessary. This means that the same XREF program can operate on the source code of any of our assemblers. Not only 6502, 65C02, and 65816 code, but also 6800, 6805, etc. Now if we could just upgrade all the rest of our cross assemblers to run under ProDOS.... The purpose (in the previous version) for opcode tables was to inform XREF of which opcodes had no operands, and which if any had the option to be used both with and without operands. This is no longer necessary. Instead, I use a single user-set parameter to indicate how many spaces to allow after an opcode before believing the comment field has been reached. The default for this parameter is two, which works well with the 6502 family. Some other micorprocessors, which have opcode names of varying length, might require a higher value. I also added code to terminate the operand scan when a semi-colon is encountered.

Another new user-set parameter allows the user to choose to ignore the ,X and ,Y indexing in the cross reference listing. I personally found no benefit to seeing those two large entries, since X and Y were not really symbol names. The default position for this parameter avoids listing references to X and Y if they are only referenced and never defined within the program. If you liked it the old way, simply change the parameter setting.

I added a third entry point. The first one, at $800, starts with a fresh slate and does a cross reference for the program in memory. $803 continues a cross reference, without erasing the symbols already gathered. The new one, at $806, lists the cross reference that is already in the symbol table. I find this useful when I need a second copy and do not wish to wait for all the files to be processed over again.

I fixed some problems in the processing of the .TI directive, so that titles now appear properly on the cross reference listing. Of course, if you have more than one .TI directive in your source code, the entire cross reference listing will use the last one.

There are more improvements, but this is enough detail for this article. If you already own the S-C XREF, you can get the upgraded ProDOS version for $10. If you purchased the source code with your XREF, we will include the new source code too. If you do not have S-C XREF yet, the price is $50 including source code, with the DOS version on one side and the new ProDOS version on the other.


Source Code on Disk for ProDOS UtilitiesPete Johnson

If you have been thinking about typing in the sample programs from Beneath Apple ProDOS, but just haven't gotten around to it yet, there is an easy way out. Pete Johnson has done all the hard part for you. He typed them in and got them running, after a lot of head-scratching. If you send him $2 and a blank diskette, he will send you a copy. The source files are in the S-C Macro Assembler format, and he includes the binary object files also. The programs are ZAP, FORMAT, TYPE, FIB, and DUMP. He does not include MAP (which is in Basic) and DUMBTERM. Pete's address is P. O. Box 5, New London, Minnesota 56273.


Apple Assembly Line (ISSN 0889-4302) 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 $14 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.)