/* * ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone * Copyright (C) 2023 Kagamiin~ * * ESFMu is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 * of the License, or (at your option) any later version. * * ESFMu is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ESFMu. If not, see . */ /* * ESFMu wouldn't have been possible without the hard work and dedication of * the retro computer hardware research and preservation community. * * I'd like to thank: * - Nuke.YKT * Developer of Nuked OPL3, which was the basis for ESFMu's code and * also a great learning resource on Yamaha FM synthesis for myself. * Nuke.YKT also gives shoutouts on behalf of Nuked OPL3 to: * - MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): * Feedback and Rhythm part calculation information. * - forums.submarine.org.uk(carbon14, opl3): * Tremolo and phase generator calculation information. * - OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): * OPL2 ROMs. * - siliconpr0n.org(John McMaster, digshadow): * YMF262 and VRC VII decaps and die shots. * - rainwarrior * For performing the initial research on ESFM drivers and documenting * ESS's patent on native mode operator organization. * - jwt27 * For kickstarting the ESFM research project and compiling rainwarrior's * findings and more in an accessible document ("ESFM Demystified"). * - pachuco/CatButts * For documenting ESS's patent on ESFM's feedback implementation, which * was vital in getting ESFMu's sound output to be accurate. * - And everybody who helped out with real hardware testing */ #include #include #ifdef __cplusplus extern "C" { #endif typedef struct _esfm_slot esfm_slot; typedef struct _esfm_slot_internal esfm_slot_internal; typedef struct _esfm_channel esfm_channel; typedef struct _esfm_chip esfm_chip; void ESFM_init (esfm_chip *chip); void ESFM_write_reg (esfm_chip *chip, uint16_t address, uint8_t data); void ESFM_write_reg_buffered (esfm_chip *chip, uint16_t address, uint8_t data); void ESFM_write_reg_buffered_fast (esfm_chip *chip, uint16_t address, uint8_t data); void ESFM_write_port (esfm_chip *chip, uint8_t offset, uint8_t data); uint8_t ESFM_readback_reg (esfm_chip *chip, uint16_t address); uint8_t ESFM_read_port (esfm_chip *chip, uint8_t offset); void ESFM_generate(esfm_chip *chip, int16_t *buf); void ESFM_generate_stream(esfm_chip *chip, int16_t *sndptr, uint32_t num_samples); int16_t ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx); // These are fake types just for syntax sugar. // Beware of their underlying types when reading/writing to them. typedef uint8_t flag; typedef uint8_t uint2; typedef uint8_t uint3; typedef uint8_t uint4; typedef uint8_t uint5; typedef uint8_t uint6; typedef uint8_t uint8; typedef uint16_t uint9; typedef uint16_t uint10; typedef uint16_t uint11; typedef uint16_t uint12; typedef uint16_t uint16; typedef uint32_t uint19; typedef uint32_t uint23; typedef uint32_t uint32; typedef uint64_t uint36; typedef int16_t int13; typedef int16_t int14; typedef int16_t int16; typedef int32_t int32; enum eg_states { EG_ATTACK, EG_DECAY, EG_SUSTAIN, EG_RELEASE }; typedef struct _esfm_write_buf { uint64_t timestamp; uint16_t address; uint8_t data; flag valid; } esfm_write_buf; typedef struct _emu_slot_channel_mapping { int channel_idx; int slot_idx; } emu_slot_channel_mapping; typedef struct _esfm_slot_internal { uint9 eg_position; uint9 eg_ksl_offset; uint10 eg_output; uint4 keyscale; int13 output; int13 emu_output_enable; int13 emu_mod_enable; int13 feedback_buf; int13 *mod_input; uint19 phase_acc; uint10 phase_out; flag phase_reset; flag *key_on; flag key_on_gate; uint2 eg_state; flag eg_delay_run; flag eg_delay_transitioned_10; flag eg_delay_transitioned_10_gate; flag eg_delay_transitioned_01; flag eg_delay_transitioned_01_gate; uint16 eg_delay_counter; uint16 eg_delay_counter_compare; } esfm_slot_internal; struct _esfm_slot { // Metadata esfm_channel *channel; esfm_chip *chip; uint2 slot_idx; // Register data int13 out_enable[2]; uint10 f_num; uint3 block; uint3 output_level; // a.k.a. feedback level in emu mode uint3 mod_in_level; uint6 t_level; uint4 mult; uint3 waveform; // Only for 4th slot uint2 rhy_noise; uint4 attack_rate; uint4 decay_rate; uint4 sustain_lvl; uint4 release_rate; flag tremolo_en; flag tremolo_deep; flag vibrato_en; flag vibrato_deep; flag emu_connection_typ; flag env_sustaining; flag ksr; uint2 ksl; uint3 env_delay; // overlaps with env_delay bit 0 // TODO: check if emu mode only uses this, or if it actually overwrites the channel field used by native mode flag emu_key_on; // Internal state esfm_slot_internal in; }; struct _esfm_channel { esfm_chip *chip; esfm_slot slots[4]; uint5 channel_idx; int16 output[2]; flag key_on; flag emu_mode_4op_enable; // Only for 17th and 18th channels flag key_on_2; flag emu_mode_4op_enable_2; }; #define ESFM_WRITEBUF_SIZE 1024 #define ESFM_WRITEBUF_DELAY 2 struct _esfm_chip { esfm_channel channels[18]; int32 output_accm[2]; uint_fast16_t addr_latch; flag emu_wavesel_enable; flag emu_newmode; flag native_mode; flag keyscale_mode; // Global state uint36 eg_timer; uint10 global_timer; uint8 eg_clocks; flag eg_tick; flag eg_timer_overflow; uint8 tremolo; uint8 tremolo_pos; uint8 vibrato_pos; uint23 lfsr; flag rm_hh_bit2; flag rm_hh_bit3; flag rm_hh_bit7; flag rm_hh_bit8; flag rm_tc_bit3; flag rm_tc_bit5; // 0xbd register in emulation mode, exposed in 0x4bd in native mode // ("bass drum" register) uint8 emu_rhy_mode_flags; flag emu_vibrato_deep; flag emu_tremolo_deep; uint8 timer_reload[2]; uint8 timer_counter[2]; flag timer_enable[2]; flag timer_mask[2]; flag timer_overflow[2]; flag irq_bit; // Halts the envelope generators from advancing. flag test_bit_eg_halt; /* * Activates some sort of waveform test mode that amplifies the output volume greatly * and continuously shifts the waveform table downwards, possibly also outputting the * waveform's derivative? (it's so weird!) */ flag test_bit_distort; // Appears to attenuate the output by about 3 dB. flag test_bit_attenuate; // Resets all phase generators and holds them in the reset state while this bit is set. flag test_bit_phase_stop_reset; esfm_write_buf write_buf[ESFM_WRITEBUF_SIZE]; size_t write_buf_start; size_t write_buf_end; uint64_t write_buf_timestamp; }; #ifdef __cplusplus } #endif