diff --git a/src/include/86box/fifo.h b/src/include/86box/fifo.h
index e76189d8a..f87f932e0 100644
--- a/src/include/86box/fifo.h
+++ b/src/include/86box/fifo.h
@@ -1,3 +1,6 @@
+#ifndef FIFO_H
+#define FIFO_H
+
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
@@ -71,3 +74,5 @@ extern void fifo_reset(void *priv);
extern void fifo_reset_evt(void *priv);
extern void fifo_close(void *priv);
extern void *fifo_init(int len);
+
+#endif /*FIFO_H*/
diff --git a/src/include/86box/snd_opl.h b/src/include/86box/snd_opl.h
index 0d89589c4..441e2a119 100644
--- a/src/include/86box/snd_opl.h
+++ b/src/include/86box/snd_opl.h
@@ -22,7 +22,8 @@ enum fm_type {
FM_YMF262 = 1, /* OPL3 */
FM_YMF289B = 2, /* OPL3-L */
FM_YMF278B = 3, /* OPL 4 */
- FM_MAX = 4
+ FM_ESFM = 4, /* ESFM */
+ FM_MAX = 5
};
enum fm_driver {
@@ -45,6 +46,7 @@ extern uint8_t fm_driver_get(int chip_id, fm_drv_t *drv);
extern const fm_drv_t nuked_opl_drv;
extern const fm_drv_t ymfm_drv;
+extern const fm_drv_t esfmu_opl_drv;
#ifdef EMU_DEVICE_H
extern const device_t ym3812_nuked_device;
@@ -54,6 +56,8 @@ extern const device_t ym3812_ymfm_device;
extern const device_t ymf262_ymfm_device;
extern const device_t ymf289b_ymfm_device;
extern const device_t ymf278b_ymfm_device;
+
+extern const device_t esfm_esfmu_device;
#endif
#endif /*SOUND_OPL_H*/
diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h
index 621cb4ade..b26c5f06e 100644
--- a/src/include/86box/snd_sb.h
+++ b/src/include/86box/snd_sb.h
@@ -126,6 +126,43 @@ typedef struct sb_ct1745_mixer_t {
int output_filter; /* for clones */
} sb_ct1745_mixer_t;
+/* ESS AudioDrive */
+typedef struct ess_mixer_t {
+ double master_l;
+ double master_r;
+ double voice_l;
+ double voice_r;
+ double fm_l;
+ double fm_r;
+ double cd_l;
+ double cd_r;
+ double line_l;
+ double line_r;
+ double mic_l;
+ double mic_r;
+ double auxb_l;
+ double auxb_r;
+ double speaker;
+ /*see sb_ct1745_mixer for values for input selector*/
+ int32_t input_selector;
+ /* extra values for input selector */
+ #define INPUT_MIXER_L 128
+ #define INPUT_MIXER_R 256
+
+ int input_filter;
+ int in_filter_freq;
+ int output_filter;
+
+ int stereo;
+ int stereo_isleft;
+
+ uint8_t index;
+ uint8_t regs[256];
+
+ uint8_t ess_id_str[4];
+ uint8_t ess_id_str_pos;
+} ess_mixer_t;
+
typedef struct sb_t {
uint8_t cms_enabled;
uint8_t opl_enabled;
@@ -138,6 +175,7 @@ typedef struct sb_t {
sb_ct1335_mixer_t mixer_sb2;
sb_ct1345_mixer_t mixer_sbpro;
sb_ct1745_mixer_t mixer_sb16;
+ ess_mixer_t mixer_ess;
};
mpu_t *mpu;
emu8k_t emu8k;
@@ -163,6 +201,10 @@ extern void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *priv);
extern uint8_t sb_ct1745_mixer_read(uint16_t addr, void *priv);
extern void sb_ct1745_mixer_reset(sb_t *sb);
+extern void sb_ess_mixer_write(uint16_t addr, uint8_t val, void *priv);
+extern uint8_t sb_ess_mixer_read(uint16_t addr, void *priv);
+extern void sb_ess_mixer_reset(sb_t *sb);
+
extern void sb_get_buffer_sbpro(int32_t *buffer, int len, void *priv);
extern void sb_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv);
extern void sbpro_filter_cd_audio(int channel, double *buffer, void *priv);
diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h
index 3e0e40e80..a00f512a3 100644
--- a/src/include/86box/snd_sb_dsp.h
+++ b/src/include/86box/snd_sb_dsp.h
@@ -1,10 +1,16 @@
#ifndef SOUND_SND_SB_DSP_H
#define SOUND_SND_SB_DSP_H
+#include <86box/fifo.h>
+
/*Sound Blaster Clones, for quirks*/
#define SB_SUBTYPE_DEFAULT 0 /*Handle as a Creative card*/
#define SB_SUBTYPE_CLONE_AZT2316A_0X11 1 /*Aztech Sound Galaxy Pro 16 AB, DSP 3.1 - SBPRO2 clone*/
#define SB_SUBTYPE_CLONE_AZT1605_0X0C 2 /*Aztech Sound Galaxy Nova 16 Extra / Packard Bell Forte 16, DSP 2.1 - SBPRO2 clone*/
+#define SB_SUBTYPE_ESS_ES1688 3 /* ESS Technology ES1688 */
+
+/* ESS-related */
+#define IS_ESS(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_ESS_ES1688) /* check for future ESS cards here */
/* aztech-related */
#define IS_AZTECH(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11 || (dsp)->sb_subtype == SB_SUBTYPE_CLONE_AZT1605_0X0C) /* check for future AZT cards here */
@@ -125,6 +131,8 @@ typedef struct sb_dsp_t {
pc_timer_t wb_timer;
int wb_full;
+ pc_timer_t irq_timer;
+
int busy_count;
int record_pos_read;
@@ -135,6 +143,28 @@ typedef struct sb_dsp_t {
uint8_t azt_eeprom[AZTECH_EEPROM_SIZE]; /* the eeprom in the Aztech cards is attached to the DSP */
+ uint8_t ess_regs[256]; /* ESS registers. */
+ uint8_t ess_playback_mode;
+ uint8_t ess_extended_mode;
+ uint8_t ess_reload_len;
+ uint32_t ess_dma_counter;
+
+ /* IRQ status flags (0x22C) */
+ uint8_t ess_irq_generic;
+ uint8_t ess_irq_dmactr;
+
+ /* ESPCM */
+ fifo64_t *espcm_fifo;
+ uint8_t espcm_fifo_reset;
+ uint8_t espcm_mode; /* see ESPCM in "NON-PCM SAMPLE FORMATS" deflist in snd_sb_dsp.c */
+ uint8_t espcm_sample_idx;
+ uint8_t espcm_range;
+ uint8_t espcm_byte_buffer[4];
+ uint8_t espcm_code_buffer[19]; /* used for ESPCM_3 and for ESPCM_4 recording */
+ int8_t espcm_sample_buffer[19]; /* used for ESPCM_4 recording */
+ uint8_t espcm_table_index; /* used for ESPCM_3 */
+ uint8_t espcm_last_value; /* used for ESPCM_3 */
+
mpu_t *mpu;
} sb_dsp_t;
diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h
index 0359c0ff8..005b56d13 100644
--- a/src/include/86box/sound.h
+++ b/src/include/86box/sound.h
@@ -182,6 +182,10 @@ extern const device_t cmi8338_onboard_device;
extern const device_t cmi8738_device;
extern const device_t cmi8738_onboard_device;
extern const device_t cmi8738_6ch_onboard_device;
+
+/* ESS Technology */
+extern const device_t ess_1688_device;
+
#endif
#endif /*EMU_SOUND_H*/
diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt
index c1bbc811f..f67630c47 100644
--- a/src/sound/CMakeLists.txt
+++ b/src/sound/CMakeLists.txt
@@ -18,7 +18,7 @@ add_library(snd OBJECT sound.c snd_opl.c snd_opl_nuked.c snd_opl_ymfm.cpp snd_re
snd_lpt_dss.c snd_ps1.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c
snd_azt2316a.c snd_cms.c snd_cmi8x38.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c
snd_emu8k.c snd_mpu401.c snd_pas16.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c
- snd_optimc.c)
+ snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c)
if(OPENAL)
if(VCPKG_TOOLCHAIN)
diff --git a/src/sound/esfmu/esfm.c b/src/sound/esfmu/esfm.c
new file mode 100644
index 000000000..89fa82d4f
--- /dev/null
+++ b/src/sound/esfmu/esfm.c
@@ -0,0 +1,2327 @@
+/*
+ * ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone
+ * Copyright (C) 2023 Kagamiin~
+ *
+ * This file includes code and data from the Nuked OPL3 project, copyright (C)
+ * 2013-2023 Nuke.YKT. Its usage, modification and redistribution is allowed
+ * under the terms of the GNU Lesser General Public License version 2.1 or
+ * later.
+ *
+ * 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.
+ * - akumanatt
+ * For helping out with code optimization.
+ * - And everybody who helped out with real hardware testing
+ */
+
+#include "esfm.h"
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Log-scale quarter sine table extracted from OPL3 ROM; taken straight from
+ * Nuked OPL3 source code.
+ * TODO: Extract sine table from ESFM die scans... does ESFM even use a sine
+ * table? Patent documents give a hint to a possible method of generating sine
+ * waves using some sort of boolean logic wizardry (lol)
+ * Optimization: All 8 waveforms are calculated and unfolded from the actual
+ * data in OPL3's ROM. Negative entries are marked by 0x8000.
+ */
+static const uint16_t logsinrom[1024*8] = {
+ // wave 0
+ 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471,
+ 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365,
+ 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd,
+ 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261,
+ 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f,
+ 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd,
+ 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195,
+ 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166,
+ 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c,
+ 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118,
+ 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8,
+ 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db,
+ 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1,
+ 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9,
+ 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094,
+ 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081,
+ 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070,
+ 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060,
+ 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052,
+ 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045,
+ 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039,
+ 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f,
+ 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026,
+ 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e,
+ 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017,
+ 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011,
+ 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c,
+ 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007,
+ 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004,
+ 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004,
+ 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007,
+ 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b,
+ 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010,
+ 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016,
+ 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d,
+ 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025,
+ 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e,
+ 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038,
+ 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043,
+ 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050,
+ 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e,
+ 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e,
+ 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f,
+ 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092,
+ 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7,
+ 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be,
+ 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7,
+ 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4,
+ 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114,
+ 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137,
+ 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160,
+ 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f,
+ 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5,
+ 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206,
+ 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256,
+ 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd,
+ 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e,
+ 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443,
+ 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859,
+ 0x8859, 0x86c3, 0x8607, 0x858b, 0x852e, 0x84e4, 0x84a6, 0x8471,
+ 0x8443, 0x841a, 0x83f5, 0x83d3, 0x83b5, 0x8398, 0x837e, 0x8365,
+ 0x834e, 0x8339, 0x8324, 0x8311, 0x82ff, 0x82ed, 0x82dc, 0x82cd,
+ 0x82bd, 0x82af, 0x82a0, 0x8293, 0x8286, 0x8279, 0x826d, 0x8261,
+ 0x8256, 0x824b, 0x8240, 0x8236, 0x822c, 0x8222, 0x8218, 0x820f,
+ 0x8206, 0x81fd, 0x81f5, 0x81ec, 0x81e4, 0x81dc, 0x81d4, 0x81cd,
+ 0x81c5, 0x81be, 0x81b7, 0x81b0, 0x81a9, 0x81a2, 0x819b, 0x8195,
+ 0x818f, 0x8188, 0x8182, 0x817c, 0x8177, 0x8171, 0x816b, 0x8166,
+ 0x8160, 0x815b, 0x8155, 0x8150, 0x814b, 0x8146, 0x8141, 0x813c,
+ 0x8137, 0x8133, 0x812e, 0x8129, 0x8125, 0x8121, 0x811c, 0x8118,
+ 0x8114, 0x810f, 0x810b, 0x8107, 0x8103, 0x80ff, 0x80fb, 0x80f8,
+ 0x80f4, 0x80f0, 0x80ec, 0x80e9, 0x80e5, 0x80e2, 0x80de, 0x80db,
+ 0x80d7, 0x80d4, 0x80d1, 0x80cd, 0x80ca, 0x80c7, 0x80c4, 0x80c1,
+ 0x80be, 0x80bb, 0x80b8, 0x80b5, 0x80b2, 0x80af, 0x80ac, 0x80a9,
+ 0x80a7, 0x80a4, 0x80a1, 0x809f, 0x809c, 0x8099, 0x8097, 0x8094,
+ 0x8092, 0x808f, 0x808d, 0x808a, 0x8088, 0x8086, 0x8083, 0x8081,
+ 0x807f, 0x807d, 0x807a, 0x8078, 0x8076, 0x8074, 0x8072, 0x8070,
+ 0x806e, 0x806c, 0x806a, 0x8068, 0x8066, 0x8064, 0x8062, 0x8060,
+ 0x805e, 0x805c, 0x805b, 0x8059, 0x8057, 0x8055, 0x8053, 0x8052,
+ 0x8050, 0x804e, 0x804d, 0x804b, 0x804a, 0x8048, 0x8046, 0x8045,
+ 0x8043, 0x8042, 0x8040, 0x803f, 0x803e, 0x803c, 0x803b, 0x8039,
+ 0x8038, 0x8037, 0x8035, 0x8034, 0x8033, 0x8031, 0x8030, 0x802f,
+ 0x802e, 0x802d, 0x802b, 0x802a, 0x8029, 0x8028, 0x8027, 0x8026,
+ 0x8025, 0x8024, 0x8023, 0x8022, 0x8021, 0x8020, 0x801f, 0x801e,
+ 0x801d, 0x801c, 0x801b, 0x801a, 0x8019, 0x8018, 0x8017, 0x8017,
+ 0x8016, 0x8015, 0x8014, 0x8014, 0x8013, 0x8012, 0x8011, 0x8011,
+ 0x8010, 0x800f, 0x800f, 0x800e, 0x800d, 0x800d, 0x800c, 0x800c,
+ 0x800b, 0x800a, 0x800a, 0x8009, 0x8009, 0x8008, 0x8008, 0x8007,
+ 0x8007, 0x8007, 0x8006, 0x8006, 0x8005, 0x8005, 0x8005, 0x8004,
+ 0x8004, 0x8004, 0x8003, 0x8003, 0x8003, 0x8002, 0x8002, 0x8002,
+ 0x8002, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8001, 0x8002,
+ 0x8002, 0x8002, 0x8002, 0x8003, 0x8003, 0x8003, 0x8004, 0x8004,
+ 0x8004, 0x8005, 0x8005, 0x8005, 0x8006, 0x8006, 0x8007, 0x8007,
+ 0x8007, 0x8008, 0x8008, 0x8009, 0x8009, 0x800a, 0x800a, 0x800b,
+ 0x800c, 0x800c, 0x800d, 0x800d, 0x800e, 0x800f, 0x800f, 0x8010,
+ 0x8011, 0x8011, 0x8012, 0x8013, 0x8014, 0x8014, 0x8015, 0x8016,
+ 0x8017, 0x8017, 0x8018, 0x8019, 0x801a, 0x801b, 0x801c, 0x801d,
+ 0x801e, 0x801f, 0x8020, 0x8021, 0x8022, 0x8023, 0x8024, 0x8025,
+ 0x8026, 0x8027, 0x8028, 0x8029, 0x802a, 0x802b, 0x802d, 0x802e,
+ 0x802f, 0x8030, 0x8031, 0x8033, 0x8034, 0x8035, 0x8037, 0x8038,
+ 0x8039, 0x803b, 0x803c, 0x803e, 0x803f, 0x8040, 0x8042, 0x8043,
+ 0x8045, 0x8046, 0x8048, 0x804a, 0x804b, 0x804d, 0x804e, 0x8050,
+ 0x8052, 0x8053, 0x8055, 0x8057, 0x8059, 0x805b, 0x805c, 0x805e,
+ 0x8060, 0x8062, 0x8064, 0x8066, 0x8068, 0x806a, 0x806c, 0x806e,
+ 0x8070, 0x8072, 0x8074, 0x8076, 0x8078, 0x807a, 0x807d, 0x807f,
+ 0x8081, 0x8083, 0x8086, 0x8088, 0x808a, 0x808d, 0x808f, 0x8092,
+ 0x8094, 0x8097, 0x8099, 0x809c, 0x809f, 0x80a1, 0x80a4, 0x80a7,
+ 0x80a9, 0x80ac, 0x80af, 0x80b2, 0x80b5, 0x80b8, 0x80bb, 0x80be,
+ 0x80c1, 0x80c4, 0x80c7, 0x80ca, 0x80cd, 0x80d1, 0x80d4, 0x80d7,
+ 0x80db, 0x80de, 0x80e2, 0x80e5, 0x80e9, 0x80ec, 0x80f0, 0x80f4,
+ 0x80f8, 0x80fb, 0x80ff, 0x8103, 0x8107, 0x810b, 0x810f, 0x8114,
+ 0x8118, 0x811c, 0x8121, 0x8125, 0x8129, 0x812e, 0x8133, 0x8137,
+ 0x813c, 0x8141, 0x8146, 0x814b, 0x8150, 0x8155, 0x815b, 0x8160,
+ 0x8166, 0x816b, 0x8171, 0x8177, 0x817c, 0x8182, 0x8188, 0x818f,
+ 0x8195, 0x819b, 0x81a2, 0x81a9, 0x81b0, 0x81b7, 0x81be, 0x81c5,
+ 0x81cd, 0x81d4, 0x81dc, 0x81e4, 0x81ec, 0x81f5, 0x81fd, 0x8206,
+ 0x820f, 0x8218, 0x8222, 0x822c, 0x8236, 0x8240, 0x824b, 0x8256,
+ 0x8261, 0x826d, 0x8279, 0x8286, 0x8293, 0x82a0, 0x82af, 0x82bd,
+ 0x82cd, 0x82dc, 0x82ed, 0x82ff, 0x8311, 0x8324, 0x8339, 0x834e,
+ 0x8365, 0x837e, 0x8398, 0x83b5, 0x83d3, 0x83f5, 0x841a, 0x8443,
+ 0x8471, 0x84a6, 0x84e4, 0x852e, 0x858b, 0x8607, 0x86c3, 0x8859,
+ // wave 1
+ 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471,
+ 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365,
+ 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd,
+ 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261,
+ 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f,
+ 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd,
+ 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195,
+ 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166,
+ 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c,
+ 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118,
+ 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8,
+ 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db,
+ 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1,
+ 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9,
+ 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094,
+ 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081,
+ 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070,
+ 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060,
+ 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052,
+ 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045,
+ 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039,
+ 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f,
+ 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026,
+ 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e,
+ 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017,
+ 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011,
+ 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c,
+ 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007,
+ 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004,
+ 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004,
+ 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007,
+ 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b,
+ 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010,
+ 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016,
+ 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d,
+ 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025,
+ 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e,
+ 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038,
+ 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043,
+ 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050,
+ 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e,
+ 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e,
+ 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f,
+ 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092,
+ 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7,
+ 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be,
+ 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7,
+ 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4,
+ 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114,
+ 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137,
+ 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160,
+ 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f,
+ 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5,
+ 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206,
+ 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256,
+ 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd,
+ 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e,
+ 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443,
+ 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ // wave 2
+ 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471,
+ 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365,
+ 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd,
+ 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261,
+ 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f,
+ 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd,
+ 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195,
+ 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166,
+ 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c,
+ 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118,
+ 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8,
+ 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db,
+ 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1,
+ 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9,
+ 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094,
+ 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081,
+ 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070,
+ 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060,
+ 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052,
+ 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045,
+ 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039,
+ 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f,
+ 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026,
+ 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e,
+ 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017,
+ 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011,
+ 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c,
+ 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007,
+ 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004,
+ 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004,
+ 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007,
+ 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b,
+ 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010,
+ 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016,
+ 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d,
+ 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025,
+ 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e,
+ 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038,
+ 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043,
+ 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050,
+ 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e,
+ 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e,
+ 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f,
+ 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092,
+ 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7,
+ 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be,
+ 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7,
+ 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4,
+ 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114,
+ 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137,
+ 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160,
+ 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f,
+ 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5,
+ 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206,
+ 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256,
+ 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd,
+ 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e,
+ 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443,
+ 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859,
+ 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471,
+ 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365,
+ 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd,
+ 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261,
+ 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f,
+ 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd,
+ 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195,
+ 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166,
+ 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c,
+ 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118,
+ 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8,
+ 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db,
+ 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1,
+ 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9,
+ 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094,
+ 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081,
+ 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070,
+ 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060,
+ 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052,
+ 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045,
+ 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039,
+ 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f,
+ 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026,
+ 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e,
+ 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017,
+ 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011,
+ 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c,
+ 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007,
+ 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004,
+ 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004,
+ 0x0004, 0x0005, 0x0005, 0x0005, 0x0006, 0x0006, 0x0007, 0x0007,
+ 0x0007, 0x0008, 0x0008, 0x0009, 0x0009, 0x000a, 0x000a, 0x000b,
+ 0x000c, 0x000c, 0x000d, 0x000d, 0x000e, 0x000f, 0x000f, 0x0010,
+ 0x0011, 0x0011, 0x0012, 0x0013, 0x0014, 0x0014, 0x0015, 0x0016,
+ 0x0017, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d,
+ 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025,
+ 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002d, 0x002e,
+ 0x002f, 0x0030, 0x0031, 0x0033, 0x0034, 0x0035, 0x0037, 0x0038,
+ 0x0039, 0x003b, 0x003c, 0x003e, 0x003f, 0x0040, 0x0042, 0x0043,
+ 0x0045, 0x0046, 0x0048, 0x004a, 0x004b, 0x004d, 0x004e, 0x0050,
+ 0x0052, 0x0053, 0x0055, 0x0057, 0x0059, 0x005b, 0x005c, 0x005e,
+ 0x0060, 0x0062, 0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e,
+ 0x0070, 0x0072, 0x0074, 0x0076, 0x0078, 0x007a, 0x007d, 0x007f,
+ 0x0081, 0x0083, 0x0086, 0x0088, 0x008a, 0x008d, 0x008f, 0x0092,
+ 0x0094, 0x0097, 0x0099, 0x009c, 0x009f, 0x00a1, 0x00a4, 0x00a7,
+ 0x00a9, 0x00ac, 0x00af, 0x00b2, 0x00b5, 0x00b8, 0x00bb, 0x00be,
+ 0x00c1, 0x00c4, 0x00c7, 0x00ca, 0x00cd, 0x00d1, 0x00d4, 0x00d7,
+ 0x00db, 0x00de, 0x00e2, 0x00e5, 0x00e9, 0x00ec, 0x00f0, 0x00f4,
+ 0x00f8, 0x00fb, 0x00ff, 0x0103, 0x0107, 0x010b, 0x010f, 0x0114,
+ 0x0118, 0x011c, 0x0121, 0x0125, 0x0129, 0x012e, 0x0133, 0x0137,
+ 0x013c, 0x0141, 0x0146, 0x014b, 0x0150, 0x0155, 0x015b, 0x0160,
+ 0x0166, 0x016b, 0x0171, 0x0177, 0x017c, 0x0182, 0x0188, 0x018f,
+ 0x0195, 0x019b, 0x01a2, 0x01a9, 0x01b0, 0x01b7, 0x01be, 0x01c5,
+ 0x01cd, 0x01d4, 0x01dc, 0x01e4, 0x01ec, 0x01f5, 0x01fd, 0x0206,
+ 0x020f, 0x0218, 0x0222, 0x022c, 0x0236, 0x0240, 0x024b, 0x0256,
+ 0x0261, 0x026d, 0x0279, 0x0286, 0x0293, 0x02a0, 0x02af, 0x02bd,
+ 0x02cd, 0x02dc, 0x02ed, 0x02ff, 0x0311, 0x0324, 0x0339, 0x034e,
+ 0x0365, 0x037e, 0x0398, 0x03b5, 0x03d3, 0x03f5, 0x041a, 0x0443,
+ 0x0471, 0x04a6, 0x04e4, 0x052e, 0x058b, 0x0607, 0x06c3, 0x0859,
+ // wave 3
+ 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471,
+ 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365,
+ 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd,
+ 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261,
+ 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f,
+ 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd,
+ 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195,
+ 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166,
+ 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c,
+ 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118,
+ 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8,
+ 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db,
+ 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1,
+ 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9,
+ 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094,
+ 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081,
+ 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070,
+ 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060,
+ 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052,
+ 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045,
+ 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039,
+ 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f,
+ 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026,
+ 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e,
+ 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017,
+ 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011,
+ 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c,
+ 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007,
+ 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004,
+ 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x0859, 0x06c3, 0x0607, 0x058b, 0x052e, 0x04e4, 0x04a6, 0x0471,
+ 0x0443, 0x041a, 0x03f5, 0x03d3, 0x03b5, 0x0398, 0x037e, 0x0365,
+ 0x034e, 0x0339, 0x0324, 0x0311, 0x02ff, 0x02ed, 0x02dc, 0x02cd,
+ 0x02bd, 0x02af, 0x02a0, 0x0293, 0x0286, 0x0279, 0x026d, 0x0261,
+ 0x0256, 0x024b, 0x0240, 0x0236, 0x022c, 0x0222, 0x0218, 0x020f,
+ 0x0206, 0x01fd, 0x01f5, 0x01ec, 0x01e4, 0x01dc, 0x01d4, 0x01cd,
+ 0x01c5, 0x01be, 0x01b7, 0x01b0, 0x01a9, 0x01a2, 0x019b, 0x0195,
+ 0x018f, 0x0188, 0x0182, 0x017c, 0x0177, 0x0171, 0x016b, 0x0166,
+ 0x0160, 0x015b, 0x0155, 0x0150, 0x014b, 0x0146, 0x0141, 0x013c,
+ 0x0137, 0x0133, 0x012e, 0x0129, 0x0125, 0x0121, 0x011c, 0x0118,
+ 0x0114, 0x010f, 0x010b, 0x0107, 0x0103, 0x00ff, 0x00fb, 0x00f8,
+ 0x00f4, 0x00f0, 0x00ec, 0x00e9, 0x00e5, 0x00e2, 0x00de, 0x00db,
+ 0x00d7, 0x00d4, 0x00d1, 0x00cd, 0x00ca, 0x00c7, 0x00c4, 0x00c1,
+ 0x00be, 0x00bb, 0x00b8, 0x00b5, 0x00b2, 0x00af, 0x00ac, 0x00a9,
+ 0x00a7, 0x00a4, 0x00a1, 0x009f, 0x009c, 0x0099, 0x0097, 0x0094,
+ 0x0092, 0x008f, 0x008d, 0x008a, 0x0088, 0x0086, 0x0083, 0x0081,
+ 0x007f, 0x007d, 0x007a, 0x0078, 0x0076, 0x0074, 0x0072, 0x0070,
+ 0x006e, 0x006c, 0x006a, 0x0068, 0x0066, 0x0064, 0x0062, 0x0060,
+ 0x005e, 0x005c, 0x005b, 0x0059, 0x0057, 0x0055, 0x0053, 0x0052,
+ 0x0050, 0x004e, 0x004d, 0x004b, 0x004a, 0x0048, 0x0046, 0x0045,
+ 0x0043, 0x0042, 0x0040, 0x003f, 0x003e, 0x003c, 0x003b, 0x0039,
+ 0x0038, 0x0037, 0x0035, 0x0034, 0x0033, 0x0031, 0x0030, 0x002f,
+ 0x002e, 0x002d, 0x002b, 0x002a, 0x0029, 0x0028, 0x0027, 0x0026,
+ 0x0025, 0x0024, 0x0023, 0x0022, 0x0021, 0x0020, 0x001f, 0x001e,
+ 0x001d, 0x001c, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017, 0x0017,
+ 0x0016, 0x0015, 0x0014, 0x0014, 0x0013, 0x0012, 0x0011, 0x0011,
+ 0x0010, 0x000f, 0x000f, 0x000e, 0x000d, 0x000d, 0x000c, 0x000c,
+ 0x000b, 0x000a, 0x000a, 0x0009, 0x0009, 0x0008, 0x0008, 0x0007,
+ 0x0007, 0x0007, 0x0006, 0x0006, 0x0005, 0x0005, 0x0005, 0x0004,
+ 0x0004, 0x0004, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ // wave 4
+ 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e,
+ 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d,
+ 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4,
+ 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b,
+ 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c,
+ 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de,
+ 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac,
+ 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083,
+ 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062,
+ 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046,
+ 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030,
+ 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f,
+ 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011,
+ 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008,
+ 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010,
+ 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d,
+ 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e,
+ 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043,
+ 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e,
+ 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f,
+ 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7,
+ 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7,
+ 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114,
+ 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160,
+ 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5,
+ 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256,
+ 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e,
+ 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859,
+ 0x8859, 0x8607, 0x852e, 0x84a6, 0x8443, 0x83f5, 0x83b5, 0x837e,
+ 0x834e, 0x8324, 0x82ff, 0x82dc, 0x82bd, 0x82a0, 0x8286, 0x826d,
+ 0x8256, 0x8240, 0x822c, 0x8218, 0x8206, 0x81f5, 0x81e4, 0x81d4,
+ 0x81c5, 0x81b7, 0x81a9, 0x819b, 0x818f, 0x8182, 0x8177, 0x816b,
+ 0x8160, 0x8155, 0x814b, 0x8141, 0x8137, 0x812e, 0x8125, 0x811c,
+ 0x8114, 0x810b, 0x8103, 0x80fb, 0x80f4, 0x80ec, 0x80e5, 0x80de,
+ 0x80d7, 0x80d1, 0x80ca, 0x80c4, 0x80be, 0x80b8, 0x80b2, 0x80ac,
+ 0x80a7, 0x80a1, 0x809c, 0x8097, 0x8092, 0x808d, 0x8088, 0x8083,
+ 0x807f, 0x807a, 0x8076, 0x8072, 0x806e, 0x806a, 0x8066, 0x8062,
+ 0x805e, 0x805b, 0x8057, 0x8053, 0x8050, 0x804d, 0x804a, 0x8046,
+ 0x8043, 0x8040, 0x803e, 0x803b, 0x8038, 0x8035, 0x8033, 0x8030,
+ 0x802e, 0x802b, 0x8029, 0x8027, 0x8025, 0x8023, 0x8021, 0x801f,
+ 0x801d, 0x801b, 0x8019, 0x8017, 0x8016, 0x8014, 0x8013, 0x8011,
+ 0x8010, 0x800f, 0x800d, 0x800c, 0x800b, 0x800a, 0x8009, 0x8008,
+ 0x8007, 0x8006, 0x8005, 0x8005, 0x8004, 0x8003, 0x8003, 0x8002,
+ 0x8002, 0x8001, 0x8001, 0x8001, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8001, 0x8001, 0x8002,
+ 0x8002, 0x8003, 0x8003, 0x8004, 0x8005, 0x8005, 0x8006, 0x8007,
+ 0x8008, 0x8009, 0x800a, 0x800b, 0x800c, 0x800d, 0x800f, 0x8010,
+ 0x8011, 0x8013, 0x8014, 0x8016, 0x8017, 0x8019, 0x801b, 0x801d,
+ 0x801f, 0x8021, 0x8023, 0x8025, 0x8027, 0x8029, 0x802b, 0x802e,
+ 0x8030, 0x8033, 0x8035, 0x8038, 0x803b, 0x803e, 0x8040, 0x8043,
+ 0x8046, 0x804a, 0x804d, 0x8050, 0x8053, 0x8057, 0x805b, 0x805e,
+ 0x8062, 0x8066, 0x806a, 0x806e, 0x8072, 0x8076, 0x807a, 0x807f,
+ 0x8083, 0x8088, 0x808d, 0x8092, 0x8097, 0x809c, 0x80a1, 0x80a7,
+ 0x80ac, 0x80b2, 0x80b8, 0x80be, 0x80c4, 0x80ca, 0x80d1, 0x80d7,
+ 0x80de, 0x80e5, 0x80ec, 0x80f4, 0x80fb, 0x8103, 0x810b, 0x8114,
+ 0x811c, 0x8125, 0x812e, 0x8137, 0x8141, 0x814b, 0x8155, 0x8160,
+ 0x816b, 0x8177, 0x8182, 0x818f, 0x819b, 0x81a9, 0x81b7, 0x81c5,
+ 0x81d4, 0x81e4, 0x81f5, 0x8206, 0x8218, 0x822c, 0x8240, 0x8256,
+ 0x826d, 0x8286, 0x82a0, 0x82bd, 0x82dc, 0x82ff, 0x8324, 0x834e,
+ 0x837e, 0x83b5, 0x83f5, 0x8443, 0x84a6, 0x852e, 0x8607, 0x8859,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ // wave 5
+ 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e,
+ 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d,
+ 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4,
+ 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b,
+ 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c,
+ 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de,
+ 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac,
+ 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083,
+ 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062,
+ 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046,
+ 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030,
+ 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f,
+ 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011,
+ 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008,
+ 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010,
+ 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d,
+ 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e,
+ 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043,
+ 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e,
+ 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f,
+ 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7,
+ 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7,
+ 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114,
+ 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160,
+ 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5,
+ 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256,
+ 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e,
+ 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859,
+ 0x0859, 0x0607, 0x052e, 0x04a6, 0x0443, 0x03f5, 0x03b5, 0x037e,
+ 0x034e, 0x0324, 0x02ff, 0x02dc, 0x02bd, 0x02a0, 0x0286, 0x026d,
+ 0x0256, 0x0240, 0x022c, 0x0218, 0x0206, 0x01f5, 0x01e4, 0x01d4,
+ 0x01c5, 0x01b7, 0x01a9, 0x019b, 0x018f, 0x0182, 0x0177, 0x016b,
+ 0x0160, 0x0155, 0x014b, 0x0141, 0x0137, 0x012e, 0x0125, 0x011c,
+ 0x0114, 0x010b, 0x0103, 0x00fb, 0x00f4, 0x00ec, 0x00e5, 0x00de,
+ 0x00d7, 0x00d1, 0x00ca, 0x00c4, 0x00be, 0x00b8, 0x00b2, 0x00ac,
+ 0x00a7, 0x00a1, 0x009c, 0x0097, 0x0092, 0x008d, 0x0088, 0x0083,
+ 0x007f, 0x007a, 0x0076, 0x0072, 0x006e, 0x006a, 0x0066, 0x0062,
+ 0x005e, 0x005b, 0x0057, 0x0053, 0x0050, 0x004d, 0x004a, 0x0046,
+ 0x0043, 0x0040, 0x003e, 0x003b, 0x0038, 0x0035, 0x0033, 0x0030,
+ 0x002e, 0x002b, 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f,
+ 0x001d, 0x001b, 0x0019, 0x0017, 0x0016, 0x0014, 0x0013, 0x0011,
+ 0x0010, 0x000f, 0x000d, 0x000c, 0x000b, 0x000a, 0x0009, 0x0008,
+ 0x0007, 0x0006, 0x0005, 0x0005, 0x0004, 0x0003, 0x0003, 0x0002,
+ 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002,
+ 0x0002, 0x0003, 0x0003, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000f, 0x0010,
+ 0x0011, 0x0013, 0x0014, 0x0016, 0x0017, 0x0019, 0x001b, 0x001d,
+ 0x001f, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002b, 0x002e,
+ 0x0030, 0x0033, 0x0035, 0x0038, 0x003b, 0x003e, 0x0040, 0x0043,
+ 0x0046, 0x004a, 0x004d, 0x0050, 0x0053, 0x0057, 0x005b, 0x005e,
+ 0x0062, 0x0066, 0x006a, 0x006e, 0x0072, 0x0076, 0x007a, 0x007f,
+ 0x0083, 0x0088, 0x008d, 0x0092, 0x0097, 0x009c, 0x00a1, 0x00a7,
+ 0x00ac, 0x00b2, 0x00b8, 0x00be, 0x00c4, 0x00ca, 0x00d1, 0x00d7,
+ 0x00de, 0x00e5, 0x00ec, 0x00f4, 0x00fb, 0x0103, 0x010b, 0x0114,
+ 0x011c, 0x0125, 0x012e, 0x0137, 0x0141, 0x014b, 0x0155, 0x0160,
+ 0x016b, 0x0177, 0x0182, 0x018f, 0x019b, 0x01a9, 0x01b7, 0x01c5,
+ 0x01d4, 0x01e4, 0x01f5, 0x0206, 0x0218, 0x022c, 0x0240, 0x0256,
+ 0x026d, 0x0286, 0x02a0, 0x02bd, 0x02dc, 0x02ff, 0x0324, 0x034e,
+ 0x037e, 0x03b5, 0x03f5, 0x0443, 0x04a6, 0x052e, 0x0607, 0x0859,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+ // wave 6
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ // wave 7
+ 0x0000, 0x0008, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038,
+ 0x0040, 0x0048, 0x0050, 0x0058, 0x0060, 0x0068, 0x0070, 0x0078,
+ 0x0080, 0x0088, 0x0090, 0x0098, 0x00a0, 0x00a8, 0x00b0, 0x00b8,
+ 0x00c0, 0x00c8, 0x00d0, 0x00d8, 0x00e0, 0x00e8, 0x00f0, 0x00f8,
+ 0x0100, 0x0108, 0x0110, 0x0118, 0x0120, 0x0128, 0x0130, 0x0138,
+ 0x0140, 0x0148, 0x0150, 0x0158, 0x0160, 0x0168, 0x0170, 0x0178,
+ 0x0180, 0x0188, 0x0190, 0x0198, 0x01a0, 0x01a8, 0x01b0, 0x01b8,
+ 0x01c0, 0x01c8, 0x01d0, 0x01d8, 0x01e0, 0x01e8, 0x01f0, 0x01f8,
+ 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, 0x0238,
+ 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278,
+ 0x0280, 0x0288, 0x0290, 0x0298, 0x02a0, 0x02a8, 0x02b0, 0x02b8,
+ 0x02c0, 0x02c8, 0x02d0, 0x02d8, 0x02e0, 0x02e8, 0x02f0, 0x02f8,
+ 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
+ 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
+ 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
+ 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8,
+ 0x0400, 0x0408, 0x0410, 0x0418, 0x0420, 0x0428, 0x0430, 0x0438,
+ 0x0440, 0x0448, 0x0450, 0x0458, 0x0460, 0x0468, 0x0470, 0x0478,
+ 0x0480, 0x0488, 0x0490, 0x0498, 0x04a0, 0x04a8, 0x04b0, 0x04b8,
+ 0x04c0, 0x04c8, 0x04d0, 0x04d8, 0x04e0, 0x04e8, 0x04f0, 0x04f8,
+ 0x0500, 0x0508, 0x0510, 0x0518, 0x0520, 0x0528, 0x0530, 0x0538,
+ 0x0540, 0x0548, 0x0550, 0x0558, 0x0560, 0x0568, 0x0570, 0x0578,
+ 0x0580, 0x0588, 0x0590, 0x0598, 0x05a0, 0x05a8, 0x05b0, 0x05b8,
+ 0x05c0, 0x05c8, 0x05d0, 0x05d8, 0x05e0, 0x05e8, 0x05f0, 0x05f8,
+ 0x0600, 0x0608, 0x0610, 0x0618, 0x0620, 0x0628, 0x0630, 0x0638,
+ 0x0640, 0x0648, 0x0650, 0x0658, 0x0660, 0x0668, 0x0670, 0x0678,
+ 0x0680, 0x0688, 0x0690, 0x0698, 0x06a0, 0x06a8, 0x06b0, 0x06b8,
+ 0x06c0, 0x06c8, 0x06d0, 0x06d8, 0x06e0, 0x06e8, 0x06f0, 0x06f8,
+ 0x0700, 0x0708, 0x0710, 0x0718, 0x0720, 0x0728, 0x0730, 0x0738,
+ 0x0740, 0x0748, 0x0750, 0x0758, 0x0760, 0x0768, 0x0770, 0x0778,
+ 0x0780, 0x0788, 0x0790, 0x0798, 0x07a0, 0x07a8, 0x07b0, 0x07b8,
+ 0x07c0, 0x07c8, 0x07d0, 0x07d8, 0x07e0, 0x07e8, 0x07f0, 0x07f8,
+ 0x0800, 0x0808, 0x0810, 0x0818, 0x0820, 0x0828, 0x0830, 0x0838,
+ 0x0840, 0x0848, 0x0850, 0x0858, 0x0860, 0x0868, 0x0870, 0x0878,
+ 0x0880, 0x0888, 0x0890, 0x0898, 0x08a0, 0x08a8, 0x08b0, 0x08b8,
+ 0x08c0, 0x08c8, 0x08d0, 0x08d8, 0x08e0, 0x08e8, 0x08f0, 0x08f8,
+ 0x0900, 0x0908, 0x0910, 0x0918, 0x0920, 0x0928, 0x0930, 0x0938,
+ 0x0940, 0x0948, 0x0950, 0x0958, 0x0960, 0x0968, 0x0970, 0x0978,
+ 0x0980, 0x0988, 0x0990, 0x0998, 0x09a0, 0x09a8, 0x09b0, 0x09b8,
+ 0x09c0, 0x09c8, 0x09d0, 0x09d8, 0x09e0, 0x09e8, 0x09f0, 0x09f8,
+ 0x0a00, 0x0a08, 0x0a10, 0x0a18, 0x0a20, 0x0a28, 0x0a30, 0x0a38,
+ 0x0a40, 0x0a48, 0x0a50, 0x0a58, 0x0a60, 0x0a68, 0x0a70, 0x0a78,
+ 0x0a80, 0x0a88, 0x0a90, 0x0a98, 0x0aa0, 0x0aa8, 0x0ab0, 0x0ab8,
+ 0x0ac0, 0x0ac8, 0x0ad0, 0x0ad8, 0x0ae0, 0x0ae8, 0x0af0, 0x0af8,
+ 0x0b00, 0x0b08, 0x0b10, 0x0b18, 0x0b20, 0x0b28, 0x0b30, 0x0b38,
+ 0x0b40, 0x0b48, 0x0b50, 0x0b58, 0x0b60, 0x0b68, 0x0b70, 0x0b78,
+ 0x0b80, 0x0b88, 0x0b90, 0x0b98, 0x0ba0, 0x0ba8, 0x0bb0, 0x0bb8,
+ 0x0bc0, 0x0bc8, 0x0bd0, 0x0bd8, 0x0be0, 0x0be8, 0x0bf0, 0x0bf8,
+ 0x0c00, 0x0c08, 0x0c10, 0x0c18, 0x0c20, 0x0c28, 0x0c30, 0x0c38,
+ 0x0c40, 0x0c48, 0x0c50, 0x0c58, 0x0c60, 0x0c68, 0x0c70, 0x0c78,
+ 0x0c80, 0x0c88, 0x0c90, 0x0c98, 0x0ca0, 0x0ca8, 0x0cb0, 0x0cb8,
+ 0x0cc0, 0x0cc8, 0x0cd0, 0x0cd8, 0x0ce0, 0x0ce8, 0x0cf0, 0x0cf8,
+ 0x0d00, 0x0d08, 0x0d10, 0x0d18, 0x0d20, 0x0d28, 0x0d30, 0x0d38,
+ 0x0d40, 0x0d48, 0x0d50, 0x0d58, 0x0d60, 0x0d68, 0x0d70, 0x0d78,
+ 0x0d80, 0x0d88, 0x0d90, 0x0d98, 0x0da0, 0x0da8, 0x0db0, 0x0db8,
+ 0x0dc0, 0x0dc8, 0x0dd0, 0x0dd8, 0x0de0, 0x0de8, 0x0df0, 0x0df8,
+ 0x0e00, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38,
+ 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78,
+ 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8,
+ 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8,
+ 0x0f00, 0x0f08, 0x0f10, 0x0f18, 0x0f20, 0x0f28, 0x0f30, 0x0f38,
+ 0x0f40, 0x0f48, 0x0f50, 0x0f58, 0x0f60, 0x0f68, 0x0f70, 0x0f78,
+ 0x0f80, 0x0f88, 0x0f90, 0x0f98, 0x0fa0, 0x0fa8, 0x0fb0, 0x0fb8,
+ 0x0fc0, 0x0fc8, 0x0fd0, 0x0fd8, 0x0fe0, 0x0fe8, 0x0ff0, 0x0ff8,
+ 0x8ff8, 0x8ff0, 0x8fe8, 0x8fe0, 0x8fd8, 0x8fd0, 0x8fc8, 0x8fc0,
+ 0x8fb8, 0x8fb0, 0x8fa8, 0x8fa0, 0x8f98, 0x8f90, 0x8f88, 0x8f80,
+ 0x8f78, 0x8f70, 0x8f68, 0x8f60, 0x8f58, 0x8f50, 0x8f48, 0x8f40,
+ 0x8f38, 0x8f30, 0x8f28, 0x8f20, 0x8f18, 0x8f10, 0x8f08, 0x8f00,
+ 0x8ef8, 0x8ef0, 0x8ee8, 0x8ee0, 0x8ed8, 0x8ed0, 0x8ec8, 0x8ec0,
+ 0x8eb8, 0x8eb0, 0x8ea8, 0x8ea0, 0x8e98, 0x8e90, 0x8e88, 0x8e80,
+ 0x8e78, 0x8e70, 0x8e68, 0x8e60, 0x8e58, 0x8e50, 0x8e48, 0x8e40,
+ 0x8e38, 0x8e30, 0x8e28, 0x8e20, 0x8e18, 0x8e10, 0x8e08, 0x8e00,
+ 0x8df8, 0x8df0, 0x8de8, 0x8de0, 0x8dd8, 0x8dd0, 0x8dc8, 0x8dc0,
+ 0x8db8, 0x8db0, 0x8da8, 0x8da0, 0x8d98, 0x8d90, 0x8d88, 0x8d80,
+ 0x8d78, 0x8d70, 0x8d68, 0x8d60, 0x8d58, 0x8d50, 0x8d48, 0x8d40,
+ 0x8d38, 0x8d30, 0x8d28, 0x8d20, 0x8d18, 0x8d10, 0x8d08, 0x8d00,
+ 0x8cf8, 0x8cf0, 0x8ce8, 0x8ce0, 0x8cd8, 0x8cd0, 0x8cc8, 0x8cc0,
+ 0x8cb8, 0x8cb0, 0x8ca8, 0x8ca0, 0x8c98, 0x8c90, 0x8c88, 0x8c80,
+ 0x8c78, 0x8c70, 0x8c68, 0x8c60, 0x8c58, 0x8c50, 0x8c48, 0x8c40,
+ 0x8c38, 0x8c30, 0x8c28, 0x8c20, 0x8c18, 0x8c10, 0x8c08, 0x8c00,
+ 0x8bf8, 0x8bf0, 0x8be8, 0x8be0, 0x8bd8, 0x8bd0, 0x8bc8, 0x8bc0,
+ 0x8bb8, 0x8bb0, 0x8ba8, 0x8ba0, 0x8b98, 0x8b90, 0x8b88, 0x8b80,
+ 0x8b78, 0x8b70, 0x8b68, 0x8b60, 0x8b58, 0x8b50, 0x8b48, 0x8b40,
+ 0x8b38, 0x8b30, 0x8b28, 0x8b20, 0x8b18, 0x8b10, 0x8b08, 0x8b00,
+ 0x8af8, 0x8af0, 0x8ae8, 0x8ae0, 0x8ad8, 0x8ad0, 0x8ac8, 0x8ac0,
+ 0x8ab8, 0x8ab0, 0x8aa8, 0x8aa0, 0x8a98, 0x8a90, 0x8a88, 0x8a80,
+ 0x8a78, 0x8a70, 0x8a68, 0x8a60, 0x8a58, 0x8a50, 0x8a48, 0x8a40,
+ 0x8a38, 0x8a30, 0x8a28, 0x8a20, 0x8a18, 0x8a10, 0x8a08, 0x8a00,
+ 0x89f8, 0x89f0, 0x89e8, 0x89e0, 0x89d8, 0x89d0, 0x89c8, 0x89c0,
+ 0x89b8, 0x89b0, 0x89a8, 0x89a0, 0x8998, 0x8990, 0x8988, 0x8980,
+ 0x8978, 0x8970, 0x8968, 0x8960, 0x8958, 0x8950, 0x8948, 0x8940,
+ 0x8938, 0x8930, 0x8928, 0x8920, 0x8918, 0x8910, 0x8908, 0x8900,
+ 0x88f8, 0x88f0, 0x88e8, 0x88e0, 0x88d8, 0x88d0, 0x88c8, 0x88c0,
+ 0x88b8, 0x88b0, 0x88a8, 0x88a0, 0x8898, 0x8890, 0x8888, 0x8880,
+ 0x8878, 0x8870, 0x8868, 0x8860, 0x8858, 0x8850, 0x8848, 0x8840,
+ 0x8838, 0x8830, 0x8828, 0x8820, 0x8818, 0x8810, 0x8808, 0x8800,
+ 0x87f8, 0x87f0, 0x87e8, 0x87e0, 0x87d8, 0x87d0, 0x87c8, 0x87c0,
+ 0x87b8, 0x87b0, 0x87a8, 0x87a0, 0x8798, 0x8790, 0x8788, 0x8780,
+ 0x8778, 0x8770, 0x8768, 0x8760, 0x8758, 0x8750, 0x8748, 0x8740,
+ 0x8738, 0x8730, 0x8728, 0x8720, 0x8718, 0x8710, 0x8708, 0x8700,
+ 0x86f8, 0x86f0, 0x86e8, 0x86e0, 0x86d8, 0x86d0, 0x86c8, 0x86c0,
+ 0x86b8, 0x86b0, 0x86a8, 0x86a0, 0x8698, 0x8690, 0x8688, 0x8680,
+ 0x8678, 0x8670, 0x8668, 0x8660, 0x8658, 0x8650, 0x8648, 0x8640,
+ 0x8638, 0x8630, 0x8628, 0x8620, 0x8618, 0x8610, 0x8608, 0x8600,
+ 0x85f8, 0x85f0, 0x85e8, 0x85e0, 0x85d8, 0x85d0, 0x85c8, 0x85c0,
+ 0x85b8, 0x85b0, 0x85a8, 0x85a0, 0x8598, 0x8590, 0x8588, 0x8580,
+ 0x8578, 0x8570, 0x8568, 0x8560, 0x8558, 0x8550, 0x8548, 0x8540,
+ 0x8538, 0x8530, 0x8528, 0x8520, 0x8518, 0x8510, 0x8508, 0x8500,
+ 0x84f8, 0x84f0, 0x84e8, 0x84e0, 0x84d8, 0x84d0, 0x84c8, 0x84c0,
+ 0x84b8, 0x84b0, 0x84a8, 0x84a0, 0x8498, 0x8490, 0x8488, 0x8480,
+ 0x8478, 0x8470, 0x8468, 0x8460, 0x8458, 0x8450, 0x8448, 0x8440,
+ 0x8438, 0x8430, 0x8428, 0x8420, 0x8418, 0x8410, 0x8408, 0x8400,
+ 0x83f8, 0x83f0, 0x83e8, 0x83e0, 0x83d8, 0x83d0, 0x83c8, 0x83c0,
+ 0x83b8, 0x83b0, 0x83a8, 0x83a0, 0x8398, 0x8390, 0x8388, 0x8380,
+ 0x8378, 0x8370, 0x8368, 0x8360, 0x8358, 0x8350, 0x8348, 0x8340,
+ 0x8338, 0x8330, 0x8328, 0x8320, 0x8318, 0x8310, 0x8308, 0x8300,
+ 0x82f8, 0x82f0, 0x82e8, 0x82e0, 0x82d8, 0x82d0, 0x82c8, 0x82c0,
+ 0x82b8, 0x82b0, 0x82a8, 0x82a0, 0x8298, 0x8290, 0x8288, 0x8280,
+ 0x8278, 0x8270, 0x8268, 0x8260, 0x8258, 0x8250, 0x8248, 0x8240,
+ 0x8238, 0x8230, 0x8228, 0x8220, 0x8218, 0x8210, 0x8208, 0x8200,
+ 0x81f8, 0x81f0, 0x81e8, 0x81e0, 0x81d8, 0x81d0, 0x81c8, 0x81c0,
+ 0x81b8, 0x81b0, 0x81a8, 0x81a0, 0x8198, 0x8190, 0x8188, 0x8180,
+ 0x8178, 0x8170, 0x8168, 0x8160, 0x8158, 0x8150, 0x8148, 0x8140,
+ 0x8138, 0x8130, 0x8128, 0x8120, 0x8118, 0x8110, 0x8108, 0x8100,
+ 0x80f8, 0x80f0, 0x80e8, 0x80e0, 0x80d8, 0x80d0, 0x80c8, 0x80c0,
+ 0x80b8, 0x80b0, 0x80a8, 0x80a0, 0x8098, 0x8090, 0x8088, 0x8080,
+ 0x8078, 0x8070, 0x8068, 0x8060, 0x8058, 0x8050, 0x8048, 0x8040,
+ 0x8038, 0x8030, 0x8028, 0x8020, 0x8018, 0x8010, 0x8008, 0x8000,
+};
+
+/*
+ * Inverse exponent table extracted from OPL3 ROM; taken straight from
+ * Nuked OPL3 source code.
+ * TODO: Verify if ESFM uses an exponent table or if it possibly uses another
+ * method to skirt around Yamaha's patents?
+ * Optimization: All entries are shifted left by one from the actual data in
+ * OPL3's ROM.
+ */
+static const uint16_t exprom[256] = {
+ 0xff4, 0xfea, 0xfde, 0xfd4, 0xfc8, 0xfbe, 0xfb4, 0xfa8,
+ 0xf9e, 0xf92, 0xf88, 0xf7e, 0xf72, 0xf68, 0xf5c, 0xf52,
+ 0xf48, 0xf3e, 0xf32, 0xf28, 0xf1e, 0xf14, 0xf08, 0xefe,
+ 0xef4, 0xeea, 0xee0, 0xed4, 0xeca, 0xec0, 0xeb6, 0xeac,
+ 0xea2, 0xe98, 0xe8e, 0xe84, 0xe7a, 0xe70, 0xe66, 0xe5c,
+ 0xe52, 0xe48, 0xe3e, 0xe34, 0xe2a, 0xe20, 0xe16, 0xe0c,
+ 0xe04, 0xdfa, 0xdf0, 0xde6, 0xddc, 0xdd2, 0xdca, 0xdc0,
+ 0xdb6, 0xdac, 0xda4, 0xd9a, 0xd90, 0xd88, 0xd7e, 0xd74,
+ 0xd6a, 0xd62, 0xd58, 0xd50, 0xd46, 0xd3c, 0xd34, 0xd2a,
+ 0xd22, 0xd18, 0xd10, 0xd06, 0xcfe, 0xcf4, 0xcec, 0xce2,
+ 0xcda, 0xcd0, 0xcc8, 0xcbe, 0xcb6, 0xcae, 0xca4, 0xc9c,
+ 0xc92, 0xc8a, 0xc82, 0xc78, 0xc70, 0xc68, 0xc60, 0xc56,
+ 0xc4e, 0xc46, 0xc3c, 0xc34, 0xc2c, 0xc24, 0xc1c, 0xc12,
+ 0xc0a, 0xc02, 0xbfa, 0xbf2, 0xbea, 0xbe0, 0xbd8, 0xbd0,
+ 0xbc8, 0xbc0, 0xbb8, 0xbb0, 0xba8, 0xba0, 0xb98, 0xb90,
+ 0xb88, 0xb80, 0xb78, 0xb70, 0xb68, 0xb60, 0xb58, 0xb50,
+ 0xb48, 0xb40, 0xb38, 0xb32, 0xb2a, 0xb22, 0xb1a, 0xb12,
+ 0xb0a, 0xb02, 0xafc, 0xaf4, 0xaec, 0xae4, 0xade, 0xad6,
+ 0xace, 0xac6, 0xac0, 0xab8, 0xab0, 0xaa8, 0xaa2, 0xa9a,
+ 0xa92, 0xa8c, 0xa84, 0xa7c, 0xa76, 0xa6e, 0xa68, 0xa60,
+ 0xa58, 0xa52, 0xa4a, 0xa44, 0xa3c, 0xa36, 0xa2e, 0xa28,
+ 0xa20, 0xa18, 0xa12, 0xa0c, 0xa04, 0x9fe, 0x9f6, 0x9f0,
+ 0x9e8, 0x9e2, 0x9da, 0x9d4, 0x9ce, 0x9c6, 0x9c0, 0x9b8,
+ 0x9b2, 0x9ac, 0x9a4, 0x99e, 0x998, 0x990, 0x98a, 0x984,
+ 0x97c, 0x976, 0x970, 0x96a, 0x962, 0x95c, 0x956, 0x950,
+ 0x948, 0x942, 0x93c, 0x936, 0x930, 0x928, 0x922, 0x91c,
+ 0x916, 0x910, 0x90a, 0x904, 0x8fc, 0x8f6, 0x8f0, 0x8ea,
+ 0x8e4, 0x8de, 0x8d8, 0x8d2, 0x8cc, 0x8c6, 0x8c0, 0x8ba,
+ 0x8b4, 0x8ae, 0x8a8, 0x8a2, 0x89c, 0x896, 0x890, 0x88a,
+ 0x884, 0x87e, 0x878, 0x872, 0x86c, 0x866, 0x860, 0x85a,
+ 0x854, 0x850, 0x84a, 0x844, 0x83e, 0x838, 0x832, 0x82c,
+ 0x828, 0x822, 0x81c, 0x816, 0x810, 0x80c, 0x806, 0x800
+};
+
+/*
+ * Frequency multiplier table multiplied by 2; taken straight from Nuked OPL3
+ * source code.
+ */
+static const uint8_t mt[16] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
+};
+
+/*
+ * This is used during the envelope generation to apply KSL to the envelope by
+ * determining how much to shift right the keyscale attenuation value before
+ * adding it to the envelope level.
+ */
+static const uint8_t kslshift[4] = {
+ 8, 1, 2, 0
+};
+
+/*
+ * This encodes which emulation mode channels are the secondary channel in a
+ * 4-op channel pair (where the entry is non-negative), and which is the
+ * corresponding primary channel for that secondary channel.
+ */
+static const int emu_4op_secondary_to_primary[18] =
+{
+ -1, -1, -1, 0, 1, 2, -1, -1, -1,
+ -1, -1, -1, 9, 10, 11, -1, -1, -1
+};
+
+/*
+ * Envelope generator dither table, taken straight from Nuked OPL3 source code.
+ */
+static const uint8_t eg_incstep[4][4] = {
+ { 0, 0, 0, 0 },
+ { 1, 0, 0, 0 },
+ { 1, 0, 1, 0 },
+ { 1, 1, 1, 0 }
+};
+
+/* ------------------------------------------------------------------------- */
+static inline int13
+ESFM_envelope_wavegen(uint3 waveform, int16 phase, uint10 envelope)
+{
+ int13 out;
+ uint16 lookup = logsinrom[((uint16)waveform << 10) | (phase & 0x3ff)];
+ uint16 level = (lookup & 0x1fff) + (envelope << 3);
+ if (level > 0x1fff)
+ {
+ level = 0x1fff;
+ }
+ out = exprom[level & 0xff] >> (level >> 8);
+ if (lookup & 0x8000)
+ {
+ out = -out;
+ }
+ return out;
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_envelope_calc(esfm_slot *slot)
+{
+ uint8 nonzero;
+ uint8 rate;
+ uint5 rate_hi;
+ uint2 rate_lo;
+ uint4 reg_rate = 0;
+ uint4 ks;
+ uint8 eg_shift, shift;
+ bool eg_off;
+ uint9 eg_rout;
+ int16 eg_inc;
+ bool reset = 0;
+ bool key_on;
+ bool key_on_signal;
+
+ key_on = *slot->in.key_on;
+ if (!slot->chip->native_mode)
+ {
+ int pair_primary_idx = emu_4op_secondary_to_primary[slot->channel->channel_idx];
+ if (pair_primary_idx >= 0)
+ {
+ esfm_channel *pair_primary = &slot->channel->chip->channels[pair_primary_idx];
+ if (pair_primary->emu_mode_4op_enable)
+ {
+ key_on = *pair_primary->slots[0].in.key_on;
+ }
+ }
+ else if ((slot->channel->channel_idx == 7 || slot->channel->channel_idx == 8)
+ && slot->slot_idx == 1)
+ {
+ key_on = slot->channel->key_on_2;
+ }
+ }
+
+ slot->in.eg_output = slot->in.eg_position + (slot->t_level << 2)
+ + (slot->in.eg_ksl_offset >> kslshift[slot->ksl]);
+ if (slot->tremolo_en)
+ {
+ uint8 tremolo;
+ if (slot->chip->native_mode)
+ {
+ tremolo = slot->channel->chip->tremolo >> ((!slot->tremolo_deep << 1) + 2);
+ }
+ else
+ {
+ tremolo = slot->channel->chip->tremolo >> ((!slot->chip->emu_tremolo_deep << 1) + 2);
+ }
+ slot->in.eg_output += tremolo;
+ }
+
+ if (slot->in.eg_delay_run && slot->in.eg_delay_counter < 32768)
+ {
+ slot->in.eg_delay_counter++;
+ }
+
+ // triggers on key-on edge
+ if (key_on && !slot->in.key_on_gate)
+ {
+ slot->in.eg_delay_run = 1;
+ slot->in.eg_delay_counter = 0;
+ slot->in.eg_delay_transitioned_01 = 0;
+ slot->in.eg_delay_transitioned_01_gate = 0;
+ slot->in.eg_delay_transitioned_10 = 0;
+ slot->in.eg_delay_transitioned_10_gate = 0;
+ slot->in.eg_delay_counter_compare = 0;
+ if (slot->env_delay > 0)
+ {
+ slot->in.eg_delay_counter_compare = 256 << slot->env_delay;
+ }
+ }
+ else if (!key_on)
+ {
+ slot->in.eg_delay_run = 0;
+ }
+
+ // TODO: is this really how the chip behaves? Can it only transition the envelope delay once? Am I implementing this in a sane way? I feel like this is a roundabout hack.
+ if ((slot->in.eg_delay_transitioned_10 && !slot->in.eg_delay_transitioned_10_gate) ||
+ (slot->in.eg_delay_transitioned_01 && !slot->in.eg_delay_transitioned_01_gate)
+ )
+ {
+ slot->in.eg_delay_counter_compare = 0;
+ if (slot->env_delay > 0)
+ {
+ slot->in.eg_delay_counter_compare = 256 << slot->env_delay;
+ }
+ if (slot->in.eg_delay_transitioned_10)
+ {
+ slot->in.eg_delay_transitioned_10_gate = 1;
+ }
+ if (slot->in.eg_delay_transitioned_01)
+ {
+ slot->in.eg_delay_transitioned_01_gate = 1;
+ }
+ }
+
+ if (key_on && ((slot->in.eg_delay_counter >= slot->in.eg_delay_counter_compare) || !slot->chip->native_mode))
+ {
+ key_on_signal = 1;
+ } else {
+ key_on_signal = 0;
+ }
+
+ if (key_on && slot->in.eg_state == EG_RELEASE)
+ {
+
+ if ((slot->in.eg_delay_counter >= slot->in.eg_delay_counter_compare) || !slot->chip->native_mode)
+ {
+ reset = 1;
+ reg_rate = slot->attack_rate;
+ }
+ else
+ {
+ reg_rate = slot->release_rate;
+ }
+ }
+ else
+ {
+ switch (slot->in.eg_state)
+ {
+ case EG_ATTACK:
+ reg_rate = slot->attack_rate;
+ break;
+ case EG_DECAY:
+ reg_rate = slot->decay_rate;
+ break;
+ case EG_SUSTAIN:
+ if (!slot->env_sustaining)
+ {
+ reg_rate = slot->release_rate;
+ }
+ break;
+ case EG_RELEASE:
+ reg_rate = slot->release_rate;
+ break;
+ }
+ }
+ slot->in.key_on_gate = key_on;
+ slot->in.phase_reset = reset;
+ ks = slot->in.keyscale >> ((!slot->ksr) << 1);
+ nonzero = (reg_rate != 0);
+ rate = ks + (reg_rate << 2);
+ rate_hi = rate >> 2;
+ rate_lo = rate & 0x03;
+ if (rate_hi & 0x10)
+ {
+ rate_hi = 0x0f;
+ }
+ eg_shift = rate_hi + slot->chip->eg_clocks;
+ shift = 0;
+ if (nonzero)
+ {
+ if (rate_hi < 12)
+ {
+ if (slot->chip->eg_tick)
+ {
+ switch (eg_shift)
+ {
+ case 12:
+ shift = 1;
+ break;
+ case 13:
+ shift = (rate_lo >> 1) & 0x01;
+ break;
+ case 14:
+ shift = rate_lo & 0x01;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ shift = (rate_hi & 0x03)
+ + eg_incstep[rate_lo][slot->chip->global_timer & 0x03];
+ if (shift & 0x04)
+ {
+ shift = 0x03;
+ }
+ if (!shift)
+ {
+ shift = slot->chip->eg_tick;
+ }
+ }
+ }
+ eg_rout = slot->in.eg_position;
+ eg_inc = 0;
+ eg_off = 0;
+ /* Instant attack */
+ if (reset && rate_hi == 0x0f)
+ {
+ eg_rout = 0x00;
+ }
+ /* Envelope off */
+ if ((slot->in.eg_position & 0x1f8) == 0x1f8)
+ {
+ eg_off = 1;
+ }
+ if (slot->in.eg_state != EG_ATTACK && !reset && eg_off)
+ {
+ eg_rout = 0x1ff;
+ }
+ switch (slot->in.eg_state)
+ {
+ case EG_ATTACK:
+ if (slot->in.eg_position == 0)
+ {
+ slot->in.eg_state = EG_DECAY;
+ }
+ else if (key_on_signal && shift > 0 && rate_hi != 0x0f)
+ {
+ eg_inc = ~slot->in.eg_position >> (4 - shift);
+ }
+ break;
+ case EG_DECAY:
+ if ((slot->in.eg_position >> 4) == slot->sustain_lvl)
+ {
+ slot->in.eg_state = EG_SUSTAIN;
+ }
+ else if (!eg_off && !reset && shift > 0)
+ {
+ eg_inc = 1 << (shift - 1);
+ }
+ break;
+ case EG_SUSTAIN:
+ case EG_RELEASE:
+ if (!eg_off && !reset && shift > 0)
+ {
+ eg_inc = 1 << (shift - 1);
+ }
+ break;
+ }
+ slot->in.eg_position = (eg_rout + eg_inc) & 0x1ff;
+ /* Key off */
+ if (reset)
+ {
+ slot->in.eg_state = EG_ATTACK;
+ }
+ if (!key_on_signal)
+ {
+ slot->in.eg_state = EG_RELEASE;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_phase_generate(esfm_slot *slot)
+{
+ esfm_chip *chip;
+ uint10 f_num;
+ uint32 basefreq;
+ bool rm_xor, n_bit;
+ uint23 noise;
+ uint10 phase;
+
+ chip = slot->chip;
+ f_num = slot->f_num;
+ if (slot->vibrato_en)
+ {
+ int8_t range;
+ uint8_t vibpos;
+
+ range = (f_num >> 7) & 7;
+ vibpos = chip->vibrato_pos;
+
+ if (!(vibpos & 3))
+ {
+ range = 0;
+ }
+ else if (vibpos & 1)
+ {
+ range >>= 1;
+ }
+ range >>= !slot->vibrato_deep;
+
+ if (vibpos & 4)
+ {
+ range = -range;
+ }
+ f_num += range;
+ }
+ basefreq = (f_num << slot->block) >> 1;
+ phase = (uint10)(slot->in.phase_acc >> 9);
+ if (slot->in.phase_reset)
+ {
+ slot->in.phase_acc = 0;
+ }
+ slot->in.phase_acc += (basefreq * mt[slot->mult]) >> 1;
+ slot->in.phase_acc &= (1 << 19) - 1;
+ slot->in.phase_out = phase;
+ /* Noise mode (rhythm) sounds */
+ noise = chip->lfsr;
+ if (slot->slot_idx == 3 && slot->rhy_noise)
+ {
+ esfm_slot *prev_slot = &slot->channel->slots[2];
+
+ chip->rm_hh_bit2 = (phase >> 2) & 1;
+ chip->rm_hh_bit3 = (phase >> 3) & 1;
+ chip->rm_hh_bit7 = (phase >> 7) & 1;
+ chip->rm_hh_bit8 = (phase >> 8) & 1;
+
+ chip->rm_tc_bit3 = (prev_slot->in.phase_out >> 3) & 1;
+ chip->rm_tc_bit5 = (prev_slot->in.phase_out >> 5) & 1;
+
+ rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7)
+ | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5)
+ | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5);
+
+ switch(slot->rhy_noise)
+ {
+ case 1:
+ // SD
+ slot->in.phase_out = (chip->rm_hh_bit8 << 9)
+ | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8);
+ break;
+ case 2:
+ // HH
+ slot->in.phase_out = rm_xor << 9;
+ if (rm_xor ^ (noise & 1))
+ {
+ slot->in.phase_out |= 0xd0;
+ }
+ else
+ {
+ slot->in.phase_out |= 0x34;
+ }
+ break;
+ case 3:
+ // TC
+ slot->in.phase_out = (rm_xor << 9) | 0x80;
+ break;
+ }
+ }
+
+ n_bit = ((noise >> 14) ^ noise) & 0x01;
+ chip->lfsr = (noise >> 1) | (n_bit << 22);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_phase_generate_emu(esfm_slot *slot)
+{
+ esfm_chip *chip;
+ uint3 block;
+ uint10 f_num;
+ uint32 basefreq;
+ bool rm_xor, n_bit;
+ uint23 noise;
+ uint10 phase;
+ int pair_primary_idx;
+
+ chip = slot->chip;
+ block = slot->channel->slots[0].block;
+ f_num = slot->channel->slots[0].f_num;
+
+ pair_primary_idx = emu_4op_secondary_to_primary[slot->channel->channel_idx];
+ if (pair_primary_idx >= 0)
+ {
+ esfm_channel *pair_primary = &slot->channel->chip->channels[pair_primary_idx];
+ if (pair_primary->emu_mode_4op_enable)
+ {
+ block = pair_primary->slots[0].block;
+ f_num = pair_primary->slots[0].f_num;
+ }
+ }
+
+ if (slot->vibrato_en)
+ {
+ int8_t range;
+ uint8_t vibpos;
+
+ range = (f_num >> 7) & 7;
+ vibpos = chip->vibrato_pos;
+
+ if (!(vibpos & 3))
+ {
+ range = 0;
+ }
+ else if (vibpos & 1)
+ {
+ range >>= 1;
+ }
+ range >>= !chip->emu_vibrato_deep;
+
+ if (vibpos & 4)
+ {
+ range = -range;
+ }
+ f_num += range;
+ }
+ basefreq = (f_num << block) >> 1;
+ phase = (uint10)(slot->in.phase_acc >> 9);
+ if (slot->in.phase_reset)
+ {
+ slot->in.phase_acc = 0;
+ }
+ slot->in.phase_acc += (basefreq * mt[slot->mult]) >> 1;
+ slot->in.phase_acc &= (1 << 19) - 1;
+ slot->in.phase_out = phase;
+
+ /* Noise mode (rhythm) sounds */
+ noise = chip->lfsr;
+ // HH
+ if (slot->channel->channel_idx == 7 && slot->slot_idx == 0)
+ {
+ chip->rm_hh_bit2 = (phase >> 2) & 1;
+ chip->rm_hh_bit3 = (phase >> 3) & 1;
+ chip->rm_hh_bit7 = (phase >> 7) & 1;
+ chip->rm_hh_bit8 = (phase >> 8) & 1;
+ }
+ // TC
+ if (slot->channel->channel_idx == 8 && slot->slot_idx == 1)
+ {
+ chip->rm_tc_bit3 = (phase >> 3) & 1;
+ chip->rm_tc_bit5 = (phase >> 5) & 1;
+ }
+ if (chip->emu_rhy_mode_flags & 0x20)
+ {
+ rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7)
+ | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5)
+ | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5);
+ if (slot->channel->channel_idx == 7)
+ {
+ if (slot->slot_idx == 0) {
+ // HH
+ slot->in.phase_out = rm_xor << 9;
+ if (rm_xor ^ (noise & 1))
+ {
+ slot->in.phase_out |= 0xd0;
+ }
+ else
+ {
+ slot->in.phase_out |= 0x34;
+ }
+ }
+ else if (slot->slot_idx == 1)
+ {
+ // SD
+ slot->in.phase_out = (chip->rm_hh_bit8 << 9)
+ | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8);
+ }
+ }
+ else if (slot->channel->channel_idx == 8 && slot->slot_idx == 1)
+ {
+ // TC
+ slot->in.phase_out = (rm_xor << 9) | 0x80;
+ }
+ }
+
+ n_bit = ((noise >> 14) ^ noise) & 0x01;
+ chip->lfsr = (noise >> 1) | (n_bit << 22);
+}
+
+/**
+ * TODO: Figure out what's ACTUALLY going on inside the real chip!
+ * This is not accurate at all, but it's the closest I was able to get with
+ * empirical testing (and it's closer than nothing).
+ */
+/* ------------------------------------------------------------------------- */
+static int16
+ESFM_slot3_noise3_mod_input_calc(esfm_slot *slot)
+{
+ esfm_channel *channel = slot->channel;
+ int16 phase;
+ int13 output_buf = *channel->slots[1].in.mod_input;
+ int i;
+
+ // Go through previous slots' partial results and recalculate outputs
+ // (we skip slot 0 because its calculation happens at the end, not at the beginning)
+ for (i = 1; i < 3; i++)
+ {
+ // double the pitch
+ phase = channel->slots[i].in.phase_acc >> 8;
+ if (channel->slots[i].mod_in_level)
+ {
+ phase += output_buf >> (7 - channel->slots[i].mod_in_level);
+ }
+ output_buf = ESFM_envelope_wavegen(channel->slots[2].waveform, phase, channel->slots[i].in.eg_output);
+ }
+
+ return output_buf >> (8 - slot->mod_in_level);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_slot_generate(esfm_slot *slot)
+{
+ int16 phase = slot->in.phase_out;
+ if (slot->mod_in_level)
+ {
+ if (slot->slot_idx == 3 && slot->rhy_noise == 3)
+ {
+ phase += ESFM_slot3_noise3_mod_input_calc(slot);
+ }
+ else
+ {
+ phase += *slot->in.mod_input >> (7 - slot->mod_in_level);
+ }
+ }
+ slot->in.output = ESFM_envelope_wavegen(slot->waveform, phase, slot->in.eg_output);
+ if (slot->output_level)
+ {
+ int13 output_value = slot->in.output >> (7 - slot->output_level);
+ slot->channel->output[0] += output_value & slot->out_enable[0];
+ slot->channel->output[1] += output_value & slot->out_enable[1];
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_slot_generate_emu(esfm_slot *slot)
+{
+ const esfm_chip *chip = slot->chip;
+ uint3 waveform = slot->waveform & (chip->emu_newmode != 0 ? 0x07 : 0x03);
+ bool rhythm_slot_double_volume = (slot->chip->emu_rhy_mode_flags & 0x20) != 0
+ && slot->channel->channel_idx >= 6 && slot->channel->channel_idx < 9;
+ int16 phase = slot->in.phase_out;
+ int14 output_value;
+
+ phase += *slot->in.mod_input & slot->in.emu_mod_enable;
+ slot->in.output = ESFM_envelope_wavegen(waveform, phase, slot->in.eg_output);
+ output_value = (slot->in.output & slot->in.emu_output_enable) << rhythm_slot_double_volume;
+ if (chip->emu_newmode)
+ {
+ slot->channel->output[0] += output_value & slot->channel->slots[0].out_enable[0];
+ slot->channel->output[1] += output_value & slot->channel->slots[0].out_enable[1];
+ }
+ else
+ {
+ slot->channel->output[0] += output_value;
+ slot->channel->output[1] += output_value;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+static void
+ESFM_process_feedback(esfm_chip *chip)
+{
+ int channel_idx;
+
+ for (channel_idx = 0; channel_idx < 18; channel_idx++)
+ {
+ esfm_slot *slot = &chip->channels[channel_idx].slots[0];
+ uint32 basefreq, phase_offset;
+ uint3 block;
+ uint10 f_num;
+ int32_t wave_out, wave_last;
+ int32_t phase_feedback;
+ uint32_t iter_counter;
+ uint3 waveform;
+ uint3 mod_in_shift;
+ uint32_t phase, phase_acc;
+ uint10 eg_output;
+
+ if (slot->mod_in_level && (chip->native_mode || (slot->in.mod_input == &slot->in.feedback_buf)))
+ {
+ if (chip->native_mode)
+ {
+ waveform = slot->waveform;
+ }
+ else
+ {
+ waveform = slot->waveform & (0x03 | (0x02 << (chip->emu_newmode != 0)));
+ }
+ f_num = slot->f_num;
+ block = slot->block;
+ basefreq = (f_num << block) >> 1;
+ phase_offset = (basefreq * mt[slot->mult]) >> 1;
+ mod_in_shift = 7 - slot->mod_in_level;
+ phase_acc = (uint32_t)(slot->in.phase_acc - phase_offset * 28);
+ eg_output = slot->in.eg_output;
+
+ // ASM optimizaions!
+#if defined(__GNUC__) && defined(__x86_64__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS)
+ asm (
+ "movzbq %[wave], %%r8 \n\t"
+ "shll $11, %%r8d \n\t"
+ "leaq %[sinrom], %%rax \n\t"
+ "addq %%rax, %%r8 \n\t"
+ "leaq %[exprom], %%r9 \n\t"
+ "movzwl %[eg_out], %%r10d \n\t"
+ "shll $3, %%r10d \n\t"
+ "xorl %%r11d, %%r11d \n\t"
+ "movl %%r11d, %[out] \n\t"
+ "movl $29, %%edx \n"
+ "1: \n\t"
+ // phase_feedback = (wave_out + wave_last) >> 2;
+ "movl %[out], %[p_fb] \n\t"
+ "addl %%r11d, %[p_fb] \n\t"
+ "sarl $2, %[p_fb] \n\t"
+ // wave_last = wave_out
+ "movl %[out], %%r11d \n\t"
+ // phase = phase_feedback >> mod_in_shift;
+ "movl %[p_fb], %%eax \n\t"
+ "movb %[mod_in], %%cl \n\t"
+ "sarl %%cl, %%eax \n\t"
+ // phase += phase_acc >> 9;
+ "movl %[p_acc], %%ebx \n\t"
+ "sarl $9, %%ebx \n\t"
+ "addl %%ebx, %%eax \n\t"
+ // lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)];
+ "andq $0x3ff, %%rax \n\t"
+ "movzwl (%%r8, %%rax, 2), %%ebx \n\t"
+ "movl %%ebx, %%eax \n\t"
+ // level = (lookup & 0x1fff) + (envelope << 3);
+ "movl $0x1fff, %%ecx \n\t"
+ "andl %%ecx, %%eax \n\t"
+ "addl %%r10d, %%eax \n\t"
+ // if (level > 0x1fff) level = 0x1fff;
+ "cmpl %%ecx, %%eax \n\t"
+ "cmoval %%ecx, %%eax \n\t"
+ // wave_out = exprom[level & 0xff] >> (level >> 8);
+ "movb %%ah, %%cl \n\t"
+ "movzbl %%al, %%eax \n\t"
+ "movzwl (%%r9, %%rax, 2), %[out] \n\t"
+ "shrl %%cl, %[out] \n\t"
+ // if (lookup & 0x8000) wave_out = -wave_out;
+ // in other words, lookup is negative
+ "movl %[out], %%ecx \n\t"
+ "negl %%ecx \n\t"
+ "testw %%bx, %%bx \n\t"
+ "cmovsl %%ecx, %[out] \n\t"
+ // phase_acc += phase_offset
+ "addl %[p_off], %[p_acc] \n\t"
+ // loop
+ "decl %%edx \n\t"
+ "jne 1b \n\t"
+ : [p_fb] "=&r" (phase_feedback),
+ [p_acc] "+r" (phase_acc),
+ [out] "=&r" (wave_out)
+ : [p_off] "r" (phase_offset),
+ [mod_in] "r" (mod_in_shift),
+ [wave] "g" (waveform),
+ [eg_out] "g" (eg_output),
+ [sinrom] "m" (logsinrom),
+ [exprom] "m" (exprom)
+ : "cc", "ax", "bx", "cx", "dx", "r8", "r9", "r10", "r11"
+ );
+#elif defined(__GNUC__) && defined(__i386__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS)
+ size_t logsinrom_addr = (size_t)logsinrom;
+ size_t exprom_addr = (size_t)exprom;
+
+ asm (
+ "movzbl %b[wave], %%eax \n\t"
+ "shll $11, %%eax \n\t"
+ "movl %[sinrom], %%edi \n\t"
+ "addl %%eax, %%edi \n\t"
+ "shlw $3, %[eg_out] \n\t"
+ "xorl %[out], %[out] \n\t"
+ "movl %[out], %[last] \n\t"
+ "movl $29, %[i] \n"
+ "1: \n\t"
+ // phase_feedback = (wave_out + wave_last) >> 2;
+ "movl %[out], %%eax \n\t"
+ "addl %[last], %%eax \n\t"
+ "sarl $2, %%eax \n\t"
+ "movl %%eax, %[p_fb] \n\t"
+ // wave_last = wave_out
+ "movl %[out], %[last] \n\t"
+ // phase = phase_feedback >> mod_in_shift;
+ "movb %[mod_in], %%cl \n\t"
+ "sarl %%cl, %%eax \n\t"
+ // phase += phase_acc >> 9;
+ "movl %[p_acc], %%ebx \n\t"
+ "shrl $9, %%ebx \n\t"
+ "addl %%ebx, %%eax \n\t"
+ // lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)];
+ "andl $0x3ff, %%eax \n\t"
+ "movzwl (%%edi, %%eax, 2), %%ebx \n\t"
+ "movl %%ebx, %%eax \n\t"
+ // level = (lookup & 0x1fff) + (envelope << 3);
+ "movl $0x1fff, %%ecx \n\t"
+ "andl %%ecx, %%eax \n\t"
+ "addw %[eg_out], %%ax \n\t"
+ // if (level > 0x1fff) level = 0x1fff;
+ "cmpl %%ecx, %%eax \n\t"
+ "cmoval %%ecx, %%eax \n\t"
+ // wave_out = exprom[level & 0xff] >> (level >> 8);
+ "movb %%ah, %%cl \n\t"
+ "movzbl %%al, %%eax \n\t"
+ "movl %[exprom], %[out] \n\t"
+ "movzwl (%[out], %%eax, 2), %[out] \n\t"
+ "shrl %%cl, %[out] \n\t"
+ // if (lookup & 0x8000) wave_out = -wave_out;
+ // in other words, lookup is negative
+ "movl %[out], %%ecx \n\t"
+ "negl %%ecx \n\t"
+ "testw %%bx, %%bx \n\t"
+ "cmovsl %%ecx, %[out] \n\t"
+ // phase_acc += phase_offset
+ "addl %[p_off], %[p_acc] \n\t"
+ // loop
+ "decl %[i] \n\t"
+ "jne 1b \n\t"
+ : [p_fb] "=&m" (phase_feedback),
+ [p_acc] "+r" (phase_acc),
+ [out] "=&r" (wave_out),
+ [last] "=&m" (wave_last),
+ [eg_out] "+m" (eg_output)
+ : [p_off] "m" (phase_offset),
+ [mod_in] "m" (mod_in_shift),
+ [wave] "m" (waveform),
+ [sinrom] "m" (logsinrom_addr),
+ [exprom] "m" (exprom_addr),
+ [i] "m" (iter_counter)
+ : "cc", "ax", "bx", "cx", "di"
+ );
+#elif defined(__GNUC__) && defined(__arm__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS)
+ asm (
+ "movs r3, #0 \n\t"
+ "movs %[out], #0 \n\t"
+ "ldr r8, =0x1fff \n\t"
+ "movs r2, #29 \n"
+ "1: \n\t"
+ // phase_feedback = (wave_out + wave_last) >> 2;
+ "adds %[p_fb], %[out], r3 \n\t"
+ "asrs %[p_fb], %[p_fb], #2 \n\t"
+ // wave_last = wave_out
+ "mov r3, %[out] \n\t"
+ // phase = phase_feedback >> mod_in_shift;
+ "asr r0, %[p_fb], %[mod_in] \n\t"
+ // phase += phase_acc >> 9;
+ "add r0, r0, %[p_acc], asr #9 \n\t"
+ // lookup = logsinrom[(waveform << 10) | (phase & 0x3ff)];
+ "lsls r0, r0, #22 \n\t"
+ "lsrs r0, r0, #21 \n\t"
+ "ldrsh r1, [%[sinrom], r0] \n\t"
+ // level = (lookup & 0x1fff) + (envelope << 3);
+ "and r0, r8, r1 \n\t"
+ "add r0, r0, %[eg_out], lsl #3 \n\t"
+ // if (level > 0x1fff) level = 0x1fff;
+ "cmp r0, r8 \n\t"
+ "it hi \n\t"
+ "movhi r0, r8 \n\t"
+ // wave_out = exprom[level & 0xff] >> (level >> 8);
+ "lsrs %[out], r0, #8 \n\t"
+ "ands r0, r0, #255 \n\t"
+ "lsls r0, r0, #1 \n\t"
+ "ldrh r0, [%[exprom], r0] \n\t"
+ "lsr %[out], r0, %[out] \n\t"
+ // if (lookup & 0x8000) wave_out = -wave_out;
+ // in other words, lookup is negative
+ "tst r1, r1 \n\t"
+ "it mi \n\t"
+ "negmi %[out], %[out] \n\t"
+ // phase_acc += phase_offset
+ "adds %[p_acc], %[p_acc], %[p_off]\n\t"
+ // loop
+ "subs r2, r2, #1 \n\t"
+ "bne 1b \n\t"
+ : [p_fb] "=&r" (phase_feedback),
+ [p_acc] "+r" (phase_acc),
+ [out] "=&r" (wave_out)
+ : [p_off] "r" (phase_offset),
+ [mod_in] "r" (mod_in_shift),
+ [eg_out] "r" (eg_output),
+ [sinrom] "r" (logsinrom + waveform * 1024),
+ [exprom] "r" (exprom)
+ : "cc", "r0", "r1", "r2", "r3", "r8"
+ );
+#else
+ wave_out = 0;
+ wave_last = 0;
+ for (iter_counter = 0; iter_counter < 29; iter_counter++)
+ {
+ phase_feedback = (wave_out + wave_last) >> 2;
+ wave_last = wave_out;
+ phase = phase_feedback >> mod_in_shift;
+ phase += phase_acc >> 9;
+ wave_out = ESFM_envelope_wavegen(waveform, phase, eg_output);
+ phase_acc += phase_offset;
+ }
+#endif
+
+ // TODO: Figure out - is this how the ESFM chip does it, like the
+ // patent literally says? (it's really hacky...)
+ // slot->in.output = wave_out;
+
+ // This would be the more canonical way to do it, reusing the rest of
+ // the synthesis pipeline to finish the calculation:
+ if (chip->native_mode)
+ {
+ slot->in.feedback_buf = phase_feedback;
+ }
+ else
+ {
+ slot->in.feedback_buf = phase_feedback >> (7 - slot->mod_in_level);
+ }
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_process_channel(esfm_channel *channel)
+{
+ int slot_idx;
+ channel->output[0] = channel->output[1] = 0;
+ for (slot_idx = 0; slot_idx < 4; slot_idx++)
+ {
+ esfm_slot *slot = &channel->slots[slot_idx];
+ ESFM_envelope_calc(slot);
+ ESFM_phase_generate(slot);
+ if(slot_idx > 0)
+ {
+ ESFM_slot_generate(slot);
+ }
+ }
+ // ESFM feedback calculation takes a large number of clock cycles, so
+ // defer slot 0 generation to the end
+ // TODO: verify this behavior on real hardware
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_process_channel_emu(esfm_channel *channel)
+{
+ int slot_idx;
+ channel->output[0] = channel->output[1] = 0;
+ for (slot_idx = 0; slot_idx < 2; slot_idx++)
+ {
+ esfm_slot *slot = &channel->slots[slot_idx];
+ ESFM_envelope_calc(slot);
+ ESFM_phase_generate_emu(slot);
+ if(slot_idx > 0)
+ {
+ ESFM_slot_generate_emu(slot);
+ }
+ }
+ // ESFM feedback calculation takes a large number of clock cycles, so
+ // defer slot 0 generation to the end
+ // TODO: verify this behavior on real hardware
+}
+
+/* ------------------------------------------------------------------------- */
+static int16_t
+ESFM_clip_sample(int32 sample)
+{
+ // TODO: Supposedly, the real ESFM chip actually overflows rather than
+ // clipping. Verify that.
+ if (sample > 32767)
+ {
+ sample = 32767;
+ }
+ else if (sample < -32768)
+ {
+ sample = -32768;
+ }
+ return (int16_t)sample;
+}
+
+#define TIMER1_CONST (0.2517482517482517)
+#define TIMER2_CONST (0.06293706293706293)
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_update_timers(esfm_chip *chip)
+{
+ int i;
+ // Tremolo
+ if ((chip->global_timer & 0x3f) == 0x3f)
+ {
+ chip->tremolo_pos = (chip->tremolo_pos + 1) % 210;
+ if (chip->tremolo_pos < 105)
+ {
+ chip->tremolo = chip->tremolo_pos;
+ }
+ else
+ {
+ chip->tremolo = (210 - chip->tremolo_pos);
+ }
+ }
+
+ // Vibrato
+ if ((chip->global_timer & 0x3ff) == 0x3ff)
+ {
+ chip->vibrato_pos = (chip->vibrato_pos + 1) & 0x07;
+ }
+
+ chip->global_timer = (chip->global_timer + 1) & 0x3ff;
+
+ // Envelope generator dither clocks
+ chip->eg_clocks = 0;
+ if (chip->eg_timer)
+ {
+ uint8 shift = 0;
+ while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0)
+ {
+ shift++;
+ }
+
+ if (shift <= 12)
+ {
+ chip->eg_clocks = shift + 1;
+ }
+ }
+
+ if (chip->eg_tick || chip->eg_timer_overflow)
+ {
+ if (chip->eg_timer == (1llu << 36) - 1)
+ {
+ chip->eg_timer = 0;
+ chip->eg_timer_overflow = 1;
+ }
+ else
+ {
+ chip->eg_timer++;
+ chip->eg_timer_overflow = 0;
+ }
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ if (chip->timer_enable[i])
+ {
+ chip->timer_accumulator[i] += (i == 0) ? TIMER1_CONST : TIMER2_CONST;
+ if (chip->timer_accumulator[i] > 1.0)
+ {
+ chip->timer_accumulator[i] -= 1.0;
+ chip->timer_counter[i]++;
+ if (chip->timer_counter[i] == 0)
+ {
+ if (chip->timer_mask[i] == 0)
+ {
+ chip->irq_bit = true;
+ chip->timer_overflow[i] = true;
+ }
+ chip->timer_counter[i] = chip->timer_reload[i];
+ }
+ }
+ }
+ }
+
+ chip->eg_tick ^= 1;
+}
+
+#define KEY_ON_REGS_START (18 * 4 * 8)
+/* ------------------------------------------------------------------------- */
+int
+ESFM_reg_write_chan_idx(esfm_chip *chip, uint16_t reg)
+{
+ int which_reg = -1;
+ if (chip->native_mode)
+ {
+ bool is_key_on_reg = reg >= KEY_ON_REGS_START && reg < (KEY_ON_REGS_START + 20);
+ if (is_key_on_reg)
+ {
+ which_reg = reg - KEY_ON_REGS_START;
+ }
+ }
+ else
+ {
+ uint8_t reg_low = reg & 0xff;
+ bool high = reg & 0x100;
+ bool is_key_on_reg = reg_low >= 0xb0 && reg_low < 0xb9;
+ if (is_key_on_reg)
+ {
+ which_reg = (reg_low & 0x0f) + high * 9;
+ }
+ }
+
+ return which_reg;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_update_write_buffer(esfm_chip *chip)
+{
+ esfm_write_buf *write_buf;
+ bool note_off_written[20];
+ bool bassdrum_written = false;
+ int i;
+ for (i = 0; i < 20; i++)
+ {
+ note_off_written[i] = false;
+ }
+ while((write_buf = &chip->write_buf[chip->write_buf_start]),
+ write_buf->valid && write_buf->timestamp <= chip->write_buf_timestamp)
+ {
+ int is_which_note_on_reg =
+ ESFM_reg_write_chan_idx(chip, write_buf->address);
+ if (is_which_note_on_reg >= 0)
+ {
+ if ((chip->native_mode && (write_buf->data & 0x01) == 0)
+ || (!chip->native_mode && (write_buf->data & 0x20) == 0)
+ )
+ {
+ // this is a note off command; note down that we got note off for this channel
+ note_off_written[is_which_note_on_reg] = true;
+ }
+ else
+ {
+ // this is a note on command; have we gotten a note off for this channel in this cycle?
+ if (note_off_written[is_which_note_on_reg])
+ {
+ // we have a conflict; let the note off be processed first and defer the
+ // rest of the buffer to the next cycle
+ break;
+ }
+ }
+ }
+ if ((chip->native_mode && write_buf->address == 0x4bd)
+ || (!chip->native_mode && (write_buf->address & 0xff) == 0xbd)
+ )
+ {
+ // bassdrum register write (rhythm mode note-on/off control)
+ // have we already written to the bassdrum register in this cycle
+ if (bassdrum_written) {
+ // we have a conflict
+ break;
+ }
+ bassdrum_written = true;
+ }
+
+ write_buf->valid = 0;
+ ESFM_write_reg(chip, write_buf->address, write_buf->data);
+ chip->write_buf_start = (chip->write_buf_start + 1) % ESFM_WRITEBUF_SIZE;
+ }
+
+ chip->write_buf_timestamp++;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_generate(esfm_chip *chip, int32_t *buf)
+{
+ int channel_idx;
+
+ chip->output_accm[0] = chip->output_accm[1] = 0;
+ for (channel_idx = 0; channel_idx < 18; channel_idx++)
+ {
+ esfm_channel *channel = &chip->channels[channel_idx];
+ if (chip->native_mode)
+ {
+ ESFM_process_channel(channel);
+ }
+ else
+ {
+ ESFM_process_channel_emu(channel);
+ }
+ }
+ ESFM_process_feedback(chip);
+ for (channel_idx = 0; channel_idx < 18; channel_idx++)
+ {
+ esfm_channel *channel = &chip->channels[channel_idx];
+ if (chip->native_mode)
+ {
+ ESFM_slot_generate(&channel->slots[0]);
+ }
+ else
+ {
+ ESFM_slot_generate_emu(&channel->slots[0]);
+ }
+ chip->output_accm[0] += channel->output[0];
+ chip->output_accm[1] += channel->output[1];
+ }
+
+ buf[0] = chip->output_accm[0];
+ buf[1] = chip->output_accm[1];
+
+ ESFM_update_timers(chip);
+ ESFM_update_write_buffer(chip);
+}
+
+/* ------------------------------------------------------------------------- */
+int16_t
+ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx)
+{
+ int16_t result;
+ int32_t temp_mix = 0;
+ int i;
+
+ if (channel_idx < 0 || channel_idx >= 18)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ esfm_slot *slot = &chip->channels[channel_idx].slots[i];
+
+ if (slot->output_level)
+ {
+ int13 output_value = slot->in.output >> (7 - slot->output_level);
+ temp_mix += output_value & slot->out_enable[0];
+ temp_mix += output_value & slot->out_enable[1];
+ }
+ }
+
+ if (temp_mix > 32767)
+ {
+ temp_mix = 32767;
+ }
+ else if (temp_mix < -32768)
+ {
+ temp_mix = -32768;
+ }
+ result = temp_mix;
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_generate_stream(esfm_chip *chip, int16_t *sndptr, uint32_t num_samples)
+{
+ uint32_t i;
+ int32_t buf[2] = { 0 };
+
+ for (i = 0; i < num_samples; i++)
+ {
+ ESFM_generate(chip, buf);
+ sndptr[0] = ESFM_clip_sample(buf[0]);
+ sndptr[1] = ESFM_clip_sample(buf[1]);
+ sndptr += 2;
+ }
+}
diff --git a/src/sound/esfmu/esfm.h b/src/sound/esfmu/esfm.h
new file mode 100644
index 000000000..766c7312d
--- /dev/null
+++ b/src/sound/esfmu/esfm.h
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ * - akumanatt
+ * For helping out with code optimization.
+ * - 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, int32_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 ebit;
+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;
+ ebit 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;
+ ebit phase_reset;
+ ebit *key_on;
+ ebit key_on_gate;
+
+ uint2 eg_state;
+ ebit eg_delay_run;
+ ebit eg_delay_transitioned_10;
+ ebit eg_delay_transitioned_10_gate;
+ ebit eg_delay_transitioned_01;
+ ebit 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;
+
+ ebit tremolo_en;
+ ebit tremolo_deep;
+ ebit vibrato_en;
+ ebit vibrato_deep;
+ ebit emu_connection_typ;
+ ebit env_sustaining;
+ ebit 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
+ ebit 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];
+ ebit key_on;
+ ebit emu_mode_4op_enable;
+ // Only for 17th and 18th channels
+ ebit key_on_2;
+ ebit 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];
+ uint16 addr_latch;
+
+ ebit emu_wavesel_enable;
+ ebit emu_newmode;
+ ebit native_mode;
+
+ ebit keyscale_mode;
+
+ // Global state
+ uint36 eg_timer;
+ uint10 global_timer;
+ uint8 eg_clocks;
+ ebit eg_tick;
+ ebit eg_timer_overflow;
+ uint8 tremolo;
+ uint8 tremolo_pos;
+ uint8 vibrato_pos;
+ uint23 lfsr;
+
+ ebit rm_hh_bit2;
+ ebit rm_hh_bit3;
+ ebit rm_hh_bit7;
+ ebit rm_hh_bit8;
+ ebit rm_tc_bit3;
+ ebit rm_tc_bit5;
+
+ // 0xbd register in emulation mode, exposed in 0x4bd in native mode
+ // ("bass drum" register)
+ uint8 emu_rhy_mode_flags;
+
+ ebit emu_vibrato_deep;
+ ebit emu_tremolo_deep;
+
+ double timer_accumulator[2];
+ uint8 timer_reload[2];
+ uint8 timer_counter[2];
+ ebit timer_enable[2];
+ ebit timer_mask[2];
+ ebit timer_overflow[2];
+ ebit irq_bit;
+
+ // -- Test bits (NOT IMPLEMENTED) --
+ // Halts the envelope generators from advancing. Written on bit 0, read back from bit 5.
+ ebit test_bit_w0_r5_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!)
+ */
+ ebit test_bit_1_distort;
+ // Seems to do nothing.
+ ebit test_bit_2;
+ // Seems to do nothing.
+ ebit test_bit_3;
+ // Appears to attenuate the output by about 3 dB.
+ ebit test_bit_4_attenuate;
+ // Written on bit 5, read back from bit 0. Seems to do nothing.
+ ebit test_bit_w5_r0;
+ // Resets all phase generators and holds them in the reset state while this bit is set.
+ ebit test_bit_6_phase_stop_reset;
+ // Seems to do nothing.
+ ebit test_bit_7;
+
+ 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
diff --git a/src/sound/esfmu/esfm_registers.c b/src/sound/esfmu/esfm_registers.c
new file mode 100644
index 000000000..e2f432ed1
--- /dev/null
+++ b/src/sound/esfmu/esfm_registers.c
@@ -0,0 +1,1009 @@
+/*
+ * ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone
+ * Copyright (C) 2023 Kagamiin~
+ *
+ * This file includes code and data from the Nuked OPL3 project, copyright (C)
+ * 2013-2023 Nuke.YKT. Its usage, modification and redistribution is allowed
+ * under the terms of the GNU Lesser General Public License version 2.1 or
+ * later.
+ *
+ * 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 "esfm.h"
+#include
+#include
+#include
+#include
+
+
+/*
+ * Table of KSL values extracted from OPL3 ROM; taken straight from Nuked OPL3
+ * source code.
+ * TODO: Check if ESFM uses the same KSL values.
+ */
+
+static const int16 kslrom[16] = {
+ 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64
+};
+
+/*
+ * This maps the low 5 bits of emulation mode address to an emulation mode
+ * slot; taken straight from Nuked OPL3. Used for decoding certain emulation
+ * mode address ranges.
+ */
+static const int8_t ad_slot[0x20] = {
+ 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
+ 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/*
+ * This maps an emulation mode slot index to a tuple representing the
+ * corresponding native mode channel and slot.
+ */
+static const emu_slot_channel_mapping emu_slot_map[36] =
+{
+ { 0, 0}, { 1, 0}, { 2, 0}, { 0, 1}, { 1, 1}, { 2, 1},
+ { 3, 0}, { 4, 0}, { 5, 0}, { 3, 1}, { 4, 1}, { 5, 1},
+ { 6, 0}, { 7, 0}, { 8, 0}, { 6, 1}, { 7, 1}, { 8, 1},
+ { 9, 0}, {10, 0}, {11, 0}, { 9, 1}, {10, 1}, {11, 1},
+ {12, 0}, {13, 0}, {14, 0}, {12, 1}, {13, 1}, {14, 1},
+ {15, 0}, {16, 0}, {17, 0}, {15, 1}, {16, 1}, {17, 1}
+};
+
+/*
+ * This encodes which emulation mode channels are the secondary channel in a
+ * 4-op channel pair (where the entry is non-negative), and which is the
+ * corresponding primary channel for that secondary channel.
+ */
+static const int emu_4op_secondary_to_primary[18] =
+{
+ -1, -1, -1, 0, 1, 2, -1, -1, -1,
+ -1, -1, -1, 9, 10, 11, -1, -1, -1
+};
+
+/*
+ * This encodes the operator outputs to be enabled or disabled for
+ * each 4-op algorithm in emulation mode.
+ * Indices: FM+FM, FM+AM, AM+FM, AM+AM (lower channel MSB, upper channel LSB)
+ * Values: enable OP1, OP2, OP3, OP4
+ */
+static const bool emu_4op_alg_output_enable[4][4] =
+{
+ {0, 0, 0, 1},
+ {0, 1, 0, 1},
+ {1, 0, 0, 1},
+ {1, 0, 1, 1}
+};
+
+/*
+ * This encodes the operator interconnections to be enabled or disabled for
+ * each 4-op algorithm in emulation mode.
+ * Indices: FM+FM, FM+AM, AM+FM, AM+AM (lower channel MSB, upper channel LSB)
+ * Values: enable OP1FB, OP1->2, OP2->3, OP3->4
+ */
+static const bool emu_4op_alg_mod_enable[4][4] =
+{
+ {1, 1, 1, 1},
+ {1, 1, 0, 1},
+ {1, 0, 1, 1},
+ {1, 0, 1, 0}
+};
+
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_emu_rearrange_connections(esfm_channel *channel)
+{
+ int secondary_to_primary;
+
+ secondary_to_primary = emu_4op_secondary_to_primary[channel->channel_idx];
+ if (secondary_to_primary >= 0)
+ {
+ esfm_channel *pair_primary = &channel->chip->channels[secondary_to_primary];
+ if (pair_primary->emu_mode_4op_enable)
+ {
+ // always work from primary channel in pair when dealing with 4-op
+ channel = pair_primary;
+ }
+ }
+
+ if (channel->emu_mode_4op_enable && (channel->channel_idx % 9) < 3 && channel->chip->emu_newmode)
+ {
+ esfm_channel *secondary = &channel->chip->channels[channel->channel_idx + 3];
+ uint2 algorithm = ((channel->slots[0].emu_connection_typ != 0) << 1)
+ | (secondary->slots[0].emu_connection_typ != 0);
+ int i;
+
+ secondary->slots[0].in.mod_input = &channel->slots[1].in.output;
+
+ for (i = 0; i < 2; i++)
+ {
+ channel->slots[i].in.emu_mod_enable =
+ emu_4op_alg_mod_enable[algorithm][i] ? ~((int13) 0) : 0;
+ channel->slots[i].in.emu_output_enable =
+ emu_4op_alg_output_enable[algorithm][i] ? ~((int13) 0) : 0;
+
+ secondary->slots[i].in.emu_mod_enable =
+ emu_4op_alg_mod_enable[algorithm][i + 2] ? ~((int13) 0) : 0;
+ secondary->slots[i].in.emu_output_enable =
+ emu_4op_alg_output_enable[algorithm][i + 2] ? ~((int13) 0) : 0;
+ }
+ }
+ else if ((channel->chip->emu_rhy_mode_flags & 0x20) != 0
+ && (channel->channel_idx == 7 || channel->channel_idx == 8))
+ {
+ channel->slots[0].in.emu_mod_enable = 0;
+ channel->slots[1].in.emu_mod_enable = 0;
+ channel->slots[0].in.emu_output_enable = ~((int13) 0);
+ channel->slots[1].in.emu_output_enable = ~((int13) 0);
+ }
+ else
+ {
+ channel->slots[0].in.mod_input = &channel->slots[0].in.feedback_buf;
+
+ channel->slots[0].in.emu_mod_enable = ~((int13) 0);
+ channel->slots[0].in.emu_output_enable =
+ (channel->slots[0].emu_connection_typ != 0) ? ~((int13) 0) : 0;
+ channel->slots[1].in.emu_output_enable = ~((int13) 0);
+ channel->slots[1].in.emu_mod_enable =
+ (channel->slots[0].emu_connection_typ != 0) ? 0 : ~((int13) 0);
+ }
+}
+
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_emu_to_native_switch(esfm_chip *chip)
+{
+ size_t channel_idx, slot_idx;
+ for (channel_idx = 0; channel_idx < 18; channel_idx++)
+ {
+ for (slot_idx = 0; slot_idx < 4; slot_idx++)
+ {
+ esfm_channel *channel = &chip->channels[channel_idx];
+ esfm_slot *slot = &channel->slots[slot_idx];
+
+ if (slot_idx == 0)
+ {
+ slot->in.mod_input = &slot->in.feedback_buf;
+ }
+ else
+ {
+ esfm_slot *prev_slot = &channel->slots[slot_idx - 1];
+ slot->in.mod_input = &prev_slot->in.output;
+ }
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_native_to_emu_switch(esfm_chip *chip)
+{
+ size_t channel_idx;
+ for (channel_idx = 0; channel_idx < 18; channel_idx++)
+ {
+ ESFM_emu_rearrange_connections(&chip->channels[channel_idx]);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_slot_update_keyscale(esfm_slot *slot)
+{
+ if (slot->slot_idx > 0 && !slot->chip->native_mode)
+ {
+ return;
+ }
+
+ int16 ksl = (kslrom[slot->f_num >> 6] << 2) - ((0x08 - slot->block) << 5);
+ if (ksl < 0)
+ {
+ ksl = 0;
+ }
+ slot->in.eg_ksl_offset = ksl;
+ slot->in.keyscale = (slot->block << 1)
+ | ((slot->f_num >> (8 + !slot->chip->keyscale_mode)) & 0x01);
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_emu_channel_update_keyscale(esfm_channel *channel)
+{
+ int secondary_to_primary;
+
+ secondary_to_primary = emu_4op_secondary_to_primary[channel->channel_idx];
+ if (secondary_to_primary >= 0)
+ {
+ esfm_channel *pair_primary = &channel->chip->channels[secondary_to_primary];
+ if (pair_primary->emu_mode_4op_enable)
+ {
+ // always work from primary channel in pair when dealing with 4-op
+ channel = pair_primary;
+ }
+ }
+
+ ESFM_slot_update_keyscale(&channel->slots[0]);
+ channel->slots[1].in.eg_ksl_offset = channel->slots[0].in.eg_ksl_offset;
+ channel->slots[1].in.keyscale = channel->slots[0].in.keyscale;
+
+ if (channel->emu_mode_4op_enable && (channel->channel_idx % 9) < 3 && channel->chip->emu_newmode)
+ {
+ int i;
+ esfm_channel *secondary = &channel->chip->channels[channel->channel_idx + 3];
+ secondary->slots[0].f_num = channel->slots[0].f_num;
+ secondary->slots[0].block = channel->slots[0].block;
+
+ for (i = 0; i < 2; i++)
+ {
+ secondary->slots[i].in.eg_ksl_offset = channel->slots[0].in.eg_ksl_offset;
+ secondary->slots[i].in.keyscale = channel->slots[0].in.keyscale;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static inline uint8_t
+ESFM_slot_readback (esfm_slot *slot, uint8_t register_idx)
+{
+ uint8_t data = 0;
+ switch (register_idx & 0x07)
+ {
+ case 0x00:
+ data |= (slot->tremolo_en != 0) << 7;
+ data |= (slot->vibrato_en != 0) << 6;
+ data |= (slot->env_sustaining != 0) << 5;
+ data |= (slot->vibrato_en != 0) << 4;
+ data |= slot->mult & 0x0f;
+ break;
+ case 0x01:
+ data |= slot->ksl << 6;
+ data |= slot->t_level & 0x3f;
+ break;
+ case 0x02:
+ data |= slot->attack_rate << 4;
+ data |= slot->decay_rate & 0x0f;
+ break;
+ case 0x03:
+ data |= slot->sustain_lvl << 4;
+ data |= slot->release_rate & 0x0f;
+ break;
+ case 0x04:
+ data = slot->f_num & 0xff;
+ break;
+ case 0x05:
+ data |= slot->env_delay << 5;
+ data |= (slot->block & 0x07) << 2;
+ data |= (slot->f_num >> 8) & 0x03;
+ break;
+ case 0x06:
+ data |= (slot->tremolo_deep != 0) << 7;
+ data |= (slot->vibrato_deep != 0) << 6;
+ data |= (slot->out_enable[1] != 0) << 5;
+ data |= (slot->out_enable[0] != 0) << 4;
+ data |= (slot->mod_in_level & 0x07) << 1;
+ data |= slot->emu_connection_typ & 0x01;
+ break;
+ case 0x07:
+ data |= slot->output_level << 5;
+ data |= (slot->rhy_noise & 0x03) << 3;
+ data |= slot->waveform & 0x07;
+ break;
+ }
+ return data;
+}
+
+/* ------------------------------------------------------------------------- */
+static inline void
+ESFM_slot_write (esfm_slot *slot, uint8_t register_idx, uint8_t data)
+{
+ switch (register_idx & 0x07)
+ {
+ case 0x00:
+ slot->tremolo_en = (data & 0x80) != 0;
+ slot->vibrato_en = (data & 0x40) != 0;
+ slot->env_sustaining = (data & 0x20) != 0;
+ slot->ksr = (data & 0x10) != 0;
+ slot->mult = data & 0x0f;
+ break;
+ case 0x01:
+ slot->ksl = data >> 6;
+ slot->t_level = data & 0x3f;
+ ESFM_slot_update_keyscale(slot);
+ break;
+ case 0x02:
+ slot->attack_rate = data >> 4;
+ slot->decay_rate = data & 0x0f;
+ break;
+ case 0x03:
+ slot->sustain_lvl = data >> 4;
+ slot->release_rate = data & 0x0f;
+ break;
+ case 0x04:
+ slot->f_num = (slot->f_num & 0x300) | data;
+ ESFM_slot_update_keyscale(slot);
+ break;
+ case 0x05:
+ if (slot->env_delay < (data >> 5))
+ {
+ slot->in.eg_delay_transitioned_01 = 1;
+ }
+ else if (slot->env_delay > (data >> 5))
+ {
+ slot->in.eg_delay_transitioned_10 = 1;
+ }
+ slot->env_delay = data >> 5;
+ slot->emu_key_on = (data >> 5) & 0x01;
+ slot->block = (data >> 2) & 0x07;
+ slot->f_num = (slot->f_num & 0xff) | ((data & 0x03) << 8);
+ ESFM_slot_update_keyscale(slot);
+ break;
+ case 0x06:
+ slot->tremolo_deep = (data & 0x80) != 0;
+ slot->vibrato_deep = (data & 0x40) != 0;
+ slot->out_enable[1] = (data & 0x20) ? ~((int13) 0) : 0;
+ slot->out_enable[0] = (data & 0x10) ? ~((int13) 0) : 0;
+ slot->mod_in_level = (data >> 1) & 0x07;
+ slot->emu_connection_typ = data & 0x01;
+ break;
+ case 0x07:
+ slot->output_level = data >> 5;
+ slot->rhy_noise = (data >> 3) & 0x03;
+ slot->waveform = data & 0x07;
+ break;
+ }
+}
+
+#define KEY_ON_REGS_START (18 * 4 * 8)
+#define TIMER1_REG (0x402)
+#define TIMER2_REG (0x403)
+#define TIMER_SETUP_REG (0x404)
+#define CONFIG_REG (0x408)
+#define BASSDRUM_REG (0x4bd)
+#define TEST_REG (0x501)
+#define FOUROP_CONN_REG (0x504)
+#define NATIVE_MODE_REG (0x505)
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_write_reg_native (esfm_chip *chip, uint16_t address, uint8_t data)
+{
+ int i;
+ address = address & 0x7ff;
+
+ if (address < KEY_ON_REGS_START)
+ {
+ // Slot register write
+ size_t channel_idx = address >> 5;
+ size_t slot_idx = (address >> 3) & 0x03;
+ size_t register_idx = address & 0x07;
+ esfm_slot *slot = &chip->channels[channel_idx].slots[slot_idx];
+
+ ESFM_slot_write(slot, register_idx, data);
+ }
+ else if (address < KEY_ON_REGS_START + 16)
+ {
+ // Key-on registers
+ size_t channel_idx = (address - KEY_ON_REGS_START);
+ esfm_channel *channel = &chip->channels[channel_idx];
+ channel->key_on = data & 0x01;
+ channel->emu_mode_4op_enable = (data & 0x02) != 0;
+ }
+ else if (address < KEY_ON_REGS_START + 20)
+ {
+ // Key-on channels 17 and 18 (each half)
+ size_t channel_idx = 16 + ((address & 0x02) >> 1);
+ bool second_half = address & 0x01;
+ esfm_channel *channel = &chip->channels[channel_idx];
+ if (second_half)
+ {
+ channel->key_on_2 = data & 0x01;
+ channel->emu_mode_4op_enable_2 = (data & 0x02) != 0;
+ }
+ else
+ {
+ channel->key_on = data & 0x01;
+ channel->emu_mode_4op_enable = (data & 0x02) != 0;
+ }
+ }
+ else
+ {
+ switch (address & 0x5ff)
+ {
+ case TIMER1_REG:
+ chip->timer_reload[0] = data;
+ chip->timer_counter[0] = data;
+ break;
+ case TIMER2_REG:
+ chip->timer_reload[1] = data;
+ chip->timer_counter[1] = data;
+ break;
+ case TIMER_SETUP_REG:
+ if (data & 0x80)
+ {
+ chip->irq_bit = 0;
+ chip->timer_overflow[0] = 0;
+ chip->timer_overflow[1] = 0;
+ break;
+ }
+ chip->timer_enable[0] = (data & 0x01) != 0;
+ chip->timer_enable[1] = (data & 0x02) != 0;
+ chip->timer_mask[1] = (data & 0x20) != 0;
+ chip->timer_mask[0] = (data & 0x40) != 0;
+ break;
+ case CONFIG_REG:
+ chip->keyscale_mode = (data & 0x40) != 0;
+ break;
+ case BASSDRUM_REG:
+ chip->emu_rhy_mode_flags = data & 0x3f;
+ chip->emu_vibrato_deep = (data & 0x40) != 0;
+ chip->emu_tremolo_deep = (data & 0x80) != 0;
+ break;
+ case FOUROP_CONN_REG:
+ for (i = 0; i < 3; i++)
+ {
+ chip->channels[i].emu_mode_4op_enable = (data >> i) & 0x01;
+ chip->channels[i + 9].emu_mode_4op_enable = (data >> (i + 3)) & 0x01;
+ }
+ break;
+ case TEST_REG:
+ chip->test_bit_w0_r5_eg_halt = (data & 0x01) | ((data & 0x20) != 0);
+ chip->test_bit_1_distort = (data & 0x02) != 0;
+ chip->test_bit_2 = (data & 0x04) != 0;
+ chip->test_bit_3 = (data & 0x08) != 0;
+ chip->test_bit_4_attenuate = (data & 0x10) != 0;
+ chip->test_bit_w5_r0 = (data & 0x20) != 0;
+ chip->test_bit_6_phase_stop_reset = (data & 0x40) != 0;
+ chip->test_bit_7 = (data & 0x80) != 0;
+ break;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+static uint8_t
+ESFM_readback_reg_native (esfm_chip *chip, uint16_t address)
+{
+ int i;
+ uint8_t data = 0;
+ address = address & 0x7ff;
+
+ if (address < KEY_ON_REGS_START)
+ {
+ // Slot register read
+ size_t channel_idx = address >> 5;
+ size_t slot_idx = (address >> 3) & 0x03;
+ size_t register_idx = address & 0x07;
+ esfm_slot *slot = &chip->channels[channel_idx].slots[slot_idx];
+
+ data = ESFM_slot_readback(slot, register_idx);
+ }
+ else if (address < KEY_ON_REGS_START + 16)
+ {
+ // Key-on registers
+ size_t channel_idx = (address - KEY_ON_REGS_START);
+ esfm_channel *channel = &chip->channels[channel_idx];
+
+ data |= channel->key_on != 0;
+ data |= (channel->emu_mode_4op_enable != 0) << 1;
+ }
+ else if (address < KEY_ON_REGS_START + 20)
+ {
+ // Key-on channels 17 and 18 (each half)
+ size_t channel_idx = 16 + ((address & 0x02) >> 1);
+ bool second_half = address & 0x01;
+ esfm_channel *channel = &chip->channels[channel_idx];
+ if (second_half)
+ {
+ data |= channel->key_on_2 != 0;
+ data |= (channel->emu_mode_4op_enable_2 != 0) << 1;
+ }
+ else
+ {
+ data |= channel->key_on != 0;
+ data |= (channel->emu_mode_4op_enable != 0) << 1;
+ }
+ }
+ else
+ {
+ switch (address & 0x5ff)
+ {
+ case TIMER1_REG:
+ data = chip->timer_counter[0];
+ break;
+ case TIMER2_REG:
+ data = chip->timer_counter[1];
+ break;
+ case TIMER_SETUP_REG:
+ data |= chip->timer_enable[0] != 0;
+ data |= (chip->timer_enable[1] != 0) << 1;
+ data |= (chip->timer_mask[1] != 0) << 5;
+ data |= (chip->timer_mask[0] != 0) << 6;
+ break;
+ case CONFIG_REG:
+ data |= (chip->keyscale_mode != 0) << 6;
+ break;
+ case BASSDRUM_REG:
+ data |= chip->emu_rhy_mode_flags;
+ data |= chip->emu_vibrato_deep << 6;
+ data |= chip->emu_tremolo_deep << 7;
+ break;
+ case TEST_REG:
+ data |= chip->test_bit_w5_r0 != 0;
+ data |= (chip->test_bit_1_distort != 0) << 1;
+ data |= (chip->test_bit_2 != 0) << 2;
+ data |= (chip->test_bit_3 != 0) << 3;
+ data |= (chip->test_bit_4_attenuate != 0) << 4;
+ data |= (chip->test_bit_w0_r5_eg_halt != 0) << 5;
+ data |= (chip->test_bit_6_phase_stop_reset != 0) << 6;
+ data |= (chip->test_bit_7 != 0) << 7;
+ break;
+ case FOUROP_CONN_REG:
+ for (i = 0; i < 3; i++)
+ {
+ data |= (chip->channels[i].emu_mode_4op_enable != 0) << i;
+ data |= (chip->channels[i + 9].emu_mode_4op_enable != 0) << (i + 3);
+ }
+ break;
+ case NATIVE_MODE_REG:
+ data |= (chip->emu_newmode != 0);
+ data |= (chip->native_mode != 0) << 7;
+ break;
+ }
+ }
+ return data;
+}
+
+/* ------------------------------------------------------------------------- */
+static void
+ESFM_write_reg_emu (esfm_chip *chip, uint16_t address, uint8_t data)
+{
+ bool high = (address & 0x100) != 0;
+ uint8_t reg = address & 0xff;
+ int emu_slot_idx = ad_slot[address & 0x1f];
+ int natv_chan_idx = -1;
+ int natv_slot_idx = -1;
+ int emu_chan_idx = (reg & 0x0f) > 8 ? -1 : ((reg & 0x0f) + high * 9);
+
+ if (emu_slot_idx >= 0)
+ {
+ if (high)
+ {
+ emu_slot_idx += 18;
+ }
+
+ natv_chan_idx = emu_slot_map[emu_slot_idx].channel_idx;
+ natv_slot_idx = emu_slot_map[emu_slot_idx].slot_idx;
+ }
+
+ if (reg == 0xbd)
+ {
+ chip->emu_rhy_mode_flags = data & 0x3f;
+ chip->emu_vibrato_deep = (data & 0x40) != 0;
+ chip->emu_tremolo_deep = (data & 0x80) != 0;
+ if (chip->emu_rhy_mode_flags & 0x20)
+ {
+ // TODO: check if writes to 0xbd actually affect the readable key-on flags at
+ // 0x246, 0x247, 0x248; and if there's any visible effect from the SD and TC flags
+ chip->channels[6].key_on = (data & 0x10) != 0;
+ chip->channels[7].key_on = (data & 0x01) != 0;
+ chip->channels[8].key_on = (data & 0x04) != 0;
+ chip->channels[7].key_on_2 = (data & 0x08) != 0;
+ chip->channels[8].key_on_2 = (data & 0x02) != 0;
+ }
+ ESFM_emu_rearrange_connections(&chip->channels[7]);
+ ESFM_emu_rearrange_connections(&chip->channels[8]);
+ return;
+ }
+
+ switch(reg & 0xf0)
+ {
+ case 0x00:
+ if (high)
+ {
+ int i;
+ switch(reg & 0x0f)
+ {
+ case 0x01:
+ chip->emu_wavesel_enable = (data & 0x20) != 0;
+ break;
+ case 0x02:
+ chip->timer_reload[0] = data;
+ chip->timer_counter[0] = data;
+ break;
+ case 0x03:
+ chip->timer_reload[1] = data;
+ chip->timer_counter[1] = data;
+ break;
+ case 0x04:
+ for (i = 0; i < 3; i++)
+ {
+ chip->channels[i].emu_mode_4op_enable = (data >> i) & 0x01;
+ chip->channels[i + 9].emu_mode_4op_enable = (data >> (i + 3)) & 0x01;
+ }
+ for (i = 0; i < 6; i++)
+ {
+ ESFM_emu_rearrange_connections(&chip->channels[i]);
+ ESFM_emu_rearrange_connections(&chip->channels[i + 9]);
+ }
+ break;
+ case 0x05:
+ chip->emu_newmode = data & 0x01;
+ if ((data & 0x80) != 0)
+ {
+ chip->native_mode = 1;
+ ESFM_emu_to_native_switch(chip);
+ }
+ break;
+ case 0x08:
+ chip->keyscale_mode = (data & 0x40) != 0;
+ break;
+ }
+ }
+ else
+ {
+ switch(reg & 0x0f)
+ {
+ case 0x01:
+ chip->emu_wavesel_enable = (data & 0x20) != 0;
+ break;
+ case 0x02:
+ chip->timer_reload[0] = data;
+ chip->timer_counter[0] = data;
+ break;
+ case 0x03:
+ chip->timer_reload[1] = data;
+ chip->timer_counter[1] = data;
+ break;
+ case 0x04:
+ if (data & 0x80)
+ {
+ chip->irq_bit = 0;
+ chip->timer_overflow[0] = 0;
+ chip->timer_overflow[1] = 0;
+ break;
+ }
+ chip->timer_enable[0] = data & 0x01;
+ chip->timer_enable[1] = (data & 0x02) != 0;
+ chip->timer_mask[1] = (data & 0x20) != 0;
+ chip->timer_mask[0] = (data & 0x40) != 0;
+ break;
+ case 0x08:
+ chip->keyscale_mode = (data & 0x40) != 0;
+ break;
+ }
+ }
+ break;
+ case 0x20: case 0x30:
+ if (emu_slot_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x0, data);
+ }
+ break;
+ case 0x40: case 0x50:
+ if (emu_slot_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x1, data);
+ ESFM_emu_channel_update_keyscale(&chip->channels[natv_chan_idx]);
+ }
+ break;
+ case 0x60: case 0x70:
+ if (emu_slot_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x2, data);
+ }
+ break;
+ case 0x80: case 0x90:
+ if (emu_slot_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x3, data);
+ }
+ break;
+ case 0xa0:
+ if (emu_chan_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[emu_chan_idx].slots[0], 0x4, data);
+ ESFM_emu_channel_update_keyscale(&chip->channels[emu_chan_idx]);
+ }
+ break;
+ case 0xb0:
+ if (emu_chan_idx >= 0)
+ {
+ esfm_channel *channel = &chip->channels[emu_chan_idx];
+ // TODO: check if emulation mode actually writes to the native mode key on registers
+ // it might only use slot 0's emu key on field...
+ channel->key_on = (data & 0x20) != 0;
+ if (channel->channel_idx == 7 || channel->channel_idx == 8)
+ {
+ channel->key_on_2 = (data & 0x20) != 0;
+ }
+ ESFM_slot_write(&channel->slots[0], 0x5, data);
+ ESFM_emu_channel_update_keyscale(&chip->channels[emu_chan_idx]);
+ }
+ break;
+ case 0xc0:
+ if (emu_chan_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[emu_chan_idx].slots[0], 0x6, data);
+ ESFM_emu_rearrange_connections(&chip->channels[emu_chan_idx]);
+ }
+ break;
+ case 0xe0: case 0xf0:
+ if (emu_slot_idx >= 0)
+ {
+ ESFM_slot_write(&chip->channels[natv_chan_idx].slots[natv_slot_idx], 0x7, data);
+ }
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_write_reg (esfm_chip *chip, uint16_t address, uint8_t data)
+{
+ if (chip->native_mode)
+ {
+ ESFM_write_reg_native(chip, address, data);
+ return;
+ }
+ else
+ {
+ ESFM_write_reg_emu(chip, address, data);
+ return;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_write_reg_buffered (esfm_chip *chip, uint16_t address, uint8_t data)
+{
+ uint64_t timestamp;
+ esfm_write_buf *new_entry, *last_entry;
+
+ new_entry = &chip->write_buf[chip->write_buf_end];
+ last_entry = &chip->write_buf[(chip->write_buf_end - 1) % ESFM_WRITEBUF_SIZE];
+
+ if (new_entry->valid) {
+ ESFM_write_reg(chip, new_entry->address, new_entry->data);
+ chip->write_buf_start = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE;
+ }
+
+ new_entry->valid = 1;
+ new_entry->address = address;
+ new_entry->data = data;
+ timestamp = last_entry->timestamp + ESFM_WRITEBUF_DELAY;
+ if (timestamp < chip->write_buf_timestamp)
+ {
+ timestamp = chip->write_buf_timestamp;
+ }
+
+ new_entry->timestamp = timestamp;
+ chip->write_buf_end = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_write_reg_buffered_fast (esfm_chip *chip, uint16_t address, uint8_t data)
+{
+ esfm_write_buf *new_entry;
+
+ new_entry = &chip->write_buf[chip->write_buf_end];
+
+ if (new_entry->valid) {
+ ESFM_write_reg(chip, new_entry->address, new_entry->data);
+ chip->write_buf_start = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE;
+ }
+
+ new_entry->valid = 1;
+ new_entry->address = address;
+ new_entry->data = data;
+ new_entry->timestamp = chip->write_buf_timestamp;
+ chip->write_buf_end = (chip->write_buf_end + 1) % ESFM_WRITEBUF_SIZE;
+}
+
+/* ------------------------------------------------------------------------- */
+uint8_t
+ESFM_readback_reg (esfm_chip *chip, uint16_t address)
+{
+ if (chip->native_mode)
+ {
+ return ESFM_readback_reg_native(chip, address);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_write_port (esfm_chip *chip, uint8_t offset, uint8_t data)
+{
+ if (chip->native_mode)
+ {
+ switch(offset)
+ {
+ case 0:
+ chip->native_mode = 0;
+ ESFM_native_to_emu_switch(chip);
+ // TODO: verify if the address write goes through
+ chip->addr_latch = data;
+ break;
+ case 1:
+ ESFM_write_reg_native(chip, chip->addr_latch, data);
+ break;
+ case 2:
+ chip->addr_latch = (chip->addr_latch & 0xff00) | data;
+ break;
+ case 3:
+ chip->addr_latch = chip->addr_latch & 0xff;
+ chip->addr_latch |= (uint16)data << 8;
+ break;
+ }
+ }
+ else
+ {
+ switch(offset)
+ {
+ case 0:
+ chip->addr_latch = data;
+ break;
+ case 1: case 3:
+ ESFM_write_reg_emu(chip, chip->addr_latch, data);
+ break;
+ case 2:
+ chip->addr_latch = (uint16)data | 0x100;
+ break;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+uint8_t
+ESFM_read_port (esfm_chip *chip, uint8_t offset)
+{
+ uint8_t data = 0;
+
+ switch(offset)
+ {
+ case 0:
+ data |= (chip->irq_bit != 0) << 7;
+ data |= (chip->timer_overflow[0] != 0) << 6;
+ data |= (chip->timer_overflow[1] != 0) << 5;
+ break;
+ case 1:
+ if (chip->native_mode)
+ {
+ data = ESFM_readback_reg_native(chip, chip->addr_latch);
+ }
+ else
+ {
+ data = 0;
+ }
+ break;
+ case 2: case 3:
+ // This matches OPL3 behavior.
+ data = 0xff;
+ break;
+ }
+
+ return data;
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_set_mode (esfm_chip *chip, bool native_mode)
+{
+ native_mode = native_mode != 0;
+
+ if (native_mode != (chip->native_mode != 0))
+ {
+ chip->native_mode = native_mode;
+ if (native_mode)
+ {
+ ESFM_emu_to_native_switch(chip);
+ }
+ else
+ {
+ ESFM_native_to_emu_switch(chip);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+void
+ESFM_init (esfm_chip *chip)
+{
+ esfm_slot *slot;
+ esfm_channel *channel;
+ size_t channel_idx, slot_idx;
+
+ memset(chip, 0, sizeof(esfm_chip));
+ for (channel_idx = 0; channel_idx < 18; channel_idx++)
+ {
+ for (slot_idx = 0; slot_idx < 4; slot_idx++)
+ {
+ channel = &chip->channels[channel_idx];
+ slot = &channel->slots[slot_idx];
+
+ channel->chip = chip;
+ channel->channel_idx = channel_idx;
+ slot->channel = channel;
+ slot->chip = chip;
+ slot->slot_idx = slot_idx;
+ slot->in.eg_position = slot->in.eg_output = 0x1ff;
+ slot->in.eg_state = EG_RELEASE;
+ slot->in.emu_mod_enable = ~((int13) 0);
+ if (slot_idx == 0)
+ {
+ slot->in.mod_input = &slot->in.feedback_buf;
+ }
+ else
+ {
+ esfm_slot *prev_slot = &channel->slots[slot_idx - 1];
+ slot->in.mod_input = &prev_slot->in.output;
+ }
+
+ if (slot_idx == 1)
+ {
+ slot->in.emu_output_enable = ~((int13) 0);
+ }
+
+ if (channel_idx > 15 && slot_idx & 0x02)
+ {
+ slot->in.key_on = &channel->key_on_2;
+ }
+ else
+ {
+ slot->in.key_on = &channel->key_on;
+ }
+
+ slot->out_enable[0] = slot->out_enable[1] = ~((int13) 0);
+ }
+ }
+
+ chip->lfsr = 1;
+}
+
diff --git a/src/sound/snd_opl.c b/src/sound/snd_opl.c
index 21cc66f04..d98b3ccc2 100644
--- a/src/sound/snd_opl.c
+++ b/src/sound/snd_opl.c
@@ -68,6 +68,11 @@ fm_driver_get(int chip_id, fm_drv_t *drv)
*drv = ymfm_drv;
drv->priv = device_add_inst(&ymf278b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++);
break;
+
+ case FM_ESFM:
+ *drv = esfmu_opl_drv;
+ drv->priv = device_add_inst(&esfm_esfmu_device, fm_dev_inst[fm_driver][chip_id]++);
+ break;
default:
return 0;
diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c
new file mode 100644
index 000000000..78cbec11d
--- /dev/null
+++ b/src/sound/snd_opl_esfm.c
@@ -0,0 +1,355 @@
+/*
+ * 86Box A hypervisor and IBM PC system emulator that specializes in
+ * running old operating systems and software designed for IBM
+ * PC systems and compatibles from 1981 through fairly recent
+ * system designs based on the PCI bus.
+ *
+ * This file is part of the 86Box distribution.
+ *
+ * ESFMu ESFM emulator.
+ *
+ *
+ * Authors: Fred N. van Kempen,
+ * Miran Grca,
+ * Alexey Khokholov (Nuke.YKT)
+ * Cacodemon345
+ *
+ * Copyright 2017-2020 Fred N. van Kempen.
+ * Copyright 2016-2020 Miran Grca.
+ * Copyright 2013-2018 Alexey Khokholov (Nuke.YKT)
+ * Copyright 2024 Cacodemon345
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "esfmu/esfm.h"
+
+#define HAVE_STDARG_H
+#include <86box/86box.h>
+#include <86box/sound.h>
+#include <86box/device.h>
+#include <86box/timer.h>
+#include <86box/snd_opl.h>
+
+#define RSM_FRAC 10
+
+typedef struct {
+ esfm_chip opl;
+ int8_t flags;
+ int8_t pad;
+
+ uint8_t status;
+ uint8_t timer_ctrl;
+ uint16_t timer_count[2];
+ uint16_t timer_cur_count[2];
+
+ pc_timer_t timers[2];
+
+ int16_t samples[2];
+
+ int pos;
+ int32_t buffer[MUSICBUFLEN * 2];
+} esfm_drv_t;
+
+enum {
+ FLAG_CYCLES = 0x02,
+ FLAG_OPL3 = 0x01
+};
+
+enum {
+ STAT_TMR_OVER = 0x60,
+ STAT_TMR1_OVER = 0x40,
+ STAT_TMR2_OVER = 0x20,
+ STAT_TMR_ANY = 0x80
+};
+
+enum {
+ CTRL_RESET = 0x80,
+ CTRL_TMR_MASK = 0x60,
+ CTRL_TMR1_MASK = 0x40,
+ CTRL_TMR2_MASK = 0x20,
+ CTRL_TMR2_START = 0x02,
+ CTRL_TMR1_START = 0x01
+};
+
+#ifdef ENABLE_OPL_LOG
+int esfm_do_log = ENABLE_OPL_LOG;
+
+static void
+esfm_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (esfm_do_log) {
+ va_start(ap, fmt);
+ pclog_ex(fmt, ap);
+ va_end(ap);
+ }
+}
+#else
+# define esfm_log(fmt, ...)
+#endif
+
+void
+esfm_generate_raw(esfm_drv_t *dev, int32_t *bufp)
+{
+ ESFM_generate(&dev->opl, bufp);
+}
+
+void
+esfm_drv_generate_stream(esfm_drv_t *dev, int32_t *sndptr, uint32_t num)
+{
+ for (uint32_t i = 0; i < num; i++) {
+ esfm_generate_raw(dev, sndptr);
+ sndptr += 2;
+ }
+}
+
+static void
+esfm_timer_tick(esfm_drv_t *dev, int tmr)
+{
+ dev->timer_cur_count[tmr] = (dev->timer_cur_count[tmr] + 1) & 0xff;
+
+ esfm_log("Ticking timer %i, count now %02X...\n", tmr, dev->timer_cur_count[tmr]);
+
+ if (dev->timer_cur_count[tmr] == 0x00) {
+ dev->status |= ((STAT_TMR1_OVER >> tmr) & ~dev->timer_ctrl);
+ dev->timer_cur_count[tmr] = dev->timer_count[tmr];
+
+ esfm_log("Count wrapped around to zero, reloading timer %i (%02X), status = %02X...\n", tmr, (STAT_TMR1_OVER >> tmr), dev->status);
+ }
+
+ timer_on_auto(&dev->timers[tmr], (tmr == 1) ? 320.0 : 80.0);
+}
+
+static void
+esfm_timer_control(esfm_drv_t *dev, int tmr, int start)
+{
+ timer_on_auto(&dev->timers[tmr], 0.0);
+
+ if (start) {
+ esfm_log("Loading timer %i count: %02X = %02X\n", tmr, dev->timer_cur_count[tmr], dev->timer_count[tmr]);
+ dev->timer_cur_count[tmr] = dev->timer_count[tmr];
+ if (dev->flags & FLAG_OPL3)
+ esfm_timer_tick(dev, tmr); /* Per the YMF 262 datasheet, OPL3 starts counting immediately, unlike OPL2. */
+ else
+ timer_on_auto(&dev->timers[tmr], (tmr == 1) ? 320.0 : 80.0);
+ } else {
+ esfm_log("Timer %i stopped\n", tmr);
+ if (tmr == 1) {
+ dev->status &= ~STAT_TMR2_OVER;
+ } else
+ dev->status &= ~STAT_TMR1_OVER;
+ }
+}
+
+static void
+esfm_timer_1(void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ esfm_timer_tick(dev, 0);
+}
+
+static void
+esfm_timer_2(void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ esfm_timer_tick(dev, 1);
+}
+
+static void
+esfm_drv_set_do_cycles(void *priv, int8_t do_cycles)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ if (do_cycles)
+ dev->flags |= FLAG_CYCLES;
+ else
+ dev->flags &= ~FLAG_CYCLES;
+}
+
+static void *
+esfm_drv_init(const device_t *info)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) calloc(1, sizeof(esfm_drv_t));
+ dev->flags = FLAG_CYCLES | FLAG_OPL3;
+
+ /* Initialize the ESFMu object. */
+ ESFM_init(&dev->opl);
+
+ timer_add(&dev->timers[0], esfm_timer_1, dev, 0);
+ timer_add(&dev->timers[1], esfm_timer_2, dev, 0);
+
+ return dev;
+}
+
+static void
+esfm_drv_close(void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+ free(dev);
+}
+
+static int32_t *
+esfm_drv_update(void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ if (dev->pos >= music_pos_global)
+ return dev->buffer;
+
+ esfm_drv_generate_stream(dev,
+ &dev->buffer[dev->pos * 2],
+ music_pos_global - dev->pos);
+
+ for (; dev->pos < music_pos_global; dev->pos++) {
+ dev->buffer[dev->pos * 2] /= 2;
+ dev->buffer[(dev->pos * 2) + 1] /= 2;
+ }
+
+ return dev->buffer;
+}
+
+static void
+esfm_drv_reset_buffer(void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ dev->pos = 0;
+}
+
+static uint8_t
+esfm_drv_read(uint16_t port, void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ if (dev->flags & FLAG_CYCLES)
+ cycles -= ((int) (isa_timing * 8));
+
+ esfm_drv_update(dev);
+
+ uint8_t ret = 0xff;
+
+ switch (port & 0x0003) {
+ case 0x0000:
+ ret = dev->status;
+ if (dev->status & STAT_TMR_OVER)
+ ret |= STAT_TMR_ANY;
+ break;
+ case 0x0001:
+ ret = ESFM_read_port(&dev->opl, port & 3);
+ switch (dev->opl.addr_latch & 0x5ff) {
+ case 0x402:
+ ret = dev->timer_count[0];
+ break;
+ case 0x403:
+ ret = dev->timer_count[1];
+ break;
+ case 0x404:
+ ret = dev->timer_ctrl;
+ break;
+ }
+ break;
+ case 0x0002:
+ case 0x0003:
+ ret = 0xff;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val)
+{
+ uint16_t p = dev->opl.addr_latch & 0x07ff;
+
+ if (dev->opl.native_mode) {
+ p -= 0x400;
+ }
+ p &= 0x1ff;
+
+ switch (p) {
+ case 0x002: /* Timer 1 */
+ dev->timer_count[0] = val;
+ esfm_log("Timer 0 count now: %i\n", dev->timer_count[0]);
+ break;
+
+ case 0x003: /* Timer 2 */
+ dev->timer_count[1] = val;
+ esfm_log("Timer 1 count now: %i\n", dev->timer_count[1]);
+ break;
+
+ case 0x004: /* Timer control */
+ if (val & CTRL_RESET) {
+ esfm_log("Resetting timer status...\n");
+ dev->status &= ~STAT_TMR_OVER;
+ } else {
+ dev->timer_ctrl = val;
+ esfm_timer_control(dev, 0, val & CTRL_TMR1_START);
+ esfm_timer_control(dev, 1, val & CTRL_TMR2_START);
+ esfm_log("Status mask now %02X (val = %02X)\n", (val & ~CTRL_TMR_MASK) & CTRL_TMR_MASK, val);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val);
+}
+
+static void
+esfm_drv_write(uint16_t port, uint8_t val, void *priv)
+{
+ esfm_drv_t *dev = (esfm_drv_t *) priv;
+
+ if (dev->flags & FLAG_CYCLES)
+ cycles -= ((int) (isa_timing * 8));
+
+ esfm_drv_update(dev);
+
+ if (dev->opl.native_mode) {
+ if ((port & 0x0003) == 0x0001)
+ esfm_drv_write_buffered(dev, val);
+ else {
+ ESFM_write_port(&dev->opl, port & 3, val);
+ }
+ } else {
+ if ((port & 0x0001) == 0x0001)
+ esfm_drv_write_buffered(dev, val);
+ else {
+ ESFM_write_port(&dev->opl, port & 3, val);
+ }
+ }
+}
+
+const device_t esfm_esfmu_device = {
+ .name = "ESS Technology ESFM (ESFMu)",
+ .internal_name = "esfm_esfmu",
+ .flags = 0,
+ .local = FM_ESFM,
+ .init = esfm_drv_init,
+ .close = esfm_drv_close,
+ .reset = NULL,
+ { .available = NULL },
+ .speed_changed = NULL,
+ .force_redraw = NULL,
+ .config = NULL
+};
+
+const fm_drv_t esfmu_opl_drv = {
+ &esfm_drv_read,
+ &esfm_drv_write,
+ &esfm_drv_update,
+ &esfm_drv_reset_buffer,
+ &esfm_drv_set_do_cycles,
+ NULL,
+ NULL,
+};
diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c
index 2365419b1..13baa3621 100644
--- a/src/sound/snd_sb.c
+++ b/src/sound/snd_sb.c
@@ -75,6 +75,25 @@ static const double sb_att_4dbstep_3bits[] = {
static const double sb_att_7dbstep_2bits[] = {
164.0, 6537.0, 14637.0, 32767.0
};
+
+/* Attenuation table for ESS 4-bit microphone volume.
+ * The last step is a jump to -48 dB. */
+static const double sb_att_1p4dbstep_4bits[] = {
+ 164.0, 3431.0, 4031.0, 4736.0, 5565.0, 6537.0, 7681.0, 9025.0,
+ 10603.0, 12458.0, 14637.0, 17196.0, 20204.0, 23738.0, 27889.0, 32767.0
+};
+
+/* Attenuation table for ESS 4-bit mixer volume.
+ * The last step is a jump to -48 dB. */
+static const double sb_att_2dbstep_4bits[] = {
+ 164.0, 1304.0, 1641.0, 2067.0, 2602.0, 3276.0, 4125.0, 5192.0,
+ 6537.0, 8230.0, 10362.0, 13044.0, 16422.0, 20674.0, 26027.0, 32767.0
+};
+
+/* Attenuation table for ESS 3-bit PC speaker volume. */
+static const double sb_att_3dbstep_3bits[] = {
+ 0.0, 4125.0, 5826.0, 8230.0, 11626.0, 16422.0, 23197.0, 32767.0
+};
// clang-format on
static const uint16_t sb_mcv_addr[8] = { 0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270 };
@@ -667,6 +686,85 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv)
*buffer = c * output_gain;
}
+void
+sb_get_buffer_ess(int32_t *buffer, int len, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ const ess_mixer_t *mixer = &ess->mixer_ess;
+ double out_l = 0.0;
+ double out_r = 0.0;
+
+ sb_dsp_update(&ess->dsp);
+
+ for (int c = 0; c < len * 2; c += 2) {
+ out_l = 0.0;
+ out_r = 0.0;
+
+ /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */
+ if (mixer->output_filter) {
+ out_l += (low_fir_sb16(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.0;
+ out_r += (low_fir_sb16(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
+ } else {
+ out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0;
+ out_r += (ess->dsp.buffer[c + 1] * mixer->voice_r) / 3.0;
+ }
+
+ /* TODO: recording from the mixer. */
+ out_l *= mixer->master_l;
+ out_r *= mixer->master_r;
+
+ buffer[c] += (int32_t) out_l;
+ buffer[c + 1] += (int32_t) out_r;
+ }
+
+ ess->dsp.pos = 0;
+}
+
+void
+sb_get_music_buffer_ess(int32_t *buffer, int len, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ const ess_mixer_t *mixer = &ess->mixer_ess;
+ double out_l = 0.0;
+ double out_r = 0.0;
+ const int32_t *opl_buf = NULL;
+
+ opl_buf = ess->opl.update(ess->opl.priv);
+
+ for (int c = 0; c < len * 2; c += 2) {
+ out_l = 0.0;
+ out_r = 0.0;
+
+ out_l = (((double) opl_buf[c]) * mixer->fm_l) * 0.7171630859375;
+ out_r = (((double) opl_buf[c + 1]) * mixer->fm_r) * 0.7171630859375;
+ if (ess->opl_mix && ess->opl_mixer)
+ ess->opl_mix(ess->opl_mixer, &out_l, &out_r);
+
+ /* TODO: recording from the mixer. */
+ out_l *= mixer->master_l;
+ out_r *= mixer->master_r;
+
+ buffer[c] += (int32_t) out_l;
+ buffer[c + 1] += (int32_t) out_r;
+ }
+
+ ess->opl.reset_buffer(ess->opl.priv);
+}
+
+void
+ess_filter_cd_audio(int channel, double *buffer, void *priv)
+{
+ const sb_t *ess = (sb_t *) priv;
+ const ess_mixer_t *mixer = &ess->mixer_ess;
+ double c;
+ double cd = channel ? mixer->cd_r : mixer->cd_l;
+ double master = channel ? mixer->master_r : mixer->master_l;
+
+ /* TODO: recording from the mixer. */
+ c = (*buffer * cd) / 3.0;
+ *buffer = c * master;
+}
+
void
sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *priv)
{
@@ -1304,6 +1402,264 @@ sb_ct1745_mixer_reset(sb_t *sb)
sb_ct1745_mixer_write(5, 0, sb);
}
+void
+ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ ess_mixer_t *mixer = &ess->mixer_ess;
+
+ if (!(addr & 1)) {
+ mixer->index = val;
+ mixer->regs[0x01] = val;
+ if (val == 0x40) {
+ mixer->ess_id_str_pos = 0;
+ }
+ } else {
+ if (mixer->index == 0) {
+ /* Reset */
+ mixer->regs[0x0a] = mixer->regs[0x0c] = 0x00;
+ mixer->regs[0x0e] = 0x00;
+ /* Changed default from -11dB to 0dB */
+ mixer->regs[0x04] = mixer->regs[0x22] = 0xee;
+ mixer->regs[0x26] = mixer->regs[0x28] = 0xee;
+ mixer->regs[0x2e] = 0x00;
+
+ /* Initialize ESS regs
+ * Defaulting to 0dB instead of the standard -11dB. */
+ mixer->regs[0x14] = mixer->regs[0x32] = 0xff;
+ mixer->regs[0x36] = mixer->regs[0x38] = 0xff;
+ mixer->regs[0x3a] = 0x00;
+ mixer->regs[0x3c] = 0x05;
+ mixer->regs[0x3e] = 0x00;
+
+ sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2);
+ } else {
+ mixer->regs[mixer->index] = val;
+
+ switch (mixer->index) {
+ /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */
+ case 0x02:
+ case 0x06:
+ case 0x08:
+ mixer->regs[mixer->index + 0x20] = ((val & 0xe) << 4) | (val & 0xe);
+ break;
+
+ case 0x0A:
+ {
+ uint8_t mic_vol_2bit = (mixer->regs[0x0a] >> 1) & 0x3;
+ mixer->mic_l = mixer->mic_r = sb_att_7dbstep_2bits[mic_vol_2bit] / 32767.0;
+ mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2) | (mic_vol_2bit << 4) | (mic_vol_2bit << 6);
+ break;
+ }
+
+ case 0x0C:
+ switch (mixer->regs[0x0C] & 6) {
+ case 2:
+ mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
+ break;
+ case 6:
+ mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
+ break;
+ default:
+ mixer->input_selector = INPUT_MIC;
+ break;
+ }
+ mixer->input_filter = !(mixer->regs[0xC] & 0x20);
+ mixer->in_filter_freq = ((mixer->regs[0xC] & 0x8) == 0) ? 3200 : 8800;
+ break;
+
+ case 0x0E:
+ mixer->output_filter = !(mixer->regs[0xE] & 0x20);
+ mixer->stereo = mixer->regs[0xE] & 2;
+ sb_dsp_set_stereo(&ess->dsp, val & 2);
+ break;
+
+ case 0x14:
+ mixer->regs[0x4] = val & 0xee;
+ break;
+
+ case 0x1A:
+ mixer->mic_l = sb_att_1p4dbstep_4bits[(mixer->regs[0x1A] >> 4) & 0xF] / 32767.0;
+ mixer->mic_r = sb_att_1p4dbstep_4bits[mixer->regs[0x1A] & 0xF] / 32767.0;
+ break;
+
+ case 0x1C:
+ if ((mixer->regs[0x1C] & 0x07) == 0x07) {
+ mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R;
+ } else if ((mixer->regs[0x1C] & 0x07) == 0x06) {
+ mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
+ } else if ((mixer->regs[0x1C] & 0x06) == 0x02) {
+ mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
+ } else if ((mixer->regs[0x1C] & 0x02) == 0) {
+ mixer->input_selector = INPUT_MIC;
+ }
+ break;
+
+ case 0x22:
+ case 0x26:
+ case 0x28:
+ case 0x2E:
+ mixer->regs[mixer->index - 0x20] = (val & 0xe);
+ mixer->regs[mixer->index + 0x10] = val;
+ break;
+
+ /* More compatibility:
+ SoundBlaster Pro selects register 020h for 030h, 022h for 032h,
+ 026h for 036h, and 028h for 038h. */
+ case 0x30:
+ case 0x32:
+ case 0x36:
+ case 0x38:
+ case 0x3e:
+ mixer->regs[mixer->index - 0x10] = (val & 0xee);
+ break;
+
+ case 0x3a:
+ case 0x3c:
+ break;
+
+ case 0x00:
+ case 0x04:
+ break;
+
+ case 0x64:
+ mixer->regs[mixer->index] &= ~0x8;
+ break;
+
+ case 0x40:
+ {
+ uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30);
+ gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200);
+
+ if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
+ /* Not on ES1688. */
+ io_removehandler(0x0388, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ if ((mixer->regs[0x40] & 0x1) != 0) {
+ io_sethandler(0x0388, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ }
+ }
+ switch ((mixer->regs[0x40] >> 5) & 0x7) {
+ case 0:
+ mpu401_change_addr(ess->mpu, 0x00);
+ mpu401_setirq(ess->mpu, -1);
+ break;
+ case 1:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, -1);
+ break;
+ case 2:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, ess->dsp.sb_irqnum);
+ break;
+ case 3:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 11);
+ break;
+ case 4:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 9);
+ break;
+ case 5:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 5);
+ break;
+ case 6:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 7);
+ break;
+ case 7:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 10);
+ break;
+ }
+ break;
+ }
+
+ default:
+ sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
+ break;
+ }
+ }
+
+ mixer->voice_l = sb_att_2dbstep_4bits[(mixer->regs[0x14] >> 4) & 0x0F] / 32767.0;
+ mixer->voice_r = sb_att_2dbstep_4bits[mixer->regs[0x14] & 0x0F] / 32767.0;
+ mixer->master_l = sb_att_2dbstep_4bits[(mixer->regs[0x32] >> 4) & 0x0F] / 32767.0;
+ mixer->master_r = sb_att_2dbstep_4bits[mixer->regs[0x32] & 0x0F] / 32767.0;
+ mixer->fm_l = sb_att_2dbstep_4bits[(mixer->regs[0x36] >> 4) & 0x0F] / 32767.0;
+ mixer->fm_r = sb_att_2dbstep_4bits[mixer->regs[0x36] & 0x0F] / 32767.0;
+ mixer->cd_l = sb_att_2dbstep_4bits[(mixer->regs[0x38] >> 4) & 0x0F] / 32767.0;
+ mixer->cd_r = sb_att_2dbstep_4bits[mixer->regs[0x38] & 0x0F] / 32767.0;
+ mixer->auxb_l = sb_att_2dbstep_4bits[(mixer->regs[0x3a] >> 4) & 0x0F] / 32767.0;
+ mixer->auxb_r = sb_att_2dbstep_4bits[mixer->regs[0x3a] & 0x0F] / 32767.0;
+ mixer->line_l = sb_att_2dbstep_4bits[(mixer->regs[0x3e] >> 4) & 0x0F] / 32767.0;
+ mixer->line_r = sb_att_2dbstep_4bits[mixer->regs[0x3e] & 0x0F] / 32767.0;
+ mixer->speaker = sb_att_3dbstep_3bits[mixer->regs[0x3c] & 0x07] / 32767.0;
+
+ /* TODO: PC Speaker volume */
+ }
+}
+
+uint8_t
+ess_mixer_read(uint16_t addr, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ ess_mixer_t *mixer = &ess->mixer_ess;
+
+ if (!(addr & 1))
+ return mixer->index;
+
+ switch (mixer->index) {
+ case 0x00:
+ case 0x04:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ case 0x14:
+ case 0x22:
+ case 0x26:
+ case 0x28:
+ case 0x2e:
+ case 0x02:
+ case 0x06:
+ case 0x30:
+ case 0x32:
+ case 0x36:
+ case 0x38:
+ case 0x3e:
+ return mixer->regs[mixer->index];
+
+ case 0x40:
+ if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
+ uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos];
+ mixer->ess_id_str_pos++;
+ if (mixer->ess_id_str_pos >= 4)
+ mixer->ess_id_str_pos = 0;
+ return val;
+ } else {
+ return mixer->regs[mixer->index];
+ }
+
+ default:
+ sb_log("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
+ break;
+ }
+
+ return 0x0a;
+}
+
+void
+ess_mixer_reset(sb_t *ess)
+{
+ ess_mixer_write(4, 0, ess);
+ ess_mixer_write(5, 0, ess);
+}
+
uint8_t
sb_mcv_read(int port, void *priv)
{
@@ -2755,6 +3111,70 @@ sb_awe32_pnp_init(const device_t *info)
return sb;
}
+static void *
+ess_1688_init(UNUSED(const device_t *info))
+{
+ sb_t *ess = calloc(sizeof(sb_t), 1);
+ uint16_t addr = device_get_config_hex16("base");
+
+ fm_driver_get(FM_ESFM, &ess->opl);
+
+ sb_dsp_set_real_opl(&ess->dsp, 1);
+ sb_dsp_init(&ess->dsp, SBPRO2, SB_SUBTYPE_ESS_ES1688, ess);
+ sb_dsp_setaddr(&ess->dsp, addr);
+ sb_dsp_setirq(&ess->dsp, device_get_config_int("irq"));
+ sb_dsp_setdma8(&ess->dsp, device_get_config_int("dma"));
+ sb_dsp_setdma16_8(&ess->dsp, device_get_config_int("dma"));
+ sb_dsp_setdma16_supported(&ess->dsp, 0);
+ ess_mixer_reset(ess);
+
+ /* DSP I/O handler is activated in sb_dsp_setaddr */
+ {
+ io_sethandler(addr, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ io_sethandler(addr + 8, 0x0002,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ io_sethandler(0x0388, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ }
+
+ ess->mixer_enabled = 1;
+ io_sethandler(addr + 4, 0x0002,
+ ess_mixer_read, NULL, NULL,
+ ess_mixer_write, NULL, NULL,
+ ess);
+ sound_add_handler(sb_get_buffer_ess, ess);
+ music_add_handler(sb_get_music_buffer_ess, ess);
+ sound_set_cd_audio_filter(ess_filter_cd_audio, ess);
+
+ if (device_get_config_int("receive_input")) {
+ midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp);
+ }
+
+ ess->mixer_ess.ess_id_str[0] = 0x16;
+ ess->mixer_ess.ess_id_str[1] = 0x88;
+ ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff;
+ ess->mixer_ess.ess_id_str[3] = addr & 0xff;
+
+ ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t));
+ /* NOTE: The MPU is initialized disabled and with no IRQ assigned.
+ * It will be later initialized by the guest OS's drivers. */
+ mpu401_init(ess->mpu, 0, -1, M_UART, 1);
+ sb_dsp_set_mpu(&ess->dsp, ess->mpu);
+
+ ess->gameport = gameport_add(&gameport_pnp_device);
+ ess->gameport_addr = 0x200;
+ gameport_remap(ess->gameport, ess->gameport_addr);
+
+ return ess;
+}
+
void
sb_close(void *priv)
{
@@ -4023,6 +4443,104 @@ static const device_config_t sb_awe64_gold_config[] = {
},
{ .name = "", .description = "", .type = CONFIG_END }
};
+
+static const device_config_t ess_1688_config[] = {
+ {
+ .name = "base",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = "",
+ .default_int = 0x220,
+ .file_filter = "",
+ .spinner = { 0 },
+ .selection = {
+ {
+ .description = "0x220",
+ .value = 0x220
+ },
+ {
+ .description = "0x230",
+ .value = 0x230
+ },
+ {
+ .description = "0x240",
+ .value = 0x240
+ },
+ {
+ .description = "0x250",
+ .value = 0x250
+ },
+ { .description = "" }
+ }
+ },
+ {
+ .name = "irq",
+ .description = "IRQ",
+ .type = CONFIG_SELECTION,
+ .default_string = "",
+ .default_int = 5,
+ .file_filter = "",
+ .spinner = { 0 },
+ .selection = {
+ {
+ .description = "IRQ 2",
+ .value = 2
+ },
+ {
+ .description = "IRQ 5",
+ .value = 5
+ },
+ {
+ .description = "IRQ 7",
+ .value = 7
+ },
+ {
+ .description = "IRQ 10",
+ .value = 10
+ },
+ { .description = "" }
+ }
+ },
+ {
+ .name = "dma",
+ .description = "DMA",
+ .type = CONFIG_SELECTION,
+ .default_string = "",
+ .default_int = 1,
+ .file_filter = "",
+ .spinner = { 0 },
+ .selection = {
+ {
+ .description = "DMA 0",
+ .value = 0
+ },
+ {
+ .description = "DMA 1",
+ .value = 1
+ },
+ {
+ .description = "DMA 3",
+ .value = 3
+ },
+ { .description = "" }
+ }
+ },
+ {
+ .name = "opl",
+ .description = "Enable OPL",
+ .type = CONFIG_BINARY,
+ .default_string = "",
+ .default_int = 1
+ },
+ {
+ .name = "receive_input",
+ .description = "Receive input (SB MIDI)",
+ .type = CONFIG_BINARY,
+ .default_string = "",
+ .default_int = 1
+ },
+ { .name = "", .description = "", .type = CONFIG_END }
+};
// clang-format on
const device_t sb_1_device = {
@@ -4360,3 +4878,17 @@ const device_t sb_awe64_gold_device = {
.force_redraw = NULL,
.config = sb_awe64_gold_config
};
+
+const device_t ess_1688_device = {
+ .name = "ESS AudioDrive ES1688",
+ .internal_name = "ess_es1688",
+ .flags = DEVICE_ISA,
+ .local = 0,
+ .init = ess_1688_init,
+ .close = sb_close,
+ .reset = NULL,
+ { .available = NULL },
+ .speed_changed = sb_speed_changed,
+ .force_redraw = NULL,
+ .config = ess_1688_config
+};
diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c
index cf4498b4b..332299c4f 100644
--- a/src/sound/snd_sb_dsp.c
+++ b/src/sound/snd_sb_dsp.c
@@ -6,6 +6,7 @@
#define _USE_MATH_DEFINES
#include
+#include
#include
#include
#include
@@ -28,9 +29,15 @@
#include <86box/plat_fallthrough.h>
#include <86box/plat_unused.h>
+/* NON-PCM SAMPLE FORMATS */
#define ADPCM_4 1
#define ADPCM_26 2
#define ADPCM_2 3
+#define ESPCM_4 4
+#define ESPCM_3 5
+/* ESPCM_2? */
+#define ESPCM_1 7
+#define ESPCM_4E 8 // for differentiating between 4-bit encoding and decoding modes
/*The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb*/
#define SB_DSP_REC_SAFEFTY_MARGIN 4096
@@ -115,6 +122,124 @@ uint8_t adjustMap2[24] = {
252, 0, 252, 0
};
+// clang-format off
+/* Upper half only used for ESPCM_3 mode. */
+/* TODO: Extract actual table (or exact ranges + range interpolation algo, whatever it is) from chip, someday, somehow.
+ * This current table is part software reverse engineering, part guesswork/extrapolation.
+ * It's close enough to what's in the chip to produce acceptable results, but not exact.
+ **/
+int8_t espcm_range_map[512] = {
+ -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7,
+ -10, -8, -7, -5, -4, -3, -2, -1, 0, 2, 3, 4, 5, 6, 8, 9,
+ -12, -11, -9, -8, -6, -5, -3, -2, 0, 2, 3, 5, 6, 8, 10, 11,
+ -14, -12, -11, -9, -7, -5, -4, -2, 0, 2, 4, 5, 7, 9, 11, 13,
+ -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14,
+ -21, -18, -16, -13, -11, -8, -6, -3, 0, 2, 5, 7, 10, 12, 15, 18,
+ -27, -24, -21, -17, -14, -11, -8, -4, 0, 3, 7, 10, 13, 17, 20, 24,
+ -35, -28, -24, -20, -16, -12, -8, -4, 0, 4, 8, 12, 16, 20, 24, 28,
+ -40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35,
+ -48, -42, -36, -30, -24, -18, -12, -6, 0, 6, 12, 18, 24, 30, 36, 43,
+ -56, -49, -42, -35, -28, -21, -14, -7, 0, 7, 14, 21, 28, 35, 42, 49,
+ -72, -63, -54, -45, -36, -27, -18, -9, 0, 9, 18, 27, 36, 45, 54, 63,
+ -85, -74, -64, -53, -43, -32, -22, -11, 0, 11, 22, 33, 43, 54, 64, 75,
+ -102, -98, -85, -71, -58, -45, -31, -14, 0, 13, 26, 39, 52, 65, 78, 90,
+ -127,-112, -96, -80, -64, -48, -32, -16, 0, 16, 32, 48, 64, 80, 96, 112,
+ -128,-127,-109, -91, -73, -54, -36, -18, 0, 18, 36, 54, 73, 91, 109, 127,
+ -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7,
+ -10, -9, -8, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 6, 7, 8,
+ -13, -11, -9, -7, -6, -5, -3, -2, -1, 2, 3, 5, 6, 7, 9, 10,
+ -15, -13, -12, -10, -8, -6, -5, -3, -1, 2, 3, 5, 6, 8, 10, 12,
+ -18, -15, -13, -11, -9, -7, -5, -3, -1, 2, 3, 5, 7, 9, 11, 13,
+ -24, -20, -17, -15, -12, -10, -7, -5, -2, 2, 3, 6, 8, 11, 13, 16,
+ -29, -26, -23, -19, -16, -13, -10, -6, -2, 2, 5, 8, 11, 15, 18, 22,
+ -34, -30, -26, -22, -18, -14, -10, -6, -2, 2, 6, 10, 14, 18, 22, 26,
+ -43, -38, -33, -28, -23, -18, -13, -8, -3, 2, 7, 12, 17, 22, 27, 32,
+ -51, -45, -39, -33, -27, -21, -15, -9, -3, 3, 9, 15, 21, 27, 33, 39,
+ -60, -53, -46, -39, -32, -25, -18, -11, -4, 3, 10, 17, 24, 31, 38, 45,
+ -77, -68, -59, -50, -41, -32, -23, -14, -5, 4, 13, 22, 31, 40, 49, 58,
+ -90, -80, -69, -59, -48, -38, -27, -17, -6, 5, 16, 27, 38, 48, 59, 69,
+ -112,-104, -91, -78, -65, -52, -38, -23, -7, 6, 19, 32, 45, 58, 71, 84,
+ -128,-120,-104, -88, -72, -56, -40, -24, -8, 8, 24, 40, 56, 72, 88, 104,
+ -128,-128,-118,-100, -82, -64, -45, -27, -9, 9, 27, 45, 63, 82, 100, 118
+};
+
+/* address = table_index(9:8) | dsp->espcm_last_value(7:3) | codeword(2:0)
+ * the value is a base index into espcm_range_map with bits at (8, 3:0),
+ * to be OR'ed with dsp->espcm_range at (7:4)
+ */
+uint16_t espcm3_dpcm_tables[1024] =
+{
+ /* Table 0 */
+ 256, 257, 258, 259, 260, 263, 266, 269, 0, 257, 258, 259, 260, 263, 266, 269,
+ 0, 1, 258, 259, 260, 263, 266, 269, 1, 2, 259, 260, 261, 263, 266, 269,
+ 1, 3, 260, 261, 262, 264, 266, 269, 1, 3, 4, 261, 262, 264, 266, 269,
+ 2, 4, 5, 262, 263, 264, 266, 269, 2, 4, 6, 263, 264, 265, 267, 269,
+ 2, 4, 6, 7, 264, 265, 267, 269, 2, 5, 7, 8, 265, 266, 267, 269,
+ 2, 5, 7, 8, 9, 266, 268, 270, 2, 5, 7, 9, 10, 267, 268, 270,
+ 2, 5, 8, 10, 11, 268, 269, 270, 2, 5, 8, 11, 12, 269, 270, 271,
+ 2, 5, 8, 11, 12, 13, 270, 271, 2, 5, 8, 11, 12, 13, 14, 271,
+ 0, 257, 258, 259, 260, 263, 266, 269, 0, 1, 258, 259, 260, 263, 266, 269,
+ 0, 1, 2, 259, 260, 263, 266, 269, 1, 2, 3, 260, 261, 263, 266, 269,
+ 1, 3, 4, 261, 262, 264, 266, 269, 1, 3, 5, 262, 263, 264, 266, 269,
+ 2, 4, 5, 6, 263, 264, 266, 269, 2, 4, 6, 7, 264, 265, 267, 269,
+ 2, 4, 6, 7, 8, 265, 267, 269, 2, 5, 7, 8, 9, 266, 267, 269,
+ 2, 5, 7, 9, 10, 267, 268, 270, 2, 5, 7, 9, 10, 11, 268, 270,
+ 2, 5, 8, 10, 11, 12, 269, 270, 2, 5, 8, 11, 12, 13, 270, 271,
+ 2, 5, 8, 11, 12, 13, 14, 271, 2, 5, 8, 11, 12, 13, 14, 15,
+ /* Table 1 */
+ 257, 260, 262, 263, 264, 265, 267, 270, 257, 260, 262, 263, 264, 265, 267, 270,
+ 1, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
+ 1, 260, 262, 263, 264, 265, 267, 270, 1, 4, 262, 263, 264, 265, 267, 270,
+ 1, 4, 262, 263, 264, 265, 267, 270, 1, 4, 6, 263, 264, 265, 267, 270,
+ 1, 4, 6, 7, 264, 265, 267, 270, 1, 4, 6, 7, 8, 265, 267, 270,
+ 1, 4, 6, 7, 8, 9, 267, 270, 1, 4, 6, 7, 8, 9, 267, 270,
+ 1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 270,
+ 1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 14,
+ 257, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
+ 1, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
+ 1, 4, 262, 263, 264, 265, 267, 270, 1, 4, 262, 263, 264, 265, 267, 270,
+ 1, 4, 6, 263, 264, 265, 267, 270, 1, 4, 6, 7, 264, 265, 267, 270,
+ 1, 4, 6, 7, 8, 265, 267, 270, 1, 4, 6, 7, 8, 9, 267, 270,
+ 1, 4, 6, 7, 8, 9, 267, 270, 1, 4, 6, 7, 8, 9, 11, 270,
+ 1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 270,
+ 1, 4, 6, 7, 8, 9, 11, 14, 1, 4, 6, 7, 8, 9, 11, 14,
+ /* Table 2 */
+ 256, 257, 258, 259, 260, 262, 265, 268, 0, 257, 258, 259, 260, 262, 265, 268,
+ 0, 1, 258, 259, 260, 262, 265, 269, 1, 2, 259, 260, 261, 263, 265, 269,
+ 1, 3, 260, 261, 262, 263, 265, 269, 1, 3, 4, 261, 262, 263, 265, 269,
+ 1, 3, 5, 262, 263, 264, 266, 269, 1, 4, 5, 6, 263, 264, 266, 269,
+ 1, 4, 6, 7, 264, 265, 266, 269, 1, 4, 6, 7, 8, 265, 266, 269,
+ 2, 4, 6, 7, 8, 9, 267, 269, 2, 4, 6, 7, 8, 9, 267, 269,
+ 2, 5, 7, 8, 9, 10, 11, 270, 2, 5, 7, 8, 9, 10, 11, 270,
+ 2, 5, 8, 9, 10, 11, 12, 270, 2, 6, 8, 10, 11, 12, 13, 14,
+ 257, 258, 259, 260, 261, 263, 265, 269, 1, 259, 260, 261, 262, 263, 266, 269,
+ 1, 260, 261, 262, 263, 264, 266, 269, 1, 260, 261, 262, 263, 264, 266, 269,
+ 2, 4, 262, 263, 264, 265, 267, 269, 2, 4, 262, 263, 264, 265, 267, 269,
+ 2, 5, 6, 263, 264, 265, 267, 270, 2, 5, 6, 7, 264, 265, 267, 270,
+ 2, 5, 7, 8, 265, 266, 267, 270, 2, 5, 7, 8, 9, 266, 268, 270,
+ 2, 6, 8, 9, 10, 267, 268, 270, 2, 6, 8, 9, 10, 11, 268, 270,
+ 2, 6, 8, 10, 11, 12, 269, 270, 2, 6, 9, 11, 12, 13, 270, 271,
+ 3, 6, 9, 11, 12, 13, 14, 271, 3, 6, 9, 11, 12, 13, 14, 15,
+ /* Table 3 */
+ 256, 258, 260, 261, 262, 263, 264, 265, 0, 258, 260, 261, 262, 263, 264, 265,
+ 1, 259, 260, 261, 262, 263, 264, 266, 1, 259, 260, 261, 262, 263, 264, 266,
+ 1, 3, 260, 261, 262, 263, 264, 266, 1, 3, 4, 261, 262, 263, 264, 267,
+ 1, 3, 4, 5, 262, 263, 264, 267, 1, 3, 4, 5, 6, 263, 264, 267,
+ 1, 3, 5, 6, 7, 264, 265, 268, 1, 3, 5, 6, 7, 8, 265, 268,
+ 1, 4, 6, 7, 8, 9, 266, 269, 1, 4, 6, 7, 8, 9, 10, 269,
+ 1, 4, 6, 7, 8, 9, 10, 269, 1, 4, 6, 7, 8, 9, 11, 270,
+ 1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 14,
+ 257, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
+ 1, 260, 262, 263, 264, 265, 267, 270, 2, 261, 262, 263, 264, 265, 267, 270,
+ 2, 261, 262, 263, 264, 265, 267, 270, 2, 5, 262, 263, 264, 265, 267, 270,
+ 3, 6, 263, 264, 265, 266, 268, 270, 3, 6, 7, 264, 265, 266, 268, 270,
+ 4, 7, 8, 265, 266, 267, 268, 270, 4, 7, 8, 9, 266, 267, 268, 270,
+ 4, 7, 8, 9, 10, 267, 268, 270, 5, 7, 8, 9, 10, 11, 268, 270,
+ 5, 7, 8, 9, 10, 11, 12, 270, 5, 7, 8, 9, 10, 11, 12, 270,
+ 6, 7, 8, 9, 10, 11, 13, 271, 6, 7, 8, 9, 10, 11, 13, 15
+};
+// clang-format on
+
double low_fir_sb16_coef[4][SB16_NCoef];
#ifdef ENABLE_SB_DSP_LOG
@@ -135,6 +260,8 @@ sb_dsp_log(const char *fmt, ...)
# define sb_dsp_log(fmt, ...)
#endif
+#define ESSreg(reg) (dsp)->ess_regs[reg - 0xA0]
+
static __inline double
sinc(double x)
{
@@ -172,6 +299,37 @@ recalc_sb16_filter(int c, int playback_freq)
low_fir_sb16_coef[c][n] /= gain;
}
+static void
+recalc_opl_filter(int c, int playback_freq)
+{
+ /* Cutoff frequency = playback / 2 */
+ int n;
+ double w;
+ double h;
+ double fC = ((double) playback_freq) / (double) (FREQ_49716 * 2);
+ double gain;
+
+ for (n = 0; n < SB16_NCoef; n++) {
+ /* Blackman window */
+ w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1)));
+ /* Sinc filter */
+ h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0)));
+
+ /* Create windowed-sinc filter */
+ low_fir_sb16_coef[c][n] = w * h;
+ }
+
+ low_fir_sb16_coef[c][(SB16_NCoef - 1) / 2] = 1.0;
+
+ gain = 0.0;
+ for (n = 0; n < SB16_NCoef; n++)
+ gain += low_fir_sb16_coef[c][n];
+
+ /* Normalise filter, to produce unity gain */
+ for (n = 0; n < SB16_NCoef; n++)
+ low_fir_sb16_coef[c][n] /= gain;
+}
+
static void
sb_irq_update_pic(void *priv, int set)
{
@@ -206,6 +364,16 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set)
{
int masked = 0;
+ if (dsp->sb_irq8 || dsp->sb_irq16)
+ return;
+
+ /* NOTE: not on ES1688 or ES1868 */
+ if (IS_ESS(dsp) && dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688
+ && !(ESSreg(0xB1) & 0x10)) // if ESS playback, and IRQ disabled, do not fire
+ {
+ return;
+ }
+
switch (bit) {
default:
case 0:
@@ -222,6 +390,17 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set)
break;
}
+ /* NOTE: not on ES1688, apparently; investigate on ES1868 */
+ if (IS_ESS(dsp) && dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688) {
+ /* TODO: Investigate real hardware for this (the ES1887 datasheet documents this bit somewhat oddly.) */
+ if (dsp->ess_playback_mode && bit <= 1 && set && !masked) {
+ if (!(ESSreg(0xB1) & 0x40)) // if ESS playback, and IRQ disabled, do not fire
+ {
+ return;
+ }
+ }
+ }
+
if (set && !masked)
dsp->irq_update(dsp->irq_priv, 1);
else if (!set)
@@ -321,6 +500,9 @@ sb_doreset(sb_dsp_t *dsp)
dsp->sb_asp_regs[5] = 0x01;
dsp->sb_asp_regs[9] = 0xf8;
+
+ /* Initialize ESS registers */
+ ESSreg(0xA5) = 0xf8;
}
void
@@ -344,6 +526,23 @@ sb_add_data(sb_dsp_t *dsp, uint8_t v)
dsp->sb_read_wp &= 0xff;
}
+static unsigned int
+sb_ess_get_dma_counter(sb_dsp_t *dsp)
+{
+ unsigned int c;
+
+ c = (unsigned int) ESSreg(0xA5) << 8U;
+ c |= (unsigned int) ESSreg(0xA4);
+
+ return c;
+}
+
+static unsigned int
+sb_ess_get_dma_len(sb_dsp_t *dsp)
+{
+ return 0x10000U - sb_ess_get_dma_counter(dsp);
+}
+
void
sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
{
@@ -375,6 +574,9 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
if (!timer_is_enabled(&dsp->output_timer))
timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho);
}
+
+ /* This will be set later for ESS playback/record modes. */
+ dsp->ess_playback_mode = 0;
}
void
@@ -407,6 +609,54 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
memset(dsp->record_buffer, 0, sizeof(dsp->record_buffer));
}
+void
+sb_start_dma_ess(sb_dsp_t *dsp)
+{
+ uint8_t real_format = 0;
+ dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
+ uint32_t len = sb_ess_get_dma_len(dsp);
+
+ if (IS_ESS(dsp)) {
+ dma_set_drq(dsp->sb_8_dmanum, 0);
+ dma_set_drq(dsp->sb_16_8_dmanum, 0);
+ }
+ real_format |= !!(ESSreg(0xB7) & 0x20) ? 0x10 : 0;
+ real_format |= !!(ESSreg(0xB7) & 0x8) ? 0x20 : 0;
+ if (!!(ESSreg(0xB8) & 8))
+ sb_start_dma_i(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, len);
+ else
+ sb_start_dma(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, len);
+ dsp->ess_playback_mode = 1;
+ dma_set_drq(dsp->sb_8_dmanum, 1);
+ dma_set_drq(dsp->sb_16_8_dmanum, 1);
+}
+
+void
+sb_stop_dma_ess(sb_dsp_t *dsp)
+{
+ dsp->sb_8_enable = dsp->sb_16_enable = 0;
+ dma_set_drq(dsp->sb_16_8_dmanum, 0);
+ dma_set_drq(dsp->sb_8_dmanum, 0);
+}
+
+static void
+sb_ess_update_dma_status(sb_dsp_t *dsp)
+{
+ bool dma_en = (ESSreg(0xB8) & 1) ? true : false;
+
+ // if the DRQ is disabled, do not start
+ if (!(ESSreg(0xB2) & 0x40))
+ dma_en = false;
+
+ if (dma_en) {
+ if (!dsp->sb_8_enable && !dsp->sb_16_enable)
+ sb_start_dma_ess(dsp);
+ } else {
+ if (dsp->sb_8_enable || dsp->sb_16_enable)
+ sb_stop_dma_ess(dsp);
+ }
+}
+
int
sb_8_read_dma(void *priv)
{
@@ -439,7 +689,8 @@ int
sb_16_read_dma(void *priv)
{
const sb_dsp_t *dsp = (sb_dsp_t *) priv;
- int temp, ret = 0;
+
+ int temp, ret = 0;
int dma_flags, dma_ch = dsp->sb_16_dmanum;
if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate)
@@ -454,7 +705,7 @@ sb_16_read_dma(void *priv)
/* High DMA channel disabled, always use the first 8-bit channel. */
dma_ch = dsp->sb_8_dmanum;
temp = dma_channel_read(dma_ch);
- ret = temp;
+ ret = temp;
if ((temp != DMA_NODATA) && !(temp & DMA_OVER)) {
temp = dma_channel_read(dma_ch);
if (temp == DMA_NODATA)
@@ -463,7 +714,7 @@ sb_16_read_dma(void *priv)
dma_flags = temp & DMA_OVER;
temp &= ~DMA_OVER;
ret |= (temp << 8) | dma_flags;
- }
+ }
}
}
@@ -474,9 +725,10 @@ int
sb_16_write_dma(void *priv, uint16_t val)
{
const sb_dsp_t *dsp = (sb_dsp_t *) priv;
+
int temp, ret = 0;
int dma_ch = dsp->sb_16_dmanum;
-
+
if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate)
ret = dma_channel_write(dma_ch, val) == DMA_NODATA;
else {
@@ -489,21 +741,68 @@ sb_16_write_dma(void *priv, uint16_t val)
/* High DMA channel disabled, always use the first 8-bit channel. */
dma_ch = dsp->sb_8_dmanum;
temp = dma_channel_write(dma_ch, val & 0xff);
- ret = temp;
+ ret = temp;
if ((temp != DMA_NODATA) && (temp != DMA_OVER)) {
temp = dma_channel_write(dma_ch, val >> 8);
- ret = temp;
+ ret = temp;
}
}
return ret;
}
+void
+sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy)
+{
+ uint8_t t = 0x00;
+ /* IRQ control */
+ if (legacy) {
+ t |= 0x80;
+ }
+ switch (dsp->sb_irqnum) {
+ case 9:
+ t |= 0x0;
+ break;
+ case 5:
+ t |= 0x5;
+ break;
+ case 7:
+ t |= 0xA;
+ break;
+ case 10:
+ t |= 0xF;
+ break;
+ }
+ ESSreg(0xB1) = (ESSreg(0xB1) & 0xF0) | t;
+
+ /* DRQ control */
+ t = 0x00;
+ if (legacy) {
+ t |= 0x80;
+ }
+ switch (dsp->sb_8_dmanum) {
+ case 0:
+ t |= 0x5;
+ break;
+ case 1:
+ t |= 0xA;
+ break;
+ case 3:
+ t |= 0xF;
+ break;
+ }
+ ESSreg(0xB2) = (ESSreg(0xB2) & 0xF0) | t;
+}
+
void
sb_dsp_setirq(sb_dsp_t *dsp, int irq)
{
sb_dsp_log("IRQ now: %i\n", irq);
dsp->sb_irqnum = irq;
+
+ sb_ess_update_irq_drq_readback_regs(dsp, true);
+
+ ESSreg(0xB1) = (ESSreg(0xB1) & 0xEF) | 0x10;
}
void
@@ -511,6 +810,8 @@ sb_dsp_setdma8(sb_dsp_t *dsp, int dma)
{
sb_dsp_log("8-bit DMA now: %i\n", dma);
dsp->sb_8_dmanum = dma;
+
+ sb_ess_update_irq_drq_readback_regs(dsp, true);
}
void
@@ -548,6 +849,227 @@ sb_dsp_setdma16_translate(sb_dsp_t *dsp, int translate)
dsp->sb_16_dma_translate = translate;
}
+static void
+sb_ess_update_reg_a2(sb_dsp_t *dsp, uint8_t val)
+{
+ double freq = (7160000.0 / (256.0 - ((double) val))) * 41.0;
+ int temp = (int) freq;
+ ESSreg(0xA2) = val;
+
+ if (dsp->sb_freq != temp)
+ recalc_sb16_filter(0, temp);
+ dsp->sb_freq = temp;
+}
+
+/* TODO: Investigate ESS cards' filtering on real hardware as well.
+ (DOSBox-X did it purely off some laptop's ESS chip, which isn't a good look.) */
+static void
+sb_ess_update_filter_freq(sb_dsp_t *dsp)
+{
+ double temp = (7160000.0 / (((((double) dsp->sb_freq) / 2.0) * 0.80) * 82.0)) - 256.0;
+
+ if (dsp->sb_freq >= 22050)
+ ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq);
+ else
+ ESSreg(0xA1) = 128 - (397700UL / dsp->sb_freq);
+
+ sb_ess_update_reg_a2(dsp, (uint8_t) temp);
+}
+
+static uint8_t
+sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg)
+{
+ switch (reg) {
+ default:
+ return ESSreg(reg);
+ }
+}
+
+static void
+sb_ess_update_autolen(sb_dsp_t *dsp)
+{
+ dsp->sb_8_autolen = dsp->sb_16_autolen = sb_ess_get_dma_len(dsp);
+}
+
+static void
+sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data)
+{
+ uint8_t chg = 0x00;
+
+ switch (reg) {
+ case 0xA1: /* Extended Mode Sample Rate Generator */
+ {
+ double temp;
+ ESSreg(reg) = data;
+ if (data & 0x80)
+ dsp->sb_freq = 795500UL / (256ul - data);
+ else
+ dsp->sb_freq = 397700UL / (128ul - data);
+ temp = 1000000.0 / dsp->sb_freq;
+ dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp;
+
+ dsp->sb_timei = dsp->sb_timeo;
+ break;
+ }
+ case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */
+ sb_ess_update_reg_a2(dsp, data);
+ break;
+
+ case 0xA4: /* DMA Transfer Count Reload (low) */
+ case 0xA5: /* DMA Transfer Count Reload (high) */
+ ESSreg(reg) = data;
+ sb_ess_update_autolen(dsp);
+ if ((dsp->sb_16_length < 0 && !dsp->sb_16_enable) && (dsp->sb_8_length < 0 && !dsp->sb_8_enable))
+ dsp->ess_reload_len = 1;
+ break;
+
+ case 0xA8: /* Analog Control */
+ /* bits 7:5 0 Reserved. Always write 0
+ * bit 4 1 Reserved. Always write 1
+ * bit 3 Record monitor 1=Enable record monitor
+ * enable
+ * bit 2 0 Reserved. Always write 0
+ * bits 1:0 Stereo/mono select 00=Reserved
+ * 01=Stereo
+ * 10=Mono
+ * 11=Reserved */
+ chg = ESSreg(reg) ^ data;
+ ESSreg(reg) = data;
+ if (chg & 0x3) {
+ if (dsp->sb_16_enable || dsp->sb_8_enable) {
+ uint8_t real_format = 0x00;
+ real_format |= !!(ESSreg(0xB7) & 0x20) ? 0x10 : 0;
+ real_format |= !!(ESSreg(0xB7) & 0x8) ? 0x20 : 0;
+
+ if (dsp->sb_16_enable)
+ dsp->sb_16_format = real_format;
+
+ if (dsp->sb_8_enable)
+ dsp->sb_8_format = real_format;
+ }
+ }
+ break;
+
+ case 0xB1: /* Legacy Audio Interrupt Control */
+ ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable
+ switch (data & 0x0C) {
+ case 0x00:
+ dsp->sb_irqnum = 2;
+ break;
+ case 0x04:
+ dsp->sb_irqnum = 5;
+ break;
+ case 0x08:
+ dsp->sb_irqnum = 7;
+ break;
+ case 0x0C:
+ dsp->sb_irqnum = 10;
+ break;
+ }
+ sb_ess_update_irq_drq_readback_regs(dsp, false);
+ break;
+ case 0xB2: /* DRQ Control */
+ chg = ESSreg(reg) ^ data;
+ ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable
+ switch (data & 0x0C) {
+ case 0x00:
+ dsp->sb_8_dmanum = -1;
+ break;
+ case 0x04:
+ dsp->sb_8_dmanum = 0;
+ break;
+ case 0x08:
+ dsp->sb_8_dmanum = 1;
+ break;
+ case 0x0C:
+ dsp->sb_8_dmanum = 3;
+ break;
+ }
+ sb_ess_update_irq_drq_readback_regs(dsp, false);
+ if (chg & 0x40)
+ sb_ess_update_dma_status(dsp);
+ break;
+ case 0xB5: /* DAC Direct Access Holding (low) */
+ case 0xB6: /* DAC Direct Access Holding (high) */
+ ESSreg(reg) = data;
+ break;
+
+ case 0xB7: /* Audio 1 Control 1 */
+ /* bit 7 Enable FIFO to/from codec
+ * bit 6 Opposite from bit 3 Must be set opposite to bit 3
+ * bit 5 FIFO signed mode 1=Data is signed twos-complement 0=Data is unsigned
+ * bit 4 Reserved Always write 1
+ * bit 3 FIFO stereo mode 1=Data is stereo
+ * bit 2 FIFO 16-bit mode 1=Data is 16-bit
+ * bit 1 Reserved Always write 0
+ * bit 0 Generate load signal */
+ chg = ESSreg(reg) ^ data;
+ ESSreg(reg) = data;
+
+ if (chg & 4)
+ sb_ess_update_autolen(dsp);
+
+ if (chg & 0x0C) {
+ if (dsp->sb_16_enable || dsp->sb_8_enable) {
+ sb_stop_dma_ess(dsp);
+ sb_start_dma_ess(dsp);
+ }
+ }
+ break;
+
+ case 0xB8: /* Audio 1 Control 2 */
+ /* bits 7:4 reserved
+ * bit 3 CODEC mode 1=first DMA converter in ADC mode
+ * 0=first DMA converter in DAC mode
+ * bit 2 DMA mode 1=auto-initialize mode
+ * 0=normal DMA mode
+ * bit 1 DMA read enable 1=first DMA is read (for ADC)
+ * 0=first DMA is write (for DAC)
+ * bit 0 DMA xfer enable 1=DMA is allowed to proceed */
+ data &= 0xF;
+ chg = ESSreg(reg) ^ data;
+ ESSreg(reg) = data;
+
+ if (chg & 1) {
+ if (dsp->sb_16_enable || dsp->sb_8_enable) {
+ if (dsp->sb_16_enable)
+ dsp->sb_16_length = sb_ess_get_dma_len(dsp);
+ if (dsp->sb_8_enable)
+ dsp->sb_8_length = sb_ess_get_dma_len(dsp);
+ } else
+ dsp->ess_reload_len = 1;
+ }
+
+ if (chg & 0x4) {
+ if (dsp->sb_16_enable) {
+ dsp->sb_16_autoinit = (ESSreg(0xB8) & 0x4) != 0;
+ }
+ if (dsp->sb_8_enable) {
+ dsp->sb_8_autoinit = (ESSreg(0xB8) & 0x4) != 0;
+ }
+ }
+
+ if (chg & 0xB) {
+ if (chg & 0xA)
+ sb_stop_dma_ess(dsp); /* changing capture/playback direction? stop DMA to reinit */
+ sb_ess_update_dma_status(dsp);
+ }
+ break;
+
+ case 0xB9: /* Audio 1 Transfer Type */
+ case 0xBA: /* Left Channel ADC Offset Adjust */
+ case 0xBB: /* Right Channel ADC Offset Adjust */
+ case 0xC3: /* Internal state register */
+ case 0xCF: /* GPO0/1 power management register */
+ ESSreg(reg) = data;
+ break;
+
+ default:
+ sb_dsp_log("UNKNOWN ESS register write reg=%02xh val=%02xh\n", reg, data);
+ break;
+ }
+}
+
void
sb_exec_command(sb_dsp_t *dsp)
{
@@ -558,8 +1080,29 @@ sb_exec_command(sb_dsp_t *dsp)
/* Update 8051 ram with the current DSP command.
See https://github.com/joncampbell123/dosbox-x/issues/1044 */
- if (dsp->sb_type >= SB16)
+ if (dsp->sb_type >= SB16) {
dsp->sb_8051_ram[0x20] = dsp->sb_command;
+ }
+
+ if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) {
+ if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) {
+ dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6);
+ return;
+ } else if (dsp->sb_command == 0xC2) {
+ sb_ess_write_reg(dsp, 0xC3, dsp->sb_data[0]);
+ } else if (dsp->sb_command == 0xC3) {
+ sb_add_data(dsp, sb_ess_read_reg(dsp, 0xC3));
+ } else if (dsp->sb_command == 0xCE) {
+ sb_add_data(dsp, sb_ess_read_reg(dsp, 0xCF));
+ } else if (dsp->sb_command == 0xCF) {
+ sb_ess_write_reg(dsp, 0xCF, dsp->sb_data[0]);
+ } else if (dsp->sb_command == 0xC0) {
+ sb_add_data(dsp, sb_ess_read_reg(dsp, dsp->sb_data[0]));
+ } else if (dsp->sb_command < 0xC0 && dsp->ess_extended_mode) {
+ sb_ess_write_reg(dsp, dsp->sb_command, dsp->sb_data[0]);
+ }
+ return;
+ }
switch (dsp->sb_command) {
case 0x01: /* ???? */
@@ -667,6 +1210,9 @@ sb_exec_command(sb_dsp_t *dsp)
case 0x10: /* 8-bit direct mode */
sb_dsp_update(dsp);
dsp->sbdat = dsp->sbdatl = dsp->sbdatr = (dsp->sb_data[0] ^ 0x80) << 8;
+ // FIXME: What does the ESS AudioDrive do to its filter/sample rate divider registers when emulating this Sound Blaster command?
+ ESSreg(0xA1) = 128 - (397700 / 22050);
+ ESSreg(0xA2) = 256 - (7160000 / (82 * ((4 * 22050) / 10)));
break;
case 0x14: /* 8-bit single cycle DMA output */
sb_start_dma(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
@@ -679,8 +1225,11 @@ sb_exec_command(sb_dsp_t *dsp)
sb_start_dma(dsp, 1, 0, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
- if (dsp->sb_command == 0x17)
+ dsp->ess_dma_counter++;
+ if (dsp->sb_command == 0x17) {
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
break;
case 0x1C: /* 8-bit autoinit DMA output */
if (dsp->sb_type >= SB15)
@@ -691,6 +1240,7 @@ sb_exec_command(sb_dsp_t *dsp)
sb_start_dma(dsp, 1, 1, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
}
break;
case 0x20: /* 8-bit direct input */
@@ -755,22 +1305,25 @@ sb_exec_command(sb_dsp_t *dsp)
dsp->sblatcho = dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_data[0]);
temp = 256 - dsp->sb_data[0];
temp = 1000000 / temp;
- sb_dsp_log("Sample rate - %ihz (%i)\n", temp, dsp->sblatcho);
+ sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho);
if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16))
recalc_sb16_filter(0, temp);
dsp->sb_freq = temp;
+ if (IS_ESS(dsp)) {
+ sb_ess_update_filter_freq(dsp);
+ }
break;
case 0x41: /* Set output sampling rate */
case 0x42: /* Set input sampling rate */
if (dsp->sb_type >= SB16) {
dsp->sblatcho = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_data[1] + (dsp->sb_data[0] << 8))));
- sb_dsp_log("Sample rate - %ihz (%i)\n", dsp->sb_data[1] + (dsp->sb_data[0] << 8), dsp->sblatcho);
+ sb_dsp_log("Sample rate - %ihz (%f)\n", dsp->sb_data[1] + (dsp->sb_data[0] << 8), dsp->sblatcho);
temp = dsp->sb_freq;
dsp->sb_freq = dsp->sb_data[1] + (dsp->sb_data[0] << 8);
dsp->sb_timeo = 256LL + dsp->sb_freq;
dsp->sblatchi = dsp->sblatcho;
dsp->sb_timei = dsp->sb_timeo;
- if (dsp->sb_freq != temp && dsp->sb_type >= SB16)
+ if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16))
recalc_sb16_filter(0, dsp->sb_freq);
dsp->sb_8051_ram[0x13] = dsp->sb_freq & 0xff;
dsp->sb_8051_ram[0x14] = (dsp->sb_freq >> 8) & 0xff;
@@ -783,6 +1336,50 @@ sb_exec_command(sb_dsp_t *dsp)
case 0x48: /* Set DSP block transfer size */
dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8);
break;
+ case 0x65: /* 4-bit ESPCM output with reference */
+ case 0x64: /* 4-bit ESPCM output */
+ if (IS_ESS(dsp)) {
+ if (dsp->espcm_mode != ESPCM_4 || (dsp->sb_8_enable && dsp->sb_8_pause)) {
+ fifo_reset(dsp->espcm_fifo);
+ dsp->espcm_sample_idx = 0;
+ }
+ dsp->espcm_mode = ESPCM_4;
+ sb_start_dma(dsp, 1, 0, ESPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
+ }
+ break;
+ case 0x67: /* 3-bit ESPCM output with reference */
+ case 0x66: /* 3-bit ESPCM output */
+ if (IS_ESS(dsp)) {
+ if (dsp->espcm_mode != ESPCM_3 || (dsp->sb_8_enable && dsp->sb_8_pause)) {
+ fifo_reset(dsp->espcm_fifo);
+ dsp->espcm_sample_idx = 0;
+ }
+ dsp->espcm_mode = ESPCM_3;
+ sb_start_dma(dsp, 1, 0, ESPCM_3, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
+ }
+ break;
+ case 0x6D: /* 1-bit ESPCM output with reference */
+ case 0x6C: /* 1-bit ESPCM output */
+ if (IS_ESS(dsp)) {
+ if (dsp->espcm_mode != ESPCM_1 || (dsp->sb_8_enable && dsp->sb_8_pause)) {
+ fifo_reset(dsp->espcm_fifo);
+ dsp->espcm_sample_idx = 0;
+ }
+ dsp->espcm_mode = ESPCM_1;
+ sb_start_dma(dsp, 1, 0, ESPCM_1, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
+ }
+ break;
+ case 0x6F: /* 4-bit ESPCM input with reference */
+ case 0x6E: /* 4-bit ESPCM input */
+ if (IS_ESS(dsp)) {
+ if (dsp->espcm_mode != ESPCM_4E || (dsp->sb_8_enable && dsp->sb_8_pause)) {
+ fifo_reset(dsp->espcm_fifo);
+ dsp->espcm_sample_idx = 0;
+ }
+ dsp->espcm_mode = ESPCM_4E;
+ sb_start_dma_i(dsp, 1, 0, ESPCM_4E, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
+ }
+ break;
case 0x75: /* 4-bit ADPCM output with reference */
dsp->sbref = dsp->dma_readb(dsp->dma_priv);
dsp->sbstep = 0;
@@ -791,8 +1388,11 @@ sb_exec_command(sb_dsp_t *dsp)
sb_start_dma(dsp, 1, 0, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
- if (dsp->sb_command == 0x75)
+ dsp->ess_dma_counter++;
+ if (dsp->sb_command == 0x75) {
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
break;
case 0x77: /* 2.6-bit ADPCM output with reference */
dsp->sbref = dsp->dma_readb(dsp->dma_priv);
@@ -802,14 +1402,18 @@ sb_exec_command(sb_dsp_t *dsp)
sb_start_dma(dsp, 1, 0, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
- if (dsp->sb_command == 0x77)
+ dsp->ess_dma_counter++;
+ if (dsp->sb_command == 0x77) {
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
break;
case 0x7D: /* 4-bit ADPCM autoinit output */
if (dsp->sb_type >= SB15) {
sb_start_dma(dsp, 1, 1, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
}
break;
case 0x7F: /* 2.6-bit ADPCM autoinit output */
@@ -817,6 +1421,7 @@ sb_exec_command(sb_dsp_t *dsp)
sb_start_dma(dsp, 1, 1, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
}
break;
case 0x80: /* Pause DAC */
@@ -906,17 +1511,21 @@ sb_exec_command(sb_dsp_t *dsp)
dsp->sb_8_pause = 1;
break;
case 0xD1: /* Speaker on */
- if (dsp->sb_type < SB15)
- dsp->sb_8_pause = 1;
- else if (dsp->sb_type < SB16)
- dsp->muted = 0;
+ if (!IS_ESS(dsp)) {
+ if (dsp->sb_type < SB15)
+ dsp->sb_8_pause = 1;
+ else if (dsp->sb_type < SB16)
+ dsp->muted = 0;
+ }
dsp->sb_speaker = 1;
break;
case 0xD3: /* Speaker off */
- if (dsp->sb_type < SB15)
- dsp->sb_8_pause = 1;
- else if (dsp->sb_type < SB16)
- dsp->muted = 1;
+ if (!IS_ESS(dsp)) {
+ if (dsp->sb_type < SB15)
+ dsp->sb_8_pause = 1;
+ else if (dsp->sb_type < SB16)
+ dsp->muted = 1;
+ }
dsp->sb_speaker = 0;
break;
case 0xD4: /* Continue 8-bit DMA */
@@ -944,6 +1553,11 @@ sb_exec_command(sb_dsp_t *dsp)
sb_add_data(dsp, ~dsp->sb_data[0]);
break;
case 0xE1: /* Get DSP version */
+ if (IS_ESS(dsp)) {
+ sb_add_data(dsp, 0x3);
+ sb_add_data(dsp, 0x1);
+ break;
+ }
if (IS_AZTECH(dsp)) {
if (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11) {
sb_add_data(dsp, 0x3);
@@ -972,23 +1586,43 @@ sb_exec_command(sb_dsp_t *dsp)
while (sb16_copyright[c])
sb_add_data(dsp, sb16_copyright[c++]);
sb_add_data(dsp, 0);
+ } else if (IS_ESS(dsp)) {
+ sb_add_data(dsp, 0);
}
break;
case 0xE4: /* Write test register */
dsp->sb_test = dsp->sb_data[0];
break;
- case 0xE7: /* ???? */
+ case 0xE7: /* ESS detect/read config on ESS cards */
+ if (IS_ESS(dsp)) {
+ switch (dsp->sb_subtype) {
+ default:
+ break;
+ case SB_SUBTYPE_ESS_ES1688:
+ // Determined via Windows driver debugging.
+ sb_add_data(dsp, 0x68);
+ sb_add_data(dsp, 0x80 | 0x09);
+ break;
+ }
+ }
break;
case 0xE8: /* Read test register */
sb_add_data(dsp, dsp->sb_test);
break;
case 0xF2: /* Trigger 8-bit IRQ */
sb_dsp_log("Trigger IRQ\n");
- sb_irq(dsp, 1);
+ if (IS_ESS(dsp)) {
+ if (!timer_is_enabled(&dsp->irq_timer)) {
+ timer_set_delay_u64(&dsp->irq_timer, (100ULL * TIMER_USEC));
+ }
+ } else {
+ sb_irq(dsp, 1);
+ dsp->ess_irq_generic = true;
+ }
break;
case 0xF3: /* Trigger 16-bit IRQ */
sb_dsp_log("Trigger IRQ\n");
- sb_irq(dsp, 0);
+ dsp->ess_irq_generic = true;
break;
case 0xF8:
if (dsp->sb_type < SB16)
@@ -1036,7 +1670,7 @@ sb_write(uint16_t a, uint8_t v, void *priv)
sb_dsp_t *dsp = (sb_dsp_t *) priv;
/* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */
- if (dsp->sb_type < SB16)
+ if (dsp->sb_type < SB16 && (!IS_ESS(dsp) || (IS_ESS(dsp) && ((a & 0xF) != 0xE))))
a &= 0xfffe;
switch (a & 0xF) {
@@ -1048,9 +1682,14 @@ sb_write(uint16_t a, uint8_t v, void *priv)
}
dsp->sbreset = v;
}
- dsp->uart_midi = 0;
- dsp->uart_irq = 0;
- dsp->onebyte_midi = 0;
+
+ if (!(v & 2) && (dsp->espcm_fifo_reset & 2)) {
+ fifo_reset(dsp->espcm_fifo);
+ }
+ dsp->espcm_fifo_reset = v;
+ dsp->uart_midi = 0;
+ dsp->uart_irq = 0;
+ dsp->onebyte_midi = 0;
return;
case 0xC: /* Command/data write */
if (dsp->uart_midi || dsp->onebyte_midi) {
@@ -1071,8 +1710,6 @@ sb_write(uint16_t a, uint8_t v, void *priv)
if (v == 0x01)
sb_add_data(dsp, 0);
dsp->sb_data_stat++;
- } else {
- dsp->sb_data[dsp->sb_data_stat++] = v;
if (IS_AZTECH(dsp)) {
/* variable length commands */
if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x08)
@@ -1080,6 +1717,24 @@ sb_write(uint16_t a, uint8_t v, void *priv)
else if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x07)
sb_commands[dsp->sb_command] = 2;
}
+ if (IS_ESS(dsp) && dsp->sb_command >= 0x64 && dsp->sb_command <= 0x6F) {
+ sb_commands[dsp->sb_command] = 2;
+ } else if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) {
+ if (dsp->sb_command <= 0xC0
+ || dsp->sb_command == 0xC2
+ || dsp->sb_command == 0xCF) {
+ sb_commands[dsp->sb_command] = 1;
+ } else if (dsp->sb_command == 0xC3
+ || dsp->sb_command == 0xC6
+ || dsp->sb_command == 0xC7
+ || dsp->sb_command == 0xCE) {
+ sb_commands[dsp->sb_command] = 0;
+ } else {
+ sb_commands[dsp->sb_command] = -1;
+ }
+ }
+ } else {
+ dsp->sb_data[dsp->sb_data_stat++] = v;
}
if (dsp->sb_data_stat == sb_commands[dsp->sb_command] || sb_commands[dsp->sb_command] == -1) {
sb_exec_command(dsp);
@@ -1104,8 +1759,12 @@ sb_read(uint16_t a, void *priv)
uint8_t ret = 0x00;
/* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */
- if (dsp->sb_type < SB16)
- a &= 0xfffe;
+ if (dsp->sb_type < SB16) {
+ /* Exception: ESS AudioDrive does not alias port base+0xf */
+ if (!IS_ESS(dsp) || !((a & 0xF) == 0xF)) {
+ a &= 0xfffe;
+ }
+ }
switch (a & 0xf) {
case 0xA: /* Read data */
@@ -1125,6 +1784,21 @@ sb_read(uint16_t a, void *priv)
dsp->busy_count = (dsp->busy_count + 1) & 3;
else
dsp->busy_count = 0;
+ if (IS_ESS(dsp)) {
+ if (dsp->wb_full || (dsp->busy_count & 2)) {
+ dsp->wb_full = timer_is_enabled(&dsp->wb_timer);
+ }
+ uint8_t busy_flag = dsp->wb_full ? 0x80 : 0x00;
+ uint8_t data_rdy = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x40;
+ uint8_t fifo_full = 0; /* Unimplemented */
+ uint8_t fifo_empty = 0; /* (this is for the 256-byte extended mode FIFO, */
+ uint8_t fifo_half = 0; /* not the standard 64-byte FIFO) */
+ uint8_t irq_generic = dsp->ess_irq_generic ? 0x04 : 0x00;
+ uint8_t irq_fifohe = 0; /* Unimplemented (ditto) */
+ uint8_t irq_dmactr = dsp->ess_irq_dmactr ? 0x01 : 0x00;
+
+ return busy_flag | data_rdy | fifo_full | fifo_empty | fifo_half | irq_generic | irq_fifohe | irq_dmactr;
+ }
if (dsp->wb_full || (dsp->busy_count & 2)) {
dsp->wb_full = timer_is_enabled(&dsp->wb_timer);
if (IS_AZTECH(dsp)) {
@@ -1146,6 +1820,7 @@ sb_read(uint16_t a, void *priv)
case 0xE: /* Read data ready */
dsp->irq_update(dsp->irq_priv, 0);
dsp->sb_irq8 = dsp->sb_irq16 = 0;
+ dsp->ess_irq_generic = dsp->ess_irq_dmactr = false;
/* Only bit 7 is defined but aztech diagnostics fail if the others are set. Keep the original behavior to not interfere with what's already working. */
if (IS_AZTECH(dsp)) {
sb_dsp_log("SB Read Data Aztech read %02X, Read RP = %d, Read WP = %d\n", (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80, dsp->sb_read_rp, dsp->sb_read_wp);
@@ -1156,10 +1831,12 @@ sb_read(uint16_t a, void *priv)
}
break;
case 0xF: /* 16-bit ack */
- dsp->sb_irq16 = 0;
- if (!dsp->sb_irq8)
- dsp->irq_update(dsp->irq_priv, 0);
- sb_dsp_log("SB 16-bit ACK read 0xFF\n");
+ if (!IS_ESS(dsp)) {
+ dsp->sb_irq16 = 0;
+ if (!dsp->sb_irq8)
+ dsp->irq_update(dsp->irq_priv, 0);
+ sb_dsp_log("SB 16-bit ACK read 0xFF\n");
+ }
ret = 0xff;
break;
@@ -1189,6 +1866,7 @@ sb_dsp_input_msg(void *priv, uint8_t *msg, uint32_t len)
for (uint32_t i = 0; i < len; i++)
sb_add_data(dsp, msg[i]);
sb_irq(dsp, 1);
+ dsp->ess_irq_generic = true;
} else if (dsp->midi_in_poll) {
for (uint32_t i = 0; i < len; i++)
sb_add_data(dsp, msg[i]);
@@ -1224,6 +1902,15 @@ sb_dsp_input_sysex(void *priv, uint8_t *buffer, uint32_t len, int abort)
return 0;
}
+void
+sb_dsp_irq_poll(void *priv)
+{
+ sb_dsp_t *dsp = (sb_dsp_t *) priv;
+
+ sb_irq(dsp, 1);
+ dsp->ess_irq_generic = true;
+}
+
void
sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent)
{
@@ -1252,15 +1939,32 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent)
timer_add(&dsp->output_timer, pollsb, dsp, 0);
timer_add(&dsp->input_timer, sb_poll_i, dsp, 0);
timer_add(&dsp->wb_timer, NULL, dsp, 0);
+ timer_add(&dsp->irq_timer, sb_dsp_irq_poll, dsp, 0);
- /* Initialise SB16 filter to same cutoff as 8-bit SBs (3.2 kHz). This will be recalculated when
- a set frequency command is sent. */
- recalc_sb16_filter(0, 3200 * 2);
- if (dsp->sb_has_real_opl)
- recalc_sb16_filter(1, FREQ_49716);
+ if (IS_ESS(dsp))
+ /* Initialize ESS filter to 8 kHz. This will be recalculated when a set frequency command is
+ sent. */
+ recalc_sb16_filter(0, 8000 * 2);
else
- recalc_sb16_filter(1, FREQ_48000);
- recalc_sb16_filter(2, FREQ_44100);
+ /* Initialise SB16 filter to same cutoff as 8-bit SBs (3.2 kHz). This will be recalculated when
+ a set frequency command is sent. */
+ recalc_sb16_filter(0, 3200 * 2);
+ if (IS_ESS(dsp) || (dsp->sb_type >= SBPRO2)) {
+ /* OPL3 or dual OPL2 is stereo. */
+ if (dsp->sb_has_real_opl)
+ recalc_opl_filter(1, FREQ_49716 * 2);
+ else
+ recalc_sb16_filter(1, FREQ_48000 * 2);
+ } else {
+ /* OPL2 is mono. */
+ if (dsp->sb_has_real_opl)
+ recalc_opl_filter(1, FREQ_49716);
+ else
+ recalc_sb16_filter(1, FREQ_48000);
+ }
+ /* CD Audio is stereo. */
+ recalc_sb16_filter(2, FREQ_44100 * 2);
+ /* PC speaker is mono. */
recalc_sb16_filter(3, 18939);
/* Initialize SB16 8051 RAM and ASP internal RAM */
@@ -1270,6 +1974,9 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent)
dsp->sb_8051_ram[0x37] = 0x38;
memset(dsp->sb_asp_ram, 0xff, sizeof(dsp->sb_asp_ram));
+
+ dsp->espcm_fifo = fifo64_init();
+ fifo_set_trigger_len(dsp->espcm_fifo, 1);
}
void
@@ -1321,6 +2028,31 @@ sb_dsp_dma_attach(sb_dsp_t *dsp,
dsp->dma_priv = priv;
}
+void
+sb_ess_finish_dma(sb_dsp_t *dsp)
+{
+ if (!dsp->ess_playback_mode)
+ return;
+ ESSreg(0xB8) &= ~0x01;
+ dma_set_drq(dsp->sb_8_dmanum, 0);
+}
+
+void
+sb_espcm_fifoctl_run(sb_dsp_t *dsp)
+{
+ if (fifo_get_empty(dsp->espcm_fifo) && !dsp->sb_8_pause) {
+ while (!fifo_get_full(dsp->espcm_fifo)) {
+ int32_t val;
+ val = dsp->dma_readb(dsp->dma_priv);
+ dsp->ess_dma_counter++;
+ fifo_write(val & 0xff, dsp->espcm_fifo);
+ if (val & DMA_OVER) {
+ break;
+ }
+ }
+ }
+}
+
void
pollsb(void *priv)
{
@@ -1330,197 +2062,433 @@ pollsb(void *priv)
int data[2];
timer_advance_u64(&dsp->output_timer, dsp->sblatcho);
- if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && dsp->sb_8_output) {
+ if (dsp->sb_8_enable && dsp->sb_pausetime < 0 && dsp->sb_8_output) {
sb_dsp_update(dsp);
switch (dsp->sb_8_format) {
case 0x00: /* Mono unsigned */
- data[0] = dsp->dma_readb(dsp->dma_priv);
- /* Needed to prevent clicking in Worms, which programs the DSP to
- auto-init DMA but programs the DMA controller to single cycle */
- if (data[0] == DMA_NODATA)
- break;
- dsp->sbdat = (data[0] ^ 0x80) << 8;
- if (dsp->stereo) {
- sb_dsp_log("pollsb: Mono unsigned, dsp->stereo, %s channel, %04X\n",
- dsp->sbleftright ? "left" : "right", dsp->sbdat);
- if (dsp->sbleftright)
- dsp->sbdatl = dsp->sbdat;
- else
- dsp->sbdatr = dsp->sbdat;
- dsp->sbleftright = !dsp->sbleftright;
- } else
- dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
- dsp->sb_8_length--;
+ if (!dsp->sb_8_pause) {
+ data[0] = dsp->dma_readb(dsp->dma_priv);
+ /* Needed to prevent clicking in Worms, which programs the DSP to
+ auto-init DMA but programs the DMA controller to single cycle */
+ if (data[0] == DMA_NODATA)
+ break;
+ dsp->sbdat = (data[0] ^ 0x80) << 8;
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: Mono unsigned, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
break;
case 0x10: /* Mono signed */
- data[0] = dsp->dma_readb(dsp->dma_priv);
- if (data[0] == DMA_NODATA)
- break;
- dsp->sbdat = data[0] << 8;
- if (dsp->stereo) {
- sb_dsp_log("pollsb: Mono signed, dsp->stereo, %s channel, %04X\n",
- dsp->sbleftright ? "left" : "right", data[0], dsp->sbdat);
- if (dsp->sbleftright)
- dsp->sbdatl = dsp->sbdat;
- else
- dsp->sbdatr = dsp->sbdat;
- dsp->sbleftright = !dsp->sbleftright;
- } else
- dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
- dsp->sb_8_length--;
+ if (!dsp->sb_8_pause) {
+ data[0] = dsp->dma_readb(dsp->dma_priv);
+ if (data[0] == DMA_NODATA)
+ break;
+ dsp->sbdat = data[0] << 8;
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: Mono signed, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", data[0], dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
break;
case 0x20: /* Stereo unsigned */
- data[0] = dsp->dma_readb(dsp->dma_priv);
- data[1] = dsp->dma_readb(dsp->dma_priv);
- if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA))
- break;
- dsp->sbdatl = (data[0] ^ 0x80) << 8;
- dsp->sbdatr = (data[1] ^ 0x80) << 8;
- dsp->sb_8_length -= 2;
+ if (!dsp->sb_8_pause) {
+ data[0] = dsp->dma_readb(dsp->dma_priv);
+ data[1] = dsp->dma_readb(dsp->dma_priv);
+ if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA))
+ break;
+ dsp->sbdatl = (data[0] ^ 0x80) << 8;
+ dsp->sbdatr = (data[1] ^ 0x80) << 8;
+ dsp->sb_8_length -= 2;
+ dsp->ess_dma_counter += 2;
+ }
break;
case 0x30: /* Stereo signed */
- data[0] = dsp->dma_readb(dsp->dma_priv);
- data[1] = dsp->dma_readb(dsp->dma_priv);
- if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA))
- break;
- dsp->sbdatl = data[0] << 8;
- dsp->sbdatr = data[1] << 8;
- dsp->sb_8_length -= 2;
+ if (!dsp->sb_8_pause) {
+ data[0] = dsp->dma_readb(dsp->dma_priv);
+ data[1] = dsp->dma_readb(dsp->dma_priv);
+ if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA))
+ break;
+ dsp->sbdatl = data[0] << 8;
+ dsp->sbdatr = data[1] << 8;
+ dsp->sb_8_length -= 2;
+ dsp->ess_dma_counter += 2;
+ }
break;
case ADPCM_4:
- if (dsp->sbdacpos)
- tempi = (dsp->sbdat2 & 0xF) + dsp->sbstep;
- else
- tempi = (dsp->sbdat2 >> 4) + dsp->sbstep;
- if (tempi < 0)
- tempi = 0;
- if (tempi > 63)
- tempi = 63;
-
- ref = dsp->sbref + scaleMap4[tempi];
- if (ref > 0xff)
- dsp->sbref = 0xff;
- else if (ref < 0x00)
- dsp->sbref = 0x00;
- else
- dsp->sbref = ref;
-
- dsp->sbstep = (dsp->sbstep + adjustMap4[tempi]) & 0xff;
- dsp->sbdat = (dsp->sbref ^ 0x80) << 8;
-
- dsp->sbdacpos++;
-
- if (dsp->sbdacpos >= 2) {
- dsp->sbdacpos = 0;
- dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
- dsp->sb_8_length--;
- }
-
- if (dsp->stereo) {
- sb_dsp_log("pollsb: ADPCM 4, dsp->stereo, %s channel, %04X\n",
- dsp->sbleftright ? "left" : "right", dsp->sbdat);
- if (dsp->sbleftright)
- dsp->sbdatl = dsp->sbdat;
+ if (!dsp->sb_8_pause) {
+ if (dsp->sbdacpos)
+ tempi = (dsp->sbdat2 & 0xF) + dsp->sbstep;
else
- dsp->sbdatr = dsp->sbdat;
- dsp->sbleftright = !dsp->sbleftright;
- } else
- dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ tempi = (dsp->sbdat2 >> 4) + dsp->sbstep;
+ if (tempi < 0)
+ tempi = 0;
+ if (tempi > 63)
+ tempi = 63;
+
+ ref = dsp->sbref + scaleMap4[tempi];
+ if (ref > 0xff)
+ dsp->sbref = 0xff;
+ else if (ref < 0x00)
+ dsp->sbref = 0x00;
+ else
+ dsp->sbref = ref;
+
+ dsp->sbstep = (dsp->sbstep + adjustMap4[tempi]) & 0xff;
+ dsp->sbdat = (dsp->sbref ^ 0x80) << 8;
+
+ dsp->sbdacpos++;
+
+ if (dsp->sbdacpos >= 2) {
+ dsp->sbdacpos = 0;
+ dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
+
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: ADPCM 4, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ }
break;
case ADPCM_26:
- if (!dsp->sbdacpos)
- tempi = (dsp->sbdat2 >> 5) + dsp->sbstep;
- else if (dsp->sbdacpos == 1)
- tempi = ((dsp->sbdat2 >> 2) & 7) + dsp->sbstep;
- else
- tempi = ((dsp->sbdat2 << 1) & 7) + dsp->sbstep;
-
- if (tempi < 0)
- tempi = 0;
- if (tempi > 39)
- tempi = 39;
-
- ref = dsp->sbref + scaleMap26[tempi];
- if (ref > 0xff)
- dsp->sbref = 0xff;
- else if (ref < 0x00)
- dsp->sbref = 0x00;
- else
- dsp->sbref = ref;
- dsp->sbstep = (dsp->sbstep + adjustMap26[tempi]) & 0xff;
-
- dsp->sbdat = (dsp->sbref ^ 0x80) << 8;
-
- dsp->sbdacpos++;
- if (dsp->sbdacpos >= 3) {
- dsp->sbdacpos = 0;
- dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
- dsp->sb_8_length--;
- }
-
- if (dsp->stereo) {
- sb_dsp_log("pollsb: ADPCM 26, dsp->stereo, %s channel, %04X\n",
- dsp->sbleftright ? "left" : "right", dsp->sbdat);
- if (dsp->sbleftright)
- dsp->sbdatl = dsp->sbdat;
+ if (!dsp->sb_8_pause) {
+ if (!dsp->sbdacpos)
+ tempi = (dsp->sbdat2 >> 5) + dsp->sbstep;
+ else if (dsp->sbdacpos == 1)
+ tempi = ((dsp->sbdat2 >> 2) & 7) + dsp->sbstep;
else
- dsp->sbdatr = dsp->sbdat;
- dsp->sbleftright = !dsp->sbleftright;
- } else
- dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ tempi = ((dsp->sbdat2 << 1) & 7) + dsp->sbstep;
+
+ if (tempi < 0)
+ tempi = 0;
+ if (tempi > 39)
+ tempi = 39;
+
+ ref = dsp->sbref + scaleMap26[tempi];
+ if (ref > 0xff)
+ dsp->sbref = 0xff;
+ else if (ref < 0x00)
+ dsp->sbref = 0x00;
+ else
+ dsp->sbref = ref;
+ dsp->sbstep = (dsp->sbstep + adjustMap26[tempi]) & 0xff;
+
+ dsp->sbdat = (dsp->sbref ^ 0x80) << 8;
+
+ dsp->sbdacpos++;
+ if (dsp->sbdacpos >= 3) {
+ dsp->sbdacpos = 0;
+ dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
+
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: ADPCM 26, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ }
break;
case ADPCM_2:
- tempi = ((dsp->sbdat2 >> ((3 - dsp->sbdacpos) * 2)) & 3) + dsp->sbstep;
- if (tempi < 0)
- tempi = 0;
- if (tempi > 23)
- tempi = 23;
+ if (!dsp->sb_8_pause) {
+ tempi = ((dsp->sbdat2 >> ((3 - dsp->sbdacpos) * 2)) & 3) + dsp->sbstep;
+ if (tempi < 0)
+ tempi = 0;
+ if (tempi > 23)
+ tempi = 23;
- ref = dsp->sbref + scaleMap2[tempi];
- if (ref > 0xff)
- dsp->sbref = 0xff;
- else if (ref < 0x00)
- dsp->sbref = 0x00;
- else
- dsp->sbref = ref;
- dsp->sbstep = (dsp->sbstep + adjustMap2[tempi]) & 0xff;
+ ref = dsp->sbref + scaleMap2[tempi];
+ if (ref > 0xff)
+ dsp->sbref = 0xff;
+ else if (ref < 0x00)
+ dsp->sbref = 0x00;
+ else
+ dsp->sbref = ref;
+ dsp->sbstep = (dsp->sbstep + adjustMap2[tempi]) & 0xff;
- dsp->sbdat = (dsp->sbref ^ 0x80) << 8;
+ dsp->sbdat = (dsp->sbref ^ 0x80) << 8;
- dsp->sbdacpos++;
- if (dsp->sbdacpos >= 4) {
- dsp->sbdacpos = 0;
- dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
+ dsp->sbdacpos++;
+ if (dsp->sbdacpos >= 4) {
+ dsp->sbdacpos = 0;
+ dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
+
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: ADPCM 2, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ }
+ break;
+
+ case ESPCM_4:
+ if (dsp->espcm_sample_idx >= 19) {
+ dsp->espcm_sample_idx = 0;
+ }
+ if (dsp->espcm_sample_idx == 0) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
+
+ dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F;
+ tempi = dsp->espcm_byte_buffer[0] >> 4;
+ } else if (dsp->espcm_sample_idx & 1) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
+ dsp->sb_8_length--;
+
+ tempi = dsp->espcm_byte_buffer[0] & 0x0F;
+ } else {
+ tempi = dsp->espcm_byte_buffer[0] >> 4;
}
+ if (dsp->espcm_sample_idx == 18) {
+ dsp->sb_8_length--;
+ }
+
+ dsp->espcm_sample_idx++;
+
+ tempi |= (dsp->espcm_range << 4);
+ data[0] = espcm_range_map[tempi];
+ dsp->sbdat = data[0] << 8;
if (dsp->stereo) {
- sb_dsp_log("pollsb: ADPCM 2, dsp->stereo, %s channel, %04X\n",
+ sb_dsp_log("pollsb: ESPCM 4, dsp->stereo, %s channel, %04X\n",
dsp->sbleftright ? "left" : "right", dsp->sbdat);
if (dsp->sbleftright)
dsp->sbdatl = dsp->sbdat;
else
dsp->sbdatr = dsp->sbdat;
dsp->sbleftright = !dsp->sbleftright;
- } else
+ } else {
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ }
+ break;
+
+ case ESPCM_3:
+ if (dsp->espcm_sample_idx >= 19) {
+ dsp->espcm_sample_idx = 0;
+ }
+ if (dsp->espcm_sample_idx == 0) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
+
+ dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F;
+ tempi = dsp->espcm_byte_buffer[0] >> 4;
+ dsp->espcm_last_value = tempi;
+ } else if (dsp->espcm_sample_idx == 1) {
+ for (tempi = 0; tempi < 4; tempi++) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[tempi] = fifo_read(dsp->espcm_fifo);
+ dsp->sb_8_length--;
+ }
+ if (tempi < 4) {
+ break;
+ }
+
+ dsp->espcm_table_index = dsp->espcm_byte_buffer[0] & 0x03;
+
+ dsp->espcm_code_buffer[0] = (dsp->espcm_byte_buffer[0] >> 2) & 0x07;
+ dsp->espcm_code_buffer[1] = (dsp->espcm_byte_buffer[0] >> 5) & 0x07;
+ dsp->espcm_code_buffer[2] = (dsp->espcm_byte_buffer[1]) & 0x07;
+ dsp->espcm_code_buffer[3] = (dsp->espcm_byte_buffer[1] >> 3) & 0x07;
+ dsp->espcm_code_buffer[4] = ((dsp->espcm_byte_buffer[1] >> 6) & 0x03) | ((dsp->espcm_byte_buffer[2] & 0x01) << 2);
+ dsp->espcm_code_buffer[5] = (dsp->espcm_byte_buffer[2] >> 1) & 0x07;
+ dsp->espcm_code_buffer[6] = (dsp->espcm_byte_buffer[2] >> 4) & 0x07;
+ dsp->espcm_code_buffer[7] = ((dsp->espcm_byte_buffer[2] >> 7) & 0x01) | ((dsp->espcm_byte_buffer[3] & 0x03) << 1);
+ dsp->espcm_code_buffer[8] = (dsp->espcm_byte_buffer[3] >> 2) & 0x07;
+ dsp->espcm_code_buffer[9] = (dsp->espcm_byte_buffer[3] >> 5) & 0x07;
+
+ tempi = (dsp->espcm_table_index << 8) | (dsp->espcm_last_value << 3) | dsp->espcm_code_buffer[0];
+ tempi = espcm3_dpcm_tables[tempi];
+ dsp->espcm_last_value = tempi;
+ } else if (dsp->espcm_sample_idx == 11) {
+ for (tempi = 1; tempi < 4; tempi++) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[tempi] = fifo_read(dsp->espcm_fifo);
+ dsp->sb_8_length--;
+ }
+ if (tempi < 4) {
+ break;
+ }
+
+ dsp->espcm_code_buffer[0] = (dsp->espcm_byte_buffer[1]) & 0x07;
+ dsp->espcm_code_buffer[1] = (dsp->espcm_byte_buffer[1] >> 3) & 0x07;
+ dsp->espcm_code_buffer[2] = ((dsp->espcm_byte_buffer[1] >> 6) & 0x03) | ((dsp->espcm_byte_buffer[2] & 0x01) << 2);
+ dsp->espcm_code_buffer[3] = (dsp->espcm_byte_buffer[2] >> 1) & 0x07;
+ dsp->espcm_code_buffer[4] = (dsp->espcm_byte_buffer[2] >> 4) & 0x07;
+ dsp->espcm_code_buffer[5] = ((dsp->espcm_byte_buffer[2] >> 7) & 0x01) | ((dsp->espcm_byte_buffer[3] & 0x03) << 1);
+ dsp->espcm_code_buffer[6] = (dsp->espcm_byte_buffer[3] >> 2) & 0x07;
+ dsp->espcm_code_buffer[7] = (dsp->espcm_byte_buffer[3] >> 5) & 0x07;
+
+ tempi = (dsp->espcm_table_index << 8) | (dsp->espcm_last_value << 3) | dsp->espcm_code_buffer[0];
+ tempi = espcm3_dpcm_tables[tempi];
+ dsp->espcm_last_value = tempi;
+ } else {
+ tempi = (dsp->espcm_table_index << 8) | (dsp->espcm_last_value << 3) | dsp->espcm_code_buffer[(dsp->espcm_sample_idx - 1) % 10];
+ tempi = espcm3_dpcm_tables[tempi];
+ dsp->espcm_last_value = tempi;
+ }
+
+ if (dsp->espcm_sample_idx == 18) {
+ dsp->sb_8_length--;
+ }
+
+ dsp->espcm_sample_idx++;
+
+ tempi |= (dsp->espcm_range << 4);
+ data[0] = espcm_range_map[tempi];
+ dsp->sbdat = data[0] << 8;
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: ESPCM 3, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else {
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ }
+ break;
+
+ case ESPCM_1:
+ if (dsp->espcm_sample_idx >= 19) {
+ dsp->espcm_sample_idx = 0;
+ }
+ if (dsp->espcm_sample_idx == 0) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
+
+ dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F;
+ dsp->espcm_byte_buffer[0] >>= 5;
+ tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4;
+ dsp->espcm_byte_buffer[0] >>= 1;
+ } else if ((dsp->espcm_sample_idx == 3) | (dsp->espcm_sample_idx == 11)) {
+ sb_espcm_fifoctl_run(dsp);
+ if (fifo_get_empty(dsp->espcm_fifo)) {
+ break;
+ }
+ dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
+ dsp->sb_8_length--;
+
+ tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4;
+ dsp->espcm_byte_buffer[0] >>= 1;
+ } else {
+ tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4;
+ dsp->espcm_byte_buffer[0] >>= 1;
+ }
+
+ if (dsp->espcm_sample_idx == 18) {
+ dsp->sb_8_length--;
+ }
+
+ dsp->espcm_sample_idx++;
+
+ tempi |= (dsp->espcm_range << 4);
+ data[0] = espcm_range_map[tempi];
+ dsp->sbdat = data[0] << 8;
+ if (dsp->stereo) {
+ sb_dsp_log("pollsb: ESPCM 1, dsp->stereo, %s channel, %04X\n",
+ dsp->sbleftright ? "left" : "right", dsp->sbdat);
+ if (dsp->sbleftright)
+ dsp->sbdatl = dsp->sbdat;
+ else
+ dsp->sbdatr = dsp->sbdat;
+ dsp->sbleftright = !dsp->sbleftright;
+ } else {
+ dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
+ }
break;
default:
break;
}
- if (dsp->sb_8_length < 0) {
+ if (dsp->sb_8_length < 0 && !dsp->ess_playback_mode) {
if (dsp->sb_8_autoinit)
dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen;
else {
dsp->sb_8_enable = 0;
timer_disable(&dsp->output_timer);
+ sb_ess_finish_dma(dsp);
}
sb_irq(dsp, 1);
+ dsp->ess_irq_generic = true;
+ }
+ if (dsp->ess_dma_counter > 0xffff) {
+ if (dsp->ess_playback_mode) {
+ if (!dsp->sb_8_autoinit) {
+ dsp->sb_8_enable = 0;
+ timer_disable(&dsp->output_timer);
+ sb_ess_finish_dma(dsp);
+ }
+ if (ESSreg(0xB1) & 0x40) {
+ sb_irq(dsp, 1);
+ dsp->ess_irq_dmactr = true;
+ }
+ }
+ uint32_t temp = dsp->ess_dma_counter & 0xffff;
+ dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
+ dsp->ess_dma_counter += temp;
}
}
if (dsp->sb_16_enable && !dsp->sb_16_pause && (dsp->sb_pausetime < 0LL) && dsp->sb_16_output) {
@@ -1533,6 +2501,7 @@ pollsb(void *priv)
break;
dsp->sbdatl = dsp->sbdatr = data[0] ^ 0x8000;
dsp->sb_16_length--;
+ dsp->ess_dma_counter += 2;
break;
case 0x10: /* Mono signed */
data[0] = dsp->dma_readw(dsp->dma_priv);
@@ -1540,6 +2509,7 @@ pollsb(void *priv)
break;
dsp->sbdatl = dsp->sbdatr = data[0];
dsp->sb_16_length--;
+ dsp->ess_dma_counter += 2;
break;
case 0x20: /* Stereo unsigned */
data[0] = dsp->dma_readw(dsp->dma_priv);
@@ -1549,6 +2519,7 @@ pollsb(void *priv)
dsp->sbdatl = data[0] ^ 0x8000;
dsp->sbdatr = data[1] ^ 0x8000;
dsp->sb_16_length -= 2;
+ dsp->ess_dma_counter += 4;
break;
case 0x30: /* Stereo signed */
data[0] = dsp->dma_readw(dsp->dma_priv);
@@ -1558,27 +2529,47 @@ pollsb(void *priv)
dsp->sbdatl = data[0];
dsp->sbdatr = data[1];
dsp->sb_16_length -= 2;
+ dsp->ess_dma_counter += 4;
break;
default:
break;
}
- if (dsp->sb_16_length < 0) {
+ if (dsp->sb_16_length < 0 && !dsp->ess_playback_mode) {
sb_dsp_log("16DMA over %i\n", dsp->sb_16_autoinit);
if (dsp->sb_16_autoinit)
dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen;
else {
dsp->sb_16_enable = 0;
timer_disable(&dsp->output_timer);
+ sb_ess_finish_dma(dsp);
}
sb_irq(dsp, 0);
+ dsp->ess_irq_generic = true;
+ }
+ if (dsp->ess_dma_counter > 0xffff) {
+ if (dsp->ess_playback_mode) {
+ if (!dsp->sb_16_autoinit) {
+ dsp->sb_16_enable = 0;
+ timer_disable(&dsp->output_timer);
+ sb_ess_finish_dma(dsp);
+ }
+ if (ESSreg(0xB1) & 0x40) {
+ sb_irq(dsp, 0);
+ dsp->ess_irq_dmactr = true;
+ }
+ }
+ uint32_t temp = dsp->ess_dma_counter & 0xffff;
+ dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
+ dsp->ess_dma_counter += temp;
}
}
if (dsp->sb_pausetime > -1) {
dsp->sb_pausetime--;
if (dsp->sb_pausetime < 0) {
sb_irq(dsp, 1);
+ dsp->ess_irq_generic = true;
if (!dsp->sb_8_enable)
timer_disable(&dsp->output_timer);
sb_dsp_log("SB pause over\n");
@@ -1599,12 +2590,14 @@ sb_poll_i(void *priv)
case 0x00: /* Mono unsigned As the manual says, only the left channel is recorded */
dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80);
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
case 0x10: /* Mono signed As the manual says, only the left channel is recorded */
dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8));
dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
@@ -1612,6 +2605,7 @@ sb_poll_i(void *priv)
dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80);
dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80);
dsp->sb_8_length -= 2;
+ dsp->ess_dma_counter += 2;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
@@ -1619,22 +2613,111 @@ sb_poll_i(void *priv)
dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8));
dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8));
dsp->sb_8_length -= 2;
+ dsp->ess_dma_counter += 2;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
+ case ESPCM_4E:
+ // I assume the real hardware double-buffers the blocks or something like that.
+ // We're not gonna do that here.
+ dsp->espcm_sample_buffer[dsp->espcm_sample_idx] = dsp->record_buffer[dsp->record_pos_read] >> 8;
+ dsp->espcm_sample_idx++;
+ dsp->record_pos_read += 2;
+ dsp->record_pos_read &= 0xFFFF;
+ if (dsp->espcm_sample_idx >= 19) {
+ int i, bit, table_addr, sigma, last_sigma;
+ int8_t min_sample = 127, max_sample = -128, s;
+ uint8_t b;
+
+ for (i = 0; i < 19; i++) {
+ s = dsp->espcm_sample_buffer[i];
+ if (s < min_sample) {
+ min_sample = s;
+ }
+ if (s > max_sample) {
+ max_sample = s;
+ }
+ }
+ if (min_sample < 0) {
+ min_sample = -min_sample;
+ }
+ if (max_sample < 0) {
+ max_sample = -max_sample;
+ }
+ if (min_sample > max_sample) {
+ max_sample = min_sample;
+ }
+
+ for (table_addr = 15; table_addr < 256; table_addr += 16) {
+ if (max_sample <= espcm_range_map[table_addr]) {
+ break;
+ }
+ }
+ dsp->espcm_range = table_addr >> 4;
+
+ for (i = 0; i < 19; i++) {
+ table_addr = dsp->espcm_range << 4;
+ last_sigma = 9999;
+ s = dsp->espcm_sample_buffer[i];
+ for (; (table_addr >> 4) == dsp->espcm_range; table_addr++) {
+ sigma = espcm_range_map[table_addr] - s;
+ if (sigma < 0) {
+ sigma = -sigma;
+ }
+ if (sigma > last_sigma) {
+ break;
+ }
+ last_sigma = sigma;
+ }
+ table_addr--;
+ dsp->espcm_code_buffer[i] = table_addr & 0x0F;
+ }
+
+ b = dsp->espcm_range | (dsp->espcm_code_buffer[0] << 4);
+ dsp->dma_writeb(dsp->dma_priv, b);
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+
+ for (i = 1; i < 10; i++) {
+ b = dsp->espcm_code_buffer[i * 2 - 1] | (dsp->espcm_code_buffer[i * 2] << 4);
+ dsp->dma_writeb(dsp->dma_priv, b);
+ dsp->sb_8_length--;
+ dsp->ess_dma_counter++;
+ }
+
+ dsp->espcm_sample_idx = 0;
+ }
default:
break;
}
- if (dsp->sb_8_length < 0) {
+ if (dsp->sb_8_length < 0 && !dsp->ess_playback_mode) {
if (dsp->sb_8_autoinit)
dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen;
else {
dsp->sb_8_enable = 0;
timer_disable(&dsp->input_timer);
+ sb_ess_finish_dma(dsp);
}
sb_irq(dsp, 1);
+ dsp->ess_irq_generic = true;
+ }
+ if (dsp->ess_dma_counter > 0xffff) {
+ if (dsp->ess_playback_mode) {
+ if (!dsp->sb_8_autoinit) {
+ dsp->sb_8_enable = 0;
+ timer_disable(&dsp->input_timer);
+ sb_ess_finish_dma(dsp);
+ }
+ if (ESSreg(0xB1) & 0x40) {
+ sb_irq(dsp, 1);
+ dsp->ess_irq_dmactr = true;
+ }
+ }
+ uint32_t temp = dsp->ess_dma_counter & 0xffff;
+ dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
+ dsp->ess_dma_counter += temp;
}
processed = 1;
}
@@ -1644,6 +2727,7 @@ sb_poll_i(void *priv)
if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000))
return;
dsp->sb_16_length--;
+ dsp->ess_dma_counter += 2;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
@@ -1651,6 +2735,7 @@ sb_poll_i(void *priv)
if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read]))
return;
dsp->sb_16_length--;
+ dsp->ess_dma_counter += 2;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
@@ -1659,6 +2744,7 @@ sb_poll_i(void *priv)
return;
dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000);
dsp->sb_16_length -= 2;
+ dsp->ess_dma_counter += 4;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
@@ -1667,6 +2753,7 @@ sb_poll_i(void *priv)
return;
dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1]);
dsp->sb_16_length -= 2;
+ dsp->ess_dma_counter += 4;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
@@ -1675,14 +2762,32 @@ sb_poll_i(void *priv)
break;
}
- if (dsp->sb_16_length < 0) {
+ if (dsp->sb_16_length < 0 && !dsp->ess_playback_mode) {
if (dsp->sb_16_autoinit)
dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen;
else {
dsp->sb_16_enable = 0;
timer_disable(&dsp->input_timer);
+ sb_ess_finish_dma(dsp);
}
sb_irq(dsp, 0);
+ dsp->ess_irq_generic = true;
+ }
+ if (dsp->ess_dma_counter > 0xffff) {
+ if (dsp->ess_playback_mode) {
+ if (!dsp->sb_16_autoinit) {
+ dsp->sb_16_enable = 0;
+ timer_disable(&dsp->input_timer);
+ sb_ess_finish_dma(dsp);
+ }
+ if (ESSreg(0xB1) & 0x40) {
+ sb_irq(dsp, 0);
+ dsp->ess_irq_dmactr = true;
+ }
+ }
+ uint32_t temp = dsp->ess_dma_counter & 0xffff;
+ dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
+ dsp->ess_dma_counter += temp;
}
processed = 1;
}
diff --git a/src/sound/sound.c b/src/sound/sound.c
index 50eb984d6..9acf99460 100644
--- a/src/sound/sound.c
+++ b/src/sound/sound.c
@@ -163,6 +163,7 @@ static const SOUND_CARD sound_cards[] = {
{ &es1371_device },
{ &ad1881_device },
{ &cs4297a_device },
+ { &ess_1688_device },
{ NULL }
// clang-format on
};