In this page...


Index

$B000-$BFFF - Monitor Jump Table

The duplicate use of the word monitor in computer terminology may be confusing at first. The term can refer to either hardware, a dedicated video screen used to display information from the computer, or software, a program used to examine and modify the contents of memory. Once you understand the difference, the meaning of monitor is usually obvious from the context. This chapter describes the 128’s built-in software monitor, which resides in the 4K block of ROM from 45056-49151/ $BOOO-$BFFF. In addition to examining and changing memory, this monitor allows you to assemble, disassemble, and execute ML routines; examine and change microprocessor register contents; and copy, compare, save, load, and verify blocks of memory.

Like the 128’s BASIC, its machine language monitor has a long heritage from previous Commodore models. All of the original CBM models (except for very early PETs) included a rudimentary monitor in ROM which allowed users to examine and modify memory and registers, execute ML programs, and load and save data, but had no provision for assembling or disassembling machine language.

The VIC-20 and Commodore 64 had no monitor in builtin ROM, but sophisticated monitors for both were available on cartridge. A number of public-domain RAM-resident monitors were also available, most notably Supermon and Micromon. Finally, the Plus/4 and 16 once again included a monitor program in ROM, a version called Tedmon.

The 128’s monitor shares many characteristics with all of its predecessors, but it includes a number of enhancements as well. One of the most notable is that it allows the entry of numbers in decimal, octal, or binary in addition to hexadecimal. Whenever the monitor expects a number, you can use a decimal value if it’s prefixed with a + character, or a binary number if it’s prefixed with a % character (in the rare case when you might want to use an octal-base 8-number, pre fix it with an ampersand, &). If no prefix is used, then hexadecimal is assumed. (Hex can also be explicitly specified by using a $ character as a prefix.)

Moving Between BASIC and the Monitor

In general, the monitor is aloof from the rest of the 128 ROM routines. Unlike BASIC-which in a number of places bypasses the Kernal jump table and calls Kernal routines directly-the monitor calls all the Kernal routines it uses through their formal jump table entries. Neither BASIC nor the Kernal calls any monitor routines other than through jump table entries. The monitor does not make use of any BASIC ROM routines or data tables and for the most part does not interfere with memory locations used by BASIC. Thus, you may pass freely back and forth between the monitor and BASIC without fear of upsetting the BASIC program currently in memory. This greatly enhances the monitor’s function as a debugging tool.

One notable-and highly unfortunate-exception to this independence from BASIC is that the monitor uses addresses in the range 96-104/$60-$68 as working pointers in most operations. This area includes the addresses used by BASIC for its floating-point accumulator 1 (FAC1), where the results of mathematical operations are stored. As a result, it is impossible to use the 128’s monitor to directly examine or change the contents of FACl. This severely limits the usefulness of the monitor for experimenting with BASIC floating-point routines.

One other overlap between BASIC and the monitor is that the two share the same input buffer area for accepting and processing commands (512-673/$0200-$02A1). Thus, it is not possible to use the monitor to examine the BASIC input buffer contents or to manipulate data in the input buffer, since the buffer will be at least partially overwritten by the monitor command to display or change the memory area.

Memory Management

Another particularly attractive feature of the 128’s monitor is the ease with which it interfaces with the computer’s memory management system. Addresses in monitor commands are specified as five-digit hexadecimal values, where the first digit refers to the bank and the remaining four specify the address within the bank. Monitor commands that accept a range of addresses can span banks-T E0000 E0FFF 1C000, for example. Thus, the monitor can effectively see the 16 separate banks as a single 1024K (16 * 64K) block of memory. One exception is the F (fill memory) command, which cannot cross bank boundaries, because doing so would overwrite the vital contents of locations $00 and $01.

The fact that the monitor can see the 128’s memory as a continuous block has an important consequence for the H (hunt for byte pattern) command. Remember that the banks are not really 16 separate blocks of memory, but rather 16 different arrangements of the available RAM and ROM. The lowest 1K of memory (including the input buffer at 512/$0200) is common to all banks, and at least the lower 4K of block 0 RAM (including the buffer at $0A80, where the search pattern is stored) appears in all even-numbered banks and in banks 13/$0 and 15/$F.

Thus, if you search any bank from beginning to end (for example, H 10000 1FFFF ‘C-128), you’ll always find at least one match for your search pattern-in the input buffer at $0200, where the search command is stored. If you search any even-numbered bank from beginning to end, you’ll find at least two matches for your search pattern-once in the input buffer at $0200 and again in the search buffer at $OA80. And if you search all banks from beginning to end (for example, H 00000 FFFFF ‘COMPUTE!), then you’ll always find at least 26 matches because of all the times the memory areas used by the input buffer and search buffer appear in the different banks. It’s important to choose your address range carefully when searching for a byte pattern.

A final note on banks and the monitor: If no bank is explicitly specified, bank 0 is assumed. This is different from BASIC, which retains the setting specified in a previous BANK statement, starting with a default of bank 15. So when using the monitor, you must always explicitly specify the bank if you wish to use any bank other than bank O. It is particularly important to remember this when using the G and J commands, lest you send the processor off to some uncharted region of memory.

For the programmer wishing to make use of ROM routines, those in the monitor are generally less useful than those in the BASIC, screen editor, and Kernal portions of the ROM. The monitor JMPs rather than JSRs to the routines used to perform monitor commands, so most major routines end by jumping directly back to the monitor’s main loop rather than with an RTS opcode. You probably wouldn’t want to incorporate calls to such routines in your own programs because the routines never return from the monitor. However, the main command execution routines use a number of subroutines that do end with RTS opcodes, and you may find some of these useful, particularly the routines to convert and print byte values as decimal numbers (at 47623/$BA07 and 47687/$BA47) or as hexadecimal numbers (at 47250/$B892).

Monitor Jump Table

Like the BASIC, screen editor, and Kernal jump tables, each three-byte entry in the following table consists of a JMP opcode followed by the address of an important routine.

45056 $B000 JMONINIT

Monitor cold-start entry point

Disassembled code

Jumps to $B021

Enters the monitor with default microprocessor register values. This is the entry point when the RUN/STOP key is held down during power-on/reset, or when the MONITOR command is executed in BASIC.

45059 $B003 JMONBRK

Monitor break entry point

Disassembled code

Jumps to $B009

Enters the monitor with the current program counter, bank, and microprocessor A, X, and Y register values preserved. The monitor is normally called via this entry point whenever a BRK opcode is executed because the Kernal RESTOR routine $E056, part of the RESET sequence, initializes the CBINV vector at $0316-$0317 to point here. CBINV determines where control is passed after a BRK.

45062 $B006 JIMONRTN

Reentry point from the IMON indirect vector.

Disassembled code

Like the BASIC and Kernal indirect vectors, the monitor’s command execution routine has an indirect vector, IMON ($032E-$032F), which is initialized by the Kernal RESTOR routine $E056 to point here. From this point, control is transferred back to $B0B2 in the main loop, the address immediately following the IMON jump. See IMON for information on how the indirect vector can be used to wedge in additional monitor commands.

45065 $B009 MONBRK

Monitor entry routine when BRK instruction encountered

Disassembled code

Prints BREAK and a {BELL} character, then retrieves and stores the bank number, program counter, and microprocessor register values that were placed on the stack by the IRQ/BRK handling routine $FF17, then branches into the following routine to fall through to the register display and main loop.

45089 $B021 MONINIT

Cold-start routine for monitor

Disassembled code

Switches to bank 15, loads all register storage locations with zeros, sets the program counter storage to $B000 and bank storage to 15, and prints MONITOR. Next (at $B046) the stack pointer is stored and Kernal error messages are enabled. The routine then falls through to display the stored register values and enter the main loop.

45136 $B050 SHOVVREG

Handles R (register display) command

Disassembled code

Prints a heading for the register display, then displays the contents of the storage locations (2-9/$02-$09) that represent the program counter (prefixed with the current bank number); status; and A, X, Y, and stack pointer register values. The storage locations are filled upon entry to the monitor and can be changed with the register change (;) command. To simplify the process of changing register values, this routine adds a semicolon before the displayed values so that you can change the stored values by typing over the displayed values and pressing RETURN. The routine ends by falling through into the main loop.

45195 $B08B MONMAIN

Main command execution loop for the monitor.

Disassembled code

Clears a line for input, then gets a command line into the input buffer ($0200-$02A0). The routine accepts characters until RETURN is pressed. Characters are then retrieved from the buffer until one is found that is not a space. This character is assumed to be a monitor command (all monitor commands consist of a single character). With this character in the accumulator, the routine jumps through the IMON indirect vector at $032E-$032F. Normally, this vector points to the jump table entry at $B006, which immediately returns to the address following the indirect jump. However, this vector can be changed to allow additional commands to be added to the monitor. See the discussion at IMON for more details.

The routine then compares the command character in the accumulator against characters from the command table at $B0E6. If no match is found, an error is assumed, and (at $B0BC) the error signal (a question mark) is printed following the command. The routine then loops back to process another command. If a match is found among the first 15 characters in the command table, then the command is executed by pushing an address from the table at $B0FC onto the stack, then jumping to read the parameter following the command. The RTS at the end of the parameter decoding routine $B7A7 will cause control to be passed to the command execution address stored on the stack. If the matching character is among characters 16-19 in the table ($-%), then a jump is taken to the base conversion routine $B9B1. If the matching character is among characters 20-22 in the table (L-V), then a jump is taken to the routine that prepares for load, save, or verify $B337.

45283 $B0E3 EXITMON

Handles X (exit to BASIC) command.

Disassembled code

Leaves the monitor and returns to BASIC by jumping indirectly through BASIC’s restart vector at $0A00-$0A01.

45286 $B0E6 COMTBL

Table of monitor commands.

Disassembled code

Each of the 22 commands consists of a single character: ACDFGHJMRTX@.>;$ + &%LSV

45308 $B0FC EXECTBL

Table of execution addresses for the monitor commands.

Disassembled code

Each two-byte entry in the table consists of the address minus 1 of the routine to perform the corresponding command. The entries are one less than the actual address because of the way the RTS opcode behaves: When RTS pulls a return address from the stack, it adds 1 to the address value before placing the value in the 8502’s program counter. The actual execution addresses for each of the commands handled by this table are as follows:

     
A (Assemble instruction) $B406
C (Compare memory blocks) $B231
D (Disassemble instruction) $B599
F (Fill memory) $B3DB
G (Go to routine) $B1D6
H (Hunt for byte) $B2CE
J (Jump to subroutine) $B1DF
M (Memory display) $B152
R (Register display) $B050
T (Transfer memory) $B234
X (eXit to BASIC) $B0E3
@ (send disk command) $BA90
. (same as A) $B406
> (change memory) $B1AB
; (change register) $B193

45338 $B11A MINDFET

INDFET call for the monitor.

Disassembled code

Calls the Kernal INDFET routine $FF74 to retrieve a character into the accumulator from the bank specified in $68 at the address pointed to in 102-103/$66-$67, and with the offset specified by the contents of the Y register. The use of $66-$68 as working addresses makes it impossible to use the monitor to examine the contents of floating-point accumulator 1 (FAC1), since the value in FAC1 will be changed by the monitor M command (which uses this routine).

45354 $B12A MINDSTA

INDSTA call for the monitor.

Disassembled code

Calls the Kernal INDSTA routine $FF77 to store the value in the accumulator into the bank specified in $68 at the address pointed to in 102-103/$66-$67, and with the offset specified in the Y register. The use of $66-$68 as working addresses makes it impossible to use the monitor to load values directly into floating-point accumulator 1 (FAC1), since the value in FAC1 will be changed by the> (memory change) command (which uses this routine).

45373 $B13D MINDCMP

INDCMP call for the monitor.

Disassembled code

Calls the Kernal INDCMP routine $FF7A to compare the value in the accumulator against the value at the address pointed to in 102-103/$66-$67, with the offset specified in the Y register, from the bank specified in $68. The only monitor routine that uses indirect comparison is the compare/transfer routine $B321, and that routine calls INDCMP directly, using instead 96-97/$60-$61 as the address pointer and 98/$62 for the bank value.

45394 $B152 SHOWMEM

Handles M (memory display) command.

Disassembled code

Displays the contents of a specified area of memory as hexadecimal values and ASCII characters. The routine functions by repeatedly calling the subroutine display lines of byte and character values $B1E8.

The format depends on the screen mode: 8 bytes per line in 40-column mode or 16 bytes per line in 80-column mode. No parameters are required, but either one or two parameters can be specified. If no parameters are specified, the display begins at whatever address is currently in 102-103/$66-$67 from the bank specified in $68. Before other operations are performed, the value in those locations is not predictable. After another M command, these locations will hold an address one line (8 or 16 bytes) higher than the previous ending address. Twelve lines of data will be displayed, representing either 96 bytes (40-column mode) or 192 bytes (80-column mode). If one address is specified, 12 lines are displayed starting at the specified address. If two addresses are specified, all bytes between the addresses are displayed; the NO SCROLL key may be used to pause the screen and the STOP key will halt the process. The routine always displays full lines, so a few bytes beyond the specified ending address may also be shown. It is possible to wrap from bank to bank; the next address after $FFFF in one bank is $0000 in the next higher bank. Thus, the M command treats the 16 banks like a continuous block of memory. However, the address will not wrap from $FFFFF to $00000. The routine ends by jumping back to the main loop $B08B.

45460 $B194 CHNGREG

Handles ; (change register) command.

Disassembled code

Allows you to change the contents of the bank, program counter, and register storage locations (2-9/$02-$09). Since the values in the storage locations are reloaded into the corresponding registers by the G and J commands, this allows you to change values that the various microprocessor registers will hold when ML routines are executed from the monitor. The register contents are usually changed by editing the values displayed by the R (register display) command $B050. Since that routine automatically provides the semicolon (;) in front of the values, you may not even have realized that this is a separate command. You are free to use the ; command independently of the register display, but it is somewhat less convenient.

The routine expects to read the values in order, so you must supply values for all registers with storage locations lower than the one you wish to change. This isn’t a problem if you’re editing the register display, but if you’re using the; (semicolon) command independently, you must supply values for all registers that are normally displayed to the left of the value you wish to change. For example, even if you want to change only the Y register value, you must still also supply address, status register, accumulator, and X register values (in order) before the Y register value.

45483 $B1AB CHNGMEM

Handles > (change memory) command.

Disassembled code

Allows you to change the contents of one or more memory locations - to a maximum of either 8 or 16 locations, depending on whether the 40- or 80-column screen is in use. Memory contents are usually changed by editing the lines of byte values displayed by the M (memory display) command $B152. Since that routine automatically provides the> in front of the address and values, you may not even have realized that this is a separate command. You are free to use the> command independently of the memory display; in fact, it’s more convenient when you need to change only one or two bytes. If no parameters are found following the > command, then a line of byte values is displayed beginning at the address in $66-$67 from the bank in $68. If no monitor commands involving ranges of addresses-for example, M, T, or C-have yet been performed, the value in these locations is unpredictable. Following the M command, the locations will hold an address one line (8 or 16 bytes, depending on the display width) higher than the address of the last line displayed.

Only hexadecimal byte value parameters can be interpreted; you cannot change memory by changing the ASCII characters displayed at the end of each line. The routine to display a line of memory $B1E8 is called to redisplay the changed locations. A full line (8 or 16 bytes) is always redisplayed, even if you have changed only one or two bytes. The routine ends by jumping back to the main loop $B08B.

45526 $B1D6 GOTOLOC

Handles G (go to routine) command.

Disassembled code

Loads the bank and program counter storage locations ($02-$04) with the specified values if a target address is supplied. The stack pointer is restored to the value it had upon entry to the monitor (stored in $09(0000#09)). This negates the effects of any stack operations the monitor routines may have performed. Finally, the Kernal JMPFAR routine $FF71 is called to transfer control to the address specified in $03-$04, and in the bank specified in $02, with the microprocessor registers loaded from $05-$08.

There’s normally no returning from a JMPFAR. If you want to get back to the monitor after executing an ML routine using G, then the routine at the target address must end with a BRK ($00) opcode. Execution of the BRK will return you to the monitor via the break entry point $B003.

If you use G to go to a routine that ends with an RTS, you’ll be returned to BASIC at the end of the routine. If you’d prefer to be returned to the monitor when a program terminates with RTS, use J instead of G.

45535 $B1DF JMPSUB

Handles J (jump to subroutine) command.

Disassembled code

Loads the bank and program counter storage locations ($02-$04) with the specified values if a target address is supplied. The Kernal JSRFAR routine $FF6E is called to transfer control to the address specified in $03-$04, and in the bank specified in $02, with the microprocessor registers loaded from 5-8/$05-$08. Upon return from the JSRFAR, the routine jumps back to the main loop $B08B.

To get back to the monitor cleanly after using J, you’ll need to be sure that the routine at the target address ends with an RTS opcode. Execution of a BRK opcode will also cause a return to the monitor (via the break entry point $B003), but in that case the JSRFAR return address will be left on the stack.

45544 $B1E8 SHOWLIN

Displays a line of memory as hex bytes and ASCII characters.

Disassembled code

Clears a screen line, then prints a > character to facilitate use of the memory change command $B1AB. Next, the bank and address of the first location in the current display line (in 102-104/$66-$68) are printed. A loop reads bytes from memory and prints two-digit hexadecimal numbers representing the byte values. The loop repeats for either 8 or 16 bytes, depending on the screen width (determined by checking the value in 215/$07). After this, the routine prints a colon (so that the following ASCII characters will not be counted as part of the input for the memory change command) and an {RVS} character (so that the following ASCII characters will be displayed in reverse video). Finally, a second loop is used to read the same 8 or 16 bytes again, but this time to display the equivalent ASCII character for the byte value. To prevent cursor or color control characters from being printed and upsetting the screen display, the character code for the period (.) is substituted if the byte value to be displayed is less than 32/$20 or between 128-159/$80-$9F.

45617 $B231 CMPXFR

Compares or transfers blocks of memory.

Disassembled code

Begins by loading an operation flag (147/$93) with a value that indicates which function is being performed: 0/$00 if the routine is entered at $B231 for C (compare) or 128/$80 if entered at $B234 for T (transfer). The transfer operation might more properly be called a copy, because the contents of the source block of memory are not changed unless the blocks overlap. A direction flag ($0AB3) is also used during transfers to indicate whether bytes are being copied downward in memory (flag value 0/$00) or upward (flag value 128/$80). The direction of a transfer is significant-downward moves must work from the starting address toward the ending address, while upward moves must work from end to start. Otherwise, if the source and destination ranges overlap, a single value would be rippled through the destination range.

The routine loads bytes from the source address range and, if a transfer operation is indicated, stores the value in the corresponding destination address using the Kernal INDSTA routine $FF77. Next, it compares the byte in the source range against the byte in the destination range using the Kernal INDCMP routine $FF7A. This performs the compare operation (the bytes should always be equal for a transfer operation). If the bytes do not match, the address of the mismatch is printed. Then the source and destination addresses are either incremented (for a compare or downward transfer) or decremented (for an upward transfer). This loop is repeated until all bytes in the range have been compared or transferred; then the routine jumps back to the main loop $B08B. However, the loop also includes a call to the Kernal STOP routine $FFE1, so the RUN/STOP key can be used to halt the compare or transfer.

45774 $B2CE SEARCH

Searches memory for byte pattern.

Disassembled code

Evaluates the address parameters and calculates the number of bytes to search, then fills the buffer at $OA80 with the search pattern. If the first nonspace character following the ending address parameter is the apostrophe (‘), then the following characters are copied directly from the input buffer into the search buffer, so the search will be for the actual ASCII characters. If the apostrophe is not found, the characters following the ending address are converted into byte values before being placed in the buffer.

Once the search buffer is prepared, a byte is loaded from memory and compared against the first byte in the search buffer. If the two bytes match, the next byte in memory is compared to the next byte in the buffer, and so on, until either a mismatch occurs or the end of the search buffer is reached (which indicates that the pattern has been matched). In the case of a match, the starting address of the match is printed, followed by two spaces. The testing process normally repeats until all bytes in the specified address range have been checked, but the loop includes a call to the Kernal STOP routine $FFE1, so the RUN/STOP key can be used to be used to terminate the search. The routine ends by jumping back to the main loop $B08B.

Be sure to see the warning in the introduction to this chapter about using the H command for searching wide address ranges.

45879 $B337 MONLSV

Prepares for load, save, or verify.

Disassembled code

Begins by setting default values for the device number, secondary address, bank, filename length, and filename address.

Setting these values directly (rather than using Kernal routines like SETLFS, SETNAM, and SETBANK) is an exception to the monitor’s otherwise strict use of Kernal jump table calls. If no characters follow the command, then a branch to $B3AB allows the L and V commands to be used alone to load or verify using “nameless” tape files.

If anything follows the command, it is assumed to be a filename and must start with a quotation mark character (“), or else an error will be signaled. Characters following the opening quotation mark are copied into the monitor buffer at $0A80 until a closing quotation mark is found. An error is signaled if no closing quotation mark is found or if more than 16 characters are used in the filename. If no other parameters are found following the closing quotation mark, a branch to 45995/$B3AB attempts to load or verify a tape file with the specified name.

If a parameter value follows the closing quotation mark, the low byte of the value is copied into the device number location (186/$BA). The next parameter value is assumed to be a starting address. A load or verify is attempted if the parameter is absent. The final parameter, if any, is assumed to be the ending address. If it’s missing, a branch to $B3D1 attempts a relocating load or verify. If it is present, the command is checked, and an error is signaled if it is not S (an ending address cannot be specified for a load or verify).

45983 $B39F MONSAVE

Handles save for monitor.

Disassembled code

Changes the secondary address setting to zero to specify a relocatable file if the device is the tape drive. The Kernal SAVE routine $FFD8 is used to write the data to the specified device. The routine ends by jumping back to the main loop $B08B.

45995 $B3AB MONLOAD

Handles load and verify.

Disassembled code

Checks that the command is either V or L, and signals an error if not. For V, the character code for V (86/$56) is left in the accumulator; for L, a zero is placed in the accumulator. The Kernal LOAD routine $FFD5 is then called, which will perform a load if the accumulator holds zero (for L) or a verify if the accumulator holds a nonzero value (for V). The routine tests for a verify error; if one occurs, the message ERROR is printed to signal that the data in memory does not match the file on disk or tape. The routine ends by jumping back to the main loop $B08B.

46033 $B3D1

Prepares for relocating load or verify.

Disassembled code

Loads the specified ending address value and changes the secondary address (185/$B9) from 1 to 0, indicating a relocating load is to be attempted. The routine then branches to attempt load or verify $B3AB.

46043 $B3DB FILLMEM

Fills memory with specified byte value.

Disassembled code

Evaluates the starting and ending addresses, and calculates the number of bytes to fill. If the starting and ending banks are not the same, an error is signaled to prevent the fill operation from crossing bank boundaries and overwriting the important values in locations $00 and $01. If the address range is valid, the fill byte value is read, and a loop begins to store fill byte in all memory locations in the specified range. The loop normally repeats until the specified number of bytes have been filled, but it includes a call to the Kernal STOP routine $FFE1, so the RUN/STOP key can be used to halt the fill. The routine ends by jumping back to the main loop $B08B.

When filling, you must be careful not to overwrite page 0 or page 2, both in the area of memory common to all banks. Page 0 contains the pointer to the byte to fill (102-104/$66- $68), and page 2 contains the INDSTA routine used to store bytes in the specified addresses. Overwriting either of these areas will likely result in a system lookup.

46086 $B406 ASSMBLE

Handles A (assemble) command or its equivalent (.).

Disassembled code

Checks for values following the A or period (.) and signals an error if none is found. The period is accepted as a synonym for A to simplify the assembly process by allowing you to edit the lines displayed by the D (disassemble) command. Since the D command automatically provides the period before each line, you may not have realized that it is treated as a separate command, but you can substitute it freely for A.

If only an address is found following the command, the routine simply returns to the main loop $B08B. Next, the routine searches for the first group of three nonspace characters. Any values on the input line with fewer than three characters are ignored; this explains why the two-digit hexadecimal byte values displayed in front of the three-character mnemonic by the disassemble routine are ignored when the instruction is edited. It also explains why changes to the two-digit byte values are ignored by this routine. The three-character pattern is then packed into a two-byte value. This packing scheme is a holdover from the RAM-resident monitors of earlier Commodore computers. It’s really unnecessary in the 128, which has room to spare in this block of memory, but Commodore’s programmers probably found it easier to reuse the existing code. All 8502 ML mnemonics consist of combinations of the alphabetic characters A-Z. Since there are only 26 different valid characters, any single character can be represented by a fivebit value (which can hold 0-31), and three five-bit values can fit nicely into two eight-bit bytes.

As an example of how this packing works, suppose the pattern found is LDA-corresponding to hex bytes $4C $44 $41. First, the value 63/$3F is subtracted from each byte, yielding $0D $05 $02. The binary equivalents are %00001101 %00000101 %00000010. The rightmost five bits of each value are shifted rightward into two bytes. The resulting packed mnemonic in these locations is %01101001 %01000100, or $69 $44.

Next, the routine infers an addressing mode from the parameter following the three-character pattern. The packed pattern is compared against those in the table at $B721. (The packing scheme does make the testing for valid mnemonics slightly faster, since only two bytes need to be compared instead of three.) An error is signaled if no match is found; otherwise, the position of the matching mnemonic in the table is used along with the addressing mode to calculate the proper opcode for the specified instruction. The opcode and its associated parameter (if any) are then stored in memory, and the routine at $B5DC is called to disassemble the line just assembled. This provides the hex values of the ML bytes. Finally, the routine loads the input buffer with an A and the next address value so that these will be found when the routine ends by jumping back to the main loop $B08B. This greatly simplifies the assembly of further instructions.

46489 $B599 DISASSM

Handles D (disassemble) command.

Disassembled code

Calculates the number of bytes to disassemble, then calls 46548/$B5D4 as many times as necessary to disassemble that many bytes. If no parameter is specified, 20 bytes are disassembled beginning at the address in 102-104/$66-$68. (Actually, up to 22 bytes may be disassembled, depending on how many are necessary for the last full instruction.) If no previous commands have been executed, the address value is unpredictable. After an earlier D command, the value will be the next address beyond the last one previously disassembled. If only a starting address is provided, 20 bytes are disassembled beginning at the specified address. If both starting and ending address parameters are provided, all instructions between those addresses will be disassembled. However, the disassembly loop includes a call to the Kemal STOP routine $FFE1, so the RUN/STOP key can be used to halt the disassembly. You may also use the NO SCROLL key to pause the disassembly. The routine ends by jumping back to the main loop $B08B.

46548 $B5D4 DISASMI

Disassembles a single instruction.

Disassembled code

Prints a period (to simplify editing of instructions), then the bank and address of the opcode byte to be disassembled. A call to $B659 calculates the addressing mode and offset into the packed mnemonic table for this opcode. The hex value of the opcode and up to two associated data bytes are then printed. They’re padded with spaces to align the mnemonics column. A call to 46753/$B6A1 unpacks and prints the mnemonic. The associated parameter value is then printed, along with such characters as #, (, and ), to identify the addressing mode for the instruction. For relative branching instructions, the target address of the branch is printed instead of the relative offset value.

46681 $B659 CALCMN

Calculates mnemonic and addressing mode.

Disassembled code

Manipulates the specified opcode (in the accumulator upon entry) to provide the offset into the table for the corresponding packed mnemonic, an addressing mode identifier value, and a count of associated data bytes (0-2).

46753 $B6A1 PRNTMN

Prints mnemonic for opcode.

Disassembled code

Unpacks and prints a mnemonic from the table at $B721. Upon entry, the accumulator holds the offset into the table for the mnemonic to be printed. As an example of how the unpacking works, the first table entry is $1C $D8. The binary equivalent is %0001110011011000. Divided into three five-bit groups (and ignoring the rightmost bit), that’s %00011 %10011 %01100, or $03 $13 $0C. Adding $3F to each yields $42 $52 $4B, corresponding to the character codes for the letters BRK. You would expect this to be the first table entry, since the BRK instruction has the lowest possible opcode ($00).

46787 $B6C3 OPCDTBL

Opcode decoding table.

Disassembled code

The values in this table are used by the mnemonic and mode calculation routine $B659 to determine the packed mnemonic table offset for the specified opcode value.

46855 $B707 INDCTBL

Table of addressing mode indicators.

Disassembled code

Each 8502 mnemonic may have several possible addressing modes, each with a different opcode. The values from this table are used to indicate the mode which should be associated with a mnemonic to represent the current opcode.

46869 $B715 MODETBL

Table of mode identification characters.

Disassembled code

The characters in this table are used by the assemble routine $B406 to determine the addressing mode being used, and by the disassembly routine $B5D4 to disassemble an instruction to add the proper characters to indicate the mode being used.

46881 $B721 MNEMTBL

Table of mnemonics in packed form.

Disassembled code

Each three-character mnemonic is packed into two bytes (see the assemble routine $B406 for details). The table is in two halves: entries at 46881-46944/$B721-$B760 are the first byte for the corresponding packed mnemonic, and entries at 46945- 47102/$B761-$B7A0 are the second byte.

47013 $B7A5 GETPARM

Evaluates a parameter in the input buffer.

Disassembled code

Interprets the next numeric parameter in the input buffer (using the parameter conversion routine $B7CE), then sets the status register accordingly: carry clear if a parameter has been found and the following character is a space, comma, or the end of the input line, or carry set if no parameter has been found. Upon exit, the accumulator holds the number of digits in the parameter.

47054 $B7CE CVTPARAM

Transforms numeric parameter into byte value.

Disassembled code

Reads a parameter value from the input buffer and converts it into a three-byte value in 96-98/$60-$63 (in low-byte to high-byte order). The parameter can be in anyone of four different numeric bases: hexadecimal, decimal, octal, or binary. Hex is assumed unless another base is specifically indicated by a prefix character: $ for hexadecima1, + for decimal, & for octal, and % for binary. Upon exit, location $0AB4 will hold a count of digits in the parameter (zero if no parameter was found). The status register’s carry bit will be set if the parameter value is too large to be interpreted (greater than 1048575/$FFFFF), and clear otherwise.

47242 $B88A BASETBL

Table of bases and bits-per-digit.

Disassembled code

This table is used by the parameter transformation routine $B7CE to determine how many bits to read for each digit of a number in the corresponding base.

47250 $B892 PRNTHEX

Prints a hexadecimal value.

Disassembled code

Prints a five-digit hexadecimal value followed by a space. The first digit comes from the low nybble of $68, the next two from 103/$67, and the final two from 102/$66. When entered at 47263/$B89F, the routine prints a four-digit hexadecimal value representing the value in the accumulator (low byte) and X register (high byte). When entered at 47269/$B8A5, a twodigit hex value representing the value in the accumulator is printed. When entered at 47272/$B8A8, only a space is printed.

47277 $B8AD

Moves cursor to start of current line.

Disassembled code

Uses the Kernal PRIMM routine $FF7D to print a carriage return, CHR$(13), followed by a cursor up, CHR$(145).

47284 $B8B4

Moves cursor to start of next line.

Disassembled code

Uses the Kernal BSOUT routine $FFD2 to print a carriage return, CHR$(13).

47289 $B8B9 CLRLIN

Clears a screen line.

Disassembled code

Uses the Kernal PRIMM $FF7D routine to print a carriage return, CHR$(13), followed by ESC Q (to clear a screen line) and a space.

47298 $B8C2 PRNTBYT

Prints two ASCII characters for a byte value.

Disassembled code

Calls the byte conversion routine $B8D2 to generate two ASCII characters representing the hex digits for the byte in the accumulator, then uses the Kernal BSOUT routine $FFD2 to print these characters.

47314 $B8D2 CVTBYT

Converts a byte value into two ASCII characters.

Disassembled code

Generates two ASCII characters representing the hexadecimal digits for the byte value in the accumulator upon entry. The characters are returned in the accumulator (high nybble) and X register (low nybble).

47335 $B8E7 TESTCHR

Tests next character in the input buffer.

Disassembled code

Tests the character in the input buffer at the previous pointer position (or, if entered at 47337/$B8E9, at the current pointer position). If the character is a colon, question mark, or the zero byte marking the end of input, the carry bit will be set upon exit to indicate that the end of the input line has been reached. Otherwise, carry will be clear to indicate that characters remain to be read.

47361 $B901 MOVEVAL

Transfers address and bank values to working pointer.

Disassembled code

Loads the working address pointer (102-103/$66-$67) with the parameter value calculated by the routine to transform input characters into byte values $B7CE in 96-97/$60-$61. The calculated bank value in 98/$62 is loaded into the bank pointer at $68.

47374 $B90E CALCCNT

Calculates number of bytes and banks to display or move.

Disassembled code

Calculates the number of bytes and banks in an address range by subtracting the starting address value in 102-103/$66-$67 from the ending address value in 96-97/$60-$61. The result of the subtraction is left in 96-97/$60-$61. The number of whole banks in the range is found by subtracting the starting bank, in $68, from the ending bank, in 98/$62, with the result left in 98/$62.

47394 $B922

Decrements pointer/counter

Disassembled code

Decrements the contents of 96-98/$60-$62 by 1 if entered at 47394/$B922, or by the value in the accumulator if entered at 47396/$B924. This is the line count for the display routine, the byte count for the disassemble routine, or the target address pointer for compare/transfer routine.

47420 $B93C DECCNT

Decrements byte count.

Disassembled code

Decrements the count of bytes for the current operation (99-100/$63-$64). If the value there rolls over from $0000 to $FFFF, the count of banks (101/$65) is also decremented. Upon exit, carry bit in the status register will be clear if the countdown is complete; otherwise, the carry bit will be set.

47440 $B950 INCPTR

Increments address pointer.

Disassembled code

Increments the address pointer (102-103/$66-$67) by 1 if entered at 47440/$B950, or by the value in the accumulator if entered at 47442/$B952. If the incrementing causes the address value to roll over from $FFFF to $0000, the bank pointer $68 is also incremented.

47456 $B960 DECPTR

Decrements address pointer.

Disassembled code

Decrements the address pointer (102-103/$66-$67). If the decrementing causes the address value to roll over from $0000 to $FFFF, the bank pointer $68 is also decremented.

47476 $B974 CHNGADD

Changes bank and address.

Disassembled code

If the parameter evaluation routine $B7A7 finds an address parameter for the command (indicated by a clear carry status bit), the address is loaded into the program counter storage locations, 3-4/$03-$04, and the bank value is loaded into the bank storage location, 2/$02. If no address parameter is provided, the values in the storage locations remain unchanged.

47491 $B983 PREPPTR

Prepares pointers for dual-address operations.

Disassembled code

Sets up the required pointers for those commands that require both starting and ending address parameters (C, F, H, and T). The starting address is loaded into 102-103/$66-$67, with the starting bank in $68. The ending address is loaded into 2743-2744/$0AB7-$0AB8, with the ending bank in 2745/ $0AB9. The number of bytes and banks affected, the difference between the starting and ending addresses, is calculated and stored in 99-101/$63-$65. If either address is missing or if the ending address is less than the starting address, the carry status bit will be set upon exit to signal the error.

47537 $B9B1 NUMXVRT

Performs number base conversion.

Disassembled code

Evaluates the parameter following the base symbol, then prints the value of the number in four different number bases: hexadecimal, decimal, octal, and binary. The routine can convert values in the range 0-1,048,575. For the hex value, a dollar sign is printed followed by four hex digits (five if the value is greater than 65535/$FFFF). For decimal, octal, and binary, this routine provides the leading character (+ for decimal, & for octal, and % for binary), then uses the subroutine at $BA47 to display the values. The routine ends by jumping back to the main loop $B08B.

47623 $BA07 HEXDEC

Converts a hexadecimal value to decimal.

Disassembled code

Converts the three-byte hexadecimal value in 96-98/$60-$62 into a decimal value in BCD (binary coded decimal) format in 2720-2723/$0AA0-$0AA3. For example, converting the value 258/$102, stored in 96-98/$60-$62 as $02 $01 $00, will result in $00 $00 $02 $58 being left in 2720-2723/$0AA0-$0AA3. This routine takes advantage of the 8502’s rarely used decimal mode. To prevent the complications that would likely be caused by interrupts occurring while the processor is set for decimal mode, interrupts are disabled until the 8502 is reset to its normal (binary) mode.

47687 $BA47 PNTOBD

Prints octal, binary, or decimal values.

Disassembled code

Prints the octal or binary equivalent of a three-byte value. Enter with 96-98/$60-$62 holding the three-byte value, the accumulator holding the maximum number of digits allowed (8/$08 for octal, 24/$18 for binary), and the Y register holding the number of bits to interpret per printed digit minus 1 (2/$02 for octal, 0/$00 for binary). To print a decimal value, enter at 47709/$BA5D with the decimal value in 2720-2723/ $0AA0-$0AA3 in BCD format (see the routine $BA07), and with 8/$08 (maximum number of digits) in the accumulator and 3/$03 (number of bits-per-digit minus 1) in the Y register.

47760 $BA90 DISKCMD

Handles @ (disk) commands.

Disassembled code

Checks whether a parameter was found following the command. If so, the low byte of the value specifies the device number for the disk command. If no parameter is specified, a device number of 8-the most common number for disk drives-is used. If the first nonspace character following the device number is a dollar sign, then the routine branches to 47875/$BB03 to display a disk directory. Any other characters following the device number parameter are sent over the serial device as a command to the specified device. The routine ends by jumping back to the main loop $B08B. Because the first item following the @ is always interpreted as a device number, you must specify some device number value (@8 IO is a valid command, but @IO isn’t because the IO will be interpreted as an invalid device number). However, an alternate syntax is possible. The parameter evaluation routine $B7A7 exits when it encounters a comma, so a comma can be used alone to specify a null parameter. Thus, a command of the form @,IO is acceptable-the default device number (8) will be used.

47875 $BB03 SHOWDIR

Displays disk directory.

Disassembled code

If the first nonspace character in the command string following the @ command is a dollar sign, this routine is branched to because a disk directory requires more elaborate screen formatting than the simple disk status messages otherwise returned by the @ command. This routine is a good model for a directory display using machine language. You can’t use this routine directly because it ends with a branch that returns to the monitor main loop $B08B.

47986-49151 $BB72-$BFFF Unused

The final 1166 bytes in the monitor ROM block are unused and contain 255/$FF (except the last two bytes, which are $00 $3A)