STC10/11/12 reverse engineering

Initialisation/Synchronisation
------------------------------

Send a constant stream of 0x7f bytes, and wait for an initial response
by the MCU.

Basic frame format
------------------

M0 M1 DR L0 L1 D0 ... Dn C0 C1 ME

M0 := 0x46
M1 := 0xb9
DR := 0x6a if host2mcu else 0x68
L  := 16 bit big endian packet length, counted from DR to ME
C  := 16 big endian modular sum from DR to Dn
ME := 0x16

D0..Dn is the packet payload

In most cases, the first byte of the payload marks the type of packet
or type of command. Responses by the MCU often use this type to tell
the programmer software which kind of command should follow. For
instance, after the baudrate handshake, the MCU replies with a
type 0x84 packet, and 0x84 is used for "erase" command packets from
the host.

Fun fact: The start marker (0x46, 0xb9) interpreted as UTF-16 is the
Unicode character U+46B9, which is an unusual CJK ideograph (䚹)
which translates as "to prepare" or "all ready" into English. How
fitting! This might not be a coincidence.

Packets host2mcu
----------------

1. Initiate baudrate handshake

Payload: 0x50, 0x07, 0x00, 0x36, 0x01, ID0, ID1
                ^ is 0x00 with current STC software and 11F08XE, what gives?

ID0 = MCU ID, byte 1
ID1 = MCU ID, byte 2

2. Test baudrate setting 

Payload: 0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap

brt := MCU baudrate timer compare
brt_csum := (2 * (256 - brt)) & 0xff
delay := delay after baudrate change (0x40 seems to be fine),
         STC software always seems to use 0xa0
iap := MCU IAP wait state register value

3. Switch to baudrate setting

Payload: 0x8e, 0xc0, brt, 0x3f, brt_csum, delay, iap
                                                  ^ current STC software *omits* this here!  
Almost the same as the test packet.     

4. Erase flash memory

Payload: 0x84, 0xff, 0x00, blks, 0x00, 0x00, size,
                ^ no idea what that is for, current STC software uses 0x00
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00,
         0x80, ..., 0x0e

blks := 256 byte blocks to clear
size := total number of 256 byte blocks (size of flash memory)

The 0x80..0x0e sequence seems to be some kind of magic code
to stop flaky connections and the like from erasing the flash
by accident.

"size" specifies the number of flash memory blocks. if blks > size,
eeprom will be erased.

Note that while erase size is specified in 256 byte blocks, the IAP
memory actually has 512 bytes physical erase block size, and the BSL
expects 512 byte aligned erase commands!

5. Program flash memory

Payload: 0x00, 0x00, 0x00, addr0, addr1, size0, size1, D0, ..., Dn

addr0, addr1 := big-endian 16 bit address
size0, size1 := big-endian 16 bit block size, always 128
D0...Dn := block data

Current STC software always seems to write at least 4 128 byte blocks
for some reason. Data is zero-padded.

Current STC software always writes a sequential set of memory. Since
flash and eeprom are essentially the same, any free space between
flash to be written and eeprom to be written is padded with zeros,
and then the whole batch is sent at once.

6. Finish flash programming

Payload: 0x69, 0x00, 0x00, 0x36, 0x01, ID0, ID1
                ^ kSTC-ISP uses 0x07

This should be sent after all flash programming is done. I am not
entirely sure why, though. Programming also works without it.

7. Set options

Payload: 0x8d, MS0, ..., MS15, CLK0, CLK1, CLK2, CLK3

MS0...MS15 := configuration registers specific to MCU model,
              not documented here.

CLK0...CLK3 := 32 bit big endian measured clock, in Hz

8. Reset MCU

Payload: 0x82


Packets mcu2host
----------------

1. Info packet

Payload: 0x50, SYNC00, SYNC01, ..., SYNC70, SYNC71,
         V1, V2, 0x00, ID0, ID1, 0x8c,
         MS0, ..., MS7,
         UID0, ..., UID6,
         unknown bytes follow

SYNC* := sequence of 8 16-bit big-endian counter values, recorded
         from the initial 0x7f sync sequence. this can be used to
         determine the MCU clock frequency.

V1    := version number, two digits packed BCD.
V2    := stepping, one ASCII character.
ID0   := MCU model ID, byte 1
ID1   := MCU model ID, byte 2
UID0...UID6 := 7 bytes of unique id

UID is only sent by some BSL versions, others send zero bytes.

2. Acknowledge baudrate handshake start

Payload: 0x8f

This means the programming software should erase the flash memory as
the next step.

3. Acknowledge baudrate test

Payload: request packet payload with some pad byte appended to payload

4. Acknowledge baudrate switch

Payload: request packet payload with some pad byte appended to payload, and
         first payload byte changed to 0x84

5. Acknowledge erase

Payload: 0x00, [UID0, ..., UID6]

The UID is optional, not sent by all BSL versions.

6. Acknowledge block write

Payload: 0x00, csum

csum := 8 bit modular sum of flash block data

7. Acknowledge finish flash writing

Payload: 0x8d

This means the programming software should set options as the next
step.

8. Acknowledge set options

Payload: 0x50, MS0, ..., MS4, 0x03, 0xff, V1, V2, MS0, ..., MS7,
         UID0, ..., UID6,
         unknown bytes follow

Some of the model-specific bytes are repeated twice (MS0-MS4).