Search
lxdream.org :: lxdream/src/eventq.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/eventq.c
changeset 586:2a3ba82cf243
prev422:61a0598e07ff
next674:377d987db8f2
author nkeynes
date Fri Mar 28 12:32:25 2008 +0000 (16 years ago)
permissions -rw-r--r--
last change Merge lxdream-render branch (643:670) to trunk
file annotate diff log raw
nkeynes@265
     1
/**
nkeynes@586
     2
 * $Id$
nkeynes@265
     3
 *
nkeynes@265
     4
 * Simple implementation of one-shot timers. Effectively this allows IO
nkeynes@265
     5
 * devices to wait until a particular time before completing. We expect 
nkeynes@265
     6
 * there to be at least half a dozen or so continually scheduled events
nkeynes@265
     7
 * (TMU and PVR2), peaking around 20+.
nkeynes@265
     8
 *
nkeynes@265
     9
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@265
    10
 *
nkeynes@265
    11
 * This program is free software; you can redistribute it and/or modify
nkeynes@265
    12
 * it under the terms of the GNU General Public License as published by
nkeynes@265
    13
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@265
    14
 * (at your option) any later version.
nkeynes@265
    15
 *
nkeynes@265
    16
 * This program is distributed in the hope that it will be useful,
nkeynes@265
    17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@265
    18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@265
    19
 * GNU General Public License for more details.
nkeynes@265
    20
 */
nkeynes@265
    21
nkeynes@265
    22
#include <assert.h>
nkeynes@265
    23
#include "dreamcast.h"
nkeynes@265
    24
#include "eventq.h"
nkeynes@422
    25
#include "asic.h"
nkeynes@586
    26
#include "sh4.h"
nkeynes@265
    27
nkeynes@265
    28
#define LONG_SCAN_PERIOD 1000000000 /* 1 second */
nkeynes@265
    29
nkeynes@265
    30
typedef struct event {
nkeynes@265
    31
    uint32_t id;
nkeynes@265
    32
    uint32_t seconds;
nkeynes@265
    33
    uint32_t nanosecs;
nkeynes@265
    34
    event_func_t func;
nkeynes@265
    35
nkeynes@265
    36
    struct event *next;
nkeynes@265
    37
} *event_t;
nkeynes@265
    38
nkeynes@265
    39
static struct event events[MAX_EVENT_ID];
nkeynes@265
    40
nkeynes@265
    41
/**
nkeynes@265
    42
 * Countdown to the next scan of the long-duration list (greater than 1 second).
nkeynes@265
    43
 */
nkeynes@265
    44
static int long_scan_time_remaining;
nkeynes@265
    45
nkeynes@265
    46
static event_t event_head;
nkeynes@265
    47
static event_t long_event_head;
nkeynes@265
    48
nkeynes@265
    49
void event_reset();
nkeynes@265
    50
void event_init();
nkeynes@265
    51
uint32_t event_run_slice( uint32_t nanosecs );
nkeynes@265
    52
void event_save_state( FILE *f );
nkeynes@265
    53
int event_load_state( FILE * f );
nkeynes@265
    54
nkeynes@265
    55
struct dreamcast_module eventq_module = { "EVENTQ", event_init, event_reset, NULL, event_run_slice,
nkeynes@265
    56
					NULL, event_save_state, event_load_state };
nkeynes@265
    57
nkeynes@265
    58
static void event_update_pending( ) 
nkeynes@265
    59
{
nkeynes@265
    60
    if( event_head == NULL ) {
nkeynes@265
    61
	if( !(sh4r.event_types & PENDING_IRQ) ) {
nkeynes@265
    62
	    sh4r.event_pending = NOT_SCHEDULED;
nkeynes@265
    63
	}
nkeynes@265
    64
	sh4r.event_types &= (~PENDING_EVENT);
nkeynes@265
    65
    } else {
nkeynes@265
    66
	if( !(sh4r.event_types & PENDING_IRQ) ) {
nkeynes@265
    67
	    sh4r.event_pending = event_head->nanosecs;
nkeynes@265
    68
	}
nkeynes@265
    69
	sh4r.event_types |= PENDING_EVENT;
nkeynes@265
    70
    }
nkeynes@265
    71
}
nkeynes@265
    72
nkeynes@265
    73
uint32_t event_get_next_time( ) 
nkeynes@265
    74
{
nkeynes@265
    75
    if( event_head == NULL ) {
nkeynes@265
    76
	return NOT_SCHEDULED;
nkeynes@265
    77
    } else {
nkeynes@265
    78
	return event_head->nanosecs;
nkeynes@265
    79
    }
nkeynes@265
    80
}
nkeynes@265
    81
nkeynes@265
    82
/**
nkeynes@265
    83
 * Add the event to the short queue.
nkeynes@265
    84
 */
nkeynes@265
    85
static void event_enqueue( event_t event ) 
nkeynes@265
    86
{
nkeynes@265
    87
    if( event_head == NULL || event->nanosecs < event_head->nanosecs ) {
nkeynes@265
    88
	event->next = event_head;
nkeynes@265
    89
	event_head = event;
nkeynes@265
    90
	event_update_pending();
nkeynes@265
    91
    } else {
nkeynes@265
    92
	event_t cur = event_head;
nkeynes@265
    93
	event_t next = cur->next;
nkeynes@265
    94
	while( next != NULL && event->nanosecs >= next->nanosecs ) {
nkeynes@265
    95
	    cur = next;
nkeynes@265
    96
	    next = cur->next;
nkeynes@265
    97
	}
nkeynes@265
    98
	event->next = next;
nkeynes@265
    99
	cur->next = event;
nkeynes@265
   100
    }
nkeynes@265
   101
}
nkeynes@265
   102
nkeynes@265
   103
static void event_dequeue( event_t event )
nkeynes@265
   104
{
nkeynes@265
   105
    if( event_head == NULL ) {
nkeynes@265
   106
	ERROR( "Empty event queue but should contain event %d", event->id );
nkeynes@265
   107
    } else if( event_head == event ) {
nkeynes@265
   108
	/* removing queue head */
nkeynes@265
   109
	event_head = event_head->next;
nkeynes@265
   110
	event_update_pending();
nkeynes@265
   111
    } else {
nkeynes@265
   112
	event_t cur = event_head;
nkeynes@265
   113
	event_t next = cur->next;
nkeynes@265
   114
	while( next != NULL ) {
nkeynes@265
   115
	    if( next == event ) {
nkeynes@265
   116
		cur->next = next->next;
nkeynes@265
   117
		break;
nkeynes@265
   118
	    }
nkeynes@265
   119
	    cur = next;
nkeynes@265
   120
	    next = cur->next;
nkeynes@265
   121
	}
nkeynes@265
   122
    }
nkeynes@265
   123
}
nkeynes@265
   124
nkeynes@265
   125
static void event_dequeue_long( event_t event ) 
nkeynes@265
   126
{
nkeynes@265
   127
    if( long_event_head == NULL ) {
nkeynes@265
   128
	ERROR( "Empty long event queue but should contain event %d", event->id );
nkeynes@265
   129
    } else if( long_event_head == event ) {
nkeynes@265
   130
	/* removing queue head */
nkeynes@265
   131
	long_event_head = long_event_head->next;
nkeynes@265
   132
    } else {
nkeynes@265
   133
	event_t cur = long_event_head;
nkeynes@265
   134
	event_t next = cur->next;
nkeynes@265
   135
	while( next != NULL ) {
nkeynes@265
   136
	    if( next == event ) {
nkeynes@265
   137
		cur->next = next->next;
nkeynes@265
   138
		break;
nkeynes@265
   139
	    }
nkeynes@265
   140
	    cur = next;
nkeynes@265
   141
	    next = cur->next;
nkeynes@265
   142
	}
nkeynes@265
   143
    }
nkeynes@265
   144
}
nkeynes@265
   145
nkeynes@265
   146
void register_event_callback( int eventid, event_func_t func )
nkeynes@265
   147
{
nkeynes@265
   148
    events[eventid].func = func;
nkeynes@265
   149
}
nkeynes@265
   150
nkeynes@265
   151
void event_schedule( int eventid, uint32_t nanosecs )
nkeynes@265
   152
{
nkeynes@265
   153
    nanosecs += sh4r.slice_cycle;
nkeynes@265
   154
nkeynes@265
   155
    event_t event = &events[eventid];
nkeynes@265
   156
nkeynes@265
   157
    if( event->nanosecs != NOT_SCHEDULED ) {
nkeynes@265
   158
	/* Event is already scheduled. Remove it from the list first */
nkeynes@265
   159
	event_cancel(eventid);
nkeynes@265
   160
    }
nkeynes@265
   161
nkeynes@265
   162
    event->id = eventid;
nkeynes@265
   163
    event->seconds = 0;
nkeynes@265
   164
    event->nanosecs = nanosecs;
nkeynes@265
   165
    
nkeynes@265
   166
    event_enqueue( event );
nkeynes@265
   167
}
nkeynes@265
   168
nkeynes@265
   169
void event_schedule_long( int eventid, uint32_t seconds, uint32_t nanosecs ) {
nkeynes@265
   170
    if( seconds == 0 ) {
nkeynes@265
   171
	event_schedule( eventid, nanosecs );
nkeynes@265
   172
    } else {
nkeynes@265
   173
	event_t event = &events[eventid];
nkeynes@265
   174
nkeynes@265
   175
	if( event->nanosecs != NOT_SCHEDULED ) {
nkeynes@265
   176
	    /* Event is already scheduled. Remove it from the list first */
nkeynes@265
   177
	    event_cancel(eventid);
nkeynes@265
   178
	}
nkeynes@265
   179
nkeynes@265
   180
	event->id = eventid;
nkeynes@265
   181
	event->seconds = seconds;
nkeynes@265
   182
	event->nanosecs = nanosecs;
nkeynes@265
   183
	event->next = long_event_head;
nkeynes@265
   184
	long_event_head = event;
nkeynes@265
   185
    }
nkeynes@265
   186
	
nkeynes@265
   187
}
nkeynes@265
   188
nkeynes@265
   189
void event_cancel( int eventid )
nkeynes@265
   190
{
nkeynes@265
   191
    event_t event = &events[eventid];
nkeynes@265
   192
    if( event->nanosecs == NOT_SCHEDULED ) {
nkeynes@265
   193
	return; /* not scheduled */
nkeynes@265
   194
    } else {
nkeynes@265
   195
	event->nanosecs = NOT_SCHEDULED;
nkeynes@265
   196
	if( event->seconds != 0 ) { /* long term event */
nkeynes@265
   197
	    event_dequeue_long( event );
nkeynes@265
   198
	} else {
nkeynes@265
   199
	    event_dequeue( event );
nkeynes@265
   200
	}
nkeynes@265
   201
    }
nkeynes@265
   202
}
nkeynes@265
   203
nkeynes@265
   204
nkeynes@265
   205
void event_execute()
nkeynes@265
   206
{
nkeynes@265
   207
    /* Loop in case we missed some or got a couple scheduled for the same time */
nkeynes@265
   208
    while( event_head != NULL && event_head->nanosecs <= sh4r.slice_cycle ) {
nkeynes@265
   209
	event_t event = event_head;
nkeynes@265
   210
	event_head = event->next;
nkeynes@265
   211
	event->nanosecs = NOT_SCHEDULED;
nkeynes@265
   212
	// Note: Make sure the internal state is consistent before calling the
nkeynes@265
   213
	// user function, as it will (quite likely) enqueue another event.
nkeynes@265
   214
	event->func( event->id );
nkeynes@265
   215
    }
nkeynes@265
   216
nkeynes@265
   217
    event_update_pending();
nkeynes@265
   218
}
nkeynes@265
   219
nkeynes@265
   220
void event_asic_callback( int eventid )
nkeynes@265
   221
{
nkeynes@265
   222
    asic_event( eventid );
nkeynes@265
   223
}
nkeynes@265
   224
nkeynes@265
   225
void event_init()
nkeynes@265
   226
{
nkeynes@265
   227
    int i;
nkeynes@265
   228
    for( i=0; i<MAX_EVENT_ID; i++ ) {
nkeynes@265
   229
	events[i].id = i;
nkeynes@265
   230
	events[i].nanosecs = NOT_SCHEDULED;
nkeynes@265
   231
	if( i < 96 ) {
nkeynes@265
   232
	    events[i].func = event_asic_callback;
nkeynes@265
   233
	} else {
nkeynes@265
   234
	    events[i].func = NULL;
nkeynes@265
   235
	}
nkeynes@265
   236
	events[i].next = NULL;
nkeynes@265
   237
    }
nkeynes@265
   238
    event_head = NULL;
nkeynes@265
   239
    long_event_head = NULL;
nkeynes@265
   240
    long_scan_time_remaining = LONG_SCAN_PERIOD;
nkeynes@265
   241
}
nkeynes@265
   242
nkeynes@265
   243
nkeynes@265
   244
nkeynes@265
   245
void event_reset()
nkeynes@265
   246
{
nkeynes@265
   247
    int i;
nkeynes@265
   248
    event_head = NULL;
nkeynes@265
   249
    long_event_head = NULL;
nkeynes@265
   250
    long_scan_time_remaining = LONG_SCAN_PERIOD;
nkeynes@265
   251
    for( i=0; i<MAX_EVENT_ID; i++ ) {
nkeynes@265
   252
	events[i].nanosecs = NOT_SCHEDULED;
nkeynes@265
   253
    }
nkeynes@265
   254
}
nkeynes@265
   255
nkeynes@265
   256
void event_save_state( FILE *f )
nkeynes@265
   257
{
nkeynes@265
   258
    int id, i;
nkeynes@265
   259
    id = event_head == NULL ? -1 : event_head->id;
nkeynes@265
   260
    fwrite( &id, sizeof(id), 1, f );
nkeynes@265
   261
    id = long_event_head == NULL ? -1 : long_event_head->id;
nkeynes@265
   262
    fwrite( &id, sizeof(id), 1, f );
nkeynes@265
   263
    fwrite( &long_scan_time_remaining, sizeof(long_scan_time_remaining), 1, f );
nkeynes@265
   264
    for( i=0; i<MAX_EVENT_ID; i++ ) {
nkeynes@265
   265
	fwrite( &events[i].id, sizeof(uint32_t), 3, f ); /* First 3 words from structure */
nkeynes@265
   266
	id = events[i].next == NULL ? -1 : events[i].next->id;
nkeynes@265
   267
	fwrite( &id, sizeof(id), 1, f );
nkeynes@265
   268
    }
nkeynes@265
   269
}
nkeynes@265
   270
nkeynes@265
   271
int event_load_state( FILE *f )
nkeynes@265
   272
{
nkeynes@265
   273
    int id, i;
nkeynes@265
   274
    fread( &id, sizeof(id), 1, f );
nkeynes@265
   275
    event_head = id == -1 ? NULL : &events[id];
nkeynes@265
   276
    fread( &id, sizeof(id), 1, f );
nkeynes@265
   277
    long_event_head = id == -1 ? NULL : &events[id];
nkeynes@265
   278
    fread( &long_scan_time_remaining, sizeof(long_scan_time_remaining), 1, f );
nkeynes@265
   279
    for( i=0; i<MAX_EVENT_ID; i++ ) {
nkeynes@265
   280
	fread( &events[i].id, sizeof(uint32_t), 3, f );
nkeynes@265
   281
	fread( &id, sizeof(id), 1, f );
nkeynes@265
   282
	events[i].next = id == -1 ? NULL : &events[id];
nkeynes@265
   283
    }
nkeynes@265
   284
    return 0;
nkeynes@265
   285
}
nkeynes@265
   286
nkeynes@265
   287
/**
nkeynes@265
   288
 * Scan all entries in the long queue, decrementing each by 1 second. Entries
nkeynes@265
   289
 * that are now < 1 second are moved to the short queue.
nkeynes@265
   290
 */
nkeynes@265
   291
static void event_scan_long()
nkeynes@265
   292
{
nkeynes@265
   293
    while( long_event_head != NULL && --long_event_head->seconds == 0 ) {
nkeynes@265
   294
	event_t event = long_event_head;
nkeynes@265
   295
	long_event_head = event->next;
nkeynes@265
   296
	event_enqueue(event);
nkeynes@265
   297
    }
nkeynes@265
   298
nkeynes@265
   299
    if( long_event_head != NULL ) {
nkeynes@265
   300
	event_t last = long_event_head;
nkeynes@265
   301
	event_t cur = last->next;
nkeynes@265
   302
	while( cur != NULL ) {
nkeynes@265
   303
	    if( --cur->seconds == 0 ) {
nkeynes@265
   304
		last->next = cur->next;
nkeynes@265
   305
		event_enqueue(cur);
nkeynes@265
   306
	    } else {
nkeynes@265
   307
		last = cur;
nkeynes@265
   308
	    }
nkeynes@265
   309
	    cur = last->next;
nkeynes@265
   310
	}
nkeynes@265
   311
    }
nkeynes@265
   312
}
nkeynes@265
   313
nkeynes@265
   314
/**
nkeynes@265
   315
 * Decrement the event time on all pending events by the supplied nanoseconds.
nkeynes@265
   316
 * It may or may not be faster to wrap around instead, but this has the benefit
nkeynes@265
   317
 * of simplicity.
nkeynes@265
   318
 */
nkeynes@265
   319
uint32_t event_run_slice( uint32_t nanosecs )
nkeynes@265
   320
{
nkeynes@265
   321
    event_t event = event_head;
nkeynes@265
   322
    while( event != NULL ) {
nkeynes@265
   323
	if( event->nanosecs <= nanosecs ) {
nkeynes@265
   324
	    event->nanosecs = 0;
nkeynes@265
   325
	} else {
nkeynes@265
   326
	    event->nanosecs -= nanosecs;
nkeynes@265
   327
	}
nkeynes@265
   328
	event = event->next;
nkeynes@265
   329
    }
nkeynes@265
   330
nkeynes@265
   331
    long_scan_time_remaining -= nanosecs;
nkeynes@265
   332
    if( long_scan_time_remaining <= 0 ) {
nkeynes@265
   333
	long_scan_time_remaining += LONG_SCAN_PERIOD;
nkeynes@265
   334
	event_scan_long();
nkeynes@265
   335
    }
nkeynes@265
   336
nkeynes@265
   337
    event_update_pending();
nkeynes@265
   338
    return nanosecs;
nkeynes@265
   339
}
nkeynes@265
   340
.