user89105: I am working on a project that requires a number of I/O pins for various purposes.  I chose the ATTiny88 because it has plenty of GPIO pins, and is relatively inexpensive.
Here is the updated code per timemage, with many thanks!
#include <Arduino.h>
#include <TM1637Display.h>
#include "PinChangeInterrupt.h"

int counter = 0;
bool Update = false;
int currentStatePhase_B;
int lastStatePhase_B;

// Shutdown Output
#define SW 11

// Rotary Encoder Inputs
#define Phase_A 13
#define Phase_B 12
#define DIO 3
#define CLK 4

TM1637Display display(CLK, DIO);

void setup() {

  // Set encoder pins as inputs
  pinMode(Phase_A,INPUT);
  pinMode(Phase_B,INPUT);
  pinMode(SW, OUTPUT);

    // Read the initial state of Phase_B
    lastStatePhase_B = digitalRead(Phase_B);
    
    // Call updateEncoder() when any high/low changed seen
    // on interrupt 0 (pin 2), or interrupt 1 (pin 3)
    attachPCINT(digitalPinToPCINT(Phase_A), updateEncoder, CHANGE);
    attachPCINT(digitalPinToPCINT(Phase_B), updateEncoder, CHANGE);

  display.setBrightness(0x0f);
  display.clear();
}

void loop() {
  delay(100);
    if (Update){
        // Show decimal numbers with/without leading zeros
    display.showNumberDec(counter, false);
    // Expect: ___0
        Update = false;
    }
}

void updateEncoder(){
  delay (4);
    // Read the current state of Phase_B
    currentStatePhase_B = digitalRead(Phase_B);

    // If last and current state of Phase_B are different, then pulse occurred
    // React to only 1 state change to avoid double count
    if (currentStatePhase_B != lastStatePhase_B  && currentStatePhase_B == 1){

        // If the Phase_A state is different than the Phase_B state then
        // the encoder is rotating CCW so decrement
        if (digitalRead(Phase_A) != currentStatePhase_B) {
            counter --;
        } else {
            // Encoder is rotating CW so increment
            counter ++;
        }

    if (counter > 1023){
      counter = 1023;
    }
    if (counter < 0){
      counter = 0;
    }
    delay(15);
    }

    // Remember last CLK state
    lastStatePhase_B = currentStatePhase_B;
    Update = true;
}

It is now capturing most, but not by any means all, interrupts, but the direction is highly unreliable.  More often than not, it counts up regardless of which way the encoder is turned,and often counts down when the encoder is turned clockwise.
Here is what seems to be a good reference:
ATtiny88 8-bit Microcontroller

user70020: Interrupt types

One source says there are only two interrupt pins, ...

There are  two "external interrupt" pins; that is the term you'll find in the chip's datasheet. This is the term that refers to the kind of pins that attachInterupt uses. These are the ones in the pinout labeled INT0,INT1,INTn. The "external" term may be confusing for a couple of reasons. Lots of external things cause interrupts, but this name is applied to when you're not talking about events tied to particular peripherals, like USART (serial peripheral) receiving a character. The special function of "external interrupt" is dedicated to dispatching interrupts based on signals on these INT0/INT1/INT# pins. There's one interrupt vector for each of these signals (two in your case). As you've probably seen in the documentation they can be configured to trigger on rising or falling edges, both edges, and low levels.
So, they're right about that to the extent that they're talking about the "external interrupt" pins.
The "external" name is also confusing because of what else you've found:

Another source says there are quite a few Change Interrupt pins

This is also true. These are the "PCINT#" signal names listed in the chip pinout.  These "pin change interrupts" are newer; not new, but newer. There exist(ed) AVR that had/have "external interrupts" but no "pin change interrupts". I'd like  to think that if they were created at the same time they wouldn't have simply called the former ones "external", because these are no less external although they're less dedicated. There's only a single ISR/interrupt for each group of 8 PCINT pins. And unlike the others they only naturally detect that a pin has changed and nothing about the nature of that change (rising vs falling). But a lot of AVR support them and those that do have a significant number of them. In the case of your ATTiny88, all of the GPIO (Arduino "digital") pins are capable of dispatching a pin change interrupt.  However, the Arduino AVR core doesn't provide a function for using them.
Modifying NicoHood's PinChangeInterrupt library
One of the people that used to be in my circle was NicoHood, who created this PinChangeInterrupt library. I have a foggy memory of helping with bits of it.
What he did was model it after the normal attachInterrupt function. So, you can use it almost as though you have the RISING/FALLING/CHANGE "external interrupt" capability on all of your PCINT# pins; in your case all of your pins. Underneath it is tracking the state of each port pin to determine what has changed and in what direction. In other words, using an interrupt that fires given a change on any of 8 pins together with variables to synthesize per-pin callbacks for not just change but also specifically rising and falling if you want. This is not perfect it is possible to lose short lived events and it takes longer to dispatch your handler function. But it's probably fine for what you're doing.
It does not support your chip out of the box. However you can modify to support the ATTiny88; partially if you're not picky. You should find it in the library manager as "PinChangeInterrupt" by NicoHood, currently at version 1.2.9. If you install that it should show up under your sketchbook/libraries directory. You will need to edit the file at path:
<sketchbook-directory>/libraries/PinChangeInterrupt/src/PinChangeInterruptBoards.h
That's your local copy of this file.
Your ATTiny88's handling of PCINT is going to be nearly the same as the ATMega328P that's in the UNO. Or rather nearly a superset of it. The chips are closely related in a way.
However, you have extra PCINT capable pins on the ATTiny88 that aren't available on the ATMega328P. So you can crudely hack in a line into this section to read:
#if defined(__AVR_ATmega328__) || defined(__AVR_ATmega328A__) || defined(__AVR_ATmega328PA__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \
   || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega168PB__) \
   || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88A__) || defined(__AVR_ATmega88PA__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) \
   || defined(__AVR_ATtiny88__) \
   || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48A__) || defined(__AVR_ATmega48PA__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PB__)

The addition of that second to last line above should make the library treat the ATTiny88 you have as though it were an ATMega328P. This compiles under ATTinyCore. I am not set up to test it on a real ATTiny88. But, I expect it will work if you try some examples.
Having done that, the library should be work on any of the pins that carry a PCINT0 through PCINT23 label.  If you want it to work with PCINT24-PCINT27, that can be done, but requires more work. But given what you've said you probably don't need one of those four and they're not available on the DIP package which I'm guessing you have anyway.
Updating your code
You would to #include "PinChangeInterrupt.h" at the top of your code, and then your current code:
    attachInterrupt(digitalPinToInterrupt(Phase_A), updateEncoder, CHANGE);
    attachInterrupt(digitalPinToInterrupt(Phase_B), updateEncoder, CHANGE);

then becomes:
    attachPCINT(digitalPinToPCINT(Phase_A), updateEncoder, CHANGE);
    attachPCINT(digitalPinToPCINT(Phase_B), updateEncoder, CHANGE);

With luck, that should do it provided Phase_A and Phase_B under your core (e.g. ATTinyCore) map to GPIO/Arduino "digital" pins that carry a PCINT# signal where the number less than 24.
As I said, I have no way of testing it on real hardware right now. But I expect it will work. If not, it is the sort of direction you need to go in.
Doing PCINT yourself
You can also take a more direct approach with reading the datasheet and sorting out how to do what the library does for you, only manually. One of the examples NicoHood provided with the library is not really an example of using the library but rather an explanation as to what the library does in the form of code. Notice it doesn't actually include the library; it would operate stand alone. So if you want to understand the guts yourself by following the ATtiny88 datasheet, that's a place to start.
One reason you might want to do it yourself anyway is if your encoders are being operated quickly, to the point where you can't afford some of the extra effective dispatch time imposed by what the PinChangeInterrupt library is doing in sorting out which pin changed and how. If you handle PCINT yourself, you can make a point of putting your two encoder pins in completely different groups of 8 such that there is only one source of pin change interrupt for each ISR and so there is no need to check which pin caused the interrupt and to route that to another function.

89105: <p>I am working on a project that requires a number of I/O pins for various purposes.  I chose the ATTiny88 because it has plenty of GPIO pins, and is relatively inexpensive.</p>
<p>Here is the updated code per timemage, with many thanks!</p>
<pre><code>#include &lt;Arduino.h&gt;
#include &lt;TM1637Display.h&gt;
#include &quot;PinChangeInterrupt.h&quot;

int counter = 0;
bool Update = false;
int currentStatePhase_B;
int lastStatePhase_B;

// Shutdown Output
#define SW 11

// Rotary Encoder Inputs
#define Phase_A 13
#define Phase_B 12
#define DIO 3
#define CLK 4

TM1637Display display(CLK, DIO);

void setup() {

  // Set encoder pins as inputs
  pinMode(Phase_A,INPUT);
  pinMode(Phase_B,INPUT);
  pinMode(SW, OUTPUT);

    // Read the initial state of Phase_B
    lastStatePhase_B = digitalRead(Phase_B);
    
    // Call updateEncoder() when any high/low changed seen
    // on interrupt 0 (pin 2), or interrupt 1 (pin 3)
    attachPCINT(digitalPinToPCINT(Phase_A), updateEncoder, CHANGE);
    attachPCINT(digitalPinToPCINT(Phase_B), updateEncoder, CHANGE);

  display.setBrightness(0x0f);
  display.clear();
}

void loop() {
  delay(100);
    if (Update){
        // Show decimal numbers with/without leading zeros
    display.showNumberDec(counter, false);
    // Expect: ___0
        Update = false;
    }
}

void updateEncoder(){
  delay (4);
    // Read the current state of Phase_B
    currentStatePhase_B = digitalRead(Phase_B);

    // If last and current state of Phase_B are different, then pulse occurred
    // React to only 1 state change to avoid double count
    if (currentStatePhase_B != lastStatePhase_B  &amp;&amp; currentStatePhase_B == 1){

        // If the Phase_A state is different than the Phase_B state then
        // the encoder is rotating CCW so decrement
        if (digitalRead(Phase_A) != currentStatePhase_B) {
            counter --;
        } else {
            // Encoder is rotating CW so increment
            counter ++;
        }

    if (counter &gt; 1023){
      counter = 1023;
    }
    if (counter &lt; 0){
      counter = 0;
    }
    delay(15);
    }

    // Remember last CLK state
    lastStatePhase_B = currentStatePhase_B;
    Update = true;
}
</code></pre>
<p>It is now capturing most, but not by any means all, interrupts, but the direction is highly unreliable.  More often than not, it counts up regardless of which way the encoder is turned,and often counts down when the encoder is turned clockwise.</p>
<p>Here is what seems to be a good reference:
<a href="https://components101.com/microcontrollers/attiny88-pinout-specs-datasheet" rel="nofollow noreferrer">ATtiny88 8-bit Microcontroller</a></p>
 70020: <h3>Interrupt types</h3>
<blockquote>
<p>One source says there are only two interrupt pins, ...</p>
</blockquote>
<p>There are  two &quot;<em>external</em> interrupt&quot; pins; that is the term you'll find in the chip's datasheet. This is the term that refers to the kind of pins that <code>attachInterupt</code> uses. These are the ones in the pinout labeled INT0,INT1,INTn. The &quot;external&quot; term may be confusing for a couple of reasons. Lots of external things cause interrupts, but this name is applied to when you're not talking about events tied to particular peripherals, like USART (serial peripheral) receiving a character. The special function of &quot;external interrupt&quot; is <em>dedicated</em> to dispatching interrupts based on signals on these INT0/INT1/INT# pins. There's one interrupt vector for each of these signals (two in your case). As you've probably seen in the documentation they can be configured to trigger on rising or falling edges, both edges, and low levels.
So, they're right about that to the extent that they're talking about the &quot;external interrupt&quot; pins.</p>
<p>The &quot;external&quot; name is also confusing because of what else you've found:</p>
<blockquote>
<p>Another source says there are quite a few Change Interrupt pins</p>
</blockquote>
<p>This is also true. These are the &quot;PCINT#&quot; signal names listed in the chip pinout.  These &quot;<em>pin change</em> interrupts&quot; are newer; not new, but new<em>er</em>. There exist(ed) AVR that had/have &quot;external interrupts&quot; but no &quot;pin change interrupts&quot;. I'd like  to think that if they were created at the same time they wouldn't have simply called the former ones &quot;external&quot;, because these are no less external although they're less dedicated. There's only a single ISR/interrupt for each group of 8 PCINT pins. And unlike the others they only naturally detect that a pin has changed and nothing about the nature of that change (rising vs falling). But a lot of AVR support them and those that do have a significant number of them. In the case of your ATTiny88, all of the GPIO (Arduino &quot;digital&quot;) pins are capable of dispatching a pin change interrupt.  However, the Arduino AVR core doesn't provide a function for using them.</p>
<h3>Modifying NicoHood's PinChangeInterrupt library</h3>
<p>One of the people that used to be in my circle was NicoHood, who created <a href="https://github.com/NicoHood/PinChangeInterrupt/" rel="nofollow noreferrer">this PinChangeInterrupt library</a>. I have a foggy memory of helping with bits of it.</p>
<p>What he did was model it after the normal <code>attachInterrupt</code> function. So, you can use it almost as though you have the <code>RISING</code>/<code>FALLING</code>/<code>CHANGE</code> &quot;external interrupt&quot; capability on all of your PCINT# pins; in your case all of your pins. Underneath it is tracking the state of each port pin to determine what has changed and in what direction. In other words, using an interrupt that fires given a change on any of 8 pins together with variables to synthesize per-pin callbacks for not just change but also specifically rising and falling if you want. This is not perfect it is possible to lose short lived events and it takes longer to dispatch your handler function. But it's probably fine for what you're doing.</p>
<p>It does not support your chip out of the box. However you can modify to support the ATTiny88; partially if you're not picky. You should find it in the library manager as &quot;PinChangeInterrupt&quot; by NicoHood, currently at version 1.2.9. If you install that it should show up under your sketchbook/libraries directory. You will need to edit the file at path:</p>
<p>&lt;sketchbook-directory&gt;<code>/libraries/PinChangeInterrupt/src/PinChangeInterruptBoards.h</code></p>
<p>That's your local copy of <a href="https://github.com/NicoHood/PinChangeInterrupt/blob/1.2.9/src/PinChangeInterruptBoards.h" rel="nofollow noreferrer">this file</a>.</p>
<p>Your ATTiny88's handling of PCINT is going to be nearly the same as the ATMega328P that's in the UNO. Or rather nearly a superset of it. The chips are closely related in a way.
However, you have extra PCINT capable pins on the ATTiny88 that aren't available on the ATMega328P. So you can crudely hack in a line into <a href="https://github.com/NicoHood/PinChangeInterrupt/blob/1.2.9/src/PinChangeInterruptBoards.h" rel="nofollow noreferrer">this section</a> to read:</p>
<pre class="lang-cpp prettyprint-override"><code>#if defined(__AVR_ATmega328__) || defined(__AVR_ATmega328A__) || defined(__AVR_ATmega328PA__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \
   || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega168PB__) \
   || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88A__) || defined(__AVR_ATmega88PA__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) \
   || defined(__AVR_ATtiny88__) \
   || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48A__) || defined(__AVR_ATmega48PA__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PB__)
</code></pre>
<p>The addition of that second to last line above should make the library treat the ATTiny88 you have as though it were an ATMega328P. This compiles under ATTinyCore. I am not set up to test it on a real ATTiny88. But, I expect it will work if you try some examples.</p>
<p>Having done that, the library should be work on any of the pins that carry a PCINT0 through PCINT23 label.  If you want it to work with PCINT24-PCINT27, that can be done, but requires more work. But given what you've said you probably don't need one of those four and they're not available on the DIP package which I'm guessing you have anyway.</p>
<h3>Updating your code</h3>
<p>You would to <code>#include &quot;PinChangeInterrupt.h&quot;</code> at the top of your code, and then your current code:</p>
<pre class="lang-cpp prettyprint-override"><code>    attachInterrupt(digitalPinToInterrupt(Phase_A), updateEncoder, CHANGE);
    attachInterrupt(digitalPinToInterrupt(Phase_B), updateEncoder, CHANGE);
</code></pre>
<p>then becomes:</p>
<pre class="lang-cpp prettyprint-override"><code>    attachPCINT(digitalPinToPCINT(Phase_A), updateEncoder, CHANGE);
    attachPCINT(digitalPinToPCINT(Phase_B), updateEncoder, CHANGE);
</code></pre>
<p>With luck, that should do it provided <code>Phase_A</code> and <code>Phase_B</code> under your core (e.g. ATTinyCore) map to GPIO/Arduino &quot;digital&quot; pins that carry a PCINT# signal where the number less than 24.</p>
<p>As I said, I have no way of testing it on real hardware right now. But I expect it will work. If not, it is the sort of direction you need to go in.</p>
<h3>Doing PCINT yourself</h3>
<p>You can also take a more direct approach with reading the datasheet and sorting out how to do what the library does for you, only manually. <a href="https://github.com/NicoHood/PinChangeInterrupt/blob/1.2.9/examples/PinChangeInterrupt_HowItWorks/PinChangeInterrupt_HowItWorks.ino" rel="nofollow noreferrer">One of the examples</a> NicoHood provided with the library is not really an example of using the library but rather an explanation as to what the library does in the form of code. Notice it doesn't actually include the library; it would operate stand alone. So if you want to understand the guts yourself by following the ATtiny88 datasheet, that's a place to start.</p>
<p>One reason you might want to do it yourself anyway is if your encoders are being operated quickly, to the point where you can't afford some of the extra effective dispatch time imposed by what the PinChangeInterrupt library is doing in sorting out which pin changed and how. If you handle PCINT yourself, you can make a point of putting your two encoder pins in completely different groups of 8 such that there is only one source of pin change interrupt for each ISR and so there is no need to check which pin caused the interrupt and to route that to another function.</p>
