Is it really that irrelevant? I mean, if you look past the specifics (directories, interrupts, DOS versions), this seems to be implementing the idea of bringing something into scope, in particular bringing it into scope from the outside, to modify the behavior of the consumer (here, assembler) without modifying the consumer itself. Today, we'd do the equivalent with `ln -sr ../../inc ../inc`.
I'd argue the general idea remains very important and useful today, though it's definitely not obvious looking back from the future what this was what APPEND was going for.
But back to the APPEND: in all my time working with MS-DOS, I don't remember ever needing that, so it was 100% irrelevant to me. But this could be because I've worked with more "modern" programs (like Turb Pascal 5) which had good support for directories.
I, too, remember the trifecta of APPEND, JOIN, and SUBST. And while I always thought they were interesting, I was also wondering for most of them when I would ever use that. At the time, DOS versions and hence applications for it that don’t know subdirectories didn’t cross my mind, as my first DOS version was 2.11, I think.
That’s like trying to stream over a 3G mobile network with substantial packet loss, except it’s physically inside your computer.
I've also used SUBST when reorganizing network drive mappings.
Even windows 3.x and 95 (surpisingly) ran faster with smartdrv preloaded. 95’s default cache for some reason was worse than smartdrv and literally produced harder sounds on my hdds.
The second favorite was a TSR NG viewer, can’t remember the name.
I know what a TSR is, but what's an "NG viewer"?
My (weak) guess is that you "32 bit disk access" and "32 bit file access" wasn't active then, i.e. Windows 95 did not use its native 32 bit disk drivers, but 16 bit BIOS access. I have a hard time seeing how Smartdrv could have done anything otherwise, unless it really had some tight integration with 32 bit Windows (which would be very surprising, it's a 16 bit TSR first and foremost).
But yeah, overall, I agree, it's surprising what even a tiny cache does. If you just manage to eliminate the seek times to the root directory for pretty much every single access to a new file, that can probably do a lot already. Especially in the days before "native command queuing", where the HD would try to reorder disk accesses to find a better (physical!) head seek path across the platter. Later HDs had some cache on board.
The downside was, that in case of a crash, you will use some data.
It’s interesting what others experienced, cause I remember nothing about that in fido echoes (someone should have lost some data eventually). Was smartdrv even that different from today’s caching algorithms on all OSes?
Came in pretty handy when I wanted to share a folder with Remote Desktop, but it would only let me select whole drives.
Made a SUBST drive letter for that folder, worked like a charm!
You can either see this by using "NtQuerySymbolicLinkObject" on "\??\M:", or calling "QueryDosDeviceW" on "M:". On Windows NT, you will see the result as an NT-native path "\??\C:\target_path" rather than a Win32-style path "C:\target_path".
"\??\" is not some kind of special notation for paths or anything, it is a real NT object that exists. It holds your drive letters and other things.
On Windows 9x, you won't see an NT-native path from QueryDosDevice, you'll instead see a Win32-style path "C:\target_path".
Weirdly enough, Sysinternals Winobj is unable to find the symbolic link object at all, despite that it exists when you query it using NT API calls.
Fun fact about NT native paths, you can use them in Win32 if you prefix them with "\\?\GLOBALROOT". So "\??\C:\" becomes "\\?\GLOBALROOT\??\C:\". You can use it in any Win32 program that doesn't actively block that kind of path (such as the file explorer/open dialog)
It is run from a registry key upon bootup.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices]
"P:"="\\??\\C:\\Dev\\Source"
Change source path accordingly, save as .reg file, import once and it'll stay.Nice thing about this vs using SUBST is that the SUBST is for your user only, so if you have a separate admin account it won't see it. However the above registry entry is for the machine, so all users on the machine will see it.
Obviously makes it less useful for terminal servers and other shared machines.
The same applies to network drive letter mappings.
Under the hood, both are implemented as NT Object Manager symbolic links, which you can see using, e.g.,
https://learn.microsoft.com/en-us/sysinternals/downloads/win...
Canonicalizing the path resolves all drive letters and symbolic links. The Rust function that canonicalizes a path can fail on some mounted filesystems if the drive letter wasn't registered using the Windows Mount Manager.
The idea of everybody having the same paths for things never sat right with me, because it's an easy way for absolute paths to sneak in, which can be (and occasionally was) a problem when trying to support multiple branches later in the project lifecycle. But if you've got a CI system, which people typically do in 2024, that's much less of an issue (because you can arrange for the CI system to build from a random path each time). And it is pretty handy when you can paste universally-usable paths into the chat.
Or you can end up having to use the magic path for all builds.
At work, we have a subst’d drive. The way we set it up means that some <evil crap> requires it (by the virtue of hardcoding, or by common configuration). But I do most of my development from a fully separate, leaner copy of the repo, and consider the subst drive to be a garbage pile.
Now the chance of introducing dependencies on the standard path is much lower. You can do it, and it will work for you, and it will work for everybody else too when they get your change. But at some point, the CI system will get it, and it will fail, and you'll know that you have something to fix.
You go on folder up and use subst to make a drive letter from which you can delete content.
Win32 allows you to use paths above MAX_PATH in length when you 1. Use the utf-16 filename apis and 2. prefix your path with \\?\.
But the shell doesn't do the latter. It may also use fixed size buffers for paths, that is a common reason for such limitations.
Are there any good books on DOS that a person who enjoys articles like this may also enjoy?
And more broadly, does anyone have any good books to suggest about the personal computers of the 80s and 90s, that don't skimp on the messy technical details?
In order for the mux to work, each TSR had to have its own identifier code. Other than some reserved ranges, no one organized such a namespace, meaning it was possible for two or more TSRs to intercept the same request, leading to the same collision problem.
This note from Ralf Brown's Interrupt List has the gory details:
http://www.ctyme.com/intr/rb-4251.htm
Incomplete list of TSRs and drivers relying on it:
Int 2F is initially handled by DOS as a stub, but additional programs (like drivers and TSRs) can override INT 2F, put their bucket of functionality and then fallback to whatever the previous installed handler was (called chaining) for whatever they don't handle.
This gives a glimpse into how much various crap could end up installed as an Int 2F handler: https://www.minuszerodegrees.net/websitecopies/Linux.old/doc...
It was often used for feature/presence checks and usually nothing time critical as that chaining setup was most definitely not timing friendly.
Lot's of crap in INT 21 too: https://fd.lod.bz/rbil/zint/index_21.html (on the whole I like this HTMLified version of Ralf Brown's Interrupt List better)
But I suppose they invented INT 2F to discourage people from doing that to INT 21.
And then Ralf Brown also proposed an alternative multiplex interrupt, INT 2D: https://fd.lod.bz/rbil/interrup/tsr/2d.html#4258
Overriding software interrupt handlers was a common mechanism, applied to BIOS interrupts as well (e.g. there was a BIOS timer interrupt that was there just for that).
The idea was that programs would take over the interrupt vector then chain-call the previous handlers. One can still see something similar at play in certain scenarios with e.g. callbacks. The system/library doesn't have to maintain a list of clients.
Nitpick, but DEC never made a mainframe. Their products like the PDP-11 were considered minicomputers (even though the CPU was the size of a fridge) to distinguish them from IBM’s mainframes and medium sized computers.
But the PDP-10 and VAX 9000 were basically mainframes. Million dollars or more. Whole large room with three phase power. Standard building AC might suffice but that was pushing the margin. And the faster clocked VAX 9000 was water cooled! That's not a minicomputer.
However the VAX 9000 wasn't. It was initially designed to be water cooled but during development they improved the air cooling enough that water cooling was never shipped.
One VAX 9000 CPU did around 70k Dhrystones/sec, so 40 VAX MIPS (DMIPS as we call them now).
A modern simple RISC 5-stage in-order CPU (e.g. Berkeley RISC-V Rocket) with decent branch prediction does 1.6 DMIPS/MHz, so will need 25 MHz to match the VAX 9000. However the December 2016 HiFive1 ran at 320 MHz, so would be around a dozen times faster than the VAX 9000 -- but without FPU or MMU. Today, a $5 Milk-V Duo running at 1 GHz (64 bit, and with MMU and FPU and vector unit) will be 40x faster than a VAX 9000.
The VAX 9000 has a vector unit, capable of a peak 125 MFLOPS. The Duo's C906 VFMACC.VV instruction has 4 cycles latency, for two 64-bit multiply-adds or four 32-bit multiply-adds, so at 1 GHz that's 1 GFLOP double precision or 2 GFLOP single precision, 8x a VAX 9000.
Don't even ask what a modern i9 or Ryzen can do!
Why is that not a minicomputer. From our perspective it's a massive installation; from the perspective of the time, it was not necessarily.
Really there is no good formal definition, dividing computers into three groups(mainframe, minicomputer, microcomputer) based on how much you payed for it, is as good a mechanism for figuring this out as any other.
The 750 was later technology, IIRC MOSFET. It also lacked the 780’s “compatibility mode”, which allowed VMS to run 16 bit RSX-11 executables by implementing the PDP-11 instruction set in addition to the 32 bit VAX one, and could boot without a console processor. If you didn’t need all the users on one machine, the 750 was cheaper per user.
Assuming, of course, that those "medium sized computers" are the midrange.
Ultimately, within IBM at least, "mainframe" ended up just referring to any computer that implemented the System/360 architecture or its descendants.
Outside IBM, I've seen the term applied to just about any multiuser system accessed primarily through a terminal interface, including x86 servers running Linux, though typically by nontechnical users.