411 lines
8.1 KiB
ArmAsm
411 lines
8.1 KiB
ArmAsm
; ================================================
|
|
; STM8 RAM Bootloader for SDCC - Optimized Version
|
|
;
|
|
; 命令帧格式:
|
|
; 字节0: 帧头 (0x5A/0xA5)
|
|
; 字节1: 命令类型 (见CMD_*常量)
|
|
; 字节2-3: 目标地址 (高字节在前)
|
|
; 字节4: 数据长度 (0-64)
|
|
; 字节5-68: 数据内容 (最多64字节)
|
|
; 字节69: 校验和 (所有字节XOR)
|
|
; ================================================
|
|
|
|
;; Register address definitions
|
|
UART1_SR = 0x5230 ; Status register
|
|
UART1_DR = 0x5231 ; Data register
|
|
UART1_BRR1 = 0x5232 ; Baud rate register 1
|
|
UART1_BRR2 = 0x5233 ; Baud rate register 2
|
|
UART1_CR2 = 0x5235 ; Control register 2
|
|
|
|
FLASH_DUKR = 0x5064 ; Data EEPROM unprotect
|
|
FLASH_PUKR = 0x5062 ; Flash unprotect
|
|
FLASH_CR2 = 0x505B ; Flash control 2
|
|
FLASH_NCR2 = 0x505C ; Flash control 2 complement
|
|
FLASH_IAPSR = 0x505F ; Flash in-application program status
|
|
|
|
OPT0_ROP = 0x4800 ; Option byte: ROP
|
|
WWDG_CR = 0x50D1 ; WWDG control register
|
|
|
|
;; Const vars
|
|
CMD_READ = 0xF1 ; 读内存命令
|
|
CMD_WRITE = 0xF2 ; 写内存命令
|
|
CMD_GO = 0xF3 ; 跳转执行命令
|
|
CMD_EXEC = 0xF4 ; 直接执行机器码命令
|
|
|
|
CMD_HEADER = 0x5A ; 帧头
|
|
ACK_HEADER = 0xA5 ; 应答帧头
|
|
|
|
SUCCESS_CODE = 0x00 ; 成功响应码
|
|
ERR_CHECKSUM = 0xE1 ; 校验错误
|
|
ERR_INVCMD = 0xE2 ; 非法命令
|
|
ERR_PGDIS = 0xE3 ; 编程受保护的地址
|
|
|
|
MAX_DATA_SIZE = 64 ; 最大数据长度
|
|
|
|
;; Global vars
|
|
;; After an MCU reset the Stack Pointer is set to its upper limit value
|
|
DEFAULT_SP_H = 0x0000 ; Saved with SP value
|
|
DEFAULT_SP_L = 0x0001 ;
|
|
rx_state = 2 ; 接收状态
|
|
rx_length = 3 ; 接收长度
|
|
tx_state = 4 ; 发送状态
|
|
tx_data_length = 5 ; 待发送的数据长度
|
|
calc_checksum = 6 ; 计算的校验和
|
|
temp_var1 = 7 ; 临时变量
|
|
temp_var2 = 8 ; 临时变量
|
|
temp_var3 = 9 ; 临时变量
|
|
tx_buffer = 0x000A ; protocol tx buffer
|
|
rx_buffer = 0x000A ; protocol rx buffer
|
|
|
|
BOOT2_ULA = 0x03CF ; boot2 ram Upper-Limit-Address
|
|
|
|
.area DATA
|
|
.area HOME
|
|
.area RAM_BOOT
|
|
|
|
.db (BOOT2_ULA-(_end-_start)+1)>>8
|
|
.db (BOOT2_ULA-(_end-_start)+1)&0xFF
|
|
|
|
_start:
|
|
; 配置UART1: 128000波特率, 8N1, 启用TX/RX
|
|
mov UART1_BRR1, #1
|
|
;mov UART1_BRR2, #0
|
|
;mov UART1_CR2, #0x0C ; TEN=1, REN=1
|
|
_main_loop:
|
|
; 接收命令帧
|
|
callr receive_frame
|
|
|
|
; 验证校验和
|
|
callr verify_checksum
|
|
jrne _checksum_error
|
|
|
|
; 根据命令类型跳转
|
|
ld A, rx_buffer+1 ; 命令类型
|
|
|
|
cp A, #CMD_READ
|
|
jreq _cmd_read
|
|
|
|
cp A, #CMD_WRITE
|
|
jreq _cmd_write
|
|
|
|
cp A, #CMD_GO
|
|
jreq _cmd_go
|
|
|
|
cp A, #CMD_EXEC
|
|
jreq _cmd_exec
|
|
|
|
_invalid_cmd_error:
|
|
; 未知命令,发送错误响应
|
|
mov tx_state, #ERR_INVCMD
|
|
call send_ack_state_response
|
|
jra _main_loop
|
|
|
|
_checksum_error:
|
|
; 校验和错误响应
|
|
mov tx_state, #ERR_CHECKSUM
|
|
call send_ack_state_response
|
|
jra _main_loop
|
|
|
|
_cmd_read:
|
|
; 读取内存命令
|
|
call read_memory
|
|
jra _main_loop
|
|
|
|
_cmd_write:
|
|
; 写入内存命令
|
|
call write_memory
|
|
jra _main_loop
|
|
|
|
_cmd_go:
|
|
; 跳转执行命令
|
|
ldw X, rx_buffer+2
|
|
jp (X)
|
|
; 注意: jump_to_address 不返回
|
|
|
|
_cmd_exec:
|
|
; 直接执行收到的机器码
|
|
ld A, #0x81 ; ret code
|
|
ld (X), A ; X point to checksum already
|
|
call rx_buffer
|
|
jra _main_loop
|
|
|
|
receive_frame:
|
|
; 初始化接收状态
|
|
clr rx_state
|
|
mov rx_length, #6
|
|
ldw X, #rx_buffer
|
|
|
|
_receive_data:
|
|
; 接收数据部分, 等待RXNE=1
|
|
btjf UART1_SR, #5, _receive_data
|
|
|
|
; 根据状态接收不同部分
|
|
ld A, rx_state
|
|
|
|
cp A, #0
|
|
jreq _receive_header
|
|
|
|
cp A, #4
|
|
jreq _receive_length
|
|
|
|
ld A, UART1_DR
|
|
_save_data:
|
|
ld (X), A
|
|
incw X
|
|
inc rx_state
|
|
|
|
dec rx_length
|
|
jrne _receive_data ; 还有数据,继续接收
|
|
|
|
; 数据接收完成
|
|
mov rx_length, rx_state
|
|
ret
|
|
|
|
_receive_header:
|
|
ld A, UART1_DR
|
|
cp A, #CMD_HEADER
|
|
jrne _receive_data
|
|
jra _save_data
|
|
_receive_length:
|
|
ld A, UART1_DR
|
|
cp A, #MAX_DATA_SIZE
|
|
; length<=MAX_DATA_SIZE
|
|
jrugt receive_frame
|
|
; save length
|
|
ld rx_length, A
|
|
inc rx_length
|
|
inc rx_length
|
|
jra _save_data
|
|
|
|
verify_checksum:
|
|
; 初始化
|
|
clr calc_checksum
|
|
|
|
; 计算需要校验的字节数
|
|
ld A, rx_length
|
|
dec A
|
|
ld temp_var1, A
|
|
|
|
; 设置指针
|
|
ldw X, #rx_buffer
|
|
|
|
_verify_loop:
|
|
ld A, (X)
|
|
xor A, calc_checksum
|
|
ld calc_checksum, A
|
|
incw X
|
|
dec temp_var1
|
|
jrne _verify_loop
|
|
|
|
; 比较校验和
|
|
ld A, calc_checksum
|
|
cp A, (X) ; X现在指向校验和位置
|
|
|
|
ret
|
|
|
|
send_response_pkg:
|
|
clr calc_checksum
|
|
ldw X, #tx_buffer
|
|
ld A, tx_data_length
|
|
add A, #5
|
|
ld temp_var1, A
|
|
; send data
|
|
_send_loop:
|
|
ld A, (X)
|
|
ld UART1_DR, A
|
|
; calc checksum
|
|
xor A, calc_checksum
|
|
ld calc_checksum, A
|
|
_wait_tx1:
|
|
btjf UART1_SR, #7, _wait_tx1
|
|
; move to next
|
|
incw X
|
|
dec temp_var1
|
|
jrne _send_loop
|
|
; send checksum
|
|
ld A, calc_checksum
|
|
ld UART1_DR, A
|
|
_wait_tx2:
|
|
btjf UART1_SR, #7, _wait_tx2
|
|
; finish
|
|
ret
|
|
|
|
; 发送应答状态帧
|
|
send_ack_state_response:
|
|
; set header
|
|
ldw X, #tx_buffer
|
|
ld A, #ACK_HEADER
|
|
ld (X), A
|
|
|
|
; set length
|
|
addw X, #4
|
|
ld A, #1
|
|
ld (X), A
|
|
ld tx_data_length, A
|
|
|
|
; set data
|
|
incw X
|
|
ld A, tx_state
|
|
ld (X), A
|
|
|
|
callr send_response_pkg
|
|
ret
|
|
|
|
; 发送应答数据帧
|
|
send_ack_data_response:
|
|
; set header
|
|
ldw X, #tx_buffer
|
|
ld A, #ACK_HEADER
|
|
ld (X), A
|
|
|
|
; set length
|
|
addw X, #4
|
|
ld A, tx_data_length
|
|
ld (X), A
|
|
|
|
; already set data
|
|
|
|
callr send_response_pkg
|
|
ret
|
|
|
|
read_memory:
|
|
; A = 读取长度
|
|
ldw X, #rx_buffer+5
|
|
ld A, (X)
|
|
ld tx_data_length, A
|
|
ld temp_var1, A
|
|
|
|
; Y = 缓存地址
|
|
ldw Y, X
|
|
|
|
; X = 读取地址
|
|
ldw X, #rx_buffer+2
|
|
ldw X, (X)
|
|
|
|
_read_loop:
|
|
; 读取字节
|
|
ld A, (X)
|
|
ld (Y), A
|
|
|
|
incw X
|
|
incw Y
|
|
dec temp_var1
|
|
jrne _read_loop
|
|
|
|
callr send_ack_data_response
|
|
ret
|
|
|
|
; temp_var1: 待写入长度
|
|
; temp_var2: 单次写长度
|
|
; temp_var3: FLASH_IAPSR
|
|
write_memory:
|
|
; A = 写入长度
|
|
ldw X, #rx_buffer+4
|
|
ld A, (X)
|
|
ld temp_var1, A
|
|
; 检查长度为0直接返回
|
|
tnz A
|
|
jreq _write_success
|
|
|
|
; Y = src
|
|
incw X
|
|
ldw Y, X
|
|
|
|
; X = dst
|
|
ldw X, #rx_buffer+2
|
|
ldw X, (X)
|
|
|
|
; 检查是否为ram地址 (0x0000-0x3FFF)
|
|
ld A, XH
|
|
cp A, #0x40
|
|
jrult _mem_write
|
|
; 检查是否为flash地址 (0x8000-0xFFFF)
|
|
cp A, #0x80
|
|
jruge _flash_write
|
|
; 检查是否为eeprom/opt地址 (0x4000-0x4FFF)
|
|
cp A, #0x50
|
|
jrult _flash_write
|
|
|
|
_mem_write:
|
|
ld A, (Y)
|
|
ld (X), A
|
|
incw X
|
|
incw Y
|
|
dec temp_var1
|
|
jrne _mem_write
|
|
callr send_ack_state_response
|
|
ret
|
|
|
|
_flash_write:
|
|
; unlock FLASH/DATA
|
|
callr unlock_flash
|
|
|
|
; Word Program
|
|
mov FLASH_CR2, #0xC0
|
|
mov FLASH_NCR2, #0x3F
|
|
|
|
_word_write:
|
|
mov temp_var2, #4
|
|
_write_loop:
|
|
; Write a Word
|
|
ld A, (Y)
|
|
ld (X), A
|
|
incw X
|
|
incw Y
|
|
dec temp_var2
|
|
jrne _write_loop
|
|
; Wait write done
|
|
_wait_flash_done:
|
|
mov temp_var3, FLASH_IAPSR
|
|
btjt temp_var3, #0, _write_error
|
|
btjf temp_var3, #2, _wait_flash_done
|
|
|
|
ld A, temp_var1
|
|
cp A, #4
|
|
jrule _write_success
|
|
sub A, #4
|
|
ld temp_var1, A
|
|
jra _word_write
|
|
|
|
_write_error:
|
|
mov tx_state, #ERR_PGDIS
|
|
jra _write_end
|
|
_write_success:
|
|
mov tx_state, #SUCCESS_CODE
|
|
_write_end:
|
|
; Word programming off
|
|
mov FLASH_CR2, #0x00
|
|
mov FLASH_NCR2, #0xFF
|
|
; lock FLASH/DATA
|
|
callr lock_flash
|
|
call send_ack_state_response
|
|
ret
|
|
|
|
unlock_flash:
|
|
; 检查是否为Flash地址 (0x8000-0xFFFF)
|
|
ld A, XH
|
|
cp A, #0x80
|
|
jrult _do_unlock_data
|
|
_do_unlock_flash:
|
|
mov FLASH_PUKR, #0x56 ; KEY1
|
|
mov FLASH_PUKR, #0xAE ; KEY2
|
|
ret
|
|
_do_unlock_data:
|
|
mov FLASH_DUKR, #0xAE ; KEY1
|
|
mov FLASH_DUKR, #0x56 ; KEY2
|
|
ret
|
|
|
|
lock_flash:
|
|
; 检查是否为Flash地址 (0x8000-0xFFFF)
|
|
ld A, XH
|
|
cp A, #0x80
|
|
jrult _do_lock_data
|
|
_do_lock_flash:
|
|
bres FLASH_IAPSR, #1
|
|
ret
|
|
_do_lock_data:
|
|
bres FLASH_IAPSR, #3
|
|
ret
|
|
|
|
_end:
|