summaryrefslogtreecommitdiffstats
path: root/firmware/target/hosted/sdl/kernel-sdl.c
blob: d933b9097ef3a924c1ee4cee70461f83b4fd36f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 by Felix Arends
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <SDL.h>
#include <SDL_thread.h>
#include <inttypes.h>
#include "system-sdl.h"
#include "thread-sdl.h"
#include "kernel.h"
#include "thread.h"
#include "panic.h"
#include "debug.h"

static SDL_TimerID tick_timer_id;
long start_tick;

/* Condition to signal that "interrupts" may proceed */
static SDL_cond *sim_thread_cond;
/* Mutex to serialize changing levels and exclude other threads while
 * inside a handler */
static SDL_mutex *sim_irq_mtx;
/* Level: 0 = enabled, not 0 = disabled */
static int volatile interrupt_level = HIGHEST_IRQ_LEVEL;
/* How many handers waiting? Not strictly needed because CondSignal is a
 * noop if no threads were waiting but it filters-out calls to functions
 * with higher overhead and provides info when debugging. */
static int handlers_pending = 0;
/* 1 = executing a handler; prevents CondSignal calls in set_irq_level
 * while in a handler */
static int status_reg = 0;

/* Nescessary logic:
 * 1) All threads must pass unblocked
 * 2) Current handler must always pass unblocked
 * 3) Threads must be excluded when irq routine is running
 * 4) No more than one handler routine should execute at a time
 */
int set_irq_level(int level)
{
    SDL_LockMutex(sim_irq_mtx);

    int oldlevel = interrupt_level;

    if (status_reg == 0 && level == 0 && oldlevel != 0)
    {
        /* Not in a handler and "interrupts" are going from disabled to
         * enabled; signal any pending handlers still waiting */
        if (handlers_pending > 0)
            SDL_CondSignal(sim_thread_cond);
    }

    interrupt_level = level; /* save new level */

    SDL_UnlockMutex(sim_irq_mtx);
    return oldlevel;
}

void sim_enter_irq_handler(void)
{
    SDL_LockMutex(sim_irq_mtx);
    handlers_pending++;

    /* Check each time before proceeding: disabled->enabled->...->disabled
     * is possible on an app thread before a handler thread is ever granted
     * the mutex; a handler can also leave "interrupts" disabled during
     * its execution */
    while (interrupt_level != 0)
        SDL_CondWait(sim_thread_cond, sim_irq_mtx);

    status_reg = 1;
}

void sim_exit_irq_handler(void)
{
    /* If any others are waiting, give the signal */
    if (--handlers_pending > 0)
        SDL_CondSignal(sim_thread_cond);

    status_reg = 0;
    SDL_UnlockMutex(sim_irq_mtx);
}

static bool sim_kernel_init(void)
{
    sim_irq_mtx = SDL_CreateMutex();
    if (sim_irq_mtx == NULL)
    {
        panicf("Cannot create sim_handler_mtx\n");
        return false;
    }

    sim_thread_cond = SDL_CreateCond();
    if (sim_thread_cond == NULL)
    {
        panicf("Cannot create sim_thread_cond\n");
        return false;
    }

    return true;
}

void sim_kernel_shutdown(void)
{
    SDL_RemoveTimer(tick_timer_id);
    SDL_DestroyMutex(sim_irq_mtx);
    SDL_DestroyCond(sim_thread_cond);
}

Uint32 tick_timer(Uint32 interval, void *param)
{
    long new_tick;

    (void) interval;
    (void) param;
    
    new_tick = (SDL_GetTicks() - start_tick) / (1000/HZ);
        
    while(new_tick != current_tick)
    {
        sim_enter_irq_handler();

        /* Run through the list of tick tasks - increments tick
         * on each iteration. */
        call_tick_tasks();

        sim_exit_irq_handler();
    }
    
    return interval;
}

void tick_start(unsigned int interval_in_ms)
{
    if (!sim_kernel_init())
    {
        panicf("Could not initialize kernel!");
        exit(-1);
    }

    if (tick_timer_id != NULL)
    {
        SDL_RemoveTimer(tick_timer_id);
        tick_timer_id = NULL;
    }
    else
    {
        start_tick = SDL_GetTicks();
    }

    tick_timer_id = SDL_AddTimer(interval_in_ms, tick_timer, NULL);
}