Added panning and load LFSR commands.

This commit is contained in:
Waldemar Pawlaszek 2022-02-21 12:41:06 +01:00
parent 869f799299
commit 6e79e84e53
7 changed files with 55 additions and 16 deletions

View File

@ -97,6 +97,8 @@ enum DivDispatchCmds {
DIV_CMD_SAA_ENVELOPE, DIV_CMD_SAA_ENVELOPE,
DIV_CMD_LYNX_LFSR_LOAD,
DIV_ALWAYS_SET_VOLUME, DIV_ALWAYS_SET_VOLUME,
DIV_CMD_MAX DIV_CMD_MAX

View File

@ -10,6 +10,7 @@
#define WRITE_BACKUP(ch,v) rWrite(0x24+(ch<<3),(v)) #define WRITE_BACKUP(ch,v) rWrite(0x24+(ch<<3),(v))
#define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v)) #define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v))
#define WRITE_OTHER(ch,v) rWrite(0x27+(ch<<3),(v)) #define WRITE_OTHER(ch,v) rWrite(0x27+(ch<<3),(v))
#define WRITE_ATTEN(ch,v) rWrite((0x40+ch),(v))
#define CHIP_DIVIDER 64 #define CHIP_DIVIDER 64
@ -68,6 +69,19 @@ const char* regCheatSheetLynx[]={
const char** DivPlatformLynx::getRegisterSheet() { const char** DivPlatformLynx::getRegisterSheet() {
return regCheatSheetLynx; return regCheatSheetLynx;
}
const char* DivPlatformLynx::getEffectName(unsigned char effect) {
switch (effect)
{
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
return "3xxx: Load LFSR (0 to FFF)";
break;
}
return NULL;
} }
void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) {
@ -101,21 +115,20 @@ void DivPlatformLynx::tick() {
} }
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); if (chan[i].lfsr >= 0) {
if (chan[i].resetLFSR) { WRITE_LFSR(i, (chan[i].lfsr&0xff));
chan[i].resetLFSR=false; WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
WRITE_LFSR(i, 0); chan[i].lfsr=-1;
WRITE_OTHER(i, 0);
} }
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
if (chan[i].std.hadDuty) { if (chan[i].std.hadDuty) {
chan[i].duty = chan[i].std.duty; chan[i].duty=chan[i].std.duty;
WRITE_FEEDBACK(i, chan[i].duty.feedback); WRITE_FEEDBACK(i, chan[i].duty.feedback);
} }
WRITE_BACKUP(i, chan[i].fd.backup); WRITE_BACKUP(i, chan[i].fd.backup);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7)); WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
} }
else if (chan[i].std.hadDuty) else if (chan[i].std.hadDuty) {
{
chan[i].duty = chan[i].std.duty; chan[i].duty = chan[i].std.duty;
WRITE_FEEDBACK(i, chan[i].duty.feedback); WRITE_FEEDBACK(i, chan[i].duty.feedback);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7)); WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
@ -129,9 +142,10 @@ int DivPlatformLynx::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].resetLFSR=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
chan[c.chan].actualNote=c.value; chan[c.chan].actualNote=c.value;
if (chan[c.chan].lfsr<0)
chan[c.chan].lfsr=0;
} }
chan[c.chan].active=true; chan[c.chan].active=true;
WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127))); WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
@ -142,6 +156,10 @@ int DivPlatformLynx::dispatch(DivCommand c) {
WRITE_VOLUME(c.chan, 0); WRITE_VOLUME(c.chan, 0);
chan[c.chan].std.init(NULL); chan[c.chan].std.init(NULL);
break; break;
case DIV_CMD_LYNX_LFSR_LOAD:
chan[c.chan].freqChanged=true;
chan[c.chan].lfsr=c.value;
break;
case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE: case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release(); chan[c.chan].std.release();
@ -159,6 +177,9 @@ int DivPlatformLynx::dispatch(DivCommand c) {
if (chan[c.chan].active) WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127))); if (chan[c.chan].active) WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
} }
break; break;
case DIV_CMD_PANNING:
WRITE_ATTEN(c.chan, c.value);
break;
case DIV_CMD_GET_VOLUME: case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.hasVol) { if (chan[c.chan].std.hasVol) {
return chan[c.chan].vol; return chan[c.chan].vol;

View File

@ -22,20 +22,22 @@ class DivPlatformLynx: public DivDispatch {
}; };
struct Channel { struct Channel {
DivMacroInt std;
MikeyFreqDiv fd; MikeyFreqDiv fd;
MikeyDuty duty; MikeyDuty duty;
int baseFreq, pitch, note, actualNote; int baseFreq, pitch, note, actualNote, lfsr;
unsigned char ins; unsigned char ins;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, resetLFSR; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
signed char vol, outVol; signed char vol, outVol;
DivMacroInt std;
Channel(): Channel():
std(),
fd(0), fd(0),
duty(0), duty(0),
baseFreq(0), baseFreq(0),
pitch(0), pitch(0),
note(0), note(0),
actualNote(0), actualNote(0),
lfsr(-1),
ins(-1), ins(-1),
active(false), active(false),
insChanged(true), insChanged(true),
@ -43,7 +45,6 @@ class DivPlatformLynx: public DivDispatch {
keyOn(false), keyOn(false),
keyOff(false), keyOff(false),
inPorta(false), inPorta(false),
resetLFSR(false),
vol(127), vol(127),
outVol(127) {} outVol(127) {}
}; };
@ -66,6 +67,7 @@ class DivPlatformLynx: public DivDispatch {
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName( unsigned char effect );
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformLynx(); ~DivPlatformLynx();

View File

@ -218,6 +218,13 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
return false; return false;
} }
break; break;
case DIV_SYSTEM_LYNX:
if (effect>=0x30 && effect<0x40) {
int value = ((int)(effect&0x0f)<<8)|effectVal;
dispatchCmd(DivCommand(DIV_CMD_LYNX_LFSR_LOAD,ch,value));
break;
}
return false;
default: default:
return false; return false;
} }

View File

@ -262,11 +262,16 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
break; break;
case DIV_SYSTEM_LYNX: case DIV_SYSTEM_LYNX:
w->writeC(0x4e); w->writeC(0x4e);
w->writeC(0x40); w->writeC(0x44);
w->writeC(0xff); //panning w->writeC(0xff); //stereo attenuation select
w->writeC(0x4e); w->writeC(0x4e);
w->writeC(0x50); w->writeC(0x50);
w->writeC(0x00); //stereo w->writeC(0x00); //stereo channel disable
for (int i=0; i<4; i++) { //stereo attenuation value
w->writeC(0x4e);
w->writeC(0x40+i);
w->writeC(0xff);
}
break; break;
default: default:
break; break;

View File

@ -4602,6 +4602,7 @@ bool FurnaceGUI::loop() {
sysChangeOption(i,DIV_SYSTEM_TIA); sysChangeOption(i,DIV_SYSTEM_TIA);
sysChangeOption(i,DIV_SYSTEM_SAA1099); sysChangeOption(i,DIV_SYSTEM_SAA1099);
sysChangeOption(i,DIV_SYSTEM_AY8930); sysChangeOption(i,DIV_SYSTEM_AY8930);
sysChangeOption(i,DIV_SYSTEM_LYNX);
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }

View File

@ -461,6 +461,7 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
ImGui::TreePop(); ImGui::TreePop();
} }