Search
lxdream.org :: lxdream/src/eventq.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/eventq.c
changeset 1119:45602839e067
prev1118:0c00e6594d01
next1187:266e7a1bae90
author nkeynes
date Fri Sep 10 08:48:34 2010 +1000 (11 years ago)
permissions -rw-r--r--
last change Run the eventq at the end of the time-slice rather than the beginning, so
that it runs for the correct period of time when the time-slice finishes
early
file annotate diff log raw
nkeynes@265
     1
/**
nkeynes@561
     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@736
    23
#include "dream.h"
nkeynes@265
    24
#include "dreamcast.h"
nkeynes@265
    25
#include "eventq.h"
nkeynes@422
    26
#include "asic.h"
nkeynes@730
    27
#include "sh4/sh4.h"
nkeynes@265
    28
nkeynes@265
    29
#define LONG_SCAN_PERIOD 1000000000 /* 1 second */
nkeynes@265
    30
nkeynes@265
    31
typedef struct event {
nkeynes@265
    32
    uint32_t id;
nkeynes@265
    33
    uint32_t seconds;
nkeynes@265
    34
    uint32_t nanosecs;
nkeynes@265
    35
    event_func_t func;
nkeynes@265
    36
nkeynes@265
    37
    struct event *next;
nkeynes@265
    38
} *event_t;
nkeynes@265
    39
nkeynes@265
    40
static struct event events[MAX_EVENT_ID];
nkeynes@265
    41
nkeynes@265
    42
/**
nkeynes@265
    43
 * Countdown to the next scan of the long-duration list (greater than 1 second).
nkeynes@265
    44
 */
nkeynes@265
    45
static int long_scan_time_remaining;
nkeynes@265
    46
nkeynes@265
    47
static event_t event_head;
nkeynes@265
    48
static event_t long_event_head;
nkeynes@265
    49
nkeynes@265
    50
void event_reset();
nkeynes@265
    51
void event_init();
nkeynes@265
    52
uint32_t event_run_slice( uint32_t nanosecs );
nkeynes@265
    53
void event_save_state( FILE *f );
nkeynes@265
    54
int event_load_state( FILE * f );
nkeynes@265
    55
nkeynes@1119
    56
struct dreamcast_module eventq_module = { "EVENTQ", NULL, event_reset, NULL, event_run_slice,
nkeynes@736
    57
        NULL, event_save_state, event_load_state };
nkeynes@265
    58
nkeynes@265
    59
static void event_update_pending( ) 
nkeynes@265
    60
{
nkeynes@265
    61
    if( event_head == NULL ) {
nkeynes@736
    62
        if( !(sh4r.event_types & PENDING_IRQ) ) {
nkeynes@736
    63
            sh4r.event_pending = NOT_SCHEDULED;
nkeynes@736
    64
        }
nkeynes@736
    65
        sh4r.event_types &= (~PENDING_EVENT);
nkeynes@265
    66
    } else {
nkeynes@736
    67
        if( !(sh4r.event_types & PENDING_IRQ) ) {
nkeynes@736
    68
            sh4r.event_pending = event_head->nanosecs;
nkeynes@736
    69
        }
nkeynes@736
    70
        sh4r.event_types |= PENDING_EVENT;
nkeynes@265
    71
    }
nkeynes@265
    72
}
nkeynes@265
    73
nkeynes@265
    74
uint32_t event_get_next_time( ) 
nkeynes@265
    75
{
nkeynes@265
    76
    if( event_head == NULL ) {
nkeynes@736
    77
        return NOT_SCHEDULED;
nkeynes@265
    78
    } else {
nkeynes@736
    79
        return event_head->nanosecs;
nkeynes@265
    80
    }
nkeynes@265
    81
}
nkeynes@265
    82
nkeynes@265
    83
/**
nkeynes@265
    84
 * Add the event to the short queue.
nkeynes@265
    85
 */
nkeynes@265
    86
static void event_enqueue( event_t event ) 
nkeynes@265
    87
{
nkeynes@265
    88
    if( event_head == NULL || event->nanosecs < event_head->nanosecs ) {
nkeynes@736
    89
        event->next = event_head;
nkeynes@736
    90
        event_head = event;
nkeynes@736
    91
        event_update_pending();
nkeynes@265
    92
    } else {
nkeynes@736
    93
        event_t cur = event_head;
nkeynes@736
    94
        event_t next = cur->next;
nkeynes@736
    95
        while( next != NULL && event->nanosecs >= next->nanosecs ) {
nkeynes@736
    96
            cur = next;
nkeynes@736
    97
            next = cur->next;
nkeynes@736
    98
        }
nkeynes@736
    99
        event->next = next;
nkeynes@736
   100
        cur->next = event;
nkeynes@265
   101
    }
nkeynes@265
   102
}
nkeynes@265
   103
nkeynes@265
   104
static void event_dequeue( event_t event )
nkeynes@265
   105
{
nkeynes@265
   106
    if( event_head == NULL ) {
nkeynes@736
   107
        ERROR( "Empty event queue but should contain event %d", event->id );
nkeynes@265
   108
    } else if( event_head == event ) {
nkeynes@736
   109
        /* removing queue head */
nkeynes@736
   110
        event_head = event_head->next;
nkeynes@736
   111
        event_update_pending();
nkeynes@265
   112
    } else {
nkeynes@736
   113
        event_t cur = event_head;
nkeynes@736
   114
        event_t next = cur->next;
nkeynes@736
   115
        while( next != NULL ) {
nkeynes@736
   116
            if( next == event ) {
nkeynes@736
   117
                cur->next = next->next;
nkeynes@736
   118
                break;
nkeynes@736
   119
            }
nkeynes@736
   120
            cur = next;
nkeynes@736
   121
            next = cur->next;
nkeynes@736
   122
        }
nkeynes@265
   123
    }
nkeynes@265
   124
}
nkeynes@265
   125
nkeynes@265
   126
static void event_dequeue_long( event_t event ) 
nkeynes@265
   127
{
nkeynes@265
   128
    if( long_event_head == NULL ) {
nkeynes@736
   129
        ERROR( "Empty long event queue but should contain event %d", event->id );
nkeynes@265
   130
    } else if( long_event_head == event ) {
nkeynes@736
   131
        /* removing queue head */
nkeynes@736
   132
        long_event_head = long_event_head->next;
nkeynes@265
   133
    } else {
nkeynes@736
   134
        event_t cur = long_event_head;
nkeynes@736
   135
        event_t next = cur->next;
nkeynes@736
   136
        while( next != NULL ) {
nkeynes@736
   137
            if( next == event ) {
nkeynes@736
   138
                cur->next = next->next;
nkeynes@736
   139
                break;
nkeynes@736
   140
            }
nkeynes@736
   141
            cur = next;
nkeynes@736
   142
            next = cur->next;
nkeynes@736
   143
        }
nkeynes@265
   144
    }
nkeynes@265
   145
}
nkeynes@265
   146
nkeynes@265
   147
void register_event_callback( int eventid, event_func_t func )
nkeynes@265
   148
{
nkeynes@265
   149
    events[eventid].func = func;
nkeynes@265
   150
}
nkeynes@265
   151
nkeynes@265
   152
void event_schedule( int eventid, uint32_t nanosecs )
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@736
   159
        /* Event is already scheduled. Remove it from the list first */
nkeynes@736
   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@736
   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@736
   172
        event_schedule( eventid, nanosecs );
nkeynes@265
   173
    } else {
nkeynes@736
   174
        event_t event = &events[eventid];
nkeynes@265
   175
nkeynes@736
   176
        if( event->nanosecs != NOT_SCHEDULED ) {
nkeynes@736
   177
            /* Event is already scheduled. Remove it from the list first */
nkeynes@736
   178
            event_cancel(eventid);
nkeynes@736
   179
        }
nkeynes@265
   180
nkeynes@736
   181
        event->id = eventid;
nkeynes@736
   182
        event->seconds = seconds;
nkeynes@1118
   183
        event->nanosecs = nanosecs + sh4r.slice_cycle + (LONG_SCAN_PERIOD - long_scan_time_remaining);
nkeynes@736
   184
        event->next = long_event_head;
nkeynes@736
   185
        long_event_head = event;
nkeynes@265
   186
    }
nkeynes@736
   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@736
   194
        return; /* not scheduled */
nkeynes@265
   195
    } else {
nkeynes@736
   196
        event->nanosecs = NOT_SCHEDULED;
nkeynes@736
   197
        if( event->seconds != 0 ) { /* long term event */
nkeynes@736
   198
            event_dequeue_long( event );
nkeynes@736
   199
        } else {
nkeynes@736
   200
            event_dequeue( event );
nkeynes@736
   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@736
   210
        event_t event = event_head;
nkeynes@736
   211
        event_head = event->next;
nkeynes@736
   212
        event->nanosecs = NOT_SCHEDULED;
nkeynes@736
   213
        // Note: Make sure the internal state is consistent before calling the
nkeynes@736
   214
        // user function, as it will (quite likely) enqueue another event.
nkeynes@736
   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@736
   230
        events[i].id = i;
nkeynes@736
   231
        events[i].nanosecs = NOT_SCHEDULED;
nkeynes@736
   232
        if( i < 96 ) {
nkeynes@736
   233
            events[i].func = event_asic_callback;
nkeynes@736
   234
        } else {
nkeynes@736
   235
            events[i].func = NULL;
nkeynes@736
   236
        }
nkeynes@736
   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@736
   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@674
   259
    int32_t 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@736
   266
        fwrite( &events[i].id, sizeof(uint32_t), 3, f ); /* First 3 words from structure */
nkeynes@736
   267
        id = events[i].next == NULL ? -1 : events[i].next->id;
nkeynes@736
   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@674
   274
    int32_t 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@736
   281
        fread( &events[i].id, sizeof(uint32_t), 3, f );
nkeynes@736
   282
        fread( &id, sizeof(id), 1, f );
nkeynes@736
   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@736
   295
        event_t event = long_event_head;
nkeynes@736
   296
        long_event_head = event->next;
nkeynes@736
   297
        event_enqueue(event);
nkeynes@265
   298
    }
nkeynes@265
   299
nkeynes@265
   300
    if( long_event_head != NULL ) {
nkeynes@736
   301
        event_t last = long_event_head;
nkeynes@736
   302
        event_t cur = last->next;
nkeynes@736
   303
        while( cur != NULL ) {
nkeynes@736
   304
            if( --cur->seconds == 0 ) {
nkeynes@736
   305
                last->next = cur->next;
nkeynes@736
   306
                event_enqueue(cur);
nkeynes@736
   307
            } else {
nkeynes@736
   308
                last = cur;
nkeynes@736
   309
            }
nkeynes@736
   310
            cur = last->next;
nkeynes@736
   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@736
   324
        if( event->nanosecs <= nanosecs ) {
nkeynes@736
   325
            event->nanosecs = 0;
nkeynes@736
   326
        } else {
nkeynes@736
   327
            event->nanosecs -= nanosecs;
nkeynes@736
   328
        }
nkeynes@736
   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@736
   334
        long_scan_time_remaining += LONG_SCAN_PERIOD;
nkeynes@736
   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
.