1
/* Mapper #24 and #26 (Konami VRC6 and VRC6V). */
2
/* These mappers are fully supported. */
3
/* IRQ prediction is supported. */
5
#include "mmc/shared.h"
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);
13
static const MMC mmc_vrc6 =
16
"Konami VRC6 + ExSound",
24
static const MMC mmc_vrc6v =
27
"Konami VRC6V + ExSound",
35
static const ENUM vrc6_mirroring_table[] =
39
MIRRORING_ONE_SCREEN_2000,
40
MIRRORING_ONE_SCREEN_2400
43
static const UINT8 vrc6_mirroring_mask = 0x0c;
45
static BOOL vrc6_swap_address_pins = FALSE;
47
static UINT8 vrc6_prg_bank[2];
48
static UINT8 vrc6_chr_bank[8];
50
/* Internal ~1.79MHz clock (scaled to ~5.37MHz for efficiency). */
51
static cpu_time_t vrc6_clock_counter = 0;
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 */
58
#define VRC6_IRQ_FREQUENCY \
59
((vrc6_irq_reg & 0x04) ? VRC6_IRQ_CYCLE_LENGTH : VRC6_IRQ_SCANLINE_LENGTH)
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;
68
static cpu_time_t vrc6_prediction_timestamp = 0;
69
static cpu_time_t vrc6_prediction_cycles = 0;
71
static void vrc6_update_irq_counter (cpu_time_t cycles, BOOL allow_irq)
73
/* Slave function used by both emulation and prediction(simulation). */
75
cpu_time_t count, offset;
77
if (!vrc6_enable_irqs)
80
/* Speed hack. Shouldn't hurt accuracy. =) */
81
cycles /= CYCLE_LENGTH;
83
/* Cache it for efficiency. */
84
count = VRC6_IRQ_FREQUENCY;
86
for (offset = 0; offset < cycles; offset++)
88
vrc6_irq_timer += CYCLE_LENGTH;
89
if (vrc6_irq_timer >= count)
91
vrc6_irq_timer -= count;
93
if (vrc6_irq_counter == 0xFF)
95
vrc6_irq_counter = vrc6_irq_latch;
98
cpu_queue_interrupt (CPU_INTERRUPT_IRQ_MMC, (vrc6_prediction_timestamp + offset));
106
static void vrc6_predict_irq (cpu_time_t cycles)
108
UINT16 saved_irq_timer;
109
UINT8 saved_irq_counter;
111
if (!vrc6_enable_irqs)
114
/* Save vars since we're just simulating. */
115
saved_irq_timer = vrc6_irq_timer;
116
saved_irq_counter = vrc6_irq_counter;
118
/* Remember to allow IRQs here. */
119
/* Just go through the motions... */
120
vrc6_update_irq_counter (cycles, TRUE);
122
/* Restore saved vars. */
123
vrc6_irq_timer = saved_irq_timer;
124
vrc6_irq_counter = saved_irq_counter;
127
static void vrc6_repredict_irq (void)
129
/* This needs to be called whenver a possible mid-scanline change to the
130
IRQ parameters occurs (to update prediction). */
132
cpu_time_t cycles, cycles_remaining;
134
cycles = cpu_get_cycles ();
136
cycles_remaining = (cycles - vrc6_prediction_timestamp);
137
if (cycles_remaining <= 0)
140
if (cycles_remaining > vrc6_prediction_cycles)
141
cycles_remaining = vrc6_prediction_cycles;
143
vrc6_predict_irq (cycles_remaining);
146
static void vrc6_process (void)
148
/* Call this before accessing the state of the mapper - before reads,
149
writes, and state-sensetive emulation. */
151
const cpu_time_t elapsed_cycles = cpu_get_elapsed_cycles (&vrc6_clock_counter);
152
if (elapsed_cycles == 0)
155
/* *Don't* allow IRQs here, or it'll conflict with prediction. */
156
vrc6_update_irq_counter (elapsed_cycles, FALSE);
159
static void vrc6_predict_irqs (cpu_time_t cycles)
161
/* Wrapper for vrc6_predict_irq() that is exposed to the MMC interfce,
162
thus must take care of a few specific things. */
164
/* Save parameters for re-prediction if a mid-scanline change occurs. */
165
vrc6_prediction_timestamp = cpu_get_cycles ();
166
vrc6_prediction_cycles = cycles;
171
vrc6_predict_irq (cycles);
174
static void vrc6_update_prg_bank (int bank)
178
case 0: /* 16k ROM page select. */
180
cpu_set_read_address_16k_rom_block (0x8000, vrc6_prg_bank[0]);
184
case 1: /* 8k ROM page select. */
186
cpu_set_read_address_8k_rom_block (0xC000, vrc6_prg_bank[1]);
195
static void vrc6_update_chr_bank (int bank)
197
if (ROM_CHR_ROM_PAGES <= 0)
200
/* Set new VROM banking. */
201
ppu_set_ram_1k_pattern_vrom_block ((bank << 10), vrc6_chr_bank[bank]);
204
static void vrc6_write (UINT16 address, UINT8 value)
211
/* Swap address pins. */
212
if (vrc6_swap_address_pins)
213
address = ((address & 0xfffc) | ((address >> 1) & 1) | ((address << 1) & 2));
215
/* Extract command indexes. */
216
major = (address & 0xf000);
217
minor = (address & 0x000f);
226
/* Set requested 16k ROM page at $8000. */
227
vrc6_prg_bank[0] = value;
228
vrc6_update_prg_bank (0);
235
/* Mirroring select. */
240
/* Discard unused bits. */
241
value = ((value & vrc6_mirroring_mask) >> 2);
243
/* Use value from LUT. */
244
ppu_set_mirroring (vrc6_mirroring_table[value]);
254
/* Set requested 8k ROM page at $C000. */
255
vrc6_prg_bank[1] = value;
256
vrc6_update_prg_bank (1);
266
/* Set requested 1k CHR-ROM page. */
267
vrc6_chr_bank[minor] = value;
268
vrc6_update_chr_bank (minor);
278
/* Set requested 1k CHR-ROM page. */
279
vrc6_chr_bank[(minor + 4)] = value;
280
vrc6_update_chr_bank ((minor + 4));
291
/* Set the IRQ counter load value. */
292
vrc6_irq_latch = value;
294
/* Update prediction. */
295
vrc6_repredict_irq ();
302
/* Save the value for future writes to $F002. */
303
vrc6_irq_reg = value;
305
/* Enable or disable the IRQ counter. */
306
vrc6_enable_irqs = TRUE_OR_FALSE(value & 0x02);
309
if (vrc6_enable_irqs)
311
/* Load the counter with the value from the $F000 latch. */
312
vrc6_irq_counter = vrc6_irq_latch;
314
/* Reset the timer. */
318
/* Reset IRQ status. */
319
cpu_clear_interrupt (CPU_INTERRUPT_IRQ_MMC);
321
/* Update prediction. */
322
vrc6_repredict_irq ();
329
/* Swap IRQ counter enable flags. */
330
vrc6_enable_irqs = TRUE_OR_FALSE(vrc6_irq_reg & 0x01);
332
/* Reset IRQ status. */
333
cpu_clear_interrupt (CPU_INTERRUPT_IRQ_MMC);
335
/* Update prediction. */
336
vrc6_repredict_irq ();
352
/* Send to ExSound. */
353
apu_write (address, value);
356
static void vrc6_reset (void)
358
/* Reset PRG banking. */
360
/* Select first 16k page in lower 16k. */
361
vrc6_prg_bank[0] = 0;
362
vrc6_update_prg_bank (0);
364
/* Select last 16k page in upper 16k. */
365
cpu_set_read_address_16k (0xc000, LAST_ROM_PAGE);
367
vrc6_prg_bank[1] = ((ROM_PRG_ROM_PAGES * 2) - 2);
368
vrc6_update_prg_bank (1);
370
/* Reset internal clock. */
371
vrc6_clock_counter = 0;
373
/* Reset IRQ variables. */
375
vrc6_irq_counter = 0x00;
376
vrc6_irq_latch = 0x00;
378
vrc6_enable_irqs = FALSE;
380
vrc6_prediction_timestamp = 0;
381
vrc6_prediction_cycles = 0;
387
static int vrc6_base_init (void)
391
/* Install write handler. */
392
cpu_set_write_handler_32k (0x8000, vrc6_write);
394
/* Install IRQ predicter. */
395
mmc_predict_irqs = vrc6_predict_irqs;
397
/* Select ExSound chip. */
398
apu_set_exsound (APU_EXSOUND_VRC6);
400
/* Set initial mappings and reset variables. */
403
/* Return success. */
407
static int vrc6_init (void)
409
/* Disable address pin swap. */
410
vrc6_swap_address_pins = FALSE;
412
return (vrc6_base_init ());
415
static int vrc6v_init (void)
417
/* Pins A0 and A1 are swapped in VRC6V. */
418
vrc6_swap_address_pins = TRUE;
420
return (vrc6_base_init ());
423
static void vrc6_save_state (PACKFILE *file, int version)
428
pack_fwrite (vrc6_prg_bank, 2, file);
429
pack_fwrite (vrc6_chr_bank, 8, file);
431
/* Save internal clock. */
432
pack_iputl (vrc6_clock_counter, file);
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);
441
pack_iputl (vrc6_prediction_timestamp, file);
442
pack_iputl (vrc6_prediction_cycles, file);
445
static void vrc6_load_state (PACKFILE *file, int version)
451
/* Restore banking. */
452
pack_fread (vrc6_prg_bank, 2, file);
453
pack_fread (vrc6_chr_bank, 8, file);
455
vrc6_update_prg_bank (0);
456
vrc6_update_prg_bank (1);
458
for (index = 0; index < 8; index++)
459
vrc6_update_chr_bank (index);
461
/* Restore internal clock. */
462
vrc6_clock_counter = pack_igetl (file);
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));
471
vrc6_prediction_timestamp = pack_igetl (file);
472
vrc6_prediction_cycles = pack_igetl (file);