1 // $Id: saveundo.c,v 1.15 2003/10/20 16:05:06 iain Exp $
8 typedef const git_uint8 * MemoryPage;
9 typedef MemoryPage * MemoryMap;
11 typedef struct UndoRecord UndoRecord;
25 static UndoRecord * gUndo = NULL;
26 static git_uint32 gUndoSize = 0;
27 static git_uint32 gMaxUndoSize = 256 * 1024;
29 static void reserveSpace (git_uint32);
30 static void deleteRecord (UndoRecord * u);
32 void initUndo (git_uint32 size)
39 int saveUndo (git_sint32 * base, git_sint32 * sp)
41 git_uint32 undoSize = sizeof(UndoRecord);
42 git_uint32 mapSize = sizeof(MemoryPage*) * (gEndMem - gRamStart) / 256;
43 git_uint32 stackSize = sizeof(git_sint32) * (sp - base);
44 git_uint32 totalSize = undoSize + mapSize + stackSize;
46 git_uint32 addr = gRamStart; // Address in glulx memory.
47 git_uint32 slot = 0; // Slot in our memory map.
49 UndoRecord * undo = malloc (undoSize);
51 fatalError ("Couldn't allocate undo record");
53 undo->endMem = gEndMem;
54 undo->memoryMap = malloc (mapSize);
55 undo->stackSize = stackSize;
56 undo->stack = malloc (stackSize);
60 if (undo->memoryMap == NULL || undo->stack == NULL)
61 fatalError ("Couldn't allocate memory for undo");
64 memcpy (undo->stack, base, undo->stackSize);
66 // Are we diffing against the previous undo record,
67 // or against the initial gamefile state?
70 // We're diffing against the gamefile.
71 for ( ; addr < gExtStart ; addr += 256, ++slot)
73 if (memcmp (gRom + addr, gRam + addr, 256) != 0)
75 // We need to save this page.
76 git_uint8 * page = malloc(256);
78 fatalError ("Couldn't allocate memory for undo");
80 memcpy (page, gRam + addr, 256);
81 undo->memoryMap[slot] = page;
86 // We don't need to save this page.
87 // Just make it point into ROM.
88 undo->memoryMap[slot] = gRom + addr;
92 // If the memory map has been extended, save the exended area
93 for (addr = gExtStart ; addr < gEndMem ; addr += 256, ++slot)
95 git_uint8 * page = malloc(256);
97 fatalError ("Couldn't allocate memory for undo");
99 memcpy (page, gRam + addr, 256);
100 undo->memoryMap[slot] = page;
106 // We're diffing against the most recent undo record.
107 git_uint32 endMem = (gUndo->endMem < gEndMem) ? gUndo->endMem : gEndMem;
108 for ( ; addr < endMem ; addr += 256, ++slot)
110 if (memcmp (gUndo->memoryMap [slot], gRam + addr, 256) != 0)
112 // We need to save this page.
113 git_uint8 * page = malloc(256);
114 memcpy (page, gRam + addr, 256);
115 undo->memoryMap[slot] = page;
120 // We don't need to save this page. Just copy
121 // the pointer from the previous undo record.
122 undo->memoryMap[slot] = gUndo->memoryMap[slot];
126 // If the memory map has been extended, save the exended area
127 for (addr = endMem ; addr < gEndMem ; addr += 256, ++slot)
129 git_uint8 * page = malloc(256);
131 fatalError ("Couldn't allocate memory for undo");
133 memcpy (page, gRam + addr, 256);
134 undo->memoryMap[slot] = page;
140 if (heap_get_summary (&(undo->heapSize), &(undo->heap)))
141 fatalError ("Couldn't get heap summary");
142 totalSize += undo->heapSize * 4;
144 // Link this record into the undo list.
151 gUndoSize += totalSize;
153 // Delete old records until we have enough free space.
160 int restoreUndo (git_sint32* base, git_uint32 protectPos, git_uint32 protectSize)
169 UndoRecord * undo = gUndo;
171 git_uint32 addr = gRamStart; // Address in glulx memory.
172 MemoryMap map = undo->memoryMap; // Glulx memory map.
174 // Restore the size of the memory map
176 resizeMemory (undo->endMem, 1);
178 // Restore the stack.
179 memcpy (base, undo->stack, undo->stackSize);
180 gStackPointer = base + (undo->stackSize / sizeof(git_sint32));
182 // Restore the contents of RAM.
184 if (protectSize > 0 && protectPos < gEndMem)
186 for ( ; addr < (protectPos & ~0xff) ; addr += 256, ++map)
187 memcpy (gRam + addr, *map, 256);
189 memcpy (gRam + addr, *map, protectPos & 0xff);
190 protectSize += protectPos & 0xff;
192 while (protectSize > 256)
193 addr += 256, ++map, protectSize -= 256;
197 memcpy (gRam + addr + protectSize,
204 for ( ; addr < gEndMem ; addr += 256, ++map)
205 memcpy (gRam + addr, *map, 256);
208 if (heap_apply_summary (undo->heapSize, undo->heap))
209 fatalError ("Couldn't apply heap summary");
211 // Delete the undo record.
219 assert (gUndoSize == 0);
228 reserveSpace (gMaxUndoSize);
229 assert (gUndo == NULL);
230 assert (gUndoSize == 0);
238 static void reserveSpace (git_uint32 n)
240 UndoRecord * u = gUndo;
244 // Find the oldest undo record.
249 // Delete records until we've freed up the required amount of space.
251 while (gUndoSize + n > gMaxUndoSize)
255 assert (u->next->prev == u);
258 deleteRecord (u->prev);
267 assert (gUndoSize == 0);
273 static void deleteRecord (UndoRecord * u)
275 git_uint32 addr = gRamStart; // Address in glulx memory.
276 git_uint32 slot = 0; // Slot in our memory map.
278 // Zero out all the slots which are duplicates
279 // of pages held in older undo records.
283 // We're diffing against the previous undo record.
284 while (addr < u->endMem && addr < u->prev->endMem)
286 if (u->memoryMap [slot] == u->prev->memoryMap [slot])
287 u->memoryMap [slot] = NULL;
293 // We're diffing against the gamefile.
294 while (addr < u->endMem && addr < gExtStart)
296 if (u->memoryMap [slot] == (gRom + addr))
297 u->memoryMap [slot] = NULL;
302 // Zero out all the slots which are duplicates
303 // of newer undo records.
310 while (addr < u->endMem && addr < u->next->endMem)
312 if (u->memoryMap [slot] == u->next->memoryMap [slot])
313 u->memoryMap [slot] = NULL;
318 // Free all the slots which are owned by this record only.
322 while (addr < u->endMem)
324 if (u->memoryMap [slot])
326 free ((void*) u->memoryMap [slot]);
332 // Free the memory map itself.
333 free ((void*) u->memoryMap);
334 gUndoSize -= sizeof(MemoryPage*) * (u->endMem - gRamStart) / 256;
338 gUndoSize -= u->stackSize;
342 gUndoSize -= u->heapSize * 4;
344 // Finally, free the record.
346 gUndoSize -= sizeof(UndoRecord);