From a6cae1a8f5fa24e6afe53c27112f674b8fbd03d4 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sat, 12 Aug 2017 08:58:26 +0000 Subject: [PATCH 1/7] Try latest mame sound cores Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4030 From 6c796f4162eb6855c7dd6041f58493e7b99a6815 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sat, 12 Aug 2017 09:11:40 +0000 Subject: [PATCH 2/7] Clean mame versions from 10 August 2017 Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4031 --- src/hardware/mame/fmopl.cpp | 2572 +++++++++++++++++++++++++++++ src/hardware/mame/fmopl.h | 109 ++ src/hardware/mame/saa1099.cpp | 458 ++++++ src/hardware/mame/saa1099.h | 96 ++ src/hardware/mame/sn76496.cpp | 474 ++++++ src/hardware/mame/sn76496.h | 155 ++ src/hardware/mame/ymdeltat.cpp | 650 ++++++++ src/hardware/mame/ymdeltat.h | 85 + src/hardware/mame/ymf262.cpp | 2788 ++++++++++++++++++++++++++++++++ src/hardware/mame/ymf262.h | 40 + 10 files changed, 7427 insertions(+) create mode 100644 src/hardware/mame/fmopl.cpp create mode 100644 src/hardware/mame/fmopl.h create mode 100644 src/hardware/mame/saa1099.cpp create mode 100644 src/hardware/mame/saa1099.h create mode 100644 src/hardware/mame/sn76496.cpp create mode 100644 src/hardware/mame/sn76496.h create mode 100644 src/hardware/mame/ymdeltat.cpp create mode 100644 src/hardware/mame/ymdeltat.h create mode 100644 src/hardware/mame/ymf262.cpp create mode 100644 src/hardware/mame/ymf262.h diff --git a/src/hardware/mame/fmopl.cpp b/src/hardware/mame/fmopl.cpp new file mode 100644 index 00000000..a9c15478 --- /dev/null +++ b/src/hardware/mame/fmopl.cpp @@ -0,0 +1,2572 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +/* +** +** File: fmopl.c - software implementation of FM sound generator +** types OPL and OPL2 +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 0.72 +** + +Revision History: + +04-08-2003 Jarek Burczynski: + - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip + handles memory read/write or during the adpcm synthesis when the chip + requests another byte of ADPCM data. + +24-07-2003 Jarek Burczynski: + - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after + some (unknown) delay). Right now it's always set. + +14-06-2003 Jarek Burczynski: + - implemented all of the status register flags in Y8950 emulation + - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since + they can be either RAM or ROM + +08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) + - corrected ym3526_read() to always set bit 2 and bit 1 + to HIGH state - identical to ym3812_read (verified on real YM3526) + +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + +03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) + Complete rewrite (all verified on real YM3812): + - corrected sin_tab and tl_tab data + - corrected operator output calculations + - corrected waveform_select_enable register; + simply: ignore all writes to waveform_select register when + waveform_select_enable == 0 and do not change the waveform previously selected. + - corrected KSR handling + - corrected Envelope Generator: attack shape, Sustain mode and + Percussive/Non-percussive modes handling + - Envelope Generator rates are two times slower now + - LFO amplitude (tremolo) and phase modulation (vibrato) + - rhythm sounds phase generation + - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) + - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) + - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) + +12-28-2001 Acho A. Tang + - reflected Delta-T EOS status on Y8950 status port. + - fixed subscription range of attack/decay tables + + + To do: + add delay before key off in CSM mode (see CSMKeyControll) + verify volume of the FM part on the Y8950 +*/ + +#include "emu.h" +#include "ymdeltat.h" +#include "fmopl.h" + + + +/* output final shift */ +#if (OPL_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<=0) + { + if (value < 0x0200) + return (value & ~0); + if (value < 0x0400) + return (value & ~1); + if (value < 0x0800) + return (value & ~3); + if (value < 0x1000) + return (value & ~7); + if (value < 0x2000) + return (value & ~15); + if (value < 0x4000) + return (value & ~31); + return (value & ~63); + } + /*else value < 0*/ + if (value > -0x0200) + return (~abs(value) & ~0); + if (value > -0x0400) + return (~abs(value) & ~1); + if (value > -0x0800) + return (~abs(value) & ~3); + if (value > -0x1000) + return (~abs(value) & ~7); + if (value > -0x2000) + return (~abs(value) & ~15); + if (value > -0x4000) + return (~abs(value) & ~31); + return (~abs(value) & ~63); +} + + +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = acc_calc(lt); \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ +#define OPL_TYPE_IO 0x08 /* I/O port */ + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) + + +namespace { + +// TODO: make these static members + +#define RATE_STEPS (8) +extern const unsigned char eg_rate_shift[16+64+16]; +extern const unsigned char eg_rate_select[16+64+16]; + + +struct OPL_SLOT +{ + uint32_t ar; /* attack rate: AR<<2 */ + uint32_t dr; /* decay rate: DR<<2 */ + uint32_t rr; /* release rate:RR<<2 */ + uint8_t KSR; /* key scale rate */ + uint8_t ksl; /* keyscale level */ + uint8_t ksr; /* key scale rate: kcode>>KSR */ + uint8_t mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + uint32_t Cnt; /* frequency counter */ + uint32_t Incr; /* frequency counter step */ + uint8_t FB; /* feedback shift value */ + int32_t *connect1; /* slot1 output pointer */ + int32_t op1_out[2]; /* slot1 output for feedback */ + uint8_t CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + uint8_t eg_type; /* percussive/non-percussive mode */ + uint8_t state; /* phase type */ + uint32_t TL; /* total level: TL << 2 */ + int32_t TLL; /* adjusted now TL */ + int32_t volume; /* envelope counter */ + uint32_t sl; /* sustain level: sl_tab[SL] */ + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint8_t eg_sh_dr; /* (decay state) */ + uint8_t eg_sel_dr; /* (decay state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ + uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + uint16_t wavetable; + + void KEYON(uint32_t key_set) + { + if( !key ) + { + /* restart Phase Generator */ + Cnt = 0; + /* phase -> Attack */ + state = EG_ATT; + } + key |= key_set; + } + + void KEYOFF(uint32_t key_clr) + { + if( key ) + { + key &= key_clr; + + if( !key ) + { + /* phase -> Release */ + if (state>EG_REL) + state = EG_REL; + } + } + } +}; + +struct OPL_CH +{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + uint32_t block_fnum; /* block+fnum */ + uint32_t fc; /* Freq. Increment base */ + uint32_t ksl_base; /* KeyScaleLevel Base step */ + uint8_t kcode; /* key code (for key scaling) */ + + + /* update phase increment counter of operator (also update the EG rates if necessary) */ + void CALC_FCSLOT(OPL_SLOT &SLOT) + { + /* (frequency) phase increment counter */ + SLOT.Incr = fc * SLOT.mul; + int const ksr = kcode >> SLOT.KSR; + + if( SLOT.ksr != ksr ) + { + SLOT.ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT.ar + SLOT.ksr) < 16+62) + { + SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; + SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; + } + else + { + SLOT.eg_sh_ar = 0; + SLOT.eg_sel_ar = 13*RATE_STEPS; + } + SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; + SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; + SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; + SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; + } + } +}; + +/* OPL state */ +struct FM_OPL +{ + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ + + uint8_t rhythm; /* Rhythm mode */ + + uint32_t fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + uint32_t LFO_AM; + int32_t LFO_PM; + + uint8_t lfo_am_depth; + uint8_t lfo_pm_depth_range; + uint32_t lfo_am_cnt; + uint32_t lfo_am_inc; + uint32_t lfo_pm_cnt; + uint32_t lfo_pm_inc; + + uint32_t noise_rng; /* 23 bit noise shift register */ + uint32_t noise_p; /* current noise 'phase' */ + uint32_t noise_f; /* current noise period */ + + uint8_t wavesel; /* waveform select enable flag */ + + uint32_t T[2]; /* timer counters */ + uint8_t st[2]; /* timer enable */ + +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + + YM_DELTAT *deltat; + + /* Keyboard and I/O ports interface */ + uint8_t portDirection; + uint8_t portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + device_t * port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + device_t * keyboard_param; +#endif + + /* external event callback handlers */ + OPL_TIMERHANDLER timer_handler; /* TIMER handler */ + device_t *TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + device_t *IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ + device_t *UpdateParam; /* stream update parameter */ + + uint8_t type; /* chip type */ + uint8_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + uint8_t mode; /* Reg.08 : CSM,notesel,etc. */ + + uint32_t clock; /* master clock (Hz) */ + uint32_t rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + attotime TimerBase; /* Timer base time (==sampling time)*/ + device_t *device; + + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + signed int output[1]; +#if BUILD_Y8950 + int32_t output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ +#endif + + + /* status set and IRQ handling */ + void STATUS_SET(int flag) + { + /* set status flag */ + status |= flag; + if(!(status & 0x80)) + { + if(status & statusmask) + { /* IRQ on */ + status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(IRQHandler) (IRQHandler)(IRQParam,1); + } + } + } + + /* status reset and IRQ handling */ + void STATUS_RESET(int flag) + { + /* reset status flag */ + status &=~flag; + if(status & 0x80) + { + if (!(status & statusmask) ) + { + status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(IRQHandler) (IRQHandler)(IRQParam,0); + } + } + } + + /* IRQ mask set */ + void STATUSMASK_SET(int flag) + { + statusmask = flag; + /* IRQ handling check */ + STATUS_SET(0); + STATUS_RESET(0); + } + + + /* advance LFO to next sample */ + void advance_lfo() + { + /* LFO */ + lfo_am_cnt += lfo_am_inc; + if (lfo_am_cnt >= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH)) /* lfo_am_table is 210 elements long */ + lfo_am_cnt -= (uint32_t(LFO_AM_TAB_ELEMENTS) << LFO_SH); + + uint8_t const tmp = lfo_am_table[ lfo_am_cnt >> LFO_SH ]; + + LFO_AM = lfo_am_depth ? tmp : tmp >> 2; + + lfo_pm_cnt += lfo_pm_inc; + LFO_PM = (lfo_pm_cnt>>LFO_SH & 7) | lfo_pm_depth_range; + } + + /* advance to next sample */ + void advance() + { + eg_timer += eg_timer_add; + + while (eg_timer >= eg_timer_overflow) + { + eg_timer -= eg_timer_overflow; + + eg_cnt++; + + for (int i=0; i<9*2; i++) + { + OPL_CH &CH = P_CH[i/2]; + OPL_SLOT &op = CH.SLOT[i&1]; + + /* Envelope Generator */ + switch(op.state) + { + case EG_ATT: /* attack phase */ + if ( !(eg_cnt & ((1<>op.eg_sh_ar)&7)]) + ) >>3; + + if (op.volume <= MIN_ATT_INDEX) + { + op.volume = MIN_ATT_INDEX; + op.state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(eg_cnt & ((1<>op.eg_sh_dr)&7)]; + + if ( op.volume >= op.sl ) + op.state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op.eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(eg_cnt & ((1<>op.eg_sh_rr)&7)]; + + if ( op.volume >= MAX_ATT_INDEX ) + op.volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(eg_cnt & ((1<>op.eg_sh_rr)&7)]; + + if ( op.volume >= MAX_ATT_INDEX ) + { + op.volume = MAX_ATT_INDEX; + op.state = EG_OFF; + } + + } + break; + + default: + break; + } + } + } + + for (int i=0; i<9*2; i++) + { + OPL_CH &CH = P_CH[i/2]; + OPL_SLOT &op = CH.SLOT[i&1]; + + /* Phase Generator */ + if(op.vib) + { + unsigned int block_fnum = CH.block_fnum; + unsigned int const fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int const lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + uint8_t const block = (block_fnum&0x1c00) >> 10; + op.Cnt += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op.mul; + } + else /* LFO phase modulation = zero */ + { + op.Cnt += op.Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op.Cnt += op.Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + noise_p += noise_f; + int i = noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + noise_p &= FREQ_MASK; + while (i) + { + /* + uint32_t j; + j = ( (noise_rng) ^ (noise_rng>>14) ^ (noise_rng>>15) ^ (noise_rng>>22) ) & 1; + noise_rng = (j<<22) | (noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (noise_rng & 1) noise_rng ^= 0x800302; + noise_rng >>= 1; + + i--; + } + } + + /* calculate output */ + void CALC_CH(OPL_CH &CH) + { + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH.SLOT[SLOT1]; + env = volume_calc(*SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(*SLOT); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); + } + + /* + operators used in the rhythm sounds generation process: + + Envelope Generator: + + channel operator register number Bass High Snare Tom Top + / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + + channel operator register number Bass High Snare Tom Top + / slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + + channel operator register number Bass High Snare Tom Top + number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + + */ + + /* calculate rhythm */ + + void CALC_RH() + { + unsigned int const noise = BIT(noise_rng, 0); + + OPL_SLOT *SLOT; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + phase_modulation = 0; + /* SLOT 1 */ + SLOT = &P_CH[6].SLOT[SLOT1]; + env = volume_calc(*SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + phase_modulation = SLOT->op1_out[0]; + /* else ignore output of operator 1 */ + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(*SLOT); + if( env < ENV_QUIET ) + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ + /* SD (16) channel 7->slot 1 */ + /* TOM (14) channel 8->slot 1 */ + /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ + + /* Envelope generation based on: */ + /* HH channel 7->slot1 */ + /* SD channel 7->slot2 */ + /* TOM channel 8->slot1 */ + /* TOP channel 8->slot2 */ + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + OPL_SLOT const &SLOT7_1 = P_CH[7].SLOT[SLOT1]; + OPL_SLOT const &SLOT8_2 = P_CH[8].SLOT[SLOT2]; + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET ) + { + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char const bit7 = BIT(SLOT7_1.Cnt >> FREQ_SH, 7); + unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3); + unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2); + + unsigned char const res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5); + unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3); + + unsigned char const res2 = bit3e ^ bit5e; + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + output[0] += op_calc(phase<> FREQ_SH, 8); + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + uint32_t phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + output[0] += op_calc(phase<> FREQ_SH, 7); + unsigned char const bit3 = BIT(SLOT7_1.Cnt >> FREQ_SH, 3); + unsigned char const bit2 = BIT(SLOT7_1.Cnt >> FREQ_SH, 2); + + unsigned char const res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + uint32_t phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char const bit5e= BIT(SLOT8_2.Cnt >> FREQ_SH, 5); + unsigned char const bit3e= BIT(SLOT8_2.Cnt >> FREQ_SH, 3); + + unsigned char const res2 = bit3e ^ bit5e; + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + output[0] += op_calc(phase<> 6]; + SLOT.TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl); + } + + /* set attack rate & decay rate */ + void set_ar_dr(int slot, int v) + { + OPL_CH &CH = P_CH[slot/2]; + OPL_SLOT &SLOT = CH.SLOT[slot&1]; + + SLOT.ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT.ar + SLOT.ksr) < 16+62) + { + SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; + SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; + } + else + { + SLOT.eg_sh_ar = 0; + SLOT.eg_sel_ar = 13*RATE_STEPS; + } + + SLOT.dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; + SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; + } + + /* set sustain level & release rate */ + void set_sl_rr(int slot, int v) + { + OPL_CH &CH = P_CH[slot/2]; + OPL_SLOT &SLOT = CH.SLOT[slot&1]; + + SLOT.sl = sl_tab[ v>>4 ]; + + SLOT.rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; + SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; + } + + + void WriteReg(int r, int v); + void ResetChip(); + void postload(); + + + /* lock/unlock for common table */ + static int LockTable(device_t *device) + { + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + /* allocate total level table (128kb space) */ + if( !init_tables() ) + { + num_lock--; + return -1; + } + + return 0; + } + + static void UnLockTable() + { + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + CloseTable(); + } + +private: + uint32_t volume_calc(OPL_SLOT const &OP) const + { + return OP.TLL + uint32_t(OP.volume) + (LFO_AM & OP.AMmask); + } + + static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) + { + uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + return (p >= TL_TAB_LEN) ? 0 : tl_tab[p]; + } + + static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) + { + uint32_t const p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; + + return (p >= TL_TAB_LEN) ? 0 : tl_tab[p]; + } + + + static int init_tables(); + + static void CloseTable() + { +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif + } + + + static constexpr uint32_t SC(uint32_t db) { return uint32_t(db * (2.0 / ENV_STEP)); } + + + static constexpr double DV = 0.1875 / 2.0; + + + /* TL_TAB_LEN is calculated as: + * 12 - sinus amplitude bits (Y axis) + * 2 - sinus sign bit (Y axis) + * TL_RES_LEN - sinus resolution (X axis) + */ + static constexpr unsigned TL_TAB_LEN = 12 * 2 * TL_RES_LEN; + static constexpr unsigned ENV_QUIET = TL_TAB_LEN >> 4; + + static constexpr unsigned LFO_AM_TAB_ELEMENTS = 210; + + static const double ksl_tab[8*16]; + static const uint32_t ksl_shift[4]; + static const uint32_t sl_tab[16]; + static const unsigned char eg_inc[15 * RATE_STEPS]; + + static const uint8_t mul_tab[16]; + static signed int tl_tab[TL_TAB_LEN]; + static unsigned int sin_tab[SIN_LEN * 4]; + + static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS]; + static const int8_t lfo_pm_table[8 * 8 * 2]; + + static int num_lock; +}; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +const double FM_OPL::ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +const uint32_t FM_OPL::ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +const uint32_t FM_OPL::sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC( 3),SC( 4),SC( 5),SC( 6),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; + + +const unsigned char FM_OPL::eg_inc[15*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +const uint8_t FM_OPL::mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML, + 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML +}; +#undef ML + +signed int FM_OPL::tl_tab[TL_TAB_LEN]; + +/* sin waveform table in 'decibel' scale */ +/* four waveforms on OPL2 type chips */ +unsigned int FM_OPL::sin_tab[SIN_LEN * 4]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +const uint8_t FM_OPL::lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +const int8_t FM_OPL::lfo_pm_table[8*8*2] = { +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +int FM_OPL::num_lock = 0; + + + +static inline int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + + +/* generic table initialize */ +int FM_OPL::init_tables() +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<12; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<12; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); + logerror("\n"); + #endif + } + /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ + } + /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + + +void FM_OPL::initialize() +{ + int i; + + /* frequency base */ + freqbase = (rate) ? ((double)clock / 72.0) / rate : 0; +#if 0 + rate = (double)clock / 72.0; + freqbase = 1.0; +#endif + + /*logerror("freqbase=%f\n", freqbase);*/ + + /* Timer base time */ + TimerBase = attotime::from_hz(clock) * 72; + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + fn_tab[i] = (uint32_t)( (double)i * 64 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, fn_tab[i]>>6, fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("FMOPL.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", static_cast(ksl_tab[i*16+j]) ); + } + logerror("\n"); + } +#endif + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + lfo_am_inc = (1.0 / 64.0 ) * (1<>1)&1; + + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + STATUS_RESET(v & (0x78-0x08)); + STATUSMASK_SET((~v) & 0x78); + + /* timer 2 */ + if(st[1] != st2) + { + attotime period = st2 ? (TimerBase * T[1]) : attotime::zero; + st[1] = st2; + if (timer_handler) (timer_handler)(TimerParam,1,period); + } + /* timer 1 */ + if(st[0] != st1) + { + attotime period = st1 ? (TimerBase * T[0]) : attotime::zero; + st[0] = st1; + if (timer_handler) (timer_handler)(TimerParam,0,period); + } + } + break; +#if BUILD_Y8950 + case 0x06: /* Key Board OUT */ + if(type&OPL_TYPE_KEYBOARD) + { + if(keyboardhandler_w) + keyboardhandler_w(keyboard_param,v); + else + device->logerror("Y8950: write unmapped KEYBOARD port\n"); + } + break; + case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + if(type&OPL_TYPE_ADPCM) + deltat->ADPCM_Write(r-0x07,v); + break; +#endif + case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + mode = v; +#if BUILD_Y8950 + if(type&OPL_TYPE_ADPCM) + deltat->ADPCM_Write(r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ +#endif + break; + +#if BUILD_Y8950 + case 0x09: /* START ADD */ + case 0x0a: + case 0x0b: /* STOP ADD */ + case 0x0c: + case 0x0d: /* PRESCALE */ + case 0x0e: + case 0x0f: /* ADPCM data write */ + case 0x10: /* DELTA-N */ + case 0x11: /* DELTA-N */ + case 0x12: /* ADPCM volume */ + if(type&OPL_TYPE_ADPCM) + deltat->ADPCM_Write(r-0x07,v); + break; + + case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ + case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ + case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ + device->logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v); + break; + + case 0x18: /* I/O CTRL (Direction) */ + if(type&OPL_TYPE_IO) + portDirection = v&0x0f; + break; + case 0x19: /* I/O DATA */ + if(type&OPL_TYPE_IO) + { + portLatch = v; + if(porthandler_w) + porthandler_w(port_param,v&portDirection); + } + break; +#endif + default: + device->logerror("FMOPL.C: write to unknown register: %02x\n",r); + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(slot,v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(slot,v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(slot,v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(slot,v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + lfo_am_depth = v & 0x80; + lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + rhythm = v&0x3f; + + if(rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + P_CH[6].SLOT[SLOT1].KEYON(2); + P_CH[6].SLOT[SLOT2].KEYON(2); + } + else + { + P_CH[6].SLOT[SLOT1].KEYOFF(~2); + P_CH[6].SLOT[SLOT2].KEYOFF(~2); + } + /* HH key on/off */ + if(v&0x01) P_CH[7].SLOT[SLOT1].KEYON ( 2); + else P_CH[7].SLOT[SLOT1].KEYOFF(~2); + /* SD key on/off */ + if(v&0x08) P_CH[7].SLOT[SLOT2].KEYON ( 2); + else P_CH[7].SLOT[SLOT2].KEYOFF(~2); + /* TOM key on/off */ + if(v&0x04) P_CH[8].SLOT[SLOT1].KEYON ( 2); + else P_CH[8].SLOT[SLOT1].KEYOFF(~2); + /* TOP-CY key on/off */ + if(v&0x02) P_CH[8].SLOT[SLOT2].KEYON ( 2); + else P_CH[8].SLOT[SLOT2].KEYOFF(~2); + } + else + { + /* BD key off */ + P_CH[6].SLOT[SLOT1].KEYOFF(~2); + P_CH[6].SLOT[SLOT2].KEYOFF(~2); + /* HH key off */ + P_CH[7].SLOT[SLOT1].KEYOFF(~2); + /* SD key off */ + P_CH[7].SLOT[SLOT2].KEYOFF(~2); + /* TOM key off */ + P_CH[8].SLOT[SLOT1].KEYOFF(~2); + /* TOP-CY off */ + P_CH[8].SLOT[SLOT2].KEYOFF(~2); + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if(v&0x20) + { + CH->SLOT[SLOT1].KEYON ( 1); + CH->SLOT[SLOT2].KEYON ( 1); + } + else + { + CH->SLOT[SLOT1].KEYOFF(~1); + CH->SLOT[SLOT2].KEYOFF(~1); + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + uint8_t block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = static_cast(ksl_tab[block_fnum>>6]); + CH->fc = fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (mode&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CH->CALC_FCSLOT(CH->SLOT[SLOT1]); + CH->CALC_FCSLOT(CH->SLOT[SLOT2]); + } + break; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &P_CH[r&0x0f]; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation; + break; + case 0xe0: /* waveform select */ + /* simply ignore write to the waveform select register if selecting not enabled in test register */ + if(wavesel) + { + slot = slot_array[r&0x1f]; + if(slot < 0) return; + CH = &P_CH[slot/2]; + + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; + } + break; + } +} + + +void FM_OPL::ResetChip() +{ + eg_timer = 0; + eg_cnt = 0; + + noise_rng = 1; /* noise shift register */ + mode = 0; /* normal mode */ + STATUS_RESET(0x7f); + + /* reset with register write */ + WriteReg(0x01,0); /* wavesel disable */ + WriteReg(0x02,0); /* Timer1 */ + WriteReg(0x03,0); /* Timer2 */ + WriteReg(0x04,0); /* IRQ mask clear */ + for(int i = 0xff ; i >= 0x20 ; i-- ) WriteReg(i,0); + + /* reset operator parameters */ + for(OPL_CH &CH : P_CH) + { + for(OPL_SLOT &SLOT : CH.SLOT) + { + /* wave table */ + SLOT.wavetable = 0; + SLOT.state = EG_OFF; + SLOT.volume = MAX_ATT_INDEX; + } + } +#if BUILD_Y8950 + if(type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = deltat; + + DELTAT->freqbase = freqbase; + DELTAT->output_pointer = &output_deltat[0]; + DELTAT->portshift = 5; + DELTAT->output_range = 1<<23; + DELTAT->ADPCM_Reset(0,YM_DELTAT::EMULATION_MODE_NORMAL,device); + } +#endif +} + + +void FM_OPL::postload() +{ + for(OPL_CH &CH : P_CH) + { + /* Look up key scale level */ + uint32_t const block_fnum = CH.block_fnum; + CH.ksl_base = static_cast(ksl_tab[block_fnum >> 6]); + CH.fc = fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); + + for(OPL_SLOT &SLOT : CH.SLOT) + { + /* Calculate key scale rate */ + SLOT.ksr = CH.kcode >> SLOT.KSR; + + /* Calculate attack, decay and release rates */ + if ((SLOT.ar + SLOT.ksr) < 16+62) + { + SLOT.eg_sh_ar = eg_rate_shift [SLOT.ar + SLOT.ksr ]; + SLOT.eg_sel_ar = eg_rate_select[SLOT.ar + SLOT.ksr ]; + } + else + { + SLOT.eg_sh_ar = 0; + SLOT.eg_sel_ar = 13*RATE_STEPS; + } + SLOT.eg_sh_dr = eg_rate_shift [SLOT.dr + SLOT.ksr ]; + SLOT.eg_sel_dr = eg_rate_select[SLOT.dr + SLOT.ksr ]; + SLOT.eg_sh_rr = eg_rate_shift [SLOT.rr + SLOT.ksr ]; + SLOT.eg_sel_rr = eg_rate_select[SLOT.rr + SLOT.ksr ]; + + /* Calculate phase increment */ + SLOT.Incr = CH.fc * SLOT.mul; + + /* Total level */ + SLOT.TLL = SLOT.TL + (CH.ksl_base >> SLOT.ksl); + + /* Connect output */ + SLOT.connect1 = SLOT.CON ? &output[0] : &phase_modulation; + } + } +#if BUILD_Y8950 + if ( (type & OPL_TYPE_ADPCM) && (deltat) ) + { + // We really should call the postlod function for the YM_DELTAT, but it's hard without registers + // (see the way the YM2610 does it) + //deltat->postload(REGS); + } +#endif +} + +} // anonymous namespace + + +static void OPLsave_state_channel(device_t *device, OPL_CH *CH) +{ + int slot, ch; + + for( ch=0 ; ch < 9 ; ch++, CH++ ) + { + /* channel */ + device->save_item(NAME(CH->block_fnum), ch); + device->save_item(NAME(CH->kcode), ch); + /* slots */ + for( slot=0 ; slot < 2 ; slot++ ) + { + OPL_SLOT *SLOT = &CH->SLOT[slot]; + + device->save_item(NAME(SLOT->ar), ch * 2 + slot); + device->save_item(NAME(SLOT->dr), ch * 2 + slot); + device->save_item(NAME(SLOT->rr), ch * 2 + slot); + device->save_item(NAME(SLOT->KSR), ch * 2 + slot); + device->save_item(NAME(SLOT->ksl), ch * 2 + slot); + device->save_item(NAME(SLOT->mul), ch * 2 + slot); + + device->save_item(NAME(SLOT->Cnt), ch * 2 + slot); + device->save_item(NAME(SLOT->FB), ch * 2 + slot); + device->save_item(NAME(SLOT->op1_out), ch * 2 + slot); + device->save_item(NAME(SLOT->CON), ch * 2 + slot); + + device->save_item(NAME(SLOT->eg_type), ch * 2 + slot); + device->save_item(NAME(SLOT->state), ch * 2 + slot); + device->save_item(NAME(SLOT->TL), ch * 2 + slot); + device->save_item(NAME(SLOT->volume), ch * 2 + slot); + device->save_item(NAME(SLOT->sl), ch * 2 + slot); + device->save_item(NAME(SLOT->key), ch * 2 + slot); + + device->save_item(NAME(SLOT->AMmask), ch * 2 + slot); + device->save_item(NAME(SLOT->vib), ch * 2 + slot); + + device->save_item(NAME(SLOT->wavetable), ch * 2 + slot); + } + } +} + + +/* Register savestate for a virtual YM3812/YM3526Y8950 */ + +static void OPL_save_state(FM_OPL *OPL, device_t *device) +{ + OPLsave_state_channel(device, OPL->P_CH); + + device->save_item(NAME(OPL->eg_cnt)); + device->save_item(NAME(OPL->eg_timer)); + + device->save_item(NAME(OPL->rhythm)); + + device->save_item(NAME(OPL->lfo_am_depth)); + device->save_item(NAME(OPL->lfo_pm_depth_range)); + device->save_item(NAME(OPL->lfo_am_cnt)); + device->save_item(NAME(OPL->lfo_pm_cnt)); + + device->save_item(NAME(OPL->noise_rng)); + device->save_item(NAME(OPL->noise_p)); + + if( OPL->type & OPL_TYPE_WAVESEL ) + { + device->save_item(NAME(OPL->wavesel)); + } + + device->save_item(NAME(OPL->T)); + device->save_item(NAME(OPL->st)); + +#if BUILD_Y8950 + if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) + { + OPL->deltat->savestate(device); + } + + if ( OPL->type & OPL_TYPE_IO ) + { + device->save_item(NAME(OPL->portDirection)); + device->save_item(NAME(OPL->portLatch)); + } +#endif + + device->save_item(NAME(OPL->address)); + device->save_item(NAME(OPL->status)); + device->save_item(NAME(OPL->statusmask)); + device->save_item(NAME(OPL->mode)); + + device->machine().save().register_postload(save_prepost_delegate(FUNC(FM_OPL::postload), OPL)); +} + +static void OPL_clock_changed(FM_OPL *OPL, uint32_t clock, uint32_t rate) +{ + OPL->clock = clock; + OPL->rate = rate; + + /* init global tables */ + OPL->initialize(); +} + + +/* Create one of virtual YM3812/YM3526/Y8950 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static FM_OPL *OPLCreate(device_t *device, uint32_t clock, uint32_t rate, int type) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + + if (FM_OPL::LockTable(device) == -1) return nullptr; + + /* calculate OPL state size */ + state_size = sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); +#endif + + /* allocate memory block */ + ptr = (char *)auto_alloc_array_clear(device->machine(), uint8_t, state_size); + + OPL = (FM_OPL *)ptr; + + ptr += sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) + { + OPL->deltat = (YM_DELTAT *)ptr; + } + ptr += sizeof(YM_DELTAT); +#endif + + OPL->device = device; + OPL->type = type; + OPL_clock_changed(OPL, clock, rate); + + return OPL; +} + +/* Destroy one of virtual YM3812 */ +static void OPLDestroy(FM_OPL *OPL) +{ + FM_OPL::UnLockTable(); + auto_free(OPL->device->machine(), OPL); +} + +/* Optional handlers */ + +static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,device_t *device) +{ + OPL->timer_handler = timer_handler; + OPL->TimerParam = device; +} +static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = device; +} +static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = device; +} + +static int OPLWrite(FM_OPL *OPL,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + OPL->address = v & 0xff; + } + else + { /* data port */ + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + OPL->WriteReg(OPL->address,v); + } + return OPL->status>>7; +} + +static unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { + /* status port */ + + #if BUILD_Y8950 + + if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */ + { + return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1); + } + + #endif + + /* OPL and OPL2 */ + return OPL->status & (OPL->statusmask|0x80); + } + +#if BUILD_Y8950 + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); + else + OPL->device->logerror("Y8950: read unmapped KEYBOARD port\n"); + } + return 0; + + case 0x0f: /* ADPCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + uint8_t val; + + val = OPL->deltat->ADPCM_Read(); + /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ + return val; + } + return 0; + + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); + else + OPL->device->logerror("Y8950:read unmapped I/O port\n"); + } + return 0; + case 0x1a: /* PCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + OPL->device->logerror("Y8950 A/D conversion is accessed but not implemented !\n"); + return 0x80; /* 2's complement PCM data - result from A/D conversion */ + } + return 0; + } +#endif + + return 0xff; +} + +/* CSM Key Controll */ +static inline void CSMKeyControll(OPL_CH *CH) +{ + CH->SLOT[SLOT1].KEYON(4); + CH->SLOT[SLOT2].KEYON(4); + + /* The key off should happen exactly one sample later - not implemented correctly yet */ + + CH->SLOT[SLOT1].KEYOFF(~4); + CH->SLOT[SLOT2].KEYOFF(~4); +} + + +static int OPLTimerOver(FM_OPL *OPL,int c) +{ + if( c ) + { /* Timer B */ + OPL->STATUS_SET(0x20); + } + else + { /* Timer A */ + OPL->STATUS_SET(0x40); + /* CSM mode key,TL controll */ + if( OPL->mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch=0; ch<9; ch++) + CSMKeyControll( &OPL->P_CH[ch] ); + } + } + /* reload timer */ + if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]); + return OPL->status>>7; +} + + +#define MAX_OPL_CHIPS 2 + + +#if (BUILD_YM3812) + +void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate) +{ + OPL_clock_changed((FM_OPL *)chip, clock, rate); +} + +void * ym3812_init(device_t *device, uint32_t clock, uint32_t rate) +{ + /* emulator create */ + FM_OPL *YM3812 = OPLCreate(device,clock,rate,OPL_TYPE_YM3812); + if (YM3812) + { + OPL_save_state(YM3812, device); + ym3812_reset_chip(YM3812); + } + return YM3812; +} + +void ym3812_shutdown(void *chip) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + + /* emulator shutdown */ + OPLDestroy(YM3812); +} +void ym3812_reset_chip(void *chip) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + YM3812->ResetChip(); +} + +int ym3812_write(void *chip, int a, int v) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + return OPLWrite(YM3812, a, v); +} + +unsigned char ym3812_read(void *chip, int a) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + /* YM3812 always returns bit2 and bit1 in HIGH state */ + return OPLRead(YM3812, a) | 0x06 ; +} +int ym3812_timer_over(void *chip, int c) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + return OPLTimerOver(YM3812, c); +} + +void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetTimerHandler(YM3812, timer_handler, device); +} +void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetIRQHandler(YM3812, IRQHandler, device); +} +void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetUpdateHandler(YM3812, UpdateHandler, device); +} + + +/* +** Generate samples for one of the YM3812's +** +** 'which' is the virtual YM3812 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length) +{ + FM_OPL *OPL = (FM_OPL *)chip; + uint8_t rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + + OPL->advance_lfo(); + + /* FM part */ + OPL->CALC_CH(OPL->P_CH[0]); + OPL->CALC_CH(OPL->P_CH[1]); + OPL->CALC_CH(OPL->P_CH[2]); + OPL->CALC_CH(OPL->P_CH[3]); + OPL->CALC_CH(OPL->P_CH[4]); + OPL->CALC_CH(OPL->P_CH[5]); + + if(!rhythm) + { + OPL->CALC_CH(OPL->P_CH[6]); + OPL->CALC_CH(OPL->P_CH[7]); + OPL->CALC_CH(OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL->CALC_RH(); + } + + lt = OPL->output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + OPL->advance(); + } + +} +#endif /* BUILD_YM3812 */ + + + +#if (BUILD_YM3526) + +void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate) +{ + OPL_clock_changed((FM_OPL *)chip, clock, rate); +} + +void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate) +{ + /* emulator create */ + FM_OPL *YM3526 = OPLCreate(device,clock,rate,OPL_TYPE_YM3526); + if (YM3526) + { + OPL_save_state(YM3526, device); + ym3526_reset_chip(YM3526); + } + return YM3526; +} + +void ym3526_shutdown(void *chip) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + /* emulator shutdown */ + OPLDestroy(YM3526); +} +void ym3526_reset_chip(void *chip) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + YM3526->ResetChip(); +} + +int ym3526_write(void *chip, int a, int v) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + return OPLWrite(YM3526, a, v); +} + +unsigned char ym3526_read(void *chip, int a) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + /* YM3526 always returns bit2 and bit1 in HIGH state */ + return OPLRead(YM3526, a) | 0x06 ; +} +int ym3526_timer_over(void *chip, int c) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + return OPLTimerOver(YM3526, c); +} + +void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetTimerHandler(YM3526, timer_handler, device); +} +void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetIRQHandler(YM3526, IRQHandler, device); +} +void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetUpdateHandler(YM3526, UpdateHandler, device); +} + + +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length) +{ + FM_OPL *OPL = (FM_OPL *)chip; + uint8_t rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + + OPL->advance_lfo(); + + /* FM part */ + OPL->CALC_CH(OPL->P_CH[0]); + OPL->CALC_CH(OPL->P_CH[1]); + OPL->CALC_CH(OPL->P_CH[2]); + OPL->CALC_CH(OPL->P_CH[3]); + OPL->CALC_CH(OPL->P_CH[4]); + OPL->CALC_CH(OPL->P_CH[5]); + + if(!rhythm) + { + OPL->CALC_CH(OPL->P_CH[6]); + OPL->CALC_CH(OPL->P_CH[7]); + OPL->CALC_CH(OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL->CALC_RH(); + } + + lt = OPL->output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + OPL->advance(); + } + +} +#endif /* BUILD_YM3526 */ + + + + +#if BUILD_Y8950 + +static void Y8950_deltat_status_set(void *chip, uint8_t changebits) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + Y8950->STATUS_SET(changebits); +} +static void Y8950_deltat_status_reset(void *chip, uint8_t changebits) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + Y8950->STATUS_RESET(changebits); +} + +void *y8950_init(device_t *device, uint32_t clock, uint32_t rate) +{ + /* emulator create */ + FM_OPL *Y8950 = OPLCreate(device,clock,rate,OPL_TYPE_Y8950); + if (Y8950) + { + Y8950->deltat->status_set_handler = Y8950_deltat_status_set; + Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset; + Y8950->deltat->status_change_which_chip = Y8950; + Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ + Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ + + /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ + /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ + /* reset */ + OPL_save_state(Y8950, device); + y8950_reset_chip(Y8950); + } + + return Y8950; +} + +void y8950_shutdown(void *chip) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + /* emulator shutdown */ + OPLDestroy(Y8950); +} +void y8950_reset_chip(void *chip) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + Y8950->ResetChip(); +} + +int y8950_write(void *chip, int a, int v) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLWrite(Y8950, a, v); +} + +unsigned char y8950_read(void *chip, int a) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLRead(Y8950, a); +} +int y8950_timer_over(void *chip, int c) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLTimerOver(Y8950, c); +} + +void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, device_t *device) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetTimerHandler(Y8950, timer_handler, device); +} +void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,device_t *device) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetIRQHandler(Y8950, IRQHandler, device); +} +void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device_t *device) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetUpdateHandler(Y8950, UpdateHandler, device); +} + +void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->deltat->memory = (uint8_t *)(deltat_mem_ptr); + OPL->deltat->memory_size = deltat_mem_size; +} + +/* +** Generate samples for one of the Y8950's +** +** 'which' is the virtual Y8950 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length) +{ + int i; + FM_OPL *OPL = (FM_OPL *)chip; + uint8_t rhythm = OPL->rhythm&0x20; + YM_DELTAT *DELTAT = OPL->deltat; + OPLSAMPLE *buf = buffer; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + OPL->output_deltat[0] = 0; + + OPL->advance_lfo(); + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 ) + DELTAT->ADPCM_CALC(); + + /* FM part */ + OPL->CALC_CH(OPL->P_CH[0]); + OPL->CALC_CH(OPL->P_CH[1]); + OPL->CALC_CH(OPL->P_CH[2]); + OPL->CALC_CH(OPL->P_CH[3]); + OPL->CALC_CH(OPL->P_CH[4]); + OPL->CALC_CH(OPL->P_CH[5]); + + if(!rhythm) + { + OPL->CALC_CH(OPL->P_CH[6]); + OPL->CALC_CH(OPL->P_CH[7]); + OPL->CALC_CH(OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL->CALC_RH(); + } + + lt = OPL->output[0] + (OPL->output_deltat[0]>>11); + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + buf[i] = lt; + + OPL->advance(); + } + +} + +void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,device_t *device) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->porthandler_w = PortHandler_w; + OPL->porthandler_r = PortHandler_r; + OPL->port_param = device; +} + +void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,device_t *device) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->keyboardhandler_w = KeyboardHandler_w; + OPL->keyboardhandler_r = KeyboardHandler_r; + OPL->keyboard_param = device; +} + +#endif diff --git a/src/hardware/mame/fmopl.h b/src/hardware/mame/fmopl.h new file mode 100644 index 00000000..c5366d7b --- /dev/null +++ b/src/hardware/mame/fmopl.h @@ -0,0 +1,109 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh +#ifndef MAME_SOUND_FMOPL_H +#define MAME_SOUND_FMOPL_H + +#pragma once + +#include + + +/* --- select emulation chips --- */ +#define BUILD_YM3812 (1) +#define BUILD_YM3526 (1) +#define BUILD_Y8950 (1) + +/* select output bits size of output : 8 or 16 */ +#define OPL_SAMPLE_BITS 16 + +typedef stream_sample_t OPLSAMPLE; +/* +#if (OPL_SAMPLE_BITS==16) +typedef int16_t OPLSAMPLE; +#endif +#if (OPL_SAMPLE_BITS==8) +typedef int8_t OPLSAMPLE; +#endif +*/ + +typedef void (*OPL_TIMERHANDLER)(device_t *device,int timer,const attotime &period); +typedef void (*OPL_IRQHANDLER)(device_t *device,int irq); +typedef void (*OPL_UPDATEHANDLER)(device_t *device,int min_interval_us); +typedef void (*OPL_PORTHANDLER_W)(device_t *device,unsigned char data); +typedef unsigned char (*OPL_PORTHANDLER_R)(device_t *device); + + +#if BUILD_YM3812 + +void *ym3812_init(device_t *device, uint32_t clock, uint32_t rate); +void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate); +void ym3812_shutdown(void *chip); +void ym3812_reset_chip(void *chip); +int ym3812_write(void *chip, int a, int v); +unsigned char ym3812_read(void *chip, int a); +int ym3812_timer_over(void *chip, int c); +void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length); + +void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); +void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); +void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); + +#endif /* BUILD_YM3812 */ + + +#if BUILD_YM3526 + +/* +** Initialize YM3526 emulator(s). +** +** 'num' is the number of virtual YM3526's to allocate +** 'clock' is the chip clock in Hz +** 'rate' is sampling rate +*/ +void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate); +void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate); +/* shutdown the YM3526 emulators*/ +void ym3526_shutdown(void *chip); +void ym3526_reset_chip(void *chip); +int ym3526_write(void *chip, int a, int v); +unsigned char ym3526_read(void *chip, int a); +int ym3526_timer_over(void *chip, int c); +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length); + +void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); +void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); +void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); + +#endif /* BUILD_YM3526 */ + + +#if BUILD_Y8950 + +/* Y8950 port handlers */ +void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, device_t *device); +void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, device_t *device); +void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ); + +void * y8950_init(device_t *device, uint32_t clock, uint32_t rate); +void y8950_shutdown(void *chip); +void y8950_reset_chip(void *chip); +int y8950_write(void *chip, int a, int v); +unsigned char y8950_read (void *chip, int a); +int y8950_timer_over(void *chip, int c); +void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length); + +void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device); +void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device); +void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device); + +#endif /* BUILD_Y8950 */ + + +#endif // MAME_SOUND_FMOPL_H diff --git a/src/hardware/mame/saa1099.cpp b/src/hardware/mame/saa1099.cpp new file mode 100644 index 00000000..3c9b334b --- /dev/null +++ b/src/hardware/mame/saa1099.cpp @@ -0,0 +1,458 @@ +// license:BSD-3-Clause +// copyright-holders:Juergen Buchmueller, Manuel Abadia +/*************************************************************************** + + Philips SAA1099 Sound driver + + By Juergen Buchmueller and Manuel Abadia + + SAA1099 register layout: + ======================== + + offs | 7654 3210 | description + -----+-----------+--------------------------- + 0x00 | ---- xxxx | Amplitude channel 0 (left) + 0x00 | xxxx ---- | Amplitude channel 0 (right) + 0x01 | ---- xxxx | Amplitude channel 1 (left) + 0x01 | xxxx ---- | Amplitude channel 1 (right) + 0x02 | ---- xxxx | Amplitude channel 2 (left) + 0x02 | xxxx ---- | Amplitude channel 2 (right) + 0x03 | ---- xxxx | Amplitude channel 3 (left) + 0x03 | xxxx ---- | Amplitude channel 3 (right) + 0x04 | ---- xxxx | Amplitude channel 4 (left) + 0x04 | xxxx ---- | Amplitude channel 4 (right) + 0x05 | ---- xxxx | Amplitude channel 5 (left) + 0x05 | xxxx ---- | Amplitude channel 5 (right) + | | + 0x08 | xxxx xxxx | Frequency channel 0 + 0x09 | xxxx xxxx | Frequency channel 1 + 0x0a | xxxx xxxx | Frequency channel 2 + 0x0b | xxxx xxxx | Frequency channel 3 + 0x0c | xxxx xxxx | Frequency channel 4 + 0x0d | xxxx xxxx | Frequency channel 5 + | | + 0x10 | ---- -xxx | Channel 0 octave select + 0x10 | -xxx ---- | Channel 1 octave select + 0x11 | ---- -xxx | Channel 2 octave select + 0x11 | -xxx ---- | Channel 3 octave select + 0x12 | ---- -xxx | Channel 4 octave select + 0x12 | -xxx ---- | Channel 5 octave select + | | + 0x14 | ---- ---x | Channel 0 frequency enable (0 = off, 1 = on) + 0x14 | ---- --x- | Channel 1 frequency enable (0 = off, 1 = on) + 0x14 | ---- -x-- | Channel 2 frequency enable (0 = off, 1 = on) + 0x14 | ---- x--- | Channel 3 frequency enable (0 = off, 1 = on) + 0x14 | ---x ---- | Channel 4 frequency enable (0 = off, 1 = on) + 0x14 | --x- ---- | Channel 5 frequency enable (0 = off, 1 = on) + | | + 0x15 | ---- ---x | Channel 0 noise enable (0 = off, 1 = on) + 0x15 | ---- --x- | Channel 1 noise enable (0 = off, 1 = on) + 0x15 | ---- -x-- | Channel 2 noise enable (0 = off, 1 = on) + 0x15 | ---- x--- | Channel 3 noise enable (0 = off, 1 = on) + 0x15 | ---x ---- | Channel 4 noise enable (0 = off, 1 = on) + 0x15 | --x- ---- | Channel 5 noise enable (0 = off, 1 = on) + | | + 0x16 | ---- --xx | Noise generator parameters 0 + 0x16 | --xx ---- | Noise generator parameters 1 + | | + 0x18 | --xx xxxx | Envelope generator 0 parameters + 0x18 | x--- ---- | Envelope generator 0 control enable (0 = off, 1 = on) + 0x19 | --xx xxxx | Envelope generator 1 parameters + 0x19 | x--- ---- | Envelope generator 1 control enable (0 = off, 1 = on) + | | + 0x1c | ---- ---x | All channels enable (0 = off, 1 = on) + 0x1c | ---- --x- | Synch & Reset generators + + Unspecified bits should be written as zero. + +***************************************************************************/ + +#include "emu.h" +#include "saa1099.h" + +#define LEFT 0x00 +#define RIGHT 0x01 + +static constexpr int amplitude_lookup[16] = { + 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, + 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, + 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, + 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 +}; + +static constexpr uint8_t envelope[8][64] = { + /* zero amplitude */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* maximum amplitude */ + {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, + /* single decay */ + {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive decay */ + {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + /* single triangular */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive triangular */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + /* single attack */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive attack */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } +}; + + +// device type definition +DEFINE_DEVICE_TYPE(SAA1099, saa1099_device, "saa1099", "Philips SAA1099") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// saa1099_device - constructor +//------------------------------------------------- + +saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SAA1099, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_stream(nullptr) + , m_noise_params{ 0, 0 } + , m_env_enable{ 0, 0 } + , m_env_reverse_right{ 0, 0 } + , m_env_mode{ 0, 0 } + , m_env_bits{ 0, 0 } + , m_env_clock{ 0, 0 } + , m_env_step{ 0, 0 } + , m_all_ch_enable(0) + , m_sync_state(0) + , m_selected_reg(0) + , m_sample_rate(0.0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void saa1099_device::device_start() +{ + /* copy global parameters */ + m_master_clock = clock(); + m_sample_rate = clock() / 256; + + /* for each chip allocate one stream */ + m_stream = stream_alloc(0, 2, m_sample_rate); + + save_item(NAME(m_noise_params)); + save_item(NAME(m_env_enable)); + save_item(NAME(m_env_reverse_right)); + save_item(NAME(m_env_mode)); + save_item(NAME(m_env_bits)); + save_item(NAME(m_env_clock)); + save_item(NAME(m_env_step)); + save_item(NAME(m_all_ch_enable)); + save_item(NAME(m_sync_state)); + save_item(NAME(m_selected_reg)); + + for (int i = 0; i < 6; i++) + { + save_item(NAME(m_channels[i].frequency), i); + save_item(NAME(m_channels[i].freq_enable), i); + save_item(NAME(m_channels[i].noise_enable), i); + save_item(NAME(m_channels[i].octave), i); + save_item(NAME(m_channels[i].amplitude), i); + save_item(NAME(m_channels[i].envelope), i); + save_item(NAME(m_channels[i].counter), i); + save_item(NAME(m_channels[i].freq), i); + save_item(NAME(m_channels[i].level), i); + } + + for (int i = 0; i < 2; i++) + { + save_item(NAME(m_noise[i].counter), i); + save_item(NAME(m_noise[i].freq), i); + save_item(NAME(m_noise[i].level), i); + } +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void saa1099_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + int j, ch; + /* if the channels are disabled we're done */ + if (!m_all_ch_enable) + { + /* init output data */ + memset(outputs[LEFT],0,samples*sizeof(*outputs[LEFT])); + memset(outputs[RIGHT],0,samples*sizeof(*outputs[RIGHT])); + return; + } + + for (ch = 0; ch < 2; ch++) + { + switch (m_noise_params[ch]) + { + case 0: m_noise[ch].freq = m_master_clock/256.0 * 2; break; + case 1: m_noise[ch].freq = m_master_clock/512.0 * 2; break; + case 2: m_noise[ch].freq = m_master_clock/1024.0 * 2; break; + case 3: m_noise[ch].freq = m_channels[ch * 3].freq; break; // todo: this case will be m_master_clock/[ch*3's octave divisor, 0 is = 256*2, higher numbers are higher] * 2 if the tone generator phase reset bit (0x1c bit 1) is set. + } + } + + /* fill all data needed */ + for( j = 0; j < samples; j++ ) + { + int output_l = 0, output_r = 0; + + /* for each channel */ + for (ch = 0; ch < 6; ch++) + { + if (m_channels[ch].freq == 0.0) + m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) / + (511.0 - (double)m_channels[ch].frequency); + + /* check the actual position in the square wave */ + m_channels[ch].counter -= m_channels[ch].freq; + while (m_channels[ch].counter < 0) + { + /* calculate new frequency now after the half wave is updated */ + m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) / + (511.0 - (double)m_channels[ch].frequency); + + m_channels[ch].counter += m_sample_rate; + m_channels[ch].level ^= 1; + + /* eventually clock the envelope counters */ + if (ch == 1 && m_env_clock[0] == 0) + envelope_w(0); + if (ch == 4 && m_env_clock[1] == 0) + envelope_w(1); + } + + // if the noise is enabled + if (m_channels[ch].noise_enable) + { + // if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) + if (m_noise[ch/3].level & 1) + { + // subtract to avoid overflows, also use only half amplitude + output_l -= m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16 / 2; + output_r -= m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16 / 2; + } + } + // if the square wave is enabled + if (m_channels[ch].freq_enable) + { + // if the channel level is high + if (m_channels[ch].level & 1) + { + output_l += m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16; + output_r += m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16; + } + } + } + + for (ch = 0; ch < 2; ch++) + { + /* update the state of the noise generator + * polynomial is x^18 + x^11 + x (i.e. 0x20400) and is a plain XOR, initial state is probably all 1s + * see http://www.vogons.org/viewtopic.php?f=9&t=51695 */ + m_noise[ch].counter -= m_noise[ch].freq; + while (m_noise[ch].counter < 0) + { + m_noise[ch].counter += m_sample_rate; + if( ((m_noise[ch].level & 0x20000) == 0) != ((m_noise[ch].level & 0x0400) == 0) ) + m_noise[ch].level = (m_noise[ch].level << 1) | 1; + else + m_noise[ch].level <<= 1; + } + } + /* write sound data to the buffer */ + outputs[LEFT][j] = output_l / 6; + outputs[RIGHT][j] = output_r / 6; + } +} + + +void saa1099_device::envelope_w(int ch) +{ + if (m_env_enable[ch]) + { + int step, mode, mask; + mode = m_env_mode[ch]; + /* step from 0..63 and then loop in steps 32..63 */ + step = m_env_step[ch] = + ((m_env_step[ch] + 1) & 0x3f) | (m_env_step[ch] & 0x20); + + mask = 15; + if (m_env_bits[ch]) + mask &= ~1; /* 3 bit resolution, mask LSB */ + + m_channels[ch*3+0].envelope[ LEFT] = + m_channels[ch*3+1].envelope[ LEFT] = + m_channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask; + if (m_env_reverse_right[ch] & 0x01) + { + m_channels[ch*3+0].envelope[RIGHT] = + m_channels[ch*3+1].envelope[RIGHT] = + m_channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask; + } + else + { + m_channels[ch*3+0].envelope[RIGHT] = + m_channels[ch*3+1].envelope[RIGHT] = + m_channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask; + } + } + else + { + /* envelope mode off, set all envelope factors to 16 */ + m_channels[ch*3+0].envelope[ LEFT] = + m_channels[ch*3+1].envelope[ LEFT] = + m_channels[ch*3+2].envelope[ LEFT] = + m_channels[ch*3+0].envelope[RIGHT] = + m_channels[ch*3+1].envelope[RIGHT] = + m_channels[ch*3+2].envelope[RIGHT] = 16; + } +} + + +WRITE8_MEMBER( saa1099_device::control_w ) +{ + if ((data & 0xff) > 0x1c) + { + /* Error! */ + logerror("%s: (SAA1099 '%s') Unknown register selected\n", machine().describe_context(), tag()); + } + + m_selected_reg = data & 0x1f; + if (m_selected_reg == 0x18 || m_selected_reg == 0x19) + { + /* clock the envelope channels */ + if (m_env_clock[0]) + envelope_w(0); + if (m_env_clock[1]) + envelope_w(1); + } +} + + +WRITE8_MEMBER( saa1099_device::data_w ) +{ + int reg = m_selected_reg; + int ch; + + /* first update the stream to this point in time */ + m_stream->update(); + + switch (reg) + { + /* channel i amplitude */ + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + ch = reg & 7; + m_channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; + m_channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; + break; + /* channel i frequency */ + case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: + ch = reg & 7; + m_channels[ch].frequency = data & 0xff; + break; + /* channel i octave */ + case 0x10: case 0x11: case 0x12: + ch = (reg - 0x10) << 1; + m_channels[ch + 0].octave = data & 0x07; + m_channels[ch + 1].octave = (data >> 4) & 0x07; + break; + /* channel i frequency enable */ + case 0x14: + m_channels[0].freq_enable = data & 0x01; + m_channels[1].freq_enable = data & 0x02; + m_channels[2].freq_enable = data & 0x04; + m_channels[3].freq_enable = data & 0x08; + m_channels[4].freq_enable = data & 0x10; + m_channels[5].freq_enable = data & 0x20; + break; + /* channel i noise enable */ + case 0x15: + m_channels[0].noise_enable = data & 0x01; + m_channels[1].noise_enable = data & 0x02; + m_channels[2].noise_enable = data & 0x04; + m_channels[3].noise_enable = data & 0x08; + m_channels[4].noise_enable = data & 0x10; + m_channels[5].noise_enable = data & 0x20; + break; + /* noise generators parameters */ + case 0x16: + m_noise_params[0] = data & 0x03; + m_noise_params[1] = (data >> 4) & 0x03; + break; + /* envelope generators parameters */ + case 0x18: case 0x19: + ch = reg - 0x18; + m_env_reverse_right[ch] = data & 0x01; + m_env_mode[ch] = (data >> 1) & 0x07; + m_env_bits[ch] = data & 0x10; + m_env_clock[ch] = data & 0x20; + m_env_enable[ch] = data & 0x80; + /* reset the envelope */ + m_env_step[ch] = 0; + break; + /* channels enable & reset generators */ + case 0x1c: + m_all_ch_enable = data & 0x01; + m_sync_state = data & 0x02; + if (data & 0x02) + { + int i; + + /* Synch & Reset generators */ + logerror("%s: (SAA1099 '%s') -reg 0x1c- Chip reset\n", machine().describe_context(), tag()); + for (i = 0; i < 6; i++) + { + m_channels[i].level = 0; + m_channels[i].counter = 0.0; + } + } + break; + default: /* Error! */ + if (data != 0) + logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n", machine().describe_context(), tag(), reg, data); + } +} + +WRITE8_MEMBER(saa1099_device::write) +{ + if (offset & 1) + control_w(space, 0, data); + else + data_w(space, 0, data); +} diff --git a/src/hardware/mame/saa1099.h b/src/hardware/mame/saa1099.h new file mode 100644 index 00000000..061f39ec --- /dev/null +++ b/src/hardware/mame/saa1099.h @@ -0,0 +1,96 @@ +// license:BSD-3-Clause +// copyright-holders:Juergen Buchmueller, Manuel Abadia +/********************************************** + Philips SAA1099 Sound driver +**********************************************/ + +#ifndef MAME_SOUND_SAA1099_H +#define MAME_SOUND_SAA1099_H + +#pragma once + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_SAA1099_ADD(_tag, _clock) \ + MCFG_DEVICE_ADD(_tag, SAA1099, _clock) +#define MCFG_SAA1099_REPLACE(_tag, _clock) \ + MCFG_DEVICE_REPLACE(_tag, SAA1099, _clock) + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// ======================> saa1099_device + +class saa1099_device : public device_t, + public device_sound_interface +{ +public: + saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_WRITE8_MEMBER( control_w ); + DECLARE_WRITE8_MEMBER( data_w ); + + DECLARE_WRITE8_MEMBER( write ); + +protected: + // device-level overrides + virtual void device_start() override; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + +private: + struct saa1099_channel + { + saa1099_channel() : amplitude{ 0, 0 }, envelope{ 0, 0 } { } + + int frequency = 0; /* frequency (0x00..0xff) */ + int freq_enable = 0; /* frequency enable */ + int noise_enable = 0; /* noise enable */ + int octave = 0; /* octave (0x00..0x07) */ + int amplitude[2]; /* amplitude (0x00..0x0f) */ + int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ + + /* vars to simulate the square wave */ + double counter = 0.0; + double freq = 0.0; + int level = 0; + }; + + struct saa1099_noise + { + saa1099_noise() { } + + /* vars to simulate the noise generator output */ + double counter = 0.0; + double freq = 0.0; + uint32_t level = 0xFFFFFFFF; /* noise polynomial shifter */ + }; + + void envelope_w(int ch); + + sound_stream *m_stream; /* our stream */ + int m_noise_params[2]; /* noise generators parameters */ + int m_env_enable[2]; /* envelope generators enable */ + int m_env_reverse_right[2]; /* envelope reversed for right channel */ + int m_env_mode[2]; /* envelope generators mode */ + int m_env_bits[2]; /* non zero = 3 bits resolution */ + int m_env_clock[2]; /* envelope clock mode (non-zero external) */ + int m_env_step[2]; /* current envelope step */ + int m_all_ch_enable; /* all channels enable */ + int m_sync_state; /* sync all channels */ + int m_selected_reg; /* selected register */ + saa1099_channel m_channels[6]; /* channels */ + saa1099_noise m_noise[2]; /* noise generators */ + double m_sample_rate; + int m_master_clock; +}; + +DECLARE_DEVICE_TYPE(SAA1099, saa1099_device) + +#endif // MAME_SOUND_SAA1099_H diff --git a/src/hardware/mame/sn76496.cpp b/src/hardware/mame/sn76496.cpp new file mode 100644 index 00000000..bccb53c2 --- /dev/null +++ b/src/hardware/mame/sn76496.cpp @@ -0,0 +1,474 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria +/*************************************************************************** + + sn76496.c + by Nicola Salmoria + with contributions by others + + Routines to emulate the: + Texas Instruments SN76489, SN76489A, SN76494/SN76496 + ( Also known as, or at least compatible with, the TMS9919 and SN94624.) + and the Sega 'PSG' used on the Master System, Game Gear, and Megadrive/Genesis + This chip is known as the Programmable Sound Generator, or PSG, and is a 4 + channel sound generator, with three squarewave channels and a noise/arbitrary + duty cycle channel. + + Noise emulation for all verified chips should be accurate: + + ** SN76489 uses a 15-bit shift register with taps on bits D and E, output on E, + XOR function. + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is inverted. + ** SN94624 is the same as SN76489 but lacks the /8 divider on its clock input. + ** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F, + XOR function. + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is not inverted. + ** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input. + ** SN76496 is identical in operation to the SN76489A, but the audio input on pin 9 is + documented. + All the TI-made PSG chips have an audio input line which is mixed with the 4 channels + of output. (It is undocumented and may not function properly on the sn76489, 76489a + and 76494; the sn76489a input is mentioned in datasheets for the tms5200) + All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is + written to the frequency register. + ** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps + on bits C and F, output on F + It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle. + (whether it uses an XOR or XNOR needs to be verified, assumed XOR) + (whether output is inverted or not needs to be verified, assumed to be inverted) + ** Sega Game Gear PSG is identical to the SMS3/MD/Genesis one except it has an + extra register for mapping which channels go to which speaker. + The register, connected to a z80 port, means: + for bits 7 6 5 4 3 2 1 0 + L3 L2 L1 L0 R3 R2 R1 R0 + Noise is an XOR function, and audio output is negated before being output. + All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written + to the frequency register. + ** NCR7496 (as used on the Tandy 1000) is similar to the SN76489 but with a + different noise LFSR patttern: taps on bits A and E, output on E + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + (all this chip's info needs to be verified) + + 28/03/2005 : Sebastien Chevalier + Update th SN76496Write func, according to SN76489 doc found on SMSPower. + - On write with 0x80 set to 0, when LastRegister is other then TONE, + the function is similar than update with 0x80 set to 1 + + 23/04/2007 : Lord Nightmare + Major update, implement all three different noise generation algorithms and a + set_variant call to discern among them. + + 28/04/2009 : Lord Nightmare + Add READY line readback; cleaned up struct a bit. Cleaned up comments. + Add more TODOs. Fixed some unsaved savestate related stuff. + + 04/11/2009 : Lord Nightmare + Changed the way that the invert works (it now selects between XOR and XNOR + for the taps), and added R->OldNoise to simulate the extra 0 that is always + output before the noise LFSR contents are after an LFSR reset. + This fixes SN76489/A to match chips. Added SN94624. + + 14/11/2009 : Lord Nightmare + Removed STEP mess, vastly simplifying the code. Made output bipolar rather + than always above the 0 line, but disabled that code due to pending issues. + + 16/11/2009 : Lord Nightmare + Fix screeching in regulus: When summing together four equal channels, the + size of the max amplitude per channel should be 1/4 of the max range, not + 1/3. Added NCR7496. + + 18/11/2009 : Lord Nightmare + Modify Init functions to support negating the audio output. The gamegear + psg does this. Change gamegear and sega psgs to use XOR rather than XNOR + based on testing. Got rid of R->OldNoise and fixed taps accordingly. + Added stereo support for game gear. + + 15/01/2010 : Lord Nightmare + Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods. + Note that properly emulating the noise cycle bit timing accurately may require + extensive rewriting. + + 24/01/2010: Lord Nightmare + Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively. + Thanks to PlgDavid for providing samples which helped immensely here. + Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than + the others, as in real life. + + 15/02/2010: Lord Nightmare & Michael Zapf (additional testing by PlgDavid) + Fix noise period when set to mirror channel 3 and channel 3 period is set to 0 (tested on hardware for noise, wave needs tests) - MZ + Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid + Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here. + + 23/02/2011: Lord Nightmare & Enik + Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the + frequency register, while the others have 0x400 as before. Should fix a bug + or two on sega games, particularly Vigilante on Sega Master System. Verified + on SMS hardware. + + 27/06/2012: Michael Zapf + Converted to modern device, legacy devices were gradually removed afterwards. + + 16/09/2015: Lord Nightmare + Fix PSG chips to have volume reg inited on reset to 0x0 based on tests by + ValleyBell. Made Sega PSG chips start up with register 0x3 selected (volume + for channel 2) based on hardware tests by Nemesis. + + TODO: * Implement the TMS9919 - any difference to sn94624? + * Implement the T6W28; has registers in a weird order, needs writes + to be 'sanitized' first. Also is stereo, similar to game gear. + * Test the NCR7496; Smspower says the whitenoise taps are A and E, + but this needs verification on real hardware. + * Factor out common code so that the SAA1099 can share some code. + +***************************************************************************/ + +#include "emu.h" +#include "sn76496.h" + +#define MAX_OUTPUT 0x7fff + + +sn76496_base_device::sn76496_base_device( + const machine_config &mconfig, + device_type type, + const char *tag, + int feedbackmask, + int noisetap1, + int noisetap2, + bool negate, + bool stereo, + int clockdivider, + bool sega, + device_t *owner, + uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_ready_handler(*this) + , m_feedback_mask(feedbackmask) + , m_whitenoise_tap1(noisetap1) + , m_whitenoise_tap2(noisetap2) + , m_negate(negate) + , m_stereo(stereo) + , m_clock_divider(clockdivider) + , m_sega_style_psg(sega) +{ +} + +sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock) +{ +} + +u8106_device::u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, U8106, tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock) +{ +} + +y2404_device::y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, Y2404, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock) +{ +} + +sn76489_device::sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76489, tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock) +{ +} + +sn76489a_device::sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76489A, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock) +{ +} + +sn76494_device::sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN76494, tag, 0x10000, 0x04, 0x08, false, false, 1, true, owner, clock) +{ +} + +sn94624_device::sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SN94624, tag, 0x4000, 0x01, 0x02, true, false, 1, true, owner, clock) +{ +} + +ncr7496_device::ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, NCR7496, tag, 0x8000, 0x02, 0x20, false, false, 8, true, owner, clock) +{ +} + +gamegear_device::gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, GAMEGEAR, tag, 0x8000, 0x01, 0x08, true, true, 8, false, owner, clock) +{ +} + +segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sn76496_base_device(mconfig, SEGAPSG, tag, 0x8000, 0x01, 0x08, true, false, 8, false, owner, clock) +{ +} + + +void sn76496_base_device::device_start() +{ + int sample_rate = clock()/2; + int i; + double out; + int gain; + + m_ready_handler.resolve_safe(); + + m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate); + + for (i = 0; i < 4; i++) m_volume[i] = 0; + + m_last_register = m_sega_style_psg?3:0; // Sega VDP PSG defaults to selected period reg for 2nd channel + for (i = 0; i < 8; i+=2) + { + m_register[i] = 0; + m_register[i + 1] = 0x0; // volume = 0x0 (max volume) on reset; this needs testing on chips other than SN76489A and Sega VDP PSG + } + + for (i = 0; i < 4; i++) + { + m_output[i] = 0; + m_period[i] = 0; + m_count[i] = 0; + } + + m_RNG = m_feedback_mask; + m_output[3] = m_RNG & 1; + + m_cycles_to_ready = 1; // assume ready is not active immediately on init. is this correct? + m_stereo_mask = 0xFF; // all channels enabled + m_current_clock = m_clock_divider-1; + + // set gain + gain = 0; + + gain &= 0xff; + + // increase max output basing on gain (0.2 dB per step) + out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range + while (gain-- > 0) + out *= 1.023292992; // = (10 ^ (0.2/20)) + + // build volume table (2dB per step) + for (i = 0; i < 15; i++) + { + // limit volume to avoid clipping + if (out > MAX_OUTPUT / 4) m_vol_table[i] = MAX_OUTPUT / 4; + else m_vol_table[i] = out; + + out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ + } + m_vol_table[15] = 0; + + m_ready_state = true; + + register_for_save_states(); +} + +WRITE8_MEMBER( sn76496_base_device::stereo_w ) +{ + m_sound->update(); + if (m_stereo) m_stereo_mask = data; + else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n"); +} + +void sn76496_base_device::write(uint8_t data) +{ + int n, r, c; + + // update the output buffer before changing the registers + m_sound->update(); + + // set number of cycles until READY is active; this is always one + // 'sample', i.e. it equals the clock divider exactly + m_cycles_to_ready = 1; + + if (data & 0x80) + { + r = (data & 0x70) >> 4; + m_last_register = r; + m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); + } + else + { + r = m_last_register; + } + + c = r >> 1; + switch (r) + { + case 0: // tone 0: frequency + case 2: // tone 1: frequency + case 4: // tone 2: frequency + if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x0f) | ((data & 0x3f) << 4); + if ((m_register[r] != 0) || (!m_sega_style_psg)) m_period[c] = m_register[r]; + else m_period[c] = 0x400; + + if (r == 4) + { + // update noise shift frequency + if ((m_register[6] & 0x03) == 0x03) m_period[3] = m_period[2]<<1; + } + break; + case 1: // tone 0: volume + case 3: // tone 1: volume + case 5: // tone 2: volume + case 7: // noise: volume + m_volume[c] = m_vol_table[data & 0x0f]; + if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); + break; + case 6: // noise: frequency, mode + { + if ((data & 0x80) == 0) logerror("sn76496_base_device: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", m_register[6], data); + if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); + n = m_register[6]; + // N/512,N/1024,N/2048,Tone #3 output + m_period[3] = ((n&3) == 3)? (m_period[2]<<1) : (1 << (5+(n&3))); + m_RNG = m_feedback_mask; + } + break; + } +} + +WRITE8_MEMBER( sn76496_base_device::write ) +{ + write(data); +} + +inline bool sn76496_base_device::in_noise_mode() +{ + return ((m_register[6] & 4)!=0); +} + +void sn76496_base_device::countdown_cycles() +{ + if (m_cycles_to_ready > 0) + { + m_cycles_to_ready--; + if (m_ready_state==true) m_ready_handler(CLEAR_LINE); + m_ready_state = false; + } + else + { + if (m_ready_state==false) m_ready_handler(ASSERT_LINE); + m_ready_state = true; + } +} + +void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + int i; + stream_sample_t *lbuffer = outputs[0]; + stream_sample_t *rbuffer = (m_stereo)? outputs[1] : nullptr; + + int16_t out; + int16_t out2 = 0; + + while (samples > 0) + { + // clock chip once + if (m_current_clock > 0) // not ready for new divided clock + { + m_current_clock--; + } + else // ready for new divided clock, make a new sample + { + m_current_clock = m_clock_divider-1; + // decrement Cycles to READY by one + countdown_cycles(); + + // handle channels 0,1,2 + for (i = 0; i < 3; i++) + { + m_count[i]--; + if (m_count[i] <= 0) + { + m_output[i] ^= 1; + m_count[i] = m_period[i]; + } + } + + // handle channel 3 + m_count[3]--; + if (m_count[3] <= 0) + { + // if noisemode is 1, both taps are enabled + // if noisemode is 0, the lower tap, whitenoisetap2, is held at 0 + // The != was a bit-XOR (^) before + if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=0) && in_noise_mode())) + { + m_RNG >>= 1; + m_RNG |= m_feedback_mask; + } + else + { + m_RNG >>= 1; + } + m_output[3] = m_RNG & 1; + + m_count[3] = m_period[3]; + } + } + + if (m_stereo) + { + out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0) + + ((((m_stereo_mask & 0x20)!=0) && (m_output[1]!=0))? m_volume[1] : 0) + + ((((m_stereo_mask & 0x40)!=0) && (m_output[2]!=0))? m_volume[2] : 0) + + ((((m_stereo_mask & 0x80)!=0) && (m_output[3]!=0))? m_volume[3] : 0); + + out2= ((((m_stereo_mask & 0x1)!=0) && (m_output[0]!=0))? m_volume[0] : 0) + + ((((m_stereo_mask & 0x2)!=0) && (m_output[1]!=0))? m_volume[1] : 0) + + ((((m_stereo_mask & 0x4)!=0) && (m_output[2]!=0))? m_volume[2] : 0) + + ((((m_stereo_mask & 0x8)!=0) && (m_output[3]!=0))? m_volume[3] : 0); + } + else + { + out= ((m_output[0]!=0)? m_volume[0]:0) + +((m_output[1]!=0)? m_volume[1]:0) + +((m_output[2]!=0)? m_volume[2]:0) + +((m_output[3]!=0)? m_volume[3]:0); + } + + if (m_negate) { out = -out; out2 = -out2; } + + *(lbuffer++) = out; + if (m_stereo) *(rbuffer++) = out2; + samples--; + } +} + +void sn76496_base_device::register_for_save_states() +{ + save_item(NAME(m_vol_table)); + save_item(NAME(m_register)); + save_item(NAME(m_last_register)); + save_item(NAME(m_volume)); + save_item(NAME(m_RNG)); +// save_item(NAME(m_clock_divider)); + save_item(NAME(m_current_clock)); +// save_item(NAME(m_feedback_mask)); +// save_item(NAME(m_whitenoise_tap1)); +// save_item(NAME(m_whitenoise_tap2)); +// save_item(NAME(m_negate)); +// save_item(NAME(m_stereo)); + save_item(NAME(m_stereo_mask)); + save_item(NAME(m_period)); + save_item(NAME(m_count)); + save_item(NAME(m_output)); + save_item(NAME(m_cycles_to_ready)); +// save_item(NAME(m_sega_style_psg)); +} + +DEFINE_DEVICE_TYPE(SN76496, sn76496_device, "sn76496", "SN76496") +DEFINE_DEVICE_TYPE(U8106, u8106_device, "u8106", "U8106") +DEFINE_DEVICE_TYPE(Y2404, y2404_device, "y2404", "Y2404") +DEFINE_DEVICE_TYPE(SN76489, sn76489_device, "sn76489", "SN76489") +DEFINE_DEVICE_TYPE(SN76489A, sn76489a_device, "sn76489a", "SN76489A") +DEFINE_DEVICE_TYPE(SN76494, sn76494_device, "sn76494", "SN76494") +DEFINE_DEVICE_TYPE(SN94624, sn94624_device, "sn94624", "SN94624") +DEFINE_DEVICE_TYPE(NCR7496, ncr7496_device, "ncr7496", "NCR7496") +DEFINE_DEVICE_TYPE(GAMEGEAR, gamegear_device, "gamegear_psg", "Game Gear PSG") +DEFINE_DEVICE_TYPE(SEGAPSG, segapsg_device, "segapsg", "Sega VDP PSG") diff --git a/src/hardware/mame/sn76496.h b/src/hardware/mame/sn76496.h new file mode 100644 index 00000000..22ad524e --- /dev/null +++ b/src/hardware/mame/sn76496.h @@ -0,0 +1,155 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria +#ifndef MAME_SOUND_SN76496_H +#define MAME_SOUND_SN76496_H + +#pragma once + + +DECLARE_DEVICE_TYPE(SN76496, sn76496_device) +DECLARE_DEVICE_TYPE(U8106, u8106_device) +DECLARE_DEVICE_TYPE(Y2404, y2404_device) +DECLARE_DEVICE_TYPE(SN76489, sn76489_device) +DECLARE_DEVICE_TYPE(SN76489A, sn76489a_device) +DECLARE_DEVICE_TYPE(SN76494, sn76494_device) +DECLARE_DEVICE_TYPE(SN94624, sn94624_device) +DECLARE_DEVICE_TYPE(NCR7496, ncr7496_device) +DECLARE_DEVICE_TYPE(GAMEGEAR, gamegear_device) +DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device) + + +#define MCFG_SN76496_READY_HANDLER(cb) \ + devcb = &sn76496_base_device::set_ready_handler(*device, (DEVCB_##cb)); + +class sn76496_base_device : public device_t, public device_sound_interface +{ +public: + // static configuration helpers + template static devcb_base &set_ready_handler(device_t &device, Object &&cb) { return downcast(device).m_ready_handler.set_callback(std::forward(cb)); } + + DECLARE_WRITE8_MEMBER( stereo_w ); + void write(uint8_t data); + DECLARE_WRITE8_MEMBER( write ); + DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; } + +protected: + sn76496_base_device( + const machine_config &mconfig, + device_type type, + const char *tag, + int feedbackmask, + int noisetap1, + int noisetap2, + bool negate, + bool stereo, + int clockdivider, + bool sega, + device_t *owner, + uint32_t clock); + + virtual void device_start() override; + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + +private: + inline bool in_noise_mode(); + void register_for_save_states(); + void countdown_cycles(); + + bool m_ready_state; + + devcb_write_line m_ready_handler; + + sound_stream* m_sound; + + const int32_t m_feedback_mask; // mask for feedback + const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14) + const int32_t m_whitenoise_tap2; // mask for white noise tap 2 (lower one, usually bit 13) + const bool m_negate; // output negate flag + const bool m_stereo; // whether we're dealing with stereo or not + const int32_t m_clock_divider; // clock divider + const bool m_sega_style_psg; // flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; AND if the initial register is pointing to 0x3 instead of 0x0 AND if the volume reg is preloaded with 0xF instead of 0x0 + + int32_t m_vol_table[16]; // volume table (for 4-bit to db conversion) + int32_t m_register[8]; // registers + int32_t m_last_register; // last register written + int32_t m_volume[4]; // db volume of voice 0-2 and noise + uint32_t m_RNG; // noise generator LFSR + int32_t m_current_clock; + int32_t m_stereo_mask; // the stereo output mask + int32_t m_period[4]; // Length of 1/2 of waveform + int32_t m_count[4]; // Position within the waveform + int32_t m_output[4]; // 1-bit output of each channel, pre-volume + int32_t m_cycles_to_ready; // number of cycles until the READY line goes active +}; + +// SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf) +class sn76496_device : public sn76496_base_device +{ +public: + sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// U8106 not verified yet. todo: verify; (a custom marked sn76489? only used on mr. do and maybe other universal games) +class u8106_device : public sn76496_base_device +{ +public: + u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// Y2404 not verified yet. todo: verify; (don't be fooled by the Y, it's a TI chip, not Yamaha) +class y2404_device : public sn76496_base_device +{ +public: + y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// SN76489 not verified yet. todo: verify; +class sn76489_device : public sn76496_base_device +{ +public: + sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid) +class sn76489a_device : public sn76496_base_device +{ +public: + sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider) +class sn76494_device : public sn76496_base_device +{ +public: + sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid +class sn94624_device : public sn76496_base_device +{ +public: + sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// NCR7496 not verified; info from smspower wiki +class ncr7496_device : public sn76496_base_device +{ +public: + ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// Verified by Justin Kerk +class gamegear_device : public sn76496_base_device +{ +public: + gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// todo: verify; from smspower wiki, assumed to have same invert as gamegear +class segapsg_device : public sn76496_base_device +{ +public: + segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +#endif // MAME_SOUND_SN76496_H diff --git a/src/hardware/mame/ymdeltat.cpp b/src/hardware/mame/ymdeltat.cpp new file mode 100644 index 00000000..29e05b4e --- /dev/null +++ b/src/hardware/mame/ymdeltat.cpp @@ -0,0 +1,650 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +/* +** +** File: ymdeltat.c +** +** YAMAHA DELTA-T adpcm sound emulation subroutine +** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) +** +** Base program is YM2610 emulator by Hiromitsu Shioya. +** Written by Tatsuyuki Satoh +** Improvements by Jarek Burczynski (bujar at mame dot net) +** +** +** History: +** +** 03-08-2003 Jarek Burczynski: +** - fixed BRDY flag implementation. +** +** 24-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset +** +** 22-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed external memory support +** +** 15-06-2003 Jarek Burczynski: +** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) +** - implemented support for the Limit address register +** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM +** - implemented external memory access (read/write) via the ADPCM data reg reads/writes +** Thanks go to Frits Hilderink for the example code. +** +** 14-06-2003 Jarek Burczynski: +** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO +** - modified EOS handling +** +** 05-04-2003 Jarek Burczynski: +** - implemented partial support for external/processor memory on sample replay +** +** 01-12-2002 Jarek Burczynski: +** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi +** - renamed/removed some YM_DELTAT struct fields +** +** 28-12-2001 Acho A. Tang +** - added EOS status report on ADPCM playback. +** +** 05-08-2001 Jarek Burczynski: +** - now_step is initialized with 0 at the start of play. +** +** 12-06-2001 Jarek Burczynski: +** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. +** Checked on real YM2610 chip - address register is 24 bits wide. +** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. +** +** TO DO: +** Check size of the address register on the other chips.... +** +** Version 0.72 +** +** sound chips that have this unit: +** YM2608 OPNA +** YM2610/B OPNB +** Y8950 MSX AUDIO +** +*/ + +#include "emu.h" +#include "ymdeltat.h" + +#define YM_DELTAT_SHIFT (16) + +#define YM_DELTAT_DELTA_MAX (24576) +#define YM_DELTAT_DELTA_MIN (127) +#define YM_DELTAT_DELTA_DEF (127) + +#define YM_DELTAT_DECODE_RANGE 32768 +#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) +#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) + + +/* Forecast to next Forecast (rate = *8) */ +/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ +static constexpr int32_t ym_deltat_decode_tableB1[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15, +}; +/* delta to next delta (rate= *64) */ +/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ +static constexpr int32_t ym_deltat_decode_tableB2[16] = { + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153 +}; + +#if 0 +void YM_DELTAT::BRDY_callback() +{ + logerror("BRDY_callback reached (flag set) !\n"); + + /* set BRDY bit in status register */ + if(status_set_handler) + if(status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); +} +#endif + +uint8_t YM_DELTAT::ADPCM_Read() +{ + uint8_t v = 0; + + /* external memory read */ + if ((portstate & 0xe0) == 0x20) + { + /* two dummy reads */ + if (memread) + { + now_addr = start << 1; + memread--; + return 0; + } + + + if (now_addr != (end << 1)) + { + v = memory[now_addr>>1]; + + /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/ + + now_addr += 2; /* two nibbles at a time */ + + /* reset BRDY bit in status register, which means we are reading the memory now */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have another data ready. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + } + else + { + /* set EOS bit in status register */ + if (status_set_handler && status_change_EOS_bit) + (status_set_handler)(status_change_which_chip, status_change_EOS_bit); + } + } + + return v; +} + + +/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ +static constexpr uint8_t dram_rightshift[4]={3,0,0,0}; + +/* DELTA-T ADPCM write register */ +void YM_DELTAT::ADPCM_Write(int r, int v) +{ + if (r >= 0x10) return; + reg[r] = v; /* stock data */ + + switch (r) + { + case 0x00: +/* +START: + Accessing *external* memory is started when START bit (D7) is set to "1", so + you must set all conditions needed for recording/playback before starting. + If you access *CPU-managed* memory, recording/playback starts after + read/write of ADPCM data register $08. + +REC: + 0 = ADPCM synthesis (playback) + 1 = ADPCM analysis (record) + +MEMDATA: + 0 = processor (*CPU-managed*) memory (means: using register $08) + 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) + + +SPOFF: + controls output pin that should disable the speaker while ADPCM analysis + +RESET and REPEAT only work with external memory. + + +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + /* handle emulation mode */ + if (emulation_mode == EMULATION_MODE_YM2610) + { + v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ + } + + portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ + + if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ + { + /* set PCM BUSY bit */ + PCM_BSY = 1; + + /* start ADPCM */ + now_step = 0; + acc = 0; + prev_acc = 0; + adpcml = 0; + adpcmd = YM_DELTAT_DELTA_DEF; + now_data = 0; + + } + + if (portstate & 0x20) /* do we access external memory? */ + { + now_addr = start << 1; + memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ + + /* if yes, then let's check if ADPCM memory is mapped and big enough */ + if (!memory) + { + device->logerror("YM Delta-T ADPCM rom not mapped\n"); + portstate = 0x00; + PCM_BSY = 0; + } + else + { + if (end >= memory_size) /* Check End in Range */ + { + device->logerror("YM Delta-T ADPCM end out of range: $%08x\n", end); + end = memory_size - 1; + } + if (start >= memory_size) /* Check Start in Range */ + { + device->logerror("YM Delta-T ADPCM start out of range: $%08x\n", start); + portstate = 0x00; + PCM_BSY = 0; + } + } + } + else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ + { + now_addr = 0; + } + + if (portstate & 0x01) + { + portstate = 0x00; + + /* clear PCM BUSY bit (in status register) */ + PCM_BSY = 0; + + /* set BRDY flag */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + } + break; + + case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ + /* handle emulation mode */ + if (emulation_mode == EMULATION_MODE_YM2610) + { + v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */ + } + + pan = &output_pointer[(v >> 6) & 0x03]; + if ((control2 & 3) != (v & 3)) + { + /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ + if (DRAMportshift != dram_rightshift[v & 3]) + { + DRAMportshift = dram_rightshift[v & 3]; + + /* final shift value depends on chip type and memory type selected: + 8 for YM2610 (ROM only), + 5 for ROM for Y8950 and YM2608, + 5 for x8bit DRAMs for Y8950 and YM2608, + 2 for x1bit DRAMs for Y8950 and YM2608. + */ + + /* refresh addresses */ + start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); + end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift); + end += (1 << (portshift - DRAMportshift)) - 1; + limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift); + } + } + control2 = v; + break; + + case 0x02: /* Start Address L */ + case 0x03: /* Start Address H */ + start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift); + /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/ + break; + + case 0x04: /* Stop Address L */ + case 0x05: /* Stop Address H */ + end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift); + end += (1 << (portshift - DRAMportshift)) - 1; + /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/ + break; + + case 0x06: /* Prescale L (ADPCM and Record frq) */ + case 0x07: /* Prescale H */ + break; + + case 0x08: /* ADPCM data */ +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + /* external memory write */ + if ((portstate & 0xe0) == 0x60) + { + if (memread) + { + now_addr = start << 1; + memread = 0; + } + + /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/ + + if (now_addr != (end << 1)) + { + memory[now_addr >> 1] = v; + now_addr += 2; /* two nybbles at a time */ + + /* reset BRDY bit in status register, which means we are processing the write */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have written the data. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); + + } + else + { + /* set EOS bit in status register */ + if (status_set_handler && status_change_EOS_bit) + (status_set_handler)(status_change_which_chip, status_change_EOS_bit); + } + + return; + } + + /* ADPCM synthesis from CPU */ + if ((portstate & 0xe0) == 0x80) + { + CPU_data = v; + + /* Reset BRDY bit in status register, which means we are full of data */ + if (status_reset_handler && status_change_BRDY_bit) + (status_reset_handler)(status_change_which_chip, status_change_BRDY_bit); + return; + } + + break; + + case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ + case 0x0a: /* DELTA-N H */ + delta = (reg[0xa] * 0x0100 | reg[0x9]); + step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase); + /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/ + break; + + case 0x0b: /* Output level control (volume, linear) */ + { + const int32_t oldvol = volume; + volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE; +/* v * ((1<<16)>>8) >> 15; +* thus: v * (1<<8) >> 15; +* thus: output_range must be (1 << (15+8)) at least +* v * ((1<<23)>>8) >> 15; +* v * (1<<15) >> 15; +*/ + /*logerror("DELTAT vol = %2x\n",v&0xff);*/ + if (oldvol != 0) + { + adpcml = int(double(adpcml) / double(oldvol) * double(volume)); + } + } + break; + + case 0x0c: /* Limit Address L */ + case 0x0d: /* Limit Address H */ + limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift); + /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/ + break; + } +} + +void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev) +{ + device = dev; + now_addr = 0; + now_step = 0; + step = 0; + start = 0; + end = 0; + limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ + volume = 0; + pan = &output_pointer[panidx]; + acc = 0; + prev_acc = 0; + adpcmd = 127; + adpcml = 0; + emulation_mode = uint8_t(mode); + portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0; + control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ + DRAMportshift = dram_rightshift[control2 & 3]; + + /* The flag mask register disables the BRDY after the reset, however + ** as soon as the mask is enabled the flag needs to be set. */ + + /* set BRDY bit in status register */ + if (status_set_handler && status_change_BRDY_bit) + (status_set_handler)(status_change_which_chip, status_change_BRDY_bit); +} + +void YM_DELTAT::postload(uint8_t *regs) +{ + /* to keep adpcml */ + volume = 0; + /* update */ + for (int r = 1; r < 16; r++) + ADPCM_Write(r, regs[r]); + reg[0] = regs[0]; + + /* current rom data */ + if (memory) + now_data = *(memory + (now_addr >> 1)); + +} +void YM_DELTAT::savestate(device_t *device) +{ +#ifdef MAME_EMU_SAVE_H + YM_DELTAT *const DELTAT = this; // makes the save name sensible + device->save_item(NAME(DELTAT->portstate)); + device->save_item(NAME(DELTAT->now_addr)); + device->save_item(NAME(DELTAT->now_step)); + device->save_item(NAME(DELTAT->acc)); + device->save_item(NAME(DELTAT->prev_acc)); + device->save_item(NAME(DELTAT->adpcmd)); + device->save_item(NAME(DELTAT->adpcml)); +#endif +} + + +#define YM_DELTAT_Limit(val,max,min) \ +{ \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) +{ + uint32_t step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) + DELTAT->now_addr = 0; + + if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ + if( DELTAT->portstate&0x10 ){ + /* repeat start */ + DELTAT->now_addr = DELTAT->start<<1; + DELTAT->acc = 0; + DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; + DELTAT->prev_acc = 0; + }else{ + /* set EOS bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_EOS_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); + + /* clear PCM BUSY bit (reflected in status register) */ + DELTAT->PCM_BSY = 0; + + DELTAT->portstate = 0; + DELTAT->adpcml = 0; + DELTAT->prev_acc = 0; + return; + } + } + + if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; + else + { + DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1)); + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + /* 12-06-2001 JB: */ + /* YM2610 address register is 24 bits wide.*/ + /* The "+1" is there because we use 1 bit more for nibble calculations.*/ + /* WARNING: */ + /* Side effect: we should take the size of the mapped ROM into account */ + DELTAT->now_addr &= ( (1<<(24+1))-1); + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + /* ElSemi: Fix interpolator. */ + /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) +{ + uint32_t step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr&1 ) + { + data = DELTAT->now_data & 0x0f; + + DELTAT->now_data = DELTAT->CPU_data; + + /* after we used CPU_data, we set BRDY bit in status register, + * which means we are ready to accept another byte of data */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + } + else + { + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +/* ADPCM B (Delta-T control type) */ +void YM_DELTAT::ADPCM_CALC() +{ +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + if ( (portstate & 0xe0)==0xa0 ) + { + YM_DELTAT_synthesis_from_external_memory(this); + return; + } + + if ( (portstate & 0xe0)==0x80 ) + { + /* ADPCM synthesis from CPU-managed memory (from reg $08) */ + YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */ + return; + } + +//todo: ADPCM analysis +// if ( (portstate & 0xe0)==0xc0 ) +// if ( (portstate & 0xe0)==0xe0 ) + + return; +} diff --git a/src/hardware/mame/ymdeltat.h b/src/hardware/mame/ymdeltat.h new file mode 100644 index 00000000..f52bd46f --- /dev/null +++ b/src/hardware/mame/ymdeltat.h @@ -0,0 +1,85 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +#ifndef MAME_SOUND_YMDELTAT_H +#define MAME_SOUND_YMDELTAT_H + +#pragma once + + +typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits); + + +/* DELTA-T (adpcm type B) struct */ +struct YM_DELTAT { /* AT: rearranged and tightened structure */ + static constexpr int EMULATION_MODE_NORMAL = 0; + static constexpr int EMULATION_MODE_YM2610 = 1; + + uint8_t *memory; + int32_t *output_pointer;/* pointer of output pointers */ + int32_t *pan; /* pan : &output_pointer[pan] */ + double freqbase; +#if 0 + double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ + double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ +#endif + uint32_t memory_size; + int output_range; + uint32_t now_addr; /* current address */ + uint32_t now_step; /* correct step */ + uint32_t step; /* step */ + uint32_t start; /* start address */ + uint32_t limit; /* limit address */ + uint32_t end; /* end address */ + uint32_t delta; /* delta scale */ + int32_t volume; /* current volume */ + int32_t acc; /* shift Measurement value*/ + int32_t adpcmd; /* next Forecast */ + int32_t adpcml; /* current value */ + int32_t prev_acc; /* leveling value */ + uint8_t now_data; /* current rom data */ + uint8_t CPU_data; /* current data from reg 08 */ + uint8_t portstate; /* port status */ + uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ + uint8_t portshift; /* address bits shift-left: + ** 8 for YM2610, + ** 5 for Y8950 and YM2608 */ + + uint8_t DRAMportshift; /* address bits shift-right: + ** 0 for ROM and x8bit DRAMs, + ** 3 for x1 DRAMs */ + + uint8_t memread; /* needed for reading/writing external memory */ + + /* handlers and parameters for the status flags support */ + STATUS_CHANGE_HANDLER status_set_handler; + STATUS_CHANGE_HANDLER status_reset_handler; + + /* note that different chips have these flags on different + ** bits of the status register + */ + void * status_change_which_chip; /* this chip id */ + uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ + uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ + uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */ + + /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, + ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) + */ + uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ + + uint8_t reg[16]; /* adpcm registers */ + uint8_t emulation_mode; /* which chip we're emulating */ + device_t *device; + + /*void BRDY_callback();*/ + + uint8_t ADPCM_Read(); + void ADPCM_Write(int r, int v); + void ADPCM_Reset(int panidx, int mode, device_t *dev); + void ADPCM_CALC(); + + void postload(uint8_t *regs); + void savestate(device_t *device); +}; + +#endif // MAME_SOUND_YMDELTAT_H diff --git a/src/hardware/mame/ymf262.cpp b/src/hardware/mame/ymf262.cpp new file mode 100644 index 00000000..f69c15da --- /dev/null +++ b/src/hardware/mame/ymf262.cpp @@ -0,0 +1,2788 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +/* +** +** File: ymf262.c - software implementation of YMF262 +** FM sound generator type OPL3 +** +** Copyright Jarek Burczynski +** +** Version 0.2 +** + +Revision History: + +03-03-2003: initial release + - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips + - thanks to Stiletto for the datasheets + + Features as listed in 4MF262A6 data sheet: + 1. Registers are compatible with YM3812 (OPL2) FM sound source. + 2. Up to six sounds can be used as four-operator melody sounds for variety. + 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators). + 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody + sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators). + 5. 8 selectable waveforms. + 6. 4-channel sound output. + 7. YMF262 compabile DAC (YAC512) is available. + 8. LFO for vibrato and tremolo effedts. + 9. 2 programable timers. + 10. Shorter register access time compared with YM3812. + 11. 5V single supply silicon gate CMOS process. + 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S). + + +differences between OPL2 and OPL3 not documented in Yamaha datahasheets: +- sinus table is a little different: the negative part is off by one... + +- in order to enable selection of four different waveforms on OPL2 + one must set bit 5 in register 0x01(test). + on OPL3 this bit is ignored and 4-waveform select works *always*. + (Don't confuse this with OPL3's 8-waveform select.) + +- Envelope Generator: all 15 x rates take zero time on OPL3 + (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates + take zero time) + +- channel calculations: output of operator 1 is in perfect sync with + output of operator 2 on OPL3; on OPL and OPL2 output of operator 1 + is always delayed by one sample compared to output of operator 2 + + +differences between OPL2 and OPL3 shown in datasheets: +- YMF262 does not support CSM mode + + +*/ + +#include "emu.h" +#include "ymf262.h" + + +/* output final shift */ +#if (OPL3_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = a; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = b; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +#define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */ + + +struct OPL3_SLOT +{ + uint32_t ar; /* attack rate: AR<<2 */ + uint32_t dr; /* decay rate: DR<<2 */ + uint32_t rr; /* release rate:RR<<2 */ + uint8_t KSR; /* key scale rate */ + uint8_t ksl; /* keyscale level */ + uint8_t ksr; /* key scale rate: kcode>>KSR */ + uint8_t mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + uint32_t Cnt; /* frequency counter */ + uint32_t Incr; /* frequency counter step */ + uint8_t FB; /* feedback shift value */ + uint8_t conn_enum; /* slot output route */ + int32_t *connect; /* slot output pointer */ + int32_t op1_out[2]; /* slot1 output for feedback */ + uint8_t CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + uint8_t eg_type; /* percussive/non-percussive mode */ + uint8_t state; /* phase type */ + uint32_t TL; /* total level: TL << 2 */ + int32_t TLL; /* adjusted now TL */ + int32_t volume; /* envelope counter */ + uint32_t sl; /* sustain level: sl_tab[SL] */ + + uint32_t eg_m_ar; /* (attack state) */ + uint8_t eg_sh_ar; /* (attack state) */ + uint8_t eg_sel_ar; /* (attack state) */ + uint32_t eg_m_dr; /* (decay state) */ + uint8_t eg_sh_dr; /* (decay state) */ + uint8_t eg_sel_dr; /* (decay state) */ + uint32_t eg_m_rr; /* (release state) */ + uint8_t eg_sh_rr; /* (release state) */ + uint8_t eg_sel_rr; /* (release state) */ + + uint32_t key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + uint32_t AMmask; /* LFO Amplitude Modulation enable mask */ + uint8_t vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + uint8_t waveform_number; + unsigned int wavetable; + + //unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2 + unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2 + +}; + +struct OPL3_CH +{ + OPL3_SLOT SLOT[2]; + + uint32_t block_fnum; /* block+fnum */ + uint32_t fc; /* Freq. Increment base */ + uint32_t ksl_base; /* KeyScaleLevel Base step */ + uint8_t kcode; /* key code (for key scaling) */ + + /* + there are 12 2-operator channels which can be combined in pairs + to form six 4-operator channel, they are: + 0 and 3, + 1 and 4, + 2 and 5, + 9 and 12, + 10 and 13, + 11 and 14 + */ + uint8_t extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */ + + unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2 + +}; + +/* OPL3 state */ +struct OPL3 +{ + OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */ + + uint32_t pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */ + uint32_t pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */ + + signed int chanout[18]; + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */ + + uint32_t eg_cnt; /* global envelope generator counter */ + uint32_t eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */ + uint32_t eg_timer_add; /* step of eg_timer */ + uint32_t eg_timer_overflow; /* envelope generator timer overflows every 1 sample (on real chip) */ + + uint32_t fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + uint32_t LFO_AM; + int32_t LFO_PM; + + uint8_t lfo_am_depth; + uint8_t lfo_pm_depth_range; + uint32_t lfo_am_cnt; + uint32_t lfo_am_inc; + uint32_t lfo_pm_cnt; + uint32_t lfo_pm_inc; + + uint32_t noise_rng; /* 23 bit noise shift register */ + uint32_t noise_p; /* current noise 'phase' */ + uint32_t noise_f; /* current noise period */ + + uint8_t OPL3_mode; /* OPL3 extension enable flag */ + + uint8_t rhythm; /* Rhythm mode */ + + int T[2]; /* timer counters */ + uint8_t st[2]; /* timer enable */ + + uint32_t address; /* address register */ + uint8_t status; /* status flag */ + uint8_t statusmask; /* status mask */ + + uint8_t nts; /* NTS (note select) */ + + /* external event callback handlers */ + OPL3_TIMERHANDLER timer_handler; + device_t *TimerParam; + OPL3_IRQHANDLER IRQHandler; + device_t *IRQParam; + OPL3_UPDATEHANDLER UpdateHandler; + device_t *UpdateParam; + + uint8_t type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + attotime TimerBase; /* Timer base time (==sampling time)*/ + device_t *device; + + /* Optional handlers */ + void SetTimerHandler(OPL3_TIMERHANDLER handler, device_t *device) + { + timer_handler = handler; + TimerParam = device; + } + void SetIRQHandler(OPL3_IRQHANDLER handler, device_t *device) + { + IRQHandler = handler; + IRQParam = device; + } + void SetUpdateHandler(OPL3_UPDATEHANDLER handler, device_t *device) + { + UpdateHandler = handler; + UpdateParam = device; + } +}; + +} // anonymous namespace + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) +static const double ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; +#undef DV + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +static const uint32_t ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (uint32_t) ( db * (2.0/ENV_STEP) ) +static const uint32_t sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/* note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const uint8_t mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML, + 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML +}; +#undef ML + +/* TL_TAB_LEN is calculated as: + +* (12+1)=13 - sinus amplitude bits (Y axis) +* additional 1: to compensate for calculations of negative part of waveform +* (if we don't add it then the greatest possible _negative_ value would be -2 +* and we really need -1 for waveform #7) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (13*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* there are eight waveforms on OPL3 chips */ +static unsigned int sin_tab[SIN_LEN * 8]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const int8_t lfo_pm_table[8*8*2] = { +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +static int num_lock = 0; + +/* work table */ +#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) +#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) +#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) +#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) + + +static inline void OPL3_SLOT_CONNECT(OPL3 *chip, OPL3_SLOT *slot) { + if (slot->conn_enum == CONN_NULL) { + slot->connect = nullptr; + } else if (slot->conn_enum >= CONN_CHAN0 && slot->conn_enum < CONN_PHASEMOD) { + slot->connect = &chip->chanout[slot->conn_enum]; + } else if (slot->conn_enum == CONN_PHASEMOD) { + slot->connect = &chip->phase_modulation; + } else if (slot->conn_enum == CONN_PHASEMOD2) { + slot->connect = &chip->phase_modulation2; + } +} + +static inline int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + + +/* status set and IRQ handling */ +static inline void OPL3_STATUS_SET(OPL3 *chip,int flag) +{ + /* set status flag masking out disabled IRQs */ + chip->status |= (flag & chip->statusmask); + if(!(chip->status & 0x80)) + { + if(chip->status & 0x7f) + { /* IRQ on */ + chip->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +static inline void OPL3_STATUS_RESET(OPL3 *chip,int flag) +{ + /* reset status flag */ + chip->status &= ~flag; + if(chip->status & 0x80) + { + if (!(chip->status & 0x7f)) + { + chip->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0); + } + } +} + +/* IRQ mask set */ +static inline void OPL3_STATUSMASK_SET(OPL3 *chip,int flag) +{ + chip->statusmask = flag; + /* IRQ handling check */ + OPL3_STATUS_SET(chip,0); + OPL3_STATUS_RESET(chip,0); +} + + +/* advance LFO to next sample */ +static inline void advance_lfo(OPL3 *chip) +{ + uint8_t tmp; + + /* LFO */ + chip->lfo_am_cnt += chip->lfo_am_inc; + if (chip->lfo_am_cnt >= ((uint32_t)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((uint32_t)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (chip->lfo_am_depth) + chip->LFO_AM = tmp; + else + chip->LFO_AM = tmp>>2; + + chip->lfo_pm_cnt += chip->lfo_pm_inc; + chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range; +} + +/* advance to next sample */ +static inline void advance(OPL3 *chip) +{ + OPL3_CH *CH; + OPL3_SLOT *op; + int i; + + chip->eg_timer += chip->eg_timer_add; + + while (chip->eg_timer >= chip->eg_timer_overflow) + { + chip->eg_timer -= chip->eg_timer_overflow; + + chip->eg_cnt++; + + for (i=0; i<9*2*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; +#if 1 + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_ar) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_dr) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ +// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_rr) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_rr) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } +#endif + } + } + + for (i=0; i<9*2*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Phase Generator */ + if(op->vib) + { + uint8_t block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + chip->noise_p += chip->noise_f; + i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + chip->noise_p &= FREQ_MASK; + while (i) + { + /* + uint32_t j; + j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; + chip->noise_rng = (j<<22) | (chip->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; + chip->noise_rng >>= 1; + + i--; + } +} + + +static inline signed int op_calc(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +static inline signed int op_calc1(uint32_t phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + uint32_t p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((uint32_t)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) + +/* calculate output of a standard 2 operator channel + (or 1st part of a 4-op channel) */ +static inline void chan_calc( OPL3 *chip, OPL3_CH *CH ) +{ + OPL3_SLOT *SLOT; + unsigned int env; + signed int out; + + chip->phase_modulation = 0; + chip->phase_modulation2= 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + SLOT->op1_out[1] = 0; + if (env < ENV_QUIET) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + if (SLOT->connect) { + *SLOT->connect += SLOT->op1_out[1]; + } +//logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env ); + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if ((env < ENV_QUIET) && SLOT->connect) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); + +//logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env ); + +} + +/* calculate output of a 2nd part of 4-op channel */ +static inline void chan_calc_ext( OPL3 *chip, OPL3_CH *CH ) +{ + OPL3_SLOT *SLOT; + unsigned int env; + + chip->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + if (env < ENV_QUIET && SLOT->connect) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable ); + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if (env < ENV_QUIET && SLOT->connect) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); + +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +static inline void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise ) +{ + OPL3_SLOT *SLOT; + signed int *chanout = chip->chanout; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + chip->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + chip->phase_modulation = SLOT->op1_out[0]; + //else ignore output of operator 1 + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) + // SD (16) channel 7->slot 1 + // TOM (14) channel 8->slot 1 + // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) + + /* Envelope generation based on: */ + // HH channel 7->slot1 + // SD channel 7->slot2 + // TOM channel 8->slot1 + // TOP channel 8->slot2 + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET ) + { + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + chanout[7] += op_calc(phase<wavetable) * 2; + } + + /* Snare Drum (verified on real YM3812) */ + env = volume_calc(SLOT7_2); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + uint32_t phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + chanout[7] += op_calc(phase<wavetable) * 2; + } + + /* Tom Tom (verified on real YM3812) */ + env = volume_calc(SLOT8_1); + if( env < ENV_QUIET ) + chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(SLOT8_2); + if( env < ENV_QUIET ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + uint32_t phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + chanout[8] += op_calc(phase<wavetable) * 2; + } + +} + + +/* generic table initialize */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */ + + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */ + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */ + logerror("\n"); + + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */ + logerror("\n"); + #endif + } + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /* waveform 4: */ + /* /\ ____/\ ____*/ + /* \/ \/ */ + /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[4*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[4*SIN_LEN+i] = sin_tab[i*2]; + + /* waveform 5: */ + /* /\/\____/\/\____*/ + /* */ + /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[5*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ]; + + /* waveform 6: ____ ____ */ + /* */ + /* ____ ____*/ + /* output maximum in half the cycle and output minimum on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[6*SIN_LEN+i] = 1; /* negative */ + else + sin_tab[6*SIN_LEN+i] = 0; /* positive */ + + /* waveform 7: */ + /* |\____ |\____ */ + /* \| \|*/ + /* output sawtooth waveform */ + + if (i & (1<<(SIN_BITS-1)) ) + x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */ + else + x = i*16; /*positive: from 0 to 8176 */ + + if (x > TL_TAB_LEN) + x = TL_TAB_LEN; /* clip to the allowed range */ + + sin_tab[7*SIN_LEN+i] = x; + + //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] ); + //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] ); + //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] ); + //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] ); + //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] ); + } + /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + +static void OPLCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif +} + + + +static void OPL3_initalize(OPL3 *chip) +{ + int i; + + /* frequency base */ + chip->freqbase = (chip->rate) ? ((double)chip->clock / (8.0*36)) / chip->rate : 0; +#if 0 + chip->rate = (double)chip->clock / (8.0*36); + chip->freqbase = 1.0; +#endif + + /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */ + + /* Timer base time */ + chip->TimerBase = attotime::from_hz(chip->clock) * (8*36); + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + chip->fn_tab[i] = (uint32_t)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("YMF262.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("YMF262.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", static_cast(ksl_tab[i*16+j]) ); + } + logerror("\n"); + } +#endif + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; + + /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ + chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; + + /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ + + /* Noise generator: a step takes 1 sample */ + chip->noise_f = (1.0 / 1.0) * (1<freqbase; + + chip->eg_timer_add = (1<freqbase; + chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ + +} + +static inline void FM_KEYON(OPL3_SLOT *SLOT, uint32_t key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +static inline void FM_KEYOFF(OPL3_SLOT *SLOT, uint32_t key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +static inline void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+60) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +static inline void set_mul(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + + if (chip->OPL3_mode & 1) + { + int chan_no = slot/2; + + /* in OPL3 mode */ + //DO THIS: + //if this is one of the slots of 1st channel forming up a 4-op channel + //do normal operation + //else normal 2 operator function + //OR THIS: + //if this is one of the slots of 2nd channel forming up a 4-op channel + //update it using channel data of 1st channel of a pair + //else normal 2 operator function + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + else + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + break; + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + /* update this SLOT using frequency data for 1st channel of a pair */ + CALC_FCSLOT(CH-3,SLOT); + } + else + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + break; + default: + /* normal */ + CALC_FCSLOT(CH,SLOT); + break; + } + } + else + { + /* in OPL2 mode */ + CALC_FCSLOT(CH,SLOT); + } +} + +/* set ksl & tl */ +static inline void set_ksl_tl(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + if (chip->OPL3_mode & 1) + { + int chan_no = slot/2; + + /* in OPL3 mode */ + //DO THIS: + //if this is one of the slots of 1st channel forming up a 4-op channel + //do normal operation + //else normal 2 operator function + //OR THIS: + //if this is one of the slots of 2nd channel forming up a 4-op channel + //update it using channel data of 1st channel of a pair + //else normal 2 operator function + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + else + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + break; + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + /* update this SLOT using frequency data for 1st channel of a pair */ + SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl); + } + else + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + break; + default: + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + break; + } + } + else + { + /* in OPL2 mode */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + +} + +/* set attack rate & decay rate */ +static inline void set_ar_dr(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */ + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +static inline void set_sl_rr(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +static void update_channels(OPL3 *chip, OPL3_CH *CH) +{ + /* update channel passed as a parameter and a channel at CH+=3; */ + if (CH->extended) + { /* we've just switched to combined 4 operator mode */ + + } + else + { /* we've just switched to normal 2 operator mode */ + + } + +} + +/* write a value v to register r on OPL chip */ +static void OPL3WriteReg(OPL3 *chip, int r, int v) +{ + OPL3_CH *CH; + unsigned int ch_offset = 0; + int slot; + int block_fnum; + + if(r&0x100) + { + switch(r) + { + case 0x101: /* test register */ + return; + + case 0x104: /* 6 channels enable */ + { + uint8_t prev; + + CH = &chip->P_CH[0]; /* channel 0 */ + prev = CH->extended; + CH->extended = (v>>0) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 1 */ + prev = CH->extended; + CH->extended = (v>>1) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 2 */ + prev = CH->extended; + CH->extended = (v>>2) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + + + CH = &chip->P_CH[9]; /* channel 9 */ + prev = CH->extended; + CH->extended = (v>>3) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 10 */ + prev = CH->extended; + CH->extended = (v>>4) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 11 */ + prev = CH->extended; + CH->extended = (v>>5) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + + } + return; + + case 0x105: /* OPL3 extensions enable register */ + + chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */ + + /* following behaviour was tested on real YMF262, + switching OPL3/OPL2 modes on the fly: + - does not change the waveform previously selected (unless when ....) + - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....) + - does not disable channels 9-17 on OPL3->OPL2 switch + - does not switch 4 operator channels back to 2 operator channels + */ + + return; + + default: + if (r < 0x120) + chip->device->logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v); + break; + } + + ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */ + } + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* test register */ + break; + case 0x02: /* Timer 1 */ + chip->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + chip->T[1] = (256-v)*16; + break; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flags clear */ + OPL3_STATUS_RESET(chip,0x60); + } + else + { /* set IRQ mask ,timer enable */ + uint8_t st1 = v & 1; + uint8_t st2 = (v>>1) & 1; + + /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */ + OPL3_STATUS_RESET(chip, v & 0x60); + OPL3_STATUSMASK_SET(chip, (~v) & 0x60 ); + + /* timer 2 */ + if(chip->st[1] != st2) + { + attotime period = st2 ? chip->TimerBase * chip->T[1] : attotime::zero; + chip->st[1] = st2; + if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period); + } + /* timer 1 */ + if(chip->st[0] != st1) + { + attotime period = st1 ? chip->TimerBase * chip->T[0] : attotime::zero; + chip->st[0] = st1; + if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period); + } + } + break; + case 0x08: /* x,NTS,x,x, x,x,x,x */ + chip->nts = v; + break; + + default: + chip->device->logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v); + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(chip, slot + ch_offset*2, v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(chip, slot + ch_offset*2, v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(chip, slot + ch_offset*2, v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(chip, slot + ch_offset*2, v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + if (ch_offset != 0) /* 0xbd register is present in set #1 only */ + return; + + chip->lfo_am_depth = v & 0x80; + chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + chip->rhythm = v&0x3f; + + if(chip->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &chip->P_CH[(r&0x0f) + ch_offset]; + + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if (chip->OPL3_mode & 1) + { + int chan_no = (r&0x0f) + ch_offset; + + /* in OPL3 mode */ + //DO THIS: + //if this is 1st channel forming up a 4-op channel + //ALSO keyon/off slots of 2nd channel forming up 4-op channel + //else normal 2 operator function keyon/off + //OR THIS: + //if this is 2nd channel forming up 4-op channel just do nothing + //else normal 2 operator function keyon/off + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + //if this is 1st channel forming up a 4-op channel + //ALSO keyon/off slots of 2nd channel forming up 4-op channel + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + FM_KEYON (&(CH+3)->SLOT[SLOT1], 1); + FM_KEYON (&(CH+3)->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1); + FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1); + } + } + else + { + //else normal 2 operator function keyon/off + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + //if this is 2nd channel forming up 4-op channel just do nothing + } + else + { + //else normal 2 operator function keyon/off + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + break; + + default: + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + break; + } + } + else + { + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + uint8_t block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = static_cast(ksl_tab[block_fnum>>6]); + CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (chip->nts&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + if (chip->OPL3_mode & 1) + { + int chan_no = (r&0x0f) + ch_offset; + /* in OPL3 mode */ + //DO THIS: + //if this is 1st channel forming up a 4-op channel + //ALSO update slots of 2nd channel forming up 4-op channel + //else normal 2 operator function keyon/off + //OR THIS: + //if this is 2nd channel forming up 4-op channel just do nothing + //else normal 2 operator function keyon/off + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + //if this is 1st channel forming up a 4-op channel + //ALSO update slots of 2nd channel forming up 4-op channel + + /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl); + (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl); + + /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]); + } + else + { + //else normal 2 operator function + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + //if this is 2nd channel forming up 4-op channel just do nothing + } + else + { + //else normal 2 operator function + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + + default: + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + break; + } + } + else + { + /* in OPL2 mode */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + } + break; + + case 0xc0: + /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */ + if( (r&0xf) > 8) return; + + CH = &chip->P_CH[(r&0xf) + ch_offset]; + + if( chip->OPL3_mode & 1 ) + { + int base = ((r&0xf) + ch_offset) * 4; + + /* OPL3 mode */ + chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */ + chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */ + chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */ + chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */ + } + else + { + int base = ((r&0xf) + ch_offset) * 4; + + /* OPL2 mode - always enabled */ + chip->pan[ base ] = ~0; /* ch.A */ + chip->pan[ base +1 ] = ~0; /* ch.B */ + chip->pan[ base +2 ] = ~0; /* ch.C */ + chip->pan[ base +3 ] = ~0; /* ch.D */ + } + + chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */ + + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + + if( chip->OPL3_mode & 1 ) + { + int chan_no = (r&0x0f) + ch_offset; + + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + uint8_t conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0); + switch(conn) + { + case 0: + /* 1 -> 2 -> 3 -> 4 - out */ + + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + case 1: + /* 1 -> 2 -\ + 3 -> 4 -+- out */ + + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + case 2: + /* 1 -----------\ + 2 -> 3 -> 4 -+- out */ + + CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; + CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + case 3: + /* 1 ------\ + 2 -> 3 -+- out + 4 ------/ */ + CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; + CH->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + (CH+3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no + 3; + (CH+3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no + 3; + break; + } + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &(CH+3)->SLOT[SLOT2]); + } + else + { + /* 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + uint8_t conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0); + switch(conn) + { + case 0: + /* 1 -> 2 -> 3 -> 4 - out */ + + (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + case 1: + /* 1 -> 2 -\ + 3 -> 4 -+- out */ + + (CH-3)->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no - 3; + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + case 2: + /* 1 -----------\ + 2 -> 3 -> 4 -+- out */ + + (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + CH->SLOT[SLOT1].conn_enum = CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + case 3: + /* 1 ------\ + 2 -> 3 -+- out + 4 ------/ */ + (CH-3)->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no - 3; + (CH-3)->SLOT[SLOT2].conn_enum = CONN_PHASEMOD2; + CH->SLOT[SLOT1].conn_enum = CONN_CHAN0 + chan_no; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + chan_no; + break; + } + OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &(CH-3)->SLOT[SLOT2]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + else + { + /* 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + break; + + default: + /* 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + break; + } + } + else + { + /* OPL2 mode - always 2 operators mode */ + CH->SLOT[SLOT1].conn_enum = CH->SLOT[SLOT1].CON ? CONN_CHAN0 + (r&0xf)+ch_offset : CONN_PHASEMOD; + CH->SLOT[SLOT2].conn_enum = CONN_CHAN0 + (r&0xf)+ch_offset; + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT1]); + OPL3_SLOT_CONNECT(chip, &CH->SLOT[SLOT2]); + } + break; + + case 0xe0: /* waveform select */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + + slot += ch_offset*2; + + CH = &chip->P_CH[slot/2]; + + + /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */ + v &= 7; + CH->SLOT[slot&1].waveform_number = v; + + /* ... but select only waveforms 0-3 in OPL2 mode */ + if( !(chip->OPL3_mode & 1) ) + { + v &= 3; /* we're in OPL2 mode */ + } + CH->SLOT[slot&1].wavetable = v * SIN_LEN; + break; + } +} + +/* lock/unlock for common table */ +static int OPL3_LockTable(device_t *device) +{ + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + if( !init_tables() ) + { + num_lock--; + return -1; + } + + return 0; +} + +static void OPL3_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + OPLCloseTable(); +} + +static void OPL3ResetChip(OPL3 *chip) +{ + int c,s; + + chip->eg_timer = 0; + chip->eg_cnt = 0; + + chip->noise_rng = 1; /* noise shift register */ + chip->nts = 0; /* note split */ + OPL3_STATUS_RESET(chip,0x60); + + /* reset with register write */ + OPL3WriteReg(chip,0x01,0); /* test register */ + OPL3WriteReg(chip,0x02,0); /* Timer1 */ + OPL3WriteReg(chip,0x03,0); /* Timer2 */ + OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */ + + +//FIX IT registers 101, 104 and 105 + + +//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) + for(c = 0xff ; c >= 0x20 ; c-- ) + OPL3WriteReg(chip,c,0); +//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) + for(c = 0x1ff ; c >= 0x120 ; c-- ) + OPL3WriteReg(chip,c,0); + + + + /* reset operator parameters */ + for( c = 0 ; c < 9*2 ; c++ ) + { + OPL3_CH *CH = &chip->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +} + +/* Create one of virtual YMF262 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type) +{ + OPL3 *chip; + + if (OPL3_LockTable(device) == -1) return nullptr; + + /* allocate memory block */ + chip = auto_alloc_clear(device->machine(), ()); + + chip->device = device; + chip->type = type; + chip->clock = clock; + chip->rate = rate; + + /* init global tables */ + OPL3_initalize(chip); + + /* reset chip */ + OPL3ResetChip(chip); + return chip; +} + +/* Destroy one of virtual YMF262 */ +static void OPL3Destroy(OPL3 *chip) +{ + OPL3_UnLockTable(); + auto_free(chip->device->machine(), chip); +} + + +/* YMF262 I/O interface */ +static int OPL3Write(OPL3 *chip, int a, int v) +{ + /* data bus is 8 bits */ + v &= 0xff; + + + switch(a&3) + { + case 0: /* address port 0 (register set #1) */ + chip->address = v; + break; + + case 1: /* data port - ignore A1 */ + case 3: /* data port - ignore A1 */ + if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0); + OPL3WriteReg(chip,chip->address,v); + break; + + case 2: /* address port 1 (register set #2) */ + + /* verified on real YMF262: + in OPL3 mode: + address line A1 is stored during *address* write and ignored during *data* write. + + in OPL2 mode: + register set#2 writes go to register set#1 (ignoring A1) + verified on registers from set#2: 0x01, 0x04, 0x20-0xef + The only exception is register 0x05. + */ + if( chip->OPL3_mode & 1 ) + { + /* OPL3 mode */ + chip->address = v | 0x100; + } + else + { + /* in OPL2 mode the only accessible in set #2 is register 0x05 */ + if( v==5 ) + chip->address = v | 0x100; + else + chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */ + } + break; + } + + return chip->status>>7; +} + +static unsigned char OPL3Read(OPL3 *chip,int a) +{ + if( a==0 ) + { + /* status port */ + return chip->status; + } + + return 0x00; /* verified on real YMF262 */ +} + + + +static int OPL3TimerOver(OPL3 *chip,int c) +{ + if( c ) + { /* Timer B */ + OPL3_STATUS_SET(chip,0x20); + } + else + { /* Timer A */ + OPL3_STATUS_SET(chip,0x40); + } + /* reload timer */ + if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]); + return chip->status>>7; +} + +static void OPL3_save_state(OPL3 *chip, device_t *device) { + for (int ch=0; ch<18; ch++) { + OPL3_CH *channel = &chip->P_CH[ch]; + device->save_item(NAME(channel->block_fnum), ch); + device->save_item(NAME(channel->fc), ch); + device->save_item(NAME(channel->ksl_base), ch); + device->save_item(NAME(channel->kcode), ch); + device->save_item(NAME(channel->extended), ch); + + for (int sl=0; sl<2; sl++) { + OPL3_SLOT *slot = &channel->SLOT[sl]; + device->save_item(NAME(slot->ar), ch*2+sl); + device->save_item(NAME(slot->dr), ch*2+sl); + device->save_item(NAME(slot->rr), ch*2+sl); + device->save_item(NAME(slot->KSR), ch*2+sl); + device->save_item(NAME(slot->ksl), ch*2+sl); + device->save_item(NAME(slot->ksr), ch*2+sl); + device->save_item(NAME(slot->mul), ch*2+sl); + + device->save_item(NAME(slot->Cnt), ch*2+sl); + device->save_item(NAME(slot->Incr), ch*2+sl); + device->save_item(NAME(slot->FB), ch*2+sl); + device->save_item(NAME(slot->conn_enum), ch*2+sl); + device->save_item(NAME(slot->op1_out), ch*2+sl); + device->save_item(NAME(slot->CON), ch*2+sl); + + device->save_item(NAME(slot->eg_type), ch*2+sl); + device->save_item(NAME(slot->state), ch*2+sl); + device->save_item(NAME(slot->TL), ch*2+sl); + device->save_item(NAME(slot->TLL), ch*2+sl); + device->save_item(NAME(slot->volume), ch*2+sl); + device->save_item(NAME(slot->sl), ch*2+sl); + + device->save_item(NAME(slot->eg_m_ar), ch*2+sl); + device->save_item(NAME(slot->eg_sh_ar), ch*2+sl); + device->save_item(NAME(slot->eg_sel_ar), ch*2+sl); + device->save_item(NAME(slot->eg_m_dr), ch*2+sl); + device->save_item(NAME(slot->eg_sh_dr), ch*2+sl); + device->save_item(NAME(slot->eg_sel_dr), ch*2+sl); + device->save_item(NAME(slot->eg_m_rr), ch*2+sl); + device->save_item(NAME(slot->eg_sh_rr), ch*2+sl); + device->save_item(NAME(slot->eg_sel_rr), ch*2+sl); + + device->save_item(NAME(slot->key), ch*2+sl); + + device->save_item(NAME(slot->AMmask), ch*2+sl); + device->save_item(NAME(slot->vib), ch*2+sl); + + device->save_item(NAME(slot->waveform_number), ch*2+sl); + device->save_item(NAME(slot->wavetable), ch*2+sl); + } + } + + device->save_item(NAME(chip->pan)); + device->save_item(NAME(chip->pan_ctrl_value)); + + device->save_item(NAME(chip->lfo_am_depth)); + device->save_item(NAME(chip->lfo_pm_depth_range)); + + device->save_item(NAME(chip->OPL3_mode)); + device->save_item(NAME(chip->rhythm)); + + device->save_item(NAME(chip->address)); + device->save_item(NAME(chip->status)); + device->save_item(NAME(chip->statusmask)); +} + +void * ymf262_init(device_t *device, int clock, int rate) +{ + void *chip = OPL3Create(device,clock,rate,OPL3_TYPE_YMF262); + OPL3_save_state((OPL3 *)chip, device); + + return chip; +} + +void ymf262_post_load(void *chip) { + OPL3 *opl3 = (OPL3 *)chip; + for (int ch=0; ch<18; ch++) { + for (int sl=0; sl<2; sl++) { + OPL3_SLOT_CONNECT(opl3, &(opl3->P_CH[ch].SLOT[sl])); + } + } +} + +void ymf262_shutdown(void *chip) +{ + OPL3Destroy((OPL3 *)chip); +} +void ymf262_reset_chip(void *chip) +{ + OPL3ResetChip((OPL3 *)chip); +} + +int ymf262_write(void *chip, int a, int v) +{ + return OPL3Write((OPL3 *)chip, a, v); +} + +unsigned char ymf262_read(void *chip, int a) +{ + /* Note on status register: */ + + /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */ + + /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */ + /* which can be used to identify the chip */ + + /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */ + + return OPL3Read((OPL3 *)chip, a); +} +int ymf262_timer_over(void *chip, int c) +{ + return OPL3TimerOver((OPL3 *)chip, c); +} + +void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, device_t *device) +{ + reinterpret_cast(chip)->SetTimerHandler(timer_handler, device); +} +void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device) +{ + reinterpret_cast(chip)->SetIRQHandler(IRQHandler, device); +} +void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device) +{ + reinterpret_cast(chip)->SetUpdateHandler(UpdateHandler, device); +} + + +/* +** Generate samples for one of the YMF262's +** +** 'which' is the virtual YMF262 number +** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D +** 'length' is the number of samples that should be generated +*/ +void ymf262_update_one(void *_chip, OPL3SAMPLE **buffers, int length) +{ + int i; + OPL3 *chip = (OPL3 *)_chip; + signed int *chanout = chip->chanout; + uint8_t rhythm = chip->rhythm&0x20; + + OPL3SAMPLE *ch_a = buffers[0]; + OPL3SAMPLE *ch_b = buffers[1]; + OPL3SAMPLE *ch_c = buffers[2]; + OPL3SAMPLE *ch_d = buffers[3]; + + for( i=0; i < length ; i++ ) + { + int a,b,c,d; + + + advance_lfo(chip); + + /* clear channel outputs */ + memset(chip->chanout, 0, sizeof(chip->chanout)); + +#if 1 + /* register set #1 */ + chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */ + if (chip->P_CH[0].extended) + chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */ + else + chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */ + + + chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */ + if (chip->P_CH[1].extended) + chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */ + else + chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */ + + + chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */ + if (chip->P_CH[2].extended) + chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */ + else + chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */ + + + if(!rhythm) + { + chan_calc(chip, &chip->P_CH[6]); + chan_calc(chip, &chip->P_CH[7]); + chan_calc(chip, &chip->P_CH[8]); + } + else /* Rhythm part */ + { + chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); + } + + /* register set #2 */ + chan_calc(chip, &chip->P_CH[ 9]); + if (chip->P_CH[9].extended) + chan_calc_ext(chip, &chip->P_CH[12]); + else + chan_calc(chip, &chip->P_CH[12]); + + + chan_calc(chip, &chip->P_CH[10]); + if (chip->P_CH[10].extended) + chan_calc_ext(chip, &chip->P_CH[13]); + else + chan_calc(chip, &chip->P_CH[13]); + + + chan_calc(chip, &chip->P_CH[11]); + if (chip->P_CH[11].extended) + chan_calc_ext(chip, &chip->P_CH[14]); + else + chan_calc(chip, &chip->P_CH[14]); + + + /* channels 15,16,17 are fixed 2-operator channels only */ + chan_calc(chip, &chip->P_CH[15]); + chan_calc(chip, &chip->P_CH[16]); + chan_calc(chip, &chip->P_CH[17]); +#endif + + /* accumulator register set #1 */ + a = chanout[0] & chip->pan[0]; + b = chanout[0] & chip->pan[1]; + c = chanout[0] & chip->pan[2]; + d = chanout[0] & chip->pan[3]; +#if 1 + a += chanout[1] & chip->pan[4]; + b += chanout[1] & chip->pan[5]; + c += chanout[1] & chip->pan[6]; + d += chanout[1] & chip->pan[7]; + a += chanout[2] & chip->pan[8]; + b += chanout[2] & chip->pan[9]; + c += chanout[2] & chip->pan[10]; + d += chanout[2] & chip->pan[11]; + + a += chanout[3] & chip->pan[12]; + b += chanout[3] & chip->pan[13]; + c += chanout[3] & chip->pan[14]; + d += chanout[3] & chip->pan[15]; + a += chanout[4] & chip->pan[16]; + b += chanout[4] & chip->pan[17]; + c += chanout[4] & chip->pan[18]; + d += chanout[4] & chip->pan[19]; + a += chanout[5] & chip->pan[20]; + b += chanout[5] & chip->pan[21]; + c += chanout[5] & chip->pan[22]; + d += chanout[5] & chip->pan[23]; + + a += chanout[6] & chip->pan[24]; + b += chanout[6] & chip->pan[25]; + c += chanout[6] & chip->pan[26]; + d += chanout[6] & chip->pan[27]; + a += chanout[7] & chip->pan[28]; + b += chanout[7] & chip->pan[29]; + c += chanout[7] & chip->pan[30]; + d += chanout[7] & chip->pan[31]; + a += chanout[8] & chip->pan[32]; + b += chanout[8] & chip->pan[33]; + c += chanout[8] & chip->pan[34]; + d += chanout[8] & chip->pan[35]; + + /* accumulator register set #2 */ + a += chanout[9] & chip->pan[36]; + b += chanout[9] & chip->pan[37]; + c += chanout[9] & chip->pan[38]; + d += chanout[9] & chip->pan[39]; + a += chanout[10] & chip->pan[40]; + b += chanout[10] & chip->pan[41]; + c += chanout[10] & chip->pan[42]; + d += chanout[10] & chip->pan[43]; + a += chanout[11] & chip->pan[44]; + b += chanout[11] & chip->pan[45]; + c += chanout[11] & chip->pan[46]; + d += chanout[11] & chip->pan[47]; + + a += chanout[12] & chip->pan[48]; + b += chanout[12] & chip->pan[49]; + c += chanout[12] & chip->pan[50]; + d += chanout[12] & chip->pan[51]; + a += chanout[13] & chip->pan[52]; + b += chanout[13] & chip->pan[53]; + c += chanout[13] & chip->pan[54]; + d += chanout[13] & chip->pan[55]; + a += chanout[14] & chip->pan[56]; + b += chanout[14] & chip->pan[57]; + c += chanout[14] & chip->pan[58]; + d += chanout[14] & chip->pan[59]; + + a += chanout[15] & chip->pan[60]; + b += chanout[15] & chip->pan[61]; + c += chanout[15] & chip->pan[62]; + d += chanout[15] & chip->pan[63]; + a += chanout[16] & chip->pan[64]; + b += chanout[16] & chip->pan[65]; + c += chanout[16] & chip->pan[66]; + d += chanout[16] & chip->pan[67]; + a += chanout[17] & chip->pan[68]; + b += chanout[17] & chip->pan[69]; + c += chanout[17] & chip->pan[70]; + d += chanout[17] & chip->pan[71]; +#endif + a >>= FINAL_SH; + b >>= FINAL_SH; + c >>= FINAL_SH; + d >>= FINAL_SH; + + /* limit check */ + a = limit( a , MAXOUT, MINOUT ); + b = limit( b , MAXOUT, MINOUT ); + c = limit( c , MAXOUT, MINOUT ); + d = limit( d , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + ch_a[i] = a; + ch_b[i] = b; + ch_c[i] = c; + ch_d[i] = d; + + advance(chip); + } + +} diff --git a/src/hardware/mame/ymf262.h b/src/hardware/mame/ymf262.h new file mode 100644 index 00000000..b82a89b9 --- /dev/null +++ b/src/hardware/mame/ymf262.h @@ -0,0 +1,40 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski +#ifndef MAME_SOUND_YMF262_H +#define MAME_SOUND_YMF262_H + +#pragma once + +/* select number of output bits: 8 or 16 */ +#define OPL3_SAMPLE_BITS 16 + +typedef stream_sample_t OPL3SAMPLE; +/* +#if (OPL3_SAMPLE_BITS==16) +typedef int16_t OPL3SAMPLE; +#endif +#if (OPL3_SAMPLE_BITS==8) +typedef int8_t OPL3SAMPLE; +#endif +*/ + +typedef void (*OPL3_TIMERHANDLER)(device_t *device,int timer,const attotime &period); +typedef void (*OPL3_IRQHANDLER)(device_t *device,int irq); +typedef void (*OPL3_UPDATEHANDLER)(device_t *device,int min_interval_us); + + +void *ymf262_init(device_t *device, int clock, int rate); +void ymf262_post_load(void *chip); +void ymf262_shutdown(void *chip); +void ymf262_reset_chip(void *chip); +int ymf262_write(void *chip, int a, int v); +unsigned char ymf262_read(void *chip, int a); +int ymf262_timer_over(void *chip, int c); +void ymf262_update_one(void *chip, OPL3SAMPLE **buffers, int length); + +void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, device_t *device); +void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device); +void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device); + + +#endif // MAME_SOUND_YMF262_H From 051c0f3afda628fd535000a486f1daf92652fed6 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sat, 12 Aug 2017 09:17:33 +0000 Subject: [PATCH 3/7] Add emu.h header to make the mame files work without too many changes Small changes to remove or disable timer and update handlers Add cheap sample rate conversion to the sn76496 emulation Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4032 --- src/hardware/mame/emu.h | 123 ++++++++++++++++++++++++++++++++++ src/hardware/mame/fmopl.cpp | 24 ++++--- src/hardware/mame/fmopl.h | 6 +- src/hardware/mame/saa1099.cpp | 3 +- src/hardware/mame/saa1099.h | 2 +- src/hardware/mame/sn76496.cpp | 45 +++++++++---- src/hardware/mame/sn76496.h | 20 ++++-- src/hardware/mame/ymf262.cpp | 12 ++-- 8 files changed, 197 insertions(+), 38 deletions(-) create mode 100644 src/hardware/mame/emu.h diff --git a/src/hardware/mame/emu.h b/src/hardware/mame/emu.h new file mode 100644 index 00000000..ace02d29 --- /dev/null +++ b/src/hardware/mame/emu.h @@ -0,0 +1,123 @@ +#ifndef DOSBOX_EMU_H +#define DOSBOX_EMU_H + + +#include "dosbox.h" +#include +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +typedef Bit16s stream_sample_t; + +typedef Bit8u u8; +typedef Bit32u u32; + +class device_t; +struct machine_config; + +#define NAME( _ASDF_ ) 0 +#define BIT( _INPUT_, _BIT_ ) ( ( _INPUT_) >> (_BIT_)) & 1 + +#define ATTR_UNUSED +#define DECLARE_READ8_MEMBER(name) u8 name( int, int) +#define DECLARE_WRITE8_MEMBER(name) void name( int, int, u8 data) +#define READ8_MEMBER(name) u8 name( int, int) +#define WRITE8_MEMBER(name) void name( int offset, int space, u8 data) + +#define DECLARE_DEVICE_TYPE(Type, Class) \ + extern const device_type Type; \ + class Class; + +#define DEFINE_DEVICE_TYPE(Type, Class, ShortName, FullName) \ + const device_type Type = 0; + + +class device_sound_interface { +public: + struct sound_stream { + void update() { + } + }; + sound_stream temp; + + sound_stream* stream_alloc(int whatever, int channels, int size) { + return &temp; + }; + + + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) = 0; + + device_sound_interface(const machine_config &mconfig, device_t& _device) { + } + +}; + +struct attotime { + int whatever; + + static attotime from_hz(int hz) { + return attotime(); + } +}; + +struct machine_config { +}; + +typedef int device_type; + +class device_t { + u32 clockRate; +public: + struct machine_t { + int describe_context() const { + return 0; + } + }; + + machine_t machine() const { + return machine_t(); + } + + //int offset, space; + + u32 clock() const { + return clockRate; + } + + void logerror(const char* msg, ...) { + } + + static int tag() { + return 0; + } + + virtual void device_start() { + } + + void save_item(int wtf, int blah= 0) { + } + + device_t(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 _clock) : clockRate( _clock ) { + } + +}; + + + +static void auto_free(const device_t::machine_t& machine, void * buffer) { + free(buffer); +} + +#define auto_alloc_array_clear(m, t, c) calloc(c, sizeof(t) ) +#define auto_alloc_clear(m, t) static_cast( calloc(1, sizeof(t) ) ) + + + + +#endif \ No newline at end of file diff --git a/src/hardware/mame/fmopl.cpp b/src/hardware/mame/fmopl.cpp index a9c15478..36414cb7 100644 --- a/src/hardware/mame/fmopl.cpp +++ b/src/hardware/mame/fmopl.cpp @@ -379,7 +379,7 @@ struct FM_OPL uint32_t clock; /* master clock (Hz) */ uint32_t rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ - attotime TimerBase; /* Timer base time (==sampling time)*/ + //attotime TimerBase; /* Timer base time (==sampling time)*/ device_t *device; signed int phase_modulation; /* phase modulation input (SLOT 2) */ @@ -1434,7 +1434,7 @@ void FM_OPL::initialize() /*logerror("freqbase=%f\n", freqbase);*/ /* Timer base time */ - TimerBase = attotime::from_hz(clock) * 72; + //TimerBase = attotime::from_hz(clock) * 72; /* make fnumber -> increment counter table */ for( i=0 ; i < 1024 ; i++ ) @@ -1514,6 +1514,7 @@ void FM_OPL::WriteReg(int r, int v) case 0x03: /* Timer 2 */ T[1] = (256-v)*16; break; +#if 0 case 0x04: /* IRQ clear / mask and Timer enable */ if(v&0x80) { /* IRQ flag clear */ @@ -1544,6 +1545,7 @@ void FM_OPL::WriteReg(int r, int v) } } break; +#endif #if BUILD_Y8950 case 0x06: /* Key Board OUT */ if(type&OPL_TYPE_KEYBOARD) @@ -1846,6 +1848,7 @@ void FM_OPL::postload() } // anonymous namespace +#if 0 static void OPLsave_state_channel(device_t *device, OPL_CH *CH) { int slot, ch; @@ -1887,7 +1890,6 @@ static void OPLsave_state_channel(device_t *device, OPL_CH *CH) } } - /* Register savestate for a virtual YM3812/YM3526Y8950 */ static void OPL_save_state(FM_OPL *OPL, device_t *device) @@ -1936,6 +1938,8 @@ static void OPL_save_state(FM_OPL *OPL, device_t *device) device->machine().save().register_postload(save_prepost_delegate(FUNC(FM_OPL::postload), OPL)); } +#endif + static void OPL_clock_changed(FM_OPL *OPL, uint32_t clock, uint32_t rate) { OPL->clock = clock; @@ -2103,7 +2107,6 @@ static inline void CSMKeyControll(OPL_CH *CH) CH->SLOT[SLOT2].KEYOFF(~4); } - static int OPLTimerOver(FM_OPL *OPL,int c) { if( c ) @@ -2123,15 +2126,14 @@ static int OPLTimerOver(FM_OPL *OPL,int c) } } /* reload timer */ - if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]); + //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]); return OPL->status>>7; } - #define MAX_OPL_CHIPS 2 -#if (BUILD_YM3812) +#if BUILD_YM3812 void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate) { @@ -2144,7 +2146,7 @@ void * ym3812_init(device_t *device, uint32_t clock, uint32_t rate) FM_OPL *YM3812 = OPLCreate(device,clock,rate,OPL_TYPE_YM3812); if (YM3812) { - OPL_save_state(YM3812, device); + //OPL_save_state(YM3812, device); ym3812_reset_chip(YM3812); } return YM3812; @@ -2197,7 +2199,6 @@ void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,device OPLSetUpdateHandler(YM3812, UpdateHandler, device); } - /* ** Generate samples for one of the YM3812's ** @@ -2277,7 +2278,7 @@ void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate) FM_OPL *YM3526 = OPLCreate(device,clock,rate,OPL_TYPE_YM3526); if (YM3526) { - OPL_save_state(YM3526, device); + //OPL_save_state(YM3526, device); ym3526_reset_chip(YM3526); } return YM3526; @@ -2425,7 +2426,7 @@ void *y8950_init(device_t *device, uint32_t clock, uint32_t rate) /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ /* reset */ - OPL_save_state(Y8950, device); + //OPL_save_state(Y8950, device); y8950_reset_chip(Y8950); } @@ -2569,4 +2570,5 @@ void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,O OPL->keyboard_param = device; } + #endif diff --git a/src/hardware/mame/fmopl.h b/src/hardware/mame/fmopl.h index c5366d7b..c64a59c9 100644 --- a/src/hardware/mame/fmopl.h +++ b/src/hardware/mame/fmopl.h @@ -9,9 +9,9 @@ /* --- select emulation chips --- */ -#define BUILD_YM3812 (1) -#define BUILD_YM3526 (1) -#define BUILD_Y8950 (1) +#define BUILD_YM3812 1 +#define BUILD_YM3526 (0) +#define BUILD_Y8950 (0) /* select output bits size of output : 8 or 16 */ #define OPL_SAMPLE_BITS 16 diff --git a/src/hardware/mame/saa1099.cpp b/src/hardware/mame/saa1099.cpp index 3c9b334b..f588358a 100644 --- a/src/hardware/mame/saa1099.cpp +++ b/src/hardware/mame/saa1099.cpp @@ -125,7 +125,8 @@ static constexpr uint8_t envelope[8][64] = { // device type definition -DEFINE_DEVICE_TYPE(SAA1099, saa1099_device, "saa1099", "Philips SAA1099") +//DEFINE_DEVICE_TYPE(SAA1099, saa1099_device, "saa1099", "Philips SAA1099") +#define SAA1099 1 //************************************************************************** // LIVE DEVICE diff --git a/src/hardware/mame/saa1099.h b/src/hardware/mame/saa1099.h index 061f39ec..ee763454 100644 --- a/src/hardware/mame/saa1099.h +++ b/src/hardware/mame/saa1099.h @@ -37,7 +37,7 @@ public: DECLARE_WRITE8_MEMBER( write ); -protected: +//protected: // device-level overrides virtual void device_start() override; diff --git a/src/hardware/mame/sn76496.cpp b/src/hardware/mame/sn76496.cpp index bccb53c2..3f010380 100644 --- a/src/hardware/mame/sn76496.cpp +++ b/src/hardware/mame/sn76496.cpp @@ -128,7 +128,8 @@ #include "sn76496.h" #define MAX_OUTPUT 0x7fff - +//When you go over this create sample +#define RATE_MAX ( 1 << 30) sn76496_base_device::sn76496_base_device( const machine_config &mconfig, @@ -145,7 +146,7 @@ sn76496_base_device::sn76496_base_device( uint32_t clock) : device_t(mconfig, type, tag, owner, clock) , device_sound_interface(mconfig, *this) - , m_ready_handler(*this) +// , m_ready_handler(*this) , m_feedback_mask(feedbackmask) , m_whitenoise_tap1(noisetap1) , m_whitenoise_tap2(noisetap2) @@ -156,6 +157,7 @@ sn76496_base_device::sn76496_base_device( { } + sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock) { @@ -206,17 +208,19 @@ segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, d { } - void sn76496_base_device::device_start() { - int sample_rate = clock()/2; + sample_rate = clock()/2; + rate_add = RATE_MAX; + rate_counter = 0; + int i; double out; int gain; - m_ready_handler.resolve_safe(); + //m_ready_handler.resolve_safe(); - m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate); + //m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate); for (i = 0; i < 4; i++) m_volume[i] = 0; @@ -264,14 +268,14 @@ void sn76496_base_device::device_start() m_ready_state = true; - register_for_save_states(); + //register_for_save_states(); } WRITE8_MEMBER( sn76496_base_device::stereo_w ) { - m_sound->update(); - if (m_stereo) m_stereo_mask = data; - else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n"); +// m_sound->update(); +// if (m_stereo) m_stereo_mask = data; +// else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n"); } void sn76496_base_device::write(uint8_t data) @@ -279,7 +283,7 @@ void sn76496_base_device::write(uint8_t data) int n, r, c; // update the output buffer before changing the registers - m_sound->update(); +// m_sound->update(); // set number of cycles until READY is active; this is always one // 'sample', i.e. it equals the clock divider exactly @@ -347,12 +351,12 @@ void sn76496_base_device::countdown_cycles() if (m_cycles_to_ready > 0) { m_cycles_to_ready--; - if (m_ready_state==true) m_ready_handler(CLEAR_LINE); + //if (m_ready_state==true) m_ready_handler(CLEAR_LINE); m_ready_state = false; } else { - if (m_ready_state==false) m_ready_handler(ASSERT_LINE); + //if (m_ready_state==false) m_ready_handler(ASSERT_LINE); m_ready_state = true; } } @@ -412,6 +416,12 @@ void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sampl } } + //Skip final generation if you don't need an actual sample + rate_counter += rate_add; + if (rate_counter < RATE_MAX) + continue; + rate_counter -= RATE_MAX; + if (m_stereo) { out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0) @@ -433,13 +443,19 @@ void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sampl } if (m_negate) { out = -out; out2 = -out2; } - *(lbuffer++) = out; if (m_stereo) *(rbuffer++) = out2; samples--; } } + +void sn76496_base_device::convert_samplerate(int32_t target_rate) { + //Simple 10 bit shift for samplerate conversion + rate_add = (int32_t)( RATE_MAX * (target_rate / (double)sample_rate) ); + rate_counter = 0; +} + void sn76496_base_device::register_for_save_states() { save_item(NAME(m_vol_table)); @@ -472,3 +488,4 @@ DEFINE_DEVICE_TYPE(SN94624, sn94624_device, "sn94624", "SN94624") DEFINE_DEVICE_TYPE(NCR7496, ncr7496_device, "ncr7496", "NCR7496") DEFINE_DEVICE_TYPE(GAMEGEAR, gamegear_device, "gamegear_psg", "Game Gear PSG") DEFINE_DEVICE_TYPE(SEGAPSG, segapsg_device, "segapsg", "Sega VDP PSG") + diff --git a/src/hardware/mame/sn76496.h b/src/hardware/mame/sn76496.h index 22ad524e..0632aaad 100644 --- a/src/hardware/mame/sn76496.h +++ b/src/hardware/mame/sn76496.h @@ -17,21 +17,26 @@ DECLARE_DEVICE_TYPE(NCR7496, ncr7496_device) DECLARE_DEVICE_TYPE(GAMEGEAR, gamegear_device) DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device) +#if 0 + #define MCFG_SN76496_READY_HANDLER(cb) \ devcb = &sn76496_base_device::set_ready_handler(*device, (DEVCB_##cb)); +#endif + class sn76496_base_device : public device_t, public device_sound_interface { public: // static configuration helpers - template static devcb_base &set_ready_handler(device_t &device, Object &&cb) { return downcast(device).m_ready_handler.set_callback(std::forward(cb)); } +// template static devcb_base &set_ready_handler(device_t &device, Object &&cb) { return downcast(device).m_ready_handler.set_callback(std::forward(cb)); } DECLARE_WRITE8_MEMBER( stereo_w ); void write(uint8_t data); DECLARE_WRITE8_MEMBER( write ); - DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; } +// DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; } + void convert_samplerate(int32_t target_rate); protected: sn76496_base_device( const machine_config &mconfig, @@ -55,11 +60,13 @@ private: void register_for_save_states(); void countdown_cycles(); + + bool m_ready_state; - devcb_write_line m_ready_handler; + //devcb_write_line m_ready_handler; - sound_stream* m_sound; + //sound_stream* m_sound; const int32_t m_feedback_mask; // mask for feedback const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14) @@ -80,6 +87,11 @@ private: int32_t m_count[4]; // Position within the waveform int32_t m_output[4]; // 1-bit output of each channel, pre-volume int32_t m_cycles_to_ready; // number of cycles until the READY line goes active + //Modifications for easier sample conversion + int32_t sample_rate; + //Sample rate conversion + int32_t rate_add; + int32_t rate_counter; }; // SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf) diff --git a/src/hardware/mame/ymf262.cpp b/src/hardware/mame/ymf262.cpp index f69c15da..1fc77a92 100644 --- a/src/hardware/mame/ymf262.cpp +++ b/src/hardware/mame/ymf262.cpp @@ -279,7 +279,7 @@ struct OPL3 int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ - attotime TimerBase; /* Timer base time (==sampling time)*/ + //attotime TimerBase; /* Timer base time (==sampling time)*/ device_t *device; /* Optional handlers */ @@ -1351,7 +1351,7 @@ static void OPL3_initalize(OPL3 *chip) /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */ /* Timer base time */ - chip->TimerBase = attotime::from_hz(chip->clock) * (8*36); + //chip->TimerBase = attotime::from_hz(chip->clock) * (8*36); /* make fnumber -> increment counter table */ for( i=0 ; i < 1024 ; i++ ) @@ -1745,6 +1745,7 @@ static void OPL3WriteReg(OPL3 *chip, int r, int v) case 0x03: /* Timer 2 */ chip->T[1] = (256-v)*16; break; +#if 0 case 0x04: /* IRQ clear / mask and Timer enable */ if(v&0x80) { /* IRQ flags clear */ @@ -1774,6 +1775,7 @@ static void OPL3WriteReg(OPL3 *chip, int r, int v) if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period); } } +#endif break; case 0x08: /* x,NTS,x,x, x,x,x,x */ chip->nts = v; @@ -2356,7 +2358,7 @@ static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type) if (OPL3_LockTable(device) == -1) return nullptr; /* allocate memory block */ - chip = auto_alloc_clear(device->machine(), ()); + chip = auto_alloc_clear(device->machine(), OPL3 ); chip->device = device; chip->type = type; @@ -2452,11 +2454,12 @@ static int OPL3TimerOver(OPL3 *chip,int c) OPL3_STATUS_SET(chip,0x40); } /* reload timer */ - if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]); +// if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]); return chip->status>>7; } static void OPL3_save_state(OPL3 *chip, device_t *device) { +#if 0 for (int ch=0; ch<18; ch++) { OPL3_CH *channel = &chip->P_CH[ch]; device->save_item(NAME(channel->block_fnum), ch); @@ -2521,6 +2524,7 @@ static void OPL3_save_state(OPL3 *chip, device_t *device) { device->save_item(NAME(chip->address)); device->save_item(NAME(chip->status)); device->save_item(NAME(chip->statusmask)); +#endif } void * ymf262_init(device_t *device, int clock, int rate) From 7ef98ed1bf73c127b327967cecc3453155b725f1 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sat, 12 Aug 2017 09:26:38 +0000 Subject: [PATCH 4/7] Make the dosbox sound card implementations use the mame files directly Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4033 --- src/dosbox.cpp | 2 +- src/hardware/adlib.cpp | 89 +++++++ src/hardware/gameblaster.cpp | 453 +++++------------------------------ src/hardware/tandy_sound.cpp | 276 ++------------------- 4 files changed, 172 insertions(+), 648 deletions(-) diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 7308d8c5..d9a81612 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -558,7 +558,7 @@ void DOSBOX_Init(void) { Pstring->Set_values(oplmodes); Pstring->Set_help("Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'."); - const char* oplemus[]={ "default", "compat", "fast", 0}; + const char* oplemus[]={ "default", "compat", "fast", "mame", 0}; Pstring = secprop->Add_string("oplemu",Property::Changeable::WhenIdle,"default"); Pstring->Set_values(oplemus); Pstring->Set_help("Provider for the OPL emulation. compat might provide better quality (see oplrate as well)."); diff --git a/src/hardware/adlib.cpp b/src/hardware/adlib.cpp index 836a3e21..de56dcc6 100644 --- a/src/hardware/adlib.cpp +++ b/src/hardware/adlib.cpp @@ -28,6 +28,13 @@ #include "mem.h" #include "dbopl.h" +#include "mame/emu.h" +#include "mame/fmopl.h" +#include "mame/ymf262.h" + +#define OPL2_INTERNAL_FREQ 3600000 // The OPL2 operates at 3.6MHz +#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz + namespace OPL2 { #include "opl.cpp" @@ -85,6 +92,80 @@ namespace OPL3 { }; } +namespace MAMEOPL2 { + +struct Handler : public Adlib::Handler { + void* chip; + + virtual void WriteReg(Bit32u reg, Bit8u val) { + ym3812_write(chip, 0, reg); + ym3812_write(chip, 1, val); + } + virtual Bit32u WriteAddr(Bit32u port, Bit8u val) { + return val; + } + virtual void Generate(MixerChannel* chan, Bitu samples) { + Bit16s buf[1024 * 2]; + while (samples > 0) { + Bitu todo = samples > 1024 ? 1024 : samples; + samples -= todo; + ym3812_update_one(chip, buf, todo); + chan->AddSamples_m16(todo, buf); + } + } + virtual void Init(Bitu rate) { + chip = ym3812_init(0, OPL2_INTERNAL_FREQ, rate); + } + ~Handler() { + ym3812_shutdown(chip); + } +}; + +} + + +namespace MAMEOPL3 { + +struct Handler : public Adlib::Handler { + void* chip; + + virtual void WriteReg(Bit32u reg, Bit8u val) { + ymf262_write(chip, 0, reg); + ymf262_write(chip, 1, val); + } + virtual Bit32u WriteAddr(Bit32u port, Bit8u val) { + return val; + } + virtual void Generate(MixerChannel* chan, Bitu samples) { + //We generate data for 4 channels, but only the first 2 are connected on a pc + Bit16s buf[4][1024]; + Bit16s result[1024][2]; + Bit16s* buffers[4] = { buf[0], buf[1], buf[2], buf[3] }; + + while (samples > 0) { + Bitu todo = samples > 1024 ? 1024 : samples; + samples -= todo; + ymf262_update_one(chip, buffers, todo); + //Interleave the samples before mixing + for (Bitu i = 0; i < todo; i++) { + result[i][0] = buf[0][i]; + result[i][1] = buf[1][i]; + } + chan->AddSamples_s16(todo, result[0]); + } + } + virtual void Init(Bitu rate) { + chip = ymf262_init(0, OPL3_INTERNAL_FREQ, rate); + } + ~Handler() { + ymf262_shutdown(chip); + } +}; + +} + + + #define RAW_SIZE 1024 @@ -717,6 +798,14 @@ Module::Module( Section* configuration ) : Module_base(configuration) { } else { handler = new OPL3::Handler(); } + } + else if (oplemu == "mame") { + if (oplmode == OPL_opl2) { + handler = new MAMEOPL2::Handler(); + } + else { + handler = new MAMEOPL3::Handler(); + } } else { handler = new DBOPL::Handler(); } diff --git a/src/hardware/gameblaster.cpp b/src/hardware/gameblaster.cpp index a3906054..dbe3b093 100644 --- a/src/hardware/gameblaster.cpp +++ b/src/hardware/gameblaster.cpp @@ -27,408 +27,75 @@ #include #include +#include "mame/emu.h" +#include "mame/saa1099.h" -#define LEFT 0x00 -#define RIGHT 0x01 -#define CMS_BUFFER_SIZE 128 -#define CMS_RATE 22050 -/*#define MASTER_CLOCK 14318180/2 */ #define MASTER_CLOCK 7159090 - -typedef Bit8u UINT8; -typedef Bit16s INT16; - -/* this structure defines a channel */ -struct saa1099_channel -{ - int frequency; /* frequency (0x00..0xff) */ - int freq_enable; /* frequency enable */ - int noise_enable; /* noise enable */ - int octave; /* octave (0x00..0x07) */ - int amplitude[2]; /* amplitude (0x00..0x0f) */ - int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ - - /* vars to simulate the square wave */ - double counter; - double freq; - int level; -}; - -/* this structure defines a noise channel */ -struct saa1099_noise -{ - /* vars to simulate the noise generator output */ - double counter; - double freq; - int level; /* noise polynomal shifter */ -}; - -/* this structure defines a SAA1099 chip */ -struct SAA1099 -{ - int stream; /* our stream */ - int noise_params[2]; /* noise generators parameters */ - int env_enable[2]; /* envelope generators enable */ - int env_reverse_right[2]; /* envelope reversed for right channel */ - int env_mode[2]; /* envelope generators mode */ - int env_bits[2]; /* non zero = 3 bits resolution */ - int env_clock[2]; /* envelope clock mode (non-zero external) */ - int env_step[2]; /* current envelope step */ - int all_ch_enable; /* all channels enable */ - int sync_state; /* sync all channels */ - int selected_reg; /* selected register */ - struct saa1099_channel channels[6]; /* channels */ - struct saa1099_noise noise[2]; /* noise generators */ -}; - -static const UINT8 envelope[8][64] = { - /* zero amplitude */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* maximum amplitude */ - {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, - /* single decay */ - {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive decay */ - {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, - /* single triangular */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive triangular */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, - /* single attack */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive attack */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } -}; - - -static const int amplitude_lookup[16] = { - 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, - 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, - 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, - 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 -}; - -/* global parameters */ -static double sample_rate; -static SAA1099 saa1099[2]; +//My mixer channel static MixerChannel * cms_chan; -static Bit16s cms_buffer[2][2][CMS_BUFFER_SIZE]; -static Bit16s * cms_buf_point[4] = { - cms_buffer[0][0],cms_buffer[0][1],cms_buffer[1][0],cms_buffer[1][1] }; - -static Bitu last_command; -static Bitu base_port; - - -static void saa1099_envelope(int chip, int ch) -{ - struct SAA1099 *saa = &saa1099[chip]; - if (saa->env_enable[ch]) - { - int step, mode, mask; - mode = saa->env_mode[ch]; - /* step from 0..63 and then loop in steps 32..63 */ - step = saa->env_step[ch] = - ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); - - mask = 15; - if (saa->env_bits[ch]) - mask &= ~1; /* 3 bit resolution, mask LSB */ - - saa->channels[ch*3+0].envelope[ LEFT] = - saa->channels[ch*3+1].envelope[ LEFT] = - saa->channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask; - if (saa->env_reverse_right[ch] & 0x01) - { - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask; - } - else - { - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask; - } - } - else - { - /* envelope mode off, set all envelope factors to 16 */ - saa->channels[ch*3+0].envelope[ LEFT] = - saa->channels[ch*3+1].envelope[ LEFT] = - saa->channels[ch*3+2].envelope[ LEFT] = - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = 16; - } -} - - -static void saa1099_update(int chip, INT16 **buffer, int length) -{ - struct SAA1099 *saa = &saa1099[chip]; - int j, ch; - - /* if the channels are disabled we're done */ - if (!saa->all_ch_enable) - { - /* init output data */ - memset(buffer[LEFT],0,length*sizeof(INT16)); - memset(buffer[RIGHT],0,length*sizeof(INT16)); - return; - } - - for (ch = 0; ch < 2; ch++) - { - switch (saa->noise_params[ch]) - { - case 0: saa->noise[ch].freq = MASTER_CLOCK/256 * 2; break; - case 1: saa->noise[ch].freq = MASTER_CLOCK/512 * 2; break; - case 2: saa->noise[ch].freq = MASTER_CLOCK/1024 * 2; break; - case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; - } - } - - /* fill all data needed */ - for( j = 0; j < length; j++ ) - { - int output_l = 0, output_r = 0; - - /* for each channel */ - for (ch = 0; ch < 6; ch++) - { - if (saa->channels[ch].freq == 0.0) - saa->channels[ch].freq = (double)((2 * MASTER_CLOCK/512) << saa->channels[ch].octave) / - (511.0 - (double)saa->channels[ch].frequency); - - /* check the actual position in the square wave */ - saa->channels[ch].counter -= saa->channels[ch].freq; - while (saa->channels[ch].counter < 0) - { - /* calculate new frequency now after the half wave is updated */ - saa->channels[ch].freq = (double)((2 * MASTER_CLOCK/512) << saa->channels[ch].octave) / - (511.0 - (double)saa->channels[ch].frequency); - - saa->channels[ch].counter += sample_rate; - saa->channels[ch].level ^= 1; - - /* eventually clock the envelope counters */ - if (ch == 1 && saa->env_clock[0] == 0) - saa1099_envelope(chip, 0); - if (ch == 4 && saa->env_clock[1] == 0) - saa1099_envelope(chip, 1); - } - - /* if the noise is enabled */ - if (saa->channels[ch].noise_enable) - { - /* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */ - if (saa->noise[ch/3].level & 1) - { - /* subtract to avoid overflows, also use only half amplitude */ - output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2; - output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2; - } - } - - /* if the square wave is enabled */ - if (saa->channels[ch].freq_enable) - { - /* if the channel level is high */ - if (saa->channels[ch].level & 1) - { - output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; - output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; - } - } - } - - for (ch = 0; ch < 2; ch++) - { - /* check the actual position in noise generator */ - saa->noise[ch].counter -= saa->noise[ch].freq; - while (saa->noise[ch].counter < 0) - { - saa->noise[ch].counter += sample_rate; - if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) - saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; - else - saa->noise[ch].level <<= 1; - } - } - /* write sound data to the buffer */ - buffer[LEFT][j] = output_l / 6; - buffer[RIGHT][j] = output_r / 6; - } -} - -static void saa1099_write_port_w( int chip, int offset, int data ) -{ - struct SAA1099 *saa = &saa1099[chip]; - if(offset == 1) { - // address port - saa->selected_reg = data & 0x1f; - if (saa->selected_reg == 0x18 || saa->selected_reg == 0x19) { - /* clock the envelope channels */ - if (saa->env_clock[0]) saa1099_envelope(chip,0); - if (saa->env_clock[1]) saa1099_envelope(chip,1); - } - return; - } - int reg = saa->selected_reg; - int ch; - - switch (reg) - { - /* channel i amplitude */ - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: - ch = reg & 7; - saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; - saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; - break; - /* channel i frequency */ - case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: - ch = reg & 7; - saa->channels[ch].frequency = data & 0xff; - break; - /* channel i octave */ - case 0x10: case 0x11: case 0x12: - ch = (reg - 0x10) << 1; - saa->channels[ch + 0].octave = data & 0x07; - saa->channels[ch + 1].octave = (data >> 4) & 0x07; - break; - /* channel i frequency enable */ - case 0x14: - saa->channels[0].freq_enable = data & 0x01; - saa->channels[1].freq_enable = data & 0x02; - saa->channels[2].freq_enable = data & 0x04; - saa->channels[3].freq_enable = data & 0x08; - saa->channels[4].freq_enable = data & 0x10; - saa->channels[5].freq_enable = data & 0x20; - break; - /* channel i noise enable */ - case 0x15: - saa->channels[0].noise_enable = data & 0x01; - saa->channels[1].noise_enable = data & 0x02; - saa->channels[2].noise_enable = data & 0x04; - saa->channels[3].noise_enable = data & 0x08; - saa->channels[4].noise_enable = data & 0x10; - saa->channels[5].noise_enable = data & 0x20; - break; - /* noise generators parameters */ - case 0x16: - saa->noise_params[0] = data & 0x03; - saa->noise_params[1] = (data >> 4) & 0x03; - break; - /* envelope generators parameters */ - case 0x18: case 0x19: - ch = reg - 0x18; - saa->env_reverse_right[ch] = data & 0x01; - saa->env_mode[ch] = (data >> 1) & 0x07; - saa->env_bits[ch] = data & 0x10; - saa->env_clock[ch] = data & 0x20; - saa->env_enable[ch] = data & 0x80; - /* reset the envelope */ - saa->env_step[ch] = 0; - break; - /* channels enable & reset generators */ - case 0x1c: - saa->all_ch_enable = data & 0x01; - saa->sync_state = data & 0x02; - if (data & 0x02) - { - int i; -// logerror("%04x: (SAA1099 #%d) -reg 0x1c- Chip reset\n",activecpu_get_pc(), chip); - /* Synch & Reset generators */ - for (i = 0; i < 6; i++) - { - saa->channels[i].level = 0; - saa->channels[i].counter = 0.0; - } - } - break; - default: /* Error! */ -// logerror("%04x: (SAA1099 #%d) Unknown operation (reg:%02x, data:%02x)\n",activecpu_get_pc(), chip, reg, data); - LOG(LOG_MISC,LOG_ERROR)("CMS Unkown write to reg %x with %x",reg, data); - } -} +//Timer to disable the channel after a while +static Bit32u lastWriteTicks; +static Bit32u cmsBase; +static saa1099_device* device[2]; static void write_cms(Bitu port, Bitu val, Bitu /* iolen */) { if(cms_chan && (!cms_chan->enabled)) cms_chan->Enable(true); - last_command = PIC_Ticks; - switch (port-base_port) { - case 0: - saa1099_write_port_w(0,0,val); - break; + lastWriteTicks = PIC_Ticks; + switch ( port - cmsBase ) { case 1: - saa1099_write_port_w(0,1,val); + device[0]->control_w(0, 0, val); break; - case 2: - saa1099_write_port_w(1,0,val); + case 0: + device[0]->data_w(0, 0, val); break; case 3: - saa1099_write_port_w(1,1,val); + device[1]->control_w(0, 0, val); + break; + case 2: + device[1]->data_w(0, 0, val); break; } } static void CMS_CallBack(Bitu len) { - if (len > CMS_BUFFER_SIZE) return; + enum { + BUFFER_SIZE = 2048 + }; - saa1099_update(0, &cms_buf_point[0], (int)len); - saa1099_update(1, &cms_buf_point[2], (int)len); + if ( len > BUFFER_SIZE ) + return; - Bit16s * stream=(Bit16s *) MixTemp; - /* Mix chip outputs */ - for (Bitu l=0;lMAX_AUDIO) *stream=MAX_AUDIO; - else if (leftMAX_AUDIO) *stream=MAX_AUDIO; - else if (rightEnable( false ); + return; + } + Bit32s result[BUFFER_SIZE][2]; + Bit16s work[2][BUFFER_SIZE]; + Bit16s* buffers[2] = { work[0], work[1] }; + device_sound_interface::sound_stream stream; + device[0]->sound_stream_update(stream, 0, buffers, len); + for (Bitu i = 0; i < len; i++) { + result[i][0] = work[0][i]; + result[i][1] = work[1][i]; + } + device[1]->sound_stream_update(stream, 0, buffers, len); + for (Bitu i = 0; i < len; i++) { + result[i][0] += work[0][i]; + result[i][1] += work[1][i]; + } + cms_chan->AddSamples_s32( len, result[0] ); } - if(cms_chan) cms_chan->AddSamples_s16(len,(Bit16s *)MixTemp); - if (last_command + 10000 < PIC_Ticks) if(cms_chan) cms_chan->Enable(false); } // The Gameblaster detection static Bit8u cms_detect_register = 0xff; static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) { - switch(port-base_port) { + switch ( port - cmsBase ) { case 0x6: case 0x7: cms_detect_register = val; @@ -438,7 +105,7 @@ static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) { static Bitu read_cms_detect(Bitu port, Bitu /* iolen */) { Bit8u retval = 0xff; - switch(port-base_port) { + switch ( port - cmsBase ) { case 0x4: retval = 0x7f; break; @@ -461,31 +128,37 @@ private: public: CMS(Section* configuration):Module_base(configuration) { Section_prop * section = static_cast(configuration); - Bitu sample_rate_temp = section->Get_int("oplrate"); - sample_rate = static_cast(sample_rate_temp); - base_port = section->Get_hex("sbbase"); - WriteHandler.Install(base_port, write_cms, IO_MB,4); + Bitu sampleRate = section->Get_int( "oplrate" ); + cmsBase = section->Get_hex("sbbase"); + WriteHandler.Install( cmsBase, write_cms, IO_MB, 4 ); // A standalone Gameblaster has a magic chip on it which is // sometimes used for detection. const char * sbtype=section->Get_string("sbtype"); if (!strcasecmp(sbtype,"gb")) { - DetWriteHandler.Install(base_port+4,write_cms_detect,IO_MB,12); - DetReadHandler.Install(base_port,read_cms_detect,IO_MB,16); + DetWriteHandler.Install( cmsBase + 4, write_cms_detect, IO_MB, 12 ); + DetReadHandler.Install(cmsBase,read_cms_detect,IO_MB,16); } /* Register the Mixer CallBack */ - cms_chan = MixerChan.Install(CMS_CallBack,sample_rate_temp,"CMS"); + cms_chan = MixerChan.Install(CMS_CallBack,sampleRate,"CMS"); - last_command = PIC_Ticks; - - for (int s=0;s<2;s++) { - struct SAA1099 *saa = &saa1099[s]; - memset(saa, 0, sizeof(struct SAA1099)); - } + lastWriteTicks = PIC_Ticks; + + Bit32u freq = 7159000; //14318180 isa clock / 2 + + machine_config config; + device[0] = new saa1099_device(config, "", 0, 7159090); + device[1] = new saa1099_device(config, "", 0, 7159090); + + device[0]->device_start(); + device[1]->device_start(); } + ~CMS() { cms_chan = 0; + delete device[0]; + delete device[1]; } }; diff --git a/src/hardware/tandy_sound.cpp b/src/hardware/tandy_sound.cpp index 5d3ef2ea..3aef0ba7 100644 --- a/src/hardware/tandy_sound.cpp +++ b/src/hardware/tandy_sound.cpp @@ -30,47 +30,11 @@ #include "hardware.h" #include #include - -#define MAX_OUTPUT 0x7fff -#define STEP 0x10000 - -/* Formulas for noise generator */ -/* bit0 = output */ - -/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */ -#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */ - -/* noise feedback for periodic noise mode */ -//#define FB_PNOISE 0x10000 /* 16bit rorate */ -#define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */ - -/* -0x08000 is definitely wrong. The Master System conversion of Marble Madness -uses periodic noise as a baseline. With a 15-bit rotate, the bassline is -out of tune. -The 16-bit rotate has been confirmed against a real PAL Sega Master System 2. -Hope that helps the System E stuff, more news on the PSG as and when! -*/ - -/* noise generator start preset (for periodic noise) */ -#define NG_PRESET 0x0f35 +#include "mame/emu.h" +#include "mame/sn76496.h" -struct SN76496 { - int SampleRate; - unsigned int UpdateStep; - int VolTable[16]; /* volume table */ - int Register[8]; /* registers */ - int LastRegister; /* last register written */ - int Volume[4]; /* volume of voice 0-2 and noise */ - unsigned int RNG; /* noise generator */ - int NoiseFB; /* noise feedback mask */ - int Period[4]; - int Count[4]; - int Output[4]; -}; - -static struct SN76496 sn; +#define SOUND_CLOCK (14318180 / 4) #define TDAC_DMA_BUFSIZE 1024 @@ -99,221 +63,36 @@ static struct { } dac; } tandy; +static sn76496_device device(machine_config(), 0, 0, SOUND_CLOCK ); + static void SN76496Write(Bitu /*port*/,Bitu data,Bitu /*iolen*/) { - struct SN76496 *R = &sn; - tandy.last_write=PIC_Ticks; if (!tandy.enabled) { tandy.chan->Enable(true); tandy.enabled=true; } - - /* update the output buffer before changing the registers */ - - if (data & 0x80) - { - int r = (data & 0x70) >> 4; - int c = r/2; - - R->LastRegister = r; - R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); - switch (r) - { - case 0: /* tone 0 : frequency */ - case 2: /* tone 1 : frequency */ - case 4: /* tone 2 : frequency */ - R->Period[c] = R->UpdateStep * R->Register[r]; - if (R->Period[c] == 0) R->Period[c] = 0x3fe; - if (r == 4) - { - /* update noise shift frequency */ - if ((R->Register[6] & 0x03) == 0x03) - R->Period[3] = 2 * R->Period[2]; - } - break; - case 1: /* tone 0 : volume */ - case 3: /* tone 1 : volume */ - case 5: /* tone 2 : volume */ - case 7: /* noise : volume */ - R->Volume[c] = R->VolTable[data & 0x0f]; - break; - case 6: /* noise : frequency, mode */ - { - int n = R->Register[6]; - R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE; - n &= 3; - /* N/512,N/1024,N/2048,Tone #3 output */ - R->Period[3] = (n == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+n)); - - /* reset noise shifter */ -// R->RNG = NG_PRESET; -// R->Output[3] = R->RNG & 1; - } - break; - } - } - else - { - int r = R->LastRegister; - int c = r/2; - - switch (r) - { - case 0: /* tone 0 : frequency */ - case 2: /* tone 1 : frequency */ - case 4: /* tone 2 : frequency */ - R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4); - R->Period[c] = R->UpdateStep * R->Register[r]; - if (R->Period[c] == 0) R->Period[c] = 0x3fe; - if (r == 4) - { - /* update noise shift frequency */ - if ((R->Register[6] & 0x03) == 0x03) - R->Period[3] = 2 * R->Period[2]; - } - break; - } - } + device.write(data); } static void SN76496Update(Bitu length) { + //Disable the channel if it's been quiet for a while if ((tandy.last_write+5000)Enable(false); + return; } - int i; - struct SN76496 *R = &sn; - Bit16s * buffer=(Bit16s *)MixTemp; + const Bitu MAX_SAMPLES = 2048; + if (length > MAX_SAMPLES) + return; + Bit16s buffer[MAX_SAMPLES]; + Bit16s* outputs = buffer; - /* If the volume is 0, increase the counter */ - for (i = 0;i < 4;i++) - { - if (R->Volume[i] == 0) - { - /* note that I do count += length, NOT count = length + 1. You might think */ - /* it's the same since the volume is 0, but doing the latter could cause */ - /* interferencies when the program is rapidly modulating the volume. */ - if (R->Count[i] <= (int)length*STEP) R->Count[i] += length*STEP; - } - } - - Bitu count=length; - while (count) - { - int vol[4]; - unsigned int out; - int left; - - - /* vol[] keeps track of how long each square wave stays */ - /* in the 1 position during the sample period. */ - vol[0] = vol[1] = vol[2] = vol[3] = 0; - - for (i = 0;i < 3;i++) - { - if (R->Output[i]) vol[i] += R->Count[i]; - R->Count[i] -= STEP; - /* Period[i] is the half period of the square wave. Here, in each */ - /* loop I add Period[i] twice, so that at the end of the loop the */ - /* square wave is in the same status (0 or 1) it was at the start. */ - /* vol[i] is also incremented by Period[i], since the wave has been 1 */ - /* exactly half of the time, regardless of the initial position. */ - /* If we exit the loop in the middle, Output[i] has to be inverted */ - /* and vol[i] incremented only if the exit status of the square */ - /* wave is 1. */ - while (R->Count[i] <= 0) - { - R->Count[i] += R->Period[i]; - if (R->Count[i] > 0) - { - R->Output[i] ^= 1; - if (R->Output[i]) vol[i] += R->Period[i]; - break; - } - R->Count[i] += R->Period[i]; - vol[i] += R->Period[i]; - } - if (R->Output[i]) vol[i] -= R->Count[i]; - } - - left = STEP; - do - { - int nextevent; - - - if (R->Count[3] < left) nextevent = R->Count[3]; - else nextevent = left; - - if (R->Output[3]) vol[3] += R->Count[3]; - R->Count[3] -= nextevent; - if (R->Count[3] <= 0) - { - if (R->RNG & 1) R->RNG ^= R->NoiseFB; - R->RNG >>= 1; - R->Output[3] = R->RNG & 1; - R->Count[3] += R->Period[3]; - if (R->Output[3]) vol[3] += R->Period[3]; - } - if (R->Output[3]) vol[3] -= R->Count[3]; - - left -= nextevent; - } while (left > 0); - - out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] + - vol[2] * R->Volume[2] + vol[3] * R->Volume[3]; - - if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP; - - *(buffer++) = (Bit16s)(out / STEP); - - count--; - } - tandy.chan->AddSamples_m16(length,(Bit16s *)MixTemp); + device_sound_interface::sound_stream stream; + static_cast(device).sound_stream_update(stream, 0, &outputs, length); + tandy.chan->AddSamples_m16(length, buffer); } - - -static void SN76496_set_clock(int clock) { - struct SN76496 *R = &sn; - - /* the base clock for the tone generators is the chip clock divided by 16; */ - /* for the noise generator, it is clock / 256. */ - /* Here we calculate the number of steps which happen during one sample */ - /* at the given sample rate. No. of events = sample rate / (clock/16). */ - /* STEP is a multiplier used to turn the fraction into a fixed point */ - /* number. */ - R->UpdateStep = (unsigned int)(((double)STEP * R->SampleRate * 16) / clock); -} - - -static void SN76496_set_gain(int gain) { - struct SN76496 *R = &sn; - int i; - double out; - - gain &= 0xff; - - /* increase max output basing on gain (0.2 dB per step) */ - out = MAX_OUTPUT / 3; - while (gain-- > 0) - out *= 1.023292992; /* = (10 ^ (0.2/20)) */ - - /* build volume table (2dB per step) */ - for (i = 0;i < 15;i++) - { - /* limit volume to avoid clipping */ - if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3; - else R->VolTable[i] = (int)out; - - out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ - } - R->VolTable[15] = 0; -} - - - bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma) { tsaddr=0; tsirq =0; @@ -548,26 +327,9 @@ public: tandy.enabled=false; real_writeb(0x40,0xd4,0xff); /* BIOS Tandy DAC initialization value */ - Bitu i; - struct SN76496 *R = &sn; - R->SampleRate = sample_rate; - SN76496_set_clock(3579545); - for (i = 0;i < 4;i++) R->Volume[i] = 0; - R->LastRegister = 0; - for (i = 0;i < 8;i+=2) - { - R->Register[i] = 0; - R->Register[i + 1] = 0x0f; /* volume = 0 */ - } - - for (i = 0;i < 4;i++) - { - R->Output[i] = 0; - R->Period[i] = R->Count[i] = R->UpdateStep; - } - R->RNG = NG_PRESET; - R->Output[3] = R->RNG & 1; - SN76496_set_gain(0x1); + ((device_t&)device).device_start(); + device.convert_samplerate(sample_rate); + } ~TANDYSOUND(){ } }; From 5845ca5592799c258c46933400620d32cc8f0ea4 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sun, 13 Aug 2017 10:38:28 +0000 Subject: [PATCH 5/7] Remove some unneeded c++11 that's not even compiling properly Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4034 --- src/hardware/mame/fmopl.cpp | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/hardware/mame/fmopl.cpp b/src/hardware/mame/fmopl.cpp index 36414cb7..53154e43 100644 --- a/src/hardware/mame/fmopl.cpp +++ b/src/hardware/mame/fmopl.cpp @@ -106,8 +106,20 @@ Revision History: #define SIN_BITS 10 #define SIN_LEN (1<> 4 ) +#define LFO_AM_TAB_ELEMENTS 210 @@ -227,7 +239,7 @@ struct OPL_SLOT uint32_t TL; /* total level: TL << 2 */ int32_t TLL; /* adjusted now TL */ int32_t volume; /* envelope counter */ - uint32_t sl; /* sustain level: sl_tab[SL] */ + int32_t sl; /* sustain level: sl_tab[SL] */ uint8_t eg_sh_ar; /* (attack state) */ uint8_t eg_sel_ar; /* (attack state) */ uint8_t eg_sh_dr; /* (decay state) */ @@ -970,26 +982,9 @@ private: #endif } - - static constexpr uint32_t SC(uint32_t db) { return uint32_t(db * (2.0 / ENV_STEP)); } - - - static constexpr double DV = 0.1875 / 2.0; - - - /* TL_TAB_LEN is calculated as: - * 12 - sinus amplitude bits (Y axis) - * 2 - sinus sign bit (Y axis) - * TL_RES_LEN - sinus resolution (X axis) - */ - static constexpr unsigned TL_TAB_LEN = 12 * 2 * TL_RES_LEN; - static constexpr unsigned ENV_QUIET = TL_TAB_LEN >> 4; - - static constexpr unsigned LFO_AM_TAB_ELEMENTS = 210; - static const double ksl_tab[8*16]; static const uint32_t ksl_shift[4]; - static const uint32_t sl_tab[16]; + static const int32_t sl_tab[16]; static const unsigned char eg_inc[15 * RATE_STEPS]; static const uint8_t mul_tab[16]; @@ -1066,7 +1061,8 @@ const uint32_t FM_OPL::ksl_shift[4] = { 31, 1, 2, 0 }; /* sustain level table (3dB per step) */ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ -const uint32_t FM_OPL::sl_tab[16]={ +#define SC(db) int32_t(db * (2.0 / ENV_STEP)) +const int32_t FM_OPL::sl_tab[16]={ SC( 0),SC( 1),SC( 2),SC( 3),SC( 4),SC( 5),SC( 6),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) }; From 06b5f723b0d584a780ec28ff93a94c16d517e22d Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Thu, 24 Aug 2017 13:01:15 +0000 Subject: [PATCH 6/7] Update automake files for mame files Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4035 --- configure.ac | 1 + src/Makefile.am | 3 ++- src/hardware/Makefile.am | 2 +- src/hardware/mame/Makefile.am | 9 +++++++++ 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/hardware/mame/Makefile.am diff --git a/configure.ac b/configure.ac index dc2aa0ff..4f67fc32 100644 --- a/configure.ac +++ b/configure.ac @@ -533,6 +533,7 @@ src/dos/Makefile src/fpu/Makefile src/gui/Makefile src/hardware/Makefile +src/hardware/mame/Makefile src/hardware/serialport/Makefile src/ints/Makefile src/libs/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 184469e0..a4029e83 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,8 @@ endif dosbox_SOURCES = dosbox.cpp $(ico_stuff) dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \ - ints/libints.a misc/libmisc.a shell/libshell.a hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a + ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \ + hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a EXTRA_DIST = winres.rc dosbox.ico diff --git a/src/hardware/Makefile.am b/src/hardware/Makefile.am index 1ec7e67d..2d739482 100644 --- a/src/hardware/Makefile.am +++ b/src/hardware/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -SUBDIRS = serialport +SUBDIRS = serialport mame EXTRA_DIST = opl.cpp opl.h adlib.h dbopl.h pci_devices.h diff --git a/src/hardware/mame/Makefile.am b/src/hardware/mame/Makefile.am new file mode 100644 index 00000000..52f7f000 --- /dev/null +++ b/src/hardware/mame/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LIBRARIES = libmame.a +libmame_a_SOURCES = emu.h \ + fmopl.cpp fmopl.h \ + saa1099.cpp saa1099.h \ + sn76496.cpp sn76496.cpp \ + ymdeltat.cpp ymdeltat.h \ + ymf262.cpp ymf262.h From c4da3879ace9f0bc137eeaa0f061d3fa03e86ad0 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sat, 26 Aug 2017 05:49:40 +0000 Subject: [PATCH 7/7] Remove come c++11 stuff and hope I don't break anything Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4036 --- src/hardware/mame/fmopl.cpp | 17 +++++++---- src/hardware/mame/saa1099.cpp | 18 ++++++++++-- src/hardware/mame/saa1099.h | 52 +++++++++++++++++++++++++--------- src/hardware/mame/sn76496.cpp | 2 +- src/hardware/mame/sn76496.h | 4 +-- src/hardware/mame/ymdeltat.cpp | 6 ++-- src/hardware/mame/ymdeltat.h | 6 ++-- src/hardware/mame/ymf262.cpp | 4 +-- 8 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/hardware/mame/fmopl.cpp b/src/hardware/mame/fmopl.cpp index 53154e43..90b88345 100644 --- a/src/hardware/mame/fmopl.cpp +++ b/src/hardware/mame/fmopl.cpp @@ -1766,10 +1766,15 @@ void FM_OPL::ResetChip() for(int i = 0xff ; i >= 0x20 ; i-- ) WriteReg(i,0); /* reset operator parameters */ - for(OPL_CH &CH : P_CH) +// for(OPL_CH &CH : P_CH) + for(int ch = 0; ch < sizeof( P_CH )/ sizeof(P_CH[0]); ch++) { - for(OPL_SLOT &SLOT : CH.SLOT) + OPL_CH &CH = P_CH[ch]; +// for(OPL_SLOT &SLOT : CH.SLOT) + for(int slot = 0; slot < sizeof( CH.SLOT ) / sizeof( CH.SLOT[0]); slot++) { + + OPL_SLOT &SLOT = CH.SLOT[slot]; /* wave table */ SLOT.wavetable = 0; SLOT.state = EG_OFF; @@ -1793,15 +1798,17 @@ void FM_OPL::ResetChip() void FM_OPL::postload() { - for(OPL_CH &CH : P_CH) + for(int ch = 0; ch < sizeof( P_CH )/ sizeof(P_CH[0]); ch++) { + OPL_CH &CH = P_CH[ch]; /* Look up key scale level */ uint32_t const block_fnum = CH.block_fnum; CH.ksl_base = static_cast(ksl_tab[block_fnum >> 6]); CH.fc = fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); - for(OPL_SLOT &SLOT : CH.SLOT) + for(int slot = 0; slot < sizeof( CH.SLOT ) / sizeof( CH.SLOT[0]); slot++) { + OPL_SLOT &SLOT = CH.SLOT[slot]; /* Calculate key scale rate */ SLOT.ksr = CH.kcode >> SLOT.KSR; @@ -1955,7 +1962,7 @@ static FM_OPL *OPLCreate(device_t *device, uint32_t clock, uint32_t rate, int ty FM_OPL *OPL; int state_size; - if (FM_OPL::LockTable(device) == -1) return nullptr; + if (FM_OPL::LockTable(device) == -1) return 0; /* calculate OPL state size */ state_size = sizeof(FM_OPL); diff --git a/src/hardware/mame/saa1099.cpp b/src/hardware/mame/saa1099.cpp index f588358a..7f9823e9 100644 --- a/src/hardware/mame/saa1099.cpp +++ b/src/hardware/mame/saa1099.cpp @@ -73,14 +73,14 @@ #define LEFT 0x00 #define RIGHT 0x01 -static constexpr int amplitude_lookup[16] = { +static const int amplitude_lookup[16] = { 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 }; -static constexpr uint8_t envelope[8][64] = { +static const uint8_t envelope[8][64] = { /* zero amplitude */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -136,10 +136,13 @@ static constexpr uint8_t envelope[8][64] = { // saa1099_device - constructor //------------------------------------------------- +#define FILL_ARRAY( _FILL_ ) memset( _FILL_, 0, sizeof( _FILL_ ) ) + saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SAA1099, tag, owner, clock) , device_sound_interface(mconfig, *this) - , m_stream(nullptr) + , m_stream(0) +#if 0 , m_noise_params{ 0, 0 } , m_env_enable{ 0, 0 } , m_env_reverse_right{ 0, 0 } @@ -147,11 +150,20 @@ saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, d , m_env_bits{ 0, 0 } , m_env_clock{ 0, 0 } , m_env_step{ 0, 0 } +#endif , m_all_ch_enable(0) , m_sync_state(0) , m_selected_reg(0) , m_sample_rate(0.0) { + FILL_ARRAY( m_noise_params ); + FILL_ARRAY( m_env_enable ); + FILL_ARRAY( m_env_reverse_right ); + FILL_ARRAY( m_env_mode ); + FILL_ARRAY( m_env_bits ); + FILL_ARRAY( m_env_clock ); + FILL_ARRAY( m_env_step ); + } diff --git a/src/hardware/mame/saa1099.h b/src/hardware/mame/saa1099.h index ee763454..cce4fb55 100644 --- a/src/hardware/mame/saa1099.h +++ b/src/hardware/mame/saa1099.h @@ -23,6 +23,22 @@ // TYPE DEFINITIONS //************************************************************************** +//Container class for int that just initalizes to 0 +class NullInt { + int value; +public: + operator int& () { + return value; + } + + int& operator= ( int set ) { + value = set; + return value; + } + + NullInt( int set = 0 ) : value( set ) { + } +}; // ======================> saa1099_device @@ -39,37 +55,45 @@ public: //protected: // device-level overrides - virtual void device_start() override; + virtual void device_start(); // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); private: struct saa1099_channel { - saa1099_channel() : amplitude{ 0, 0 }, envelope{ 0, 0 } { } + saa1099_channel() { + //Quite hacky, but let's see how it goes + memset( this, 0, sizeof( *this ) ); + } - int frequency = 0; /* frequency (0x00..0xff) */ - int freq_enable = 0; /* frequency enable */ - int noise_enable = 0; /* noise enable */ - int octave = 0; /* octave (0x00..0x07) */ + int frequency ; /* frequency (0x00..0xff) */ + int freq_enable ; /* frequency enable */ + int noise_enable ; /* noise enable */ + int octave ; /* octave (0x00..0x07) */ int amplitude[2]; /* amplitude (0x00..0x0f) */ int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ /* vars to simulate the square wave */ - double counter = 0.0; - double freq = 0.0; - int level = 0; + double counter ; + double freq ; + int level ; + }; struct saa1099_noise { - saa1099_noise() { } + saa1099_noise() { + counter = 0; + freq = 0; + level = 0xFFFFFFFF; + } /* vars to simulate the noise generator output */ - double counter = 0.0; - double freq = 0.0; - uint32_t level = 0xFFFFFFFF; /* noise polynomial shifter */ + double counter; + double freq; + uint32_t level; /* noise polynomial shifter */ }; void envelope_w(int ch); diff --git a/src/hardware/mame/sn76496.cpp b/src/hardware/mame/sn76496.cpp index 3f010380..b2e3be6a 100644 --- a/src/hardware/mame/sn76496.cpp +++ b/src/hardware/mame/sn76496.cpp @@ -365,7 +365,7 @@ void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sampl { int i; stream_sample_t *lbuffer = outputs[0]; - stream_sample_t *rbuffer = (m_stereo)? outputs[1] : nullptr; + stream_sample_t *rbuffer = (m_stereo)? outputs[1] : 0; int16_t out; int16_t out2 = 0; diff --git a/src/hardware/mame/sn76496.h b/src/hardware/mame/sn76496.h index 0632aaad..a2bdb993 100644 --- a/src/hardware/mame/sn76496.h +++ b/src/hardware/mame/sn76496.h @@ -52,8 +52,8 @@ protected: device_t *owner, uint32_t clock); - virtual void device_start() override; - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + virtual void device_start(); + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); private: inline bool in_noise_mode(); diff --git a/src/hardware/mame/ymdeltat.cpp b/src/hardware/mame/ymdeltat.cpp index 29e05b4e..e5170138 100644 --- a/src/hardware/mame/ymdeltat.cpp +++ b/src/hardware/mame/ymdeltat.cpp @@ -80,13 +80,13 @@ /* Forecast to next Forecast (rate = *8) */ /* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ -static constexpr int32_t ym_deltat_decode_tableB1[16] = { +static const int32_t ym_deltat_decode_tableB1[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, }; /* delta to next delta (rate= *64) */ /* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ -static constexpr int32_t ym_deltat_decode_tableB2[16] = { +static const int32_t ym_deltat_decode_tableB2[16] = { 57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153 }; @@ -152,7 +152,7 @@ uint8_t YM_DELTAT::ADPCM_Read() /* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ -static constexpr uint8_t dram_rightshift[4]={3,0,0,0}; +static const uint8_t dram_rightshift[4]={3,0,0,0}; /* DELTA-T ADPCM write register */ void YM_DELTAT::ADPCM_Write(int r, int v) diff --git a/src/hardware/mame/ymdeltat.h b/src/hardware/mame/ymdeltat.h index f52bd46f..ad9e92ee 100644 --- a/src/hardware/mame/ymdeltat.h +++ b/src/hardware/mame/ymdeltat.h @@ -11,8 +11,10 @@ typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits); /* DELTA-T (adpcm type B) struct */ struct YM_DELTAT { /* AT: rearranged and tightened structure */ - static constexpr int EMULATION_MODE_NORMAL = 0; - static constexpr int EMULATION_MODE_YM2610 = 1; + enum { + EMULATION_MODE_NORMAL = 0, + EMULATION_MODE_YM2610 = 1, + }; uint8_t *memory; int32_t *output_pointer;/* pointer of output pointers */ diff --git a/src/hardware/mame/ymf262.cpp b/src/hardware/mame/ymf262.cpp index 1fc77a92..98056c5d 100644 --- a/src/hardware/mame/ymf262.cpp +++ b/src/hardware/mame/ymf262.cpp @@ -628,7 +628,7 @@ static int num_lock = 0; static inline void OPL3_SLOT_CONNECT(OPL3 *chip, OPL3_SLOT *slot) { if (slot->conn_enum == CONN_NULL) { - slot->connect = nullptr; + slot->connect = 0; } else if (slot->conn_enum >= CONN_CHAN0 && slot->conn_enum < CONN_PHASEMOD) { slot->connect = &chip->chanout[slot->conn_enum]; } else if (slot->conn_enum == CONN_PHASEMOD) { @@ -2355,7 +2355,7 @@ static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type) { OPL3 *chip; - if (OPL3_LockTable(device) == -1) return nullptr; + if (OPL3_LockTable(device) == -1) return 0; /* allocate memory block */ chip = auto_alloc_clear(device->machine(), OPL3 );