PS2 SDL Timer

Create a single thread for each patch to be added to the repository. Please try to stay on topic.
Post Reply
protomank
Posts: 59
Joined: Thu Dec 18, 2008 1:41 am
Location: Porto Alegre, RS, Brazil
Contact:

PS2 SDL Timer

Post by protomank »

Hello. The current implementation of SDL timers and SDL_Delay is wrong, and do not work. Based in the work of shawn_t (link), I've made a very simple version of timers that still need some work (please take time to review it at will), but is already much better than having a non-working function.

Problems: time is not perfect yet, but close.
TODO List: Add support to multiple timers using threads as in the original SDL, fix the never implemented SDL_SYS_StartTimer.

I do not expect this patch to be submited, I just want people to help me out and point blockers that need to be fixed before commit :)

EDIT: forgot to include the diff:


Left file: /arquivos/programas/devel/ps2/sdl/src/timer/ps2sdk/SDL_systimer.c
Right file: /arquivos/programas/devel/ps2/sdl/src/timer/ps2sdk/SDL_systimer.c.original
1c1
< /*
---
> /*
29,31d28
< #include <tamtypes.h>
< #include <kernel.h>
< #include <debug.h>
44,48c41,44
< // =====================
< // Static Timer Variable
< // =====================
< static int s_tnInterruptID = -1;
< static u64 s_tnInterruptCount = 0;
---
> #define INTC_TIM1 10
> #define T1_COUNT (volatile u32 *)0x10000800
> #define T1_MODE (volatile u32 *)0x10000810
> #define T1_COMP (volatile u32 *)0x10000820
50d45
<
56,200d50
<
<
<
< // ================================================
< // Defines and Enumerations for timer related tasks
< // ================================================
< #define T0_COUNT ((volatile unsigned long*)0x10000000)
< #define T0_MODE ((volatile unsigned long*)0x10000010)
< #define T0_COMP ((volatile unsigned long*)0x10000020)
< #define T0_HOLD ((volatile unsigned long*)0x10000030)
<
< #define T1_COUNT ((volatile unsigned long*)0x10000800)
< #define T1_MODE ((volatile unsigned long*)0x10000810)
< #define T1_COMP ((volatile unsigned long*)0x10000820)
< #define T1_HOLD ((volatile unsigned long*)0x10000830)
<
< // Note! T2 and T3 don't have a Tn_HOLD register!
< // ----------------------------------------------
< #define T2_COUNT ((volatile unsigned long*)0x10001000)
< #define T2_MODE ((volatile unsigned long*)0x10001010)
< #define T2_COMP ((volatile unsigned long*)0x10001020)
<
< #define T3_COUNT ((volatile unsigned long*)0x10001800)
< #define T3_MODE ((volatile unsigned long*)0x10001810)
< #define T3_COMP ((volatile unsigned long*)0x10001820)
<
< #define Tn_MODE(CLKS,GATE,GATS,GATM,ZRET,CUE,CMPE,OVFE,EQUF,OVFF) \
< (u32)((u32)(CLKS) | ((u32)(GATE) << 2) | \
< ((u32)(GATS) << 3) | ((u32)(GATM) << 4) | \
< ((u32)(ZRET) << 6) | ((u32)(CUE) << 7) | \
< ((u32)(CMPE) << 8) | ((u32)(OVFE) << 9) | \
< ((u32)(EQUF) << 10) | ((u32)(OVFF) << 11))
<
< #define kBUSCLK (147456000)
< #define kBUSCLKBY16 (kBUSCLK / 16)
< #define kBUSCLKBY256 (kBUSCLK / 256)
< #define kHBLNK_NTSC (15734)
< #define kHBLNK_PAL (15625)
< #define kHBLNK_DTV480p (31469)
< #define kHBLNK_DTV1080i (33750)
<
< // =======================
< // Time Interrupt Callback
< // =======================
< int tnTimeInterrupt(int ca) {
< s_tnInterruptCount++;
< // A write to the overflow flag will clear the overflow flag
< // ---------------------------------------------------------
< *T1_MODE |= (1 << 11);
< return -1;
< }
<
< // ==============
< // Time functions
< // ==============
< void tnTimeInit(void) {
< // ============================================================
< // I am using 1/256 of the BUSCLK below in the Tn_MODE register
< // which means that the timer will count at a rate of:
< // 147,456,000 / 256 = 576,000 Hz
< // This implies that the accuracy of this timer is:
< // 1 / 576,000 = 0.0000017361 seconds (~1.74 usec!)
< // The Tn_COUNT registers are 16 bit and overflow in:
< // 1 << 16 = 65536 seconds
< // This implies that our timer will overflow in:
< // 65536 / 576,000 = 0.1138 seconds
< // I use an interrupt to recognize this overflow and increment
< // the <s_tnInterruptCount> variable so I can easily compute
< // the total time. This results in a very accurate timer that
< // is also very efficient. It is possible to have an even more
< // accurate timer by modifying the Tn_MODE, but at the expense
< // of having to call the overflow interrupt more frequently.
< // For example, if you wanted to use 1/16 of the BUSCLK, the
< // timer would count at a rate of:
< // 147,456,000 / 16 = 9,216,000 Hz
< // which implies an accuracy of:
< // 1 / 9,216,000 = 0.0000001085 seconds (0.11 usec!)
< // However, the timer will overflow in:
< // 65536 / 9,216,000 = 0.0071 seconds (7.1 msec)
< // meaning, the interrupt would be called more then 140 times a
< // second. For my purposes the accuracy of ~1.74 usec is fine!
< // ============================================================
<
< // Disable T1_MODE
< // ---------------
< *T1_MODE = 0x0000;
<
< // Initialize the overflow interrupt handler.
< // -----------------------------------------
< s_tnInterruptID = AddIntcHandler(kINTC_TIMER1, tnTimeInterrupt, 0);
< EnableIntc(kINTC_TIMER1);
<
< // Initialize the timer registers
< // CLKS: 0x02 - 1/256 of the BUSCLK (0x01 is 1/16th)
< // CUE: 0x01 - Start/Restart the counting
< // OVFE: 0x01 - An interrupt is generated when an overflow occurs
< // --------------------------------------------------------------
< *T1_COUNT = 0;
< *T1_MODE = Tn_MODE(0x02, 0, 0, 0, 0, 0x01, 0, 0x01, 0, 0);
<
< s_tnInterruptCount = 0;
< }
<
< u64 tnTime(void) {
< u64 t;
<
< // Tn_COUNT is 16 bit precision. Therefore, each
< // <s_tnInterruptCount> is 65536 ticks
< // ---------------------------------------------
< t = *T1_COUNT + (s_tnInterruptCount << 16);
<
< t = t * 1000000 / kBUSCLKBY256;
<
< return t;
< }
<
< void tnResetTimer(void) {
< s_tnInterruptCount = 0;
< }
<
< void tnTimeFini(void) {
< // Stop the timer
< // --------------
< *T1_MODE = 0x0000;
<
< // Disable the interrupt
< // ---------------------
< if (s_tnInterruptID >= 0)
< {
< DisableIntc(kINTC_TIMER1);
< RemoveIntcHandler(kINTC_TIMER1, s_tnInterruptID);
< s_tnInterruptID = -1;
< }
< s_tnInterruptCount = 0;
< }
<
< #define ONE_SECOND 1356155
<
< void wait_mseconds(double mseconds)
< {
< tnResetTimer();
< int TimeCheck = (mseconds*ONE_SECOND/1000); /* Relative Conversion of a Second to a Usec */
< while (tnTime() < TimeCheck);
< }
<
222,223c72,122
< void SDL_Delay(Uint32 ms) {
< wait_mseconds(ms);
---
> void SDL_Delay(Uint32 ms)
> {
> int i;
>
> if (nsleeping_threads >= MAX_SLEEPING_THREADS)
> {
> fprintf(stderr, "too many threads are sleeping at SDL_Delay (current thread %d)\n", GetThreadId());
> return;
> }
>
> /* in theory, a locking mechanism is required here. but since
> * nothing happens if interrupt handler wakes up an already
> * running thread, I chose not to use one.
> */
>
> i = nsleeping_threads;
> nsleeping_threads++;
>
> sleeping_threads = GetThreadId();
>
> while (ms > 0)
> {
> SleepThread();
> ms--;
> }
>
> nsleeping_threads--;
> sleeping_threads = sleeping_threads[nsleeping_threads];
> sleeping_threads[nsleeping_threads] = -1;
> }
>
> static int ms_handler(int ca)
> {
> int i;
>
> ticks++;
>
> for (i=0; i<nsleeping_threads; i++)
> {
> if (sleeping_threads >= 0)
> {
> iWakeupThread(sleeping_threads);
> }
> }
>
> /* reset counter */
> *T1_COUNT = 0;
> /* reset interrupt */
> *T1_MODE |= (1 << 10);
> __asm__ volatile("sync.l; ei");
> return 0;
227,229c126,138
< int SDL_SYS_TimerInit(void) {
< tnTimeInit();
< return 0;
---
> int SDL_SYS_TimerInit(void)
> {
> printf("initializing timer..\n");
>
> tim1_handler_id = AddIntcHandler(INTC_TIM1, ms_handler, 0);
> EnableIntc(INTC_TIM1);
>
> *T1_COUNT = 0;
> *T1_COMP = 586; /* 150MHZ / 256 / 1000 */
> *T1_MODE = 2 | (0<<2) | (0<<6) | (1<<7) | (1<<8);
>
> printf("timer init ended okay\n");
> return 0;
232,233c141,149
< void SDL_SYS_TimerQuit(void) {
< tnTimeFini();
---
> void SDL_SYS_TimerQuit(void)
> {
> DisableIntc(INTC_TIM1);
>
> if (tim1_handler_id >= 0)
> {
> RemoveIntcHandler(INTC_TIM1, tim1_handler_id);
> tim1_handler_id = -1;
> }
236c152,153
< int SDL_SYS_StartTimer(void) {
---
> int SDL_SYS_StartTimer(void)
> {
Post Reply