pax_global_header00006660000000000000000000000064137436425140014523gustar00rootroot0000000000000052 comment=26c930ea613437f0d754583c910d4e780216d0c1 intelhex-2.3.0/000077500000000000000000000000001374364251400133455ustar00rootroot00000000000000intelhex-2.3.0/.bzrignore000066400000000000000000000003541374364251400153510ustar00rootroot00000000000000./api ./build ./dist ./docs/manual.html ./docs/manual/.build/doctrees/* ./docs/manual/.build/html/* ./intelhex-* ./MANIFEST __pycache__ tags fastimport.marks fastimport.stream wheelhouse ./.git ./.gitignore ./venv ./python.bat ./*.html intelhex-2.3.0/.gitignore000066400000000000000000000002601374364251400153330ustar00rootroot00000000000000*.egg-info *.pyc .bzr/* *.~* docs/manual.html docs/manual/.build/doctrees/* docs/manual/.build/html/* venv/ dist/ .tox/ MANIFEST python.bat NEWS.html README.html intelhex-2.3.0/AUTHORS.rst000066400000000000000000000010721374364251400152240ustar00rootroot00000000000000Author: Alexander Belchenko Copyright (C) Alexander Belchenko, 2005-2018 Special thanks to Bernhard Leiner for huge help in porting IntelHex library to Python 3. Contributors: * Alexander Belchenko * Alex Mueller * Andrew Fernandes * Bernhard Leiner * Enoch H. Wexler * Heiko Henkelmann * Henrik Maier * Masayuki Takeda * Morgan McClure * Nathan P. Stien * Piotr Korowacki * Reis Baltaoglu * Ryan Downing * Scott Armitage * Stefan Schmitt * Theo Sbrissa * Zachary Clifford * "durexyl" @ GitHub * "erki1993" @ GitHub * "mentaal" @ GitHub intelhex-2.3.0/LICENSE.txt000066400000000000000000000027701374364251400151760ustar00rootroot00000000000000Copyright (c) 2005-2018, Alexander Belchenko All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. intelhex-2.3.0/MANIFEST.in000066400000000000000000000001141374364251400150770ustar00rootroot00000000000000include AUTHORS.rst include LICENSE.txt include NEWS.rst include README.rst intelhex-2.3.0/Makefile000066400000000000000000000012221374364251400150020ustar00rootroot00000000000000PYTHON := python all: @echo Available targets: @echo clean - clean build directory @echo test - run unittest @echo epydoc - run epydoc to create API documentation (python 2) @echo wininst - Windows installer for Python @echo docs - build docs with ReST and Sphinx @echo wheel - build python wheel binary archive .PHONY: clean test epydoc wininst docs clean: $(PYTHON) setup.py clean -a test: $(PYTHON) setup.py test -q epydoc: epydoc.py -o api -v intelhex wininst: $(PYTHON) setup.py bdist_wininst -d. docs: rst2html5.py docs/manual.txt docs/manual.html make -C docs/manual html wheel: $(PYTHON) -m pip wheel -w dist . intelhex-2.3.0/NEWS.rst000066400000000000000000000162631374364251400146630ustar00rootroot00000000000000***************** IntelHex releases ***************** 2.3.0 (2020-10-20) ------------------ * Add ``IntelHex.find()`` method to find a given byte pattern. (Scott Armitage) * API changes: ``IntelHex.segments()`` method supports new optional parameter ``min_gap`` to allow consolidation of segments with small but existing gaps into a single segment. Default value is 1. (Ryan Downing) * API changes: ``IntelHex.tofile()`` now supports the optional ``byte_count`` parameter from ``IntelHex.write_hex_file()``. Only used if ``format = hex``. (Reis Baltaoglu) * Fix Python 3.9 compatibility issue with 'array' module (Piotr Korowacki) * Fix installation for Python version taking setup rather from setuptools than distutils (Theo Sbrissa) 2.2.1 (2018-01-30) ------------------ * Fixes for PyPI. 2.2 (2018-01-28) ---------------- * API changes: ``IntelHex.write_hex_file`` method: added support for new parameter: ``eolstyle = native | CRLF``. (Alexander Belchenko) * API changes: ``IntelHex.write_hex_file()`` method gets new optional parameter ``byte_count`` to specify how many bytes should be written to each data record in output file. Default value is 16. (patch from GitHub user erki1993) * Unit tests: Fixed xrange overflow test for Python 2.7 on 64-bit platforms. Use ``sys.maxint`` to ensure we trigger an exception. (Masayuki Takeda) * Script ``hexinfo.py``: Python 3 compatibility for processing start address dict keys. (patch from GitHub user mentaal) * Added ``get_memory_size()`` method: approx memory footprint of IntelHex object plus data. (Alexander Belchenko) * Better compatibility with Python 3. (Alexander Belchenko) 2.1 (2016-03-31) ---------------- * API changes: added ``IntelHex.segments()`` method that returns a list of ordered tuple objects, representing contiguous occupied data addresses. (Andrew Fernandes) * New command-line script ``hexinfo.py`` to print summary about hex files contents (file name, start address, address ranges covered by the data) in YAML format. (Andrew Fernandes) * Better Python 3 compatibility when ``hex2bin.py`` and ``bin2hex.py`` scripts are trying to read/write binary data from stdin or to stdout. (GitHub issue https://github.com/python-intelhex/intelhex/issues/4) * The main activity of the IntelHex project slowly drifting towards GitHub - the main social network for OSS developers. I'd really like to get some help from additional maintainer though. * API changes: ``IntelHex.dump()`` method gets new optional parameters: ``width``, ``withpadding`` to control generation of output text. (patch from GitHub user durexyl) * Script ``hex2dump.py`` gets new option ``--width`` to support corresponding parameter in ``IntelHex.dump()`` method. 2.0 (2015-04-12) ---------------- * The same codebase can be run on both Python 2 (2.4-2.7) and Python 3 (3.2+). No need to use 2to3. * ``compat.py``: provide more helper functions and aliases to reduce changes required to convert python 2 compatible sources to python 3. The code becomes quite ugly, but such compatibility has its price. * Python 3 compatibility: tobinstr should return bytes not unicode string (Bug #1212698). * Python 2: better support for long int addresses (over 2GB) (Bug #1408934) 1.5 (2013-08-02) ---------------- * API changes: Functions tobinarray/tobinstr/tobinfile: pad parameter is deprecated and will be removed in future releases. Use IntelHex.padding attribute instead, and don't pass pad as None explicitly please. If you need to use size parameter, then use syntax like that: ``ih.tobinarray(start=xxx, size=yyy)`` * API changes: Functions tobinarray/tobinstr/tobinfile: default value of pad is None now (was ``0xFF``) to allow using value of ``IntelHex.padding`` if no explicit pad specified. * Fixed bug: wrong ``getopt`` error handling in some scripts. (Thanks to Andy Mozhevilov for bug report) * PEP-8 style improvements. (Thanks to Stefan Schmitt) * ``IntelHex16bit.tobinarray`` method returns array of unsigned short (words) values. (Feature request from Stefan Schmitt) * Improved Python 3 compatibility (don't use old file() function). (Thanks to Luis Panadero Guardeño for bug report) 1.4 (2012-04-25) ---------------- * New feature: compare 2 hex files using hex dump as string representation. Feature available as worker function diff_dumps() and as command-line utility hexdiff.py (#627924). * Changes in the codebase suggested by 2to3 tool to provide compatibility with Python3. Now sources can be successfully converted to Python3 with 2to3 utility. See Python 3 notes in README.txt and documentation. (Thanks to Bernhard Leiner for his help) * Fixed bug #988148: ``IntelHex16bit`` should copy all public attributes from source IntelHex 8-bit object. (Thanks to Morgan McClure) 1.3 (2010-11-24) ---------------- * ``hex2dump``: show 0x7F character as dot for better compatibility with GNU less utility. * tobinarray, tobinfile, tobinstr: added size parameter. (Bug #408748) * fixed error in ``hexmerge.py`` script. (#676023) 1.2 (2009-08-04) ---------------- * Fixed bug 372620: tobinarray on empty file should return pad bytes when address range explicitly specified. * Improved docstrings: explicitly say that ``end`` param of to-* methods is always inclusive. (see bug #372625 for details). * Improved documentation on ``ih.dump(tofile)``. 1.1 (2009-03-12) ---------------- * Fixed bug in writing hex files with small chains of bytes * Improved Python 2.6 compatibility 1.0 (2009-01-01) ---------------- * Improved API, better performance * New User Manual (Zachary Clifford) 0.9 (2007-06-16) ---------------- New API release. * New API * Performance improvements: read hex file now ~45% faster 0.8.6 (2007-04-27) ------------------ Bug fixes and performance improvements. * ``IntelHex`` is able to read/write start address records (HEX record type ``03`` and ``05``). (fix bug #109872) * Backport (from 0.9 branch) of performance improvements for reading hex files 0.8.5 (2007-02-26) ------------------ BugFix Release. Performance improvements for writing big hex files when starting address is far from 0. Patch from Heiko Henkelmann. 0.8.4 (2007-02-26) ------------------ License added. The code is actually licensed under BSD, but there was no LICENSE file in sources archive. Added license file and explicit declaration in the source code. 0.8.3 (2006-09-05) ------------------ BugFix Release. Fix writing hex files with extended linear records (when address overlaps 64K boundary). Patch from Henrik Maier. 0.8.2 (2006-04-11) ------------------ Major improvements release. * Introduced new class ``IntelHex16bit`` for manipulate data as 16-bit values * You can manipulate data using dictionary-like interface (i.e. syntax like: ``ih[addr] = value``) * Added new method ``writefile(file)`` for writing data to hex file * Using unittest for testing functionality 0.6 (2006-03) ------------- Convertor engine ``hex2bin`` extracted to stand-alone function for using by external clients of intelhex. 0.5 (2005) ---------- First public release. intelhex-2.3.0/README.rst000066400000000000000000000033721374364251400150410ustar00rootroot00000000000000Python IntelHex library *********************** Introduction ------------ The Intel HEX file format is widely used in microprocessors and microcontrollers area (embedded systems etc) as the de facto standard for representation of code to be programmed into microelectronic devices. This work implements an ``intelhex`` Python library to read, write, create from scratch and manipulate data from Intel HEX file format. The distribution package also includes several convenience Python scripts, including "classic" ``hex2bin`` and ``bin2hex`` converters and more, those based on the library itself. Check the docs to know more. License ------- The code is distributed under BSD license, see `LICENSE.txt `_. In short: you can use IntelHex library in your project without *any* restrictions. Supported Python versions ------------------------- IntelHex library supports Python 3 (3.5 or later) only. The 2.2.1 release was the last one which has been checked against Python 2.7 and Python 3 until 3.5. Install ------- Install using ``pip`` (recommended, no separate download required):: pip install intelhex Download -------- * https://pypi.org/project/IntelHex/ * https://github.com/python-intelhex/intelhex/releases Source code, bug reports, patches --------------------------------- IntelHex on GitHub: https://github.com/python-intelhex/intelhex User manual ----------- User manual for IntelHex is available in the sources ``docs/manual/`` directory. You can browse User Manual online: https://readthedocs.org/projects/python-intelhex/ Changelog --------- See `NEWS.rst `_ intelhex-2.3.0/docs/000077500000000000000000000000001374364251400142755ustar00rootroot00000000000000intelhex-2.3.0/docs/intelhex.pdf000066400000000000000000000475371374364251400166300ustar00rootroot00000000000000%PDF-1.2 %쏢 7 0 obj <> stream xRr )f&Ơ_똶43t0B2~.HnS@rJhx/ Spt rʲZh`Rh :'5yNPBSFSZ@#OOmlKذԈoCeVRMJ~^50Y`UGc52!S{e*ec,|S ضv\eA"z"(]8>|y_aL6..b,0@_hJ'GEέWx|n Ix 6^Zi:c b|Ay)[v:Ӡd{͇-?As)?JuNz%[f;'Myβ^&2[k`,7d:ю O9tAgWW+iƲ> stream xWr6}W[ŅIIfik/ YHx*]%Izf$=8K?KEއ=͞ KyQuzr13 ۬ՀffM ~Րȫua~̎G,4|TVR [9s1~"Uڱ,y׌,>$C:rL^põ9>@fςCz(XHCeB/iknXYnA}au2VT%YQz<R:&bz(c <4oKt';ҫaqG9#qdQ* |Ѩ^V7cpQ![м'8/EpL8@rތ碊O} y;?>?F3`eu_E4we69endstream endobj 17 0 obj 1328 endobj 20 0 obj <> stream xWMsF WbK&vzL.ءH_`Hv2=T(l2I8u_W#DZG-e1{s-8"-3L*4R [lgG_)k߈מtl.T?S+"=@.I_~WM7RP-4a3FEfBa@$K'M\|>ZE#VEq2< $?υፎqs0F8f,݇7| E2fKz=r'{\vȃ H#9*A9=֧*)l//I2f6A@Wkj߶R!5Dv@@b~:ϐs?5 `UBXAi%=j۾ג')x}G=i\},[nXMev2Fw*[XeUY^ k7޼V-*ٗW /Ǿ$濸$hmC.7Y-[WGG3'%ȵHd q5 fA@-s8h,ٮZQw+RںxO1u@!{<8_Vw0M+V"Pb{|7жvu(߮5Up}R r]9o N O"sVA܆0 #/́\ e~/kSV(h_:9{Sqw:;eS6<|z3GyBR)>+8 69X8޺%vHjCyj .7ؾݹ&VcK/xGĻRB}FПmT"~վeuB Wf|Pɛ/ -,Z?^4k˅ '*&NwayjN-gnr#4Mo>=Moo~_opdXxvYVyu7I |`p+Z-h8D,1 61 [H~) /gРy,v~@M+(vJ,ג=X`;J4몓CpH< }eǿ_;endstream endobj 21 0 obj 1598 endobj 24 0 obj <> stream xWKSFW (<,l JcmH=3e{HG3p&}=W3La|/wC„b$ 2 XB$!LV#$|t&)=)K$xHOH\%,&l@8=θc)"#e3Kpenҹ#?X$FjjxP. }}d ʁA$Y;=Gv5<# `QA@8L`5=Vқ-,zPG#bLQlʣ d+z8 |c" "\Eqb]~ @Pl|ČÃ!Ul43cXe TSw ⃃Hp˅ 4zWv`s(7 <⷟>_Nތ\>uUWbƅbr+tH:Qʲ̸A- ћ ,[2pzqȽJB7Ñy&RR$aG\Pl%8٣9N/P] cG=wŇeepLiHQ@xV;b ؜&iJ!"a51w^pCm>;ڹd$[/%nkmdnQwuAm͓W@D)Z4TFoa$f`rcJ}z2=:sg⋿u]1}N ]]ژWOOvNm qmfLO%&5Un5_!ub19.;yӡ$\m^Đ*< uQ+ʙfcǸ*?UMq+\ׯch&K>yi] *5Y/jE9+ɥ"l_v Y L>֬1FctٙǼTv oGY(nEWXWQGmE^|yJ62׮ R0 m>?{2qwA?)\ܚ nW%zN ]Ӆ6טϛTL 2˼1`ZN=#9G="K.I0rogro ϥ.y~0loiӆTlabRv*9Hɠ`2\XyJ f_Rva^,Hd]y~*R8&b? m6Xsdo 3G3*?#ip~~<)fPnz.vtR7yp5]`9oyƌZ4aJlsJJ~.=߷c8ט;7QgWcgX %tkT*prMqV/:cl.5m>g8mP1'2 >A{ ?mc7=l^4|lc""Ɲ갼?i%Buy+ L,endstream endobj 25 0 obj 1574 endobj 28 0 obj <> stream xW[S6~ϯ8o!J$ ;˖CI6v98@vS!Y>=gR_2[0n>[dZp&pd*Y 3&G)ilp086EO''wW0HXFV,5^ No&$,d<ݜK#[,]aVM5(.F,-,lng:[-f2YcGz\ԑ03Y̢pg_Mm(+`@Hi8Z8]}˙6=UH .TaT<-ْLjw~@Ehv 4o-2/lV|^ٺpNV0Q^I)~܋rSP}>of<}x@ߓt.c%^ l `BcRʝ^,?VͲڇ,ڰ":VWAFuf*QS=i7dgߡެ,#5|$f é*d7W䢔%ϒ7\$]k.+ޯmѴGRg E#xQTX%6 Mta5ӱb݀ (z HWQ?B3t>0!ȕX[W\Tr?pDH7P~&q (Ma궒}6KdW/˶cM blqC$Odn[bLryARXxIinnL(T;SkuE8;^, GnjJHYYXz7E2Mendstream endobj 29 0 obj 1595 endobj 32 0 obj <> stream xWKoFWM6"227%v I d*QIv)J#y9;7 1@דoo bJw׋ J2X,'S`) ѱRXOHL&D"D :]| wiLQM "NRSw=T-t"2~ Tuaʪv o"#Z@Ē"t: i&ʩ45d5wpin֦`^i۠ jV[%\Ϡ`=@v{kU=ܕ1=̢yYg4U5^.}VZg+\5yjebE{cYgUݢ`K+j69 tj'7c)sch!Y51q]u1fXU9BLs=R!/,HY jQ\W߫baZVfU7r֘WVZc|8O($H,Dgp*f2`8K ?w3gp5SQ7=jS2ke,%%)Oz(]%*"$0{`-L ஬2aR-32MWl=Flg/tlVrӬ( BMQB* >}{yx*._)jtf[dDwn[չx{-6zG92!f{Dz$|C{K3>(L"PϚZ Zpmv Mt꡵vnk 8L }:'j~m|Gn*peց9Zuq8{MSx*J%]u(ҷYYqÊJ:sBk#,6wހs^QwDۃ, GB #>o}HFUBRk(/M75l<{gc5T@_~7av?@:>zYH*a s-$ :Mc*a=IkSnN8}3DG3[H pl{u6*6ڨpX(YSE9> stream xVKs6WMbܔ؞d4: MBQTH*gxd1ɩ⅄~@"ƁɫWR&hWv1  XJDLypXTQQ'g3`v҈p>c4L#c@=BHljNuSn!VwP/ag*[CQ>] *Fzᧆ ˺˜||6Qֆ IJFPe_4p[7]Yo *] '8+ݭϳu[g]yǶ^:|) uYO[&Pufe٦ 4e:-6ߵ-l1~ӛn]st v;R}`j_F<.8-7EeaI^-4Ù_X cY<G(%=]ߟ[Rb ra+ R) {_['j'[ĉJkшjjM:BED (S mI$ 884J$8.șZ8E)us.q4N/^ԑs06u+@*J>.i8PX,c()DtN}6cCx 'jd愻B(qВ ȩԤ։Qg4<䔈c?c$;4ñpθGЅ/7sPT"zЙY~'XoWVY qI&GPè! .endstream endobj 37 0 obj 1390 endobj 40 0 obj <> stream xVr8+&Jbps8J'HT E$dGƢcyEl~7f8x(I}9%@))$4|3 %y1M8&ki@"S>a4 x$ ̂%qec;JBNýj`Z{ˢJP=zYnBDN _j;]e %eA@~oY!A8,'Q$Փl Y+dP;~/fwߠkk!VK|.4Ωhz+V-R -4"Q8<+\yF]O8 y$VG+.\kvRԞ猤kOcbc8_{ln]kB7/W.a)]Tr j osƃ3lGfA,ɪe+wєp6k4E]1PpuO NM'M? z+I8?E,7v6tK;i5lظh׿_-LժUZRĮ֗?f<9?[R=r}]^3|#Ԧv4>?3H'0K<#bm@`:O6\eV@pâ}4|?~E%:Qgă{nĔdaz`z?j|%)ңA#I<aH؀I gcofg+q#*ߞba-&1˽={exHW͢Qq0?.fU04ن?θ}D&vX7:nBI8$qO4LGbÈcw3]l[lMfsC#V5Ǯ6Bb03iGShvUR++.Z zlrJ9npEw ]z&8<9Yx.ap/WLb# zdOJᩰ7p71 aKos|endstream endobj 41 0 obj 1027 endobj 44 0 obj <> stream xWIoFWؤgan FZꡀ/8HCq "$ J!s}o~2}7?fW)UJi{ߦJҔޏ?2j$KjDR"Ԅt6:I2(aZA"e`Z$e^HgqhG,R/>iWS8:}'QrݯP ?|ДmM\]~݉H긑4)1[ +x:;4v$4OՎ@GrT %61Y.pq -mIICcQ  ޼\MG%%]L]0&լQ\qt.'^b L`V˷&~Y*G󅒱^TΨ,`zoeR N]p.Ӎ۵${rr|0edsLt@ƨCIЏ`ǹ(%kxm[A ?Әntk|S(4{nm,NO{cŧ 1 4^Ht9n^wd`H n@5[ϭD 9H*_?ӧta!,k[@ghQ~Ƕ\Wx;ppg"/-or%Fjk" UןimݠÏqS.jYݱ CpFgvJQQ4 I{)C:7\"iqZ䟆~W?<ؼFO [[@ 9w0{^ Uy- G`!̄ Sw -#nIA a2bpL'%/x{`+o^L.Qi=p{yϰ7#$&cZ.R{ Xc\Zg(> stream xVYOH~ݞ, Y}X㘉gx0oaFHK$(}"zAN|M[F!%!b "5$a() I >~ bQbmEF9Mܽ&|$%JC8IuB+i󲁇f6+ y;xyoFp3OC{8 .&_`s)ŽGHCNQ\ % b wBH 0.+t_MψjIHKS K6hTPb)I (aBI Wʹwwb!0ߗ-<ԫE6w!|# Ũ.QJ,v?D>9c{8t:Ǟ#0_]^8IHK+bA f`qWprlM,7Eo$->WT XL+ŻK{it`7i3MEEUqJPnhJ)7s&[3C@+Z.?#)i]˄K¢0 o-ɅPO`-!wI`0_V.Y\zv (=`c&>}ؚ֒V:Xbv$?KvwiJg!蚵ܷ&d߿9_a}Q.%N {\}b~g 3dEʵif\E^T"VM\[ᆍݛXM\ e;/8]yU'  `o~[(S Np/b׳rsp4KS*b'w`ˠ\i){Le^D.TL3 g5yYyX^r4w*_9 zk]K(Fc?.d#Ky%_E<5n,G(+=OM1k:At8F! SǷJCx1j\yVOUb0jyVYM`“DI8,n#Ͽ!r>endstream endobj 49 0 obj 1193 endobj 6 0 obj <> /Contents 7 0 R >> endobj 15 0 obj <> /Contents 16 0 R >> endobj 19 0 obj <> /Contents 20 0 R >> endobj 23 0 obj <> /Contents 24 0 R >> endobj 27 0 obj <> /Contents 28 0 R >> endobj 31 0 obj <> /Contents 32 0 R >> endobj 35 0 obj <> /Contents 36 0 R >> endobj 39 0 obj <> /Contents 40 0 R >> endobj 43 0 obj <> /Contents 44 0 R >> endobj 47 0 obj <> /Contents 48 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 6 0 R 15 0 R 19 0 R 23 0 R 27 0 R 31 0 R 35 0 R 39 0 R 43 0 R 47 0 R ] /Count 10 >> endobj 1 0 obj <> endobj 5 0 obj <> endobj 13 0 obj <> endobj 14 0 obj <> endobj 4 0 obj <>stream xc``ddbffaaeccgWPRQ7421wtvq  KHJI+(*)ohljjnimkk8ig̜5g -^tيVY~æ[m߹k}>rSϞ;W^~[o߹{=~/^|͛/_~_~ @?Q endstream endobj 18 0 obj <> endobj 22 0 obj <> endobj 26 0 obj <> endobj 30 0 obj <> endobj 34 0 obj <> endobj 38 0 obj <> endobj 42 0 obj <> endobj 46 0 obj <> endobj 50 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 9 0 obj <> endobj 10 0 obj <> endobj 51 0 obj <> endobj 2 0 obj <>endobj xref 0 52 0000000000 65535 f 0000015655 00000 n 0000019093 00000 n 0000015532 00000 n 0000015828 00000 n 0000015703 00000 n 0000014076 00000 n 0000000015 00000 n 0000000640 00000 n 0000017796 00000 n 0000017857 00000 n 0000016594 00000 n 0000016655 00000 n 0000015755 00000 n 0000015785 00000 n 0000014236 00000 n 0000000659 00000 n 0000002059 00000 n 0000016229 00000 n 0000014380 00000 n 0000002080 00000 n 0000003750 00000 n 0000016261 00000 n 0000014524 00000 n 0000003771 00000 n 0000005417 00000 n 0000016304 00000 n 0000014668 00000 n 0000005438 00000 n 0000007105 00000 n 0000016347 00000 n 0000014812 00000 n 0000007126 00000 n 0000008581 00000 n 0000016390 00000 n 0000014956 00000 n 0000008602 00000 n 0000010064 00000 n 0000016433 00000 n 0000015100 00000 n 0000010085 00000 n 0000011184 00000 n 0000016476 00000 n 0000015244 00000 n 0000011205 00000 n 0000012769 00000 n 0000016508 00000 n 0000015388 00000 n 0000012790 00000 n 0000014055 00000 n 0000016551 00000 n 0000019010 00000 n trailer << /Size 52 /Root 1 0 R /Info 2 0 R >> startxref 19200 %%EOF intelhex-2.3.0/docs/manual.txt000066400000000000000000000034151374364251400163160ustar00rootroot00000000000000----------------------------------- Python IntelHex Library User Manual ----------------------------------- :Version: 2.3.0 .. contents:: .. sectnum:: Introduction ------------ .. include:: manual/part1-1.txt .. include:: manual/part1-2.txt .. include:: manual/part1-3.txt .. include:: manual/part1-4.txt Basic API and usage ------------------- .. include:: manual/part2-1.txt .. include:: manual/part2-2.txt .. include:: manual/part2-3.txt .. include:: manual/part2-4.txt .. include:: manual/part2-5.txt .. include:: manual/part2-6.txt .. include:: manual/part2-7.txt .. include:: manual/part2-8.txt Convenience Scripts ------------------- When IntelHex is installed and added to the system path, some scripts are available for usage. Each one is meant to be operated from the command line. They provide help if called incorrectly. .. include:: manual/part3-1.txt .. include:: manual/part3-2.txt .. include:: manual/part3-3.txt .. include:: manual/part3-4.txt .. include:: manual/part3-5.txt .. include:: manual/part3-6.txt Embedding into other projects ***************************** IntelHex should be easy to embed in other projects. The directory ``intelhex`` containing ``__init__.py`` can be directly placed in a depending project and used directly. From that project the same import statements described above can be used to make the library work. From other projects the import statement would change to:: >>> from myproject.intelhex import IntelHex Alternatively, the IntelHex package can be installed into the site-packages directory and used as a system package. In either case, IntelHex is distributed with a BSD-style license. This permits you to use it in any way you see fit, provided that the package is appropriately credited. .. include:: manual/appendix-a.txt intelhex-2.3.0/docs/manual/000077500000000000000000000000001374364251400155525ustar00rootroot00000000000000intelhex-2.3.0/docs/manual/Makefile000066400000000000000000000046131374364251400172160ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf .build/* html: # mkdir -p .build/html .build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html @echo @echo "Build finished. The HTML pages are in .build/html." pickle: mkdir -p .build/pickle .build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle @echo @echo "Build finished; now you can process the pickle files." web: pickle json: mkdir -p .build/json .build/doctrees $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: mkdir -p .build/htmlhelp .build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in .build/htmlhelp." latex: mkdir -p .build/latex .build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex @echo @echo "Build finished; the LaTeX files are in .build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p .build/changes .build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes @echo @echo "The overview file is in .build/changes." linkcheck: mkdir -p .build/linkcheck .build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in .build/linkcheck/output.txt." intelhex-2.3.0/docs/manual/appendix-a.txt000066400000000000000000000027531374364251400203500ustar00rootroot00000000000000Appendix A. IntelHex Errors Hierarchy ------------------------------------- * ``IntelHexError`` - base error * ``HexReaderError`` - general hex reader error * ``AddressOverlapError`` - data for the same address overlap * ``HexRecordError`` - hex record decoder base error * ``RecordLengthError`` - record has invalid length * ``RecordTypeError`` - record has invalid type (RECTYP) * ``RecordChecksumError`` - record checksum mismatch * ``EOFRecordError`` - invalid EOF record (type 01) * ``ExtendedAddressRecordError`` - extended address record base error * ``ExtendedSegmentAddressRecordError`` - invalid extended segment address record (type 02) * ``ExtendedLinearAddressRecordError`` - invalid extended linear address record (type 04) * ``StartAddressRecordError`` - start address record base error * ``StartSegmentAddressRecordError`` - invalid start segment address record (type 03) * ``StartLinearAddressRecordError`` - invalid start linear address record (type 05) * ``DuplicateStartAddressRecordError`` - start address record appears twice * ``InvalidStartAddressValueError`` - invalid value of start addr record * ``BadAccess16bit`` - not enough data to read 16 bit value * ``NotEnoughDataError`` - not enough data to read N contiguous bytes * ``EmptyIntelHexError`` - requested operation cannot be performed with empty object intelhex-2.3.0/docs/manual/conf.py000066400000000000000000000144401374364251400170540ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # IntelHex documentation build configuration file, created by # sphinx-quickstart on Thu Jan 01 06:25:34 2009. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('.')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix of source filenames. source_suffix = '.txt' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Python IntelHex library' copyright = '2005-2018, Alexander Belchenko' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # let's use it right from the intelhex package exec(open('../../intelhex/__version__.py').read()) VERSION = version_str # from __version__.py # # The short X.Y version. version = VERSION # The full version, including alpha/beta/rc tags. release = VERSION # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['.build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- #html_theme = 'classic' # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. #html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['.static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'PythonIntelhexLibrary' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'IntelHex.tex', 'Python IntelHex library Documentation', 'Alexander Belchenko', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True intelhex-2.3.0/docs/manual/index.txt000066400000000000000000000007601374364251400174250ustar00rootroot00000000000000.. IntelHex documentation master file, created by sphinx-quickstart on Thu Jan 01 06:25:34 2009. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Python IntelHex Library User Manual ----------------------------------- Contents: .. toctree:: :maxdepth: 3 part1 part2 part3 part4 appendix-a Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` * :ref:`search` intelhex-2.3.0/docs/manual/part1-1.txt000066400000000000000000000043461374364251400175070ustar00rootroot00000000000000About ***** The Intel HEX file format is widely used in microprocessors and microcontrollers area as the de facto standard for code representation for microelectronic devices programming. This work implements an **intelhex** Python library to read, write, create from scratch and manipulate data from HEX (also known as Intel HEX) file format. These operations are provided by ``IntelHex`` class. The distribution package also includes several convenience Python scripts to do basic tasks that utilize this library. The ``bin2hex.py`` script converts binary data to HEX, and the ``hex2bin.py`` works the other direction. ``hex2dump.py`` converts data from HEX to a hexdump which is useful for inspecting data, and ``hexmerge.py`` merges multiple HEX files into one. In addition you can try inspecting differences between two HEX files with ``hexdiff.py`` utility which uses dump output similar to ``hex2dump.py``. You can find IntelHex library on PyPI: https://pypi.python.org/pypi/IntelHex on GitHub: https://github.com/python-intelhex/intelhex Motivation ~~~~~~~~~~ This work was partially inspired by SRecord_ software at the moment when I stuck with its limitations and unintuitive behavior (for me personally). So I've made this library and related tools which give me full control over data and HEX file creation. Not the best reason to start yet another project. But, as you probably know, nothing is better than scratch our own itches, especially if you want to re-implement something in your favorite programming language. Over the years it turned out that my small python library was very useful to many people, and allowed them not only to create utilities to manipulate with HEX files, but also to create custom bootloaders for their devices. I started writing this library in 2005, and now more than 10 years later it's still alive and useful to other developers. That keeps me working on improving the code, even though I don't work on embedding systems for some time. If you find IntelHex library useful, please send me email and tell a bit about how you're using it and in which projects/areas. That will not only satisfy my curiosity but also will help me to keep working on this project. .. _SRecord: http://srecord.sourceforge.net/ intelhex-2.3.0/docs/manual/part1-2.txt000066400000000000000000000001411374364251400174750ustar00rootroot00000000000000License ******* The code distributed under the BSD license. See LICENSE.txt in sources archive. intelhex-2.3.0/docs/manual/part1-3.txt000066400000000000000000000054301374364251400175040ustar00rootroot00000000000000Installation ************ Note: some commands below have `sudo` as first word. It's required only on Linux or Mac OS X. Omit the prefix if you're on Windows. Installing with pip ~~~~~~~~~~~~~~~~~~~ If you just need IntelHex library installed as your system python library then it's better to use modern tool called ``pip`` (http://www.pip-installer.org/en/latest/) to install with the command: sudo pip install intelhex The latest versions of Python interpreter (like 2.7.9, or 3.4.x and later) have pip in the standard installer/distribution. The simplest way to check whether you have pip installed is to check command (for Python 2.5+): python -m pip list If this does not work, you can install pip by downloading single file from this page: https://pip.pypa.io/en/latest/installing.html#install-pip and run it as sudo python get-pip.py Download sources ~~~~~~~~~~~~~~~~ You can get archive with the latest released code, docs and other files from PyPI: https://pypi.python.org/pypi/IntelHex You can get the archive with the released code from corresponding section of GitHub project: https://github.com/python-intelhex/intelhex/releases or even unreleased bleeding edge code from GitHub page: https://github.com/python-intelhex/intelhex Use the corresponding menu item in the right-hand side bar on that page (e.g. "Download ZIP"). Get source code with git ~~~~~~~~~~~~~~~~~~~~~~~~ git clone https://github.com/python-intelhex/intelhex.git Install from sources ~~~~~~~~~~~~~~~~~~~~ IntelHex has got stadard setup.py installation script. Assuming Python is properly installed on your platform, installation should require just running of the following command from the root directory of the sources:: sudo python setup.py install This will install the intelhex package into your system's site-packages directory and place the helper scripts in your Python site-packages binaries directory. Once it is done, any other Python scripts or modules should be able to import the package using:: >>> from intelhex import IntelHex The scripts should be in your PATH so that they could be called from anywhere in the file system. See the Python distutils website for more information, or try typing, ``python setup.py --help`` from the root directory of the sources. Note for Windows users ~~~~~~~~~~~~~~~~~~~~~~ Please note that for historical reasons IntelHex library doesn't use setuptools for installation task, therefore we don't create exe-wrappers for helper scripts as hex2bin.py, bin2hex.py and other mentioned in this documentation (see section Convenience Scripts). You can find these scripts in your python Script directory (usually `C:\\PythonXY\\Scripts`). You need either to create batch file to run them, or use Python interpreter: python C:\\PythonXY\\Scripts\\hex2bin.py ... intelhex-2.3.0/docs/manual/part1-4.txt000066400000000000000000000020201374364251400174750ustar00rootroot00000000000000Python 3 compatibility ********************** Intelhex library supports Python 2 (2.4-2.7) and Python 3 (3.2-3.6) without external libraries or 2to3 tool. Enjoy. I've successfully run unit tests of IntelHex against following versions of Python: 2.4.4, 2.5.4, 2.6.6 (32/64 bits), 2.7.9 (32/64 bits), 3.2.5 (32/64 bits), 3.3.5 (32/64 bits), 3.4.3 (32/64 bits), 3.5.0a3 (32/64 bits), and also PyPy 2.5.1 (which is Python 2.7.9) Which Python version should you use? ------------------------------------ If you don't really know which version of Python (2 or 3) you should use with IntelHex then please check following pre-requisites: 1) Are you already have some Python installed on your computer and that version is supported by IntelHex (see above)? 2) Should you use another third-party libraries? If so, check their requirements. 3) Python 2.7 is the safest choice so far, but if you have a chance then try latest stable Python 3 version. 4) Updated by January 2018 - Python 3 is highly recommended. intelhex-2.3.0/docs/manual/part1.txt000066400000000000000000000000761374364251400173450ustar00rootroot00000000000000Introduction ------------ .. toctree:: :glob: part1-* intelhex-2.3.0/docs/manual/part2-1.txt000066400000000000000000000015321374364251400175020ustar00rootroot00000000000000Initializing the class ********************** Example of typical initialization of ``IntelHex`` class:: >>> from intelhex import IntelHex >>> ih = IntelHex("foo.hex") In the second line we are creating an instance of the class. The constructor optionally takes data to initialize the class. This can be the name of the HEX file, a file-like object, a dictionary, or another instance of IntelHex. If specified, this source is automatically read and decoded. Because of the flexibility of file-like objects in Python, objects like sys.stdin can be used. If the source is another instance of IntelHex, the new object will become a copy of the source. Finally, a Python dictionary may be specified. This dictionary should have keys equal to memory locations and values equal to the data stored in those locations. See the docstrings for more details. intelhex-2.3.0/docs/manual/part2-2.txt000066400000000000000000000035511374364251400175060ustar00rootroot00000000000000Reading data ************ Once created, an IntelHex object can be loaded with data. This is only necessary if "source" was unspecified in the constructor. You can also load data several times (but if addresses in those files overlap you get exception ``AddressOverlapError``). This error is only raised when reading from hex files. When reading from other formats, without explicitly calling ``merge``, the data will be overwritten. E.g.:: >>> from intelhex import IntelHex >>> ih = IntelHex() # create empty object >>> ih.loadhex('foo.hex') # load from hex >>> ih.loadfile('bar.hex',format='hex') # also load from hex >>> ih.fromfile('bar.hex',format='hex') # also load from hex **NOTE**: using ``IntelHex.fromfile`` is recommended way. All of the above examples will read from HEX files. IntelHex also supports reading straight binary files. For example:: >>> from intelhex import IntelHex >>> ih = IntelHex() # create empty object >>> ih.loadbin('foo.bin') # load from bin >>> ih.fromfile('bar.bin',format='bin') # also load from bin >>> ih.loadbin('baz.bin',offset=0x1000) # load binary data and place them >>> # starting with specified offset Finally, data can be loaded from an appropriate Python dictionary. This will permit you to store the data in an IntelHex object to a builtin dictionary and restore the object at a later time. For example:: >>> from intelhex import IntelHex >>> ih = IntelHex('foo.hex') # create empty object >>> pydict = ih.todict() # dump contents to pydict ...do something with the dictionary... :: >>> newIH = IntelHex(pydict) # recreate object with dict >>> another = IntelHex() # make a blank instance >>> another.fromdict(pydict) # now another is the same as newIH intelhex-2.3.0/docs/manual/part2-3.txt000066400000000000000000000024021374364251400175010ustar00rootroot00000000000000Basic data inspection ********************* You can get or modify some data by address in the usual way: via Python indexing operations:: >>> print ih[0] # read data from address 0 When you need to work with 16-bit data stored in 8-bit Intel HEX files you need to use class ``IntelHex16bit``. This class is derived from IntelHex and has all its methods. Some of methods have been modified to implement 16-bit behaviour. **NOTE:** ``IntelHex16bit`` class despite its name **can't handle** real HEX16 files. Initially ``IntelHex16bit`` has been created as helper class to work with HEX files for Microchip's PIC16 family firmware. It may or may not work for your purpose. This class assumes the data is in Little Endian byte order. The data can be accessed exactly like above, except that data returned will be 16 bits, and the addresses should be word addresses. Another useful inspection tool is the dump command. This will output the entire contents of the hex file to stdout or to a specified file object like so:: >>> ih.dump() # dump contents of ih to stdout in tabular hexdump format >>> f = open('hexdump.txt', 'w') # open file for writing >>> ih.dump(f) # dump to file object >>> f.close() # close file intelhex-2.3.0/docs/manual/part2-4.txt000066400000000000000000000062231374364251400175070ustar00rootroot00000000000000More data inspection ******************** IntelHex provides some metadata about the hex file it contains. To obtain address limits use methods ``.minaddr()`` and ``.maxaddr()``. These are computed based on the lowest and highest used memory spaces respectively. Some linkers write to produced HEX file information about start address (either record 03 or 05). IntelHex is able correctly read such records and store information internally in ``start_addr`` attribute that itself is either ``None`` or a dictionary with the address value(s). When input HEX file contains record type 03 (Start Segment Address Record), ``start_addr`` takes value:: {'CS': XXX, 'IP': YYY} Here: * ``XXX`` is value of CS register * ``YYY`` is value of IP register To obtain or change ``CS`` or ``IP`` value you need to use their names as keys for ``start_addr`` dictionary:: >>> ih = IntelHex('file_with_03.hex') >>> print ih.start_addr['CS'] >>> print ih.start_addr['IP'] When input HEX file contains record type 05 (Start Linear Address Record), ``start_addr`` takes value:: {'EIP': ZZZ} Here ``ZZZ`` is value of EIP register. Example:: >>> ih = IntelHex('file_with_05.hex') >>> print ih.start_addr['EIP'] You can manually set required start address:: >>> ih.start_addr = {'CS': 0x1234, 'IP': 0x5678} >>> ih.start_addr = {'EIP': 0x12345678} To delete start address info give value ``None`` or empty dictionary:: >>> ih.start_addr = None >>> ih.start_addr = {} When you write data to HEX file you can disable writing start address with additional argument ``write_start_addr``:: >>> ih.write_hex_file('out.hex') # by default writing start address >>> ih.write_hex_file('out.hex', True) # as above >>> ih.write_hex_file('out.hex', False) # don't write start address When ``start_addr`` is ``None`` or an empty dictionary nothing will be written regardless of ``write_start_addr`` argument value. For more information about start address, please see the Intel Hex file format specification. Because Intel Hex files do not specify every location in memory, it is necessary to have a padding byte defined. Whenever a read is attempted from an address that is unspecified, the padding byte is returned. This default data is set via attribute ``.padding`` of class instance. This defaults to '0xFF', but it can be changed by the user like so:: >>> print ih[0] # prints 0xFF because this location is blank >>> ih.padding = 0x00 # change padding byte >>> print ih[0] # prints 0x00 because this location is blank Summarizing the data chunks ~~~~~~~~~~~~~~~~~~~~~~~~~~~ One of the more useful properties of HEX files is that they can specify data in discontinuous segments. There are two main methods to summarize which data addresses are occupied:: >>> ih.addresses() >>> ih.segments() The first will return a list of occupied data addresses in sorted order. The second will return a list of 2-tuples objects, in sorted order, representing start and stop addresses of contiguous segment chunks of occupied data. Those 2-tuples are suitable to be used as ``start`` and ``stop`` arguments of standard ``range`` function. intelhex-2.3.0/docs/manual/part2-5.txt000066400000000000000000000046301374364251400175100ustar00rootroot00000000000000Writing out data **************** Data contained in IntelHex can be written out in a few different formats, including HEX, bin, or python dictionaries. You can write out HEX data contained in object by method ``.write_hex_file(f)``. Parameter ``f`` should be filename or file-like object. Note that this can include builtins like sys.stdout. Also you can use the universal tofile. To convert data of IntelHex object to HEX8 file format without actually saving it to disk you can use the builtin StringIO file-like object, e.g.:: >>> from cStringIO import StringIO >>> from intelhex import IntelHex >>> ih = IntelHex() >>> ih[0] = 0x55 >>> sio = StringIO() >>> ih.write_hex_file(sio) >>> hexstr = sio.getvalue() >>> sio.close() Variable ``hexstr`` will contain a string with the content of a HEX8 file. You can customize hex file output with following optional arguments to ``write_hex_file`` call: * ``write_start_addr`` - you can disable start address record in new hex file; * ``eolstyle`` - you can force ``CRLF`` line endings in new hex file. * ``byte_count`` - you can control how many bytes should be written to each data record. Data converters ~~~~~~~~~~~~~~~ To write data as a hex file with default settings you also can use universal method ``tofile``:: # the code below is the same as "ih.write_hex_file(sio)" >>> ih.tofile(sio, format='hex') Class IntelHex has several methods for converting data of IntelHex objects into binary form: * ``tobinarray`` (returns array of unsigned char bytes); * ``tobinstr`` (returns string of bytes); * ``tobinfile`` (convert content to binary form and write to file). Example:: >>> from intelhex import IntelHex >>> ih = IntelHex("foo.hex") >>> ih.tobinfile("foo.bin") Also you can use universal method ``tofile`` to write data as binary file:: >>> ih.tofile("foo.bin", format='bin') Writing data in chunks ~~~~~~~~~~~~~~~~~~~~~~ If you need to get binary data from IntelHex as series of chunks then you can pass to ``tobinarray``/``tobinstr`` methods either start/end addresses or start address and required size of the chunk. This could be useful if you're creating Eeprom/Flash IC programmer or bootloader. :: EEPROM_SIZE = 8192 # 8K bytes BLOCK_SIZE = 128 # 128 bytes for addr in range(0, EEPROM_SIZE, BLOCK_SIZE): eeprom.i2c_write(addr, ih.tobinarray(start=addr, size=BLOCK_SIZE)) intelhex-2.3.0/docs/manual/part2-6.txt000066400000000000000000000016711374364251400175130ustar00rootroot00000000000000Merging two hex files ********************* IntelHex supports merging two different hex files into one. This is done by initializing one IntelHex object with data and calling its merge method:: >>> original = IntelHex("foo.hex") >>> new = IntelHex("bar.hex") >>> original.merge(new, overlap='replace') Now original will contain foo.hex merged with bar.hex. The overlap parameter specifies what should be done when memory locations in the original object overlap with locations in the new object. It can take three options: * ``error`` - stop and raise an exception (default) * ``ignore`` - keep data from the original that contains data at overlapped address * ``replace`` - use data from the new object that contains data at overlapped address You can merge only part of other hex file by using slice index notation:: >>> original = IntelHex("foo.hex") >>> new = IntelHex("bar.hex") >>> original.merge(new[0x0F:0x3F]) intelhex-2.3.0/docs/manual/part2-7.txt000066400000000000000000000040511374364251400175070ustar00rootroot00000000000000Creating Intel Hex files from scratch ************************************* Some facilities are provided for synthesizing Intel Hex files from scratch. These can also be used to modify a hex file in place. Just as you can use indexed reads to retrieve data, you can use indexed writes to modify the file, e.g.:: >>> ih[1] = 0x55 # modify data at address 1 A common usage would be to read a hex file with IntelHex, use the above syntax to modify it, and then write out the modified file. The above command can be used on an empty IntelHex object to synthesize a hex file from scratch. Another important feature helps work with C strings via ``putsz``/``getsz``, e.g.:: >>> ih.putsz(0x100,"A string") This places "A string" followed by a terminating NULL character in address 0x100. The ``getsz`` method similarly retrieves a null terminated string from a specified address like so:: >>> ih.getsz(0x100) This should retrieve the "A string" we stored earlier. Additionally, ``puts``/``gets`` can be used to retrieve strings of specific length from the hex file like so:: >>> ih.puts(0x100,"data") >>> ih.gets(0x100,4) The second command should retrieve the characters 'd','a','t','a'. These methods do not use terminating NULLs, so the data need not be interpreted as a string. One usage of these commands comes from the Python ``struct`` module. This module allows the programmer to specify a C struct, and it will allow conversion between the variables and a packed string representation for use with ``puts``/``gets``. For example, suppose we need to deal with a struct containing a char, a short, and a float:: >>> import struct >>> formatstring = 'chf' # see Python docs for full list of valid struct formats >>> ih.puts(0x10, struct.pack(formatstring,'a',24,18.6)) # put data in hex file >>> (mychar,myshort,myfloat) = struct.unpack(formatstring, ih.gets(0x10,7) Now ``mychar``, ``myshort``, and ``myfloat`` should contain the original data (assuming ``sizeof(float) = 4`` on this platform, otherwise the size may be wrong). intelhex-2.3.0/docs/manual/part2-8.txt000066400000000000000000000013761374364251400175170ustar00rootroot00000000000000Handling errors *************** Many of the methods in IntelHex throw Python exceptions during error conditions. These can be caught and handled using try...except blocks like so:: >>> try: ... mystring = ih.gets(0x20,20) >>> except intelhex.NotEnoughDataError: ... print "There is not enough data at that location" See the API docs for information about errors raised by IntelHex. They are all subclasses of IntelHexError, so the except block above could be used to catch all of them. If your application has a way to gracefully handle these exceptions, they should be caught. Otherwise, Python will exit with a descriptive error message about the uncaught exception. See `Appendix A `_ for error classes hierarchy. intelhex-2.3.0/docs/manual/part2.txt000066400000000000000000000001141374364251400173370ustar00rootroot00000000000000Basic API and usage ------------------- .. toctree:: :glob: part2-* intelhex-2.3.0/docs/manual/part3-1.txt000066400000000000000000000017301374364251400175030ustar00rootroot00000000000000Script ``hex2bin.py`` ********************* You can use ``hex2bin.py`` as handy hex-to-bin converter. This script is just a frontend for function ``hex2bin`` from intelhex package. :: Usage: python hex2bin.py [options] INFILE [OUTFILE] Arguments: INFILE name of hex file for processing. OUTFILE name of output file. If omitted then output will be writing to stdout. Options: -h, --help this help message. -p, --pad=FF pad byte for empty spaces (hex value). -r, --range=START:END specify address range for writing output (hex value). Range can be in form 'START:' or ':END'. -l, --length=NNNN, -s, --size=NNNN size of output (decimal value). Per example, converting content of foo.hex to foo.bin addresses from 0 to FF:: $ python hex2bin.py -r 0000:00FF foo.hex Or (equivalent):: $ python hex2bin.py -r 0000: -s 256 foo.hex intelhex-2.3.0/docs/manual/part3-2.txt000066400000000000000000000012031374364251400174770ustar00rootroot00000000000000Script ``bin2hex.py`` ********************************* You can use bin2hex.py as simple bin-to-hex convertor. This script is just a frontend for function ``bin2hex`` from intelhex package. :: Usage: python bin2hex.py [options] INFILE [OUTFILE] Arguments: INFILE name of bin file for processing. Use '-' for reading from stdin. OUTFILE name of output file. If omitted then output will be writing to stdout. Options: -h, --help this help message. --offset=N offset for loading bin file (default: 0). intelhex-2.3.0/docs/manual/part3-3.txt000066400000000000000000000011261374364251400175040ustar00rootroot00000000000000Script ``hex2dump.py`` *********************** This is a script to dump a hex file to a hexdump format. It is a frontend for ``dump`` function in ``IntelHex`` class. :: Usage: python hex2dump.py [options] HEXFILE Options: -h, --help this help message. -r, --range=START:END specify address range for dumping (ascii hex value). Range can be in form 'START:' or ':END'. Arguments: HEXFILE name of hex file for processing (use '-' to read from stdin) intelhex-2.3.0/docs/manual/part3-4.txt000066400000000000000000000033271374364251400175120ustar00rootroot00000000000000Script ``hexmerge.py`` ********************************** This is a script to merge two different hex files. It is a frontend for the ``merge`` function in ``IntelHex`` class. :: Usage: python hexmerge.py [options] FILES... Options: -h, --help this help message. -o, --output=FILENAME output file name (emit output to stdout if option is not specified) -r, --range=START:END specify address range for output (ascii hex value). Range can be in form 'START:' or ':END'. --no-start-addr Don't write start addr to output file. --overlap=METHOD What to do when data in files overlapped. Supported variants: * error -- stop and show error message (default) * ignore -- keep data from first file that contains data at overlapped address * replace -- use data from last file that contains data at overlapped address Arguments: FILES list of hex files for merging (use '-' to read content from stdin) You can specify address range for each file in the form: filename:START:END See description of range option above. You can omit START or END, so supported variants are: filename:START: read filename and use data starting from START addr filename::END read filename and use data till END addr Use entire file content: filename or filename:: intelhex-2.3.0/docs/manual/part3-5.txt000066400000000000000000000007611374364251400175120ustar00rootroot00000000000000Script ``hexdiff.py`` ********************* This is a script to diff context of two hex files. To create human-readable diff this utility converts both hex files to hex dumps first, and then utility compares those hex dumps and produces unified diff output for changes. :: hexdiff: diff dumps of 2 hex files. Usage: python hexdiff.py [options] FILE1 FILE2 Options: -h, --help this help message. -v, --version version info. intelhex-2.3.0/docs/manual/part3-6.txt000066400000000000000000000010151374364251400175040ustar00rootroot00000000000000Script ``hexinfo.py`` ********************* This is a script to summarize a hex file's contents. This utility creates a YAML-formatted, human-readable summary of a set of HEX files. It includes the file name, execution start address (if any), and the address ranges covered by the data (if any). :: hexinfo: summarize a hex file's contents. Usage: python hexinfo.py [options] FILE [ FILE ... ] Options: -h, --help this help message. -v, --version version info. intelhex-2.3.0/docs/manual/part3.txt000066400000000000000000000004231374364251400173430ustar00rootroot00000000000000Convenience Scripts ------------------- When IntelHex is installed and added to the system path, some scripts are available for usage. Each one is meant to be operated from the command line. They provide help if called incorrectly. .. toctree:: :glob: part3-* intelhex-2.3.0/docs/manual/part4.txt000066400000000000000000000020571374364251400173510ustar00rootroot00000000000000Embedding into other projects ***************************** IntelHex should be easy to embed in other projects. The directory ``intelhex`` containing ``__init__.py`` can be directly placed in a depending project and used directly. From that project the same import statements described above can be used to make the library work. From other projects the import statement would change to:: >>> from myproject.intelhex import IntelHex Alternatively, the IntelHex package can be installed into the site-packages directory and used as a system package. In either case, IntelHex is distributed with a BSD-style license. This permits you to use it in any way you see fit, provided that the package is appropriately credited. If you're using IntelHex library in your open-source project, or your company created freely available set of tools, utilities or sdk based on IntelHex library - please, send me email (to alexander belchenko at gmail com) and tell something about your project. I'd like to add name of your project/company to page "Who Uses IntelHex". intelhex-2.3.0/intelhex/000077500000000000000000000000001374364251400151655ustar00rootroot00000000000000intelhex-2.3.0/intelhex/__init__.py000066400000000000000000001456071374364251400173130ustar00rootroot00000000000000# Copyright (c) 2005-2018, Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''Intel HEX format manipulation library.''' __docformat__ = "javadoc" from array import array from binascii import hexlify, unhexlify from bisect import bisect_right import os import sys from intelhex.compat import ( IntTypes, StrType, StringIO, array_tobytes, asbytes, asstr, dict_items_g, dict_keys, dict_keys_g, range_g, range_l, ) from intelhex.getsizeof import total_size class _DeprecatedParam(object): pass _DEPRECATED = _DeprecatedParam() class IntelHex(object): ''' Intel HEX file reader. ''' def __init__(self, source=None): ''' Constructor. If source specified, object will be initialized with the contents of source. Otherwise the object will be empty. @param source source for initialization (file name of HEX file, file object, addr dict or other IntelHex object) ''' # public members self.padding = 0x0FF # Start Address self.start_addr = None # private members self._buf = {} self._offset = 0 if source is not None: if isinstance(source, StrType) or getattr(source, "read", None): # load hex file self.loadhex(source) elif isinstance(source, dict): self.fromdict(source) elif isinstance(source, IntelHex): self.padding = source.padding if source.start_addr: self.start_addr = source.start_addr.copy() self._buf = source._buf.copy() else: raise ValueError("source: bad initializer type") def _decode_record(self, s, line=0): '''Decode one record of HEX file. @param s line with HEX record. @param line line number (for error messages). @raise EndOfFile if EOF record encountered. ''' s = s.rstrip('\r\n') if not s: return # empty line if s[0] == ':': try: bin = array('B', unhexlify(asbytes(s[1:]))) except (TypeError, ValueError): # this might be raised by unhexlify when odd hexascii digits raise HexRecordError(line=line) length = len(bin) if length < 5: raise HexRecordError(line=line) else: raise HexRecordError(line=line) record_length = bin[0] if length != (5 + record_length): raise RecordLengthError(line=line) addr = bin[1]*256 + bin[2] record_type = bin[3] if not (0 <= record_type <= 5): raise RecordTypeError(line=line) crc = sum(bin) crc &= 0x0FF if crc != 0: raise RecordChecksumError(line=line) if record_type == 0: # data record addr += self._offset for i in range_g(4, 4+record_length): if not self._buf.get(addr, None) is None: raise AddressOverlapError(address=addr, line=line) self._buf[addr] = bin[i] addr += 1 # FIXME: addr should be wrapped # BUT after 02 record (at 64K boundary) # and after 04 record (at 4G boundary) elif record_type == 1: # end of file record if record_length != 0: raise EOFRecordError(line=line) raise _EndOfFile elif record_type == 2: # Extended 8086 Segment Record if record_length != 2 or addr != 0: raise ExtendedSegmentAddressRecordError(line=line) self._offset = (bin[4]*256 + bin[5]) * 16 elif record_type == 4: # Extended Linear Address Record if record_length != 2 or addr != 0: raise ExtendedLinearAddressRecordError(line=line) self._offset = (bin[4]*256 + bin[5]) * 65536 elif record_type == 3: # Start Segment Address Record if record_length != 4 or addr != 0: raise StartSegmentAddressRecordError(line=line) if self.start_addr: raise DuplicateStartAddressRecordError(line=line) self.start_addr = {'CS': bin[4]*256 + bin[5], 'IP': bin[6]*256 + bin[7], } elif record_type == 5: # Start Linear Address Record if record_length != 4 or addr != 0: raise StartLinearAddressRecordError(line=line) if self.start_addr: raise DuplicateStartAddressRecordError(line=line) self.start_addr = {'EIP': (bin[4]*16777216 + bin[5]*65536 + bin[6]*256 + bin[7]), } def loadhex(self, fobj): """Load hex file into internal buffer. This is not necessary if object was initialized with source set. This will overwrite addresses if object was already initialized. @param fobj file name or file-like object """ if getattr(fobj, "read", None) is None: fobj = open(fobj, "r") fclose = fobj.close else: fclose = None self._offset = 0 line = 0 try: decode = self._decode_record try: for s in fobj: line += 1 decode(s, line) except _EndOfFile: pass finally: if fclose: fclose() def loadbin(self, fobj, offset=0): """Load bin file into internal buffer. Not needed if source set in constructor. This will overwrite addresses without warning if object was already initialized. @param fobj file name or file-like object @param offset starting address offset """ fread = getattr(fobj, "read", None) if fread is None: f = open(fobj, "rb") fread = f.read fclose = f.close else: fclose = None try: self.frombytes(array('B', asbytes(fread())), offset=offset) finally: if fclose: fclose() def loadfile(self, fobj, format): """Load data file into internal buffer. Preferred wrapper over loadbin or loadhex. @param fobj file name or file-like object @param format file format ("hex" or "bin") """ if format == "hex": self.loadhex(fobj) elif format == "bin": self.loadbin(fobj) else: raise ValueError('format should be either "hex" or "bin";' ' got %r instead' % format) # alias (to be consistent with method tofile) fromfile = loadfile def fromdict(self, dikt): """Load data from dictionary. Dictionary should contain int keys representing addresses. Values should be the data to be stored in those addresses in unsigned char form (i.e. not strings). The dictionary may contain the key, ``start_addr`` to indicate the starting address of the data as described in README. The contents of the dict will be merged with this object and will overwrite any conflicts. This function is not necessary if the object was initialized with source specified. """ s = dikt.copy() start_addr = s.get('start_addr') if start_addr is not None: del s['start_addr'] for k in dict_keys_g(s): if type(k) not in IntTypes or k < 0: raise ValueError('Source dictionary should have only int keys') self._buf.update(s) if start_addr is not None: self.start_addr = start_addr def frombytes(self, bytes, offset=0): """Load data from array or list of bytes. Similar to loadbin() method but works directly with iterable bytes. """ for b in bytes: self._buf[offset] = b offset += 1 def _get_start_end(self, start=None, end=None, size=None): """Return default values for start and end if they are None. If this IntelHex object is empty then it's error to invoke this method with both start and end as None. """ if (start,end) == (None,None) and self._buf == {}: raise EmptyIntelHexError if size is not None: if None not in (start, end): raise ValueError("tobinarray: you can't use start,end and size" " arguments in the same time") if (start, end) == (None, None): start = self.minaddr() if start is not None: end = start + size - 1 else: start = end - size + 1 if start < 0: raise ValueError("tobinarray: invalid size (%d) " "for given end address (%d)" % (size,end)) else: if start is None: start = self.minaddr() if end is None: end = self.maxaddr() if start > end: start, end = end, start return start, end def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None): ''' Convert this object to binary form as array. If start and end unspecified, they will be inferred from the data. @param start start address of output bytes. @param end end address of output bytes (inclusive). @param pad [DEPRECATED PARAMETER, please use self.padding instead] fill empty spaces with this value (if pad is None then this method uses self.padding). @param size size of the block, used with start or end parameter. @return array of unsigned char data. ''' if not isinstance(pad, _DeprecatedParam): print ("IntelHex.tobinarray: 'pad' parameter is deprecated.") if pad is not None: print ("Please, use IntelHex.padding attribute instead.") else: print ("Please, don't pass it explicitly.") print ("Use syntax like this: ih.tobinarray(start=xxx, end=yyy, size=zzz)") else: pad = None return self._tobinarray_really(start, end, pad, size) def _tobinarray_really(self, start, end, pad, size): """Return binary array.""" if pad is None: pad = self.padding bin = array('B') if self._buf == {} and None in (start, end): return bin if size is not None and size <= 0: raise ValueError("tobinarray: wrong value for size") start, end = self._get_start_end(start, end, size) for i in range_g(start, end+1): bin.append(self._buf.get(i, pad)) return bin def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None): ''' Convert to binary form and return as binary string. @param start start address of output bytes. @param end end address of output bytes (inclusive). @param pad [DEPRECATED PARAMETER, please use self.padding instead] fill empty spaces with this value (if pad is None then this method uses self.padding). @param size size of the block, used with start or end parameter. @return bytes string of binary data. ''' if not isinstance(pad, _DeprecatedParam): print ("IntelHex.tobinstr: 'pad' parameter is deprecated.") if pad is not None: print ("Please, use IntelHex.padding attribute instead.") else: print ("Please, don't pass it explicitly.") print ("Use syntax like this: ih.tobinstr(start=xxx, end=yyy, size=zzz)") else: pad = None return self._tobinstr_really(start, end, pad, size) def _tobinstr_really(self, start, end, pad, size): return array_tobytes(self._tobinarray_really(start, end, pad, size)) def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None): '''Convert to binary and write to file. @param fobj file name or file object for writing output bytes. @param start start address of output bytes. @param end end address of output bytes (inclusive). @param pad [DEPRECATED PARAMETER, please use self.padding instead] fill empty spaces with this value (if pad is None then this method uses self.padding). @param size size of the block, used with start or end parameter. ''' if not isinstance(pad, _DeprecatedParam): print ("IntelHex.tobinfile: 'pad' parameter is deprecated.") if pad is not None: print ("Please, use IntelHex.padding attribute instead.") else: print ("Please, don't pass it explicitly.") print ("Use syntax like this: ih.tobinfile(start=xxx, end=yyy, size=zzz)") else: pad = None if getattr(fobj, "write", None) is None: fobj = open(fobj, "wb") close_fd = True else: close_fd = False fobj.write(self._tobinstr_really(start, end, pad, size)) if close_fd: fobj.close() def todict(self): '''Convert to python dictionary. @return dict suitable for initializing another IntelHex object. ''' r = {} r.update(self._buf) if self.start_addr: r['start_addr'] = self.start_addr return r def addresses(self): '''Returns all used addresses in sorted order. @return list of occupied data addresses in sorted order. ''' aa = dict_keys(self._buf) aa.sort() return aa def minaddr(self): '''Get minimal address of HEX content. @return minimal address or None if no data ''' aa = dict_keys(self._buf) if aa == []: return None else: return min(aa) def maxaddr(self): '''Get maximal address of HEX content. @return maximal address or None if no data ''' aa = dict_keys(self._buf) if aa == []: return None else: return max(aa) def __getitem__(self, addr): ''' Get requested byte from address. @param addr address of byte. @return byte if address exists in HEX file, or self.padding if no data found. ''' t = type(addr) if t in IntTypes: if addr < 0: raise TypeError('Address should be >= 0.') return self._buf.get(addr, self.padding) elif t == slice: addresses = dict_keys(self._buf) ih = IntelHex() if addresses: addresses.sort() start = addr.start or addresses[0] stop = addr.stop or (addresses[-1]+1) step = addr.step or 1 for i in range_g(start, stop, step): x = self._buf.get(i) if x is not None: ih[i] = x return ih else: raise TypeError('Address has unsupported type: %s' % t) def __setitem__(self, addr, byte): """Set byte at address.""" t = type(addr) if t in IntTypes: if addr < 0: raise TypeError('Address should be >= 0.') self._buf[addr] = byte elif t == slice: if not isinstance(byte, (list, tuple)): raise ValueError('Slice operation expects sequence of bytes') start = addr.start stop = addr.stop step = addr.step or 1 if None not in (start, stop): ra = range_l(start, stop, step) if len(ra) != len(byte): raise ValueError('Length of bytes sequence does not match ' 'address range') elif (start, stop) == (None, None): raise TypeError('Unsupported address range') elif start is None: start = stop - len(byte) elif stop is None: stop = start + len(byte) if start < 0: raise TypeError('start address cannot be negative') if stop < 0: raise TypeError('stop address cannot be negative') j = 0 for i in range_g(start, stop, step): self._buf[i] = byte[j] j += 1 else: raise TypeError('Address has unsupported type: %s' % t) def __delitem__(self, addr): """Delete byte at address.""" t = type(addr) if t in IntTypes: if addr < 0: raise TypeError('Address should be >= 0.') del self._buf[addr] elif t == slice: addresses = dict_keys(self._buf) if addresses: addresses.sort() start = addr.start or addresses[0] stop = addr.stop or (addresses[-1]+1) step = addr.step or 1 for i in range_g(start, stop, step): x = self._buf.get(i) if x is not None: del self._buf[i] else: raise TypeError('Address has unsupported type: %s' % t) def __len__(self): """Return count of bytes with real values.""" return len(dict_keys(self._buf)) def _get_eol_textfile(eolstyle, platform): if eolstyle == 'native': return '\n' elif eolstyle == 'CRLF': if platform != 'win32': return '\r\n' else: return '\n' else: raise ValueError("wrong eolstyle %s" % repr(eolstyle)) _get_eol_textfile = staticmethod(_get_eol_textfile) def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count=16): """Write data to file f in HEX format. @param f filename or file-like object for writing @param write_start_addr enable or disable writing start address record to file (enabled by default). If there is no start address in obj, nothing will be written regardless of this setting. @param eolstyle can be used to force CRLF line-endings for output file on different platforms. Supported eol styles: 'native', 'CRLF'. @param byte_count number of bytes in the data field """ if byte_count > 255 or byte_count < 1: raise ValueError("wrong byte_count value: %s" % byte_count) fwrite = getattr(f, "write", None) if fwrite: fobj = f fclose = None else: fobj = open(f, 'w') fwrite = fobj.write fclose = fobj.close eol = IntelHex._get_eol_textfile(eolstyle, sys.platform) # Translation table for uppercasing hex ascii string. # timeit shows that using hexstr.translate(table) # is faster than hexstr.upper(): # 0.452ms vs. 0.652ms (translate vs. upper) if sys.version_info[0] >= 3: # Python 3 table = bytes(range_l(256)).upper() else: # Python 2 table = ''.join(chr(i).upper() for i in range_g(256)) # start address record if any if self.start_addr and write_start_addr: keys = dict_keys(self.start_addr) keys.sort() bin = array('B', asbytes('\0'*9)) if keys == ['CS','IP']: # Start Segment Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 3 # rectyp cs = self.start_addr['CS'] bin[4] = (cs >> 8) & 0x0FF bin[5] = cs & 0x0FF ip = self.start_addr['IP'] bin[6] = (ip >> 8) & 0x0FF bin[7] = ip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(array_tobytes(bin)).translate(table)) + eol) elif keys == ['EIP']: # Start Linear Address Record bin[0] = 4 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 5 # rectyp eip = self.start_addr['EIP'] bin[4] = (eip >> 24) & 0x0FF bin[5] = (eip >> 16) & 0x0FF bin[6] = (eip >> 8) & 0x0FF bin[7] = eip & 0x0FF bin[8] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(array_tobytes(bin)).translate(table)) + eol) else: if fclose: fclose() raise InvalidStartAddressValueError(start_addr=self.start_addr) # data addresses = dict_keys(self._buf) addresses.sort() addr_len = len(addresses) if addr_len: minaddr = addresses[0] maxaddr = addresses[-1] if maxaddr > 65535: need_offset_record = True else: need_offset_record = False high_ofs = 0 cur_addr = minaddr cur_ix = 0 while cur_addr <= maxaddr: if need_offset_record: bin = array('B', asbytes('\0'*7)) bin[0] = 2 # reclen bin[1] = 0 # offset msb bin[2] = 0 # offset lsb bin[3] = 4 # rectyp high_ofs = int(cur_addr>>16) b = divmod(high_ofs, 256) bin[4] = b[0] # msb of high_ofs bin[5] = b[1] # lsb of high_ofs bin[6] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(array_tobytes(bin)).translate(table)) + eol) while True: # produce one record low_addr = cur_addr & 0x0FFFF # chain_len off by 1 chain_len = min(byte_count-1, 65535-low_addr, maxaddr-cur_addr) # search continuous chain stop_addr = cur_addr + chain_len if chain_len: ix = bisect_right(addresses, stop_addr, cur_ix, min(cur_ix+chain_len+1, addr_len)) chain_len = ix - cur_ix # real chain_len # there could be small holes in the chain # but we will catch them by try-except later # so for big continuous files we will work # at maximum possible speed else: chain_len = 1 # real chain_len bin = array('B', asbytes('\0'*(5+chain_len))) b = divmod(low_addr, 256) bin[1] = b[0] # msb of low_addr bin[2] = b[1] # lsb of low_addr bin[3] = 0 # rectype try: # if there is small holes we'll catch them for i in range_g(chain_len): bin[4+i] = self._buf[cur_addr+i] except KeyError: # we catch a hole so we should shrink the chain chain_len = i bin = bin[:5+i] bin[0] = chain_len bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum fwrite(':' + asstr(hexlify(array_tobytes(bin)).translate(table)) + eol) # adjust cur_addr/cur_ix cur_ix += chain_len if cur_ix < addr_len: cur_addr = addresses[cur_ix] else: cur_addr = maxaddr + 1 break high_addr = int(cur_addr>>16) if high_addr > high_ofs: break # end-of-file record fwrite(":00000001FF"+eol) if fclose: fclose() def tofile(self, fobj, format, byte_count=16): """Write data to hex or bin file. Preferred method over tobin or tohex. @param fobj file name or file-like object @param format file format ("hex" or "bin") @param byte_count bytes per line """ if format == 'hex': self.write_hex_file(fobj, byte_count=byte_count) elif format == 'bin': self.tobinfile(fobj) else: raise ValueError('format should be either "hex" or "bin";' ' got %r instead' % format) def gets(self, addr, length): """Get string of bytes from given address. If any entries are blank from addr through addr+length, a NotEnoughDataError exception will be raised. Padding is not used. """ a = array('B', asbytes('\0'*length)) try: for i in range_g(length): a[i] = self._buf[addr+i] except KeyError: raise NotEnoughDataError(address=addr, length=length) return array_tobytes(a) def puts(self, addr, s): """Put string of bytes at given address. Will overwrite any previous entries. """ a = array('B', asbytes(s)) for i in range_g(len(a)): self._buf[addr+i] = a[i] def getsz(self, addr): """Get zero-terminated bytes string from given address. Will raise NotEnoughDataError exception if a hole is encountered before a 0. """ i = 0 try: while True: if self._buf[addr+i] == 0: break i += 1 except KeyError: raise NotEnoughDataError(msg=('Bad access at 0x%X: ' 'not enough data to read zero-terminated string') % addr) return self.gets(addr, i) def putsz(self, addr, s): """Put bytes string in object at addr and append terminating zero at end.""" self.puts(addr, s) self._buf[addr+len(s)] = 0 def find(self, sub, start=None, end=None): """Return the lowest index in self[start:end] where subsection sub is found. Optional arguments start and end are interpreted as in slice notation. @param sub bytes-like subsection to find @param start start of section to search within (optional) @param end end of section to search within (optional) """ sub = bytes(sub) for start, end in self[slice(start,end)].segments(): b = self.gets(start, end-start) i = b.find(sub) if i != -1: return start+i return -1 def dump(self, tofile=None, width=16, withpadding=False): """Dump object content to specified file object or to stdout if None. Format is a hexdump with some header information at the beginning, addresses on the left, and data on right. @param tofile file-like object to dump to @param width number of bytes per line (i.e. columns) @param withpadding print padding character instead of '--' @raise ValueError if width is not a positive integer """ if not isinstance(width,int) or width < 1: raise ValueError('width must be a positive integer.') # The integer can be of float type - does not work with bit operations width = int(width) if tofile is None: tofile = sys.stdout # start addr possibly if self.start_addr is not None: cs = self.start_addr.get('CS') ip = self.start_addr.get('IP') eip = self.start_addr.get('EIP') if eip is not None and cs is None and ip is None: tofile.write('EIP = 0x%08X\n' % eip) elif eip is None and cs is not None and ip is not None: tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) else: tofile.write('start_addr = %r\n' % start_addr) # actual data addresses = dict_keys(self._buf) if addresses: addresses.sort() minaddr = addresses[0] maxaddr = addresses[-1] startaddr = (minaddr // width) * width endaddr = ((maxaddr // width) + 1) * width maxdigits = max(len(hex(endaddr)) - 2, 4) # Less 2 to exclude '0x' templa = '%%0%dX' % maxdigits rangewidth = range_l(width) if withpadding: pad = self.padding else: pad = None for i in range_g(startaddr, endaddr, width): tofile.write(templa % i) tofile.write(' ') s = [] for j in rangewidth: x = self._buf.get(i+j, pad) if x is not None: tofile.write(' %02X' % x) if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot s.append(chr(x)) else: s.append('.') else: tofile.write(' --') s.append(' ') tofile.write(' |' + ''.join(s) + '|\n') def merge(self, other, overlap='error'): """Merge content of other IntelHex object into current object (self). @param other other IntelHex object. @param overlap action on overlap of data or starting addr: - error: raising OverlapError; - ignore: ignore other data and keep current data in overlapping region; - replace: replace data with other data in overlapping region. @raise TypeError if other is not instance of IntelHex @raise ValueError if other is the same object as self (it can't merge itself) @raise ValueError if overlap argument has incorrect value @raise AddressOverlapError on overlapped data """ # check args if not isinstance(other, IntelHex): raise TypeError('other should be IntelHex object') if other is self: raise ValueError("Can't merge itself") if overlap not in ('error', 'ignore', 'replace'): raise ValueError("overlap argument should be either " "'error', 'ignore' or 'replace'") # merge data this_buf = self._buf other_buf = other._buf for i in other_buf: if i in this_buf: if overlap == 'error': raise AddressOverlapError( 'Data overlapped at address 0x%X' % i) elif overlap == 'ignore': continue this_buf[i] = other_buf[i] # merge start_addr if self.start_addr != other.start_addr: if self.start_addr is None: # set start addr from other self.start_addr = other.start_addr elif other.start_addr is None: # keep existing start addr pass else: # conflict if overlap == 'error': raise AddressOverlapError( 'Starting addresses are different') elif overlap == 'replace': self.start_addr = other.start_addr def segments(self, min_gap=1): """Return a list of ordered tuple objects, representing contiguous occupied data addresses. Each tuple has a length of two and follows the semantics of the range and xrange objects. The second entry of the tuple is always an integer greater than the first entry. @param min_gap the minimum gap size between data in order to separate the segments """ addresses = self.addresses() if not addresses: return [] elif len(addresses) == 1: return([(addresses[0], addresses[0]+1)]) adjacent_differences = [(b - a) for (a, b) in zip(addresses[:-1], addresses[1:])] breaks = [i for (i, x) in enumerate(adjacent_differences) if x > min_gap] endings = [addresses[b] for b in breaks] endings.append(addresses[-1]) beginnings = [addresses[b+1] for b in breaks] beginnings.insert(0, addresses[0]) return [(a, b+1) for (a, b) in zip(beginnings, endings)] def get_memory_size(self): """Returns the approximate memory footprint for data.""" n = sys.getsizeof(self) n += sys.getsizeof(self.padding) n += total_size(self.start_addr) n += total_size(self._buf) n += sys.getsizeof(self._offset) return n #/IntelHex class IntelHex16bit(IntelHex): """Access to data as 16-bit words. Intended to use with Microchip HEX files.""" def __init__(self, source=None): """Construct class from HEX file or from instance of ordinary IntelHex class. If IntelHex object is passed as source, the original IntelHex object should not be used again because this class will alter it. This class leaves padding alone unless it was precisely 0xFF. In that instance it is sign extended to 0xFFFF. @param source file name of HEX file or file object or instance of ordinary IntelHex class. Will also accept dictionary from todict method. """ if isinstance(source, IntelHex): # from ihex8 self.padding = source.padding self.start_addr = source.start_addr # private members self._buf = source._buf self._offset = source._offset elif isinstance(source, dict): raise IntelHexError("IntelHex16bit does not support initialization from dictionary yet.\n" "Patches are welcome.") else: IntelHex.__init__(self, source) if self.padding == 0x0FF: self.padding = 0x0FFFF def __getitem__(self, addr16): """Get 16-bit word from address. Raise error if only one byte from the pair is set. We assume a Little Endian interpretation of the hex file. @param addr16 address of word (addr8 = 2 * addr16). @return word if bytes exists in HEX file, or self.padding if no data found. """ addr1 = addr16 * 2 addr2 = addr1 + 1 byte1 = self._buf.get(addr1, None) byte2 = self._buf.get(addr2, None) if byte1 != None and byte2 != None: return byte1 | (byte2 << 8) # low endian if byte1 == None and byte2 == None: return self.padding raise BadAccess16bit(address=addr16) def __setitem__(self, addr16, word): """Sets the address at addr16 to word assuming Little Endian mode. """ addr_byte = addr16 * 2 b = divmod(word, 256) self._buf[addr_byte] = b[1] self._buf[addr_byte+1] = b[0] def minaddr(self): '''Get minimal address of HEX content in 16-bit mode. @return minimal address used in this object ''' aa = dict_keys(self._buf) if aa == []: return 0 else: return min(aa)>>1 def maxaddr(self): '''Get maximal address of HEX content in 16-bit mode. @return maximal address used in this object ''' aa = dict_keys(self._buf) if aa == []: return 0 else: return max(aa)>>1 def tobinarray(self, start=None, end=None, size=None): '''Convert this object to binary form as array (of 2-bytes word data). If start and end unspecified, they will be inferred from the data. @param start start address of output data. @param end end address of output data (inclusive). @param size size of the block (number of words), used with start or end parameter. @return array of unsigned short (uint16_t) data. ''' bin = array('H') if self._buf == {} and None in (start, end): return bin if size is not None and size <= 0: raise ValueError("tobinarray: wrong value for size") start, end = self._get_start_end(start, end, size) for addr in range_g(start, end+1): bin.append(self[addr]) return bin #/class IntelHex16bit def hex2bin(fin, fout, start=None, end=None, size=None, pad=None): """Hex-to-Bin convertor engine. @return 0 if all OK @param fin input hex file (filename or file-like object) @param fout output bin file (filename or file-like object) @param start start of address range (optional) @param end end of address range (inclusive; optional) @param size size of resulting file (in bytes) (optional) @param pad padding byte (optional) """ try: h = IntelHex(fin) except HexReaderError: e = sys.exc_info()[1] # current exception txt = "ERROR: bad HEX file: %s" % str(e) print(txt) return 1 # start, end, size if size != None and size != 0: if end == None: if start == None: start = h.minaddr() end = start + size - 1 else: if (end+1) >= size: start = end + 1 - size else: start = 0 try: if pad is not None: # using .padding attribute rather than pad argument to function call h.padding = pad h.tobinfile(fout, start, end) except IOError: e = sys.exc_info()[1] # current exception txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e)) print(txt) return 1 return 0 #/def hex2bin def bin2hex(fin, fout, offset=0): """Simple bin-to-hex convertor. @return 0 if all OK @param fin input bin file (filename or file-like object) @param fout output hex file (filename or file-like object) @param offset starting address offset for loading bin """ h = IntelHex() try: h.loadbin(fin, offset) except IOError: e = sys.exc_info()[1] # current exception txt = 'ERROR: unable to load bin file:', str(e) print(txt) return 1 try: h.tofile(fout, format='hex') except IOError: e = sys.exc_info()[1] # current exception txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e)) print(txt) return 1 return 0 #/def bin2hex def diff_dumps(ih1, ih2, tofile=None, name1="a", name2="b", n_context=3): """Diff 2 IntelHex objects and produce unified diff output for their hex dumps. @param ih1 first IntelHex object to compare @param ih2 second IntelHex object to compare @param tofile file-like object to write output @param name1 name of the first hex file to show in the diff header @param name2 name of the first hex file to show in the diff header @param n_context number of context lines in the unidiff output """ def prepare_lines(ih): sio = StringIO() ih.dump(sio) dump = sio.getvalue() lines = dump.splitlines() return lines a = prepare_lines(ih1) b = prepare_lines(ih2) import difflib result = list(difflib.unified_diff(a, b, fromfile=name1, tofile=name2, n=n_context, lineterm='')) if tofile is None: tofile = sys.stdout output = '\n'.join(result)+'\n' tofile.write(output) class Record(object): """Helper methods to build valid ihex records.""" def _from_bytes(bytes): """Takes a list of bytes, computes the checksum, and outputs the entire record as a string. bytes should be the hex record without the colon or final checksum. @param bytes list of byte values so far to pack into record. @return String representation of one HEX record """ assert len(bytes) >= 4 # calculate checksum s = (-sum(bytes)) & 0x0FF bin = array('B', bytes + [s]) return ':' + asstr(hexlify(array_tobytes(bin))).upper() _from_bytes = staticmethod(_from_bytes) def data(offset, bytes): """Return Data record. This constructs the full record, including the length information, the record type (0x00), the checksum, and the offset. @param offset load offset of first byte. @param bytes list of byte values to pack into record. @return String representation of one HEX record """ assert 0 <= offset < 65536 assert 0 < len(bytes) < 256 b = [len(bytes), (offset>>8)&0x0FF, offset&0x0FF, 0x00] + bytes return Record._from_bytes(b) data = staticmethod(data) def eof(): """Return End of File record as a string. @return String representation of Intel Hex EOF record """ return ':00000001FF' eof = staticmethod(eof) def extended_segment_address(usba): """Return Extended Segment Address Record. @param usba Upper Segment Base Address. @return String representation of Intel Hex USBA record. """ b = [2, 0, 0, 0x02, (usba>>8)&0x0FF, usba&0x0FF] return Record._from_bytes(b) extended_segment_address = staticmethod(extended_segment_address) def start_segment_address(cs, ip): """Return Start Segment Address Record. @param cs 16-bit value for CS register. @param ip 16-bit value for IP register. @return String representation of Intel Hex SSA record. """ b = [4, 0, 0, 0x03, (cs>>8)&0x0FF, cs&0x0FF, (ip>>8)&0x0FF, ip&0x0FF] return Record._from_bytes(b) start_segment_address = staticmethod(start_segment_address) def extended_linear_address(ulba): """Return Extended Linear Address Record. @param ulba Upper Linear Base Address. @return String representation of Intel Hex ELA record. """ b = [2, 0, 0, 0x04, (ulba>>8)&0x0FF, ulba&0x0FF] return Record._from_bytes(b) extended_linear_address = staticmethod(extended_linear_address) def start_linear_address(eip): """Return Start Linear Address Record. @param eip 32-bit linear address for the EIP register. @return String representation of Intel Hex SLA record. """ b = [4, 0, 0, 0x05, (eip>>24)&0x0FF, (eip>>16)&0x0FF, (eip>>8)&0x0FF, eip&0x0FF] return Record._from_bytes(b) start_linear_address = staticmethod(start_linear_address) class _BadFileNotation(Exception): """Special error class to use with _get_file_and_addr_range.""" pass def _get_file_and_addr_range(s, _support_drive_letter=None): """Special method for hexmerge.py script to split file notation into 3 parts: (filename, start, end) @raise _BadFileNotation when string cannot be safely split. """ if _support_drive_letter is None: _support_drive_letter = (os.name == 'nt') drive = '' if _support_drive_letter: if s[1:2] == ':' and s[0].upper() in ''.join([chr(i) for i in range_g(ord('A'), ord('Z')+1)]): drive = s[:2] s = s[2:] parts = s.split(':') n = len(parts) if n == 1: fname = parts[0] fstart = None fend = None elif n != 3: raise _BadFileNotation else: fname = parts[0] def ascii_hex_to_int(ascii): if ascii is not None: try: return int(ascii, 16) except ValueError: raise _BadFileNotation return ascii fstart = ascii_hex_to_int(parts[1] or None) fend = ascii_hex_to_int(parts[2] or None) return drive+fname, fstart, fend ## # IntelHex Errors Hierarchy: # # IntelHexError - basic error # HexReaderError - general hex reader error # AddressOverlapError - data for the same address overlap # HexRecordError - hex record decoder base error # RecordLengthError - record has invalid length # RecordTypeError - record has invalid type (RECTYP) # RecordChecksumError - record checksum mismatch # EOFRecordError - invalid EOF record (type 01) # ExtendedAddressRecordError - extended address record base error # ExtendedSegmentAddressRecordError - invalid extended segment address record (type 02) # ExtendedLinearAddressRecordError - invalid extended linear address record (type 04) # StartAddressRecordError - start address record base error # StartSegmentAddressRecordError - invalid start segment address record (type 03) # StartLinearAddressRecordError - invalid start linear address record (type 05) # DuplicateStartAddressRecordError - start address record appears twice # InvalidStartAddressValueError - invalid value of start addr record # _EndOfFile - it's not real error, used internally by hex reader as signal that EOF record found # BadAccess16bit - not enough data to read 16 bit value (deprecated, see NotEnoughDataError) # NotEnoughDataError - not enough data to read N contiguous bytes # EmptyIntelHexError - requested operation cannot be performed with empty object class IntelHexError(Exception): '''Base Exception class for IntelHex module''' _fmt = 'IntelHex base error' #: format string def __init__(self, msg=None, **kw): """Initialize the Exception with the given message. """ self.msg = msg for key, value in dict_items_g(kw): setattr(self, key, value) def __str__(self): """Return the message in this Exception.""" if self.msg: return self.msg try: return self._fmt % self.__dict__ except (NameError, ValueError, KeyError): e = sys.exc_info()[1] # current exception return 'Unprintable exception %s: %s' \ % (repr(e), str(e)) class _EndOfFile(IntelHexError): """Used for internal needs only.""" _fmt = 'EOF record reached -- signal to stop read file' class HexReaderError(IntelHexError): _fmt = 'Hex reader base error' class AddressOverlapError(HexReaderError): _fmt = 'Hex file has data overlap at address 0x%(address)X on line %(line)d' # class NotAHexFileError was removed in trunk.revno.54 because it's not used class HexRecordError(HexReaderError): _fmt = 'Hex file contains invalid record at line %(line)d' class RecordLengthError(HexRecordError): _fmt = 'Record at line %(line)d has invalid length' class RecordTypeError(HexRecordError): _fmt = 'Record at line %(line)d has invalid record type' class RecordChecksumError(HexRecordError): _fmt = 'Record at line %(line)d has invalid checksum' class EOFRecordError(HexRecordError): _fmt = 'File has invalid End-of-File record' class ExtendedAddressRecordError(HexRecordError): _fmt = 'Base class for extended address exceptions' class ExtendedSegmentAddressRecordError(ExtendedAddressRecordError): _fmt = 'Invalid Extended Segment Address Record at line %(line)d' class ExtendedLinearAddressRecordError(ExtendedAddressRecordError): _fmt = 'Invalid Extended Linear Address Record at line %(line)d' class StartAddressRecordError(HexRecordError): _fmt = 'Base class for start address exceptions' class StartSegmentAddressRecordError(StartAddressRecordError): _fmt = 'Invalid Start Segment Address Record at line %(line)d' class StartLinearAddressRecordError(StartAddressRecordError): _fmt = 'Invalid Start Linear Address Record at line %(line)d' class DuplicateStartAddressRecordError(StartAddressRecordError): _fmt = 'Start Address Record appears twice at line %(line)d' class InvalidStartAddressValueError(StartAddressRecordError): _fmt = 'Invalid start address value: %(start_addr)s' class NotEnoughDataError(IntelHexError): _fmt = ('Bad access at 0x%(address)X: ' 'not enough data to read %(length)d contiguous bytes') class BadAccess16bit(NotEnoughDataError): _fmt = 'Bad access at 0x%(address)X: not enough data to read 16 bit value' class EmptyIntelHexError(IntelHexError): _fmt = "Requested operation cannot be executed with empty object" intelhex-2.3.0/intelhex/__main__.py000066400000000000000000000035241374364251400172630ustar00rootroot00000000000000# Copyright (c) 2016-2018, Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if __name__ == '__main__': print("Welcome to IntelHex Python library.") print() print("The intelhex package has some executable points:") print(" python -m intelhex.test -- easy way to run unit tests.") print(" python -m intelhex.bench -- run benchmarks.") intelhex-2.3.0/intelhex/__version__.py000066400000000000000000000001731374364251400200210ustar00rootroot00000000000000# IntelHex library version information version_info = (2, 3, 0) version_str = '.'.join([str(i) for i in version_info]) intelhex-2.3.0/intelhex/bench.py000066400000000000000000000221121374364251400166140ustar00rootroot00000000000000#!/usr/bin/python # (c) Alexander Belchenko, 2007, 2009 # [2013/08] NOTE: This file is keeping for historical reasons. # It may or may not work actually with current version of intelhex, # and most likely it requires some fixes here and there. """Benchmarking. Run each test 3 times and get median value. Using 10K array as base test time. Each other test compared with base with next formula:: Tc * Nb q = --------- Tb * Nc Here: * Tc - execution time of current test * Tb - execution time of base * Nb - array size of base (10K) * Nc - array size of current test If resulting value is ``q <= 1.0`` it's the best possible result, i.e. time increase proportionally to array size. """ import gc import sys import time import intelhex from intelhex.compat import StringIO, range_g def median(values): """Return median value for the list of values. @param values: list of values for processing. @return: median value. """ values.sort() n = int(len(values) / 2) return values[n] def run_test(func, fobj): """Run func with argument fobj and measure execution time. @param func: function for test @param fobj: data for test @return: execution time """ gc.disable() try: begin = time.time() func(fobj) end = time.time() finally: gc.enable() return end - begin def run_readtest_N_times(func, hexstr, n): """Run each test N times. @param func: function for test @param hexstr: string with content of hex file to read @param n: times to repeat. @return: (median time, times list) """ assert n > 0 times = [] for i in range_g(n): sio = StringIO(hexstr) times.append(run_test(func, sio)) sio.close() t = median(times) return t, times def run_writetest_N_times(func, n): """Run each test N times. @param func: function for test @param n: times to repeat. @return: (median time, times list) """ assert n > 0 times = [] for i in range_g(n): sio = StringIO() times.append(run_test(func, sio)) sio.close() t = median(times) return t, times def time_coef(tc, nc, tb, nb): """Return time coefficient relative to base numbers. @param tc: current test time @param nc: current test data size @param tb: base test time @param nb: base test data size @return: time coef. """ tc = float(tc) nc = float(nc) tb = float(tb) nb = float(nb) q = (tc * nb) / (tb * nc) return q def get_test_data(n1, offset, n2): """Create test data on given pattern. @param n1: size of first part of array at base address 0. @param offset: offset for second part of array. @param n2: size of second part of array at given offset. @return: (overall size, hex file, IntelHex object) """ # make IntelHex object ih = intelhex.IntelHex() addr = 0 for i in range_g(n1): ih[addr] = addr % 256 addr += 1 addr += offset for i in range_g(n2): ih[addr] = addr % 256 addr += 1 # make hex file sio = StringIO() ih.write_hex_file(sio) hexstr = sio.getvalue() sio.close() # return n1+n2, hexstr, ih def get_base_50K(): return get_test_data(50000, 0, 0) def get_250K(): return get_test_data(250000, 0, 0) def get_100K_100K(): return get_test_data(100000, 1000000, 100000) def get_0_100K(): return get_test_data(0, 1000000, 100000) def get_1M(): return get_test_data(1000000, 0, 0) class Measure(object): """Measure execution time helper.""" data_set = [ # (data name, getter) ('base 50K', get_base_50K), # first should be base numbers ('250K', get_250K), ('1M', get_1M), ('100K+100K', get_100K_100K), ('0+100K', get_0_100K), ] def __init__(self, n=3, read=True, write=True): self.n = n self.read = read self.write = write self.results = [] def measure_one(self, data): """Do measuring of read and write operations. @param data: 3-tuple from get_test_data @return: (time readhex, time writehex) """ _unused, hexstr, ih = data tread, twrite = 0.0, 0.0 if self.read: tread = run_readtest_N_times(intelhex.IntelHex, hexstr, self.n)[0] if self.write: twrite = run_writetest_N_times(ih.write_hex_file, self.n)[0] return tread, twrite def measure_all(self): for name, getter in self.data_set: data = getter() times = self.measure_one(data) self.results.append((name, times, data[0])) def print_report(self, to_file=None): if to_file is None: to_file = sys.stdout base_title, base_times, base_n = self.results[0] base_read, base_write = base_times read_report = ['%-10s\t%7.3f' % (base_title, base_read)] write_report = ['%-10s\t%7.3f' % (base_title, base_write)] for item in self.results[1:]: cur_title, cur_times, cur_n = item cur_read, cur_write = cur_times if self.read: qread = time_coef(cur_read, cur_n, base_read, base_n) read_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title, cur_read, qread)) if self.write: qwrite = time_coef(cur_write, cur_n, base_write, base_n) write_report.append('%-10s\t%7.3f\t%7.3f' % (cur_title, cur_write, qwrite)) if self.read: to_file.write('Read operation:\n') to_file.write('\n'.join(read_report)) to_file.write('\n\n') if self.write: to_file.write('Write operation:\n') to_file.write('\n'.join(write_report)) to_file.write('\n\n') HELP = """\ Usage: python _bench.py [OPTIONS] Options: -h this help -n N repeat tests N times -r run only tests for read operation -w run only tests for write operation If option -r or -w is not specified then all tests will be run. """ def main(argv=None): """Main function to run benchmarks. @param argv: command-line arguments. @return: exit code (0 is OK). """ import getopt # default values test_read = None test_write = None n = 3 # number of repeat if argv is None: argv = sys.argv[1:] try: opts, args = getopt.getopt(argv, 'hn:rw', []) for o,a in opts: if o == '-h': print(HELP) return 0 elif o == '-n': n = int(a) elif o == '-r': test_read = True elif o == '-w': test_write = True if args: raise getopt.GetoptError('Arguments are not used.') except getopt.GetoptError: msg = sys.exc_info()[1] # current exception txt = str(msg) print(txt) return 1 if (test_read, test_write) == (None, None): test_read = test_write = True m = Measure(n, test_read, test_write) m.measure_all() m.print_report() return 0 if __name__ == '__main__': sys.exit(main(sys.argv[1:])) """ Some Results ************ 21/04/2007 revno.40 Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.031 100K 0.360 1.161 1M 3.500 1.129 100K+100K 0.719 1.160 0+100K 0.360 1.161 Write operation: base 10K 0.031 100K 0.297 0.958 1M 2.953 0.953 100K+100K 1.328 2.142 0+100K 0.312 1.006 21/04/2007 revno.46 Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.016 100K 0.203 1.269 1M 2.000 1.250 100K+100K 0.422 1.319 0+100K 0.203 1.269 Write operation: base 10K 0.031 100K 0.297 0.958 1M 2.969 0.958 100K+100K 1.328 2.142 0+100K 0.312 1.006 22/04/2007 revno.48 Python 2.5 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.016 100K 0.187 1.169 1M 1.891 1.182 100K+100K 0.406 1.269 0+100K 0.188 1.175 Write operation: base 10K 0.031 100K 0.296 0.955 1M 2.969 0.958 100K+100K 1.328 2.142 0+100K 0.312 1.006 19/08/2008 revno.72 Python 2.5.2 @ Windows XP, Intel Celeron M CPU 430 @ 1.73GHz Read operation: base 10K 0.016 100K 0.171 1.069 1M 1.734 1.084 100K+100K 0.375 1.172 0+100K 0.172 1.075 Write operation: base 10K 0.016 100K 0.156 0.975 1M 1.532 0.957 100K+100K 0.344 1.075 0+100K 0.156 0.975 """ intelhex-2.3.0/intelhex/compat.py000066400000000000000000000116531374364251400170300ustar00rootroot00000000000000# Copyright (c) 2011, Bernhard Leiner # Copyright (c) 2013-2018 Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''Compatibility functions for python 2 and 3. @author Bernhard Leiner (bleiner AT gmail com) @author Alexander Belchenko (alexander belchenko AT gmail com) ''' __docformat__ = "javadoc" import sys, array if sys.version_info[0] >= 3: # Python 3 Python = 3 def asbytes(s): if isinstance(s, bytes): return s return s.encode('latin1') def asstr(s): if isinstance(s, str): return s return s.decode('latin1') # for python >= 3.2 use 'tobytes', otherwise 'tostring' array_tobytes = array.array.tobytes if sys.version_info[1] >= 2 else array.array.tostring IntTypes = (int,) StrType = str UnicodeType = str range_g = range # range generator def range_l(*args): # range list return list(range(*args)) def dict_keys(dikt): # dict keys list return list(dikt.keys()) def dict_keys_g(dikt): # dict keys generator return dikt.keys() def dict_items_g(dikt): # dict items generator return dikt.items() from io import StringIO, BytesIO def get_binary_stdout(): return sys.stdout.buffer def get_binary_stdin(): return sys.stdin.buffer else: # Python 2 Python = 2 asbytes = str asstr = str array_tobytes = array.array.tostring IntTypes = (int, long) StrType = basestring UnicodeType = unicode #range_g = xrange # range generator def range_g(*args): # we want to use xrange here but on python 2 it does not work with long ints try: return xrange(*args) except OverflowError: start = 0 stop = 0 step = 1 n = len(args) if n == 1: stop = args[0] elif n == 2: start, stop = args elif n == 3: start, stop, step = args else: raise TypeError('wrong number of arguments in range_g call!') if step == 0: raise ValueError('step cannot be zero') if step > 0: def up(start, stop, step): while start < stop: yield start start += step return up(start, stop, step) else: def down(start, stop, step): while start > stop: yield start start += step return down(start, stop, step) range_l = range # range list def dict_keys(dikt): # dict keys list return dikt.keys() def dict_keys_g(dikt): # dict keys generator return dikt.keys() def dict_items_g(dikt): # dict items generator return dikt.items() from cStringIO import StringIO BytesIO = StringIO import os def _force_stream_binary(stream): """Force binary mode for stream on Windows.""" if os.name == 'nt': f_fileno = getattr(stream, 'fileno', None) if f_fileno: fileno = f_fileno() if fileno >= 0: import msvcrt msvcrt.setmode(fileno, os.O_BINARY) return stream def get_binary_stdout(): return _force_stream_binary(sys.stdout) def get_binary_stdin(): return _force_stream_binary(sys.stdin) intelhex-2.3.0/intelhex/getsizeof.py000066400000000000000000000041771374364251400175470ustar00rootroot00000000000000# Recursive version sys.getsizeof(). Extendable with custom handlers. # Code from http://code.activestate.com/recipes/577504/ # Created by Raymond Hettinger on Fri, 17 Dec 2010 (MIT) import sys from itertools import chain from collections import deque try: from reprlib import repr except ImportError: pass def total_size(o, handlers={}, verbose=False): """ Returns the approximate memory footprint an object and all of its contents. Automatically finds the contents of the following builtin containers and their subclasses: tuple, list, deque, dict, set and frozenset. To search other containers, add handlers to iterate over their contents: handlers = {SomeContainerClass: iter, OtherContainerClass: OtherContainerClass.get_elements} """ dict_handler = lambda d: chain.from_iterable(d.items()) all_handlers = {tuple: iter, list: iter, deque: iter, dict: dict_handler, set: iter, frozenset: iter, } all_handlers.update(handlers) # user handlers take precedence seen = set() # track which object id's have already been seen default_size = sys.getsizeof(0) # estimate sizeof object without __sizeof__ def sizeof(o): if id(o) in seen: # do not double count the same object return 0 seen.add(id(o)) s = sys.getsizeof(o, default_size) if verbose: print(s, type(o), repr(o))#, file=stderr) for typ, handler in all_handlers.items(): if isinstance(o, typ): s += sum(map(sizeof, handler(o))) break return s return sizeof(o) ##### Example call ##### if __name__ == '__main__': #d = dict(a=1, b=2, c=3, d=[4,5,6,7], e='a string of chars') print("dict 3 elements") d = {0:0xFF, 1:0xEE, 2:0xCC} print(total_size(d, verbose=True)) #print("array 3 elements") #import array #print(total_size(array.array('B', b'\x01\x02\x03'))) intelhex-2.3.0/intelhex/test.py000077500000000000000000002117661374364251400165360ustar00rootroot00000000000000# Copyright (c) 2005-2018, Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Test suite for IntelHex library.""" import array import os import shlex import subprocess import sys import tempfile import unittest import intelhex from intelhex import ( IntelHex, IntelHexError, HexReaderError, AddressOverlapError, HexRecordError, RecordLengthError, RecordTypeError, RecordChecksumError, EOFRecordError, ExtendedSegmentAddressRecordError, ExtendedLinearAddressRecordError, StartSegmentAddressRecordError, StartLinearAddressRecordError, DuplicateStartAddressRecordError, InvalidStartAddressValueError, _EndOfFile, BadAccess16bit, hex2bin, Record, ) from intelhex import compat from intelhex.compat import ( BytesIO, StringIO, UnicodeType, array_tobytes, asbytes, asstr, dict_items_g, range_g, range_l, ) from intelhex.__version__ import version_str __docformat__ = 'restructuredtext' ## # Data for tests hex8 = '''\ :1004E300CFF0FBE2FDF220FF20F2E120E2FBE6F396 :1004F3000A00FDE0E1E2E3B4E4E5BAE6E7B3BFE80E :10050300E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8E0 :10051300F9FCFEFF00C0C1C2C3A5C4C5AAC6C7B2C9 :10052300AFC8C9CACBCCCDCECFD0D1D2D3D4D5D6F8 :07053300D7D8D9DCDEDF00A0 :10053A0078227C007D007BFF7A0479F57E007F2398 :10054A0012042F78457C007D007BFF7A0579187E9E :10055A00007F2212042F759850438920758DDDD2B1 :10056A008ED2996390017BFF7A0479E31200658049 :01057A00FE82 :030000000205A254 :0C05A200787FE4F6D8FD75817A02053AF6 :10035F00E709F608DFFA8046E709F208DFFA803E80 :10036F0088828C83E709F0A3DFFA8032E309F6086D :10037F00DFFA8078E309F208DFFA807088828C83D5 :10038F00E309F0A3DFFA806489828A83E0A3F60889 :10039F00DFFA805889828A83E0A3F208DFFA804C63 :1003AF0080D280FA80C680D4806980F2803380103A :1003BF0080A680EA809A80A880DA80E280CA8033A3 :1003CF0089828A83ECFAE493A3C8C582C8CCC5831B :1003DF00CCF0A3C8C582C8CCC583CCDFE9DEE780EB :1003EF000D89828A83E493A3F608DFF9ECFAA9F06A :1003FF00EDFB2289828A83ECFAE0A3C8C582C8CCC0 :10040F00C583CCF0A3C8C582C8CCC583CCDFEADED8 :10041F00E880DB89828A83E493A3F208DFF980CC3A :10042F0088F0EF60010E4E60C388F0ED2402B40433 :10043F000050B9F582EB2402B4040050AF232345DA :06044F0082239003AF734D :10000300E576246AF8E60576227867300702786A8F :10001300E475F0011204AD0204552000EB7F2ED2EB :10002300008018EF540F2490D43440D4FF30040BD5 :10003300EF24BFB41A0050032461FFE57760021573 :1000430077057AE57A7002057930070D7867E475EC :10005300F0011204ADEF02049B02057B7403D20787 :100063008003E4C207F5768B678A688969E4F577CC :10007300F579F57AE57760077F2012003E80F57504 :1000830078FFC201C200C202C203C205C206C2088F :1000930012000CFF700D3007057F0012004FAF7A7E :1000A300AE7922B4255FC2D5C20412000CFF24D05E :1000B300B40A00501A75F00A787730D50508B6FFF0 :1000C3000106C6A426F620D5047002D20380D924E3 :1000D300CFB41A00EF5004C2E5D20402024FD2019A :1000E30080C6D20080C0D20280BCD2D580BAD205ED :1000F30080B47F2012003E2002077401B5770040D0 :10010300F1120003FF12003E020077D208D20680EC :1001130095120003FB120003FA120003F94A4B7015 :100123000679207A037BFF20022EE577602A7E0082 :100133008E8275830012046E60060EEE657870F091 :10014300C2D5EBC0E0EAC0E0E9C0E0EE120296D00F :10015300E0F9D0E0FAD0E0FB120455FF60AAEBC04F :10016300E0EAC0E0E9C0E012003ED0E02401F9D0AB :10017300E03400FAD0E0FBE5780460DCD578D98080 :10018300877BFF7A027992D202809C791080027970 :1001930008C206C2088008D2D5790A8004790AC247 :1001A300D5E578047002F578E4FAFDFEFF1200034A :1001B300FC7B08200113120003FD7B1030000A12A0 :1001C3000003FE120003FF7B20EC3382D592D5504F :1001D30013C3E43000069FFFE49EFEE42001039D69 :1001E300FDE49CFCE4CBF8C201EC700CCFCECDCC8B :1001F300E824F8F870F38017C3EF33FFEE33FEED16 :1002030033FDEC33FCEB33FB994002FB0FD8E9EBF6 :10021300300105F8D0E0C448B201C0E00AEC4D4E0D :100223004F78207B0070C2EAB5780040BCC0E01272 :100233000298D0F0D0E0200104C4C0E0C4B201C0F1 :10024300F0120027D0F0D5F0EB0200771204BD01C5 :100253001453018E5800E54C00E14201924F019A7C :0F02630044019A4900FA4301A0550184460184E1 :100272004501844703405000E92D00ED2E01102B6B :1002820000F123010E2003292A00A94800000108D9 :100292003F3F3F00790AA2D5200314300509B91067 :1002A200020404B9080104A2D52006025001042068 :1002B20002689202B577005034C0E07F2030031903 :1002C2007F30A20272067205500F1202EFC202C202 :1002D20006C205C2087F30800F300503E9C0E01274 :1002E200003E300503D0E0F9D0E0B577CC300517F9 :1002F2007F30B9100C12003E7F583004077F78809F :1003020003B9080312003E3002057F2D02003E7F32 :10031200202008F87F2B2006F322920280CF286E3D :10032200756C6C2900D2011200033001F8C2017809 :100332007730D50108F60200A92D50434958120022 :10034200032403B405004001E490033B9312002F01 :0D035200743A12002FD20375770402018E59 :10045500BB010689828A83E0225002E722BBFE02A5 :09046500E32289828A83E49322D8 :10046E00BB010CE58229F582E5833AF583E0225043 :10047E0006E92582F8E622BBFE06E92582F8E2228D :0D048E00E58229F582E5833AF583E49322A7 :10049B00BB010689828A83F0225002F722BBFE0140 :0204AB00F3223A :1004AD00FAE6FB0808E6F925F0F618E6CA3AF62250 :1004BD00D083D082F8E4937012740193700DA3A3CE :1004CD0093F8740193F5828883E4737402936860E2 :0604DD00EFA3A3A380DFE2 :10057B00EFB40A07740D120586740A309811A89906 :10058B00B8130CC2983098FDA899C298B811F630E0 :07059B0099FDC299F59922B8 :00000001FF ''' bin8 = array.array('B',[2, 5, 162, 229, 118, 36, 106, 248, 230, 5, 118, 34, 120, 103, 48, 7, 2, 120, 106, 228, 117, 240, 1, 18, 4, 173, 2, 4, 85, 32, 0, 235, 127, 46, 210, 0, 128, 24, 239, 84, 15, 36, 144, 212, 52, 64, 212, 255, 48, 4, 11, 239, 36, 191, 180, 26, 0, 80, 3, 36, 97, 255, 229, 119, 96, 2, 21, 119, 5, 122, 229, 122, 112, 2, 5, 121, 48, 7, 13, 120, 103, 228, 117, 240, 1, 18, 4, 173, 239, 2, 4, 155, 2, 5, 123, 116, 3, 210, 7, 128, 3, 228, 194, 7, 245, 118, 139, 103, 138, 104, 137, 105, 228, 245, 119, 245, 121, 245, 122, 229, 119, 96, 7, 127, 32, 18, 0, 62, 128, 245, 117, 120, 255, 194, 1, 194, 0, 194, 2, 194, 3, 194, 5, 194, 6, 194, 8, 18, 0, 12, 255, 112, 13, 48, 7, 5, 127, 0, 18, 0, 79, 175, 122, 174, 121, 34, 180, 37, 95, 194, 213, 194, 4, 18, 0, 12, 255, 36, 208, 180, 10, 0, 80, 26, 117, 240, 10, 120, 119, 48, 213, 5, 8, 182, 255, 1, 6, 198, 164, 38, 246, 32, 213, 4, 112, 2, 210, 3, 128, 217, 36, 207, 180, 26, 0, 239, 80, 4, 194, 229, 210, 4, 2, 2, 79, 210, 1, 128, 198, 210, 0, 128, 192, 210, 2, 128, 188, 210, 213, 128, 186, 210, 5, 128, 180, 127, 32, 18, 0, 62, 32, 2, 7, 116, 1, 181, 119, 0, 64, 241, 18, 0, 3, 255, 18, 0, 62, 2, 0, 119, 210, 8, 210, 6, 128, 149, 18, 0, 3, 251, 18, 0, 3, 250, 18, 0, 3, 249, 74, 75, 112, 6, 121, 32, 122, 3, 123, 255, 32, 2, 46, 229, 119, 96, 42, 126, 0, 142, 130, 117, 131, 0, 18, 4, 110, 96, 6, 14, 238, 101, 120, 112, 240, 194, 213, 235, 192, 224, 234, 192, 224, 233, 192, 224, 238, 18, 2, 150, 208, 224, 249, 208, 224, 250, 208, 224, 251, 18, 4, 85, 255, 96, 170, 235, 192, 224, 234, 192, 224, 233, 192, 224, 18, 0, 62, 208, 224, 36, 1, 249, 208, 224, 52, 0, 250, 208, 224, 251, 229, 120, 4, 96, 220, 213, 120, 217, 128, 135, 123, 255, 122, 2, 121, 146, 210, 2, 128, 156, 121, 16, 128, 2, 121, 8, 194, 6, 194, 8, 128, 8, 210, 213, 121, 10, 128, 4, 121, 10, 194, 213, 229, 120, 4, 112, 2, 245, 120, 228, 250, 253, 254, 255, 18, 0, 3, 252, 123, 8, 32, 1, 19, 18, 0, 3, 253, 123, 16, 48, 0, 10, 18, 0, 3, 254, 18, 0, 3, 255, 123, 32, 236, 51, 130, 213, 146, 213, 80, 19, 195, 228, 48, 0, 6, 159, 255, 228, 158, 254, 228, 32, 1, 3, 157, 253, 228, 156, 252, 228, 203, 248, 194, 1, 236, 112, 12, 207, 206, 205, 204, 232, 36, 248, 248, 112, 243, 128, 23, 195, 239, 51, 255, 238, 51, 254, 237, 51, 253, 236, 51, 252, 235, 51, 251, 153, 64, 2, 251, 15, 216, 233, 235, 48, 1, 5, 248, 208, 224, 196, 72, 178, 1, 192, 224, 10, 236, 77, 78, 79, 120, 32, 123, 0, 112, 194, 234, 181, 120, 0, 64, 188, 192, 224, 18, 2, 152, 208, 240, 208, 224, 32, 1, 4, 196, 192, 224, 196, 178, 1, 192, 240, 18, 0, 39, 208, 240, 213, 240, 235, 2, 0, 119, 18, 4, 189, 1, 20, 83, 1, 142, 88, 0, 229, 76, 0, 225, 66, 1, 146, 79, 1, 154, 68, 1, 154, 73, 0, 250, 67, 1, 160, 85, 1, 132, 70, 1, 132, 69, 1, 132, 71, 3, 64, 80, 0, 233, 45, 0, 237, 46, 1, 16, 43, 0, 241, 35, 1, 14, 32, 3, 41, 42, 0, 169, 72, 0, 0, 1, 8, 63, 63, 63, 0, 121, 10, 162, 213, 32, 3, 20, 48, 5, 9, 185, 16, 2, 4, 4, 185, 8, 1, 4, 162, 213, 32, 6, 2, 80, 1, 4, 32, 2, 104, 146, 2, 181, 119, 0, 80, 52, 192, 224, 127, 32, 48, 3, 25, 127, 48, 162, 2, 114, 6, 114, 5, 80, 15, 18, 2, 239, 194, 2, 194, 6, 194, 5, 194, 8, 127, 48, 128, 15, 48, 5, 3, 233, 192, 224, 18, 0, 62, 48, 5, 3, 208, 224, 249, 208, 224, 181, 119, 204, 48, 5, 23, 127, 48, 185, 16, 12, 18, 0, 62, 127, 88, 48, 4, 7, 127, 120, 128, 3, 185, 8, 3, 18, 0, 62, 48, 2, 5, 127, 45, 2, 0, 62, 127, 32, 32, 8, 248, 127, 43, 32, 6, 243, 34, 146, 2, 128, 207, 40, 110, 117, 108, 108, 41, 0, 210, 1, 18, 0, 3, 48, 1, 248, 194, 1, 120, 119, 48, 213, 1, 8, 246, 2, 0, 169, 45, 80, 67, 73, 88, 18, 0, 3, 36, 3, 180, 5, 0, 64, 1, 228, 144, 3, 59, 147, 18, 0, 47, 116, 58, 18, 0, 47, 210, 3, 117, 119, 4, 2, 1, 142, 231, 9, 246, 8, 223, 250, 128, 70, 231, 9, 242, 8, 223, 250, 128, 62, 136, 130, 140, 131, 231, 9, 240, 163, 223, 250, 128, 50, 227, 9, 246, 8, 223, 250, 128, 120, 227, 9, 242, 8, 223, 250, 128, 112, 136, 130, 140, 131, 227, 9, 240, 163, 223, 250, 128, 100, 137, 130, 138, 131, 224, 163, 246, 8, 223, 250, 128, 88, 137, 130, 138, 131, 224, 163, 242, 8, 223, 250, 128, 76, 128, 210, 128, 250, 128, 198, 128, 212, 128, 105, 128, 242, 128, 51, 128, 16, 128, 166, 128, 234, 128, 154, 128, 168, 128, 218, 128, 226, 128, 202, 128, 51, 137, 130, 138, 131, 236, 250, 228, 147, 163, 200, 197, 130, 200, 204, 197, 131, 204, 240, 163, 200, 197, 130, 200, 204, 197, 131, 204, 223, 233, 222, 231, 128, 13, 137, 130, 138, 131, 228, 147, 163, 246, 8, 223, 249, 236, 250, 169, 240, 237, 251, 34, 137, 130, 138, 131, 236, 250, 224, 163, 200, 197, 130, 200, 204, 197, 131, 204, 240, 163, 200, 197, 130, 200, 204, 197, 131, 204, 223, 234, 222, 232, 128, 219, 137, 130, 138, 131, 228, 147, 163, 242, 8, 223, 249, 128, 204, 136, 240, 239, 96, 1, 14, 78, 96, 195, 136, 240, 237, 36, 2, 180, 4, 0, 80, 185, 245, 130, 235, 36, 2, 180, 4, 0, 80, 175, 35, 35, 69, 130, 35, 144, 3, 175, 115, 187, 1, 6, 137, 130, 138, 131, 224, 34, 80, 2, 231, 34, 187, 254, 2, 227, 34, 137, 130, 138, 131, 228, 147, 34, 187, 1, 12, 229, 130, 41, 245, 130, 229, 131, 58, 245, 131, 224, 34, 80, 6, 233, 37, 130, 248, 230, 34, 187, 254, 6, 233, 37, 130, 248, 226, 34, 229, 130, 41, 245, 130, 229, 131, 58, 245, 131, 228, 147, 34, 187, 1, 6, 137, 130, 138, 131, 240, 34, 80, 2, 247, 34, 187, 254, 1, 243, 34, 250, 230, 251, 8, 8, 230, 249, 37, 240, 246, 24, 230, 202, 58, 246, 34, 208, 131, 208, 130, 248, 228, 147, 112, 18, 116, 1, 147, 112, 13, 163, 163, 147, 248, 116, 1, 147, 245, 130, 136, 131, 228, 115, 116, 2, 147, 104, 96, 239, 163, 163, 163, 128, 223, 207, 240, 251, 226, 253, 242, 32, 255, 32, 242, 225, 32, 226, 251, 230, 243, 10, 0, 253, 224, 225, 226, 227, 180, 228, 229, 186, 230, 231, 179, 191, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 252, 254, 255, 0, 192, 193, 194, 195, 165, 196, 197, 170, 198, 199, 178, 175, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 220, 222, 223, 0, 120, 34, 124, 0, 125, 0, 123, 255, 122, 4, 121, 245, 126, 0, 127, 35, 18, 4, 47, 120, 69, 124, 0, 125, 0, 123, 255, 122, 5, 121, 24, 126, 0, 127, 34, 18, 4, 47, 117, 152, 80, 67, 137, 32, 117, 141, 221, 210, 142, 210, 153, 99, 144, 1, 123, 255, 122, 4, 121, 227, 18, 0, 101, 128, 254, 239, 180, 10, 7, 116, 13, 18, 5, 134, 116, 10, 48, 152, 17, 168, 153, 184, 19, 12, 194, 152, 48, 152, 253, 168, 153, 194, 152, 184, 17, 246, 48, 153, 253, 194, 153, 245, 153, 34, 120, 127, 228, 246, 216, 253, 117, 129, 122, 2, 5, 58]) hex16 = """:020000040000FA :10000000000083120313072055301820042883169C :10001000031340309900181598168312031318160D :1000200098170800831203138C1E14281A0808005E :0C003000831203130C1E1A28990008000C :00000001FF """ bin16 = array.array('H', [0x0000, 0x1283, 0x1303, 0x2007, 0x3055, 0x2018, 0x2804, 0x1683, 0x1303, 0x3040, 0x0099, 0x1518, 0x1698, 0x1283, 0x1303, 0x1618, 0x1798, 0x0008, 0x1283, 0x1303, 0x1E8C, 0x2814, 0x081A, 0x0008, 0x1283, 0x1303, 0x1E0C, 0x281A, 0x0099, 0x0008, 0x3FFF, 0x3FFF]) hex64k = """:020000040000FA :0100000001FE :020000040001F9 :0100000002FD :00000001FF """ data64k = {0: 1, 0x10000: 2} hex_rectype3 = """:0400000312345678E5 :0100000001FE :00000001FF """ data_rectype3 = {0: 1} start_addr_rectype3 = {'CS': 0x1234, 'IP': 0x5678} hex_rectype5 = """:0400000512345678E3 :0100000002FD :00000001FF """ data_rectype5 = {0: 2} start_addr_rectype5 = {'EIP': 0x12345678} hex_empty_file = ':00000001FF\n' hex_simple = """\ :10000000000083120313072055301820042883169C :10001000031340309900181598168312031318160D :1000200098170800831203138C1E14281A0808005E :0C003000831203130C1E1A28990008000C :00000001FF """ hex_bug_lp_341051 = """\ :020FEC00E4E738 :040FF00022E122E1F7 :00000001FF """ ## # Test cases class TestIntelHexBase(unittest.TestCase): """Base class for all tests. Provide additional functionality for testing. """ def assertRaisesMsg(self, excClass, msg, callableObj, *args, **kwargs): """Just like unittest.TestCase.assertRaises, but checks that the message is right too. Borrowed from Ned Batchelder Blog. See: http://www.nedbatchelder.com/blog/200609.html#e20060905T064418 Typical usage:: self.assertRaisesMsg(MyException, "Exception message", my_function, (arg1, arg2)) """ try: callableObj(*args, **kwargs) except excClass: exc = sys.exc_info()[1] # current exception excMsg = str(exc) if not msg: # No message provided: any message is fine. return elif excMsg == msg: # Message provided, and we got the right message: it passes. return else: # Message provided, and it didn't match: fail! raise self.failureException( "Right exception, wrong message: got '%s' expected '%s'" % (excMsg, msg) ) else: if hasattr(excClass, '__name__'): excName = excClass.__name__ else: excName = str(excClass) raise self.failureException( "Expected to raise %s, didn't get an exception at all" % excName ) def assertEqualWrittenData(self, a, b): return self.assertEqual(a, b, """Written data is incorrect Should be: %s Written: %s """ % (a, b)) #/class TestIntelHexBase class TestIntelHex(TestIntelHexBase): def setUp(self): self.f = StringIO(hex8) def tearDown(self): self.f.close() del self.f def test_init_from_file(self): ih = IntelHex(self.f) for addr in range_g(len(bin8)): expected = bin8[addr] actual = ih[addr] self.assertEqual(expected, actual, "Data different at address " "%x (%x != %x)" % (addr, expected, actual)) def test_hex_fromfile(self): ih = IntelHex() ih.fromfile(self.f, format='hex') for addr in range_g(len(bin8)): expected = bin8[addr] actual = ih[addr] self.assertEqual(expected, actual, "Data different at address " "%x (%x != %x)" % (addr, expected, actual)) def test_unicode_filename(self): handle, fname = tempfile.mkstemp(UnicodeType('')) os.close(handle) try: self.assertTrue(isinstance(fname, UnicodeType)) f = open(fname, 'w') try: f.write(hex8) finally: f.close() ih = IntelHex(fname) self.assertEqual(0, ih.minaddr()) self.assertEqual(len(bin8)-1, ih.maxaddr()) finally: os.remove(fname) def test_tobinarray_empty(self): ih = IntelHex() ih.padding = 0xFF # set-up explicit padding value and don't use pad parameter self.assertEqual(array.array('B', []), ih.tobinarray()) self.assertEqual(array.array('B', []), ih.tobinarray(start=0)) self.assertEqual(array.array('B', []), ih.tobinarray(end=2)) self.assertEqual(array.array('B', [255,255,255]), ih.tobinarray(0,2)) def test_tobinarray_with_size(self): ih = IntelHex(self.f) self.assertEqual(array.array('B', [2, 5, 162, 229, 118, 36, 106, 248]), ih.tobinarray(size=8)) # from addr 0 self.assertEqual(array.array('B', [120, 103, 48, 7, 2, 120, 106, 228]), ih.tobinarray(start=12, size=8)) self.assertEqual(array.array('B', [2, 5, 162, 229, 118, 36, 106, 248]), ih.tobinarray(end=7, size=8)) # addr: 0..7, 8 bytes self.assertEqual(array.array('B', [120, 103, 48, 7, 2, 120, 106, 228]), ih.tobinarray(end=19, size=8)) # addr: 12..19, 8 bytes self.assertRaises(ValueError, ih.tobinarray, start=0, end=7, size=8) self.assertRaises(ValueError, ih.tobinarray, end=3, size=8) self.assertRaises(ValueError, ih.tobinarray, size=0) self.assertRaises(ValueError, ih.tobinarray, size=-1) def test_tobinstr(self): ih = IntelHex(self.f) s1 = ih.tobinstr() s2 = array_tobytes(bin8) self.assertEqual(s2, s1, "data not equal\n%s\n\n%s" % (s1, s2)) def test_tobinfile(self): ih = IntelHex(self.f) sio = BytesIO() ih.tobinfile(sio) s1 = sio.getvalue() sio.close() s2 = array_tobytes(bin8) self.assertEqual(s2, s1, "data not equal\n%s\n\n%s" % (s1, s2)) # new API: .tofile universal method sio = BytesIO() ih.tofile(sio, format='bin') s1 = sio.getvalue() sio.close() s2 = array_tobytes(bin8) self.assertEqual(s2, s1, "data not equal\n%s\n\n%s" % (s1, s2)) def test_tobinfile_realfile(self): ih = IntelHex(self.f) tf = tempfile.TemporaryFile(mode='wb') try: ih.tobinfile(tf) finally: tf.close() def test__get_eol_textfile(self): self.assertEqual('\n', IntelHex._get_eol_textfile('native', 'win32')) self.assertEqual('\n', IntelHex._get_eol_textfile('native', 'linux')) self.assertEqual('\n', IntelHex._get_eol_textfile('CRLF', 'win32')) self.assertEqual('\r\n', IntelHex._get_eol_textfile('CRLF', 'linux')) self.assertRaisesMsg(ValueError, "wrong eolstyle 'LF'", IntelHex._get_eol_textfile, 'LF', 'win32') def test_write_empty_hexfile(self): ih = intelhex.IntelHex() sio = StringIO() ih.write_hex_file(sio) s = sio.getvalue() sio.close() self.assertEqualWrittenData(hex_empty_file, s) def test_write_hexfile(self): ih = intelhex.IntelHex(StringIO(hex_simple)) sio = StringIO() ih.write_hex_file(sio) s = sio.getvalue() sio.close() self.assertEqualWrittenData(hex_simple, s) # new API: .tofile universal method sio = StringIO() ih.tofile(sio, format='hex') s = sio.getvalue() sio.close() self.assertEqualWrittenData(hex_simple, s) def test_write_hex_bug_341051(self): ih = intelhex.IntelHex(StringIO(hex_bug_lp_341051)) sio = StringIO() ih.tofile(sio, format='hex') s = sio.getvalue() sio.close() self.assertEqualWrittenData(hex_bug_lp_341051, s) def test_write_hex_first_extended_linear_address(self): ih = IntelHex({0x20000: 0x01}) sio = StringIO() ih.write_hex_file(sio) s = sio.getvalue() sio.close() # should be r = [Record.extended_linear_address(2), Record.data(0x0000, [0x01]), Record.eof()] h = '\n'.join(r) + '\n' # compare self.assertEqual(h, s) def test_tofile_wrong_format(self): ih = IntelHex() sio = StringIO() self.assertRaises(ValueError, ih.tofile, sio, {'format': 'bad'}) def test_todict(self): ih = IntelHex() self.assertEqual({}, ih.todict()) ih = IntelHex(StringIO(hex64k)) self.assertEqual(data64k, ih.todict()) ih = IntelHex() ih[1] = 2 ih.start_addr = {'EIP': 1234} self.assertEqual({1: 2, 'start_addr': {'EIP': 1234}}, ih.todict()) def test_fromdict(self): ih = IntelHex() ih.fromdict({1:2, 3:4}) self.assertEqual({1:2, 3:4}, ih.todict()) ih.fromdict({1:5, 6:7}) self.assertEqual({1:5, 3:4, 6:7}, ih.todict()) ih = IntelHex() ih.fromdict({1: 2, 'start_addr': {'EIP': 1234}}) self.assertEqual({1: 2, 'start_addr': {'EIP': 1234}}, ih.todict()) # bad dict self.assertRaises(ValueError, ih.fromdict, {'EIP': 1234}) self.assertRaises(ValueError, ih.fromdict, {-1: 1234}) def test_init_from_obj(self): ih = IntelHex({1:2, 3:4}) self.assertEqual({1:2, 3:4}, ih.todict()) ih.start_addr = {'EIP': 1234} ih2 = IntelHex(ih) ih[1] = 5 ih.start_addr = {'EIP': 5678} self.assertEqual({1:2, 3:4, 'start_addr': {'EIP': 1234}}, ih2.todict()) self.assertNotEqual(id(ih), id(ih2)) def test_dict_interface(self): ih = IntelHex() self.assertEqual(0xFF, ih[0]) # padding byte substitution ih[0] = 1 self.assertEqual(1, ih[0]) del ih[0] self.assertEqual({}, ih.todict()) # padding byte substitution def test_len(self): ih = IntelHex() self.assertEqual(0, len(ih)) ih[2] = 1 self.assertEqual(1, len(ih)) ih[1000] = 2 self.assertEqual(2, len(ih)) def test__getitem__(self): ih = IntelHex() # simple cases self.assertEqual(0xFF, ih[0]) ih[0] = 1 self.assertEqual(1, ih[0]) # big address self.assertEqual(0xFF, ih[2**32-1]) # wrong addr type/value for indexing operations def getitem(index): return ih[index] self.assertRaisesMsg(TypeError, 'Address should be >= 0.', getitem, -1) self.assertRaisesMsg(TypeError, "Address has unsupported type: %s" % type('foo'), getitem, 'foo') # new object with some data ih = IntelHex() ih[0] = 1 ih[1] = 2 ih[2] = 3 ih[10] = 4 # full copy via slicing ih2 = ih[:] self.assertTrue(isinstance(ih2, IntelHex)) self.assertEqual({0:1, 1:2, 2:3, 10:4}, ih2.todict()) # other slice operations self.assertEqual({}, ih[3:8].todict()) self.assertEqual({0:1, 1:2}, ih[0:2].todict()) self.assertEqual({0:1, 1:2}, ih[:2].todict()) self.assertEqual({2:3, 10:4}, ih[2:].todict()) self.assertEqual({0:1, 2:3, 10:4}, ih[::2].todict()) self.assertEqual({10:4}, ih[3:11].todict()) def test__setitem__(self): ih = IntelHex() # simple indexing operation ih[0] = 1 self.assertEqual({0:1}, ih.todict()) # errors def setitem(a,b): ih[a] = b self.assertRaisesMsg(TypeError, 'Address should be >= 0.', setitem, -1, 0) self.assertRaisesMsg(TypeError, "Address has unsupported type: %s" % type('foo'), setitem, 'foo', 0) # slice operations ih[0:4] = range_l(4) self.assertEqual({0:0, 1:1, 2:2, 3:3}, ih.todict()) ih[0:] = range_l(5,9) self.assertEqual({0:5, 1:6, 2:7, 3:8}, ih.todict()) ih[:4] = range_l(9,13) self.assertEqual({0:9, 1:10, 2:11, 3:12}, ih.todict()) # with step ih = IntelHex() ih[0:8:2] = range_l(4) self.assertEqual({0:0, 2:1, 4:2, 6:3}, ih.todict()) # errors in slice operations # ih[1:2] = 'a' self.assertRaisesMsg(ValueError, 'Slice operation expects sequence of bytes', setitem, slice(1,2,None), 'a') # ih[0:1] = [1,2,3] self.assertRaisesMsg(ValueError, 'Length of bytes sequence does not match address range', setitem, slice(0,1,None), [1,2,3]) # ih[:] = [1,2,3] self.assertRaisesMsg(TypeError, 'Unsupported address range', setitem, slice(None,None,None), [1,2,3]) # ih[:2] = [1,2,3] self.assertRaisesMsg(TypeError, 'start address cannot be negative', setitem, slice(None,2,None), [1,2,3]) # ih[0:-3:-1] = [1,2,3] self.assertRaisesMsg(TypeError, 'stop address cannot be negative', setitem, slice(0,-3,-1), [1,2,3]) def test__delitem__(self): ih = IntelHex() ih[0] = 1 del ih[0] self.assertEqual({}, ih.todict()) # errors def delitem(addr): del ih[addr] self.assertRaises(KeyError, delitem, 1) self.assertRaisesMsg(TypeError, 'Address should be >= 0.', delitem, -1) self.assertRaisesMsg(TypeError, "Address has unsupported type: %s" % type('foo'), delitem, 'foo') # deleting slice del ih[0:1] # no error here because of slicing # def ihex(size=8): ih = IntelHex() for i in range_g(size): ih[i] = i return ih ih = ihex(8) del ih[:] # delete all data self.assertEqual({}, ih.todict()) ih = ihex(8) del ih[2:6] self.assertEqual({0:0, 1:1, 6:6, 7:7}, ih.todict()) ih = ihex(8) del ih[::2] self.assertEqual({1:1, 3:3, 5:5, 7:7}, ih.todict()) def test_addresses(self): # empty object ih = IntelHex() self.assertEqual([], ih.addresses()) self.assertEqual(None, ih.minaddr()) self.assertEqual(None, ih.maxaddr()) # normal object ih = IntelHex({1:2, 7:8, 10:0}) self.assertEqual([1,7,10], ih.addresses()) self.assertEqual(1, ih.minaddr()) self.assertEqual(10, ih.maxaddr()) def test__get_start_end(self): # test for private method _get_start_end # for empty object ih = IntelHex() self.assertRaises(intelhex.EmptyIntelHexError, ih._get_start_end) self.assertRaises(intelhex.EmptyIntelHexError, ih._get_start_end, size=10) self.assertEqual((0,9), ih._get_start_end(start=0, size=10)) self.assertEqual((1,10), ih._get_start_end(end=10, size=10)) # normal object ih = IntelHex({1:2, 7:8, 10:0}) self.assertEqual((1,10), ih._get_start_end()) self.assertEqual((1,10), ih._get_start_end(size=10)) self.assertEqual((0,9), ih._get_start_end(start=0, size=10)) self.assertEqual((1,10), ih._get_start_end(end=10, size=10)) def test_segments(self): # test that address segments are correctly summarized ih = IntelHex() sg = ih.segments() self.assertTrue(isinstance(sg, list)) self.assertEqual(len(sg), 0) ih[0x100] = 0 sg = ih.segments() self.assertTrue(isinstance(sg, list)) self.assertEqual(len(sg), 1) self.assertTrue(isinstance(sg[0], tuple)) self.assertTrue(len(sg[0]) == 2) self.assertTrue(sg[0][0] < sg[0][1]) self.assertEqual(min(sg[0]), 0x100) self.assertEqual(max(sg[0]), 0x101) ih[0x101] = 1 sg = ih.segments() self.assertTrue(isinstance(sg, list)) self.assertEqual(len(sg), 1) self.assertTrue(isinstance(sg[0], tuple)) self.assertTrue(len(sg[0]) == 2) self.assertTrue(sg[0][0] < sg[0][1]) self.assertEqual(min(sg[0]), 0x100) self.assertEqual(max(sg[0]), 0x102) ih[0x200] = 2 ih[0x201] = 3 ih[0x202] = 4 sg = ih.segments() self.assertTrue(isinstance(sg, list)) self.assertEqual(len(sg), 2) self.assertTrue(isinstance(sg[0], tuple)) self.assertTrue(len(sg[0]) == 2) self.assertTrue(sg[0][0] < sg[0][1]) self.assertTrue(isinstance(sg[1], tuple)) self.assertTrue(len(sg[1]) == 2) self.assertTrue(sg[1][0] < sg[1][1]) self.assertEqual(min(sg[0]), 0x100) self.assertEqual(max(sg[0]), 0x102) self.assertEqual(min(sg[1]), 0x200) self.assertEqual(max(sg[1]), 0x203) ih[0x204] = 5 sg = ih.segments() self.assertEqual(len(sg), 3) sg = ih.segments(min_gap=2) self.assertEqual(len(sg), 2) self.assertEqual(min(sg[1]), 0x200) self.assertEqual(max(sg[1]), 0x205) pass class TestIntelHexLoadBin(TestIntelHexBase): def setUp(self): self.bytes = asbytes('0123456789') self.f = BytesIO(self.bytes) def tearDown(self): self.f.close() def test_loadbin(self): ih = IntelHex() ih.loadbin(self.f) self.assertEqual(0, ih.minaddr()) self.assertEqual(9, ih.maxaddr()) self.assertEqual(self.bytes, ih.tobinstr()) def test_bin_fromfile(self): ih = IntelHex() ih.fromfile(self.f, format='bin') self.assertEqual(0, ih.minaddr()) self.assertEqual(9, ih.maxaddr()) self.assertEqual(self.bytes, ih.tobinstr()) def test_loadbin_w_offset(self): ih = IntelHex() ih.loadbin(self.f, offset=100) self.assertEqual(100, ih.minaddr()) self.assertEqual(109, ih.maxaddr()) self.assertEqual(self.bytes, ih.tobinstr()) def test_loadfile_format_bin(self): ih = IntelHex() ih.loadfile(self.f, format='bin') self.assertEqual(0, ih.minaddr()) self.assertEqual(9, ih.maxaddr()) self.assertEqual(self.bytes, ih.tobinstr()) class TestIntelHexStartingAddressRecords(TestIntelHexBase): def _test_read(self, hexstr, data, start_addr): sio = StringIO(hexstr) ih = IntelHex(sio) sio.close() # test data self.assertEqual(data, ih._buf, "Internal buffer: %r != %r" % (data, ih._buf)) self.assertEqual(start_addr, ih.start_addr, "Start address: %r != %r" % (start_addr, ih.start_addr)) def test_read_rectype3(self): self._test_read(hex_rectype3, data_rectype3, start_addr_rectype3) def test_read_rectype5(self): self._test_read(hex_rectype5, data_rectype5, start_addr_rectype5) def _test_write(self, hexstr, data, start_addr, write_start_addr=True): # prepare ih = IntelHex(None) ih._buf = data ih.start_addr = start_addr # write sio = StringIO() ih.write_hex_file(sio, write_start_addr) s = sio.getvalue() sio.close() # check self.assertEqualWrittenData(hexstr, s) def _test_dont_write(self, hexstr, data, start_addr): expected = ''.join(hexstr.splitlines(True)[1:]) self._test_write(expected, data, start_addr, False) def test_write_rectype3(self): self._test_write(hex_rectype3, data_rectype3, start_addr_rectype3) def test_dont_write_rectype3(self): self._test_dont_write(hex_rectype3, data_rectype3, start_addr_rectype3) def test_write_rectype5(self): self._test_write(hex_rectype5, data_rectype5, start_addr_rectype5) def test_dont_write_rectype5(self): self._test_dont_write(hex_rectype5, data_rectype5, start_addr_rectype5) def test_write_invalid_start_addr_value(self): ih = IntelHex() ih.start_addr = {'foo': 1} sio = StringIO() self.assertRaises(InvalidStartAddressValueError, ih.write_hex_file, sio) class TestIntelHex_big_files(TestIntelHexBase): """Test that data bigger than 64K read/write correctly""" def setUp(self): self.f = StringIO(hex64k) def tearDown(self): self.f.close() del self.f def test_readfile(self): ih = intelhex.IntelHex(self.f) for addr, byte in dict_items_g(data64k): readed = ih[addr] self.assertEqual(byte, readed, "data not equal at addr %X " "(%X != %X)" % (addr, byte, readed)) def test_write_hex_file(self): ih = intelhex.IntelHex(self.f) sio = StringIO() ih.write_hex_file(sio) s = sio.getvalue() sio.close() self.assertEqualWrittenData(hex64k, s) class TestIntelHexGetPutString(TestIntelHexBase): def setUp(self): self.ih = IntelHex() for i in range_g(10): self.ih[i] = i def test_gets(self): self.assertEqual(asbytes('\x00\x01\x02\x03\x04\x05\x06\x07'), self.ih.gets(0, 8)) self.assertEqual(asbytes('\x07\x08\x09'), self.ih.gets(7, 3)) self.assertRaisesMsg(intelhex.NotEnoughDataError, 'Bad access at 0x1: ' 'not enough data to read 10 contiguous bytes', self.ih.gets, 1, 10) def test_puts(self): self.ih.puts(0x03, asbytes('hello')) self.assertEqual(asbytes('\x00\x01\x02hello\x08\x09'), self.ih.gets(0, 10)) def test_getsz(self): self.assertEqual(asbytes(''), self.ih.getsz(0)) self.assertRaisesMsg(intelhex.NotEnoughDataError, 'Bad access at 0x1: ' 'not enough data to read zero-terminated string', self.ih.getsz, 1) self.ih[4] = 0 self.assertEqual(asbytes('\x01\x02\x03'), self.ih.getsz(1)) def test_putsz(self): self.ih.putsz(0x03, asbytes('hello')) self.assertEqual(asbytes('\x00\x01\x02hello\x00\x09'), self.ih.gets(0, 10)) def test_find(self): self.assertEqual(0, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'))) self.assertEqual(0, self.ih.find(asbytes('\x00'))) self.assertEqual(3, self.ih.find(asbytes('\x03\x04\x05\x06'))) self.assertEqual(3, self.ih.find(asbytes('\x03'))) self.assertEqual(7, self.ih.find(asbytes('\x07\x08\x09'))) self.assertEqual(7, self.ih.find(asbytes('\x07'))) self.assertEqual(-1, self.ih.find(asbytes('\x0a'))) self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'))) self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'))) def test_find_start(self): self.assertEqual(-1, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'), start=3)) self.assertEqual(-1, self.ih.find(asbytes('\x00'), start=3)) self.assertEqual(3, self.ih.find(asbytes('\x03\x04\x05\x06'), start=3)) self.assertEqual(3, self.ih.find(asbytes('\x03'), start=3)) self.assertEqual(7, self.ih.find(asbytes('\x07\x08\x09'), start=3)) self.assertEqual(7, self.ih.find(asbytes('\x07'), start=3)) self.assertEqual(-1, self.ih.find(asbytes('\x0a'), start=3)) self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'), start=3)) self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'), start=3)) def test_find_end(self): self.assertEqual(-1, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'), end=4)) self.assertEqual(0, self.ih.find(asbytes('\x00'), end=4)) self.assertEqual(-1, self.ih.find(asbytes('\x03\x04\x05\x06'), end=4)) self.assertEqual(3, self.ih.find(asbytes('\x03'), end=4)) self.assertEqual(-1, self.ih.find(asbytes('\x07\x08\x09'), end=4)) self.assertEqual(-1, self.ih.find(asbytes('\x07'), end=4)) self.assertEqual(-1, self.ih.find(asbytes('\x0a'), end=4)) self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'), end=4)) self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'), end=4)) def test_find_start_end(self): self.assertEqual(-1, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'), start=3, end=7)) self.assertEqual(-1, self.ih.find(asbytes('\x00'), start=3, end=7)) self.assertEqual(3, self.ih.find(asbytes('\x03\x04\x05\x06'), start=3, end=7)) self.assertEqual(3, self.ih.find(asbytes('\x03'), start=3, end=7)) self.assertEqual(-1, self.ih.find(asbytes('\x07\x08\x09'), start=3, end=7)) self.assertEqual(-1, self.ih.find(asbytes('\x07'), start=3, end=7)) self.assertEqual(-1, self.ih.find(asbytes('\x0a'), start=3, end=7)) self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'), start=3, end=7)) self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'), start=3, end=7)) class TestIntelHexDump(TestIntelHexBase): def test_empty(self): ih = IntelHex() sio = StringIO() ih.dump(sio) self.assertEqual('', sio.getvalue()) def test_simple(self): ih = IntelHex() ih[0] = 0x12 ih[1] = 0x34 sio = StringIO() ih.dump(sio) self.assertEqual( '0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n', sio.getvalue()) ih[16] = 0x56 ih[30] = 0x98 sio = StringIO() ih.dump(sio) self.assertEqual( '0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n' '0010 56 -- -- -- -- -- -- -- -- -- -- -- -- -- 98 -- |V . |\n', sio.getvalue()) def test_minaddr_not_zero(self): ih = IntelHex() ih[16] = 0x56 ih[30] = 0x98 sio = StringIO() ih.dump(sio) self.assertEqual( '0010 56 -- -- -- -- -- -- -- -- -- -- -- -- -- 98 -- |V . |\n', sio.getvalue()) def test_start_addr(self): ih = IntelHex() ih[0] = 0x12 ih[1] = 0x34 ih.start_addr = {'CS': 0x1234, 'IP': 0x5678} sio = StringIO() ih.dump(sio) self.assertEqual( 'CS = 0x1234, IP = 0x5678\n' '0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n', sio.getvalue()) ih.start_addr = {'EIP': 0x12345678} sio = StringIO() ih.dump(sio) self.assertEqual( 'EIP = 0x12345678\n' '0000 12 34 -- -- -- -- -- -- -- -- -- -- -- -- -- -- |.4 |\n', sio.getvalue()) def test_bad_width(self): ih = IntelHex() sio = StringIO() badwidths = [0, -1, -10.5, 2.5] for bw in badwidths: self.assertRaisesMsg(ValueError, "width must be a positive integer.", ih.dump, sio, bw) badwidthtypes = ['', {}, [], sio] for bwt in badwidthtypes: self.assertRaisesMsg(ValueError, "width must be a positive integer.", ih.dump, sio, bwt) def test_simple_width3(self): ih = IntelHex() ih[0] = 0x12 ih[1] = 0x34 sio = StringIO() ih.dump(tofile=sio, width=3) self.assertEqual( '0000 12 34 -- |.4 |\n', sio.getvalue()) ih[16] = 0x56 ih[30] = 0x98 sio = StringIO() ih.dump(tofile=sio, width=3) self.assertEqual( '0000 12 34 -- |.4 |\n' '0003 -- -- -- | |\n' '0006 -- -- -- | |\n' '0009 -- -- -- | |\n' '000C -- -- -- | |\n' '000F -- 56 -- | V |\n' '0012 -- -- -- | |\n' '0015 -- -- -- | |\n' '0018 -- -- -- | |\n' '001B -- -- -- | |\n' '001E 98 -- -- |. |\n', sio.getvalue()) def test_minaddr_not_zero_width3_padding(self): ih = IntelHex() ih[17] = 0x56 ih[30] = 0x98 sio = StringIO() ih.dump(tofile=sio, width=3, withpadding=True) self.assertEqual( '000F FF FF 56 |..V|\n' '0012 FF FF FF |...|\n' '0015 FF FF FF |...|\n' '0018 FF FF FF |...|\n' '001B FF FF FF |...|\n' '001E 98 FF FF |...|\n', sio.getvalue()) class TestIntelHexMerge(TestIntelHexBase): def test_merge_empty(self): ih1 = IntelHex() ih2 = IntelHex() ih1.merge(ih2) self.assertEqual({}, ih1.todict()) def test_merge_simple(self): ih1 = IntelHex({0:1, 1:2, 2:3}) ih2 = IntelHex({3:4, 4:5, 5:6}) ih1.merge(ih2) self.assertEqual({0:1, 1:2, 2:3, 3:4, 4:5, 5:6}, ih1.todict()) def test_merge_wrong_args(self): ih1 = IntelHex() self.assertRaisesMsg(TypeError, 'other should be IntelHex object', ih1.merge, {0:1}) self.assertRaisesMsg(ValueError, "Can't merge itself", ih1.merge, ih1) ih2 = IntelHex() self.assertRaisesMsg(ValueError, "overlap argument should be either " "'error', 'ignore' or 'replace'", ih1.merge, ih2, overlap='spam') def test_merge_overlap(self): # error ih1 = IntelHex({0:1}) ih2 = IntelHex({0:2}) self.assertRaisesMsg(intelhex.AddressOverlapError, 'Data overlapped at address 0x0', ih1.merge, ih2, overlap='error') # ignore ih1 = IntelHex({0:1}) ih2 = IntelHex({0:2}) ih1.merge(ih2, overlap='ignore') self.assertEqual({0:1}, ih1.todict()) # replace ih1 = IntelHex({0:1}) ih2 = IntelHex({0:2}) ih1.merge(ih2, overlap='replace') self.assertEqual({0:2}, ih1.todict()) def test_merge_start_addr(self): # this, None ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih2 = IntelHex() ih1.merge(ih2) self.assertEqual({'start_addr': {'EIP': 0x12345678}}, ih1.todict()) # None, other ih1 = IntelHex() ih2 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih1.merge(ih2) self.assertEqual({'start_addr': {'EIP': 0x12345678}}, ih1.todict()) # this == other: no conflict ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih2 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih1.merge(ih2) self.assertEqual({'start_addr': {'EIP': 0x12345678}}, ih1.todict()) # this != other: conflict ## overlap=error ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih2 = IntelHex({'start_addr': {'EIP': 0x87654321}}) self.assertRaisesMsg(AddressOverlapError, 'Starting addresses are different', ih1.merge, ih2, overlap='error') ## overlap=ignore ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih2 = IntelHex({'start_addr': {'EIP': 0x87654321}}) ih1.merge(ih2, overlap='ignore') self.assertEqual({'start_addr': {'EIP': 0x12345678}}, ih1.todict()) ## overlap=replace ih1 = IntelHex({'start_addr': {'EIP': 0x12345678}}) ih2 = IntelHex({'start_addr': {'EIP': 0x87654321}}) ih1.merge(ih2, overlap='replace') self.assertEqual({'start_addr': {'EIP': 0x87654321}}, ih1.todict()) class TestIntelHex16bit(TestIntelHexBase): def setUp(self): self.f = StringIO(hex16) def tearDown(self): self.f.close() del self.f def test_init_from_file(self): ih = intelhex.IntelHex16bit(self.f) def test_init_from_ih(self): ih = intelhex.IntelHex(self.f) ih16 = intelhex.IntelHex16bit(ih) def test_default_padding(self): ih16 = intelhex.IntelHex16bit() self.assertEqual(0x0FFFF, ih16.padding) self.assertEqual(0x0FFFF, ih16[0]) def test_minaddr(self): ih = intelhex.IntelHex16bit(self.f) addr = ih.minaddr() self.assertEqual(0, addr, 'Error in detection of minaddr (0 != 0x%x)' % addr) def test_maxaddr(self): ih = intelhex.IntelHex16bit(self.f) addr = ih.maxaddr() self.assertEqual(0x001D, addr, 'Error in detection of maxaddr ' '(0x001D != 0x%x)' % addr) def test_getitem(self): ih = intelhex.IntelHex16bit(self.f) ih.padding = 0x3FFF for addr, word in enumerate(bin16): self.assertEqual(word, ih[addr], 'Data mismatch at address ' '0x%x (0x%x != 0x%x)' % (addr, word, ih[addr])) def test_not_enough_data(self): ih = intelhex.IntelHex() ih[0] = 1 ih16 = intelhex.IntelHex16bit(ih) self.assertRaisesMsg(BadAccess16bit, 'Bad access at 0x0: ' 'not enough data to read 16 bit value', lambda x: ih16[x], 0) def test_write_hex_file(self): ih = intelhex.IntelHex16bit(self.f) sio = StringIO() ih.write_hex_file(sio) s = sio.getvalue() sio.close() fin = StringIO(s) ih2 = intelhex.IntelHex16bit(fin) self.assertEqual(ih.tobinstr(), ih2.tobinstr(), "Written hex file does not equal with original") def test_bug_988148(self): # see https://bugs.launchpad.net/intelhex/+bug/988148 ih = intelhex.IntelHex16bit(intelhex.IntelHex()) ih[0] = 25 sio = StringIO() ih.write_hex_file(sio) def test_setitem(self): ih = intelhex.IntelHex16bit(self.f) old = ih[0] ih[0] = old ^ 0xFFFF self.assertNotEqual(old, ih[0], "Setting new value to internal buffer failed") def test_tobinarray(self): ih = intelhex.IntelHex16bit() ih[0] = 0x1234 ih[1] = 0x5678 self.assertEqual(array.array('H', [0x1234,0x5678,0xFFFF]), ih.tobinarray(start=0, end=2)) # change padding ih.padding = 0x3FFF self.assertEqual(array.array('H', [0x1234,0x5678,0x3FFF]), ih.tobinarray(start=0, end=2)) #/class TestIntelHex16bit class TestIntelHexErrors(TestIntelHexBase): """Tests for custom errors classes""" def assertEqualExc(self, message, exception): return self.assertEqual(message, str(exception)) def test_IntelHexError(self): self.assertEqualExc('IntelHex base error', IntelHexError()) def test_IntelHexError_message(self): self.assertEqualExc('IntelHex custom error message', IntelHexError(msg='IntelHex custom error message')) self.assertEqualExc('IntelHex base error', IntelHexError(msg='')) def test_HexReaderError(self): self.assertEqualExc('Hex reader base error', HexReaderError()) def test_HexRecordError(self): self.assertEqualExc('Hex file contains invalid record at line 1', HexRecordError(line=1)) def test_RecordLengthError(self): self.assertEqualExc('Record at line 1 has invalid length', RecordLengthError(line=1)) def test_RecordTypeError(self): self.assertEqualExc('Record at line 1 has invalid record type', RecordTypeError(line=1)) def test_RecordChecksumError(self): self.assertEqualExc('Record at line 1 has invalid checksum', RecordChecksumError(line=1)) def test_EOFRecordError(self): self.assertEqualExc('File has invalid End-of-File record', EOFRecordError()) def test_ExtendedSegmentAddressRecordError(self): self.assertEqualExc( 'Invalid Extended Segment Address Record at line 1', ExtendedSegmentAddressRecordError(line=1)) def test_ExtendedLinearAddressRecordError(self): self.assertEqualExc('Invalid Extended Linear Address Record at line 1', ExtendedLinearAddressRecordError(line=1)) def test_StartSegmentAddressRecordError(self): self.assertEqualExc('Invalid Start Segment Address Record at line 1', StartSegmentAddressRecordError(line=1)) def test_StartLinearAddressRecordError(self): self.assertEqualExc('Invalid Start Linear Address Record at line 1', StartLinearAddressRecordError(line=1)) def test_DuplicateStartAddressRecord(self): self.assertEqualExc('Start Address Record appears twice at line 1', DuplicateStartAddressRecordError(line=1)) def test_InvalidStartAddressValue(self): self.assertEqualExc("Invalid start address value: {'foo': 1}", InvalidStartAddressValueError(start_addr={'foo': 1})) def test_AddressOverlapError(self): self.assertEqualExc('Hex file has data overlap at address 0x1234 ' 'on line 1', AddressOverlapError(address=0x1234, line=1)) def test_NotEnoughDataError(self): self.assertEqualExc('Bad access at 0x1234: ' 'not enough data to read 10 contiguous bytes', intelhex.NotEnoughDataError(address=0x1234, length=10)) def test_BadAccess16bit(self): self.assertEqualExc('Bad access at 0x1234: ' 'not enough data to read 16 bit value', BadAccess16bit(address=0x1234)) #/class TestIntelHexErrors class TestDecodeHexRecords(TestIntelHexBase): """Testing that decoding of records is correct and all errors raised when needed """ def setUp(self): self.ih = IntelHex() self.decode_record = self.ih._decode_record def tearDown(self): del self.ih def test_empty_line(self): # do we could to accept empty lines in hex files? # standard don't say anything about this self.decode_record('') def test_non_empty_line(self): self.assertRaisesMsg(HexRecordError, 'Hex file contains invalid record at line 1', self.decode_record, ' ', 1) def test_short_record(self): # if record too short it's not a hex record self.assertRaisesMsg(HexRecordError, 'Hex file contains invalid record at line 1', self.decode_record, ':', 1) def test_odd_hexascii_digits(self): self.assertRaisesMsg(HexRecordError, 'Hex file contains invalid record at line 1', self.decode_record, ':0100000100F', 1) def test_invalid_length(self): self.assertRaisesMsg(RecordLengthError, 'Record at line 1 has invalid length', self.decode_record, ':FF00000100', 1) def test_invalid_record_type(self): self.assertRaisesMsg(RecordTypeError, 'Record at line 1 has invalid record type', self.decode_record, ':000000FF01', 1) def test_invalid_checksum(self): self.assertRaisesMsg(RecordChecksumError, 'Record at line 1 has invalid checksum', self.decode_record, ':0000000100', 1) def test_invalid_eof(self): self.assertRaisesMsg(EOFRecordError, 'File has invalid End-of-File record', self.decode_record, ':0100000100FE', 1) def test_invalid_extended_segment(self): # length self.assertRaisesMsg(ExtendedSegmentAddressRecordError, 'Invalid Extended Segment Address Record at line 1', self.decode_record, ':00000002FE', 1) # addr field self.assertRaisesMsg(ExtendedSegmentAddressRecordError, 'Invalid Extended Segment Address Record at line 1', self.decode_record, ':020001020000FB', 1) def test_invalid_linear_address(self): # length self.assertRaisesMsg(ExtendedLinearAddressRecordError, 'Invalid Extended Linear Address Record ' 'at line 1', self.decode_record, ':00000004FC', 1) # addr field self.assertRaisesMsg(ExtendedLinearAddressRecordError, 'Invalid Extended Linear Address Record ' 'at line 1', self.decode_record, ':020001040000F9', 1) def test_invalid_start_segment_addr(self): # length self.assertRaisesMsg(StartSegmentAddressRecordError, 'Invalid Start Segment Address Record at line 1', self.decode_record, ':00000003FD', 1) # addr field self.assertRaisesMsg(StartSegmentAddressRecordError, 'Invalid Start Segment Address Record at line 1', self.decode_record, ':0400010300000000F8', 1) def test_duplicate_start_segment_addr(self): self.decode_record(':0400000312345678E5') self.assertRaisesMsg(DuplicateStartAddressRecordError, 'Start Address Record appears twice at line 2', self.decode_record, ':0400000300000000F9', 2) def test_invalid_start_linear_addr(self): # length self.assertRaisesMsg(StartLinearAddressRecordError, 'Invalid Start Linear Address Record at line 1', self.decode_record, ':00000005FB', 1) # addr field self.assertRaisesMsg(StartLinearAddressRecordError, 'Invalid Start Linear Address Record at line 1', self.decode_record, ':0400010500000000F6', 1) def test_duplicate_start_linear_addr(self): self.decode_record(':0400000512345678E3') self.assertRaisesMsg(DuplicateStartAddressRecordError, 'Start Address Record appears twice at line 2', self.decode_record, ':0400000500000000F7', 2) def test_addr_overlap(self): self.decode_record(':0100000000FF') self.assertRaisesMsg(AddressOverlapError, 'Hex file has data overlap at address 0x0 ' 'on line 1', self.decode_record, ':0100000000FF', 1) def test_data_record(self): # should be no exceptions self.decode_record(':0100000000FF\n') self.decode_record(':03000100000102F9\r\n') self.decode_record(':1004E300CFF0FBE2FDF220FF20F2E120E2FBE6F396') def test_eof(self): # EOF should raise special exception self.assertRaises(_EndOfFile, self.decode_record, ':00000001FF') #/class TestDecodeHexRecords class TestHex2Bin(unittest.TestCase): def setUp(self): self.fin = StringIO(hex8) self.fout = BytesIO() def tearDown(self): self.fin.close() self.fout.close() def test_hex2bin(self): ih = hex2bin(self.fin, self.fout) data = array.array('B', asbytes(self.fout.getvalue())) for addr in range_g(len(bin8)): expected = bin8[addr] actual = data[addr] self.assertEqual(expected, actual, "Data different at address " "%x (%x != %x)" % (addr, expected, actual)) class TestDiffDumps(unittest.TestCase): def test_simple(self): ih1 = IntelHex({1:0x30, 20:0x31, 40:0x33}) ih2 = IntelHex({1:0x30, 20:0x32, 40:0x33}) sio = StringIO() intelhex.diff_dumps(ih1, ih2, sio) result = sio.getvalue() extra = ' ' if sys.version_info[0] >= 3 or sys.version >= '2.7': extra = '' shouldbe = ( "--- a%(extra)s\n" "+++ b%(extra)s\n" "@@ -1,3 +1,3 @@\n" " 0000 -- 30 -- -- -- -- -- -- -- -- -- -- -- -- -- -- | 0 |\n" "-0010 -- -- -- -- 31 -- -- -- -- -- -- -- -- -- -- -- | 1 |\n" "+0010 -- -- -- -- 32 -- -- -- -- -- -- -- -- -- -- -- | 2 |\n" " 0020 -- -- -- -- -- -- -- -- 33 -- -- -- -- -- -- -- | 3 |\n" ) % dict(extra=extra) self.assertEqual(shouldbe, result) class TestBuildRecords(TestIntelHexBase): def test__from_bytes(self): self.assertEqual(':00000001FF', intelhex.Record._from_bytes([0,0,0,1])) def test_data(self): self.assertEqual(':011234005663', intelhex.Record.data(0x1234, [0x56])) self.assertEqual(':0312340056789059', intelhex.Record.data(0x1234, [0x56, 0x78, 0x90])) def test_eof(self): self.assertEqual(':00000001FF', intelhex.Record.eof()) def test_extended_segment_address(self): self.assertEqual(':020000021234B6', intelhex.Record.extended_segment_address(0x1234)) def test_start_segment_address(self): self.assertEqual(':0400000312345678E5', intelhex.Record.start_segment_address(0x1234, 0x5678)) def test_extended_linear_address(self): self.assertEqual(':020000041234B4', intelhex.Record.extended_linear_address(0x1234)) def test_start_linear_address(self): self.assertEqual(':0400000512345678E3', intelhex.Record.start_linear_address(0x12345678)) class Test_GetFileAndAddrRange(TestIntelHexBase): def test_simple(self): self.assertEqual(('filename.hex', None, None), intelhex._get_file_and_addr_range('filename.hex')) self.assertEqual(('f', None, None), intelhex._get_file_and_addr_range('f')) self.assertEqual(('filename.hex', 1, None), intelhex._get_file_and_addr_range('filename.hex:1:')) self.assertEqual(('filename.hex', None, 10), intelhex._get_file_and_addr_range('filename.hex::A')) self.assertEqual(('filename.hex', 1, 10), intelhex._get_file_and_addr_range('filename.hex:1:A')) self.assertEqual(('filename.hex', 1, 10), intelhex._get_file_and_addr_range('filename.hex:0001:000A')) def test_bad_notation(self): self.assertRaises(intelhex._BadFileNotation, intelhex._get_file_and_addr_range, 'filename.hex:') self.assertRaises(intelhex._BadFileNotation, intelhex._get_file_and_addr_range, 'filename.hex:::') self.assertRaises(intelhex._BadFileNotation, intelhex._get_file_and_addr_range, 'C:\\filename.hex:', True) def test_drive_letter(self): self.assertEqual(('C:\\filename.hex', None, None), intelhex._get_file_and_addr_range('C:\\filename.hex', True)) self.assertEqual(('C:\\filename.hex', 1, None), intelhex._get_file_and_addr_range('C:\\filename.hex:1:', True)) self.assertEqual(('C:\\filename.hex', None, 10), intelhex._get_file_and_addr_range('C:\\filename.hex::A', True)) self.assertEqual(('C:\\filename.hex', 1, 10), intelhex._get_file_and_addr_range('C:\\filename.hex:1:A', True)) self.assertEqual(('C:\\filename.hex', 1, 10), intelhex._get_file_and_addr_range('C:\\filename.hex:0001:000A', True)) class TestXrangeLongInt(unittest.TestCase): def test_xrange_longint(self): # Bug #1408934: xrange(longint) blows with OverflowError: if compat.Python == 2: self.assertRaises(OverflowError, xrange, sys.maxint, sys.maxint+3) # upr = compat.range_g(2684625744, 2684625747) self.assertEqual([2684625744, 2684625745, 2684625746], list(upr)) upr = compat.range_g(2684625744, 2684625747, 2) self.assertEqual([2684625744, 2684625746], list(upr)) # dnr = compat.range_g(2684625746, 2684625743, -1) self.assertEqual([2684625746, 2684625745, 2684625744], list(dnr)) dnr = compat.range_g(2684625746, 2684625743, -2) self.assertEqual([2684625746, 2684625744], list(dnr)) class TestInSubprocess(unittest.TestCase): def runProcessAndGetAsciiStdoutOrStderr(self, cmdline): if sys.platform != 'win32': cmdline = shlex.split(cmdline) p = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() retcode = p.poll() if stdout: output = stdout.decode('ascii', 'replace') elif stderr: output = stderr.decode('ascii', 'replace') output = output.replace('\r', '') return retcode, output def versionChecker(self, cmdline_template): cmdline = cmdline_template % sys.executable retcode, output = self.runProcessAndGetAsciiStdoutOrStderr(cmdline) self.assertEqual(version_str, output.rstrip()) self.assertEqual(0, retcode) def test_setup_version(self): self.versionChecker('%s setup.py --version') def test_sripts_bin2hex_version(self): self.versionChecker('%s scripts/bin2hex.py --version') def test_sripts_hex2bin_version(self): self.versionChecker('%s scripts/hex2bin.py --version') def test_sripts_hex2dump_version(self): self.versionChecker('%s scripts/hex2dump.py --version') def test_sripts_hexdiff_version(self): self.versionChecker('%s scripts/hexdiff.py --version') def test_sripts_hexmerge_version(self): self.versionChecker('%s scripts/hexmerge.py --version') class TestWriteHexFileByteCount(unittest.TestCase): def setUp(self): self.f = StringIO(hex8) def tearDown(self): self.f.close() del self.f def test_write_hex_file_bad_byte_count(self): ih = intelhex.IntelHex(self.f) sio = StringIO() self.assertRaises(ValueError, ih.write_hex_file, sio, byte_count=0) self.assertRaises(ValueError, ih.write_hex_file, sio, byte_count=-1) self.assertRaises(ValueError, ih.write_hex_file, sio, byte_count=256) def test_write_hex_file_byte_count_1(self): ih = intelhex.IntelHex(self.f) ih1 = ih[:4] sio = StringIO() ih1.write_hex_file(sio, byte_count=1) s = sio.getvalue() sio.close() # check that we have all data records with data length == 1 self.assertEqual(( ':0100000002FD\n' ':0100010005F9\n' ':01000200A25B\n' ':01000300E517\n' ':00000001FF\n' ), s, "Written hex is not in byte count 1") # read back and check content fin = StringIO(s) ih2 = intelhex.IntelHex(fin) self.assertEqual(ih1.tobinstr(), ih2.tobinstr(), "Written hex file does not equal with original") def test_write_hex_file_byte_count_13(self): ih = intelhex.IntelHex(self.f) sio = StringIO() ih.write_hex_file(sio, byte_count=13) s = sio.getvalue() # control written hex first line to check that byte count is 13 sio.seek(0) self.assertEqual(sio.readline(), ':0D0000000205A2E576246AF8E6057622786E\n', "Written hex is not in byte count 13") sio.close() fin = StringIO(s) ih2 = intelhex.IntelHex(fin) self.assertEqual(ih.tobinstr(), ih2.tobinstr(), "Written hex file does not equal with original") def test_write_hex_file_byte_count_255(self): ih = intelhex.IntelHex(self.f) sio = StringIO() ih.write_hex_file(sio, byte_count=255) s = sio.getvalue() # control written hex first line to check that byte count is 255 sio.seek(0) self.assertEqual(sio.readline(), (':FF0000000205A2E576246AF8E60576227867300702786AE475F0011204AD02' '04552000EB7F2ED2008018EF540F2490D43440D4FF30040BEF24BFB41A0050' '032461FFE57760021577057AE57A7002057930070D7867E475F0011204ADEF' '02049B02057B7403D2078003E4C207F5768B678A688969E4F577F579F57AE5' '7760077F2012003E80F57578FFC201C200C202C203C205C206C20812000CFF' '700D3007057F0012004FAF7AAE7922B4255FC2D5C20412000CFF24D0B40A00' '501A75F00A787730D50508B6FF0106C6A426F620D5047002D20380D924CFB4' '1A00EF5004C2E5D20402024FD20180C6D20080C0D20280BCD2D580BAD20580' 'B47F2012003E20020774010E\n'), "Written hex is not in byte count 255") sio.close() fin = StringIO(s) ih2 = intelhex.IntelHex(fin) self.assertEqual(ih.tobinstr(), ih2.tobinstr(), "Written hex file does not equal with original") ## # MAIN if __name__ == '__main__': unittest.main() intelhex-2.3.0/requirements-dev.txt000066400000000000000000000003401374364251400174020ustar00rootroot00000000000000# requirements for development and release process twine docutils sphinx readme_renderer # to check long description in setup.py: https://github.com/pypa/readme_renderer # python setup.py check -r -s intelhex-2.3.0/scripts/000077500000000000000000000000001374364251400150345ustar00rootroot00000000000000intelhex-2.3.0/scripts/bin2hex.py000077500000000000000000000073531374364251400167600ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2008-2018 Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''Intel HEX file format bin2hex convertor utility.''' VERSION = '2.3.0' if __name__ == '__main__': import getopt import os import sys usage = '''Bin2Hex convertor utility. Usage: python bin2hex.py [options] INFILE [OUTFILE] Arguments: INFILE name of bin file for processing. Use '-' for reading from stdin. OUTFILE name of output file. If omitted then output will be writing to stdout. Options: -h, --help this help message. -v, --version version info. --offset=N offset for loading bin file (default: 0). ''' offset = 0 try: opts, args = getopt.getopt(sys.argv[1:], "hv", ["help", "version", "offset="]) for o, a in opts: if o in ("-h", "--help"): print(usage) sys.exit(0) elif o in ("-v", "--version"): print(VERSION) sys.exit(0) elif o in ("--offset"): base = 10 if a[:2].lower() == '0x': base = 16 try: offset = int(a, base) except: raise getopt.GetoptError('Bad offset value') if not args: raise getopt.GetoptError('Input file is not specified') if len(args) > 2: raise getopt.GetoptError('Too many arguments') except getopt.GetoptError: msg = sys.exc_info()[1] # current exception txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool print(txt) print(usage) sys.exit(2) from intelhex import compat fin = args[0] if fin == '-': # read from stdin fin = compat.get_binary_stdin() elif not os.path.isfile(fin): txt = "ERROR: File not found: %s" % fin # that's required to get not-so-dumb result from 2to3 tool print(txt) sys.exit(1) if len(args) == 2: fout = args[1] else: # write to stdout fout = sys.stdout # compat.get_binary_stdout() from intelhex import bin2hex sys.exit(bin2hex(fin, fout, offset)) intelhex-2.3.0/scripts/hex2bin.py000077500000000000000000000111061374364251400167470ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2005-2018 Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''Intel HEX file format hex2bin convertor utility.''' VERSION = '2.3.0' if __name__ == '__main__': import getopt import os import sys usage = '''Hex2Bin convertor utility. Usage: python hex2bin.py [options] INFILE [OUTFILE] Arguments: INFILE name of hex file for processing. OUTFILE name of output file. If omitted then output will be writing to stdout. Options: -h, --help this help message. -v, --version version info. -p, --pad=FF pad byte for empty spaces (ascii hex value). -r, --range=START:END specify address range for writing output (ascii hex value). Range can be in form 'START:' or ':END'. -l, --length=NNNN, -s, --size=NNNN size of output (decimal value). ''' pad = None start = None end = None size = None try: opts, args = getopt.getopt(sys.argv[1:], "hvp:r:l:s:", ["help", "version", "pad=", "range=", "length=", "size="]) for o, a in opts: if o in ("-h", "--help"): print(usage) sys.exit(0) elif o in ("-v", "--version"): print(VERSION) sys.exit(0) elif o in ("-p", "--pad"): try: pad = int(a, 16) & 0x0FF except: raise getopt.GetoptError('Bad pad value') elif o in ("-r", "--range"): try: l = a.split(":") if l[0] != '': start = int(l[0], 16) if l[1] != '': end = int(l[1], 16) except: raise getopt.GetoptError('Bad range value(s)') elif o in ("-l", "--lenght", "-s", "--size"): try: size = int(a, 10) except: raise getopt.GetoptError('Bad size value') if start != None and end != None and size != None: raise getopt.GetoptError('Cannot specify START:END and SIZE simultaneously') if not args: raise getopt.GetoptError('Hex file is not specified') if len(args) > 2: raise getopt.GetoptError('Too many arguments') except getopt.GetoptError: msg = sys.exc_info()[1] # current exception txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool print(txt) print(usage) sys.exit(2) fin = args[0] if not os.path.isfile(fin): txt = "ERROR: File not found: %s" % fin # that's required to get not-so-dumb result from 2to3 tool print(txt) sys.exit(1) if len(args) == 2: fout = args[1] else: # write to stdout from intelhex import compat fout = compat.get_binary_stdout() from intelhex import hex2bin sys.exit(hex2bin(fin, fout, start, end, size, pad)) intelhex-2.3.0/scripts/hex2dump.py000077500000000000000000000106771374364251400171600ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2008-2018 Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Show content of hex file as hexdump.""" VERSION = '2.3.0' USAGE = '''hex2dump: show content of hex file as hexdump. Usage: python hex2dump.py [options] HEXFILE Options: -h, --help this help message. -v, --version version info. -r, --range=START:END specify address range for dumping (ascii hex value). Range can be in form 'START:' or ':END'. --width=N dump N data bytes per line (default: 16). Arguments: HEXFILE name of hex file for processing (use '-' to read from stdin) ''' import sys DEFAULT_WIDTH = 16 def hex2dump(hexfile, start=None, end=None, width=DEFAULT_WIDTH): import intelhex if hexfile == '-': hexfile = sys.stdin try: ih = intelhex.IntelHex(hexfile) except (IOError, intelhex.IntelHexError): e = sys.exc_info()[1] # current exception sys.stderr.write('Error reading file: %s\n' % e) return 1 if not (start is None and end is None): ih = ih[slice(start,end)] ih.dump(tofile=sys.stdout, width=width) return 0 def main(argv=None): import getopt if argv is None: argv = sys.argv[1:] start = None end = None width = DEFAULT_WIDTH try: opts, args = getopt.getopt(sys.argv[1:], "hvp:r:", ["help", "version", "range=", "width="]) for o, a in opts: if o in ("-h", "--help"): print(USAGE) return 0 elif o in ("-v", "--version"): print(VERSION) return 0 elif o in ("-r", "--range"): try: l = a.split(":") if l[0] != '': start = int(l[0], 16) if l[1] != '': end = int(l[1], 16) except: raise getopt.GetoptError('Bad range value(s)') elif o == "--width": try: width = int(a) if width < 1: raise ValueError except: raise getopt.GetoptError('Bad width value (%s)' % a) if not args: raise getopt.GetoptError('Hex file is not specified') if len(args) > 1: raise getopt.GetoptError('Too many arguments') except getopt.GetoptError: msg = sys.exc_info()[1] # current exception txt = 'ERROR: '+str(msg) # that's required to get not-so-dumb result from 2to3 tool print(txt) print(USAGE) return 2 try: return hex2dump(args[0], start, end, width) except IOError: e = sys.exc_info()[1] # current exception import errno if e.errno not in (0, errno.EPIPE): raise if __name__ == '__main__': import sys sys.exit(main()) intelhex-2.3.0/scripts/hexdiff.py000077500000000000000000000054431374364251400170340ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2011-2018 Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Produce diff for 2 hex files using hex dump as string representation of compared data. """ VERSION = '2.3.0' USAGE = '''hexdiff: diff dumps of 2 hex files. Usage: python hexdiff.py [options] FILE1 FILE2 Options: -h, --help this help message. -v, --version version info. ''' import sys def main(argv=None): import getopt if argv is None: argv = sys.argv[1:] try: opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) for o,a in opts: if o in ('-h', '--help'): print(USAGE) return 0 elif o in ('-v', '--version'): print(VERSION) return 0 except getopt.GetoptError: e = sys.exc_info()[1] # current exception sys.stderr.write(str(e)+"\n") sys.stderr.write(USAGE+"\n") return 1 if len(args) != 2: sys.stderr.write("ERROR: You should specify 2 files to diff.\n") sys.stderr.write(USAGE+"\n") return 1 fname1, fname2 = args from intelhex import IntelHex, diff_dumps ih1 = IntelHex(fname1) ih2 = IntelHex(fname2) diff_dumps(ih1, ih2, name1=fname1, name2=fname2) if __name__ == '__main__': sys.exit(main()) intelhex-2.3.0/scripts/hexinfo.py000077500000000000000000000071171374364251400170570ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2015 Andrew Fernandes # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Summarize the information in a hex file by printing the execution start address (if any), and the address ranges covered by the data (if any), in YAML format. """ VERSION = '2.3.0' USAGE = '''hexinfo: summarize a hex file's contents. Usage: python hexinfo.py [options] FILE [ FILE ... ] Options: -h, --help this help message. -v, --version version info. ''' import sys INDENT = ' ' INLIST = '- ' def summarize_yaml(fname): print("{:s}file: '{:s}'".format(INLIST, fname)) from intelhex import IntelHex ih = IntelHex(fname) if ih.start_addr: keys = sorted(ih.start_addr.keys()) if keys == ['CS','IP']: entry = ih.start_addr['CS'] * 65536 + ih.start_addr['IP'] elif keys == ['EIP']: entry = ih.start_addr['EIP'] else: raise RuntimeError("unknown 'IntelHex.start_addr' found") print("{:s}entry: 0x{:08X}".format(INDENT, entry)) segments = ih.segments() if segments: print("{:s}data:".format(INDENT)) for s in segments: print("{:s}{:s}{{ first: 0x{:08X}, last: 0x{:08X}, length: 0x{:08X} }}".format(INDENT, INLIST, s[0], s[1]-1, s[1]-s[0])) print("") def main(argv=None): import getopt if argv is None: argv = sys.argv[1:] try: opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) for o,a in opts: if o in ('-h', '--help'): print(USAGE) return 0 elif o in ('-v', '--version'): print(VERSION) return 0 except getopt.GetoptError: e = sys.exc_info()[1] # current exception sys.stderr.write(str(e)+"\n") sys.stderr.write(USAGE+"\n") return 1 if len(args) < 1: sys.stderr.write("ERROR: You should specify one or more files to summarize.\n") sys.stderr.write(USAGE+"\n") return 1 for fname in args: summarize_yaml(fname) if __name__ == '__main__': sys.exit(main()) intelhex-2.3.0/scripts/hexmerge.py000077500000000000000000000141001374364251400172110ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2008-2018 Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Merge content of several hex files into one file.""" VERSION = '2.3.0' USAGE = '''hexmerge: merge content of hex files. Usage: python hexmerge.py [options] FILES... Options: -h, --help this help message. -v, --version version info. -o, --output=FILENAME output file name (emit output to stdout if option is not specified) -r, --range=START:END specify address range for output (ascii hex value). Both values are inclusive. Range can be in form 'START:' or ':END'. --no-start-addr Don't write start addr to output file. --overlap=METHOD What to do when data in files overlapped. Supported variants: * error -- stop and show error message (default) * ignore -- keep data from first file that contains data at overlapped address * replace -- use data from last file that contains data at overlapped address Arguments: FILES list of hex files for merging (use '-' to read content from stdin) You can specify address range for each file in the form: filename:START:END See description of range option above. You can omit START or END, so supported variants are: filename:START: read filename and use data starting from START addr filename::END read filename and use data till END addr Use entire file content: filename or filename:: ''' import sys def main(args=None): import getopt output = None start = None end = None write_start_addr = True overlap = 'error' if args is None: args = sys.argv[1:] try: opts, args = getopt.gnu_getopt(args, 'hvo:r:', ['help', 'version', 'output=', 'range=', 'no-start-addr', 'overlap=', ]) for o,a in opts: if o in ('-h', '--help'): print(USAGE) return 0 elif o in ('-v', '--version'): print(VERSION) return 0 elif o in ('-o', '--output'): output = a elif o in ("-r", "--range"): try: l = a.split(":") if l[0] != '': start = int(l[0], 16) if l[1] != '': end = int(l[1], 16) except (ValueError, IndexError): raise getopt.GetoptError('Bad range value(s)') elif o == '--no-start-addr': write_start_addr = False elif o == '--overlap': if a in ('error', 'ignore', 'replace'): overlap = a else: raise getopt.GetoptError('Bad overlap value') if len(args) == 0: raise getopt.GetoptError('You should specify file list') except getopt.GetoptError: e = sys.exc_info()[1] # current exception sys.stderr.write(str(e)+"\n") sys.stderr.write(USAGE+"\n") return 1 import intelhex # TODO: move actual merge code into intelhex package as helper function # and write couple of tests for it. res = intelhex.IntelHex() def end_addr_inclusive(addr): if addr is not None: return addr + 1 return addr for f in args: try: fname, fstart, fend = intelhex._get_file_and_addr_range(f) except intelhex._BadFileNotation: sys.stderr.write('Bad argument: "%s"\n' % f) sys.stderr.write(USAGE+"\n") return 1 if fname == '-': fname = sys.stdin ih = intelhex.IntelHex(fname) if (fstart, fend) != (None, None): ih = ih[fstart:end_addr_inclusive(fend)] try: res.merge(ih, overlap) except intelhex.AddressOverlapError: e = sys.exc_info()[1] # current exception sys.stderr.write('Merging: '+fname+"\n") sys.stderr.write(str(e)+"\n") return 1 if (start, end) != (None, None): res = res[start:end_addr_inclusive(end)] if output is None: output = sys.stdout res.write_hex_file(output, write_start_addr) return 0 if __name__ == '__main__': sys.exit(main()) intelhex-2.3.0/setup.cfg000066400000000000000000000000301374364251400151570ustar00rootroot00000000000000[wheel] universal = 1 intelhex-2.3.0/setup.py000077500000000000000000000110061374364251400150600ustar00rootroot00000000000000#!/usr/bin/python # Copyright (c) 2008-2018, Alexander Belchenko # All rights reserved. # # Redistribution and use in source and binary forms, # with or without modification, are permitted provided # that the following conditions are met: # # * Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # * Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author nor the names # of its contributors may be used to endorse # or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Setup script for IntelHex.""" import sys, glob try: from setuptools import setup except ImportError: from distutils.core import setup from distutils.core import Command import intelhex, intelhex.__version__ LONG_DESCRIPTION = open('README.rst', 'r').read() METADATA = dict( name='intelhex', version=intelhex.__version__.version_str, scripts=glob.glob('scripts/*'), packages=['intelhex'], author='Alexander Belchenko', author_email='alexander.belchenko@gmail.com', maintainer='Bert van Hall', maintainer_email='bert.vanhall@gmx.de', url='https://github.com/python-intelhex/intelhex', description='Python library for Intel HEX files manipulations', long_description=LONG_DESCRIPTION, keywords='Intel HEX hex2bin HEX8', license='BSD', classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', ], ) class test(Command): description = "unittest for intelhex" user_options = [] boolean_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): import unittest import intelhex.test verbosity = 1 if self.verbose: verbosity = 2 suite = unittest.TestSuite() loader = unittest.TestLoader() suite.addTest(loader.loadTestsFromModule(intelhex.test)) runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=verbosity) result = runner.run(suite) if result.errors or result.failures: sys.exit(1) class bench(Command): description = "benchmarks for read/write HEX" user_options = [ ('repeat', 'n', 'repeat tests N times'), ('read', 'r', 'run only tests for read operation'), ('write', 'w', 'run only tests for write operation'), ] boolean_options = ['read', 'write'] def initialize_options(self): self.repeat = 3 self.read = None self.write = None def finalize_options(self): if not self.read and not self.write: self.read = self.write = True def run(self): from intelhex.bench import Measure m = Measure(self.repeat, self.read, self.write) m.measure_all() m.print_report() def main(): metadata = METADATA.copy() metadata['cmdclass'] = { 'test': test, 'bench': bench, # bench is out of date } return setup(**metadata) if __name__ == '__main__': main() intelhex-2.3.0/test-all-python.sh000077500000000000000000000010721374364251400167500ustar00rootroot00000000000000#!/bin/sh TARGETS="3.5.10 3.6.12 3.7.9 3.8.1 3.9.0 pypy2.7-7.3.1 pypy3.5-7.0.0 pypy3.6-7.3.1" DIR=$(dirname "$(readlink -f "$0")") _pyenv=$(command -v pyenv) [ -z "$_pyenv" ] && { echo "pyenv is missing on your system."; exit 1; } _tox=$(command -v tox) [ -z "$_tox" ] && { echo "tox is missing on your system."; exit 1; } cd "$DIR" echo "Installing (if needed) Python versions: $TARGETS" for version in $TARGETS ; do $_pyenv install --skip-existing $version done echo "Enabling Python versions from pyenv..." $_pyenv local $TARGETS echo "Running tox..." $_tox intelhex-2.3.0/test_memory.py000066400000000000000000000002101374364251400162570ustar00rootroot00000000000000import intelhex ih = intelhex.IntelHex() for i in range(65536): ih[i] = i & 0x0FF print(len(ih)) print(ih.get_memory_size()) intelhex-2.3.0/tox.ini000066400000000000000000000007071374364251400146640ustar00rootroot00000000000000# tox (https://tox.readthedocs.io/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py35, py36, py37, py38, py39, pypy2.7, pypy3.5, pypy3.6 skip_missing_interpreters = True tox_pyenv_fallback = False [testenv] deps = setuptools >= 45 commands = python setup.py test