File: makecombinedhex.py

package info (click to toggle)
firmware-microbit-micropython 1.0.1-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 25,448 kB
  • sloc: ansic: 83,496; cpp: 27,664; python: 2,475; asm: 274; makefile: 245; javascript: 41; sh: 25
file content (66 lines) | stat: -rwxr-xr-x 2,297 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/usr/bin/env python3

'''
Combine the MicroPython firmware with a Python script and produce a hex file
ready for uploading to the micro:bit.

Usage: ./makecombinedhex.py <firmware.hex> <script.py> [-o <combined.hex>]

Output goes to stdout if no filename is given.
'''

import sys
import argparse
import hexlifyscript

def get_largest_addr(hexfile):
    largest_addr = 0
    for line in hexfile:
        count = int(line[1:3], 16)
        addr = int(line[3:7], 16)
        type = int(line[7:9], 16)
        if count == 2 and type == 4: # ext linear addr
            page = int(line[9:13], 16) << 16
        elif type == 0: # data
            # only count pages in flash, not in the UICR
            if page < 0x10000000:
                largest_addr = max(largest_addr, page + addr + count)
    return largest_addr

def find_uicr_line(hexfile):
    for i, line in enumerate(hexfile):
        # UICR from 0x10001000 so we expect an extended linear address record
        if ':020000041000EA' in line:
            return i
    return None

if __name__ == '__main__':
    arg_parser = argparse.ArgumentParser(description='Produce combined hex firmware for the micro:bit.')
    arg_parser.add_argument('-o', '--output', default=sys.stdout, type=argparse.FileType('wt'), help='output file (default is stdout)')
    arg_parser.add_argument('firmware', nargs=1, help='input MicroPython firmware')
    arg_parser.add_argument('script', nargs=1, help='input Python script')
    args = arg_parser.parse_args()

    # read in the firmware
    with open(args.firmware[0], 'rt') as f:
        firmware = f.readlines()

    # check the firmware is not too large
    if get_largest_addr(firmware) > hexlifyscript.SCRIPT_ADDR:
        raise Exception('firmware overflows into script region')

    # hexlify the script
    with open(args.script[0], 'rb') as f:
        script = hexlifyscript.hexlify_script(f.read())

    # print lines until UICR area or the start linear address record
    firmware_end = find_uicr_line(firmware) or len(firmware) - 2
    for line in firmware[:firmware_end]:
        print(line, end='', file=args.output)

    # print script
    print(script, file=args.output)

    # print rest of hex file
    for line in firmware[firmware_end:]:
        print(line, end='', file=args.output)