From 5f17aa0e0329ce6af1a4b8b9d017ab154deb4023 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 11 May 2022 19:10:12 -0500 Subject: [PATCH 01/42] documentation mistake --- papers/doc/7-systems/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index fe5af1aba..d87cfd204 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -17,6 +17,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Yamaha YM2612 standalone](ym2612.md) - [Yamaha YM2151 standalone](ym2151.md) - [SegaPCM](segapcm.md) +- [Capcom QSound](qsound.md) - [Atari 2600](tia.md) - [Philips SAA1099](saa1099.md) - [Microchip AY8930](ay8930.md) From f5ef1ad472b3fb6dff0e929bfe6be59e4cffe3a3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 02:25:13 -0500 Subject: [PATCH 02/42] more documentation work --- papers/doc/7-systems/ym2203.md | 101 ++++++++++++++++++++++++++++++++ papers/doc/7-systems/ym2608.md | 101 ++++++++++++++++++++++++++++++++ papers/doc/7-systems/ym2610b.md | 6 +- 3 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 papers/doc/7-systems/ym2203.md create mode 100644 papers/doc/7-systems/ym2608.md diff --git a/papers/doc/7-systems/ym2203.md b/papers/doc/7-systems/ym2203.md new file mode 100644 index 000000000..18f051e8d --- /dev/null +++ b/papers/doc/7-systems/ym2203.md @@ -0,0 +1,101 @@ +# Yamaha YM2203 (OPN) + +a cost-reduced version of the YM2151 (OPM). +it only has 3 FM channels instead of 8 and removes the LFO. + +however it does contain an AY/SSG part which provides 3 channels of square wave with noise and envelope. + +this chip was used in the PC-88/PC-98 series of computers, and in some arcade boards. + +several variants of this chip were released as well, with more features. + +# effects + +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `14xx`: set operator 3 level. +- `15xx`: set operator 4 level. +- `16xy`: set multiplier of operator. + - `x` is the operator (1-4). + - `y` is the mutliplier. +- `18xx`: toggle extended channel 3 mode. + - 0 disables it and 1 enables it. + - only in extended channel 3 system. +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. +- `1Cxx`: set attack of operator 3. +- `1Dxx`: set attack of operator 4. +- `20xx`: set SSG channel mode. `xx` may be one of the following: + - `00`: square + - `01`: noise + - `02`: square and noise + - `03`: nothing (apparently) + - `04`: envelope and square + - `05`: envelope and noise + - `06`: envelope and square and noise + - `07`: nothing +- `21xx`: set noise frequency. `xx` is a value between 00 and 1F. +- `22xy`: set envelope mode. + - `x` sets the envelope shape, which may be one of the following: + - `0: \___` decay + - `4: /___` attack once + - `8: \\\\` saw + - `9: \___` decay + - `A: \/\/` inverse obelisco + - `B: \¯¯¯` decay once + - `C: ////` inverse saw + - `D: /¯¯¯` attack + - `E: /\/\` obelisco + - `F: /___` attack once + - if `y` is 1 then the envelope will affect this channel. +- `23xx`: set envelope period low byte. +- `24xx`: set envelope period high byte. +- `25xx`: slide envelope period up. +- `26xx`: slide envelope period down. +- `29xy`: enable SSG auto-envelope mode. + - in this mode the envelope period is set to the channel's notes, multiplied by a fraction. + - `x` is the numerator. + - `y` is the denominator. + - if `x` or `y` are 0 this will disable auto-envelope mode. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. diff --git a/papers/doc/7-systems/ym2608.md b/papers/doc/7-systems/ym2608.md new file mode 100644 index 000000000..51720d1b0 --- /dev/null +++ b/papers/doc/7-systems/ym2608.md @@ -0,0 +1,101 @@ +# Yamaha YM2608 (OPNA) + +like YM2203, but with twice the FM channels, an ADPCM channel and built-in drums ("rhythm")! + +it was one of the available sound chips for the PC-98 series of computers. + +the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-in drums have been replaced with 6 sample channels. + +# effects + +- `10xy`: set LFO parameters. + - `x` toggles the LFO. + - `y` sets its speed. +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `14xx`: set operator 3 level. +- `15xx`: set operator 4 level. +- `16xy`: set multiplier of operator. + - `x` is the operator (1-4). + - `y` is the mutliplier. +- `18xx`: toggle extended channel 3 mode. + - 0 disables it and 1 enables it. + - only in extended channel 3 system. +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. +- `1Cxx`: set attack of operator 3. +- `1Dxx`: set attack of operator 4. +- `20xx`: set SSG channel mode. `xx` may be one of the following: + - `00`: square + - `01`: noise + - `02`: square and noise + - `03`: nothing (apparently) + - `04`: envelope and square + - `05`: envelope and noise + - `06`: envelope and square and noise + - `07`: nothing +- `21xx`: set noise frequency. `xx` is a value between 00 and 1F. +- `22xy`: set envelope mode. + - `x` sets the envelope shape, which may be one of the following: + - `0: \___` decay + - `4: /___` attack once + - `8: \\\\` saw + - `9: \___` decay + - `A: \/\/` inverse obelisco + - `B: \¯¯¯` decay once + - `C: ////` inverse saw + - `D: /¯¯¯` attack + - `E: /\/\` obelisco + - `F: /___` attack once + - if `y` is 1 then the envelope will affect this channel. +- `23xx`: set envelope period low byte. +- `24xx`: set envelope period high byte. +- `25xx`: slide envelope period up. +- `26xx`: slide envelope period down. +- `29xy`: enable SSG auto-envelope mode. + - in this mode the envelope period is set to the channel's notes, multiplied by a fraction. + - `x` is the numerator. + - `y` is the denominator. + - if `x` or `y` are 0 this will disable auto-envelope mode. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md index 17f274559..4133681ff 100644 --- a/papers/doc/7-systems/ym2610b.md +++ b/papers/doc/7-systems/ym2610b.md @@ -16,9 +16,9 @@ it is backward compatible with the original chip. - `16xy`: set multiplier of operator. - `x` is the operator (1-4). - `y` is the mutliplier. -- `18xx`: toggle extended channel 2 mode. +- `18xx`: toggle extended channel 3 mode. - 0 disables it and 1 enables it. - - only in extended channel 2 system. + - only in extended channel 3 system. - `19xx`: set attack of all operators. - `1Axx`: set attack of operator 1. - `1Bxx`: set attack of operator 2. @@ -95,4 +95,4 @@ it is backward compatible with the original chip. - `5Cxx`: set D2R/SR of operator 1. - `5Dxx`: set D2R/SR of operator 2. - `5Exx`: set D2R/SR of operator 3. -- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file +- `5Fxx`: set D2R/SR of operator 4. From 4e2e542c7a2b935964ecffa33b02a2eee491c0f4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 02:25:59 -0500 Subject: [PATCH 03/42] OPN: implement muting and remove panning --- src/engine/platform/ym2203.cpp | 121 ++++++++++++++++----------------- src/engine/platform/ym2203.h | 4 +- 2 files changed, 60 insertions(+), 65 deletions(-) diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 83233e8f5..722dc51bb 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -51,7 +51,6 @@ const char* regCheatSheetYM2203[]={ "SSG_Control_Env", "00D", // FM (Common) "FM_Test", "021", - "FM_LFOFreq", "022", "ClockA1", "024", "ClockA2", "025", "ClockB", "026", @@ -157,9 +156,6 @@ const char* regCheatSheetYM2203[]={ "FM1_FB_ALG", "0B0", "FM2_FB_ALG", "0B1", "FM3_FB_ALG", "0B2", - "FM1_Pan_LFO", "0B4", - "FM2_Pan_LFO", "0B5", - "FM3_Pan_LFO", "0B6", NULL }; @@ -169,9 +165,6 @@ const char** DivPlatformYM2203::getRegisterSheet() { const char* DivPlatformYM2203::getEffectName(unsigned char effect) { switch (effect) { - case 0x10: - return "10xy: Setup LFO (x: enable; y: speed)"; - break; case 0x11: return "11xx: Set feedback (0 to 7)"; break; @@ -313,7 +306,7 @@ void DivPlatformYM2203::acquire(short* bufL, short* bufR, size_t start, size_t l fm->write(0x1,w.val); regPool[w.addr&0xff]=w.val; writes.pop(); - delay=16; + delay=6; } } @@ -363,10 +356,14 @@ void DivPlatformYM2203::tick(bool sysTick) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } } @@ -387,11 +384,6 @@ void DivPlatformYM2203::tick(bool sysTick) { } } - if (chan[i].std.panL.had) { - chan[i].pan=chan[i].std.panL.val&3; - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); - } - if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { chan[i].pitch2+=chan[i].std.pitch.val; @@ -429,14 +421,6 @@ void DivPlatformYM2203::tick(bool sysTick) { chan[i].state.fb=chan[i].std.fb.val; rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); } - if (chan[i].std.fms.had) { - chan[i].state.fms=chan[i].std.fms.val; - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); - } - if (chan[i].std.ams.had) { - chan[i].state.ams=chan[i].std.ams.val; - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); - } for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; @@ -467,10 +451,14 @@ void DivPlatformYM2203::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } if (m.rs.had) { @@ -574,13 +562,17 @@ int DivPlatformYM2203::dispatch(DivCommand c) { for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isOutput[chan[c.chan].state.alg][i]) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); - } + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][i]) { + if (!chan[c.chan].active || chan[c.chan].insChanged) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } + } else { + if (chan[c.chan].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } if (chan[c.chan].insChanged) { @@ -594,7 +586,6 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } if (chan[c.chan].insChanged) { rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); } chan[c.chan].insChanged=false; @@ -631,10 +622,14 @@ int DivPlatformYM2203::dispatch(DivCommand c) { for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][i]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } break; @@ -649,15 +644,6 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } chan[c.chan].ins=c.value; break; - case DIV_CMD_PANNING: { - if (c.value==0 && c.value2==0) { - chan[c.chan].pan=3; - } else { - chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); - } - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); - break; - } case DIV_CMD_PITCH: { chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; @@ -736,10 +722,6 @@ int DivPlatformYM2203::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; } - case DIV_CMD_FM_LFO: { - rWrite(0x22,(c.value&7)|((c.value>>4)<<3)); - break; - } case DIV_CMD_FM_FB: { if (c.chan>2) break; chan[c.chan].state.fb=c.value&7; @@ -759,10 +741,14 @@ int DivPlatformYM2203::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; - if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][c.value]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } break; } @@ -944,8 +930,19 @@ void DivPlatformYM2203::muteChannel(int ch, bool mute) { ay->muteChannel(ch-3,mute); return; } - // FM - rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[ch].state.op[j]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (isOutput[chan[ch].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } } void DivPlatformYM2203::forceIns() { @@ -953,10 +950,14 @@ void DivPlatformYM2203::forceIns() { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); @@ -966,7 +967,6 @@ void DivPlatformYM2203::forceIns() { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; @@ -1039,9 +1039,6 @@ void DivPlatformYM2203::reset() { extMode=false; - // LFO - immWrite(0x22,0x08); - ay->reset(); ay->getRegisterWrites().clear(); ay->flushWrites(); diff --git a/src/engine/platform/ym2203.h b/src/engine/platform/ym2203.h index 1a6dad584..ab527eeaa 100644 --- a/src/engine/platform/ym2203.h +++ b/src/engine/platform/ym2203.h @@ -45,7 +45,6 @@ class DivPlatformYM2203: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; int vol, outVol; int sample; - unsigned char pan; DivMacroInt std; void macroInit(DivInstrument* which) { std.init(which); @@ -75,8 +74,7 @@ class DivPlatformYM2203: public DivDispatch { hardReset(false), vol(0), outVol(15), - sample(-1), - pan(3) {} + sample(-1) {} }; Channel chan[6]; DivDispatchOscBuffer* oscBuf[6]; From cdf4c55f0f5aaa8d8a6be7fc4625bf635c4f26c3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 02:39:46 -0500 Subject: [PATCH 04/42] SCC: fix forceIns (finally) fixes #447 --- src/engine/platform/scc.cpp | 13 ++++++++++++- src/engine/platform/scc.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index 96ec2dfd2..88132b124 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -105,6 +105,11 @@ void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) void DivPlatformSCC::updateWave(int ch) { int dstCh=(!isPlus && ch>=4)?3:ch; + if (ch==3) { + lastUpdated34=3; + } else if (ch==4) { + lastUpdated34=4; + } for (int i=0; i<32; i++) { rWrite(dstCh*32+i,(unsigned char)chan[ch].ws.output[i]-128); } @@ -281,10 +286,15 @@ void DivPlatformSCC::forceIns() { for (int i=0; i<5; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; - if (chan[i].active) { + if (isPlus || i<3) { updateWave(i); } } + if (!isPlus) { + if (lastUpdated34>=3) { + updateWave(lastUpdated34); + } + } } void* DivPlatformSCC::getChanState(int ch) { @@ -318,6 +328,7 @@ void DivPlatformSCC::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } + lastUpdated34=0; } bool DivPlatformSCC::isStereo() { diff --git a/src/engine/platform/scc.h b/src/engine/platform/scc.h index 58059532f..11d6521c7 100644 --- a/src/engine/platform/scc.h +++ b/src/engine/platform/scc.h @@ -57,6 +57,7 @@ class DivPlatformSCC: public DivDispatch { DivDispatchOscBuffer* oscBuf[5]; bool isMuted[5]; unsigned char writeOscBuf; + int lastUpdated34; scc_core* scc; bool isPlus; From 0e03bf6bec5a1dafcac82db9401ebda0d34f7550 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 02:49:09 -0500 Subject: [PATCH 05/42] GUI: fix default vScroll for pitch macro --- src/gui/insEdit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 82b47fb69..cfe544582 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1152,8 +1152,8 @@ void FurnaceGUI::drawMacros(std::vector& macros) { i.macro->vZoom=24; i.macro->vScroll=120-12; } else if (i.macro->name=="pitch") { - i.macro->vZoom=64; - i.macro->vScroll=1024-32; + i.macro->vZoom=128; + i.macro->vScroll=2048-64; } else { i.macro->vZoom=i.max-i.min; i.macro->vScroll=0; From 76adccc0fc5a8cc1f2f254e0cea1a3a237e76653 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 03:01:35 -0500 Subject: [PATCH 06/42] GUI: even more improvements to macro editor --- src/gui/insEdit.cpp | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index cfe544582..567c0f3be 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1192,18 +1192,24 @@ void FurnaceGUI::drawMacros(std::vector& macros) { processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); } if (i.macro->open) { - if (ImGui::IsItemHovered() && ctrlWheeling) { - if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { - i.macro->vZoom+=wheelY; - if (i.macro->vZoom<1) i.macro->vZoom=1; - if (i.macro->vZoom>(i.max-i.min)) i.macro->vZoom=i.max-i.min; - if ((i.macro->vScroll+i.macro->vZoom)>(i.max-i.min)) { - i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; + if (ImGui::IsItemHovered()) { + if (ctrlWheeling) { + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { + i.macro->vZoom+=wheelY*(1+(i.macro->vZoom>>4)); + if (i.macro->vZoom<1) i.macro->vZoom=1; + if (i.macro->vZoom>(i.max-i.min)) i.macro->vZoom=i.max-i.min; + if ((i.macro->vScroll+i.macro->vZoom)>(i.max-i.min)) { + i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; + } + } else { + macroPointSize+=wheelY; + if (macroPointSize<1) macroPointSize=1; + if (macroPointSize>256) macroPointSize=256; } - } else { - macroPointSize+=wheelY; - if (macroPointSize<1) macroPointSize=1; - if (macroPointSize>256) macroPointSize=256; + } else if ((ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) && wheelY!=0) { + i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4)); + if (i.macro->vScroll<0) i.macro->vScroll=0; + if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; } } @@ -1214,12 +1220,16 @@ void FurnaceGUI::drawMacros(std::vector& macros) { ImS64 contentsV=(i.max-i.min); ImGui::SameLine(0.0f); + ImGui::SetCursorPosX(ImGui::GetCursorPosX()-ImGui::GetStyle().ItemSpacing.x); ImRect scrollbarPos=ImRect(ImGui::GetCursorScreenPos(),ImGui::GetCursorScreenPos()); - scrollbarPos.Min.x-=ImGui::GetStyle().ItemSpacing.x; scrollbarPos.Max.x+=ImGui::GetStyle().ScrollbarSize; - scrollbarPos.Max.x-=ImGui::GetStyle().ItemSpacing.x; scrollbarPos.Max.y+=i.height*dpiScale; ImGui::Dummy(ImVec2(ImGui::GetStyle().ScrollbarSize,i.height*dpiScale)); + if (ImGui::IsItemHovered() && ctrlWheeling) { + i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4)); + if (i.macro->vScroll<0) i.macro->vScroll=0; + if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; + } if (ImGui::ScrollbarEx(scrollbarPos,ImGui::GetID("IMacroVScroll"),ImGuiAxis_Y,&scrollV,availV,contentsV,0)) { i.macro->vScroll=(i.max-i.min-i.macro->vZoom)-scrollV; From 613b538fb9d13136a94cb1b744f32c85f7dac053 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 03:29:34 -0500 Subject: [PATCH 07/42] GUI: add setting to bring old macro vSlider back --- src/gui/gui.h | 2 ++ src/gui/insEdit.cpp | 47 ++++++++++++++++++++++++++++---------------- src/gui/settings.cpp | 10 +++++++++- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index e02a065c7..0501e2422 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -890,6 +890,7 @@ class FurnaceGUI { int hiddenSystems; int horizontalDataView; int noMultiSystem; + int oldMacroVSlider; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -974,6 +975,7 @@ class FurnaceGUI { hiddenSystems(0), horizontalDataView(0), noMultiSystem(0), + oldMacroVSlider(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 567c0f3be..0080f1da2 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1077,7 +1077,7 @@ void FurnaceGUI::drawMacros(std::vector& macros) { float loopIndicator[256]; int index=0; - float reservedSpace=ImGui::GetStyle().ScrollbarSize; + float reservedSpace=(settings.oldMacroVSlider)?(20.0f*dpiScale+ImGui::GetStyle().ItemSpacing.x):ImGui::GetStyle().ScrollbarSize; if (ImGui::BeginTable("MacroSpace",2)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); @@ -1215,24 +1215,37 @@ void FurnaceGUI::drawMacros(std::vector& macros) { // slider if (!i.isBitfield) { - ImS64 scrollV=(i.max-i.min-i.macro->vZoom)-i.macro->vScroll; - ImS64 availV=i.macro->vZoom; - ImS64 contentsV=(i.max-i.min); + if (settings.oldMacroVSlider) { + ImGui::SameLine(0.0f); + if (ImGui::VSliderInt("IMacroVScroll",ImVec2(20.0f*dpiScale,i.height*dpiScale),&i.macro->vScroll,0,(i.max-i.min)-i.macro->vZoom,"")) { + if (i.macro->vScroll<0) i.macro->vScroll=0; + if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; + } + if (ImGui::IsItemHovered() && ctrlWheeling) { + i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4)); + if (i.macro->vScroll<0) i.macro->vScroll=0; + if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; + } + } else { + ImS64 scrollV=(i.max-i.min-i.macro->vZoom)-i.macro->vScroll; + ImS64 availV=i.macro->vZoom; + ImS64 contentsV=(i.max-i.min); - ImGui::SameLine(0.0f); - ImGui::SetCursorPosX(ImGui::GetCursorPosX()-ImGui::GetStyle().ItemSpacing.x); - ImRect scrollbarPos=ImRect(ImGui::GetCursorScreenPos(),ImGui::GetCursorScreenPos()); - scrollbarPos.Max.x+=ImGui::GetStyle().ScrollbarSize; - scrollbarPos.Max.y+=i.height*dpiScale; - ImGui::Dummy(ImVec2(ImGui::GetStyle().ScrollbarSize,i.height*dpiScale)); - if (ImGui::IsItemHovered() && ctrlWheeling) { - i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4)); - if (i.macro->vScroll<0) i.macro->vScroll=0; - if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; - } + ImGui::SameLine(0.0f); + ImGui::SetCursorPosX(ImGui::GetCursorPosX()-ImGui::GetStyle().ItemSpacing.x); + ImRect scrollbarPos=ImRect(ImGui::GetCursorScreenPos(),ImGui::GetCursorScreenPos()); + scrollbarPos.Max.x+=ImGui::GetStyle().ScrollbarSize; + scrollbarPos.Max.y+=i.height*dpiScale; + ImGui::Dummy(ImVec2(ImGui::GetStyle().ScrollbarSize,i.height*dpiScale)); + if (ImGui::IsItemHovered() && ctrlWheeling) { + i.macro->vScroll+=wheelY*(1+(i.macro->vZoom>>4)); + if (i.macro->vScroll<0) i.macro->vScroll=0; + if (i.macro->vScroll>((i.max-i.min)-i.macro->vZoom)) i.macro->vScroll=(i.max-i.min)-i.macro->vZoom; + } - if (ImGui::ScrollbarEx(scrollbarPos,ImGui::GetID("IMacroVScroll"),ImGuiAxis_Y,&scrollV,availV,contentsV,0)) { - i.macro->vScroll=(i.max-i.min-i.macro->vZoom)-scrollV; + if (ImGui::ScrollbarEx(scrollbarPos,ImGui::GetID("IMacroVScroll"),ImGuiAxis_Y,&scrollV,availV,contentsV,0)) { + i.macro->vScroll=(i.max-i.min-i.macro->vZoom)-scrollV; + } } } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 1c22755a8..e6ba74a29 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1147,6 +1147,11 @@ void FurnaceGUI::drawSettings() { settings.sampleLayout=sampleLayoutB; } + bool oldMacroVSliderB=settings.oldMacroVSlider; + if (ImGui::Checkbox("Use classic macro editor vertical slider",&oldMacroVSliderB)) { + settings.oldMacroVSlider=oldMacroVSliderB; + } + bool roundedWindowsB=settings.roundedWindows; if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { settings.roundedWindows=roundedWindowsB; @@ -1849,6 +1854,7 @@ void FurnaceGUI::syncSettings() { settings.hiddenSystems=e->getConfInt("hiddenSystems",0); settings.horizontalDataView=e->getConfInt("horizontalDataView",0); settings.noMultiSystem=e->getConfInt("noMultiSystem",0); + settings.oldMacroVSlider=e->getConfInt("oldMacroVSlider",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1920,7 +1926,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.hiddenSystems,0,1); clampSetting(settings.horizontalDataView,0,1); - clampSetting(settings.noMultiSystem,0,1) + clampSetting(settings.noMultiSystem,0,1); + clampSetting(settings.oldMacroVSlider,0,1); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); if (settings.initialSys.size()<4) { @@ -2034,6 +2041,7 @@ void FurnaceGUI::commitSettings() { e->setConf("initialSys",e->encodeSysDesc(settings.initialSys)); e->setConf("horizontalDataView",settings.horizontalDataView); e->setConf("noMultiSystem",settings.noMultiSystem); + e->setConf("oldMacroVSlider",settings.oldMacroVSlider); // colors for (int i=0; i Date: Wed, 11 May 2022 23:39:36 +0200 Subject: [PATCH 08/42] Add emulation settings for YRW801, TG100 and MU5 sample ROMs. --- src/engine/engine.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++ src/engine/engine.h | 14 +++++++- src/gui/gui.cpp | 27 +++++++++++++++ src/gui/gui.h | 13 ++++++-- src/gui/settings.cpp | 43 ++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 364b0d135..157aea7fe 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -491,6 +491,77 @@ void DivEngine::notifyWaveChange(int wave) { BUSY_END; } +int DivEngine::loadSampleRom(String path, ssize_t expectedSize, unsigned char*& ret) { + ret = NULL; + if (path.empty()) { + return 0; + } + logI("loading ROM %s...",path); + FILE* f=ps_fopen(path.c_str(),"rb"); + if (f==NULL) { + logE("error: %s",strerror(errno)); + lastError=strerror(errno); + return -1; + } + if (fseek(f,0,SEEK_END)<0) { + logE("size error: %s",strerror(errno)); + lastError=fmt::sprintf("on seek: %s",strerror(errno)); + fclose(f); + return -1; + } + ssize_t len=ftell(f); + if (len==(SIZE_MAX>>1)) { + logE("could not get file length: %s",strerror(errno)); + lastError=fmt::sprintf("on pre tell: %s",strerror(errno)); + fclose(f); + return -1; + } + if (len<1) { + if (len==0) { + logE("that file is empty!"); + lastError="file is empty"; + } else { + logE("tell error: %s",strerror(errno)); + lastError=fmt::sprintf("on tell: %s",strerror(errno)); + } + fclose(f); + return -1; + } + if (len!=expectedSize) { + logE("ROM size mismatch, expected: %d bytes, was: %d bytes", expectedSize, len); + lastError=fmt::sprintf("ROM size mismatch, expected: %d bytes, was: %d", expectedSize, len); + return -1; + } + if (fseek(f,0,SEEK_SET)<0) { + logE("size error: %s",strerror(errno)); + lastError=fmt::sprintf("on get size: %s",strerror(errno)); + fclose(f); + return -1; + } + unsigned char* file=new unsigned char[len]; + if (fread(file,1,(size_t)len,f)!=(size_t)len) { + logE("read error: %s",strerror(errno)); + lastError=fmt::sprintf("on read: %s",strerror(errno)); + fclose(f); + delete[] file; + return -1; + } + fclose(f); + ret = file; + return 0; +} + +int DivEngine::loadSampleRoms() { + delete[] yrw801Rom; + delete[] tg100Rom; + delete[] mu5Rom; + int error = 0; + error += loadSampleRom(getConfString("yrw801Path",""), 0x200000, yrw801Rom); + error += loadSampleRom(getConfString("tg100Path",""), 0x200000, tg100Rom); + error += loadSampleRom(getConfString("mu5Path",""), 0x200000, mu5Rom); + return error; +} + void DivEngine::renderSamplesP() { BUSY_BEGIN; renderSamples(); @@ -2662,6 +2733,8 @@ bool DivEngine::init() { loadConf(); + loadSampleRoms(); + // set default system preset if (!hasLoadedSomething) { logD("setting default preset"); @@ -2735,5 +2808,8 @@ bool DivEngine::quit() { active=false; delete[] oscBuf[0]; delete[] oscBuf[1]; + delete[] yrw801Rom; + delete[] tg100Rom; + delete[] mu5Rom; return true; } diff --git a/src/engine/engine.h b/src/engine/engine.h index e9a5db2e0..701fc70fd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -397,6 +397,8 @@ class DivEngine { void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); + int loadSampleRom(String path, ssize_t expectedSize, unsigned char*& ret); + bool initAudioBackend(); bool deinitAudioBackend(); @@ -783,6 +785,9 @@ class DivEngine { // get register cheatsheet const char** getRegisterSheet(int sys); + // load sample ROMs + int loadSampleRoms(); + // UNSAFE render samples - only execute when locked void renderSamples(); @@ -853,6 +858,10 @@ class DivEngine { // terminate the engine. bool quit(); + unsigned char* yrw801Rom; + unsigned char* tg100Rom; + unsigned char* mu5Rom; + DivEngine(): output(NULL), exportThread(NULL), @@ -927,7 +936,10 @@ class DivEngine { oscReadPos(0), oscWritePos(0), tickMult(1), - processTime(0) { + processTime(0), + yrw801Rom(NULL), + tg100Rom(NULL), + mu5Rom(NULL) { memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 700e55d09..17f04a9fd 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1501,6 +1501,19 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { dpiScale ); break; + case GUI_FILE_YRW801_ROM_OPEN: + case GUI_FILE_TG100_ROM_OPEN: + case GUI_FILE_MU5_ROM_OPEN: + if (!dirExists(workingDirSample)) workingDirSample=getHomeDir(); + hasOpened=fileDialog->openLoad( + "Load ROM", + {"compatible files", "*.rom *.bin", + "all files", ".*"}, + "compatible files{.rom,.bin},.*", + workingDirRom, + dpiScale + ); + break; } if (hasOpened) curFileDialog=type; //ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard; @@ -2983,6 +2996,11 @@ bool FurnaceGUI::loop() { case GUI_FILE_EXPORT_LAYOUT: workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR; break; + case GUI_FILE_YRW801_ROM_OPEN: + case GUI_FILE_TG100_ROM_OPEN: + case GUI_FILE_MU5_ROM_OPEN: + workingDirRom=fileDialog->getPath()+DIR_SEPARATOR_STR; + break; } if (fileDialog->accepted()) { fileName=fileDialog->getFileName(); @@ -3196,6 +3214,15 @@ bool FurnaceGUI::loop() { case GUI_FILE_EXPORT_LAYOUT: exportLayout(copyOfName); break; + case GUI_FILE_YRW801_ROM_OPEN: + settings.yrw801Path=copyOfName; + break; + case GUI_FILE_TG100_ROM_OPEN: + settings.tg100Path=copyOfName; + break; + case GUI_FILE_MU5_ROM_OPEN: + settings.mu5Path=copyOfName; + break; } curFileDialog=GUI_FILE_OPEN; } diff --git a/src/gui/gui.h b/src/gui/gui.h index cfbf90e3f..7bf0095ea 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -257,7 +257,10 @@ enum FurnaceGUIFileDialogs { GUI_FILE_IMPORT_LAYOUT, GUI_FILE_EXPORT_COLORS, GUI_FILE_EXPORT_KEYBINDS, - GUI_FILE_EXPORT_LAYOUT + GUI_FILE_EXPORT_LAYOUT, + GUI_FILE_YRW801_ROM_OPEN, + GUI_FILE_TG100_ROM_OPEN, + GUI_FILE_MU5_ROM_OPEN }; enum FurnaceGUIWarnings { @@ -734,7 +737,7 @@ class FurnaceGUI { bool updateSampleTex; String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile; - String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout; + String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout, workingDirRom; String mmlString[17]; String mmlStringW; @@ -792,6 +795,9 @@ class FurnaceGUI { int saaCore; int nesCore; int fdsCore; + String yrw801Path; + String tg100Path; + String mu5Path; int mainFont; int patFont; int audioRate; @@ -879,6 +885,9 @@ class FurnaceGUI { saaCore(1), nesCore(0), fdsCore(0), + yrw801Path(""), + tg100Path(""), + mu5Path(""), mainFont(0), patFont(0), audioRate(44100), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 87ed1fa1f..e139bfcde 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -887,6 +887,33 @@ void FurnaceGUI::drawSettings() { ImGui::Text("FDS core"); ImGui::SameLine(); ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + + ImGui::Separator(); + ImGui::Text("Sample ROMs:"); + + ImGui::Text("OPL4 YRW801 path"); + ImGui::SameLine(); + ImGui::InputText("##YRW801Path",&settings.yrw801Path); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##YRW801Load")) { + openFileDialog(GUI_FILE_YRW801_ROM_OPEN); + } + + ImGui::Text("MultiPCM TG100 path"); + ImGui::SameLine(); + ImGui::InputText("##TG100Path",&settings.tg100Path); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##TG100Load")) { + openFileDialog(GUI_FILE_TG100_ROM_OPEN); + } + + ImGui::Text("MultiPCM MU5 path"); + ImGui::SameLine(); + ImGui::InputText("##MU5Path",&settings.mu5Path); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##MU5Load")) { + openFileDialog(GUI_FILE_MU5_ROM_OPEN); + } } ImGui::EndChild(); ImGui::EndTabItem(); @@ -1789,6 +1816,9 @@ void FurnaceGUI::syncSettings() { settings.saaCore=e->getConfInt("saaCore",1); settings.nesCore=e->getConfInt("nesCore",0); settings.fdsCore=e->getConfInt("fdsCore",0); + settings.yrw801Path=e->getConfString("yrw801Path",""); + settings.tg100Path=e->getConfString("tg100Path",""); + settings.mu5Path=e->getConfString("mu5Path",""); settings.mainFont=e->getConfInt("mainFont",0); settings.patFont=e->getConfInt("patFont",0); settings.mainFontPath=e->getConfString("mainFontPath",""); @@ -1960,6 +1990,10 @@ void FurnaceGUI::syncSettings() { } void FurnaceGUI::commitSettings() { + bool sampleRomsChanged = settings.yrw801Path!=e->getConfString("yrw801Path","") || + settings.tg100Path!=e->getConfString("tg100Path","") || + settings.mu5Path!=e->getConfString("mu5Path",""); + e->setConf("mainFontSize",settings.mainFontSize); e->setConf("patFontSize",settings.patFontSize); e->setConf("iconSize",settings.iconSize); @@ -1975,6 +2009,9 @@ void FurnaceGUI::commitSettings() { e->setConf("saaCore",settings.saaCore); e->setConf("nesCore",settings.nesCore); e->setConf("fdsCore",settings.fdsCore); + e->setConf("yrw801Path",settings.yrw801Path); + e->setConf("tg100Path",settings.tg100Path); + e->setConf("mu5Path",settings.mu5Path); e->setConf("mainFont",settings.mainFont); e->setConf("patFont",settings.patFont); e->setConf("mainFontPath",settings.mainFontPath); @@ -2063,6 +2100,12 @@ void FurnaceGUI::commitSettings() { e->saveConf(); + if (sampleRomsChanged) { + if (e->loadSampleRoms()) { + showError(e->getLastError()); + } + } + if (!e->switchMaster()) { showError("could not initialize audio!"); } From 3c3d75c60536219bb5d58a3e9175691ccbe4b24e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 14:21:58 -0500 Subject: [PATCH 09/42] add SCC documentation --- papers/doc/7-systems/README.md | 1 + papers/doc/7-systems/scc.md | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 papers/doc/7-systems/scc.md diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index d87cfd204..fcf2e8860 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -29,6 +29,7 @@ this is a list of systems that Furnace supports, including each system's effects - [Yamaha OPL](opl.md) - [PC Speaker](pcspkr.md) - [Commodore PET](pet.md) +- [Konami SCC](scc.md) - [Commodore VIC-20](vic20.md) - [Konami VRC6](vrc6.md) - [Famicom Disk System](fds.md) diff --git a/papers/doc/7-systems/scc.md b/papers/doc/7-systems/scc.md new file mode 100644 index 000000000..fb69a9cb8 --- /dev/null +++ b/papers/doc/7-systems/scc.md @@ -0,0 +1,11 @@ +# Konami SCC/SCC+ + +the Sound Creative Chip (SCC) adds 5 channels of wavetable to your MSX! +it was used in (of course) several Konami games, which had better audio quality due to the extra channels provided by this chip (poor AY since nobody used the envelope for bass). + +the only problem? the waveform of the fourth channel is shared with the fifth one due to not enough memory in the chip! +the SCC+ fixes this issue though (while being compatible with SCC games). + +# effects + +- `10xx`: change wave. From 7d7740d96f9082928572d4f8c13bde6e86a8d0cd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 22:08:25 -0500 Subject: [PATCH 10/42] ... #452 cherry-picking --- papers/doc/7-systems/ym2203.md | 4 ++-- papers/doc/7-systems/ym2608.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/papers/doc/7-systems/ym2203.md b/papers/doc/7-systems/ym2203.md index 18f051e8d..6b0bbf8d9 100644 --- a/papers/doc/7-systems/ym2203.md +++ b/papers/doc/7-systems/ym2203.md @@ -1,11 +1,11 @@ # Yamaha YM2203 (OPN) a cost-reduced version of the YM2151 (OPM). -it only has 3 FM channels instead of 8 and removes the LFO. +it only has 3 FM channels instead of 8 and removes stereo, the LFO and DT2 (coarse detune). however it does contain an AY/SSG part which provides 3 channels of square wave with noise and envelope. -this chip was used in the PC-88/PC-98 series of computers, and in some arcade boards. +this chip was used in the NEC PC-88/PC-98 series of computers, the Fujitsu FM-7AV and in some arcade boards. several variants of this chip were released as well, with more features. diff --git a/papers/doc/7-systems/ym2608.md b/papers/doc/7-systems/ym2608.md index 51720d1b0..736258a38 100644 --- a/papers/doc/7-systems/ym2608.md +++ b/papers/doc/7-systems/ym2608.md @@ -1,8 +1,8 @@ # Yamaha YM2608 (OPNA) -like YM2203, but with twice the FM channels, an ADPCM channel and built-in drums ("rhythm")! +like YM2203, but with twice the FM channels, stereo, an ADPCM channel and built-in drums ("rhythm")! -it was one of the available sound chips for the PC-98 series of computers. +it was one of the available sound chips for the NEC PC-88VA and PC-98 series of computers. the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-in drums have been replaced with 6 sample channels. From 1b4256bb2815b0a0e9b81b3deda6aaf08ad202da Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 12 May 2022 22:15:03 -0500 Subject: [PATCH 11/42] spelling change: Rom -> ROM - PLEASE READ @Grauw: please update your MultiPCM code. i know it's just a trivial change I made, but in my opinion "ROM" indicates it's an acronym... --- src/engine/engine.cpp | 33 +++++++++++++++++++++------------ src/engine/engine.h | 16 ++++++++-------- src/gui/gui.cpp | 4 ++-- src/gui/gui.h | 2 +- src/gui/settings.cpp | 6 +++--- 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index cc1428e15..99e41313c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -491,7 +491,7 @@ void DivEngine::notifyWaveChange(int wave) { BUSY_END; } -int DivEngine::loadSampleRom(String path, ssize_t expectedSize, unsigned char*& ret) { +int DivEngine::loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret) { ret = NULL; if (path.empty()) { return 0; @@ -551,14 +551,23 @@ int DivEngine::loadSampleRom(String path, ssize_t expectedSize, unsigned char*& return 0; } -int DivEngine::loadSampleRoms() { - delete[] yrw801Rom; - delete[] tg100Rom; - delete[] mu5Rom; +int DivEngine::loadSampleROMs() { + if (yrw801ROM!=NULL) { + delete[] yrw801ROM; + yrw801ROM=NULL; + } + if (tg100ROM!=NULL) { + delete[] tg100ROM; + tg100ROM=NULL; + } + if (mu5ROM!=NULL) { + delete[] mu5ROM; + mu5ROM=NULL; + } int error = 0; - error += loadSampleRom(getConfString("yrw801Path",""), 0x200000, yrw801Rom); - error += loadSampleRom(getConfString("tg100Path",""), 0x200000, tg100Rom); - error += loadSampleRom(getConfString("mu5Path",""), 0x200000, mu5Rom); + error += loadSampleROM(getConfString("yrw801Path",""), 0x200000, yrw801ROM); + error += loadSampleROM(getConfString("tg100Path",""), 0x200000, tg100ROM); + error += loadSampleROM(getConfString("mu5Path",""), 0x200000, mu5ROM); return error; } @@ -2779,7 +2788,7 @@ bool DivEngine::init() { loadConf(); - loadSampleRoms(); + loadSampleROMs(); // set default system preset if (!hasLoadedSomething) { @@ -2854,8 +2863,8 @@ bool DivEngine::quit() { active=false; delete[] oscBuf[0]; delete[] oscBuf[1]; - delete[] yrw801Rom; - delete[] tg100Rom; - delete[] mu5Rom; + if (yrw801ROM!=NULL) delete[] yrw801ROM; + if (tg100ROM!=NULL) delete[] tg100ROM; + if (mu5ROM!=NULL) delete[] mu5ROM; return true; } diff --git a/src/engine/engine.h b/src/engine/engine.h index 3338b4a54..8a95e935f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -397,7 +397,7 @@ class DivEngine { void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); - int loadSampleRom(String path, ssize_t expectedSize, unsigned char*& ret); + int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret); bool initAudioBackend(); bool deinitAudioBackend(); @@ -786,7 +786,7 @@ class DivEngine { const char** getRegisterSheet(int sys); // load sample ROMs - int loadSampleRoms(); + int loadSampleROMs(); // UNSAFE render samples - only execute when locked void renderSamples(); @@ -861,9 +861,9 @@ class DivEngine { // terminate the engine. bool quit(); - unsigned char* yrw801Rom; - unsigned char* tg100Rom; - unsigned char* mu5Rom; + unsigned char* yrw801ROM; + unsigned char* tg100ROM; + unsigned char* mu5ROM; DivEngine(): output(NULL), @@ -940,9 +940,9 @@ class DivEngine { oscWritePos(0), tickMult(1), processTime(0), - yrw801Rom(NULL), - tg100Rom(NULL), - mu5Rom(NULL) { + yrw801ROM(NULL), + tg100ROM(NULL), + mu5ROM(NULL) { memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4d237c1d5..abeb569d4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1516,7 +1516,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { {"compatible files", "*.rom *.bin", "all files", ".*"}, "compatible files{.rom,.bin},.*", - workingDirRom, + workingDirROM, dpiScale ); break; @@ -3040,7 +3040,7 @@ bool FurnaceGUI::loop() { case GUI_FILE_YRW801_ROM_OPEN: case GUI_FILE_TG100_ROM_OPEN: case GUI_FILE_MU5_ROM_OPEN: - workingDirRom=fileDialog->getPath()+DIR_SEPARATOR_STR; + workingDirROM=fileDialog->getPath()+DIR_SEPARATOR_STR; break; } if (fileDialog->accepted()) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 03a3d7e32..61b2d912f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -768,7 +768,7 @@ class FurnaceGUI { bool updateSampleTex; String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile; - String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout, workingDirRom; + String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout, workingDirROM; String mmlString[17]; String mmlStringW; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index c38e1dc89..f44c2aa92 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1990,7 +1990,7 @@ void FurnaceGUI::syncSettings() { } void FurnaceGUI::commitSettings() { - bool sampleRomsChanged = settings.yrw801Path!=e->getConfString("yrw801Path","") || + bool sampleROMsChanged = settings.yrw801Path!=e->getConfString("yrw801Path","") || settings.tg100Path!=e->getConfString("tg100Path","") || settings.mu5Path!=e->getConfString("mu5Path",""); @@ -2100,8 +2100,8 @@ void FurnaceGUI::commitSettings() { e->saveConf(); - if (sampleRomsChanged) { - if (e->loadSampleRoms()) { + if (sampleROMsChanged) { + if (e->loadSampleROMs()) { showError(e->getLastError()); } } From f2db15b2ce6720e054eeca0a7828cbd40662f638 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 02:52:43 -0500 Subject: [PATCH 12/42] add OPNA chip (work in progress) --- CMakeLists.txt | 3 + TODO.md | 4 +- src/engine/dispatchContainer.cpp | 4 + src/engine/platform/sound/rss.h | 543 ++++++++++++++++++++++++ src/engine/platform/ym2203.cpp | 2 + src/engine/platform/ym2608.cpp | 241 ++++++----- src/engine/platform/ym2608.h | 21 +- src/engine/platform/ym2608Interface.cpp | 42 ++ 8 files changed, 744 insertions(+), 116 deletions(-) create mode 100644 src/engine/platform/sound/rss.h create mode 100644 src/engine/platform/ym2608Interface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9e9c6a4..4f912faea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,6 +318,8 @@ src/engine/platform/sound/vrcvi/vrcvi.cpp src/engine/platform/sound/scc/scc.cpp +src/engine/platform/ym2608Interface.cpp + src/engine/platform/ym2610Interface.cpp src/engine/blip_buf.c @@ -352,6 +354,7 @@ src/engine/platform/c64.cpp src/engine/platform/arcade.cpp src/engine/platform/tx81z.cpp src/engine/platform/ym2203.cpp +src/engine/platform/ym2608.cpp src/engine/platform/ym2610.cpp src/engine/platform/ym2610ext.cpp src/engine/platform/ym2610b.cpp diff --git a/TODO.md b/TODO.md index fc526b29e..52eae1713 100644 --- a/TODO.md +++ b/TODO.md @@ -5,8 +5,6 @@ - input pad - settings - RF5C68 system -- OPN system -- OPNA system - ZX beeper system - Y8950 system - maybe YMU759 ADPCM channel @@ -27,4 +25,4 @@ - Apply button in settings - find and replace - finish wave synth -- add mono/poly note preview button \ No newline at end of file +- add mono/poly note preview button diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 7595d14bb..46efffc31 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -30,6 +30,7 @@ #include "platform/arcade.h" #include "platform/tx81z.h" #include "platform/ym2203.h" +#include "platform/ym2608.h" #include "platform/ym2610.h" #include "platform/ym2610ext.h" #include "platform/ym2610b.h" @@ -238,6 +239,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_OPN: dispatch=new DivPlatformYM2203; break; + case DIV_SYSTEM_PC98: + dispatch=new DivPlatformYM2608; + break; case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_VRC7: diff --git a/src/engine/platform/sound/rss.h b/src/engine/platform/sound/rss.h new file mode 100644 index 000000000..1fcd6ada3 --- /dev/null +++ b/src/engine/platform/sound/rss.h @@ -0,0 +1,543 @@ +/* + This data is derived from the chip's output - internal ROM can't be read. + It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal. +*/ + +const unsigned char YM2608_ADPCM_ROM[0x2000] = { + +/* Source: 01BD.ROM */ +/* Length: 448 / 0x000001C0 */ + +0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54, +0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97, +0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0, +0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1, +0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24, +0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99, +0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99, +0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09, +0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8, +0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39, +0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9, +0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A, +0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40, +0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00, +0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12, +0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18, +0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52, +0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02, +0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B, +0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC, +0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A, +0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98, +0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A, +0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99, +0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99, +0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11, +0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31, +0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + +/* Source: 02SD.ROM */ +/* Length: 640 / 0x00000280 */ + +0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1, +0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41, +0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03, +0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18, +0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02, +0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92, +0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28, +0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62, +0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E, +0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09, +0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB, +0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83, +0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85, +0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21, +0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0, +0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80, +0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21, +0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8, +0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F, +0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A, +0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12, +0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36, +0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80, +0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90, +0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99, +0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03, +0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89, +0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18, +0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27, +0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9, +0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39, +0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23, +0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1, +0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3, +0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9, +0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71, +0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A, +0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88, +0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C, +0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10, + +/* Source: 04TOP.ROM */ +/* Length: 5952 / 0x00001740 */ + +0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C, +0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00, +0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E, +0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C, +0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38, +0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81, +0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A, +0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81, +0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4, +0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8, +0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10, +0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29, +0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98, +0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59, +0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3, +0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28, +0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B, +0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B, +0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11, +0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0, +0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90, +0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00, +0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8, +0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29, +0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F, +0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12, +0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8, +0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4, +0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82, +0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11, +0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09, +0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A, +0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3, +0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85, +0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88, +0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80, +0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C, +0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10, +0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81, +0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81, +0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8, +0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A, +0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79, +0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49, +0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA, +0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99, +0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24, +0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7, +0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8, +0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0, +0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15, +0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18, +0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B, +0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C, +0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B, +0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7, +0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14, +0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02, +0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98, +0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88, +0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01, +0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2, +0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D, +0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19, +0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28, +0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21, +0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A, +0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2, +0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87, +0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11, +0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C, +0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A, +0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0, +0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1, +0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11, +0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90, +0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00, +0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2, +0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3, +0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82, +0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00, +0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B, +0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28, +0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79, +0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90, +0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1, +0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05, +0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10, +0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19, +0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D, +0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5, +0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90, +0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49, +0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C, +0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C, +0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0, +0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3, +0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4, +0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4, +0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9, +0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01, +0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88, +0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12, +0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D, +0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02, +0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09, +0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89, +0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A, +0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2, +0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49, +0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50, +0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80, +0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C, +0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C, +0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13, +0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01, +0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18, +0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C, +0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1, +0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3, +0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B, +0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E, +0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90, +0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28, +0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28, +0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E, +0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91, +0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00, +0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71, +0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20, +0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8, +0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03, +0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7, +0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C, +0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09, +0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12, +0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2, +0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6, +0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02, +0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B, +0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC, +0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0, +0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00, +0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A, +0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2, +0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3, +0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F, +0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A, +0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0, +0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84, +0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10, +0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21, +0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F, +0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B, +0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24, +0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3, +0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00, +0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93, +0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10, +0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30, +0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0, +0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7, +0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1, +0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19, +0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B, +0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99, +0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A, +0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C, +0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03, +0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1, +0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A, +0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19, +0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8, +0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11, +0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A, +0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59, +0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F, +0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C, +0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80, +0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5, +0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08, +0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29, +0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1, +0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28, +0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D, +0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A, +0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D, +0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48, +0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A, +0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3, +0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1, +0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18, +0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C, +0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A, +0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19, +0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91, +0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04, +0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29, +0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10, +0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1, +0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0, +0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84, +0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95, +0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11, +0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B, +0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19, +0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10, +0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1, +0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17, +0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A, +0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08, +0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C, +0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1, +0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2, +0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09, +0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20, +0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A, +0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D, +0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82, +0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1, +0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2, +0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99, +0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18, +0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1, +0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1, +0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01, +0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40, +0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A, +0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C, +0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83, +0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12, +0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09, +0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68, +0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92, +0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A, +0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80, +0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82, +0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80, +0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B, +0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B, +0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18, +0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91, +0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1, +0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6, +0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B, +0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89, +0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90, +0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A, +0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01, +0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98, +0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29, +0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D, +0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92, +0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93, +0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88, +0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79, +0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20, +0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59, +0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9, +0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8, +0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07, +0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13, +0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80, +0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB, +0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A, +0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20, +0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0, +0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20, +0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39, +0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92, +0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1, +0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0, +0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41, +0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC, +0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30, +0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18, +0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B, +0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A, +0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09, +0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10, +0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1, +0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96, +0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09, +0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09, +0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A, +0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B, +0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90, +0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0, +0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1, +0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28, +0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18, +0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF, +0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0, +0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02, +0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08, +0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96, +0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88, +0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28, +0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D, +0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0, +0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83, +0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3, +0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04, +0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2, +0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21, +0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98, +0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8, +0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23, +0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39, +0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08, +0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E, +0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14, +0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4, +0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0, +0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88, +0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0, +0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69, +0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08, +0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13, +0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0, +0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA, +0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81, +0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01, +0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B, +0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B, +0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4, +0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18, +0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01, +0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0, +0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5, +0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94, +0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A, +0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A, +0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88, +0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B, +0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01, +0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12, +0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12, +0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D, +0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02, +0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1, +0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82, +0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1, +0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A, +0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A, +0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F, +0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98, +0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1, +0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3, +0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68, +0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91, +0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A, +0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C, +0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90, +0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6, +0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89, +0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B, +0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA, +0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1, +0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93, +0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93, +0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00, +0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B, +0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2, +0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92, +0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89, +0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48, +0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10, +0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29, +0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91, +0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3, +0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90, + +/* Source: 08HH.ROM */ +/* Length: 384 / 0x00000180 */ + +0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3, +0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7, +0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D, +0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80, +0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B, +0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2, +0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91, +0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D, +0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C, +0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5, +0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B, +0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01, +0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92, +0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93, +0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4, +0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3, +0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08, +0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3, +0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88, +0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92, +0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00, +0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4, +0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4, +0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0, + +/* Source: 10TOM.ROM */ +/* Length: 640 / 0x00000280 */ + +0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2, +0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11, +0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1, +0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32, +0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9, +0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01, +0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14, +0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA, +0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81, +0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00, +0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63, +0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB, +0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34, +0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB, +0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31, +0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99, +0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A, +0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30, +0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02, +0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98, +0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11, +0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11, +0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A, +0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35, +0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11, +0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB, +0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42, +0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99, +0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20, +0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81, +0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31, +0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC, +0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33, +0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB, +0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31, +0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99, +0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99, +0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88, +0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12, +0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00, + +/* Source: 20RIM.ROM */ +/* Length: 128 / 0x00000080 */ + +0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A, +0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2, +0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C, +0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91, +0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59, +0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92, +0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6, +0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80, +}; diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 722dc51bb..33809d17e 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -23,6 +23,7 @@ #include #include +#include "sound/ymfm/ymfm_opn.h" #include "ym2203shared.h" #include "fmshared_OPN.h" @@ -1101,6 +1102,7 @@ int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, unsigned in oscBuf[i]=new DivDispatchOscBuffer; } fm=new ymfm::ym2203(iface); + fm->set_fidelity(ymfm::OPN_FIDELITY_MIN); // YM2149, 2MHz ay=new DivPlatformAY8910; ay->init(p,3,sugRate,35); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 3b3d55767..ae32c0ebd 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -20,9 +20,11 @@ #include "ym2608.h" #include "sound/ymfm/ymfm.h" #include "../engine.h" +#include "../../ta-log.h" #include #include +#include "sound/ymfm/ymfm_opn.h" #include "ym2610shared.h" #include "fmshared_OPN.h" @@ -33,7 +35,7 @@ static unsigned char konOffs[6]={ #define CHIP_DIVIDER 32 -const char* regCheatSheetYM2610B[]={ +const char* regCheatSheetYM2608[]={ // SSG "SSG_FreqL_A", "000", "SSG_FreqH_A", "001", @@ -181,30 +183,6 @@ const char* regCheatSheetYM2610B[]={ "ADPCMA_Ch4_Vol", "10B", "ADPCMA_Ch5_Vol", "10C", "ADPCMA_Ch6_Vol", "10D", - "ADPCMA_Ch1_StL", "110", - "ADPCMA_Ch2_StL", "111", - "ADPCMA_Ch3_StL", "112", - "ADPCMA_Ch4_StL", "113", - "ADPCMA_Ch5_StL", "114", - "ADPCMA_Ch6_StL", "115", - "ADPCMA_Ch1_StH", "118", - "ADPCMA_Ch2_StH", "119", - "ADPCMA_Ch3_StH", "11A", - "ADPCMA_Ch4_StH", "11B", - "ADPCMA_Ch5_StH", "11C", - "ADPCMA_Ch6_StH", "11D", - "ADPCMA_Ch1_EdL", "120", - "ADPCMA_Ch2_EdL", "121", - "ADPCMA_Ch3_EdL", "122", - "ADPCMA_Ch4_EdL", "123", - "ADPCMA_Ch5_EdL", "124", - "ADPCMA_Ch6_EdL", "125", - "ADPCMA_Ch1_EdH", "128", - "ADPCMA_Ch2_EdH", "129", - "ADPCMA_Ch3_EdH", "12A", - "ADPCMA_Ch4_EdH", "12B", - "ADPCMA_Ch5_EdH", "12C", - "ADPCMA_Ch6_EdH", "12D", // FM (Channel 4-6) "FM4_Op1_DT_MULT", "130", "FM5_Op1_DT_MULT", "131", @@ -305,11 +283,11 @@ const char* regCheatSheetYM2610B[]={ NULL }; -const char** DivPlatformYM2610B::getRegisterSheet() { - return regCheatSheetYM2610B; +const char** DivPlatformYM2608::getRegisterSheet() { + return regCheatSheetYM2608; } -const char* DivPlatformYM2610B::getEffectName(unsigned char effect) { +const char* DivPlatformYM2608::getEffectName(unsigned char effect) { switch (effect) { case 0x10: return "10xy: Setup LFO (x: enable; y: speed)"; @@ -429,7 +407,7 @@ const char* DivPlatformYM2610B::getEffectName(unsigned char effect) { return NULL; } -double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) { +double DivPlatformYM2608::NOTE_OPNB(int ch, int note) { if (ch>8) { // ADPCM-B return NOTE_ADPCMB(note); } else if (ch>5) { // PSG @@ -439,7 +417,7 @@ double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) { return NOTE_FNUM_BLOCK(note,11); } -double DivPlatformYM2610B::NOTE_ADPCMB(int note) { +double DivPlatformYM2608::NOTE_ADPCMB(int note) { if (chan[15].sample>=0 && chan[15].samplesong.sampleLen) { double off=65535.0*(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0; return parent->calcBaseFreq((double)chipClock/144,off,note,false); @@ -447,9 +425,10 @@ double DivPlatformYM2610B::NOTE_ADPCMB(int note) { return 0; } -void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) { +void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + /* ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); @@ -463,6 +442,7 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t fmChan[i]=fme->debug_channel(i); adpcmAChan[i]=aae->debug_channel(i); } + */ for (size_t h=start; hdata[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); } @@ -505,10 +485,11 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t } oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); + */ } } -void DivPlatformYM2610B::tick(bool sysTick) { +void DivPlatformYM2608::tick(bool sysTick) { // PSG ay->tick(sysTick); ay->flushWrites(); @@ -684,7 +665,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (chan[15].std.vol.had) { chan[15].outVol=(chan[15].vol*MIN(64,chan[15].std.vol.val))/64; - immWrite(0x1b,chan[15].outVol); + immWrite(0x10b,chan[15].outVol); } if (chan[15].std.arp.had) { @@ -710,8 +691,8 @@ void DivPlatformYM2610B::tick(bool sysTick) { } else { chan[15].freq=0; } - immWrite(0x19,chan[15].freq&0xff); - immWrite(0x1a,(chan[15].freq>>8)&0xff); + immWrite(0x109,chan[15].freq&0xff); + immWrite(0x10a,(chan[15].freq>>8)&0xff); chan[15].freqChanged=false; } @@ -752,7 +733,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { } } -int DivPlatformYM2610B::dispatch(DivCommand c) { +int DivPlatformYM2608::dispatch(DivCommand c) { if (c.chan>5 && c.chan<9) { c.chan-=6; return ay->dispatch(c); @@ -771,18 +752,19 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; - immWrite(0x1b,chan[c.chan].outVol); + immWrite(0x10b,chan[c.chan].outVol); } chan[c.chan].sample=ins->amiga.initSample; if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); - immWrite(0x12,(s->offB>>8)&0xff); - immWrite(0x13,s->offB>>16); + immWrite(0x102,(s->offB>>5)&0xff); + immWrite(0x103,(s->offB>>13)&0xff); int end=s->offB+s->lengthB-1; - immWrite(0x14,(end>>8)&0xff); - immWrite(0x15,end>>16); - immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat + immWrite(0x104,(end>>5)&0xff); + immWrite(0x105,(end>>13)&0xff); + printf("sample want\n"); + immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2); + immWrite(0x100,(s->loopStart>=0)?0xb0:0xa0); // start/repeat if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note); @@ -791,11 +773,11 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; } else { - immWrite(0x10,0x01); // reset - immWrite(0x12,0); - immWrite(0x13,0); - immWrite(0x14,0); - immWrite(0x15,0); + immWrite(0x100,0x01); // reset + immWrite(0x102,0); + immWrite(0x103,0); + immWrite(0x104,0); + immWrite(0x105,0); break; } } else { @@ -803,44 +785,30 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].macroInit(NULL); chan[c.chan].outVol=chan[c.chan].vol; if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { - immWrite(0x10,0x01); // reset - immWrite(0x12,0); - immWrite(0x13,0); - immWrite(0x14,0); - immWrite(0x15,0); + immWrite(0x100,0x01); // reset + immWrite(0x102,0); + immWrite(0x103,0); + immWrite(0x104,0); + immWrite(0x105,0); break; } DivSample* s=parent->getSample(12*sampleBank+c.value%12); - immWrite(0x12,(s->offB>>8)&0xff); - immWrite(0x13,s->offB>>16); + immWrite(0x102,(s->offB>>5)&0xff); + immWrite(0x103,(s->offB>>13)&0xff); int end=s->offB+s->lengthB-1; - immWrite(0x14,(end>>8)&0xff); - immWrite(0x15,end>>16); - immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat + immWrite(0x104,(end>>5)&0xff); + immWrite(0x105,(end>>13)&0xff); + immWrite(0x101,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); + immWrite(0x100,(s->loopStart>=0)?0x90:0x80); // start/repeat chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); chan[c.chan].freqChanged=true; } break; } - if (c.chan>8) { // ADPCM-A + if (c.chan>8) { // RSS TODO: improve rhythm writing strategy if (skipRegisterWrites) break; - if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { - immWrite(0x100,0x80|(1<<(c.chan-9))); - immWrite(0x110+c.chan-9,0); - immWrite(0x118+c.chan-9,0); - immWrite(0x120+c.chan-9,0); - immWrite(0x128+c.chan-9,0); - break; - } - DivSample* s=parent->getSample(12*sampleBank+c.value%12); - immWrite(0x110+c.chan-9,(s->offA>>8)&0xff); - immWrite(0x118+c.chan-9,s->offA>>16); - int end=s->offA+s->lengthA-1; - immWrite(0x120+c.chan-9,(end>>8)&0xff); - immWrite(0x128+c.chan-9,end>>16); - immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); - immWrite(0x100,0x00|(1<<(c.chan-9))); + immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); + immWrite(0x10,0x00|(1<<(c.chan-9))); break; } DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); @@ -894,11 +862,11 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } case DIV_CMD_NOTE_OFF: if (c.chan>14) { - immWrite(0x10,0x01); // reset + immWrite(0x100,0x01); // reset break; } if (c.chan>8) { - immWrite(0x100,0x80|(1<<(c.chan-9))); + immWrite(0x10,0x80|(1<<(c.chan-9))); break; } chan[c.chan].keyOff=true; @@ -908,11 +876,11 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; case DIV_CMD_NOTE_OFF_ENV: if (c.chan>14) { - immWrite(0x10,0x01); // reset + immWrite(0x100,0x01); // reset break; } if (c.chan>8) { - immWrite(0x100,0x80|(1<<(c.chan-9))); + immWrite(0x10,0x80|(1<<(c.chan-9))); break; } chan[c.chan].keyOff=true; @@ -929,11 +897,11 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].outVol=c.value; } if (c.chan>14) { // ADPCM-B - immWrite(0x1b,chan[c.chan].outVol); + immWrite(0x10b,chan[c.chan].outVol); break; } if (c.chan>8) { // ADPCM-A - immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); + immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); break; } for (int i=0; i<4; i++) { @@ -964,11 +932,11 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } if (c.chan>14) { - immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); + immWrite(0x101,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); break; } if (c.chan>8) { - immWrite(0x108+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); + immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); break; } rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); @@ -1263,13 +1231,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { return 1; } -void DivPlatformYM2610B::muteChannel(int ch, bool mute) { +void DivPlatformYM2608::muteChannel(int ch, bool mute) { isMuted[ch]=mute; if (ch>14) { // ADPCM-B - immWrite(0x11,isMuted[ch]?0:(chan[ch].pan<<6)); + //immWrite(0x11,isMuted[ch]?0:(chan[ch].pan<<6)); } if (ch>8) { // ADPCM-A - immWrite(0x108+(ch-9),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol)); + immWrite(0x18+(ch-9),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol)); return; } if (ch>5) { // PSG @@ -1280,7 +1248,7 @@ void DivPlatformYM2610B::muteChannel(int ch, bool mute) { rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); } -void DivPlatformYM2610B::forceIns() { +void DivPlatformYM2608::forceIns() { for (int i=0; i<6; i++) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; @@ -1316,31 +1284,31 @@ void DivPlatformYM2610B::forceIns() { ay->getRegisterWrites().clear(); } -void* DivPlatformYM2610B::getChanState(int ch) { +void* DivPlatformYM2608::getChanState(int ch) { return &chan[ch]; } -DivDispatchOscBuffer* DivPlatformYM2610B::getOscBuffer(int ch) { +DivDispatchOscBuffer* DivPlatformYM2608::getOscBuffer(int ch) { return oscBuf[ch]; } -unsigned char* DivPlatformYM2610B::getRegisterPool() { +unsigned char* DivPlatformYM2608::getRegisterPool() { return regPool; } -int DivPlatformYM2610B::getRegisterPoolSize() { +int DivPlatformYM2608::getRegisterPoolSize() { return 512; } -void DivPlatformYM2610B::poke(unsigned int addr, unsigned short val) { +void DivPlatformYM2608::poke(unsigned int addr, unsigned short val) { immWrite(addr,val); } -void DivPlatformYM2610B::poke(std::vector& wlist) { +void DivPlatformYM2608::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) immWrite(i.addr,i.val); } -void DivPlatformYM2610B::reset() { +void DivPlatformYM2608::reset() { while (!writes.empty()) writes.pop(); memset(regPool,0,512); if (dumpWrites) { @@ -1348,7 +1316,7 @@ void DivPlatformYM2610B::reset() { } fm->reset(); for (int i=0; i<16; i++) { - chan[i]=DivPlatformYM2610B::Channel(); + chan[i]=DivPlatformYM2608::Channel(); chan[i].std.setEngine(parent); } for (int i=0; i<6; i++) { @@ -1379,23 +1347,30 @@ void DivPlatformYM2610B::reset() { immWrite(0x22,0x08); // PCM volume - immWrite(0x101,0x3f); // A - immWrite(0x1b,0xff); // B + immWrite(0x11,0x3f); // A + immWrite(0x10b,0xff); // B + + // ADPCM limit + immWrite(0x10d,0xff); + immWrite(0x10c,0xff); + + // enable 6 channel mode + immWrite(0x29,0x80); ay->reset(); ay->getRegisterWrites().clear(); ay->flushWrites(); } -bool DivPlatformYM2610B::isStereo() { +bool DivPlatformYM2608::isStereo() { return true; } -bool DivPlatformYM2610B::keyOffAffectsArp(int ch) { +bool DivPlatformYM2608::keyOffAffectsArp(int ch) { return (ch>5); } -void DivPlatformYM2610B::notifyInsChange(int ins) { +void DivPlatformYM2608::notifyInsChange(int ins) { for (int i=0; i<16; i++) { if (chan[i].ins==ins) { chan[i].insChanged=true; @@ -1404,17 +1379,59 @@ void DivPlatformYM2610B::notifyInsChange(int ins) { ay->notifyInsChange(ins); } -void DivPlatformYM2610B::notifyInsDeletion(void* ins) { +void DivPlatformYM2608::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); } -void DivPlatformYM2610B::setSkipRegisterWrites(bool value) { +void DivPlatformYM2608::setSkipRegisterWrites(bool value) { DivDispatch::setSkipRegisterWrites(value); ay->setSkipRegisterWrites(value); } -int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { - DivPlatformYM2610Base::init(p, channels, sugRate, flags); +const void* DivPlatformYM2608::getSampleMem(int index) { + return index == 0 ? adpcmBMem : NULL; +} + +size_t DivPlatformYM2608::getSampleMemCapacity(int index) { + return index == 0 ? 2097152 : 0; +} + +size_t DivPlatformYM2608::getSampleMemUsage(int index) { + return index == 0 ? adpcmBMemLen : 0; +} + +void DivPlatformYM2608::renderSamples() { + memset(adpcmBMem,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthB+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM memory for sample %d!",i); + } else { + memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + } + s->offB=memPos; + memPos+=paddedLen; + } + adpcmBMemLen=memPos+256; +} + +int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + adpcmBMem=new unsigned char[getSampleMemCapacity(0)]; + adpcmBMemLen=0; + iface.adpcmBMem=adpcmBMem; + iface.sampleBank=0; dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<16; i++) { @@ -1422,11 +1439,13 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i oscBuf[i]=new DivDispatchOscBuffer; } chipClock=8000000; - rate=chipClock/16; + fm=new ymfm::ym2608(iface); + fm->set_fidelity(ymfm::OPN_FIDELITY_MIN); + rate=fm->sample_rate(chipClock); + printf("rate: %d\n",rate); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } - fm=new ymfm::ym2610b(iface); // YM2149, 2MHz ay=new DivPlatformAY8910; ay->init(p,3,sugRate,35); @@ -1435,15 +1454,15 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i return 16; } -void DivPlatformYM2610B::quit() { +void DivPlatformYM2608::quit() { for (int i=0; i<16; i++) { delete oscBuf[i]; } ay->quit(); delete ay; delete fm; - DivPlatformYM2610Base::quit(); + delete[] adpcmBMem; } -DivPlatformYM2610B::~DivPlatformYM2610B() { +DivPlatformYM2608::~DivPlatformYM2608() { } diff --git a/src/engine/platform/ym2608.h b/src/engine/platform/ym2608.h index 0de28f6fd..6845f0a3e 100644 --- a/src/engine/platform/ym2608.h +++ b/src/engine/platform/ym2608.h @@ -24,9 +24,18 @@ #include #include "sound/ymfm/ymfm_opn.h" -#include "ym2610.h" +#include "ay.h" -class DivPlatformYM2608: public DivPlatformYM2610Base { +class DivYM2608Interface: public ymfm::ymfm_interface { + public: + unsigned char* adpcmBMem; + int sampleBank; + uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); + void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); + DivYM2608Interface(): adpcmBMem(NULL), sampleBank(0) {} +}; + +class DivPlatformYM2608: public DivDispatch { protected: const unsigned short chanOffs[6]={ 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 @@ -88,6 +97,10 @@ class DivPlatformYM2608: public DivPlatformYM2610Base { ymfm::ym2608::output_data fmout; unsigned char regPool[512]; unsigned char lastBusy; + + unsigned char* adpcmBMem; + size_t adpcmBMemLen; + DivYM2608Interface iface; DivPlatformAY8910* ay; unsigned char sampleBank; @@ -123,6 +136,10 @@ class DivPlatformYM2608: public DivPlatformYM2610Base { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformYM2608(); diff --git a/src/engine/platform/ym2608Interface.cpp b/src/engine/platform/ym2608Interface.cpp new file mode 100644 index 000000000..62cd8899f --- /dev/null +++ b/src/engine/platform/ym2608Interface.cpp @@ -0,0 +1,42 @@ +/** + * 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 "sound/ymfm/ymfm.h" +#include "ym2608.h" +#include "../engine.h" + +#include "sound/rss.h" + +uint8_t DivYM2608Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { + switch (type) { + case ymfm::ACCESS_ADPCM_A: + return YM2608_ADPCM_ROM[address&0x1fff]; + case ymfm::ACCESS_ADPCM_B: + if (adpcmBMem==NULL) { + return 0; + } + return adpcmBMem[address&0xffffff]; + default: + return 0; + } + return 0; +} + +void DivYM2608Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) { +} From db2ebc49307bcc9762456209594ec2f95540425d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 03:18:14 -0500 Subject: [PATCH 13/42] OPNA: improvements --- src/engine/engine.cpp | 8 +++++--- src/engine/platform/ym2608.cpp | 2 -- src/engine/sysDef.cpp | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 99e41313c..320425c7f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1612,7 +1612,9 @@ int DivEngine::addInstrument(int refChan) { *ins=song.nullInsQSound; } ins->name=fmt::sprintf("Instrument %d",insCount); - ins->type=prefType; + if (prefType!=DIV_INS_NULL) { + ins->type=prefType; + } saveLock.lock(); song.ins.push_back(ins); song.insLen=insCount+1; @@ -2310,9 +2312,9 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 1. check which channels are viable for this instrument DivInstrument* insInst=getIns(ins); - if (getPreferInsType(finalChan)!=insInst->type && getPreferInsSecondType(finalChan)!=insInst->type) notInViableChannel=true; + if (getPreferInsType(finalChan)!=insInst->type && getPreferInsSecondType(finalChan)!=insInst->type && getPreferInsType(finalChan)!=DIV_INS_NULL) notInViableChannel=true; for (int i=0; i=song.insLen || getPreferInsType(i)==insInst->type || getPreferInsSecondType(i)==insInst->type) { + if (ins==-1 || ins>=song.insLen || getPreferInsType(i)==insInst->type || (getPreferInsType(i)==DIV_INS_NULL && finalChanType==DIV_CH_NOISE) || getPreferInsSecondType(i)==insInst->type) { if (insInst->type==DIV_INS_OPL) { if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) { isViable[i]=true; diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index ae32c0ebd..31ecba9c3 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -762,7 +762,6 @@ int DivPlatformYM2608::dispatch(DivCommand c) { int end=s->offB+s->lengthB-1; immWrite(0x104,(end>>5)&0xff); immWrite(0x105,(end>>13)&0xff); - printf("sample want\n"); immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2); immWrite(0x100,(s->loopStart>=0)?0xb0:0xa0); // start/repeat if (c.value!=DIV_NOTE_NULL) { @@ -1442,7 +1441,6 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, unsigned in fm=new ymfm::ym2608(iface); fm->set_fidelity(ymfm::OPN_FIDELITY_MIN); rate=fm->sample_rate(chipClock); - printf("rate: %d\n",rate); for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 934789890..3621c77e9 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1274,7 +1274,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_OPL]=new DivSysDef( From 34d868522bc3a4c68ea2eae03dcf73de0c2688b3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 03:18:29 -0500 Subject: [PATCH 14/42] update Bubble System and SegaPCM docs --- papers/doc/7-systems/bubblesystem.md | 12 +++++------- papers/doc/7-systems/segapcm.md | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/papers/doc/7-systems/bubblesystem.md b/papers/doc/7-systems/bubblesystem.md index 0c363163e..7b14e90ea 100644 --- a/papers/doc/7-systems/bubblesystem.md +++ b/papers/doc/7-systems/bubblesystem.md @@ -1,14 +1,12 @@ # Bubble System WSG -a Konami's 2 channel wavetable sound generator logic used at their arcade hardware Bubble System. +a Konami-made 2 channel wavetable sound generator logic used on the Bubble System arcade board, configured with K005289, a 4-bit PROM and DAC. -It's configured with K005289, 4 bit PROM and DAC. +however, the K005289 is just part of the logic used for pitch and wavetable ROM address. +waveform select and volume control are tied with single AY-3-8910 IO for both channels. +another AY-3-8910 IO is used for reading sound hardware status. -Also known as K005289, but that's just part of the logic used for pitch and wavetable ROM address. -Waveform select and Volume control are tied with single AY-3-8910 IO for both channels. -Another AY-3-8910 IO is used for reading sound hardware status. - -Furnace emulates this configurations as single system, waveform format is 15 level and 32 width. +Furnace emulates this configuration as single system with 32x16 wavetables. # effects diff --git a/papers/doc/7-systems/segapcm.md b/papers/doc/7-systems/segapcm.md index 1b3ed63fb..364626332 100644 --- a/papers/doc/7-systems/segapcm.md +++ b/papers/doc/7-systems/segapcm.md @@ -4,7 +4,7 @@ yep, that's right! 16 channels of PCM! -a chip used in the Sega OutRun/X/Y arcade boards. eventually the MultiPCM surpassed it with 24 channels, and later they joined the software mixing gang. +a chip used in the Sega OutRun/X/Y arcade boards. eventually the MultiPCM surpassed it with 28 channels, and later they joined the software mixing gang. # effects - `20xx`: set PCM frequency. From 4a563a416e901711348752fdd1c75bf03f15f068 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 14:59:36 -0500 Subject: [PATCH 15/42] prepare for Y8950/YMU759 ADPCM --- CMakeLists.txt | 2 +- src/engine/platform/opl.cpp | 96 ++++++++++++++++++++++++++- src/engine/platform/opl.h | 21 +++++- src/engine/platform/oplAInterface.cpp | 38 +++++++++++ 4 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 src/engine/platform/oplAInterface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f912faea..dd2624944 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,8 +318,8 @@ src/engine/platform/sound/vrcvi/vrcvi.cpp src/engine/platform/sound/scc/scc.cpp +src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp - src/engine/platform/ym2610Interface.cpp src/engine/blip_buf.c diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 0933934c8..8fd1ede37 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -19,6 +19,7 @@ #include "opl.h" #include "../engine.h" +#include "../../ta-log.h" #include #include @@ -246,19 +247,48 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) { void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) { static short o[2]; static int os[2]; + static ymfm::ymfm_output<2> aOut; for (size_t h=start; h=0) { + adpcmB->write(w.addr-7,(w.val&15)|0x80); + OPL3_WriteReg(&fm,w.addr,w.val&0xc0); + } else { + OPL3_WriteReg(&fm,w.addr,w.val); + } + break; + case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 21: case 22: case 23: + if (adpcmChan>=0) { + adpcmB->write(w.addr-7,w.val); + } else { + OPL3_WriteReg(&fm,w.addr,w.val); + } + break; + default: + OPL3_WriteReg(&fm,w.addr,w.val); + break; + } regPool[w.addr&511]=w.val; writes.pop(); } OPL3_Generate(&fm,o); os[0]+=o[0]; os[1]+=o[1]; + if (adpcmChan>=0) { + adpcmB->clock(); + aOut.clear(); + adpcmB->output<2>(aOut,0); + + os[0]+=aOut.data[0]; + os[1]+=aOut.data[1]; + } + for (int i=0; i=0) ? adpcmBMem : NULL; +} + +size_t DivPlatformOPL::getSampleMemCapacity(int index) { + return (index==0 && adpcmChan>=0) ? 2097152 : 0; +} + +size_t DivPlatformOPL::getSampleMemUsage(int index) { + return (index==0 && adpcmChan>=0) ? adpcmBMemLen : 0; +} + +void DivPlatformOPL::renderSamples() { + if (adpcmChan<0) return; + memset(adpcmBMem,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthB+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM memory for sample %d!",i); + } else { + memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + } + s->offB=memPos; + memPos+=paddedLen; + } + adpcmBMemLen=memPos+256; +} + int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; @@ -1437,6 +1515,14 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f } setFlags(flags); + if (adpcmChan>=0) { + adpcmBMem=new unsigned char[getSampleMemCapacity(0)]; + adpcmBMemLen=0; + iface.adpcmBMem=adpcmBMem; + iface.sampleBank=0; + adpcmB=new ymfm::adpcm_b_engine(iface,2); + } + reset(); return totalChans; } @@ -1445,6 +1531,10 @@ void DivPlatformOPL::quit() { for (int i=0; i<18; i++) { delete oscBuf[i]; } + if (adpcmChan>=0) { + delete adpcmB; + delete[] adpcmBMem; + } } DivPlatformOPL::~DivPlatformOPL() { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 64db4d665..c4e0215d7 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -23,6 +23,16 @@ #include "../macroInt.h" #include #include "../../../extern/opl/opl3.h" +#include "sound/ymfm/ymfm_adpcm.h" + +class DivOPLAInterface: public ymfm::ymfm_interface { + public: + unsigned char* adpcmBMem; + int sampleBank; + uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); + void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); + DivOPLAInterface(): adpcmBMem(NULL), sampleBank(0) {} +}; class DivPlatformOPL: public DivDispatch { protected: @@ -73,13 +83,18 @@ class DivPlatformOPL: public DivDispatch { }; std::queue writes; opl3_chip fm; + unsigned char* adpcmBMem; + size_t adpcmBMemLen; + DivOPLAInterface iface; + + ymfm::adpcm_b_engine* adpcmB; const unsigned char** slotsNonDrums; const unsigned char** slotsDrums; const unsigned char** slots; const unsigned short* chanMap; const unsigned char* outChanMap; double chipFreqBase; - int delay, oplType, chans, melodicChans, totalChans; + int delay, oplType, chans, melodicChans, totalChans, adpcmChan; unsigned char lastBusy; unsigned char drumState; unsigned char drumVol[5]; @@ -127,6 +142,10 @@ class DivPlatformOPL: public DivDispatch { void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformOPL(); diff --git a/src/engine/platform/oplAInterface.cpp b/src/engine/platform/oplAInterface.cpp new file mode 100644 index 000000000..999fb47d1 --- /dev/null +++ b/src/engine/platform/oplAInterface.cpp @@ -0,0 +1,38 @@ +/** + * 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 "sound/ymfm/ymfm.h" +#include "opl.h" +#include "../engine.h" + +uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t address) { + switch (type) { + case ymfm::ACCESS_ADPCM_B: + if (adpcmBMem==NULL) { + return 0; + } + return adpcmBMem[address&0xffffff]; + default: + return 0; + } + return 0; +} + +void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) { +} From 7218710268acefe5710339102bf66f06a1951a79 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 16:52:44 -0500 Subject: [PATCH 16/42] add Y8950 system --- src/engine/dispatchContainer.cpp | 8 ++ src/engine/platform/opl.cpp | 184 +++++++++++++++++++++++++++++-- src/engine/platform/opl.h | 10 +- src/engine/sysDef.cpp | 10 +- src/gui/guiConst.cpp | 2 + src/gui/presets.cpp | 26 +++++ 6 files changed, 225 insertions(+), 15 deletions(-) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 46efffc31..e74dd8225 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -273,6 +273,14 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do dispatch=new DivPlatformOPL; ((DivPlatformOPL*)dispatch)->setOPLType(3,true); break; + case DIV_SYSTEM_Y8950: + dispatch=new DivPlatformOPL; + ((DivPlatformOPL*)dispatch)->setOPLType(8950,false); + break; + case DIV_SYSTEM_Y8950_DRUMS: + dispatch=new DivPlatformOPL; + ((DivPlatformOPL*)dispatch)->setOPLType(8950,true); + break; case DIV_SYSTEM_OPZ: dispatch=new DivPlatformTX81Z; break; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 8fd1ede37..97a382a18 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -286,7 +286,8 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_ adpcmB->output<2>(aOut,0); os[0]+=aOut.data[0]; - os[1]+=aOut.data[1]; + os[1]+=aOut.data[0]; + oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]+=aOut.data[0]; } for (int i=0; i=0 && chan[adpcmChan].samplesong.sampleLen) { + double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0; + return parent->calcBaseFreq((double)chipClock/144,off,note,false); + } + return 0; +} + void DivPlatformOPL::tick(bool sysTick) { for (int i=0; i=0) { + if (chan[adpcmChan].furnacePCM) { + chan[adpcmChan].std.next(); + + if (chan[adpcmChan].std.vol.had) { + chan[adpcmChan].outVol=(chan[adpcmChan].vol*MIN(64,chan[adpcmChan].std.vol.val))/64; + immWrite(18,chan[adpcmChan].outVol); + } + + if (chan[adpcmChan].std.arp.had) { + if (!chan[adpcmChan].inPorta) { + if (chan[adpcmChan].std.arp.mode) { + chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].std.arp.val); + } else { + chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note+(signed char)chan[adpcmChan].std.arp.val); + } + } + chan[adpcmChan].freqChanged=true; + } else { + if (chan[adpcmChan].std.arp.mode && chan[adpcmChan].std.arp.finished) { + chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note); + chan[adpcmChan].freqChanged=true; + } + } + } + if (chan[adpcmChan].freqChanged) { + if (chan[adpcmChan].sample>=0 && chan[adpcmChan].samplesong.sampleLen) { + double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0; + chan[adpcmChan].freq=parent->calcFreq(chan[adpcmChan].baseFreq,chan[adpcmChan].pitch,false,4,chan[adpcmChan].pitch2,(double)chipClock/144,off); + } else { + chan[adpcmChan].freq=0; + } + immWrite(16,chan[adpcmChan].freq&0xff); + immWrite(17,(chan[adpcmChan].freq>>8)&0xff); + chan[adpcmChan].freqChanged=false; + } + } + for (int i=0; i<512; i++) { if (pendingWrites[i]!=oldWrites[i]) { immWrite(i,pendingWrites[i]&0xff); @@ -655,13 +704,77 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) { } int DivPlatformOPL::dispatch(DivCommand c) { - if (c.chan>=totalChans) return 0; + if (c.chan>=totalChans && c.chan!=adpcmChan) return 0; // ineffective in 4-op mode - if (oplType==3 && c.chan<14 && (c.chan&1) && c.cmd!=DIV_CMD_GET_VOLMAX && c.cmd!=DIV_ALWAYS_SET_VOLUME) { + if (oplType==3 && c.chan!=adpcmChan && c.chan<14 && (c.chan&1) && c.cmd!=DIV_CMD_GET_VOLMAX && c.cmd!=DIV_ALWAYS_SET_VOLUME) { if (chan[c.chan-1].fourOp) return 0; } switch (c.cmd) { case DIV_CMD_NOTE_ON: { + if (c.chan==adpcmChan) { // ADPCM + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + if (ins->type==DIV_INS_AMIGA) { + chan[c.chan].furnacePCM=true; + } else { + chan[c.chan].furnacePCM=false; + } + if (skipRegisterWrites) break; + if (chan[c.chan].furnacePCM) { + chan[c.chan].macroInit(ins); + if (!chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + immWrite(18,chan[c.chan].outVol); + } + chan[c.chan].sample=ins->amiga.initSample; + if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { + DivSample* s=parent->getSample(chan[c.chan].sample); + immWrite(9,(s->offB>>2)&0xff); + immWrite(10,(s->offB>>10)&0xff); + int end=s->offB+s->lengthB-1; + immWrite(11,(end>>2)&0xff); + immWrite(12,(end>>10)&0xff); + immWrite(8,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2); + immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].note=c.value; + chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note); + chan[c.chan].freqChanged=true; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + } else { + immWrite(7,0x01); // reset + immWrite(9,0); + immWrite(10,0); + immWrite(11,0); + immWrite(12,0); + break; + } + } else { + chan[c.chan].sample=-1; + chan[c.chan].macroInit(NULL); + chan[c.chan].outVol=chan[c.chan].vol; + if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { + immWrite(7,0x01); // reset + immWrite(9,0); + immWrite(10,0); + immWrite(11,0); + immWrite(12,0); + break; + } + DivSample* s=parent->getSample(12*sampleBank+c.value%12); + immWrite(9,(s->offB>>2)&0xff); + immWrite(10,(s->offB>>10)&0xff); + int end=s->offB+s->lengthB-1; + immWrite(11,(end>>2)&0xff); + immWrite(12,(end>>10)&0xff); + immWrite(8,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); + immWrite(7,(s->loopStart>=0)?0x90:0x80); // start/repeat + chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); + chan[c.chan].freqChanged=true; + } + break; + } DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL); if (chan[c.chan].insChanged) { @@ -744,11 +857,19 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_OFF: + if (c.chan==adpcmChan) { + immWrite(7,0x01); // reset + break; + } chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; break; case DIV_CMD_NOTE_OFF_ENV: + if (c.chan==adpcmChan) { + immWrite(7,0x01); // reset + break; + } chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; @@ -758,7 +879,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { chan[c.chan].std.release(); break; case DIV_CMD_VOLUME: { - if (pretendYMU) { + if (pretendYMU && c.chan!=adpcmChan) { c.value=pow(((double)c.value/127.0),0.5)*63.0; if (c.value<0) c.value=0; if (c.value>63) c.value=63; @@ -767,6 +888,10 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; } + if (c.chan==adpcmChan) { // ADPCM-B + immWrite(18,chan[c.chan].outVol); + break; + } int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; for (int i=0; ichan[c.chan].baseFreq) { @@ -857,13 +983,22 @@ int DivPlatformOPL::dispatch(DivCommand c) { } break; } + case DIV_CMD_SAMPLE_BANK: + if (adpcmChan<0) break; + sampleBank=c.value; + if (sampleBank>(int)(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + iface.sampleBank=sampleBank; + break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value)); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; } case DIV_CMD_FM_LFO: { + if (c.chan==adpcmChan) break; if (c.value&2) { dvb=c.value&1; } else { @@ -873,6 +1008,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_FB: { + if (c.chan==adpcmChan) break; chan[c.chan].state.fb=c.value&7; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (isMuted[c.chan]) { @@ -889,6 +1025,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_MULT: { + if (c.chan==adpcmChan) break; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (c.value>=ops) break; DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; @@ -900,6 +1037,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_TL: { + if (c.chan==adpcmChan) break; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (c.value>=ops) break; DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; @@ -919,6 +1057,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { + if (c.chan==adpcmChan) break; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (c.value<0) { for (int i=0; i=0) { + chan[adpcmChan]=DivPlatformOPL::Channel(); + chan[adpcmChan].std.setEngine(parent); + chan[adpcmChan].vol=0xff; + chan[adpcmChan].outVol=0xff; + + adpcmB->reset(); + + // volume + immWrite(18,0xff); + // ADPCM limit + immWrite(20,0xff); + immWrite(19,0xff); + } if (oplType<3) for (int i=0; i=0) ? 2097152 : 0; + return (index==0 && adpcmChan>=0) ? 262144 : 0; } size_t DivPlatformOPL::getSampleMemUsage(int index) { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index c4e0215d7..a67d50380 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -40,8 +40,8 @@ class DivPlatformOPL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, pitch2, note, ins; - bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp, hardReset; + int freq, baseFreq, pitch, pitch2, note, ins, sample; + bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnacePCM, inPorta, fourOp, hardReset; int vol, outVol; unsigned char pan; void macroInit(DivInstrument* which) { @@ -57,13 +57,14 @@ class DivPlatformOPL: public DivDispatch { pitch2(0), note(0), ins(-1), + sample(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), - furnaceDac(false), + furnacePCM(false), inPorta(false), fourOp(false), hardReset(false), @@ -94,7 +95,7 @@ class DivPlatformOPL: public DivDispatch { const unsigned short* chanMap; const unsigned char* outChanMap; double chipFreqBase; - int delay, oplType, chans, melodicChans, totalChans, adpcmChan; + int delay, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank; unsigned char lastBusy; unsigned char drumState; unsigned char drumVol[5]; @@ -112,6 +113,7 @@ class DivPlatformOPL: public DivDispatch { int octave(int freq); int toFreq(int freq); + double NOTE_ADPCMB(int note); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 3621c77e9..85e5eb9cc 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1736,7 +1736,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "PCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "PCM"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA}, + {}, + oplEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_Y8950_DRUMS]=new DivSysDef( @@ -1744,7 +1747,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH", "PCM"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA}, + {}, + oplEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_SCC_PLUS]=new DivSysDef( diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 4ea2de0c2..88e9c7c26 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -841,6 +841,8 @@ const int availableSystems[]={ DIV_SYSTEM_VRC7, DIV_SYSTEM_OPL, DIV_SYSTEM_OPL_DRUMS, + DIV_SYSTEM_Y8950, + DIV_SYSTEM_Y8950_DRUMS, DIV_SYSTEM_OPL2, DIV_SYSTEM_OPL2_DRUMS, DIV_SYSTEM_OPL3, diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index dd003803a..65ad3a2f3 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -106,6 +106,18 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha Y8950", { + DIV_SYSTEM_Y8950, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha Y8950 (drums mode)", { + DIV_SYSTEM_Y8950_DRUMS, 64, 0, 0, + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "Yamaha YM3812", { DIV_SYSTEM_OPL2, 64, 0, 0, @@ -411,6 +423,20 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + MSX-AUDIO", { + DIV_SYSTEM_AY8910, 64, 0, 16, + DIV_SYSTEM_Y8950, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + MSX-AUDIO (drums mode)", { + DIV_SYSTEM_AY8910, 64, 0, 16, + DIV_SYSTEM_Y8950_DRUMS, 64, 0, 0, + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "MSX + MSX-MUSIC", { DIV_SYSTEM_AY8910, 64, 0, 16, From 315018ff24a0140434ae584cfeba400f3f5a450d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 17:22:00 -0500 Subject: [PATCH 17/42] YMU759: ADPCM --- src/engine/platform/opl.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 97a382a18..5ba5342f6 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -568,6 +568,7 @@ void DivPlatformOPL::tick(bool sysTick) { } } if (chan[adpcmChan].freqChanged) { + printf("freq changed\n"); if (chan[adpcmChan].sample>=0 && chan[adpcmChan].samplesong.sampleLen) { double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0; chan[adpcmChan].freq=parent->calcFreq(chan[adpcmChan].baseFreq,chan[adpcmChan].pitch,false,4,chan[adpcmChan].pitch2,(double)chipClock/144,off); @@ -733,7 +734,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { int end=s->offB+s->lengthB-1; immWrite(11,(end>>2)&0xff); immWrite(12,(end>>10)&0xff); - immWrite(8,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2); + immWrite(8,2); immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; @@ -768,10 +769,11 @@ int DivPlatformOPL::dispatch(DivCommand c) { int end=s->offB+s->lengthB-1; immWrite(11,(end>>2)&0xff); immWrite(12,(end>>10)&0xff); - immWrite(8,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(7,(s->loopStart>=0)?0x90:0x80); // start/repeat - chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); - chan[c.chan].freqChanged=true; + immWrite(8,2); + immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat + int freq=(65536.0*(double)s->rate)/(double)rate; + immWrite(16,freq&0xff); + immWrite(17,(freq>>8)&0xff); } break; } @@ -947,6 +949,9 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_PITCH: { + if (c.chan==adpcmChan) { + if (!chan[c.chan].furnacePCM) break; + } chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; From 1ffbfe17d458733773ea141579f9a9a8f3e39b4e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 17:40:05 -0500 Subject: [PATCH 18/42] Y8950/YMU759: ADPCM muting --- src/engine/platform/opl.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 5ba5342f6..fe8fd4397 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -285,9 +285,13 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_ aOut.clear(); adpcmB->output<2>(aOut,0); - os[0]+=aOut.data[0]; - os[1]+=aOut.data[0]; - oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]+=aOut.data[0]; + if (!isMuted[adpcmChan]) { + os[0]+=aOut.data[0]; + os[1]+=aOut.data[0]; + oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]+=aOut.data[0]; + } else { + oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=0; + } } for (int i=0; i Date: Fri, 13 May 2022 17:41:01 -0500 Subject: [PATCH 19/42] fix loading of pre-0.4 .dmf modules --- src/engine/fileOps.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index e0f207948..99b46319d 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -654,7 +654,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } for (int j=0; j0x05) { // current pattern format + if (ds.version>0x08) { // current pattern format for (int k=0; kdata[k][0]=reader.readS(); @@ -741,11 +741,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { unsigned char vol=reader.readC(); unsigned char fx=reader.readC(); unsigned char fxVal=reader.readC(); - pat->data[k][3]=(vol==0x80)?-1:vol; + pat->data[k][3]=(vol==0x80 || vol==0xff)?-1:vol; // effect - pat->data[k][4]=(fx==0x80)?-1:fx; - pat->data[k][5]=(fxVal==0x80)?-1:fxVal; - // instrument wasn't stored back then + pat->data[k][4]=(fx==0x80 || fx==0xff)?-1:fx; + pat->data[k][5]=(fxVal==0x80 || fx==0xff)?-1:fxVal; + // instrument + if (ds.version>0x05) { + pat->data[k][2]=reader.readC(); + if (pat->data[k][2]==0x80 || pat->data[k][2]==0xff) pat->data[k][2]=-1; + } } } } From 9936c66184ba9d391b3982040085e8f59e52e0dc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 17:41:13 -0500 Subject: [PATCH 20/42] OPL: remove printf --- src/engine/platform/opl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index fe8fd4397..20b95c9b9 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -572,7 +572,6 @@ void DivPlatformOPL::tick(bool sysTick) { } } if (chan[adpcmChan].freqChanged) { - printf("freq changed\n"); if (chan[adpcmChan].sample>=0 && chan[adpcmChan].samplesong.sampleLen) { double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0; chan[adpcmChan].freq=parent->calcFreq(chan[adpcmChan].baseFreq,chan[adpcmChan].pitch,false,4,chan[adpcmChan].pitch2,(double)chipClock/144,off); From be0cbfa1f767d8e844d6075b595040abcff1e514 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 18:03:35 -0500 Subject: [PATCH 21/42] GUI: fix non-spaced MML string parsing fixes #457 --- src/gui/gui.cpp | 80 +++++++++++-------------------------------------- src/gui/gui.h | 2 -- 2 files changed, 18 insertions(+), 64 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index abeb569d4..e424de045 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -194,21 +194,6 @@ void FurnaceGUI::decodeKeyMap(std::map& map, String source) { } } -void FurnaceGUI::encodeMMLStr(String& target, unsigned char* macro, unsigned char macroLen, signed char macroLoop, signed char macroRel) { - target=""; - char buf[32]; - for (int i=0; imacroMax) macro[macroLen]=macroMax; - macroLen++; - buf=0; - } - break; - case '|': - if (macroLoop==-1) { - macroLoop=macroLen; - } - break; - case '/': - if (macroRel==-1) { - macroRel=macroLen; - } - break; - } - if (macroLen>=128) break; - } - if (hasVal && macroLen<128) { - hasVal=false; - macro[macroLen]=buf; - if (macro[macroLen]macroMax) macro[macroLen]=macroMax; - macroLen++; - buf=0; - } -} - void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel) { int buf=0; bool negaBuf=false; @@ -370,11 +308,29 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe } break; case '|': + if (hasVal) { + hasVal=false; + macro[macroLen]=negaBuf?-buf:buf; + negaBuf=false; + if (macro[macroLen]macroMax) macro[macroLen]=macroMax; + macroLen++; + buf=0; + } if (macroLoop==-1) { macroLoop=macroLen; } break; case '/': + if (hasVal) { + hasVal=false; + macro[macroLen]=negaBuf?-buf:buf; + negaBuf=false; + if (macro[macroLen]macroMax) macro[macroLen]=macroMax; + macroLen++; + buf=0; + } if (macroRel==-1) { macroRel=macroLen; } diff --git a/src/gui/gui.h b/src/gui/gui.h index 61b2d912f..4017dccaa 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1318,8 +1318,6 @@ class FurnaceGUI { void initSystemPresets(); void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false); - void encodeMMLStr(String& target, unsigned char* macro, unsigned char macroLen, signed char macroLoop, signed char macroRel); - void decodeMMLStr(String& source, unsigned char* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel); void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel); void decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMax, bool hex=false); From 77481f23cb128230c9aea7be03444a1608d36ada Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 18:46:52 -0500 Subject: [PATCH 22/42] prepare to implement sample map --- src/engine/instrument.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 47fc95ddc..18cefd017 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -311,6 +311,32 @@ struct DivInstrumentAmiga { int noteFreq[120]; short noteMap[120]; + /** + * get the sample at specified note. + * @return the sample. + */ + inline short getSample(int note) { + if (useNoteMap) { + if (note<0) note=0; + if (note>119) note=119; + return noteMap[note]; + } + return initSample; + } + + /** + * get the sample frequency at specified note. + * @return the frequency, or -1 if not using note map. + */ + inline int getFreq(int note) { + if (useNoteMap) { + if (note<0) note=0; + if (note>119) note=119; + return noteFreq[note]; + } + return -1; + } + DivInstrumentAmiga(): initSample(0), useNoteMap(false), From bf11e85bff650fcaee45241a3e453b9e935c1231 Mon Sep 17 00:00:00 2001 From: LovelyA72 <29054052+LovelyA72@users.noreply.github.com> Date: Fri, 13 May 2022 21:52:33 -0400 Subject: [PATCH 23/42] Enable loading of common Chinese characters --- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index 4017dccaa..957ee16ae 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -867,6 +867,7 @@ class FurnaceGUI { int roundedButtons; int roundedMenus; int loadJapanese; + int loadChinese; int fmLayout; int sampleLayout; int waveLayout; @@ -955,6 +956,7 @@ class FurnaceGUI { roundedButtons(1), roundedMenus(0), loadJapanese(0), + loadChinese(0), fmLayout(0), sampleLayout(0), waveLayout(0), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f44c2aa92..741705aee 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -978,6 +978,19 @@ void FurnaceGUI::drawSettings() { ); } + bool loadChineseB=settings.loadChinese; + if (ImGui::Checkbox("Display Chinese (Simplified) characters",&loadChineseB)) { + settings.loadChinese=loadChineseB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "请在确保你有足够的显存后再启动此设定\n" + "这是一个在ImGui实现动态字体加载之前的临时解决方案" + ); + } + ImGui::Separator(); ImGui::Text("Orders row number format:"); @@ -1855,6 +1868,7 @@ void FurnaceGUI::syncSettings() { settings.roundedButtons=e->getConfInt("roundedButtons",1); settings.roundedMenus=e->getConfInt("roundedMenus",0); settings.loadJapanese=e->getConfInt("loadJapanese",0); + settings.loadChinese=e->getConfInt("loadChinese",0); settings.fmLayout=e->getConfInt("fmLayout",0); settings.sampleLayout=e->getConfInt("sampleLayout",0); settings.waveLayout=e->getConfInt("waveLayout",0); @@ -1931,6 +1945,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.roundedButtons,0,1); clampSetting(settings.roundedMenus,0,1); clampSetting(settings.loadJapanese,0,1); + clampSetting(settings.loadChinese,0,1); clampSetting(settings.fmLayout,0,3); clampSetting(settings.susPosition,0,1); clampSetting(settings.effectCursorDir,0,2); @@ -2048,6 +2063,7 @@ void FurnaceGUI::commitSettings() { e->setConf("roundedButtons",settings.roundedButtons); e->setConf("roundedMenus",settings.roundedMenus); e->setConf("loadJapanese",settings.loadJapanese); + e->setConf("loadChinese",settings.loadChinese); e->setConf("fmLayout",settings.fmLayout); e->setConf("sampleLayout",settings.sampleLayout); e->setConf("waveLayout",settings.waveLayout); @@ -2638,6 +2654,9 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { if (settings.loadJapanese) { range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesJapanese()); } + if (settings.loadChinese) { + range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon()); + } // I'm terribly sorry range.UsedChars[0x80>>5]=0; From 14e0189993448db643491916db0d11ddc13c63da Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 21:44:34 -0500 Subject: [PATCH 24/42] WonderSwan: fix forceIns() messing with noise --- src/engine/platform/swan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index b089394a7..22c35511d 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -448,6 +448,7 @@ void DivPlatformSwan::muteChannel(int ch, bool mute) { } void DivPlatformSwan::forceIns() { + noise=0; for (int i=0; i<4; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; From d7ec3b13c5db274b7dfde0385311c74d118bf8fd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 22:43:00 -0500 Subject: [PATCH 25/42] ADPCM-B: fixes to compatible mode --- src/engine/platform/ym2608.cpp | 11 +++++++---- src/engine/platform/ym2610.cpp | 7 +++++-- src/engine/platform/ym2610b.cpp | 7 +++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 31ecba9c3..ec41a8c64 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -797,10 +797,11 @@ int DivPlatformYM2608::dispatch(DivCommand c) { int end=s->offB+s->lengthB-1; immWrite(0x104,(end>>5)&0xff); immWrite(0x105,(end>>13)&0xff); - immWrite(0x101,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); - immWrite(0x100,(s->loopStart>=0)?0x90:0x80); // start/repeat - chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); - chan[c.chan].freqChanged=true; + immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2); + immWrite(0x100,(s->loopStart>=0)?0xb0:0xa0); // start/repeat + int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0); + immWrite(0x109,freq&0xff); + immWrite(0x10a,(freq>>8)&0xff); } break; } @@ -942,6 +943,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { break; } case DIV_CMD_PITCH: { + if (c.chan==15 && !chan[c.chan].furnacePCM) break; chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; @@ -1022,6 +1024,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { iface.sampleBank=sampleBank; break; case DIV_CMD_LEGATO: { + if (c.chan==15 && !chan[c.chan].furnacePCM) break; chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 397f6a8f1..ba29263b6 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -840,8 +840,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) { immWrite(0x15,end>>16); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat - chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); - chan[c.chan].freqChanged=true; + int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0); + immWrite(0x19,freq&0xff); + immWrite(0x1a,(freq>>8)&0xff); } break; } @@ -997,6 +998,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; } case DIV_CMD_PITCH: { + if (c.chan==13 && !chan[c.chan].furnacePCM) break; chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; @@ -1077,6 +1079,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { iface.sampleBank=sampleBank; break; case DIV_CMD_LEGATO: { + if (c.chan==13 && !chan[c.chan].furnacePCM) break; chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index cd40fe139..b5afce5e2 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -818,8 +818,9 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { immWrite(0x15,end>>16); immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat - chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144); - chan[c.chan].freqChanged=true; + int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0); + immWrite(0x19,freq&0xff); + immWrite(0x1a,(freq>>8)&0xff); } break; } @@ -975,6 +976,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; } case DIV_CMD_PITCH: { + if (c.chan==15 && !chan[c.chan].furnacePCM) break; chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; @@ -1055,6 +1057,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { iface.sampleBank=sampleBank; break; case DIV_CMD_LEGATO: { + if (c.chan==15 && !chan[c.chan].furnacePCM) break; chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; From 07871eacc3967f89d101316097168ceaca04ec4c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 22:55:17 -0500 Subject: [PATCH 26/42] Amiga: linear pitch oopsie --- src/engine/platform/amiga.cpp | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index df742d352..c665b9165 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -220,7 +220,7 @@ void DivPlatformAmiga::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + chan[i].freq=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) { @@ -238,7 +238,6 @@ int DivPlatformAmiga::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); - double off=1.0; if (ins->amiga.useWave) { chan[c.chan].useWave=true; chan[c.chan].audLen=(ins->amiga.waveLen+1)>>1; @@ -252,17 +251,9 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } else { chan[c.chan].sample=ins->amiga.initSample; chan[c.chan].useWave=false; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; - } - } } if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value)); + chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value)); } if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { chan[c.chan].sample=-1; @@ -329,16 +320,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { case DIV_CMD_NOTE_PORTA: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); chan[c.chan].sample=ins->amiga.initSample; - double off=1.0; - if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; - } - } - int destFreq=round(off*NOTE_PERIODIC_NOROUND(c.value2)); + int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -361,16 +343,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - double off=1.0; - if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; - } - } - chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)))); + chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; From 0a0ddfa661f5e7cdfd8f59cf8806621c5827c508 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 13 May 2022 23:04:40 -0500 Subject: [PATCH 27/42] partially bring up sample map --- src/engine/platform/amiga.cpp | 4 ++-- src/engine/platform/genesis.cpp | 2 +- src/engine/platform/mmc5.cpp | 2 +- src/engine/platform/nes.cpp | 2 +- src/engine/platform/opl.cpp | 2 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/segapcm.cpp | 2 +- src/engine/platform/su.cpp | 5 +++-- src/engine/platform/swan.cpp | 2 +- src/engine/platform/vera.cpp | 2 +- src/engine/platform/vrc6.cpp | 2 +- src/engine/platform/x1_010.cpp | 2 +- src/engine/platform/ym2608.cpp | 2 +- src/engine/platform/ym2610.cpp | 2 +- src/engine/platform/ym2610b.cpp | 2 +- 16 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index c665b9165..e93e3419e 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -249,7 +249,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } } } else { - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); chan[c.chan].useWave=false; } if (c.value!=DIV_NOTE_NULL) { @@ -319,7 +319,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { break; case DIV_CMD_NOTE_PORTA: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value2); int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index b35d09eef..a8d5c5258 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -547,7 +547,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (c.chan==5 && dacMode) { if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { // Furnace mode - dacSample=ins->amiga.initSample; + dacSample=ins->amiga.getSample(c.value); if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; if (dumpWrites) addWrite(0xffff0002,0); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 9775e6397..abfdfd571 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -192,7 +192,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { if (c.chan==2) { // PCM DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); if (ins->type==DIV_INS_AMIGA) { - dacSample=ins->amiga.initSample; + dacSample=ins->amiga.getSample(c.value); if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; if (dumpWrites) addWrite(0xffff0002,0); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index ef3b51f06..427fdb423 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -381,7 +381,7 @@ int DivPlatformNES::dispatch(DivCommand c) { if (c.chan==4) { // PCM DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); if (ins->type==DIV_INS_AMIGA) { - dacSample=ins->amiga.initSample; + dacSample=ins->amiga.getSample(c.value); if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 20b95c9b9..949d104b8 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -730,7 +730,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { chan[c.chan].outVol=chan[c.chan].vol; immWrite(18,chan[c.chan].outVol); } - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); immWrite(9,(s->offB>>2)&0xff); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 9838f987f..775cdc1a4 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -270,7 +270,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { if (chan[c.chan].pcm) { if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { - chan[c.chan].dacSample=ins->amiga.initSample; + chan[c.chan].dacSample=ins->amiga.getSample(c.value); if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { chan[c.chan].dacSample=-1; if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index e9b4e8a48..c07e07141 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -390,7 +390,7 @@ int DivPlatformQSound::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].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value); } diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 346daeae1..a16f0e161 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -162,7 +162,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { - chan[c.chan].pcm.sample=ins->amiga.initSample; + chan[c.chan].pcm.sample=ins->amiga.getSample(c.value); if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) { chan[c.chan].pcm.sample=-1; if (dumpWrites) { diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 4af605547..724fb5901 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -186,7 +186,8 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].pcm) { DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - DivSample* sample=parent->getSample(ins->amiga.initSample); + // TODO: sample map? + DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note)); if (sample!=NULL) { double off=0.25; if (sample->centerRate<1) { @@ -204,7 +205,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { if (chan[i].keyOn) { if (chan[i].pcm) { DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - DivSample* sample=parent->getSample(ins->amiga.initSample); + DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note)); if (sample!=NULL) { unsigned int sampleEnd=sample->offSU+sample->samples; if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 22c35511d..dbec54593 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -263,7 +263,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (ins->type==DIV_INS_AMIGA) { - dacSample=ins->amiga.initSample; + dacSample=ins->amiga.getSample(c.value); if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; if (dumpWrites) addWrite(0xffff0002,0); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 65a7452ee..52554f3cc 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -260,7 +260,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { if (c.chan<16) { rWriteLo(c.chan,2,chan[c.chan].vol); } else { - chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.initSample; + chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.getSample(c.value); if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) { chan[16].pcm.sample=-1; } diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 4e8117942..ba3815a62 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -243,7 +243,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { if (chan[c.chan].pcm) { if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { - chan[c.chan].dacSample=ins->amiga.initSample; + chan[c.chan].dacSample=ins->amiga.getSample(c.value); if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { chan[c.chan].dacSample=-1; if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index f1711243c..c161b56b3 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -559,7 +559,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { if (chan[c.chan].furnacePCM) { chan[c.chan].pcm=true; chan[c.chan].macroInit(ins); - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); chWrite(c.chan,4,(s->offX1_010>>12)&0xff); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index ec41a8c64..a6855550c 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -754,7 +754,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { chan[c.chan].outVol=chan[c.chan].vol; immWrite(0x10b,chan[c.chan].outVol); } - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); immWrite(0x102,(s->offB>>5)&0xff); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index ba29263b6..0797110c7 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -795,7 +795,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].outVol=chan[c.chan].vol; immWrite(0x1b,chan[c.chan].outVol); } - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); immWrite(0x12,(s->offB>>8)&0xff); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index b5afce5e2..c9d29736c 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -773,7 +773,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].outVol=chan[c.chan].vol; immWrite(0x1b,chan[c.chan].outVol); } - chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].sample=ins->amiga.getSample(c.value); if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); immWrite(0x12,(s->offB>>8)&0xff); From 895765b68ad99637e25eeeec8bcbc6905119e92d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 00:22:23 -0500 Subject: [PATCH 28/42] and here's your ZX beeper no real hardware yet specs of this dispatch may change at any time to fit a driver --- CMakeLists.txt | 1 + src/engine/dispatchContainer.cpp | 4 + src/engine/platform/zxbeeper.cpp | 332 +++++++++++++++++++++++++++++++ src/engine/platform/zxbeeper.h | 108 ++++++++++ src/engine/sysDef.cpp | 2 +- src/gui/guiConst.cpp | 3 +- src/gui/insEdit.cpp | 6 +- 7 files changed, 453 insertions(+), 3 deletions(-) create mode 100644 src/engine/platform/zxbeeper.cpp create mode 100644 src/engine/platform/zxbeeper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dd2624944..52cef63c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -374,6 +374,7 @@ src/engine/platform/lynx.cpp src/engine/platform/su.cpp src/engine/platform/swan.cpp src/engine/platform/vera.cpp +src/engine/platform/zxbeeper.cpp src/engine/platform/bubsyswsg.cpp src/engine/platform/n163.cpp src/engine/platform/pet.cpp diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index e74dd8225..d175b2539 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -59,6 +59,7 @@ #include "platform/scc.h" #include "platform/dummy.h" #include "../ta-log.h" +#include "platform/zxbeeper.h" #include "song.h" void DivDispatchContainer::setRates(double gotRate) { @@ -294,6 +295,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_PCSPKR: dispatch=new DivPlatformPCSpeaker; break; + case DIV_SYSTEM_SFX_BEEPER: + dispatch=new DivPlatformZXBeeper; + break; case DIV_SYSTEM_LYNX: dispatch=new DivPlatformLynx; break; diff --git a/src/engine/platform/zxbeeper.cpp b/src/engine/platform/zxbeeper.cpp new file mode 100644 index 000000000..086ff0051 --- /dev/null +++ b/src/engine/platform/zxbeeper.cpp @@ -0,0 +1,332 @@ +/** + * 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 "zxbeeper.h" +#include "../engine.h" +#include + +#define CHIP_FREQBASE 8192*6 + +const char** DivPlatformZXBeeper::getRegisterSheet() { + return NULL; +} + +const char* DivPlatformZXBeeper::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xx: Change waveform"; + break; + case 0x11: + return "11xx: Toggle noise mode"; + break; + case 0x12: + return "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)"; + break; + case 0x13: + return "13xx: Set LFO speed"; + break; + case 0x17: + return "17xx: Toggle PCM mode"; + break; + } + return NULL; +} + +void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hchan[curChan].sPosition) { + if (!isMuted[curChan] && chan[curChan].outVol) sOffTimer+=chan[curChan].duty; + } + if (++curChan>=6) curChan=0; + + bufL[h]=o?16384:0; + } +} + +void DivPlatformZXBeeper::tick(bool sysTick) { + for (int i=0; i<6; i++) { + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=((chan[i].vol&1)*MIN(1,chan[i].std.vol.val)); + } + if (chan[i].std.duty.had) { + chan[i].duty=chan[i].std.duty.val; + chan[i].freqChanged=true; + } + if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + if (chan[i].std.arp.mode) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); + } else { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arp.mode && chan[i].std.arp.finished) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); + 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,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + if (chan[i].active) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE); + if (chan[i].freq>65535) chan[i].freq=65535; + } + if (chan[i].keyOn) { + //rWrite(16+i*5,0x80); + //chWrite(i,0x04,0x80|chan[i].vol); + } + if (chan[i].keyOff) { + chan[i].freq=0; + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } +} + +int DivPlatformZXBeeper::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + 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); + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].dacSample=-1; + if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + chan[c.chan].pcm=false; + 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; + chan[c.chan].insChanged=true; + } + 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; + } + } + 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_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_FREQUENCY(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_STD_NOISE_MODE: + chan[c.chan].duty=c.value; + break; + case DIV_CMD_SAMPLE_MODE: + chan[c.chan].pcm=c.value; + break; + case DIV_CMD_SAMPLE_BANK: + sampleBank=c.value; + if (sampleBank>(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + break; + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(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_BEEPER)); + } + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 1; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformZXBeeper::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; +} + +void DivPlatformZXBeeper::forceIns() { + for (int i=0; i<6; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + } +} + +void* DivPlatformZXBeeper::getChanState(int ch) { + return &chan[ch]; +} + +DivDispatchOscBuffer* DivPlatformZXBeeper::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformZXBeeper::getRegisterPool() { + return regPool; +} + +int DivPlatformZXBeeper::getRegisterPoolSize() { + return 112; +} + +void DivPlatformZXBeeper::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,128); + for (int i=0; i<6; i++) { + chan[i]=DivPlatformZXBeeper::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + lastPan=0xff; + memset(tempL,0,32*sizeof(int)); + memset(tempR,0,32*sizeof(int)); + cycles=0; + curChan=0; + sOffTimer=0; + sampleBank=0; +} + +bool DivPlatformZXBeeper::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformZXBeeper::notifyWaveChange(int wave) { +} + +void DivPlatformZXBeeper::notifyInsDeletion(void* ins) { + for (int i=0; i<6; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformZXBeeper::setFlags(unsigned int flags) { + if (flags&1) { // technically there is no PAL PC Engine but oh well... + chipClock=COLOR_PAL*4.0/5.0; + } else { + chipClock=COLOR_NTSC; + } + rate=chipClock/4; + for (int i=0; i<6; i++) { + oscBuf[i]->rate=rate; + } +} + +void DivPlatformZXBeeper::poke(unsigned int addr, unsigned short val) { + +} + +void DivPlatformZXBeeper::poke(std::vector& wlist) { + +} + +int DivPlatformZXBeeper::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<6; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + setFlags(flags); + reset(); + return 6; +} + +void DivPlatformZXBeeper::quit() { + for (int i=0; i<6; i++) { + delete oscBuf[i]; + } +} + +DivPlatformZXBeeper::~DivPlatformZXBeeper() { +} diff --git a/src/engine/platform/zxbeeper.h b/src/engine/platform/zxbeeper.h new file mode 100644 index 000000000..3a8b9ac2c --- /dev/null +++ b/src/engine/platform/zxbeeper.h @@ -0,0 +1,108 @@ +/** + * 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 _ZXBEEPER_H +#define _ZXBEEPER_H + +#include "../dispatch.h" +#include +#include "../macroInt.h" + +class DivPlatformZXBeeper: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, pitch2, note; + int dacPeriod, dacRate; + unsigned int dacPos; + int dacSample, ins; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac; + signed char vol, outVol; + unsigned short sPosition; + unsigned char duty; + DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } + Channel(): + freq(0), + baseFreq(0), + pitch(0), + pitch2(0), + note(0), + dacPeriod(0), + dacRate(0), + dacPos(0), + dacSample(-1), + ins(-1), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + noise(false), + pcm(false), + furnaceDac(false), + vol(1), + outVol(1), + sPosition(0), + duty(3) {} + }; + Channel chan[6]; + DivDispatchOscBuffer* oscBuf[6]; + bool isMuted[6]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + unsigned char lastPan; + + int cycles, curChan, sOffTimer, delay; + int tempL[32]; + int tempR[32]; + unsigned char sampleBank, lfoMode, lfoSpeed; + unsigned char regPool[128]; + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + 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 keyOffAffectsArp(int ch); + void setFlags(unsigned int flags); + void notifyWaveChange(int wave); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformZXBeeper(); +}; + +#endif diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 85e5eb9cc..a83e88a73 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1326,7 +1326,7 @@ void DivEngine::registerSystems() { {"Square"}, {"SQ"}, {DIV_CH_PULSE}, - {DIV_INS_STD} + {DIV_INS_BEEPER} ); sysDefs[DIV_SYSTEM_POKEY]=new DivSysDef( diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 88e9c7c26..a3474dce0 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -101,7 +101,7 @@ const char* insTypes[DIV_INS_MAX]={ "Konami SCC/Bubble System WSG", "FM (OPZ)", "POKEY", - "PC Beeper", + "Beeper", "WonderSwan", "Atari Lynx", "VERA", @@ -831,6 +831,7 @@ const int availableSystems[]={ DIV_SYSTEM_AY8910, DIV_SYSTEM_AMIGA, DIV_SYSTEM_PCSPKR, + DIV_SYSTEM_SFX_BEEPER, DIV_SYSTEM_YMU759, DIV_SYSTEM_DUMMY, DIV_SYSTEM_SOUND_UNIT, diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 0080f1da2..5cfbd9765 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2874,7 +2874,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_GB) { volMax=0; } - if (ins->type==DIV_INS_PET) { + if (ins->type==DIV_INS_PET || ins->type==DIV_INS_BEEPER) { volMax=1; } if (ins->type==DIV_INS_FDS) { @@ -2906,6 +2906,10 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Duty/Int"; dutyMax=10; } + if (ins->type==DIV_INS_BEEPER) { + dutyLabel="Pulse Width"; + dutyMax=255; + } if (ins->type==DIV_INS_AY8930) { dutyMax=255; } From cb63921747acedfbc64eb6667829c1fa001f4479 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 00:31:14 -0500 Subject: [PATCH 29/42] ZX beeper: set a more audible duty --- TODO.md | 4 +--- src/engine/platform/zxbeeper.h | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index 52eae1713..45d042792 100644 --- a/TODO.md +++ b/TODO.md @@ -5,9 +5,7 @@ - input pad - settings - RF5C68 system -- ZX beeper system -- Y8950 system -- maybe YMU759 ADPCM channel +- ZX beeper system overlay percussion - ADPCM chips - Game Boy envelope macro/sequence - rewrite the system name detection function anyway diff --git a/src/engine/platform/zxbeeper.h b/src/engine/platform/zxbeeper.h index 3a8b9ac2c..a91b518b0 100644 --- a/src/engine/platform/zxbeeper.h +++ b/src/engine/platform/zxbeeper.h @@ -62,7 +62,7 @@ class DivPlatformZXBeeper: public DivDispatch { vol(1), outVol(1), sPosition(0), - duty(3) {} + duty(64) {} }; Channel chan[6]; DivDispatchOscBuffer* oscBuf[6]; From c685b60a3cd4464e24aa881bed13a064e94abb0b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 00:36:45 -0500 Subject: [PATCH 30/42] ZX beeper: "register" view --- src/engine/platform/zxbeeper.cpp | 6 ++++-- src/engine/platform/zxbeeper.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/zxbeeper.cpp b/src/engine/platform/zxbeeper.cpp index 086ff0051..145cfe5b5 100644 --- a/src/engine/platform/zxbeeper.cpp +++ b/src/engine/platform/zxbeeper.cpp @@ -250,11 +250,12 @@ DivDispatchOscBuffer* DivPlatformZXBeeper::getOscBuffer(int ch) { } unsigned char* DivPlatformZXBeeper::getRegisterPool() { - return regPool; + ulaOut=sOffTimer?0x10:0x08; + return &ulaOut; } int DivPlatformZXBeeper::getRegisterPoolSize() { - return 112; + return 1; } void DivPlatformZXBeeper::reset() { @@ -274,6 +275,7 @@ void DivPlatformZXBeeper::reset() { curChan=0; sOffTimer=0; sampleBank=0; + ulaOut=0; } bool DivPlatformZXBeeper::keyOffAffectsArp(int ch) { diff --git a/src/engine/platform/zxbeeper.h b/src/engine/platform/zxbeeper.h index a91b518b0..4e5feabfc 100644 --- a/src/engine/platform/zxbeeper.h +++ b/src/engine/platform/zxbeeper.h @@ -73,7 +73,7 @@ class DivPlatformZXBeeper: public DivDispatch { QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} }; std::queue writes; - unsigned char lastPan; + unsigned char lastPan, ulaOut; int cycles, curChan, sOffTimer, delay; int tempL[32]; From 031a0fc1339b04744334d9bf13f6d0f36e02f81c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 01:02:28 -0500 Subject: [PATCH 31/42] OPNA: RSS write optimization --- src/engine/platform/ym2608.cpp | 20 ++++++++++++++++---- src/engine/platform/ym2608.h | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index a6855550c..f610467ab 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -696,6 +696,11 @@ void DivPlatformYM2608::tick(bool sysTick) { chan[15].freqChanged=false; } + if (writeRSSOff) { + immWrite(0x10,0x80|writeRSSOff); + writeRSSOff=0; + } + for (int i=16; i<512; i++) { if (pendingWrites[i]!=oldWrites[i]) { immWrite(i,pendingWrites[i]&0xff); @@ -731,6 +736,11 @@ void DivPlatformYM2608::tick(bool sysTick) { chan[i].keyOn=false; } } + + if (writeRSSOn) { + immWrite(0x10,writeRSSOn); + writeRSSOn=0; + } } int DivPlatformYM2608::dispatch(DivCommand c) { @@ -805,10 +815,10 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } break; } - if (c.chan>8) { // RSS TODO: improve rhythm writing strategy + if (c.chan>8) { // RSS if (skipRegisterWrites) break; + writeRSSOn|=(1<<(c.chan-9)); immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); - immWrite(0x10,0x00|(1<<(c.chan-9))); break; } DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); @@ -866,7 +876,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { break; } if (c.chan>8) { - immWrite(0x10,0x80|(1<<(c.chan-9))); + writeRSSOff|=1<<(c.chan-9); break; } chan[c.chan].keyOff=true; @@ -880,7 +890,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { break; } if (c.chan>8) { - immWrite(0x10,0x80|(1<<(c.chan-9))); + writeRSSOff|=1<<(c.chan-9); break; } chan[c.chan].keyOff=true; @@ -1340,6 +1350,8 @@ void DivPlatformYM2608::reset() { lastBusy=60; sampleBank=0; + writeRSSOff=0; + writeRSSOn=0; delay=0; diff --git a/src/engine/platform/ym2608.h b/src/engine/platform/ym2608.h index 6845f0a3e..5c6736295 100644 --- a/src/engine/platform/ym2608.h +++ b/src/engine/platform/ym2608.h @@ -104,6 +104,7 @@ class DivPlatformYM2608: public DivDispatch { DivPlatformAY8910* ay; unsigned char sampleBank; + unsigned char writeRSSOff, writeRSSOn; int delay; From ae5dcd3746d925afe7a0caa9637a531c44273513 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 01:05:05 -0500 Subject: [PATCH 32/42] OPNA: proper ADPCM muting --- src/engine/platform/ym2608.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index f610467ab..5e058dab3 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -817,7 +817,9 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } if (c.chan>8) { // RSS if (skipRegisterWrites) break; - writeRSSOn|=(1<<(c.chan-9)); + if (!isMuted[c.chan]) { + writeRSSOn|=(1<<(c.chan-9)); + } immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol)); break; } @@ -942,7 +944,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } if (c.chan>14) { - immWrite(0x101,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); + immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2); break; } if (c.chan>8) { @@ -1246,7 +1248,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { void DivPlatformYM2608::muteChannel(int ch, bool mute) { isMuted[ch]=mute; if (ch>14) { // ADPCM-B - //immWrite(0x11,isMuted[ch]?0:(chan[ch].pan<<6)); + immWrite(0x101,(isMuted[ch]?0:(chan[ch].pan<<6))|2); } if (ch>8) { // ADPCM-A immWrite(0x18+(ch-9),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol)); From 06e00ab17b661312577be5a2368203c213699a54 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 01:07:32 -0500 Subject: [PATCH 33/42] OPNA: per-channel oscilloscope --- src/engine/platform/sound/ymfm/ymfm_opn.h | 6 ++++++ src/engine/platform/ym2608.cpp | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_opn.h b/src/engine/platform/sound/ymfm/ymfm_opn.h index 1b47885b9..7fc76dac5 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opn.h +++ b/src/engine/platform/sound/ymfm/ymfm_opn.h @@ -546,6 +546,12 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + // get the engine + fm_engine* debug_fm_engine() { return &m_fm; } + ssg_engine* debug_ssg_engine() { return &m_ssg; } + adpcm_a_engine* debug_adpcm_a_engine() { return &m_adpcm_a; } + adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; } + protected: // internal helpers void update_prescale(uint8_t prescale); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 5e058dab3..4f05fc01c 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -428,7 +428,6 @@ double DivPlatformYM2608::NOTE_ADPCMB(int note) { void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; - /* ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); @@ -442,7 +441,6 @@ void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t l fmChan[i]=fme->debug_channel(i); adpcmAChan[i]=aae->debug_channel(i); } - */ for (size_t h=start; hdata[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); } @@ -485,7 +482,6 @@ void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t l } oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); - */ } } From 6288b485fc8b7c7e26d5e5e61925618b9407c9d1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 01:09:24 -0500 Subject: [PATCH 34/42] oops --- src/engine/platform/ym2608.cpp | 2 +- src/engine/platform/ym2610.cpp | 2 +- src/engine/platform/ym2610b.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 4f05fc01c..236e6a087 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -428,7 +428,7 @@ double DivPlatformYM2608::NOTE_ADPCMB(int note) { void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; - ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); + ymfm::ym2608::fm_engine* fme=fm->debug_fm_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 0797110c7..ce3e4b7c1 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -470,7 +470,7 @@ double DivPlatformYM2610::NOTE_ADPCMB(int note) { void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; - ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); + ymfm::ym2610::fm_engine* fme=fm->debug_fm_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index c9d29736c..5a36aaaf4 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -450,7 +450,7 @@ double DivPlatformYM2610B::NOTE_ADPCMB(int note) { void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; - ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); + ymfm::ym2610b::fm_engine* fme=fm->debug_fm_engine(); ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); From 297ab01d4c236779b281d8be74bac2cf33edd748 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 01:12:23 -0500 Subject: [PATCH 35/42] OPN: per-channel oscilloscope --- src/engine/platform/sound/ymfm/ymfm_opn.h | 2 ++ src/engine/platform/ym2203.cpp | 31 ++++++----------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_opn.h b/src/engine/platform/sound/ymfm/ymfm_opn.h index 7fc76dac5..74d2b01dd 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opn.h +++ b/src/engine/platform/sound/ymfm/ymfm_opn.h @@ -465,6 +465,8 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + protected: // internal helpers void update_prescale(uint8_t prescale); diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 33809d17e..10fde1f29 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -284,19 +284,12 @@ const char* DivPlatformYM2203::getEffectName(unsigned char effect) { void DivPlatformYM2203::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os; - /*ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); - ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); - ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); - ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); + ymfm::ym2203::fm_engine* fme=fm->debug_fm_engine(); - ymfm::ssg_engine::output_data ssgOut; - - ymfm::fm_channel>* fmChan[6]; - ymfm::adpcm_a_channel* adpcmAChan[6]; - for (int i=0; i<6; i++) { + ymfm::fm_channel>* fmChan[3]; + for (int i=0; i<3; i++) { fmChan[i]=fme->debug_channel(i); - adpcmAChan[i]=aae->debug_channel(i); - }*/ + } for (size_t h=start; hdata[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); } - ssge->get_last_out(ssgOut); - for (int i=6; i<9; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]; + for (int i=3; i<6; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]; } - - for (int i=9; i<15; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1); - } - - oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); - */ } } From 1ba5ac67661387523bf738fc9d0af8a8f037a5ff Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 14 May 2022 01:46:06 -0500 Subject: [PATCH 36/42] GUI: add ability to select entire row when clicking on row number --- TODO.md | 1 - src/gui/cursor.cpp | 49 ++++++++++++++++++++++++++++++++------------- src/gui/gui.cpp | 3 ++- src/gui/gui.h | 8 ++++---- src/gui/pattern.cpp | 15 ++++++++++++-- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/TODO.md b/TODO.md index 45d042792..34f7d1d7d 100644 --- a/TODO.md +++ b/TODO.md @@ -16,7 +16,6 @@ - try to find out why does VSlider not accept keyboard input - finish lock layout - if macros have release, note off should release them -- add ability to select entire row when clicking on row number - store edit/followOrders/followPattern state in config - add ability to select a column by double clicking - add ability to move selection by dragging diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 6bf4e76ff..972beee02 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -21,28 +21,48 @@ #include "actionUtil.h" -void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { +void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) { + DETERMINE_FIRST_LAST; + if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { curNibble=false; } - cursor.xCoarse=xCoarse; - cursor.xFine=xFine; - cursor.y=y; - selStart.xCoarse=xCoarse; - selStart.xFine=xFine; - selStart.y=y; - selEnd.xCoarse=xCoarse; - selEnd.xFine=xFine; - selEnd.y=y; + + if (fullRow) { + selStart.xCoarse=firstChannel; + selStart.xFine=0; + selEnd.xCoarse=lastChannel-1; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; + selStart.y=y; + selEnd.y=y; + } else { + cursor.xCoarse=xCoarse; + cursor.xFine=xFine; + cursor.y=y; + selStart.xCoarse=xCoarse; + selStart.xFine=xFine; + selStart.y=y; + selEnd.xCoarse=xCoarse; + selEnd.xFine=xFine; + selEnd.y=y; + } selecting=true; + selectingFull=fullRow; e->setMidiBaseChan(cursor.xCoarse); } -void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { +void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { if (!selecting) return; - selEnd.xCoarse=xCoarse; - selEnd.xFine=xFine; - selEnd.y=y; + if (selectingFull) { + DETERMINE_LAST; + selEnd.xCoarse=lastChannel-1; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; + selEnd.y=y; + } else { + selEnd.xCoarse=xCoarse; + selEnd.xFine=xFine; + selEnd.y=y; + } } void FurnaceGUI::finishSelection() { @@ -66,6 +86,7 @@ void FurnaceGUI::finishSelection() { selEnd.xFine^=selStart.xFine; } selecting=false; + selectingFull=false; // boundary check int chanCount=e->getTotalChannelCount(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e424de045..4f5a5e92f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2350,7 +2350,7 @@ bool FurnaceGUI::loop() { } sampleDragActive=false; if (selecting) { - cursor=selEnd; + if (!selectingFull) cursor=selEnd; finishSelection(); demandScrollX=true; if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && @@ -3975,6 +3975,7 @@ FurnaceGUI::FurnaceGUI(): chanOscDocked(false), */ selecting(false), + selectingFull(false), curNibble(false), orderNibble(false), followOrders(true), diff --git a/src/gui/gui.h b/src/gui/gui.h index 957ee16ae..182f04085 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1016,7 +1016,7 @@ class FurnaceGUI { */ SelectionPoint selStart, selEnd, cursor; - bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; + bool selecting, selectingFull, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; FurnaceGUIWindows curWindow, nextWindow, curWindowLast; float peak[2]; @@ -1121,7 +1121,7 @@ class FurnaceGUI { ImVec2 patWindowPos, patWindowSize; // pattern view specific - ImVec2 threeChars, twoChars; + ImVec2 fourChars, threeChars, twoChars; SelectionPoint sel1, sel2; int dummyRows, demandX; int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax; @@ -1262,8 +1262,8 @@ class FurnaceGUI { void commitSettings(); void processDrags(int dragX, int dragY); - void startSelection(int xCoarse, int xFine, int y); - void updateSelection(int xCoarse, int xFine, int y); + void startSelection(int xCoarse, int xFine, int y, bool fullRow=false); + void updateSelection(int xCoarse, int xFine, int y, bool fullRow=false); void finishSelection(); void moveCursor(int x, int y, bool select); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index d284b08fc..ae9bedb2c 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -88,11 +88,21 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int } } // row number + ImGui::PushStyleColor(ImGuiCol_Text,rowIndexColor); + if (settings.patRowsBase==1) { - ImGui::TextColored(rowIndexColor," %.2X ",i); + snprintf(id,31," %.2X ##PR_%d",i,i); } else { - ImGui::TextColored(rowIndexColor,"%3d ",i); + snprintf(id,31,"%3d ##PR_%d",i,i); } + ImGui::Selectable(id,false,ImGuiSelectableFlags_NoPadWithHalfSpacing,fourChars); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { + updateSelection(0,0,i,true); + } + if (ImGui::IsItemClicked()) { + startSelection(0,0,i,true); + } + ImGui::PopStyleColor(); // for each column for (int j=0; j