Fixing the Race Drivin' Y2K Bug in Version 2.4         Jed Margolin  7/12/02
 



These are the subroutines from which the code was taken.

The TimeKeeper's registers are incremented and decremented using parameters from a table (TIMETABLE) which contains wrap limits and data masks.

Table driven software is more efficient (spacewise) than inline code and although it takes longer to write initially, it is easier to add entries to a table than to write more inline code.
 

306                                      XDEF TIMETABLE,TSETINC,TSETDEC,CALSET
307                                     * Setting the Clock via User Controls
308                                     TIMETABLE:
309
310                                     * Parameter to change;
311                                     * Parameter Bit Mask; Low Wrap; High Wrap
312          0000804A FFFF 4FF6             DC.L HOUR
313          0000804E 003F 0000 0023        DC.W $3F,$0,$23
314
315          00008054 FFFF 4FF4             DC.L MINUTE
316          00008058 007F 0000 0059        DC.W $7F,$0,$59
317
318          0000805E FFFF 4FF2             DC.L SECOND
319          00008062 007F 0000 0059        DC.W $7F,$0,$59
320
321          00008068 FFFF 4FF8             DC.L DAY
322          0000806C 0007 0001 0007        DC.W $07,$1,$07
323
324          00008072 FFFF 4FFC             DC.L MONTH
325          00008076 001F 0001 0012        DC.W $1F,$1,$12
326
327          0000807C FFFF 4FFA             DC.L DATE
328          00008080 003F 0001 0031        DC.W $3F,$1,$31
329
330          00008086 FFFF 4FFE             DC.L YEAR
331          0000808A 00FF 0088 0099        DC.W $0FF,$88,$99
332
333                                     * D0.W contains the index number
334                                     TSETINC:
335          00008090 40E7                  MOVE.W SR,-(A7) * Save Interrupt Mask
336          00008092 46FC 2F00             MOVE.W #$2F00,SR * Disable Interrupts
337          00008096 4279 0060 4008        CLR.W ZP1WEN  * Enable ZeroPower RAM Writes
338          0000809C 4279 0060 401A        CLR.W ZP2WEN
339
340          000080A2 45F9 0000 804A        LEA.L TIMETABLE,A2
341          000080A8 C0FC 000A             MULU #10,D0  *
342          000080AC D4C0                  ADDA D0,A2  * A2 points to table entry
343
344                                     * Increment and wrap if necessary
345          000080AE 023C 00EF             AND.B #$0EF,CCR * Clear Extend Flag
346          000080B2 143C 0001             MOVE.B #1,D2  * ADD Decimal is either R,R or mem,mem
347
348          000080B6 2652                  MOVE.L (A2),A3  * Get parameter
349          000080B8 1013                  MOVE.B (A3),D0
350          000080BA C102                  ABCD D2,D0
351
352          000080BC B02A 0009             CMP.B 9(A2),D0 * Compare to High Wrap
353          000080C0 6F00 0006             BLE TSLP1  * Overflow?
354                                     * Yes
355          000080C4 102A 0007             MOVE.B 7(A2),D0 * Make it wrap
356
357                                     * Mask the data
358          000080C8 322A 0004         TSLP1 MOVE.W 4(A2),D1 * Get the Bit Mask
359          000080CC C001                  AND.B D1,D0  * Apply the mask to the new data
360          000080CE 4641                  NOT.W D1  * Complement the mask
361          000080D0 C213                  AND.B (A3),D1  * Get the bits that will not be changed
362          000080D2 8001                  OR.B D1,D0  * ready to write
363
364          000080D4 0039 0080 FFFF        OR.B #$80,CLKCON * Set Write
                      4FF0
365          000080DC 1680                  MOVE.B D0,(A3)  * Write Data
366          000080DE 0239 007F FFFF        AND.B #$7F,CLKCON * End Write
                      4FF0
367
368          000080E6 6100 FDB8             BSR RCLKBR  * Copy to the Shadow Display Registers
369
370                                     TSLP4
371          000080EA 4279 0060 4018        CLR.W ZP1WDIS  * Disable ZeroPower RAM Writes
372          000080F0 4279 0060 400A        CLR.W ZP2WDIS
373          000080F6 46DF                  MOVE.W (A7)+,SR * Restore Interrupt Mask
374
375          000080F8 4E75                  RTS   * End
376                                     *----
377                                     * D0.W contains the index number
378          000080FA 40E7              TSETDEC: MOVE.W SR,-(A7) * Save Interrupt Mask
379          000080FC 46FC 2F00              MOVE.W #$2F00,SR * Disable Interrupts
380          00008100 4279 0060 4008        CLR.W ZP1WEN  * Enable ZeroPower RAM Writes
381          00008106 4279 0060 401A        CLR.W ZP2WEN
382
383          0000810C 45F9 0000 804A        LEA.L TIMETABLE,A2
384          00008112 C0FC 000A             MULU #10,D0  *
385          00008116 D4C0                  ADDA D0,A2  * A2 points to table entry
386
387                                     * Decrement and wrap if necessary
388          00008118 023C 00EF             AND.B #$0EF,CCR * Clear Extend Flag
389          0000811C 143C 0001             MOVE.B #1,D2  * SUB Decimal is either R,R or mem,mem
390
391          00008120 2652                  MOVE.L (A2),A3  * Get parameter
392          00008122 1013                  MOVE.B (A3),D0
393          00008124 8102                  SBCD D2,D0
394
395          00008126 B02A 0007             CMP.B 7(A2),D0 * Compare to Low Wrap
396          0000812A 6A00 0006             BPL TSLP3  * Under flow?
397                                     * Yes
398          0000812E 102A 0009             MOVE.B 9(A2),D0 * Make it wrap
399
400          00008132 6094              TSLP3 BRA TSLP1  * Mask it and Write it
401



This displays the clock onscreen. It was not amenable to the table driven approach.

578                                      TTL 'Display the Clock'
580                                      XDEF DISPCLK,CLKPOS
581          00000012                    CLKPOS EQU 18  * Clock Position Line #
582                                     * DISPLAY THE CLOCK
583                                     DISPCLK:
584          00008304 0C39 0055 FFFF        CMP.B #$55,CLKSTAT
                      4FCE
585          0000830C 6700 0004             BEQ DSPC
586          00008310 4E75                  RTS   * Clock is Off or missing so don't display it.
587
588          00008312 343C 00FF         DSPC MOVE.W #WHITE,D2  * Color
589          00008316 4EB9 0000 0000  E     JSR SECOLOR
590
591                                     * Hours:Minutes:Seconds
592          0000831C 303C 0012             MOVE.W #CLKPOS,D0  * Y line
593          00008320 323C 000A             MOVE.W #10,D1   * X Position
594          00008324 4EB9 0000 0000  E     JSR DO_ADR   * Result in D0
595
596          0000832A 1238 8004             MOVE.B DHOUR,D1  * HOUR
597          0000832E 0C01 0013             CMP.B #$13,D1   * If it is .GE. $13 then SUBD $12
598          00008332 6D00 000C             BLT DSPC1
599
600          00008336 023C 00EF             AND.B #$0EF,CCR  * Clear Extend Flag
601          0000833A 183C 0012             MOVE.B #$12,D4   * SUB Decimal is either R,R or mem,mem
602          0000833E 8304                  SBCD D4,D1
603
604          00008340 4A01              DSPC1 TST.B D1   * If it is '0' make it '12'
605          00008342 6600 0006             BNE DSPC2
606          00008346 123C 0012             MOVE.B #$12,D1
607
608          0000834A 4EB9 0000 0000  E DSPC2 JSR VW2_SEX   * View two digits, blank a leading zero
609
610          00008350 123C 003A             MOVE.B #':',D1   * ':'
611          00008354 4EB9 0000 0000  E     JSR HAMMER
612
613          0000835A 1238 8005             MOVE.B DMIN,D1   * MINUTE
614          0000835E 4EB9 0000 0000  E     JSR VW2_HEX
615
616          00008364 123C 003A             MOVE.B #':',D1   * ':'
617          00008368 4EB9 0000 0000  E     JSR HAMMER
618
619          0000836E 1238 8006             MOVE.B DSEC,D1   * SECOND
620          00008372 4EB9 0000 0000  E     JSR VW2_HEX
621                                     *---------------------
622                                     * AM/PM
623          00008378 123C 002A             MOVE.B #'*',D1   * space
624          0000837C 4EB9 0000 0000  E     JSR HAMMER
625
626          00008382 0C38 0012 8004        CMP.B #$12,DHOUR  *
627          00008388 6C00 000A             BGE DSPC3   * HOUR .GE. $12 = 'PM'
628          0000838C 123C 0041             MOVE.B #'A',D1
629          00008390 6000 0006             BRA DSPC4
630
631          00008394 123C 0050         DSPC3 MOVE.B #'P',D1
632
633          00008398 4EB9 0000 0000  E DSPC4 JSR HAMMER   * 'A' or 'P'
634          0000839E 123C 004D             MOVE.B #'M',D1
635          000083A2 4EB9 0000 0000  E     JSR HAMMER   * 'M'
636                                     *---------------------
637                                     * Day Name
638          000083A8 303C 0013             MOVE.W #(CLKPOS+1),D0  * Y line
639          000083AC 323C 0002             MOVE.W #2,D1   * X Position
640          000083B0 4EB9 0000 0000  E     JSR DO_ADR   * Result in D0
641
642          000083B6 4241                  CLR.W D1   * Day Name
643          000083B8 1238 8003             MOVE.B DDAY,D1
644          000083BC 0201 0007             AND.B #$07,D1
645          000083C0 41F9 0000 842A        LEA.L DOOL,A0
646
647          000083C6 6100 0174             BSR CLKMSG
648                                     *---------------------
649                                     * Month,Date,Year
650          000083CA 303C 0013             MOVE.W #(CLKPOS+1),D0  * Y line
651          000083CE 323C 000F             MOVE.W #15,D1   * X Position
652          000083D2 4EB9 0000 0000  E     JSR DO_ADR   * Result in D0
653
654          000083D8 4241                  CLR.W D1   * Month Name
655          000083DA 1238 8001             MOVE.B DMON,D1
656          000083DE 0201 00FF             AND.B #$0FF,D1
657          000083E2 41F9 0000 848E        LEA.L MOOL,A0
658
659          000083E8 6100 0152             BSR CLKMSG
660                                     *---------------------
661          000083EC 123C 002A             MOVE.B #'*',D1   * Space
662          000083F0 4EB9 0000 0000  E     JSR HAMMER
663                                     *---------------------
664          000083F6 1238 8002             MOVE.B DDATE,D1  * Date
665          000083FA 4EB9 0000 0000  E     JSR VW2_HEX
666                                     *---------------------
667          00008400 123C 002C             MOVE.B #',',D1   * ,
668          00008404 4EB9 0000 0000  E     JSR HAMMER
669                                     *---------------------
670          0000840A 123C 0031             MOVE.B #'1',D1   * 19
671          0000840E 4EB9 0000 0000  E     JSR HAMMER
672          00008414 123C 0039             MOVE.B #'9',D1   *
673          00008418 4EB9 0000 0000  E     JSR HAMMER
674                                     *---------------------
675          0000841E 1238 8000             MOVE.B DYR,D1   * Year
676          00008422 4EB9 0000 0000  E     JSR VW2_HEX
677                                     *---------------------
678          00008428 4E75                  RTS
679 



Why the addresses produced by the VAX assembler don't exactly match those produced by my PC based assembler.
 

The reason is very interesting. There is a place in the program with the instruction that does:

        MOVE.B    $FFFF8000,D1

The PC based assembler produces the code:   1238 8000   which is a MOVE.B instruction using absolute short addressing for the source data. This is proper because the 68010 sign extends absolute short addresses.

It assembles     MOVE.B  $00008000,D1  as  1239 0000 8000  which is a MOVE.B instruction using absolute long addressing for the source data. This is correct because otherwise $8000 would be sign extended to $FFFF8000.

The PC Based assembler correctly understands that the 68010's absolute word addressing mode will sign extend the value.

However, when the instruction was assembled by the assembler running on the VAX,

            MOVE.B    $FFFF8000,D1

the code produced was   1239 FFFF 8000   which uses absolute long addressing.

Apparently this assembler, which came from a well known company, which I believe is still around, didn't know about the 68010's absolute short addressing.

Of course, this code was produced back in the 1980s, when the Earth was still smoldering with active volcanos and dinosaurs roamed the land, but I wonder if I should send them a bug report.   :-)



Finding the Data in Other Versions
 

In changing the year limits, I found the code:

330    00008086 FFFF 4FFE              DC.L YEAR
331    0000808A 00FF 0088 0099         DC.W $0FF,$88,$99

in the merged file made from  77_5002.bin and 77_5001.bin at:

8080:  00 01 00 31 ff ff 4f fe 00 ff 00 88 00 99 40 e7

What I didn't show was that later on is the ASCII text for the day labels:

008440  84 74 00 00 84 7D 00 00 84 84 09 44 41 59 2A 45   .t...}.....DAY*E
008450  52 52 4F 52 06 53 55 4E 44 41 59 06 4D 4F 4E 44   RROR.SUNDAY.MOND
008460  41 59 07 54 55 45 53 44 41 59 09 57 45 44 4E 45   AY.TUESDAY.WEDNE
008470  53 44 41 59 08 54 48 55 52 53 44 41 59 06 46 52   SDAY.THURSDAY.FR
008480  49 44 41 59 08 53 41 54 55 52 44 41 59 00 00 00   IDAY.SATURDAY...

If we use the address of the 'S' in 'Sunday as a reference ($8455), the code we are looking for, starting at $8089 is $8455 - $8089 = $3CC before the 'S' in 'SUNDAY'.

The code that increments the count in TSETINC:

348   000080B6 2652                MOVE.L (A2),A3   * Get parameter
349   000080B8 1013                MOVE.B (A3),D0
350   000080BA C102                ABCD D2,D0
351
352   000080BC B02A 0009           CMP.B 9(A2),D0   * Compare to High Wrap
353   000080C0 6F00 0006           BLE TSLP1        * Overflow?
354                         * Yes
355   000080C4 102A 0007           MOVE.B 7(A2),D0  * Make it wrap

was found at:

80b0: 14 3c 00 01 26 52 10 13 c1 02 b0 2a 00 09 6f 00
80c0: 00 06 10 2a 00 07 32 2a 00 04 c0 01 46 41 c2 13

'6f' is at address $80BE, which is $8455 - $80BE = $397 before the 'S' in 'SUNDAY'.

The code for the century data:

670          0000840A 123C0031            MOVE.B #'1',D1     * 19
671          0000840E 4EB9 0000 0000  E    JSR HAMMER
672          00008414 123C0039            MOVE.B #'9',D1
673          00008418 4EB9 0000 0000  E    JSR HAMMER

was found at:

8400: 00 2c 4e b9 00 00 31 38 12 3c 00 31 4e b9 00 00
8410: 31 38 12 3c 00 39 4e b9 00 00 31 38 12 39 ff ff

The '12' is at address $8408, so that this code segment starts at
$8455 - $8408 = $4D before the 'S' in Sunday.
 

To check this theory, I will use the Roms from a Race Drivin' Compact, Version 1.7 .
(I believe the final version of Race Drivin' Compact was 1.9 .)

First we find 'SUNDAY'. Note that the strings may be broken up, making them difficult to find with an editor.

0079F0  00 00 7A 2E 09 44 41 59 2A 45 52 52 4F 52 06 53   ..z..DAY*ERROR.S
007A00  55 4E 44 41 59 06 4D 4F 4E 44 41 59 07 54 55 45   UNDAY.MONDAY.TUE
007A10  53 44 41 59 09 57 45 44 4E 45 53 44 41 59 08 54   SDAY.WEDNESDAY.T
007A20  48 55 52 53 44 41 59 06 46 52 49 44 41 59 08 53   HURSDAY.FRIDAY.S
007A30  41 54 55 52 44 41 59 00 00 00 7A 84 00 00 7A 90   ATURDAY...z...z.

'SUNDAY' starts at $79FF.
 

Years Limits:

If we subtract $3CC from $79FF we get $7633. The code there is

007630  4F FE 00 FF 00 88 00 99 40 E7 46 FC 2F 00 42 79
007640  00 60 40 08 42 79 00 60 40 1A 45 F9 00 00 75 F2

which is exactly what we were looking for.
 

Change BLE to BLS in TSETINC

If we subtract $397 from $79FF we get $7668. The code there is:

007660  10 13 C1 02 B0 2A 00 09 6F 00 00 06 10 2A 00 07
007670  32 2A 00 04 C0 01 46 41 C2 13 80 01 00 39 00 80

Bingo.
 

Change the Century Data:

If we subtract $4D from $79FF we get $79B2. The code there is:

0079B0  2B 5E 12 3C 00 31 4E B9 00 00 2B 5E 12 3C 00 39
0079C0  4E B9 00 00 2B 5E 12 39 FF FF 80 00 4E B9 00 00
 

Now all we have to do is transform these addresses back to their orginal addresses in the Roms (and then burn some Roms) and we will have fixed the Y2K bug in Race Drivin' Compact Version 1.7 .



Software

Since people might not have the programs to find and make the changes necessary to fix the Y2K bug I am making available the ones I wrote.

The programs are written for Microsoft Visual C++ 6.0 and are Console Applications.

The easiest way to run them is in full screen DOS (or in a DOS box)  in a directory containing the data files and the executable program. If you use the Windows Run box you have to specify the complete paths to the input files.

Although they look like DOS applications, they aren't. They require Windows in order to run. (It will tell you that if you run them after booting up in Command Mode.)

If you want to run pure DOS versions, you can compile them under Borland Turbo C if you comment out one line. That line is documented in the source files.

In either case, I recommend that you examine the source file and compile it yourself so you will know that there is nothing nasty hiding in it. I also recommend that you download it only through my Web site (www.jmargolin.com) and only through this page.

Each program has a test subdirectory containing the released executable version of the program along with test data.

Instructions for running each program can be obtained by running the program without any command line arguments.

The programs are copyright 2002 by Jed Margolin (that's me).

I am making the programs available under the following terms:

Individuals who are legal owners of an Atari Games Hard Drivin' or Race Drivin' coin-op game (any version) have permission to make copies of these programs for their own use.

Any commercial or other use is prohibited.
 

mdump.zip

This program merges and interleaves two binary files and sends an ASCII dump of the results to the screen.

The program is run as follows:

  mdump file1.bin file2.bin

The output goes to the screen.

In order to save the dump in a file, redirect the output to a file:

  mdump file1.bin file2.bin  >saveit.txt

I have included two test files: test1.bin and test2.bin .
 

Make sure you put the files in the correct order:

  mdump  test2.bin test1.bin >saveit.txt

Here it is:    mdump.zip  (320KB)
 
 

dump.zip

This program sends an ASCII dump of the input file to the screen.

The program is run as follows:

  dump file.bin

The output goes to the screen.

In order to save the dump in a file, redirect the output to a file:

  dump file.bin >saveit.txt

I have included a test file: test1.bin .

Here it is:   dump.zip  (142KB)
 
 

checksum.zip

This program calculates the checksum of a file by adding the bytes and reporting the low 16 bits of the sum.

The program is run as follows:

  checksum file.bin

The output goes to the screen.

Here it is:   checksum.zip  (184KB)
 
 

change.zip

This program reads an input file, uses a change file containing addresses and data to  change, and sends the modified image to an output file. It does not change the input file.

The program is run as follows:

       change <infile1.bin> <changes.dat> <outfile.bin>

          Input File and Output File must be different
 

The file format of the change file is:

     address,data

        There may be only one address,data entry per line.

        The address may be decimal or hex text (example: 0x12334).

        The data may be decimal or hex text (example: 0x12).

        Addresses must be in ascending order.

        Lines beginning with an asterisk  ' * ' or spaces are ignored (example: *  comment).

I have included the test files: test1.bin and change1.dat as well as a batch file (change1.bat) to make it easier to run.

Here it is:   change.zip  (227KB)



Jed Margolin
San Jose, CA
July 12, 2002     Revised: 7/18/02, 7/20/02


Please send comments here