-
Notifications
You must be signed in to change notification settings - Fork 22
/
glulxe.h
407 lines (351 loc) · 14.9 KB
/
glulxe.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
/* glulxe.h: Glulxe header file.
Designed by Andrew Plotkin <erkyrath@eblong.com>
http://eblong.com/zarf/glulx/index.html
*/
#ifndef _GLULXE_H
#define _GLULXE_H
/* Import definitions for glui32, glsi32, and other Glk types. */
#include "glk.h"
/* We define our own TRUE and FALSE and NULL, because ANSI
is a strange world. */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif
/* If glk.h defined GLK_ATTRIBUTE_NORETURN, great, we'll use it.
(This is a function attribute for functions that never return, e.g
glk_exit().) If we have an older glk.h, that definition is missing,
so we define it as a blank stub. */
#ifndef GLK_ATTRIBUTE_NORETURN
#define GLK_ATTRIBUTE_NORETURN
#endif /* GLK_ATTRIBUTE_NORETURN */
/* If your system does not have <stdint.h>, you'll have to remove this
include line. Then edit the definition of glui16 to make sure it's
really a 16-bit unsigned integer type, and glsi16 to make sure
it's really a 16-bit signed integer type. If they're not, horrible
things will happen. */
#include <stdint.h>
typedef uint16_t glui16;
typedef int16_t glsi16;
/* Comment this definition to turn off memory-address checking. With
verification on, all reads and writes to main memory will be checked
to ensure they're in range. This is slower, but prevents malformed
game files from crashing the interpreter. */
#define VERIFY_MEMORY_ACCESS (1)
/* Uncomment this definition to permit an exception for memory-address
checking for @glk and @copy opcodes that try to write to memory address 0.
This was a bug in old Superglus-built game files. */
/* #define TOLERATE_SUPERGLUS_BUG (1) */
/* Uncomment this definition to turn on Glulx VM profiling. In this
mode, all function calls are timed, and the timing information is
written to a data file called "profile-raw".
(Build note: on Linux, glibc may require you to also define
_BSD_SOURCE or _DEFAULT_SOURCE or both for the timeradd() macro.) */
/* #define VM_PROFILING (1) */
/* Uncomment this definition to turn on the Glulx debugger. You should
only do this when debugging facilities are desired; it slows down
the interpreter. If you do, you will need to build with libxml2;
see the Makefile. */
/* #define VM_DEBUGGER (1) */
/* Comment these definitions to turn off floating-point support. You
might need to do this if you are building on a very limited platform
with no math library.
You can compile this interpreter with FLOAT_SUPPORT and omit
DOUBLE_SUPPORT, but not the other way around. DOUBLE_SUPPORT requires
FLOAT_SUPPORT.
*/
#define FLOAT_SUPPORT (1)
#define DOUBLE_SUPPORT (1)
/* Comment this definition to not cache the original state of RAM in
(real) memory. This saves some memory, but slows down save/restore/undo
operations, which will have to read the original state off disk
every time. */
#define SERIALIZE_CACHE_RAM (1)
/* Some macros to read and write integers to memory, always in big-endian
format. */
#define Read4(ptr) \
( (glui32)((glui32)((unsigned char *)(ptr))[0] << 24) \
| (glui32)((glui32)((unsigned char *)(ptr))[1] << 16) \
| (glui32)((glui32)((unsigned char *)(ptr))[2] << 8) \
| (glui32)((glui32)((unsigned char *)(ptr))[3]))
#define Read2(ptr) \
( (glui16)(((unsigned char *)(ptr))[0] << 8) \
| (glui16)(((unsigned char *)(ptr))[1]))
#define Read1(ptr) \
((unsigned char)(((unsigned char *)(ptr))[0]))
#define Write4(ptr, vl) \
(((ptr)[0] = (unsigned char)(((glui32)(vl)) >> 24)), \
((ptr)[1] = (unsigned char)(((glui32)(vl)) >> 16)), \
((ptr)[2] = (unsigned char)(((glui32)(vl)) >> 8)), \
((ptr)[3] = (unsigned char)(((glui32)(vl)))))
#define Write2(ptr, vl) \
(((ptr)[0] = (unsigned char)(((glui32)(vl)) >> 8)), \
((ptr)[1] = (unsigned char)(((glui32)(vl)))))
#define Write1(ptr, vl) \
(((unsigned char *)(ptr))[0] = (vl))
#if VERIFY_MEMORY_ACCESS
#define Verify(adr, ln) verify_address(adr, ln)
#define VerifyW(adr, ln) verify_address_write(adr, ln)
#define VerifyStk(adr, ln) verify_address_stack(adr, ln)
#else
#define Verify(adr, ln) (0)
#define VerifyW(adr, ln) (0)
#define VerifyStk(adr, ln) (0)
#endif /* VERIFY_MEMORY_ACCESS */
#define Mem1(adr) (Verify(adr, 1), Read1(memmap+(adr)))
#define Mem2(adr) (Verify(adr, 2), Read2(memmap+(adr)))
#define Mem4(adr) (Verify(adr, 4), Read4(memmap+(adr)))
#define MemW1(adr, vl) (VerifyW(adr, 1), Write1(memmap+(adr), (vl)))
#define MemW2(adr, vl) (VerifyW(adr, 2), Write2(memmap+(adr), (vl)))
#define MemW4(adr, vl) (VerifyW(adr, 4), Write4(memmap+(adr), (vl)))
/* Macros to access values on the stack. These *must* be used
with proper alignment! (That is, Stk4 and StkW4 must take
addresses which are multiples of four, etc.) If the alignment
rules are not followed, the program will see performance
degradation or even crashes, depending on the machine CPU. */
#define Stk1(adr) \
(VerifyStk(adr, 1), *((unsigned char *)(stack+(adr))))
#define Stk2(adr) \
(VerifyStk(adr, 2), *((glui16 *)(stack+(adr))))
#define Stk4(adr) \
(VerifyStk(adr, 4), *((glui32 *)(stack+(adr))))
#define StkW1(adr, vl) \
(VerifyStk(adr, 1), *((unsigned char *)(stack+(adr))) = (unsigned char)(vl))
#define StkW2(adr, vl) \
(VerifyStk(adr, 2), *((glui16 *)(stack+(adr))) = (glui16)(vl))
#define StkW4(adr, vl) \
(VerifyStk(adr, 4), *((glui32 *)(stack+(adr))) = (glui32)(vl))
/* Some useful structures. */
/* oparg_t:
Represents one operand value to an instruction being executed. The
code in exec.c assumes that no instruction has more than MAX_OPERANDS
of these.
*/
typedef struct oparg_struct {
glui32 desttype;
glui32 value;
} oparg_t;
#define MAX_OPERANDS (8)
/* operandlist_t:
Represents the operand structure of an opcode.
*/
typedef struct operandlist_struct {
int num_ops; /* Number of operands for this opcode */
int arg_size; /* Usually 4, but can be 1 or 2 */
int *formlist; /* Array of values, either modeform_Load or modeform_Store */
} operandlist_t;
#define modeform_Load (1)
#define modeform_Store (2)
/* Some useful globals */
extern int vm_exited_cleanly;
extern strid_t gamefile;
extern glui32 gamefile_start, gamefile_len;
extern char *init_err, *init_err2;
extern unsigned char *memmap;
extern unsigned char *stack;
extern glui32 ramstart;
extern glui32 endgamefile;
extern glui32 origendmem;
extern glui32 stacksize;
extern glui32 startfuncaddr;
extern glui32 checksum;
extern glui32 stackptr;
extern glui32 frameptr;
extern glui32 pc;
extern glui32 origstringtable;
extern glui32 stringtable;
extern glui32 valstackbase;
extern glui32 localsbase;
extern glui32 endmem;
extern glui32 protectstart, protectend;
extern glui32 prevpc;
extern void (*stream_char_handler)(unsigned char ch);
extern void (*stream_unichar_handler)(glui32 ch);
/* main.c */
extern glui32 init_rng_seed;
extern void set_library_start_hook(void (*)(void));
extern void set_library_autorestore_hook(void (*)(void));
extern void fatal_error_handler(char *str, char *arg, int useval, glsi32 val) GLK_ATTRIBUTE_NORETURN;
extern void nonfatal_warning_handler(char *str, char *arg, int useval, glsi32 val);
#define fatal_error(s) (fatal_error_handler((s), NULL, FALSE, 0))
#define fatal_error_2(s1, s2) (fatal_error_handler((s1), (s2), FALSE, 0))
#define fatal_error_i(s, v) (fatal_error_handler((s), NULL, TRUE, (v)))
#define nonfatal_warning(s) (nonfatal_warning_handler((s), NULL, FALSE, 0))
#define nonfatal_warning_2(s1, s2) (nonfatal_warning_handler((s1), (s2), FALSE, 0))
#define nonfatal_warning_i(s, v) (nonfatal_warning_handler((s), NULL, TRUE, (v)))
/* files.c */
extern int is_gamefile_valid(void);
extern int locate_gamefile(int isblorb);
/* vm.c */
extern void setup_vm(void);
extern void finalize_vm(void);
extern void vm_restart(void);
extern glui32 change_memsize(glui32 newlen, int internal);
extern glui32 *pop_arguments(glui32 count, glui32 addr);
extern void verify_address(glui32 addr, glui32 count);
extern void verify_address_write(glui32 addr, glui32 count);
extern void verify_address_stack(glui32 stackpos, glui32 count);
extern void verify_array_addresses(glui32 addr, glui32 count, glui32 size);
/* exec.c */
extern void execute_loop(void);
/* operand.c */
extern const operandlist_t *fast_operandlist[0x80];
extern void init_operands(void);
extern const operandlist_t *lookup_operandlist(glui32 opcode);
extern void parse_operands(oparg_t *opargs, const operandlist_t *oplist);
extern void store_operand(glui32 desttype, glui32 destaddr, glui32 storeval);
extern void store_operand_s(glui32 desttype, glui32 destaddr, glui32 storeval);
extern void store_operand_b(glui32 desttype, glui32 destaddr, glui32 storeval);
/* funcs.c */
extern void enter_function(glui32 addr, glui32 argc, glui32 *argv);
extern void leave_function(void);
extern void push_callstub(glui32 desttype, glui32 destaddr);
extern void pop_callstub(glui32 returnvalue);
extern glui32 pop_callstub_string(int *bitnum);
/* string.c */
extern void stream_num(glsi32 val, int inmiddle, int charnum);
extern void stream_string(glui32 addr, int inmiddle, int bitnum);
extern glui32 stream_get_table(void);
extern void stream_set_table(glui32 addr);
extern void stream_get_iosys(glui32 *mode, glui32 *rock);
extern void stream_set_iosys(glui32 mode, glui32 rock);
extern char *make_temp_string(glui32 addr);
extern glui32 *make_temp_ustring(glui32 addr);
extern void free_temp_string(char *str);
extern void free_temp_ustring(glui32 *str);
/* heap.c */
extern void heap_clear(void);
extern int heap_is_active(void);
extern glui32 heap_get_start(void);
extern glui32 heap_alloc(glui32 len);
extern void heap_free(glui32 addr);
extern int heap_get_summary(glui32 *valcount, glui32 **summary);
extern int heap_apply_summary(glui32 valcount, glui32 *summary);
extern void heap_sanity_check(void);
/* serial.c */
extern int max_undo_level;
extern int init_serial(void);
extern void final_serial(void);
extern glui32 perform_save(strid_t str);
extern glui32 perform_restore(strid_t str, int fromshell);
extern glui32 perform_saveundo(void);
extern glui32 perform_restoreundo(void);
extern glui32 has_undo(void);
extern void discard_undo(void);
extern glui32 perform_verify(void);
/* search.c */
extern glui32 linear_search(glui32 key, glui32 keysize,
glui32 start, glui32 structsize, glui32 numstructs,
glui32 keyoffset, glui32 options);
extern glui32 binary_search(glui32 key, glui32 keysize,
glui32 start, glui32 structsize, glui32 numstructs,
glui32 keyoffset, glui32 options);
extern glui32 linked_search(glui32 key, glui32 keysize,
glui32 start, glui32 keyoffset, glui32 nextoffset,
glui32 options);
/* osdepend.c */
extern void *glulx_malloc(glui32 len);
extern void *glulx_realloc(void *ptr, glui32 len);
extern void glulx_free(void *ptr);
extern void glulx_setrandom(glui32 seed);
extern glui32 glulx_random(void);
extern void glulx_random_get_detstate(int *usenative, glui32 **arr, int *count);
extern void glulx_random_set_detstate(int usenative, glui32 *arr, int count);
extern void glulx_sort(void *addr, int count, int size,
int (*comparefunc)(void *p1, void *p2));
/* gestalt.c */
extern glui32 do_gestalt(glui32 val, glui32 val2);
/* glkop.c */
extern void set_library_select_hook(void (*func)(glui32, glui32, glui32, glui32));
extern int init_dispatch(void);
extern glui32 perform_glk(glui32 funcnum, glui32 numargs, glui32 *arglist);
extern strid_t find_stream_by_id(glui32 objid);
extern glui32 find_id_for_window(winid_t win);
extern glui32 find_id_for_stream(strid_t str);
extern glui32 find_id_for_fileref(frefid_t fref);
extern glui32 find_id_for_schannel(schanid_t schan);
/* profile.c */
extern void setup_profile(strid_t stream, char *filename);
extern int init_profile(void);
extern void profile_set_call_counts(int flag);
#if VM_PROFILING
extern glui32 profile_opcount;
#define profile_tick() (profile_opcount++)
extern int profile_profiling_active(void);
extern void profile_in(glui32 addr, glui32 stackuse, int accel);
extern void profile_out(glui32 stackuse);
extern void profile_fail(char *reason);
extern void profile_quit(void);
#else /* VM_PROFILING */
#define profile_tick() (0)
#define profile_profiling_active() (0)
#define profile_in(addr, stackuse, accel) (0)
#define profile_out(stackuse) (0)
#define profile_fail(reason) (0)
#define profile_quit() (0)
#endif /* VM_PROFILING */
#if VM_DEBUGGER
extern unsigned long debugger_opcount;
#define debugger_tick() (debugger_opcount++)
extern int debugger_load_info_stream(strid_t stream);
extern int debugger_load_info_chunk(strid_t stream, glui32 pos, glui32 len);
extern void debugger_track_cpu(int flag);
extern void debugger_set_start_trap(int flag);
extern void debugger_set_quit_trap(int flag);
extern void debugger_set_crash_trap(int flag);
extern void debugger_check_story_file(void);
extern void debugger_setup_start_state(void);
extern int debugger_ever_invoked(void);
extern int debugger_cmd_handler(char *cmd);
extern void debugger_cycle_handler(int cycle);
extern void debugger_check_func_breakpoint(glui32 addr);
extern void debugger_block_and_debug(char *msg);
extern void debugger_handle_crash(char *msg);
extern void debugger_handle_quit(void);
#else /* VM_DEBUGGER */
#define debugger_tick() (0)
#define debugger_check_story_file() (0)
#define debugger_setup_start_state() (0)
#define debugger_check_func_breakpoint(addr) (0)
#define debugger_handle_crash(msg) (0)
#endif /* VM_DEBUGGER */
/* accel.c */
typedef glui32 (*acceleration_func)(glui32 argc, glui32 *argv);
extern void init_accel(void);
extern acceleration_func accel_find_func(glui32 index);
extern acceleration_func accel_get_func(glui32 addr);
extern void accel_set_func(glui32 index, glui32 addr);
extern void accel_set_param(glui32 index, glui32 val);
extern glui32 accel_get_param_count(void);
extern glui32 accel_get_param(glui32 index);
extern void accel_iterate_funcs(void (*func)(glui32 index, glui32 addr));
#ifdef FLOAT_SUPPORT
/* You may have to edit the definition of gfloat32 to make sure it's really
a 32-bit floating-point type. */
typedef float gfloat32;
/* Uncomment this definition if your gfloat32 type is not a standard
IEEE-754 single-precision (32-bit) format. Normally, Glulxe assumes
that it can reinterpret-cast IEEE-754 int values into gfloat32
values. If you uncomment this, Glulxe switches to lengthier
(but safer) encoding and decoding functions. */
/* #define FLOAT_NOT_NATIVE (1) */
/* float.c */
extern int init_float(void);
extern glui32 encode_float(gfloat32 val);
extern gfloat32 decode_float(glui32 val);
extern gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2);
#ifdef DOUBLE_SUPPORT
/* You may have to edit the definition of gfloat64 to make sure it's really
a 64-bit floating-point type. */
typedef double gfloat64;
extern void encode_double(gfloat64 val, glui32 *reshi, glui32 *reslo);
extern gfloat64 decode_double(glui32 valhi, glui32 vallo);
extern gfloat64 glulx_pow(gfloat64 val1, gfloat64 val2);
#endif /* DOUBLE_SUPPORT */
#endif /* FLOAT_SUPPORT */
#endif /* _GLULXE_H */