-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathasyncc.h
155 lines (128 loc) · 6.64 KB
/
asyncc.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
// @file asyncc.h
// Async for (embedded) C
//
// Copyright (c) 2023 Tom Wolf
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//
#ifndef ASYNCC_H
#define ASYNCC_H
#define ASYNCC_VERSION_MAJOR 0
#define ASYNCC_VERSION_MINOR 0
#define ASYNCC_VERSION_PATCH 1
// This magic is used to allow a nice declaration of local variables in the
// ASYNC_BEGIN() macro (up to 10 locals, which is a reasonable limit and
// can be easily extended with some modifications)
#define FE_0(ACTION)
#define FE_1(ACTION, X) ACTION(X)
#define FE_2(ACTION, X, ...) ACTION(X)FE_1(ACTION, __VA_ARGS__)
#define FE_3(ACTION, X, ...) ACTION(X)FE_2(ACTION, __VA_ARGS__)
#define FE_4(ACTION, X, ...) ACTION(X)FE_3(ACTION, __VA_ARGS__)
#define FE_5(ACTION, X, ...) ACTION(X)FE_4(ACTION, __VA_ARGS__)
#define FE_6(ACTION, X, ...) ACTION(X)FE_4(ACTION, __VA_ARGS__)
#define FE_7(ACTION, X, ...) ACTION(X)FE_4(ACTION, __VA_ARGS__)
#define FE_8(ACTION, X, ...) ACTION(X)FE_4(ACTION, __VA_ARGS__)
#define FE_9(ACTION, X, ...) ACTION(X)FE_4(ACTION, __VA_ARGS__)
#define FE_10(ACTION, X, ...) ACTION(X)FE_4(ACTION, __VA_ARGS__)
#define GET_MACRO(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,NAME,...) NAME
#define FOR_EACH(ACTION,...) \
GET_MACRO(_0,__VA_ARGS__,FE_10,FE_9,FE_8,FE_7,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(ACTION,__VA_ARGS__)
// Some actions
#define L_DEFINE(X) X;
#define L_DEFINES(...) FOR_EACH(L_DEFINE,__VA_ARGS__)
enum async {
ASYNC_INIT,
ASYNC_CONT = ASYNC_INIT,
ASYNC_ERR,
ASYNC_DONE,
};
// IDEA: provide a #define to disable all stack bounds checking (scary)
// Simplest way to provide local state is to expand it to a local struct, point
// it to the top of the stack, and advance the stack index by sizeof(struct)
#ifdef LIVE_DANGEROUSLY
// Init stack index and initial spot within function (no len, live dangerously)
#define async_init(s, len) \
*((uint16_t*)s+0) = 2; \
*((uint16_t*)s+1) = ASYNC_INIT
#define ASYNC_BEGIN(s, ...) \
uint16_t *s_idx = (uint16_t*)(s); \
struct locals { L_DEFINES(uint16_t spot, __VA_ARGS__) } *l; \
l = (struct locals*)(s + *s_idx); \
*s_idx += sizeof(struct locals); \
switch (l->spot) { default:
#else
// Init stack index, max length, and initial spot within function
#define async_init(s, len) \
*((uint16_t*)s+0) = 4; \
*((uint16_t*)s+1) = len; \
*((uint16_t*)s+2) = ASYNC_INIT
#define async_begin(s, ...) \
uint16_t *s_idx = (uint16_t*)(s); \
uint16_t *s_max = (uint16_t*)(s)+1; \
struct locals { L_DEFINES(uint16_t spot, __VA_ARGS__) } *l; \
if ((*s_idx + sizeof(struct locals)) > *s_max) { \
async_err(s, sizeof(struct locals)); \
a_pop(); \
return ASYNC_ERR; \
} else { \
l = (struct locals*)(s + *s_idx); \
a_push(); \
switch (l->spot) { default:
#endif
#define a_push() *s_idx+=sizeof(struct locals)
#define a_pop() *s_idx-=sizeof(struct locals)
#define async_end(s) case ASYNC_DONE: a_pop(); return ASYNC_DONE; } }
#define async_done(s) *((uint16_t*)s+2) = ASYNC_DONE
#define await_while(cond) l->spot = __LINE__; case __LINE__:if (cond) { a_pop(); return ASYNC_CONT; }
#define await(cond) await_while(!(cond))
#define async_yield l->spot = __LINE__; a_pop(); return ASYNC_CONT; case __LINE__:
#define async_exit l->spot = ASYNC_DONE; a_pop(); return ASYNC_DONE
// For those who don't like dereferencing struct members so much:
#define _(v) l->v
// Helpers to get stack index, max len, and current spot
#define IDX(s) *((uint16_t*)s+0)
#define MAX(s) *((uint16_t*)s+1)
#define SPOT(s) *((uint16_t*)s+2)
// Gets the 8-bit stack value for printing
#define SVAL(s,idx) *((uint8_t*)s+idx)
// Print the contents of the stack (for debugging)
#define print_stack(s) \
printf("STACKDUMP: %s(): line %d\n", __FUNCTION__, __LINE__); \
printf(" STACK: %s (%p)\n", #s, s); \
printf(" IDX: 0x%04X (%d), SIZE: 0x%04X (%d)\n", \
IDX(s), IDX(s), MAX(s), MAX(s)); \
printf(" SPOT: %d \n", SPOT(s)); \
printf(" MEMORY: | "); \
for (int i=0; i<0x10; i++) { \
printf("0x%02X ", i); \
} \
printf("\n "); \
for (int i=0; i<0x11; i++) { \
printf("-----"); \
} \
printf("\n"); \
for (int row=0; (row * 0x10) < MAX(s); row++) { \
printf(" 0x%04X: | ", row*0x10); \
for (int i=row*0x10; (i<row*0x10+0x10) && i<MAX(s); i++) { \
printf("0x%02X ", SVAL(s,i)); \
} \
printf("\n"); \
}
#endif // ASYNCC_H