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 17 20:08:50 2010 +1000 (13 years ago)
permissions -rw-r--r--
last change Refactor shader management to support multiple programs, which are all
defined in the shaders.glsl, rather than split up into one file per
fragment.
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
.