Device manager startup screen.
Documentation and technical notes for the C128 device manager.

What started out as a simple device id switcher/muter turned into a pretty complex beast, with extended power-on self test, diagnostics, system information, boot manager, fastload capabilities and some fixes for bugs in the 128 kernal and jiffydos128. But that complexity also means there are more situations in which things may not work as expected, and while many complex configurations should be auto detected, there are corner cases where this will not work. Hence, it may be helpfull to understand some of the technical details. This will also be the closest to official documentation you will get.

Power on and self test sequence

At power-on, the very first thing that will run once the 8502 cpu gets control is a tiny bit of kernal code. This code will check for autostart function roms, and start them when found. At this point the device manager is started and will run its initial test to see if it is allowed to, and able to run.

The first 2 things this test does is check for the escape key (which will make it disable itself) and run-stop key, which will drop you into the ML monitor.

After this, it will check if zeropage ram works properly, if not it will turn the vic2 display red and stop.

If ZP ram is ok, stack ram will be tested. If this fails, the system will stop with a yellow vic2 display.

Third, the memory at $7c00-7cff will be tested, the remainder of the self test needs this memory. If this fails, the system will stop with a brown vic2 display.

After this, the system will switch to 2mhz mode and test if this works reliably. If the function rom is running as C128 external rom from an Ultimate II(+) cartridge, it will also determine if the CPU address ready after phi2 setting is correct.

If running on a 40 column display, the system will now switch back to 1mhz, else it will keep running at 2mhz until the device manager exists.

With all this said and done, it is time to start the remainder of the power-on self test, which consists of a test of the mmu and a fast ram test.

After the self-test

Once the self-test completes, the system will reset and start the device manager. This will wait for a few seconds, giving you a chance to press ins/del to enter the advanced menu, or press some keys to change the behavior. Press space to make it pause, and press space again to make it continue.

IEC bus probing

The device manager will probe all IEC bus IDs from 4 to 24 to see if anything responds. If so, it either makes an assumption about the device (IDs < 8) or it will probe the device for its type. 15xx drives, CMD devices etc will be detected by reading small bits of their rom and comparing this against expected values. For devices that do not support this functionality, the UI command is used to check the device init message.

If a UII(+) cartridge is detected, the ultimate command interface will be used to check the power state of drive a and b (if present), and both drives will be temporarily disabled when the initial device scan is running. Also, if a UII is detected, any devices found during the initial scan below ID 12 will be moved to an ID above 24. After the scan, drive a and b will be enabled again, and DM will determine if their IDs would conflict with previously moved devices.

This way DM can detect physical and virtual devices with conflicting IDs. This obviously does not work for conflicts between physical devices. If such a conflict exists, the IEC probing will never finish!!

Once all information is gathered, and based on options selected in the startup screen, DM will decide how to deal with conflicting devices, or ask the user about this.

Device 8 selection

If ID 8 is empty, or the device at ID 8 supports changing its ID, it can be swapped with any other device that supports ID changing. Just before the system enters 128 or 64 mode, you'll be presented with a list of devices to select the one to use for ID 8.

128 mode 'phoenix' stage

Phoenix is the name of the kernal code responsible for starting expansion roms and checking for a bootable disk in drive #8. While most of the DM code runs very early, even before most of kernal initialization takes place, part of it runs as part of the phoenix boot stage. This happens as last step in the startup of BASIC, and will check if the IO and NMI handlers can and should be enabled. After this control is passed back and C128 autoboot starts.

C128 and autoboot

The C128 will reset a device as part of trying to autoboot from it, which will move that device back to its initial device ID, at least in case of Commodore 15xx drives. This does not happen with a sd2iec, which will stick at its current device ID. The consequence is you can only boot from a 15xx device when its default ID is 8. Consequently, you can have UII drive A configured at id 8, and a 1571 also configured at id 8, and autoboot from either device. But you cannot autoboot from a 1571 when its default id is not 8. This will be solved in a future version but requires a rewrite of the C128 autoboot code.

IO rom mirror

When running as C128 external rom from a UII(+) cartridge, extra functionality is available. This functionality can also be obtained with an eprom on a cartridge but requires special hardware.

In order to be nearly invisible to the system and any running software, DM uses some rom mirrored to the $de00-$dfff area (similar to how many C64 utility cartridges do). This is a feature on the UII(+) cart, and allows some of the code to be available while the rom is not mapped into memory.

In order to make a cartridge capable of this, you will have to map io areas 1 and 2 to offset $5e00 and $5f00 of the rom respectively.

While the DM rom uses the entire io1 and io2 ranges, it leaves small holes for the ACIA/swiftlink interface at $de00, and the REU and Ultimate Command Interface at $df00, and hence does not conflict with those when running on an UII(+) cartridge.<

This bit of rom is used by the NMI handler and IO handler code, and hence the JiffyDOS IRQ fix and udrive support depend on this, and will be disabled if this rom mirror is not available.

IO handler

The IO handler takes care of fast access to usb filesystems and images on a UII+ cartridge (UII currently not supported for this feature). This also provides a framework for adding disk speeders like sjdos or the fc3 disk speeder, and things like ramdos. Which of those will actually get added will depend on available rom space, ramdos is a comparatively large piece of code, and while only part of that is needed for a preloaded ramdisk, setting up a new ramdisk would still require running software from disk. Either sjdos or fc3 disk speeder are likely candidates to get added at some point.

JiffyDOS IRQ fix

If a JiffyDOS 128 kernal is detected, the IO handler will enable fix for a bug in JiffyDOS. This bug exists because JiffyDOS fails to make sure it sets bit 7 of $d011 to 0 when it enables the vic2 display after a load. This is a problem only when the load happens to start at a display line > 255. Unlike a C64, the C128 uses a raster interupt for its keyboard scan. This raster interupt is at line 254 (actually runs at 255), and if bit 7 in $d011 is set to 1, the vic2 will now be waiting for an impossible scanline, hence no raster irq will happen and the system appears to hang. The IO handler will intercept all loads, call JiffyDOS to do its job, and force bit 7 of $d011 to 0 after JiffyDOS is done and before returning control to the caller.

NMI and basic reset redirection

If the IO rom mirror is available, an NMI and basic warm start handler are installed. The NMI handler adds a CTRL+RESTORE key combination to reset the VDC to its default state and upload fresh character data. The NMI handler works in conjunction with the basic warm start handler, which ensures all vectors for io, nmi etc are set to point to the DM rom functions. This happens whenever you press STOP+RESTORE, exit the monitor, etc, even if the NMI handler got overwritten or reset (by for example STOP+RESTORE). Hence, pressing STOP+RESTORE should re-enable the fastload and jiffydos fix etc if it ever gets disabled by something (some software insists on calling a reset vectors when exiting to basic, probably to clean up the mess it created.. not that this step helps, but it happens).

RAM useage

DM needs a very tiny bit of ram while the system is running if the IO and nmi handlers are enabled. This concerns the following addresses:

  • $02a1: device ID for ultimate drive, can be changed, but don't do silly things like setting it to 0 or 3.. 1 however would work (but is not supported)
  • $03e4-$03eb: those addresses are used for temp data during io calls, nothing will get lost when overwriting data there, but count on whatever you put there getting overwritten.
  • $03ec-$03ef: those addresses are used for data that needs to persist between io calls, overwriting those bytes will cause the udrive support to fail.
  • $03fd-$03ff: those addresses are used for data that needs to persist between io calls, overwriting 03fd or 03fe may cause corrupt data to be read, overwriting $03ff may cause unexpected behavior of the io and nmi handler as this byte contains important flag and configuration bits.

Load, save and DMA

The redirected kernal load and save routines for the ultimate drive use DMA for data transfer, which makes them very very fast, but has a few peculiarities.

This is a result of how the mmu in a 128 works. It has a separate register for the ram block to be used by DMA. This is independent from the ram block the CPU uses, and also bypasses the shared ram overlay as seen by the CPU. However, VIC II uses the same register for where to look for its video buffer. This has 2 consequences:

  • While loading to/saving from ram block 1, the VIC II output will be incorrect for a brief time while the transfer takes place. This may be visible during startup of some C128 games for example.
  • It is possible to load/save the $0000-$03ff range of ram block 1, which is not possible with the regular kernal load/save, as this will end up in the shared ram block (unless you turn that off, but that comes with its own can of worms).

Another peculiarity of DMA load/save is how addresses above $feff are handled. While kernal is ok with saving that range, it does not allow loading past $feff (out of memory error). DMA loads will continue to $ffff, but the default irq/nmi/reset handlers and other required content will be restored after that. This is an artifact of the UII+ DMA transfer.

This allows a few things not possible with the regular load/save routines, but should be highly backward compatible, and every 'bank' recognized by kernal will work as expected, including things like loading to/saving from ram below rom and io space when using banks 0 or 1.

sequential io

Sequential io on the ultimate drive is also accelerated, performance depends a fair bit on how your code reads/writes sequential data. Reading one char a time between each chkin and clrchn will be slow, reading chunks of 256 bytes at a time will be very efficient.

There is much more to say here.. and probably more will follow in due time.