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