1 /* serial.c: Glulxe code for saving and restoring the VM state.
2 Designed by Andrew Plotkin <erkyrath@eblong.com>
3 http://eblong.com/zarf/glulx/index.html
10 /* This structure allows us to write either to a Glk stream or to
11 a dynamically-allocated memory chunk. */
12 typedef struct dest_struct {
15 /* If it's a Glk stream: */
18 /* If it's a block of memory: */
24 #define IFFID(c1, c2, c3, c4) \
25 ( (((glui32)c1) << 24) \
26 | (((glui32)c2) << 16) \
27 | (((glui32)c3) << 8) \
30 /* This can be adjusted before startup by platform-specific startup
31 code -- that is, preference code. */
32 int max_undo_level = 8;
34 static int undo_chain_size = 0;
35 static int undo_chain_num = 0;
36 unsigned char **undo_chain = NULL;
38 static glui32 protect_pos = 0;
39 static glui32 protect_len = 0;
41 static glui32 write_memstate(dest_t *dest);
42 static glui32 write_heapstate(dest_t *dest, int portable);
43 static glui32 write_stackstate(dest_t *dest, int portable);
44 static glui32 read_memstate(dest_t *dest, glui32 chunklen);
45 static glui32 read_heapstate(dest_t *dest, glui32 chunklen, int portable,
46 glui32 *sumlen, glui32 **summary);
47 static glui32 read_stackstate(dest_t *dest, glui32 chunklen, int portable);
48 static glui32 write_heapstate_sub(glui32 sumlen, glui32 *sumarray,
49 dest_t *dest, int portable);
50 static int sort_heap_summary(void *p1, void *p2);
51 static int write_long(dest_t *dest, glui32 val);
52 static int read_long(dest_t *dest, glui32 *val);
53 static int write_byte(dest_t *dest, unsigned char val);
54 static int read_byte(dest_t *dest, unsigned char *val);
55 static int reposition_write(dest_t *dest, glui32 pos);
58 Set up the undo chain and anything else that needs to be set up.
63 undo_chain_size = max_undo_level;
64 undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size);
71 /* perform_saveundo():
72 Add a state pointer to the undo chain. This returns 0 on success,
75 glui32 perform_saveundo()
79 glui32 memstart, memlen, heapstart, heaplen, stackstart, stacklen;
81 /* The format for undo-saves is simpler than for saves on disk. We
82 just have a memory chunk, a heap chunk, and a stack chunk, in
83 that order. We skip the IFF chunk headers (although the size
84 fields are still there.) We also don't bother with IFF's 16-bit
87 if (undo_chain_size == 0)
98 res = write_long(&dest, 0); /* space for chunk length */
102 res = write_memstate(&dest);
103 memlen = dest.pos - memstart;
106 res = write_long(&dest, 0); /* space for chunk length */
109 heapstart = dest.pos;
110 res = write_heapstate(&dest, FALSE);
111 heaplen = dest.pos - heapstart;
114 res = write_long(&dest, 0); /* space for chunk length */
117 stackstart = dest.pos;
118 res = write_stackstate(&dest, FALSE);
119 stacklen = dest.pos - stackstart;
123 /* Trim it down to the perfect size. */
124 dest.ptr = glulx_realloc(dest.ptr, dest.pos);
129 res = reposition_write(&dest, memstart-4);
132 res = write_long(&dest, memlen);
135 res = reposition_write(&dest, heapstart-4);
138 res = write_long(&dest, heaplen);
141 res = reposition_write(&dest, stackstart-4);
144 res = write_long(&dest, stacklen);
149 if (undo_chain_num >= undo_chain_size) {
150 glulx_free(undo_chain[undo_chain_num-1]);
151 undo_chain[undo_chain_num-1] = NULL;
153 if (undo_chain_size > 1)
154 memmove(undo_chain+1, undo_chain,
155 (undo_chain_size-1) * sizeof(unsigned char *));
156 undo_chain[0] = dest.ptr;
157 if (undo_chain_num < undo_chain_size)
162 /* It didn't work. */
164 glulx_free(dest.ptr);
172 /* perform_restoreundo():
173 Pull a state pointer from the undo chain. This returns 0 on success,
174 1 on failure. Note that if it succeeds, the frameptr, localsbase,
175 and valstackbase registers are invalid; they must be rebuilt from
178 glui32 perform_restoreundo()
182 glui32 heapsumlen = 0;
183 glui32 *heapsumarr = NULL;
185 if (undo_chain_size == 0 || undo_chain_num == 0)
191 dest.ptr = undo_chain[0];
196 res = read_long(&dest, &val);
199 res = read_memstate(&dest, val);
202 res = read_long(&dest, &val);
205 res = read_heapstate(&dest, val, FALSE, &heapsumlen, &heapsumarr);
208 res = read_long(&dest, &val);
211 res = read_stackstate(&dest, val, FALSE);
213 /* ### really, many of the failure modes of those calls ought to
214 cause fatal errors. The stack or main memory may be damaged now. */
218 res = heap_apply_summary(heapsumlen, heapsumarr);
223 if (undo_chain_size > 1)
224 memmove(undo_chain, undo_chain+1,
225 (undo_chain_size-1) * sizeof(unsigned char *));
227 glulx_free(dest.ptr);
231 /* It didn't work. */
239 Write the state to the output stream. This returns 0 on success,
242 glui32 perform_save(strid_t str)
247 glui32 memstart, memlen, stackstart, stacklen, heapstart, heaplen;
248 glui32 filestart, filelen;
250 stream_get_iosys(&val, &lx);
252 /* Not using the Glk I/O system, so bail. This function only
253 knows how to write to a Glk stream. */
254 fatal_error("Streams are only available in Glk I/O system.");
268 /* Quetzal header. */
270 res = write_long(&dest, IFFID('F', 'O', 'R', 'M'));
273 res = write_long(&dest, 0); /* space for file length */
274 filestart = dest.pos;
278 res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */
281 /* Header chunk. This is the first 128 bytes of memory. */
283 res = write_long(&dest, IFFID('I', 'F', 'h', 'd'));
286 res = write_long(&dest, 128);
288 for (ix=0; res==0 && ix<128; ix++) {
289 res = write_byte(&dest, Mem1(ix));
291 /* Always even, so no padding necessary. */
295 res = write_long(&dest, IFFID('C', 'M', 'e', 'm'));
298 res = write_long(&dest, 0); /* space for chunk length */
302 res = write_memstate(&dest);
303 memlen = dest.pos - memstart;
305 if (res == 0 && (memlen & 1) != 0) {
306 res = write_byte(&dest, 0);
311 res = write_long(&dest, IFFID('M', 'A', 'l', 'l'));
314 res = write_long(&dest, 0); /* space for chunk length */
317 heapstart = dest.pos;
318 res = write_heapstate(&dest, TRUE);
319 heaplen = dest.pos - heapstart;
321 /* Always even, so no padding necessary. */
325 res = write_long(&dest, IFFID('S', 't', 'k', 's'));
328 res = write_long(&dest, 0); /* space for chunk length */
331 stackstart = dest.pos;
332 res = write_stackstate(&dest, TRUE);
333 stacklen = dest.pos - stackstart;
335 if (res == 0 && (stacklen & 1) != 0) {
336 res = write_byte(&dest, 0);
339 filelen = dest.pos - filestart;
341 /* Okay, fill in all the lengths. */
343 res = reposition_write(&dest, memstart-4);
346 res = write_long(&dest, memlen);
349 res = reposition_write(&dest, heapstart-4);
352 res = write_long(&dest, heaplen);
355 res = reposition_write(&dest, stackstart-4);
358 res = write_long(&dest, stacklen);
361 res = reposition_write(&dest, filestart-4);
364 res = write_long(&dest, filelen);
372 /* perform_restore():
373 Pull a state pointer from a stream. This returns 0 on success,
374 1 on failure. Note that if it succeeds, the frameptr, localsbase,
375 and valstackbase registers are invalid; they must be rebuilt from
378 glui32 perform_restore(strid_t str)
383 glui32 filestart, filelen;
384 glui32 heapsumlen = 0;
385 glui32 *heapsumarr = NULL;
387 stream_get_iosys(&val, &lx);
389 /* Not using the Glk I/O system, so bail. This function only
390 knows how to read from a Glk stream. */
391 fatal_error("Streams are only available in Glk I/O system.");
405 /* ### the format errors checked below should send error messages to
406 the current stream. */
409 res = read_long(&dest, &val);
411 if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) {
416 res = read_long(&dest, &filelen);
418 filestart = dest.pos;
421 res = read_long(&dest, &val);
423 if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */
428 while (res == 0 && dest.pos < filestart+filelen) {
429 /* Read a chunk and deal with it. */
430 glui32 chunktype, chunkstart, chunklen;
434 res = read_long(&dest, &chunktype);
437 res = read_long(&dest, &chunklen);
439 chunkstart = dest.pos;
441 if (chunktype == IFFID('I', 'F', 'h', 'd')) {
442 for (ix=0; res==0 && ix<128; ix++) {
443 res = read_byte(&dest, &dummy);
444 if (res == 0 && Mem1(ix) != dummy) {
445 /* ### non-matching header */
450 else if (chunktype == IFFID('C', 'M', 'e', 'm')) {
451 res = read_memstate(&dest, chunklen);
453 else if (chunktype == IFFID('M', 'A', 'l', 'l')) {
454 res = read_heapstate(&dest, chunklen, TRUE, &heapsumlen, &heapsumarr);
456 else if (chunktype == IFFID('S', 't', 'k', 's')) {
457 res = read_stackstate(&dest, chunklen, TRUE);
460 /* Unknown chunk type. Skip it. */
461 for (lx=0; res==0 && lx<chunklen; lx++) {
462 res = read_byte(&dest, &dummy);
466 if (chunkstart+chunklen != dest.pos) {
467 /* ### funny chunk length */
471 if ((chunklen & 1) != 0) {
473 res = read_byte(&dest, &dummy);
480 /* The summary might have come from any interpreter, so it could
481 be out of order. We'll sort it. */
482 glulx_sort(heapsumarr+2, (heapsumlen-2)/2, 2*sizeof(glui32),
484 res = heap_apply_summary(heapsumlen, heapsumarr);
494 static int reposition_write(dest_t *dest, glui32 pos)
500 glk_stream_set_position(dest->str, pos, seekmode_Start);
507 static int write_buffer(dest_t *dest, unsigned char *ptr, glui32 len)
510 if (dest->pos+len > dest->size) {
511 dest->size = dest->pos+len+1024;
513 dest->ptr = glulx_malloc(dest->size);
516 dest->ptr = glulx_realloc(dest->ptr, dest->size);
521 memcpy(dest->ptr+dest->pos, ptr, len);
524 glk_put_buffer_stream(dest->str, (char *)ptr, len);
532 static int read_buffer(dest_t *dest, unsigned char *ptr, glui32 len)
537 memcpy(ptr, dest->ptr+dest->pos, len);
540 newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len);
550 static int write_long(dest_t *dest, glui32 val)
552 unsigned char buf[4];
554 return write_buffer(dest, buf, 4);
557 static int write_short(dest_t *dest, glui16 val)
559 unsigned char buf[2];
561 return write_buffer(dest, buf, 2);
564 static int write_byte(dest_t *dest, unsigned char val)
566 return write_buffer(dest, &val, 1);
569 static int read_long(dest_t *dest, glui32 *val)
571 unsigned char buf[4];
572 int res = read_buffer(dest, buf, 4);
579 static int read_short(dest_t *dest, glui16 *val)
581 unsigned char buf[2];
582 int res = read_buffer(dest, buf, 2);
589 static int read_byte(dest_t *dest, unsigned char *val)
591 return read_buffer(dest, val, 1);
594 static glui32 write_memstate(dest_t *dest)
601 res = write_long(dest, endmem);
606 glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
608 for (pos=ramstart; pos<endmem; pos++) {
610 if (pos < endgamefile) {
611 val = glk_get_char_stream(gamefile);
613 fatal_error("The game file ended unexpectedly while saving.");
615 ch ^= (unsigned char)val;
621 /* Write any run we've got. */
627 res = write_byte(dest, 0);
630 res = write_byte(dest, (val-1));
635 /* Write the byte we got. */
636 res = write_byte(dest, ch);
641 /* It's possible we've got a run left over, but we don't write it. */
646 static glui32 read_memstate(dest_t *dest, glui32 chunklen)
648 glui32 chunkend = dest->pos + chunklen;
653 unsigned char ch, ch2;
657 res = read_long(dest, &newlen);
661 res = change_memsize(newlen, FALSE);
666 glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
668 for (pos=ramstart; pos<endmem; pos++) {
669 if (pos < endgamefile) {
670 val = glk_get_char_stream(gamefile);
672 fatal_error("The game file ended unexpectedly while restoring.");
674 ch = (unsigned char)val;
680 if (dest->pos >= chunkend) {
681 /* we're into the final, unstored run. */
687 res = read_byte(dest, &ch2);
691 res = read_byte(dest, &ch2);
694 runlen = (glui32)ch2;
701 if (pos >= protectstart && pos < protectend)
710 static glui32 write_heapstate(dest_t *dest, int portable)
716 res = heap_get_summary(&sumlen, &sumarray);
721 return 0; /* no heap */
723 res = write_heapstate_sub(sumlen, sumarray, dest, portable);
725 glulx_free(sumarray);
729 static glui32 write_heapstate_sub(glui32 sumlen, glui32 *sumarray,
730 dest_t *dest, int portable)
734 /* If we're storing for the purpose of undo, we don't need to do any
735 byte-swapping, because the result will only be used by this session. */
737 res = write_buffer(dest, (void *)sumarray, sumlen*sizeof(glui32));
743 for (lx=0; lx<sumlen; lx++) {
744 res = write_long(dest, sumarray[lx]);
752 static int sort_heap_summary(void *p1, void *p2)
754 glui32 *v1 = (glui32 *)p1;
755 glui32 *v2 = (glui32 *)p2;
764 static glui32 read_heapstate(dest_t *dest, glui32 chunklen, int portable,
765 glui32 *sumlen, glui32 **summary)
767 glui32 res, count, lx;
774 return 0; /* no heap */
777 count = chunklen / sizeof(glui32);
779 arr = glulx_malloc(chunklen);
783 res = read_buffer(dest, (void *)arr, chunklen);
793 count = chunklen / 4;
795 arr = glulx_malloc(count * sizeof(glui32));
799 for (lx=0; lx<count; lx++) {
800 res = read_long(dest, arr+lx);
811 static glui32 write_stackstate(dest_t *dest, int portable)
818 /* If we're storing for the purpose of undo, we don't need to do any
819 byte-swapping, because the result will only be used by this session. */
821 res = write_buffer(dest, stack, stackptr);
827 /* Write a portable stack image. To do this, we have to write stack
828 frames in order, bottom to top. Remember that the last word of
829 every stack frame is a pointer to the beginning of that stack frame.
830 (This includes the last frame, because the save opcode pushes on
831 a call stub before it calls perform_save().) */
833 lastframe = (glui32)(-1);
835 glui32 frameend, frm, frm2, frm3;
836 unsigned char loctype, loccount;
837 glui32 numlocals, frlen, locpos;
839 /* Find the next stack frame (after the one in lastframe). Sadly,
840 this requires searching the stack from the top down. We have to
841 do this for *every* frame, which takes N^2 time overall. But
842 save routines usually aren't nested very deep.
843 If it becomes a practical problem, we can build a stack-frame
844 array, which requires dynamic allocation. */
845 for (frm = stackptr, frameend = stackptr;
846 frm != 0 && (frm2 = Stk4(frm-4)) != lastframe;
847 frameend = frm, frm = frm2) { };
849 /* Write out the frame. */
854 res = write_long(dest, frlen);
859 res = write_long(dest, locpos);
867 loctype = Stk1(frm2);
869 loccount = Stk1(frm2);
872 res = write_byte(dest, loctype);
875 res = write_byte(dest, loccount);
879 if (loctype == 0 && loccount == 0)
885 if ((numlocals & 1) == 0) {
886 res = write_byte(dest, 0);
889 res = write_byte(dest, 0);
895 if (frm2 != frm+locpos)
896 fatal_error("Inconsistent stack frame during save.");
898 /* Write out the locals. */
899 for (lx=0; lx<numlocals; lx++) {
900 loctype = Stk1(frm3);
902 loccount = Stk1(frm3);
905 if (loctype == 0 && loccount == 0)
908 /* Put in up to 0, 1, or 3 bytes of padding, depending on loctype. */
909 while (frm2 & (loctype-1)) {
910 res = write_byte(dest, 0);
916 /* Put in this set of locals. */
921 res = write_byte(dest, Stk1(frm2));
931 res = write_short(dest, Stk2(frm2));
941 res = write_long(dest, Stk4(frm2));
952 if (frm2 != frm+frlen)
953 fatal_error("Inconsistent stack frame during save.");
955 while (frm2 < frameend) {
956 res = write_long(dest, Stk4(frm2));
962 /* Go on to the next frame. */
963 if (frameend == stackptr)
964 break; /* All done. */
971 static glui32 read_stackstate(dest_t *dest, glui32 chunklen, int portable)
975 glui32 frameend, frm, frm2, frm3, locpos, frlen, numlocals;
977 if (chunklen > stacksize)
986 res = read_buffer(dest, stack, stackptr);
992 /* This isn't going to be pleasant; we're going to read the data in
993 as a block, and then convert it in-place. */
994 res = read_buffer(dest, stack, stackptr);
999 while (frameend != 0) {
1000 /* Read the beginning-of-frame pointer. Remember, right now, the
1001 whole frame is stored big-endian. So we have to read with the
1002 Read*() macros, and then write with the StkW*() macros. */
1003 frm = Read4(stack+(frameend-4));
1007 frlen = Read4(stack+frm2);
1010 locpos = Read4(stack+frm2);
1011 StkW4(frm2, locpos);
1014 /* The locals-format list is in bytes, so we don't have to convert it. */
1021 unsigned char loctype, loccount;
1022 loctype = Read1(stack+frm3);
1024 loccount = Read1(stack+frm3);
1027 if (loctype == 0 && loccount == 0)
1030 /* Skip up to 0, 1, or 3 bytes of padding, depending on loctype. */
1031 while (frm2 & (loctype-1)) {
1036 /* Convert this set of locals. */
1041 /* Don't need to convert bytes. */
1049 glui16 loc = Read2(stack+frm2);
1058 glui32 loc = Read4(stack+frm2);
1070 if ((numlocals & 1) == 0) {
1077 if (frm3 != frm+locpos) {
1086 if (frm2 != frm+frlen) {
1090 /* Now, the values pushed on the stack after the call frame itself.
1091 This includes the stub. */
1092 while (frm2 < frameend) {
1093 glui32 loc = Read4(stack+frm2);
1104 glui32 perform_verify()
1106 glui32 len, checksum, newlen;
1107 unsigned char buf[4];
1108 glui32 val, newsum, ix;
1112 if (len < 256 || (len & 0xFF) != 0)
1115 glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
1118 /* Read the header */
1119 for (ix=0; ix<9; ix++) {
1120 newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
1134 /* Read everything else */
1135 for (; ix < len/4; ix++) {
1136 newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
1143 if (newsum != checksum)