The Long View
The Long View
Macintosh Y2020
Computers have short careers as products, but they don't just disappear when their popularity fades. In the early 1980's, when the original Macintosh operating system was under construction, nobody at Apple expected it would still be in use 35 years later, and they were right...almost. But there are lots of these machines still around and working, and there are a few people who still use them. Some woke up on the morning of January 1, 2020 unable set the date on their old Macintosh.
Those who live in glass houses
The Macintosh date-setting problem resembles the infamous Y2K issue that caused a panic in the press twenty years ago. The name 'Y2K bug' is applied to a number of different things, but in most it arose because the year in dates was represented by two decimal digits (00-99). These were interpreted as the span from 1900 to 1999. In the affected systems it was impossible to set (or worse, to represent) the date past the end of 1999. It doesn't sound like the kind of thing that would set off a panic, but pundits predicted airplanes would fall from the sky and vital hospital equipment would fail. Some of it was just hype, but anyway it was fixed by an army of programmers recruited to fix everything before the arrival of the millennium. The real cause of the Y2K bug was system designers who underestimated how long the systems would remain in use. They knew they were coding a problem into their software. They just thought nobody would be using the affected code long enough to encounter the problem.
During the peak of the Y2k brouhaha, Apple ran an advertising campaign bragging that the Macintosh was immune from such problems. They ran a television ad during the Super Bowl in January 1999. In it, HAL, the fictional computer from the film 2001:A Space Odyssey, explained that the millennium bug was caused by bad judgement on the part of programmers. By implication, Apple’s ad claimed that their programmers were better, and did not make mistakes like that. It was...not exactly accurate. Maybe nobody told the ad company that Apple had planted a similar time bomb into their operating system. It was just set to go off 20 years later.
Time must have a stop
Computers usually keep track of time by counting the number of seconds that have elapsed since some starting date. The number of seconds that can be counted depends on how the number is stored in memory. Many small systems and personal computers of the past stored time as an unsigned 32 bit integer, so time could run from 0 to 4,294,967,295 (232-1) seconds. That sounds like a lot, but seconds add up pretty quickly. There are 86,400 seconds in a day, and 30,758,400 seconds in a (non-leap) year. In a 4 year span including one leap year, there are 123,897,600 seconds. That means a 32 bit second-counter can keep time for 138 years and almost 8 months. Some systems used a signed 32 bit integer, so could only encode time for half this long.
The time and date selected for the start of time is also an important factor. For example, if a system used a signed 32 bit integer and set time 0 to be in 1970, its clock would overflow in 2038. At that point, the clocks would start over and the date would read as 1970 again.
Luckily, the designers of the Macintosh didn't do anything quite that bad. They used an unsigned 32 bit number, at least. If they had started clock time on the release date of the Macintosh, January 24, 1984, then the end time for those systems would occur in 2122. Wouldn't that have been great? They didn't do that; I don't know why. Maybe there was a good reason. Some business computers are intended to inherit old files generated on earlier generations of machines, and they need to correctly interpret the date stamps on the old files. But the Macintosh was supposed to be something new. No effort was made to make it compatible with any older system, even any older Apple computer. It seems crazy now, but time zero on the Macintosh was assigned to midnight, January 1, 1904. That's right, 80 years before the Macintosh even existed, and decades before any computer ever created a file with a date stamp. Eighty of the Macintosh's 138 years and 8 months of clock time were squandered. Macintosh clock time expires at 06:28:16 GMT Monday February 6, 2040. That’s going to be a problem for users of the old Macintosh, but it is a problem for 20 years from now. Fixing that will be possible, but it will require some deeper cutting than we need to do now. The problem now is just setting the clock.
Setting the clock
When the clock struck Midnight on Jan 1 the clocks in most old Macintoshes just ticked right on into 2020, the correct date continued to be shown in the control panel and on any files created or changed. Many retro-computing hobbyists and other who use the old machines may not know there is any problem, until they try to set the date in the Alarm Clock or Date & Time control panel. The most likely reason for wanting to set the date is a power failure in the battery powering the clock. Leaky clock batteries are a leading cause of death among old personal computers. Lots of hobbyists take them out. Of course, if you remove the battery, you need to set the clock every time the system boots up.
In the first epoch of Macintosh systems (up to and including system 6), the date field in the Alarm Clock and Control Panel shows only two decimal digits. In later versions, the date field in the control panel can be set to show the century too, but only the least significant two digits are editable. The others change on their own. In Systems 7.1-8.6, when you enter 20 in the editable digits to set the date to 2020, the date is set to 1920. Any value between 20 and 99 results in a 20th century date. Entries between 00 and 19 (inclusive), gives 21st century results. This is the problem being encountered by users now. It is like the Y2k bug, except that Apple windowed years into the range 1920-2019 instead of 1900-1999.
The most obvious solution is to give up trying to use the Date & Time Control Panel or any other built-in utility to set the date, and write an ordinary user program that sets the clock to any date within the Macintosh time span, 1904-2040. This was famously done by computing aficionado Robert Braun whose program SetDate can be been downloaded from his site and is used by most Macintosh hobbyists. This is why there was no alarm in January 2020 among the small but dedicated group of Old Macintosh enthusiasts.
But why not fix the built-in utilities instead? If Apple engineers had mapped the editable year digits to the years 1941 to 2040, this problem would not have arisen. Nobody I know wants to set the computer’s clock to 1920. Would it be possible to restore the Date & Time Control Panel to its former usefulness by just remapping the years? Maybe only a small patch is required; something like changing a 1920 in the code to 1940. This windowing approach worked for some systems as a Y2k fix.
Of course, the Macintosh had not just one, but a sequence of operating systems, starting with system 0.97 in January 1984, and ending with System 9.2.2 released in December, 2001. The programs that were used to set the time changed a lot over this period, and they don't all work alike. We will fix them all. In the process, we will take a trip through the evolution of the Macintosh operating system, the elegant simplicity of its original system, the heady extravagance of System 7, and the labyrinthine patchwork of the powerPC versions in the final days.
In the beginning: Systems 0.97-2.2
The original Macintosh operating system was contained in just two files and one ROM. One of the files was called System, and one was called Finder. Strictly speaking, Finder was not part of the operating system. It was the user-level program that ran after boot-up was complete, and it provided the user interface for a lot of essential functions like creating, copying, deleting, printing and renaming files. It also displayed information about files on disks, allowed you to change disks, and was a launcher that would start up another program when needed to display or edit a file. Finder could do a lot of things, but it couldn't set the date, so we won't have to think about it at all. The System file contained a menagerie of things, both data and code, everything in the OS that was not in the Toolbox ROM. The Toolbox was a library of useful system calls and data that could be evoked by programs. The System file had an internal structure; it was itself a kind of directory. The pieces of code and data that lived in the System were called resources, there were a lot of different kinds of them. Among the resources in the System file were some stand-alone mini-programs called Desk Accessories. These ran in the memory space of whatever program was running, and executed in parallel with it. They could create their own user interfaces in small windows that could be moved around on top of the main program's views. There could be more than one of them running, and the main program regularly donated a little time for each of them to run on a round-robin basis so they could all be doing things periodically. The way Desk Accessories were stored and executed changed as the system evolved, but they continued to work throughout the entire version sequence of the Macintosh. Most old desk accessories that ran on the original system 0.97 would continue to work in System 9.
According to Andy Hertzfeld, one of the first ever Desk Accessory was a simple clock that displayed the full date and time in a small window. It could not be used to set the time or date; it was intended only as a prototype for Desk Accessories. I think that Clock Desk Accessory may be the same one that was distributed very briefly with the first ever Guided Tour of Macintosh disk. It will prove useful while we patch two other Desk Accessories that can set the date. Before the first Macintosh release, Andy Hertzfeld wrote a Desk Accessory that could be used to change an array of system parameters, including the date and time. This was the original Control Panel, a beautiful text-free classic of graphical user interface design. It was built in collaboration with legendary pixel-artist Susan Kare. Many other pieces of Macintosh software would at some time wear the name Control Panel, but never as well as the original.
The original Macintosh release contained another Desk Accessory called Alarm Clock, written by Donn Denman. That one could also set the date and time, and it included an alarm and countdown timer as well. In the beginning these two Desk Accessories worked almost, but not exactly, the same. In later iterations of the system, their differences disappeared. Ultimately, the Alarm Clock would be removed and time setting would become the exclusive purview of the Date & Time control panel.
In these, and in all future Alarm Clocks and Control Panels, there were two ways for the user to set the year. One was to select the lower two digits in the year, and type one or two numerals on the keyboard. When the user typed the year, the new number was immediately displayed, but the system time (stored as the number of seconds since 1904) was not changed until the user typed enter, changed the focus by clicking on some other control, or closed the window. The second method to change the year was to click the little up or down arrows that appeared when it was selected. Again this did not actually alter the time until applied by typing enter or changing the selection in the window.
If you have an old Macintosh or an emulator like minivmac that can run the original Macintosh operating system, you should test this. You may be surprised see that the original system has no Y2020 bug. This was introduced later. There is a Y2021 bug...sort of. You can easily set the year to 2020, but if you type 21 in either the Control Panel or the Alarm Clock, it will set the date to 1921. Clicking the arrow buttons offers more flexibility. In the control panel, (but not the Alarm Clock) you can use the arrow buttons to go to any year you like. Just keep clicking. In the Alarm Clock, if you try to click across the boundary between 2019 and 2020, it will set you back to 1920. But if you type a 20 to get to 2020, you can use the arrow buttons to get to any later year. The creators of the original Control Panel and Alarm Clock left an undocumented loophole that allows users to set the date to any year in the Macintosh time span. This loophole is present in all systems prior to system version 4. If this function had been preserved in later systems, there would be little reason to bother patching anything.
Patching the original Control Panel
There isn't really anything wrong with the original Control Panel, but it is instructive to remap the 2-digit date setting, changing the span from 1920-2019 to 1941-2040. It is easier to understand later versions with knowledge of their simpler predecessors. The Control Panel was unchanged from System version 0.97 to System 2.1. We'll fix it in System 1.1/Finder 1.1, released 14-April 1985. Unless you were one of the pioneers who bought a Macintosh 128k in its first three months, this is the System file that came with your computer. It's the System that came with my Macintosh 512K in October 1984.
The Control Panel date displays only 2 digits for the year. We can't tell what century we are in without some other clock. I'll use the Clock desk accessory from the first Guided Tour disk, extracted using the Font/DA mover. I will also use the incredible interactive debugger, TMON 2.8, written by Waldemar Horwat. TMON can disassemble and step through the executing code. It will reveal everything.
We can learn a lot from just watching the Control Panel in operation. It doesn't think too hard about the numbers you enter, at least when you first enter them. It lets you make almost any mistake, and then corrects it when you apply your change and set the clock. For example, it will let you type 88 into the month field, but when you apply the change it will replace your 88 with 4 (remainder of 88/12). likewise, if you try to set the date to February 31, it will let you do that, but will change it to March 3 (on a non-leap year), when it applies the change. But when is the century check done? Is it when you hit enter, or is it when you are typing in the digits? You can't really tell, because you only see the two digits you entered. The only way to know is to see the private data storage that is being updated as you type in the number. I have to look for the place where the year is being stored, and the code that processes the key code. As a start, I trigger TMON in the Control Panel on the trap _SetDateTime, which will register the change and alter the clock time. This should execute when I type enter to actually change the date. Maybe, this will lead to the location of the private date record. Enter _SetDateTime in the line labeled Trap intercept in the user area, and exit TMON. Back in the Control Panel, change the date and type enter. If the starting year is 19 (2019), and we change it to 20, the clock will be changed to 2020. In TMON we can disassembly the executing code by opening a Disassembly window and anchoring it to an address a little above that contained in the program counter (PC).
This little piece of code takes the date, as a DateTimeRecord, converts it to seconds since Jan 1, 1904 using the toolbox call _Date2Secs, and then sets the clock with that using _SetDateTime. The DateTimeRecord is a sequence of words in memory, separately representing the year, month, day, hour, minute, second, and day of the week. The _Date2Secs trap expects the address of the DateTimeRecord to be in A0 at the time it is evoked. Opening a Dump window on (A0) displays the contents of memory starting at the location in A0. That location is the local storage of the date that I'm looking for. The year is in the first word, 07E4. This is hex for 2020. The next word is the month, 4, and the next is the day, 4. April 4, 2020. So at this point in execution, some previous set of instructions has already calculated the date to which the clock should be set, and stored it in location 01CC18 (the value of A0). I want to find the code that did that, but it isn't in this subroutine. We got here by typing a key, so maybe the routine that evoked the code we are seeing is one that handles keystrokes. If we ended up here via a subroutine call, we might find the keystroke handling routine via the return address stored on the stack. So look at the stack addresses using TMON's Stack addresses function. This will display the return address for the currently activated code block. We just put the cursor on that line in the user area and hit enter. It displays the return address for in the code that called the subroutine that contains _SetDateTime.
The return address is offset 026C in the in the Control Panel, DRVR 0012, memory address 01BEA4. We can point our disassemble window to a region near that location. Oh, there it is. See the line that says CMPI.W #$0014,D1? $14 is decimal 20, as in 1920 (or 2020). This line is testing to see if the value of D1 exceeds 20. If it is, it will branch, skipping a line that adds 100 ($64) to D1, and then it will add 1900 ($076C). So depending on whether D1 is greater than 20, it will set the century to 1900 or 2000. D1 must contain the low two digits of the year that we entered. This is the code block we seek. Isn't it clear and simple? The Control Panel was originally written in mc68000 assembly language by one of the masters of that genre; we are seeing it exactly as Andy Hertzfeld wrote it. Let's set a breakpoint on the line that does the comparison and try changing the date again. Then step through the code to reveal the flow.
At this the beginning of this block, register D2 contains the numerical value of the digit that has just been typed. A0 contains the address of a local storage area for all variables maintained by the Control Panel. The DateTimeRecord is stored at location $1C(A0) (meaning that it starts 28 bytes beyond the location contained in A0). The program indexes into the DateTimeRecord one word at a time, using D0. In mc68000 assembly this is specified as $1C(A0,D0.W). If D0 contains a zero, this accesses the year, if D0 is 1 it is the month, etc. The user changes the year one keystroke (one numeral) at a time. There is a variable stored elsewhere in the local storage area (at $1A(A0)) that toggles on every keystroke. It indicates whether the numeral just entered is the first or the second numeral in the typed sequence. If it is zero that means the numeral in D2 the first numeral typed since the date was last changed. If so, that numeral is copied into D1 and immediately tested directly to see if it is larger than 20. Of course it will not be. So it will be added to 100 (#$64), and then added to 1900 (#$076C) to make a complete date between 2000 and 2009. That is stored in the location at $1C(A0,D0.W). Next, the zero stored at $1A(A0) is toggled (using NOT) to indicate there may be a next character, and this block is exited, and reentered when the next numeral is typed. If there is a next character, it is in D2. The year calculated after the first numeral is fetched into D1, multiplied by 10 ($A), and the second numeral is added. The result is divided by 100 and the remainder moved to the first word in register D1. This will calculate the low two digits of the year generated by the two keystrokes. For example, imagine we typed a 2 originally and ended up with year 2002 on the first time through, and now we type a 1. 2002 will be multiplied by 10 to give 20020, added to 1 to get 20021, divided by 100, and the remainder (21) stored in D1. This would now be tested against 20, and being larger, would be added to 1900 but without adding 100. This would give the new date of 1921. The century calculation is done as you type in the two digits of the year, not when you type enter and actually set the clock.
I think we understand this well enough to try to fix it, and we can fix it pretty easily. Just change 20 ($14) in the comparison to 40 ($28), and 2-digit years will be interpreted as being between 1939 and 2040. I can try and test this patch in TMON, but any change in TMON is applied to the program in memory, not in the code on disk, and it will not stick. It is necessary to change the Control Panel code on disk.
This should be done using a hex editor. Using the hex editor built into in the resource editor Resorcerer has the advantage of a built-in disassembler so it's easy to verify that the change is made at the right spot. We don’t want to try to change the System file while we are using it, so make a copy of the Desk Accessory in a separate file using the Font/DA Mover, and open that file in Resorcerer. The code is in the DRVR resource, ID 12. In TMON we saw the offset of the relevant line of code from the start of the Alarm Clock DRVR resource. Scrolling to that vicinity (it won’t be exactly the same), we can see the original hex value for the CMPI.W instruction. It is 0C41 0014. Change 14 to 28 and save. Use the Font/DA Mover to install the patched version in the System file. Patch complete.
The Alarm Clock
With the Control Panel fixed, we might hope that the alarm clock will have a similar block of code testing for year 20 and it does. Maybe Andy Hertzfeld and Donn Denman were coordinating their code, or maybe it is just a case of great minds working alike. We can use the same approach in TMON to find the equivalent block of code in the Alarm Clock, and it can be quickly located by searching in any hex editor for the hex sequence 0C41 0014. There is (luckily) only one such sequence in the Alarm Clock. Change the 14 to 28.
There it is, and you can see it’s right because the code around it looks similar, with the add 100 (64 hex) and the add 1900 (076C hex). Change 0014 to 0028, and what happens? The patch works with typed numerals, but not with changes made by clicking the little arrow buttons. In the Alarm Clock, there must be another test to go with the arrow buttons. That makes sense because the arrows don't work quite the same way in the Alarm Clock. In the Control Panel, arrow buttons could take you to any year. The Alarm Clock detects an attempt to click the arrows across the 2019-2020 border and sets the clock back to 1921. The opposite thing happens if you click the down arrow to go from 1921 to 1920. There is no other instance of C014 0014 anywhere, but the critical section of code can be found by searching for 1920 (0780), or 2020 (07E4). Find this place in TMON, set a breakpoint there, and walk through to see what is happening.
Start at the line that says LEA $002E(A1),A0. When this is called, D4 contains the increment in year made by clicking the arrow (+1 or -1). D5 contains the field that is being changed ( 0 for year, 1 for month, etc.). The DateTimeRecord before the change is located at location $2E(A1). This address is placed in A0, and the change in D4 is added to the field indicated by D5. Then that value is turned into a date in Seconds, and then converted back into a DateTimeRecord. This is unnecessary if we are changing the year, but it is helpful if we incremented the month (for example if the month was 12 and we incremented it).The changed date field is collected into D0 from the DateTimeRecord. If D5 is zero, meaning D0 contains the year, it is then compared with #$780 (1920). The first test checks if the resulting year is less than 1920. If the year is greater than or equal to 1920 it is tested again to see if it is less than 2020. If it is, then the gets through the test unchanged, and the test is over. If it is outside this range, the year stored in D0 is changed by 2 in the opposite direction of the original arrow click. You can’t see it here, but ultimately this will cause an up click from 2019 to traverse a loop, backing down the years one by one till it gets to 1919, at which point the loop reverses direction, advance one forward to 1920. At this point the test is satisfied and it stops. A down click from 1920 will cause the opposite thing, a cascade of year increases until 2020 is reached, a change in direction back to 2019, and a stop. If the date is somehow already outside the range, this test doesn't matter. If the date has been set to 2020 in some other piece of code, up arrow clicks will just advance it forward.
Why does the Alarm Clock use such a simple test for typed in years, and a loop for arrow clicks? Wouldn't it be simpler to just detect a year above 2019 and subtract 100 from it. I'm sure there is a reason and I just don't see it here. Maybe it was to implement a Y2020 loophole like the one in the Control panel. Anyway, it is easy for us to patch for 2020. We just change #$780 to #$794 (1940) and #$7E4 to #$7F8 (2040), and the Alarm Clock, like the Control Panel, works the way we want it to.
Although changes in other pieces of code cause them to occur at different locations in the Alarm Clock code, these code blocks and the appropriate patches are the same for systems 0.97 through 6.0.8. We are finished with the Alarm Clock until we get to System 7.
System 3.x/Finder 5.1-5.3 Control Panel
System 3 was released on 04-January 1986, and was distributed with the Macintosh Plus. It introduced the Hierarchical File System, which among other things allowed placement of operating system files in a special folder, called the System Folder. There were substantial changes to the Control Panel. It is larger on screen, because things are spaced farther apart and it abandons the artistic pictures-only approach taken in the original. I guess it was decided that users needed explanatory text to understand the settings (but now it needs to be localized). Also, there are also two new settings, one to activate AppleTalk, and one to set the the RAM Cache. It is not easy to see how RAM cache could have been represented in pictures. Susan Kare might have been able to do it, but by this time she had left Apple to work at NeXT. Something was removed too: setting the date and time. In all the various versions of system 3, the date and time must be set with the Alarm Clock. The Alarm Clock was unchanged, however, and so can be patched as described above, or a patched Alarm Clock from an earlier system can be installed using the Font/DA Mover. This means we have no work to do for System file versions 3.x.
System 4.x/Finder 5.4-6.0
In system 4, released in 1987, there was a foundational change to the Control Panel. It became a container for control panel modules, each with its own code and user interface. The user selected a control panel module from a list of icons along the left margin of the main window. The Control Panel itself was still a Desk Accessory, but the modules were not, and they were not contained in the System file. They were packaged into a new kind of code resource, called cdev, and placed as individual files scattered around in the System Folder. The Control Panel desk accessory would look for them there and load them when it started up. This made it possible to add control panels unique to individual features of each Macintosh. This was needed for the Macintosh II (released March, 1987), which had a card cage into which users could put 3rd party boards with unanticipated functions. This style of Control Panel would be used until release of System 7 in May, 1991. The NeXT computer (released October 1988) used a similar approach but with the icon list at the top, and today's Mac inherited the style of its System Preferences panels from NeXT.
Most of the functions of the old Control Panel, including date and time setting, were implemented in a cdev called General. The General cdev evolved over time. Apple attached version numbers to their cdevs so it is possible to track the changes. System 4 had General v1.0, System 4.1 had General 3.1, and version 4.2 had version 3.2. The versions differed, especially between System 4.0 and 4.1, but in all of them, the parts we need to patch are conserved.
The original code section, the one that compares the low two digits of the year to 20, are still there and work exactly the same when the date is changed using the keyboard. They can be found and patched as described for the original Control Panel. But in system 4 the Control Panel, like the Alarm Clock, included a year check for changes made with the arrow buttons. The code is similar to the Alarm Clock, except that instead of storing the limit dates ($780 and $7E3) in code, they are stored as data in a DateTimeRecord. When searching for these values in a hex editor, the strings to search for are now 0780 0001 0001 0000 0000 (which is midnight on Jan 1, 1920), and 07E3 000C 001F 003B 003B 0000 (one second before midnight, Jan 1, 2020).
In the system 4 and above General Control Panel, unlike the Alarm Clock, the date changes resulting from arrow clicks are checked at all years, so it is impossible to set the year past 2020 by any method. Sadly, somehow in the course of reintroducing time and date setting to the Control Panel, we lost the clever loophole left for us by Andy Hertzfeld, the one that let us set the date however we wanted using only 2 digits. Why was this done, I wonder? The loophole still exists in the Alarm Clock. Maybe the programmers of the General Control Panel thought the loophole was unintentional, and not part of the original design concept. Maybe they thought Andy Hertzfeld just made a mistake, and they fixed it. Sometimes programmers think their job is to keep users from doing things that might be wrong, rather than to enable users to do what they want. Whatever the reason was, closing that loophole is the act that introduced the Y2020 problem in the Macintosh.
System 5.0-6.08
Systems versions 5 and 6 share one version of the Alarm Clock, v1.5. The General Control Panel in System 5.0/Finder is version 3.2, the same one as in System file 4.2. In System 6.03 the General Control Panel is version 3.3.1, in System 6.05 and 6.08 it is has small changes and is version 3.3.3. The changes overall are small ones, and none of them involve setting date or time. The patches we need to make occur at slightly different places in the file, but they can be identified in the same way.
System 7.0 and 7.01
System 7 brought many changes to the appearance and operation of the Macintosh, and also to the underlying code. Desk Accessories, including the Alarm Clock and the Control Panel, were removed from the System file. Desk Accessories could still be used, but now they functioned like ordinary applications. They could be executed from anywhere in the file system but to emulate the way they were used in previous systems, they were ususally added to the Apple Menu. System 7 initially came with a new version of Alarm Clock already installed in the Apple Menu Folder, and with a General cdev. Both of these were given version number 7, as Apple had synchronized version numbers across the line of software distributed with the operating system. These new versions of the General control panel and Alarm Clock were both stripped of any method for setting the date outside the 1921-2020 range. Apparently the code written in System 4 for the General Control Panel was used as a model for the new Alarm Clock. I would have rather it had gone the other way. In keeping with that explanation, the Alarm Clock now inherited the General Control Panel's variable-based comparison of dates, using DateTimeRecords for the limit dates, rather than years as immediate values in the comparisons. However, these changes are made with only a slight change in the code, and it is straightforward to patch both the General control panel and the Alarm clock in System 7 and 7.01 by the method already described..
System 7.1 - 7.6
System 7.1 was released in August 1992. The fact that for the first time Apple charged customers for the upgrade was a hint that this was not just a maintenance release. But its list of new features was not all that impressive, and as I remember, I skipped this version. But there were lots of code changes. In 7.1, date and time setting was still present in the General control panel, and there was still the Alarm Clock. In addition to these, an entirely new Date & Time Control Panel was added. Now there were three different programs that could set the clock. The Alarm Clock looks exactly the same as the original version. The Date & Time control panel differed from the General control panel by adding a choice of ways to format dates.
The resemblance of the General Control Panel and Alarm Clock to previous versions is deceptive. The code that sets the time was completely rewritten in both of them and is exactly the same as the Time & Date Control Panel. The examples here will all be for the Alarm Clock, but you could not tell except for position in code.
Searches for 0780, or 07E3, or C041 0014 all come up empty so it’s back to the debugger. For this I left TMON behind and switched to use MacsBug. There is a version of TMON that works in System 7, but it is nowhere near as useful as the one I’ve been using up to now, and anyway we will have to use Macsbug for Systems 8 and 9. All three date changing programs are small, so I just turned on logging in Macsbug and disassembled each of them into a text file. Conveniently, in all three programs, function names were present in the code. The important part was a function called MyToggleDate.
None of these programs actually range-check a date. They don’t even have code to change the date. All that work is done by a Toolbox call introduced in System 7, called ToggleDate. It was part of a new suite of functions called the International Date and Time Utilities. All of them are evoked from a single Toolbox entry point, _ScriptUtil, and utilize a new version of the DateTimeRecord called LongDateTimeRecord. One of the parameters to ScriptUtil is a selector for the date and time function we wish to execute. The key function for us is ToggleDate (_ScriptUtil with selector #$820EFFEE).
ToggleDate abstracts out exactly the task we have been studying. The user has typed a numeral key on the keyboard or has clicked one of the up or down arrows to change the month, day, or year. This should result in a change in a local copy of the date, either in a DateTimeRecord or as a number of seconds since Midnight 1/1/1904, that can be used to reset the clock when the user later types enter or changes focus in the window. The parameters to ToggleDate are: (1) The address of a data structure containing the current date in seconds (which will be modified by ToggleDate), (2) a variable called field, indicating which field of the LongDateTimeRec we wish to change (e.g. year, month, day, etc, encoded as an enumeration), (3) delta, and (4) ch, which together to determine the change we are making in the date, and (5) a parameter block called a TogglePB, whose first field is a longword of flag bits telling which fields should be range checked and what kind of range checking to do. To increment or decrement the year (if the user clicked the arrow buttons), delta would be +1 or -1, and ch would be ignored. If the user is keying in the new date, delta is set to 0, and the single character typed is entered (as ascii) in ch.
Internally, ToggleDate first converts the current date (which is in seconds) to a LongDateTimeRecord. Then it changes the field specified by the field parameter to the values contained in the combined delta and ch parameters. Then it passes the resulting LongDateTimeRecord to another Toolbox call called ValidDate. That function converts the date back to seconds, and then tests to see if it is valid. Validity of a date includes two kinds of range checking. One constrains the date to the range of times that the Macintosh clock can represent (midnight GMT on January 1, 1904 to 06:28:16 GMT on February 6, 2040). The second is to enforce the old (system 4) Control Panel range of 1920-2019. These two range checks are controlled by the bitfields in the first field of the parameter TogglePB. In all of the three Date and Time setting utilities provided in System 7.1, the bitfields are set to engage both range checks. I guess in 1992, when System 7.1 was released, 2020 still seemed like a long way away. By altering only a single bit in that field, we can remove the 1920-2019 range check and fix the Y2020 bug.
The two bits are 31 (smallDateBit), which restricts to the range of all Macintosh times, and 27 (genCdevRangeBit), which restricts dates to the range 1920-2019.
in the 7th line of MyToggleDate,, a strange number, $8800007E is loaded into a location on the stack frame, -$001E(A6). Farther down in MyToggleDate, when loading the parameters for ToggleDate, that location will be pushed on the stack. It is a TogglePB. That first longword ($8800007E) contains the flags that determine range checking. The high word, 8800, is 1000 1000 0000 0000 in binary. The first 1 is bit 31, indicating range checking for the clock time range. The second 1 is bit 27, the one that restricts the range to 1920-2019. If we just set that one bit to 0, we can turn that off. There is only one instance of $8800007E in the entire Alarm Clock, one in the General Control Panel, and one in the Date & Time Control Panel. Search for that in a hex editor, and change it to 8000007E in all three.
Starting in System 7.5, the Alarm Clock was removed, and time setting was removed from the General Control Panel. The patch above works for Time & Date Control panel for Systems 7.1-7.6. We patched the code in the Control Panel by changing a single bit in a parameter to a ToolBox call.
Systems 8.0 & 8.1
System 8 introduced the Appearance Manager. The Appearance Manager made a lot of changes to the look of windows and controls, and there was a whole new set of Appearance Manager-compatible controls. One completely new control, the Clock Control, was included. This was a pretty complex widget that drew the date or time in a set of boxes like those we have been using to set the clock all along. The Clock Control also includes all the code necessary to track mouse movements, make selections, process keystrokes, and detect clicks on those little arrow buttons that can increment and decrement the day, month, or year. It can even change the date. All the code we were searching for and finding in the Date & Time Control Panel in System 7 is now gone. There is still a lot of code there. In fact it is larger than ever, it's just that none of it actually has to do with setting the date and time anymore. All of that has been put into the control, so now we will have to go patch the control. Where is it?
Code for each kind of control is stored in a resource called a Control Definition Function (CDEF). The Clock CDEF has a resource ID of 15. Normally, one looks for CDEFs in the System file, but in System 8.0 and 8.1 all the new features introduced by the Appearance Manager were implemented in a special resource file called Appearance Extensions, located in the Extensions Folder.
Disassembling the CDEF code in Resorcerer, we easily find that familiar piece of code from System 7.1-7.6, including the call to ToggleDate. Once again, a few (about 20) lines earlier, the TogglePB is loaded with the bit fields $8800007E. Again, we change this to $8000007E, and we are good to go. As always, we are changing system files that are in use, so it is best to make a copy of the Appearance Extension file, alter the file, and then replace the old one with the patched one.
This patch works in both System 8.0 and 8.1, which are the only versions that had the Appearance Manager as an Extension file. They are also the last versions for which the time and date setting code is in mc68000 assembly. Over the course of the Sytem 8.x series, System code is gradually shifted out of mc68000 located in resources and replaced with powerPC code stored in the data fork. In System 8.0 and 8.1, there is some powerPC code, and some of it is even part of the Appearance Manager, but there is no powerPC version of the Clock CDEF. As a part of the transition to powerPC, it was possible to have redundant code, allowing a Power Macintosh to execute a function natively, whereas a classic Macintosh could execute the mc68000 version of the same function. A built-in emulator allowed a Power Macintosh to execute mc68000 code that still had not made the transition to powerPC. So even when run on a Power Macintosh, the mc68000 executable code in CDEF 15 in the Appearance Manager Extension executes, and the patch describe above will fix the Time and Date Control Panel. System 8.1 is the last version that could be run on a 68k Macintosh. In Systems 8.5 and beyond, there is a CDEF 15 resource, but it is just a stub. The real code for the CDEF is powerPC and in the data fork. Yikes, now we have to dive into powerPC code in the data fork?
Systems 8.5 and 8.6
Starting in System 8.5, the Date & Time Control Panel, the Clock Control, and ToggleDate are all powerPC code, stored in big globs appropriately called fragments, in the data fork of the System file. The resource manager is no longer any use to us. Finding your way around in the code fragments is possible, but thankfully we won't have to. Instead, relying on the hope that things are organized basically the same as they are in System 8.0 and 8.1, the relevant code will be revealed by a brute force disassembly in MacsBug.
The classic Macintosh operating system was written mostly in assembly language. It is hard today to imagine writing an entire operating system in assembly language, but it wasn't unusual at the time. Also, the mc68000 instruction set was very compact and understandable. One key feature that makes it so readable is the simplicity of subroutine calls. A single LINK instruction can set up a complete stack frame, and an UNLNK can restore the stack. One MOVEM instruction could save or restore the contents of a list of registers, and RTS is all that was needed to return from a subroutine. As we have been looking at mc68000 assembly language for date and time setting, very little of the code we saw was taken up by housekeeping for subroutine calls; most of it has been the heart of the calculations. Moreover, the code was written by real people for readability by other people, because it was written and maintained in assembly language form.
None of this can be said about the Macintosh Operating System versions 8.5-9.2. It was written and maintained in C. The code we will be looking at was not written by a person but by a compiler. The compiler was designed to optimize performance, not readability. Nobody at Apple was trying to read it in the form we will. PowerPC code is verbose at the beginning and end of subroutine calls, and even when calling subroutines. Parameters are passed in registers, but a stack frame is still required. The start of every subroutine has a large prolog occupied with saving registers, creating a stack frame, and moving parameters from the registers used as parameters (usually registers 3-10) to those used for local variables. At the end of every subroutine there is a long epilog of housekeeping instructions. This, combined with the (correct) practice of modularizing everything into small subroutines that don't individually do much, produces very long programs dominated by instructions that don't do anything we care about. Finding the code we are looking for is searching for a needle in a haystack. To make matters worse, the powerPC runtime environment adopted by Apple contains many messy transitions between powerPC and mc68000 code, and this requires a lot more housekeeping. Ok enough complaining.
I guess that some programmer was just told to take the Clock CDEF and Toolbox calls like ToggleDate, translate them into C, and compile them for powerPC. I have a Lombard powerbook that has system 8.6 and MacsBug installed, so I waded in. Of course, I started by just searching the data fork for 8800007E, hoping to shortcircuit the entire thing. Didnt work. So I tried the same MacsBug strategy used in the beginning. One difference – stepping through code in System 8.6 is hopeless. It immediately takes you down a rabbit hole of subroutines calling others, some powerPC, some mc68000. Eventually you end up in a place called “Bowels of the Memory Manager”, from which there is no return on the time scale of a human life. Stepping over every subroutine call would save me from that, but I would miss the one I am looking for. I am hoping there will still be a Clock CDEF that calls ToggleDate.
I open the Date & Time Control Panel, and trigger MacsBug (from an FKEY because the powerbook has no programmer's switch). I set an A-Trap breakpoint at _Date2Secs (now called DateToSeconds), and leave MacsBug. Then enter a number in the years field, and I am back in MacsBug. I step across DateToSeconds, which was apparently called by PutIcon (which is still in mc68000). I have to single step through that and when it finishes I end up in ReadDateTime, which almost immediately calls ClockCDEF.
A-Trap break at FFC0C59E _PutIcon+070F6: A9C7 (_DateToSeconds)
Step (into)
_PutIcon
+070F6 FFC0C59E _DateToSeconds ; FFC39668 | A9C7
_DateToSeconds
_PutIcon
+070F8 FFC0C5A0 MOVE.L D0,Scratch20 | 21C0 01E4
+070FC FFC0C5A4 MOVEQ #$00,D0 | 7000
+070FE FFC0C5A6 LEA $000E(A7),A7 | 4FEF 000E
+07102 FFC0C5AA LEA $0060(A7),A7 | 4FEF 0060
+07106 FFC0C5AE MOVEM.L (A7)+,D3-D5/A0/A1 | 4CDF 0338
+0710A FFC0C5B2 RTS | 4E75
+0C150 FFC115F8 MOVE.L Scratch20,Time | 21F8 01E4 020C
+0C156 FFC115FE MOVEM.L (A7)+,D1/D2/D4/D5/A0/A2/A3 | 4CDF 0D36
+0C15A FFC11602 TST.W D0 | 4A40
+0C15C FFC11604 RTS | 4E75
+0BB30 FFC10FD8 MOVE.L Time,(A0) | 20B8 020C
+0BB34 FFC10FDC RTS | 4E75
No procedure name
0EFC0040 DC.W $FE02 ; ???? | FE02
ReadDateTime
+0002C FFD1C654 lwz RTOC,0x0014(SP) | 80410014
+00030 FFD1C658 lwz r12,0x0048(SP) | 81810048
+00034 FFD1C65C stw r3,0x0038(SP) | 90610038
+00038 FFD1C660 addi r4,SP,0x0000 | 38810000
+0003C FFD1C664 addic SP,SP,0x0040 | 30210040
+00040 FFD1C668 mtlr r12 ; LR = 0x0008 | 7D8803A6
+00044 FFD1C66C lhau r3,0x003A(r4) | AC64003A
+00048 FFD1C670 blr | 4E800020
ClockCDEF
+03CC8 1FAE5510 lwz RTOC,0x0014(SP) | 80410014
1FAE5510-3CC8 = $1FAE1848 #531503176 #531503176 '•Æ•H' ClockCDEF
(between #506M and #507M)
Great, there is something called ClockCDEF. I can calculate it's starting place by subtracting the offset of the current instruction address from its address.
Now it is possible to just disassemble the entire ClockCDEF starting at the beginning, and step to a call to ToggleDate, and find it at offset 2310. Here it is.
+022FC 1FAE3B44 addi r4,r31,0x0000 | 389F0000
+02300 1FAE3B48 addi r5,r29,0x0000 | 38BD0000
+02304 1FAE3B4C addi r6,r30,0x0000 | 38DE0000
+02308 1FAE3B50 addi r3,SP,0x0038 | 38610038
+0230C 1FAE3B54 addi r7,SP,0x0040 | 38E10040
+02310 1FAE3B58 bl ToggleDate ; 0x1FAFD96C | 48019E15
The parameters to ToggleDate are [1] the date in seconds (in r3), [2] field, which should be 1 if we are changing the year (in r4), [3] delta, which should be zero because we entered a numeric digit (in r5), [4] ch, which should be the ascii code for the digit we entered (in r6), and [5] the togglePB, containing our range checking flags 8800007E (address in r7). We are interested in r7, which here is being loaded from a location $40 bytes up the stack. We have to look up about 360 lines, but eventually we find it. Are you kidding? The code that took 20 lines of mc68000 in System 8.5 took 300 lines of powerPC in System 8.6? Yes. Yes it did.
+021A4 1FAE39EC lis r3,-0x7800 | 3C608800
+021A8 1FAE39F0 addi r3,r3,0x007E | 3863007E
+021AC 1FAE39F4 stw r3,0x0040(SP) | 90610040
Here, the flag fields of the togglePB are put together in pieces into r3, and then stored on the stack where we will find them later. And there they are. -0x7800 is hex 8800, written signed. It occupies the top 16 bits of r3, and the lower 16 come from the next line, 0x007E. The actual code we will be looking for in the hex editor is written in the right column. We should search for the string 3C608800 3863007E. There is only be one instance of that in the data fork of the version 8.6 System file. We change it to 3C608000 3863007E, and everything should be fine.
This method fixes System 8.5 and 8.6. It seems like such a small change. Given all the stuff that you can change in the Date & Time Control Panel, wouldn't it have been great if there was just a checkbox to turn off the date range enforcement of the System version 4 Control Panel? There isn't one. You can see it is hard coded in the ClockCDEF.
System version 9.x.
System 9.0 was released in October, 1999, almost 9 months after the SuperBowl advertisement in which Apple bragged about being immune to things like the Y2k bug. Maybe at some time in the interim, somebody at Apple recognized the dishonesty in that commercial. Maybe not. For whatever reason, in System 9, the flags passed to ToggleDate by the ClockCDEF were altered in just the way we altered them above. System 9 is already patched. You can set the date however you like, within the limits of the Macintosh system clock. We are good to go till 2040.
BG
Sunday, April 5, 2020