optiboot.c
1 /**********************************************************/ 2 /* Optiboot bootloader for Arduino */ 3 /* */ 4 /* http://optiboot.googlecode.com */ 5 /* */ 6 /* Arduino-maintained version : See README.TXT */ 7 /* http://code.google.com/p/arduino/ */ 8 /* */ 9 /* Heavily optimised bootloader that is faster and */ 10 /* smaller than the Arduino standard bootloader */ 11 /* */ 12 /* Enhancements: */ 13 /* Fits in 512 bytes, saving 1.5K of code space */ 14 /* Background page erasing speeds up programming */ 15 /* Higher baud rate speeds up programming */ 16 /* Written almost entirely in C */ 17 /* Customisable timeout with accurate timeconstant */ 18 /* Optional virtual UART. No hardware UART required. */ 19 /* Optional virtual boot partition for devices without. */ 20 /* */ 21 /* What you lose: */ 22 /* Implements a skeleton STK500 protocol which is */ 23 /* missing several features including EEPROM */ 24 /* programming and non-page-aligned writes */ 25 /* High baud rate breaks compatibility with standard */ 26 /* Arduino flash settings */ 27 /* */ 28 /* Fully supported: */ 29 /* ATmega168 based devices (Diecimila etc) */ 30 /* ATmega328P based devices (Duemilanove etc) */ 31 /* */ 32 /* Alpha test */ 33 /* ATmega1280 based devices (Arduino Mega) */ 34 /* */ 35 /* Work in progress: */ 36 /* ATmega644P based devices (Sanguino) */ 37 /* ATtiny84 based devices (Luminet) */ 38 /* */ 39 /* Does not support: */ 40 /* USB based devices (eg. Teensy) */ 41 /* */ 42 /* Assumptions: */ 43 /* The code makes several assumptions that reduce the */ 44 /* code size. They are all true after a hardware reset, */ 45 /* but may not be true if the bootloader is called by */ 46 /* other means or on other hardware. */ 47 /* No interrupts can occur */ 48 /* UART and Timer 1 are set to their reset state */ 49 /* SP points to RAMEND */ 50 /* */ 51 /* Code builds on code, libraries and optimisations from: */ 52 /* stk500boot.c by Jason P. Kyle */ 53 /* Arduino bootloader http://www.arduino.cc */ 54 /* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */ 55 /* avr-libc project http://nongnu.org/avr-libc */ 56 /* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */ 57 /* AVR305 Atmel Application Note */ 58 /* */ 59 /* This program is free software; you can redistribute it */ 60 /* and/or modify it under the terms of the GNU General */ 61 /* Public License as published by the Free Software */ 62 /* Foundation; either version 2 of the License, or */ 63 /* (at your option) any later version. */ 64 /* */ 65 /* This program is distributed in the hope that it will */ 66 /* be useful, but WITHOUT ANY WARRANTY; without even the */ 67 /* implied warranty of MERCHANTABILITY or FITNESS FOR A */ 68 /* PARTICULAR PURPOSE. See the GNU General Public */ 69 /* License for more details. */ 70 /* */ 71 /* You should have received a copy of the GNU General */ 72 /* Public License along with this program; if not, write */ 73 /* to the Free Software Foundation, Inc., */ 74 /* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 75 /* */ 76 /* Licence can be viewed at */ 77 /* http://www.fsf.org/licenses/gpl.txt */ 78 /* */ 79 /**********************************************************/ 80 81 82 /**********************************************************/ 83 /* */ 84 /* Optional defines: */ 85 /* */ 86 /**********************************************************/ 87 /* */ 88 /* BIG_BOOT: */ 89 /* Build a 1k bootloader, not 512 bytes. This turns on */ 90 /* extra functionality. */ 91 /* */ 92 /* BAUD_RATE: */ 93 /* Set bootloader baud rate. */ 94 /* */ 95 /* LUDICROUS_SPEED: */ 96 /* 230400 baud :-) */ 97 /* */ 98 /* SOFT_UART: */ 99 /* Use AVR305 soft-UART instead of hardware UART. */ 100 /* */ 101 /* LED_START_FLASHES: */ 102 /* Number of LED flashes on bootup. */ 103 /* */ 104 /* LED_DATA_FLASH: */ 105 /* Flash LED when transferring data. For boards without */ 106 /* TX or RX LEDs, or for people who like blinky lights. */ 107 /* */ 108 /* SUPPORT_EEPROM: */ 109 /* Support reading and writing from EEPROM. This is not */ 110 /* used by Arduino, so off by default. */ 111 /* */ 112 /* TIMEOUT_MS: */ 113 /* Bootloader timeout period, in milliseconds. */ 114 /* 500,1000,2000,4000,8000 supported. */ 115 /* */ 116 /**********************************************************/ 117 118 /**********************************************************/ 119 /* Version Numbers! */ 120 /* */ 121 /* Arduino Optiboot now includes this Version number in */ 122 /* the source and object code. */ 123 /* */ 124 /* Version 3 was released as zip from the optiboot */ 125 /* repository and was distributed with Arduino 0022. */ 126 /* Version 4 starts with the arduino repository commit */ 127 /* that brought the arduino repository up-to-date with */ 128 /* the optiboot source tree changes since v3. */ 129 /* */ 130 /**********************************************************/ 131 132 /**********************************************************/ 133 /* Edit History: */ 134 /* */ 135 /* 4.4 WestfW: add initialization of address to keep */ 136 /* the compiler happy. Change SC'ed targets. */ 137 /* Return the SW version via READ PARAM */ 138 /* 4.3 WestfW: catch framing errors in getch(), so that */ 139 /* AVRISP works without HW kludges. */ 140 /* http://code.google.com/p/arduino/issues/detail?id=368n*/ 141 /* 4.2 WestfW: reduce code size, fix timeouts, change */ 142 /* verifySpace to use WDT instead of appstart */ 143 /* 4.1 WestfW: put version number in binary. */ 144 /**********************************************************/ 145 146 #define OPTIBOOT_MAJVER 4 147 #define OPTIBOOT_MINVER 4 148 149 #define MAKESTR(a) #a 150 #define MAKEVER(a, b) MAKESTR(a*256+b) 151 152 asm(" .section .version\n" 153 "optiboot_version: .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n" 154 " .section .text\n"); 155 156 #include <inttypes.h> 157 #include <avr/io.h> 158 #include <avr/pgmspace.h> 159 160 // <avr/boot.h> uses sts instructions, but this version uses out instructions 161 // This saves cycles and program memory. 162 #include "boot.h" 163 164 165 // We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need. 166 167 #include "pin_defs.h" 168 #include "stk500.h" 169 170 #ifndef LED_START_FLASHES 171 #define LED_START_FLASHES 0 172 #endif 173 174 #ifdef LUDICROUS_SPEED 175 #define BAUD_RATE 230400L 176 #endif 177 178 /* set the UART baud rate defaults */ 179 #ifndef BAUD_RATE 180 #if F_CPU >= 8000000L 181 #define BAUD_RATE 115200L // Highest rate Avrdude win32 will support 182 #elsif F_CPU >= 1000000L 183 #define BAUD_RATE 9600L // 19200 also supported, but with significant error 184 #elsif F_CPU >= 128000L 185 #define BAUD_RATE 4800L // Good for 128kHz internal RC 186 #else 187 #define BAUD_RATE 1200L // Good even at 32768Hz 188 #endif 189 #endif 190 191 /* Switch in soft UART for hard baud rates */ 192 #if (F_CPU/BAUD_RATE) > 280 // > 57600 for 16MHz 193 #ifndef SOFT_UART 194 #define SOFT_UART 195 #endif 196 #endif 197 198 /* Watchdog settings */ 199 #define WATCHDOG_OFF (0) 200 #define WATCHDOG_16MS (_BV(WDE)) 201 #define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE)) 202 #define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE)) 203 #define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE)) 204 #define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE)) 205 #define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE)) 206 #define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE)) 207 #define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE)) 208 #ifndef __AVR_ATmega8__ 209 #define WATCHDOG_4S (_BV(WDP3) | _BV(WDE)) 210 #define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE)) 211 #endif 212 213 /* Function Prototypes */ 214 /* The main function is in init9, which removes the interrupt vector table */ 215 /* we don't need. It is also 'naked', which means the compiler does not */ 216 /* generate any entry or exit code itself. */ 217 int main(void) __attribute__ ((naked)) __attribute__ ((section (".init9"))); 218 void putch(char); 219 uint8_t getch(void); 220 static inline void getNch(uint8_t); /* "static inline" is a compiler hint to reduce code size */ 221 void verifySpace(); 222 static inline void flash_led(uint8_t); 223 uint8_t getLen(); 224 static inline void watchdogReset(); 225 void watchdogConfig(uint8_t x); 226 #ifdef SOFT_UART 227 void uartDelay() __attribute__ ((naked)); 228 #endif 229 void appStart() __attribute__ ((naked)); 230 231 #if defined(__AVR_ATmega168__) 232 #define RAMSTART (0x100) 233 #define NRWWSTART (0x3800) 234 #elif defined(__AVR_ATmega328P__) 235 #define RAMSTART (0x100) 236 #define NRWWSTART (0x7000) 237 #elif defined (__AVR_ATmega644P__) 238 #define RAMSTART (0x100) 239 #define NRWWSTART (0xE000) 240 #elif defined(__AVR_ATtiny84__) 241 #define RAMSTART (0x100) 242 #define NRWWSTART (0x0000) 243 #elif defined(__AVR_ATmega1280__) 244 #define RAMSTART (0x200) 245 #define NRWWSTART (0xE000) 246 #elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__) 247 #define RAMSTART (0x100) 248 #define NRWWSTART (0x1800) 249 #endif 250 251 /* C zero initialises all global variables. However, that requires */ 252 /* These definitions are NOT zero initialised, but that doesn't matter */ 253 /* This allows us to drop the zero init code, saving us memory */ 254 #define buff ((uint8_t*)(RAMSTART)) 255 #ifdef VIRTUAL_BOOT_PARTITION 256 #define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4)) 257 #define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6)) 258 #endif 259 260 /* main program starts here */ 261 int main(void) { 262 uint8_t ch; 263 264 /* 265 * Making these local and in registers prevents the need for initializing 266 * them, and also saves space because code no longer stores to memory. 267 * (initializing address keeps the compiler happy, but isn't really 268 * necessary, and uses 4 bytes of flash.) 269 */ 270 register uint16_t address = 0; 271 register uint8_t length; 272 273 // After the zero init loop, this is the first code to run. 274 // 275 // This code makes the following assumptions: 276 // No interrupts will execute 277 // SP points to RAMEND 278 // r1 contains zero 279 // 280 // If not, uncomment the following instructions: 281 // cli(); 282 asm volatile ("clr __zero_reg__"); 283 #ifdef __AVR_ATmega8__ 284 SP=RAMEND; // This is done by hardware reset 285 #endif 286 287 // Adaboot no-wait mod 288 ch = MCUSR; 289 MCUSR = 0; 290 if (!(ch & _BV(EXTRF))) appStart(); 291 292 #if LED_START_FLASHES > 0 293 // Set up Timer 1 for timeout counter 294 TCCR1B = _BV(CS12) | _BV(CS10); // div 1024 295 #endif 296 #ifndef SOFT_UART 297 #ifdef __AVR_ATmega8__ 298 UCSRA = _BV(U2X); //Double speed mode USART 299 UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx 300 UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1 301 UBRRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); 302 #else 303 UCSR0A = _BV(U2X0); //Double speed mode USART0 304 UCSR0B = _BV(RXEN0) | _BV(TXEN0); 305 UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); 306 UBRR0L = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); 307 #endif 308 #endif 309 310 // Set up watchdog to trigger after 500ms 311 watchdogConfig(WATCHDOG_1S); 312 313 /* Set LED pin as output */ 314 LED_DDR |= _BV(LED); 315 316 #ifdef SOFT_UART 317 /* Set TX pin as output */ 318 UART_DDR |= _BV(UART_TX_BIT); 319 #endif 320 321 #if LED_START_FLASHES > 0 322 /* Flash onboard LED to signal entering of bootloader */ 323 flash_led(LED_START_FLASHES * 2); 324 #endif 325 326 /* Forever loop */ 327 for (;;) { 328 /* get character from UART */ 329 ch = getch(); 330 331 if(ch == STK_GET_PARAMETER) { 332 unsigned char which = getch(); 333 verifySpace(); 334 if (which == 0x82) { 335 /* 336 * Send optiboot version as "minor SW version" 337 */ 338 putch(OPTIBOOT_MINVER); 339 } else if (which == 0x81) { 340 putch(OPTIBOOT_MAJVER); 341 } else { 342 /* 343 * GET PARAMETER returns a generic 0x03 reply for 344 * other parameters - enough to keep Avrdude happy 345 */ 346 putch(0x03); 347 } 348 } 349 else if(ch == STK_SET_DEVICE) { 350 // SET DEVICE is ignored 351 getNch(20); 352 } 353 else if(ch == STK_SET_DEVICE_EXT) { 354 // SET DEVICE EXT is ignored 355 getNch(5); 356 } 357 else if(ch == STK_LOAD_ADDRESS) { 358 // LOAD ADDRESS 359 uint16_t newAddress; 360 newAddress = getch(); 361 newAddress = (newAddress & 0xff) | (getch() << 8); 362 #ifdef RAMPZ 363 // Transfer top bit to RAMPZ 364 RAMPZ = (newAddress & 0x8000) ? 1 : 0; 365 #endif 366 newAddress += newAddress; // Convert from word address to byte address 367 address = newAddress; 368 verifySpace(); 369 } 370 else if(ch == STK_UNIVERSAL) { 371 // UNIVERSAL command is ignored 372 getNch(4); 373 putch(0x00); 374 } 375 /* Write memory, length is big endian and is in bytes */ 376 else if(ch == STK_PROG_PAGE) { 377 // PROGRAM PAGE - we support flash programming only, not EEPROM 378 uint8_t *bufPtr; 379 uint16_t addrPtr; 380 381 getch(); /* getlen() */ 382 length = getch(); 383 getch(); 384 385 // If we are in RWW section, immediately start page erase 386 if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address); 387 388 // While that is going on, read in page contents 389 bufPtr = buff; 390 do *bufPtr++ = getch(); 391 while (--length); 392 393 // If we are in NRWW section, page erase has to be delayed until now. 394 // Todo: Take RAMPZ into account 395 if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address); 396 397 // Read command terminator, start reply 398 verifySpace(); 399 400 // If only a partial page is to be programmed, the erase might not be complete. 401 // So check that here 402 boot_spm_busy_wait(); 403 404 #ifdef VIRTUAL_BOOT_PARTITION 405 if ((uint16_t)(void*)address == 0) { 406 // This is the reset vector page. We need to live-patch the code so the 407 // bootloader runs. 408 // 409 // Move RESET vector to WDT vector 410 uint16_t vect = buff[0] | (buff[1]<<8); 411 rstVect = vect; 412 wdtVect = buff[8] | (buff[9]<<8); 413 vect -= 4; // Instruction is a relative jump (rjmp), so recalculate. 414 buff[8] = vect & 0xff; 415 buff[9] = vect >> 8; 416 417 // Add jump to bootloader at RESET vector 418 buff[0] = 0x7f; 419 buff[1] = 0xce; // rjmp 0x1d00 instruction 420 } 421 #endif 422 423 // Copy buffer into programming buffer 424 bufPtr = buff; 425 addrPtr = (uint16_t)(void*)address; 426 ch = SPM_PAGESIZE / 2; 427 do { 428 uint16_t a; 429 a = *bufPtr++; 430 a |= (*bufPtr++) << 8; 431 __boot_page_fill_short((uint16_t)(void*)addrPtr,a); 432 addrPtr += 2; 433 } while (--ch); 434 435 // Write from programming buffer 436 __boot_page_write_short((uint16_t)(void*)address); 437 boot_spm_busy_wait(); 438 439 #if defined(RWWSRE) 440 // Reenable read access to flash 441 boot_rww_enable(); 442 #endif 443 444 } 445 /* Read memory block mode, length is big endian. */ 446 else if(ch == STK_READ_PAGE) { 447 // READ PAGE - we only read flash 448 getch(); /* getlen() */ 449 length = getch(); 450 getch(); 451 452 verifySpace(); 453 #ifdef VIRTUAL_BOOT_PARTITION 454 do { 455 // Undo vector patch in bottom page so verify passes 456 if (address == 0) ch=rstVect & 0xff; 457 else if (address == 1) ch=rstVect >> 8; 458 else if (address == 8) ch=wdtVect & 0xff; 459 else if (address == 9) ch=wdtVect >> 8; 460 else ch = pgm_read_byte_near(address); 461 address++; 462 putch(ch); 463 } while (--length); 464 #else 465 #ifdef __AVR_ATmega1280__ 466 // do putch(pgm_read_byte_near(address++)); 467 // while (--length); 468 do { 469 uint8_t result; 470 __asm__ ("elpm %0,Z\n":"=r"(result):"z"(address)); 471 putch(result); 472 address++; 473 } 474 while (--length); 475 #else 476 do putch(pgm_read_byte_near(address++)); 477 while (--length); 478 #endif 479 #endif 480 } 481 482 /* Get device signature bytes */ 483 else if(ch == STK_READ_SIGN) { 484 // READ SIGN - return what Avrdude wants to hear 485 verifySpace(); 486 putch(SIGNATURE_0); 487 putch(SIGNATURE_1); 488 putch(SIGNATURE_2); 489 } 490 else if (ch == 'Q') { 491 // Adaboot no-wait mod 492 watchdogConfig(WATCHDOG_16MS); 493 verifySpace(); 494 } 495 else { 496 // This covers the response to commands like STK_ENTER_PROGMODE 497 verifySpace(); 498 } 499 putch(STK_OK); 500 } 501 } 502 503 void putch(char ch) { 504 #ifndef SOFT_UART 505 while (!(UCSR0A & _BV(UDRE0))); 506 UDR0 = ch; 507 #else 508 __asm__ __volatile__ ( 509 " com %[ch]\n" // ones complement, carry set 510 " sec\n" 511 "1: brcc 2f\n" 512 " cbi %[uartPort],%[uartBit]\n" 513 " rjmp 3f\n" 514 "2: sbi %[uartPort],%[uartBit]\n" 515 " nop\n" 516 "3: rcall uartDelay\n" 517 " rcall uartDelay\n" 518 " lsr %[ch]\n" 519 " dec %[bitcnt]\n" 520 " brne 1b\n" 521 : 522 : 523 [bitcnt] "d" (10), 524 [ch] "r" (ch), 525 [uartPort] "I" (_SFR_IO_ADDR(UART_PORT)), 526 [uartBit] "I" (UART_TX_BIT) 527 : 528 "r25" 529 ); 530 #endif 531 } 532 533 uint8_t getch(void) { 534 uint8_t ch; 535 536 #ifdef LED_DATA_FLASH 537 #ifdef __AVR_ATmega8__ 538 LED_PORT ^= _BV(LED); 539 #else 540 LED_PIN |= _BV(LED); 541 #endif 542 #endif 543 544 #ifdef SOFT_UART 545 __asm__ __volatile__ ( 546 "1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge 547 " rjmp 1b\n" 548 " rcall uartDelay\n" // Get to middle of start bit 549 "2: rcall uartDelay\n" // Wait 1 bit period 550 " rcall uartDelay\n" // Wait 1 bit period 551 " clc\n" 552 " sbic %[uartPin],%[uartBit]\n" 553 " sec\n" 554 " dec %[bitCnt]\n" 555 " breq 3f\n" 556 " ror %[ch]\n" 557 " rjmp 2b\n" 558 "3:\n" 559 : 560 [ch] "=r" (ch) 561 : 562 [bitCnt] "d" (9), 563 [uartPin] "I" (_SFR_IO_ADDR(UART_PIN)), 564 [uartBit] "I" (UART_RX_BIT) 565 : 566 "r25" 567 ); 568 #else 569 while(!(UCSR0A & _BV(RXC0))) 570 ; 571 if (!(UCSR0A & _BV(FE0))) { 572 /* 573 * A Framing Error indicates (probably) that something is talking 574 * to us at the wrong bit rate. Assume that this is because it 575 * expects to be talking to the application, and DON'T reset the 576 * watchdog. This should cause the bootloader to abort and run 577 * the application "soon", if it keeps happening. (Note that we 578 * don't care that an invalid char is returned...) 579 */ 580 watchdogReset(); 581 } 582 583 ch = UDR0; 584 #endif 585 586 #ifdef LED_DATA_FLASH 587 #ifdef __AVR_ATmega8__ 588 LED_PORT ^= _BV(LED); 589 #else 590 LED_PIN |= _BV(LED); 591 #endif 592 #endif 593 594 return ch; 595 } 596 597 #ifdef SOFT_UART 598 // AVR350 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6) 599 // Adding 3 to numerator simulates nearest rounding for more accurate baud rates 600 #define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6) 601 #if UART_B_VALUE > 255 602 #error Baud rate too slow for soft UART 603 #endif 604 605 void uartDelay() { 606 __asm__ __volatile__ ( 607 "ldi r25,%[count]\n" 608 "1:dec r25\n" 609 "brne 1b\n" 610 "ret\n" 611 ::[count] "M" (UART_B_VALUE) 612 ); 613 } 614 #endif 615 616 void getNch(uint8_t count) { 617 do getch(); while (--count); 618 verifySpace(); 619 } 620 621 void verifySpace() { 622 if (getch() != CRC_EOP) { 623 watchdogConfig(WATCHDOG_16MS); // shorten WD timeout 624 while (1) // and busy-loop so that WD causes 625 ; // a reset and app start. 626 } 627 putch(STK_INSYNC); 628 } 629 630 #if LED_START_FLASHES > 0 631 void flash_led(uint8_t count) { 632 do { 633 TCNT1 = -(F_CPU/(1024*16)); 634 TIFR1 = _BV(TOV1); 635 while(!(TIFR1 & _BV(TOV1))); 636 #ifdef __AVR_ATmega8__ 637 LED_PORT ^= _BV(LED); 638 #else 639 LED_PIN |= _BV(LED); 640 #endif 641 watchdogReset(); 642 } while (--count); 643 } 644 #endif 645 646 // Watchdog functions. These are only safe with interrupts turned off. 647 void watchdogReset() { 648 __asm__ __volatile__ ( 649 "wdr\n" 650 ); 651 } 652 653 void watchdogConfig(uint8_t x) { 654 WDTCSR = _BV(WDCE) | _BV(WDE); 655 WDTCSR = x; 656 } 657 658 void appStart() { 659 watchdogConfig(WATCHDOG_OFF); 660 __asm__ __volatile__ ( 661 #ifdef VIRTUAL_BOOT_PARTITION 662 // Jump to WDT vector 663 "ldi r30,4\n" 664 "clr r31\n" 665 #else 666 // Jump to RST vector 667 "clr r30\n" 668 "clr r31\n" 669 #endif 670 "ijmp\n" 671 ); 672 }