-
Notifications
You must be signed in to change notification settings - Fork 0
/
alarm.h
189 lines (146 loc) · 5.41 KB
/
alarm.h
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
* alarm.h - Alarm handling.
*
* Written by
* Ettore Perazzoli <ettore@comm2000.it>
* Andreas Boose <viceteam@t-online.de>
*
* This file is part of VICE, the Versatile Commodore Emulator.
* See README for copyright notice.
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA.
*
*/
#ifndef VICE_ALARM_H
#define VICE_ALARM_H
#include "types.h"
#define ALARM_CONTEXT_MAX_PENDING_ALARMS 0x100
typedef void (*alarm_callback_t)(CLOCK offset, void *data);
/* An alarm. */
struct alarm_s {
/* Descriptive name of the alarm. */
char *name;
/* Alarm context this alarm is in. */
struct alarm_context_s *context;
/* Callback to be called when the alarm is dispatched. */
alarm_callback_t callback;
/* Index into the pending alarm list. If < 0, the alarm is not
pending. */
int pending_idx;
/* Call data */
void *data;
/* Link to the next and previous alarms in the list. */
struct alarm_s *next, *prev;
};
typedef struct alarm_s alarm_t;
struct pending_alarms_s {
/* The alarm. */
struct alarm_s *alarm;
/* Clock tick at which this alarm should be activated. */
CLOCK clk;
};
typedef struct pending_alarms_s pending_alarms_t;
/* An alarm context. */
struct alarm_context_s {
/* Descriptive name of the alarm context. */
char *name;
/* Alarm list. */
struct alarm_s *alarms;
/* Pending alarm array. Statically allocated because it's slightly
faster this way. */
pending_alarms_t pending_alarms[ALARM_CONTEXT_MAX_PENDING_ALARMS];
unsigned int num_pending_alarms;
/* Clock tick for the next pending alarm. */
CLOCK next_pending_alarm_clk;
/* Pending alarm number. */
int next_pending_alarm_idx;
};
typedef struct alarm_context_s alarm_context_t;
/* ------------------------------------------------------------------------ */
extern alarm_context_t *alarm_context_new(const char *name);
extern void alarm_context_init(alarm_context_t *context, const char *name);
extern void alarm_context_destroy(alarm_context_t *context);
extern void alarm_context_time_warp(alarm_context_t *context, CLOCK warp_amount,
int warp_direction);
extern alarm_t *alarm_new(alarm_context_t *context, const char *name,
alarm_callback_t callback, void *data);
extern void alarm_destroy(alarm_t *alarm);
extern void alarm_unset(alarm_t *alarm);
extern void alarm_log_too_many_alarms(void);
/* ------------------------------------------------------------------------- */
/* Inline functions. */
inline static CLOCK alarm_context_next_pending_clk(alarm_context_t *context)
{
return context->next_pending_alarm_clk;
}
inline static void alarm_context_update_next_pending(alarm_context_t *context)
{
CLOCK next_pending_alarm_clk = (CLOCK)~0L;
unsigned int next_pending_alarm_idx;
unsigned int i;
next_pending_alarm_idx = context->next_pending_alarm_idx;
for (i = 0; i < context->num_pending_alarms; i++) {
CLOCK pending_clk = context->pending_alarms[i].clk;
if (pending_clk <= next_pending_alarm_clk) {
next_pending_alarm_clk = pending_clk;
next_pending_alarm_idx = i;
}
}
context->next_pending_alarm_clk = next_pending_alarm_clk;
context->next_pending_alarm_idx = next_pending_alarm_idx;
}
inline static void alarm_context_dispatch(alarm_context_t *context,
CLOCK cpu_clk)
{
CLOCK offset;
unsigned int idx;
alarm_t *alarm;
offset = (CLOCK)(cpu_clk - context->next_pending_alarm_clk);
idx = context->next_pending_alarm_idx;
alarm = context->pending_alarms[idx].alarm;
(alarm->callback)(offset, alarm->data);
}
inline static void alarm_set(alarm_t *alarm, CLOCK cpu_clk)
{
alarm_context_t *context;
int idx;
context = alarm->context;
idx = alarm->pending_idx;
if (idx < 0) {
unsigned int new_idx;
/* Not pending yet: add. */
new_idx = context->num_pending_alarms;
if (new_idx >= ALARM_CONTEXT_MAX_PENDING_ALARMS) {
alarm_log_too_many_alarms();
return;
}
context->pending_alarms[new_idx].alarm = alarm;
context->pending_alarms[new_idx].clk = cpu_clk;
context->num_pending_alarms++;
if (cpu_clk < context->next_pending_alarm_clk) {
context->next_pending_alarm_clk = cpu_clk;
context->next_pending_alarm_idx = new_idx;
}
alarm->pending_idx = new_idx;
} else {
/* Already pending: modify. */
context->pending_alarms[idx].clk = cpu_clk;
if (context->next_pending_alarm_clk > cpu_clk
|| idx == context->next_pending_alarm_idx)
alarm_context_update_next_pending(context);
}
}
#endif