c62d2810 |
1 | /* FCE Ultra - NES/Famicom Emulator |
2 | * |
3 | * Copyright notice for this file: |
d97315ac |
4 | * Copyright (C) 2002 Xodnizel |
c62d2810 |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software |
43725da7 |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | * |
20 | * VRC-6 |
21 | * |
c62d2810 |
22 | */ |
23 | |
24 | #include "mapinc.h" |
25 | |
d97315ac |
26 | static void (*sfun[3])(void); |
27 | |
c62d2810 |
28 | #define vrctemp mapbyte1[0] |
c62d2810 |
29 | #define VPSG2 mapbyte3 |
30 | #define VPSG mapbyte2 |
31 | |
32 | static void DoSQV1(void); |
33 | static void DoSQV2(void); |
34 | static void DoSawV(void); |
35 | |
36 | static int swaparoo; |
c62d2810 |
37 | |
38 | static int acount=0; |
39 | |
43725da7 |
40 | static void FP_FASTAPASS(1) KonamiIRQHook(int a) |
c62d2810 |
41 | { |
d97315ac |
42 | #define LCYCS 341 |
43 | // #define LCYCS ((227*2)+1) |
c62d2810 |
44 | if(IRQa) |
45 | { |
d97315ac |
46 | acount+=a*3; |
c62d2810 |
47 | if(acount>=LCYCS) |
48 | { |
49 | doagainbub:acount-=LCYCS;IRQCount++; |
d97315ac |
50 | if(IRQCount==0x100) |
51 | { |
52 | X6502_IRQBegin(FCEU_IQEXT); |
53 | IRQCount=IRQLatch; |
54 | } |
c62d2810 |
55 | if(acount>=LCYCS) goto doagainbub; |
56 | } |
57 | } |
58 | } |
59 | |
d97315ac |
60 | static DECLFW(VRC6SW) |
c62d2810 |
61 | { |
d97315ac |
62 | A&=0xF003; |
63 | if(A>=0x9000 && A<=0x9002) |
64 | { |
65 | VPSG[A&3]=V; |
66 | if(sfun[0]) sfun[0](); |
67 | } |
68 | else if(A>=0xa000 && A<=0xa002) |
69 | { |
70 | VPSG[4|(A&3)]=V; |
71 | if(sfun[1]) sfun[1](); |
72 | } |
73 | else if(A>=0xb000 && A<=0xb002) |
74 | { |
75 | VPSG2[A&3]=V; |
76 | if(sfun[2]) sfun[2](); |
77 | } |
c62d2810 |
78 | |
d97315ac |
79 | } |
80 | |
81 | static DECLFW(Mapper24_write) |
82 | { |
83 | if(swaparoo) |
84 | A=(A&0xFFFC)|((A>>1)&1)|((A<<1)&2); |
85 | if(A>=0x9000 && A<=0xb002) |
86 | { |
87 | VRC6SW(A,V); |
88 | return; |
89 | } |
90 | A&=0xF003; |
91 | // if(A>=0xF000) printf("%d, %d, $%04x:$%02x\n",scanline,timestamp,A,V); |
c62d2810 |
92 | switch(A&0xF003) |
d97315ac |
93 | { |
94 | case 0x8000:ROM_BANK16(0x8000,V);break; |
c62d2810 |
95 | case 0xB003: |
d97315ac |
96 | switch(V&0xF) |
97 | { |
98 | case 0x0:MIRROR_SET2(1);break; |
99 | case 0x4:MIRROR_SET2(0);break; |
100 | case 0x8:onemir(0);break; |
101 | case 0xC:onemir(1);break; |
102 | } |
103 | break; |
104 | case 0xC000:ROM_BANK8(0xC000,V);break; |
c62d2810 |
105 | case 0xD000:VROM_BANK1(0x0000,V);break; |
106 | case 0xD001:VROM_BANK1(0x0400,V);break; |
107 | case 0xD002:VROM_BANK1(0x0800,V);break; |
108 | case 0xD003:VROM_BANK1(0x0c00,V);break; |
109 | case 0xE000:VROM_BANK1(0x1000,V);break; |
110 | case 0xE001:VROM_BANK1(0x1400,V);break; |
111 | case 0xE002:VROM_BANK1(0x1800,V);break; |
112 | case 0xE003:VROM_BANK1(0x1c00,V);break; |
d97315ac |
113 | case 0xF000:IRQLatch=V; |
114 | //acount=0; |
115 | break; |
c62d2810 |
116 | case 0xF001:IRQa=V&2; |
117 | vrctemp=V&1; |
d97315ac |
118 | if(V&2) |
119 | { |
120 | IRQCount=IRQLatch; |
121 | acount=0; |
122 | } |
123 | X6502_IRQEnd(FCEU_IQEXT); |
c62d2810 |
124 | break; |
d97315ac |
125 | case 0xf002:IRQa=vrctemp; |
126 | X6502_IRQEnd(FCEU_IQEXT);break; |
c62d2810 |
127 | case 0xF003:break; |
128 | } |
129 | } |
130 | |
d97315ac |
131 | static int32 CVBC[3]; |
132 | static int32 vcount[3]; |
133 | static int32 dcount[2]; |
c62d2810 |
134 | |
d97315ac |
135 | static INLINE void DoSQV(int x) |
c62d2810 |
136 | { |
d97315ac |
137 | int32 V; |
138 | int32 amp=(((VPSG[x<<2]&15)<<8)*6/8)>>4; |
139 | int32 start,end; |
140 | |
141 | start=CVBC[x]; |
142 | end=(SOUNDTS<<16)/soundtsinc; |
143 | if(end<=start) return; |
144 | CVBC[x]=end; |
145 | |
146 | if(VPSG[(x<<2)|0x2]&0x80) |
147 | { |
148 | if(VPSG[x<<2]&0x80) |
149 | { |
150 | for(V=start;V<end;V++) |
151 | Wave[V>>4]+=amp; |
152 | } |
153 | else |
154 | { |
155 | int32 thresh=(VPSG[x<<2]>>4)&7; |
156 | int32 freq=((VPSG[(x<<2)|0x1]|((VPSG[(x<<2)|0x2]&15)<<8))+1)<<17; |
157 | for(V=start;V<end;V++) |
158 | { |
159 | if(dcount[x]>thresh) /* Greater than, not >=. Important. */ |
160 | Wave[V>>4]+=amp; |
161 | vcount[x]-=nesincsize; |
162 | while(vcount[x]<=0) /* Should only be <0 in a few circumstances. */ |
c62d2810 |
163 | { |
d97315ac |
164 | vcount[x]+=freq; |
165 | dcount[x]=(dcount[x]+1)&15; |
c62d2810 |
166 | } |
d97315ac |
167 | } |
168 | } |
169 | } |
c62d2810 |
170 | } |
c62d2810 |
171 | |
d97315ac |
172 | static void DoSQV1(void) |
173 | { |
174 | DoSQV(0); |
175 | } |
c62d2810 |
176 | |
d97315ac |
177 | static void DoSQV2(void) |
178 | { |
179 | DoSQV(1); |
c62d2810 |
180 | } |
181 | |
182 | static void DoSawV(void) |
183 | { |
184 | int V; |
185 | int32 start,end; |
186 | |
187 | start=CVBC[2]; |
4fdfab07 |
188 | end=(SOUNDTS<<16)/soundtsinc; |
c62d2810 |
189 | if(end<=start) return; |
190 | CVBC[2]=end; |
191 | |
192 | if(VPSG2[2]&0x80) |
193 | { |
d97315ac |
194 | static int32 saw1phaseacc=0; |
c62d2810 |
195 | uint32 freq3; |
196 | static uint8 b3=0; |
197 | static int32 phaseacc=0; |
198 | static uint32 duff=0; |
199 | |
200 | freq3=(VPSG2[1]+((VPSG2[2]&15)<<8)+1); |
201 | |
202 | for(V=start;V<end;V++) |
203 | { |
d97315ac |
204 | saw1phaseacc-=nesincsize; |
c62d2810 |
205 | if(saw1phaseacc<=0) |
206 | { |
d97315ac |
207 | int32 t; |
c62d2810 |
208 | rea: |
209 | t=freq3; |
d97315ac |
210 | t<<=18; |
c62d2810 |
211 | saw1phaseacc+=t; |
212 | phaseacc+=VPSG2[0]&0x3f; |
213 | b3++; |
214 | if(b3==7) |
215 | { |
216 | b3=0; |
217 | phaseacc=0; |
218 | } |
c0bf6f9f |
219 | if(saw1phaseacc<=0) |
c62d2810 |
220 | goto rea; |
d97315ac |
221 | duff=(((phaseacc>>3)&0x1f)<<4)*6/8; |
c62d2810 |
222 | } |
223 | Wave[V>>4]+=duff; |
224 | } |
225 | } |
226 | } |
227 | |
d97315ac |
228 | static INLINE void DoSQVHQ(int x) |
c62d2810 |
229 | { |
43725da7 |
230 | int32 V; |
d97315ac |
231 | int32 amp=((VPSG[x<<2]&15)<<8)*6/8; |
c62d2810 |
232 | |
d97315ac |
233 | if(VPSG[(x<<2)|0x2]&0x80) |
234 | { |
235 | if(VPSG[x<<2]&0x80) |
236 | { |
237 | for(V=CVBC[x];V<SOUNDTS;V++) |
238 | WaveHi[V]+=amp; |
239 | } |
240 | else |
241 | { |
242 | int32 thresh=(VPSG[x<<2]>>4)&7; |
243 | for(V=CVBC[x];V<SOUNDTS;V++) |
244 | { |
245 | if(dcount[x]>thresh) /* Greater than, not >=. Important. */ |
246 | WaveHi[V]+=amp; |
247 | vcount[x]--; |
248 | if(vcount[x]<=0) /* Should only be <0 in a few circumstances. */ |
249 | { |
250 | vcount[x]=(VPSG[(x<<2)|0x1]|((VPSG[(x<<2)|0x2]&15)<<8))+1; |
251 | dcount[x]=(dcount[x]+1)&15; |
252 | } |
253 | } |
254 | } |
255 | } |
256 | CVBC[x]=SOUNDTS; |
c62d2810 |
257 | } |
258 | |
d97315ac |
259 | static void DoSQV1HQ(void) |
260 | { |
261 | DoSQVHQ(0); |
262 | } |
c62d2810 |
263 | |
d97315ac |
264 | static void DoSQV2HQ(void) |
c62d2810 |
265 | { |
d97315ac |
266 | DoSQVHQ(1); |
267 | } |
c62d2810 |
268 | |
d97315ac |
269 | static void DoSawVHQ(void) |
270 | { |
271 | static uint8 b3=0; |
272 | static int32 phaseacc=0; |
43725da7 |
273 | int32 V; |
d97315ac |
274 | |
275 | if(VPSG2[2]&0x80) |
276 | { |
277 | for(V=CVBC[2];V<SOUNDTS;V++) |
c62d2810 |
278 | { |
d97315ac |
279 | WaveHi[V]+=(((phaseacc>>3)&0x1f)<<8)*6/8; |
280 | vcount[2]--; |
281 | if(vcount[2]<=0) |
c62d2810 |
282 | { |
d97315ac |
283 | vcount[2]=(VPSG2[1]+((VPSG2[2]&15)<<8)+1)<<1; |
284 | phaseacc+=VPSG2[0]&0x3f; |
285 | b3++; |
286 | if(b3==7) |
287 | { |
288 | b3=0; |
289 | phaseacc=0; |
290 | } |
291 | |
c62d2810 |
292 | } |
293 | } |
d97315ac |
294 | } |
295 | CVBC[2]=SOUNDTS; |
c62d2810 |
296 | } |
d97315ac |
297 | |
c62d2810 |
298 | |
d97315ac |
299 | void VRC6Sound(int Count) |
c62d2810 |
300 | { |
d97315ac |
301 | int x; |
c62d2810 |
302 | |
d97315ac |
303 | DoSQV1(); |
304 | DoSQV2(); |
305 | DoSawV(); |
306 | for(x=0;x<3;x++) |
307 | CVBC[x]=Count; |
308 | } |
c62d2810 |
309 | |
d97315ac |
310 | void VRC6SoundHQ(void) |
311 | { |
312 | DoSQV1HQ(); |
313 | DoSQV2HQ(); |
314 | DoSawVHQ(); |
315 | } |
316 | |
317 | void VRC6SyncHQ(int32 ts) |
318 | { |
319 | int x; |
320 | for(x=0;x<3;x++) CVBC[x]=ts; |
321 | } |
d97315ac |
322 | |
323 | static void VRC6_ESI(void) |
324 | { |
325 | GameExpSound.RChange=VRC6_ESI; |
c62d2810 |
326 | GameExpSound.Fill=VRC6Sound; |
386f5371 |
327 | GameExpSound.HiFill=VRC6SoundHQ; |
328 | GameExpSound.HiSync=VRC6SyncHQ; |
d97315ac |
329 | |
330 | memset(CVBC,0,sizeof(CVBC)); |
331 | memset(vcount,0,sizeof(vcount)); |
332 | memset(dcount,0,sizeof(dcount)); |
c62d2810 |
333 | if(FSettings.SndRate) |
d97315ac |
334 | { |
d97315ac |
335 | if(FSettings.soundq>=1) |
336 | { |
337 | sfun[0]=DoSQV1HQ; |
338 | sfun[1]=DoSQV2HQ; |
339 | sfun[2]=DoSawVHQ; |
340 | } |
341 | else |
c62d2810 |
342 | { |
d97315ac |
343 | sfun[0]=DoSQV1; |
344 | sfun[1]=DoSQV2; |
345 | sfun[2]=DoSawV; |
c62d2810 |
346 | } |
d97315ac |
347 | } |
348 | else |
349 | memset(sfun,0,sizeof(sfun)); |
c62d2810 |
350 | } |
351 | |
352 | void Mapper24_init(void) |
353 | { |
354 | SetWriteHandler(0x8000,0xffff,Mapper24_write); |
d97315ac |
355 | VRC6_ESI(); |
c62d2810 |
356 | MapIRQHook=KonamiIRQHook; |
d97315ac |
357 | swaparoo=0; |
c62d2810 |
358 | } |
359 | |
360 | void Mapper26_init(void) |
361 | { |
362 | SetWriteHandler(0x8000,0xffff,Mapper24_write); |
d97315ac |
363 | VRC6_ESI(); |
c62d2810 |
364 | MapIRQHook=KonamiIRQHook; |
d97315ac |
365 | swaparoo=1; |
c62d2810 |
366 | } |
367 | |
d97315ac |
368 | void NSFVRC6_Init(void) |
369 | { |
370 | VRC6_ESI(); |
371 | SetWriteHandler(0x8000,0xbfff,VRC6SW); |
372 | } |