!lm10
!rm76
Extending the Apple Monitor

Just as the creators of Applesoft included the wonderful "&" statement to allow language extensions, so also Steve Wozniak included a means for adding new monitor commands.  The "control-Y" command branches to a user-defined maching language routine, which can supplement the existing commands in the Monitor ROM.

The control-Y command executes your subroutine starting at $3F8.  All there is room for at $3F8 is a JMP to where your subroutine is REALLY stored.  When you boot DOS, a JMP $FF65 instruction is inserted at $3F8, setting the control-Y command to merely re-enter the monitor.  By changing the address of that JMP instruction, you can have it jump to your own code.  If you look ahead at the listing of MONITOR EXTENSIONS, lines 1170-1210 store the address of my CTRLY subroutine into the JMP instruction.

I have thought of at least three features that I miss all the time in the monitor.  (I just now thought of several more, but they will have to wait for another article.)
!lm15
!rm71

1.  The monitor already includes the ability to add and subtract single-byte values, and print the single-byte result.  I would like to be able to do this with 16-bit values.

2.  The monitor can already dump memory in hexadecimal, but I want to see it as ASCII characters also.  There is room on the screen for both at once.

3.  The monitor can already disassemble code to the screen, 20 lines at a time.  If I want more than 20 lines, I can type "LLLLLL", one L for each 20 lines.  But I would like to be able to just specify the beginning and ending addresses for the disassembly, like I do for the hexadecimal printout.
!lm10
!rm76

If you enter the MONITOR EXTENSIONS program, these three functions will be added to the monitor.  To add or subtract two values, type the two values separated by "+" or "-"; then type control-Y, and carriage return.  To dump in combined hex and ASCII, type the beginning and ending addresses separated by a period, then control-Y and carriage return.  To disassemble a range of memory, type the beginning and ending addresses separated by a period, then control-Y, "L", and a carriage return.

Looking again at the listing, lines 1230-1340 figure out which of the above command options you have typed in.  When the monitor branches to $3F8, the following conditions have been set up:
!lm15

(A) = 0 if only one address was typed;
    = code for separator character if two addresses
      were typed.
!np
(X) = 0 if no hex digit typed immediately before the
        control-Y;
    = 1 if any hex digits immediately before the
        control-Y.

(Y) = 0

($34) = index into input buffer of next character after
        the control-Y.
!rm71

Up to five 16-bit variables (called A1, A2, A3, A4, and A5) are filled from the hexadecimal values in the command.  If you type a "<" after the first value, then that value will be stored in A4 and A5 (A4 is at $42,43; A5 at $44,45).  If you type a ".", "+", "-", or ":" after a hexadecimal value, then that value will be stored in A1 and A3 (A1 is at $3C,3D; A3 at $40,41).  If you type a hexadecimal value immediately before the control-Y, then that value will be stored in A2 (which is at $3E,3F).
!lm10
!rm76

Looking again at lines 1230-1340, I branch to SUB if the separator is "-", or ADD if it is "+".  If the separator is a colon, I just return; I don't have any control-Y command which accepts a colon separator.  If the separator is not any of the above, then either there was no separator, or it was a period.  In both of these cases, I want to dump memory.  If the character after the control-Y is not "L", then I want a combined hex-ASCII dump; if it is "L", I want disassembly.  Line 1340 increments the buffer pointer so that the "L" command will not be re-executed by the regular monitor routine after my control-Y routine is finished.

Lines 1360-1450 control the disassembly option.  I used a monitor subroutine to copy the beginning address from A1 into PC.  Then I wrote a loop that calles the monitor routine to disassemble one line, and then checks to see if we have reached the ending address.  Compare this to the code in the monitor ROM at $FE5E through $FE74.  There is one trick in this code.  I wanted to compare PC to END.ADDR, and continue if PC was less than or equal to END.ADDR.   The normal comparison technique would either SET carry at line 1390, but I CLEARed it.  This has the same affect as using one less than the value in PC as the first comparand.  I needed this, because BCC at line 1440 only branches if the first comparand is LESS THAN the second one.  In other words, since it is difficult to implement IF PC <= END.ADDR THEN ..., I implemented IF PC-1 < END.ADDR THEN ....

Lines 1470-1780 perform the combined hex-ASCII dump.  I must give credit to Hugh McKinney, of Dunwoody, GA, for some of the ideas in this code.  Just for fun, I set it up to always print complete rows of eight bytes; the starting address is rounded down to the nearest multiple of 8, and the ending address is rounded up.  This means that typing just one address will get you eight, also.

I had to make a judgment about what characters to display for the ASCII portion of the dump.  There are 256 possible values, and only 96 printing characters.  In fact, if you don't have a lower case adapter, your screen only shows 64 printing characters (unless you count inverse and flashing characters as different; in that case you have 192).  I decided to display control characters (codes 00-1F and 80-9F) as flashing characters (codes 40-5F).  Codes 60-7F and E0-FF display as lower case characters if you have a lower case adapter.  Codes 20-5F and A0-DF display as normal video characters (the standard upper case set).  If you want a different mapping, change lines 1660-1690 to do it your way.

Lines 1800-1930 perform the 16-bit addition and subtraction in the normal way.  Lines 1940-1980 print out an equal sign, and the value.

If you get really ambitious, you might try programming for your Apple II Plus the S and T commands that Apple removed from the Autostart ROM.  You can just about copy the code right out of the reference manual.  You might also like to add a memory move command that will work correctly even when the target area overlaps the source area.
