dingusdev / dingusppc

An experimental emulator
GNU General Public License v3.0
200 stars 21 forks source link

RTC and TBR fixes #34

Closed joevt closed 1 year ago

joevt commented 1 year ago

1) Fixed a typo that caused rtc@ to always return 0 for the upper 32 bits (represents seconds). The problem could cause the following to hang on Power Mac 7500:

cr 2000 0 do get-msecs u. 1 ms loop

2) Fixed an issue where the following would cause inconsistent results (tb in the left column would sometimes decrement instead of increment):

: tbrtctest
2 0 do
    2 0 do
        cr tb@ 8 u.r ." ." 8 u.r
    loop
    2 0 do
        cr 12 spaces rtc@ 8 u.r ." ." 8 u.r
    loop
    2 0 do
        cr tb@ 8 u.r ." ." 8 u.r space
        rtc@ 8 u.r ." ." 8 u.r
    loop
loop
;
tbrtctest

RTC and TBR could not be used simultaneously because they are both incremented by an amount based on the last time stamp but that time stamp can be changed by accessing either RTC or TBR. The solution is to have a different time stamp for each.

3) Fixed an issue where TBR and RTC don't have full 64-bit range. The original calculation was 64 bit and ended with a ÷ 1000000000. This means the max for the upper 32 bits is 2^32/10^9 = 4. The problem is worse for TBR than RTC because the RTC timestamp is changed whenever RTC is read. For TBR, the timestamp is updated only when TBR is written. But the problem still exists for RTC when it hasn't been accessed for a long time.

The solution is to use a multiplication method that supports a 96 bit product. I added a couple routines for that. GCC/clang have 128 bit integer calculations but I don't know if that's portable for Windows. Another option is to use floating point, but you need a floating point type with at least 64 bits of mantissa. long double should work if this assertion is true: assert ((uint64_t)((long double)0x1000000000000001) == 0x1000000000000001);

The following can calculate mantissa sizes:

    #define findmantissasize(t) do { int i = 1; t x = 1; while (x > (t)(x - 1)) { x = x * 2 + 1; i++; }; printf("%s:%d\n", #t, i - 1); } while (0)
    findmantissasize(float);
    findmantissasize(double);
    findmantissasize(long double);

float:24
double:53
long double:64

TBR driving frequency is assumed to be less than 1 GHz. Some minor modification is required for > 1 GHz support.

4) Fixed an issue where get-msecs-601 and get-msecs-60x were not returning the same value. RTC was being calculated using timebase frequency instead of nanosecond frequency. 601 uses RTC. 60x uses TBR. On a real Mac, a G3 CPU won't have a RTC and would cause an exception. This is not the case for dingusppc but I don't think that's a problem.

The new implementation keeps all bits of rtc_lo so that it can be incremented correctly. It's only during reading or setting the rtc_L SPR that the 7 least significant bits are masked.

5) Fixed an issue where RTC was not being updated if only the upper 32 bits was read.

6) Fixed an issue where setting RTC upper or lower doesn't adjust the other.