PS2SDK
PS2 Homebrew Libraries
vblank.c
1 /*
2 # _____ ___ ____ ___ ____
3 # ____| | ____| | | |____|
4 # | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5 #-----------------------------------------------------------------------
6 # Copyright ps2dev - http://www.ps2dev.org
7 # Licenced under Academic Free License version 2.0
8 # Review ps2sdk README & LICENSE files for further details.
9 */
10 
11 #include "vblank.h"
12 #include "irx_imports.h"
13 #include "kerr.h"
14 
15 extern struct irx_export_table _exp_vblank;
16 
17 #ifdef _IOP
18 IRX_ID("Vblank_service", 1, 1);
19 #endif
20 // Based on the module from SCE SDK 1.3.4.
21 
22 static vblank_internals_t vblank_internals;
23 
24 static int irq_vblank_interrupt_handler(void *userdata);
25 static int irq_evblank_interrupt_handler(void *userdata);
26 static int vblank_handler_base_beginning(void *userdata);
27 static int vblank_handler_base_end(void *userdata);
28 static void linked_list_set_self(vblank_ll_t *ll);
29 static int linked_list_next_is_self(const vblank_ll_t *ll);
30 static void linked_list_remove(vblank_ll_t *ll);
31 static void linked_list_add_after(vblank_ll_t *ll1, vblank_ll_t *ll2);
32 
33 int _start(int ac, char **av)
34 {
35  unsigned int i;
36  iop_event_t evfp;
37  int state;
38 
39  (void)ac;
40  (void)av;
41 
42  CpuSuspendIntr(&state);
43  if ( RegisterLibraryEntries(&_exp_vblank) )
44  {
45  CpuResumeIntr(state);
46  return 1;
47  }
48 
49  memset(&vblank_internals, 0, sizeof(vblank_internals));
50  linked_list_set_self(&vblank_internals.list_00);
51  linked_list_set_self(&vblank_internals.list_11);
52  linked_list_set_self(&vblank_internals.list_free);
53  for ( i = 0; i < (sizeof(vblank_internals.list_items) / sizeof(vblank_internals.list_items[0])); i += 1 )
54  {
55  linked_list_add_after(&vblank_internals.list_free, &(vblank_internals.list_items[i].ll));
56  }
57  evfp.bits = 0;
58  evfp.attr = EA_MULTI;
59  evfp.option = 0;
60  vblank_internals.ef = CreateEventFlag(&evfp);
61  RegisterVblankHandler(0, 128, vblank_handler_base_beginning, &vblank_internals);
62  RegisterVblankHandler(1, 128, vblank_handler_base_end, &vblank_internals);
63  RegisterIntrHandler(IOP_IRQ_VBLANK, 1, irq_vblank_interrupt_handler, &vblank_internals);
64  RegisterIntrHandler(IOP_IRQ_EVBLANK, 1, irq_evblank_interrupt_handler, &vblank_internals);
65  EnableIntr(IOP_IRQ_VBLANK);
66  EnableIntr(IOP_IRQ_EVBLANK);
67  CpuResumeIntr(state);
68  return 0;
69 }
70 
71 vblank_internals_t *GetVblankInternalData(void)
72 {
73  return &vblank_internals;
74 }
75 
76 int RegisterVblankHandler(int startend, int priority, int (*handler)(void *userdata), void *arg)
77 {
78  const vblank_ll_t *list;
79  vblank_ll_t *item;
80  vblank_item_t *item_new;
81  int state;
82 
83  if ( QueryIntrContext() != 0 )
84  {
85  return KE_ILLEGAL_CONTEXT;
86  }
87  CpuSuspendIntr(&state);
88  if ( linked_list_next_is_self(&vblank_internals.list_free) )
89  {
90  CpuResumeIntr(state);
91  return KE_NO_MEMORY;
92  }
93  list = &vblank_internals.list_11;
94  if ( !startend )
95  list = &vblank_internals.list_00;
96  item = list->next;
97  if ( list->next != list )
98  {
99  do
100  {
101  const vblank_item_t *item_vblank;
102 
103  item_vblank = (vblank_item_t *)item;
104  if ( item_vblank->callback == handler )
105  {
106  CpuResumeIntr(state);
107  return KE_FOUND_HANDLER;
108  }
109  item = item->next;
110  } while ( item != list );
111  item = list->next;
112  }
113  for ( ; item != list; item = item->next )
114  {
115  const vblank_item_t *item_vblank;
116 
117  item_vblank = (vblank_item_t *)item;
118  if ( priority < item_vblank->priority )
119  break;
120  }
121  item_new = (vblank_item_t *)vblank_internals.list_free.next;
122  linked_list_remove(vblank_internals.list_free.next);
123  item_new->callback = handler;
124  item_new->userdata = arg;
125  item_new->priority = priority;
126  linked_list_add_after(item, &item_new->ll);
127  CpuResumeIntr(state);
128  return 0;
129 }
130 
131 // cppcheck-suppress constParameterPointer
132 int ReleaseVblankHandler(int startend, int (*handler)(void *userdata))
133 {
134  const vblank_ll_t *list;
135  vblank_ll_t *item;
136  int state;
137 
138  if ( QueryIntrContext() != 0 )
139  {
140  return KE_ILLEGAL_CONTEXT;
141  }
142  CpuSuspendIntr(&state);
143  list = &vblank_internals.list_11;
144  if ( !startend )
145  list = &vblank_internals.list_00;
146  item = list->next;
147  if ( list->next == list )
148  {
149  CpuResumeIntr(state);
150  return KE_NOTFOUND_HANDLER;
151  }
152  {
153  const vblank_item_t *item_vblank;
154 
155  item_vblank = (vblank_item_t *)item;
156  while ( item_vblank->callback != handler )
157  {
158  item = item->next;
159  item_vblank = (vblank_item_t *)item;
160  if ( item == list )
161  {
162  CpuResumeIntr(state);
163  return KE_NOTFOUND_HANDLER;
164  }
165  }
166  }
167  linked_list_remove(item);
168  linked_list_add_after(&vblank_internals.list_free, item);
169  CpuResumeIntr(state);
170  return 0;
171 }
172 
173 static int irq_vblank_interrupt_handler(void *userdata)
174 {
175  int item_count;
176  vblank_ll_t *item;
177  vblank_ll_t *item_tmp;
178  vblank_internals_t *vbi;
179 
180  vbi = (vblank_internals_t *)userdata;
181  item_count = vbi->item_count;
182  if ( !item_count )
183  {
184  iSetEventFlag(GetSystemStatusFlag(), 0x200);
185  item_count = vbi->item_count;
186  }
187  item = vbi->list_00.next;
188  vbi->item_count = item_count + 1;
189  if ( item != &vbi->list_00 )
190  {
191  do
192  {
193  vblank_item_t *item_vblank;
194 
195  item_vblank = (vblank_item_t *)item;
196  item_tmp = item->next;
197  if ( item_vblank->callback(item_vblank->userdata) == 0 )
198  {
199  linked_list_remove(item);
200  linked_list_add_after(&vbi->list_free, item);
201  }
202  item = item_tmp;
203  } while ( item_tmp != &vbi->list_00 );
204  }
205  return 1;
206 }
207 
208 static int irq_evblank_interrupt_handler(void *userdata)
209 {
210  vblank_ll_t *item;
211  const vblank_ll_t *list_tail;
212  vblank_ll_t *item_tmp;
213  vblank_internals_t *vbi;
214 
215  vbi = (vblank_internals_t *)userdata;
216  item = vbi->list_11.next;
217  list_tail = &vbi->list_11;
218  if ( item != list_tail )
219  {
220  do
221  {
222  vblank_item_t *item_vblank;
223 
224  item_vblank = (vblank_item_t *)item;
225  item_tmp = item->next;
226  if ( item_vblank->callback(item_vblank->userdata) == 0 )
227  {
228  linked_list_remove(item);
229  linked_list_add_after(&vbi->list_free, item);
230  }
231  item = item_tmp;
232  } while ( item_tmp != list_tail );
233  }
234  return 1;
235 }
236 
237 #define EF_VBLANK_START 1
238 #define EF_VBLANK_END 4
239 #define EF_VBLANK 2
240 #define EF_NON_VBLANK 8
241 
242 static int vblank_handler_base_beginning(void *userdata)
243 {
244  vblank_internals_t *vbi;
245 
246  vbi = (vblank_internals_t *)userdata;
247  iSetEventFlag(vbi->ef, EF_VBLANK_START);
248  iSetEventFlag(vbi->ef, EF_VBLANK);
249  iClearEventFlag(vbi->ef, ~(9));
250  return 1;
251 }
252 
253 static int vblank_handler_base_end(void *userdata)
254 {
255  vblank_internals_t *vbi;
256 
257  vbi = (vblank_internals_t *)userdata;
258  iSetEventFlag(vbi->ef, EF_VBLANK_END);
259  iSetEventFlag(vbi->ef, EF_NON_VBLANK);
260  iClearEventFlag(vbi->ef, ~(6));
261  return 1;
262 }
263 
264 void WaitVblankStart()
265 {
266  WaitEventFlag(vblank_internals.ef, EF_VBLANK_START, WEF_OR, NULL);
267 }
268 
269 void WaitVblankEnd()
270 {
271  WaitEventFlag(vblank_internals.ef, EF_VBLANK_END, WEF_OR, NULL);
272 }
273 
274 void WaitVblank()
275 {
276  WaitEventFlag(vblank_internals.ef, EF_VBLANK, WEF_OR, NULL);
277 }
278 
279 void WaitNonVblank()
280 {
281  WaitEventFlag(vblank_internals.ef, EF_NON_VBLANK, WEF_OR, NULL);
282 }
283 
284 static void linked_list_set_self(vblank_ll_t *ll)
285 {
286  ll->next = ll;
287  ll->prev = ll;
288 }
289 
290 static int linked_list_next_is_self(const vblank_ll_t *ll)
291 {
292  return ll->next == ll;
293 }
294 
295 static void linked_list_remove(vblank_ll_t *ll)
296 {
297  ll->next->prev = ll->prev;
298  ll->prev->next = ll->next;
299 }
300 
301 #if 0
302 // This function is unused
303 static int linked_list_is_circular(vblank_ll_t *ll)
304 {
305  return ll->prev == ll->next;
306 }
307 #endif
308 
309 static void linked_list_add_after(vblank_ll_t *ll1, vblank_ll_t *ll2)
310 {
311  ll2->next = ll1;
312  ll2->prev = ll1->prev;
313  ll1->prev = ll2;
314  ll2->prev->next = ll2;
315 }
RegisterIntrHandler
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition: intrman.c:115
vblank_item_
Definition: vblank.h:31
EA_MULTI
#define EA_MULTI
Definition: thevent.h:35
QueryIntrContext
int QueryIntrContext(void)
CpuSuspendIntr
int CpuSuspendIntr(int *state)
Definition: intrman.c:195
EnableIntr
int EnableIntr(int irq)
Definition: intrman.c:336
irx_export_table
Definition: irx.h:90
vblank.h
CpuResumeIntr
int CpuResumeIntr(int state)
Definition: intrman.c:217
vblank_ll_
Definition: vblank.h:25
iop_event_t
Definition: thevent.h:37
kerr.h
vblank_internals_
Definition: vblank.h:39