$LOADER

$LOADER 2081 ENHANCED

Top  Previous  Next

Action

Instruct the compiler to create a boot loader at the specified address.

Can be used for all AVR that support a boot loader like ATMEGA and ATXMEGA chips.

 

Syntax

$LOADER = address

 

Remarks

address

The address where the boot loader is located. You can find this address in the data sheet.

 

In version 2081 a constant named _LOADER_PAGE is created that holds the 64 KB page number.

 

A lot of AVR microcontrollers are configured such that it is possible to use a boot

loader able to receive firmware updates and to reprogram the Flash memory on

demand.

These AVR which support boot loader have a so called boot section.

Normally a chip will start at address 0 when it resets.

This is also called the reset vector.

Chips that have a boot section, split the flash memory in two parts. The boot section is a small part of the normal flash and by setting a fuse bit you select that the chip runs code at the boot sector when it resets instead of the normal reset vector.

 

The Program Flash memory space of ATXMEGA chips is also divided into Application and Boot sections. Both sections

have dedicated Lock Bits for setting restrictions on write or read/write operations.

 

ATXMEGA Program Flash memory parts:

1.Application Section for application code
2.Application Table Section for application code or data storage
3.Boot Section for application code or bootloader code

 

 

noticeYou need to set the fuse bits so the chip jump to the boot loader address at reset (BOOTRST) !

 

Some chips also have fuse bits to select the size of the boot loader (e.g. 1024 words, 2048 words, 4096 words)

 

The boot loader start address depends also on the boot size.

You can find following information in the data sheet of the device (example for ATMEGA644):

 

Boot Size

Boot Loader Flash Section

Boot Reset Address (Start Boot Loader Section)

512 words

0x7E00 - 0x7FFF

$loader = $7E00

1024 words

0x7C00 - 0x7FFF

$loader = $7C00

2048 words

0x7800 - 0x7FFF

$loader = $7800

4096 words

0x7000 - 0x7FFF

$loader = $7000

 

For ATXMEGA chips like ATXMEGA32A4 the boot section is part of the Flash Program Memory.

You can find following information in the data sheet of the ATXMEGA device under Flash Program Memory

(example for ATxmega16A4 .....ATxmega128A4):

 

Chip

Boot Loader Flash Section

Boot Reset Address (Start Boot Loader Section)

ATxmega16A4

0x2000 - 0x7FFF

$loader = &H2000

ATxmega32A4

0x4000 - 0x47FF

$loader = &H4000

ATxmega64A4

0x8000 - 0x87FF

$loader = &H8000

ATxmega128A4

0x10000 - 0x10FFF

$loader = &H10000

 

 

noticeAn external programmer is needed to program the boot loader into the chip. After the fuse bits are set and the boot loader is programmed you do not need the external programmer anymore for this chip (except you want to change the fuse bits).

 

The MCS boot loader sample is a serial boot loader that uses the serial port (USART).

 

With ATXMEGA or with ATMEGA with more then one USART you can choose which USART (COM port) should be used with the boot loader.

 

For example you can use COM7 with an ATXMEGA:

 
Config Com7 = 57600 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8       '
Open "COM7:" For Binary As #7
 

noticeWhen using another UART as COM1 do not forget to add the Interface number (in this example #7) to all the Serial IO functions like Waitkey(#7) or   Print #7 , Chr(bstatus); in the boot loader example
 

The boot loader uses the X-modem checksum protocol to receive the data. (XModem protocol (packet size = 128))

Most terminal emulators can send X-modem checksum.

 

The Boot loader sample can upload both normal flash programs and EEPROM images.

The Boot loader sends a byte with value of 123 to the AVR Boot loader. This boot loader program then enter the boot loader or will jump to the reset vector (0000) to execute the normal flash program.

 

When it receives 124 instead of 123, it will upload the EEPROM.

When you select a BIN file the flash will be uploaded. When you select an EEP file, the EEPROM will be uploaded.

 

The following sample is written so it supports all chips with a boot section.

 

How you need to use this ATMEGA boot loader  example program:

 

1. Uncomment the Chip type and Const Loaderchip you want to use (for example ATMEGA644)

 

$regfile = "m644def.dat"
'$regfile = "m644Pdef.dat"
Const Loaderchip = 644

 

2.Double check the baud rate and COM port you want to use
3.Compile the boot loader example
4.Program it into the chip with an external programmer like AVR ISP MKII
5.Select MCS Bootloader from programmer (select the right COM Port and baud rate)
6.compile a new program or example for this chip
7.reset the chip

 

 

Ways to reset the AVR chip:

 

Hardware reset:

1. Hardware Reset switch/button to GND (manual)
2. MCS Bootloader can set and reset the DTR or RTS line of serial COM port which can be used to reset the AVR (automatic)

 

Software Reset:

1. Reset with Watchdog Timer (e.g. setting the Watchdog to 16ms, start it and let it time out)
2. With GOTO command (e.g. when ATMEGA644 is used the boot loader start at $7c00 ($loader = $7c00).

 Then  you  can use:

 GOTO &H7c00

  to jump to the boot loader start.

3. With ATXMEGA there is a special register to reset the ATXMEGA via software. See also topic ATXMEGA
4. With MCS Bootloader you can send one or several ASCII character to reset the chip like with string "boot_me". In this case the "boot_me" must be detected in your main application on the AVR and then use for example Watchdog or GOTO to reset the chip.

 

The boot loader is written to work at a baud rate of 57600. This baud rate works for most chips that use the internal oscillator. But it is best to check it first with a simple program. When you use a crystal you might even use a higher baud rate.

You can change this by changing the baud rate in the boot loader example (take care to use also the same baud rate in the boot loader application (e.g. MCS Bootloader) on the PC side)

 

Now make a new test program and compile it. Press F4 to start the MCS bootloader. You now need to reset the chip so that it will start the boot loader section. The boot loader will send a byte with value of 123 and the Bascom boot loader receives this and thus starts the loader process.

 

There will be a stand alone boot loader available too. And the sample will be extended to support other AVR chips with boot section too.

 

notice There is a $BOOT directive too. It is advised to use $LOADER as it allows you to write the boot loader in BASIC.

 

importantYou can not use interrupts in your boot loader program as the interrupts will point to the reset vector which is located in the lower section of the flash. When you start to writing pages, you overwrite this part.

 

noticeTake care when Watchdog is enabled by fuse bits and using a boot loader. You need to reset or deactivate the Watchdog in the boot loader example otherwise the firmware upload could be terminated by watchdog reset !

 

noticeIf you want to analyze the MCU Control and Status Register to know which reset source caused the reset you need to save this register already in the boot loader example because this register will be cleared and it will be always 0 when you check it at start of your application.

 

noticeWhen you use a boot loader it will use space from the available flash memory. The compiler does not know if you use a boot loader or not. When your program exceeds the available space and runs into the boot sector space, it will overwrite the boot loader.

The $LOADERSIZE directive will take the boot loader size into account so you will get an error when the target file gets too big.

 

 

Encryption/Decryption with Bootloader:

You can use for example AES or XTEA ( XTEADECODE, XTEAENCODE ) in combination with boot loader examples.

There is an AES with boot loader and AVR-DOS example in the ...BASCOM-AVR\SAMPLES\boot     folder (xmega_dos_boot_AES.zip).

 

 

See also

$BOOT , $LOADERSIZE, MCS Bootloader , CONFIG INTVECTORSELECTION , $BOOTVECTOR

 

 

There is an example for ATMEGA chips and for ATXMEGA Chips:

 

ATMEGA Example:

'----------------------------------------------------------------
'                          (c) 1995-2016, MCS
'                        Bootloader.bas
'  This sample demonstrates how you can write your own bootloader
'  in BASCOM BASIC
'  VERSION 2 of the BOOTLOADER. The waiting for the NAK is stretched
'  further a bug was resolved for the M64/M128 that have a big page size
'-----------------------------------------------------------------
'This sample will be extended to support other chips with bootloader
'The loader is supported from the IDE
$crystal = 8000000
'$crystal = 14745600
$baud = 57600                                               'this loader uses serial com
'It is VERY IMPORTANT that the baud rate matches the one of the boot loader
'do not try to use buffered com as we can not use interrupts
 
'possible return codes of the PC bootloader.exe
' -6005    Cancel requested
' -6006    Fatal time out
' -6007    Unrecoverable event during protocol
' -6008    Too many errors during protocol
' -6009    Block sequence error in Xmodem
' -6016    Session aborted
 
'$regfile = "m8def.dat"
 
'Const Loaderchip = 8
'$regfile = "m168def.dat"
'Const Loaderchip = 168
 
'$regfile = "m16def.dat"
'Const Loaderchip = 16
 
'$regfile = "m32def.dat"
'Const Loaderchip = 32
 
'$regfile = "m88def.dat"
'Const Loaderchip = 88
 
'$regfile = "m162def.dat"
'Const Loaderchip = 162
 
'$regfile = "m8515.dat"
'Const Loaderchip = 8515
 
'$regfile = "m128def.dat"
'Const Loaderchip = 128
 
'$regfile = "m64def.dat"
'Const Loaderchip = 64
 
'$regfile = "m2561def.dat"
'Const Loaderchip = 2561
 
'$regfile = "m2560def.dat"
'Const Loaderchip = 2560
 
'$regfile = "m329def.dat"
'Const Loaderchip = 329
 
'$regfile = "m324pdef.dat"
'Const Loaderchip = 324
 
$regfile = "m644def.dat"
'$regfile = "m644Pdef.dat"
Const Loaderchip = 644
 
#if Loaderchip = 88                                         'Mega88
  $loader = $c00                                         'this address you can find in the datasheet
  'the loader address is the same as the boot vector address
  Const Maxwordbit = 5
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 168                                       'Mega168
  $loader = $1c00                                         'this address you can find in the datasheet
  'the loader address is the same as the boot vector address
  Const Maxwordbit = 6
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 32                                         ' Mega32
  $loader = $3c00                                         ' 1024 words
  Const Maxwordbit = 6                                   'Z6 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
#if Loaderchip = 8                                         ' Mega8
  $loader = $c00                                         ' 1024 words
  Const Maxwordbit = 5                                   'Z5 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
#if Loaderchip = 161                                       ' Mega161
  $loader = $1e00                                         ' 1024 words
  Const Maxwordbit = 6                                   'Z6 is maximum bit                                   '
#endif
#if Loaderchip = 162                                       ' Mega162
  $loader = $1c00                                         ' 1024 words
  Const Maxwordbit = 6                                   'Z6 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 8515                                       ' Mega8515
  $loader = $c00                                         ' 1024 words
  Const Maxwordbit = 5                                   'Z6 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
  Osccal = &HB3                                           ' the internal osc needed a new value
#endif
 
#if Loaderchip = 64                                         ' Mega64
  $loader = $7c00                                         ' 1024 words
  Const Maxwordbit = 7                                   'Z7 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 128                                       ' Mega128
  $loader = &HFC00                                       ' 1024 words
  Const Maxwordbit = 7                                   'Z7 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 2561                                       ' Mega2561
  $loader = &H1FC00                                       ' 1024 words
  Const Maxwordbit = 7                                   'Z7 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 2560                                       ' Mega2560
  $loader = &H1FC00                                       ' 1024 words
  Const Maxwordbit = 7                                   'Z7 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 16                                         ' Mega16
  $loader = $1c00                                         ' 1024 words
  Const Maxwordbit = 6                                   'Z6 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 329                                       ' Mega32
  $loader = $3c00                                         ' 1024 words
  Const Maxwordbit = 6                                   'Z6 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
#if Loaderchip = 324                                       ' Mega32
  $loader = $3c00                                         ' 1024 words
  Const Maxwordbit = 6                                   'Z6 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
 
#if Loaderchip = 644                                       ' Mega644P
  $loader = $7c00                                         ' 1024 words
  Const Maxwordbit = 7                                   'Z7 is maximum bit                                   '
  Config Com1 = Dummy , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
#endif
 
Const Maxword =(2 ^ Maxwordbit) * 2                         '128
Const Maxwordshift = Maxwordbit + 1
Const Cdebug = 0                                           ' leave this to 0
 
#if Cdebug
  Print Maxword
  Print Maxwordshift
#endif
 
'Dim the used variables
Dim Bstatus As Byte , Bretries As Byte , Bblock As Byte , Bblocklocal As Byte
Dim Bcsum1 As Byte , Bcsum2 As Byte , Buf(128) As Byte , Csum As Byte
Dim J As Byte , Spmcrval As Byte                           ' self program command byte value
Dim Z As Long                                               'this is the Z pointer word
Dim Vl As Byte , Vh As Byte                                 ' these bytes are used for the data values
Dim Wrd As Word , Page As Word                             'these vars contain the page and word address
Dim Bkind As Byte , Bstarted As Byte
'Mega 88 : 32 words, 128 pages
 
 
 
Disable Interrupts                                         'we do not use ints
'Waitms 100                                                  'wait 100 msec sec
'We start with receiving a file. The PC must send this binary file
 
'some constants used in serial com
Const Nak = &H15
Const Ack = &H06
Const Can = &H18
 
'we use some leds as indication in this sample , you might want to remove it
Config Pinb.2 = Output
Portb.2 = 1                                                 'the stk200 has inverted logic for the leds
Config Pinb.3 = Output
Portb.3 = 1
 
$timeout = 400000                                           'we use a timeout
'When you get LOADER errors during the upload, increase the timeout value
'for example at 16 Mhz, use 200000
 
Bretries = 5                                               'we try 5 times
Testfor123:
#if Cdebug
  Print "Try " ; Bretries
  Print "Wait"
#endif
Bstatus = Waitkey()                                         'wait for the loader to send a byte
#if Cdebug
  Print "Got "
#endif
 
Print Chr(bstatus);
 
If Bstatus = 123 Then                                       'did we received value 123 ?
  Bkind = 0                                               'normal flash loader
  Goto Loader
Elseif Bstatus = 124 Then                                   ' EEPROM
  Bkind = 1                                               ' EEPROM loader
  Goto Loader
Elseif Bstatus <> 0 Then
  Decr Bretries
  If Bretries <> 0 Then Goto Testfor123                   'we test again
End If
 
For J = 1 To 10                                             'this is a simple indication that we start the normal reset vector
  Toggle Portb.2 : Waitms 100
Next
 
#if Cdebug
Print "RESET"
#endif
Goto _reset                                                 'goto the normal reset vector at address 0
 
 
'this is the loader routine. It is a Xmodem-checksum reception routine
Loader:
#if Cdebug
    Print "Clear buffer"
#endif
Do
    Bstatus = Waitkey()
Loop Until Bstatus = 0
 
For J = 1 To 3                                           'this is a simple indication that we start the normal reset vector
    Toggle Portb.2 : Waitms 50
Next
 
If Bkind = 0 Then
    Spmcrval = 3 : Gosub Do_spm                           ' erase  the first page
    Spmcrval = 17 : Gosub Do_spm                           ' re-enable page
End If
 
Bretries = 10                                               'number of retries
 
Do
 Bstarted = 0                                             ' we were not started yet
 Csum = 0                                                 'checksum is 0 when we start
Print Chr(nak);                                           ' firt time send a nack
Do
   Bstatus = Waitkey()                                     'wait for statuse byte
 
  Select Case Bstatus
      Case 1:                                             ' start of heading, PC is ready to send
          Incr Bblocklocal                               'increase local block count
           Csum = 1                                       'checksum is 1
           Bblock = Waitkey() : Csum = Csum + Bblock       'get block
           Bcsum1 = Waitkey() : Csum = Csum + Bcsum1       'get checksum first byte
          For J = 1 To 128                               'get 128 bytes
             Buf(j) = Waitkey() : Csum = Csum + Buf(j)
          Next
           Bcsum2 = Waitkey()                             'get second checksum byte
          If Bblocklocal = Bblock Then                   'are the blocks the same?
              If Bcsum2 = Csum Then                       'is the checksum the same?
                Gosub Writepage                           'yes go write the page
                Print Chr(ack);                           'acknowledge
              Else                                         'no match so send nak
                Print Chr(nak);
              End If
          Else
              Print Chr(nak);                             'blocks do not match
          End If
      Case 4:                                             ' end of transmission , file is transmitted
            If Wrd > 0 And Bkind = 0 Then                 'if there was something left in the page
                Wrd = 0                                   'Z pointer needs wrd to be 0
                Spmcrval = 5 : Gosub Do_spm               'write page
                Spmcrval = 17 : Gosub Do_spm               ' re-enable page
            End If
          '  Waitms 100                                   ' OPTIONAL REMARK THIS IF THE DTR SIGNAL ARRIVES TO EARLY
            Print Chr(ack);                               ' send ack and ready
 
            Portb.3 = 0                                   ' simple indication that we are finished and ok
            Waitms 20
            Goto _reset                                   ' start new program
      Case &H18:                                           ' PC aborts transmission
            Goto _reset                                   ' ready
      Case 123 : Exit Do                                   'was probably still in the buffer
      Case 124 : Exit Do
      Case Else
        Exit Do                                           ' no valid data
  End Select
Loop
If Bretries > 0 Then                                     'attempte left?
    Waitms 1000
    Decr Bretries                                         'decrease attempts
Else
    Goto _reset                                           'reset chip
End If
Loop
 
'write one or more pages
Writepage:
If Bkind = 0 Then
  For J = 1 To 128 Step 2                                 'we write 2 bytes into a page
     Vl = Buf(j) : Vh = Buf(j + 1)                         'get Low and High bytes
!     lds r0, {vl}                                         'store them into r0 and r1 registers
!     lds r1, {vh}
     Spmcrval = 1 : Gosub Do_spm                           'write value into page at word address
     Wrd = Wrd + 2                                         ' word address increases with 2 because LS bit of Z is not used
    If Wrd = Maxword Then                                 ' page is full
         Wrd = 0                                           'Z pointer needs wrd to be 0
         Spmcrval = 5 : Gosub Do_spm                       'write page
         Spmcrval = 17 : Gosub Do_spm                     ' re-enable page
 
         Page = Page + 1                                   'next page
         Spmcrval = 3 : Gosub Do_spm                       ' erase  next page
         Spmcrval = 17 : Gosub Do_spm                     ' re-enable page
    End If
  Next
 
Else                                                       'eeprom
    For J = 1 To 128
      Writeeeprom Buf(j) , Wrd
      Wrd = Wrd + 1
    Next
End If
Toggle Portb.2 : Waitms 10 : Toggle Portb.2               'indication that we write
Return
 
 
Do_spm:
Bitwait Spmcsr.0 , Reset                                 ' check for previous SPM complete
Bitwait Eecr.1 , Reset                                   'wait for eeprom
 
 Z = Page                                                 'make equal to page
Shift Z , Left , Maxwordshift                             'shift to proper place
 Z = Z + Wrd                                               'add word
! lds r30,{Z}
! lds r31,{Z+1}
 
#if _romsize > 65536
!     lds r24,{Z+2}
!     sts rampz,r24                                         ' we need to set rampz also for the M128
#endif
 
Spmcsr = Spmcrval                                         'assign register
! spm                                                       'this is an asm instruction
! nop
! nop
Return
 
 
'How you need to use this program:
'1- compile this program
'2- program into chip with sample elctronics programmer
'3- select MCS Bootloader from programmers
'4- compile a new program for example M88.bas
'5- press F4 and reset your micro
' the program will now be uploaded into the chip with Xmodem Checksum
' you can write your own loader.too
'A stand alone command line loader is also available
 
'How to call the bootloader from your program without a reset ???
'Do
'   Print "test"
'   Waitms 1000
'   If Inkey() = 27 Then
'      Print "boot"
'      Goto &H1C00
'   End If
'Loop
 
'The GOTO will do the work, you need to specify the correct bootloader address
'this is the same as the $LOADER statement.

 

 

 

ATXMEGA Example:

 

 

'----------------------------------------------------------------
'                          (c) 1995-2016, MCS
'                        BootloaderXmega32A4.bas
'  This sample demonstrates how you can write your own bootloader
'  in BASCOM BASIC for the XMEGA
'-----------------------------------------------------------------
'The loader is supported from the IDE
$crystal = 32000000                                         ' xmega128 is running on 32 MHz
$regfile = "xm32a4def.dat"
$lib "xmega.lib"                                           ' add a reference to this lib
 
'first enabled the osc of your choice
Config Osc = Disabled , 32mhzosc = Enabled                 'internal 2 MHz and 32 MHz enabled
'configure the systemclock
Config Sysclock = 32mhz , Prescalea = 1 , Prescalebc = 1_1 ' we will use 32 MHz and divide by 1 to end up with 32 MHz
 
$loader = &H4000                                           ' bootloader starts after the application
 
Config Com1 = 57600 , Mode = Asynchroneous , Parity = None , Stopbits = 1 , Databits = 8       ' use USART C0
'COM0-USARTC0, COM1-USARTC2, COM2-USARTD0. etc.
Config Portc.3 = Output                                     'define TX as output
Config Pinc.2 = Input
 
Const Maxwordbit = 7                                       ' Z7 is maximum bit                                   '
Const Maxword =(2 ^ Maxwordbit) * 2                         '128
Const Maxwordshift = Maxwordbit + 1
Const Cdebug = 0                                           ' leave this to 0
 
'Dim the used variables
Dim Bstatus As Byte , Bretries As Byte , Bmincount As Byte , Bblock As Byte , Bblocklocal As Byte
Dim Bcsum1 As Byte , Bcsum2 As Byte , Buf(128) As Byte , Csum As Byte
Dim J As Byte , Spmcrval As Byte                           ' self program command byte value
Dim Z As Long                                               'this is the Z pointer word
Dim Vl As Byte , Vh As Byte                                 ' these bytes are used for the data values
Dim Wrd As Word , Page As Word                             'these vars contain the page and word address
 
Disable Interrupts                                         'we do not use ints
 
'We start with receiving a file. The PC must send this binary file
 
'some constants used in serial com
Const Nak = &H15
Const Ack = &H06
Const Can = &H18
 
$timeout = 300000                                           'we use a timeout
'When you get LOADER errors during the upload, increase the timeout value
'for example at 16 Mhz, use 200000
 
Bretries = 5 : Bmincount = 3                               'we try 10 times and want to get 123 at least 3 times
Do
 Bstatus = Waitkey()                                       'wait for the loader to send a byte
 
If Bstatus = 123 Then                                     'did we received value 123 ?
    If Bmincount > 0 Then
      Decr Bmincount
    Else
      Print Chr(bstatus);
      Goto Loader                                         ' yes so run bootloader
    End If
Else                                                     'we received some other data
    If Bretries > 0 Then                                   'retries left?
       Bmincount = 3
      Decr Bretries
    Else
      Rampz = 0
      Goto Proces_reset                                   'goto the normal reset vector at address 0
    End If
End If
Loop
 
'this is the loader routine. It is a Xmodem-checksum reception routine
Loader:
Do
    Bstatus = Waitkey()
Loop Until Bstatus = 0
 
 Spmcrval = &H20 : Gosub Do_spm                           ' erase  all app pages
 
 
Bretries = 10                                               'number of retries
 
Do
 Csum = 0                                                 'checksum is 0 when we start
Print Chr(nak);                                           ' firt time send a nack
Do
 
   Bstatus = Waitkey()                                     'wait for statuse byte
 
  Select Case Bstatus
      Case 1:                                             ' start of heading, PC is ready to send
          Incr Bblocklocal                               'increase local block count
           Csum = 1                                       'checksum is 1
           Bblock = Waitkey() : Csum = Csum + Bblock       'get block
           Bcsum1 = Waitkey() : Csum = Csum + Bcsum1       'get checksum first byte
          For J = 1 To 128                               'get 128 bytes
             Buf(j) = Waitkey() : Csum = Csum + Buf(j)
          Next
           Bcsum2 = Waitkey()                             'get second checksum byte
 
          If Bblocklocal = Bblock Then                   'are the blocks the same?
 
              If Bcsum2 = Csum Then                       'is the checksum the same?
                Gosub Writepage                           'yes go write the page
                Print Chr(ack);                           'acknowledge
              Else                                         'no match so send nak
                Print Chr(nak);
              End If
          Else
              Print Chr(nak);                             'blocks do not match
          End If
      Case 4:                                             ' end of transmission , file is transmitted
            If Wrd > 0 Then                               'if there was something left in the page
                Wrd = 0                                   'Z pointer needs wrd to be 0
                Spmcrval = &H24 : Gosub Do_spm             'write page
            End If
            Print Chr(ack);                               ' send ack and ready
            Waitms 20
            Goto Proces_reset
      Case &H18:                                           ' PC aborts transmission
            Goto Proces_reset                             ' ready
      Case 123 : Exit Do                                   'was probably still in the buffer
      Case 124 : Exit Do
      Case Else
        Exit Do                                           ' no valid data
  End Select
Loop
If Bretries > 0 Then                                     'attempte left?
    Waitms 1000
    Decr Bretries                                         'decrease attempts
Else
    Goto Proces_reset                                     'reset chip
End If
Loop
 
'write one or more pages
Writepage:
  For J = 1 To 128 Step 2                                 'we write 2 bytes into a page
     Vl = Buf(j) : Vh = Buf(j + 1)                         'get Low and High bytes
!     lds r0, {vl}                                         'store them into r0 and r1 registers
!     lds r1, {vh}
     Spmcrval = &H23 : Gosub Do_spm                       'write value into page at word address
     Wrd = Wrd + 2                                         ' word address increases with 2 because LS bit of Z is not used
    If Wrd = Maxword Then                                 ' page is full
         Wrd = 0                                           'Z pointer needs wrd to be 0
         Spmcrval = &H24 : Gosub Do_spm                   'write page
         Page = Page + 1                                   'next page
    End If
  Next
Return
 
Do_spm:
 Z = Page                                                 'make equal to page
Shift Z , Left , Maxwordshift                             'shift to proper place
 Z = Z + Wrd                                               'add word
! lds r30,{Z}
! lds r31,{Z+1}
 
#if _romsize > 65536
!     lds r24,{Z+2}
!     sts rampz,r24                                         ' we need to set rampz also for the M128
#endif
 
Nvm_cmd = Spmcrval
Cpu_ccp = &H9D
! spm                                                       'this is an asm instruction
Do_spm_busy:
!   lds r23, NVM_STATUS
!   sbrc r23,7 ;if  busy bit is cleared  skip next instruc tion
!   rjmp do_spm_busy
Return
 
Proces_reset:
Rampz = 0
Goto _reset                                               'start at address 0