Merge branch 'master' into es5506_alt

This commit is contained in:
cam900 2022-12-15 16:06:59 +09:00 committed by GitHub
commit 8365d1c977
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 1400 additions and 150 deletions

1
.gitmodules vendored
View file

@ -4,7 +4,6 @@
[submodule "extern/SDL"] [submodule "extern/SDL"]
path = extern/SDL path = extern/SDL
url = https://github.com/libsdl-org/SDL.git url = https://github.com/libsdl-org/SDL.git
branch = 2.0.22
[submodule "extern/libsndfile"] [submodule "extern/libsndfile"]
path = extern/libsndfile path = extern/libsndfile
url = https://github.com/libsndfile/libsndfile.git url = https://github.com/libsndfile/libsndfile.git

View file

@ -526,6 +526,7 @@ src/engine/platform/ymz280b.cpp
src/engine/platform/namcowsg.cpp src/engine/platform/namcowsg.cpp
src/engine/platform/rf5c68.cpp src/engine/platform/rf5c68.cpp
src/engine/platform/snes.cpp src/engine/platform/snes.cpp
src/engine/platform/k007232.cpp
src/engine/platform/pcmdac.cpp src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp src/engine/platform/dummy.cpp
) )

View file

@ -248,7 +248,9 @@ public class HIDDeviceManager {
0x1689, // Razer Onza 0x1689, // Razer Onza
0x1949, // Lab126, Inc. 0x1949, // Lab126, Inc.
0x1bad, // Harmonix 0x1bad, // Harmonix
0x20d6, // PowerA
0x24c6, // PowerA 0x24c6, // PowerA
0x2c22, // Qanba
}; };
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC && if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
@ -274,7 +276,9 @@ public class HIDDeviceManager {
0x0e6f, // PDP 0x0e6f, // PDP
0x0f0d, // Hori 0x0f0d, // Hori
0x1532, // Razer Wildcat 0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA 0x24c6, // PowerA
0x2dc8, /* 8BitDo */
0x2e24, // Hyperkin 0x2e24, // Hyperkin
}; };

View file

@ -27,7 +27,9 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.Selection;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
@ -63,6 +65,112 @@ import java.util.Locale;
*/ */
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener { public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
private static final String TAG = "SDL"; private static final String TAG = "SDL";
/*
// Display InputType.SOURCE/CLASS of events and devices
//
// SDLActivity.debugSource(device.getSources(), "device[" + device.getName() + "]");
// SDLActivity.debugSource(event.getSource(), "event");
public static void debugSource(int sources, String prefix) {
int s = sources;
int s_copy = sources;
String cls = "";
String src = "";
int tst = 0;
int FLAG_TAINTED = 0x80000000;
if ((s & InputDevice.SOURCE_CLASS_BUTTON) != 0) cls += " BUTTON";
if ((s & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) cls += " JOYSTICK";
if ((s & InputDevice.SOURCE_CLASS_POINTER) != 0) cls += " POINTER";
if ((s & InputDevice.SOURCE_CLASS_POSITION) != 0) cls += " POSITION";
if ((s & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) cls += " TRACKBALL";
int s2 = s_copy & ~InputDevice.SOURCE_ANY; // keep class bits
s2 &= ~( InputDevice.SOURCE_CLASS_BUTTON
| InputDevice.SOURCE_CLASS_JOYSTICK
| InputDevice.SOURCE_CLASS_POINTER
| InputDevice.SOURCE_CLASS_POSITION
| InputDevice.SOURCE_CLASS_TRACKBALL);
if (s2 != 0) cls += "Some_Unkown";
s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
tst = InputDevice.SOURCE_BLUETOOTH_STYLUS;
if ((s & tst) == tst) src += " BLUETOOTH_STYLUS";
s2 &= ~tst;
}
tst = InputDevice.SOURCE_DPAD;
if ((s & tst) == tst) src += " DPAD";
s2 &= ~tst;
tst = InputDevice.SOURCE_GAMEPAD;
if ((s & tst) == tst) src += " GAMEPAD";
s2 &= ~tst;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tst = InputDevice.SOURCE_HDMI;
if ((s & tst) == tst) src += " HDMI";
s2 &= ~tst;
}
tst = InputDevice.SOURCE_JOYSTICK;
if ((s & tst) == tst) src += " JOYSTICK";
s2 &= ~tst;
tst = InputDevice.SOURCE_KEYBOARD;
if ((s & tst) == tst) src += " KEYBOARD";
s2 &= ~tst;
tst = InputDevice.SOURCE_MOUSE;
if ((s & tst) == tst) src += " MOUSE";
s2 &= ~tst;
if (Build.VERSION.SDK_INT >= 26) {
tst = InputDevice.SOURCE_MOUSE_RELATIVE;
if ((s & tst) == tst) src += " MOUSE_RELATIVE";
s2 &= ~tst;
tst = InputDevice.SOURCE_ROTARY_ENCODER;
if ((s & tst) == tst) src += " ROTARY_ENCODER";
s2 &= ~tst;
}
tst = InputDevice.SOURCE_STYLUS;
if ((s & tst) == tst) src += " STYLUS";
s2 &= ~tst;
tst = InputDevice.SOURCE_TOUCHPAD;
if ((s & tst) == tst) src += " TOUCHPAD";
s2 &= ~tst;
tst = InputDevice.SOURCE_TOUCHSCREEN;
if ((s & tst) == tst) src += " TOUCHSCREEN";
s2 &= ~tst;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
tst = InputDevice.SOURCE_TOUCH_NAVIGATION;
if ((s & tst) == tst) src += " TOUCH_NAVIGATION";
s2 &= ~tst;
}
tst = InputDevice.SOURCE_TRACKBALL;
if ((s & tst) == tst) src += " TRACKBALL";
s2 &= ~tst;
tst = InputDevice.SOURCE_ANY;
if ((s & tst) == tst) src += " ANY";
s2 &= ~tst;
if (s == FLAG_TAINTED) src += " FLAG_TAINTED";
s2 &= ~FLAG_TAINTED;
if (s2 != 0) src += " Some_Unkown";
Log.v(TAG, prefix + "int=" + s_copy + " CLASS={" + cls + " } source(s):" + src);
}
*/
public static boolean mIsResumedCalled, mHasFocus; public static boolean mIsResumedCalled, mHasFocus;
public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24); public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24);
@ -1204,8 +1312,21 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
for (int id : ids) { for (int id : ids) {
InputDevice device = InputDevice.getDevice(id); InputDevice device = InputDevice.getDevice(id);
if (device != null && (device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) { /* Allow SOURCE_TOUCHSCREEN and also Virtual InputDevices because they can send TOUCHSCREEN events */
nativeAddTouch(device.getId(), device.getName()); if (device != null && ((device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN
|| device.isVirtual())) {
int touchDevId = device.getId();
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
nativeAddTouch(touchDevId, device.getName());
} }
} }
} }
@ -1895,7 +2016,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
} }
} }
if ((source & InputDevice.SOURCE_KEYBOARD) != 0) { if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLActivity.isTextInputEvent(event)) { if (SDLActivity.isTextInputEvent(event)) {
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1); SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
@ -1908,7 +2029,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
} }
} }
if ((source & InputDevice.SOURCE_MOUSE) != 0) { if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
// they are ignored here because sending them as mouse input to SDL is messy // they are ignored here because sending them as mouse input to SDL is messy
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) { if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
@ -2195,7 +2316,7 @@ class DummyEdit extends View implements View.OnKeyListener {
public InputConnection onCreateInputConnection(EditorInfo outAttrs) { public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new SDLInputConnection(this, true); ic = new SDLInputConnection(this, true);
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */; | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
@ -2237,6 +2358,29 @@ class SDLInputConnection extends BaseInputConnection {
@Override @Override
public boolean commitText(CharSequence text, int newCursorPosition) { public boolean commitText(CharSequence text, int newCursorPosition) {
/* Generate backspaces for the text we're going to replace */
final Editable content = getEditable();
if (content != null) {
int a = getComposingSpanStart(content);
int b = getComposingSpanEnd(content);
if (a == -1 || b == -1) {
a = Selection.getSelectionStart(content);
b = Selection.getSelectionEnd(content);
}
if (a < 0) a = 0;
if (b < 0) b = 0;
if (b < a) {
int tmp = a;
a = b;
b = tmp;
}
int backspaces = (b - a);
for (int i = 0; i < backspaces; i++) {
nativeGenerateScancodeForUnichar('\b');
}
}
for (int i = 0; i < text.length(); i++) { for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i); char c = text.charAt(i);
if (c == '\n') { if (c == '\n') {
@ -2271,14 +2415,11 @@ class SDLInputConnection extends BaseInputConnection {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265 // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if (beforeLength > 0 && afterLength == 0) { if (beforeLength > 0 && afterLength == 0) {
boolean ret = true;
// backspace(s) // backspace(s)
while (beforeLength-- > 0) { while (beforeLength-- > 0) {
boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) nativeGenerateScancodeForUnichar('\b');
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
ret = ret && ret_key;
} }
return ret; return true;
} }
return super.deleteSurroundingText(beforeLength, afterLength); return super.deleteSurroundingText(beforeLength, afterLength);

View file

@ -255,7 +255,6 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
@Override @Override
public boolean handleMotionEvent(MotionEvent event) { public boolean handleMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex(); int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked(); int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) { if (action == MotionEvent.ACTION_MOVE) {
@ -274,7 +273,6 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
} }
} }
} }
}
return true; return true;
} }
@ -562,8 +560,6 @@ class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
switch ( event.getSource() ) { switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK: case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event); return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE: case InputDevice.SOURCE_MOUSE:
@ -693,8 +689,6 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
switch ( event.getSource() ) { switch ( event.getSource() ) {
case InputDevice.SOURCE_JOYSTICK: case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD:
return SDLControllerManager.handleJoystickMotionEvent(event); return SDLControllerManager.handleJoystickMotionEvent(event);
case InputDevice.SOURCE_MOUSE: case InputDevice.SOURCE_MOUSE:

2
extern/SDL vendored

@ -1 +1 @@
Subproject commit f8e14ad25aa6f0b4bbed411583a9863c855ad9e6 Subproject commit 55b03c7493a7abed33cf803d1380a40fa8af903f

10
extern/opm/opm.c vendored
View file

@ -1892,9 +1892,9 @@ void OPM_Clock(opm_t *chip, int32_t *output, uint8_t *sh1, uint8_t *sh2, uint8_t
OPM_PhaseCalcIncrement(chip); OPM_PhaseCalcIncrement(chip);
OPM_PhaseCalcFNumBlock(chip); OPM_PhaseCalcFNumBlock(chip);
/*OPM_DoTimerIRQ(chip); OPM_DoTimerIRQ(chip);
OPM_DoTimerA(chip); OPM_DoTimerA(chip);
OPM_DoTimerB(chip);*/ OPM_DoTimerB(chip);
OPM_DoLFOMult(chip); OPM_DoLFOMult(chip);
OPM_DoLFO1(chip); OPM_DoLFO1(chip);
OPM_Noise(chip); OPM_Noise(chip);
@ -1904,10 +1904,10 @@ void OPM_Clock(opm_t *chip, int32_t *output, uint8_t *sh1, uint8_t *sh2, uint8_t
OPM_NoiseTimer(chip); OPM_NoiseTimer(chip);
OPM_KeyOn1(chip); OPM_KeyOn1(chip);
OPM_DoIO(chip); OPM_DoIO(chip);
/*OPM_DoTimerA2(chip); OPM_DoTimerA2(chip);
OPM_DoTimerB2(chip);*/ OPM_DoTimerB2(chip);
OPM_DoLFO2(chip); OPM_DoLFO2(chip);
//OPM_CSM(chip); OPM_CSM(chip);
OPM_NoiseChannel(chip); OPM_NoiseChannel(chip);
OPM_Output(chip); OPM_Output(chip);
OPM_DAC(chip); OPM_DAC(chip);

View file

@ -25,6 +25,7 @@ as of Furnace 0.6, the following sound chips have sample support:
- tildearrow Sound Unit - tildearrow Sound Unit
- VERA (last channel only) - VERA (last channel only)
- Y8950 (last channel only) - Y8950 (last channel only)
- Konami K007232
- a few more that I've forgotten to mention - a few more that I've forgotten to mention
## compatible sample mode ## compatible sample mode

View file

@ -12,6 +12,7 @@ this is a list of sound chips that Furnace supports, including effects.
- [Generic PCM DAC](dac.md) - [Generic PCM DAC](dac.md)
- [Famicom Disk System](fds.md) - [Famicom Disk System](fds.md)
- [Game Boy](game-boy.md) - [Game Boy](game-boy.md)
- [Konami K007232](k007232.md)
- [Konami SCC](scc.md) - [Konami SCC](scc.md)
- [Konami VRC6](vrc6.md) - [Konami VRC6](vrc6.md)
- [Atari Lynx](lynx.md) - [Atari Lynx](lynx.md)

View file

@ -0,0 +1,11 @@
# Konami K007232
a 2-channel PCM sound chip from Konami which was used in some of their 1986-1990 arcade boards.
Its sample format is unique; the topmost bit is the end marker, and the low 7 bits are used for generating sound (unsigned format).
It has 7 bit digital output per each channel and no volume register on chip, so it needs external logic to control channel volume.
# effects
- Nothing for now

View file

@ -271,6 +271,8 @@ size | description
| - 0xc3: OPN CSM - 10 channels | - 0xc3: OPN CSM - 10 channels
| - 0xc4: PC-98 CSM - 20 channels | - 0xc4: PC-98 CSM - 20 channels
| - 0xc5: YM2610B CSM - 20 channels | - 0xc5: YM2610B CSM - 20 channels
| - 0xc6: K007232 - 2 channels
| - 0xc7: GA20 - 4 channels
| - 0xde: YM2610B extended - 19 channels | - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels | - 0xe0: QSound - 19 channels
| - 0xfc: Pong - 1 channel | - 0xfc: Pong - 1 channel

View file

@ -65,6 +65,57 @@ size | description
??? | data... ??? | data...
``` ```
the following instrument types are available:
- 0: SN76489
- 1: FM (OPN)
- 2: Game Boy
- 3: C64
- 4: Amiga/sample
- 5: PC Engine
- 6: AY-3-8910
- 7: AY8930
- 8: TIA
- 9: SAA1099
- 10: VIC
- 11: PET
- 12: VRC6
- 13: OPLL
- 14: OPL
- 15: FDS
- 16: Virtual Boy
- 17: Namco 163
- 18: SCC
- 19: OPZ
- 20: POKEY
- 21: PC Speaker
- 22: WonderSwan
- 23: Lynx
- 24: VERA
- 25: X1-010
- 26: VRC6 (saw)
- 27: ES5506
- 28: MultiPCM
- 29: SNES
- 30: Sound Unit
- 31: Namco WSG
- 32: OPL (drums)
- 33: FM (OPM)
- 34: NES
- 35: MSM6258
- 36: MSM6295
- 37: ADPCM-A
- 38: ADPCM-B
- 39: SegaPCM
- 40: QSound
- 41: YMZ280B
- 42: RF5C68
- 43: MSM5232
- 44: T6W28
- 45: K007232
- 46: GA20
- 47: Pokémon Mini
the following feature codes are recognized: the following feature codes are recognized:
- `NA`: instrument name - `NA`: instrument name

View file

@ -72,6 +72,7 @@
#include "platform/rf5c68.h" #include "platform/rf5c68.h"
#include "platform/snes.h" #include "platform/snes.h"
#include "platform/vb.h" #include "platform/vb.h"
#include "platform/k007232.h"
#include "platform/pcmdac.h" #include "platform/pcmdac.h"
#include "platform/dummy.h" #include "platform/dummy.h"
#include "../ta-log.h" #include "../ta-log.h"
@ -426,6 +427,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_SNES: case DIV_SYSTEM_SNES:
dispatch=new DivPlatformSNES; dispatch=new DivPlatformSNES;
break; break;
case DIV_SYSTEM_K007232:
dispatch=new DivPlatformK007232;
break;
case DIV_SYSTEM_PCM_DAC: case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC; dispatch=new DivPlatformPCMDAC;
break; break;

View file

@ -3918,9 +3918,13 @@ void DivEngine::setConsoleMode(bool enable) {
consoleMode=enable; consoleMode=enable;
} }
bool DivEngine::switchMaster() { bool DivEngine::switchMaster(bool full) {
logI("switching output..."); logI("switching output...");
deinitAudioBackend(true); deinitAudioBackend(true);
if (full) {
quitDispatch();
initDispatch();
}
if (initAudioBackend()) { if (initAudioBackend()) {
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
disCont[i].setRates(got.rate); disCont[i].setRates(got.rate);

View file

@ -976,7 +976,7 @@ class DivEngine {
String getPlaybackDebugInfo(); String getPlaybackDebugInfo();
// switch master // switch master
bool switchMaster(); bool switchMaster(bool full=false);
// set MIDI base channel // set MIDI base channel
void setMidiBaseChan(int chan); void setMidiBaseChan(int chan);

View file

@ -911,6 +911,16 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break; break;
case DIV_INS_T6W28: case DIV_INS_T6W28:
break; break;
case DIV_INS_K007232:
featureSM=true;
featureSL=true;
break;
case DIV_INS_GA20:
featureSM=true;
featureSL=true;
break;
case DIV_INS_POKEMINI:
break;
case DIV_INS_MAX: case DIV_INS_MAX:
break; break;

View file

@ -77,6 +77,9 @@ enum DivInstrumentType: unsigned short {
DIV_INS_RF5C68=42, DIV_INS_RF5C68=42,
DIV_INS_MSM5232=43, DIV_INS_MSM5232=43,
DIV_INS_T6W28=44, DIV_INS_T6W28=44,
DIV_INS_K007232=45,
DIV_INS_GA20=46,
DIV_INS_POKEMINI=47,
DIV_INS_MAX, DIV_INS_MAX,
DIV_INS_NULL DIV_INS_NULL
}; };

View file

@ -19,6 +19,7 @@
#include "genesis.h" #include "genesis.h"
#include "../engine.h" #include "../engine.h"
#include "../../ta-log.h"
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
@ -27,6 +28,22 @@
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6]))) #define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
void DivYM2612Interface::ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) {
if (tnum==1) {
countB=duration_in_clocks;
} else if (tnum==0) {
countA=duration_in_clocks;
}
logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks);
}
void DivYM2612Interface::clock() {
if (countA>=0) {
countA-=144;
if (countA<0) m_engine->engine_timer_expired(0);
}
}
void DivPlatformGenesis::processDAC(int iRate) { void DivPlatformGenesis::processDAC(int iRate) {
if (softPCM) { if (softPCM) {
softPCMTimer+=chipClock/576; softPCMTimer+=chipClock/576;
@ -197,6 +214,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
} else { } else {
((ymfm::ym3438*)fm_ymfm)->generate(&out_ymfm); ((ymfm::ym3438*)fm_ymfm)->generate(&out_ymfm);
} }
iface.clock();
os[0]=out_ymfm.data[0]; os[0]=out_ymfm.data[0];
os[1]=out_ymfm.data[1]; os[1]=out_ymfm.data[1];
//OPN2_Write(&fm,0,0); //OPN2_Write(&fm,0,0);

View file

@ -26,7 +26,15 @@
class DivYM2612Interface: public ymfm::ymfm_interface { class DivYM2612Interface: public ymfm::ymfm_interface {
int countA, countB;
public:
void clock();
void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks);
DivYM2612Interface():
ymfm::ymfm_interface(),
countA(-1),
countB(-1) {}
}; };
class DivPlatformGenesis: public DivPlatformOPN { class DivPlatformGenesis: public DivPlatformOPN {

View file

@ -0,0 +1,568 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "k007232.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.emplace(a,v); if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 64
const char* regCheatSheetK007232[]={
// on-chip
"CHX_FreqL", "X*6+0",
"CHX_FreqH", "X*6+1",
"CHX_StartL", "X*6+2",
"CHX_StartM", "X*6+3",
"CHX_StartH", "X*6+4",
"CHX_Keyon", "X*6+5",
"SLEV", "C", // external IO
"Loop", "D",
// off-chip
"CHX_Volume", "X*2+10",
"CHX_Bank", "X*2+12",
NULL
};
const char** DivPlatformK007232::getRegisterSheet() {
return regCheatSheetK007232;
}
inline void DivPlatformK007232::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) {
if ((ch<2) && (addr<6)) {
rWrite((ch*6)+(addr&7),val);
}
}
}
void DivPlatformK007232::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
if ((--delay)<=0) {
delay=MAX(0,delay);
if (!writes.empty()) {
QueuedWrite& w=writes.front();
// write on-chip register
if (w.addr<=0xd) {
k007232.write(w.addr,w.val);
}
regPool[w.addr]=w.val;
writes.pop();
delay=w.delay;
}
}
k007232.tick();
if (stereo) {
const unsigned char vol1=regPool[0x10],vol2=regPool[0x11];
const signed int lout[2]={(k007232.output(0)*(vol1&0xf)),(k007232.output(1)*(vol2&0xf))};
const signed int rout[2]={(k007232.output(0)*((vol1>>4)&0xf)),(k007232.output(1)*((vol2>>4)&0xf))};
bufL[h]=(lout[0]+lout[1])<<4;
bufR[h]=(rout[0]+rout[1])<<4;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<4;
}
} else {
const unsigned char vol=regPool[0xc];
const signed int out[2]={(k007232.output(0)*(vol&0xf)),(k007232.output(1)*((vol>>4)&0xf))};
bufL[h]=bufR[h]=(out[0]+out[1])<<4;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<5;
}
}
}
}
u8 DivPlatformK007232::read_sample(u8 ne, u32 address) {
if ((sampleMem!=NULL) && (address<getSampleMemCapacity())) {
return sampleMem[((regPool[0x12+(ne&1)]<<17)|(address&0x1ffff))&0xffffff];
}
return 0;
}
void DivPlatformK007232::tick(bool sysTick) {
for (int i=0; i<2; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
const signed char macroVol=((chan[i].vol&0xf)*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
if ((!isMuted[i]) && (macroVol!=chan[i].outVol)) {
chan[i].outVol=macroVol;
chan[i].volumeChanged=true;
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
// volume and panning registers are off-chip
if (chan[i].std.panL.had) {
chan[i].panning&=0xf0;
chan[i].panning|=chan[i].std.panL.val&15;
if ((!isMuted[i]) && stereo) {
chan[i].volumeChanged=true;
}
}
if (chan[i].std.panR.had) {
chan[i].panning&=0x0f;
chan[i].panning|=(chan[i].std.panR.val&15)<<4;
if ((!isMuted[i]) && stereo) {
chan[i].volumeChanged=true;
}
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].audPos=0;
chan[i].setPos=true;
}
}
if (chan[i].volumeChanged) {
chan[i].resVol=isMuted[i]?0:chan[i].outVol&0xf;
if (stereo) {
chan[i].lvol=((chan[i].resVol&0xf)*((chan[i].panning>>0)&0xf))/15;
chan[i].rvol=((chan[i].resVol&0xf)*((chan[i].panning>>4)&0xf))/15;
const int newPan=(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4);
if (chan[i].prevPan!=newPan) {
rWrite(0x10+i,(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4));
chan[i].prevPan=newPan;
}
}
else {
const unsigned char prevVolume=lastVolume;
lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2));
if (prevVolume!=lastVolume) {
rWrite(0xc,lastVolume);
}
}
chan[i].volumeChanged=false;
}
if (chan[i].setPos) {
// force keyon
chan[i].keyOn=true;
chan[i].setPos=false;
} else {
chan[i].audPos=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
double off=1.0;
int sample=chan[i].sample;
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/s->centerRate;
}
}
DivSample* s=parent->getSample(chan[i].sample);
chan[i].freq=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {
unsigned int bank=0;
unsigned int start=0;
unsigned int loop=0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
bank=sampleOffK007232[chan[i].sample]>>17;
start=sampleOffK007232[chan[i].sample]&0x1ffff;
loop=start+s->length8;
}
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,MIN(131072-1,s->length8));
}
start=MIN(start,MIN(getSampleMemCapacity(),131072)-1);
loop=MIN(loop,MIN(getSampleMemCapacity(),131072)-1);
// force keyoff first
chWrite(i,2,0xff);
chWrite(i,3,0xff);
chWrite(i,4,0x1);
chWrite(i,5,0);
// keyon
const unsigned char prevLoop=lastLoop;
if (s->isLoopable()) {
loop=start+s->loopStart;
lastLoop|=(1<<i);
} else {
lastLoop&=~(1<<i);
}
if (prevLoop!=lastLoop) {
rWrite(0xd,lastLoop);
}
if (chan[i].prevBank!=(int)bank) {
rWrite(0x12+i,bank);
chan[i].prevBank=bank;
}
if (chan[i].prevFreq!=chan[i].freq) {
chWrite(i,0,chan[i].freq&0xff);
chWrite(i,1,(chan[i].freq>>8)&0xf);
chan[i].prevFreq=chan[i].freq;
}
chWrite(i,2,start&0xff);
chWrite(i,3,start>>8);
chWrite(i,4,start>>16);
chWrite(i,5,0);
if (s->isLoopable() && start!=loop) {
chWrite(i,2,loop&0xff);
chWrite(i,3,loop>>8);
chWrite(i,4,loop>>16);
}
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
if (!isMuted[i]) {
chan[i].volumeChanged=true;
}
}
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
chWrite(i,2,0xff);
chWrite(i,3,0xff);
chWrite(i,4,0x1);
chWrite(i,5,0);
const unsigned char prevLoop=lastLoop;
lastLoop&=~(1<<i);
if (prevLoop!=lastLoop) {
rWrite(0xd,lastLoop);
}
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
if (chan[i].prevFreq!=chan[i].freq) {
chWrite(i,0,chan[i].freq&0xff);
chWrite(i,1,(chan[i].freq>>8)&0xf);
chan[i].prevFreq=chan[i].freq;
}
chan[i].freqChanged=false;
}
}
}
}
int DivPlatformK007232::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
}
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].sample=-1;
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
}
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.vol.has) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PANNING:
chan[c.chan].panning=(c.value>>4)|(c.value2&0xf0);
if (!isMuted[c.chan] && stereo) {
chan[c.chan].volumeChanged=true;
}
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
const int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO: {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformK007232::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
chan[ch].volumeChanged=true;
}
void DivPlatformK007232::forceIns() {
while (!writes.empty()) writes.pop();
for (int i=0; i<2; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
}
}
void* DivPlatformK007232::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformK007232::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformK007232::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformK007232::reset() {
while (!writes.empty()) {
writes.pop();
}
memset(regPool,0,20);
k007232.reset();
lastLoop=0;
lastVolume=0;
for (int i=0; i<2; i++) {
chan[i]=DivPlatformK007232::Channel();
chan[i].std.setEngine(parent);
// keyoff all channels
chWrite(i,0,0);
chWrite(i,1,0);
chWrite(i,2,0xff);
chWrite(i,3,0xff);
chWrite(i,4,1);
chWrite(i,5,0);
}
}
bool DivPlatformK007232::isStereo() {
return stereo;
}
void DivPlatformK007232::notifyInsChange(int ins) {
for (int i=0; i<2; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformK007232::notifyWaveChange(int wave) {
// TODO when wavetables are added
// TODO they probably won't be added unless the samples reside in RAM
}
void DivPlatformK007232::notifyInsDeletion(void* ins) {
for (int i=0; i<2; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformK007232::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC;
CHECK_CUSTOM_CLOCK;
rate=chipClock/4;
stereo=flags.getBool("stereo",false);
for (int i=0; i<2; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformK007232::poke(unsigned int addr, unsigned short val) {
rWrite(addr&0x1f,val);
}
void DivPlatformK007232::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr&0x1f,i.val);
}
unsigned char* DivPlatformK007232::getRegisterPool() {
return regPool;
}
int DivPlatformK007232::getRegisterPoolSize() {
return 20;
}
const void* DivPlatformK007232::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformK007232::getSampleMemCapacity(int index) {
return index == 0 ? 16777216 : 0;
}
size_t DivPlatformK007232::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformK007232::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
}
void DivPlatformK007232::renderSamples(int sysID) {
memset(sampleMem,0xc0,getSampleMemCapacity());
memset(sampleOffK007232,0,256*sizeof(unsigned int));
memset(sampleLoaded,0,256*sizeof(bool));
size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
if (!s->renderOn[0][sysID]) {
sampleOffK007232[i]=0;
continue;
}
const int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-1,length);
if (actualLength>0) {
if (actualLength>131072-1) {
actualLength=131072-1;
}
if ((memPos&0xfe0000)!=((memPos+actualLength+1)&0xfe0000)) {
memPos=(memPos+0x1ffff)&0xfe0000;
}
sampleOffK007232[i]=memPos;
for (int j=0; j<actualLength; j++) {
// convert to 7 bit unsigned
unsigned char val=(unsigned char)(s->data8[j])^0x80;
sampleMem[memPos++]=(val>>1)&0x7f;
}
// write end of sample marker
memset(&sampleMem[memPos],0xc0,1);
memPos+=1;
}
if (actualLength<length) {
logW("out of K007232 PCM memory for sample %d!",i);
break;
}
sampleLoaded[i]=true;
}
sampleMemLen=memPos;
}
int DivPlatformK007232::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<2; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
sampleMem=new unsigned char[getSampleMemCapacity()];
sampleMemLen=0;
setFlags(flags);
reset();
return 2;
}
void DivPlatformK007232::quit() {
delete[] sampleMem;
for (int i=0; i<2; i++) {
delete oscBuf[i];
}
}

View file

@ -0,0 +1,116 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _K007232_H
#define _K007232_H
#include "../dispatch.h"
#include <queue>
#include "../macroInt.h"
#include "vgsound_emu/src/k007232/k007232.hpp"
class DivPlatformK007232: public DivDispatch, public k007232_intf {
struct Channel: public SharedChannel<int> {
int prevFreq;
unsigned int audPos;
int prevBank;
int sample;
int panning, prevPan;
bool volumeChanged, setPos;
int resVol, lvol, rvol;
int macroVolMul;
Channel():
SharedChannel<int>(15),
prevFreq(-1),
audPos(0),
prevBank(-1),
sample(-1),
panning(255),
prevPan(-1),
volumeChanged(false),
setPos(false),
resVol(15),
lvol(15),
rvol(15),
macroVolMul(64) {}
};
Channel chan[2];
DivDispatchOscBuffer* oscBuf[2];
bool isMuted[2];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
unsigned short delay;
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=1):
addr(a),
val(v),
delay(d) {}
};
std::queue<QueuedWrite> writes;
unsigned int sampleOffK007232[256];
bool sampleLoaded[256];
int delay;
unsigned char lastLoop, lastVolume;
bool stereo;
unsigned char* sampleMem;
size_t sampleMemLen;
k007232_core k007232;
unsigned char regPool[20];
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
public:
u8 read_sample(u8 ne, u32 address);
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
void setChipModel(int type);
void notifyInsChange(int ins);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);
void setFlags(const DivConfig& flags);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);
bool isSampleLoaded(int index, int sample);
void renderSamples(int chipID);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
DivPlatformK007232():
DivDispatch(),
k007232_intf(),
k007232(*this) {}
};
#endif

View file

@ -40,6 +40,7 @@
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <string> #include <string>
@ -325,6 +326,86 @@ struct ymfm_output
}; };
// ======================> ymfm_wavfile
// this class is a debugging helper that accumulates data and writes it to wav files
template<int Channels>
class ymfm_wavfile
{
public:
// construction
ymfm_wavfile(uint32_t samplerate = 44100) :
m_samplerate(samplerate)
{
}
// configuration
ymfm_wavfile &set_index(uint32_t index) { m_index = index; return *this; }
ymfm_wavfile &set_samplerate(uint32_t samplerate) { m_samplerate = samplerate; return *this; }
// destruction
~ymfm_wavfile()
{
if (!m_buffer.empty())
{
// create file
char name[20];
sprintf(name, "wavlog-%02d.wav", m_index);
FILE *out = fopen(name, "wb");
// make the wav file header
uint8_t header[44];
memcpy(&header[0], "RIFF", 4);
*(uint32_t *)&header[4] = m_buffer.size() * 2 + 44 - 8;
memcpy(&header[8], "WAVE", 4);
memcpy(&header[12], "fmt ", 4);
*(uint32_t *)&header[16] = 16;
*(uint16_t *)&header[20] = 1;
*(uint16_t *)&header[22] = Channels;
*(uint32_t *)&header[24] = m_samplerate;
*(uint32_t *)&header[28] = m_samplerate * 2 * Channels;
*(uint16_t *)&header[32] = 2 * Channels;
*(uint16_t *)&header[34] = 16;
memcpy(&header[36], "data", 4);
*(uint32_t *)&header[40] = m_buffer.size() * 2 + 44 - 44;
// write header then data
fwrite(&header[0], 1, sizeof(header), out);
fwrite(&m_buffer[0], 2, m_buffer.size(), out);
fclose(out);
}
}
// add data to the file
template<int Outputs>
void add(ymfm_output<Outputs> output)
{
int16_t sum[Channels] = { 0 };
for (int index = 0; index < Outputs; index++)
sum[index % Channels] += output.data[index];
for (int index = 0; index < Channels; index++)
m_buffer.push_back(sum[index]);
}
// add data to the file, using a reference
template<int Outputs>
void add(ymfm_output<Outputs> output, ymfm_output<Outputs> const &ref)
{
int16_t sum[Channels] = { 0 };
for (int index = 0; index < Outputs; index++)
sum[index % Channels] += output.data[index] - ref.data[index];
for (int index = 0; index < Channels; index++)
m_buffer.push_back(sum[index]);
}
private:
// internal state
uint32_t m_index;
uint32_t m_samplerate;
std::vector<int16_t> m_buffer;
};
// ======================> ymfm_saved_state // ======================> ymfm_saved_state
// this class contains a managed vector of bytes that is used to save and // this class contains a managed vector of bytes that is used to save and

View file

@ -465,13 +465,24 @@ void adpcm_b_channel::clock()
if (position < 0x10000) if (position < 0x10000)
return; return;
// if playing from RAM/ROM, check the end address and process // if we're about to process nibble 0, fetch sample
if (m_curnibble == 0)
{
// playing from RAM/ROM
if (m_regs.external())
m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress);
}
// extract the nibble from our current byte
uint8_t data = uint8_t(m_curbyte << (4 * m_curnibble)) >> 4;
m_curnibble ^= 1;
// we just processed the last nibble
if (m_curnibble == 0)
{
// if playing from RAM/ROM, check the end/limit address or advance
if (m_regs.external()) if (m_regs.external())
{ {
// wrap at the limit address
if (at_limit())
m_curaddress = 0;
// handle the sample end, either repeating or stopping // handle the sample end, either repeating or stopping
if (at_end()) if (at_end())
{ {
@ -479,7 +490,7 @@ void adpcm_b_channel::clock()
if (m_regs.repeat()) if (m_regs.repeat())
load_start(); load_start();
// otherwise, done; set the EOS bit and return // otherwise, done; set the EOS bit
else else
{ {
m_accumulator = 0; m_accumulator = 0;
@ -490,24 +501,25 @@ void adpcm_b_channel::clock()
} }
} }
// if we're about to process nibble 0, fetch and increment // wrap at the limit address
if (m_curnibble == 0) else if (at_limit())
m_curaddress = 0;
// otherwise, advance the current address
else
{ {
m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++); m_curaddress++;
m_curaddress &= 0xffffff; m_curaddress &= 0xffffff;
} }
} }
// extract the nibble from our current byte // if CPU-driven, copy the next byte and request more
uint8_t data = uint8_t(m_curbyte << (4 * m_curnibble)) >> 4; else
m_curnibble ^= 1;
// if CPU-driven and we just processed the last nibble, copy the next byte and request more
if (m_curnibble == 0 && !m_regs.external())
{ {
m_curbyte = m_regs.cpudata(); m_curbyte = m_regs.cpudata();
m_status |= STATUS_BRDY; m_status |= STATUS_BRDY;
} }
}
// remember previous value for interpolation // remember previous value for interpolation
m_prev_accum = m_accumulator; m_prev_accum = m_accumulator;
@ -574,19 +586,28 @@ uint8_t adpcm_b_channel::read(uint32_t regnum)
m_dummy_read--; m_dummy_read--;
} }
// read the data
else
{
// read from outside of the chip
result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++);
// did we hit the end? if so, signal EOS // did we hit the end? if so, signal EOS
if (at_end()) if (at_end())
{ {
m_status = STATUS_EOS | STATUS_BRDY; m_status = STATUS_EOS | STATUS_BRDY;
debug::log_keyon("%s\n", "ADPCM EOS"); debug::log_keyon("%s\n", "ADPCM EOS");
} }
// otherwise, write the data and signal ready
else else
{ {
result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++); // signal ready
m_status = STATUS_BRDY; m_status = STATUS_BRDY;
} }
// wrap at the limit address
if (at_limit())
m_curaddress = 0;
}
} }
return result; return result;
} }

View file

@ -351,11 +351,11 @@ private:
// load the start address // load the start address
void load_start(); void load_start();
// limit checker // limit checker; stops at the last byte of the chunk described by address_shift()
bool at_limit() const { return (m_curaddress >> address_shift()) >= m_regs.limit(); } bool at_limit() const { return (m_curaddress == (((m_regs.limit() + 1) << address_shift()) - 1)); }
// end checker // end checker; stops at the last byte of the chunk described by address_shift()
bool at_end() const { return (m_curaddress >> address_shift()) > m_regs.end(); } bool at_end() const { return (m_curaddress == (((m_regs.end() + 1) << address_shift()) - 1)); }
// internal state // internal state
uint32_t const m_address_shift; // address bits shift-left uint32_t const m_address_shift; // address bits shift-left

View file

@ -33,6 +33,8 @@
#pragma once #pragma once
#define YMFM_DEBUG_LOG_WAVFILES (0)
namespace ymfm namespace ymfm
{ {
@ -162,8 +164,8 @@ template<class RegisterType> class fm_engine_base;
template<class RegisterType> template<class RegisterType>
class fm_operator class fm_operator
{ {
// "quiet" value, used to optimize when we can skip doing working // "quiet" value, used to optimize when we can skip doing work
static constexpr uint32_t EG_QUIET = 0x200; static constexpr uint32_t EG_QUIET = 0x380;
public: public:
// constructor // constructor
@ -206,6 +208,7 @@ public:
// simple getters for debugging // simple getters for debugging
envelope_state debug_eg_state() const { return m_env_state; } envelope_state debug_eg_state() const { return m_env_state; }
uint16_t debug_eg_attenuation() const { return m_env_attenuation; } uint16_t debug_eg_attenuation() const { return m_env_attenuation; }
uint8_t debug_ssg_inverted() const { return m_ssg_inverted; }
opdata_cache &debug_cache() { return m_cache; } opdata_cache &debug_cache() { return m_cache; }
private: private:
@ -406,7 +409,14 @@ public:
void set_clock_prescale(uint32_t prescale) { m_clock_prescale = prescale; } void set_clock_prescale(uint32_t prescale) { m_clock_prescale = prescale; }
// compute sample rate // compute sample rate
uint32_t sample_rate(uint32_t baseclock) const { return baseclock / (m_clock_prescale * OPERATORS); } uint32_t sample_rate(uint32_t baseclock) const
{
#if (YMFM_DEBUG_LOG_WAVFILES)
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
m_wavfile[chnum].set_samplerate(baseclock / (m_clock_prescale * OPERATORS));
#endif
return baseclock / (m_clock_prescale * OPERATORS);
}
// return the owning device // return the owning device
ymfm_interface &intf() const { return m_intf; } ymfm_interface &intf() const { return m_intf; }
@ -453,6 +463,9 @@ protected:
RegisterType m_regs; // register accessor RegisterType m_regs; // register accessor
std::unique_ptr<fm_channel<RegisterType>> m_channel[CHANNELS]; // channel pointers std::unique_ptr<fm_channel<RegisterType>> m_channel[CHANNELS]; // channel pointers
std::unique_ptr<fm_operator<RegisterType>> m_operator[OPERATORS]; // operator pointers std::unique_ptr<fm_operator<RegisterType>> m_operator[OPERATORS]; // operator pointers
#if (YMFM_DEBUG_LOG_WAVFILES)
mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging
#endif
}; };
} }

View file

@ -448,6 +448,8 @@ void fm_operator<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
// clock the SSG-EG state (OPN/OPNA) // clock the SSG-EG state (OPN/OPNA)
if (m_regs.op_ssg_eg_enable(m_opoffs)) if (m_regs.op_ssg_eg_enable(m_opoffs))
clock_ssg_eg_state(); clock_ssg_eg_state();
else
m_ssg_inverted = false;
// clock the envelope if on an envelope cycle; env_counter is a x.2 value // clock the envelope if on an envelope cycle; env_counter is a x.2 value
if (bitfield(env_counter, 0, 2) == 0) if (bitfield(env_counter, 0, 2) == 0)
@ -470,15 +472,6 @@ int32_t fm_operator<RegisterType>::compute_volume(uint32_t phase, uint32_t am_of
// the low 10 bits of phase represents a full 2*PI period over // the low 10 bits of phase represents a full 2*PI period over
// the full sin wave // the full sin wave
#if 0
// temporary envelope logging
if (m_choffs == 0)
{
printf(" %c@%02X:%03X", "PADSRV"[m_env_state], m_cache.eg_rate[m_env_state], envelope_attenuation(am_offset));
if (m_opoffs == 0x18) printf("\n");
}
#endif
// early out if the envelope is effectively off // early out if the envelope is effectively off
if (m_env_attenuation > EG_QUIET && m_cache.eg_shift == 0) if (m_env_attenuation > EG_QUIET && m_cache.eg_shift == 0)
return 0; return 0;
@ -896,6 +889,23 @@ void fm_channel<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
if (m_op[opnum] != nullptr) if (m_op[opnum] != nullptr)
m_op[opnum]->clock(env_counter, lfo_raw_pm); m_op[opnum]->clock(env_counter, lfo_raw_pm);
/*
useful temporary code for envelope debugging
if (m_choffs == 0x101)
{
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
{
auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)];
printf(" %c%03X%c%c ",
"PADSRV"[op.debug_eg_state()],
op.debug_eg_attenuation(),
op.debug_ssg_inverted() ? '-' : '+',
m_regs.op_ssg_eg_enable(op.opoffs()) ? '0' + m_regs.op_ssg_eg_mode(op.opoffs()) : ' ');
}
printf(" -- ");
}
*/
} }
@ -943,7 +953,8 @@ void fm_channel<RegisterType>::output_2op(output_data &output, uint32_t rshift,
} }
else else
{ {
result = op1value + (m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift); result = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> rshift;
result += m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift;
int32_t clipmin = -clipmax - 1; int32_t clipmin = -clipmax - 1;
result = clamp(result, clipmin, clipmax); result = clamp(result, clipmin, clipmax);
} }
@ -1180,6 +1191,7 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB), m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
m_irq_state(0), m_irq_state(0),
m_timer_running{0,0}, m_timer_running{0,0},
m_total_clocks(0),
m_active_channels(ALL_CHANNELS), m_active_channels(ALL_CHANNELS),
m_modified_channels(ALL_CHANNELS), m_modified_channels(ALL_CHANNELS),
m_prepare_count(0) m_prepare_count(0)
@ -1195,6 +1207,11 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum)); m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
#if (YMFM_DEBUG_LOG_WAVFILES)
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
m_wavfile[chnum].set_index(chnum);
#endif
// do the initial operator assignment // do the initial operator assignment
assign_operators(); assign_operators();
} }
@ -1305,24 +1322,6 @@ uint32_t fm_engine_base<RegisterType>::clock(uint32_t chanmask)
if (bitfield(chanmask, chnum)) if (bitfield(chanmask, chnum))
m_channel[chnum]->clock(m_env_counter, lfo_raw_pm); m_channel[chnum]->clock(m_env_counter, lfo_raw_pm);
#if 0
//Temporary debugging...
static double curtime = 0;
//for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
uint32_t chnum = 4;
{
printf("t=%.4f ch%d: ", curtime, chnum);
for (uint32_t opnum = 0; opnum < 4; opnum++)
{
auto op = debug_channel(chnum)->debug_operator(opnum);
auto eg_state = op->debug_eg_state();
printf(" %c%03X[%02X]%c ", "PADSRV"[eg_state], op.debug_eg_attenuation(), op.debug_cache().eg_rate[eg_state], m_regs.op_ssg_eg_enable(op.opoffs()) ? '*' : ' ');
}
printf(" -- ");
}
curtime += 1.0 / double(sample_rate(7670454));
#endif
// return the envelope counter as it is used to clock ADPCM-A // return the envelope counter as it is used to clock ADPCM-A
return m_env_counter; return m_env_counter;
} }
@ -1340,6 +1339,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
chanmask &= debug::GLOBAL_FM_CHANNEL_MASK; chanmask &= debug::GLOBAL_FM_CHANNEL_MASK;
// mask out inactive channels // mask out inactive channels
if (!YMFM_DEBUG_LOG_WAVFILES)
chanmask &= m_active_channels; chanmask &= m_active_channels;
// handle the rhythm case, where some of the operators are dedicated // handle the rhythm case, where some of the operators are dedicated
@ -1358,6 +1358,9 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(chanmask, chnum)) if (bitfield(chanmask, chnum))
{ {
#if (YMFM_DEBUG_LOG_WAVFILES)
auto reference = output;
#endif
if (chnum == 6) if (chnum == 6)
m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax); m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax);
else if (chnum == 7) else if (chnum == 7)
@ -1368,6 +1371,9 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
m_channel[chnum]->output_4op(output, rshift, clipmax); m_channel[chnum]->output_4op(output, rshift, clipmax);
else else
m_channel[chnum]->output_2op(output, rshift, clipmax); m_channel[chnum]->output_2op(output, rshift, clipmax);
#if (YMFM_DEBUG_LOG_WAVFILES)
m_wavfile[chnum].add(output, reference);
#endif
} }
} }
else else
@ -1376,10 +1382,16 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(chanmask, chnum)) if (bitfield(chanmask, chnum))
{ {
#if (YMFM_DEBUG_LOG_WAVFILES)
auto reference = output;
#endif
if (m_channel[chnum]->is4op()) if (m_channel[chnum]->is4op())
m_channel[chnum]->output_4op(output, rshift, clipmax); m_channel[chnum]->output_4op(output, rshift, clipmax);
else else
m_channel[chnum]->output_2op(output, rshift, clipmax); m_channel[chnum]->output_2op(output, rshift, clipmax);
#if (YMFM_DEBUG_LOG_WAVFILES)
m_wavfile[chnum].add(output, reference);
#endif
} }
} }
} }
@ -1508,7 +1520,10 @@ void fm_engine_base<RegisterType>::engine_timer_expired(uint32_t tnum)
if (tnum == 0 && m_regs.csm()) if (tnum == 0 && m_regs.csm())
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum)) if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum))
m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum); {
m_channel[chnum]->keyonoff(0xf, KEYON_CSM, chnum);
m_modified_channels |= 1 << chnum;
}
// reset // reset
m_timer_running[tnum] = false; m_timer_running[tnum] = false;

View file

@ -174,7 +174,7 @@ public:
// system-wide registers // system-wide registers
uint32_t test() const { return byte(0x01, 0, 8); } uint32_t test() const { return byte(0x01, 0, 8); }
uint32_t lfo_reset() const { return byte(0x01, 1, 1); } uint32_t lfo_reset() const { return byte(0x01, 1, 1); }
uint32_t noise_frequency() const { return byte(0x0f, 0, 5); } uint32_t noise_frequency() const { return byte(0x0f, 0, 5) ^ 0x1f; }
uint32_t noise_enable() const { return byte(0x0f, 7, 1); } uint32_t noise_enable() const { return byte(0x0f, 7, 1); }
uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); } uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); }
uint32_t timer_b_value() const { return byte(0x12, 0, 8); } uint32_t timer_b_value() const { return byte(0x12, 0, 8); }

View file

@ -146,7 +146,10 @@ bool opn_registers_base<IsOpnA>::write(uint16_t index, uint8_t data, uint32_t &c
// borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations // borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations
if ((index & 0xf0) == 0xa0) if ((index & 0xf0) == 0xa0)
{ {
uint32_t latchindex = 0xb8 | (bitfield(index, 3) << 2) | bitfield(index, 0, 2); if (bitfield(index, 0, 2) == 3)
return false;
uint32_t latchindex = 0xb8 | bitfield(index, 3);
if (IsOpnA) if (IsOpnA)
latchindex |= index & 0x100; latchindex |= index & 0x100;
@ -157,9 +160,16 @@ bool opn_registers_base<IsOpnA>::write(uint16_t index, uint8_t data, uint32_t &c
// writes to the lower half only commit if the latch is there // writes to the lower half only commit if the latch is there
else if (bitfield(m_regdata[latchindex], 7)) else if (bitfield(m_regdata[latchindex], 7))
{ {
m_regdata[index] = data;
m_regdata[index | 4] = m_regdata[latchindex] & 0x3f; m_regdata[index | 4] = m_regdata[latchindex] & 0x3f;
m_regdata[latchindex] = 0; m_regdata[latchindex] = 0;
} }
return false;
}
else if ((index & 0xf8) == 0xb8)
{
// registers 0xb8-0xbf are used internally
return false;
} }
// everything else is normal // everything else is normal
@ -195,7 +205,12 @@ int32_t opn_registers_base<IsOpnA>::clock_noise_and_lfo()
if (!IsOpnA || !lfo_enable()) if (!IsOpnA || !lfo_enable())
{ {
m_lfo_counter = 0; m_lfo_counter = 0;
m_lfo_am = 0;
// special case: if LFO is disabled on OPNA, it basically just keeps the counter
// at 0; since position 0 gives an AM value of 0x3f, it is important to reflect
// that here; for example, MegaDrive Venom plays some notes with LFO globally
// disabled but enabling LFO on the operators, and it expects this added attenutation
m_lfo_am = IsOpnA ? 0x3f : 0x00;
return 0; return 0;
} }
@ -417,10 +432,10 @@ std::string opn_registers_base<IsOpnA>::log_keyon(uint32_t choffs, uint32_t opof
ch_output_1(choffs) ? 'R' : '-'); ch_output_1(choffs) ? 'R' : '-');
if (op_ssg_eg_enable(opoffs)) if (op_ssg_eg_enable(opoffs))
end += sprintf(end, " ssg=%X", op_ssg_eg_mode(opoffs)); end += sprintf(end, " ssg=%X", op_ssg_eg_mode(opoffs));
bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0);
if (am) if (am)
end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs));
bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0); bool pm = (ch_lfo_pm_sens(choffs) != 0);
if (pm) if (pm)
end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs));
if (am || pm) if (am || pm)
@ -1094,7 +1109,7 @@ uint8_t ym2608::read_status_hi()
uint8_t ym2608::read_data_hi() uint8_t ym2608::read_data_hi()
{ {
uint8_t result = 0; uint8_t result = 0;
if (m_address < 0x10) if ((m_address & 0xff) < 0x10)
{ {
// 00-0F: Read from ADPCM-B // 00-0F: Read from ADPCM-B
result = m_adpcm_b.read(m_address & 0x0f); result = m_adpcm_b.read(m_address & 0x0f);

View file

@ -784,7 +784,7 @@ public:
protected: protected:
// simulate the DAC discontinuity // simulate the DAC discontinuity
int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); } int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 3) : (value + 4); }
// internal state // internal state
uint16_t m_address; // address register uint16_t m_address; // address register

View file

@ -201,19 +201,14 @@ void ssg_engine::output(output_data &output)
for (int chan = 0; chan < 3; chan++) for (int chan = 0; chan < 3; chan++)
{ {
// noise depends on the noise state, which is the LSB of m_noise_state // noise depends on the noise state, which is the LSB of m_noise_state
uint32_t noise_on = m_regs.ch_noise_enable(chan) & m_noise_state; uint32_t noise_on = m_regs.ch_noise_enable_n(chan) | m_noise_state;
// tone depends on the current tone state // tone depends on the current tone state
uint32_t tone_on = m_regs.ch_tone_enable(chan) & m_tone_state[chan]; uint32_t tone_on = m_regs.ch_tone_enable_n(chan) | m_tone_state[chan];
// if envelope is enabled but tone and noise aren't, use the envelope
// volume
uint32_t volume;
if (m_regs.ch_envelope_enable(chan) && !m_regs.ch_noise_enable(chan) && !m_regs.ch_tone_enable(chan))
volume = envelope_volume;
// if neither tone nor noise enabled, return 0 // if neither tone nor noise enabled, return 0
else if ((noise_on | tone_on) == 0) uint32_t volume;
if ((noise_on & tone_on) == 0)
volume = 0; volume = 0;
// if the envelope is enabled, use its amplitude // if the envelope is enabled, use its amplitude

View file

@ -130,8 +130,8 @@ public:
uint32_t io_b_data() const { return m_regdata[0x0f]; } uint32_t io_b_data() const { return m_regdata[0x0f]; }
// per-channel registers // per-channel registers
uint32_t ch_noise_enable(uint32_t choffs) const { return bitfield(~m_regdata[0x07], 3 + choffs); } uint32_t ch_noise_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 3 + choffs); }
uint32_t ch_tone_enable(uint32_t choffs) const { return bitfield(~m_regdata[0x07], 0 + choffs); } uint32_t ch_tone_enable_n(uint32_t choffs) const { return bitfield(m_regdata[0x07], 0 + choffs); }
uint32_t ch_tone_period(uint32_t choffs) const { return m_regdata[0x00 + 2 * choffs] | (bitfield(m_regdata[0x01 + 2 * choffs], 0, 4) << 8); } uint32_t ch_tone_period(uint32_t choffs) const { return m_regdata[0x00 + 2 * choffs] | (bitfield(m_regdata[0x01 + 2 * choffs], 0, 4) << 8); }
uint32_t ch_envelope_enable(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 4); } uint32_t ch_envelope_enable(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 4); }
uint32_t ch_amplitude(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 0, 4); } uint32_t ch_amplitude(uint32_t choffs) const { return bitfield(m_regdata[0x08 + choffs], 0, 4); }

View file

@ -115,6 +115,8 @@ enum DivSystem {
DIV_SYSTEM_YM2612_FRAC_EXT, DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_MSM5232, DIV_SYSTEM_MSM5232,
DIV_SYSTEM_T6W28, DIV_SYSTEM_T6W28,
DIV_SYSTEM_K007232,
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG, DIV_SYSTEM_PONG,
DIV_SYSTEM_DUMMY, DIV_SYSTEM_DUMMY,

View file

@ -1179,10 +1179,10 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef( sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef(
"Pokémon Mini", NULL, 0x99, 0, 1, false, true, 0, false, 0, "Pokémon Mini", NULL, 0x99, 0, 1, false, true, 0, false, 0,
"this one is like PC Speaker but has duty cycles.", "this one is like PC Speaker but has duty cycles.",
{"Square"}, {"Pulse"},
{"SQ"}, {"P"},
{DIV_CH_PULSE}, {DIV_CH_PULSE},
{DIV_INS_BEEPER} {DIV_INS_POKEMINI}
); );
sysDefs[DIV_SYSTEM_SEGAPCM]=new DivSysDef( sysDefs[DIV_SYSTEM_SEGAPCM]=new DivSysDef(
@ -1732,6 +1732,26 @@ void DivEngine::registerSystems() {
{DIV_INS_AMIGA} {DIV_INS_AMIGA}
); );
sysDefs[DIV_SYSTEM_K007232]=new DivSysDef(
"Konami K007232", NULL, 0xc6, 0, 2, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
"this PCM chip was widely used at Konami arcade boards in 1986-1990.",
{"Channel 1", "Channel 2"},
{"CH1", "CH2"},
{DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_K007232, DIV_INS_K007232},
{DIV_INS_AMIGA, DIV_INS_AMIGA}
);
sysDefs[DIV_SYSTEM_GA20]=new DivSysDef(
"Irem GA20", NULL, 0xc7, 0, 4, false, true, 0x171, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
"yet another PCM chip from Irem.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_GA20, DIV_INS_GA20, DIV_INS_GA20, DIV_INS_GA20},
{DIV_INS_AMIGA, DIV_INS_AMIGA}
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0,
"this is a system designed for testing purposes.", "this is a system designed for testing purposes.",

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define _USE_MATH_DEFINES
#include "gui.h" #include "gui.h"
#include "../ta-log.h" #include "../ta-log.h"
#include "imgui.h" #include "imgui.h"

View file

@ -418,6 +418,18 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_T6W28]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_T6W28]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i); name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break; break;
case DIV_INS_K007232:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K007232]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_GA20:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GA20]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_POKEMINI:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEMINI]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
default: default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i); name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);

View file

@ -49,6 +49,7 @@
#include "../engine/platform/es5506.h" #include "../engine/platform/es5506.h"
#include "../engine/platform/lynx.h" #include "../engine/platform/lynx.h"
#include "../engine/platform/pcmdac.h" #include "../engine/platform/pcmdac.h"
#include "../engine/platform/k007232.h"
#include "../engine/platform/dummy.h" #include "../engine/platform/dummy.h"
#define COMMON_CHIP_DEBUG \ #define COMMON_CHIP_DEBUG \
@ -509,6 +510,17 @@ void putDispatchChip(void* data, int type) {
ImGui::TextColored(ch->irqTrigger?colorOn:colorOff,">> IrqTrigger"); ImGui::TextColored(ch->irqTrigger?colorOn:colorOff,">> IrqTrigger");
break; break;
} }
case DIV_SYSTEM_K007232: {
DivPlatformK007232* ch=(DivPlatformK007232*)data;
ImGui::Text("> K007232");
COMMON_CHIP_DEBUG;
ImGui::Text("- delay: %.2x",ch->delay);
ImGui::Text("- lastLoop: %.2x",ch->lastLoop);
ImGui::Text("- lastVolume: %.2x",ch->lastVolume);
COMMON_CHIP_DEBUG_BOOL;
ImGui::TextColored(ch->stereo?colorOn:colorOff,">> Stereo");
break;
}
default: default:
ImGui::Text("Unimplemented chip! Help!"); ImGui::Text("Unimplemented chip! Help!");
break; break;
@ -991,6 +1003,25 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos"); ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
break; break;
} }
case DIV_SYSTEM_K007232: {
DivPlatformK007232::Channel* ch=(DivPlatformK007232::Channel*)data;
ImGui::Text("> K007232");
COMMON_CHAN_DEBUG;
ImGui::Text("- prevFreq: %d",ch->prevFreq);
ImGui::Text("* Sample: %d",ch->sample);
ImGui::Text(" - pos: %d",ch->audPos);
ImGui::Text(" - prevBank: %d",ch->prevBank);
ImGui::Text("* panning: %d",ch->panning);
ImGui::Text(" - prev: %d",ch->prevPan);
ImGui::Text("- resVol: %.2x",ch->resVol);
ImGui::Text("- lvol: %.2x",ch->lvol);
ImGui::Text("- rvol: %.2x",ch->rvol);
ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->volumeChanged?colorOn:colorOff,">> VolumeChanged");
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
break;
}
default: default:
ImGui::Text("Unimplemented chip! Help!"); ImGui::Text("Unimplemented chip! Help!");
break; break;

View file

@ -1324,7 +1324,8 @@ void FurnaceGUI::doAction(int what) {
i==DIV_INS_VRC6 || i==DIV_INS_VRC6 ||
i==DIV_INS_SU || i==DIV_INS_SU ||
i==DIV_INS_SNES || i==DIV_INS_SNES ||
i==DIV_INS_ES5506) { i==DIV_INS_ES5506 ||
i==DIV_INS_K007232) {
makeInsTypeList.push_back(i); makeInsTypeList.push_back(i);
} }
} }

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define _USE_MATH_DEFINES
#include "gui.h" #include "gui.h"
#include "IconsFontAwesome4.h" #include "IconsFontAwesome4.h"
#include <fmt/printf.h> #include <fmt/printf.h>

View file

@ -185,6 +185,9 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_RF5C68, GUI_COLOR_INSTR_RF5C68,
GUI_COLOR_INSTR_MSM5232, GUI_COLOR_INSTR_MSM5232,
GUI_COLOR_INSTR_T6W28, GUI_COLOR_INSTR_T6W28,
GUI_COLOR_INSTR_K007232,
GUI_COLOR_INSTR_GA20,
GUI_COLOR_INSTR_POKEMINI,
GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG, GUI_COLOR_CHANNEL_BG,

View file

@ -125,6 +125,9 @@ const char* insTypes[DIV_INS_MAX+1]={
"RF5C68", "RF5C68",
"MSM5232", "MSM5232",
"T6W28", "T6W28",
"K007232",
"GA20",
"Pokémon Mini",
NULL NULL
}; };
@ -808,6 +811,9 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_RF5C68,"",ImVec4(1.0f,0.3f,0.3f,1.0f)), D(GUI_COLOR_INSTR_RF5C68,"",ImVec4(1.0f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_MSM5232,"",ImVec4(0.5f,0.9f,1.0f,1.0f)), D(GUI_COLOR_INSTR_MSM5232,"",ImVec4(0.5f,0.9f,1.0f,1.0f)),
D(GUI_COLOR_INSTR_T6W28,"",ImVec4(1.0f,0.8f,0.1f,1.0f)), D(GUI_COLOR_INSTR_T6W28,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_K007232,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_GA20,"",ImVec4(0.1f,1.0f,0.4f,1.0f)),
D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@ -977,6 +983,7 @@ const int availableSystems[]={
DIV_SYSTEM_RF5C68, DIV_SYSTEM_RF5C68,
DIV_SYSTEM_SNES, DIV_SYSTEM_SNES,
DIV_SYSTEM_MSM5232, DIV_SYSTEM_MSM5232,
DIV_SYSTEM_K007232,
DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG, DIV_SYSTEM_PONG,
0 // don't remove this last one! 0 // don't remove this last one!
@ -1076,6 +1083,7 @@ const int chipsSample[]={
DIV_SYSTEM_MSM6295, DIV_SYSTEM_MSM6295,
DIV_SYSTEM_RF5C68, DIV_SYSTEM_RF5C68,
DIV_SYSTEM_SNES, DIV_SYSTEM_SNES,
DIV_SYSTEM_K007232,
DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_ES5506, DIV_SYSTEM_ES5506,
0 // don't remove this last one! 0 // don't remove this last one!

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define _USE_MATH_DEFINES
#include "gui.h" #include "gui.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "../engine/macroInt.h" #include "../engine/macroInt.h"
@ -4251,7 +4252,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_VRC6 || ins->type==DIV_INS_VRC6 ||
ins->type==DIV_INS_SU || ins->type==DIV_INS_SU ||
ins->type==DIV_INS_SNES || ins->type==DIV_INS_SNES ||
ins->type==DIV_INS_ES5506) { ins->type==DIV_INS_ES5506 ||
ins->type==DIV_INS_K007232) {
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
String sName; String sName;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
@ -4971,6 +4973,9 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_QSOUND) { if (ins->type==DIV_INS_QSOUND) {
volMax=16383; volMax=16383;
} }
if (ins->type==DIV_INS_POKEMINI) {
volMax=2;
}
const char* dutyLabel="Duty/Noise"; const char* dutyLabel="Duty/Noise";
int dutyMin=0; int dutyMin=0;
@ -5004,7 +5009,7 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="Group Ctrl"; dutyLabel="Group Ctrl";
dutyMax=5; dutyMax=5;
} }
if (ins->type==DIV_INS_BEEPER) { if (ins->type==DIV_INS_BEEPER || ins->type==DIV_INS_POKEMINI) {
dutyLabel="Pulse Width"; dutyLabel="Pulse Width";
dutyMax=255; dutyMax=255;
} }
@ -5017,7 +5022,7 @@ void FurnaceGUI::drawInsEdit() {
} }
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC || if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC ||
ins->type==DIV_INS_PET || ins->type==DIV_INS_VIC || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_PET || ins->type==DIV_INS_VIC || ins->type==DIV_INS_SEGAPCM ||
ins->type==DIV_INS_FM) { ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232) {
dutyMax=0; dutyMax=0;
} }
if (ins->type==DIV_INS_VBOY) { if (ins->type==DIV_INS_VBOY) {
@ -5121,6 +5126,8 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_MSM6258) waveMax=0; if (ins->type==DIV_INS_MSM6258) waveMax=0;
if (ins->type==DIV_INS_MSM6295) waveMax=0; if (ins->type==DIV_INS_MSM6295) waveMax=0;
if (ins->type==DIV_INS_SEGAPCM) waveMax=0; if (ins->type==DIV_INS_SEGAPCM) waveMax=0;
if (ins->type==DIV_INS_K007232) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_SU) waveMax=7; if (ins->type==DIV_INS_SU) waveMax=7;
if (ins->type==DIV_INS_PET) { if (ins->type==DIV_INS_PET) {
waveMax=8; waveMax=8;
@ -5210,7 +5217,7 @@ void FurnaceGUI::drawInsEdit() {
} }
if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE || ins->type==DIV_INS_MIKEY || if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE || ins->type==DIV_INS_MIKEY ||
ins->type==DIV_INS_SAA1099 || ins->type==DIV_INS_NAMCO || ins->type==DIV_INS_RF5C68 || ins->type==DIV_INS_SAA1099 || ins->type==DIV_INS_NAMCO || ins->type==DIV_INS_RF5C68 ||
ins->type==DIV_INS_VBOY || ins->type==DIV_INS_T6W28) { ins->type==DIV_INS_VBOY || ins->type==DIV_INS_T6W28 || ins->type==DIV_INS_K007232) {
panMax=15; panMax=15;
} }
if (ins->type==DIV_INS_SEGAPCM) { if (ins->type==DIV_INS_SEGAPCM) {
@ -5319,7 +5326,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_ES5506 || ins->type==DIV_INS_ES5506 ||
ins->type==DIV_INS_T6W28 || ins->type==DIV_INS_T6W28 ||
ins->type==DIV_INS_VBOY || ins->type==DIV_INS_VBOY ||
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample)) { (ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232) {
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
} }
if (ex1Max>0) { if (ex1Max>0) {

View file

@ -18,7 +18,6 @@
*/ */
// for suck's fake Clang extension! // for suck's fake Clang extension!
#include <imgui.h>
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
#include "gui.h" #include "gui.h"
#include "../ta-log.h" #include "../ta-log.h"

View file

@ -1153,6 +1153,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_SNES, 64, 0, "") CH(DIV_SYSTEM_SNES, 64, 0, "")
} }
); );
ENTRY(
"Konami K007232", {
CH(DIV_SYSTEM_K007232, 64, 0, "")
}
);
ENTRY( ENTRY(
"Generic PCM DAC", { "Generic PCM DAC", {
CH(DIV_SYSTEM_PCM_DAC, 64, 0, "") CH(DIV_SYSTEM_PCM_DAC, 64, 0, "")
@ -1341,6 +1346,12 @@ void FurnaceGUI::initSystemPresets() {
// VLM5030 exists but not used for music at all // VLM5030 exists but not used for music at all
} }
); );
ENTRY(
"Konami MX5000", {
CH(DIV_SYSTEM_YM2151, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY( ENTRY(
"Konami Battlantis", { "Konami Battlantis", {
CH(DIV_SYSTEM_OPL2, 64, 0, "clockSel=3"), // 3MHz CH(DIV_SYSTEM_OPL2, 64, 0, "clockSel=3"), // 3MHz
@ -1365,6 +1376,54 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "clockSel=3") // "" CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "clockSel=3") // ""
} }
); );
ENTRY(
"Konami Fast Lane", {
CH(DIV_SYSTEM_K007232, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY(
"Konami Chequered Flag", {
CH(DIV_SYSTEM_YM2151, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K007232, 64, 0, "stereo=true"), // ""
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY(
"Konami Haunted Castle", {
CH(DIV_SYSTEM_OPL2, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_SCC, 64, 0, ""), // ""
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY(
"Konami Haunted Castle (drums mode)", {
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_SCC, 64, 0, ""), // ""
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY(
"Konami Hot Chase", {
CH(DIV_SYSTEM_K007232, 64, 0, "stereo=true"), // 3.58MHz
CH(DIV_SYSTEM_K007232, 64, 0, "stereo=true"), // ""
CH(DIV_SYSTEM_K007232, 64, 0, "stereo=true") // ""
}
);
ENTRY(
"Konami S.P.Y.", {
CH(DIV_SYSTEM_OPL2, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K007232, 64, 0, ""), // ""
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY(
"Konami S.P.Y. (drums mode)", {
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K007232, 64, 0, ""), // ""
CH(DIV_SYSTEM_K007232, 64, 0, "") // ""
}
);
ENTRY( ENTRY(
"Konami Hexion", { "Konami Hexion", {
CH(DIV_SYSTEM_SCC, 64, 0, "clockSel=2"), // 1.5MHz (3MHz input) CH(DIV_SYSTEM_SCC, 64, 0, "clockSel=2"), // 1.5MHz (3MHz input)

View file

@ -16,6 +16,8 @@
* with this program; if not, write to the Free Software Foundation, Inc., * with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define _USE_MATH_DEFINES
#include "gui.h" #include "gui.h"
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>

View file

@ -1787,6 +1787,10 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_INSTR_QSOUND,"QSound"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_QSOUND,"QSound");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_YMZ280B,"YMZ280B"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_YMZ280B,"YMZ280B");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_RF5C68,"RF5C68"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_RF5C68,"RF5C68");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_MSM5232,"MSM5232");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_K007232,"K007232");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_GA20,"GA20");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
ImGui::TreePop(); ImGui::TreePop();
} }
@ -2594,6 +2598,15 @@ void FurnaceGUI::commitSettings() {
settings.tg100Path!=e->getConfString("tg100Path","") || settings.tg100Path!=e->getConfString("tg100Path","") ||
settings.mu5Path!=e->getConfString("mu5Path",""); settings.mu5Path!=e->getConfString("mu5Path","");
bool coresChanged=(
settings.arcadeCore!=e->getConfInt("arcadeCore",0) ||
settings.ym2612Core!=e->getConfInt("ym2612Core",0) ||
settings.snCore!=e->getConfInt("snCore",0) ||
settings.nesCore!=e->getConfInt("nesCore",0) ||
settings.fdsCore!=e->getConfInt("fdsCore",0) ||
settings.c64Core!=e->getConfInt("c64Core",1)
);
e->setConf("mainFontSize",settings.mainFontSize); e->setConf("mainFontSize",settings.mainFontSize);
e->setConf("patFontSize",settings.patFontSize); e->setConf("patFontSize",settings.patFontSize);
e->setConf("iconSize",settings.iconSize); e->setConf("iconSize",settings.iconSize);
@ -2754,7 +2767,7 @@ void FurnaceGUI::commitSettings() {
} }
} }
if (!e->switchMaster()) { if (!e->switchMaster(coresChanged)) {
showError("could not initialize audio!"); showError("could not initialize audio!");
} }

View file

@ -1563,6 +1563,20 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
} }
break; break;
} }
case DIV_SYSTEM_K007232: {
bool stereo=flags.getBool("stereo",false);
if (ImGui::Checkbox("Stereo",&stereo)) {
altered=true;
}
if (altered) {
e->lockSave([&]() {
flags.set("stereo",stereo);
});
}
break;
}
case DIV_SYSTEM_SWAN: case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET: case DIV_SYSTEM_PET: