├── .bzrignore ├── .gitignore ├── AUTHORS.rst ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── NEWS.rst ├── README.rst ├── docs ├── intelhex.pdf ├── manual.txt └── manual │ ├── Makefile │ ├── appendix-a.txt │ ├── conf.py │ ├── index.txt │ ├── part1-1.txt │ ├── part1-2.txt │ ├── part1-3.txt │ ├── part1-4.txt │ ├── part1.txt │ ├── part2-1.txt │ ├── part2-2.txt │ ├── part2-3.txt │ ├── part2-4.txt │ ├── part2-5.txt │ ├── part2-6.txt │ ├── part2-7.txt │ ├── part2-8.txt │ ├── part2.txt │ ├── part3-1.txt │ ├── part3-2.txt │ ├── part3-3.txt │ ├── part3-4.txt │ ├── part3-5.txt │ ├── part3-6.txt │ ├── part3.txt │ └── part4.txt ├── intelhex ├── __init__.py ├── __main__.py ├── __version__.py ├── bench.py ├── compat.py ├── getsizeof.py ├── scripts │ ├── __init__.py │ ├── bin2hex.py │ ├── hex2bin.py │ ├── hex2dump.py │ ├── hexdiff.py │ ├── hexinfo.py │ └── hexmerge.py └── test.py ├── requirements-dev.txt ├── setup.cfg ├── setup.py ├── test-all-python.sh ├── test_memory.py └── tox.ini /.bzrignore: -------------------------------------------------------------------------------- 1 | ./api 2 | ./build 3 | ./dist 4 | ./docs/manual.html 5 | ./docs/manual/.build/doctrees/* 6 | ./docs/manual/.build/html/* 7 | ./intelhex-* 8 | ./MANIFEST 9 | __pycache__ 10 | tags 11 | fastimport.marks 12 | fastimport.stream 13 | wheelhouse 14 | ./.git 15 | ./.gitignore 16 | ./venv 17 | ./python.bat 18 | ./*.html 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.pyc 3 | .bzr/* 4 | *.~* 5 | docs/manual.html 6 | docs/manual/.build/doctrees/* 7 | docs/manual/.build/html/* 8 | venv/ 9 | dist/ 10 | .tox/ 11 | MANIFEST 12 | python.bat 13 | NEWS.html 14 | README.html 15 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Author: Alexander Belchenko 2 | 3 | Copyright (C) Alexander Belchenko, 2005-2018 4 | 5 | Special thanks to Bernhard Leiner for huge help in porting 6 | IntelHex library to Python 3. 7 | 8 | Contributors: 9 | 10 | * Alexander Belchenko 11 | * Alex Mueller 12 | * Andrew Fernandes 13 | * Bernhard Leiner 14 | * Enoch H. Wexler 15 | * Heiko Henkelmann 16 | * Henrik Maier 17 | * Masayuki Takeda 18 | * Morgan McClure 19 | * Nathan P. Stien 20 | * Piotr Korowacki 21 | * Reis Baltaoglu 22 | * Ryan Downing 23 | * Scott Armitage 24 | * Stefan Schmitt 25 | * Svein Seldal 26 | * Theo Sbrissa 27 | * Zachary Clifford 28 | * "durexyl" @ GitHub 29 | * "erki1993" @ GitHub 30 | * "mentaal" @ GitHub 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2018, Alexander Belchenko 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, 5 | with or without modification, are permitted provided 6 | that the following conditions are met: 7 | 8 | * Redistributions of source code must retain 9 | the above copyright notice, this list of conditions 10 | and the following disclaimer. 11 | * Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions 13 | and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | * Neither the name of the author nor the names 16 | of its contributors may be used to endorse 17 | or promote products derived from this software 18 | without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 22 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 23 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 26 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 32 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include LICENSE.txt 3 | include NEWS.rst 4 | include README.rst 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON := python 2 | 3 | all: 4 | @echo Available targets: 5 | @echo clean - clean build directory 6 | @echo test - run unittest 7 | @echo dev - install via pip as editable package 8 | @echo apidoc - run epdoc to create API documentation 9 | @echo wininst - Windows installer for Python 10 | @echo docs - build docs with ReST and Sphinx 11 | @echo wheel - build python wheel binary archive 12 | 13 | .PHONY: clean test epydoc wininst docs dev apidoc wheel 14 | 15 | clean: 16 | $(PYTHON) setup.py clean -a 17 | 18 | test: 19 | $(PYTHON) setup.py test -q 20 | 21 | dev: 22 | $(PYTHON) -m pip install -e . 23 | 24 | apidoc: 25 | pdoc -o docs/api --html -f intelhex 26 | 27 | wininst: 28 | $(PYTHON) setup.py bdist_wininst -d. 29 | 30 | docs: 31 | rst2html5.py docs/manual.txt docs/manual.html 32 | $(MAKE) -C docs/manual html 33 | 34 | wheel: 35 | $(PYTHON) -m pip wheel -w dist . 36 | -------------------------------------------------------------------------------- /NEWS.rst: -------------------------------------------------------------------------------- 1 | ***************** 2 | IntelHex releases 3 | ***************** 4 | 5 | * Added support for ``in`` operator to check if address is in HEX file. 6 | 7 | 2.3.0 (2020-10-20) 8 | ------------------ 9 | * Add ``IntelHex.find()`` method to find a given byte pattern. (Scott Armitage) 10 | * API changes: ``IntelHex.segments()`` method supports new optional parameter 11 | ``min_gap`` to allow consolidation of segments with small but existing gaps 12 | into a single segment. Default value is 1. (Ryan Downing) 13 | * API changes: ``IntelHex.tofile()`` now supports the optional ``byte_count`` 14 | parameter from ``IntelHex.write_hex_file()``. Only used if ``format = hex``. 15 | (Reis Baltaoglu) 16 | * Fix Python 3.9 compatibility issue with 'array' module (Piotr Korowacki) 17 | * Fix installation for Python version taking setup rather from setuptools than 18 | distutils (Theo Sbrissa) 19 | 20 | 2.2.1 (2018-01-30) 21 | ------------------ 22 | * Fixes for PyPI. 23 | 24 | 2.2 (2018-01-28) 25 | ---------------- 26 | * API changes: ``IntelHex.write_hex_file`` method: added support for new 27 | parameter: ``eolstyle = native | CRLF``. (Alexander Belchenko) 28 | * API changes: ``IntelHex.write_hex_file()`` method gets new optional 29 | parameter ``byte_count`` to specify how many bytes should be written 30 | to each data record in output file. Default value is 16. 31 | (patch from GitHub user erki1993) 32 | * Unit tests: Fixed xrange overflow test for Python 2.7 on 64-bit platforms. 33 | Use ``sys.maxint`` to ensure we trigger an exception. (Masayuki Takeda) 34 | * Script ``hexinfo.py``: Python 3 compatibility for processing start address 35 | dict keys. (patch from GitHub user mentaal) 36 | * Added ``get_memory_size()`` method: approx memory footprint of IntelHex object 37 | plus data. (Alexander Belchenko) 38 | * Better compatibility with Python 3. (Alexander Belchenko) 39 | 40 | 2.1 (2016-03-31) 41 | ---------------- 42 | * API changes: added ``IntelHex.segments()`` method that returns 43 | a list of ordered tuple objects, representing contiguous occupied data 44 | addresses. (Andrew Fernandes) 45 | * New command-line script ``hexinfo.py`` to print summary about hex files 46 | contents (file name, start address, address ranges covered by the data) 47 | in YAML format. (Andrew Fernandes) 48 | * Better Python 3 compatibility when ``hex2bin.py`` and ``bin2hex.py`` 49 | scripts are trying to read/write binary data from stdin or to stdout. 50 | (GitHub issue https://github.com/python-intelhex/intelhex/issues/4) 51 | * The main activity of the IntelHex project slowly drifting towards 52 | GitHub - the main social network for OSS developers. 53 | I'd really like to get some help from additional maintainer though. 54 | * API changes: ``IntelHex.dump()`` method gets new optional parameters: 55 | ``width``, ``withpadding`` to control generation of output text. 56 | (patch from GitHub user durexyl) 57 | * Script ``hex2dump.py`` gets new option ``--width`` to support 58 | corresponding parameter in ``IntelHex.dump()`` method. 59 | 60 | 2.0 (2015-04-12) 61 | ---------------- 62 | * The same codebase can be run on both Python 2 (2.4-2.7) 63 | and Python 3 (3.2+). No need to use 2to3. 64 | * ``compat.py``: provide more helper functions and aliases to reduce changes 65 | required to convert python 2 compatible sources to python 3. 66 | The code becomes quite ugly, but such compatibility has its price. 67 | * Python 3 compatibility: tobinstr should return bytes not unicode string 68 | (Bug #1212698). 69 | * Python 2: better support for long int addresses (over 2GB) 70 | (Bug #1408934) 71 | 72 | 1.5 (2013-08-02) 73 | ---------------- 74 | * API changes: Functions tobinarray/tobinstr/tobinfile: 75 | pad parameter is deprecated and will be removed in 76 | future releases. Use IntelHex.padding attribute instead, 77 | and don't pass pad as None explicitly please. 78 | If you need to use size parameter, then use syntax like that: 79 | ``ih.tobinarray(start=xxx, size=yyy)`` 80 | * API changes: Functions tobinarray/tobinstr/tobinfile: 81 | default value of pad is None now (was ``0xFF``) 82 | to allow using value of ``IntelHex.padding`` 83 | if no explicit pad specified. 84 | * Fixed bug: wrong ``getopt`` error handling in some scripts. 85 | (Thanks to Andy Mozhevilov for bug report) 86 | * PEP-8 style improvements. (Thanks to Stefan Schmitt) 87 | * ``IntelHex16bit.tobinarray`` method returns array of unsigned short 88 | (words) values. (Feature request from Stefan Schmitt) 89 | * Improved Python 3 compatibility (don't use old file() function). 90 | (Thanks to Luis Panadero Guardeño for bug report) 91 | 92 | 1.4 (2012-04-25) 93 | ---------------- 94 | * New feature: compare 2 hex files using hex dump 95 | as string representation. Feature available as 96 | worker function diff_dumps() and as command-line 97 | utility hexdiff.py (#627924). 98 | * Changes in the codebase suggested by 2to3 tool to provide 99 | compatibility with Python3. Now sources can be successfully 100 | converted to Python3 with 2to3 utility. 101 | See Python 3 notes in README.txt and documentation. 102 | (Thanks to Bernhard Leiner for his help) 103 | * Fixed bug #988148: ``IntelHex16bit`` should copy all public attributes 104 | from source IntelHex 8-bit object. (Thanks to Morgan McClure) 105 | 106 | 1.3 (2010-11-24) 107 | ---------------- 108 | * ``hex2dump``: show 0x7F character as dot for better compatibility 109 | with GNU less utility. 110 | * tobinarray, tobinfile, tobinstr: added size parameter. (Bug #408748) 111 | * fixed error in ``hexmerge.py`` script. (#676023) 112 | 113 | 1.2 (2009-08-04) 114 | ---------------- 115 | * Fixed bug 372620: tobinarray on empty file should return pad bytes 116 | when address range explicitly specified. 117 | * Improved docstrings: explicitly say that ``end`` param of to-* methods 118 | is always inclusive. (see bug #372625 for details). 119 | * Improved documentation on ``ih.dump(tofile)``. 120 | 121 | 1.1 (2009-03-12) 122 | ---------------- 123 | * Fixed bug in writing hex files with small chains of bytes 124 | * Improved Python 2.6 compatibility 125 | 126 | 1.0 (2009-01-01) 127 | ---------------- 128 | * Improved API, better performance 129 | * New User Manual (Zachary Clifford) 130 | 131 | 0.9 (2007-06-16) 132 | ---------------- 133 | New API release. 134 | 135 | * New API 136 | * Performance improvements: read hex file now ~45% faster 137 | 138 | 0.8.6 (2007-04-27) 139 | ------------------ 140 | Bug fixes and performance improvements. 141 | 142 | * ``IntelHex`` is able to read/write start address records 143 | (HEX record type ``03`` and ``05``). (fix bug #109872) 144 | * Backport (from 0.9 branch) of performance improvements 145 | for reading hex files 146 | 147 | 0.8.5 (2007-02-26) 148 | ------------------ 149 | BugFix Release. 150 | 151 | Performance improvements for writing big hex files 152 | when starting address is far from 0. Patch from Heiko Henkelmann. 153 | 154 | 0.8.4 (2007-02-26) 155 | ------------------ 156 | License added. 157 | 158 | The code is actually licensed under BSD, but there was 159 | no LICENSE file in sources archive. Added license file 160 | and explicit declaration in the source code. 161 | 162 | 0.8.3 (2006-09-05) 163 | ------------------ 164 | BugFix Release. 165 | 166 | Fix writing hex files with extended linear records 167 | (when address overlaps 64K boundary). Patch from Henrik Maier. 168 | 169 | 0.8.2 (2006-04-11) 170 | ------------------ 171 | Major improvements release. 172 | 173 | * Introduced new class ``IntelHex16bit`` for manipulate data as 16-bit values 174 | * You can manipulate data using dictionary-like interface 175 | (i.e. syntax like: ``ih[addr] = value``) 176 | * Added new method ``writefile(file)`` for writing data to hex file 177 | * Using unittest for testing functionality 178 | 179 | 0.6 (2006-03) 180 | ------------- 181 | Convertor engine ``hex2bin`` extracted to stand-alone function 182 | for using by external clients of intelhex. 183 | 184 | 0.5 (2005) 185 | ---------- 186 | First public release. 187 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Python IntelHex library 2 | *********************** 3 | 4 | Introduction 5 | ------------ 6 | The Intel HEX file format is widely used in microprocessors and microcontrollers 7 | area (embedded systems etc) as the de facto standard 8 | for representation of code to be programmed into microelectronic devices. 9 | 10 | This work implements an ``intelhex`` Python library to read, write, 11 | create from scratch and manipulate data from Intel HEX file format. 12 | 13 | The distribution package also includes several convenience Python scripts, 14 | including "classic" ``hex2bin`` and ``bin2hex`` converters and more, 15 | those based on the library itself. Check the docs to know more. 16 | 17 | License 18 | ------- 19 | The code is distributed under BSD license, 20 | see `LICENSE.txt `_. 21 | 22 | In short: you can use IntelHex library in your project without *any* 23 | restrictions. 24 | 25 | Supported Python versions 26 | ------------------------- 27 | IntelHex library supports Python 3 (3.5 or later) only. The 2.2.1 release was 28 | the last one which has been checked against Python 2.7 and Python 3 until 3.5. 29 | 30 | Install 31 | ------- 32 | Install using ``pip`` (recommended, no separate download required):: 33 | 34 | pip install intelhex 35 | 36 | Download 37 | -------- 38 | * https://pypi.org/project/IntelHex/ 39 | * https://github.com/python-intelhex/intelhex/releases 40 | 41 | Source code, bug reports, patches 42 | --------------------------------- 43 | IntelHex on GitHub: 44 | 45 | https://github.com/python-intelhex/intelhex 46 | 47 | User manual 48 | ----------- 49 | User manual for IntelHex is available in the sources ``docs/manual/`` directory. 50 | You can browse User Manual online: 51 | 52 | https://readthedocs.org/projects/python-intelhex/ 53 | 54 | Changelog 55 | --------- 56 | See `NEWS.rst `_ 57 | -------------------------------------------------------------------------------- /docs/intelhex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-intelhex/intelhex/bdc202f437ab084036699d4cc9857d0844cd2dbe/docs/intelhex.pdf -------------------------------------------------------------------------------- /docs/manual.txt: -------------------------------------------------------------------------------- 1 | ----------------------------------- 2 | Python IntelHex Library User Manual 3 | ----------------------------------- 4 | 5 | :Version: 2.3.0 6 | 7 | .. contents:: 8 | .. sectnum:: 9 | 10 | Introduction 11 | ------------ 12 | .. include:: manual/part1-1.txt 13 | .. include:: manual/part1-2.txt 14 | .. include:: manual/part1-3.txt 15 | .. include:: manual/part1-4.txt 16 | 17 | Basic API and usage 18 | ------------------- 19 | .. include:: manual/part2-1.txt 20 | .. include:: manual/part2-2.txt 21 | .. include:: manual/part2-3.txt 22 | .. include:: manual/part2-4.txt 23 | .. include:: manual/part2-5.txt 24 | .. include:: manual/part2-6.txt 25 | .. include:: manual/part2-7.txt 26 | .. include:: manual/part2-8.txt 27 | 28 | Convenience Scripts 29 | ------------------- 30 | When IntelHex is installed and added to the system path, 31 | some scripts are available for usage. 32 | Each one is meant to be operated from the command line. 33 | They provide help if called incorrectly. 34 | 35 | .. include:: manual/part3-1.txt 36 | .. include:: manual/part3-2.txt 37 | .. include:: manual/part3-3.txt 38 | .. include:: manual/part3-4.txt 39 | .. include:: manual/part3-5.txt 40 | .. include:: manual/part3-6.txt 41 | 42 | Embedding into other projects 43 | ***************************** 44 | IntelHex should be easy to embed in other projects. 45 | The directory ``intelhex`` containing ``__init__.py`` can be directly placed 46 | in a depending project and used directly. From that project the same import 47 | statements described above can be used to make the library work. 48 | From other projects the import statement would change to:: 49 | 50 | >>> from myproject.intelhex import IntelHex 51 | 52 | Alternatively, the IntelHex package can be installed into the site-packages 53 | directory and used as a system package. 54 | 55 | In either case, IntelHex is distributed with a BSD-style license. 56 | This permits you to use it in any way you see fit, provided that the package 57 | is appropriately credited. 58 | 59 | 60 | .. include:: manual/appendix-a.txt 61 | -------------------------------------------------------------------------------- /docs/manual/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | 9 | # Internal variables. 10 | PAPEROPT_a4 = -D latex_paper_size=a4 11 | PAPEROPT_letter = -D latex_paper_size=letter 12 | ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 13 | 14 | .PHONY: help clean html web pickle htmlhelp latex changes linkcheck 15 | 16 | help: 17 | @echo "Please use \`make ' where is one of" 18 | @echo " html to make standalone HTML files" 19 | @echo " pickle to make pickle files" 20 | @echo " json to make JSON files" 21 | @echo " htmlhelp to make HTML files and a HTML help project" 22 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 23 | @echo " changes to make an overview over all changed/added/deprecated items" 24 | @echo " linkcheck to check all external links for integrity" 25 | 26 | clean: 27 | -rm -rf .build/* 28 | 29 | html: 30 | # mkdir -p .build/html .build/doctrees 31 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html 32 | @echo 33 | @echo "Build finished. The HTML pages are in .build/html." 34 | 35 | pickle: 36 | mkdir -p .build/pickle .build/doctrees 37 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle 38 | @echo 39 | @echo "Build finished; now you can process the pickle files." 40 | 41 | web: pickle 42 | 43 | json: 44 | mkdir -p .build/json .build/doctrees 45 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json 46 | @echo 47 | @echo "Build finished; now you can process the JSON files." 48 | 49 | htmlhelp: 50 | mkdir -p .build/htmlhelp .build/doctrees 51 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp 52 | @echo 53 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 54 | ".hhp project file in .build/htmlhelp." 55 | 56 | latex: 57 | mkdir -p .build/latex .build/doctrees 58 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex 59 | @echo 60 | @echo "Build finished; the LaTeX files are in .build/latex." 61 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 62 | "run these through (pdf)latex." 63 | 64 | changes: 65 | mkdir -p .build/changes .build/doctrees 66 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes 67 | @echo 68 | @echo "The overview file is in .build/changes." 69 | 70 | linkcheck: 71 | mkdir -p .build/linkcheck .build/doctrees 72 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck 73 | @echo 74 | @echo "Link check complete; look for any errors in the above output " \ 75 | "or in .build/linkcheck/output.txt." 76 | -------------------------------------------------------------------------------- /docs/manual/appendix-a.txt: -------------------------------------------------------------------------------- 1 | Appendix A. IntelHex Errors Hierarchy 2 | ------------------------------------- 3 | 4 | * ``IntelHexError`` - base error 5 | 6 | * ``HexReaderError`` - general hex reader error 7 | 8 | * ``AddressOverlapError`` - data for the same address overlap 9 | * ``HexRecordError`` - hex record decoder base error 10 | 11 | * ``RecordLengthError`` - record has invalid length 12 | * ``RecordTypeError`` - record has invalid type (RECTYP) 13 | * ``RecordChecksumError`` - record checksum mismatch 14 | * ``EOFRecordError`` - invalid EOF record (type 01) 15 | * ``ExtendedAddressRecordError`` - extended address record base error 16 | 17 | * ``ExtendedSegmentAddressRecordError`` - invalid extended segment address record (type 02) 18 | * ``ExtendedLinearAddressRecordError`` - invalid extended linear address record (type 04) 19 | 20 | * ``StartAddressRecordError`` - start address record base error 21 | 22 | * ``StartSegmentAddressRecordError`` - invalid start segment address record (type 03) 23 | * ``StartLinearAddressRecordError`` - invalid start linear address record (type 05) 24 | * ``DuplicateStartAddressRecordError`` - start address record appears twice 25 | * ``InvalidStartAddressValueError`` - invalid value of start addr record 26 | 27 | * ``BadAccess16bit`` - not enough data to read 16 bit value 28 | * ``NotEnoughDataError`` - not enough data to read N contiguous bytes 29 | * ``EmptyIntelHexError`` - requested operation cannot be performed with empty object 30 | -------------------------------------------------------------------------------- /docs/manual/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # IntelHex documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Jan 01 06:25:34 2009. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # The contents of this file are pickled, so don't put values in the namespace 9 | # that aren't pickleable (module imports are okay, they're removed automatically). 10 | # 11 | # Note that not all possible configuration values are present in this 12 | # autogenerated file. 13 | # 14 | # All configuration values have a default; values that are commented out 15 | # serve to show the default. 16 | 17 | import sys, os 18 | 19 | # If your extensions are in another directory, add it here. If the directory 20 | # is relative to the documentation root, use os.path.abspath to make it 21 | # absolute, like shown here. 22 | #sys.path.append(os.path.abspath('.')) 23 | 24 | # General configuration 25 | # --------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = [] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['.templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.txt' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = 'Python IntelHex library' 45 | copyright = '2005-2018, Alexander Belchenko' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | 51 | # let's use it right from the intelhex package 52 | exec(open('../../intelhex/__version__.py').read()) 53 | VERSION = version_str # from __version__.py 54 | 55 | # 56 | # The short X.Y version. 57 | version = VERSION 58 | # The full version, including alpha/beta/rc tags. 59 | release = VERSION 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | #language = None 64 | 65 | # There are two options for replacing |today|: either, you set today to some 66 | # non-false value, then it is used: 67 | #today = '' 68 | # Else, today_fmt is used as the format for a strftime call. 69 | #today_fmt = '%B %d, %Y' 70 | 71 | # List of documents that shouldn't be included in the build. 72 | #unused_docs = [] 73 | 74 | # List of directories, relative to source directory, that shouldn't be searched 75 | # for source files. 76 | exclude_trees = ['.build'] 77 | 78 | # The reST default role (used for this markup: `text`) to use for all documents. 79 | #default_role = None 80 | 81 | # If true, '()' will be appended to :func: etc. cross-reference text. 82 | #add_function_parentheses = True 83 | 84 | # If true, the current module name will be prepended to all description 85 | # unit titles (such as .. function::). 86 | #add_module_names = True 87 | 88 | # If true, sectionauthor and moduleauthor directives will be shown in the 89 | # output. They are ignored by default. 90 | #show_authors = False 91 | 92 | # The name of the Pygments (syntax highlighting) style to use. 93 | pygments_style = 'sphinx' 94 | 95 | 96 | # Options for HTML output 97 | # ----------------------- 98 | 99 | #html_theme = 'classic' 100 | 101 | # The style sheet to use for HTML and HTML Help pages. A file of that name 102 | # must exist either in Sphinx' static/ path, or in one of the custom paths 103 | # given in html_static_path. 104 | #html_style = 'default.css' 105 | 106 | # The name for this set of Sphinx documents. If None, it defaults to 107 | # " v documentation". 108 | #html_title = None 109 | 110 | # A shorter title for the navigation bar. Default is the same as html_title. 111 | #html_short_title = None 112 | 113 | # The name of an image file (relative to this directory) to place at the top 114 | # of the sidebar. 115 | #html_logo = None 116 | 117 | # The name of an image file (within the static path) to use as favicon of the 118 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 119 | # pixels large. 120 | #html_favicon = None 121 | 122 | # Add any paths that contain custom static files (such as style sheets) here, 123 | # relative to this directory. They are copied after the builtin static files, 124 | # so a file named "default.css" will overwrite the builtin "default.css". 125 | html_static_path = ['.static'] 126 | 127 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 128 | # using the given strftime format. 129 | #html_last_updated_fmt = '%b %d, %Y' 130 | 131 | # If true, SmartyPants will be used to convert quotes and dashes to 132 | # typographically correct entities. 133 | #html_use_smartypants = True 134 | 135 | # Custom sidebar templates, maps document names to template names. 136 | #html_sidebars = {} 137 | 138 | # Additional templates that should be rendered to pages, maps page names to 139 | # template names. 140 | #html_additional_pages = {} 141 | 142 | # If false, no module index is generated. 143 | #html_use_modindex = True 144 | 145 | # If false, no index is generated. 146 | #html_use_index = True 147 | 148 | # If true, the index is split into individual pages for each letter. 149 | #html_split_index = False 150 | 151 | # If true, the reST sources are included in the HTML build as _sources/. 152 | #html_copy_source = True 153 | 154 | # If true, an OpenSearch description file will be output, and all pages will 155 | # contain a tag referring to it. The value of this option must be the 156 | # base URL from which the finished HTML is served. 157 | #html_use_opensearch = '' 158 | 159 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 160 | #html_file_suffix = '' 161 | 162 | # Output file base name for HTML help builder. 163 | htmlhelp_basename = 'PythonIntelhexLibrary' 164 | 165 | 166 | # Options for LaTeX output 167 | # ------------------------ 168 | 169 | # The paper size ('letter' or 'a4'). 170 | #latex_paper_size = 'letter' 171 | 172 | # The font size ('10pt', '11pt' or '12pt'). 173 | #latex_font_size = '10pt' 174 | 175 | # Grouping the document tree into LaTeX files. List of tuples 176 | # (source start file, target name, title, author, document class [howto/manual]). 177 | latex_documents = [ 178 | ('index', 'IntelHex.tex', 'Python IntelHex library Documentation', 179 | 'Alexander Belchenko', 'manual'), 180 | ] 181 | 182 | # The name of an image file (relative to this directory) to place at the top of 183 | # the title page. 184 | #latex_logo = None 185 | 186 | # For "manual" documents, if this is true, then toplevel headings are parts, 187 | # not chapters. 188 | #latex_use_parts = False 189 | 190 | # Additional stuff for the LaTeX preamble. 191 | #latex_preamble = '' 192 | 193 | # Documents to append as an appendix to all manuals. 194 | #latex_appendices = [] 195 | 196 | # If false, no module index is generated. 197 | #latex_use_modindex = True 198 | -------------------------------------------------------------------------------- /docs/manual/index.txt: -------------------------------------------------------------------------------- 1 | .. IntelHex documentation master file, created by sphinx-quickstart on Thu Jan 01 06:25:34 2009. 2 | You can adapt this file completely to your liking, but it should at least 3 | contain the root `toctree` directive. 4 | 5 | Python IntelHex Library User Manual 6 | ----------------------------------- 7 | 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 3 12 | 13 | part1 14 | part2 15 | part3 16 | part4 17 | appendix-a 18 | 19 | Indices and tables 20 | ------------------ 21 | 22 | * :ref:`genindex` 23 | * :ref:`modindex` 24 | * :ref:`search` 25 | -------------------------------------------------------------------------------- /docs/manual/part1-1.txt: -------------------------------------------------------------------------------- 1 | About 2 | ***** 3 | The Intel HEX file format is widely used in microprocessors and microcontrollers 4 | area as the de facto standard for code representation for microelectronic devices programming. 5 | 6 | This work implements an **intelhex** Python library to read, write, 7 | create from scratch and manipulate data from HEX (also known as Intel HEX) 8 | file format. These operations are provided by ``IntelHex`` class. 9 | 10 | The distribution package also includes several convenience Python scripts 11 | to do basic tasks that utilize this library. The ``bin2hex.py`` script 12 | converts binary data to HEX, and the ``hex2bin.py`` works the other direction. 13 | ``hex2dump.py`` converts data from HEX to a hexdump which is useful for 14 | inspecting data, and ``hexmerge.py`` merges multiple HEX files into one. 15 | In addition you can try inspecting differences between two HEX files with 16 | ``hexdiff.py`` utility which uses dump output similar to ``hex2dump.py``. 17 | 18 | You can find IntelHex library on PyPI: 19 | 20 | https://pypi.python.org/pypi/IntelHex 21 | 22 | on GitHub: 23 | 24 | https://github.com/python-intelhex/intelhex 25 | 26 | 27 | Motivation 28 | ~~~~~~~~~~ 29 | This work was partially inspired by SRecord_ software at the moment 30 | when I stuck with its limitations and unintuitive behavior (for me personally). 31 | 32 | So I've made this library and related tools which give me 33 | full control over data and HEX file creation. 34 | Not the best reason to start yet another project. 35 | But, as you probably know, nothing is better than scratch our own itches, 36 | especially if you want to re-implement something 37 | in your favorite programming language. 38 | 39 | Over the years it turned out that my small python library was very useful 40 | to many people, and allowed them not only to create utilities to manipulate 41 | with HEX files, but also to create custom bootloaders for their devices. 42 | 43 | I started writing this library in 2005, and now more than 10 years later 44 | it's still alive and useful to other developers. 45 | That keeps me working on improving the code, 46 | even though I don't work on embedding systems for some time. 47 | 48 | If you find IntelHex library useful, please send me email 49 | and tell a bit about how you're using it and in which projects/areas. 50 | That will not only satisfy my curiosity but also will help me to keep working 51 | on this project. 52 | 53 | .. _SRecord: http://srecord.sourceforge.net/ 54 | -------------------------------------------------------------------------------- /docs/manual/part1-2.txt: -------------------------------------------------------------------------------- 1 | License 2 | ******* 3 | The code distributed under the BSD license. 4 | See LICENSE.txt in sources archive. 5 | -------------------------------------------------------------------------------- /docs/manual/part1-3.txt: -------------------------------------------------------------------------------- 1 | Installation 2 | ************ 3 | 4 | Note: some commands below have `sudo` as first word. 5 | It's required only on Linux or Mac OS X. 6 | Omit the prefix if you're on Windows. 7 | 8 | Installing with pip 9 | ~~~~~~~~~~~~~~~~~~~ 10 | If you just need IntelHex library installed as your system python library 11 | then it's better to use modern tool called ``pip`` 12 | (http://www.pip-installer.org/en/latest/) 13 | to install with the command: 14 | 15 | sudo pip install intelhex 16 | 17 | The latest versions of Python interpreter (like 2.7.9, or 3.4.x and later) 18 | have pip in the standard installer/distribution. 19 | 20 | The simplest way to check whether you have pip installed 21 | is to check command (for Python 2.5+): 22 | 23 | python -m pip list 24 | 25 | If this does not work, you can install pip by downloading single file 26 | from this page: https://pip.pypa.io/en/latest/installing.html#install-pip 27 | and run it as 28 | 29 | sudo python get-pip.py 30 | 31 | 32 | Download sources 33 | ~~~~~~~~~~~~~~~~ 34 | You can get archive with the latest released code, docs and other files 35 | from PyPI: 36 | 37 | https://pypi.python.org/pypi/IntelHex 38 | 39 | You can get the archive with the released code from corresponding section 40 | of GitHub project: 41 | 42 | https://github.com/python-intelhex/intelhex/releases 43 | 44 | or even unreleased bleeding edge code from GitHub page: 45 | 46 | https://github.com/python-intelhex/intelhex 47 | 48 | Use the corresponding menu item in the right-hand side bar on that page 49 | (e.g. "Download ZIP"). 50 | 51 | 52 | Get source code with git 53 | ~~~~~~~~~~~~~~~~~~~~~~~~ 54 | git clone https://github.com/python-intelhex/intelhex.git 55 | 56 | 57 | Install from sources 58 | ~~~~~~~~~~~~~~~~~~~~ 59 | IntelHex has got stadard setup.py installation script. 60 | Assuming Python is properly installed on your platform, 61 | installation should require just running of the following command 62 | from the root directory of the sources:: 63 | 64 | sudo python setup.py install 65 | 66 | This will install the intelhex package into your system's site-packages 67 | directory and place the helper scripts in your Python site-packages 68 | binaries directory. Once it is done, any other Python scripts or modules 69 | should be able to import the package using:: 70 | 71 | >>> from intelhex import IntelHex 72 | 73 | The scripts should be in your PATH so that they could be called from anywhere 74 | in the file system. 75 | 76 | See the Python distutils website for more information, or try typing, 77 | ``python setup.py --help`` from the root directory of the sources. 78 | 79 | Note for Windows users 80 | ~~~~~~~~~~~~~~~~~~~~~~ 81 | Please note that for historical reasons IntelHex library doesn't use 82 | setuptools for installation task, therefore we don't create exe-wrappers 83 | for helper scripts as hex2bin.py, bin2hex.py and other mentioned in this 84 | documentation (see section Convenience Scripts). 85 | 86 | You can find these scripts in your python Script directory 87 | (usually `C:\\PythonXY\\Scripts`). 88 | You need either to create batch file to run them, or use Python interpreter: 89 | 90 | python C:\\PythonXY\\Scripts\\hex2bin.py ... 91 | -------------------------------------------------------------------------------- /docs/manual/part1-4.txt: -------------------------------------------------------------------------------- 1 | Python 3 compatibility 2 | ********************** 3 | Intelhex library supports Python 2 (2.4-2.7) and Python 3 (3.2-3.6) without 4 | external libraries or 2to3 tool. Enjoy. 5 | 6 | I've successfully run unit tests of IntelHex against following versions of 7 | Python: 8 | 9 | 2.4.4, 2.5.4, 2.6.6 (32/64 bits), 2.7.9 (32/64 bits), 10 | 3.2.5 (32/64 bits), 3.3.5 (32/64 bits), 3.4.3 (32/64 bits), 11 | 3.5.0a3 (32/64 bits), 12 | and also PyPy 2.5.1 (which is Python 2.7.9) 13 | 14 | 15 | Which Python version should you use? 16 | ------------------------------------ 17 | If you don't really know which version of Python (2 or 3) you should use with 18 | IntelHex then please check following pre-requisites: 19 | 20 | 1) Are you already have some Python installed on your computer and that 21 | version is supported by IntelHex (see above)? 22 | 2) Should you use another third-party libraries? If so, check their 23 | requirements. 24 | 3) Python 2.7 is the safest choice so far, but if you have a chance 25 | then try latest stable Python 3 version. 26 | 4) Updated by January 2018 - Python 3 is highly recommended. 27 | -------------------------------------------------------------------------------- /docs/manual/part1.txt: -------------------------------------------------------------------------------- 1 | Introduction 2 | ------------ 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | part1-* 8 | -------------------------------------------------------------------------------- /docs/manual/part2-1.txt: -------------------------------------------------------------------------------- 1 | Initializing the class 2 | ********************** 3 | Example of typical initialization of ``IntelHex`` class:: 4 | 5 | >>> from intelhex import IntelHex 6 | >>> ih = IntelHex("foo.hex") 7 | 8 | In the second line we are creating an instance of the class. 9 | The constructor optionally takes data to initialize the class. 10 | This can be the name of the HEX file, a file-like object, a dictionary, 11 | or another instance of IntelHex. If specified, this source is automatically 12 | read and decoded. Because of the flexibility of file-like objects in Python, 13 | objects like sys.stdin can be used. 14 | 15 | If the source is another instance of IntelHex, the new object will become 16 | a copy of the source. Finally, a Python dictionary may be specified. 17 | This dictionary should have keys equal to memory locations and values equal 18 | to the data stored in those locations. See the docstrings for more details. 19 | -------------------------------------------------------------------------------- /docs/manual/part2-2.txt: -------------------------------------------------------------------------------- 1 | Reading data 2 | ************ 3 | Once created, an IntelHex object can be loaded with data. 4 | This is only necessary if "source" was unspecified in the constructor. 5 | You can also load data several times (but if addresses in those files overlap 6 | you get exception ``AddressOverlapError``). This error is only raised when 7 | reading from hex files. When reading from other formats, without explicitly 8 | calling ``merge``, the data will be overwritten. E.g.:: 9 | 10 | >>> from intelhex import IntelHex 11 | >>> ih = IntelHex() # create empty object 12 | >>> ih.loadhex('foo.hex') # load from hex 13 | >>> ih.loadfile('bar.hex',format='hex') # also load from hex 14 | >>> ih.fromfile('bar.hex',format='hex') # also load from hex 15 | 16 | **NOTE**: using ``IntelHex.fromfile`` is recommended way. 17 | 18 | All of the above examples will read from HEX files. 19 | IntelHex also supports reading straight binary files. For example:: 20 | 21 | >>> from intelhex import IntelHex 22 | >>> ih = IntelHex() # create empty object 23 | >>> ih.loadbin('foo.bin') # load from bin 24 | >>> ih.fromfile('bar.bin',format='bin') # also load from bin 25 | >>> ih.loadbin('baz.bin',offset=0x1000) # load binary data and place them 26 | >>> # starting with specified offset 27 | 28 | Finally, data can be loaded from an appropriate Python dictionary. 29 | This will permit you to store the data in an IntelHex object 30 | to a builtin dictionary and restore the object at a later time. 31 | For example:: 32 | 33 | >>> from intelhex import IntelHex 34 | >>> ih = IntelHex('foo.hex') # create empty object 35 | >>> pydict = ih.todict() # dump contents to pydict 36 | 37 | ...do something with the dictionary... 38 | 39 | :: 40 | 41 | >>> newIH = IntelHex(pydict) # recreate object with dict 42 | >>> another = IntelHex() # make a blank instance 43 | >>> another.fromdict(pydict) # now another is the same as newIH 44 | -------------------------------------------------------------------------------- /docs/manual/part2-3.txt: -------------------------------------------------------------------------------- 1 | Basic data inspection 2 | ********************* 3 | You can get or modify some data by address in the usual way: 4 | via Python indexing operations:: 5 | 6 | >>> print ih[0] # read data from address 0 7 | 8 | When you need to work with 16-bit data stored in 8-bit Intel HEX files 9 | you need to use class ``IntelHex16bit``. This class is derived from IntelHex 10 | and has all its methods. Some of methods have been modified to implement 11 | 16-bit behaviour. 12 | 13 | **NOTE:** ``IntelHex16bit`` class despite its name **can't handle** real HEX16 14 | files. Initially ``IntelHex16bit`` has been created as helper class to work with 15 | HEX files for Microchip's PIC16 family firmware. It may or may not work for 16 | your purpose. 17 | 18 | This class assumes the data is in Little Endian byte order. 19 | The data can be accessed exactly like above, except that data returned will be 20 | 16 bits, and the addresses should be word addresses. 21 | 22 | Another useful inspection tool is the dump command. This will output 23 | the entire contents of the hex file to stdout or to a specified file object 24 | like so:: 25 | 26 | >>> ih.dump() # dump contents of ih to stdout in tabular hexdump format 27 | 28 | >>> f = open('hexdump.txt', 'w') # open file for writing 29 | >>> ih.dump(f) # dump to file object 30 | >>> f.close() # close file 31 | -------------------------------------------------------------------------------- /docs/manual/part2-4.txt: -------------------------------------------------------------------------------- 1 | More data inspection 2 | ******************** 3 | IntelHex provides some metadata about the hex file it contains. 4 | To obtain address limits use methods ``.minaddr()`` and ``.maxaddr()``. 5 | These are computed based on the lowest and highest used memory spaces 6 | respectively. 7 | 8 | Some linkers write to produced HEX file information about start address 9 | (either record 03 or 05). IntelHex is able correctly read such records 10 | and store information internally in ``start_addr`` attribute that itself 11 | is either ``None`` or a dictionary with the address value(s). 12 | 13 | When input HEX file contains record type 03 (Start Segment Address Record), 14 | ``start_addr`` takes value:: 15 | 16 | {'CS': XXX, 'IP': YYY} 17 | 18 | Here: 19 | 20 | * ``XXX`` is value of CS register 21 | * ``YYY`` is value of IP register 22 | 23 | To obtain or change ``CS`` or ``IP`` value you need to use their names as keys 24 | for ``start_addr`` dictionary:: 25 | 26 | >>> ih = IntelHex('file_with_03.hex') 27 | >>> print ih.start_addr['CS'] 28 | >>> print ih.start_addr['IP'] 29 | 30 | When input HEX file contains record type 05 (Start Linear Address Record), 31 | ``start_addr`` takes value:: 32 | 33 | {'EIP': ZZZ} 34 | 35 | Here ``ZZZ`` is value of EIP register. 36 | 37 | Example:: 38 | 39 | >>> ih = IntelHex('file_with_05.hex') 40 | >>> print ih.start_addr['EIP'] 41 | 42 | You can manually set required start address:: 43 | 44 | >>> ih.start_addr = {'CS': 0x1234, 'IP': 0x5678} 45 | >>> ih.start_addr = {'EIP': 0x12345678} 46 | 47 | To delete start address info give value ``None`` or empty dictionary:: 48 | 49 | >>> ih.start_addr = None 50 | >>> ih.start_addr = {} 51 | 52 | When you write data to HEX file you can disable writing start address 53 | with additional argument ``write_start_addr``:: 54 | 55 | >>> ih.write_hex_file('out.hex') # by default writing start address 56 | >>> ih.write_hex_file('out.hex', True) # as above 57 | >>> ih.write_hex_file('out.hex', False) # don't write start address 58 | 59 | When ``start_addr`` is ``None`` or an empty dictionary nothing will be written 60 | regardless of ``write_start_addr`` argument value. 61 | 62 | For more information about start address, please see the Intel Hex file format 63 | specification. 64 | 65 | Because Intel Hex files do not specify every location in memory, 66 | it is necessary to have a padding byte defined. Whenever a read is attempted 67 | from an address that is unspecified, the padding byte is returned. 68 | This default data is set via attribute ``.padding`` of class instance. 69 | This defaults to '0xFF', but it can be changed by the user like so:: 70 | 71 | >>> print ih[0] # prints 0xFF because this location is blank 72 | >>> ih.padding = 0x00 # change padding byte 73 | >>> print ih[0] # prints 0x00 because this location is blank 74 | 75 | Summarizing the data chunks 76 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 77 | One of the more useful properties of HEX files is that they can specify data 78 | in discontinuous segments. There are two main methods to summarize which 79 | data addresses are occupied:: 80 | 81 | >>> ih.addresses() 82 | >>> ih.segments() 83 | 84 | The first will return a list of occupied data addresses in sorted order. The 85 | second will return a list of 2-tuples objects, in sorted order, representing 86 | start and stop addresses of contiguous segment chunks of occupied data. 87 | Those 2-tuples are suitable to be used as ``start`` and ``stop`` 88 | arguments of standard ``range`` function. 89 | -------------------------------------------------------------------------------- /docs/manual/part2-5.txt: -------------------------------------------------------------------------------- 1 | Writing out data 2 | **************** 3 | Data contained in IntelHex can be written out in a few different formats, 4 | including HEX, bin, or python dictionaries. 5 | 6 | You can write out HEX data contained in object by method ``.write_hex_file(f)``. 7 | Parameter ``f`` should be filename or file-like object. 8 | Note that this can include builtins like sys.stdout. 9 | Also you can use the universal tofile. 10 | 11 | To convert data of IntelHex object to HEX8 file format without actually saving it 12 | to disk you can use the builtin StringIO file-like object, e.g.:: 13 | 14 | >>> from cStringIO import StringIO 15 | >>> from intelhex import IntelHex 16 | >>> ih = IntelHex() 17 | >>> ih[0] = 0x55 18 | >>> sio = StringIO() 19 | >>> ih.write_hex_file(sio) 20 | >>> hexstr = sio.getvalue() 21 | >>> sio.close() 22 | 23 | Variable ``hexstr`` will contain a string with the content of a HEX8 file. 24 | 25 | You can customize hex file output with following optional arguments 26 | to ``write_hex_file`` call: 27 | 28 | * ``write_start_addr`` - you can disable start address record in new hex file; 29 | * ``eolstyle`` - you can force ``CRLF`` line endings in new hex file. 30 | * ``byte_count`` - you can control how many bytes should be written to each 31 | data record. 32 | 33 | 34 | Data converters 35 | ~~~~~~~~~~~~~~~ 36 | 37 | To write data as a hex file with default settings you also can use 38 | universal method ``tofile``:: 39 | 40 | # the code below is the same as "ih.write_hex_file(sio)" 41 | >>> ih.tofile(sio, format='hex') 42 | 43 | Class IntelHex has several methods for converting data of IntelHex objects 44 | into binary form: 45 | 46 | * ``tobinarray`` (returns array of unsigned char bytes); 47 | * ``tobinstr`` (returns string of bytes); 48 | * ``tobinfile`` (convert content to binary form and write to file). 49 | 50 | Example:: 51 | 52 | >>> from intelhex import IntelHex 53 | >>> ih = IntelHex("foo.hex") 54 | >>> ih.tobinfile("foo.bin") 55 | 56 | Also you can use universal method ``tofile`` to write data as binary file:: 57 | 58 | >>> ih.tofile("foo.bin", format='bin') 59 | 60 | 61 | Writing data in chunks 62 | ~~~~~~~~~~~~~~~~~~~~~~ 63 | If you need to get binary data from IntelHex as series of chunks then you can 64 | pass to ``tobinarray``/``tobinstr`` methods either start/end addresses 65 | or start address and required size of the chunk. This could be useful if 66 | you're creating Eeprom/Flash IC programmer or bootloader. 67 | :: 68 | 69 | EEPROM_SIZE = 8192 # 8K bytes 70 | BLOCK_SIZE = 128 # 128 bytes 71 | for addr in range(0, EEPROM_SIZE, BLOCK_SIZE): 72 | eeprom.i2c_write(addr, ih.tobinarray(start=addr, size=BLOCK_SIZE)) 73 | -------------------------------------------------------------------------------- /docs/manual/part2-6.txt: -------------------------------------------------------------------------------- 1 | Merging two hex files 2 | ********************* 3 | IntelHex supports merging two different hex files into one. 4 | This is done by initializing one IntelHex object with data 5 | and calling its merge method:: 6 | 7 | >>> original = IntelHex("foo.hex") 8 | >>> new = IntelHex("bar.hex") 9 | >>> original.merge(new, overlap='replace') 10 | 11 | Now original will contain foo.hex merged with bar.hex. 12 | The overlap parameter specifies what should be done when memory 13 | locations in the original object overlap with locations in the new object. 14 | It can take three options: 15 | 16 | * ``error`` - stop and raise an exception (default) 17 | * ``ignore`` - keep data from the original that contains data at overlapped address 18 | * ``replace`` - use data from the new object that contains data at overlapped address 19 | 20 | You can merge only part of other hex file by using slice index notation:: 21 | 22 | >>> original = IntelHex("foo.hex") 23 | >>> new = IntelHex("bar.hex") 24 | >>> original.merge(new[0x0F:0x3F]) 25 | -------------------------------------------------------------------------------- /docs/manual/part2-7.txt: -------------------------------------------------------------------------------- 1 | Creating Intel Hex files from scratch 2 | ************************************* 3 | Some facilities are provided for synthesizing Intel Hex files from scratch. 4 | These can also be used to modify a hex file in place. 5 | Just as you can use indexed reads to retrieve data, you can use indexed writes 6 | to modify the file, e.g.:: 7 | 8 | >>> ih[1] = 0x55 # modify data at address 1 9 | 10 | A common usage would be to read a hex file with IntelHex, use the above syntax 11 | to modify it, and then write out the modified file. The above command can be used 12 | on an empty IntelHex object to synthesize a hex file from scratch. 13 | 14 | Another important feature helps work with C strings via ``putsz``/``getsz``, e.g.:: 15 | 16 | >>> ih.putsz(0x100,"A string") 17 | 18 | This places "A string" followed by a terminating NULL character in address 0x100. 19 | The ``getsz`` method similarly retrieves a null terminated string from a specified address like so:: 20 | 21 | >>> ih.getsz(0x100) 22 | 23 | This should retrieve the "A string" we stored earlier. 24 | 25 | Additionally, ``puts``/``gets`` can be used to retrieve strings of specific length 26 | from the hex file like so:: 27 | 28 | >>> ih.puts(0x100,"data") 29 | >>> ih.gets(0x100,4) 30 | 31 | The second command should retrieve the characters 'd','a','t','a'. 32 | These methods do not use terminating NULLs, so the data need not be interpreted as a string. 33 | One usage of these commands comes from the Python ``struct`` module. 34 | This module allows the programmer to specify a C struct, and it will allow conversion 35 | between the variables and a packed string representation for use with 36 | ``puts``/``gets``. 37 | For example, suppose we need to deal with a struct containing a char, a short, and a float:: 38 | 39 | >>> import struct 40 | >>> formatstring = 'chf' # see Python docs for full list of valid struct formats 41 | >>> ih.puts(0x10, struct.pack(formatstring,'a',24,18.6)) # put data in hex file 42 | >>> (mychar,myshort,myfloat) = struct.unpack(formatstring, ih.gets(0x10,7) 43 | 44 | Now ``mychar``, ``myshort``, and ``myfloat`` should contain the original data 45 | (assuming ``sizeof(float) = 4`` on this platform, otherwise the size may be 46 | wrong). 47 | -------------------------------------------------------------------------------- /docs/manual/part2-8.txt: -------------------------------------------------------------------------------- 1 | Handling errors 2 | *************** 3 | Many of the methods in IntelHex throw Python exceptions during error conditions. 4 | These can be caught and handled using try...except blocks like so:: 5 | 6 | >>> try: 7 | ... mystring = ih.gets(0x20,20) 8 | >>> except intelhex.NotEnoughDataError: 9 | ... print "There is not enough data at that location" 10 | 11 | See the API docs for information about errors raised by IntelHex. 12 | They are all subclasses of IntelHexError, so the except block above could be used 13 | to catch all of them. If your application has a way to gracefully handle these 14 | exceptions, they should be caught. Otherwise, Python will exit with a descriptive 15 | error message about the uncaught exception. 16 | 17 | See `Appendix A `_ for error classes hierarchy. 18 | -------------------------------------------------------------------------------- /docs/manual/part2.txt: -------------------------------------------------------------------------------- 1 | Basic API and usage 2 | ------------------- 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | part2-* 8 | -------------------------------------------------------------------------------- /docs/manual/part3-1.txt: -------------------------------------------------------------------------------- 1 | Script ``hex2bin.py`` 2 | ********************* 3 | You can use ``hex2bin.py`` as handy hex-to-bin converter. 4 | This script is just a frontend for function ``hex2bin`` from intelhex package. 5 | :: 6 | 7 | Usage: 8 | python hex2bin.py [options] INFILE [OUTFILE] 9 | 10 | Arguments: 11 | INFILE name of hex file for processing. 12 | OUTFILE name of output file. If omitted then output 13 | will be writing to stdout. 14 | 15 | 16 | Options: 17 | -h, --help this help message. 18 | -p, --pad=FF pad byte for empty spaces (hex value). 19 | -r, --range=START:END specify address range for writing output 20 | (hex value). 21 | Range can be in form 'START:' or ':END'. 22 | -l, --length=NNNN, 23 | -s, --size=NNNN size of output (decimal value). 24 | 25 | Per example, converting content of foo.hex to foo.bin addresses from 0 to FF:: 26 | 27 | $ python hex2bin.py -r 0000:00FF foo.hex 28 | 29 | Or (equivalent):: 30 | 31 | $ python hex2bin.py -r 0000: -s 256 foo.hex 32 | -------------------------------------------------------------------------------- /docs/manual/part3-2.txt: -------------------------------------------------------------------------------- 1 | Script ``bin2hex.py`` 2 | ********************************* 3 | You can use bin2hex.py as simple bin-to-hex convertor. This script is 4 | just a frontend for function ``bin2hex`` from intelhex package. 5 | :: 6 | 7 | Usage: 8 | python bin2hex.py [options] INFILE [OUTFILE] 9 | 10 | Arguments: 11 | INFILE name of bin file for processing. 12 | Use '-' for reading from stdin. 13 | 14 | OUTFILE name of output file. If omitted then output 15 | will be writing to stdout. 16 | 17 | Options: 18 | -h, --help this help message. 19 | --offset=N offset for loading bin file (default: 0). 20 | -------------------------------------------------------------------------------- /docs/manual/part3-3.txt: -------------------------------------------------------------------------------- 1 | Script ``hex2dump.py`` 2 | *********************** 3 | This is a script to dump a hex file to a hexdump format. It is a frontend for 4 | ``dump`` function in ``IntelHex`` class. 5 | :: 6 | 7 | Usage: 8 | python hex2dump.py [options] HEXFILE 9 | 10 | Options: 11 | -h, --help this help message. 12 | -r, --range=START:END specify address range for dumping 13 | (ascii hex value). 14 | Range can be in form 'START:' or ':END'. 15 | 16 | Arguments: 17 | HEXFILE name of hex file for processing (use '-' to read 18 | from stdin) 19 | -------------------------------------------------------------------------------- /docs/manual/part3-4.txt: -------------------------------------------------------------------------------- 1 | Script ``hexmerge.py`` 2 | ********************************** 3 | This is a script to merge two different hex files. It is a frontend for the 4 | ``merge`` function in ``IntelHex`` class. 5 | :: 6 | 7 | Usage: 8 | python hexmerge.py [options] FILES... 9 | 10 | Options: 11 | -h, --help this help message. 12 | -o, --output=FILENAME output file name (emit output to stdout 13 | if option is not specified) 14 | -r, --range=START:END specify address range for output 15 | (ascii hex value). 16 | Range can be in form 'START:' or ':END'. 17 | --no-start-addr Don't write start addr to output file. 18 | --overlap=METHOD What to do when data in files overlapped. 19 | Supported variants: 20 | * error -- stop and show error message (default) 21 | * ignore -- keep data from first file that 22 | contains data at overlapped address 23 | * replace -- use data from last file that 24 | contains data at overlapped address 25 | 26 | Arguments: 27 | FILES list of hex files for merging 28 | (use '-' to read content from stdin) 29 | 30 | You can specify address range for each file in the form: 31 | 32 | filename:START:END 33 | 34 | See description of range option above. 35 | 36 | You can omit START or END, so supported variants are: 37 | 38 | filename:START: read filename and use data starting from START addr 39 | filename::END read filename and use data till END addr 40 | 41 | Use entire file content: 42 | 43 | filename 44 | or 45 | filename:: 46 | -------------------------------------------------------------------------------- /docs/manual/part3-5.txt: -------------------------------------------------------------------------------- 1 | Script ``hexdiff.py`` 2 | ********************* 3 | This is a script to diff context of two hex files. 4 | 5 | To create human-readable diff this utility converts 6 | both hex files to hex dumps first, 7 | and then utility compares those hex dumps and 8 | produces unified diff output for changes. 9 | 10 | :: 11 | 12 | hexdiff: diff dumps of 2 hex files. 13 | 14 | Usage: 15 | python hexdiff.py [options] FILE1 FILE2 16 | 17 | Options: 18 | -h, --help this help message. 19 | -v, --version version info. 20 | -------------------------------------------------------------------------------- /docs/manual/part3-6.txt: -------------------------------------------------------------------------------- 1 | Script ``hexinfo.py`` 2 | ********************* 3 | This is a script to summarize a hex file's contents. 4 | 5 | This utility creates a YAML-formatted, human-readable 6 | summary of a set of HEX files. It includes the file name, 7 | execution start address (if any), and the address ranges 8 | covered by the data (if any). 9 | 10 | :: 11 | 12 | hexinfo: summarize a hex file's contents. 13 | 14 | Usage: 15 | python hexinfo.py [options] FILE [ FILE ... ] 16 | 17 | Options: 18 | -h, --help this help message. 19 | -v, --version version info. 20 | -------------------------------------------------------------------------------- /docs/manual/part3.txt: -------------------------------------------------------------------------------- 1 | Convenience Scripts 2 | ------------------- 3 | 4 | When IntelHex is installed and added to the system path, 5 | some scripts are available for usage. 6 | Each one is meant to be operated from the command line. 7 | They provide help if called incorrectly. 8 | 9 | .. toctree:: 10 | :glob: 11 | 12 | part3-* 13 | -------------------------------------------------------------------------------- /docs/manual/part4.txt: -------------------------------------------------------------------------------- 1 | Embedding into other projects 2 | ***************************** 3 | IntelHex should be easy to embed in other projects. 4 | The directory ``intelhex`` containing ``__init__.py`` can be directly placed 5 | in a depending project and used directly. From that project the same import 6 | statements described above can be used to make the library work. 7 | From other projects the import statement would change to:: 8 | 9 | >>> from myproject.intelhex import IntelHex 10 | 11 | Alternatively, the IntelHex package can be installed into the site-packages 12 | directory and used as a system package. 13 | 14 | In either case, IntelHex is distributed with a BSD-style license. 15 | This permits you to use it in any way you see fit, provided that the package 16 | is appropriately credited. 17 | 18 | If you're using IntelHex library in your open-source project, or your company 19 | created freely available set of tools, utilities or sdk based on IntelHex 20 | library - please, send me email (to alexander belchenko at gmail com) 21 | and tell something about your project. 22 | I'd like to add name of your project/company to page "Who Uses IntelHex". 23 | -------------------------------------------------------------------------------- /intelhex/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005-2018, Alexander Belchenko 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, 5 | # with or without modification, are permitted provided 6 | # that the following conditions are met: 7 | # 8 | # * Redistributions of source code must retain 9 | # the above copyright notice, this list of conditions 10 | # and the following disclaimer. 11 | # * Redistributions in binary form must reproduce 12 | # the above copyright notice, this list of conditions 13 | # and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # * Neither the name of the author nor the names 16 | # of its contributors may be used to endorse 17 | # or promote products derived from this software 18 | # without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 22 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 23 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 26 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | '''Intel HEX format manipulation library.''' 35 | 36 | __docformat__ = "javadoc" 37 | 38 | from array import array 39 | from binascii import hexlify, unhexlify 40 | from bisect import bisect_right 41 | import os 42 | import sys 43 | 44 | from intelhex.compat import ( 45 | IntTypes, 46 | StrType, 47 | StringIO, 48 | array_tobytes, 49 | asbytes, 50 | asstr, 51 | dict_items_g, 52 | dict_keys, 53 | dict_keys_g, 54 | range_g, 55 | range_l, 56 | ) 57 | 58 | from intelhex.getsizeof import total_size 59 | 60 | 61 | class _DeprecatedParam(object): 62 | pass 63 | 64 | _DEPRECATED = _DeprecatedParam() 65 | 66 | 67 | class IntelHex(object): 68 | ''' Intel HEX file reader. ''' 69 | 70 | def __init__(self, source=None): 71 | ''' Constructor. If source specified, object will be initialized 72 | with the contents of source. Otherwise the object will be empty. 73 | 74 | @param source source for initialization 75 | (file name of HEX file, file object, addr dict or 76 | other IntelHex object) 77 | ''' 78 | # public members 79 | self.padding = 0x0FF 80 | # Start Address 81 | self.start_addr = None 82 | 83 | # private members 84 | self._buf = {} 85 | self._offset = 0 86 | 87 | if source is not None: 88 | if isinstance(source, StrType) or getattr(source, "read", None): 89 | # load hex file 90 | self.loadhex(source) 91 | elif isinstance(source, dict): 92 | self.fromdict(source) 93 | elif isinstance(source, IntelHex): 94 | self.padding = source.padding 95 | if source.start_addr: 96 | self.start_addr = source.start_addr.copy() 97 | self._buf = source._buf.copy() 98 | else: 99 | raise ValueError("source: bad initializer type") 100 | 101 | def _decode_record(self, s, line=0): 102 | '''Decode one record of HEX file. 103 | 104 | @param s line with HEX record. 105 | @param line line number (for error messages). 106 | 107 | @raise EndOfFile if EOF record encountered. 108 | ''' 109 | s = s.rstrip('\r\n') 110 | if not s: 111 | return # empty line 112 | 113 | if s[0] == ':': 114 | try: 115 | bin = array('B', unhexlify(asbytes(s[1:]))) 116 | except (TypeError, ValueError): 117 | # this might be raised by unhexlify when odd hexascii digits 118 | raise HexRecordError(line=line) 119 | length = len(bin) 120 | if length < 5: 121 | raise HexRecordError(line=line) 122 | else: 123 | raise HexRecordError(line=line) 124 | 125 | record_length = bin[0] 126 | if length != (5 + record_length): 127 | raise RecordLengthError(line=line) 128 | 129 | addr = bin[1]*256 + bin[2] 130 | 131 | record_type = bin[3] 132 | if not (0 <= record_type <= 5): 133 | raise RecordTypeError(line=line) 134 | 135 | crc = sum(bin) 136 | crc &= 0x0FF 137 | if crc != 0: 138 | raise RecordChecksumError(line=line) 139 | 140 | if record_type == 0: 141 | # data record 142 | addr += self._offset 143 | for i in range_g(4, 4+record_length): 144 | if not self._buf.get(addr, None) is None: 145 | raise AddressOverlapError(address=addr, line=line) 146 | self._buf[addr] = bin[i] 147 | addr += 1 # FIXME: addr should be wrapped 148 | # BUT after 02 record (at 64K boundary) 149 | # and after 04 record (at 4G boundary) 150 | 151 | elif record_type == 1: 152 | # end of file record 153 | if record_length != 0: 154 | raise EOFRecordError(line=line) 155 | raise _EndOfFile 156 | 157 | elif record_type == 2: 158 | # Extended 8086 Segment Record 159 | if record_length != 2 or addr != 0: 160 | raise ExtendedSegmentAddressRecordError(line=line) 161 | self._offset = (bin[4]*256 + bin[5]) * 16 162 | 163 | elif record_type == 4: 164 | # Extended Linear Address Record 165 | if record_length != 2 or addr != 0: 166 | raise ExtendedLinearAddressRecordError(line=line) 167 | self._offset = (bin[4]*256 + bin[5]) * 65536 168 | 169 | elif record_type == 3: 170 | # Start Segment Address Record 171 | if record_length != 4 or addr != 0: 172 | raise StartSegmentAddressRecordError(line=line) 173 | if self.start_addr: 174 | raise DuplicateStartAddressRecordError(line=line) 175 | self.start_addr = {'CS': bin[4]*256 + bin[5], 176 | 'IP': bin[6]*256 + bin[7], 177 | } 178 | 179 | elif record_type == 5: 180 | # Start Linear Address Record 181 | if record_length != 4 or addr != 0: 182 | raise StartLinearAddressRecordError(line=line) 183 | if self.start_addr: 184 | raise DuplicateStartAddressRecordError(line=line) 185 | self.start_addr = {'EIP': (bin[4]*16777216 + 186 | bin[5]*65536 + 187 | bin[6]*256 + 188 | bin[7]), 189 | } 190 | 191 | def loadhex(self, fobj): 192 | """Load hex file into internal buffer. This is not necessary 193 | if object was initialized with source set. This will overwrite 194 | addresses if object was already initialized. 195 | 196 | @param fobj file name or file-like object 197 | """ 198 | if getattr(fobj, "read", None) is None: 199 | fobj = open(fobj, "r") 200 | fclose = fobj.close 201 | else: 202 | fclose = None 203 | 204 | self._offset = 0 205 | line = 0 206 | 207 | try: 208 | decode = self._decode_record 209 | try: 210 | for s in fobj: 211 | line += 1 212 | decode(s, line) 213 | except _EndOfFile: 214 | pass 215 | finally: 216 | if fclose: 217 | fclose() 218 | 219 | def loadbin(self, fobj, offset=0): 220 | """Load bin file into internal buffer. Not needed if source set in 221 | constructor. This will overwrite addresses without warning 222 | if object was already initialized. 223 | 224 | @param fobj file name or file-like object 225 | @param offset starting address offset 226 | """ 227 | fread = getattr(fobj, "read", None) 228 | if fread is None: 229 | f = open(fobj, "rb") 230 | fread = f.read 231 | fclose = f.close 232 | else: 233 | fclose = None 234 | 235 | try: 236 | self.frombytes(array('B', asbytes(fread())), offset=offset) 237 | finally: 238 | if fclose: 239 | fclose() 240 | 241 | def loadfile(self, fobj, format): 242 | """Load data file into internal buffer. Preferred wrapper over 243 | loadbin or loadhex. 244 | 245 | @param fobj file name or file-like object 246 | @param format file format ("hex" or "bin") 247 | """ 248 | if format == "hex": 249 | self.loadhex(fobj) 250 | elif format == "bin": 251 | self.loadbin(fobj) 252 | else: 253 | raise ValueError('format should be either "hex" or "bin";' 254 | ' got %r instead' % format) 255 | 256 | # alias (to be consistent with method tofile) 257 | fromfile = loadfile 258 | 259 | def fromdict(self, dikt): 260 | """Load data from dictionary. Dictionary should contain int keys 261 | representing addresses. Values should be the data to be stored in 262 | those addresses in unsigned char form (i.e. not strings). 263 | The dictionary may contain the key, ``start_addr`` 264 | to indicate the starting address of the data as described in README. 265 | 266 | The contents of the dict will be merged with this object and will 267 | overwrite any conflicts. This function is not necessary if the 268 | object was initialized with source specified. 269 | """ 270 | s = dikt.copy() 271 | start_addr = s.get('start_addr') 272 | if start_addr is not None: 273 | del s['start_addr'] 274 | for k in dict_keys_g(s): 275 | if type(k) not in IntTypes or k < 0: 276 | raise ValueError('Source dictionary should have only int keys') 277 | self._buf.update(s) 278 | if start_addr is not None: 279 | self.start_addr = start_addr 280 | 281 | def frombytes(self, bytes, offset=0): 282 | """Load data from array or list of bytes. 283 | Similar to loadbin() method but works directly with iterable bytes. 284 | """ 285 | for b in bytes: 286 | self._buf[offset] = b 287 | offset += 1 288 | 289 | def _get_start_end(self, start=None, end=None, size=None): 290 | """Return default values for start and end if they are None. 291 | If this IntelHex object is empty then it's error to 292 | invoke this method with both start and end as None. 293 | """ 294 | if (start,end) == (None,None) and self._buf == {}: 295 | raise EmptyIntelHexError 296 | if size is not None: 297 | if None not in (start, end): 298 | raise ValueError("tobinarray: you can't use start,end and size" 299 | " arguments in the same time") 300 | if (start, end) == (None, None): 301 | start = self.minaddr() 302 | if start is not None: 303 | end = start + size - 1 304 | else: 305 | start = end - size + 1 306 | if start < 0: 307 | raise ValueError("tobinarray: invalid size (%d) " 308 | "for given end address (%d)" % (size,end)) 309 | else: 310 | if start is None: 311 | start = self.minaddr() 312 | if end is None: 313 | end = self.maxaddr() 314 | if start > end: 315 | start, end = end, start 316 | return start, end 317 | 318 | def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None): 319 | ''' Convert this object to binary form as array. If start and end 320 | unspecified, they will be inferred from the data. 321 | @param start start address of output bytes. 322 | @param end end address of output bytes (inclusive). 323 | @param pad [DEPRECATED PARAMETER, please use self.padding instead] 324 | fill empty spaces with this value 325 | (if pad is None then this method uses self.padding). 326 | @param size size of the block, used with start or end parameter. 327 | @return array of unsigned char data. 328 | ''' 329 | if not isinstance(pad, _DeprecatedParam): 330 | print ("IntelHex.tobinarray: 'pad' parameter is deprecated.") 331 | if pad is not None: 332 | print ("Please, use IntelHex.padding attribute instead.") 333 | else: 334 | print ("Please, don't pass it explicitly.") 335 | print ("Use syntax like this: ih.tobinarray(start=xxx, end=yyy, size=zzz)") 336 | else: 337 | pad = None 338 | return self._tobinarray_really(start, end, pad, size) 339 | 340 | def _tobinarray_really(self, start, end, pad, size): 341 | """Return binary array.""" 342 | if pad is None: 343 | pad = self.padding 344 | bin = array('B') 345 | if self._buf == {} and None in (start, end): 346 | return bin 347 | if size is not None and size <= 0: 348 | raise ValueError("tobinarray: wrong value for size") 349 | start, end = self._get_start_end(start, end, size) 350 | for i in range_g(start, end+1): 351 | bin.append(self._buf.get(i, pad)) 352 | return bin 353 | 354 | def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None): 355 | ''' Convert to binary form and return as binary string. 356 | @param start start address of output bytes. 357 | @param end end address of output bytes (inclusive). 358 | @param pad [DEPRECATED PARAMETER, please use self.padding instead] 359 | fill empty spaces with this value 360 | (if pad is None then this method uses self.padding). 361 | @param size size of the block, used with start or end parameter. 362 | @return bytes string of binary data. 363 | ''' 364 | if not isinstance(pad, _DeprecatedParam): 365 | print ("IntelHex.tobinstr: 'pad' parameter is deprecated.") 366 | if pad is not None: 367 | print ("Please, use IntelHex.padding attribute instead.") 368 | else: 369 | print ("Please, don't pass it explicitly.") 370 | print ("Use syntax like this: ih.tobinstr(start=xxx, end=yyy, size=zzz)") 371 | else: 372 | pad = None 373 | return self._tobinstr_really(start, end, pad, size) 374 | 375 | def _tobinstr_really(self, start, end, pad, size): 376 | return array_tobytes(self._tobinarray_really(start, end, pad, size)) 377 | 378 | def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None): 379 | '''Convert to binary and write to file. 380 | 381 | @param fobj file name or file object for writing output bytes. 382 | @param start start address of output bytes. 383 | @param end end address of output bytes (inclusive). 384 | @param pad [DEPRECATED PARAMETER, please use self.padding instead] 385 | fill empty spaces with this value 386 | (if pad is None then this method uses self.padding). 387 | @param size size of the block, used with start or end parameter. 388 | ''' 389 | if not isinstance(pad, _DeprecatedParam): 390 | print ("IntelHex.tobinfile: 'pad' parameter is deprecated.") 391 | if pad is not None: 392 | print ("Please, use IntelHex.padding attribute instead.") 393 | else: 394 | print ("Please, don't pass it explicitly.") 395 | print ("Use syntax like this: ih.tobinfile(start=xxx, end=yyy, size=zzz)") 396 | else: 397 | pad = None 398 | if getattr(fobj, "write", None) is None: 399 | fobj = open(fobj, "wb") 400 | close_fd = True 401 | else: 402 | close_fd = False 403 | 404 | fobj.write(self._tobinstr_really(start, end, pad, size)) 405 | 406 | if close_fd: 407 | fobj.close() 408 | 409 | def todict(self): 410 | '''Convert to python dictionary. 411 | 412 | @return dict suitable for initializing another IntelHex object. 413 | ''' 414 | r = {} 415 | r.update(self._buf) 416 | if self.start_addr: 417 | r['start_addr'] = self.start_addr 418 | return r 419 | 420 | def addresses(self): 421 | '''Returns all used addresses in sorted order. 422 | @return list of occupied data addresses in sorted order. 423 | ''' 424 | aa = dict_keys(self._buf) 425 | aa.sort() 426 | return aa 427 | 428 | def minaddr(self): 429 | '''Get minimal address of HEX content. 430 | @return minimal address or None if no data 431 | ''' 432 | aa = dict_keys(self._buf) 433 | if aa == []: 434 | return None 435 | else: 436 | return min(aa) 437 | 438 | def maxaddr(self): 439 | '''Get maximal address of HEX content. 440 | @return maximal address or None if no data 441 | ''' 442 | aa = dict_keys(self._buf) 443 | if aa == []: 444 | return None 445 | else: 446 | return max(aa) 447 | 448 | def __contains__(self, addr): 449 | ''' Returns a boolean if the address is present. 450 | @param addr address of byte. 451 | @return bool if address exists in HEX file. 452 | ''' 453 | if addr < 0: 454 | raise TypeError('Address should be >= 0.') 455 | return addr in self._buf 456 | 457 | def __getitem__(self, addr): 458 | ''' Get requested byte from address. 459 | @param addr address of byte. 460 | @return byte if address exists in HEX file, or self.padding 461 | if no data found. 462 | ''' 463 | t = type(addr) 464 | if t in IntTypes: 465 | if addr < 0: 466 | raise TypeError('Address should be >= 0.') 467 | addresses = dict_keys(self._buf) 468 | if not addresses or addr > max(addresses): 469 | raise IndexError 470 | return self._buf.get(addr, self.padding) 471 | elif t == slice: 472 | addresses = dict_keys(self._buf) 473 | ih = IntelHex() 474 | if addresses: 475 | addresses.sort() 476 | start = addr.start if addr.start is not None else addresses[0] 477 | stop = addr.stop if addr.stop is not None else addresses[-1] + 1 478 | step = addr.step or 1 479 | for i in range_g(start, stop, step): 480 | x = self._buf.get(i) 481 | if x is not None: 482 | ih[i] = x 483 | return ih 484 | else: 485 | raise TypeError('Address has unsupported type: %s' % t) 486 | 487 | def __setitem__(self, addr, byte): 488 | """Set byte at address.""" 489 | t = type(addr) 490 | if t in IntTypes: 491 | if addr < 0: 492 | raise TypeError('Address should be >= 0.') 493 | self._buf[addr] = byte 494 | elif t == slice: 495 | if not isinstance(byte, (list, tuple)): 496 | raise ValueError('Slice operation expects sequence of bytes') 497 | start = addr.start 498 | stop = addr.stop 499 | step = addr.step or 1 500 | if None not in (start, stop): 501 | ra = range_l(start, stop, step) 502 | if len(ra) != len(byte): 503 | raise ValueError('Length of bytes sequence does not match ' 504 | 'address range') 505 | elif (start, stop) == (None, None): 506 | raise TypeError('Unsupported address range') 507 | elif start is None: 508 | start = stop - len(byte) 509 | elif stop is None: 510 | stop = start + len(byte) 511 | if start < 0: 512 | raise TypeError('start address cannot be negative') 513 | if stop < 0: 514 | raise TypeError('stop address cannot be negative') 515 | j = 0 516 | for i in range_g(start, stop, step): 517 | self._buf[i] = byte[j] 518 | j += 1 519 | else: 520 | raise TypeError('Address has unsupported type: %s' % t) 521 | 522 | def __delitem__(self, addr): 523 | """Delete byte at address.""" 524 | t = type(addr) 525 | if t in IntTypes: 526 | if addr < 0: 527 | raise TypeError('Address should be >= 0.') 528 | del self._buf[addr] 529 | elif t == slice: 530 | addresses = dict_keys(self._buf) 531 | if addresses: 532 | addresses.sort() 533 | start = addr.start or addresses[0] 534 | stop = addr.stop or (addresses[-1]+1) 535 | step = addr.step or 1 536 | for i in range_g(start, stop, step): 537 | x = self._buf.get(i) 538 | if x is not None: 539 | del self._buf[i] 540 | else: 541 | raise TypeError('Address has unsupported type: %s' % t) 542 | 543 | def __len__(self): 544 | """Return count of bytes with real values.""" 545 | return len(dict_keys(self._buf)) 546 | 547 | def _get_eol_textfile(eolstyle, platform): 548 | if eolstyle == 'native': 549 | return '\n' 550 | elif eolstyle == 'CRLF': 551 | if platform != 'win32': 552 | return '\r\n' 553 | else: 554 | return '\n' 555 | else: 556 | raise ValueError("wrong eolstyle %s" % repr(eolstyle)) 557 | _get_eol_textfile = staticmethod(_get_eol_textfile) 558 | 559 | def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count=16): 560 | """Write data to file f in HEX format. 561 | 562 | @param f filename or file-like object for writing 563 | @param write_start_addr enable or disable writing start address 564 | record to file (enabled by default). 565 | If there is no start address in obj, nothing 566 | will be written regardless of this setting. 567 | @param eolstyle can be used to force CRLF line-endings 568 | for output file on different platforms. 569 | Supported eol styles: 'native', 'CRLF'. 570 | @param byte_count number of bytes in the data field 571 | """ 572 | if byte_count > 255 or byte_count < 1: 573 | raise ValueError("wrong byte_count value: %s" % byte_count) 574 | fwrite = getattr(f, "write", None) 575 | if fwrite: 576 | fobj = f 577 | fclose = None 578 | else: 579 | fobj = open(f, 'w') 580 | fwrite = fobj.write 581 | fclose = fobj.close 582 | 583 | eol = IntelHex._get_eol_textfile(eolstyle, sys.platform) 584 | 585 | # Translation table for uppercasing hex ascii string. 586 | # timeit shows that using hexstr.translate(table) 587 | # is faster than hexstr.upper(): 588 | # 0.452ms vs. 0.652ms (translate vs. upper) 589 | if sys.version_info[0] >= 3: 590 | # Python 3 591 | table = bytes(range_l(256)).upper() 592 | else: 593 | # Python 2 594 | table = ''.join(chr(i).upper() for i in range_g(256)) 595 | 596 | # start address record if any 597 | if self.start_addr and write_start_addr: 598 | keys = dict_keys(self.start_addr) 599 | keys.sort() 600 | bin = array('B', asbytes('\0'*9)) 601 | if keys == ['CS','IP']: 602 | # Start Segment Address Record 603 | bin[0] = 4 # reclen 604 | bin[1] = 0 # offset msb 605 | bin[2] = 0 # offset lsb 606 | bin[3] = 3 # rectyp 607 | cs = self.start_addr['CS'] 608 | bin[4] = (cs >> 8) & 0x0FF 609 | bin[5] = cs & 0x0FF 610 | ip = self.start_addr['IP'] 611 | bin[6] = (ip >> 8) & 0x0FF 612 | bin[7] = ip & 0x0FF 613 | bin[8] = (-sum(bin)) & 0x0FF # chksum 614 | fwrite(':' + 615 | asstr(hexlify(array_tobytes(bin)).translate(table)) + 616 | eol) 617 | elif keys == ['EIP']: 618 | # Start Linear Address Record 619 | bin[0] = 4 # reclen 620 | bin[1] = 0 # offset msb 621 | bin[2] = 0 # offset lsb 622 | bin[3] = 5 # rectyp 623 | eip = self.start_addr['EIP'] 624 | bin[4] = (eip >> 24) & 0x0FF 625 | bin[5] = (eip >> 16) & 0x0FF 626 | bin[6] = (eip >> 8) & 0x0FF 627 | bin[7] = eip & 0x0FF 628 | bin[8] = (-sum(bin)) & 0x0FF # chksum 629 | fwrite(':' + 630 | asstr(hexlify(array_tobytes(bin)).translate(table)) + 631 | eol) 632 | else: 633 | if fclose: 634 | fclose() 635 | raise InvalidStartAddressValueError(start_addr=self.start_addr) 636 | 637 | # data 638 | addresses = dict_keys(self._buf) 639 | addresses.sort() 640 | addr_len = len(addresses) 641 | if addr_len: 642 | minaddr = addresses[0] 643 | maxaddr = addresses[-1] 644 | 645 | if maxaddr > 65535: 646 | need_offset_record = True 647 | else: 648 | need_offset_record = False 649 | high_ofs = 0 650 | 651 | cur_addr = minaddr 652 | cur_ix = 0 653 | 654 | while cur_addr <= maxaddr: 655 | if need_offset_record: 656 | bin = array('B', asbytes('\0'*7)) 657 | bin[0] = 2 # reclen 658 | bin[1] = 0 # offset msb 659 | bin[2] = 0 # offset lsb 660 | bin[3] = 4 # rectyp 661 | high_ofs = int(cur_addr>>16) 662 | b = divmod(high_ofs, 256) 663 | bin[4] = b[0] # msb of high_ofs 664 | bin[5] = b[1] # lsb of high_ofs 665 | bin[6] = (-sum(bin)) & 0x0FF # chksum 666 | fwrite(':' + 667 | asstr(hexlify(array_tobytes(bin)).translate(table)) + 668 | eol) 669 | 670 | while True: 671 | # produce one record 672 | low_addr = cur_addr & 0x0FFFF 673 | # chain_len off by 1 674 | chain_len = min(byte_count-1, 65535-low_addr, maxaddr-cur_addr) 675 | 676 | # search continuous chain 677 | stop_addr = cur_addr + chain_len 678 | if chain_len: 679 | ix = bisect_right(addresses, stop_addr, 680 | cur_ix, 681 | min(cur_ix+chain_len+1, addr_len)) 682 | chain_len = ix - cur_ix # real chain_len 683 | # there could be small holes in the chain 684 | # but we will catch them by try-except later 685 | # so for big continuous files we will work 686 | # at maximum possible speed 687 | else: 688 | chain_len = 1 # real chain_len 689 | 690 | bin = array('B', asbytes('\0'*(5+chain_len))) 691 | b = divmod(low_addr, 256) 692 | bin[1] = b[0] # msb of low_addr 693 | bin[2] = b[1] # lsb of low_addr 694 | bin[3] = 0 # rectype 695 | try: # if there is small holes we'll catch them 696 | for i in range_g(chain_len): 697 | bin[4+i] = self._buf[cur_addr+i] 698 | except KeyError: 699 | # we catch a hole so we should shrink the chain 700 | chain_len = i 701 | bin = bin[:5+i] 702 | bin[0] = chain_len 703 | bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum 704 | fwrite(':' + 705 | asstr(hexlify(array_tobytes(bin)).translate(table)) + 706 | eol) 707 | 708 | # adjust cur_addr/cur_ix 709 | cur_ix += chain_len 710 | if cur_ix < addr_len: 711 | cur_addr = addresses[cur_ix] 712 | else: 713 | cur_addr = maxaddr + 1 714 | break 715 | high_addr = int(cur_addr>>16) 716 | if high_addr > high_ofs: 717 | break 718 | 719 | # end-of-file record 720 | fwrite(":00000001FF"+eol) 721 | if fclose: 722 | fclose() 723 | 724 | def tofile(self, fobj, format, byte_count=16): 725 | """Write data to hex or bin file. Preferred method over tobin or tohex. 726 | 727 | @param fobj file name or file-like object 728 | @param format file format ("hex" or "bin") 729 | @param byte_count bytes per line 730 | """ 731 | if format == 'hex': 732 | self.write_hex_file(fobj, byte_count=byte_count) 733 | elif format == 'bin': 734 | self.tobinfile(fobj) 735 | else: 736 | raise ValueError('format should be either "hex" or "bin";' 737 | ' got %r instead' % format) 738 | 739 | def gets(self, addr, length): 740 | """Get string of bytes from given address. If any entries are blank 741 | from addr through addr+length, a NotEnoughDataError exception will 742 | be raised. Padding is not used. 743 | """ 744 | a = array('B', asbytes('\0'*length)) 745 | try: 746 | for i in range_g(length): 747 | a[i] = self._buf[addr+i] 748 | except KeyError: 749 | raise NotEnoughDataError(address=addr, length=length) 750 | return array_tobytes(a) 751 | 752 | def puts(self, addr, s): 753 | """Put string of bytes at given address. Will overwrite any previous 754 | entries. 755 | """ 756 | a = array('B', asbytes(s)) 757 | for i in range_g(len(a)): 758 | self._buf[addr+i] = a[i] 759 | 760 | def getsz(self, addr): 761 | """Get zero-terminated bytes string from given address. Will raise 762 | NotEnoughDataError exception if a hole is encountered before a 0. 763 | """ 764 | i = 0 765 | try: 766 | while True: 767 | if self._buf[addr+i] == 0: 768 | break 769 | i += 1 770 | except KeyError: 771 | raise NotEnoughDataError(msg=('Bad access at 0x%X: ' 772 | 'not enough data to read zero-terminated string') % addr) 773 | return self.gets(addr, i) 774 | 775 | def putsz(self, addr, s): 776 | """Put bytes string in object at addr and append terminating zero at end.""" 777 | self.puts(addr, s) 778 | self._buf[addr+len(s)] = 0 779 | 780 | def find(self, sub, start=None, end=None): 781 | """Return the lowest index in self[start:end] where subsection sub is found. 782 | Optional arguments start and end are interpreted as in slice notation. 783 | 784 | @param sub bytes-like subsection to find 785 | @param start start of section to search within (optional) 786 | @param end end of section to search within (optional) 787 | """ 788 | sub = bytes(sub) 789 | for start, end in self[slice(start,end)].segments(): 790 | b = self.gets(start, end-start) 791 | i = b.find(sub) 792 | if i != -1: 793 | return start+i 794 | return -1 795 | 796 | def dump(self, tofile=None, width=16, withpadding=False): 797 | """Dump object content to specified file object or to stdout if None. 798 | Format is a hexdump with some header information at the beginning, 799 | addresses on the left, and data on right. 800 | 801 | @param tofile file-like object to dump to 802 | @param width number of bytes per line (i.e. columns) 803 | @param withpadding print padding character instead of '--' 804 | @raise ValueError if width is not a positive integer 805 | """ 806 | 807 | if not isinstance(width,int) or width < 1: 808 | raise ValueError('width must be a positive integer.') 809 | # The integer can be of float type - does not work with bit operations 810 | width = int(width) 811 | if tofile is None: 812 | tofile = sys.stdout 813 | 814 | # start addr possibly 815 | if self.start_addr is not None: 816 | cs = self.start_addr.get('CS') 817 | ip = self.start_addr.get('IP') 818 | eip = self.start_addr.get('EIP') 819 | if eip is not None and cs is None and ip is None: 820 | tofile.write('EIP = 0x%08X\n' % eip) 821 | elif eip is None and cs is not None and ip is not None: 822 | tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) 823 | else: 824 | tofile.write('start_addr = %r\n' % self.start_addr) 825 | # actual data 826 | addresses = dict_keys(self._buf) 827 | if addresses: 828 | addresses.sort() 829 | minaddr = addresses[0] 830 | maxaddr = addresses[-1] 831 | startaddr = (minaddr // width) * width 832 | endaddr = ((maxaddr // width) + 1) * width 833 | maxdigits = max(len(hex(endaddr)) - 2, 4) # Less 2 to exclude '0x' 834 | templa = '%%0%dX' % maxdigits 835 | rangewidth = range_l(width) 836 | if withpadding: 837 | pad = self.padding 838 | else: 839 | pad = None 840 | for i in range_g(startaddr, endaddr, width): 841 | tofile.write(templa % i) 842 | tofile.write(' ') 843 | s = [] 844 | for j in rangewidth: 845 | x = self._buf.get(i+j, pad) 846 | if x is not None: 847 | tofile.write(' %02X' % x) 848 | if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot 849 | s.append(chr(x)) 850 | else: 851 | s.append('.') 852 | else: 853 | tofile.write(' --') 854 | s.append(' ') 855 | tofile.write(' |' + ''.join(s) + '|\n') 856 | 857 | def merge(self, other, overlap='error'): 858 | """Merge content of other IntelHex object into current object (self). 859 | @param other other IntelHex object. 860 | @param overlap action on overlap of data or starting addr: 861 | - error: raising OverlapError; 862 | - ignore: ignore other data and keep current data 863 | in overlapping region; 864 | - replace: replace data with other data 865 | in overlapping region. 866 | 867 | @raise TypeError if other is not instance of IntelHex 868 | @raise ValueError if other is the same object as self 869 | (it can't merge itself) 870 | @raise ValueError if overlap argument has incorrect value 871 | @raise AddressOverlapError on overlapped data 872 | """ 873 | # check args 874 | if not isinstance(other, IntelHex): 875 | raise TypeError('other should be IntelHex object') 876 | if other is self: 877 | raise ValueError("Can't merge itself") 878 | if overlap not in ('error', 'ignore', 'replace'): 879 | raise ValueError("overlap argument should be either " 880 | "'error', 'ignore' or 'replace'") 881 | # merge data 882 | this_buf = self._buf 883 | other_buf = other._buf 884 | for i in other_buf: 885 | if i in this_buf: 886 | if overlap == 'error': 887 | raise AddressOverlapError( 888 | 'Data overlapped at address 0x%X' % i) 889 | elif overlap == 'ignore': 890 | continue 891 | this_buf[i] = other_buf[i] 892 | # merge start_addr 893 | if self.start_addr != other.start_addr: 894 | if self.start_addr is None: # set start addr from other 895 | self.start_addr = other.start_addr 896 | elif other.start_addr is None: # keep existing start addr 897 | pass 898 | else: # conflict 899 | if overlap == 'error': 900 | raise AddressOverlapError( 901 | 'Starting addresses are different') 902 | elif overlap == 'replace': 903 | self.start_addr = other.start_addr 904 | 905 | def segments(self, min_gap=1): 906 | """Return a list of ordered tuple objects, representing contiguous occupied data addresses. 907 | Each tuple has a length of two and follows the semantics of the range and xrange objects. 908 | The second entry of the tuple is always an integer greater than the first entry. 909 | @param min_gap the minimum gap size between data in order to separate the segments 910 | """ 911 | addresses = self.addresses() 912 | if not addresses: 913 | return [] 914 | elif len(addresses) == 1: 915 | return([(addresses[0], addresses[0]+1)]) 916 | adjacent_differences = [(b - a) for (a, b) in zip(addresses[:-1], addresses[1:])] 917 | breaks = [i for (i, x) in enumerate(adjacent_differences) if x > min_gap] 918 | endings = [addresses[b] for b in breaks] 919 | endings.append(addresses[-1]) 920 | beginnings = [addresses[b+1] for b in breaks] 921 | beginnings.insert(0, addresses[0]) 922 | return [(a, b+1) for (a, b) in zip(beginnings, endings)] 923 | 924 | def get_memory_size(self): 925 | """Returns the approximate memory footprint for data.""" 926 | n = sys.getsizeof(self) 927 | n += sys.getsizeof(self.padding) 928 | n += total_size(self.start_addr) 929 | n += total_size(self._buf) 930 | n += sys.getsizeof(self._offset) 931 | return n 932 | 933 | #/IntelHex 934 | 935 | 936 | class IntelHex16bit(IntelHex): 937 | """Access to data as 16-bit words. Intended to use with Microchip HEX files.""" 938 | 939 | def __init__(self, source=None): 940 | """Construct class from HEX file 941 | or from instance of ordinary IntelHex class. If IntelHex object 942 | is passed as source, the original IntelHex object should not be used 943 | again because this class will alter it. This class leaves padding 944 | alone unless it was precisely 0xFF. In that instance it is sign 945 | extended to 0xFFFF. 946 | 947 | @param source file name of HEX file or file object 948 | or instance of ordinary IntelHex class. 949 | Will also accept dictionary from todict method. 950 | """ 951 | if isinstance(source, IntelHex): 952 | # from ihex8 953 | self.padding = source.padding 954 | self.start_addr = source.start_addr 955 | # private members 956 | self._buf = source._buf 957 | self._offset = source._offset 958 | elif isinstance(source, dict): 959 | raise IntelHexError("IntelHex16bit does not support initialization from dictionary yet.\n" 960 | "Patches are welcome.") 961 | else: 962 | IntelHex.__init__(self, source) 963 | 964 | if self.padding == 0x0FF: 965 | self.padding = 0x0FFFF 966 | 967 | def __getitem__(self, addr16): 968 | """Get 16-bit word from address. 969 | Raise error if only one byte from the pair is set. 970 | We assume a Little Endian interpretation of the hex file. 971 | 972 | @param addr16 address of word (addr8 = 2 * addr16). 973 | @return word if bytes exists in HEX file, or self.padding 974 | if no data found. 975 | """ 976 | addr1 = addr16 * 2 977 | addr2 = addr1 + 1 978 | byte1 = self._buf.get(addr1, None) 979 | byte2 = self._buf.get(addr2, None) 980 | 981 | if byte1 != None and byte2 != None: 982 | return byte1 | (byte2 << 8) # low endian 983 | 984 | if byte1 == None and byte2 == None: 985 | return self.padding 986 | 987 | raise BadAccess16bit(address=addr16) 988 | 989 | def __setitem__(self, addr16, word): 990 | """Sets the address at addr16 to word assuming Little Endian mode. 991 | """ 992 | addr_byte = addr16 * 2 993 | b = divmod(word, 256) 994 | self._buf[addr_byte] = b[1] 995 | self._buf[addr_byte+1] = b[0] 996 | 997 | def minaddr(self): 998 | '''Get minimal address of HEX content in 16-bit mode. 999 | 1000 | @return minimal address used in this object 1001 | ''' 1002 | aa = dict_keys(self._buf) 1003 | if aa == []: 1004 | return 0 1005 | else: 1006 | return min(aa)>>1 1007 | 1008 | def maxaddr(self): 1009 | '''Get maximal address of HEX content in 16-bit mode. 1010 | 1011 | @return maximal address used in this object 1012 | ''' 1013 | aa = dict_keys(self._buf) 1014 | if aa == []: 1015 | return 0 1016 | else: 1017 | return max(aa)>>1 1018 | 1019 | def tobinarray(self, start=None, end=None, size=None): 1020 | '''Convert this object to binary form as array (of 2-bytes word data). 1021 | If start and end unspecified, they will be inferred from the data. 1022 | @param start start address of output data. 1023 | @param end end address of output data (inclusive). 1024 | @param size size of the block (number of words), 1025 | used with start or end parameter. 1026 | @return array of unsigned short (uint16_t) data. 1027 | ''' 1028 | bin = array('H') 1029 | 1030 | if self._buf == {} and None in (start, end): 1031 | return bin 1032 | 1033 | if size is not None and size <= 0: 1034 | raise ValueError("tobinarray: wrong value for size") 1035 | 1036 | start, end = self._get_start_end(start, end, size) 1037 | 1038 | for addr in range_g(start, end+1): 1039 | bin.append(self[addr]) 1040 | 1041 | return bin 1042 | 1043 | 1044 | #/class IntelHex16bit 1045 | 1046 | 1047 | def hex2bin(fin, fout, start=None, end=None, size=None, pad=None): 1048 | """Hex-to-Bin convertor engine. 1049 | @return 0 if all OK 1050 | 1051 | @param fin input hex file (filename or file-like object) 1052 | @param fout output bin file (filename or file-like object) 1053 | @param start start of address range (optional) 1054 | @param end end of address range (inclusive; optional) 1055 | @param size size of resulting file (in bytes) (optional) 1056 | @param pad padding byte (optional) 1057 | """ 1058 | try: 1059 | h = IntelHex(fin) 1060 | except HexReaderError: 1061 | e = sys.exc_info()[1] # current exception 1062 | txt = "ERROR: bad HEX file: %s" % str(e) 1063 | print(txt) 1064 | return 1 1065 | 1066 | # start, end, size 1067 | if size != None and size != 0: 1068 | if end == None: 1069 | if start == None: 1070 | start = h.minaddr() 1071 | end = start + size - 1 1072 | else: 1073 | if (end+1) >= size: 1074 | start = end + 1 - size 1075 | else: 1076 | start = 0 1077 | 1078 | try: 1079 | if pad is not None: 1080 | # using .padding attribute rather than pad argument to function call 1081 | h.padding = pad 1082 | h.tobinfile(fout, start, end) 1083 | except IOError: 1084 | e = sys.exc_info()[1] # current exception 1085 | txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e)) 1086 | print(txt) 1087 | return 1 1088 | 1089 | return 0 1090 | #/def hex2bin 1091 | 1092 | 1093 | def bin2hex(fin, fout, offset=0): 1094 | """Simple bin-to-hex convertor. 1095 | @return 0 if all OK 1096 | 1097 | @param fin input bin file (filename or file-like object) 1098 | @param fout output hex file (filename or file-like object) 1099 | @param offset starting address offset for loading bin 1100 | """ 1101 | h = IntelHex() 1102 | try: 1103 | h.loadbin(fin, offset) 1104 | except IOError: 1105 | e = sys.exc_info()[1] # current exception 1106 | txt = 'ERROR: unable to load bin file:', str(e) 1107 | print(txt) 1108 | return 1 1109 | 1110 | try: 1111 | h.tofile(fout, format='hex') 1112 | except IOError: 1113 | e = sys.exc_info()[1] # current exception 1114 | txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e)) 1115 | print(txt) 1116 | return 1 1117 | 1118 | return 0 1119 | #/def bin2hex 1120 | 1121 | 1122 | def diff_dumps(ih1, ih2, tofile=None, name1="a", name2="b", n_context=3): 1123 | """Diff 2 IntelHex objects and produce unified diff output for their 1124 | hex dumps. 1125 | 1126 | @param ih1 first IntelHex object to compare 1127 | @param ih2 second IntelHex object to compare 1128 | @param tofile file-like object to write output 1129 | @param name1 name of the first hex file to show in the diff header 1130 | @param name2 name of the first hex file to show in the diff header 1131 | @param n_context number of context lines in the unidiff output 1132 | """ 1133 | def prepare_lines(ih): 1134 | sio = StringIO() 1135 | ih.dump(sio) 1136 | dump = sio.getvalue() 1137 | lines = dump.splitlines() 1138 | return lines 1139 | a = prepare_lines(ih1) 1140 | b = prepare_lines(ih2) 1141 | import difflib 1142 | result = list(difflib.unified_diff(a, b, fromfile=name1, tofile=name2, n=n_context, lineterm='')) 1143 | if tofile is None: 1144 | tofile = sys.stdout 1145 | output = '\n'.join(result)+'\n' 1146 | tofile.write(output) 1147 | 1148 | 1149 | class Record(object): 1150 | """Helper methods to build valid ihex records.""" 1151 | 1152 | def _from_bytes(bytes): 1153 | """Takes a list of bytes, computes the checksum, and outputs the entire 1154 | record as a string. bytes should be the hex record without the colon 1155 | or final checksum. 1156 | 1157 | @param bytes list of byte values so far to pack into record. 1158 | @return String representation of one HEX record 1159 | """ 1160 | assert len(bytes) >= 4 1161 | # calculate checksum 1162 | s = (-sum(bytes)) & 0x0FF 1163 | bin = array('B', bytes + [s]) 1164 | return ':' + asstr(hexlify(array_tobytes(bin))).upper() 1165 | _from_bytes = staticmethod(_from_bytes) 1166 | 1167 | def data(offset, bytes): 1168 | """Return Data record. This constructs the full record, including 1169 | the length information, the record type (0x00), the 1170 | checksum, and the offset. 1171 | 1172 | @param offset load offset of first byte. 1173 | @param bytes list of byte values to pack into record. 1174 | 1175 | @return String representation of one HEX record 1176 | """ 1177 | assert 0 <= offset < 65536 1178 | assert 0 < len(bytes) < 256 1179 | b = [len(bytes), (offset>>8)&0x0FF, offset&0x0FF, 0x00] + bytes 1180 | return Record._from_bytes(b) 1181 | data = staticmethod(data) 1182 | 1183 | def eof(): 1184 | """Return End of File record as a string. 1185 | @return String representation of Intel Hex EOF record 1186 | """ 1187 | return ':00000001FF' 1188 | eof = staticmethod(eof) 1189 | 1190 | def extended_segment_address(usba): 1191 | """Return Extended Segment Address Record. 1192 | @param usba Upper Segment Base Address. 1193 | 1194 | @return String representation of Intel Hex USBA record. 1195 | """ 1196 | b = [2, 0, 0, 0x02, (usba>>8)&0x0FF, usba&0x0FF] 1197 | return Record._from_bytes(b) 1198 | extended_segment_address = staticmethod(extended_segment_address) 1199 | 1200 | def start_segment_address(cs, ip): 1201 | """Return Start Segment Address Record. 1202 | @param cs 16-bit value for CS register. 1203 | @param ip 16-bit value for IP register. 1204 | 1205 | @return String representation of Intel Hex SSA record. 1206 | """ 1207 | b = [4, 0, 0, 0x03, (cs>>8)&0x0FF, cs&0x0FF, 1208 | (ip>>8)&0x0FF, ip&0x0FF] 1209 | return Record._from_bytes(b) 1210 | start_segment_address = staticmethod(start_segment_address) 1211 | 1212 | def extended_linear_address(ulba): 1213 | """Return Extended Linear Address Record. 1214 | @param ulba Upper Linear Base Address. 1215 | 1216 | @return String representation of Intel Hex ELA record. 1217 | """ 1218 | b = [2, 0, 0, 0x04, (ulba>>8)&0x0FF, ulba&0x0FF] 1219 | return Record._from_bytes(b) 1220 | extended_linear_address = staticmethod(extended_linear_address) 1221 | 1222 | def start_linear_address(eip): 1223 | """Return Start Linear Address Record. 1224 | @param eip 32-bit linear address for the EIP register. 1225 | 1226 | @return String representation of Intel Hex SLA record. 1227 | """ 1228 | b = [4, 0, 0, 0x05, (eip>>24)&0x0FF, (eip>>16)&0x0FF, 1229 | (eip>>8)&0x0FF, eip&0x0FF] 1230 | return Record._from_bytes(b) 1231 | start_linear_address = staticmethod(start_linear_address) 1232 | 1233 | 1234 | class _BadFileNotation(Exception): 1235 | """Special error class to use with _get_file_and_addr_range.""" 1236 | pass 1237 | 1238 | def _get_file_and_addr_range(s, _support_drive_letter=None): 1239 | """Special method for hexmerge.py script to split file notation 1240 | into 3 parts: (filename, start, end) 1241 | 1242 | @raise _BadFileNotation when string cannot be safely split. 1243 | """ 1244 | if _support_drive_letter is None: 1245 | _support_drive_letter = (os.name == 'nt') 1246 | drive = '' 1247 | if _support_drive_letter: 1248 | if s[1:2] == ':' and s[0].upper() in ''.join([chr(i) for i in range_g(ord('A'), ord('Z')+1)]): 1249 | drive = s[:2] 1250 | s = s[2:] 1251 | parts = s.split(':') 1252 | n = len(parts) 1253 | if n == 1: 1254 | fname = parts[0] 1255 | fstart = None 1256 | fend = None 1257 | elif n != 3: 1258 | raise _BadFileNotation 1259 | else: 1260 | fname = parts[0] 1261 | def ascii_hex_to_int(ascii): 1262 | if ascii is not None: 1263 | try: 1264 | return int(ascii, 16) 1265 | except ValueError: 1266 | raise _BadFileNotation 1267 | return ascii 1268 | fstart = ascii_hex_to_int(parts[1] or None) 1269 | fend = ascii_hex_to_int(parts[2] or None) 1270 | return drive+fname, fstart, fend 1271 | 1272 | 1273 | ## 1274 | # IntelHex Errors Hierarchy: 1275 | # 1276 | # IntelHexError - basic error 1277 | # HexReaderError - general hex reader error 1278 | # AddressOverlapError - data for the same address overlap 1279 | # HexRecordError - hex record decoder base error 1280 | # RecordLengthError - record has invalid length 1281 | # RecordTypeError - record has invalid type (RECTYP) 1282 | # RecordChecksumError - record checksum mismatch 1283 | # EOFRecordError - invalid EOF record (type 01) 1284 | # ExtendedAddressRecordError - extended address record base error 1285 | # ExtendedSegmentAddressRecordError - invalid extended segment address record (type 02) 1286 | # ExtendedLinearAddressRecordError - invalid extended linear address record (type 04) 1287 | # StartAddressRecordError - start address record base error 1288 | # StartSegmentAddressRecordError - invalid start segment address record (type 03) 1289 | # StartLinearAddressRecordError - invalid start linear address record (type 05) 1290 | # DuplicateStartAddressRecordError - start address record appears twice 1291 | # InvalidStartAddressValueError - invalid value of start addr record 1292 | # _EndOfFile - it's not real error, used internally by hex reader as signal that EOF record found 1293 | # BadAccess16bit - not enough data to read 16 bit value (deprecated, see NotEnoughDataError) 1294 | # NotEnoughDataError - not enough data to read N contiguous bytes 1295 | # EmptyIntelHexError - requested operation cannot be performed with empty object 1296 | 1297 | class IntelHexError(Exception): 1298 | '''Base Exception class for IntelHex module''' 1299 | 1300 | _fmt = 'IntelHex base error' #: format string 1301 | 1302 | def __init__(self, msg=None, **kw): 1303 | """Initialize the Exception with the given message. 1304 | """ 1305 | self.msg = msg 1306 | for key, value in dict_items_g(kw): 1307 | setattr(self, key, value) 1308 | 1309 | def __str__(self): 1310 | """Return the message in this Exception.""" 1311 | if self.msg: 1312 | return self.msg 1313 | try: 1314 | return self._fmt % self.__dict__ 1315 | except (NameError, ValueError, KeyError): 1316 | e = sys.exc_info()[1] # current exception 1317 | return 'Unprintable exception %s: %s' \ 1318 | % (repr(e), str(e)) 1319 | 1320 | class _EndOfFile(IntelHexError): 1321 | """Used for internal needs only.""" 1322 | _fmt = 'EOF record reached -- signal to stop read file' 1323 | 1324 | class HexReaderError(IntelHexError): 1325 | _fmt = 'Hex reader base error' 1326 | 1327 | class AddressOverlapError(HexReaderError): 1328 | _fmt = 'Hex file has data overlap at address 0x%(address)X on line %(line)d' 1329 | 1330 | # class NotAHexFileError was removed in trunk.revno.54 because it's not used 1331 | 1332 | 1333 | class HexRecordError(HexReaderError): 1334 | _fmt = 'Hex file contains invalid record at line %(line)d' 1335 | 1336 | 1337 | class RecordLengthError(HexRecordError): 1338 | _fmt = 'Record at line %(line)d has invalid length' 1339 | 1340 | class RecordTypeError(HexRecordError): 1341 | _fmt = 'Record at line %(line)d has invalid record type' 1342 | 1343 | class RecordChecksumError(HexRecordError): 1344 | _fmt = 'Record at line %(line)d has invalid checksum' 1345 | 1346 | class EOFRecordError(HexRecordError): 1347 | _fmt = 'File has invalid End-of-File record' 1348 | 1349 | 1350 | class ExtendedAddressRecordError(HexRecordError): 1351 | _fmt = 'Base class for extended address exceptions' 1352 | 1353 | class ExtendedSegmentAddressRecordError(ExtendedAddressRecordError): 1354 | _fmt = 'Invalid Extended Segment Address Record at line %(line)d' 1355 | 1356 | class ExtendedLinearAddressRecordError(ExtendedAddressRecordError): 1357 | _fmt = 'Invalid Extended Linear Address Record at line %(line)d' 1358 | 1359 | 1360 | class StartAddressRecordError(HexRecordError): 1361 | _fmt = 'Base class for start address exceptions' 1362 | 1363 | class StartSegmentAddressRecordError(StartAddressRecordError): 1364 | _fmt = 'Invalid Start Segment Address Record at line %(line)d' 1365 | 1366 | class StartLinearAddressRecordError(StartAddressRecordError): 1367 | _fmt = 'Invalid Start Linear Address Record at line %(line)d' 1368 | 1369 | class DuplicateStartAddressRecordError(StartAddressRecordError): 1370 | _fmt = 'Start Address Record appears twice at line %(line)d' 1371 | 1372 | class InvalidStartAddressValueError(StartAddressRecordError): 1373 | _fmt = 'Invalid start address value: %(start_addr)s' 1374 | 1375 | 1376 | class NotEnoughDataError(IntelHexError): 1377 | _fmt = ('Bad access at 0x%(address)X: ' 1378 | 'not enough data to read %(length)d contiguous bytes') 1379 | 1380 | class BadAccess16bit(NotEnoughDataError): 1381 | _fmt = 'Bad access at 0x%(address)X: not enough data to read 16 bit value' 1382 | 1383 | class EmptyIntelHexError(IntelHexError): 1384 | _fmt = "Requested operation cannot be executed with empty object" 1385 | -------------------------------------------------------------------------------- /intelhex/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016-2018, Alexander Belchenko 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, 5 | # with or without modification, are permitted provided 6 | # that the following conditions are met: 7 | # 8 | # * Redistributions of source code must retain 9 | # the above copyright notice, this list of conditions 10 | # and the following disclaimer. 11 | # * Redistributions in binary form must reproduce 12 | # the above copyright notice, this list of conditions 13 | # and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # * Neither the name of the author nor the names 16 | # of its contributors may be used to endorse 17 | # or promote products derived from this software 18 | # without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 22 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 23 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 25 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 26 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 32 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | if __name__ == '__main__': 35 | print("Welcome to IntelHex Python library.") 36 | print() 37 | print("The intelhex package has some executable points:") 38 | print(" python -m intelhex.test -- easy way to run unit tests.") 39 | print(" python -m intelhex.bench -- run benchmarks.") 40 | -------------------------------------------------------------------------------- /intelhex/__version__.py: -------------------------------------------------------------------------------- 1 | # IntelHex library version information 2 | version_info = (2, 3, 0) 3 | version_str = '.'.join([str(i) for i in version_info]) 4 | -------------------------------------------------------------------------------- /intelhex/bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # (c) Alexander Belchenko, 2007, 2009 3 | 4 | # [2013/08] NOTE: This file is keeping for historical reasons. 5 | # It may or may not work actually with current version of intelhex, 6 | # and most likely it requires some fixes here and there. 7 | 8 | """Benchmarking. 9 | 10 | Run each test 3 times and get median value. 11 | Using 10K array as base test time. 12 | 13 | Each other test compared with base with next formula:: 14 | 15 | Tc * Nb 16 | q = --------- 17 | Tb * Nc 18 | 19 | Here: 20 | 21 | * Tc - execution time of current test 22 | * Tb - execution time of base 23 | * Nb - array size of base (10K) 24 | * Nc - array size of current test 25 | 26 | If resulting value is ``q <= 1.0`` it's the best possible result, 27 | i.e. time increase proportionally to array size. 28 | """ 29 | 30 | import gc 31 | import sys 32 | import time 33 | 34 | import intelhex 35 | from intelhex.compat import StringIO, range_g 36 | 37 | def median(values): 38 | """Return median value for the list of values. 39 | @param values: list of values for processing. 40 | @return: median value. 41 | """ 42 | values.sort() 43 | n = int(len(values) / 2) 44 | return values[n] 45 | 46 | def run_test(func, fobj): 47 | """Run func with argument fobj and measure execution time. 48 | @param func: function for test 49 | @param fobj: data for test 50 | @return: execution time 51 | """ 52 | gc.disable() 53 | try: 54 | begin = time.time() 55 | func(fobj) 56 | end = time.time() 57 | finally: 58 | gc.enable() 59 | return end - begin 60 | 61 | def run_readtest_N_times(func, hexstr, n): 62 | """Run each test N times. 63 | @param func: function for test 64 | @param hexstr: string with content of hex file to read 65 | @param n: times to repeat. 66 | @return: (median time, times list) 67 | """ 68 | assert n > 0 69 | times = [] 70 | for i in range_g(n): 71 | sio = StringIO(hexstr) 72 | times.append(run_test(func, sio)) 73 | sio.close() 74 | t = median(times) 75 | return t, times 76 | 77 | def run_writetest_N_times(func, n): 78 | """Run each test N times. 79 | @param func: function for test 80 | @param n: times to repeat. 81 | @return: (median time, times list) 82 | """ 83 | assert n > 0 84 | times = [] 85 | for i in range_g(n): 86 | sio = StringIO() 87 | times.append(run_test(func, sio)) 88 | sio.close() 89 | t = median(times) 90 | return t, times 91 | 92 | def time_coef(tc, nc, tb, nb): 93 | """Return time coefficient relative to base numbers. 94 | @param tc: current test time 95 | @param nc: current test data size 96 | @param tb: base test time 97 | @param nb: base test data size 98 | @return: time coef. 99 | """ 100 | tc = float(tc) 101 | nc = float(nc) 102 | tb = float(tb) 103 | nb = float(nb) 104 | q = (tc * nb) / (tb * nc) 105 | return q 106 | 107 | def get_test_data(n1, offset, n2): 108 | """Create test data on given pattern. 109 | @param n1: size of first part of array at base address 0. 110 | @param offset: offset for second part of array. 111 | @param n2: size of second part of array at given offset. 112 | @return: (overall size, hex file, IntelHex object) 113 | """ 114 | # make IntelHex object 115 | ih = intelhex.IntelHex() 116 | addr = 0 117 | for i in range_g(n1): 118 | ih[addr] = addr % 256 119 | addr += 1 120 | addr += offset 121 | for i in range_g(n2): 122 | ih[addr] = addr % 256 123 | addr += 1 124 | # make hex file 125 | sio = StringIO() 126 | ih.write_hex_file(sio) 127 | hexstr = sio.getvalue() 128 | sio.close() 129 | # 130 | return n1+n2, hexstr, ih 131 | 132 | def get_base_50K(): 133 | return get_test_data(50000, 0, 0) 134 | 135 | def get_250K(): 136 | return get_test_data(250000, 0, 0) 137 | 138 | def get_100K_100K(): 139 | return get_test_data(100000, 1000000, 100000) 140 | 141 | def get_0_100K(): 142 | return get_test_data(0, 1000000, 100000) 143 | 144 | def get_1M(): 145 | return get_test_data(1000000, 0, 0) 146 | 147 | 148 | class Measure(object): 149 | """Measure execution time helper.""" 150 | 151 | data_set = [ 152 | # (data name, getter) 153 | ('base 50K', get_base_50K), # first should be base numbers 154 | ('250K', get_250K), 155 | ('1M', get_1M), 156 | ('100K+100K', get_100K_100K), 157 | ('0+100K', get_0_100K), 158 | ] 159 | 160 | def __init__(self, n=3, read=True, write=True): 161 | self.n = n 162 | self.read = read 163 | self.write = write 164 | self.results = [] 165 | 166 | def measure_one(self, data): 167 | """Do measuring of read and write operations. 168 | @param data: 3-tuple from get_test_data 169 | @return: (time readhex, time writehex) 170 | """ 171 | _unused, hexstr, ih = data 172 | tread, twrite = 0.0, 0.0 173 | if self.read: 174 | tread = run_readtest_N_times(intelhex.IntelHex, hexstr, self.n)[0] 175 | if self.write: 176 | twrite = run_writetest_N_times(ih.write_hex_file, self.n)[0] 177 | return tread, twrite 178 | 179 | def measure_all(self): 180 | for name, getter in self.data_set: 181 | data = getter() 182 | times = self.measure_one(data) 183 | self.results.append((name, times, data[0])) 184 | 185 | def print_report(self, to_file=None): 186 | if to_file is None: 187 | to_file = sys.stdout 188 | 189 | base_title, base_times, base_n = self.results[0] 190 | base_read, base_write = base_times 191 | read_report = ['%-10s\t%7.3f' % (base_title, base_read)] 192 | write_report = ['%-10s\t%7.3f' % (base_title, base_write)] 193 | 194 | for item in self.results[1:]: 195 | cur_title, cur_times, cur_n = item 196 | cur_read, cur_write = cur_times 197 | if self.read: 198 | qread = time_coef(cur_read, cur_n, 199 | base_read, base_n) 200 | read_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title, 201 | cur_read, 202 | qread)) 203 | if self.write: 204 | qwrite = time_coef(cur_write, cur_n, 205 | base_write, base_n) 206 | write_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title, 207 | cur_write, 208 | qwrite)) 209 | if self.read: 210 | to_file.write('Read operation:\n') 211 | to_file.write('\n'.join(read_report)) 212 | to_file.write('\n\n') 213 | if self.write: 214 | to_file.write('Write operation:\n') 215 | to_file.write('\n'.join(write_report)) 216 | to_file.write('\n\n') 217 | 218 | 219 | HELP = """\ 220 | Usage: python _bench.py [OPTIONS] 221 | 222 | Options: 223 | -h this help 224 | -n N repeat tests N times 225 | -r run only tests for read operation 226 | -w run only tests for write operation 227 | 228 | If option -r or -w is not specified then all tests will be run. 229 | """ 230 | 231 | 232 | def main(argv=None): 233 | """Main function to run benchmarks. 234 | @param argv: command-line arguments. 235 | @return: exit code (0 is OK). 236 | """ 237 | import getopt 238 | 239 | # default values 240 | test_read = None 241 | test_write = None 242 | n = 3 # number of repeat 243 | 244 | if argv is None: 245 | argv = sys.argv[1:] 246 | 247 | try: 248 | opts, args = getopt.getopt(argv, 'hn:rw', []) 249 | 250 | for o,a in opts: 251 | if o == '-h': 252 | print(HELP) 253 | return 0 254 | elif o == '-n': 255 | n = int(a) 256 | elif o == '-r': 257 | test_read = True 258 | elif o == '-w': 259 | test_write = True 260 | 261 | if args: 262 | raise getopt.GetoptError('Arguments are not used.') 263 | except getopt.GetoptError: 264 | msg = sys.exc_info()[1] # current exception 265 | txt = str(msg) 266 | print(txt) 267 | return 1 268 | 269 | if (test_read, test_write) == (None, None): 270 | test_read = test_write = True 271 | 272 | m = Measure(n, test_read, test_write) 273 | m.measure_all() 274 | m.print_report() 275 | 276 | return 0 277 | 278 | 279 | if __name__ == '__main__': 280 | sys.exit(main(sys.argv[1:])) 281 | 282 | 283 | """ 284 | 285 | Some Results 286 | ************ 287 | 288 | 289 | 21/04/2007 revno.40 290 | Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz 291 | 292 | Read operation: 293 | base 10K 0.031 294 | 100K 0.360 1.161 295 | 1M 3.500 1.129 296 | 100K+100K 0.719 1.160 297 | 0+100K 0.360 1.161 298 | 299 | Write operation: 300 | base 10K 0.031 301 | 100K 0.297 0.958 302 | 1M 2.953 0.953 303 | 100K+100K 1.328 2.142 304 | 0+100K 0.312 1.006 305 | 306 | 307 | 21/04/2007 revno.46 308 | Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz 309 | 310 | Read operation: 311 | base 10K 0.016 312 | 100K 0.203 1.269 313 | 1M 2.000 1.250 314 | 100K+100K 0.422 1.319 315 | 0+100K 0.203 1.269 316 | 317 | Write operation: 318 | base 10K 0.031 319 | 100K 0.297 0.958 320 | 1M 2.969 0.958 321 | 100K+100K 1.328 2.142 322 | 0+100K 0.312 1.006 323 | 324 | 325 | 22/04/2007 revno.48 326 | Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz 327 | 328 | Read operation: 329 | base 10K 0.016 330 | 100K 0.187 1.169 331 | 1M 1.891 1.182 332 | 100K+100K 0.406 1.269 333 | 0+100K 0.188 1.175 334 | 335 | Write operation: 336 | base 10K 0.031 337 | 100K 0.296 0.955 338 | 1M 2.969 0.958 339 | 100K+100K 1.328 2.142 340 | 0+100K 0.312 1.006 341 | 342 | 343 | 19/08/2008 revno.72 344 | Python 2.5.2 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz 345 | 346 | Read operation: 347 | base 10K 0.016 348 | 100K 0.171 1.069 349 | 1M 1.734 1.084 350 | 100K+100K 0.375 1.172 351 | 0+100K 0.172 1.075 352 | 353 | Write operation: 354 | base 10K 0.016 355 | 100K 0.156 0.975 356 | 1M 1.532 0.957 357 | 100K+100K 0.344 1.075 358 | 0+100K 0.156 0.975 359 | 360 | """ 361 | -------------------------------------------------------------------------------- /intelhex/compat.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Bernhard Leiner 2 | # Copyright (c) 2013-2018 Alexander Belchenko 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, 6 | # with or without modification, are permitted provided 7 | # that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain 10 | # the above copyright notice, this list of conditions 11 | # and the following disclaimer. 12 | # * Redistributions in binary form must reproduce 13 | # the above copyright notice, this list of conditions 14 | # and the following disclaimer in the documentation 15 | # and/or other materials provided with the distribution. 16 | # * Neither the name of the author nor the names 17 | # of its contributors may be used to endorse 18 | # or promote products derived from this software 19 | # without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 23 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 24 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 26 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 27 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 29 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 33 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | '''Compatibility functions for python 2 and 3. 36 | 37 | @author Bernhard Leiner (bleiner AT gmail com) 38 | @author Alexander Belchenko (alexander belchenko AT gmail com) 39 | ''' 40 | 41 | __docformat__ = "javadoc" 42 | 43 | 44 | import sys, array 45 | 46 | 47 | if sys.version_info[0] >= 3: 48 | # Python 3 49 | Python = 3 50 | 51 | def asbytes(s): 52 | if isinstance(s, bytes): 53 | return s 54 | return s.encode('latin1') 55 | def asstr(s): 56 | if isinstance(s, str): 57 | return s 58 | return s.decode('latin1') 59 | 60 | # for python >= 3.2 use 'tobytes', otherwise 'tostring' 61 | array_tobytes = array.array.tobytes if sys.version_info[1] >= 2 else array.array.tostring 62 | 63 | IntTypes = (int,) 64 | StrType = str 65 | UnicodeType = str 66 | 67 | range_g = range # range generator 68 | def range_l(*args): # range list 69 | return list(range(*args)) 70 | 71 | def dict_keys(dikt): # dict keys list 72 | return list(dikt.keys()) 73 | def dict_keys_g(dikt): # dict keys generator 74 | return dikt.keys() 75 | def dict_items_g(dikt): # dict items generator 76 | return dikt.items() 77 | 78 | from io import StringIO, BytesIO 79 | 80 | def get_binary_stdout(): 81 | return sys.stdout.buffer 82 | 83 | def get_binary_stdin(): 84 | return sys.stdin.buffer 85 | 86 | else: 87 | # Python 2 88 | Python = 2 89 | 90 | asbytes = str 91 | asstr = str 92 | 93 | array_tobytes = array.array.tostring 94 | 95 | IntTypes = (int, long) 96 | StrType = basestring 97 | UnicodeType = unicode 98 | 99 | #range_g = xrange # range generator 100 | def range_g(*args): 101 | # we want to use xrange here but on python 2 it does not work with long ints 102 | try: 103 | return xrange(*args) 104 | except OverflowError: 105 | start = 0 106 | stop = 0 107 | step = 1 108 | n = len(args) 109 | if n == 1: 110 | stop = args[0] 111 | elif n == 2: 112 | start, stop = args 113 | elif n == 3: 114 | start, stop, step = args 115 | else: 116 | raise TypeError('wrong number of arguments in range_g call!') 117 | if step == 0: 118 | raise ValueError('step cannot be zero') 119 | if step > 0: 120 | def up(start, stop, step): 121 | while start < stop: 122 | yield start 123 | start += step 124 | return up(start, stop, step) 125 | else: 126 | def down(start, stop, step): 127 | while start > stop: 128 | yield start 129 | start += step 130 | return down(start, stop, step) 131 | 132 | range_l = range # range list 133 | 134 | def dict_keys(dikt): # dict keys list 135 | return dikt.keys() 136 | def dict_keys_g(dikt): # dict keys generator 137 | return dikt.keys() 138 | def dict_items_g(dikt): # dict items generator 139 | return dikt.items() 140 | 141 | from cStringIO import StringIO 142 | BytesIO = StringIO 143 | 144 | import os 145 | def _force_stream_binary(stream): 146 | """Force binary mode for stream on Windows.""" 147 | if os.name == 'nt': 148 | f_fileno = getattr(stream, 'fileno', None) 149 | if f_fileno: 150 | fileno = f_fileno() 151 | if fileno >= 0: 152 | import msvcrt 153 | msvcrt.setmode(fileno, os.O_BINARY) 154 | return stream 155 | 156 | def get_binary_stdout(): 157 | return _force_stream_binary(sys.stdout) 158 | 159 | def get_binary_stdin(): 160 | return _force_stream_binary(sys.stdin) 161 | -------------------------------------------------------------------------------- /intelhex/getsizeof.py: -------------------------------------------------------------------------------- 1 | # Recursive version sys.getsizeof(). Extendable with custom handlers. 2 | # Code from http://code.activestate.com/recipes/577504/ 3 | # Created by Raymond Hettinger on Fri, 17 Dec 2010 (MIT) 4 | 5 | import sys 6 | from itertools import chain 7 | from collections import deque 8 | try: 9 | from reprlib import repr 10 | except ImportError: 11 | pass 12 | 13 | def total_size(o, handlers={}, verbose=False): 14 | """ Returns the approximate memory footprint an object and all of its contents. 15 | 16 | Automatically finds the contents of the following builtin containers and 17 | their subclasses: tuple, list, deque, dict, set and frozenset. 18 | To search other containers, add handlers to iterate over their contents: 19 | 20 | handlers = {SomeContainerClass: iter, 21 | OtherContainerClass: OtherContainerClass.get_elements} 22 | 23 | """ 24 | dict_handler = lambda d: chain.from_iterable(d.items()) 25 | all_handlers = {tuple: iter, 26 | list: iter, 27 | deque: iter, 28 | dict: dict_handler, 29 | set: iter, 30 | frozenset: iter, 31 | } 32 | all_handlers.update(handlers) # user handlers take precedence 33 | seen = set() # track which object id's have already been seen 34 | default_size = sys.getsizeof(0) # estimate sizeof object without __sizeof__ 35 | 36 | def sizeof(o): 37 | if id(o) in seen: # do not double count the same object 38 | return 0 39 | seen.add(id(o)) 40 | s = sys.getsizeof(o, default_size) 41 | 42 | if verbose: 43 | print(s, type(o), repr(o))#, file=stderr) 44 | 45 | for typ, handler in all_handlers.items(): 46 | if isinstance(o, typ): 47 | s += sum(map(sizeof, handler(o))) 48 | break 49 | return s 50 | 51 | return sizeof(o) 52 | 53 | 54 | ##### Example call ##### 55 | 56 | if __name__ == '__main__': 57 | #d = dict(a=1, b=2, c=3, d=[4,5,6,7], e='a string of chars') 58 | print("dict 3 elements") 59 | d = {0:0xFF, 1:0xEE, 2:0xCC} 60 | print(total_size(d, verbose=True)) 61 | 62 | #print("array 3 elements") 63 | #import array 64 | #print(total_size(array.array('B', b'\x01\x02\x03'))) 65 | -------------------------------------------------------------------------------- /intelhex/scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-intelhex/intelhex/bdc202f437ab084036699d4cc9857d0844cd2dbe/intelhex/scripts/__init__.py -------------------------------------------------------------------------------- /intelhex/scripts/bin2hex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2008-2018 Alexander Belchenko 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | '''Intel HEX file format bin2hex convertor utility.''' 37 | 38 | VERSION = '2.3.0' 39 | 40 | def main(): 41 | import getopt 42 | import os 43 | import sys 44 | 45 | usage = '''Bin2Hex convertor utility. 46 | Usage: 47 | python bin2hex.py [options] INFILE [OUTFILE] 48 | 49 | Arguments: 50 | INFILE name of bin file for processing. 51 | Use '-' for reading from stdin. 52 | 53 | OUTFILE name of output file. If omitted then output 54 | will be writing to stdout. 55 | 56 | Options: 57 | -h, --help this help message. 58 | -v, --version version info. 59 | --offset=N offset for loading bin file (default: 0). 60 | ''' 61 | 62 | offset = 0 63 | 64 | try: 65 | opts, args = getopt.getopt(sys.argv[1:], "hv", 66 | ["help", "version", "offset="]) 67 | 68 | for o, a in opts: 69 | if o in ("-h", "--help"): 70 | print(usage) 71 | sys.exit(0) 72 | elif o in ("-v", "--version"): 73 | print(VERSION) 74 | sys.exit(0) 75 | elif o in ("--offset"): 76 | base = 10 77 | if a[:2].lower() == '0x': 78 | base = 16 79 | try: 80 | offset = int(a, base) 81 | except: 82 | raise getopt.GetoptError('Bad offset value') 83 | 84 | if not args: 85 | raise getopt.GetoptError('Input file is not specified') 86 | 87 | if len(args) > 2: 88 | raise getopt.GetoptError('Too many arguments') 89 | 90 | except getopt.GetoptError: 91 | msg = sys.exc_info()[1] # current exception 92 | txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool 93 | print(txt) 94 | print(usage) 95 | sys.exit(2) 96 | 97 | from intelhex import compat 98 | 99 | fin = args[0] 100 | if fin == '-': 101 | # read from stdin 102 | fin = compat.get_binary_stdin() 103 | elif not os.path.isfile(fin): 104 | txt = "ERROR: File not found: %s" % fin # that's required to get not-so-dumb result from 2to3 tool 105 | print(txt) 106 | sys.exit(1) 107 | 108 | if len(args) == 2: 109 | fout = args[1] 110 | else: 111 | # write to stdout 112 | fout = sys.stdout # compat.get_binary_stdout() 113 | 114 | from intelhex import bin2hex 115 | sys.exit(bin2hex(fin, fout, offset)) 116 | 117 | if __name__ == '__main__': 118 | main() 119 | -------------------------------------------------------------------------------- /intelhex/scripts/hex2bin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2005-2018 Alexander Belchenko 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | '''Intel HEX file format hex2bin convertor utility.''' 37 | 38 | VERSION = '2.3.0' 39 | 40 | def main(): 41 | import getopt 42 | import os 43 | import sys 44 | 45 | usage = '''Hex2Bin convertor utility. 46 | Usage: 47 | python hex2bin.py [options] INFILE [OUTFILE] 48 | 49 | Arguments: 50 | INFILE name of hex file for processing. 51 | OUTFILE name of output file. If omitted then output 52 | will be writing to stdout. 53 | 54 | Options: 55 | -h, --help this help message. 56 | -v, --version version info. 57 | -p, --pad=FF pad byte for empty spaces (ascii hex value). 58 | -r, --range=START:END specify address range for writing output 59 | (ascii hex value). 60 | Range can be in form 'START:' or ':END'. 61 | -l, --length=NNNN, 62 | -s, --size=NNNN size of output (decimal value). 63 | ''' 64 | 65 | pad = None 66 | start = None 67 | end = None 68 | size = None 69 | 70 | try: 71 | opts, args = getopt.getopt(sys.argv[1:], "hvp:r:l:s:", 72 | ["help", "version", "pad=", "range=", 73 | "length=", "size="]) 74 | 75 | for o, a in opts: 76 | if o in ("-h", "--help"): 77 | print(usage) 78 | sys.exit(0) 79 | elif o in ("-v", "--version"): 80 | print(VERSION) 81 | sys.exit(0) 82 | elif o in ("-p", "--pad"): 83 | try: 84 | pad = int(a, 16) & 0x0FF 85 | except: 86 | raise getopt.GetoptError('Bad pad value') 87 | elif o in ("-r", "--range"): 88 | try: 89 | l = a.split(":") 90 | if l[0] != '': 91 | start = int(l[0], 16) 92 | if l[1] != '': 93 | end = int(l[1], 16) 94 | except: 95 | raise getopt.GetoptError('Bad range value(s)') 96 | elif o in ("-l", "--lenght", "-s", "--size"): 97 | try: 98 | size = int(a, 10) 99 | except: 100 | raise getopt.GetoptError('Bad size value') 101 | 102 | if start != None and end != None and size != None: 103 | raise getopt.GetoptError('Cannot specify START:END and SIZE simultaneously') 104 | 105 | if not args: 106 | raise getopt.GetoptError('Hex file is not specified') 107 | 108 | if len(args) > 2: 109 | raise getopt.GetoptError('Too many arguments') 110 | 111 | except getopt.GetoptError: 112 | msg = sys.exc_info()[1] # current exception 113 | txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool 114 | print(txt) 115 | print(usage) 116 | sys.exit(2) 117 | 118 | fin = args[0] 119 | if not os.path.isfile(fin): 120 | txt = "ERROR: File not found: %s" % fin # that's required to get not-so-dumb result from 2to3 tool 121 | print(txt) 122 | sys.exit(1) 123 | 124 | if len(args) == 2: 125 | fout = args[1] 126 | else: 127 | # write to stdout 128 | from intelhex import compat 129 | fout = compat.get_binary_stdout() 130 | 131 | from intelhex import hex2bin 132 | sys.exit(hex2bin(fin, fout, start, end, size, pad)) 133 | 134 | if __name__ == '__main__': 135 | main() 136 | -------------------------------------------------------------------------------- /intelhex/scripts/hex2dump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2008-2018 Alexander Belchenko 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | """Show content of hex file as hexdump.""" 37 | 38 | VERSION = '2.3.0' 39 | 40 | USAGE = '''hex2dump: show content of hex file as hexdump. 41 | Usage: 42 | python hex2dump.py [options] HEXFILE 43 | 44 | Options: 45 | -h, --help this help message. 46 | -v, --version version info. 47 | -r, --range=START:END specify address range for dumping 48 | (ascii hex value). 49 | Range can be in form 'START:' or ':END'. 50 | --width=N dump N data bytes per line (default: 16). 51 | 52 | Arguments: 53 | HEXFILE name of hex file for processing (use '-' to read 54 | from stdin) 55 | ''' 56 | 57 | import sys 58 | 59 | DEFAULT_WIDTH = 16 60 | 61 | def hex2dump(hexfile, start=None, end=None, width=DEFAULT_WIDTH): 62 | import intelhex 63 | if hexfile == '-': 64 | hexfile = sys.stdin 65 | try: 66 | ih = intelhex.IntelHex(hexfile) 67 | except (IOError, intelhex.IntelHexError): 68 | e = sys.exc_info()[1] # current exception 69 | sys.stderr.write('Error reading file: %s\n' % e) 70 | return 1 71 | if not (start is None and end is None): 72 | ih = ih[slice(start,end)] 73 | ih.dump(tofile=sys.stdout, width=width) 74 | return 0 75 | 76 | 77 | def main(argv=None): 78 | import getopt 79 | 80 | if argv is None: 81 | argv = sys.argv[1:] 82 | 83 | start = None 84 | end = None 85 | width = DEFAULT_WIDTH 86 | 87 | try: 88 | opts, args = getopt.getopt(sys.argv[1:], "hvp:r:", 89 | ["help", "version", "range=", "width="]) 90 | for o, a in opts: 91 | if o in ("-h", "--help"): 92 | print(USAGE) 93 | return 0 94 | elif o in ("-v", "--version"): 95 | print(VERSION) 96 | return 0 97 | elif o in ("-r", "--range"): 98 | try: 99 | l = a.split(":") 100 | if l[0] != '': 101 | start = int(l[0], 16) 102 | if l[1] != '': 103 | end = int(l[1], 16) 104 | except: 105 | raise getopt.GetoptError('Bad range value(s)') 106 | elif o == "--width": 107 | try: 108 | width = int(a) 109 | if width < 1: 110 | raise ValueError 111 | except: 112 | raise getopt.GetoptError('Bad width value (%s)' % a) 113 | if not args: 114 | raise getopt.GetoptError('Hex file is not specified') 115 | if len(args) > 1: 116 | raise getopt.GetoptError('Too many arguments') 117 | except getopt.GetoptError: 118 | msg = sys.exc_info()[1] # current exception 119 | txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool 120 | print(txt) 121 | print(USAGE) 122 | return 2 123 | 124 | try: 125 | return hex2dump(args[0], start, end, width) 126 | except IOError: 127 | e = sys.exc_info()[1] # current exception 128 | import errno 129 | if e.errno not in (0, errno.EPIPE): 130 | raise 131 | 132 | 133 | if __name__ == '__main__': 134 | import sys 135 | sys.exit(main()) 136 | -------------------------------------------------------------------------------- /intelhex/scripts/hexdiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2011-2018 Alexander Belchenko 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | """Produce diff for 2 hex files using hex dump as string representation 37 | of compared data. 38 | """ 39 | 40 | VERSION = '2.3.0' 41 | 42 | USAGE = '''hexdiff: diff dumps of 2 hex files. 43 | Usage: 44 | python hexdiff.py [options] FILE1 FILE2 45 | 46 | Options: 47 | -h, --help this help message. 48 | -v, --version version info. 49 | ''' 50 | 51 | import sys 52 | 53 | 54 | def main(argv=None): 55 | import getopt 56 | 57 | if argv is None: 58 | argv = sys.argv[1:] 59 | try: 60 | opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) 61 | 62 | for o,a in opts: 63 | if o in ('-h', '--help'): 64 | print(USAGE) 65 | return 0 66 | elif o in ('-v', '--version'): 67 | print(VERSION) 68 | return 0 69 | 70 | except getopt.GetoptError: 71 | e = sys.exc_info()[1] # current exception 72 | sys.stderr.write(str(e)+"\n") 73 | sys.stderr.write(USAGE+"\n") 74 | return 1 75 | 76 | if len(args) != 2: 77 | sys.stderr.write("ERROR: You should specify 2 files to diff.\n") 78 | sys.stderr.write(USAGE+"\n") 79 | return 1 80 | 81 | fname1, fname2 = args 82 | 83 | from intelhex import IntelHex, diff_dumps 84 | ih1 = IntelHex(fname1) 85 | ih2 = IntelHex(fname2) 86 | diff_dumps(ih1, ih2, name1=fname1, name2=fname2) 87 | 88 | 89 | if __name__ == '__main__': 90 | sys.exit(main()) 91 | -------------------------------------------------------------------------------- /intelhex/scripts/hexinfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2015 Andrew Fernandes 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | """Summarize the information in a hex file by printing the execution 37 | start address (if any), and the address ranges covered by the 38 | data (if any), in YAML format. 39 | """ 40 | 41 | VERSION = '2.3.0' 42 | 43 | USAGE = '''hexinfo: summarize a hex file's contents. 44 | Usage: 45 | python hexinfo.py [options] FILE [ FILE ... ] 46 | 47 | Options: 48 | -h, --help this help message. 49 | -v, --version version info. 50 | ''' 51 | 52 | import sys 53 | 54 | INDENT = ' ' 55 | INLIST = '- ' 56 | 57 | def summarize_yaml(fname): 58 | print("{:s}file: '{:s}'".format(INLIST, fname)) 59 | from intelhex import IntelHex 60 | ih = IntelHex(fname) 61 | if ih.start_addr: 62 | keys = sorted(ih.start_addr.keys()) 63 | if keys == ['CS','IP']: 64 | entry = ih.start_addr['CS'] * 16 + ih.start_addr['IP'] 65 | elif keys == ['EIP']: 66 | entry = ih.start_addr['EIP'] 67 | else: 68 | raise RuntimeError("unknown 'IntelHex.start_addr' found") 69 | print("{:s}entry: 0x{:08X}".format(INDENT, entry)) 70 | segments = ih.segments() 71 | if segments: 72 | print("{:s}data:".format(INDENT)) 73 | for s in segments: 74 | print("{:s}{:s}{{ first: 0x{:08X}, last: 0x{:08X}, length: 0x{:08X} }}".format(INDENT, INLIST, s[0], s[1]-1, s[1]-s[0])) 75 | print("") 76 | 77 | def main(argv=None): 78 | import getopt 79 | 80 | if argv is None: 81 | argv = sys.argv[1:] 82 | try: 83 | opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) 84 | 85 | for o,a in opts: 86 | if o in ('-h', '--help'): 87 | print(USAGE) 88 | return 0 89 | elif o in ('-v', '--version'): 90 | print(VERSION) 91 | return 0 92 | 93 | except getopt.GetoptError: 94 | e = sys.exc_info()[1] # current exception 95 | sys.stderr.write(str(e)+"\n") 96 | sys.stderr.write(USAGE+"\n") 97 | return 1 98 | 99 | if len(args) < 1: 100 | sys.stderr.write("ERROR: You should specify one or more files to summarize.\n") 101 | sys.stderr.write(USAGE+"\n") 102 | return 1 103 | 104 | for fname in args: 105 | summarize_yaml(fname) 106 | 107 | if __name__ == '__main__': 108 | sys.exit(main()) 109 | -------------------------------------------------------------------------------- /intelhex/scripts/hexmerge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2008-2018 Alexander Belchenko 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | """Merge content of several hex files into one file.""" 37 | 38 | VERSION = '2.3.0' 39 | 40 | USAGE = '''hexmerge: merge content of hex files. 41 | Usage: 42 | python hexmerge.py [options] FILES... 43 | 44 | Options: 45 | -h, --help this help message. 46 | -v, --version version info. 47 | -o, --output=FILENAME output file name (emit output to stdout 48 | if option is not specified) 49 | -r, --range=START:END specify address range for output 50 | (ascii hex value). Both values are inclusive. 51 | Range can be in form 'START:' or ':END'. 52 | --no-start-addr Don't write start addr to output file. 53 | --overlap=METHOD What to do when data in files overlapped. 54 | Supported variants: 55 | * error -- stop and show error message (default) 56 | * ignore -- keep data from first file that 57 | contains data at overlapped address 58 | * replace -- use data from last file that 59 | contains data at overlapped address 60 | 61 | Arguments: 62 | FILES list of hex files for merging 63 | (use '-' to read content from stdin) 64 | 65 | You can specify address range for each file in the form: 66 | 67 | filename:START:END 68 | 69 | See description of range option above. 70 | 71 | You can omit START or END, so supported variants are: 72 | 73 | filename:START: read filename and use data starting from START addr 74 | filename::END read filename and use data till END addr 75 | 76 | Use entire file content: 77 | 78 | filename 79 | or 80 | filename:: 81 | ''' 82 | 83 | import sys 84 | 85 | 86 | def main(args=None): 87 | import getopt 88 | 89 | output = None 90 | start = None 91 | end = None 92 | write_start_addr = True 93 | overlap = 'error' 94 | 95 | if args is None: 96 | args = sys.argv[1:] 97 | try: 98 | opts, args = getopt.gnu_getopt(args, 'hvo:r:', 99 | ['help', 'version', 100 | 'output=', 'range=', 101 | 'no-start-addr', 'overlap=', 102 | ]) 103 | 104 | for o,a in opts: 105 | if o in ('-h', '--help'): 106 | print(USAGE) 107 | return 0 108 | elif o in ('-v', '--version'): 109 | print(VERSION) 110 | return 0 111 | elif o in ('-o', '--output'): 112 | output = a 113 | elif o in ("-r", "--range"): 114 | try: 115 | l = a.split(":") 116 | if l[0] != '': 117 | start = int(l[0], 16) 118 | if l[1] != '': 119 | end = int(l[1], 16) 120 | except (ValueError, IndexError): 121 | raise getopt.GetoptError('Bad range value(s)') 122 | elif o == '--no-start-addr': 123 | write_start_addr = False 124 | elif o == '--overlap': 125 | if a in ('error', 'ignore', 'replace'): 126 | overlap = a 127 | else: 128 | raise getopt.GetoptError('Bad overlap value') 129 | 130 | if len(args) == 0: 131 | raise getopt.GetoptError('You should specify file list') 132 | 133 | except getopt.GetoptError: 134 | e = sys.exc_info()[1] # current exception 135 | sys.stderr.write(str(e)+"\n") 136 | sys.stderr.write(USAGE+"\n") 137 | return 1 138 | 139 | import intelhex 140 | # TODO: move actual merge code into intelhex package as helper function 141 | # and write couple of tests for it. 142 | res = intelhex.IntelHex() 143 | 144 | def end_addr_inclusive(addr): 145 | if addr is not None: 146 | return addr + 1 147 | return addr 148 | 149 | for f in args: 150 | try: 151 | fname, fstart, fend = intelhex._get_file_and_addr_range(f) 152 | except intelhex._BadFileNotation: 153 | sys.stderr.write('Bad argument: "%s"\n' % f) 154 | sys.stderr.write(USAGE+"\n") 155 | return 1 156 | if fname == '-': 157 | fname = sys.stdin 158 | ih = intelhex.IntelHex(fname) 159 | if (fstart, fend) != (None, None): 160 | ih = ih[fstart:end_addr_inclusive(fend)] 161 | try: 162 | res.merge(ih, overlap) 163 | except intelhex.AddressOverlapError: 164 | e = sys.exc_info()[1] # current exception 165 | sys.stderr.write('Merging: '+fname+"\n") 166 | sys.stderr.write(str(e)+"\n") 167 | return 1 168 | 169 | if (start, end) != (None, None): 170 | res = res[start:end_addr_inclusive(end)] 171 | if output is None: 172 | output = sys.stdout 173 | res.write_hex_file(output, write_start_addr) 174 | return 0 175 | 176 | 177 | if __name__ == '__main__': 178 | sys.exit(main()) 179 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # requirements for development and release process 2 | twine # PyPi interaction, long description check in setup.py (twine check dist/intelhex-*.whl) 3 | rst2html # documentation generation 4 | sphinx # documentation generation 5 | docutils # sphinx and rest2html dependency 6 | pdoc # API documentation 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2008-2018, Alexander Belchenko 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, 7 | # with or without modification, are permitted provided 8 | # that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain 11 | # the above copyright notice, this list of conditions 12 | # and the following disclaimer. 13 | # * Redistributions in binary form must reproduce 14 | # the above copyright notice, this list of conditions 15 | # and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * Neither the name of the author nor the names 18 | # of its contributors may be used to endorse 19 | # or promote products derived from this software 20 | # without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 24 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 25 | # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 | # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 27 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 28 | # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 30 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 34 | # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | """Setup script for IntelHex.""" 37 | 38 | import os, sys, glob 39 | try: 40 | from setuptools import setup 41 | except ImportError: 42 | from distutils.core import setup 43 | from distutils.core import Command 44 | 45 | import intelhex, intelhex.__version__ 46 | 47 | LONG_DESCRIPTION = open('README.rst', 'r').read() 48 | 49 | SCRIPT_MODULES = [os.path.splitext(x)[0] for x in glob.glob('intelhex/scripts/*')] 50 | 51 | METADATA = dict( 52 | name='intelhex', 53 | version=intelhex.__version__.version_str, 54 | 55 | # Include the scripts as a subpackage 56 | packages=['intelhex', 'intelhex.scripts'], 57 | 58 | # For every script file, add an entrypoint 59 | entry_points={ 60 | "console_scripts": [ 61 | "{name}.py = intelhex.scripts.{name}:main".format(name=os.path.basename(x)) 62 | for x in SCRIPT_MODULES 63 | ] 64 | }, 65 | 66 | author='Alexander Belchenko', 67 | author_email='alexander.belchenko@gmail.com', 68 | maintainer='Bert van Hall', 69 | maintainer_email='bert.vanhall@gmx.de', 70 | url='https://github.com/python-intelhex/intelhex', 71 | description='Python library for Intel HEX files manipulations', 72 | long_description=LONG_DESCRIPTION, 73 | keywords='Intel HEX hex2bin HEX8', 74 | license='BSD', 75 | classifiers = [ 76 | 'Development Status :: 5 - Production/Stable', 77 | 'Environment :: Console', 78 | 'Intended Audience :: Developers', 79 | 'License :: OSI Approved :: BSD License', 80 | 'Operating System :: OS Independent', 81 | 'Programming Language :: Python', 82 | 'Programming Language :: Python :: 3', 83 | 'Programming Language :: Python :: 3.8', 84 | 'Programming Language :: Python :: 3.9', 85 | 'Programming Language :: Python :: 3.10', 86 | 'Programming Language :: Python :: 3.11', 87 | 'Programming Language :: Python :: 3.12', 88 | 'Programming Language :: Python :: 3.13', 89 | ], 90 | ) 91 | 92 | 93 | class test(Command): 94 | description = "unittest for intelhex" 95 | user_options = [] 96 | boolean_options = [] 97 | 98 | def initialize_options(self): 99 | pass 100 | 101 | def finalize_options(self): 102 | pass 103 | 104 | def run(self): 105 | import unittest 106 | import intelhex.test 107 | verbosity = 1 108 | if self.verbose: 109 | verbosity = 2 110 | suite = unittest.TestSuite() 111 | loader = unittest.TestLoader() 112 | suite.addTest(loader.loadTestsFromModule(intelhex.test)) 113 | runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=verbosity) 114 | result = runner.run(suite) 115 | if result.errors or result.failures: 116 | sys.exit(1) 117 | 118 | 119 | class bench(Command): 120 | description = "benchmarks for read/write HEX" 121 | user_options = [ 122 | ('repeat', 'n', 'repeat tests N times'), 123 | ('read', 'r', 'run only tests for read operation'), 124 | ('write', 'w', 'run only tests for write operation'), 125 | ] 126 | boolean_options = ['read', 'write'] 127 | 128 | def initialize_options(self): 129 | self.repeat = 3 130 | self.read = None 131 | self.write = None 132 | 133 | def finalize_options(self): 134 | if not self.read and not self.write: 135 | self.read = self.write = True 136 | 137 | def run(self): 138 | from intelhex.bench import Measure 139 | m = Measure(self.repeat, self.read, self.write) 140 | m.measure_all() 141 | m.print_report() 142 | 143 | 144 | def main(): 145 | metadata = METADATA.copy() 146 | metadata['cmdclass'] = { 147 | 'test': test, 148 | 'bench': bench, # bench is out of date 149 | } 150 | return setup(**metadata) 151 | 152 | if __name__ == '__main__': 153 | main() 154 | -------------------------------------------------------------------------------- /test-all-python.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TARGETS="3.8 3.9 3.10 3.11 3.12 3.13" 4 | DIR=$(dirname "$(readlink -f "$0")") 5 | 6 | _pyenv=$(command -v pyenv) 7 | [ -z "$_pyenv" ] && { echo "pyenv is missing on your system."; exit 1; } 8 | 9 | _tox=$(command -v tox) 10 | [ -z "$_tox" ] && { echo "tox is missing on your system."; exit 1; } 11 | 12 | cd "$DIR" 13 | 14 | echo "Installing (if needed) Python versions: $TARGETS" 15 | for version in $TARGETS ; do 16 | $_pyenv install --skip-existing $version 17 | done 18 | 19 | echo "Enabling Python versions from pyenv..." 20 | $_pyenv local $TARGETS 21 | 22 | echo "Running tox..." 23 | $_tox 24 | 25 | -------------------------------------------------------------------------------- /test_memory.py: -------------------------------------------------------------------------------- 1 | import intelhex 2 | 3 | ih = intelhex.IntelHex() 4 | for i in range(65536): ih[i] = i & 0x0FF 5 | 6 | print(len(ih)) 7 | 8 | print(ih.get_memory_size()) 9 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # tox (https://tox.readthedocs.io/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py38, py39, py310, py311, py312, py313 8 | skip_missing_interpreters = True 9 | tox_pyenv_fallback = False 10 | 11 | [testenv] 12 | deps = 13 | setuptools >= 72 14 | packaging >= 21 15 | 16 | commands = 17 | python setup.py test 18 | --------------------------------------------------------------------------------