public AudioChannel()
 {
     AudioControl   = new AudioControlBits(0);
     FeedbackEnable = new ShiftRegisterFeedbackEnable(0);
 }
Beispiel #2
0
        public void Poke(ushort address, byte value)
        {
            // Timer addresses can be handled separately
            if (address >= Mikey.Addresses.HTIMBKUP && address <= Mikey.Addresses.TIM7CTLB)
            {
                int   offset = address - Mikey.Addresses.HTIMBKUP;
                int   index  = offset >> 2;              // Divide by 4 to get index of timer
                Timer timer  = Timers[index];

                switch (offset % 4)
                {
                case 0:                         // Backup value
                    timer.BackupValue = value;
                    return;

                case 1:                         // Static control
                    StaticControlBits control = new StaticControlBits(value);
                    timer.StaticControlBits = control;

                    // "It is set on time out, reset with the reset timer done bit (xxx1, B6)"
                    if (control.ResetTimerDone)
                    {
                        timer.DynamicControlBits.TimerDone = false;
                    }
                    if (control.EnableCount || control.ResetTimerDone)
                    {
                        timer.Start(device.SystemClock.CompatibleCycleCount);
                        ForceTimerUpdate();
                    }
                    return;

                case 2:                         // Current value
                    timer.CurrentValue = value;
                    ForceTimerUpdate();
                    return;

                case 3:                         // Dynamic control bits
                    timer.DynamicControlBits.ByteData = value;
                    return;
                }
            }

            if (address >= Mikey.Addresses.AUD0VOL && address <= Mikey.Addresses.AUD3MISC && SoundEnabled)
            {
                int          offset  = address - Mikey.Addresses.AUD0VOL;
                int          index   = offset >> 3;      // Divide by 8 to get index of audio channel
                AudioChannel channel = AudioChannels[index];

                // "FD20 -> FD27 Audio channel 0, links from timer 7"
                // "FD28 -> FD2F Audio channel 1, links from audio timer 0"
                // "FD30 -> FD37 Audio channel 2, links from audio timer 1"
                // "FD38 -> FD3F Audio channel 3, links trom audio timer 2"
                switch (offset % 8)
                {
                case 0:                         // "8 bit. 2's Complement Volume Control"
                    channel.VolumeControl = (sbyte)value;
                    return;

                case 1:                         // "Shift register feedback enable"
                    channel.FeedbackEnable.ByteData = value;
                    return;

                case 2:                         // "Audio output value"
                    channel.OutputValue = (sbyte)value;
                    return;

                case 3:                         // "Lower 8 Bits of Shift Register"
                    channel.LowerShiftRegister = value;
                    return;

                case 4:                         // "Audio Timer Backup Value"
                    channel.BackupValue = value;
                    return;

                case 5:                         // "Audio Control Bits"
                                                //channel.AudioControl.ByteData = value;
                    AudioControlBits control = new AudioControlBits(value);
                    channel.AudioControl = control;

                    // "It is set on time out, reset with the reset timer done bit (xxx1, B6)"
                    if (control.ResetTimerDone)
                    {
                        channel.DynamicControlBits.TimerDone = false;
                    }
                    if (control.EnableCount || control.ResetTimerDone)
                    {
                        channel.Start(device.SystemClock.CompatibleCycleCount);
                        ForceTimerUpdate();
                    }
                    return;

                case 6:                         // "Audio counter"
                    channel.CurrentValue = value;
                    ForceTimerUpdate();
                    return;

                case 7:                         // "Other control bits"
                    channel.OtherControlBits = value;
                    return;
                }
            }

            // Handle other addresses
            switch (address)
            {
            case Mikey.Addresses.MSTEREO:
                // "The Howard boards were not yet finished, so we went ahead and implemented this stereo on them.
                // This form of stereo was channel switching controlled by FD50."
                // TODO: Implement stereo
                return;

            case Mikey.Addresses.MPAN:
                // TODO: Implement panning
                return;

            case Mikey.Addresses.INTRST:
                // "Read is a poll, write will reset the int that corresponds to a set bit."
                value ^= 0xff;
                timerInterruptStatusRegister &= value;

                // When timer interrupt status register is zero, IRQ line goes back up (not-active)
                device.Cpu.SignalInterrupt(timerInterruptStatusRegister != 0 ? InterruptType.Irq : InterruptType.None);

                ForceTimerUpdate();
                return;

            case Mikey.Addresses.INTSET:
                // "Read is a poll, write will set the int that corresponds to a set bit."
                timerInterruptStatusRegister |= value;

                // When timer interrupt status register is zero, IRQ line goes back up (not-active)
                device.Cpu.SignalInterrupt(timerInterruptStatusRegister != 0 ? InterruptType.Irq : InterruptType.None);

                ForceTimerUpdate();
                return;

            case Mikey.Addresses.MIKEYSREV:
                // "No actual register is implemented"
                return;

            // "Also note that only the lines that are set to input are actually valid for reading."
            // "8 bits I/O direction corresponding to the 8 bits at FD8B 0=input, 1= output"
            case Mikey.Addresses.IODIR:
                IODIR.ByteData = value;
                return;

            // "Mikey Parallel Data(sort of a R/W) 8 bits of general purpose I/O data"
            case Mikey.Addresses.IODAT:
                IODAT.ByteData = value;

                // "One is that it is the data pin for the shifter that holds the cartridge address."
                device.Cartridge.CartAddressData(IODAT.CartAddressData);

                // "The other is that it controls power to the cartridge."
                device.CartridgePowerOn = !IODAT.CartPowerOff;

                // "In its current use, it is the write enable line for writeable elements in the cartridge."
                if (IODIR.AuxiliaryDigitalInOut == DataDirection.Output)
                {
                    device.Cartridge.AuxiliaryDigitalInOut = IODAT.AuxiliaryDigitalInOut;
                }

                // "In its current use, it is the write enable line for writeable elements in the cartridge."
                if (IODIR.AuxiliaryDigitalInOut == DataDirection.Output)
                {
                    device.Cartridge.WriteEnabled = IODAT.AuxiliaryDigitalInOut;
                }
                return;

            case Mikey.Addresses.SERCTL:
                ComLynx.SERCTL = value;
                return;

            case Mikey.Addresses.SERDAT:
                ComLynx.SERDAT = value;
                return;

            case Mikey.Addresses.SYSCTL1:
                SYSCTL1.ByteData = value;
                if (!SYSCTL1.Power)
                {
                    device.Reset();
                    // TODO: Enter debug mode if configured
                }
                device.Cartridge.CartAddressStrobe(SYSCTL1.CartAddressStrobe);
                return;

            case Mikey.Addresses.SDONEACK:
                // "Write a '00' to SDONEACK, allowing Mikey to respond to sleep commands."

                // "The Suzy Done Acknowledge address must be written to prior to running the sprite engine.
                // This is required even prior to the first time the sprite engine is activated.
                // It it is not written to in the appropriate sequences, the CPU will not go to sleep
                // when so requested. In addition, if some software accidentally allows a Suzy operation to
                // complete without then following that completion with a write to SDONEACK, the CPU
                // will not sleep. So if sprites stop working, something may have gone wrong with your
                // SDONEACK software."

                // TODO: Implement state for Suzy Done acknowledgement if necessary
                return;

            case Mikey.Addresses.CPUSLEEP:
                // "A write of '0' to this address will reset the CPU bus request flip flop.
                // The setting of the flip flop is described in the hardware specification."

                // BUG: "Sleep does not work if Suzy does not have the bus."
                // "We assume that everyone knows about this bug and behaves accordingly, so
                // writing zero here must be to give Suzy access to the bus and it will start drawing
                // sprites and will signal when it is done."
                // This is implemented as a new wakeup time by calculating the number of cycles used
                // and skipping forward in time to that moment.
                ulong suzyCycles = device.Suzy.RenderSprites();

                // TODO: For now use estimate of cycles for sprite drawing.
                // Needs to be replaced with a better calculation. See also Handy PaintSprites code.
                suzyCycles = 0;                        //1000;
                device.Cpu.TrySleep(suzyCycles);
                return;

            case Mikey.Addresses.DISPCTL:
                DISPCTL.ByteData = value;
                return;

            case Mikey.Addresses.PBKUP:
                // "Additionally, the magic 'P' counter has to be set to match the LCD scan rate. The formula is:
                // INT((((line time - .5us) / 15) * 4) -1)"
                PBKUP = value;
                return;

            case Mikey.Addresses.DISPADRL:
                // "DISPADRL (FD94) is lower 8 bits of display address with the bottom 2 bit ignored by the hardware.
                // The address of the upper left corner of a display buffer must always have '00' in the bottom 2 bits."
                VideoDisplayStartAddress.LowByte = (byte)(value & 0xFD);
                return;

            case Mikey.Addresses.DISPADRH:
                // "DISPADRH (FD95) is upper 8 bits of display address."
                VideoDisplayStartAddress.HighByte = value;
                return;

            case Mikey.Addresses.MAGRDY0:
            case Mikey.Addresses.MAGRDY1:
            case Mikey.Addresses.AUDIN:
            case Mikey.Addresses.MIKEYHREV:
                //Debug.WriteLineIf(GeneralSwitch.TraceWarning, String.Format("Mikey::Poke - Read-only address {0:X4} used (value {1:X2}).", address, value));
                break;

            default:
                break;
            }

            if (address > Mikey.Addresses.BLUEREDF)
            {
                return;
            }

            // Blue and red color map
            if (address >= Mikey.Addresses.BLUERED0 && address <= Mikey.Addresses.BLUEREDF)
            {
                int index = address - Mikey.Addresses.BLUERED0;
                BlueRedColorMap[index] = value;
                ArgbColorMap[index]   &= 0xFF00FF00;
                ArgbColorMap[index]   |= (uint)((value & 0xF0) << 16);              // Blue
                ArgbColorMap[index]   |= (uint)((value & 0x0F) << 4);               // Red
                return;
            }

            // Green color map
            if (address >= Mikey.Addresses.GREEN0)
            {
                int index = address - Mikey.Addresses.GREEN0;
                GreenColorMap[index] = value;
                ArgbColorMap[index] &= 0xFFFF00FF;
                ArgbColorMap[index] |= (uint)((value & 0x0F) << 12);
                return;
            }

            if (address >= 0xFD20 && address <= 0xFD3F)
            {
                return;
            }

            //Trace.WriteLineIf(GeneralSwitch.TraceWarning, String.Format("Mikey::Poke: Unknown address ${0:X4} specified (value={1:X2}).", address,value));
        }