const byte datapin = 2;
const byte clockpin = 3;
const byte latchpin = 4;
const byte digits = 8;

volatile uint8_t isr_buffer[digits];
volatile uint8_t isr_digit;

SIGNAL(TIMER0_COMPA_vect)  {
  shiftOut(datapin, clockpin, LSBFIRST, ~(1 << isr_digit));
  shiftOut(datapin, clockpin, LSBFIRST, isr_buffer[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 1kHz Timer0 used by millis()
  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);
}

void loop() {
  uint32_t num = 0;
  while (1) {
    write_7seg(num, 10);
    delay(50);
    num += random(1000);
    if (num > 99999999)
      num = 0;
  }
}

const uint8_t segment_map[] = {
  // 0 to 9
  0b11111100, 0b01100000, 0b11011010, 0b11110010, 0b01100110,
  0b10110110, 0b10111110, 0b11100000, 0b11111110, 0b11110110,
  // A to F
  0b11101110, 0b00111110, 0b00011010, 0b01111010, 0b10011110,
  0b10001110
};

// 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] |= 1;
  }
}
74HC595
74HC595