Previously, up to four separate but identical ISR routines were defined,
for PCINT0, PCINT1, PCINT2 and PCINT3. Each of these would generate
their own function, with a lot of push-popping because another function
was called.
Now, the ISR_ALIASOF macro from avr-libc is used to declare just the
PCINT0 version and make all other ISRs point to that one, saving a lot
of program space, as well as some speed because of improved inlining.
On an Arduino Uno with gcc 4.3, this saves 168 bytes. With gcc 4.8, this
saves 150 bytes.
Similar to SoftwareSerial::write, this rewrites the loop to only touch
the MSB and then shift those bits up, allowing the compiler to generate
more efficient code. Unlike the write function however, it is not needed
to put all instance variables used into local variables, for some reason
the compiler already does this (and doing it manually even makes the
code bigger).
On the Arduino Uno using gcc 4.3 this saves 26 bytes. Using gcc 4.8 this
saves 30 bytes.
Note that this removes the else clause in the code, making the C code
unbalanced, which looks like it breaks timing balance. However, looking
at the code generated by the compiler, it turns out that the old code
was actually unbalanced, while the new code is properly balanced.
This change restructures the loop, to help the compiler generate shorter
code (because now only the LSB of the data byte is checked and
subsequent bytes are shifted down one by one, it can use th "skip if bit
set" instruction).
Furthermore, it puts most attributes in local variables, which causes
the compiler to put them into registers. This makes the timing-critical
part of the code smaller, making it easier to provide accurate timings.
On an Arduino uno using gcc 4.3, this saves 58 bytes. On gcc 4.8, this
saves 14 bytes.
Before, there was nearly identical code for the inverted and regular
cases. However, simply inverting the byte in the inverted case allows
using the regular code twice, reducing the generated code size by 100
bytes (on an Arduino Uno and gcc 4.3, on gcc 4.8 the reduction is 50
bytes).
stopListening also disabled the interrupt, if needed, so calling that
function makes more sense. Since stopListening only disables the
interrupt when the current SoftwareSerial is the active object, and that
can only be the case when _rx_delay_stopbit is non-zero, there is no
need to separately check _rx_delay_stopbit anymore.
This prevents interrupts from triggering when the SoftwareSerial
instance is not even listening.
Additionally, this removes the need to disable interrupts in
SoftwareSerial::listen, since no interrupts are active while it touches
the variables.
The current check is still always false when the old check was, but
additionally it will not disable the interrupts when they were never
enabled (which shouldn't matter much, but this is more consistent).
In this case, SoftwareSerial::begin will not have enabled the
interrupts, so better not allow the SoftwareSerial instance to enter the
listening state either.
Before enabling interupts, begin would see if the given receive pin
actually has an associated PCINT register. If not, the interrupts would
not be enabled.
Now, the same check is done, but when no register is available, the rx
parameters are not loaded at all (which in turn prevents the interrupt
from being enabled). This allows all code to use the same "is rx
enabled" (which will be added next).