In this page...


Index

$E000-$FFFF - Kernal Rom, Standard Commodore Jump Table

Kernal ROM

In software engineering jargon, the collection of subroutines that perform basic input and output functions for a computer is referred to as the operating system kernel. The developers of the kernel for the original Commodore PET spelled (or misspelled) the term as kernal, and Commodore operating systems have been referred to as the Kernal ever since. There are both similarities and significant differences between the 128 Kernal and the Kernals of earlier models.

The Kernal handles input from or output to five basic sources: the keyboard, the video screen, the tape drive (Datassette), the RS-232 port, and the serial bus (to which disk drives and printers are connected). In the 128 Kernal, all keyboard and video functions have been transferred to a separate block of ROM, the screen editor, at $C000.

Significant enhancements include the addition of an 80- column screen, ESC-key screen-editing sequences, and keyboard table pointers in RAM that make it easy to customize the keyboard. Tape and RS-232 support is largely unchanged from that provided in the Commodore 64 Kernal. Serial bus operation is significantly enhanced by the addition of a new fast serial mode which can transfer data much more quickly than the old system, now fittingly referred to as slow serial mode.

Additional new features of the 128 Kernal include routines to handle the storage, retrieval, and comparison of data from the various memory banks supported by the system, and support for DMA (Direct Memory Access) transfer operations to and from the 1700 and 1750 Memory Expansion Modules.

The heart of the Kernal is the collection of routines called by the Kernal jump table at 65409-65525/$FF81-$FFF5. The routines called from that table, and their supporting subroutines, make up the bulk of the Kernal and provide access to the majority of the 128’s input/output (I/O) capabilities. Almost any I/O operation can (and should) be performed through the appropriate jump table entry. The Kernal jump table has been a feature of all Commodore operating systems.

The 128 adds an additional jump table with 19 new entries at 65351-65407/$FF47-$FF7F. These entries provide access to most of the 128 Kernal’s new or enhanced features.

57344 $E000 RESET

Performs main system initialization sequence

Resets the processor stack pointer to the top of the stack, disables IRQ interrupts, and insures that the processor is not in decimal mode; then sets the MMU configuration register for bank 15. Other MMU registers are initialized from the table at $E04B. Next, the initialization status flag ($0A04) is reset to 0/$00 to indicate that all variables and vectors need to be initialized. The interrupt and reset handling routines at 65285-65348/$FF05-$FF44 in Kernal ROM are copied to that same area in all RAM banks, and the INDFET, INDSTA, INDCMP, JSRFAR, JMPFAR, and DMA-CALL routines are copied from the table at 63488/$F800 into bank 0 RAM.

Locations 65525-65527/$FFF5-$FFF7 in bank 1 are then examined to see if they contain the character codes for the letters CBM. If not, those locations are initialized with that character pattern, and the soft reset vector at 65528/$FFF8 in bank 1 is initialized. However, if the test pattern is found (indicating that RESET has already been performed at least once), the routine jumps to the address in the vector. Normally, the vector points to the routine at $E224, which simply reinitializes the test pattern and vector. You can change the address to add your own extra steps to the reset sequence.

The subroutine at 57922/$E242 is called to check for the presence of a Commodore 64 cartridge. If one is detected, the system is switched to 64 mode and the computer becomes a Commodore 64. (You must press the RESET button or turn the computer off and back on to return to 128 mode.) Otherwise, the subroutine records the presence of any 128 function ROMs in the table at 2753-2756/$0AC1-$0AC4. If a logged function ROM is autostarting, its cold start routine is called. It is possible that an autostarting ROM will retain control of the system and will not return to complete the reset sequence.

Next, the IOINIT routine $E109 is called to initialize the video, CIA, and SID chip registers. The keyboard column which includes the RUN/STOP and Commodore keys is scanned. If RUN/STOP has been pressed, the Kernal memory initialization flag (2562/$0A02) is checked. If the flag contains a nonzero value, the following memory initialization step is skipped. Since the flag is given the value 165/$A5 after the first call to RAMTAS $E093, zero-page values and memory pointers are preserved if RUN/STOP is held down during a subsequent reset. Otherwise, RAMTAS $E093 is called to clear all zero-page RAM locations and reestablish Kernal pointers.

RESTOR $E056 is called to load default Kernal indirect vectors into 788-819/$0314-$0333. CINT $C000 is called to initialize the screen editor, after which IRQ interrupts are once again allowed. If the RUN/STOP key has been pressed, the monitor is entered through its cold start entry point $B000. If the Commodore key has been held down, 64 mode is entered using the C64-MODE routine $E24B. Otherwise, BASIC 7.0 is entered via the restart vector at 2560/$0A00. If the RAMTAS step has been performed, the restart vector will point to BASIC’s cold start entry point $4000.

It is possible to perform most of the reset sequence without losing the BASIC program currently in memory. Simply hold down RUN/STOP while pressing the RESET button. This will skip the RAMTAS step, which would wipe out important program pointers. You will land in the monitor after the reset; type X to exit to BASIC, where the current program should still be intact.

Note that the reset routine does not explicitly attempt to boot a disk or to initialize function ROMs that are not autostarting. These tasks are performed by the Kernal PHOENIX routine $F867. In 128 ROM, PHOENIX is called only during the BASIC cold start routine $4023. However, as long as the RUN/STOP or Commodore key is not held down, the reset routine ends by jumping to the BASIC cold start routine, so those actions are implicitly part of the normal reset sequence.

57419 $E04B

Table of default MMU register settings

The 11 values in this table are copied into the MMU chip registers at $D500-$D50A by the system reset routine $E000.

57430 $E056 RESTOR

Restores Kernal indirect vectors to their default values

This routine has a Kernal jump table entry at $FF8A

Loads the X and Y registers with the value $E073, the address of the default vector table, then clears the status register carry bit, and falls through into the following routine to load default vector values.

57435 $E05B VECTOR

Loads or copies Kernal indirect vector values

This routine has a Kernal jump table entry at $FF8D

Transfers the address value in the X and Y registers upon entry into a working pointer (195-196/$C3-$C4). If the carry bit is clear, 32 bytes starting at the specified address are copied to the Kernal indirect vectors at 788-819/$0314-$0333. If carry is set, the contents of the indirect vectors are copied to 32 locations starting at the specified address. In either case, the target address must be visible in bank 15.

57459 $E073

Table of default Kernal indirect vector values

The 16 two-byte values in this table are copied to the Kernal indirect vectors (788-819/$0314-0$333) by the RESTOR routine $E056, part of the reset sequence.

57491 $E093 RAMTAS

Initializes zero page and Kernal pointers

This routine has a Kernal jump table entry at $FF87

Sets all zero page RAM locations (2-255/$02-$FF) to 0/$00, then initializes the cassette buffer pointer ($B2-$B3) to $0B00, the RS-232 input buffer pointer ($C8-$C9) to $0C00, and the RS-232 output buffer pointer (202-203/$CA-$CB) to 3328/$0D00. The MEMSIZ pointer (2567-2568/$0A07-$0A08) is set to 65280/$FF00 to mark the top of free RAM, and the MEMSTR pointer (2565-2566/$0A05-$0A06) is set to 7168/$1C00 to mark the bottom of free RAM in bank 0. The BASIC restart indirect vector (2560-2561/$0A00-$0A01) is loaded with 16384/$4000, the address of BASIC’s cold start entry point. Finally, the Kernal memory initialization flag ($0A02) is set to 165/$A5 to indicate that this routine has been performed.

57549 $E0CD

Initializes all RAM-resident Kernal routines

Copies the interrupt- and reset-handling routines at 65285-65348/$FF05-$FF44 in Kernal ROM into the same addresses in both RAM banks. These routines redirect interrupts and reset to the proper handling routine in Kernal ROM (bank 15). It’s necessary to have a copy in each bank, because an interrupt or reset can occur while the system is configured for any bank. The routines are actually copied into banks 0-3, even though there isn’t unique RAM in banks 2 and 3 in the current 128.

Next, the code for the RAM-resident portions of vital indirect access routines-INDFET $02A2, INDSTA $02AF, INDCMP $02BE, JSRFAR $02CD, and JMPFAR $02E3-is copied from the table at 63488/$F800 into page 2 of the common area of RAM. Finally, the code for the DMA-CALL execution routine $03F0 is copied from $F85A into page 3 of the common area of RAM.

57609 $E109 IOINIT

Initializes I/O chip registers

This routine has a Kernal jump table entry at $FF84

Begins by disabling all interrupt sources on both CIA chips and halting all CIA timers. All lines from the four CIA I/O ports (ports A and B on both chips) are assigned their default directions, input or output. Output lines are also assigned their default states, high or low. The direction and status of lines connected to the processor’s built-in I/O port are also established.

The VIC-II video chip’s raster compare register is tested to determine whether the raster line count ever reaches the value 264/$108. This indicates which video system, NTSC (North American) or PAL (European), is being used. The video system depends on the VIC-II chip currently installed. There are separate versions for NTSC and PAL. The highest possible raster line for an NTSC system is 263, so a value of 264 here means that the system is using PAL. The NTSC/PAL flag (2563/$0A03) is set accordingly-to 0/$00 for NTSC or 255/$FF for PAL. By designing the Kernal to adjust itself for either system, Commodore’s engineers avoided the need for separate versions of the Kernal for North American and European 128s.

Next, a number of Kernal I/O flags are cleared: clock mode storage ($0A37), IRQ vector storage ($0A0A), custom mode setting ($0A3A), and jiffy count compensation (2614/$0A36). The keyboard (device 0) is made the current input device (153/$99), and the screen (device 3) is made the current output device (154/$9A).

VIC-II chip registers (53248-53296/$D000-$D030) are initialized from the table at 58055/$E2C7, and 8563 video chip registers are initialized using the subroutine at 57820/$E1DC. If the version number of the 8563 chip (bits 0-2 of the register at 54784/$D600) indicates that one of the newer revisions of that chip is installed, the subroutine is called again to adjust the horizontal scrolling register (R25). If the PAL video system is in use, the subroutine is called yet again to adjust vertical display registers (R4 and R7). Bit 7 of the initialization status flag ($0A04) is tested. If the bit is set to %1, IOINIT has been called at least once before, so the following step, which sets up the 80-column character set, is skipped. If the bit is %0 (as will normally be the case when this routine is called as part of the reset sequence), the screen editor INIT80 routine $C027 is called to copy the ROM character definitions into 8563 RAM. Then bit 7 of the initialization status flag will be set to %1 to indicate that the step has been performed.

All registers for the SID sound chip (54272-54296/ $D400-$D418) are cleared to zero to disable any sound output; then VIC-II raster interrupts are enabled. The raster interrupt (set by the default table value to occur at scan line 255) is the normal source of jiffy IRQ interrupts for the 128. The fast serial flag (2588/$0A1C) and RS-232 activity flag ($0A0F) are cleared to zero. Timer B of CIA #1 is loaded with 65535/$FFFF and started counting continuously. Finally, a fast serial mode setup sequence is performed.

57820 $E1DC

Initializes 80-column video chip registers

Retrieves a byte from the position in the table at 58104/$E2F8 specified in the X register. The value is used as a register number and the next value in the table is written to that 8563 register. Once called, the routine repeatedly reads register numbers and initializes registers until a value greater than 127/$7F is read for the register number. Normal X register values upon entry are 0/$00 (to initialize the 8563), 59/$3B (to adjust horizontal scrolling for different versions of the 8563 chip), or 62/$3E (to adjust register settings for a PAL video system).

57840 $E1F0

Initializes or jumps through the soft reset vector

Examines the contents of locations 65525-65527/$FFF5-$FFF7 in bank 1 to determine whether those locations contain the character codes for the letters CBM. If not, a branch is taken to the following routine to initialize the test pattern and vector. If the pattern is found, the reset vector has already been initialized, so the address in the vector at 65528-65529/$FFF8-$FFF9 in bank 1 is loaded into locations 2-3/$02-$03. The routine then takes an indirect jump to the specified address. (The system will still be configured for bank 15, so the target routine must in that bank.)

57892 $E224

Initializes the soft reset vector

Loads the system soft reset vector, locations 65528-65529/ $FFF8-$FFF9 in bank 1, with the value $E224, the address of this routine. Next, the character codes for the letters CBM are copied to locations 65525-65527/$FFF5-$FFF7 in bank 1 to indicate that the vector has been initialized.

57922 $E242

Checks for the presence of 64 cartridges or 128 function ROMs

Tests the GAME and EXROM lines from the memory expansion port (reflected by bits 4 and 5 of the MMU mode configuration register at 54533/$D505). If either of the lines is grounded, the routine falls through to enter 64 mode. Otherwise, a branch is taken to the routine at 57963/$E26B to check for 128 function ROMs.

57931 $E24B C64-MODE

Switches the system into 64 mode

This routine has a Kernal jump table entry at $FF4D

Loads the processor data direction and I/O port registers (locations 0-1/$00-$01) with their standard Commodore 64 settings, copies the reset routine from 57955-57962/ $E263-$E26A to 2-9/$02-$09, stores a zero in the clock rate register (53296/$D030) to insure that the system is in slow (1 MHz) mode, then jumps to the reset-to-64 routine at 2/$02. The short routine copied there stores the value 247/$F7 in the MMU mode configuration register. Compared to the default setting, that value clears bit 3 (prohibiting fast serial output) and sets bit 6 (making 64 ROM visible while making 128 ROM and the MMU chip registers invisible). The routine then initializes 64 mode by jumping through the hardware reset vector (65532/$FFFC) in the now-visible Commodore 64 ROM.

57963 $E26B

Logs 128 function ROMs

Clears the function ROM ID table (2753-2756/$0AC1-$0AC4), then checks the seventh through ninth bytes beyond the starting address in each of the four possible memory slots for function ROM: 32768/$8000 and 49152/$C000 in bank 8 for external (cartridge) ROM, and 32768/$8000 and 49152/$C000 in bank 4 for internal ROM (in the free socket on the 128’s main circuit board). If the bytes are the character codes for the letters CBM, a valid function ROM is present in the slot, and the sixth byte beyond the starting address (the cartridge ID) is retrieved and stored in the ID table. If the ID value is I, indicating an autostarting ROM, the starting address of the ROM is loaded into the JSRFAR pointer (3-4/$03-$04), and JSRFAR [$02CD] is used to call the ROM’s cold start routine. (It is possible that the auto starting ROM will retain control of the system and not return.) ROMs that don’t autostart are initialized during the PHOENIX routine $F867.

58052 $E2C4

Initialization test pattern

These three bytes are the character codes for the letters CBM, used as a test pattern in various operations: testing whether the soft reset vector has been initialized $E1F0, checking for 128 function ROM $E26B, and checking for boot disks $F890.

58055 $E2C7

Table of default VIC chip register values

The 49 values in this table are copied to the VIC-II 40-column video chip registers during the IOINIT routine $E109 to establish default register settings.

58104 $E2F8

Table of default 8563 chip register values

The first value in each two-byte table entry is the number of the 8563 80-column video chip register into which the second value is to be copied. The register settings are initialized by the routine at 57820/$E1DC.

58171 $E33B TALK

Sends TALK command to a serial device

This routine has a Kernal jump table entry at $FFB4 Sets bit 6 of the device number value in the accumulator to %1 (the serial bus TALK command has the format %010nnnnn, where %nnnnn is the number of the device being commanded to talk). A BIT opcode is used to fall through into the next routine to send the byte as a serial bus command.

58174 $E33E LISTEN

Sends LISTEN command to a serial device

This routine has a Kernal jump table entry at $FFB1

Sets bit 5 of the device number value in the accumulator to %1 (the serial bus LISTEN command has the format %001nnnnn, where %nnnnn is the number of the device being commanded to listen). RS-232 activity is disabled. If a serial byte is awaiting transmission in the buffer at 149/$95, it is sent with an EOI (end-or-identify) handshake; then the command byte is placed in the buffer.

The subroutine at $E573 is used to disable IRQ interrupts and standardize timing. Then the serial bus DATA line is allowed to go high. If the serial bus ATN line is not currently low, the routine attempts to establish fast serial mode. It does this by setting the serial port for fast serial output and sending out the value 255/$FF (eight %1 bits) using the fast serial hardware. The port is then set for fast serial input mode, and a short delay loop is executed. If a serial device capable of fast serial communications is present, it should respond by sending back a byte. This will cause a serial register interrupt on CIA #1, which will be detected later to determine that fast serial mode is available.

Sending a Serial Command Byte

  1. 128 pulls ATN line low.
  2. 128 pulls CLK line low and allows the DATA line to go high.
  3. The external device must respond by holding the DATA line low. If the DATA line is still high after approximately one millisecond, it is assumed that the device is not present.
  4. If the external device responded, the 128 allows the CLK line to go high.
  5. The external device must now allow DATA to go high again. (The 128 will wait indefinitely for this to happen.)
  6. Once DATA goes high, the 128 responds by pulling the CLK line low.
  7. The command byte is then sent one bit at a time, starting with the least significant bit (bit 0). Command bytes are always sent in slow serial mode. To send a bit, the DATA line is set either low or high, depending on whether the bit being sent is %0 or % 1. Then the CLK is allowed to go high briefly to signal that a valid bit can be read on the DATA line.
  8. After the last data bit is sent, the 128 checks the DATA line. The external device must pull that line low within approximately one millisecond or a write-timeout error will be indicated.
  9. The status of the ATN line after a command is sent depends on the command. If it is TALK or LISTEN, ATN remains high so that the secondary address can be sent as a command as well. However, ATN is immediately pulled low following UNTALK and UNLISTEN commands.

Next, the serial bus ATN line is pulled low to indicate that the following byte will be a command. The serial bus CLK line is pulled low, and the DATA line is allowed to go high. A delay loop of approximately one millisecond is executed, and the routine falls through into the following routine to send the command byte.

Note that there is no corresponding routine for the 128 to receive a serial bus command. The 128 has no ATN input line, so it cannot be commanded to listen (it only listens “voluntarily”). It must always be the only master device on the serial bus.

58252 $E38C

Sends buffered byte to a serial device

Begins by calling the subroutine at 58739/$E573 to disable IRQ interrupts and standardize timing. If the system is set for the fast (2 MHz) clock mode, it will be temporarily reset to the normal (1 MHz) mode, which explains why serial communications are not significantly affected by the clock mode.

The routine makes sure that the serial bus DATA line is free to go high, then tests the state of the line. If the external device is not holding DATA low, the device is considered not present, so bit 7 of the serial status flag (144/$90) will be set to %1, the ATN and CLK lines will be allowed to go high, and the routine will exit. Otherwise, the routine will allow the CLK line to go high. If bit 7 of the EOI flag (163/$A3) is set to %1, the routine performs the EOI (end-or-identify) handshake by waiting for the external device to set the DATA line high and then low. Next, the routine waits for the external device to release the DATA line to a high state. While it’s waiting, the routine checks whether a CIA #1 serial register interrupt has occurred, indicating that a byte has been received via the fast serial hardware. If so, the external device can accept fast serial input, and the fast serial flag (2588/$OA1C) is set to 192/$C0 to indicate this.

Once the DATA line goes high, the routine immediately pulls the CLK line low and proceeds to send the byte of data. If fast serial mode is available, the process is simple: The serial port is set for fast serial output, and the buffered byte from 149/$95 is stored in the CIA #1 serial data register ($DC0C). After that, the transfer is automatic, handled by the CIA chip hardware. The routine simply waits until a serial register interrupt indicates that the byte has been completely sent, then resets the port for fast serial input (its default state).

The process for sending a byte in standard (slow) serial mode is more complicated because it is handled in software. Bits are pulled from the buffered byte (149/$95), one at a time, starting with the least significant bit (bit 0). For each bit, the serial bus DATA output line is set either high or low, depending on whether the bit to be sent is %0 or %1. Then the CLK line is allowed to go high to signal to the external device that a valid data bit can be read from the DATA line. After a brief delay, the CLK line is pulled low, and the DATA line is allowed to go high again. If the external device holds the DATA line low between bits, a write-timeout occurs (the routine sets bits 0 and 1 in the serial status flag to % 1, allows the ATN and CLK lines to go high, and exits).

Sending a Serial Data Byte

  1. The 128 allows the DATA line to go high. If the external device does not continue to hold the line low, it is assumed that the device is not present.
  2. The 128 allows the eLK line to go high.
  3. The external device must now allow DATA to go high. (The 128 will wait indefinitely for this to happen.)
  4. Once DATA goes high, the 128 responds by pulling the eLK line low.
  5. The data byte is sent one bit at a time using either fast or slow serial mode. To send a bit in slow serial mode (illustrated above), the DATA line is set either low or high, depending on whether the bit being sent is %0 or % 1. Then the eLK line is allowed to go high briefly to signal that a valid bit can be read on the DATA line. Fast serial mode works in a similar manner, except the transfer is managed by CIA chip hardware rather than ROM software, and the bits are clocked by pulses on the SRQ line rather than on the eLK line.
  6. After all of the byte is sent, the 128 checks the DATA line. The external device must pull that line low within approximately one millisecond, or a write-timeout error will be indicated.

After the byte has been sent (in either mode), the routine waits for the external device to pull the DATA line low. If this does not happen within approximately one millisecond, a write-timeout occurs, and the routine sets bits 0 and 1 in the serial status flag to %1, allows the ATN and CLK lines to go high, and exits. Otherwise, the routine at $E59F is used to restore interrupts and the clock mode setting, then exits with the status register carry bit clear. The Y register is unused during the routine, and the X register value is preserved.

Serial EOI Handshake

  1. As in a normal byte transfer, the external device must hold the DATA line low or it will be assumed that the device is not present.
  2. The 128 allows the eLK line to go high.
  3. The external device should then allow the DATA line to go high. (The 128 will wait indefinitely for this to happen.)
  4. When the 128 does not respond by pulling the eLK line low within 200 microseconds, the external device should recognize that an EOI handshake is being performed and should respond by pulling DATA low again. (The 128 will wait indefinitely for DATA to go low.)
  5. The external device should then release DATA high again. The 128 will respond by pulling eLK low, and the final byte of the file is then sent in the usual manner. (See steps 5-6 of Sending a Serial Data Byte)

58430 $E43E ACPTR

Reads a byte from a serial device

This routine has a Kernal jump table entry at $FFA5 Begins by calling the subroutine at 58739/$E573 to disable IRQ interrupts and standardize timing. If the system is set for the fast (2 MHz) clock mode, it will be temporarily reset to the normal (1 MHz) mode, which explains why serial communications are not significantly affected by the clock mode. The routine insures that the serial bus CLK line is free to go high, then tests the state of the line and waits in a loop until it goes high. Next, a delay counter is initialized, and the DATA line is allowed to go high. If the external device responds by pulling the CLK line low before the delay count expires, the routine begins to process the incoming data bits. Otherwise, it assumes that the external device is requesting an EOI (end-or-identify) handshake. So the EOI bit (bit 6) of the serial status flag (144/$90) is set to %1, and the DATA line is pulled low, then allowed to go high again. The external device must acknowledge the EOI handshake by pulling the CLK line low before another delay period expires. If it doesn’t, a read-timeout occurs, and the routine sets bit 1 in the serial status flag to %1, allows the ATN and CLK lines to go high, and exits.

If a serial register interrupt has occurred on CIA #1, a byte has been received via the fast serial hardware. In this case, the byte is retrieved from the serial data register (56332/$DC0C) and stored in 164/$A4; then the fast serial flag (2588/$0A1C) is reset to 192/$C0.

The process for receiving a byte in standard (slow) serial mode is more complicated because it is handled in software. The routine waits for the data line to go high and then for the CLK line to go low. Then, when CLK goes high again, a bit is read from the DATA line and shifted into the working byte (164/$A4). The routine waits until CLK goes low before attempting to read the next bit, and the process is repeated for each of the eight bits of the byte.

After a byte has been received, the DATA line is pulled low to mark the end of the frame. If the EOI handshake has been performed, the CLK line is allowed to go high. The routine at $E59F is used to restore interrupts and the clock mode setting. The routine exits with the status register carry bit clear and with the received byte in the accumulator. The Y register is unused during the routine, and the X register value is preserved.

Receiving a Serial Data Byte

  1. The 128 allows the CLK line to go high and waits for it to go high if the external device is holding it low.
  2. Once the CLK line goes high, the 128 allows the DATA line to go high.
  3. The 128 waits for the external device to respond by pulling the CLK line low. If this hasn’t happened after a delay of approximately 260 microseconds, the 128 assumes that an EOI handshake is requested and briefly pulls the DATA line low. The external device must respond by pulling CLK low before another 260- microsecond delay expires; otherwise, a read-timeout error will occur.
  4. Once the CLK line goes low, the 128 prepares to read data. To receive a bit in slow serial mode (illustrated above), the 128 waits until the CLK line goes high, reads the CIA port bit connected to the DATA input line, then waits for the CLK line to go low again. Fast serial mode works in a similar manner, except the transfer is managed by CIA chip hardware rather than ROM software, and the bits are clocked by pulses on the SRQ line rather than the CLK line.
  5. After the byte is received, the 128 pulls the DATA line low to indicate the end of the byte frame.
  6. If the EOI handshake is performed, the CLK line is allowed to go high after the byte is received.

58578 $E4D2 SECOND

Sends secondary address after LISTEN

This routine has a Kernal jump table entry at $FF93

Stores the secondary address value from the accumulator into the serial byte buffer at 149/$95, sends the buffered byte as a command (the ATN line should still be high from the previous LISTEN command), then falls through into the next routine to allow the ATN line to go high again so that following bytes will be seen as data instead of commands.

58583 $E4D7

Allows the serial bus ATN output line to go high

Forces bit 3 of CIA #1 port A to %0. Since that bit is connected to the serial bus ATN output line via an inverter, this will set the ATN output line to a high state (+5 volts).

58592 $E4E0 TKSA

Sends secondary address after TALK

This routine has a Kernal jump table entry at $FF96

Stores the secondary address value from the accumulator into the serial byte buffer at 149/$95, then sends the buffered byte as a command (the ATN line should still be high from the previous TALK command). If the device is not present, the routine exits (after allowing the ATN and CLK lines to go high again). Otherwise, the routine falls through into the next one to make the 128 the listener and recognize the external device as the talker.

58601 $E4E9

Performs talk-listen turnaround

Begins by calling the subroutine at 58739/$E573 to disable IRQ interrupts and standardize timing. The DATA line is held low, and the ATN line is allowed to go high (signaling the end of the command). The CLK line is then allowed to go high. The device which is commanded to talk should respond by pulling CLK low. The routine waits for this to happen, then jumps to the routine at 58783/$E59F to restore interrupts and the clock mode.

Talk-Listen Turnaround

  1. The 128 holds the DATA line low and allows the ATN line to go high.
  2. The 128 allows the eLK line to go high.
  3. The external device must respond by pulling eLK low. (The 128 will wait indefinitely for this to happen.) The external device is now the talker and the 128 is the listener.

58627 $E503 CIOUT

Sends a byte to a serial device

This routine has a Kernal jump table entry at $FFA8

Tests bit 7 of the serial buffer flag (148/$94). If the bit is %0, indicating that the one-byte serial buffer (149/$95) is empty, the byte in the accumulator is simply stored in the buffer and the flag bit is set to % 1 before exiting. However, if the flag bit is already %1, a character is currently waiting in the buffer. In this case, the routine at 58252/$E38C is used to send the previously buffered character before the new character is added to the buffer. Carry will always be clear upon exit, and the byte value will still be in the accumulator. The X and Y register values are also preserved. The success of the operation can be determined from the value in the serial status flag (144/$90). The purpose of the buffering scheme is to make it possible to perform the EOI (end-or-identify) handshake with the final character of a file.

58645 $E515 UNTLK

Sends UNTALK command to a serial device

This routine has a Kernal jump table entry at $FFAB

Begins by calling the subroutine at $E573 to disable IRQ interrupts and standardize timing. The CLK line is pulled low; then ATN is pulled low as well to indicate that the byte will be a command. The accumulator is loaded with the UNTALK command value, 95/$5F. Then a BIT opcode is used to fall through into the next routine to send the byte as a command. Unlike TALK, which affects only a specified serial device, UNTALK affects all serial devices. However, this shouldn’t cause problems because the 128 serial bus allows only one active talker at any given time.

58662 $E526 UNLSN

Sends UNLISTEN command to a serial device

This routine has a Kernal jump table entry at $FFAE

Loads the accumulator with the UNLISTEN command value, 63/$3F, then clears bit 7 of the serial mode flag ($0A1C) to disable fast serial mode, and sends the byte in the accumulator as a command on the serial bus. Afterward, the ATN line is allowed to go high (signaling the end of the command), and, after a short delay, the CLK and DATA lines are allowed to go high as well. Unlike LISTEN, which affects only a specified serial device, UNLISTEN affects all serial devices. However, this shouldn’t cause problems because the 128 serial bus normally has only one listener at any given time.

58693 $E545

Allows serial bus CLK output line to go high

Sets bit 4 of CIA #2 port A to %0. Since the output line for that port bit is connected to the serial bus CLK output line via an inverter, this will set the output line to a high state (+5 volts).

58702 $E54E

Pulls serial bus CLK output line low

Sets bit 4 of CIA #2 port A to %1. Since the output line for that port bit is connected to the serial bus CLK output line via an inverter, this will set the output line to a low state (0 volts).

58711 $E557

Allows serial bus DATA output line to go high

Sets bit 5 of CIA #2 port A to %0. Since the output line for that port bit is connected to the serial bus DATA output line via an inverter, this will set the output line to a high state (+5 volts).

58720 $E560

Pulls serial bus DATA output line low

Sets bit 5 of CIA #2 port A to %1. Since the output line for that port bit is connected to the serial bus DATA output line via an inverter, this will set the output line to a low state (0 volts).

58729 $E569

Reads the serial bus DATA and eLK input lines

Reads the value at port A of CIA #2, then shifts the value of bit 7, connected to the serial bus DATA input line, into the status register carry bit. This will also shift the value of bit 6, connected to the CLK input line, into bit 7. Upon return, the DATA bit can be tested with BCC/BCS or shifted into a working byte, and the CLK bit can be tested with BPL/BMI.

58739 $E573

Disables IRQ interrupts and standardizes timing during I/O operations

Begins by disabling IRQ interrupts. Next (at $E574), the custom mode flag (2618/$0A3A) is checked. If bit 7 of this flag is set to %1, the routine exits without changing the clock mode setting or disabling sprites. The 128 sets this flag to zero during the 10INIT routine (part of both the reset and RUN/STOP-RESTORE sequences) and does not normally change the value. You can set bit 7 of the flag if for some reason you want to retain fast clock mode or sprites during an I/O operation. The clock mode storage flag (2615/$0A37) is then checked. The routine exits if bit 7 of this flag is set to %1, indicating that a clock mode has already been stored (bits 2-7 of $D030 are always %1). Otherwise, the value in the VIC-II register at 53296/$D030, which determines the system clock frequency, is stored in the clock mode flag, and the value in the register at 53269/$D015, which determines which sprites are currently enabled, is stored in 2616/$0A38. Both registers are then reset to zero, which disables all sprites and sets the system to normal (1 MHz) clock speed. This is done to standardize system timing. Tape and serial data transfers rely on software delay loops for critical timing functions, so the system must be in a standard mode for the loops to provide the correct delay. If any sprites have previously been enabled, a delay loop is executed before the routine exits.

58783 $E59F

Reenables interrupts and restores clock mode after I/O operations

Begins by checking the custom mode flag (2618/$0A3A). If bit 7 of this flag is set to %1, the routine skips ahead to reenable interrupts and exit. The 128 sets this flag to zero during the IOINIT routine (part of both the reset and RUN/STOPRESTORE sequences) and does not normally change the value. You can set bit 7 of the flag if for some reason you want the clock mode or sprite enable setting preserved after an I/O operation. The clock mode storage flag (2615/$0A37) is then checked. If bit 7 of this flag is set to %0, indicating that no clock mode is stored, the routine skips ahead to reenable interrupts and exit. Otherwise, the value in 2616/$0A38 is restored to the VIC-II sprite enable register (53269/$D015), and the value in 2615/$0A37 is restored to the clock mode register (53296/$D030). The clock mode flag is then reset to zero. IRQ interrupts are reenabled before exiting.

58812 $E5BC

Performs fast serial turnaround

Waits for a serial register interrupt on CIA #1, indicating that the current output byte has been completely sent, then falls through into the next routine to reset the serial port lines for fast serial input. The port is left set up for input so that incoming fast serial communications can be detected automatically.

58819 $E5C3 SPIN

Sets serial device for fast serial input

Sets the serial line from CIA #1 for input and halts timer A on CIA #1; then clears bit 3 of the MMU register at 54533/$D505 to set the serial port’s fast communications hardware for input.

58838 $E5D6 SPOUT

Sets serial device for fast serial output

Sets bit 3 of the MMU register at 54533/$D505 to set the serial port’s fast communications hardware for output; then clears all CIA #1 interrupts. Timer A is loaded with 4/$0004, the timing constant for fast serial output bits. The serial line for CIA #1 is set for output, and timer A is started. Bytes subsequently stored in the serial data register of CIA #1 will be sent via the fast serial hardware.

58875 $E5FB SPIN_SPOUT

Sets serial device for fast serial input or output

Branches to one of the fast serial setup routines, depending on the setting of the status register carry bit. If carry is clear, the routine at $E5C3 is used to set up the port for fast serial input. If carry is set, the routine at $E5D6 is used to set up the port for fast serial output.

58879 $E5FF

Prepares next bit for RS-232 transmission.

(Called by the NMI handling routine when a timer A interrupt occurs.) If the count of bits remaining to be sent is zero, indicating that all stop bits for the current byte have been sent, a branch is taken to 58954/$E64A to prepare the next byte for transmission. If bit 7 of the count is set, a branch is taken to 58948/$E644 to prepare to send a stop bit. Otherwise, the next bit to be sent is pulled from the data byte storage (182/$B6) into the carry bit, and the parity flag (189/$BD) is updated accordingly. The count of remaining bits is decremented. If the result is zero, a branch is taken to 58907/$E61B to prepare parity or stop bits. Finally, bit 2 of 181/$B5 is set to the bit value to be sent.

58907 $E61B

Prepares parity and stop bits

Checks bit 5 of the RS-232 command register (2577/$0A11) to determine whether a parity bit is to be sent. If not (if the bit is %0), the routine skips ahead to determine the number of stop bits. Otherwise, a parity bit is prepared. Bits 6 and 7 of the command register determine the parity type. These are possible types:

Bits 7-6 Parity type
0 0 Odd
0 1 Even
1 0 Mark
1 1 Space

If odd parity is specified, the parity flag (189/$BD) is tested. When the flag is nonzero, indicating that an odd number of %1 bits has already been sent in the current byte, the routine prepares a parity bit of %0. When the number of %1 bits already sent is even, a parity bit of %1 is prepared to make the total odd. If even parity is specified, a parity bit of %0 will be prepared when the parity flag indicates that the number of %1 bits already sent is even, and a parity bit of % 1 will be prepared when the number of %1 bits sent is odd so that the total number of %1 bits sent (including the parity bit) will always be even. Mark and space parity are simpler: In the former case, the parity bit is always %1; in the latter, it’s always %0. (Early versions of the Commodore 64 Kernal incorrectly computed even and odd parity. All types of parity are handled properly in the 128 Kernal and in the version of the Commodore 64 Kernal used for 64 mode in the 128.)

Next, the routine prepares for the transmission of either one or two stop bits, depending on the setting of bit 7 of the RS-232 control register (2576/$0A10). If one stop bit is specified (if the register bit is %0), the count of bits remaining to be sent (180/$B4) is decremented once (to 255/$FF). If two stop bits are sent (if the register bit is %1), the count is decremented twice (to 254/$FE). The routine ends by branching back into the previous routine to set the prepared parity or stop bit as the next bit to send.

58948 $E644

Prepares to send a stop bit

Increments the count of bits remaining to be sent (180/$B4), then prepares a %1 bit for transmission (stop bits are always %1).

58954 $E64A

Prepares to transmit next byte

Checks bit 0 of the RS-232 command register (2577/$0A11) to determine which handshaking mode is in use. If x-line handshaking is specified (if the register bit is % 1), the RS-232 DSR and CTS lines (pins K and L of the user port) are tested. If the external device is not holding these lines high, the corresponding bit in the RS-232 status flag (2580/$0A14) is set - bit 6 for DSR missing or bit 4 for CTS missing - then the routine disables timer A interrupts to halt transmission. For three-line handshaking, or for x-line handshaking when DSR and CTS are held high, the parity flag (189/$BD) is cleared and the current bit flag (181/$B5) is set to 0/$00 (start bits are always %0). The count of bits to send (180/$B4) is loaded from 2581/$0A15. If the RS-232 output buffer is empty, the routine disables timer A interrupts and exits. Otherwise, the next available byte from the output buffer is loaded into 182/$B6, and the pointer to the head of the buffer (2586/$0A1A) is incremented.

59007 $E67F

Sets CIA interrupt register and RS-232 activity flag

Sets or clears bits in the interrupt control register for CIA #2, depending on the value in the accumulator. If bit 7 of the accumulator value is %0, the interrupt register bits corresponding to the %1 bits in the accumulator value will be cleared. If bit 7 is %1, the interrupt register bits corresponding to any other %1 bits in the accumulator value will be set. The RS-232 activity flag (2575/$0A0F) is then updated to reflect the new setting of the interrupt register.

59022 $E68E

Computes bit count for the RS-232 operation.

Computes a bit count based on the setting of bits 5 and 6 of the RS-232 control register (2576/$0A10). The count value, which will be one greater than the number of data bits specified, will be returned in the X register.

Bits 6-5 Data bits Bit count
0 0 8 9
0 1 7 8
1 0 6 7
1 1 5 6

59037 $E69D

Processes received bits

Checks the start bit flag (169/$A9) to determine whether the start bit for a byte has been read yet. If not, a branch is taken to 59092/$E6D4 to see whether this is a start bit. Otherwise, the received bit count (168/$A8) is decremented. If the count has reached zero, all bits for a byte have been received, so a branch is taken to $E6DF to process the byte. Otherwise, the parity indicator is toggled, and the received bit in 167/$A7 is shifted into the work byte (170/$AA).

59058 $E6B2

Tests for stop bit

Decrements the received bit count and checks whether the received bit is a %1 (stop bits are always %1). If it is, bit 7 of the RS-232 control register (2576/$0A10) is tested. If one stop bit is specified (if the register bit is %0), the routine falls through into the next one to prepare to receive the next byte. If two stop bits have been specified, the routine exits to look for another stop bit.

If a stop bit has not been received, the routine branches to set a bit in the serial status flag (2580/$0A14) according to the previously received byte (170/$AA): bit I-the framing error bit-if the previously received byte is nonzero, or bit 7-the break error bit-if the previously received byte is zero (indicating that the received data line is being held low).

59074 $E6C2

Enables FLAG interrupts for CIA #1, then updates the RS-232

activity flag (2575/$0A0F) to indicate that FLAG interrupts are active. The nonzero flag value will also be stored in the start bit flag (169/$A9) to indicate that no start bit has been received. Timer B interrupts are then disabled, and the activity flag is updated to reflect this. (Timer B interrupts, used to time incoming bits, are reenabled after the FLAG interrupt occurs.)

59092 $E6D4

Tests for start bit

Tests the received bit (in 167/$A7). If it is not %0, it is not a start bit, so a branch is taken to 59074/$E6B2 to look for another byte. Otherwise, the zero value is stored in the start bit flag (169/$A9) to indicate that a start bit has been received, and the parity indicator flag (171/$AB) is initialized to 1/$01.

59103 $E6DF

Stores received character in buffer and checks parity

Checks whether space for an additional character is available in the input buffer. If not, the routine branches to set the receiver buffer overflow bit (bit 2) in the RS-232 status flag (2580/$0A14) and to prepare for the reception of the next byte. (The received character is lost in this case.) Otherwise, the received character is padded with %0 bits if it is less than eight bits long and then stored at the current tail of the input buffer. If no parity is used, a branch is taken to 59058/$E6B2 to check for a stop bit. Otherwise, the current bit received (167/$A7) is taken to be a parity bit and is compared against the calculated parity for the byte. If the two do not correspond to the specified parity type, the parity error bit (bit 0) of the status flag will be set, and the routine will jump to reset for the reception of the next byte.

59177 $E729

Handles CKOUT for RS-232 device

Sets the device number in the accumulator as the current output device (154/$9A), then tests bit 0 of the RS-232 command register (2577/$0A11). If the bit is %0, three-line handshaking has been specified, so the routine exits at this point. For x-line handshaking, the routine tests the DSR line (pin L of the user port). If the external device is not holding this line high, a branch is taken to set bit 6 of the RS-232 status flag (2580/ $0A14) and exit with the status register carry bit clear. If DSR is high, the state of the RTS line (pin D of the user port) is checked. If the 128 previously set this line high, the routine exits with carry clear. Otherwise, the routine waits until any current transmission is completed, then waits for the external device to pull the CTS line (pin K of the user port) low. The routine then sets the RTS line high to signal that it is ready to send a byte and waits until an external device sets the CTS line high to acknowledge that it is ready. (If DSR goes low while the routine is waiting, bit 6 of the status flag will be set). The routine then exits with carry clear.

59228 $E75C

Handles BSOUT for RS-232 device

Checks whether the output buffer is currently full. If no space is available, the routine loops to enable interrupts for RS-232 transmission and waits until space becomes available in the buffer. The buffer tail pointer (2587/$0A1B) is incremented, and the value in the accumulator is placed at the tail of the output buffer. If bit 0 of the RS-232 activity flag (2575/$0A0F) is set to %1, timer A interrupts are already enabled, so the routine exits at this point. Otherwise, timer A is loaded with the bit timing constant value in 2582-2583/$0A16-$0A17; then timer A interrupts are enabled, and the activity flag is updated to reflect this. The routine at 58954/$E64A is called to prepare to transmit the byte; then timer A is started.

59285 $E795

Handles CHKIN for RS-232 device

Sets the device number in the accumulator as the current input device (153/$99), then tests bit 0 of the RS-232 command register (2577/$0A11). If the bit is %0, three-line handshaking has been specified, so the routine skips ahead to test whether interrupts are enabled. For x-line handshaking, the routine tests bit 4 of the command register to determine the duplex mode in use. For full duplex (bit 4 is %0), the routine skips ahead to test whether interrupts are available. For half duplex, the RS-232 DSR line (pin L of the user port) is tested. If the external device is not holding this line high, a branch is taken to set bit 6 of the RS-232 status flag (2580/$0A14) and exit with the status register carry bit clear. If DSR is high, the state of the RTS line (pin 0 of the user port) is checked. If the 128 is currently holding this line high, the routine exits with carry clear, then pulls the CTS line (pin K of the user port) low. The routine then waits for DTR line to go high, after which FLAG interrupts are enabled to detect the start bit.

The final step of the routine is to test whether FLAG or timer B interrupts are enabled. If neither is enabled, FLAG interrupts are enabled. The routine exits with carry clear.

59342 $E7CE

Handles GETIN for RS-232 device

Checks whether any characters are available in the input buffer. If so, bit 3 of the RS-232 status flag (2580/$0A14) is cleared, the buffer head pointer (2585/$0A19) is incremented, and the character from the buffer is returned in the accumulator. If no characters are available, bit 3 of the status flag is set to %1, and the value 0/$00 is returned in the accumulator. The carry bit will be set in this case.

59372 $E7EC

Disables RS-232 activity during tape or serial bus operations

Exits immediately if the RS-232 activity flag (2575/$0A0F) contains the value 0/$00, indicating that no RS-232 operations are being used. Otherwise, the routine waits until the transmission or reception of the current byte is completed, then disables FLAG interrupts so that no further bytes can be received and clears the activity flag so that no more bytes will be sent. The contents of the accumulator are preserved during this routine.

59397 $E805

Handles NMI interrupts for RS-232

Controls the transmission and reception of data through the RS-232 port. Three CIA #2 interrupt sources are used in RS-232 communications. Timer A is used to establish the duration of bits being transmitted. The FLAG line, which triggers an interrupt when it detects a high-to-Iow transition, is used to initiate the reception of a byte when an incoming start bit is detected. Timer B is used to time the reception of subsequent bits.

The routine begins by comparing the CIA #2 interrupt register value at the time of the NMI interrupt (in the Y register when the routine is called by the main NMI handler at 65285/$FF05) against the value in the RS-232 activity flag (2575/$0A0F). If bit 0 is set in both, a valid timer A interrupt has occurred to indicate that it is time to send the next bit. So, the bit value (in bit 2 of 181/$B5) is written to bit 2 of CIA #2 port A, which is connected to the transmitted data line (pin M of the user port). RS-232 interrupts are then reenabled by writing the activity flag contents to the CIA interrupt register (56589/$DD0D). In addition, the routine checks whether bit 1 or 4 is set in both the interrupt register value and the activity flag, indicating that a valid FLAG or timer B interrupt occurred concurrently with a timer A interrupt (RS-232 devices must be capable of simultaneous transmission and reception). If neither has occurred, the routine skips ahead to prepare the next bit for transmission. For timer B interrupts, the subroutine at 59512/$E878 is called to read a bit. For FLAG interrupts, the subroutine at 59561/$EA81 is called to start reception of a byte. The subroutine at 58879/$E5FF is then called to prepare the next bit for transmission. CIA interrupt sources are again enabled before exiting.

If no timer A interrupt occurred, the routine checks whether bit 1 is set in both the interrupt register value and the activity flag, indicating that a valid timer B interrupt occurred. If so, the subroutine at 59512/$E878 is called to read a bit. Otherwise, a test is made of bit 4 in both the interrupt register value and the activity flag. If the bit is set in both, a valid FLAG interrupt has occurred, so the subroutine at 59561/ $E8A9 is called to start reception of a byte. In either case, the activity flag value is stored in the CIA interrupt register to reenable RS-232 interrupts before exiting.

59472 $E850

Table of baud rate timing constants for NTSC systems

The ten two-byte values in this table (in low-byte/high-byte order) are the CIA timer settings used to transmit and receive bits at the ten standard baud rates when the 128 is operating with NTSC clock frequency (1.02273 MHz). This is the formula for table values:

value = 1.02273E6 / (2 * baud rate) - 100

59492 $E864

Table of baud rate timing constants for PAL systems

The ten two-byte values in this table (in low-byte/high-byte order) are the CIA timer settings used to transmit and receive bits at the ten standard baud rates when the 128 is operating with PAL clock frequency (0.985265 MHz). This is the formula for table values:

value = 0.985265E6 / (2 * baud rate) - 100

59512 $E878

Reads a bit from RS-232 device

(Called by the NMI handling routine when a timer B interrupt occurs.) Reads the current status of the RS-232 received data line (pin C of the user port) and stores the bit value in 167/$A7. The interrupt time for the next bit is calculated and stored in the timer B latch (56582-56583/$DD06-$DD07); then timer B is restarted. CIA #2 interrupts are reestablished by storing the RS-232 activity flag (2575/$0A0F) in the interrupt register (56589/$DD0D), and the timer latch is reloaded with $FFFF. The routine ends by jumping to 59037/$E69D to process the received bit.

59561 $E8A9

Initiates reception of RS-232 byte

(Called by the NMI handling routine when a FLAG interrupt occurs.) Begins by copying the bit timing constant value (2578-2579/ $0A12-$0A13) into the latch for timer B (56582-56583/ $DD06-$DD07) and starting timer B. Then, FLAG interrupts are disabled and timer B interrupts are enabled. The RS-232 activity flag (2575/$0A0F) is updated to reflect the change. The latch for timer B is loaded with $FFFF so that the timer will count continuously after it counts down for the next bit. Finally, the count of bits to be received for the current character is loaded from 2581/$0A15 into the working counter (168/$A8).

59600 $E8D0

Reads next header block from tape

Calls the subroutine at 59890/$E9F2 to fill the cassette buffer with the next block from tape, exiting with the status register carry bit set if the RUN/STOP key is pressed while the block is being loaded.

The first byte in the header block, the type identifier, is then examined. If the identifier value is 5, this is an end-of-tape marker, so the routine branches to exit with carry set (in this case, the Y register will hold the value 255/$FF). If the identifier value is something other than 1, 3, or 4, the routine loops back to read another block. If Kernal messages are allowed, FOUND is displayed, followed by 16 filename characters from the buffer. (The buffer is filled with space characters when the header is written, so the displayed name will be padded with spaces if it is fewer than 16 characters long.) A delay loop lasting for two increments of the middle byte of the jiffy clock (161/$A1), about 8-1/2 seconds, is then started. If the space key is pressed during this delay, the routine loops back to the beginning to read another header. If any other key in the same column (for example, Commodore or CONTROL) is pressed, the loop is terminated. At the end of the loop, the routine exits with carry clear. The X register will hold the type identifier value for the header.

59673 $E919

Writes a header block to tape

Stores the type identifier value from the accumulator in 158/ $9E, then checks the address of the cassette buffer, exiting immediately if it is less than 512/$0200. Otherwise, the contents of the starting address pointer (193-194/$C1-$C2) and ending address pointer (174-175/$AE-$AF) for the current operation are stored on the stack, and the cassette buffer is filled with space characters (32/$20). The type identifier value is placed in the first byte of the buffer, and the starting address and ending address values are placed in the next four bytes (each in standard low-byte/high-byte order). The characters, if any, of the current filename are then copied into the buffer following the filename (the filename can fill the remainder of the buffer, up to 187 characters). The buffer starting and ending addresses are then set as the operation starting and ending addresses; the leader flag (171/$AB) is loaded with 105/$69 for a long interfile leader; and the subroutine at 59932/$EA1C is called to write the buffer contents to tape as a header. The original starting and ending address pointer values are then restored from the stack. Carry will be clear upon exit unless the RUN/STOP key is pressed while the header is being written.

59776 $E980

Loads and tests cassette buffer address

Loads the address value in the cassette buffer pointer (178-179/ $B2-$B3) into the X and Y registers; then compares the high byte (in the Y register) with the value 2/$02 to test whether the buffer address is greater than 511/$01FF. Upon exit, the status register carry and Z bits will reflect the result of the comparison.

59783 $E987

Sets buffer address as block address

Loads the address of the cassette buffer into the pointer to the starting address of the block to be read or written (193-194/ $C1-$C2) and the ending address of the buffer (192 bytes beyond the starting address) into the pointer to the end of the block (174-175/$AE-$AF).

59802 $E99A

Searches for a specified header

Calls the routine at 59600/$E8D0 to load the next header from tape into the cassette buffer; exits if carry is set upon return from that routine (indicating that the RUN/STOP key has been pressed or that an end-of-tape header has been read). Characters from the current filename (pointed to by 187-188/ $BB-$BC in the bank specified in 199/$C7) are compared against those in the buffer. If all characters match up to the end of the current filename, the names are considered matching, and the routine exits with carry clear, regardless of how many characters may remain untested in the buffer (there’s nothing to indicate the length of the name read from tape). However, if a mismatch is found, the routine loops back to search for another filename.

59838 $E9BE

Checks for cassette buffer filled or emptied

Calls the routine at 59776/$E980 to get the high byte of the cassette buffer address into the X register. The buffer index (166/$A6) is then incremented, loaded into the Y register, and compared against the value 192/$C0, the maximum count of characters in the buffer. Upon exit, the status register Z and carry bits will reflect the result of the comparison (both will be set if the end of the buffer is reached).

59848 $E9C8

equests PLAY button if necessary

Checks whether a tape button is currently pressed; exits with the status register carry and Z bits set if any buttons are already pressed. Otherwise, if Kernal messages are allowed, PRESS PLAY ON TAPE is displayed. The routine then waits for a tape button to be pressed. If the RUN/STOP key is pressed in the meantime, the routine exits with the status register carry bit set. When a button is pressed, the routine displays OK (if messages are allowed) and exits.

59871 $E9DF

Checks tape buttons

Tests the setting of bit 4 of the processor on-chip I/O port (1/$01), connected to the cassette button sense line. The status register Z bit will be clear if no button is pressed or will be set if any button is detected.

59881 $E9E9

Requests RECORD and PLAY buttons if necessary

Checks whether a tape button is currently pressed; exits with the status register carry and Z bits set if any buttons are already pressed. Otherwise, if Kernal messages are allowed, PRESS RECORD & PLAY ON TAPE is displayed. The routine then waits for a tape button to be pressed. If the RUN/STOP key is pressed in the meantime, the routine exits with the status register carry bit set. When a button is pressed, OK is displayed (if messages are allowed) and the routine exits.

59890 $E9F2

Reads next header or data block from tape

Clears the tape status flag (144/$90) and operation flag (147/$93) to zero; then sets the starting and ending addresses of the cassette buffer as the starting and ending addresses for the current operation and falls through into the next routine.

59899 $E9FB

Reads or verifies a block from tape

Calls the subroutine at 59848/$E9C8 to request that the PLAY button be pressed and exits (with carry set) if the RUN/STOP key is pressed during that subroutine. IRQ interrupts are disabled, and a series of variables and counters are initialized. The accumulator is loaded with 144/$90 (the value to enable FLAG interrupts), and the X register is loaded with 14/$0E (the offset for read interrupts), and the routine branches to the main tape I/O routine (59942/$EA26).

59925 $EA15

Writes a header or data block to tape

Calls the subroutine at 59783/$E987 to set the buffer addresses as the starting and ending addresses for the operation; then falls through into the next routine to write the buffer block to tape.

59928 $EA18

Writes a block to tape

Loads the leader flag (171/$AB) with 20/$14 to specify a short leader between the header and the block; then calls the subroutine at 59881/$E9E9 to request that the PLAY and RECORD buttons be pressed. (The routine will exit with carry set if the RUN/STOP key is pressed during the subroutine.) IRQ interrupts are disabled and the accumulator is loaded with 130/$82 (the value to enable timer B interrupts), and the X register is loaded with 8/$08 (the offset to write leader bits). The routine then falls through into the next one to perform the operation.

59942 $EA26

Initiates tape I/O operation.

Begins by disabling all VIC-II interrupt sources and clearing any.pending VIC-II interrupts. The value in the accumulator upon entry is loaded into the interrupt control register for CIA #1 (56333/$DC0D), and timer B is started. Tape operations depend on precise timing, so all other activities that affect system timing are disabled: RS-232 interrupts are disabled, the 40- column screen is blanked, and the subroutine at 58740/ $E574 is called to switch to standard (1 MHz) clock mode and disable sprites. The current address in the IIRQ vector at 788-789/ $0314-$0315 is preserved in 2569-2570/$0A09-$0A0A; then the subroutine at 61083/$EE9B is called to load a new value into IIRQ, according to the value in the X register. This new IRQ service routine will be responsible for reading or writing data to tape. The count of blocks to be read or written (190/ $BE) is initialized to 2 (blocks are always read or written in pairs). The subroutine at 60762/$ED5A is called to initialize variables; then the tape motor is started, the interlock location (192/$CO) is set to keep it on, and a delay loop is executed to allow the motor to get up to normal speed. IRQ interrupts are then enabled to begin the reading or writing process.

Since the actual tape operations are performed during IRQ interrupts, the routine must now wait in a loop for the operation to be completed. It continually tests the IRQ storage flag (2570/$0A0A), waiting for the IIRQ vector to be reloaded with the address stored there, which will happen after the IRQ-driven tape routines are finished. In the meantime, the RUN/ STOP key is also tested. If that key is pressed, the operation is halted and the routine exits with carry set. Otherwise, when the original IIRQ address is restored at the end of the operation, the vector storage flag will be reset to zero, and the routine will exit with carry clear.

60047 $EA8F

Checks for RUN/STOP keypress during tape operations

Calls the Kernal STOP routine [$FFE1] to determine whether the RUN/STOP key has been pressed. If it has been, the cassette motor is stopped, the default IIRQ vector address is restored, the return address of the calling routine is removed from the stack, and the IRQ vector storage flag (2570/$0A0A) is cleared.

60065 $EAA1

Sets timer A to check for FLAG interrupts

The reception of tape dipole is normally initiated by a FLAG interrupt from CIA #t which is triggered by the low-to-high transition on the cassette read line at the start of a dipole. Timer A of CIA #1 is loaded with a timing value for the type of dipole being read, and it’s used to check whether too much time elapses between FLAG interrupts (which should be equally spaced) in an attempt to determine whether any dipoles might have been missed. If no FLAG interrupt occurs before the timer counts down to zero, the timer will trigger an interrupt. This prevents the 128 from waiting indefinitely for a FLAG interrupt.

60139 $EAEB

Reads or verifies a block of data from tape

(This is a tape IRQ service routine.) Reads magnetic patterns (known as dipoles) from tape, assembles them into bytes, and loads the bytes into memory (or compares the bytes against memory) until a specified ending address is reached. The routine for reading from tape is the longest and most complex one in the Kernal, and will not be discussed in detail here. For a thorough description of the process, refer to COMPUTE’s VIC-20 and Commodore 64 Tool Kit: Kernal, by Dan Heeb.

Briefly, the routine reads dipoles from tape and determines whether they represent leaders, word markers, or data bytes. The routine does not demand that the dipoles have an absolutely exact duration, but rather it employs a concept known as an adjustable baseline to determine whether the dipole is within an acceptable range for a particular type. This makes it possible to compensate for minor variations in the motor speed of different Datassette units. Because two complete copies of the data block are recorded, error correction is possible. If an error is detected while a byte is being read from the first block, the address of the byte which could not be read is recorded in page 1. Up to 31 error addresses can be recorded in 256-317/$0100-$013D. When the second block is read, the bytes can be corrected if no error is encountered when the byte is read from that block.

When a byte is successfully read from tape, the handling of the byte depends on the value in the operation flag (147/ $93). If the flag value is zero, the byte is stored in the location pointed to by 172-173/$AC-$AD in the bank specified in 198/$C6. If the value is nonzero, the byte is compared against the contents of the location pointed to by 172-173/$AC-$AD in the bank specified in 198/$C6 (a verify operation).

Errors which cannot be corrected are recorded in the tape status flag (144/$90). If the end of a block is reached before the specified ending address (a short-block error), bit 2 of the flag is set to %1. If more than 31 errors occur while the first block is being read, or if an error recorded during the first block cannot be connected during the second block, an unrecoverable-read error occurs, and bit 4 of the flag is set to %1. That bit is also set during a verify operation if the byte in memory doesn’t match the corresponding byte in either block on tape. If the byte read from tape as a checksum doesn’t match the calculated checksum for the bytes previously read from tape, a checksum error occurs, and bit 5 of the flag is set to %1.

60753 $ED51

Loads working pointer with starting address

Transfers the starting address of the current block (193-194/ $C1-$C2) to the tape working pointer (172-173/$AC-$AD).

60762 $ED5A

Initializes tape variables between each byte

Resets the count of bits for the next byte (163/$A3) to 8; then clears the dipole flag (164/$A4), the word marker half-dipole flag (168/$A8), the parity work byte (155/$9B), and the word marker flag (169/$A9).

60777 $ED69

Initiates writing of a tape half-dipole

Reads the bit value for the current half-dipole to be sent (the current value of the rightmost bit in 189/$BD) and loads CIA #1 timer B with the appropriate value: $0060 for a short halfdipole if the bit is %0 or $OOBO for a long half-dipole if the bit is %1. The routine then clears the CIA interrupt register, starts timer B, and toggles the cassette write line (bit 3 of the 8502’s on-chip I/O port at location 1/$01) to begin writing the current half-dipole. Upon exit, the status register Z bit will be set if the line is currently low or will be clear if the line is currently high.

60816 $ED90

Writes a block of data to tape

(This is a tape IRQ service routine.) Writes bytes of data from memory to tape until the specified ending address is reached. The system used for representing the bytes on tape is rather complex. Each bit of a byte is represented by a magnetic pattern called a dipole, which is generated by holding the cassette write line (bit 3 of the 8502’s onchip I/O port at location 1/$01) high for a period, then low for a different period. The duration of the periods is determined by the value loaded into CIA #1 timer B, which controls the amount of time between IRQ interrupts for tape. The routine at 60777/$ED69 actually writes each half of the dipole. A %0 bit is represented by a short half-dipole followed by a long one, while a %1 bit is represented by a long halfdipole followed by a short one. Each byte is preceded by a word marker dipole, which consists of an extra-long word marker half-dipole followed by a long half-dipole. Each byte is followed by a parity bit dipole. The parity bit will be either %0 or %1, as necessary to provide an odd total number of %1 bits in the byte and parity bit combined. The routine writes two complete copies of the data block, separated by a short leader. As each byte is written, it is also exclusive-ORed with a checksum work byte. This checksum byte is written to tape following the second copy of the block to provide an additional error check. For a more thorough description of the process, refer to COMPUTE!’s VIC-20 and Commodore 64 Tool Kit: Kernal, by Dan Heeb.

60974 $EE2E

Writes a leader to tape and prepares to write a data block

(This is a tape IRQ service routine.) Writes leader dipoles to tape until the count specified in 171/$AB is decremented to zero, about 9.5 seconds for the leader before a header, 1.9 seconds for the leader between a header and the first data block, or 0.045 seconds between data blocks. The IIRQ vector is then loaded with the address of the routine to write the data block $ED90, but if both blocks have been written, the routine branches to 61077/$EE95 to restore normal IRQs and exit from the operation. Otherwise, the starting address for the block to be written is loaded into the working pointer (173-174/$AC-$AD), and a branch is taken into the block write routine to write the block countdown characters. Each block is preceded by a countdown character pattern-8 7 6 5 4 3 2 1 O-to mark the end of the leader and the beginning of data. (For the second block of a pair, bit 7 of the countdown character codes will be set to %1.)

61015 $EE57

Restores IRQ vector and operating modes after tape operation.

Reenables the screen (it is normally blanked during tape operations), then checks the custom mode flag (2618/$0A3A). If the flag has bit 7 set, indicating that a special operation mode has been specified, the routine skips ahead to turn off the cassette motor and restore the IRQ vector. Otherwise, the VIC-II chip sprite enable register is reloaded with the value stored in 2616/$0A38 at the start of the operation. The clock mode register is reloaded with its original value from 2615/$0A37; then the clock mode storage location is reset to zero to indicate that no value is stored. The routine to turn off the cassette motor $EEB0 is called; then raster interrupts are reenabled, and the IIRQ vector (788-789/$0314-$0315) is reloaded with its original address, stored in 2569-2570/$0A09-$0A0A at the beginning of the operation.

61077 $EE95

Ends tape write interrupts

Calls the subroutine at 61015/$EE57 to restore the IRQ interrupt vector address and system operating modes to their original values; then exits from the interrupt to return to normal processing.

61083 $EE9B

Loads IIRQ vector for tape operation

Loads the IIRQ vector (788-789/$0314/$0315) with a value from the table at 61096/$EEA8, depending on the offset specified in the X register:

Offset Vector address Function
8/$08 60974/$EE2E Write a leader to tape
10/$0A 60816/$E090 Write a block to tape
12/$0C 64101/$FA65 Restore normal IRQ functions
14/$0E 60139/$EAEB Read a block from tape

61104 $EEB0

Turns cassette motor off

Sets bit 5 of the processor I/O port (location 1/$01) to %1, which has the effect of turning off power to the cassette motor (pins 3 and C of the cassette port).

61111 $EEB7

Tests whether ending address has been reached

Compares the value in the working pointer for tape and serial operations (172-173/$AC-$AD) against the ending address in 174-175/$AE-$AF. Upon exit, the status register carry and Z bits will reflect the result of the comparison.

61121 $EEC1

Increments the working pointer

Increments the address in 172-173/$AC-$AD.

61128 $EEC8

Handles FLAG interrupts for tape

Insures that the B bit in the status register value on the stack is clear, then jumps to the IRQ interrupt handler $FF17.

61136 $EED0

Controls tape motor interlock

Checks bit 4 of the processor I/O port (location 1/$01) to determine whether any buttons are pressed on the Datassette. If no button is pressed (indicated when the bit is %1), the interlock byte (192/$C0) is cleared and the tape drive motor is turned off by setting bit 5 of the processor I/O port to %1. If a button is pressed (if bit 4 is %0), the interlock byte is checked. If that byte already contains a nonzero value, the routine exits. Otherwise, the tape motor is turned on by setting bit 5 of the processor I/O port to %0.

This routine is part of the normal IRQ sequence, so it’s not possible to control the tape motor by directly changing bit 5 of the processor I/O port. If you turn on the motor while no buttons are pressed, it will be turned off again during the next IRQ interrupt. If you turn off the motor while a button is pressed, it will be turned on again during the next IRQ interrupt (unless the interlock byte contains a nonzero value).

61163 $EEEB GETIN

Retrieves a byte from the current input device

(This routine is the normal target of the jump table entry at 65508/$FFE4, via the indirect vector at 810/$032A.) Checks whether the current input device (153/$99) is the keyboard (device 0) or RS-232 (device 2). If it is neither of these, the routine branches to 61205/$EF15 in the BASIN routine to accept a byte from the specified device. For keyboard and RS232, the significant difference between GETIN and BASIN is that GETIN returns the value 0/$00 if no character is available, whereas BASIN will wait until a valid character becomes available.

For keyboard input, the routine checks the number of characters available in the keyboard buffer (208/$D0) and the number of characters available from the current programmable key string (209/$D1). If both are zero (no characters available), the routine exits with 0/$00 in the accumulator and carry clear. However, if characters are available, the screen routine at 49158/$C006 is used to retrieve the next available character and return it in the accumulator. Neither the X nor the Y register values are preserved in this case.

For RS-232 input, the routine at 59342/$E7CE is used to retrieve a byte from the RS-232 input buffer and return it in he accumulator. If the buffer is empty, the accumulator will contain 0/$00, and bit 3 of the RS-232 status flag (2580/$0A14) will be set to %1. For RS-232 input, the X register is unused, and the Y register value is preserved.

61190 $EF06 BASIN

Accepts a byte from the current input device

(This routine is the normal target of the jump table entry at 65487/$FFCF, via the indirect vector at 804/$0324.) If the current input device (153/$99) is the keyboard (device 0), the routine sets the current cursor row and column (232/$E8 and 233/$E9) as the starting row and column for input (235/$EB and 236/$EC); then it calls the screen editor routine to retrieve a character from a keyboard input line [$C009]. For input from the screen (device 3), the routine stores the device number in the input source flag (214/$D6) and sets the current right window margin (231/$E7) as the ending column for input (234/$EA); then it calls the screen editor routine to retrieve a character from the screen line [$C009]. Unfortunately, this is not the proper setup procedure for screen input, so BASIN from the screen does not work in the current 128 Kernal. For screen input, the ending row for input (2608/$0A30) must be specified, and the setting of bit 7 of the input source flag must be preserved so that the screen editor routine will know when the end of the input line is reached. See the entry at 49819/$C29B for details of the proper procedure.

To retrieve a byte from a serial device (device number greater than 3), a branch is taken to the routine at 61276/ $EF5C. For RS-232 input (device 2), a branch is taken to the routine at 61287/$EF67. For tape (device 1), this routine falls through into the next routine.

61224 $EF28

Accepts a byte from tape

Retrieves a byte from the cassette buffer (2816-3007/ $0B00-$0BBF), then checks the next character in the buffer. If that character is the zero byte marking the end of the file, bit 6 of the tape status flag (144/$90) will be set to %1 to indicate that the end has been reached. If all characters have been retrieved from the buffer, the next block of data will be read. The retrieved byte will be in the accumulator upon exit and the X register contents will be preserved. Carry will be clear unless the RUN/STOP key has been pressed (the accumulator will contain 0/$00) or an error is encountered when reading the data block from tape (the accumulator will contain the error code).

61276 $EF5C

Accepts a byte from a serial device

Checks the serial status flag (144/$90) and returns the code for the RETURN character (13/$OD) if the status is nonzero (indicating error or end of file). Otherwise, the routine jumps to the Kernal ACPTR routine [$E43E] to retrieve a byte from a serial device.

61287 $EF67

Accepts a byte from RS-232

Calls the RS-232 GETIN routine at 61181/$EEFD to retrieve a character from the RS-232 input buffer. If the character value is nonzero, indicating that a valid character has been returned, the routine exits with that value in the accumulator. However, because of a branch to an incorrect address, the routine neglects to clear the carry bit, which will be set as a result of the comparison with zero. Thus, contrary to other versions of the Kernal, the carry bit will be set when the character code is valid. If the value returned is zero, the routine checks bit 6 of the RS-232 status flag (2580/$0A14) to determine whether the DSR signal is still present (indicating that the external device is still active). If the flag value indicates that the DSR signal is present, the routine loops back to see whether a character has arrived in the input buffer. If DSR is missing, the character code for RETURN, 13/$0D, is returned in the accumulator with the carry bit clear. In any case, the X register value is unused and the Y register value is preserved.

61305 $EF79 BSOUT

Sends a byte to the current output device

(This routine is the normal target of the jump table entry at 65490/$FFD2, via the indirect vector at 806/$0326.) If the current output device (154/$9A) is the screen (device 3), the routine jumps to the screen editor routine to display the character in the accumulator $C00C. If a serial device is specified (device number greater than 3), the routine jumps to the CIOUT routine [$E503] to send the byte in the accumulator over the serial bus. Otherwise, the value to be sent is stored in 158/$9E, and the X and Y register contents are placed on the stack for later restoration. For RS-232 (device 2), a branch is taken to 61367/$EFB7. The branch will also be taken if the keyboard (device 0) is specified as the output device, but that shouldn’t happen if you use normal Kernal calls to set the device number-CKOUT [$F14C] won’t accept the keyboard as an output device. For tape (device 1), this routine falls through into the following routine.

61332 $EF94

Sends a byte to tape

Checks whether the cassette buffer has been filled. If not, the byte stored in 158/$9E is placed in the next available position in the buffer. However, if the buffer is currently full, the current block must be written before the character can be placed in the buffer. The subroutine to write the block to tape [$EA15] is called; then the type identifier value for a data block (2/$02) is placed in the first byte of the buffer, and the buffer pointer (166/$A6) is initialized to the next buffer position. If the RUN/STOP key is pressed while the block is being written, the routine will exit with the status register carry bit set and with the accumulator containing the value 0/$00.

After the character is placed in the buffer, the X and Y register values will be restored from the stack, and the accumulator will be reloaded with the stored byte value so that all registers have the same value on exit that they did on entry. The carry bit will be clear upon exit.

61367 $EFB7

Sends a byte to the RS-232 port

Calls the routine at 59231/$E75F to store the character code from the accumulator into the RS-232 output buffer; then jumps into the preceding routine to restore the accumulator and X and Y register values from the stack and exits with carry clear.

61373 $EFBD OPEN

Opens a logical file to a specified device

(This routine is the normal target of the jump table entry at 65472/$FFC0, via the indirect vector at 794/$031A.) Checks whether the specified logical file number (184/$B8) is already used for a currently open file and exits with a Kernal file-open error if it is (the status register carry bit will be set, and the accumulator will hold the error code, 2/$02).

Next, the routine checks the number of files currently open (152/$98) and exits with a Kernal too-many-files error if ten files are already open (the carry bit will be set and the accumulator will hold the error code, 1/$01). Otherwise, the number of open files is incremented and the logical file number is added to the file number table (866/$0362). The current secondary address (185/$B9) is ORed with the value 96/$60 and placed in the corresponding position in the secondary address table (886/$0376). The OR step is significant; some Kemal routines expect bits 5 and 6 of the secondary address to be set. The current device number (186/$BA) is placed in the corresponding position in the device number table (876/$036C).

For files opened to the keyboard (device 0) or screen (device 3), the routine exits at this point, since no further setup steps are required for those devices. For RS-232 files (device 2), a branch is taken to the routine at 61504/$F040. For tape (device 1), a branch is taken to the routine at 61430/$EFF6. For a serial device (device number greater than 3), the subroutine at 61643/$FOCB is called; then the routine exits with carry clear (except in the case where the specified device is not present).

61430 $EFF6

Opens a file for input or output to tape

Checks the cassette buffer address and exits with a Kernal illegal-de vice-number error if it is less than 512/$0200. The lower four bits of the secondary address are then tested. If all are %0, the file has been opened for reading, so the routine searches for the specified tape header (or simply for the next header if no filename is specified). If the RUN/STOP key is pressed while the routine is searching for the header, the routine will exit with carry set and with the accumulator holding the value 0/$00. If an end-of-tape header is read before the data header is found, the routine exits with a Kemal file-notfound error (the carry bit will be set, and the accumulator will contain the error code, 4/$04). If a file header is found, the routine sets the buffer pointer (166/$A6) to 191/$BF (the value to indicate that the buffer is empty) so that the first block of data will be read when BASIN is called for the first time; then it exits with carry clear.

This routine does have a flaw: It neglects to check whether the loaded header block has a type identifier of 4 (the type identifier for data file headers). Thus, if a program file (type identifier of 1 or 3) is found with the specified name, or before the next data header if no name is specified, it will be opened. However, tape program files cannot be read or written, only saved or loaded, so it does no good to open them for reading. When a program file is opened, a long block error occurs after you try to read the first block.

If any of bits 0-3 of the secondary address are %1, a data file will be opened for writing. The subroutine to request that the PLAY and RECORD buttons be pressed is called. If the RUN/STOP key is pressed during that subroutine, the routine will exit with carry set and with the accumulator containing 0/$00. The subroutine at 59673/$E919 is then called to write a data header for the file (type identifier of 4). After the header is written, the data block type identifier value (2/$02) is placed in the first position of the cassette buffer; then the buffer pointer is initialized to the next position, and the routine exits with carry clear.

61504 $F040

Opens a file for RS-232 communications

Calls the subroutine at 61616/$F0B0 to initialize the CIA #2 port lines to the user port; then clears the RS-232 status flag (2580/$0A14) and retrieves up to four characters from the current filename (if more are specified, those after the first four are ignored). The first character, if present, is placed in the RS232 control register (2576/$0A10), the second into the command register (2577/$0A11), and the last two into the baud rate timing constant (2578-2579/$0A12-$0A13). The number of bits to be sent or received is calculated and stored in 2581/$0A15. The lower four bits of the control register value are checked to determine the baud rate to use. If the bits are all %0, a custom rate is indicated, so the following step to load a value from the tables is skipped. Otherwise, the value from bits 0-3 is used as an index into one of the baud rate timing constant tables, depending on the video system in use (59470/$E84E for NTSC or 59490/$E862 for PAL), and the specified table entry is loaded into 2578-2579/$0A12-$0AI3. No error checking is done, so if you specify a value greater than 10 (%1010) in those bits, you’ll wind up with an invalid timing constant and won’t be able to send or receive data. Also, for higher baud rates, the time between interrupts is so short that the system may not have time to process received bytes. In general, stick to rates of 1200 baud or lower.

The rate factor value in 2578-2579/$0A12-$0A13 is then converted into a bit timing constant, the number of CIA timer counts for the bit duration, and the result is stored in 2582-2583/$0A16-$0A17. The formula is bit time equals rate factor times 2 plus 200.

Next, the routine checks the handshaking setting specified by bit 0 of the command register. For x-line handshaking, the routine checks the CIA port bit connected to the DSR line (pin L of the user port). If the external RS-232 device is not holding this line high, bit 6 of the RS-232 status flag will be set. However, there’s a bug here: If the line is high, indicating that the external device is present, the routine will proceed with carry set, which is normally used to indicate a problem. (Carry clear upon return from OPEN is supposed to mean that the file has been opened successfully.)

If you’re calling OPEN from machine language, you can just remember that a set carry can be ignored when x-line handshaking is in use. However, BASIC still thinks a problem has occurred and will give a DEVICE NOT PRESENT error when the device is present. The converse side of the problem is that carry will be clear when DSR is low, so BASIC will think everything is okay when the device is absent. The final step is to set the input buffer tail pointer (2585/$0A19) equal to the head pointer (2584/$0A18), and the output buffer head pOinter (2586/$0A1A) equal to the tail pointer (2587/$0A1B). This effectively makes both buffers irtitially empty.

61616 $F0B0

Sets up CIA #2 ports for RS-232 communications

Disables all interrupt sources from CIA #2; then makes the user port lines connected to bits 0 and 3-7 of port B inputs, while making the lines for bits 1-2 outputs and setting them high (+5 volts). The line for bit 2 of port A (initialized to an output line during IOINIT) is set high. This is the transmitted data line, which is normally held high when no data is being sent. For information on the usage of the other lines, refer to the discussion of CIA #2 in Chapter 8. Finally, the RS-232 activity flag (2575/$0A0F) is cleared to indicate that no CIA #2 interrupts are enabled.

61643 $F0CB

Opens a file for serial bus communications

Checks the current secondary address (185/$B9) and exits immediately (with the status register carry bit clear) if the secondary address value has bit 7 set (if the value is greater than 127). The routine also exits without performing any other actions if the length of the name specified for the file being opened (183/$B7) is zero. Otherwise, the serial status flag (144/$90) is cleared to zero, and the specified device (186/$BA) is commanded to listen. If the serial status flag indicates that the device has not responded, the return address of the calling routine will be discarded and the routine will exit with a Kernal device-not-present error (the carry bit will be set and the accumulator will contain the error code, 5/$05). If the device has responded, the secondary address is sent as a command using the SECOND routine [$E4D2]. (The upper four bits of the secondary address are masked to %0000, so only the lower four bits are significant.) Again, if the device does not respond, the routine exits OPEN with a Kernal device-notpresent error. If a filename has been specified, the characters of the name are sent to the serial device. (Since the ATN line is allowed to go high after the secondary address is sent, the filename characters are not seen as commands.) The routine ends by commanding the device to unlisten.

61702 $F106 CHKIN

Sets the current input file for GETIN and BASIN

(This routine is the normal target of the jump table entry at 65478/$FFC6, via the indirect vector at 798/$031E.) Checks whether a file with the logical file number specified in the X register is currently open by verifying that a matching entry exists in the logical file table at 866-875/$0362-$036B. If no match is found, the routine exits with a Kemal file-notopen error (the status register carry bit will be set and the accumulator will hold the error code, 3/$03). If an entry for the file is found in the table, the file number is loaded into 184/$B8, and the corresponding device number and secondary address values are loaded into 186/$BA and 185/$B9, respectively. If the device number is 0 (keyboard) or 3 (screen), the routine skips ahead to set this as the input device number and exit, since that’s all that’s necessary for input from those sources. For device numbers greater than 3 (serial devices), a branch is taken to the routine at 61735/$F127. For device 2 (RS-232), a jump is taken to the routine at 59285/$E795 to prepare for RS-232 input. For device 1 (tape), the secondary address is tested. If it is not 96/$60 (0/$00 before the OR in the OPEN routine), the file has been opened for writing, so the routine exits with a Kernal not-input-file error (the status register carry bit will be set and the accumulator will hold the error code, 6/$06). Otherwise, the device number is set as the current input device (153/$99), and the routine exits with carry clear.

61735 $F127

Prepares a serial device file for input

Sends a TALK command to the serial device specified in the accumulator, then tests the serial status flag (144/$90) to see whether the device has responded. If the flag indicates that the device is not present, the routine exits with a Kernal device-not-present error (the status register carry bit will be set and the accumulator will contain the error code, 5/$05). If the device has responded, the routine checks the value of the secondary address (185/$B9). If bit 7 of the value is %1 (if the secondary address is 128 or greater), no secondary address is sent-the routine simply performs the talk-listen turnaround. Otherwise, the TKSA routine [$E4E0] is called to send the secondary address as a command on the serial bus and perform the talk-listen turnaround. If the device responds again, the routine sets the device number as the current output device (154/$9A) and exits with carry clear. If the device does not respond, the routine exits with a Kernal device-not-present error.

61772 $F14C CKOUT

Sets the current output file for BSOUT

(This routine is the normal target of the jump table entry at 65481/$FFC9, via the indirect vector at 800/$0320.) Checks whether a file with the logical file number specified in the X register is currently open by verifying that a matching entry exists in the logical file table at 866-875/$0362-$036B. If no match is found, the routine exits with a Kernal file-notopen error (the status register carry bit will be set and the accumulator will hold the error code, 3/$03). If an entry for the file is found in the table, the file number is loaded into 184/ $B8, and the corresponding device number and secondary address values are loaded into 186/$BA and 185/$B9, respectively. If the device number is 0, the routine exits with a Kernal not-output-file error (the status register carry bit will be set and the accumulator will hold the error code, 7/$07), since it’s not possible to send output to the keyboard. If the device number is 3, the routine skips ahead to set this as the output device number and exit, since that’s all that’s necessary to route output to the screen. For device numbers greater than 3 (serial devices), a branch is taken to the routine at 61805/ $F16D. For device 2 (RS-232), a jump is taken to the routine at 59177/$E729 to prepare for RS-232 output. For device 1 (tape), the secondary address is tested. If it is 96/$60 (0/$00 before the OR in the OPEN routine), the file has been opened for reading, so the routine exits with a Kernal not-output-file error. Otherwise, the device number is set as the current output device (1154/$9A), and the routine exits with carry clear.

61805 $F16D

Prepares a serial device file for output

Sends a LISTEN command to the device specified in the accumulator, then tests the serial status flag (144/$90) to see whether the device has responded. If the flag indicates that the device is not present, the routine exits with a Kernal device-not-present error (the status register carry bit will be set and the accumulator will contain the error code, 5/$05). If the device has responded, the routine checks the value of the secondary address (185/$B9). If bit 7 of the value is %1 (if the secondary address is 128 or greater), no secondary address is sent-the routine simply allows the serial bus ATN line to go high. Otherwise, the SECOND routine [$E4D2] is called to send the secondary address as a command on the serial bus. If the device responds again, the routine sets the device number as the current output device (154/$9A) and exits with carry clear. If the device does not respond, the routine exits with a Kernal device-not-present error.

61832 $F188 CLOSE

Closes a specified logical file

(This routine is the normal target of the jump table entry at 65475/$FFC3, via the indirect vector at 796/$031C.) Shifts the value of the status register carry bit upon entry into bit 7 of 146/$92; then checks whether a file with the logical file number specified in the accumulator is currently open. If not, the routine simply exits with carry clear. If an entry for the file is found in the file number table, the file number is loaded into 184/$B8, and the corresponding device number and secondary address values are loaded into 186/$BA and 185/$B9, respectively. The file’s position in the tables is placed on the stack for later retrieval. The routine then checks whether the file is open to the keyboard (device 0) or screen (device 3). In either of these cases, simply removing the table entries for the file is sufficient to close the file, so a branch is taken to the routine at 61924/$F1E4. For device numbers greater than 3 (serial devices), a branch is taken to the routine at 61903/$F1CF. For device 1 (tape), a branch is taken to the routine at 61865/$F1A9. For device 2 (RS-232), the table entries for the file are deleted, then a jump is taken to the routine at 61616/$FOBO to reinitialize the CIA ports and disable the interrupts that drive RS-232 communications.

61865 $F1A9

Closes a tape file

Checks the secondary address for the file (185/$B9). If it is 0, indicating that the file has been opened for reading, all that is required is to delete the table entries for the file, so a branch is taken to 61924/$FIE4. If the file has been opened for writing, the routine adds a zero following the last data byte in the buffer (which contains the final block of data for the file), then writes the final block to tape. If the RUN/STOP key is pressed while the last block is being written, the routine will exit with status register carry bit set and with the accumulator holding 0/$00. If bit 1 of the secondary address is set to %1, then an end-of-tape header will be written following the final data block. The routine then jumps to 61924/$F1E4 to remove the table entries for the file.

61903 $F1CF

Closes a file on a serial device

Checks the preserved status of the carry bit when the CLOSE routine is entered. If carry is set, and if the device number (186/$BA) is 8 or greater, and if the secondary address for the file is 15 (indicating that the file has been opened as a command channel to the drive), a branch is taken to 61924/$F1E4 to simply remove the file entries rather than actually closing the file. This provides a solution to a problem in previous versions of the Kernal. Closing the command channel to a drive also closes all other open files on the drive, possibly an unwanted side effect. By calling CLOSE with carry set, this can now be avoided.

If carry is clear when CLOSE is called, the normal closing steps are performed: The subroutine at 62878/$F59E is called to close the file on the serial device; then the routine falls through into the next one to remove the table entries for the file.

61924 $F1E4

Removes an entry from the logical file tables

Retrieves the offset to the file’s position in the logical file number, device number, and secondary address tables from the stack and decrements the number of open files (152/$98). Next, the routine checks whether the file to be deleted is the last entry in the tables. If so, decrementing the number of open files is sufficient to effectively remove the file’s table entries; and the routine exits at this point. Otherwise, the current last entry in the table is copied to the specified file’s position, overwriting the entries for the file to be deleted. Carry will always be clear upon exit.

61954 $F202

Checks whether a file is already open

Clears the serial status flag (144/$90), then searches the logical file number table (866-875/$0362-$036B) for an entry with the same number as the value specified in the X register. If no match is found, or if no files are open, the routine exits with the status register N bit set (test with BMI). If a file using the specified number is already open, the status register Z bit will be set (test with BEQ).

61970 $F212

Load parameters for a logical file

Loads the current logical file number (184/$B8), current device number (186/$BA), and secondary address (185/$B9) with the values for a specified open file from the tables at 866-895/$0362-$037F. The routine should be called with the X register containing the offset (0-9) into the tables for the file’s entry.

61986 $F222 CLALL

Clears file table entries

(This routine is the normal target of the jump table entry at 65511/$FFE7, via the indirect vector at 812/$032C.) Resets the number of open files (152/$98) to zero, then falls through into the next routine to reestablish default I/O channels. Note that this routine doesn’t actually close any files that may be open to external devices. Unclosed tape or disk files may cause problems and should be avoided. See the CLOSE-ALL entry at 62013/$F23D for a routine that properly closes all files opened to a specified serial device.

61990 $F226 CLRCH

Resets default I/O channels

(This routine is the normal target of the jump table entry at 65484/$FFCC, via the indirect vector at 802/$0322.) Sends an UNLISTEN command over the serial bus if the current output device number (154/$9A) is greater than 3, and an UN TALK command if the current input device number (153/ $99) is greater than 3. The channels are then reset to the default devices: 3 (screen) for output and 0 (keyboard) for input.

62013 $F23D CLOSE-ALL

Closes all open files for a specified serial device

(This routine has a jump table entry at 65354/$FF4A.) Stores the value in the accumulator as the current device number (186/$BA). If the specified device number is the current input or output device, the input or output channel is reset to the default device (keyboard or screen). Next, the routine searches the device number table at 876-885/$036C-$0375 for files that might be open to the specified device. Any that are found are closed by using the Kernal CLOSE routine 65475/$FFC3.

62053 $F265 LOAD

Loads or verifies a program file from disk or tape

(This routine has a jump table entry at 65493/$FFD5.) Stores the address value from the X and Y registers into the pointer to the starting address for the load (195-196/ $C3-$C4), then takes an indirect jump through the ILOAD vector (816/$0330). The vector normally points back to the location immediately following the jump, but you can modify the action of the LOAD routine by redirecting this vector to a routine of your own. See the ILOAD entry for details.

The operation type value in the accumulator is stored in the load/verify flag (147/$93), and the tape/serial status flag (144/$90) is cleared. The device number (186/$BA) is then tested. If it’s 4 or greater, a branch is taken to the routine at 62075/$F27B to attempt a load from the specified serial device. Otherwise, a jump is taken to the routine at 62246/ $F326 to attempt a load from tape.

62075 $F27B

Loads or verifies a file from a serial device

Begins by clearing bits 6 and 0 of the fast serial flag ($2588/$0A1C). Bit 6 is cleared so that the routine which attempts fast serial output will have to verify for itself that fast communications are available; there is no apparent reason for clearing bit O. The current secondary address (185/$B9) is placed in temporary storage (158/$9E). If the length of the current filename (183/$B7) is zero, the routine exits with the Kernal missing-filename error (the status register carry bit will be set and the accumulator will hold the error code, 8/$08). Otherwise, the length value is placed in temporary storage (159/$9F), and (if Kernal control messages are allowed) SEARCHING FOR, followed by the filename, is displayed. The subroutine at 62369/$F3A1 is called to attempt a fast serial load or verify of the specified file. If carry is clear upon return (indicating that a fast load or verify has been performed), the routine exits with carry clear and with the X and Y registers holding the ending address for the data. Otherwise, the routine proceeds with the standard serial load or verify.

The filename length is restored from temporary storage, and the current secondary address is set to 96/$60-corresponding to a secondary address of zero, the value for reading a file. The serial OPEN routine [$F0CB] is called to send the filename to the specified device; then the device is commanded to talk and is sent the secondary address. The first two bytes, which, for a program (PRG) file, contain the starting address, are loaded into the working pointer (174-175/ $AE-$AF). If the original secondary address value in 158/$9E is 0/$00, indicating that a relocating load has been specified, the starting address in the pointer is replaced with the one in 195-196/$C3-$C4. If Kernal messages are allowed, LOADING or VERIFYING is displayed.

The main reading step consists of clearing the read timeout bit (bit 1) of the serial status flag, checking for a RUN/STOP keypress, calling ACPTR [$E43E] to read a byte from the program file, and testing whether the read timeout bit has been set during the read. If it has been set, the routine loops back to try to read the byte again. If RUN/STOP is pressed, the routine goes to 62901/$F5B5 to halt the operation.

For a verify operation (nonzero value in 147/$93), the byte read from disk is compared to the one at the address pointed to by 174-175/$AE-$AF in the bank specified in 198/$C6. If the bytes do not match, the verify error bit (bit 4) in the serial status flag is set to %1. For a load operation (0 in 147/$93), the byte read from disk is stored at the address pointed to by 174-175 /$AE-$AF in the bank specified in 198/$C6. In either case, the address in the pointer is then incremented and compared against the value 65280/$FF00. If it reaches this value, a Kernal out-of-memory error occurs (the routine exits with carry set and with the accumulator holding the error code, 16/$10). If the EOI bit in the serial status flag (bit 6) is not %1, the routine loops back to read another byte. When EOI is set, indicating that the end of the file has been reached, the routine sends an UN TALK command, closes the file, loads the address from 174-175/$AE-$AF (which will point to the location immediately following the last byte loaded) into the X and Y registers, then exits with carry clear.

62246 $F326

Loads or verifies a program file from tape

Exits with the Kernal illegal-device-number error if the device number in the accumulator is not 1/$01, or if the cassette buffer address is less than $0200. (The status register carry bit will be set and the accumulator will hold the error code, 9/$09.) Otherwise, a request for the PLAY button will be printed if no buttons are currently pressed, and if Kernal messages are allowed. If the RUN/STOP key is pressed while waiting for a button press (or at any other time during the routine), the routine will exit with carry set and with 0/$00 in the accumulator. If Kernal messages are allowed, SEARCHING is displayed; if a nonzero filename length is specified, that will be followed by FOR and the filename. If a filename is supplied, the routine looks for a specified header block [$E99A]. Otherwise, it merely loads the next header block [$E8D0]. In either case, if an end-of-tape header (type identifier of 5) is encountered, the routine exits with a Kernal file-not-found error (the carry bit will be set and the accumulator will hold the error code, 4/$04). The routine also exits with carry set if bit 4 of the tape status flag (144/$90) is %1 after the header is loaded, indicating that a read error has occurred. If the type identifier byte for the header is not 1 or 3, this is not a program file, so the routine loops back to read another header.

If the type identifier is 3, indicating that this is a nonrelocatable program file, or if the secondary address (185/$B9) is nonzero, the current starting address pointer value in 195-196/$C3-$C4 is replaced with the one specified in the two header bytes following the type identifier. Next, the ending address for the file is calculated and stored in 174-175/$AE-$AF. If the ending address value is greater than 65279/$FEFF, the routine exits with a Kernal out-of-memory error (the carry bit will be set and the accumulator will hold the error code, 16/$10). The starting address is transferred to a working pointer (193-194/$C1-$C2) and, if Kernal messages are allowed, WADING or VERIFYING is displayed. The subroutine at 59899/$E9FB is used to read the program data from tape into the specified area of memory (carry should be clear upon return if the load is successful). Finally, the ending address is loaded into the X and Y registers before exiting.

62369 $F3A1

Attempts to set up fast serial load or verify

Checks the first character of the filename to be loaded and exits immediately if it’s $, indicating that a disk directory rather than a program is to be loaded. Otherwise, the routine opens a command file (secondary address of 15) to the specified serial device. If the file is not opened successfully, the routine exits with the Kernal device-not-present error (the status register carry bit will be set and the accumulator will hold the error code, 5/$05). If the command channel has been opened, the burst mode load command-VO, followed by the value 31/ $1F to specify a program file, followed by the codes for the characters of the filename-is sent to the drive. The CLRCH routine $FFCC is called to reset the default IjO channels. Bit 7 of the fast serial flag at 2588/$0A1C is then tested. If the serial device is capable of fast serial communications, the bit will have been set to %1 by the BSOUT routine calls used to send characters to the device. In this case, the routine branches to 62442/$F3EA to perform the operation using the very high speed burst mode. If fast serial communications are not available, the command channel file is closed, and the routine returns with carry set, indicating that a standard (slow) load or verify must be performed.

62442 $F3EA

Loads or verifies a file using fast serial burst mode

Performs a high-speed load or verify of the file specified in the preceding routine. Burst mode loads are quite different from standard loads. In burst mode, data is sent a sector (254 bytes) at a time. A status byte is sent before the data bytes for each sector. The serial bus CLK line must be toggled between bytes to acknowledge receipt of the byte.

The routine begins by restoring the filename length (183/$B7) from temporary storage (159/$9F), then disabling IRQ interrupts, allowing the serial bus CLK line to go high and setting the serial port for fast serial input. The status byte for the first sector is read. If the status value is 2/$02, the specified file has not been found, so the command channel to the drive is closed and the routine exits with a Kemal file-notfound error (the status register carry bit will be set and the accumulator will hold the error code, 4/$04). If the status byte is 31/$1F, the file is only one block long. The LOADING or VERIFYING message is then displayed (if Kernal control messages are allowed), and the first two data bytes from the first sector are then read and stored in the starting address pointer (174-175/$AE-$AF). If the stored secondary address (158/ $9E) is zero, a relocating load has been specified, so the pointer is reloaded with the value from 195-196/$C3-$C4. The starting address is then transferred to the working pointer (172-173/$AC-$AD).

Before each sector is read, the RUN/STOP key is tested. If that key has been pressed, a branch is taken to the routine at 62630/$F4A6 to halt the operation. Otherwise, the count of data bytes in the sector (165/$A5) is initialized-252 bytes for a full first sector, 254 bytes in following full sectors, and a variable number of bytes in the last sector-and the subroutine at 62661/$F4C5 is called to load or verify the data bytes. After each sector is loaded or verified, the status byte for the next sector is read. If the status value is 0 or 1, the routine loops to read the next sector. If it is 31/$lF, the next block is the last one for the file, so an additional byte is read from the device. This value will be the number of bytes in the final sector. If the status value is 2 or greater (but not 31), an error has occurred, so a branch is taken to the routine at 62616/$F498 to handle the error.

After all bytes for the file have been read, the routine allows the CLK line to go high, reenables IRQ interrupts, and closes the command channel file. (The status register carry bit is set before CLOSE $FFCC is called so that the special command file close is performed.) The status register carry bit will be clear upon exit for a successful load. Bit 4 of the serial status flag (144/$90) will reflect the success of a verify operation.

62616 $F498

Handles read error during burst mode load/verify

Sets bit 1 of the serial status flag (144/$90) to %1 to indicate a read timeout. Then it discards the return address of the calling routine and exits with the status register carry bit set and the accumulator holding the value 41/$29, the code for BASIC’s FILE READ error message.

62630 $F4A6

Stops burst mode load/verify if RUN/STOP key pressed

Closes the file, then changes the current secondary address to zero, discards the return address of the calling routine, and exits with the status register carry bit set and the accumulator holding the value 0/$00.

62642 $F4B2

Aborts burst mode load/verify if maximum address exceeded

Closes the file, then discards the return address of the calling routine and exits with the Kernal out-of-memory error (upon exit, the status register carry bit will be set and the accumulator will hold the error code, 16/$10).

62650 $F4BA

Reads a byte using fast serial hardware

Waits for a serial register interrupt to occur on CIA #1 (indicating that a byte has been received via the fast serial hardware), then retrieves the byte from the serial data register and returns it in the accumulator.

62661 $F4C5

Loads or verifies a block of data using burst mode

Waits for a serial register interrupt to occur on CIA #1 (indicating that a byte has been received via the fast serial hardware); then retrieves the byte from the serial data register and toggles the eLK line to acknowledge reception of the byte. If the operation flag (147/$93) contains a nonzero value, indicating verify, the byte read from the serial bus is compared with the one pointed to by 174-175/$AE-$AF in the bank specified in 198/$C6. If the bytes do not match, the verify error bit (bit 4) of the serial status flag (144/$90) is set to %1. For a load operation, the byte read from the bus is stored at the address pointed to by 174-175/$AE-$AF in the bank specified in 198/$C6. The address value in the pointer is then incremented; if it exceeds 65279/$FEFF, the routine will exit with the status register carry bit set. The count of bytes to be read from this sector (165/$A5) is decremented. If bytes remain to be read, the routine is repeated. Otherwise, it exits with carry clear and with the accumulator holding the high byte of the next load address

62723 $F503

Toggles state of serial bus CLK line

Reverses the value of bit 4 of CIA #2 port A. Since the line for this bit is connected to the serial bus CLK output line, this will reverse the state of the line. This is the handshake for burst mode loads from disk

62735 $F50F

Displays SEARCHING FOR message

Checks the Kernal message flag (157/$9D), exiting immediately if messages are not allowed. The message SEARCHING is displayed; then the current filename length (183/$B7) is tested. If it’s nonzero, the message FOR and the characters of the filename are displayed following SEARCHING.

62771 $F533

Displays LOADING or VERIFYING message

Loads the accumulator with the offset for either the LOADING or VERIFYING message, depending on whether the value in the operation flag (147/$93) is zero or nonzero. The routine at 63262/$F71E is then called to display the message (if Kernal messages are allowed).

62782 $F53E SAVE

Saves a block of memory to tape or disk

(This routine has a jump table entry at 65496/$FFD8.) Stores the address value from the X and Y registers into the pointer to the ending address for the save (174-175/$AE-$AF), and stores the address from the two-byte zero-page pointer specified in the accumulator into the pointer to the starting address for the save (193-194/$C1-$C2). The routine then takes an indirect jump through the ISAVE vector at 818/$0332. That vector normally points back to the location immediately following the indirect jump, but you can modify the actions of the SAVE routine by redirecting the vector to your own routine. See the ISAVE entry in Chapter 2 for details. The device number (186/$BA) is then tested. If it’s 1 (tape), a branch is taken to the routine at 62920/$F5C8. If it’s 4 or greater (serial device), a branch is taken to the routine at 62817/$F561. Otherwise, the routine exits with the Kernal illegal-device-number error (the status register carry bit will be set and the accumulator will hold the error code, 9/$09).

62817 $F561

Saves a block of memory to a serial device

Begins by checking the filename length (183/$B7). If the length value is zero, the routine exits with the Kernal missingfilename error (the status register carry bit will be set and the accumulator will contain the error code, 8/$08). The secondary address is set to 97/$61, the value to open a program file for writing; then a file is opened using the current filename (pointed to by 187-188/$BB-$BC). A LISTEN command is sent to the current device (186/$BA) along with the secondary address. The low and high bytes of the starting address for the save are then written as the first two bytes of the file.

The subroutine at 63436/$F7CC is used to retrieve a byte from the specified memory area, and the subroutine at 58629/ $E503 is used to write the byte to the file (using fast serial mode if it is available). After each byte, the routine checks whether the RUN/STOP key has been pressed. If it has been pressed, a branch is taken to the routine at 62901/$F5B5 to abort the SAVE. Otherwise, the address pointer is incremented, and the process repeats until all bytes have been written. (Before this routine is called, the starting address must be loaded into 193-194/$C1-$C2 and the ending address plus 1 into 174-175/$AE-$AF.) When all bytes have been written, an UNLISTEN command is sent to the serial device; then the routine falls through into the following one to close the file.

When fast serial communications are available, files are loaded by sectors (254-byte chunks of data) using a special feature of the 1571 drive known as burst mode. However, fast mode SAVEs are still done byte by byte. This is the reason more time is required to save a file using fast serial mode than to load a file of the same length.

62878 $F59E

Closes a file on a serial device

Exits immediately if the secondary address is greater than 127/$7F (if bit 7 of 185/$B9 is %1). Otherwise, a LISTEN command is sent to the current serial device; then the upper four bits of the secondary address are set to %1110/$E to form the CLOSE command for the logical file. This command is then sent to the current serial device, followed by an UNLISTEN command. The routine then exits with the status register carry bit clear. The serial status flag (144/$90) will reflect the success of the operation.

62901 $F5B5

Aborts LOAD or SAVE to serial device

Calls the subroutine at 62878/$F59E to close the file, then loads the accumulator with zero and exits with the status register Z and carry bits set.

62908 $F5BC

Displays SAVING message and filename

Checks the Kernal message flag (157/$9D) and exits immediately if Kernal messages are not allowed. Otherwise, the message SAVING is printed. If a filename is being used (indicated by a nonzero length value in 183/$B7), the name is also printed following SAVING.

62920 $F5C8

Saves a block of memory to tape

Begins by checking the tape buffer address, exiting immediately if it is less than $0200. If no buttons are currently pressed on the tape drive, the subroutine to print PRESS PLAY & RECORD ON TAPE (if Kernal messages are allowed) and wait for a button press is called. If the RUN/STOP key has been pressed while waiting for the tape button (or at any other time during this routine), the routine exits with the status register carry bit set and with the accumulator holding 0/$00. Otherwise, if Kernal messages are allowed, the SAVING message is displayed, followed by the filename (if one is used). Next, a header type identifier value is selected for the file, according to bit 0 of the secondary address (185/$B9). If that bit is %0, a type identifier of 1 (relocatable file) is used. If the bit is % 1, the type identifier byte will be 3 (nonrelocatable file). The subroutine at 59673/$E919 is used to write the header for the file. The subroutine at 59928/$EAI8 is used to write the data to the file. (Before this routine is called, the starting address must be loaded into 193-194/$Cl-$C2 and the ending address plus 1 into 174-175/$AE-$AF.) Finally, bit 1 of the secondary address is tested. If that bit is %0, the routine exits with carry clear. However, if the bit is % 1, an endof-file header (type identifier of 5) is written to tape following the program.

62968 $F5F8 UDTIM

Updates jiffy timers and checks RUN/STOP key column

(This routine has a jump table entry at 65514/$FFEA.) Increments the software jiffy clock at 160-162/$A0-$A2 (part of the normal IRQ sequence). If the timer has reached a count of 5184001/$4F1A01 (corresponding to a time of 24:00:00), all three timer bytes are reset to zero. Next, the jiffy timer at 2589-2591/$0A1D-$0A1F is decremented. The 128 uses this timer only when executing the BASIC SLEEP statement; otherwise, it is available for your own timing applications.

If the PAL/NTSC flag (2563/$0A03) indicates that PAL (European) video is in use, the jiffy clock compensation counter at 2614/$0A36 is decremented. Each time this counter rolls over from 0 to 255/$FF, it is reset to 5 and the routine is repeated. Thus, in a PAL system the timers are updated 6 times for every 5 IRQ interrupts, or 60 times for every 50 interrupts. As a result, the clock is incremented 60 times per second regardless of whether the system interrupts occur at the NTSC rate (60 times per second) or the PAL rate (50 times per second). The routine then falls through into the following one.

63037 $F63D

Scans RUN/STOP key column

Reads the CIA port connecting the rows of the keyboard matrix. The keyboard scan routine $C55D, normally performed earlier in the IRQ sequence, leaves the CIA port connected to the columns of the keyboard matrix set to scan column 7, the column containing the RUN/STOP key. If the RUN/STOP key is pressed, the matrix rows for columns 1 and 6 (containing the SHIFT keys) are tested. If any key in those columns is pressed, the routine exits (so SHIFT -RUN /STOP will not be registered as a RUN/STOP keypress). Otherwise, the row register value is stored in 145/$91.

63070 $F65E RDTIM

Reads the software jiffy clock

(This routine has a jump table entry at 65502/$FFDE.) Returns the values in the software jiffy clock locations (160-162/$A0-$A2), which hold the count of jiffies (1/60 second intervals) since the system has been turned on or since the clock time has last been reset. Upon return, the accumulator will hold the low byte of the clock value (from 162/$A2), the X register will hold the middle byte (from 161/$A1), and the Y register will hold the high byte (from 160/$A0).

63077 $F665 SETTIM

Sets the software jiffy clock

(This routine has a jump table entry at 65499/$FFDB.) Stores the value in the accumulator upon entry in 162/$A2, the low byte of the clock. The X register contents will be placed in 161/$A1, the middle byte of the clock value, and the Y register contents will be placed in 160/$A0, the high byte of the clock value.

63086 $F66E STOP

Tests for a RUN/STOP keypress

(This routine is the normal target of the jump table entry at 65505/$FFE1 via the ISTOP vector at 808/$0328.) Checks the STOP key flag (145/$91), set during the UDTIM routine $F5F8 in the IRQ sequence. If the flag contains the value 127/$7F, indicating that RUN/STOP has been pressed, the Kernal CLRCH routine $FFCC is called to restore default I/O, and the count of characters in the keyboard buffer (208/$D0) is reset to zero. Upon exit, the status register Z bit will be set if the RUN/STOP key has been pressed or clear otherwise.

63100 $F67C

Handles Kernal I/O errors

Loads the accumulator with an error number depending on the entry point into the routine, then uses BIT opcodes to fall through to handle the error.

Entry point Error number Meaning
63100/$F67C 1 Too many files
63103/$F67F 2 File open
63106/$F682 3 File not open
63109/$F685 4 File not found
63112/$F688 5 Device not present
63115/$F68B 6 Not input file
63118/$F68E 7 Not output file
63121/$F691 8 Missing filename
63124/$F694 9 Illegal device number
63127/$F697 16 Out of memory

Next, the CLRCH routine $FFCC is used to reset default I/O (output to screen). If Kernal error messages are allowed, I/O ERROR # is printed, followed by the character code for the digit corresponding to the error number. Upon exit, the error number will be in the accumulator and the carry bit will be set.

Kemal error messages are normally disabled when BASIC is active. (BASIC substitutes its own, more verbose error messages.) However, Kemal error messages are enabled while the machine language monitor is active.

63152 $F6B0

Table of Kernal control messages

The messages in this table are displayed by the following routine. The end of each message is marked by a character with its high bit set to %1.

Offset Message
0/$00 I/O ERROR #
12/$0C SEARCHING
23/$17 FOR
27/$1B PRESS PLAY ON TAPE
46/$2E PRESS RECORD & PLAY ON TAPE
73/$49 LOADING
81/$51 SAVING
89/$59 VERIFYING
99/$63 FOUND
106/$6A OK

63262 $F71E

Handles Kernal control messages

Begins by checking the Kemal message flag (157/$9D), exiting immediately if bit 7 of the flag is %0 (indicating that Kernal control messages are disabled). If control messages are allowed, the value in the Y register is used as an offset to the first character of the message in the table at 63152/$F6B0. Characters from the message string are printed until a character with its high bit set is encountered. Upon exit, the carry bit will be clear.

63281 $F731 SETNAM

Sets the length and address of filename for I/O operations

(This routine has a jump table entry at 65469/$FFBD.) Stores the value in the accumulator upon entry as the length of the current filename (183/$B7), and the value in the X and Y registers as the starting address of the character codes for the current filename (187-188/$BB-$BC). The low byte of the address should be in the X register and the high byte in Y.

63288 $F738 SETLFS

Sets logical file number, device number, and secondary address for I/O operations

(This routine has a jump table entry at 65466/$FFBA.) Stores the value in the accumulator upon entry as the current logical file number (184/$B8), the value in the X register as the current device number (186/$BA), and the value in the Y register as the current secondary address (185/$B9).

63295 $F73F SETBNK

Sets data and filename banks for I/O operations

(This routine has a jump table entry at 65384/$FF68.) Stores the value in the accumulator upon entry as the bank number for data being saved or loaded (198/$C6), and the value in the X register as the bank where the current filename can be found (199/$C7).

63300 $F744 READSS

Reads the tape/serial or RS-232 status byte

(This routine has a jump table entry at 65463/$FFB7.) Checks the current device number (186/$BA); if it’s 2 (RS232), the value in the RS-232 status flag (2580/$0A14) is returned in the accumulator and the status flag is reset to zero. For other device numbers, the value in the tape/serial status flag (144/$90) is returned in the accumulator.

63324 $F75C SETMSG

Sets the Kernal message control flag

(This routine has a jump table entry at 65424/$FF90.) Stores the value in the accumulator upon entry as the Kernal message flag (157/$9D).

63327 $F75F SETTMO

Sets the IEEE timeout flag

(This routine has a jump table entry at 65442/$FFA2.) Stores the value in the accumulator upon entry as the IEEE timeout (2574/$0A0E). This location is unused by the 128. The routine is a holdover from the original PET/CBM Kernal; the IEEE-488 parallel interface is not implemented in the 128.

63331 $F763 MEMTOP

Sets or reads the system’s top-of-memory pointer

(This routine has a jump table entry at 65433/$FF99.) Begins by checking the status register carry bit. If carry is clear, the values in the X and Y registers are loaded into the system top-of-memory pointer (2567-2568/$0A07-$0A08), the low byte from X and the high byte from Y. If carry is set, the current top-of-memory pointer value is returned in the X and Y registers, the low byte in X and the high byte in Y.

63346 $F772 MEMBOT

Sets or reads the system’s bottom-of-memory pointer.

(This routine has a jump table entry at 65436/$FF9C.) Begins by checking the status register carry bit. If carry is clear, the values in the X and Y registers are loaded into the system bottom-of-memory pointer (2565-2566/$0A05-$0A06), the low byte from X and the high byte from Y. If carry is set, the current bottom-of-memory pointer value is returned in the X and Y registers, the low byte in X and the high byte in Y.

63361 $F781 IOBASE

Returns base address of I/0 block

(This routine has a jump table entry at 65523/$FFF3.) Returns the value 53248/$D000, the lowest address in the system’s I/O block, in the X and Y registers, 0/$00 in X and 208/$D0 in Y.

63366 $F786 LKUPSA

Checks whether a secondary address value is used

(This routine has a jump table entry at 6S372/$FF5C.) Searches the secondary address table (886-895/$0376-$037F) for an open file with the secondary address value specified in the Y register upon entry. If no match is found, the status register carry bit will be set upon exit. If an open file with the same secondary address is found, the corresponding logical file number will be returned in the accumulator, the device number in the X register and the secondary address in the Y register. In this case the carry bit will be clear upon exit.

63389 $F79D LKUPLA

Checks whether a logical file number value is used

(This routine has a jump table entry at 65369/$FF59.) Searches the logical file number table (866-875/$0362-$036B) for an open file with the logical file number value specified in the accumulator upon entry. If no match is found, the status register carry bit will be set upon exit. If an open file with the same file number is found, the logical file number will be returned in the accumulator, with the corresponding device number returned in the X register and the secondary address in the Y register. In this case the carry bit will be clear upon exit.

63397 $F7A5 DMA_CALL

Performs a DMA operation

(This routine has a jump table entry at 65360/$FF50.) Translates the bank number for the current operation, in the X register upon entry, into the equivalent MMU configuration register setting value; then forces bit 0 of the setting value to %0 to insure that the I/O block will be visible at 53248-57343/ $D000-$DFFF. With that value in the accumulator, and with the DMA chip command register value in the Y register, the routine then jumps to the DMA request routine in common RAM [$03F0].

This routine is provided for the purpose of passing commands to the REC (RAM Expansion Controller) chip in the Commodore 1700 and 1750 RAM Expansion Modules. The chip appears in 128 memory at 57088-57098/$DF00-$DF0A in the I/O block when one of the modules is plugged in. Additional setup steps may be required, depending on the command.

63406 $F7AE

Retrieves a character from the current filename

Loads a character from the current filename address (pointed to by 187-188/$BB-$BC) in the bank specified in 199/$C7. The Y register value is used as an offset into the filename. The character will be returned in the accumulator; the X register value upon entry will be preserved during this routine.

63420 $F7BC

Writes a byte value to memory

Stores the value in the accumulator upon entry into the location specified by the address in 172-173/$AC-$AD (plus the offset specified in the Y register) in the bank specified in 198/$C6.

63423 $F7BF

Writes a byte value to memory

Stores the value in the accumulator upon entry into the location specified by the address in 174-175/$AE-$AF (plus the offset specified in the Y register) in the bank specified in 198/$C6.

63433 $F7C9

Reads a byte value from memory

Returns with the accumulator holding the value from the location specified by the address in 174-175/$AE-$AF (plus the offset specified in the Y register) in the bank specified in 198/$C6.

63436 $F7CC

Reads a byte value from memory

Returns with the accumulator holding the value from the location specified by the address in 172-173/$AC-$AD (plus the offset specified in the Y register) in the bank specified in 198/$C6.

63440 $F7D0 INDFET

Retrieves a character from any bank

(This routine has a jump table entry at 65396/$FF74.) Stores the zero-page pointer address, in the accumulator upon entry, in the INDFET address pointer byte (682/$02AA); then converts the bank number for the target address, in the X register upon entry, into the corresponding MMU configuration register setting and calls the RAM-resident portion of the INDFET routine $02A2. Upon return, the accumulator will hold the value from the location at the address specified in the zero-page pointer (plus the offset specified in the Y register) in the specified bank.

63450 $F7DA INDSTA

Stores the accumulator contents in any bank

(This routine has a jump table entry at 65399/$FF77.) Converts the bank number for the target address, in the X register upon entry, into the corresponding MMU configuration register setting; then calls the RAM -resident portion of the INDSTA routine $02AF. This will place the value in the accumulator into the specified bank at the address specified in a zero-page pointer, plus the offset in the Y register. The address of the zero-page pointer must be stored in location 697/$02B9 before this routine is called.

63459 $F7E3 INDCMP

Compares the accumulator contents with a value from any bank

(This routine has a jump table entry at 65402/$FF7A.) Converts the bank number for the target address, in the X register upon entry, into the corresponding MMU configuration register setting; then calls the RAM-resident portion of the INDCMP routine $02BE. This will compare the value in the accumulator with the value at the address specified in a zeropage pointer, plus the offset in the Y register, in the specified bank. The address of the zero-page pOinter must be stored in location 712/$02C8 before this routine is called. Upon return, the status register N, Z, and C (carry) bits will reflect the result of the comparison.

63468 $F7EC GETCFG

Translates a bank number into an MMU register setting

(This routine has a jump table entry at 65387/$FF6B.) Returns with the accumulator holding the MMU register setting value corresponding to the bank number in the X register upon entry.

63472 $F7F0

Table of MMU register settings for standard banks

Each of the 16 values in this table corresponds to the MMU configuration register value that sets up one of the 16 standard banks.

Bank MMU Configuration Setting
0/$00 63/$3F (0/000111111)
1/$01 127/$7F (0/001111111)
2/$02 191/$BF (0/010111111)
3/$03 255/$FF (0/011111111)
4/$04 22/$16 (0/000010110)
5/$05 86/$56 (0/001010110)
6/$06 150/$96 (0/010010110)
7/$07 214/$06 (%11010110)
8/$08 42/$2A (%00101010)
9/$09 106/$6A (%01101010)
1O/$0A 170/$AA (%10101010)
11/$0B 234/$EA (%11101010)
12/$0C 6/$06 (%00000110)
13/$0D 13/$00 (%00001110)
14/$0E 1/$01 (%00000001)
15/$0F 0/$00 (%00000000)

63488 $F800

Code for Kernal RAM-based subroutines

This area of ROM contains the code for the RAM-resident portions of the INDFET, IND5TA, INDCMP, JSRFAR, JMPFAR, and DMA_CALL routines. The routines are copied to the appropriate areas of RAM by the routine at $E0CD, part of the reset sequence.

63591 $F867 PHOENIX

Initializes function ROMs and attempts to boot a disk in the default drive

(This routine has a jump table entry at 65366/$FF56.) Initializes any internal or external 128 function ROMs logged during the reset sequence (by the routine at 57963/$E26B). If a ROM is detected at one of the four possible address areas for function ROMs, the corresponding byte in the ROM ID table at 2753-2756/$0AC1-$0AC4 will contain a nonzero value. For logged ROMs, the JSRFAR routine is used to call the cold start vector of the ROM ($8000 or $C000 in bank 4, or $8000 or $C000 in bank 8). Depending on the ROMs, there may be no return from the JSR. However, if the routine does return from all the ROM initializations (or if no ROMs are present), the X register is loaded with the value 8, and the accumulator with 48/$30, the character code for 0. The routine then falls through into the following one to attempt to boot a disk in drive 0 of device 8.

63632 $F890 BOOT_CALL

Attempts to boot a disk

(This routine has a jump table entry at 65363/$FF53.) Stores the value in the accumulator upon entry as the character code for the current drive number and the value in the X register as the current device number; then closes any open files on the specified device. The sector number ($C2/194) is initialized to 0 and the track number is initialized to 1 (booting begins at sector 0 of track 1). The disk block read command is copied from the table at 64008/$FA08 into the disk command buffer at 256-268/$0100-$010C. Logical file 0 (the system file) is opened as a command channel and logical file 13 as a data channel. The first boot sector is read from the specified drive into the buffer at 2816-3071/$0B00-$0BFF. If the first three bytes in the sector (bytes 0-2) are the character codes for the letters CBM, this is a valid first boot sector. Otherwise, the drive will be reset and the routine will exit.

For a valid first boot sector, the message BOOTING is printed; then bytes 3-4 from the sector (the load address for data from any following boot sectors) are stored in locations 172-173/$AC-$AD, byte 5 from the sector (the bank number into which data is to be loaded) is stored in 174/$AE, and byte 6 (the number of additional boot sectors to load) is stored in 175/$AF. Subsequent bytes from the sector are printed to the screen as character codes until a byte with the value zero is encountered or until the end of the sector is reached. Following that message, three periods ( … ) will be printed.

The bank number for boot data is transferred into the working bank number location (198/$C6). If any more boot sectors are to be loaded (if the value in 175/$AF is nonzero), data from the sectors is loaded into the bank specified in 198/$C6, starting at the address in 172-173/$AC-$AD. Additional sectors are loaded sequentially starting with sector 1 of track 1. The high byte of the load address will not be allowed to roll over from 255/$FF to 0/$00, regardless of the number of boot sectors specified. (That is, the boot sectors should not attempt to load data to addresses above 65279/$FEFF.)

After all additional boot sectors are loaded (or if no additional sectors are to be loaded), the drive is reset. The routine then searches the buffer from the zero byte marking the end of the message until another byte containing a zero is found. The characters, if any, between the zero bytes are taken to be the name of a file to be loaded, and the drive number and a colon are placed immediately before the name in the buffer. If a filename is found, the routine attempts to load a file with that name into bank O. Because the Kernal LOAD routine is used, this file must be PRG (program) type. This file is always loaded into bank 0, regardless of the bank number specified for boot sectors.

After the file is loaded (or if no filename is specified), the JSRFAR address pointer (3-4/$03-$04) holds the address of the buffer location following the end-of-filename zero byte, and the JSRFAR bank (2/$02) is set for bank 15. The JSRFAR routine is then used to execute the machine language subroutine following the filename in the boot sector buffer. (Some machine language code must be present, even if it’s only an RTS opcode.) Finally, the routine exits with the status register carry bit clear.

63883 $F98B

Resets the disk drive

Preserves the status register and accumulator values on the stack for later retrieval, then restores the data channel for booting (logical file 13). The reset command (UI) is sent to the disk drive; then I/O channels are reset and the command channel (logical file 0) is closed as well. Finally, the status register and accumulator are restored to their original values before exiting.

63923 $F9B3

Loads additional boot sectors

Begins by incrementing the sector number (194/$C2). If the count exceeds 20 (the maximum sector number for tracks 1-17), the sector number is reset to zero and the track number (193/$C1) is incremented. (Since a maximum of 255 additional sectors can be loaded, all boot sectors will be located on tracks 1-13.) The equivalent ASCII digits for the track and sector values are then added to the block read command in the buffer at 256-268/$0100-$010C, and the command is sent to the drive via the command channel (logical file 0). The 256 data bytes from the sector are then read via the data channel (logical file 13) and are stored starting at the address in 172-173/$AC-$AD in the bank specified in 198/$C6.

63995 $F9FB

Converts a byte value into two ASCII digits

Returns two character codes representing digits for the decimal equivalent of the value in the accumulator upon entry. The left digit will be in the X register upon return, and the right digit will be in the accumulator. This routine works only for input values in the range 0-99/$00-$63. 64008 $FA08 Table of disk commands for booting. This table holds the text for the disk block read command for booting (Ul:13 0 01 00), the initialize command (I), and the channel number command (#).

64023 $FA17 PRIMM

Handles PRIMM (print immediate) function

(This routine has a jump table entry at 65405/$FF7D.) Begins by stashing the accumulator and X and Y register values on the stack for later retrieval, then increments the return address on the stack and loads it into a working pointer at 206-207/$CE-$CF. The pointer thus contains the address of the location immediately following the JSR which called this routine. The byte at that location is retrieved and, unless its value is zero, is printed as a character. The routine then loops back to increment the address on the stack and retrieve another character, repeating until a zero byte is found. At that point, the original accumulator and X and Y register values are restored and the routine exits. Because the return address on the stack has been incremented, the routine will return to the address following the zero byte rather than to the address following the calling JSR.

It’s very important always to call this routine (or its jump table entry) with JSR, not JMP. Only JSR puts a return address on the stack in the expected position; entering with JMP will cause the stack to be garbled, which will almost certainly result in a crash.

64064 $FA40 NMI

Handles NMI interrupts

(This routine is the default target of the INMI indirect vector at 792/$0318.) Begins by clearing the status register D (decimal) bit to insure that the system is not in decimal mode. Next, all CIA #2 interrupt sources are disabled, and the interrupt control register for that chip is checked to determine whether any CIA #2 source triggered the NMI interrupt. If so, the routine skips ahead to call the RS-232 handling routine. (CIA #2 interrupts are used to drive RS-232 output.) If the NMI was not triggered by a CIA #2 source, the routine checks whether the RUN/STOP key is pressed. If so, it’s assumed that the NMI was triggered by pressing the RESTORE key, so the RUN/STOP-RESTORE sequence is performed. Otherwise, the RS-232 NMI handling routine $E805 is called (if the NMI was not triggered by a CIA #2 source, this step will simply reenable any active RS232 CIA #2 interrupt sources), and the routine exits via the common interrupt return $FF33.

The RUN/STOP-RESTORE sequence consists of the following steps:

64101 $FA65 IRQ

Handles IRQ interrupts

(This routine is the default target of the IIRQ indirect vector at 788/$0314.) Begins by clearing the status register D (decimal) bit to insure that the system is not in decimal mode. The screen editor IRQ routine $C024 is called to handle screen mode settings, scan the keyboard, and blink the cursor. If the carry is clear upon return from that routine, indicating that the interrupt was triggered by a midscreen raster interrupt, the routine exits without performing any further actions. Otherwise, the UDTIM routine $F5F8 is called to update the jiffy timers; then the tape motor interlock handling routine $EED0 is called. The interrupt control register for CIA #1 is read to clear any CIA #1 interrupts that might have occurred. Next, the initialization status flag (2564/$OA04) is checked. If the flag has bit 0 set to % I, indicating that BASIC has been initialized, the BASIC IRQ routine [$4006] is called to handle sprite movement and sound statements. Finally, the routine exits via the common interrupt return $FF33.

You can modify IRQ handling by redirecting the IIRQ vector (788-789/$0314-$0315) to a routine of your own

64128 $FA80

Standard keyboard decoding tables

The following five 89-byte tables are used to translate the keyscan code generated by the SCNKEY routine $C55D into the corresponding character code. The appropriate table is selected according to the value in the shift key flag (211/$D3), and its address is loaded into the keyboard table pointer (204-205/$CC-$CD). Then the keyscan code is used as an offset into the table to retrieve the appropriate character code. The table starting addresses are as follows:

Address Table
64128/$FA80 Standard (unshifted) and ALT
64217/$FAD9 SHIFT
64306/$FB32 Commodore
64395/$FB8B CONTROL
64484/$FBE4 CAPS LOCK

See 830-841/$033E-$0349 for information on how you can customize the tables. Values in the standard table (the one addressed in 830-831/$033E-$033F, normally 64128/$FA80), rather than the physical keyboard layout, determine which keys are treated as shift keys. Any key having an entry in that table with one of the following values is treated as the corresponding shift key:

   
1/$01 SHIFT
2/$02 Commodore
4/$04 CONTROL
8/$08 ALT

This also means that these character codes cannot be returned by a key in the standard table. The special functions of the RUN/STOP key (halting a BASIC program, stopping a listing, aborting a save or load, working with RESTORE, and so forth) cannot be transferred to another key. That key has a character code of 3/$03 in the standard table, but for its special functions, the keyboard column containing RUN/STOP is scanned separately (see 63037/$F63D), and its character code is irrelevant.

Table 9-5 (the CAPS LOCK table) shows one of the more amusing bugs in the first version of 128 Kernal ROM. Note the entry for the Q key. That key is separated from the other alphabetic keys, so the programmer at Commodore who prepared this table overlooked it and neglected to change the entry when the other letter values were changed to their SHIFTed equivalents. The Q entry should be 209/$D1, which explains why the Q key is unaffected by CAPS LOCK.

64573-65279 $FC3D-$FEFF Unused

All bytes in this unused area of Kernal ROM hold the value 255/$FF.

65280 $FF00 MMURCR

Configuration register

This is one of the most important locations in all of 128 memory, since the value here determines what other memory elements will be visible to the processor. The entire design of the 128 is contingent on the MMU’s ability to make various selections of the system’s memory resources visible at shared locations within the processor’s limited address space. The 16 banks supported by the operating system are merely 16 predefined settings of this register-not 16 physical arrangements of memory.

Configurations other than the standard banks are certainly possible. Since each of the eight bits of this register is assigned a function in the MMU specifications, there are theoretically 256 possible different memory configurations. Actually, there are only 128 functional combinations because bit 7 of the register in not implemented in the current version of the MMU. However, not all of these possible configurations are equally useful. For example, none of the configurations which involve either internal or external function ROM are useful unless you have a function ROM installed. The only configuration regularly employed by the system that doesn’t correspond to a standard bank is one used by BASIC, consisting of BASIC ROM, screen editor ROM, character ROM, and Kernal ROM, plus block 1 RAM (essentially the same as bank 14, but with RAM from block 1 instead of block 0). Machine language programmers may find it useful to set up a configuration which switches out BASIC ROM while retaining the I/O block and screen editor and Kernal ROM. Such a configuration leaves 41K free for ML programs in the range 7168-49151/$1C00-$BFFF. To set up this arrangement, use LDA #$0F:STA $FF00

This location has an identical twin at address 54528/$D500. Actually, there is only one configuration register, but it can be accessed at two different addresses. The higher address is used almost exclusively because it is visible in all memory configurations, whereas the register is visible at 54528/$D500 only when the I/O block is selected (and you must have access to the configuration register to make the I/O block visible). Both registers will hold identical values, regardless of which register is written to set the value.

There is an alternative to storing values directly in this register. You can store up to four configuration register settings in the preconfiguration registers at 54529-54532/$D501-$D504, then transfer the values to the configuration register by writing to the corresponding load configuration registers at 65281-65284/$FF01-$FF04. See the preconfiguration and load configuration register entries for details.

Bit 0: this bit determines what is seen at addresses in the range 53248-57343/$D000-$DFFF. When the bit is set to %0, the I/O block (containing hardware chip registers and color RAM) is visible. When the bit is set to %1, the contents of the address area is determined by the setting of bits 4-5.

Bit 1: this bit determines what is seen at addresses in the range 16384-32767/$4000-$7FFF. When the bit is set to %0, the lower portion of BASIC ROM appears there. When the bit is set to %1, the address area will contain RAM from the block specified in bits 6-7.

Bits 2-3: these bits determine what is seen at addresses in the range 32768-49151/$8000-$BFFF. The four possible selections are as follows:

Bits 3 2 Address range contents
0 0 Upper portion of BASIC ROM ($8000-$AFFF), plus monitor ROM ($B000-$BFFF)
0 1 Internal function ROM
1 0 External function ROM
1 1 RAM

Internal function ROM refers to ROM in the free ROM socket on the 128 circuit board. External function ROM refers to ROM in a cartridge plugged into the expansion port. If you select either of these sources when no ROM is actually installed, the area will appear to contain unpredictable changing values. When RAM is selected in this area, the block from which the RAM will be seen is determined by the setting of bits 6-7.

Bits 4-5: these bits determine what is seen at addresses in the range 49152-65535/$C000-$FFFF, with some exceptions. The MMU configuration and load configuration registers always appear at 65280-65284/$FF00-$FF04, regardless of the settings of these bits. Also, bit 0 of this register can override the specification for the contents of addresses in the range 53248-57343/$D000-$DFFF. As long as bit 0 is set to %0, the I/O block will be seen in that portion of this area, regardless of the setting of these bits. The four possible selections for this area are as follows:

Bits 5 4 Address area contents
0 0 Screen editor ROM ($C000-$CFFF), character ROM ($D000-$DFFF), Kernal ROM ($E000-$FFFF)
0 1 Internal function ROM
1 0 External function ROM
1 1 RAM

Internal function ROM refers to ROM in the free ROM socket on the 128 circuit board. External function ROM refers to ROM in a cartridge plugged into the expansion port. If internal or external function ROM is selected when no ROM is actually present, the area will appear to contain unpredictable changing values. When RAM is selected, the area will contain RAM from the block specified in bits 6-7, unless the MMU’s RAM configuration register at 54534/$D506 specifies a common area at the top of memory. When a common area is enabled, all RAM in the common area will come from block 0, regardless of the block specified in bits 6-7.

Bits 6-7: the memory configuration established by the 128 always includes RAM in the lowest 16K area (addresses 2-16383/$0002-$3FFF), and RAM may be selected in any of the other three 16K segments in the processor’s 64K address space. These bits determine which 64K RAM block the RAM in the selected configuration will be seen from. The formal specifications for these bits are as follows:

Bits 7 6 RAM block selected
0 0 Block 0
0 1 Block 1
1 0 Block 2
1 1 Block 3

Remember, however, that the 128 actually has only two 64K blocks of RAM (blocks 0 and 1). Thus, the setting of bit 7 is meaningless and has no effect in the current version of the MMU. Bit 7 will retain whatever value you write to it, but only bit 6 is significant.

The block specification in these bits will be overridden for certain ranges of memory if any common RAM areas are specified by the RAM configuration register at 54534/$D506. When common areas are enabled, any visible RAM in the common range will be seen from block 0, regardless of the setting of these bits.

These bits specify the RAM block for the processor only; the block in which the VIC (40-column) chip’s video RAM bank is seen can be selected independently. The VIC block is specified in bits 6-7 of the MMU’s RAM configuration register at $D506. The RAM configuration register bits, rather than the configuration register bits, also determine which block will be affected by other DMA (direct memory access) operations, such as data transfers by the REC (RAM expansion controller) chip in the RAM expansion modules.

65281-65282-65283-65284 $FF01-$FF02-$FF03-$FF04 LCRA LCRB LCRC LCRD

Load configuration register

Each of these registers has a corresponding preconfiguration register at 54529-54532/$D501-$D504. Storing a value in a load configuration register causes the value in the preconfiguration register to be transferred to the configuration register. The value stored in the load configuration register is irrelevant; it is the store operation, rather than the value stored, which causes the transfer. Reading any of the load configuration register locations returns the value in the corresponding preconfiguration register. Values stored in a load configuration register location have no effect on the value returned when the register is read.

The 128 operating system does not use the preconfiguration or load configuration registers, but BASIC does. See the entry above for the pre configuration registers for details of the standard configuration settings.

65285 $FF05 JNMI

Jump to NMI handler routine

(This routine is the target of the processor NMI vector at 65530/$FFFA.) Pushes the accumulator and X and Y register values onto the stack, then places the current MMU configuration register value onto the stack as well. (Thus, 128 interrupts place one more byte on the stack than do Commodore 64 interrupts.) The routine then configures the system for bank 15 and jumps through the INMI indirect vector at 792/$0318 to a routine to handle the NMI. The vector normally points to 64064/$FA40, but you can redirect it to a routine of your own for special handling. See the INMI entry for details. If normal processing is to continue following the interrupt handling, the handling routine should end with a jump to the routine at 65331/$FF33 to restore the processor registers and MMU configuration to their original values.

This routine is also copied into all RAM banks to handle interrupts which occur when the system is configured for a bank where Kernal ROM is not visible.

65303 $FF17 JIRQ

Jump to IRQ or BRK handler routine

(This routine is the target of the processor IRQ/BRK vector at 65534/$FFFE.) Pushes the accumulator and X and Y register values onto the stack, then places the current MMU configuration register value onto the stack as well. (Thus, 128 interrupts place one more byte on the stack than do Commodore 64 interrupts.) The routine then configures the system for bank 15 and reads the processor status register value on the stack (the interrupt automatically causes the status register value and return address to be placed on the stack before this routine is called). If the B bit (bit 4) of the status register is set to %1, indicating that the interrupt is the result of the execution of a BRK instruction (a software interrupt), the routine jumps through the IBRK indirect vector at 790/$0316 to a routine to handle the BRK. That vector normally points to 45059/$B003, the break entry point into the machine language monitor. If the B status bit is %0, a hardware interrupt has been triggered by an external source, so the routine jumps through the IIRQ indirect vector at 788/$0314. That vector normally points to the handling routine at 64101/$FA65. In either case, you can redirect the vector to a routine of your own for special handling. See the IBRK and IIRQ entries for details.

If normal processing is to continue following the interrupt handling, the handling routine should end with a jump to the routine at 65331/$FF33 to restore the processor registers and MMU configuration to their original values. For example, the normal IRQ service routine exits in this manner, so processing resumes unaffected after a standard IRQ interrupt. However, the BRK service routine does not return, since normal processing is halted when the monitor is entered.

This routine is also copied into all RAM banks to handle interrupts which occur when the system is configured for a bank where Kernal ROM is not visible.

65331 $FF33 CRTI

Common exit routine for all interrupt routines

Retrieves the MMU configuration register value from the stack and restores the system to its original bank setting, then restores the Y and X register and accumulator values from the stack. Processing resumes at the instruction following the one during which the interrupt has occurred.

65341 $FF3D JRESET

Jump to reset handler routine.

(This routine is the target of the processor RESET vector at 65532/$FFFC.) Sets the system for the bank 15 configuration, then jumps to the RESET routine $E000. This routine is also copied into all RAM banks to handle any reset which might occur when the system is configured for a bank where Kernal ROM is not visible.

65349-65350 $FF45-$FF46 Unused

Two unused bytes, filled with the value 255/$FF.

New 128 Kernal Jump Table

Locations 65351-65407/$FF47-$FF7F are a table of jump vectors to routines found in 128 ROM, but not in previous versions of the Kernal for earlier Commodore computers. As with the other jump tables, each table entry consists of a JMP opcode (76/$4C) followed by the address of the target routine.

65351 $FF47 SPIN SPOUT

The C128/1571 fast serial protocol utilizes CIA 1 (6526 at $DC00) and a special driver circuit controlled in part by the MMU (at $D500). SPINP and SPOUT are routines used by the system to set up the CIA and fast serial driver circuit for input or output. SPINP sets up CRA (CIA 1 register 14) and clears the FSDIR bit (MMU register 5) for input. SPOUT sets up CRA, ICR (CIA 1 register 13), timer A (CIA 1 registers 4 and 5), and sets the FSDIR bit for output. Note the state of the TODIN bit of CRA is always preserved, but the state of the GAME, EXROM and SENSE40 outputs of the MMU are not (reading these ports return the state of the port and not the register values consequently they cannot be preserved). These routines are required only by applications driving the fast serial bus themselves from the lowest level.

65354 $FF4A CLOSE ALL

The FAT is searched for the given FA. A proper CLOSE is performed for all matches. If one of the CLOSEd channels is the current I/O channel, then the default channel is restored. This call is utilized, for example, by the BASIC command DCLOSE. It is also called by the Kernal BOOT routine.

65357 $FF4D C64MODE

There is no return from this routine. The 8502 DDR and port are initialized, and the VIC is set to 1MHz (slow) mode. Control is passed to code in common (shared) RAM, which sets the MMU mode register (#5) to C64 mode. From this point on, the MMU and C128 ROMs are not accessible. The routine exits via an indirect jump through the C64 RESET vector.

Since C64 operation does not allow for MMU access, all MMU registers must be configured for proper operation before the C64 mode bit is set. Similarly, because the start-up of the C64 operating system is not from a true hardware reset, there is the possibility that unusual I/O states in effect prior to C64MODE calls can cause unpredictable and presumably undesirable situations once in C64 mode.

There is no way to switch from C64 mode back to C128 mode; only a hardware reset or power off/on will restore the C128 mode of operation. A reset will always initiate C128 mode, although altering the SYSTEM vector beforehand is one way to automatically “throw” a system back to C64 mode.

65360 $FF50 DMA CALL

DMA CALL is designed to communicate with an external expansion cartridge capable of DMA and mapped into system memory at 102 ($DFxx). The DMA CALL converts the logical C128 bank parameter to MMU configuration via GETCFG, OR’s in the I/O enable bit, and transfers control to RAM code at $03F0. Here the C128 bank specified is brought into context, and the user’s command is issued to the DMA controller. The actual DMA transfer is performed at this point, with the 8502 kept off the bus in a wait state. As soon as the DMA controller releases the processor, memory is reconfigured to the state it was in at the time of the call and control is returned to the caller. The user must analyze the completion status by reading the DMA status register at $DF00. Care should be taken in the utilization of the C128 RAM expansion product by any application using the built-in Kernal interface. This includes especially the use of the C128 BASIC commands FETCH, STASH and SWAP. In the routine that prepares a DMA request for the user, the Kernal forces the I/O block to be always in context. Consequently, data from the DMA device is likely to corrupt sensitive I/O devices. Users should either bypass the Kernal DMA routine by providing their own interface, or limit the DMA data transfers to the areas above and below the I/O block. Only strict observance of the latter will guarantee proper utilization of the BASIC commands. The following code, used instead of the DMA CALL in the above example, illustrates a work-around:

  LDX	#$00	// C128 bank
  LDY	#$84	// DMA command to "STASH"
  JSR	$FF6B	// GETCFG
  TAX
  JSR	$3F0	// execute DMA command

Example

  LDA	#$00	// setup C128 base address
  STA	$DF02	// low
  LDA	#$20
  STA	$DF03	// high

  LDA	#$00	// setup expansion RAM address
  STA	$DF04	// low
  STA	$DF05	// high
  STA	$DF06	// bank (0-n, where n = 3 if 256K)

  LDA	#$40	// setup number of bytes
  STA	$DF07	// low
  LDA	#$1F
  STA	$DF08	// high

  LDX	#$00	// C128 bank
  LDY	#$84	// DMA command to "STASH"
  JSR	$FF50	// execute DMA command

65363 $FF53 BOOT CALL

BOOT CALL attempts to load and execute the boot sector from an auto-boot disk in the given drive and device. The BOOT protocol is as follows:

On any error, the BOOT operation is aborted and the UI command is issued to the disk. A return may or may not be made to the caller depending upon the completion status and the BOOTed code. The BOOT sector has the following layout:

$00 $01 $02 $03 $04 $05 $06   A   B C
C B M adrl adrh bank blk# title 0 file 0 code
where: A = $07 + LEN(title)
       B =   A + LEN(filename)
       C =   B + 1

The following examples illustrate the flexibility of this layout. This loads and runs a BASIC program:

$00	->	CBM	:key
$03	->	$00,$00,$00,$00	:no other BOOT sector
$07	->	NAME,$00	:message "NAME"
$0C	->	$00	:no filename
$0D	->	$A2,$13,$A0,$0B
$4C,$A5,$AF	:code
$14	->	RUN"PROGRAM"	:data (BASIC stmt)
$20	->	$00

This results in the message Booting NAME… being displayed and, utilizing a C128 BASIC jump table entry that finds and executes a BASIC statement, loads and runs the BASIC program named “ P R O G R A M . “ The same header can be used to load and execute a binary (machine code) program by simply changing RUN to BOOT. (While the file auto-load feature of the boot header could be used to load binary files simply by furnishing a filename, to execute it you must know the starting address and JMP to it. BASIC’s BOOT command does that, and allows a more generic mechanism.) In the next example, a menu is displayed and you are asked to select the operating mode. Nothing else is loaded in this “configure”-type header:

$00	->	CBM	:key
$03	->	$00,$00,$00,$00	:no other BOOT sector
$07	->	$00	:no message
$0C	->	$00	:no filename
$0D	->	$20,$7D, $FF,$0D, $53, $45,$4C, $45
$43, $54, $20,$4D, $4F, $44, $45,$3A
$0D,$0D, $20, $31,$2E, $20, $43, $36
$34, $20, $20, $42, $41, $53, $49, $43
$0D, $20, $32,$2E, $20, $43, $31, $32
$38, $20, $42, $41, $53, $49, $43,$0D
$20, $33,$2E, $20, $43, $31, $32, $38
$20,$4D, $4F, $4E, $49, $54, $4F, $52
$0D,$0D, $00, $20,$E4,$FF,$C9, $31
$D0, $03,$4C,$4D,$FF,$C9, $32,$D0
$03,$4C, $03, $40,$C9, $33,$DO, $E3
$4C, $00,$B0

The loading of sequential sectors is designed primarily for specialized applications (such as CP/M or games) that do not need a disk directory entry.

65366 $FF56 PHOENIX

The C128 Kernal initialization routine POLL creates a Physical Address Table (PAT) containing the ID’s of all installed function ROM cartridges. PHOENIX calls each logged cartridge’s cold-start entry in the order: external low/high, and internal low/high. After calling the cartridges (if any), PHOENIX calls the Kernal BOOT routine to look for an auto-boot disk in drive 0 of device 8 (see BOOT CALL). Control may or may not be returned to the user. PHOENIX is called by BASIC at the conclusion of its cold initialization.

65369 $FF59 LKUPLA

LKUPLA is a Kernal routine used primarily by BASIC DOS commands to work around a user’s open disk channels. The Kernal requires unique logical device numbers (LA’s), and the disk requires unique secondary addresses (SA’s); therefore BASIC must find alternative unused values whenever it needs to establish a disk channel.

65372 $FF5C LKUPSA

LKUPSA is a Kernal routine used primarily by BASIC DOS commands to work around a user’s open disk channels. The Kernal requires unique logical device numbers (LA’s), and the disk requires unique secondary addresses (SA’s); therefore BASIC must find alternative unused values whenever it needs to establish a disk channel.

65375 $FF5F SWAPPER

SWAPPER is an Editor utility used to switch between the 40-column VIC (composite) video display and the 80-column 8563 (RGBI) video display. The routine simply swaps local (associated with a particular screen) variables, TAB tables and line wrap maps with those describing the other screen. The MSB of MODE, location $D7, is toggled by SWAPPER to indicate the current display mode: $80= 80-column, $00= 40-column.

65378 $FF62 DLCHR

DLCHR (alias INIT80) is an Editor utility to copy the VIC character definitions from ROM ($D000-$DFFF, bank 14) to 8563 display RAM ($2000-$3FFF, local to 8563-not in processor address space). The 8 by 8 VIC character cells are padded with nulls ($00) to fill out the 8 by 16 8563 character cells. Refer to Chapter 10 in Commodore 128 Programmers Reference Guide, Programming the 80-Column (8563) Chip for details concerning the 8563 font layout.

65381 $FF65 PFKEY

PFKEY (alias KEYSET) is an Editor utility to replace a C128 function key string with a user’s string. Keys 1-8 are F1-F8, 9 is the SHIFT RUN string, and 10 is the HELP string. The example below replaces the “help” RETURN string assigned at system initialization to the H E L P key with the string “string.” Both the key length table, PKYBUF ($1000-$1009), and the definition area, PKYDEF ($100A-$10FF) are compressed and updated. The maximum length of all ten strings is 246 characters. No change is made if there is insufficient room for a new definition.

65384 $FF68 SETBNK

SETBNK is a prerequisite for any memory I/O operations, and must be used along with SETLFS and SETNAM prior to OPENing files, etc. BA (198/$C6) sets the current 64KB memory bank for LOAD/SAVE/VERIFY operations. FNBANK (199/$C7) indicates the bank in which the filename string is found. The Kernal routine GETCFG is used to translate the given logical bank numbers (0-15). SETBNK is often used along with SETNAM and SETLFS calls prior to OPEN’s. See the Kernal OPEN, LOAD and SAVE calls for examples.

65387 $FF6B GETCFG

GETCFG allows a universal, logical approach to physical bank numbers by providing a simple lookup conversion for obtaining the actual MMU configuration data. In all cases where a bank number 0-15 is required, you can expect GETCFG to be called to convert that number accordingly. There is no error checking; if the given logical bank number is out of range the result is invalid. Refer to the Memory Management Unit in the Commodore 128 section later in this chapter for details concerning memory configuration. The C128 Kernal memory banks are assigned as follows: |Bank|Value|Description| |-|-|-| |0|%00111111|RAM 0 only| |1|%01111111|RAM 1 only| |2|%10111111|RAM 2 only| |3|%11111111|RAM 3 only| |4|%00010110|INT ROM, RAM 0, I/O| |5|%01010110|INT ROM, RAM 1, I/O| |6|%10010110|INT ROM, RAM 2, I/O| |7|%11010110|INT ROM, RAM 3, I/O| |8|%00101010|EXT ROM, RAM 0, I/O| |9|%01101010|EXT ROM, RAM 1, I/O| |10|%10101010|EXT ROM, RAM 2, I/O| |11|%11101010|EXT ROM, RAM 3, I/O| |12|%00000110|KERNAL, INT LO, RAM 0, I/O| |13|%00001010|KERNAL, EXT LO, RAM 0, I/O| |14|%00000001|KERNAL, BASIC, RAM 0, CHAR ROM| |15|%00000000|KERNAL, BASIC, RAM 0, I/O|

65390 $FF6E JSRFAR

The routine JSRFAR, enable code executing in the system bank of memory to call a routine in any other bank. In the case of JSRFAR, a return will be made to the caller’s bank. It should be noted that JSRFAR calls JMPFAR, which calls GETCFG. When calling a non-system bank, the user should take necessary precautions to ensure that interrupts (IRQ’s and NMI’s) will be handled properly (or disabled beforehand). Both JSRFAR and JMPFAR are RAM-based routines located in common (shared) RAM at $2CD and $2E3 respectively.

The following code illustrates how to call a subroutine in the second RAM bank from the system bank. Note that we need not worry about IRQ’s and NMI’s in this case because the system will handle them properly in any configuration that has the Kernal ROM or any valid RAM bank in context at the top page of memory.

Example

  STY	$08	  // assumes registers and status
  STX	$07	  // already setup for call
  STA	$06
  PHP
  PLA
  STA	$05

  LDA	#1	  // want to call $2000 in bank 1
  LDY	#$20
  LDX	#$00
  STA	$02
  STY	$03
  STX	$04

  JSR	$FF6E // JSRFAR

  LDA	$05	  // restore status and registers
  PHA
  LDA	$06
  LDX	$07
  LDY	$08
  PLP

65393 $FF71 JMPFAR

The routine JMPFAR, enable code executing in the system bank of memory to JMP to a routine in any other bank. It should be noted that JSRFAR calls JMPFAR, which calls GETCFG. When calling a non-system bank, the user should take necessary precautions to ensure that interrupts (IRQ’s and NMI’s) will be handled properly (or disabled beforehand). Both JSRFAR and JMPFAR are RAM-based routines located in common (shared) RAM at $2CD and $2E3 respectively.

The following code illustrates how to call a subroutine in the second RAM bank from the system bank. Note that we need not worry about IRQ’s and NMI’s in this case because the system will handle them properly in any configuration that has the Kernal ROM or any valid RAM bank in context at the top page of memory.

Example

  STY	$08	  // assumes registers and status
  STX	$07	  // already setup for call
  STA	$06
  PHP
  PLA
  STA	$05

  LDA	#1	  // want to call $2000 in bank 1
  LDY	#$20
  LDX	#$00
  STA	$02
  STY	$03
  STX	$04

  JSR	$FF6E // JSRFAR

  LDA	$05	  // restore status and registers
  PHA
  LDA	$06
  LDX	$07
  LDY	$08
  PLP

65396 $FF74 INDFET

INDFET enables applications to read data from any other bank. It sets up FETVEC ($2AA), calls GETCFG to convert the bank number, and JMPs to code in common (shared) RAM at $2A2 which switches banks, loads the data, restores the user’s bank, and returns. When calling a non-system bank, the user should take necessary precautions to ensure that interrupts (IRQ’s and NMI’s) will be handled properly (or disabled beforehand).

Example

  LDA #$00    // setup to read $2000
  STA $FA
  LDA #$20
  STA $FB
  LDA #$FA
  LDX #$01    // in bank 1
  LDY #$00
  JSR $FF74   // LDA ($FA,RAM 1),Y
  BEQ etc

65399 $FF77 INDSTA

INDSTA enables applications to write data to any other bank. After you set up STAVEC ($2B9), it calls GETCFG to convert the bank number and JMPs to code in common (shared) RAM at $2AF which switches banks, stores the data, restores your bank, and returns. When calling a nonsystem bank, the user should take necessary precautions to ensure that interrupts (IRQ’s and NMI’s) will be handled properly (or disabled beforehand).

Example

  LDA #$00      // setup write to $2000
  STA $FA
  LDA #$20
  STA $FB
  LDA #$FA
  STA $2B9
  LDA data
  LDX #$01      // in bank 1
  LDY #$00
  JSR $FF77     // STA ($FA,RAM 1),Y

65402 $FF7A INDCMP

INDCMP enables applications to compare data to any other bank. After you set up CMPVEC ($2C8), it calls GETCFG to convert the bank number and JMP’s to code in common (shared) RAM at $2BE which switches banks, compares the data, restores your bank, and returns. When calling a nonsystem bank, the user should take necessary precautions to ensure that interrupts (IRQ’s and NMI’s) will be handled properly (or disabled beforehand).

Example

  LDA #$00    // setup to verify $2000
  STA $FA
  LDA #$20
  STA $FB
  LDA #$FA
  STA $2C3
  LDA data
  LDX #$01    // in bank 1
  LDY #$00
  JSR $FF7A   // CMP ($FA,RAM 1),Y
  BEQ same

65405 $FF7D PRIMM

PRIMM is a Kernal utility used to print (to the default output device) an ASCII string which immediately follows the call. The string must be no longer than 255 characters and is terminated by a null ($00) character. It cannot contain any embedded null characters. Because PRIMM uses the system stack to find the string and a return address, you must not JMP to PRIMM. There must be a valid address on the stack.

65409 $FF81 CINT

CINT is the Editor’s initialization routine. Both 40- and 80-column display modes are prepared, editor indirect vectors installed, programmable key definitions asigned, and the 40/80 key scanned for default display determination. CINT sets the VIC bank and VIC nybble bank, enables the character ROM, resets SID volume, places both 40- and 80-column screens and clears them. The only thing it does not do that pertains to the Editor is I/O initialization, which is needed for IRQ’s (keyscan, VIC cursor blink, split screen modes), key lines, screen background colors, etc. (see IOINIT). Because CINT updates Editor indirect vectors that are used during IRQ processing, you should disable IRQ’s prior to callint it. CINT utilizes the status byte INIT_STATUS ($A04) as follows:

$A04 bit 6 = 0 -> Full initialization. (set INIT_STATUS bit 6)
             1 -> Partial initialization. (not key matrix pointers) and (not program key definitions)

CINT is also an Editor jump table entry ($C00).

65412 $FF84 IOINIT

IOINIT is perhaps the major function of the Reset handler. It initializes both CIA’s (timers, keyboard, serial port, user port, cassette) and the 8502 port (keyboard, cassette, VIC bank). It distinguishes a PAL system from and NTSC one and sets PALCNT ($0A03) if PAL. The VIC, SID and 8563 devices are initialized, including the downloading of character definitions to 8563 display RAM (if necessary). The system 60Hz IRQ source, the VIC raster, is started (pending IRQs are cleared). IOINIT utilizes the status byte INIT_STATUS ($0A04) as follows:

$A04 bit 7 = 0 -> Full initialization (set INIT_STATUS bit 7)
             1 -> Partial initialization. (not 8563 character definitions)

You should be sure IRQ’s are disabled before calling IOINIT to avoid interrupts while the various I/O devices are bing initialized.

65415 $FF87 RAMTAS

RAMTAS clears all page-zero RAM, allocates the cassette and RS-232 buffers, sets pointers to the top and bottom of system RAM (RAM 0) and points the SYSTEM_VECTOR ($0A00) to BASIC cold start ($4000). Finally, it sets a flag, DEJAVU ($0A02), to indicate to other routines that system RAM has been initialized and that the SYSTEM_VECTOR is valid. It should be noted that the C128 RAMTAS routine does not in any way test RAM.

65418 $FF8A RESTOR

RESTOR restores the default values of all the Kernal indirect vectors from the Kernal ROM list. It does not affect any other vectors, such as those used by the Editor (see CINT) and BASIC. Because it is possible for an interrupt (IRQ or NMI) to occur during the updating of the interrupt indirect vectors, you should disable interrupts prior to calling RESTOR. Seee also the VECTOR call.

65421 $FF8D VECTOR

VECTOR reads or writes the Kernal RAM indirect vectors. Calling VECTOR with the carry status set stores the current contents of the indirect vectors to the RAM address passed in the X and Y registers (to the current RAM bank). Calling VECTOR with the carry status clear updates the Kernal indirect vectors from the user list passed in the X and Y registers (from the current RAM bank). Interrupts (IRQ and NMI) should be disabled when updating the indirects. See also the RESTOR call.

65424 $FF90 SETMSG

SETMSG updates the Kernal message flag byte MSGFLG (157/$9D) that determines whether system error and/or control messages will be displayed. BASIC normally disables error messages always and disables control messages in Run mode. Note that the Kernal error messages are not the verbose ones printed by BASIC, but simply the I/O ERROR # message that you see when in the Monitor, for example. Examples of Kernal control messages are LOADING, FOUND, and PRESS PLAY ON TAPE. The MSGFLG control bits are:

MSGFLG bit 7 = 1 -> enable CONTROL messages
       bit 6 = 1 -> enable ERROR messages

65427 $FF93 SECOND

SECOND is a low-level serial routine used to send a secondary address (SA) to a LISTENing device (see LISTEN Kernal call). An SA is usually used to provide setup information to a device before the actual data I/O operation begins. Attention is released after a call to SECOND. SECOND is not used to send an SA to a TALKing device (see TKSA). (Most applications should use the higher level I/O routines: see OPEN and CHKOUT).

65430 $FF96 TKSA

TKSA is a low-level serial routine used to send a secondary address (SA) to a device commanded to TALK (see TALK Kernal call). An SA is usually used to provide setup information to a device before the actual data I/O operation begins. (Most applications should use the higher-level I/O routines; see OPEN and CHKIN).

65433 $FF99 MEMTOP

MEMTOP is used to read or set the top of system RAM, pointed to by MEMSIZE ($0A07). This call is included in the C128 for completeness, but neither the Kernal nor BASIC utilizes MEMTOP since it has little meaning in the banked memory environment of the C128 (even the RS-232 buffers are permanently allocated). Nonetheless, set the carry status to load MEMSIZE into X and Y, and clear it to update the pointer from X and Y. Note that MEMSIZE references only system RAM (RAM0). The Kernal initially sets MEMSIZE to $FF00 (MMU and Kernal RAM code start here).

65436 $FF9C MEMBOT

MEMBOT is used to read or set the bottom of system RAM, pointed to by MEMSTR ($0A05). This call is included in the C128 for completeness, but neither the Kernal nor BASIC utilizes MEMBOT since it has little meaning in the backed memory environment of the C128. Nonetheless, set the carry status to load MEMSTR into X and Y, and clear it to update the pointer from X and Y. Note that MEMSTR references only system RAM (RAM0). The Kernal initially sets MEMSTR to $1000 (BASIC text starts here).

65439 $FF9F SCNKEY

SCNKEY is an Editor routine that scans the entire C128 keyboard (except the 40/80 key). It distinguishes between ASCII keys, control keys, and programmable keys, setting keyboard status bytes and managing the keyboard buffer. After decoding the key, SCNKEY will manage such features as toggling cases, pauses or delays, and key repeats. It is normally called by the operating system during the 60Hz IRQ processing. Upon conclusion, SCNKEY leaves the keyboard hardware driving the keyline on which the STOP key is located.

There are two indirect RAM jumps encountered during a keyscan: KEYVEC ($033A) and KEYCHK ($033C). KEYVEC (alias KEYLOG) is taken whenever a key depression is discovered, before the key in A has been decoded. KEYCHK is taken after the key has been decoded, just before putting it into the key buffer. KEYCHK carries the ASCII character in A, the keycode in Y, and the skiftkey status in X.

The keyboard decode matrices are addressed via indirect RAM vectors as well, located at DECODE ($33E).

The following table describes them:

$33E	Mode 1	->	normal keys
$340	Mode 2	->	SHIFT keys
$342	Mode 3	->	C= keys
$344	Mode 4	->	CONTROL keys
$346	Mode 5	->	CAPS LOCK keys
$348	Mode 6	->	ALT keys

The following list briefly describes some of the more vital variables utilized or maintained by SCNKEY:

ROWS    $DC01 ->  I/O port outputting keys
COLM    $DC00 ->  I/O port driving C64 keys
VIC #47 $D02F ->  I/O port driving new keys
8502 P6 $0001 ->  I/O port sensing CAPS key
NDX     $D0   ->  keyboard buffer index
KEYD    $34A  ->  keyboard buffer
XMAX    $A20  ->  keyboard buffer size
SHFLAG  $D3   ->  shift key status
RPTFLG  $A22  ->  repeat key enables
LOCKS   $F7   ->  pause, mode disables

SCNKEY is also found in the Editor jump table at $C012.

65442 $FFA2 SETTMO

SETTMO is not used in the C128 but is included for compatibility and completeness. It is used in the C64 by the IEEE communication cartridge to disable I/O timeouts.

65445 $FFA5 ACPTR

ACPTR is a low-level serial I/O utility to accept a single byte from the current serial bus TALKer using full handshaking. To prepare for this routine, a device must first have been established as a TALKer (see TALK) and passed a secondary address if necessary (see TKSA). The byte is returned in A. Most applications should use the higher-level I/O routines; see BASIN and GETIN.

65448 $FFA8 CIOUT

CIOUT is a low-level serial I/O utility to transmit a single byte to the current serial bus LISTENer using full handshaking. To prepare for this routine, a device must first have been established as a LISTENer (see LISTEN) and passed a secondary address if necessary (see SECOND). The byte is passed in A. Serial output data is buffered by one character, with the last character being transmitted with EOI after a call to UNLSN. Most applications should use the higher level I/O routines; see BSOUT.

65451 $FFAB UNTLK

UNTLK is a low-level Kernal serial bus routine that sends and UNTALK command to all serial bus devices. It commands all TALKing devices to sop sending data. (Most applications should us the higher-level I/O routines; see CLRCHN).

65454 $FFAE UNLSN

UNLSN is a low-level Kernal serial bus routine that sends an UNLISTEN command to all serial bus devices. It commands all LISTENing devices to stop reading data. (Most applications should us the higher-level I/O routines; see CLRCHN)

65457 $FFB1 LISTEN

LISTEN is a low-level Kernal serial bus routine that sends a LISTEN command to the serial bus device in A. It commands the device to start reading data. Most applications should use the higher-level I/O routines; see CHKOUT.

65460 $FFB4 TALK

TALK is a low-level Kernal serial bus routine that sends a TALK command to the serial bus device in A. It commands the device to start sending data. (Most applications should use the higher-level I/O routines; see CHKIN.)

65463 $FFB7 READST

READST (alias READSS) returns the status byte associated with the last I/O operation (serial, cassette or RS-232) performed. Serial and cassette tape operations update STATUS (144/$90) and RS-232 I/O updates RSSTAT ($0A14). Note that to simulate an ACIA, RSSTAT is cleared after it is read via READST. The last I/O operation is determined by the contents of FA (186/$BA); thus applications that drive I/O devices using the lower-level Kernal calls should not use READST.

65466 $FFBA SETLFS

SETLFS sets the logical file number (LA, 184/$B8), device number (FA, 186/$BA) and secondary address (SA, 185/$B9) for the higher-level Kernal I/O routines. The LA must be unique among OPENed files and is used to identify specific files for I/O operations. The device number rangs is 0 to 31 and is used to target I/O. The SA is a command to be sent to the indicated device, usually to place it in a particular mode. If the SA is not needed, the Y register should pass $FF. SETLFS is often used along with SETNAM and SETBNK calls prior to OPENs. See the Kernal OPEN, LOAD and SAVE calls for examples.

65466 $FFBD SETNAM

SETNAM sets up the filename or command string for higher-level Kernal I/O calls such as OPEN, LOAD and SAVE. The string (filename or command) length is passed in A and updates FNLEN (183/$B7). The address of the string is passed in X (low) and Y (high). See the companion call, SETBNK, which specifies in which RAM bank the string is found. If there is no string, SETNAM should still be called and a null ($00) length specified (the address does not matter). SETNAM is often used along with SETBNK and SETLFS calls prior to OPENs. See the Kernal OPEN, LOAD and SAVE calls for examples.

65472 $FFC0 OPEN

OPEN prepares a logical file for I/O operations. It creates a unique entry in the Kernal logical file tables LAT ($0362), FAT ($036C) and SAT ($0376) using its index LDTND (152/$98) and data supplied by the user via SETLFS. There can be up to then logical files OPENed simultaneously. OPEN performs device-specific opening tasks for serial, cassette and RS-232 devices, including clearing the previous status and transmitting any given filename or command string supplied by the user via SETNAM and SETBNK. The I/O status is updated appropriately and can be read via READST.

The path to OPEN is through an indirect RAM vector at $031A. Applications may therefor provide their own OPEN procedures or supplement the system’s by redirecting this vector to their own routine.

65475 $FFC3 CLOSE

CLOSE removes the logical file (LA) passed in A from the logical file tables and performs device-specific closing tasks. Keyboard, screen and any unOPENed files pass through. Cassette files opened for output are closed by writing the last buffer and (optionally) an EOT mark. RS-232 I/O devices are reset, losing any buffered data. Serial files are closed by transmitting a CLOSE command (if an SA was given when it was opened), sending any buffered character, and UNLSNing the bus. There is a special provision incorporated into the CLOSE routine of systems featuring a BASIC DOS command. If the following conditions are all true, a full CLOSE is not performed; the table entry is removed but a CLOSE command is not transmitted to the device. This allows the disk command channel to be properly OPENed and CLOSEd without the disk operating system closing all files on its end:

.C = 1	->	indicates special CLOSE
FA >= 8	->	device is a disk
SA = 15	->	command channel

The path to CLOSE is through an indirect RAM vector at $031C. Applications may therefor provide their own CLOSE procedure or supplement the system’s by redirecting this vector to their own routine.

65478 $FFC6 CHKIN

CHKIN establishes an input channel to the device associated with the logical address (LA) passed in X, in preparation for at call to BASIN or GETIN. The Kernal variable DFLTN (153/$99) is updated to indicate the current input device and the variables LA, FA and SA are updated with the file’s parameters from its entry in the logical file tables (put there by OPEN). CHKIN performs certain device specific tasks: screen and keyboard channels pass through, cassette files are confirmed for input, and serial channels are sent a TALK command and the SA transmitted (if necessary). Call CLRCHN to restore normal I/O channels.

CHKIN is required for all input except the keyboard. If keyboard input is desired and no other input channel is established, you do not need to call CHKIN or OPEN. The keyboard is the default input device for BASIN and GETIN.

The path to CHKIN is through an indirect RAM vector at $031E. Applications may therefor provide their own CHKIN procedure or supplement the system’s by redirecting this vector to their own routine.

65481 $FFC9 CHKOUT

CHKOUT establishes an output channel to the device associated with the logical address (LA) passed in X, in preparation for a call to BSOUT. The Kernal variable DFLTO 154/$9A is updated to indicate the current output device and the variables LA, FA and SA are updated with the file’s parameters from its entry in the logical file tables (put there by OPEN). CKOUT performs certain device specific tasks: keyboard channels are illegal, screen channels pass through, cassette files are confirmed for output, and serial channels are sent a LISTN command and the SA transmitted (if necessary). Call CLRCH to restore normal I/O channels.

CKOUT is required for all output except the screen. If screen output is desired and no other output channel is established, you do not need to call CKOUT or OPEN. The screen is the default output device for BSOUT.

The path to CKOUT is through an indirect RAM vector at $0320. Applications may therefore provide their own CKOUT procedures or supplement the system’s by redirecting this vector to their own routine.

65484 $FFCC CLRCHN

CLRCHN (alias CLRCH) is used to clear all open channels and restore the system default I/O channels after other channels have been established via CHKIN and/or CHKOUT. The keyboard is the default input device and the screen is the default output device. If the input channel was to a serial device, CLRCH first UNTLKs it. If the output channel was to a serial device, it is UNLSNed first.

The path to CLRCH is through an indirect RAM vector at $0322. Applications may therefore provide their own CLRCH procedures or supplement the system’s by redirecting this vector to their own routine.

65487 $FFCF CHRIN/BASIN

CHRIN (alias BASIN) reads a character from the current input device (DFLTN 153/$99) and returns it in A. Input from devices other than the keyboard (the default input device) must be OPENed and CHKINed. The character is read from the input buffer associated with the current input channel:

The path to CHKIN is through an indirect RAM vector at $0324. Applications may therefore provide their own BASIN procedures or supplement the system’s by redirecting this vector to their own routine.

65490 $FFD2 CHROUT/BSOUT

CHROUT (alias BSOUT) writes the character in A to the current output device (DFLTO 154/$9A). Output to devices other than the screen (the default output device) must be OPENed and CKOUTed. The character is written to the output buffer associated with the current output channel:

The path to CHROUT is through an indirect RAM vector at $0326. Applications may therefore provide their own CHROUT procedures or supplement the system’s by redirecting this vector to their own routine.

65493 $FFD5 LOAD

This routine LOADs data from an input device into C128 memory. It can also be used to VERIFY that data in memory matches that in a file. LOAD performs device-specific tasks for serial and cassette LOADs. You cannot LOAD from RS-232 devices, the screen or the keyboard. While LOAD performs all the tasks of an OPEN, it does not create any logical files as an OPEN does. Also note that LOAD cannot “wrap” memory banks. As with any FO, the I/O status is updated appropriately and can be read via READSS. LOAD has two options that the user must select:

The C128 serial LOAD routine automatically attempts to BURST load a file, and resorts to the normal load mechanism (but still using the FAST serial routines) if the BURST handshake is not returned.

The path to LOAD is through an indirect RAM vector at $0330. Applications may therefore provide their own LOAD procedures or supplement the system procedures by redirecting this vector to their own routine.

Example

  LDA	#length       // fnlen
  LDX	#filename
  JSR	$FFBD         // SETNAM

  LDA	#0            // load/verify bank (RAM0)
  LDX	#0            // fnbank (RAM0)
  JSR	$FF68         // SETBNK

  LDA	#0            // la (not used)
  LDX	#8            // fa
  LDY	#$FF          // sa (SA>0 normal load)
  JSR	$FFBA         // SETLFS

  LDA	#0            //  load, not verify
  LDX	#<load adr    // (used only if SA = 0)
  LDY	#>load adr    // (used only if SA = 0)
  JSR	$FFD5         // LOAD
  BCS	error
  STX	end_lo
  STY	end_hi

65496 $FFD8 SAVE

This routine SAVEs data from C128 memory to an output device. SAVE per- forms device-specific tasks for serial and cassette SAVEs. You cannot SAVE from RS-232 devices, the screen or the keyboard. While SAVE performs all the tasks of an OPEN, it does not create any logical files as an OPEN does. The starting address of the area to be SAVEd must be placed in a zero-page vector and the address of this vector passed to SAVE in A at the time of the call. The address of the last byte to be SAVEd PLUS ONE is passed in X and Y at the same time. Cassette SAVEs utilize the secondary address (SA) to specify the type of tape header(s) to be generated:

SA (bit 0) = 0 -> relocatable  (blf) file
           = 1 -> absolute     (plf) file
SA (bit 1) = 0 -> normal end
           = 1 -> write EOT header at end

There is no BURST save; the normal FAST serial routines are used. As with any I/O, the I/O status will be updated appropriately and can be read via READST.

The path to SAVE is through an indirect RAM vector at $0332. Applications may therefore provide their own SAVE procedures or supplement the system’s by redirecting this vector to their own routine.

Example

// SAVE "program",8
  LDA #length     // fnlen
  LDX #<filename  // fnadr
  LDY #>filename
  JSR $FFBD       // SETNAM

  LDA #0          // save from bank (RAM0)
  LDX #0          // fnbank (RAM0)
  JSR $FF68       // SETBNK

  LDA #0          // la (not used)
  LDX #8          // fa
  LDY #0          // sa (cassette only)
  JSR $FFBA       // SETLFS

  LDA #start      // pointer to start address
  LDX end         // ending address lo
  LDY end+1       // ending adr hi
  JSR $FFD8       // SAVE
  BCS error
filename  .BYTE "program
length    = 7
start     .WORD address1		// page-0
end       .WORD address2

65499 $FFDB SETTIM

SETTIM sets the system software (jiffie) clock, which counts sixtieths (1/60) of a second. The timer is incremented during system IRQ processing (see UDTIM), and reset at the 24-hour point. SETTIM disables IRQ’s, updates the three-byte timer with the contents of A, X and Y, and re-enables IRQ’s.

65502 $FFDE RDTIM

RDTIM reads the system software (jiffie) clock, which counts sixtieths (1/60) of a second. The timer is incremented during system IRQ processing (see UDTIM), and reset at the 24-hour point. RDTIM disables IRQ’s, loads A, X and Y with the contents of the 3-byte timer, and re-enables IRQ’s.

65505 $FFE1 STOP

STOP checks a Kernal variable STKEY (145/$91), which is updated by UDTIM during normal IRQ processing and contains the last scan of keyboard column C7. The STOP key is bit 7, which will be 0 if the key is down. If it is, default I/O channels are restored via CLRCH and the keyboard queue is flushed by resetting NDX ($D0). The keys on keyboard line C7 are:

Bit 7 6 5 4 3 2 1 0
Key STOP Q C= SPACE 2 CTRL <- 1

The path to STOP is through an indirect RAM vector at $0328. Applications may therefore provide their own STOP procedures or supplement the system’s by redirecting this vector to their own routine.

65508 $FFE4 GETIN

GETIN reads a character from the current input device (DFLTN 153/$99) buffer and returns it in A. Input from devices other than the keyboard (the default input device) must be OPENed and CHKINed. The character is read from the input buffer associated with the current input channel:

The path to GETIN is through an indirect RAM vector at $032A. Applications may therefore provide their own GETIN procedures or supplement the system’s by redirecting this vector to their own routine.

65514 $FFEA UDTIM

UDTIM increments the system software (jiffie) clock, which counts sixtieths (1/60) of a second when called by the system 60Hz IRQ. TIME, a 3-byte counter located at $A0, is reset at the 24-hour point. UDTIM also decrements TIMER, also a 3-byte counter, located at $AlD (BASIC uses this for the SLEEP command, for example). You should be sure IRQ’s are disabled before calling UDTIM to prevent system calls to UDTIM while you are modifying TIME and TIMER. UDTIM also scans key line C7, on which the STOP key lies, and stores the result in STKEY (145/$91). The Kernal routine STOP utilizes this variable.

65511 $FFE7 CLALL

CLALL deletes all logical file table entries by resetting the table index, LDTND (152/$98). It clears current serial channels (if any) and restores the default I/O channels via CLRCHN. The path to CLALL is through an indirect RAM vector at $032C. Applications may therefore provide their own CLALL procedures or supplement the system’s by redirecting this vector to their own routine.

65517 $FFED SCRORG

SCRORG is an Editor routine that has been slightly changed from previous CBM systems. Instead of returning the maximum SCREEN dimensions in X and Y, the C128 SCRORG returns the current WINDOW dimensions. It does return the maximum SCREEN width in A. These changes make it possible for applications to “fit” themselves on the current screen window. SCRORG is also an Editor jump table entry ($C00F).

65520 $FFF0 PLOT

PLOT is an Editor routine that has been slightly changed from previous CBM systems. Instead of using absolute coordinates when referencing the cursor position, PLOT now uses relative coordinates, based upon the current screen window. The following local Editor variables are useful:

When called with the carry status set, PLOT returns the current cursor position relative to the current window origin (not screen origin). When called with the carry status clear, PLOT attempts to move the cursor to the indicated line and column relative to the current window origin (not screen origin). PLOT will return a clear carry status if the cursor was moved, and a set carry status if the requested position was outside the current window (no change has been made).

Example

  SEC
  JSR $FFF0   // read current position
  INX         // move down one line
  INY         // move right one space
  CLC
  JSR $FFF0   // set cursor position
  BCS error   // new position outside window

65523 $FFF3 IOBASE

IOBASE is not used in the C128 but is included for compatibility and completeness. It returns the address of the I/O block in X and Y.