Mercurial > repos > blastem
annotate ym2612.c @ 363:c708dea45f8b
Fix push AF
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Wed, 29 May 2013 00:13:48 -0700 |
parents | b7c3facee762 |
children | 62177cc39049 |
rev | line source |
---|---|
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
1 #include <string.h> |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
2 #include <math.h> |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
3 #include <stdio.h> |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
4 #include "ym2612.h" |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
5 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
6 #define BUSY_CYCLES 17 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
7 #define TIMERA_UPDATE_PERIOD 144 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
8 |
362 | 9 enum { |
10 REG_TIMERA_HIGH = 0x24, | |
11 REG_TIMERA_LOW, | |
12 REG_TIMERB, | |
13 REG_TIME_CTRL, | |
14 REG_KEY_ONOFF, | |
15 REG_DAC = 0x2A, | |
16 REG_DAC_ENABLE, | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
17 |
362 | 18 REG_DETUNE_MULT = 0x30, |
19 REG_TOTAL_LEVEL = 0x40, | |
20 REG_ATTACK_KS = 0x50, | |
21 REG_DECAY_AM = 0x60, | |
22 REG_SUSTAIN_RATE = 0x70, | |
23 REG_S_LVL_R_RATE = 0x80, | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
24 |
362 | 25 REG_FNUM_LOW = 0xA0, |
26 REG_BLOCK_FNUM_H = 0xA4, | |
27 REG_FNUM_LOW_CH3 = 0xA8, | |
28 REG_BLOCK_FN_CH3 = 0xAC, | |
29 REG_ALG_FEEDBACK = 0xB0, | |
30 REG_LR_AMS_PMS = 0xB4 | |
31 }; | |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
32 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
33 #define BIT_TIMERA_ENABLE 0x1 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
34 #define BIT_TIMERB_ENABLE 0x2 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
35 #define BIT_TIMERA_OVEREN 0x4 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
36 #define BIT_TIMERB_OVEREN 0x8 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
37 #define BIT_TIMERA_RESET 0x10 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
38 #define BIT_TIMERB_RESET 0x20 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
39 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
40 #define BIT_STATUS_TIMERA 0x1 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
41 #define BIT_STATUS_TIMERB 0x2 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
42 |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
43 enum { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
44 PHASE_ATTACK, |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
45 PHASE_DECAY, |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
46 PHASE_SUSTAIN, |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
47 PHASE_RELEASE |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
48 }; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
49 |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
50 uint8_t did_tbl_init = 0; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
51 //According to Nemesis, real hardware only uses a 256 entry quarter sine table; however, |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
52 //memory is cheap so using a half sine table will probably save some cycles |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
53 //a full sine table would be nice, but negative numbers don't get along with log2 |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
54 #define SINE_TABLE_SIZE 512 |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
55 uint16_t sine_table[SINE_TABLE_SIZE]; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
56 //Similar deal here with the power table for log -> linear conversion |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
57 //According to Nemesis, real hardware only uses a 256 entry table for the fractional part |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
58 //and uses the whole part as a shift amount. |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
59 #define POW_TABLE_SIZE (1 << 13) |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
60 uint16_t pow_table[POW_TABLE_SIZE]; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
61 |
362 | 62 uint16_t rate_table_base[] = { |
63 //main portion | |
64 0,1,0,1,0,1,0,1, | |
65 0,1,0,1,1,1,0,1, | |
66 0,1,1,1,0,1,1,1, | |
67 0,1,1,1,1,1,1,1, | |
68 //top end | |
69 1,1,1,1,1,1,1,1, | |
70 1,1,1,2,1,1,1,2, | |
71 1,2,1,2,1,2,1,2, | |
72 1,2,2,2,1,2,2,2, | |
73 }; | |
74 | |
75 uint16_t rate_table[64]; | |
76 | |
77 #define MAX_ENVELOPE 0xFFC | |
78 | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
79 |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
80 uint16_t round_fixed_point(double value, int dec_bits) |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
81 { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
82 return value * (1 << dec_bits) + 0.5; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
83 } |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
84 |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
85 void ym_init(ym2612_context * context) |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
86 { |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
87 memset(context, 0, sizeof(*context)); |
362 | 88 for (int i = 0; i < NUM_OPERATORS; i++) { |
89 context->operators[i].envelope = MAX_ENVELOPE; | |
90 context->operators[i].env_phase = PHASE_RELEASE; | |
91 } | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
92 if (!did_tbl_init) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
93 //populate sine table |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
94 for (int32_t i = 0; i < 512; i++) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
95 double sine = sin( ((double)(i*2+1) / SINE_TABLE_SIZE) * M_PI_2 ); |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
96 |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
97 //table stores 4.8 fixed pointed representation of the base 2 log |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
98 sine_table[i] = round_fixed_point(-log2(sine), 8); |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
99 } |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
100 //populate power table |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
101 for (int32_t i = 0; i < POW_TABLE_SIZE; i++) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
102 double linear = pow(2, -((double)((i & 0xFF)+1) / 256.0)); |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
103 int32_t tmp = round_fixed_point(linear, 11); |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
104 int32_t shift = (i >> 8) - 2; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
105 if (shift < 0) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
106 tmp <<= 0-shift; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
107 } else { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
108 tmp >>= shift; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
109 } |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
110 pow_table[i] = tmp; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
111 } |
362 | 112 //populate envelope generator rate table, from small base table |
113 for (int rate = 0; rate < 64; rate++) { | |
114 for (int cycle = 0; cycle < 7; cycle++) { | |
115 uint16_t value; | |
116 if (rate < 3) { | |
117 value = 0; | |
118 } else if (value >= 60) { | |
119 value = 8; | |
120 } else if (value < 8) { | |
121 value = rate_table_base[((rate & 6) == 6 ? 16 : 8) + cycle]; | |
122 } else if (value < 48) { | |
123 value = rate_table_base[(rate & 0x3) * 8 + cycle]; | |
124 } else { | |
125 value = rate_table_base[32 + (rate & 0x3) * 8 + cycle] << (rate >> 2); | |
126 } | |
127 } | |
128 } | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
129 } |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
130 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
131 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
132 void ym_run(ym2612_context * context, uint32_t to_cycle) |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
133 { |
362 | 134 //printf("Running YM2612 from cycle %d to cycle %d\n", context->current_cycle, to_cycle); |
135 //TODO: Fix channel update order OR remap channels in register write | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
136 for (; context->current_cycle < to_cycle; context->current_cycle += 6) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
137 uint32_t update_cyc = context->current_cycle % 144; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
138 //Update timers at beginning of 144 cycle period |
362 | 139 if (!update_cyc && context->timer_control & BIT_TIMERA_ENABLE) { |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
140 if (context->timer_a) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
141 context->timer_a--; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
142 } else { |
362 | 143 if (context->timer_control & BIT_TIMERA_OVEREN) { |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
144 context->status |= BIT_STATUS_TIMERA; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
145 } |
362 | 146 context->timer_a = context->timer_a_load; |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
147 } |
362 | 148 if (context->timer_control & BIT_TIMERB_ENABLE) { |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
149 uint32_t b_cyc = (context->current_cycle / 144) % 16; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
150 if (!b_cyc) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
151 if (context->timer_b) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
152 context->timer_b--; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
153 } else { |
362 | 154 if (context->timer_control & BIT_TIMERB_OVEREN) { |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
155 context->status |= BIT_STATUS_TIMERB; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
156 } |
362 | 157 context->timer_b = context->timer_b_load; |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
158 } |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
159 } |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
160 } |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
161 } |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
162 //Update Envelope Generator |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
163 if (update_cyc == 0 || update_cyc == 72) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
164 uint32_t env_cyc = context->current_cycle / 72; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
165 uint32_t op = env_cyc % 24; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
166 env_cyc /= 24; |
362 | 167 ym_operator * operator = context->operators + op; |
168 ym_channel * channel = context->channels + op/4; | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
169 uint8_t rate; |
362 | 170 for(;;) { |
171 rate = operator->rates[operator->env_phase]; | |
172 if (rate) { | |
173 uint8_t ks = channel->keycode >> operator->key_scaling;; | |
174 rate = rate*2 + ks; | |
175 if (rate > 63) { | |
176 rate = 63; | |
177 } | |
178 } | |
179 //Deal with "infinite" rates | |
180 //It's possible this should be handled in key-on as well | |
181 if (rate == 63 && operator->env_phase < PHASE_SUSTAIN) { | |
182 if (operator->env_phase == PHASE_ATTACK) { | |
183 operator->env_phase = PHASE_DECAY; | |
184 operator->envelope = operator->total_level; | |
185 } else { | |
186 operator->env_phase = PHASE_SUSTAIN; | |
187 operator->envelope = operator->sustain_level; | |
188 } | |
189 } else { | |
190 break; | |
191 } | |
192 } | |
193 uint32_t cycle_shift = rate < 0x30 ? ((0x2F - rate) >> 2) : 0; | |
194 if (!(env_cyc & ((1 << cycle_shift) - 1))) { | |
195 uint32_t update_cycle = env_cyc >> cycle_shift & 0x7; | |
196 //envelope value is 10-bits, but it will be used as a 4.8 value | |
197 uint16_t envelope_inc = rate_table[rate * 8 + update_cycle] << 2; | |
198 if (operator->env_phase == PHASE_ATTACK) { | |
199 //this can probably be optimized to a single shift rather than a multiply + shift | |
200 operator->envelope += (~operator->envelope * envelope_inc) >> 4; | |
201 operator->envelope &= MAX_ENVELOPE; | |
202 if (operator->envelope <= operator->total_level) { | |
203 operator->envelope = operator->total_level; | |
204 operator->env_phase = PHASE_DECAY; | |
205 } | |
206 } else { | |
207 operator->envelope += envelope_inc; | |
208 //clamp to max attenuation value | |
209 if (operator->envelope > MAX_ENVELOPE) { | |
210 operator->envelope = MAX_ENVELOPE; | |
211 } | |
212 if (operator->env_phase == PHASE_DECAY && operator->envelope >= operator->sustain_level) { | |
213 operator->envelope = operator->sustain_level; | |
214 operator->env_phase = PHASE_SUSTAIN; | |
215 } | |
216 } | |
217 | |
218 | |
219 } | |
220 } | |
221 | |
222 //Update Phase Generator | |
223 uint32_t channel = update_cyc / 24; | |
224 if (channel != 5 || !context->dac_enable) { | |
225 uint32_t op = (update_cyc) / 6; | |
226 //printf("updating operator %d of channel %d\n", op, channel); | |
227 ym_operator * operator = context->operators + op; | |
228 ym_channel * chan = context->channels + channel; | |
229 //TODO: Modulate phase by LFO if necessary | |
230 operator->phase_counter += operator->phase_inc; | |
231 uint16_t phase = operator->phase_counter >> 10 & 0x3FF; | |
232 switch (op % 4) | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
233 { |
362 | 234 case 0://Operator 1 |
235 //TODO: Feedback | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
236 break; |
362 | 237 case 1://Operator 3 |
238 switch(chan->algorithm) | |
239 { | |
240 case 0: | |
241 case 2: | |
242 //modulate by operator 2 | |
243 phase += context->operators[op+1].output >> 4; | |
244 break; | |
245 case 1: | |
246 //modulate by operator 1+2 | |
247 phase += (context->operators[op-1].output + context->operators[op+1].output) >> 4; | |
248 break; | |
249 case 5: | |
250 //modulate by operator 1 | |
251 phase += context->operators[op-1].output >> 4; | |
252 } | |
253 break; | |
254 case 2://Operator 2 | |
255 if (chan->algorithm != 1 && chan->algorithm != 2 || chan->algorithm != 7) { | |
256 //modulate by Operator 1 | |
257 phase += context->operators[op-2].output >> 4; | |
258 } | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
259 break; |
362 | 260 case 3://Operator 4 |
261 switch(chan->algorithm) | |
262 { | |
263 case 0: | |
264 case 1: | |
265 case 4: | |
266 //modulate by operator 3 | |
267 phase += context->operators[op-2].output >> 4; | |
268 break; | |
269 case 2: | |
270 //modulate by operator 1+3 | |
271 phase += (context->operators[op-3].output + context->operators[op-2].output) >> 4; | |
272 break; | |
273 case 3: | |
274 //modulate by operator 2+3 | |
275 phase += (context->operators[op-1].output + context->operators[op-2].output) >> 4; | |
276 break; | |
277 case 5: | |
278 //modulate by operator 1 | |
279 phase += context->operators[op-3].output >> 4; | |
280 break; | |
281 } | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
282 break; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
283 } |
362 | 284 //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)); |
285 uint16_t output = pow_table[sine_table[phase & 0x1FF] + operator->envelope]; | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
286 if (phase & 0x200) { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
287 output = -output; |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
288 } |
362 | 289 operator->output = output; |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
290 //Update the channel output if we've updated all operators |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
291 if (op % 4 == 3) { |
362 | 292 if (chan->algorithm < 4) { |
293 chan->output = operator->output; | |
294 } else if(chan->algorithm == 4) { | |
295 chan->output = operator->output + context->operators[channel * 4 + 1].output; | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
296 } else { |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
297 output = 0; |
362 | 298 for (uint32_t op = ((chan->algorithm == 7) ? 0 : 1) + channel*4; op < (channel+1)*4; op++) { |
299 output += context->operators[op].output; | |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
300 } |
362 | 301 chan->output = output; |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
302 } |
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
303 } |
362 | 304 //puts("operator update done"); |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
305 } |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
306 } |
359
cc39629e8d06
YM2612 WIP snapshot before register refactor
Mike Pavone <pavone@retrodev.com>
parents:
288
diff
changeset
|
307 if (context->current_cycle >= context->write_cycle + BUSY_CYCLES) { |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
308 context->status &= 0x7F; |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
309 } |
362 | 310 //printf("Done running YM2612 at cycle %d\n", context->current_cycle, to_cycle); |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
311 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
312 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
313 void ym_address_write_part1(ym2612_context * context, uint8_t address) |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
314 { |
362 | 315 context->selected_reg = address; |
316 context->selected_part = 0; | |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
317 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
318 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
319 void ym_address_write_part2(ym2612_context * context, uint8_t address) |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
320 { |
362 | 321 context->selected_reg = address; |
322 context->selected_part = 1; | |
323 } | |
324 | |
325 uint8_t fnum_to_keycode[] = { | |
326 //F11 = 0 | |
327 0,0,0,0,0,0,0,1, | |
328 //F11 = 1 | |
329 2,3,3,3,3,3,3,3 | |
330 }; | |
331 | |
332 //table courtesy of Nemesis | |
333 uint32_t detune_table[][4] = { | |
334 {0, 0, 1, 2}, //0 (0x00) | |
335 {0, 0, 1, 2}, //1 (0x01) | |
336 {0, 0, 1, 2}, //2 (0x02) | |
337 {0, 0, 1, 2}, //3 (0x03) | |
338 {0, 1, 2, 2}, //4 (0x04) | |
339 {0, 1, 2, 3}, //5 (0x05) | |
340 {0, 1, 2, 3}, //6 (0x06) | |
341 {0, 1, 2, 3}, //7 (0x07) | |
342 {0, 1, 2, 4}, //8 (0x08) | |
343 {0, 1, 3, 4}, //9 (0x09) | |
344 {0, 1, 3, 4}, //10 (0x0A) | |
345 {0, 1, 3, 5}, //11 (0x0B) | |
346 {0, 2, 4, 5}, //12 (0x0C) | |
347 {0, 2, 4, 6}, //13 (0x0D) | |
348 {0, 2, 4, 6}, //14 (0x0E) | |
349 {0, 2, 5, 7}, //15 (0x0F) | |
350 {0, 2, 5, 8}, //16 (0x10) | |
351 {0, 3, 6, 8}, //17 (0x11) | |
352 {0, 3, 6, 9}, //18 (0x12) | |
353 {0, 3, 7,10}, //19 (0x13) | |
354 {0, 4, 8,11}, //20 (0x14) | |
355 {0, 4, 8,12}, //21 (0x15) | |
356 {0, 4, 9,13}, //22 (0x16) | |
357 {0, 5,10,14}, //23 (0x17) | |
358 {0, 5,11,16}, //24 (0x18) | |
359 {0, 6,12,17}, //25 (0x19) | |
360 {0, 6,13,19}, //26 (0x1A) | |
361 {0, 7,14,20}, //27 (0x1B) | |
362 {0, 8,16,22}, //28 (0x1C) | |
363 {0, 8,16,22}, //29 (0x1D) | |
364 {0, 8,16,22}, //30 (0x1E) | |
365 {0, 8,16,22} | |
366 }; //31 (0x1F) | |
367 | |
368 void ym_update_phase_inc(ym2612_context * context, ym_operator * operator, uint32_t op) | |
369 { | |
370 uint32_t chan_num = op / 4; | |
371 //printf("ym_update_phase_inc | channel: %d, op: %d\n", chan_num, op); | |
372 //base frequency | |
373 ym_channel * channel = context->channels + chan_num; | |
374 uint32_t inc = channel->fnum; | |
375 if (!channel->block) { | |
376 inc >> 1; | |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
377 } else { |
362 | 378 inc << (channel->block-1); |
379 } | |
380 //detune | |
381 uint32_t detune = detune_table[channel->keycode][operator->detune & 0x3]; | |
382 if (operator->detune & 0x40) { | |
383 inc -= detune; | |
384 //this can underflow, mask to 17-bit result | |
385 inc &= 0x1FFFF; | |
386 } else { | |
387 inc += detune; | |
388 } | |
389 //multiple | |
390 if (operator->multiple) { | |
391 inc *= operator->multiple; | |
392 } else { | |
393 //0.5 | |
394 inc >>= 1; | |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
395 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
396 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
397 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
398 void ym_data_write(ym2612_context * context, uint8_t value) |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
399 { |
362 | 400 if (context->selected_reg < 0x21 || context->selected_reg > 0xB6 || (context->selected_reg < 0x30 && context->selected_part)) { |
401 return; | |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
402 } |
362 | 403 //printf("write to reg %X in part %d\n", context->selected_reg, context->selected_part+1); |
404 if (context->selected_reg < 0x30) { | |
405 //Shared regs | |
406 switch (context->selected_reg) | |
407 { | |
408 //TODO: Test reg and LFO | |
409 case REG_TIMERA_HIGH: | |
410 context->timer_a_load &= 0x3; | |
411 context->timer_a_load |= value << 2; | |
412 break; | |
413 case REG_TIMERA_LOW: | |
414 context->timer_a_load &= 0xFFFC; | |
415 context->timer_a_load |= value & 0x3; | |
416 break; | |
417 case REG_TIMERB: | |
418 context->timer_b_load = value; | |
419 break; | |
420 case REG_TIME_CTRL: | |
421 context->timer_control = value; | |
422 break; | |
423 case REG_KEY_ONOFF: { | |
424 uint8_t channel = value & 0x7; | |
425 if (channel < NUM_CHANNELS) { | |
426 for (uint8_t op = channel * 4, bit = 0x10; op < (channel + 1) * 4; op++, bit <<= 1) { | |
427 if (value & bit) { | |
428 //printf("Key On for operator %d in channel %d\n", op, channel); | |
429 context->operators[op].phase_counter = 0; | |
430 context->operators[op].env_phase = PHASE_ATTACK; | |
431 context->operators[op].envelope = MAX_ENVELOPE; | |
432 } else { | |
433 //printf("Key Off for operator %d in channel %d\n", op, channel); | |
434 context->operators[op].env_phase = PHASE_RELEASE; | |
435 } | |
436 } | |
437 } | |
438 break; | |
439 } | |
440 case REG_DAC: | |
441 if (context->dac_enable) { | |
442 context->channels[5].output = (((int16_t)value) - 0x80) << 6; | |
443 } | |
444 break; | |
445 case REG_DAC_ENABLE: | |
446 context->dac_enable = value & 0x80; | |
447 break; | |
448 } | |
449 } else if (context->selected_reg < 0xA0) { | |
450 //part | |
451 uint8_t op = context->selected_part ? (NUM_OPERATORS/2) : 0; | |
452 //channel in part | |
453 if ((context->selected_reg & 0x3) != 0x3) { | |
454 op += 4 * (context->selected_reg & 0x3); | |
455 //operator in channel | |
456 switch (context->selected_reg & 0xC) | |
457 { | |
458 case 0: | |
459 break; | |
460 case 4: | |
461 op += 2; | |
462 break; | |
463 case 8: | |
464 op += 1; | |
465 break; | |
466 case 0xC: | |
467 op += 3; | |
468 break; | |
469 } | |
470 //printf("write targets operator %d (%d of channel %d)\n", op, op % 4, op / 4); | |
471 ym_operator * operator = context->operators + op; | |
472 switch (context->selected_reg & 0xF0) | |
473 { | |
474 case REG_DETUNE_MULT: | |
475 operator->detune = value >> 4 & 0x7; | |
476 operator->multiple = value & 0xF; | |
477 ym_update_phase_inc(context, operator, op); | |
478 break; | |
479 case REG_TOTAL_LEVEL: | |
480 operator->total_level = (value & 0x7F) << 5; | |
481 break; | |
482 case REG_ATTACK_KS: | |
483 operator->key_scaling = value >> 6; | |
484 operator->rates[PHASE_ATTACK] = value & 0x1F; | |
485 break; | |
486 case REG_DECAY_AM: | |
487 //TODO: AM flag for LFO | |
488 operator->rates[PHASE_DECAY] = value & 0x1F; | |
489 break; | |
490 case REG_SUSTAIN_RATE: | |
491 operator->rates[PHASE_SUSTAIN] = value & 0x1F; | |
492 break; | |
493 case REG_S_LVL_R_RATE: | |
494 operator->rates[PHASE_RELEASE] = (value & 0xF) << 1 | 1; | |
495 operator->sustain_level = value & 0xF0 << 4; | |
496 break; | |
497 } | |
498 } | |
499 } else { | |
500 uint8_t channel = context->selected_reg & 0x3; | |
501 if (channel != 3) { | |
502 if (context->selected_part) { | |
503 channel += 3; | |
504 } | |
505 //printf("write targets channel %d\n", channel); | |
506 switch (context->selected_reg & 0xFC) | |
507 { | |
508 case REG_FNUM_LOW: | |
509 context->channels[channel].block = context->channels[channel].block_fnum_latch >> 3 & 0x7; | |
510 context->channels[channel].fnum = (context->channels[channel].block_fnum_latch & 0x7) << 8 | value; | |
511 context->channels[channel].keycode = context->channels[channel].block << 2 | fnum_to_keycode[context->channels[channel].fnum >> 7]; | |
512 ym_update_phase_inc(context, context->operators + channel*4, channel*4); | |
513 ym_update_phase_inc(context, context->operators + channel*4+1, channel*4+1); | |
514 ym_update_phase_inc(context, context->operators + channel*4+2, channel*4+2); | |
515 ym_update_phase_inc(context, context->operators + channel*4+3, channel*4+3); | |
516 break; | |
517 case REG_BLOCK_FNUM_H:{ | |
518 context->channels[channel].block_fnum_latch = value; | |
519 break; | |
520 } | |
521 //TODO: Channel 3 special/CSM modes | |
522 case REG_ALG_FEEDBACK: | |
523 context->channels[channel].algorithm = value & 0x7; | |
524 context->channels[channel].feedback = value >> 3 & 0x7; | |
525 break; | |
526 case REG_LR_AMS_PMS: | |
527 context->channels[channel].pms = value & 0x7; | |
528 context->channels[channel].ams = value >> 4 & 0x3; | |
529 context->channels[channel].lr = value & 0xC0; | |
530 break; | |
531 } | |
532 } | |
533 } | |
534 | |
535 context->write_cycle = context->current_cycle; | |
536 context->selected_reg = 0;//TODO: Verify this | |
288
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
537 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
538 |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
539 uint8_t ym_read_status(ym2612_context * context) |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
540 { |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
541 return context->status; |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
542 } |
a8ee7934a1f8
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
Mike Pavone <pavone@retrodev.com>
parents:
diff
changeset
|
543 |