# HG changeset patch # User Mike Pavone # Date 1369673698 25200 # Node ID cc39629e8d068dac69a19121995c6dd8ee20b04a # Parent 9498cfa7f7c83850bf6b55b171e32098b471e03c YM2612 WIP snapshot before register refactor diff -r 9498cfa7f7c8 -r cc39629e8d06 Makefile --- a/Makefile Fri May 24 00:41:54 2013 -0700 +++ b/Makefile Mon May 27 09:54:58 2013 -0700 @@ -8,7 +8,7 @@ all : dis trans stateview blastem blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o - $(CC) -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o `pkg-config --libs $(LIBS)` + $(CC) -ggdb -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o psg.o render_sdl.o `pkg-config --libs $(LIBS)` dis : dis.o 68kinst.o $(CC) -o dis dis.o 68kinst.o diff -r 9498cfa7f7c8 -r cc39629e8d06 ym2612.c --- a/ym2612.c Fri May 24 00:41:54 2013 -0700 +++ b/ym2612.c Mon May 27 09:54:58 2013 -0700 @@ -1,13 +1,27 @@ #include +#include +#include #include "ym2612.h" #define BUSY_CYCLES 17 #define TIMERA_UPDATE_PERIOD 144 -#define REG_TIMERA_HIGH 0x3 // 0x24 -#define REG_TIMERA_LOW 0x4 // 0x25 -#define REG_TIMERB 0x5 // 0x26 -#define REG_TIME_CTRL 0x6 // 0x27 +#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 + +//offset to add to "shared" regs when looking for them in Part I +#define REG_SHARED 0x10 + + +#define REG_ALG_FEEDBACK (0xB0-0x30) +#define REG_ATTACK_KS (0x50-0x30) +#define REG_DECAY_AM (0x60-0x30) +#define REG_SUSTAIN_RATE (0x70-0x30) + #define BIT_TIMERA_ENABLE 0x1 #define BIT_TIMERB_ENABLE 0x2 @@ -19,45 +33,155 @@ #define BIT_STATUS_TIMERA 0x1 #define BIT_STATUS_TIMERB 0x2 +enum { + PHASE_ATTACK, + PHASE_DECAY, + PHASE_SUSTAIN, + PHASE_RELEASE +}; + +uint8_t did_tbl_init = 0; +//According to Nemesis, real hardware only uses a 256 entry quarter sine table; however, +//memory is cheap so using a half sine table will probably save some cycles +//a full sine table would be nice, but negative numbers don't get along with log2 +#define SINE_TABLE_SIZE 512 +uint16_t sine_table[SINE_TABLE_SIZE]; +//Similar deal here with the power table for log -> linear conversion +//According to Nemesis, real hardware only uses a 256 entry table for the fractional part +//and uses the whole part as a shift amount. +#define POW_TABLE_SIZE (1 << 13) +uint16_t pow_table[POW_TABLE_SIZE]; + + +uint16_t round_fixed_point(double value, int dec_bits) +{ + return value * (1 << dec_bits) + 0.5; +} + void ym_init(ym2612_context * context) { memset(context, 0, sizeof(*context)); + if (!did_tbl_init) { + //populate sine table + for (int32_t i = 0; i < 512; i++) { + double sine = sin( ((double)(i*2+1) / SINE_TABLE_SIZE) * M_PI_2 ); + + //table stores 4.8 fixed pointed representation of the base 2 log + sine_table[i] = round_fixed_point(-log2(sine), 8); + } + //populate power table + for (int32_t i = 0; i < POW_TABLE_SIZE; i++) { + double linear = pow(2, -((double)((i & 0xFF)+1) / 256.0)); + int32_t tmp = round_fixed_point(linear, 11); + int32_t shift = (i >> 8) - 2; + if (shift < 0) { + tmp <<= 0-shift; + } else { + tmp >>= shift; + } + pow_table[i] = tmp; + } + } } void ym_run(ym2612_context * context, uint32_t to_cycle) { - uint32_t delta = to_cycle - context->current_cycle; - //Timers won't be perfect with this, but it's good enough for now - //once actual FM emulation is in place the timers should just be - //decremented/reloaded on the appropriate ticks - uint32_t timer_delta = to_cycle / TIMERA_UPDATE_PERIOD; - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) { - if (timer_delta > context->timer_a) { - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) { - context->status |= BIT_STATUS_TIMERA; + 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 (context->timer_a) { + context->timer_a--; + } else { + if (context->part1_regs[REG_TIME_CTRL] & 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); } - uint32_t rem_delta = timer_delta - (context->timer_a+1); - uint16_t timer_val = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3); - context->timer_a = timer_val - (rem_delta % (timer_val + 1)); - } else { - context->timer_a -= timer_delta; + if (context->part1_regs[REG_TIME_CTRL] & 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) { + context->status |= BIT_STATUS_TIMERB; + } + context->timer_b = context->part1_regs[REG_TIMERB]; + } + } + } } - } - timer_delta /= 16; //Timer B runs at 1/16th the speed of Timer A - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) { - if (timer_delta > context->timer_b) { - if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) { - context->status |= BIT_STATUS_TIMERB; + //Update Envelope Generator + if (update_cyc == 0 || update_cyc == 72) { + uint32_t env_cyc = context->current_cycle / 72; + uint32_t op = env_cyc % 24; + env_cyc /= 24; + uint8_t rate; + switch(context->env_phase[op]) + { + case PHASE_ATTACK: + rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_ATTACK_KS + op] : context->part2_regs[REG_ATTACK_KS + op - 3]) & 0x1F; + 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; + 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; + 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; + } } - uint32_t rem_delta = timer_delta - (context->timer_b+1); - uint8_t timer_val = context->part1_regs[REG_TIMERB]; - context->timer_b = timer_val - (rem_delta % (timer_val + 1)); - } else { - context->timer_a -= timer_delta; } + //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]]; + if (phase & 0x200) { + output = -output; + } + context->op_out[op] = 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]; + } else { + output = 0; + for (uint32_t op = ((alg == 7) ? 0 : 1) + channel*4; op < (channel+1)*4; op++) { + output += context->op_out[op]; + } + context->channel_out[channel] = output; + } + } + } + } - context->current_cycle = to_cycle; - if (to_cycle >= context->write_cycle + BUSY_CYCLES) { + if (context->current_cycle >= context->write_cycle + BUSY_CYCLES) { context->status &= 0x7F; } } diff -r 9498cfa7f7c8 -r cc39629e8d06 ym2612.h --- a/ym2612.h Fri May 24 00:41:54 2013 -0700 +++ b/ym2612.h Mon May 27 09:54:58 2013 -0700 @@ -5,12 +5,20 @@ #define NUM_SHARED_REGS (0x30-0x21) #define NUM_PART_REGS (0xB7-0x30) +#define NUM_OPERATORS (4*6) 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;