const byte datapin = PB5;
const byte clockpin = PB4;
const byte latchpin = PB3;
const byte digits = 8;

volatile uint8_t isr_buffer[digits];
volatile uint8_t isr_digit = 0;

// which pin on the 1st shift register illuminates which digit?
uint8_t digit_pin_map[digits] = {
  1<<7, 1<<0, 1<<1, 1<<2,
  1<<6, 1<<3, 1<<4, 1<<5
};

SIGNAL(TIM0_COMPA_vect) {
  shiftOut(datapin, clockpin, LSBFIRST, isr_buffer[isr_digit]);
  shiftOut(datapin, clockpin, LSBFIRST, digit_pin_map[isr_digit]);
  digitalWrite(latchpin, HIGH);
  digitalWrite(latchpin, LOW);
  if (++isr_digit == sizeof(isr_buffer))
    isr_digit = 0;
}

void setup() {
  pinMode(datapin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  pinMode(latchpin, OUTPUT);
  // piggy back on the existing Timer0
  OCR0A = 0xAF;
  TIMSK |= _BV(OCIE0A);
}

void loop() {
  uint32_t n = 0;
  while (1) {
    write_7seg(n++, 16);
  }
}

// which pin on the 2nd shift register illuminates which segment?
enum SegmentBits {
  seg_a = 0b10000000, seg_b = 0b00100000, seg_c = 0b00001000,
  seg_d = 0b00000010, seg_e = 0b00000001, seg_f = 0b01000000,
  seg_g = 0b00010000, seg_dp = 0b00000100
};

const uint8_t segment_map[] = {
  seg_a | seg_b | seg_c | seg_d | seg_e | seg_f, // 0
  seg_b | seg_c, // 1
  seg_a | seg_b | seg_g | seg_e | seg_d, // 2
  seg_a | seg_b | seg_g | seg_c | seg_d, // 3
  seg_f | seg_g | seg_b | seg_c, // 4
  seg_a | seg_f | seg_g | seg_c | seg_d, // 5
  seg_a | seg_f | seg_e | seg_d | seg_c | seg_g, // 6
  seg_a | seg_b | seg_c, // 7
  seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g, // 8
  seg_a | seg_b | seg_c | seg_d | seg_f | seg_g, // 9

  seg_e | seg_f | seg_a | seg_b | seg_c | seg_g, // A
  seg_f | seg_e | seg_d | seg_c | seg_g, // b
  seg_g | seg_e | seg_d, // c
  seg_g | seg_e | seg_d | seg_c | seg_b, // d
  seg_a | seg_f | seg_g | seg_e | seg_d, // E
  seg_a | seg_f | seg_g | seg_e, // F
};

// base can be 2 to 16.
void write_7seg(uint32_t num, uint8_t base) {
  for (uint8_t i = 0; i < digits; i++) {
    byte digit = num % base;
    num /= base;
    isr_buffer[i] = ~segment_map[digit];
  }
  // overflow? set all the decimal points
  if (num != 0) {
    for (uint8_t i = 0; i < digits; i++)
      isr_buffer[i] ^= seg_dp;
  }
}
ATTINY8520PU
tiny:PB5
tiny:PB3
tiny:PB4
tiny:GND
tiny:PB0
tiny:PB1
tiny:PB2
tiny:VCC
sevseg1:A
sevseg1:B
sevseg1:C
sevseg1:D
sevseg1:E
sevseg1:F
sevseg1:G
sevseg1:DP
sevseg1:DIG1
sevseg1:DIG2
sevseg1:DIG3
sevseg1:DIG4
sevseg1:COM
sevseg1:CLN
sevseg2:A
sevseg2:B
sevseg2:C
sevseg2:D
sevseg2:E
sevseg2:F
sevseg2:G
sevseg2:DP
sevseg2:DIG1
sevseg2:DIG2
sevseg2:DIG3
sevseg2:DIG4
sevseg2:COM
sevseg2:CLN
74HC595
sr1:Q1
sr1:Q2
sr1:Q3
sr1:Q4
sr1:Q5
sr1:Q6
sr1:Q7
sr1:GND
sr1:Q7S
sr1:MR
sr1:SHCP
sr1:STCP
sr1:OE
sr1:DS
sr1:Q0
sr1:VCC
74HC595
sr2:Q1
sr2:Q2
sr2:Q3
sr2:Q4
sr2:Q5
sr2:Q6
sr2:Q7
sr2:GND
sr2:Q7S
sr2:MR
sr2:SHCP
sr2:STCP
sr2:OE
sr2:DS
sr2:Q0
sr2:VCC
r1:1
r1:2
r2:1
r2:2
r3:1
r3:2
r4:1
r4:2
r5:1
r5:2
r6:1
r6:2
r7:1
r7:2
r8:1
r8:2
r9:1
r9:2
r10:1
r10:2
r11:1
r11:2
r12:1
r12:2
r13:1
r13:2
r14:1
r14:2
r15:1
r15:2
r16:1
r16:2
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
led4:A
led4:C
led5:A
led5:C
led6:A
led6:C
led7:A
led7:C
led8:A
led8:C
led9:A
led9:C
led10:A
led10:C
led11:A
led11:C
led12:A
led12:C
led13:A
led13:C
led14:A
led14:C
led15:A
led15:C
led16:A
led16:C