# HG changeset patch # User Mike Pavone # Date 1369718351 25200 # Node ID b7c3facee762c36e128af7f64df16479fe43d95b # Parent 946ae37492609d2a1db6ed1820063cf7c2d12560 YM2612 WIP update diff -r 946ae3749260 -r b7c3facee762 blastem.c --- a/blastem.c Mon May 27 20:56:02 2013 -0700 +++ b/blastem.c Mon May 27 22:19:11 2013 -0700 @@ -198,7 +198,7 @@ sync_z80(z_context, mclks); if (mclks >= mclks_per_frame) { ym_run(gen->ym, context->current_cycle); - gen->ym->current_cycle -= mclks_per_frame/MCLKS_PER_68K; + gen->ym->current_cycle -= ((mclks_per_frame/MCLKS_PER_68K) / 6) * 6; //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); vdp_run_context(v_context, mclks_per_frame); psg_run(gen->psg, mclks/MCLKS_PER_PSG); diff -r 946ae3749260 -r b7c3facee762 ym2612.c --- a/ym2612.c Mon May 27 20:56:02 2013 -0700 +++ b/ym2612.c Mon May 27 22:19:11 2013 -0700 @@ -6,22 +6,29 @@ #define BUSY_CYCLES 17 #define TIMERA_UPDATE_PERIOD 144 -#define REG_TIMERA_HIGH 0x03 // 0x24 -#define REG_TIMERA_LOW 0x04 // 0x25 -#define REG_TIMERB 0x05 // 0x26 -#define REG_TIME_CTRL 0x06 // 0x27 -#define REG_DAC 0x0A // 0x2A -#define REG_DAC_ENABLE 0x0B // 0x2B +enum { + REG_TIMERA_HIGH = 0x24, + REG_TIMERA_LOW, + REG_TIMERB, + REG_TIME_CTRL, + REG_KEY_ONOFF, + REG_DAC = 0x2A, + REG_DAC_ENABLE, -//offset to add to "shared" regs when looking for them in Part I -#define REG_SHARED 0x10 - + REG_DETUNE_MULT = 0x30, + REG_TOTAL_LEVEL = 0x40, + REG_ATTACK_KS = 0x50, + REG_DECAY_AM = 0x60, + REG_SUSTAIN_RATE = 0x70, + REG_S_LVL_R_RATE = 0x80, -#define REG_ALG_FEEDBACK (0xB0-0x30) -#define REG_ATTACK_KS (0x50-0x30) -#define REG_DECAY_AM (0x60-0x30) -#define REG_SUSTAIN_RATE (0x70-0x30) - + REG_FNUM_LOW = 0xA0, + REG_BLOCK_FNUM_H = 0xA4, + REG_FNUM_LOW_CH3 = 0xA8, + REG_BLOCK_FN_CH3 = 0xAC, + REG_ALG_FEEDBACK = 0xB0, + REG_LR_AMS_PMS = 0xB4 +}; #define BIT_TIMERA_ENABLE 0x1 #define BIT_TIMERB_ENABLE 0x2 @@ -52,6 +59,23 @@ #define POW_TABLE_SIZE (1 << 13) uint16_t pow_table[POW_TABLE_SIZE]; +uint16_t rate_table_base[] = { + //main portion + 0,1,0,1,0,1,0,1, + 0,1,0,1,1,1,0,1, + 0,1,1,1,0,1,1,1, + 0,1,1,1,1,1,1,1, + //top end + 1,1,1,1,1,1,1,1, + 1,1,1,2,1,1,1,2, + 1,2,1,2,1,2,1,2, + 1,2,2,2,1,2,2,2, +}; + +uint16_t rate_table[64]; + +#define MAX_ENVELOPE 0xFFC + uint16_t round_fixed_point(double value, int dec_bits) { @@ -61,6 +85,10 @@ void ym_init(ym2612_context * context) { memset(context, 0, sizeof(*context)); + for (int i = 0; i < NUM_OPERATORS; i++) { + context->operators[i].envelope = MAX_ENVELOPE; + context->operators[i].env_phase = PHASE_RELEASE; + } if (!did_tbl_init) { //populate sine table for (int32_t i = 0; i < 512; i++) { @@ -81,33 +109,52 @@ } pow_table[i] = tmp; } + //populate envelope generator rate table, from small base table + for (int rate = 0; rate < 64; rate++) { + for (int cycle = 0; cycle < 7; cycle++) { + uint16_t value; + if (rate < 3) { + value = 0; + } else if (value >= 60) { + value = 8; + } else if (value < 8) { + value = rate_table_base[((rate & 6) == 6 ? 16 : 8) + cycle]; + } else if (value < 48) { + value = rate_table_base[(rate & 0x3) * 8 + cycle]; + } else { + value = rate_table_base[32 + (rate & 0x3) * 8 + cycle] << (rate >> 2); + } + } + } } } void ym_run(ym2612_context * context, uint32_t to_cycle) { + //printf("Running YM2612 from cycle %d to cycle %d\n", context->current_cycle, to_cycle); + //TODO: Fix channel update order OR remap channels in register write for (; context->current_cycle < to_cycle; context->current_cycle += 6) { uint32_t update_cyc = context->current_cycle % 144; //Update timers at beginning of 144 cycle period - if (!update_cyc && context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) { + if (!update_cyc && context->timer_control & BIT_TIMERA_ENABLE) { if (context->timer_a) { context->timer_a--; } else { - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) { + if (context->timer_control & BIT_TIMERA_OVEREN) { context->status |= BIT_STATUS_TIMERA; } - context->timer_a = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3); + context->timer_a = context->timer_a_load; } - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) { + if (context->timer_control & BIT_TIMERB_ENABLE) { uint32_t b_cyc = (context->current_cycle / 144) % 16; if (!b_cyc) { if (context->timer_b) { context->timer_b--; } else { - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) { + if (context->timer_control & BIT_TIMERB_OVEREN) { context->status |= BIT_STATUS_TIMERB; } - context->timer_b = context->part1_regs[REG_TIMERB]; + context->timer_b = context->timer_b_load; } } } @@ -117,100 +164,376 @@ uint32_t env_cyc = context->current_cycle / 72; uint32_t op = env_cyc % 24; env_cyc /= 24; + ym_operator * operator = context->operators + op; + ym_channel * channel = context->channels + op/4; uint8_t rate; - switch(context->env_phase[op]) + for(;;) { + rate = operator->rates[operator->env_phase]; + if (rate) { + uint8_t ks = channel->keycode >> operator->key_scaling;; + rate = rate*2 + ks; + if (rate > 63) { + rate = 63; + } + } + //Deal with "infinite" rates + //It's possible this should be handled in key-on as well + if (rate == 63 && operator->env_phase < PHASE_SUSTAIN) { + if (operator->env_phase == PHASE_ATTACK) { + operator->env_phase = PHASE_DECAY; + operator->envelope = operator->total_level; + } else { + operator->env_phase = PHASE_SUSTAIN; + operator->envelope = operator->sustain_level; + } + } else { + break; + } + } + uint32_t cycle_shift = rate < 0x30 ? ((0x2F - rate) >> 2) : 0; + if (!(env_cyc & ((1 << cycle_shift) - 1))) { + uint32_t update_cycle = env_cyc >> cycle_shift & 0x7; + //envelope value is 10-bits, but it will be used as a 4.8 value + uint16_t envelope_inc = rate_table[rate * 8 + update_cycle] << 2; + if (operator->env_phase == PHASE_ATTACK) { + //this can probably be optimized to a single shift rather than a multiply + shift + operator->envelope += (~operator->envelope * envelope_inc) >> 4; + operator->envelope &= MAX_ENVELOPE; + if (operator->envelope <= operator->total_level) { + operator->envelope = operator->total_level; + operator->env_phase = PHASE_DECAY; + } + } else { + operator->envelope += envelope_inc; + //clamp to max attenuation value + if (operator->envelope > MAX_ENVELOPE) { + operator->envelope = MAX_ENVELOPE; + } + if (operator->env_phase == PHASE_DECAY && operator->envelope >= operator->sustain_level) { + operator->envelope = operator->sustain_level; + operator->env_phase = PHASE_SUSTAIN; + } + } + + + } + } + + //Update Phase Generator + uint32_t channel = update_cyc / 24; + if (channel != 5 || !context->dac_enable) { + uint32_t op = (update_cyc) / 6; + //printf("updating operator %d of channel %d\n", op, channel); + ym_operator * operator = context->operators + op; + ym_channel * chan = context->channels + channel; + //TODO: Modulate phase by LFO if necessary + operator->phase_counter += operator->phase_inc; + uint16_t phase = operator->phase_counter >> 10 & 0x3FF; + switch (op % 4) { - case PHASE_ATTACK: - rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_ATTACK_KS + op] : context->part2_regs[REG_ATTACK_KS + op - 3]) & 0x1F; + case 0://Operator 1 + //TODO: Feedback break; - case PHASE_DECAY: - rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_DECAY_AM + op] : context->part2_regs[REG_DECAY_AM + op - 3]) & 0x1F; + case 1://Operator 3 + switch(chan->algorithm) + { + case 0: + case 2: + //modulate by operator 2 + phase += context->operators[op+1].output >> 4; + break; + case 1: + //modulate by operator 1+2 + phase += (context->operators[op-1].output + context->operators[op+1].output) >> 4; + break; + case 5: + //modulate by operator 1 + phase += context->operators[op-1].output >> 4; + } + break; + case 2://Operator 2 + if (chan->algorithm != 1 && chan->algorithm != 2 || chan->algorithm != 7) { + //modulate by Operator 1 + phase += context->operators[op-2].output >> 4; + } break; - case PHASE_SUSTAIN: - rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_SUSTAIN_RATE + op] : context->part2_regs[REG_SUSTAIN_RATE + op - 3]) & 0x1F; - break; - case PHASE_RELEASE: - rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_DECAY_AM + op] : context->part2_regs[REG_DECAY_AM + op - 3]) << 1 & 0x1E | 1; + case 3://Operator 4 + switch(chan->algorithm) + { + case 0: + case 1: + case 4: + //modulate by operator 3 + phase += context->operators[op-2].output >> 4; + break; + case 2: + //modulate by operator 1+3 + phase += (context->operators[op-3].output + context->operators[op-2].output) >> 4; + break; + case 3: + //modulate by operator 2+3 + phase += (context->operators[op-1].output + context->operators[op-2].output) >> 4; + break; + case 5: + //modulate by operator 1 + phase += context->operators[op-3].output >> 4; + break; + } break; } - if (rate) { - //apply key scaling - uint8_t shift = (op < 3 ? - context->part1_regs[REG_SHARED + REG_ATTACK_KS + op] : - context->part2_regs[REG_ATTACK_KS + op - 3] - ) >> 6; - uint8_t ks = context->keycode[op] >> (3 - shift); - rate = rate*2 + ks; - if (rate > 63) { - rate = 63; - } - } - } - //Update Phase Generator - uint32_t channel = update_cyc / 24; - if (channel != 6 || !(context->part1_regs[REG_DAC_ENABLE] & 0x80)) { - uint32_t op = (update_cyc) / 6; - uint8_t alg; - if (op < 3) { - alg = context->part1_regs[REG_SHARED + REG_ALG_FEEDBACK + op] & 0x7; - } else { - alg = context->part2_regs[REG_ALG_FEEDBACK + op-3] & 0x7; - } - context->phase_counter[op] += context->phase_inc[op]; - uint16_t phase = context->phase_counter[op] >> 10 & 0x3FF; - //TODO: Modulate phase if necessary - uint16_t output = pow_table[sine_table[phase & 0x1FF] + context->envelope[op]]; + //printf("sine_table[%X] + %X = %X, sizeof(pow_table)/sizeof(*pow_table) = %X\n", phase & 0x1FF, operator->envelope, sine_table[phase & 0x1FF] + operator->envelope, sizeof(pow_table)/ sizeof(*pow_table)); + uint16_t output = pow_table[sine_table[phase & 0x1FF] + operator->envelope]; if (phase & 0x200) { output = -output; } - context->op_out[op] = output; + operator->output = output; //Update the channel output if we've updated all operators if (op % 4 == 3) { - if (alg < 4) { - context->channel_out[channel] = context->op_out[channel * 4 + 3]; - } else if(alg == 4) { - context->channel_out[channel] = context->op_out[channel * 4 + 3] + context->op_out[channel * 4 + 1]; + if (chan->algorithm < 4) { + chan->output = operator->output; + } else if(chan->algorithm == 4) { + chan->output = operator->output + context->operators[channel * 4 + 1].output; } else { output = 0; - for (uint32_t op = ((alg == 7) ? 0 : 1) + channel*4; op < (channel+1)*4; op++) { - output += context->op_out[op]; + for (uint32_t op = ((chan->algorithm == 7) ? 0 : 1) + channel*4; op < (channel+1)*4; op++) { + output += context->operators[op].output; } - context->channel_out[channel] = output; + chan->output = output; } } + //puts("operator update done"); } - } if (context->current_cycle >= context->write_cycle + BUSY_CYCLES) { context->status &= 0x7F; } + //printf("Done running YM2612 at cycle %d\n", context->current_cycle, to_cycle); } void ym_address_write_part1(ym2612_context * context, uint8_t address) { - if (address >= 0x21 && address < 0xB7) { - context->selected_reg = context->part1_regs + address - 0x21; - } else { - context->selected_reg = NULL; - } + context->selected_reg = address; + context->selected_part = 0; } void ym_address_write_part2(ym2612_context * context, uint8_t address) { - if (address >= 0x30 && address < 0xB7) { - context->selected_reg = context->part1_regs + address - 0x30; + context->selected_reg = address; + context->selected_part = 1; +} + +uint8_t fnum_to_keycode[] = { + //F11 = 0 + 0,0,0,0,0,0,0,1, + //F11 = 1 + 2,3,3,3,3,3,3,3 +}; + +//table courtesy of Nemesis +uint32_t detune_table[][4] = { + {0, 0, 1, 2}, //0 (0x00) + {0, 0, 1, 2}, //1 (0x01) + {0, 0, 1, 2}, //2 (0x02) + {0, 0, 1, 2}, //3 (0x03) + {0, 1, 2, 2}, //4 (0x04) + {0, 1, 2, 3}, //5 (0x05) + {0, 1, 2, 3}, //6 (0x06) + {0, 1, 2, 3}, //7 (0x07) + {0, 1, 2, 4}, //8 (0x08) + {0, 1, 3, 4}, //9 (0x09) + {0, 1, 3, 4}, //10 (0x0A) + {0, 1, 3, 5}, //11 (0x0B) + {0, 2, 4, 5}, //12 (0x0C) + {0, 2, 4, 6}, //13 (0x0D) + {0, 2, 4, 6}, //14 (0x0E) + {0, 2, 5, 7}, //15 (0x0F) + {0, 2, 5, 8}, //16 (0x10) + {0, 3, 6, 8}, //17 (0x11) + {0, 3, 6, 9}, //18 (0x12) + {0, 3, 7,10}, //19 (0x13) + {0, 4, 8,11}, //20 (0x14) + {0, 4, 8,12}, //21 (0x15) + {0, 4, 9,13}, //22 (0x16) + {0, 5,10,14}, //23 (0x17) + {0, 5,11,16}, //24 (0x18) + {0, 6,12,17}, //25 (0x19) + {0, 6,13,19}, //26 (0x1A) + {0, 7,14,20}, //27 (0x1B) + {0, 8,16,22}, //28 (0x1C) + {0, 8,16,22}, //29 (0x1D) + {0, 8,16,22}, //30 (0x1E) + {0, 8,16,22} +}; //31 (0x1F) + +void ym_update_phase_inc(ym2612_context * context, ym_operator * operator, uint32_t op) +{ + uint32_t chan_num = op / 4; + //printf("ym_update_phase_inc | channel: %d, op: %d\n", chan_num, op); + //base frequency + ym_channel * channel = context->channels + chan_num; + uint32_t inc = channel->fnum; + if (!channel->block) { + inc >> 1; } else { - context->selected_reg = NULL; + inc << (channel->block-1); + } + //detune + uint32_t detune = detune_table[channel->keycode][operator->detune & 0x3]; + if (operator->detune & 0x40) { + inc -= detune; + //this can underflow, mask to 17-bit result + inc &= 0x1FFFF; + } else { + inc += detune; + } + //multiple + if (operator->multiple) { + inc *= operator->multiple; + } else { + //0.5 + inc >>= 1; } } void ym_data_write(ym2612_context * context, uint8_t value) { - if (context->selected_reg && !(context->status & 0x80)) { - *context->selected_reg = value; - context->write_cycle = context->current_cycle; - context->selected_reg = NULL;//TODO: Verify this + if (context->selected_reg < 0x21 || context->selected_reg > 0xB6 || (context->selected_reg < 0x30 && context->selected_part)) { + return; } + //printf("write to reg %X in part %d\n", context->selected_reg, context->selected_part+1); + if (context->selected_reg < 0x30) { + //Shared regs + switch (context->selected_reg) + { + //TODO: Test reg and LFO + case REG_TIMERA_HIGH: + context->timer_a_load &= 0x3; + context->timer_a_load |= value << 2; + break; + case REG_TIMERA_LOW: + context->timer_a_load &= 0xFFFC; + context->timer_a_load |= value & 0x3; + break; + case REG_TIMERB: + context->timer_b_load = value; + break; + case REG_TIME_CTRL: + context->timer_control = value; + break; + case REG_KEY_ONOFF: { + uint8_t channel = value & 0x7; + if (channel < NUM_CHANNELS) { + for (uint8_t op = channel * 4, bit = 0x10; op < (channel + 1) * 4; op++, bit <<= 1) { + if (value & bit) { + //printf("Key On for operator %d in channel %d\n", op, channel); + context->operators[op].phase_counter = 0; + context->operators[op].env_phase = PHASE_ATTACK; + context->operators[op].envelope = MAX_ENVELOPE; + } else { + //printf("Key Off for operator %d in channel %d\n", op, channel); + context->operators[op].env_phase = PHASE_RELEASE; + } + } + } + break; + } + case REG_DAC: + if (context->dac_enable) { + context->channels[5].output = (((int16_t)value) - 0x80) << 6; + } + break; + case REG_DAC_ENABLE: + context->dac_enable = value & 0x80; + break; + } + } else if (context->selected_reg < 0xA0) { + //part + uint8_t op = context->selected_part ? (NUM_OPERATORS/2) : 0; + //channel in part + if ((context->selected_reg & 0x3) != 0x3) { + op += 4 * (context->selected_reg & 0x3); + //operator in channel + switch (context->selected_reg & 0xC) + { + case 0: + break; + case 4: + op += 2; + break; + case 8: + op += 1; + break; + case 0xC: + op += 3; + break; + } + //printf("write targets operator %d (%d of channel %d)\n", op, op % 4, op / 4); + ym_operator * operator = context->operators + op; + switch (context->selected_reg & 0xF0) + { + case REG_DETUNE_MULT: + operator->detune = value >> 4 & 0x7; + operator->multiple = value & 0xF; + ym_update_phase_inc(context, operator, op); + break; + case REG_TOTAL_LEVEL: + operator->total_level = (value & 0x7F) << 5; + break; + case REG_ATTACK_KS: + operator->key_scaling = value >> 6; + operator->rates[PHASE_ATTACK] = value & 0x1F; + break; + case REG_DECAY_AM: + //TODO: AM flag for LFO + operator->rates[PHASE_DECAY] = value & 0x1F; + break; + case REG_SUSTAIN_RATE: + operator->rates[PHASE_SUSTAIN] = value & 0x1F; + break; + case REG_S_LVL_R_RATE: + operator->rates[PHASE_RELEASE] = (value & 0xF) << 1 | 1; + operator->sustain_level = value & 0xF0 << 4; + break; + } + } + } else { + uint8_t channel = context->selected_reg & 0x3; + if (channel != 3) { + if (context->selected_part) { + channel += 3; + } + //printf("write targets channel %d\n", channel); + switch (context->selected_reg & 0xFC) + { + case REG_FNUM_LOW: + context->channels[channel].block = context->channels[channel].block_fnum_latch >> 3 & 0x7; + context->channels[channel].fnum = (context->channels[channel].block_fnum_latch & 0x7) << 8 | value; + context->channels[channel].keycode = context->channels[channel].block << 2 | fnum_to_keycode[context->channels[channel].fnum >> 7]; + ym_update_phase_inc(context, context->operators + channel*4, channel*4); + ym_update_phase_inc(context, context->operators + channel*4+1, channel*4+1); + ym_update_phase_inc(context, context->operators + channel*4+2, channel*4+2); + ym_update_phase_inc(context, context->operators + channel*4+3, channel*4+3); + break; + case REG_BLOCK_FNUM_H:{ + context->channels[channel].block_fnum_latch = value; + break; + } + //TODO: Channel 3 special/CSM modes + case REG_ALG_FEEDBACK: + context->channels[channel].algorithm = value & 0x7; + context->channels[channel].feedback = value >> 3 & 0x7; + break; + case REG_LR_AMS_PMS: + context->channels[channel].pms = value & 0x7; + context->channels[channel].ams = value >> 4 & 0x3; + context->channels[channel].lr = value & 0xC0; + break; + } + } + } + + context->write_cycle = context->current_cycle; + context->selected_reg = 0;//TODO: Verify this } uint8_t ym_read_status(ym2612_context * context) diff -r 946ae3749260 -r b7c3facee762 ym2612.h --- a/ym2612.h Mon May 27 20:56:02 2013 -0700 +++ b/ym2612.h Mon May 27 22:19:11 2013 -0700 @@ -3,27 +3,51 @@ #include -#define NUM_SHARED_REGS (0x30-0x21) #define NUM_PART_REGS (0xB7-0x30) -#define NUM_OPERATORS (4*6) +#define NUM_CHANNELS 6 +#define NUM_OPERATORS (4*NUM_CHANNELS) + +typedef struct { + uint32_t phase_inc; + uint32_t phase_counter; + uint16_t envelope; + uint16_t output; + uint16_t total_level; + uint16_t sustain_level; + uint8_t rates[4]; + uint8_t key_scaling; + uint8_t multiple; + uint8_t detune; + uint8_t env_phase; +} ym_operator; typedef struct { - uint32_t current_cycle; - uint32_t write_cycle; - uint8_t *selected_reg; - uint32_t phase_inc[NUM_OPERATORS]; - uint32_t phase_counter[NUM_OPERATORS]; - uint16_t envelope[NUM_OPERATORS]; - uint16_t op_out[NUM_OPERATORS]; - uint16_t channel_out[6]; - uint16_t timer_a; - uint8_t env_phase[NUM_OPERATORS]; - uint8_t keycode[NUM_OPERATORS]; - uint8_t timer_b; - uint8_t reg_num; - uint8_t status; - uint8_t part1_regs[NUM_SHARED_REGS+NUM_PART_REGS]; - uint8_t part2_regs[NUM_PART_REGS]; + uint16_t fnum; + int16_t output; + uint8_t block_fnum_latch; + uint8_t block; + uint8_t keycode; + uint8_t algorithm; + uint8_t feedback; + uint8_t ams; + uint8_t pms; + uint8_t lr; +} ym_channel; + +typedef struct { + uint32_t current_cycle; + uint32_t write_cycle; + ym_operator operators[NUM_OPERATORS]; + ym_channel channels[NUM_CHANNELS]; + uint16_t timer_a; + uint16_t timer_a_load; + uint8_t timer_b; + uint8_t timer_b_load; + uint8_t timer_control; + uint8_t dac_enable; + uint8_t status; + uint8_t selected_reg; + uint8_t selected_part; } ym2612_context; void ym_init(ym2612_context * context);