From 2fd511cc585568a5eb02daf16ee980db93e67dec Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Thu, 29 Feb 2024 21:31:16 +0600 Subject: [PATCH 01/66] Add ESFMu for ESFM emulation --- src/cpu/cpu.h | 7 + src/sound/CMakeLists.txt | 2 +- src/sound/esfmu/esfm.c | 2316 ++++++++++++++++++++++++++++++ src/sound/esfmu/esfm.h | 289 ++++ src/sound/esfmu/esfm_registers.c | 999 +++++++++++++ src/sound/snd_opl_esfm.c | 223 +++ 6 files changed, 3835 insertions(+), 1 deletion(-) create mode 100644 src/sound/esfmu/esfm.c create mode 100644 src/sound/esfmu/esfm.h create mode 100644 src/sound/esfmu/esfm_registers.c create mode 100644 src/sound/snd_opl_esfm.c diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 16a9eba10..f2e1242df 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -21,7 +21,14 @@ #ifndef EMU_CPU_H #define EMU_CPU_H +#ifndef NO_SOFTFLOAT_INCLUDE #include "softfloat/softfloat.h" +#else +typedef struct floatx80 { // leave alignment to compiler + uint64_t exp; + uint16_t fraction; +} floatx80; +#endif enum { FPU_NONE, diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index 72f05ff6d..9b2cc1416 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_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..7409603fe --- /dev/null +++ b/src/sound/esfmu/esfm.c @@ -0,0 +1,2316 @@ +/* + * 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; + } +} + +/* ------------------------------------------------------------------------- */ +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__) + 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__) + asm ( + "movzbl %b[wave], %%eax \n\t" + "shll $11, %%eax \n\t" + "leal %[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" + "leal %[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), + [exprom] "m" (exprom), + [i] "m" (iter_counter) + : "cc", "ax", "bx", "cx", "di" + ); +#elif defined(__GNUC__) && defined(__arm__) + 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->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, int16_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] = ESFM_clip_sample(chip->output_accm[0]); + buf[1] = ESFM_clip_sample(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; + + for (i = 0; i < num_samples; i++) + { + ESFM_generate(chip, sndptr); + sndptr += 2; + } +} diff --git a/src/sound/esfmu/esfm.h b/src/sound/esfmu/esfm.h new file mode 100644 index 000000000..41ac6983f --- /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, int16_t *buf); +void ESFM_generate_stream(esfm_chip *chip, int16_t *sndptr, uint32_t num_samples); +int16_t ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx); + + +// These are fake types just for syntax sugar. +// Beware of their underlying types when reading/writing to them. +typedef uint8_t flag; +typedef uint8_t uint2; +typedef uint8_t uint3; +typedef uint8_t uint4; +typedef uint8_t uint5; +typedef uint8_t uint6; +typedef uint8_t uint8; +typedef uint16_t uint9; +typedef uint16_t uint10; +typedef uint16_t uint11; +typedef uint16_t uint12; +typedef uint16_t uint16; +typedef uint32_t uint19; +typedef uint32_t uint23; +typedef uint32_t uint32; +typedef uint64_t uint36; + +typedef int16_t int13; +typedef int16_t int14; +typedef int16_t int16; +typedef int32_t int32; + +enum eg_states +{ + EG_ATTACK, + EG_DECAY, + EG_SUSTAIN, + EG_RELEASE +}; + + +typedef struct _esfm_write_buf +{ + uint64_t timestamp; + uint16_t address; + uint8_t data; + flag valid; + +} esfm_write_buf; + +typedef struct _emu_slot_channel_mapping +{ + int channel_idx; + int slot_idx; + +} emu_slot_channel_mapping; + +typedef struct _esfm_slot_internal +{ + uint9 eg_position; + uint9 eg_ksl_offset; + uint10 eg_output; + + uint4 keyscale; + + int13 output; + int13 emu_output_enable; + int13 emu_mod_enable; + int13 feedback_buf; + int13 *mod_input; + + uint19 phase_acc; + uint10 phase_out; + flag phase_reset; + flag *key_on; + flag key_on_gate; + + uint2 eg_state; + flag eg_delay_run; + flag eg_delay_transitioned_10; + flag eg_delay_transitioned_10_gate; + flag eg_delay_transitioned_01; + flag eg_delay_transitioned_01_gate; + uint16 eg_delay_counter; + uint16 eg_delay_counter_compare; + +} esfm_slot_internal; + +struct _esfm_slot +{ + // Metadata + esfm_channel *channel; + esfm_chip *chip; + uint2 slot_idx; + + // Register data + int13 out_enable[2]; + uint10 f_num; + uint3 block; + uint3 output_level; + // a.k.a. feedback level in emu mode + uint3 mod_in_level; + + uint6 t_level; + uint4 mult; + uint3 waveform; + // Only for 4th slot + uint2 rhy_noise; + + uint4 attack_rate; + uint4 decay_rate; + uint4 sustain_lvl; + uint4 release_rate; + + flag tremolo_en; + flag tremolo_deep; + flag vibrato_en; + flag vibrato_deep; + flag emu_connection_typ; + flag env_sustaining; + flag ksr; + uint2 ksl; + uint3 env_delay; + // overlaps with env_delay bit 0 + // TODO: check if emu mode only uses this, or if it actually overwrites the channel field used by native mode + flag emu_key_on; + + // Internal state + esfm_slot_internal in; +}; + +struct _esfm_channel +{ + esfm_chip *chip; + esfm_slot slots[4]; + uint5 channel_idx; + int16 output[2]; + flag key_on; + flag emu_mode_4op_enable; + // Only for 17th and 18th channels + flag key_on_2; + flag emu_mode_4op_enable_2; +}; + +#define ESFM_WRITEBUF_SIZE 1024 +#define ESFM_WRITEBUF_DELAY 2 + +struct _esfm_chip +{ + esfm_channel channels[18]; + int32 output_accm[2]; + uint16 addr_latch; + + flag emu_wavesel_enable; + flag emu_newmode; + flag native_mode; + + flag keyscale_mode; + + // Global state + uint36 eg_timer; + uint10 global_timer; + uint8 eg_clocks; + flag eg_tick; + flag eg_timer_overflow; + uint8 tremolo; + uint8 tremolo_pos; + uint8 vibrato_pos; + uint23 lfsr; + + flag rm_hh_bit2; + flag rm_hh_bit3; + flag rm_hh_bit7; + flag rm_hh_bit8; + flag rm_tc_bit3; + flag rm_tc_bit5; + + // 0xbd register in emulation mode, exposed in 0x4bd in native mode + // ("bass drum" register) + uint8 emu_rhy_mode_flags; + + flag emu_vibrato_deep; + flag emu_tremolo_deep; + + double timer_accumulator[2]; + uint8 timer_reload[2]; + uint8 timer_counter[2]; + flag timer_enable[2]; + flag timer_mask[2]; + flag timer_overflow[2]; + flag irq_bit; + + // -- Test bits (NOT IMPLEMENTED) -- + // Halts the envelope generators from advancing. Written on bit 0, read back from bit 5. + flag 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!) + */ + flag test_bit_1_distort; + // Seems to do nothing. + flag test_bit_2; + // Seems to do nothing. + flag test_bit_3; + // Appears to attenuate the output by about 3 dB. + flag test_bit_4_attenuate; + // Written on bit 5, read back from bit 0. Seems to do nothing. + flag test_bit_w5_r0; + // Resets all phase generators and holds them in the reset state while this bit is set. + flag test_bit_6_phase_stop_reset; + // Seems to do nothing. + flag 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..34ce8b19e --- /dev/null +++ b/src/sound/esfmu/esfm_registers.c @@ -0,0 +1,999 @@ +/* + * 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; + break; + case TIMER2_REG: + chip->timer_reload[1] = data; + break; + case TIMER_SETUP_REG: + if (data & 0x80) + { + chip->timer_overflow[0] = 0; + chip->timer_overflow[1] = 0; + chip->irq_bit = 0; + } + chip->timer_enable[0] = (data & 0x01) != 0; + chip->timer_enable[1] = (data & 0x02) != 0; + chip->timer_mask[0] = (data & 0x20) != 0; + chip->timer_mask[1] = (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_reload[0]; + break; + case TIMER2_REG: + data = chip->timer_reload[1]; + break; + case TIMER_SETUP_REG: + data |= chip->timer_enable[0] != 0; + data |= (chip->timer_enable[1] != 0) << 1; + data |= (chip->timer_mask[0] != 0) << 5; + data |= (chip->timer_mask[1] != 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; + break; + case 0x03: + chip->timer_reload[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; + break; + case 0x03: + chip->timer_reload[1] = data; + break; + case 0x04: + chip->timer_enable[0] = data & 0x01; + chip->timer_enable[1] = (data & 0x02) != 0; + chip->timer_mask[0] = (data & 0x20) != 0; + chip->timer_mask[1] = (data & 0x40) != 0; + if (data & 0x80) + { + chip->irq_bit = 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_esfm.c b/src/sound/snd_opl_esfm.c new file mode 100644 index 000000000..f7372730c --- /dev/null +++ b/src/sound/snd_opl_esfm.c @@ -0,0 +1,223 @@ +/* + * 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 +#define NO_SOFTFLOAT_INCLUDE +#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 + +enum { + FLAG_CYCLES = 0x02, + FLAG_OPL3 = 0x01 +}; + +typedef struct { + esfm_chip opl; + int8_t flags; + int8_t pad; + + uint16_t port; + uint8_t status; + uint8_t timer_ctrl; + uint16_t timer_count[2]; + uint16_t timer_cur_count[2]; + + // OPL3L + int32_t rateratio; + int32_t samplecnt; + int32_t oldsamples[2]; + int32_t samples[2]; + + int pos; + int32_t buffer[SOUNDBUFLEN * 2]; +} esfm_drv_t; + +void +esfm_drv_generate_resampled(esfm_drv_t *dev, int32_t *bufp) +{ + while (dev->samplecnt >= dev->rateratio) { + int16_t samples[2] = { 0, 0 }; + dev->oldsamples[0] = dev->samples[0]; + dev->oldsamples[1] = dev->samples[1]; + ESFM_generate(&dev->opl, samples); + dev->samples[0] = samples[0]; + dev->samples[1] = samples[1]; + dev->samplecnt -= dev->rateratio; + } + + bufp[0] = (int32_t) ((dev->oldsamples[0] * (dev->rateratio - dev->samplecnt) + + dev->samples[0] * dev->samplecnt) + / dev->rateratio); + bufp[1] = (int32_t) ((dev->oldsamples[1] * (dev->rateratio - dev->samplecnt) + + dev->samples[1] * dev->samplecnt) + / dev->rateratio); + + dev->samplecnt += 1 << RSM_FRAC; +} + +void +esfm_drv_generate_stream(esfm_drv_t *dev, int32_t *sndptr, uint32_t num) +{ + for (uint32_t i = 0; i < num; i++) { + esfm_drv_generate_resampled(dev, sndptr); + sndptr += 2; + } +} + +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); + dev->rateratio = (SOUND_FREQ << RSM_FRAC) / 49716; + + 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 >= sound_pos_global) + return dev->buffer; + + esfm_drv_generate_stream(dev, + &dev->buffer[dev->pos * 2], + sound_pos_global - dev->pos); + + for (; dev->pos < sound_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; + + ret = ESFM_read_port(&dev->opl, port & 3); + + return ret; +} + +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 & 3) == 1) { + ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); + } else { + ESFM_write_port(&dev->opl, port & 3, val); + } + } else { + if ((port & 3) == 1 || (port & 3) == 3) { + ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); + } else { + ESFM_write_port(&dev->opl, port & 3, val); + } + } +} + +const device_t esfm_esfmu_device = { + .name = "ESS Technology ESFM (ESFMu)", + .internal_name = "ymf262_nuked", + .flags = 0, + .local = FM_YMF262, + .init = esfm_drv_init, + .close = esfm_drv_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const fm_drv_t nuked_opl_drv = { + &esfm_drv_read, + &esfm_drv_write, + &esfm_drv_update, + &esfm_drv_reset_buffer, + &esfm_drv_set_do_cycles, + NULL, + NULL, +}; From eec49e4a76400273edb402e1c5f63041836b2e4b Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Thu, 29 Feb 2024 22:21:25 +0600 Subject: [PATCH 02/66] Fix multiple symbol definition error --- src/sound/snd_opl_esfm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index f7372730c..ec549700a 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -212,7 +212,7 @@ const device_t esfm_esfmu_device = { .config = NULL }; -const fm_drv_t nuked_opl_drv = { +const fm_drv_t esfmu_opl_drv = { &esfm_drv_read, &esfm_drv_write, &esfm_drv_update, From 12c64ab43d7d5164deabc7c09f763be669808fa4 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Thu, 29 Feb 2024 22:57:51 +0600 Subject: [PATCH 03/66] Fix incorrect internal name --- src/sound/snd_opl_esfm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index ec549700a..47796a911 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -200,9 +200,9 @@ esfm_drv_write(uint16_t port, uint8_t val, void *priv) const device_t esfm_esfmu_device = { .name = "ESS Technology ESFM (ESFMu)", - .internal_name = "ymf262_nuked", + .internal_name = "esfm_esfmu", .flags = 0, - .local = FM_YMF262, + .local = 0, .init = esfm_drv_init, .close = esfm_drv_close, .reset = NULL, From 2341b28c7f4cb222dededb44a69873e9ce57f315 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 1 Mar 2024 01:58:48 +0600 Subject: [PATCH 04/66] Add FM_ESFM type --- src/include/86box/snd_opl.h | 6 +++++- src/sound/snd_opl.c | 5 +++++ src/sound/snd_opl_esfm.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) 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/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 index 47796a911..d8e350418 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -202,7 +202,7 @@ const device_t esfm_esfmu_device = { .name = "ESS Technology ESFM (ESFMu)", .internal_name = "esfm_esfmu", .flags = 0, - .local = 0, + .local = FM_ESFM, .init = esfm_drv_init, .close = esfm_drv_close, .reset = NULL, From e1badc3e0f70b0d28d39888e820f543ebb65ae69 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 1 Mar 2024 13:10:06 +0600 Subject: [PATCH 05/66] ESFM update --- src/sound/esfmu/esfm.c | 3 ++- src/sound/esfmu/esfm_registers.c | 15 +++++++++++---- src/sound/snd_opl_esfm.c | 32 ++++++-------------------------- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/sound/esfmu/esfm.c b/src/sound/esfmu/esfm.c index 7409603fe..08beadb5a 100644 --- a/src/sound/esfmu/esfm.c +++ b/src/sound/esfmu/esfm.c @@ -2117,7 +2117,7 @@ ESFM_update_timers(esfm_chip *chip) { if (chip->timer_enable[i]) { - chip->timer_accumulator[i] += i == 0 ? TIMER1_CONST : TIMER2_CONST; + chip->timer_accumulator[i] += (i == 0) ? TIMER1_CONST : TIMER2_CONST; if (chip->timer_accumulator[i] > 1.0) { chip->timer_accumulator[i] -= 1.0; @@ -2126,6 +2126,7 @@ ESFM_update_timers(esfm_chip *chip) { if (chip->timer_mask[i] == 0) { + chip->irq_bit = true; chip->timer_overflow[i] = true; } chip->timer_counter[i] = chip->timer_reload[i]; diff --git a/src/sound/esfmu/esfm_registers.c b/src/sound/esfmu/esfm_registers.c index 34ce8b19e..653d91e9a 100644 --- a/src/sound/esfmu/esfm_registers.c +++ b/src/sound/esfmu/esfm_registers.c @@ -647,9 +647,11 @@ ESFM_write_reg_emu (esfm_chip *chip, uint16_t address, uint8_t data) 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++) @@ -685,19 +687,24 @@ ESFM_write_reg_emu (esfm_chip *chip, uint16_t address, uint8_t data) 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: - chip->timer_enable[0] = data & 0x01; - chip->timer_enable[1] = (data & 0x02) != 0; - chip->timer_mask[0] = (data & 0x20) != 0; - chip->timer_mask[1] = (data & 0x40) != 0; 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; diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index d8e350418..16f728ab3 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -54,44 +54,25 @@ typedef struct { uint16_t timer_count[2]; uint16_t timer_cur_count[2]; - // OPL3L - int32_t rateratio; - int32_t samplecnt; - int32_t oldsamples[2]; - int32_t samples[2]; - int pos; int32_t buffer[SOUNDBUFLEN * 2]; } esfm_drv_t; void -esfm_drv_generate_resampled(esfm_drv_t *dev, int32_t *bufp) +esfm_generate_raw(esfm_drv_t *dev, int32_t *bufp) { - while (dev->samplecnt >= dev->rateratio) { - int16_t samples[2] = { 0, 0 }; - dev->oldsamples[0] = dev->samples[0]; - dev->oldsamples[1] = dev->samples[1]; - ESFM_generate(&dev->opl, samples); - dev->samples[0] = samples[0]; - dev->samples[1] = samples[1]; - dev->samplecnt -= dev->rateratio; - } + int16_t samples[2] = { 0, 0 }; + ESFM_generate(&dev->opl, samples); - bufp[0] = (int32_t) ((dev->oldsamples[0] * (dev->rateratio - dev->samplecnt) - + dev->samples[0] * dev->samplecnt) - / dev->rateratio); - bufp[1] = (int32_t) ((dev->oldsamples[1] * (dev->rateratio - dev->samplecnt) - + dev->samples[1] * dev->samplecnt) - / dev->rateratio); - - dev->samplecnt += 1 << RSM_FRAC; + bufp[0] = (int32_t) samples[0]; + bufp[1] = (int32_t) samples[1]; } void esfm_drv_generate_stream(esfm_drv_t *dev, int32_t *sndptr, uint32_t num) { for (uint32_t i = 0; i < num; i++) { - esfm_drv_generate_resampled(dev, sndptr); + esfm_generate_raw(dev, sndptr); sndptr += 2; } } @@ -115,7 +96,6 @@ esfm_drv_init(const device_t *info) /* Initialize the ESFMu object. */ ESFM_init(&dev->opl); - dev->rateratio = (SOUND_FREQ << RSM_FRAC) / 49716; return dev; } From 5d97fb886fb6ac8ea78b71e765a60eb193ebd93e Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 1 Mar 2024 16:10:31 +0600 Subject: [PATCH 06/66] Fix bad audio on ESFM emulation --- src/sound/snd_opl_esfm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index 16f728ab3..3720c8ada 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -112,14 +112,14 @@ esfm_drv_update(void *priv) { esfm_drv_t *dev = (esfm_drv_t *) priv; - if (dev->pos >= sound_pos_global) + if (dev->pos >= music_pos_global) return dev->buffer; esfm_drv_generate_stream(dev, &dev->buffer[dev->pos * 2], - sound_pos_global - dev->pos); + music_pos_global - dev->pos); - for (; dev->pos < sound_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; } From d806ce250ea51bb4ccd44411cd7ddcd4465edac8 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 1 Mar 2024 22:14:41 +0600 Subject: [PATCH 07/66] ESFMu update --- src/sound/esfmu/esfm_registers.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sound/esfmu/esfm_registers.c b/src/sound/esfmu/esfm_registers.c index 653d91e9a..e2f432ed1 100644 --- a/src/sound/esfmu/esfm_registers.c +++ b/src/sound/esfmu/esfm_registers.c @@ -453,21 +453,24 @@ ESFM_write_reg_native (esfm_chip *chip, uint16_t address, uint8_t data) { 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; - chip->irq_bit = 0; + break; } chip->timer_enable[0] = (data & 0x01) != 0; chip->timer_enable[1] = (data & 0x02) != 0; - chip->timer_mask[0] = (data & 0x20) != 0; - chip->timer_mask[1] = (data & 0x40) != 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; @@ -547,16 +550,16 @@ ESFM_readback_reg_native (esfm_chip *chip, uint16_t address) switch (address & 0x5ff) { case TIMER1_REG: - data = chip->timer_reload[0]; + data = chip->timer_counter[0]; break; case TIMER2_REG: - data = chip->timer_reload[1]; + 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[0] != 0) << 5; - data |= (chip->timer_mask[1] != 0) << 6; + data |= (chip->timer_mask[1] != 0) << 5; + data |= (chip->timer_mask[0] != 0) << 6; break; case CONFIG_REG: data |= (chip->keyscale_mode != 0) << 6; From 8308f410694b3b46670efbd5c3fb41622d712447 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 02:51:07 +0600 Subject: [PATCH 08/66] A bit of ESS --- src/include/86box/snd_sb_dsp.h | 5 +++++ src/sound/snd_sb_dsp.c | 27 ++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 3e0e40e80..568b02f47 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -5,6 +5,11 @@ #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_ES688 3 /* ESS Technology ES688 */ +#define SB_SUBTYPE_ESS_ES1688 4 /* ESS Technology ES1688 */ + +/* ESS-related */ +#define IS_ESS(dsp) ((dsp)->sb_subtype >= SB_SUBTYPE_ESS_ES688) /* 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 */ diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index cf4498b4b..d94faa29e 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -906,17 +906,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 +948,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); @@ -1036,7 +1045,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) { From d5dad279c4e9a7bb12286ef35a38e98e4fe546fa Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 16:50:29 +0600 Subject: [PATCH 09/66] ESSreg macro --- src/include/86box/snd_sb_dsp.h | 2 ++ src/sound/snd_sb_dsp.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 568b02f47..bf9ceab53 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -140,6 +140,8 @@ 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. */ + mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index d94faa29e..79415ac5b 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -135,6 +135,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) { From 97b239aed50c3b5c149e4195548cc1d75e3677e0 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 17:12:26 +0600 Subject: [PATCH 10/66] More small pieces of ESS emulation --- src/include/86box/snd_sb_dsp.h | 1 + src/sound/snd_sb_dsp.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index bf9ceab53..6f94a9801 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -141,6 +141,7 @@ 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; mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 79415ac5b..1e2d469a2 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -1332,6 +1332,15 @@ 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 pollsb(void *priv) { @@ -1530,6 +1539,7 @@ pollsb(void *priv) else { dsp->sb_8_enable = 0; timer_disable(&dsp->output_timer); + sb_ess_finish_dma(dsp); } sb_irq(dsp, 1); } @@ -1582,6 +1592,7 @@ pollsb(void *priv) else { dsp->sb_16_enable = 0; timer_disable(&dsp->output_timer); + sb_ess_finish_dma(dsp); } sb_irq(dsp, 0); } From 3f72c788bdd837cb8c940f1559f76929e6e3ffbd Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 17:29:05 +0600 Subject: [PATCH 11/66] ESS bits for IRQ raise --- src/sound/snd_sb_dsp.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 1e2d469a2..c028f98c1 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -224,6 +224,12 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) break; } + /* 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) @@ -377,6 +383,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 @@ -409,6 +418,32 @@ 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, int dma8, int autoinit, uint8_t format, int len) +{ + if (IS_ESS(dsp)) { + dma_set_drq(dsp->sb_8_dmanum, 0); + dma_set_drq(dsp->sb_16_8_dmanum, 0); + } + sb_start_dma(dsp, dma8, autoinit, 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_start_dma_ess_i(sb_dsp_t* dsp, int dma8, int autoinit, uint8_t format, int len) +{ + if (IS_ESS(dsp)) { + dma_set_drq(dsp->sb_8_dmanum, 0); + dma_set_drq(dsp->sb_16_8_dmanum, 0); + } + sb_start_dma_i(dsp, dma8, autoinit, format, len); + dsp->ess_playback_mode = 1; + dma_set_drq(dsp->sb_8_dmanum, 1); + dma_set_drq(dsp->sb_16_8_dmanum, 1); +} + int sb_8_read_dma(void *priv) { From 4369284f6549258ee465566416b3c93ee4b59d0e Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 17:36:55 +0600 Subject: [PATCH 12/66] ESS register 0xA2 update function --- src/sound/snd_sb_dsp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index c028f98c1..12b04a008 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -585,6 +585,18 @@ sb_dsp_setdma16_translate(sb_dsp_t *dsp, int translate) dsp->sb_16_dma_translate = translate; } +/* 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) { + if (dsp->sb_freq >= 22050) + ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq); + else + ESSreg(0xA1) = 128 - (397700UL / dsp->sb_freq); + + unsigned int freq = ((dsp->sb_freq * 4) / (5 * 2)); /* 80% of 1/2 the sample rate */ + ESSreg(0xA2) = 256 - (7160000 / (freq * 82)); +} + void sb_exec_command(sb_dsp_t *dsp) { @@ -796,6 +808,9 @@ sb_exec_command(sb_dsp_t *dsp) 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 */ From 3f7fbc7467266e5456281b7808222cd6c9622a79 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 17:41:51 +0600 Subject: [PATCH 13/66] Extended mode toggle --- src/include/86box/snd_sb_dsp.h | 1 + src/sound/snd_sb_dsp.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 6f94a9801..f04734ebb 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -142,6 +142,7 @@ typedef struct sb_dsp_t { uint8_t ess_regs[256]; /* ESS registers. */ uint8_t ess_playback_mode; + uint8_t ess_extended_mode; mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 12b04a008..08a69ec82 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -610,6 +610,13 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->sb_type >= SB16) dsp->sb_8051_ram[0x20] = dsp->sb_command; + if (IS_ESS(dsp)) { + if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7){ + dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6); + return; + } + } + switch (dsp->sb_command) { case 0x01: /* ???? */ if (dsp->sb_type >= SB16) From eda528d98c0f2371b555dc6c61abd67b6e34daff Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 2 Mar 2024 17:48:21 +0600 Subject: [PATCH 14/66] ESS register read function --- src/sound/snd_sb_dsp.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 08a69ec82..05b6abe43 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -587,7 +587,8 @@ sb_dsp_setdma16_translate(sb_dsp_t *dsp, int translate) /* 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) { +static void sb_ess_update_filter_freq(sb_dsp_t *dsp) +{ if (dsp->sb_freq >= 22050) ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq); else @@ -597,6 +598,27 @@ static void sb_ess_update_filter_freq(sb_dsp_t *dsp) { ESSreg(0xA2) = 256 - (7160000 / (freq * 82)); } +static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp) +{ + unsigned int r; + + r = (unsigned int)ESSreg(0xA5) << 8U; + r |= (unsigned int)ESSreg(0xA4); + + /* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */ + return 0x10000U-r; +} + +static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) +{ + switch (reg) { + default: + return ESSreg(reg); + } + + return 0xFF; +} + void sb_exec_command(sb_dsp_t *dsp) { From e7e582cd740541dfe61f290967e77e5e1d618f51 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sun, 3 Mar 2024 16:02:56 +0600 Subject: [PATCH 15/66] Finish DSP part of ESS --- src/include/86box/snd_sb_dsp.h | 1 + src/sound/CMakeLists.txt | 2 +- src/sound/snd_ess.c | 258 +++++++++++++++++++++++++++++++++ src/sound/snd_sb_dsp.c | 219 ++++++++++++++++++++++++---- 4 files changed, 454 insertions(+), 26 deletions(-) create mode 100644 src/sound/snd_ess.c diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index f04734ebb..2b39a0303 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -143,6 +143,7 @@ typedef struct sb_dsp_t { uint8_t ess_regs[256]; /* ESS registers. */ uint8_t ess_playback_mode; uint8_t ess_extended_mode; + uint8_t ess_reload_len; mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index 9b2cc1416..9206f6be9 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_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c - snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c) + snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c snd_ess.c) if(OPENAL) if(VCPKG_TOOLCHAIN) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c new file mode 100644 index 000000000..d762117cd --- /dev/null +++ b/src/sound/snd_ess.c @@ -0,0 +1,258 @@ +/* + * 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. + * + * Sound Blaster emulation. + * + * + * + * Authors: Sarah Walker, + * Miran Grca, + * TheCollector1995, + * + * Copyright 2008-2020 Sarah Walker. + * Copyright 2016-2020 Miran Grca. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H + +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/filters.h> +#include <86box/gameport.h> +#include <86box/hdc.h> +#include <86box/isapnp.h> +#include <86box/hdc_ide.h> +#include <86box/io.h> +#include <86box/mca.h> +#include <86box/mem.h> +#include <86box/midi.h> +#include <86box/pic.h> +#include <86box/rom.h> +#include <86box/sound.h> +#include <86box/timer.h> +#include <86box/snd_sb.h> +#include <86box/plat_unused.h> + +static const double sb_att_4dbstep_3bits[] = { + 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0 +}; + +static const double sb_att_7dbstep_2bits[] = { + 164.0, 6537.0, 14637.0, 32767.0 +}; + +/* SB PRO */ +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; + /*see sb_ct1745_mixer for values for input selector*/ + int32_t input_selector; + + int input_filter; + int in_filter_freq; + int output_filter; + + int stereo; + int stereo_isleft; + + uint8_t index; + uint8_t regs[256]; +} ess_mixer_t; + +typedef struct ess_t { + uint8_t mixer_enabled; + fm_drv_t opl; + sb_dsp_t dsp; + union { + ess_mixer_t mixer_sbpro; + }; + mpu_t *mpu; + emu8k_t emu8k; + void *gameport; + + int pnp; + + uint8_t pos_regs[8]; + uint8_t pnp_rom[512]; + + uint16_t opl_pnp_addr; + uint16_t gameport_addr; + + void *opl_mixer; + void (*opl_mix)(void*, double*, double*); +} ess_t; + +static inline uint8_t expand16to32(const uint8_t t) { + /* 4-bit -> 5-bit expansion. + * + * 0 -> 0 + * 1 -> 2 + * 2 -> 4 + * 3 -> 6 + * .... + * 7 -> 14 + * 8 -> 17 + * 9 -> 19 + * 10 -> 21 + * 11 -> 23 + * .... + * 15 -> 31 */ + return (t << 1) | (t >> 3); +} + +void +ess_mixer_write(uint16_t addr, uint8_t val, void *priv) +{ + ess_t *ess = (ess_t *) priv; + ess_mixer_t *mixer = &ess->mixer_sbpro; + + if (!(addr & 1)) { + mixer->index = val; + mixer->regs[0x01] = val; + } 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; + 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 0x22: + case 0x26: + case 0x28: + mixer->regs[mixer->index - 0x20] = (val & 0xe); + 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: + mixer->regs[mixer->index - 0x10] = (val & 0xee); + break; + + case 0x00: + case 0x04: + case 0x0a: + case 0x0c: + case 0x0e: + case 0x2e: + break; + + default: + //sb_log("ess: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; + } + } + + mixer->voice_l = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 5) & 0x7] / 32768.0; + mixer->voice_r = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 1) & 0x7] / 32768.0; + mixer->master_l = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 5) & 0x7] / 32768.0; + mixer->master_r = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 1) & 0x7] / 32768.0; + mixer->fm_l = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 5) & 0x7] / 32768.0; + mixer->fm_r = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 1) & 0x7] / 32768.0; + mixer->cd_l = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 5) & 0x7] / 32768.0; + mixer->cd_r = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 1) & 0x7] / 32768.0; + mixer->line_l = sb_att_4dbstep_3bits[(mixer->regs[0x2e] >> 5) & 0x7] / 32768.0; + mixer->line_r = sb_att_4dbstep_3bits[(mixer->regs[0x2e] >> 1) & 0x7] / 32768.0; + + mixer->mic = sb_att_7dbstep_2bits[(mixer->regs[0x0a] >> 1) & 0x3] / 32768.0; + + mixer->output_filter = !(mixer->regs[0xe] & 0x20); + mixer->input_filter = !(mixer->regs[0xc] & 0x20); + mixer->in_filter_freq = ((mixer->regs[0xc] & 0x8) == 0) ? 3200 : 8800; + mixer->stereo = mixer->regs[0xe] & 2; + if (mixer->index == 0xe) + sb_dsp_set_stereo(&ess->dsp, val & 2); + + switch (mixer->regs[0xc] & 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; + } + + /* TODO: pcspeaker volume? Or is it not worth? */ + } +} + +uint8_t +ess_mixer_read(uint16_t addr, void *priv) +{ + const ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; + + if (!(addr & 1)) + return mixer->index; + + switch (mixer->index) { + case 0x00: + case 0x04: + case 0x0a: + case 0x0c: + case 0x0e: + case 0x22: + case 0x26: + case 0x28: + case 0x2e: + case 0x02: + case 0x06: + case 0x30: + case 0x32: + case 0x36: + case 0x38: + return mixer->regs[mixer->index]; + + default: + //sb_log("ess: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; + } + + return 0xff; +} + +void +ess_mixer_reset(ess_t *ess) +{ + ess_mixer_write(4, 0, ess); + ess_mixer_write(5, 0, ess); +} \ No newline at end of file diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 05b6abe43..29b065477 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 @@ -418,30 +419,65 @@ 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, int dma8, int autoinit, uint8_t format, int len) + +static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp) { + unsigned int r; + + r = (unsigned int)ESSreg(0xA5) << 8U; + r |= (unsigned int)ESSreg(0xA4); + + /* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */ + return 0x10000U-r; +} + +void +sb_start_dma_ess(sb_dsp_t* dsp) +{ + uint8_t real_format = 0; + uint32_t len = !(ESSreg(0xB7) & 4) ? dsp->sb_8_length : dsp->sb_16_length; + + if (!dsp->ess_reload_len) { + 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); } - sb_start_dma(dsp, dma8, autoinit, format, len); + 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, sb_ess_get_dma_len(dsp)); + else + sb_start_dma(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, sb_ess_get_dma_len(dsp)); dsp->ess_playback_mode = 1; dma_set_drq(dsp->sb_8_dmanum, 1); dma_set_drq(dsp->sb_16_8_dmanum, 1); } void -sb_start_dma_ess_i(sb_dsp_t* dsp, int dma8, int autoinit, uint8_t format, int len) +sb_stop_dma_ess(sb_dsp_t* dsp) { - if (IS_ESS(dsp)) { - dma_set_drq(dsp->sb_8_dmanum, 0); - dma_set_drq(dsp->sb_16_8_dmanum, 0); + 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); } - sb_start_dma_i(dsp, dma8, autoinit, format, len); - dsp->ess_playback_mode = 1; - dma_set_drq(dsp->sb_8_dmanum, 1); - dma_set_drq(dsp->sb_16_8_dmanum, 1); } int @@ -598,17 +634,6 @@ static void sb_ess_update_filter_freq(sb_dsp_t *dsp) ESSreg(0xA2) = 256 - (7160000 / (freq * 82)); } -static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp) -{ - unsigned int r; - - r = (unsigned int)ESSreg(0xA5) << 8U; - r |= (unsigned int)ESSreg(0xA4); - - /* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */ - return 0x10000U-r; -} - static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) { switch (reg) { @@ -619,6 +644,120 @@ static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) return 0xFF; } +static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) +{ + uint8_t chg = 0x00; + sb_dsp_log("ESS register write reg=%02xh val=%02xh\n",reg,data); + + switch (reg) { + case 0xA1: /* Extended Mode Sample Rate Generator */ + { + ESSreg(reg) = data; + if (data & 0x80) + dsp->sb_freq = 795500UL / (256ul - data); + else + dsp->sb_freq = 397700UL / (128ul - data); + + if (dsp->sb_16_enable || dsp->sb_8_enable) { + sb_stop_dma_ess(dsp); + sb_start_dma_ess(dsp); + } + break; + } + case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */ + ESSreg(reg) = data; + break; + + case 0xA4: /* DMA Transfer Count Reload (low) */ + case 0xA5: /* DMA Transfer Count Reload (high) */ + ESSreg(reg) = data; + if (dsp->sb_16_length == 0 || dsp->sb_8_length == 0) + 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) { + sb_stop_dma_ess(dsp); + sb_start_dma_ess(dsp); + } + } + break; + + case 0xB1: /* Legacy Audio Interrupt Control */ + case 0xB2: /* DRQ Control */ + chg = ESSreg(reg) ^ data; + ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable + 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 & 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) + dsp->ess_reload_len = 1; + + 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 */ + ESSreg(reg) = data; + break; + + default: + break; + } +} + void sb_exec_command(sb_dsp_t *dsp) { @@ -632,11 +771,17 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->sb_type >= SB16) dsp->sb_8051_ram[0x20] = dsp->sb_command; - if (IS_ESS(dsp)) { - if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7){ + 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; } + 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) { @@ -1062,12 +1207,29 @@ 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_ES688: + sb_add_data(dsp, 0x68); + sb_add_data(dsp, 0x80 | 0x04); + 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); @@ -1170,6 +1332,13 @@ 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 >= 0xA0 && dsp->sb_command <= 0xCF) { + if (dsp->sb_command <= 0xC0) { + sb_commands[dsp->sb_command] = 1; + } else { + sb_commands[dsp->sb_command] = 0; + } + } } if (dsp->sb_data_stat == sb_commands[dsp->sb_command] || sb_commands[dsp->sb_command] == -1) { sb_exec_command(dsp); From 7f9f072b3e51324a299b07bd2140f1cebebcb261 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sun, 3 Mar 2024 16:19:01 +0600 Subject: [PATCH 16/66] Add ESS ES1688 (COMPLETELY UNTESTED!!!) --- src/include/86box/sound.h | 4 + src/sound/snd_ess.c | 261 +++++++++++++++++++++++++++++++++++++- src/sound/sound.c | 1 + 3 files changed, 265 insertions(+), 1 deletion(-) diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index b8f9be5b2..c6fdada89 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -184,6 +184,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/snd_ess.c b/src/sound/snd_ess.c index d762117cd..5b6852b68 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -255,4 +255,263 @@ ess_mixer_reset(ess_t *ess) { ess_mixer_write(4, 0, ess); ess_mixer_write(5, 0, ess); -} \ No newline at end of file +} + +void +ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) +{ + sb_t *sb = (sb_t *) priv; + const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + double out_l = 0.0; + double out_r = 0.0; + + sb_dsp_update(&sb->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 += (sb_iir(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.9; + out_r += (sb_iir(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.9; + } else { + out_l += (sb->dsp.buffer[c] * mixer->voice_l) / 3.0; + out_r += (sb->dsp.buffer[c + 1] * mixer->voice_r) / 3.0; + } + + /* TODO: recording CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ + out_l *= mixer->master_l; + out_r *= mixer->master_r; + + buffer[c] += (int32_t) out_l; + buffer[c + 1] += (int32_t) out_r; + } + + sb->dsp.pos = 0; +} + +void +ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) +{ + ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; + double out_l = 0.0; + double out_r = 0.0; + const int32_t *opl_buf = NULL; + const int32_t *opl2_buf = NULL; + + opl_buf = ess->opl.update(ess->opl.priv); + + sb_dsp_update(&ess->dsp); + + 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 CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ + 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 ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; + double c; + double cd = channel ? mixer->cd_r : mixer->cd_l; + double master = channel ? mixer->master_r : mixer->master_l; + + if (mixer->output_filter) + c = (sb_iir(2, channel, *buffer) * cd) / 3.9; + else + c = (*buffer * cd) / 3.0; + *buffer = c * master; +} + +static void * +ess_1688_init(UNUSED(const device_t *info)) +{ + /* SB Pro 2 port mappings, 220h or 240h. + 2x0 to 2x3 -> FM chip (18 voices) + 2x4 to 2x5 -> Mixer interface + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip (9 voices) + 2x0+10 to 2x0+13 CDROM interface. */ + ess_t *ess = malloc(sizeof(ess_t)); + uint16_t addr = device_get_config_hex16("base"); + memset(ess, 0, sizeof(ess_t)); + + 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")); + 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(ess_get_buffer_sbpro, ess); + music_add_handler(ess_get_music_buffer_sbpro, 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); + + return ess; +} + +void +ess_close(void *priv) +{ + ess_t *ess = (ess_t *) priv; + sb_dsp_close(&ess->dsp); + + free(ess); +} + +void +ess_speed_changed(void *priv) +{ + ess_t *ess = (ess_t *) priv; + + sb_dsp_speed_changed(&ess->dsp); +} + +static const device_config_t ess_config[] = { + { + .name = "base", + .description = "Address", + .type = CONFIG_HEX16, + .default_string = "", + .default_int = 0x220, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { + .description = "0x220", + .value = 0x220 + }, + { + .description = "0x240", + .value = 0x240 + }, + { .description = "" } + } + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 7, + .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 } +}; + +const device_t ess_1688_device = { + .name = "ESS Technology ESS1688", + .internal_name = "ess_1688", + .flags = DEVICE_ISA, + .local = 0, + .init = ess_1688_init, + .close = ess_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = ess_speed_changed, + .force_redraw = NULL, + .config = ess_config +}; \ No newline at end of file diff --git a/src/sound/sound.c b/src/sound/sound.c index 81f70d921..2f6b91aca 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -165,6 +165,7 @@ static const SOUND_CARD sound_cards[] = { { &es1371_device }, { &ad1881_device }, { &cs4297a_device }, + { &ess_1688_device }, { NULL } // clang-format on }; From 6ab45767e5fad8b8d1638c677a3be07cf8e55d50 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sun, 3 Mar 2024 16:26:49 +0600 Subject: [PATCH 17/66] Some cleanup and crash fixes --- src/sound/snd_ess.c | 1 - src/sound/snd_opl_esfm.c | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 5b6852b68..f0f1b5ec7 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -299,7 +299,6 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) double out_l = 0.0; double out_r = 0.0; const int32_t *opl_buf = NULL; - const int32_t *opl2_buf = NULL; opl_buf = ess->opl.update(ess->opl.priv); diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index 3720c8ada..cbd9a93a3 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -54,18 +54,19 @@ typedef struct { uint16_t timer_count[2]; uint16_t timer_cur_count[2]; + int16_t samples[2]; + int pos; - int32_t buffer[SOUNDBUFLEN * 2]; + int32_t buffer[MUSICBUFLEN * 2]; } esfm_drv_t; void esfm_generate_raw(esfm_drv_t *dev, int32_t *bufp) { - int16_t samples[2] = { 0, 0 }; - ESFM_generate(&dev->opl, samples); + ESFM_generate(&dev->opl, &dev->samples[0]); - bufp[0] = (int32_t) samples[0]; - bufp[1] = (int32_t) samples[1]; + bufp[0] = dev->samples[0]; + bufp[1] = dev->samples[1]; } void From d46e00e5a05307e25ed24dfb7540a6ec8f393432 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 00:16:04 +0600 Subject: [PATCH 18/66] Autolen updating --- src/sound/snd_sb_dsp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 29b065477..ed74fea68 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -644,6 +644,10 @@ static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) return 0xFF; } +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; @@ -671,7 +675,8 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) case 0xA4: /* DMA Transfer Count Reload (low) */ case 0xA5: /* DMA Transfer Count Reload (high) */ ESSreg(reg) = data; - if (dsp->sb_16_length == 0 || dsp->sb_8_length == 0) + sb_ess_update_autolen(dsp); + if (dsp->sb_16_length < 0 || dsp->sb_8_length < 0) dsp->ess_reload_len = 1; break; @@ -741,6 +746,9 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) if (chg & 1) dsp->ess_reload_len = 1; + if (chg & 4) + sb_ess_update_autolen(dsp); + 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); From 650b7e633beecd54d278707719b74b055426739e Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 00:20:27 +0600 Subject: [PATCH 19/66] Minor fixing --- src/sound/snd_sb_dsp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index ed74fea68..7977cc030 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -722,6 +722,10 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) * 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); @@ -746,9 +750,6 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) if (chg & 1) dsp->ess_reload_len = 1; - if (chg & 4) - sb_ess_update_autolen(dsp); - 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); From 68f6779b2f53216308841fbead06f23f07383a66 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 01:35:34 +0600 Subject: [PATCH 20/66] Handle length reloading correctly --- src/sound/snd_sb_dsp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 7977cc030..58131515d 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -437,8 +437,9 @@ sb_start_dma_ess(sb_dsp_t* dsp) uint8_t real_format = 0; uint32_t len = !(ESSreg(0xB7) & 4) ? dsp->sb_8_length : dsp->sb_16_length; - if (!dsp->ess_reload_len) { + if (dsp->ess_reload_len) { len = sb_ess_get_dma_len(dsp); + dsp->ess_reload_len = 0; } if (IS_ESS(dsp)) { @@ -676,7 +677,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) case 0xA5: /* DMA Transfer Count Reload (high) */ ESSreg(reg) = data; sb_ess_update_autolen(dsp); - if (dsp->sb_16_length < 0 || dsp->sb_8_length < 0) + 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; @@ -1912,6 +1913,7 @@ sb_poll_i(void *priv) else { dsp->sb_8_enable = 0; timer_disable(&dsp->input_timer); + sb_ess_finish_dma(dsp); } sb_irq(dsp, 1); } @@ -1960,6 +1962,7 @@ sb_poll_i(void *priv) else { dsp->sb_16_enable = 0; timer_disable(&dsp->input_timer); + sb_ess_finish_dma(dsp); } sb_irq(dsp, 0); } From dfa0ec6be80dab9dc800724dd988d9c9584eeae9 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 02:00:07 +0600 Subject: [PATCH 21/66] Implement ESS identification mixer register --- src/sound/snd_ess.c | 21 +++++++++++++++++++-- src/sound/snd_sb_dsp.c | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index f0f1b5ec7..9347a755e 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -76,6 +76,9 @@ typedef struct ess_mixer_t { uint8_t index; uint8_t regs[256]; + + uint8_t ess_id_str[4]; + uint8_t ess_id_str_pos : 2; } ess_mixer_t; typedef struct ess_t { @@ -128,6 +131,8 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) 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 */ @@ -218,8 +223,8 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) uint8_t ess_mixer_read(uint16_t addr, void *priv) { - const ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; + ess_t *ess = (ess_t *) priv; + ess_mixer_t *mixer = &ess->mixer_sbpro; if (!(addr & 1)) return mixer->index; @@ -242,6 +247,11 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x38: return mixer->regs[mixer->index]; + case 0x40: + { + return mixer->ess_id_str[mixer->ess_id_str_pos++]; + } + default: //sb_log("ess: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; @@ -391,6 +401,13 @@ ess_1688_init(UNUSED(const device_t *info)) if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); + { + ess->mixer_sbpro.ess_id_str[0] = 0x16; + ess->mixer_sbpro.ess_id_str[1] = 0x88; + ess->mixer_sbpro.ess_id_str[2] = (addr >> 8) & 0xff; + ess->mixer_sbpro.ess_id_str[3] = addr & 0xff; + } + return ess; } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 58131515d..45f8440a2 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -900,6 +900,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)); From a2b13cadbff2d8ca9c295cd9431371527b5e3b75 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 13:18:50 +0600 Subject: [PATCH 22/66] ESS: implement mixer regs and fix ESS-specific DMA --- src/sound/snd_ess.c | 105 ++++++++++++++++++++++++++++++++++++----- src/sound/snd_sb_dsp.c | 17 +++++-- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 9347a755e..b58618c3a 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #define HAVE_STDARG_H @@ -122,6 +123,11 @@ static inline uint8_t expand16to32(const uint8_t t) { return (t << 1) | (t >> 3); } +static double ess_mixer_get_vol_4bit(uint8_t vol) +{ + return (48.0 + (20.0 * log((vol & 0xF) / 15.0))) / 48.0; +} + void ess_mixer_write(uint16_t addr, uint8_t val, void *priv) { @@ -142,6 +148,14 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[0x04] = mixer->regs[0x22] = 0xee; mixer->regs[0x26] = mixer->regs[0x28] = 0xee; mixer->regs[0x2e] = 0x00; + + /* Initialize ESS regs. */ + mixer->regs[0x14] = mixer->regs[0x32] = 0x88; + mixer->regs[0x36] = 0x88; + mixer->regs[0x38] = 0x00; + mixer->regs[0x3a] = 0x00; + mixer->regs[0x3e] = 0x00; + sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2); } else { mixer->regs[mixer->index] = val; @@ -153,20 +167,29 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) case 0x08: mixer->regs[mixer->index + 0x20] = ((val & 0xe) << 4) | (val & 0xe); break; + + case 0x14: + mixer->regs[0x4] = val & 0xee; + 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: + mixer->regs[mixer->index - 0x10] = (val & 0xee); + break; case 0x32: case 0x36: case 0x38: + case 0x3e: mixer->regs[mixer->index - 0x10] = (val & 0xee); break; @@ -175,25 +198,75 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) case 0x0a: case 0x0c: case 0x0e: - case 0x2e: break; + case 0x40: { + uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] & 0x38) << 1); + gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); + io_removehandler(0x0388, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + if (mixer->regs[0x40] & 1) { + io_sethandler(0x0388, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + } + + switch ((mixer->regs[0x40] >> 5) & 7) { + 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, 0xE); + break; + case 4: + mpu401_change_addr(ess->mpu, mpu401_base_addr); + mpu401_setirq(ess->mpu, 0xA); + break; + case 5: + mpu401_change_addr(ess->mpu, mpu401_base_addr); + mpu401_setirq(ess->mpu, 0xB); + break; + case 6: + mpu401_change_addr(ess->mpu, mpu401_base_addr); + mpu401_setirq(ess->mpu, 0xC); + break; + case 7: + mpu401_change_addr(ess->mpu, mpu401_base_addr); + mpu401_setirq(ess->mpu, 0xD); + break; + } + break; + } + default: //sb_log("ess: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } - mixer->voice_l = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 5) & 0x7] / 32768.0; - mixer->voice_r = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 1) & 0x7] / 32768.0; - mixer->master_l = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 5) & 0x7] / 32768.0; - mixer->master_r = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 1) & 0x7] / 32768.0; - mixer->fm_l = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 5) & 0x7] / 32768.0; - mixer->fm_r = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 1) & 0x7] / 32768.0; - mixer->cd_l = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 5) & 0x7] / 32768.0; - mixer->cd_r = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 1) & 0x7] / 32768.0; - mixer->line_l = sb_att_4dbstep_3bits[(mixer->regs[0x2e] >> 5) & 0x7] / 32768.0; - mixer->line_r = sb_att_4dbstep_3bits[(mixer->regs[0x2e] >> 1) & 0x7] / 32768.0; + mixer->voice_l = ess_mixer_get_vol_4bit(mixer->regs[0x14]); + mixer->voice_r = ess_mixer_get_vol_4bit(mixer->regs[0x14] >> 4); + mixer->master_l = ess_mixer_get_vol_4bit(mixer->regs[0x32]); + mixer->master_r = ess_mixer_get_vol_4bit(mixer->regs[0x32] >> 4); + mixer->fm_l = ess_mixer_get_vol_4bit(mixer->regs[0x36]); + mixer->fm_r = ess_mixer_get_vol_4bit(mixer->regs[0x36] >> 4); + mixer->cd_l = ess_mixer_get_vol_4bit(mixer->regs[0x38]); + mixer->cd_r = ess_mixer_get_vol_4bit(mixer->regs[0x38] >> 4); + mixer->line_l = ess_mixer_get_vol_4bit(mixer->regs[0x3e]); + mixer->line_r = ess_mixer_get_vol_4bit(mixer->regs[0x3e] >> 4); mixer->mic = sb_att_7dbstep_2bits[(mixer->regs[0x0a] >> 1) & 0x3] / 32768.0; @@ -372,6 +445,8 @@ ess_1688_init(UNUSED(const device_t *info)) 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 */ { @@ -408,6 +483,14 @@ ess_1688_init(UNUSED(const device_t *info)) ess->mixer_sbpro.ess_id_str[3] = addr & 0xff; } + ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); + mpu401_init(ess->mpu, 0, 0, M_UART, 1); + sb_dsp_set_mpu(&ess->dsp, ess->mpu); + + ess->gameport = gameport_add(&gameport_pnp_device); + ess->gameport_addr = 0x000; + gameport_remap(ess->gameport, ess->gameport_addr); + return ess; } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 45f8440a2..fa3133768 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -437,7 +437,7 @@ sb_start_dma_ess(sb_dsp_t* dsp) uint8_t real_format = 0; uint32_t len = !(ESSreg(0xB7) & 4) ? dsp->sb_8_length : dsp->sb_16_length; - if (dsp->ess_reload_len) { + if (dsp->ess_reload_len || len <= 0) { len = sb_ess_get_dma_len(dsp); dsp->ess_reload_len = 0; } @@ -449,9 +449,9 @@ sb_start_dma_ess(sb_dsp_t* dsp) 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, sb_ess_get_dma_len(dsp)); + 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, sb_ess_get_dma_len(dsp)); + 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); @@ -748,8 +748,15 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) chg = ESSreg(reg) ^ data; ESSreg(reg) = data; - if (chg & 1) - dsp->ess_reload_len = 1; + 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 & 0xB) { if (chg & 0xA) sb_stop_dma_ess(dsp); /* changing capture/playback direction? stop DMA to reinit */ From 181d42610fa7299d6b55e2a3570e67c7ed18a94f Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 13:20:20 +0600 Subject: [PATCH 23/66] Correct name of ESS device --- src/sound/snd_ess.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index b58618c3a..d3e02bf2f 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -602,8 +602,8 @@ static const device_config_t ess_config[] = { }; const device_t ess_1688_device = { - .name = "ESS Technology ESS1688", - .internal_name = "ess_1688", + .name = "ESS Technology ES1688", + .internal_name = "ess_es1688", .flags = DEVICE_ISA, .local = 0, .init = ess_1688_init, From 2446c4ebd4671c16b289163d26d09ea76190ac63 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 14:56:07 +0600 Subject: [PATCH 24/66] Fix some stale functions --- src/sound/snd_ess.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index d3e02bf2f..504122a0b 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -78,27 +78,19 @@ typedef struct ess_mixer_t { uint8_t index; uint8_t regs[256]; - uint8_t ess_id_str[4]; - uint8_t ess_id_str_pos : 2; + uint8_t ess_id_str[256]; + uint8_t ess_id_str_pos; } ess_mixer_t; typedef struct ess_t { uint8_t mixer_enabled; fm_drv_t opl; sb_dsp_t dsp; - union { - ess_mixer_t mixer_sbpro; - }; + ess_mixer_t mixer_sbpro; + mpu_t *mpu; - emu8k_t emu8k; void *gameport; - int pnp; - - uint8_t pos_regs[8]; - uint8_t pnp_rom[512]; - - uint16_t opl_pnp_addr; uint16_t gameport_addr; void *opl_mixer; @@ -322,7 +314,11 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x40: { - return mixer->ess_id_str[mixer->ess_id_str_pos++]; + 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; } default: @@ -343,12 +339,12 @@ ess_mixer_reset(ess_t *ess) void ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) { - sb_t *sb = (sb_t *) priv; - const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; double out_l = 0.0; double out_r = 0.0; - sb_dsp_update(&sb->dsp); + sb_dsp_update(&ess->dsp); for (int c = 0; c < len * 2; c += 2) { out_l = 0.0; @@ -356,11 +352,11 @@ ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */ if (mixer->output_filter) { - out_l += (sb_iir(0, 0, (double) sb->dsp.buffer[c]) * mixer->voice_l) / 3.9; - out_r += (sb_iir(0, 1, (double) sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3.9; + out_l += (sb_iir(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.9; + out_r += (sb_iir(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.9; } else { - out_l += (sb->dsp.buffer[c] * mixer->voice_l) / 3.0; - out_r += (sb->dsp.buffer[c + 1] * mixer->voice_r) / 3.0; + 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 CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ @@ -371,7 +367,7 @@ ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) buffer[c + 1] += (int32_t) out_r; } - sb->dsp.pos = 0; + ess->dsp.pos = 0; } void @@ -434,9 +430,8 @@ ess_1688_init(UNUSED(const device_t *info)) 2x6, 2xA, 2xC, 2xE -> DSP chip 2x8, 2x9, 388 and 389 FM chip (9 voices) 2x0+10 to 2x0+13 CDROM interface. */ - ess_t *ess = malloc(sizeof(ess_t)); + ess_t *ess = calloc(sizeof(ess_t), 1); uint16_t addr = device_get_config_hex16("base"); - memset(ess, 0, sizeof(ess_t)); fm_driver_get(FM_ESFM, &ess->opl); From 6ae6ca11714b746b2967fa1b8a18f2587c02f9d8 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 15:04:17 +0600 Subject: [PATCH 25/66] Fix OPL address decoding getting disabled for some reason --- src/sound/snd_ess.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 504122a0b..0d507dc5f 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -195,6 +195,8 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) case 0x40: { uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] & 0x38) << 1); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); + /* This doesn't work yet. */ + /* io_removehandler(0x0388, 0x0004, ess->opl.read, NULL, NULL, ess->opl.write, NULL, NULL, @@ -204,7 +206,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) ess->opl.read, NULL, NULL, ess->opl.write, NULL, NULL, ess->opl.priv); - } + }*/ switch ((mixer->regs[0x40] >> 5) & 7) { case 0: From 2bdd5ca9bcfa4361650367003173855e14b44eb8 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 15:58:02 +0600 Subject: [PATCH 26/66] Correct IRQ selection and detection fixes --- src/sound/snd_ess.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 0d507dc5f..7f48c95c0 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -79,7 +79,7 @@ typedef struct ess_mixer_t { uint8_t regs[256]; uint8_t ess_id_str[256]; - uint8_t ess_id_str_pos; + uint8_t ess_id_str_pos : 2; } ess_mixer_t; typedef struct ess_t { @@ -192,6 +192,10 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) case 0x0e: break; + case 0x64: + mixer->regs[mixer->index] &= ~0x8; + break; + case 0x40: { uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] & 0x38) << 1); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); @@ -223,23 +227,23 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 3: mpu401_change_addr(ess->mpu, mpu401_base_addr); - mpu401_setirq(ess->mpu, 0xE); + mpu401_setirq(ess->mpu, 11); break; case 4: mpu401_change_addr(ess->mpu, mpu401_base_addr); - mpu401_setirq(ess->mpu, 0xA); + mpu401_setirq(ess->mpu, 9); break; case 5: mpu401_change_addr(ess->mpu, mpu401_base_addr); - mpu401_setirq(ess->mpu, 0xB); + mpu401_setirq(ess->mpu, 5); break; case 6: mpu401_change_addr(ess->mpu, mpu401_base_addr); - mpu401_setirq(ess->mpu, 0xC); + mpu401_setirq(ess->mpu, 7); break; case 7: mpu401_change_addr(ess->mpu, mpu401_base_addr); - mpu401_setirq(ess->mpu, 0xD); + mpu401_setirq(ess->mpu, 10); break; } break; @@ -302,6 +306,7 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x0a: case 0x0c: case 0x0e: + case 0x14: case 0x22: case 0x26: case 0x28: @@ -312,8 +317,12 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x32: case 0x36: case 0x38: + case 0x3e: return mixer->regs[mixer->index]; + case 0x64: + return mixer->regs[mixer->index] & 7; + case 0x40: { uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos]; @@ -324,11 +333,11 @@ ess_mixer_read(uint16_t addr, void *priv) } default: - //sb_log("ess: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + pclog("ess: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } - return 0xff; + return 0x00; } void From 552f595bc549b17a754fe57bb73ba8ae8434dfc3 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 4 Mar 2024 16:00:41 +0600 Subject: [PATCH 27/66] Fix AudioDrive detection on Windows 3.1 Sound is not working, and neither is MPU-401 --- src/sound/snd_ess.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 7f48c95c0..400983aba 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -320,9 +320,6 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x3e: return mixer->regs[mixer->index]; - case 0x64: - return mixer->regs[mixer->index] & 7; - case 0x40: { uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos]; From 2e9e20c0787a37301493c3c7896ebd73cb08097a Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 5 Mar 2024 14:06:20 +0600 Subject: [PATCH 28/66] Deal with edge cases where drivers use non-ESS playback route --- src/sound/snd_sb_dsp.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index fa3133768..90d66c144 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -353,11 +353,27 @@ sb_add_data(sb_dsp_t *dsp, uint8_t v) dsp->sb_read_wp &= 0xff; } +static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp) +{ + unsigned int r; + + r = (unsigned int)ESSreg(0xA5) << 8U; + r |= (unsigned int)ESSreg(0xA4); + + /* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */ + return 0x10000U-r; +} + void sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { dsp->sb_pausetime = -1; + if (dsp->ess_reload_len) { + len = sb_ess_get_dma_len(dsp); + dsp->ess_reload_len = 0; + } + if (dma8) { dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; @@ -392,6 +408,11 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { + if (dsp->ess_reload_len) { + len = sb_ess_get_dma_len(dsp); + dsp->ess_reload_len = 0; + } + if (dma8) { dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; @@ -419,18 +440,6 @@ 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)); } - -static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp) -{ - unsigned int r; - - r = (unsigned int)ESSreg(0xA5) << 8U; - r |= (unsigned int)ESSreg(0xA4); - - /* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */ - return 0x10000U-r; -} - void sb_start_dma_ess(sb_dsp_t* dsp) { From 0362f563f6ffbac876a002c7e552885cbf358d34 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 5 Mar 2024 16:14:50 +0600 Subject: [PATCH 29/66] Some fixes --- src/sound/snd_ess.c | 4 ++-- src/sound/snd_sb_dsp.c | 43 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 400983aba..57ba092c3 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -250,7 +250,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } default: - //sb_log("ess: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + pclog("ess: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -334,7 +334,7 @@ ess_mixer_read(uint16_t addr, void *priv) break; } - return 0x00; + return 0x0a; } void diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 90d66c144..7cc189d30 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -585,15 +585,53 @@ sb_16_write_dma(void *priv, uint16_t val) void sb_dsp_setirq(sb_dsp_t *dsp, int irq) { + uint8_t t = 0x00; sb_dsp_log("IRQ now: %i\n", irq); dsp->sb_irqnum = irq; + + /* legacy audio interrupt control */ + t = 0x80;/*game compatible IRQ*/ + switch (dsp->sb_irqnum) { + case 5: t |= 0x5; break; + case 7: t |= 0xA; break; + case 10: t |= 0xF; break; + } + ESSreg(0xB1) = t; + + /* DRQ control */ + t = 0x80;/*game compatible DRQ */ + switch (dsp->sb_8_dmanum) { + case 0: t |= 0x5; break; + case 1: t |= 0xA; break; + case 3: t |= 0xF; break; + } + ESSreg(0xB2) = t; } void sb_dsp_setdma8(sb_dsp_t *dsp, int dma) { + uint8_t t = 0x00; sb_dsp_log("8-bit DMA now: %i\n", dma); dsp->sb_8_dmanum = dma; + + /* legacy audio interrupt control */ + t = 0x80;/*game compatible IRQ*/ + switch (dsp->sb_irqnum) { + case 5: t |= 0x5; break; + case 7: t |= 0xA; break; + case 10: t |= 0xF; break; + } + ESSreg(0xB1) = t; + + /* DRQ control */ + t = 0x80;/*game compatible DRQ */ + switch (dsp->sb_8_dmanum) { + case 0: t |= 0x5; break; + case 1: t |= 0xA; break; + case 3: t |= 0xF; break; + } + ESSreg(0xB2) = t; } void @@ -661,7 +699,7 @@ static void sb_ess_update_autolen(sb_dsp_t *dsp) { static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) { uint8_t chg = 0x00; - sb_dsp_log("ESS register write reg=%02xh val=%02xh\n",reg,data); + pclog("ESS register write reg=%02xh val=%02xh\n",reg,data); switch (reg) { case 0xA1: /* Extended Mode Sample Rate Generator */ @@ -798,6 +836,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_8051_ram[0x20] = dsp->sb_command; if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { + pclog("dsp->sb_command = 0x%X\n", dsp->sb_command); if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) { dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6); return; @@ -1365,7 +1404,7 @@ sb_write(uint16_t a, uint8_t v, void *priv) if (dsp->sb_command <= 0xC0) { sb_commands[dsp->sb_command] = 1; } else { - sb_commands[dsp->sb_command] = 0; + sb_commands[dsp->sb_command] = -1; } } } From f4c2a9c3ac6daffba83d395ae3b64bc672cd31e3 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 5 Mar 2024 21:43:57 +0600 Subject: [PATCH 30/66] Logging aids --- src/sound/snd_ess.c | 6 +++++- src/sound/snd_sb_dsp.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 57ba092c3..a97d5e244 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -151,6 +151,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2); } else { mixer->regs[mixer->index] = val; + pclog("ess: Register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); switch (mixer->index) { /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */ @@ -197,6 +198,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0x40: { + break; uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] & 0x38) << 1); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); /* This doesn't work yet. */ @@ -318,6 +320,7 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x36: case 0x38: case 0x3e: + pclog("ess: Register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); return mixer->regs[mixer->index]; case 0x40: @@ -326,6 +329,7 @@ ess_mixer_read(uint16_t addr, void *priv) mixer->ess_id_str_pos++; if (mixer->ess_id_str_pos >= 4) mixer->ess_id_str_pos = 0; + pclog("ess: ID READ: %02X (pos %d)\n", val, mixer->ess_id_str_pos); return val; } @@ -491,7 +495,7 @@ ess_1688_init(UNUSED(const device_t *info)) sb_dsp_set_mpu(&ess->dsp, ess->mpu); ess->gameport = gameport_add(&gameport_pnp_device); - ess->gameport_addr = 0x000; + ess->gameport_addr = 0x200; gameport_remap(ess->gameport, ess->gameport_addr); return ess; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 7cc189d30..a6069226d 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -834,9 +834,9 @@ sb_exec_command(sb_dsp_t *dsp) See https://github.com/joncampbell123/dosbox-x/issues/1044 */ if (dsp->sb_type >= SB16) dsp->sb_8051_ram[0x20] = dsp->sb_command; + pclog("dsp->sb_command = 0x%X\n", dsp->sb_command); if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { - pclog("dsp->sb_command = 0x%X\n", dsp->sb_command); if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) { dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6); return; From b8ff131996de0538bc6b5c6cb86c050d7ac803bd Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 5 Mar 2024 23:41:38 +0600 Subject: [PATCH 31/66] More changes --- src/sound/snd_sb_dsp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index a6069226d..44d9d0950 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -710,10 +710,6 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) else dsp->sb_freq = 397700UL / (128ul - data); - if (dsp->sb_16_enable || dsp->sb_8_enable) { - sb_stop_dma_ess(dsp); - sb_start_dma_ess(dsp); - } break; } case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */ @@ -742,8 +738,15 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) ESSreg(reg) = data; if (chg & 0x3) { if (dsp->sb_16_enable || dsp->sb_8_enable) { - sb_stop_dma_ess(dsp); - sb_start_dma_ess(dsp); + 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; From c76ada30b7105f09700b9fe0f6e72248d1c5bc8d Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Tue, 5 Mar 2024 21:22:15 -0300 Subject: [PATCH 32/66] Some cleanup, implementing IRQ and DMA channel register update --- src/sound/snd_ess.c | 62 +++++++++++++++---------- src/sound/snd_sb_dsp.c | 102 +++++++++++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index a97d5e244..0193c69c9 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -6,16 +6,20 @@ * * This file is part of the 86Box distribution. * - * Sound Blaster emulation. + * ESS AudioDrive emulation. * * * * Authors: Sarah Walker, * Miran Grca, * TheCollector1995, + * Cacodemon345, + * Kagamiin~, * * Copyright 2008-2020 Sarah Walker. * Copyright 2016-2020 Miran Grca. + * Copyright 2024 Cacodemon345 + * Copyright 2024 Kagamiin~ */ #include #include @@ -79,7 +83,7 @@ typedef struct ess_mixer_t { uint8_t regs[256]; uint8_t ess_id_str[256]; - uint8_t ess_id_str_pos : 2; + uint8_t ess_id_str_pos; } ess_mixer_t; typedef struct ess_t { @@ -130,7 +134,10 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->index = val; mixer->regs[0x01] = val; if (val == 0x40) + { + pclog("ess: Mixer addr 0x40 selected, ID string offset reset\n"); mixer->ess_id_str_pos = 0; + } } else { if (mixer->index == 0) { /* Reset */ @@ -151,7 +158,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2); } else { mixer->regs[mixer->index] = val; - pclog("ess: Register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + pclog("ess: Mixer Register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); switch (mixer->index) { /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */ @@ -198,23 +205,27 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0x40: { - break; - uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] & 0x38) << 1); - gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); - /* This doesn't work yet. */ - /* - io_removehandler(0x0388, 0x0004, - ess->opl.read, NULL, NULL, - ess->opl.write, NULL, NULL, - ess->opl.priv); - if (mixer->regs[0x40] & 1) { - io_sethandler(0x0388, 0x0004, - ess->opl.read, NULL, NULL, - ess->opl.write, NULL, NULL, - ess->opl.priv); - }*/ + /* TODO: Implement "Read-Sequence-Key" method of software address selection + * (needed for ESSCFG.EXE to work properly) */ - switch ((mixer->regs[0x40] >> 5) & 7) { + uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30); + gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); + + /* This doesn't work yet. */ +#if 1 + 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); + } +#endif + switch ((mixer->regs[0x40] >> 5) & 0x7) { case 0: mpu401_change_addr(ess->mpu, 0x00); mpu401_setirq(ess->mpu, -1); @@ -252,7 +263,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } default: - pclog("ess: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + pclog("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -320,21 +331,22 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x36: case 0x38: case 0x3e: - pclog("ess: Register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + pclog("ess: Mixer Register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); return mixer->regs[mixer->index]; case 0x40: { uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos]; + uint8_t pos_log = mixer->ess_id_str_pos; /* TODO remove */ mixer->ess_id_str_pos++; if (mixer->ess_id_str_pos >= 4) mixer->ess_id_str_pos = 0; - pclog("ess: ID READ: %02X (pos %d)\n", val, mixer->ess_id_str_pos); + pclog("ess: ID READ: %02X (pos %d)\n", val, pos_log); return val; } default: - pclog("ess: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } @@ -491,7 +503,7 @@ ess_1688_init(UNUSED(const device_t *info)) } ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); - mpu401_init(ess->mpu, 0, 0, M_UART, 1); + mpu401_init(ess->mpu, 0, -1, M_UART, 1); sb_dsp_set_mpu(&ess->dsp, ess->mpu); ess->gameport = gameport_add(&gameport_pnp_device); @@ -620,4 +632,4 @@ const device_t ess_1688_device = { .speed_changed = ess_speed_changed, .force_redraw = NULL, .config = ess_config -}; \ No newline at end of file +}; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 44d9d0950..d14b41d9e 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -582,6 +582,37 @@ sb_16_write_dma(void *priv, uint16_t val) 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 5: t |= 0x5; break; + case 7: t |= 0xA; break; + case 10: t |= 0xF; break; + } + pclog("ESSreg 0xB1 was %02X, irqnum is %d, t is %02X; new 0xB1 is %02X\n", ESSreg(0xB1), dsp->sb_irqnum, t, (ESSreg(0xB1) & 0xF0) | t); + 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) { @@ -589,23 +620,7 @@ sb_dsp_setirq(sb_dsp_t *dsp, int irq) sb_dsp_log("IRQ now: %i\n", irq); dsp->sb_irqnum = irq; - /* legacy audio interrupt control */ - t = 0x80;/*game compatible IRQ*/ - switch (dsp->sb_irqnum) { - case 5: t |= 0x5; break; - case 7: t |= 0xA; break; - case 10: t |= 0xF; break; - } - ESSreg(0xB1) = t; - - /* DRQ control */ - t = 0x80;/*game compatible DRQ */ - switch (dsp->sb_8_dmanum) { - case 0: t |= 0x5; break; - case 1: t |= 0xA; break; - case 3: t |= 0xF; break; - } - ESSreg(0xB2) = t; + sb_ess_update_irq_drq_readback_regs(dsp, true); } void @@ -615,23 +630,7 @@ sb_dsp_setdma8(sb_dsp_t *dsp, int dma) sb_dsp_log("8-bit DMA now: %i\n", dma); dsp->sb_8_dmanum = dma; - /* legacy audio interrupt control */ - t = 0x80;/*game compatible IRQ*/ - switch (dsp->sb_irqnum) { - case 5: t |= 0x5; break; - case 7: t |= 0xA; break; - case 10: t |= 0xF; break; - } - ESSreg(0xB1) = t; - - /* DRQ control */ - t = 0x80;/*game compatible DRQ */ - switch (dsp->sb_8_dmanum) { - case 0: t |= 0x5; break; - case 1: t |= 0xA; break; - case 3: t |= 0xF; break; - } - ESSreg(0xB2) = t; + sb_ess_update_irq_drq_readback_regs(dsp, true); } void @@ -686,6 +685,7 @@ static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) { switch (reg) { default: + pclog("ESS register read reg=%02xh val=%02xh\n",reg, ESSreg(reg)); return ESSreg(reg); } @@ -752,9 +752,43 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) 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) */ From d3aa111ba3b4e93667fdcf1181459621539225c5 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 8 Mar 2024 12:13:09 -0300 Subject: [PATCH 33/66] Fix bug in command length override; fix some other stuff; logging galore --- src/sound/snd_ess.c | 8 +++++ src/sound/snd_sb_dsp.c | 74 +++++++++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 0193c69c9..3de6615a3 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -335,7 +335,10 @@ ess_mixer_read(uint16_t addr, void *priv) return mixer->regs[mixer->index]; case 0x40: + + if (0) { + // not on ES1688 uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos]; uint8_t pos_log = mixer->ess_id_str_pos; /* TODO remove */ mixer->ess_id_str_pos++; @@ -344,6 +347,11 @@ ess_mixer_read(uint16_t addr, void *priv) pclog("ess: ID READ: %02X (pos %d)\n", val, pos_log); return val; } + else + { + pclog("ess: Mixer Register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + return mixer->regs[mixer->index]; + } default: pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index d14b41d9e..fdcaf4587 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -228,7 +228,10 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) /* 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 + { + pclog("ess: IRQ was masked"); return; + } } if (set && !masked) @@ -240,6 +243,7 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) void sb_irq(sb_dsp_t *dsp, int irq8) { + pclog("sb: IRQ raised\n"); sb_update_status(dsp, !irq8, 1); } @@ -330,6 +334,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 @@ -596,7 +603,6 @@ sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy) case 7: t |= 0xA; break; case 10: t |= 0xF; break; } - pclog("ESSreg 0xB1 was %02X, irqnum is %d, t is %02X; new 0xB1 is %02X\n", ESSreg(0xB1), dsp->sb_irqnum, t, (ESSreg(0xB1) & 0xF0) | t); ESSreg(0xB1) = (ESSreg(0xB1) & 0xF0) | t; /* DRQ control */ @@ -704,11 +710,16 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) 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; + pclog("ess: Sample rate - %ihz (%f)\n", dsp->sb_freq, dsp->sblatcho); break; } @@ -720,6 +731,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) case 0xA5: /* DMA Transfer Count Reload (high) */ ESSreg(reg) = data; sb_ess_update_autolen(dsp); + pclog("ess: DMA Transfer Count Reload length set to %d samples\n", sb_ess_get_dma_len(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; @@ -855,6 +867,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) break; default: + pclog("UNKNOWN ESS register write reg=%02xh val=%02xh\n",reg,data); break; } } @@ -870,8 +883,21 @@ 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) + { dsp->sb_8051_ram[0x20] = dsp->sb_command; - pclog("dsp->sb_command = 0x%X\n", dsp->sb_command); + } + + { + int i; + char data_s[256]; + data_s[0] = '\0'; + + for (i = 0; i < sb_commands[dsp->sb_command]; i++) + { + snprintf(data_s, 256, " 0x%02X", dsp->sb_data[i]); + } + pclog("dsp->sb_command = 0x%02X%s, length %d\n", dsp->sb_command, data_s, sb_commands[dsp->sb_command]); + } if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) { @@ -1083,7 +1109,8 @@ 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); + pclog("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; @@ -1095,7 +1122,8 @@ sb_exec_command(sb_dsp_t *dsp) 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); + pclog("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; @@ -1396,6 +1424,8 @@ sb_write(uint16_t a, uint8_t v, void *priv) if (dsp->sb_type < SB16 && (!IS_ESS(dsp) || (IS_ESS(dsp) && ((a & 0xF) != 0xE)))) a &= 0xfffe; + //pclog("sb: port write %03x %02x\n", a, v); + switch (a & 0xF) { case 6: /* Reset */ if (!dsp->uart_midi) { @@ -1428,8 +1458,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) @@ -1444,6 +1472,8 @@ sb_write(uint16_t a, uint8_t v, void *priv) 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); @@ -1469,7 +1499,13 @@ sb_read(uint16_t a, void *priv) /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ if (dsp->sb_type < SB16) - a &= 0xfffe; + { + /* 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 */ @@ -1509,6 +1545,10 @@ sb_read(uint16_t a, void *priv) break; case 0xE: /* Read data ready */ dsp->irq_update(dsp->irq_priv, 0); + if (dsp->sb_irq8 || dsp->sb_irq16) + { + pclog("sb: IRQ acknowledged\n"); + } dsp->sb_irq8 = dsp->sb_irq16 = 0; /* 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)) { @@ -1520,16 +1560,25 @@ 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)) + { + if (dsp->sb_irq16) + { + pclog("sb: 16-bit IRQ acknowledged"); + } + 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; default: break; } + + //pclog("sb: port read %03x %02x\n", a, ret); return ret; } @@ -1634,6 +1683,7 @@ 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)); + } void @@ -1692,6 +1742,7 @@ sb_ess_finish_dma(sb_dsp_t *dsp) return; ESSreg(0xB8) &= ~0x01; dma_set_drq(dsp->sb_8_dmanum, 0); + pclog("ess: DMA finished"); } void @@ -1886,6 +1937,7 @@ pollsb(void *priv) break; } + if (dsp->sb_8_length < 0) { if (dsp->sb_8_autoinit) dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen; From 6d3f2c478b97299c41d0735367b2e33069410b5f Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 9 Mar 2024 19:14:36 -0300 Subject: [PATCH 34/66] Fix port 388h being disabled erroneously; set filter freq on sample rate change --- src/sound/snd_ess.c | 30 +++++++++++++++--------------- src/sound/snd_sb_dsp.c | 5 +++++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 3de6615a3..f771ee6db 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -135,7 +135,6 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[0x01] = val; if (val == 0x40) { - pclog("ess: Mixer addr 0x40 selected, ID string offset reset\n"); mixer->ess_id_str_pos = 0; } } else { @@ -211,20 +210,21 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); - /* This doesn't work yet. */ -#if 1 - io_removehandler(0x0388, 0x0004, - ess->opl.read, NULL, NULL, - ess->opl.write, NULL, NULL, - ess->opl.priv); - if ((mixer->regs[0x40] & 0x1) != 0) + if (0) { - io_sethandler(0x0388, 0x0004, - ess->opl.read, NULL, NULL, - ess->opl.write, NULL, NULL, - ess->opl.priv); + /* 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); + } } -#endif switch ((mixer->regs[0x40] >> 5) & 0x7) { case 0: mpu401_change_addr(ess->mpu, 0x00); @@ -338,7 +338,7 @@ ess_mixer_read(uint16_t addr, void *priv) if (0) { - // not on ES1688 + /* not on ES1688 */ uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos]; uint8_t pos_log = mixer->ess_id_str_pos; /* TODO remove */ mixer->ess_id_str_pos++; @@ -475,6 +475,7 @@ ess_1688_init(UNUSED(const device_t *info)) 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, @@ -502,7 +503,6 @@ ess_1688_init(UNUSED(const device_t *info)) if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); - { ess->mixer_sbpro.ess_id_str[0] = 0x16; ess->mixer_sbpro.ess_id_str[1] = 0x88; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index fdcaf4587..3d150b9f9 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -716,6 +716,11 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_freq = 795500UL / (256ul - data); else dsp->sb_freq = 397700UL / (128ul - data); + // TODO: check if this command updates the filter or not + sb_ess_update_filter_freq(dsp); + ESSreg(reg) = data; /* HACK: sb_ess_update_filter_freq updates 0xA1. + * I'm not sure if that could cause an off by one + * error, so this is just to be safe. */ temp = 1000000.0 / dsp->sb_freq; dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp; dsp->sb_timei = dsp->sb_timeo; From b59db332f0152b3271351fd2c4518137e902b457 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 9 Mar 2024 23:55:36 -0300 Subject: [PATCH 35/66] Implement registers 0xC2/0xC3; sound now works in Win3.1 --- src/sound/snd_sb_dsp.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 3d150b9f9..0b55953da 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -868,6 +868,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) 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 */ ESSreg(reg) = data; break; @@ -909,9 +910,18 @@ sb_exec_command(sb_dsp_t *dsp) dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6); return; } - if (dsp->sb_command == 0xC0) { + 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 == 0xC0) { sb_add_data(dsp, sb_ess_read_reg(dsp, dsp->sb_data[0])); - } else if (dsp->sb_command < 0xC0 && dsp->ess_extended_mode) { + } + else if (dsp->sb_command < 0xC0 && dsp->ess_extended_mode) { sb_ess_write_reg(dsp, dsp->sb_command, dsp->sb_data[0]); } return; @@ -1471,7 +1481,7 @@ sb_write(uint16_t a, uint8_t v, void *priv) sb_commands[dsp->sb_command] = 2; } if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { - if (dsp->sb_command <= 0xC0) { + if (dsp->sb_command <= 0xC0 || dsp->sb_command == 0xC2) { sb_commands[dsp->sb_command] = 1; } else { sb_commands[dsp->sb_command] = -1; From 34be04ab800321ce797cf1634ee5318e52f63c10 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sun, 10 Mar 2024 15:32:57 -0300 Subject: [PATCH 36/66] Implementing command 0xF2 IRQ masking behavior --- src/include/86box/snd_sb_dsp.h | 2 ++ src/sound/snd_sb_dsp.c | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 2b39a0303..4c0aab113 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -130,6 +130,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; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 0b55953da..30931f3d6 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -209,6 +209,15 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) { int masked = 0; + if (dsp->sb_irq8 || dsp->sb_irq16) + return; + + if (!(ESSreg(0xB1) & 0x10)) // if ESS playback, and IRQ disabled, do not fire + { + pclog("ess: IRQ was masked"); + return; + } + switch (bit) { default: case 0: @@ -599,6 +608,7 @@ sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool 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; @@ -627,6 +637,8 @@ sb_dsp_setirq(sb_dsp_t *dsp, int irq) dsp->sb_irqnum = irq; sb_ess_update_irq_drq_readback_regs(dsp, true); + + ESSreg(0xB1) = (ESSreg(0xB1) & 0xEF) | 0x10; } void @@ -1384,7 +1396,15 @@ sb_exec_command(sb_dsp_t *dsp) 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)) + pclog("F2 already written\n"); + else { + timer_set_delay_u64(&dsp->irq_timer, (100ULL * TIMER_USEC)); + pclog("F2 written\n"); + } + } else + sb_irq(dsp, 1); break; case 0xF3: /* Trigger 16-bit IRQ */ sb_dsp_log("Trigger IRQ\n"); @@ -1483,6 +1503,8 @@ sb_write(uint16_t a, uint8_t v, void *priv) if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { if (dsp->sb_command <= 0xC0 || dsp->sb_command == 0xC2) { sb_commands[dsp->sb_command] = 1; + } else if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) { + sb_commands[dsp->sb_command] = 0; } else { sb_commands[dsp->sb_command] = -1; } @@ -1652,6 +1674,14 @@ 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); +} + void sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) { @@ -1680,6 +1710,7 @@ 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. */ From 7c998872935b4582df029b3e503a1dc0fa6b63b6 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Mon, 11 Mar 2024 18:49:10 -0300 Subject: [PATCH 37/66] Implementing ESS DMA counter; handling disable of auto-init while DMA is turned on --- src/include/86box/snd_sb_dsp.h | 1 + src/sound/snd_sb_dsp.c | 178 +++++++++++++++++++++++++++------ 2 files changed, 148 insertions(+), 31 deletions(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 4c0aab113..4833dea33 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -146,6 +146,7 @@ typedef struct sb_dsp_t { uint8_t ess_playback_mode; uint8_t ess_extended_mode; uint8_t ess_reload_len; + uint32_t ess_dma_counter; mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 30931f3d6..ebda12652 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -212,9 +212,11 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) if (dsp->sb_irq8 || dsp->sb_irq16) return; - if (!(ESSreg(0xB1) & 0x10)) // if ESS playback, and IRQ disabled, do not fire + /* 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 { - pclog("ess: IRQ was masked"); + pclog("ess: IRQ was masked\n"); return; } @@ -238,7 +240,7 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) if (dsp->ess_playback_mode && bit <= 1 && set && !masked) { if (!(ESSreg(0xB1) & 0x40)) // if ESS playback, and IRQ disabled, do not fire { - pclog("ess: IRQ was masked"); + pclog("ess: IRQ was masked\n"); return; } } @@ -369,15 +371,19 @@ 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) { - unsigned int r; - - r = (unsigned int)ESSreg(0xA5) << 8U; - r |= (unsigned int)ESSreg(0xA4); - - /* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */ - return 0x10000U-r; + return 0x10000U - sb_ess_get_dma_counter(dsp); } void @@ -385,11 +391,6 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { dsp->sb_pausetime = -1; - if (dsp->ess_reload_len) { - len = sb_ess_get_dma_len(dsp); - dsp->ess_reload_len = 0; - } - if (dma8) { dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; @@ -424,11 +425,6 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { - if (dsp->ess_reload_len) { - len = sb_ess_get_dma_len(dsp); - dsp->ess_reload_len = 0; - } - if (dma8) { dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; @@ -460,12 +456,8 @@ void sb_start_dma_ess(sb_dsp_t* dsp) { uint8_t real_format = 0; - uint32_t len = !(ESSreg(0xB7) & 4) ? dsp->sb_8_length : dsp->sb_16_length; - - if (dsp->ess_reload_len || len <= 0) { - len = sb_ess_get_dma_len(dsp); - dsp->ess_reload_len = 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); @@ -748,7 +740,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) case 0xA5: /* DMA Transfer Count Reload (high) */ ESSreg(reg) = data; sb_ess_update_autolen(dsp); - pclog("ess: DMA Transfer Count Reload length set to %d samples\n", sb_ess_get_dma_len(dsp)); + pclog("ess: DMA Transfer Count Reload length set to %d samples (0x%x)\n", sb_ess_get_dma_len(dsp), sb_ess_get_dma_counter(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; @@ -871,6 +863,18 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) 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); @@ -1060,8 +1064,12 @@ 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--; + 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) @@ -1072,6 +1080,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 */ @@ -1177,8 +1186,12 @@ 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--; + 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); @@ -1188,14 +1201,19 @@ 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--; + 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 */ @@ -1203,6 +1221,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 */ @@ -1822,6 +1841,7 @@ pollsb(void *priv) } 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); @@ -1839,6 +1859,7 @@ pollsb(void *priv) } 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); @@ -1848,6 +1869,7 @@ pollsb(void *priv) 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); @@ -1857,6 +1879,7 @@ pollsb(void *priv) dsp->sbdatl = data[0] << 8; dsp->sbdatr = data[1] << 8; dsp->sb_8_length -= 2; + dsp->ess_dma_counter += 2; break; case ADPCM_4: @@ -1886,6 +1909,7 @@ pollsb(void *priv) dsp->sbdacpos = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; + dsp->ess_dma_counter++; } if (dsp->stereo) { @@ -1929,6 +1953,7 @@ pollsb(void *priv) dsp->sbdacpos = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; + dsp->ess_dma_counter++; } if (dsp->stereo) { @@ -1965,6 +1990,8 @@ pollsb(void *priv) 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) { @@ -1984,7 +2011,7 @@ pollsb(void *priv) } - 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 { @@ -1994,6 +2021,26 @@ pollsb(void *priv) } sb_irq(dsp, 1); } + 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); + pclog("IRQ fired via ESS DMA counter, next IRQ in %d samples\n", sb_ess_get_dma_len(dsp)); + } + } + 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) { sb_dsp_update(dsp); @@ -2005,6 +2052,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); @@ -2012,6 +2060,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); @@ -2021,6 +2070,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); @@ -2030,13 +2080,14 @@ 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; @@ -2047,6 +2098,25 @@ pollsb(void *priv) } sb_irq(dsp, 0); } + 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); + } + } + 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--; @@ -2072,12 +2142,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; @@ -2085,6 +2157,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; @@ -2092,6 +2165,7 @@ 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; @@ -2100,7 +2174,7 @@ sb_poll_i(void *priv) 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 { @@ -2110,6 +2184,25 @@ sb_poll_i(void *priv) } sb_irq(dsp, 1); } + 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); + } + } + 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; } if (dsp->sb_16_enable && !dsp->sb_16_pause && (dsp->sb_pausetime < 0LL) && !dsp->sb_16_output) { @@ -2118,6 +2211,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; @@ -2125,6 +2219,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; @@ -2133,6 +2228,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; @@ -2141,6 +2237,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; @@ -2149,7 +2246,7 @@ 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 { @@ -2159,6 +2256,25 @@ sb_poll_i(void *priv) } sb_irq(dsp, 0); } + 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); + } + } + 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; } /* Assume this is direct mode */ From 7ad48f8d2996b5ced373b19dbd4f80a0d9827647 Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 12 Mar 2024 10:10:00 -0300 Subject: [PATCH 38/66] Switching filter implementation to use SB16 filters; fixing CD audio volume --- src/sound/snd_ess.c | 11 ++---- src/sound/snd_sb_dsp.c | 88 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index f771ee6db..9996ad006 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -150,7 +150,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) /* Initialize ESS regs. */ mixer->regs[0x14] = mixer->regs[0x32] = 0x88; mixer->regs[0x36] = 0x88; - mixer->regs[0x38] = 0x00; + mixer->regs[0x38] = 0x88; mixer->regs[0x3a] = 0x00; mixer->regs[0x3e] = 0x00; @@ -384,8 +384,8 @@ ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */ if (mixer->output_filter) { - out_l += (sb_iir(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.9; - out_r += (sb_iir(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.9; + 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; @@ -446,10 +446,7 @@ ess_filter_cd_audio(int channel, double *buffer, void *priv) double cd = channel ? mixer->cd_r : mixer->cd_l; double master = channel ? mixer->master_r : mixer->master_l; - if (mixer->output_filter) - c = (sb_iir(2, channel, *buffer) * cd) / 3.9; - else - c = (*buffer * cd) / 3.0; + c = (*buffer * cd) / 3.0; *buffer = c * master; } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index ebda12652..f179ce284 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -175,6 +175,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) { @@ -678,17 +709,29 @@ 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); - unsigned int freq = ((dsp->sb_freq * 4) / (5 * 2)); /* 80% of 1/2 the sample rate */ - ESSreg(0xA2) = 256 - (7160000 / (freq * 82)); + sb_ess_update_reg_a2(dsp, (uint8_t) temp); } static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) @@ -720,20 +763,14 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_freq = 795500UL / (256ul - data); else dsp->sb_freq = 397700UL / (128ul - data); - // TODO: check if this command updates the filter or not - sb_ess_update_filter_freq(dsp); - ESSreg(reg) = data; /* HACK: sb_ess_update_filter_freq updates 0xA1. - * I'm not sure if that could cause an off by one - * error, so this is just to be safe. */ temp = 1000000.0 / dsp->sb_freq; dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp; dsp->sb_timei = dsp->sb_timeo; pclog("ess: Sample rate - %ihz (%f)\n", dsp->sb_freq, dsp->sblatcho); - break; } case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */ - ESSreg(reg) = data; + sb_ess_update_reg_a2(dsp, data); break; case 0xA4: /* DMA Transfer Count Reload (low) */ @@ -1147,6 +1184,7 @@ sb_exec_command(sb_dsp_t *dsp) temp = 1000000 / temp; sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); pclog("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); + // if ((dsp->sb_freq != temp) && (IS_ESS(dsp) || (dsp->sb_type >= SB16))) if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) recalc_sb16_filter(0, temp); dsp->sb_freq = temp; @@ -1165,7 +1203,7 @@ sb_exec_command(sb_dsp_t *dsp) 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; @@ -1731,14 +1769,30 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) 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 */ From 0ed203cbd50dc697a453ffebdf8722db8420308f Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Tue, 12 Mar 2024 18:12:57 -0300 Subject: [PATCH 39/66] Mixer functions; recording (incomplete/commented out); set default IRQ to 5 --- src/include/86box/snd_sb.h | 16 +-- src/sound/snd_ess.c | 211 +++++++++++++++++++++++++++++++------ 2 files changed, 188 insertions(+), 39 deletions(-) diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h index 621cb4ade..f236c284a 100644 --- a/src/include/86box/snd_sb.h +++ b/src/include/86box/snd_sb.h @@ -105,13 +105,15 @@ typedef struct sb_ct1745_mixer_t { int input_selector_left; int input_selector_right; -#define INPUT_MIC 1 -#define INPUT_CD_R 2 -#define INPUT_CD_L 4 -#define INPUT_LINE_R 8 -#define INPUT_LINE_L 16 -#define INPUT_MIDI_R 32 -#define INPUT_MIDI_L 64 +#define INPUT_MIC 1 +#define INPUT_CD_R 2 +#define INPUT_CD_L 4 +#define INPUT_LINE_R 8 +#define INPUT_LINE_L 16 +#define INPUT_MIDI_R 32 +#define INPUT_MIDI_L 64 +#define INPUT_MIXER_L 128 +#define INPUT_MIXER_R 256 int mic_agc; diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 9996ad006..0f57bca7d 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -21,6 +21,7 @@ * Copyright 2024 Cacodemon345 * Copyright 2024 Kagamiin~ */ + #include #include #include @@ -48,6 +49,8 @@ #include <86box/snd_sb.h> #include <86box/plat_unused.h> + + static const double sb_att_4dbstep_3bits[] = { 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0 }; @@ -56,6 +59,11 @@ static const double sb_att_7dbstep_2bits[] = { 164.0, 6537.0, 14637.0, 32767.0 }; +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 +}; + /* SB PRO */ typedef struct ess_mixer_t { double master_l; @@ -68,7 +76,10 @@ typedef struct ess_mixer_t { double cd_r; double line_l; double line_r; - double mic; + double mic_l; + double mic_r; + double auxb_l; + double auxb_r; /*see sb_ct1745_mixer for values for input selector*/ int32_t input_selector; @@ -84,6 +95,12 @@ typedef struct ess_mixer_t { uint8_t ess_id_str[256]; uint8_t ess_id_str_pos; + +#if 0 + int record_pos_write_cd; + double record_pos_write_cd_sigma; + int record_pos_write_music; +#endif } ess_mixer_t; typedef struct ess_t { @@ -166,11 +183,57 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) 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] / 32768.0; + mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2); + 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; + } + break; + case 0x14: mixer->regs[0x4] = val & 0xee; break; + case 0x1A: + mixer->mic_l = sb_att_1p4dbstep_4bits[(mixer->regs[0x1A] >> 4) & 0xF]; + mixer->mic_r = sb_att_1p4dbstep_4bits[mixer->regs[0x1A] & 0xF]; + 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: @@ -192,10 +255,13 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[mixer->index - 0x10] = (val & 0xee); break; + case 0x3a: + break; + case 0x00: case 0x04: - case 0x0a: - case 0x0c: + break; + case 0x0e: break; @@ -268,18 +334,18 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } } - mixer->voice_l = ess_mixer_get_vol_4bit(mixer->regs[0x14]); - mixer->voice_r = ess_mixer_get_vol_4bit(mixer->regs[0x14] >> 4); - mixer->master_l = ess_mixer_get_vol_4bit(mixer->regs[0x32]); - mixer->master_r = ess_mixer_get_vol_4bit(mixer->regs[0x32] >> 4); - mixer->fm_l = ess_mixer_get_vol_4bit(mixer->regs[0x36]); - mixer->fm_r = ess_mixer_get_vol_4bit(mixer->regs[0x36] >> 4); - mixer->cd_l = ess_mixer_get_vol_4bit(mixer->regs[0x38]); - mixer->cd_r = ess_mixer_get_vol_4bit(mixer->regs[0x38] >> 4); - mixer->line_l = ess_mixer_get_vol_4bit(mixer->regs[0x3e]); - mixer->line_r = ess_mixer_get_vol_4bit(mixer->regs[0x3e] >> 4); - - mixer->mic = sb_att_7dbstep_2bits[(mixer->regs[0x0a] >> 1) & 0x3] / 32768.0; + mixer->voice_l = ess_mixer_get_vol_4bit(mixer->regs[0x14] >> 4); + mixer->voice_r = ess_mixer_get_vol_4bit(mixer->regs[0x14]); + mixer->master_l = ess_mixer_get_vol_4bit(mixer->regs[0x32] >> 4); + mixer->master_r = ess_mixer_get_vol_4bit(mixer->regs[0x32]); + mixer->fm_l = ess_mixer_get_vol_4bit(mixer->regs[0x36] >> 4); + mixer->fm_r = ess_mixer_get_vol_4bit(mixer->regs[0x36]); + mixer->cd_l = ess_mixer_get_vol_4bit(mixer->regs[0x38] >> 4); + mixer->cd_r = ess_mixer_get_vol_4bit(mixer->regs[0x38]); + mixer->line_l = ess_mixer_get_vol_4bit(mixer->regs[0x3e] >> 4); + mixer->line_r = ess_mixer_get_vol_4bit(mixer->regs[0x3e]); + mixer->auxb_l = ess_mixer_get_vol_4bit(mixer->regs[0x3a] >> 4); + mixer->auxb_r = ess_mixer_get_vol_4bit(mixer->regs[0x3a]); mixer->output_filter = !(mixer->regs[0xe] & 0x20); mixer->input_filter = !(mixer->regs[0xc] & 0x20); @@ -288,18 +354,6 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) if (mixer->index == 0xe) sb_dsp_set_stereo(&ess->dsp, val & 2); - switch (mixer->regs[0xc] & 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; - } - /* TODO: pcspeaker volume? Or is it not worth? */ } } @@ -406,7 +460,13 @@ void ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) { ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; + const ess_mixer_t *mixer = &ess->mixer_sbpro; +#if 0 + int rec_pos = ess->mixer_sbpro.record_pos_write_music; + int c_record; + int32_t in_l; + int32_t in_r; +#endif double out_l = 0.0; double out_r = 0.0; const int32_t *opl_buf = NULL; @@ -430,24 +490,106 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) out_l *= mixer->master_l; out_r *= mixer->master_r; +#if 0 + // Pull input after applying mixer's master volume scaling + in_l = (mixer->input_selector & INPUT_MIXER_L) ? ((int32_t) out_l) : 0; + in_r = (mixer->input_selector & INPUT_MIXER_R) ? ((int32_t) out_l) : 0; + + if (ess->dsp.sb_enable_i) { + // NOTE: this is nearest-neighbor sampling. This is gonna generate aliasing like HECK. Is this what the real card does? + c_record = rec_pos + ((c * ess->dsp.sb_freq) / MUSIC_FREQ); + + ess->dsp.record_buffer[c_record & 0xfffe] += in_l; + ess->dsp.record_buffer[(c_record & 0xfffe) + 1] += in_r; + + if (ess->dsp.record_buffer[c_record & 0xfffe] < -32768) + { + ess->dsp.record_buffer[c_record & 0xfffe] = -32768; + } + else if (ess->dsp.record_buffer[c_record & 0xfffe] > 32767) + { + ess->dsp.record_buffer[c_record & 0xfffe] = 32767; + } + + if (ess->dsp.record_buffer[(c_record & 0xfffe) + 1] < -32768) + { + ess->dsp.record_buffer[(c_record & 0xfffe) + 1] = -32768; + } + else if (ess->dsp.record_buffer[(c_record & 0xfffe) + 1] > 32767) + { + ess->dsp.record_buffer[(c_record & 0xfffe) + 1] = 32767; + } + } + buffer[c] += (int32_t) out_l; buffer[c + 1] += (int32_t) out_r; +#endif } +#if 0 + ess->mixer_sbpro.record_pos_write_music += ((len * 2 * ess->dsp.sb_freq) / MUSIC_FREQ); + ess->mixer_sbpro.record_pos_write_music &= 0xfffe; + + if (ess->mixer_sbpro.record_pos_write_music < ess->mixer_sbpro.record_pos_write_cd) + { + ess->dsp.record_pos_write = ess->mixer_sbpro.record_pos_write_music; + } +#endif + ess->opl.reset_buffer(ess->opl.priv); } void ess_filter_cd_audio(int channel, double *buffer, void *priv) { - const ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; + const ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; double c; +#if 0 + double rec_pos = ess->mixer_sbpro.record_pos_write_cd; + double rec_pos_sigma = ess->mixer_sbpro.record_pos_write_cd_sigma; + int c_record; + int selector = channel ? INPUT_MIXER_R : INPUT_MIXER_L; + int rec_buf_pos; + int32_t in; +#endif double cd = channel ? mixer->cd_r : mixer->cd_l; double master = channel ? mixer->master_r : mixer->master_l; c = (*buffer * cd) / 3.0; *buffer = c * master; +#if 0 + in = (mixer->input_selector & selector) ? (int32_t)(c * master) : 0; + + if (ess->dsp.sb_enable_i) + { + // NOTE: this is nearest-neighbor sampling. This is gonna generate aliasing like HECK. Is this what the real card does? + c_record = (int)(rec_pos + rec_pos_sigma); + rec_buf_pos = channel ? ((c_record & 0xfffe) + 1) : (c_record & 0xfffe); + + ess->dsp.record_buffer[rec_buf_pos] += in; + + if (ess->dsp.record_buffer[rec_buf_pos] < -32768) + { + ess->dsp.record_buffer[rec_buf_pos] = -32768; + } + else if (ess->dsp.record_buffer[rec_buf_pos] > 32767) + { + ess->dsp.record_buffer[rec_buf_pos] = 32767; + } + } + + ess->mixer_sbpro.record_pos_write_cd += ((2 * (double)ess->dsp.sb_freq) / MUSIC_FREQ) + rec_pos_sigma; + ess->mixer_sbpro.record_pos_write_cd &= ~1; + ess->mixer_sbpro.record_pos_write_cd_sigma = (double)rec_pos + ((2 * (double)ess->dsp.sb_freq) / MUSIC_FREQ) + rec_pos_sigma - ess->mixer_sbpro.record_pos_write_cd; + + ess->mixer_sbpro.record_pos_write_cd &= 0xfffe; + + if (ess->mixer_sbpro.record_pos_write_cd < ess->mixer_sbpro.record_pos_write_music) + { + ess->dsp.record_pos_write = ess->mixer_sbpro.record_pos_write_cd; + } +#endif } static void * @@ -507,6 +649,11 @@ ess_1688_init(UNUSED(const device_t *info)) ess->mixer_sbpro.ess_id_str[3] = addr & 0xff; } +#if 0 + ess->mixer_sbpro.record_pos_write_cd = ess->dsp.record_pos_write; + ess->mixer_sbpro.record_pos_write_music = ess->dsp.record_pos_write; +#endif + ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); mpu401_init(ess->mpu, 0, -1, M_UART, 1); sb_dsp_set_mpu(&ess->dsp, ess->mpu); @@ -561,7 +708,7 @@ static const device_config_t ess_config[] = { .description = "IRQ", .type = CONFIG_SELECTION, .default_string = "", - .default_int = 7, + .default_int = 5, .file_filter = "", .spinner = { 0 }, .selection = { From f4c75226efee7fc23267322f5a9ffd80fd139660 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Wed, 13 Mar 2024 00:05:45 -0300 Subject: [PATCH 40/66] Implementing ESPCM decompression (incomplete) --- src/include/86box/snd_sb_dsp.h | 9 +- src/sound/snd_sb_dsp.c | 200 +++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+), 1 deletion(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 4833dea33..6e5266b43 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -9,7 +9,7 @@ #define SB_SUBTYPE_ESS_ES1688 4 /* ESS Technology ES1688 */ /* ESS-related */ -#define IS_ESS(dsp) ((dsp)->sb_subtype >= SB_SUBTYPE_ESS_ES688) +#define IS_ESS(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_ESS_ES688 || (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 */ @@ -148,6 +148,13 @@ typedef struct sb_dsp_t { uint8_t ess_reload_len; uint32_t ess_dma_counter; + // ESPCM + uint8_t espcm_sample_idx; + uint8_t espcm_range; + uint8_t espcm_byte_buffer[4]; + uint8_t espcm_code_buffer[10]; /* used for ESPCM_3 */ + uint8_t espcm_last_nibble; /* used for ESPCM_3 */ + mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index f179ce284..54c8bcc5e 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -32,6 +32,9 @@ #define ADPCM_4 1 #define ADPCM_26 2 #define ADPCM_2 3 +#define ESPCM_4 4 +#define ESPCM_3 5 +#define ESPCM_1 6 // not ESPCM_2, unlike what the manuals say /*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 @@ -116,6 +119,25 @@ uint8_t adjustMap2[24] = { 252, 0, 252, 0 }; +uint8_t espcm_range_map[256] = { + -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 +}; + double low_fir_sb16_coef[4][SB16_NCoef]; #ifdef ENABLE_SB_DSP_LOG @@ -1216,6 +1238,78 @@ 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 */ + if (!IS_ESS(dsp)) + { + break; + } + dsp->sbref = dsp->dma_readb(dsp->dma_priv); + dsp->sb_8_length--; + dsp->ess_dma_counter++; + fallthrough; + case 0x64: /* 4-bit ESPCM output */ + if (IS_ESS(dsp)) + { + sb_start_dma(dsp, 1, 0, ESPCM_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 0x67: /* 3-bit ESPCM output with reference */ + if (!IS_ESS(dsp)) + { + break; + } + dsp->sbref = dsp->dma_readb(dsp->dma_priv); + dsp->sb_8_length--; + dsp->ess_dma_counter++; + fallthrough; + case 0x66: /* 3-bit ESPCM output */ + if (IS_ESS(dsp)) + { + sb_start_dma(dsp, 1, 0, ESPCM_3, 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 0x6B: /* 1-bit ESPCM output with reference */ + if (!IS_ESS(dsp)) + { + break; + } + dsp->sbref = dsp->dma_readb(dsp->dma_priv); + dsp->sb_8_length--; + dsp->ess_dma_counter++; + fallthrough; + case 0x6A: /* 1-bit ESPCM output */ + if (IS_ESS(dsp)) + { + sb_start_dma(dsp, 1, 0, ESPCM_1, 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 0x6F: /* 4-bit ESPCM input with reference */ + if (!IS_ESS(dsp)) + { + break; + } + dsp->sbref = (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80; + dsp->record_pos_read += 2; + dsp->record_pos_read &= 0xFFFF; + fallthrough; + case 0x6E: /* 4-bit ESPCM input */ + if (IS_ESS(dsp)) + { + sb_start_dma_i(dsp, 1, 0, ESPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); + dsp->sbdat2 = (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80; + dsp->record_pos_read += 2; + dsp->record_pos_read &= 0xFFFF; + } + break; case 0x75: /* 4-bit ADPCM output with reference */ dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; @@ -2060,6 +2154,112 @@ pollsb(void *priv) 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) + { + dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); + dsp->espcm_sample_idx++; + dsp->sb_8_length--; + dsp->ess_dma_counter++; + + dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F; + tempi = dsp->espcm_byte_buffer[0] >> 4; + } + else if (dsp->espcm_sample_idx & 1) + { + dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); + dsp->espcm_sample_idx++; + dsp->sb_8_length--; + dsp->ess_dma_counter++; + + tempi = dsp->espcm_byte_buffer[0] & 0x0F; + } + else + { + dsp->espcm_sample_idx++; + tempi = dsp->espcm_byte_buffer[0] >> 4; + } + + tempi |= (dsp->espcm_range << 4); + data[0] = espcm_range_map[tempi]; + dsp->sbdat = data[0] << 8; + if (dsp->stereo) + { + 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 + { + dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + } + break; + + case ESPCM_3: + // TODO + break; + + case ESPCM_1: + if (dsp->espcm_sample_idx >= 19) + { + dsp->espcm_sample_idx = 0; + } + if (dsp->espcm_sample_idx == 0) + { + dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); + dsp->espcm_sample_idx++; + dsp->sb_8_length--; + dsp->ess_dma_counter++; + + dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F; + dsp->espcm_byte_buffer[0] >>= 5; + tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xF : 0x0; + dsp->espcm_byte_buffer[0] >>= 1; + } + else if (dsp->espcm_sample_idx == 3 | dsp->espcm_sample_idx == 11) + { + dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); + dsp->espcm_sample_idx++; + dsp->sb_8_length--; + dsp->ess_dma_counter++; + + tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xF : 0x0; + dsp->espcm_byte_buffer[0] >>= 1; + } + else + { + dsp->espcm_sample_idx++; + tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xF : 0x0; + dsp->espcm_byte_buffer[0] >>= 1; + } + + 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; } From e2200f8d750d89543d5975d08e9895c0152f0c30 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Wed, 13 Mar 2024 10:51:11 -0300 Subject: [PATCH 41/66] Add tables for ESPCM_3 mode --- src/include/86box/snd_sb_dsp.h | 2 +- src/sound/snd_sb_dsp.c | 101 ++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 6e5266b43..7b516a987 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -153,7 +153,7 @@ typedef struct sb_dsp_t { uint8_t espcm_range; uint8_t espcm_byte_buffer[4]; uint8_t espcm_code_buffer[10]; /* used for ESPCM_3 */ - uint8_t espcm_last_nibble; /* used for ESPCM_3 */ + uint8_t espcm_last_value; /* used for ESPCM_3 */ mpu_t *mpu; } sb_dsp_t; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 54c8bcc5e..530d16015 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -119,7 +119,12 @@ uint8_t adjustMap2[24] = { 252, 0, 252, 0 }; -uint8_t espcm_range_map[256] = { +/* 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. + **/ +uint8_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, @@ -135,7 +140,99 @@ uint8_t espcm_range_map[256] = { -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 + -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 }; double low_fir_sb16_coef[4][SB16_NCoef]; From 9d54a78918cdae2d40b085be82bffd492105254a Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Wed, 13 Mar 2024 11:39:56 -0300 Subject: [PATCH 42/66] Implement ESPCM_3 decoding --- src/include/86box/snd_sb_dsp.h | 1 + src/sound/snd_sb_dsp.c | 91 +++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 7b516a987..a9bd93ddc 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -153,6 +153,7 @@ typedef struct sb_dsp_t { uint8_t espcm_range; uint8_t espcm_byte_buffer[4]; uint8_t espcm_code_buffer[10]; /* used for ESPCM_3 */ + uint8_t espcm_table_index; /* used for ESPCM_3 */ uint8_t espcm_last_value; /* used for ESPCM_3 */ mpu_t *mpu; diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 530d16015..018e2d380 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -2301,7 +2301,96 @@ pollsb(void *priv) break; case ESPCM_3: - // TODO + if (dsp->espcm_sample_idx >= 19) + { + dsp->espcm_sample_idx = 0; + } + if (dsp->espcm_sample_idx == 0) + { + dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); + dsp->sb_8_length--; + dsp->ess_dma_counter++; + + dsp->espcm_sample_idx++; + + 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++) + { + dsp->espcm_byte_buffer[tempi] = dsp->dma_readb(dsp->dma_priv); + dsp->sb_8_length--; + dsp->ess_dma_counter++; + } + + 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; + dsp->espcm_sample_idx++; + } + else if (dsp->espcm_sample_idx == 11) + { + for (tempi = 1; tempi < 4; tempi++) + { + dsp->espcm_byte_buffer[tempi] = dsp->dma_readb(dsp->dma_priv); + dsp->sb_8_length--; + dsp->ess_dma_counter++; + } + + 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; + dsp->espcm_sample_idx++; + } + 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; + 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: From 416edcf1a5b93d70209ba7e6ee1a0fcdd58b5320 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Wed, 13 Mar 2024 11:41:50 -0300 Subject: [PATCH 43/66] Fix: clear espcm_sample_idx upon starting ESPCM DMA --- src/sound/snd_sb_dsp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 018e2d380..7972864a1 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -1348,6 +1348,7 @@ sb_exec_command(sb_dsp_t *dsp) if (IS_ESS(dsp)) { sb_start_dma(dsp, 1, 0, ESPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); + dsp->espcm_sample_idx = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; dsp->ess_dma_counter++; @@ -1366,6 +1367,7 @@ sb_exec_command(sb_dsp_t *dsp) if (IS_ESS(dsp)) { sb_start_dma(dsp, 1, 0, ESPCM_3, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); + dsp->espcm_sample_idx = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; dsp->ess_dma_counter++; @@ -1384,6 +1386,7 @@ sb_exec_command(sb_dsp_t *dsp) if (IS_ESS(dsp)) { sb_start_dma(dsp, 1, 0, ESPCM_1, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); + dsp->espcm_sample_idx = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; dsp->ess_dma_counter++; @@ -1402,6 +1405,7 @@ sb_exec_command(sb_dsp_t *dsp) if (IS_ESS(dsp)) { sb_start_dma_i(dsp, 1, 0, ESPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); + dsp->espcm_sample_idx = 0; dsp->sbdat2 = (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; From 6fc43a80828c44aba861832dc3fbe22a390ec243 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Wed, 13 Mar 2024 19:28:12 -0300 Subject: [PATCH 44/66] Implement ESPCM_4 recording --- src/include/86box/snd_sb_dsp.h | 7 +-- src/sound/snd_sb_dsp.c | 87 +++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index a9bd93ddc..7a1873b9c 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -152,9 +152,10 @@ typedef struct sb_dsp_t { uint8_t espcm_sample_idx; uint8_t espcm_range; uint8_t espcm_byte_buffer[4]; - uint8_t espcm_code_buffer[10]; /* used for ESPCM_3 */ - uint8_t espcm_table_index; /* used for ESPCM_3 */ - uint8_t espcm_last_value; /* used for ESPCM_3 */ + 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/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 7972864a1..0c1b789d1 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -124,7 +124,7 @@ uint8_t adjustMap2[24] = { * 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. **/ -uint8_t espcm_range_map[512] = { +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, @@ -2331,6 +2331,7 @@ pollsb(void *priv) } 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; @@ -2613,6 +2614,90 @@ sb_poll_i(void *priv) dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; + case ESPCM_4: + // 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; From 011b06441dac21fa9a598f4af13ec716ea559878 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Wed, 13 Mar 2024 22:00:46 -0300 Subject: [PATCH 45/66] Removing extraneous DSP update from ess_get_music_buffer_sbpro --- src/sound/snd_ess.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 0f57bca7d..55c6f9d60 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -473,8 +473,6 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) opl_buf = ess->opl.update(ess->opl.priv); - sb_dsp_update(&ess->dsp); - for (int c = 0; c < len * 2; c += 2) { out_l = 0.0; out_r = 0.0; From 40607b291dcfc909081d7128dd7374dc630bf953 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Tue, 19 Mar 2024 13:01:54 -0300 Subject: [PATCH 46/66] Fixing ESPCM 1/2 --- src/include/86box/fifo.h | 5 + src/include/86box/snd_sb_dsp.h | 29 ++-- src/sound/snd_sb_dsp.c | 274 +++++++++++++++++++++------------ 3 files changed, 201 insertions(+), 107 deletions(-) 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_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 7a1873b9c..f5905ccc2 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -1,6 +1,8 @@ #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*/ @@ -142,20 +144,25 @@ 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; + 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; + int ess_irq_generic; + int ess_irq_dmactr; // ESPCM - 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 */ + fifo64_t *espcm_fifo; + int espcm_fifo_reset; + int espcm_mode; + 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/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 0c1b789d1..34ddb1a50 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -34,7 +34,8 @@ #define ADPCM_2 3 #define ESPCM_4 4 #define ESPCM_3 5 -#define ESPCM_1 6 // not ESPCM_2, unlike what the manuals say +#define ESPCM_1 7 +#define ESPCM_4E 8 // for encoding mode switching /*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 @@ -386,12 +387,16 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) break; } - /* 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 - { - pclog("ess: IRQ was masked\n"); - return; + /* 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 + { + pclog("ess: IRQ was masked\n"); + return; + } } } @@ -945,6 +950,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_irqnum = 10; break; } + pclog("ess: IRQ set to %d\n", dsp->sb_irqnum); sb_ess_update_irq_drq_readback_regs(dsp, false); break; case 0xB2: /* DRQ Control */ @@ -965,6 +971,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_8_dmanum = 3; break; } + pclog("ess: DMA set to %d\n", dsp->sb_8_dmanum); sb_ess_update_irq_drq_readback_regs(dsp, false); if (chg & 0x40) sb_ess_update_dma_status(dsp); break; @@ -1041,6 +1048,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) 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; @@ -1066,13 +1074,15 @@ sb_exec_command(sb_dsp_t *dsp) } { - int i; - char data_s[256]; + int i, l, s = 0; + char data_s[256], *dsptr = data_s; data_s[0] = '\0'; for (i = 0; i < sb_commands[dsp->sb_command]; i++) { - snprintf(data_s, 256, " 0x%02X", dsp->sb_data[i]); + l = snprintf(dsptr, 256 - s, " 0x%02X", dsp->sb_data[i]); + s += l; + dsptr += l; } pclog("dsp->sb_command = 0x%02X%s, length %d\n", dsp->sb_command, data_s, sb_commands[dsp->sb_command]); } @@ -1090,6 +1100,14 @@ sb_exec_command(sb_dsp_t *dsp) { 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])); } @@ -1336,79 +1354,60 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8); break; case 0x65: /* 4-bit ESPCM output with reference */ - if (!IS_ESS(dsp)) - { - break; - } - dsp->sbref = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; - fallthrough; case 0x64: /* 4-bit ESPCM output */ if (IS_ESS(dsp)) { + if (dsp->espcm_mode != ESPCM_4) + { + 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)); - dsp->espcm_sample_idx = 0; - dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; } break; case 0x67: /* 3-bit ESPCM output with reference */ - if (!IS_ESS(dsp)) - { - break; - } - dsp->sbref = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; - fallthrough; case 0x66: /* 3-bit ESPCM output */ if (IS_ESS(dsp)) { + pclog("ess: Starting espcm3 transfer\n"); + if (dsp->espcm_mode != ESPCM_3) + { + pclog("ess: ESPCM FIFO reset\n"); + 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)); - dsp->espcm_sample_idx = 0; - dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; } break; - case 0x6B: /* 1-bit ESPCM output with reference */ - if (!IS_ESS(dsp)) - { - break; - } - dsp->sbref = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; - fallthrough; - case 0x6A: /* 1-bit ESPCM output */ + case 0x6D: /* 1-bit ESPCM output with reference */ + case 0x6C: /* 1-bit ESPCM output */ if (IS_ESS(dsp)) { + pclog("ess: Starting espcm1 transfer\n"); + if (dsp->espcm_mode != ESPCM_1) + { + pclog("ess: ESPCM FIFO reset\n"); + 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)); - dsp->espcm_sample_idx = 0; - dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; } break; case 0x6F: /* 4-bit ESPCM input with reference */ - if (!IS_ESS(dsp)) - { - break; - } - dsp->sbref = (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80; - dsp->record_pos_read += 2; - dsp->record_pos_read &= 0xFFFF; - fallthrough; case 0x6E: /* 4-bit ESPCM input */ if (IS_ESS(dsp)) { - sb_start_dma_i(dsp, 1, 0, ESPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); + if (dsp->espcm_mode != ESPCM_4E) + { + 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)); dsp->espcm_sample_idx = 0; - dsp->sbdat2 = (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80; - dsp->record_pos_read += 2; - dsp->record_pos_read &= 0xFFFF; } break; case 0x75: /* 4-bit ADPCM output with reference */ @@ -1655,12 +1654,15 @@ sb_exec_command(sb_dsp_t *dsp) timer_set_delay_u64(&dsp->irq_timer, (100ULL * TIMER_USEC)); pclog("F2 written\n"); } - } else + } 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) @@ -1722,6 +1724,12 @@ sb_write(uint16_t a, uint8_t v, void *priv) } dsp->sbreset = v; } + + 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; @@ -1752,10 +1760,23 @@ 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 >= 0xA0 && dsp->sb_command <= 0xCF) { - if (dsp->sb_command <= 0xC0 || dsp->sb_command == 0xC2) { + if (IS_ESS(dsp) && dsp->sb_command >= 0x64 && dsp->sb_command <= 0x6F) + { + if (dsp->sb_subtype == SB_SUBTYPE_ESS_ES1688) + { + sb_commands[dsp->sb_command] = 2; + } + else + { + 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 == 0xC6 || dsp->sb_command == 0xC7) { + } 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; @@ -1787,6 +1808,10 @@ sb_read(uint16_t a, void *priv) uint8_t ret = 0x00; /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ + if ((a & 0xF) == 0x9 || (a & 0xF) == 0xB) + { + pclog("ess: Read-Sequence-Key? port 0x%X", a); + } if (dsp->sb_type < SB16) { /* Exception: ESS AudioDrive does not alias port base+0xf */ @@ -1814,6 +1839,23 @@ 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; + uint8_t fifo_half = 0; + uint8_t irq_generic = dsp->ess_irq_generic ? 0x04 : 0x00; + uint8_t irq_fifohe = 0; // Unimplemented + 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)) { @@ -1839,6 +1881,7 @@ sb_read(uint16_t a, void *priv) pclog("sb: IRQ acknowledged\n"); } 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); @@ -1891,6 +1934,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]); @@ -1932,6 +1976,7 @@ sb_dsp_irq_poll(void *priv) sb_dsp_t *dsp = (sb_dsp_t *) priv; sb_irq(dsp, 1); + dsp->ess_irq_generic = true; } void @@ -1998,6 +2043,8 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) 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 @@ -2059,6 +2106,25 @@ sb_ess_finish_dma(sb_dsp_t *dsp) pclog("ess: DMA finished"); } +void +sb_espcm_fifoctl_run(sb_dsp_t *dsp) +{ + if (fifo_get_empty(dsp->espcm_fifo)) + { + 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) { @@ -2262,29 +2328,32 @@ pollsb(void *priv) } if (dsp->espcm_sample_idx == 0) { - dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); - dsp->espcm_sample_idx++; - dsp->sb_8_length--; - dsp->ess_dma_counter++; + sb_espcm_fifoctl_run(dsp); + 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) { - dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); - dsp->espcm_sample_idx++; + sb_espcm_fifoctl_run(dsp); + dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo); dsp->sb_8_length--; - dsp->ess_dma_counter++; tempi = dsp->espcm_byte_buffer[0] & 0x0F; } else { - dsp->espcm_sample_idx++; 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; @@ -2311,11 +2380,8 @@ pollsb(void *priv) } if (dsp->espcm_sample_idx == 0) { - dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); - dsp->sb_8_length--; - dsp->ess_dma_counter++; - - dsp->espcm_sample_idx++; + sb_espcm_fifoctl_run(dsp); + 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; @@ -2325,9 +2391,9 @@ pollsb(void *priv) { for (tempi = 0; tempi < 4; tempi++) { - dsp->espcm_byte_buffer[tempi] = dsp->dma_readb(dsp->dma_priv); + sb_espcm_fifoctl_run(dsp); + dsp->espcm_byte_buffer[tempi] = fifo_read(dsp->espcm_fifo); dsp->sb_8_length--; - dsp->ess_dma_counter++; } dsp->espcm_table_index = dsp->espcm_byte_buffer[0] & 0x03; @@ -2346,15 +2412,14 @@ pollsb(void *priv) 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; - dsp->espcm_sample_idx++; } else if (dsp->espcm_sample_idx == 11) { for (tempi = 1; tempi < 4; tempi++) { - dsp->espcm_byte_buffer[tempi] = dsp->dma_readb(dsp->dma_priv); + sb_espcm_fifoctl_run(dsp); + dsp->espcm_byte_buffer[tempi] = fifo_read(dsp->espcm_fifo); dsp->sb_8_length--; - dsp->ess_dma_counter++; } dsp->espcm_code_buffer[0] = (dsp->espcm_byte_buffer[1]) & 0x07; @@ -2369,16 +2434,21 @@ pollsb(void *priv) 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; - dsp->espcm_sample_idx++; } 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; - dsp->espcm_sample_idx++; } + 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; @@ -2405,33 +2475,36 @@ pollsb(void *priv) } if (dsp->espcm_sample_idx == 0) { - dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); - dsp->espcm_sample_idx++; - dsp->sb_8_length--; - dsp->ess_dma_counter++; + sb_espcm_fifoctl_run(dsp); + 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 ? 0xF : 0x0; + 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) { - dsp->espcm_byte_buffer[0] = dsp->dma_readb(dsp->dma_priv); - dsp->espcm_sample_idx++; + sb_espcm_fifoctl_run(dsp); + dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo); dsp->sb_8_length--; - dsp->ess_dma_counter++; - tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xF : 0x0; + tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4; dsp->espcm_byte_buffer[0] >>= 1; } else { - dsp->espcm_sample_idx++; - tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xF : 0x0; + 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; @@ -2465,6 +2538,7 @@ pollsb(void *priv) sb_ess_finish_dma(dsp); } sb_irq(dsp, 1); + dsp->ess_irq_generic = true; } if (dsp->ess_dma_counter > 0xffff) { @@ -2479,6 +2553,7 @@ pollsb(void *priv) if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 1); + dsp->ess_irq_dmactr = true; pclog("IRQ fired via ESS DMA counter, next IRQ in %d samples\n", sb_ess_get_dma_len(dsp)); } } @@ -2542,6 +2617,7 @@ pollsb(void *priv) sb_ess_finish_dma(dsp); } sb_irq(dsp, 0); + dsp->ess_irq_generic = true; } if (dsp->ess_dma_counter > 0xffff) { @@ -2556,6 +2632,7 @@ pollsb(void *priv) if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 0); + dsp->ess_irq_dmactr = true; } } uint32_t temp = dsp->ess_dma_counter & 0xffff; @@ -2567,6 +2644,7 @@ pollsb(void *priv) 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"); @@ -2614,7 +2692,7 @@ sb_poll_i(void *priv) dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; - case ESPCM_4: + 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; @@ -2712,6 +2790,7 @@ sb_poll_i(void *priv) sb_ess_finish_dma(dsp); } sb_irq(dsp, 1); + dsp->ess_irq_generic = true; } if (dsp->ess_dma_counter > 0xffff) { @@ -2726,6 +2805,7 @@ sb_poll_i(void *priv) if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 1); + dsp->ess_irq_dmactr = true; } } uint32_t temp = dsp->ess_dma_counter & 0xffff; @@ -2784,6 +2864,7 @@ sb_poll_i(void *priv) sb_ess_finish_dma(dsp); } sb_irq(dsp, 0); + dsp->ess_irq_generic = true; } if (dsp->ess_dma_counter > 0xffff) { @@ -2798,6 +2879,7 @@ sb_poll_i(void *priv) if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 0); + dsp->ess_irq_dmactr = true; } } uint32_t temp = dsp->ess_dma_counter & 0xffff; From 56859a9173f4007c2316c87727c2d34ca93065ac Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Tue, 19 Mar 2024 18:48:51 -0300 Subject: [PATCH 47/66] Fixing ESPCM 2/2 --- src/sound/snd_sb_dsp.c | 399 ++++++++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 169 deletions(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 34ddb1a50..f94724e68 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -1357,8 +1357,10 @@ sb_exec_command(sb_dsp_t *dsp) case 0x64: /* 4-bit ESPCM output */ if (IS_ESS(dsp)) { - if (dsp->espcm_mode != ESPCM_4) + if (dsp->espcm_mode != ESPCM_4 + || (dsp->sb_8_enable && dsp->sb_8_pause)) { + pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1370,8 +1372,8 @@ sb_exec_command(sb_dsp_t *dsp) case 0x66: /* 3-bit ESPCM output */ if (IS_ESS(dsp)) { - pclog("ess: Starting espcm3 transfer\n"); - if (dsp->espcm_mode != ESPCM_3) + if (dsp->espcm_mode != ESPCM_3 + || (dsp->sb_8_enable && dsp->sb_8_pause)) { pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); @@ -1385,8 +1387,8 @@ sb_exec_command(sb_dsp_t *dsp) case 0x6C: /* 1-bit ESPCM output */ if (IS_ESS(dsp)) { - pclog("ess: Starting espcm1 transfer\n"); - if (dsp->espcm_mode != ESPCM_1) + if (dsp->espcm_mode != ESPCM_1 + || (dsp->sb_8_enable && dsp->sb_8_pause)) { pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); @@ -1400,8 +1402,10 @@ sb_exec_command(sb_dsp_t *dsp) case 0x6E: /* 4-bit ESPCM input */ if (IS_ESS(dsp)) { - if (dsp->espcm_mode != ESPCM_4E) + if (dsp->espcm_mode != ESPCM_4E + || (dsp->sb_8_enable && dsp->sb_8_pause)) { + pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -2109,7 +2113,7 @@ sb_ess_finish_dma(sb_dsp_t *dsp) void sb_espcm_fifoctl_run(sb_dsp_t *dsp) { - if (fifo_get_empty(dsp->espcm_fifo)) + if (fifo_get_empty(dsp->espcm_fifo) && !dsp->sb_8_pause) { while (!fifo_get_full(dsp->espcm_fifo)) { @@ -2134,191 +2138,212 @@ 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--; - dsp->ess_dma_counter++; + 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--; - dsp->ess_dma_counter++; + 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; - dsp->ess_dma_counter += 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; - dsp->ess_dma_counter += 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--; - 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; + 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--; - 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; + 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; - - dsp->sbdat = (dsp->sbref ^ 0x80) << 8; - - 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; + ref = dsp->sbref + scaleMap2[tempi]; + if (ref > 0xff) + dsp->sbref = 0xff; + else if (ref < 0x00) + dsp->sbref = 0x00; else - dsp->sbdatr = dsp->sbdat; - dsp->sbleftright = !dsp->sbleftright; - } else - dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + dsp->sbref = ref; + dsp->sbstep = (dsp->sbstep + adjustMap2[tempi]) & 0xff; + + dsp->sbdat = (dsp->sbref ^ 0x80) << 8; + + 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: @@ -2329,6 +2354,10 @@ pollsb(void *priv) 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; @@ -2337,6 +2366,10 @@ pollsb(void *priv) 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--; @@ -2381,6 +2414,10 @@ pollsb(void *priv) 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; @@ -2392,9 +2429,17 @@ pollsb(void *priv) 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; @@ -2418,9 +2463,17 @@ pollsb(void *priv) 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; @@ -2476,6 +2529,10 @@ pollsb(void *priv) 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; @@ -2486,6 +2543,10 @@ pollsb(void *priv) 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--; From 882b65de48deb50af17fe70de5162268c772f1b9 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Thu, 21 Mar 2024 23:32:26 -0300 Subject: [PATCH 48/66] Implementing ESFM timer interface using 86Box's timers; dropping ESFMu's internal timer emulation Co-authored-by: OBattler --- src/sound/snd_opl_esfm.c | 216 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 204 insertions(+), 12 deletions(-) diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index cbd9a93a3..d7523da8b 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -38,11 +38,6 @@ #define RSM_FRAC 10 -enum { - FLAG_CYCLES = 0x02, - FLAG_OPL3 = 0x01 -}; - typedef struct { esfm_chip opl; int8_t flags; @@ -54,12 +49,53 @@ typedef struct { 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) { @@ -78,6 +114,60 @@ esfm_drv_generate_stream(esfm_drv_t *dev, int32_t *sndptr, uint32_t num) } } +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) { @@ -98,6 +188,9 @@ esfm_drv_init(const device_t *info) /* 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; } @@ -149,32 +242,131 @@ esfm_drv_read(uint16_t port, void *priv) uint8_t ret = 0xff; - ret = ESFM_read_port(&dev->opl, port & 3); + 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; + } + + pclog("esfm: [%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, port, ret); return ret; } +static void +esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val) +{ + ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); + + if (dev->opl.native_mode) + { + switch (dev->port & 0x5ff) + { + case 0x402: /* Timer 1 */ + dev->timer_count[0] = val; + esfm_log("Timer 0 count now: %i\n", dev->timer_count[0]); + break; + + case 0x403: /* Timer 2 */ + dev->timer_count[1] = val; + esfm_log("Timer 1 count now: %i\n", dev->timer_count[1]); + break; + + case 0x404: /* 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; + } + } + else + { + switch (dev->port & 0x1ff) + { + 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; + } + } +} + static void esfm_drv_write(uint16_t port, uint8_t val, void *priv) { esfm_drv_t *dev = (esfm_drv_t *) priv; + pclog("esfm: [%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, port, val); + if (dev->flags & FLAG_CYCLES) cycles -= ((int) (isa_timing * 8)); esfm_drv_update(dev); if (dev->opl.native_mode) { - if ((port & 3) == 1) { - ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); - } else { + if ((port & 0x0003) == 0x0001) + esfm_drv_write_buffered(dev, val); + else { ESFM_write_port(&dev->opl, port & 3, val); + dev->port = dev->opl.addr_latch & 0x07ff; } } else { - if ((port & 3) == 1 || (port & 3) == 3) { - ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); - } else { + if ((port & 0x0001) == 0x0001) + esfm_drv_write_buffered(dev, val); + else { ESFM_write_port(&dev->opl, port & 3, val); + dev->port = dev->opl.addr_latch & 0x01ff; } } } From c835a4d1560548051285dce9cca8616da1cbf7b1 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 00:32:57 -0300 Subject: [PATCH 49/66] Uncluttering logs; making ESFM output work again (oops) --- src/sound/snd_ess.c | 2 +- src/sound/snd_opl_esfm.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 55c6f9d60..6ba5a9db8 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -518,10 +518,10 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) ess->dsp.record_buffer[(c_record & 0xfffe) + 1] = 32767; } } +#endif buffer[c] += (int32_t) out_l; buffer[c + 1] += (int32_t) out_r; -#endif } #if 0 diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index d7523da8b..cf4e94cb1 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -270,7 +270,7 @@ esfm_drv_read(uint16_t port, void *priv) break; } - pclog("esfm: [%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, port, ret); + esfm_log("esfm: [%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, port, ret); return ret; } @@ -347,7 +347,7 @@ esfm_drv_write(uint16_t port, uint8_t val, void *priv) { esfm_drv_t *dev = (esfm_drv_t *) priv; - pclog("esfm: [%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, port, val); + esfm_log("esfm: [%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, port, val); if (dev->flags & FLAG_CYCLES) cycles -= ((int) (isa_timing * 8)); From 3e539c630b36e3c13fd5be620ed6ee615d97c659 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 01:31:13 -0300 Subject: [PATCH 50/66] Mixer code cleanup: fixing ESS volume calculation --- src/sound/snd_ess.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 6ba5a9db8..b2a67419a 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -50,7 +50,7 @@ #include <86box/plat_unused.h> - +// clang-format off static const double sb_att_4dbstep_3bits[] = { 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0 }; @@ -64,6 +64,13 @@ static const double sb_att_1p4dbstep_4bits[] = { 10603.0, 12458.0, 14637.0, 17196.0, 20204.0, 23738.0, 27889.0, 32767.0 }; +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 +}; +// clang-format on + + /* SB PRO */ typedef struct ess_mixer_t { double master_l; @@ -118,27 +125,9 @@ typedef struct ess_t { void (*opl_mix)(void*, double*, double*); } ess_t; -static inline uint8_t expand16to32(const uint8_t t) { - /* 4-bit -> 5-bit expansion. - * - * 0 -> 0 - * 1 -> 2 - * 2 -> 4 - * 3 -> 6 - * .... - * 7 -> 14 - * 8 -> 17 - * 9 -> 19 - * 10 -> 21 - * 11 -> 23 - * .... - * 15 -> 31 */ - return (t << 1) | (t >> 3); -} - static double ess_mixer_get_vol_4bit(uint8_t vol) { - return (48.0 + (20.0 * log((vol & 0xF) / 15.0))) / 48.0; + return sb_att_2dbstep_4bits[vol] / 32767.0; } void From d41e791aecfe622b81bc020a9dc8e00bb59ef2cb Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 01:35:31 -0300 Subject: [PATCH 51/66] Fix bug introduced by last commit --- src/sound/snd_ess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index b2a67419a..f70e508ed 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -127,7 +127,7 @@ typedef struct ess_t { static double ess_mixer_get_vol_4bit(uint8_t vol) { - return sb_att_2dbstep_4bits[vol] / 32767.0; + return sb_att_2dbstep_4bits[vol & 0x0F] / 32767.0; } void From 937537f78ead2243b71d7c60480b02d955acb3c1 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 11:17:34 -0300 Subject: [PATCH 52/66] Legacy microphone volume mapping --- src/sound/snd_ess.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index f70e508ed..3f544ec4f 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -59,11 +59,15 @@ static const double sb_att_7dbstep_2bits[] = { 164.0, 6537.0, 14637.0, 32767.0 }; +/* Attenuation table for 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 4-bit mixer avolume. + * 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 @@ -177,7 +181,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) { uint8_t mic_vol_2bit = (mixer->regs[0x0a] >> 1) & 0x3; mixer->mic_l = mixer->mic_r = sb_att_7dbstep_2bits[mic_vol_2bit] / 32768.0; - mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2); + mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2) | (mic_vol_2bit << 4) | (mic_vol_2bit << 6); break; } From d46e2ef7c8a3d3fc0bc87598bf25efa5b1624160 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 11:56:01 -0300 Subject: [PATCH 53/66] Refactored ESFM timers; removed 16-bit clipping from ESFMu Co-authored-by: OBattler --- src/sound/esfmu/esfm.c | 15 +++++-- src/sound/esfmu/esfm.h | 2 +- src/sound/snd_opl_esfm.c | 88 +++++++++++++--------------------------- 3 files changed, 40 insertions(+), 65 deletions(-) diff --git a/src/sound/esfmu/esfm.c b/src/sound/esfmu/esfm.c index 08beadb5a..274afd277 100644 --- a/src/sound/esfmu/esfm.c +++ b/src/sound/esfmu/esfm.c @@ -1741,6 +1741,10 @@ ESFM_slot_generate_emu(esfm_slot *slot) } /* ------------------------------------------------------------------------- */ +#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) { @@ -2226,7 +2230,7 @@ ESFM_update_write_buffer(esfm_chip *chip) /* ------------------------------------------------------------------------- */ void -ESFM_generate(esfm_chip *chip, int16_t *buf) +ESFM_generate(esfm_chip *chip, int32_t *buf) { int channel_idx; @@ -2259,8 +2263,8 @@ ESFM_generate(esfm_chip *chip, int16_t *buf) chip->output_accm[1] += channel->output[1]; } - buf[0] = ESFM_clip_sample(chip->output_accm[0]); - buf[1] = ESFM_clip_sample(chip->output_accm[1]); + buf[0] = chip->output_accm[0]; + buf[1] = chip->output_accm[1]; ESFM_update_timers(chip); ESFM_update_write_buffer(chip); @@ -2308,10 +2312,13 @@ 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, sndptr); + 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 index 41ac6983f..eda8bcbb3 100644 --- a/src/sound/esfmu/esfm.h +++ b/src/sound/esfmu/esfm.h @@ -67,7 +67,7 @@ void ESFM_write_reg_buffered_fast (esfm_chip *chip, uint16_t address, uint8_t da void ESFM_write_port (esfm_chip *chip, uint8_t offset, uint8_t data); uint8_t ESFM_readback_reg (esfm_chip *chip, uint16_t address); uint8_t ESFM_read_port (esfm_chip *chip, uint8_t offset); -void ESFM_generate(esfm_chip *chip, int16_t *buf); +void ESFM_generate(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); diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index cf4e94cb1..174830523 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -99,10 +99,7 @@ esfm_log(const char *fmt, ...) void esfm_generate_raw(esfm_drv_t *dev, int32_t *bufp) { - ESFM_generate(&dev->opl, &dev->samples[0]); - - bufp[0] = dev->samples[0]; - bufp[1] = dev->samples[1]; + ESFM_generate(&dev->opl, bufp); } void @@ -270,75 +267,48 @@ esfm_drv_read(uint16_t port, void *priv) break; } - esfm_log("esfm: [%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, port, ret); - return ret; } static void esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val) { + uint16_t p = dev->port; + ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); if (dev->opl.native_mode) { - switch (dev->port & 0x5ff) - { - case 0x402: /* Timer 1 */ - dev->timer_count[0] = val; - esfm_log("Timer 0 count now: %i\n", dev->timer_count[0]); - break; - - case 0x403: /* Timer 2 */ - dev->timer_count[1] = val; - esfm_log("Timer 1 count now: %i\n", dev->timer_count[1]); - break; - - case 0x404: /* 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; - } + p -= 0x400; + p &= 0x1ff; } - else + + switch (p) { - switch (dev->port & 0x1ff) - { - case 0x002: /* Timer 1 */ - dev->timer_count[0] = val; - esfm_log("Timer 0 count now: %i\n", dev->timer_count[0]); - break; + 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 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; + 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; - } + default: + break; } } @@ -347,8 +317,6 @@ esfm_drv_write(uint16_t port, uint8_t val, void *priv) { esfm_drv_t *dev = (esfm_drv_t *) priv; - esfm_log("esfm: [%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, port, val); - if (dev->flags & FLAG_CYCLES) cycles -= ((int) (isa_timing * 8)); From 5e318dfce9420f264f44ea4b0a1f165c4d2c2256 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 12:13:57 -0300 Subject: [PATCH 54/66] Updating ESFMu to version v1.2.6 --- src/sound/esfmu/esfm.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sound/esfmu/esfm.c b/src/sound/esfmu/esfm.c index 274afd277..89fa82d4f 100644 --- a/src/sound/esfmu/esfm.c +++ b/src/sound/esfmu/esfm.c @@ -1783,7 +1783,7 @@ ESFM_process_feedback(esfm_chip *chip) eg_output = slot->in.eg_output; // ASM optimizaions! -#if defined(__GNUC__) && defined(__x86_64__) +#if defined(__GNUC__) && defined(__x86_64__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS) asm ( "movzbq %[wave], %%r8 \n\t" "shll $11, %%r8d \n\t" @@ -1848,11 +1848,14 @@ ESFM_process_feedback(esfm_chip *chip) [exprom] "m" (exprom) : "cc", "ax", "bx", "cx", "dx", "r8", "r9", "r10", "r11" ); -#elif defined(__GNUC__) && defined(__i386__) +#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" - "leal %[sinrom], %%edi \n\t" + "movl %[sinrom], %%edi \n\t" "addl %%eax, %%edi \n\t" "shlw $3, %[eg_out] \n\t" "xorl %[out], %[out] \n\t" @@ -1887,7 +1890,7 @@ ESFM_process_feedback(esfm_chip *chip) // wave_out = exprom[level & 0xff] >> (level >> 8); "movb %%ah, %%cl \n\t" "movzbl %%al, %%eax \n\t" - "leal %[exprom], %[out] \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; @@ -1909,12 +1912,12 @@ ESFM_process_feedback(esfm_chip *chip) : [p_off] "m" (phase_offset), [mod_in] "m" (mod_in_shift), [wave] "m" (waveform), - [sinrom] "m" (logsinrom), - [exprom] "m" (exprom), + [sinrom] "m" (logsinrom_addr), + [exprom] "m" (exprom_addr), [i] "m" (iter_counter) : "cc", "ax", "bx", "cx", "di" ); -#elif defined(__GNUC__) && defined(__arm__) +#elif defined(__GNUC__) && defined(__arm__) && !defined(_ESFMU_DISABLE_ASM_OPTIMIZATIONS) asm ( "movs r3, #0 \n\t" "movs %[out], #0 \n\t" From 4b93999790191540bf0c274c2137dedcb79903b8 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 21:30:23 -0300 Subject: [PATCH 55/66] Cleanup: removing a bunch of logging statements --- src/sound/snd_ess.c | 12 +++------- src/sound/snd_sb_dsp.c | 52 +++--------------------------------------- 2 files changed, 6 insertions(+), 58 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 3f544ec4f..44106fa83 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -167,7 +167,6 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2); } else { mixer->regs[mixer->index] = val; - pclog("ess: Mixer Register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); switch (mixer->index) { /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */ @@ -322,7 +321,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } default: - pclog("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + //pclog("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -378,30 +377,25 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x36: case 0x38: case 0x3e: - pclog("ess: Mixer Register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); return mixer->regs[mixer->index]; case 0x40: - if (0) + if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) { - /* not on ES1688 */ uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos]; - uint8_t pos_log = mixer->ess_id_str_pos; /* TODO remove */ mixer->ess_id_str_pos++; if (mixer->ess_id_str_pos >= 4) mixer->ess_id_str_pos = 0; - pclog("ess: ID READ: %02X (pos %d)\n", val, pos_log); return val; } else { - pclog("ess: Mixer Register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); return mixer->regs[mixer->index]; } default: - pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + //pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index f94724e68..03a6ba4ba 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -367,7 +367,6 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) if (IS_ESS(dsp) && dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688 && !(ESSreg(0xB1) & 0x10)) // if ESS playback, and IRQ disabled, do not fire { - pclog("ess: IRQ was masked\n"); return; } @@ -394,7 +393,6 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) if (dsp->ess_playback_mode && bit <= 1 && set && !masked) { if (!(ESSreg(0xB1) & 0x40)) // if ESS playback, and IRQ disabled, do not fire { - pclog("ess: IRQ was masked\n"); return; } } @@ -409,7 +407,6 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) void sb_irq(sb_dsp_t *dsp, int irq8) { - pclog("sb: IRQ raised\n"); sb_update_status(dsp, !irq8, 1); } @@ -862,7 +859,6 @@ static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) { switch (reg) { default: - pclog("ESS register read reg=%02xh val=%02xh\n",reg, ESSreg(reg)); return ESSreg(reg); } @@ -876,7 +872,6 @@ static void sb_ess_update_autolen(sb_dsp_t *dsp) { static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) { uint8_t chg = 0x00; - pclog("ESS register write reg=%02xh val=%02xh\n",reg,data); switch (reg) { case 0xA1: /* Extended Mode Sample Rate Generator */ @@ -890,7 +885,6 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) temp = 1000000.0 / dsp->sb_freq; dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp; dsp->sb_timei = dsp->sb_timeo; - pclog("ess: Sample rate - %ihz (%f)\n", dsp->sb_freq, dsp->sblatcho); break; } case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */ @@ -901,7 +895,6 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) case 0xA5: /* DMA Transfer Count Reload (high) */ ESSreg(reg) = data; sb_ess_update_autolen(dsp); - pclog("ess: DMA Transfer Count Reload length set to %d samples (0x%x)\n", sb_ess_get_dma_len(dsp), sb_ess_get_dma_counter(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; @@ -950,7 +943,6 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_irqnum = 10; break; } - pclog("ess: IRQ set to %d\n", dsp->sb_irqnum); sb_ess_update_irq_drq_readback_regs(dsp, false); break; case 0xB2: /* DRQ Control */ @@ -971,7 +963,6 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_8_dmanum = 3; break; } - pclog("ess: DMA set to %d\n", dsp->sb_8_dmanum); sb_ess_update_irq_drq_readback_regs(dsp, false); if (chg & 0x40) sb_ess_update_dma_status(dsp); break; @@ -1053,7 +1044,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) break; default: - pclog("UNKNOWN ESS register write reg=%02xh val=%02xh\n",reg,data); + sb_dsp_log("UNKNOWN ESS register write reg=%02xh val=%02xh\n",reg,data); break; } } @@ -1072,20 +1063,6 @@ sb_exec_command(sb_dsp_t *dsp) { dsp->sb_8051_ram[0x20] = dsp->sb_command; } - - { - int i, l, s = 0; - char data_s[256], *dsptr = data_s; - data_s[0] = '\0'; - - for (i = 0; i < sb_commands[dsp->sb_command]; i++) - { - l = snprintf(dsptr, 256 - s, " 0x%02X", dsp->sb_data[i]); - s += l; - dsptr += l; - } - pclog("dsp->sb_command = 0x%02X%s, length %d\n", dsp->sb_command, data_s, sb_commands[dsp->sb_command]); - } if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) { @@ -1320,7 +1297,6 @@ sb_exec_command(sb_dsp_t *dsp) temp = 256 - dsp->sb_data[0]; temp = 1000000 / temp; sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); - pclog("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); // if ((dsp->sb_freq != temp) && (IS_ESS(dsp) || (dsp->sb_type >= SB16))) if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) recalc_sb16_filter(0, temp); @@ -1334,7 +1310,6 @@ sb_exec_command(sb_dsp_t *dsp) 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 (%f)\n", dsp->sb_data[1] + (dsp->sb_data[0] << 8), dsp->sblatcho); - pclog("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; @@ -1360,7 +1335,6 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->espcm_mode != ESPCM_4 || (dsp->sb_8_enable && dsp->sb_8_pause)) { - pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1375,7 +1349,6 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->espcm_mode != ESPCM_3 || (dsp->sb_8_enable && dsp->sb_8_pause)) { - pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1390,7 +1363,6 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->espcm_mode != ESPCM_1 || (dsp->sb_8_enable && dsp->sb_8_pause)) { - pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1405,7 +1377,6 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->espcm_mode != ESPCM_4E || (dsp->sb_8_enable && dsp->sb_8_pause)) { - pclog("ess: ESPCM FIFO reset\n"); fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1652,11 +1623,9 @@ sb_exec_command(sb_dsp_t *dsp) case 0xF2: /* Trigger 8-bit IRQ */ sb_dsp_log("Trigger IRQ\n"); if (IS_ESS(dsp)) { - if (timer_is_enabled(&dsp->irq_timer)) - pclog("F2 already written\n"); - else { + if (!timer_is_enabled(&dsp->irq_timer)) + { timer_set_delay_u64(&dsp->irq_timer, (100ULL * TIMER_USEC)); - pclog("F2 written\n"); } } else { sb_irq(dsp, 1); @@ -1665,7 +1634,6 @@ sb_exec_command(sb_dsp_t *dsp) 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: @@ -1812,10 +1780,6 @@ sb_read(uint16_t a, void *priv) uint8_t ret = 0x00; /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ - if ((a & 0xF) == 0x9 || (a & 0xF) == 0xB) - { - pclog("ess: Read-Sequence-Key? port 0x%X", a); - } if (dsp->sb_type < SB16) { /* Exception: ESS AudioDrive does not alias port base+0xf */ @@ -1880,10 +1844,6 @@ sb_read(uint16_t a, void *priv) break; case 0xE: /* Read data ready */ dsp->irq_update(dsp->irq_priv, 0); - if (dsp->sb_irq8 || dsp->sb_irq16) - { - pclog("sb: IRQ acknowledged\n"); - } 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. */ @@ -1898,10 +1858,6 @@ sb_read(uint16_t a, void *priv) case 0xF: /* 16-bit ack */ if (!IS_ESS(dsp)) { - if (dsp->sb_irq16) - { - pclog("sb: 16-bit IRQ acknowledged"); - } dsp->sb_irq16 = 0; if (!dsp->sb_irq8) dsp->irq_update(dsp->irq_priv, 0); @@ -2107,7 +2063,6 @@ sb_ess_finish_dma(sb_dsp_t *dsp) return; ESSreg(0xB8) &= ~0x01; dma_set_drq(dsp->sb_8_dmanum, 0); - pclog("ess: DMA finished"); } void @@ -2615,7 +2570,6 @@ pollsb(void *priv) { sb_irq(dsp, 1); dsp->ess_irq_dmactr = true; - pclog("IRQ fired via ESS DMA counter, next IRQ in %d samples\n", sb_ess_get_dma_len(dsp)); } } uint32_t temp = dsp->ess_dma_counter & 0xffff; From f2091e349006ecc0118ce89557d3a1186f83f2b0 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 21:36:41 -0300 Subject: [PATCH 56/66] Cleanup: removing broken recording functionality --- src/sound/snd_ess.c | 103 +------------------------------------------- 1 file changed, 2 insertions(+), 101 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 44106fa83..b07c2d18f 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -66,7 +66,7 @@ static const double sb_att_1p4dbstep_4bits[] = { 10603.0, 12458.0, 14637.0, 17196.0, 20204.0, 23738.0, 27889.0, 32767.0 }; -/* Attenuation table for 4-bit mixer avolume. +/* Attenuation table for 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, @@ -104,14 +104,8 @@ typedef struct ess_mixer_t { uint8_t index; uint8_t regs[256]; - uint8_t ess_id_str[256]; + uint8_t ess_id_str[4]; uint8_t ess_id_str_pos; - -#if 0 - int record_pos_write_cd; - double record_pos_write_cd_sigma; - int record_pos_write_music; -#endif } ess_mixer_t; typedef struct ess_t { @@ -448,12 +442,6 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) { ess_t *ess = (ess_t *) priv; const ess_mixer_t *mixer = &ess->mixer_sbpro; -#if 0 - int rec_pos = ess->mixer_sbpro.record_pos_write_music; - int c_record; - int32_t in_l; - int32_t in_r; -#endif double out_l = 0.0; double out_r = 0.0; const int32_t *opl_buf = NULL; @@ -475,52 +463,10 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) out_l *= mixer->master_l; out_r *= mixer->master_r; -#if 0 - // Pull input after applying mixer's master volume scaling - in_l = (mixer->input_selector & INPUT_MIXER_L) ? ((int32_t) out_l) : 0; - in_r = (mixer->input_selector & INPUT_MIXER_R) ? ((int32_t) out_l) : 0; - - if (ess->dsp.sb_enable_i) { - // NOTE: this is nearest-neighbor sampling. This is gonna generate aliasing like HECK. Is this what the real card does? - c_record = rec_pos + ((c * ess->dsp.sb_freq) / MUSIC_FREQ); - - ess->dsp.record_buffer[c_record & 0xfffe] += in_l; - ess->dsp.record_buffer[(c_record & 0xfffe) + 1] += in_r; - - if (ess->dsp.record_buffer[c_record & 0xfffe] < -32768) - { - ess->dsp.record_buffer[c_record & 0xfffe] = -32768; - } - else if (ess->dsp.record_buffer[c_record & 0xfffe] > 32767) - { - ess->dsp.record_buffer[c_record & 0xfffe] = 32767; - } - - if (ess->dsp.record_buffer[(c_record & 0xfffe) + 1] < -32768) - { - ess->dsp.record_buffer[(c_record & 0xfffe) + 1] = -32768; - } - else if (ess->dsp.record_buffer[(c_record & 0xfffe) + 1] > 32767) - { - ess->dsp.record_buffer[(c_record & 0xfffe) + 1] = 32767; - } - } -#endif - buffer[c] += (int32_t) out_l; buffer[c + 1] += (int32_t) out_r; } -#if 0 - ess->mixer_sbpro.record_pos_write_music += ((len * 2 * ess->dsp.sb_freq) / MUSIC_FREQ); - ess->mixer_sbpro.record_pos_write_music &= 0xfffe; - - if (ess->mixer_sbpro.record_pos_write_music < ess->mixer_sbpro.record_pos_write_cd) - { - ess->dsp.record_pos_write = ess->mixer_sbpro.record_pos_write_music; - } -#endif - ess->opl.reset_buffer(ess->opl.priv); } @@ -530,51 +476,11 @@ ess_filter_cd_audio(int channel, double *buffer, void *priv) const ess_t *ess = (ess_t *) priv; const ess_mixer_t *mixer = &ess->mixer_sbpro; double c; -#if 0 - double rec_pos = ess->mixer_sbpro.record_pos_write_cd; - double rec_pos_sigma = ess->mixer_sbpro.record_pos_write_cd_sigma; - int c_record; - int selector = channel ? INPUT_MIXER_R : INPUT_MIXER_L; - int rec_buf_pos; - int32_t in; -#endif double cd = channel ? mixer->cd_r : mixer->cd_l; double master = channel ? mixer->master_r : mixer->master_l; c = (*buffer * cd) / 3.0; *buffer = c * master; -#if 0 - in = (mixer->input_selector & selector) ? (int32_t)(c * master) : 0; - - if (ess->dsp.sb_enable_i) - { - // NOTE: this is nearest-neighbor sampling. This is gonna generate aliasing like HECK. Is this what the real card does? - c_record = (int)(rec_pos + rec_pos_sigma); - rec_buf_pos = channel ? ((c_record & 0xfffe) + 1) : (c_record & 0xfffe); - - ess->dsp.record_buffer[rec_buf_pos] += in; - - if (ess->dsp.record_buffer[rec_buf_pos] < -32768) - { - ess->dsp.record_buffer[rec_buf_pos] = -32768; - } - else if (ess->dsp.record_buffer[rec_buf_pos] > 32767) - { - ess->dsp.record_buffer[rec_buf_pos] = 32767; - } - } - - ess->mixer_sbpro.record_pos_write_cd += ((2 * (double)ess->dsp.sb_freq) / MUSIC_FREQ) + rec_pos_sigma; - ess->mixer_sbpro.record_pos_write_cd &= ~1; - ess->mixer_sbpro.record_pos_write_cd_sigma = (double)rec_pos + ((2 * (double)ess->dsp.sb_freq) / MUSIC_FREQ) + rec_pos_sigma - ess->mixer_sbpro.record_pos_write_cd; - - ess->mixer_sbpro.record_pos_write_cd &= 0xfffe; - - if (ess->mixer_sbpro.record_pos_write_cd < ess->mixer_sbpro.record_pos_write_music) - { - ess->dsp.record_pos_write = ess->mixer_sbpro.record_pos_write_cd; - } -#endif } static void * @@ -634,11 +540,6 @@ ess_1688_init(UNUSED(const device_t *info)) ess->mixer_sbpro.ess_id_str[3] = addr & 0xff; } -#if 0 - ess->mixer_sbpro.record_pos_write_cd = ess->dsp.record_pos_write; - ess->mixer_sbpro.record_pos_write_music = ess->dsp.record_pos_write; -#endif - ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); mpu401_init(ess->mpu, 0, -1, M_UART, 1); sb_dsp_set_mpu(&ess->dsp, ess->mpu); From 81029da95024678d7378d1fda1401cda2f063b8b Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 21:46:27 -0300 Subject: [PATCH 57/66] Cleanup: rolling back extraneous change in cpu.h --- src/cpu/cpu.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index f2e1242df..16a9eba10 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -21,14 +21,7 @@ #ifndef EMU_CPU_H #define EMU_CPU_H -#ifndef NO_SOFTFLOAT_INCLUDE #include "softfloat/softfloat.h" -#else -typedef struct floatx80 { // leave alignment to compiler - uint64_t exp; - uint16_t fraction; -} floatx80; -#endif enum { FPU_NONE, From ad616723859a1e4824fae0687958bc52a5fef943 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 22:21:27 -0300 Subject: [PATCH 58/66] Rename ESFMu flag type to ebit --- src/sound/esfmu/esfm.h | 96 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/sound/esfmu/esfm.h b/src/sound/esfmu/esfm.h index eda8bcbb3..766c7312d 100644 --- a/src/sound/esfmu/esfm.h +++ b/src/sound/esfmu/esfm.h @@ -74,7 +74,7 @@ int16_t ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx); // These are fake types just for syntax sugar. // Beware of their underlying types when reading/writing to them. -typedef uint8_t flag; +typedef uint8_t ebit; typedef uint8_t uint2; typedef uint8_t uint3; typedef uint8_t uint4; @@ -110,7 +110,7 @@ typedef struct _esfm_write_buf uint64_t timestamp; uint16_t address; uint8_t data; - flag valid; + ebit valid; } esfm_write_buf; @@ -137,16 +137,16 @@ typedef struct _esfm_slot_internal uint19 phase_acc; uint10 phase_out; - flag phase_reset; - flag *key_on; - flag key_on_gate; + ebit phase_reset; + ebit *key_on; + ebit key_on_gate; uint2 eg_state; - flag eg_delay_run; - flag eg_delay_transitioned_10; - flag eg_delay_transitioned_10_gate; - flag eg_delay_transitioned_01; - flag eg_delay_transitioned_01_gate; + 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; @@ -178,18 +178,18 @@ struct _esfm_slot uint4 sustain_lvl; uint4 release_rate; - flag tremolo_en; - flag tremolo_deep; - flag vibrato_en; - flag vibrato_deep; - flag emu_connection_typ; - flag env_sustaining; - flag ksr; + 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 - flag emu_key_on; + ebit emu_key_on; // Internal state esfm_slot_internal in; @@ -201,11 +201,11 @@ struct _esfm_channel esfm_slot slots[4]; uint5 channel_idx; int16 output[2]; - flag key_on; - flag emu_mode_4op_enable; + ebit key_on; + ebit emu_mode_4op_enable; // Only for 17th and 18th channels - flag key_on_2; - flag emu_mode_4op_enable_2; + ebit key_on_2; + ebit emu_mode_4op_enable_2; }; #define ESFM_WRITEBUF_SIZE 1024 @@ -217,66 +217,66 @@ struct _esfm_chip int32 output_accm[2]; uint16 addr_latch; - flag emu_wavesel_enable; - flag emu_newmode; - flag native_mode; + ebit emu_wavesel_enable; + ebit emu_newmode; + ebit native_mode; - flag keyscale_mode; + ebit keyscale_mode; // Global state uint36 eg_timer; uint10 global_timer; uint8 eg_clocks; - flag eg_tick; - flag eg_timer_overflow; + ebit eg_tick; + ebit eg_timer_overflow; uint8 tremolo; uint8 tremolo_pos; uint8 vibrato_pos; uint23 lfsr; - flag rm_hh_bit2; - flag rm_hh_bit3; - flag rm_hh_bit7; - flag rm_hh_bit8; - flag rm_tc_bit3; - flag rm_tc_bit5; + 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; - flag emu_vibrato_deep; - flag emu_tremolo_deep; + ebit emu_vibrato_deep; + ebit emu_tremolo_deep; double timer_accumulator[2]; uint8 timer_reload[2]; uint8 timer_counter[2]; - flag timer_enable[2]; - flag timer_mask[2]; - flag timer_overflow[2]; - flag irq_bit; + 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. - flag test_bit_w0_r5_eg_halt; + 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!) */ - flag test_bit_1_distort; + ebit test_bit_1_distort; // Seems to do nothing. - flag test_bit_2; + ebit test_bit_2; // Seems to do nothing. - flag test_bit_3; + ebit test_bit_3; // Appears to attenuate the output by about 3 dB. - flag test_bit_4_attenuate; + ebit test_bit_4_attenuate; // Written on bit 5, read back from bit 0. Seems to do nothing. - flag test_bit_w5_r0; + ebit test_bit_w5_r0; // Resets all phase generators and holds them in the reset state while this bit is set. - flag test_bit_6_phase_stop_reset; + ebit test_bit_6_phase_stop_reset; // Seems to do nothing. - flag test_bit_7; + ebit test_bit_7; esfm_write_buf write_buf[ESFM_WRITEBUF_SIZE]; size_t write_buf_start; From 8bfcfec280c3c531bfded21e59733616acf193ac Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 23:07:12 -0300 Subject: [PATCH 59/66] Cleanup: clang-format --- src/sound/snd_ess.c | 226 +++++++------- src/sound/snd_opl_esfm.c | 30 +- src/sound/snd_sb_dsp.c | 645 +++++++++++++++++---------------------- 3 files changed, 392 insertions(+), 509 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index b07c2d18f..38610c6ee 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -13,7 +13,7 @@ * Authors: Sarah Walker, * Miran Grca, * TheCollector1995, - * Cacodemon345, + * Cacodemon345, * Kagamiin~, * * Copyright 2008-2020 Sarah Walker. @@ -49,7 +49,6 @@ #include <86box/snd_sb.h> #include <86box/plat_unused.h> - // clang-format off static const double sb_att_4dbstep_3bits[] = { 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0 @@ -74,7 +73,6 @@ static const double sb_att_2dbstep_4bits[] = { }; // clang-format on - /* SB PRO */ typedef struct ess_mixer_t { double master_l; @@ -109,21 +107,22 @@ typedef struct ess_mixer_t { } ess_mixer_t; typedef struct ess_t { - uint8_t mixer_enabled; - fm_drv_t opl; - sb_dsp_t dsp; + uint8_t mixer_enabled; + fm_drv_t opl; + sb_dsp_t dsp; ess_mixer_t mixer_sbpro; - mpu_t *mpu; - void *gameport; + mpu_t *mpu; + void *gameport; uint16_t gameport_addr; - void *opl_mixer; - void (*opl_mix)(void*, double*, double*); + void *opl_mixer; + void (*opl_mix)(void *, double *, double *); } ess_t; -static double ess_mixer_get_vol_4bit(uint8_t vol) +static double +ess_mixer_get_vol_4bit(uint8_t vol) { return sb_att_2dbstep_4bits[vol & 0x0F] / 32767.0; } @@ -137,8 +136,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) if (!(addr & 1)) { mixer->index = val; mixer->regs[0x01] = val; - if (val == 0x40) - { + if (val == 0x40) { mixer->ess_id_str_pos = 0; } } else { @@ -153,10 +151,10 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) /* Initialize ESS regs. */ mixer->regs[0x14] = mixer->regs[0x32] = 0x88; - mixer->regs[0x36] = 0x88; - mixer->regs[0x38] = 0x88; - mixer->regs[0x3a] = 0x00; - mixer->regs[0x3e] = 0x00; + mixer->regs[0x36] = 0x88; + mixer->regs[0x38] = 0x88; + mixer->regs[0x3a] = 0x00; + mixer->regs[0x3e] = 0x00; sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2); } else { @@ -171,24 +169,24 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) 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] / 32768.0; - mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2) | (mic_vol_2bit << 4) | (mic_vol_2bit << 6); - break; - } + { + uint8_t mic_vol_2bit = (mixer->regs[0x0a] >> 1) & 0x3; + mixer->mic_l = mixer->mic_r = sb_att_7dbstep_2bits[mic_vol_2bit] / 32768.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; + 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; } break; @@ -202,20 +200,13 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0x1C: - if ((mixer->regs[0x1C] & 0x07) == 0x07) - { + if ((mixer->regs[0x1C] & 0x07) == 0x07) { mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R; - } - else if ((mixer->regs[0x1C] & 0x07) == 0x06) - { + } else if ((mixer->regs[0x1C] & 0x07) == 0x06) { mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R; - } - else if ((mixer->regs[0x1C] & 0x06) == 0x02) - { + } else if ((mixer->regs[0x1C] & 0x06) == 0x02) { mixer->input_selector = INPUT_CD_L | INPUT_CD_R; - } - else if ((mixer->regs[0x1C] & 0x02) == 0) - { + } else if ((mixer->regs[0x1C] & 0x02) == 0) { mixer->input_selector = INPUT_MIC; } break; @@ -255,26 +246,22 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[mixer->index] &= ~0x8; break; - case 0x40: { - /* TODO: Implement "Read-Sequence-Key" method of software address selection - * (needed for ESSCFG.EXE to work properly) */ - + case 0x40: + { uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); - if (0) - { + if (0) { /* 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) - { + 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); + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); } } switch ((mixer->regs[0x40] >> 5) & 0x7) { @@ -315,7 +302,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } default: - //pclog("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + // pclog("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -375,21 +362,18 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x40: - if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) - { + 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 - { + } else { return mixer->regs[mixer->index]; } default: - //pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + // pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } @@ -406,10 +390,10 @@ ess_mixer_reset(ess_t *ess) void ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) { - ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; - double out_l = 0.0; - double out_r = 0.0; + ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; + double out_l = 0.0; + double out_r = 0.0; sb_dsp_update(&ess->dsp); @@ -440,11 +424,11 @@ ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) void ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) { - ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; - double out_l = 0.0; - double out_r = 0.0; - const int32_t *opl_buf = NULL; + ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; + double out_l = 0.0; + double out_r = 0.0; + const int32_t *opl_buf = NULL; opl_buf = ess->opl.update(ess->opl.priv); @@ -473,13 +457,13 @@ ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) void ess_filter_cd_audio(int channel, double *buffer, void *priv) { - const ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; - double c; - double cd = channel ? mixer->cd_r : mixer->cd_l; - double master = channel ? mixer->master_r : mixer->master_l; + const ess_t *ess = (ess_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_sbpro; + double c; + double cd = channel ? mixer->cd_r : mixer->cd_l; + double master = channel ? mixer->master_r : mixer->master_l; - c = (*buffer * cd) / 3.0; + c = (*buffer * cd) / 3.0; *buffer = c * master; } @@ -492,8 +476,8 @@ ess_1688_init(UNUSED(const device_t *info)) 2x6, 2xA, 2xC, 2xE -> DSP chip 2x8, 2x9, 388 and 389 FM chip (9 voices) 2x0+10 to 2x0+13 CDROM interface. */ - ess_t *ess = calloc(sizeof(ess_t), 1); - uint16_t addr = device_get_config_hex16("base"); + ess_t *ess = calloc(sizeof(ess_t), 1); + uint16_t addr = device_get_config_hex16("base"); fm_driver_get(FM_ESFM, &ess->opl); @@ -544,7 +528,7 @@ ess_1688_init(UNUSED(const device_t *info)) 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 = gameport_add(&gameport_pnp_device); ess->gameport_addr = 0x200; gameport_remap(ess->gameport, ess->gameport_addr); @@ -568,95 +552,97 @@ ess_speed_changed(void *priv) sb_dsp_speed_changed(&ess->dsp); } +// clang-format off static const device_config_t ess_config[] = { { - .name = "base", - .description = "Address", - .type = CONFIG_HEX16, + .name = "base", + .description = "Address", + .type = CONFIG_HEX16, .default_string = "", - .default_int = 0x220, - .file_filter = "", - .spinner = { 0 }, - .selection = { + .default_int = 0x220, + .file_filter = "", + .spinner = { 0 }, + .selection = { { .description = "0x220", - .value = 0x220 + .value = 0x220 }, { .description = "0x240", - .value = 0x240 + .value = 0x240 }, { .description = "" } } }, { - .name = "irq", - .description = "IRQ", - .type = CONFIG_SELECTION, + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, .default_string = "", - .default_int = 5, - .file_filter = "", - .spinner = { 0 }, - .selection = { + .default_int = 5, + .file_filter = "", + .spinner = { 0 }, + .selection = { { .description = "IRQ 2", - .value = 2 + .value = 2 }, { .description = "IRQ 5", - .value = 5 + .value = 5 }, { .description = "IRQ 7", - .value = 7 + .value = 7 }, { .description = "IRQ 10", - .value = 10 + .value = 10 }, { .description = "" } } }, { - .name = "dma", - .description = "DMA", - .type = CONFIG_SELECTION, + .name = "dma", + .description = "DMA", + .type = CONFIG_SELECTION, .default_string = "", - .default_int = 1, - .file_filter = "", - .spinner = { 0 }, - .selection = { + .default_int = 1, + .file_filter = "", + .spinner = { 0 }, + .selection = { { .description = "DMA 0", - .value = 0 + .value = 0 }, { .description = "DMA 1", - .value = 1 + .value = 1 }, { .description = "DMA 3", - .value = 3 + .value = 3 }, { .description = "" } } }, { - .name = "opl", - .description = "Enable OPL", - .type = CONFIG_BINARY, + .name = "opl", + .description = "Enable OPL", + .type = CONFIG_BINARY, .default_string = "", - .default_int = 1 + .default_int = 1 }, { - .name = "receive_input", - .description = "Receive input (SB MIDI)", - .type = CONFIG_BINARY, + .name = "receive_input", + .description = "Receive input (SB MIDI)", + .type = CONFIG_BINARY, .default_string = "", - .default_int = 1 + .default_int = 1 }, { .name = "", .description = "", .type = CONFIG_END } }; +// clang-format on const device_t ess_1688_device = { .name = "ESS Technology ES1688", diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index 174830523..c36ec4160 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -7,8 +7,8 @@ * This file is part of the 86Box distribution. * * ESFMu ESFM emulator. - * - * + * + * * Authors: Fred N. van Kempen, * Miran Grca, * Alexey Khokholov (Nuke.YKT) @@ -29,19 +29,18 @@ #include "esfmu/esfm.h" #define HAVE_STDARG_H -#define NO_SOFTFLOAT_INCLUDE #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 +#define RSM_FRAC 10 typedef struct { esfm_chip opl; - int8_t flags; - int8_t pad; + int8_t flags; + int8_t pad; uint16_t port; uint8_t status; @@ -180,7 +179,7 @@ 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; + dev->flags = FLAG_CYCLES | FLAG_OPL3; /* Initialize the ESFMu object. */ ESFM_init(&dev->opl); @@ -207,8 +206,8 @@ esfm_drv_update(void *priv) return dev->buffer; esfm_drv_generate_stream(dev, - &dev->buffer[dev->pos * 2], - music_pos_global - dev->pos); + &dev->buffer[dev->pos * 2], + music_pos_global - dev->pos); for (; dev->pos < music_pos_global; dev->pos++) { dev->buffer[dev->pos * 2] /= 2; @@ -218,7 +217,6 @@ esfm_drv_update(void *priv) return dev->buffer; } - static void esfm_drv_reset_buffer(void *priv) { @@ -239,8 +237,7 @@ esfm_drv_read(uint16_t port, void *priv) uint8_t ret = 0xff; - switch (port & 0x0003) - { + switch (port & 0x0003) { case 0x0000: ret = dev->status; if (dev->status & STAT_TMR_OVER) @@ -248,8 +245,7 @@ esfm_drv_read(uint16_t port, void *priv) break; case 0x0001: ret = ESFM_read_port(&dev->opl, port & 3); - switch (dev->opl.addr_latch & 0x5ff) - { + switch (dev->opl.addr_latch & 0x5ff) { case 0x402: ret = dev->timer_count[0]; break; @@ -277,14 +273,12 @@ esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val) ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); - if (dev->opl.native_mode) - { + if (dev->opl.native_mode) { p -= 0x400; p &= 0x1ff; } - switch (p) - { + switch (p) { case 0x002: /* Timer 1 */ dev->timer_count[0] = val; esfm_log("Timer 0 count now: %i\n", dev->timer_count[0]); diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 03a6ba4ba..5290d6bc5 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -120,6 +120,7 @@ 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. @@ -235,6 +236,7 @@ uint16_t espcm3_dpcm_tables[1024] = 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]; @@ -387,8 +389,7 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) } /* NOTE: not on ES1688, apparently; investigate on ES1868 */ - if (IS_ESS(dsp) && dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688) - { + 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 @@ -523,17 +524,19 @@ 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) +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); + 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) +static unsigned int +sb_ess_get_dma_len(sb_dsp_t *dsp) { return 0x10000U - sb_ess_get_dma_counter(dsp); } @@ -605,11 +608,11 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) } void -sb_start_dma_ess(sb_dsp_t* dsp) +sb_start_dma_ess(sb_dsp_t *dsp) { - uint8_t real_format = 0; + uint8_t real_format = 0; dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp); - uint32_t len = sb_ess_get_dma_len(dsp); + uint32_t len = sb_ess_get_dma_len(dsp); if (IS_ESS(dsp)) { dma_set_drq(dsp->sb_8_dmanum, 0); @@ -627,26 +630,28 @@ sb_start_dma_ess(sb_dsp_t* dsp) } void -sb_stop_dma_ess(sb_dsp_t* dsp) +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) +static void +sb_ess_update_dma_status(sb_dsp_t *dsp) { - bool dma_en = (ESSreg(0xB8) & 1)?true:false; + 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); + 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); } } @@ -682,8 +687,8 @@ int sb_16_read_dma(void *priv) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; - int temp, ret = 0; - int dma_flags, dma_ch = dsp->sb_16_dmanum; + 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) ret = dma_channel_read(dma_ch); @@ -697,7 +702,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) @@ -706,7 +711,7 @@ sb_16_read_dma(void *priv) dma_flags = temp & DMA_OVER; temp &= ~DMA_OVER; ret |= (temp << 8) | dma_flags; - } + } } } @@ -717,9 +722,9 @@ 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; - + 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 { @@ -732,10 +737,10 @@ 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; } } @@ -747,28 +752,40 @@ sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy) { uint8_t t = 0x00; /* IRQ control */ - if (legacy) - { + 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; + 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) - { + if (legacy) { t |= 0x80; } switch (dsp->sb_8_dmanum) { - case 0: t |= 0x5; break; - case 1: t |= 0xA; break; - case 3: t |= 0xF; break; + case 0: + t |= 0x5; + break; + case 1: + t |= 0xA; + break; + case 3: + t |= 0xF; + break; } ESSreg(0xB2) = (ESSreg(0xB2) & 0xF0) | t; } @@ -830,10 +847,11 @@ 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) +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; + double freq = (7160000.0 / (256.0 - ((double) val))) * 41.0; + int temp = (int) freq; ESSreg(0xA2) = val; if (dsp->sb_freq != temp) @@ -843,7 +861,8 @@ static void sb_ess_update_reg_a2(sb_dsp_t *dsp, uint8_t val) /* 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) +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; @@ -855,7 +874,8 @@ static void sb_ess_update_filter_freq(sb_dsp_t *dsp) sb_ess_update_reg_a2(dsp, (uint8_t) temp); } -static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) +static uint8_t +sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) { switch (reg) { default: @@ -865,28 +885,31 @@ static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) return 0xFF; } -static void sb_ess_update_autolen(sb_dsp_t *dsp) { +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) +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; - } + { + 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; @@ -909,7 +932,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) * 01=Stereo * 10=Mono * 11=Reserved */ - chg = ESSreg(reg) ^ data; + chg = ESSreg(reg) ^ data; ESSreg(reg) = data; if (chg & 0x3) { if (dsp->sb_16_enable || dsp->sb_8_enable) { @@ -926,45 +949,44 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) } break; - case 0xB1: /* Legacy Audio Interrupt Control */ + 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; + 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; + 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; + 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); + if (chg & 0x40) + sb_ess_update_dma_status(dsp); break; case 0xB5: /* DAC Direct Access Holding (low) */ case 0xB6: /* DAC Direct Access Holding (high) */ @@ -980,7 +1002,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) * 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; + chg = ESSreg(reg) ^ data; ESSreg(reg) = data; if (chg & 4) @@ -1004,7 +1026,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) * 0=first DMA is write (for DAC) * bit 0 DMA xfer enable 1=DMA is allowed to proceed */ data &= 0xF; - chg = ESSreg(reg) ^ data; + chg = ESSreg(reg) ^ data; ESSreg(reg) = data; if (chg & 1) { @@ -1017,20 +1039,18 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->ess_reload_len = 1; } - if (chg & 0x4) - { - if (dsp->sb_16_enable) - { + if (chg & 0x4) { + if (dsp->sb_16_enable) { dsp->sb_16_autoinit = (ESSreg(0xB8) & 0x4) != 0; } - if (dsp->sb_8_enable) - { + 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 */ + if (chg & 0xA) + sb_stop_dma_ess(dsp); /* changing capture/playback direction? stop DMA to reinit */ sb_ess_update_dma_status(dsp); } break; @@ -1044,7 +1064,7 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) break; default: - sb_dsp_log("UNKNOWN ESS register write reg=%02xh val=%02xh\n",reg,data); + sb_dsp_log("UNKNOWN ESS register write reg=%02xh val=%02xh\n", reg, data); break; } } @@ -1059,8 +1079,7 @@ 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; } @@ -1068,27 +1087,17 @@ sb_exec_command(sb_dsp_t *dsp) if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) { dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6); return; - } - else if (dsp->sb_command == 0xC2) - { + } else if (dsp->sb_command == 0xC2) { sb_ess_write_reg(dsp, 0xC3, dsp->sb_data[0]); - } - else if (dsp->sb_command == 0xC3) - { + } else if (dsp->sb_command == 0xC3) { sb_add_data(dsp, sb_ess_read_reg(dsp, 0xC3)); - } - else if (dsp->sb_command == 0xCE) - { + } else if (dsp->sb_command == 0xCE) { sb_add_data(dsp, sb_ess_read_reg(dsp, 0xCF)); - } - else if (dsp->sb_command == 0xCF) - { + } else if (dsp->sb_command == 0xCF) { sb_ess_write_reg(dsp, 0xCF, dsp->sb_data[0]); - } - else if (dsp->sb_command == 0xC0) { + } 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) { + } else if (dsp->sb_command < 0xC0 && dsp->ess_extended_mode) { sb_ess_write_reg(dsp, dsp->sb_command, dsp->sb_data[0]); } return; @@ -1216,8 +1225,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; dsp->ess_dma_counter++; - if (dsp->sb_command == 0x17) - { + if (dsp->sb_command == 0x17) { dsp->sb_8_length--; dsp->ess_dma_counter++; } @@ -1330,11 +1338,9 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0x65: /* 4-bit ESPCM output with reference */ case 0x64: /* 4-bit ESPCM output */ - if (IS_ESS(dsp)) - { + if (IS_ESS(dsp)) { if (dsp->espcm_mode != ESPCM_4 - || (dsp->sb_8_enable && dsp->sb_8_pause)) - { + || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1344,11 +1350,9 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0x67: /* 3-bit ESPCM output with reference */ case 0x66: /* 3-bit ESPCM output */ - if (IS_ESS(dsp)) - { + if (IS_ESS(dsp)) { if (dsp->espcm_mode != ESPCM_3 - || (dsp->sb_8_enable && dsp->sb_8_pause)) - { + || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1358,11 +1362,9 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0x6D: /* 1-bit ESPCM output with reference */ case 0x6C: /* 1-bit ESPCM output */ - if (IS_ESS(dsp)) - { + if (IS_ESS(dsp)) { if (dsp->espcm_mode != ESPCM_1 - || (dsp->sb_8_enable && dsp->sb_8_pause)) - { + || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1372,11 +1374,9 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0x6F: /* 4-bit ESPCM input with reference */ case 0x6E: /* 4-bit ESPCM input */ - if (IS_ESS(dsp)) - { + if (IS_ESS(dsp)) { if (dsp->espcm_mode != ESPCM_4E - || (dsp->sb_8_enable && dsp->sb_8_pause)) - { + || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1394,8 +1394,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; dsp->ess_dma_counter++; - if (dsp->sb_command == 0x75) - { + if (dsp->sb_command == 0x75) { dsp->sb_8_length--; dsp->ess_dma_counter++; } @@ -1409,8 +1408,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; dsp->ess_dma_counter++; - if (dsp->sb_command == 0x77) - { + if (dsp->sb_command == 0x77) { dsp->sb_8_length--; dsp->ess_dma_counter++; } @@ -1561,9 +1559,9 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0xE1: /* Get DSP version */ if (IS_ESS(dsp)) { - sb_add_data(dsp, 0x3); - sb_add_data(dsp, 0x1); - break; + sb_add_data(dsp, 0x3); + sb_add_data(dsp, 0x1); + break; } if (IS_AZTECH(dsp)) { if (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11) { @@ -1603,17 +1601,17 @@ sb_exec_command(sb_dsp_t *dsp) case 0xE7: /* ???? */ /* ESS detect/read config on ESS cards */ if (IS_ESS(dsp)) { switch (dsp->sb_subtype) { - default: - break; - case SB_SUBTYPE_ESS_ES688: - sb_add_data(dsp, 0x68); - sb_add_data(dsp, 0x80 | 0x04); - break; - case SB_SUBTYPE_ESS_ES1688: - // Determined via Windows driver debugging. - sb_add_data(dsp, 0x68); - sb_add_data(dsp, 0x80 | 0x09); - break; + default: + break; + case SB_SUBTYPE_ESS_ES688: + sb_add_data(dsp, 0x68); + sb_add_data(dsp, 0x80 | 0x04); + break; + case SB_SUBTYPE_ESS_ES1688: + // Determined via Windows driver debugging. + sb_add_data(dsp, 0x68); + sb_add_data(dsp, 0x80 | 0x09); + break; } } break; @@ -1623,8 +1621,7 @@ sb_exec_command(sb_dsp_t *dsp) case 0xF2: /* Trigger 8-bit IRQ */ sb_dsp_log("Trigger IRQ\n"); if (IS_ESS(dsp)) { - if (!timer_is_enabled(&dsp->irq_timer)) - { + if (!timer_is_enabled(&dsp->irq_timer)) { timer_set_delay_u64(&dsp->irq_timer, (100ULL * TIMER_USEC)); } } else { @@ -1685,7 +1682,7 @@ sb_write(uint16_t a, uint8_t v, void *priv) if (dsp->sb_type < SB16 && (!IS_ESS(dsp) || (IS_ESS(dsp) && ((a & 0xF) != 0xE)))) a &= 0xfffe; - //pclog("sb: port write %03x %02x\n", a, v); + // pclog("sb: port write %03x %02x\n", a, v); switch (a & 0xF) { case 6: /* Reset */ @@ -1697,14 +1694,13 @@ sb_write(uint16_t a, uint8_t v, void *priv) dsp->sbreset = v; } - if (!(v & 2) && (dsp->espcm_fifo_reset & 2)) - { + 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; + 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) { @@ -1732,23 +1728,18 @@ 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) - { - if (dsp->sb_subtype == SB_SUBTYPE_ESS_ES1688) - { + if (IS_ESS(dsp) && dsp->sb_command >= 0x64 && dsp->sb_command <= 0x6F) { + if (dsp->sb_subtype == SB_SUBTYPE_ESS_ES1688) { + sb_commands[dsp->sb_command] = 2; + } else { sb_commands[dsp->sb_command] = 2; } - else - { - sb_commands[dsp->sb_command] = 2; - } - } - else if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { + } 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) { + || dsp->sb_command == 0xC7 || dsp->sb_command == 0xCE) { sb_commands[dsp->sb_command] = 0; } else { sb_commands[dsp->sb_command] = -1; @@ -1780,11 +1771,9 @@ 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) - { + if (dsp->sb_type < SB16) { /* Exception: ESS AudioDrive does not alias port base+0xf */ - if (!IS_ESS(dsp) || !((a & 0xF) == 0xF)) - { + if (!IS_ESS(dsp) || !((a & 0xF) == 0xF)) { a &= 0xfffe; } } @@ -1807,19 +1796,18 @@ 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 (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; - uint8_t fifo_half = 0; + 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; + uint8_t fifo_half = 0; uint8_t irq_generic = dsp->ess_irq_generic ? 0x04 : 0x00; - uint8_t irq_fifohe = 0; // Unimplemented - uint8_t irq_dmactr = dsp->ess_irq_dmactr ? 0x01 : 0x00; + uint8_t irq_fifohe = 0; // Unimplemented + 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; @@ -1856,8 +1844,7 @@ sb_read(uint16_t a, void *priv) } break; case 0xF: /* 16-bit ack */ - if (!IS_ESS(dsp)) - { + if (!IS_ESS(dsp)) { dsp->sb_irq16 = 0; if (!dsp->sb_irq8) dsp->irq_update(dsp->irq_priv, 0); @@ -1869,8 +1856,8 @@ sb_read(uint16_t a, void *priv) default: break; } - - //pclog("sb: port read %03x %02x\n", a, ret); + + // pclog("sb: port read %03x %02x\n", a, ret); return ret; } @@ -2068,16 +2055,13 @@ sb_ess_finish_dma(sb_dsp_t *dsp) 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)) - { + 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) - { + if (val & DMA_OVER) { break; } } @@ -2098,8 +2082,7 @@ pollsb(void *priv) switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned */ - if (!dsp->sb_8_pause) - { + 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 */ @@ -2108,7 +2091,7 @@ pollsb(void *priv) 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); + dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else @@ -2121,15 +2104,14 @@ pollsb(void *priv) } break; case 0x10: /* Mono signed */ - if (!dsp->sb_8_pause) - { + 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); + dsp->sbleftright ? "left" : "right", data[0], dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else @@ -2142,8 +2124,7 @@ pollsb(void *priv) } break; case 0x20: /* Stereo unsigned */ - if (!dsp->sb_8_pause) - { + 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)) @@ -2155,8 +2136,7 @@ pollsb(void *priv) } break; case 0x30: /* Stereo signed */ - if (!dsp->sb_8_pause) - { + 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)) @@ -2169,8 +2149,7 @@ pollsb(void *priv) break; case ADPCM_4: - if (!dsp->sb_8_pause) - { + if (!dsp->sb_8_pause) { if (dsp->sbdacpos) tempi = (dsp->sbdat2 & 0xF) + dsp->sbstep; else @@ -2202,7 +2181,7 @@ pollsb(void *priv) if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 4, dsp->stereo, %s channel, %04X\n", - dsp->sbleftright ? "left" : "right", dsp->sbdat); + dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else @@ -2214,8 +2193,7 @@ pollsb(void *priv) break; case ADPCM_26: - if (!dsp->sb_8_pause) - { + if (!dsp->sb_8_pause) { if (!dsp->sbdacpos) tempi = (dsp->sbdat2 >> 5) + dsp->sbstep; else if (dsp->sbdacpos == 1) @@ -2249,7 +2227,7 @@ pollsb(void *priv) if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 26, dsp->stereo, %s channel, %04X\n", - dsp->sbleftright ? "left" : "right", dsp->sbdat); + dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else @@ -2261,8 +2239,7 @@ pollsb(void *priv) break; case ADPCM_2: - if (!dsp->sb_8_pause) - { + if (!dsp->sb_8_pause) { tempi = ((dsp->sbdat2 >> ((3 - dsp->sbdacpos) * 2)) & 3) + dsp->sbstep; if (tempi < 0) tempi = 0; @@ -2290,7 +2267,7 @@ pollsb(void *priv) if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 2, dsp->stereo, %s channel, %04X\n", - dsp->sbleftright ? "left" : "right", dsp->sbdat); + dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else @@ -2302,97 +2279,77 @@ pollsb(void *priv) break; case ESPCM_4: - if (dsp->espcm_sample_idx >= 19) - { + if (dsp->espcm_sample_idx >= 19) { dsp->espcm_sample_idx = 0; } - if (dsp->espcm_sample_idx == 0) - { + if (dsp->espcm_sample_idx == 0) { sb_espcm_fifoctl_run(dsp); - if (fifo_get_empty(dsp->espcm_fifo)) - { + 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) - { + 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)) - { + 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 - { + } else { tempi = dsp->espcm_byte_buffer[0] >> 4; } - if (dsp->espcm_sample_idx == 18) - { + 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]; + data[0] = espcm_range_map[tempi]; dsp->sbdat = data[0] << 8; - if (dsp->stereo) - { + if (dsp->stereo) { sb_dsp_log("pollsb: ESPCM 4, dsp->stereo, %s channel, %04X\n", - dsp->sbleftright ? "left" : "right", dsp->sbdat); + 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) - { + if (dsp->espcm_sample_idx >= 19) { dsp->espcm_sample_idx = 0; } - if (dsp->espcm_sample_idx == 0) - { + if (dsp->espcm_sample_idx == 0) { sb_espcm_fifoctl_run(dsp); - if (fifo_get_empty(dsp->espcm_fifo)) - { + 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_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++) - { + } 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)) - { + 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) - { + if (tempi < 4) { break; } @@ -2409,24 +2366,19 @@ pollsb(void *priv) 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]; + 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++) - { + } 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)) - { + 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) - { + if (tempi < 4) { break; } @@ -2439,53 +2391,44 @@ pollsb(void *priv) 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]; + 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]; + } 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) - { + 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]; + data[0] = espcm_range_map[tempi]; dsp->sbdat = data[0] << 8; - if (dsp->stereo) - { + if (dsp->stereo) { sb_dsp_log("pollsb: ESPCM 3, dsp->stereo, %s channel, %04X\n", - dsp->sbleftright ? "left" : "right", dsp->sbdat); + 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_1: - if (dsp->espcm_sample_idx >= 19) - { + if (dsp->espcm_sample_idx >= 19) { dsp->espcm_sample_idx = 0; } - if (dsp->espcm_sample_idx == 0) - { + if (dsp->espcm_sample_idx == 0) { sb_espcm_fifoctl_run(dsp); - if (fifo_get_empty(dsp->espcm_fifo)) - { + if (fifo_get_empty(dsp->espcm_fifo)) { break; } dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo); @@ -2494,12 +2437,9 @@ pollsb(void *priv) 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) - { + } else if (dsp->espcm_sample_idx == 3 | dsp->espcm_sample_idx == 11) { sb_espcm_fifoctl_run(dsp); - if (fifo_get_empty(dsp->espcm_fifo)) - { + if (fifo_get_empty(dsp->espcm_fifo)) { break; } dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo); @@ -2507,35 +2447,29 @@ pollsb(void *priv) tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4; dsp->espcm_byte_buffer[0] >>= 1; - } - else - { + } else { tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4; dsp->espcm_byte_buffer[0] >>= 1; } - if (dsp->espcm_sample_idx == 18) - { + 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]; + data[0] = espcm_range_map[tempi]; dsp->sbdat = data[0] << 8; - if (dsp->stereo) - { + if (dsp->stereo) { sb_dsp_log("pollsb: ESPCM 1, dsp->stereo, %s channel, %04X\n", - dsp->sbleftright ? "left" : "right", dsp->sbdat); + 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; @@ -2544,7 +2478,6 @@ pollsb(void *priv) break; } - 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; @@ -2556,23 +2489,19 @@ pollsb(void *priv) 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) - { + 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) - { + if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 1); dsp->ess_irq_dmactr = true; } } - uint32_t temp = dsp->ess_dma_counter & 0xffff; + uint32_t temp = dsp->ess_dma_counter & 0xffff; dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp); dsp->ess_dma_counter += temp; } @@ -2634,23 +2563,19 @@ pollsb(void *priv) 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) - { + 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) - { + if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 0); dsp->ess_irq_dmactr = true; } } - uint32_t temp = dsp->ess_dma_counter & 0xffff; + uint32_t temp = dsp->ess_dma_counter & 0xffff; dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp); dsp->ess_dma_counter += temp; } @@ -2714,60 +2639,47 @@ sb_poll_i(void *priv) 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; + 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++) - { + for (i = 0; i < 19; i++) { s = dsp->espcm_sample_buffer[i]; - if (s < min_sample) - { + if (s < min_sample) { min_sample = s; } - if (s > max_sample) - { + if (s > max_sample) { max_sample = s; } } - if (min_sample < 0) - { + if (min_sample < 0) { min_sample = -min_sample; } - if (max_sample < 0) - { + if (max_sample < 0) { max_sample = -max_sample; } - if (min_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]) - { + 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++) - { + 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++) - { + 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) - { + if (sigma < 0) { sigma = -sigma; } - if (sigma > last_sigma) - { + if (sigma > last_sigma) { break; } last_sigma = sigma; @@ -2781,8 +2693,7 @@ sb_poll_i(void *priv) dsp->sb_8_length--; dsp->ess_dma_counter++; - for (i = 1; i < 10; i++) - { + 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--; @@ -2807,23 +2718,19 @@ sb_poll_i(void *priv) 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) - { + 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) - { + if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 1); dsp->ess_irq_dmactr = true; } } - uint32_t temp = dsp->ess_dma_counter & 0xffff; + uint32_t temp = dsp->ess_dma_counter & 0xffff; dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp); dsp->ess_dma_counter += temp; } @@ -2881,23 +2788,19 @@ sb_poll_i(void *priv) 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) - { + 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) - { + if (ESSreg(0xB1) & 0x40) { sb_irq(dsp, 0); dsp->ess_irq_dmactr = true; } } - uint32_t temp = dsp->ess_dma_counter & 0xffff; + uint32_t temp = dsp->ess_dma_counter & 0xffff; dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp); dsp->ess_dma_counter += temp; } From d846a168260ef0b55e2450b7612d244753a43751 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 23:36:56 -0300 Subject: [PATCH 60/66] Cleanup: some touch-ups here and there --- src/sound/snd_ess.c | 6 +++-- src/sound/snd_sb_dsp.c | 57 ++++++++++++++++-------------------------- 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index 38610c6ee..0239506bd 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -49,6 +49,8 @@ #include <86box/snd_sb.h> #include <86box/plat_unused.h> +# define sb_log(fmt, ...) + // clang-format off static const double sb_att_4dbstep_3bits[] = { 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0 @@ -302,7 +304,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } default: - // pclog("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -373,7 +375,7 @@ ess_mixer_read(uint16_t addr, void *priv) } default: - // pclog("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 5290d6bc5..aae7dd458 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -35,7 +35,7 @@ #define ESPCM_4 4 #define ESPCM_3 5 #define ESPCM_1 7 -#define ESPCM_4E 8 // for encoding mode switching +#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 @@ -687,8 +687,9 @@ int sb_16_read_dma(void *priv) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; - int temp, ret = 0; - int dma_flags, dma_ch = dsp->sb_16_dmanum; + + 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) ret = dma_channel_read(dma_ch); @@ -722,8 +723,9 @@ 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; + + 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; @@ -793,7 +795,6 @@ sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy) void sb_dsp_setirq(sb_dsp_t *dsp, int irq) { - uint8_t t = 0x00; sb_dsp_log("IRQ now: %i\n", irq); dsp->sb_irqnum = irq; @@ -805,7 +806,6 @@ sb_dsp_setirq(sb_dsp_t *dsp, int irq) void sb_dsp_setdma8(sb_dsp_t *dsp, int dma) { - uint8_t t = 0x00; sb_dsp_log("8-bit DMA now: %i\n", dma); dsp->sb_8_dmanum = dma; @@ -881,8 +881,6 @@ sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) default: return ESSreg(reg); } - - return 0xFF; } static void @@ -907,7 +905,8 @@ sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) 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; + + dsp->sb_timei = dsp->sb_timeo; break; } case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */ @@ -1305,7 +1304,6 @@ sb_exec_command(sb_dsp_t *dsp) temp = 256 - dsp->sb_data[0]; temp = 1000000 / temp; sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); - // if ((dsp->sb_freq != temp) && (IS_ESS(dsp) || (dsp->sb_type >= SB16))) if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) recalc_sb16_filter(0, temp); dsp->sb_freq = temp; @@ -1339,8 +1337,7 @@ sb_exec_command(sb_dsp_t *dsp) 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)) { + if (dsp->espcm_mode != ESPCM_4 || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1351,8 +1348,7 @@ sb_exec_command(sb_dsp_t *dsp) 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)) { + if (dsp->espcm_mode != ESPCM_3 || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1363,8 +1359,7 @@ sb_exec_command(sb_dsp_t *dsp) 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)) { + if (dsp->espcm_mode != ESPCM_1 || (dsp->sb_8_enable && dsp->sb_8_pause)) { fifo_reset(dsp->espcm_fifo); dsp->espcm_sample_idx = 0; } @@ -1375,14 +1370,12 @@ sb_exec_command(sb_dsp_t *dsp) 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)) { + 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)); - dsp->espcm_sample_idx = 0; } break; case 0x75: /* 4-bit ADPCM output with reference */ @@ -1598,7 +1591,7 @@ sb_exec_command(sb_dsp_t *dsp) case 0xE4: /* Write test register */ dsp->sb_test = dsp->sb_data[0]; break; - case 0xE7: /* ???? */ /* ESS detect/read config on ESS cards */ + case 0xE7: /* ESS detect/read config on ESS cards */ if (IS_ESS(dsp)) { switch (dsp->sb_subtype) { default: @@ -1682,8 +1675,6 @@ sb_write(uint16_t a, uint8_t v, void *priv) if (dsp->sb_type < SB16 && (!IS_ESS(dsp) || (IS_ESS(dsp) && ((a & 0xF) != 0xE)))) a &= 0xfffe; - // pclog("sb: port write %03x %02x\n", a, v); - switch (a & 0xF) { case 6: /* Reset */ if (!dsp->uart_midi) { @@ -1729,17 +1720,16 @@ sb_write(uint16_t a, uint8_t v, void *priv) sb_commands[dsp->sb_command] = 2; } if (IS_ESS(dsp) && dsp->sb_command >= 0x64 && dsp->sb_command <= 0x6F) { - if (dsp->sb_subtype == SB_SUBTYPE_ESS_ES1688) { - sb_commands[dsp->sb_command] = 2; - } else { - sb_commands[dsp->sb_command] = 2; - } + 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 + 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) { + } 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; @@ -1809,8 +1799,7 @@ sb_read(uint16_t a, void *priv) uint8_t irq_fifohe = 0; // Unimplemented 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; + 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); @@ -1857,8 +1846,6 @@ sb_read(uint16_t a, void *priv) break; } - // pclog("sb: port read %03x %02x\n", a, ret); - return ret; } From eb6f4c1118191b232da802dac98a9984bb6a4093 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Fri, 22 Mar 2024 23:49:02 -0300 Subject: [PATCH 61/66] Fixing compiler warning about parentheses --- src/sound/snd_sb_dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index aae7dd458..ad6209fb5 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -2424,7 +2424,7 @@ pollsb(void *priv) 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) { + } 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; From 922a403eb33a580010aee091f93788d4a4a5f388 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 23 Mar 2024 00:52:05 -0300 Subject: [PATCH 62/66] Cleanup: get rid of snd_ess.c; move ess to snd_sb.c/.h --- src/include/86box/snd_sb.h | 57 +++- src/sound/CMakeLists.txt | 2 +- src/sound/snd_ess.c | 661 ------------------------------------- src/sound/snd_sb.c | 536 ++++++++++++++++++++++++++++++ 4 files changed, 585 insertions(+), 671 deletions(-) delete mode 100644 src/sound/snd_ess.c diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h index f236c284a..1ab94c9e1 100644 --- a/src/include/86box/snd_sb.h +++ b/src/include/86box/snd_sb.h @@ -105,15 +105,13 @@ typedef struct sb_ct1745_mixer_t { int input_selector_left; int input_selector_right; -#define INPUT_MIC 1 -#define INPUT_CD_R 2 -#define INPUT_CD_L 4 -#define INPUT_LINE_R 8 -#define INPUT_LINE_L 16 -#define INPUT_MIDI_R 32 -#define INPUT_MIDI_L 64 -#define INPUT_MIXER_L 128 -#define INPUT_MIXER_R 256 +#define INPUT_MIC 1 +#define INPUT_CD_R 2 +#define INPUT_CD_L 4 +#define INPUT_LINE_R 8 +#define INPUT_LINE_L 16 +#define INPUT_MIDI_R 32 +#define INPUT_MIDI_L 64 int mic_agc; @@ -128,6 +126,42 @@ 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; + /*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; @@ -140,6 +174,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; @@ -165,6 +200,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/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index 9206f6be9..9b2cc1416 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_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c - snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c snd_ess.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/snd_ess.c b/src/sound/snd_ess.c deleted file mode 100644 index 0239506bd..000000000 --- a/src/sound/snd_ess.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * 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. - * - * ESS AudioDrive emulation. - * - * - * - * Authors: Sarah Walker, - * Miran Grca, - * TheCollector1995, - * Cacodemon345, - * Kagamiin~, - * - * Copyright 2008-2020 Sarah Walker. - * Copyright 2016-2020 Miran Grca. - * Copyright 2024 Cacodemon345 - * Copyright 2024 Kagamiin~ - */ - -#include -#include -#include -#include -#include -#include -#include -#define HAVE_STDARG_H - -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/filters.h> -#include <86box/gameport.h> -#include <86box/hdc.h> -#include <86box/isapnp.h> -#include <86box/hdc_ide.h> -#include <86box/io.h> -#include <86box/mca.h> -#include <86box/mem.h> -#include <86box/midi.h> -#include <86box/pic.h> -#include <86box/rom.h> -#include <86box/sound.h> -#include <86box/timer.h> -#include <86box/snd_sb.h> -#include <86box/plat_unused.h> - -# define sb_log(fmt, ...) - -// clang-format off -static const double sb_att_4dbstep_3bits[] = { - 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0 -}; - -static const double sb_att_7dbstep_2bits[] = { - 164.0, 6537.0, 14637.0, 32767.0 -}; - -/* Attenuation table for 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 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 -}; -// clang-format on - -/* SB PRO */ -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; - /*see sb_ct1745_mixer for values for input selector*/ - int32_t input_selector; - - 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 ess_t { - uint8_t mixer_enabled; - fm_drv_t opl; - sb_dsp_t dsp; - ess_mixer_t mixer_sbpro; - - mpu_t *mpu; - void *gameport; - - uint16_t gameport_addr; - - void *opl_mixer; - void (*opl_mix)(void *, double *, double *); -} ess_t; - -static double -ess_mixer_get_vol_4bit(uint8_t vol) -{ - return sb_att_2dbstep_4bits[vol & 0x0F] / 32767.0; -} - -void -ess_mixer_write(uint16_t addr, uint8_t val, void *priv) -{ - ess_t *ess = (ess_t *) priv; - ess_mixer_t *mixer = &ess->mixer_sbpro; - - 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. */ - mixer->regs[0x14] = mixer->regs[0x32] = 0x88; - mixer->regs[0x36] = 0x88; - mixer->regs[0x38] = 0x88; - mixer->regs[0x3a] = 0x00; - 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] / 32768.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; - } - break; - - case 0x14: - mixer->regs[0x4] = val & 0xee; - break; - - case 0x1A: - mixer->mic_l = sb_att_1p4dbstep_4bits[(mixer->regs[0x1A] >> 4) & 0xF]; - mixer->mic_r = sb_att_1p4dbstep_4bits[mixer->regs[0x1A] & 0xF]; - 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: - mixer->regs[mixer->index - 0x10] = (val & 0xee); - break; - case 0x32: - case 0x36: - case 0x38: - case 0x3e: - mixer->regs[mixer->index - 0x10] = (val & 0xee); - break; - - case 0x3a: - break; - - case 0x00: - case 0x04: - break; - - case 0x0e: - 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 (0) { - /* 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 = ess_mixer_get_vol_4bit(mixer->regs[0x14] >> 4); - mixer->voice_r = ess_mixer_get_vol_4bit(mixer->regs[0x14]); - mixer->master_l = ess_mixer_get_vol_4bit(mixer->regs[0x32] >> 4); - mixer->master_r = ess_mixer_get_vol_4bit(mixer->regs[0x32]); - mixer->fm_l = ess_mixer_get_vol_4bit(mixer->regs[0x36] >> 4); - mixer->fm_r = ess_mixer_get_vol_4bit(mixer->regs[0x36]); - mixer->cd_l = ess_mixer_get_vol_4bit(mixer->regs[0x38] >> 4); - mixer->cd_r = ess_mixer_get_vol_4bit(mixer->regs[0x38]); - mixer->line_l = ess_mixer_get_vol_4bit(mixer->regs[0x3e] >> 4); - mixer->line_r = ess_mixer_get_vol_4bit(mixer->regs[0x3e]); - mixer->auxb_l = ess_mixer_get_vol_4bit(mixer->regs[0x3a] >> 4); - mixer->auxb_r = ess_mixer_get_vol_4bit(mixer->regs[0x3a]); - - mixer->output_filter = !(mixer->regs[0xe] & 0x20); - mixer->input_filter = !(mixer->regs[0xc] & 0x20); - mixer->in_filter_freq = ((mixer->regs[0xc] & 0x8) == 0) ? 3200 : 8800; - mixer->stereo = mixer->regs[0xe] & 2; - if (mixer->index == 0xe) - sb_dsp_set_stereo(&ess->dsp, val & 2); - - /* TODO: pcspeaker volume? Or is it not worth? */ - } -} - -uint8_t -ess_mixer_read(uint16_t addr, void *priv) -{ - ess_t *ess = (ess_t *) priv; - ess_mixer_t *mixer = &ess->mixer_sbpro; - - 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(ess_t *ess) -{ - ess_mixer_write(4, 0, ess); - ess_mixer_write(5, 0, ess); -} - -void -ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) -{ - ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; - 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 CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ - 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 -ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) -{ - ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; - 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 CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ - 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 ess_t *ess = (ess_t *) priv; - const ess_mixer_t *mixer = &ess->mixer_sbpro; - double c; - double cd = channel ? mixer->cd_r : mixer->cd_l; - double master = channel ? mixer->master_r : mixer->master_l; - - c = (*buffer * cd) / 3.0; - *buffer = c * master; -} - -static void * -ess_1688_init(UNUSED(const device_t *info)) -{ - /* SB Pro 2 port mappings, 220h or 240h. - 2x0 to 2x3 -> FM chip (18 voices) - 2x4 to 2x5 -> Mixer interface - 2x6, 2xA, 2xC, 2xE -> DSP chip - 2x8, 2x9, 388 and 389 FM chip (9 voices) - 2x0+10 to 2x0+13 CDROM interface. */ - ess_t *ess = calloc(sizeof(ess_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(ess_get_buffer_sbpro, ess); - music_add_handler(ess_get_music_buffer_sbpro, 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_sbpro.ess_id_str[0] = 0x16; - ess->mixer_sbpro.ess_id_str[1] = 0x88; - ess->mixer_sbpro.ess_id_str[2] = (addr >> 8) & 0xff; - ess->mixer_sbpro.ess_id_str[3] = addr & 0xff; - } - - ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); - 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 -ess_close(void *priv) -{ - ess_t *ess = (ess_t *) priv; - sb_dsp_close(&ess->dsp); - - free(ess); -} - -void -ess_speed_changed(void *priv) -{ - ess_t *ess = (ess_t *) priv; - - sb_dsp_speed_changed(&ess->dsp); -} - -// clang-format off -static const device_config_t ess_config[] = { - { - .name = "base", - .description = "Address", - .type = CONFIG_HEX16, - .default_string = "", - .default_int = 0x220, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { - .description = "0x220", - .value = 0x220 - }, - { - .description = "0x240", - .value = 0x240 - }, - { .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 ess_1688_device = { - .name = "ESS Technology ES1688", - .internal_name = "ess_es1688", - .flags = DEVICE_ISA, - .local = 0, - .init = ess_1688_init, - .close = ess_close, - .reset = NULL, - { .available = NULL }, - .speed_changed = ess_speed_changed, - .force_redraw = NULL, - .config = ess_config -}; diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 2365419b1..1489cb769 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -75,6 +75,20 @@ 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 +}; // clang-format on static const uint16_t sb_mcv_addr[8] = { 0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270 }; @@ -667,6 +681,87 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv) *buffer = c * output_gain; } +void +ess_get_buffer_sbpro(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 +ess_get_music_buffer_sbpro(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 +1399,265 @@ 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. */ + mixer->regs[0x14] = mixer->regs[0x32] = 0x88; + mixer->regs[0x36] = 0x88; + mixer->regs[0x38] = 0x88; + mixer->regs[0x3a] = 0x00; + 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; + } + 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: + mixer->regs[mixer->index - 0x10] = (val & 0xee); + break; + case 0x32: + case 0x36: + case 0x38: + case 0x3e: + mixer->regs[mixer->index - 0x10] = (val & 0xee); + break; + + case 0x3a: + break; + + case 0x00: + case 0x04: + break; + + case 0x0e: + 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 (0) { + /* 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->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->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->output_filter = !(mixer->regs[0xe] & 0x20); + mixer->input_filter = !(mixer->regs[0xc] & 0x20); + mixer->in_filter_freq = ((mixer->regs[0xc] & 0x8) == 0) ? 3200 : 8800; + mixer->stereo = mixer->regs[0xe] & 2; + if (mixer->index == 0xe) + sb_dsp_set_stereo(&ess->dsp, val & 2); + + /* TODO: pcspeaker volume? Or is it not worth? */ + } +} + +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 +3109,76 @@ sb_awe32_pnp_init(const device_t *info) return sb; } +static void * +ess_1688_init(UNUSED(const device_t *info)) +{ + /* SB Pro 2 port mappings, 220h or 240h. + 2x0 to 2x3 -> FM chip (18 voices) + 2x4 to 2x5 -> Mixer interface + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip (9 voices) + 2x0+10 to 2x0+13 CDROM interface. */ + 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(ess_get_buffer_sbpro, ess); + music_add_handler(ess_get_music_buffer_sbpro, 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 +4447,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 = "0x240", + .value = 0x240 + }, + { + .description = "0x260", + .value = 0x260 + }, + { + .description = "0x280", + .value = 0x280 + }, + { .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 +4882,17 @@ const device_t sb_awe64_gold_device = { .force_redraw = NULL, .config = sb_awe64_gold_config }; + +const device_t ess_1688_device = { + .name = "ESS Technology 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 +}; From eee63d7312f821631a27d88a655bef21de4fbbb8 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 23 Mar 2024 00:58:31 -0300 Subject: [PATCH 63/66] Better function naming --- src/sound/snd_sb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 1489cb769..fc6b9c6e9 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -682,7 +682,7 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv) } void -ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) +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; @@ -716,7 +716,7 @@ ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) } void -ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) +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; @@ -3153,8 +3153,8 @@ ess_1688_init(UNUSED(const device_t *info)) ess_mixer_read, NULL, NULL, ess_mixer_write, NULL, NULL, ess); - sound_add_handler(ess_get_buffer_sbpro, ess); - music_add_handler(ess_get_music_buffer_sbpro, 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")) { From 639f05d07c26e5724741eff879f1144201f49c73 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 23 Mar 2024 01:09:49 -0300 Subject: [PATCH 64/66] Small fixes --- src/sound/snd_sb.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index fc6b9c6e9..61dbc8c7a 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -1523,7 +1523,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); - if (0) { + if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) { /* Not on ES1688. */ io_removehandler(0x0388, 0x0004, ess->opl.read, NULL, NULL, @@ -3112,12 +3112,6 @@ sb_awe32_pnp_init(const device_t *info) static void * ess_1688_init(UNUSED(const device_t *info)) { - /* SB Pro 2 port mappings, 220h or 240h. - 2x0 to 2x3 -> FM chip (18 voices) - 2x4 to 2x5 -> Mixer interface - 2x6, 2xA, 2xC, 2xE -> DSP chip - 2x8, 2x9, 388 and 389 FM chip (9 voices) - 2x0+10 to 2x0+13 CDROM interface. */ sb_t *ess = calloc(sizeof(sb_t), 1); uint16_t addr = device_get_config_hex16("base"); @@ -4462,17 +4456,17 @@ static const device_config_t ess_1688_config[] = { .description = "0x220", .value = 0x220 }, + { + .description = "0x230", + .value = 0x230 + }, { .description = "0x240", .value = 0x240 }, { - .description = "0x260", - .value = 0x260 - }, - { - .description = "0x280", - .value = 0x280 + .description = "0x250", + .value = 0x250 }, { .description = "" } } From 6c519904fd43130a3e1e53fcafa5165cfe44b9c2 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 23 Mar 2024 12:22:11 -0300 Subject: [PATCH 65/66] Cleanup: miscellaneous --- src/include/86box/snd_sb.h | 1 + src/include/86box/snd_sb_dsp.h | 17 +++++------ src/sound/snd_opl_esfm.c | 11 +++---- src/sound/snd_sb.c | 52 ++++++++++++++++++---------------- src/sound/snd_sb_dsp.c | 14 ++++----- 5 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h index 1ab94c9e1..b26c5f06e 100644 --- a/src/include/86box/snd_sb.h +++ b/src/include/86box/snd_sb.h @@ -142,6 +142,7 @@ typedef struct ess_mixer_t { 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 */ diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index f5905ccc2..a00f512a3 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -7,11 +7,10 @@ #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_ES688 3 /* ESS Technology ES688 */ -#define SB_SUBTYPE_ESS_ES1688 4 /* ESS Technology ES1688 */ +#define SB_SUBTYPE_ESS_ES1688 3 /* ESS Technology ES1688 */ /* ESS-related */ -#define IS_ESS(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_ESS_ES688 || (dsp)->sb_subtype == SB_SUBTYPE_ESS_ES1688) /* check for future ESS cards here */ +#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 */ @@ -149,13 +148,15 @@ typedef struct sb_dsp_t { uint8_t ess_extended_mode; uint8_t ess_reload_len; uint32_t ess_dma_counter; - int ess_irq_generic; - int ess_irq_dmactr; - // ESPCM + /* IRQ status flags (0x22C) */ + uint8_t ess_irq_generic; + uint8_t ess_irq_dmactr; + + /* ESPCM */ fifo64_t *espcm_fifo; - int espcm_fifo_reset; - int espcm_mode; + 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]; diff --git a/src/sound/snd_opl_esfm.c b/src/sound/snd_opl_esfm.c index c36ec4160..78cbec11d 100644 --- a/src/sound/snd_opl_esfm.c +++ b/src/sound/snd_opl_esfm.c @@ -42,7 +42,6 @@ typedef struct { int8_t flags; int8_t pad; - uint16_t port; uint8_t status; uint8_t timer_ctrl; uint16_t timer_count[2]; @@ -269,14 +268,12 @@ esfm_drv_read(uint16_t port, void *priv) static void esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val) { - uint16_t p = dev->port; - - ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); + uint16_t p = dev->opl.addr_latch & 0x07ff; if (dev->opl.native_mode) { p -= 0x400; - p &= 0x1ff; } + p &= 0x1ff; switch (p) { case 0x002: /* Timer 1 */ @@ -304,6 +301,8 @@ esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val) default: break; } + + ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val); } static void @@ -321,14 +320,12 @@ esfm_drv_write(uint16_t port, uint8_t val, void *priv) esfm_drv_write_buffered(dev, val); else { ESFM_write_port(&dev->opl, port & 3, val); - dev->port = dev->opl.addr_latch & 0x07ff; } } else { if ((port & 0x0001) == 0x0001) esfm_drv_write_buffered(dev, val); else { ESFM_write_port(&dev->opl, port & 3, val); - dev->port = dev->opl.addr_latch & 0x01ff; } } } diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 61dbc8c7a..f26fba1eb 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -89,6 +89,11 @@ 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 }; @@ -730,12 +735,10 @@ sb_get_music_buffer_ess(int32_t *buffer, int len, void *priv) 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); - } + 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; @@ -1421,11 +1424,12 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) mixer->regs[0x26] = mixer->regs[0x28] = 0xee; mixer->regs[0x2e] = 0x00; - /* Initialize ESS regs. */ - mixer->regs[0x14] = mixer->regs[0x32] = 0x88; - mixer->regs[0x36] = 0x88; - mixer->regs[0x38] = 0x88; + /* 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); @@ -1460,6 +1464,14 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) 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: @@ -1495,8 +1507,6 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) SoundBlaster Pro selects register 020h for 030h, 022h for 032h, 026h for 036h, and 028h for 038h. */ case 0x30: - mixer->regs[mixer->index - 0x10] = (val & 0xee); - break; case 0x32: case 0x36: case 0x38: @@ -1505,15 +1515,13 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0x3a: + case 0x3c: break; case 0x00: case 0x04: break; - case 0x0e: - break; - case 0x64: mixer->regs[mixer->index] &= ~0x8; break; @@ -1587,19 +1595,13 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) 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->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->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; - mixer->output_filter = !(mixer->regs[0xe] & 0x20); - mixer->input_filter = !(mixer->regs[0xc] & 0x20); - mixer->in_filter_freq = ((mixer->regs[0xc] & 0x8) == 0) ? 3200 : 8800; - mixer->stereo = mixer->regs[0xe] & 2; - if (mixer->index == 0xe) - sb_dsp_set_stereo(&ess->dsp, val & 2); - - /* TODO: pcspeaker volume? Or is it not worth? */ + /* TODO: PC Speaker volume */ } } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index ad6209fb5..332299c4f 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -29,11 +29,13 @@ #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 @@ -1596,10 +1598,6 @@ sb_exec_command(sb_dsp_t *dsp) switch (dsp->sb_subtype) { default: break; - case SB_SUBTYPE_ESS_ES688: - sb_add_data(dsp, 0x68); - sb_add_data(dsp, 0x80 | 0x04); - break; case SB_SUBTYPE_ESS_ES1688: // Determined via Windows driver debugging. sb_add_data(dsp, 0x68); @@ -1792,11 +1790,11 @@ sb_read(uint16_t a, void *priv) } 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; - uint8_t fifo_half = 0; + 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 + 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; From 0af83efb8f5e7f6c919d2f9b7a4dc7c02e074b16 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 23 Mar 2024 12:47:41 -0300 Subject: [PATCH 66/66] Correcting device name --- src/sound/snd_sb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index f26fba1eb..13baa3621 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -4880,7 +4880,7 @@ const device_t sb_awe64_gold_device = { }; const device_t ess_1688_device = { - .name = "ESS Technology ES1688", + .name = "ESS AudioDrive ES1688", .internal_name = "ess_es1688", .flags = DEVICE_ISA, .local = 0,