DxCore icon indicating copy to clipboard operation
DxCore copied to clipboard

Improvements to pinConfigure

Open MCUdude opened this issue 3 years ago • 24 comments

Due to limited time the last six months (I have two small kids now, life is busy...) I haven't been able to catch up to all the work you have been putting into DxCore and megaTinyCore.

Anyways, I decided to implement pinConfigure to MegaCoreX, because I see the benefits of having such functionality. It works well, but is so compact and optimized it's very difficult to keep track of what's going on without reading in the datasheets and the io.h file at the same time.

The first improvement would be to convert this into a template function. I've done it, and it works great. Instead of having to OR the pin settings together, you can just add them as separate parameters.

pinConfigure(PIN_PA0, PIN_DIR_OUT);
pinConfigure(PIN_PA1, PIN_DIR_OUT, PIN_OUT_HIGH, PIN_INVERT_SET);
pinConfigure(PIN_PA1, PIN_DIR_IN, PIN_PULLUP_SET);

The second improvement is to use a typedef enum instead of defined constants that provides no guard rails. By using enums, the user is only allowed to pass the PIN constants, unless not casted. We done need any ::'s The will still appear and work with

What do you think? Here's what all this looks like (Because this is for MegaCoreX, I've removed the MVIO part):

typedef enum
{
 // OUTPUT
  PIN_DIR_SET        = 0x0001,
  PIN_DIRSET         = 0x0001,
  PIN_DIR_OUTPUT     = 0x0001,
  PIN_DIR_OUT        = 0x0001,
 // INPUT
  PIN_DIR_CLR        = 0x0002,
  PIN_DIRCLR         = 0x0002,
  PIN_DIR_INPUT      = 0x0002,
  PIN_DIR_IN         = 0x0002,
 // TOGGLE INPUT/OUTPUT
  PIN_DIR_TGL        = 0x0003,
  PIN_DIRTGL         = 0x0003,
  PIN_DIR_TOGGLE     = 0x0003,
 // HIGH
  PIN_OUT_SET        = 0x0004,
  PIN_OUTSET         = 0x0004,
  PIN_OUT_HIGH       = 0x0004,
 // LOW
  PIN_OUT_CLR        = 0x0008,
  PIN_OUTCLR         = 0x0008,
  PIN_OUT_LOW        = 0x0008,
// CHANGE/TOGGLE
  PIN_OUT_TGL        = 0x000C,
  PIN_OUTTGL         = 0x000C,
  PIN_OUT_TOGGLE     = 0x000C,
//Interrupt disabled but input buffer enabled
  PIN_ISC_ENABLE     = 0x0080,
  PIN_INPUT_ENABLE   = 0x0080,
 // Interrupt on change
  PIN_ISC_CHANGE     = 0x0090,
  PIN_INT_CHANGE     = 0x0090,
// Interrupt on rising edge
  PIN_ISC_RISE       = 0x00A0,
  PIN_INT_RISE       = 0x00A0,
// Interrupt on falling edge
  PIN_ISC_FALL       = 0x00B0,
  PIN_INT_FALL       = 0x00B0,
// Interrupt and input buffer disabled
  PIN_ISC_DISABLE    = 0x00C0,
  PIN_INPUT_DISABLE  = 0x00C0,
// Interrupt enabled with sense on low level
  PIN_ISC_LEVEL      = 0x00D0,
  PIN_INT_LEVEL      = 0x00D0,
// PULLUP ON
  PIN_PULLUP_ON      = 0x0100,
  PIN_PULLUP         = 0x0100,
  PIN_PULLUP_SET     = 0x0100,
// PULLUP OFF
  PIN_PULLUP_OFF     = 0x0200,
  PIN_PULLUP_CLR     = 0x0200,
  PIN_NOPULLUP       = 0x0200,
// PULLUP TOGGLE
  PIN_PULLUP_TGL     = 0x0300,
  PIN_PULLUP_TOGGLE  = 0x0300,
// PIN INVERT ON
  PIN_INVERT_ON      = 0x4000,
  PIN_INVERT_SET     = 0x4000,
// PIN INVERT OFF
  PIN_INVERT_OFF     = 0x8000,
  PIN_INVERT_CLR     = 0x8000,
// PIN_INVERT_TOGGLE
  PIN_INVERT_TGL     = 0xC000,
  PIN_INVERT_TOGGLE  = 0xC000,
} pin_configure_t;


// Helper function to catch the last argument
pin_configure_t pinConfigure(const uint8_t digital_pin, const pin_configure_t mode)
{
  (void)digital_pin;
  return mode;
}

// PinConfigure variadic template function
template<typename MODE, typename... MODES>
void pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
{
  // Start by or-ing together the arguments
  uint8_t pin_config = pinConfigure(digital_pin, mode) | pinConfigure(digital_pin, modes...);
 
  uint8_t bit_mask = digitalPinToBitMask(digital_pin);
  if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
    return;

  uint8_t bit_pos  = digitalPinToBitPosition(digital_pin);
  volatile uint8_t *portbase = (volatile uint8_t*) digitalPinToPortStruct(digital_pin);

  // Write to selected pin direction register
  uint8_t setting = pin_config & 0x03; // Mask out direction bits (DIR, DIRSET, DIRCLR, DIRTGL)
  if(setting) 
    *(portbase + setting) = bit_mask;

  // Write to selected output register
  pin_config >>= 2;
  setting = pin_config & 0x03;
  if(setting)
    *(portbase + 4 + setting) = bit_mask;

  // Return if there is nothing more to configure
  if(!(pin_config & 0x3FFC))
    return;

  uint8_t oldSREG = SREG; // Store SREG
  cli(); // Disable interrupts

  // PINnCTRL register
  pin_config >>= 2;
  uint8_t pinncfg = *(portbase + 0x10 + bit_pos);
  // Input sense configuration (ISC)
  if(pin_config & 0x08)
    pinncfg = (pinncfg & 0xF8) | (pin_config & PORT_ISC_gm);
  // Pullup resistor
  uint8_t temp = pin_config & 0x30;
  if(temp)
  {
    if(temp == 0x30)
      pinncfg ^= PORT_PULLUPEN_bm;    // Toggle pullup
    else if(temp == 0x20)
      pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
    else
      pinncfg |= PORT_PULLUPEN_bm;    // Set pullup
  }
  // Invert pin
  pin_config >>= 8;
  temp = pin_config & 0x0C;
  if(temp)
  {
    if(temp == 0x0C)
      pinncfg ^= PORT_INVEN_bm;    // Toggle invert
    else if(temp == 0x08)
      pinncfg &= ~(PORT_INVEN_bm); // Clear
    else
      pinncfg |= PORT_INVEN_bm;    // Set
  }
  // Write to PINnCTRL register
  *(portbase + 0x10 + bit_pos) = pinncfg;

  // Restore SREG
  SREG = oldSREG;
}

MCUdude avatar Dec 20 '21 21:12 MCUdude

Damn, that looks awesome - I was never happy with the need to OR the constants together, but templates are a dark art which I do not have experience with.

SpenceKonde avatar Dec 23 '21 05:12 SpenceKonde

Hmm, okay - so I do NOT know how to make that compile... Which isnt surprising since I don't really know how to use templates much less variadic ones.

Though uint8_t pin_config should definitely be uint16_t

SpenceKonde avatar Dec 26 '21 09:12 SpenceKonde

The issue may be because your cores already have pinConfigure defined. Try removing or renaming them. Here is an updated version where pin_configure_t is a 16-bit enum.

// Compiles with MegaCoreX
typedef enum : uint16_t
{
 // OUTPUT
  PIN_DIR_SET        = 0x0001,
  PIN_DIRSET         = 0x0001,
  PIN_DIR_OUTPUT     = 0x0001,
  PIN_DIR_OUT        = 0x0001,
 // INPUT
  PIN_DIR_CLR        = 0x0002,
  PIN_DIRCLR         = 0x0002,
  PIN_DIR_INPUT      = 0x0002,
  PIN_DIR_IN         = 0x0002,
 // TOGGLE INPUT/OUTPUT
  PIN_DIR_TGL        = 0x0003,
  PIN_DIRTGL         = 0x0003,
  PIN_DIR_TOGGLE     = 0x0003,
 // HIGH
  PIN_OUT_SET        = 0x0004,
  PIN_OUTSET         = 0x0004,
  PIN_OUT_HIGH       = 0x0004,
 // LOW
  PIN_OUT_CLR        = 0x0008,
  PIN_OUTCLR         = 0x0008,
  PIN_OUT_LOW        = 0x0008,
// CHANGE/TOGGLE
  PIN_OUT_TGL        = 0x000C,
  PIN_OUTTGL         = 0x000C,
  PIN_OUT_TOGGLE     = 0x000C,
//Interrupt disabled but input buffer enabled
  PIN_ISC_ENABLE     = 0x0080,
  PIN_INPUT_ENABLE   = 0x0080,
 // Interrupt on change
  PIN_ISC_CHANGE     = 0x0090,
  PIN_INT_CHANGE     = 0x0090,
// Interrupt on rising edge
  PIN_ISC_RISE       = 0x00A0,
  PIN_INT_RISE       = 0x00A0,
// Interrupt on falling edge
  PIN_ISC_FALL       = 0x00B0,
  PIN_INT_FALL       = 0x00B0,
// Interrupt and input buffer disabled
  PIN_ISC_DISABLE    = 0x00C0,
  PIN_INPUT_DISABLE  = 0x00C0,
// Interrupt enabled with sense on low level
  PIN_ISC_LEVEL      = 0x00D0,
  PIN_INT_LEVEL      = 0x00D0,
// PULLUP ON
  PIN_PULLUP_ON      = 0x0100,
  PIN_PULLUP         = 0x0100,
  PIN_PULLUP_SET     = 0x0100,
// PULLUP OFF
  PIN_PULLUP_OFF     = 0x0200,
  PIN_PULLUP_CLR     = 0x0200,
  PIN_NOPULLUP       = 0x0200,
// PULLUP TOGGLE
  PIN_PULLUP_TGL     = 0x0300,
  PIN_PULLUP_TOGGLE  = 0x0300,
// PIN INVERT ON
  PIN_INVERT_ON      = 0x4000,
  PIN_INVERT_SET     = 0x4000,
// PIN INVERT OFF
  PIN_INVERT_OFF     = 0x8000,
  PIN_INVERT_CLR     = 0x8000,
// PIN_INVERT_TOGGLE
  PIN_INVERT_TGL     = 0xC000,
  PIN_INVERT_TOGGLE  = 0xC000,
} pin_configure_t;



pin_configure_t pinConfigure(const uint8_t digital_pin, const pin_configure_t mode)
{
  (void)digital_pin;
  return mode;
}

template <typename MODE, typename... MODES>
void pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
{
  // Start by or-ing together the arguments
  uint8_t pin_config = pinConfigure(digital_pin, mode) | pinConfigure(digital_pin, modes...);
 
  uint8_t bit_mask = digitalPinToBitMask(digital_pin);
  if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
    return;

  uint8_t bit_pos  = digitalPinToBitPosition(digital_pin);
  volatile uint8_t *portbase = (volatile uint8_t*) digitalPinToPortStruct(digital_pin);

  // Write to selected pin direction register
  uint8_t setting = pin_config & 0x03; // Mask out direction bits (DIR, DIRSET, DIRCLR, DIRTGL)
  if(setting) 
    *(portbase + setting) = bit_mask;

  // Write to selected output register
  pin_config >>= 2;
  setting = pin_config & 0x03;
  if(setting)
    *(portbase + 4 + setting) = bit_mask;

  // Return if there is nothing more to configure
  if(!(pin_config & 0x3FFC))
    return;

  uint8_t oldSREG = SREG; // Store SREG
  cli(); // Disable interrupts

  // PINnCTRL register
  pin_config >>= 2;
  uint8_t pinncfg = *(portbase + 0x10 + bit_pos);
  // Input sense configuration (ISC)
  if(pin_config & 0x08)
    pinncfg = (pinncfg & 0xF8) | (pin_config & PORT_ISC_gm);
  // Pullup resistor
  uint8_t temp = pin_config & 0x30;
  if(temp)
  {
    if(temp == 0x30)
      pinncfg ^= PORT_PULLUPEN_bm;    // Toggle pullup
    else if(temp == 0x20)
      pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
    else
      pinncfg |= PORT_PULLUPEN_bm;    // Set pullup
  }
  // Invert pin
  pin_config >>= 8;
  temp = pin_config & 0x0C;
  if(temp)
  {
    if(temp == 0x0C)
      pinncfg ^= PORT_INVEN_bm;    // Toggle invert
    else if(temp == 0x08)
      pinncfg &= ~(PORT_INVEN_bm); // Clear
    else
      pinncfg |= PORT_INVEN_bm;    // Set
  }
  // Write to PINnCTRL register
  *(portbase + 0x10 + bit_pos) = pinncfg;

  // Restore SREG
  SREG = oldSREG;
}


void setup() {

}

void loop() {

  pinConfigure(13, PIN_OUT_SET, PIN_DIRCLR);
  delay(1000);

}

MCUdude avatar Dec 26 '21 10:12 MCUdude

Ah, you need inline in order to get it to compile:

https://github.com/MCUdude/MegaCoreX/commit/47ef471683d9856af8147705d410bf096ba282c8

MCUdude avatar Dec 26 '21 20:12 MCUdude

Hmm, uhhh...

uint8_t pin_config = pinConfigure(digital_pin, mode) | pinConfigure(digital_pin, modes...);

pinConfigure with more than two modes fails to compile, complaining of there being no operator | defined for pin_configure_t and void - because pinConfigure with a single mode argument calls the helper function which returns a pin_configure_t, while the one with more argument options returns void. This looks like a tricky thing to fix if I'm not missing something.

SpenceKonde avatar Dec 30 '21 09:12 SpenceKonde

This looks like a tricky thing to fix if I'm not missing something.

Happy new year!

Nah, just me being dumb. Fixed now. https://github.com/MCUdude/MegaCoreX/commit/a7dd83c062ea4f0284bf243bbe4c4025e7dcd979

MCUdude avatar Jan 01 '22 19:01 MCUdude

Happy new year to you!

Huh, okay, yeah, now it;s compiling and looks to be working. I notice that the flash usage of a single call to it is competitive with a call to the old version, but as you call it more, it's flash usage grows markedly faster... Do you happen to know why this would be?

SpenceKonde avatar Jan 01 '22 19:01 SpenceKonde

Huh, okay, yeah, now it;s compiling and looks to be working. I notice that the flash usage of a single call to it is competitive with a call to the old version, but as you call it more, it's flash usage grows markedly faster... Do you happen to know why this would be?

I'm not sure why it's that much larger. The thing about templates is that things are figured out at compile time, so in theory all the ORing and function calling should have been done by the compiler.

Have a look at the OR template function below. Give it a try if you have time. No matter what you put into it, as long as it's know at compile time, the size will not grow much at all.

template<typename T>
T orer(T first) {
  return first;
}

template<typename T, typename... Args>
T orer(T first, Args... args) {
  return first | orer(args...);
}

// call orer(arg1, arg2, arg3 etc)

MCUdude avatar Jan 01 '22 21:01 MCUdude

Based on

void setup() {
  pinConfigure(PIN_PA7, PIN_PULLUP_ON | PIN_ISC_DISABLE | PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_DIR_SET | PIN_INPUT_ENABLE | PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA6, PIN_PULLUP_ON | PIN_ISC_DISABLE| PIN_INVERT_ON);
  pinConfigure(PIN_PA6, PIN_DIR_SET | PIN_INPUT_ENABLE| PIN_INVERT_ON);
  pinConfigure(PIN_PA6, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA5, PIN_PULLUP_ON | PIN_ISC_DISABLE| PIN_INVERT_ON);
  pinConfigure(PIN_PA5, PIN_DIR_SET | PIN_INPUT_ENABLE| PIN_INVERT_ON);
  pinConfigure(PIN_PA5, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA4, PIN_PULLUP_ON | PIN_ISC_DISABLE| PIN_INVERT_ON);
  pinConfigure(PIN_PA4, PIN_DIR_SET | PIN_INPUT_ENABLE| PIN_INVERT_ON);
  pinConfigure(PIN_PA4, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
}
void loop() {
  
}

vs

void setup() {
  pinConfigure(PIN_PA7, PIN_PULLUP_ON | PIN_ISC_DISABLE | PIN_INVERT_ON);
  pinConfigure(PIN_PA4, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
}
void loop() {
}

744 vs 496 24.8 bytes per call.

With the equivalent (substitute |'s for commas): 1424 vs 1070 35.4 bytes per call.

Now compare:


void setup() {
  pinConfigure(PIN_PA7, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA6, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA5, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA4, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA3, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA2, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA1, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PB0, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
}
void loop() {
  
}

with

void setup() {
  pinConfigure(PIN_PA7, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA7, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
  pinConfigure(PIN_PA7, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
  pinConfigure(PIN_PA7, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
}
void loop() {
  
}

1220 vs 1076 That's a 20 byte overhead for a different pin, whereas the overhead is only 6 bytes with the classic implementation.

What's even more horrifying is the comparison of the generated assembly in the calling section (shown for the first example with the 12 calls):

  pinConfigure(PIN_PA7, PIN_PULLUP_ON | PIN_ISC_DISABLE | PIN_INVERT_ON);
 24c: 60 ec         ldi r22, 0xC0 ; 192
 24e: 71 e4         ldi r23, 0x41 ; 65
 250: 83 e0         ldi r24, 0x03 ; 3
 252: 31 df         rcall .-414     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA7, PIN_DIR_SET | PIN_INPUT_ENABLE | PIN_INVERT_ON);
 254: 61 e8         ldi r22, 0x81 ; 129
 256: 70 e4         ldi r23, 0x40 ; 64
 258: 83 e0         ldi r24, 0x03 ; 3
 25a: 2d df         rcall .-422     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA7, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
 25c: 69 ec         ldi r22, 0xC9 ; 201
 25e: 70 e0         ldi r23, 0x00 ; 0
 260: 83 e0         ldi r24, 0x03 ; 3
 262: 29 df         rcall .-430     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA6, PIN_PULLUP_ON | PIN_ISC_DISABLE| PIN_INVERT_ON);
 264: 60 ec         ldi r22, 0xC0 ; 192
 266: 71 e4         ldi r23, 0x41 ; 65
 268: 82 e0         ldi r24, 0x02 ; 2
 26a: 25 df         rcall .-438     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA6, PIN_DIR_SET | PIN_INPUT_ENABLE| PIN_INVERT_ON);
 26c: 61 e8         ldi r22, 0x81 ; 129
 26e: 70 e4         ldi r23, 0x40 ; 64
 270: 82 e0         ldi r24, 0x02 ; 2
 272: 21 df         rcall .-446     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA6, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
 274: 69 ec         ldi r22, 0xC9 ; 201
 276: 70 e0         ldi r23, 0x00 ; 0
 278: 82 e0         ldi r24, 0x02 ; 2
 27a: 1d df         rcall .-454     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA5, PIN_PULLUP_ON | PIN_ISC_DISABLE| PIN_INVERT_ON);
 27c: 60 ec         ldi r22, 0xC0 ; 192
 27e: 71 e4         ldi r23, 0x41 ; 65
 280: 81 e0         ldi r24, 0x01 ; 1
 282: 19 df         rcall .-462     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA5, PIN_DIR_SET | PIN_INPUT_ENABLE| PIN_INVERT_ON);
 284: 61 e8         ldi r22, 0x81 ; 129
 286: 70 e4         ldi r23, 0x40 ; 64
 288: 81 e0         ldi r24, 0x01 ; 1
 28a: 15 df         rcall .-470     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA5, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
 28c: 69 ec         ldi r22, 0xC9 ; 201
 28e: 70 e0         ldi r23, 0x00 ; 0
 290: 81 e0         ldi r24, 0x01 ; 1
 292: 11 df         rcall .-478     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA4, PIN_PULLUP_ON | PIN_ISC_DISABLE| PIN_INVERT_ON);
 294: 60 ec         ldi r22, 0xC0 ; 192
 296: 71 e4         ldi r23, 0x41 ; 65
 298: 80 e0         ldi r24, 0x00 ; 0
 29a: 0d df         rcall .-486     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA4, PIN_DIR_SET | PIN_INPUT_ENABLE| PIN_INVERT_ON);
 29c: 61 e8         ldi r22, 0x81 ; 129
 29e: 70 e4         ldi r23, 0x40 ; 64
 2a0: 80 e0         ldi r24, 0x00 ; 0
 2a2: 09 df         rcall .-494     ; 0xb6 <pinConfigure>
  pinConfigure(PIN_PA4, PIN_OUT_CLR | PIN_DIR_SET| PIN_INPUT_DISABLE);
 2a4: 69 ec         ldi r22, 0xC9 ; 201
 2a6: 70 e0         ldi r23, 0x00 ; 0
 2a8: 80 e0         ldi r24, 0x00 ; 0
 2aa: 05 df         rcall .-502     ; 0xb6 <pinConfigure>

Vs this - somehow it is doing a horrifyingly bad job of optimizing that.

   pinConfigure(PIN_PA7, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
 38e: c1 2c         mov r12, r1
 390: 80 e4         ldi r24, 0x40 ; 64
 392: d8 2e         mov r13, r24
 394: c9 82         std Y+1, r12  ; 0x01
 396: da 82         std Y+2, r13  ; 0x02
 398: 90 ec         ldi r25, 0xC0 ; 192
 39a: e9 2e         mov r14, r25
 39c: f1 2c         mov r15, r1
 39e: eb 82         std Y+3, r14  ; 0x03
 3a0: fc 82         std Y+4, r15  ; 0x04
 3a2: 81 2c         mov r8, r1
 3a4: 99 24         eor r9, r9
 3a6: 93 94         inc r9
 3a8: 8d 82         std Y+5, r8 ; 0x05
 3aa: 9e 82         std Y+6, r9 ; 0x06
 3ac: 9e 01         movw  r18, r28
 3ae: 2f 5f         subi  r18, 0xFF ; 255
 3b0: 3f 4f         sbci  r19, 0xFF ; 255
 3b2: ae 01         movw  r20, r28
 3b4: 4d 5f         subi  r20, 0xFD ; 253
 3b6: 5f 4f         sbci  r21, 0xFF ; 255
 3b8: be 01         movw  r22, r28
 3ba: 6b 5f         subi  r22, 0xFB ; 251
 3bc: 7f 4f         sbci  r23, 0xFF ; 255
 3be: 83 e0         ldi r24, 0x03 ; 3
 3c0: 7a de         rcall .-780     ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA7, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
 3c2: c9 82         std Y+1, r12  ; 0x01
 3c4: da 82         std Y+2, r13  ; 0x02
 3c6: 30 e8         ldi r19, 0x80 ; 128
 3c8: a3 2e         mov r10, r19
 3ca: b1 2c         mov r11, r1
 3cc: ab 82         std Y+3, r10  ; 0x03
 3ce: bc 82         std Y+4, r11  ; 0x04
 3d0: 01 e0         ldi r16, 0x01 ; 1
 3d2: 10 e0         ldi r17, 0x00 ; 0
 3d4: 0d 83         std Y+5, r16  ; 0x05
 3d6: 1e 83         std Y+6, r17  ; 0x06
 3d8: 9e 01         movw  r18, r28
 3da: 2f 5f         subi  r18, 0xFF ; 255
 3dc: 3f 4f         sbci  r19, 0xFF ; 255
 3de: ae 01         movw  r20, r28
 3e0: 4d 5f         subi  r20, 0xFD ; 253
 3e2: 5f 4f         sbci  r21, 0xFF ; 255
 3e4: be 01         movw  r22, r28
 3e6: 6b 5f         subi  r22, 0xFB ; 251
 3e8: 7f 4f         sbci  r23, 0xFF ; 255
 3ea: 83 e0         ldi r24, 0x03 ; 3
 3ec: 64 de         rcall .-824     ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA7, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
 3ee: e9 82         std Y+1, r14  ; 0x01
 3f0: fa 82         std Y+2, r15  ; 0x02
 3f2: 0b 83         std Y+3, r16  ; 0x03
 3f4: 1c 83         std Y+4, r17  ; 0x04
 3f6: 48 e0         ldi r20, 0x08 ; 8
 3f8: 64 2e         mov r6, r20
 3fa: 71 2c         mov r7, r1
 3fc: 6d 82         std Y+5, r6 ; 0x05
 3fe: 7e 82         std Y+6, r7 ; 0x06
 400: 9e 01         movw  r18, r28
 402: 2f 5f         subi  r18, 0xFF ; 255
 404: 3f 4f         sbci  r19, 0xFF ; 255
 406: ae 01         movw  r20, r28
 408: 4d 5f         subi  r20, 0xFD ; 253
 40a: 5f 4f         sbci  r21, 0xFF ; 255
 40c: be 01         movw  r22, r28
 40e: 6b 5f         subi  r22, 0xFB ; 251
 410: 7f 4f         sbci  r23, 0xFF ; 255
 412: 83 e0         ldi r24, 0x03 ; 3
 414: 50 de         rcall .-864     ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA6, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
 416: c9 82         std Y+1, r12  ; 0x01
 418: da 82         std Y+2, r13  ; 0x02
 41a: eb 82         std Y+3, r14  ; 0x03
 41c: fc 82         std Y+4, r15  ; 0x04
 41e: 8d 82         std Y+5, r8 ; 0x05
 420: 9e 82         std Y+6, r9 ; 0x06
 422: 9e 01         movw  r18, r28
 424: 2f 5f         subi  r18, 0xFF ; 255
 426: 3f 4f         sbci  r19, 0xFF ; 255
 428: ae 01         movw  r20, r28
 42a: 4d 5f         subi  r20, 0xFD ; 253
 42c: 5f 4f         sbci  r21, 0xFF ; 255
 42e: be 01         movw  r22, r28
 430: 6b 5f         subi  r22, 0xFB ; 251
 432: 7f 4f         sbci  r23, 0xFF ; 255
 434: 82 e0         ldi r24, 0x02 ; 2
 436: 3f de         rcall .-898     ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA6, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
 438: c9 82         std Y+1, r12  ; 0x01
 43a: da 82         std Y+2, r13  ; 0x02
 43c: ab 82         std Y+3, r10  ; 0x03
 43e: bc 82         std Y+4, r11  ; 0x04
 440: 0d 83         std Y+5, r16  ; 0x05
 442: 1e 83         std Y+6, r17  ; 0x06
 444: 9e 01         movw  r18, r28
 446: 2f 5f         subi  r18, 0xFF ; 255
 448: 3f 4f         sbci  r19, 0xFF ; 255
 44a: ae 01         movw  r20, r28
 44c: 4d 5f         subi  r20, 0xFD ; 253
 44e: 5f 4f         sbci  r21, 0xFF ; 255
 450: be 01         movw  r22, r28
 452: 6b 5f         subi  r22, 0xFB ; 251
 454: 7f 4f         sbci  r23, 0xFF ; 255
 456: 82 e0         ldi r24, 0x02 ; 2
 458: 2e de         rcall .-932     ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA6, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
 45a: e9 82         std Y+1, r14  ; 0x01
 45c: fa 82         std Y+2, r15  ; 0x02
 45e: 0b 83         std Y+3, r16  ; 0x03
 460: 1c 83         std Y+4, r17  ; 0x04
 462: 6d 82         std Y+5, r6 ; 0x05
 464: 7e 82         std Y+6, r7 ; 0x06
 466: 9e 01         movw  r18, r28
 468: 2f 5f         subi  r18, 0xFF ; 255
 46a: 3f 4f         sbci  r19, 0xFF ; 255
 46c: ae 01         movw  r20, r28
 46e: 4d 5f         subi  r20, 0xFD ; 253
 470: 5f 4f         sbci  r21, 0xFF ; 255
 472: be 01         movw  r22, r28
 474: 6b 5f         subi  r22, 0xFB ; 251
 476: 7f 4f         sbci  r23, 0xFF ; 255
 478: 82 e0         ldi r24, 0x02 ; 2
 47a: 1d de         rcall .-966     ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA5, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
 47c: c9 82         std Y+1, r12  ; 0x01
 47e: da 82         std Y+2, r13  ; 0x02
 480: eb 82         std Y+3, r14  ; 0x03
 482: fc 82         std Y+4, r15  ; 0x04
 484: 8d 82         std Y+5, r8 ; 0x05
 486: 9e 82         std Y+6, r9 ; 0x06
 488: 9e 01         movw  r18, r28
 48a: 2f 5f         subi  r18, 0xFF ; 255
 48c: 3f 4f         sbci  r19, 0xFF ; 255
 48e: ae 01         movw  r20, r28
 490: 4d 5f         subi  r20, 0xFD ; 253
 492: 5f 4f         sbci  r21, 0xFF ; 255
 494: be 01         movw  r22, r28
 496: 6b 5f         subi  r22, 0xFB ; 251
 498: 7f 4f         sbci  r23, 0xFF ; 255
 49a: 81 e0         ldi r24, 0x01 ; 1
 49c: 0c de         rcall .-1000    ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA5, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
 49e: c9 82         std Y+1, r12  ; 0x01
 4a0: da 82         std Y+2, r13  ; 0x02
 4a2: ab 82         std Y+3, r10  ; 0x03
 4a4: bc 82         std Y+4, r11  ; 0x04
 4a6: 0d 83         std Y+5, r16  ; 0x05
 4a8: 1e 83         std Y+6, r17  ; 0x06
 4aa: 9e 01         movw  r18, r28
 4ac: 2f 5f         subi  r18, 0xFF ; 255
 4ae: 3f 4f         sbci  r19, 0xFF ; 255
 4b0: ae 01         movw  r20, r28
 4b2: 4d 5f         subi  r20, 0xFD ; 253
 4b4: 5f 4f         sbci  r21, 0xFF ; 255
 4b6: be 01         movw  r22, r28
 4b8: 6b 5f         subi  r22, 0xFB ; 251
 4ba: 7f 4f         sbci  r23, 0xFF ; 255
 4bc: 81 e0         ldi r24, 0x01 ; 1
 4be: fb dd         rcall .-1034    ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA5, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
 4c0: e9 82         std Y+1, r14  ; 0x01
 4c2: fa 82         std Y+2, r15  ; 0x02
 4c4: 0b 83         std Y+3, r16  ; 0x03
 4c6: 1c 83         std Y+4, r17  ; 0x04
 4c8: 6d 82         std Y+5, r6 ; 0x05
 4ca: 7e 82         std Y+6, r7 ; 0x06
 4cc: 9e 01         movw  r18, r28
 4ce: 2f 5f         subi  r18, 0xFF ; 255
 4d0: 3f 4f         sbci  r19, 0xFF ; 255
 4d2: ae 01         movw  r20, r28
 4d4: 4d 5f         subi  r20, 0xFD ; 253
 4d6: 5f 4f         sbci  r21, 0xFF ; 255
 4d8: be 01         movw  r22, r28
 4da: 6b 5f         subi  r22, 0xFB ; 251
 4dc: 7f 4f         sbci  r23, 0xFF ; 255
 4de: 81 e0         ldi r24, 0x01 ; 1
 4e0: ea dd         rcall .-1068    ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA4, PIN_PULLUP_ON , PIN_ISC_DISABLE, PIN_INVERT_ON);
 4e2: c9 82         std Y+1, r12  ; 0x01
 4e4: da 82         std Y+2, r13  ; 0x02
 4e6: eb 82         std Y+3, r14  ; 0x03
 4e8: fc 82         std Y+4, r15  ; 0x04
 4ea: 8d 82         std Y+5, r8 ; 0x05
 4ec: 9e 82         std Y+6, r9 ; 0x06
 4ee: 9e 01         movw  r18, r28
 4f0: 2f 5f         subi  r18, 0xFF ; 255
 4f2: 3f 4f         sbci  r19, 0xFF ; 255
 4f4: ae 01         movw  r20, r28
 4f6: 4d 5f         subi  r20, 0xFD ; 253
 4f8: 5f 4f         sbci  r21, 0xFF ; 255
 4fa: be 01         movw  r22, r28
 4fc: 6b 5f         subi  r22, 0xFB ; 251
 4fe: 7f 4f         sbci  r23, 0xFF ; 255
 500: 80 e0         ldi r24, 0x00 ; 0
 502: d9 dd         rcall .-1102    ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA4, PIN_DIR_SET , PIN_INPUT_ENABLE, PIN_INVERT_ON);
 504: c9 82         std Y+1, r12  ; 0x01
 506: da 82         std Y+2, r13  ; 0x02
 508: ab 82         std Y+3, r10  ; 0x03
 50a: bc 82         std Y+4, r11  ; 0x04
 50c: 0d 83         std Y+5, r16  ; 0x05
 50e: 1e 83         std Y+6, r17  ; 0x06
 510: 9e 01         movw  r18, r28
 512: 2f 5f         subi  r18, 0xFF ; 255
 514: 3f 4f         sbci  r19, 0xFF ; 255
 516: ae 01         movw  r20, r28
 518: 4d 5f         subi  r20, 0xFD ; 253
 51a: 5f 4f         sbci  r21, 0xFF ; 255
 51c: be 01         movw  r22, r28
 51e: 6b 5f         subi  r22, 0xFB ; 251
 520: 7f 4f         sbci  r23, 0xFF ; 255
 522: 80 e0         ldi r24, 0x00 ; 0
 524: c8 dd         rcall .-1136    ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>
  pinConfigure(PIN_PA4, PIN_OUT_CLR , PIN_DIR_SET, PIN_INPUT_DISABLE);
 526: e9 82         std Y+1, r14  ; 0x01
 528: fa 82         std Y+2, r15  ; 0x02
 52a: 0b 83         std Y+3, r16  ; 0x03
 52c: 1c 83         std Y+4, r17  ; 0x04
 52e: 6d 82         std Y+5, r6 ; 0x05
 530: 7e 82         std Y+6, r7 ; 0x06
 532: 9e 01         movw  r18, r28
 534: 2f 5f         subi  r18, 0xFF ; 255
 536: 3f 4f         sbci  r19, 0xFF ; 255
 538: ae 01         movw  r20, r28
 53a: 4d 5f         subi  r20, 0xFD ; 253
 53c: 5f 4f         sbci  r21, 0xFF ; 255
 53e: be 01         movw  r22, r28
 540: 6b 5f         subi  r22, 0xFB ; 251
 542: 7f 4f         sbci  r23, 0xFF ; 255
 544: 80 e0         ldi r24, 0x00 ; 0
 546: b7 dd         rcall .-1170    ; 0xb6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>

SpenceKonde avatar Jan 02 '22 04:01 SpenceKonde

void pinConfigure(uint8_t pin, uint16_t pinconfig) {
  check_valid_digital_pin(pin);
  uint8_t bit_mask = digitalPinToBitMask(pin);
  b8:	fc 01       	movw	r30, r24
  ba:	ea 52       	subi	r30, 0x2A	; 42
  bc:	fd 47       	sbci	r31, 0x7D	; 125
  be:	40 81       	ld	r20, Z
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:54
  if (bit_mask == NOT_A_PIN) {
  c0:	4f 3f       	cpi	r20, 0xFF	; 255
  c2:	09 f4       	brne	.+2      	; 0xc6 <pinConfigure+0x10>
  c4:	3f c0       	rjmp	.+126    	; 0x144 <__EEPROM_REGION_LENGTH__+0x44>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:57
    return;                             /* ignore invalid pins passed at runtime */
  }
  volatile uint8_t *portbase = (volatile uint8_t*) digitalPinToPortStruct(pin);
  c6:	fc 01       	movw	r30, r24
  c8:	ec 53       	subi	r30, 0x3C	; 60
  ca:	fd 47       	sbci	r31, 0x7D	; 125
  cc:	e0 81       	ld	r30, Z
  ce:	20 e2       	ldi	r18, 0x20	; 32
  d0:	e2 9f       	mul	r30, r18
  d2:	f0 01       	movw	r30, r0
  d4:	11 24       	eor	r1, r1
  d6:	fc 5f       	subi	r31, 0xFC	; 252
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:58
  uint8_t bit_pos = digitalPinToBitPosition(pin);
  d8:	8e 54       	subi	r24, 0x4E	; 78
  da:	9d 47       	sbci	r25, 0x7D	; 125
  dc:	dc 01       	movw	r26, r24
  de:	8c 91       	ld	r24, X
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:59
  uint8_t setting = pinconfig & 0x03; // grab direction bits
  e0:	96 2f       	mov	r25, r22
  e2:	93 70       	andi	r25, 0x03	; 3
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:60
  if (setting) {
  e4:	09 f0       	breq	.+2      	; 0xe8 <pinConfigure+0x32>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:61
    *(portbase + setting) = bit_mask;
  e6:	41 83       	std	Z+1, r20	; 0x01
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:63
  }
  pinconfig >>= 2;
  e8:	9b 01       	movw	r18, r22
  ea:	36 95       	lsr	r19
  ec:	27 95       	ror	r18
  ee:	36 95       	lsr	r19
  f0:	27 95       	ror	r18
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:64
  setting = pinconfig & 0x03; // as above, only for output
  f2:	a2 2f       	mov	r26, r18
  f4:	a3 70       	andi	r26, 0x03	; 3
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:65
  if (setting) {
  f6:	29 f0       	breq	.+10     	; 0x102 <__EEPROM_REGION_LENGTH__+0x2>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:66
    *(portbase + 4 + setting) = bit_mask;
  f8:	b0 e0       	ldi	r27, 0x00	; 0
  fa:	14 96       	adiw	r26, 0x04	; 4
  fc:	ae 0f       	add	r26, r30
  fe:	bf 1f       	adc	r27, r31
 100:	4c 93       	st	X, r20
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:68
  }
  if (!(pinconfig & 0x03FFC)) return;
 102:	2c 7f       	andi	r18, 0xFC	; 252
 104:	3f 73       	andi	r19, 0x3F	; 63
 106:	23 2b       	or	r18, r19
 108:	e9 f0       	breq	.+58     	; 0x144 <__EEPROM_REGION_LENGTH__+0x44>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:69
  pinconfig >>= 2;
 10a:	9b 01       	movw	r18, r22
 10c:	94 e0       	ldi	r25, 0x04	; 4
 10e:	36 95       	lsr	r19
 110:	27 95       	ror	r18
 112:	9a 95       	dec	r25
 114:	e1 f7       	brne	.-8      	; 0x10e <__EEPROM_REGION_LENGTH__+0xe>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:70
  uint8_t oldSREG = SREG;
 116:	4f b7       	in	r20, 0x3f	; 63
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:71
  cli();
 118:	f8 94       	cli
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:72
  uint8_t pinncfg = *(portbase + 0x10 + bit_pos);
 11a:	90 e0       	ldi	r25, 0x00	; 0
 11c:	40 96       	adiw	r24, 0x10	; 16
 11e:	e8 0f       	add	r30, r24
 120:	f9 1f       	adc	r31, r25
 122:	80 81       	ld	r24, Z
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:73
  if (pinconfig & 0x08) {
 124:	23 ff       	sbrs	r18, 3
 126:	04 c0       	rjmp	.+8      	; 0x130 <__EEPROM_REGION_LENGTH__+0x30>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:74
    pinncfg = (pinncfg & 0xF8) | (pinconfig & 0x07);
 128:	88 7f       	andi	r24, 0xF8	; 248
 12a:	92 2f       	mov	r25, r18
 12c:	97 70       	andi	r25, 0x07	; 7
 12e:	89 2b       	or	r24, r25
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:76
  }
  uint8_t temp = pinconfig & 0x30;
 130:	20 73       	andi	r18, 0x30	; 48
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:77
  if (temp) {
 132:	09 f0       	breq	.+2      	; 0x136 <__EEPROM_REGION_LENGTH__+0x36>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:83
    if (temp == 0x30) {
      pinncfg ^= 0x08;    // toggle pullup - of dubious utility
    } else if (temp == 0x20) {
      pinncfg &= ~(0x08); // clear
    } else {
      pinncfg |= 0x08;    // set
 134:	88 60       	ori	r24, 0x08	; 8
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:86
    }
  }
  pinconfig >>= 8; // now it's just the last 4 bits.
 136:	97 2f       	mov	r25, r23
 138:	92 95       	swap	r25
 13a:	9f 70       	andi	r25, 0x0F	; 15
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:115
      pinncfg &= ~(0x40);   // clear
    }
  }
  #endif
  temp = pinconfig & 0x0C;
  if (temp) {
 13c:	09 f0       	breq	.+2      	; 0x140 <__EEPROM_REGION_LENGTH__+0x40>
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:121
    if (temp == 0x0C) {
      pinncfg ^= 0x80;    // toggle invert - of dubious utility, but I'll accept it.
    } else if (temp == 0x08) {
      pinncfg &= ~(0x80); // clear
    } else {
      pinncfg |= 0x80;    // set
 13e:	80 68       	ori	r24, 0x80	; 128
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:124
    }
  }
  *(portbase + 0x10 + bit_pos)=pinncfg;
 140:	80 83       	st	Z, r24
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:125
  SREG = oldSREG; // re-enable interrupts
 142:	4f bf       	out	0x3f, r20	; 63
C:\arduino-1.8.13-portable\portable\packages\megaTinyCore\hardware\megaavr\2.5.4\cores\megatinycore/wiring_digital.c:126
}
 144:	08 95       	ret

vs

000000b6 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)>:
pinConfigure():
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:465
    configure every pin parameter, both those configured through the PORTx
    regiters and those that are configured through the PORTx.PINnCTRL register.
  */

  template <typename MODE, typename... MODES>
  uint16_t pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
  b6:	ff 92       	push	r15
  b8:	0f 93       	push	r16
  ba:	1f 93       	push	r17
  bc:	cf 93       	push	r28
  be:	df 93       	push	r29
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:468
  {
    // Or-ing together the arguments using recursion
    uint16_t pin_config = mode | (pin_configure_t)pinConfigure(digital_pin, modes...);
  c0:	fb 01       	movw	r30, r22
  c2:	00 81       	ld	r16, Z
  c4:	11 81       	ldd	r17, Z+1	; 0x01
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:470

    uint8_t bit_mask = digitalPinToBitMask(digital_pin);
  c6:	68 2f       	mov	r22, r24
  c8:	70 e0       	ldi	r23, 0x00	; 0
  ca:	eb 01       	movw	r28, r22
  cc:	c8 5e       	subi	r28, 0xE8	; 232
  ce:	db 47       	sbci	r29, 0x7B	; 123
  d0:	98 81       	ld	r25, Y
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:471
    if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
  d2:	9f 3f       	cpi	r25, 0xFF	; 255
  d4:	09 f4       	brne	.+2      	; 0xd8 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)+0x22>
  d6:	5b c0       	rjmp	.+182    	; 0x18e <__EEPROM_REGION_LENGTH__+0x8e>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:468

  template <typename MODE, typename... MODES>
  uint16_t pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
  {
    // Or-ing together the arguments using recursion
    uint16_t pin_config = mode | (pin_configure_t)pinConfigure(digital_pin, modes...);
  d8:	fa 01       	movw	r30, r20
  da:	40 81       	ld	r20, Z
  dc:	51 81       	ldd	r21, Z+1	; 0x01
  de:	f9 01       	movw	r30, r18
  e0:	20 81       	ld	r18, Z
  e2:	31 81       	ldd	r19, Z+1	; 0x01
  e4:	24 2b       	or	r18, r20
  e6:	35 2b       	or	r19, r21
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:471

    uint8_t bit_mask = digitalPinToBitMask(digital_pin);
    if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
  e8:	21 15       	cp	r18, r1
  ea:	31 05       	cpc	r19, r1
  ec:	09 f4       	brne	.+2      	; 0xf0 <unsigned int pinConfigure<pin_configure_t, pin_configure_t, pin_configure_t>(unsigned char, pin_configure_t const&, pin_configure_t const&, pin_configure_t const&)+0x3a>
  ee:	6f c0       	rjmp	.+222    	; 0x1ce <__EEPROM_REGION_LENGTH__+0xce>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:474
      return 0;

    uint8_t bit_pos  = digitalPinToBitPosition(digital_pin);
  f0:	fb 01       	movw	r30, r22
  f2:	ee 5f       	subi	r30, 0xFE	; 254
  f4:	fb 47       	sbci	r31, 0x7B	; 123
  f6:	80 81       	ld	r24, Z
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:475
    volatile uint8_t *portbase = (volatile uint8_t*) digitalPinToPortStruct(digital_pin);
  f8:	fb 01       	movw	r30, r22
  fa:	e4 51       	subi	r30, 0x14	; 20
  fc:	fc 47       	sbci	r31, 0x7C	; 124
  fe:	e0 81       	ld	r30, Z
 100:	f0 e2       	ldi	r31, 0x20	; 32
 102:	ef 9f       	mul	r30, r31
 104:	a0 01       	movw	r20, r0
 106:	11 24       	eor	r1, r1
 108:	5c 5f       	subi	r21, 0xFC	; 252
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:478

    // Write to selected pin direction register
    uint8_t setting = pin_config & 0x03; // Mask out direction bits (DIR, DIRSET, DIRCLR, DIRTGL)
 10a:	e2 2f       	mov	r30, r18
 10c:	e3 70       	andi	r30, 0x03	; 3
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:479
    if(setting)
 10e:	21 f0       	breq	.+8      	; 0x118 <__EEPROM_REGION_LENGTH__+0x18>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:480
      *(portbase + setting) = bit_mask;
 110:	e4 0f       	add	r30, r20
 112:	f5 2f       	mov	r31, r21
 114:	f1 1d       	adc	r31, r1
 116:	90 83       	st	Z, r25
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:483

    // Write to selected output register
    pin_config >>= 2;
 118:	f9 01       	movw	r30, r18
 11a:	f6 95       	lsr	r31
 11c:	e7 95       	ror	r30
 11e:	f6 95       	lsr	r31
 120:	e7 95       	ror	r30
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:484
    setting = pin_config & 0x03;
 122:	ae 2f       	mov	r26, r30
 124:	a3 70       	andi	r26, 0x03	; 3
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:485
    if(setting)
 126:	29 f0       	breq	.+10     	; 0x132 <__EEPROM_REGION_LENGTH__+0x32>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:486
      *(portbase + 4 + setting) = bit_mask;
 128:	b0 e0       	ldi	r27, 0x00	; 0
 12a:	14 96       	adiw	r26, 0x04	; 4
 12c:	a4 0f       	add	r26, r20
 12e:	b5 1f       	adc	r27, r21
 130:	9c 93       	st	X, r25
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:489

    // Return if there is nothing more to configure
    if(!(pin_config & 0x3FFC))
 132:	ec 7f       	andi	r30, 0xFC	; 252
 134:	ff 73       	andi	r31, 0x3F	; 63
 136:	ef 2b       	or	r30, r31
 138:	09 f4       	brne	.+2      	; 0x13c <__EEPROM_REGION_LENGTH__+0x3c>
 13a:	49 c0       	rjmp	.+146    	; 0x1ce <__EEPROM_REGION_LENGTH__+0xce>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:492
      return 0;

    uint8_t oldSREG = SREG; // Store SREG
 13c:	ff b6       	in	r15, 0x3f	; 63
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:493
    cli(); // Disable interrupts
 13e:	f8 94       	cli
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:496

    // PINnCTRL register
    pin_config >>= 2;
 140:	d9 01       	movw	r26, r18
 142:	94 e0       	ldi	r25, 0x04	; 4
 144:	b6 95       	lsr	r27
 146:	a7 95       	ror	r26
 148:	9a 95       	dec	r25
 14a:	e1 f7       	brne	.-8      	; 0x144 <__EEPROM_REGION_LENGTH__+0x44>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:497
    uint8_t pinncfg = *(portbase + 0x10 + bit_pos);
 14c:	e8 2f       	mov	r30, r24
 14e:	f0 e0       	ldi	r31, 0x00	; 0
 150:	70 96       	adiw	r30, 0x10	; 16
 152:	e4 0f       	add	r30, r20
 154:	f5 1f       	adc	r31, r21
 156:	90 81       	ld	r25, Z
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:499
    // Input sense configuration (ISC)
    if(pin_config & 0x08)
 158:	a3 ff       	sbrs	r26, 3
 15a:	04 c0       	rjmp	.+8      	; 0x164 <__EEPROM_REGION_LENGTH__+0x64>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:500
      pinncfg = (pinncfg & 0xF8) | (pin_config & PORT_ISC_gm);
 15c:	98 7f       	andi	r25, 0xF8	; 248
 15e:	8a 2f       	mov	r24, r26
 160:	87 70       	andi	r24, 0x07	; 7
 162:	98 2b       	or	r25, r24
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:502
    // Pullup resistor
    uint8_t temp = pin_config & 0x30;
 164:	a0 73       	andi	r26, 0x30	; 48
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:503
    if(temp)
 166:	21 f0       	breq	.+8      	; 0x170 <__EEPROM_REGION_LENGTH__+0x70>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:505
    {
      if(temp == 0x30)
 168:	a0 33       	cpi	r26, 0x30	; 48
 16a:	c9 f4       	brne	.+50     	; 0x19e <__EEPROM_REGION_LENGTH__+0x9e>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:506
        pinncfg ^= PORT_PULLUPEN_bm;    // Toggle pullup
 16c:	88 e0       	ldi	r24, 0x08	; 8
 16e:	98 27       	eor	r25, r24
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:513
        pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
      else
        pinncfg |= PORT_PULLUPEN_bm;    // Set pullup
    }
    // Invert pin
    pin_config >>= 8;
 170:	23 2f       	mov	r18, r19
 172:	33 27       	eor	r19, r19
 174:	22 95       	swap	r18
 176:	2f 70       	andi	r18, 0x0F	; 15
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:514
    temp = pin_config & 0x0C;
 178:	82 2f       	mov	r24, r18
 17a:	8c 70       	andi	r24, 0x0C	; 12
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:515
    if(temp)
 17c:	19 f0       	breq	.+6      	; 0x184 <__EEPROM_REGION_LENGTH__+0x84>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:517
    {
      if(temp == 0x0C)
 17e:	8c 30       	cpi	r24, 0x0C	; 12
 180:	a1 f4       	brne	.+40     	; 0x1aa <__EEPROM_REGION_LENGTH__+0xaa>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:518
        pinncfg ^= PORT_INVEN_bm;    // Toggle invert
 182:	90 58       	subi	r25, 0x80	; 128
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:525
        pinncfg &= ~(PORT_INVEN_bm); // Clear
      else
        pinncfg |= PORT_INVEN_bm;    // Set
    }
    // Write to PINnCTRL register
    *(portbase + 0x10 + bit_pos) = pinncfg;
 184:	90 83       	st	Z, r25
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:528

    // Restore SREG
    SREG = oldSREG;
 186:	ff be       	out	0x3f, r15	; 63
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:470
  uint16_t pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
  {
    // Or-ing together the arguments using recursion
    uint16_t pin_config = mode | (pin_configure_t)pinConfigure(digital_pin, modes...);

    uint8_t bit_mask = digitalPinToBitMask(digital_pin);
 188:	48 81       	ld	r20, Y
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:471
    if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
 18a:	4f 3f       	cpi	r20, 0xFF	; 255
 18c:	19 f5       	brne	.+70     	; 0x1d4 <__EEPROM_REGION_LENGTH__+0xd4>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:472
      return 0;
 18e:	80 e0       	ldi	r24, 0x00	; 0
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:531

    // Restore SREG
    SREG = oldSREG;

    return pin_config;
  }
 190:	90 e0       	ldi	r25, 0x00	; 0
 192:	df 91       	pop	r29
 194:	cf 91       	pop	r28
 196:	1f 91       	pop	r17
 198:	0f 91       	pop	r16
 19a:	ff 90       	pop	r15
 19c:	08 95       	ret
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:507
    uint8_t temp = pin_config & 0x30;
    if(temp)
    {
      if(temp == 0x30)
        pinncfg ^= PORT_PULLUPEN_bm;    // Toggle pullup
      else if(temp == 0x20)
 19e:	a0 32       	cpi	r26, 0x20	; 32
 1a0:	11 f4       	brne	.+4      	; 0x1a6 <__EEPROM_REGION_LENGTH__+0xa6>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:508
        pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
 1a2:	97 7f       	andi	r25, 0xF7	; 247
 1a4:	e5 cf       	rjmp	.-54     	; 0x170 <__EEPROM_REGION_LENGTH__+0x70>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:510
      else
        pinncfg |= PORT_PULLUPEN_bm;    // Set pullup
 1a6:	98 60       	ori	r25, 0x08	; 8
 1a8:	e3 cf       	rjmp	.-58     	; 0x170 <__EEPROM_REGION_LENGTH__+0x70>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:519
    temp = pin_config & 0x0C;
    if(temp)
    {
      if(temp == 0x0C)
        pinncfg ^= PORT_INVEN_bm;    // Toggle invert
      else if(temp == 0x08)
 1aa:	88 30       	cpi	r24, 0x08	; 8
 1ac:	11 f4       	brne	.+4      	; 0x1b2 <__EEPROM_REGION_LENGTH__+0xb2>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:520
        pinncfg &= ~(PORT_INVEN_bm); // Clear
 1ae:	9f 77       	andi	r25, 0x7F	; 127
 1b0:	e9 cf       	rjmp	.-46     	; 0x184 <__EEPROM_REGION_LENGTH__+0x84>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:522
      else
        pinncfg |= PORT_INVEN_bm;    // Set
 1b2:	90 68       	ori	r25, 0x80	; 128
 1b4:	e7 cf       	rjmp	.-50     	; 0x184 <__EEPROM_REGION_LENGTH__+0x84>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:507
    uint8_t temp = pin_config & 0x30;
    if(temp)
    {
      if(temp == 0x30)
        pinncfg ^= PORT_PULLUPEN_bm;    // Toggle pullup
      else if(temp == 0x20)
 1b6:	40 32       	cpi	r20, 0x20	; 32
 1b8:	11 f4       	brne	.+4      	; 0x1be <__EEPROM_REGION_LENGTH__+0xbe>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:508
        pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
 1ba:	97 7f       	andi	r25, 0xF7	; 247
 1bc:	51 c0       	rjmp	.+162    	; 0x260 <__EEPROM_REGION_LENGTH__+0x160>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:510
      else
        pinncfg |= PORT_PULLUPEN_bm;    // Set pullup
 1be:	98 60       	ori	r25, 0x08	; 8
 1c0:	4f c0       	rjmp	.+158    	; 0x260 <__EEPROM_REGION_LENGTH__+0x160>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:519
    temp = pin_config & 0x0C;
    if(temp)
    {
      if(temp == 0x0C)
        pinncfg ^= PORT_INVEN_bm;    // Toggle invert
      else if(temp == 0x08)
 1c2:	28 30       	cpi	r18, 0x08	; 8
 1c4:	11 f4       	brne	.+4      	; 0x1ca <__EEPROM_REGION_LENGTH__+0xca>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:520
        pinncfg &= ~(PORT_INVEN_bm); // Clear
 1c6:	9f 77       	andi	r25, 0x7F	; 127
 1c8:	55 c0       	rjmp	.+170    	; 0x274 <__EEPROM_REGION_LENGTH__+0x174>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:522
      else
        pinncfg |= PORT_INVEN_bm;    // Set
 1ca:	90 68       	ori	r25, 0x80	; 128
 1cc:	53 c0       	rjmp	.+166    	; 0x274 <__EEPROM_REGION_LENGTH__+0x174>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:470
  uint16_t pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
  {
    // Or-ing together the arguments using recursion
    uint16_t pin_config = mode | (pin_configure_t)pinConfigure(digital_pin, modes...);

    uint8_t bit_mask = digitalPinToBitMask(digital_pin);
 1ce:	48 81       	ld	r20, Y
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:472
    if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
      return 0;
 1d0:	30 e0       	ldi	r19, 0x00	; 0
 1d2:	20 e0       	ldi	r18, 0x00	; 0
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:468

  template <typename MODE, typename... MODES>
  uint16_t pinConfigure(const uint8_t digital_pin, const MODE& mode, const MODES&... modes)
  {
    // Or-ing together the arguments using recursion
    uint16_t pin_config = mode | (pin_configure_t)pinConfigure(digital_pin, modes...);
 1d4:	20 2b       	or	r18, r16
 1d6:	31 2b       	or	r19, r17
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:471

    uint8_t bit_mask = digitalPinToBitMask(digital_pin);
    if(bit_mask == NOT_A_PIN || !pin_config) // Return if digital pin is invalid or the other parameters or out to zero
 1d8:	21 15       	cp	r18, r1
 1da:	31 05       	cpc	r19, r1
 1dc:	c1 f2       	breq	.-80     	; 0x18e <__EEPROM_REGION_LENGTH__+0x8e>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:474
      return 0;

    uint8_t bit_pos  = digitalPinToBitPosition(digital_pin);
 1de:	fb 01       	movw	r30, r22
 1e0:	ee 5f       	subi	r30, 0xFE	; 254
 1e2:	fb 47       	sbci	r31, 0x7B	; 123
 1e4:	a0 81       	ld	r26, Z
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:475
    volatile uint8_t *portbase = (volatile uint8_t*) digitalPinToPortStruct(digital_pin);
 1e6:	64 51       	subi	r22, 0x14	; 20
 1e8:	7c 47       	sbci	r23, 0x7C	; 124
 1ea:	fb 01       	movw	r30, r22
 1ec:	80 81       	ld	r24, Z
 1ee:	f0 e2       	ldi	r31, 0x20	; 32
 1f0:	8f 9f       	mul	r24, r31
 1f2:	c0 01       	movw	r24, r0
 1f4:	11 24       	eor	r1, r1
 1f6:	9c 5f       	subi	r25, 0xFC	; 252
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:478

    // Write to selected pin direction register
    uint8_t setting = pin_config & 0x03; // Mask out direction bits (DIR, DIRSET, DIRCLR, DIRTGL)
 1f8:	e2 2f       	mov	r30, r18
 1fa:	e3 70       	andi	r30, 0x03	; 3
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:479
    if(setting)
 1fc:	21 f0       	breq	.+8      	; 0x206 <__EEPROM_REGION_LENGTH__+0x106>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:480
      *(portbase + setting) = bit_mask;
 1fe:	e8 0f       	add	r30, r24
 200:	f9 2f       	mov	r31, r25
 202:	f1 1d       	adc	r31, r1
 204:	40 83       	st	Z, r20
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:483

    // Write to selected output register
    pin_config >>= 2;
 206:	b9 01       	movw	r22, r18
 208:	76 95       	lsr	r23
 20a:	67 95       	ror	r22
 20c:	76 95       	lsr	r23
 20e:	67 95       	ror	r22
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:484
    setting = pin_config & 0x03;
 210:	e6 2f       	mov	r30, r22
 212:	e3 70       	andi	r30, 0x03	; 3
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:485
    if(setting)
 214:	29 f0       	breq	.+10     	; 0x220 <__EEPROM_REGION_LENGTH__+0x120>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:486
      *(portbase + 4 + setting) = bit_mask;
 216:	f0 e0       	ldi	r31, 0x00	; 0
 218:	34 96       	adiw	r30, 0x04	; 4
 21a:	e8 0f       	add	r30, r24
 21c:	f9 1f       	adc	r31, r25
 21e:	40 83       	st	Z, r20
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:489

    // Return if there is nothing more to configure
    if(!(pin_config & 0x3FFC))
 220:	6c 7f       	andi	r22, 0xFC	; 252
 222:	7f 73       	andi	r23, 0x3F	; 63
 224:	67 2b       	or	r22, r23
 226:	09 f4       	brne	.+2      	; 0x22a <__EEPROM_REGION_LENGTH__+0x12a>
 228:	b2 cf       	rjmp	.-156    	; 0x18e <__EEPROM_REGION_LENGTH__+0x8e>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:492
      return 0;

    uint8_t oldSREG = SREG; // Store SREG
 22a:	6f b7       	in	r22, 0x3f	; 63
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:493
    cli(); // Disable interrupts
 22c:	f8 94       	cli
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:496

    // PINnCTRL register
    pin_config >>= 2;
 22e:	a9 01       	movw	r20, r18
 230:	24 e0       	ldi	r18, 0x04	; 4
 232:	56 95       	lsr	r21
 234:	47 95       	ror	r20
 236:	2a 95       	dec	r18
 238:	e1 f7       	brne	.-8      	; 0x232 <__EEPROM_REGION_LENGTH__+0x132>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:497
    uint8_t pinncfg = *(portbase + 0x10 + bit_pos);
 23a:	ea 2f       	mov	r30, r26
 23c:	f0 e0       	ldi	r31, 0x00	; 0
 23e:	70 96       	adiw	r30, 0x10	; 16
 240:	e8 0f       	add	r30, r24
 242:	f9 1f       	adc	r31, r25
 244:	90 81       	ld	r25, Z
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:499
    // Input sense configuration (ISC)
    if(pin_config & 0x08)
 246:	43 ff       	sbrs	r20, 3
 248:	04 c0       	rjmp	.+8      	; 0x252 <__EEPROM_REGION_LENGTH__+0x152>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:500
      pinncfg = (pinncfg & 0xF8) | (pin_config & PORT_ISC_gm);
 24a:	98 7f       	andi	r25, 0xF8	; 248
 24c:	84 2f       	mov	r24, r20
 24e:	87 70       	andi	r24, 0x07	; 7
 250:	98 2b       	or	r25, r24
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:502
    // Pullup resistor
    uint8_t temp = pin_config & 0x30;
 252:	40 73       	andi	r20, 0x30	; 48
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:503
    if(temp)
 254:	29 f0       	breq	.+10     	; 0x260 <__EEPROM_REGION_LENGTH__+0x160>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:505
    {
      if(temp == 0x30)
 256:	40 33       	cpi	r20, 0x30	; 48
 258:	09 f0       	breq	.+2      	; 0x25c <__EEPROM_REGION_LENGTH__+0x15c>
 25a:	ad cf       	rjmp	.-166    	; 0x1b6 <__EEPROM_REGION_LENGTH__+0xb6>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:506
        pinncfg ^= PORT_PULLUPEN_bm;    // Toggle pullup
 25c:	88 e0       	ldi	r24, 0x08	; 8
 25e:	98 27       	eor	r25, r24
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:513
        pinncfg &= ~(PORT_PULLUPEN_bm); // Clear pullup
      else
        pinncfg |= PORT_PULLUPEN_bm;    // Set pullup
    }
    // Invert pin
    pin_config >>= 8;
 260:	83 2f       	mov	r24, r19
 262:	82 95       	swap	r24
 264:	8f 70       	andi	r24, 0x0F	; 15
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:514
    temp = pin_config & 0x0C;
 266:	28 2f       	mov	r18, r24
 268:	2c 70       	andi	r18, 0x0C	; 12
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:515
    if(temp)
 26a:	21 f0       	breq	.+8      	; 0x274 <__EEPROM_REGION_LENGTH__+0x174>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:517
    {
      if(temp == 0x0C)
 26c:	2c 30       	cpi	r18, 0x0C	; 12
 26e:	09 f0       	breq	.+2      	; 0x272 <__EEPROM_REGION_LENGTH__+0x172>
 270:	a8 cf       	rjmp	.-176    	; 0x1c2 <__EEPROM_REGION_LENGTH__+0xc2>
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:518
        pinncfg ^= PORT_INVEN_bm;    // Toggle invert
 272:	90 58       	subi	r25, 0x80	; 128
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:525
        pinncfg &= ~(PORT_INVEN_bm); // Clear
      else
        pinncfg |= PORT_INVEN_bm;    // Set
    }
    // Write to PINnCTRL register
    *(portbase + 0x10 + bit_pos) = pinncfg;
 274:	90 83       	st	Z, r25
C:\Users\Spence\Documents\Arduino\hardware\megaTinyCore\megaavr\cores\megatinycore/Arduino.h:528

    // Restore SREG
    SREG = oldSREG;
 276:	6f bf       	out	0x3f, r22	; 63
 278:	8b cf       	rjmp	.-234    	; 0x190 <__EEPROM_REGION_LENGTH__+0x90>

SpenceKonde avatar Jan 02 '22 05:01 SpenceKonde

What is very clear is that stuff is being done at runtime that should be getting done at compile time.

SpenceKonde avatar Jan 02 '22 05:01 SpenceKonde

Ok, I figured it out. The compiler wasn't able to figure out what was going on when the template function called itself. It usually can, but for some reason, not in this case. Fixed now.

https://github.com/MCUdude/MegaCoreX/commit/854d9e1041bef33e3bc50458f5efa686ab5118ec#diff-3425c70d2b78d865cd401a9c605f563eda19ce9cf9d095e63d5770d06423c949

MCUdude avatar Jan 03 '22 20:01 MCUdude

Hi, Let me show you my Pin Lib. AVRxDB_Pin.zip

mikrocoder avatar Feb 03 '22 12:02 mikrocoder

I forget where I was on the MCUDude implementation, The last thing I remember, things had been improvedm but there was still a substantial overhead, which matters since pinConfigure gets ported to megaTinyCore where it's serving parts with 2k of flash). But I've owed the classic tiny people a release for too long, ATTinyCore 2.0.0 goes out next.

Hi, Let me show you my Pin Lib. AVRxDB_Pin.zip

What's the overhead like?

SpenceKonde avatar Feb 04 '22 06:02 SpenceKonde

No overhead, small "footprint". Do you have a standard code to compare? Make led blink or something similar. The difference is that I work with objects and their methods. Everything is compiled at compile time.

mikrocoder avatar Feb 04 '22 15:02 mikrocoder

Sorry for not saying more here sooner. Basically, I can't pull in the code in the condition it is in. I was testing it on megaTinyCore, and the impact on codesize is catastrophic, and renders it unusable on some of the target platforms.

It appears that the attempts mafr have still been unsuccessful at making sure the ORing always happens at compile time if at all possible.

I am not qualified to rectify this myself; but I was wondering - would it make a difference if the variadic function was replaced with a macro? Wouldn't that get us to the point where, with compiletime constant values, a series of comma separated values could be made to shake out into a series of values separated by bitwise OR operators? Then any compiletime known ones would get constant folding applied; , and if they included things that weren't constant, it would still fold to the greatest extent possible, and it would never be bitwise or-ing compiletime constant values at runtime.

I love the improved syntax of pinConfigure(pin,option1,option2,option3) (in fact,I really hate the syntax I was forced to use!). And, from a logical or complexity stantpoint it make no sense that it couldn't be done - somehow., I see no reason that can't compile to something just as small. Except thathat the implementations we've been able to come up with don't. But I'm pretty useless at this because it's all aspects of the language that I don't really understand. As I've often remarked, I don't know C++, I know C and Assembly.

I guess you'd also have the same non-macro function that my implementation uses, which took only a single option argument that is the bitwise combination of the desirerd options, and that's what everything else would condense down to....

SpenceKonde avatar Mar 12 '22 01:03 SpenceKonde

@mikrocoder - there are a few reasons I don't really like that code. None of them are technical.

The first thing is, it is not in keeping with the style of Arduino. Every other Arduino function to work with a pin is structured as functionName(pin,options) - a function takes a pin identifier and options (if any) and does something.

Second, - It's greek to me, I don't understand it very well.

Third - I still worry if one tried to use if on a larger scale, you'd suddenly find that under some situation, constant folding failed, with catastrophic size and performance effects. The optimizer isn't much better with C++ than I am. For example, classes sem to compltely disable most optimization. If you try to guard Serial.begin() against illegal compiletime known values by doimg

if (__builtin_constant_p(baud)) {
if (baud > maxbaud || baud < minbaud) {
badArg("The requested baud cannot be generated at this clock speed"); 
}
} 

(which is what I tried shortly after discovering that technique) It doesn't work. Because it's a class method. even though it's only called once with an integer literal argment, the baud rate is never treated as a compile known constant! C++ feaures appear to be kryptonite to the optimizer, and with flash space and memory at such a premium on the smaller devices, something that makes the code more "modern and object porented-like" at the expense of compiled binary size is not an improvement. Anything I do that increases compile size is something I really think about whether I can justify it, particularly if it might get ported to megaTinyCore which serves 2k parts/

In the case of a the automatic clearing of the reset flags and stashing of them in GPIOR0 at startup. I nearly didn't make that the default behavior. But as I considered what the consequences of not doing that were, it was clear since, no matter what I wrote, 95% of users wouldn't follow my copy-paste instructions to ensure reset flags were cleared after every startup. And the problem with that it prevents the chip from detecting tat it just experienced a "dirty reset". hence all expectations of init() and all api functions were violated, we might even be in a state where CPUINT considers us to be in an interrupt and so won't execute any ISRs but we're executing normal code. If execution ever got to 0x0000, it is guaranteed that the code has malfunction, and it is highly likely that doing anthing other than an immediate software reset is unlikely to restore any approximation of functioning, and could result in arbitrary incorrect behavior. I further realized that dirty resets are liekly the main cause of AVRs getting into a "bad state" amd requiring a manual reset to revive. In light of the magnitude of the problems that it would preempt, I decided it would be irresponsible of me to know how to catch that case and not use it by default.

But support for alt pins with analogWrite? That's in DxCore, but I decided I couldn't justify porting to megaTinyCore.

There are a number of cases where I got most of the way through implemening some new feature, and started thinking about whether the usefulness justified the flash cost, decided it did not, and removed it.

SpenceKonde avatar Mar 12 '22 01:03 SpenceKonde

Sorry for not saying more here sooner. Basically, I can't pull in the code in the condition it is in. I was testing it on megaTinyCore, and the impact on codesize is catastrophic, and renders it unusable on some of the target platforms.

It's been a while, and I don't remember all the details, but if you have a look at the current MegaCoreX pinConfigure implementation, this is able to OR together the parameters at compile time, and is just as small as your implementation with no overhead. The trick was to move the entire OR logic template out of the pinConfigure template.

MCUdude avatar Mar 12 '22 06:03 MCUdude

Well, I have to apologize. I'm rather emvbarassed. I re-ran the tests with the version I'd put into megaTinyCore. The results did not match my notes at all .

Sometimes your implementation. wins and sometimes mine does... What is really interesting tough?

If you use the grammar for my version, with your implementation, the result is the worst of all!!

So please disregard ,my inaccrate objections.

I will release the next version, then merge this in.to master and then we can see whether people find problemswith it.

SpenceKonde avatar Mar 12 '22 07:03 SpenceKonde

Thanks and sorry for bot the long delay an inaccrate claims;

SpenceKonde avatar Mar 12 '22 07:03 SpenceKonde

Hello,

our approaches are just different. :wink: Everyone can handle this as they wish. In any case, I would like to get away from the define macro programming. The risk for errors is too high. I just wanted to show it. It's okay if you don't like it. It's just OOP.

I don't like these multi functions with hundreds of parameters. That is also very error prone. One typo and a pin is misconfigured without the compiler can complain. Or a pin is changed at another place unintentionally. You can overwrite anything with anything.

You can handle this as you like. :wink:

mikrocoder avatar Mar 12 '22 15:03 mikrocoder

Hi,

you know what's funny now. I'm testing the Logic and Event examples right now because of deprecated warnings. That's what you wanted me to test. You don't like my pin classes. But the event class is okay? :smile: And with Logic the Arduino user should suddenly deal directly with hardware accesses. That makes your rejection a bit contradictory. :wink:

mikrocoder avatar Mar 12 '22 16:03 mikrocoder

Those libraries are written in a very different style than they would be if I had written them. - they were by @MCUdude - I always find it striking how in some ways we are driven to the same thing - universal cores for all the decent arduino parts, chp-centric rather than board-centric, etc etc - and et, our tastes in coding style could hardly be more different than they are. The most recent version of the event library was the product of considerable debate, the sort of debate where, at t's heart, the disagreement is on what that goal should be.

For a great example of what happened when neither of us were aware of the others work, compare the Flash library in DxCore (which I wrote) with the one in megaTinyCore (which he wrote).

Specifically on event, there is a desperate need to have some form of abstraction hat is vaguely usablel; and he had a better idea of how to do it that I did.

The Ex-series is going to need a considerable amount of work there by the way, - the are changing up how pin and PIT events work. (The end goal, I think, is to make all channels the same. Actually, that is what they've done) but that obviously means some major changes are going to be needed in Event. A close examination of the ATPACK will reveal basically all there is to know about them - adapting Event to it... a little less straightforward, because we have gone from a setting in one place fully defining the event configuration for a given generator channel, to having the two event generators for each port configured in the PORT register, and the event generator pointed to one of those.

SpenceKonde avatar Mar 12 '22 18:03 SpenceKonde

He also did Comparator, ZCD, and OPAMP libaries.. Without which there wouldn't be wrappers aroundthat funcrionalty at all. The key difference is that without his libraries, there was no wrapper around these things. I don't object to your library existing, but I'd need to do a lot of of work if I was to integrate it with the cores, just to to port it across megaTinyCore and DxCore to all parts, and it's functionality that already exists.through standard API functions.

SpenceKonde avatar Mar 12 '22 19:03 SpenceKonde

This matter is a lower priority than:

  • Getting serial to work which is a prerequisite for
  • Getting timekeeping to work
  • Bootloader must be fixed
  • Getting holes to be handled in SerialUPDI
  • Comparator and ZCD are hosed. I think I understand the former, I do not understand the latter issue,
  • pinout diagrams, which I thinkwe have in a PR
  • Merging and proofreading the platform IO docs Thus this quality of life chance is deferred to 1.5.x

If it is determined that the arguments cannot be combined at runtime, resulting in a grotesque expansion of the code, this change will not be made. If it possible without bloat, which it should be, the comma syntax is immeasurably superior and far more natural that what I came up with, and would be worth a moderate amount of development time - especially considering that, for the case if a pin known to not be outputting PWM, but which could, pinConfigure is faster than digitalWrite....

SpenceKonde avatar Nov 15 '22 12:11 SpenceKonde

How strange! With only one or two calls to pinConfigure, or with lots of calls to it, the commas compile significantly smaller. In the 3-6 call range though, if they're all bitwise OR's, they come out smaller. But the bitwise or otherwise loses! What?!

Anyway, this is at a point where I'm comfortable merging it.

SpenceKonde avatar Nov 25 '22 05:11 SpenceKonde