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