In this page...


Index

$D500-$D50B

54528 $D500 MMUCRI

Configuration register

The value in the configuration register determines which of the available memory resources will be visible to the microprocessor at any given time. However, this particular register is only rarely used-not at all in 128 ROM except during MMU register initialization-because it has an identical twin at address 65280/$FF00 which is more convenient. There is really only one configuration register; it just appears at two different addresses. The register is accessible here only when the I/O block is visible, whereas it is always accessible at 65280/$FF00, regardless of the memory configuration. As a result, memory configuration is usually done with the higher configuration register location. Refer to the entry (below) for the other configuration register for details of the function of the register bits.

This register is initialized during the Kernal RESET routine $E000 to 0/$00, the setting for the bank 15 configuration. However, that step of the routine is redundant because the same value has previously been stored in 65280/$FF00. Reading this register returns the current configuration setting, regardless of whether the setting has been established by writing to this register or to 65280/$FF00.

54529-54530-54531-54532 $D501-$D502-$D503-$D504 MMUCRI

Preconfiguration register

These registers provide an indirect method of setting up a memory configuration. Whenever a value-any value-is stored in one of the four load configuration registers at 65281-65284/$FF01-$FF04, the value in the corresponding pre configuration register is transferred to the configuration register. Thus, the pre configuration registers allow you to set up as many as four different memory configurations, each of which can then be established with a single store operation. The bit functions for pre configuration register locations are the same as for the configuration register. For details, see the entry below for 65280/$FF00.

The Kernal RESET routine $E000 initializes all the preconfiguration registers to 0/$00, the setting for the bank 15 configuration. The preconfiguration/load configuration register memory management technique is not used by the operating system. However, BASIC ROM routines do make use of this method. The BASIC cold start $4023 and warm start $4009 routines both initialize the preconfiguration registers as follows:

Register Setting Bank configuration
54529/$D501 63/$3F 0
54530/$D502 127/$7F 1
54531/$D503 1/$01 14
54532/$D504 65/$41 A custom setting with the same visible ROM as bank 14, but with RAM from block 1 instead of block 0

In a number of instances in BASIC ROM you’ll find instructions like STA $FF03, where BASIC is using this shortcut method of bank selection.

Because BASIC depends on standard values in the preconfiguration registers, it’s generally best to avoid changing the register settings in machine language programs which must work in conjunction with BASIC. To reset the preconfiguration registers to their default values without performing the entire BASIC warm start or cold start sequence, call the BASIC ROM subroutine at 16762/$417 A. Of course, if BASIC is not being used, you’re free to use the pre configuration registers to set up any memory configuration you desire.

54533 $D505 MMUMCR

Mode configuration register

The primary function of this register is to select the current operating mode from the three possibilities-128, 64, or CP/M.

Bit 0: this bit determines which microprocessor is in control of the system. Writing a %0 here puts the Z80 in command, while writing a %1 switches to the 8502. Since this bit is reset to %0 when the system is reset or powered on, the Z80 always has control of the system before the 8502. The reset sequence in Z80 ROM is nearly identical to that in 128 mode ROM, including checking for the presence of Commodore 64 cartridges and testing whether the Commodore key is held down for Commodore 64 mode. If the Z80 reset routine does not find a CP/M boot disk in the drive (or a Commodore 64 cartridge or the Commodore key held down), it jumps to a routine it has copied into 65504/$FFE0 in block 0 RAM. That routine ends by setting this bit to %1 to return control to the 8502 for 128 mode.

Switching processors is not for the faint of heart. When you activate the Z80, it will begin executing instructions at whatever address is currently in its program counter registers. The address in those internal processor registers can’t be changed from 128 mode, so you’re stuck with having the Z80 take up wherever it left off when the system was switched to 128 mode. This address is usually 65518/$FFEE, the location following the one where 128 mode was activated at the end of the Z80’s reset routine. In block 0 RAM, that location is initialized with a Z80 instruction (RST1) to perform a warm start of CP/M mode. If you don’t have a valid Z80 machine language instruction there when you activate the Z80-for example, if the system is in a memory configuration such as bank 15 where 128 Kernal ROM is seen at that address-you’ll probably experience an immediate system lockup.

Bits 1-2: unused. These bits always return %1 when read, and writing to these bits has no effect.

Bit 3: this bit is connected to the MMU pin labeled FSDIR. This line is bidirectional, meaning that it can be both an input and an output. The 128 uses the line only as an output, to control the direction of data flow on the fast serial bus. The lines of the slow serial bus are each controlled by a pair of CIA chip lines, one for input and one for output, but the fast serial bus uses the same CIA chip lines for both input and output. The 128’s designers added additional circuitry which insures that the fast serial bus will ignore incoming data during fast serial output. The FSDIR (fast serial direction) line controls that circuitry. Writing a %0 to the bit pulls the line to a low (0 volts) state, which sets the fast serial bus for input. Writing a %1 to the bit allows the line to go to a high (+5 volts) state, which sets the fast serial bus for output. The IOINIT routine $E109 leaves this bit set to %0 so that the system can detect fast serial input. The setting of this bit is controlled in Kernal ROM by the routines SPIN $E5C3 and SPOUT $E5D6.

Bits 4-5: these bits are connected, respectively, to the MMU pins labeled GAME and EXROM, which in the 128 are connected to the memory expansion port lines with the same names. These MMU lines are bidirectional, meaning that they can be both inputs and outputs. The 128 uses the lines only as inputs, to read the state of the memory expansion port lines (pins 8 and 9 of the port). Writing to one of the bits sets the corresponding line’s output level. Writing a %0 to the bit pulls the line to a low (0 volts) state, while writing a %1 to the bit allows the line to go to a high (+5 volts) state. When used for input, an external device connected to the line can pull the line low if its output level is set high, but cannot bring the line high if its output level is set low, so writing a %0 to one of these bits effectively blocks the use of the line as an input.

The Kernal RESET routine $E000 initializes both of these bits to % 1 so that they can be used to read the state of the port lines. In 128 ROM, these bits are read during the reset sequence by the routine which checks for the presence of a Commodore 64 ROM cartridge [$E242]. Almost all Commodore 64 cartridges ground one or both of these port lines, so the 128 will assume that a 64 cartridge is present if either of these bits is found to be %0, and will respond by switching to Commodore 64 mode.

While the system is in 128 mode, you can use these bits to control the corresponding expansion port pins as either inputs or outputs. Keep in mind, however, that the pins must be high during reset or the system will enter Commodore 64 mode.

Bit 6: this bit controls which set of ROMs will be visible to the system. Writing a %0 here selects the 128 mode ROM set, while writing a %1 selects the Commodore 64 mode ROM. The 128 mode RESET routine $E000, of course, initializes this bit to %0. However, simply writing a % 1 here won’t cause a clean transfer to 64 mode; it’s necessary to perform the 64 mode reset sequence after the 64 mode ROM is selected. See the C64_MODE routine [$E24B] for details. This bit has one other effect-selecting 64 mode also makes all the MMU chip registers invisible, so once you make the jump to 64 mode there is no way back to 128 mode short of resetting the computer or turning it off and back on.

Bit 7: this bit is connected to the MMU pin labeled 40/80. This line is bidirectional, meaning that it can be both an input and an output. The 128 uses the line only as an input, to read the 40/80 DISPLAY switch on the 128’s keyboard. Writing to the bit sets the state of the line output. Writing a %0 to the bit pulls the line to a low (0 volts) state, while writing a % 1 to the bit allows the line to go to a high (+ 5 volts) state. The switch connected to the line can pull the line low if its output level is set high, but cannot bring the line high if its output level is set low. Thus, writing a %0 to this bit effectively blocks the reading of the 40/80 DISPLAY switch. The CINT screen editor initialization routine $C07B, part of both the reset and RUN/STOP-RESTORE sequences, sets this bit to %1 to insure that the switch can be read. The bit returns a %0 when read while the switch is down (80-column position), or a % 1 while the switch is up (40-column position). In 128 ROM, this bit is read only during the CINT routine, where its setting is used to determine which display to make active.

54534 $D506 MMURCR

RAM configuration register

An important aspect of the MMU’s memory configuration capabilities is its ability to create common areas of RAM, areas where the same RAM is seen regardless of the configuration register selection. Kernal routines copied into the default common area allow the processor to jump from bank to bank, or to manipulate data in other banks. This register controls the common RAM feature, and also specifies which RAM block the VIC video banks are seen in.

Bits 0-3: these bits control the common RAM feature. Bits 0-1 control the size of the common areas, and bits 2-3 control whether the common areas will exist at the top, bottom, or both top and bottom of the RAM blocks. (No RAM will be common if bits 2-3 are set to %00.) When common RAM is specified, the RAM seen as common is always that from block O. As long as the common RAM feature is enabled, there is no way for the processor to access the RAM in block 1 which is covered by the common area. When a common area at the top of memory is selected, it will be visible only when bits 4-5 of the configuration register are set to make RAM visible at the top of memory.

There are a few exceptions to the common RAM rules. First, locations 0-1/$00-$01, the 8502 on-chip I/O port, and locations 65280-65284/$FF00O-$FF04, the upper MMU registers, always appear in any configuration, regardless of the setting of these bits. That is, there is no way to make the processor see anything other than the hardware registers in these locations (at least not while the computer is operating in 128 mode). Second, the area where the two lowest pages of memory are seen is also affected by the registers at 54535-54538/ $D507-$D50A. Even if you change the page pointers at 54535/$D507 or 54537/$D509 to physically move zero page or page 1 somewhere in memory outside the common area, the contents of those pages will still appear to be common as long as a common area at the bottom of memory is selected. If you disable the common area at the bottom of memory, all references to zero page and page 1 will continue to affect only block a RAM unless you specifically change the block pointers for those pages in the registers at 54536/$DF08 and 54538/$DFOA. Finally, you should be aware that the common area setting does not affect the RAM block from which the VIC chip sees its video bank. The VIC’s RAM block is determined by bits 6-7 of this register, and all RAM in the VIC bank is seen in the block specified in those bits without regard for the common area specification. The VIC chip can see the memory that is hidden from the processor.

The four possible selections for the size of the common areas are as follows: |Bits 1 0|Size of common areas| |-|-| |0 0|1K| |0 1|4K| |1 0|8K| |1 1|16K|

The four possible selections for location of the common areas are as follows: |Bits 3 2|Location of common areas| |-|-| |0 0|No common RAM| |0 1|Common area at bottom of memory| |1 0|Common area at top of memory| |1 1|Common areas at both top and bottom of memory|

The default value in these bits, established during the Kernal RESET routine $E000, is %0100 = $4. This setting selects a 1K common area at the bottom of memory, addresses 0-1023/ $0000-$03FF. The proper functioning of both the operating system and BASIC depends on the presence of this common area, so you should change the setting of these bits with great care. BASIC is almost certain to crash if the common area is disabled altogether. The operating system will crash if an interrupt occurs while the system is configured for block 1 RAM with the common area disabled.

You can manipulate these bits to gain temporary access to the 1K of RAM from block 1 which is normally hidden by the standard common area, but making any practical use of the hidden area is more than a little complicated. The standard interbank data transfer routines like INDFET and INDSTA can’t be used because they depend on the common area. Furthermore, the machine language routine you write to perform the transfer can’t use any zero-page locations, nor can it use jumps to subroutines or other instructions which would affect the stack (page 1). Interrupts must also be disabled while the common area is disabled. The following routine swaps the contents of the hidden block 1 area with the contents of the next higher 1K area of block 1 (addresses 1024-2047/$0400-$07FF). Note that this routine must be placed in block 1 RAM

1FF60 SEI         // Disable interrupts
1FF61 LDA #$7E    // Make I/O block visible
1FF63 STA $FF00
1FF66 LDA #$00    // Disable common RAM
1FF68 STA $D506
1FF6B LDA #$01    // Make pages 0 and 1 visible
1FF60 STA $D508   // block 1 RAM
1FF70 STA $D50A
1FF73 LDA #$00
1FF75 STA $D507
1FF78 LDA #$01
1FF7A STA $D509   // Initialize source starting 
1FF70 LDA #$00    // address locations
1FF7F STA $FF91
1FF82 STA $FF98
1FF85 LDA #$04    // Initialize target starting
1FF87 STA $FF95   // address locations
1FF8A STA $FF9C
1FF80 LDY #$02    // Initialize index for first page
1FF8F LDA $0000,Y // Swap bytes
1FF92 TAX
1FF93 LDA $0400,Y
1FF96 STA $0000,Y
1FF99 TXA
1FF9A STA $0400,Y
1FF90 INY         // Repeat for 256 bytes per page
1FF9E BNE $FF8F
1FFA0 INC $FF91   // Increment address high bytes
1FFA3 INC $FF95
1FFA6 INC $FF98
1FFA9 INC $FF9C
1FFAC LDA $FF9C   // Check whether all four pages have
1FFAF CMP #$08    // been swapped
1FFB1 BCC $FF8F
1FFB3 LDA #$00    // If so, restore pages 0 and 1
1FFB5 STA $D508   // to block 0 RAM
1FFB8 STA $D50A
1FFBB STA $D507
1FFBE LDA #$01
1FFC0 STA $D509
1FFC3 LDA #$04    // Reenable common RAM
1FFC5 STA $D506
1FFC8 CLI         // Reenable interrupts
1FFC9 RTS

To execute the routine, use J 1FF60 from the monitor or BANK l:SYS 65376 from BASIC.

Bits 4-5: these two bits are not used in the current version of MMU. The design specifications for the chip indicate that in future versions these bits may be used in a “superbanking” scheme to select one of four separate 256K blocks of RAM in a 1M (one-megabyte, or 1024K) system. It’s a tantalizing prospect, but there’s no guarantee that a Commodore 1024 will ever be produced. In the current MMU, the bits will retain whatever value is written to them, but changing the bit settings has no effect on the memory configuration. The bits are initialized during the Kernal RESET routine $E000 to %00

Bits 6-7: these bits determine which 64K block of RAM the VIC video bank is located in. (See the section on the VIC chip earlier in this chapter for more information on video banks.) The formally defined selections for the bits are as follows:

Bits 7 6 RAM block for VIC video bank
0 0 0
0 1 1
1 0 2
1 1 3

Remember, however, the 128 actually has only two 64K blocks of RAM. Thus, the setting of bit 7 is meaningless, although that bit will retain whatever value you write to it. Only the setting of bit 6 matters, since the choice is between blocks 0 and 1. The default setting for these bits is %00, since the normal VIC text and bitmapped screens are located in block 0 RAM.

You should note that the operating system and BASIC will not support a VIC video bank in block 1. That is, all the screen editor ROM routines and BASIC bitmapped graphics routines assume that the VIC screen is located in block 0 RAM, and will continue to write data to block 0 even after you’ve changed these bits to switch the video bank into block 1 RAM. Thus, you must provide your own text and graphics routines for a display from block 1. For text displays, you can use the standard character sets because the character ROM is still visible in a block 1 video bank.

These bits actually determine the RAM block for all DMA (direct memory access) operations, not just for the VIC chip, but the VIC is the only built-in device in the 128 to use DMA. The only other commonly available DMA device for the 128 is the REC (RAM expansion controller) chip in the 1700 and 1750 Memory Expansion Modules. These bits, rather than bits 6-7 of the configuration register, determine which block of 128 system RAM is affected by REC transfer operations.

54535-54536 $D507-D508 MMUP0L MMUP0H

Page 0 pointers

One unique feature provided by the MMU is the ability to relocate page 0 anywhere in memory. Zero page is the most heavily used area of memory in any computer built around a 6502-family microprocessor like the 128’s 8502, since the processor has many instructions that are designed to work only with this page. Although neither the 128 operating system or BASIC makes use of this page-relocation capability, it has several interesting applications. For example, you could have several programs in memory simultaneously and give each its own personal zero page, without having to worry about memory conflicts.

Location 54535/$D507, the page pointer, selects the page of memory to which zero page is to be relocated. The Kernal RESET routine $E000 stores the value 0/$00 here to locate zero page initially in the true zero page. To move zero page, store the target page number here. (The page number is equivalent to the high byte of the first address in the page.) Actually, the operation is more a swap than a relocation. When you redirect zero page to another page of memory, all references to addresses in the target page are diverted to the true zero page. For example, if you store the value 19/$13 in this register to move zero page to page 19, then all references to addresses in the range 2-255/$02-$FF will be redirected to 4866-5119/$1302-$13FF, and all references to 4864-5119/$1300-$13FF will be redirected to 0-255/$00-$FF. Notice that addresses 0-1 from zero page are not affected. These are the processor’s on-chip I/O port registers-not RAM. Storing values in these locations always affects the registers rather than RAM.

Location 54536/$D508 selects the block of RAM in which zero page will be seen. Only bit 0 of the block pointer is significant. Setting that bit to %0 selects block 0 RAM, while setting it to %1 selects block 1. The bit is set to %0 by the Kernal RESET routine $E000. Bits 1-3 will retain whatever value is written to them, but changing these bits has no effect. The RESET routine sets these bits to %000. Bits 4-7 are also unused, but these bits always return % 1 when read. As a result, the value in this register will always be at least 240/$FO. Values written to the page pointer take effect immediately, but values written to the block pointer are actually effective only after the next time a value is stored in the page pointer. Thus, if you are changing both registers it is important to remember to store the value in the block pointer (54536/$D508) before storing the value in the page pointer (54535/$D507).

However, the block pointer value is ineffective while the RAM configuration register (54534/$D506) is set to provide a common area at the bottom of memory. With the bottom common area enabled, zero page is always seen in block 0 RAM, although it can still be moved around freely within block 0. Since this is the default configuration for the 128, there is rarely a need to change the value in 54536/$D508. If you select block 1 RAM while the bottom common area is enabled, a strange effect results. Zero page is relocated to the page specified in the page pointer, but that page is not swapped with zero page. That is, both pages will occupy the same area of memory, and the true zero-page area remains untouched.

When choosing areas of memory for a relocated zero page, you must avoid page 1 (or the area where you relocate page 1), since that page is also vital to proper system operation. You should also avoid page 255/$FF, since attempting to place zero page there will cause conflicts with the MMU registers. Because all references to the target page will affect true zero page, you should be careful to avoid using any locations in the target page while zero page is relocated (unless you really want to change true zero page). After relocating zero page, you will probably want to call the Kernal routines RAMTAS [$FF87] and CINT [$FF81] to initialize important zero-page locations. (If you switch to a zero page without proper screen editor variables, you may find yourself looking at a garbled mess on the screen.)

54537-54538 $D509-D50A MMUP1L MMUP1H

Page 1 pointers

Just as the MMU can relocate zero page, it also has the ability to make page 1 appear anywhere in memory. Page 1 is vital in any computer built around a 6502-family microprocessor like the 128’s 8502, since that page is the processor stack. The stack is the area of memory where the processor stores return addresses while it is executing subroutines or handling interrupts. The stack is also used extensively for temporary data storage. Although neither the 128 operating system or BASIC makes use of this page-relocation capability, it has several interesting applications. For example, you could have several programs in memory simultaneously and give each its own personal stack, without having to worry about memory conflicts.

Location 54537/$D509, the page pointer, selects the page of memory to which page 1 is to be relocated. The Kernal RESET routine $E000 stores the value 0/$01 here to locate page 1 initially in the true page 1. To move page 1, store the target page number here. (The page number is equivalent to the high byte of the first address in the page.) Actually, the operation is more a swap than a relocation. When you redirect page 1 to another page of memory, all references to addresses in the target page are diverted to the true page 1. For example, if you store the value 21/$15 in this register to move page 1 to page 21, then all references to addresses in the range 256-511/ $0100-$01FF will be redirected to 5376-5631/$1500-$15FF, and all references to 5376-5631/$1500-$15FF will be redirected to 256-511/$0100-$01FF.

Location 54539/$D50A selects the block of RAM in which page 1 will be seen. Only bit 0 of the block pointer is significant. Setting that bit to %0 selects block 0 RAM, while setting it to % 1 selects block 1. The Kernal RESET routine $E000 sets this bit to %0 for block 0. Bits 1-3 will retain whatever value is written to them, but changing these bits has no effect. The bits are set by the RESET routine to %000. Bits 4-7 are also unused, but these bits always return %1 when read. As a result, the value in this register will always be at least 240/$F0. Values written to the page pointer take effect immediately, but values written to the block pointer are actually effective only after the next time a value is stored in the page pointer. Thus, if you are changing both registers it is important to remember to store the value in the block pointer (54538/$D50A) before storing the value in the page pointer (54537/$D509).

However, the block pointer value is ineffective while the RAM configuration register (54534/$D506) is set to provide a common area at the bottom of memory. With the bottom common area enabled, page 1 is always seen in block 0 RAM-although it can still be moved around freely within block O. Since this is the default configuration for the 128, there is rarely a need to change the value in 54538/$D50A. If you select block 1 RAM while the bottom common area is enabled, a strange effect results. Page 1 is relocated to the page specified in the page pointer, but that page is not swapped with page 1. That is, both pages will occupy the same area of memory, and the true page 1 area remains untouched.

This mobile page 1 allows a tricky technique for filling areas of memory. Instead of storing values in a series of locations, you can redirect page 1 to the desired area of memory and push values onto the relocated stack. The advantage of this is that the PHA instruction takes only half as long to execute as the STA (address),Y instruction. Just be sure to preserve the original stack pointer; otherwise, your program will crash when the memory moving subroutine tries to return to its calling routine. The two routines below are alternate methods of filling the high-resolution screen area. The conventional method (on the left) is shorter, but the relocated stack method (on the right) is about 30 percent faster.

B00 LDA #$00              C00 TSX
B02 STA $FB               C01 STX $FB
B04 LDA #$20              C03 LDX #$FF
B06 STA $FC               C05 TXS
B08 LDA #$FF              C06 LDA #20
B0A LDX #$20              C08 STA $D509
B0C LDY #$00              C0B LDA #$FF
B0E STA ($FB),Y           C0D LDX #$20
B10 INY                   C0F LDY #$00
B11 BNE $BOE              C11 PHA
B13 INC $FC               C12 INY
B15 DEX                   C13 BNE $C11
B16 BNE $C11              C15 INC $D509
B18 RTS                   C18 DEX
                          C19 BNE $C11
                          C1B LDX $FB
                          C10 TXS
                          C1E LDA #$01
                          C20 STA $0509
                          C23 RTS 

54539 $D50B MMUVER

Version register

This read-only register returns a constant value (much like a ROM location) reflecting the amount of RAM in the system and the version of the MMU. If Commodore ever introduces successors to the 128 built with similar system architecture (a Commodore 256 or 1024, for example), this register will allow software to identify the amount of memory available.

Bits 0-3: these four bits indicate the version of the MMU chip installed in the 128. In current 128s the value here is %0000 = 0, indicating version 0 of the MMU, but this may change if new versions of the chip are introduced. Bits 4-7: these four bits indicate the number of 64K blocks of RAM present in the system. In the 128, the value here is %0010 = 2, indicating 128K of RAM in two 64K blocks.

54540-54783 $D50C-$D5FF Unused

All the unused register addresses in this range return the value 255/$FF when read. Writing to these locations has no effect.