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