Volume 2 -- Issue 9 | June 1982

In This Issue...

- Implementing New Opcodes Using 'BRK'
- A New Hi-Res Function for Applesoft (HXPLOT)
- Bubble Sort Demonstration
- DOS File Exchange: A Review
- Macro Hint
- Yes/No Subroutine
- My Own Little Bell
- Using the Shift-Key Mod
- Search for Page-Zero References
- Automatic CATALOG for S-C Macro Assembler
- Examiner

Advertising in AAL

Due to the increased costs of printing more than 1600 copies per month, and with the desire to limit the percentage of advertising pages to less than 30% each month, I have decided to raise the page rate again.

For the July 1982 issue the price will be $50 for a full page, $30 for a half page. So-called "classified" ads, of up to forty words, will be $5.

About "The Other Epson Reference Manual"

I have received a number of complaints from readers that Cut the Bull software Co. doesn't cut the mustard. Their $5 checks have been cashed, but after many weeks they still have not received the booklet they ordered. I have no phone number for either the company or the owner, and apparently neither does Information. I know the booklet exists, because I have one. I believe the company plans to fill all the orders, but procrastination has taken over. (They sent me a copy of another booklet, which I don't plan to review.) Until they see fit to fill their back-orders, and to publish a phone number, I don't recommend ordering their booklet.

Implementing New Opcodes Using 'BRK' | Bob Sander-Cederlof

If you have the Autostart ROM, you can control what happens when a BRK instruction is executed. If you do nothing, a BRK will cause entry into the Apple Monitor, and the register contents will be displayed. But (if you have the Autostart Monitor) by a small amount of programming you can make the BRK do marvelous things.

Like simulate neat instructions from the 6809, which are not in the 6502, for example. I am thinking particularly of the LEAX instruction, which loads the effective address into a 16-bit register; of BSR, which enters a subroutine like JSR, but with a relative address; and of BRA, which is a relatively addressed JMP. With these three instructions you can write position-independent programs (programs that execute properly without any modification regardless of where they are loaded in memory).

I am thinking of these because of an article by A. Sato in "Lab Letters" (a publication of ESD Laboratories in Tokyo, JAPAN) Volume 6 No. 1, pages 91-93. It is all written in Japanese (see example below), but I think I deciphered what he is saying.

When a BRK instruction is executed, the program is interrupted as though a Non-Maskable Interrupt (NMI) occurred. The B bit in the status register is set, so the Apple can tell that the interrupt was caused by BRK rather than some external event. After making this determination, the Autostart Monitor performs a "JMP ($3F0)" instruction. This means that you can get control by placing the address of your own program into $3F0 and $3F1. The monitor initialization process puts the address $FA59 there.

By the time the monitor branches to the BRK processor (its own or yours) all the registers have been saved. The address of the BRK instruction plus 2 (PC) has been saved at $3A and $3B; the registers A, X, Y, P (status), and S (stack pointer) have been saved in $45 through $49, respectively.

BRK Interceptor/Interpreter

In the program below, lines 1180-1230 will set up the BRK-vector at $3F0 and $3F1 to point to your own BRK processor. Lines 1250-1320 back up the PC value by one, to point at the byte immediately following the BRK instruction. At this point I can decide what to do about the BRK.

Since I want to simulate the operation of LEAX, BSR, and BRA, I will use the BRK instruction to introduce a pseudo instruction of three bytes. I decided to copy A. Sato on this. LEAX is a BRK instruction followed by LDX from an absolute address. This is $AE in hexadecimal, followed by a 16-bit value representing a relative address. BSR is BRK followed by a JSR instruction ($20) and a relative address; BRA is BRK followed by a JMP instruction ($4C) and a relative address.

Looking back at the program, lines 1310 and 1320 store the address of the secondary opcode byte into PNTR and PNTR+1. These two bytes are inside an instruction at line 1760. I didn't want to use any page-zero space, so I had to resort to this kind of self-modifying code. While we are here, lines 1750-1780 pick up the byte whose address is in PNTR. Lines 1710-1740 increment PNTR. If we call GET.THIS.BYTE, it just picks up the byte currently pointed at. If we call GET.NEXT.BYTE, it increments the pointer and gets the next byte.

Lines 1330-1370 pick up the three bytes which follow the BRK. The opcode byte is saved in the Y-register. Lines 1380-1450 compute the effective address, by adding the actual address of the instruction to the relative address inside the instruction.

Lines 1470-1540 classify the opcode; if it is one of the three we have implemented, it branches to the appropriate code. If not, it jumps back into the monitor and processes the BRK in the normal monitor way.

Opcode Implementation

Lines 1560-1780 implement the three opcodes BSR, BRA, and LEAX. BRA (Branch Always) is the easiest one. We have already computed the effective address and stored it in the address field of the JMP instruction at line 1620. All BRA does is restore the registers (line 1610), and JMP to the effective address.

BSR (Branch to Subroutine) is only slightly harder. We first have to push the return address on the stack, and then do a BRA. Lines 1560-1590 do the pushing.

LEA (Load Effective Address) is the hardest. Lines 1650-1690 do the work. First GET.NEXT.BYTE moves the address in PNTR,PNTR+1 to point at the first byte of the next instruction. That is so we can continue exectution. Then MON.RESTORE gets back the original contents of all the registers. THEN LDY and LDX pick up the effective address in the Y- and X-registers. The high byte of the effective address is in the X-register, and the Z- and N-bits in the status register reflect the value of this byte. If you wish, you could modify this to not change the status by inserting a PHP opcode after line 1660, and PLP after line 1680; then the status register would remain unchanged by the entire LEA process. Or you could reverse lines 1670 and 1680, so that the status reflected the low-order byte of the effective address.

Demonstration Using the New Opcodes

Lines 1800 and beyond are a demonstration of the use of the new opcodes. First I defined some macros for the new opcodes. I didn't have to do this, but it is convenient if you have a macro assembler. If you don't, you can use the BRK instruction on one line, followed by a LDX, JSR, or JMP instruction with a relative address on the next line.

My macros are defined in a nested fashion. The BRK macro generates two lines: BRK on the first line, and a second line consisting of the specified opcode and operand. The LEA, BSR, and BRA macros call BRK to generate LDX, JSR, and JMP instructions after the BRK. The operand field is a relative address, computed within the BRK macro.

The demonstration program will run anywhere in memory, as long as the BRK interpreter has been loaded and initialized. You can test this by moving $871-89F to other places and running it. What it does is print out the message in line 2090.

     1010  *--------------------------------
     1020  *      IMPLEMENTING BSR, BRA, AND LEA OPCODES
     1030  *      USING THE 'BRK' VECTOR WITH THE
     1040  *      AUTOSTART ROM
     1050  *
     1060  *      ADAPTED FROM AN ARTICLE IN "LAB LETTERS"
     1070  *           BY A. SATO
     1080  *--------------------------------
     1090  MON.PC     .EQ $3A,3B
     1100  MON.XREG   .EQ $46
     1110  MON.YREG   .EQ $47
     1120  *--------------------------------
     1130  BRK.VECTOR .EQ $3F0,3F1
     1140  *--------------------------------
     1150  MON.BRK     .EQ $FA59
     1160  MON.RESTORE .EQ $FF3F
     1170  *--------------------------------
     1180  SETUP
     1190         LDA #BREAK.INTERPRETER
     1200         STA BRK.VECTOR
     1210         LDA /BREAK.INTERPRETER
     1220         STA BRK.VECTOR+1
     1230         RTS
     1240  *--------------------------------
     1250  BREAK.INTERPRETER
     1260         LDY MON.PC+1     PICK UP ADDRESS OF THIRD BYTE
     1270         LDX MON.PC
     1280         BNE .1       BACK UP TO SECOND BYTE
     1290         DEY
     1300  .1     DEX
     1310         STX PNTR     MODIFY ADDRESS IN GET.THIS.BYTE SUBROUTINE
     1320         STY PNTR+1
     1330         JSR GET.THIS.BYTE
     1340         TAY          OPCODE BYTE
     1350         JSR GET.NEXT.BYTE
     1360         PHA          ADDR-LOW BYTE
     1370         JSR GET.NEXT.BYTE
     1380         TAX
     1390         PLA
     1400         SEC          ADDR-HIGH BYTE
     1410         ADC PNTR     COMPUTE EFFECTIVE ADDRESS
     1420         STA EFF.ADDR
     1430         TXA
     1440         ADC PNTR+1
     1450         STA EFF.ADDR+1
     1460  *--------------------------------
     1470         CPY #$20     CLASSIFY OPCODE
     1480         BEQ BSR
     1490         CPY #$4C
     1500         BEQ BRA
     1510         CPY #$AE
     1520         BEQ LEA
     1530         STY MON.YREG
     1540         JMP MON.BRK
     1550  *--------------------------------
     1560  BSR    LDA PNTR+1   PUSH RETURN ADDRESS ON STACK
     1570         PHA
     1580         LDA PNTR
     1590         PHA          AND DO BRA
     1600  *--------------------------------
     1610  BRA    JSR MON.RESTORE
     1620         JMP *-*
     1630  EFF.ADDR   .EQ *-2
     1640  *--------------------------------
     1650  LEA    JSR GET.NEXT.BYTE  POINT AT NEXT INSTRUCTION
     1660         JSR MON.RESTORE   RESTORE A-REG AND STATUS
     1670         LDY EFF.ADDR      ADDR-LO IN Y
     1680         LDX EFF.ADDR+1    ADDR-HI IN X
     1690         JMP (PNTR)
     1700  *--------------------------------
     1710  GET.NEXT.BYTE
     1720         INC PNTR
     1730         BNE GET.THIS.BYTE
     1740         INC PNTR+1
     1750  GET.THIS.BYTE
     1760         LDA $FFFF    (FILLED IN)
     1770  PNTR   .EQ *-2
     1780         RTS
     1790  *--------------------------------
     1800  MSG    .EQ 0,1
     1810  JMP.COUT JMP $FDED
     1820         .MA LEA
     1830         >BRK LDX,]1
     1840         .EM
     1850         .MA BSR
     1860         >BRK JSR,]1
     1870         .EM
     1880         .MA BRA
     1890         >BRK JMP,]1
     1900         .EM
     1910         .MA BRK
     1920         BRK
     1930         ]1 ]2-:1
     1940  :1
     1950         .EM
     1960  TEST   >LEA MESSAGE
     1970         STX MSG+1
     1980         STY MSG
     1990         LDY #0
     2000  .1     LDA (MSG),Y
     2010         PHA
     2020         ORA #$80
     2030         >BSR JMP.COUT
     2040         INY
     2050         PLA
     2060         BPL .1
     2070         LDA #$8D     CARRIAGE RETURN
     2080         >BRA JMP.COUT
     2090  MESSAGE .AT /THIS IS MY MESSAGE/

A New Hi-Res Function for Applesoft | Mike Laumer

Most people use the language card as nothing more than a ROM simulator for the other version of BASIC that is not on the motherboard. But it can do much more since the memory is actually RAM. Indeed Bob S-C's Macro Assembler has a version which runs in a Language Card. The FLASH! Integer BASIC compiler which I wrote uses the language card in place of a disk file providing higher speed compilations for those people who have a language card.

One nice aspect of having the language card is the ability to move Apple software from ROM to RAM in the card and make changes to add a new capability. Some people have done this already with the Apple monitor to add an extra feature or two at the expense of another (who needs the tape I/O routines).

The program assciated with this article will allow you to patch a RAM card version of Applesoft to modify the 'HPLOT' command to function as an 'HXPLOT' command. What is 'HXPLOT' you say. Remember the DRAW and XDRAW commands in Applesoft. The 'DRAW' command will place a shape on the screen; 'XDRAW' does the same thing, but 'XDRAW' has the unique ability to redraw the shape and erase it from the screen leaving whatever was on the screen initially still intact. The 'HXPLOT' function in the listing functions the same way for the 'HPLOT' command as 'XDRAW' does for the 'DRAW' command.

I have been developing a Hi-Res graphics editor as my next product. During the development cycle I was working with a line draw game paddle routine. You move a cursor to a position and anchor one end of the line to a point. Then you can move to another position and while you move a line stretches out from the point like a rubber band to the current cursor position. This gives you a preview of what the line looks like before you plot the line. The 'HXPLOT' function does have one sleight problem: it plots independent of the current color.

What the function actually does as it draws a line is to invert each dot of the line path instead of plotting a color. When the same line is drawn with the same coordinates the bits on the line path are inverted again back to their original value, restoring the screen to what it was before you started HXPLOTting.

You may be wondering why not just use the 'HPLOT' as it is to do this. You could just draw the line once with a color of 3 then change the color to 0 and erase the line with another 'HPLOT'. This only works if you have a black screen with no other images on it. If their are other images on the screen then when you erase the line you will draw a black line through those other images causing them to change. Only a function like 'XDRAW' or the 'HXPLOT' will be non-destructive of the background data on the screen.

How It Works

The 'HPLOT' command in Applesoft is actually two commands in one.

         HPLOT x, y             plots 1 point
         HPLOT x1,y1 TO x2,y2   plots a line

Each of the routines have one common place where they plot a bit onto the hi-res screen. The point plotting routine is at $F457 in the ROM and the line routine is at $F53A in the ROM. By putting Applesoft into the RAM card we can patch into these routines and modify their operation.

The two areas that are patched are at $F457 and $F58D. After you run the patch program you should see the Applesoft prompt character and there will be no program in memory. So type in the small demo program listed here and run it.

         10 HGR2: POKE 769,1
         15 POKE 28,127: CALL 62454
         20 FOR I=0 TO 279 STEP 10: FOR J=0 TO 192 STEP 10
         25 HPLOT 140,96 TO I,J
         30 FOR Z=1 TO 1: NEXT Z
         40 HPLOT 140,96 TO I,J
         50 NEXT J: NEXT I
         100 GOTO 10

If you have an Integer BASIC motherboard you should boot up your system master disk and have Applesoft loaded into your RAM card before using the routine.

     1000  *--------------------------------
     1010  * THIS ROUTINE ADDS AN XPLOT CAPABILITY
     1020  * TO APPLESOFT.  THE FLAG AT $301 (769)
     1030  * CONTROLS WHETHER HPLOT OR XPLOT IS
     1040  * FUNCTIONING.
     1050  *
     1060  *      POKE 769,0   ENABLES HPLOT
     1070  *      POKE 769,1   ENABLES XPLOT
     1080  *--------------------------------
     1090         .OR $300
     1100         .TF B.HXPLOT
     1110  NEW.HLIN LDA #0     TEST 'XPLOT' FLAG
     1120         BNE .2       YES 'XPLOT' MODE
     1130         LDA ($26),Y  PLOT NORMAL LINE
     1140         EOR $1C
     1150         AND $30
     1160  .1     JMP $F593    BACK INTO APPLESOFT LINE ROUTINE
     1170  .2     LDA #$7F     MASK COLOR SHIFT BIT
     1180         AND $30      OFF OF BIT MASK
     1190         AND ($26),Y  TEST SCREEN BIT
     1200         BNE .1       BIT IS SET!... SO CLEAR IT
     1210         LDA #$7F     BIT IS CLEAR!...SO SET IT
     1220         AND $30      BIT MASK WITHOUT COLOR SHIFT BIT
     1230         BPL .1       BRANCH ALWAYS
     1240  *--------------------------------
     1250  NEW.PLOT JSR $F411  CALL HPOSN ROUTINE
     1260         LDA $301     TEST 'XPLOT' FLAG
     1270         BNE .1       YES 'XPLOT' MODE
     1280         JMP $F45A    PLOT NORMAL
     1290  .1     LDA #$7F     XPLOT
     1300         AND $30      MASK COLOR SHIFT BIT OFF
     1310         AND ($26),Y  TEST SCREEN BIT
     1320         BNE .2       SCREEN BIT IS SET
     1330         LDA #$7F     ...CLEAR SO PREPARE TO
     1340         AND $30      SET SCREEN BIT
     1350  .2     JMP $F460    BACK INTO APPLESOFT XPLOT ROUTINE
     1360  *--------------------------------
     1370  *
     1380  * TO USE THE ABOVE FUNCTION YOU MUST HAVE A RAM CARD.
     1390  * APPLESOFT MUST BE IN THE RAM CARD.
     1400  * THEN YOU MUST DO THE FOLLOWING:
     1410  *
     1420  * 0. BLOAD B.XPLOT.FOR.FP     LOAD THE XPLOT ROUTINE
     1430  * 1. CALL-151  TO ENTER THE MONITOR
     1440  * 2. C081 C081   TO WRITE ENABLE THE CARD
     1450  * 3. GO TO STEP 5 IF YOU HAVE AN INTEGER BASIC MOTHER BOARD
     1460  * 4. D000<D000.FFFFM   PUT APPLESOFT INTO RAM CARD
     1470  * 5. F58D:4C 00 03   PATCH FOR LINE ROUTINE
     1480  * 6. F457:4C 1B 03   PATCH FOR POINT PLOT ROUTINE
     1490  * 7. C080            WRITE PROTECT THE RAM CARD
     1500  * 8. 3D3G            START APPLESOFT UP
     1510  *--------------------------------
     1520  * FOR LAZY SOULS HERE IS AN AUTOMATIC PATCH ROUTINE.
     1530  *--------------------------------
     1540  MON.COUT .EQ $FDED   MONITOR CHARACTER OUT ROUTINE
     1550         .OR $4000
     1560         .TF B.PATCH.XPLOT
     1570  START  LDY #0
     1580  .1     LDA MESG,Y
     1590         BEQ L.100
     1600         JSR MON.COUT PRINT MESSAGE
     1610         INY
     1620         BNE .1       BRANCH ALWAYS
     1630  MESG   .HS 8D84
     1640         .AS -/BLOAD B.XPLOT.FOR.FP/
     1650         .HS 8D00
     1660  L.100  LDA $C081    ROM READ
     1670         LDA $C081    RAM CARD WRITE
     1680         LDA $E000    CHECK MOTHERBOARD ROM
     1690         CMP #$20     IS IT INTEGER BASIC
     1700         BEQ L.200    YES SO MUST HAVE FP FROM SYSTEM MASTER
     1710         LDA #$D0     NO SO COPY FP FROM ROM TO RAM CARD
     1720         STA $1
     1730         LDA #0
     1740         STA $0
     1750  .1     LDY #0
     1760  .2     LDA ($0),Y
     1770         STA ($0),Y
     1780         INY
     1790         BNE .2
     1800         INC $1
     1810         BNE .1
     1820  L.200  LDA #$4C     SET PATCHES INTO RAM CARD APPLESOFT
     1830         STA $F58D
     1840         STA $F457
     1850         LDA #NEW.HLIN
     1860         STA $F58E
     1870         LDA /NEW.HLIN
     1880         STA $F58F
     1890         LDA #NEW.PLOT
     1900         STA $F458
     1910         LDA /NEW.PLOT
     1920         STA $F459
     1930         LDA $C080
     1940         JMP $3D3     START UP RAM CARD APPLESOFT

Bubble Sort Demonstration Program | Bob Sander-Cederlof

The following program implements one of the most inefficient methods of sorting a list of items ever invented. It is also a very specific implementation, not general at all. But it should be valuable to study if you are not already well-versed in sorting techniques. After execution, the bytes from $00 to $0F will be in ascending order.

     1000  *--------------------------------
     1010  *      BUBBLE-SORT DEMO
     1020  *--------------------------------
     1030  LIST   .EQ $00 THRU $0F
     1040  N      .EQ 16
     1050  FLAG   .EQ $10
     1060  *--------------------------------
     1070  BUBBLE LDY #0       INITIAL INDEX
     1080         STY FLAG     INTERCHANGE FLAG
     1090  .1     LDA LIST+1,Y COMPARE TWO ADJACENT ITEMS
     1100         CMP LIST,Y
     1110         BCS .2       ALREADY IN CORRECT ORDER
     1120         PHA          INTERCHANGE THEM
     1130         LDA LIST,Y
     1140         STA LIST+1,Y
     1150         PLA
     1160         STA LIST,Y
     1170         LDA #$FF     SET INTERCHANGE FLAG
     1180         STA FLAG
     1190  .2     INY          NEXT OVERLAPPING PAIR
     1200         CPY #N-1
     1210         BCC .1       STILL A PAIR LEFT
     1220         LDA FLAG     WAS AN INTERCHANGE PERFORMED?
     1230         BNE BUBBLE   YES, MAKE ANOTHER PASS
     1240         RTS          NO, ALL SORTED

DOS File Exchange: A Review | Bill Morgan

I've just been playing with a new program called DOS File Exchange (DFX), and it is wonderful. Author Graeme Scott has provided a very useful tool for transferring any type of files through a modem, with full error-checking. You can even chat at the keyboards while the transfer is going on!

The DFX program must be running on both computers, and one of them must be using an original (primary) disk of the program. The program can be copied to produce a secondary disk; DFX will even send a copy of itself to a remote Apple, but the copy will be a secondary.

To transfer files, one user selects a "master" mode, so he will control both Apples. He then chooses whether he will send or receive; the program then transmits the sending Apple's disk catalog to the receiver. The master user selects the files wanted from the catalog and starts the transfer. Both users are then free to chat, supervise the transfer in one of three display modes, or even leave the room.

At almost any time, you can switch back and forth between Function and Chat modes. Function is used to select all control and menu choices; Chat sends all characters entered to the other Apple.

There are three display modes, called M(enu), U(tility), and G(raphic). Menu shows choices, including the disk catalog when files are being chosen. Utility displays the transmitted and received data streams, and allows more space for chatting. Graphic displays the data being transferred on the Hi-res screen, so if you are receiving a picture you can watch it take shape.

The only drawbacks I've found are that DFX will only operate with a Hayes Micromodem II in slot 2 and the disk in slot 6, drive 1.

DFX is available from Arrow Micro Software, 11 Kingsford, Kanata Ont., K2K 1T5 Canada.

Macro Hint | Bob and Bill

For an easy semi-automatic SAVE, we use the following line in every program:

         1000 *HHHHHHSAVE filename

The six H's are control H's (backspaces), entered by holding the CTRL key down and typing OHOHOHOHOHOH. (Control-O allows a following control character to be entered into a line.) To save the source file, just type LIST 1000, esc-I, and copy over the line. Make it a point to always have the SAVE in line 1000; it's much easier to remember.

Yes/No Subroutine | Bob Sander-Cederlof

It happens all the time! I am continually needing to ask Yes/No questions in my programs. I do it now with the following subroutine, which has been somewhat stripped down for publication.

Assume you have just printed the question itself on the screen, preferably with " (Y/N)?" on the end. Then call my subroutine with "JSR YES.NO". The subroutine will clear the keyboard strobe, so that it is sure it is getting the answer to this question, and not just a stray character you accidentally typed. Then as soon as you hit any key, it will put it on the screen where the question ended and return to you.

At the point you should use BNE to branch where you want to if the user has typed something other than "Y" or "N". Once that is out of the way, use BCC or BCS to branch on whether it was "Y" or "N". The subroutine sets carry for "N" and clears carry for "Y".

In my actual programs, I have one more line between 1120 and 1130. It is JSR MESSAGE.PRINTER, which expects a message number in the Y-register. You can use it either way. You might also like to insert two more lines to call the message printer to print " (Y/N)? " for every question; that way the common string does not have to be repeatedly stored in memory with every question.

     1000  *--------------------------------
     1010  *      YES/NO SUBROUTINE
     1020  *
     1030  *      RETURN .NE. IF NEITHER "Y" NOR "N"
     1040  *             .EQ. AND .CC. IF "Y"
     1050  *             .EQ. AND .CS. IF "N"
     1060  *--------------------------------
     1070  STROBE     .EQ $C010
     1080  MON.RDKEY  .EQ $FD0C
     1090  MON.CH     .EQ $24
     1100  MON.BASE   .EQ $28 AND $29
     1110  *--------------------------------
     1120  YES.NO
     1130         STA STROBE
     1140         JSR MON.RDKEY
     1150         LDY MON.CH
     1160         CMP #'N+$80
     1170         BEQ .1
     1180         CMP #'Y+$80
     1190         BNE .2
     1200         CLC
     1210  .1     STA (MON.BASE),Y
     1220  .2     RTS

My Own Little Bell | Bob Sander-Cederlof

The other day I was working on my Apple at home, and the kids were trying to sleep in the same room. The program I was working on needed to indicate erroneous input by a bell, and I had to test it. Suddenly I realized how loud and sharp the Apple bell is!

With all that motivation, I threw together this little routine which makes a soft and pleasant tone to use for my own little bell. It generates fifty repetitions of a triple-toggle pattern, with time intervals selected for their harmonious character.

Lines 1070, 1170, and 1180 establish a loop equivalent to the Applesoft code:

         FOR X = 50 TO 1 STEP -1: . . . : NEXT

In assembly language it frequently occurs that backwards running loop counts are easier to use than forward ones, and this is just such a case.

Examine lines 1080-1160, and you will see a pattern repeated three times. In each case I load A with a value, call MON.DELAY, and toggle the speaker. The value passed to MON.DELAY is first 14, then 10, and then 6. MON.DELAY is a subroutine in the Apple Monitor ROM which delays an amount of time depending on what value you pass in the A-register, according to the following formula:

         # cycles delay = 2.5*N*N + 13.5*N +13

This includes the six cycles of the JSR used to call the subroutine. Each cycle is...well, the Apple clock is roughly 1.023 MHz...so a cycle is about .9775 microseconds long. The counts of 14, 10, and 6 give intervals between toggles of 630.5, 204, and 195 (including the overhead instructions in SC.BELL).

You can play with the values, and try creating your own variations. You might try adding a fourth toggle per loop, changing the number of loops, changing the delay counts, and so on. Have fun!

     1000  *---------------------------------
     1010  *      MY OWN LITTLE BELL
     1020  *--------------------------------
     1030  MON.DELAY  .EQ $FCA8
     1040  SPEAKER    .EQ $C030
     1050  *--------------------------------
     1060  SC.BELL
     1070         LDX #50
     1080  .1     LDA #14
     1090         JSR MON.DELAY
     1100         LDA SPEAKER
     1110         LDA #10
     1120         JSR MON.DELAY
     1130         LDA SPEAKER
     1140         LDA #6
     1150         JSR MON.DELAY
     1160         LDA SPEAKER
     1170         DEX
     1180         BNE .1
     1190         RTS

Using the Shift-Key Mod | Bob Sander-Cederlof

Have you heard of the "Shift-Key Mod"? By running a wire from the game connector to the right spot on the keyboard circuit, you can use software to tell whether or not the shift key is pressed. You can make your Apple keyboard almost normal!

Some word processors come with a convenient device which has a clip on one end of a wire, and a DIP socket-plug on the other. (I sell such a device for $15 without any software.) Apples with the piggy-back board below the keyboard can use the clip. If you don't have that kind of Apple, you need to solder a small wire to the bottom of either shift key, and clip onto that wire. Of course, you can run the wire all the way to the game connector and avoid the extra expense...I did it that way on my first Apple.

But what about software? All the mod does is bring the shift key into the game connector as PB2. You can read it with LDA $C063. If the value read is $00-7F, the shift key is being pressed; if $80-FF, the shift key is not pressed. You have to write a special keyboard input subroutine to convert letters to lower case ASCII codes if the shift key is not down.

Here is just such a subroutine! It is the one I use in my word processor (a product still being developed). Another routine sets up a cursor on the screen, and then calls READ.KEY.WITH.CASE to get the next keypress.

Lines 1140-1160 read the keyboard, and keep reading until you press a key other than the shift key. Once you press a key, the value at KEYBRD will be a code between $80 and $DF; the value is considered negative by the 6502, so execution continues at line 1170.

Lines 1170-1200 are an optional keyclick routine. In my word processor, a control-P turns the keyclicking on and off. I discovered that a very short "bell" sounds like a clicking keyboard, so that is what I use. The monitor bell subroutine toggles the speaker 192 times at about a 1000 Hertz rate to make a beep; I do it 10 times to make a click.

Lines 1210-1220 pick up the keypress code again and clear the keyboard strobe. This sets up the keyboard electronics so that you can read the next keypress next time around.

Lines 1230-1240 test the shift key. If it is down, the BPL will branch to the upper case section at line 1320. If the shift key is not down, lines 1270-1280 test whether the character is a letter. If so, line 1290 makes it into a lower-case code.

I am using the codes from $E0 through $FF for lower-case. This is standard ASCII, and is also compatible with the various lower-case display adapters available on the Apple. $E1 through $FA are the letters a-z; $E0 is a tick-mark; $FB-FF are special punctuation marks. If you don't have a lower-case display adapter, these codes display as punctuation and numbers.

Lines 1320-1420 handle characters typed with the shift key down. If the code is less than $C0, the keyboard input code is correct already. Above $C0, the code is correct unless you have typed M, N, or P. The Apple translates these shifted letters into @, ], and ^, respectively. My logic translates them back into capital letters.

I use a special control sequence to enter the punctuation characters with codes above $C0, which is not shown here. You type control-O, which stands for "override", and then one of the letters klmnop or KLMNOP. The letter translates into the corresponding punctuation code. For example, control-O, shift-M is a right bracket (]); control-O, shift-P is an at-sign (@).

     1000  *--------------------------------
     1010  *      READ KEY WITH CASE CONTROL
     1020  *--------------------------------
     1030  KEYBRD     .EQ $C000
     1040  KYSTRB     .EQ $C010
     1050  SPKR       .EQ $C030
     1060  SHIFT.KEY  .EQ $C063
     1070  *--------------------------------
     1080  MON.BELL2  .EQ $FBE4
     1090  *--------------------------------
     1100  KEY.CLICK.FLAG      .EQ $00
     1110  CASE.INPUT.FLAG     .EQ $01
     1120  CURRENT.CHAR        .EQ $02
     1130  *--------------------------------
     1140  READ.KEY.WITH.CASE
     1150         LDA KEYBRD   GET CHAR FROM KEYBOARD
     1160         BPL READ.KEY.WITH.CASE
     1170         LDA KEY.CLICK.FLAG   CLICKING?
     1180         BEQ .1       NO
     1190         LDY #10      YES, 10 HALF-CYCLES WILL
     1200         JSR MON.BELL2    SOUND LIKE A CLICK
     1210  .1     LDA KEYBRD   CHAR AGAIN
     1220         STA KYSTRB
     1230         BIT SHIFT.KEY    SHIFT KEY DOWN?
     1240         BPL .2       YES
     1250         BIT CASE.INPUT.FLAG
     1260         BMI .2       IN SHIFT LOCK UPPER CASE
     1270         CMP #$C0     NO, LOWER CASE IF LETTER
     1280         BCC .5       NOT A LETTER
     1290         ORA #$20     LETTER, MAKE LOWER CASE
     1300         BNE .5       ...ALWAYS
     1310  *---SHIFT KEY PRESSED------------
     1320  .2     CMP #$C0     SEE IF LETTER
     1330         BCC .5       NOT A LETTER KEY
     1340         BEQ .4       SHIFT-P
     1350         CMP #$DD     SHIFT-M
     1360         BEQ .3       YES
     1370         CMP #$DE     SHIFT-N
     1380         BNE .5       NO
     1390  .3     AND #$EF     MAKE CAPITAL-M OR -N
     1400         BNE .5       ...ALWAYS
     1410  *--------------------------------
     1420  .4     LDA #$D0     MAKE CAPITAL-P
     1430  .5     STA CURRENT.CHAR
     1440         RTS

Search for Page-Zero References | Bob Sander-Cederlof

Many times I have wanted a utility which would list out all references to page-zero locations withing a program. For example, when I am trying to avoid conflicts with DOS or Applesoft, I need to know which ones they use and where.

The following little program hooks into the Apple Monitor through the control-Y user command. You type in the address range you want to search through, control-Y, and a carriage return. The Apple will disassemble only those instructions within the address range which reference page-zero locations.

Lines 1220-1280 set up the control-Y vector. When the monitor detects a control-Y command, it branches to $3F8. The JMP instruction there in turn branches to CTRL.Y at line 1320.

Line 1330 loads the first address of the range into PCL and PCH. If you did not type any range before the control-Y, the previous value will be used.

Lines 1340-1540 decide whether the instruction starting at the address in PCL,PCH references page-zero or not. All instructions which reference page-zero have opcodes of the form x1, x4, x5, or x6. All of the x1, x5, and x6 possiblities are valid; only 24, 84-C4, and E4 in the x4 column are valid.

Lines 1580 and 1590 call on a piece of the monitor L-command to disassemble the one instruction. This also updates PCL,PCH to point to the next opcode byte.

Lines 1600-1700 allow you to stop/start the listing by typing any key, to single-step the listing by pressing any two keys simultaneously, and to abort by typing RETURN.

Lines 1740-1780 are executed if the instruction does not reference page-zero. The call on pieces of the L-command to figure out the number of bytes in the instruction and update PCL,PCH accordingly.

Lines 1820-1870 check to see if the range you specified has been covered yet. If not, keep searching; if so, stop.

This kind of program should be in your tool-kit when you are debugging. Just don't lose it under all those other tools!

     1000  *SAVE S.LOOK FOR PAGE ZERO
     1010  *--------------------------------
     1020  *      SEARCH FOR PAGE ZERO REFERENCES
     1030  *--------------------------------
     1040  MON.A1L    .EQ $3C
     1050  MON.A1H    .EQ $3D
     1060  MON.A2L    .EQ $3E
     1070  MON.A2H    .EQ $3F
     1080  MON.PCL    .EQ $3A
     1090  MON.PCH    .EQ $3B
     1100  *--------------------------------
     1110  KEYBOARD   .EQ $C000
     1120  STROBE     .EQ $C010
     1130  *--------------------------------
     1140  MON.LIST2  .EQ $FE63
     1150  MON.INSDS  .EQ $F88C
     1160  MON.A1PC   .EQ $FE75
     1170  MON.PCADJ  .EQ $F953
     1180  MON.NXTA1  .EQ $FCBA
     1190  *--------------------------------
     1200  *      SET UP CONTROL-Y VECTOR
     1210  *--------------------------------
     1220  SETUPY LDA #$4C     'JMP' OPCODE
     1230         STA $3F8
     1240         LDA #CTRL.Y
     1250         STA $3F9
     1260         LDA /CTRL.Y
     1270         STA $3FA
     1280         RTS
     1290  *--------------------------------
     1300  *      CONTROL-Y COMES HERE
     1310  *--------------------------------
     1320  CTRL.Y
     1330         JSR MON.A1PC IF ADDRESS SPECIFIED, PUT IN PC
     1340  .1     LDY #0
     1350         LDA (MON.PCL),Y
     1360         AND #$0F
     1370         CMP #1
     1380         BEQ .3
     1390         CMP #4
     1400         BCC .6
     1410         BNE .2
     1420         LDA (MON.PCL),Y
     1430         AND #$F0
     1440         CMP #$20     BIT Z
     1450         BEQ .3
     1460         CMP #$80
     1470         BCC .6       NO
     1480         CMP #$D0
     1490         BEQ .6       NO
     1500         CMP #$F0
     1510         BEQ .6       NO
     1520         BNE .3       YES
     1530  .2     CMP #7
     1540         BCS .6
     1550  *--------------------------------
     1560  *      INSTRUCTION REFERENCES PAGE-ZERO
     1570  *--------------------------------
     1580  .3     LDA #1       DISASSEMBLE THIS ONE INSTRUCTION
     1590         JSR MON.LIST2   DISASSEMBLE
     1600         LDA KEYBOARD    SEE IF KEYPRESS
     1610         BPL .7       NO
     1620         STA STROBE   YES, CLEAR IT
     1630         CMP #$8D
     1640         BEQ .5
     1650  .4     LDA KEYBOARD
     1660         BPL .4
     1670         STA STROBE
     1680         CMP #$8D
     1690         BNE .7
     1700  .5     RTS
     1710  *--------------------------------
     1720  *      DOES NOT REFERENCE PAGE-ZERO
     1730  *--------------------------------
     1740  .6     LDX #0
     1750         JSR MON.INSDS    GET LENGTH OF INSTRUCTION
     1760         JSR MON.PCADJ
     1770         STA MON.PCL
     1780         STY MON.PCH
     1790  *--------------------------------
     1800  *      TEST IF FINISHED
     1810  *--------------------------------
     1820  .7     LDA MON.PCL
     1830         CMP MON.A2L
     1840         LDA MON.PCH
     1850         SBC MON.A2H
     1860         BCC .1
     1870         RTS

Automatic CATALOG for S-C Macro Assembler | Bill Morgan

Being a thoroughly lazy (and fumblefingered) typist, I have been itching for an automatic CATALOG command to go with the automatic LOAD in the S-C Macro Assembler. Well I finally have it; now loading a file is just esc-C, esc-I...IL. I chose esc-C for CATALOG because I never use the esc-ABCD cursor moves. If you do like those, esc-G and -H are available; right now they are like NOP's.

The Macro Assembler takes the character following an escape (@, A, B,..., L, M) and makes it an index into a jump table located from $1467-1482. Esc-C is at $146D in the table, esc-G is $1475, and esc-H is $1477.

The patch is only $28 bytes long, short enough to easily fit in page 3, but I decided to go ahead and create a spare page for patches by moving the symbol table up one page. This technique is mentioned on page 5-3 of the Macro Assembler manual.

To install the patch, first move the symbol table base up by changing location $101D from $32 to $33. Now insert the address of the patch into the jump table by changing locations $146D-6E from $65 FC to $FF 31 (or your location-1). Type "BLOAD PATCH", then "BSAVE ASM MACRO.MOD,A$1000,L$22FF", and there you have it.

     1000  *--------------------------------
     1010         .OR $3200
     1020         .TF PATCH
     1030  *--------------------------------
     1040  CH     .EQ $24
     1050  BASL   .EQ $28
     1060  XSAVE  .EQ $40
     1070  WBUF   .EQ $200
     1080  *--------------------------------
     1090  ESCAPE.C
     1100         CPX #0       BEGINNING OF LINE?
     1110         BNE .2       NO, RETURN
     1120         LDY #1
     1130  .1     LDA MSG-1,Y  GET CHARACTER
     1140         STA (BASL),Y PUT ON SCREEN
     1150         STA WBUF,X   PUT IN BUFFER
     1160         INY
     1170         INX
     1180         CPY #8       DONE?
     1190         BNE .1       NO
     1200         STY CH
     1210         STX XSAVE    TELL THE ASSEMBLER
     1220         TSX          THAT THIS WAS AN
     1230         LDA #$CC     ESCAPE-L, SO IT WILL
     1240         STA $103,X   GO AHEAD AND EXECUTE
     1250         LDX XSAVE    THE COMMAND
     1260  .2     RTS
     1270  *--------------------------------
     1280  MSG    .AS -/CATALOG/

Examiner | Bill Morgan

Here is the program I like to use to examine memory; it displays an entire page on the screen in both hex and ASCII formats. This makes the screen kind of crowded, but I particularly wanted a full page at a time. A program like this is useful for inspecting the results of last month's TRACK READ program, studying the internal format of an Applesoft program, or just exploring inside your Apple.

Examiner uses the left and right arrow keys to decrement or increment the page being displayed. You can also type "P" to allow entry of a page number in hex. Notice that the number entered is rolled into the page number from the right. Escape exits the program.

Lines 1180-1260 set things up to start with page zero.

Lines 1280-1390 display the index, then twelve bytes in hex format.

Lines 1410-1460 reset the indices to display the same twelve bytes in ASCII.

Lines 1480-1630 do the ASCII display, changing any inverse or flashing values to normal and substituting periods for control characters.

Lines 1700-1870 process the commands to change the page being displayed.

Lines 1890-2160 accept characters "0" through "F" and convert them into hex values, rolling the values into the page number to be displayed.

Lines 2180-2260 display the header "page=".

This is threatening to turn into a monthly column; what do you readers think of that idea? Are these routines too trivial? Too complicated? Do you have any questions about them? About anything fairly basic? Drop me a line here at AAL and let me know what you think. I'll look forward to hearing from you.

     1000  *--------------------------------
     1010         .OR $300
     1020         .TF EXAMINER
     1030  *--------------------------------
     1040  POINT  .EQ $00,01
     1050  PAGE   .EQ $01
     1060  CH     .EQ $24
     1070  *
     1080  KEYBOARD .EQ $C000
     1090  STROBE   .EQ $C010
     1100  *
     1110  PRBL2  .EQ $F94A
     1120  HOME   .EQ $FC58
     1130  RDKEY  .EQ $FD0C
     1140  CROUT  .EQ $FD8E
     1150  PRBYTE .EQ $FDDA
     1160  COUT   .EQ $FDED
     1170  *--------------------------------
     1180  START  LDA #$00
     1190         STA POINT    START WITH
     1200         STA PAGE     PAGE ZERO
     1210  *--------------------------------
     1220  DISPLAY.NEW.PAGE
     1230         JSR HOME
     1240         JSR PRINT.HEADER
     1250         JSR CROUT
     1260         LDY #$00
     1270  *
     1280  NEW.LINE
     1290         LDX #$0C     TWELVE BYTES AT A TIME
     1300         TYA
     1310         JSR PRBYTE   PRINT INDEX
     1320         LDA #$A0
     1330         JSR COUT     SPACE
     1340  .1     LDA (POINT),Y
     1350         JSR PRBYTE   PRINT HEX
     1360         INY
     1370         BEQ FILLIN   PAGE DONE?
     1380         DEX
     1390         BNE .1       TWELVE YET?
     1400  *
     1410  ADJUST TYA
     1420         SBC #$0C     RESET Y
     1430         TAY
     1440         LDA #$A0
     1450         JSR COUT     SPACE
     1460         LDX #$0C     TWELVE AGAIN
     1470  *
     1480  ASCII  LDA (POINT),Y
     1490         CMP #$40     INVERSE?
     1500         BCS .1       NO
     1510         ORA #$C0     NORMALIZE
     1520  .1     CMP #$80     FLASHING?
     1530         BCS .2       NO
     1540         ORA #$80     NORMALIZE
     1550  .2     CMP #$A0     CONTROL?
     1560         BCS .3       NO
     1570         LDA #$AE     PUT PERIOD
     1580  .3     JSR COUT     SEND IT
     1590         INY
     1600         BEQ GET.COMMAND   PAGE DONE?
     1610         DEX
     1620         BNE ASCII    LINE DONE?
     1630         BEQ NEW.LINE
     1640  *
     1650  FILLIN LDX #$10     FILL LAST PARTIAL
     1660         JSR PRBL2    LINE WITH SPACES
     1670         LDY #$08     ADJUST Y
     1680         BNE ADJUST
     1690  *--------------------------------
     1700  GET.COMMAND
     1710         JSR CROUT
     1720  .1     JSR RDKEY
     1730         CMP #$9B     ESCAPE?
     1740         BEQ .2
     1750         CMP #$95     RIGHT ARROW?
     1760         BEQ .3
     1770         CMP #$88     LEFT ARROW?
     1780         BEQ .4
     1790         CMP #$D0     "P"?
     1800         BEQ GET.PAGE.NUMBER
     1810         BNE .1       NONE OF THE ABOVE
     1820  .2     RTS
     1830  *
     1840  .3     INC PAGE
     1850         JMP DISPLAY.NEW.PAGE
     1860  .4     DEC PAGE
     1870         JMP DISPLAY.NEW.PAGE
     1880  *--------------------------------
     1890  GET.PAGE.NUMBER
     1900         JSR PRINT.HEADER
     1910  .1     DEC CH       SO PRBYTE WILL ALWAYS
     1920         DEC CH       DISPLAY IN SAME PLACE
     1930  .2     LDA KEYBOARD
     1940         BPL .2
     1950         STA STROBE
     1960         CMP #$8D     RETURN?
     1970         BEQ .5       YES, EXIT
     1980         EOR #$B0
     1990         CMP #$A      0-9?
     2000         BCC .3       YES
     2010         ADC #$88
     2020         CMP #$FA     A-F?
     2030         BCC .2       NO
     2040  *
     2050  .3     LDY #$3      LOOP 4 TIMES
     2060         ASL          THROW AWAY HIGH NYBBLE
     2070         ASL
     2080         ASL
     2090         ASL
     2100  .4     ASL          SHIFT INTO
     2110         ROL PAGE     PAGE NUMBER
     2120         DEY
     2130         BPL .4
     2140         LDA PAGE
     2150         JSR PRBYTE   DISPLAY PAGE NUMBER
     2160         JMP .1       GET NEXT KEYPRESS
     2170  .5     JMP DISPLAY.NEW.PAGE
     2180  *--------------------------------
     2190  PRINT.HEADER
     2200         LDY #$00
     2210  .1     LDA QPAGE,Y
     2220         JSR COUT
     2230         INY
     2240         CPY #$05
     2250         BNE .1
     2260         LDA PAGE
     2270         JMP PRBYTE
     2280  *
     2290  QPAGE  .AS -/PAGE=/

Apple Assembly Line is published monthly by S-C SOFTWARE, P. O. Box 280300, Dallas, TX 75228. Phone (214) 324-2050 Subscription rate is $15 per year, in the USA, sent Second Class Mail; $18 per year sent First Class Mail in USA, Canada, and Mexico; $28 per year sent Air Mail to 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.)