#include "fmdriver_pmd.h" #include "fmdriver_common.h" #include enum { SSG_ENV_STATE_OLD_AL, SSG_ENV_STATE_OLD_SR, SSG_ENV_STATE_OLD_RR, SSG_ENV_STATE_OLD_OFF, SSG_ENV_STATE_OLD_NEW = 0xff }; enum { SSG_ENV_STATE_NEW_OFF, SSG_ENV_STATE_NEW_AR, SSG_ENV_STATE_NEW_DR, SSG_ENV_STATE_NEW_SR, SSG_ENV_STATE_NEW_RR }; enum { SSG_ENV_PARAM_NEW_AR, SSG_ENV_PARAM_NEW_DR, SSG_ENV_PARAM_NEW_SR, SSG_ENV_PARAM_NEW_RR }; enum { SSG_ENV_PARAM_OLD_AL, SSG_ENV_PARAM_OLD_AD, SSG_ENV_PARAM_OLD_SR, SSG_ENV_PARAM_OLD_RR }; enum { LFO_WF_TRIANGLE1, LFO_WF_SAWTOOTH, LFO_WF_SQUARE, LFO_WF_RANDOM, LFO_WF_TRIANGLE2, LFO_WF_TRIANGLE3, LFO_WF_ONESHOT }; // 0790: PPZ8 static uint8_t pmd_adpcm_freq2key(uint16_t freq) { if (!freq) return 0x00; static const uint16_t freqtab[12] = { 0x8738, // e upper limit 0x8f42, 0x97c7, 0xa0cd, 0xaa5d, 0xb47e, 0xbf3a, 0xca99, 0xd6a5, 0xe369, 0xf0ee, 0xff42, }; int octave = 5; while (!(freq & 0x8000u)) { freq <<= 1; octave -= 1; } int key = 0; for (; key < 12; key++) { if (freq < freqtab[key]) break; } key += 5; if (key >= 12) { key -= 12; octave++; } if (octave < 0) return 0x00; if (octave > 8) return 0x8b; return (octave << 4) | key; } // 33f7: write standard // 341f: write extended // 3447 static void pmd_reg_write(struct fmdriver_work *work, struct driver_pmd *pmd, uint8_t addr, uint8_t data ) { work->opna_writereg(work, addr | (pmd->opna_a1 ? 0x100:0), data); } // 132b static void pmd_opna_a1_on(struct driver_pmd *pmd) { pmd->opna_a1 = true; } // 133d static void pmd_opna_a1_off(struct driver_pmd *pmd) { pmd->opna_a1 = false; } // 346f static uint8_t pmd_opna_read_ssgout(struct fmdriver_work *work) { return work->opna_readreg(work, 0x07); } /* // 331f static void pmd_stop_sound(struct fmdriver_work *work, struct driver_pmd *pmd) { // stop all FM sound except fm effect (CH6) // set release rate to 0xff bool fmeff = pmd->fmeff_playing; for (int a1 = 0; a1 < 2; a1++) { a1 ? pmd_opna_a1_on(pmd) : pmd_opna_a1_off(pmd); for (int s = 0; s < 4; s++) { for (int c = 0; c < 3; c++) { if (a1 && fmeff && (c == 2)) continue; pmd_reg_write(work, pmd, 0x80|c|(s<<2), 0xff); } } } // 3353 // keyoff for (int c = 0; c < 3; c++) { work->opna_writereg(work, 0x28, 0x00|c); } for (int c = 0; c < 3; c++) { if (fmeff && (c == 2)) continue; work->opna_writereg(work, 0x28, 0x04|c); } // 3375 // stop SSG sound if (!pmd->ssgeff_num) { if (pmd->ppsdrv_enabled) { // TODO: PPSDRV } work->opna_writereg(work, 0x07, 0xbf); } else { // 338f // don't stop ssg effect when playing uint8_t ssgout = pmd_opna_read_ssgout(work); ssgout &= 0x3f; ssgout |= 0x9b; work->opna_writereg(work, 0x07, ssgout); } // 33a2 // stop ADPCM if (!pmd->adpcmeff_playing && !pmd->adpcm_disabled) { work->opna_writereg(work, 0x101, 0x02); work->opna_writereg(work, 0x100, 0x01); work->opna_writereg(work, 0x110, 0x80); work->opna_writereg(work, 0x110, 0x18); } // 33c8 // stop PPZ8 if (work->ppz8_functbl) { for (int c = 0; c < 8; c++) { work->ppz8_functbl->channel_stop(work->ppz8, c); } } } */ /* // 11df static void pmd_mstop(struct fmdriver_work *work, struct driver_pmd *pmd) { pmd->playing = false; pmd->paused = false; pmd->fadeout_speed = 0; pmd->status2 = 0xff; pmd->fadeout_vol = 0xff; pmd_stop_sound(work, pmd); } */ // 1058 static void pmd_reset_state(struct driver_pmd *pmd) { pmd->fadeout_vol = 0; pmd->fadeout_speed = 0; pmd->faded_out = false; for (struct pmd_part *p = pmd->parts; p < (&pmd->parts[PMD_PART_NUM]); p++) { /* bool ext = p->mask.ext; bool effect = p->mask.effect; bool mml = p->mask.mml; bool ff = p->mask.ff; uint8_t note_proc = p->note_proc; zero reset part structure */ p->actual_note = 0xff; p->curr_note = 0xff; } for (struct pmd_part *p = &pmd->parts[PMD_PART_PPZ_1]; p <= (&pmd->parts[PMD_PART_PPZ_8]); p++) { p->loop.ended = true; } for (struct pmd_part *p = &pmd->parts[PMD_PART_FM_3B]; p <= (&pmd->parts[PMD_PART_FM_3D]); p++) { p->loop.ended = true; } // 1090 pmd->no_keyoff = false; pmd->status1 = 0; pmd->status2 = 0; pmd->meas_cnt = 0; pmd->tick_cnt = 0; pmd->timera_cnt = 0; pmd->timera_cnt_b = 0; for (int i = 0; i < 6; i++) pmd->fm_slotkey[i] = 0; pmd->fm3ex_fb_alg = 0; pmd->tonemask_fb_alg = false; pmd->adpcm_start = 0; pmd->adpcm_stop = 0; pmd->adpcm_release = 0x8000; pmd->ssgrhythm = 0; pmd->opnarhythm = 0; pmd->rand = 0; pmd->fm3ex_force = false; for (int s = 0; s < 4; s++) { pmd->fm3ex_det[s] = 0; } pmd->fm3ex_needed = 0; pmd->fm3ex_mode = 0x3f; pmd->opna_a1 = false; pmd->meas_len = 96; pmd->fm_voldown = pmd->fm_voldown_orig; pmd->ssg_voldown = pmd->ssg_voldown_orig; pmd->adpcm_voldown = pmd->adpcm_voldown_orig; pmd->ppz8_voldown = pmd->ppz8_voldown_orig; pmd->opnarhythm_voldown = pmd->opnarhythm_voldown_orig; pmd->pcm86_vol_spb = pmd->pcm86_vol_spb_orig; } // 0fa9 static bool pmd_data_init(struct driver_pmd *pmd) { if (pmd->data[-1] > 1) return false; pmd->opm_flag = pmd->data[-1]; if (pmd->datalen < 0x18) return false; if (pmd->data[0] != 0x18) { if (pmd->datalen < (0x18+2)) return false; pmd->tone_ptr = read16le(&pmd->data[0x18]); pmd->tone_included = true; } else { pmd->tone_included = false; } // 0fcd for (int pi = 0; pi <= PMD_PART_RHYTHM; pi++) { struct pmd_part *p = &pmd->parts[pi]; p->ptr = read16le(&pmd->data[pi*2]); if (pmd->datalen < (p->ptr + 1)) return false; p->len_cnt = 1; p->keystatus.off = true; p->keystatus.off_mask = true; p->md_cnt = 0xff; p->md_cnt_set = 0xff; p->md_cnt_b = 0xff; p->md_cnt_set_b = 0xff; p->actual_note = 0xff; p->curr_note = 0xff; if (pi <= PMD_PART_FM_6) { p->vol = 108; p->pan = 0xc0; p->fm_slotmask = 0xf0; p->fm_tone_slotmask = 0xff; } else if (pi <= PMD_PART_SSG_3) { p->vol = 8; p->ssg_mix = 0x07; p->ssg_env_state_old = SSG_ENV_STATE_OLD_OFF; } else if (pi == PMD_PART_ADPCM) { p->vol = 128; p->pan = 0xc0; } else if (pi == PMD_PART_RHYTHM) { p->vol = 15; } } // 1049 // part R pmd->r_offset = read16le(&pmd->data[0x16]); pmd->r_ptr = 0; return true; } // 111c static void pmd_reset_opna( struct fmdriver_work *work, struct driver_pmd *pmd ) { // enable FM 4-6ch and set IRQ mask work->opna_writereg(work, 0x29, 0x83); // set ssg noise freq to 0 and write pmd->ssg_noise_freq = 0; if (!pmd->ssgeff_priority) { work->opna_writereg(work, 0x06, 0x00); pmd->ssg_noise_freq_wrote = 0; } // 1139 // reset pan/ams/pms for (int a = 0; a < 2; a++) { a ? pmd_opna_a1_on(pmd) : pmd_opna_a1_off(pmd); for (int c = 0; c < 3; c++) { pmd_reg_write(work, pmd, 0xb4+c, 0xc0); } } pmd_opna_a1_off(pmd); // 1166 // reset HLFO pmd->opna_last22 = 0x00; work->opna_writereg(work, 0x22, 0x00); // 1170 // reset opnarhythm for (int i = 0; i < 6; i++) { pmd->opnarhythm_ilpan[i] = 0xcf; } work->opna_writereg(work, 0x10, 0xff); if (pmd->opnarhythm_voldown) { pmd->opnarhythm_tl = (0xc0 * (0x100-pmd->opnarhythm_voldown)) >> 10; } else { pmd->opnarhythm_tl = 0x30; } work->opna_writereg(work, 0x11, pmd->opnarhythm_tl); // 11a0 // adpcm: set limit address to 0xffff if (!pmd->adpcm_disabled) { work->opna_writereg(work, 0x10c, 0xff); work->opna_writereg(work, 0x10d, 0xff); } // 11b3 // reset ppz8 pan // dx=5 ah=0x13 al=i-1 call 0790 if (work->ppz8_functbl) { for (int c = 0; c < 8; c++) { work->ppz8_functbl->channel_pan(work->ppz8, c, 5); } } } // 2329 static void pmd_calc_tempo( struct driver_pmd *pmd ) { uint8_t timerb = 0x100 - pmd->timerb; int tempo = 0xff; if (timerb) { tempo = 0x112c / timerb; if ((0x112c % timerb) & 0x80) tempo++; if (tempo > 0xff) tempo = 0xff; } pmd->tempo = tempo; pmd->tempo_bak = tempo; } // 2348 static void pmd_calc_tempo_rev( struct driver_pmd *pmd ) { uint8_t tempo = pmd->tempo; int timerb = 0; if (tempo) { timerb = 0x112c / tempo; timerb = 0x100 - timerb; if ((0x112c % tempo) & 0x80) timerb--; if (timerb < 0) timerb = 0; } pmd->timerb = timerb; pmd->timerb_bak = timerb; } // 3e43 static void pmd_timerb_write( struct fmdriver_work *work, struct driver_pmd *pmd ) { // fast forward not implemented pmd->timerb_wrote = pmd->timerb; work->opna_writereg(work, 0x26, pmd->timerb); } // 32eb static void pmd_reset_timer( struct fmdriver_work *work, struct driver_pmd *pmd ) { pmd->timerb = 200; work->timerb = pmd->timerb; pmd->timerb_bak = pmd->timerb; pmd_calc_tempo(pmd); pmd_timerb_write(work, pmd); // set timera to 0x000 // enable both timers work->opna_writereg(work, 0x25, 0x00); work->opna_writereg(work, 0x24, 0x00); work->opna_writereg(work, 0x27, 0x3f); pmd->tick_cnt = 0; pmd->meas_cnt = 0; pmd->meas_len = 96; } // 2cf6 static void pmd_part_off_ssg( struct pmd_part *part ) { if (part->actual_note == 0xff) return; if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { part->ssg_env_state_new = SSG_ENV_STATE_NEW_RR; } else { part->ssg_env_state_old = SSG_ENV_STATE_OLD_RR; } } // 3102 static void pmd_lfo_reset( struct pmd_part *part ) { part->lfo_diff = 0; part->lfo = part->lfo_set; part->md_cnt = part->md_cnt_set; if (part->lfo_waveform == LFO_WF_SQUARE || part->lfo_waveform == LFO_WF_RANDOM) { part->lfo.speed = 1; } else { part->lfo.speed++; } } // 26ec static void pmd_opnarhythm_tl_write( struct fmdriver_work *work, struct driver_pmd *pmd, uint8_t vol ) { uint8_t outvol = pmd->fadeout_vol; if (outvol) { outvol = ((uint16_t)vol * (0x100-outvol)) >> 8; } work->opna_writereg(work, 0x11, outvol); } static uint8_t pmd_part_cmdload( struct driver_pmd *pmd, struct pmd_part *part ) { if (!part->ptr || (pmd->datalen < (part->ptr + 1))) { part->ptr = 0; return 0x80; } return pmd->data[part->ptr++]; } // 2f5a static void pmd_part_md_tick( struct pmd_part *part ) { if (--part->md_speed) return; part->md_speed = part->md_speed_set; if (!part->md_cnt) return; if (!(part->md_cnt & 0x80)) part->md_cnt--; // 2f73 uint8_t step = part->lfo.step; if (step & 0x80) { // 2f7a step = -step; step += part->md_depth; if (step & 0x80) { // 2f87 step = 0; if (part->md_depth & 0x80) step = 0x81; } else { // 2f81 step = -step; } } else { // 2f93 step += part->md_depth; if (step & 0x80) { step = 0; if (part->md_depth & 0x80) step = 0x7f; } } part->lfo.step = step; } // 2fa8 static uint16_t pmd_rand( struct driver_pmd *pmd, uint16_t range ) { uint16_t rand = pmd->rand * 0x103u; rand += 3; rand &= 0x7fffu; pmd->rand = rand; uint32_t mul = (uint32_t)rand * range; return mul / 0x7fffu; } // 2e7f static void pmd_lfo_tick_waveform( struct driver_pmd *pmd, struct pmd_part *part ) { if (part->lfo.speed != 1) { if (part->lfo.speed != 0xff) { part->lfo.speed--; } return; } part->lfo.speed = part->lfo_set.speed; switch (part->lfo_waveform) { case LFO_WF_TRIANGLE1: case LFO_WF_TRIANGLE2: case LFO_WF_TRIANGLE3: { int16_t deptha = u8s8(part->lfo.step); if (part->lfo_waveform == LFO_WF_TRIANGLE3) { deptha *= (deptha > 0) ? deptha : -deptha; } // 2ec6 part->lfo_diff = u16s16((uint16_t)part->lfo_diff + (uint16_t)deptha); if (!part->lfo_diff) pmd_part_md_tick(part); if (part->lfo.times != 0xff) { if (!--part->lfo.times) { uint8_t times = part->lfo_set.times; if (part->lfo_waveform != LFO_WF_TRIANGLE2) times *= 2; part->lfo.times = times; part->lfo.step = -part->lfo.step; } } } break; case LFO_WF_SAWTOOTH: { int16_t deptha = u8s8(part->lfo.step); part->lfo_diff = u16s16((uint16_t)part->lfo_diff + (uint16_t)deptha); if (part->lfo.times != 0xff) { if (!--part->lfo.times) { // 2f09 part->lfo_diff = -part->lfo_diff; pmd_part_md_tick(part); part->lfo.times = part->lfo_set.times * 2; } } } break; case LFO_WF_SQUARE: part->lfo_diff = u8s8(part->lfo.step) * u8s8(part->lfo.times); pmd_part_md_tick(part); part->lfo.step = -part->lfo.step; break; case LFO_WF_RANDOM: { // 2f40 int deptha = u8s8(part->lfo.step); if (deptha < 0) deptha = -deptha; uint16_t range = (unsigned)deptha * part->lfo.times; uint16_t rand = pmd_rand(pmd, range*2); rand -= range; part->lfo_diff = rand; pmd_part_md_tick(part); } break; case LFO_WF_ONESHOT: // 2f18 if (part->lfo.times) { if (part->lfo.times != 0xff) { part->lfo.times--; } part->lfo_diff = u16s16((uint16_t)part->lfo_diff + (uint16_t)u8s8(part->lfo.step)); } break; } } // 2e48 // returns true if lfo_diff updated static bool pmd_lfo_tick( struct driver_pmd *pmd, struct pmd_part *part ) { if (part->lfo.delay) { part->lfo.delay--; return false; } int16_t lfo_diff_prev = part->lfo_diff; if (part->flagext.lfo) { // 2e58 uint8_t cnt = pmd->timera_cnt - pmd->timera_cnt_b; for (int i = 0; i < cnt; i++) { pmd_lfo_tick_waveform(pmd, part); } } else { // 2e6f pmd_lfo_tick_waveform(pmd, part); } // 2e76 return part->lfo_diff != lfo_diff_prev; } // 2493 static void pmd_part_lfo_flip(struct pmd_part *part) { int16_t tmpi16; uint8_t tmpu8; struct pmd_lfo tmplfo; struct pmd_part_lfo_flags tmplfof; struct pmd_part_flagext tmpflagext; tmpi16 = part->lfo_diff_b; part->lfo_diff_b = part->lfo_diff; part->lfo_diff = tmpi16; tmplfof = part->lfof_b; part->lfof_b = part->lfof; part->lfof = tmplfof; tmpflagext = part->flagext_b; part->flagext_b = part->flagext; part->flagext = tmpflagext; tmplfo = part->lfo_b; part->lfo_b = part->lfo; part->lfo = tmplfo; tmplfo = part->lfo_set_b; part->lfo_set_b = part->lfo_set; part->lfo_set = tmplfo; tmpu8 = part->md_depth_b; part->md_depth_b = part->md_depth; part->md_depth = tmpu8; tmpu8 = part->md_speed_b; part->md_speed_b = part->md_speed; part->md_speed = tmpu8; tmpu8 = part->md_speed_set_b; part->md_speed_set_b = part->md_speed_set; part->md_speed_set = tmpu8; tmpu8 = part->lfo_waveform_b; part->lfo_waveform_b = part->lfo_waveform; part->lfo_waveform = tmpu8; tmpu8 = part->md_cnt_b; part->md_cnt_b = part->md_cnt; part->md_cnt = tmpu8; tmpu8 = part->md_cnt_set_b; part->md_cnt_set_b = part->md_cnt_set; part->md_cnt_set = tmpu8; } // 30a6 static void pmd_lfo_tick_if_needed_hlfo( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { part->hlfo_delay = part->hlfo_delay_set; if (part->hlfo_delay) { pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, part->pan & 0xc0); } // 30c0 part->slot_delay_cnt = part->slot_delay; if (part->lfof.freq || part->lfof.vol) { if (!part->lfof.sync) { pmd_lfo_reset(part); } pmd_lfo_tick(pmd, part); } // 30db if (part->lfof_b.freq || part->lfof_b.vol) { if (!part->lfof_b.sync) { pmd_part_lfo_flip(part); pmd_lfo_reset(part); pmd_part_lfo_flip(part); } pmd_part_lfo_flip(part); pmd_lfo_tick(pmd, part); pmd_part_lfo_flip(part); } } // 3086 static void pmd_lfo_tick_if_needed( struct driver_pmd *pmd, struct pmd_part *part ) { if (part->lfof.freq || part->lfof.vol) { pmd_lfo_tick(pmd, part); } // 3091 if (part->lfof_b.freq || part->lfof_b.vol) { pmd_part_lfo_flip(part); pmd_lfo_tick(pmd, part); pmd_part_lfo_flip(part); } } // 31ce static void pmd_ssg_env_tick_new( struct pmd_part *part ) { switch (part->ssg_env_state_new) { case SSG_ENV_STATE_NEW_AR: // 31d2 { uint8_t ar = part->ssg_env_param[SSG_ENV_PARAM_NEW_AR]; if (((unsigned)(ar-1)) & 0x80) { // 31ff if (part->ssg_env_param_set[SSG_ENV_PARAM_NEW_AR]) { part->ssg_env_param[SSG_ENV_PARAM_NEW_AR]++; } } else { // 31d9 part->ssg_env_vol = u8s8(part->ssg_env_vol + ar); if (part->ssg_env_vol <= 0xf) { // 31e4 part->ssg_env_param[SSG_ENV_PARAM_NEW_AR] = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_AR] - 0x10; } else { // 31ee part->ssg_env_vol = 0xf; part->ssg_env_state_new = SSG_ENV_STATE_NEW_DR; if (part->ssg_env_param_sl == 0xf) { part->ssg_env_state_new = SSG_ENV_STATE_NEW_SR; } } } } break; case SSG_ENV_STATE_NEW_DR: // 320d { uint8_t dr = part->ssg_env_param[SSG_ENV_PARAM_NEW_DR]; if (((unsigned)(dr-1)) & 0x80) { // 3238 if (part->ssg_env_param_set[SSG_ENV_PARAM_NEW_DR]) { part->ssg_env_param[SSG_ENV_PARAM_NEW_DR]++; } } else { // 3214 part->ssg_env_vol = u8s8(part->ssg_env_vol - dr); uint8_t sl = part->ssg_env_param_sl; if ((part->ssg_env_vol >= 0) && (part->ssg_env_vol >= sl)) { // 3223 uint8_t newdr = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_DR] - 0x10; if (newdr & 0x80) newdr += newdr; part->ssg_env_param[SSG_ENV_PARAM_NEW_DR] = newdr; } else { // 3231 part->ssg_env_vol = sl; part->ssg_env_state_new = SSG_ENV_STATE_NEW_SR; } } } break; case SSG_ENV_STATE_NEW_SR: // 3246 { uint8_t sr = part->ssg_env_param[SSG_ENV_PARAM_NEW_SR]; if (((unsigned)(sr-1)) & 0x80) { // 3266 if (part->ssg_env_param_set[SSG_ENV_PARAM_NEW_SR]) { part->ssg_env_param[SSG_ENV_PARAM_NEW_SR]++; } } else { // 324d part->ssg_env_vol = u8s8(part->ssg_env_vol - sr); if (part->ssg_env_vol < 0) part->ssg_env_vol = 0; uint8_t newsr = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_SR] - 0x10; if (newsr & 0x80) newsr += newsr; part->ssg_env_param[SSG_ENV_PARAM_NEW_SR] = newsr; } } break; default: // SSG_ENV_STATE_NEW_RR // 3273 { uint8_t rr = part->ssg_env_param[SSG_ENV_PARAM_NEW_RR]; if (((unsigned)(rr-1)) & 0x80) { // 3291 if (part->ssg_env_param_set[SSG_ENV_PARAM_NEW_RR]) { part->ssg_env_param[SSG_ENV_PARAM_NEW_RR]++; } } else { // 327a part->ssg_env_vol = u8s8((unsigned)part->ssg_env_vol - rr); if (part->ssg_env_vol < 0) part->ssg_env_vol = 0; uint8_t newrr = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_RR]; newrr += newrr; newrr -= 0x10; part->ssg_env_param[SSG_ENV_PARAM_NEW_RR] = newrr; } } break; } } // 31b9 // returns true if volume update needed static bool pmd_ssg_env_tick_new_check( struct pmd_part *part ) { if (part->ssg_env_state_new == SSG_ENV_STATE_NEW_OFF) return false; int8_t prev_vol = part->ssg_env_vol; pmd_ssg_env_tick_new(part); return part->ssg_env_vol != prev_vol; } // 3060 static uint8_t pmd_part_lfo_init_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t note ) { uint8_t n = note & 0xf; if (n == 0xc) { note = part->curr_note; n = note & 0xf; } // 3072 part->curr_note = note; if (n == 0xf) { pmd_lfo_tick_if_needed(pmd, part); return note; } part->portamento_diff = 0; if (pmd->no_keyoff) { pmd_lfo_tick_if_needed(pmd, part); } else { pmd_lfo_tick_if_needed_hlfo(work, pmd, part); } return note; } // 2fc4 static uint8_t pmd_part_lfo_init_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t note ) { uint8_t n = note & 0xf; if (n == 0xc) { note = part->curr_note; n = note & 0xf; } // 2fd6 part->curr_note = note; if (n == 0xf) { pmd_lfo_tick_if_needed(pmd, part); return note; } // 2fe1 part->portamento_diff = 0; if (pmd->no_keyoff) { pmd_lfo_tick_if_needed(pmd, part); return note; } if (part->ssg_env_state_old != SSG_ENV_STATE_OLD_NEW) { // 2ff6 part->ssg_env_state_old = SSG_ENV_STATE_OLD_AL; part->ssg_env_vol = 0; // isn't this reversed? uint8_t al = part->ssg_env_param_set[SSG_ENV_PARAM_OLD_AL] = part->ssg_env_param[SSG_ENV_PARAM_OLD_AL]; if (!al) { // 3008 part->ssg_env_state_old = SSG_ENV_STATE_OLD_SR; part->ssg_env_vol = part->ssg_env_param_set[SSG_ENV_PARAM_OLD_AD]; } // 3012 part->ssg_env_param_set[SSG_ENV_PARAM_OLD_SR] = part->ssg_env_param[SSG_ENV_PARAM_OLD_SR]; part->ssg_env_param_set[SSG_ENV_PARAM_OLD_RR] = part->ssg_env_param[SSG_ENV_PARAM_OLD_RR]; pmd_lfo_tick_if_needed_hlfo(work, pmd, part); } else { // 3021 part->ssg_env_param[SSG_ENV_PARAM_NEW_AR] = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_AR] - 0x10; int dr = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_DR] - 0x10; if (dr < 0) dr += dr; part->ssg_env_param[SSG_ENV_PARAM_NEW_DR] = dr; int sr = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_SR] - 0x10; if (sr < 0) sr += sr; part->ssg_env_param[SSG_ENV_PARAM_NEW_SR] = sr; uint8_t rr = part->ssg_env_param_set[SSG_ENV_PARAM_NEW_RR]; rr += rr; rr -= 0x10; part->ssg_env_param[SSG_ENV_PARAM_NEW_RR] = rr; // 304f part->ssg_env_vol = part->ssg_env_param_al; part->ssg_env_state_new = SSG_ENV_STATE_NEW_AR; // 31b9 pmd_ssg_env_tick_new_check(part); pmd_lfo_tick_if_needed_hlfo(work, pmd, part); } return note; } // 0e5e static void pmd_ssgeff_stop( struct fmdriver_work *work, struct driver_pmd *pmd ) { // TODO: PPSDRV work->opna_writereg(work, 0x0a, 0x00); uint8_t ssgout = pmd_opna_read_ssgout(work); ssgout |= (1<<2) | (1<<5); work->opna_writereg(work, 0x07, ssgout); pmd->ssgeff_priority = 0; pmd->ssgeff_num = 0xff; pmd->ssgeff_on = false; } // 0def static void pmd_ssgeff_advance( struct fmdriver_work *work, struct driver_pmd *pmd ) { const struct pmd_ssgeff_data *ssgeff = pmd->ssgeff_ptr; if (ssgeff->wait == 0xff) { pmd_ssgeff_stop(work, pmd); return; } pmd->ssgeff_wait = ssgeff->wait; pmd->ssgeff_tonefreq = ssgeff->tone_freq; work->opna_writereg(work, 0x04, pmd->ssgeff_tonefreq); work->opna_writereg(work, 0x05, pmd->ssgeff_tonefreq>>8); pmd->ssgeff_noisefreq = ssgeff->noise_freq; work->opna_writereg(work, 0x06, pmd->ssgeff_noisefreq); pmd->ssg_noise_freq_wrote = pmd->ssgeff_noisefreq; uint8_t ssgout = pmd_opna_read_ssgout(work); ssgout &= ~((1<<2) | (1<<5)); ssgout |= ((!ssgeff->tone_mix)<<2) | ((!ssgeff->noise_mix)<<5); pmd->ssgeff_tone_mix = ssgeff->tone_mix; pmd->ssgeff_noise_mix = ssgeff->noise_mix; work->opna_writereg(work, 0x07, ssgout); work->opna_writereg(work, 0x0a, ssgeff->env_level); work->opna_writereg(work, 0x0b, ssgeff->env_freq); work->opna_writereg(work, 0x0c, ssgeff->env_freq>>8); work->opna_writereg(work, 0x0d, ssgeff->env_waveform); pmd->ssgeff_tonefreq_add = ssgeff->tonefreq_add; pmd->ssgeff_noisefreq_add = ssgeff->noisefreq_add; pmd->ssgeff_noisefreq_cnt_set = ssgeff->noisefreq_wait; pmd->ssgeff_noisefreq_cnt = pmd->ssgeff_noisefreq_cnt_set; pmd->ssgeff_ptr++; } // 0e8d static void pmd_ssgeff_tick_idle( struct fmdriver_work *work, struct driver_pmd *pmd ) { pmd->ssgeff_tonefreq += pmd->ssgeff_tonefreq_add; work->opna_writereg(work, 0x04, pmd->ssgeff_tonefreq); work->opna_writereg(work, 0x05, pmd->ssgeff_tonefreq>>8); // pmd_opna_read_ssgout(work); if (!pmd->ssgeff_noisefreq_add && !pmd->ssgeff_noisefreq_cnt_set) return; if (--pmd->ssgeff_noisefreq_cnt) return; pmd->ssgeff_noisefreq_cnt = pmd->ssgeff_noisefreq_cnt_set; pmd->ssgeff_noisefreq += pmd->ssgeff_noisefreq_add; work->opna_writereg(work, 0x06, pmd->ssgeff_noisefreq); pmd->ssg_noise_freq_wrote = pmd->ssgeff_noisefreq; } // 0dde static void pmd_ssgeff_tick( struct fmdriver_work *work, struct driver_pmd *pmd ) { if (--pmd->ssgeff_wait) { pmd_ssgeff_tick_idle(work, pmd); } else { pmd_ssgeff_advance(work, pmd); } } // 2dda static uint8_t *pmd_get_toneptr( struct driver_pmd *pmd, uint8_t tonenum ) { if (pmd->tone_included) { for (uint16_t toneptr = pmd->tone_ptr; ; toneptr += 0x1a) { if (pmd->datalen < (toneptr+0x1a)) return 0; if (pmd->data[toneptr] == tonenum) { return &pmd->data[toneptr+1]; } } } // TODO: external tone return 0; } // 2cbc static void pmd_part_do_keyoff_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t out = pmd->proc_ch-1; int ch_ind = pmd->proc_ch-1; if (pmd->opna_a1) { ch_ind += 3; out |= 4; } pmd->fm_slotkey[ch_ind] &= ~part->fm_slotmask; out |= pmd->fm_slotkey[ch_ind]; work->opna_writereg(work, 0x28, out); } // 2e15 static bool pmd_part_tone_slotmask_stop( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t mask = part->fm_tone_slotmask; if (!mask) return false; uint8_t addr = 0x3f + pmd->proc_ch; for (int s = 0; s < 4; s++, addr += 4) { if (!(mask & (1<<(7-s)))) continue; // TL pmd_reg_write(work, pmd, addr, 0x7f); // SL/RR (why 0x7f instead of 0xff?) pmd_reg_write(work, pmd, addr+0x40, 0x7f); } pmd_part_do_keyoff_fm(work, pmd, part); return true; } // 3ee7 static const uint8_t pmd_alg_output_slot_table[8] = { 0x80, 0x80, 0x80, 0x80, 0xa0, 0xe0, 0xe0, 0xf0 }; // 3eef static const uint8_t pmd_alg_tone_slot_table[8] = { 0xee, 0xee, 0xee, 0xee, 0xcc, 0x88, 0x88, 0x00 }; // 2dce static void pmd_part_tone_tl_update( struct pmd_part *part, uint8_t const *toneptr ) { for (int s = 0; s < 4; s++) { part->fm_tone_tl[s] = toneptr[0x04+s]; } } // 2d0d static void pmd_part_tone_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t const * const toneptr = pmd_get_toneptr(pmd, part->tonenum); if (!toneptr) return; if (!pmd_part_tone_slotmask_stop(work, pmd, part)) { // 2d15 //toneptr += 4; } else { // 2d1b uint8_t fb_alg = toneptr[0x18]; if (pmd->tonemask_fb_alg) fb_alg = part->fb_alg; if (!pmd->opna_a1 && pmd->proc_ch == 3) { if (pmd->tonemask_fb_alg) { // 2d43 fb_alg = pmd->fm3ex_fb_alg; } else { // 2d49 if (!(part->fm_slotmask & 0x10)) { fb_alg &= 0x7; fb_alg |= pmd->fm3ex_fb_alg & 0x38; } // 2d59 pmd->fm3ex_fb_alg = fb_alg; } } // 2d5d pmd_reg_write(work, pmd, 0xaf+pmd->proc_ch, fb_alg); part->fb_alg = fb_alg; uint8_t outslot = pmd_alg_output_slot_table[fb_alg&7]; if (!(part->vol_lfo_slotmask & 0xf)) { part->vol_lfo_slotmask = outslot; } if (!(part->vol_lfo_slotmask_b & 0xf)) { part->vol_lfo_slotmask_b = outslot; } // 2d83 part->fm_slotout = outslot; uint8_t tone_slotmask = part->fm_tone_slotmask; uint8_t tl_slotmask = pmd_alg_tone_slot_table[fb_alg&7] & tone_slotmask; for (int s = 0; s < 4; s++) { if (tone_slotmask & (1<<(7-s))) { pmd_reg_write(work, pmd, 0x2f+pmd->proc_ch+s*4, toneptr[0x00+s]); } } for (int s = 0; s < 4; s++) { if (tl_slotmask & (1<<(7-s))) { pmd_reg_write(work, pmd, 0x3f+pmd->proc_ch+s*4, toneptr[0x04+s]); } } for (int s = 0; s < 4; s++) { if (tone_slotmask & (1<<(3-s))) { pmd_reg_write(work, pmd, 0x4f+pmd->proc_ch+s*4, toneptr[0x08+s]); pmd_reg_write(work, pmd, 0x6f+pmd->proc_ch+s*4, toneptr[0x10+s]); } if (tone_slotmask & (1<<(7-s))) { pmd_reg_write(work, pmd, 0x5f+pmd->proc_ch+s*4, toneptr[0x0c+s]); pmd_reg_write(work, pmd, 0x7f+pmd->proc_ch+s*4, toneptr[0x14+s]); } } } // 2dce pmd_part_tone_tl_update(part, toneptr); } #include "pmd_ssgeff.h" // 0d28, 0d09 static void pmd_ssg_effect( struct fmdriver_work *work, struct driver_pmd *pmd, uint8_t effnum ) { if (effnum > PMD_SSGEFF_CNT) return; pmd->ssgeff_internal = true; if (pmd->ssgeff_disabled) return; // TODO: PPSDRV // 0da4 pmd->ssgeff_num = effnum; // 3ef7 if (pmd->ssgeff_priority > pmd_ssgeff_table[effnum].priority) return; // TODO: PPSDRV // 0dc5 pmd->ssgeff_ptr = pmd_ssgeff_table[effnum].data; pmd->parts[PMD_PART_SSG_3].mask.effect = true; pmd_ssgeff_advance(work, pmd); pmd->ssgeff_priority = pmd_ssgeff_table[effnum].priority; pmd->ssgeff_on = true; } // 1a58 static void pmd_cmd_null_16( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { for (int i = 0; i < 16; i++) { pmd_part_cmdload(pmd, part); } } // 1a5b static void pmd_cmd_null_6( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { for (int i = 0; i < 6; i++) { pmd_part_cmdload(pmd, part); } } // 1a5c static void pmd_cmd_null_5( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { for (int i = 0; i < 5; i++) { pmd_part_cmdload(pmd, part); } } // 1a5d static void pmd_cmd_null_4( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { for (int i = 0; i < 4; i++) { pmd_part_cmdload(pmd, part); } } // 1a5e static void pmd_cmd_null_3( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { for (int i = 0; i < 3; i++) { pmd_part_cmdload(pmd, part); } } // 1a5f static void pmd_cmd_null_2( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { for (int i = 0; i < 2; i++) { pmd_part_cmdload(pmd, part); } } // 1a60 static void pmd_cmd_null_1( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { pmd_part_cmdload(pmd, part); } // 1a61 static void pmd_cmd_null_0( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { } // 271a static uint8_t pmd_part_note_transpose( struct pmd_part *part, uint8_t note ) { if (note == 0x0f) return note; int8_t transpose = u8s8((uint8_t)(part->transpose + part->transpose_master)); if (!transpose) return note; uint8_t octave = note >> 4; note &= 0x0f; if (transpose < 0) { int newnote = note + transpose; while (newnote < 0) { octave--; newnote += 0xc; } note = newnote; } else { int newnote = note + transpose; while (newnote >= 0xc) { octave++; newnote -= 0xc; } note = newnote; } return octave<<4 | note; } // 2771 static void pmd_note_freq_fm( struct pmd_part *part, uint8_t note ) { if ((note & 0xf) == 0xf) { // 2798 part->actual_note = 0xff; if (!part->lfof.freq && !part->lfof_b.freq) { part->actual_freq = 0; } return; } // 277b part->actual_note = note; static const uint16_t fm_tonetable[0x10] = { // 3eb7 // round(144*440*(2**((-9+i)/12))*(1<<17)/7987200) 0x026a, 0x028f, 0x02b6, 0x02df, 0x030b, 0x0339, 0x036a, 0x039e, 0x03d5, 0x0410, 0x044e, 0x048f, 0, 0, 0, 0 }; uint16_t freq = fm_tonetable[note & 0x0f]; freq |= (note & 0x70) << 7; part->actual_freq = freq; } // 27a8 static void pmd_note_freq_ssg( struct pmd_part *part, uint8_t note ) { if ((note & 0xf) == 0xf) { part->actual_note = 0xff; if (!part->lfof.freq && !part->lfof_b.freq) { part->actual_freq = 0; } return; } part->actual_note = note; static const uint16_t ssg_tonetable[0x10] = { // 3ecf // round(7987200/(64*440*(2**-4)*(2.0**((3+i)/12)))) 0x0ee8, 0x0e12, 0x0d48, 0x0c89, 0x0bd5, 0x0b2b, 0x0a8a, 0x09f3, 0x0964, 0x08dd, 0x085e, 0x07e6, 0, 0, 0, 0 }; uint8_t octave = note >> 4; uint16_t tonefreq = ssg_tonetable[note & 0x0f]; if (octave) { tonefreq >>= (octave-1); bool inc = tonefreq & 1; tonefreq >>= 1; tonefreq += inc; } part->actual_freq = tonefreq; } // 0671 static void pmd_note_freq_adpcm( struct pmd_part *part, uint8_t note ) { if ((note & 0xf) == 0xf) { // 2798 part->actual_note = 0xff; if (!part->lfof.freq && !part->lfof_b.freq) { part->actual_freq = 0; } return; } part->actual_note = note; static const uint16_t adpcm_tonetable[0x10] = { // 0788 // ??? // different from round((16000*2*0x10000/(8000000/144))*(2**((i-7)/12))) 0x6264, 0x6840, 0x6e74, 0x7506, 0x7bfc, 0x835e, 0x8b2e, 0x9376,// g 0x9c3c, 0xa588, 0xaf62, 0xb9d0, 0, 0, 0, 0 }; uint8_t octave = note >> 4; uint16_t tonefreq = adpcm_tonetable[note & 0x0f]; if (octave <= 5) { tonefreq >>= (5 - octave); } else { // 06a8 octave = 5; if (!(tonefreq & 0x8000)) { tonefreq <<= 1; octave++; } part->actual_note = (part->actual_note & 0xf) | (octave << 4); } part->actual_freq = tonefreq; } // 0c97 static void pmd_note_freq_ppz8( struct pmd_part *part, uint8_t note ) { if ((note & 0xf) == 0xf) { // 0cd8 part->actual_note = 0xff; if (!part->lfof.freq && !part->lfof_b.freq) { part->actual_freq = 0; part->actual_freq_upper = 0; } return; } part->actual_note = note; static const uint16_t ppz8_tonetable[0x10] = { // 0cf1 // documented in ppz8.doc // different from round(0x8000*(2**(i/12))) 0x8000, 0x87a6, 0x8fb3, 0x9838, 0xa146, 0xaade, 0xb4ff, 0xbfcc, 0xcb34, 0xd747, 0xe418, 0xf1a5, 0, 0, 0, 0 }; int octave = (note >> 4) - 4; uint32_t freq = ppz8_tonetable[note & 0x0f]; if (octave < 0) { freq >>= -octave; } else { freq <<= octave; } part->actual_freq = freq; part->actual_freq_upper = freq >> 16; } // 14a0 static void pmd_part_calc_gate( struct driver_pmd *pmd, struct pmd_part *part ) { if (pmd->datalen < (part->ptr + 1)) { if (pmd->data[part->ptr] == 0xc1) { part->ptr++; part->gate = 0; return; } } uint8_t gate = part->gate_abs; if (part->gate_rel) { // 14ae gate += ((uint16_t)part->len_cnt * part->gate_rel) >> 8; } // 14b8 if (part->gate_rand_range) { // 14be uint16_t range = part->gate_rand_range & 0x7f; range++; uint16_t rand = pmd_rand(pmd, range); if (part->gate_rand_range & 0x80) { gate -= rand; } else { gate += rand; } } // 14de if (part->gate_min) { int len = part->len_cnt - part->gate_min; if (len < 0) { part->gate = 0; return; } if (gate >= len) { gate = len; } } // 14f2 part->gate = gate; } // 2b4c // bl = slotmask // al = vol static void pmd_fm_vol_out_sub( uint8_t slotmask, uint8_t *table, uint8_t vol ) { for (int s = 0; s < 4; s++) { if (!(slotmask & (1<<(7-s)))) continue; if (!(vol & 0x80)) { int newvol = table[s] - vol; if (newvol < 0) newvol = 0; table[s] = newvol; } else { int newvol = table[s] + (uint8_t)(-vol); if (newvol > 0xff) newvol = 0xff; table[s] = newvol; } } } // 2a38 static void pmd_fm_vol_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t slotmask = part->fm_slotmask; if (!slotmask) return; uint8_t vol = part->volume_save; if (vol) { vol--; } else { vol = part->vol; } // 2a4e if (part != &pmd->parts[PMD_PART_FM_EFF]) { // 2a56 if (pmd->fm_voldown) { uint8_t voldown = -pmd->fm_voldown; vol = (vol * voldown) >> 8; } if (pmd->fadeout_vol) { uint8_t fadeout = -pmd->fadeout_vol; vol = (vol * fadeout) >> 8; } } // 2a72 // 4, 3, 2, 1 uint8_t table[4] = { 0x80, 0x80, 0x80, 0x80 }; vol ^= 0xff; uint8_t oslotmask = slotmask & part->fm_slotout; uint8_t update_slots = oslotmask; for (int s = 0; s < 4; s++) { if (oslotmask & (1<<(7-s))) table[s] = vol; } // 2aa8 if (vol != 0xff) { if (part->lfof.vol) { uint8_t lslotmask = slotmask & part->vol_lfo_slotmask; update_slots |= lslotmask; pmd_fm_vol_out_sub(lslotmask, table, part->lfo_diff); } if (part->lfof_b.vol) { uint8_t lslotmask = slotmask & part->vol_lfo_slotmask_b; update_slots |= lslotmask; pmd_fm_vol_out_sub(lslotmask, table, part->lfo_diff_b); } } // 2ad3 for (int s = 0; s < 4; s++) { if (!(update_slots & (1<<(7-s)))) continue; static const uint8_t tladdr[4] = { 0x4b, 0x43, 0x47, 0x3f }; static const uint8_t toneind[4] = { 3, 1, 2, 0 }; int sout = table[s] + part->fm_tone_tl[toneind[s]]; if (sout > 0xff) sout = 0xff; sout -= 0x80; if (sout < 0) sout = 0; pmd_reg_write(work, pmd, tladdr[s]+pmd->proc_ch, sout); } } // 2b70 static void pmd_ssg_vol_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_OFF || (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW && part->ssg_env_state_new == SSG_ENV_STATE_NEW_OFF)) { return; } uint8_t vol = part->volume_save; if (vol) { vol--; } else { vol = part->vol; } // 2b91 if (pmd->ssg_voldown) { uint8_t voldown = -pmd->ssg_voldown; vol = vol * voldown >> 8; } if (pmd->fadeout_vol) { uint8_t fadeout = -pmd->fadeout_vol; vol = vol * fadeout >> 8; } // 2bad if (vol) { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { // 2bb7 uint8_t envvol = part->ssg_env_vol; if (!envvol) { // 2bd9 vol = 0; // -> 2c0f } else { vol = (vol * (envvol+1)) >> 3; if (vol & 1) { vol >>= 1; vol++; } else { vol >>= 1; } } } else { // 2bd4 vol += part->ssg_env_vol; if (vol & 0x80) vol = 0; // -> 2c0f if (vol > 0xf) vol = 0xf; } if (vol) { // 2be4 if (part->lfof.vol || part->lfof_b.vol) { int32_t lfovol = 0; // TODO: if (part->lfof.vol) lfovol += part->lfo_diff; if (part->lfof_b.vol) lfovol += part->lfo_diff_b; lfovol += vol; vol = lfovol; if (lfovol < 0) { vol = 0; } else if (lfovol > 0xf) { vol = 0xf; } } } } // 2c0f work->opna_writereg(work, 0x07+pmd->proc_ch, vol); } // 049a static void pmd_adpcm_vol_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t vol = part->volume_save; if (!vol) vol = part->vol; // 04a4 if (pmd->adpcm_voldown) { uint8_t voldown = -pmd->adpcm_voldown; vol = vol * voldown >> 8; } // 04b3 if (pmd->fadeout_vol) { uint8_t fadeout = -pmd->fadeout_vol; fadeout = ((uint16_t)fadeout * fadeout) >> 8; vol = vol * fadeout >> 8; } if (vol) { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { // 04d0 uint8_t envvol = part->ssg_env_vol; if (!envvol) { // 04fd vol = 0; // -> 053f } else { int ivol = (vol * (envvol+1)) >> 3; if (ivol & 1) { ivol >>= 1; ivol++; } else { ivol >>= 1; } vol = ivol; } } else { // 04e8 int newvol = vol + (part->ssg_env_vol << 4); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; vol = newvol; } if (vol) { if (part->lfof.vol || part->lfof_b.vol) { int32_t lfovol = 0; if (part->lfof.vol) vol += part->lfo_diff; if (part->lfof_b.vol) vol += part->lfo_diff_b; lfovol += vol; vol = lfovol; if (lfovol < 0) { vol = 0; } else if (lfovol > 0xff) { vol = 0xff; } } } } // 053f work->opna_writereg(work, 0x10b, vol); } // 0b33 static void pmd_ppz8_vol_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t vol = part->volume_save; if (!vol) vol = part->vol; // 0b3f if (pmd->ppz8_voldown) { uint8_t voldown = -pmd->ppz8_voldown; vol = vol * voldown >> 8; } // 0b4c if (pmd->fadeout_vol) { uint8_t fadeout = -pmd->fadeout_vol; vol = vol * fadeout >> 8; } // 0b59 if (vol) { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { uint8_t envvol = part->ssg_env_vol; if (!envvol) { vol = 0; // -> 0bd4 } else { int ivol = (vol * (envvol+1)) >> 3; if (ivol & 1) { ivol >>= 1; ivol++; } else { ivol >>= 1; } vol = ivol; } } else { // 0b7d int newvol = vol + (part->ssg_env_vol << 4); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; vol = newvol; } // 0ba4 if (part->lfof.vol || part->lfof_b.vol) { int32_t lfovol = 0; if (part->lfof.vol) lfovol += part->lfo_diff; if (part->lfof_b.vol) lfovol += part->lfo_diff_b; lfovol += vol; vol = lfovol; if (lfovol < 0) { vol = 0; } else if (lfovol > 0xff) { vol = 0xff; } } } // 0bd4 if (work->ppz8) { if (vol) { work->ppz8_functbl->channel_volume(work->ppz8, pmd->proc_ch, vol >> 4); } else { work->ppz8_functbl->channel_stop(work->ppz8, pmd->proc_ch); } } } // 2985 static void pmd_ssg_freq_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint16_t freq = part->actual_freq; if (!freq) return; freq += (unsigned)part->portamento_diff; if (!part->flagext.detune) { // 2996 freq += (unsigned)(-part->detune); if (part->lfof.freq) { freq += (unsigned)(-part->lfo_diff); } if (part->lfof_b.freq) { freq += (unsigned)(-part->lfo_diff_b); } } else { // 29ad if (part->detune) { int32_t det = (((int32_t)freq) * ((int32_t)part->detune)) >> 12; if (det >= 0) { det++; } else { det--; } freq += (uint16_t)(-det); } if (part->lfof.freq || part->lfof_b.freq) { // 29db int32_t lfo = 0; if (part->lfof.freq) lfo += part->lfo_diff; if (part->lfof_b.freq) lfo += part->lfo_diff_b; if (lfo) { lfo = (((int32_t)freq) * lfo) >> 12; if (lfo >= 0) { lfo++; } else { lfo--; } freq += (uint16_t)(-lfo); } } // 2a0d } // 2a10 if (freq >= 0x8000) freq = 0; if (freq >= 0x1000) freq = 0x0fff; // 2a20 part->output_freq = freq; work->opna_writereg(work, (pmd->proc_ch-1)*2, freq); work->opna_writereg(work, (pmd->proc_ch-1)*2+1, freq>>8); } // 0620 static void pmd_adpcm_freq_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint16_t freq = part->actual_freq; if (!freq) return; freq += (unsigned)part->portamento_diff; uint32_t det = 0; if (part->lfof.freq || part->lfof_b.freq) { // 29db if (part->lfof.freq) det += part->lfo_diff; if (part->lfof_b.freq) det += part->lfo_diff_b; det <<= 2; } // 0649 det += part->detune; int32_t newfreq = freq + u16s16(det); if (newfreq > 0xffff) newfreq = 0xffff; if (newfreq < 0) newfreq = 0; freq = newfreq; part->output_freq = freq; work->opna_writereg(work, 0x109, freq); work->opna_writereg(work, 0x10a, freq >> 8); } // 0c27 static void pmd_ppz8_freq_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint32_t freq = part->actual_freq | (((uint32_t)part->actual_freq_upper) << 16); if (!freq) return; if (part->portamento_diff) { freq += part->portamento_diff << 4; } int32_t det = 0; if (part->lfof.freq || part->lfof_b.freq) { if (part->lfof.freq) det += part->lfo_diff; if (part->lfof_b.freq) det += part->lfo_diff_b; } // 0c6a det += part->detune; det *= freq >> 8; int64_t outfreq = freq + det; if (outfreq < 0) outfreq = 0; if (outfreq > INT32_MAX) outfreq = INT32_MAX; part->output_freq = outfreq; if (work->ppz8) { work->ppz8_functbl->channel_freq(work->ppz8, pmd->proc_ch, outfreq); } } // 2c9f static uint16_t pmd_part_ssg_readout( struct fmdriver_work *work, struct driver_pmd *pmd ) { uint16_t ret = 0x0900<<(pmd->proc_ch-1); ret |= pmd_opna_read_ssgout(work); return ret; } // 2c67 static void pmd_ssg_note_noise_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->actual_note == 0xff) return; uint16_t noteout = pmd_part_ssg_readout(work, pmd); uint8_t ssgout = noteout | (noteout >> 8); uint8_t pmdout = (noteout >> 8) & part->ssg_mix; ssgout &= ~pmdout; work->opna_writereg(work, 0x07, ssgout); if (pmd->ssg_noise_freq != pmd->ssg_noise_freq_wrote) { if (pmd->ssgeff_num & 0x80) { work->opna_writereg(work, 0x06, pmd->ssg_noise_freq); pmd->ssg_noise_freq_wrote = pmd->ssg_noise_freq; } } } // 148b static void pmd_part_loop_check( struct driver_pmd *pmd, struct pmd_part *part ) { pmd->loop.looped &= part->loop.looped; pmd->loop.ended &= part->loop.ended; pmd->loop.env &= part->loop.env; } // 2c19 static void pmd_part_fm_keyon( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->actual_note == 0xff) return; uint8_t *slotkey = &pmd->fm_slotkey[pmd->proc_ch-1+(3*pmd->opna_a1)]; *slotkey |= part->fm_slotmask; if (part->slot_delay_cnt) { *slotkey &= part->slot_delay_mask; } work->opna_writereg(work, 0x28, *slotkey | (pmd->proc_ch-1) | (pmd->opna_a1<<2)); } // 0547 static void pmd_part_adpcm_keyon( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->actual_note == 0xff) return; work->opna_writereg(work, 0x101, 0x02); work->opna_writereg(work, 0x100, 0x21); work->opna_writereg(work, 0x102, pmd->adpcm_start); work->opna_writereg(work, 0x103, pmd->adpcm_start >> 8); work->opna_writereg(work, 0x104, pmd->adpcm_stop); work->opna_writereg(work, 0x105, pmd->adpcm_stop >> 8); if (!pmd->adpcm_start_loop && !pmd->adpcm_stop_loop) { work->opna_writereg(work, 0x100, 0xa0); work->opna_writereg(work, 0x101, part->pan | 0x02); } else { work->opna_writereg(work, 0x100, 0xb0); work->opna_writereg(work, 0x101, part->pan | 0x02); work->opna_writereg(work, 0x102, pmd->adpcm_start_loop); work->opna_writereg(work, 0x103, pmd->adpcm_start_loop >> 8); work->opna_writereg(work, 0x104, pmd->adpcm_stop_loop); work->opna_writereg(work, 0x105, pmd->adpcm_stop_loop >> 8); } } // 0bf6 static void pmd_part_ppz8_keyon( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->actual_note == 0xff) return; if (work->ppz8) { work->ppz8_functbl->channel_play(work->ppz8, pmd->proc_ch, part->tonenum); } } // 2942 static uint32_t pmd_blkfnum_normalize( uint16_t blk, uint16_t fnum ) { for (;;) { if ((fnum & 0x8000) || (fnum < 0x26a)) { int32_t iblk = (int32_t)blk - 0x0800; if (iblk < 0) { // 2976 blk = 0; if (fnum & 0x8000) fnum = 0x0008; if (fnum < 0x0008) fnum = 0x0008; break; } blk = iblk; fnum += 0x26a; continue; } if (fnum < 0x4d4) break; // 2950 blk += 0x0800; if (blk != 0x4000) { fnum -= 0x26a; continue; } blk = 0x3800; if (fnum > 0x07ff) fnum = 0x7ff; break; } return (((uint32_t)blk) << 16) | fnum; } // 27d8 static void pmd_fm_freq_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint16_t fnum = part->actual_freq; if (!fnum) return; if (!part->fm_slotmask) return; uint16_t blk = fnum & 0x3800; fnum &= 0x7ff; fnum += (uint16_t)part->portamento_diff; fnum += (uint16_t)part->detune; if (pmd->proc_ch != 3 || pmd->opna_a1 || pmd->fm3ex_mode == 0x3f) { // 280c if (part->lfof.freq) fnum += (uint16_t)part->lfo_diff; if (part->lfof_b.freq) fnum += (uint16_t)part->lfo_diff_b; uint32_t blkfnum = pmd_blkfnum_normalize(blk, fnum); blkfnum |= blkfnum >> 16; blkfnum &= 0xffff; part->output_freq = blkfnum; pmd_reg_write(work, pmd, pmd->proc_ch + 0xa3, blkfnum >> 8); pmd_reg_write(work, pmd, pmd->proc_ch + 0x9f, blkfnum); } else { // 2837 uint8_t fm_slotmask = part->fm_slotmask; uint8_t lfo_slotmask = part->vol_lfo_slotmask; if (!(lfo_slotmask & 0x0f)) { lfo_slotmask = 0xf0; } uint8_t lfo_slotmask_b = part->vol_lfo_slotmask_b; if (!(lfo_slotmask_b & 0x0f)) { lfo_slotmask_b = 0xf0; } static const uint8_t slot_addr[2][4] = { {0xad, 0xae, 0xac, 0xa6}, {0xa9, 0xaa, 0xa8, 0xa2} }; for (int s = 3; s >= 0; s--) { const uint8_t slot_bit = (1 << (4+s)); if (fm_slotmask & slot_bit) { // 2858 uint16_t fnums = fnum + (uint16_t)pmd->fm3ex_det[s]; if ((lfo_slotmask & slot_bit) && part->lfof.freq) fnums += (uint16_t)part->lfo_diff; if ((lfo_slotmask_b & slot_bit) && part->lfof_b.freq) fnums += (uint16_t)part->lfo_diff_b; uint32_t blkfnum = pmd_blkfnum_normalize(blk, fnums); blkfnum |= blkfnum >> 16; blkfnum &= 0xffff; part->output_freq = blkfnum; pmd_reg_write(work, pmd, slot_addr[0][s], blkfnum >> 8); pmd_reg_write(work, pmd, slot_addr[1][s], blkfnum); } } } } // 13b5 static void pmd_part_fm_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->volume_save && part->actual_note != 0xff) { if (!pmd->volume_saved) { part->volume_save = 0; } pmd->volume_saved = false; } pmd_fm_vol_out(work, pmd, part); pmd_fm_freq_out(work, pmd, part); pmd_part_fm_keyon(work, pmd, part); part->note_proc++; pmd->no_keyoff = false; pmd->volume_saved = false; part->keystatus.off = false; part->keystatus.off_mask = false; if (pmd->datalen > (part->ptr + 1)) { if (pmd->data[part->ptr] == 0xfb) { part->keystatus.off_mask = true; } } pmd_part_loop_check(pmd, part); } // 15d4 static void pmd_part_ssg_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->volume_save && part->actual_note != 0xff) { if (!pmd->volume_saved) { part->volume_save = 0; } pmd->volume_saved = false; } // 15ef pmd_ssg_vol_out(work, pmd, part); pmd_ssg_freq_out(work, pmd, part); pmd_ssg_note_noise_out(work, pmd, part); part->note_proc++; pmd->no_keyoff = false; pmd->volume_saved = 0; part->keystatus.off = false; part->keystatus.off_mask = false; if (pmd->datalen > (part->ptr + 1)) { if (pmd->data[part->ptr] == 0xfb) { part->keystatus.off_mask = true; } } pmd_part_loop_check(pmd, part); } // 01b1 static void pmd_part_adpcm_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->volume_save && part->actual_note != 0xff) { if (!pmd->volume_saved) { part->volume_save = 0; } pmd->volume_saved = false; } pmd_adpcm_vol_out(work, pmd, part); pmd_adpcm_freq_out(work, pmd, part); if (part->keystatus.off) { pmd_part_adpcm_keyon(work, pmd, part); } part->note_proc++; pmd->no_keyoff = false; pmd->volume_saved = false; part->keystatus.off = false; part->keystatus.off_mask = false; if (pmd->datalen > (part->ptr + 1)) { if (pmd->data[part->ptr] == 0xfb) { part->keystatus.off_mask = true; } } pmd_part_loop_check(pmd, part); } // 0803 static void pmd_part_ppz8_out( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->volume_save && part->actual_note != 0xff) { if (!pmd->volume_saved) { part->volume_save = 0; } pmd->volume_saved = false; } pmd_ppz8_vol_out(work, pmd, part); pmd_ppz8_freq_out(work, pmd, part); if (part->keystatus.off) { pmd_part_ppz8_keyon(work, pmd, part); } // 082d part->note_proc++; pmd->no_keyoff = false; pmd->volume_saved = false; part->keystatus.off = false; part->keystatus.off_mask = false; if (pmd->datalen > (part->ptr + 1)) { if (pmd->data[part->ptr] == 0xfb) { part->keystatus.off_mask = true; } } pmd_part_loop_check(pmd, part); } // none static bool pmd_part_masked( const struct pmd_part *part ) { return part->mask.ext || part->mask.effect || part->mask.disabled || part->mask.slot || part->mask.mml || part->mask.ff; } // 1f7e static void pmd_fm_freq_out_mask( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (!pmd_part_masked(part)) { pmd_fm_freq_out(work, pmd, part); } } // 1ee3 static void pmd_fm3ex_mode_update( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { int partbit; if (part == &pmd->parts[PMD_PART_FM_3]) partbit = 1; else if (part == &pmd->parts[PMD_PART_FM_3B]) partbit = 2; else if (part == &pmd->parts[PMD_PART_FM_3C]) partbit = 4; else partbit = 8; bool fm3ex; if (!(part->fm_slotmask & 0xf0)) fm3ex = false; else if (part->fm_slotmask != 0xf0) fm3ex = true; else if (!(part->vol_lfo_slotmask & 0x0f)) fm3ex = false; else if (part->lfof.freq) fm3ex = true; else if (!(part->vol_lfo_slotmask_b & 0x0f)) fm3ex = false; else if (part->lfof_b.freq) fm3ex = true; else fm3ex = false; if (!fm3ex) { pmd->fm3ex_needed &= ~partbit; } else { pmd->fm3ex_needed |= partbit; } uint8_t fm3ex_mode; if (pmd->fm3ex_needed || pmd->fm3ex_force) { fm3ex_mode = 0x7f; } else { fm3ex_mode = 0x3f; } if (fm3ex_mode == pmd->fm3ex_mode) return; pmd->fm3ex_mode = fm3ex_mode; // output to 0x27 to update fm3exmode // but mask with 0xcf to prevent timer resetting work->opna_writereg(work, 0x27, fm3ex_mode & 0xcf); if (fm3ex_mode == 0x3f) return; if (part == &pmd->parts[PMD_PART_FM_3]) return; pmd_fm_freq_out_mask(work, pmd, &pmd->parts[PMD_PART_FM_3]); if (part == &pmd->parts[PMD_PART_FM_3B]) return; pmd_fm_freq_out_mask(work, pmd, &pmd->parts[PMD_PART_FM_3B]); if (part == &pmd->parts[PMD_PART_FM_3C]) return; pmd_fm_freq_out_mask(work, pmd, &pmd->parts[PMD_PART_FM_3C]); } // 1d7b static bool pmd_fm3ex_mode_update_check( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (pmd->proc_ch == 3 && !pmd->opna_a1) { pmd_fm3ex_mode_update(work, pmd, part); return true; } return false; } // 2268 static void pmd_cmdff_tonenum( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t tonenum = pmd_part_cmdload(pmd, part); part->tonenum = tonenum; if (!pmd_part_masked(part)) { pmd_part_tone_fm(work, pmd, part); return; } // 2277 uint8_t const * const toneptr = pmd_get_toneptr(pmd, tonenum); if (!toneptr) return; part->fb_alg = toneptr[0x18]; pmd_part_tone_tl_update(part, toneptr); if (pmd->proc_ch == 3 && part->fm_tone_slotmask && !pmd->opna_a1) { uint8_t fb_alg = part->fb_alg; if (!(part->fm_slotmask & 0x10)) { fb_alg &= 0x07; fb_alg |= pmd->fm3ex_fb_alg & 0x38; } pmd->fm3ex_fb_alg = fb_alg; part->fb_alg = fb_alg; } } // 046c static void pmd_cmdff_tonenum_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t tonenum = pmd_part_cmdload(pmd, part); part->tonenum = tonenum; pmd->adpcm_start = pmd->adpcm_addr[tonenum][0]; pmd->adpcm_stop = pmd->adpcm_addr[tonenum][1]; pmd->adpcm_start_loop = 0; pmd->adpcm_stop_loop = 0; pmd->adpcm_release = 0x8000; } // 0afd static void pmd_cmdff_tonenum_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->tonenum = pmd_part_cmdload(pmd, part); // 0a3c if (work->ppz8) { work->ppz8_functbl->channel_loop_voice(work->ppz8, pmd->proc_ch, part->tonenum); } // set origfreq } // 22b3 static void pmd_cmdfe_gate_abs( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ (void)work; part->gate_abs = pmd_part_cmdload(pmd, part); part->gate_rand_range = 0; } // 22cb static void pmd_cmdfd_vol( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ (void)work; part->vol = pmd_part_cmdload(pmd, part); } // 22d0 static void pmd_cmdfc_tempo( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ (void)work; uint8_t val = pmd_part_cmdload(pmd, part); if (val < 0xfb) { pmd->timerb = val; pmd->timerb_bak = val; pmd_calc_tempo(pmd); } else if (val == 0xff) { // 22e1 uint8_t tempo = pmd_part_cmdload(pmd, part); if (tempo < 0x12) tempo = 0x12; pmd->tempo = tempo; pmd->tempo_bak = tempo; pmd_calc_tempo_rev(pmd); } else if (val == 0xfe) { // 22f0 int timerbdiff = u8s8(pmd_part_cmdload(pmd, part)); int timerb = pmd->timerb_bak + timerbdiff; if (timerb > 0xfa) timerb = 0xfa; if (timerb < 0) timerb = 0; pmd->timerb = timerb; pmd->timerb_bak = timerb; pmd_calc_tempo(pmd); } else { // 2313 int tempodiff = u8s8(pmd_part_cmdload(pmd, part)); int tempo = pmd->tempo_bak + tempodiff; if (tempo > 0xff) tempo = 0xff; if (tempo < 0x12) tempo = 0x12; pmd->tempo = tempo; pmd->tempo_bak = tempo; pmd_calc_tempo_rev(pmd); } work->timerb = pmd->timerb; } // 236b static void pmd_cmdfb_tie( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->no_keyoff = true; } // 2371 static void pmd_cmdfa_det( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ (void)work; uint16_t val = pmd_part_cmdload(pmd, part); val |= ((uint16_t)(pmd_part_cmdload(pmd, part)) << 8); part->detune = u16s16(val); } // 2376 static void pmd_cmdd5_detrel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ (void)work; uint16_t val = pmd_part_cmdload(pmd, part); val |= ((uint16_t)(pmd_part_cmdload(pmd, part)) << 8); part->detune += u16s16(val); } // 237b static void pmd_cmdf9_repeat_reset( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint16_t ptr = pmd_part_cmdload(pmd, part); ptr |= ((uint16_t)(pmd_part_cmdload(pmd, part)) << 8); ptr++; if (pmd->datalen < (ptr+1)) { // TODO: better error handling part->ptr = 0; } else { pmd->data[ptr] = 0; } } // 2391 static void pmd_cmdf8_repeat( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t repeat = pmd_part_cmdload(pmd, part); if (repeat) { // 0xf8 repeat cnt ptr if (pmd->datalen < (part->ptr+1)) { part->ptr = 0; return; } pmd->data[part->ptr]++; uint8_t repeatcnt = pmd_part_cmdload(pmd, part); if (repeat == repeatcnt) { part->ptr += 2; return; } } else { // 0xf8 0x00 0x00 ptr part->ptr++; part->loop.looped = true; part->loop.ended = false; } // 23a7 uint16_t ptr = pmd_part_cmdload(pmd, part); ptr |= ((uint16_t)(pmd_part_cmdload(pmd, part)) << 8); ptr += 2; part->ptr = ptr; } // 23bd static void pmd_cmdf7_repeat_exit( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint16_t ptr = pmd_part_cmdload(pmd, part); ptr |= ((uint16_t)(pmd_part_cmdload(pmd, part)) << 8); if (pmd->datalen < (ptr+2)) { part->ptr = 0; return; } uint8_t repeat = pmd->data[ptr]; uint8_t repeatcnt = pmd->data[ptr+1]; if (repeatcnt == (repeat-1)) { part->ptr = ptr+4; } } // 23de static void pmd_cmdf6_set_loop( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->loop_ptr = part->ptr; } // 23e2 static void pmd_cmdf5_transpose( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->transpose = pmd_part_cmdload(pmd, part); } // 23f4 static void pmd_cmdf4_volinc_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->vol += 4; if (part->vol > 0x7f) part->vol = 0x7f; } // 2409 static void pmd_cmdf4_volinc_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ if (part->vol < 0xf) part->vol++; } // 0421 static void pmd_cmdf4_volinc_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int newvol = part->vol + 0x10; if (newvol > 0xff) newvol = 0xff; part->vol = newvol; } // 241c static void pmd_cmdf3_voldec_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int vol = part->vol - 4; if (vol < 0) vol = 0; part->vol = vol; } // 2435 static void pmd_cmdf3_voldec_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ if (part->vol) part->vol--; } // 0434 static void pmd_cmdf3_voldec_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int newvol = part->vol - 0x10; if (newvol < 0) newvol = 0; part->vol = newvol; } // 24ea static void pmd_cmdf2_lfo( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->lfo_set.delay = pmd_part_cmdload(pmd, part); part->lfo_set.speed = pmd_part_cmdload(pmd, part); part->lfo_set.step = pmd_part_cmdload(pmd, part); part->lfo_set.times = pmd_part_cmdload(pmd, part); part->lfo = part->lfo_set; pmd_lfo_reset(part); } // 2513 static void pmd_cmdf1_lfo_switch( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t val = pmd_part_cmdload(pmd, part); if (val & 0xf8) val = 1; part->lfof.freq = val & (1<<0); part->lfof.vol = val & (1<<1); part->lfof.sync = val & (1<<2); pmd_lfo_reset(part); } // 2526 static void pmd_cmdf1_lfo_switch_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_cmdf1_lfo_switch(work, pmd, part); pmd_fm3ex_mode_update_check(work, pmd, part); } // 1d31 static void pmd_cmdf1_ppsdrv( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ /* uint8_t data = */pmd_part_cmdload(pmd, part); // TODO: PPSDRV } // 252c static void pmd_cmdf0_env_old( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t val; val = pmd_part_cmdload(pmd, part); part->ssg_env_param_set[SSG_ENV_PARAM_OLD_AL] = val; part->ssg_env_param[SSG_ENV_PARAM_OLD_AL] = val; val = pmd_part_cmdload(pmd, part); part->ssg_env_param_set[SSG_ENV_PARAM_OLD_AD] = val; val = pmd_part_cmdload(pmd, part); part->ssg_env_param_set[SSG_ENV_PARAM_OLD_SR] = val; part->ssg_env_param[SSG_ENV_PARAM_OLD_SR] = val; val = pmd_part_cmdload(pmd, part); part->ssg_env_param_set[SSG_ENV_PARAM_OLD_RR] = val; part->ssg_env_param[SSG_ENV_PARAM_OLD_RR] = val; if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { part->ssg_env_state_old = SSG_ENV_STATE_OLD_RR; part->ssg_env_vol = -15; } } // 2554 static void pmd_cmdef_poke( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t reg = pmd_part_cmdload(pmd, part); uint8_t data = pmd_part_cmdload(pmd, part); pmd_reg_write(work, pmd, reg, data); } // 255c static void pmd_cmdee_noise_freq( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->ssg_noise_freq = pmd_part_cmdload(pmd, part); } // 2576 static void pmd_cmded_ssgmix( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->ssg_mix = pmd_part_cmdload(pmd, part); } // 25cc static uint8_t pmd_hlfo_delay_check( struct pmd_part *part, uint8_t pan_ams_pms ) { if (part->hlfo_delay) pan_ams_pms &= 0xc0; return pan_ams_pms; } // 257c static void pmd_part_set_pan( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t pan ){ uint8_t pan_ams_pms = (pan & 3) << 6; pan_ams_pms |= part->pan & 0x3f; part->pan = pan_ams_pms; // 258e if (pmd->proc_ch == 3 && !pmd->opna_a1) { pmd->parts[PMD_PART_FM_3].pan = pan_ams_pms; pmd->parts[PMD_PART_FM_3B].pan = pan_ams_pms; pmd->parts[PMD_PART_FM_3C].pan = pan_ams_pms; pmd->parts[PMD_PART_FM_3D].pan = pan_ams_pms; } // 25b6 if (pmd_part_masked(part)) return; pan_ams_pms = pmd_hlfo_delay_check(part, pan_ams_pms); pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, pan_ams_pms); } // 257b static void pmd_cmdec_pan( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_part_set_pan(work, pmd, part, pmd_part_cmdload(pmd, part)); } // 044d static void pmd_cmdec_pan_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->pan = pmd_part_cmdload(pmd, part) << 6; } // 0aca static void pmd_cmdec_pan_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t pan = pmd_part_cmdload(pmd, part); // 0ced static const uint8_t pantable[4] = {0, 9, 1, 5}; part->pan = 0; if (pan < 4) part->pan = pantable[pan]; if (work->ppz8) { work->ppz8_functbl->channel_pan(work->ppz8, pmd->proc_ch, part->pan); } } // 265d static void pmd_opnarhythm_inc(uint8_t *incdata, uint8_t val) { for (int i = 0; i < 6; i++) { if (val & (1<opnarhythm_mask; if (!val) return; if (pmd->fadeout_vol) { pmd_opnarhythm_tl_write(work, pmd, pmd->opnarhythm_tl); } if (!(val & 0x80)) { // 260c for (int i = 0; i < 6; i++) { if (val & (1<opna_writereg(work, 0x18+i, pmd->opnarhythm_ilpan[i]); } } } // 2626 work->opna_writereg(work, 0x10, val); if (val & 0x80) { // 262d pmd_opnarhythm_inc(pmd->opnarhythm_dump_inc, val); pmd->opnarhythm &= val ^ 0xff; } else { // 263b pmd_opnarhythm_inc(pmd->opnarhythm_shot_inc, val); pmd->opnarhythm |= val; } } // 266c static void pmd_cmdea_opnarhythm_il( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t val = pmd_part_cmdload(pmd, part); uint8_t i = (val >> 5) - 1; if (i >= 6) return; uint8_t ilpan = pmd->opnarhythm_ilpan[i]; ilpan &= ~0x1fu; ilpan |= val & 0x1f; pmd->opnarhythm_ilpan[i] = ilpan; work->opna_writereg(work, 0x18+i, ilpan); } // 26c8 static void pmd_cmde9_opnarhythm_pan( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t val = pmd_part_cmdload(pmd, part); uint8_t i = (val >> 5) - 1; if (i >= 6) return; uint8_t ilpan = pmd->opnarhythm_ilpan[i]; ilpan &= ~0xc0u; ilpan |= (val & 0x3) << 6; pmd->opnarhythm_ilpan[i] = ilpan; work->opna_writereg(work, 0x18+i, ilpan); } // 26d8 static void pmd_cmde8_opnarhythm_tl( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t tl = pmd_part_cmdload(pmd, part); if (pmd->opnarhythm_voldown) { tl = ((uint16_t)tl * (0x100-pmd->opnarhythm_voldown)) >> 8; } pmd->opnarhythm_tl = tl; if (pmd->fadeout_vol) { tl = ((uint16_t)tl * (0x100-pmd->fadeout_vol)) >> 8; } work->opna_writereg(work, 0x11, tl); } // 23e7 static void pmd_cmde7_transpose_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->transpose += pmd_part_cmdload(pmd, part); } // 26fe static void pmd_cmde6_opnarhythm_tl_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int tl = pmd->opnarhythm_tl + u8s8(pmd_part_cmdload(pmd, part)); if (tl > 0x3f) tl = 0x3f; if (tl < 0) tl = 0; pmd->opnarhythm_tl = tl; if (pmd->fadeout_vol) { tl = ((uint16_t)tl * (0x100-pmd->fadeout_vol)) >> 8; } work->opna_writereg(work, 0x11, tl); } // 2695 static void pmd_cmde5_opnarhythm_il_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t i = pmd_part_cmdload(pmd, part) - 1; if (i >= 6) { part->ptr++; return; } int il = pmd->opnarhythm_ilpan[i] & 0x1f; il += u8s8(pmd_part_cmdload(pmd, part)); if (il > 0x1f) il = 0x1f; if (il < 0) il = 0; pmd->opnarhythm_ilpan[i] &= ~0x1fu; pmd->opnarhythm_ilpan[i] |= il; work->opna_writereg(work, 0x18+i, pmd->opnarhythm_ilpan[i]); } // 225e static void pmd_cmde4_hlfo_delay( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->hlfo_delay_set = pmd_part_cmdload(pmd, part); } // 2403 static void pmd_cmde3_vol_add_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = part->vol + pmd_part_cmdload(pmd, part); if (vol > 0x7f) vol = 0x7f; part->vol = vol; } // 2416 static void pmd_cmde3_vol_add_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = part->vol + pmd_part_cmdload(pmd, part); if (vol > 0xf) vol = 0xf; part->vol = vol; } // 042e static void pmd_cmde3_vol_add_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int vol = part->vol + pmd_part_cmdload(pmd, part); if (vol > 0xff) vol = 0xff; part->vol = vol; } // FM: 2427 // SSG: 2440 // ADPCM: 043f static void pmd_cmde2_vol_sub( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int vol = part->vol - pmd_part_cmdload(pmd, part); if (vol < 0) vol = 0; part->vol = vol; } // 2209 static void pmd_cmde1_ams_pms( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t pan_ams_pms = pmd_part_cmdload(pmd, part); pan_ams_pms |= part->pan & 0xc0; part->pan = pan_ams_pms; if (pmd->proc_ch == 3 && !pmd->opna_a1) { pmd->parts[PMD_PART_FM_3].pan = pan_ams_pms; pmd->parts[PMD_PART_FM_3B].pan = pan_ams_pms; pmd->parts[PMD_PART_FM_3C].pan = pan_ams_pms; pmd->parts[PMD_PART_FM_3D].pan = pan_ams_pms; } if (pmd_part_masked(part)) return; pan_ams_pms = pmd_hlfo_delay_check(part, pan_ams_pms); pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, pan_ams_pms); } // 2252 static void pmd_cmde0_hlfo( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t hlfo = pmd_part_cmdload(pmd, part); pmd->opna_last22 = hlfo; work->opna_writereg(work, 0x22, hlfo); } // 2263 static void pmd_cmddf_meas_len( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->meas_len = pmd_part_cmdload(pmd, part); } // 21cc static void pmd_cmdde_echo_init_add_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = part->vol + pmd_part_cmdload(pmd, part); if (vol > 0x7f) vol = 0x7f; part->volume_save = vol+1; pmd->volume_saved = true; } // 21e1 static void pmd_cmdde_echo_init_add_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = part->vol + pmd_part_cmdload(pmd, part); if (vol > 0xf) vol = 0xf; part->volume_save = vol+1; pmd->volume_saved = true; } // 21ed static void pmd_cmdde_echo_init_add_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int vol = part->vol + pmd_part_cmdload(pmd, part); if (vol > 0xfe) vol = 0xfe; part->volume_save = vol+1; pmd->volume_saved = true; } // 21fb static void pmd_cmddd_echo_init_sub( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int vol = part->vol - pmd_part_cmdload(pmd, part); if (vol < 0) vol = 0; part->volume_save = vol+1; pmd->volume_saved = true; } // 21be static void pmd_cmddc_status1( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->status1 = pmd_part_cmdload(pmd, part); } // 21c3 static void pmd_cmddb_status1_add( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->status1 += pmd_part_cmdload(pmd, part); } // 2104 static void pmd_cmdda_portamento_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t note = pmd_part_cmdload(pmd, part); if (pmd_part_masked(part)) { return; } note = pmd_part_lfo_init_fm(work, pmd, part, note); note = pmd_part_note_transpose(part, note); pmd_note_freq_fm(part, note); uint16_t f1 = part->actual_freq; uint8_t n1 = part->actual_note; note = pmd_part_cmdload(pmd, part); note = pmd_part_note_transpose(part, note); pmd_note_freq_fm(part, note); uint16_t f2 = part->actual_freq; part->actual_freq = f1; part->actual_note = n1; uint8_t f2blk = f2 >> 11; uint8_t f1blk = f1 >> 11; f1 &= 0x7ff; f2 &= 0x7ff; int32_t blkdiff = (int32_t)(f2blk - f1blk) * 0x26a; int32_t freqdiff = f2 - f1 + blkdiff; uint8_t clocks = pmd_part_cmdload(pmd, part); part->len = part->len_cnt = clocks; pmd_part_calc_gate(pmd, part); int16_t p_add = 0; int16_t p_rem = 0; if (clocks) { p_add = freqdiff / clocks; p_rem = freqdiff % clocks; } part->portamento_add = p_add; part->portamento_rem = p_rem; part->lfof.portamento = true; pmd_part_fm_out(work, pmd, part); } // 2176 static void pmd_cmdda_portamento_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t note = pmd_part_cmdload(pmd, part); if (pmd_part_masked(part)) { return; } note = pmd_part_lfo_init_ssg(work, pmd, part, note); note = pmd_part_note_transpose(part, note); pmd_note_freq_ssg(part, note); uint16_t f1 = part->actual_freq; uint8_t n1 = part->actual_note; note = pmd_part_cmdload(pmd, part); note = pmd_part_note_transpose(part, note); pmd_note_freq_ssg(part, note); uint16_t f2 = part->actual_freq; part->actual_freq = f1; part->actual_note = n1; uint8_t clocks = pmd_part_cmdload(pmd, part); part->len = part->len_cnt = clocks; int freqdiff = f2 - f1; pmd_part_calc_gate(pmd, part); int16_t p_add = 0; int16_t p_rem = 0; if (clocks) { p_add = freqdiff / clocks; p_rem = freqdiff % clocks; } part->portamento_add = p_add; part->portamento_rem = p_rem; part->lfof.portamento = true; pmd_part_ssg_out(work, pmd, part); } // 03d6 static void pmd_cmdda_portamento_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t note = pmd_part_cmdload(pmd, part); if (pmd_part_masked(part)) return; pmd_part_lfo_init_ssg(work, pmd, part, note); note = pmd_part_note_transpose(part, note); pmd_note_freq_adpcm(part, note); uint16_t f1 = part->actual_freq; uint8_t n1 = part->actual_note; note = pmd_part_cmdload(pmd, part); note = pmd_part_note_transpose(part, note); pmd_note_freq_adpcm(part, note); uint16_t f2 = part->actual_freq; part->actual_freq = f1; part->actual_note = n1; int freqdiff = f2 - f1; uint8_t clocks = pmd_part_cmdload(pmd, part); part->len = part->len_cnt = clocks; pmd_part_calc_gate(pmd, part); int16_t p_add = 0; int16_t p_rem = 0; if (clocks) { p_add = freqdiff / clocks; p_rem = freqdiff % clocks; } part->portamento_add = p_add; part->portamento_rem = p_rem; part->lfof.portamento = true; pmd_part_adpcm_out(work, pmd, part); } // 0a62 static void pmd_cmdda_portamento_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t note = pmd_part_cmdload(pmd, part); if (pmd_part_masked(part)) return; pmd_part_lfo_init_ssg(work, pmd, part, note); note = pmd_part_note_transpose(part, note); pmd_note_freq_ppz8(part, note); uint32_t f1 = (((uint32_t)part->actual_freq_upper) << 16) | part->actual_freq; uint8_t n1 = part->actual_note; note = pmd_part_cmdload(pmd, part); note = pmd_part_note_transpose(part, note); pmd_note_freq_ppz8(part, note); uint32_t f2 = (((uint32_t)part->actual_freq_upper) << 16) | part->actual_freq; part->actual_freq = f1; part->actual_freq_upper = f1 >> 16; part->actual_note = n1; int16_t freqdiff = u16s16((f2 - f1)>>4); uint8_t clocks = pmd_part_cmdload(pmd, part); part->len = part->len_cnt = clocks; pmd_part_calc_gate(pmd, part); int16_t p_add = 0; int16_t p_rem = 0; if (clocks) { p_add = freqdiff / clocks; p_rem = freqdiff % clocks; } part->portamento_add = p_add; part->portamento_rem = p_rem; part->lfof.portamento = true; pmd_part_ppz8_out(work, pmd, part); } // 20bf static void pmd_cmdd6_md( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->md_speed = pmd_part_cmdload(pmd, part); part->md_speed_set = part->md_speed; part->md_depth = pmd_part_cmdload(pmd, part); } // 2054 static void pmd_cmdd4_ssgeff( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t cmd = pmd_part_cmdload(pmd, part); if (pmd_part_masked(part)) return; if (cmd) { pmd_ssg_effect(work, pmd, cmd); } else { pmd_ssgeff_stop(work, pmd); } } // 206f static void pmd_cmdd3_fmeff( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ // TODO: 42ae uint8_t val = pmd_part_cmdload(pmd, part); if (pmd_part_masked(part)) return; bool a1 = pmd->opna_a1; if (val) { //3be1(); } else { //3c43(); } pmd->opna_a1 = a1; } // 20b6 static void pmd_cmdd2_fadeout( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ // TODO pmd_part_cmdload(pmd, part); } // 2561 static void pmd_cmdd0_ssgnoisefreqadd( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ int nf = pmd->ssg_noise_freq + u8s8(pmd_part_cmdload(pmd, part)); if (nf < 0) nf = 0; if (nf > 0x1f) nf = 0x1f; pmd->ssg_noise_freq = nf; } // 2044 static void pmd_part_fm_keyon_if_not_masked( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (!pmd_part_masked(part) && !part->keystatus.off_mask) pmd_part_fm_keyon(work, pmd, part); } // 1f88 static void pmd_cmdcf_slotmask( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); if (data & 0x0f) { part->fm_slotout = data << 4; } else { // 1f9c uint8_t alg = 0; if (pmd->proc_ch == 3 && !pmd->opna_a1) { alg = pmd->fm3ex_fb_alg; } else { // 2dda const uint8_t *toneptr = pmd_get_toneptr(pmd, part->tonenum); if (toneptr) { alg = toneptr[0x18]; } } alg &= 7; part->fm_slotout = pmd_alg_output_slot_table[alg]; } // 1fc9 data &= 0xf0; if (data == part->fm_slotmask) return; // 1fd1 part->fm_slotmask = data; part->mask.slot = !data; // 1fe3 if (pmd_fm3ex_mode_update_check(work, pmd, part) && part != &pmd->parts[PMD_PART_FM_3]) { pmd_part_fm_keyon_if_not_masked(work, pmd, &pmd->parts[PMD_PART_FM_3]); if (part != &pmd->parts[PMD_PART_FM_3B]) { pmd_part_fm_keyon_if_not_masked(work, pmd, &pmd->parts[PMD_PART_FM_3B]); if (part != &pmd->parts[PMD_PART_FM_3C]) { pmd_part_fm_keyon_if_not_masked(work, pmd, &pmd->parts[PMD_PART_FM_3C]); } } } // 2012 uint8_t tonemask = 0; if (part->fm_slotmask & 0x80) tonemask |= 0x11; if (part->fm_slotmask & 0x40) tonemask |= 0x44; if (part->fm_slotmask & 0x20) tonemask |= 0x22; if (part->fm_slotmask & 0x10) tonemask |= 0x88; part->fm_tone_slotmask = tonemask; part->proc_masked = pmd_part_masked(part); } // 039a static void pmd_cmdce_adpcm_loop( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint16_t diff = pmd_part_cmdload(pmd, part); diff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; if (!(diff & 0x8000)) diff += pmd->adpcm_start; else diff += pmd->adpcm_stop; pmd->adpcm_start_loop = diff; diff = pmd_part_cmdload(pmd, part); diff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; if (diff && !(diff & 0x80)) diff += pmd->adpcm_start; else diff += pmd->adpcm_stop; pmd->adpcm_stop_loop = diff; diff = pmd_part_cmdload(pmd, part); diff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; if (diff != 0x8000) { if (!(diff & 0x80)) diff += pmd->adpcm_start; else diff += pmd->adpcm_stop; } pmd->adpcm_release = diff; } // 0a04 static void pmd_cmdce_ppz8_loop( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint32_t startoff = pmd_part_cmdload(pmd, part); startoff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; uint32_t endoff = pmd_part_cmdload(pmd, part); endoff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; if (work->ppz8) { uint32_t len = work->ppz8_functbl->voice_length(work->ppz8, part->tonenum); if (startoff & 0x8000) startoff = len + u16s16(startoff); if (endoff & 0x8000) endoff = len + u16s16(endoff); work->ppz8_functbl->channel_loopoffset(work->ppz8, pmd->proc_ch, startoff, endoff); } pmd_part_cmdload(pmd, part); pmd_part_cmdload(pmd, part); } // 1e1b static void pmd_cmdcd_env_new( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->ssg_env_param_set[SSG_ENV_PARAM_NEW_AR] = pmd_part_cmdload(pmd, part) & 0x1f; part->ssg_env_param_set[SSG_ENV_PARAM_NEW_DR] = pmd_part_cmdload(pmd, part) & 0x1f; part->ssg_env_param_set[SSG_ENV_PARAM_NEW_SR] = pmd_part_cmdload(pmd, part) & 0x1f; uint8_t data = pmd_part_cmdload(pmd, part); part->ssg_env_param_set[SSG_ENV_PARAM_NEW_RR] = data & 0xf; part->ssg_env_param_sl = (data >> 4) ^ 0xf; part->ssg_env_param_al = pmd_part_cmdload(pmd, part) & 0xf; if (part->ssg_env_state_old != SSG_ENV_STATE_OLD_NEW) { part->ssg_env_state_old = SSG_ENV_STATE_OLD_NEW; part->ssg_env_state_new = SSG_ENV_STATE_NEW_RR; part->ssg_env_vol = 0; } } // 1def static void pmd_cmdcc_det_ext( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->flagext.detune = pmd_part_cmdload(pmd, part) & 1; } // 1e16 static void pmd_cmdcb_lfo_waveform( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->lfo_waveform = pmd_part_cmdload(pmd, part); } // 1dfa static void pmd_cmdca_lfo_ext( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->flagext.lfo = pmd_part_cmdload(pmd, part) & 1; } // 1e07 static void pmd_cmdc9_env_ext( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->flagext.env = pmd_part_cmdload(pmd, part) & 1; } // 1e99 static void pmd_cmdc8_fm3ex_det( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ if (pmd->proc_ch != 3 || pmd->opna_a1) { pmd_cmd_null_3(work, pmd, part); return; } // 1ead uint8_t mask = pmd_part_cmdload(pmd, part); uint16_t val16 = pmd_part_cmdload(pmd, part); val16 |= ((uint16_t)pmd_part_cmdload(pmd, part)) << 8; int16_t det = u16s16(val16); for (int s = 0; s < 4; s++) { if (mask & (1<fm3ex_det[s] = det; } } // 1ecd pmd->fm3ex_force = false; for (int s = 0; s < 4; s++) { if (pmd->fm3ex_det[s]) pmd->fm3ex_force = true; } pmd_fm3ex_mode_update(work, pmd, part); } // 1e5f static void pmd_cmdc7_fm3ex_det_add( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ if (pmd->proc_ch != 3 || pmd->opna_a1) { pmd_cmd_null_3(work, pmd, part); return; } // 1e73 uint8_t mask = pmd_part_cmdload(pmd, part); uint16_t det = pmd_part_cmdload(pmd, part); det |= ((uint16_t)pmd_part_cmdload(pmd, part)) << 8; for (int s = 0; s < 4; s++) { if (mask & (1<fm3ex_det[s] = u16s16((uint16_t)pmd->fm3ex_det[s] + det); } } pmd->fm3ex_force = false; for (int s = 0; s < 4; s++) { if (pmd->fm3ex_det[s]) pmd->fm3ex_force = true; } pmd_fm3ex_mode_update(work, pmd, part); } // 1dc0 static void pmd_fm3ex_init( struct driver_pmd *pmd, struct pmd_part *part, uint16_t ptr ) { part->ptr = ptr; part->len = part->len_cnt = 1; part->keystatus.off = true; part->keystatus.off_mask = true; part->md_cnt = 0xff; part->md_cnt_set = 0xff; part->md_cnt_b = 0xff; part->md_cnt_set_b = 0xff; part->actual_note = 0xff; part->curr_note = 0xff; part->vol = 0x6c; part->pan = pmd->parts[PMD_PART_FM_3].pan; part->mask.slot = true; part->loop.ended = false; } // 1d90 static void pmd_cmdc6_fm3ex_init( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ for (int i = 0; i < 3; i++) { uint16_t ptr = pmd_part_cmdload(pmd, part); ptr |= ((uint16_t)pmd_part_cmdload(pmd, part)) << 8; if (!ptr) continue; pmd_fm3ex_init(pmd, &pmd->parts[PMD_PART_FM_3B+i], ptr); } } // 1d45 static void pmd_cmdc5_lfo_slotmask( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t mask = pmd_part_cmdload(pmd, part); mask &= 0x0f; if (mask) { // 1d4a mask <<= 4; mask |= 0x0f; part->vol_lfo_slotmask = mask; } else { // 1d59 part->vol_lfo_slotmask = part->fm_slotout; } // 1d7b pmd_fm3ex_mode_update_check(work, pmd, part); } // 22c6 static void pmd_cmdc4_gate_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->gate_rel = pmd_part_cmdload(pmd, part); } // 25d9 static void pmd_cmdc3_pan_ex( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); pmd_part_cmdload(pmd, part); uint8_t pan; if (data == 0) pan = 3; else if (data & 0x80) pan = 1; else pan = 2; pmd_part_set_pan(work, pmd, part, pan); } // 0458 static void pmd_cmdc3_pan_ex_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); pmd_part_cmdload(pmd, part); uint8_t pan; if (!data) pan = 0xc0; else if (data & 0x80) pan = 0x40; else pan = 0x80; part->pan = pan; } // 0ae5 static void pmd_cmdc3_pan_ex_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); pmd_part_cmdload(pmd, part); if (!(data & 0x80)) { if (data > 4) data = 4; } else { if (data < 0xfc) data = 0xfc; } data += 5; uint8_t pan = data; part->pan = pan; if (work->ppz8) { work->ppz8_functbl->channel_pan(work->ppz8, pmd->proc_ch, part->pan); } } // 2509 static void pmd_cmdc2_lfo_delay( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t d = pmd_part_cmdload(pmd, part); part->lfo.delay = d; part->lfo_set.delay = d; pmd_lfo_reset(part); } // 1a95 // DF static void pmd_cmdc0_ff_fm_voldown( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->fm_voldown = pmd_part_cmdload(pmd, part); } // 1ab0 // DF +/- static void pmd_cmdc0_fe_fm_voldown_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = pmd_part_cmdload(pmd, part); if (!vol) { pmd->fm_voldown = pmd->fm_voldown_orig; } else { int newvol = pmd->fm_voldown + u8s8(vol); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; pmd->fm_voldown = newvol; } } // 1a9c // DS static void pmd_cmdc0_fd_ssg_voldown( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->ssg_voldown = pmd_part_cmdload(pmd, part); } // 1ad6 // DS +/- static void pmd_cmdc0_fc_ssg_voldown_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = pmd_part_cmdload(pmd, part); if (!vol) { pmd->ssg_voldown = pmd->ssg_voldown_orig; } else { int newvol = pmd->ssg_voldown + u8s8(vol); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; pmd->ssg_voldown = newvol; } } // 1aa1 // DP static void pmd_cmdc0_fb_adpcm_voldown( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->adpcm_voldown = pmd_part_cmdload(pmd, part); } // 1ade // DP +/- static void pmd_cmdc0_fa_adpcm_voldown_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = pmd_part_cmdload(pmd, part); if (!vol) { pmd->adpcm_voldown = pmd->adpcm_voldown_orig; } else { int newvol = pmd->adpcm_voldown + u8s8(vol); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; pmd->adpcm_voldown = newvol; } } // 1aa6 // DR static void pmd_cmdc0_f9_opnarhythm_voldown( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->opnarhythm_voldown = pmd_part_cmdload(pmd, part); } // 1ae6 // DR +/- static void pmd_cmdc0_f8_opnarhythm_voldown_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = pmd_part_cmdload(pmd, part); if (!vol) { pmd->opnarhythm_voldown = pmd->opnarhythm_voldown_orig; } else { int newvol = pmd->opnarhythm_voldown + u8s8(vol); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; pmd->opnarhythm_voldown = newvol; } } // 1a8e // probably A static void pmd_cmdc0_f7_pcm86_vol( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->pcm86_vol_spb = pmd_part_cmdload(pmd, part) & 1; } // 1aab // unimplemented (disabled in PMDPPZ.COM) static void pmd_cmdc0_f6_ppz8_voldown( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd->ppz8_voldown = pmd_part_cmdload(pmd, part); } // 1aee // unimplemented (disabled in PMDPPZ.COM) static void pmd_cmdc0_f5_ppz8_voldown_rel( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t vol = pmd_part_cmdload(pmd, part); if (!vol) { pmd->ppz8_voldown = pmd->ppz8_voldown_orig; } else { int newvol = pmd->ppz8_voldown + u8s8(vol); if (newvol > 0xff) newvol = 0xff; if (newvol < 0) newvol = 0; pmd->ppz8_voldown = newvol; } } typedef void (*pmd_cmd_func) ( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ); static const pmd_cmd_func pmd_cmd_table_c0[] = { pmd_cmdc0_ff_fm_voldown, pmd_cmdc0_fe_fm_voldown_rel, pmd_cmdc0_fd_ssg_voldown, pmd_cmdc0_fc_ssg_voldown_rel, pmd_cmdc0_fb_adpcm_voldown, pmd_cmdc0_fa_adpcm_voldown_rel, pmd_cmdc0_f9_opnarhythm_voldown, pmd_cmdc0_f8_opnarhythm_voldown_rel, pmd_cmdc0_f7_pcm86_vol, // f6, f5 is disabled pmd_cmdc0_f6_ppz8_voldown, pmd_cmdc0_f5_ppz8_voldown_rel, }; // 1a62 static void pmd_cmdc0_extended( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t data ) { if (data < 0xf7) { part->ptr = 0; return; } pmd_cmd_table_c0[data^0xff](work, pmd, part); } // 1cc0 static void pmd_part_fm_unmask( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (!part->fm_tone_slotmask) return; //uint8_t tonenum = part->tonenum; uint8_t fm_tone_tl[4]; for (int i = 0; i < 4; i++) { fm_tone_tl[i] = part->fm_tone_tl[i]; } pmd->tonemask_fb_alg = true; pmd_part_tone_fm(work, pmd, part); pmd->tonemask_fb_alg = false; for (int i = 0; i < 4; i++) { part->fm_tone_tl[i] = fm_tone_tl[i]; } uint8_t slotmask = (part->fm_slotout ^ 0xff) & part->fm_slotmask; if (slotmask) { for (int s = 0; s < 4; s++) { if (!(slotmask & (1<<(7-s)))) continue; static const uint8_t regoff[4] = {3, 1, 2, 0}; pmd_reg_write(work, pmd, 0x3f+regoff[s]*4+pmd->proc_ch, fm_tone_tl[regoff[s]]); } } // 1d23 pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, pmd_hlfo_delay_check(part, part->pan)); } // 1c50 static void pmd_cmdc0_mml_mask_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); if (data >= 2) { pmd_cmdc0_extended(work, pmd, part, data); return; } else if (data == 1) { // 1c5c part->mask.mml = false; bool masked = pmd_part_masked(part); part->mask.mml = true; if (!masked) { pmd_part_tone_slotmask_stop(work, pmd, part); } // 1c9c part->proc_masked = true; } else { // 1ca0 part->mask.mml = false; if (pmd_part_masked(part)) { part->proc_masked = true; } else { pmd_part_fm_unmask(work, pmd, part); part->proc_masked = false; } } } // 1c7a static void pmd_cmdc0_mml_mask_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); if (data >= 2) { pmd_cmdc0_extended(work, pmd, part, data); return; } else if (data == 1) { // 1c86 part->mask.mml = false; bool masked = pmd_part_masked(part); part->mask.mml = true; if (!masked) { // 1c90 uint16_t ssgout = pmd_part_ssg_readout(work, pmd); ssgout |= ssgout>>8; work->opna_writereg(work, 0x07, ssgout); } // 1c9c part->proc_masked = true; } else { // 1ca0 part->mask.mml = false; part->proc_masked = pmd_part_masked(part); } } // 036a static void pmd_cmdc0_mml_mask_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t data = pmd_part_cmdload(pmd, part); if (data >= 2) { pmd_cmdc0_extended(work, pmd, part, data); return; } else if (data == 1) { // 0376 part->mask.mml = false; bool masked = pmd_part_masked(part); part->mask.mml = true; if (!masked) { work->opna_writereg(work, 0x101, 0x02); work->opna_writereg(work, 0x100, 0x01); } part->proc_masked = true; } else { // 0390 part->mask.mml = false; part->proc_masked = pmd_part_masked(part); } } // 09d8 static void pmd_cmdc0_mml_mask_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { uint8_t data = pmd_part_cmdload(pmd, part); if (data >= 2) { pmd_cmdc0_extended(work, pmd, part, data); return; } else if (data == 1) { // 09e4 part->mask.mml = false; bool masked = pmd_part_masked(part); part->mask.mml = true; if (!masked) { if (work->ppz8) { work->ppz8_functbl->channel_stop(work->ppz8, pmd->proc_ch); } } part->proc_masked = true; // -> 08ca } else { // 09fa part->mask.mml = false; part->proc_masked = pmd_part_masked(part); // -> 07cd } } // 1caa static void pmd_cmdc0_mml_mask_rhythm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); if (data >= 2) { pmd_cmdc0_extended(work, pmd, part, data); return; } else if (data == 1) { part->mask.mml = true; } else { part->mask.mml = false; } } // 244e static void pmd_cmdbf_lfo2( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_part_lfo_flip(part); pmd_cmdf2_lfo(work, pmd, part); pmd_part_lfo_flip(part); } // 2473 static void pmd_cmdbe_lfo2_switch( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t val = pmd_part_cmdload(pmd, part); part->lfof_b.freq = val & (1<<0); part->lfof_b.vol = val & (1<<1); part->lfof_b.sync = val & (1<<2); pmd_part_lfo_flip(part); pmd_lfo_reset(part); pmd_part_lfo_flip(part); } // 248d static void pmd_cmdbe_lfo2_switch_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_cmdbe_lfo2_switch(work, pmd, part); pmd_fm3ex_mode_update_check(work, pmd, part); } // 245f static void pmd_cmdbd_lfo2_md( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_part_lfo_flip(part); pmd_cmdd6_md(work, pmd, part); pmd_part_lfo_flip(part); } // 2464 static void pmd_cmdbc_lfo2_waveform( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_part_lfo_flip(part); pmd_cmdcb_lfo_waveform(work, pmd, part); pmd_part_lfo_flip(part); } // 2469 static void pmd_cmdbb_lfo2_ext( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_part_lfo_flip(part); pmd_cmdca_lfo_ext(work, pmd, part); pmd_part_lfo_flip(part); } // 1d61 static void pmd_cmdba_lfo2_slotmask( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t mask = pmd_part_cmdload(pmd, part); mask &= 0x0f; if (mask) { // 1d4a mask <<= 4; mask |= 0x0f; part->vol_lfo_slotmask_b = mask; } else { // 1d59 part->vol_lfo_slotmask_b = part->fm_slotout; } // 1d7b pmd_fm3ex_mode_update_check(work, pmd, part); } // 246e static void pmd_cmdb9_lfo2_delay( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ pmd_part_lfo_flip(part); pmd_cmdc2_lfo_delay(work, pmd, part); pmd_part_lfo_flip(part); } // 1b87 // command O static void pmd_cmdb8_slottl( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); uint8_t slotmask = data & 0xf & (part->fm_slotmask>>4); uint8_t val = pmd_part_cmdload(pmd, part); bool masked = pmd_part_masked(part); bool relative = data & 0x80; static const uint8_t slottable[4] = {0, 2, 1, 3}; for (int s = 0; s < 4; s++) { if (!(slotmask & (1<fm_tone_tl[slottable[s]] = val & 0x7f; } else { uint8_t vol = part->fm_tone_tl[slottable[s]] + val; if (vol & 0x80) { if (val & 0x80) vol = 0; else vol = 0x7f; } part->fm_tone_tl[slottable[s]] = vol; } if (!masked) { pmd_reg_write(work, pmd, 0x3f+pmd->proc_ch+8*s, part->fm_tone_tl[slottable[s]]); } } } // 20cb static void pmd_cmdb7_lfo_md_cnt( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); bool b = data & 0x80; data &= 0x7f; if (!data) data = 0xff; if (!b) { part->md_cnt = part->md_cnt_set = data; } else { part->md_cnt_b = part->md_cnt_set_b = data; } } // 1b19 static void pmd_part_write_fb( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t data ) { uint8_t newfb = (data & 7) << 3; uint8_t fbalg; if ((pmd->proc_ch == 3) && (!pmd->opna_a1)) { pmd->fm3ex_fb_alg &= 7; pmd->fm3ex_fb_alg |= newfb; fbalg = pmd->fm3ex_fb_alg; } else { part->fb_alg &= 7; part->fb_alg |= newfb; fbalg = part->fb_alg; } if ((pmd->proc_ch == 3) && (!pmd->opna_a1) && (!(part->fm_slotmask & 0x10))) return; pmd_reg_write(work, pmd, 0xaf+pmd->proc_ch, fbalg); part->fb_alg = fbalg; } // 1b0e static void pmd_cmdb6_fb( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); uint8_t fbalg; if (!(data & 0x80)) { pmd_part_write_fb(work, pmd, part, data); } else { // 1b51 if (!(data & 0x40)) data &= 7; if ((pmd->proc_ch == 3) && (!pmd->opna_a1)) { fbalg = pmd->fm3ex_fb_alg; } else { fbalg = part->fb_alg; } fbalg >>= 3; fbalg &= 7; fbalg += data; if (fbalg & 0x80) { pmd_part_write_fb(work, pmd, part, 0); } else if (fbalg > 7) { pmd_part_write_fb(work, pmd, part, 7); } else { pmd_part_write_fb(work, pmd, part, fbalg); } } } // 1af6 static void pmd_cmdb5_slot_delay( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ uint8_t data = pmd_part_cmdload(pmd, part); data = (~data) & 0xf; data <<= 4; part->slot_delay_mask = data; part->slot_delay = pmd_part_cmdload(pmd, part); part->slot_delay_cnt = part->slot_delay; } // 099f static void pmd_cmdb4_ppz8_init( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ for (int i = 0; i < 8; i++) { uint16_t ptr = pmd_part_cmdload(pmd, part); ptr |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; if (!ptr) continue; struct pmd_part *ppzpart = &pmd->parts[PMD_PART_PPZ_1+i]; ppzpart->ptr = ptr; ppzpart->len = ppzpart->len_cnt = 1; ppzpart->keystatus.off = true; ppzpart->keystatus.off_mask = true; ppzpart->md_cnt = 0xff; ppzpart->md_cnt_set = 0xff; ppzpart->md_cnt_b = 0xff; ppzpart->md_cnt_set_b = 0xff; ppzpart->actual_note = 0xff; ppzpart->vol = 0x80; ppzpart->pan = 5; ppzpart->loop.ended = false; } } // 22bc static void pmd_cmdb3_gate_min( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->gate_min = pmd_part_cmdload(pmd, part); } // 23ef static void pmd_cmdb2_transpose_master( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->transpose_master = pmd_part_cmdload(pmd, part); } // 22c1 static void pmd_cmdb1_gate_rand_range( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ){ part->gate_rand_range = pmd_part_cmdload(pmd, part); } enum { PMD_CMD_CNT = 0x100-0xb1 }; static const pmd_cmd_func pmd_cmd_table_fm[PMD_CMD_CNT] = { pmd_cmdff_tonenum, pmd_cmdfe_gate_abs, pmd_cmdfd_vol, pmd_cmdfc_tempo, pmd_cmdfb_tie, pmd_cmdfa_det, pmd_cmdf9_repeat_reset, pmd_cmdf8_repeat, pmd_cmdf7_repeat_exit, pmd_cmdf6_set_loop, pmd_cmdf5_transpose, pmd_cmdf4_volinc_fm, pmd_cmdf3_voldec_fm, pmd_cmdf2_lfo, pmd_cmdf1_lfo_switch_fm, pmd_cmd_null_4, pmd_cmdef_poke, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdec_pan, pmd_cmdeb_opnarhythm, pmd_cmdea_opnarhythm_il, pmd_cmde9_opnarhythm_pan, pmd_cmde8_opnarhythm_tl, pmd_cmde7_transpose_rel, pmd_cmde6_opnarhythm_tl_rel, pmd_cmde5_opnarhythm_il_rel, pmd_cmde4_hlfo_delay, pmd_cmde3_vol_add_fm, pmd_cmde2_vol_sub, pmd_cmde1_ams_pms, pmd_cmde0_hlfo, pmd_cmddf_meas_len, pmd_cmdde_echo_init_add_fm, pmd_cmddd_echo_init_sub, pmd_cmddc_status1, pmd_cmddb_status1_add, pmd_cmdda_portamento_fm, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdd6_md, pmd_cmdd5_detrel, pmd_cmdd4_ssgeff, pmd_cmdd3_fmeff, pmd_cmdd2_fadeout, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdcf_slotmask, pmd_cmd_null_6, pmd_cmd_null_5, pmd_cmd_null_1, pmd_cmdcb_lfo_waveform, pmd_cmdca_lfo_ext, pmd_cmd_null_1, pmd_cmdc8_fm3ex_det, pmd_cmdc7_fm3ex_det_add, pmd_cmdc6_fm3ex_init, pmd_cmdc5_lfo_slotmask, pmd_cmdc4_gate_rel, pmd_cmdc3_pan_ex, pmd_cmdc2_lfo_delay, pmd_cmd_null_0, pmd_cmdc0_mml_mask_fm, pmd_cmdbf_lfo2, pmd_cmdbe_lfo2_switch_fm, pmd_cmdbd_lfo2_md, pmd_cmdbc_lfo2_waveform, pmd_cmdbb_lfo2_ext, pmd_cmdba_lfo2_slotmask, pmd_cmdb9_lfo2_delay, pmd_cmdb8_slottl, pmd_cmdb7_lfo_md_cnt, pmd_cmdb6_fb, pmd_cmdb5_slot_delay, pmd_cmd_null_16, pmd_cmdb3_gate_min, pmd_cmdb2_transpose_master, pmd_cmdb1_gate_rand_range }; static const pmd_cmd_func pmd_cmd_table_ssg[PMD_CMD_CNT] = { pmd_cmd_null_1, pmd_cmdfe_gate_abs, pmd_cmdfd_vol, pmd_cmdfc_tempo, pmd_cmdfb_tie, pmd_cmdfa_det, pmd_cmdf9_repeat_reset, pmd_cmdf8_repeat, pmd_cmdf7_repeat_exit, pmd_cmdf6_set_loop, pmd_cmdf5_transpose, pmd_cmdf4_volinc_ssg, pmd_cmdf3_voldec_ssg, pmd_cmdf2_lfo, pmd_cmdf1_lfo_switch, pmd_cmdf0_env_old, pmd_cmdef_poke, pmd_cmdee_noise_freq, pmd_cmded_ssgmix, pmd_cmd_null_1, pmd_cmdeb_opnarhythm, pmd_cmdea_opnarhythm_il, pmd_cmde9_opnarhythm_pan, pmd_cmde8_opnarhythm_tl, pmd_cmde7_transpose_rel, pmd_cmde6_opnarhythm_tl_rel, pmd_cmde5_opnarhythm_il_rel, pmd_cmd_null_1, pmd_cmde3_vol_add_ssg, pmd_cmde2_vol_sub, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmddf_meas_len, pmd_cmdde_echo_init_add_ssg, pmd_cmddd_echo_init_sub, pmd_cmddc_status1, pmd_cmddb_status1_add, pmd_cmdda_portamento_ssg, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdd6_md, pmd_cmdd5_detrel, pmd_cmdd4_ssgeff, pmd_cmdd3_fmeff, pmd_cmdd2_fadeout, pmd_cmd_null_1, pmd_cmdd0_ssgnoisefreqadd, pmd_cmd_null_1, pmd_cmd_null_6, pmd_cmdcd_env_new, pmd_cmdcc_det_ext, pmd_cmdcb_lfo_waveform, pmd_cmdca_lfo_ext, pmd_cmdc9_env_ext, pmd_cmd_null_3, pmd_cmd_null_3, pmd_cmd_null_6, pmd_cmd_null_1, pmd_cmdc4_gate_rel, pmd_cmd_null_2, pmd_cmdc2_lfo_delay, pmd_cmd_null_0, pmd_cmdc0_mml_mask_ssg, pmd_cmdbf_lfo2, pmd_cmdbe_lfo2_switch, pmd_cmdbd_lfo2_md, pmd_cmdbc_lfo2_waveform, pmd_cmdbb_lfo2_ext, pmd_cmd_null_1, pmd_cmdb9_lfo2_delay, pmd_cmd_null_2, pmd_cmdb7_lfo_md_cnt, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmd_null_16, pmd_cmdb3_gate_min, pmd_cmdb2_transpose_master, pmd_cmdb1_gate_rand_range }; static const pmd_cmd_func pmd_cmd_table_rhythm[PMD_CMD_CNT] = { pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdfd_vol, pmd_cmdfc_tempo, pmd_cmdfb_tie, pmd_cmdfa_det, pmd_cmdf9_repeat_reset, pmd_cmdf8_repeat, pmd_cmdf7_repeat_exit, pmd_cmdf6_set_loop, pmd_cmd_null_1, pmd_cmdf4_volinc_ssg, pmd_cmdf3_voldec_ssg, pmd_cmd_null_4, pmd_cmdf1_ppsdrv, pmd_cmd_null_4, pmd_cmdef_poke, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdeb_opnarhythm, pmd_cmdea_opnarhythm_il, pmd_cmde9_opnarhythm_pan, pmd_cmde8_opnarhythm_tl, pmd_cmd_null_1, pmd_cmde6_opnarhythm_tl_rel, pmd_cmde5_opnarhythm_il_rel, pmd_cmd_null_1, pmd_cmde3_vol_add_ssg, pmd_cmde2_vol_sub, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmddf_meas_len, pmd_cmdde_echo_init_add_ssg, pmd_cmddd_echo_init_sub, pmd_cmddc_status1, pmd_cmddb_status1_add, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmdd5_detrel, pmd_cmdd4_ssgeff, pmd_cmdd3_fmeff, pmd_cmdd2_fadeout, pmd_cmd_null_1, pmd_cmd_null_1, // d0 pmd_cmd_null_1, pmd_cmd_null_6, pmd_cmd_null_5, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_3, // c8 pmd_cmd_null_3, pmd_cmd_null_6, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmd_null_1, pmd_cmd_null_0, pmd_cmdc0_mml_mask_rhythm, // c0 pmd_cmd_null_4, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_2, // b8 pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmd_null_16, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1 }; static const pmd_cmd_func pmd_cmd_table_adpcm[PMD_CMD_CNT] = { pmd_cmdff_tonenum_adpcm, pmd_cmdfe_gate_abs, pmd_cmdfd_vol, pmd_cmdfc_tempo, pmd_cmdfb_tie, pmd_cmdfa_det, pmd_cmdf9_repeat_reset, pmd_cmdf8_repeat, pmd_cmdf7_repeat_exit, pmd_cmdf6_set_loop, pmd_cmdf5_transpose, pmd_cmdf4_volinc_adpcm, pmd_cmdf3_voldec_adpcm, pmd_cmdf2_lfo, pmd_cmdf1_lfo_switch, pmd_cmdf0_env_old, pmd_cmdef_poke, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdec_pan_adpcm, pmd_cmdeb_opnarhythm, pmd_cmdea_opnarhythm_il, pmd_cmde9_opnarhythm_pan, pmd_cmde8_opnarhythm_tl, pmd_cmde7_transpose_rel, pmd_cmde6_opnarhythm_tl_rel, pmd_cmde5_opnarhythm_il_rel, pmd_cmd_null_1, pmd_cmde3_vol_add_adpcm, pmd_cmde2_vol_sub, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmddf_meas_len, pmd_cmdde_echo_init_add_adpcm, pmd_cmddd_echo_init_sub, pmd_cmddc_status1, pmd_cmddb_status1_add, pmd_cmdda_portamento_adpcm, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdd6_md, pmd_cmdd5_detrel, pmd_cmdd4_ssgeff, pmd_cmdd3_fmeff, pmd_cmdd2_fadeout, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdce_adpcm_loop, pmd_cmdcd_env_new, pmd_cmd_null_1, pmd_cmdcb_lfo_waveform, pmd_cmdca_lfo_ext, pmd_cmdc9_env_ext, pmd_cmd_null_3, pmd_cmd_null_3, pmd_cmd_null_6, pmd_cmd_null_1, pmd_cmdc4_gate_rel, pmd_cmdc3_pan_ex_adpcm, pmd_cmdc2_lfo_delay, pmd_cmd_null_0, pmd_cmdc0_mml_mask_adpcm, pmd_cmdbf_lfo2, pmd_cmdbe_lfo2_switch, pmd_cmdbd_lfo2_md, pmd_cmdbc_lfo2_waveform, pmd_cmdbb_lfo2_ext, pmd_cmdba_lfo2_slotmask, // slotmask on ADPCM?? pmd_cmdb9_lfo2_delay, pmd_cmd_null_2, pmd_cmdb7_lfo_md_cnt, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmdb4_ppz8_init, pmd_cmdb3_gate_min, pmd_cmdb2_transpose_master, pmd_cmdb1_gate_rand_range }; static const pmd_cmd_func pmd_cmd_table_ppz8[PMD_CMD_CNT] = { pmd_cmdff_tonenum_ppz8, pmd_cmdfe_gate_abs, pmd_cmdfd_vol, pmd_cmdfc_tempo, pmd_cmdfb_tie, pmd_cmdfa_det, pmd_cmdf9_repeat_reset, pmd_cmdf8_repeat, pmd_cmdf7_repeat_exit, pmd_cmdf6_set_loop, pmd_cmdf5_transpose, pmd_cmdf4_volinc_adpcm, pmd_cmdf3_voldec_adpcm, pmd_cmdf2_lfo, pmd_cmdf1_lfo_switch, pmd_cmdf0_env_old, pmd_cmdef_poke, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdec_pan_ppz8, pmd_cmdeb_opnarhythm, pmd_cmdea_opnarhythm_il, pmd_cmde9_opnarhythm_pan, pmd_cmde8_opnarhythm_tl, pmd_cmde7_transpose_rel, pmd_cmde6_opnarhythm_tl_rel, pmd_cmde5_opnarhythm_il_rel, pmd_cmd_null_1, pmd_cmde3_vol_add_adpcm, pmd_cmde2_vol_sub, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmddf_meas_len, pmd_cmdde_echo_init_add_adpcm, pmd_cmddd_echo_init_sub, pmd_cmddc_status1, pmd_cmddb_status1_add, pmd_cmdda_portamento_ppz8, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdd6_md, pmd_cmdd5_detrel, pmd_cmdd4_ssgeff, pmd_cmdd3_fmeff, pmd_cmdd2_fadeout, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmd_null_1, pmd_cmdce_ppz8_loop, pmd_cmdcd_env_new, pmd_cmd_null_1, pmd_cmdcb_lfo_waveform, pmd_cmdca_lfo_ext, pmd_cmdc9_env_ext, pmd_cmd_null_3, pmd_cmd_null_3, pmd_cmd_null_6, pmd_cmd_null_1, pmd_cmdc4_gate_rel, pmd_cmdc3_pan_ex_ppz8, pmd_cmdc2_lfo_delay, pmd_cmd_null_0, pmd_cmdc0_mml_mask_ppz8, pmd_cmdbf_lfo2, pmd_cmdbe_lfo2_switch, pmd_cmdbd_lfo2_md, pmd_cmdbc_lfo2_waveform, pmd_cmdbb_lfo2_ext, pmd_cmdba_lfo2_slotmask, // slotmask on PPZ8?? pmd_cmdb9_lfo2_delay, pmd_cmd_null_2, pmd_cmdb7_lfo_md_cnt, pmd_cmd_null_1, pmd_cmd_null_2, pmd_cmd_null_16, pmd_cmdb3_gate_min, pmd_cmdb2_transpose_master, pmd_cmdb1_gate_rand_range }; // 1857 static void pmd_part_cmd_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t cmd ) { if (cmd < 0xb1) { part->ptr = 0; return; } pmd_cmd_table_ssg[cmd^0xff](work, pmd, part); } // 184d static void pmd_part_cmd_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t cmd ) { if (cmd < 0xb1) { part->ptr = 0; return; } pmd_cmd_table_fm[cmd^0xff](work, pmd, part); } // 1852 static void pmd_part_cmd_rhythm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t cmd ) { if (cmd < 0xb1) { part->ptr = 0; return; } pmd_cmd_table_rhythm[cmd^0xff](work, pmd, part); } // 02c6 static void pmd_part_cmd_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t cmd ) { if (cmd < 0xb1) { part->ptr = 0; return; } pmd_cmd_table_adpcm[cmd^0xff](work, pmd, part); } // 02c6 static void pmd_part_cmd_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t cmd ) { if (cmd < 0xb1) { part->ptr = 0; return; } pmd_cmd_table_ppz8[cmd^0xff](work, pmd, part); } // 20e8 static void pmd_portamento_tick( struct pmd_part *part ) { part->portamento_diff = u16s16(((uint16_t)part->portamento_diff) + ((uint16_t)part->portamento_add)); if (part->portamento_rem > 0) { part->portamento_rem--; part->portamento_diff = u16s16(((uint16_t)part->portamento_diff) + 1); } else if (part->portamento_rem < 0) { part->portamento_rem++; part->portamento_diff = u16s16(((uint16_t)part->portamento_diff) - 1); } } // 3161 static void pmd_ssg_env_tick_old( struct pmd_part *part ) { switch (part->ssg_env_state_old) { case SSG_ENV_STATE_OLD_AL: // 3167 if (--part->ssg_env_param_set[SSG_ENV_PARAM_OLD_AL]) return; part->ssg_env_state_old = SSG_ENV_STATE_OLD_SR; part->ssg_env_vol = u8s8(part->ssg_env_param_set[SSG_ENV_PARAM_OLD_AD]); return; case SSG_ENV_STATE_OLD_SR: // 317c if (!part->ssg_env_param_set[SSG_ENV_PARAM_OLD_SR]) return; if (--part->ssg_env_param_set[SSG_ENV_PARAM_OLD_SR]) return; part->ssg_env_param_set[SSG_ENV_PARAM_OLD_SR] = part->ssg_env_param[SSG_ENV_PARAM_OLD_SR]; part->ssg_env_vol = u8s8(((uint8_t)part->ssg_env_vol) - 1); if ((-15 <= part->ssg_env_vol) && (part->ssg_env_vol < 15)) return; part->ssg_env_vol = -15; return; case SSG_ENV_STATE_OLD_RR: // 31a3 if (!part->ssg_env_param_set[SSG_ENV_PARAM_OLD_RR]) { part->ssg_env_vol = -15; return; } if (--part->ssg_env_param_set[SSG_ENV_PARAM_OLD_RR]) return; part->ssg_env_param_set[SSG_ENV_PARAM_OLD_RR] = part->ssg_env_param[SSG_ENV_PARAM_OLD_RR]; part->ssg_env_vol = u8s8(((uint8_t)part->ssg_env_vol) - 1); if ((-15 <= part->ssg_env_vol) && (part->ssg_env_vol < 15)) return; part->ssg_env_vol = -15; return; } } // 314e // returns true if volume update needed static bool pmd_ssg_env_tick_check( struct pmd_part *part ) { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { return pmd_ssg_env_tick_new_check(part); } int8_t old_env_vol = part->ssg_env_vol; pmd_ssg_env_tick_old(part); return old_env_vol != part->ssg_env_vol; } // 312e // returns true if volume update needed static bool pmd_ssg_env_proc( struct driver_pmd *pmd, struct pmd_part *part ) { if (part->flagext.env) { // 3134 uint8_t timera_lapsed = pmd->timera_cnt - pmd->timera_cnt_b; bool update_needed = false; for (int i = 0; i < timera_lapsed; i++) { update_needed |= pmd_ssg_env_tick_check(part); } return update_needed; } else { return pmd_ssg_env_tick_check(part); } } // 1617 static void pmd_part_proc_ssg_lfoenv( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { static const struct pmd_part_lfo_flags lfof_z; pmd->lfoprocf = lfof_z; pmd->lfoprocf_b = lfof_z; pmd->lfoprocf.portamento = part->lfof.portamento; if (part->lfof.freq || part->lfof.vol || part->lfof.sync || part->lfof.portamento || part->lfof_b.freq || part->lfof_b.vol || part->lfof_b.sync || part->lfof_b.portamento) { // 1625 if (part->lfof.freq || part->lfof.vol) { if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf.freq = part->lfof.freq; pmd->lfoprocf.vol = part->lfof.vol; } } // 1637 if (part->lfof_b.freq || part->lfof_b.vol) { pmd_part_lfo_flip(part); if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf_b.freq = part->lfof_b.freq; pmd->lfoprocf_b.vol = part->lfof_b.vol; } pmd_part_lfo_flip(part); } // 1659 if (pmd->lfoprocf.freq || pmd->lfoprocf.portamento || pmd->lfoprocf_b.freq) { if (pmd->lfoprocf.portamento) { pmd_portamento_tick(part); } pmd_ssg_freq_out(work, pmd, part); } } // 166d if (pmd_ssg_env_proc(pmd, part) || pmd->lfoprocf.vol || pmd->lfoprocf_b.vol || pmd->fadeout_speed) { pmd_ssg_vol_out(work, pmd, part); } pmd_part_loop_check(pmd, part); } // 01fa static void pmd_part_proc_adpcm_lfoenv( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { static const struct pmd_part_lfo_flags lfof_z; pmd->lfoprocf = lfof_z; pmd->lfoprocf_b = lfof_z; pmd->lfoprocf.portamento = part->lfof.portamento; if (part->lfof.freq || part->lfof.vol || part->lfof.sync || part->lfof.portamento || part->lfof_b.freq || part->lfof_b.vol || part->lfof_b.sync || part->lfof_b.portamento) { if (part->lfof.freq || part->lfof.vol) { if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf.freq = part->lfof.freq; pmd->lfoprocf.vol = part->lfof.vol; } } if (part->lfof_b.freq || part->lfof_b.vol) { pmd_part_lfo_flip(part); if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf_b.freq = part->lfof_b.freq; pmd->lfoprocf_b.vol = part->lfof_b.vol; } pmd_part_lfo_flip(part); } if (pmd->lfoprocf.freq || pmd->lfoprocf.portamento || pmd->lfoprocf_b.freq) { if (pmd->lfoprocf.portamento) { pmd_portamento_tick(part); } pmd_adpcm_freq_out(work, pmd, part); } } // 0250 if (pmd_ssg_env_proc(pmd, part) || pmd->lfoprocf.vol || pmd->lfoprocf_b.vol || pmd->fadeout_speed) { // 049a pmd_adpcm_vol_out(work, pmd, part); } pmd_part_loop_check(pmd, part); } // 084c static void pmd_part_proc_ppz8_lfoenv( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { static const struct pmd_part_lfo_flags lfof_z; pmd->lfoprocf = lfof_z; pmd->lfoprocf_b = lfof_z; pmd->lfoprocf.portamento = part->lfof.portamento; if (part->lfof.freq || part->lfof.vol || part->lfof.sync || part->lfof.portamento || part->lfof_b.freq || part->lfof_b.vol || part->lfof_b.sync || part->lfof_b.portamento) { // 085a if (part->lfof.freq || part->lfof.vol) { if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf.freq = part->lfof.freq; pmd->lfoprocf.vol = part->lfof.vol; } } if (part->lfof_b.freq || part->lfof_b.vol) { pmd_part_lfo_flip(part); if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf_b.freq = part->lfof_b.freq; pmd->lfoprocf_b.vol = part->lfof_b.vol; } pmd_part_lfo_flip(part); } // 088e if (pmd->lfoprocf.freq || pmd->lfoprocf.portamento || pmd->lfoprocf_b.freq) { if (pmd->lfoprocf.portamento) { pmd_portamento_tick(part); } pmd_ppz8_freq_out(work, pmd, part); } } // 08a2 if (pmd_ssg_env_proc(pmd, part) || pmd->lfoprocf.vol || pmd->lfoprocf_b.vol || pmd->fadeout_speed) { pmd_ppz8_vol_out(work, pmd, part); } pmd_part_loop_check(pmd, part); } // 16d9 static bool pmd_part_ssg_next_masked( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part, uint8_t note ) { if (part->mask.ext) return true; if (!part->mask.effect) return true; if (pmd->ssgeff_priority >= 2) return true; if ((note & 0xf) == 0xf) return true; if (pmd->ssgeff_priority == 1) { pmd_ssgeff_stop(work, pmd); } part->mask.effect = false; return pmd_part_masked(part); } // 1561 static void pmd_part_loop_check_masked( struct driver_pmd *pmd, struct pmd_part *part ) { pmd->no_keyoff = false; pmd->volume_saved = false; pmd_part_loop_check(pmd, part); } // 1541 static void pmd_part_proc_note_masked( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { part->actual_freq = 0; part->actual_note = 0xff; part->curr_note = 0xff; part->len = part->len_cnt = pmd_part_cmdload(pmd, part); part->note_proc++; if (!pmd->volume_saved) { part->volume_save = 0; } pmd->no_keyoff = false; pmd->volume_saved = false; pmd_part_loop_check(pmd, part); } // 2cb5 static void pmd_part_off_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->keystatus.off && part->keystatus.off_mask) return; pmd_part_do_keyoff_fm(work, pmd, part); } // 13f8 static void pmd_part_proc_fm_lfo( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->hlfo_delay) { if (!--part->hlfo_delay) { pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, part->pan); } } // 1410 if (part->slot_delay_cnt) { if (!--part->slot_delay_cnt) { if (!part->keystatus.off) { pmd_part_fm_keyon(work, pmd, part); } } } static const struct pmd_part_lfo_flags lfof_z; pmd->lfoprocf = lfof_z; pmd->lfoprocf_b = lfof_z; pmd->lfoprocf.portamento = part->lfof.portamento; // 1424 if (part->lfof.freq || part->lfof.vol || part->lfof.sync || part->lfof.portamento || part->lfof_b.freq || part->lfof_b.vol || part->lfof_b.sync || part->lfof_b.portamento) { if (part->lfof.freq || part->lfof.vol) { if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf.freq = part->lfof.freq; pmd->lfoprocf.vol = part->lfof.vol; } } if (part->lfof_b.freq || part->lfof_b.vol) { pmd_part_lfo_flip(part); if (pmd_lfo_tick(pmd, part)) { pmd->lfoprocf_b.freq = part->lfof.freq; pmd->lfoprocf_b.vol = part->lfof.vol; } pmd_part_lfo_flip(part); } // 1466 if (pmd->lfoprocf.freq || pmd->lfoprocf.portamento || pmd->lfoprocf_b.freq) { if (pmd->lfoprocf.portamento) { pmd_portamento_tick(part); } pmd_fm_freq_out(work, pmd, part); } // 147a } // 1481 if (pmd->lfoprocf.vol || pmd->lfoprocf_b.vol || pmd->fadeout_speed) { pmd_fm_vol_out(work, pmd, part); } pmd_part_loop_check(pmd, part); } // 1350 / 14fc static void pmd_part_proc_fm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (!part->ptr) return; part->proc_masked = pmd_part_masked(part); part->len_cnt--; if (part->proc_masked) { part->keystatus.off = true; part->keystatus.off_mask = true; } else if (!part->keystatus.off && !part->keystatus.off_mask) { if (part->len_cnt <= part->gate) { pmd_part_off_fm(work, pmd, part); part->keystatus.off = true; part->keystatus.off_mask = true; } } // 1377 if (part->len_cnt) { if (part->proc_masked) { pmd_part_loop_check(pmd, part); } else { pmd_part_proc_fm_lfo(work, pmd, part); } return; } if (part->proc_masked) { // 1505 if (part->mask.effect && !pmd->fmeff_playing) { part->mask.effect = false; part->proc_masked = pmd_part_masked(part); } } // 137b if (!part->proc_masked) { part->lfof.portamento = false; } for (;;) { // 137f / 151b uint8_t cmd = pmd_part_cmdload(pmd, part); if (cmd & 0x80) { if (cmd != 0x80) { // 1386 / 1522 pmd_part_cmd_fm(work, pmd, part, cmd); if (cmd == 0xda && !pmd_part_masked(part)) return; } else { // 138b / 1527 part->ptr = 0; part->loop.looped = true; part->loop.ended = true; part->actual_note = 0xff; if (!part->loop_ptr) { if (part->proc_masked) { pmd_part_loop_check_masked(pmd, part); } else { pmd_part_proc_fm_lfo(work, pmd, part); } return; } part->ptr = part->loop_ptr; part->loop.ended = false; } } else { // 13a5 / 1541 if (part->proc_masked) { pmd_part_proc_note_masked(work, pmd, part); return; } else { cmd = pmd_part_lfo_init_fm(work, pmd, part, cmd); cmd = pmd_part_note_transpose(part, cmd); pmd_note_freq_fm(part, cmd); part->len = part->len_cnt = pmd_part_cmdload(pmd, part); pmd_part_calc_gate(pmd, part); // 13b5 pmd_part_fm_out(work, pmd, part); return; } } } } // 156f / 1689 static void pmd_part_proc_ssg( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { /* if (!part->ptr) { // original if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { part->ssg_env_state_new = SSG_ENV_STATE_NEW_RR; } else { part->ssg_env_state_old = SSG_ENV_STATE_OLD_RR; } // original end return; } */ part->proc_masked = pmd_part_masked(part); part->len_cnt--; if (part->proc_masked) { part->keystatus.off = true; part->keystatus.off_mask = true; } else if (!part->keystatus.off && !part->keystatus.off_mask) { if (part->len_cnt <= part->gate) { pmd_part_off_ssg(part); part->keystatus.off = true; part->keystatus.off_mask = true; } } // 1596 if (part->len_cnt) { if (part->proc_masked) { pmd_part_loop_check(pmd, part); } else { pmd_part_proc_ssg_lfoenv(work, pmd, part); } return; } part->lfof.portamento = false; for (;;) { // 159e / 1699 uint8_t cmd = pmd_part_cmdload(pmd, part); if (cmd & 0x80) { if (cmd != 0x80) { if (part->proc_masked && (cmd == 0xda)) { // 16a0 part->proc_masked = pmd_part_ssg_next_masked(work, pmd, part, cmd); } // 15a5 pmd_part_cmd_ssg(work, pmd, part, cmd); if (cmd == 0xda && !pmd_part_masked(part)) return; } else { // 15aa part->ptr = 0; part->loop.looped = true; part->loop.ended = true; part->actual_note = 0xff; if (!part->loop_ptr) { if (part->proc_masked) { pmd_part_loop_check_masked(pmd, part); } else { // 1617 pmd_part_proc_ssg_lfoenv(work, pmd, part); } return; } // 15bc part->ptr = part->loop_ptr; part->loop.ended = false; } } else { if (part->proc_masked && pmd_part_ssg_next_masked(work, pmd, part, cmd)) { // 16ce pmd_part_proc_note_masked(work, pmd, part); return; } else { // 15c4 cmd = pmd_part_lfo_init_ssg(work, pmd, part, cmd); cmd = pmd_part_note_transpose(part, cmd); pmd_note_freq_ssg(part, cmd); part->len = part->len_cnt = pmd_part_cmdload(pmd, part); pmd_part_calc_gate(pmd, part); // 15d4 if (part->volume_save && (part->actual_note != 0xff)) { if (!pmd->volume_saved) { part->volume_save = 0; } pmd->volume_saved = false; } // 15ef pmd_ssg_vol_out(work, pmd, part); pmd_ssg_freq_out(work, pmd, part); pmd_ssg_note_noise_out(work, pmd, part); part->note_proc++; pmd->no_keyoff = false; pmd->volume_saved = false; part->keystatus.off = false; part->keystatus.off_mask = false; // fb if (pmd->datalen > (part->ptr + 1)) { if (pmd->data[part->ptr] == 0xfb) { part->keystatus.off_mask = true; } } pmd_part_loop_check(pmd, part); return; } } } //pmd_part_proc_ssg_lfoenv(work, pmd, part); } // 42d6 static const struct pmd_ssgrhythm_opna_table_entry { uint8_t reg; uint8_t data; uint8_t kon; } pmd_ssgrhythm_opna_table[11] = { {0x18, 0xdf, 0x01}, {0x19, 0xdf, 0x02}, {0x1c, 0x5f, 0x10}, {0x1c, 0xdf, 0x10}, {0x1c, 0x9f, 0x10}, {0x1d, 0xd3, 0x20}, {0x19, 0xdf, 0x02}, {0x1b, 0x9c, 0x88}, {0x1a, 0x9d, 0x04}, {0x1a, 0xdf, 0x04}, {0x1a, 0x5e, 0x04}, }; // 1813 static void pmd_ssgrhythm_opna_out( struct fmdriver_work *work, struct driver_pmd *pmd, const struct pmd_ssgrhythm_opna_table_entry *r ) { work->opna_writereg(work, r->reg, r->data); uint8_t kon = r->kon & pmd->opnarhythm_mask; if (!kon) return; if (kon & 0x80) { // 1828 work->opna_writereg(work, 0x10, 0x84); kon = 0x08 & pmd->opnarhythm_mask; if (!kon) return; } work->opna_writereg(work, 0x10, kon); } // 170d static void pmd_part_proc_opnarhythm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (!part->ptr) return; if (--part->len_cnt) { pmd_part_loop_check(pmd, part); return; } // 171b for (;;) { while (pmd->r_ptr && (pmd->datalen >= (pmd->r_ptr+1)) && (pmd->data[pmd->r_ptr] != 0xff)) { // 1726 uint8_t rcmd = pmd->data[pmd->r_ptr++]; if (rcmd & 0x80) { // 1785 if (rcmd & 0x40) { uint16_t tmp = part->ptr; part->ptr = pmd->r_ptr; pmd->r_ptr = tmp; pmd_part_cmd_rhythm(work, pmd, part, rcmd); tmp = part->ptr; part->ptr = pmd->r_ptr; pmd->r_ptr = tmp; continue; } // 1794 if (pmd_part_masked(part)) { pmd->ssgrhythm = 0; pmd->r_ptr++; part->len = part->len_cnt = pmd->data[pmd->r_ptr++]; pmd_part_loop_check_masked(pmd, part); part->note_proc++; return; } pmd->ssgrhythm = ((rcmd << 8) | pmd->data[pmd->r_ptr++]) & 0x3fff; if (pmd->ssgrhythm) { // 17b0 if (pmd->ssgrhythm_opna) { for (int i = 0; i < 11; i++) { if (pmd->ssgrhythm & (1<fadeout_vol) { if (pmd->ssgrhythm_opna) { pmd_opnarhythm_tl_write(work, pmd, pmd->opnarhythm_tl); } } // 17ea if (!pmd->fadeout_vol || pmd->ppsdrv_enabled) { // TODO: PPSDRV } // 180c uint8_t eff; for (eff = 0;; eff++) { if (pmd->ssgrhythm & (1<ssgrhythm = 0; } part->len = part->len_cnt = pmd->data[pmd->r_ptr++]; pmd_part_loop_check_masked(pmd, part); part->note_proc++; return; } // 1740 for (;;) { uint8_t cmd = pmd_part_cmdload(pmd, part); if (cmd & 0x80) { if (cmd != 0x80) { pmd_part_cmd_rhythm(work, pmd, part, cmd); } else { // 1765 part->ptr = 0; part->loop.looped = true; part->loop.ended = true; if (!part->loop_ptr) { part->ptr = 0; pmd_part_loop_check_masked(pmd, part); return; } part->ptr = part->loop_ptr; part->loop.ended = false; } } else { // 174c uint16_t ptr = pmd->r_offset + cmd*2; if (pmd->datalen < (ptr + 2)) { part->ptr = 0; return; } pmd->r_ptr = pmd->data[ptr] | (((uint16_t)pmd->data[ptr+1]) << 8); break; } } } } // 05cf static void pmd_part_off_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { if (part->ssg_env_state_new == SSG_ENV_STATE_NEW_RR) return; } else { if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_RR) return; } // 05e2 if (pmd->adpcm_release != 0x8000) { work->opna_writereg(work, 0x100, 0x21); work->opna_writereg(work, 0x102, pmd->adpcm_release); work->opna_writereg(work, 0x103, pmd->adpcm_release>>8); work->opna_writereg(work, 0x104, pmd->adpcm_stop); work->opna_writereg(work, 0x105, pmd->adpcm_stop>>8); work->opna_writereg(work, 0x100, 0xa0); } pmd_part_off_ssg(part); } // 0c0e static void pmd_part_off_ppz8( struct pmd_part *part ) { if (part->ssg_env_state_old != SSG_ENV_STATE_OLD_NEW) { if (part->ssg_env_state_old != SSG_ENV_STATE_OLD_RR) pmd_part_off_ssg(part); } else { if (part->ssg_env_state_new != SSG_ENV_STATE_NEW_RR) pmd_part_off_ssg(part); } } // 0149 / 026c static void pmd_part_proc_adpcm( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { if (!part->ptr) return; part->proc_masked = pmd_part_masked(part); part->len_cnt--; if (part->proc_masked) { part->keystatus.off = true; part->keystatus.off_mask = true; } else if (!part->keystatus.off && !part->keystatus.off_mask) { //0164 if (part->len_cnt <= part->gate) { part->keystatus.off = true; part->keystatus.off_mask = true; pmd_part_off_adpcm(work, pmd, part); } } // 0170 / 0273 if (part->len_cnt) { if (part->proc_masked) { pmd_part_loop_check(pmd, part); } else { pmd_part_proc_adpcm_lfoenv(work, pmd, part); } return; } // PCM effect not implemented // 0177 part->lfof.portamento = false; for (;;) { // 017b / 029a uint8_t cmd = pmd_part_cmdload(pmd, part); if (cmd & 0x80) { if (cmd != 0x80) { pmd_part_cmd_adpcm(work, pmd, part, cmd); if (cmd == 0xda && !pmd_part_masked(part)) return; } else { // 0187 part->ptr = 0; part->loop.looped = true; part->loop.ended = true; part->actual_note = 0xff; if (!part->loop_ptr) { if (part->proc_masked) { pmd_part_loop_check_masked(pmd, part); } else { pmd_part_proc_adpcm_lfoenv(work, pmd, part); } return; } part->ptr = part->loop_ptr; part->loop.ended = false; } } else { // 01a1 / 02a1 if (part->proc_masked) { pmd_part_proc_note_masked(work, pmd, part); } else { cmd = pmd_part_lfo_init_ssg(work, pmd, part, cmd); cmd = pmd_part_note_transpose(part, cmd); pmd_note_freq_adpcm(part, cmd); part->len = part->len_cnt = pmd_part_cmdload(pmd, part); pmd_part_calc_gate(pmd, part); pmd_part_adpcm_out(work, pmd, part); } return; } } } // 079b / 08be static void pmd_part_proc_ppz8( struct fmdriver_work *work, struct driver_pmd *pmd, struct pmd_part *part ) { //if (!part->ptr) return; part->proc_masked = pmd_part_masked(part); part->len_cnt--; if (part->proc_masked) { part->keystatus.off = true; part->keystatus.off_mask = true; } else if (!part->keystatus.off && !part->keystatus.off_mask) { // 07b6 if (part->len_cnt <= part->gate) { part->keystatus.off = true; part->keystatus.off_mask = true; pmd_part_off_ppz8(part); } } // 07c2 if (part->len_cnt) { if (part->proc_masked) { pmd_part_loop_check(pmd, part); } else { pmd_part_proc_ppz8_lfoenv(work, pmd, part); } return; } part->lfof.portamento = false; for (;;) { // 07cd / 08ca uint8_t cmd = pmd_part_cmdload(pmd, part); if (cmd & 0x80) { if (cmd != 0x80) { pmd_part_cmd_ppz8(work, pmd, part, cmd); if (cmd == 0xda && !pmd_part_masked(part)) return; } else { // 07d9 / 08d6 part->ptr = 0; part->loop.looped = true; part->loop.ended = true; part->actual_note = 0xff; if (!part->loop_ptr) { if (part->proc_masked) { pmd_part_loop_check_masked(pmd, part); } else { pmd_part_proc_ppz8_lfoenv(work, pmd, part); } return; } part->ptr = part->loop_ptr; part->loop.ended = false; } } else { // 07f3 / 08f3 if (part->proc_masked) { part->actual_freq_upper = 0; pmd_part_proc_note_masked(work, pmd, part); } else { cmd = pmd_part_lfo_init_ssg(work, pmd, part, cmd); cmd = pmd_part_note_transpose(part, cmd); pmd_note_freq_ppz8(part, cmd); part->len = part->len_cnt = pmd_part_cmdload(pmd, part); pmd_part_calc_gate(pmd, part); pmd_part_ppz8_out(work, pmd, part); } return; } } } // 11fd static void pmd_proc_parts( struct fmdriver_work *work, struct driver_pmd *pmd ) { pmd->loop.looped = true; pmd->loop.ended = true; pmd->loop.env = false; if (!pmd->opm_flag) { for (int c = 0; c < 3; c++) { pmd->proc_ch = c+1; struct pmd_part *part = &pmd->parts[PMD_PART_SSG_1+c]; pmd_part_proc_ssg(work, pmd, part); } } // 122a pmd_opna_a1_on(pmd); for (int c = 0; c < 3; c++) { pmd->proc_ch = c+1; struct pmd_part *part = &pmd->parts[PMD_PART_FM_4+c]; pmd_part_proc_fm(work, pmd, part); } pmd_opna_a1_off(pmd); for (int c = 0; c < 3; c++) { pmd->proc_ch = c+1; struct pmd_part *part = &pmd->parts[PMD_PART_FM_1+c]; pmd_part_proc_fm(work, pmd, part); } // 1272 for (int c = 0; c < 3; c++) { struct pmd_part *part = &pmd->parts[PMD_PART_FM_3B+c]; pmd_part_proc_fm(work, pmd, part); } // 1284 if (!pmd->opm_flag) { pmd_part_proc_opnarhythm(work, pmd, &pmd->parts[PMD_PART_RHYTHM]); pmd_part_proc_adpcm(work, pmd, &pmd->parts[PMD_PART_ADPCM]); for (int c = 0; c < 8; c++) { pmd->proc_ch = c; struct pmd_part *part = &pmd->parts[PMD_PART_PPZ_1+c]; pmd_part_proc_ppz8(work, pmd, part); } } // 12ef if (!pmd->loop.looped && !pmd->loop.ended && !pmd->loop.env) return; // 12f7 for (int i = 0; i < 12; i++) { struct pmd_part *part = &pmd->parts[PMD_PART_FM_1+i]; if (!part->loop.looped || !part->loop.ended || part->loop.env) { part->loop.looped = false; part->loop.ended = false; part->loop.env = false; } } // 130d if (!pmd->loop.looped || !pmd->loop.ended || pmd->loop.env) { work->timerb_cnt_loop = 0; if (++pmd->status2 == 0xff) pmd->status2 = 1; } else { pmd->status2 = 0xff; // stop work->playing = false; } work->loop_cnt = pmd->status2; } // 3e2e static void pmd_update_note_meas( struct driver_pmd *pmd ) { uint8_t tick = ++pmd->tick_cnt; if (tick == pmd->meas_len) { pmd->tick_cnt = 0; pmd->meas_cnt++; } } // 3d68 static void pmd_timerb( struct fmdriver_work *work, struct driver_pmd *pmd ) { if (pmd->playing) { // 3d8f pmd_proc_parts(work, pmd); pmd_timerb_write(work, pmd); pmd_update_note_meas(pmd); pmd->timera_cnt_b = pmd->timera_cnt; } // 3d9e // user interrupt } // 329e static void pmd_fadeout_tick( struct fmdriver_work *work, struct driver_pmd *pmd ) { if (pmd->paused) return; if (!pmd->fadeout_speed) return; int newvol = pmd->fadeout_vol + u8s8(pmd->fadeout_speed); if (newvol < 0) { newvol = 0; pmd->fadeout_vol = 0; pmd->fadeout_speed = 0; pmd_opnarhythm_tl_write(work, pmd, pmd->opnarhythm_tl); } else if (newvol > 0xff) { newvol = 0xff; pmd->fadeout_speed = 0; //if (pmd_fadeout_mstop) pmd-> } } // 3daf static void pmd_timera( struct fmdriver_work *work, struct driver_pmd *pmd ) { if (!(++pmd->timera_cnt & 7)) { // 3dbf pmd_fadeout_tick(work, pmd); // 3e66: fast-forward } if (pmd->ssgeff_priority) { // TODO: PPSDRV if (!pmd->ppsdrv_enabled || (!(pmd->ssgeff_num & 0x80))) { pmd_ssgeff_tick(work, pmd); } } // 3ddd if (pmd->fmeff_playing) { // TODO: FMEFF // call 0x0f03 } // esc/grph key check?? } // 3d1b static void pmd_timer( struct fmdriver_work *work, struct driver_pmd *pmd ) { uint8_t status = work->opna_status(work, 0); work->opna_writereg(work, 0x27, pmd->fm3ex_mode); if (status & 1) { pmd_timera(work, pmd); } if (status & 2) { pmd_timerb(work, pmd); if (work->playing) { work->timerb_cnt++; work->timerb_cnt_loop++; } } } enum pmd_part_type { PART_TYPE_FM, PART_TYPE_SSG, PART_TYPE_ADPCM, PART_TYPE_PPZ8 }; static const struct { uint8_t ind; enum pmd_part_type type; bool fm3ex; } pmd_track_map[FMDRIVER_TRACK_NUM] = { {PMD_PART_FM_1, PART_TYPE_FM, false}, {PMD_PART_FM_2, PART_TYPE_FM, false}, {PMD_PART_FM_3, PART_TYPE_FM, true}, {PMD_PART_FM_3B, PART_TYPE_FM, true}, {PMD_PART_FM_3C, PART_TYPE_FM, true}, {PMD_PART_FM_3D, PART_TYPE_FM, true}, {PMD_PART_FM_4, PART_TYPE_FM, false}, {PMD_PART_FM_5, PART_TYPE_FM, false}, {PMD_PART_FM_6, PART_TYPE_FM, false}, {PMD_PART_SSG_1, PART_TYPE_SSG, false}, {PMD_PART_SSG_2, PART_TYPE_SSG, false}, {PMD_PART_SSG_3, PART_TYPE_SSG, false}, {PMD_PART_ADPCM, PART_TYPE_ADPCM, false}, {PMD_PART_PPZ_1, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_2, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_3, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_4, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_5, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_6, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_7, PART_TYPE_PPZ8, false}, {PMD_PART_PPZ_8, PART_TYPE_PPZ8, false}, }; static void pmd_work_status_update( struct fmdriver_work *work, const struct driver_pmd *pmd ) { for (int t = 0; t < FMDRIVER_TRACK_NUM; t++) { struct fmdriver_track_status *track = &work->track_status[t]; const struct pmd_part *part = &pmd->parts[pmd_track_map[t].ind]; track->playing = !part->loop.ended; track->info = FMDRIVER_TRACK_INFO_NORMAL; track->ticks = part->len; track->ticks_left = part->len_cnt; track->key = part->actual_note; track->fmslotmask[0] = !(part->fm_slotmask & (1<<4)); track->fmslotmask[1] = !(part->fm_slotmask & (1<<5)); track->fmslotmask[2] = !(part->fm_slotmask & (1<<6)); track->fmslotmask[3] = !(part->fm_slotmask & (1<<7)); track->tonenum = part->tonenum; track->volume = part->vol; track->gate = part->gate; int detune = part->detune; if (detune > INT8_MAX) detune = INT8_MAX; if (detune < INT8_MIN) detune = INT8_MIN; track->detune = detune; track->status[0] = part->lfof.freq ? 'P' : '-'; track->status[1] = part->lfof.vol ? 'A' : '-'; track->status[2] = part->lfof.sync ? 'S' : '-'; track->status[3] = part->lfof_b.freq ? 'P' : '-'; track->status[4] = part->lfof_b.vol ? 'A' : '-'; track->status[5] = part->lfof_b.sync ? 'S' : '-'; track->status[6] = '-'; track->status[7] = part->lfof.portamento ? 'P' : '-'; switch (pmd_track_map[t].type) { case PART_TYPE_FM: track->actual_key = ((track->key & 0xf) == 0xf) ? 0xff : fmdriver_fm_freq2key(part->output_freq); if (pmd_track_map[t].fm3ex) { if (part->fm_slotmask != 0xf0) track->info = FMDRIVER_TRACK_INFO_FM3EX; } break; case PART_TYPE_SSG: track->info = FMDRIVER_TRACK_INFO_SSG; track->actual_key = ((track->key & 0xf) == 0xf) ? 0xff : fmdriver_ssg_freq2key(part->output_freq); track->ssg_tone = part->ssg_mix & (1<<0); track->ssg_noise = part->ssg_mix & (1<<3); if (part == &pmd->parts[PMD_PART_SSG_3]) { if (pmd->ssgeff_on) { track->info = FMDRIVER_TRACK_INFO_SSGEFF; track->tonenum = pmd->ssgeff_num; track->ssg_tone = pmd->ssgeff_tone_mix; track->ssg_noise = pmd->ssgeff_noise_mix; track->actual_key = pmd->ssgeff_tone_mix ? fmdriver_ssg_freq2key(pmd->ssgeff_tonefreq) : 0xff; } } break; case PART_TYPE_ADPCM: track->actual_key = ((track->key & 0xf) == 0xf) ? 0xff : pmd_adpcm_freq2key(part->output_freq); break; case PART_TYPE_PPZ8: track->actual_key = ((track->key & 0xf) == 0xf) ? 0xff : fmdriver_ppz8_freq2key(part->output_freq); break; } } work->ssg_noise_freq = pmd->ssg_noise_freq_wrote; } // 3c76 static void pmd_opna_interrupt(struct fmdriver_work *work) { struct driver_pmd *pmd = (struct driver_pmd *)work->driver; // 3d1b for (;;) { uint8_t status = work->opna_status(work, 0); if (status & 3) { pmd_timer(work, pmd); } else { break; } } // 3cc0 pmd_work_status_update(work, pmd); } /* // 0f4d static void pmd_mstart( struct fmdriver_work *work, struct driver_pmd *pmd ) { pmd_mstop(work, pmd); pmd_reset_state(pmd); pmd_data_init(pmd); pmd->fadeout_vol = 0; if (pmd->adpcm_disabled) { pmd->parts[PMD_PART_ADPCM].mask.disabled = true; } // reset PPZ8 // 0f99 pmd_reset_opna(work, pmd); pmd_reset_timer(work, pmd); pmd->playing = true; // TODO // 4375?? } */ bool pmd_load(struct driver_pmd *pmd, uint8_t *data, uint16_t datalen) { if (!datalen) return false; pmd->data = data + 1; pmd->datalen = datalen - 1; pmd_reset_state(pmd); pmd->ssgeff_num = 0xff; if (!pmd_data_init(pmd)) return false; pmd->fadeout_vol = 0; if (pmd->adpcm_disabled) { pmd->parts[PMD_PART_ADPCM].mask.disabled = true; } pmd->opnarhythm_mask = 0xff; pmd->ssgrhythm_opna = true; return true; } // check if null-terminated string is valid static const char *pmd_check_str( const struct driver_pmd *pmd, uint16_t ptr ) { uint16_t c = ptr; while (pmd->datalen >= (c+1)) { if (!pmd->data[c++]) return (const char *)&pmd->data[ptr]; } return 0; } // 383e const char *pmd_get_memo( const struct driver_pmd *pmd, int index ) { if (index < -2) return 0; if (pmd->datalen < 2) return 0; if (read16le(&pmd->data[0]) == 0x18) return 0; if (pmd->datalen < (0x18+2)) return 0; uint16_t toneptr = read16le(&pmd->data[0x18]); if (toneptr < 4) return 0; if (pmd->datalen < toneptr) return 0; uint8_t flaglow = pmd->data[toneptr-2]; uint8_t flaghigh = pmd->data[toneptr-1]; if (flaglow != 0x40) { if (flaghigh != 0xfe) return 0; if (flaglow < 0x41) return 0; } if (flaglow >= 0x42) index++; if (flaglow >= 0x48) index++; if (index < 0) return 0; // 3873 uint16_t memoptr = read16le(&pmd->data[toneptr-4]); while ((pmd->datalen >= (memoptr+2)) && read16le(&pmd->data[memoptr])) { if (index == 0) { if (pmd->datalen < (memoptr+2)) return 0; return pmd_check_str(pmd, read16le(&pmd->data[memoptr])); } memoptr += 2; index--; } return 0; } void pmd_filenamecopy(char *dest, const char *src) { int i; for (i = 0; i < (PMD_FILENAMELEN+1); i++) { if (src[i] == '.') { dest[i] = 0; return; } dest[i] = src[i]; if (!src[i]) return; } dest[0] = 0; } static const char *pmd_get_comment(struct fmdriver_work *work, int line) { struct driver_pmd *pmd = work->driver; if (line < 0) return 0; const char *str = pmd_get_memo(pmd, line + 1); if (str && !*str) return 0; return str; } void pmd_init(struct fmdriver_work *work, struct driver_pmd *pmd) { // TODO: reset ppz8 // 0f99 work->driver = pmd; work->comment_mode_pmd = true; work->get_comment = pmd_get_comment; pmd_reset_opna(work, pmd); pmd_reset_timer(work, pmd); pmd->playing = true; work->driver_opna_interrupt = pmd_opna_interrupt; /* static const int memotable[3] = {1, 4, 5}; for (int i = 0; i < 3; i++) { const char *title = pmd_get_memo(pmd, memotable[i]); int c = 0; if (title) { while (title[c] && (c < (FMDRIVER_TITLE_BUFLEN-1))) { work->comment[i][c] = title[c]; c++; } } work->comment[i][c] = 0; } */ const char *pcmfile = pmd_get_memo(pmd, -2); if (pcmfile) { pmd_filenamecopy(pmd->ppzfile, pcmfile); } pcmfile = pmd_get_memo(pmd, -1); if (pcmfile) { pmd_filenamecopy(pmd->ppsfile, pcmfile); } pcmfile = pmd_get_memo(pmd, 0); if (pcmfile) { pmd_filenamecopy(pmd->ppcfile, pcmfile); } fmdriver_fillpcmname(work->pcmname[0], pmd->ppcfile); fmdriver_fillpcmname(work->pcmname[1], pmd->ppzfile); work->playing = true; } enum { PPC_HEADER_SIZE = 30+2+4*256 }; bool pmd_ppc_load( struct fmdriver_work *work, uint8_t *data, size_t datalen ) { struct driver_pmd *pmd = (struct driver_pmd *)work->driver; if (datalen < PPC_HEADER_SIZE) return false; const char *header = "ADPCM DATA for PMD ver.4.4- "; for (int i = 0; i < 30; i++) { if (data[i] != (uint8_t)header[i]) return false; } for (int i = 0; i < 256; i++) { pmd->adpcm_addr[i][0] = read16le(&data[32+4*i+0]); pmd->adpcm_addr[i][1] = read16le(&data[32+4*i+2]); } work->opna_writereg(work, 0x100, 0x01); work->opna_writereg(work, 0x110, 0x13); work->opna_writereg(work, 0x110, 0x80); work->opna_writereg(work, 0x100, 0x60); work->opna_writereg(work, 0x101, 0x02); work->opna_writereg(work, 0x102, 0x00); work->opna_writereg(work, 0x103, 0x00); work->opna_writereg(work, 0x104, 0xff); work->opna_writereg(work, 0x105, 0xff); work->opna_writereg(work, 0x10c, 0xff); work->opna_writereg(work, 0x10d, 0xff); for (int i = 0; i < 0x4c0; i++) { work->opna_writereg(work, 0x108, 0); } for (size_t i = PPC_HEADER_SIZE; i < datalen; i++) { work->opna_writereg(work, 0x108, data[i]); } work->opna_writereg(work, 0x110, 0x0c); work->opna_writereg(work, 0x100, 0x01); return true; }