/* sound.c YM2612 and SN76489 emulation */ #include "shared.h" /* YM2612 data */ int fm_timera_tab[0x400]; /* Precalculated timer A values */ int fm_timerb_tab[0x100]; /* Precalculated timer B values */ uint8 fm_reg[2][0x100]; /* Register arrays (2x256) */ uint8 fm_latch[2]; /* Register latches */ uint8 fm_status; /* Read-only status flags */ t_timer timer[2]; /* Timers A and B */ extern void *myFM; /* Initialize the YM2612 and SN76489 emulation */ void sound_init(void) { /* Timers run at half the YM2612 input clock */ float clock = ((Master_Clock / 1000000.0) / 7.0) / 2.0; int i; /* Make Timer A table */ for(i = 0; i < 1024; i += 1) { /* Formula is "time(us) = 72 * (1024 - A) / clock" */ fm_timera_tab[i] = (int)((double)(72 * (1024 - i)) / clock); } /* Make Timer B table */ for(i = 0; i < 256; i += 1) { /* Formula is "time(us) = 1152 * (256 - B) / clock" */ fm_timerb_tab[i] = (int)((double)(1152 * (256 - i)) / clock); } } void fm_restore(void) { int i; if (FM_GENS) YM2612_Reset(); else YM2612ResetChip(myFM); /* feed all the registers and update internal state */ for(i = 0; i < 0x100; i++) { if (FM_GENS) { YM2612_Write(0, i); YM2612_Write(1, fm_reg[0][i]); YM2612_Write(2, i); YM2612_Write(3, fm_reg[1][i]); } else { YM2612Write(myFM, 0, i); YM2612Write(myFM, 1, fm_reg[0][i]); YM2612Write(myFM, 2, i); YM2612Write(myFM, 3, fm_reg[1][i]); } } } void fm_reset(void) { if (FM_GENS) YM2612_Reset(); else YM2612ResetChip(myFM); /* reset timers status */ timer[0].running = 0; timer[1].running = 0; timer[0].enable = 0; timer[1].enable = 0; timer[0].count = 0; timer[1].count = 0; timer[0].base = 0; timer[1].base = 0; timer[0].index = 0; timer[1].index = 0; /* reset FM status */ fm_status = 0; } void fm_write(uint8 cpu, int address, int data) { int a0 = (address & 1); int a1 = (address >> 1) & 1; if(a0) { /* Register data */ fm_reg[a1][fm_latch[a1]] = data; /* Timer control only in set A */ if(a1 == 0) switch(fm_latch[a1]) { case 0x24: /* Timer A (LSB) */ timer[0].index = ((timer[0].index & 0x0003) | (data << 2)) & 0x03FF; if (timer[0].base != fm_timera_tab[timer[0].index]) { timer[0].base = fm_timera_tab[timer[0].index]; timer[0].count = 0; } break; case 0x25: /* Timer A (MSB) */ timer[0].index = ((timer[0].index & 0x03FC) | (data & 3)) & 0x03FF; if (timer[0].base != fm_timera_tab[timer[0].index]) { timer[0].base = fm_timera_tab[timer[0].index]; timer[0].count = 0; } break; case 0x26: /* Timer B */ timer[1].index = data; if (timer[1].base != fm_timerb_tab[timer[1].index]) { timer[1].base = fm_timerb_tab[timer[1].index]; timer[1].count = 0; } break; case 0x27: /* Timer Control */ /* LOAD */ timer[0].running = (data & 1); timer[1].running = (data & 2); /* ENABLE */ timer[0].enable = (data >> 2) & 1; timer[1].enable = (data >> 3) & 1; /* RESET */ if(data & 0x10) fm_status &= ~1; if(data & 0x20) fm_status &= ~2; break; } } else { /* Register latch */ fm_latch[a1] = data; } if (snd.enabled) { if (cpu) snd.fm.curStage = (count_z80 + current_z80 - z80_ICount) / z80cyc_per_sample; /* write from Z80 */ else snd.fm.curStage = (count_m68k + m68k_cycles_run()) / m68cyc_per_sample; /* write from 68000 */ if(snd.fm.curStage - snd.fm.lastStage > 0) { if (FM_GENS) { int *tempBuffer[2]; tempBuffer[0] = snd.fm.gens_buffer[0] + snd.fm.lastStage; tempBuffer[1] = snd.fm.gens_buffer[1] + snd.fm.lastStage; YM2612_Update(tempBuffer, snd.fm.curStage - snd.fm.lastStage); } else { int16 *tempBuffer[2]; tempBuffer[0] = snd.fm.buffer[0] + snd.fm.lastStage; tempBuffer[1] = snd.fm.buffer[1] + snd.fm.lastStage; YM2612UpdateOne(myFM, tempBuffer, snd.fm.curStage - snd.fm.lastStage); } snd.fm.lastStage = snd.fm.curStage; } if (FM_GENS) YM2612_Write(address & 3, data); else YM2612Write(myFM, address & 3, data); } } int fm_read(int address) { return (fm_status); } void fm_update_timers(int inc) { int i; /* Process YM2612 timers */ for(i = 0; i < 2; i += 1) { /* Is the timer running? */ if(timer[i].running) { /* Each scanline takes up roughly 64 microseconds */ timer[i].count += inc; /* Check if the counter overflowed */ if(timer[i].count >= timer[i].base) { /* Reload counter */ timer[i].count -= timer[i].base; /* Set overflow flag (if flag setting is enabled) */ if(timer[i].enable) fm_status |= (1 << i); /* Notice FM core (some CH operation on TimerA) */ if(i==0) { if (FM_GENS) YM2612TimerAOver(); else YM2612TimerOver(myFM,0); } } } } } void psg_write(uint8 cpu, int data) { if(snd.enabled) { if (cpu) snd.psg.curStage = (count_z80 + current_z80 - z80_ICount) / z80cyc_per_sample; /* write from Z80 */ else snd.psg.curStage = (count_m68k + m68k_cycles_run()) / m68cyc_per_sample; /* write from 68000 */ if(snd.psg.curStage - snd.psg.lastStage > 0) { int16 *tempBuffer; tempBuffer = snd.psg.buffer + snd.psg.lastStage; if (PSG_MAME) SN76496Update (0, tempBuffer, snd.psg.curStage - snd.psg.lastStage); else SN76489_Update(0, tempBuffer, snd.psg.curStage - snd.psg.lastStage); snd.psg.lastStage = snd.psg.curStage; } if (PSG_MAME) SN76496Write(0, data); else SN76489_Write(0, data); } }