149ab747fSPaolo Bonzini /* 249ab747fSPaolo Bonzini * GUSEMU32 - bus interface part 349ab747fSPaolo Bonzini * 449ab747fSPaolo Bonzini * Copyright (C) 2000-2007 Tibor "TS" Schütz 549ab747fSPaolo Bonzini * 649ab747fSPaolo Bonzini * Permission is hereby granted, free of charge, to any person obtaining a copy 749ab747fSPaolo Bonzini * of this software and associated documentation files (the "Software"), to deal 849ab747fSPaolo Bonzini * in the Software without restriction, including without limitation the rights 949ab747fSPaolo Bonzini * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1049ab747fSPaolo Bonzini * copies of the Software, and to permit persons to whom the Software is 1149ab747fSPaolo Bonzini * furnished to do so, subject to the following conditions: 1249ab747fSPaolo Bonzini * 1349ab747fSPaolo Bonzini * The above copyright notice and this permission notice shall be included in 1449ab747fSPaolo Bonzini * all copies or substantial portions of the Software. 1549ab747fSPaolo Bonzini * 1649ab747fSPaolo Bonzini * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1749ab747fSPaolo Bonzini * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1849ab747fSPaolo Bonzini * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1949ab747fSPaolo Bonzini * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2049ab747fSPaolo Bonzini * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2149ab747fSPaolo Bonzini * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2249ab747fSPaolo Bonzini * THE SOFTWARE. 2349ab747fSPaolo Bonzini */ 2449ab747fSPaolo Bonzini 2549ab747fSPaolo Bonzini /* 2649ab747fSPaolo Bonzini * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)? 2749ab747fSPaolo Bonzini */ 2849ab747fSPaolo Bonzini 29*47b43a1fSPaolo Bonzini #include "gustate.h" 30*47b43a1fSPaolo Bonzini #include "gusemu.h" 3149ab747fSPaolo Bonzini 3249ab747fSPaolo Bonzini #define GUSregb(position) (* (gusptr+(position))) 3349ab747fSPaolo Bonzini #define GUSregw(position) (*(GUSword *) (gusptr+(position))) 3449ab747fSPaolo Bonzini #define GUSregd(position) (*(GUSdword *)(gusptr+(position))) 3549ab747fSPaolo Bonzini 3649ab747fSPaolo Bonzini /* size given in bytes */ 3749ab747fSPaolo Bonzini unsigned int gus_read(GUSEmuState * state, int port, int size) 3849ab747fSPaolo Bonzini { 3949ab747fSPaolo Bonzini int value_read = 0; 4049ab747fSPaolo Bonzini 4149ab747fSPaolo Bonzini GUSbyte *gusptr; 4249ab747fSPaolo Bonzini gusptr = state->gusdatapos; 4349ab747fSPaolo Bonzini GUSregd(portaccesses)++; 4449ab747fSPaolo Bonzini 4549ab747fSPaolo Bonzini switch (port & 0xff0f) 4649ab747fSPaolo Bonzini { 4749ab747fSPaolo Bonzini /* MixerCtrlReg (read not supported on GUS classic) */ 4849ab747fSPaolo Bonzini /* case 0x200: return GUSregb(MixerCtrlReg2x0); */ 4949ab747fSPaolo Bonzini case 0x206: /* IRQstatReg / SB2x6IRQ */ 5049ab747fSPaolo Bonzini /* adlib/sb bits set in port handlers */ 5149ab747fSPaolo Bonzini /* timer/voice bits set in gus_irqgen() */ 5249ab747fSPaolo Bonzini /* dma bit set in gus_dma_transferdata */ 5349ab747fSPaolo Bonzini /* midi not implemented yet */ 5449ab747fSPaolo Bonzini return GUSregb(IRQStatReg2x6); 5549ab747fSPaolo Bonzini /* case 0x308: */ /* AdLib388 */ 5649ab747fSPaolo Bonzini case 0x208: 5749ab747fSPaolo Bonzini if (GUSregb(GUS45TimerCtrl) & 1) 5849ab747fSPaolo Bonzini return GUSregb(TimerStatus2x8); 5949ab747fSPaolo Bonzini return GUSregb(AdLibStatus2x8); /* AdLibStatus */ 6049ab747fSPaolo Bonzini case 0x309: /* AdLib389 */ 6149ab747fSPaolo Bonzini case 0x209: 6249ab747fSPaolo Bonzini return GUSregb(AdLibData2x9); /* AdLibData */ 6349ab747fSPaolo Bonzini case 0x20A: 6449ab747fSPaolo Bonzini return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */ 6549ab747fSPaolo Bonzini 6649ab747fSPaolo Bonzini #if 0 6749ab747fSPaolo Bonzini case 0x20B: /* GUS hidden registers (read not supported on GUS classic) */ 6849ab747fSPaolo Bonzini switch (GUSregb(RegCtrl_2xF) & 0x07) 6949ab747fSPaolo Bonzini { 7049ab747fSPaolo Bonzini case 0: /* IRQ/DMA select */ 7149ab747fSPaolo Bonzini if (GUSregb(MixerCtrlReg2x0) & 0x40) 7249ab747fSPaolo Bonzini return GUSregb(IRQ_2xB); /* control register select bit */ 7349ab747fSPaolo Bonzini else 7449ab747fSPaolo Bonzini return GUSregb(DMA_2xB); 7549ab747fSPaolo Bonzini /* case 1-5: */ /* general purpose emulation regs */ 7649ab747fSPaolo Bonzini /* return ... */ /* + status reset reg (write only) */ 7749ab747fSPaolo Bonzini case 6: 7849ab747fSPaolo Bonzini return GUSregb(Jumper_2xB); /* Joystick/MIDI enable (JumperReg) */ 7949ab747fSPaolo Bonzini default:; 8049ab747fSPaolo Bonzini } 8149ab747fSPaolo Bonzini break; 8249ab747fSPaolo Bonzini #endif 8349ab747fSPaolo Bonzini 8449ab747fSPaolo Bonzini case 0x20C: /* SB2xCd */ 8549ab747fSPaolo Bonzini value_read = GUSregb(SB2xCd); 8649ab747fSPaolo Bonzini if (GUSregb(StatRead_2xF) & 0x20) 8749ab747fSPaolo Bonzini GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */ 8849ab747fSPaolo Bonzini return value_read; 8949ab747fSPaolo Bonzini /* case 0x20D: */ /* SB2xD is write only -> 2xE writes to it*/ 9049ab747fSPaolo Bonzini case 0x20E: 9149ab747fSPaolo Bonzini if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */ 9249ab747fSPaolo Bonzini { 9349ab747fSPaolo Bonzini GUSregb(StatRead_2xF) |= 0x80; 9449ab747fSPaolo Bonzini GUS_irqrequest(state, state->gusirq, 1); 9549ab747fSPaolo Bonzini } 9649ab747fSPaolo Bonzini return GUSregb(SB2xE); /* SB2xE */ 9749ab747fSPaolo Bonzini case 0x20F: /* StatRead_2xF */ 9849ab747fSPaolo Bonzini /*set/clear fixed bits */ 9949ab747fSPaolo Bonzini /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/ 10049ab747fSPaolo Bonzini value_read = (GUSregb(StatRead_2xF) & 0xf9); 10149ab747fSPaolo Bonzini if (GUSregb(MixerCtrlReg2x0) & 0x08) 10249ab747fSPaolo Bonzini value_read |= 2; /* DMA/IRQ enabled flag */ 10349ab747fSPaolo Bonzini return value_read; 10449ab747fSPaolo Bonzini /* case 0x300: */ /* MIDI (not implemented) */ 10549ab747fSPaolo Bonzini /* case 0x301: */ /* MIDI (not implemented) */ 10649ab747fSPaolo Bonzini case 0x302: 10749ab747fSPaolo Bonzini return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */ 10849ab747fSPaolo Bonzini case 0x303: 10949ab747fSPaolo Bonzini return GUSregb(FunkSelReg3x3); /* FunkSelReg */ 11049ab747fSPaolo Bonzini case 0x304: /* DataRegLoByte3x4 + DataRegWord3x4 */ 11149ab747fSPaolo Bonzini case 0x305: /* DataRegHiByte3x5 */ 11249ab747fSPaolo Bonzini switch (GUSregb(FunkSelReg3x3)) 11349ab747fSPaolo Bonzini { 11449ab747fSPaolo Bonzini /* common functions */ 11549ab747fSPaolo Bonzini case 0x41: /* DramDMAContrReg */ 11649ab747fSPaolo Bonzini value_read = GUSregb(GUS41DMACtrl); /* &0xfb */ 11749ab747fSPaolo Bonzini GUSregb(GUS41DMACtrl) &= 0xbb; 11849ab747fSPaolo Bonzini if (state->gusdma >= 4) 11949ab747fSPaolo Bonzini value_read |= 0x04; 12049ab747fSPaolo Bonzini if (GUSregb(IRQStatReg2x6) & 0x80) 12149ab747fSPaolo Bonzini { 12249ab747fSPaolo Bonzini value_read |= 0x40; 12349ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) &= 0x7f; 12449ab747fSPaolo Bonzini if (!GUSregb(IRQStatReg2x6)) 12549ab747fSPaolo Bonzini GUS_irqclear(state, state->gusirq); 12649ab747fSPaolo Bonzini } 12749ab747fSPaolo Bonzini return (GUSbyte) value_read; 12849ab747fSPaolo Bonzini /* DramDMAmemPosReg */ 12949ab747fSPaolo Bonzini /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/ 13049ab747fSPaolo Bonzini /* 43h+44h write only */ 13149ab747fSPaolo Bonzini case 0x45: 13249ab747fSPaolo Bonzini return GUSregb(GUS45TimerCtrl); /* TimerCtrlReg */ 13349ab747fSPaolo Bonzini /* 46h+47h write only */ 13449ab747fSPaolo Bonzini /* 48h: samp freq - write only */ 13549ab747fSPaolo Bonzini case 0x49: 13649ab747fSPaolo Bonzini return GUSregb(GUS49SampCtrl) & 0xbf; /* SampCtrlReg */ 13749ab747fSPaolo Bonzini /* case 4bh: */ /* joystick trim not supported */ 13849ab747fSPaolo Bonzini /* case 0x4c: return GUSregb(GUS4cReset); */ /* GUSreset: write only*/ 13949ab747fSPaolo Bonzini /* voice specific functions */ 14049ab747fSPaolo Bonzini case 0x80: 14149ab747fSPaolo Bonzini case 0x81: 14249ab747fSPaolo Bonzini case 0x82: 14349ab747fSPaolo Bonzini case 0x83: 14449ab747fSPaolo Bonzini case 0x84: 14549ab747fSPaolo Bonzini case 0x85: 14649ab747fSPaolo Bonzini case 0x86: 14749ab747fSPaolo Bonzini case 0x87: 14849ab747fSPaolo Bonzini case 0x88: 14949ab747fSPaolo Bonzini case 0x89: 15049ab747fSPaolo Bonzini case 0x8a: 15149ab747fSPaolo Bonzini case 0x8b: 15249ab747fSPaolo Bonzini case 0x8c: 15349ab747fSPaolo Bonzini case 0x8d: 15449ab747fSPaolo Bonzini { 15549ab747fSPaolo Bonzini int offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); 15649ab747fSPaolo Bonzini offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ 15749ab747fSPaolo Bonzini value_read = GUSregw(offset); 15849ab747fSPaolo Bonzini } 15949ab747fSPaolo Bonzini break; 16049ab747fSPaolo Bonzini /* voice unspecific functions */ 16149ab747fSPaolo Bonzini case 0x8e: /* NumVoice */ 16249ab747fSPaolo Bonzini return GUSregb(NumVoices); 16349ab747fSPaolo Bonzini case 0x8f: /* irqstatreg */ 16449ab747fSPaolo Bonzini /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */ 16549ab747fSPaolo Bonzini return GUSregb(SynVoiceIRQ8f); 16649ab747fSPaolo Bonzini default: 16749ab747fSPaolo Bonzini return 0xffff; 16849ab747fSPaolo Bonzini } 16949ab747fSPaolo Bonzini if (size == 1) 17049ab747fSPaolo Bonzini { 17149ab747fSPaolo Bonzini if ((port & 0xff0f) == 0x305) 17249ab747fSPaolo Bonzini value_read = value_read >> 8; 17349ab747fSPaolo Bonzini value_read &= 0xff; 17449ab747fSPaolo Bonzini } 17549ab747fSPaolo Bonzini return (GUSword) value_read; 17649ab747fSPaolo Bonzini /* case 0x306: */ /* Mixer/Version info */ 17749ab747fSPaolo Bonzini /* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */ 17849ab747fSPaolo Bonzini case 0x307: /* DRAMaccess */ 17949ab747fSPaolo Bonzini { 18049ab747fSPaolo Bonzini GUSbyte *adr; 18149ab747fSPaolo Bonzini adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); 18249ab747fSPaolo Bonzini return *adr; 18349ab747fSPaolo Bonzini } 18449ab747fSPaolo Bonzini default:; 18549ab747fSPaolo Bonzini } 18649ab747fSPaolo Bonzini return 0xffff; 18749ab747fSPaolo Bonzini } 18849ab747fSPaolo Bonzini 18949ab747fSPaolo Bonzini void gus_write(GUSEmuState * state, int port, int size, unsigned int data) 19049ab747fSPaolo Bonzini { 19149ab747fSPaolo Bonzini GUSbyte *gusptr; 19249ab747fSPaolo Bonzini gusptr = state->gusdatapos; 19349ab747fSPaolo Bonzini GUSregd(portaccesses)++; 19449ab747fSPaolo Bonzini 19549ab747fSPaolo Bonzini switch (port & 0xff0f) 19649ab747fSPaolo Bonzini { 19749ab747fSPaolo Bonzini case 0x200: /* MixerCtrlReg */ 19849ab747fSPaolo Bonzini GUSregb(MixerCtrlReg2x0) = (GUSbyte) data; 19949ab747fSPaolo Bonzini break; 20049ab747fSPaolo Bonzini case 0x206: /* IRQstatReg / SB2x6IRQ */ 20149ab747fSPaolo Bonzini if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */ 20249ab747fSPaolo Bonzini { 20349ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 0x08; 20449ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) = 0x10; 20549ab747fSPaolo Bonzini GUS_irqrequest(state, state->gusirq, 1); 20649ab747fSPaolo Bonzini } 20749ab747fSPaolo Bonzini break; 20849ab747fSPaolo Bonzini case 0x308: /* AdLib 388h */ 20949ab747fSPaolo Bonzini case 0x208: /* AdLibCommandReg */ 21049ab747fSPaolo Bonzini GUSregb(AdLibCommand2xA) = (GUSbyte) data; 21149ab747fSPaolo Bonzini break; 21249ab747fSPaolo Bonzini case 0x309: /* AdLib 389h */ 21349ab747fSPaolo Bonzini case 0x209: /* AdLibDataReg */ 21449ab747fSPaolo Bonzini if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */ 21549ab747fSPaolo Bonzini { 21649ab747fSPaolo Bonzini if (data & 0x80) 21749ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */ 21849ab747fSPaolo Bonzini else 21949ab747fSPaolo Bonzini GUSregb(TimerDataReg2x9) = (GUSbyte) data; 22049ab747fSPaolo Bonzini } 22149ab747fSPaolo Bonzini else 22249ab747fSPaolo Bonzini { 22349ab747fSPaolo Bonzini GUSregb(AdLibData2x9) = (GUSbyte) data; 22449ab747fSPaolo Bonzini if (GUSregb(GUS45TimerCtrl) & 0x02) 22549ab747fSPaolo Bonzini { 22649ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 0x01; 22749ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) = 0x10; 22849ab747fSPaolo Bonzini GUS_irqrequest(state, state->gusirq, 1); 22949ab747fSPaolo Bonzini } 23049ab747fSPaolo Bonzini } 23149ab747fSPaolo Bonzini break; 23249ab747fSPaolo Bonzini case 0x20A: 23349ab747fSPaolo Bonzini GUSregb(AdLibStatus2x8) = (GUSbyte) data; 23449ab747fSPaolo Bonzini break; /* AdLibStatus2x8 */ 23549ab747fSPaolo Bonzini case 0x20B: /* GUS hidden registers */ 23649ab747fSPaolo Bonzini switch (GUSregb(RegCtrl_2xF) & 0x7) 23749ab747fSPaolo Bonzini { 23849ab747fSPaolo Bonzini case 0: 23949ab747fSPaolo Bonzini if (GUSregb(MixerCtrlReg2x0) & 0x40) 24049ab747fSPaolo Bonzini GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */ 24149ab747fSPaolo Bonzini else 24249ab747fSPaolo Bonzini GUSregb(DMA_2xB) = (GUSbyte) data; 24349ab747fSPaolo Bonzini break; 24449ab747fSPaolo Bonzini /* case 1-4: general purpose emulation regs */ 24549ab747fSPaolo Bonzini case 5: /* clear stat reg 2xF */ 24649ab747fSPaolo Bonzini GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */ 24749ab747fSPaolo Bonzini if (!GUSregb(IRQStatReg2x6)) 24849ab747fSPaolo Bonzini GUS_irqclear(state, state->gusirq); 24949ab747fSPaolo Bonzini break; 25049ab747fSPaolo Bonzini case 6: /* Jumper reg (Joystick/MIDI enable) */ 25149ab747fSPaolo Bonzini GUSregb(Jumper_2xB) = (GUSbyte) data; 25249ab747fSPaolo Bonzini break; 25349ab747fSPaolo Bonzini default:; 25449ab747fSPaolo Bonzini } 25549ab747fSPaolo Bonzini break; 25649ab747fSPaolo Bonzini case 0x20C: /* SB2xCd */ 25749ab747fSPaolo Bonzini if (GUSregb(GUS45TimerCtrl) & 0x20) 25849ab747fSPaolo Bonzini { 25949ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */ 26049ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) = 0x10; 26149ab747fSPaolo Bonzini GUS_irqrequest(state, state->gusirq, 1); 26249ab747fSPaolo Bonzini } 26349ab747fSPaolo Bonzini case 0x20D: /* SB2xCd no IRQ */ 26449ab747fSPaolo Bonzini GUSregb(SB2xCd) = (GUSbyte) data; 26549ab747fSPaolo Bonzini break; 26649ab747fSPaolo Bonzini case 0x20E: /* SB2xE */ 26749ab747fSPaolo Bonzini GUSregb(SB2xE) = (GUSbyte) data; 26849ab747fSPaolo Bonzini break; 26949ab747fSPaolo Bonzini case 0x20F: 27049ab747fSPaolo Bonzini GUSregb(RegCtrl_2xF) = (GUSbyte) data; 27149ab747fSPaolo Bonzini break; /* CtrlReg2xF */ 27249ab747fSPaolo Bonzini case 0x302: /* VoiceSelReg */ 27349ab747fSPaolo Bonzini GUSregb(VoiceSelReg3x2) = (GUSbyte) data; 27449ab747fSPaolo Bonzini break; 27549ab747fSPaolo Bonzini case 0x303: /* FunkSelReg */ 27649ab747fSPaolo Bonzini GUSregb(FunkSelReg3x3) = (GUSbyte) data; 27749ab747fSPaolo Bonzini if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */ 27849ab747fSPaolo Bonzini { 27949ab747fSPaolo Bonzini int voice; 28049ab747fSPaolo Bonzini if (GUSregd(voicewavetableirq)) /* WavetableIRQ */ 28149ab747fSPaolo Bonzini { 28249ab747fSPaolo Bonzini for (voice = 0; voice < 31; voice++) 28349ab747fSPaolo Bonzini { 28449ab747fSPaolo Bonzini if (GUSregd(voicewavetableirq) & (1 << voice)) 28549ab747fSPaolo Bonzini { 28649ab747fSPaolo Bonzini GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */ 28749ab747fSPaolo Bonzini GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */ 28849ab747fSPaolo Bonzini if (!GUSregd(voicewavetableirq)) 28949ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) &= 0xdf; 29049ab747fSPaolo Bonzini if (!GUSregb(IRQStatReg2x6)) 29149ab747fSPaolo Bonzini GUS_irqclear(state, state->gusirq); 29249ab747fSPaolo Bonzini GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */ 29349ab747fSPaolo Bonzini return; 29449ab747fSPaolo Bonzini } 29549ab747fSPaolo Bonzini } 29649ab747fSPaolo Bonzini } 29749ab747fSPaolo Bonzini else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */ 29849ab747fSPaolo Bonzini { 29949ab747fSPaolo Bonzini for (voice = 0; voice < 31; voice++) 30049ab747fSPaolo Bonzini { 30149ab747fSPaolo Bonzini if (GUSregd(voicevolrampirq) & (1 << voice)) 30249ab747fSPaolo Bonzini { 30349ab747fSPaolo Bonzini GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */ 30449ab747fSPaolo Bonzini GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */ 30549ab747fSPaolo Bonzini if (!GUSregd(voicevolrampirq)) 30649ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) &= 0xbf; 30749ab747fSPaolo Bonzini if (!GUSregb(IRQStatReg2x6)) 30849ab747fSPaolo Bonzini GUS_irqclear(state, state->gusirq); 30949ab747fSPaolo Bonzini GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */ 31049ab747fSPaolo Bonzini return; 31149ab747fSPaolo Bonzini } 31249ab747fSPaolo Bonzini } 31349ab747fSPaolo Bonzini } 31449ab747fSPaolo Bonzini GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */ 31549ab747fSPaolo Bonzini } 31649ab747fSPaolo Bonzini break; 31749ab747fSPaolo Bonzini case 0x304: 31849ab747fSPaolo Bonzini case 0x305: 31949ab747fSPaolo Bonzini { 32049ab747fSPaolo Bonzini GUSword writedata = (GUSword) data; 32149ab747fSPaolo Bonzini GUSword readmask = 0x0000; 32249ab747fSPaolo Bonzini if (size == 1) 32349ab747fSPaolo Bonzini { 32449ab747fSPaolo Bonzini readmask = 0xff00; 32549ab747fSPaolo Bonzini writedata &= 0xff; 32649ab747fSPaolo Bonzini if ((port & 0xff0f) == 0x305) 32749ab747fSPaolo Bonzini { 32849ab747fSPaolo Bonzini writedata = (GUSword) (writedata << 8); 32949ab747fSPaolo Bonzini readmask = 0x00ff; 33049ab747fSPaolo Bonzini } 33149ab747fSPaolo Bonzini } 33249ab747fSPaolo Bonzini switch (GUSregb(FunkSelReg3x3)) 33349ab747fSPaolo Bonzini { 33449ab747fSPaolo Bonzini /* voice specific functions */ 33549ab747fSPaolo Bonzini case 0x00: 33649ab747fSPaolo Bonzini case 0x01: 33749ab747fSPaolo Bonzini case 0x02: 33849ab747fSPaolo Bonzini case 0x03: 33949ab747fSPaolo Bonzini case 0x04: 34049ab747fSPaolo Bonzini case 0x05: 34149ab747fSPaolo Bonzini case 0x06: 34249ab747fSPaolo Bonzini case 0x07: 34349ab747fSPaolo Bonzini case 0x08: 34449ab747fSPaolo Bonzini case 0x09: 34549ab747fSPaolo Bonzini case 0x0a: 34649ab747fSPaolo Bonzini case 0x0b: 34749ab747fSPaolo Bonzini case 0x0c: 34849ab747fSPaolo Bonzini case 0x0d: 34949ab747fSPaolo Bonzini { 35049ab747fSPaolo Bonzini int offset; 35149ab747fSPaolo Bonzini if (!(GUSregb(GUS4cReset) & 0x01)) 35249ab747fSPaolo Bonzini break; /* reset flag active? */ 35349ab747fSPaolo Bonzini offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); 35449ab747fSPaolo Bonzini offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ 35549ab747fSPaolo Bonzini GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata); 35649ab747fSPaolo Bonzini } 35749ab747fSPaolo Bonzini break; 35849ab747fSPaolo Bonzini /* voice unspecific functions */ 35949ab747fSPaolo Bonzini case 0x0e: /* NumVoices */ 36049ab747fSPaolo Bonzini GUSregb(NumVoices) = (GUSbyte) data; 36149ab747fSPaolo Bonzini break; 36249ab747fSPaolo Bonzini /* case 0x0f: */ /* read only */ 36349ab747fSPaolo Bonzini /* common functions */ 36449ab747fSPaolo Bonzini case 0x41: /* DramDMAContrReg */ 36549ab747fSPaolo Bonzini GUSregb(GUS41DMACtrl) = (GUSbyte) data; 36649ab747fSPaolo Bonzini if (data & 0x01) 36749ab747fSPaolo Bonzini GUS_dmarequest(state); 36849ab747fSPaolo Bonzini break; 36949ab747fSPaolo Bonzini case 0x42: /* DramDMAmemPosReg */ 37049ab747fSPaolo Bonzini GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata; 37149ab747fSPaolo Bonzini GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */ 37249ab747fSPaolo Bonzini break; 37349ab747fSPaolo Bonzini case 0x43: /* DRAMaddrLo */ 37449ab747fSPaolo Bonzini GUSregd(GUSDRAMPOS24bit) = 37549ab747fSPaolo Bonzini (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata; 37649ab747fSPaolo Bonzini break; 37749ab747fSPaolo Bonzini case 0x44: /* DRAMaddrHi */ 37849ab747fSPaolo Bonzini GUSregd(GUSDRAMPOS24bit) = 37949ab747fSPaolo Bonzini (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16); 38049ab747fSPaolo Bonzini break; 38149ab747fSPaolo Bonzini case 0x45: /* TCtrlReg */ 38249ab747fSPaolo Bonzini GUSregb(GUS45TimerCtrl) = (GUSbyte) data; 38349ab747fSPaolo Bonzini if (!(data & 0x20)) 38449ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */ 38549ab747fSPaolo Bonzini if (!(data & 0x02)) 38649ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) &= 0xfe; /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */ 38749ab747fSPaolo Bonzini if (!(GUSregb(TimerStatus2x8) & 0x19)) 38849ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) &= 0xef; /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */ 38949ab747fSPaolo Bonzini /* catch up delayed timer IRQs: */ 39049ab747fSPaolo Bonzini if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3)) 39149ab747fSPaolo Bonzini { 39249ab747fSPaolo Bonzini if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ 39349ab747fSPaolo Bonzini { 39449ab747fSPaolo Bonzini if (!(GUSregb(TimerDataReg2x9) & 0x40)) 39549ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ 39649ab747fSPaolo Bonzini if (data & 4) /* timer1 irq enable */ 39749ab747fSPaolo Bonzini { 39849ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ 39949ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ 40049ab747fSPaolo Bonzini } 40149ab747fSPaolo Bonzini } 40249ab747fSPaolo Bonzini if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ 40349ab747fSPaolo Bonzini { 40449ab747fSPaolo Bonzini if (!(GUSregb(TimerDataReg2x9) & 0x20)) 40549ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ 40649ab747fSPaolo Bonzini if (data & 8) /* timer2 irq enable */ 40749ab747fSPaolo Bonzini { 40849ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ 40949ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ 41049ab747fSPaolo Bonzini } 41149ab747fSPaolo Bonzini } 41249ab747fSPaolo Bonzini GUSregw(TimerIRQs)--; 41349ab747fSPaolo Bonzini if (GUSregw(BusyTimerIRQs) > 1) 41449ab747fSPaolo Bonzini GUSregw(BusyTimerIRQs)--; 41549ab747fSPaolo Bonzini else 41649ab747fSPaolo Bonzini GUSregw(BusyTimerIRQs) = 41749ab747fSPaolo Bonzini GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs)); 41849ab747fSPaolo Bonzini } 41949ab747fSPaolo Bonzini else 42049ab747fSPaolo Bonzini GUSregw(TimerIRQs) = 0; 42149ab747fSPaolo Bonzini 42249ab747fSPaolo Bonzini if (!(data & 0x04)) 42349ab747fSPaolo Bonzini { 42449ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */ 42549ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) &= 0xfb; 42649ab747fSPaolo Bonzini } 42749ab747fSPaolo Bonzini if (!(data & 0x08)) 42849ab747fSPaolo Bonzini { 42949ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */ 43049ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) &= 0xf7; 43149ab747fSPaolo Bonzini } 43249ab747fSPaolo Bonzini if (!GUSregb(IRQStatReg2x6)) 43349ab747fSPaolo Bonzini GUS_irqclear(state, state->gusirq); 43449ab747fSPaolo Bonzini break; 43549ab747fSPaolo Bonzini case 0x46: /* Counter1 */ 43649ab747fSPaolo Bonzini GUSregb(GUS46Counter1) = (GUSbyte) data; 43749ab747fSPaolo Bonzini break; 43849ab747fSPaolo Bonzini case 0x47: /* Counter2 */ 43949ab747fSPaolo Bonzini GUSregb(GUS47Counter2) = (GUSbyte) data; 44049ab747fSPaolo Bonzini break; 44149ab747fSPaolo Bonzini /* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */ 44249ab747fSPaolo Bonzini case 0x49: /* SampCtrlReg */ 44349ab747fSPaolo Bonzini GUSregb(GUS49SampCtrl) = (GUSbyte) data; 44449ab747fSPaolo Bonzini break; 44549ab747fSPaolo Bonzini /* case 0x4b: */ /* joystick trim not emulated */ 44649ab747fSPaolo Bonzini case 0x4c: /* GUSreset */ 44749ab747fSPaolo Bonzini GUSregb(GUS4cReset) = (GUSbyte) data; 44849ab747fSPaolo Bonzini if (!(GUSregb(GUS4cReset) & 1)) /* reset... */ 44949ab747fSPaolo Bonzini { 45049ab747fSPaolo Bonzini GUSregd(voicewavetableirq) = 0; 45149ab747fSPaolo Bonzini GUSregd(voicevolrampirq) = 0; 45249ab747fSPaolo Bonzini GUSregw(TimerIRQs) = 0; 45349ab747fSPaolo Bonzini GUSregw(BusyTimerIRQs) = 0; 45449ab747fSPaolo Bonzini GUSregb(NumVoices) = 0xcd; 45549ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) = 0; 45649ab747fSPaolo Bonzini GUSregb(TimerStatus2x8) = 0; 45749ab747fSPaolo Bonzini GUSregb(AdLibData2x9) = 0; 45849ab747fSPaolo Bonzini GUSregb(TimerDataReg2x9) = 0; 45949ab747fSPaolo Bonzini GUSregb(GUS41DMACtrl) = 0; 46049ab747fSPaolo Bonzini GUSregb(GUS45TimerCtrl) = 0; 46149ab747fSPaolo Bonzini GUSregb(GUS49SampCtrl) = 0; 46249ab747fSPaolo Bonzini GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */ 46349ab747fSPaolo Bonzini GUS_irqclear(state, state->gusirq); 46449ab747fSPaolo Bonzini } 46549ab747fSPaolo Bonzini /* IRQ enable bit checked elsewhere */ 46649ab747fSPaolo Bonzini /* EnableDAC bit may be used by external callers */ 46749ab747fSPaolo Bonzini break; 46849ab747fSPaolo Bonzini } 46949ab747fSPaolo Bonzini } 47049ab747fSPaolo Bonzini break; 47149ab747fSPaolo Bonzini case 0x307: /* DRAMaccess */ 47249ab747fSPaolo Bonzini { 47349ab747fSPaolo Bonzini GUSbyte *adr; 47449ab747fSPaolo Bonzini adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); 47549ab747fSPaolo Bonzini *adr = (GUSbyte) data; 47649ab747fSPaolo Bonzini } 47749ab747fSPaolo Bonzini break; 47849ab747fSPaolo Bonzini } 47949ab747fSPaolo Bonzini } 48049ab747fSPaolo Bonzini 48149ab747fSPaolo Bonzini /* Attention when breaking up a single DMA transfer to multiple ones: 48249ab747fSPaolo Bonzini * it may lead to multiple terminal count interrupts and broken transfers: 48349ab747fSPaolo Bonzini * 48449ab747fSPaolo Bonzini * 1. Whenever you transfer a piece of data, the gusemu callback is invoked 48549ab747fSPaolo Bonzini * 2. The callback may generate a TC irq (if the register was set up to do so) 48649ab747fSPaolo Bonzini * 3. The irq may result in the program using the GUS to reprogram the GUS 48749ab747fSPaolo Bonzini * 48849ab747fSPaolo Bonzini * Some programs also decide to upload by just checking if TC occurs 48949ab747fSPaolo Bonzini * (via interrupt or a cleared GUS dma flag) 49049ab747fSPaolo Bonzini * and then start the next transfer, without checking DMA state 49149ab747fSPaolo Bonzini * 49249ab747fSPaolo Bonzini * Thus: Always make sure to set the TC flag correctly! 49349ab747fSPaolo Bonzini * 49449ab747fSPaolo Bonzini * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA 49549ab747fSPaolo Bonzini * while later cards had atomic granularity provided by an additional GUS50DMAHigh register 49649ab747fSPaolo Bonzini * GUSemu also uses this register to support byte-granular transfers for better compatibility 49749ab747fSPaolo Bonzini * with emulators other than GUSemu32 49849ab747fSPaolo Bonzini */ 49949ab747fSPaolo Bonzini 50049ab747fSPaolo Bonzini void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC) 50149ab747fSPaolo Bonzini { 50249ab747fSPaolo Bonzini /* this function gets called by the callback function as soon as a DMA transfer is about to start 50349ab747fSPaolo Bonzini * dma_addr is a translated address within accessible memory, not the physical one, 50449ab747fSPaolo Bonzini * count is (real dma count register)+1 50549ab747fSPaolo Bonzini * note that the amount of bytes transferred is fully determined by values in the DMA registers 50649ab747fSPaolo Bonzini * do not forget to update DMA states after transferring the entire block: 50749ab747fSPaolo Bonzini * DREQ cleared & TC asserted after the _whole_ transfer */ 50849ab747fSPaolo Bonzini 50949ab747fSPaolo Bonzini char *srcaddr; 51049ab747fSPaolo Bonzini char *destaddr; 51149ab747fSPaolo Bonzini char msbmask = 0; 51249ab747fSPaolo Bonzini GUSbyte *gusptr; 51349ab747fSPaolo Bonzini gusptr = state->gusdatapos; 51449ab747fSPaolo Bonzini 51549ab747fSPaolo Bonzini srcaddr = dma_addr; /* system memory address */ 51649ab747fSPaolo Bonzini { 51749ab747fSPaolo Bonzini int offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf); 51849ab747fSPaolo Bonzini if (state->gusdma >= 4) 51949ab747fSPaolo Bonzini offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */ 52049ab747fSPaolo Bonzini destaddr = (char *) state->himemaddr + offset; /* wavetable RAM address */ 52149ab747fSPaolo Bonzini } 52249ab747fSPaolo Bonzini 52349ab747fSPaolo Bonzini GUSregw(GUS42DMAStart) += (GUSword) (count >> 4); /* ToDo: add 16bit GUS page limit? */ 52449ab747fSPaolo Bonzini GUSregb(GUS50DMAHigh) = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */ 52549ab747fSPaolo Bonzini 52649ab747fSPaolo Bonzini if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */ 52749ab747fSPaolo Bonzini { 52849ab747fSPaolo Bonzini char *tmpaddr = destaddr; 52949ab747fSPaolo Bonzini destaddr = srcaddr; 53049ab747fSPaolo Bonzini srcaddr = tmpaddr; 53149ab747fSPaolo Bonzini } 53249ab747fSPaolo Bonzini 53349ab747fSPaolo Bonzini if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02))) 53449ab747fSPaolo Bonzini msbmask = (const char) 0x80; /* invert MSB */ 53549ab747fSPaolo Bonzini for (; count > 0; count--) 53649ab747fSPaolo Bonzini { 53749ab747fSPaolo Bonzini if (GUSregb(GUS41DMACtrl) & 0x40) 53849ab747fSPaolo Bonzini *(destaddr++) = *(srcaddr++); /* 16 bit lobyte */ 53949ab747fSPaolo Bonzini else 54049ab747fSPaolo Bonzini *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */ 54149ab747fSPaolo Bonzini if (state->gusdma >= 4) 54249ab747fSPaolo Bonzini *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */ 54349ab747fSPaolo Bonzini } 54449ab747fSPaolo Bonzini 54549ab747fSPaolo Bonzini if (TC) 54649ab747fSPaolo Bonzini { 54749ab747fSPaolo Bonzini (GUSregb(GUS41DMACtrl)) &= 0xfe; /* clear DMA request bit */ 54849ab747fSPaolo Bonzini if (GUSregb(GUS41DMACtrl) & 0x20) /* DMA terminal count IRQ */ 54949ab747fSPaolo Bonzini { 55049ab747fSPaolo Bonzini GUSregb(IRQStatReg2x6) |= 0x80; 55149ab747fSPaolo Bonzini GUS_irqrequest(state, state->gusirq, 1); 55249ab747fSPaolo Bonzini } 55349ab747fSPaolo Bonzini } 55449ab747fSPaolo Bonzini } 555