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

от автора

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

В отдельную ветку выделилась версия $0D загрузчика. Основное требование к этой версии: максимально сжать код. На сегодняшний день размер кода во FLASH памяти 8 байт в EEPROM памяти 35 байт.

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

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

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

Начальный копировщик boot_FLASH rev. $0D находится в адресах $8000…$8007 и полностью занимает векторы RESET и TRAP (Software interrupt). Все остальное пространство $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 

Здесь при первом вхождении в цикл команда ldw X,#$405A($415A, $425A) выполняется, а команда decw X не выполняется.

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

Команда 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) {RAM END - 31} ; {$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 принятым байтам. Именно с таким размером хост программа должна отправлять дампы с кодом.

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

После передачи управления принятому дампу код начального загрузчика остается в стеке и повторно принимает управление, когда хост программа решит заменить дамп с кодом. Любой принятый дамп с кодом располагается в стеке в адресах $02ED…$03DF и принимает управление по адресу $02F0. Код начального загрузчика располагается в стеке в адресах $03E0…$03FF, первоначально принимает управление по адресу $03E0, а при необходимости заменить дамп с кодом вызывается по адресу $03E8.
Команда передачи управления прикладной программе находится в стеке по адресу $03FB.
Ячейка с адресом $03FF может использоваться текущим дампом.

Целиком исходный код начального копировщика и начального загрузчика в STM8 версии $0D с таблицей в EEPROM памяти в адресах $4039…$405C ($4139…$415C, $4239…$425C).

Файл bootF_rev0D_5A.asm:

stm8/  TITLE “bootF_rev0D_5A.asm”     MOTOROLA     WORDS     .NOLIST ;    #include "STM8S003F.inc"     #include "STM8S103F.inc"     .LIST  ; начальный загрузчик boot_EEPROM версия $0D($5A)     segment byte at 4039-405C 'bootE_rev0D_405A' ; STM8S003F ;    segment byte at 4139-415C 'bootE_rev0D_415A' ;    segment byte at 4239-425C 'bootE_rev0D_425A' ; $403A9 ($03DD) {RAM END - 34} terminator переносится в RAM   ????? 	dc.b   $00        ; [00] ; $403A ($03DE) {RAM END - 33} ; {$0400 - (bootE_go_adr - bootE_start - 1) }     dc.w   $03E0      ; [03 E0] ; настраивает UART 96001N8 ; отправляет BREAK и номер версии $0D bootE_start:           ; $403C ($03E0) {RAM END - 31}        	ld    A, #%00001101 ; [A6 0D] 	ld    UART1_BRR1, A ; [C7 52 32] 	ld    UART1_CR2, A  ; [C7 52 35] ; $4044 ($03E8) {RAM END - 23}  	ld    UART1_DR, A   ; [C7 52 31] ; принимает дамп размером 243 байта ; и помещаеn его в стек	 ; $40487 ($03EB) {RAM END - 20}    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] ; передает управление принятому дампу ; по адресу $02EF (последние два байта в дампе $EF $02) ; $4056 ($03FA) {RAM END - 5}     ret              ; [81] ; $4057 ($03FB) {RAM END - 4} bootE_exit: 	jp   [bootE_go_adr] ; [72 CC 40 5B] {RAM END - 1} ; ($03FF) RAM END  может использоваться текущим дампом ; адрес передачи управления прикладной программе ; $405B не переносится в RAM bootE_go_adr:     dc.w   main_flash ; [80 08]  ; начальный копировщик 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 ; STM8S003F ;    dc.b   $AE, $41  ; [AE 41 5A] ldw X,#$415A ;    dc.b   $AE, $42  ; [AE 42 5A] ldw X,#$425A cycle: ; при первом вхождении в цикл ; команда dec 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  ; прикладная программа      segment byte at 8008 'main_flash' main_flash:     jra    *          ; [20 FE]  ;    end ; bootF_rev0D_5A.asm 

Целиком исходный код начального копировщика и начального загрузчика в STM8 версии $0D с таблицей в EEPROM памяти в адресах $4065…$4088 ($4165…$4188).

Файл bootF_rev0D_88.asm:

stm8/ TITLE “bootF_rev0D_88.asm”       MOTOROLA       WORDS       .NOLIST ;    #include "STM8S003F.inc"       #include "STM8S103F.inc"       .LIST  ; начальный загрузчик boot_EEPROM версия $0D($88)     segment byte at 4065-4088 'bootE_rev0D_88' ;    segment byte at 4165-4188 'bootE_rev0D_88' ; $4065 terminator не переносится в RAM 	dc.b   $00        ; [00] ; $4066 ($03DE) {RAM END - 33} ; {$0400 - (bootE_go_adr - bootE_start) }     dc.w   $03E0      ; [03 E0] ; настраивает UART 9600 1N8 ; отправляет BREAK и номер версии $0D bootE_start:           ; $4068 ($03E0) {RAM END - 31}        	ld    A, #%00001101 ; [A6 0D] 	ld    UART1_BRR1, A ; [C7 52 32] 	ld    UART1_CR2, A  ; [C7 52 35] ; $4070 ($03E8) {RAM END - 23}  	ld    UART1_DR, A   ; [C7 52 31] ; принимает дамп размером 243 байта ; и помещаеn его в стек	 ; $4073 ($03EB) {RAM END - 20}    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] ; передает управление принятому дампу ; по адресу $02EF (последние два байта в дампе $EF $02) ; $4082 ($03FA) {RAM END - 5}     ret              ; [81] ; $4083 ($03FB) {RAM END - 4} bootE_exit: 	jp   [bootE_go_adr] ; [72 CC 40 87] {RAM END - 1} ; ($03FF) RAM END  может использоваться текущим дампом ; адрес передачи управления прикладной программе ; $4087 ($4088 не) переносится в RAM bootE_go_adr:     dc.w   main_flash ; [80 08]  	 ; начальный копировщик boot_FLASH версия $0D($88)  ; копирует код начиная с конечного адреса $4087($4187) ; в стек до первого нулевого байта $00 ; и передает управление коду     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 затеняется командой 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  ; прикладная программа      segment byte at 8008 'main_flash' main_flash:     jra    *          ; [20 FE]      end ; bootF_rev0D_88.asm 

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

Файл main_rev0D.asm:

stm8/  TITLE “main_rev0D.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.asm 

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

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

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

Файл Read_128000v0D.asm:

stm8/ TITLE "Read_128000v0D.asm"       MOTOROLA       WORDS       .NOLIST       #include "STM8S103F3P.inc"       .LIST ; размер дампа должен быть 243 байта ; при этом дамп будет находится в адресах $02EE...$03DF ; (затирает адеса $03DF:$03E0 с адресом перехода к начальному загрузчику) ; и вызываться по адресу $02F0 ; вызвавший дамп начальный загрузчик находится в адресах $03E1...$03FE ; и повторно может быть вызван по адресу $03E1 (переключит скорость на 9600, надо A<=$0D SP<=$03E0 X<=$0000) ; или по адресу $03E9 (скорость не переключит, надо A<=$0D SP<=$03E8 X<=$0000) ; команда перехода к прикладной программе здесь $03FB       segment byte at 0000-00F2 'ram0'       dc.w   $02F0 start: ; настраиваем 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    $03E7, UART1_DR wait_byte_adrL_toM;      btjf   UART1_SR, #5, wait_byte_adrL_toM     mov    $03E8, 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     [$03E7.w] 	 	SKIP   59, $00 ; ячейки для адреса перехода 	dc.b   $00 	dc.b   $00 	 	end ; Read_128000v0D.asm 

Файл WriteBlocks_FLASH_128000v0D.asm:

stm8/  TITLE "WriteBlocks_FLASH_128000v0D.asm"     MOTOROLA     WORDS 	.NOLIST 	#include "STM8S103F3P.inc"     .LIST ; размер дампа должен быть 243 байта ; при этом дамп будет находится в адресах $02EE...$03DF ; (затирает адеса $03DF:$03E0 с адресом перехода к начальному загрузчику) ; и вызываться по адресу $02F0 ; вызвавший дамп начальный загрузчик находится в адресах $03E1...$03FE ; и повторно может быть вызван по адресу $03E1 (переключит скорость на 9600, надо A<=$0D SP<=$03E0 X<=$0000) ; или по адресу $03E9 (скорость не переключит, надо A<=$0D SP<=$03E8 X<=$0000) ; команда перехода к прикладной программе здесь $03FB       segment byte at 0000-00F2 'ram0'       dc.w   $02F0 start: ; настраиваем 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    $03E7, UART1_DR wait_byte_adrL_toM;      btjf   UART1_SR, #5, wait_byte_adrL_toM     mov    $03E8, 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     [$03E7.w] 	 	SKIP   28, $00 ; ячейки для адреса перехода 	dc.b   $00 	dc.b   $00 	 	end ; WriteBlocks_FLASH_128000v0D.asm 

Файл Reset_128000v0D.asm:

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

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

   public readonly static byte[] Read_128000v0D = { 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, 0xE7, 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, 0xE8, 0x03, 0x31,  0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xE7, 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,  0xF0, 0x02 }; 

   public readonly static byte[] WriteBlocks_FLASH_128000v0D = { 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, 0xE7, 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, 0xE8, 0x03,  0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xE7, 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,  0xF0, 0x02 }; 

   public readonly static byte[] Reset_128000v0D = { 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,  0xF0, 0x02 };   

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

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


ссылка на оригинал статьи https://habr.com/post/418483/