PS2SDK
PS2 Homebrew Libraries
alarm.c
1 /*
2 # _____ ___ ____ ___ ____
3 # ____| | ____| | | |____|
4 # | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5 #-----------------------------------------------------------------------
6 # Licenced under Academic Free License version 2.0
7 # Review ps2sdk README & LICENSE files for further details.
8 */
9 
10 #include "tamtypes.h"
11 #include "timer.h"
12 #include "ee_regs.h"
13 #include "kernel.h"
14 
15 #include "internal.h"
16 
17 #define USER_MODE_DISPATCHER 0x00082000 // Call the replacement dispatcher (the original dispatcher function is located at 0x00081fe0).
18 
19 #define INTC_TIM3 12
20 #define T3_COUNT_W ((vu32 *)0xB0001800)
21 #define T3_MODE_W ((vu32 *)0xB0001810)
22 #define T3_COMP_W ((vu32 *)0xB0001820)
23 
24 static int AlarmCount = 0;
25 static u64 AlarmStatus = 0;
26 
27 struct alarm
28 {
29  u16 target; // 0x00
30  u16 time; // 0x02
31  int id; // 0x04
32  void *callback; // 0x08
33  void *common; // 0x0c
34  void *gp; // 0x10
35 };
36 
37 static struct alarm alarms[MAX_ALARMS];
38 
39 // Function prototypes
40 static u32 CalculateTimeDiff(u32 t1, u32 t2);
41 static int InsertAlarm(u32 now, u32 target);
42 static s32 SetAlarmInternal(u16 time, void (*callback)(s32 dispatch_id, u16 time, void *common), void *common);
43 static void SetupTIM3(u16 ticks);
44 
45 static u32 CalculateTimeDiff(u32 t1, u32 t2)
46 {
47  return (t2 < t1 ? (0x10000 | t2) : t2);
48 }
49 
50 static int InsertAlarm(u32 now, u32 target)
51 {
52  int pos, i;
53 
54  pos = 0;
55  for (pos = 0; pos < AlarmCount; pos++) {
56  if (target < CalculateTimeDiff(now, alarms[pos].time)) { // Spot found. If it is not at the end of the list, move back the list.
57  for (i = AlarmCount - 1; i >= pos; i--)
58  alarms[i + 1] = alarms[i];
59 
60  break;
61  }
62  }
63 
64  return pos;
65 }
66 
67 static s32 SetAlarmInternal(u16 time, void (*callback)(s32 dispatch_id, u16 time, void *common), void *common)
68 {
69  void *gp;
70  u32 target, now;
71  struct alarm *alarm;
72  int i, alarmID, pos;
73 
74  now = *T3_COUNT_W;
75  target = now + time;
76 
77  if (AlarmCount >= MAX_ALARMS)
78  return -1;
79 
80  alarmID = -1;
81  for (i = 0; i < MAX_ALARMS; i++) {
82  if (((AlarmStatus >> i) & 1) == 0) {
83  AlarmStatus |= (1 << i);
84  alarmID = i;
85  break;
86  }
87  }
88 
89  if (alarmID < 0)
90  return alarmID;
91 
92  gp = GetGP();
93  pos = InsertAlarm(now, target);
94  alarm = &alarms[pos];
95  alarm->time = time;
96  alarm->target = (u16)target;
97  alarm->id = alarmID;
98  alarm->gp = gp;
99  alarm->callback = callback;
100  alarm->common = common;
101  AlarmCount++;
102  SetupTIM3(alarms[0].target);
103 
104  return alarm->id;
105 }
106 
107 s32 ReleaseAlarm(s32 id)
108 {
109  u16 time;
110  s32 result;
111  int i, j;
112 
113  result = -1;
114  for (i = 0; i < AlarmCount; i++) {
115  if (alarms[i].id == id) {
116  if (alarms[i].target == *T3_COMP_W) {
117  if (*R_EE_I_STAT & (1 << INTC_TIM3)) // Cannot release alarm that has already triggered.
118  return -1;
119  }
120 
121  time = alarms[i].time;
122  // Move forward list.
123  for (j = i; j < AlarmCount - 1; j++)
124  alarms[j] = alarms[j + 1];
125 
126  --AlarmCount;
127  AlarmStatus &= ~(1 << id);
128 
129  if (i == 0) // If the first alarm was released, update timer comparator.
130  SetupTIM3(alarms[0].target);
131 
132  if (AlarmCount == 0) // Stop timer if there are no alarms left.
133  *T3_MODE_W = Tn_MODE(3, 0, 0, 0, 0, 1, 0, 0, 0, 0);
134 
135  result = (CalculateTimeDiff(time, *T3_COUNT_W) - time);
136  }
137  }
138 
139  EE_SYNC();
140  return result;
141 }
142 
143 s32 SetAlarm(u16 time, void (*callback)(s32 dispatch_id, u16 time, void *common), void *common)
144 {
145  s32 result;
146 
147  result = SetAlarmInternal(time, callback, common);
148  EE_SYNC();
149 
150  return result;
151 }
152 
153 static void SetupTIM3(u16 ticks)
154 {
155  *T3_COMP_W = ticks;
156  EE_SYNC();
157  *T3_MODE_W = Tn_MODE(3, 0, 0, 0, 0, 1, 1, 0, 1, 0);
158 }
159 
160 // INTC 12 (TIM3) handler
161 void Intc12Handler(void)
162 {
163  struct alarm alarm;
164  int i;
165 
166  for (i = 0; i < AlarmCount; i++) { // Attempt to find another alarm request that has a different target. Update TIM3's comparator.
167  if (alarms[i].target != alarms[0].target) {
168  SetupTIM3(alarms[i].target);
169  break;
170  }
171  }
172 
173  do {
174  void *gp;
175 
176  alarm = alarms[0];
177  AlarmCount--;
178  for (i = 0; i < AlarmCount; i++)
179  alarms[i] = alarms[i + 1];
180 
181  gp = ChangeGP(alarm.gp);
182  AlarmStatus &= ~(1 << alarm.id);
183  InvokeUserModeCallback((void *)USER_MODE_DISPATCHER, alarm.callback, alarm.id, alarm.target, alarm.common);
184  SetGP(gp);
185 
186  if (AlarmCount <= 0)
187  break;
188  } while (alarms[0].target == alarm.target);
189 
190  if (AlarmCount <= 0) // If there are no further alarms, disable CMPE.
191  *T3_MODE_W = Tn_MODE(3, 0, 0, 0, 0, 1, 0, 0, 1, 0);
192  else
193  SetupTIM3(alarms[0].target);
194 
195  ExitHandler();
196 }
kernel.h
alarm
Definition: alarm.c:27
timer.h
tamtypes.h
R_EE_I_STAT
#define R_EE_I_STAT
Definition: ee_regs.h:462
ee_regs.h