Главная » Хабрахабр » Как сжать загрузчик для STM8 до размера 8 байт в памяти FLASH

Как сжать загрузчик для STM8 до размера 8 байт в памяти FLASH

Со времени написания предыдущей статьи ” Как сжать загрузчик для STM8 до размера 18 байт в памяти FLASH” появились две версии загрузчика STM8uLoader . Загрузчик STM8uLoader версии $36 научился передавать управление прикладной программе по любому адресу в памяти RAM без участия хост-программы. Размер 18 байт загрузчика в памяти FLASH не изменился, в области OPTION Bytes размер увеличился до 53 байта (занял все доступное пространство).

Основное требование к этой версии: максимально сжать код. В отдельную ветку выделилась версия $0D загрузчика. Загрузчик состоит из хост программы boot_PC и кода загрузчика boot_uC в памяти STM8. На сегодняшний день размер кода во FLASH памяти 8 байт и в EEPROM памяти 35 байт.
Напомню аритектуру загрузчика STM8uLoader. Последний делится на код начального копировщика boot_FLASH, находящийся в памяти FLASH, и код начального загрузчика boot_EEPROM (или boot_OPTION), находящийся в памяти EEPROM (или области OPTION Bytes).

Начальный загрузчик настраивает UART передает хост программе байт с номером своей версии и, если не получает в течении таймаута от хост программы информации, передает управление прикладной программе во FLASH памяти. После события RESET запускается начальный копировщик boot_FLASH, переносит образ кода начального загрузчика boot_EEPROM (или boot_OPTION) в оперативную память RAM и передает ему управление. Либо переносит код прикладной программы из памяти EEPROM (или области OPTION Bytes) в память RAM и передает ему управление.

Принятый дамп с кодом выполняет свою текущую задачу: чтение / копирование / стирание / запись ячеек STM8, передача управления по заданному адресу с предварительной инициализацией регистров ядра STM8, передача управления начальному загрузчику для смены текущего дампа в памяти RAM хост программой. Если после отправки хост программе байта с номером версии начальный загрузчик принимает по UART дамп с кодом, то размещает его в памяти RAM и передает управление.

$0D находится в адресах $8000…$8007 и полностью занимает векторы RESET и TRAP (Software interrupt). Начальный копировщик boot_FLASH rev. Таблица векторов прерываний также находится на своем месте. Все остальное пространство $8008…$9FFF памяти FLASH полностью доступно прикладной программе.

Рассмотрим код копировщика:

; начальный копировщик boot_FLASH версия $0D($88) ; копирует код начиная с конечного адреса $4087($4187)
; в стек до первого нулевого байта $00
; и передает управление коду segment byte at 8000-8007 'bootF_rev0D'
; $8000 RESET Reset vector dc.b $AE, $40 ; [AE 40 88] ldw X,#$4088
; dc.b $AE, $41 ; [AE 41 88] ldw X,#$4188
cycle:
; при первом вхождении в цикл
; команда push A затеняется командой ldw X,#$4088($4188) push A ; [88] decw X ; [5A] ; $8004 TRAP Software interrupt ld A, (X) ; [F6] jrne cycle ; [26 FB] ret ; [81] ; $8008 TLI IRQ0 External top level interrupt

Здесь пересекается код двух команд: команды ldw X,#$4088 (ldw X,#$4188) и команды push A. При первом вхождении в цикл команда ldw X,#$4088 (ldw X,#$4188) выполняется, а команда push A не выполняется. Такое решение сэкономило один байт, но привело к сужению области хранения таблицы с образом кода начального загрузчика. Младший байт $88 адреса таблицы не позволяет разместить ее в области OPTION Bytes. Для модели STM8S103F3 доступны только адреса $4088 и $4188 в памяти EEPROM для размещения указанной таблицы. Для владельцев STM8S003F3 с EEPROM памятью 128 байт ($4000…$407F) и эти адреса не доступны. Для них все же есть лазейка.

Необходимо только поменять местами команды push A и decw X:

; начальный копировщик boot_FLASH версия $0D($5A) ; копирует код начиная с конечного адреса $405A($415A, $425A)
; в стек до первого нулевого байта $00
; и передает управление коду segment byte at 8000-8007 'bootF_rev0D'
; $8000 RESET Reset vector dc.b $AE, $40 ; [AE 40 5A] ldw X,#$405A для STM8S003F3
; dc.b $AE, $41 ; [AE 41 5A] ldw X,#$415A
; dc.b $AE, $41 ; [AE 42 5A] ldw X,#$425A
cycle:
; при первом вхождении в цикл
; команда decw X затеняется командой ldw X,#$405A($415A, $425A) decw X ; [5A] push A ; [88] ; $8004 TRAP Software interrupt ld A, (X) ; [F6] jrne cycle ; [26 FB] ret ; [81] ; $8008 TLI IRQ0 External top level interrupt

Это решение приведет к усложнению кода начального копировщика. Эта статья про упрощение кода. Описание кода начального копировщика для STM8S003F3 останется за рамками статьи. Отметим только, что при первом вхождении в цикл команда ldw X,#$405A($415A, $425A) выполняется, а команда decw X не выполняется.

Стек растет в сторону уменьшения адресов. Напомним, что событием RESET указатель вершины стека SP аппаратно инициализируется величиной $03FF. Соответствующим образом расположен образ кода начального загрузчика в памяти EEPROM.
Для экономии отказались от счетчика цикла. Для заполнения стека с хвоста таблицу с кодом начального загрузчика также будем читать с хвоста, отсюда команда decw X. При каждой итерации читаем байт из таблицы и если он не равен нулю проталкиваем его в стек. Условились, что образ кода начального загрузчика будет иметь нулевой байт только в начале таблицы, а адрес передачи управления прикладной программе (который может иметь нулевой байт) вынесем за пределы таблицы. Прочитанный нулевой байт – условие выхода из цикла.

Рассмотрим теперь код начального загрузчика: Команда ret здесь означает передать управление по адресу, который содержится в двух байтах, которые последними попали в стек.

; начальный загрузчик boot_EEPROM версия $0D($88) segment byte at 4067-4089 'bootE_rev0D_88'
; segment byte at 417B-4189 'bootE_rev0D_88'
; $4067 terminator не переносится в RAM dc.b $00 ; [00] ; $4068 ($03E0)
; {$0400 - (bootE_go_adr - bootE_start) } dc.w $03E2 ; [03 E2] $4067 - $3C85 = $03E2
; настраивает UART 96001N8
; отправляет BREAK и номер версии $0D
bootE_start: ; $406A ($03E2) {RAM END - 29} ld A, #%00001101 ; [A6 0D] ld UART1_BRR1, A ; [C7 52 32] ld UART1_CR2, A ; [C7 52 35] ld UART1_DR, A ; [C7 52 31] ; принимает первый дамп размером 243 байта
; и помещаеn его в стек ; $4075 ($03ED) {RAM END - 18} bootE_rx_byte: incw X ; [5C] jreq bootE_exit ; [27 0C] btjf UART1_SR, #5, bootE_rx_byte ; [72 0B 52 30 F8] push UART1_DR ; [3B 52 31] ; clrw X ; [5F] inc A ; [4C] jrne bootE_rx_byte ; [26 F2] ; передает управление принятому дампу
; по адресу $02F1 (последние два байта в дампе $FE $02) ret ; [81] ; $4084 ($03FC) {RAM END - 3}
bootE_exit: jp [bootE_go_adr] ; [72 CC 40 88] RAM END
; адрес передачи управления прикладной программе
; $4088 не переносится в RAM
bootE_go_adr: dc.w main_flash ; [80 08]

В скобках показаны адреса кода после копирования в стек. Первым идет нулевой байт, в память RAM не копируется. Следом идет адрес передачи управления начальному загрузчику, фактически это адрес следующей ячейки в памяти RAM. Эта пара в стек попадает, но потом затирается за ненадобность кодом очередного дампа из хост программы. Весь последующий код также копируется в стек, кроме пары с адресом передачи управления прикладной программе
Следом идет инициализация UART. Здесь во все три регистра помещается одно и то же число $0D. В первом случае это скорость UART (16000000/8/9600/16=13). Во втором случае это разрешение передачи/приема, здесь паровозом зацепилась однократная генерация события BREAK. В последнем случае это отправка хост программе версии загрузчика. Фактически хост программа получит два байта $00 (событие BREAK) и $0D (номер версии загрузчика) – это сигнал, что можно отправлять дамп с кодом.

Именно с таким размером хост программа должна отправлять дампы с кодом. Регистр A с содержимым $0D далее выполняет функции счетчика принятых байтов и считает он их с инкрементом до нуля, что эквивалентно 243 принятым байтам.

Теперь он с инкрементом также будет считать до нуля (осталось еще 49048) отсчитывая время, отведенное на работу начальному загрузчику. Индексный регистр X в начальном копировщике досчитал до $4068. Иначе управление будет передано прикладной программе и придется опять нажимать кнопку сброса и перезапускать хост программу. За это время начальный загрузчик должен успеть принять дамп с кодом размером 243 байт поместить его дальше в стек и передать ему управление.

Любой принятый дамп с кодом располагается в стеке в адресах $02EF...$03E1 и принимает управление по адресу $02F1. После передачи управления принятому дампу код начального загрузчика остается в стеке и повторно принимает управление, когда хост программа решит заменить дамп с кодом. Код начального загрузчика располагается в стеке в адресах $03E2...$03FF, первоначально принимает управление по адресу $03E2, а при необходимости заменить дамп с кодом вызывается по адресу $03EA.

Целиком исходный код начального копировщика и начального загрузчика в STM8 за спойлером. Адрес передачи управления прикладной программе остается в памяти EEPROM в паре $4088:$4088.

Файл bootF_rev0D_88.asm:

stm8/ TITLE “bootF_rev0D_88.asm” MOTOROLA WORDS .NOLIST #include "STM8S103F.inc" .LIST segment byte at 407B-4089 'bootE_rev0D_88'
; segment byte at 417B-4189 'bootE_rev0D_88'
; $407B terminator не переносится в RAM dc.b $00 ; [00] ; $407C ($03F4)
; {$0400 - (bootE_go_adr - bootE_start) } dc.w $03F6 ; [03 F6] 407B - 3c85 = 03F6
bootE_start: ; $407E ($03F6) bset PB_DDR,#5 ; [72 1A 50 07] bset PB_CR1,#5 ; [72 1A 50 08] ; $4086 ($03FE) {RAM END - 1} jra * ; [20 FE] RAM END
; $4088 не переносится в RAM
bootE_go_adr: dc.w main_flash ; [80 08] ; копировщик boot_FLASH версия $0D $88 segment byte at 8000-8007 'bootF_rev0D'
; $8000 RESET Reset vector dc.b $AE, $40 ; [AE 40] ldw X,#$4088
; dc.b $AE, $41 ; [AE 41] ldw X,#$4188
cycle: push A ; [88] decw X ; [5A] ; $8004 TRAP Software interrupt ld A, (X) ; [F6] jrne cycle ; [26 FB] ret ; [81] ; $8008 TLI IRQ0 External top level interrupt ; прикладная программа segment byte at 8008-9FFF 'main_flash'
main_flash: jra * ; [20 FE] end
; bootF_rev0D_88.asm

Набросаем простейшую прикладную программу для выполнения во FLASH памяти STM8.

stm8/ TITLE “main_rev0D_4088.asm” MOTOROLA WORDS .NOLIST #include "STM8S103F.inc" .LIST ; прикладная программа segment byte at 8008-9FFF 'main_flash'
main_flash: main_cycle:
; включаем светодиод bset PB_DDR,#5 ; bset PB_CR1,#5 ; callr flash_delay
; выключаем светодиод bres PB_DDR,#5 ; bres PB_CR1,#5 ; callr flash_delay jra main_cycle flash_delay: ldw X, #35000
flash_delay_cycle: decw X jrne flash_delay_cycle ret end
; main_rev0D_4088.asm

Прошьем программатором загрузчик в STM8. Подключаем плату к переходнику USB-TTL(UART). Запускаем пакетный файл runSTM8uLoader.bat. Нажимаем кнопку сброса на плате. Наблюдаем результат:

Начинает моргать светодиод. Код прикладной программы прошивается загрузчиком во FLASH память, устройство перезагружается.

Примеры исходных кодов дампов, которые хост программа посылает загрузчику для выполнения в памяти RAM:

Файл Read_128000v0D88.asm:

stm8/ TITLE "Read_128000v0D88.asm" MOTOROLA WORDS .NOLIST #include "STM8S103F3P.inc" .LIST
; размер дампа должен быть 243 байта
; при этом дамп будет находится в адресах $02EF...$03E1
; (затирает адреса $03E0:$03E1 с адресом перехода к начальному загрузчику)
; и вызываться по адресу $02F1
; вызвавший дамп начальный загрузчик находится в в адресах $03E2...$03FF
; и повторно может быть вызван по адресу $03E2 (переключит скорость на 9600, надо A<=$0D SP<=$03E1 X<=$0000)
; или по адресу $03EA (скорость не переключит, надо A<=$0D SP<=$03E9 X<=$0000)
; адрес перехода к прикладной программе здесь $4088:$4089 segment byte at 0000-00F2 'ram0' dc.w $02F1
start:
; Включаем pull-up на портах (если подтяжка не предусмотрена внешней схемой) или не включаем, все равно работает, экономим 14 байт
; ld A, #%01001100 ; [A6 4C] ; cpl A ; [43] ; ld PA_CR1, A ; [C7 50 03] ; ld PB_CR1, A ; [C7 50 08] ; ld PC_CR1, A ; [C7 50 0D] ; ld PD_CR1, A ; [C7 50 12] подтяжка на PD6(UART1_RX), PD2, PD1
; настраиваем UART1 на прием/передачу на скорости 9600, остальные настройки по умолчанию (8 бит, нет бита четности, 1 стоповый бит) mov UART1_BRR2, #0 ; [35 00 52 33] для Fmaster=16/8=2МГц и 128000 mov UART1_BRR1, #1 ; [35 0D 52 32] для Fmaster=16/8=2МГц и 128000 mov UART1_CR2, #%00001100 ; [35 0C 52 35] UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 разрешаем передачу/прием main_cycle: wait_byte_adrH_cmnd: btjf UART1_SR, #5, wait_byte_adrH_cmnd ld A, UART1_DR cp A, #$EF JRUGT wait_byte_cmd_test ; Relative jump if Unsigned Greater Than ld XH, A ; старший байт адреса в XH ld UART1_DR, A ; эхо старшего байта адреса
tx_echo_adrH: btjf UART1_SR, #7, tx_echo_adrH ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty wait_byte_adrL: btjf UART1_SR, #5, wait_byte_adrL ld A, UART1_DR ld XL, A ; младший байт адреса в XL wait_byte_cntr: btjf UART1_SR, #5, wait_byte_cntr ld A, #$00 ld YH, A ; ld A, UART1_DR ld YL, A ; счетчик ; включаем светодиод bset PB_DDR,#5 ; bset PB_CR1,#5 ; ; read_block_cycle: ld A, (X) ld UART1_DR, A
wait_tx: btjf UART1_SR, #7, wait_tx ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty incw X decw Y jrne read_block_cycle ; выключаем светодиод bres PB_DDR,#5 ; bres PB_CR1,#5 ; jra main_cycle ; если первый байт $F0 и более
wait_byte_cmd_test: cp A, #$F5 ; команда перехода jreq echo_F5cmd
wait_tx_err: mov UART1_DR, #$F1 ; неизвестная команда btjf UART1_SR, #7, wait_tx_err ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty jra main_cycle echo_F5cmd: ld UART1_DR, A ; эхо команды перехода
wait_tx_echo_F5cmd: btjf UART1_SR, #7, wait_tx_echo_F5cmd ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty ; GoAdr
wait_byte_adrH_toM; btjf UART1_SR, #5, wait_byte_adrH_toM mov $03E8, UART1_DR
wait_byte_adrL_toM; btjf UART1_SR, #5, wait_byte_adrL_toM mov $03E9, UART1_DR
; SP
wait_byte_adrH_toSP; btjf UART1_SR, #5, wait_byte_adrH_toSP ld A, UART1_DR ld XH, A
wait_byte_adrL_toSP; btjf UART1_SR, #5, wait_byte_adrL_toSP ld A, UART1_DR ld XL, A ldw SP, X
; Y
wait_byte_adrH_toY; ; btjf UART1_SR, #5, wait_byte_adrH_toY
; ld A, UART1_DR
; ld YH, A
wait_byte_adrL_toY; ; btjf UART1_SR, #5, wait_byte_adrL_toY
; ld A, UART1_DR
; ld YL, A
; X
wait_byte_adrH_toX; btjf UART1_SR, #5, wait_byte_adrH_toX ld A, UART1_DR ld XH, A
wait_byte_adrL_toX; btjf UART1_SR, #5, wait_byte_adrL_toX ld A, UART1_DR ld XL, A
; A
wait_byte_cntr_toA; btjf UART1_SR, #5, wait_byte_cntr_toA ld A, UART1_DR jp [$03E8.w] SKIP 59, $00
; ячейки для адреса перехода dc.b $00 dc.b $00 end
; Read_128000v0D88.asm

Файл WriteBlocks_FLASH_128000v0D88.asm:

stm8/ TITLE "WriteBlocks_FLASH_128000v0D88.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA BYTES segment byte at 0000-00F2 'ram0' dc.w $02F1
start:
; Включаем pull-up на портах (если подтяжка не предусмотрена внешней схемой) или не включаем, все равно работает, экономим 14 байт
; ld A, #%01001100 ; [A6 4C] ; cpl A ; [43] ; ld PA_CR1, A ; [C7 50 03] ; ld PB_CR1, A ; [C7 50 08] ; ld PC_CR1, A ; [C7 50 0D] ; ld PD_CR1, A ; [C7 50 12] подтяжка на PD6(UART1_RX), PD2, PD1
; настраиваем UART1 на прием/передачу на скорости 9600, остальные настройки по умолчанию (8 бит, нет бита четности, 1 стоповый бит) mov UART1_BRR2, #0 ; [35 00 52 33] для Fmaster=16/8=2МГц и 128000 mov UART1_BRR1, #1 ; [35 0D 52 32] для Fmaster=16/8=2МГц и 128000 mov UART1_CR2, #%00001100 ; [35 0C 52 35] UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 разрешаем передачу/прием main_cycle: wait_byte_adrH_cmnd: btjf UART1_SR, #5, wait_byte_adrH_cmnd ld A, UART1_DR cp A, #$F0 JRUGE wait_byte_cmd_test ; Relative jump if Unsigned Greater or Equal ld XH, A ; старший байт адреса в XH ld UART1_DR, A ; эхо старшего байта адреса
tx_echo_adrH: btjf UART1_SR, #7, tx_echo_adrH ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty wait_byte_adrL: btjf UART1_SR, #5, wait_byte_adrL ld A, UART1_DR ld XL, A ; младший байт адреса в XL wait_byte_cntr: btjf UART1_SR, #5, wait_byte_cntr ; количество блоков mov $03FF, UART1_DR write_block_cycle: ldw Y, #64 ; размер блока ; unlock FLASH memory (writing the correct MASS keys) mov FLASH_PUKR, #$56 ; Write $56 then $AE in FLASH_PUKR($5062) mov FLASH_PUKR, #$AE ; If wrong keys have been entered, another key programming sequence can be issued without resetting the device.
; FLASH Block programming mode после записи блока последовательность надо повторить mov FLASH_CR2, #$01 mov FLASH_NCR2, #$FE ; ; FLASH Word programming mode после записи слова последовательность надо повторить
; mov FLASH_CR2, #$40 ; mov FLASH_NCR2, #$BF
; else FLASH byte programming ; включаем светодиод bset PB_DDR,#5 ; bset PB_CR1,#5 ; ; при записи блоками нет необходимости создавать буфер
; так как запись начинается только после копирования целого буфера
wait_rx_byte: btjf UART1_SR, #5, wait_rx_byte ld A, UART1_DR ld (X), A incw X decw Y jrne wait_rx_byte mov UART1_DR, #$FA ; OK
wait_tx_OK_FA: btjf UART1_SR, #7, wait_tx_OK_FA ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty
; выключаем светодиод bres PB_DDR,#5 ; bres PB_CR1,#5 ; dec $03FF jrne write_block_cycle jra main_cycle ; если первый байт $F0 и более
wait_byte_cmd_test: cp A, #$F5 ; команда перехода к boot_OPTION jreq echo_F5cmd
wait_tx_err: mov UART1_DR, #$F1 ; неизвестная команда
wait_tx_err_F1: btjf UART1_SR, #7, wait_tx_err_F1 ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty jra main_cycle echo_F5cmd: ld UART1_DR, A ; эхо команды перехода к boot_OPTION
wait_tx_echo_F5cmd: btjf UART1_SR, #7, wait_tx_echo_F5cmd ; skip if UART1_SR.TXE = 0 TXE: Transmit data register empty ; GoAdr
wait_byte_adrH_toM; btjf UART1_SR, #5, wait_byte_adrH_toM mov $03E8, UART1_DR
wait_byte_adrL_toM; btjf UART1_SR, #5, wait_byte_adrL_toM mov $03E9, UART1_DR
; SP
wait_byte_adrH_toSP; btjf UART1_SR, #5, wait_byte_adrH_toSP ld A, UART1_DR ld XH, A
wait_byte_adrL_toSP; btjf UART1_SR, #5, wait_byte_adrL_toSP ld A, UART1_DR ld XL, A ldw SP, X
; Y
wait_byte_adrH_toY; ; btjf UART1_SR, #5, wait_byte_adrH_toY
; ld A, UART1_DR
; ld YH, A
wait_byte_adrL_toY; ; btjf UART1_SR, #5, wait_byte_adrL_toY
; ld A, UART1_DR
; ld YL, A
; X
wait_byte_adrH_toX; btjf UART1_SR, #5, wait_byte_adrH_toX ld A, UART1_DR ld XH, A
wait_byte_adrL_toX; btjf UART1_SR, #5, wait_byte_adrL_toX ld A, UART1_DR ld XL, A
; A
wait_byte_cntr_toA; btjf UART1_SR, #5, wait_byte_cntr_toA ld A, UART1_DR jp [$03E8.w] SKIP 28, $00
; ячейки для адреса перехода dc.b $00 dc.b $00 end
; WriteBlocks_FLASH_128000v0D88.asm

Файл Reset_128000v0D88.asm:

stm8/ TITLE "Reset_128000v0D88.asm" MOTOROLA WORDS .NOLIST #include "STM8S103F3P.inc" .LIST
; размер дампа должен быть 243 байта
; при этом дамп будет находится в адресах $02EF...$03E1
; (затирает адеса $03E0:$03E1 с адресом перехода к начальному загрузчику)
; и вызываться по адресу $02F1
; вызвавший дамп начальный загрузчик находится в в адресах $03E2...$03FF
; и повторно может быть вызван по адресу $03E2 (переключит скорость на 9600, надо A<=$0D SP<=$03E1 X<=$0000)
; или по адресу $03EA (скорость не переключит, надо A<=$0D SP<=$03E9 X<=$0000)
; адрес перехода к прикладной программе здесь $4088:$4089 segment byte at 0000-00F2 'ram0' dc.w $02F1
start:
; Инициализируем регистры и передаем управление на вектор RESET ldw X, #$03FF ldw SP, X clrw X clrw Y clr A jp $8000 SKIP 230, $00 end
; Reset_128000v0D88.asm

В исходном коде хост программы эти же дампы присутствуют в следующим веде:

public readonly static byte[] Read_128000v0D88 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x03, 0xCC, 0x72, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x94, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xE9, 0x03, 0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xE8, 0x03, 0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0xA5, 0x20, 0xF7, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xF1, 0x35, 0x0B, 0x27, 0xF5, 0xA1, 0xB4, 0x20, 0x08, 0x50, 0x1B, 0x72, 0x07, 0x50, 0x1B, 0x72, 0xF2, 0x26, 0x5A, 0x90, 0x5C, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0xF6, 0x08, 0x50, 0x1A, 0x72, 0x07, 0x50, 0x1A, 0x72, 0x97, 0x90, 0x31, 0x52, 0xC6, 0x95, 0x90, 0x00, 0xA6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0x95, 0x40, 0x22, 0xEF, 0xA1, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x35, 0x52, 0x0C, 0x35, 0x32, 0x52, 0x01, 0x35, 0x33, 0x52, 0x00, 0x35, 0xF1, 0x02 };

public readonly static byte[] WriteBlocks_FLASH_128000v0D88 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x03, 0xCC, 0x72, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x94, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xE9, 0x03, 0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xE8, 0x03, 0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0x86, 0x20, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xF1, 0x35, 0x0B, 0x27, 0xF5, 0xA1, 0x95, 0x20, 0xBF, 0x26, 0xFF, 0x03, 0x5A, 0x72, 0x08, 0x50, 0x1B, 0x72, 0x07, 0x50, 0x1B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xFA, 0x35, 0xF2, 0x26, 0x5A, 0x90, 0x5C, 0xF7, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x08, 0x50, 0x1A, 0x72, 0x07, 0x50, 0x1A, 0x72, 0x5C, 0x50, 0xFE, 0x35, 0x5B, 0x50, 0x01, 0x35, 0x62, 0x50, 0xAE, 0x35, 0x62, 0x50, 0x56, 0x35, 0x40, 0x00, 0xAE, 0x90, 0xFF, 0x03, 0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0x95, 0x5F, 0x24, 0xF0, 0xA1, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x35, 0x52, 0x0C, 0x35, 0x32, 0x52, 0x01, 0x35, 0x33, 0x52, 0x00, 0x35, 0xF1, 0x02 };

public readonly static byte[] Reset_128000v0D88 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCC, 0x4F, 0x5F, 0x90, 0x5F, 0x94, 0xFF, 0x03, 0xAE, 0xF1, 0x02 };

Предыдущая статья: ” Как сжать загрузчик для STM8 до размера 18 байт в памяти FLASH” .
Сайт с проектом http://nflic.ru/STM8/STM8uLoader/000.html .
Проект на https://sourceforge.net/projects/ovsp .
Проект на https://github.com/ovsp/STM8uLoader .

Прошу от читателей целевой критики и предложений по дальнейшему уменьшению кода.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Из песочницы] LVEE — самая неформальная и душевная ИТ-тусовка

О событии Под Минском завершилась ежегодная конференция LVEE 2018 (Linux Vacation/Eastern Europe), организованная белорусской группой пользователей Linux еще в 2005 году. В мероприятии принимали участие более сотни людей из 7 стран, а программу составили 23 доклада, лайтнинги, воркшоп, круглый стол, ...

Губительная ошибка новичков в геймдеве

Перед началом любого дела необходимо составить план, сделать «пробы пера», одним словом — черновик. Именно это помогает определить стартовую точку и понять направление движения. Не хотите тратить тонны усилий впустую? Хотите делать быстрее и качественней остальных? 90% начинающих разработчиков этого ...