Mercurial > repos > blastem
comparison ym2612.c @ 359:cc39629e8d06
YM2612 WIP snapshot before register refactor
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 27 May 2013 09:54:58 -0700 |
parents | a8ee7934a1f8 |
children | b7c3facee762 |
comparison
equal
deleted
inserted
replaced
358:9498cfa7f7c8 | 359:cc39629e8d06 |
---|---|
1 #include <string.h> | 1 #include <string.h> |
2 #include <math.h> | |
3 #include <stdio.h> | |
2 #include "ym2612.h" | 4 #include "ym2612.h" |
3 | 5 |
4 #define BUSY_CYCLES 17 | 6 #define BUSY_CYCLES 17 |
5 #define TIMERA_UPDATE_PERIOD 144 | 7 #define TIMERA_UPDATE_PERIOD 144 |
6 | 8 |
7 #define REG_TIMERA_HIGH 0x3 // 0x24 | 9 #define REG_TIMERA_HIGH 0x03 // 0x24 |
8 #define REG_TIMERA_LOW 0x4 // 0x25 | 10 #define REG_TIMERA_LOW 0x04 // 0x25 |
9 #define REG_TIMERB 0x5 // 0x26 | 11 #define REG_TIMERB 0x05 // 0x26 |
10 #define REG_TIME_CTRL 0x6 // 0x27 | 12 #define REG_TIME_CTRL 0x06 // 0x27 |
13 #define REG_DAC 0x0A // 0x2A | |
14 #define REG_DAC_ENABLE 0x0B // 0x2B | |
15 | |
16 //offset to add to "shared" regs when looking for them in Part I | |
17 #define REG_SHARED 0x10 | |
18 | |
19 | |
20 #define REG_ALG_FEEDBACK (0xB0-0x30) | |
21 #define REG_ATTACK_KS (0x50-0x30) | |
22 #define REG_DECAY_AM (0x60-0x30) | |
23 #define REG_SUSTAIN_RATE (0x70-0x30) | |
24 | |
11 | 25 |
12 #define BIT_TIMERA_ENABLE 0x1 | 26 #define BIT_TIMERA_ENABLE 0x1 |
13 #define BIT_TIMERB_ENABLE 0x2 | 27 #define BIT_TIMERB_ENABLE 0x2 |
14 #define BIT_TIMERA_OVEREN 0x4 | 28 #define BIT_TIMERA_OVEREN 0x4 |
15 #define BIT_TIMERB_OVEREN 0x8 | 29 #define BIT_TIMERB_OVEREN 0x8 |
17 #define BIT_TIMERB_RESET 0x20 | 31 #define BIT_TIMERB_RESET 0x20 |
18 | 32 |
19 #define BIT_STATUS_TIMERA 0x1 | 33 #define BIT_STATUS_TIMERA 0x1 |
20 #define BIT_STATUS_TIMERB 0x2 | 34 #define BIT_STATUS_TIMERB 0x2 |
21 | 35 |
36 enum { | |
37 PHASE_ATTACK, | |
38 PHASE_DECAY, | |
39 PHASE_SUSTAIN, | |
40 PHASE_RELEASE | |
41 }; | |
42 | |
43 uint8_t did_tbl_init = 0; | |
44 //According to Nemesis, real hardware only uses a 256 entry quarter sine table; however, | |
45 //memory is cheap so using a half sine table will probably save some cycles | |
46 //a full sine table would be nice, but negative numbers don't get along with log2 | |
47 #define SINE_TABLE_SIZE 512 | |
48 uint16_t sine_table[SINE_TABLE_SIZE]; | |
49 //Similar deal here with the power table for log -> linear conversion | |
50 //According to Nemesis, real hardware only uses a 256 entry table for the fractional part | |
51 //and uses the whole part as a shift amount. | |
52 #define POW_TABLE_SIZE (1 << 13) | |
53 uint16_t pow_table[POW_TABLE_SIZE]; | |
54 | |
55 | |
56 uint16_t round_fixed_point(double value, int dec_bits) | |
57 { | |
58 return value * (1 << dec_bits) + 0.5; | |
59 } | |
60 | |
22 void ym_init(ym2612_context * context) | 61 void ym_init(ym2612_context * context) |
23 { | 62 { |
24 memset(context, 0, sizeof(*context)); | 63 memset(context, 0, sizeof(*context)); |
64 if (!did_tbl_init) { | |
65 //populate sine table | |
66 for (int32_t i = 0; i < 512; i++) { | |
67 double sine = sin( ((double)(i*2+1) / SINE_TABLE_SIZE) * M_PI_2 ); | |
68 | |
69 //table stores 4.8 fixed pointed representation of the base 2 log | |
70 sine_table[i] = round_fixed_point(-log2(sine), 8); | |
71 } | |
72 //populate power table | |
73 for (int32_t i = 0; i < POW_TABLE_SIZE; i++) { | |
74 double linear = pow(2, -((double)((i & 0xFF)+1) / 256.0)); | |
75 int32_t tmp = round_fixed_point(linear, 11); | |
76 int32_t shift = (i >> 8) - 2; | |
77 if (shift < 0) { | |
78 tmp <<= 0-shift; | |
79 } else { | |
80 tmp >>= shift; | |
81 } | |
82 pow_table[i] = tmp; | |
83 } | |
84 } | |
25 } | 85 } |
26 | 86 |
27 void ym_run(ym2612_context * context, uint32_t to_cycle) | 87 void ym_run(ym2612_context * context, uint32_t to_cycle) |
28 { | 88 { |
29 uint32_t delta = to_cycle - context->current_cycle; | 89 for (; context->current_cycle < to_cycle; context->current_cycle += 6) { |
30 //Timers won't be perfect with this, but it's good enough for now | 90 uint32_t update_cyc = context->current_cycle % 144; |
31 //once actual FM emulation is in place the timers should just be | 91 //Update timers at beginning of 144 cycle period |
32 //decremented/reloaded on the appropriate ticks | 92 if (!update_cyc && context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) { |
33 uint32_t timer_delta = to_cycle / TIMERA_UPDATE_PERIOD; | 93 if (context->timer_a) { |
34 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) { | 94 context->timer_a--; |
35 if (timer_delta > context->timer_a) { | 95 } else { |
36 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) { | 96 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) { |
37 context->status |= BIT_STATUS_TIMERA; | 97 context->status |= BIT_STATUS_TIMERA; |
38 } | 98 } |
39 uint32_t rem_delta = timer_delta - (context->timer_a+1); | 99 context->timer_a = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3); |
40 uint16_t timer_val = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3); | 100 } |
41 context->timer_a = timer_val - (rem_delta % (timer_val + 1)); | 101 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) { |
42 } else { | 102 uint32_t b_cyc = (context->current_cycle / 144) % 16; |
43 context->timer_a -= timer_delta; | 103 if (!b_cyc) { |
44 } | 104 if (context->timer_b) { |
45 } | 105 context->timer_b--; |
46 timer_delta /= 16; //Timer B runs at 1/16th the speed of Timer A | 106 } else { |
47 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) { | 107 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) { |
48 if (timer_delta > context->timer_b) { | 108 context->status |= BIT_STATUS_TIMERB; |
49 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) { | 109 } |
50 context->status |= BIT_STATUS_TIMERB; | 110 context->timer_b = context->part1_regs[REG_TIMERB]; |
51 } | 111 } |
52 uint32_t rem_delta = timer_delta - (context->timer_b+1); | 112 } |
53 uint8_t timer_val = context->part1_regs[REG_TIMERB]; | 113 } |
54 context->timer_b = timer_val - (rem_delta % (timer_val + 1)); | 114 } |
55 } else { | 115 //Update Envelope Generator |
56 context->timer_a -= timer_delta; | 116 if (update_cyc == 0 || update_cyc == 72) { |
57 } | 117 uint32_t env_cyc = context->current_cycle / 72; |
58 } | 118 uint32_t op = env_cyc % 24; |
59 context->current_cycle = to_cycle; | 119 env_cyc /= 24; |
60 if (to_cycle >= context->write_cycle + BUSY_CYCLES) { | 120 uint8_t rate; |
121 switch(context->env_phase[op]) | |
122 { | |
123 case PHASE_ATTACK: | |
124 rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_ATTACK_KS + op] : context->part2_regs[REG_ATTACK_KS + op - 3]) & 0x1F; | |
125 break; | |
126 case PHASE_DECAY: | |
127 rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_DECAY_AM + op] : context->part2_regs[REG_DECAY_AM + op - 3]) & 0x1F; | |
128 break; | |
129 case PHASE_SUSTAIN: | |
130 rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_SUSTAIN_RATE + op] : context->part2_regs[REG_SUSTAIN_RATE + op - 3]) & 0x1F; | |
131 break; | |
132 case PHASE_RELEASE: | |
133 rate = (op < 3 ? context->part1_regs[REG_SHARED + REG_DECAY_AM + op] : context->part2_regs[REG_DECAY_AM + op - 3]) << 1 & 0x1E | 1; | |
134 break; | |
135 } | |
136 if (rate) { | |
137 //apply key scaling | |
138 uint8_t shift = (op < 3 ? | |
139 context->part1_regs[REG_SHARED + REG_ATTACK_KS + op] : | |
140 context->part2_regs[REG_ATTACK_KS + op - 3] | |
141 ) >> 6; | |
142 uint8_t ks = context->keycode[op] >> (3 - shift); | |
143 rate = rate*2 + ks; | |
144 if (rate > 63) { | |
145 rate = 63; | |
146 } | |
147 } | |
148 } | |
149 //Update Phase Generator | |
150 uint32_t channel = update_cyc / 24; | |
151 if (channel != 6 || !(context->part1_regs[REG_DAC_ENABLE] & 0x80)) { | |
152 uint32_t op = (update_cyc) / 6; | |
153 uint8_t alg; | |
154 if (op < 3) { | |
155 alg = context->part1_regs[REG_SHARED + REG_ALG_FEEDBACK + op] & 0x7; | |
156 } else { | |
157 alg = context->part2_regs[REG_ALG_FEEDBACK + op-3] & 0x7; | |
158 } | |
159 context->phase_counter[op] += context->phase_inc[op]; | |
160 uint16_t phase = context->phase_counter[op] >> 10 & 0x3FF; | |
161 //TODO: Modulate phase if necessary | |
162 uint16_t output = pow_table[sine_table[phase & 0x1FF] + context->envelope[op]]; | |
163 if (phase & 0x200) { | |
164 output = -output; | |
165 } | |
166 context->op_out[op] = output; | |
167 //Update the channel output if we've updated all operators | |
168 if (op % 4 == 3) { | |
169 if (alg < 4) { | |
170 context->channel_out[channel] = context->op_out[channel * 4 + 3]; | |
171 } else if(alg == 4) { | |
172 context->channel_out[channel] = context->op_out[channel * 4 + 3] + context->op_out[channel * 4 + 1]; | |
173 } else { | |
174 output = 0; | |
175 for (uint32_t op = ((alg == 7) ? 0 : 1) + channel*4; op < (channel+1)*4; op++) { | |
176 output += context->op_out[op]; | |
177 } | |
178 context->channel_out[channel] = output; | |
179 } | |
180 } | |
181 } | |
182 | |
183 } | |
184 if (context->current_cycle >= context->write_cycle + BUSY_CYCLES) { | |
61 context->status &= 0x7F; | 185 context->status &= 0x7F; |
62 } | 186 } |
63 } | 187 } |
64 | 188 |
65 void ym_address_write_part1(ym2612_context * context, uint8_t address) | 189 void ym_address_write_part1(ym2612_context * context, uint8_t address) |