*



  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