map004.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  ** map4.c
 21  **
 22  ** mapper 4 interface
 23  ** $Id: map004.c,v 1.2 2001/04/27 14:37:11 neil Exp $
 24  */
 25  
 26  #include "noftypes.h"
 27  #include "nes_mmc.h"
 28  #include "nes.h"
 29  #include "libsnss.h"
 30  
 31  static struct
 32  {
 33     int counter, latch;
 34     bool enabled, reset;
 35  } irq;
 36  
 37  static uint8 reg;
 38  static uint8 command;
 39  static uint16 vrombase;
 40  
 41  /* mapper 4: MMC3 */
 42  static void map4_write(uint32 address, uint8 value)
 43  {
 44     switch (address & 0xE001)
 45     {
 46     case 0x8000:
 47        command = value;
 48        vrombase = (command & 0x80) ? 0x1000 : 0x0000;
 49        
 50        if (reg != (value & 0x40))
 51        {
 52           if (value & 0x40)
 53              mmc_bankrom(8, 0x8000, (mmc_getinfo()->rom_banks * 2) - 2);
 54           else
 55              mmc_bankrom(8, 0xC000, (mmc_getinfo()->rom_banks * 2) - 2);
 56        }
 57        reg = value & 0x40;
 58        break;
 59  
 60     case 0x8001:
 61        switch (command & 0x07)
 62        {
 63        case 0:
 64           value &= 0xFE;
 65           mmc_bankvrom(1, vrombase ^ 0x0000, value);
 66           mmc_bankvrom(1, vrombase ^ 0x0400, value + 1);
 67           break;
 68  
 69        case 1:
 70           value &= 0xFE;
 71           mmc_bankvrom(1, vrombase ^ 0x0800, value);
 72           mmc_bankvrom(1, vrombase ^ 0x0C00, value + 1);
 73           break;
 74  
 75        case 2:
 76           mmc_bankvrom(1, vrombase ^ 0x1000, value);
 77           break;
 78  
 79        case 3:
 80           mmc_bankvrom(1, vrombase ^ 0x1400, value);
 81           break;
 82  
 83        case 4:
 84           mmc_bankvrom(1, vrombase ^ 0x1800, value);
 85           break;
 86  
 87        case 5:
 88           mmc_bankvrom(1, vrombase ^ 0x1C00, value);
 89           break;
 90  
 91        case 6:
 92           mmc_bankrom(8, (command & 0x40) ? 0xC000 : 0x8000, value);
 93           break;
 94  
 95        case 7:
 96           mmc_bankrom(8, 0xA000, value);
 97           break;
 98        }
 99        break;
100  
101     case 0xA000:
102        /* four screen mirroring crap */
103        if (0 == (mmc_getinfo()->flags & ROM_FLAG_FOURSCREEN))
104        {
105           if (value & 1)
106              ppu_mirror(0, 0, 1, 1); /* horizontal */
107           else
108              ppu_mirror(0, 1, 0, 1); /* vertical */
109        }
110        break;
111  
112     case 0xA001:
113        /* Save RAM enable / disable */
114        /* Messes up Startropics I/II if implemented -- bah */
115        break;
116  
117     case 0xC000:
118        irq.latch = value;
119  //      if (irq.reset)
120  //         irq.counter = irq.latch;
121        break;
122  
123     case 0xC001:
124        irq.reset = true;
125        irq.counter = irq.latch;
126        break;
127  
128     case 0xE000:
129        irq.enabled = false;
130  //      if (irq.reset)
131  //         irq.counter = irq.latch;
132        break;
133  
134     case 0xE001:
135        irq.enabled = true;
136  //      if (irq.reset)
137  //         irq.counter = irq.latch;
138        break;
139  
140     default:
141        break;
142     }
143  
144     if (true == irq.reset)
145        irq.counter = irq.latch;
146  }
147  
148  static void map4_hblank(int vblank)
149  {
150     if (vblank)
151        return;
152  
153     if (ppu_enabled())
154     {
155        if (irq.counter >= 0)
156        {
157           irq.reset = false;
158           irq.counter--;
159  
160           if (irq.counter < 0)
161           {
162              if (irq.enabled)
163              {
164                 irq.reset = true;
165                 nes_irq();
166              }
167           }
168        }
169     }
170  }
171  
172  static void map4_getstate(SnssMapperBlock *state)
173  {
174     state->extraData.mapper4.irqCounter = irq.counter;
175     state->extraData.mapper4.irqLatchCounter = irq.latch;
176     state->extraData.mapper4.irqCounterEnabled = irq.enabled;
177     state->extraData.mapper4.last8000Write = command;
178  }
179  
180  static void map4_setstate(SnssMapperBlock *state)
181  {
182     irq.counter = state->extraData.mapper4.irqCounter;
183     irq.latch = state->extraData.mapper4.irqLatchCounter;
184     irq.enabled = state->extraData.mapper4.irqCounterEnabled;
185     command = state->extraData.mapper4.last8000Write;
186  }
187  
188  static void map4_init(void)
189  {
190     irq.counter = irq.latch = 0;
191     irq.enabled = irq.reset = false;
192     reg = command = 0;
193     vrombase = 0x0000;
194  }
195  
196  static const map_memwrite map4_memwrite[] =
197  {
198     { 0x8000, 0xFFFF, map4_write },
199     {     -1,     -1, NULL }
200  };
201  
202  const mapintf_t map4_intf =
203  {
204     4, /* mapper number */
205     "MMC3", /* mapper name */
206     map4_init, /* init routine */
207     NULL, /* vblank callback */
208     map4_hblank, /* hblank callback */
209     map4_getstate, /* get state (snss) */
210     map4_setstate, /* set state (snss) */
211     NULL, /* memory read structure */
212     map4_memwrite, /* memory write structure */
213     NULL /* external sound device */
214  };
215  
216  /*
217  ** $Log: map004.c,v $
218  ** Revision 1.2  2001/04/27 14:37:11  neil
219  ** wheeee
220  **
221  ** Revision 1.1  2001/04/27 12:54:40  neil
222  ** blah
223  **
224  ** Revision 1.1.1.1  2001/04/27 07:03:54  neil
225  ** initial
226  **
227  ** Revision 1.2  2000/11/26 15:40:49  matt
228  ** hey, it actually works now
229  **
230  ** Revision 1.1  2000/10/24 12:19:32  matt
231  ** changed directory structure
232  **
233  ** Revision 1.12  2000/10/23 15:53:27  matt
234  ** suppressed warnings
235  **
236  ** Revision 1.11  2000/10/22 19:17:46  matt
237  ** mapper cleanups galore
238  **
239  ** Revision 1.10  2000/10/22 15:03:13  matt
240  ** simplified mirroring
241  **
242  ** Revision 1.9  2000/10/21 19:33:38  matt
243  ** many more cleanups
244  **
245  ** Revision 1.8  2000/10/10 13:58:17  matt
246  ** stroustrup squeezing his way in the door
247  **
248  ** Revision 1.7  2000/10/08 18:05:44  matt
249  ** kept old version around, just in case....
250  **
251  ** Revision 1.6  2000/07/15 23:52:19  matt
252  ** rounded out a bunch more mapper interfaces
253  **
254  ** Revision 1.5  2000/07/10 13:51:25  matt
255  ** using generic nes_irq() routine now
256  **
257  ** Revision 1.4  2000/07/10 05:29:03  matt
258  ** cleaned up some mirroring issues
259  **
260  ** Revision 1.3  2000/07/06 02:48:43  matt
261  ** clearly labelled structure members
262  **
263  ** Revision 1.2  2000/07/05 05:04:39  matt
264  ** minor modifications
265  **
266  ** Revision 1.1  2000/07/04 23:11:45  matt
267  ** initial revision
268  **
269  */