*
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 [1]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, [2]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 [3]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 [4]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 [5]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,
[6]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 [7]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 [8]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
[9]
[email protected]
Sunday, April 5, 2020
[10]Made on a Mac
[11]< previous
[12]next >
References
1.
https://www.youtube.com/watch?v=BwUQ1YtUpzw
2.
https://en.wikipedia.org/wiki/Year_2038_problem
3.
http://synack.net/~bbraun/
4.
https://www.folklore.org/StoryView.py?project=Macintosh&story=Desk_Ornaments.txt
5.
http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2011/8/31_Guided_Tour_of_Macintosh.html
6.
http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2010/3/27_TMON.html
7.
http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2010/12/20_PowerPC.html
8.
http://basalgangster.macgui.com/RetroMacComputing/Lombard.html
9. mailto:
[email protected]?subject=
10.
http://apple.com/mac
11.
http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2020/4/5_Macintosh_Y2020.html
12.
http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2020/4/5_Macintosh_Y2020.html