~jazzva/fakenes/ubuntu

« back to all changes in this revision

Viewing changes to src/include/mmc/vrc6.h

  • Committer: Sasa Bodiroza
  • Date: 2007-08-15 05:37:49 UTC
  • Revision ID: jazzva@gmail.com-20070815053749-76l0xj66tzgt290p
Upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Mapper #24 and #26 (Konami VRC6 and VRC6V). */
 
2
/* These mappers are fully supported. */
 
3
/* IRQ prediction is supported. */
 
4
 
 
5
#include "mmc/shared.h"
 
6
 
 
7
static int vrc6_init (void);
 
8
static int vrc6v_init (void);
 
9
static void vrc6_reset (void);
 
10
static void vrc6_save_state (PACKFILE *, int);
 
11
static void vrc6_load_state (PACKFILE *, int);
 
12
 
 
13
static const MMC mmc_vrc6 =
 
14
{
 
15
   24,
 
16
   "Konami VRC6 + ExSound",
 
17
   vrc6_init,
 
18
   vrc6_reset,
 
19
   "VRC6\0\0\0\0",
 
20
   vrc6_save_state,
 
21
   vrc6_load_state
 
22
};
 
23
 
 
24
static const MMC mmc_vrc6v =
 
25
{
 
26
   26,
 
27
   "Konami VRC6V + ExSound",
 
28
   vrc6v_init,
 
29
   vrc6_reset,
 
30
   "VRC6V\0\0\0",
 
31
   vrc6_save_state,
 
32
   vrc6_load_state
 
33
};
 
34
 
 
35
static const ENUM vrc6_mirroring_table[] =
 
36
{
 
37
   MIRRORING_VERTICAL,
 
38
   MIRRORING_HORIZONTAL,
 
39
   MIRRORING_ONE_SCREEN_2000,
 
40
   MIRRORING_ONE_SCREEN_2400
 
41
};
 
42
 
 
43
static const UINT8 vrc6_mirroring_mask = 0x0c;
 
44
 
 
45
static BOOL vrc6_swap_address_pins = FALSE;
 
46
 
 
47
static UINT8 vrc6_prg_bank[2];
 
48
static UINT8 vrc6_chr_bank[8];
 
49
 
 
50
/* Internal ~1.79MHz clock (scaled to ~5.37MHz for efficiency). */
 
51
static cpu_time_t vrc6_clock_counter = 0;
 
52
 
 
53
/* How often the IRQ counter gets clocked in CYCLE MODE. */
 
54
#define VRC6_IRQ_CYCLE_LENGTH    CYCLE_LENGTH                  /* 1*3 */
 
55
/* How often the IRQ counter gets clocked in SCANLINE MODE. */
 
56
#define VRC6_IRQ_SCANLINE_LENGTH SCANLINE_LENGTH               /* 341 */
 
57
 
 
58
#define VRC6_IRQ_FREQUENCY \
 
59
   ((vrc6_irq_reg & 0x04) ? VRC6_IRQ_CYCLE_LENGTH : VRC6_IRQ_SCANLINE_LENGTH)
 
60
 
 
61
static UINT16 vrc6_irq_timer   = 0;
 
62
static UINT8  vrc6_irq_counter = 0x00;
 
63
static UINT8  vrc6_irq_latch   = 0x00;
 
64
static UINT8  vrc6_irq_reg     = 0x00;
 
65
static BOOL   vrc6_enable_irqs = FALSE;
 
66
 
 
67
/* IRQ prediction. */
 
68
static cpu_time_t vrc6_prediction_timestamp = 0;
 
69
static cpu_time_t vrc6_prediction_cycles = 0;
 
70
 
 
71
static void vrc6_update_irq_counter (cpu_time_t cycles, BOOL allow_irq)
 
72
{
 
73
   /* Slave function used by both emulation and prediction(simulation). */
 
74
 
 
75
   cpu_time_t count, offset;
 
76
 
 
77
   if (!vrc6_enable_irqs)
 
78
      return;
 
79
 
 
80
   /* Speed hack.  Shouldn't hurt accuracy. =) */
 
81
   cycles /= CYCLE_LENGTH;
 
82
 
 
83
   /* Cache it for efficiency. */
 
84
   count = VRC6_IRQ_FREQUENCY;
 
85
 
 
86
   for (offset = 0; offset < cycles; offset++)
 
87
   {
 
88
      vrc6_irq_timer += CYCLE_LENGTH;
 
89
      if (vrc6_irq_timer >= count)
 
90
      {
 
91
         vrc6_irq_timer -= count;
 
92
   
 
93
         if (vrc6_irq_counter == 0xFF)
 
94
         {
 
95
            vrc6_irq_counter = vrc6_irq_latch;
 
96
 
 
97
            if (allow_irq)
 
98
               cpu_queue_interrupt (CPU_INTERRUPT_IRQ_MMC, (vrc6_prediction_timestamp + offset));
 
99
         }
 
100
         else
 
101
            vrc6_irq_counter++;
 
102
      }
 
103
   }
 
104
}
 
105
 
 
106
static void vrc6_predict_irq (cpu_time_t cycles)
 
107
{
 
108
   UINT16 saved_irq_timer;
 
109
   UINT8 saved_irq_counter;
 
110
 
 
111
   if (!vrc6_enable_irqs)
 
112
      return;
 
113
 
 
114
   /* Save vars since we're just simulating. */
 
115
   saved_irq_timer = vrc6_irq_timer;
 
116
   saved_irq_counter = vrc6_irq_counter;
 
117
 
 
118
   /* Remember to allow IRQs here. */
 
119
   /* Just go through the motions... */
 
120
   vrc6_update_irq_counter (cycles, TRUE);
 
121
 
 
122
   /* Restore saved vars. */
 
123
   vrc6_irq_timer = saved_irq_timer;
 
124
   vrc6_irq_counter = saved_irq_counter;
 
125
}
 
126
 
 
127
static void vrc6_repredict_irq (void)
 
128
{
 
129
   /* This needs to be called whenver a possible mid-scanline change to the
 
130
      IRQ parameters occurs (to update prediction). */
 
131
 
 
132
   cpu_time_t cycles, cycles_remaining;
 
133
 
 
134
   cycles = cpu_get_cycles ();
 
135
 
 
136
   cycles_remaining = (cycles - vrc6_prediction_timestamp);
 
137
   if (cycles_remaining <= 0)
 
138
      return;
 
139
 
 
140
   if (cycles_remaining > vrc6_prediction_cycles)
 
141
      cycles_remaining = vrc6_prediction_cycles;
 
142
 
 
143
   vrc6_predict_irq (cycles_remaining);
 
144
}
 
145
 
 
146
static void vrc6_process (void)
 
147
{
 
148
   /* Call this before accessing the state of the mapper - before reads,
 
149
      writes, and state-sensetive emulation. */
 
150
 
 
151
   const cpu_time_t elapsed_cycles = cpu_get_elapsed_cycles (&vrc6_clock_counter);
 
152
   if (elapsed_cycles == 0)
 
153
      return;
 
154
 
 
155
   /* *Don't* allow IRQs here, or it'll conflict with prediction. */
 
156
   vrc6_update_irq_counter (elapsed_cycles, FALSE);
 
157
}
 
158
 
 
159
static void vrc6_predict_irqs (cpu_time_t cycles)
 
160
{
 
161
   /* Wrapper for vrc6_predict_irq() that is exposed to the MMC interfce,
 
162
      thus must take care of a few specific things. */
 
163
 
 
164
   /* Save parameters for re-prediction if a mid-scanline change occurs. */
 
165
   vrc6_prediction_timestamp = cpu_get_cycles ();
 
166
   vrc6_prediction_cycles = cycles;
 
167
 
 
168
   /* Sync state. */
 
169
   vrc6_process ();
 
170
 
 
171
   vrc6_predict_irq (cycles);
 
172
}
 
173
 
 
174
static void vrc6_update_prg_bank (int bank)
 
175
{
 
176
   switch (bank)
 
177
   {
 
178
      case 0:  /* 16k ROM page select. */
 
179
      {
 
180
         cpu_set_read_address_16k_rom_block (0x8000, vrc6_prg_bank[0]);
 
181
         break;
 
182
      }
 
183
 
 
184
      case 1:  /* 8k ROM page select. */
 
185
      {
 
186
         cpu_set_read_address_8k_rom_block (0xC000, vrc6_prg_bank[1]);
 
187
         break;
 
188
      }
 
189
 
 
190
      default:
 
191
         WARN_GENERIC();
 
192
   }
 
193
}
 
194
 
 
195
static void vrc6_update_chr_bank (int bank)
 
196
{
 
197
   if (ROM_CHR_ROM_PAGES <= 0)
 
198
      return;
 
199
 
 
200
   /* Set new VROM banking. */
 
201
   ppu_set_ram_1k_pattern_vrom_block ((bank << 10), vrc6_chr_bank[bank]);
 
202
}
 
203
 
 
204
static void vrc6_write (UINT16 address, UINT8 value)
 
205
{
 
206
   int major, minor;
 
207
 
 
208
   /* Sync state. */
 
209
   vrc6_process ();
 
210
 
 
211
   /* Swap address pins. */
 
212
   if (vrc6_swap_address_pins)
 
213
      address = ((address & 0xfffc) | ((address >> 1) & 1) | ((address << 1) & 2));
 
214
 
 
215
   /* Extract command indexes. */
 
216
   major = (address & 0xf000);
 
217
   minor = (address & 0x000f);
 
218
 
 
219
   switch (major)
 
220
   {
 
221
      case 0x8000:
 
222
      {
 
223
         if (minor != 0x0000)
 
224
            break;
 
225
 
 
226
         /* Set requested 16k ROM page at $8000. */
 
227
         vrc6_prg_bank[0] = value;
 
228
         vrc6_update_prg_bank (0);
 
229
 
 
230
         break;
 
231
      }
 
232
 
 
233
      case 0xb000:
 
234
      {
 
235
         /* Mirroring select. */
 
236
 
 
237
         if (minor != 0x0003)
 
238
            break;
 
239
 
 
240
         /* Discard unused bits. */
 
241
         value = ((value & vrc6_mirroring_mask) >> 2);
 
242
 
 
243
         /* Use value from LUT. */
 
244
         ppu_set_mirroring (vrc6_mirroring_table[value]);
 
245
 
 
246
         break;
 
247
      }
 
248
 
 
249
      case 0xc000:
 
250
      {
 
251
         if (minor != 0x0000)
 
252
            break;
 
253
 
 
254
         /* Set requested 8k ROM page at $C000. */
 
255
         vrc6_prg_bank[1] = value;
 
256
         vrc6_update_prg_bank (1);
 
257
 
 
258
         break;
 
259
      }
 
260
 
 
261
      case 0xd000:
 
262
      {
 
263
         if (minor >= 0x0004)
 
264
            break;
 
265
 
 
266
         /* Set requested 1k CHR-ROM page. */
 
267
         vrc6_chr_bank[minor] = value;
 
268
         vrc6_update_chr_bank (minor);
 
269
 
 
270
         break;
 
271
      }
 
272
 
 
273
      case 0xe000:
 
274
      {
 
275
         if (minor >= 0x0004)
 
276
            break;
 
277
 
 
278
         /* Set requested 1k CHR-ROM page. */
 
279
         vrc6_chr_bank[(minor + 4)] = value;
 
280
         vrc6_update_chr_bank ((minor + 4));
 
281
 
 
282
         break;
 
283
      }
 
284
 
 
285
      case 0xf000:
 
286
      {
 
287
         switch (minor)
 
288
         {
 
289
            case 0x0000:
 
290
            {
 
291
               /* Set the IRQ counter load value. */
 
292
               vrc6_irq_latch = value;
 
293
 
 
294
               /* Update prediction. */
 
295
               vrc6_repredict_irq ();
 
296
 
 
297
               break;
 
298
            }
 
299
 
 
300
            case 0x0001:
 
301
            {
 
302
               /* Save the value for future writes to $F002. */
 
303
               vrc6_irq_reg = value;
 
304
 
 
305
               /* Enable or disable the IRQ counter. */
 
306
               vrc6_enable_irqs = TRUE_OR_FALSE(value & 0x02);
 
307
 
 
308
               /* If enabled... */
 
309
               if (vrc6_enable_irqs)
 
310
               {
 
311
                  /* Load the counter with the value from the $F000 latch. */
 
312
                  vrc6_irq_counter = vrc6_irq_latch;
 
313
 
 
314
                  /* Reset the timer. */
 
315
                  vrc6_irq_timer = 0;
 
316
               }
 
317
 
 
318
               /* Reset IRQ status. */
 
319
               cpu_clear_interrupt (CPU_INTERRUPT_IRQ_MMC);
 
320
 
 
321
               /* Update prediction. */
 
322
               vrc6_repredict_irq ();
 
323
 
 
324
               break;
 
325
            }
 
326
 
 
327
            case 0x0002:
 
328
            {
 
329
               /* Swap IRQ counter enable flags. */
 
330
               vrc6_enable_irqs = TRUE_OR_FALSE(vrc6_irq_reg & 0x01);
 
331
 
 
332
               /* Reset IRQ status. */
 
333
               cpu_clear_interrupt (CPU_INTERRUPT_IRQ_MMC);
 
334
 
 
335
               /* Update prediction. */
 
336
               vrc6_repredict_irq ();
 
337
 
 
338
               break;
 
339
            }         
 
340
 
 
341
            default:
 
342
               break;
 
343
         }
 
344
 
 
345
         break;
 
346
      }
 
347
 
 
348
      default:
 
349
         break;
 
350
   } 
 
351
 
 
352
   /* Send to ExSound. */
 
353
   apu_write (address, value);
 
354
}
 
355
 
 
356
static void vrc6_reset (void)
 
357
{
 
358
   /* Reset PRG banking. */
 
359
 
 
360
   /* Select first 16k page in lower 16k. */
 
361
   vrc6_prg_bank[0] = 0;
 
362
   vrc6_update_prg_bank (0);
 
363
 
 
364
   /* Select last 16k page in upper 16k. */
 
365
   cpu_set_read_address_16k (0xc000, LAST_ROM_PAGE);
 
366
 
 
367
   vrc6_prg_bank[1] = ((ROM_PRG_ROM_PAGES * 2) - 2);
 
368
   vrc6_update_prg_bank (1);
 
369
 
 
370
   /* Reset internal clock. */
 
371
   vrc6_clock_counter = 0;
 
372
 
 
373
   /* Reset IRQ variables. */
 
374
   vrc6_irq_timer   = 0;
 
375
   vrc6_irq_counter = 0x00;
 
376
   vrc6_irq_latch   = 0x00;
 
377
   vrc6_irq_reg     = 0x00;
 
378
   vrc6_enable_irqs = FALSE;
 
379
 
 
380
   vrc6_prediction_timestamp = 0;
 
381
   vrc6_prediction_cycles = 0;
 
382
 
 
383
   /* Reset ExSound. */
 
384
   apu_reset_exsound();
 
385
}
 
386
 
 
387
static int vrc6_base_init (void)
 
388
{
 
389
   int index;
 
390
 
 
391
   /* Install write handler. */
 
392
   cpu_set_write_handler_32k (0x8000, vrc6_write);
 
393
 
 
394
   /* Install IRQ predicter. */
 
395
   mmc_predict_irqs = vrc6_predict_irqs;
 
396
 
 
397
   /* Select ExSound chip. */
 
398
   apu_set_exsound (APU_EXSOUND_VRC6);
 
399
 
 
400
   /* Set initial mappings and reset variables. */
 
401
   vrc6_reset ();
 
402
 
 
403
   /* Return success. */
 
404
   return (0);
 
405
}
 
406
 
 
407
static int vrc6_init (void)
 
408
{
 
409
   /* Disable address pin swap. */
 
410
   vrc6_swap_address_pins = FALSE;
 
411
 
 
412
   return (vrc6_base_init ());
 
413
}
 
414
 
 
415
static int vrc6v_init (void)
 
416
{
 
417
   /* Pins A0 and A1 are swapped in VRC6V. */
 
418
   vrc6_swap_address_pins = TRUE;
 
419
 
 
420
   return (vrc6_base_init ());
 
421
}
 
422
 
 
423
static void vrc6_save_state (PACKFILE *file, int version)
 
424
{
 
425
   RT_ASSERT(file);
 
426
 
 
427
   /* Save banking. */
 
428
   pack_fwrite (vrc6_prg_bank, 2, file);
 
429
   pack_fwrite (vrc6_chr_bank, 8, file);
 
430
 
 
431
   /* Save internal clock. */
 
432
   pack_iputl (vrc6_clock_counter, file);
 
433
 
 
434
   /* Save IRQ status. */
 
435
   pack_iputw (vrc6_irq_timer,             file);
 
436
   pack_putc  (vrc6_irq_counter,           file);
 
437
   pack_putc  (vrc6_irq_latch,             file);
 
438
   pack_putc  (vrc6_irq_reg,               file);        
 
439
   pack_putc  ((vrc6_enable_irqs ? 1 : 0), file);
 
440
 
 
441
   pack_iputl (vrc6_prediction_timestamp, file);
 
442
   pack_iputl (vrc6_prediction_cycles,    file);
 
443
}
 
444
 
 
445
static void vrc6_load_state (PACKFILE *file, int version)
 
446
{
 
447
   int index;
 
448
 
 
449
   RT_ASSERT(file);
 
450
 
 
451
   /* Restore banking. */
 
452
   pack_fread (vrc6_prg_bank, 2, file);
 
453
   pack_fread (vrc6_chr_bank, 8, file);
 
454
 
 
455
   vrc6_update_prg_bank (0);
 
456
   vrc6_update_prg_bank (1);
 
457
 
 
458
   for (index = 0; index < 8; index++)
 
459
      vrc6_update_chr_bank (index);
 
460
 
 
461
   /* Restore internal clock. */
 
462
   vrc6_clock_counter = pack_igetl (file);
 
463
 
 
464
   /* Restore IRQ status. */
 
465
   vrc6_irq_timer   = pack_igetw (file);
 
466
   vrc6_irq_counter = pack_getc  (file);
 
467
   vrc6_irq_latch   = pack_getc  (file);
 
468
   vrc6_irq_reg     = pack_getc  (file);
 
469
   vrc6_enable_irqs = TRUE_OR_FALSE(pack_getc (file));
 
470
 
 
471
   vrc6_prediction_timestamp = pack_igetl (file);
 
472
   vrc6_prediction_cycles    = pack_igetl (file);
 
473
}