Example #1
0
        public void FrameAdvance(bool render, bool rendersound)
        {
            _lagged      = true;
            DriveLightOn = false;
            Frame++;
            CheckSpriteLimit();
            PSG.BeginFrame(Cpu.TotalExecutedCycles);

            Cpu.Debug = Tracer.Enabled;

            if (SuperGrafx)
            {
                VPC.ExecFrame(render);
            }
            else
            {
                VDC1.ExecFrame(render);
            }

            PSG.EndFrame(Cpu.TotalExecutedCycles);

            if (_lagged)
            {
                _lagCount++;
                _isLag = true;
            }
            else
            {
                _isLag = false;
            }
        }
Example #2
0
        private void WritePort(ushort port, byte value)
        {
            port &= 0xFF;

            if (port >= 0xA0 && port <= 0xBF)
            {
                if ((port & 1) == 0)
                {
                    VDP.WriteVdpData(value);
                }
                else
                {
                    VDP.WriteVdpControl(value);
                }
                return;
            }

            if (port >= 0x80 && port <= 0x9F)
            {
                InputPortSelection = InputPortMode.Right;
                return;
            }

            if (port >= 0xC0 && port <= 0xDF)
            {
                InputPortSelection = InputPortMode.Left;
                return;
            }

            if (port >= 0xE0)
            {
                PSG.WritePsgData(value, Cpu.TotalExecutedCycles);
                return;
            }
        }
Example #3
0
        private void SyncState(Serializer ser)
        {
            byte[] core = null;
            if (ser.IsWriter)
            {
                var ms = new MemoryStream();
                ms.Close();
                core = ms.ToArray();
            }
            _cpu.SyncState(ser);

            ser.BeginSection("Coleco");
            _vdp.SyncState(ser);
            PSG.SyncState(ser);
            ser.Sync("RAM", ref _ram, false);
            ser.Sync("Frame", ref _frame);
            ser.Sync("LagCount", ref _lagCount);
            ser.Sync("IsLag", ref _isLag);
            ser.EndSection();

            if (ser.IsReader)
            {
                SyncAllByteArrayDomains();
            }
        }
Example #4
0
        void WriteMemorySGX(int addr, byte value)
        {
            if (addr >= 0x1F0000 && addr < 0x1F8000) // write RAM.
                Ram[addr & 0x7FFF] = value;

            else if (addr >= 0x1FE000) // hardware page.
            {
                if (addr < 0x1FE400)
                {
                    addr &= 0x1F;
                    if (addr <= 0x07) VDC1.WriteVDC(addr, value);
                    else if (addr <= 0x0F) VPC.WriteVPC(addr, value);
                    else if (addr <= 0x17) VDC2.WriteVDC(addr, value);
                }
                else if (addr < 0x1FE800) { Cpu.PendingCycles--; VCE.WriteVCE(addr, value); }
                else if (addr < 0x1FEC00) { IOBuffer = value; PSG.WritePSG((byte)addr, value, Cpu.TotalExecutedCycles); }
                else if (addr == 0x1FEC00) { IOBuffer = value; Cpu.WriteTimer(value); }
                else if (addr == 0x1FEC01) { IOBuffer = value; Cpu.WriteTimerEnable(value); }
                else if (addr >= 0x1FF000 &&
                         addr < 0x1FF400) { IOBuffer = value; WriteInput(value); }
                else if (addr == 0x1FF402) { IOBuffer = value; Cpu.WriteIrqControl(value); }
                else if (addr == 0x1FF403) { IOBuffer = value; Cpu.WriteIrqStatus(); }
                else Log.Error("MEM", "unhandled hardware write [{0:X6}] : {1:X2}", addr, value);
            }
            else
                Log.Error("MEM", "UNHANDLED WRITE: {0:X6}:{1:X2}", addr, value);
        }
        public void FrameAdvance(IController controller, bool render, bool renderSound)
        {
            _controller = controller;
            _cpu.Debug  = _tracer.Enabled;
            _frame++;
            _isLag = true;
            PSG.BeginFrame(_cpu.TotalExecutedCycles);

            if (_cpu.Debug && _cpu.Logger == null)             // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
            {
                _cpu.Logger = (s) => _tracer.Put(s);
            }

            byte tempRet1 = ControllerDeck.ReadPort1(controller, true, true);
            byte tempRet2 = ControllerDeck.ReadPort2(controller, true, true);

            bool intPending = (!tempRet1.Bit(4)) | (!tempRet2.Bit(4));

            _vdp.ExecuteFrame(intPending);

            PSG.EndFrame(_cpu.TotalExecutedCycles);

            if (_isLag)
            {
                _lagCount++;
            }
        }
Example #6
0
        public void FrameAdvance(bool render, bool renderSound)
        {
            Cpu.Debug = Tracer.Enabled;
            Frame++;
            _isLag = true;
            PSG.BeginFrame(Cpu.TotalExecutedCycles);

            if (Cpu.Debug && Cpu.Logger == null)             // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
            {
                Cpu.Logger = (s) => Tracer.Put(s);
            }

            byte temp_ret1 = ControllerDeck.ReadPort1(Controller, true, true);
            byte temp_ret2 = ControllerDeck.ReadPort2(Controller, true, true);

            bool Int_pending = (!temp_ret1.Bit(4)) | (!temp_ret2.Bit(4));

            VDP.ExecuteFrame(Int_pending);

            PSG.EndFrame(Cpu.TotalExecutedCycles);

            if (_isLag)
            {
                _lagCount++;
            }
        }
Example #7
0
        /// <summary>
        /// Writes to Port C
        /// </summary>
        private void OUTPortC(int data)
        {
            // latch the data
            Regs[PORT_C] = (byte)data;

            if (DirPortCL == PortDirection.Output)
            {
                // lower Port C bits OUT
                // keyboard line update
                Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
            }

            if (DirPortCU == PortDirection.Output)
            {
                // upper Port C bits OUT
                // write to PSG using latched data
                PSG.SetFunction(data);
                PSG.PortWrite(Regs[PORT_A]);

                // cassete write data
                //not implemeted

                // cas motor control
                Tape.TapeMotor = Regs[PORT_C].Bit(4);
            }
        }
Example #8
0
        public bool FrameAdvance(IController controller, bool render, bool renderSound)
        {
            _controller  = controller;
            _lagged      = true;
            DriveLightOn = false;
            CheckSpriteLimit();
            PSG.BeginFrame(Cpu.TotalExecutedCycles);

            Cpu.Debug = Tracer.IsEnabled();

            if (SuperGrafx)
            {
                VPC.ExecFrame(render);
            }
            else
            {
                VDC1.ExecFrame(render);
            }

            PSG.EndFrame(Cpu.TotalExecutedCycles);

            if (_lagged)
            {
                _lagCount++;
                _isLag = true;
            }
            else
            {
                _isLag = false;
            }

            Frame++;

            return(true);
        }
Example #9
0
        public void PSG_reset(PSG psg)
        {
            int i;

            psg.base_count = 0;

            for (i = 0; i < 3; i++)
            {
                psg.cout[i]   = 0;
                psg.count[i]  = 0x1000;
                psg.freq[i]   = 0;
                psg.edge[i]   = 0;
                psg.volume[i] = 0;
            }

            psg.mask = 0;

            for (i = 0; i < 16; i++)
            {
                psg.reg[i] = 0;
            }
            psg.adr = 0;

            psg.noise_seed  = 0xffff;
            psg.noise_count = 0x40;
            psg.noise_freq  = 0;

            psg.env_volume = 0;
            psg.env_ptr    = 0;
            psg.env_freq   = 0;
            psg.env_count  = 0;
            psg.env_pause  = 1;

            psg._out = 0;
        }
Example #10
0
        private void SyncState(Serializer ser)
        {
            byte[] core = null;
            if (ser.IsWriter)
            {
                var ms = new MemoryStream();
                ms.Close();
                core = ms.ToArray();
            }
            Cpu.SyncState(ser);

            ser.BeginSection("SMS");
            Vdp.SyncState(ser);
            PSG.SyncState(ser);
            ser.Sync("RAM", ref SystemRam, false);
            ser.Sync("RomBank0", ref RomBank0);
            ser.Sync("RomBank1", ref RomBank1);
            ser.Sync("RomBank2", ref RomBank2);
            ser.Sync("RomBank3", ref RomBank3);
            ser.Sync("Port01", ref Port01);
            ser.Sync("Port02", ref Port02);
            ser.Sync("Port3E", ref Port3E);
            ser.Sync("Port3F", ref Port3F);
            ser.Sync("Paddle1High", ref Paddle1High);
            ser.Sync("Paddle2High", ref Paddle2High);
            ser.Sync("LatchLightPhaser", ref LatchLightPhaser);

            if (SaveRAM != null)
            {
                ser.Sync("SaveRAM", ref SaveRAM, false);
                ser.Sync("SaveRamBank", ref SaveRamBank);
            }

            if (ExtRam != null)
            {
                ser.Sync("ExtRAM", ref ExtRam, true);
            }

            if (HasYM2413)
            {
                YM2413.SyncState(ser);
            }

            if (EEPROM != null)
            {
                EEPROM.SyncState(ser);
            }

            ser.Sync("Frame", ref _frame);
            ser.Sync("LagCount", ref _lagCount);
            ser.Sync("IsLag", ref _isLag);

            ser.EndSection();

            if (ser.IsReader)
            {
                SyncAllByteArrayDomains();
            }
        }
Example #11
0
        /// <summary>
        /// Writes to Port A
        /// </summary>
        private void OUTPortA(int data)
        {
            // latch the data
            Regs[PORT_A] = (byte)data;

            if (DirPortA == PortDirection.Output)
            {
                // PSG write
                PSG.PortWrite(data);
            }
        }
Example #12
0
 public void PSG_writeIO(PSG psg, UInt32 adr, UInt32 val)
 {
     if ((adr & 1) != 0)
     {
         PSG_writeReg(psg, psg.adr, val);
     }
     else
     {
         psg.adr = val & 0x1f;
     }
 }
Example #13
0
        public UInt32 PSG_toggleMask(PSG psg, UInt32 mask)
        {
            UInt32 ret = 0;

            if (psg != null)
            {
                ret       = psg.mask;
                psg.mask ^= mask;
            }
            return(ret);
        }
Example #14
0
 private void SyncState(Serializer ser)
 {
     ser.BeginSection("Coleco");
     Cpu.SyncState(ser);
     VDP.SyncState(ser);
     PSG.SyncState(ser);
     ser.Sync("RAM", ref Ram, false);
     ser.Sync("Frame", ref frame);
     ser.Sync("LagCount", ref _lagCount);
     ser.Sync("IsLag", ref _isLag);
     ser.EndSection();
 }
Example #15
0
        public bool FrameAdvance(IController controller, bool render, bool rendersound)
        {
            _controller = controller;
            _lagged     = true;
            _frame++;
            PSG.BeginFrame(Cpu.TotalExecutedCycles);

            if (!IsGameGear)
            {
                PSG.StereoPanning = Settings.ForceStereoSeparation ? ForceStereoByte : (byte)0xFF;
            }

            if (Tracer.Enabled)
            {
                Cpu.TraceCallback = s => Tracer.Put(s);
            }
            else
            {
                Cpu.TraceCallback = null;
            }

            if (IsGameGear_C == false)
            {
                Cpu.NonMaskableInterrupt = controller.IsPressed("Pause");
            }
            else if (!IsGameGear && IsGameGear_C)
            {
                Cpu.NonMaskableInterrupt = controller.IsPressed("P1 Start");
            }

            if (IsGame3D && Settings.Fix3D)
            {
                Vdp.ExecFrame((Frame & 1) == 0);
            }
            else
            {
                Vdp.ExecFrame(render);
            }

            PSG.EndFrame(Cpu.TotalExecutedCycles);
            if (_lagged)
            {
                _lagCount++;
                _isLag = true;
            }
            else
            {
                _isLag = false;
            }

            return(true);
        }
Example #16
0
        /// <summary>
        /// Writes to the control register
        /// </summary>
        /// <param name="data"></param>
        private void OUTControl(int data)
        {
            if (data.Bit(7))
            {
                // update configuration
                Regs[PORT_CONTROL] = (byte)data;

                // Writing to PIO Control Register (with Bit7 set), automatically resets PIO Ports A,B,C to 00h each
                Regs[PORT_A] = 0;
                Regs[PORT_B] = 0;
                Regs[PORT_C] = 0;
            }
            else
            {
                // register is used to set/reset a single bit in Port C
                bool isSet = data.Bit(0);

                // get the bit in PortC that we wish to change
                var bit = (data >> 1) & 7;

                // modify this bit
                if (isSet)
                {
                    Regs[PORT_C] = (byte)(Regs[PORT_C] | (bit * bit));
                }
                else
                {
                    Regs[PORT_C] = (byte)(Regs[PORT_C] & ~(bit * bit));
                }

                // any other ouput business
                if (DirPortCL == PortDirection.Output)
                {
                    // update keyboard line
                    Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
                }

                if (DirPortCU == PortDirection.Output)
                {
                    // write to PSG using latched data
                    PSG.SetFunction(data);
                    PSG.PortWrite(Regs[PORT_A]);

                    // cassete write data
                    //not implemeted

                    // cas motor control
                    Tape.TapeMotor = Regs[PORT_C].Bit(4);
                }
            }
        }
Example #17
0
        public void WriteMemoryZ80(ushort address, byte value)
        {
            if (address < 0x4000)
            {
                //Console.WriteLine("write z80 memory {0:X4}: {1:X2}",address, value);
                Z80Ram[address & 0x1FFF] = value;
                return;
            }
            if (address >= 0x4000 && address < 0x6000)
            {
                //Console.WriteLine(" === Z80 WRITES YM2612 {0:X4}:{1:X2} ===",address, value);
                YM2612.Write(address & 3, value, SoundCPU.TotalExecutedCycles);
                return;
            }
            if (address < 0x6100)
            {
                BankRegion >>= 1;
                BankRegion  |= (value & 1) << 23;
                BankRegion  &= 0x00FF8000;
                //Console.WriteLine("Bank pointing at {0:X8}",BankRegion);
                return;
            }
            if (address >= 0x7F00 && address < 0x7F20)
            {
                switch (address & 0x1F)
                {
                case 0x00:
                case 0x02:
                    VDP.WriteVdpData((ushort)((value << 8) | value));
                    return;

                case 0x04:
                case 0x06:
                    VDP.WriteVdpControl((ushort)((value << 8) | value));
                    return;

                case 0x11:
                case 0x13:
                case 0x15:
                case 0x17:
                    PSG.WritePsgData(value, SoundCPU.TotalExecutedCycles);
                    return;
                }
            }
            if (address >= 0x8000)
            {
                WriteByte(BankRegion | (address & 0x7FFF), (sbyte)value);
                return;
            }
            Console.WriteLine("UNHANDLED Z80 WRITE {0:X4}:{1:X2}", address, value);
        }
Example #18
0
 /// <summary>
 /// Reads from Port A
 /// </summary>
 /// <returns></returns>
 private int INPortA()
 {
     if (DirPortA == PortDirection.Input)
     {
         // read from PSG
         return(PSG.PortRead());
     }
     else
     {
         // Port A is set to output
         // return latched value
         return(Regs[PORT_A]);
     }
 }
Example #19
0
 private void internal_refresh(PSG psg)
 {
     if (psg.quality != 0)
     {
         psg.base_incr = 1 << GETA_BITS;
         psg.realstep  = (UInt32)((1 << 31) / psg.rate);
         psg.psgstep   = (UInt32)((1 << 31) / (psg.clk / 16));
         psg.psgtime   = 0;
     }
     else
     {
         psg.base_incr =
             (UInt32)((double)psg.clk * (1 << GETA_BITS) / (16 * psg.rate));
     }
 }
Example #20
0
        public void PSG_setVolumeMode(PSG psg, Int32 type)
        {
            switch (type)
            {
            case 1:
                psg.voltbl = voltbl[EMU2149_VOL_YM2149];
                break;

            case 2:
                psg.voltbl = voltbl[EMU2149_VOL_AY_3_8910];
                break;

            default:
                psg.voltbl = voltbl[EMU2149_VOL_DEFAULT];
                break;
            }
        }
        private void SyncState(Serializer ser)
        {
            ser.BeginSection("Coleco");
            _cpu.SyncState(ser);
            _vdp.SyncState(ser);
            PSG.SyncState(ser);
            ser.Sync("RAM", ref _ram, false);
            ser.Sync("Frame", ref _frame);
            ser.Sync("LagCount", ref _lagCount);
            ser.Sync("IsLag", ref _isLag);
            ser.EndSection();

            if (ser.IsReader)
            {
                SyncAllByteArrayDomains();
            }
        }
Example #22
0
        public PSG PSG_new(UInt32 c, UInt32 r)
        {
            PSG psg;

            psg = new PSG();
            if (psg == null)
            {
                return(null);
            }

            PSG_setVolumeMode(psg, EMU2149_VOL_DEFAULT);
            psg.clk  = c;
            psg.rate = r != 0 ? r : 44100;
            PSG_set_quality(psg, 0);

            return(psg);
        }
        private void SyncState(Serializer ser)
        {
            ser.BeginSection("SMS");
            Cpu.SyncState(ser);
            Vdp.SyncState(ser);
            PSG.SyncState(ser);
            ser.Sync("RAM", ref SystemRam, false);
            ser.Sync("RomBank0", ref RomBank0);
            ser.Sync("RomBank1", ref RomBank1);
            ser.Sync("RomBank2", ref RomBank2);
            ser.Sync("RomBank3", ref RomBank3);
            ser.Sync("Port01", ref Port01);
            ser.Sync("Port02", ref Port02);
            ser.Sync("Port3E", ref Port3E);
            ser.Sync("Port3F", ref Port3F);

            if (SaveRAM != null)
            {
                ser.Sync("SaveRAM", ref SaveRAM, false);
                ser.Sync("SaveRamBank", ref SaveRamBank);
            }

            if (ExtRam != null)
            {
                ser.Sync("ExtRAM", ref ExtRam, true);
            }

            if (HasYM2413)
            {
                YM2413.SyncState(ser);
            }

            ser.Sync("Frame", ref frame);
            ser.Sync("LagCount", ref _lagCount);
            ser.Sync("IsLag", ref _isLag);

            ser.EndSection();

            if (ser.IsReader)
            {
                SyncAllByteArrayDomains();
            }
        }
Example #24
0
        public Int16 PSG_calc(PSG psg)
        {
            if (psg.quality == 0)
            {
                return((Int16)(calc(psg) << 4));
            }

            /* Simple rate converter */
            while (psg.realstep > psg.psgtime)
            {
                psg.psgtime += psg.psgstep;
                psg._out    += calc(psg);
                psg._out   >>= 1;
            }

            psg.psgtime = psg.psgtime - psg.realstep;

            return((Int16)(psg._out << 4));
        }
Example #25
0
        public void FrameAdvance(IController controller, bool render, bool rendersound)
        {
            _controller = controller;
            _lagged     = true;
            _frame++;
            PSG.BeginFrame(Cpu.TotalExecutedCycles);
            Cpu.Debug = Tracer.Enabled;
            if (!IsGameGear)
            {
                PSG.StereoPanning = Settings.ForceStereoSeparation ? ForceStereoByte : (byte)0xFF;
            }

            if (Cpu.Debug && Cpu.Logger == null)             // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
            {
                Cpu.Logger = s => Tracer.Put(s);
            }

            if (IsGameGear == false)
            {
                Cpu.NonMaskableInterrupt = controller.IsPressed("Pause");
            }

            if (IsGame3D && Settings.Fix3D)
            {
                Vdp.ExecFrame((Frame & 1) == 0);
            }
            else
            {
                Vdp.ExecFrame(render);
            }

            PSG.EndFrame(Cpu.TotalExecutedCycles);
            if (_lagged)
            {
                _lagCount++;
                _isLag = true;
            }
            else
            {
                _isLag = false;
            }
        }
Example #26
0
        public void FrameAdvance(bool render, bool renderSound)
        {
            Cpu.Debug = Tracer.Enabled;
            Frame++;
            _isLag = true;
            PSG.BeginFrame(Cpu.TotalExecutedCycles);

            if (Cpu.Debug && Cpu.Logger == null)             // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
            {
                Cpu.Logger = (s) => Tracer.Put(s);
            }

            VDP.ExecuteFrame();
            PSG.EndFrame(Cpu.TotalExecutedCycles);

            if (_isLag)
            {
                _lagCount++;
            }
        }
Example #27
0
        public void FrameAdvance(IController controller, bool render, bool renderSound)
        {
            _controller = controller;

            // NOTE: Need to research differences between reset and power cycle
            if (_controller.IsPressed("Power"))
            {
                HardReset();
            }

            if (_controller.IsPressed("Reset"))
            {
                SoftReset();
            }

            _frame++;
            _isLag = true;
            PSG.BeginFrame(_cpu.TotalExecutedCycles);

            if (_tracer.Enabled)
            {
                _cpu.TraceCallback = s => _tracer.Put(s);
            }
            else
            {
                _cpu.TraceCallback = null;
            }
            byte tempRet1 = ControllerDeck.ReadPort1(controller, true, true);
            byte tempRet2 = ControllerDeck.ReadPort2(controller, true, true);

            bool intPending = (!tempRet1.Bit(4)) | (!tempRet2.Bit(4));

            _vdp.ExecuteFrame(intPending);

            PSG.EndFrame(_cpu.TotalExecutedCycles);

            if (_isLag)
            {
                _lagCount++;
            }
        }
Example #28
0
        public void FrameAdvance(IController controller, bool render, bool renderSound)
        {
            _controller = controller;

            // NOTE: Need to research differences between reset and power cycle
            if (_controller.IsPressed("Power"))
            {
                HardReset();
            }

            if (_controller.IsPressed("Reset"))
            {
                SoftReset();
            }

            _cpu.Debug = _tracer.Enabled;
            _frame++;
            _isLag = true;
            PSG.BeginFrame(_cpu.TotalExecutedCycles);

            if (_cpu.Debug && _cpu.Logger == null)             // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
            {
                _cpu.Logger = (s) => _tracer.Put(s);
            }

            byte tempRet1 = ControllerDeck.ReadPort1(controller, true, true);
            byte tempRet2 = ControllerDeck.ReadPort2(controller, true, true);

            bool intPending = (!tempRet1.Bit(4)) | (!tempRet2.Bit(4));

            _vdp.ExecuteFrame(intPending);

            PSG.EndFrame(_cpu.TotalExecutedCycles);

            if (_isLag)
            {
                _lagCount++;
            }
        }
Example #29
0
        private void SyncState(Serializer ser)
        {
            byte[] core = null;
            if (ser.IsWriter)
            {
                var ms = new MemoryStream();
                ms.Close();
                core = ms.ToArray();
            }
            _cpu.SyncState(ser);

            ser.BeginSection("Coleco");
            _vdp.SyncState(ser);
            ControllerDeck.SyncState(ser);
            PSG.SyncState(ser);
            SGM_sound.SyncState(ser);
            ser.Sync("UseSGM", ref use_SGM);
            ser.Sync("is_MC", ref is_MC);
            ser.Sync("MC_bank", ref MC_bank);
            ser.Sync("EnableSGMhigh", ref enable_SGM_high);
            ser.Sync("EnableSGMlow", ref enable_SGM_low);
            ser.Sync("Port_0x53", ref port_0x53);
            ser.Sync("Port_0x7F", ref port_0x7F);
            ser.Sync("RAM", ref _ram, false);
            ser.Sync("SGM_high_RAM", ref SGM_high_RAM, false);
            ser.Sync("SGM_low_RAM", ref SGM_low_RAM, false);
            ser.Sync("Frame", ref _frame);
            ser.Sync("LagCount", ref _lagCount);
            ser.Sync("IsLag", ref _isLag);

            ser.EndSection();

            if (ser.IsReader)
            {
                SyncAllByteArrayDomains();
            }
        }
        public bool FrameAdvance(IController controller, bool render, bool renderSound)
        {
            _controller = controller;

            // NOTE: Need to research differences between reset and power cycle
            if (_controller.IsPressed("Power"))
            {
                HardReset();
            }

            if (_controller.IsPressed("Reset"))
            {
                SoftReset();
            }

            _frame++;

            _isLag = true;
            if (_tracer.Enabled)
            {
                _cpu.TraceCallback = s => _tracer.Put(s);
            }
            else
            {
                _cpu.TraceCallback = null;
            }
            byte tempRet1 = ControllerDeck.ReadPort1(controller, true, false);
            byte tempRet2 = ControllerDeck.ReadPort2(controller, true, false);

            bool intPending = false;

            // the return values represent the controller's current state, but the sampling rate is not high enough
            // to catch all changes in wheel orientation
            // so we use the wheel variable and interpolate between frames

            // first determine how many degrees the wheels changed, and how many regions have been traversed
            float change1 = (float)(((ControllerDeck.wheel1 - ControllerDeck.temp_wheel1) % 180) / 1.25);
            float change2 = (float)(((ControllerDeck.wheel2 - ControllerDeck.temp_wheel2) % 180) / 1.25);

            // special cases
            if ((ControllerDeck.temp_wheel1 > 270) && (ControllerDeck.wheel1 < 90))
            {
                change1 = (float)((ControllerDeck.wheel1 + (360 - ControllerDeck.temp_wheel1)) / 1.25);
            }

            if ((ControllerDeck.wheel1 > 270) && (ControllerDeck.temp_wheel1 < 90))
            {
                change1 = -(float)((ControllerDeck.temp_wheel1 + (360 - ControllerDeck.wheel1)) / 1.25);
            }

            if ((ControllerDeck.temp_wheel2 > 270) && (ControllerDeck.wheel2 < 90))
            {
                change2 = (float)((ControllerDeck.wheel2 + (360 - ControllerDeck.temp_wheel2)) / 1.25);
            }

            if ((ControllerDeck.wheel2 > 270) && (ControllerDeck.temp_wheel2 < 90))
            {
                change2 = -(float)((ControllerDeck.temp_wheel2 + (360 - ControllerDeck.wheel2)) / 1.25);
            }

            int changes1 = change1 > 0 ? (int)Math.Floor(change1) : (int)Math.Ceiling(change1);
            int changes2 = change2 > 0 ? (int)Math.Floor(change2) : (int)Math.Ceiling(change2);

            for (int scanLine = 0; scanLine < 262; scanLine++)
            {
                _vdp.RenderScanline(scanLine);

                if (scanLine == 192)
                {
                    _vdp.InterruptPending = true;

                    if (_vdp.EnableInterrupts)
                    {
                        _cpu.NonMaskableInterrupt = true;
                    }
                }

                for (int i = 0; i < 228; i++)
                {
                    PSG.generate_sound(1);
                    if (use_SGM)
                    {
                        SGM_sound.generate_sound(1);
                    }
                    _cpu.ExecuteOne();

                    // pick out sound samples from the sound devices twice per scanline
                    int v = PSG.Sample();

                    if (use_SGM)
                    {
                        v += SGM_sound.Sample();
                    }

                    if (v != _latchedSample)
                    {
                        _blip.AddDelta((uint)_sampleClock, v - _latchedSample);
                        _latchedSample = v;
                    }

                    _sampleClock++;
                }

                // starting from scanline 20, changes to the wheel are added once per scanline (up to 144)
                if (scanLine > 20)
                {
                    if (changes1 != 0)
                    {
                        if (changes1 > 0)
                        {
                            ControllerDeck.temp_wheel1 = (float)((ControllerDeck.temp_wheel1 + 1.25) % 360);
                            changes1--;
                        }
                        else
                        {
                            ControllerDeck.temp_wheel1 = (float)((ControllerDeck.temp_wheel1 - 1.25) % 360);
                            changes1++;
                        }
                    }

                    if (changes2 != 0)
                    {
                        if (changes2 > 0)
                        {
                            ControllerDeck.temp_wheel2 = (float)((ControllerDeck.temp_wheel2 + 1.25) % 360);
                            changes2--;
                        }
                        else
                        {
                            ControllerDeck.temp_wheel2 = (float)((ControllerDeck.temp_wheel2 - 1.25) % 360);
                            changes2++;
                        }
                    }
                }

                tempRet1 = ControllerDeck.ReadPort1(controller, true, true);
                tempRet2 = ControllerDeck.ReadPort2(controller, true, true);

                intPending = (!tempRet1.Bit(4) && temp_1_prev) | (!tempRet2.Bit(4) && temp_2_prev);

                _cpu.FlagI = false;
                if (intPending)
                {
                    _cpu.FlagI = true;
                    intPending = false;
                }

                temp_1_prev = tempRet1.Bit(4);
                temp_2_prev = tempRet2.Bit(4);
            }

            ControllerDeck.temp_wheel1 = ControllerDeck.wheel1;
            ControllerDeck.temp_wheel2 = ControllerDeck.wheel2;

            if (_isLag)
            {
                _lagCount++;
            }

            return(true);
        }