1 /*
2 * memory.c
3 *
4 * Code and data caching routines
5 *
6 */
7
8 #include "ztypes.h"
9
10 /* A cache entry */
11
12 typedef struct cache_entry {
13 struct cache_entry *flink;
14 int page_number;
15 zbyte_t data[PAGE_SIZE];
16 } cache_entry_t;
17
18 /* Cache chain anchor */
19
20 static cache_entry_t *cache = NULL;
21
22 /* Pseudo translation buffer, one entry each for code and data pages */
23
24 static unsigned int current_code_page = 0;
25 static cache_entry_t *current_code_cachep = NULL;
26 static unsigned int current_data_page = 0;
27 static cache_entry_t *current_data_cachep = NULL;
28
29 #ifdef __STDC__
30 static unsigned int calc_data_pages (void);
31 static cache_entry_t *update_cache (int);
32 #else
33 static unsigned int calc_data_pages ();
34 static cache_entry_t *update_cache ();
35 #endif
36
37 /*
38 * load_cache
39 *
40 * Initialise the cache and any other dynamic memory objects. The memory
41 * required can be split into two areas. Firstly, three buffers are required for
42 * input, output and status line. Secondly, two data areas are required for
43 * writeable data and read only data. The writeable data is the first chunk of
44 * the file and is put into non-paged cache. The read only data is the remainder
45 * of the file which can be paged into the cache as required. Writeable data has
46 * to be memory resident because it cannot be written out to a backing store.
47 *
48 */
49
50 #ifdef __STDC__
load_cache(void)51 void load_cache (void)
52 #else
53 void load_cache ()
54 #endif
55 {
56 unsigned long file_size;
57 unsigned int i, file_pages, data_pages;
58 cache_entry_t *cachep;
59
60 /* Allocate output and status line buffers */
61
62 /* output buffer is now handled elsewhere --zarf */
63 /*line = (char *) malloc (screen_cols + 1);
64 if (line == NULL)
65 fatal ("Insufficient memory to play game");*/
66
67 status_line = (char *) malloc (screen_cols + 1);
68 if (status_line == NULL)
69 fatal ("Insufficient memory to play game");
70
71 /* Must have at least one cache page for memory calculation */
72
73 cachep = (cache_entry_t *) malloc (sizeof (cache_entry_t));
74 if (cachep == NULL)
75 fatal ("Insufficient memory to play game");
76 cachep->flink = cache;
77 cachep->page_number = 0;
78 cache = cachep;
79
80 /* Calculate dynamic cache pages required */
81
82 if (h_config & CONFIG_MAX_DATA)
83 data_pages = calc_data_pages ();
84 else
85 data_pages = (h_data_size + PAGE_MASK) >> PAGE_SHIFT;
86 data_size = data_pages * PAGE_SIZE;
87 file_size = (unsigned long) h_file_size * story_scaler;
88 file_pages = (unsigned int) ((file_size + PAGE_MASK) >> PAGE_SHIFT);
89
90 /* Allocate static data area and initialise it */
91
92 datap = (zbyte_t *) malloc (data_size);
93 if (datap == NULL)
94 fatal ("Insufficient memory to play game");
95 for (i = 0; i < data_pages; i++)
96 read_page (i, &datap[i * PAGE_SIZE]);
97
98 /* Allocate memory for undo */
99
100 undo_datap = (zbyte_t *) malloc (data_size);
101
102 /* Allocate cache pages and initialise them */
103
104 for (i = data_pages; cachep != NULL && i < file_pages; i++) {
105 cachep = (cache_entry_t *) malloc (sizeof (cache_entry_t));
106 if (cachep != NULL) {
107 cachep->flink = cache;
108 cachep->page_number = i;
109 read_page (cachep->page_number, cachep->data);
110 cache = cachep;
111 }
112 }
113
114 }/* load_cache */
115
116 /*
117 * unload_cache
118 *
119 * Deallocate cache and other memory objects.
120 *
121 */
122
123 #ifdef __STDC__
unload_cache(void)124 void unload_cache (void)
125 #else
126 void unload_cache ()
127 #endif
128 {
129 cache_entry_t *cachep, *nextp;
130
131 /* Make sure all output has been flushed */
132
133 new_line ();
134
135 /* Free output buffer, status line and data memory */
136
137 /*free (line); */ /* this is gone now. --zarf */
138 free (status_line);
139 status_line = NULL;
140 free (datap);
141 datap = NULL;
142 free (undo_datap);
143 undo_datap = NULL;
144
145 /* Free cache memory */
146
147 for (cachep = cache; cachep->flink != NULL; cachep = nextp) {
148 nextp = cachep->flink;
149 free (cachep);
150 }
151
152 }/* unload_cache */
153
154 /*
155 * read_code_word
156 *
157 * Read a word from the instruction stream.
158 *
159 */
160
161 #ifdef __STDC__
read_code_word(void)162 zword_t read_code_word (void)
163 #else
164 zword_t read_code_word ()
165 #endif
166 {
167 zword_t w;
168
169 w = (zword_t) read_code_byte () << 8;
170 w |= (zword_t) read_code_byte ();
171
172 return (w);
173
174 }/* read_code_word */
175
176 /*
177 * read_code_byte
178 *
179 * Read a byte from the instruction stream.
180 *
181 */
182
183 #ifdef __STDC__
read_code_byte(void)184 zbyte_t read_code_byte (void)
185 #else
186 zbyte_t read_code_byte ()
187 #endif
188 {
189 unsigned int page_number, page_offset;
190
191 /* Calculate page and offset values */
192
193 page_number = (unsigned int) (pc >> PAGE_SHIFT);
194 page_offset = (unsigned int) pc & PAGE_MASK;
195
196 /* Load page into translation buffer */
197
198 if (page_number != current_code_page) {
199 current_code_cachep = update_cache (page_number);
200 current_code_page = page_number;
201 }
202
203 /* Update the PC */
204
205 pc++;
206
207 /* Return byte from page offset */
208
209 return (current_code_cachep->data[page_offset]);
210
211 }/* read_code_byte */
212
213 /*
214 * read_data_word
215 *
216 * Read a word from the data area.
217 *
218 */
219
220 #ifdef __STDC__
read_data_word(unsigned long * addr)221 zword_t read_data_word (unsigned long *addr)
222 #else
223 zword_t read_data_word (addr)
224 unsigned long *addr;
225 #endif
226 {
227 zword_t w;
228
229 w = (zword_t) read_data_byte (addr) << 8;
230 w |= (zword_t) read_data_byte (addr);
231
232 return (w);
233
234 }/* read_data_word */
235
236 /*
237 * read_data_byte
238 *
239 * Read a byte from the data area.
240 *
241 */
242
243 #ifdef __STDC__
read_data_byte(unsigned long * addr)244 zbyte_t read_data_byte (unsigned long *addr)
245 #else
246 zbyte_t read_data_byte (addr)
247 unsigned long *addr;
248 #endif
249 {
250 unsigned int page_number, page_offset;
251 zbyte_t value;
252
253 /* Check if byte is in non-paged cache */
254
255 if (*addr < (unsigned long) data_size)
256 value = datap[*addr];
257 else {
258
259 /* Calculate page and offset values */
260
261 page_number = (int) (*addr >> PAGE_SHIFT);
262 page_offset = (int) *addr & PAGE_MASK;
263
264 /* Load page into translation buffer */
265
266 if (page_number != current_data_page) {
267 current_data_cachep = update_cache (page_number);
268 current_data_page = page_number;
269 }
270
271 /* Fetch byte from page offset */
272
273 value = current_data_cachep->data[page_offset];
274 }
275
276 /* Update the address */
277
278 (*addr)++;
279
280 return (value);
281
282 }/* read_data_byte */
283
284 /*
285 * calc_data_pages
286 *
287 * Compute the best size for the data area cache. Some games have the data size
288 * header parameter set too low. This causes a write outside of data area on
289 * some games. To alleviate this problem the data area size is set to the
290 * maximum of the restart size, the data size and the end of the dictionary. An
291 * attempt is made to put the dictionary in the data area to stop paging during
292 * a dictionary lookup. Some games have the dictionary end very close to the
293 * 64K limit which may cause problems for machines that allocate memory in
294 * 64K chunks.
295 *
296 */
297
298 #ifdef __STDC__
calc_data_pages(void)299 static unsigned int calc_data_pages (void)
300 #else
301 static unsigned int calc_data_pages ()
302 #endif
303 {
304 unsigned long offset, data_end, dictionary_end;
305 int separator_count, word_size, word_count;
306 unsigned int data_pages;
307
308 /* Calculate end of data area, use restart size if data size is too low */
309
310 if (h_data_size > h_restart_size)
311 data_end = h_data_size;
312 else
313 data_end = h_restart_size;
314
315 /* Calculate end of dictionary table */
316
317 offset = h_words_offset;
318 separator_count = read_data_byte (&offset);
319 offset += separator_count;
320 word_size = read_data_byte (&offset);
321 word_count = read_data_word (&offset);
322 dictionary_end = offset + (word_size * word_count);
323
324 /* If data end is too low then use end of dictionary instead */
325
326 if (dictionary_end > data_end)
327 data_pages = (unsigned int) ((dictionary_end + PAGE_MASK) >> PAGE_SHIFT);
328 else
329 data_pages = (unsigned int) ((data_end + PAGE_MASK) >> PAGE_SHIFT);
330
331 return (data_pages);
332
333 }/* calc_data_pages */
334
335 /*
336 * update_cache
337 *
338 * Called on a code or data page cache miss to find the page in the cache or
339 * read the page in from disk. The chain is kept as a simple LRU chain. If a
340 * page cannot be found then the page on the end of the chain is reused. If the
341 * page is found, or reused, then it is moved to the front of the chain.
342 *
343 */
344
345 #ifdef __STDC__
update_cache(int page_number)346 static cache_entry_t *update_cache (int page_number)
347 #else
348 static cache_entry_t *update_cache (page_number)
349 int page_number;
350 #endif
351 {
352 cache_entry_t *cachep, *lastp;
353
354 /* Search the cache chain for the page */
355
356 for (lastp = cache, cachep = cache;
357 cachep->flink != NULL &&
358 cachep->page_number &&
359 cachep->page_number != page_number;
360 lastp = cachep, cachep = cachep->flink)
361 ;
362
363 /* If no page in chain then read it from disk */
364
365 if (cachep->page_number != page_number) {
366
367 /* Reusing last cache page, so invalidate cache if page was in use */
368
369 if (cachep->flink == NULL && cachep->page_number) {
370 if (current_code_page == (unsigned int) cachep->page_number)
371 current_code_page = 0;
372 if (current_data_page == (unsigned int) cachep->page_number)
373 current_data_page = 0;
374 }
375
376 /* Load the new page number and the page contents from disk */
377
378 cachep->page_number = page_number;
379 read_page (page_number, cachep->data);
380 }
381
382 /* If page is not at front of cache chain then move it there */
383
384 if (lastp != cache) {
385 lastp->flink = cachep->flink;
386 cachep->flink = cache;
387 cache = cachep;
388 }
389
390 return (cachep);
391
392 }/* update_cache */
393