31

From the Wikipedia article on reserved device names

... and the CLOCK$ (still named CLOCK in some issues of MS-DOS 2.11) clock device were introduced with DOS 2.0, ...

Why was is necessary to add a $ character to this device name?
And why didn't they name this device CLK which would have made sense in a list such as CON, AUX, PRN, LST, NUL?

Microsoft advices against using these device names as filenames regardless of extension. Perhaps too many users couldn't resist having CLOCK as a filename?

Sep Roland
  • 1,043
  • 5
  • 14
  • 3
    What was the name of the clock application that was in Windows 1.0? I'd guess there's a fair chance it was called CLOCK ... – dave Apr 03 '22 at 21:45
  • 5
    @another-dave At least in Windows 3.1, I see the WINDOWS directory has a CLOCK.EXE and a CLOCK.INI. – Sep Roland Apr 03 '22 at 21:56
  • 4
    I (mercifully) have forgotten the vagaries of the DOS FAT implementation. Wasn't it the case that if the filename was a known device name, any extension specified by the program was ignored? If so, that's surely the answer. – dave Apr 03 '22 at 22:28
  • 4
    @another-dave Except that would have been an anachronism, because Windows 1.0 was released when DOS 3.10 was already available, so it couldn’t have influenced the development of DOS 2. On the other and, perhaps other, older clock apps already started to appear (or were expected to appear)… – user3840170 Apr 04 '22 at 06:51
  • 4
  • 1
    @user3840170 Thanks for the link to this most interesting article. The very last comment there sums it up: "Not requiring the colon (which was allowed but optional) seems to have been the fatal flaw. Once there was no way to distinguish between a file and a device based on its pathname, there was just no good way out." – Sep Roland Apr 04 '22 at 16:22

3 Answers3

38

Quick partial answer:

  • CON, LST, PRN and AUX (*1) are names inherited from CP/M, either direct form its BIOS or CP/M's main file handling utility PIP (*2). NUL was add with DOS.

  • DOS 1.x added additional names for installable interfaces like LPT1..4 or COM1..4

  • CLOCK was introduced with DOS 2.0

But DOS 2.0 added loadable device drivers as well, each able to bring any arbitrary name of its own. Basically not a big issue, but it would make it quite hard for MS to make sure that any new name they may come up in future versions has not been taken by any third party driver.

To solve this MS defined that all future (MS) device names, except for the DOS1.x like names, which may be present at boot, has to include a $ sign as last character (*3).

Problem solved - except, that definition was only made after DOS 2.0 was published, so CLOCK came preinstalled in DOS 2.0 without a $-suffix, a lapse fixed in DOS 2.1.

CLOCK$ was, BTW, not the only one added that way, so was SCREEN$, KEYBD$ or MOUSE$.


*1 - there were a few more, like RDR (Reader) or PCH (Punch), that got added over time, but didn't make it to MS-DOS

*2 - CP/M PIP in turn inherited them from RSTS/E.

*3 - They had to pick one that had no other meaning at the time and would be valid wherever a device name is possible.

Raffzahn
  • 222,541
  • 22
  • 631
  • 918
  • 2
    And RSTS inherited them from TOPS-10. – dave Apr 03 '22 at 23:42
  • 4
    Incidentally, the 'symbols with a dollar sign are reserved to us' was also a DEC convention, certainly on RSX-11M systems (I forget about RSTS). – dave Apr 04 '22 at 00:56
  • 3
    ‘To solve this MS defined that all future (MS) device names, except for the DOS1.x like names, which may be present at boot, has to include a $ sign as last character’ — where did they define that? Why didn’t devices like XMSXXXX0 (HIMEM.SYS), EMMXXXX0 (EMM386.EXE), SETVERXX (SETVER.EXE), _doubleB (SMARTDRV.EXE) or MSCD0001 (commonly used with MSCDEX.EXE) obey this? – user3840170 Apr 04 '22 at 06:35
  • 2
    @user3840170 The keywords within the cited sentence are present at boot. CLOCK is such a a build in driver. Ofc, the same issue exists with loadable drivers, thus the 'fancy' names - which some even allow to be set (e.g. MSCDEX' /D:xxx). – Raffzahn Apr 04 '22 at 09:18
  • 1
    It might be worth noting that the undecorated names- CON, LST and so on- all handle a stream of characters. That is not necessarily the case with devices such a CLOCK$, which generally speaking have device-specific interfaces access via an interrupt or locateable entry point. – Mark Morgan Lloyd Apr 04 '22 at 09:56
  • 4
    @MarkMorganLloyd CLOCK$ also needs to handle a stream of characters; specifically, sequences of three words. Try COPY CLOCK$ CON ;-). – Stephen Kitt Apr 04 '22 at 10:07
  • 1
    @MarkMorganLloyd CLOCK$ is a regular character device, thus ofc able to handle a character stream - in fact, it does even useful stuff. Just try reading it :)) Interrupts and 'local entry points' are not any interfaces to a device driver, that's what IOCTRL is for. Installing additional interrupts or chaining existing is about providing additional services, which can as well be done by any TSR. – Raffzahn Apr 04 '22 at 10:18
  • 1
    Why make this artificial distinction between in-kernel and loadable drivers? By the time of Windows Millennium, the XMSXXXX0 driver was integrated into the DOS kernel anyway (at which point they could not rename it any more). Was suffixing with $ actually adopted anywhere as a principle, or was it just a bunch of ad-hoc decisions? Because if the latter, saying they ‘defined that all future (MS) device names’ should be so named is a stretch at best. – user3840170 Apr 04 '22 at 10:33
  • 3
    @Raffzahn Yes, it /is/ a standard device, but in this type of situation a name and the standard stream API (not that that term was in common use in the early 80s) is more a convenience than a requirement. And in DOS the ioctl() function was woefully underused: it was far more common to find that once a driver or TSR was in memory it used some proprietary interface... which of course is where the Blessed Ralph's magnum opus was important. – Mark Morgan Lloyd Apr 04 '22 at 10:52
  • 1
    @MarkMorganLloyd The fact that hobbyist programming was rather standard in DOS can not really be used as a reasoning, can it? – Raffzahn Apr 04 '22 at 11:08
  • 1
    @user3840170 It was integrated afterwards, so staying compatible is mandatory, isn't it. In general, arguing backwards isn't really of any merit - unless you'd own a time machien. – Raffzahn Apr 04 '22 at 11:10
  • 1
    @Raffzahn Who the heck said anything about hobbyists? Look at the APIs provided by any of the big companies intended to access e.g. tape drives (QIC etc.), or intended to support graphics (GKS) or SCSI via ASPI. For that matter look at the extension APIs provided by IBM: I've wasted months sorting out control flow in one of those after they started shipping hardware that was /just/ too fast to run their code reliably. I can assure you that I'd have loved it if they'd made some effort to use ioctl() in a consistent fashion... but the truth is that they didn't. – Mark Morgan Lloyd Apr 04 '22 at 11:20
  • 1
    @MarkMorganLloyd Didn't say anything about hobbyists, but hobbyist programming - as in doing direct hardware access or peeking around memory when there are standard interfaces and procedures that would ensure compatibility. Working for a 'big' company doesn't rectify writing bad code, or does it? – Raffzahn Apr 04 '22 at 11:28
  • 1
    @Raffzahn it appears to have escaped you that the examples I gave were intended to avoid people having to go direct to the hardware. – Mark Morgan Lloyd Apr 04 '22 at 11:47
  • 1
    @MarkMorganLloyd It feels as if this is slipping. Point made by you was that drivers (of whatever kind) used proprietary interfaces instead of IOCTL, wasn't it? And that's the one my comment referrers to. It doesn't matter if these drivers tried offer a less hardware dependent access. Point it that they them self didn't follow the guidelines of using IOCTL as it should have been. (And believe me, I've been there, doing DOS since XT times, dissected (and rewritten) more than a few drivers - it was a well payed side job :) – Raffzahn Apr 04 '22 at 12:01
  • 2
    Minor correction: The Punch 'device file' in CP/M PIP was PUN:, not PCH:. In PIP these names were followed with a colon to distinguish them from normal filenames; it was MS-DOS that put them in the same namespace. – john_e Apr 04 '22 at 15:07
25

As Raffzahn explains, the clock device driver was added in DOS 2.0. CON, AUX, etc. were device names already present in DOS 1.0, some of them even earlier in CP/M; these names couldn’t be changed to preserve backwards compatibility. Of all the standard (in-kernel) device drivers, CLOCK$ is the only one which wasn’t already present in DOS 1.0.

An important subtlety in DOS is that opening a file or device (interrupt 0x21, service 0x3D when using file handles) will open a device in preference to a file for any file name matching a device, ignoring the file extension. Opening NUL, NUL.TXT etc. will all result in opening the NUL device driver, not a file on disk. Thus, any new device driver effectively replaces any file using the same name, in any directory, regardless of extension.¹ CP/M required colons (PRN: etc.) to identify device names, but this requirement was dropped in MS-DOS, which merged the namespaces.

As far as the clock device goes, DOS itself doesn’t care what the name of the device is; the clock device is identified by a specific bit in the device driver attributes (bit 3). The DOS reference manual mentions this:

MS-DOS assumes that some sort of clock is available in the system. This may either be a CMOS real-time clock or an interval timer that is initialized at boot time by the user. The CLOCK device defines and performs functions like any other character device, except that it is identified by a bit in the attribute word. The DOS uses this bit to identify it; consequently, the CLOCK device may take any name. The IBM implementation uses the name $CLOCK so as not to conflict with existing files named clock.

It would appear that the CLOCK device name was chosen, and then someone realised that it would create conflicts with any existing file matching CLOCK.*; to avoid that, the driver was renamed to $CLOCK in PC DOS, and CLOCK$ in MS-DOS (which was also the name used in DR DOS), but not before some releases ended up using that name in the wild.

Without this change, people with files named CLOCK.EXE or CLOCK.TXT etc. on floppy disks would lose access to them when they switched from DOS 1 to DOS 2. CON, NUL, AUX etc. don’t have this problem because no one would ever have been able to create such files on a PC using DOS (except by modifying the directory entry directly on disk).

All this is the reason why installable character device drivers use variously-decorated names: MSCD0001 and so on for MSCDEX, EMMXXXX0 for EMM386, the list goes on.


¹ Ignoring both the directory and extension were deliberate design decisions. DOS 1 programs didn’t know about directories, and expected to be able to open devices anywhere (see “From a Feature to a Bug” on OS/2 Museum). Some programs (particularly those ported from CP/M) also forcibly applied their own extension to whatever was provided by the user, so extensions had to be ignored so that devices could be used (being able to ask a program to use PRN or COM1 instead of a regular file could be very useful in some circumstances). DOS supports a Unix-style \DEV virtual directory, but that never gained much traction.

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
5

The $ suffix was meant to fence off the Microsoft-reserved namespace. It works the way the __ prefix fences off the implementation-reserved namespace in C and C++.

Similar to C and C++, DOS had a long legacy. The legacy devices inherited from CP/M and DOS 1.x were kept unchanged, as everyone writing custom device drivers would be aware of them. The MS-reserved namespace was there so that DOS-specific device names would not clash with those provided by 3rd-party drivers.

As for why some implementation-provided drivers, like XMM/EMS, didn't have $-prefix: they had a numeric suffix... This was another part of the device namespace MS has fenced-off for their own use. Those things weren't always written down, and absent a clear design documentation stating these constraints, the implementers often did things "similar but not quite".

Speaking of XMM and EMS specifically: the named device files wouldn't be used for anything but detecting the presence of the drivers. To use the services, you'd directly use service-specific entry points, e.g. INT 67h for EMM services. The name-based device detection was a bit more robust, as in some circumstances the non-installed interrupt vectors could point to garbage. The file-handle based access to performance-critical services such as EMM had too much overhead in the early days of DOS. DOS+BIOS made the decision to have quite a bit of system device access done by dedicated APIs, and not using the character device abstraction. Which, admittedly, was a shame.