a2d icon indicating copy to clipboard operation
a2d copied to clipboard

Handle mirrored SmartPort drives in ProDOS 2.x

Open inexorabletash opened this issue 2 years ago • 3 comments

lib/smartport.s is used to map a ProDOS unit number (DSSSxxxx) to a SmartPort entry vector and unit, but this is limited.

ProDOS 1.2...1.9 mirror S5,D3/4 to S2,D1/2 only, and leave DEVADR entry pointing at $C5xx. Therefore, if the driver address is firmware ($Cn): If the unit number slot matches the driver slot, the device is not mirrored, and SmartPort unit is 1 or 2. Otherwise, the device is mirrored, and SmartPort unit is 3 or 4. So ProDOS 1.x is handled as best it can be.

But ProDOS 2.x mirror to non-device slots, and point DEVADR at RAM-based drivers, not firmware entry points. If the device is not mirrored, a firmware entry point will be used, and therefore the unit number slot will match the driver slot, and the drive gives the SmartPort unit as 1 or 2. But further mirrored drives are impossible to determine given data external to ProDOS, without exactly replicating ProDOS's remapping algorithm for a particular version, which is fragile.

All extant versions of ProDOS that remap (2.0 through 2.0.3, 2.4.x, and ProDOS.FX) use a similar internal approach:

  • Point the DEVADR entries at a procedure that maps ProDOS calls to SmartPort calls.
  • The procedure maps the DSSS bits of the unit number into an offset.
  • The offset is used to look up the SmartPort entry vector and unit in tables. These tables are populated on boot.

The unfortunately, the tables vary in memory location across versions of ProDOS. Fortunately, so does the map proc address placed in DEVADR. This means that the DEVADR can be used as key into a table to find the mapping tables.

Version DEVADR spunit-1 spvectlo-1 spvecthi-1
ProDOS 2.0 $FD1D $FD5C $FD6A $FD78
ProDOS 2.0.1 $FD08 $D6EF $FD6E $FD7D
ProDOS 2.0.2 $FD08 $D6EF $FD6E $FD7D
ProDOS 2.0.3 $FD08 $D6EF $FD6E $FD7D
ProDOS 2.4 $FCE6 $D6EF $FD51 $FD60
ProDOS 2.4.1 $FCE6 $D6EF $FD51 $FD60
ProDOS 2.4.2 $FCE6 $D6EF $FD51 $FD60
ProDOS FX 0.94 $FCA6 $FE6F $FE7E $FE8D
ProDOS FX HEAD $FC8B $FE54 $FE63 $FE72

NOTE: The "-1" notation is because DSSS is used as an index into a table, and because Slot 0 doesn't exist, each table has 15 entries (starting at S1,D1) rather than 16 to save space. So in the ProDOS source, spunit is actually one greater than the value given above. A byte is wasted for S0,D2.

Further observations:

  • A2D ships with ProDOS 2.4.x so supporting just that is a good starting point.
  • I looked at two different ProDOS.FX builds. On the plus side, they have different DEVADR values for mirrored drives. On the minus side, they aren't stable across FX builds, so there's a remote chance of a collision some day. But back on the plus side, they seem to be moving in a safe direction: 2.0.x = $FD08, 2.4.x = $FCE6, FX 0.94 circa 2020 = $FCA6, FX from this last week = $FC8B. Yay?
  • All the remapping procs share a similar structure in the code, so rather than hardcoding anything the following could be used:
    1. Look at the 3 bytes preceding the DEVADR entry. If it's not 4C 00 10 (JMP $1000), fail.
    2. Scan forward from that for 4A 4A 4A 4A AA (LSR x4, TAX) which maps unit number to offset.
    3. This should be followed by LDA spunit-1,X / STA ... / LDA spvecl-1,X / STA ... / LDA spvech-1,X / STA ...

...

Supporting this would enable the following functionality for mirrored drives:

  • DeskTop: Special > Eject Disk (or drag to trash)
  • DeskTop: Polling for disks manually ejected
  • DeskTop: Polling for disks inserted
  • DeskTop: Correct device names in Special > Format a Disk... and Special > Erase a Disk...
  • Disk Copy: Automatic ejection when swapping disks

inexorabletash avatar Mar 31 '22 06:03 inexorabletash

Also for reference, ProDOS 2.0.3 source, via the A2osX crew:

inexorabletash avatar Mar 31 '22 06:03 inexorabletash

I handle this differently in ProRWTS - just brute-forcing the device number, after detecting that it has been mirrored, by iterating through all device numbers and issuing a read until I get a match.

peterferrie avatar Mar 31 '22 10:03 peterferrie

An alternative approach would be (per the suggestion above) to construct mappings on startup. Make the MLI ONLINE call and then iterate over all SmartPort controllers/devices and grab the directory blocks, matching names as @peterferrie suggests. This could then populate tables exactly like the ProDOS internal tables mapping DSSS to spunit/spvect.

  • DeskTop would do this on startup (in desktop/init.s), and stash the tables somewhere handy in main.
  • Disk Copy is launched from DeskTop and could "steal" the tables during its bootstrap.
  • Desktop.System techncially uses this mapping code as well as of f6cdeb6, but it's really just using it to search for RAM disks, so mapped drives could simply be ignored.

inexorabletash avatar Apr 01 '22 04:04 inexorabletash

I took a stab at supporting ProDOS 2.0. The ProDOS code for that version must be buggy - with a mirrored drive (at least in GSPlus), calls to ON_LINE cause a crash.

Given that, I'm going to call this item "done" as the dominant versions of ProDOS are supported. If anyone wants to do a more robust implementation I'd accept it, though.

inexorabletash avatar Sep 12 '22 01:09 inexorabletash

If you have a disk image that reproduces the crash, I'd be happy to investigate.

peterferrie avatar Sep 12 '22 01:09 peterferrie