nes.c
  1  /*
  2  ** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
  3  **
  4  **
  5  ** This program is free software; you can redistribute it and/or
  6  ** modify it under the terms of version 2 of the GNU Library General 
  7  ** Public License as published by the Free Software Foundation.
  8  **
  9  ** This program is distributed in the hope that it will be useful, 
 10  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 12  ** Library General Public License for more details.  To obtain a 
 13  ** copy of the GNU Library General Public License, write to the Free 
 14  ** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 15  **
 16  ** Any permitted reproduction of these routines, in whole or in part,
 17  ** must bear this legend.
 18  **
 19  **
 20  ** nes.c
 21  **
 22  ** NES hardware related routines
 23  ** $Id: nes.c,v 1.2 2001/04/27 14:37:11 neil Exp $
 24  */
 25  
 26  #include <stdio.h>
 27  #include <string.h>
 28  #include <stdlib.h>
 29  #include "noftypes.h"
 30  #include "nes6502.h"
 31  #include "log.h"
 32  #include "osd.h"
 33  #include "nes.h"
 34  #include "nes_apu.h"
 35  #include "nes_ppu.h"
 36  #include "nes_rom.h"
 37  #include "nes_mmc.h"
 38  #include "vid_drv.h"
 39  #include "nofrendo.h"
 40  
 41  
 42  #define  NES_CLOCK_DIVIDER    12
 43  //#define  NES_MASTER_CLOCK     21477272.727272727272
 44  #define  NES_MASTER_CLOCK     (236250000 / 11)
 45  #define  NES_SCANLINE_CYCLES  (1364.0 / NES_CLOCK_DIVIDER)
 46  #define  NES_FIQ_PERIOD       (NES_MASTER_CLOCK / NES_CLOCK_DIVIDER / 60)
 47  
 48  #define  NES_RAMSIZE          0x800
 49  
 50  #define  NES_SKIP_LIMIT       (NES_REFRESH_RATE / 5)   /* 12 or 10, depending on PAL/NTSC */
 51  
 52  static nes_t nes;
 53  
 54  /* find out if a file is ours */
 55  int nes_isourfile(const char *filename)
 56  {
 57     return rom_checkmagic(filename);
 58  }
 59  
 60  /* TODO: just asking for problems -- please remove */
 61  nes_t *nes_getcontextptr(void)
 62  {
 63     return &nes;
 64  }
 65  
 66  void nes_getcontext(nes_t *machine)
 67  {
 68     apu_getcontext(nes.apu);
 69     ppu_getcontext(nes.ppu);
 70     nes6502_getcontext(nes.cpu);
 71     mmc_getcontext(nes.mmc);
 72  
 73     *machine = nes;
 74  }
 75  
 76  void nes_setcontext(nes_t *machine)
 77  {
 78     ASSERT(machine);
 79  
 80     apu_setcontext(machine->apu);
 81     ppu_setcontext(machine->ppu);
 82     nes6502_setcontext(machine->cpu);
 83     mmc_setcontext(machine->mmc);
 84  
 85     nes = *machine;
 86  }
 87  
 88  static uint8 ram_read(uint32 address)
 89  {
 90     return nes.cpu->mem_page[0][address & (NES_RAMSIZE - 1)];
 91  }
 92  
 93  static void ram_write(uint32 address, uint8 value)
 94  {
 95     nes.cpu->mem_page[0][address & (NES_RAMSIZE - 1)] = value;
 96  }
 97  
 98  static void write_protect(uint32 address, uint8 value)
 99  {
100     /* don't allow write to go through */
101     UNUSED(address);
102     UNUSED(value);
103  }
104  
105  static uint8 read_protect(uint32 address)
106  {
107     /* don't allow read to go through */
108     UNUSED(address);
109  
110     return 0xFF;
111  }
112  
113  #define  LAST_MEMORY_HANDLER  { -1, -1, NULL }
114  /* read/write handlers for standard NES */
115  static const nes6502_memread default_readhandler[] =
116  {
117     { 0x0800, 0x1FFF, ram_read },
118     { 0x2000, 0x3FFF, ppu_read },
119     { 0x4000, 0x4015, apu_read },
120     { 0x4016, 0x4017, ppu_readhigh },
121     LAST_MEMORY_HANDLER
122  };
123  
124  static const nes6502_memwrite default_writehandler[] =
125  {
126     { 0x0800, 0x1FFF, ram_write },
127     { 0x2000, 0x3FFF, ppu_write },
128     { 0x4000, 0x4013, apu_write },
129     { 0x4015, 0x4015, apu_write },
130     { 0x4014, 0x4017, ppu_writehigh },
131     LAST_MEMORY_HANDLER
132  };
133  
134  /* this big nasty boy sets up the address handlers that the CPU uses */
135  static void build_address_handlers(nes_t *machine)
136  {
137     int count, num_handlers = 0;
138     mapintf_t *intf;
139     
140     ASSERT(machine);
141     intf = machine->mmc->intf;
142  
143     memset(machine->readhandler, 0, sizeof(machine->readhandler));
144     memset(machine->writehandler, 0, sizeof(machine->writehandler));
145  
146     for (count = 0; num_handlers < MAX_MEM_HANDLERS; count++, num_handlers++)
147     {
148        if (NULL == default_readhandler[count].read_func)
149           break;
150  
151        memcpy(&machine->readhandler[num_handlers], &default_readhandler[count],
152               sizeof(nes6502_memread));
153     }
154  
155     if (intf->sound_ext)
156     {
157        if (NULL != intf->sound_ext->mem_read)
158        {
159           for (count = 0; num_handlers < MAX_MEM_HANDLERS; count++, num_handlers++)
160           {
161              if (NULL == intf->sound_ext->mem_read[count].read_func)
162                 break;
163  
164              memcpy(&machine->readhandler[num_handlers], &intf->sound_ext->mem_read[count],
165                     sizeof(nes6502_memread));
166           }
167        }
168     }
169  
170     if (NULL != intf->mem_read)
171     {
172        for (count = 0; num_handlers < MAX_MEM_HANDLERS; count++, num_handlers++)
173        {
174           if (NULL == intf->mem_read[count].read_func)
175              break;
176  
177           memcpy(&machine->readhandler[num_handlers], &intf->mem_read[count],
178                  sizeof(nes6502_memread));
179        }
180     }
181  
182     /* TODO: poof! numbers */
183     machine->readhandler[num_handlers].min_range = 0x4018;
184     machine->readhandler[num_handlers].max_range = 0x5FFF;
185     machine->readhandler[num_handlers].read_func = read_protect;
186     num_handlers++;
187     machine->readhandler[num_handlers].min_range = -1;
188     machine->readhandler[num_handlers].max_range = -1;
189     machine->readhandler[num_handlers].read_func = NULL;
190     num_handlers++;
191     ASSERT(num_handlers <= MAX_MEM_HANDLERS);
192  
193     num_handlers = 0;
194  
195     for (count = 0; num_handlers < MAX_MEM_HANDLERS; count++, num_handlers++)
196     {
197        if (NULL == default_writehandler[count].write_func)
198           break;
199  
200        memcpy(&machine->writehandler[num_handlers], &default_writehandler[count],
201               sizeof(nes6502_memwrite));
202     }
203  
204     if (intf->sound_ext)
205     {
206        if (NULL != intf->sound_ext->mem_write)
207        {
208           for (count = 0; num_handlers < MAX_MEM_HANDLERS; count++, num_handlers++)
209           {
210              if (NULL == intf->sound_ext->mem_write[count].write_func)
211                 break;
212  
213              memcpy(&machine->writehandler[num_handlers], &intf->sound_ext->mem_write[count],
214                     sizeof(nes6502_memwrite));
215           }
216        }
217     }
218  
219     if (NULL != intf->mem_write)
220     {
221        for (count = 0; num_handlers < MAX_MEM_HANDLERS; count++, num_handlers++)
222        {
223           if (NULL == intf->mem_write[count].write_func)
224              break;
225  
226           memcpy(&machine->writehandler[num_handlers], &intf->mem_write[count],
227                  sizeof(nes6502_memwrite));
228        }
229     }
230  
231     /* catch-all for bad writes */
232     /* TODO: poof! numbers */
233     machine->writehandler[num_handlers].min_range = 0x4018;
234     machine->writehandler[num_handlers].max_range = 0x5FFF;
235     machine->writehandler[num_handlers].write_func = write_protect;
236     num_handlers++;
237     machine->writehandler[num_handlers].min_range = 0x8000;
238     machine->writehandler[num_handlers].max_range = 0xFFFF;
239     machine->writehandler[num_handlers].write_func = write_protect;
240     num_handlers++;
241     machine->writehandler[num_handlers].min_range = -1;
242     machine->writehandler[num_handlers].max_range = -1;
243     machine->writehandler[num_handlers].write_func = NULL;
244     num_handlers++;
245     ASSERT(num_handlers <= MAX_MEM_HANDLERS);
246  }
247  
248  /* raise an IRQ */
249  void nes_irq(void)
250  {
251     nes6502_irq();
252  }
253  
254  static uint8 nes_clearfiq(void)
255  {
256     if (nes.fiq_occurred)
257     {
258        nes.fiq_occurred = false;
259        return 0x40;
260     }
261  
262     return 0;
263  }
264  
265  void nes_setfiq(uint8 value)
266  {
267     nes.fiq_state = value;
268     nes.fiq_cycles = (int) NES_FIQ_PERIOD;
269  }
270  
271  static void nes_checkfiq(int cycles)
272  {
273     nes.fiq_cycles -= cycles;
274     if (nes.fiq_cycles <= 0)
275     {
276        nes.fiq_cycles += (int) NES_FIQ_PERIOD;
277        if (0 == (nes.fiq_state & 0xC0))
278        {
279           nes.fiq_occurred = true;
280           nes6502_irq();
281        }
282     }
283  }
284  
285  void nes_nmi(void)
286  {
287     nes6502_nmi();
288  }
289  
290  static void nes_renderframe(bool draw_flag)
291  {
292     int elapsed_cycles;
293     mapintf_t *mapintf = nes.mmc->intf;
294     int in_vblank = 0;
295  
296     while (262 != nes.scanline)
297     {
298  //      ppu_scanline(nes.vidbuf, nes.scanline, draw_flag);
299  		ppu_scanline(vid_getbuffer(), nes.scanline, draw_flag);
300  
301        if (241 == nes.scanline)
302        {
303           /* 7-9 cycle delay between when VINT flag goes up and NMI is taken */
304           elapsed_cycles = nes6502_execute(7);
305           nes.scanline_cycles -= elapsed_cycles;
306           nes_checkfiq(elapsed_cycles);
307  
308           ppu_checknmi();
309  
310           if (mapintf->vblank)
311              mapintf->vblank();
312           in_vblank = 1;
313        } 
314  
315        if (mapintf->hblank)
316           mapintf->hblank(in_vblank);
317  
318        nes.scanline_cycles += (float) NES_SCANLINE_CYCLES;
319        elapsed_cycles = nes6502_execute((int) nes.scanline_cycles);
320        nes.scanline_cycles -= (float) elapsed_cycles;
321        nes_checkfiq(elapsed_cycles);
322  
323        ppu_endscanline(nes.scanline);
324        nes.scanline++;
325     }
326  
327     nes.scanline = 0;
328  }
329  
330  static void system_video(bool draw)
331  {
332  
333  #ifdef NOLOOP
334  #else   
335  #endif
336     /* blit to screen */
337     vid_flush();
338  
339     /* grab input */
340     osd_getinput();
341  }
342  
343  /* main emulation loop */
344  static int last_ticks, frames_to_render;
345  void nes_emulate(void)
346  {
347     osd_setsound(nes.apu->process);
348  
349     last_ticks = nofrendo_ticks;
350     frames_to_render = 0;
351     nes.scanline_cycles = 0;
352     nes.fiq_cycles = (int) NES_FIQ_PERIOD;
353  #ifdef NOLOOP
354  #else   
355     while (false == nes.poweroff)
356     {
357        if (nofrendo_ticks != last_ticks)
358        {
359           int tick_diff = nofrendo_ticks - last_ticks;
360  
361           frames_to_render += tick_diff;
362           last_ticks = nofrendo_ticks;
363        }
364  
365        if (true == nes.pause)
366        {
367           /* TODO: dim the screen, and pause/silence the apu */
368           system_video(true);
369           frames_to_render = 0;
370        }
371        else if (frames_to_render > 1)
372        {
373           frames_to_render--;
374           nes_renderframe(false);
375           system_video(false);
376        }
377        else if ((1 == frames_to_render && true == nes.autoframeskip)
378                 || false == nes.autoframeskip)
379        {
380           frames_to_render = 0;
381           nes_renderframe(true);
382           system_video(true);
383        }
384     }
385  #endif   
386  }
387  
388  //ADD ON
389  #ifdef NOLOOP
390  void nes_step(int skip)
391  {
392     if (skip) {
393        nes_renderframe(false);
394        system_video(false);
395     }
396     else {
397        nes_renderframe(true);
398        system_video(true);
399     }    
400  
401  }
402  #endif
403  
404  static void mem_trash(uint8 *buffer, int length)
405  {
406     int i;
407  
408     for (i = 0; i < length; i++)
409        buffer[i] = (uint8) rand();
410  }
411  
412  /* Reset NES hardware */
413  void nes_reset(int reset_type)
414  {
415     if (HARD_RESET == reset_type)
416     {
417        memset(nes.cpu->mem_page[0], 0, NES_RAMSIZE);
418        if (nes.rominfo->vram)
419           mem_trash(nes.rominfo->vram, 0x2000 * nes.rominfo->vram_banks);
420     }
421  
422     apu_reset();
423     ppu_reset(reset_type);
424     mmc_reset();
425     nes6502_reset();
426  
427     nes.scanline = 241;
428  }
429  
430  void nes_destroy(nes_t **machine)
431  {
432     if (*machine)
433     {
434        rom_free(&(*machine)->rominfo);
435        mmc_destroy(&(*machine)->mmc);
436        ppu_destroy(&(*machine)->ppu);
437        apu_destroy(&(*machine)->apu);
438  //      bmp_destroy(&(*machine)->vidbuf);
439        if ((*machine)->cpu)
440        {
441           if ((*machine)->cpu->mem_page[0])
442              free((*machine)->cpu->mem_page[0]);
443           free((*machine)->cpu);
444        }
445  
446        free(*machine);
447        *machine = NULL;
448     }
449  }
450  
451  void nes_poweroff(void)
452  {
453     nes.poweroff = true;
454  }
455  
456  void nes_togglepause(void)
457  {
458     nes.pause ^= true;
459  }
460  
461  /* insert a cart into the NES */
462  int nes_insertcart(const char *filename, nes_t *machine)
463  {
464     nes6502_setcontext(machine->cpu);
465     /* rom file */
466     machine->rominfo = rom_load(filename);
467     if (NULL == machine->rominfo)
468        goto _fail;
469     /* map cart's SRAM to CPU $6000-$7FFF */
470     if (machine->rominfo->sram)
471     {
472        machine->cpu->mem_page[6] = machine->rominfo->sram;
473        machine->cpu->mem_page[7] = machine->rominfo->sram + 0x1000;
474     } 
475     /* mapper */
476     machine->mmc = mmc_create(machine->rominfo);
477     if (NULL == machine->mmc)
478        goto _fail;
479  
480     /* if there's VRAM, let the PPU know */
481     if (NULL != machine->rominfo->vram)
482        machine->ppu->vram_present = true; 
483     apu_setext(machine->apu, machine->mmc->intf->sound_ext);
484     build_address_handlers(machine);
485     nes_setcontext(machine);
486     nes_reset(HARD_RESET); 
487     
488     return 0;
489  
490  _fail:
491     nes_destroy(&machine);
492     return -1;
493  }
494  
495  
496  /* Initialize NES CPU, hardware, etc. */
497  nes_t *nes_create(void)
498  {
499     nes_t *machine;
500     sndinfo_t osd_sound;
501     int i;
502  
503     machine = malloc(sizeof(nes_t));
504     if (NULL == machine)
505        return NULL;
506  
507     memset(machine, 0, sizeof(nes_t));
508  
509     /* bitmap */
510     /* 8 pixel overdraw */
511  //   machine->vidbuf = bmp_create(NES_SCREEN_WIDTH, NES_SCREEN_HEIGHT, 8);
512  //   if (NULL == machine->vidbuf)
513  //      goto _fail;
514  
515     machine->autoframeskip = true;
516  
517     /* cpu */
518     machine->cpu = malloc(sizeof(nes6502_context));
519     if (NULL == machine->cpu)
520        goto _fail;
521  
522     memset(machine->cpu, 0, sizeof(nes6502_context));
523     
524     /* allocate 2kB RAM */
525     machine->cpu->mem_page[0] = malloc(NES_RAMSIZE);
526     if (NULL == machine->cpu->mem_page[0])
527        goto _fail;
528  
529     /* point all pages at NULL for now */
530     for (i = 1; i < NES6502_NUMBANKS; i++)
531        machine->cpu->mem_page[i] = NULL;
532  
533     machine->cpu->read_handler = machine->readhandler;
534     machine->cpu->write_handler = machine->writehandler;
535  
536     /* apu */
537     osd_getsoundinfo(&osd_sound);
538     machine->apu = apu_create(0, osd_sound.sample_rate, NES_REFRESH_RATE, osd_sound.bps);
539  
540     if (NULL == machine->apu)
541        goto _fail;
542  
543     /* set the IRQ routines */
544     machine->apu->irq_callback = nes_irq;
545     machine->apu->irqclear_callback = nes_clearfiq;
546  
547     /* ppu */
548     machine->ppu = ppu_create();
549     if (NULL == machine->ppu)
550        goto _fail;
551  
552     machine->poweroff = false;
553     machine->pause = false;
554  
555     return machine;
556  
557  _fail:
558     nes_destroy(&machine);
559     return NULL;
560  }
561  
562  /*
563  ** $Log: nes.c,v $
564  ** Revision 1.2  2001/04/27 14:37:11  neil
565  ** wheeee
566  **
567  ** Revision 1.1.1.1  2001/04/27 07:03:54  neil
568  ** initial
569  **
570  ** Revision 1.18  2000/11/29 12:58:23  matt
571  ** timing/fiq fixes
572  **
573  ** Revision 1.17  2000/11/27 19:36:15  matt
574  ** more timing fixes
575  **
576  ** Revision 1.16  2000/11/26 16:13:13  matt
577  ** slight fix (?) to nes_fiq
578  **
579  ** Revision 1.15  2000/11/26 15:51:13  matt
580  ** frame IRQ emulation
581  **
582  ** Revision 1.14  2000/11/25 20:30:39  matt
583  ** scanline emulation simplifications/timing fixes
584  **
585  ** Revision 1.13  2000/11/25 01:53:42  matt
586  ** bool stinks sometimes
587  **
588  ** Revision 1.12  2000/11/21 13:28:40  matt
589  ** take care to zero allocated mem
590  **
591  ** Revision 1.11  2000/11/20 13:23:32  matt
592  ** nofrendo.c now handles timer
593  **
594  ** Revision 1.10  2000/11/09 14:07:27  matt
595  ** state load fixed, state save mostly fixed
596  **
597  ** Revision 1.9  2000/11/05 22:19:37  matt
598  ** pause buglet fixed
599  **
600  ** Revision 1.8  2000/11/05 06:27:09  matt
601  ** thinlib spawns changes
602  **
603  ** Revision 1.7  2000/10/29 14:36:45  matt
604  ** nes_clearframeirq is static
605  **
606  ** Revision 1.6  2000/10/28 15:20:41  matt
607  ** irq callbacks in nes_apu
608  **
609  ** Revision 1.5  2000/10/27 12:55:58  matt
610  ** nes6502 now uses 4kB banks across the boards
611  **
612  ** Revision 1.4  2000/10/25 13:44:02  matt
613  ** no more silly define names
614  **
615  ** Revision 1.3  2000/10/25 01:23:08  matt
616  ** basic system autodetection
617  **
618  ** Revision 1.2  2000/10/25 00:23:16  matt
619  ** makefiles updated for new directory structure
620  **
621  ** Revision 1.1  2000/10/24 12:20:28  matt
622  ** changed directory structure
623  **
624  ** Revision 1.50  2000/10/23 17:51:09  matt
625  ** adding fds support
626  **
627  ** Revision 1.49  2000/10/23 15:53:08  matt
628  ** better system handling
629  **
630  ** Revision 1.48  2000/10/22 20:02:29  matt
631  ** autoframeskip bugfix
632  **
633  ** Revision 1.47  2000/10/22 19:16:15  matt
634  ** more sane timer ISR / autoframeskip
635  **
636  ** Revision 1.46  2000/10/21 19:26:59  matt
637  ** many more cleanups
638  **
639  ** Revision 1.45  2000/10/17 12:00:56  matt
640  ** selectable apu base frequency
641  **
642  ** Revision 1.44  2000/10/10 13:58:14  matt
643  ** stroustrup squeezing his way in the door
644  **
645  ** Revision 1.43  2000/10/10 13:05:30  matt
646  ** Mr. Clean makes a guest appearance
647  **
648  ** Revision 1.42  2000/10/08 17:53:37  matt
649  ** minor accuracy changes
650  **
651  ** Revision 1.41  2000/09/18 02:09:12  matt
652  ** -pedantic is your friend
653  **
654  ** Revision 1.40  2000/09/15 13:38:39  matt
655  ** changes for optimized apu core
656  **
657  ** Revision 1.39  2000/09/15 04:58:07  matt
658  ** simplifying and optimizing APU core
659  **
660  ** Revision 1.38  2000/09/08 11:57:29  matt
661  ** no more nes_fiq
662  **
663  ** Revision 1.37  2000/08/31 02:39:01  matt
664  ** moved dos stuff in here (temp)
665  **
666  ** Revision 1.36  2000/08/16 02:51:55  matt
667  ** random cleanups
668  **
669  ** Revision 1.35  2000/08/11 02:43:50  matt
670  ** moved frame irq stuff out of APU into here
671  **
672  ** Revision 1.34  2000/08/11 01:42:43  matt
673  ** change to OSD sound info interface
674  **
675  ** Revision 1.33  2000/07/31 04:27:59  matt
676  ** one million cleanups
677  **
678  ** Revision 1.32  2000/07/30 04:32:32  matt
679  ** emulation of the NES frame IRQ
680  **
681  ** Revision 1.31  2000/07/27 04:07:14  matt
682  ** cleaned up the neighborhood lawns
683  **
684  ** Revision 1.30  2000/07/27 03:59:52  neil
685  ** pausing tweaks, during fullscreen toggles
686  **
687  ** Revision 1.29  2000/07/27 03:19:22  matt
688  ** just a little cleaner, that's all
689  **
690  ** Revision 1.28  2000/07/27 02:55:23  matt
691  ** nes_emulate went through detox
692  **
693  ** Revision 1.27  2000/07/27 02:49:18  matt
694  ** cleaner flow in nes_emulate
695  **
696  ** Revision 1.26  2000/07/27 01:17:09  matt
697  ** nes_insertrom -> nes_insertcart
698  **
699  ** Revision 1.25  2000/07/26 21:36:14  neil
700  ** Big honkin' change -- see the mailing list
701  **
702  ** Revision 1.24  2000/07/25 02:25:53  matt
703  ** safer xxx_destroy calls
704  **
705  ** Revision 1.23  2000/07/24 04:32:40  matt
706  ** autoframeskip bugfix
707  **
708  ** Revision 1.22  2000/07/23 15:13:48  matt
709  ** apu API change, autoframeskip part of nes_t struct
710  **
711  ** Revision 1.21  2000/07/21 02:44:41  matt
712  ** merged osd_getinput and osd_gethostinput
713  **
714  ** Revision 1.20  2000/07/17 05:12:55  matt
715  ** nes_ppu.c is no longer a scary place to be-- cleaner & faster
716  **
717  ** Revision 1.19  2000/07/17 01:52:28  matt
718  ** made sure last line of all source files is a newline
719  **
720  ** Revision 1.18  2000/07/15 23:51:23  matt
721  ** hack for certain filthy NES titles
722  **
723  ** Revision 1.17  2000/07/11 04:31:54  matt
724  ** less magic number nastiness for screen dimensions
725  **
726  ** Revision 1.16  2000/07/11 02:38:25  matt
727  ** encapsulated memory address handlers into nes/nsf
728  **
729  ** Revision 1.15  2000/07/10 13:50:49  matt
730  ** added function nes_irq()
731  **
732  ** Revision 1.14  2000/07/10 05:27:55  matt
733  ** cleaned up mapper-specific callbacks
734  **
735  ** Revision 1.13  2000/07/09 03:43:26  matt
736  ** minor changes to gui handling
737  **
738  ** Revision 1.12  2000/07/06 16:42:23  matt
739  ** updated for new video driver
740  **
741  ** Revision 1.11  2000/07/05 19:57:36  neil
742  ** __GNUC -> __DJGPP in nes.c
743  **
744  ** Revision 1.10  2000/07/05 12:23:03  matt
745  ** removed unnecessary references
746  **
747  ** Revision 1.9  2000/07/04 23:12:34  matt
748  ** memory protection handlers
749  **
750  ** Revision 1.8  2000/07/04 04:58:29  matt
751  ** dynamic memory range handlers
752  **
753  ** Revision 1.7  2000/06/26 04:58:51  matt
754  ** minor bugfix
755  **
756  ** Revision 1.6  2000/06/20 20:42:12  matt
757  ** fixed some NULL pointer problems
758  **
759  ** Revision 1.5  2000/06/09 15:12:26  matt
760  ** initial revision
761  **
762  */