a2d
a2d copied to clipboard
Handle mirrored SmartPort drives in ProDOS 2.x
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:
- Look at the 3 bytes preceding the
DEVADR
entry. If it's not4C 00 10
(JMP $1000
), fail. - Scan forward from that for
4A 4A 4A 4A AA
(LSR
x4,TAX
) which maps unit number to offset. - This should be followed by
LDA spunit-1,X
/STA ...
/LDA spvecl-1,X
/STA ...
/LDA spvech-1,X
/STA ...
- Look at the 3 bytes preceding the
...
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
Also for reference, ProDOS 2.0.3 source, via the A2osX crew:
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.
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.
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.
If you have a disk image that reproduces the crash, I'd be happy to investigate.