forked from adafruit/Adafruit-Trinket-Gemma-Bootloader
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathboot.c
executable file
·639 lines (567 loc) · 19.3 KB
/
boot.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
/* GemmaBoot by [email protected]
*
* GemmaBoot is a bootloader that emulates a USBtinyISP (from Adafruit Industries)
*
* Gemma (from Adafruit Industries) will use GemmaBoot
*
* This code is heavily derived from USBaspLoader, but also from USBtiny, with USBtinyISP's settings
Copyright (c) 2013 Adafruit Industries
All rights reserved.
GemmaBoot is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
GemmaBoot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with GemmaBoot. If not, see
<http://www.gnu.org/licenses/>.
*/
/*
Some important notes
This code also borrows from http://embedded-creations.com/projects/attiny85-usb-bootloader-overview/ , but using USBtiny protocol instead of USBasp
This chip does not have RWW, so the CPU is halted during flash erase and write. This fact is why it will miss USB communication during flash operations. A small change to avrdude.conf to extend the delays to make this less of a problem but it is still a problem.
The CPU will be halted during flash operations, so you do not need to wait for SPM to finish, but interrupts must be disabled before calling SPM because it can only be called within 4 cycles of writing to SPMCSR
The datasheet says each flash operation will take a maximum of 4.5ms
Another challenge is how to retarget the interrupt jump table.
The jump table is generated by "jump.asm", this file ensures that the bootloader is executed first before the userapp, and that PCINT is targeted within the bootloader first. The two jumps inside this region is protected from being overwritten by this code.
The page before the BOOTLOADER_ADDRESS is used to store 2 additional jump vectors that jumps into the user app, therefor these jump instructions must not be used by the userapp. These instructions are protected from being overwritten by this code.
A magic byte at the bottom of the stack is checked to see where to jump when the PCINT ISR fires.
*/
#include <avr/io.h>
#ifndef MCUSR
#define MCUSR MCUCSR // hack for enabling ATmega8 support
#endif
#define SIGRD 5 // this is missing from some of the io.h files, this is a hack so avr/boot.h can be used
#include <avr/boot.h>
#include <avr/pgmspace.h>
//#include <avr/fuse.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <util/delay.h>
#include <usbconfig.h>
#include <bootloaderconfig.h>
#include <osccal.h>
#include <usbdrv/usbdrv.c> // must be included, because of static function declarations are being used, which saves flash space
// enable features here
#define ENABLE_FLASH_WRITING
#define ENABLE_FLASH_READING
//#define ENABLE_EEPROM_WRITING
//#define ENABLE_EEPROM_READING
#define ENABLE_SIG_READING
//#define ENABLE_SIG_FAKING // this actually uses 2 more bytes, so doesn't actually save space
//#define ENABLE_FUSE_READING
#define ENABLE_CHIP_ERASE
#define ENABLE_WRITE_WHEN_TOLD
#define ENABLE_LED_BLINK
#define ENABLE_RESTORE_OSCCAL
// timeout settings
#define ENTER_TIMEOUT_MS 1000
#define EXIT_TIMEOUT_MS 500
#if (F_CPU == 16500000)
#define ACTIVITY_TIMEOUT_MS_16 2 // 2 is about 20 seconds, not precise!
#elif (F_CPU == 12000000)
#define ACTIVITY_TIMEOUT_MS_16 3 // 4 is about 20 seconds, not precise!
#else
#error F_CPU is not defined
#endif
#ifdef ENABLE_LED_BLINK
// blinks the pin (fades using timer)
#define LED_PIN 1
#define LED_BLINK() do { OCR0B += 16; if (OCR0B == 0) TCCR0A ^= _BV(COM0B0); } while (0) /* PORTB ^= _BV(LED_PIN); */
#endif
enum
{
// Generic requests
USBTINY_ECHO = 0, // echo test
USBTINY_READ, // read byte
USBTINY_WRITE, // write byte
USBTINY_CLR, // clear bit
USBTINY_SET, // set bit
// Programming requests
USBTINY_POWERUP, // apply power (wValue:SCK-period, wIndex:RESET)
USBTINY_POWERDOWN, // remove power from chip
USBTINY_SPI = 7, // issue SPI command (wValue:c1c0, wIndex:c3c2)
USBTINY_POLL_BYTES, // set poll bytes for write (wValue:p1p2)
USBTINY_FLASH_READ = 9, // read flash (wIndex:address)
USBTINY_FLASH_WRITE = 10, // write flash (wIndex:address, wValue:timeout)
USBTINY_EEPROM_READ = 11, // read eeprom (wIndex:address)
USBTINY_EEPROM_WRITE = 12, // write eeprom (wIndex:address, wValue:timeout)
USBTINY_DDRWRITE, // set port direction
USBTINY_SPI1 // a single SPI command
};
#if (FLASHEND) > 0xFFFF // need long addressing for large flash
# define CUR_ADDR cur_addr.addr
# define ERASE_ADDR erase_addr.addr
# define addr_t uint32_t
#else
# define CUR_ADDR cur_addr.u16[0]
# define ERASE_ADDR erase_addr.u16[0]
# define addr_t uint16_t
#endif
typedef union longConverter { // utility for manipulating address pointer with proper endianness
addr_t addr;
uint16_t u16[sizeof(addr_t)/2];
uint8_t u8[sizeof(addr_t)];
} longConverter_t;
static uint8_t usb_maybe_finalizing = 0;
static uint8_t usb_has_activity = 0;
static longConverter_t cur_addr;
static longConverter_t erase_addr;
static uchar dirty = 0; // if flash needs to be written
static uint16_t idle_cnt = 0; // used for USB timeouts
static uint8_t idle_cnt2 = 0; // overflow for idle_cnt, 32 bit cost too much flash, so we are using 24 bitT/RS>
static uchar cmd0; // current read/write command byte
static uint8_t remaining; // bytes remaining in current transaction
static uchar buffer[8]; // talk via setup
#if !defined(ENABLE_CHIP_ERASE) && defined(ENABLE_WRITE_WHEN_TOLD)
static char erase_next = 0; // automatically erase next page when writing current page
#endif
#ifdef ENABLE_CHIP_ERASE
static char full_erase_requested = 0; // programmer requested a full chip erase
#endif
#ifdef ENABLE_WRITE_WHEN_TOLD
static char need_write_page = 0; // programmer requested a page write
#endif
#ifdef ENABLE_LED_BLINK
static uint8_t blink_cnt = 0; // times the LED brightness incrementation
#endif
volatile char usbHasRxed = 0; // whether or not USB comm is active
static uint16_t fake_page[SPM_PAGESIZE]; // we can't write to the first page while interrupts are active, so store it in RAM instead
static char fake_page_used = 0; // indicates whether or not there's a fake page waiting to be written
static char last_page_used = 0; // indicates whether or not the user jump instructions were written
// ----------------------------------------------------------------------
// a filter for ISR vectors, protects the vectors used by the bootloader
// ----------------------------------------------------------------------
static void write_word(uint16_t data)
{
if (CUR_ADDR <= BOOTLOADER_ADDRESS - 4) {
dirty = 1;
cli();
boot_page_fill(CUR_ADDR, data);
sei();
CUR_ADDR += 2;
}
}
// ----------------------------------------------------------------------
// finishes a write operation if already started
// ----------------------------------------------------------------------
static void finalize_flash_if_dirty()
{
if (dirty != 0 && CUR_ADDR <= BOOTLOADER_ADDRESS)
{
cli();
#ifdef ENABLE_FLASH_WRITING
if (CUR_ADDR >= BOOTLOADER_ADDRESS - SPM_PAGESIZE + 2) {
// this is the last page before the bootloader
// the very last 4 bytes will contain these 2 RJMP instructions
// these two instructions are from the user app, but re-targeted
boot_page_fill(BOOTLOADER_ADDRESS - 2, 0xCFFF & (((fake_page[0] & 0x0FFF) - (BOOTLOADER_ADDRESS / 2) + 1) | 0xC000)); // jump to start
boot_page_fill(BOOTLOADER_ADDRESS - 4, 0xCFFF & (((fake_page[4] & 0x0FFF) - (BOOTLOADER_ADDRESS / 2) + 4) | 0xC000)); // USB interrupt
sei();
last_page_used = 1;
}
boot_page_write(CUR_ADDR - 2);
//boot_spm_busy_wait(); // wait not required since CPU is halted
#if !defined(ENABLE_CHIP_ERASE) && defined(ENABLE_WRITE_WHEN_TOLD)
if (erase_next != 0 && (cur_addr.u16[0] & (SPM_PAGESIZE - 1)) == 0) {
boot_page_erase(CUR_ADDR);
//boot_spm_busy_wait(); // wait not required since CPU is halted
}
#endif
#endif
sei();
}
dirty = 0;
}
// ----------------------------------------------------------------------
// Handle a non-standard SETUP packet.
// ----------------------------------------------------------------------
uchar usbFunctionSetup ( uchar data[8] )
{
uchar req;
usbRequest_t *rq = (void *)data;
idle_cnt = idle_cnt2 = 0;
// Generic requests
req = data[1];
if ( req == USBTINY_READ) {
// do nothing
return 1;
}
else if ( req == USBTINY_POWERDOWN )
{
usb_maybe_finalizing = 1;
#ifndef ENABLE_WRITE_WHEN_TOLD
finalize_flash_if_dirty();
#endif
return 0;
}
else if ( req == USBTINY_SPI )
{
usb_maybe_finalizing = 1;
#ifndef ENABLE_WRITE_WHEN_TOLD
finalize_flash_if_dirty(); // partial page writes are not fully written unless this is called here, it must be HERE
#endif
usbMsgPtr = (usbMsgPtr_t)buffer;
buffer[2] = data[3]; // this tricks "usbtiny_cmd" into succeeding
buffer[3] = 0; // fools safemode into succeeding
// for the commands, refer to ATmega datasheet under "Serial Programming Instruction Set"
// usage of avr/boot.h here is experimental
if (0) { } // fools the C preprocessor
#ifdef ENABLE_SIG_READING
else if (data[2] == 0x30 && data[3] == 0x00) {
// read signature byte
#ifdef ENABLE_SIG_FAKING
buffer[3] = 0x01;
#else
buffer[3] = boot_signature_byte_get(data[4] * 2);
#endif
}
#endif
#ifdef ENABLE_FUSE_READING
else if (data[2] == 0x50 && data[3] == 0x00 && data[4] == 0x00) {
// read LFUSE
buffer[3] = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
}
else if (data[2] == 0x58 && data[3] == 0x08 && data[4] == 0x00) {
// read HFUSE
buffer[3] = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
}
else if (data[2] == 0x50 && data[3] == 0x08 && data[4] == 0x00) {
// read EFUSE
buffer[3] = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS);
}
else if (data[2] == 0x58 && data[3] == 0x00 && data[4] == 0x00) {
// read lock bits
buffer[3] = boot_lock_fuse_bits_get(GET_LOCK_BITS);
}
else if (data[2] == 0x38 && data[3] == 0x00 && data[4] == 0x00) {
// read calibration
buffer[3] = boot_signature_byte_get(0);
}
#endif
#ifdef ENABLE_CHIP_ERASE
else if (data[2] == 0xAC && data[3] == 0x80 && data[4] == 0x00 && data[5] == 0x00)
{
// chip erase, will erase all pages except the first page and the bootloader region
full_erase_requested = 1;
}
#endif
#ifdef ENABLE_WRITE_WHEN_TOLD
else if (data[2] == 0x4C && data[5] == 0x00)
{
// write memory page
// page is data[3] as MSB and data[4] as LSB
need_write_page = 1;
}
#endif
// all other commands are unhandled
return 4;
}
/*
else if ( req == USBTINY_SPI1 )
{
// I don't know what this is used for, there are no single SPI transactions in the ISP protocol
usb_maybe_finalizing = 1;
#ifndef ENABLE_WRITE_WHEN_TOLD
finalize_flash_if_dirty();
#endif
return 1;
}
*/
else if ( req == USBTINY_POLL_BYTES )
{
usb_maybe_finalizing = 1;
#ifndef ENABLE_WRITE_WHEN_TOLD
finalize_flash_if_dirty();
#endif
return 0;
}
CUR_ADDR = *((uint16_t*)(&data[4]));
remaining = rq->wLength.bytes[0];
if ( req >= USBTINY_FLASH_READ && req <= USBTINY_EEPROM_WRITE )
{
#ifdef ENABLE_LED_BLINK
OCR0B = 0x7F;
#endif
usb_has_activity = 1;
cmd0 = req;
if ( cmd0 != USBTINY_FLASH_WRITE ) {
usb_maybe_finalizing = 1;
#ifndef ENABLE_WRITE_WHEN_TOLD
finalize_flash_if_dirty();
#endif
}
return USB_NO_MSG; // usbFunctionRead() or usbFunctionWrite() will be called to handle the data
}
// do nothing if nothing done
return 0;
}
// ----------------------------------------------------------------------
// Handle an IN packet.
// ----------------------------------------------------------------------
uchar usbFunctionRead ( uchar* data, uchar len )
{
uchar i;
if(len > remaining) {
len = remaining;
}
remaining -= len;
for ( i = 0; i < len; )
{
if (cmd0 == USBTINY_EEPROM_READ)
{
#ifdef ENABLE_EEPROM_READING
*data = eeprom_read_byte((void *)cur_addr.u16[0]);
i++;
#endif
}
else if (cmd0 == USBTINY_FLASH_READ)
{
#ifdef ENABLE_FLASH_READING
if (CUR_ADDR < SPM_PAGESIZE)
{
*((uint16_t*)data) = fake_page[CUR_ADDR];
data += 2;
CUR_ADDR += 2;
i += 2;
}
else
{
if (CUR_ADDR >= BOOTLOADER_ADDRESS - 4) { // these last two bytes are reserved for the RJMPs back into userapp
*data = 0xFF; // fools verification
}
else {
*data = pgm_read_byte((void *)CUR_ADDR);
}
data += 1;
CUR_ADDR += 1;
i += 1;
}
#endif
}
}
return len;
}
// ----------------------------------------------------------------------
// Handle an OUT packet.
// ----------------------------------------------------------------------
uchar usbFunctionWrite ( uchar* data, uchar len )
{
uchar i, isLast;
if(len > remaining) {
len = remaining;
}
remaining -= len;
isLast = remaining == 0;
usb_maybe_finalizing = 0;
if (cmd0 == USBTINY_EEPROM_WRITE)
{
#ifdef ENABLE_EEPROM_WRITING
for ( i = 0; i < len; i++ ) {
eeprom_write_byte((void *)(cur_addr.u16[0]++), *data++);
}
#endif
}
else if (cmd0 == USBTINY_FLASH_WRITE)
{
#ifdef ENABLE_FLASH_WRITING
for ( i = 0; i < len; )
{
if (CUR_ADDR < SPM_PAGESIZE)
{
fake_page_used = 1;
fake_page[CUR_ADDR] = *((uint16_t*)data);
CUR_ADDR += 2;
data += 2;
i += 2;
}
else
{
#ifndef ENABLE_CHIP_ERASE
if ((cur_addr.u16[0] & (SPM_PAGESIZE - 1)) == 0 && CUR_ADDR < BOOTLOADER_ADDRESS) {
// page start, erase
boot_page_erase(CUR_ADDR);
//boot_spm_busy_wait(); // wait not required since CPU is halted
}
#endif
write_word(*((uint16_t*)data));
data += 2;
i += 2;
#ifndef ENABLE_WRITE_WHEN_TOLD
if ((cur_addr.u16[0] & (SPM_PAGESIZE - 1)) == 0) {
// end of page
finalize_flash_if_dirty();
}
#endif
}
}
#endif
}
return isLast;
}
void power_up (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); // place in early startup, before main
void power_up (void)
{
// disable watchdog if previously enabled
MCUSR &= ~(1 << WDRF);
wdt_disable();
#ifdef LOW_VOLTAGE
// slow down the clock just in case we are running at 3.3V (or any voltage too low for 16 MHz)
// we speed up as soon as we detect USB activity (which means we have 5V available)
clock_prescale_set(clock_div_2);
#endif
// write magic word (0xAA) to end of stack (RAMEND)
// the ISR will check this and determine whether or not we need to jump to
// either the user app's ISR or the bootloader's ISR
asm volatile("ldi r16, 0xAA"::);
asm volatile("push r16"::);
}
// ----------------------------------------------------------------------
// Bootloader main entry point
// ----------------------------------------------------------------------
__attribute__((naked)) __attribute__((noreturn)) extern int main ( void )
{
if( MCUSR &= (1 << PORF) )//|| (*(uint8_t*)(RAMEND)==0xAA) )
{
//skip bootloader reset source is something other than a the reset-button
//todo: also skip bootloaded when reset is presses while still inside the bootloader (first 10 seconds)
// clear Power-on Reset Flag
MCUSR &= ~(1 << PORF);
// clear magic word from bottom of stack before jumping to the app
*(uint8_t*)(RAMEND) = 0x00;
// this will jump to user app
asm volatile("rjmp (__vectors - 2)");
}
#ifdef ENABLE_RESTORE_OSCCAL
uint8_t old_osccal = OSCCAL; // remember this so we can restore it later
// this must saved before interrupts are enabled, because of when calibrateOscillator is called
#endif
#ifdef ENABLE_LED_BLINK
DDRB |= _BV(LED_PIN);
TCCR0A = _BV(COM0B1) | _BV(WGM01) | _BV(WGM00); // sets PWM for LED fading
TCCR0B = 0x1;
#endif
// start USB and force a re-enumeration by faking a disconnect
usbInit();
usbDeviceDisconnect();
_delay_ms(200);
usbDeviceConnect();
sei();
// main program loop
while (1)
{
usbPoll();
#ifdef ENABLE_CHIP_ERASE
// full chip erase is handled in the main loop so that usbPoll isn't recursively called
if (full_erase_requested != 0)
{
for (ERASE_ADDR = 2; ERASE_ADDR < BOOTLOADER_ADDRESS; ERASE_ADDR += 2)
{
if (ERASE_ADDR % SPM_PAGESIZE == 0) {
boot_page_erase(ERASE_ADDR);
//boot_spm_busy_wait(); // wait not required since CPU is halted
// each erase operation takes 4.5ms maximum, and the CPU is halted during this time, it won't respond to interrupts
}
usbPoll(); // make sure we respond to USB during this time
}
full_erase_requested = 0;
}
#endif
#ifdef ENABLE_WRITE_WHEN_TOLD
if (need_write_page != 0)
{
usbPoll();
#if !defined(ENABLE_CHIP_ERASE) && defined(ENABLE_WRITE_WHEN_TOLD)
erase_next = 1;
#endif
finalize_flash_if_dirty();
need_write_page = 0;
usbPoll();
}
#endif
if (usbHasRxed == 0 || usb_maybe_finalizing != 0 || usb_has_activity == 0)
{
#ifdef ENABLE_LED_BLINK
// blink rate undetermined
if (usbHasRxed != 0 && blink_cnt == 0) {
LED_BLINK();
}
blink_cnt++;
#endif
#if (F_CPU == 16500000)
_delay_us(50);
#elif (F_CPU == 12000000)
_delay_us(25);
#endif
idle_cnt++;
idle_cnt2 = idle_cnt == 0 ? idle_cnt2 + 1 : idle_cnt2;
if (
#if (F_CPU == 16500000)
(usbHasRxed == 0 && idle_cnt > ENTER_TIMEOUT_MS * 20) ||
(usb_maybe_finalizing != 0 && idle_cnt > EXIT_TIMEOUT_MS * 20) ||
#elif (F_CPU == 12000000)
(usbHasRxed == 0 && idle_cnt > ENTER_TIMEOUT_MS * 40) ||
(usb_maybe_finalizing != 0 && idle_cnt > EXIT_TIMEOUT_MS * 40) ||
#endif
(usbHasRxed != 0 && usb_has_activity == 0 && idle_cnt2 > ACTIVITY_TIMEOUT_MS_16) || // enumerated but no action by user
0) {
// timed out
break;
}
}
}
// cleanup!
// deinitialize USB (disable USB interrupts)
USB_INTR_CFG = 0;
USB_INTR_ENABLE = 0;
cli();// disable interrupts
#if !defined(ENABLE_CHIP_ERASE) && defined(ENABLE_WRITE_WHEN_TOLD)
erase_next = 0;
#endif
// the call to finalize will write the two jump vectors
if (fake_page_used != 0 && last_page_used == 0) {
CUR_ADDR = BOOTLOADER_ADDRESS - 4;
#ifndef ENABLE_CHIP_ERASE
boot_page_erase(CUR_ADDR);
//boot_spm_busy_wait(); // wait not required since CPU is halted
#endif
dirty = 1;
finalize_flash_if_dirty();
}
if (fake_page_used != 0)
{
//write the fake page to actual flash
boot_page_erase(0);
//boot_spm_busy_wait(); // wait not required since CPU is halted
for (CUR_ADDR = 0; CUR_ADDR < SPM_PAGESIZE; )
{
uint16_t tmp = fake_page[CUR_ADDR];
if (CUR_ADDR == 0 || CUR_ADDR == 4) { // this is where the old reset and PCINT vectors are
tmp = pgm_read_word(BOOTLOADER_ADDRESS + CUR_ADDR); // get the original instruction inside bootloader
tmp += (BOOTLOADER_ADDRESS / 2); // rjmp to the same place but shifted in the bootloader
}
write_word(tmp);
}
finalize_flash_if_dirty();
}
// clear magic word from bottom of stack before jumping to the app
*(uint8_t*)(RAMEND) = 0x00;
// turn off LED pulsing
DDRB = 0;
TCCR0B = 0;
TCCR0A = 0;
#ifdef ENABLE_RESTORE_OSCCAL
// restore the old OSCCAL, but gradually so the processor doesn't freak out
while (old_osccal > OSCCAL) OSCCAL++;
while (old_osccal < OSCCAL) OSCCAL--;
#endif
#ifdef LOW_VOLTAGE
clock_prescale_set(clock_div_2); // slow down because the user is expecting 8 MHz
#endif
// this will jump to user app
asm volatile("rjmp (__vectors - 2)");
}