/* Copyright (C) 2015-2016 Andrew J. Kroll
   and
Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Contact information
-------------------

Circuits At Home, LTD
Web      :  https://www.circuitsathome.com
e-mail   :  support@circuitsathome.com
 */

#ifndef MACROS_H
#define MACROS_H
#include "macro_logic.h"
/*
 * Universal Arduino(tm) "IDE" fixups.
 */

// Just in case...
#ifndef SERIAL_PORT_MONITOR
#define SERIAL_PORT_MONITOR Serial
#endif

#ifndef INT16_MIN
#define INT16_MIN -32768
#endif
// require 10607+
#if defined(ARDUINO) && ARDUINO >=10607
// nop :-)
#else
#error "Arduino version too old, and must be at least 1.6.7"
#endif

// Nuke screwed up macro junk from the IDE.
#ifdef __cplusplus
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif
#endif

#ifndef UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE

#ifndef UHS_BIG_FLASH

#if defined(FLASHEND) && defined(FLASHSTART)
#if (FLASHEND - FLASHSTART) > 0x0FFFFU
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif

#elif defined(__PIC32_FLASH_SIZE)
#if __PIC32_FLASH_SIZE > 511
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif

#elif defined(FLASHEND) && !defined(FLASHSTART)
// Assumes flash starts at 0x00000, is this a safe assumption?
// 192K + should be OK
#if FLASHEND > 0x02FFFFU
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif

#elif defined(IFLASH_SIZE)
#if IFLASH_SIZE > 0x0FFFFU
#define UHS_BIG_FLASH 1
#else
#define UHS_BIG_FLASH 0
#endif

#elif defined(ESP8266)
#define UHS_BIG_FLASH 1
#define SYSTEM_OR_SPECIAL_YIELD(...) yield()

#elif defined(__arm__) && defined(CORE_TEENSY)
#define UHS_BIG_FLASH 1

#elif defined(ARDUINO_spresense_ast)
#define UHS_BIG_FLASH 1
#else
// safe default
#warning Small flash?
#define UHS_BIG_FLASH 0
#endif
#endif

#if UHS_BIG_FLASH
#define UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE 1
#else
#define UHS_DEVICE_WINDOWS_USB_SPEC_VIOLATION_DESCRIPTOR_DEVICE 0
#endif
#endif

#if defined(__arm__) && defined(CORE_TEENSY)
#define UHS_PIN_WRITE(p, v) digitalWriteFast(p, v)
#define UHS_PIN_READ(p) digitalReadFast(p)
#endif
// TODO: Fast inline code for AVR and SAM based microcontrollers
//       This can be done pretty easily.
//       For now, this will just work out-of-the-box.
#ifndef UHS_PIN_WRITE
#define UHS_PIN_WRITE(p, v) digitalWrite(p, v)
#endif
#ifndef UHS_PIN_READ
#define UHS_PIN_READ(p) digitalRead(p)
#endif

#if defined( __PIC32MX__ ) && !defined(interrupts) // compiling with Microchip XC32 compiler
#define interrupts() __builtin_enable_interrupts()
#edfine noInterrupts() __builtin_disable_interrupts()
#endif

#ifndef ARDUINO_SAMD_ZERO
#ifdef ARDUINO_AVR_ADK
#define UHS_GET_DPI(x) (x == 54 ? 6 : digitalPinToInterrupt(x))
#else
#define UHS_GET_DPI(x) digitalPinToInterrupt(x)
#endif
#else
#define UHS_GET_DPI(x) (x)
#endif

#include "../../../../HAL/shared/progmem.h"

////////////////////////////////////////////////////////////////////////////////
// HANDY MACROS
////////////////////////////////////////////////////////////////////////////////

// Atomically set/clear single bits using bitbands.
// Believe it or not, this boils down to a constant,
// and is less code than using |= &= operators.
// Bonus, it makes code easier to read too.
// Bitbanding is a wonderful thing.
#define BITNR(i) (i&0x1?0:i&0x2?1:i&0x4?2:i&0x8?3:i&0x10?4:i&0x20?5:i&0x40?6:i&0x80?7:i&0x100?8:i&0x200?9:i&0x400?10:i&0x800?11:i&0x1000?12:i&0x2000?13:i&0x4000?14:i&0x8000?15:i&0x10000?16:i&0x20000?17:i&0x40000?18:i&0x80000?19:i&0x100000?20:i&0x200000?21:i&0x400000?22:i&0x800000?23:i&0x1000000?24:i&0x2000000?25:i&0x4000000?26:i&0x8000000?27:i&0x10000000?28:i&0x20000000?29:i&0x40000000?30:i&0x80000000?31:32)
#define UHS_KIO_BITBAND_ADDR(r, i) (((uint32_t)&(r) - 0x40000000) * 32 + (i) * 4 + 0x42000000)
#define UHS_KIO_SETBIT_ATOMIC(r, m) (*(uint32_t *)UHS_KIO_BITBAND_ADDR((r), BITNR((m)))) = 1
#define UHS_KIO_CLRBIT_ATOMIC(r, m) (*(uint32_t *)UHS_KIO_BITBAND_ADDR((r), BITNR((m)))) = 0

#define VALUE_BETWEEN(v,l,h) (((v)>(l)) && ((v)<(h)))
#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h)))
#define output_pgm_message(wa,fp,mp,el) wa = &mp, fp((char *)pgm_read_pointer(wa), el)
#define output_if_between(v,l,h,wa,fp,mp,el) if(VALUE_BETWEEN(v,l,h)) output_pgm_message(wa,fp,mp[v-(l+1)],el);

#define UHS_SWAP_VALUES(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))
#ifndef __BYTE_GRABBING_DEFINED__
#define __BYTE_GRABBING_DEFINED__ 1
#ifdef BROKEN_OPTIMIZER_LITTLE_ENDIAN
// Note: Use this if your compiler generates horrible assembler!
#define UHS_UINT8_BYTE0(__usi__)  (((uint8_t *)&(__usi__))[0])
#define UHS_UINT8_BYTE1(__usi__)  (((uint8_t *)&(__usi__))[1])
#define UHS_UINT8_BYTE2(__usi__)  (((uint8_t *)&(__usi__))[2])
#define UHS_UINT8_BYTE3(__usi__)  (((uint8_t *)&(__usi__))[3])
#define UHS_UINT8_BYTE4(__usi__)  (((uint8_t *)&(__usi__))[4])
#define UHS_UINT8_BYTE5(__usi__)  (((uint8_t *)&(__usi__))[5])
#define UHS_UINT8_BYTE6(__usi__)  (((uint8_t *)&(__usi__))[6])
#define UHS_UINT8_BYTE7(__usi__)  (((uint8_t *)&(__usi__))[7])
#else
// Note: The cast alone to uint8_t is actually enough.
// GCC throws out the "& 0xFF", and the size is no different.
// Some compilers need it.
#define UHS_UINT8_BYTE0(__usi__)  ((uint8_t)((__usi__) & 0xFF ))
#define UHS_UINT8_BYTE1(__usi__)  ((uint8_t)(((__usi__) >> 8) & 0xFF))
#define UHS_UINT8_BYTE2(__usi__)  ((uint8_t)(((__usi__) >> 16) & 0xFF))
#define UHS_UINT8_BYTE3(__usi__)  ((uint8_t)(((__usi__) >> 24) & 0xFF))
#define UHS_UINT8_BYTE4(__usi__)  ((uint8_t)(((__usi__) >> 32) & 0xFF))
#define UHS_UINT8_BYTE5(__usi__)  ((uint8_t)(((__usi__) >> 40) & 0xFF))
#define UHS_UINT8_BYTE6(__usi__)  ((uint8_t)(((__usi__) >> 48) & 0xFF))
#define UHS_UINT8_BYTE7(__usi__)  ((uint8_t)(((__usi__) >> 56) & 0xFF))
#endif
#define UHS_UINT16_SET_BYTE1(__usi__)  ((uint16_t)(__usi__) << 8)
#define UHS_UINT32_SET_BYTE1(__usi__)  ((uint32_t)(__usi__) << 8)
#define UHS_UINT64_SET_BYTE1(__usi__)  ((uint64_t)(__usi__) << 8)
#define UHS_UINT32_SET_BYTE2(__usi__)  ((uint32_t)(__usi__) << 16)
#define UHS_UINT64_SET_BYTE2(__usi__)  ((uint64_t)(__usi__) << 16)
#define UHS_UINT32_SET_BYTE3(__usi__)  ((uint32_t)(__usi__) << 24)
#define UHS_UINT64_SET_BYTE3(__usi__)  ((uint64_t)(__usi__) << 24)
#define UHS_UINT64_SET_BYTE4(__usi__)  ((uint64_t)(__usi__) << 32)
#define UHS_UINT64_SET_BYTE5(__usi__)  ((uint64_t)(__usi__) << 40)
#define UHS_UINT64_SET_BYTE6(__usi__)  ((uint64_t)(__usi__) << 48)
#define UHS_UINT64_SET_BYTE7(__usi__)  ((uint64_t)(__usi__) << 56)

// These are the smallest and fastest ways I have found so far in pure C/C++.
#define UHS_BYTES_TO_UINT16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)UHS_UINT16_SET_BYTE1(__usc1__)))
#define UHS_BYTES_TO_UINT32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | UHS_UINT32_SET_BYTE1(__usc1__) | UHS_UINT32_SET_BYTE2(__usc2__) | UHS_UINT32_SET_BYTE3(__usc3__)))
#define UHS_BYTES_TO_UINT64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | UHS_UINT64_SET_BYTE1(__usc1__) | UHS_UINT64_SET_BYTE2(__usc2__) | UHS_UINT64_SET_BYTE3(__usc3__) | UHS_UINT64_SET_BYTE4(__usc4__) | UHS_UINT64_SET_BYTE5(__usc5__) | UHS_UINT64_SET_BYTE6(__usc6__) | UHS_UINT64_SET_BYTE7(__usc7__)))
#endif
/*
 * Debug macros.
 * Useful when porting from UHS2.
 * Do not use these for any new code.
 * Change to better debugging after port is completed.
 * Strings are stored in progmem (flash) instead of RAM.
 */
#define USBTRACE1(s,l) (Notify(PSTR(s), l))
#define USBTRACE(s) (USBTRACE1((s), 0x80)); USB_HOST_SERIAL.flush()
#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR("\r\n"), l))
#define USBTRACE3X(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l))
#define USBTRACE2(s,r) (USBTRACE3((s),(r),0x80)); USB_HOST_SERIAL.flush()
#define USBTRACE2X(s,r) (USBTRACE3X((s),(r),0x80)); USB_HOST_SERIAL.flush()

#define VOID0 ((void)0)
#ifndef NOTUSED
#define NOTUSED(...)  __VA_ARGS__ __attribute__((unused))
#endif
#endif /* MACROS_H */
