282 lines
		
	
	
		
			7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 <https://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * 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 <stddef.h>
 | |
| #include <stdint.h>
 | |
| 
 | |
| #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, uint8_t fast);
 | |
| 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;
 | |
| 
 | |
| 	uint16 fb_out0;
 | |
| 	uint16 fb_out1;
 | |
| } 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;
 | |
| 
 | |
|         flag fast_mode;
 | |
| };
 | |
| 
 | |
| #ifdef __cplusplus
 | |
| }
 | |
| #endif
 | 
