<commit_before>//
// Copyright (c) 2017 The nanoFramework project contributors
// Portions Copyright (c) Microsoft Corporation.  All rights reserved.
// See LICENSE file in the project root for full license information.
//

#include <ch.h>
#include <hal.h>
#include <cmsis_os.h>

#include <targetPAL.h>
#include "win_dev_gpio_native.h"
#include "nf_rt_events_native.h"

///////////////////////////////////////////////////////////////////////////////////////
// !!! KEEP IN SYNC WITH Windows.Devices.Gpio.GpioPinDriveMode (in managed code) !!! //
///////////////////////////////////////////////////////////////////////////////////////

enum GpioPinDriveMode
{
    GpioPinDriveMode_Input = 0,
    GpioPinDriveMode_InputPullDown,
    GpioPinDriveMode_InputPullUp,
    GpioPinDriveMode_Output,
    GpioPinDriveMode_OutputOpenDrain,
    GpioPinDriveMode_OutputOpenDrainPullUp,
    GpioPinDriveMode_OutputOpenSource,
    GpioPinDriveMode_OutputOpenSourcePullDown
};

///////////////////////////////////////////////////////////////////////////////////
// !!! KEEP IN SYNC WITH Windows.Devices.Gpio.GpioPinValue (in managed code) !!! //
///////////////////////////////////////////////////////////////////////////////////

enum GpioPinValue
{
    GpioPinValue_Low = 0,
    GpioPinValue_High,
};

///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////

// this timer is used to handle the debounce time
static virtual_timer_t debounceTimer;

volatile uint16_t lastPadValue;

// this is an utility function to get a ChibiOS PAL IoLine from our "encoded" pin number
ioline_t GetIoLine(int16_t pinNumber)
{
    stm32_gpio_t* port  = GPIO_PORT(pinNumber);
    int16_t pad = pinNumber % 16;

    return PAL_LINE(port, pad);

}

static void debounceTimer_Callback( void* arg )
{
    CLR_RT_HeapBlock*  pThis = (CLR_RT_HeapBlock*)arg;

    if(pThis == NULL)
    {
        // no Gpio pin here, leave now
        chSysUnlockFromISR();
        return;
    }

    // check if object has been disposed
    if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
    {
        // object has been disposed, leave now
        chSysUnlockFromISR();
        return;
    }

    // get pin number
    int16_t pinNumber = pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___pinNumber ].NumericByRefConst().s4;

    // read line
    uint16_t currentValue = palReadLine(GetIoLine(pinNumber));

    if(lastPadValue == currentValue)
    {
        // value hasn't change for debounce interval so this is a valid change
        // post a managed event with the current pin value
        PostManagedEvent( EVENT_GPIO, 0, pinNumber, currentValue );
    }
}

static void GpioEventCallback(void *arg)
{
    CLR_RT_HeapBlock*  pThis = (CLR_RT_HeapBlock*)arg;

    NATIVE_INTERRUPT_START

    chSysLockFromISR();

    //CLR_RT_HeapBlock*  pThis = channelPinMapping[channel];

    if(pThis == NULL)
    {
        // no Gpio pin here, leave now
        chSysUnlockFromISR();
        return;
    }

    // check if object has been disposed
    if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
    {
        // object has been disposed, leave now
        chSysUnlockFromISR();

        NATIVE_INTERRUPT_END

        return;
    }

    // get pin number and take the port and pad references from that one
    int16_t pinNumber = pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___pinNumber ].NumericByRefConst().s4;

    // get IoLine from pin number
    ioline_t ioLine = GetIoLine(pinNumber);

    // check if there is a debounce time set
    int64_t debounceTimeoutMilsec = (CLR_INT64_TEMP_CAST) pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___debounceTimeout ].NumericByRefConst().s8 / TIME_CONVERSION__TO_MILLISECONDS;

    if(debounceTimeoutMilsec > 0)
    {
        // debounce set, need to handle it

        if(chVTIsArmed(&debounceTimer))
        {
            // there is a debounce timer already running so this change in pin value should be discarded
            chSysUnlockFromISR();

            NATIVE_INTERRUPT_END

            return;
        }

        // read pad
        lastPadValue = palReadLine(ioLine);

        // setup timer
        chVTSetI(&debounceTimer, TIME_MS2I(debounceTimeoutMilsec), debounceTimer_Callback, pThis);
    }
    else
    {
        // post a managed event with the current pin reading
        PostManagedEvent( EVENT_GPIO, 0, pinNumber, palReadLine(ioLine) );
    }

    chSysUnlockFromISR();

    NATIVE_INTERRUPT_END

}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::Read___WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        // check if object has been disposed
        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        stack.SetResult_I4( GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4) );
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::DisposeNative___VOID( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock* pThis = stack.This();  FAULT_ON_NULL(pThis);

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        // disable the EXT interrupt channel
        // it's OK to do always this, no matter if it's enabled or not
        palDisableLineEvent(ioLine);

        // set pin to input to save power
        palSetLineMode(ioLine, PAL_MODE_INPUT);
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeIsDriveModeSupported___BOOLEAN__WindowsDevicesGpioGpioPinDriveMode( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4;

        bool driveModeSupported = false;

        // check if the requested drive mode is support by ChibiOS config
        if ((driveMode == GpioPinDriveMode_Input) ||
            (driveMode == GpioPinDriveMode_InputPullDown) ||
            (driveMode == GpioPinDriveMode_InputPullUp) ||
            (driveMode == GpioPinDriveMode_Output) ||
            (driveMode == GpioPinDriveMode_OutputOpenDrain))
        {
            driveModeSupported = true;
        }

        // Return value to the managed application
        stack.SetResult_Boolean( driveModeSupported ) ;
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetDriveMode___VOID__WindowsDevicesGpioGpioPinDriveMode( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        // it's better cast this this to the appropriate enum
        GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4;

        // check if drive mode is input
        bool driveModeIsInput = false;

        if( driveMode == GpioPinDriveMode_Input ||
            driveMode == GpioPinDriveMode_InputPullDown ||
            driveMode == GpioPinDriveMode_InputPullUp)
            {
                driveModeIsInput = true;
            }

        // flag to signal that interrupts need to be setup
        bool setupInterrupt = false;

        // flag to determine if there are any callbacks registered in managed code
        bool callbacksRegistered = (pThis[ FIELD___callbacks ].Dereference() != NULL);

        switch (driveMode)
        {
            case GpioPinDriveMode_Input:
                palSetLineMode(ioLine, PAL_MODE_INPUT);
                setupInterrupt = true;
                break;

            case GpioPinDriveMode_InputPullDown:
                palSetLineMode(ioLine, PAL_MODE_INPUT_PULLDOWN);
                setupInterrupt = true;
                break;

            case GpioPinDriveMode_InputPullUp:
                palSetLineMode(ioLine, PAL_MODE_INPUT_PULLUP);
                setupInterrupt = true;
                break;

            case GpioPinDriveMode_Output:
                palSetLineMode(ioLine, PAL_MODE_OUTPUT_PUSHPULL);
                break;

            case GpioPinDriveMode_OutputOpenDrain:
                palSetLineMode(ioLine, PAL_MODE_OUTPUT_OPENDRAIN);
                break;

            default:
                // all other modes are NOT supported
                NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
                break;
        }

        // if drive mode is output, read the pad to update the managed field _lastOutputValue
        if(!driveModeIsInput)
        {
            pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = palReadLine(ioLine);
        }

        if(callbacksRegistered && setupInterrupt)
        {
            // there are callbacks registered and...
            // the drive mode is input so need to setup the interrupt

            // save pin
            //channelPinMapping[pad] = pThis;

            palEnableLineEvent(ioLine, PAL_EVENT_MODE_BOTH_EDGES);
            palSetLineCallback(ioLine, GpioEventCallback, pThis);

            // protect this from GC so that the callback is where it's supposed to
            CLR_RT_ProtectFromGC         gc( *pThis );

            // read pad and store current value to check on debounce callback
            lastPadValue = palReadLine(ioLine);
        }
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeInit___BOOLEAN__I4( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        int16_t pinNumber = stack.Arg1().NumericByRef().s4;

        // TODO is probably a good idea keep track of the used pins, so we can check that here
        // TODO is probably a good idea to check if this pin exists

        // Return value to the managed application
        stack.SetResult_Boolean(true );
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetDebounceTimeout___VOID( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();

    // nothing to do here as the debounce timeout is grabbed from the managed object when required

    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::WriteNative___VOID__WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        // check if object has been disposed
        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        GpioPinDriveMode driveMode = (GpioPinDriveMode)pThis[ FIELD___driveMode ].NumericByRefConst().s4;

        GpioPinValue state = (GpioPinValue)stack.Arg1().NumericByRef().s4;

        // sanity check for drive mode set to output so we don't mess up writing to an input pin
        if ((driveMode == GpioPinDriveMode_Output) ||
            (driveMode == GpioPinDriveMode_OutputOpenDrain) ||
            (driveMode == GpioPinDriveMode_OutputOpenDrainPullUp) ||
            (driveMode == GpioPinDriveMode_OutputOpenSource) ||
            (driveMode == GpioPinDriveMode_OutputOpenSourcePullDown))
        {
            palWriteLine(ioLine, state);
        }
        else
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
        }
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetAlternateFunction___VOID__I4( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        // check if object has been disposed
        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        // get alternate function argument
		int32_t alternateFunction = stack.Arg1().NumericByRef().s4;

        palSetLineMode(ioLine, PAL_MODE_ALTERNATE(alternateFunction));

    }
    NANOCLR_NOCLEANUP();
}
<commit_msg>Correct code reading Gpio value (#635)<commit_after>//
// Copyright (c) 2017 The nanoFramework project contributors
// Portions Copyright (c) Microsoft Corporation.  All rights reserved.
// See LICENSE file in the project root for full license information.
//

#include <ch.h>
#include <hal.h>
#include <cmsis_os.h>

#include <targetPAL.h>
#include "win_dev_gpio_native.h"
#include "nf_rt_events_native.h"

///////////////////////////////////////////////////////////////////////////////////////
// !!! KEEP IN SYNC WITH Windows.Devices.Gpio.GpioPinDriveMode (in managed code) !!! //
///////////////////////////////////////////////////////////////////////////////////////

enum GpioPinDriveMode
{
    GpioPinDriveMode_Input = 0,
    GpioPinDriveMode_InputPullDown,
    GpioPinDriveMode_InputPullUp,
    GpioPinDriveMode_Output,
    GpioPinDriveMode_OutputOpenDrain,
    GpioPinDriveMode_OutputOpenDrainPullUp,
    GpioPinDriveMode_OutputOpenSource,
    GpioPinDriveMode_OutputOpenSourcePullDown
};

///////////////////////////////////////////////////////////////////////////////////
// !!! KEEP IN SYNC WITH Windows.Devices.Gpio.GpioPinValue (in managed code) !!! //
///////////////////////////////////////////////////////////////////////////////////

enum GpioPinValue
{
    GpioPinValue_Low = 0,
    GpioPinValue_High,
};

///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////

// this timer is used to handle the debounce time
static virtual_timer_t debounceTimer;

volatile uint16_t lastPadValue;

// this is an utility function to get a ChibiOS PAL IoLine from our "encoded" pin number
ioline_t GetIoLine(int16_t pinNumber)
{
    stm32_gpio_t* port  = GPIO_PORT(pinNumber);
    int16_t pad = pinNumber % 16;

    return PAL_LINE(port, pad);

}

static void debounceTimer_Callback( void* arg )
{
    CLR_RT_HeapBlock*  pThis = (CLR_RT_HeapBlock*)arg;

    if(pThis == NULL)
    {
        // no Gpio pin here, leave now
        chSysUnlockFromISR();
        return;
    }

    // check if object has been disposed
    if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
    {
        // object has been disposed, leave now
        chSysUnlockFromISR();
        return;
    }

    // get pin number
    int16_t pinNumber = pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___pinNumber ].NumericByRefConst().s4;

    // read line
    uint16_t currentValue = palReadLine(GetIoLine(pinNumber));

    if(lastPadValue == currentValue)
    {
        // value hasn't change for debounce interval so this is a valid change
        // post a managed event with the current pin value
        PostManagedEvent( EVENT_GPIO, 0, pinNumber, currentValue );
    }
}

static void GpioEventCallback(void *arg)
{
    CLR_RT_HeapBlock*  pThis = (CLR_RT_HeapBlock*)arg;

    NATIVE_INTERRUPT_START

    chSysLockFromISR();

    //CLR_RT_HeapBlock*  pThis = channelPinMapping[channel];

    if(pThis == NULL)
    {
        // no Gpio pin here, leave now
        chSysUnlockFromISR();
        return;
    }

    // check if object has been disposed
    if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
    {
        // object has been disposed, leave now
        chSysUnlockFromISR();

        NATIVE_INTERRUPT_END

        return;
    }

    // get pin number and take the port and pad references from that one
    int16_t pinNumber = pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___pinNumber ].NumericByRefConst().s4;

    // get IoLine from pin number
    ioline_t ioLine = GetIoLine(pinNumber);

    // check if there is a debounce time set
    int64_t debounceTimeoutMilsec = (CLR_INT64_TEMP_CAST) pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___debounceTimeout ].NumericByRefConst().s8 / TIME_CONVERSION__TO_MILLISECONDS;

    if(debounceTimeoutMilsec > 0)
    {
        // debounce set, need to handle it

        if(chVTIsArmed(&debounceTimer))
        {
            // there is a debounce timer already running so this change in pin value should be discarded
            chSysUnlockFromISR();

            NATIVE_INTERRUPT_END

            return;
        }

        // read pad
        lastPadValue = palReadLine(ioLine);

        // setup timer
        chVTSetI(&debounceTimer, TIME_MS2I(debounceTimeoutMilsec), debounceTimer_Callback, pThis);
    }
    else
    {
        // post a managed event with the current pin reading
        PostManagedEvent( EVENT_GPIO, 0, pinNumber, palReadLine(ioLine) );
    }

    chSysUnlockFromISR();

    NATIVE_INTERRUPT_END

}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::Read___WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        // check if object has been disposed
        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        stack.SetResult_I4( palReadLine(GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4)) );
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::DisposeNative___VOID( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock* pThis = stack.This();  FAULT_ON_NULL(pThis);

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        // disable the EXT interrupt channel
        // it's OK to do always this, no matter if it's enabled or not
        palDisableLineEvent(ioLine);

        // set pin to input to save power
        palSetLineMode(ioLine, PAL_MODE_INPUT);
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeIsDriveModeSupported___BOOLEAN__WindowsDevicesGpioGpioPinDriveMode( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4;

        bool driveModeSupported = false;

        // check if the requested drive mode is support by ChibiOS config
        if ((driveMode == GpioPinDriveMode_Input) ||
            (driveMode == GpioPinDriveMode_InputPullDown) ||
            (driveMode == GpioPinDriveMode_InputPullUp) ||
            (driveMode == GpioPinDriveMode_Output) ||
            (driveMode == GpioPinDriveMode_OutputOpenDrain))
        {
            driveModeSupported = true;
        }

        // Return value to the managed application
        stack.SetResult_Boolean( driveModeSupported ) ;
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetDriveMode___VOID__WindowsDevicesGpioGpioPinDriveMode( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        // it's better cast this this to the appropriate enum
        GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4;

        // check if drive mode is input
        bool driveModeIsInput = false;

        if( driveMode == GpioPinDriveMode_Input ||
            driveMode == GpioPinDriveMode_InputPullDown ||
            driveMode == GpioPinDriveMode_InputPullUp)
            {
                driveModeIsInput = true;
            }

        // flag to signal that interrupts need to be setup
        bool setupInterrupt = false;

        // flag to determine if there are any callbacks registered in managed code
        bool callbacksRegistered = (pThis[ FIELD___callbacks ].Dereference() != NULL);

        switch (driveMode)
        {
            case GpioPinDriveMode_Input:
                palSetLineMode(ioLine, PAL_MODE_INPUT);
                setupInterrupt = true;
                break;

            case GpioPinDriveMode_InputPullDown:
                palSetLineMode(ioLine, PAL_MODE_INPUT_PULLDOWN);
                setupInterrupt = true;
                break;

            case GpioPinDriveMode_InputPullUp:
                palSetLineMode(ioLine, PAL_MODE_INPUT_PULLUP);
                setupInterrupt = true;
                break;

            case GpioPinDriveMode_Output:
                palSetLineMode(ioLine, PAL_MODE_OUTPUT_PUSHPULL);
                break;

            case GpioPinDriveMode_OutputOpenDrain:
                palSetLineMode(ioLine, PAL_MODE_OUTPUT_OPENDRAIN);
                break;

            default:
                // all other modes are NOT supported
                NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
                break;
        }

        // if drive mode is output, read the pad to update the managed field _lastOutputValue
        if(!driveModeIsInput)
        {
            pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = palReadLine(ioLine);
        }

        if(callbacksRegistered && setupInterrupt)
        {
            // there are callbacks registered and...
            // the drive mode is input so need to setup the interrupt

            // save pin
            //channelPinMapping[pad] = pThis;

            palEnableLineEvent(ioLine, PAL_EVENT_MODE_BOTH_EDGES);
            palSetLineCallback(ioLine, GpioEventCallback, pThis);

            // protect this from GC so that the callback is where it's supposed to
            CLR_RT_ProtectFromGC         gc( *pThis );

            // read pad and store current value to check on debounce callback
            lastPadValue = palReadLine(ioLine);
        }
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeInit___BOOLEAN__I4( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        int16_t pinNumber = stack.Arg1().NumericByRef().s4;

        // TODO is probably a good idea keep track of the used pins, so we can check that here
        // TODO is probably a good idea to check if this pin exists

        // Return value to the managed application
        stack.SetResult_Boolean(true );
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetDebounceTimeout___VOID( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();

    // nothing to do here as the debounce timeout is grabbed from the managed object when required

    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::WriteNative___VOID__WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        // check if object has been disposed
        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        GpioPinDriveMode driveMode = (GpioPinDriveMode)pThis[ FIELD___driveMode ].NumericByRefConst().s4;

        GpioPinValue state = (GpioPinValue)stack.Arg1().NumericByRef().s4;

        // sanity check for drive mode set to output so we don't mess up writing to an input pin
        if ((driveMode == GpioPinDriveMode_Output) ||
            (driveMode == GpioPinDriveMode_OutputOpenDrain) ||
            (driveMode == GpioPinDriveMode_OutputOpenDrainPullUp) ||
            (driveMode == GpioPinDriveMode_OutputOpenSource) ||
            (driveMode == GpioPinDriveMode_OutputOpenSourcePullDown))
        {
            palWriteLine(ioLine, state);
        }
        else
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER);
        }
    }
    NANOCLR_NOCLEANUP();
}

HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetAlternateFunction___VOID__I4( CLR_RT_StackFrame& stack )
{
    NANOCLR_HEADER();
    {
        CLR_RT_HeapBlock*  pThis = stack.This();  FAULT_ON_NULL(pThis);

        // check if object has been disposed
        if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0)
        {
            NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED);
        }

        // get IoLine from pin number
        ioline_t ioLine = GetIoLine(pThis[ FIELD___pinNumber ].NumericByRefConst().s4);

        // get alternate function argument
		int32_t alternateFunction = stack.Arg1().NumericByRef().s4;

        palSetLineMode(ioLine, PAL_MODE_ALTERNATE(alternateFunction));

    }
    NANOCLR_NOCLEANUP();
}
<|endoftext|>


<commit_before>//
// RemotePhotoTool - remote camera control software
// Copyright (C) 2008-2014 Michael Fink
//
/// \file Variant.cpp Canon control - Variant data
//

// includes
#include "StdAfx.h"
#include "Variant.hpp"

Variant::Variant() throw()
:m_enType(typeInvalid),
 m_bIsArray(false)
{
}

CString Variant::ToString() const
{
   CString cszValue;

   if (m_bIsArray)
   {
      switch (m_enType)
      {
      case typeUInt8:
         {
            CString cszTemp;
            std::vector<unsigned char> vecData = GetArray<unsigned char>();
            for (size_t i=0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%02x"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;

      case typeUInt16:
         {
            CString cszTemp;
            std::vector<unsigned short> vecData = GetArray<unsigned short>();
            for (size_t i = 0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%04x"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;

      case typeUInt32:
         {
            CString cszTemp;
            std::vector<unsigned int> vecData = GetArray<unsigned int>();
            for (size_t i=0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%08x"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;

      case typeInt32:
         {
            CString cszTemp;
            std::vector<int> vecData = GetArray<int>();
            for (size_t i=0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%i"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;
      default:
         ATLASSERT(false);
         cszValue = _T("??? (array)");
         break;
      }
   }
   else
   switch (m_enType)
   {
   case typeBool:
      cszValue = Get<bool>() ? _T("yes") : _T("no");
      break;

   case typeString:
      cszValue = Get<CString>();
      break;

   case typeInt8:
      cszValue.Format(_T("%i"), Get<char>());
      break;

   case typeUInt8:
      cszValue.Format(_T("%02x"), Get<unsigned char>());
      break;

   case typeInt16:
      cszValue.Format(_T("%i"), Get<short>());
      break;

   case typeUInt16:
      cszValue.Format(_T("%04x"), Get<unsigned short>());
      break;

   case typeInt32:
      cszValue.Format(_T("%i"), Get<int>());
      break;

   case typeUInt32:
      cszValue.Format(_T("%08x"), Get<unsigned int>());
      break;

   case typeDouble:
      cszValue.Format(_T("%f"), Get<double>());
      break;

   case typeInvalid:
      cszValue = _T("invalid type");
      break;

   case typeInt64:
   case typeUInt64:
   case typeFloat:
   case typeByteBlock:
   case typeRational:
   case typePoint:
   case typeRect:
   case typeTime:
      // not implement

   default:
      ATLASSERT(false);
      cszValue = _T("???");
      break;
   }
   return cszValue;
}

LPCTSTR Variant::TypeAsString(VariantType vt) throw()
{
   switch(vt)
   {
   case typeBool:      return _T("Bool");
   case typeString:    return _T("String");
   case typeInt8:      return _T("Int8");
   case typeUInt8:     return _T("UInt8");
   case typeInt16:     return _T("Int16");
   case typeUInt16:    return _T("UInt16");
   case typeInt32:     return _T("Int32");
   case typeUInt32:    return _T("UInt32");
   case typeInt64:     return _T("Int64");
   case typeUInt64:    return _T("UInt64");
   case typeFloat:     return _T("Float");
   case typeDouble:    return _T("Double");
   case typeByteBlock: return _T("ByteBlock");
   case typeRational:  return _T("Rational");
   case typePoint:     return _T("Point");
   case typeRect:      return _T("Rect");
   case typeTime:      return _T("Time");
   case typeInvalid:   return _T("Invalid");
   default:
      ATLASSERT(false);
      return _T("???");
   }
}
<commit_msg>added formatting float types in Variant<commit_after>//
// RemotePhotoTool - remote camera control software
// Copyright (C) 2008-2014 Michael Fink
//
/// \file Variant.cpp Canon control - Variant data
//

// includes
#include "StdAfx.h"
#include "Variant.hpp"

Variant::Variant() throw()
:m_enType(typeInvalid),
 m_bIsArray(false)
{
}

CString Variant::ToString() const
{
   CString cszValue;

   if (m_bIsArray)
   {
      switch (m_enType)
      {
      case typeUInt8:
         {
            CString cszTemp;
            std::vector<unsigned char> vecData = GetArray<unsigned char>();
            for (size_t i=0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%02x"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;

      case typeUInt16:
         {
            CString cszTemp;
            std::vector<unsigned short> vecData = GetArray<unsigned short>();
            for (size_t i = 0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%04x"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;

      case typeUInt32:
         {
            CString cszTemp;
            std::vector<unsigned int> vecData = GetArray<unsigned int>();
            for (size_t i=0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%08x"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;

      case typeInt32:
         {
            CString cszTemp;
            std::vector<int> vecData = GetArray<int>();
            for (size_t i=0, iMax = vecData.size(); i<iMax; i++)
            {
               cszTemp.Format(_T("%s%i"), i == 0 ? _T("") : _T(" "), vecData[i]);
               cszValue += cszTemp;
            }
         }
         break;
      default:
         ATLASSERT(false);
         cszValue = _T("??? (array)");
         break;
      }
   }
   else
   switch (m_enType)
   {
   case typeBool:
      cszValue = Get<bool>() ? _T("yes") : _T("no");
      break;

   case typeString:
      cszValue = Get<CString>();
      break;

   case typeInt8:
      cszValue.Format(_T("%i"), Get<char>());
      break;

   case typeUInt8:
      cszValue.Format(_T("%02x"), Get<unsigned char>());
      break;

   case typeInt16:
      cszValue.Format(_T("%i"), Get<short>());
      break;

   case typeUInt16:
      cszValue.Format(_T("%04x"), Get<unsigned short>());
      break;

   case typeInt32:
      cszValue.Format(_T("%i"), Get<int>());
      break;

   case typeUInt32:
      cszValue.Format(_T("%08x"), Get<unsigned int>());
      break;

   case typeDouble:
      cszValue.Format(_T("%f"), Get<double>());
      break;

   case typeFloat: // used in gPhoto2
      cszValue.Format(_T("%f"), Get<float>());
      break;

   case typeInvalid:
      cszValue = _T("invalid type");
      break;

   case typeInt64:
   case typeUInt64:
   case typeByteBlock:
   case typeRational:
   case typePoint:
   case typeRect:
   case typeTime:
      // not implement

   default:
      ATLASSERT(false);
      cszValue = _T("???");
      break;
   }
   return cszValue;
}

LPCTSTR Variant::TypeAsString(VariantType vt) throw()
{
   switch(vt)
   {
   case typeBool:      return _T("Bool");
   case typeString:    return _T("String");
   case typeInt8:      return _T("Int8");
   case typeUInt8:     return _T("UInt8");
   case typeInt16:     return _T("Int16");
   case typeUInt16:    return _T("UInt16");
   case typeInt32:     return _T("Int32");
   case typeUInt32:    return _T("UInt32");
   case typeInt64:     return _T("Int64");
   case typeUInt64:    return _T("UInt64");
   case typeFloat:     return _T("Float");
   case typeDouble:    return _T("Double");
   case typeByteBlock: return _T("ByteBlock");
   case typeRational:  return _T("Rational");
   case typePoint:     return _T("Point");
   case typeRect:      return _T("Rect");
   case typeTime:      return _T("Time");
   case typeInvalid:   return _T("Invalid");
   default:
      ATLASSERT(false);
      return _T("???");
   }
}
<|endoftext|>


<commit_before>#ifndef DOMStringImpl_HEADER_GUARD_
#define DOMStringImpl_HEADER_GUARD_

/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/**
 * $Log$
 * Revision 1.1  2000/01/05 22:16:26  robweir
 * Move DOMString implementation class declarations into a new
 * file: DOMStringImpl.hpp.  Include this header in DOMString.hpp
 * for XML_DEBUG builds so the underlying character array will be
 * visible in the debugger.  <robert_weir@lotus.com>
 *
 *
 */

//
//  This file is part of the internal implementation of the C++ XML DOM.
//  It should NOT be included or used directly by application programs.
//

#include <util/XML4CDefs.hpp>

class   DOMStringData
{
public:
    int                 fBufferLength;
    int                 fRefCount;
    XMLCh               fData[1];

    static DOMStringData *allocateBuffer(int length);
    inline void         addRef();
    inline void         removeRef();
};

class  DOMStringHandle
{
public:
            int              fLength;
            int              fRefCount;
            DOMStringData    *fDSData;

    void    *operator new( size_t sizeToAlloc);
    void    operator delete( void *pvMem );
private:
    static  void *freeListPtr;
public:
    static  DOMStringHandle  *createNewStringHandle(int bufLength);
            DOMStringHandle  *cloneStringHandle();
    inline  void             addRef();
    inline  void             removeRef();
                             ~DOMStringHandle() {};
private:
    inline                   DOMStringHandle() {};
};

#endif

<commit_msg>Included header for size_t<commit_after>#ifndef DOMStringImpl_HEADER_GUARD_
#define DOMStringImpl_HEADER_GUARD_

/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/**
 * $Log$
 * Revision 1.2  2000/01/12 19:55:14  aruna1
 * Included header for size_t
 *
 * Revision 1.1  2000/01/05 22:16:26  robweir
 * Move DOMString implementation class declarations into a new
 * file: DOMStringImpl.hpp.  Include this header in DOMString.hpp
 * for XML_DEBUG builds so the underlying character array will be
 * visible in the debugger.  <robert_weir@lotus.com>
 *
 *
 */

//
//  This file is part of the internal implementation of the C++ XML DOM.
//  It should NOT be included or used directly by application programs.
//

#include <util/XML4CDefs.hpp>
#include <stdio.h>

class   DOMStringData
{
public:
    int                 fBufferLength;
    int                 fRefCount;
    XMLCh               fData[1];

    static DOMStringData *allocateBuffer(int length);
    inline void         addRef();
    inline void         removeRef();
};

class  DOMStringHandle
{
public:
            int              fLength;
            int              fRefCount;
            DOMStringData    *fDSData;

    void    *operator new( size_t sizeToAlloc);
    void    operator delete( void *pvMem );
private:
    static  void *freeListPtr;
public:
    static  DOMStringHandle  *createNewStringHandle(int bufLength);
            DOMStringHandle  *cloneStringHandle();
    inline  void             addRef();
    inline  void             removeRef();
                             ~DOMStringHandle() {};
private:
    inline                   DOMStringHandle() {};
};

#endif

<|endoftext|>


<commit_before>#include "stdafx.h"
#include "aabb2.h"
#include "mathutil.h"
#include "matrix2.h"

namespace dukat
{
	bool AABB2::overlaps(const AABB2& another) const
	{
		if (max.x < another.min.x) return false; // a is left of b
		if (min.x > another.max.x) return false; // a is right of b
		if (max.y < another.min.y) return false; // a is above b
		if (min.y > another.max.y) return false; // a is below b
		return true; // boxes overlap
	}

	bool AABB2::intersect(const AABB2& another, Collision& collision) const
	{
		auto this_c = center();
		auto that_c = another.center();
		auto d = this_c - that_c;
		auto p = ((max - min) + (another.max - another.min)) * 0.5f - d.abs();
		if (p.x <= 0.0f || p.y <= 0.0f)
			return false;

		if (p.x < p.y)
		{
			auto sx = sgn(d.x);
			collision.delta.x = p.x * sx;
			collision.normal.x = static_cast<float>(sx);
			collision.pos.x = sx < 0 ? min.x : max.x;
			collision.pos.y = that_c.y;
		}
		else
		{
			auto sy = sgn(d.y);
			collision.delta.y = p.y * sy;
			collision.normal.y = static_cast<float>(sy);
			collision.pos.x = that_c.x;
			collision.pos.y = sy < 0 ? min.y : max.y;
		}
		return true;
	}

	bool AABB2::contains(const Vector2& p) const
	{
		// check for overlap on each axis
		return
			(p.x >= min.x) && (p.x <= max.x) &&
			(p.y >= min.y) && (p.y <= max.y);
	}

	bool AABB2::intersect_circle(const Vector2& p, float radius) const
	{
		const auto center = this->center();
		auto v = center - p;
		const auto dist2_centers = v.mag2();

		// compute inner & outer radius of bounding box
		auto outer_rad = (center - min).mag2();
		auto inner_rad = (max - center).mag2();
		if (outer_rad < inner_rad)
			std::swap(outer_rad, inner_rad);

		// Case #1 - bb definitely outside of bb
		if (dist2_centers > outer_rad + radius)
			return false;
		// Case #2 - bb definitely inside of bb
		if (dist2_centers < inner_rad + radius)
			return true;
		// Case #3 - test point on circle on vector between centers
		v.normalize();
		return contains(center + v * radius);
	}

	void AABB2::clear()
	{
		min.x = min.y = big_number;
		max.x = max.y = -big_number;
	}

	void AABB2::add(const Vector2& p)
	{
		if (p.x < min.x)
		{
			min.x = p.x;
		}
		if (p.y < min.y)
		{
			min.y = p.y;
		}
		if (p.x > max.x)
		{
			max.x = p.x;
		}
		if (p.y > max.y)
		{
			max.y = p.y;
		}
	}

	void AABB2::add(const AABB2& box)
	{
		add(box.min);
		add(box.max);
	}

	bool AABB2::empty(void) const
	{
		// check if we're inverted on any axis
		return (min.x > max.x) || (min.y > max.y);
	}

	void AABB2::set_to_transformed_box(const AABB2& box, const Transform2& t)
	{
		clear();
		add(t.pos + box.min.rotate(t.rot));
		add(t.pos + box.max.rotate(t.rot));
	}

	float AABB2::intersect_ray(const Ray2& ray, float near, float far) const
	{
		auto tmin = (min.x - ray.origin.x) / ray.dir.x;
		auto tmax = (max.x - ray.origin.x) / ray.dir.x;
		if (tmin > tmax)
			std::swap(tmin, tmax);
		auto tymin = (min.y - ray.origin.y) / ray.dir.y;
		auto tymax = (max.y - ray.origin.y) / ray.dir.y;
		if (tymin > tymax)
			std::swap(tymin, tymax);
		if ((tmin > tymax) || (tymin > tmax))
			return no_intersection;
		if (tymin > tmin)
			tmin = tymin;
		if (tymax < tmax)
			tmax = tymax;
		if ((tmin > far) || (tmax < near))
			return no_intersection;
		return tmin;
	}

	int AABB2::classify_ray(const Ray2& ray) const
	{
		// compute ray normal by rotating the direction by 90 degrees CW
		// this means that the "right" side of the ray is considered its
		// front side
		Vector2 n(-ray.dir.y, ray.dir.x);
		auto d = ray.origin * n;

		// Inspect the normal and compute the minimum and
		// maximum D values
		float mind, maxd;

		if (n.x > 0.0f)
		{
			mind = n.x * min.x;
			maxd = n.x * max.x;
		}
		else
		{
			mind = n.x * max.x;
			maxd = n.x * min.x;
		}

		if (n.y > 0.0f)
		{
			mind += n.y * min.y;
			maxd += n.y * max.y;
		}
		else
		{
			mind += n.y * max.y;
			maxd += n.y * min.y;
		}

		// check if completely on the front side of the plane
		if (mind >= d)
		{
			return 1;
		}
		// check if completely on the back side of plane
		else if (maxd <= d)
		{
			return -1;
		}
		else
		{
			return 0;
		}
	}
}<commit_msg>Fixing said method<commit_after>#include "stdafx.h"
#include "aabb2.h"
#include "mathutil.h"
#include "matrix2.h"

namespace dukat
{
	bool AABB2::overlaps(const AABB2& another) const
	{
		if (max.x < another.min.x) return false; // a is left of b
		if (min.x > another.max.x) return false; // a is right of b
		if (max.y < another.min.y) return false; // a is above b
		if (min.y > another.max.y) return false; // a is below b
		return true; // boxes overlap
	}

	bool AABB2::intersect(const AABB2& another, Collision& collision) const
	{
		auto this_c = center();
		auto that_c = another.center();
		auto d = this_c - that_c;
		auto p = ((max - min) + (another.max - another.min)) * 0.5f - d.abs();
		if (p.x <= 0.0f || p.y <= 0.0f)
			return false;

		if (p.x < p.y)
		{
			auto sx = sgn(d.x);
			collision.delta.x = p.x * sx;
			collision.normal.x = static_cast<float>(sx);
			collision.pos.x = sx < 0 ? min.x : max.x;
			collision.pos.y = that_c.y;
		}
		else
		{
			auto sy = sgn(d.y);
			collision.delta.y = p.y * sy;
			collision.normal.y = static_cast<float>(sy);
			collision.pos.x = that_c.x;
			collision.pos.y = sy < 0 ? min.y : max.y;
		}
		return true;
	}

	bool AABB2::contains(const Vector2& p) const
	{
		// check for overlap on each axis
		return
			(p.x >= min.x) && (p.x <= max.x) &&
			(p.y >= min.y) && (p.y <= max.y);
	}

	bool AABB2::intersect_circle(const Vector2& p, float radius) const
	{
		const auto center = this->center();
		auto v = center - p;
		const auto dist2_centers = v.mag2();

		// compute inner & outer radius of bounding box
		auto outer_rad = (center - min).mag2();
		auto inner_rad = (max - center).mag2();
		if (outer_rad < inner_rad)
			std::swap(outer_rad, inner_rad);

		// Case #1 - bb definitely outside of bb
		if (dist2_centers > outer_rad + radius * radius)
			return false;
		// Case #2 - bb definitely inside of bb
		if (dist2_centers < inner_rad + radius * radius)
			return true;
		// Case #3 - test point on circle on vector between centers
		v.normalize();
		return contains(center + v * radius);
	}

	void AABB2::clear()
	{
		min.x = min.y = big_number;
		max.x = max.y = -big_number;
	}

	void AABB2::add(const Vector2& p)
	{
		if (p.x < min.x)
		{
			min.x = p.x;
		}
		if (p.y < min.y)
		{
			min.y = p.y;
		}
		if (p.x > max.x)
		{
			max.x = p.x;
		}
		if (p.y > max.y)
		{
			max.y = p.y;
		}
	}

	void AABB2::add(const AABB2& box)
	{
		add(box.min);
		add(box.max);
	}

	bool AABB2::empty(void) const
	{
		// check if we're inverted on any axis
		return (min.x > max.x) || (min.y > max.y);
	}

	void AABB2::set_to_transformed_box(const AABB2& box, const Transform2& t)
	{
		clear();
		add(t.pos + box.min.rotate(t.rot));
		add(t.pos + box.max.rotate(t.rot));
	}

	float AABB2::intersect_ray(const Ray2& ray, float near, float far) const
	{
		auto tmin = (min.x - ray.origin.x) / ray.dir.x;
		auto tmax = (max.x - ray.origin.x) / ray.dir.x;
		if (tmin > tmax)
			std::swap(tmin, tmax);
		auto tymin = (min.y - ray.origin.y) / ray.dir.y;
		auto tymax = (max.y - ray.origin.y) / ray.dir.y;
		if (tymin > tymax)
			std::swap(tymin, tymax);
		if ((tmin > tymax) || (tymin > tmax))
			return no_intersection;
		if (tymin > tmin)
			tmin = tymin;
		if (tymax < tmax)
			tmax = tymax;
		if ((tmin > far) || (tmax < near))
			return no_intersection;
		return tmin;
	}

	int AABB2::classify_ray(const Ray2& ray) const
	{
		// compute ray normal by rotating the direction by 90 degrees CW
		// this means that the "right" side of the ray is considered its
		// front side
		Vector2 n(-ray.dir.y, ray.dir.x);
		auto d = ray.origin * n;

		// Inspect the normal and compute the minimum and
		// maximum D values
		float mind, maxd;

		if (n.x > 0.0f)
		{
			mind = n.x * min.x;
			maxd = n.x * max.x;
		}
		else
		{
			mind = n.x * max.x;
			maxd = n.x * min.x;
		}

		if (n.y > 0.0f)
		{
			mind += n.y * min.y;
			maxd += n.y * max.y;
		}
		else
		{
			mind += n.y * max.y;
			maxd += n.y * min.y;
		}

		// check if completely on the front side of the plane
		if (mind >= d)
		{
			return 1;
		}
		// check if completely on the back side of plane
		else if (maxd <= d)
		{
			return -1;
		}
		else
		{
			return 0;
		}
	}
}<|endoftext|>


<commit_before>#include "GolfBall.hpp"
#include "../Resources.hpp"
#include "Default3D.vert.hpp"
#include "Default3D.geom.hpp"
#include "Default3D.frag.hpp"
#include "glm/gtc/constants.hpp"
#include "../Util/Log.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/quaternion.hpp>

GolfBall::GolfBall(BallType ballType, TerrainObject* terrain) : ModelObject(modelGeometry = Resources().CreateOBJModel("Resources/Models/GolfBall/GolfBall.obj"),
                                                                            "Resources/Models/GolfBall/Diffuse.png",
                                                                            "Resources/Models/GolfBall/Normal.png",
                                                                            "Resources/Models/GolfBall/Specular.png") {
    active = false;

    restitution = ballType == TWOPIECE ? 0.78f : 0.68f;
    this->terrain = terrain;
    groundLevel = this->terrain->Position().y;
    origin = glm::vec3(1.f, 0.f, 1.f);

    /// @todo Mass based on explosive material.
    mass = 0.0459f;
    this->ballType = ballType;

    sphere.position = Position();
    SetRadius(0.0214f);
}

GolfBall::~GolfBall() {
    Resources().FreeOBJModel(modelGeometry);
}

void GolfBall::Reset(){
    active = false;
    velocity = glm::vec3(0.f, 0.f, 0.f);
    angularVelocity = glm::vec3(0.f, 0.f, 0.f);
    SetPosition(origin);
    sphere.position = Position();
    orientation = glm::quat();
}

void GolfBall::Update(double time, const glm::vec3& wind, std::vector<PlayerObject>& players) {
    if (active) {
        Move(static_cast<float>(time) * velocity);
        sphere.position = Position();

        if (glm::length(angularVelocity) > 0.0001f) {
            glm::quat deltaQuat = glm::angleAxis(static_cast<float>(time) * glm::length(angularVelocity), glm::normalize(angularVelocity));
            orientation = deltaQuat * orientation;
        }

		//check for collision
		if ((sphere.position.y - sphere.radius) < groundLevel){
			float vCritical = 0.2f;
			float e = 0.55f;
			float mu = 0.91f;
			float muRolling = 0.011f;
			SetPosition(Position().x, groundLevel + sphere.radius, Position().z);
			sphere.position = Position();
			glm::vec3 surfaceNormal = glm::normalize(glm::vec3(0.f, 1.f, 0.f));
			glm::vec3 eRoh = glm::normalize(surfaceNormal);
			//@TODO: Find better approximative value for vCrit.
			glm::vec3 tangentialVelocity = velocity - (glm::dot(velocity, surfaceNormal))*surfaceNormal;
			//If the velocity projected along the surface normal isn't enough to lift the ball off the surface, then the ball is either rolling or sliding across the surface.
			//@TODO: Move ball along surfacenormal instead of along y-axis. Need to know distance between balls current position and triangle.
			//glm::vec3 originAtTriangle
			//glm::vec3 displacementAlongNormal = surfaceNormal*sphere.radius;
			//modelObject->SetPosition(displacementAlongNormal + originAtTriangle);
			glm::vec3 eFriction = glm::normalize((sphere.radius*glm::cross(eRoh, angularVelocity) + tangentialVelocity));
			glm::vec3 vRoh = velocity*eRoh;
			glm::vec3 eR = -eRoh;
			float deltaU = glm::length(-(e + 1.f)*vRoh);
			//Log() << glm::length(glm::dot(velocity, eRoh));
			glm::vec3 angularDirection = glm::cross(glm::normalize(tangentialVelocity), eR);
			if (glm::length(glm::dot(velocity, eRoh)) < vCritical){
				if (glm::dot(angularDirection, angularVelocity) < 0.f)
				{
					Log() << "Slajding.\n";
					//slajding
					velocity = tangentialVelocity - static_cast<float>(time)*(eFriction*mu)*glm::vec3(0.f, 9.82f, 0.f);
					angularVelocity += (5.f / 2.f)*(mu*9.82f / sphere.radius)*static_cast<float>(time)*angularDirection;
				}
				else{
					Log() << "Rolling.\n";
					//rolling
					velocity += angularVelocity*sphere.radius;
				}
			} else {
				glm::vec3 vRoh = velocity*eRoh;
				float deltaU = glm::length(-(e + 1.f)*vRoh);
				velocity += (deltaU)*(eRoh + mu*eFriction);
				angularVelocity += ((mu*glm::length(deltaU)) / (sphere.radius))*(glm::cross(eR, eFriction));
			}
		}
        Log() << angularVelocity << "\n";
        //Log() << glm::dot(velocity, glm::vec3(0.f,1.f,0.f)) << "\n";

        // Calculate magnus force.
        float v = glm::length(velocity);
        float u = glm::length(velocity - wind);
        float w = glm::length(angularVelocity);
        glm::vec3 eU = (velocity - wind) / u;
        glm::vec3 magnusForce = glm::vec3(0.f, 0.f, 0.f);
        if (v > 0.f && w > 0.f) {
            float Cm = (sqrt(1.f + 0.31f * (w / v)) - 1.f) / 20.f;
            float Fm = 0.5f * Cm * 1.23f * area * u * u;
            magnusForce = Fm * glm::cross(eU, glm::normalize(angularVelocity));
        }

        // Calculate drag force.
        float cD;
        if (ballType == TWOPIECE)
            cD = v < 65.f ? -0.0051f * v + 0.53f : 0.21f;
        else
            cD = v < 60.f ? -0.0084f * v + 0.73f : 0.22f;

        glm::vec3 dragForce = -0.5f * 1.23f * area * cD * u * u * eU;

        // Calculate gravitational force.
        glm::vec3 gravitationForce = glm::vec3(0.f, mass * -9.82f, 0.f);

        // Get acceleration from total force.
        glm::vec3 acceleration = (dragForce + magnusForce + gravitationForce) / mass;
        velocity += acceleration * static_cast<float>(time);
    }
}

void GolfBall::Render(Camera* camera, const glm::vec2& screenSize, const glm::vec4& clippingPlane) const{
    ModelObject::Render(camera, screenSize, clippingPlane);
}

void GolfBall::Explode(std::vector<PlayerObject>& players, int playerIndex){
    //@TODO: Set mass equivalent depending on material used.
    float equivalenceFactor = 1.0f;
    float massEquivalent = mass*equivalenceFactor;
    for (auto &player : players){
        glm::vec3 distanceV = (Position() - player.Position());
        float distance = glm::length(distanceV);
        //pow(meq, 1.f/3.f) => cube root of meq
        float z = distance / (pow(massEquivalent, 1.f / 3.f));
        float alpha = 1 + pow((z / 4.5f), 2.f);
        float beta = 1 + pow((z / 0.048f), 2.f);
        float gamma = 1 + pow((z / 1.35f), 2.f);
        float delta = 1 + pow((z / 0.32f), 2.f);
        float Pf = 8.08f*pow(10.f, 7.f)*alpha;
        Pf = Pf / sqrt(beta*gamma*delta);
        player.TakeDamage(Pf);
    }
    origin = players[playerIndex].Position();
    Reset();
}

void GolfBall::Strike(ClubType club, const glm::vec3& clubVelocity) {
    active = true;

    // Club velocity in strike plane.
    float v = glm::length(clubVelocity);
    float sinLoft = sin(club.loft);
    float cosLoft = cos(club.loft);

    // Ball velocity.
    float massCoefficient = club.mass / (club.mass + mass);
    float Up = (1.f + restitution) * massCoefficient * v * cosLoft;
    float Un = (2.f / 7.f) * massCoefficient * v * sinLoft;

    // Go back from strike plane to 3D.
    glm::vec3 forward = clubVelocity / v;
    glm::vec3 up = glm::cross(forward, glm::cross(glm::vec3(0.f, 1.f, 0.f), forward));
    glm::vec3 ep = glm::normalize(cosLoft * forward + sinLoft * up);
    glm::vec3 en = glm::normalize(sinLoft * forward - cosLoft * up);

    // Total velocity.
    velocity = Up * ep + Un * en;
    angularVelocity = -Un / sphere.radius * glm::cross(ep, en);
}

void GolfBall::SetRadius(float radius) {
    sphere.radius = radius;
    SetScale(glm::vec3(radius, radius, radius));
    area = glm::pi<float>() * radius * radius;
}

glm::mat4 GolfBall::Orientation() const {
	return glm::toMat4(orientation);
}
<commit_msg>More work on sliding<commit_after>#include "GolfBall.hpp"
#include "../Resources.hpp"
#include "Default3D.vert.hpp"
#include "Default3D.geom.hpp"
#include "Default3D.frag.hpp"
#include "glm/gtc/constants.hpp"
#include "../Util/Log.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/quaternion.hpp>

GolfBall::GolfBall(BallType ballType, TerrainObject* terrain) : ModelObject(modelGeometry = Resources().CreateOBJModel("Resources/Models/GolfBall/GolfBall.obj"),
                                                                            "Resources/Models/GolfBall/Diffuse.png",
                                                                            "Resources/Models/GolfBall/Normal.png",
                                                                            "Resources/Models/GolfBall/Specular.png") {
    active = false;

    restitution = ballType == TWOPIECE ? 0.78f : 0.68f;
    this->terrain = terrain;
    groundLevel = this->terrain->Position().y;
    origin = glm::vec3(1.f, 0.f, 1.f);

    /// @todo Mass based on explosive material.
    mass = 0.0459f;
    this->ballType = ballType;

    sphere.position = Position();
    SetRadius(0.0214f);
}

GolfBall::~GolfBall() {
    Resources().FreeOBJModel(modelGeometry);
}

void GolfBall::Reset(){
    active = false;
    velocity = glm::vec3(0.f, 0.f, 0.f);
    angularVelocity = glm::vec3(0.f, 0.f, 0.f);
    SetPosition(origin);
    sphere.position = Position();
    orientation = glm::quat();
}

void GolfBall::Update(double time, const glm::vec3& wind, std::vector<PlayerObject>& players) {
    if (active) {
        Move(static_cast<float>(time) * velocity);
        sphere.position = Position();

        if (glm::length(angularVelocity) > 0.0001f) {
            glm::quat deltaQuat = glm::angleAxis(static_cast<float>(time) * glm::length(angularVelocity), glm::normalize(angularVelocity));
            orientation = deltaQuat * orientation;
        }

		//check for collision
		if ((sphere.position.y - sphere.radius) < groundLevel){
			float vCritical = 0.2f;
			float e = 0.55f;
			float mu = 0.91f;
			float muRolling = 0.011f;
			SetPosition(Position().x, groundLevel + sphere.radius, Position().z);
			sphere.position = Position();
			glm::vec3 surfaceNormal = glm::normalize(glm::vec3(0.f, 1.f, 0.f));
			glm::vec3 eRoh = glm::normalize(surfaceNormal);
			//@TODO: Find better approximative value for vCrit.
			glm::vec3 tangentialVelocity = velocity - (glm::dot(velocity, surfaceNormal))*surfaceNormal;
			//If the velocity projected along the surface normal isn't enough to lift the ball off the surface, then the ball is either rolling or sliding across the surface.
			//@TODO: Move ball along surfacenormal instead of along y-axis. Need to know distance between balls current position and triangle.
			//glm::vec3 originAtTriangle
			//glm::vec3 displacementAlongNormal = surfaceNormal*sphere.radius;
			//modelObject->SetPosition(displacementAlongNormal + originAtTriangle);
			glm::vec3 eFriction = glm::normalize((sphere.radius*glm::cross(eRoh, angularVelocity) + tangentialVelocity));
			glm::vec3 vRoh = velocity*eRoh;
			glm::vec3 eR = -eRoh;
			float deltaU = glm::length(-(e + 1.f)*vRoh);
			//Log() << glm::length(glm::dot(velocity, eRoh));
			glm::vec3 angularDirection = glm::cross(glm::normalize(tangentialVelocity), eR);
			float w = glm::dot(angularVelocity, angularDirection);
			if (glm::length(glm::dot(velocity, eRoh)) < vCritical){
				if (w*sphere.radius > glm::length(tangentialVelocity))
				{
					Log() << "Slajding.\n";
					//slajding
					velocity = tangentialVelocity - static_cast<float>(time)*(eFriction*mu)*glm::vec3(0.f, 9.82f, 0.f);
					angularVelocity += (5.f / 2.f)*(mu*9.82f / sphere.radius)*static_cast<float>(time)*angularDirection;
				} else {
					Log() << "Rolling.\n";
					velocity = tangentialVelocity - static_cast<float>(time)*(eFriction*muRolling)*glm::vec3(0.f, 9.82f, 0.f);
				}
			} else {
				glm::vec3 vRoh = velocity*eRoh;
				float deltaU = glm::length(-(e + 1.f)*vRoh);
				velocity += (deltaU)*(eRoh + mu*eFriction);
				angularVelocity += ((mu*glm::length(deltaU)) / (sphere.radius))*(glm::cross(eR, eFriction));
			}
		}
        Log() << angularVelocity << "\n";
        //Log() << glm::dot(velocity, glm::vec3(0.f,1.f,0.f)) << "\n";

        // Calculate magnus force.
        float v = glm::length(velocity);
        float u = glm::length(velocity - wind);
        float w = glm::length(angularVelocity);
        glm::vec3 eU = (velocity - wind) / u;
        glm::vec3 magnusForce = glm::vec3(0.f, 0.f, 0.f);
        if (v > 0.f && w > 0.f) {
            float Cm = (sqrt(1.f + 0.31f * (w / v)) - 1.f) / 20.f;
            float Fm = 0.5f * Cm * 1.23f * area * u * u;
            magnusForce = Fm * glm::cross(eU, glm::normalize(angularVelocity));
        }

        // Calculate drag force.
        float cD;
        if (ballType == TWOPIECE)
            cD = v < 65.f ? -0.0051f * v + 0.53f : 0.21f;
        else
            cD = v < 60.f ? -0.0084f * v + 0.73f : 0.22f;

        glm::vec3 dragForce = -0.5f * 1.23f * area * cD * u * u * eU;

        // Calculate gravitational force.
        glm::vec3 gravitationForce = glm::vec3(0.f, mass * -9.82f, 0.f);

        // Get acceleration from total force.
        glm::vec3 acceleration = (dragForce + magnusForce + gravitationForce) / mass;
        velocity += acceleration * static_cast<float>(time);
    }
}

void GolfBall::Render(Camera* camera, const glm::vec2& screenSize, const glm::vec4& clippingPlane) const{
    ModelObject::Render(camera, screenSize, clippingPlane);
}

void GolfBall::Explode(std::vector<PlayerObject>& players, int playerIndex){
    //@TODO: Set mass equivalent depending on material used.
    float equivalenceFactor = 1.0f;
    float massEquivalent = mass*equivalenceFactor;
    for (auto &player : players){
        glm::vec3 distanceV = (Position() - player.Position());
        float distance = glm::length(distanceV);
        //pow(meq, 1.f/3.f) => cube root of meq
        float z = distance / (pow(massEquivalent, 1.f / 3.f));
        float alpha = 1 + pow((z / 4.5f), 2.f);
        float beta = 1 + pow((z / 0.048f), 2.f);
        float gamma = 1 + pow((z / 1.35f), 2.f);
        float delta = 1 + pow((z / 0.32f), 2.f);
        float Pf = 8.08f*pow(10.f, 7.f)*alpha;
        Pf = Pf / sqrt(beta*gamma*delta);
        player.TakeDamage(Pf);
    }
    origin = players[playerIndex].Position();
    Reset();
}

void GolfBall::Strike(ClubType club, const glm::vec3& clubVelocity) {
    active = true;

    // Club velocity in strike plane.
    float v = glm::length(clubVelocity);
    float sinLoft = sin(club.loft);
    float cosLoft = cos(club.loft);

    // Ball velocity.
    float massCoefficient = club.mass / (club.mass + mass);
    float Up = (1.f + restitution) * massCoefficient * v * cosLoft;
    float Un = (2.f / 7.f) * massCoefficient * v * sinLoft;

    // Go back from strike plane to 3D.
    glm::vec3 forward = clubVelocity / v;
    glm::vec3 up = glm::cross(forward, glm::cross(glm::vec3(0.f, 1.f, 0.f), forward));
    glm::vec3 ep = glm::normalize(cosLoft * forward + sinLoft * up);
    glm::vec3 en = glm::normalize(sinLoft * forward - cosLoft * up);

    // Total velocity.
    velocity = Up * ep + Un * en;
    angularVelocity = -Un / sphere.radius * glm::cross(ep, en);
}

void GolfBall::SetRadius(float radius) {
    sphere.radius = radius;
    SetScale(glm::vec3(radius, radius, radius));
    area = glm::pi<float>() * radius * radius;
}

glm::mat4 GolfBall::Orientation() const {
	return glm::toMat4(orientation);
}
<|endoftext|>


<commit_before>#include <cstdint>
#include <memory>

#include <boost/assert.hpp>

namespace blackhole {

template<typename T>
class monadic;

class config_t {
public:
    virtual auto operator[](const std::size_t& idx) const -> monadic<config_t> = 0;
    virtual auto operator[](const std::string& key) const -> monadic<config_t> = 0;

    virtual auto to_i64() const -> std::int64_t = 0;
    virtual auto to_u64() const -> std::uint64_t = 0;
    virtual auto to_string() const -> std::string = 0;
};

// TODO: Can be hidden for the sake of encapsulation.
template<>
class monadic<config_t> {
    std::unique_ptr<config_t> inner;

public:
    explicit monadic(std::unique_ptr<config_t> inner) :
        inner(std::move(inner))
    {
        BOOST_ASSERT(this->inner);
    }

    auto operator[](const std::size_t& idx) const -> monadic<config_t> {
        return inner->operator[](idx);
    }

    auto operator[](const std::string& key) const -> monadic<config_t> {
        return inner->operator[](key);
    }

    auto operator->() -> config_t* {
        return inner.get();
    }
};

template<typename T, typename... Args>
auto make_monadic(Args&&... args) -> monadic<config_t> {
    return monadic<config_t>(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}

}  // namespace blackhole

#include <gtest/gtest.h>

#include <boost/algorithm/string.hpp>

#include <rapidjson/document.h>

namespace blackhole {
namespace testing {

// builder(xml) - читает и генерирует свое дерево и реализации config_t.
//  .sevmap(...) - просто запоминает
//  .build() - ищет ключ "formatter/type" config["formatter"]["type"]
//             по type делегирует config["formatter"] в нужную фабрику

/// None value. Throws an exception on any get access, but maps to none on subscription.
class none_t : public config_t {
    auto operator[](const std::size_t& idx) const -> monadic<config_t> {
        return make_monadic<none_t>();
    }

    auto operator[](const std::string& key) const -> monadic<config_t> {
        return make_monadic<none_t>();
    }

    auto to_i64() const -> std::int64_t {
        throw std::out_of_range("none");
    }

    auto to_u64() const -> std::uint64_t {
        throw std::out_of_range("none");
    }

    auto to_string() const -> std::string {
        throw std::out_of_range("none");
    }
};

class json_t : public config_t {
    const rapidjson::Value& value;

public:
    explicit json_t(const rapidjson::Value& value) :
        value(value)
    {}

    auto operator[](const std::size_t& idx) const -> monadic<config_t> {
        if (value.IsArray() && idx < value.Size()) {
            return make_monadic<json_t>(value[idx]);
        }

        return make_monadic<none_t>();
    }

    auto operator[](const std::string& key) const -> monadic<config_t> {
        if (value.IsObject() && value.HasMember(key.c_str())) {
            return make_monadic<json_t>(value[key.c_str()]);
        }

        return make_monadic<none_t>();
    }

    auto to_i64() const -> std::int64_t {
        if (value.IsInt64()) {
            return value.GetInt64();
        }

        throw std::invalid_argument(""); // TODO: Bad cast.
    }

    auto to_u64() const -> std::uint64_t {
        if (value.IsUint64()) {
            return value.GetUint64();
        }

        throw std::invalid_argument(""); // TODO: Bad cast.
    }

    auto to_string() const -> std::string {
        if (value.IsString()) {
            return value.GetString();
        }

        throw std::invalid_argument(""); // TODO: Bad cast.
    }
};

class json_factory_t {
    rapidjson::Document doc;

public:
    explicit json_factory_t(const std::string& value) {
        doc.Parse<0>(value.c_str());
        if (doc.HasParseError()) {
            throw std::runtime_error("parse error");
        }
    }

    auto build() -> void {
        auto config = json_t(doc);
        const auto type = config["formatter"]["type"]->to_string();
    }
};

TEST(config_t, json) {
    static const std::string JSON = R"({
        'formatter': {
            'type': 'string',
            'pattern': '[%(level)s]: %(message)s'
        },
        'sinks': [
            {
                'type': 'files',
                'path': 'test.log',
                'rotation': {
                    'pattern': 'test.log.%N',
                    'backups': 5,
                    'size': 1000000
                }
            }
        ]
    })";

    const std::string valid(boost::algorithm::replace_all_copy(JSON, "'", "\""));
    rapidjson::Document doc;
    doc.Parse<0>(valid.c_str());
    ASSERT_FALSE(doc.HasParseError());

    json_t config(doc);

    EXPECT_EQ("string", config["formatter"]["type"]->to_string());
    EXPECT_EQ("[%(level)s]: %(message)s", config["formatter"]["pattern"]->to_string());

    EXPECT_EQ("files", config["sinks"][0]["type"]->to_string());
    EXPECT_EQ("test.log", config["sinks"][0]["path"]->to_string());
    EXPECT_EQ("test.log.%N", config["sinks"][0]["rotation"]["pattern"]->to_string());
    EXPECT_EQ(5, config["sinks"][0]["rotation"]["backups"]->to_i64());
    EXPECT_EQ(1000000, config["sinks"][0]["rotation"]["size"]->to_u64());
}

}  // namespace testing
}  // namespace blackhole
<commit_msg>feat(builder): add each and each_map methods<commit_after>#include <cstdint>
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/assert.hpp>

namespace blackhole {

template<typename T>
class monadic;

class config_t {
public:
    typedef std::function<auto(const config_t&) -> void> each_function;
    typedef std::function<auto(const std::string&, const config_t&) -> void> member_function;

    virtual auto operator[](const std::size_t& idx) const -> monadic<config_t> = 0;
    virtual auto operator[](const std::string& key) const -> monadic<config_t> = 0;

    virtual auto to_i64() const -> std::int64_t = 0;
    virtual auto to_u64() const -> std::uint64_t = 0;
    virtual auto to_string() const -> std::string = 0;

    virtual auto each(const each_function& fn) -> void = 0;
    virtual auto each_map(const member_function& fn) -> void = 0;
};

/// None value. Throws an exception on any get access, but maps to none on subscription.
class none_t : public config_t {
    auto operator[](const std::size_t& idx) const -> monadic<config_t>;
    auto operator[](const std::string& key) const -> monadic<config_t>;
    auto to_i64() const -> std::int64_t;
    auto to_u64() const -> std::uint64_t;
    auto to_string() const -> std::string;

    auto each(const each_function& fn) -> void;
    auto each_map(const member_function& fn) -> void;
};

// TODO: Can be hidden for the sake of encapsulation.
template<>
class monadic<config_t> {
    std::unique_ptr<config_t> inner;

public:
    monadic() :
        inner(new none_t)
    {}

    explicit monadic(std::unique_ptr<config_t> inner) :
        inner(std::move(inner))
    {
        BOOST_ASSERT(this->inner);
    }

    auto operator[](const std::size_t& idx) const -> monadic<config_t> {
        return inner->operator[](idx);
    }

    auto operator[](const std::string& key) const -> monadic<config_t> {
        return inner->operator[](key);
    }

    auto operator->() -> config_t* {
        return inner.get();
    }

    auto operator*() const -> const config_t& {
        return *inner;
    }
};

auto
none_t::operator[](const std::size_t& idx) const -> monadic<config_t> {
    return {};
}

auto
none_t::operator[](const std::string& key) const -> monadic<config_t> {
    return {};
}

auto
none_t::to_i64() const -> std::int64_t {
    throw std::out_of_range("none");
}

auto
none_t::to_u64() const -> std::uint64_t {
    throw std::out_of_range("none");
}

auto
none_t::to_string() const -> std::string {
    throw std::out_of_range("none");
}

auto
none_t::each(const each_function& fn) -> void {
    throw std::out_of_range("none");
}

auto
none_t::each_map(const member_function& fn) -> void {
    throw std::out_of_range("none");
}

template<typename T, typename... Args>
auto make_monadic(Args&&... args) -> monadic<config_t> {
    return monadic<config_t>(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}

}  // namespace blackhole

#include <gtest/gtest.h>

#include <boost/algorithm/string.hpp>

#include <rapidjson/document.h>

namespace blackhole {
namespace testing {

// builder(xml) - читает и генерирует свое дерево и реализации config_t.
//  .sevmap(...) - просто запоминает
//  .build() - ищет ключ "formatter/type" config["formatter"]["type"]
//             по type делегирует config["formatter"] в нужную фабрику

class json_t : public config_t {
    const rapidjson::Value& value;

public:
    explicit json_t(const rapidjson::Value& value) :
        value(value)
    {}

    auto operator[](const std::size_t& idx) const -> monadic<config_t> {
        if (value.IsArray() && idx < value.Size()) {
            return make_monadic<json_t>(value[idx]);
        }

        return {};
    }

    auto operator[](const std::string& key) const -> monadic<config_t> {
        if (value.IsObject() && value.HasMember(key.c_str())) {
            return make_monadic<json_t>(value[key.c_str()]);
        }

        return {};
    }

    auto to_i64() const -> std::int64_t {
        if (value.IsInt64()) {
            return value.GetInt64();
        }

        throw std::invalid_argument(""); // TODO: Bad cast.
    }

    auto to_u64() const -> std::uint64_t {
        if (value.IsUint64()) {
            return value.GetUint64();
        }

        throw std::invalid_argument(""); // TODO: Bad cast.
    }

    auto to_string() const -> std::string {
        if (value.IsString()) {
            return value.GetString();
        }

        throw std::invalid_argument(""); // TODO: Bad cast.
    }

    auto each(const each_function& fn) -> void {
        for (auto it = value.Begin(); it != value.End(); ++it) {
            fn(*make_monadic<json_t>(*it));
        }
    }

    auto each_map(const member_function& fn) -> void {
        for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
            fn(it->name.GetString(), *make_monadic<json_t>(it->value));
        }
    }
};

TEST(config_t, json) {
    static const std::string JSON = R"({
        'formatter': {
            'type': 'string',
            'pattern': '[%(level)s]: %(message)s'
        },
        'sinks': [
            {
                'type': 'files',
                'path': 'test.log',
                'rotation': {
                    'pattern': 'test.log.%N',
                    'backups': 5,
                    'size': 1000000
                }
            }
        ]
    })";

    const std::string valid(boost::algorithm::replace_all_copy(JSON, "'", "\""));
    rapidjson::Document doc;
    doc.Parse<0>(valid.c_str());
    ASSERT_FALSE(doc.HasParseError());

    json_t config(doc);

    EXPECT_EQ("string", config["formatter"]["type"]->to_string());
    EXPECT_EQ("[%(level)s]: %(message)s", config["formatter"]["pattern"]->to_string());

    EXPECT_EQ("files", config["sinks"][0]["type"]->to_string());
    EXPECT_EQ("test.log", config["sinks"][0]["path"]->to_string());
    EXPECT_EQ("test.log.%N", config["sinks"][0]["rotation"]["pattern"]->to_string());
    EXPECT_EQ(5, config["sinks"][0]["rotation"]["backups"]->to_i64());
    EXPECT_EQ(1000000, config["sinks"][0]["rotation"]["size"]->to_u64());

    // NOTE: There is only one sink, so it's okay to compare strongly.
    auto counter = 0;
    config["sinks"]->each([&](const config_t& sink) {
        EXPECT_EQ("files", sink["type"]->to_string());
        EXPECT_EQ("test.log", sink["path"]->to_string());
        EXPECT_EQ("test.log.%N", sink["rotation"]["pattern"]->to_string());
        EXPECT_EQ(5, sink["rotation"]["backups"]->to_i64());
        EXPECT_EQ(1000000, sink["rotation"]["size"]->to_u64());
        ++counter;
    });

    EXPECT_EQ(1, counter);

    std::vector<std::string> keys;
    config.each_map([&](const std::string& key, const config_t& sink) {
        keys.push_back(key);
    });

    EXPECT_EQ((std::vector<std::string>{"formatter", "sinks"}), keys);
}

}  // namespace testing
}  // namespace blackhole
<|endoftext|>


<commit_before>#include <nds.h>
#include <stdlib.h>
#include <stdio.h>
#include "gameboy.h"
#include "gbcpu.h"
#include "gbgfx.h"
#include "gbsnd.h"
#include "mmu.h"
#include "timer.h"
#include "main.h"
#include "inputhelper.h"
#include "nifi.h"
#include "console.h"

int mode2Cycles, mode3Cycles;
int scanlineCounter;
int doubleSpeed;

int turbo;
int turboFrameSkip;
int frameskip;
int frameskipCounter;
bool fpsOutput=true;
bool timeOutput=true;
bool fastForwardMode = false; // controlled by the menu
bool fastForwardKey = false;  // only while its hotkey is pressed

// ...what is phase? I think I made that up. Used for timing when the gameboy
// screen is off.
int phaseCounter;
int dividerCounter;
int serialCounter;

int timerCounter;
int timerPeriod;
long periods[4];

bool screenOn = true;

int cyclesToEvent;
int maxWaitCycles;

inline void setEventCycles(int cycles) {
    if (cycles < cyclesToEvent) {
        cyclesToEvent = cycles;
        /*
        if (cyclesToEvent <= 0) {
           cyclesToEvent = 1;
        }
        */
    }
}

int updateInput() {
    readKeys();
    int retval = handleEvents();		// Input mostly
    if (!consoleDebugOutput && getTimerTicks() >= 1000)
    {
        consoleClear();
        int line=0;
        if (fpsOutput) {
            consoleClear();
            printf("FPS: %d\n", fps);
            line++;
        }
        fps = 0;
        startTimer();
        if (timeOutput) {
            for (; line<23-1; line++)
                printf("\n");
            time_t rawTime = time(NULL);
            char *timeString = ctime(&rawTime);
            for (int i=0;; i++) {
                if (timeString[i] == ':') {
                    timeString += i-2;
                    break;
                }
            }
            char s[50];
            strncpy(s, timeString, 50);
            s[5] = '\0';
            int spaces = 31-strlen(s);
            for (int i=0; i<spaces; i++)
                printf(" ");
            printf("%s\n", s);
        }
    }
    return retval;
}

int extraCycles;
void runEmul()
{
    for (;;)
    {
emuLoopStart:
        cyclesToEvent -= extraCycles;
        int cycles;
        if (halt)
            cycles = cyclesToEvent;
        else
            cycles = runOpcode(cyclesToEvent);
        cycles += extraCycles;

        cyclesToEvent = maxWaitCycles;
        extraCycles=0;

        if (serialCounter > 0) {
            serialCounter -= cycles;
            if (serialCounter <= 0) {
                serialCounter = 0;
                packetData = 0xff;
                transferReady = true;
            }
            else
                setEventCycles(serialCounter);
        }
        if (transferReady) {
            if (!(ioRam[0x02] & 1)) {
                sendPacketByte(56, sendData);
            }
            timerStop(2);
            ioRam[0x01] = packetData;
            requestInterrupt(SERIAL);
            ioRam[0x02] &= ~0x80;
            packetData = -1;
            transferReady = false;
        }
        updateTimers(cycles);
        updateSound(cycles);

        if (updateLCD(cycles))
            // Emulation is being reset or something
            goto emuLoopStart;

        int interruptTriggered = ioRam[0x0F] & ioRam[0xFF];
        // Run another opcode before triggering an interrupt.
        // Robocop 2 needs this.
        if (interruptTriggered) {
            if (!halt)
                extraCycles += runOpcode(4);
            handleInterrupts(interruptTriggered);
        }
    }
}

void initLCD()
{
    // Pokemon Yellow hack: I need to intentionally SLOW DOWN emulation for
    // Pikachu's pitch to sound right...
    // The exact value of this will vary, so I'm going to leave it commented for
    // now.
    /*
    if (strcmp(getRomTitle(), "POKEMON YELLOW") == 0)
       maxWaitCycles = 100;
    else
    */
    maxWaitCycles = 800;

    setDoubleSpeed(0);

    scanlineCounter = 456*(doubleSpeed?2:1);
    phaseCounter = 456*153;
    timerCounter = 0;
    dividerCounter = 256;
    serialCounter = 0;
    turboFrameSkip = 4;
    turbo=0;

    // Timer stuff
    periods[0] = clockSpeed/4096;
    periods[1] = clockSpeed/262144;
    periods[2] = clockSpeed/65536;
    periods[3] = clockSpeed/16384;
    timerPeriod = periods[0];

    timerStop(2);
}

inline int updateLCD(int cycles)
{
    if (!(ioRam[0x40] & 0x80))		// If LCD is off
    {
        scanlineCounter = 456*(doubleSpeed?2:1);;
        ioRam[0x44] = 0;
        ioRam[0x41] &= 0xF8;
        // Normally timing is synchronized with gameboy's vblank. If the screen
        // is off, this code kicks in. The "phaseCounter" is a counter until the
        // ds should check for input and whatnot.
        phaseCounter -= cycles;
        if (phaseCounter <= 0) {
            if (!(fastForwardMode || fastForwardKey))
                swiIntrWait(interruptWaitMode, 1);
            fps++;
            phaseCounter += 456*153*(doubleSpeed?2:1);
            if (screenOn) {
                disableScreen();
                screenOn = false;
            }
            if (updateInput())
                return 1;
        }
        return 0;
    }

    scanlineCounter -= cycles;

    switch(ioRam[0x41]&3)
    {
        case 2:
            {
                if (scanlineCounter <= mode2Cycles) {
                    ioRam[0x41]++;
                    setEventCycles(scanlineCounter-mode3Cycles);
                }
                else
                    setEventCycles(scanlineCounter-mode2Cycles);
            }
            break;
        case 3:
            {
                if (scanlineCounter <= mode3Cycles) {
                    ioRam[0x41] &= ~3;

                    if (ioRam[0x41]&0x8)
                    {
                        requestInterrupt(LCD);
                    }

                    drawScanline(ioRam[0x44]);
                    if (updateHblankDMA()) {
                        extraCycles += 50;
                    }

                    setEventCycles(scanlineCounter);
                }
                else
                    setEventCycles(scanlineCounter-mode3Cycles);
            }
            break;
        case 0:
            {
                // fall through to next case
            }
        case 1:
            if (scanlineCounter <= 0)
            {
                scanlineCounter += 456*(doubleSpeed?2:1);
                ioRam[0x44]++;

                if (ioRam[0x44] < 144 || ioRam[0x44] >= 153) {
                    setEventCycles(scanlineCounter-mode2Cycles);
                    ioRam[0x41] &= ~3;
                    ioRam[0x41] |= 2;
                    if (ioRam[0x41]&0x20)
                    {
                        requestInterrupt(LCD);
                    }

                    if (ioRam[0x44] >= 153)
                    {
                        ioRam[0x44] = 0;
                    }
                }
                else if (ioRam[0x44] == 144)
                {
                    ioRam[0x41] &= ~3;
                    ioRam[0x41] |= 1;

                    requestInterrupt(VBLANK);
                    if (ioRam[0x41]&0x10)
                    {
                        requestInterrupt(LCD);
                    }

                    fps++;
                    drawScreen();
                    if (!screenOn) {
                        enableScreen();
                        screenOn = true;
                    }
                    if (updateInput())
                        return 1;
                }
                if (ioRam[0x44] >= 144) {
                    setEventCycles(scanlineCounter);
                }

                // LYC check
                if (ioRam[0x44] == ioRam[0x45])
                {
                    ioRam[0x41] |= 4;
                    if (ioRam[0x41]&0x40)
                        requestInterrupt(LCD);
                }
                else
                    ioRam[0x41] &= ~4;

            }
            else {
                setEventCycles(scanlineCounter);
            }
            break;
    }

    return 0;
}

inline void updateTimers(int cycles)
{
    if (ioRam[0x07] & 0x4)
    {
        timerCounter -= cycles;
        while (timerCounter <= 0)
        {
            timerCounter = timerPeriod + timerCounter;
            if ((++ioRam[0x05]) == 0)
            {
                requestInterrupt(TIMER);
                ioRam[0x05] = ioRam[0x06];
            }
        }
        setEventCycles(timerCounter+timerPeriod*(255-ioRam[0x05]));
    }
    dividerCounter -= cycles;
    if (dividerCounter <= 0)
    {
        dividerCounter = 256+dividerCounter;
        ioRam[0x04]++;
    }
    //setEventCycles(dividerCounter);
}

void requestInterrupt(int id)
{
    ioRam[0x0F] |= id;
    if (ioRam[0x0F] & ioRam[0xFF])
        cyclesToExecute = 0;
}

void setDoubleSpeed(int val) {
    if (val == 0) {
        mode2Cycles = 456 - 80;
        mode3Cycles = 456 - 172 - 80;
        doubleSpeed = 0;
        ioRam[0x4D] &= ~0x80;
    }
    else {
        mode2Cycles = (456 - 80)*2;
        mode3Cycles = (456 - 172 - 80)*2;
        doubleSpeed = 1;
        ioRam[0x4D] |= 0x80;
    }
}
<commit_msg>Sound is updated less often to increase speed<commit_after>#include <nds.h>
#include <stdlib.h>
#include <stdio.h>
#include "gameboy.h"
#include "gbcpu.h"
#include "gbgfx.h"
#include "gbsnd.h"
#include "mmu.h"
#include "timer.h"
#include "main.h"
#include "inputhelper.h"
#include "nifi.h"
#include "console.h"

int mode2Cycles, mode3Cycles;
int scanlineCounter;
int doubleSpeed;

int turbo;
int turboFrameSkip;
int frameskip;
int frameskipCounter;
bool fpsOutput=true;
bool timeOutput=true;
bool fastForwardMode = false; // controlled by the menu
bool fastForwardKey = false;  // only while its hotkey is pressed

// ...what is phase? I think I made that up. Used for timing when the gameboy
// screen is off.
int phaseCounter;
int dividerCounter;
int serialCounter;

int timerCounter;
int timerPeriod;
long periods[4];

bool screenOn = true;

int cyclesToEvent;
int maxWaitCycles;

inline void setEventCycles(int cycles) {
    if (cycles < cyclesToEvent) {
        cyclesToEvent = cycles;
        /*
        if (cyclesToEvent <= 0) {
           cyclesToEvent = 1;
        }
        */
    }
}

int updateInput() {
    readKeys();
    int retval = handleEvents();		// Input mostly
    if (!consoleDebugOutput && getTimerTicks() >= 1000)
    {
        consoleClear();
        int line=0;
        if (fpsOutput) {
            consoleClear();
            printf("FPS: %d\n", fps);
            line++;
        }
        fps = 0;
        startTimer();
        if (timeOutput) {
            for (; line<23-1; line++)
                printf("\n");
            time_t rawTime = time(NULL);
            char *timeString = ctime(&rawTime);
            for (int i=0;; i++) {
                if (timeString[i] == ':') {
                    timeString += i-2;
                    break;
                }
            }
            char s[50];
            strncpy(s, timeString, 50);
            s[5] = '\0';
            int spaces = 31-strlen(s);
            for (int i=0; i<spaces; i++)
                printf(" ");
            printf("%s\n", s);
        }
    }
    return retval;
}

int soundCycles=0;
int extraCycles;
void runEmul()
{
    for (;;)
    {
emuLoopStart:
        cyclesToEvent -= extraCycles;
        int cycles;
        if (halt)
            cycles = cyclesToEvent;
        else
            cycles = runOpcode(cyclesToEvent);
        cycles += extraCycles;

        cyclesToEvent = maxWaitCycles;
        extraCycles=0;

        if (serialCounter > 0) {
            serialCounter -= cycles;
            if (serialCounter <= 0) {
                serialCounter = 0;
                packetData = 0xff;
                transferReady = true;
            }
            else
                setEventCycles(serialCounter);
        }
        if (transferReady) {
            if (!(ioRam[0x02] & 1)) {
                sendPacketByte(56, sendData);
            }
            timerStop(2);
            ioRam[0x01] = packetData;
            requestInterrupt(SERIAL);
            ioRam[0x02] &= ~0x80;
            packetData = -1;
            transferReady = false;
        }
        updateTimers(cycles);
        soundCycles += cycles;
        if (soundCycles >= 66666) {
            updateSound(soundCycles);
            soundCycles = 0;
        }

        if (updateLCD(cycles))
            // Emulation is being reset or something
            goto emuLoopStart;

        int interruptTriggered = ioRam[0x0F] & ioRam[0xFF];
        // Run another opcode before triggering an interrupt.
        // Robocop 2 needs this.
        if (interruptTriggered) {
            if (!halt)
                extraCycles += runOpcode(4);
            handleInterrupts(interruptTriggered);
        }
    }
}

void initLCD()
{
    // Pokemon Yellow hack: I need to intentionally SLOW DOWN emulation for
    // Pikachu's pitch to sound right...
    // The exact value of this will vary, so I'm going to leave it commented for
    // now.
    /*
    if (strcmp(getRomTitle(), "POKEMON YELLOW") == 0)
       maxWaitCycles = 100;
    else
    */
    maxWaitCycles = 800;

    setDoubleSpeed(0);

    scanlineCounter = 456*(doubleSpeed?2:1);
    phaseCounter = 456*153;
    timerCounter = 0;
    dividerCounter = 256;
    serialCounter = 0;
    turboFrameSkip = 4;
    turbo=0;

    // Timer stuff
    periods[0] = clockSpeed/4096;
    periods[1] = clockSpeed/262144;
    periods[2] = clockSpeed/65536;
    periods[3] = clockSpeed/16384;
    timerPeriod = periods[0];

    timerStop(2);
}

inline int updateLCD(int cycles)
{
    if (!(ioRam[0x40] & 0x80))		// If LCD is off
    {
        scanlineCounter = 456*(doubleSpeed?2:1);;
        ioRam[0x44] = 0;
        ioRam[0x41] &= 0xF8;
        // Normally timing is synchronized with gameboy's vblank. If the screen
        // is off, this code kicks in. The "phaseCounter" is a counter until the
        // ds should check for input and whatnot.
        phaseCounter -= cycles;
        if (phaseCounter <= 0) {
            if (!(fastForwardMode || fastForwardKey))
                swiIntrWait(interruptWaitMode, 1);
            fps++;
            phaseCounter += 456*153*(doubleSpeed?2:1);
            if (screenOn) {
                disableScreen();
                screenOn = false;
            }
            if (updateInput())
                return 1;
        }
        return 0;
    }

    scanlineCounter -= cycles;

    switch(ioRam[0x41]&3)
    {
        case 2:
            {
                if (scanlineCounter <= mode2Cycles) {
                    ioRam[0x41]++;
                    setEventCycles(scanlineCounter-mode3Cycles);
                }
                else
                    setEventCycles(scanlineCounter-mode2Cycles);
            }
            break;
        case 3:
            {
                if (scanlineCounter <= mode3Cycles) {
                    ioRam[0x41] &= ~3;

                    if (ioRam[0x41]&0x8)
                    {
                        requestInterrupt(LCD);
                    }

                    drawScanline(ioRam[0x44]);
                    if (updateHblankDMA()) {
                        extraCycles += 50;
                    }

                    setEventCycles(scanlineCounter);
                }
                else
                    setEventCycles(scanlineCounter-mode3Cycles);
            }
            break;
        case 0:
            {
                // fall through to next case
            }
        case 1:
            if (scanlineCounter <= 0)
            {
                scanlineCounter += 456*(doubleSpeed?2:1);
                ioRam[0x44]++;

                if (ioRam[0x44] < 144 || ioRam[0x44] >= 153) {
                    setEventCycles(scanlineCounter-mode2Cycles);
                    ioRam[0x41] &= ~3;
                    ioRam[0x41] |= 2;
                    if (ioRam[0x41]&0x20)
                    {
                        requestInterrupt(LCD);
                    }

                    if (ioRam[0x44] >= 153)
                    {
                        ioRam[0x44] = 0;
                    }
                }
                else if (ioRam[0x44] == 144)
                {
                    ioRam[0x41] &= ~3;
                    ioRam[0x41] |= 1;

                    requestInterrupt(VBLANK);
                    if (ioRam[0x41]&0x10)
                    {
                        requestInterrupt(LCD);
                    }

                    fps++;
                    drawScreen();
                    if (!screenOn) {
                        enableScreen();
                        screenOn = true;
                    }
                    if (updateInput())
                        return 1;
                }
                if (ioRam[0x44] >= 144) {
                    setEventCycles(scanlineCounter);
                }

                // LYC check
                if (ioRam[0x44] == ioRam[0x45])
                {
                    ioRam[0x41] |= 4;
                    if (ioRam[0x41]&0x40)
                        requestInterrupt(LCD);
                }
                else
                    ioRam[0x41] &= ~4;

            }
            else {
                setEventCycles(scanlineCounter);
            }
            break;
    }

    return 0;
}

inline void updateTimers(int cycles)
{
    if (ioRam[0x07] & 0x4)
    {
        timerCounter -= cycles;
        while (timerCounter <= 0)
        {
            timerCounter = timerPeriod + timerCounter;
            if ((++ioRam[0x05]) == 0)
            {
                requestInterrupt(TIMER);
                ioRam[0x05] = ioRam[0x06];
            }
        }
        setEventCycles(timerCounter+timerPeriod*(255-ioRam[0x05]));
    }
    dividerCounter -= cycles;
    if (dividerCounter <= 0)
    {
        dividerCounter = 256+dividerCounter;
        ioRam[0x04]++;
    }
    //setEventCycles(dividerCounter);
}

void requestInterrupt(int id)
{
    ioRam[0x0F] |= id;
    if (ioRam[0x0F] & ioRam[0xFF])
        cyclesToExecute = 0;
}

void setDoubleSpeed(int val) {
    if (val == 0) {
        mode2Cycles = 456 - 80;
        mode3Cycles = 456 - 172 - 80;
        doubleSpeed = 0;
        ioRam[0x4D] &= ~0x80;
    }
    else {
        mode2Cycles = (456 - 80)*2;
        mode3Cycles = (456 - 172 - 80)*2;
        doubleSpeed = 1;
        ioRam[0x4D] |= 0x80;
    }
}
<|endoftext|>