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 408 409 410 411 412 413 414 415 416 417 418 419
|
/* $Id: memory.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: memory.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: memory.c,v $
* Revision 1.2 2000/05/25 22:28:56 jholder
* changes routine names to reflect zmachine opcode names per spec 1.0
*
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
*
* imported
*
*
* --------------------------------------------------------------------
*/
/*
* memory.c
*
* Code and data caching routines
*
*/
#include "ztypes.h"
/* A cache entry */
typedef struct cache_entry
{
struct cache_entry *flink;
int page_number;
zbyte_t data[PAGE_SIZE];
}
cache_entry_t;
/* Cache chain anchor */
static cache_entry_t *cache = NULL;
/* Pseudo translation buffer, one entry each for code and data pages */
static unsigned int current_code_page = 0;
static cache_entry_t *current_code_cachep = NULL;
static unsigned int current_data_page = 0;
static cache_entry_t *current_data_cachep = NULL;
static unsigned int calc_data_pages( void );
static cache_entry_t *update_cache( int );
/*
* load_cache
*
* Initialise the cache and any other dynamic memory objects. The memory
* required can be split into two areas. Firstly, three buffers are required for
* input, output and status line. Secondly, two data areas are required for
* writeable data and read only data. The writeable data is the first chunk of
* the file and is put into non-paged cache. The read only data is the remainder
* of the file which can be paged into the cache as required. Writeable data has
* to be memory resident because it cannot be written out to a backing store.
*
*/
void load_cache( void )
{
unsigned long file_size;
unsigned int i, file_pages, data_pages;
cache_entry_t *cachep;
/* Allocate output and status line buffers */
line = ( char * ) malloc( screen_cols + 1 );
if ( line == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
status_line = ( char * ) malloc( screen_cols + 1 );
if ( status_line == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
/* Must have at least one cache page for memory calculation */
cachep = ( cache_entry_t * ) malloc( sizeof ( cache_entry_t ) );
if ( cachep == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
cachep->flink = cache;
cachep->page_number = 0;
cache = cachep;
/* Calculate dynamic cache pages required */
if ( h_config & CONFIG_MAX_DATA )
{
data_pages = calc_data_pages( );
}
else
{
data_pages = ( h_data_size + PAGE_MASK ) >> PAGE_SHIFT;
}
data_size = data_pages * PAGE_SIZE;
file_size = ( unsigned long ) h_file_size *story_scaler;
file_pages = ( unsigned int ) ( ( file_size + PAGE_MASK ) >> PAGE_SHIFT );
/* Allocate static data area and initialise it */
datap = ( zbyte_t * ) malloc( data_size );
if ( datap == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
for ( i = 0; i < data_pages; i++ )
{
read_page( i, &datap[i * PAGE_SIZE] );
}
/* Allocate memory for undo */
undo_datap = ( zbyte_t * ) malloc( data_size );
/* Allocate cache pages and initialise them */
for ( i = data_pages; cachep != NULL && i < file_pages; i++ )
{
cachep = ( cache_entry_t * ) malloc( sizeof ( cache_entry_t ) );
if ( cachep != NULL )
{
cachep->flink = cache;
cachep->page_number = i;
read_page( cachep->page_number, cachep->data );
cache = cachep;
}
}
} /* load_cache */
/*
* unload_cache
*
* Deallocate cache and other memory objects.
*
*/
void unload_cache( void )
{
cache_entry_t *cachep, *nextp;
/* Make sure all output has been flushed */
z_new_line( );
/* Free output buffer, status line and data memory */
free( line );
free( status_line );
free( datap );
free( undo_datap );
/* Free cache memory */
for ( cachep = cache; cachep->flink != NULL; cachep = nextp )
{
nextp = cachep->flink;
free( cachep );
}
} /* unload_cache */
/*
* read_code_word
*
* Read a word from the instruction stream.
*
*/
zword_t read_code_word( void )
{
zword_t w;
w = ( zword_t ) read_code_byte( ) << 8;
w |= ( zword_t ) read_code_byte( );
return ( w );
} /* read_code_word */
/*
* read_code_byte
*
* Read a byte from the instruction stream.
*
*/
zbyte_t read_code_byte( void )
{
unsigned int page_number, page_offset;
/* Calculate page and offset values */
page_number = ( unsigned int ) ( pc >> PAGE_SHIFT );
page_offset = ( unsigned int ) pc & PAGE_MASK;
/* Load page into translation buffer */
if ( page_number != current_code_page )
{
current_code_cachep = update_cache( page_number );
current_code_page = page_number;
}
/* Update the PC */
pc++;
/* Return byte from page offset */
if ( !current_code_cachep )
{
fatal
( "read_code_byte(): read from non-existant page!\n\t(Your dynamic memory usage _may_ be over 64k in size!)" );
}
return ( current_code_cachep->data[page_offset] );
} /* read_code_byte */
/*
* read_data_word
*
* Read a word from the data area.
*
*/
zword_t read_data_word( unsigned long *addr )
{
zword_t w;
w = ( zword_t ) read_data_byte( addr ) << 8;
w |= ( zword_t ) read_data_byte( addr );
return ( w );
} /* read_data_word */
/*
* read_data_byte
*
* Read a byte from the data area.
*
*/
zbyte_t read_data_byte( unsigned long *addr )
{
unsigned int page_number, page_offset;
zbyte_t value=0;
/* Check if byte is in non-paged cache */
if ( *addr < ( unsigned long ) data_size )
{
value = datap[*addr];
}
else
{
/* Calculate page and offset values */
page_number = ( int ) ( *addr >> PAGE_SHIFT );
page_offset = ( int ) *addr & PAGE_MASK;
/* Load page into translation buffer */
if ( page_number != current_data_page )
{
current_data_cachep = update_cache( page_number );
current_data_page = page_number;
}
/* Fetch byte from page offset */
if ( current_data_cachep )
{
value = current_data_cachep->data[page_offset];
}
else
{
fatal( "read_data_byte(): Fetching data from invalid page!" );
}
}
/* Update the address */
( *addr )++;
return ( value );
} /* read_data_byte */
/*
* calc_data_pages
*
* Compute the best size for the data area cache. Some games have the data size
* header parameter set too low. This causes a write outside of data area on
* some games. To alleviate this problem the data area size is set to the
* maximum of the restart size, the data size and the end of the dictionary. An
* attempt is made to put the dictionary in the data area to stop paging during
* a dictionary lookup. Some games have the dictionary end very close to the
* 64K limit which may cause problems for machines that allocate memory in
* 64K chunks.
*
*/
static unsigned int calc_data_pages( void )
{
unsigned long offset, data_end, dictionary_end;
int separator_count, word_size, word_count;
unsigned int data_pages;
/* Calculate end of data area, use restart size if data size is too low */
if ( h_data_size > h_restart_size )
{
data_end = h_data_size;
}
else
{
data_end = h_restart_size;
}
/* Calculate end of dictionary table */
offset = h_words_offset;
separator_count = read_data_byte( &offset );
offset += separator_count;
word_size = read_data_byte( &offset );
word_count = read_data_word( &offset );
dictionary_end = offset + ( word_size * word_count );
/* If data end is too low then use end of dictionary instead */
if ( dictionary_end > data_end )
{
data_pages = ( unsigned int ) ( ( dictionary_end + PAGE_MASK ) >> PAGE_SHIFT );
}
else
{
data_pages = ( unsigned int ) ( ( data_end + PAGE_MASK ) >> PAGE_SHIFT );
}
return ( data_pages );
} /* calc_data_pages */
/*
* update_cache
*
* Called on a code or data page cache miss to find the page in the cache or
* read the page in from disk. The chain is kept as a simple LRU chain. If a
* page cannot be found then the page on the end of the chain is reused. If the
* page is found, or reused, then it is moved to the front of the chain.
*
*/
static cache_entry_t *update_cache( int page_number )
{
cache_entry_t *cachep, *lastp;
/* Search the cache chain for the page */
for ( lastp = cache, cachep = cache;
cachep->flink != NULL && cachep->page_number && cachep->page_number != page_number;
lastp = cachep, cachep = cachep->flink )
;
/* If no page in chain then read it from disk */
if ( cachep->page_number != page_number )
{
/* Reusing last cache page, so invalidate cache if page was in use */
if ( cachep->flink == NULL && cachep->page_number )
{
if ( current_code_page == ( unsigned int ) cachep->page_number )
{
current_code_page = 0;
}
if ( current_data_page == ( unsigned int ) cachep->page_number )
{
current_data_page = 0;
}
}
/* Load the new page number and the page contents from disk */
cachep->page_number = page_number;
read_page( page_number, cachep->data );
}
/* If page is not at front of cache chain then move it there */
if ( lastp != cache )
{
lastp->flink = cachep->flink;
cachep->flink = cache;
cache = cachep;
}
return ( cachep );
} /* update_cache */
|