 // :00000001FF
 
 void upload_program() {
-	uint16_t			address, w;
-	uint8_t *			buffer = NULL, length, *p, *q, type;
-	bool				success = false;
-
-	// The page size of ATtiny4,5,9,10 is 16 bytes (§15.3.2).
-	// AVRStudio generates Intel .hex files with 16-byte data rows
-	// in most cases, though they can be smaller.  The buffer allocation
-	// here is wide enough to receive 64 rows of 16 bytes each, plus
-	// a length byte and an address word, which would be what AVRStudio
-	// should output if all 1024 bytes of the ATtiny9,10 memory is filled.
-	// I don't expect this to overrun in most cases but it is possible if
-	// somehow the .hex file contains lots of smaller-than-page-sized rows.
-	// In that case I recommend writing a small python script or similar
-	// to preprocess the .hex file and convert it into longer rows.
-	// The Intel hex file format is here:
-	// https://en.wikipedia.org/wiki/Intel_HEX
-
-	if ( ! ( p = buffer = (uint8_t *) malloc( 64 * ( 1 + 2 + 16 ) ) ) ) {
-		Serial.println( "Could not allocate enough memory" );
-		goto done;
-	}
-
-	Serial.println( F( "Upload hex file content to serial monitor" ) );
-
-	do {
-		while ( serial_read() != ':' ) ;
-
-		length = serial_decode_byte();
-		address = (uint16_t) serial_decode_byte() << 8;
-		address |= serial_decode_byte();
-		type = serial_decode_byte();
-
-		if ( length ) {
-			if ( type == kIHTypeData ) {
-				*p++ = length;
-				*p++ = address >> 8 & 0xff;
-				*p++ = address & 0xff;
-
-				while ( length-- ) *p++ = serial_decode_byte();
-			} else {
-				while ( length-- ) serial_decode_byte();
-			}
-		}
-
-		serial_decode_byte();			// consume checksum
-	} while ( type != kIHTypeEOF );
-
-	Serial.println( F( "Upload complete" ) );
-
-	tpi_chip_erase();
-
-	Serial.println( F( "Writing flash" ) );
-
-	tpi_enable();
-
-	for ( q = buffer; q < p; ) {
-		length = *q++;
-		address = (uint16_t) *q++ << 8;
-		address |= *q++;
-
-		print_hex( length, 2 );
-		Serial.print( F( " ") );
-		print_hex( address, 4 );
-		Serial.print( F( " ") );
-
-		address |= kTiny4Flash;
-
-		if ( address & 1 ) {
-			address &= ~1;
-			print_hex( *q, 2 );
-			w = 0xff00 | *q++;
-			if ( ! tpi_memory_write_word( address, w ) ) goto done;
-			address += 2; 
-			--length;
-		}
-		for ( ; length > 1; address += 2, length -= 2 ) {
-			print_hex( *q, 2 );
-			w = (uint16_t) *q++ << 8;
-			print_hex( *q, 2 );
-			w |= *q++;
-			if ( ! tpi_memory_write_word( address, w ) ) goto done;
-		}
-		if ( length ) {
-			print_hex( *q, 2 );
-			w = (uint16_t) *q++ << 8 | 0xffu;
-			if ( ! tpi_memory_write_word( address, w ) ) goto done;
-		}
-
-		Serial.println( F( " OK" ) );
-	}
-
-	Serial.println( F( "Done" ) );
-
-	success = true;
+    uint16_t            address, w;
+    uint8_t *           buffer = NULL, length, *p, *q, type;
+    bool                success = false;
+
+    // The page size of ATtiny4,5,9,10 is 16 bytes (§15.3.2).
+    // AVRStudio generates Intel .hex files with 16-byte data rows
+    // in most cases, though they can be smaller.  The buffer allocation
+    // here is wide enough to receive 64 rows of 16 bytes each, plus
+    // a length byte and an address word, which would be what AVRStudio
+    // should output if all 1024 bytes of the ATtiny9,10 memory is filled.
+    // I don't expect this to overrun in most cases but it is possible if
+    // somehow the .hex file contains lots of smaller-than-page-sized rows.
+    // In that case I recommend writing a small python script or similar
+    // to preprocess the .hex file and convert it into longer rows.
+    // The Intel hex file format is here:
+    // https://en.wikipedia.org/wiki/Intel_HEX
+
+    if (! (p = buffer = (uint8_t *) malloc(64 * (1 + 2 + 16)))) {
+        Serial.println("Could not allocate enough memory");
+        goto done;
+    }
+
+    Serial.println(F("Upload hex file content to serial monitor"));
+
+    do {
+        while (serial_read() != ':') ;
+
+        length = serial_decode_byte();
+        address = (uint16_t) serial_decode_byte() << 8;
+        address |= serial_decode_byte();
+        type = serial_decode_byte();
+
+        if (length) {
+            if (type == kIHTypeData) {
+                *p++ = length;
+                *p++ = address >> 8 & 0xff;
+                *p++ = address & 0xff;
+
+                while (length--) *p++ = serial_decode_byte();
+            } else {
+                while (length--) serial_decode_byte();
+            }
+        }
+
+        serial_decode_byte();           // consume checksum
+    } while (type != kIHTypeEOF);
+
+    Serial.println(F("Upload complete"));
+
+    tpi_chip_erase();
+
+    Serial.println(F("Writing flash"));
+
+    tpi_enable();
+
+    for (q = buffer; q < p;) {
+        length = *q++;
+        address = (uint16_t) *q++ << 8;
+        address |= *q++;
+
+        print_hex(length, 2);
+        Serial.print(F(" "));
+        print_hex(address, 4);
+        Serial.print(F(" "));
+
+        address |= kTiny4Flash;
+
+        if (address & 1) {
+            address &= ~1;
+            print_hex(*q, 2);
+            w = 0xff00 | *q++;
+            if (! tpi_memory_write_word(address, w)) goto done;
+            address += 2; 
+            --length;
+        }
+        for (; length > 1; address += 2, length -= 2) {
+            print_hex(*q, 2);
+            w = (uint16_t) *q++ << 8;
+            print_hex(*q, 2);
+            w |= *q++;
+            if (! tpi_memory_write_word(address, w)) goto done;
+        }
+        if (length) {
+            print_hex(*q, 2);
+            w = (uint16_t) *q++ << 8 | 0xffu;
+            if (! tpi_memory_write_word(address, w)) goto done;
+        }
+
+        Serial.println(F(" OK"));
+    }
+
+    Serial.println(F("Done"));
+
+    success = true;
 
 done:
 
-	if ( ! success ) Serial.println( F( "Upload to ATtiny failed" ) );
-
-	if ( buffer ) free( buffer );
-
-	tpi_disable();
+    if (! success) Serial.println(F("Upload to ATtiny failed"));
+
+    if (buffer) free(buffer);
+
+    tpi_disable();
 }
 
 uint8_t serial_decode_byte() {
-	uint8_t				b0, b1;
-
-	b0 = serial_read();
-	b1 = serial_read();
-
-	return unhex( b0, b1 );
+    uint8_t             b0, b1;
+
+    b0 = serial_read();
+    b1 = serial_read();
+
+    return unhex(b0, b1);
 }
 
 uint8_t serial_read() {
-	while ( Serial.available() < 1 ) ;
-
-	return Serial.read();
+    while (Serial.available() < 1) ;
+
+    return Serial.read();
 }
 
 // converts two chars hexadecimal characters to the byte they represent
-uint8_t unhex( uint8_t high, uint8_t low ) {
-	uint8_t				b;
-
-	b = ( high >= '0' && high <= '9' ? high - '0' : ( high & ~0x20 ) - 'A' + 0xa ) << 4;
-	b |= low >= '0' && low <= '9' ? low - '0' : ( low & ~0x20 ) - 'A' + 0xa;
-
-	return b;
+uint8_t unhex(uint8_t high, uint8_t low) {
+    uint8_t             b;
+
+    b = (high >= '0' && high <= '9' ? high - '0' : (high & ~0x20) - 'A' + 0xa) << 4;
+    b |= low >= '0' && low <= '9' ? low - '0' : (low & ~0x20) - 'A' + 0xa;
+
+    return b;
 }
 
 // ATtiny TPI (Tiny Programming Interface) (§14 pp. 95+)
 
 bool tpi_chip_erase() {
-	tpi_enable();
-
-	// §15.4.3.1
-	// 1. write the CHIP_ERASE command to the NVMCMD register:
-	tpi_io_write( NVMCMD, NVMChipErase );
-
-	// 2. Start the erase operation by writing a dummy byte to
-	// the high byte of any word location inside the code section
-	tpi_set_pointer( kTiny4Flash + 1 );
-	tpi_memory_write( 0x1 );
-
-	// 3. Wait until the NVMBSY bit has been cleared
-	if ( tpi_nvm_wait() ) Serial.println( F( "Chip erased" ) );
-	else Serial.println( F( "Chip erase failed" ) );
-
-	tpi_disable();
-}
-
-bool tpi_css_read( uint8_t address, uint8_t &data ) {
-	// §14.5.6
-	tpi_serial_write( SLDCS | address );
-
-	return tpi_serial_read( data );
-}
-
-void tpi_css_write( uint8_t address, uint8_t data ) {
-	// §14.5.7
-	tpi_serial_write( SSTCS | address );
-	tpi_serial_write( data );
+    tpi_enable();
+
+    // §15.4.3.1
+    // 1. write the CHIP_ERASE command to the NVMCMD register:
+    tpi_io_write(NVMCMD, NVMChipErase);
+
+    // 2. Start the erase operation by writing a dummy byte to
+    // the high byte of any word location inside the code section
+    tpi_set_pointer(kTiny4Flash + 1);
+    tpi_memory_write(0x1);
+
+    // 3. Wait until the NVMBSY bit has been cleared
+    if (tpi_nvm_wait()) Serial.println(F("Chip erased"));
+    else Serial.println(F("Chip erase failed"));
+
+    tpi_disable();
+}
+
+bool tpi_css_read(uint8_t address, uint8_t &data) {
+    // §14.5.6
+    tpi_serial_write(SLDCS | address);
+
+    return tpi_serial_read(data);
+}
+
+void tpi_css_write(uint8_t address, uint8_t data) {
+    // §14.5.7
+    tpi_serial_write(SSTCS | address);
+    tpi_serial_write(data);
 }
 
 void tpi_disable() {
-	tpi_css_write( TPISR, NVMDisable );
-
-	digitalWrite( SS, HIGH );							// release RESET
-	delay( 1 );											// tRST min = 400 ns @ Vcc = 5 V
-
-	SPI.end();
-
-	tristate_arduino_spi();
+    tpi_css_write(TPISR, NVMDisable);
+
+    digitalWrite(SS, HIGH);                         // release RESET
+    delay(1);                                       // tRST min = 400 ns @ Vcc = 5 V
+
+    SPI.end();
+
+    tristate_arduino_spi();
 }
 
 void tpi_dump_memory() {
-	uint8_t				b, i;
-	uint16_t			flashEnd;
-
-	tpi_identify_device();
-
-	tpi_enable();
-	tpi_set_pointer( kTiny4IO );
-
-	switch ( gATtinyType ) {
-		case kATtiny4:
-		case kATtiny5:		flashEnd = 0x0200;		break;
-		case kATtiny9:
-		case kATtiny10:		flashEnd = 0x0400;		break;
-		case kATtiny20:		flashEnd = 0x0800;		break;
-		case kATtiny40:		flashEnd = 0x1000;		break;
-	}
-
-	flashEnd += kTiny4Flash;
-
-	while ( gTPIPointer < flashEnd ) {
-		if ( ! tpi_memory_read( b, true, false ) ) {
-			Serial.println( F( "Failed to read memory" ) );
-			goto done;
-		}
-
-		// read all the memory, but only print
-		// the register, SRAM, config and signature memory
-		if (
-			gTPIPointer >= kTiny4IO && gTPIPointer < kTiny4Reserved0 ||
-			gTPIPointer >= kTiny4NVMLock && gTPIPointer < kTiny4Reserved1 ||
-			gTPIPointer >= kTiny4Configuration && gTPIPointer < kTiny4Reserved2 ||
-			gTPIPointer >= kTiny4Calibration && gTPIPointer < kTiny4Reserved3 ||
-			gTPIPointer >= kTiny4DeviceId && gTPIPointer < kTiny4Reserved4 ||
-			gTPIPointer >= kTiny4Flash && gTPIPointer < flashEnd )
-		{
-			if (
-				gTPIPointer == kTiny4IO ||
-				gTPIPointer == kTiny4NVMLock ||
-				gTPIPointer == kTiny4Configuration ||
-				gTPIPointer == kTiny4Calibration ||
-				gTPIPointer == kTiny4DeviceId ||
-				gTPIPointer == kTiny4Flash )
-			{
-				if ( gTPIPointer != kTiny4IO ) {
-					Serial.println( F( "" ) );
-					Serial.println( F( "" ) );
-				}
-
-				if ( gTPIPointer == kTiny4IO ) Serial.println( F( "Registers, SRAM:" ) );
-				else if ( gTPIPointer == kTiny4NVMLock ) Serial.println( F( "NVM Lock:" ) );
-				else if ( gTPIPointer == kTiny4Configuration ) Serial.println( F( "Configuration:" ) );
-				else if ( gTPIPointer == kTiny4Calibration ) Serial.println( F( "Calibration:" ) );
-				else if ( gTPIPointer == kTiny4DeviceId ) Serial.println( F( "Device ID:" ) );
-				else if ( gTPIPointer == kTiny4Flash ) Serial.println( F( "Flash:" ) );
-
-				Serial.println( F( "" ) );
-
-				for ( i = 0; i < 5; ++i ) Serial.print( F( " " ) );
-				for ( i = 0; i < 16; ++i ) {
-					Serial.print( F( " +" ) );
-					Serial.print( i, HEX );
-				}
-			}
-			if ( ! ( gTPIPointer & 0x000f ) ) {
-				Serial.println( F( "" ) );
-				print_hex( gTPIPointer, 4 );
-				Serial.print( F( ": " ) );
-			}
-			print_hex( b, 2 );
-			Serial.print( F( " " ) );
-		}
-
-		++gTPIPointer;
-
-		if ( gTPIPointer == kTiny4Reserved0 ) tpi_set_pointer( kTiny4NVMLock );
-	}
-
-	Serial.println( F( "" ) );
+    uint8_t             b, i;
+    uint16_t            flashEnd;
+
+    tpi_identify_device();
+
+    tpi_enable();
+    tpi_set_pointer(kTiny4IO);
+
+    switch (gATtinyType) {
+        case kATtiny4:
+        case kATtiny5:      flashEnd = 0x0200;      break;
+        case kATtiny9:
+        case kATtiny10:     flashEnd = 0x0400;      break;
+        case kATtiny20:     flashEnd = 0x0800;      break;
+        case kATtiny40:     flashEnd = 0x1000;      break;
+    }
+
+    flashEnd += kTiny4Flash;
+
+    while (gTPIPointer < flashEnd) {
+        if (! tpi_memory_read(b, true, false)) {
+            Serial.println(F("Failed to read memory"));
+            goto done;
+        }
+
+        // read all the memory, but only print
+        // the register, SRAM, config and signature memory
+        if (
+            gTPIPointer >= kTiny4IO && gTPIPointer < kTiny4Reserved0 ||
+            gTPIPointer >= kTiny4NVMLock && gTPIPointer < kTiny4Reserved1 ||
+            gTPIPointer >= kTiny4Configuration && gTPIPointer < kTiny4Reserved2 ||
+            gTPIPointer >= kTiny4Calibration && gTPIPointer < kTiny4Reserved3 ||
+            gTPIPointer >= kTiny4DeviceId && gTPIPointer < kTiny4Reserved4 ||
+            gTPIPointer >= kTiny4Flash && gTPIPointer < flashEnd)
+        {
+            if (
+                gTPIPointer == kTiny4IO ||
+                gTPIPointer == kTiny4NVMLock ||
+                gTPIPointer == kTiny4Configuration ||
+                gTPIPointer == kTiny4Calibration ||
+                gTPIPointer == kTiny4DeviceId ||
+                gTPIPointer == kTiny4Flash)
+            {
+                if (gTPIPointer != kTiny4IO) {
+                    Serial.println(F(""));
+                    Serial.println(F(""));
+                }
+
+                if (gTPIPointer == kTiny4IO) Serial.println(F("Registers, SRAM:"));
+                else if (gTPIPointer == kTiny4NVMLock) Serial.println(F("NVM Lock:"));
+                else if (gTPIPointer == kTiny4Configuration) Serial.println(F("Configuration:"));
+                else if (gTPIPointer == kTiny4Calibration) Serial.println(F("Calibration:"));
+                else if (gTPIPointer == kTiny4DeviceId) Serial.println(F("Device ID:"));
+                else if (gTPIPointer == kTiny4Flash) Serial.println(F("Flash:"));
+
+                Serial.println(F(""));
+
+                for (i = 0; i < 5; ++i) Serial.print(F(" "));
+                for (i = 0; i < 16; ++i) {
+                    Serial.print(F(" +"));
+                    Serial.print(i, HEX);
+                }
+            }
+            if (! (gTPIPointer & 0x000f)) {
+                Serial.println(F(""));
+                print_hex(gTPIPointer, 4);
+                Serial.print(F(": "));
+            }
+            print_hex(b, 2);
+            Serial.print(F(" "));
+        }
+
+        ++gTPIPointer;
+
+        if (gTPIPointer == kTiny4Reserved0) tpi_set_pointer(kTiny4NVMLock);
+    }
+
+    Serial.println(F(""));
 
 done:
 
-	tpi_disable();
+    tpi_disable();
 }
 
 bool tpi_enable() {
-	uint8_t				data;
-	uint64_t			nvmKey = 0x1289ab45cdd888ffull;	// §14.6
-
-	// §14.3.1, p. 96, enable TPI
-
-	SPI.begin();
-	SPI.setBitOrder( LSBFIRST );
-	SPI.setDataMode( SPI_MODE0 );
-	SPI.setClockDivider( SPI_CLOCK_DIV16 );				// §16.8 2MHz maximum serial programming frequency
-														// I'm using Arduino Nano (16MHz), so set serial clock to 1MHz
-
-	digitalWrite( SS, LOW );							// assert RESET on tiny
-	delay( 1 );											// §16.5 tRST min = 400ns @ Vcc = 5 V
-
-	SPI.transfer( 0xff );								// activate TPI by emitting
-	SPI.transfer( 0xff );								// 16 or more pulses on TPICLK
-														// while holding TPIDATA to "1"
-
-	// §14.7.2
-	tpi_css_write( TPIPCR, GT2 );						// TPIPCR, guard time = 8 idle bits (default=128)
-
-	tpi_serial_write( SKEY );							// enable NVM
-	do { tpi_serial_write( nvmKey & 0xff ); } while ( nvmKey >>= 8 );
-	
-	// §14.7.3 wait for NVM to enable
-	do {
-		if ( ! tpi_css_read( TPISR, data ) ) {
-			Serial.println( F( "Failed to enable TPI" ) );
-			return false;
-		}
-	} while ( ! data );
-
-	return true;
+    uint8_t             data;
+    uint64_t            nvmKey = 0x1289ab45cdd888ffull; // §14.6
+
+    // §14.3.1, p. 96, enable TPI
+
+    SPI.begin();
+    SPI.setBitOrder(LSBFIRST);
+    SPI.setDataMode(SPI_MODE0);
+    SPI.setClockDivider(SPI_CLOCK_DIV16);           // §16.8 2MHz maximum serial programming frequency
+                                                    // I'm using Arduino Nano (16MHz), so set serial clock to 1MHz
+
+    digitalWrite(SS, LOW);                          // assert RESET on tiny
+    delay(1);										// §16.5 tRST min = 400ns @ Vcc = 5 V
+
+    SPI.transfer(0xff);                             // activate TPI by emitting
+    SPI.transfer(0xff);                             // 16 or more pulses on TPICLK
+                                                    // while holding TPIDATA to "1"
+
+    // §14.7.2
+    tpi_css_write(TPIPCR, GT2);                     // TPIPCR, guard time = 8 idle bits (default=128)
+
+    tpi_serial_write(SKEY);                         // enable NVM
+    do { tpi_serial_write(nvmKey & 0xff); } while (nvmKey >>= 8);
+    
+    // §14.7.3 wait for NVM to enable
+    do {
+        if (! tpi_css_read(TPISR, data)) {
+            Serial.println(F("Failed to enable TPI"));
+            return false;
+        }
+    } while (! data);
+
+    return true;
 }
 
 bool tpi_identify_device() {
-	uint8_t				b0, b1, b2;
-
-	gATtinyType = kATtinyUnknown;
-
-	tpi_enable();
-	tpi_set_pointer( kTiny4DeviceId );
-
-	if ( ! tpi_memory_read( b0 ) ) goto done;
-	if ( ! tpi_memory_read( b1 ) ) goto done;
-	if ( ! tpi_memory_read( b2 ) ) goto done;
-
-	// §15.3.4
-	if ( b0 == 0x1e ) {
-		switch ( b1 ) {
-			case 0x8f: {
-				switch ( b2 ) {
-					case 0x0a:		gATtinyType = kATtiny4;		break;
-					case 0x09:		gATtinyType = kATtiny5;		break;
-				}
-			} break;
-
-			case 0x90: {
-				switch ( b2 ) {
-					case 0x08:		gATtinyType = kATtiny9;		break;
-					case 0x03:		gATtinyType = kATtiny10;	break;
-				}
-			} break;
-
-			case 0x91: {
-				switch ( b2 ) {
-					case 0x0f:		gATtinyType = kATtiny20;	break;
-				}
-			} break;
-
-			case 0x92: {
-				switch ( b2 ) {
-					case 0x0e:		gATtinyType = kATtiny40;	break;
-				}
-			} break;
-		}
-	}
-
-	if ( gATtinyType ) {
-		Serial.print( F( "ATtiny" ) );
-		Serial.print( gATtinyType );
-		Serial.println( F( " connected" ) );
-	} else {
-		Serial.println( F( "Unable to identify device" ) );
-	}
+    uint8_t             b0, b1, b2;
+
+    gATtinyType = kATtinyUnknown;
+
+    tpi_enable();
+    tpi_set_pointer(kTiny4DeviceId);
+
+    if (! tpi_memory_read(b0)) goto done;
+    if (! tpi_memory_read(b1)) goto done;
+    if (! tpi_memory_read(b2)) goto done;
+
+    // §15.3.4
+    if (b0 == 0x1e) {
+        switch (b1) {
+            case 0x8f: {
+                switch (b2) {
+                    case 0x0a:      gATtinyType = kATtiny4;     break;
+                    case 0x09:      gATtinyType = kATtiny5;     break;
+                }
+            } break;
+
+            case 0x90: {
+                switch (b2) {
+                    case 0x08:      gATtinyType = kATtiny9;     break;
+                    case 0x03:      gATtinyType = kATtiny10;    break;
+                }
+            } break;
+
+            case 0x91: {
+                switch (b2) {
+                    case 0x0f:      gATtinyType = kATtiny20;    break;
+                }
+            } break;
+
+            case 0x92: {
+                switch (b2) {
+                    case 0x0e:      gATtinyType = kATtiny40;    break;
+                }
+            } break;
+        }
+    }
+
+    if (gATtinyType) {
+        Serial.print(F("ATtiny"));
+        Serial.print(gATtinyType);
+        Serial.println(F(" connected"));
+    } else {
+        Serial.println(F("Unable to identify device"));
+    }
 
 done:
 
-	tpi_disable();
-
-	return gATtinyType != kATtinyUnknown;
-}
-
-bool tpi_io_read( uint8_t address, uint8_t &data ) {
-	// §14.5.4 SIN 0b0aa1aaaa replace a with 6 address bits
-	tpi_serial_write( SIN | ( address & 0x30 ) << 1 | address & 0x0f );
-
-	return tpi_serial_read( data );
-}
-
-void tpi_io_write( uint8_t address, uint8_t data ) {
-	// §14.5.5 SOUT 0b1aa1aaaa replace a with 6 address bits
-	tpi_serial_write( SOUT | ( address & 0x30 ) << 1 | address & 0x0f );
-	tpi_serial_write( data );
+    tpi_disable();
+
+    return gATtinyType != kATtinyUnknown;
+}
+
+bool tpi_io_read(uint8_t address, uint8_t &data) {
+    // §14.5.4 SIN 0b0aa1aaaa replace a with 6 address bits
+    tpi_serial_write(SIN | (address & 0x30) << 1 | address & 0x0f);
+
+    return tpi_serial_read(data);
+}
+
+void tpi_io_write(uint8_t address, uint8_t data) {
+    // §14.5.5 SOUT 0b1aa1aaaa replace a with 6 address bits
+    tpi_serial_write(SOUT | (address & 0x30) << 1 | address & 0x0f);
+    tpi_serial_write(data);
 }
 
 // read memory indirectly.  must call tpi_set_pointer() first
-bool tpi_memory_read( uint8_t &data, bool postIncrement, bool updateGTPIPointer ) {
-	tpi_serial_write( postIncrement ? SLD_postIncrement : SLD );
-
-	if ( tpi_serial_read( data ) ) {
-		if ( postIncrement && updateGTPIPointer ) ++gTPIPointer;
-		return true;
-	}
-
-	return false;
+bool tpi_memory_read(uint8_t &data, bool postIncrement, bool updateGTPIPointer) {
+    tpi_serial_write(postIncrement ? SLD_postIncrement : SLD);
+
+    if (tpi_serial_read(data)) {
+        if (postIncrement && updateGTPIPointer) ++gTPIPointer;
+        return true;
+    }
+
+    return false;
 }
 
 // write memory indirectly.  must call tpi_set_pointer() first
-bool tpi_memory_write( uint8_t data, bool postIncrement, bool updateGTPIPointer ) {
-	tpi_serial_write( postIncrement ? SST_postIncrement : SST );
-	tpi_serial_write( data );
-
-	if ( postIncrement && updateGTPIPointer ) ++gTPIPointer;
-}
-
-bool tpi_memory_write_word( uint16_t address, uint16_t data ) {
-	tpi_io_write( NVMCMD, NVMWordWrite );
-
-	tpi_set_pointer( address );
-	tpi_memory_write( data >> 8 & 0xff );
-	tpi_memory_write( data & 0xff );
-
-	if ( ! tpi_nvm_wait() ) {
-		Serial.println( F( "tpi_memory_write_word() failed" ) );
-		return false;
-	}
-
-	return true;
+bool tpi_memory_write(uint8_t data, bool postIncrement, bool updateGTPIPointer) {
+    tpi_serial_write(postIncrement ? SST_postIncrement : SST);
+    tpi_serial_write(data);
+
+    if (postIncrement && updateGTPIPointer) ++gTPIPointer;
+}
+
+bool tpi_memory_write_word(uint16_t address, uint16_t data) {
+    tpi_io_write(NVMCMD, NVMWordWrite);
+
+    tpi_set_pointer(address);
+    tpi_memory_write(data >> 8 & 0xff);
+    tpi_memory_write(data & 0xff);
+
+    if (! tpi_nvm_wait()) {
+        Serial.println(F("tpi_memory_write_word() failed"));
+        return false;
+    }
+
+    return true;
 }
 
 bool tpi_nvm_wait() {
-	uint8_t				b;
-
-	do {
-		if ( ! tpi_io_read( NVMCSR, b ) ) return false;
-	} while ( b & NVMBusy );
-
-	return true;
+    uint8_t             b;
+
+    do {
+        if (! tpi_io_read(NVMCSR, b)) return false;
+    } while (b & NVMBusy);
+
+    return true;
 }
 
 // read TPI 12-bit format byte data via SPI
 // 2 bytes (16 clocks) or 3 bytes (24 clocks)
-bool tpi_serial_read( uint8_t &data ) {
-	uint8_t				b0, b1;
-
-	// keep transmitting high(idle) while waiting for a start bit
-	while ( ( b0 = SPI.transfer( 0xff ) ) == 0xff ) ;
-
-	// get (partial) data bits
-	b1 = SPI.transfer( 0xff );
-
-	// if the first byte(b0) contains less than 4 data bits
-	// we need to get a third byte to get the parity and stop bits
-	if ( ( b0 & 0x0f ) == 0x0f ) SPI.transfer( 0xff );
-
-	// b0 should hold only idle and start bits = 0b01111111
-	// if it doesn't, shift (b1|b0) left until it does.
-	// bd 2014.09.26: b0 == 0xff will cause an infinite loop, so test for it
-	if ( b0 == 0xff ) {
-		Serial.println( F( "tpi_serial_read() got bad data" ) );
-		return false;
-	}
-	while ( b0 != 0x7f ) {
-		b1 <<= 1;
-		if ( b0 & 0x80 ) b1 |= 1;
-		b0 <<= 1;
-		b0 |= 1;									// fill with idle bit
-	}
-
-	// now the data byte is stored in b1
-	data = b1;
-
-	return true;
+bool tpi_serial_read(uint8_t &data) {
+    uint8_t             b0, b1;
+
+    // keep transmitting high(idle) while waiting for a start bit
+    while ((b0 = SPI.transfer(0xff)) == 0xff) ;
+
+    // get (partial) data bits
+    b1 = SPI.transfer(0xff);
+
+    // if the first byte(b0) contains less than 4 data bits
+    // we need to get a third byte to get the parity and stop bits
+    if ((b0 & 0x0f) == 0x0f) SPI.transfer(0xff);
+
+    // b0 should hold only idle and start bits = 0b01111111
+    // if it doesn't, shift (b1|b0) left until it does.
+    // bd 2014.09.26: b0 == 0xff will cause an infinite loop, so test for it
+    if (b0 == 0xff) {
+        Serial.println(F("tpi_serial_read() got bad data"));
+        return false;
+    }
+    while (b0 != 0x7f) {
+        b1 <<= 1;
+        if (b0 & 0x80) b1 |= 1;
+        b0 <<= 1;
+        b0 |= 1;                                    // fill with idle bit
+    }
+
+    // now the data byte is stored in b1
+    data = b1;
+
+    return true;
 }
 
 // Send a byte in one TPI frame (12 bits: 1 start + 8 data + 1 parity + 2 stop)
 // using 2 SPI data bytes (2 x 8 = 16 clocks) with 4 extra idle bits
-void tpi_serial_write( uint8_t data ) {
-	// compute parity bit
-	uint8_t				parity = data;
-
-	// §14.3.4 p. 97
-	// even parity function is:
-	// d0 ^ d1 ^ d2 ^ d3 ^ d4 ^ d5 ^ d6 ^ d7 ^ 0
-
-	// due to commutativity, this becomes:
-	// d0 ^ d4 ^ d1 ^ d5 ^ d2 ^ d6 ^ d3 ^ d7 ^ 0
-	// due to associativity, this becomes:
-	// ( d0 ^ d4 ) ^ ( d1 ^ d5 ) ^ ( d2 ^ d6 ) ^ ( d3 ^ d7 ) ^ 0
-	parity ^= ( parity >> 4 );		// b[7:4] ^ b[3:0]
-	// which yields:
-	// d0 ^ d1 ^ d2 ^ d3 ^ 0
-	// due to commutativity:
-	// d0 ^ d2 ^ d1 ^ d3 ^ 0
-	// due to associativity:
-	// ( d0 ^ d2 ) ^ ( d1 ^ d3 ) ^ 0
-	parity ^= ( parity >> 2 );		// b[3:2] ^ b[1:0]
-	// which yields:
-	// d0 ^ d1 ^ 0
-	// due to associativity:
-	// ( d0 ^ d1 ) ^ 0
-	parity ^= ( parity >> 1 );		// b[1] ^ b[0]
-	// which yields:
-	// d0 ^ 0
-	// which yields parity bit in d0
-
-	// TPI transmits data in LSBfirst mode and idle is high
-	// 2 idle(high) + 1 start bit(low) + data[4:0]
-	SPI.transfer( 0x03 | data << 3 );
-
-	// data[7:5] + 1 parity + 2 stop bits(high) + 2 idle(high)
-	SPI.transfer( 0xf0 | parity << 3 | data >> 5 );
-}
-
-void tpi_set_pointer( unsigned short address ) {
-	// §14.5.3
-	tpi_serial_write( SSTPR_low );
-	tpi_serial_write( address & 0xff );
-	tpi_serial_write( SSTPR_high );
-	tpi_serial_write( address >> 8 & 0xff );
-
-	gTPIPointer = address;
-}
+void tpi_serial_write(uint8_t data) {
+    // compute parity bit
+    uint8_t             parity = data;
+
+    // §14.3.4 p. 97
+    // even parity function is:
+    // d0 ^ d1 ^ d2 ^ d3 ^ d4 ^ d5 ^ d6 ^ d7 ^ 0
+
+    // due to commutativity, this becomes:
+    // d0 ^ d4 ^ d1 ^ d5 ^ d2 ^ d6 ^ d3 ^ d7 ^ 0
+    // due to associativity, this becomes:
+    // (d0 ^ d4) ^ (d1 ^ d5) ^ (d2 ^ d6) ^ (d3 ^ d7) ^ 0
+    parity ^= (parity >> 4);        // b[7:4] ^ b[3:0]
+    // which yields:
+    // d0 ^ d1 ^ d2 ^ d3 ^ 0
+    // due to commutativity:
+    // d0 ^ d2 ^ d1 ^ d3 ^ 0
+    // due to associativity:
+    // (d0 ^ d2) ^ (d1 ^ d3) ^ 0
+    parity ^= (parity >> 2);        // b[3:2] ^ b[1:0]
+    // which yields:
+    // d0 ^ d1 ^ 0
+    // due to associativity:
+    // (d0 ^ d1) ^ 0
+    parity ^= (parity >> 1);        // b[1] ^ b[0]
+    // which yields:
+    // d0 ^ 0
+    // which yields parity bit in d0
+
+    // TPI transmits data in LSBfirst mode and idle is high
+    // 2 idle(high) + 1 start bit(low) + data[4:0]
+    SPI.transfer(0x03 | data << 3);
+
+    // data[7:5] + 1 parity + 2 stop bits(high) + 2 idle(high)
+    SPI.transfer(0xf0 | parity << 3 | data >> 5);
+}
+
+void tpi_set_pointer(unsigned short address) {
+    // §14.5.3
+    tpi_serial_write(SSTPR_low);
+    tpi_serial_write(address & 0xff);
+    tpi_serial_write(SSTPR_high);
+    tpi_serial_write(address >> 8 & 0xff);
+
+    gTPIPointer = address;
+}

#include "pins_arduino.h"

enum ArduinoPortBBits {
	kPortBSS			=	1 << 2,
	kPortBMOSI			=	1 << 3,
	kPortBMISO			=	1 << 4,
	kPortBSCK			=	1 << 5,
};

enum ATtiny4_5_9_10AddressSpace {
	// §5.2
	kTiny4IO			=	0x0000,	// 0x0000 - 0x003f
	kTiny4SRAM			=	0x0040,	// 0x0040 - 0x005f
	kTiny4Reserved0		=	0x0060,	// 0x0060 - 0x3eff
	kTiny4NVMLock		=	0x3f00,	// 0x3f00 - 0x3f01
	kTiny4Reserved1		=	0x3f02,	// 0x3f02 - 0x3f3f
	kTiny4Configuration	=	0x3f40, // 0x3f40 - 0x3f41
	kTiny4Reserved2		=	0x3f42,	// 0x3f42 - 0x3f7f
	kTiny4Calibration	=	0x3f80,	// 0x3f80 - 0x3f81
	kTiny4Reserved3		=	0x3f82,	// 0x3f82 - 0x3fbf
	kTiny4DeviceId		=	0x3fc0, // 0x3fc0 - 0x3fc3
	kTiny4Reserved4		=	0x3fc4,	// 0x3fc4 - 0x3fff
	kTiny4Flash			=	0x4000, // 0x4000 - 0x41ff (tiny 4/5), 0x4000 - 0x43ff (tiny 9/10)
};

enum ATtinyType {
	kATtinyUnknown,

	kATtiny4			=	4,
	kATtiny5			=	5,
	kATtiny9			=	9,
	kATtiny10			=	10,
	kATtiny20			=	20,
	kATtiny40			=	40,
};

enum IntelHexFileType {
	kIHTypeData			=	0,
	kIHTypeEOF			=	1,
	kIHTypeExtSegAddr	=	2,
	kIHTypeStartSegAddr	=	3,
	kIHTypeExtLinAddr	=	4,
	kIHTypeStartLinAddr	=	5,
};

enum TPICSS {
	TPIIR				=	0x0f,	// §14.7.1
	TPIPCR				=	0x02,	// §14.7.2
	TPISR				=	0x00,	// §14.7.3

	GT0					=	0x01,	// §14.7.2 guard time bit 0
	GT1					=	0x02,	// §14.7.2 guard time bit 1
	GT2					=	0x04,	// §14.7.2 guard time bit 2
	NVMDisable			=	0x00,	// §14.7.3
	NVMEnable			=	0x02,	// §14.7.3
	TPIIdentification	=	0x80,	// §14.7.1
};

// §14.5
enum TPIInstruction {
	SIN					=	0x10,	// serial in from i/o space
	SKEY				=	0xe0,	// serial key signaling
	SLD					=	0x20,	// serial load from data space
	SLD_postIncrement	=	0x24,	// serial load from data space with post increment
	SLDCS				=	0x80,	// serial load from control and status space
	SOUT				=	0x90,	// serial out to i/o space
	SST					=	0x60,	// serial store to data space
	SST_postIncrement	=	0x64,	// serial store to data space with post increment
	SSTCS				=	0xc0,	// serial store to control and status space
	SSTPR_high			=	0x69,	// serial store to pointer register high byte
	SSTPR_low			=	0x68,	// serial store to pointer register low byte
};

enum TPINVM {
	NVMCMD				=	0x33,	// §15.7.2
	NVMCSR				=	0x32,	// §15.7.1

	NVMBusy				=	0x80,	// §15.7.1
	NVMChipErase		=	0x10,	// §15.7.2
	NVMNop				=	0x00,	// §15.7.2
	NVMSectionErase		=	0x14,	// §15.7.2
	NVMWordWrite		=	0x1d,	// §15.7.2
};

ATtinyType				gATtinyType;
uint16_t				gTPIPointer;

bool tpi_memory_read( uint8_t &data, bool postIncrement = true, bool updateGTPIPointer = true );
bool tpi_memory_write( uint8_t data, bool postIncrement = true, bool updateGTPIPointer = true );


uint16_t get_free_memory() {
  uint16_t				v; 
  extern uint16_t		__heap_start, *__brkval;

  return (uint16_t) &v - ( __brkval ? (uint16_t) &__heap_start : (uint16_t) __brkval );
}

void setup() {
	tristate_arduino_spi();		// so we can test the tiny at power on

	Serial.begin( 115200 );
}

void loop() {
	Serial.print( F( "Command [d,e,i,m,u,v,?] > " ) );

	for ( ;; ) {
		switch ( serial_read() ) {
			case 'd': case 'D':		tpi_dump_memory();			break;
			case 'e': case 'E':		tpi_chip_erase();			break;
			case 'i': case 'I':		tpi_identify_device();		break;
			case 'm': case 'M':		print_free_memory();		break;
			case 'u': case 'U':		upload_program();			break;
			case 'v': case 'V':		print_version();			break;

			case ' ': case '\t': case '\r': case '\n':			continue;

			default: {
				Serial.println( F( "Commands:" ) );
				Serial.println( F( "D: Dump memory" ) );
				Serial.println( F( "E: Erase memory" ) );
				Serial.println( F( "I: Identify device" ) );
				Serial.println( F( "M: Print free memory" ) );
				Serial.println( F( "U: Upload program" ) );
				Serial.println( F( "V: Print version" ) );
				Serial.println( F( "?: Help" ) );
			} break;
		}

		break;
	}
}

void print_free_memory() {
	Serial.print( F( "Free memory: " ) );
	Serial.print( get_free_memory() );
	Serial.println( F( " bytes" ) );
}

void print_hex( uint16_t value, int8_t nibbles ) {
	while ( --nibbles > 0 ) {
		if ( value >> nibbles * 4 & 0x0f ) break;		// if msb is != 0

		Serial.print( F( "0" ) );
	}

	Serial.print( value, HEX );
}

void print_version() {
	Serial.println( F( "Version 1.0" ) );
}

void tristate_arduino_spi() {
	const uint8_t		kPortBTriState = ~( kPortBSS | kPortBMOSI | kPortBMISO | kPortBSCK );

	DDRB &= kPortBTriState;								// tri-state SPI so ATtiny can be tested
	PORTB &= kPortBTriState;
}

// I had tons of failures trying to write the ATtiny10 while

//    (HVP)   D9 |---       | 6 (RESET#/PB3)
//               |          |
//
//  -[R]-  =  2.2kΩ resistor
//
//  2011.12.08:     Original image by pcm1723
//
//  2014.09.26 bd:  The pcm1723 image had resistors on D10, D12, and D13.
//                  Both the Arduino Nano and the ATtiny10 operate at 5V and
//                  are rated at 40mA per pin (ATtiny reference §16.1 p. 115,
//                  Nano via online) so I'm getting rid of all the resistors
