|
1 /* |
|
2 * GUSEMU32 - bus interface part |
|
3 * |
|
4 * Copyright (C) 2000-2007 Tibor "TS" Schütz |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 |
|
25 /* |
|
26 * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)? |
|
27 */ |
|
28 |
|
29 #include "gustate.h" |
|
30 #include "gusemu.h" |
|
31 |
|
32 #define GUSregb(position) (* (gusptr+(position))) |
|
33 #define GUSregw(position) (*(GUSword *) (gusptr+(position))) |
|
34 #define GUSregd(position) (*(GUSdword *)(gusptr+(position))) |
|
35 |
|
36 /* size given in bytes */ |
|
37 unsigned int gus_read(GUSEmuState * state, int port, int size) |
|
38 { |
|
39 int value_read = 0; |
|
40 |
|
41 GUSbyte *gusptr; |
|
42 gusptr = state->gusdatapos; |
|
43 GUSregd(portaccesses)++; |
|
44 |
|
45 switch (port & 0xff0f) |
|
46 { |
|
47 /* MixerCtrlReg (read not supported on GUS classic) */ |
|
48 /* case 0x200: return GUSregb(MixerCtrlReg2x0); */ |
|
49 case 0x206: /* IRQstatReg / SB2x6IRQ */ |
|
50 /* adlib/sb bits set in port handlers */ |
|
51 /* timer/voice bits set in gus_irqgen() */ |
|
52 /* dma bit set in gus_dma_transferdata */ |
|
53 /* midi not implemented yet */ |
|
54 return GUSregb(IRQStatReg2x6); |
|
55 /* case 0x308: */ /* AdLib388 */ |
|
56 case 0x208: |
|
57 if (GUSregb(GUS45TimerCtrl) & 1) |
|
58 return GUSregb(TimerStatus2x8); |
|
59 return GUSregb(AdLibStatus2x8); /* AdLibStatus */ |
|
60 case 0x309: /* AdLib389 */ |
|
61 case 0x209: |
|
62 return GUSregb(AdLibData2x9); /* AdLibData */ |
|
63 case 0x20A: |
|
64 return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */ |
|
65 |
|
66 #if 0 |
|
67 case 0x20B: /* GUS hidden registers (read not supported on GUS classic) */ |
|
68 switch (GUSregb(RegCtrl_2xF) & 0x07) |
|
69 { |
|
70 case 0: /* IRQ/DMA select */ |
|
71 if (GUSregb(MixerCtrlReg2x0) & 0x40) |
|
72 return GUSregb(IRQ_2xB); /* control register select bit */ |
|
73 else |
|
74 return GUSregb(DMA_2xB); |
|
75 /* case 1-5: */ /* general purpose emulation regs */ |
|
76 /* return ... */ /* + status reset reg (write only) */ |
|
77 case 6: |
|
78 return GUSregb(Jumper_2xB); /* Joystick/MIDI enable (JumperReg) */ |
|
79 default:; |
|
80 } |
|
81 break; |
|
82 #endif |
|
83 |
|
84 case 0x20C: /* SB2xCd */ |
|
85 value_read = GUSregb(SB2xCd); |
|
86 if (GUSregb(StatRead_2xF) & 0x20) |
|
87 GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */ |
|
88 return value_read; |
|
89 /* case 0x20D: */ /* SB2xD is write only -> 2xE writes to it*/ |
|
90 case 0x20E: |
|
91 if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */ |
|
92 { |
|
93 GUSregb(StatRead_2xF) |= 0x80; |
|
94 GUS_irqrequest(state, state->gusirq, 1); |
|
95 } |
|
96 return GUSregb(SB2xE); /* SB2xE */ |
|
97 case 0x20F: /* StatRead_2xF */ |
|
98 /*set/clear fixed bits */ |
|
99 /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/ |
|
100 value_read = (GUSregb(StatRead_2xF) & 0xf9); |
|
101 if (GUSregb(MixerCtrlReg2x0) & 0x08) |
|
102 value_read |= 2; /* DMA/IRQ enabled flag */ |
|
103 return value_read; |
|
104 /* case 0x300: */ /* MIDI (not implemented) */ |
|
105 /* case 0x301: */ /* MIDI (not implemented) */ |
|
106 case 0x302: |
|
107 return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */ |
|
108 case 0x303: |
|
109 return GUSregb(FunkSelReg3x3); /* FunkSelReg */ |
|
110 case 0x304: /* DataRegLoByte3x4 + DataRegWord3x4 */ |
|
111 case 0x305: /* DataRegHiByte3x5 */ |
|
112 switch (GUSregb(FunkSelReg3x3)) |
|
113 { |
|
114 /* common functions */ |
|
115 case 0x41: /* DramDMAContrReg */ |
|
116 value_read = GUSregb(GUS41DMACtrl); /* &0xfb */ |
|
117 GUSregb(GUS41DMACtrl) &= 0xbb; |
|
118 if (state->gusdma >= 4) |
|
119 value_read |= 0x04; |
|
120 if (GUSregb(IRQStatReg2x6) & 0x80) |
|
121 { |
|
122 value_read |= 0x40; |
|
123 GUSregb(IRQStatReg2x6) &= 0x7f; |
|
124 if (!GUSregb(IRQStatReg2x6)) |
|
125 GUS_irqclear(state, state->gusirq); |
|
126 } |
|
127 return (GUSbyte) value_read; |
|
128 /* DramDMAmemPosReg */ |
|
129 /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/ |
|
130 /* 43h+44h write only */ |
|
131 case 0x45: |
|
132 return GUSregb(GUS45TimerCtrl); /* TimerCtrlReg */ |
|
133 /* 46h+47h write only */ |
|
134 /* 48h: samp freq - write only */ |
|
135 case 0x49: |
|
136 return GUSregb(GUS49SampCtrl) & 0xbf; /* SampCtrlReg */ |
|
137 /* case 4bh: */ /* joystick trim not supported */ |
|
138 /* case 0x4c: return GUSregb(GUS4cReset); */ /* GUSreset: write only*/ |
|
139 /* voice specific functions */ |
|
140 case 0x80: |
|
141 case 0x81: |
|
142 case 0x82: |
|
143 case 0x83: |
|
144 case 0x84: |
|
145 case 0x85: |
|
146 case 0x86: |
|
147 case 0x87: |
|
148 case 0x88: |
|
149 case 0x89: |
|
150 case 0x8a: |
|
151 case 0x8b: |
|
152 case 0x8c: |
|
153 case 0x8d: |
|
154 { |
|
155 int offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); |
|
156 offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ |
|
157 value_read = GUSregw(offset); |
|
158 } |
|
159 break; |
|
160 /* voice unspecific functions */ |
|
161 case 0x8e: /* NumVoice */ |
|
162 return GUSregb(NumVoices); |
|
163 case 0x8f: /* irqstatreg */ |
|
164 /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */ |
|
165 return GUSregb(SynVoiceIRQ8f); |
|
166 default: |
|
167 return 0xffff; |
|
168 } |
|
169 if (size == 1) |
|
170 { |
|
171 if ((port & 0xff0f) == 0x305) |
|
172 value_read = value_read >> 8; |
|
173 value_read &= 0xff; |
|
174 } |
|
175 return (GUSword) value_read; |
|
176 /* case 0x306: */ /* Mixer/Version info */ |
|
177 /* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */ |
|
178 case 0x307: /* DRAMaccess */ |
|
179 { |
|
180 GUSbyte *adr; |
|
181 adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); |
|
182 return *adr; |
|
183 } |
|
184 default:; |
|
185 } |
|
186 return 0xffff; |
|
187 } |
|
188 |
|
189 void gus_write(GUSEmuState * state, int port, int size, unsigned int data) |
|
190 { |
|
191 GUSbyte *gusptr; |
|
192 gusptr = state->gusdatapos; |
|
193 GUSregd(portaccesses)++; |
|
194 |
|
195 switch (port & 0xff0f) |
|
196 { |
|
197 case 0x200: /* MixerCtrlReg */ |
|
198 GUSregb(MixerCtrlReg2x0) = (GUSbyte) data; |
|
199 break; |
|
200 case 0x206: /* IRQstatReg / SB2x6IRQ */ |
|
201 if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */ |
|
202 { |
|
203 GUSregb(TimerStatus2x8) |= 0x08; |
|
204 GUSregb(IRQStatReg2x6) = 0x10; |
|
205 GUS_irqrequest(state, state->gusirq, 1); |
|
206 } |
|
207 break; |
|
208 case 0x308: /* AdLib 388h */ |
|
209 case 0x208: /* AdLibCommandReg */ |
|
210 GUSregb(AdLibCommand2xA) = (GUSbyte) data; |
|
211 break; |
|
212 case 0x309: /* AdLib 389h */ |
|
213 case 0x209: /* AdLibDataReg */ |
|
214 if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */ |
|
215 { |
|
216 if (data & 0x80) |
|
217 GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */ |
|
218 else |
|
219 GUSregb(TimerDataReg2x9) = (GUSbyte) data; |
|
220 } |
|
221 else |
|
222 { |
|
223 GUSregb(AdLibData2x9) = (GUSbyte) data; |
|
224 if (GUSregb(GUS45TimerCtrl) & 0x02) |
|
225 { |
|
226 GUSregb(TimerStatus2x8) |= 0x01; |
|
227 GUSregb(IRQStatReg2x6) = 0x10; |
|
228 GUS_irqrequest(state, state->gusirq, 1); |
|
229 } |
|
230 } |
|
231 break; |
|
232 case 0x20A: |
|
233 GUSregb(AdLibStatus2x8) = (GUSbyte) data; |
|
234 break; /* AdLibStatus2x8 */ |
|
235 case 0x20B: /* GUS hidden registers */ |
|
236 switch (GUSregb(RegCtrl_2xF) & 0x7) |
|
237 { |
|
238 case 0: |
|
239 if (GUSregb(MixerCtrlReg2x0) & 0x40) |
|
240 GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */ |
|
241 else |
|
242 GUSregb(DMA_2xB) = (GUSbyte) data; |
|
243 break; |
|
244 /* case 1-4: general purpose emulation regs */ |
|
245 case 5: /* clear stat reg 2xF */ |
|
246 GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */ |
|
247 if (!GUSregb(IRQStatReg2x6)) |
|
248 GUS_irqclear(state, state->gusirq); |
|
249 break; |
|
250 case 6: /* Jumper reg (Joystick/MIDI enable) */ |
|
251 GUSregb(Jumper_2xB) = (GUSbyte) data; |
|
252 break; |
|
253 default:; |
|
254 } |
|
255 break; |
|
256 case 0x20C: /* SB2xCd */ |
|
257 if (GUSregb(GUS45TimerCtrl) & 0x20) |
|
258 { |
|
259 GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */ |
|
260 GUSregb(IRQStatReg2x6) = 0x10; |
|
261 GUS_irqrequest(state, state->gusirq, 1); |
|
262 } |
|
263 case 0x20D: /* SB2xCd no IRQ */ |
|
264 GUSregb(SB2xCd) = (GUSbyte) data; |
|
265 break; |
|
266 case 0x20E: /* SB2xE */ |
|
267 GUSregb(SB2xE) = (GUSbyte) data; |
|
268 break; |
|
269 case 0x20F: |
|
270 GUSregb(RegCtrl_2xF) = (GUSbyte) data; |
|
271 break; /* CtrlReg2xF */ |
|
272 case 0x302: /* VoiceSelReg */ |
|
273 GUSregb(VoiceSelReg3x2) = (GUSbyte) data; |
|
274 break; |
|
275 case 0x303: /* FunkSelReg */ |
|
276 GUSregb(FunkSelReg3x3) = (GUSbyte) data; |
|
277 if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */ |
|
278 { |
|
279 int voice; |
|
280 if (GUSregd(voicewavetableirq)) /* WavetableIRQ */ |
|
281 { |
|
282 for (voice = 0; voice < 31; voice++) |
|
283 { |
|
284 if (GUSregd(voicewavetableirq) & (1 << voice)) |
|
285 { |
|
286 GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */ |
|
287 GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */ |
|
288 if (!GUSregd(voicewavetableirq)) |
|
289 GUSregb(IRQStatReg2x6) &= 0xdf; |
|
290 if (!GUSregb(IRQStatReg2x6)) |
|
291 GUS_irqclear(state, state->gusirq); |
|
292 GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */ |
|
293 return; |
|
294 } |
|
295 } |
|
296 } |
|
297 else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */ |
|
298 { |
|
299 for (voice = 0; voice < 31; voice++) |
|
300 { |
|
301 if (GUSregd(voicevolrampirq) & (1 << voice)) |
|
302 { |
|
303 GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */ |
|
304 GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */ |
|
305 if (!GUSregd(voicevolrampirq)) |
|
306 GUSregb(IRQStatReg2x6) &= 0xbf; |
|
307 if (!GUSregb(IRQStatReg2x6)) |
|
308 GUS_irqclear(state, state->gusirq); |
|
309 GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */ |
|
310 return; |
|
311 } |
|
312 } |
|
313 } |
|
314 GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */ |
|
315 } |
|
316 break; |
|
317 case 0x304: |
|
318 case 0x305: |
|
319 { |
|
320 GUSword writedata = (GUSword) data; |
|
321 GUSword readmask = 0x0000; |
|
322 if (size == 1) |
|
323 { |
|
324 readmask = 0xff00; |
|
325 writedata &= 0xff; |
|
326 if ((port & 0xff0f) == 0x305) |
|
327 { |
|
328 writedata = (GUSword) (writedata << 8); |
|
329 readmask = 0x00ff; |
|
330 } |
|
331 } |
|
332 switch (GUSregb(FunkSelReg3x3)) |
|
333 { |
|
334 /* voice specific functions */ |
|
335 case 0x00: |
|
336 case 0x01: |
|
337 case 0x02: |
|
338 case 0x03: |
|
339 case 0x04: |
|
340 case 0x05: |
|
341 case 0x06: |
|
342 case 0x07: |
|
343 case 0x08: |
|
344 case 0x09: |
|
345 case 0x0a: |
|
346 case 0x0b: |
|
347 case 0x0c: |
|
348 case 0x0d: |
|
349 { |
|
350 int offset; |
|
351 if (!(GUSregb(GUS4cReset) & 0x01)) |
|
352 break; /* reset flag active? */ |
|
353 offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); |
|
354 offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ |
|
355 GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata); |
|
356 } |
|
357 break; |
|
358 /* voice unspecific functions */ |
|
359 case 0x0e: /* NumVoices */ |
|
360 GUSregb(NumVoices) = (GUSbyte) data; |
|
361 break; |
|
362 /* case 0x0f: */ /* read only */ |
|
363 /* common functions */ |
|
364 case 0x41: /* DramDMAContrReg */ |
|
365 GUSregb(GUS41DMACtrl) = (GUSbyte) data; |
|
366 if (data & 0x01) |
|
367 GUS_dmarequest(state); |
|
368 break; |
|
369 case 0x42: /* DramDMAmemPosReg */ |
|
370 GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata; |
|
371 GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */ |
|
372 break; |
|
373 case 0x43: /* DRAMaddrLo */ |
|
374 GUSregd(GUSDRAMPOS24bit) = |
|
375 (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata; |
|
376 break; |
|
377 case 0x44: /* DRAMaddrHi */ |
|
378 GUSregd(GUSDRAMPOS24bit) = |
|
379 (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16); |
|
380 break; |
|
381 case 0x45: /* TCtrlReg */ |
|
382 GUSregb(GUS45TimerCtrl) = (GUSbyte) data; |
|
383 if (!(data & 0x20)) |
|
384 GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */ |
|
385 if (!(data & 0x02)) |
|
386 GUSregb(TimerStatus2x8) &= 0xfe; /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */ |
|
387 if (!(GUSregb(TimerStatus2x8) & 0x19)) |
|
388 GUSregb(IRQStatReg2x6) &= 0xef; /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */ |
|
389 /* catch up delayed timer IRQs: */ |
|
390 if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3)) |
|
391 { |
|
392 if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ |
|
393 { |
|
394 if (!(GUSregb(TimerDataReg2x9) & 0x40)) |
|
395 GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ |
|
396 if (data & 4) /* timer1 irq enable */ |
|
397 { |
|
398 GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ |
|
399 GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ |
|
400 } |
|
401 } |
|
402 if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ |
|
403 { |
|
404 if (!(GUSregb(TimerDataReg2x9) & 0x20)) |
|
405 GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ |
|
406 if (data & 8) /* timer2 irq enable */ |
|
407 { |
|
408 GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ |
|
409 GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ |
|
410 } |
|
411 } |
|
412 GUSregw(TimerIRQs)--; |
|
413 if (GUSregw(BusyTimerIRQs) > 1) |
|
414 GUSregw(BusyTimerIRQs)--; |
|
415 else |
|
416 GUSregw(BusyTimerIRQs) = |
|
417 GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs)); |
|
418 } |
|
419 else |
|
420 GUSregw(TimerIRQs) = 0; |
|
421 |
|
422 if (!(data & 0x04)) |
|
423 { |
|
424 GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */ |
|
425 GUSregb(IRQStatReg2x6) &= 0xfb; |
|
426 } |
|
427 if (!(data & 0x08)) |
|
428 { |
|
429 GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */ |
|
430 GUSregb(IRQStatReg2x6) &= 0xf7; |
|
431 } |
|
432 if (!GUSregb(IRQStatReg2x6)) |
|
433 GUS_irqclear(state, state->gusirq); |
|
434 break; |
|
435 case 0x46: /* Counter1 */ |
|
436 GUSregb(GUS46Counter1) = (GUSbyte) data; |
|
437 break; |
|
438 case 0x47: /* Counter2 */ |
|
439 GUSregb(GUS47Counter2) = (GUSbyte) data; |
|
440 break; |
|
441 /* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */ |
|
442 case 0x49: /* SampCtrlReg */ |
|
443 GUSregb(GUS49SampCtrl) = (GUSbyte) data; |
|
444 break; |
|
445 /* case 0x4b: */ /* joystick trim not emulated */ |
|
446 case 0x4c: /* GUSreset */ |
|
447 GUSregb(GUS4cReset) = (GUSbyte) data; |
|
448 if (!(GUSregb(GUS4cReset) & 1)) /* reset... */ |
|
449 { |
|
450 GUSregd(voicewavetableirq) = 0; |
|
451 GUSregd(voicevolrampirq) = 0; |
|
452 GUSregw(TimerIRQs) = 0; |
|
453 GUSregw(BusyTimerIRQs) = 0; |
|
454 GUSregb(NumVoices) = 0xcd; |
|
455 GUSregb(IRQStatReg2x6) = 0; |
|
456 GUSregb(TimerStatus2x8) = 0; |
|
457 GUSregb(AdLibData2x9) = 0; |
|
458 GUSregb(TimerDataReg2x9) = 0; |
|
459 GUSregb(GUS41DMACtrl) = 0; |
|
460 GUSregb(GUS45TimerCtrl) = 0; |
|
461 GUSregb(GUS49SampCtrl) = 0; |
|
462 GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */ |
|
463 GUS_irqclear(state, state->gusirq); |
|
464 } |
|
465 /* IRQ enable bit checked elsewhere */ |
|
466 /* EnableDAC bit may be used by external callers */ |
|
467 break; |
|
468 } |
|
469 } |
|
470 break; |
|
471 case 0x307: /* DRAMaccess */ |
|
472 { |
|
473 GUSbyte *adr; |
|
474 adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); |
|
475 *adr = (GUSbyte) data; |
|
476 } |
|
477 break; |
|
478 } |
|
479 } |
|
480 |
|
481 /* Attention when breaking up a single DMA transfer to multiple ones: |
|
482 * it may lead to multiple terminal count interrupts and broken transfers: |
|
483 * |
|
484 * 1. Whenever you transfer a piece of data, the gusemu callback is invoked |
|
485 * 2. The callback may generate a TC irq (if the register was set up to do so) |
|
486 * 3. The irq may result in the program using the GUS to reprogram the GUS |
|
487 * |
|
488 * Some programs also decide to upload by just checking if TC occurs |
|
489 * (via interrupt or a cleared GUS dma flag) |
|
490 * and then start the next transfer, without checking DMA state |
|
491 * |
|
492 * Thus: Always make sure to set the TC flag correctly! |
|
493 * |
|
494 * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA |
|
495 * while later cards had atomic granularity provided by an additional GUS50DMAHigh register |
|
496 * GUSemu also uses this register to support byte-granular transfers for better compatibility |
|
497 * with emulators other than GUSemu32 |
|
498 */ |
|
499 |
|
500 void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC) |
|
501 { |
|
502 /* this function gets called by the callback function as soon as a DMA transfer is about to start |
|
503 * dma_addr is a translated address within accessible memory, not the physical one, |
|
504 * count is (real dma count register)+1 |
|
505 * note that the amount of bytes transfered is fully determined by values in the DMA registers |
|
506 * do not forget to update DMA states after transferring the entire block: |
|
507 * DREQ cleared & TC asserted after the _whole_ transfer */ |
|
508 |
|
509 char *srcaddr; |
|
510 char *destaddr; |
|
511 char msbmask = 0; |
|
512 GUSbyte *gusptr; |
|
513 gusptr = state->gusdatapos; |
|
514 |
|
515 srcaddr = dma_addr; /* system memory address */ |
|
516 { |
|
517 int offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf); |
|
518 if (state->gusdma >= 4) |
|
519 offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */ |
|
520 destaddr = (char *) state->himemaddr + offset; /* wavetable RAM adress */ |
|
521 } |
|
522 |
|
523 GUSregw(GUS42DMAStart) += (GUSword) (count >> 4); /* ToDo: add 16bit GUS page limit? */ |
|
524 GUSregb(GUS50DMAHigh) = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */ |
|
525 |
|
526 if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */ |
|
527 { |
|
528 char *tmpaddr = destaddr; |
|
529 destaddr = srcaddr; |
|
530 srcaddr = tmpaddr; |
|
531 } |
|
532 |
|
533 if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02))) |
|
534 msbmask = (const char) 0x80; /* invert MSB */ |
|
535 for (; count > 0; count--) |
|
536 { |
|
537 if (GUSregb(GUS41DMACtrl) & 0x40) |
|
538 *(destaddr++) = *(srcaddr++); /* 16 bit lobyte */ |
|
539 else |
|
540 *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */ |
|
541 if (state->gusdma >= 4) |
|
542 *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */ |
|
543 } |
|
544 |
|
545 if (TC) |
|
546 { |
|
547 (GUSregb(GUS41DMACtrl)) &= 0xfe; /* clear DMA request bit */ |
|
548 if (GUSregb(GUS41DMACtrl) & 0x20) /* DMA terminal count IRQ */ |
|
549 { |
|
550 GUSregb(IRQStatReg2x6) |= 0x80; |
|
551 GUS_irqrequest(state, state->gusirq, 1); |
|
552 } |
|
553 } |
|
554 } |