-
Notifications
You must be signed in to change notification settings - Fork 9
/
urboot.c
2706 lines (2288 loc) · 94.6 KB
/
urboot.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
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Urboot bootloader for 8-bit AVR microprocessors
*
* published under GNU General Public License, version 3 (GPL-3.0)
* author Stefan Rueger <stefan.rueger@urclocks.com>
*
* v7.7
* 29.12.2022 (first version published in July 2016)
*
* Feature-rich bootloader that is fast and small
* - Tight code: most bootloaders fit into
* + 256 bytes albeit without EEPROM read/write support
* + 384 bytes with EEPROM read/write support
* + 512 bytes with dual-boot, EEPROM read/write support and Chip Erase capability
* - Highly modular feature set:
* + Dedicated protocol between programmer and bootloader (urprotocol)
* + EEPROM read/write support
* + Vector bootloader for devices without a boot section or to save space on devices with one
* + Software UART: no hardware UART required, but also useful when the chip's CPU frequency
* and the bootloader's desired baud rate are incompatible (eg, 8 MHz and 115,200 baud)
* + Subroutine pgm_write_page(sram, progmem) for applications so they can change themselves:
* on many MCUs the SPM write flash only works when called from the bootloader section
* + Dual programming from external SPI flash memory for over-the-air programming
* + Template bootloader with nops that will be replaced "on the fly" with code to manipulate
* the right LED/TX/RX/CS pins at bootloader-burning time (see accompanying urloader sketch)
* + Saves the reset flags in R2 for inspection by the application via the .init0 section
* + Bootloader protects itself from overwriting
* + Automatic host baud rate detection
* + Chip erase in bootloader (faster than -c urclock emulation)
* - Avrdude (from v7.1 onwards) supports urboot bootloaders with -c urclock
*
* Supported and tested
* - ATmega328P (Urclock, UrSense, UrclockMini, Uno, Duemilanove, Anarduino, Moteino, Jeenode etc)
* - ATmega1284P (UrclockMega, MoteinoMega)
* - ATmega2560 (Mega R3)
* - ATmega162
* - ATtiny88 (MH Tiny)
* - ATtiny85 (Digispark)
* - ATtiny167 (Digispark Pro)
* - ATtiny2313
* - ATtiny1634
* - ATtiny841
*
* Unsupported (without ever planning to support them)
* - AVRtiny core (ATtiny4, ATtiny5, ATtiny9, ATtiny10, ATtiny20, ATtiny40, ATtiny102, ATtiny104)
* - Minimal AVR1 core (AT90S1200, ATtiny11, ATtiny12, ATtiny15, ATtiny28)
* - AVR2 core without movw instructions (ATtiny22, ATtiny26, AT90S2313, AT90S2323, AT90S2333,
* AT90S2343, AT90S4414, AT90S4433, AT90S4434, AT90S8515, AT90C8534, AT90S8535)
* - AVR3 core without movw instructions (ATmega103, AT43USB320, AT43USB355, AT76C711)
*
* Currently unsupported: ATxmega devices, newer avr8x devices
*
* Compiles but entirely untested for the following -mmcu=... options
* at90can128 at90can32 at90can64 at90pwm1 at90pwm161 at90pwm2 at90pwm216 at90pwm2b at90pwm3
* at90pwm316 at90pwm3b at90pwm81 at90scr100 at90usb1286 at90usb1287 at90usb162 at90usb646
* at90usb647 at90usb82 ata5272 ata5505 ata5702m322 ata5782 ata5790 ata5790n ata5791 ata5795
* ata5831 ata6285 ata6286 ata6289 ata6612c ata6613c ata6614q ata6616c ata6617c ata664251 ata8210
* ata8510 atmega128 atmega1280 atmega1281 atmega1284 atmega1284rfr2 atmega128a atmega128rfa1
* atmega128rfr2 atmega16 atmega161 atmega163 atmega164a atmega164p atmega164pa atmega165
* atmega165a atmega165p atmega165pa atmega168 atmega168a atmega168p atmega168pa atmega168pb
* atmega169 atmega169a atmega169p atmega169pa atmega16a atmega16hva atmega16hva2 atmega16hvb
* atmega16hvbrevb atmega16m1 atmega16u2 atmega16u4 atmega2561 atmega2564rfr2 atmega256rfr2
* atmega32 atmega323 atmega324a atmega324p atmega324pa atmega324pb atmega325 atmega3250 atmega3250a
* atmega3250p atmega3250pa atmega325a atmega325p atmega325pa atmega328 atmega328pb atmega329
* atmega3290 atmega3290a atmega3290p atmega3290pa atmega329a atmega329p atmega329pa atmega32a
* atmega32c1 atmega32hvb atmega32hvbrevb atmega32m1 atmega32u2 atmega32u4 atmega32u6 atmega406
* atmega48 atmega48a atmega48p atmega48pa atmega48pb atmega64 atmega640 atmega644 atmega644a
* atmega644p atmega644pa atmega644rfr2 atmega645 atmega6450 atmega6450a atmega6450p atmega645a
* atmega645p atmega649 atmega6490 atmega6490a atmega6490p atmega649a atmega649p atmega64a
* atmega64c1 atmega64hve atmega64hve2 atmega64m1 atmega64rfr2 atmega8 atmega8515 atmega8535
* atmega88 atmega88a atmega88p atmega88pa atmega88pb atmega8a atmega8hva atmega8u2 attiny13
* attiny13a attiny2313a attiny24 attiny24a attiny25 attiny261 attiny261a attiny4313 attiny43u
* attiny44 attiny441 attiny44a attiny45 attiny461 attiny461a attiny48 attiny828 attiny84
* attiny84a attiny861 attiny861a
*
* How the bootloader works
*
* In common with bootloaders, it runs after every reset and tries to communicate with an
* external uploader program via a serial interface using a variant of the STK500v1 protocol.
* Hence, only the serial I/O needs to be connected from a Raspberry Pi/PC/laptop host to the
* board with the to-be-programmed MCU on it. After every reset the bootloader code determines
* its cause: if that was an external reset as opposed to power-up reset or watchdog timeout
* (WDT) it waits a small time for protocol initialisation bytes from an uploader program. If
* there was was a valid handshake, the bootloader carries out one of several possible tasks
* driven by the host's uploader such as receiving and burning a new sketch, downloading an
* existing sketch on the MCU, and, if compiled for it, reading or writing the EEPROM on the MCU.
* The bootloader finishes this part typically through a watchdog timeout reset that kicks in
* when initially no valid handshake could be detected, when a serial hardware or protocol error
* occurred during burning/verification or when the bootloader finished its task successfully.
* Watchdog timeout resets the MCU just like an external reset. When the bootloader is entered
* under this condition, though, it then normally jumps directly to the application. However,
* when the bootloader was compiled with dual-boot support, it first checks external SPI flash
* memory to see it contains a new sketch, in which case a copy of it is burned from external
* memory onto internal flash. The idea is that the application sketch could have been written to
* receive a new version via a radio and have placed it onto the external SPI flash memory before
* issuing a WDT reset. Hence, this dual-boot property is also called over the air programming,
* although one could conceivably also plug a new external SPI flash into a board for
* programming.
*
*
* Assumptions, limitations and caveats of *any* bootloader
*
* - Establishing communication invariably causes some small startup delay on external reset.
*
* - For bootloaders on ISP MCUs there is no need for a physical programmer, though in order to
* burn the bootloader itself onto the MCU, an SPI header and an SPI programmer is needed at
* least once in the beginning. Burning the bootloader using SPI necessitates the MCU be in
* reset mode: all chip select lines of attached SPI hardware (external flash memory, radios
* etc) need therefore be pulled high through resistors to ensure all external SPI devices are
* inactive during SPI programming of the bootloader.
*
* - In most cases the connection from the PC for uploading sketches via the bootloader will be
* through USB, which necessitates a USB to serial converter cable if the destination board
* does not have USB. Some PCs (most notably, the Raspberry Pi) have a native serial connection
* that also could be used for connecting to a board without USB.
*
* - The bootloader sets the TX line of the UART or the designated TX pin of the software serial
* communication to output after every reset. If the bootloader is compiled to blink an LED, to
* output a square wave for debugging or to communicate via SPI with external memory for dual
* programming, there will be other lines that are set to output shortly after each reset. Not
* all projects can deal gracefully with these short flares of output activity on some of the
* pins. If you use a bootloader for production settings it is best to carefully consider the
* hardware implications on the circuit.
*
* - As with all bootloaders they work best when the host has a way to reset the board, which is
* typically done via DTR/RTS lines of the host's serial port. Avrdude -c [arduino|urclock]
* does this automatically. The board needs to have hardware to facilitate the DTR/RTS
* connection for reset. Most boards do. If not, sometimes running a small dedicated reset
* program on the host just before running avrdude helps. This reset program somehow needs to
* pull the board's reset line low for a short time; on a Raspberry Pi external GPIO pins can
* be used for that. If that is not possible either, then setting the watchdog timeout to a
* long time (see the WDTO option below) may be helpful, so one can manually reset the board
* before calling avrdude. The default for the timeout is 500 ms. Only with the urclock
* programmer can smaller timeouts down to 128 ms be reliably utilised.
*
*
* Assumptions, limitations, caveats, tips and tricks for *this* bootloader
*
* - The uploading program is assumed to be avrdude with either the arduino or urclock
* programmer: call avrdude -c [arduino|urclock] for this. I have not tested other uploaders.
* The tightest bootloader code (see URPROTOCOL=1 option below) requires avrdude's urclock
* programmer as this forgoes some get/put parameter calls that arduino issues unnecessarily
* and uses its own leaner protocol.
*
* - A bootloader with dual-boot support needs to know which port pin is assigned to the chip
* select of the SPI flash memory, which pin drives a blinking LED (if wanted), where the tx/rx
* lines are for serial communication, how long the watchdog timeout should be etc. This
* explodes the option space for this bootloader. Using the accompanying urloader sketch for
* burning this bootloader onto a board mitigates this somewhat by the use of "template"
* bootloaders (see TEMPLATE option). Urloader lets you interactively set at bootloader burn
* time which pins it should set/clear for LED, chip select, and RX/TX (for software serial
* I/O). Urloader knows some common boards and offers the right bootloaders for them.
*
* - Remember that dual-boot bootloaders communicate with the SPI flash memory at all external
* and WDT resets. Therefore, all other attached SPI devices need their chip select pulled
* high. While in theory urboot.c could be changed so that it uses software pullup for these
* additional devices, this could cost additional code and will complicate the already too big
* option space. It is better practice to use hardware pullups in the board design.
*
* - A dual-boot bootloader deals gracefully with a board that has no external SPI flash memory:
* it simply reads 0xff values and decides there is nothing interesting there. However, this
* causes unnecessary delay at each external reset and each WDT reset, and it toggles the SPI
* and chip select lines (see above); it is therefore not recommended to burn the most-feature
* rich urboot bootloader onto all your boards. Carefully determine what you actually need.
*
* - When using external SPI memory on a board with a dual-boot bootloader, remember to reserve
* the first FLASHEND+1 bytes exclusively for sketches to be burned onto the MCU. These are
* recognised by the second byte being indicative of an rjmp or a jmp opcode. Placing some
* random data in that area risks the MCU being programmed with just these random data.
*
* - Vector bootloaders are great for devices with no dedicated boot section. They assume the
* + Vector table of the MCU, and therefore the reset vector, resides at address zero
* + Compiler puts either a jmp or an rjmp at address zero
* + Compiler does not zap/shorten the vector table if no or few interrupts are used
* + Compiler does not utilise unused interrupt vectors to place code there
*
* This should be the case with all regular sketches produced by avr-gcc. Vector bootloaders
* are also useful for devices with boot section support to allow them to use less space than
* the smallest offered hardware-supported boot section. In this case ensure that the fuses are
* set to make the processor jump to the reset vector as opposed to the bootloader. And ensure
* that the protection bits in the lock byte actually allow code be written into the bootloader
* section with SPM instructions. Otherwise the extra space in the boot section that is freed
* by smaller vector bootloaders cannot be used. The urloader sketch sets fuses and lock bits
* appropriately when burning a vector bootloader onto your board. More on VBLs below.
*
* - The code makes several assumptions that reduce the code size (eg, no interrupts can occur,
* SP points to RAMEND). They are true after a hardware reset, but will not necessarily be true
* if the bootloader is called directly. So don't.
*
*
* Acknowledgements. Code builds on the following work
* - stk500boot.c by Jason P. Kyle
* - Arduino bootloader http://arduino.cc
* - Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml
* - avr-libc project http://nongnu.org/avr-libc
* - Adaboot http://www.ladyada.net/library/arduino/bootloader.html
* - AVR305 Atmel Application Note
*
* Copyright 2016-2022 for complete rewrite by Stefan Rueger (based on a 2016 optiboot version)
* Copyright 2013-2015 previous versions by Bill Westfield
* Copyright 2010 by Peter Knight
*
*/
/*
* Rewritten entirely by Stefan Rueger in 2016
*
*
* There are now 6 main optional features (EEPROM support, vector bootloader, software serial code,
* dual boot from external flash, on-the-fly setting of LED/TX/RX/CS pins at burn-time and a
* pgm_write_page() function that can be called from the application code) plus a lot of fine-
* grained compile-time options to control size and behaviour. The first three already existed, but
* didn't necessarily fit into 512 bytes. Now most features fit into 512 bytes.
*
* Urboot version number in the object code
*
* I put my own spiel on version numbers so that they are encoded in one byte (bit packing) freeing
* one byte for a description of the features of the specific bootloader such as VBL, DUAL, EEPROM,
* ... This is useful, eg, to detect whether a bootloader is a VBL or provides the pgm_write_page()
* function. The urboot-gcc wrapper makes use of that feature, as does the urloader program.
*
*
* Condition to enter the bootloader
*
* Multiple reset flags can be set on startup: watchdog, brown out, power on, external and jtag.
* When should the application be started, when the serial bootloader be run, and when the external
* flash memory be checked for a viable program image? In this implementation the first decision is
* between serial bootloader code and starting the app. If the latter then the external flash
* memory is checked if and only if the watchdog reset flag was set; that check happens before the
* actual app is started. Here the choices for the first decision:
*
* if(ch != _BV(EXTRF))
*
* Only run serial bootloader on 'external reset exclusively' - anything else: run the application
* (with a possible detour over checking dual booting from external SPI memory). This can lead to
* problems when the application happens to trigger the watchdog early on, which makes the "reset
* and nothing else" condition nearly impossible.
*
* if(!(ch & _BV(EXTRF)))
*
* This runs the serial bootloader if and only if there was an external reset (irrespective of a
* watchdog timeout), otherwise run the application with a possible detour over checking dual
* booting from external SPI flash memory. My choice here.
*
* if(ch & (_BV(WDRF) | _BV(BORF) | _BV(PORF)))
*
* This is the condition for starting the app in other bootloaders. It used to be that the serial
* bootloader also would run when nothing caused the reset (all xxxRF == 0), so you could run it by
* jumping direct to START from the application.
*
*
* Edit History:
*
* Nov 2022
* 7.7 smr: added autobaud and, for vector bootloaders, reset vector protection
* Jun 2022
* 7.6 smr: refined the URPROTOCOL protocol implementation, now compiles for 184 MCUs
* Dec 2021
* 7.5 smr: added option URPROTOCOL that significantly reduces code by shifting a lot of work
* to avrdude (patching vector table for vector bootloader, removing avrdude requests for
* unnecessary info, etc). Also created a programmer urclock for avrdude
* Apr 2021
* 7.4 smr: changed condition entering bootloader to "external reset" from "only external reset"
* 7.3 smr: added TEMPLATE feature
*
* Jul 2016
* 7.2 smr: packed feature bits into urboot_version so that urloader displays them; new Atmel port
* port code (atmel_ports.h) so TX/RX/SFMCS all can be set same as LEDs; reformatted code;
* wrote urboot-gcc wrapper in perl and checked that code compiles for 110+ MCUs
*
* 7.1 smr: changed Makefile to work with new options; further reduced code size with careful
* recoding; rewrote eeprom access and moved address to r30; Adding 3-4 features keeps the code
* in under 512 byte for most MCUs
*
* Jun 2016
* 7.0 smr: reviewed Bill Westfield's code; rewrote it reducing its size; introduced fine-grained
* compile time options to better control the size of bootloader; made vector bootloaders work;
* introduced dual boot, auto-increment (removed in v7.5), pgm_write_page()
*/
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/boot.h>
#include "urboot_r30.h" // Own definitions for address being kept globally in Z=r30/31
#include "urboot_mcuid.h" // MCU id (a small int constant sent to avrdude)
#include "pin_defs.h" // Definitions for LEDs and some avr name differences
#include "urboot_wdt.h" // Don't use <avr/wdt.h> owing to unwanted interrupt overhead
#include "errata.h"
#ifndef UARTNUM // Default UART to 0
#define UARTNUM 0
#endif
#ifndef UARTALT // Default UART ALT pin setting to 0 (ie, not active)
#define UARTALT 0
#endif
#include "ur_uart.h" // Unified register and bit names for various UART types
#include "ur_uartnum.h" // Generic UARTn names depending on UARTNUM/UARTALT
#ifndef VERSION
#define VERSION 077 // v7.7 in octal, as minor version is in 3 least significant bits
#endif
#define MAJORVER (VERSION >> 3) // Max 31
#define MINORVER (VERSION & 7) // Max 7
/*
* Different flash memory size categories:
* - 8k or less: no jmp, only rjmp, small device
* - 64k or less: no need for RAMPZ, plain sailing, use lmp/smp
* - 128k or less: needs RAMPZ for elpm/spm, jmp targets; STK500 word addresses fit in 16-bit
* - more than 128k: needs RAMPZ for elpm/spm, EIND for indirect jumps, jmp targets exceed 16-bit
* also needs extention for STK500 v1 protocol
* - more than 4M: low 16 bit of jmp opcode no longer 0x940c (currently only a theoretical issue)
*/
#define FLASHin8k (FLASHEND <= 8191ul)
#define FLASHabove8k (FLASHEND > 8191ul)
#define FLASHin64k (FLASHEND <= 65535ul)
#define FLASHabove64k (FLASHEND > 65535ul)
#define FLASHin128k (FLASHEND <= 0x1fffful)
#define FLASHabove128k (FLASHEND > 0x1fffful)
#define FLASHabove4M (FLASHEND > 0x3ffffful)
// Defaults to 1 iff flash size is power of 2
#ifndef FLASHWRAPS
#define FLASHWRAPS (!(FLASHEND & (FLASHEND+1UL)))
#endif
#ifndef DUAL
#define DUAL 0
#endif
// Vector bootloaders
#define VBL_NONE 0 // Regular bootloader for an MCU with boot section support
#define VBL_JUMP 1 // Bootloader jumps to the application via an interrupt vector
#define VBL_PATCH 2 // When uploading patch reset and designated interrupt vector
#define VBL_VERIFY 3 // Patch during upload and ignore verify attempts of vector table
#ifndef VBL
#define VBL VBL_NONE
#endif
#if defined(SFMCS) || defined(LED)
#define TEMPLATE 0
#endif
#if (defined(BLINK) && BLINK) || (!defined(BLINK) && defined(FRILLS) && FRILLS >= 1)
#if !defined(LED) && !defined(TEMPLATE)
#define TEMPLATE 1
#elif !defined(LED) && defined(TEMPLATE) && ! TEMPLATE && defined(DEFLED)
#define LED DEFLED
#elif !defined(LED) && defined(TEMPLATE) && ! TEMPLATE
#error BLINK needs LED defined or TEMPLATE=1
#endif
#endif
#if !defined(SFMCS) && !defined(TEMPLATE)
#define TEMPLATE 1
#elif DUAL && !defined(SFMCS) && defined(TEMPLATE) && ! TEMPLATE
#error DUAL needs to know the chip select for external memory, eg, SFMCS=ArduinoPin8, or TEMPLATE=1
#endif
// TEMPLATE=1 creates nops for LED and CS, so the right pin can be dropped in at burn time
#ifndef TEMPLATE
#define TEMPLATE 0
#endif
#if DUAL
#if !defined(SPDR) || !defined(SPSR) || !defined(SPIF) || !defined(MSTR) || \
!defined(SPE) || !defined(SPCR)
#error SPI communication not implemented for this MCU; might be as simple as renaming registers...
#endif
#if TEMPLATE
#define SAME_PORT_SFMCS_CS 0
#else
#define SAME_PORT_SFMCS_CS (UR_PORT_VALUE(SFMCS) == UR_PORT_VALUE(AtmelCS))
#endif
#endif // DUAL
#ifndef EEPROM
#define EEPROM 1
#endif
#ifndef PGMWRITEPAGE
#define PGMWRITEPAGE 1
#endif
#define RET_OPCODE 0x9508
#if !defined(RJMPWP) || !PGMWRITEPAGE || (RJMPWP&0xF000) != 0xC000 || RJMPWP == 0xCFFF
#undef RJMPWP
#define RJMPWP RET_OPCODE
#endif
#undef PROTECTME // From v7.7 enforce bootloader self-protection
#define PROTECTME 1
#ifndef PROTECTRESET
#if defined(_urboot_AVAILABLE) && VBL == 1
#if FLASHabove64k && _urboot_AVAILABLE >= 18
#define PROTECTRESET 1
#define _urboot_AVAILABLE_r1 (_urboot_AVAILABLE - 18)
#elif !FLASHabove64k && _urboot_AVAILABLE >= 14
#define PROTECTRESET 1
#define _urboot_AVAILABLE_r1 (_urboot_AVAILABLE - 14)
#else
#define PROTECTRESET 0
#define _urboot_AVAILABLE_r1 _urboot_AVAILABLE
#endif
#else // !(defined(_urboot_AVAILABLE) && VBL == 1)
#define PROTECTRESET 0
#endif
#endif // PROTECTRESET
// 500 ms default watchdog timeout
#ifndef WDTO
#define WDTO 500MS
#endif
#define expandcat(x,y) x##y
#define WATCHDOG_TIMEOUT(x) expandcat(WATCHDOG_,x)
#ifndef URPROTOCOL
#define URPROTOCOL 0
#endif
#ifndef FRILLS
#define FRILLS 0
#endif
#ifndef PAGE_ERASE
#define PAGE_ERASE 0
#endif
#ifndef CHIP_ERASE
#define CHIP_ERASE (FRILLS >= 7)
#endif
#ifndef RETSWVERS
#define RETSWVERS (FRILLS >= 6)
#endif
#ifndef QEXITEND
#define QEXITEND (FRILLS >= 5)
#endif
#ifndef QEXITERR
#define QEXITERR (FRILLS >= 4)
#endif
#ifndef EXITFE
#define EXITFE (FRILLS >= 3? 2: FRILLS >= 2)
#endif
#ifndef BLINK
#define BLINK (FRILLS >= 1)
#endif
#ifndef AUTOBAUD
#define AUTOBAUD 0
#endif
// Some parts can only erase 4 pages at a time
#define FOUR_PAGE_ERASE (defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__) || \
defined(__AVR_ATtiny1634__) || defined(__AVR_ATtiny1634R__))
#if TEMPLATE
// Different NOP codes (mov rN,rN) can be replaced on the fly by urloader
#define NOP_LED_SBIPORT() asm volatile("mov r0, r0\n")
#define NOP_LED_CBIPORT() asm volatile("mov r1, r1\n")
#define NOP_LED_SBIDDR() asm volatile("mov r12, r12\n") // Once had asm("mov r2, %0\n") in code
#define NOP_LED_CLRPORT() asm volatile("mov r3, r3\n")
#define NOP_LED_CLRDDR() asm volatile("mov r4, r4\n")
#define NOP_SFM_SBIPORT() asm volatile("mov r5, r5\n")
#define NOP_SFM_CBIPORT() asm volatile("mov r6, r6\n")
#define NOP_SFM_SBIDDR() asm volatile("mov r7, r7\n")
#define NOP_SFM_CLRPORT() asm volatile("mov r8, r8\n")
#define NOP_SFM_CLRDDR() asm volatile("mov r9, r9\n")
#define sfm_release() NOP_SFM_SBIPORT()
#define sfm_assert() NOP_SFM_CBIPORT()
#define sfm_setupcs() NOP_SFM_SBIDDR()
#define sfm_resetddr() NOP_SFM_CLRDDR()
#define sfm_resetport() NOP_SFM_CLRPORT()
#else // !TEMPLATE
#define sfm_release() (UR_PORT(SFMCS) |= UR_BV(SFMCS))
#define sfm_assert() (UR_PORT(SFMCS) &= ~UR_BV(SFMCS))
#define sfm_setupcs() (UR_DDR(SFMCS) |= UR_BV(SFMCS))
#define sfm_resetddr() (UR_DDR(SFMCS) = 0)
#define sfm_resetport() (UR_PORT(SFMCS) = 0)
#endif
#if BLINK
#if TEMPLATE
#define led_on() NOP_LED_SBIPORT()
#define led_off() NOP_LED_CBIPORT()
#define led_setup() NOP_LED_SBIDDR()
#define led_resetddr() NOP_LED_CLRDDR()
#define led_resetport() NOP_LED_CLRPORT()
#else // !TEMPLATE
#if defined(LEDPOLARITY) && LEDPOLARITY < 0
#define led_on() (UR_PORT(LED) &= ~UR_BV(LED))
#define led_off() (UR_PORT(LED) |= UR_BV(LED))
#else
#define led_on() (UR_PORT(LED) |= UR_BV(LED))
#define led_off() (UR_PORT(LED) &= ~UR_BV(LED))
#endif
#define led_setup() (UR_DDR(LED) |= UR_BV(LED))
#define led_resetddr() (UR_DDR(LED) = 0)
#define led_resetport() (UR_PORT(LED) = 0)
#endif // TEMPLATE
#else // !BLINK
#define led_on()
#define led_off()
#define led_setup()
#define led_resetddr()
#define led_resetport()
#endif
// I/O settings
#if UR_NUMUARTS == 0 && !defined(SWIO)
#define SWIO 1
#warning no uart: defaulting to SWIO=1
#endif
#ifndef SWIO
#define SWIO 0
#endif
#if SWIO
#ifndef RX
#warning SWIO set but not RX? Defaulting to AtmelPB0 (but this is unlikely what you want)
#define RX AtmelPB0
#endif
#ifndef TX
#warning SWIO set but not TX? Defaulting to AtmelPB1 (but this is unlikely what you want)
#define TX AtmelPB1
#endif
#endif
#if AUTOBAUD
#ifndef RX
#warning AUTOBAUD set but not RX? Defaulting to AtmelPB0 (but this is unlikely what you want)
#define RX AtmelPB0
#endif
#endif
#if AUTOBAUD && SWIO
#warning unable to perform AUTOBAUD for SWIO
#undef AUTOBAUD
#define AUTOBAUD 0
#endif
#if !AUTOBAUD
// Set the baud rate defaults
#if !defined(BAUD_RATE)
#if F_CPU >= 7800000L
#define BAUD_RATE 115200L // Generally works (but needs SWIO for ca 8 MHz)
#elif F_CPU >= 1000000L
#define BAUD_RATE 9600L // 19200 may exhibit significant error
#elif F_CPU >= 128000L
#define BAUD_RATE 4800L // Good for 128 kHz internal RC
#else
#define BAUD_RATE 1200L // Good even at 32768 Hz
#endif
#endif // !defined(BAUD_RATE)
#if !SWIO // ... and compute baud rate settings for various UART types
#if UR_UARTTYPE == UR_UARTTYPE_CLASSIC
// Baud setting and actual baud rate in UART 2x mode and normal mode
#define BAUD_SETTING2X (((F_CPU + 4L*BAUD_RATE)/(8L*BAUD_RATE)) < 1? 0: ((F_CPU + 4L*BAUD_RATE)/(8L*BAUD_RATE)) - 1)
#define BAUD_ACTUAL2X (F_CPU/(8L*(BAUD_SETTING2X+1)))
#define BAUD_SETTING1X (((F_CPU + 8L*BAUD_RATE)/(16L*BAUD_RATE)) < 1? 0: ((F_CPU + 8L*BAUD_RATE)/(16L*BAUD_RATE)) -1 )
#define BAUD_ACTUAL1X (F_CPU/(16L*(BAUD_SETTING1X+1)))
// Error per 1000, ie, in 0.1%
#define baud_error(act) ((1000L*(BAUD_RATE>=(act)? BAUD_RATE-(act): (act)-BAUD_RATE))/BAUD_RATE)
// Some classic parts cannot switch to 2x mode
#if !defined(A_U2Xn)
#undef UART2X
#define UART2X 0
#endif
// Using double speed mode UART costs 6 byte initialisation. Is it worth it?
#if !defined(UART2X) || UART2X == 1
#undef UART2X
/*
* Switch on 2x mode if error for normal mode is > 2.5% and error with 2x less than normal mode
* considering that normal mode has higher tolerances than 2x speed mode
*/
#if 20*baud_error(BAUD_ACTUAL2X) < 15*baud_error(BAUD_ACTUAL1X) && baud_error(BAUD_ACTUAL1X) > 25
#define UART2X 1
#else
#define UART2X 0
#endif
#endif
#if UART2X
#define BAUD_SETTING BAUD_SETTING2X
#define BAUD_ACTUAL BAUD_ACTUAL2X
#define BAUD_ERROR baud_error(BAUD_ACTUAL2X)
#else
#define BAUD_SETTING BAUD_SETTING1X
#define BAUD_ACTUAL BAUD_ACTUAL1X
#define BAUD_ERROR baud_error(BAUD_ACTUAL1X)
#endif
#if BAUD_ERROR > 50 && BAUD_ACTUAL < BAUD_RATE
#warning BAUD_RATE quantisation error exceeds -5%
#elif BAUD_ERROR > 50
#warning BAUD_RATE quantisation error greater than 5%
#elif BAUD_ERROR > 25 && BAUD_ACTUAL < BAUD_RATE
#warning BAUD_RATE quantisation error exceeds -2.5%
#elif BAUD_ERROR > 25
#warning BAUD_RATE error quantisation greater than 2.5%
#endif
#if (defined(__AVR_ATmega161__) || defined(__AVR_AT94K__))
#if BAUD_SETTING > 4095
#error Unachievable baud rate (too slow)
#endif
#elif (!defined(UBRRnH) && BAUD_SETTING > 255) || BAUD_SETTING > 4095
#error Unachievable baud rate (too slow)
#elif BAUD_SETTING < 0
#error Unachievable baud rate (too fast)
#endif
#elif UR_UARTTYPE == UR_UARTTYPE_LIN
// 8-bit baud rate register given LBT value (number of samples in 8..63) and target baud rate bd
#define _linbrr(lbt, bd) ({ const int _brrlbt = (lbt); const long _bd = (bd); \
const long _brrret = (F_CPU + (_brrlbt)*(_bd)/2)/((_brrlbt)*(_bd)) - 1L; \
_brrret < 0? 0L: _brrret > 255L? 255L: _brrret; \
})
// Baud rate given LBT value (number of samples in 8..63) and target baud rate bd
#define _linbaud(lbt, bd) ({ const int _baudlbt = (lbt); F_CPU/((_baudlbt)*(_linbrr(_baudlbt, bd)+1L)); })
// Baud rate quantisation error in 0.1 percent given LBT value and target baud rate bd
#define _linabserr(lbt, bd) ({ const long _aebdf = 1000L*(_linbaud(lbt, bd) - (bd)); _aebdf >= 0? _aebdf/(bd): -_aebdf/(bd); })
// Better of two LBT values: if the quantised(!) quantisation error is the same, the larger number of samples wins
#define _better2(l1, l2, bd) ({ const int _l1 = (l1), _l2 = (l2); long _e1 = _linabserr(_l1, bd), _e2 = _linabserr(_l2, bd); \
_e1 < _e2? _l1: _e1 > _e2? _l2: _l1 > _l2? _l1: _l2; })
#define _better4(l1, l2, l3, l4, bd) _better2(_better2(l1, l2, bd), _better2(l3, l4, bd), bd)
#define _better8(l1, l2, l3, l4, l5, l6, l7, l8, bd) \
_better2(_better4(l1, l2, l3, l4, bd), _better4(l5, l6, l7, l8, bd), bd)
// Best LBT value from 8..63
#define _linbestlbt(bd) ({ const int \
_bestl2 = _better8( 8, 9, 10, 11, 12, 13, 14, 15, bd), \
_bestl3 = _better8(16, 17, 18, 19, 20, 21, 22, 23, bd), \
_bestl4 = _better8(24, 25, 26, 27, 28, 29, 30, 31, bd), \
_bestl5 = _better8(32, 33, 34, 35, 36, 37, 38, 39, bd), \
_bestl6 = _better8(40, 41, 42, 43, 44, 45, 46, 47, bd), \
_bestl7 = _better8(48, 49, 50, 51, 52, 53, 54, 55, bd), \
_bestl8 = _better8(56, 57, 58, 59, 60, 61, 62, 63, bd); \
_better8(8, _bestl2, _bestl3, _bestl4, _bestl5, _bestl6, _bestl7, _bestl8, bd); \
})
#define BAUD_LINLBT _linbestlbt(BAUD_RATE)
#define BAUD_SETTING _linbrr(_linbestlbt(BAUD_RATE), BAUD_RATE)
#define UBRRnL LINBRRnL
// Most baud rates can be generated, though there are some edge cases
#if F_CPU > BAUD_RATE*63L*260L
#error LIN_UART BAUD_RATE too small for 8-bit LINBRR
#elif F_CPU < 78L*BAUD_RATE/10L
#error LIN_UART BAUD_RATE too big
#endif
#endif // UR_UARTTYPE
#endif // !SWIO
#endif // !AUTOBAUD
// Extended version word (from v 7.5 onwards)
#define UR_EXT_VBLVECTNUMMASK 0x7f00 // Mask for the vector bootloader vector number
#define UR_EXT_BLPAGESMASK 0x00ff // Mask for the number of bootloader pages
#define UR_EXT_VBLVECTNUM (VBL? VBL_VECT_NUM: 0) // For external program to patch the application
#define UR_EXT_BLPAGES ((FLASHEND+1UL-START+(SPM_PAGESIZE-1))/SPM_PAGESIZE) // # bootloader pages
#define UR_EXT_WORD(vectnum, blpages) ( (uint16_t) ( \
(((vectnum)<<8) & UR_EXT_VBLVECTNUMMASK) | \
((blpages) & UR_EXT_BLPAGESMASK) \
))
#define ext_vblvectnum(ext) ((uint8_t) (((ext) & UR_EXT_VBLVECTNUMMASK)>>8))
#define ext_blpages(ext) ((uint8_t) (((ext) & UR_EXT_BLPAGESMASK)>>0))
/*
* Dedicated interrupt vector for VBL to store the jump to the application
* - set custom VBL_VECT_NUM in Makefile using vector name (or number)
* - defaults to SPM_READY_vect_num or, if that does not exist, to EE_READY_vect_num, if that does
* not exist either, the first unused vector in the table is taken or the table expanded
* - if num is -1 it refers to the additional vbl interrupt vector; uploaded sketches need to have
* created that slot (see comment in VBL section above how to do that for all your sketches)
*/
#define VBL_VECT_additional (_VECTORS_SIZE/(FLASHin8k? 2: 4))
#if defined(VBL_VECT_NUM)
#if VBL_VECT_NUM == -1
#undef VBL_VECT_NUM
#define VBL_VECT_NUM VBL_VECT_additional
#elif VBL_VECT_NUM <= 0 || VBL_VECT_NUM > VBL_VECT_additional
#error VBL_VECT_NUM has been set out of range for this MCU
#endif
#else // !defined(VBL_VECT_NUM)
#if VBL == VBL_NONE
#define VBL_VECT_NUM 0
#elif defined(SPMR_vect_num)
#define VBL_VECT_NUM (SPMR_vect_num)
#elif defined(SPM_RDY_vect_num)
#define VBL_VECT_NUM (SPM_RDY_vect_num)
#elif defined(SPM_Ready_vect_num)
#define VBL_VECT_NUM (SPM_Ready_vect_num)
#elif defined(SPM_READY_vect_num)
#define VBL_VECT_NUM (SPM_READY_vect_num)
#elif defined(SPMREADY_vect_num)
#define VBL_VECT_NUM (SPMREADY_vect_num)
#elif defined(SPM_vect_num)
#define VBL_VECT_NUM (SPM_vect_num)
#elif defined(NVM_SPM_vect_num)
#define VBL_VECT_NUM (NVM_SPM_vect_num)
#elif defined(EEPROM_Ready_vect_num)
#define VBL_VECT_NUM (EEPROM_Ready_vect_num)
#elif defined(EEPROM_READY_vect_num)
#define VBL_VECT_NUM (EEPROM_READY_vect_num)
#elif defined(EE_RDY_vect_num)
#define VBL_VECT_NUM (EE_RDY_vect_num)
#elif defined(EE_READY_vect_num)
#define VBL_VECT_NUM (EE_READY_vect_num)
#elif defined(EEREADY_vect_num)
#define VBL_VECT_NUM (EEREADY_vect_num)
#elif defined(ERDY_vect_num)
#define VBL_VECT_NUM (ERDY_vect_num)
#elif defined(NVM_EE_vect_num)
#define VBL_VECT_NUM (NVM_EE_vect_num)
#else
#define VBL_VECT_NUM UB_VBLVECUU(__AVR_DEVICE_NAME__) // Suggested Vector
#if VBL_VECT_NUM <= 0 || VBL_VECT_NUM > VBL_VECT_additional
#error VBL_VECT_NUM has been assigned out of range for this MCU
#endif
#if VBL_VECT_NUM == VBL_VECT_additional // Warn user as they need to know about this decision
#warning Assigning a vector just past vector table for VBL_VECT_NUM
#endif
#endif
#endif // VBL_VECT_NUM
#if EEPROM
#if defined(EEAR)
#define set_eear(addr) (EEAR = addr)
#elif defined(EEARH) && defined(EEARL)
#define set_eear(addr) ({uint16_t x = addr; EEARH = (x) >> 8; EEARL = (x) & 0xff; })
#elif defined(EEARL)
#define set_eear(addr) (EEARL = addr)
#endif
#endif
// Watchdog settings
#define WATCHDOG_OFF (0)
#if !defined(WDP0) && defined(WDPS0)
/*
* Timings are likely to be radically different, look up data sheet for those devices (ATA57xxM322,
* ATA57xx, ATA5790N, ATA583x, ATA628x, ATA821x, ATA851x).
*/
#define WDP0 WDPS0
#define WDP1 WDPS1
#define WDP2 WDPS2
#endif
#if defined(WDP0)
#define WATCHDOG_16MS (_BV(WDE))
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
#endif
#ifdef WDP3
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
#endif
// STK500 command definitions
#define STK_GET_PARAMETER 0x41
#define STK_SET_DEVICE 0x42
#define STK_SET_DEVICE_EXT 0x45
#define STK_ENTER_PROGMODE 0x50
#define STK_LEAVE_PROGMODE 0x51
#define STK_CHIP_ERASE 0x52
#define STK_LOAD_ADDRESS 0x55
#define STK_UNIVERSAL 0x56
#define STK_UNIVERSAL_EXT 0x4d
#define STK_UNIVERSAL_CE0 0xac
#define STK_UNIVERSAL_CE1 0x80
#define STK_PROG_PAGE 0x64
#define STK_READ_PAGE 0x74
#define STK_READ_SIGN 0x75
#define UR_PROG_PAGE_EE 0x00
#define UR_READ_PAGE_EE 0x01
#define UR_PROG_PAGE_FL 0x02
#define UR_READ_PAGE_FL 0x03
#define UR_PAGE_ERASE 0x04
// STK500 communication protocol
#define STK_INSYNC_C 0x14 // Original STK500 constant
#define STK_OK_C 0x10 // Original STK500 constant
#define STK_INSYNC STK_INSYNC_C // Possibly redefined further below
#define STK_OK STK_OK_C // Possibly redefined further below
#define CRC_EOP 0x20
// STK500 parameter definitions
#define Parm_STK_HW_VER 0x80
#define Parm_STK_SW_MAJOR 0x81
#define Parm_STK_SW_MINOR 0x82
#if URPROTOCOL
// Redefine STK_INSYNC and STK_OK so they inform avrdude about MCU type and 5 bit urboot features
#undef STK_INSYNC
#undef STK_OK
#define UB_N_MCU 2040 // MCU id is in 0..2039
// 5 bit urboot features
#define UB_RESERVED_1 1u
#define UB_RESERVED_2 2u
#define UB_READ_FLASH 4u // Urboot always can read flash
#if !PGMWRITEPAGE && CHIP_ERASE // No page erase during flash STK_PROG_PAGE
#define UB_FLASH_LL_NOR 8u // Flash programming with STK_PROG_PAGE looks like NAND memory
#else
#define UB_FLASH_LL_NOR 0u // Uploader needs to read flash first for sub-page modifications
#endif
#if CHIP_ERASE
#define UB_CHIP_ERASE 16u
#else
#define UB_CHIP_ERASE 0u
#endif
#if UB_MCUID(__AVR_DEVICE_NAME__) >= UB_N_MCU
#error MCU id out of range
#endif
#define UB_FEATURES (UB_READ_FLASH | UB_FLASH_LL_NOR | UB_CHIP_ERASE)
#define UB_INFO (UB_FEATURES*UB_N_MCU + UB_MCUID(__AVR_DEVICE_NAME__))
// Reserve one slot for URPROTOCOL=0 case and 256 more to make STK_OK and STK_INSYNC different
#if UB_INFO >= 255*256-1
#error Afraid, cannot work with this MCU/feature combination
#endif
// Put TMP_INSYNC in [0, 255] and TMP_OK1 in [0, 254]
#define TMP_INSYNC (UB_INFO / 255)
#define TMP_OK1 (UB_INFO % 255)
// TMP_OK is in [0, 255] and different from TMP_INSYNC
#define TMP_OK ((TMP_OK1 < TMP_INSYNC)? TMP_OK1: TMP_OK1+1)
// Indistinguishable from URPROTOCOL==0 case? map to 255/254
#if TMP_INSYNC == STK_INSYNC_C && TMP_OK == STK_OK_C
#define STK_INSYNC 255
#define STK_OK 254
#else
#define STK_INSYNC TMP_INSYNC
#define STK_OK TMP_OK
#endif
#endif // URPROTOCOL
/*
* Global variables and where they are stored
*
* For small code size it's useful to have a handful of global variables. The most important
* variable that deals with addresses for lpm and spm opcodes to read and write flash is the 16-bit
* zaddress. It is stored permanently in the Z register pair r31:r30 throughout. This is
* complemented by RAMPZ on larger devices. Where a Boolean variable is needed the code uses bits
* from GPIOR0, which results in shorter opcodes (in and out rather than lds and sts). We also need
* a global variable, extaddr, to keep a copy of the extended avrdude load address for those larger
* devices, so RAMPZ can be set appropriately. Extaddr is placed into GPIOR1 for similar reasons.
* SRAM is only needed for copies of page buffers read, written or verified.
*
* register uint16_t zaddress asm("r30");
*
* For flash programming the strategy is to always use a 16 bit address and, if necessary, RAMPZ to
* deal with larger addresses. The spm opcode uses RAMPZ/Z (Z=r31:r30), elmp uses RAMPZ/Z, so
* making the 16-bit address global and putting it in Z=r31:r30 makes it "right at home". The only
* trouble is that this register pair is call-clobbered, ie, is likely to be destroyed by library
* function calls. Fortunately, this source does not use library calls. For the full application
* binary interface and register usage of avr-gcc see https://gcc.gnu.org/wiki/avr-gcc
*
* As for EEPROM read/write, I don't think there are AVRs with EEPROM beyond 64k, certainly not the
* target MCUs for urboot, so again 16 bit look OK for EEPROM read/write addresses.
*
* Executive summary: the asm("r30") is a nifty trick; safely ignore the call-clobbered warnings
*/
register uint16_t zaddress asm("r30");
#if FLASHabove128k
#define extaddr GPIOR1 // Only used for ATmega2560/2561 and similar, should be in IO area
#endif
#if VBL >= VBL_VERIFY
/*
* Use GPIOR0/TWBR for Booleans so that compiler can utilise shorter sbi, cbi, sbic, ... opcodes.
* These booleans are only set in parts of the bootloader that exit with a watchdog timer reset, so
* GPIOR0/TWBR will be set to 0 after that WDT reset.
*/
typedef struct {
uint8_t b0:1; uint8_t b1:1; uint8_t b2:1; uint8_t b3:1;
uint8_t b4:1; uint8_t b5:1; uint8_t b6:1; uint8_t b7:1;
} bools_t;
#if !defined(GPIOR0) && defined(TWBR)
#define GPIOR0 TWBR // ATmega8/16/32/64/128
#endif
#if !defined(GPIOR0) && VBL >= VBL_VERIFY // eg, ATmega161
#error Cannot handle VBL level on this device
#endif
#if VBL >= VBL_VERIFY
#define modverify (((volatile bools_t *)_SFR_MEM_ADDR(GPIOR0))->b0)
#endif
#endif // VBL >= VBL_VERIFY
#if !defined(RAMSIZE) && defined(RAMSTART) && defined(RAMEND)
#define RAMSIZE ((RAMEND)-(RAMSTART)+1)
#endif
#if !defined(RAMSTART) || !defined(RAMSIZE)
#error need to know RAMSTART and either RAMSIZE or RAMEND