#ifndef YMFM_OPM_H
#define YMFM_OPM_H
#pragma once
#include "ymfm.h"
#include "ymfm_fm.h"
namespace ymfm
{
class opm_registers : public fm_registers_base
{
static constexpr uint32_t LFO_WAVEFORM_LENGTH = 256;
public:
static constexpr uint32_t OUTPUTS = 2;
static constexpr uint32_t CHANNELS = 8;
static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
static constexpr uint32_t OPERATORS = CHANNELS * 4;
static constexpr uint32_t WAVEFORMS = 1;
static constexpr uint32_t REGISTERS = 0x100;
static constexpr uint32_t DEFAULT_PRESCALE = 2;
static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
static constexpr uint32_t REG_MODE = 0x14;
static constexpr uint8_t STATUS_TIMERA = 0x01;
static constexpr uint8_t STATUS_TIMERB = 0x02;
static constexpr uint8_t STATUS_BUSY = 0x80;
static constexpr uint8_t STATUS_IRQ = 0;
opm_registers();
void reset();
void save_restore(ymfm_saved_state &state);
static constexpr uint32_t channel_offset(uint32_t chnum)
{
assert(chnum < CHANNELS);
return chnum;
}
static constexpr uint32_t operator_offset(uint32_t opnum)
{
assert(opnum < OPERATORS);
return opnum;
}
struct operator_mapping { uint32_t chan[CHANNELS]; };
void operator_map(operator_mapping &dest) const;
bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask);
int32_t clock_noise_and_lfo();
uint32_t lfo_am_offset(uint32_t choffs) const;
uint32_t noise_state() const { return m_noise_state; }
void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache);
uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm);
std::string log_keyon(uint32_t choffs, uint32_t opoffs);
uint32_t test() const { return byte(0x01, 0, 8); }
uint32_t lfo_reset() const { return byte(0x01, 1, 1); }
uint32_t noise_frequency() const { return byte(0x0f, 0, 5) ^ 0x1f; }
uint32_t noise_enable() const { return byte(0x0f, 7, 1); }
uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); }
uint32_t timer_b_value() const { return byte(0x12, 0, 8); }
uint32_t csm() const { return byte(0x14, 7, 1); }
uint32_t reset_timer_b() const { return byte(0x14, 5, 1); }
uint32_t reset_timer_a() const { return byte(0x14, 4, 1); }
uint32_t enable_timer_b() const { return byte(0x14, 3, 1); }
uint32_t enable_timer_a() const { return byte(0x14, 2, 1); }
uint32_t load_timer_b() const { return byte(0x14, 1, 1); }
uint32_t load_timer_a() const { return byte(0x14, 0, 1); }
uint32_t lfo_rate() const { return byte(0x18, 0, 8); }
uint32_t lfo_am_depth() const { return byte(0x19, 0, 7); }
uint32_t lfo_pm_depth() const { return byte(0x1a, 0, 7); }
uint32_t output_bits() const { return byte(0x1b, 6, 2); }
uint32_t lfo_waveform() const { return byte(0x1b, 0, 2); }
uint32_t ch_output_any(uint32_t choffs) const { return byte(0x20, 6, 2, choffs); }
uint32_t ch_output_0(uint32_t choffs) const { return byte(0x20, 6, 1, choffs); }
uint32_t ch_output_1(uint32_t choffs) const { return byte(0x20, 7, 1, choffs); }
uint32_t ch_output_2(uint32_t choffs) const { return 0; }
uint32_t ch_output_3(uint32_t choffs) const { return 0; }
uint32_t ch_feedback(uint32_t choffs) const { return byte(0x20, 3, 3, choffs); }
uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x20, 0, 3, choffs); }
uint32_t ch_block_freq(uint32_t choffs) const { return word(0x28, 0, 7, 0x30, 2, 6, choffs); }
uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x38, 4, 3, choffs); }
uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x38, 0, 2, choffs); }
uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
uint32_t op_multiple(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); }
uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); }
uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); }
uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); }
uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0xa0, 0, 5, opoffs); }
uint32_t op_detune2(uint32_t opoffs) const { return byte(0xc0, 6, 2, opoffs); }
uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0xc0, 0, 5, opoffs); }
uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0xe0, 4, 4, opoffs); }
uint32_t op_release_rate(uint32_t opoffs) const { return byte(0xe0, 0, 4, opoffs); }
protected:
uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const
{
return bitfield(m_regdata[offset + extra_offset], start, count);
}
uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const
{
return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset);
}
uint32_t m_lfo_counter; uint32_t m_noise_lfsr; uint8_t m_noise_counter; uint8_t m_noise_state; uint8_t m_noise_lfo; uint8_t m_lfo_am; uint8_t m_regdata[REGISTERS]; int16_t m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; };
class ym2151
{
public:
using fm_engine = fm_engine_base<opm_registers>;
using output_data = fm_engine::output_data;
static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
ym2151(ymfm_interface &intf) : ym2151(intf, VARIANT_YM2151) { }
void reset();
void save_restore(ymfm_saved_state &state);
uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); }
void invalidate_caches() { m_fm.invalidate_caches(); }
uint8_t read_status();
uint8_t read(uint32_t offset);
void write_address(uint8_t data);
void write_data(uint8_t data);
void write(uint32_t offset, uint8_t data);
void generate(output_data *output, uint32_t numsamples = 1);
protected:
enum opm_variant
{
VARIANT_YM2151,
VARIANT_YM2164
};
ym2151(ymfm_interface &intf, opm_variant variant);
opm_variant m_variant; uint8_t m_address; fm_engine m_fm; };
class ym2164 : public ym2151
{
public:
ym2164(ymfm_interface &intf) : ym2151(intf, VARIANT_YM2164) { }
};
}
#endif