#ifndef YMFM_OPZ_H
#define YMFM_OPZ_H
#pragma once
#include "ymfm.h"
#include "ymfm_fm.h"
namespace ymfm
{
class opz_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 = 8;
static constexpr uint32_t REGISTERS = 0x190;
static constexpr uint32_t DEFAULT_PRESCALE = 2;
static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
static constexpr bool EG_HAS_REVERB = true;
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;
opz_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 noise_frequency() const { return byte(0x0f, 0, 5); }
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 lfo2_pm_depth() const { return byte(0x188, 0, 7); } uint32_t lfo2_rate() const { return byte(0x16, 0, 8); }
uint32_t lfo2_am_depth() const { return byte(0x17, 0, 7); }
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(0x189, 0, 7); } uint32_t output_bits() const { return byte(0x1b, 6, 2); }
uint32_t lfo2_sync() const { return byte(0x1b, 5, 1); }
uint32_t lfo_sync() const { return byte(0x1b, 4, 1); }
uint32_t lfo2_waveform() const { return byte(0x1b, 2, 2); }
uint32_t lfo_waveform() const { return byte(0x1b, 0, 2); }
uint32_t ch_volume(uint32_t choffs) const { return byte(0x00, 0, 8, choffs); }
uint32_t ch_output_any(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); }
uint32_t ch_output_0(uint32_t choffs) const { return byte(0x30, 0, 1, choffs); }
uint32_t ch_output_1(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 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_key_on(uint32_t choffs) const { return byte(0x20, 6, 1, choffs); }
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 ch_lfo2_pm_sens(uint32_t choffs) const { return byte(0x180, 4, 3, choffs); } uint32_t ch_lfo2_am_sens(uint32_t choffs) const { return byte(0x180, 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_fix_range(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }
uint32_t op_fix_frequency(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); }
uint32_t op_waveform(uint32_t opoffs) const { return byte(0x100, 4, 3, opoffs); } uint32_t op_fine(uint32_t opoffs) const { return byte(0x100, 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_fix_mode(uint32_t opoffs) const { return byte(0x80, 5, 1, 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_eg_shift(uint32_t opoffs) const { return byte(0x120, 6, 2, opoffs); } uint32_t op_reverb_rate(uint32_t opoffs) const { return byte(0x120, 0, 3, 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[2]; 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[2]; uint8_t m_regdata[REGISTERS]; uint16_t m_phase_substep[OPERATORS]; int16_t m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; };
class ym2414
{
public:
using fm_engine = fm_engine_base<opz_registers>;
static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS;
using output_data = fm_engine::output_data;
ym2414(ymfm_interface &intf);
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:
uint8_t m_address; fm_engine m_fm; };
}
#endif